Twinkle Twinkle Little Haskell

Update Sept 28,2010: the Makefile mentioned below worked fine, except for something having to do with timing.  I was too lazy to track the problem down, but fortunately, I found an scons script (using the scons build system) that I modified to run on Mac OSX, and it works perfectly.  The original script is here—thanks Homin Lee!  The post has been modified appropriately.

Update Oct 1, 2010: Homin Lee has updated the script to work on Mac OSX, so you can just grab the original script now.


It’s been a few months almost a year(!) since John Van Enk showed us how to twinkle (blink) an LED on his Arduino microcontroller using Atom/Haskell.  Since that time, Atom (a Haskell embedded domain-specific language for generating constant time/space C programs) has undergone multiple revisions, and the standard Arduino tool-chain has been updated, so I thought it’d be worthwhile to “re-solve” the problem with something more streamlined that should work today for all your Haskell -> Arduino programming needs.  With the changes to Atom, we can blink a LED with just a couple lines of core logic (as you’d expect given the simplicity of the problem).

For this post, I’m using

If you’ve played with the Arduino, you’ve noticed how nice the integrated IDE/tool-chain is.  Ok, the editor leaves everything to be desired, but otherwise, things just work.  The language is basically C with a few macros and Atmel AVR-specific libraries (the family to which Arduino hardware belongs).

However, if you venture off the beaten path at all—say, trying to compile your own C program outside the IDE—things get messy quickly.  Fortunately, with the scons script, things are a piece of cake.

What we’ll do is write a Haskell program AtomLED.hs and use that to generate AtomLED.c.  From that, the scons script will take care of the rest.

The Core Logic

Here’s the core logic we use for blinking the LED from Atom:

ph :: Int
ph = 40000 -- Sufficiently large number of ticks (the Duemilanove is 16MHz)

blink :: Atom ()
blink = do
  on <- bool "on" True -- Declare a Boolean variable on, initialized to True.

  -- At period ph and phase 0, do ...
  period ph $ phase 0 $ atom "blinkOn" $ do
    call "avr_blink"        -- Call a locally-defined C function, blink().
    on <== not_ (value on)  -- Flip the Boolean.

  period ph $ phase (quot ph 8) $ atom "blinkOff" $ do
    call "avr_blink"
    on <== not_ (value on)

And that’s it!  The blink function has two rules, “blinkOn” and “blinkOff”.  Both rules execute every 40,000 ticks.  (A “tick” in our case is just a local variable that’s incremented, but it could be run off the hardware clock.  Nevertheless, we still know we’re getting nearly constant-time due to the code Atom generates.)  The first rule starts at tick 0, and executes at ticks 40,000, 80,000, etc., while the second starts at tick 40,000/8 = 5000 and executes at ticks 5000, 45,000, 85,000, etc.  In each rule, after calling the avr_blink() C function (we’ll define), it modulates a Boolean upon which blink() depends. Thus, the LED is on 1/8 of the time and off 7/8 of the time. (If we wanted the LED to be on the same amount of time as it is off, we could have done the whole thing with one rule.)

The Details

Really the only other thing we need to do is add a bit of C code around the core logic.  Here’s the listing for the C code stuck at the beginning, written as strings:

[ (varInit Int16 "ledPin" "13") -- We're using pin 13 on the Arduino.
, "void avr_blink(void);"
]

and here’s some code for afterward:

[
"void setup() {"
, " // initialize the digital pin as an output:"
, " pinMode(ledPin, OUTPUT);"
, "}"
, ""
, "// set the LED on or off"
, "void avr_blink() { digitalWrite(ledPin, state.AtomLED.on); }"
, ""
, "void loop() {"
, " " ++ atomName ++ "();"
, "}"
]

The IDE tool-chain expects there to be a setup() and loop() function defined, and it then pretty-prints a main() function from which both are called. The code never returns from loop().

To blink the LED, we call digitalWrite() from avr_blink(). digitalWrite() is provided by the Arduino language.  (In John’s post, he manipulated the port registers directly, which is faster and doesn’t rely on the Arduino libraries, but it’s also lower-level and less portable between Atmel controllers.)  Atom-defined variables are stored in a struct, so state.AtomLED.on references the Atom Boolean variable defined earlier.

Make it Work!

Now just drop the scons script into the directory (the directory must have the same name as the Haskell file, dropping the extension), and run

> runhaskell AtomLED.hs
> scons
> scons upload

And your Haskell should be twinkling your LED. runhaskell AtomLED.hs invokes the Atom compile function to generate a C file and headers; scons invokes the build script to build an ELF image to upload, and scons upload again invokes the compiler to upload to your board.

This should work for any Atom-generated program you want to run on your Arduino (modulo deviations from the configuration I mentioned initially). Also, note the conventions and parameters to set in the scons script.

Post if you have any problems, and I might be able to help. Also, I’d love to package the boilerplate up into a “backend” for Atom, but if you have time, please beat me to it.  Thanks.

Code:

12 Responses to “Twinkle Twinkle Little Haskell”

  1. Resources for Learning Haskell « Tinkering with Technology Says:

    […] you can even write Arduino programs in Haskell if you so wish ! Do you have any favourite Haskell sites? If so, let me know in the […]

  2. suapapa Says:

    Impressive!

    I’m not familiar with Haskell.
    but, I think, you can also embed;
    “runhaskell AtomLED.hs” to the SConstruct
    (instead of fnProcessing()) and save some typing.

    ps. now, the arscons works on Mac by default (os detection).

  3. Jurriën Stutterheim Says:

    Hi there,

    Very interesting! I’m trying to make this work. Unfortunately the .hs file linked in the post seems to be incomplete; it does not compile. I modified the file[1] a bit so it compiles, but it does not seem to generate a .pde file with content). The C-code it generates won’t compile either..
    What could be going wrong?

    Cheers!

    [1] http://pastie.org/1328296

    • Lee Pike Says:

      Jurriën,

      Ugh, I’m really sorry. It turns out that when I pasted my Haskell code into WordPress, there was some HTML-ifying that broke it. In any event, I think I’ve fixed it—it seems to work for me. Try getting the Haskell code from here and the SConstruct from here. Then in a directory called AtomLED,

      > runhaskell AtomLED.hs
      > scons
      > scons upload

      And that should do it. Add another comment if that doesn’t work.

      • Jurriën Stutterheim Says:

        My LED is blinking like there’s no tomorrow; thanks for the quick reply!

      • Lee Pike Says:

        :) Yay! Sorry again about not double-checking the cut-and-pasting…

      • Jurriën Stutterheim Says:

        No problem :) I’m really glad it works now. Tomorrow I hope to find some time to play around with the language-c package, so I can get rid of the C code inside of strings. Hopefully I’ll be able to upload something to GitHub tomorrow :)

      • Lee Pike Says:

        That’d be great if you can get rid of the C code inside strings. I’ve used a little ad-hoc library for some of this on Hackage here, but it’d be nice to have a separate package to handle this. (As far as I know, language-c is concerned with parsing C, so we’ll need something else…)

      • Jurriën Stutterheim Says:

        My first attempt at getting rid of the C strings can be found in my blink.hs repository: https://github.com/norm2782/blink.hs
        It uses a little library I hacked together in a few minutes. It’s far from ideal, but it works for blink.hs’s purposes.

        The language-c package does seem have a pretty printer:
        http://hackage.haskell.org/packages/archive/language-c/0.3.2/doc/html/Language-C-Pretty.html
        If it’s anything like the haskell-src-exts pretty printer, it should be able to spew out C code :) Its source looks encouraging.

  4. Tim Dysinger Says:

    I put all this up on github in a repo with clear readme

    https://github.com/dysinger/AtomLED

Leave a comment