Small enclosure with 0.96 inch OLED display and rotary encoder knob, ESP32-S3 and AHT20 sensor on breadboard

ระดับกลาง

สร้างแผงควบคุมเซนเซอร์ขนาดกะทัดรัด

จอแสดงผลเซ็นเซอร์IoT

สร้างแดชบอร์ดขนาดเล็กที่รวมการอ่านค่าเซ็นเซอร์หลายค่าไว้ในจอแสดงผลเดียว และแนะนำข้อมูลที่มีโครงสร้าง การจัดวางหน้าจอ และการอัปเดตเป็นระยะ

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

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

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

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

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

โครงการนี้นำค่าที่อ่านได้จากเซ็นเซอร์หลายตัวมารวมกันบนจอแสดงผลเดียวพร้อมกัน โดยก้าวข้ามการแสดงผลค่าเดียวไปสู่การจัดวางที่มีโครงสร้าง แหล่งข้อมูลหลายแหล่ง และส่วนต่อประสานผู้ใช้ที่เป็นจริง (แม้จะเรียบง่ายก็ตาม)

การรวบรวมค่าเซ็นเซอร์หลายค่า

เมื่อเชื่อมต่อเซ็นเซอร์มากกว่าหนึ่งตัว ให้อ่านค่าแต่ละตัวในแต่ละรอบ และเก็บค่าไว้ในชุดตัวแปรขนาดเล็กที่มีชื่อชัดเจนก่อนที่จะสัมผัสหน้าจอเลย การผสมตรรกะการอ่านและการวาดเข้าด้วยกันมักจะทำให้การดีบักยากขึ้นทั้งสองอย่าง

การจัดวางหน้าจอ

ตัดสินใจเลือกการจัดวางที่แน่นอนว่าค่าใดจะไปอยู่ที่ใดบนหน้าจอก่อนที่จะเขียนโค้ดการวาด ตารางอย่างง่าย (หนึ่งช่องสำหรับแต่ละค่า) จะง่ายกว่าที่จะทำได้ถูกต้องในการลองครั้งแรกมากกว่าการพยายามจัดวางสิ่งต่างๆ แบบไดนามิก

ช่วงเวลาการรีเฟรช

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

การหลีกเลี่ยงการกะพริบของหน้าจอ

เท่าที่เป็นไปได้ ให้วาดใหม่เฉพาะส่วนของหน้าจอที่ค่ามีการเปลี่ยนแปลงตั้งแต่การอัปเดตครั้งล่าสุดเท่านั้น แทนที่จะล้างและวาดใหม่ทั้งหน้าจอในทุกรอบการทำงาน ไม่ใช่ทุกไลบรารีการแสดงผลจะรองรับการอัปเดตบางส่วนได้ดีเท่ากัน ตรวจสอบว่าจอแสดงผลและไลบรารีของคุณรองรับอะไรบ้างก่อนที่จะสันนิษฐานว่าการเพิ่มประสิทธิภาพนี้มีให้ใช้งาน

การจัดรูปแบบข้อมูล

ตัดสินใจเลือกการจัดรูปแบบที่สอดคล้องกันสำหรับแต่ละค่า (ทศนิยม หน่วย ความกว้างสูงสุดที่กำหนด) ก่อนแสดงผล เพื่อให้การจัดวางไม่เลื่อนไปมาอย่างเห็นได้ชัดเมื่อตัวเลขเปลี่ยนความยาวจากการอ่านค่าหนึ่งไปอีกค่าหนึ่ง

การตรวจสอบความถูกต้องของข้อมูล

หลักการตรวจสอบความถูกต้องแบบเดียวกันจากโครงการเซ็นเซอร์เดียวใช้ได้กับทุกค่าที่นี่: อย่าแสดง (หรือดำเนินการตาม) การอ่านค่าที่ไม่น่าเชื่อถือจากเซ็นเซอร์ใดๆ โดยไม่ทำเครื่องหมายไว้อย่างน้อย เนื่องจากค่าที่ไม่ดีจากเซ็นเซอร์หนึ่งไม่ควรนำเสนอด้วยความมั่นใจเท่ากับค่าที่ทำงานได้

การขยายเพิ่มเติมทางเลือก: ปุ่มและเมนู

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

การอัปเกรดทางเลือก: เวอร์ชันที่เชื่อมต่อเครือข่าย

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

การเดินสายไฟ

  • OLED VCC → 3.3V, GND → GND, SDA → GPIO8, SCL → GPIO9 (ขา I2C เริ่มต้นของ ESP32-S3)
  • AHT20 VIN → 3.3V, GND → GND, SDA → GPIO8, SCL → GPIO9 — บัส I2C เดียวกันกับ OLED (OLED ที่ 0x3C, AHT20 ที่ 0x38, ไม่มีข้อขัดแย้ง)
  • ตัวเข้ารหัสแบบหมุน VCC → 3.3V, GND → GND
  • ตัวเข้ารหัสแบบหมุน CLK → ESP32-S3 GPIO4 (`ENC_CLK_PIN`)
  • ตัวเข้ารหัสแบบหมุน DT → ESP32-S3 GPIO5 (`ENC_DT_PIN`)
  • ตัวเข้ารหัสแบบหมุน SW → ESP32-S3 GPIO6 (`ENC_SW_PIN`, ใช้ `INPUT_PULLUP`)

อุปกรณ์ I2C ทั้งสองใช้บัสเดียวกันได้ดีที่ที่อยู่เริ่มต้นของมัน — เพียงแค่ตรวจสอบให้แน่ใจว่าโมดูล OLED ของคุณไม่ใช่หนึ่งในรุ่น 0x3D ที่หายาก หลีกเลี่ยงการเดินสายอะไรก็ตามไปยัง GPIO0 บน S3 (เป็นขา BOOT/strapping); ขาด้านบนเป็น GPIO ปกติบนบอร์ดพัฒนา ESP32-S3 ส่วนใหญ่ แต่ให้ยืนยันกับแผนภาพการจัดขาของบอร์ดเฉพาะของคุณ ตัวเข้ารหัสจะถูกอ่านโดยการสำรวจอย่างง่ายที่นี่เพื่อความชัดเจน — หากคุณเห็นว่ามีการพลาด Detent ในการหมุนที่เร็วขึ้น การเปลี่ยน CLK/DT ไปเป็นการอ่านแบบ Interrupt-driven เป็นวิธีแก้ไขทั่วไป

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

VCC GND SDA → GPIO8 SCL → GPIO9 VIN GND SDA → GPIO8 SCL → GPIO9 VCC GND CLK → GPIO4 DT → GPIO5 SW → GPIO6 ESP32-S3 OLED AHT20 Rotary encoder 3.3V GND

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

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_AHTX0.h>

#define SDA_PIN 8
#define SCL_PIN 9

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define OLED_ADDR 0x3C

#define ENC_CLK_PIN 4
#define ENC_DT_PIN 5
#define ENC_SW_PIN 6

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_AHTX0 aht;
sensors_event_t humidityEvent, tempEvent;

int screenIndex = 0;
const int NUM_SCREENS = 3;

int lastClkState;
unsigned long lastButtonPress = 0;

void readSensor() {
  aht.getEvent(&humidityEvent, &tempEvent);
}

void drawScreen() {
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);

  switch (screenIndex) {
    case 0:
      display.println("TEMPERATURE");
      display.setTextSize(2);
      display.setCursor(0, 20);
      display.print(tempEvent.temperature, 1);
      display.println(" C");
      break;
    case 1:
      display.println("HUMIDITY");
      display.setTextSize(2);
      display.setCursor(0, 20);
      display.print(humidityEvent.relative_humidity, 1);
      display.println(" %");
      break;
    case 2:
      display.println("SUMMARY");
      display.setCursor(0, 20);
      display.print("Temp: ");
      display.print(tempEvent.temperature, 1);
      display.println(" C");
      display.print("Hum:  ");
      display.print(humidityEvent.relative_humidity, 1);
      display.println(" %");
      break;
  }
  display.display();
}

void setup() {
  Serial.begin(115200);
  Wire.begin(SDA_PIN, SCL_PIN);

  pinMode(ENC_CLK_PIN, INPUT);
  pinMode(ENC_DT_PIN, INPUT);
  pinMode(ENC_SW_PIN, INPUT_PULLUP);
  lastClkState = digitalRead(ENC_CLK_PIN);

  if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
    Serial.println("OLED not found — check wiring.");
    while (1) delay(1000);
  }
  if (!aht.begin()) {
    Serial.println("AHT20 not found — check wiring.");
    while (1) delay(1000);
  }

  readSensor();
  drawScreen();
}

void loop() {
  // Rotary encoder rotation: compare CLK against DT on each CLK edge
  int clkState = digitalRead(ENC_CLK_PIN);
  if (clkState != lastClkState && clkState == HIGH) {
    if (digitalRead(ENC_DT_PIN) != clkState) {
      screenIndex = (screenIndex + 1) % NUM_SCREENS;      // clockwise
    } else {
      screenIndex = (screenIndex - 1 + NUM_SCREENS) % NUM_SCREENS; // counter-clockwise
    }
    drawScreen();
  }
  lastClkState = clkState;

  // Push button: force a sensor refresh
  if (digitalRead(ENC_SW_PIN) == LOW && millis() - lastButtonPress > 250) {
    lastButtonPress = millis();
    readSensor();
    drawScreen();
  }

  static unsigned long lastAutoRead = 0;
  if (millis() - lastAutoRead > 2000) {
    lastAutoRead = millis();
    readSensor();
    drawScreen();
  }
}

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