Tuesday, June 21, 2011

The day GPS Selective Availability (SA) was switched off

I was going through some old archived projects recently and came across an interesting GPS track log I recorded on 2 May 2000.

For us GPS early adopters, this was a remarkable day: the day GPS became truly useful for terrestrial applications. I had left my Garmin GPS35 logging to capture the event, and here it is:


It may seem that the GPS was violently oscillating about the place and then stopped half way through the period of time recorded on the chart. Believe it or not it was completely stationary for the entire duration! So what happened?

First a little GPS history. GPS was conceived by the US military as an exclusively military navigation aid in the early 1970s.

In September 1983, Korean Airline Flight 007 was mistaken for a hostile military aircraft by the Soviets when it accidently strayed into their airspace. It was shot down with the loss of 269 lives. Following that disaster it was decided that GPS would be made available for civilian use to prevent such tragedies from happening again.

Naturally the military were concerned that the super accurate navigation provided by GPS could be abused. To address these concerns two signals were transmitted: one for military receivers with the full 10m accuracy, and a separate signal for civilian receivers with about 100m accuracy. 100m was considered adequate for aviation and marine applications at the time (but not accurate enough to allow a missile to hit a target). This degraded signal was known a Selected Availability (SA).

During the 1990s with the end of the cold war and an increasing civilian use of GPS the thinking at the US government was that the economic and safety benefit of removing SA far outweighed the risks.

So on 1 May 2000, the White House issued a press release announcing the end of SA with the following quote attributed to President Bill Clinton:
"The decision to discontinue Selective Availability is the latest measure in an ongoing effort to make GPS more responsive to civil and commercial users worldwide…This increase in accuracy will allow new GPS applications to emerge and continue to enhance the lives of people around the world."
Right on cue, on 2 May 2000 the SA was disabled and that moment was caught here. Only longitude is displayed for clarity, but latitude and altitude would have been oscillating with a similar amplitude. And a big difference it made, opening up a mass market for consumer GPS devices.

[Aside: SA was temporarily removed at some point [can't find a date] during the first Persian Gulf War in 1991 because of a shortage of military GPS receivers. Instead civilian units were used. These were just as accurate with SA switched off. SA was restored again on 1 July 1991.]

References:
http://ngs.woc.noaa.gov/FGCS/info/sans_SA/docs/statement.html

Updates:

27 Dec 2018: I thought I had lost the original data file, but found it a few days ago on an old archive CD-ROM while cleaning for Christmas. I created a better chart showing both east-west deviation and also a north/south deviation as function of time.    Data and Gnuplot source files are available here: https://github.com/jdesbonnet/GPS_SA_switch_off

Tuesday, May 24, 2011

Arduino to Android IO on the cheap (aka Poor Man's NFC)

Summary: This article describes how to implement a very low bandwidth one way communication channel between an Arduino (or any other microcontroller) and an Android device using nothing more than about a meter of magnet wire, a resistor and diode. Links to a software sketch for the Arduino and the Android source code is included.

A cheap and simple way of communication between a microcontroller (eg Arduino) and an Android device is surprisingly difficult to achieve. The most practical means of accomplishing this is to use a Bluetooth or WiFi module adding significantly to the cost of a project (in the order of $30 - $60).

This is a little hack that allows very low bandwidth communications in one direction for practically no cost. It's not practical for most applications, but I thought the idea was sufficiently interesting to explore and write up in a blog post.

You'll need the following:
  • An Android phone or device which features a magnetometer (electronic compass). I'm not aware of any device without one.
  • An Arduino or other microcontroller (my examples are for the Arduino)
  • About 1 meter of  enameled copper wire (magnet wire), although any thin insulated wire will do.
  • The Android 'Tricorder' app (available free from the Android app store)
  • A 120 ohm resistor 
  • A diode (optional)
Make a coil by wrapping the 1 meter of magnet wire around something cylindrical  (eg AAA cell) with a diameter about 1cm. You should get about 30 turns from 1 meter. The diameter or number of turns is not important as long as they are in that ball park. Wrap a light thread around the coil to help keep its shape.



Connect the coil as illustrated in the schematic above. The 120 ohm resistor limits current draw to about 40mA which is the maximum allowed [1]. The purpose of the diode is to protect the Arduino from an EMF kick when pin 13 is driven low. This is known as a  flyback diode. With only 30 turns and an air core, you'll probably get away without using it. But it's good practice to include it.

Tricorder app displaying magnetic field. For the
HTC Desire the signal is strongest at the bottom
left of the LCD where the magnetometer is located
This is likely to vary from device to device.
Now to locate the magnetometer. It's location will vary from device to device. This is where the tricorder comes in handy. First load this sketch on the Arduino before you connect the coil.


/**
 * Generate 1Hz square wave on pin 13
 */
void setup() {
  pinMode(13,OUTPUT);
}

void loop() {
    digitalWrite(13,1);
    delay(500);
    digitalWrite(13,0);
    delay(500);
}


When the coil is disconnected you should see the Arduino's LED blink. Now connect the coil. The LED should stop blinking at this point because the coil shorts the LED -- this is expected. Start the Tricorder app and switch to MAG mode and start scanning. Methodically scan the coil across the surface of the Android phone until you start seeing a strong square wave. Where the amplitude of this signal peaks is where the magnetometer is located. You should see a similar square wave if you position the coil at the same location on the bottom surface of the device. Use some tape to keep it positioned there.

Now we have a way of signalling the Android. The temptation at this point is to connect the coil to the Arduino UART and to start firing data at 9600bps. Unfortunately this isn't going to happen for a few reasons:

We are limited by the magnetometer sampling rate available to us by the Android OS. Here is a histogram of time-between-samples from a HTC Desire running Froyo (Anrdoi 2.2) at maximum sampling speed:



We can see here that almost all the samples come in at between 18ms and 25ms. There are a few outliers going all the way up to 45ms but there numbers are so few we can ignore them. This means we are limited to a maximum of a 40Hz sampling rate. If we drive the coil with a digital IO line that immediately caps our maximum bandwidth at 40bps.

But does the magnetometer respond quickly enough?

This Arduino sketch energizes the coil with a square wave of starting at 500Hz and decreases to about 1Hz over a few seconds.

int i;
int d = 1;
void setup() {
  pinMode(13,OUTPUT);
}

void loop() {
  for (i = 1; i < 500; i+=d) {
    
    // Increase increment as frequency decreases
    d = (i>>6) + 1;
   
    digitalWrite(13,1);
    delay(i);
    digitalWrite(13,0);
    delay(i);
  }
}


Here is the magnetometer data:

As expected high frequencies (on the left) are not be detected. It seems that we only get good pickup when the frequency drops to about 6Hz!

For encoding I'm going to use NRZ (non return to zero) similar to that used in RS232 with a bit period of 140ms (about 7 bps). The Arduino's UART lower limit is 300bps, so I can't use it to generate the signal.  So I'm going to 'bit bang' the signal in software. Likewise, on the Android I'm going to have to decode in software also.

#define BIT_DELAY 140

int i;
char *text = "Hello World! ";

void setup() {
  pinMode(13,OUTPUT);
}

void loop() {
 
  char *s;
  s = text;
  while (*s != 0) {
    transmitByte(*s++);
  }
  
  delay (BIT_DELAY*10); 
}
/**
 * Bit bang a byte
 */
void transmitByte (byte c) {
  
  // Start bit
  digitalWrite (13,1);
  delay(BIT_DELAY);
  
  // Data bits
  for (i = 0; i < 8; i++) {
      digitalWrite (13, ( (c&0x80)!=0 ? 1 : 0) );
      delay(BIT_DELAY);
      c <<= 1;
  }
  
  // Stop bit
  digitalWrite (13, 0);
  delay(BIT_DELAY);
}

Here it is in action:


The Android source is available here.

Potential improvements


My simple example uses a digital IO line to drive the coil. The Android magnetometers typically have at least 8 bits of resolution per channel [2]. It may be possible to squeeze another few bits per symbol by driving the coil with a DAC [3]. For example, 4 power levels can be used to encode 2 bits per symbol. The odd bit error  can be corrected using forward error correction.

Also the magnetometer has three channels (X,Y and Z). It may be possible to construct a set of coils that excite the X,Y and Z channels independently … multiplying bandwidth by a further x3. Another thought: I've only discussed digital communication with the Android. Some applications might only require an analog signal.

These ideas are left as an exercise for the reader, but I’d love to hear how you get on if you try it. 

Footnotes


[1] Googleing "Arduino pin 13", one is lead to believe there is a current limiting resistor in series with the MCU pin. However looking at the schematics, the tap for the pin 13 header comes *before* the resistor: so the resistor does not limit current. I only discovered this at the end of the experiment while proofing this post. Omitting the resistor did me no harm, but would advise to include the resistor to be safe. We need as much current as possible (more current means more magnetic flux in the coil). The Arduino Duemilanove datasheet specifies a maximum draw of 40mA on a IO pin, so 120 to 150 ohms should do the trick. If you need more current you'll need to use a transistor.

[2] The HTC Desire uses a AKM Semiconductor AK8973 3-axis magnetometer which uses a combination of a 8 bit ADC and a 8 bit offset DAC to yield up to 12 bits of equivalent resolution spanning +/- 2000µT of magnetic flux.

[3] The Arduino does not have a true DAC, however DAC functionality can be achieved by passing the PWM output through a low pass filter.

Thursday, May 19, 2011

Changing Mimetex fonts

An example of Mimetex in action
Mimetex is a handy little add-on for web applications that will render math expressions in TeX/LaTeX syntax and convert them directly to GIF images. It's just one binary executable file which is normally deposited into the "/cgi-bin" directory of the web server. Everything is compiled into the executable, including the fonts. Which is nice: no configuration... no font directories... Simple!

The mimetex distribution comes with the default Computer Modern fonts built in. I was recently asked to change these fonts to a sans-serif font. In case anyone else needs to do something similar I'm documenting what I did in this post.

I'm assuming a Ubuntu Linux distibution (although it should work with other Linux/BSD distros) and you'll need a full TeX/LaTeX distribution installed.

Then download this script. It's good practice to review it first before running it.

The script will:

1) Download Mimetex (if you have not already done so)
2) Render the sans-serif version of Computer Modern to a format that can be used by mimetex
3) Patch mimetex.h to use the sans-serif fonts instead of the default Computer Modern font
4) Compile a new mimetex binary executable file

The results?

Before:

After:

What's going on under the hood?

The starting point is a Metafont description of the new font. Fortunately there is a sans-serif variant of computer modern available in Metafont format (cmss and cmssi) included in the TeX distribution.

The process of adding new fonts involves rendering the Metafont file into a bitmaps (.gf) files. Those binary .gf files are then converted into a ASCII printable format using "gftype -i" and then the gfuntype utility (shipped with Mimetex) will convert that to a C header (.h) file. The fonts are run off at various sizes and all the resulting .h files are concatenated together to form 'texfonts.h' which is compiled into the executable.

Is it possible to compile in Type1?

As far as I can tell, there is no easy way to use fonts in formats other than Metafont. And I can't find any way to convert Type1 and other formats to either Metafont or the intermediate .gf bitmap files. I'd be delighted to hear about any suggestions if this is possible.

Also it should be pointed out, there are probably better alternatives to Mimetex available nowadays, eg Mathtex and Mathjax.

Saturday, April 16, 2011

'Smart' electricity meter based on Efergy Elite monitor, part 2

Summary: In part 1, I documented the Efergy Elite’s radio protocol and wrote a PIC program that ran in a tight loop decoding the data and sending it to a serial port. This post improves upon the decoding technique used allowing for other applications (eg temperature monitoring) to multi-task on the same PIC. A simple control loop multitasking framework using interrupts to capture time critical events is explained.

The ultimate goal of this project is a electricity consumption monitor that provides real time feed back and also logs historical power use for analysis and report generation. For the moment my focus is feeding the data to a PC/laptop. In reality a more power efficient logging device will be needed. But that's for a future post.

Recently I wanted to update this receiver to include data from temperature sensors and output data from both the Efergy Elite and the temperature sensors to a single serial port.

However the tight timing loops which are continuously running make it near impossible to perform any other task. Fortunately the PIC has lots of useful hardware that can be brought to bear on this problem. Timing pulses with a loop is rather clumsy, inaccurate and wasteful of system resources. Almost all PICs have timers and interrupts which can be used for this task.

By using this peripheral hardware a lot of the decoding grunt work can be happen silently in the background, freeing up the CPU for other stuff.

Simple Multitasking with a PIC


Although not a proper multitasking processor such as a Pentium, a very simple form of multitasking can be easily achieved with a PIC. This comprises a master loop which calls application service routines for each application in turn. This is often called “simple control loop multitasking”.

The application service routines check to see if there is any work to be done. If not it immediately exits and the control loop then pass control the the next application service routine. If there is work to be done, the application service routine attempts to tackle as much as can be achieved in it’s allocated time. If all the work cannot be completed in time, it must relinquish control prematurely and continue the task on the next call.

void main (void) {

  // Initialize apps
  initAppA();
  initAppB();
  initAppC();
  initAppD();

  // Main loop
  while (1) {
    appA();
    appB();
    appC();
    appD();

    // Clear watchdog timer
    CLRWTD();  
  }
}

Architecture diagram of simple control loop multitasking with interrupts used to capture time critical events.


In order to be responsive the main loop must iterate many times a second. The iteration frequency will depend on the nature of the application(s). 1kHz is a common choice. At 1kHz the entire loop must complete in 1ms and each app service routine must take no more than 250µs (assuming there are 4 applications requiring equal CPU time).

These routines must be written so as not to use more than their allocated time slice. If a task cannot be accomplished in that time frame it must be broken into smaller work units.

Unlike a modern preemptive multitasking operating system such as Linux these constraints are not enforced. A badly written service routine will affect the performance of the entire system. For example a deadlock in one application will cause the entire system to fail. One safeguard against rogue applications is to enable the Watchdog Timer and clear it on each iteration of the loop. Should one application lock up the system will reset.

Interrupts can be used to capture time critical events. The ISR (Interrupt Service Routine) is called (almost) the instant the event occurs. Its job is to store the event for the attention of the application service routine whenever it gets control of the processor. It’s often a good idea to buffer these events in case more than one arrives before the service routine gets its turn to run.

Efergy Elite decoding


A quick recap on the Efergy Elite setup. A sensor mounted in the utility meter box transmits by 433MHz radio a power reading to the display unit every 6, 12 or 18s (configurable). On the display unit PCB I tapped into the radio base band output and connected this to a low cost PIC MCU. It does the job of decoding the signal and sending a nicely formatted record to the PC via serial IO.



Bits are encoded on the base band signal using digital FSK (Frequency Shift Keying). Three square wave cycles of 2ms duration in total (ie 1500Hz) is a logic 0 and 4 square wave cycles of 2ms duration (ie 2000Hz) is a logic 1.


Each packet is prefixed by a synchronization header of 0xAB 0xAB and 0x2D. This is followed by 8 octets (bytes) of data and one octet checksum. See previous post [link] for details.

The decoding process starts with the radio hardware which outputs the base band signal. Using pulse width measurements the symbols (bits) are decoded. First the bits are are shoved onto a 16 bit shift register until the end sync header is identified (ie the contents of the shift register will read 0xAB2D). Then 8 octets of data is read followed by one checksum octet (the checksum is the arithmetic sum of the 8 data octets).


The choice of what to implement in the ISR and what to implement in the application service routines is sometimes not clear. The rule of thumb is to keep the ISR as small and fast as possible. I had originally considered using the ISR to time pulses and send pulse durations to the application layer. As there can be as little as 250µs between pulses the entire main loop would have to execute in under that time. That’s only 250 instructions on a 4MHz clocked PIC. Not enough!

For just a few more instructions the pulse to symbol decoding can happen in the ISR thus allowing for up to 2000µs between application service routine calls.

To improve matters further, instead of storing one symbol at a time, I push the decoded symbols into a buffer (implemented as a 8 bit shift register) allowing for up to 16000µs between calls.

For the first attempt I connected the radio base band signal to the INT pin (RB0), traped INT interrupts and used Timer1 to measure the pulse widths. Timer1 is a 16 bit timer which can be clocked using the system clock divided by 4. If you’re using the 4MHz internal clock that’s a convenient 1µs per clock tick.

There are a few gotchas when using interrupts. For example: variables shared between the ISR and main program must be declared with the “volatile” modifier. When accessing state information which can be manipulated by the ISR, it is often necessary to briefly disable the interrupt while reading to ensure that an interrupt does not case the state to appear corrupted.

Measuring pulse width with INT interrupt.
The PIC can be programmed to trigger INT on a rising edge or falling edge. But not both at the same time. To measure pulse widths I’ll need both. The work around is to alternate the triggering mode.

I’m measuring inverse pulses (ie the duration of time when the signal is at low voltage). So when waiting for a pulse to arrive, set to trigger on a falling edge. In the ISR, record the clock and set trigger mode to rising edge. The next invocation of the ISR will occur at the end of the pulse. Subtract the clock from the previously recorded value -- that is the pulse width. Now reset INT to trigger on a falling edge again for the next pulse.

An alternative implementation using Capture/Compare


Microchip PICs have so much peripheral hardware that there is often several ways of solving the same problem. The Capture/Compare module is probably better suited for this problem. The INT approach does have one advantage though: if there is nothing else to do the device can be put in low power mode with the SLEEP instruction. Activity on INT will automatically wake the device up. I’ll post that implementation in a separate blog post.

The main control loop


My main loop looks like this:

// Main control loop.
 while (1) {

  // AppA: Decode Efergy Elite signal. 
  if (efergy_elite_interrupt_decode()) {

   // Have a complete record

   // Extract single phase power reading (12 bits)
   power = ((ee_buf[3]&0x0f) << 8) | ee_buf[4];

   // Display full record on the serial port
   for (i = 0; i < 8; i++) {
    writeHex(ee_buf[i]);
    putch (' ');
   }
   crlf();
  }


  // AppB: Blink LED if power use above threshold
  if (power > POWER_THRESHOLD) {

   // Cause LED to blink every 4096 iterations
   if ( (t&0x0fff) == 0) {
    DEBUG_PIN = !DEBUG_PIN;
   }
  } else {
   // LED off if below threshold
   DEBUG_PIN=0;
  }

  // Increment iteration counter
  t++;

  // Clear watchdog timer
  CLRWDT();    
 }
}

This has just two simultaneously running tasks:
  • One to decode telemetry from the Efergy Elite and write the data to the serial port
  • One to blink a LED if power levels are above some threshold
Instead of putting each application into a separate service function as in the template above, I've got blocks of code in the main loop as the code is short and simple.

The LED blinker (AppB) illustrates how to write these application service routines. A simplistic approach would be to compare power consumption against the threshold then turn on the LED, execute a delay loop for 0.5s, turn the LED off and delay again for another 0.5s. That will work, but violates the principle that the code should execute as quickly as possible (taking a whole second to complete a blink).

A smarter approach is implement LED blinking with a timer. I added an iteration counter to the main loop which acts as a low accuracy timer (it seemed easier than using the PICs built in timers). This way the code only takes a few instructions to execute.

The code which writes the record data to the serial port could be optimized in a similar way, writing just one character to the serial port per iteration. But that's an exercise for another day.

The Hardware

You will need the following:
  • Efergy Elite electricity monitor (obviously)
  • A PIC 16F627A or 16F628A (or any other PIC, but you may need to modify the code a little).
  • 1 LED
  • 1 resistor
  • A suitable PIC programmer. I use a PICKit2 from Microchip (about $30)
  • Breadboard (or other prototyping system)
  • Logic level Serial IO to PC interface (see text below)
This hack requires that you solder a wire to the base band pin or test pad of the Efergy Elite receiver unit. See my previous blog post on how to do this.  You will also need a wire for the battery ground. Beware that this will void your warranty -- so only proceed if you know what you are doing.
Schematic of Efergy Elite decoder using a PIC 16F627A or 16F628A (or equivalent).
Once upon a time interfacing a serial IO port to a PC was a straightforward if somewhat laborious task. PCs had a RS232 port which used +/- 13V levels which was not compatible with logic levels (usually 0 and 5V). You needed a level converter. A MAX232 chip + associated capacitors being one of the most popular solutions. Fortunately, nowadays the RS232 is very rarely found on modern equipment. So how do you interface serial IO at logic levels to a PC? There are many options available, and sadly may require that you purchase some hardware. But the good news is that all of these options are useful tools which can be repurposed for other projects. Here are some options:
  • FTDI cable available from SparkFun.com (DEV-09718), CoolComponents.co.uk and many others. Cost about $18.
  • The Dangerous Prototypes Bus Pirate available from SeeedStudio.com ($30 -- but you get a whole load of extra functionality with that. Well worth it.)
  • An old Nokia phone cable
  • Any development board that features a USB/Serial IO chip can probably be easily hacked for this purpose, including the Arduino, TI MSP LaunchPad etc. (Although you'd probably want to ask yourself if you are better off using the MCU on the development board instead of a separate PIC. If you want to see this decoder implemented for other MCUs leave a comment at the end of the blog post and I'll see what I can do.)

Annotated photograph of setup required to decode telemetry from a Efergy Elite electricity monitor and relay to PC.

A note on powering the PIC: as this is currently very much a prototype I haven't considered how I might power this device. Right now I'm using the PICKit2 to supply power via the ICSP (3.3 - 5V should work). Practical options are to pull the battery positive terminal out of the Efergy Elite display unit and use that to power the device. If connected to a PC via a FTDI cable, these cables usually have a +5V line which will deliver plenty of current. Or use a separate battery or wallwart power supply.

If running successfully this program will output to the serial port each record from the power sensor as it arrives (every 6, 12 or 18s). The output looks something like this:


EM 0.2.0
00 0D 5A 40 6E 00 01 00 
00 0D 5A 40 6E 00 01 00 
00 0D 5A 40 73 00 01 00 
00 0D 5A 40 6E 00 01 00 
00 0D 5A 40 6E 00 01 00 
00 0D 5A 41 BF 00 01 00 
00 0D 5A 41 BE 00 01 00 

As for implementing the temperature sensors I mentioned at the start -- that's for another day. So right now it’s not a terribly exciting application, but could form the basis of something more useful.

The code can be downloaded here. It’s written for the HI-TECH PICC compiler, and has been tested on PICs 16F627A and 16F628A. It should be easy to adapt to other PIC compilers and PIC devices, or indeed other MCU families.

Thursday, March 3, 2011

A makeshift MRF24J40MB module breadboard socket

I wanted to experiment with a Microchip MRF24J40MB 802.15.4/ZigBee module, but having only one (and at $30 each) I wasn't willing to commit to soldering it into place just yet. So came up with this idea: a makeshift temporary socket comprising pin headers and an elastic band!


Does it work? Almost. I found that a few pins weren't making electrical contact. Not one to give up easily, I stripped some stranded wire and wrapped individual strands around the pins. This extra bulk and roughness around the pins seemed to have done the job.

If you try this, double check all your contacts with a continuity checker. This is obviously not something you want to use in a production setting!




Update (17 Aug 2011): The MRF24J40 based modules from Microchip have come down in price significantly since this I wrote this blog post. In single quantities the MRF24J40MB is now about $15, and the MRF24J40MA about $10.

Thursday, February 17, 2011

Using the Microchip ZENA ZigBee/802.15.4 network analyzer with Linux

Summary: The Microchip Technologies Inc ZENA is a 2.4GHz 802.15.4 (ZigBee, MiWi etc) network analyzer. It comes with free Windows-only software. There is no support for Linux and cannot be used with powerful tools like Wireshark. There is very little documentation available for the device hardware. In this post I've documented reverse engineering efforts by myself and others and present a small C utility for pcap packet capture on the Linux platform. 
Update (23 July 2013): Mr-TI has written an updated version of this tool to work with the latest version of the ZENA hardware: https://github.com/Mr-TI/ZenaNG

Update (3 Mar 2012): This article was originally written in Feb 2011. Microchip have just released a new version of the ZENA which comes in a USB 'thumbdrive' casing. This new version is based on their own MRF24J40 transceiver chip. This article relates to the older model.

The ZENA is a 802.15.4 network analyzer from Microchip Technologies Inc. It costs about $130.  It comes with free (closed source) Windows software. If you need encryption support you'll need to separately purchase an enhanced version of the ZENA software for $5.

The Windows ZENA isn't bad. It displays plackets as horizontal slabs and color codes each part of the packet. There is some basic filtering functions. For more comprehensive reviews see [1] and [2]. If you need to use this with Linux or you need a long packet capture you're out of luck. The export from the Windows software is limited to about 21 kB of data at a time.


Documentation on the hardware and USB protocol is scant. Joshua Wright did some reverse engineering work on the ZENA back in 2009 and wrote a Python script that selected a 802.15.4 channel of your choice and dumped raw data from the USB port to the screen. Due to the relatively high cost and limitations (packet injection not possible) of the ZENA his work on this device as shelved in favor of the cheaper and more flexible alternatives (eg Atmel's RZUSBStick)

I've taken Joshua's work and brought it to a point where it can be used to capture long ZigBee packet dumps in pcap format and analyze them with Wireshark.

The ZENA hardware

The ZENA comprises 75mm x 40mm PCB with a PIC 18LF2550 MCU clocked at 16MHz, a MRF24J40   CC2420 2.4GHz 802.15.4 radio transceiver, PCB antenna (with the option of installing a SMA connector for external antenna) and a mini-USB port to communicate with the host computer and provide power for the device.

The PCB seems to have been designed with other applications in mind also, having an unused area for a button cell on the underside and pads for switches on top. For some odd reason the MRF24J20 chip is unmarked.  Update: I've been informed that the ZENA predated Microchip's own MRF24J40 so the Texas Instruments CC2420 chip (originally made by Chipcon before being acquired by TI) was used instead. The markings must have been removed by Microchip for commercial reasons but are still just visible under the right light.


There are 3 green LEDs marked D2, D4 and D3. D2 appears to be a heartbeat which flashes with a one second on / one second off duty cycle while the ZENA is in sniffer mode. The rhythm is disrupted whenever a packet is received. D3 also flashes with network activity but I cannot deduce a pattern. D4 is illuminated only during the powerup test sequence.

ZENA USB Protocol

The ZENA (vendor ID 0x04d8 , product ID 0x000E) presents itself as a Human Interface Device (HID).

"lsusb" is a useful utility to discover metadata about USB devices. The following command will dump information about the ZENA to screen: "lsusb -v -d 04d8:000e". This output looks like this (I've highlighted lines of interest in bold):

Bus 002 Device 003: ID 04d8:000e Microchip Technology, Inc. 
Device Descriptor:
  bLength                18
  bDescriptorType         1
  bcdUSB               2.00
  bDeviceClass            0 (Defined at Interface level)
  bDeviceSubClass         0 
  bDeviceProtocol         0 
  bMaxPacketSize0         8
  idVendor           0x04d8 Microchip Technology, Inc.
  idProduct          0x000e 
  bcdDevice            0.00
  iManufacturer           1 
  iProduct                2 
  iSerial                 0 
  bNumConfigurations      1
  Configuration Descriptor:
    bLength                 9
    bDescriptorType         2
    wTotalLength           41
    bNumInterfaces          1
    bConfigurationValue     1
    iConfiguration          0 
    bmAttributes         0x80
      (Bus Powered)
    MaxPower              100mA
    Interface Descriptor:
      bLength                 9
      bDescriptorType         4
      bInterfaceNumber        0
      bAlternateSetting       0
      bNumEndpoints           2
      bInterfaceClass         3 Human Interface Device
      bInterfaceSubClass      0 No Subclass
      bInterfaceProtocol      0 None
      iInterface              0 
        HID Device Descriptor:
          bLength                 9
          bDescriptorType        33
          bcdHID               1.01
          bCountryCode            0 Not supported
          bNumDescriptors         1
          bDescriptorType        34 Report
          wDescriptorLength      34
         Report Descriptors: 
           ** UNAVAILABLE **
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x81  EP 1 IN
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1
      Endpoint Descriptor:
        bLength                 7
        bDescriptorType         5
        bEndpointAddress     0x01  EP 1 OUT
        bmAttributes            3
          Transfer Type            Interrupt
          Synch Type               None
          Usage Type               Data
        wMaxPacketSize     0x0040  1x 64 bytes
        bInterval               1


USBSnoop and usbmon are another useful tools which can be used to eavesdrop on USB traffic when used with the supplied Windows software.

This is what I know about the ZENA USB interface so far (lots gleaned from Joshua Wright's blog post and the Python script linked above):
  • There are two end points: a control channel (0x01) and a packet channel (0x81)
  • There is only one known control function: to select a 802.15.4 channel in the range 11 to 26. This is achieved by allocating a 64 byte buffer, zeroing it and setting the channel number (11 to 26) in byte offset 1 (second byte of the buffer). Send this to the control end point 0x01 using  usb_interrupt_write()
  • To read 802.15.4 packet data allocate a 64 byte buffer and issue a usb_interrupt_read() to end point 0x81. If there are no packets available, usb_interrupt_read() will block until a packet arrives or the timeout period is reached. A timeout can be detected by checking the return status for -110.
  • The first 6 bytes in the buffer are a ZENA header. Byte 0 is always 0x00. 
  • ZENA header bytes 1 - 4 is a packet timestamp. Bytes 1 and 2 is the fraction of a second in 2^-16 second units (first byte is the least significant). Bytes 3 and 4 is the seconds part of the timestamp.
  • ZENA header byte 5 is the number of bytes of data remaining. The remaining data at this point is the 802.15.4 packet data (excluding FCS) plus two reception quality bytes at the end.
  • If the length field is greater than 58 bytes then one or more additional usb_interrupt_read() requests must be issued to retrieve the remaining data. The data will continue at byte offset 1 (for some reason the first byte returned by usb_interrupt_read() to this device is always 0x00)
  • The ZENA does not return the 802.15.4 FCS. You will need to recompute it if you need it. It does however return a single FCS OK bit in the last byte of the data.
  • Instead of the FCS, the last two bytes is reception quality information: I assume the values are as described in the MRF 24J40 datasheet (see sections 3.7 and 3.6) CC2420 datasheet section 16.4: "The first FCS byte is replaced by the 8-bit RSSI value. This RSSI value is measured over the first 8 symbols following the SFD... The 7 least significant bits in the last FCS byte are replaced by the average correlation value of the 8 first symbols of the received PHY header (length field) and PHY Service Data Unit (PSDU). This correlation value may be used as a basis for calculating the LQI."

The LQI and RSSI values from a sample packet capture look like this:


The ZENA command line utility

I've opted to use plain C instead of Python. I'm more familiar (albeit very rusty) with C and it eliminates dependency on Python and PyUSB. You will still require libusb and associated development files which may not be installed by default. I am currently using libusb version 0.1. I plan to move to the more recent version 1.0 for the next release. The file can be downloaded from the download area at http://code.google.com/p/microchip-zena/

To install on Ubuntu make sure libusb (version 0.1) is installed:
sudo apt-get install libusb libusb-dev

To compile just do:
gcc -o zena zena.c -lusb -lrt

(Update 20 Feb 2011:  version 0.2+ of the zena utility has slightly different dependencies and compile instructions. See comments near the top of the C file on how to compile and run.)

To run:

You'll probably need to be logged in as root to do all of these things. On Ubuntu you can do
sudo bash
to avoid prefixing everything with "sudo".

Prior to running you need to make sure the ZENA device is "unbound" from any kernel drivers. If you get a "ERROR: ZENA device not found or not accessible", that is likely your problem.  I would love to be able to check for this and unbind programatically, but I don't know how to go about it. Suggestions welcome. (Update 20 Feb 2011: It is possible to check if a device is bound to a kernel driver and unbind it in libusb 1.0 using libusb_kernel_driver_active() and libusb_detach_kernel_driver() functions. This will be incorporated in the next release – version 0.3).

This is how to "unbind" it: While ZENA is *not* plugged in, look at files in /sys/bus/usb/drivers/usbhid/
There should be files eg "1-3.1:1.0". Now connect the ZENA. You will find a new file in that directory. Make note of it. For this example assume it’s "1-3.3:1.0". Now do:

echo 1-3.3:1.0 > /sys/bus/usb/drivers/usbhid/unbind

(replace the "1-3.3:1.0" with whatever you find for your system)

Once the device is available you can run the zena utility.

zena -c channel [-f format] [-v] [-h] [-d level] [-h]

-c selects 802.15.4 channel and is mandatory. A number between 11 and 26 is expected.

-f selects packet dump format. Two output formats are supported:
  • pcap (default):  packet capture file which can then be imported into Wireshark and other tools
  • usbhex: this dumps the raw 64 byte chunks of data obtained from the USB port in hex. One line per 64 byte chunk. Each chunk is prefixed with a timestamp obtained from the host computer
-v will display version information and quit.
-h will display usage information and quit.
-d will display debugging information to stderr. A level of 0 implies no debugging (default), 9 maximum verbosity.

The packet capture output is sent to standard output.



Important note: I have observed kernel crashes running this utility on a fully updated (15 Feb 2011) Ubuntu 10.4 (kernel 2.6.32-28-generic).  A fresh Ubuntu 10.10 (kernel 2.6.35-22-generic) installed from CD image seems not to be affected by this problem.  I can only assume it's a bug in the USB part of the kernel. I suggest bringing the computer to single user mode when you try this the first time (or just be prepared to power cycle if you have to). Alternatively install Linux in a VM, attach the ZENA device to the VM and run from there: the kernel crash is contained within the VM. The Python script which this utility is based on also causes a similar crash -- so it's not specific to my implementation. (Update 22 Feb 2011: this seems not to happen any more since the move to libusb 1.0)

No binary is provided at this time. But it's trivial to build if you have gcc and libusb installed.

Corrections or additional information would be greatly appreciated. I'd be glad to expand this utility to use the ZENA to it's full potential. Let me know what you need.

Project files are hosted at Google:
http://code.google.com/p/microchip-zena/

I can be contacted at jdesbonnet (at) gmail (dot) com

References:
[1]
http://homewireless.org/wp/2010/05/a-tale-of-two-packet-sniffers-microchip-zena-and-avr-rz-usbstick/
[2]
http://www.willhackforsushi.com/?p=198

ZENA is a trademark of Microchip Technologies Inc.

Tuesday, February 8, 2011

Battery monitoring using a diode/LED and resistor

Summary: A common problem with wireless battery powered sensors is knowing when the battery is about to expire in sufficient time to replace the battery before it fails. This post outlines a method that achieves this with nothing more than one resistor, one diode (or LED), one ADC input and one digital output. A practical application using a PIC 12F675 is described and some results presented.

There are many approaches to the problem of gauging a battery State-of-Charge (SoC) or battery low indicator. Modern "smart batteries" maintain a SoC by use of complex algorithms combining battery/cell voltage, 'coulomb counting' and a battery charge/discharge history.

For small devices based on microcontrollers (MCUs) like the PIC 12F675 the only practical metric is battery voltage under load. But measuring this is not as straight forward as it may first seem.

Many MCU analog to digital converters (ADCs) will measure from 0V up to power supply voltage (Vbat or Vdd). Therefore connecting the ADC to the battery will always report the full scale value (Amax) by definition. Not very useful.

Here is an idea:

Between Vbat and 0V connect a resistor and diode in series. Connect the center point (Vd) to the ADC. Vd is voltage drop across the diode which will be a constant value – about 0.7V for a silicon diode. If A is the ADC reading, and Amax is the full scale value of the ADC then Vbat = Amax * Vd /A.

As the battery voltage dwindles the value of A will increase because the fixed diode voltage drop (Vd) forms a bigger fraction of Vbat.

The design can be further improved: in the above configuration, current (I = (Vbat - Vd) / R) is being continuously drawn which will have a detrimental effect on battery life. Current is only required to flow when a measurement is being taken. So the resistor + diode circuit is connected to digital output line (Dout) instead. When set to logic 0 no current will flow. When set to logic 1 the voltage applied will be almost the same as Vbat.


A further improvement is to replace the silicon diode with a LED.  This way the battery measurement circuit can double as a visual indicator (eg a periodic heartbeat).

One point to note about LEDs: the junction voltage drop (Vd) is significantly higher than that of a silicon diode and varies considerably depending on the color and chemistry (about 1.7V for red and as much as 4V for other 'exotic' colors). You will need to consult the manufacturer's datasheet or measure it yourself (most digital multimeters have this function). Also bear in mind that the diode voltage drop needs to be less than the application's minimum supply voltage, which might rule out use of green or blue LEDs if operating on 2 x NiMH cells in series.

Here is a real application – a wireless environment sensor powered by 2 x AAA NiMH cells in series using a PIC12F675  MCU. The sensor unit periodically transmits environment data to a receiver which is directly connected a server running data acquisition software.

The LED is a typical red LED (Vd = 1.7V) and the series resistor is 3.3kΩ. The radio transmitter is a Holy Stone Enterprise Co. MO-SAWR-A (sold by SparkFun, SKU WRL-08945 @ $4 each). The environment sensor circuitry has been omitted as it's not relevant to this post.

The 12F675 has just 6 IO/analog pins so it's important to maximize use of each pin. In this configuration, pin GP4 is configured as a digital output and serves three functions:
  • Applies a voltage to the resistor-diode network for battery voltage measurement
  • Illuminates a red indicator LED
  • Acts as the baseband signal input to the radio module (a high impedance input, so it will not interfere with the battery measurements)
A desirable side-effect of this arrangement is the LED will illuminate every time the radio is used. This serves as heartbeat / activity indicator.

Another desirable side-effect is that the the transition from logic 0 to logic 1 will energize the radio module, drawing a relatively large current (about 10mA, compared to about 0.5mA consumed by an active 12F675). Battery voltage is best measured under the maximum possible load for the application. (A completely depleted battery can still show a healthy 'open circuit' voltage). Since the radio module requires a 20ms warm up period prior to transmitting data, I use this opportunity to take a battery voltage measurement, ensuring that none of the precious battery charge goes to waste.

The following chart is what the battery voltage vs time look like. I'm assuming Vd is 1.7V.


The chart looks like the classic NiMH/NiCd battery discharge curve (which is similar to alkaline cells too). There is an initial voltage drop at the start of the cycle, followed by a relatively flat curve for most of the discharge cycle and a sudden drop near the end of the charge cycle. If you're curious about the small 'bumps' see footnote [1].

In applications such as laptops and electric cars it's important to know the State-of-Charge at any point in time (as a percentage, kWh, miles or time remaining depending on the application). In my application this is not important. All I require is to get an battery low alert in sufficient time to act on it (a few days will do).

So how to determine the battery low condition? And who's job is figure this out – the hardware constrained sensor/transmitter MCU or the substantially more powerful receiver/data acquisition server. Or both?


Making the battery low determination at the data acquisition end has the advantage of having a powerful CPU with megabytes of RAM available for the task. It makes sense to transmit the battery voltage as part of the data packet for this purpose. But my sensor unit only has a radio transmitter, so it's not possible to communicate the battery low status back to it.

Ideally whatever algorithm is used should be simple enough to run on the limited resources of the sensor MCU. Detecting battery low condition here has advantages. For example it could conserve charge by decreasing the sampling frequency.

I can see two approaches:

  • An absolute battery voltage threshold
  • A rate of change of battery voltage threshold

In theory the absolute voltage threshold is simple to implement. You figure out what is the minimum voltage required for reliable operation (about 2.2V in this application). Then add 100mV or 200mV to that value to arrive at the battery low threshold. The problem with this approach is that the threshold may be in the rapidly decaying part of the battery discharge curve giving little or no useful warning. If set too high the warning kicks in way too early. Getting this right is going to be tricky.

The rate of change approach has its own problems. Here is a chart of the rate of change of ADC values vs time with ADC vs time for comparison. Remember that ADC values increase as the battery voltage decreases.


The problem is the difference between ADC readings from one sample to the next is most likely going to be exactly zero ­– there is insufficient resolution available in the ADC. Some sort of low pass filter (LPF) or sub-sampling needs to be applied to the signal first. The following is mean ADC values in 1 hour bins and the time derivative of that.


Setting a dADC/dt threshold of 2 units / hour will catch the end of battery decay, with perhaps the odd false alarm (eg at day 18). But the capability of performing 1 hour averages is pushing the capability of the 12F675 (considering all the other tasks that need to be performed must be stuffed into its tiny 1Kbyte program memory). Sub-sampling is an alternative. This chart was obtained by sampling one in every 360 samples:


It can be seen to be almost identical to the 1 hour bin chart. Sub-sampling is certainly do-able on a 12F675.

Unfortunately I don't yet have enough data to decide which approach is best. Each battery discharge cycle is over two weeks long and right now I have only one sensor utilizing 2 x AAA cells. It has been running since November 2010. Interestingly a second identical sensor powered by 4 x high capacity AA NiMH cells has been running continuously on the same discharge cycle since November 2010 (it's now February 2011)!

I will post a further analysis when more data has been obtained.

Conclusion:

Measuring the ADC value of the voltage drop across a diode (signal diode or LED) seems to be a feasible way of gauging the battery voltage of a battery powered device. In a hardware constrained MCU like the PIC12F675 the measurement circuit can double as a LED indicator (and other functions) making maximum use of precious IO pins.

However translating this voltage to a reliable State-of-Charge or battery low indicator is a trickier problem and not one that I've fully solved yet.



Footnote 1: It is interesting to note there are small 'bumps' on an otherwise smooth voltage-time curve. It can be seen to correlate with ambient temperature. I'm assuming this is due to the effect of temperature on cell voltage.

Monday, January 24, 2011

Hotpoint Aquarius FDW60 dishwasher controller board

Some information on the Hotpoint Aquarius FDW60 dishwasher controller board for anyone interested in DIY repairs. The following was gleaned from an evening of poking around. I'm not certain about anything here, but hopefully it might be of use to someone else. If you have any feedback please post a comment at the end of this posting.

Warning: if the dishwasher power switch is on this board will have live mains (230V) PCB traces which are a shock hazard. Only access this board if you know what you are doing. It may also void your warranty.


The underside of the controller board. The large chip is the MCU (Renesas 6433662B94H). The triacs near the connector at the bottom of the photo are a 4 Amp STMicroelectronics T4-0560 (large triac on left) and 5 smaller triacs: Z7SY424 (also ST Microelectronics, but sorry can't find the datasheet). The triacs are located on the neutral (N) side of the load so the inputs to these triacs float at 230V AC when off.

I don't know what the 8 pin IC to the left of the MCU is – I'm guessing an EEPROM. The microswitch is the "Delayed Start" front panel button. The connector at the top-right connects the LEDs and switches on the left side of the dishwasher's front panel. The large traces at the bottom left provide power for the water heater.


This photo is the top side of the controller board. Visible are: black relay for heater on left. Buzzer (black disc in the center). The white wire pair connecting to the board at the bottom of the photo is (I believe) the water intake flow meter. The black wire pair I guess to be a temperature sensor. The blue component just above the barcode label is a combined crystal/capacitor for the MCU clock. Misc components to the left of the board are a mains to DC power supply for the low voltage electronics.

The 4 pin header to the right of the buzzer seems to be a diagnostics port. Pins on this header from left to right connect to the MCU's NMI, RXD, TXD. The last pin I'm not sure about. I thought ground would be the obvious choice, but I was seeing -5V on RXD and TXD relative to this pin. Maybe it's a +5V. This needs to be investigated further another time.



This is a close up shot of the MCU. The first digit is a '6' is difficult to see in this photo. The marking is 6433662B94H.

This is what I could figure from the mains voltage (230V AC) connector on the controller board:

White, Gray and Brown are sense wires (MCU senses presence of 230V AC or not).  Blue,  Red/Blue, Black, Violet, Red and Cyan drive a load (motor or actuator).


OrangeProvides 230V AC power to the board.
WhiteSense: ? water pressure -- it can sense if wash arm is obstructed
GraySense: Salt related ?
BrownSense: float switch on drip tray – triggered in the event of a leak
BlueWash pump motor
Red/BlueLower wash arm valve
BlackDrain pump
VoiletSalt related?
RedWater intake valve
CyanDetergent release door


Other information:

My 'howto' on unsticking the FDW60 drain pump:
http://jdesbonnet.blogspot.com/2010/11/hotpoint-aquarius-fdw60-drain-pump.html

Interesting dishwasher application note from Renesas:
http://am.renesas.com/applications/consumer/home_appliance/dishwasher/dishwasher.jsp

Excellent dishwasher hack here ("Arduino Controlled Dishwasher"):
http://www.neonsquirt.com/dishwasher.html

Please add any additional information or corrections in the comments below. Thanks!

Thursday, January 20, 2011

Why does my tooth brush have a wireless network?

My toothbrush (Braun Oral-B 5000) has been displaying a nagging "Replace brush head" message recently. I initially though nothing of it. Almost all consumer electronics will nag you when some consumable needs changing.

But then I thought: how does it know? It's not like the head is wired in. It's deliberately detachable for cleaning. There are no electrical contacts. Could it be there is a RFID chip in the head and a RFID reader in the tooth brush?

Sure enough the Braun Oral-B 5000 features a RFID reader and RFID chips in the heads – a toothbrush area wireless network!

Out of curiosity I wrapped about 1 meter of magnet wire (about 30 turns) around the head and connected it to an oscilloscope. There is a strong excitation signal at about 13MHz for about a second after the brush is powered up.

A quick google of  "13MHz RFID" does reveal that 13.56MHz is popular RFID frequency. Unfortunately decoding this signal seems involved. There is an open hardware sniffer project here: http://www.openpcd.org. However I don't really have the time or incentive to hack my toothbrush right now.



An oscilloscope trace from the coil wrapped around the toothbrush head. A strong signal of about 75ns period is observed for about 1 second after the brush is switched on.

Sunday, January 9, 2011

Sanitas SBM30 (aka HL868BA) teardown

The next step of my Ambulatory Blood Pressure Monitor (ABPM) project is to move from a wrist cuff monitor to an upper arm cuff which is far more practical for ABPM. My local Lidl store just had a Sanitas SBM30 upper arm cuff BP monitor on special offer. So I bought one. I was pleasantly surprised to find that it is almost identical to the HL168Y which my previous modification was based on. (Indeed the fine print on the box states that it is identical to the HL868BA).

As it is likely to be a few months before I can post an update on my ABPM project, I've provided some tear down photos and some technical information about this device in case anyone else would like to modify it.

Opening the device:

Before proceeding to open, be aware that this is likely to void your warranty. It may also comprise the reliable operation of the device. So if you require this for medical reasons I would advise you get a separate device for experimentation and clearly label which is which.

At the bottom of the device two screws need to be removed. The top and bottom parts of the case can then be separated. There are 6 plastics clips (is that the right word?) located at the left center and right of the top and bottom.

Once the top is removed, the rubber button contacts can be lifted off. Next remove screws at the top of the LCD holder. Carefully remove the LCD from the white holder. Be careful not to damage the LCD or the cable which attaches it to the PCB. This will expose two more screws under the LCD. Removing those screws allows the white LCD holder to be removed. Now the top of the PCB is exposed. Unfortunately the LCD is permanently connected to the PCB. It will not be easy to reconnect if it is detached for any reason.






Test pads:

Unlike the HL168Y where the test pads are scattered around the PCB, the test pads on this device are conveniently brought together exposed through a slot in the battery compartment. The pitch is 2.54mm. In theory this should facilitate the construction of a cable to mate with these test pads. Any suggestions on how to accomplish this would be greatly appreciated. Please email jdesbonnet at gmail dot com if you have any suggestions.

Test pads TP8 and TP10 are for power and ground respectively, so any such cable can dispense with the need for batteries. Perhaps the connector can be attached to the battery compartment cover. TP 1 to 4 are directly connected to the front panel buttons. Pulling these low is the same thing as depressing a button. TP15 and TP18 can be used to snoop on read/write traffic to the EEPROM (see earlier posts on how to do this).

TP1"Mode" button. Pull to 0V to activate.
TP2"Set" button. Pull to 0V to activate.
TP3"Memory" button. Pull to 0V to activate.
TP4"Start" button. Pull to 0V to activate.
TP5?
TP8+5V / battery
TP100V
TP15EEPROM I2C bus SCL
TP18EEPROM I2C bus SDA

The MCU:

This is similar or identical to the MCU in the HL168Y. The chip marking first line reads 86CX23, the second line HLEEF7. It seems to be a custom chip made by the Health and Life Co (the "HL"). I have no doubt it's based on some standard core. But beyond that I have no clue what's going on inside... nor is it necessary to know for my application.


The EEPROM:

The device uses the EEPROM chip to store blood pressure records and a few configuration variables. This chip is a ST Microelectronics 24C08WP. This is the same 8 kbit (1 kbyte) chip used in the HL168Y. I didn't get a chance to verify if the memory map is the same as that documented for the HL168Y. I would be surprised if it differed. The data can be accessed by passively snooping on the bus SCL and SDA lines (TP15 and TP18). I've documented a way of doing this in this blog post.

The following is a description of the HL168Y (and presumably the HL868BA also) memory map:

Blood pressure records  are 8 bytes long starting at address 0x0010 and are always 8 byte block aligned. First record is stored at 0x0010, next at 0x0018 etc.

byte 0month 1 .. 12
byte 1day of month 1 .. 31
byte 2 bit 7Hour of day pm flag. am if clear.
byte 2 bits 3:012 hour clock time
byte 3minutes 0 .. 59
byte 4 bits 7:4the hundreds decimal digit of systolic BP in mmHg
byte 4 bits 3:0the hundreds decimal digit of diastolic BP in mmHg
byte 5 bits 7:4the tens decimal digit of systolic BP in mmHg
byte 5 bits 3:0the least significant decimal digit of systolic BP in mmHg
byte 6 bits 7:4the tens digit of diastolic BP
byte 6 bits 3:0the least significant digit of diastolic BP in mmHg
byte 7heart rate in bpm (beats per minute)

Locations 0x0000 to 0x000f are reserved for other things. I haven't figured out what they are all for yet.
Location 0x0007 is the number of BP records in memory.

The pneumatics:

The pneumatics comprises a pump, electrically activated release valve, a slow release valve and a pressure sensor (on the PCB). The configuration is identical to that of the HL168Y which I have documented here.


More tear down photos are here:
http://picasaweb.google.com/jdesbonnet/ABPMProject

If you have any questions or information to contribute, please do not hesitate to contact me by email: jdesbonnet at gmail dot com.