Project showcase
Temp. And Humidity Sensor With A CR2032 For Over 1 Year!

Temp. And Humidity Sensor With A CR2032 For Over 1 Year! © GPL3+

Long-range wireless sensor based on Whisper Node capable of running for over a year on a single CR2032, transmitting data every 60 seconds.

  • 81 respects

Components and supplies

Apps and online services

About this project


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.


We 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 results

So 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 problem

To 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.

Picking the components

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 got

Ok, 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 need

Now 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:


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 strategy

As 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

Practical experimentation

To 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() {
    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
    // Turn on the Blue LED and we can measure again
    digitalWrite(T2_WPN_LED_1, HIGH);
    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 consumption

With 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.


  • 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


  • 901.799mA*ms / (60000ms + 301ms) = 901.799mA*ms / 60301ms = 0.01495mA

Sensor node final results

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!

Sensor node code optimization

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 hardware

Having 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 server

Many 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.


Designing 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.


Here 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.


Note this code is designed to run on the Whisper Node but can be adapted to any other AVR MCU. Also, it requires the Talk2 Library, Low-Power Library, RadioHead RFM69 Library and the DHT Adafruit Library as well. The last one has been modified to be more energy efficient - details can be found in the Article.
 * Talk2 Example: DHT11 Sensor Node
 * This example demonstrate how you can use the Talk2 Whisper Node to
 * read and send temperature and humidity data from a DHT11 (or DHT22)
 * to a Base node.
 *  Copyright 2015-2016 by Mike Musskopf.
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  GNU General Public License for more details.
 *  You should have received a copy of the GNU General Public License
 *  along with this program. If not, see <>.
#include <T2WhisperNode.h>
#include <LowPower.h>
#include <DHT.h>

// DHT 11
#define DHT_PWD_PIN A2 // We power the DHT11 via a MCU GPIO so we can control when it's up or not
#define DHT_DATA_PIN A1
#define DHT_TYPE DHT11

// SPI Flash
T2Flash myFlash;

// RFM69 Radio
#define RADIO_FREQUENCY 918.0
#define RADIO_TX_POWER 13
#define RADIO_ENCRYPTION_KEY { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F }
RH_RF69 myRadio;

// T2 Message
T2Message myMsg;
#define nodeAddr 0x81
#define baseAddr 0x0a

void setup()
  // Serial
  Serial.println(F("Example: DHT11 Sensor Node"));

  Serial.println(F("Putting Radio and SPI Flash to Sleep"));
  // Radio - Initialize the radio and put it to sleep to save energy
  uint8_t myRadioEncryptionKey[] = RADIO_ENCRYPTION_KEY;

  // Flash - We're not using, so just power it down to save energy

  // DHT11
  digitalWrite(DHT_PWD_PIN, LOW);

  // Setup the Blue LED pin
  digitalWrite(T2_WPN_LED_1, LOW); // Set LED to Off
  pinMode(T2_WPN_LED_1, OUTPUT);   // Set LED pint to OUTPUT


uint8_t loopCount = 7;

void loop()

  // We only do something every 7 sleep cycles (7 * 8 = 56 seconds)
  if(loopCount == 7)

    // Blue LED blink means we just wake up
    digitalWrite(T2_WPN_LED_1, HIGH);
    digitalWrite(T2_WPN_LED_1, LOW);

    // Do some work!

    loopCount = 0;


  // Using Low-Power library to put the MCU to Sleep
  LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);


void sendTempHumidity()
  // Turn the Sensor ON
  digitalWrite(DHT_PWD_PIN, HIGH);

  // Read temperature as Celsius * 100 to remove any decimals
  // False, True means we don't want Fahrenheit and we want to force the reading.
  int16_t t = dht.readTemperature(false, true) * 100;

  // * 100 to remove any decimals. We do not force the reading, so we use the last info
  uint16_t h = dht.readHumidity() * 100;

  //Turn the Sensor OFF
  digitalWrite(DHT_PWD_PIN, LOW);

  // For Debbuging only

  // Prepare Temperature Message and send
  myMsg.cmd = 0x03; // Return Data
  myMsg.idx = 0x05; // HID and Sensors
  myMsg.sdx = 0x0a; // Temperature[0] = 0x01; // Operation Temp in Celcius * 100[1] = 0x01; // Sensor ID - we only have 1 in this case[2] = t >> 8;[3] = t;
  myMsg.len = 4; // Update length

  // Now change message details with the Humidity details and send it
  myMsg.cmd = 0x03; // Return Data
  myMsg.idx = 0x05; // HID and Sensors
  myMsg.sdx = 0x0b; // Humidity[0] = 0x01; // Operation Humidity in % * 100[1] = 0x01; // Sensor ID - we only have 1 in this case[2] = h >> 8;[3] = h;
  myMsg.len = 4; // Update length


void sendTestVoltage(uint8_t supply)
  uint16_t voltage = 0;

  // Get Voltage readings from supply/battery
  if(supply == 1)
    voltage = T2Utils::readVoltage(T2_WPN_VBAT_VOLTAGE, T2_WPN_VBAT_CONTROL);
    myMsg.sdx = 0x64; // Battery

  if(supply == 2)
    voltage = T2Utils::readVoltage(T2_WPN_VIN_VOLTAGE, T2_WPN_VIN_CONTROL);
    myMsg.sdx = 0x65; // Supply

  // Fill the message details and send it
  myMsg.cmd = 0x03; // Return Data
  myMsg.idx = 0x06; // Node Info[0] = 0x01; // Operation Last Reading[1] = 0x01; // Battery/Supply Index, if multiple supplies[2] = voltage >> 8;[3] = voltage;
  myMsg.len = 4; // Update length



void sendMessage()
  uint8_t radioBufLen = 0;

  // Prepare the Message headers
  myMsg.src = nodeAddr;
  myMsg.dst = baseAddr;

  // Encode Message and get the full length
  myMsg.getSerializedMessage(radioBuf, &radioBufLen);

  // Send it
  myRadio.send(radioBuf, radioBufLen);

Whisper Node Board
Documentation for the Whisper Node Board
Talk2 Arduino Library
Whisper Node Library
Low-Power Library
Library to control ATmega328p power modes/sleeping
DHT Adafruit LIbrary
Note that the original library is not very efficient. Look in the post the details how to replace "delays" with "sleeps" to save energy.


Nordic Article about CR2032
High pulse drain impact on CR2032 coin cell battery capacity
TI Article about coin cells and peak current draw
Coin cells and peak current draw. Using capacitor to minimize voltage drops.
Internet Article on CR2032 and BLE
Study about the CR2032 in Bluetooth Low Energy applications.


Similar projects you might like

MKR1000 Temp and Humidity Sensor

Project tutorial by Don Coleman

  • 31 respects

DHT11 /DHT22 Temperature Sensor

Project tutorial by Daniel Porrey

  • 133 respects

Arduino Temperature - Humidity - Rain Sensor

Project showcase by Rick_Findus

  • 13 respects

Portable Arduino Temp/Humidity Sensor with LCD

Project tutorial by ThothLoki

  • 185 respects

Using the YL-39 + YL-69 Soil Humidity Sensor with Arduino

Project tutorial by Sorin Trimbitas

  • 17 respects

Date, Time, Temperature and Humidity Display

Project tutorial by Chamath Vithanawasam

  • 29 respects
Add projectSign up / Login