How to blink a light in 10+ different programming languages

Purpose

The purpose of this post is to demonstrate the diversity and availability of programming languages on embedded Linux platforms, like the BeagleBone and Raspberry Pi. It is not meant to demonstrate how it would or could be done on platform X or platform Y, such as Arduino. Additionally, it based on and assumed that the Linux kernel has included the [GPIO Sysfs Interface for Userspace][linuxgpio]. If you are not familiar with it, it maps the SoCs hardware to the Linux file system. For example, on the BeagleBone, there are 4 ‘user’ leds that are mapped to the file system as follows.

/sys/class/leds/beaglebone\:green\:usr[0-3]

Why do this?

The examples below are simple and basically open a file and write to it, however, I think it demonstrates two things:

  1. There are serveral languages that are quite easily installed from the package manager of the Linux distribution running on the SoC.
  2. The blinking light is the hardware hacking equivalent of the ‘Hello World’ program in software. If you can blink a light programatically, you can extend that to reading sensors and controlling actuators.

You can use these examples as a starting point and extend them to your own projects. I am personally interested in the ‘Internet of Things’ and/or Home Automation.

A great book that I’ve read on the BeagleBone is by Derek Molloy.

I’ve also used the BeagleBone Black and RPi 2 for these examples:

First Steps

I am making the following assumptions:

  • you already have a Linux version running on your platform
  • a package manager is available to install software (the examples below use debian’s apt-get)
  • you can connect into your platform, either through telnet or ssh
  • you can compile and run the various examples based on the programming language (although I should add that as well to the examples)

Notes about the examples

  • I am using the default provided user leds on BeagleBone which are mapped via GPIO Sysfs
  • these are simple examples and wouldn’t be used in production and therefore best practices are not always applied (see Why do this?)
  • all the examples work, they blink an led on a beaglebone
  • the examples could be coded differently to demonstrate the same thing

(1) Bash Shell

The first language we’ll use is the ‘bash’ shell. It’s installed by default.

#!/bin/bash
STATE=0
while :
do
  STATE=$((STATE==0))
  echo $STATE > /sys/class/leds/beaglebone\:green\:usr0/brightness
  sleep 2
done

(2) Perl

Perl is installed by default.

#!/usr/bin/perl
use warnings;
my $state = 1;
my $filename = "/sys/class/leds/beaglebone:green:usr0/brightness";

while (1) {
  open LED , ">>$filename" or die "Error opening $filename: $!";
  syswrite (LED, $state , 1);
  $state = 1 - $state;
  sleep 2;
}

(3) C

The C compiler (gcc) is installed by default.

#include <stdio.h>
#include <stdlib.h>

int main() {

  FILE *fp = NULL;
  char *filename = "/sys/class/leds/beaglebone:green:usr0/brightness";
  int state = 1;
  int retval = 0;

  if ((fp = fopen(filename, "w")) == NULL) {
    printf("failed to fopen led\n");
    exit(-1);
  }

  for (;;) {
    if ((retval = fputc('0' + state, fp)) == EOF) {
      printf("failed to fputc led\n");
      exit(-1);
    }
    fflush(fp);
    sleep (2);
    state = 1 - state;
  }
}

(4) C++

The C++ compiler (g++) isn’t installed by default:

# sudo apt-get install g++

#include <iostream>
#include <fstream>
#include <unistd.h>
#include <stdlib.h>

using namespace std;

int main() {
  ofstream led;
  string filename("/sys/class/leds/beaglebone:green:usr0/brightness");
  int state = 1;

  led.open (filename.c_str(), ios::binary);
  if (led.is_open()) {
    while (true) {
      led << state << endl;
      sleep (2);
      state = 1 - state;
    }
  }
  else {
    cout << "failed to open led" << endl;
    exit(-1);
  }
}

(5) Python

Python is installed by default.

import time

filename = "/sys/class/leds/beaglebone:green:usr0/brightness"
f = open(filename, "w")
state = 1

while True:
  f.write(str(state))
  f.flush()
  time.sleep(2)
  state = 1 - state

(6) Ruby

Ruby is not installed by default:

# sudo apt-get install ruby

#!/usr/bin/ruby

filename = "/sys/class/leds/beaglebone:green:usr0/brightness"
state = 1

begin
  file = File.open(filename, "w")
  loop do
    file.write(state.to_s)
    file.flush
    sleep(2)
    state = 1 - state
  end
rescue IOError => e
  puts "failed: #{e}"
ensure
  file.close unless file == nil
end

(7) Java

Java is not installed by default:

# sudo apt-get install openjdk-7-jdk

import java.io.*;
import java.nio.file.*;
import java.nio.charset.StandardCharsets;

public class Blink {
  public static void main(String[] args) throws IOException, InterruptedException {
    int state = 1;
    Path path = Paths.get("/sys/class/leds/beaglebone:green:usr0/brightness");
    try(BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
      while (true) {
        writer.write('0' + state);
        writer.flush();
        Thread.sleep(2000);
        state = 1 - state;
      }
    }
  }
}

(8) Scala

Scala is not installed by default:

# sudo apt-get install scala

import java.io.File
import java.io.FileWriter
import java.io.BufferedWriter

object Blink {
  def main(args: Array[String]) {
    var state = 1
    val file = new File("/sys/class/leds/beaglebone:green:usr0/brightness")
    val writer = new BufferedWriter(new FileWriter(file))
    while (true) {
      writer.write('0' + state)
      writer.flush()
      Thread.sleep (2000)
      state = 1 - state;
    }
  }
}

(9) Go

Go is not installed by default

# sudo apt-get install golang

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 {
    _, err = f.WriteString(strconv.Itoa(state))
    if err != nil {
      log.Fatal(err)
    }
    time.Sleep(2 * time.Second)
    state = 1 - state
  }
}

(10) Clojure

Clojure is not installed by default:

# sudo apt-get install clojure1.4

(defn blink []
  (with-open [fw (clojure.java.io/writer "/sys/class/leds/beaglebone:green:usr0/brightness" :append true)]
    (loop [state 1]
      (.write fw (Integer/toString state))
      (.flush fw)
      (Thread/sleep 2000)
      (recur (- 1 state)))))
(blink)

(11) Pascal

Pascal is not installed by default:

# sudo apt-get install fpc

{$IFDEF FPC}
  {$MODE DELPHI}
{$ENDIF}

Program Blink;

uses
  Sysutils;

const
  LED_FNAME = '/sys/class/leds/beaglebone:green:usr0/brightness';

var
  Led: TextFile;
  State: Integer;

begin
  State:=1;
  try
    Assign(Led, LED_FNAME);
    Append(Led);
    While true do
    begin
      Writeln(Led, State);
      Flush(Led);
      Sleep(2000);
      State:=1-State;
    end;
  except
    on E: EInOutError do
      Writeln('failed: ', E.Message);
  end;
  CloseFile(led);
end.

(12) NodeJS

NodeJS is not installed by default:

# sudo apt-get install nodejs

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

(13) Haskell

Haskell (ghc) is not installed by default:

# sudo apt-get install ghc

TODO

(13) Rust

Rust is not installed by default:

TODO

[linuxgpio]: https://www.kernel.org/doc/Documentation/gpio/sysfs.txt