Showing posts with label Home Automation. Show all posts
Showing posts with label Home Automation. Show all posts

Sunday, February 8, 2015

Lidl PIR nightlight teardown and hack

Summary: I picked up some low cost (E8) battery powered / PIR triggered nightlights that were on sale in Lidl. The heart of the device is a common PIR IC. One of the IC pins can be tapped to drive digital IO line for occupancy detection or an intruder alarm. Given that PIR sensors alone in small volumes can often exceed €10/$10 this represents good value for money.

The device (Livarno Lux brand (Lidl), model Z31685C)  comprises 8 white LEDs, a  PIR (Pyroelectric Infrared) sensor with a polyethylene plastic fresnel lens [1] and a photodiode (which suppresses the illumination of the LEDs during the day). It is powered by 8 x AAA alkaline cells (supplied) arranged in two sets of 4 in series.

The device can be mounted on a wall or ceiling by attaching a mounting plate with screws or an adhesive strip (also supplied).

Teardown:

The top cover comes off easily enough: seems to be held on by friction alone. Then an aluminiumized plastic reflector can be pulled of revealing a single sided PCB secured to the base with three screws. On the top surface of the PCB is the PIR sensor, a light sensing photodiode and two electrolytic caps.

Underside of nighlight PCB.
 (the thin red and yellow wires soldered to the main IC were
added by myself to facilitate interfacing with MCU).
Click to expand image.
The fun is on the underside. At the heart is a 16 pin PIR IC marked "HS0001". Googling this didn't yield any useful results, but I found several PIR ICs that had similar marking codes: LP0001 [3] and BISS0001 [4]. The datasheets for these have a reference design which look very similar to the layout of this PCB. After some probing, I can say with certainty that the HS0001 used on this board is functionally identical to the LP0001 and BISS0001.

The board is powered by the two parallel banks of 4 x AAA alkaline cells. Each bank supplies approx 5.5V. A diode in series with each bank prevents one bank from driving the other in reverse (which can cause alkaline cells to leak). A linear regulator provides a 3.3V rail to the PIR IC.
Reference design schematic from LP0001 datasheet.


Most of the passive components on the board are resistors and capacitors used to set various tunable parameters of this IC [list pins]

The VO (pin 2) line from the IC drives the gate of a (FET?) transistor which switches the lighting LEDs (driven by the 5.5V direct from the batteries).

So how power efficient is this? Using a little hack I measured the quiescent current at about 90uA and the current with LEDs on at 70mA. Unfortunately the supplied AAA cells don't have any product code, but typically AAA alkaline cells have capacities in the range from 800 - 1200 mAh. Let's say 1000mAh. And as there is two sets in parallel that's a total of 2000mAh capacity. If you assuming the LEDs are never triggered, that's an approximate battery lifetime of 2000mAh/90uA = 22222 hours or 2.5 years. In reality if the LEDs are on at least a few minutes a day so that brings the battery lifetime down to under a year.

Driving a MCU

So can this cheap nightlight be used as a PIR sensor for another application? 
So this is what the OUT2 (output of second stage opamp) pin of the PIR IC looks like on an oscilloscope when I walk into the room (about half way though the trace).  So it seems OUT2 tends to about half the supply voltage (1.6V) when there is no activity. Then when a person enters its field of view OUT2 oscillates between 0 and 3.3V.  So if you connect this directly to a digital IO input, all you need to do is detect a transition (high to low or low to high). Apply classic switch debounce logic (except on a multisecond time scale) and you have a occupancy / intruder detection mechanism!

To facilitate my hack, I soldered some think (30AWG) wire to OUT2 , GND / battern -ve and the 3.3V rail (to draw power for my MCU application).


Oscilloscope trace from the OUT2 pin showing a person enter the room at about the half way mark.

Conclusion:

If you can find these in Lidl (it's a very hit and miss thing with them) this can be converted into a nice neat PIR sensor for less money than ordering a dedicated PIR sensor [2]. Plus you get the LED night light functionality and 8 AAA alkaline cells!

References:

[1] Focusing devices for pyroelectric infrared sensors
http://www.glolab.com/focusdevices/focus.html

[2] Adafruit PIR (motion) sensor  ($10)
http://www.adafruit.com/products/189

[3] LP0001 datasheet
http://akizukidenshi.com/download/ds/sctech/LP0001.pdf

[4] BISS0001 datasheet
http://www.seeedstudio.com/wiki/images/2/2f/Twig_-_BISS0001.pdf


Thursday, December 18, 2014

Experiments with the RFM69 433MHz digital transceiver module

Summary: 

This post is an overview of an board I'm developing which encapsulates the RFM69 radio module with a MCU to expose a simple UART API which can be tailored for specific applications. This simplifies embedding a radio into existing applications with very little additional expense in terms of hardware or power budget. The API is implemented by a light weight radio OS (currently just 5KiB) which is open source and the bill-of-materials for the board can be as little as $8 / €7.

Background to project:

WRSC2014 contestant Team FASt from University of Porto.
The World Robotic Sailing Championship 2014 was held in Galway Ireland (my home town) in September this year. It was originally anticipated that each contestant would use a RFM69 radio module to relay real time positioning information back to a central hub for recording the race and scoring. It was also hoped that the race organizers could use the same radio network to broadcast race waypoints, starting times, obstacles etc. However given the short time available it became clear that race contestants would not have sufficient time to incorporate this module into their hardware and write the associated supporting firmware.

Based on the experience of WRSC 2014 I believe the best solution for such competitions is to develop a low cost ready made 'black box' which is given (ready made and programmed) to each contestant. The black box would expose a very simple API to the boat's software which should not take long to integrate. Or if it's a case the contestant simply does not have time to make any changes to their software the radio can be mated with a GPS module and it will send real time position, speed and heading back to the race organizers.

This project is the first step in such a system.  An entire race system will incorporate other elements such as base station software, real time visualization tools, race protocols etc. This blog post only discusses the radio board.

RFM69 family of modules:

The RFM range of modules are made by HopeRF and based on the Semtech radio ICs. The RFM69 comes in several varieties. The one I've been using is the higher power version: the RFM69HW [1] which is based on the Semtech SX1231H [2]  chip.

Feature highlights:
  • Digital SPI interface
  • Operates on 433MHz unlicensed ISM band
  • Continuous or packet mode (66 byte FIFO)
  • Built in AES encryption and CRC-16 engine
  • Configurable bit rates up to 300kbps
  • Configurable power settings
  • Line-of-sight ranges in excess of 1km
  • Sleep current < 1µA
  • Only $4 in small quantities

The radio board:

This is the general architecture of the board: a MCU acts as the go between the application hardware and software and the RFM69 radio module. The MCU takes care of all the complex register configuration of the RFM69 and takes of low level functions (such as responding to pings, remote configuration commands etc) without ever bothering the application. The application talks to the MCU thru a standard UART interface using a very simple protocol.
In addition to the core API there is also the option to compile in application specific functions (for example I've build in support for a rain tip-bucket and a temperature sensor: more on that later).


Prototype mk1:

Unfortunately these modules have a 2mm pad pitch making them difficult to use directly with standard 2.54mm spacing prototype board or breadboard. So for the first prototype I glued the module onto a prototype board and used thin 30AWG wire to link the module pads to pads on the protoboard.


Reasonably need looking from above...



The problem is on the underside...




Even though it's only a hand full of wires, cutting, stripping and soldering these is very time consuming and error prone (well in excess of three hours per board).

For the first prototype I used a LPC810 ARM Cortex-M0+ MCU as the controller.  The ARM Cortex-M range is my MCU of preference these days due to the ease of 32 bit programming, open source and well supported tools and a wide range of vendors (NXP, TI, Freescale etc).

This 8 pin DIP, 4KiB program memory device was adequate for initial tests, but I quickly ran up against the 4KiB program limit. The 8 pin DIP package also had the advantage that I could socket it and try different versions of firmware just by swapping out the device.

With two of these modules I did some range tests. I soldered 17cm wire (quarter wave at 433MHz) to both sides of the board just at the RFM69 antenna output pad. I was able to achieve two way communication of about 1km along the (line-of-sight) length of the Doughiska Road in east Galway city.


Prototype mk2:

For the next prototype I moved to the LPC812: in many ways identical to the LPC810 but with more IO pins and more memory (16KiB program memory, 4KiB SRAM). Rather than use time consuming protoboard I decided it was worth spending some money on a PCB. I designed a two layer board using the free version of CadSoft Eagle and used Eurocircuit's standard FR4 pool to fabricate. This job cost €80, but for that I got 20 boards. The turn around time was nominally 10 working days, but I had them in a week.



The board in the photo actually comprises two boards:  one for the RFM69 discussed here and another for a similar (better?) module the RFM98 which I'll discuss in another blog post. The two halves are separated with a hacksaw.

One very handy feature of the LPC81x range of MCU is the switch matrix which allows many of the MCU functions to be mapped to an arbitrary pin.  This makes PCB layout a lot easier.

After populating with the MCU, module and 3 other components (LED, resistor and decoupling capacitor for MCU) this is what the board looks like. There is the option to use a wire as antenna, or alternatively splash out on a SMA connector (probably the most expensive component on the board!). This photo shows a SMA connector with a 433MHz stub antenna obtained from Farnell [3].



On the left is the UART connector for the radio API, pins for Serial Wire Debug (SWD) for debugging and reprogramming. Left top is the power connector (3.3V nominal, but will work down to 1.8V).

There is also a LED at the very top-left. This is a general purpose indicator that's under control of the MCU. I use this to flash a sequence after boot up tests are complete and flash it each time a packet is sent or received.

In the center there is an option for another connector which will give direct access to the RFM69 SPI port for debugging, or simply using the PCB as a breakout board for the radio module (leaving the MCU pads unpopulated).

The bill of materials is PCB €2;  LPC812M101JD20 €1.50; RFM69HW €3 ; passives, pin headers €0.50; SMA connector (optional) €3.50 ; stub antenna (optional) €5. That's a total of €7 without the SMA connector option. Those prices with the exception of the PCB are for single quantities from Element 14's catalog.  Significant discounts are usually available when ordering 20+. Also components can often be sourced at a much lower cost from Chinese retailers (eg Alibaba and Deal Extreme). The PCB cost is more complex: many fabricators have a fixed job charge (it was about €50 for Eurocircuits) and they then charge per PCB after that. So if you make a lot of them the price per board goes down quickly. If you make just one it will be very expensive.


The radio OS firmware:

My firmware is written in C and currently uses the LPCXpresso IDE to build. This uses the GCC compiler under the hood. The main purpose of the firmware is to expose an simple UART API which the host application uses. The API protocol is simple: a single character command/response code followed by parameters separated by spaces followed by a carriage-return to end the command. All parameters are in hexadecimal. Example:

application to radio:
T 44 5232

'T' means transmit a packet. It takes two parameters: the to-address and the packet payload.

radio to application:
p FF 42 7A14051F003D AD

The 'p' response code is transmitted to the application whenever a packet is received. There are four parameters: the to-address (FF hex being the broadcast address),  from-address, the packet payload and the RSSI (received signal strength indicator). 

Other commands include 'R' and 'W' to read/write RFM69 radio configuration registers. 'M' to switch operating mode from an active awake/listening (20mA current use) to a sleep-polling mode using an average of about 140µA.

Some advanced experimental commands are remote register read/write, allowing a remote radio to be reconfigured. Remote watchdog timer configuration which will reset the radio to default configuration should there be a prolonged loss of communication. Remote command execution (which can be used for remote configuration and to relay packets), remote battery voltage measurement.


Test application:

As mentioned earlier the intended application is for robot sailboat telemetry. But I'm conducting initial tests by using one of these radios with a rain tip-bucket and temperature sensor in my garden. I've got the radio board in an IP66 enclosure with a small PV panel and LiPo battery (and associated charger). 

The tip-bucket causes a magnet to fly past a reed-switch each time the bucket tips. I use this in conjunction with one of the spare LPC812 pins and an interrupt channel to count each time this happens. I've also got a DS18B20 temperature sensor on another spare pin. 

I run this in low power polling mode. Every 30 seconds the MCU comes out of deep sleep mode, makes a temperature measurement and fires up the transmitter and sends a status packet with the tip bucket counter, current temperature, and battery voltage [4]. It listens for about 100ms for a response packet. If none is received it goes back into deep sleep mode. 

If I need to communicate to the out door unit I must wait for a poll packet and then immediately send my command. If multiple commands need to be issued, the first command is to switch the radio into active listen mode, issue all the necessary commands and then put back into low power mode.

So far tests have been successful with the only communications disruptions attributed to either running out of battery charge (it's winter here and the days are short: unless  it's a sunny day there is not enough stored charge to keep it going through the night) or a problem with the USB/Serial cable at the receiving end.

This is 48 hours of some sample data. The disruptions are due to problems with the USB/Serial cable at the receiving end: the radio worked flawlessly. Also note the elevated temperatures are due to the sun falling directly on the IP66 box. 


Current and future work:

I'm currently working an a web browser based application/UI to interact with these radios. The application will allow configuration of the local and remote radios, performing experiments with various radio settings. Monitoring network performance etc.

Also I'm planning to make a third prototype. This will be smaller than the current board, perhaps putting the MCU on the underside of the board. I'm also considering moving to the recently announced LPC82x MCUs [ref] which feature more program flash, more SRAM, a full ADC and other new features.

Features for future firmware include mesh networking, encryption, remote program execution and over-the-air firmware upgrades.


Conclusions:

So far I'm very impressed with these radio modules. At only $4 they're a steal. I've verified two way communication at over 1km distance line-of-sight and I've heard reports of success well in excess of 1.5km line-of-sight. With the addition of an LPC812 MCU with my radio OS and simple UART API, embedding this radio into an application is far simpler than building the radio driver directly into the application software. The entire bill of materials is about $7 with the option of a SMA connector which might bring it to $10. My PCB layout is available under the BSD licence on GitHub [5] and the firmware is also available on GitHub [6].


Footnotes:

[1] RFM69 Datasheet
http://www.hoperf.cn/upload/rf/RFM69-V1.3.pdf

[2] Semtech SX1231H product information and datasheet
http://www.semtech.com/wireless-rf/rf-transceivers/sx1231h/

[3] Farnell/Element 14 SKU 2305893, €4.86 in single quantities.

[4] One feature I miss in the LPC81x range of MCU is a full ADC. There is however a comparitor which can be used with a voltage ladder to make crude voltage measurements. I use this for battery voltage measurement (Vcc thru voltage ladder on one comparitor input, and the built in 0.9V voltage reference on the other). In my tests it can detect low battery voltage condition, but due to the coarse resolution I get a warning only about a hour before the voltage drops below the cutoff threshold.

[5] https://github.com/jdesbonnet/RFMxx_LPC812

[6] https://github.com/jdesbonnet/RFM69_LPC812_firmware

Updates:

11 Feb 2015: Added link to firmware repository on GitHub.

Wednesday, September 28, 2011

Controlling XBee IO lines with ZigBee commands

Summary: this post explains to send remote AT commands to an XBee module using ZigBee commands. The end point, profile ID, cluster ID and command and response format is documented. This is useful if you need to control a XBee's IO lines from a non-XBee device.
The Digi XBee Series 2 module. 
The Digi XBee module is a popular RF module featuring a UART and several IO lines that can be configured as input/output or in some cases ADC or PWM. The "Series 2" version of these modules can be flashed with ZigBee compatible firmware. The modules then participate in a ZigBee mesh network.

The modules are configured and controlled by  AT commands (like the modems of old) which are issued through the module's UART by a computer or microcontroller. There are two varieties of the firmware: AT mode and API mode. The latter provides access to more advanced features of the module.

One feature of the API mode is the ability to send AT commands to remote XBee modules. This can be used to control relays, read the state of switches, read temperature etc. This in theory allows many small control applications to be accomplished with nothing more than a XBee module on it's own -- no need for an attached microcontroller.

There is a problem however. All the examples and documentation I've seen to date (I may very well have missed something, but not for the lack of trying) only cover controlling a XBee module through the XBee API. So if your interface to the ZigBee network is not a XBee (or equivalent Digi product) you're out of luck. In my case I have a Texas Instruments CC2530 based USB dongle and I require that software running on a computer control the state of a remote XBee's IO lines.

So, reverse engineering time! I wrote scripts to control the XBee digital IO lines from another XBee, and simultaneously ran a 802.15.4 packet sniffer. This is what I learned:

For remote AT commands the sending XBee issues a ZigBee command to the remote XBee on endpoint 230 (0xE6), with profile 0xC105 (Digi private profile), cluster 0x0021. The command is formatted as follows:

0x00, 0x32, 0x00, frame-id, sender-ieee-addr, 0x00, 0x00, atcmd0, atcmd1, [param]

frame-id: one byte API frame ID used in many XBee API calls
sender-ieee-addr: the 64 bit IEEE address (8 bytes, the most significant byte first)
atcmd0: the ASCII code of first character of the AT command (eg 0x4d or 'M' if command is ATMY)
atcmd1: the ASCII code of the second char of the AT command (eg ...)
params: zero, one or more optional parameter bytes

For example: to send ATMY (get 16 bit network address) the command will be:
0x00, 0x32, 0x00, 0x0f, 0x00, 0x13, 0xa2, 0x00, 0x40, 0x3c, 0x15, 0x5c, 0x00, 0x00, 0x4d, 0x59

The bytes with values 0x00 and 0x32 may have some significance, but I have no idea what it might be. I'm not sure if the sender IEEE address is important. It seems to work the same no mater what address I use.

Responses are sent on cluster 0x00a1. In response to the ATMY command I get:
0x0f, 0x4d, 0x59, 0x00, 0xd1, 0xed

So that seems to be:
frame-id, atcmd0, atcmd1, 0x00, atresponse...

The XBee digital IO lines are configured with the ATDn commands, where 'n' is the number of the IO line. The lines can be configured as input, output low, output high, analog and PWM (not all lines are capable of all the functions). The two functions that are of interest to me are output high (parameter value 5) and output low (parameter value 4).

Note: there is potential for some confusion encoding AT commands. Take for example the command ATD04. What this means is AT command D0 with parameter 4.  The digit 0 in the command part is encoded as the ASCII code ie 0x30, but the digit in the parameter part (4) is the byte value 0x04. So the command and parameters is encoded as 0x44, 0x30, 0x04.

Other clusters in endpoint 230 have other functions, which I'll document in another post.

Update (2 Oct 2011): Small edit. I omitted the frame-id in the format of the AT command response.

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.

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.

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.