Bicycle Computer #3 – Sensors and Sensor Integration

This is the third in a series of articles demonstrating how someone with modest .NET programming capabilities can now write applications for embedded devices using the .NET Micro Framework and Visual Studio.  The first of the series can be found here.  Just as a reminder, if you would like to dive into more detail on anything we cover (or something that we didn't) let me know.  I am looking into the feasibility of putting this project out on www.codeplex.com so that the full project is available as it matures.  This is a little tricky since what is out there will not match any of the articles except possibly the last one. 

In this article, I will make the initial selection of sensors.  I find this to be the most fun of these projects – to get them responding to the real world.   Since I have no background with many of these sensors, there will be some trial and error in this process. First let’s look at sensing the speed and the cadence since those are easy. Bicycle computers traditionally use a magnetic switch to count rotations of the tires and we can extend that to rotations of the pedals.

clip_image002

At Digi-Key, I found this switch that senses at about 1 cm (Part # CKN6002-ND). This device costs about $7 in small quantities and down to $3.72 by the time you get to 1000. Since it is a switch, we can hook it up to any interrupt enabled GPIO (General Purpose Input/Output). The switch closes when the magnet comes within range. This means that we can either connect the switch to either the ground or a positive voltage that the GPIO can handle - depending on whether we want to switch when the pin goes high or low. We will connect the switch between the GPIO and the 5 volt pin. When we create the InterruptPort, we will tell the CLR whether to trigger an event when the GPIO pin transitions from low to high.

For this project, I elected to connect the switch to pin IO18 (an interrupt enabled GPIO ) on the EMX board from GHI that I am using. That makes our InterruptPort declaration look like:

speedInterrupt = new InterruptPort(GHIElectronics.NETMF.Hardware.EMX.Pin.IO18, true, Port.ResistorMode.PullDown, Port.InterruptMode.InterruptEdgeHigh);

and adding the event handler looks like:

speedInterrupt.OnInterrupt += new NativeEventHandler(switch_OnInterrupt);

With Intellisense, you can see all the options for the parameters here and the help has some useful definitions as well. For example, setting the Port.InteruptMode to InterruptEdgeHigh triggers the event when the pin is brought high by the switch closing. That also means that we need to set the Port.ResisterMode to PullDown which means that we need a pull-down resistor attached to this GPIO as well. The pull down resister insures that the GPIO doesn’t wander high enough to spuriously trigger the event.  Without the pull down resister connecting the pin to ground, the state of the pin when the switch is open is unknown.   This simple circuit looks like:

clip_image004

You can see how the resister will pull down the pin unless the switch is closed to drive it high. I used a 47K Ohm resister to limit the current flow and preserve the batteries.

Next I wrote a very simple event handler that just counts the rotations of the wheel:

private void switch_OnInterrupt(uint data1, uint data2, DateTime time)
{
    switchCounter++;
    Debug.Print("Pin " + _switchPin.ToString() + " : "  + switchCounter.ToString());
}

 

As you can see, I write the count to the Output window on each event to see if I am getting what I expect. The first thing that I noticed was that I would frequently get several events for one pass of the magnet by the switch. This indicated that the switch was bouncing open and closed several times when the magnet went by quickly. That is why I turned the GlitchFilter on (‘true’ in the second argument of the InterruptPort definition.) Playing around a bit, I confirmed that I could get by with a very low value on the GlitchFilter so that I won’t interfere with the possible fastest possible event rate.

Cpu.GlitchFilterTime = new TimeSpan(0, 0, 0, 0, 2);

Note that this is a global setting and will impact all interrupt input.

Incline vs Elevation Gain

The next sensor is a bit more of a challenge. We wanted to give the rider some feedback on how much of a hill they are going up or down. There are two approaches that we discussed. One is to use a pressure sensor to measure the relative elevation. The challenge here is whether it is sensitive enough to tell us what the rate of elevation gain is in a way that is useful to the rider. For example, if we have a long slow grade, do we have to pedal through half of it before we get enough pressure change to inform the rider? The advantage of the pressure senor is that they are very inexpensive.

The other option is an inclinometer. These are more expensive – especially for ones that will measure small changes. However, the ability to detect a change in the slope of the bike should be instantaneous.

Armed with very little knowledge about the options and how they are going to work, I decided to buy several sensors of each type and try them out. Let’s look at our source options. For the magnetic switch, a source like DigiKey or Mouser or one of the ‘professional’ suppliers was required because this is a relatively obscure item. For sensors, it is worth looking at the hobbyist sites like Sparkfun and Pololu. The ‘professional’ distributors have a larger selection but are less likely to have things like coding samples. For these sensors, I did a little of both. I found a pressure sensor that “Under ideal conditions, this SPI sensor can detect the pressure difference within a 9cm column of air.” The great news is that this as available with a breakout board that includes the pull-down resisters we talked about above as well as bypass capacitors already accounted for. (Sparkfun part # SEN-08161)

clip_image006

I also found a dual axis accelerometer that may be sensitive enough to test the incline approach to the feedback. It is an analog device (or PWM) with 17 bit precision.

clip_image008

Since I am not familiar with any of these sensors, I decided to get some additional ones to try from DigiKey. These are the Analog Devices Inclinometer (ADIS1620x/PCB) and the Bosch Pressure sensor (BMP085-SHUTL). These are also mounted on breakout boards.  Since the ADIS16203 uses the same intereface as the Sparkfun pressure sensor, I will start with those two sensors.

In upcoming articles, we will look at the drivers for these sensors and how they work out in this application.

Here is the full switch sensor call that I use for both speed and cadence.

using Microsoft.SPOT.Hardware;
using Microsoft.SPOT;

namespace NETMFBikeComputer
{
    public class SwitchSensors
    {

        public SwitchSensors(Cpu.Pin switchPin)
        {
            _switchPin = switchPin;

            Cpu.GlitchFilterTime = new TimeSpan(0, 0, 0, 0, 2);

            switchInterrupt = new InterruptPort(_switchPin, true, Port.ResistorMode.PullDown, Port.InterruptMode.InterruptEdgeHigh);
            switchCounter = 0;
            switchInterrupt.OnInterrupt += new NativeEventHandler(switch_OnInterrupt);
            switchInterrupt.DisableInterrupt();
        }

        private void switch_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            switchCounter++;
            Debug.Print("Pin " + _switchPin.ToString() + " : "  + switchCounter.ToString());
        }

        public void PauseSensors()
        {
            switchInterrupt.DisableInterrupt();
        }

        public void StartSensors()
        {
            switchCounter = 0;
            switchInterrupt.EnableInterrupt();
        }

        public ulong getSpeedCount()
        {
            ulong returncount = switchCounter;
            switchCounter = 0;
            return returncount;
        }

        static ulong switchCounter;
        private InterruptPort switchInterrupt;
        private Cpu.Pin _switchPin;

    }
}