This article will discuss and demonstrate how to build a remote Temperature and Humidity sensor based on a Whisper Node and a DHT11 (worst sensor ever, I know), capable of running for well over a year on a single CR2032 coin-cell.
The remote node will be configured to transmit data every 60 seconds to another Whisper Node, acting as a gateway/base station and located 100 meters away. The data will be finally uploaded to a Google Could server running InfluxDB and Grafana to display the data.
Be aware that this won't be a short 20 lines story! The information here will focus on low-power measurement and techniques, as well a detailed analysis of how much energy we really have available from a coin-cell.
This post will present many practical tests and some maths, sparing some common knowledge like: how to put your Arduino to sleep, which can be easily found on the Internet.
MotivationWe all know, or at least have an idea, how to put a few sensors and an Arduino together. It's great and lots of fun to see the data being collected and printed over a Serial Terminal. Unfortunately things start to get more complicated when we need to move this bench sketch to the real world scenario. Normally far away from a computers, power-outlets or USB cables.
Knowing there are difficulties and pit-falls, this article aims to guide the reader through how a real-life, remote, low-power solution can be built to work according to the expectations. The Temperature and Humidity sensor is just an example and similar principles can be tailored for many other project.
Finally, the reason for choosing the DHT11 and a CR2032 was to create a sense of bigger challenge. If you already had the opportunity to work with both parts you know that the DHT11 is a very slow and clunky sensor, theoretically incompatible with low-power. At the same time the CR2032 has very little capacity, no more than 650 milliwatts hour* of energy!
*For comparison, a single AA Alkaline has over 3000 milliwatts hour!
Final resultsSo readers don't get bored, I've decided to show part of the final results before getting into the full technical details. Below you can see two Grafana screen-shots using the data published to the cloud, followed by the prototype and final hardware:
In terms of energy, we conclude that our solutions had only 171mA/h @ 3.3v available. In other words 564mW/h, and we'll prove that we can make it run for almost over one year!
All calculations, together with practical scope captures will be demonstrated below. Although it might sound boring and difficult, you just need to know how to add, divide and multiply.
As a final word, low-power is not quite the most simple subject, some thinking, persistence and trial+error are necessary to get the most of a battery.
Understanding the problemTo understand better the problem, let's have a look on what we're trying to archive:
The basic idea is to publish the DHT11 sensor's data to a server running in the Cloud. Again that it's just a model, this can be easily adapted to similar projects.
Together with the diagram above, let's suppose we gather the following requirements:
- The sensor node is located 100m away from any power source, in a enclosed area of difficult access - inside a factory's attic for example;
- The data collected needs to be available on the Internet in the form of graphs, showing current and historical values;
- Data needs to be collected every 60 seconds and available 24/7;
- Temperatures on the attic varies from 0°C to over 40°C;
- The equipment needs to be small as possible, no thicker than 15mm.
Now that we have a list of requirements, presented above, it's obvious that the solution needs to be powered by battery and rely on wireless communication.
The last requirement is pushing us to use a CR2032 battery as power source because of size constraints. On a real-life example different requirements will determine the options for your project.
There's nothing forcing the usage of a DHT11 sensor, which is indeed a very poor option for this kind of application. The decision to use the DHT11 is just to make it a better exercise, showing how hardware and software can be adapted to run in a low-power configuration. For a real-life application a more robust sensor could be used, like the TE HTU21D.
For the wireless communication, it needs to be based on radio as there's no visual/line-of-sight for IR or Laser beans. Because the solution needs to be powered by battery, WiFi (802.11) or any other high-level protocol is out-of-question* - ESP8266 is a no no!
For that reason simple sub-GHz digital radio based on FSK modulation is the best option in terms of reliability and power-consumption. Although OOK modulation consumes even less energy, the transmission speed is normally lower, making the FSK a more efficient transmission method.
*The power requirements to communicate using sophisticated protocols like WiFi is incredibly high, making the use of those not possible!
The Whisper Node has been chosen because it has a built-in RFM69 sub-GHz radio module and it's designed with low-power in mind. The board can be powered by standard batteries while providing a stable 3.3V from any source, down to 0.9V. The stable voltage is essential for reliability, specially when dealing with devices that only work at certain voltage levels or can suffer with voltage variations: like most of the sensors.
Choosing ready-to-use boards and modules saves development time and improves the reliability, significantly cutting down research, prototyping and implementation costs for experiments and low to medium volume production.
How much energy do we gotOk, now that we have an idea which components to use, the nest step is to understand if the solution is feasible or not. The most critical component in our application is the power source, the coin-cell battery, so getting familiar with it is essential.
A CR2032 battery has no more than 250mAh, and this is only true if you use the energy in a very low constant current or quick pulses. Additionally the cell's voltage, when band new, is around 3V and this value will drop down to 2V by the end of the battery's life. For that reason it's important to know the Battery Energy in milliwatts as well. According to the Energizer CR2032 datasheet, each coin-cell holds around 653 milliwatts hour.
If we expect to run our solution for a whole year without need to change batteries, here the average consumption we need to archive at the battery side:
1 year = 365 days * 24 hours = 8760 hours
250mAh / 8760h = 0.0285mA or around 28uA of average consumption.
Note that this figure is not taking other variables like the battery self-discharge neither the voltage dropping and already that's very little to consume!
Note that I've mentioned "at the battery side" before. That's because we still need to consider the step-up regulator and its efficiency. Remember that the battery provides no more than 3V, but the Whisper Node runs at 3.3V.
To calculate the energy available to the board itself, after the step-up regulator, we simply divide the cell energy by the voltage:
653mW/h / 3.3V = 197mA/h
That give us the real capacity at 3.3V. But wait, 197mA/h of capacity would only be possible if we had an 100% efficient step-up. Looking at the Whisper Node step-up documentation, we can see it's close to 95% efficient @ 2.5V with a 100R load (33mA).
Also checking the MCP16251 regulator datasheet, we can see the efficiency drops to around 83% at low currents @ 2.4v (at 0.1mA).
As our application should spend time on both modes, sleeping and running, we can chose something in between. Let's defined we have 87% of efficiency from our step-up:
197mA/h * 0.87 = 171mA/h
At this point we know that we have only 171mA/h @ 3.3V (564mW/h) available to spend along a full year. In other words 171mA/h / 8760h = 0.0195mA of average current, less than 20uA!
How much energy do we needNow we know how much (or little) energy we have available. The next step is to list every place we're going to spend it, starting with the usages that we can't change, like the sleeping current, followed by the important ones.
This exercise can be done by using information provided by datasheets together with some way of "pausing" or timing each event. Serial "printing" how long each operation takes can be used. For example, print the "micros" before and after sending a radio message and you'll have a good idea how long it takes:
(...)
Serial.println(micros());
myRadio.send(myMessage);
Serial.pring(micros());
(...)
Repeat the step above for each little task and you can get the approximated time you spend on each one.
Alternatively, a more practical method is using an oscilloscope and a shunt resistor. This arrangement might not be ideal for low current measurements because it'll will require a very high resistor to measure properly the little spikes, otherwise you'll only see noise.
I personally like to use a uCurrent, but I use it with a custom resistor configuration. By joining the current input terminals with a high precision 0R1 (0.1 Ohms) and moving the selector switch to 1mV/nA I can get 10mV per mA, instead of the original 1mV per mA. This is required because the oscilloscope will always have a few millivolts of noise no matter how expensive it is.
Finally, the low currents and tasks where you can "hold" it, like sleeping modes or blinking an LED might be better measured using the good and old ammeter. Just make sure you "blink" or sleep for a bit longer (few seconds) so you can get a good reading from the current.
DHT11 powering strategyAs the DHT11 does not offer any kind of sleeping mode, I'll be using a MCU GPIO to power it. This technique allows to turn the sensor On and Off only when it's required.
Also some DHT11 libraries on the Internet (like Adafruit one) enables the internal pull-up for the MCU pin connected to the DHT11 data pin. This is not necessary as we already have an external pull-up. Disabling it help us saving some current.
Moreover, the DHT11 is a terribly slow sensor, which requires long waiting periods before you can start reading data from it. For that reason many of the "delays" used on the DHT11 library have been replaced with "MCU sleep" calls. Example*:
(...)
// Go into high impedence state to let pull-up raise data line level and
// start the reading process.
digitalWrite(_pin, HIGH);
//delay(250); // <- Commented and replaced with the line below!
LowPower.powerDown(SLEEP_250MS, ADC_OFF, BOD_OFF);
// First set data line low for 20 milliseconds.
pinMode(_pin, OUTPUT);
digitalWrite(_pin, LOW);
delay(20); // <- Commented and replaced with the line below!
LowPower.powerDown(SLEEP_30MS, ADC_OFF, BOD_OFF);
uint32_t cycles[80];
{
// Turn off interrupts temporarily because the next sections are timing critical
// and we don't want any interruptions.
InterruptLock lock;
(...)
*Code above from https://github.com/adafruit/DHT-sensor-library/blob/master/DHT.cpp
Practical experimentationTo get some real-life current consumption figures the following code has been written. The idea is to turn On and Off a few components and measure their consumption individually - this not the real code, just for illustration:
#include <LowPower.h>
#define DHT_PWD_PIN A2
setup() {
myRadio.sleep();
myFlash.sleep();
digitalWrite(DHT_PWD_PIN, LOW);
pinMode(DHT_PWD_PIN, OUTPUT);
}
loop() {
// Here we can measure how much current we need at sleep
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
// Here we can measure how much the MCU running consumes
delay(10000);
// Turn on the Blue LED and we can measure again
digitalWrite(T2_WPN_LED_1, HIGH);
delay(10000);
digitalWrite(T2_WPN_LED_1, LOW);
// Turn the DHT11 Sensor ON and we can measure again
digitalWrite(DHT_PWD_PIN, HIGH);
LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
digitalWrite(DHT_PWD_PIN, LOW);
}
All the current measuring has been done after the voltage regulator - in other words, this is the actual components' consumption at 3.3V.
- All board components sleeping + MCU Watchdog: 0.0045mA (4.5uA)
- MCU running and all board components sleeping: 6.94mA
- MCU running, Blue Led ON and all board components sleeping: 9.44mA
- Everything sleeping + MCU Watchdog + DHT11 Powered On: 0.5mA
As we can see the values are pretty close to the component datasheet:
Now we still need to measure some other tasks like the Radio power usage together with the timing of each operation. For this exercise an oscilloscope will be used to capture the first version of our real-code running. As discussed previously, this could be done by isolating the tasks and "printing" the time it takes to run.
Using the trick of adding a 0.1R resistor to the uCurrent, each milliamp will appear as 10 millivolts + noise in the scope. Below is how our code behaves:
From the scope capture above we now know the following:
- Each Radio TX message takes around 1.6ms and consumes 55mA. We have 4 messages to send;
- Each analog reading + filling the radio buffer takes about 0.3ms and consumes about 9mA - total of 0.6ms;
- Reading the DHT11 consumes about 10mA and it takes 4ms;
- We keep the LED On for 10ms;
- The DHT11 is On (while MCU is sleeping) for 250ms + 30ms*;
*Note I've changed this values from the scope capture as the sensor was misbehaving with less than 250ms running time before start to reading it.
Again, the observed values are very close to the datasheet:
The reason why we don't do all measurements using an oscilloscope is because small currents are translated into very low voltages by the uCurrent and the oscilloscope always capture noise.
Calculating consumptionWith the information above we can now calculate the average consumption of our solution. To do that we need to basically multiply the milliamps by the time it's running and later divide by the total time.
The consumption will be break into parts so it's easy to understand the weight of each part. Remember, out full cycle is 60 seconds sleeping + execution time.
LED:
- 10ms * 9.44mA = 94.4mA*ms
Battery and power supply voltage reading and transmitting:
- 0.6ms * 9mA + (1.6ms * 2) * 55mA =
- 5.4mA*ms + 176mA*ms = 181.4mA*ms
DHT11 reading and transmitting:
- (250ms + 30ms) * 0.5mA + 4ms * 10mA + (1.6ms * 2) * 55mA =
- 140mA*ms + 40mA*ms + 176mA*ms = 356mA*ms
Total Time and Running period Average:
- Times for LED + Bat + DHT11 = Total Time:
- 10ms + 0.6ms + 3.2ms + 250ms + 30ms + 4ms + 3.2ms = 301ms
- Consumption for (LED + Bat + DHT11)/Total Time:
(94.4mA*ms + 181.4mA*ms + 356mA*ms) / 301ms = 2.099mA average
Now that we know our average consumption for the running period, we need to calculate the average for our whole cycle, 60 seconds sleeping + 301 milliseconds running:
- 60000ms * 0.0045mA + 301ms * 2.099mA =
- 270mA*ms + 631.799mA*ms = 901.799mA*ms
Finally:
- 901.799mA*ms / (60000ms + 301ms) = 901.799mA*ms / 60301ms = 0.01495mA
Considering we have an average consumption of 0.01495mA, or just over 15uA, we can now calculate how many days the solution can run with only 171mAh:
- 171mAh / 0.015mA = 11400 hours or 475 days or 1.3 years!
As part of the code improvements for this project, the most important is to put the MCU into sleep while giving some time to the DHT11 to work.
A few additional improvements for power-saving could also be done at the coding level:
- Use "sleep" instead of "delay" for the LED blinking;
- Remove the LED blinking at all;
- Reduce the clock frequency to 8MHz (would require testing);
- Spread the radio TX calls along the 60 seconds to balance the current draw pulses;
- Reduce the message size to spend less time transmitting.
Additional improvement could be done by reducing the frequency the battery and/or power supply voltages are read/sent. Instead of every minute, it could be done every 10 minutes.
Base node hardwareHaving a sensor running at very low-power is useless if you don't have a way to receive the data and publish/store it somewhere. The fact that our remote node is running a very simple RF protocol require us to have a gateway.
The gateway function is to "translate" the messages from a simple format to a more complex format to be used in other systems. We can't expect the sensor node to send data directly into an InfluxDB.
For this solution I've chosen to use another Whisper Node connected via Serial port to a Raspberry PI. Although this is not the only way of implementing it, I believe the RPI is a very flexible, powerful and reliable platform.
Before you think/ask: no you wouldn't never build the sensor node using a RPI powered by battery... try commuting using an 8 axes truck!Base node software
For the software part I've coded it in Python. It's an open scripted language full of modules and libraries. Note that the messages will first arrive into the base Whisper Node's RFM69, which will simply "print" it to the serial port using a binary or hex representation.
The messages are kept in binary as much as possible. This reduces the overall size and increase communication speed. We leave for the RPI to decode those messages and convert into any other desired format.
For this project the messages received by the RPI, via Serial, are decoded and routed to the InfluxDB running on a Cloud server. Again the RPI is powerful enough to decode and re-encode messages in any other format you chose. With a bit of code everything is possible.
For additional resilience and flexibility a queuing service, like RabbitMQ could be using in between the RPI and the InfluxDB and the Cloud Server. In this case even Internet connectivity could be lost that the RPI's RabbitMQ instance would hold the messages and re-sent them all once the Cloud Server is reachable again (see Shovel Plug-in).
Cloud serverMany times we tend to keep all services locally, in our own servers. For many scenarios this is a reasonable approach, but having the option to rent a server in the cloud can be a great alternative.
Do you know that you can "rent" a server for less than USD6.00 at the Google Cloud? By-the-way, I don't have any commercial relationship with Google but it's amazing to have a very reliable service at this price.
The price is for the most basic service, but even that, there's no need to worry about power, connectivity, hardware upgrades, nothing!
Now you still need to pay attention for a few things:
- Security
- Confidentiality
- Capacity to relocate
Although the whole Cloud services can be very secure, it's up to the owner to configure firewall, SSH keys and mechanism to prevent unauthorized access. For example, you probably want to look for ways to prevent everybody on the Internet seeing your SSH service.
For the Confidentiality, in this project, we don't have much to worry about as there's not top-secret data being stored. But for other kinds of project you might need to verify contracts and ways of encrypting the data so it's not easily accessible by the Cloud vendor.
It's very important as well to think ways of moving your services off the Cloud or to another cloud provider. This could be for many reasons, from price change to service quality.
ConclusionDesigning and implementing a remote ultra-low power remote node can be a challenging task. For that reason it's very important to clearly define the requirements instead of guessing how the end-result will look like.
Knowing the available hardware and being familiar with low-power coding sometimes is not enough. It's also necessary to understand measurement techniques and use use the proper tools.
Every uA and ms running count!
The impact of a single design decision might determine if you can run your project for one month or one year using a single battery. In the same way, adding to much complexity to your hardware and software can lead to obscure behavior with disastrous results.
ExtrasHere some interesting graph showing the battery voltage variation along 3 days, where it's easy to see the affect of the ambient temperature over the battery voltage reading.
Note: Although the idea of this article focus more on the hardware and coding techniques, the Whisper Node AVR code, together with the Python gateway code will be uploaded soon, after they're properly commented-out so keep eye on the project attachments.
Comments