
How to blink a light in 10 different programming languages - mcconnma
http://www.mcconn.xyz/posts/blinklight-10-languages/
======
userbinator
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.

~~~
kyberias
That's not a fair comparison in any way. Your assembler program doesn't do
half of the original code.

~~~
userbinator
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.

~~~
mcconnma
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.

------
miguelrochefort
C#

[https://dotnetfiddle.net/afyTvh](https://dotnetfiddle.net/afyTvh)

    
    
        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);	
        		}
        	    }
        	}
        }
    

Reactive Extensions (C#)

[https://dotnetfiddle.net/bpOF5e](https://dotnetfiddle.net/bpOF5e)

    
    
        Observable
    	.Repeat(Observable.Unit)
    	.Select((index, _) => index % 2)
            .Select(string.Format({0:00})
    	.Delay(TimeSpan.FromSeconds(2))
    	.Aggregate(
    	    new StreamWriter(@"/sys/class/leds/beaglebone\:green\:usr0/brightness"), 
    	    writer => (writer, state) => writer.Write(state)
    	)
    	.Subscribe(writer => writer.Close());

~~~
userbinator
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:

[https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris...](https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition)

~~~
mcconnma
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.

------
Sanddancer
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.

~~~
mcconnma
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.

------
icefox
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.

~~~
pdkl95
> invoking another shell

That wasn't a subshell - $(( )) is Arithmetic Expansion. (you may be thinking
of $( ) for Command Substitution)

    
    
        $ echo $BASH_SUBSHELL $((BASH_SUBSHELL)) $(echo $BASH_SUBSHELL)
        0 0 1
    

Now, using the result of a boolean is a bit strange; I would have used your
math with the modern expansion style:

    
    
        # the new style allows spaces
        STATE=$(( 1 - $STATE ))

~~~
icefox
Ah, thats great learned a new bash trick

~~~
barrkel
Shorthand for $(( )) is $[ ]

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

------
z3t4
With JavaScript (nodejs)

    
    
      var lightsOn = true;
    
      setInterval(blink, 2000);
    
      function blink() {
        var fs = require("fs");
    	
        fs.writeFile("/sys/class/leds/beaglebone:green:usr0/brightness", lightsOn, function (err) {
          if (err) throw err;
        });
    	
        lightsOn = lightsOn ? false : true;
    
      }

~~~
have_faith
It makes no difference but for some reason I feel the urge to shorten the last
line to:

lightsOn = !lightsOn;

I'll see myself out.

~~~
Cshelton
Every time I write that in JavaScript, I delete it and just use ! As well haha

------
eutectic
And in Haskell (feel free to include this):

    
    
        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

~~~
mcconnma
Thanks, any thoughts on why I get this?

$ ghc -o blink blink.hs [1 of 1] Compiling Main ( blink.hs, blink.o )

blink.hs:7:22: parse error on input `='

~~~
eutectic
Not sure; I copy-pasted it back from hackernews and it works fine for me.

------
blcknight
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'

~~~
ripa
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. :)

------
ahoge
Dart:

    
    
      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.

~~~
ju-st
Yes, I like it! Only thing I don't understand is '0$state'.

~~~
ahoge
That's string interpolation. That line writes either "00" or "01". It's
essentially the same as `'0' \+ state`.

    
    
      'x: $x, y: $y'
    

is a lot easier to type than:

    
    
      'x: ' + x + ', y: ' + y
    

I actually had to triple check that line. Concatenation is very error-prone. I
often mess it up.

String interpolation is a fairly popular feature nowadays:

[https://en.wikipedia.org/wiki/String_interpolation](https://en.wikipedia.org/wiki/String_interpolation)

------
AirMarshalHenry
A bit more compact go-version:

    
    
        package main
    
        import (
        	"log"
        	"os"
        	"strconv"
        	"time"
        )
    
        func main() {
        	f, err := os.OpenFile("/sys/class/leds/beaglebone:green:usr0/brightness", os.O_RDWR, 0666)
        	if err != nil {
        		log.Fatal(err)
        	}
        	defer f.Close()
    
        	state := 1
        	for i := 0; i < 3*2; i++ {
        		_, err = f.WriteString(strconv.Itoa(state))
        		if err != nil {
        			log.Fatal(err)
        		}
        		time.Sleep(2 * time.Second)
        		state = 1 - state
        	}
        }

~~~
mcconnma
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.

------
kyberias
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.

------
asciimike
Bonescript is pretty simple as well:

    
    
        // npm install bonescript
        var b = require('bonescript');
        b.pinMode('P8_13', b.OUTPUT);
        b.digitalWrite('P8_13', 1);
    

Pretty sure Bonescript is just doing the filesystem stuff under the hood,
though doing memory mapping would be _way_ nicer.

------
mbanzi
Alternatively one could use the Arduino API on the beaglebone with
[https://github.com/prpplague/Userspace-
Arduino](https://github.com/prpplague/Userspace-Arduino)

It would make the whole thing less scary for beginners and there are tons of
examples and documentation out there.

~~~
mcconnma
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.

------
z3t4
I find using name-spaces in imports makes the code a hundred times easier to
reason about.

------
valleyer
First example seems like it would want >, not <

~~~
mcconnma
you're right,fixed.

------
escaped_hn
How to open a file in 10 different programming languages.

~~~
mcconnma
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.

~~~
pjc50
.. 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'.

~~~
mcconnma
good point, I updated the front matter of the post to make this more explicit.

