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
lopower.upload.protocol=stk500
lopower.upload.maximum_size=30720
lopower.upload.speed=57600
lopower.bootloader.low_fuses=0xFF
lopower.bootloader.high_fuses=0xDA
lopower.bootloader.extended_fuses=0x06
lopower.bootloader.path=atmega
lopower.bootloader.file=ATmegaBOOT_168_atmega328_pro_8MHz.hex
lopower.bootloader.unlock_bits=0x3F
lopower.bootloader.lock_bits=0x0F
lopower.build.mcu=atmega328p
lopower.build.f_cpu=8000000L
lopower.build.core=arduino
Power Experimentation
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?
Current Consumption
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.
Capacity
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.
Power Calculations
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?
| ‘Inputs ‘ | ||
| Sleep Consumption | 0.14 | mA |
| Operating Consumption | 13.57 | mA |
| Sleep Duration | 60 | s |
| Operating Duration | 0.027 | s |
| Capacity | 2035.5 | mAh |
| ‘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 |
| ‘Results ‘ | ||
| 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 |
This file is available on Box.net: Power Calculations.ods CC BY-NC-SA
Sensors
What shall we measure, and how?
Temperature
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!
Battery Voltage
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”.
Software
These nodes are running my RF24Network stack. Built on my RF24 radio driver, the network layer provides node addressing and routing to build a tree topology out of the nRF24L01+ radios.
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');
network.write(header,&message,sizeof(message));
// Leaf nodes sleep
if ( is_leaf() )
{
// Power down the radio. Note that the radio will get powered back up
// on the next write() call.
radio.powerDown();
// Sleep the MCU. The watchdog timer will awaken in a short while, and
// continue execution here.
Sleep.go();
}
}
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.
| Qty | Vendor# | Description | Price |
| 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
Schematic
Eagle file is on box.net: RF Duinode V3.sch CC BY-NC-SA
Circuit Board
Eagle file is on box.net: RF Duinode V3.brd CC BY-NC-SA





Excellent work – I’m going down the same route myself and this is most helpful! Many Thanks!
Thanks! And thanks for writing, glad to hear it’s helpful. Please share a link if you post details about your project!
Hi, just came across your blog, and in particular this post, and all I can say is…….WOW.
Would you be interested in helping modifying a C# library so that it will be possible to create a sensor network with your nodes, and receive the data on Dot Net MicroFramework devices?
Is it also possible to allow Dynamic payload size?
Keep up the fantastic work, this is a direct concurrent for the JeeNodes
Thanks! Yes, the RF24 library allows you to use dynamic payload size. I’ll be in touch about the C# library.
This is indeed awesome…. please keep on innovating… i will eventually get into the same line of stuff… ad i like what you did so far.
great work and Thank you for sharing.
Jay.
Hi,
What is the range of this system?
Do you know if this or something similar can work up to 100 meters?
Thanks, Ron Otsig
Never tried it at that distance. I get about 50 feet indoors, around 2 corners, at 250kbps
Thanks a lot for this beautiful information. I don’t understand one thing, how do you know the sleep duration is 60s. And you said you are waking once a second. If the sleep duration is 60s, shouldn’t it be once a 60s?
Many Thanks,
Ameet
Thanks for pointing that out, I’ve updated the text to say “once a minute”. I know the sleep duration is 60s because the code controls the sleep duration! It can be whatever we want it to be.
Thanks a lot! I am clear now.
Many Thanks,
Ameet
Any news on code being available for this ?
Not yet. I have been working on something to share. It will combine the RF24Network and NanodeUIP examples to post readings from a whole network up to Pachube.
Great stuff, I was particularly interested in the idea of holding some calibration data. I’d be happy to see a pre-release code, drop me an e-mail.
Do you think it would be possible to support a network of hundreds of sensor nodes with the nRF24L01+? I’m thinking that if the nodes are asleep most of the time and just wake at regular intervals up to ping the base, and wake up on interrupt from the sensor to send a reading, then it would be possible to use the same address for each node and overcome the limit of 6 receivers when in PRX mode. An id in the payload could be used to uniquely identify each node. Do you think this is workable? Do you have any other suggestions of how to support hundreds of nodes?
Definitely possible. A few things to think through…
Even if the nodes are mostly sleeping, you’ll still need a strategy to deal with inevitable collisions. Typically, you would listen on the channel to see if it’s clear before talking. If it’s not, you could adjust the sleep window so next time the node doesn’t wake up and collide AGAIN.
Also have to think about range. Would all hundreds of nodes be in range of the destination node? Or would you build a tree of routers, and then just have dozens of nodes hit a single router? Or build a much fancier mesh network?
To minimise collisions, I was thinking that the nodes could wake up in RX mode, then the base node could ping each node in turn. Once a node has responded it goes back to sleep again. This will reduce the chances of collisions but not eliminate them, so a simple back-off strategy would still be required.
Regarding the range, that is something that I will need to test. The nodes need to be as small and as low power as possible, but the base node can be larger and have power so could be fitted with an SMA connector and a decent antenna. But I guess adding repeaters would be fairly straight forward, in fact the repeaters could form a mesh network, with one or more providing internet connectivity.
Thanks for your help, and the RF24 library.
Hi maniacbug,
This project looks awesome and I am wanting to replicating it. How did you produce the PCBs? did you get someone to manufacture them for you and if so is it possible to order some?
Hi there. I used iTeadStudio’s PCB service. The files are available above and you’re welcome to send some off for them to fab, just follow the terms of the CC BY-NC-SA. (Mainly, don’t sell them.)
Thanks Maniacbug,
Ordering some now, some of the PCB quotes I was receiving were about ranging from $10-$60 per PCB + shipping so paying $0.99 per board + $4 shipping makes this project worth doing.
Hi,
I’d like to ordersome of your PCB’s off of iTeadStudio’s PCB Service, but I’m a bit flustered with all the different options, ie: single sided/double-sided, size, and all that… Do you have the “parameters” that you used to order them?
Thanks!
Yannick.
Yup. I just ordered some today. This link should do what you need: http://iteadstudio.com/store/index.php?main_page=product_info&cPath=19_20&products_id=173
Thanks!
Just ordered my own batch using your link!
Thanks again, looking forward to implementing, I currently have a bunch of wires going everywhere and was looking for a simple wireless alternative…
Using DS18B20 Sensors instead though…
Y,
Hi There,
I just noticed that the nRF24L01+ module you used uses a 8 pins but a lot of the nRF24L01+ modules use 10pins (2 pins each for VCC&GND). Thought I might just put the comment out there as if people are planning to replicate you project they need to get the correct nRF24L01+ module or change the wiring a little.
Interesting to know, thanks. Can you provide links to some of those 10-pin modules? I have only ever seen the 8-pin versions.
________________________________
The GND pin has been moved to pins9&10, Pin1 is connected to Pin2(VCC)
There is more info here http://www.electrodragon.com/?product=storage-container-set
But the ones I have/ordered look more like this one http://www.kuuco.com/images/propic/090022_S11.jpg
and found on ebay for around $2aud each
I am working on a home automation project and your rf24 library is the backbone. Thank you for the awesome work.
I was wondering if you tried using the internal 8MHz oscillator (i am using atmega328). This will be really useful to save more pins if I move to a smaller attiny…
Hi. Glad it’s helpful No, I’ve never touched the internal oscillator. Probably there are people on the Arduino forums who have used it, tho.
Thank you so much for your work. It’s helping me a lot in preparing my school final exam. I followed this http://arduino.cc/en/Tutorial/ArduinoToBreadboard tutorial with an Arduino UNO R3, using an 8mhz external oscillator and working with 3.3V. It works like a charm. I’m using that board with an nRF24L01+ and a DS18B20 from Maxim IC, and I didn’t implement the voltage sensing yet. The board is being powered by two AA batteries and it works perfectly. I can transmit temperature to my other Arduino UNO wich stays listening and once received a packet sends it via Serial port.
I’m writing this to you because i found a bit strange the fact that your circuit has a current usage of 14mA while transmitting and 410uA while sleeping. My board consumes 5,4uA while sleeping and 4mA while transmitting. I had not the opportunity to check how much it stays active when transmitting, yet.
For letting the MCU sleep i used this excellent library https://github.com/rocketscream/Low-Power.
If you want some other infos from me just send me an email!
The 410uA strikes me as measurement error on my part, but I really haven’t dug into it further. On the other hand, I can say for SURE that 4mA while transmitting is measurement error on your part. The data sheet calls out 13mA on high-power modes. It may just not be transmitting long enough for you to get a complete reading?
Wow, that low power lib is pretty powerful, thanks for the tip. That’s definitely worth digging through.
Sir,
Your setting of:
lopower.bootloader.low_fuses=OxFF
prevents the use of the internal oscillator, at least in my findings. I have defaulted to OxE2, and all is well
Other than that, your only other modified setting from the default “ATmega328 on a breadboard (8 MHz internal clock)” config was:
lopower.bootloader.extended_fuses=OxO6 ;verses the normal setting of OxO5.
Which seems to work.
Yes, I did not want the internal oscillator. The node has an external 8MHz osc. And yes, the big change is to set the brownout detector to kick in at 1.8V instead of 2.7V. Given that the unit runs at 2.4V, a 2.7V brownout is catastrophic!
Dear maniacbug,
I have noticed that RFCE and RFCSN are connected to pins 14 and 13 (digital pins 8 and 7) of ATMEGA respectively, instead of pins 15 and 16 (digital pins 9 and 10) as they are normally connected. However, the sofware defines digital pins 9 and 10. Is there a mistake?
The examples from the RF24 libraries are just that: examples. In order to run them on actual hardware, the software must be configured for the hardware being used. So yes, to run RF24 library examples on the nodes in this blog post, it’s necessary to change the software to use pins 8 & 7. The library uses 9 & 10 because that’s how the ‘getting started’ boards are all set up, thereby making it easier on new people to have everything work together nicely out of the box.
________________________________
Thank you very much for your answer! Congratulations for your work!
Great work! I just tested out my first design using your library and it works! Then I realized my 3.3v regulator was going to really hurt my battery life as it draws 3.3mA all the time. I will be trying out the 2 batteries setup next. Thanks so much! I would never have figured all this out on my own, but it looks like it will end up being just what I wanted.
I can’t seem to find a article on your base node or your SQL stuff… Where is that please? I’m about to build 10 of these things.
I’ve never gotten that to the point where I was comfortable publishing it. Still needs a lot more cleanup!
Hi,
The idea is great
But I have a question.
Where can I buy this board?
Thanks
Alberto
It’s not for sale. It’s a personal project I built. You’re welcome to build one yourself!
A great tutorial, great links, great libraries.
I have to say thank you very much for this contributing.
We can make this low cost sensor and make the nrf24 a very useful wireless transmitting.
I’m going to make a board and do like this for my gardening project.
Hope to share the result soon.
Thanks a lot
Good luck! Once you’ve completed your project, it would be great if you could write something up and post a link here.
Hi, I was wondering if you can program the node with the 5V FTDI breakout board or if you need the 3.3V version? Cheers.
Right, good question. I DO use the 5V FTDI breakout board, BUT I do not connect the +5V line. Instead, during programming the module needs to be powered on with its own power. This works great.