ESP32

ESP32 Data Logging Temperature to MicroSD Card

기하 2021. 8. 16. 22:44

이 프로젝트는 ESP32를 사용하여

microSD 카드에 타임스탬프가 있는 데이터를 기록하는 방법을 보여줍니다. 

예를 들어 DS18B20 센서의 온도 판독값을 10분마다 기록합니다. 

ESP32는 각 판독 사이에 최대 절전 모드가 되며

NTP(네트워크 시간 프로토콜)를 사용하여 날짜와 시간을 요청합니다.

프로젝트 개요

시작하기 전에 프로젝트의 주요 기능을 강조해 보겠습니다.

 

  • ESP32는 DS18B20 온도 센서를 사용하여 온도를 읽습니다.
  • 온도를 얻은 후 NTP(Network Time Protocol) 서버에 날짜와 시간을 요청합니다. 
    따라서 ESP32는 Wi-Fi 연결이 필요합니다.
  • 데이터(온도 및 타임스탬프)는 microSD 카드에 기록됩니다. 
    microSD 카드에 데이터를 기록하기 위해 microSD 카드 모듈을 사용하고 있습니다.
  • 이러한 이전 작업을 완료한 후 ESP32는 10분 동안 절전 모드로 전환됩니다.
  • ESP32가 깨어나서 프로세스를 반복합니다.

필요한 부품

다음은 이 프로젝트를 구축하는 데 필요한 부품 목록입니다

  • ESP32 DOIT DEVKIT V1 보드 
  • MicroSD 카드 모듈
  • 마이크로SD 카드
  • DS18B20 온도 센서
  • 10k 옴 저항
  • 점퍼 와이어
  • 브레드보드

microSD 카드 모듈 준비

ESP32를 사용하여 microSD 카드에 데이터를 저장하기 위해 

SPI 통신 프로토콜을 사용하여 ESP32와 통신 하는 다음 microSD 카드 모듈 을 사용합니다.

microSD 카드 포맷하기

ESP32와 함께 microSD 카드를 사용할 때는 먼저 포맷해야 합니다. 

다음 지침에 따라 microSD 카드를 포맷하십시오.

 

1.  microSD 카드를 컴퓨터에 삽입합니다. 내 컴퓨터로 이동하여 SD 카드를

마우스 오른쪽 버튼으로 클릭합니다.  아래 그림과 같이 형식  선택  합니다.

2.  새 창이 팝업됩니다. FAT32를 선택하고  

시작  을 눌러  포맷 프로세스를 초기화하고 화면의 지시를 따릅니다.

개략도

다음 회로도를 따라 이 프로젝트의 회로를 조립하십시오.

 

또한 다음 표를 참조로 사용하여 microSD 카드 모듈을 연결할 수 있습니다.

MicroSD 카드 모듈 ESP32
3V3 3V3
CS GPIO 5
MOSI GPIO 23
CLK GPIO 18
MISO GPIO 19
GND GND

다음 그림은 회로가 어떻게 생겼는지 보여줍니다.

 

아두이노 IDE 준비

Arduino IDE 및 프로그래밍 언어를 사용하여 ESP32를 프로그래밍할 수 있는

Arduino IDE용 애드온이 있습니다. ESP32와 함께 작동하도록 Arduino IDE를 준비하려면

다음 자습서 중 하나를 따르세요.

ESP32 추가 기능이 설치되어 있는지 확인한 후 이 자습서를 계속할 수 있습니다.

라이브러리 설치

코드를 업로드하기 전에 Arduino IDE에 몇 가지 라이브러리를 설치해야 합니다. 

DS18B20 센서를 사용할 수 있도록 Paul Stoffregen 의  OneWire 라이브러리  및  

Dallas Temperature 라이브러리 . 

또한  NTP 서버에 요청하려면 Taranais 에서 분기한 NTPClient 라이브러리 를 설치해야  합니다.

 

Arduino IDE에 해당 라이브러리를 설치하려면 다음 단계를 따르세요.

 

OneWire library

  1. OneWire 라이브러리를 다운로드하려면 여기를 클릭하십시오 . 다운로드에 .zip 폴더가 있어야 합니다.
  2. .zip  폴더의  압축을  풀면 OneWire-master  폴더가 나타납니다.
  3. 다음에서 폴더 이름 바꾸기 OneWire-master에서  OneWire으로
  4. OneWire  폴더를 Arduino IDE 설치  라이브러리  폴더로 이동  합니다.
  5. 마지막으로 Arduino IDE를 다시 엽니다.

Dallas Temperature library

  1. DallasTemperature 라이브러리를 다운로드하려면 여기를 클릭하십시오 . 
    다운로드에 .zip 폴더가 있어야 합니다.
  2. .zip  폴더의  압축을  풀면 Arduino-Temperature-Control-Library-master  폴더가 있어야 합니다.
  3. 폴더 이름 바꾸기  Arduino-Temperature-Control-Library-master to DallasTemperature
  4. DallasTemperature 폴더를 Arduino IDE 설치  라이브러리  폴더로 이동  합니다.
  5. 마지막으로 Arduino IDE를 다시 엽니다.

NTP클라이언트 라이브러리

  1. NTPClient 라이브러리를 다운로드하려면 여기를 클릭하십시오 . 다운로드에 .zip 폴더가 있어야 합니다.
  2. .zip  폴더의 압축을  풀면 NTPClient-master  폴더가 있어야  합니다.
  3. 다음에서 폴더 이름 바꾸기 NTP클라이언트-마스터 에  NtpClient에
  4. NTPClient 폴더를 Arduino IDE 설치  라이브러리  폴더로 이동  합니다.
  5. 마지막으로 Arduino IDE를 다시 엽니다.

코드 업로드

다음은 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는 각 판독 사이에 깊은 절전 모드에 있습니다.

 깊은 절전 모드에서 ESP32가 절대 loop() 에 도달항 수 없기 때문에
모든 코드는 setup() 함수에 있어야 합니다.

라이브러리 가져오기

먼저 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>

deep sleep time 설정

이 예에서는 마이크로초에서 초로의 변환 계수를 사용하므로TIME_TO_SLEEP 변수에 

sleep time을 설정할 수 있습니다. 

 

이 경우 ESP32를 10분(600초) 동안 수면모드로 전환하도록 설정합니다. 

ESP32가 다른 시간 동안 절전 모드로 전환되도록 하려면

TIME_TO_SLEEP 변수에 deeep 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";

센서 및 변수 초기화

다음으로 microSD 카드 SD 핀을 정의합니다. 이 경우 다음으로 설정됩니다.GPIO 5.

#define SD_CS 5

reading ID를 유지하기 위해 readingID변를 생성합니다. 이것은 읽기를 정리하는 방법입니다. 

딥 슬립 동안 변수 값을 저장하기 위해 RTC 메모리에 저장할 수 있습니다. 

RTC 메모리에 데이터를 저장하려면 다음을 추가하기만 하면 됩니다.RTC_DATA_ATTR 

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

microSD 카드에 저장할 데이터를 저장할 String 변수를 생성합니다.

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

그런 다음 DS18B20 센서에서 검색한 온도를 유지하기 위해 부동 변수를 생성합니다.

float temperature;

다음 두 줄은 NTP 서버에서 날짜와 시간을 요청하는 NTPClient를 정의합니다.

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);

그런 다음 문자열 변수를 초기화하여 날짜와 시간을 저장합니다.

 

setup()

 

ESP32와 함께 딥 슬립을 사용할 때 모든 코드는 setup() 함수 내에 있어야 하며 

ESP32는 절대 loop()에 도달하지 않기 때문입니다.

 

Wi-Fi에 연결

다음 코드 스니펫은 Wi-Fi 네트워크에 연결합니다. 

NTP 서버에서 날짜와 시간을 요청하려면 Wi-Fi에 연결해야 합니다.

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 카드가 제대로 장착되었는지 확인합니다.

D.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
}

 

그런 다음 microSD 카드 에서 data.txt 파일 을 열어보십시오 .

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 서버에서 날짜와 시간을 가져옵니다.
  • 로그SD카드(): 이전 데이터를 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 스트링 변수에 연결합니다.

모든 reading 은 콤마로 구별됩니다.

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

참고 :dataMessage변수의 끝에 있는 "\r\n"다음 판독값이 다음 줄에 기록되도록 합니다.

 

그런 다음 다음 줄을 사용하여 모든 정보를  microSD 카드  data.txt 파일에 씁니다  .

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

참고 :appendFile()함수는 메시지에 대해 const char 유형의 변수만 허용합니다. 

따라서 dataMessage변수를 형변환하기 위해 c_str()을 사용합니다

writeFile() 및 appendFile()

마지막 두 기능: writeFile() and appendFile() 함수는 microSD 카드에 데이터를 쓰고 추가하는 데 사용됩니다. 
SD 카드 라이브러리 예제와 함께 제공되며 수정해서는 안 됩니다.

 

microSD 카드로 작업하는 다른 예제를 시도하려면  파일 > 예제 > SD(esp32) 로 이동하십시오 .

코드 업로드

이제 ESP32에 코드를 업로드합니다. 올바른 보드와 COM 포트를 선택했는지 확인하십시오.

데모

115200의 전송 속도로 직렬 모니터를 엽니다.

ESP32 활성화 버튼을 누르고 모든 것이 제대로 작동하는지 확인합니다

(ESP32가 로컬 네트워크에 연결되어 있고 microSD 카드가 제대로 연결되어 있음).

 

참고: 모든 것이 올바르게 연결되어 있고 SD 카드 초기화 오류가 계속 발생하는 경우

microSD 카드 모듈에 5V 전원을 공급하면 문제가 해결될 수 있습니다.

 

ESP32를 몇 시간 동안 실행하여 모든 것이 예상대로 작동하는지 테스트합니다. 

테스트 기간이 끝나면 microSD 카드를 제거하고 컴퓨터에 삽입하십시오. 

microSD 카드에는 data.txt 라는 파일이 있어야 합니다 .

예를 들어 파일 콘텐츠를

Google 스프레드시트의 스프레드시트에 복사한 다음 데이터를 쉼표로 분할할 수 있습니다. 

데이터를 쉼표로 분할하려면 데이터가 있는 열을 선택한 다음

 Data > Split text to columns…... 로 이동하여  차트를 작성하여 데이터를 분석할 수 있습니다.

마무리

이 자습서에서는 ESP32를 사용하여 microSD 카드에 데이터를 기록하는 방법을 보여주었습니다. 

또한 DS18B20 온도 센서에서 온도를 읽는 방법과 NTP 서버에서 시간을 요청하는 방법을 보여주었습니다.

이 튜토리얼의 개념을 자신의 프로젝트에 적용할 수 있습니다. 

ESP32가 마음에 들고 더 자세히 알고 싶다면 ESP32 전용 과정: Arduino IDE로 ESP32 배우기 를 확인하십시오 .

 

 

[출처번역인용]https://randomnerdtutorials.com/esp32-data-logging-temperature-to-microsd-card/