ระดับกลาง
สร้างเครื่องบันทึกข้อมูลสภาพแวดล้อมแบบไร้สาย
IoTเซ็นเซอร์ไร้สาย
รวมการตรวจจับด้านสิ่งแวดล้อม, การเชื่อมต่อไร้สาย และการบันทึกข้อมูลเป็นระยะเข้าด้วยกันเพื่อสร้างโหนดการตรวจสอบที่ใช้งานได้จริง
รวมชิ้นส่วนจริง 8 รายการ
ราคารวมชุดนี้: 1,768.00 ฿
ยังไม่รวมส่วนลดคำสั่งซื้อที่คุณอาจได้รับตอนชำระเงิน
สิ่งที่คุณต้องใช้
เป้าหมายของโปรเจกต์
โปรเจกต์นี้เป็นการรวมทุกอย่างจากโปรเจกต์เซ็นเซอร์สำหรับผู้เริ่มต้นเข้ากับการเชื่อมต่อไร้สาย เปลี่ยนการอ่านค่าเพียงครั้งเดียวให้เป็นโหนดตรวจสอบจริงที่สามารถรายงานข้อมูลได้โดยไม่ต้องเชื่อมต่อสายกับคอมพิวเตอร์อย่างถาวร
การเลือกช่วงเวลาการสุ่มตัวอย่าง
ตัดสินใจว่าเซ็นเซอร์จำเป็นต้องอ่านค่าบ่อยแค่ไหนก่อนที่จะเขียนโค้ด โหนดที่อุณหภูมิห้องอาจสุ่มตัวอย่างหนึ่งครั้งต่อนาที ส่วนการวัดที่เปลี่ยนแปลงอย่างรวดเร็วอาจต้องการทุกๆ วินาที การสุ่มตัวอย่างบ่อยเกินกว่าที่ข้อมูลเปลี่ยนแปลงจริงจะสิ้นเปลืองพลังงาน และบนเครือข่ายไร้สายที่ใช้ร่วมกัน ก็จะเปลืองเวลาออกอากาศโดยไม่จำเป็น
การอ่านค่าเซ็นเซอร์
ส่วนนี้เหมือนกับโปรเจกต์เซ็นเซอร์อื่นๆ คืออ่านค่าปัจจุบันในแต่ละรอบ สิ่งใหม่คือสิ่งที่เกิดขึ้นกับค่านั้นหลังจากนั้น
การบันทึกข้อมูลแบบภายในเทียบกับแบบระยะไกล
การบันทึกข้อมูลแบบภายใน (Local logging) หมายถึงการจัดเก็บค่าที่อ่านได้บนอุปกรณ์นั้นๆ (ในหน่วยความจำหรือพื้นที่เก็บข้อมูลภายใน) เพื่อเรียกใช้ภายหลัง การบันทึกข้อมูลแบบระยะไกล (Remote logging) หมายถึงการส่งค่าที่อ่านได้แต่ละค่าไปยังที่อื่นผ่านเครือข่ายทันทีที่อ่านค่าได้ ทั้งสองวิธีนี้ไม่จำเป็นต้องแยกจากกัน โหนดที่แข็งแกร่งมักจะเก็บบัฟเฟอร์ขนาดเล็กไว้ภายในโดยเฉพาะ เพื่อให้มีที่สำหรับเก็บข้อมูลหากการเชื่อมต่อระยะไกลไม่พร้อมใช้งานชั่วคราว
ตัวเลือกไร้สาย
Wi-Fi เป็นตัวเลือกที่ตรงไปตรงมาที่สุดหากมีเครือข่ายพร้อมใช้งานในตำแหน่งที่โหนดจะทำงาน และพลังงานไม่ได้ถูกจำกัดอย่างมาก มีตัวเลือกไร้สายอื่นๆ สำหรับระยะทางที่ไกลขึ้นหรือพลังงานที่ต่ำลง โดยมีข้อเสียคือความซับซ้อนในการตั้งค่าที่มากขึ้น การเลือกที่เหมาะสมขึ้นอยู่กับข้อจำกัดด้านระยะทาง พลังงาน และโครงสร้างพื้นฐานเฉพาะของคุณ ไม่ใช่ขึ้นอยู่กับตัวเลือกใดที่คุ้นเคยที่สุด
พฤติกรรมการลองใหม่
การเชื่อมต่อไร้สายอาจล้มเหลวในการส่งข้อมูลเป็นครั้งคราว วางแผนสำหรับเรื่องนี้ตั้งแต่เริ่มต้น แทนที่จะถือว่าเป็นกรณีพิเศษที่เกิดขึ้นไม่บ่อย แนวทางที่เรียบง่ายแต่ใช้งานได้: หากการส่งล้มเหลว ให้เก็บค่าที่อ่านได้ (หรือบัฟเฟอร์สั้นๆ ของค่าที่อ่านได้ล่าสุด) และลองใหม่ในรอบถัดไป แทนที่จะทิ้งไปเงียบๆ
การประทับเวลา
ข้อมูลที่บันทึกทุกรายการควรมีเวลาประทับที่ระบุเวลาที่อ่านค่าจริง ไม่ใช่เวลาที่ได้รับหรือบันทึกจากระยะไกล ซึ่งทั้งสองอย่างอาจคลาดเคลื่อนกันได้ โดยเฉพาะอย่างยิ่งหากมีการลองใหม่
ข้อควรพิจารณาเรื่องพลังงาน
วิทยุไร้สายใช้พลังงานมากกว่าคอนโทรลเลอร์อย่างมาก โดยเฉพาะอย่างยิ่งในขณะที่กำลังส่งข้อมูล หากโหนดจะทำงานด้วยแบตเตอรี่ การลดระยะเวลาที่วิทยุทำงานในแต่ละรอบ (ส่งข้อมูลอย่างรวดเร็ว แล้วกลับสู่สถานะพลังงานต่ำ) มีความสำคัญมากกว่าการเพิ่มประสิทธิภาพอื่นๆ เกือบทั้งหมดที่คุณสามารถทำได้
ความถูกต้องของข้อมูล
ตรวจสอบความถูกต้องของค่าที่อ่านได้ก่อนที่จะบันทึก เซ็นเซอร์ที่ส่งค่าที่ไม่สมเหตุสมผล (นอกช่วงที่กำหนดไว้มาก) มีแนวโน้มที่จะบ่งชี้ถึงข้อผิดพลาดในการเดินสายหรือการอ่านมากกว่าการวัดค่าที่แท้จริง และการบันทึกโดยไม่ตรวจสอบจะทำให้ข้อมูลของคุณปนเปื้อน
การอัปเกรดเสริม: การทำงานด้วยแบตเตอรี่
เมื่อเวอร์ชันที่ใช้สายไฟและแหล่งจ่ายไฟหลักเชื่อถือได้ การเปลี่ยนไปใช้พลังงานจากแบตเตอรี่เป็นขั้นตอนต่อไปที่สมเหตุสมผล พิจารณาข้อควรพิจารณาเรื่องพลังงานข้างต้นโดยเฉพาะ เนื่องจากอายุการใช้งานแบตเตอรี่จะกลายเป็นข้อจำกัดหลักเมื่อคุณทำการเปลี่ยนแปลงนั้น
คู่มือนี้อธิบายแนวคิดที่โหนดบันทึกข้อมูลไร้สายต้องการ ไม่ได้สมมติหรืออ้างว่ามีการสร้างบริการคลาวด์หรือการผสานรวมแดชบอร์ดเฉพาะใดๆ ไว้แล้ว ถือว่าสิ่งเหล่านี้เป็นการเพิ่มเติมแยกต่างหากในภายหลัง เมื่อการบันทึกข้อมูลเองได้รับการพิสูจน์แล้วว่าเชื่อถือได้
การเดินสาย
- AHT20 VIN → 3.3V, GND → GND, SDA → GPIO8, SCL → GPIO9 (บัส I2C, แอดเดรส 0x38)
- DS3231 VCC → 3.3V, GND → GND, SDA → GPIO8, SCL → GPIO9 — บัส I2C เดียวกันกับ AHT20 (แอดเดรส 0x68, ไม่ขัดแย้ง); ปล่อย SQW/32K ไม่เชื่อมต่อ
- โมดูลการ์ด SD VCC → 5V (โมดูล SD breakout ส่วนใหญ่มีตัวควบคุม 3.3V และการปรับระดับบนบอร์ด; หากของคุณเป็นโมดูล 3.3V เท่านั้น ให้ใช้ 3.3V แทน — ตรวจสอบ silkscreen)
- โมดูลการ์ด SD GND → GND
- โมดูลการ์ด SD CS → GPIO7 (`SD_CS_PIN`)
- โมดูลการ์ด SD SCK → GPIO4 (`SD_SCK_PIN`)
- โมดูลการ์ด SD MOSI/DI → GPIO6 (`SD_MOSI_PIN`)
- โมดูลการ์ด SD MISO/DO → GPIO5 (`SD_MISO_PIN`)
ESP32-C3 ไม่มีพิน SPI แบบคงที่ — เมทริกซ์ GPIO ของมันช่วยให้คุณสามารถกำหนดเส้นทาง SPI ไปยังพินที่ว่างเปลือบได้เกือบทุกพิน ซึ่งเป็นเหตุผลที่โค้ดเรียกใช้ `SPI.begin()` อย่างชัดเจนด้วยหมายเลขพินทั้งสี่ด้านบน แทนที่จะพึ่งพาค่าเริ่มต้น; ปรับ `#define`s หากคุณเดินสายต่างกัน ในการอัปโหลดครั้งแรก DS3231 ต้องตั้งค่านาฬิกาหนึ่งครั้งจากเวลาคอมไพล์ของสเก็ตช์ (ทำใน `setup()` ด้านล่าง) — หลังจากนั้นแบตเตอรี่สำรองแบบเหรียญจะรักษาเวลาผ่านวงจรเปิดปิดเครื่อง เรียกใช้การ์ด SD ขึ้นมาเป็นลำดับสุดท้ายใน `setup()` (หลังจาก I2C) เพื่อไม่ให้การ์ดที่หายไป/เสียขัดขวางการบันทึกข้อมูลเซ็นเซอร์ทั้งหมด; ชื่อ "ไร้สาย" ในชื่อโปรเจกต์นี้จริงๆ แล้วเกี่ยวกับการมี Wi-Fi ในตัวของ ESP32-C3 สำหรับการอัปเกรดในอนาคต — การเพิ่มการอัปโหลดเป็นระยะด้วย `WiFi.begin()` และไคลเอนต์ HTTP เป็นขั้นตอนต่อไปตามธรรมชาติเมื่อการบันทึกข้อมูลภายในมีความเสถียร
แผนภาพการต่อสาย
โค้ดเริ่มต้น
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <RTClib.h>
#include <Adafruit_AHTX0.h>
// I2C — ESP32-C3/S3 Arduino-ESP32 default pins
#define SDA_PIN 8
#define SCL_PIN 9
// SPI for the SD module — adjust to match how you wired it
#define SD_CS_PIN 7
#define SD_SCK_PIN 4
#define SD_MOSI_PIN 6
#define SD_MISO_PIN 5
const char* LOG_FILE = "/envlog.csv";
const unsigned long LOG_INTERVAL_MS = 60000; // once a minute
RTC_DS3231 rtc;
Adafruit_AHTX0 aht;
sensors_event_t humidityEvent, tempEvent;
unsigned long lastLog = 0;
void setup() {
Serial.begin(115200);
delay(500);
Wire.begin(SDA_PIN, SCL_PIN);
if (!rtc.begin()) {
Serial.println("DS3231 not found — check wiring.");
while (1) delay(1000);
}
if (rtc.lostPower()) {
Serial.println("RTC lost power, setting time from compile time.");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
if (!aht.begin()) {
Serial.println("AHT20 not found — check wiring.");
while (1) delay(1000);
}
SPI.begin(SD_SCK_PIN, SD_MISO_PIN, SD_MOSI_PIN, SD_CS_PIN);
if (!SD.begin(SD_CS_PIN)) {
Serial.println("SD card init failed — check wiring/card.");
while (1) delay(1000);
}
// Write a CSV header if the file doesn't exist yet
if (!SD.exists(LOG_FILE)) {
File f = SD.open(LOG_FILE, FILE_WRITE);
if (f) {
f.println("timestamp,temperature_c,humidity_pct");
f.close();
}
}
Serial.println("Logger ready.");
}
void loop() {
unsigned long now = millis();
if (now - lastLog < LOG_INTERVAL_MS) return;
lastLog = now;
DateTime ts = rtc.now();
aht.getEvent(&humidityEvent, &tempEvent);
char timestamp[20];
snprintf(timestamp, sizeof(timestamp), "%04d-%02d-%02d %02d:%02d:%02d",
ts.year(), ts.month(), ts.day(), ts.hour(), ts.minute(), ts.second());
File f = SD.open(LOG_FILE, FILE_WRITE);
if (f) {
f.print(timestamp);
f.print(",");
f.print(tempEvent.temperature);
f.print(",");
f.println(humidityEvent.relative_humidity);
f.close();
Serial.printf("Logged: %s, %.1fC, %.1f%%\n", timestamp, tempEvent.temperature, humidityEvent.relative_humidity);
} else {
Serial.println("Failed to open log file for writing.");
}
}
ไม่แน่ใจขั้นตอนไหน?
ถามเรา