Links

Ben Laurie blathering

14 Dec 2010

Grown-up Arduino Programming

Filed under: Arduino/Freeduino,Programming — Ben @ 13:38

As I mentioned in a previous post, I am not a big a fan of the Arduino IDE. Since writing that, I’ve discovered I like it even less, because it does some ad-hoc mangling of what you write to turn it from a nearly-C language into genuine C. As a result, it is possible to write some C++ and get away with it, but whether C++ stuff works or not seems entirely random.

It may sound nuts to want to write C++ for a processor so small. But read on – it turns out you can do some nice things at essentially zero cost. But first you need a proper C++ toolchain. One way to get hold of it would be to install the Arduino IDE, in fact, since it uses it under the hood. But I did it from scratch on my FreeBSD system. This turns out to be mostly easy, but there were a couple of wrinkles worth writing down for the greater good.

Firstly, on FreeBSD the compiler is available as a port, so I just installed it with my favourite ports tool

portmaster devel/avr-gcc

(note that there are several variants based on different versions of the compiler available – this is the default variant which, at the time of writing, is based on gcc 4.2.4).

Sadly, although the libc part of the toolchain is available as a port, too, at the time of writing both versions (devel/avr-libc and devel/avr-libc-devel) are broken because they depend on defunct source code. So, I had to build this one by hand, starting with avr-libc-1.6.8.tar.bz2 from http://download.savannah.gnu.org/releases/avr-libc/. This is not too hard, just a slightly customised configuration followed by the usual make commands:

./configure --prefix=/usr/local/avr --host=avr
make
make install

Setting the prefix to /usr/local/avr is advisable as some things get installed immediately below the prefix and so could conflict with native compilers and libraries. However, it does cause some things to end up in /usr/local/avr/avr. Oh, well.

Next up, a test program is a good idea. avr-glibc comes with demo code, which can be found in /usr/local/avr/share/doc/avr-libc-1.6.8/examples/, but none of it is particularly well suited to an Arduino. So, I stole the Makefile from the demo sample and used this code instead of demo.c

#include <avr/interrupt.h>
#include <avr/io.h>

#define FLASH		PB5  // "Pin 13" (Arduino pin) - ATmega168 pin 19
#define CONTROL_PORT	PORTB
#define CONTROL_DDR	DDRB

static void ioinit(void)
    {
    CONTROL_DDR = _BV(FLASH);
    }

int main(void)
    {
    long n;

    ioinit();

    for( ; ; )
	{
	CONTROL_PORT &= ~_BV(FLASH);
	for(n=0; n < 300000; ++n)
	    ;
	CONTROL_PORT |= _BV(FLASH);
	for(n=0; n < 300000; ++n)
	    ;
	}
    }

and modified the Makefile to remove optimisation (essential, otherwise the delay loops get optimised away), select the right CPU (atmega168) and to modify these two lines

DEFS = -I /usr/local/avr/avr/include
LIBS = -B /usr/local/avr/avr/lib

-B is a new flag to me: it specifies where binaries and the crt0 files are found. The last ingredient is a way to upload to the Arduino. The utility avrdude can do this for you

avrdude -p m168 -P /dev/cuaU0 -c arduino -b 19200 -U flash:w:yourstuff.hex

Of course, delay loops are horrible, so my second attempt does this properly, using a timer interrupt. And this is where the C++ comes in: the “standard” way to set up the CPU is to write code like

#define FLASH		PB5  // "Pin 13" (Arduino pin) - ATmega168 pin 19
#define CONTROL_PORT	PORTB
#define CONTROL_DDR	DDRB

static void ioinit(void)
    {
    // PWM, 10-bit, phase-correct
    TCCR1A = _BV(WGM10) | _BV(WGM11);
    // Pre-scaler set to 1024
    TCCR1B = _BV(CS12) | _BV(CS10);
    // Set flash pin to output
    CONTROL_DDR = _BV(FLASH);
    // Enable timer 1 overflow interrupt
    TIMSK1 = _BV(TOIE1);
    sei();
    }

which is pretty revolting and involves a lot of manual-reading to understand. So, as is my habit when dealing with hardware, I tried wrapping it up in nice C++ classes to see what the run-time cost is. I won’t show the C++ classes here as they’re quite verbose and are a work in progress, but the net effect on the setup code is that it now looks like this

#define FLASH		PortB5  // "Pin 13" (Arduino pin) - ATmega168 pin 19

static void ioinit(void)
    {
    Control c;

    c.tc1.OC1ADisconnected();
    c.tc1.OC1BDisconnected();
    c.tc1.PWMPhaseCorrect10Bit();
    c.tc1.Prescaler1024();
    c.tc1.OverflowInterruptEnable();

    FLASH::Out(&c);

    c.Set();

    sei();
    }

which I hope you’ll agree is much more readable. The amazing thing is that, despite the increased verbosity, there’s no cost at all: this produces almost exactly the same assembler as the original code (it is in a slightly different order, though even that could be fixed if needed). The wonders of optimisation.

Note, by the way, the use of a PWM mode is simply because the demo code I borrowed from actually did use PWM – but pin 19 (where the LED is on a standard Ardunio/Freeduino) isn’t a PWM pin, so my code just uses the timer interrupt to time when to turn the LED on or off. The PWM is not really needed but the timer has to be in some mode, so I haven’t yet bothered to figure out a more appropriate one.

When I’ve got more stuff encapsulated in C++ I’ll start sharing the code.

27 Nov 2010

Fun With Freeduino

Filed under: Arduino/Freeduino,Sustainable Energy — Ben @ 16:43

As I may have mentioned before, I have a place in Wales. That place is pretty primitive, we have a solid fuel heating system, otherwise known as a Rayburn, and until recently the extent of the control systems in the whole house was a thermostat strapped to the hot water tank. When the temperature hits the set point, a pump switches on, which pumps the hot water round the radiators instead.

I did consider adding a second thermostat for a while: if you let the Rayburn get overexcited it can boil the water in the convection system before the hot water tank is up to temperature. This is pretty exciting to be in amongst, the whole place vibrates and clanks. So, I mused about putting in a second thermostat that monitored the temperature of the pipes and switched on the pump if they got too hot. But then I learnt to control the Rayburn better and it has become a non-issue.

But anyway, the prospect of a house with basically no control systems in it has me thinking about building my own: something I’ve long wanted to play with. So, I started looking at the Arduino. I have read that Arduinos are not actually completely open (I believe their PCB design is not, even though everything else is), but in any case, Freeduinos seem to be cheaper, and completely compatible, so I got one from an outfit called nueletronics.

It turns out that there’s a package based on Processing which lets you very easily write and upload C/C++ code for the board, available from the Arduino site, so step one is obviously to write some code to do stuff with the LEDs on the Arduino board. I won’t bore you with that, since the source comes with the Arduino software.

Of course, that’s only fun for a short while, so I had to move on to something relevant to Wales. My first thought was to measure temperature, and perhaps do something about avoiding freezing when we’re not there. In a past life I played with 1-wire devices from Dallas[1] so the natural choice for the thermometer was a Dallas 18B20, also available from nueletronics, as it happens. It turns out that you need a 4.7 kOhm pull-up resistor for the parasitic power, so I bought some of those from eBay (amusingly buying 100 costs the same as buying 10, it seems, so of course I bought 100 – the cost was something like 1p each). Then I needed something to connect it to the Arduino, so I also bought a prototyping shield (shields are what they call daughter boards for the Arduino) and a breadboard, also from eBay.

Then I realised I’d need to communicate the temperature to some more serious machine, for recording/monitoring purposes. So I also bought an Ethernet shield, again from nuelectronics (note that this is not a “standard” Arduino ethernet shield). While I was browsing I realised I could be both lazy and a little tidier by buying yet more stuff from nuelectronics, namely a real-time datalog and I/O shield and a DS18B20 module. Ultimately I’ll probably connect the thermometer directly, but for now the I/O shield provides wiring (the rest of the board is unused in this little project) and the DS18B20 module saves me having a breadboard floating around – the software is exactly the same as it would be if I’d wired it direct.

So, now I’ve got all the hardware, my plan was to read the 1-wire temperature over Ethernet. Simples. Turns out that this was pretty easy once you have all the info (which is not so easy to find!) and cobble together the various bits of software. But since it was actually pretty painful to figure out all the bits, let me explain a step at a time.

  1. Connect the Freeduino to your PC/Mac/whatever with a USB cable.
  2. Fire up the Arduino software and configure it for the right USB port (sorry, no great advice on how to do this in general – in my case I had to install drivers on XP to do it).
  3. Convince yourself that you’ve got this working by running a sketch (this is what the Arduino software calls a program) that blinks the LED.
  4. Once you’ve done that, then disconnect the USB (which disconnects the power) and stack the I/O board and the Ethernet board on top of the Freeduino, in that order (the Ethernet has to go on top because the connector is too tall to fit between two boards).
  5. Connect the DS18B20 module to P7 on the I/O board (the centre connector).
  6. Plug the USB back in.
  7. Unzip the Ethernet shield drivers into the libraries directory of the Arduino software. They should end up in a directory called etherShield.
  8. Load up this sketch[2].
  9. Set an appropriate IP address[3] in the myip array at the top of the code. Also set the same address in baseurl.
  10. Build and install the sketch.
  11. Point your web browser at the IP address you configured. You should see the current temperature and a button to refresh.
  12. If you want to monitor temperature over time (and you are on Unix), run a script like this:

    while true
    do
            wget -q -O - 'http://193.133.15.111/?cmd=2'
            echo -n ' '
            date +%s
            sleep 60
    done
    

And that’s all folks. My first Arduino thermometer 🙂

What next? Well, firstly, the Arduino software environment is actually pretty annoying if you’re a professional coder like me, so I plan to figure out how to do this “properly” with the avr-gcc compiler suite (the Arduino software uses this under the hood, so it can’t be that hard). Secondly it seems to be quite hard to find out what pins are actually used by shields and what they connect to, so I’m probably going to start trying to gather that info in a useful format (does it already exist somewhere?), at least for components I have. I’m also planning to actually control some heating using the Arduino – for that purpose I’ll almost certainly use this relay module with the I/O board, at least for the prototype.

I’ll blog about these when I get round to them.

[1] 1-Wire is a really cute technology that allows you to control and power multiple devices over a single wire (+ ground).
[2] This is the demo code from nuelectronics, tidied up a bit and with some extra functionality added.
[3] If you don’t know what “appropriate” means, you should probably ask someone. It’s hard to give general advice.

Powered by WordPress