Breadboard SD card data logger with AHT20 sensor, SD card module and RTC module, outdoors on a rock

ผู้เริ่มต้น

สร้างเครื่องบันทึกข้อมูล SD Card

พื้นที่จัดเก็บข้อมูลเซ็นเซอร์IoT

เครื่องบันทึกข้อมูลจริงแบบออฟไลน์ ไม่ต้องใช้ Wi-Fi ไม่ต้องมีบัญชีคลาวด์ แค่บันทึกข้อมูลที่ประทับเวลาได้อย่างแม่นยำลงในการ์ดโดยตรง

รวมชิ้นส่วนจริง 7 รายการ

ราคารวมชุดนี้: 1,679.00 ฿

ยังไม่รวมส่วนลดคำสั่งซื้อที่คุณอาจได้รับตอนชำระเงิน

สิ่งที่คุณต้องใช้

เป้าหมายของโครงการ

โครงการนี้สร้างเครื่องบันทึกข้อมูลแบบออฟไลน์ที่ใช้งานได้จริง: เซ็นเซอร์, นาฬิกา และการ์ดจัดเก็บข้อมูล ทำงานร่วมกันเพื่อบันทึกข้อมูลที่ประทับเวลาไว้เมื่อเวลาผ่านไป โดยไม่ต้องใช้ Wi-Fi และไม่ต้องมีบัญชีคลาวด์

สิ่งที่ตัวควบคุมทำ

ตามกำหนดเวลา ตัวควบคุมจะตื่นขึ้น, อ่านค่าจากเซ็นเซอร์, ขอเวลาปัจจุบันจาก RTC (นาฬิกาเรียลไทม์), และผนวกทั้งสองเป็นบรรทัดใหม่หนึ่งบรรทัดลงในไฟล์บนการ์ด SD สามขั้นตอนง่ายๆ ทำซ้ำไปเรื่อยๆ

สิ่งที่เซ็นเซอร์ทำ

AHT20 รายงานค่าอุณหภูมิและความชื้นจริงผ่าน I2C เมื่อมีการร้องขอ ทำให้เครื่องบันทึกมีสิ่งที่คุ้มค่าแก่การบันทึกในทุกรอบ

สิ่งที่โมดูล SD การ์ดและ RTC ทำ

โมดูล SD การ์ดให้พื้นที่สำหรับตัวควบคุมในการเขียนข้อมูลที่ยังคงอยู่แม้จะมีการปิดเปิดเครื่องใหม่ ซึ่งหน่วยความจำภายในของตัวควบคุมไม่สามารถทำได้ในขนาดจริง RTC DS3231 รักษาเวลาที่แม่นยำด้วยตัวเอง โดยมีแบตเตอรี่สำรอง ดังนั้นแต่ละแถวที่บันทึกไว้จะได้รับเวลาประทับที่ถูกต้องจริง แม้ว่าตัวควบคุมเองจะไม่รู้ว่าเวลาเท่าไรแล้วก็ตาม

การบันทึกแบบ CSV

แต่ละค่าที่อ่านได้จะถูกผนวกเป็นแถวใหม่ที่ประทับเวลาลงในไฟล์ข้อความธรรมดา ซึ่งเป็นรูปแบบง่ายๆ ที่โปรแกรมสเปรดชีตสามารถเปิดได้โดยตรง เมื่อเวลาผ่านไปหลายวันหรือหลายสัปดาห์ ไฟล์นั้นจะกลายเป็นชุดข้อมูลที่มีประโยชน์จริง ข้อจำกัดที่สำคัญข้อหนึ่งที่ควรกล่าวถึงอย่างชัดเจนคือ: การ์ด SD มีขีดจำกัดในการเขียนข้อมูล และการเขียนบ่อยครั้งเป็นเวลาหลายเดือนหรือหลายปีอาจทำให้การ์ดเสื่อมสภาพได้ในที่สุด

แนวคิดซอฟต์แวร์และไลบรารี

ทำให้แต่ละส่วนทำงานได้ด้วยตัวเองก่อน: อ่านเซ็นเซอร์แล้วพิมพ์ค่า, อ่าน RTC แล้วพิมพ์ค่า, เขียนไฟล์ทดสอบลงในการ์ด SD ทั้งหมดนี้ต้องยืนยันว่าทำงานแยกกันได้ก่อนที่จะรวมเข้าด้วยกันเป็นลูปการบันทึกข้อมูล

การทดสอบและการแก้ไขปัญหา

  • ไฟล์ไม่สามารถสร้างหรือเขียนได้เลย ให้ตรวจสอบว่าการ์ด SD ฟอร์แมตเป็น FAT32 ซึ่งเป็นปัญหาที่พบบ่อยมากกับโมดูลเหล่านี้
  • เวลาประทับออกมาผิด RTC จะไม่รู้เวลาที่ถูกต้องตั้งแต่แรกใช้งาน จะต้องตั้งค่าเพียงครั้งเดียวผ่านโค้ดในการใช้งานครั้งแรก
  • การบันทึกหยุดทำงานหลังจากผ่านไปสักพัก ให้ตรวจสอบพื้นที่ว่างในการ์ด หรือยืนยันว่าไฟล์ถูกปิดหรือล้างอย่างถูกต้องในโค้ด แทนที่จะปล่อยเปิดทิ้งไว้
  • ค่าที่อ่านได้เหมือนกันทุกแถว ให้ยืนยันว่าเซ็นเซอร์กำลังถูกอ่านซ้ำทุกรอบจริง ๆ ไม่ใช่การนำค่าแรกกลับมาใช้ซ้ำ

การอัปเกรดเสริมในอนาคต: การบันทึกผ่าน Wi-Fi

เมื่อเวอร์ชันออฟไลน์นี้ทำงานได้อย่างน่าเชื่อถือแล้ว โปรเจกต์ Wireless Environmental Data Logger จะแสดงให้เห็นว่าเซ็นเซอร์และ RTC ชุดเดียวกันนี้สามารถส่งข้อมูลไปยังบันทึกระยะไกลแทน หรือควบคู่ไปกับการ์ด SD ในเครื่องได้อย่างไร

การเดินสาย

  • AHT20 VCC → ESP32-C3 SuperMini 3.3V
  • AHT20 GND → GND
  • AHT20 SDA → GPIO8 (`SDA_PIN`) — บัส I2C ที่ใช้ร่วมกัน
  • AHT20 SCL → GPIO9 (`SCL_PIN`) — บัส I2C ที่ใช้ร่วมกัน
  • DS3231 VCC → 3.3V
  • DS3231 GND → GND
  • DS3231 SDA → GPIO8 (บัส I2C เดียวกันที่ใช้ร่วมกับ AHT20)
  • DS3231 SCL → GPIO9 (บัส I2C เดียวกันที่ใช้ร่วมกับ AHT20)
  • SD Module VCC → 3.3V (โมดูลเบรคเอาต์ส่วนใหญ่ใช้ตรรกะ 3.3V; ตรวจสอบของคุณหากมีตัวควบคุมที่รองรับ 5V)
  • SD Module GND → GND
  • SD Module SCK → GPIO4 (`SPI_SCK_PIN`)
  • SD Module MISO → GPIO5 (`SPI_MISO_PIN`)
  • SD Module MOSI → GPIO6 (`SPI_MOSI_PIN`)
  • SD Module CS → GPIO7 (`SD_CS_PIN`)

AHT20 และ DS3231 ใช้บัส I2C เดียวกันแต่มีแอดเดรสต่างกัน ซึ่งเป็นเรื่องปกติ – ไม่จำเป็นต้องเดินสายเพิ่มเติม นอกจากการต่อ SDA/SCL เข้าด้วยกัน ใส่การ์ด microSD ที่ฟอร์แมตเป็น FAT32 ก่อนเปิดเครื่อง หาก `SD.begin()` ล้มเหลว สาเหตุที่พบบ่อยที่สุดคือสาย CS ไม่ดี หรือการ์ดไม่ได้ฟอร์แมต/เข้ากันไม่ได้ ไม่ใช่จากเซ็นเซอร์ บรรทัดตั้งเวลา RTC จำเป็นต้องรันเพียงครั้งเดียว (มันจะตั้งนาฬิกาตามเวลาคอมไพล์ของคอมพิวเตอร์ของคุณ) – ลบหรือคอมเมนต์ออกหลังจากอัปโหลดสำเร็จครั้งแรก เพื่อป้องกันไม่ให้ถ่านกระดุมที่หมดทำให้เกิดการรีเซ็ตผิดพลาดโดยไม่รู้ตัว

แผนภาพการต่อสาย

VCC → 3.3V GND SDA → GPIO8 SCL → GPIO9 VCC GND SDA → GPIO8 SCL → GPIO9 VCC GND SCK → GPIO4 MISO → GPIO5 MOSI → GPIO6 CS → GPIO7 ESP32-C3 SuperMini AHT20 DS3231 SD Module GND 3.3V

โค้ดเริ่มต้น

#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <RTClib.h>
#include <Adafruit_AHTX0.h>

#define SDA_PIN 8    // ESP32-C3 default I2C SDA - adjust if your board differs
#define SCL_PIN 9    // ESP32-C3 default I2C SCL - adjust if your board differs

#define SPI_SCK_PIN  4  // ESP32-C3 default hardware SPI pins - adjust to match your board's silkscreen if different
#define SPI_MISO_PIN 5
#define SPI_MOSI_PIN 6
#define SD_CS_PIN    7  // SD module chip select - any free digital GPIO

const char* LOG_FILENAME = "/datalog.csv";
const unsigned long LOG_INTERVAL_MS = 5000; // log every 5 seconds

Adafruit_AHTX0 aht;
RTC_DS3231 rtc;
unsigned long lastLog = 0;

void setup() {
  Serial.begin(115200);
  delay(500);

  Wire.begin(SDA_PIN, SCL_PIN);

  if (!aht.begin()) {
    Serial.println("AHT20 not found - check I2C wiring");
    while (true) delay(1000);
  }

  if (!rtc.begin()) {
    Serial.println("DS3231 not found - check I2C wiring");
    while (true) delay(1000);
  }

  if (rtc.lostPower()) {
    // Sets the RTC to the sketch's compile time. Only needed once after the
    // coin cell has been out / on first power-up - remove after the RTC is set.
    Serial.println("RTC lost power, setting time to compile time");
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }

  SPI.begin(SPI_SCK_PIN, SPI_MISO_PIN, SPI_MOSI_PIN, SD_CS_PIN);
  if (!SD.begin(SD_CS_PIN)) {
    Serial.println("SD card init failed - check wiring/card");
    while (true) delay(1000);
  }

  if (!SD.exists(LOG_FILENAME)) {
    File f = SD.open(LOG_FILENAME, FILE_WRITE);
    if (f) {
      f.println("timestamp,temperature_C,humidity_percent");
      f.close();
    }
  }

  Serial.println("Logger ready");
}

void loop() {
  if (millis() - lastLog < LOG_INTERVAL_MS) return;
  lastLog = millis();

  sensors_event_t humidity, temp;
  aht.getEvent(&humidity, &temp);
  DateTime now = rtc.now();

  char timestamp[20];
  snprintf(timestamp, sizeof(timestamp), "%04d-%02d-%02d %02d:%02d:%02d",
           now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second());

  File f = SD.open(LOG_FILENAME, FILE_WRITE);
  if (f) {
    f.printf("%s,%.2f,%.2f\n", timestamp, temp.temperature, humidity.relative_humidity);
    f.close();
    Serial.printf("Logged: %s, %.2f C, %.2f %%\n", timestamp, temp.temperature, humidity.relative_humidity);
  } else {
    Serial.println("Failed to open log file for append");
  }
}

ไม่แน่ใจขั้นตอนไหน? ถามเรา