ESP32: Логирование температуры на MicroSD карту

Этот проект показывает, как логировать данные с метками времени на microSD карту с помощью ESP32. В качестве примера мы будем записывать показания температуры с датчика DS18B20 каждые 10 минут. ESP32 будет находиться в режиме глубокого сна между каждым измерением, а дату и время будет запрашивать с помощью протокола сетевого времени (NTP).

ESP32 логирование данных температуры на MicroSD карту

Вам также может понравиться: ESP32: How to Log Data (10 Different Ways)

Обзор проекта

Прежде чем приступить, давайте выделим основные особенности проекта:

Обзор проекта логирования данных ESP32
  • ESP32 считывает температуру с помощью датчика температуры DS18B20.

  • После получения температуры он отправляет запрос к NTP (Network Time Protocol) серверу для получения даты и времени. Поэтому ESP32 необходимо Wi-Fi соединение.

  • Данные (температура и метка времени) записываются на microSD карту. Для записи данных на microSD карту мы используем модуль microSD карт.

  • После выполнения этих задач ESP32 засыпает на 10 минут.

  • ESP32 просыпается и повторяет процесс.

Необходимые компоненты

Вот список компонентов, необходимых для сборки этого проекта (нажмите на ссылки ниже, чтобы найти лучшую цену на Maker Advisor):

Необходимые компоненты для логирования данных ESP32

Вы можете использовать ссылки выше или перейти непосредственно на MakerAdvisor.com/tools, чтобы найти все компоненты для ваших проектов по лучшей цене!

Подготовка модуля MicroSD карт

Для сохранения данных на microSD карту с ESP32 мы используем следующий модуль microSD карт, который взаимодействует с ESP32 по протоколу связи SPI.

Модуль MicroSD карт

Связанный контент: ESP32: Guide for MicroSD Card Module using Arduino IDE

Форматирование microSD карты

При использовании microSD карты с ESP32 её необходимо сначала отформатировать. Следуйте приведённым ниже инструкциям для форматирования вашей microSD карты.

1. Вставьте microSD карту в компьютер. Перейдите в Мой компьютер и щёлкните правой кнопкой мыши по SD карте. Выберите Форматировать, как показано на рисунке ниже.

Форматирование SD карты - шаг 1

2. Появится новое окно. Выберите FAT32, нажмите Начать для запуска процесса форматирования и следуйте инструкциям на экране.

Форматирование SD карты - шаг 2

Схема подключения

Следуйте приведённой ниже принципиальной схеме для сборки цепи данного проекта.

Схема подключения ESP32 для логирования данных

Вы также можете использовать следующую таблицу в качестве справки для подключения модуля microSD карт:

Модуль MicroSD карт

ESP32

3V3

3V3

CS

GPIO 5

MOSI

GPIO 23

CLK

GPIO 18

MISO

GPIO 19

GND

GND

На следующем изображении показано, как должна выглядеть ваша схема:

Фото собранной схемы ESP32 с модулем MicroSD карт

Подготовка Arduino IDE

Существует дополнение для Arduino IDE, которое позволяет программировать ESP32, используя Arduino IDE и его язык программирования. Следуйте приведённому ниже руководству, чтобы подготовить вашу Arduino IDE для работы с ESP32, если вы ещё этого не сделали.

Установка библиотек

Перед загрузкой кода вам необходимо установить несколько библиотек в Arduino IDE. Библиотеку OneWire от Paul Stoffregen и библиотеку Dallas Temperature, чтобы вы могли использовать датчик DS18B20. Вам также необходимо установить библиотеку NTPClient, форк от Taranais, для отправки запросов к NTP серверу.

Следуйте приведённым ниже шагам для установки этих библиотек в вашей Arduino IDE:

Библиотека OneWire

  1. В Arduino IDE перейдите в Sketch > Include Library > Manage Libraries.

  2. Найдите OneWire.

  3. Установите библиотеку OneWire, как показано на рисунке ниже.

Установка библиотеки OneWire в Arduino IDE

Библиотека Dallas Temperature

  1. В Arduino IDE перейдите в Sketch > Include Library > Manage Libraries.

  2. Найдите DallasTemperature.

  3. Установите библиотеку DallasTemperature от Miles Burton.

Установка библиотеки DallasTemperature в Arduino IDE

Библиотека NTPClient

ВАЖНО: мы не используем стандартную библиотеку NTPClient. Для прохождения этого руководства вам необходимо установить библиотеку, которую мы рекомендуем, используя следующие шаги.

Мы будем использовать библиотеку NTPClient, форк от Taranais. Следуйте приведённым ниже шагам для установки этой библиотеки в вашей Arduino IDE:

  1. Нажмите здесь, чтобы скачать библиотеку NTP Client. У вас должен появиться .zip файл в папке Downloads.

  2. В вашей Arduino IDE перейдите в Sketch > Include Library > Add .ZIP library…

  3. Выберите .ZIP файл библиотеки, которую вы только что скачали.

  4. Библиотека будет установлена через несколько секунд.

Загрузка кода

Вот код, который вам нужно загрузить на ваш ESP32. Перед загрузкой вам необходимо изменить код, указав ваши сетевые учётные данные (SSID и пароль). Продолжайте чтение, чтобы узнать, как работает код.

/*********
  Rui Santos
  Complete project details at https://randomnerdtutorials.com
*********/

// 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();
}

Просмотреть исходный код

Как работает код

В этом примере ESP32 находится в режиме глубокого сна между каждым измерением. В режиме глубокого сна весь ваш код должен находиться в функции setup(), потому что ESP32 никогда не достигает loop().

Импорт библиотек

Сначала вы импортируете необходимые библиотеки для модуля microSD карт:

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

Импортируйте эти библиотеки для работы с датчиком температуры DS18B20.

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

Следующие библиотеки позволяют запрашивать дату и время с NTP сервера.

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

Настройка времени глубокого сна

В этом примере используется коэффициент преобразования из микросекунд в секунды, чтобы вы могли задать время сна в переменной TIME_TO_SLEEP в секундах.

В данном случае мы настраиваем ESP32 на засыпание на 10 минут (600 секунд). Если вы хотите, чтобы ESP32 спал другой период времени, вам просто нужно ввести количество секунд глубокого сна в переменную TIME_TO_SLEEP.

// 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;

Настройка сетевых учётных данных

Введите ваши сетевые учётные данные в следующих переменных, чтобы ESP32 мог подключиться к вашей локальной сети.

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

Инициализация датчиков и переменных

Далее определите пин CS для модуля microSD карт. В данном случае он назначен на GPIO 5.

#define SD_CS 5

Создайте переменную readingID для хранения идентификатора измерения. Это способ упорядочить ваши показания. Чтобы сохранить значение переменной во время глубокого сна, мы можем сохранить его в RTC памяти. Для сохранения данных в RTC памяти вам просто нужно добавить RTC_DATA_ATTR перед определением переменной.

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

Создайте строковую переменную для хранения данных, которые будут сохранены на microSD карту.

String dataMessage;

Далее создайте экземпляры, необходимые для датчика температуры. Датчик температуры подключён к 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);

Затем создайте переменную типа float для хранения температуры, полученной от датчика DS18B20.

float temperature;

Следующие две строки определяют NTPClient для запроса даты и времени с NTP сервера.

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

Затем инициализируйте строковые переменные для сохранения даты и времени.

String formattedDate;
String dayStamp;
String timeStamp;

setup()

При использовании глубокого сна с ESP32 весь код должен находиться внутри функции setup(), потому что ESP32 никогда не достигает loop().

Подключение к Wi-Fi

Следующий фрагмент кода подключается к Wi-Fi сети. Вам необходимо подключиться к Wi-Fi для запроса даты и времени с NTP сервера.

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

Инициализация NTP клиента

Далее инициализируйте NTP клиент для получения даты и времени с NTP сервера.

timeClient.begin();

Вы можете использовать метод setTimeOffset(<time>) для настройки времени под ваш часовой пояс.

timeClient.setTimeOffset(3600);

Вот несколько примеров для различных часовых поясов:

  • GMT +1 = 3600

  • GMT +8 = 28800

  • GMT -1 = -3600

  • GMT 0 = 0

Инициализация модуля microSD карт

Затем инициализируйте microSD карту. Следующие условия if проверяют, правильно ли подключена microSD карта.

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
}

Затем попробуйте открыть файл data.txt на microSD карте.

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

Если этот файл не существует, нам нужно создать его и записать заголовок для файла .txt.

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

Если файл уже существует, код продолжает выполнение.

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

Наконец, мы закрываем файл.

file.close();

Включение пробуждения по таймеру

Затем вы включаете пробуждение по таймеру с временем, которое вы определили ранее в переменной TIME_TO_SLEEP.

esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);

Инициализация библиотеки для DS18B20

Далее вы инициализируете библиотеку для датчика температуры DS18B20.

sensors.begin();

Получение показаний и логирование данных

После инициализации всего мы можем получить показания, метку времени и записать всё на microSD карту.

Чтобы сделать код более понятным, мы создали следующие функции:

  • getReadings(): считывает температуру с датчика температуры DS18B20;

  • getTimeStamp(): получает дату и время с NTP сервера;

  • logSDcard(): записывает вышеуказанные данные на microSD карту.

После выполнения этих задач мы увеличиваем readingID.

readingID++;

Наконец, ESP32 переходит в глубокий сон.

esp_deep_sleep_start();

getReadings()

Давайте посмотрим на функцию getReadings(). Эта функция просто считывает температуру с датчика температуры DS18B20.

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

По умолчанию код получает температуру в градусах Цельсия. Вы можете раскомментировать следующую строку и закомментировать предыдущую, чтобы получить температуру в градусах Фаренгейта.

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

getTimeStamp()

Функция getTimeStamp() получает дату и время. Следующие строки обеспечивают получение корректной даты и времени:

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

Иногда NTPClient возвращает 1970 год. Чтобы этого не происходило, мы принудительно обновляем данные.

Затем преобразуйте дату и время в читаемый формат с помощью метода getFormattedDate():

formattedDate = timeClient.getFormattedDate();

Дата и время возвращаются в следующем формате:

2018-04-30T16:00:13Z

Поэтому нам нужно разделить эту строку, чтобы получить дату и время по отдельности. Вот что мы делаем здесь:

// 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);

Дата сохраняется в переменной dayStamp, а время – в переменной timeStamp.

logSDCard()

Функция logSDCard() объединяет всю информацию в строковой переменной dataMessage. Каждое измерение разделяется запятыми.

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

Примечание: \r\n в конце переменной dataMessage обеспечивает запись следующего измерения на новой строке.

Затем с помощью следующей строки мы записываем всю информацию в файл data.txt на microSD карте.

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

Примечание: функция appendFile() принимает только переменные типа const char для сообщения. Поэтому используйте метод c_str() для преобразования переменной dataMessage.

writeFile() и appendFile()

Последние две функции: writeFile() и appendFile() используются для записи и добавления данных на microSD карту. Они взяты из примеров библиотеки SD карт, и вам не следует их изменять.

Чтобы попробовать другие примеры работы с microSD картой, перейдите в File > Examples > SD(esp32).

Загрузка кода на плату

Теперь загрузите код на ваш ESP32. Убедитесь, что у вас выбрана правильная плата и COM порт.

Кнопка загрузки Arduino IDE 2

Демонстрация

Откройте Serial Monitor со скоростью передачи 115200 бод.

Нажмите кнопку Enable на ESP32 и проверьте, что всё работает правильно (ESP32 подключён к вашей локальной сети, и microSD карта правильно подключена).

Монитор последовательного порта ESP32 при логировании данных

Примечание: Если всё подключено правильно, но вы продолжаете получать ошибку инициализации SD карты, питание модуля microSD карты от 5V может решить проблему.

Дайте ESP32 поработать несколько часов, чтобы проверить, что всё работает как ожидается. После периода тестирования извлеките microSD карту и вставьте её в компьютер. На microSD карте должен быть файл data.txt.

Файл данных data.txt на microSD карте

Вы можете скопировать содержимое файла в таблицу, например, в Google Sheets, а затем разделить данные по запятым. Для разделения данных по запятым выделите столбец с вашими данными, затем перейдите в Data > Split text to columns… Затем вы можете построить графики для анализа данных.

График температуры из логированных данных ESP32

Заключение

В этом руководстве вы научились логировать данные на microSD карту с помощью ESP32. Мы также показали вам, как считывать температуру с датчика температуры DS18B20 и как запрашивать время с NTP сервера.

Вы можете применить концепции из этого руководства в ваших собственных проектах. Вам также могут понравиться:

Вам также может быть интересно прочитать другие статьи, связанные с ESP32:


Источник: ESP32 Data Logging Temperature to MicroSD Card