diff --git a/OLEDDisplay.cpp b/OLEDDisplay.cpp new file mode 100644 index 0000000..3fc4009 --- /dev/null +++ b/OLEDDisplay.cpp @@ -0,0 +1,201 @@ +// ===================================================================== +// OLEDDisplay.cpp +// ===================================================================== +#include "OLEDDisplay.h" + +OLEDDisplay::OLEDDisplay(uint8_t sdaPin, uint8_t sclPin, uint8_t i2cAddr) + : _display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET), + _sdaPin(sdaPin), + _sclPin(sclPin), + _i2cAddr(i2cAddr) { +} + +bool OLEDDisplay::begin() { + Wire.begin(_sdaPin, _sclPin); + + if (!_display.begin(SSD1306_SWITCHCAPVCC, _i2cAddr)) { + Serial.println(F("[OLED] Không thể khởi tạo màn hình!")); + return false; + } + + _display.clearDisplay(); + _display.setTextColor(SSD1306_WHITE); + _display.display(); + + Serial.println(F("[OLED] Đã khởi tạo thành công")); + return true; +} + +void OLEDDisplay::clear() { + _display.clearDisplay(); + _display.display(); +} + +void OLEDDisplay::centerText(const char* text, int16_t y) { + int16_t x1, y1; + uint16_t w, h; + _display.getTextBounds(text, 0, 0, &x1, &y1, &w, &h); + _display.setCursor((SCREEN_WIDTH - w) / 2, y); + _display.print(text); +} + +void OLEDDisplay::showBootScreen(const char* nodeId) { + _display.clearDisplay(); + _display.setTextSize(1); + + centerText("ESP32-S3", 10); + centerText("LD2453 RADAR", 25); + + _display.setCursor(0, 45); + _display.print("Node: "); + _display.println(nodeId); + + _display.display(); +} + +void OLEDDisplay::showWiFiConnecting(const char* ssid) { + _display.clearDisplay(); + _display.setTextSize(1); + + centerText("Ket noi WiFi...", 15); + + _display.setCursor(0, 35); + _display.print("SSID: "); + + // Cắt SSID nếu quá dài + char shortSSID[17]; + strncpy(shortSSID, ssid, 16); + shortSSID[16] = '\0'; + _display.println(shortSSID); + + _display.display(); +} + +void OLEDDisplay::showWiFiConnected(IPAddress ip) { + _display.clearDisplay(); + _display.setTextSize(1); + + centerText("WiFi OK!", 15); + + _display.setCursor(0, 35); + _display.print("IP: "); + _display.println(ip); + + _display.display(); + delay(2000); // Hiển thị 2 giây +} + +void OLEDDisplay::showDiagnosticMode() { + _display.clearDisplay(); + _display.setTextSize(1); + + centerText("DIAGNOSTIC MODE", 10); + centerText("Serial Only", 25); + centerText("No WiFi", 40); + + _display.display(); + delay(2000); +} + +void OLEDDisplay::updateRadarInfo(const RadarFrame &frame, const LD2453Stats &stats) { + _display.clearDisplay(); + _display.setTextSize(1); + + // Dòng 1: Tiêu đề + _display.setCursor(0, 0); + _display.print("LD2453 "); + if (stats.bytesReceived > 0) { + _display.print("RX:"); + _display.print(stats.bytesReceived); + } else { + _display.print("NO DATA"); + } + + // Dòng 2: Frames parsed + _display.setCursor(0, 12); + _display.print("Frames: "); + _display.print(stats.framesParsed); + + // Dòng 3: Errors + _display.setCursor(0, 24); + _display.print("Errors: "); + _display.print(stats.parseErrors); + + // Dòng 4: PPS + _display.setCursor(0, 36); + _display.print("PPS: "); + _display.print(stats.packetsPerSecond, 1); + + // Dòng 5: Target count + _display.setCursor(0, 48); + _display.print("Targets: "); + _display.print(frame.targetCount); + + // Hiển thị các target nếu có + for (uint8_t i = 0; i < frame.targetCount && i < 3; i++) { + if (frame.targets[i].valid) { + _display.print(" T"); + _display.print(i + 1); + } + } + + _display.display(); +} + +void OLEDDisplay::updateDetectionSummary(uint8_t targetCount, bool zone1, bool zone2, bool zone3) { + _display.clearDisplay(); + _display.setTextSize(1); + + // Tiêu đề + _display.setCursor(0, 0); + _display.print("DETECTION STATUS"); + + // Số target phát hiện + _display.setCursor(0, 15); + _display.setTextSize(2); + _display.print("T:"); + _display.print(targetCount); + + // Zones + _display.setTextSize(1); + _display.setCursor(0, 35); + _display.print("Zone1: "); + _display.println(zone1 ? "YES" : "NO"); + + _display.setCursor(0, 45); + _display.print("Zone2: "); + _display.println(zone2 ? "YES" : "NO"); + + _display.setCursor(0, 55); + _display.print("Zone3: "); + _display.println(zone3 ? "YES" : "NO"); + + _display.display(); +} + +void OLEDDisplay::updateStats(const LD2453Stats &stats) { + _display.clearDisplay(); + _display.setTextSize(1); + + // Tiêu đề + centerText("LD2453 RADAR", 0); + + // Thống kê + _display.setCursor(0, 15); + _display.print("Bytes RX: "); + _display.println(stats.bytesReceived); + + _display.setCursor(0, 27); + _display.print("Frames: "); + _display.println(stats.framesParsed); + + _display.setCursor(0, 39); + _display.print("Errors: "); + _display.println(stats.parseErrors); + + _display.setCursor(0, 51); + _display.print("PPS: "); + _display.print(stats.packetsPerSecond, 1); + + _display.display(); +} diff --git a/OLEDDisplay.h b/OLEDDisplay.h new file mode 100644 index 0000000..3467c8a --- /dev/null +++ b/OLEDDisplay.h @@ -0,0 +1,50 @@ +// ===================================================================== +// OLEDDisplay.h +// Quản lý hiển thị màn hình OLED I2C 128x64 +// ===================================================================== +#pragma once + +#include +#include +#include +#include +#include "LD2453Parser.h" + +#define SCREEN_WIDTH 128 +#define SCREEN_HEIGHT 64 +#define OLED_RESET -1 // Reset pin (hoặc -1 nếu dùng chung với ESP32 reset) + +class OLEDDisplay { +public: + OLEDDisplay(uint8_t sdaPin, uint8_t sclPin, uint8_t i2cAddr); + + bool begin(); + void clear(); + + // Hiển thị trạng thái khởi động + void showBootScreen(const char* nodeId); + + // Hiển thị trạng thái kết nối WiFi + void showWiFiConnecting(const char* ssid); + void showWiFiConnected(IPAddress ip); + + // Hiển thị chế độ Diagnostic + void showDiagnosticMode(); + + // Hiển thị thông tin radar (DIAGNOSTIC_MODE) + void updateRadarInfo(const RadarFrame &frame, const LD2453Stats &stats); + + // Hiển thị tóm tắt detection (full mode) + void updateDetectionSummary(uint8_t targetCount, bool zone1, bool zone2, bool zone3); + + // Hiển thị thống kê khi không có frame + void updateStats(const LD2453Stats &stats); + +private: + Adafruit_SSD1306 _display; + uint8_t _sdaPin; + uint8_t _sclPin; + uint8_t _i2cAddr; + + void centerText(const char* text, int16_t y); +}; diff --git a/README.md b/README.md index 292d72a..3f46c04 100644 --- a/README.md +++ b/README.md @@ -44,14 +44,24 @@ capture) is fully implemented and ready to go. ## Wiring +### LD2453 Radar ``` -LD2453 ESP32-S3 + LD2453 ESP32-S3 V -> 3V3 G -> GND T/TX -> RADAR_RX_PIN (default GPIO18) R/RX -> RADAR_TX_PIN (default GPIO17) ``` +### OLED Display I2C 128x64 +``` + OLED ESP32-S3 +VCC -> 3V3 +GND -> GND +SCL -> GPIO7 (OLED_SCL_PIN) +SDA -> GPIO6 (OLED_SDA_PIN) +``` + Change `RADAR_RX_PIN` / `RADAR_TX_PIN` in `config.h` to match your wiring — any two free GPIOs that support UART on your ESP32-S3 board will work, since the ESP32 Arduino core remaps UART pins in software. @@ -83,10 +93,42 @@ ESP32_LD2453/ LD2453Parser.h/.cpp - UART acquisition + frame parsing (STUBBED, see above) ZoneManager.h/.cpp - rectangular zone occupancy logic WebServerManager.h/.cpp - Wi-Fi, REST API, WebSocket broadcast + OLEDDisplay.h/.cpp - OLED I2C 128x64 display manager config.example.h - copy to config.h and edit README.md ``` +## OLED Display Features + +Màn hình OLED 128x64 hiển thị các thông tin: + +### DIAGNOSTIC_MODE = 1 (Serial Debug Mode) +- **Màn hình boot**: Hiển thị logo và Node ID +- **Chế độ Diagnostic**: Thông báo Serial-only mode +- **Thống kê radar mỗi 5 giây**: + - Số bytes nhận được từ LD2453 + - Số frames đã parse + - Số lỗi parse + - Packets per second (PPS) + - Số target phát hiện được + +### DIAGNOSTIC_MODE = 0 (Full WiFi Mode) +- **Kết nối WiFi**: Hiển thị SSID đang kết nối +- **WiFi thành công**: Hiển thị địa chỉ IP +- **Detection realtime**: + - Số target phát hiện (T:0-3) + - Trạng thái 3 zones (YES/NO) + +### Cấu hình OLED trong config.h +```cpp +#define OLED_ENABLED 1 // 1 = bật OLED, 0 = tắt +#define OLED_SDA_PIN 6 // Chân SDA +#define OLED_SCL_PIN 7 // Chân SCL +#define OLED_I2C_ADDR 0x3C // Địa chỉ I2C (thường là 0x3C hoặc 0x3D) +``` + +**Lưu ý**: Nếu màn hình không hoạt động, hãy thử đổi `OLED_I2C_ADDR` từ `0x3C` sang `0x3D`. + ## Stage 1: diagnostic mode (do this first) 1. Copy `config.example.h` to `config.h`. diff --git a/WebServerManager.h b/WebServerManager.h index 7e9139f..28a3b23 100644 --- a/WebServerManager.h +++ b/WebServerManager.h @@ -31,7 +31,8 @@ public: // Format") to all connected WebSocket clients for this frame. // No-op if no clients are connected. void broadcastFrame(const RadarFrame &frame); - + + IPAddress getLocalIP() const { return WiFi.localIP(); } bool isWifiConnected() const { return WiFi.status() == WL_CONNECTED; } private: diff --git a/config.h b/config.h index ae2fd45..28d9430 100644 --- a/config.h +++ b/config.h @@ -35,6 +35,14 @@ // --------------------------------------------------------------- #define NODE_ID "RADAR_A" +// --------------------------------------------------------------- +// OLED Display (I2C 128x64) +// --------------------------------------------------------------- +#define OLED_ENABLED 1 // 1 = enable OLED display, 0 = disable +#define OLED_SDA_PIN 6 // I2C SDA pin +#define OLED_SCL_PIN 7 // I2C SCL pin +#define OLED_I2C_ADDR 0x3C // I2C address (0x3C or 0x3D, check your module) + // --------------------------------------------------------------- // Debug / diagnostic switches // --------------------------------------------------------------- diff --git a/esp32-s3-ld2453.ino b/esp32-s3-ld2453.ino index a470449..41cdc4e 100644 --- a/esp32-s3-ld2453.ino +++ b/esp32-s3-ld2453.ino @@ -35,6 +35,10 @@ HardwareSerial &RadarSerial = Serial2; LD2453Parser radar; ZoneManager zoneManager; +#if OLED_ENABLED +OLEDDisplay oledDisplay(OLED_SDA_PIN, OLED_SCL_PIN, OLED_I2C_ADDR); +#endif + #if !DIAGNOSTIC_MODE WebServerManager webServer(zoneManager); #endif @@ -104,8 +108,22 @@ void loop() { #if DIAGNOSTIC_MODE printDiagnostics(frame, radar.getStats()); + + #if OLED_ENABLED + oledDisplay.updateRadarInfo(frame, radar.getStats()); + #endif #else webServer.broadcastFrame(frame); + + #if OLED_ENABLED + // Hiển thị tóm tắt detection và zones + oledDisplay.updateDetectionSummary( + frame.targetCount, + zoneManager.isZoneOccupied(0), + zoneManager.isZoneOccupied(1), + zoneManager.isZoneOccupied(2) + ); + #endif #endif } @@ -125,6 +143,11 @@ void loop() { Serial.print(F("Packets/sec: ")); Serial.println(stats.packetsPerSecond, 1); lastStatsReport = now; + + #if OLED_ENABLED + // Cập nhật OLED ngay cả khi không có frame + oledDisplay.updateStats(stats); + #endif } #else webServer.loop();