Reading serial, what's the absolute most efficient way to do this?

The following is a driver for the Sparkfun 9DoF Razor IMU. The Razor runs a custom version of the AHRS firmware that waits for a query before outputting data. That way, I’m not dealing with a wasting time parsing constant stream of data.

I want to see if I can get even more efficient than that, though.

Anybody have any suggestions?

/*
 * Sparkfun 9DoF AHRS Razor IMU Driver
 *    Coded by Chris Seto August 2010
 *    <chris@ chrisseto.com> 
 *    
 * Note: This driver requires a slight modification to the IMU firmware. 
 * Please use the output.pde file that comes bundled with this driver to include 
 * the mod in your IMU's firmware.
 *    
 * This software is released under the Apache 2.0 license. 
 * Copyright Chris Seto, 2010
 * 
 * Chris Seto: Initial release (1.0)
 * Chris Seto: Removed angle remapping
 * Chris Seto: Support for accel readings (1.1)
 * 
 * */

using System.IO.Ports;
using System.Text;
using System.Threading;
using Microsoft.SPOT;

namespace Razor_9DoF_API
{
    class Razor_9DoF
    {
        /// <summary>
        /// Serial Port handle for the Razor
        /// </summary>
        private SerialPort _Razor;

        /// <summary>
        /// Yaw degrees (-180 to 180)
        /// </summary>
        public double Yaw
        {
            get
            {
                return GetValue(2);
            }
        }

        /// <summary>
        /// Pitch degrees (-180 to 180)
        /// </summary>
        public double Pitch
        {
            get
            {
                return GetValue(1);
            }
        }

        /// <summary>
        /// Roll degrees (-180 to 180)
        /// </summary>
        public double Roll
        {
            get
            {
                return GetValue(0);
            }
        }

        /// <summary>
        /// Accel X
        /// </summary>
        public double Accel_X
        {
            get
            {
                return GetValue(3);
            }
        }

        /// <summary>
        /// Accel Y
        /// </summary>
        public double Accel_Y
        {
            get
            {
                return GetValue(4);
            }
        }

        /// <summary>
        /// Accel Z
        /// </summary>
        public double Accel_Z
        {
            get
            {
                return GetValue(5);
            }
        }

        /// <summary>
        /// Keep the values here until we process them
        /// </summary>
        private double[] Raws = new double[6];

        /// <summary>
        /// Construct, set the recv event, bind the serial handle
        /// </summary>
        /// <param name="razorPort"></param>
        public Razor_9DoF(string razorPort)
        {
            // Bind the serial port
            _Razor = new SerialPort(razorPort, 57600);

            // Options..
            _Razor.ReadTimeout = 0;

            // Open the port
            _Razor.Open();
        }

        /// <summary>
        /// Get a certain word
        /// </summary>
        /// <param name="word"></param>
        /// <returns></returns>
        private double GetValue(int word)
        {
            ReadRazor();
            return Raws[word];
        }

        /// <summary>
        /// Check for valid data, parse
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ReadRazor()
        {
            // Capital P
            _Razor.Write(new byte[] {80}, 0, 1);

            Thread.Sleep(25);

            // Create the buffer
            byte[] buffer = new byte[_Razor.BytesToRead];

            // Read the serial buffer
            _Razor.Read(buffer, 0, buffer.Length);
            _Razor.DiscardInBuffer();

            Debug.Print(new string(System.Text.UTF8Encoding.UTF8.GetChars(buffer)));

            // Get the tokens in one sentence (Yes, I know, FUGLY!)
            string[] recv = new string(System.Text.UTF8Encoding.UTF8.GetChars(buffer)).Split('\n')[0].Split(',');

            // If it's complete, process it
            if (recv.Length > 7)
            {
                // Loop through the values in the sentence, parse 
                // them and add them to the raws array
                for (int i = 1; i <= 6; i++)
                    Raws[i - 1] = double.Parse(recv[i]);
            }
        }
    }
}

Hey Chris… this looks good. I like this approach a lot better than tying up background threads and interrupts processing data that won’t ever be read.

A few ideas on streamlining it:

In ReadRazor(), the buffer could be static and reused. If you make it reasonably big, you should be good. That will save some GC cycles.

You call ReadRazor() for each property read (the GetValue() call). I think in my code loop, I would call ReadRazor() once and the read out the individual variables. This would save cycles on each IMU processing loop and the values would all be more synchronized with each other since they were sampled closer to the same moment.

Also, to speed up parsing, since you already hacked up the IMU firmware, why don’t we come up with a binary fixed-length format so we don’t have to worry about this UTF8/Split business.

Hi Chris,
haven’t read my posting about that? In the quadrocopter thread page 6. I have 2 version of the 9dof parser. The first parses the data without the need of any firmware change, it uses char arrays of constant size, also I use my own parse functions for numbers. Numbers are parsed just in time. GC NEVER runs with this version =)

The other version is a parser for the ArduIMU, which is entirely binary. ArduIMU binary format has also checksums to ensure that the data is transmitted correctly! Therefor I have modified the 9dof firmware to output the ArduIMU format. This is the version I use in my quadrocopter project. It’s very fast and safe.

Also it looks like you are using the test firmware delivered originally with the razor 9dof imu. It’s not considered for real use I think.

Frequent calls to properties are also more time consuming for small embedded devices such as netmf.

Why not merging the best parts from our all work to one?
Fezzer should get hg repositories :wink:

Real-time coding on NETMF, nice work :slight_smile:

Good ideas so far. You could also cache the “last read” and data if you want to stick with “multiple” calls, and fetch new data if the cached data is older than X ms. Depending on your usage and calling patterns this may or may not be more efficient. If you are using multiple variables in a tight “loop”, which I think you would, I would return all of the data from one function.

What’s the purpose of the Thread.Sleep() after writing the data? Read() is blocking until the buffer.Length bytes (or less) are read, unless you changed ReadTimeout, you can just do away with this “sleep”.

zerov83’s binary protocol would be more efficient parsing I’m sure.

Hi Chris,

This was posted a long time ago, hence the reason for the ASCII.

I am using the AHRS code, like yours, my firmware is modified.

And yes, I have been planning on merging with yours :slight_smile:

… haven’t looked at the date of the first posting ;D

Why not making a google code project?

How about CodePlex? CodePlex is wonderful :slight_smile:

I see CodePlex also has mercurial now, which was my reason for google code. :smiley:

Perfect.

You want to create the page or should I?

Feel free to create one. Only for wide range of AHRS’? Or a universal library project for a wide range of UAV’s?

Hmmm, well, I was just thinking for the 9DoF, for the firmware mod and driver.

Ok, let’s create a lib for different ARHS’. When you create the project, please select Mercurial as Source control.

Done. what’ your CodePlex name?
http://9dof.codeplex.com

chriszero

You’re in.

Chris (either one)… can you add me. codeplex name=andrejk

^^ Added.

Chris, do you want to commit your initial Microframework UAV IMU code in?

Yes, I would do it, but Source Control isn’t avaliable…

Thats the message from codeplex. Could you have a look at this issue?

I have (or had, haven’t checked again) the same issue. they do that every once in a while. It won’t be down for long.