Tapestry Training -- From The Source

Let me help you get your team up to speed in Tapestry ... fast. Visit howardlewisship.com for details on training, mentoring and support!

Friday, December 04, 2009

Change of pace: Arduino

As a total change of pace, I've been playing around with actual hardware, in the form of an Arduino board. This is a hoot, an actual computer that you can carry around in your hand. They call it physical computing.

I'm just getting started with it, in my tiny shards of free time. Your code is in C (and a smattering of C++). I've hooked up four LEDs and two buttons that allow me to cycle the LEDs forward or backward. Since you're working at such a low level, you have to be aware of tiny factors such as key bounce (closing a switch will, for a short period, yield unstable results due to physical and electrical factors).

There's a tool called Fritzing to help you document your projects. It's very alpha, but the simple results are rather nice:


The code is still evolving:

#define FIRST_LED 12
#define LED_COUNT 4
#define DEBOUNCE_PERIOD 50 // ms

class Debounce
{
public:
  Debounce(int pin);
  boolean read();
private:
  int _pin;
  int _previousValue;
  int _lastButtonDebounce;
  boolean _enabled;
};

Debounce::Debounce(int pin)
{
  _pin = pin;
  _previousValue = LOW;
  _lastButtonDebounce = 0; // never
  _enabled = true;

  pinMode(_pin, INPUT);  
}

boolean Debounce::read()
{
  int currentValue = digitalRead(_pin);

  long now = millis();

  if (currentValue != _previousValue)
  {
    _lastButtonDebounce = now;
    _previousValue = currentValue;
    return false;
  }

  if (now - _lastButtonDebounce < DEBOUNCE_PERIOD) {
    return false;
  }


  // It's gone HIGH to LOW

  if (currentValue == LOW) {
    _enabled = true;
    return false;
  }

  // It's gone LOW to HIGH

  if (_enabled) {
    _enabled = false;
    return true;
  }

  return false;
}

// First press will move to the first LED.
int currentLed = -1;

Debounce advanceButton = Debounce(2);
Debounce retreatButton = Debounce(3);

void setup()
{
  for (int i = 0; i < LED_COUNT; i++) {
    int pin = FIRST_LED - i;
    pinMode(pin, OUTPUT);
    digitalWrite(pin, HIGH);
  }

  delay(250);

  for (int i = 0; i < LED_COUNT; i++)
  {
    digitalWrite(FIRST_LED - i, LOW);
  }
}

void advance()
{
  if (currentLed >= 0)
    digitalWrite(FIRST_LED - currentLed, LOW);

  if (++currentLed == LED_COUNT)
    currentLed = 0;

  digitalWrite(FIRST_LED - currentLed, HIGH);
}

void retreat()
{
  if (currentLed >= 0)
    digitalWrite(FIRST_LED - currentLed, LOW);

  if (--currentLed < 0)
    currentLed = LED_COUNT - 1;

  digitalWrite(FIRST_LED - currentLed, HIGH);
}

void loop()
{
  if (advanceButton.read()) advance();

  if (retreatButton.read()) retreat();
}

Fun stuff ... and I don't see me writing a web framework for it, which is a change of pace.

My ultimate goal is to write Arduino apps in a Clojure DSL, and have Clojure generate the machine code for the AVR processor that runs the Arduino. Of course, that means learning AVR machine code and writing a lot of code to compile and assemble the DSL into something that can execute inside the Arduino.

Plan B: Perhaps its time to learn Forth?

3 comments:

fel said...

you might miss this to fix the chaos with your buttons : http://en.wikipedia.org/wiki/Schmitt_trigger

BR and thanks for your nice tapestry framework!

Curious Attempt Bunny said...

YouTube it! Let's see it in action!

Sophie said...

You don't need to go to AVR machine code, it may be easier to generate the stylized C++ that the Arduino IDE uses, and use its facilities to compile and load that onto the board. I'd suggest at first allowing limited Clojure constructs in your DSL (e.g. simple closure-based iteration, conditionals, etc.) and require Clojure type annotations.

There's a comparable Ruby project for this rad.rubyforge.org but of course Ruby lacks macros & type annotations so it does not work that great.

Would love to know if you actually get going with this!