After days of getting lost in TinyOS 1.1 and 2.1 low level hardware abstractions, the Harvard design BCI pulse oximeter now works in TinyOS 2.1.
Prior to writing the driver/interface code, I had to procure the pulseox hardware. Detailed descriptions of hardware, software, and applications are found at the Harvard CodeBlue website. I first ordered a Smiths Medical OEM Digital Micro Power Pulse Oximeter Board. I was able to get in contact with sales staff by emailing Smiths Medical. The pulseox board comes with a finger sensor and runs about $200.
Next, I needed an interface board to connect the BCI pulseox board to the 51 pin connector on the Crossbow IRIS. The Harvard CodeBlue source code has documentation for a PCB layout and suggests that you order the PCB from an online manufacturer. You also have to purchase some other components that must be soldered onto the interface board. Fortunately, I was able to “skip” this step with the help of Leo Selavo, who graciously supplied me with two interface boards. He emphasized that soldering the components onto the interface board requires a lot of skill and experience and if I were to try on my own that I would most likely break the PCB or components several times until I got it right. The setup with pulseox board, interface board, IRIS mote, and finger sensor is illustrated below:
With the pulseox board, interface board, and of course IRIS mote, I was ready to get my heart rate. The biggest challenge still lay ahead: writing code to interface with the pulseox. The CodeBlue pulseox code was written in 2005 for TinyOS 1.0. All of the low level UART and mote hardware interfaces have changed drastically since then, so the old code wouldn’t just work right out of the box. I had seen plenty of posts on the CodeBlue mailing list about implementations for the pulseox in TinyOS 2.x, but nobody had bitten the bullet and actually written anything.
What I wanted was to be able to use the pulseox like a sensor on the MTS300/310 sensorboard: call a Pulse.read() or Oxygen.read() command and get back a uint16_t with pulse or blood oxygen saturation. This is fairly straightforward since I had experience modifying the MTS310 sensorboard code to power on the sensors manually. I created configurations PulseC and OxygenC to provide SplitControl and Read interfaces to power on the sensor and read, respectively. I then wired PulseC and OxygenC to a PulseoxP, which functioned as an intermediary between the application and the low-level pulseox driver code. Like in the original CodeBlue source, I created a BciC configuration and BciP implementation to communicate with the pulseox directly through the UART and return data back to PulseoxP.
The key changes between the CodeBlue TinyOS 1.0 code and my implementation are all found in BciP (or BCIM.nc in the original CodeBlue source). Two issues come to mind. First, setting the mote hardware pins is done differently in TinyOS 2.1. Calls like TOSH_SET_PW0_PIN() are replaced by abstractions. I had to wire MicaBusC.PW0 to the GeneralIO interface in BciC and then call GeneralIO.set() in BciP.
The second difference is with the UART. TinyOS 1.0 uses the HPLUART interface for low level UART communication, but this has been replaced by Atm128Uart0C for the Atmel 1281 architecture. The CodeBlue source code initializes the low level UART to its default state, turns it off, and then sets the hardware registers to the desired UART configuration so that the mote can communicate with the pulseox board. After figuring out what the CodeBlue UART configuration code did, I can’t believe that you can actually write to the registers directly! Imagine if you could do that with user-level code on a PC! To enable transmissions and reception along with interrupts for the UART in TinyOS 1.x, you had to do the following:
// Enable tx/rx interrupts and tx/rx
outp(((1 << RXCIE) | (1 << TXCIE) | (1 << RXEN) | (1 << TXEN)) ,UCSR0B);
TinyOS 1.0 uses the outp() macro to set bits of a given register, but this macro does not exist in TinyOS 2.x. Instead, the UART register bits are configured in a struct with fields for each bit in the register. For the above example in TinyOS 2.1, the following union represents the control register in Atm128Uart.h:
/* UART Control Register */
typedef union {
struct Atm128_UCSRB_t {
uint8_t txb8 : 1; //!< UART Transmit Data Bit 8
uint8_t rxb8 : 1; //!< UART Receive Data Bit 8
uint8_t ucsz2 : 1; //!< UART Character Size (Bit 2)
uint8_t txen : 1; //!< UART Transmitter Enable
uint8_t rxen : 1; //!< UART Receiver Enable
uint8_t udrie : 1; //!< USART Data Register Enable
uint8_t txcie : 1; //!< UART TX Complete Interrupt Enable
uint8_t rxcie : 1; //!< UART RX Complete Interrupt Enable
} bits;
uint8_t flat;
} Atm128UartControl_t;
The flat uint8_t is a real slick way to convert all the elements in the struct to a single word, which can then be written to the register. Following the code that initializes the UART in tos/chips/atm128/HplAtm128UartP.nc, I was able to configure the UART to run with the pulseox board requirements: 4800 baud, double rate, transmission and reception interrupts enabled, no parity checking, 1 stop bit, and 8 bit word size. So, with the previous example to enable transmissions and reception, you do the following in TinyOS 2.x:
Atm128UartControl_t ctrl;
ctrl.bits = (struct Atm128_UCSRB_t) {rxcie:1, txcie:1, rxen:1, txen:1};
UCSR0B = ctrl.flat; // ctrl.flat
I ran my concerns by the CodeBlue mailing list, which elicited a reply from Prof. Matt Welsh that I was headed in the right direction. I finished my implementation, worked out some compile errors, and loaded the new pulseox and general data collection code onto a mote to see what would happen. Almost never does my TinyOS code run correctly on the first try — usually I spend hours or days debugging, but this time it was magic. A number jumped onto the screen in the data column of my PC Java application, which was connected wirelessly though a base station mote to the pulseox mote. The sensor readings seemed reasonable for a heart rate, for jumping up and down and breathing hard made it go up and laying down and trying to breathe slowly made it slow down.
I have made my code available here and any feedback, suggestions, or questions are encouraged. To use it, place the pulseox directory into the tos/sensorboards directory in TinyOS 2.x. In the makefile for your application, add the line “SENSORBOARD=pulseox”.


#1 by Vlado Handziski on June 17, 2009 - 7:48 am
Great work Matt! I’m glad you managed to find your way in the 2.1 code base. We definitely need more of the 1.x sensor board drivers ported to 2.x. Please consider uploading your code to tinyos-2.x-contrib: http://docs.tinyos.net/index.php/Contributing_Code_to_TinyOS
Best,
Vlado
#2 by sam on August 29, 2009 - 9:58 am
Hi matt
can you tell me which model of “Smiths Medical OEM Digital Micro Power Pulse Oximeter Board” you used?I guess you used the model with “CATALOG NUMBER
WW3711″,is it true?
and another question,Harvard university did something like this,Did you use the model that harvard use?
#3 by Matt on August 29, 2009 - 11:42 am
The link I provided in the post is the exact board I used:
http://www.smiths-medical.com/bci-oem/
#4 by CS on September 12, 2009 - 5:24 pm
Hi,
great to hear. Perhaps you can help me. I am working with IRIS Motes and MTS400. It is not possible to get any sensor data und TinyOS 2.x Do you have an idea where to finde the needed drivers? The TinyOS-Help Mailinglist and even Crossbow gives no answers.
Would be great if you can profide me a download or link.
Thanks, CS
#5 by Matt on September 13, 2009 - 1:03 pm
More than likely, nobody has drivers available for this hardware yet. You’ll probably have to write your own if you wish to use it. If you look at the Crossbow MTS user manual available on the Crossbow website, you’ll be able to figure out how to write a TinyOS driver for each sensor. The user manual has details on how each sensor is powered and how data can be retrieved on each sensorboard.