Evolution of a Blog

This blog has evolved as I have as a maker. It starts at the beginning of my journey where I began to re-tread my tires in the useful lore of micro electronics and the open-source software that can drive them. While building solutions around micro-electronics are still an occasional topic my more recent focus has been on the 3D Printing side of making.

Tuesday, October 30, 2012

Power Stability = RPi Stability

The troubleshooting area of the Raspberry Pi forum uses the "check your power" a lot in answer to user problems with crashes and other bad behavior.   At first glance one would almost wonder if this is their version of either the reboot it stupid or RTFM.  In fact, the RPi is enormously, hugely, and frustratingly, sensitive to power issues.

Through the process of integrating my RPi with the robot it is meant to control I have learned more than I really expected about power.    Here are some of the problems that I have had that have tied back to "get your power sorted stupid"!
  1. RPi would not boot as input power was too low.   Would cycle through the color splash, get to the point where power was not adequate, and crash
  2. Wireless connection would drop.   Often occurred at a point where current draw from somewhere other than the RPi would draw down voltage and away goes wireless.
  3. RPi would crash when servos were activated to control the sensor pan and tilt stalk.
  4. Webcam would drop offline.   Finally determined that this was also power related and that it was dropping when power levels to the USB hub dropped.
I have not measured the various thresholds when bad stuff happens but after making the following changes, and running on reasonably fresh batteries, the 'bot is stable:
  1. The Rover 5 comes with a six AA cell battery pack that connects to the Explorer PCB where it is regulated down to a 5v and 3.3v supply.   I added a second six cell battery pack and upgraded the rechargeable batteries in these two packs to high end cells (1850mAh).
  2. To further sanitize the power going to the RPi I added a dedicated regulator that draws directly from the battery circuit and is dedicated to the RPi
  3. Somewhere along the line I read the Explorer PCB manual and realized that it provides three power outputs with one dedicated for servos.   Once I connected the servos to their own power source the servo induced crashes stopped.
I still have one more improvement pending and that will be to have two 3.7v 18560 Li-Ion cells (4000mAh) to drive the RPi with the a dedicated regulator from the main batteries powering the USB hub.

Addendum of 1/11/2012: In the days following this post I was also able to determine that problems with my compass were ALSO power related!

Structure of 'Bot in Python

The goal of the first phase of my 'Bot project is to have the entire platform integrated but operating under control of a web application.   The web application will pass commands to Python code running on the onboard RPi, which in turn will drive the Arduino to manage the 'Bot.  I am close to completing this first phase but for a fair amount of tuning...of the software, the hardware, and the integration of the two!  

The structure of things, however, is fairly well established so this post is intended to provide a high level overview of the python code running on the RPi.

Initialization, a dictionary used for a command and control structure, and the primary processing loop comprise the module Main.py.   The other modules of the 'Bot, mostly called from within the Main.py processing loop, are as follows:
  • Bridge.py - Not in use right now but anticipated to be the home of autonomous logic.
  • Control.py - Gets commands from the PHP web control script, via the ControlDaemon.py, and passes them on for execution.
  • Mapping.py - My thought was that this module would generate a map of a room using the rangefinder.   It currently does a scan but until I figure out how to accurately pin the robot position I am not sure how this will work.
  • Navigation.py - Not in use now but for returning a bearing.  Will ultimately keep track of exactly where the 'bot is and where it is going.
  • Propulsion.py - Controls the treads to move forward, backward, and to turn. 
  • Proximity.py - Detects objects using the four corner infra-red detectors and issues a stop order for propulsion.
  • Py2Ard.py - Called by any module needing to interface with the Arduino.   Discussed here in this blog.
  • Radar.py - Controls the ultrasonic sensors in a collision and speed governing mode while moving forward.  During mapping operations is driven by Mapping.py to scan an rangefind obstacles.
  • Sensor.py - Controls the two servo's that orient the pan and tilt sensor platform on which the radar (rangefinder) and webcam reside.
  • Utility.py - Home for a number of utility functions and supported by UtilityDaemon.py where lower priority processing is conducted.
As mentioned above a PHP script called Robot.php runs in the cloud and is used for manual control of the phase 1 robot.

Sunday, October 28, 2012

Continued Saga of Robot Power Issues

My legacy of power issues continues but I think I have solved at least one.  When the 'bot initializes it fires up the servos for the pan and tilt sensor stalk.   The current draw from this action has been causing the RPi to crash. 

Reading the documentation is probably a good thing. Turns out the Explorer PCB has a separate power supply for servos that delivers them their own 5v supply 'dioded' down from the batteries.  Go figure.  Unfortunately the first time I hooked it up I managed to make things smoke as I connected what I thought where "+" and "-" connections to the power rail for the servos.   The two pins were meant for a jumper so what I did was install a short.   Smoke is not good but nothing seems to have actually tipped over which is surprising at best (how does something smoke but still everything works?).   The servo's are now isolated from the RPi and that is a good thing.

I have still not received the new Lithium Ion batteries that I have ordered to power the RPi independent of the rest of the 'Bot.  I did, however, get a new power regulator and have installed that to further isolate the RPi from power glitches.   This seems to help but I need to complete my battery upgrades (still waiting for some new nicad rechargeable's as well) to be confident that my power issues are behind me.

Friday, October 26, 2012

Time for some Python

I have chosen to use Python as the development language for my 'bot.   One reason being that it seems to be the 'standard' higher level language for the Raspberry Pi and is a default on the Wheezy distribution of the O/S.   A second is that it is a cool language!  I grew up with interpreted languages and really like being able to prototype code at the command line.   More importantly it is also a good OO platform and has a large eco-system of extensions.  

In any case...I am using it and liking it as well.   One thing that I do find a little bizarro is the spacing sensitive syntax.   Instead of relying on something like "{" to group code Python relies on levels of indent.   Makes sense and also enforces neat code but is still strange!

From this point in the blog posts will start to mix between the hardware, which has consumed me to date, and the software that will be driving it.

Learning Python By Lutz, Mark (Google Affiliate Ad)

Wednesday, October 24, 2012

More Compass Shenanigans

My compass continues to be possessed by evil demons.  I am pretty confident that living in the shadow of Brunel's Sounding Arch bridge outside of Maidenhead, home of one of the worlds busiest train lines, is part of the problem.  The HMC5883L is supposed to be accurate within a couple of degrees but mine varies all over the map and is convinced that east and west are not 180 degrees apart.   I have not had a chance to prove this by testing somewhere else but will at some point.

I could live with this behavior for now assuming it were reasonably stable...which it has not been.   For example, I tell the 'bot to turn 10 degrees to the right and it went into an infinite loop chasing a bearing that it never attained.  This is not good!   It turns out that my own electric motors kick up enough magnetic disturbance to freak it out (maybe in tandem with the electric train lines near us)?   I had originally mounted the device on the bread board in the center of the Explorer PCB but moved it to the rear and mounted it on a little mast.   I have now raised that mast and it seems to have helped...with at least one aspect of it's behavior!

My original thought was that I could use the compass when doing mapping (a function that I envisioned for my 'bot).   At this point I am not convinced that I am going to be able to do this given the required accuracy.   This leaves me with the quandary of how to know the bot's position.   I was hoping to have a bearing and use the range finder for distance.   Hmmmm.

For an update on this issue see this post.

Monday, October 22, 2012

Crude FTP Update in Python

I have been keeping the code for my 'bot on my Ubuntu laptop, updating it from my iMac, and then running a Python script to FTP it to the RPi.   Once major problem with this is that sometimes I will make a change and forget to run the update and then wonder at the abstinence of the 'bot to have changed it's behavior!  

I could simply cron the script to run every how often but this didn't feel very eloquent especially as the copy includes everything whether it has changed or not.  So not that my solution is terribly eloquent but I decided to write a quick script to only move code that has been changed and then put this inside a 15 second iteration loop.

I have one big try statement as my assumption is that a failure means the RPi is not online and that I should try later.

Code for "Crude FTP Update"


Wednesday, October 17, 2012

Arduino - Voltmeter

The following code illustrates both the reading of a voltage with the Arduino and then adjusting it using the code from the Secret Arduino Voltmeter article by Scott Daniels:

Do some initialization stuff (obviously this is not Arduino code as I am using my interface library):

    import time
    from Py2Ard import interfaceClass
    my_board = interfaceClass('/dev/ttyACM0')


Get the reference voltage and use it to calculate an adjustment from 5 volts:

    if my_board.referenceVoltage() != 0:
        print "Error"
    else:
        print
        print "--------------------------------------"
        refVoltage = float(my_board.returnValue) / 1000
        print "Reference................. {0:.3f} volts".format(refVoltage)
       
        adjustToRef = 1 + (refVoltage - 5) / refVoltage
        print "Adjust to Reference....... {0:.3f}".format(adjustToRef)
        print "--------------------------------------"
       

Get the voltage we are trying to measure and adjust it using the above factor.  Note that the '* .0049' converts the analog return from reading the voltage to a five volt scale.  The '* 2' then doubles that value to compensate for the voltage divider factor of 50%:

    if my_board.pinModeInput(5) != 0:
        print "Error"
    if my_board.analogRead(5) != 0:
        print "Error"
    else:
        mainVoltage = float(my_board.returnValue) *.0049 * 2
        print "Input - As Read........... {0:.3f} volts".format(mainVoltage)
       
        mainVoltage = mainVoltage * adjustToRef
        print "Input - Adjusted.......... {0:.3f} volts".format(mainVoltage)
   
    
The actual voltage at this point in time is 7.82 as read from our meter so calculate an error percentage:
    
        actualVoltage = 7.82
        error = (actualVoltage - mainVoltage) / actualVoltage * 100
        print "Error from Actual......... {0:.1f}%".format(error)
        print "--------------------------------------"
        print
   
    
The output from the above script is shown below.   Note that I have also adjusted the Arduino referenceVoltage script provided by Scott Daniels as he suggests in his article (adjusting for the inaccuracy of the 1.1v reference by actually measuring the 5v Arduino output and adjusting for the difference).

    --------------------------------------
    Reference................. 4.910 volts 
    Adjust to Reference....... 0.982
    --------------------------------------
    Input - As Read........... 8.026 volts 
    Input - Adjusted.......... 7.879 volts
    Error from Actual......... -0.8% 
    --------------------------------------


Caveat comes here...it feels like I am double adjusting things to arrive at the answer that I want rather than the right answer...  I may post this on Scott's page and ask him if my not electronically oriented brain has miss interpreted things badly!


Tuesday, October 16, 2012

Raspberry Pi Wireless Network Drops

My Belkin N150 network connection had been dropping on a regular basis. The RPi is still humming along (it is connected to an Arduino and is still commanding it happily) but no network.

I logged this problem on the Arduino Forum and got a number of suggestions with the ever present check of power high among the suggestions.   In fact, when I am on battery power I do have more problems as I am having power regulation issues.  This said, the problem does not go away when on wall power.

The thing that has most seemed to help was suggested by "Pluggy" and that was to add the following to the bottom of "/etc/network/interfaces":

    wireless-power off

I could not find a lot of documentation about this entry on the web though what I read implied that it disabled a standby power down.   This would make sense in terms of it helping if my drops were during periods of inactivity...but they are not...and it helped!

Not sure why but I guess one should not look gift horses in the mouth as my connection is very much more stable!?!?

Here is the whole thread on the Arduino Forum (which is a very useful place).

Arduino and Voltage Measurement - Voltage Divider

Every good 'bot should measure it's power levels and beam them home in a telemetry stream.   I really did want to keep track of my main batteries but I might have gone a little overboard when I tossed in measurement of power within the RPi and on the 5v Bus of the Explorer PCB.   There are a number of really good articles on the web that I used for this endeavor so I will start with them:

Voltage Divider - The Arduino can only measure a range to 5v so dividing a larger source is a must and this article is a good one for some help. 

Secret Arduino Voltmeter - Describes how to use the Arduino's reference voltage to adjust to variations in the input voltage that is driving your board.   The code provided by this example is now behind the method referenceVoltage in my interface library.  I will talk about this in my next post.


VOLTAGE DIVIDER

My main battery pack is six AA cells so my input voltage will be somewhere around 7.2 volts (1.2v x 6 cells).   This is clearly above the 5v maximum for the Arduino so we need this voltage divider!

For this demo I am using two resistors of 4.6k each.   This will divide the voltage in half (for more on how this works please see the article referenced above. I am not sure where the break points are but two low and you burn more power than you should and two high your accuracy suffers to the point of the Arduino not being able to get a reading.

The batteries are connected to the breadboard power bus outside of the camera frame.

The two resistors bridge the power bus to where I am taking my reading of the divided power.  

The input is 7.92 and the output is 3.96.  As near as I can tell that is a pretty good division by two!

The Explorer PCB has an area intended for soldered in additions but my 'bot has a breadboard mounted there so I can easily mess with things.   That is where the same division happens for the 'bot.

Sunday, October 14, 2012

Python to Arduino Interface - Part 2

This is the second part of a topic introduced a while ago here but not finished until now.  Note that Part 3 of this series of posts, including an updated library, is now available here.

One of the files packaged with the Python to Arduino Interface download is a short sample illustrating basic functionality.   Here is that sample with some additional commentary and a screen shot of it in operation:

Import the library

    import time     
    from Py2Ard import interfaceClass

The only argument the class needs is the port on which to find the Arduino

    # Specify the port as an argument     
    my_board = interfaceClass('/dev/ttyUSB0')

The two options below are useful for debugging.   Setting TraceOn will result in both sides of the interface making a log of what they are asked to do.   On the Python side this means writing to Py2Ard.log.   On the Arduino side it means caching the last 50 commands and then dumping them on command to the Python side.    Setting DebugOn causes the Python side of the interface to echo what is being done and the Arduino side to return long responses.    Obviously both of these options slow things down!

    print "---------------------------"
    my_board.setTraceOn()
    print "---------------------------"
    my_board.setDebugOn()
    print "---------------------------"

The following code causes the onboard LED to blink.    It is 1-1 analogous to what you would do in Arduino code directly.  Note that a -1 is returned from the Python side of the interface if there has been an error with the "returnDebug" attribute having the reason for the error.

    print "Pin Mode to Output" 
    if my_board.pinModeOutput(13) == -1: 
        print "Error:" + my_board.returnDebug
    print "Set High" 
    if my_board.setHigh(13) == -1:
         print "Error:" + my_board.returnDebug 
    time.sleep(1)
    print "Set Low" 
    if my_board.setLow(13) == -1:
         print "Error:" + my_board.returnDebug
    time.sleep(2)

Finally we dump the trace stuff that the Arduino side of the interface has been collecting.   if debug is currently on, as it is in this case, the dump will be shown on the screen as well as written to the log file.

    print "---------------------------"
    my_board.dumpTrace()
    print "---------------------------"

The screen shot of this sample running:

    ---------------------------
    ---------------------------
    returned False value Debug set to 1
    ---------------------------
    Pin Mode to Output
    @setLastTime
    returned False value Set last time for trace
    @pinModeOutput for pin 13
    returned False value Pin 13 set to Output
    Set High
    @setLastTime
    returned False value Set last time for trace
    @setHigh for pin 13
    returned False value Pin 13 set HIGH
    Set Low
    @setLastTime
    returned False value Set last time for trace
    @setLow for pin 13
    returned False value Pin 13 set LOW
    ---------------------------
    @setLastTime:09:25:26
    returned False value Set last time for trace
    @dumpTrace
    returned False value Trace dump follows
    Arduino -  - 0,Trace set to 1 - 12718 (12718)
    Arduino -  - 0,Set last time for trace - 12726 (8)
    Arduino - 09:25:23 - 0,Debug set to 1 - 12734 (8)
    Arduino - 09:25:23 - 0,Set last time for trace - 12742 (8)
    Arduino - 09:25:23 - 0,Pin 13 set to Output - 12750 (8)
    Arduino - 09:25:23 - 0,Set last time for trace - 12761 (11)
    Arduino - 09:25:23 - 0,Pin 13 set HIGH - 12770 (9)
    Arduino - 09:25:23 - 0,Set last time for trace - 13779 (1009)
    Arduino - 09:25:24 - 0,Pin 13 set LOW - 13788 (9)
    Arduino - 09:25:24 - 0,Set last time for trace - 15799 (2011)
    Arduino - 09:25:26 - 0,Trace dump follows - 15808 (9)
    Arduino -
    Arduino - #EOD#
    ---------------------------
 

The bottom of the screen shot (between the dashed lines) is the trace dump returned from the Arduino.   The second column is a time stamp set from the Python side to help you sync up what was happening.   The five digit number followed by the number in parenthesis is the execution time on the Arduino with the parenthesis being the time between the previous command and the one in parenthesis.

The trace functionality is a large part of the reason that I wrote my own interface.   If you are adding your own functionality to the Arduino side the ability to log stuff can be very useful.   Just call traceEntry with your own message for the trace log.

Thursday, October 11, 2012

Socket Communications (PHP to Python)

The first phase of this project is to get the 'bot working as a rather expensive RC toy with a web application accessed via my iPad being the control panel.


The idea here was one of simple expediency.  One of the other languages that I have decided to learn is PHP.   I have a forms development environment (FB4PHP) that makes it easy to build interactive applications (though it is still a beta product).  The thought was that the robot could periodically reach out via http and grab commands from the server where my little micro app was running.

Unfortunately the world of the internet is a world of latency and occasionally commands were taking way to long to get to the 'bot.   So naturally, this being an educational project, I decided that socket to socket communications would be just the trick!   The server process will run on the RPi on the back of the 'bot and the web application will be the client.  

It did turn out to have much improved latency over the http solution but I still have some tuning to do.   Luckily we have proximity detection for when a stop command just doesn't make it in time!

Here are the code snippets for each side of the conversation (first the server running in Python on the RPi):

    #  Create and open the socket will be listening on
    server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server_socket.bind(("192.168.1.95", 5555))
    server_socket.listen(5)

    # Inside processing loop we wait for a connection
    client_socket, address = server_socket.accept()
    # First get the size of the package we will be getting...
    packageLength = client_socket.recv(1)
    if len(packageLength) == 0:
        break
    packageLength = ord(packageLength)


    # ... then get the package itself
    controlCommand = client_socket.recv(packageLength)
    if controlCommand == "Terminate":
        break


    ... 
    ... DO SOME STUFF 
    ...

    # Respond to the client with current telemetry and status

    client_socket.send(chr(len(sendTelemetry)))
    client_socket.send(sendTelemetry)

    client_socket.close()


Then the client running in PHP on a server in the cloud:

    function CommunicateWithRobot($Commands) {
        // Create a TCP/IP socket.
        $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if ($socket === false) {
            return "Error: socket_create() failed: reason: " .
                socket_strerror(socket_last_error());
        }
        // Connect to the server running on the 'bot
        $result = socket_connect($socket, '99.99.99.99', '5555');
        if ($result === false) {
            return "Error: socket_connect() failed.\nReason: ($result) " .
                socket_strerror(socket_last_error($socket));
        }
        // Write two transmission, one of the size of the package,
        // and the second the package itself
        socket_write($socket, chr(strlen($Commands)), 1);
        socket_write($socket, $Commands, strlen($Commands));

        // Get the response from the server - our current telemetry
        $resultLength = socket_read($socket, 1);
        if (strlen($resultLength) == 0) {
            return "Error: emptyness passed back from server";
        }
        else {
            $Telemetry = socket_read($socket, ord($resultLength));
        }
        socket_close($socket);
        return $Telemetry;
    }

Tuesday, October 9, 2012

How Much Power is Needed?

This is supposed to be a 'bot that can scurry around the floor so obviously it needs to be battery powered.   The Rover comes with an integral six battery pack but that will not be enough so I am dedicating it to power the motors and sensors (other than the compass which I am powering from the Arduino's 3.3v power supply. 

The Raspberry Pi message boards indicate that the RPi runs at around 2.5 or so watts.   To this I have to add the  overhead of the USB hub, a wireless card, a webcam, and the Arduino.   Due to some power supply issues, namely a wimpy regulator that is supplying power to all the smart stuff, I have not been able to measure actual current draw from the RPi yet but I was able to see that all the rest of the stuff was drawing about 200-230 mA.

Assuming that the 2.5 watts (or 500 mA) is accurate, and adding 240 mA for the USB hub and the stuff it powers, I will burn 750 mA or 3.75 watts at 5v.   My battery pack for the smart stuff is the same as for propulsion so with old technology rechargeable batteries I would have between 600 and 1000 mAh at 7.2v or 4.3 to 7.2 watt hours.   Assuming that I lose 20% of my power to translation from 7.2v to 5v that leaves me with from 3.5 to 5.8 watt hours.   Burning 3.75 watts means from 0.9 to 1.5 hours of run time.

Power for the 'Brain'
New technology NiCd are supposed to deliver from 2000 to 2450 mAh which would yield run times of 3.6 to 4.4 hours which would be a huge improvement.   I have some on order (with a new power regulator) but for now have been using some worn out, old technology, NiCds and they don't really give me squat (though that may also be the wimpy power regulator that I am using since I fried my better one).

Here is a spreadsheet that calculates this stuff.

If you are doing ANYTHING with an RPi you will need to worry about power.  I can testify that it is VERY sensitive to how you feed it.

Friday, October 5, 2012

Current Robot Glamour Shot

I am posting this blog in retro fashion but thought that I would fast forward to the current moment and show a picture of how the robot is looking right now.
As of October 4th, 2012
There has been a fair amount of work to get to this point but there is a LOT more left to do.   In general, at this point, the hardware is largely integrated though there are still a number of things either not working 100% (compass accuracy) or not at all (encoders and current overload detection).

From a software perspective it is even more bleak as all the robot does at the moment is go backwards and forwards under command from its web interface!   It is sensitive to collision avoidance, it does upload images, and it can do a rudimentary mapping scan, but there is much more undone than done!

Wednesday, October 3, 2012

Triple Axis Magnetometer (e.g. Compass)

If one's 'bot is going to know where it is going to need to know in what direction it is traveling.   Enter the HMC5883L Triple Axis Magnetometer.  I am not sure why I got this particular device as all I need is one axis!   In any case it is another cool little device if only I could get it to work properly.   More on that later.

The device has four connection points, two of which are for 3.3v power.  I am using the power from the Arduino though I could have used it from the Explorer PCB.   The other two connection points are for the "I2C / TWI" interface used by the compass.  This is a serial type of interface that can have multiple devices like HMC5883L on the same circuit connected back to the Arduino.   The two connections are labeled SCL (clock) and SDA (data) on both the compass and on the Arduino.

Once the compass is connected you need to use the "Wire" interface to talk to it.   I scoured the internet for sample code and pulled the below from it for my purposes.   Note that what is shown is an excerpt from my  Python to Arduino Interface library so it will not run as shown but still gives you the idea.

There is a really good article on the Love Electronics website that discusses this device in detail.

Now a little about my current issue with this device...I can't get it to act like a sane compass.   You would think that west and east (90 and 180) would be on the same axis but this does not seem to be the case.   I can deal with the advertized accuracy of the compass (+/- 2 degrees) but I am seeing deviations from actual compass bearings of dozens of degrees.   At times it even flips by 90 or more degrees.

I am not sure that I can blame the device though.  My wife and I are living in a temporary rental while we navigate the painful English house buying process.  The place we are in is really quite nice and sits right on the Thames at Maidenhead.   Right on the Thames and next to the famous 'sounding arches' railroad bridge built by Brunel to host a major train line into London.    Major as in really busy and next to as in just about right under.    I am assuming the electromagnetic fields from the railroad are what is messing with my compass though I have not taken it afield to prove this.

    // I2C Arduino Library for a compass
    #include <Wire.h>
    // Reference the HMC5883L Compass Library
    #include <HMC5883L.h>
    // Store our compass as a variable.
    HMC5883L compass;
    int compassError = FALSE;


    // ***** Initialize Compass
    case 'I':
        outputLine = "0,";
        Wire.begin();
        compass = HMC5883L();
        compassError = compass.SetScale(1.3);
        if (compassError != 0) {
            if (debugOn == TRUE) {
                outputLine = "1,";
                outputLine += compass.GetErrorText(compassError);
                cmdSubInvalid = FALSE;
                break;
            }                   
        }
        compassError = compass.SetMeasurementMode(Measurement_Continuous);
        if(compassError != 0) {
            if (debugOn == TRUE) {
                outputLine = "1,";
                outputLine += compass.GetErrorText(compassError);
                cmdSubInvalid = FALSE;
                break;
            }                   
        }
        serialPrint(outputLine);
        cmdSubInvalid = FALSE;
        break;

    // ***** Compass bearing
    case 'B':
        xSum = 0;
        MagnetometerRaw raw = compass.ReadRawAxis();
        MagnetometerScaled scaled = compass.ReadScaledAxis();
        int MilliGauss_OnThe_XAxis = scaled.XAxis;// (or YAxis, or ZAxis)
        float heading = atan2(scaled.YAxis, scaled.XAxis);
        // Declination for London should be 24.73
        float declinationAngle = 24.73/1000;
        heading += declinationAngle;
        if(heading < 0)
            heading += 2*PI;
        if(heading > 2*PI)
            heading -= 2*PI;
        float headingDegrees = heading * 180/M_PI;

        int tempHeadingDegrees = int(headingDegrees);
        int tempHeadingDegreesF = (headingDegrees - tempHeadingDegrees) * 100;
        String outHeadingDegrees = String(tempHeadingDegrees);
        outHeadingDegrees += ".";
        outHeadingDegrees += String(tempHeadingDegreesF);

        outputLine = "0,";
        outputLine += outHeadingDegrees;
        if (debugOn == TRUE) {
            outputLine += ",Compass reading";
        }
        serialPrint(outputLine);
        cmdSubInvalid = FALSE;
        break;


More on the compass story here.