My goal today is to create a sensor node which can be used in a wireless sensor network, to capture environmental information and send it back to the base. My main goals are for the nodes to: Be cheap, and last for a year.
How cheap and how low-power can we go? In the end, I got down under $12 for a very capable Arduino-compatible node with a 2.4GHz radio running ~2.6V that should last a year and a half on 2 AA’s, or 5 months on a coin cell.
Adventures in Low Power
Previously, I ran a small test network of these nodes using 3.3V regulated power. After doing that, I realized it’s possible to run on even less power and with an even cheaper BOM by ditching the regulator entirely. Two AA cells will range from 2.4 to 2.9V. An Atmega328P MCU and the nRF24L01+ radio will both operate just fine in that range.
To enable this, I needed to create a new ‘board’ in the Arduino IDE. This board has two changes from the stock Duemilanove:
- Set the correct fuses for the ‘Brown-Out Detector’ (BOD). The Arduino IDE as-shipped sets the BOD to 2.7V, so the chip won’t even power up with 2x AA’s.
- Run the chip at 8MHz clock rate. That is exactly the edge of the specified clock rate curve for 2.4V, the low end of my voltage range.
Here’s what my entry to the boards.txt file looks like exactly:
lopower.name=Arduino Ultru Low-Power (<2.4V, 8 MHz) w/ ATmega328
Now it’s time to measure and estimate power. Our usage pattern will be the MCU and radio powered down as low as they can go most of the time. Then they wake once every minute or two, transmit, and then go back to sleep.
These are the specific questions I needed to answer, so I ran experiments for each:
- How much power is consumed when sleeping?
- How much power is consumed when awake and transmitting?
- How long will it be sleeping each cycle?
- How long will it be awake each cycle?
- How much capacity is really available from the batteries?
To measure current, we need a multimeter capable of doing so. I use a Extech EX330 Autoranging Mini Multimeter, but many cheaper meters have current-measuring features. Look for an “mA” setting. The methodology here is to insert the multimeter inline into the circuit at the point we want to know the current flow. That is, the current flows THROUGH the meter on its way from the battery to the load. To keep things simple, it’s a good idea to use the actual battery being tested as the current source, rather than some external bench power.
There are two current measurements needed here, one while sleeping and the other while operating. So I created simple test sketches that did each. One just puts everything to sleep (MCU and radio), the other transmits constantly to a working receiver. Simply measure the current from the meter during both tests.
Actually for the ‘transmitting’ test, I am using the “endless sending” test. This is consuming 13.57mA reliably.
For ‘sleeping’, I am getting strange results. With the radio removed, and the voltage measurement circuit removed, the meter still reports 395 uA. But on mA mode, it reports 0.14mA. Argh! Also, adding the radio in doesn’t seem to matter much. Adding back in the voltage measurement, I hover up at 410 uA.
Duration / Duty Cycle
How long between wake-ups is defined by the application. For now, I am waking once a minute, but that is likely much more frequently than needed to effectively measure temperature.
To measure how long it stays awake, I used the same “endless sending” test as for capacity. I have the system endlessly send readings without actually ever sleeping. Every 30 seconds, the receiver posts a reading to the database. By counting the number of readings elapsed between each saved reading, I can see how long it takes to measure and send a reading. In my tests, there were reliably 1128 readings every 30 seconds, for an awake duration of 27ms.
The one true test for this is to take a fully-charged battery, and burn it to the ground. Because consumption varies with current draw, it’s important to run the test at the correct amount of draw. It seemed like the logical choice was to use the ‘constant transmitting’ sketch which I used above to measure draw, and leave it on as long as it takes. Two AA batteries lasted over a week! So 156 hours @ 13.57mA tells us these ‘2300mAh’ batteries actually gave about 2110mAh.
Just for fun, I had the sender transmit its voltage level, so I could graph the progress.
In the end, I decided to go with a 3V coin cell battery, CR2450, rated at 540mAh. This is a big trade-off. With the coin cell, the units are much smaller and easier to place. They are also a few dollars cheaper. But I’ll only get 5 months, and the batteries are not rechargeable.
Armed with these results, we can estimate how long the node will really last. I have a handy spreadsheet I use for these calculations. The results for this test are pasted below, but the same calculations can work for anything sleep/wake setup. we plug in the answers to the five questions above, and out comes the answer we want to know: How long will it operate?
|‘Per sleep cycle ‘|
|Capacity Used Sleeping||8.4||=Sleep_Consumption * Sleep_Duration|
|Capacity Used Operating||0.36639||=Operating_Consumption * Operating_Duration|
|Total Capacity Used mAs||8.76639||=Operating_Consumption * Operating_Duration + Sleep_Consumption * Sleep_Duration|
|Total Capacity Used||0.00243510833333333||=Total_Capacity_Used_mAs/3600|
|Time Available cycles||835897.102456085||=Capacity / Total_Capacity_Used|
|Time Available hours||13931.6183742681||=Time_Available_cycles / (3600/Sleep_Duration)|
|Time Available days||580.484098927837||=Time_Available_hours / 24|
|Time Available months||19.3494699642612||=Time_Available_days / 30|
What shall we measure, and how?
The “hello world” of wireless sensor networks is temperature. I figure it’s always interesting to know what the temperature is, so now I can find out what the temperature is in 25 unique points within the house :) My weapon of choice is a Microchip MCP9700. There are many options. This one seemed simple and cheap.
To drive it, I created a simple MCP9700 library. It supports both the ’00 and ’01 variants, maintains a calibrated error value, and handles the average of multiple readings. Soon to be available on github!
It’s useful to know the battery voltage, from which we can infer the remaining battery capacity. So the nodes collect and transmit their battery voltage with every sample.
These nodes use the internal 1.1V reference voltage, so the battery voltage has to be divided down before it can be sampled. For this, I use a simple voltage divider circuit with 1M and 470k resistors. The resistors are chosen to have very high impedance so they don’t draw a ton of current. As a consequence, a capacitor is needed. (Not sure why exactly, but it was suggested on the Arduino forum, and definitely solved the problem of poor readings.)
Here is a very simple schematic of the voltage divider at work:
Others to Come
To future-proof this node just a little, I included an expansion port. This exposes 4 unused pins (2 digital, 2 analog) and the SPI bus. It will allow me to add additional sensors in the future without having to re-spin the board. For details, see my post on the Arduino Box Header Platform; this is “Port B”.
The temperature reading & sending code on the client is really simple. It is also using my MCP9700 library for the temp sensor and my Sleep library for putting the node to sleep and waking at the right interval.
// If there's a reading ready, send it.
if ( temp_sensor && temp_sensor->available() )
// Take a reading
message.temp = temp_sensor->read();
if ( voltage_sensor )
message.voltage = voltage_sensor->read();
// Send it to the base
RF24NetworkHeader header(/*to node*/ base_node,/*message type*/ 'T');
// Leaf nodes sleep
if ( is_leaf() )
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
// Sleep the MCU. The watchdog timer will awaken in a short while, and
// continue execution here.
The receiving node and SQL storage infrastructure will be the topics of future blog posts.
Bill of Materials
Pricing for all of these parts is in quantity 25, which is about how many I want to have operating at once. All part ##’s are from Mouser.
|1||556-ATMEGA328P-PU||Atmel Microcontrollers (MCU) 32KB In-system Flash 20MHz 1.8V-5.5V||$3.05|
|1||614-CR2450N.IB||Renata Coin Cell Battery 3V 24.5 x 5.0mm 540mAh||$1.10|
|1||122-00224-GR||Eagle Plastic Devices Battery Holders, Snaps & Contacts 24.5MM THRUHOLE BLK||$0.65|
|1||611-OS102011MS2QN1||C&K Components Slide Switches SPDT On-On||$0.32|
|1||571-1-390261-9||TE Connectivity IC & Component Sockets 28P ECONOMY TIN SKT||$0.30|
|1||774-ATS080B||CTS Electronic Components Crystals 8.0MHz 18pF Fund. -20C +70C||$0.29|
|1||579-MCP9700A-E/TO||Microchip Board Mount Temperature Sensors Lin Active Therm||$0.26|
|2||140-50N2-220J-RC||Xicon Ceramic Disc Capacitors 50V 22pF NPO 5% Tol||$0.08|
|1||642-MJTP1230||Apem Tactile & Jog Switches 6mm TACTILE SW||$0.06|
|1||604-WP132XYD||Kingbright Standard LED – Through Hole YELLOW DIFFUSED||$0.06|
|1||604-WP132XGD||Kingbright Standard LED – Through Hole GREEN DIFFUSED||$0.07|
|1||604-WP7104HD||Kingbright Standard LED – Through Hole RED DIFFUSED||$0.06|
|4||594-K104M15X7RF53L2||Vishay Multilayer Ceramic Capacitors (MLCC) – Leaded 0.1uF 50volts 20% X7R 2.5mm LS||$0.04|
|1||660-MF1/4DCT52R1004F||KOA Speer Metal Film Resistors – Through Hole 1/4W 1M ohm 1%||$0.05|
|1||660-MF1/4DC4703F||KOA Speer Metal Film Resistors – Through Hole 470K 1% 100PPM||$0.05|
|1||660-MF1/4DC1002F||KOA Speer Metal Film Resistors – Through Hole 10K 1% 100PPM||$0.06|
|3||660-MF1/4DC3300F||KOA Speer Metal Film Resistors – Through Hole 330ohm 1% 100PPM||$0.06|
The radio unit is from iTeadStudio, 2.4G Wireless nRF24L01+ Module for $4.00.
Cost breakdown:BOM $6.69, Radio $4, Board $1, Total $11.69