Datalogging Weather Station (Battery Powered)

Updated 29 April 2022

This project will measure atmospheric conditions (Temperature, Humidity, Air Pressure) and log them to an SD card over a long period (months!) while running off a few AA or AAA cells.

Usually, projects powered by an Arduino Uno are quite power hungry and not really appropriate for extended battery power. Using a Power Timer module allows us to cycle power to the Arduino at a set interval (eg. 10 minutes) - that way we can have the Arduino wake up to perform measurements and datalogging, then send a shutdown signal to the power timer which will completely remove power until the next measurement interval.

Bill of Materials

Note on batteries: to maintain the correct voltages, this project requires either:

  • 3xAA / AAA alkaline cells, OR;
  • 4xAA / AAA Nickle-Metal Hydride (NiMH) Rechargeable Cells

Initial Assembly

To begin you'll need to solder headers to the pressure sensor and power timer. You can jig them up in the breadboard, to keep the pins square during soldering.

Mount the modules

Plug the SD card module into the far left of the breadboard and connect GND to the negative bus. That's the right-most pin.

Fold the leads on the DHT11 at a right angle, and plug it in next to the SD card module. Connect Pin1 to the positive bus. Connect Pin4 to the negative bus. Connect the 10K resistor between Pin1 and Pin2

Plug the pressure sensor in next to the DHT11. Connect Vin to the positive bus, and GND to the negative bus.

mount-the-sd-card-modulemount-the-dht-sensormount-mpl-3115

SD card connections

Connect Arduino:Pin13 to SD:SCK

Connect Arduino:Pin12 to SD:MISO

Connect Arduino:Pin11 to SD:MOSI

Connect Arduino:Pin4 to SD:SS (sometimes labelled CS)

Connect Arduino 3V3 to SD:3.3V

sd-card-connections-datasd-card-connections-to-arduinosd-card-power

Temperature, Humidity data connection

  • Connect the 10K resistor (that came with the DHT11) between Pin1 and Pin2 of the DHT11.
  • Connect the DHT11 data pin (Pin2) to the Arduino Pin2
dht-resistordht11-data-connectiondht11-data-connection-arduino

Pressure sensor data connections

  • Connect Pressure Sensor:SDA to Arduino:SDA
  • Connect Pressure Sensor:SCL to Arduino:SCL
mpl3115-datampl3115-data-arduino-uno

Power to breadboard

  • Connect a black jumper wire from Arduino:GND to the negative bus.
  • Connect a red jumper wire from Arduino:5V to the positive bus

breadboard-power-connectionbreadboard-power-connection-arduino

Program and Test

All our sensors and storage are wired up. This is a good point to bench test the system before adding the complexity of the power timer. Insert the SD card into the SD module and upload the following script to your Arduino.

/*
   Weather Station Datalogger
   This project samples various sensors and logs the results to an SD card.
   The data file saved to the SD card is comma-separated so it can be easily
   imported into a spreadsheet eg. Excel
   Power is managed by an external power timer, so this project can run for a
   long time on batteries.

   Michael Ruppe | Core Electronics | July 2021

   No warranty expressed or implied
*/


#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <EEPROM.h>
#include <Adafruit_MPL3115A2.h>
#include "DHT.h"

#define DATA_FILE_NAME "data.txt"

const int sampleRate = 15; // [minutes] between each sample, as configured by the SparkFun power timer with switches D+E

const int powerCtrlPin = 6; // This pin will send the "Done" signal to the power timer
const int chipSelect = 4; // SD card CS (or "SS") pin
const int dhtPin = 2; // DHT11 data pin
// Uncomment whatever type you're using
#define DHTTYPE DHT11   // DHT 11
//#define DHTTYPE DHT22   // DHT 22  (AM2302), AM2321
//#define DHTTYPE DHT21   // DHT 21 (AM2301)
DHT dht(dhtPin, DHTTYPE);
Adafruit_MPL3115A2 baro = Adafruit_MPL3115A2();

const int eeAddress = 0; // where we will keep sample number in EEPROM
unsigned long sampleNumber = 0;

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  // Initialise SD card
  if (!SD.begin(chipSelect)) {
    Serial.println("*** Could not find SD card");
  } else {
    Serial.println("SD Card found");
  }

  // Initialise Pressure sensor
  if (! baro.begin()) {
    Serial.println("*** Could not find Pressure Sensor");
    return;
  } else {
    Serial.println("Pressure Sensor found");
  }

  // Initialise Temperature/Humidity sensor
  dht.begin();
  float h = dht.readHumidity(); // Read humididy (%RH)
  float t = dht.readTemperature();  // Read temperature as Celsius (the default)
  Serial.print("Initial  Temperature: "); Serial.print(t); Serial.print(" °C");
  Serial.print("  Humidity: "); Serial.print(h); Serial.println(" %");

}

void loop() {
  // Sample weather data
  float h = dht.readHumidity();
  float t = dht.readTemperature();

  // Barometric pressure (and altitude)
  float pascals = baro.getPressure();
  float hPa = pascals / 100.0;
  float altm = baro.getAltitude();

  // Check if data file already exists
  bool createNewFile = false;
  File dataFile = SD.open(DATA_FILE_NAME); //open with read permission
  if (!dataFile) createNewFile = true; // if the file does not exist, that means we're about to create it
  dataFile.close(); delay(1);

  // Write data to file
  dataFile = SD.open(DATA_FILE_NAME, FILE_WRITE); // open with write permission
  if (dataFile) { // if we can access the file
    if (createNewFile == true) { // Print a nice header at the top of our file if it is the first time we access it
      dataFile.println("SampleNumber,MissionTime[dhm],MissionTime[minutes],Temperature[C],Humididy[%],Pressure[hPa],Altitude[m]");
      sampleNumber = 0;
      EEPROM.put(eeAddress, sampleNumber); // Reset the sample count
    }
    EEPROM.get(eeAddress, sampleNumber); // Retrieve the sample count from memory
    const long day = 1440;  // minutes in a day
    const long hour = 60;   // minutes in an hour
    unsigned long timeNow = sampleRate * sampleNumber;  // the current mission time [minutes]
    unsigned long days = timeNow / day;                 // current mission time [days component]
    unsigned long hours = (timeNow % day) / hour;       // current mission time [hours component]
    unsigned long minutes = (timeNow % day) % hour;     // current mission time [minutes component]

    // construct data string - concatenate each piece of data
    String dataString = "";
    dataString += String(sampleNumber); dataString += ",";
    dataString += String(days); dataString += "d"; dataString += String(hours); dataString += "h"; dataString += (minutes); dataString += "m,"; // The mission time in minutes only - for nice Excel Plots
    dataString += String(timeNow); dataString += ","; // The mission time in minutes only - for nice Excel Plots
    dataString += String(t); dataString += ",";
    dataString += String(h); dataString += ",";
    dataString += String(hPa); dataString += ",";
    dataString += String(altm);
    Serial.println(dataString);
    dataFile.println(dataString); // Commit the samples to the data file
    dataFile.close();
    EEPROM.put(eeAddress, ++sampleNumber); // Update the sample count and store
  } else {
    Serial.println("Error, couldn't open data file");
    dataFile.close();
  }

  // We're done, remove power, wait for next powerup
  pinMode(powerCtrlPin, OUTPUT);
  digitalWrite(powerCtrlPin, HIGH);
  delay(1000);

  // We should never reach this point under intended operation conditions (using the power timer)
  // But while we're commissioning on the bench without the power timer,
  // it's useful to loop past this point, so we can check the datalogger is working.


}

In the Arduino serial console (left image) you should see some initialisation messages that will tell you if the Arduino can connect to the SD card and each sensor. Then the console will begin logging data with a short interval.

If you de-power your Arduino, and transfer the SD card to a computer, you ought to see a DATA.TXT file on the SD card. The first line of the file is a header that tells us what each column of data means. Each row after the first is one sample. The samples are indexed with a SampleNumber, and the MissionTime is also shown in days-hours-minutes (human-readable), and minutes-only (for easy plotting in Excel). The following entries on the row are for temperature, humididty, air pressure, and altitude (inferred from air pressure).

example-data-console-arduinoexample-data-file

Notice that the MissionTime parameter increases by the amount set by the sampleRate variable (defined near the top of the code) which is 10 minutes by default. The sampleRate variable should be set to the same interval as your power timer for the time entries to make sense. We'll set that up next.

Add Power Timer

Once you're satisfied the code is working, it's time to add the power timer.

  • Mount the power timer and connect the GND pin to the Negative Bus.
  • Connect the Timer:DRV pin to the Positive Bus through the Schottky Diode. Pay attention to the direction of the diode, shown by the silver/white stripe.
  • Connect Timer:Done to Arduino:Pin6
  • Connect your battery pack (with power switch OFF)

The diode will protect the power timer - allowing you to connect your Arduino to your computer by USB, without fear of back-feeding power into the timer.

If you decide to use the SparkFun Power timer, I've included the wiring instructions in the Appendix.

connect-power-timer-to-breadboardconnect-power-timer-to-arduinoconnect-power-timer-to-batteries

For the timer to run with a 10 minute period, the resistance between GND and Delay pins should be 57.44kOhm. You can measure the resistance between these pins with a multimeter and turn the on-board potentiometer to dial in the time you desire. For a full rundown on this module's use, check out the documentation.

Dial in your desired time and turn on the battery pack. The timer should take care of the rest - cycling power to the Arduino at your set interval. You can always set the interval to something shorter to make sure everything is working.

Experiment

I set my timer to an interval of about six seconds and left my datalogger running overnight. (my sampleRate variable was set to 10 minutes). The plot below was generated by importing the DATA.TXT file into Excel. Even though the actual time that passed was only about 16 hours, the Arduino took enough samples to simlate running a 15 day mission, sampling every 10 minutes - result!

simulated-mission-data

A foray into power analysis

So how long will this project be able to run for? Well... it depends. A better question might be "How many samples can this project take?" Since the project is being power cycled, and each cycle ought to take the same amount of time, it stands to reason we can figure out how many samples the project can make before the batteries run flat.

We busted out the Otii Arc to measure the power profile, and here's what we found. The image below shows a measurement interval, lasting a few seconds. I'm powering my project from a 3AAA alkaline battery pack. The software shows the energy used by a single sample (121 micro Watt hours)

analysing-power-usage

From left to right: At 14s the circuit powers on. The Arduino's bootloader runs for about two seconds, until about 16s, at which point we see a step-up in power usage as the Arduino code begins to execute. The sensors are sampled, the data is saved and at 16.75s the Arduino sends the DONE signal and the circuit shuts down.

According to this resource, a high-quality AAA cell can have a capacity of 1.87 Wh... 1.87 [Wh] / 121e-6 [Wh/sample]  15,000 samples! For a 3xAAA alkaline cell setup, let's assume we can only use half the capacity of the cells before the voltage falls too low for the arduino to use - still that's nearly 8000 samples. With an interval of 10 minutes, that's enough to last about a month! You could assume that doubling the interval time would also double the total datalogging time.

Now, the Arduino bootloader runs for about 2 seconds on an Uno. If you directly program your Arduino using an In System Programmer (another Arduino!) then this bootloader sequence (the first two seconds in the plot) is removed and the Arduino boots instantly. That means the power usage can get cut almost in half!

Let's be really pessemistic and assume we can only make use of a third of our battery capacity... Some back-of-the-envelope maths with the energy-usage measured earlier gives us the following table with expected battery life for some reasonable sample intervals:

battery-life-estimations

Download a copy of this tool here.

Conclusion

This project was a great first-go at creating a battery powered project. Using a power timer makes for an easy stepping stone - taking power-hungry prototypes and getting them running much longer than they ordinarily would. One part I found took a bit of consideration was writing the code to handle powering up gracefully. If you think about it, every power-up could be the first, where the data file needs to be created and poluated with the column headings. Every power-up after that is just business as usual - log the data without reprinting the file header.

For now, my datalogger is logging away... I'll let you know how far it gets once the batteries go flat!

Happy Making!

Edit: Some empirical results

To test the actual battery life I set the power timer up to the smallest interval I could easily program that would allow the Arduino to power-on, sample, log and shutdown - about 6 seconds. I left the datalogger running for several days until the 3xAAA alkaline cells were flat and collected a whopping 27,000 samples - that's nearly 190 days.

Why the big difference? Perhaps my estimates of usable capacity were very conservative - perhaps the usable capacity is higher than a third. Also, power measurements made in the Power Analysis section were made at a supply voltage of 4.2V. As the cells drain and the supply voltage drops, the power-usage per sample will also drop. In any case, thats a very promising result!

Appendix

I recommend using the SparkFun Power Timer because it seems much easier to set to a desired interval - by using a combination of switch positions. Here's how I'd wire that one up for this project:

  • Connect the diode from Power Timer +Out to the Positive Bus
  • Connect a ground on the Power Timer to the Negative Bus
  • Connect Arduino:Pin6 to the Power Timer:Done pin
  • Connect your Battery Pack (+) to Power Timer:IN+
  • Connect your Battery Pack (-) to Power Timer:IN-

If your battery pack has the appropriate JST connector already fitted - you can simply plug your battery pack into the Power Timer's battery connector!

alternative-power-timer

The SparkFun tutorial for this product provides handy tables to decide on what switch combination creates a certain interval. Reading from their table, setting switch D and E should give a delay of 15 minutes.

Have a question? Ask the Author of this guide today!

Please enter minimum 20 characters

Your comment will be posted (automatically) on our Support Forum which is publicly accessible. Don't enter private information, such as your phone number.

Expect a quick reply during business hours, many of us check-in over the weekend as well.

Comments


Loading...
Feedback

Please continue if you would like to leave feedback for any of these topics:

  • Website features/issues
  • Content errors/improvements
  • Missing products/categories
  • Product assignments to categories
  • Search results relevance

For all other inquiries (orders status, stock levels, etc), please contact our support team for quick assistance.

Note: click continue and a draft email will be opened to edit. If you don't have an email client on your device, then send a message via the chat icon on the bottom left of our website.

Makers love reviews as much as you do, please follow this link to review the products you have purchased.