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.

Thursday, December 23, 2010

The Emerald Isle with a frosting of snow

This is a beautiful image of Ireland taken at about 11am this morning (23 December 2010) which I grabed from the ESA's Miravi service. This image is cropped to show just the island of Ireland and is contrast enhanced. The last week or so has been a particularly severe cold snap with record low temperatures for December. The Corrib lake is seen to be partially covered by ice.

The original image is here, (12MB).

There is a similar image available from the Irish Met Office here.

Tuesday, December 14, 2010

Arduino powered Christmas tree star

This is a quick little hack to put a little sparkle on the top of your Christmas tree. You'll need the following:
  • an Arduino (and suitable power supply eg USB connection to a laptop)
  • a nice powerful red-green-blue LED such as this one from Seeed Studio
  • a power supply for the LED (the Arduino cannot provide the required current). I used 4 x NiMH AA cells.
  • 3 x  NPN transistors that saturate with Ic as close as possible to the maximum allowed current for the LED. I used 3 x BC547 as it was the only ones I had handy. Unfortunately that saturates at only 100mA (I'm allowed up to 350mA per channel for this LED), but it was sufficient for the effect to be successful.
  • 3 x resistors (150 ohms or so) to limit the transistor base current which will be sourced from the Arduino PWM IO pins
  • a length of 4 core cable that will run the length of your tree down to your bread board
  • a breadboard (this is a very temporary hack)
A word of caution: while the voltage is low, the currents running up to the RBG LED are sufficient to pose a small (but non negligible) fire hazard should something go wrong (eg LED failure or a short circuit).  Only operate this while in the room and power off while not attended.

I soldered the LED onto a square of strip board and attached it to my star Christmas tree ornament.

This particular RGB LED can sink up to 350mA per channel (1A in total). There is no way you can source this much current directly from the Arduino IO pins.  Instead the Arduino switches transistors which in turn drive the LED current. The BC547 transistors I had on hand saturate at only 100mA but I found this sufficient for a reasonably good effect.

The transistor base current is provided by pins 9, 10 and 11 of the Arduino. These pins are capable of a simulated digital-to-analog conversion using pulse width modulation (PWM). A 150 ohm resistor limits the base current to about 33mA (the Arduino's limit is 40mA per pin). This is sufficient to ensure the transistor is fully on (saturated).  Failure to fully saturate the transistor can lead to excessive heat dissipation in the transistor. There is no need for a LED current limiting resistor – the transistor performs that function.

The following sketch is my "supernova" program. The idea is to use this as a lighting the tree ceremony. First the star slowly comes to life in a supernova effect (with buckets of artistic licence) and when it reaches full brightness you then power up all the other traditional lighting.

Here is a video of the effect:


You can easily adapt the sketch for other effects.

/*
 * Christmas tree star LED driver. Simulate "supernova" effect
 * with lots of artistic licence. You need 3 x transistors 
 * to power LEDs.
 *
 *
 * See blog post:
 * http://jdesbonnet.blogspot.com/2010/12/arduino-powered-christmas-tree-star.html
 *
 * Joe Desbonnet
 * jdesbonnet@gmail.com
 * This is public domain code. No copyright is asserted.
 */

// Define pins for Red, Green and Blue LEDs.
int LEDR = 9; 
int LEDG = 10;
int LEDB = 11;

void setup()  { 
  // nothing to setup
} 

void loop()  { 
    int i;
    
    setRGB(0,0,0);
    
    // Fist a red faint red glow... growing slowly
    for (i = 0; i < 16; i++) {
        setRGB(i,0,0);
        delay(500);
    }
    
    // Getting brighter faster now
    for (i = 16; i < 64; i++) {
        setRGB(i,0,0);
        delay(100);
    }
    
    // Add some gree to make the light yellow (like dawn)
    for (i = 64; i < 200; i++) {
        setRGB(i,i-64,0);
        delay(100);
    }
    
    // Now a bit of blue
    for (i = 200; i < 255; i++) {
        setRGB(i,i-64,i-200);
        delay(100);
    }
    
    // Flash
    setRGB(255,220,128);
    delay(10);
    setRGB(0,0,0);
        
    delay (100);
    
    // Another flash    
    setRGB(255,255,200);
    delay(10);
    setRGB(0,0,0);
    
    delay (100);
        
      
    // More flashes, with random color variations and intervals
    for (i = 0; i < 32; i++) {
      setRGB (random(192,255),
            random(192,255),
            random(192,255) );
      delay(20);
      setRGB(0,0,0);
       
      delay (random(50,200));
    }
 
   // End of program... switch LED on full all channels (~ white)
   setRGB(255,255,255);
   
   // Loop forever
   while (1);

}

void setRGB (int r, int g, int b) {
  analogWrite(LEDR,r);
  analogWrite(LEDG,g);
  analogWrite(LEDB,b);
}

Tuesday, November 30, 2010

Hot water tank experiment

I've been wondering why my hot water takes an unusually long time to heat up in recently. When I first moved in almost 8 years ago it seemed that it took only about 20 minutes to get piping hot water. Nowadays it takes 40 minutes or more ... and even then it's not that hot.

I was curious about the temperature profiles of my hot water tank as it was being heated by my natural gas powered water heater ("boiler" or "furnace"). As is normal in Ireland, household hot water is provided from hot water storage tank which is heated via heat exchanger by the central heating system.

The following is the temperature at the heat exchanger inlet and outlet of the hot water tank. Temperature was measured with DS18B20 temperature sensors which were taped to the pipe near the tank.

A few things struck me as being interesting about this graph. First there is a brief but noticeable dip in inlet temperature when the system is first switched on. This is clearly the cold water ahead of the pump/heater entering the heat exchanger. There is a quick rise in temperature with the outlet temperature following the input minus a temperature drop (due to heat transferred to the water in the tank) and a time lag (due to the transit time of water in the heat exchanger). However the most striking feature is the temperature cycling.

It seems that my heater cannot transfer heat to the tank fast enough. The water in the heat exchanger circuit reaches the heater's target water flow temperature (user settable from 30°C-85°C) and the flame cuts off. The pump continues to pump until the temperature drops below some threshold and the cycle repeats. The duty cycle is about 50 seconds flame on, and 180 seconds flame off.

There is another interesting effect during the cooling phase: a brief increase in temperature for about 30 seconds before dropping again. I believe this artifact is because the heater flame is on for less time that it takes for the water to circulate around the system. So there is a hot spot which makes a second round through the system.

I re-ran the experiment with a higher heater temperature:

It looked identical (excepted at elevated temperatures). But only when I looked at the numbers more closely while writing this post did I realize that the period between cycles had increased from about 230 seconds to 330 seconds. So perhaps if I set the heater at the max temperature I could increase the period to a sufficiently long time to heat the water in the tank in just one cycle.

The following graph is from a third run. This time the heater was set at the max temperature (supposedly 85°C) and I set the tank thermostat at 40°C.

Unfortunately there was still cycling, 4 in total, but the cycle period was considerable longer than any than the previous runs. More importantly the duty cycle was much better: 500 seconds of flame on, 100 seconds flame off for the first cycle; 220s on/100s off for second, 180s on / 100s off for third, and 140s on for the last cycle. The tank thermostat cut off the heater at about 1800 seconds (30 minutes) into the experiment.

Conclusion:

For fast hot water, running the heater as hot as possible works best (at least for me). At some point the heater temperature must have been turned low which explains why in recent years it seems to be taking ages for the water to heat. However all this probably makes no difference to the energy efficiency of the system.

Tuesday, November 2, 2010

Hotpoint Aquarius FDW60 dishwasher drain pump repair

A few years ago our 2004 Hotpoint dishwasher (Aquarius FDW60) started failing in mid cycle with a flashing "Eco" and "Fast" LED. The underlying cause was that the grey water drain pump wasn't pumping. There was precious little information about reparing the device on the internet. Faced with a repair bill of at least €140 we were close dumping and replacing it. I figured there was nothing to lose by investigating the problem myself and after a little puzzling over the layout of the various inards of the device I eventually located the drain pump, disconnect it, manually rotated the impeller a few times with a srewdriver, reconnected it... and sure enough everything was back to normal.



Two years later after flawless operation, the problem has returned. This time I thought I'd document the repair procedure in case it's of use to anyone else.

First a caveat: don't try this unless you are comfortable with DIY repair and have at least a basic understanding of electrical safety. The instructions here worked for me, but may not be the "correct" way of doing things. If you do decide to proceed YOU DO SO AT YOUR OWN RISK. Also, if there is any warrantee remaining on the appliance it may be voided by your attempt to repair. Feel free to add feedback below if you have any interesting information to add.

The tools you need for this are:

  • Phillips head screwdriver (number 2), but you might get away with a flat head
  • Torch (optional)
  • Bucket (optional)
  • Rag (optional)
  • Brush and/or vacuum cleaner (optional)
  • FDW60 manual (optional)

Empty any water remaining in the dishwasher. Remove the filters and drawers (the top drawer requires that you to remove a plastic stopper at the end of the drawer rail). Use a rag to mop up any remaining water in the drain well. Switch of the unit and remove power at the wall socket or fuse.

Before disconnecting the cold water in ensure that the water supply to the appliance is OFF. This is usually accomplished by means of a valve leaver near where the hose connects to the water supply. Leaver perpendicular to the pipe usually means OFF. Have a bucket and rag ready to catch any residual water in the hose.



Same applies to the grey water outlet hose. This water may have a foul oder -- so be prepared.



Ensure that the power cord and the two water hoses are free to move as you gently pull the appliance out.


Place some newspaper on the ground and have some rags on the ready. Stuff a rag in the drain well to absorb residual water. Gently lay the appliance on its side. Be prepared from some water to seap out. The bottom of the appliance is now exposed. Use a brush or vacuum cleaner to clean the bottom.


Double check that you have electrically disconnected the appliance (don't just rely on the appliance on/off button!). Best to physically disconnect the power cord from the wall socket. If you can't do this then trip the fuse associated with the appliance. Verify there is no power by switching the device on and ensure the power neon does not light.

The bottom panel can be removed by unscrewing 6 (Phillips Head Size 2, I think) screws marked by red arrows in photo. You may need to temporarly remove the front legs to access two of the front screws(just twist the leg until it comes out).


Most of the essential dishwasher components are now accessible. The drain pump can be removed by unscrewing one screw (see photo) and twisting the pump assembly.



The impeller should now be visible inside the pump chamber. Use a pen or screw driver to rotate the impeller a few revolutions. There will be some resistance due to the pump motor – that is normal.


Ensure that there are no foreign objects stuck in the chamber (use a torch to illuminate the chamber while rotating the impeller). In my case I found a pumpkin seed!


Reattach the pump assembly (be careful to ensure the o-ring seal is still there).

Replace the bottom panel. Bring the unit upright. Reconnect power and water hoses. Run a "Prewash" cycle to test.

That should be it!

Boot note: On my first attempt at reattaching the pump assembly I failed to engage all three posts of the bayonet twist lock mechanism (the one at the back which isn't easy seen had not engaged). This resulted in water leaking during my test which caused the float switch activate. This manifests itself as as flashing "Eco" LED and the drain pump runs continuously. Once the pump was attached properly everything ran smoothly.

Update (24 Jan 2011): I've added additional information about the this dishwasher's controller board in this blog post.

Update (7 Mar 2011): A Hotpoint technician told me that this can often be solved without removing/opening the unit by using something like a wire coat hanger to unstick the drain pump impeller. I've never tried this, so I can't comment on its effectiveness and it probably won't help if there is a foreign object lodged in there.

Update (25 May 2011): Impeller got stuck again today. This time I tried the coat hanger trick that was suggested to me by the Hotpoint technician. It worked! I had to do it twice... first time the pump started working, but it sounded rough and then jammed again. The second time it again sounded rough for a few seconds but recovered and seems to be fully back to normal now. There was probably a foreign object in the pump chamber that got expelled eventually. You need a wire coat hanger and make a right angle bend like this:


The horizontal part (running parallel to my measuring tape) goes into the drain hole. See photo of the underside of the dishwasher above to get an idea of what's going on. About 10cm - 11cm is what you need to reach the impeller. Then just wiggle it... all it takes is just the slightest movement of the impeller to unstick it. Of course if you have a large foreign object stuck in there this probably won't help – you'll need to disassemble the pump as described in the main part of this post.

Thursday, October 28, 2010

Computer interface to a low cost electronic kitchen scale (part 1)

On a few occasions I was looking for a low cost electronic weigh scales with a computer interface. It seems they don't exist. There are electronic kitchen scales for as little as €15, but as soon as you require a computer interface you're into specialized scientific/point-of-sale equipment and the starting price jumps to €400+.

I had a spare low cost kitchen scale lying around that was purchased in Maplin (model KS108) a few years ago. Opening it up one can see the weight/force (also known as a "load cell") sensor is a cantilever beam anchored to the base of the unit with strain gauges glued to the top and bottom of the beam. The strain gauges are covered in a white epoxy for environmental protection. A weight (force) applied to the business end of the beam causes the beam to bend slightly causing the strain gauges on top to stretch and those on the bottom to compress.


A commonly used configuration comprises 4 strain gauges wired in a "full bridge" Wheatstone bridge. All four resistors of the bridge (see schematic further down) are strain gauges. Two on the top (R1, R4) and two on the bottom (R2, R3). This configuration provides good sensitivity to bending, and automatically compensates for resistance variation due to temperature.


The interface to the sensor comprises 4 wires: the red and black wires are are the 'excite' (labeled E+ and E- on the PCB) and white and blue are the sense wires (labeled S+ and S-).

The changes in resistance of individual strain gauges are too small to measure accurately by direct measurement. However in a Wheatstone bridge configuration the tiny changes in resistance causes the bridge to unbalance and measurable potential difference is generated on on the sense wires.

I was hoping to piggy back my own sensor electronics without interfering with the operation of the scale. Unfortunately it seems the scale's electronics was interfering with my ability to measure anything. So I cut the sensor wires and attached a simple plug / socket to allow the sensor to be connected to my system but still have the option to revert back to the original configuration if required.

After disconnecting the sensor from the scale's PCB I was able to measure a small voltage difference on the sense wires. But only 1.4mV when 1kg is applied. This is far too small to be measured directly by the ADC (each step of a 0–5V 10 bit ADC is about 5mV).

The two sense wires are connected to an op amp in differential configuration (a ST Microelectronics TS954 dual rail-to-rail op amp) set at moderately high gain produces a signal useable by an ADC.
Normally the Rg resistor is set set to the same value as Rf. In this configuration Vo is 0V when no force is applied to the sensor and can swing up the the supply rail (5V).

If Rg is omitted, at rest (zero force) the output from the op amp (Vo) is about 2.5V. Depending on how the sense wires are connected to the op amp, Vo can swing from 2.5V to 5V or from 2.5V down to 0V (the ground rail). So it seems we lose half of the ADC's dynamic range. However we can put this situation to good use.

Let's call the voltage change due to the application of force on the sensor as ΔV. Instead of wiring the excite wires to the power/ground rail, two digital IO output lines (D0 and D1 in the schematic) are used to excite the sensor. With D0 set to logic high (5V) and D1 set to logic low (0V) a measurement is taken: a0 = 2.5V + ΔV. By toggling the IO lines the excitation polarity is reversed and another measurement is taken: a1 = 2.5V - ΔV. The difference (Δa = a0 - a1) is 2.5V + ΔV - 2.5V + ΔV = 2ΔV. The measuring resolution is doubled. Another advantage of this setup is that is helps eliminate common mode interference.

A word of caution: one must allow sufficient time for the signal to settle after changing the excitation polarity. As can be seen from the oscilloscope trace (taken at Vo where a grid square = 200µs x 1V) at least 500µs is required for the sense voltage to settle after a reverse in direction.



To illustrate this noise reduction in action, I deliberately introduced mains noise by pinching one of the sense wires with my fingers. I wrote a Arduino sketch to take 300 samples with alternating polarities (and allowing 750µs settling time). In the chart below, one can see a0 and a1 have 50Hz mains noise of amplitude ~ 15 ADC units, but the noise in Δa has an amplitude of only ~ 5 ADC units.


Calibration

Ideally a calibration weight kit (expensive!) is used for calibration. But this is not intended to be a super accurate scales. I am aiming for ±0.5g. For that resolution there is a calibration kit (literally) in your pocket: coins are minted to precise specifications. Freshly minted coins are best, but any clean coin in reasonable condition will suffice for this purpose.

The following calibration chart was obtained by mounting Euro 10c (4.10g), 20c (5.74g) and 50c (7.80g) coins on the scales.

Using Gnuplot to plot and curve fit the data to the equation of a line: Δa(x) = c + mx
where x is the mass in grams applied to the scale.

Gnuplot curve fit reports:
c               = 17.3111          +/- 0.09565      (0.5525%)
m               = 2.49864          +/- 0.0006569    (0.02629%)


As can be seen from the residuals plot there is excellent linearity (the residuals are random and ranging from -0.8 to +0.8 ADC units with a root-mean-square of just 0.376). The slope or sensitivity (m) has a value of 2.49864 (let's call that 2.5) which means that each gram added registers a Δa of 2.5 ADC units, or each ADC increment is 1/2.5 = 0.4g. Combined with the noise (the spread in residuals) and it seems we are safely in the ±0.5g area.

An Arduino was used for the initial prototyping. The following sketch (below) excites the sensor in both direction, takes a time averaged ADC of Δa and spits it out on the serial port.

In part 2 I will discuss the interfacing to a computer, the computer side software and possible applications.



/**
 * Take measurement from weigh scales sensor and output to serial
 * port. By reversing polarity of excite signal can reduce common
 * mode noise. Take 256 samples and get mean before outputing 
 * result -- this helps reduce noise and adds extra resolution.
 * Joe Desbonnet, 28 Oct 2010. jdesbonnet@gmail.com. 
 */
 
 // Define bridge excite digital IO lines
#define D0 3
#define D1 2

// Use ADC port 0
#define AN0 0
 
// Delain in microseconds to allow signal to settle 
// after excite polarity reversal
#define DELAY 800

void setup()   {
  
  Serial.begin(115200); 
  
  pinMode(D0, OUTPUT);
  pinMode(D1, OUTPUT);
}

void loop()                     
{
  
  // Get zero point ('tare') of scale
  int tare = measure ();
  
  while (true) {
     Serial.println ( measure() - tare );
     delay (1000);
  }
  
}

int measure () {
  int i,a0,a1;
  long s;
  for (i = 0; i < 256; i++) {
    digitalWrite (D0,HIGH);
    digitalWrite (D1,LOW);
    delayMicroseconds(DELAY);
    a0 = analogRead(AN0);
     
    // reverse polarity
    digitalWrite (D0,LOW);
    digitalWrite (D1,HIGH);
    delayMicroseconds(DELAY);
    a1 = analogRead(AN0);
    
    s += (long) (a0 - a1);
      
   } // next i
   
   // Don't care about polarity
   if ( s < 0) {
       s = -s;
   }
   
   // As there is a large number of samples being averaged we
   // can probably extract another bit or two from the ADC.
   // Going to be conservative and going for one extra bit.
   // So instead of dividing by 256, will divide by just 128
   // instead (ie right shift 7 bits).
   return s>>7;
}