Event Loop for the Arduino

March 27th, 2009 § Comments Off on Event Loop for the Arduino § permalink

Making an LED blink or driving a single stepper using the Arduino is very easy – pulse a pin in the Arduino loop handler is all you need. However, as builds become more complex, there is often the need to manage multiple devices.

The RepRap firmware is such a project. The firmware has numerous steppers, motors, heaters, fans and sensors – it is chalk full of features. The developers have literally performed magic with the tiny little Arduino. However, I believe they will admit that the code base is becoming a little unwieldy and difficult to modify.

As an operating systems developer, my job is to identify patterns in application development, build abstractions which simplify these common cases and generate boiler plate or error prone code. In my post on the RepRap firmware refactor, I described several design patterns which are applicable to not just RepRap, but many other complex embedded projects. The best place to start is with the root of all evil – the event loop.

Event Loop

Event driven programing is a staple of modern application development. In my experience, event driven patterns are not as prevalent in embedded development – it’s a shame, because it is very useful. The main conceptual leap requires the developer to give up control to the power of the loop – a leap that is often very difficult as they are used to complete control. I’ve completed an implementation, which in its current state is more of a timer loop, but will grow to enable queued events (both on and off board).

This code is currently checked into the RepRap source forge repository. It is only dependent on the Dynamic Array implementation checked into the tree as well.

There are several features of this event loop:

  • Periodic (or cycle) events
  • Timed events, with support for wrapping (since the milliclock quickly overflows)
  • Reentrant – You can add or remove events while processing an event
  • Support for nested, independent Eventloops (for those special cases)

(NOTE: It is not interrupt safe at the moment)

Sample Use

void StepperDevice::turn(float numberOfRevolutions)
{
    if (_currentTick)
    {
        stop();
    }
    
    _targetTick = (int)(numberOfRevolutions / _ticksPerRev);

    notifyObservers(StepperEvent_Start, this);
    EventLoop::current()->addTimer(this);
}

void StepperDevice::fire()
{
    digitalWrite(_stepPin, HIGH);
    delayMicroseconds(5);
    digitalWrite(_stepPin, LOW);

    ++_currentTick;
    
    if (_targetTick && (_currentTick == _targetTick))
    {
        notifyObservers(StepperEvent_Complete, this);
        stop();
    }
}

The Event Loop Header

#ifndef EventLoop_h
#define EventLoop_h

typedef unsigned long milliclock_t;
extern const unsigned long MILLICLOCK_MAX;

//
// Periodic Event Callback
//  Derive from this class to implement a periodic servicing
//
class PeriodicCallback
{
public:
  // NOTE: This is a harmless warning: 
  //  alignment of 'PeriodicCallback::_ZTV16PeriodicCallback' 
  // is greater than maximum object file alignment.
  // Bug in avr-g++. 
  // See http://www.mail-archive.com/avr-chat@nongnu.org/msg00982.html
    PeriodicCallback();
    virtual ~PeriodicCallback();
    
    virtual void service() = 0;
};

//
// Timer
//  Derive from this class to implement a periodic timer.
//  This class also contains information needed for 
//  maintianing a timer, designed to be memory efficient.
//
class EventLoopTimer
{
private:
    milliclock_t _lastTimeout;
    milliclock_t _period;
protected:
    inline void setPeriod(milliclock_t period) 
        { _period = period; }
        
public:
    EventLoopTimer(unsigned long period);
    virtual ~EventLoopTimer();
    
    virtual void fire() = 0;
    
    inline milliclock_t period() const 
    { return _period; }
    inline milliclock_t lastTimeout() const 
        { return _lastTimeout; }
    milliclock_t nextTimeout() const;
    
    inline void setLastTimeout(milliclock_t nextTimeout) 
        { _lastTimeout = nextTimeout; }
};

//
// Event Loop
//  This class implements the main loop. 
//  It allows clients to register for periodic 
//      servicing, or timed servicing.
//  
class EventLoop
{
private:
    DArray _periodicEvents;
    DArray _timers;
    milliclock_t _lastTimeout;
    bool _running;
    
    void sortTimers();
    DArray* findFiringTimers();
public:
    EventLoop();
    ~EventLoop();
    
    void addPeriodicCallback(PeriodicCallback* callback);
    void removePeriodicCallback(PeriodicCallback* callback);
    
    int periodicCallbacks() { return _periodicEvents.count(); }

    void addTimer(EventLoopTimer* timer);
    void removeTimer(EventLoopTimer* timer);
    int timers() { return _timers.count(); }
    
    bool running() { return _running; }
    void exit() { _running = false; }

    void run();
    
    static EventLoop* current();
};

#endif

Updated Arduino plugin

March 24th, 2009 § Comments Off on Updated Arduino plugin § permalink

I updated the Arduino plugin with the following features:

  • ATMega328 support
  • Arduino programming model (setup/loop instead of main)
  • Support for pde files (which are just C++ source files)

Download link

Let me know if you have any issues. (or send a quick note to say you use it)

Retrofitting an EasyDriver

March 5th, 2009 § Comments Off on Retrofitting an EasyDriver § permalink

Brian Schmalz’s easy driver, offered by SparkFun is a nice little stepper driver. There are two major deficiencies however: It is hard wired to eighth step mode, and the ground pin is opposite the signal pins. I decided to fix these as I reassemble the extruder.

The step mode on the A3967 is selected by pin 12 & pin 13. By bringing these pins low, we can select full step. This is achieved by clipping the pins from the pads and soldering a wire to ground – the same ground I wanted to route over to the signal pins. In order to seat the polarized header, I filed a pin slot and soldered the ground jumper.
Rev EasyDriver

Grid Beam Workbench

February 26th, 2009 § 4 comments § permalink

Grid Beam is a building technique developed in the 70’s. It is a simple technique which uses perforated square beams, connected by normal furniture bolts. I first learned about the technique from the book How to build with Grid Beam by new society publishers:

how-to-build-with-grid-beam

While the book is more of an history of grid beam, it does have some techniques and discussion on design. I think the technique has huge potential as a reusable building method

In need of a Dirty workbench, I decided to build a ‘stick’ making jig and attempt to build a bench. I was surprised by a few things:

  1. Beam stability is a function of the species, not the holes
  2. Hole accuracy is important, but not a deal breaker – the bolts pull the beams together nicely
  3. It takes about 20 minutes to drill a 6 foot beam – not including finishing touches like chamfer

Here’s my bench:
Bench

Tri-joint

Leg

Why is my home so Drafty?

February 22nd, 2009 § 3 comments § permalink

Couldn’t be the new Windows could it?

Front Door has a 1″ gap:
img_7050

Here’s the thermal:
ir20090213_1727

New Windows:
img_7049

No more gap:
img_7051

Smart home improvement

February 21st, 2009 § 6 comments § permalink

Our home is expensive to heat. Not only is that, but it feels drafty. We were convinced that our main heating problems were the Windows in our family room (where the thermostat is) – all were replaced without any effect.

Annoyed, we needed to find a way to pinpoint our homes problems. We turned to Thermal Imaging by Northwest Infrared to help us understand the problems. I am very impressed and highly recommend their service. I believe this is the single best thing that can be done to your home. It is inexpensive and pinpoints phantom energy loss. Northwest Infrared only does energy evaluation – you’ll get brutally honest information without the sales pitch.

Thermal analysis is a two step process:

  1. Depressurize the home taking accurate barometric measurements to determine how much leakage there is.
  2. Use a thermal imager to see where cold air is leaking into the home.

Common Problems

Attic access not sealed well:
ir20090213_1757

Pipe cutouts are huge openings to the crawl space:
ir20090213_1756

Downdraft vent in the cooktop isn’t sealed or baffled:
ir20090213_1735

Return air vent uses the floor joists instead of being ducted – drawing lots of air from the ‘area between floors’
ir20090213_1734

– And in our case those floor joists are open to the outside!
ir20090213_1729

Check out this ghostly image:
ir20090213_1731

Our Unique Problems

Our new windows weren’t sealed correctly (you can bet we’ll be bringing that up with Anderson Renewable):

ir20090213_1719

Our vents aren’t sealed correctly:
ir20090213_1757

Our worst offender is our heating room which is open to the crawl space. This room is essentially outside, but not insulated like it is an inside closet:
ir20090213_1741

Just a dog

ir20090213_1737

Video

I took some video during the process:

avr-libc realloc ‘fix’

February 11th, 2009 § Comments Off on avr-libc realloc ‘fix’ § permalink

I’ve been working on patterns for Arduino, which relies on a dynamic array for managing event loops, observables, and device abstraction. However, I was blocked by a critical failure in realloc. While I could have worked around it using malloc/free, but wanted to understand what was going on in libc.

One thing that struck me about realloc was the naming conventions; What was the difference between fp1, fp2, cp1, cp2? Some referred to a free block, some referred to a free pointer. I gave up and renamed the variables to something more readable.

The fatal flaw has to do with mixing sizeof(__freelist) and sizeof(size_t). In many cases, the difference results in indexing into the middle of a free list entry, thus corrupting memory.

(compare to avr-libc/libc/stdlib/realloc.c)

void* realloc(void *ptr, size_t newLength)
{
    struct __freelist *currentBlock, *nextBlock, 
        *currentFreeBlock, *previousFreeBlock;
    char *currentPointer, *nextPointer;
    void *memp;
    size_t largestBlockSize, sizeIncrease;
    
    /* Trivial case, required by C standard. */
    if (ptr == 0)
        return Malloc(newLength);
    
    currentPointer = (char *)ptr;
    currentBlock = (struct __freelist *)
         (currentPointer - sizeof(size_t));
    
    nextPointer = (char *)ptr + newLength; /* new next pointer */
    if (nextPointer < currentPointer)
    {
        /* Pointer wrapped across top of RAM, fail. */
        return 0;
    }
    
    nextBlock = (struct __freelist *)(nextPointer - sizeof(size_t));
    
    /*
     * See whether we are growing or shrinking.  When shrinking,
     * we split off a chunk for the released portion, and call
     * free() on it.  Therefore, we can only shrink if the new
     * size is at least sizeof(struct __freelist) smaller than the
     * previous size.
     */
    if (newLength <= currentBlock->sz) 
    {
        /* The first test catches a possible unsigned int
         * rollover condition. */
        if (currentBlock->sz <= sizeof(struct __freelist) ||
            newLength > currentBlock->sz - sizeof(struct __freelist))
        {
            return ptr;
        }
        
        nextBlock->sz = currentBlock->sz - newLength - sizeof(size_t);
        currentBlock->sz = newLength;
        Free(&(nextBlock->nx));
        return ptr;
    }
    
    /*
     * If we get here, we are growing.  First, see whether there
     * is space in the free list on top of our current chunk.
     */
    sizeIncrease = newLength - currentBlock->sz;
    currentPointer = (char *)ptr + currentBlock->sz;
    for (largestBlockSize = 0, previousFreeBlock = 0, 
         currentFreeBlock = __flp; currentFreeBlock; 
         previousFreeBlock = currentFreeBlock, 
             currentFreeBlock = currentFreeBlock->nx) 
    {
        if (currentFreeBlock == nextBlock && 
            currentFreeBlock->sz >= sizeIncrease) 
        {
            /* found something that fits */
            if (sizeIncrease <= currentFreeBlock->sz + sizeof(size_t))
            {
                /* it just fits, so use it entirely */
                currentBlock->sz += 
                    currentFreeBlock->sz + sizeof(size_t);
                if (previousFreeBlock)
                    previousFreeBlock->nx = currentFreeBlock->nx;
                else
                    __flp = currentFreeBlock->nx;
                return ptr;
            }
            
            /* split off a new freelist entry */
            currentPointer = (char *)ptr + newLength;
            nextBlock = (struct __freelist *)
                (currentPointer - sizeof(size_t));
            nextBlock->nx = currentFreeBlock->nx;
            nextBlock->sz = currentFreeBlock->sz -
                 sizeIncrease - sizeof(size_t);
            
            if (previousFreeBlock)
                previousFreeBlock->nx = nextBlock;
            else
                __flp = nextBlock;
            currentBlock->sz = newLength;
            return ptr;
        }
        
        /*
         * Find the largest chunk on the freelist while
         * walking it.
         */
        if (currentFreeBlock->sz > largestBlockSize)
        {
            largestBlockSize = currentFreeBlock->sz;
        }
    }
    /*
     * If we are the topmost chunk in memory, and there was no
     * large enough chunk on the freelist that could be re-used
     * (by a call to malloc() below), quickly extend the
     * allocation area if possible, without need to copy the old
     * data.
     */
    if (__brkval == (char *)ptr + currentBlock->sz && newLength > largestBlockSize) 
    {
        nextPointer = __malloc_heap_end;
        currentPointer = (char *)ptr + newLength;
        if (nextPointer == 0)
        {
            nextPointer = STACK_POINTER() - __malloc_margin;
        }
        
        if (currentPointer < nextPointer) 
        {
            __brkval = currentPointer;
            currentBlock->sz = newLength;
            return ptr;
        }
        
        /* If that failed, we are out of luck. */
        return 0;
    }
    
    /*
     * Call malloc() for a new chunk, then copy over the data, and
     * release the old region.
     */
    if ((memp = Malloc(newLength)) == 0)
        return 0;
    memcpy(memp, ptr, currentBlock->sz);
    Free(ptr);
    return memp;
}

avr-libc – Realloc bug

January 30th, 2009 § Comments Off on avr-libc – Realloc bug § permalink

A few months ago I proposed a refactor for the Arduino RepRap firmware. I was quick to code it up and build test cases on the desktop, then ported to the Arduino… Where it failed miserably. There the code languished as I spent time on it here and there. I attempted getting SimulAVR working, but it doesn’t support the Arduino chipsets (thought about adding support). I tried AVR Studio in a bootcamp’d windows install, but it kept hanging when debugging this problem. As a last resort, I ported the avr-libc memory apis to the initial desktop refactor, and was able to see the problem clear as day. I spent some time reducing the repro to the smallest form:

void testMalloc()
{
    size_t* array = (size_t*)malloc(4 * sizeof(size_t));
    free(array);
    array = NULL;

    array = (size_t*)realloc(array, sizeof(size_t));
    array = (size_t*)realloc(array, 2 * sizeof(size_t));
    array = (size_t*)realloc(array, 3 * sizeof(size_t));
    realloc(array, 4 * sizeof(size_t));
}

There is a bug in the free list manager in Realloc, specifically when growing a buffer into the next free entry. I was convinced I had a bug in a fairly large codebase, and whittled it down to this reproduction. I’m now playing with a couple of fixes, but need to figure out how to get a ‘blessed’ fix into lib-avr.

Stepping through the malloc into the free results in:

00000010 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000

Over the first realloc:

00000008 00000000 00000000 00000004
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000

Over the third:

0000000c 00000000 00000000 00000004
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000

And finally:

00000010 00000000 00000000 00000004
00000000 FFFFFFFC 00000000 00000000
00000000 00000000 00000000 00000000

At this point the free block pointer (__flp) points to the second line, with a size of 0xfffffffc. The next allocation fails.

First Grid Beam

January 22nd, 2009 § Comments Off on First Grid Beam § permalink

I’ve created my first grid beam using the grid beam jig I posted about. It took me about a half an hour to cut beam holes in an 8 foot beam. Like others, I’m curious if the number of holes will compromise the beam…

Only 5 more to cut.

Arduino for TextMate posted (beta)

January 12th, 2009 § Comments Off on Arduino for TextMate posted (beta) § permalink

I’ve added a page for the Arduino for TextMate plugin. Download from the dedicated page.