Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

SweetSpot - The Beginnings of a Glucose Meter Downloader

  In this installment of "Some Assembly Required" column, Scott Hanselman, a Type 1 Diabetic, uses .NET to download blood sugar numbers from a Glucose Meter. Along the way he lays down the rough scaffolding for a plugin system so other meters could be added easily. The application exports to XML or CSV for analysis within Microsoft Excel. Thanks to http://www.sweetspot.dm for their support and the initial idea.
Scott's Blog

Difficulty: Easy
Time Required: Less than 1 hour
Cost: Less Than $50
Software: Visual Basic or Visual C# Express Editions
Hardware: A FreeStyle or FreeStyle Flash Glucose Meter (sample data is included in the code for those who don't have a meter)
Download: Download

Summary: In this installment of "Some Assembly Required" column, Scott Hanselman, a Type 1 Diabetic, uses .NET to download blood sugar numbers from a Glucose Meter. Along the way he lays down the rough scaffolding for a plugin system so other meters could be added easily. The application exports to XML or CSV for analysis within Microsoft Excel. Thanks to http://www.sweetspot.dm for their support and the initial idea. Visit SweetSpot to see a greatly expanded version of this Meter Downloader and take a look at http://www.hanselman.com/fightdiabetes to make a donation to the ADA and Team Hanselman and this year's Walk for Diabetes.

The Idea

I've been diabetic since I was twenty-one. Actually, it happened a just few months before my 21st birthday. Needless to say, it's no fun. Most diabetes prick their fingers at least four times a day, often more. I test my blood sugar at least 10 times a day. This adds up to a lot of data points in a Glucose Meter. Checking your numbers gives you a snapshot of how you're doing, but the really valuable information lies in the analysis - but the data is trapped inside the blood sugar meter.

Most meters have some kind of cable that can be hooked up to your computer, allowing downloads of these hundreds of data points. My friend Adam from SweetSpot and I wanted to get at that raw data, geeks that we are, so we decided to write a program to do it.

Plugins

I'm using the FreeStyle Flash meter, but there's lots of different meters out there. We wanted a simple plugin system so that folks could write their own plugins and expand the application. First, I created this simple interface:

namespace C4FGlucoseMeterDownloader
{
    public interface IMeter
    {
        MeterResponse  GetMeterData(string configuration);
        Image GetMeterPicture();
        string GetDisplayName();
    }
}

It could be made more complex, but this one does the job nicely. The program will call GetMeterData passing in any saved configuration (like USB or Serial details...this was included for future use) when it's time for the plugin to fetch the data from the meter. We'll talk about the "MeterResponse" type later in this article.

We'll also call GetMeterPicture and GetDisplayName while we spin through all the meter types to fill out the details of our main interface as seen at right.

For this sample, we'll be keeping a Dictionary of IMeter instances at the ready. We spin through the currently executing assembly looking for types that implement IMeter. A future version might look for assemblies in a \plugins directory.

Type[] types = Assembly.GetExecutingAssembly().GetTypes();
foreach (Type t in types)
{
    if (typeof(IMeter).IsAssignableFrom(t) && t.IsInterface == false)
    {
        IMeter i = Activator.CreateInstance(t) as IMeter;
        string displayName = i.GetDisplayName();
        this.comboBox1.Items.Add(displayName);
        meterTypes.Add(displayName, i);
    }
}

When using reflection to find types that implement a certain interface, you use the very-not-intuitive method "IsAssignableFrom." It makes sense once you think about it, but it didn't jump out at me. I expected a method like "ImplementsInterface," but perhaps that's just me.

The image of the meter would be stored in the same assembly as the plugins, so we can put it out easily without external dependencies.

public Image GetMeterPicture()
{
    using (Stream sr = Assembly.GetExecutingAssembly().
GetManifestResourceStream("
C4FGlucoseMeterDownloader.Resources.freestyle_flash_gm.jpg"
)) { Image i = Image.FromStream(sr); return i; } }

Note the use of the "using" statement. I'm a huge fan of this statement. Be sure to use it anytime you're creating something that is IDisposable and you'll be sure to avoid object leaks.

Meter Communication - Talking Serial

The FreeStyle meter uses a 1/8" headphone jack connecting to a 9-PIN RS-232 serial port. I use a standard USB to 9-PIN adapter to hook it all together.  We could ask the user for the Serial Port that the device is connected to, but how would they know? Things like COM Port names are hidden better than ever before. Do I really want my Grandma going into the Device Manager to make an educated guess? Instead, since the FreeStyle has a fantastically simple protocol with easy to spot results, we'll just spin through every COM Port and send the command until one of them works.

There's many ways to tackle Serial Communication with .NET. There's the standard port.Read() way of doing things, but for simple "dump-style" communication, I like the SerialDataReceivedEventHandler. It's an event that's raised by the SerialPort when data shows up. You just have to call port.ReadExisting. It's nice and simple.

string[] portNames = System.IO.Ports.SerialPort.GetPortNames();
foreach (string portName in portNames)
{
    if (found == true)  {  break; }
    try
    {
        Debug.WriteLine("Trying " + portName);
        port = new SerialPort(portName);
        port.BaudRate = 19200;
        port.StopBits = StopBits.One;
        port.Parity = Parity.None;
        port.DataBits = 8;

        port.DataReceived += new SerialDataReceivedEventHandler(PortDataReceived);

        port.ReadTimeout = 500;  port.WriteTimeout = 500;

        port.Open();
        port.Write("mem");

        long timeout = 0;
        while (port.IsOpen)
        {
            System.Threading.Thread.Sleep(500);
            if (++timeout > 10 && found == false)
            {
                break; //give up after 5 seconds..
            }
        }
    }
    //ERROR HANDLING REMOVED FOR BREVITY...SEE THE CODE
    finally
    {
        if (port.IsOpen)
        {
            port.DataReceived -= PortDataReceived;
            port.Close();
        }
        port = null;
    }
}

Here we just keep sending "mem" - the FreeStyle's dump command - to each port we have until one gives us something. The eventHandler sets the "found" flag.

void PortDataReceived(object sender, SerialDataReceivedEventArgs e)
{
    string next = port.ReadExisting();
    found = true;
    System.Diagnostics.Debug.WriteLine("RECEIVED:" + next);
    result += next; //Not using a StringBuilder is inefficient, truly, but this is easier and allows us to watch the the string for "END" without worrying about it being truncated like "EN" and "D"
    if (result.Contains("END"))
    {
        Properties.Settings.Default.LastPortFound = port.PortName;
        port.Close();
    }
}

We keep going until we find the "END" string returned by the Glucose Meter. Again, there's literally a half-dozen ways to have done this. Simply spinning in a while(port.ReadBytes()) loop would do the job as well, but this is a good way to introduce the SerialDataReceivedEventHandler. We just keep appending the received data and the SerialPort class keeps things in order and ensures nothing is dropped.

NOTE: You can run this sample even without a Glucose Meter. Simply uncomment out the line marked "//HACK:" in the MeterFreestyle.cs file and the Meter plugin will return sample data.

Parsing

The data returned from the Meter is quite regular, for example:

128 Oct 09 2006 23:44 26 0x00
122 Oct 09 2006 22:18 26 0x00
261 Oct 09 2006 21:14 26 0x00
070 Oct 09 2006 21:14 26 0x00
0x3B2C END

The first number is the blood sugar value. Ideal numbers are between 80 and 120mg/dl. Diabetics aim for these kinds of values, but often their blood sugar varies. Checking blood sugar often allows us to get back to normal as soon as possible, thereby avoiding side effects of chronically high sugar. Take a look at "Scott's Diabetes Explanation: The Airplane Analogy" for more details on Diabetes, or the Diabetes Section of my blog.

We want to parse this data and turn it into both XML and CSV. I created a simple data structure to get us started, and marked it up for XML Serialization:

namespace C4FGlucoseMeterDownloader
{
    public class MeterResponse
    {
        public MeterResponse() { GlucoseReadings = new List<GlucoseReading>(); }

        [XmlIgnore]
        public List<GlucoseReading> GlucoseReadings;

        [XmlArray("GlucoseReadings")]
        public GlucoseReading[] ArrayGlucoseReadings
        {
            get { return GlucoseReadings.ToArray(); }
            set { GlucoseReadings = new List<GlucoseReading>(value); }
        }
    }

    public class GlucoseReading
    {
        public GlucoseReading() { }

        public GlucoseReading(DateTime date, decimal BG)
        {
            this.BG = BG;
            this.Date = date;
        }

        public decimal BG;
        public DateTime Date;
    }
}

As we parse the raw data, we return GlucoseReadings to build up a MeterResponse.

protected GlucoseReading ParseResultRowToGlucoseReading(string data)
{
    //TODO: Error Handling
    //227  Oct  11 2006 01:38 17 0x00
    string BGString = data.Substring(0,5);
    decimal BGValue = decimal.Parse(BGString.Trim(), 
System.Globalization.CultureInfo.InvariantCulture); string timeString = data.Substring(5,18); DateTime recordDateTime = DateTime.Parse(timeString,
System.Globalization.CultureInfo.InvariantCulture); return new GlucoseReading(recordDateTime, BGValue); }

And the resulting data is then exported automatically into CSV and XML:

public void SerializeAsXml(MeterResponse r, string fileNameSeed)
{
    XmlSerializer x = new XmlSerializer(typeof(MeterResponse));
    using (Stream s = File.OpenWrite(fileNameSeed + ".xml"))
    {
        x.Serialize(s, result);
    }
}

public void SerializeAsCsv(MeterResponse r, string fileNameSeed)
{
    using (StreamWriter sw = new StreamWriter(fileNameSeed + ".csv"))
    {
        sw.WriteLine("Date,Glucose");
        foreach(GlucoseReading gr in r.GlucoseReadings)
        {
            sw.WriteLine(String.Format("{0},{1}",gr.Date,gr.BG.ToString()));
        }
    }
}

Analyzing the Data

The XML could be styled with XSLT, manipulated by another program, or you could store it in a database. The CSV file is easily consumed by Excel as seen in the screenshot at right.

Make sure you use a "scatter plot" in Excel if you want to see all your data. Axes in Line Charts that use Dates as their date type in Excel have all values appearing at midnight. Since Glucose data is time (even minute) sensitive, use a scatter chart to get accuracy.

Don't worry, those aren't my actual blood sugar numbers, it's just generated sample data.

Conclusion

Take a look at http://www.hanselman.com/fightdiabetes to read my personal story and make a donation to the ADA and Team Hanselman and this year's Walk for Diabetes.

There's things that could be extended, added, and improved on with this project. Here are some ideas to get you started:

  • Add support for more Glucose Meters.
  • Add embedded charts and graphs.
  • Create a PDF report and email it to your doctor or family.
  • Make it a more generic medical application that takes data from blood pressure machines or other medical data-gathering devices.

Be sure to check out http://www.sweetspot.dm and get in on their beta. They've already extended this application significantly with multiple meter support, ClickOnce Deployment, lots of special sauce, and an extensive online analysis system. Thanks again, have fun and have no fear when faced with the words - Some Assembly Required!


Scott Hanselman is the Chief Architect at the Corillian Corporation, an eFinance enabler. He has thirteen years experience developing software in C, C++, VB, COM, and most recently in VB.NET and C#. Scott is proud to be both a Microsoft RD and Architecture MVP. He is co-author of Professional ASP.NET 2.0 with Bill Evjen, available on BookPool.com and Amazon. His thoughts on the Zen of .NET, Programming and Web Services can be found on his blog at http://www.computerzen.com. He thanks his wife and son Zenzo for indulging him in these hobbies!

Tags:

Follow the Discussion

  • Worth BeckerWorth Becker

    We did this for our Microsoft Imagine Cup project for 2006. We used the infrared capabilities of one of the acu-check glucometers to pull data to a pocket pc, correlate it with diet and exercise information logged on the pocket pc and charted it. The pocket pc would sync with our website allowing doctors to log in, and view his patients data daily. The goal was to cut log books out all together. Our application was called TypeZero.

  • MadlobsterMadlobster

    Hmmmm.  I have a Freestyle Freedom and can't get it to return any data from the unit.  It comes back NULL.  Anyone know if the "mem" command is the same on this unit?

  • Bernard FarrellBernard Farrell

    Nice job Scott

    I'd love it if the XML was a microformat, this might be the start of developing and promoting a standard format for blood glucose readings.

    If we did this for independent software packages (microformat import/export) then maybe meter manufacturers would think about supporting it in their meter download software going forward.

    More about this is in my paper in the March 2007 edition of the Journal of Diabetes Science and Technology (http://www.journalofdst.org/March2007/pdf/JDST-Vol-2-Abstracts/VOL-1-2-CGM1-FARRELL-ABS.pdf).

  • FokkoFokko

    I own a lifescan One touch ultra, is it possible for this meter to read out the memory ? does anyone has expirence with this glucose meter ?

    Thanks in advance,

  • Bruce S.Bruce S.

    Do you happen to know where I can find the pin configurations to make a data cable for the FreeStyle Flash Diabetic Meter? All I need is the wireing diagram on what wires connect from the DB plug to the 1/8 inch mini plug and I can make my own and save myself some money since I cannot work due to medical problems.

    cbs12@waltonville.net

  • BurchBurch

    This is awesome! I always wondered if I could hook up to one of my blood glucose meters.

    thanks!

  • spsp

    pinout diagram for Freestyle data cable:

    Tip ---> pin 2 of DB9

    Ring ---> pin 3 of DB9

    Base ---> pin 5 of DB9

  • padmanabhanpadmanabhan

    Hi,

    I'm using same glucometer, but i try to read data through serial port using PHP. Please can anyone have idea or anybody used?

    Thanks,

  • rprp

    Does anyone know how to reset the user data points to zero on the the FreeStyle Flash??  Not just the calendar, but the actual readings?

    Thanks.

  • RolysentRolysent

    Hi!! I just would like to know if you have certain DLL or OCX for freestyle flash glucose meter. Is this code of yours can be compiled in Visual Studio .Net Professional Edition? please send me some feedback... Thanks and more power...

  • RussRuss

    Anyone have the schematic pin out for a Free Style USB to stereo cable?

  • chui101chui101

    The Freestyle connector is a serial interface, and there is no USB controller on board. You can build the TRS->DB9 cable that sp mentioned above and plug it into a USB-serial adapter.

  • GeorgeGeorge

    I understand "Tip" (ground or +) and "Ring" (battery or -) in the context of telephony, but not data.  Does "Tip" refer to XMIT (transmit) and "Ring" refer to RCV (receive)?

    Thanks

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.