There is a ‘low power’ library available for the Arduino. But before we move on to that, how long will the battery last with no modification to the sketch? If you have completed the tutorial Battery power - connecting and charging the Li Po battery, you should now be in a position to monitor the battery’s state of charge (SoC) over time.
We created a sketch in the tutorial Battery power - connecting and charging the Li Po battery. This is the sketch I ran to monitor the battery SoC. The results were recorded in an Excel spreadsheet. Remember, this is with no ‘low power’ modification to our existing set-up.
A few words of explanation:
Date – The day I recorded the SoC measurement.
Time – The time of day. Although you can only see the time in this column, it also includes the date. This is what the first reading looks like in the formula bar. 10/01/2025 13:46:00
Epoch Time – This is the duration in seconds since 1 January 1970 00:00:00 UTC. This gives us an absolute value to work with. Epoch time is calculated from the Time column using this formula - =TEXT((B2-DATE(1970,1,1))*86400, "0").
Time Interval Seconds – This is simply the Epoch Time column with the first value subtracted - =C2-1736516760.
Time Interval Days – This is the Time Interval Seconds column divided by the number of seconds in a day - =D2/86400, to two places of decimals.
SoC % - Is the percentage state of charge as read from our Datacake dashboard.
Send Interval Minutes – Is how often the Arduino sends the SoC data. This is set to ten minutes, but could easily be sent hourly.
Battery – And battery is the serial number of the battery. As you can see, with no modification to the Arduino battery, the battery is flat after about four days.
We can get Excel to draw a graph to illustrate the battery’s rate of decline. Highlight the two columns ‘Time Interval Days’ and ‘SoC %’, then go to ‘Insert’ > ‘Recommended Charts’ > ‘Scatter’, then click ‘OK’. You should see something like this:
The line may have been a bit straighter had I taken the SoC readings at regular intervals.
We can get Excel to straighten it for us. Click in the ‘Tell me what you want to do’ box in the green bar at the top and search ‘Trendline’, then choose ‘Linear’. Go back into the search box and search ‘More Trendline Options’. In the box that appears, select ‘Display Equation on Chart’. You should end up with something like this.
The equation we asked to be inserted is at the bottom right of the chart, and reads: y=-23.208x + 92.342. What this formula means, is that for every unit increase in the x axis (the number of days) the value of y (the SoC %) decreases by 23.208. The number of 92.342 is the value of y when x is zero, in other words, this is our starting value of SoC %.
In summary, we are losing around 23% of our charge for every day that passes. So nearly a quarter. No wonder the battery only lasted four days!
We will now repeat the exercise, but after we have installed the Low Power library. We will be using the same sketch as before, but with the library included.
To install the low power library, open the sketch in your Arduino IDE. Then go to Sketch > Include Library > Manage libraries. Click on Manage libraries, then search for ‘Arduino Low Power’. Select the latest version, then click ‘Install’.
This is what it looks like in Arduino IDE 2:
In order to use the library, it only takes a couple of lines of code. Here is the sketch we used before, but with the new lines of code highlighted in bold. The first new line near the top of the sketch includes the ‘ArduinoLowPower’ library in the sketch. The second new line is near the bottom of the sketch and uses the ‘LowPower.deepSleep’ method to turn off all non-essential power consumption.
// For LoRa:
#include <MKRWAN.h>
// For temperature readings:
#include <OneWire.h>
#include <DallasTemperature.h>>
// For battery monitor:
#include "Adafruit_MAX1704X.h"
Adafruit_MAX17048 maxlipo;
// For battery charger
#include <Arduino_PMIC.h>
// For LowPower
#include "ArduinoLowPower.h"
LoRaModem modem;
// Please enter your sensitive data in the arduino_secrets.h tab
#include "arduino_secrets.h"
// Start of temperature reading code...
// Data wire is conected to the Arduino digital pin 4
#define ONE_WIRE_BUS 4 // labelled ~4 on the mkr wan board
// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature sensor
DallasTemperature sensors(&oneWire);
// initialises a variable to take temperature data:
//float tempVariable = 0;
int tempVariable = 0; //LoRa payload needs integers.
// initialises a variable for battery state of charge:
int cellPercent = 0;
// ...end of temperature reading code.
// We need the details below to connect your device to LoRa
// Read arduino secrets
String appEui = APP_EUI;
String appKey = APP_KEY;
void setup() {
// Put your setup code here, to run once:
// Start of battery charger code...
if (!PMIC.begin()) {
Serial.println("Failed to initialize PMIC!");
while (1);
}
// Set the input current limit to 2 A and the overload input voltage to 3.88 V
if (!PMIC.setInputCurrentLimit(2.0)) {
Serial.println("Error in set input current limit");
}
if (!PMIC.setInputVoltageLimit(3.88)) {
Serial.println("Error in set input voltage limit");
}
// set the minimum voltage used to feeding the module embed on Board
if (!PMIC.setMinimumSystemVoltage(3.5)) {
Serial.println("Error in set minimum system volage");
}
// Set the desired charge voltage to 4.11 V
if (!PMIC.setChargeVoltage(4.2)) {
Serial.println("Error in set charge volage");
}
// Set the charge current to 375 mA
// the charge current should be definde as maximum at (C for hour)/2h
// to avoid battery explosion (for example for a 750mAh battery set to 0.375 A)
if (!PMIC.setChargeCurrent(0.375)) {
Serial.println("Error in set charge current");
}
Serial.println("Initialization done!");
if (!PMIC.enableCharge()) {
Serial.println("Error enabling Charge mode");
}
// End of battery charger code.
Serial.begin(9600);
while (!Serial && millis() < 10000); //This line is needed to work with batteries https://forum.arduino.cc/t/it-does-not-work-powered-by-2xaa-batteries/520097/5
sensors.begin();
// The code below connects your device to LoRa
if (!modem.begin(EU868)) { // change this to your regional band (eg. US915, AS923, ...)
Serial.println("Failed to start module");
while (1) {}
};
Serial.print("Your module version is: ");
Serial.println(modem.version());
Serial.print("Your device EUI is: ");
Serial.println(modem.deviceEUI());
int connected = modem.joinOTAA(appEui, appKey);
// Start of battery monitor code...
Serial.println(F("\nAdafruit MAX17048 simple demo"));
if (!maxlipo.begin()) {
Serial.println(F("Couldnt find Adafruit MAX17048?\nMake sure a battery is plugged in!"));
while (1) delay(10);
}
Serial.print(F("Found MAX17048"));
Serial.print(F(" with Chip ID: 0x"));
Serial.println(maxlipo.getChipID(), HEX);
// ...end of battery monitor code.
if (!connected) {
Serial.println("Something went wrong; are you indoors? Move near a window and retry");
while (1) {}
}
}
void loop() {
// put your main code here, to run repeatedly:
// Start of temperature reading code...
// Call sensors.requestTemperatures() to issue a global temperature and Requests to all devices on the bus
sensors.requestTemperatures();
Serial.print("Celsius temperature: ");
// Why "byIndex"? You can have more than one IC on the same bus. 0 refers to the first IC on the wire
Serial.print(sensors.getTempCByIndex(0));
Serial.print(" - Fahrenheit temperature: ");
Serial.println(sensors.getTempFByIndex(0));
// Populate our tempVariable variable.
tempVariable = (sensors.getTempCByIndex(0));
// We can have a problem if the sensors detect a minus temperature. In which case the tempVariable sends 255.
// Lets add on 50 to make sure we can send a positive value for temperature (unless you are living through an Antarctic winter).
// Don't forget to deduct 50 when you run the decoder at the other end.
tempVariable = tempVariable + 50;
// ...end of temperature reading code.
// Start of battery monitor code...
cellPercent =(maxlipo.cellPercent());
Serial.print(F("Batt Voltage: ")); Serial.print(maxlipo.cellVoltage(), 3); Serial.println(" V");
Serial.print(F("Batt Percent: ")); Serial.print(maxlipo.cellPercent(), 1); Serial.println(" %");
Serial.println();
// ...end of battery monitor code.
byte payload[2]; // The [2] means we are sending two items in the array. Arrays start at zero.
payload[0] = tempVariable; // Send temperature by LoRa.
payload[1] = cellPercent; // Send state of charge by LoRa.
delay(1000); // Gives it time to execute.
modem.beginPacket();
modem.write(payload,2); // The number after the word 'payload' is the number of items sent in the array above. If you add another item, increment this number.
int err = modem.endPacket(false);
if (err > 0) {
Serial.println("Data Sent");
} else {
Serial.println("Error");
}
Serial.print("temp data sent is: ");
Serial.print("temp is: "); Serial.println(tempVariable);
Serial.print("battery % is: "); Serial.println(cellPercent);
LowPower.deepSleep(600000); // 10 minute intervals
//delay(600000); // This is the interval between each temperature and state of charge reading. The default is five minutes.
//delay(30000); // We can use 30 seconds for testing.
}
The completed sketch is available on Github, you can download it here.
Don't forget to include your arduino_secrets.h file.
Once we have recharged the battery, we are now ready to test the battery SoC again. As before, we will record our results in an Excel spreadsheet:
This time I have tried to take the SoC readings at more regular intervals, usually in the morning for each day from the 16th to the 30th January. The last two readings were taken a few days apart.
This time the equation reads: y=-0.7712x + 94.24.
So, for every unit increase in the x axis (the number of days) the value of y (the SoC %) decreases by 0.7712. That’s about three quarters of a percent a day!
If we were to divide the starting charge of 94.24% by the rate of discharge at 0.7712% a day, we find that the battery will not be fully discharged for another 122 days, around four months!
Those two lines of code have certainly made a great difference to the life expectancy of the battery.
And this is with the Arduino sending data every ten minutes. Imagine how long the battery would last if we sent data once an hour, or once a day. But that test can wait for another time
If you have followed all of the previous tutorials, you should now be able to collect sensor data, send the data to the internet using long range radio, and visualise that data on an application such as Datacake.
With this basic set-up in place, you could try swapping some of the components. For instance, you could try different sensors. Or send your data to different dashboards, such as Cayenne or Grafana, or even your own website. You could also use a different microcontroller, for example Seeeduino LoRaWAN or RAKwireless WisBlock.
Meanwhile, happy experimenting!