Design a site like this with WordPress.com
Get started

ESP32 Data Logging Temperature to MicroSD Card

This project shows how to log data with timestamps to a microSD card using the ESP32. As an example, we’ll log temperature readings from the DS18B20 sensor every 10 minutes. The ESP32 will be in deep sleep mode between each reading, and it will request the date and time using Network Time Protocol (NTP).

Project Overview

Before getting started, let’s highlight the project’s main features:

  • The ESP32 reads temperature using the DS18B20 temperature sensor.
  • After getting the temperature, it makes a request to an NTP (Network Time Protocol) server to get date and time. So, the ESP32 needs a Wi-Fi connection.
  • The data (temperature and timestamp) are logged to a microSD card. To log data to the microSD card we’re using a microSD card module.
  • After completing these previous tasks, the ESP32 sleeps for 10 minutes.
  • The ESP32 wakes up and repeats the process.

Parts Required

  • ESP32 DOIT DEVKIT V1 Board – read ESP32 Development Boards Review and Comparison
  • MicroSD card module
  • MicroSD card
  • DS18B20 temperature sensor
  • 10k Ohm resistor
  • Jumper wires
  • Breadboard

Preparing the microSD Card Module

To save data on the microSD card with the ESP32, we use the following microSD card module that communicates with the ESP32 using SPI communication protocol.

Formatting the microSD card

When using a microSD card with the ESP32, you should format it first. Follow the next instructions to format your microSD card.

1. Insert the microSD card in your computer. Go to My Computer and right click on the SD card. Select Format as shown in figure below.

2. A new window pops up. Select FAT32, press Start to initialize the formatting process and follow the onscreen instructions.

Schematic

Follow the next schematic diagram to assemble the circuit for this project.

You can also use the following table as a reference to wire the microSD card module:

MicroSD Card ModuleESP32
3V33V3
CSGPIO 5
MOSIGPIO 23
CLKGPIO 18
MISOGPIO 19
GNDGND

The next figure shows how your circuit should look like:

Preparing the Arduino IDE

There’s an add-on for the Arduino IDE that allows you to program the ESP32 using the Arduino IDE and its programming language. Follow one of the next tutorials to prepare your Arduino IDE to work with the ESP32, if you haven’t already.

After making sure you have the ESP32 add-on installed, you can continue with this tutorial.

Installing Libraries

Before uploading the code, you need to install some libraries in your Arduino IDE.  The OneWire library by Paul Stoffregen and the Dallas Temperature library, so that you can use the DS18B20 sensor. You also need to install the NTPClient library forked by Taranais to make request to an NTP server.

Follow the next steps to install those libraries in your Arduino IDE:

OneWire library

  1. Click here to download the OneWire library. You should have a .zip folder in your Downloads
  2. Unzip the .zip folder and you should get OneWire-master folder
  3. Rename your folder from OneWire-master to OneWire
  4. Move the OneWire folder to your Arduino IDE installation libraries folder
  5. Finally, re-open your Arduino IDE

Dallas Temperature library

  1. Click here to download the DallasTemperature library. You should have a .zip folder in your Downloads
  2. Unzip the .zip folder and you should get Arduino-Temperature-Control-Library-master folder
  3. Rename your folder from Arduino-Temperature-Control-Library-master to DallasTemperature
  4. Move the DallasTemperaturefolder to your Arduino IDE installation libraries folder
  5. Finally, re-open your Arduino IDE

NTPClient library

  1. Click here to download the NTPClient library. You should have a .zip folder in your Downloads
  2. Unzip the .zip folder and you should get NTPClient-master folder
  3. Rename your folder from NTPClient-master to NTPClient
  4. Move the NTPClientfolder to your Arduino IDE installation libraries folder
  5. Finally, re-open your Arduino IDE

Uploading Code

Here’s the code you need to upload to your ESP32. Before uploading, you need to modify the code to include your network credentials (SSID and password). Continue reading to learn how the code works.

/*********
 
  Complete to Gnd_To_Vcc  
*********/

// Libraries for SD card
#include "FS.h"
#include "SD.h"
#include <SPI.h>

//DS18B20 libraries
#include <OneWire.h>
#include <DallasTemperature.h>

// Libraries to get time from NTP Server
#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>

// Define deep sleep options
uint64_t uS_TO_S_FACTOR = 1000000;  // Conversion factor for micro seconds to seconds
// Sleep for 10 minutes = 600 seconds
uint64_t TIME_TO_SLEEP = 600;

// Replace with your network credentials
const char* ssid     = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

// Define CS pin for the SD card module
#define SD_CS 5

// Save reading number on RTC memory
RTC_DATA_ATTR int readingID = 0;

String dataMessage;

// Data wire is connected to ESP32 GPIO 21
#define ONE_WIRE_BUS 21
// Setup a oneWire instance to communicate with a OneWire device
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature sensor 
DallasTemperature sensors(&oneWire);

// Temperature Sensor variables
float temperature;

// Define NTP Client to get time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

// Variables to save date and time
String formattedDate;
String dayStamp;
String timeStamp;

void setup() {
  // Start serial communication for debugging purposes
  Serial.begin(115200);

  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected.");

  // Initialize a NTPClient to get time
  timeClient.begin();
  // Set offset time in seconds to adjust for your timezone, for example:
  // GMT +1 = 3600
  // GMT +8 = 28800
  // GMT -1 = -3600
  // GMT 0 = 0
  timeClient.setTimeOffset(3600);

  // Initialize SD card
  SD.begin(SD_CS);  
  if(!SD.begin(SD_CS)) {
    Serial.println("Card Mount Failed");
    return;
  }
  uint8_t cardType = SD.cardType();
  if(cardType == CARD_NONE) {
    Serial.println("No SD card attached");
    return;
  }
  Serial.println("Initializing SD card...");
  if (!SD.begin(SD_CS)) {
    Serial.println("ERROR - SD card initialization failed!");
    return;    // init failed
  }

  // If the data.txt file doesn't exist
  // Create a file on the SD card and write the data labels
  File file = SD.open("/data.txt");
  if(!file) {
    Serial.println("File doens't exist");
    Serial.println("Creating file...");
    writeFile(SD, "/data.txt", "Reading ID, Date, Hour, Temperature \r\n");
  }
  else {
    Serial.println("File already exists");  
  }
  file.close();

  // Enable Timer wake_up
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);

  // Start the DallasTemperature library
  sensors.begin(); 

  getReadings();
  getTimeStamp();
  logSDCard();
  
  // Increment readingID on every new reading
  readingID++;
  
  // Start deep sleep
  Serial.println("DONE! Going to sleep now.");
  esp_deep_sleep_start(); 
}

void loop() {
  // The ESP32 will be in deep sleep
  // it never reaches the loop()
}

// Function to get temperature
void getReadings(){
  sensors.requestTemperatures(); 
  temperature = sensors.getTempCByIndex(0); // Temperature in Celsius
  //temperature = sensors.getTempFByIndex(0); // Temperature in Fahrenheit
  Serial.print("Temperature: ");
  Serial.println(temperature);
}

// Function to get date and time from NTPClient
void getTimeStamp() {
  while(!timeClient.update()) {
    timeClient.forceUpdate();
  }
  // The formattedDate comes with the following format:
  // 2018-05-28T16:00:13Z
  // We need to extract date and time
  formattedDate = timeClient.getFormattedDate();
  Serial.println(formattedDate);

  // Extract date
  int splitT = formattedDate.indexOf("T");
  dayStamp = formattedDate.substring(0, splitT);
  Serial.println(dayStamp);
  // Extract time
  timeStamp = formattedDate.substring(splitT+1, formattedDate.length()-1);
  Serial.println(timeStamp);
}

// Write the sensor readings on the SD card
void logSDCard() {
  dataMessage = String(readingID) + "," + String(dayStamp) + "," + String(timeStamp) + "," + 
                String(temperature) + "\r\n";
  Serial.print("Save data: ");
  Serial.println(dataMessage);
  appendFile(SD, "/data.txt", dataMessage.c_str());
}

// Write to the SD card (DON'T MODIFY THIS FUNCTION)
void writeFile(fs::FS &fs, const char * path, const char * message) {
  Serial.printf("Writing file: %s\n", path);

  File file = fs.open(path, FILE_WRITE);
  if(!file) {
    Serial.println("Failed to open file for writing");
    return;
  }
  if(file.print(message)) {
    Serial.println("File written");
  } else {
    Serial.println("Write failed");
  }
  file.close();
}

// Append data to the SD card (DON'T MODIFY THIS FUNCTION)
void appendFile(fs::FS &fs, const char * path, const char * message) {
  Serial.printf("Appending to file: %s\n", path);

  File file = fs.open(path, FILE_APPEND);
  if(!file) {
    Serial.println("Failed to open file for appending");
    return;
  }
  if(file.print(message)) {
    Serial.println("Message appended");
  } else {
    Serial.println("Append failed");
  }
  file.close();
}

How the Code Works

In this example, the ESP32 is in deep sleep mode between each reading. In deep sleep mode, all your code should go in the setup() function, because the ESP32 never reaches the loop().

Importing libraries

First, you import the needed libraries for the microSD card module:

#include "FS.h"
#include "SD.h"
#include <SPI.h>

Import these libraries to work with the DS18B20 temperature sensor.

#include <OneWire.h>
#include <DallasTemperature.h>

The following libraries allow you to request the date and time from an NTP server.

#include <WiFi.h>
#include <NTPClient.h>
#include <WiFiUdp.h>

Setting deep sleep time

This example uses a conversion factor from microseconds to seconds, so that you can set the sleep time in the TIME_TO_SLEEP variable in seconds.

In this case, we’re setting the ESP32 to go to sleep for 10 minutes (600 seconds). If you want the ESP32 to sleep for a different period of time, you just need to enter the number of seconds for deep sleep in the TIME_TO_SLEEP variable.

// Define deep sleep options
uint64_t uS_TO_S_FACTOR = 1000000; // Conversion factor for micro seconds to seconds
// Sleep for 10 minutes = 600 seconds
uint64_t TIME_TO_SLEEP = 600;

Setting your network credentials

Type your network credentials in the following variables, so that the ESP32 is able to connect to your local network.

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Initializing sensors and variables

Next, define the microSD card SD pin. In this case, it is set to GPIO 5.

#define SD_CS 5

Create a variable called readingID to hold the reading ID. This is a way to get your readings organized. To save a variable value during deep sleep, we can save it in the RTC memory. To save data on the RTC memory, you just need to add RTC_DATA_ATTR before the variable definition.

// Save reading number on RTC memory
RTC_DATA_ATTR int readingID = 0;

Create a String variable to hold the data to be saved on the microSD card.

String dataMessage;

Next, create the instances needed for the temperature sensor. The temperature sensor is connected to GPIO 21.

// Data wire is connected to ESP32 GPIO21
#define ONE_WIRE_BUS 21
// Setup a oneWire instance to communicate with a OneWire device
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature sensor 
DallasTemperature sensors(&oneWire);

Then, create a float variable to hold the temperature retrieved by the DS18B20 sensor.

float temperature;

The following two lines define an NTPClient to request date and time from an NTP server.

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

Then, initialize String variables to save the date and time.

String formattedDate;
String dayStamp;
String timeStamp;

setup()

When you use deep sleep with the ESP32, all the code should go inside the setup() function, because the ESP32 never reaches the loop().https://tpc.googlesyndication.com/safeframe/1-0-37/html/container.htmlReport this ad

Connecting to Wi-Fi

The following snippet of code connects to the Wi-Fi network. You need to connect to wi-fi to request date and time from the NTP server.

Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.print(".");
}

Initializing the NTP client

Next, initialize the NTP client to get date and time from an NTP server.

timeClient.begin();

You can use the setTimeOffset(<time>) method to adjust the time for your timezone.

timeClient.setTimeOffset(3600);

Here are some examples for different timezones:

  • GMT +1 = 3600
  • GMT +8 = 28800
  • GMT -1 = -3600
  • GMT 0 = 0

Initializing the microSD card module

Then, initialize the microSD card. The following if statements check if the microSD card is properly attached.

SD.begin(SD_CS); 
if(!SD.begin(SD_CS)) {
  Serial.println("Card Mount Failed");
  return;
}
uint8_t cardType = SD.cardType();
if(cardType == CARD_NONE) {
  Serial.println("No SD card attached");
  return;
}
Serial.println("Initializing SD card...");
if (!SD.begin(SD_CS)) {
  Serial.println("ERROR - SD card initialization failed!");
  return; // init failed
}

https://tpc.googlesyndication.com/safeframe/1-0-37/html/container.htmlReport this ad

Then, try to open the data.txt file on the microSD card.

File file = SD.open("/data.txt");

If that file doesn’t exist, we need to create it and write the heading for the .txt file.

writeFile(SD, "/data.txt", "Reading ID, Date, Hour, Temperature \r\n");

If the file already exists, the code continues.

else {
  Serial.println("File already exists");
}

Finally, we close the file.

file.close();

Enable timer wake up

Then, you enable the timer wake up with the timer you’ve defined earlier in the TIME_TO_SLEEP variable.

esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);

Initializing the library for DS18B20

Next, you initialize the library for the DS18B20 temperature sensor.

sensors.begin();

Getting the readings and data logging

After having everything initialized, we can get the readings, timestamp, and log everything into the microSD card.

To make the code easier to understand, we’ve created the following functions:

  • getReadings(): reads the temperature from the DS18B20 temperature sensor;
  • getTimeStamp(): gets date and time from the NTP server;
  • logSDcard(): logs the preceding data to the microSD card.

After completing these tasks, we increment the readingID.

readingID++;

Finally, the ESP32 starts the deep sleep.

esp_deep_sleep_start();

getReadings()

Let’s take a look at the getReadings() function. This function simply reads temperature from the DS18B20 temperature sensor.

sensors.requestTemperatures(); 
temperature = sensors.getTempCByIndex(0); // Temperature in Celsius

By default, the code retrieves the temperature in Celsius degrees. You can uncomment the following line and comment the previous one to get temperature in Fahrenheit.

//temperature = sensors.getTempFByIndex(0); // Temperature in Fahrenheit

getTimeStamp()

The getTimeStamp() function gets the date and time. These next lines ensure that we get a valid date and time:

while(!timeClient.update()) {
  timeClient.forceUpdate();
}

Sometimes the NTPClient retrieves the year of 1970. To ensure that doesn’t happen we force the update.

Then, convert the date and time to a readable format with the getFormattedDate() method:

formattedDate = timeClient.getFormattedDate();

The date and time are returned in this format:

2018-04-30T16:00:13Z

So, we need to split that string to get date and time separately. That’s what we do here:

// Extract date
int splitT = formattedDate.indexOf("T");
dayStamp = formattedDate.substring(0, splitT);
Serial.println(dayStamp);
// Extract time
timeStamp = formattedDate.substring(splitT+1, formattedDate.length()-1);
Serial.println(timeStamp);

The date is saved on the dayStamp variable, and the time on the timeStamp variable.

logSDCard()

The logSDCard() function concatenates all the information in the dataMessage String variable. Each reading is separated by commas.

dataMessage = String(readingID) + "," + String(dayStamp) + "," + String(timeStamp) + "," + String(temperature) + "\r\n";

Note: the “\r\n” at the end of the dataMessagevariable ensures the next reading is written on the next line.

Then, with the following line, we write all the information to the data.txt file in the microSD card.

appendFile(SD, "/data.txt", dataMessage.c_str());

Note: the appendFile() function only accepts variables of type const char for the message. So, use the c_str() method to convert the dataMessage variable.

writeFile() and appendFile()

The last two functions: writeFile() and appendFile() are used to write and append data to the microSD card. They come with the SD card library examples and you shouldn’t modify them.

To try other examples to work with the microSD card, go to File Examples SD(esp32).

Uploading the Code

Now, upload the code to your ESP32. Make sure you have the right board and COM port selected.

Demonstration

Open the Serial Monitor at a baud rate of 115200.

Press the ESP32 Enable button, and check that everything is working properly (the ESP32 is connected to your local network, and the microSD card is properly attached).https://tpc.googlesyndication.com/safeframe/1-0-37/html/container.html

Note: If everything is wired properly and you keep getting an error initializing the SD card, powering your microSD card module with 5V might solve the issue.

Let the ESP32 run for a few hours to test if everything is working as expected. After the testing period, remove the microSD card and insert it into your computer. The microSD card should contain a file called data.txt.

You can copy the file content to a spreadsheet on Google Sheets for example, and then split the data by commas. To split data by commas, select the column where you have your data, then go to Data > Split text to columns… Then, you can build charts to analyse the data.

Wrapping Up

In this tutorial we’ve shown you how to log data to a microSD card using the ESP32. We’ve also shown you how to read temperature from the DS18B20 temperature sensor and how to request time from an NTP server.

You might also like reading other articles related with ESP32:

Thanks for reading.

Advertisement

Published by Gnd_To_Vcc

Here to spread my knowledge . Knowledge should always be spread not stored.

12 thoughts on “ESP32 Data Logging Temperature to MicroSD Card

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: