You may "How to blink a light extremely inefficiently 10 different ways." ;-)
Seriously, not even one Asm example? If you have a PC that has a parallel port and can boot to DOS, you can stick a LED in series with a 100-ohm resistor across pins 2(+) and 18(-) and do this:
mov dx, 378h
xor ax, ax
blinkloop:
out dx, al
mov cx, 36
call delay
not ax
jmp blinkloop
delay:
hlt
loop delay
ret
I find it somewhat amazing that this program, in Assembly, is actually shorter than the 10 HLL programs in the article.
Here's something more appropriate (air coded, modulo my iPhone's spell corrector)
section .text
global _start
_start:
; Write the ‘1’ to a file
mov eax, 4 ; sys_write
mov edx, 1 ; # of bytes to write
mov ecx, ‘1’ ; Means light should be on
mov ebx, file_open ; File descriptor
mov eax, 4 ; Call to sys_write
int 0x80 ; Call kernel to write file
; Close file
move eax, 6
mov mov ebx,
int 0x80 ; Call kernel to close file
; Quit to OS
move eax, 1 ; SYS_EXIT
int 0x80 ; Call kernel to exit to OS
section.data
filename db ‘/sys/class/leds/beaglebone:green:usr0/brightness’,0
len equ $-filename
section .bss
file_open resb 1
state db ‘1’
That's the point. It doesn't do all the work of the original code because it doesn't need to. You don't need a whole OS and several layers of abstraction just to blink an LED, not even a dozen instructions (and I notice now, since I was writing it off the top of my head, that the delay subroutine is only called once, so there's another two "wasted" instructions...)
I think it's very important that programmers are exposed to such "absolute minimum simplicity" solutions, as it helps greatly with understanding the essence of the problem and its solution - in this case it's nothing more than toggling a bit in an I/O port. Complexity can be added afterwards as needed, but not gratuitously.
I like how one of the other comments here and in the article mentions that there's no error handling in many of the examples, since it brings up another point about complexity: it can introduce opportunities for errors. If you're working with the GPIO directly, errors either can't occur, or they're hardware-level (e.g. shorted output) and nothing can really be done about it anyway unless the hardware has additional features for signaling such conditions. If you're going through the OS' filesystem, errors can occur at any one of the multiple layers for not-so-related reasons to the actual task. One should then ask the question of whether the code that's added to handle errors is actually doing something useful for the task, or serves no purpose beyond satisfying the other layers of complexity that introduce them.
Good points, and I agree about understanding minimal simplicity and you don't need an OS and several layers of abstractions to blink a light. The post wasn't explicit (I think it is now after updating), but the intent was to demonstrate the variety of languages available on Linux based platforms that have GPIO SysFS, so assembly was never considered. It is more focused on those environments in which these HLL do have a role/place.
userbinator's example does not conform to the API, which writes to a file (and on some platforms, uses best practices to write atomically and check for errors to boot). I took a stab at it elsewhere.
using System;
using System.IO;
using System.Threading;
public class Blink
{
const string FilePath = @"/sys/class/leds/beaglebone\:green\:usr0/brightness";
const int BlinkDelay = 2000;
public static void Main()
{
using (var writer = new StreamWriter(FilePath))
{
for (var state = true; true; state = !state)
{
writer.Write((state ? 1 : 0).ToString("00"));
Thread.Sleep(2000);
}
}
}
}
Both your example and the Java one are not abstract enough (although the added string conversion is nice.) I was expecting more design patterns and layering for a truly enterprise-quality solution, like this:
This is my favorite comment :-) After years of programming in enterprise Java, I failed to truly make it enterprise worthy! I'm going to see if I can get an assembly example added per your other comment.
I am disappointed. I opened this article expecting a myriad of MOVs and JMPs, PEEKS and POKEs, &s and *s, <s and +s, DOs and GO TOs. These are all languages that borrow a lot from each other, and as such look mostly the same. We need some variety in here, some zing. Give some FORTRAN IV, some COBOL, some BASIC even, and it could be interesting, especially if you add in how to do it with raw io; sysfs is ugly and boring.
It wasn't clear in the original post but this was really meant for embedded Linux platforms that already have GPIO mapped via SysFS so assembly wasn't considered. I've updated the front matter of the post to reflect that. I don't know if Pascal is considered 'zing' these days, but I added that as an example. Thanks for your comment.
The bash example should have set -e or #!/bin/bash -e so that it will exit on error. That or the rest of the languages also shouldn't bother to check for errors. Also rather than invoking another shell use let and do let STATE=1-$STATE just like all the other programs. And why does bash need to have comments. Maybe it was a subtle joke that C doesn't get comments, but bash does :D in which case well played.
#!/bin/bash -e
STATE=1
while : do
echo $STATE > /sys/class/leds/beaglebone\:green\:usr0/brightness
let STATE=1-$STATE
sleep 2
done
While all of the programs can be made smaller in size the bash script is probably the default way that the script would be tossed together. Bash has a ton of problems, but sadly between it and the standard unix commands you can get the job done too quickly and easily.
In predicate contexts (which means return code in bash) like if, while or the left hand side of '&&', consider using (( )) - this has an exit code of 0 if the arithmetic expression is true (non-zero) and 1 otherwise. Use it like this:
let x=3*3
if (( x > 5 )); then
echo greater than five
else
echo less than or equal to five
end
Writing a boolean type to SysFS/GPIO from NodeJS doesn't quite work so I tweaked your example to following similar pattern of the other languages. Thanks.
I wanted to show the power of JavaScript types, and you got a type bug? What the irony. I'll probably report that as a bug in nodejs or an undocumented "feature".
import Control.Concurrent (threadDelay)
import System.IO
main :: IO ()
main = withFile "/sys/class/leds/beaglebone:green:usr0/brightness" AppendMode go
where put h s = hPutStrLn h s >> threadDelay 2000000
go h = let m = put h "0" >> put h "1" >> m in m
As always, prefer prebuilt recursion scheme combinators instead of making your own. The following, using `forever`, is IMO much more readable because you don't have to mentally resolve the recursion:
main :: IO ()
main = withFile ledFile AppendMode $ \h -> do
let put s = hPutStrLn h s >> threadDelay 2000000
forever $ do
put "0"
put "1"
where
ledFile = "/sys/class/leds/beaglebone:green:usr0/brightness"
Ruby example is not very idiomatic. Give File.open a block and it'll handle closing it. Instead of flushing the file every time, you could just set file.sync = true once.
Also, if one was going to use the ensure block, use nil? not '== nil'
Similar quirks in the Python example. Opening the file should ideally be done with a "with" statement to close the file automatically at the end. But in the Ruby example the author at least closed the file instead of doing nothing as in the Python example. :)
import 'dart:async';
import 'dart:io';
main() {
var file = new File('/sys/class/leds/beaglebone:green:usr0/brightness');
var state = 0;
new Timer.periodic(new Duration(seconds: 2), (_) {
state = 1 - state;
file.writeAsStringSync('0$state', flush: true);
});
}
While it's not the most concise version, it's certainly very readable. Everyone can clearly see that the duration is 2 seconds and that that boolean flag is for flushing, because that's exactly what the code says.
Thanks, yeah, I went crazy and implemented it via go channels, which isn't a fair comparison. I updated with a slightly tweaked version of what you provided.
The Ruby example doesn't handle exceptions with the same granularity as Java example and is therefore much shorter. Please do not compare the examples.
that is interesting, not sure if it is any easier than compiling some of the examples I provide, but it does open Linux platforms to those that know wiring, thanks.
It is a little more than that as you also need to write alternating state to the file, but you are right, and that is the point of the post. You can read/write embedded sensors/actuators in whatever language you are comfortable by simply reading/writing from the Linux file system.
.. on a correctly set up beaglebone, not other systems. It would be much better if this were in the title.
On most embedded systems and FPGAs faffing with the toolchain and hunting in the documentation is 99% of the effort of getting an LED blinking, that's why it's used as the embedded 'hello world'.
Seriously, not even one Asm example? If you have a PC that has a parallel port and can boot to DOS, you can stick a LED in series with a 100-ohm resistor across pins 2(+) and 18(-) and do this:
I find it somewhat amazing that this program, in Assembly, is actually shorter than the 10 HLL programs in the article.