Tuesday, 13 August 2013

Using the Raspberry Pi to prototype for Arduino

The Raspberry Pi has a number of advantages over Arduino boards when it comes to writing programs. The main one is that it is much easier to debug a program on a screen than on a headless micro controller. You can set up an LCD display but they can be quite limited in terms of what you can show. You can also buy a debuging shield but that adds another layer of complexity that you might not want to get into.
You also have a greater choice of IDEs on the Pi. I use Eclipse because it is available on all platforms that I use. If you are only programming on the Pi I suggest you find a leaner one that does not use the Java platform. Java is a bit too resource intensive for the Pi.
If you are using an Arduino for simple data gathering then this might be an overkill but you have 32K of memory (on an UNO) and that allows you to deploy some non-trivial processing via your sketches.
My application was a fish tank pH balancer. I bought a temperature probe and pH stamp from atlas-scientific.com. I wanted to use those in combination to contiually monitor the pH of our fish tank and when it drops below a predefined level, use an output pin to release some pH balancing fluid. The pH stamp uses RS232 serial communication with the controller and the temperature probe uses an analog input pin.

Using Compiler Directives to Separate Code

The goal is for the major program logic to be the same for both platforms and to have the differences minimised, hopefully to the few lines of code where the platforms need different interfaces. They both can be programmed in the C language so that was the obvious choice. The Arduino uses two functions, setup() and loop() which can be easily emulated on the Pi (see Example 0). I also used the wiringpi library because many of the commands in wiringpi are the same as those on Arduino.
Example 0 - Emulating Arduino on the Pi
int main()
{
    setup();
    while (1)
    {
        loop();
    }
}


The basic tool for separating code was the #ifdef compiler directive. This, along with the #define directive allows you to controll which code gets compiles, e.g.
Example 1 - Code on the Pi
#define _pi_

#ifdef _pi_
    printf("This will appear on the Pi's standard output");
    // this will be compiled because _pi_ has been defined
#else
    lcd.print("Write to the Arduino's LCD");
    // this will be ignored
#endif

Example 2 - Code on Arduino

//#define _pi_

#ifdef _pi_
    printf("This will appear on the Pi's standard output");
    // this will be ignored
#else
    lcd.print("Write to the Arduino's LCD");
    // this will be compiled because the #define _pi_ has been    // commented out
#endif

Caveat 1 - #include is also a compiler directive

The first problem with this approach showed up when I needed to use different libraries on the different platforms.
Example 3 - Included Libraries
//#define _pi_

#ifdef _pi_
    #include "wiringpi.h" // will not be ignored by the Arduino compiler
    int someGlobalVariable; // this is code and will be ignored
#else
    #include <LiquidCrystal.h>
    // this may be completely screwed because of the Pi includes above
#endif

The only solution is to delete or comment out the Pi includes when you transfer the code onto the Arduino IDE. This is generally easy because the #include lines are at the top of the file.
Compiler directives allow you to separate your code but they don't make it any prettier. You can reduce the number if #ifdefs by moving them to separate functions. This will make your code more complex as well so it's a balance between lots of #ifdefs and function calls.

Beware when moving platforms - int != int

An issue to remember when programming for two different platforms is that the fundamentals of the hardware may be different. This is the case with the Pi and the UNO. The Pi is a 32-bit platform and the UNO is 16-bit. Therefore an int data type on the UNO has a maximum value of 32,767. Generally not a problem but my pH stamp needed a baud rate of 38400bps. This lead to the following, hard to track down problem:
Example 4 - Ints
int baud = 38400;

setup()
    {
#ifdef _pi_
    serialDevId = serialOpen(dev, baud);
    // will work fine - baud will equal 38400
#else
    Serial.begin(baud);
    // will not work at all - baud will equal -27000 or there abouts
#endif
    }

If you have a true constant that is needed in your program use a #define instead
Example 4 - Revised
#define baud 38400;

setup()
    {
#ifdef _pi_
    serialDevId = serialOpen(dev, baud); // will work fine
#else
    Serial.begin(baud);
    // will work now - the compiler will replace baud with 38400
#endif
    }

RS232 debugging

The Pi can also be used for debugging RS232 conversations.
The pH stamp is designed to have a simple conversation with the micro controller via RS232. The only feedback that it gave was a green flash when it got a message it liked and a red flash when it recieved a bad message. However simple the conversation, it is possible to get it wrong. For example, I mis-typed 38400 as 34800 and the stamp refused to respond. Then, I needed to get the termination right - it needs a return character and not line feed plus return.
The Pi also has an RS232 interface and with a simple terminal program like minicom you can see what the UNO is generating and simulate the conversation with the device.
You need to set the communications parameters up so that all three devices are talking the same dialect. The stamp needed 38400bps, no parity, 8 data bits and one stop bit and so the UNO and Pi have to comply. Make sure that Tx on the UNO is connected to Rx on the Pi and simularly Rx on the UNO is connected to Tx on the Pi and you should be able to happily talk RS232 with your UNO.

Caveat 2 - Arduino UNO has only one RS232 interface

When using RS232 on the UNO you have to remember that USB uses the same circuitry as the RS232 pins. Therefore, every time you want to download a sketch you must disconnect pin 0 (Rx) and pin 1 (Tx). If you don't do this you'll get a response like "the programmer is not responding". Frustrating and confusing.

Emulating Analog Input

One thing you cannot do on the Pi is analog input. To get this working you are on your own with the UNO. I found that a 16x2 LCD display was invaluable for this.
If you need to test the code on the Pi with values representing the analog input you need to somehow hardwire them into your code. One way would be like this:
Example 5 - Simulating Analog Input
float analogVals[] = {12.5, 16.22, 18.43, 22.5};
int currentAnalogVal = 0;

float readAnalogVal()
{
#ifdef _pi_
    ... // check that currentAnalogVal is not longer than the array
    return analogVals[currentAnalogVal++];
#else
    ... // get the real analog value
    return theRealAnalogValue;
#endif
}


Alternatively, if the analog value is not expected to change (like the temperature in my fish tank) much you can use a command line, e.g.
Example 6 - Command line input
float currentTemp; // a global variable

float readTemperature()
{
#ifdef _pi_
    return currentTemp;
#else
    // get the real temperature
#endif
}

#ifdef _pi_ // main is not needed on Arduino
int main(int argc, char * argv)
{
    currentTemp = (float)(atof(argv[argc - 1]));
    ...
}
#endif

Final word - %f is not supported

One odd little thing that I came across is that the %f string formating variable is not supported by the Arduino compiler. This might change at some time in the future but for now,
float floatVar;
char str[] = " ";
sprintf(str, "%2.2f", floatVar);

will give unexpected results. Instead, you must use:

float floatVar;
char str[] = " ";
dtostrf((double) floatVar, 5, 2, str);

Wierd but true!

I hope this helps - happy programming.