diff --git a/.gitignore b/.gitignore index 8b13789..496ee2c 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ - +.DS_Store \ No newline at end of file diff --git a/Global/config/config.h b/Global/config/config.h new file mode 100644 index 0000000..76883cd --- /dev/null +++ b/Global/config/config.h @@ -0,0 +1,42 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include + +// --- SoftAP Configuration (Hosted by Station 1) --- +const char* AP_SSID = "Web3_Showcase_AP"; +const char* AP_PASSWORD = NULL; // Open network for easy Captive Portal + +// --- IP Address Mapping --- +// Gateway (Station 1) must be x.x.x.1 +IPAddress IP_STATION1_AP(192, 168, 4, 1); // Core2: Identity & AP +IPAddress IP_STATION2_MON(192, 168, 4, 2); // Core2: Auth Monitor +IPAddress IP_RESET_MON(192, 168, 4, 3); // Core Basic: System Monitor & Reset + +IPAddress IP_STICKC(192, 168, 4, 10); // Wearable +IPAddress IP_ATOM_MATRIX(192, 168, 4, 20); // S2/S4 Sensor +IPAddress IP_ATOM_ECHO(192, 168, 4, 30); // Sound +IPAddress IP_PAPER_S3(192, 168, 4, 40); // Earn +IPAddress IP_PAPER_S4(192, 168, 4, 50); // Spend + +IPAddress NETMASK(255, 255, 255, 0); + +// --- Endpoints --- +#define ENDPOINT_HEARTBEAT "/heartbeat" +#define ENDPOINT_SET_USER "/set_user" +#define ENDPOINT_SET_AUTH "/set_auth" +#define ENDPOINT_RESET_GLOBAL "/reset_global" + +// Earn และ Spend +#define ENDPOINT_EARN_COIN "/earn_coin" +#define ENDPOINT_SPEND_COIN "/spend_coin" + +// Station 4 Order System +#define ENDPOINT_GET_ORDER "/get_order" +#define ENDPOINT_SEND_ORDER "/send_order" + +// Audio Endpoints +#define ENDPOINT_PLAY_AUTH "/play_auth" +#define ENDPOINT_PLAY_TX "/play_tx" + +#endif // CONFIG_H \ No newline at end of file diff --git a/Global/core2/core2.cpp b/Global/core2/core2.cpp new file mode 100644 index 0000000..5e1d086 --- /dev/null +++ b/Global/core2/core2.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include +#include + +// 1. กำหนดค่า SoftAP +const char *ssid = "Web3Showcase_AP"; +const char *password = "12345678"; +const char* STICKC_IP = "192.168.4.2"; +const int STICKC_PORT = 88; + +IPAddress localIP(192, 168, 4, 1); +IPAddress gateway(192, 168, 4, 1); +IPAddress subnet(255, 255, 255, 0); + +AsyncWebServer server(80); +String capturedUsername = ""; + +// Forward Declarations: ประกาศฟังก์ชันไว้ด้านบนเพื่อแก้ปัญหา Scope +void sendUsernameToStickC(String username); +void displayQRCode(const char* data); + +// โค้ด HTML สำหรับ Captive Portal +const char* CAPTIVE_HTML = R"raw( + + + + + + Web3 Student Club + + + +

Web3 Student Club

+
+
+
+ +
+ + +)raw"; + +// ฟังก์ชันสำหรับส่ง Username ไปยัง StickC-Plus2 +void sendUsernameToStickC(String username) { + if (WiFi.getMode() == WIFI_AP) { + HTTPClient http; + String url = "http://" + String(STICKC_IP) + ":" + String(STICKC_PORT) + "/set_username"; + + http.begin(url); + http.addHeader("Content-Type", "application/x-www-form-urlencoded"); + + String postData = "username=" + username; + + int httpResponseCode = http.POST(postData); + + M5.Lcd.setCursor(5, 100); + if (httpResponseCode > 0) { + M5.Lcd.printf("Sent to StickC (Code: %d)", httpResponseCode); + } else { + M5.Lcd.printf("StickC Request Failed (Error: %s)", http.errorToString(httpResponseCode).c_str()); + } + + http.end(); + } +} + +// ฟังก์ชันสำหรับแสดง QR Code +void displayQRCode(const char* data) { + M5.Lcd.fillScreen(BLACK); + M5.Lcd.setTextDatum(top_center); + M5.Lcd.setTextColor(WHITE); + M5.Lcd.setFont(&fonts::Font4); + M5.Lcd.drawString("Scan Here", M5.Lcd.width() / 2, 10); + + int size = 180; + int x = (M5.Lcd.width() - size) / 2; + int y = 50; + + QRCode qrcode; + uint8_t qrcodeData[qrcode_getBufferSize(3)]; + qrcode_initText(&qrcode, qrcodeData, 3, 0, data); + M5.Lcd.qrcode(data, x, y, size); +} + +void setup() { + auto cfg = M5.config(); + M5.begin(cfg); + M5.Lcd.setRotation(1); + + // 1. สร้าง WiFi Access Point (SoftAP) + M5.Lcd.print("Setting up SoftAP..."); + WiFi.mode(WIFI_AP); + WiFi.softAPConfig(localIP, gateway, subnet); + bool result = WiFi.softAP(ssid, password); + + if (result) { + M5.Lcd.printf("\nSoftAP Ready! SSID: %s\n", ssid); + M5.Lcd.printf("IP: %s\n", WiFi.softAPIP().toString().c_str()); + } else { + M5.Lcd.println("SoftAP Failed!"); + return; + } + + // 2. แสดง QR Code สำหรับการเชื่อมต่อ + String qrData = "WIFI:S:" + String(ssid) + ";T:WPA;P:" + String(password) + ";;"; + displayQRCode(qrData.c_str()); + + // 3. ตั้งค่า Web Server สำหรับ Captive Portal + + // A. Handle Root Path (หน้า Captive Portal หลัก) + server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ + request->send(200, "text/html", CAPTIVE_HTML); + }); + + // B. Handle Submission (รับ Username) + server.on("/submit", HTTP_POST, [](AsyncWebServerRequest *request){ + if (request->hasParam("username", true)) { + // ดึง Username จาก Form Data + String capturedUsername = request->getParam("username", true)->value(); + + M5.Lcd.fillScreen(BLACK); + M5.Lcd.setTextDatum(top_left); + M5.Lcd.setFont(&fonts::Font2); + M5.Lcd.printf("Username Received: %s", capturedUsername.c_str()); + + request->send(200, "text/plain", "Username '" + capturedUsername + "' submitted successfully."); + + // 7a. ส่งข้อมูล Username ที่ได้รับมาไปยัง StickC-Plus2 + sendUsernameToStickC(capturedUsername); + + } else { + request->send(400, "text/plain", "Missing Username"); + } + }); + + server.begin(); + M5.Lcd.println("\nHTTP Server Started."); +} + +void loop() { + M5.update(); +} \ No newline at end of file diff --git a/Global/stick/m5stick-c-plus.cpp b/Global/stick/m5stick-c-plus.cpp new file mode 100644 index 0000000..24b438d --- /dev/null +++ b/Global/stick/m5stick-c-plus.cpp @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include +#include +#include +#include // เพิ่ม Library นี้สำหรับเปลี่ยนชื่อแบบ Fast Mode + +// --- CONFIGURATION --- +const char *SSID_AP = "Web3Showcase_AP"; +const char *PASSWORD_AP = "12345678"; +const int LOCAL_PORT = 88; + +// --- FIX IP SETTINGS --- +IPAddress localIP(192, 168, 4, 10); +IPAddress gateway(192, 168, 4, 1); +IPAddress subnet(255, 255, 255, 0); + +// --- GLOBALS --- +AsyncWebServer stickCServer(LOCAL_PORT); +String currentUsername = "Not Registered"; +String authenStatus = "X"; +int ccoin = 0; +String alertText = "Waiting for Identity..."; +String myMacAddress = ""; + +BLEServer *pServer = NULL; +BLEAdvertising *pAdvertising = NULL; + +// Flag ฝากงานให้ Loop ทำ +bool shouldRestartBLE = false; +String newBLEName = ""; + +// --- FUNCTIONS --- + +// 1. ฟังก์ชันเปลี่ยนชื่อ BLE (ฉบับแก้ไข: ไม่ต้องปิดระบบ Matrix เห็นไวมาก) +void startBLE(String name) { + // กรณีเพิ่งเริ่มครั้งแรก (Start) + if (pAdvertising == NULL) { + BLEDevice::init(name.c_str()); + pServer = BLEDevice::createServer(); + pAdvertising = BLEDevice::getAdvertising(); + pAdvertising->setScanResponse(true); + pAdvertising->setMinPreferred(0x06); + pAdvertising->setMinPreferred(0x12); + } else { + // กรณีเปลี่ยนชื่อ: แค่หยุดประกาศชั่วคราว (Stop) + pAdvertising->stop(); + } + + // --- ส่วนสำคัญ: เปลี่ยนชื่อระดับ Hardware โดยไม่ต้อง deinit --- + esp_ble_gap_set_device_name(name.c_str()); + // ------------------------------------------------------- + + // จัดกระเป๋าใบที่ 1 (Main): ใส่ UUID 1234 + BLEAdvertisementData oAdvertisementData = BLEAdvertisementData(); + oAdvertisementData.setFlags(0x06); + oAdvertisementData.setCompleteServices(BLEUUID("1234")); + pAdvertising->setAdvertisementData(oAdvertisementData); + + // จัดกระเป๋าใบที่ 2 (Response): ใส่ชื่อใหม่ + BLEAdvertisementData oScanResponseData = BLEAdvertisementData(); + oScanResponseData.setName(name.c_str()); + pAdvertising->setScanResponseData(oScanResponseData); + + // เริ่มประกาศอีกครั้ง (Start) + pAdvertising->start(); + + Serial.printf("[BLE] Name Updated to: %s (UUID: 1234)\n", name.c_str()); +} + +// 2. ฟังก์ชันอัปเดตหน้าจอ +void updateDisplay() { + M5.Display.fillScreen(BLACK); + M5.Display.setTextDatum(top_left); + M5.Display.setFont(&fonts::Font2); + M5.Display.setTextColor(WHITE); + + M5.Display.setCursor(5, 5); + M5.Display.printf("IP: %s\n", WiFi.localIP().toString().c_str()); + M5.Display.setCursor(5, 25); + M5.Display.printf("User: %s\n", currentUsername.c_str()); + M5.Display.printf("Status: %s\n", authenStatus.c_str()); + M5.Display.printf("CCoin: %d\n", ccoin); + + if (authenStatus == "/") M5.Display.setTextColor(GREEN); + else M5.Display.setTextColor(ORANGE); + + M5.Display.setCursor(5, 95); + M5.Display.printf("MSG: %s", alertText.c_str()); +} + +// 3. Handler รับ Username จาก Core2 +void handleUsername(AsyncWebServerRequest *request) { + String receivedUsername = ""; + if (request->hasParam("username", true)) { + receivedUsername = request->getParam("username", true)->value(); + } else if (request->hasParam("username", false)) { + receivedUsername = request->getParam("username", false)->value(); + } + + if (receivedUsername != "") { + M5.Speaker.tone(1500, 150); + + Serial.printf("[HTTP] Received Username: %s\n", receivedUsername.c_str()); + + currentUsername = receivedUsername; + alertText = "Identity Confirmed!"; + authenStatus = "/"; + + // ยกธงบอก Loop ให้เปลี่ยนชื่อ + newBLEName = receivedUsername; + shouldRestartBLE = true; + + updateDisplay(); + request->send(200, "text/plain", "OK"); + } else { + request->send(400, "text/plain", "Fail: Missing Username"); + } +} + +// Handler สำหรับรับ Coin จาก Paper (/add_coin) +void handleCoin(AsyncWebServerRequest *request) { + if (request->hasParam("value", true)) { + String coin = request->getParam("value", true)->value(); + + M5.Speaker.tone(1500, 150); + + ccoin += coin.toInt(); + alertText = "Receive " + coin + " CCoin"; + updateDisplay(); + + request->send(200, "text/plain", "Coin received."); + } else { + request->send(400, "text/plain", "Missing value Parameter."); + } +} + +void setup() { + auto cfg = M5.config(); + M5.begin(cfg); + M5.Display.setRotation(3); + Serial.begin(115200); + + // --- Phase 1: Connecting WiFi --- + M5.Display.fillScreen(BLACK); + M5.Display.setFont(&fonts::Font2); + M5.Display.setTextColor(WHITE); + M5.Display.setTextDatum(middle_center); + M5.Display.drawString("Connecting WiFi...", M5.Display.width()/2, M5.Display.height()/2 - 10); + + WiFi.mode(WIFI_STA); + + WiFi.config(localIP, gateway, subnet); + WiFi.begin(SSID_AP, PASSWORD_AP); + + Serial.println("[WiFi] Connecting..."); + + M5.Display.setTextDatum(top_left); + M5.Display.setCursor(10, M5.Display.height()/2 + 10); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + M5.Display.print("."); + Serial.print("."); + } + Serial.println("\n[WiFi] Connected!"); + + // --- Phase 2: System Ready --- + myMacAddress = WiFi.macAddress(); + + stickCServer.on("/set_username", HTTP_POST, handleUsername); + stickCServer.on("/set_username", HTTP_GET, handleUsername); + stickCServer.on("/add_coin", HTTP_POST, handleCoin); + stickCServer.begin(); + + // เริ่ม BLE ครั้งแรก + startBLE("GUEST-PLAYER"); + + updateDisplay(); +} + +void loop() { + // --- โซนทำงานหนัก (ปลอดภัย ไม่รีเซ็ต) --- + if (shouldRestartBLE) { + delay(100); + M5.Speaker.tone(2000, 300); + + // เรียกฟังก์ชันเปลี่ยนชื่อแบบใหม่ (ที่ไม่พัง) + startBLE(newBLEName); + + shouldRestartBLE = false; + } + + M5.update(); +} \ No newline at end of file diff --git a/Station2/echo/echo.ino b/Station2/echo/echo.ino new file mode 100644 index 0000000..fa14f81 --- /dev/null +++ b/Station2/echo/echo.ino @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include // เพิ่มเพื่อบังคับ Channel + +#define CONFIG_I2S_BCK_PIN 19 +#define CONFIG_I2S_LRCK_PIN 33 +#define CONFIG_I2S_DATA_PIN 22 +#define CONFIG_I2S_DATA_IN_PIN 23 + +// ตั้งค่า I2S +void InitI2S() { + i2s_config_t i2s_config = { + .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), + .sample_rate = 44100, + .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, + .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, + .communication_format = I2S_COMM_FORMAT_I2S_MSB, // ใช้ MSB เสียงจะดีกว่า + .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, + .dma_buf_count = 8, + .dma_buf_len = 64 + }; + i2s_pin_config_t pin_config = { + .bck_io_num = CONFIG_I2S_BCK_PIN, + .ws_io_num = CONFIG_I2S_LRCK_PIN, + .data_out_num = CONFIG_I2S_DATA_PIN, + .data_in_num = CONFIG_I2S_DATA_IN_PIN + }; + i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); + i2s_set_pin(I2S_NUM_0, &pin_config); + i2s_set_clk(I2S_NUM_0, 44100, I2S_BITS_PER_SAMPLE_16BIT, I2S_CHANNEL_MONO); +} + +// สร้างเสียง Beep +void PlayBeep(int freq, int duration_ms) { + size_t bytes_written; + int samples = 44100 * duration_ms / 1000; + int16_t *buffer = (int16_t *)malloc(samples * 2); + if (!buffer) return; + + int period = 44100 / freq; + int volume = 2000; + + for (int i = 0; i < samples; i++) { + if ((i % period) < (period / 2)) buffer[i] = volume; + else buffer[i] = -volume; + } + + i2s_write(I2S_NUM_0, buffer, samples * 2, &bytes_written, portMAX_DELAY); + free(buffer); + + // ล้างท่อเสียง + int16_t silence[1024] = {0}; + for (int k = 0; k < 3; k++) { + i2s_write(I2S_NUM_0, silence, sizeof(silence), &bytes_written, portMAX_DELAY); + } +} + +bool triggerSound = false; + +// Callback รับข้อมูล +void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { + if (len > 0 && *incomingData == 1) { + triggerSound = true; + } +} + +void setup() { + M5.begin(true, false, true); + InitI2S(); + Serial.begin(115200); + + // 1. โชว์ MAC Address ครั้งเดียวพอ (ดูใน Serial Monitor) + WiFi.mode(WIFI_STA); + Serial.println("\n--------------------------------"); + Serial.print("MY MAC ADDRESS: "); + Serial.println(WiFi.macAddress()); + Serial.println("--------------------------------"); + + // 2. ⚠️ บังคับ Channel 1 (เพื่อให้ตรงกับ Matrix) ⚠️ + esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE); + + // 3. Test Sound + Serial.println("System Start: Testing Beep..."); + M5.dis.drawpix(0, 0xFFFFFF); + PlayBeep(2000, 100); + M5.dis.drawpix(0, 0x0000FF); + + if (esp_now_init() != ESP_OK) return; + esp_now_register_recv_cb(OnDataRecv); +} + +void loop() { + M5.update(); + + // ปุ่มกดเทสเสียง + if (M5.Btn.wasPressed()) { + Serial.println("Button Clicked!"); + triggerSound = true; + } + + // เมื่อได้รับคำสั่งจาก Matrix + if (triggerSound) { + M5.dis.drawpix(0, 0x00FF00); // สีเขียว + + Serial.println("Matrix Command -> Beep!"); + PlayBeep(1500, 100); + delay(50); + PlayBeep(2500, 200); + + triggerSound = false; + M5.dis.drawpix(0, 0x0000FF); // กลับเป็นน้ำเงิน + } +} \ No newline at end of file diff --git a/Station2/matrix/matrix.ino b/Station2/matrix/matrix.ino new file mode 100644 index 0000000..62f1c5e --- /dev/null +++ b/Station2/matrix/matrix.ino @@ -0,0 +1,117 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// --- ตั้งค่าระยะที่ต้องการ --- +const int RSSI_THRESHOLD = -45; + +// หน่วงเวลาห้ามส่งซ้ำ (2000ms = 2 วินาที) +const int COOLDOWN_TIME = 5000; + +BLEUUID targetUUID = BLEUUID("1234"); + +// MAC Address ของ Echo +uint8_t echoAddress[] = {0x90, 0x15, 0x06, 0xFD, 0xF2, 0xF8}; + +// ✅ +int checkIcon[] = { + 15, // หางสั้น (ซ้ายล่าง) + 21, // จุดกลับตัว (ล่างสุด) + 17, // เส้นเฉียงขึ้น + 13, // เส้นเฉียงขึ้น + 9 // ปลายหางยาว (ขวาบน) +}; + +BLEScan* pBLEScan; +unsigned long lastTriggerTime = 0; + +void setup() { + M5.begin(true, false, true); + delay(10); + + WiFi.mode(WIFI_STA); + if (esp_now_init() != ESP_OK) { + Serial.println("Error initializing ESP-NOW"); + return; + } + + esp_now_peer_info_t peerInfo; + memset(&peerInfo, 0, sizeof(peerInfo)); + for (int i = 0; i < 6; i++) { + peerInfo.peer_addr[i] = echoAddress[i]; + } + peerInfo.channel = 0; + peerInfo.encrypt = false; + + if (esp_now_add_peer(&peerInfo) != ESP_OK){ + Serial.println("Failed to add peer"); + return; + } + + BLEDevice::init(""); + pBLEScan = BLEDevice::getScan(); + pBLEScan->setActiveScan(true); + pBLEScan->setInterval(100); + pBLEScan->setWindow(99); + + Serial.println("System Ready: Scanning..."); + M5.dis.fillpix(0x0000FF); // เริ่มต้น: สีน้ำเงิน +} + +void loop() { + M5.update(); + + //แก้: ใช้ Pointer (*) เพื่อแก้ Error เก่า + BLEScanResults *foundDevices = pBLEScan->start(1, false); + + bool foundTarget = false; + int targetRSSI = -999; + + for (int i = 0; i < foundDevices->getCount(); i++) { + BLEAdvertisedDevice device = foundDevices->getDevice(i); + + // เช็ค UUID + if (device.haveServiceUUID() && device.isAdvertisingService(targetUUID)) { + targetRSSI = device.getRSSI(); + Serial.printf("Target Found! RSSI: %d\n", targetRSSI); + + if (targetRSSI > RSSI_THRESHOLD) { + foundTarget = true; + } + } + } + + // --- ตัดสินใจ --- + if (foundTarget) { + + // ---ติ๊กถูก (Checkmark) --- + M5.dis.clear(); // ล้างสีเดิมก่อน + for (int i = 0; i < 5; i++) { + M5.dis.drawpix(checkIcon[i], 0x00FF00); // วาดจุดสีเขียวตามแบบแปลน + } + // ------------------------------------------- + + // เช็ค Cooldown ก่อนส่งคำสั่ง + if (millis() - lastTriggerTime > COOLDOWN_TIME) { + + Serial.println(">>> UNLOCK! Sending to Echo <<<"); + + uint8_t data = 1; + esp_now_send(echoAddress, &data, sizeof(data)); + + lastTriggerTime = millis(); + } + + } else { + // ไม่เจอ -> สีน้ำเงิน 🔵 + M5.dis.fillpix(0x0000ff); + Serial.println("Searching..."); + } + + pBLEScan->clearResults(); +} diff --git a/Station3/paper/config.h b/Station3/paper/config.h new file mode 100644 index 0000000..2e58b41 --- /dev/null +++ b/Station3/paper/config.h @@ -0,0 +1,42 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include + +// --- SoftAP Configuration (Hosted by Station 1) --- +const char* AP_SSID = "Web3_Showcase_AP"; +const char* AP_PASSWORD = "12345678"; // Open network for easy Captive Portal + +// --- IP Address Mapping --- +// Gateway (Station 1) must be x.x.x.1 +IPAddress IP_STATION1_AP(192, 168, 4, 1); // Core2: Identity & AP +IPAddress IP_STATION2_MON(192, 168, 4, 2); // Core2: Auth Monitor +IPAddress IP_RESET_MON(192, 168, 4, 3); // Core Basic: System Monitor & Reset + +IPAddress IP_STICKC(192, 168, 4, 10); // Wearable +IPAddress IP_ATOM_MATRIX(192, 168, 4, 20); // S2/S4 Sensor +IPAddress IP_ATOM_ECHO(192, 168, 4, 30); // Sound +IPAddress IP_PAPER_S3(192, 168, 4, 40); // Earn +IPAddress IP_PAPER_S4(192, 168, 4, 50); // Spend + +IPAddress NETMASK(255, 255, 255, 0); + +// --- Endpoints --- +#define ENDPOINT_HEARTBEAT "/heartbeat" +#define ENDPOINT_SET_USER "/set_user" +#define ENDPOINT_SET_AUTH "/set_auth" +#define ENDPOINT_RESET_GLOBAL "/reset_global" + +// Earn และ Spend +#define ENDPOINT_EARN_COIN "/earn_coin" +#define ENDPOINT_SPEND_COIN "/spend_coin" + +// Station 4 Order System +#define ENDPOINT_GET_ORDER "/get_order" +#define ENDPOINT_SEND_ORDER "/send_order" + +// Audio Endpoints +#define ENDPOINT_PLAY_AUTH "/play_auth" +#define ENDPOINT_PLAY_TX "/play_tx" + +#endif // CONFIG_H \ No newline at end of file diff --git a/Station3/paper/paper.ino b/Station3/paper/paper.ino new file mode 100644 index 0000000..53966a5 --- /dev/null +++ b/Station3/paper/paper.ino @@ -0,0 +1,228 @@ +#include +#include +#include +#include +#include "config.h" + +// สร้าง Canvas 2 ใบ (ใบใหญ่=เมนู, ใบเล็ก=Status) +M5EPD_Canvas canvas(&M5.EPD); +M5EPD_Canvas status_canvas(&M5.EPD); +M5EPD_Canvas choice_canvas(&M5.EPD); + +int selectedChoice = 0; +bool submitted = false; +AsyncWebServer server(80); + +struct Activity { + String name; + String coin; +}; + +Activity activities[] = { + {"Walk 1000 steps", "10"}, + {"Recycle bottle", "5"}, + {"Bike 1 km", "20"}, + {"Reuse cup", "5"} +}; + +void drawMenu() { + + canvas.createCanvas(540, 960); + + // Header + canvas.setTextSize(4); + canvas.drawString("What did you do today?", 8, 50); + + canvas.setTextSize(3); + + int yRect = 130; + int yString = 155; + for (int i = 1; i <= 4; i++) { + canvas.drawRect(0, yRect, 540, 80, 15); + String name = String(i) + ". " + activities[i-1].name; + canvas.drawString(name, 30, yString); + String coin = " --> " + activities[i-1].coin + " CCoin"; + canvas.drawString(coin, 30, yString + 30); + defaultSelectButton(i); + yRect += 100; + yString += 100; + } + + canvas.fillRect(120, 550, 300, 100, 15); + canvas.setTextColor(0, 15); + canvas.setTextSize(4); + canvas.drawString("Submit", 200, 585); + + canvas.setTextColor(15, 0); + + canvas.pushCanvas(0, 0, UPDATE_MODE_GC16); +} + +void updateStatus(String msg) { + status_canvas.createCanvas(540, 100); + status_canvas.fillCanvas(0); + status_canvas.setTextSize(3); + status_canvas.drawString(msg, 20, 20); + + status_canvas.pushCanvas(0, 700, UPDATE_MODE_DU4); +} + +void sumbitStatus(String msg1, String msg2) { + status_canvas.createCanvas(540, 100); + status_canvas.fillCanvas(0); + status_canvas.setTextSize(3); + status_canvas.drawString(msg1, 20, 20); + status_canvas.drawString(msg2, 23, 50); + + status_canvas.pushCanvas(0, 700, UPDATE_MODE_DU4); + + Serial.println("Submitted"); +} + +void selectButton(int choice) { + int ySelector = choice * 100 + 70; + canvas.fillCircle(490, ySelector, 30, 15); +} + +void defaultSelectButton(int choice) { + int ySelector = choice * 100 + 70; + canvas.fillCircle(490, ySelector, 30, 0); + canvas.drawCircle(490, ySelector, 30, 15); +} + +void handleSystemReset(AsyncWebServerRequest *request) { + Serial.println("Received reset signal from Core"); + request->send(200, "text/plain", "M5-Paper S3 reset complete."); + ESP.restart(); +} + +void pingStickC() { + WiFiClient client; + Serial.print("Pinging StickC ("); + Serial.print(IP_STICKC); + Serial.print(":80)... "); + + if (client.connect(IP_STICKC, 80)) { + Serial.println("OK"); + client.stop(); + } else { + Serial.println("Failed"); + } +} + +bool sendReceiveCoin(String coin_value) { + HTTPClient http; + String url = "http://" + IP_STICKC.toString() + ENDPOINT_EARN_COIN; + Serial.println("Sending " + coin_value + " to " + url); + http.begin(url); + http.addHeader("Content-Type", "application/x-www-form-urlencoded"); + + String postData = "amount=" + coin_value; + + int code = http.POST(postData); + String response = http.getString(); + http.end(); + if (code == 200) { + Serial.println("Sent OK"); + return true; + } + Serial.println(String(code) + " Error: " + response); + return false; +} + +void setup() { + M5.begin(); + M5.EPD.SetRotation(90); // Landscape + Serial.begin(115200); + + // กำหนด Static IP + WiFi.config(IP_PAPER_S3, IP_STATION1_AP, IPAddress(255, 255, 255, 0)); + WiFi.begin(AP_SSID, AP_PASSWORD); + + Serial.println("Connecting to AP..."); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.print("\nConnected to AP. IP: "); + Serial.println(WiFi.localIP()); + + // Check connection to StickC + pingStickC(); + + // ตั้งค่า Server Endpoints + server.on(ENDPOINT_RESET_GLOBAL, HTTP_POST, handleSystemReset); + server.on(ENDPOINT_HEARTBEAT, HTTP_GET, [] (AsyncWebServerRequest *r) { + r->send(200, "text/plain", "OK"); + }); + + server.begin(); + + drawMenu(); +} + +void loop() { + M5.update(); + + if (M5.TP.available()) { + if (!M5.TP.isFingerUp()) { + M5.TP.update(); + + // อ่านค่า X (0-540) และ Y (0-960) + int x = M5.TP.readFingerX(0); + int y = M5.TP.readFingerY(0); + + // Serial.printf("X: %d, Y: %d\n", x, y); + if (!submitted) { + if (selectedChoice != 1 && x >= 130 && x <= 229) { + if (selectedChoice > 0) { + defaultSelectButton(selectedChoice); + } + selectedChoice = 1; + selectButton(selectedChoice); + canvas.pushCanvas(0, 0, UPDATE_MODE_DU4); + } + else if (selectedChoice != 2 && x >= 230 && x <= 329) { + if (selectedChoice > 0) { + defaultSelectButton(selectedChoice); + } + selectedChoice = 2; + selectButton(selectedChoice); + canvas.pushCanvas(0, 0, UPDATE_MODE_DU4); + } + else if (selectedChoice != 3 && x >= 330 && x <= 410) { + if (selectedChoice > 0) { + defaultSelectButton(selectedChoice); + } + selectedChoice = 3; + selectButton(selectedChoice); + canvas.pushCanvas(0, 0, UPDATE_MODE_DU4); + } + else if (selectedChoice != 4 && x >= 430 && x <= 510) { + if (selectedChoice > 0) { + defaultSelectButton(selectedChoice); + } + selectedChoice = 4; + selectButton(selectedChoice); + canvas.pushCanvas(0, 0, UPDATE_MODE_DU4); + } + // ปุ่ม Submit (เช็ค X ให้อยู่ในกรอบ 120-420) + else if (x >= 550 && x <= 650 && y >= 120 && y <= 420) { // 120 <= y <= 420 && 550 <= x <= 650 + if (selectedChoice > 0) { + updateStatus("Submitting..."); + String type = "--> " + activities[selectedChoice-1].name; + String coin = "You'll receive: " + activities[selectedChoice-1].coin + " CCoin"; + submitted = sendReceiveCoin(activities[selectedChoice-1].coin); + + if (submitted) { + sumbitStatus(type, coin); + } else { + updateStatus("Error sending coin."); + } + } + } + } + delay(100); + } + } +} \ No newline at end of file diff --git a/Station4/echo/echo.ino b/Station4/echo/echo.ino new file mode 100644 index 0000000..78fb7d4 --- /dev/null +++ b/Station4/echo/echo.ino @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include + + +// --- 1. CONFIGURATION: MAC ADDRESSES --- +uint8_t matrixMAC[] = {0x4C, 0x75, 0x25, 0xAC, 0xBE, 0x18}; + +// --- 3. SOUND SEQUENCER VARIABLES --- +const int shortBeepDuration = 200; +const int longBeepDuration = 700; +const int beepFreq = 1600; +const int pauseDuration = 100; + +enum SoundState { + IDLE, + BEEP1_START, BEEP1_WAIT, BEEP1_PAUSE, + BEEP3_START, BEEP3_WAIT, + DONE, + RESET_WAIT +}; + +SoundState currentSoundState = IDLE; +unsigned long stateChangeTime = 0; + +//ฟังก์ชันทำงานเมื่อได้รับข้อมูลกลับมาจากPaper +void OnDataRecv(const esp_now_recv_info_t * info, const uint8_t *incomingData, int len) { + Serial.print("receive from matrix\nReceived: "); + Serial.print(incomingData[0]); + Serial.println(); + + if (incomingData[0] == 45) { + Serial.println("Receive reset trigger from Core."); + ESP.restart(); + } + Serial.printf("Receive Trigger event from matrix"); + + // Trigger the sound sequence upon receiving data + currentSoundState = BEEP1_START; +} + +void addPeer(uint8_t *macAddr) { + esp_now_peer_info_t peerInfo; + memset(&peerInfo, 0, sizeof(peerInfo)); + memcpy(peerInfo.peer_addr, macAddr, 6); + peerInfo.channel = 1; + peerInfo.encrypt = false; + esp_now_add_peer(&peerInfo); +} + +// ------------------------------------------------------ +// SETUP +// ------------------------------------------------------ +void setup() { + auto cfg = M5.config(); + + // --- 1. ตั้งค่าพื้นฐาน --- + cfg.serial_baudrate = 115200; + + // สำคัญมาก: Atom Echo ต้องเปิด output_power เพื่อจ่ายไฟให้ลำโพง (GPIO21/25) + cfg.output_power = true; + + // *** ลบบรรทัด cfg.external_speaker.atomic_echo = true; ออกครับ *** + // ให้ M5Unified ตรวจสอบ Board อัตโนมัติ (มันฉลาดพอจะรู้ว่าเป็น Atom Echo) + + M5.begin(cfg); + + // --- 2. ตั้งค่าเสียง --- + M5.Speaker.begin(); // สั่งเริ่มระบบเสียงให้ชัวร์ + M5.Speaker.setVolume(200); + + // --- 3. TEST SOUND (ทดสอบทันทีที่เปิดเครื่อง) --- + // ถ้าบรรทัดนี้ไม่ดัง แสดงว่าฮาร์ดแวร์มีปัญหา หรือไฟไม่พอ + Serial.println("Testing Speaker..."); + M5.Speaker.tone(1000, 500); + delay(1000); // รอฟังเสียง + M5.Speaker.tone(2000, 500); + + WiFi.mode(WIFI_STA); + esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE); + + Serial.println("\n--- M5Atom Echo Started ---"); + Serial.print("MAC: "); Serial.println(WiFi.macAddress()); + + if (esp_now_init() != ESP_OK) { + Serial.println("Error initializing ESP-NOW"); + ESP.restart(); + } + + esp_now_register_recv_cb(OnDataRecv); + + auto addPeer = [](const uint8_t* addr) { + esp_now_peer_info_t peerInfo = {}; + memcpy(peerInfo.peer_addr, addr, 6); + peerInfo.channel = 1; + peerInfo.encrypt = false; + if (esp_now_add_peer(&peerInfo) != ESP_OK) { + Serial.println("Failed to add peer"); + } + }; + + addPeer(matrixMAC); +} + +// ------------------------------------------------------ +// MAIN LOOP +// ------------------------------------------------------ +void loop() { + M5.update(); + unsigned long currentTime = millis(); + + if (currentSoundState != IDLE) { + switch (currentSoundState) { + + // --- เสียงที่ 1 (สั้น) --- + case BEEP1_START: + M5.Speaker.tone(beepFreq); // สั่งดังค้างไว้เลย ไม่ต้องใส่ duration + stateChangeTime = currentTime; + currentSoundState = BEEP1_WAIT; + break; + + case BEEP1_WAIT: + if (currentTime - stateChangeTime >= shortBeepDuration) { + M5.Speaker.stop(); // *** สั่งหยุดเสียงเองเมื่อครบเวลา *** + stateChangeTime = currentTime; + currentSoundState = BEEP1_PAUSE; + } + break; + + case BEEP1_PAUSE: + if (currentTime - stateChangeTime >= pauseDuration) { + currentSoundState = BEEP3_START; + } + break; + + // --- เสียงที่ 3 (ยาว) --- + case BEEP3_START: + M5.Speaker.tone(beepFreq); // สั่งดัง + stateChangeTime = currentTime; + currentSoundState = BEEP3_WAIT; + break; + + case BEEP3_WAIT: + if (currentTime - stateChangeTime >= longBeepDuration) { + M5.Speaker.stop(); // *** สั่งหยุด *** + currentSoundState = DONE; + } + break; + + case DONE: + stateChangeTime = currentTime; + currentSoundState = RESET_WAIT; + break; + + case RESET_WAIT: + if (currentTime - stateChangeTime >= 2000) { + M5.Display.fillScreen(0x0000FF); + currentSoundState = IDLE; + Serial.println(">> READY <<"); + } + break; + } + } + + // Manual Trigger + if (M5.BtnA.wasPressed() && currentSoundState == IDLE) { + Serial.println("Manual Button Trigger!"); + currentSoundState = BEEP1_START; + } +} \ No newline at end of file diff --git a/Station4/matrix/config.h b/Station4/matrix/config.h new file mode 100644 index 0000000..821a002 --- /dev/null +++ b/Station4/matrix/config.h @@ -0,0 +1,43 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include + +// --- SoftAP Configuration (Hosted by Station 1) --- +const char* AP_SSID = "Web3_Showcase_AP"; +const char* AP_PASSWORD = "12345678"; // Open network for easy Captive Portal + +// --- IP Address Mapping --- +// Gateway (Station 1) must be x.x.x.1 +IPAddress IP_STATION1_AP(192, 168, 4, 1); // Core2: Identity & AP +IPAddress IP_STATION2_MON(192, 168, 4, 2); // Core2: Auth Monitor +IPAddress IP_RESET_MON(192, 168, 4, 3); // Core Basic: System Monitor & Reset + +IPAddress IP_STICKC(192, 168, 4, 10); // Wearable +IPAddress IP_ATOM_MATRIX(192, 168, 4, 20); // S2 Sensor +IPAddress IP_ATOM_MATRIX_S4(192, 168, 4, 25); // S4 Sensor +IPAddress IP_ATOM_ECHO(192, 168, 4, 30); // Sound +IPAddress IP_PAPER_S3(192, 168, 4, 40); // Earn +IPAddress IP_PAPER_S4(192, 168, 4, 50); // Spend + +IPAddress NETMASK(255, 255, 255, 0); + +// --- Endpoints --- +#define ENDPOINT_HEARTBEAT "/heartbeat" +#define ENDPOINT_SET_USER "/set_user" +#define ENDPOINT_SET_AUTH "/set_auth" +#define ENDPOINT_RESET_GLOBAL "/reset_global" + +// Earn และ Spend +#define ENDPOINT_EARN_COIN "/earn_coin" +#define ENDPOINT_SPEND_COIN "/spend_coin" + +// Station 4 Order System +#define ENDPOINT_GET_ORDER "/get_order" +#define ENDPOINT_SEND_ORDER "/send_order" + +// Audio Endpoints +#define ENDPOINT_PLAY_AUTH "/play_auth" +#define ENDPOINT_PLAY_TX "/play_tx" + +#endif // CONFIG_H \ No newline at end of file diff --git a/Station4/matrix/matrix.ino b/Station4/matrix/matrix.ino new file mode 100644 index 0000000..dda1944 --- /dev/null +++ b/Station4/matrix/matrix.ino @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include +#include "config.h" + +//ใส่ MAC Address +uint8_t paperAddress[] = {0x08, 0xF9, 0xE0, 0xF6, 0x23, 0x58}; // M5Paper +uint8_t stickc1Address[] = {0x00, 0x4B, 0x12, 0xC4, 0x2D, 0xF8}; +uint8_t stickc2Address[] = {0x00, 0x4b, 0x12, 0xC4, 0x35, 0x48}; +uint8_t echoAddress[] = {0x90, 0x15, 0x06, 0xFA, 0xE7, 0x70}; // Echo + +int standbyIcon[] = { 0, 1, 2, 3, 4, 5, 9, 10, 12, 14, 15, 19, 20, 21, 22, 23, 24 }; + +// ไอคอนติ๊กถูก +int checkIcon[] = { 19, 18, 17, 16, 15 }; + +bool sending = false; + +typedef struct struct_message { + char msg[32]; +} struct_message; + +struct_message outgoing; + +void showStandbyPattern() { + M5.dis.clear(); + for (int i = 0; i < 17; i++) M5.dis.drawpix(standbyIcon[i], 0x0000FF); +} + +void showSuccessPattern() { + M5.dis.clear(); + for (int i = 0; i < 5; i++) M5.dis.drawpix(checkIcon[i], 0x00FF00); //สีเขียว +} + +void showProcessingPattern() { + M5.dis.clear(); + M5.dis.fillpix(0xFFFF00); //สีเหลืองทั้งจอ คือ กำลังส่ง +} + +void showFailedPattern() { + M5.dis.clear(); + M5.dis.fillpix(0xFF0000); //สีแดงทั้งจอ คือ ไม่ส่งได้ +} + +void addPeer(uint8_t *macAddr) { + esp_now_peer_info_t peerInfo; + memset(&peerInfo, 0, sizeof(peerInfo)); + memcpy(peerInfo.peer_addr, macAddr, 6); + peerInfo.channel = WiFi.channel(); + peerInfo.encrypt = false; + esp_now_add_peer(&peerInfo); +} + + +void requestOrder() { + const String api_url = "http://" + IP_PAPER_S4.toString() + ENDPOINT_GET_ORDER; + + HTTPClient http; + Serial.println("[HTTP] Begin request to: " + api_url); + + http.begin(api_url); + + int httpCode = http.GET(); + + if (httpCode == 200) { + showSuccessPattern(); + delay(2000); + showStandbyPattern(); + } else { + showFailedPattern(); + delay(2000); + showStandbyPattern(); + } + + http.end(); + + sending = false; +} + +//ฟังก์ชันทำงานเมื่อได้รับข้อมูลกลับมาจากPaper +void OnDataRecv(const esp_now_recv_info_t * info, const uint8_t *incomingData, int len) { + + // Print first received byte and match from list + // 45 = -1 choice + // 48 = 0 choice + // 49 = 1st choice + // 50 = 2nd choice + // 51 = 3rd choice + // 52 = 4th choice + + Serial.print("Received: "); + Serial.print(incomingData[0]); + Serial.println(); + + if (incomingData[0] == 45) { + Serial.println("Received reset trigger from Core"); + String msg = String(-1); + strcpy(outgoing.msg, msg.c_str()); + esp_now_send(echoAddress, (uint8_t *)&outgoing, sizeof(outgoing)); + ESP.restart(); + } +} + +void setup() { + M5.begin(true, false, true); + delay(10); + + + // กำหนด Static IP + WiFi.mode(WIFI_STA); + + WiFi.config(IP_ATOM_MATRIX_S4, IP_STATION1_AP, IPAddress(255, 255, 255, 0)); + WiFi.begin(AP_SSID, AP_PASSWORD); + + Serial.println("Connecting to AP..."); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.print("\nConnected to AP. IP: "); + Serial.println(WiFi.localIP()); + + if (esp_now_init() != ESP_OK) return; + + addPeer(paperAddress); + addPeer(stickc1Address); + addPeer(stickc2Address); + addPeer(echoAddress); + + esp_now_register_recv_cb(OnDataRecv); + + Serial.println("System Ready"); + showStandbyPattern(); //สีน้ำเงิน +} + +void loop() { + M5.update(); + if (!sending && M5.Btn.wasPressed()) { + + //แสดงสีเหลือง ส่งข้อมูลไปถาม Paper + showProcessingPattern(); + + sending = true; + requestOrder(); + } + delay(100); +} \ No newline at end of file diff --git a/Station4/paper/config.h b/Station4/paper/config.h new file mode 100644 index 0000000..2e58b41 --- /dev/null +++ b/Station4/paper/config.h @@ -0,0 +1,42 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#include + +// --- SoftAP Configuration (Hosted by Station 1) --- +const char* AP_SSID = "Web3_Showcase_AP"; +const char* AP_PASSWORD = "12345678"; // Open network for easy Captive Portal + +// --- IP Address Mapping --- +// Gateway (Station 1) must be x.x.x.1 +IPAddress IP_STATION1_AP(192, 168, 4, 1); // Core2: Identity & AP +IPAddress IP_STATION2_MON(192, 168, 4, 2); // Core2: Auth Monitor +IPAddress IP_RESET_MON(192, 168, 4, 3); // Core Basic: System Monitor & Reset + +IPAddress IP_STICKC(192, 168, 4, 10); // Wearable +IPAddress IP_ATOM_MATRIX(192, 168, 4, 20); // S2/S4 Sensor +IPAddress IP_ATOM_ECHO(192, 168, 4, 30); // Sound +IPAddress IP_PAPER_S3(192, 168, 4, 40); // Earn +IPAddress IP_PAPER_S4(192, 168, 4, 50); // Spend + +IPAddress NETMASK(255, 255, 255, 0); + +// --- Endpoints --- +#define ENDPOINT_HEARTBEAT "/heartbeat" +#define ENDPOINT_SET_USER "/set_user" +#define ENDPOINT_SET_AUTH "/set_auth" +#define ENDPOINT_RESET_GLOBAL "/reset_global" + +// Earn และ Spend +#define ENDPOINT_EARN_COIN "/earn_coin" +#define ENDPOINT_SPEND_COIN "/spend_coin" + +// Station 4 Order System +#define ENDPOINT_GET_ORDER "/get_order" +#define ENDPOINT_SEND_ORDER "/send_order" + +// Audio Endpoints +#define ENDPOINT_PLAY_AUTH "/play_auth" +#define ENDPOINT_PLAY_TX "/play_tx" + +#endif // CONFIG_H \ No newline at end of file diff --git a/Station4/paper/paper.ino b/Station4/paper/paper.ino new file mode 100644 index 0000000..1479ed5 --- /dev/null +++ b/Station4/paper/paper.ino @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include +#include +#include "config.h" + +// สร้าง Canvas 2 ใบ (ใบใหญ่=เมนู, ใบเล็ก=Status) +M5EPD_Canvas canvas(&M5.EPD); +M5EPD_Canvas status_canvas(&M5.EPD); + +uint8_t atomMAC[] = {0x4C, 0x75, 0x25, 0xAC, 0xBE, 0x18}; +uint8_t stickc1MAC[] = {0x00, 0x4B, 0x12, 0xC4, 0x2D, 0xF8}; +uint8_t stickc2MAC[] = {0x00, 0x4b, 0x12, 0xC4, 0x35, 0x48}; +uint8_t echoMAC[] = {0x90, 0x15, 0x06, 0xFA, 0xE7, 0x70}; + +typedef struct struct_message { + char msg[32]; +} struct_message; + +struct_message outgoing; + +int selectedChoice = 0; +bool submitted = false; +AsyncWebServer server(80); + +struct Activity { + String name; +}; + +Activity activities[] = { + {"Coffee.......2 CCoin"}, + {"Croissant....3 CCoin"}, + {"Lunch Set....5 CCoin"}, + {"Tea..........2 CCoin"} +}; + +// Send number of order to atom matrix +void sendCommand(uint8_t *macAddr, const char *cmd) { + strcpy(outgoing.msg, cmd); + esp_now_send(macAddr, (uint8_t *)&outgoing, sizeof(outgoing)); + Serial.print("Sent: "); + Serial.println(cmd); +} + +void drawMenu() { + + canvas.createCanvas(540, 960); + + canvas.fillCanvas(0); + + // Header + canvas.setTextSize(4); + canvas.drawString("CAMT WEB3 CAFE", 100, 50); + + canvas.setTextSize(3); + + int yRect = 130; + int yString = 155; + for (int i = 1; i <= 4; i++) { + canvas.drawRect(0, yRect, 540, 80, 15); + String name = String(i) + ". " + activities[i-1].name; + canvas.drawString(name, 30, yString); + defaultSelectButton(i); + yRect += 100; + yString += 100; + } + + canvas.setTextColor(15, 0); + + canvas.pushCanvas(0, 0, UPDATE_MODE_GC16); +} + +void selectButton(int choice) { + int ySelector = choice * 100 + 70; + canvas.fillCircle(490, ySelector, 30, 15); +} + +void defaultSelectButton(int choice) { + int ySelector = choice * 100 + 70; + canvas.fillCircle(490, ySelector, 30, 0); + canvas.drawCircle(490, ySelector, 30, 15); +} + +void handleSystemReset(AsyncWebServerRequest *request) { + String msg = String(-1); + sendCommand(atomMAC, msg.c_str()); + sendCommand(echoMAC, msg.c_str()); + Serial.println("Received reset signal from Core"); + request->send(200, "text/plain", "M5-Paper S4 reset complete."); + ESP.restart(); +} + +void addPeer(uint8_t *macAddr) { + esp_now_peer_info_t peerInfo; + memset(&peerInfo, 0, sizeof(peerInfo)); + memcpy(peerInfo.peer_addr, macAddr, 6); + peerInfo.channel = WiFi.channel(); + peerInfo.encrypt = false; + esp_now_add_peer(&peerInfo); +} + +void setup() { + M5.begin(); + M5.EPD.SetRotation(90); // Landscape + Serial.begin(115200); + + // กำหนด Static IP + WiFi.config(IP_PAPER_S4, IP_STATION1_AP, IPAddress(255, 255, 255, 0)); + WiFi.setTxPower(WIFI_POWER_19_5dBm); // Recommended to set power before connect + esp_wifi_set_channel(1, WIFI_SECOND_CHAN_NONE); // Force channel 1 + WiFi.begin(AP_SSID, AP_PASSWORD); + + Serial.println("Connecting to AP..."); + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + } + Serial.print("\nConnected to AP. IP: "); + Serial.println(WiFi.localIP()); + + // Init ESP-NOW + if (esp_now_init() != ESP_OK) { + Serial.println("Error initializing ESP-NOW"); + return; + } + + addPeer(atomMAC); + addPeer(stickc1MAC); + addPeer(stickc2MAC); + addPeer(echoMAC); + + // ตั้งค่า Server Endpoints + server.on(ENDPOINT_RESET_GLOBAL, HTTP_POST, handleSystemReset); + server.on(ENDPOINT_GET_ORDER, HTTP_GET, [] (AsyncWebServerRequest *r) { + if (selectedChoice == 0) { + r->send(201, "text/plain", "Choice not selected"); + return; + } + String msg = ""; + if (selectedChoice == 0) { + msg = "48"; + } else if (selectedChoice == 1) { + msg = "49"; + } else if (selectedChoice == 2) { + msg = "50"; + } else if (selectedChoice == 3) { + msg = "51"; + } else if (selectedChoice == 4) { + msg = "52"; + } + sendCommand(echoMAC, msg.c_str()); + sendCommand(stickc1MAC, msg.c_str()); + sendCommand(stickc2MAC, msg.c_str()); + + r->send(200, "text/plain", "OK"); + }); + server.on(ENDPOINT_HEARTBEAT, HTTP_GET, [] (AsyncWebServerRequest *r) { + r->send(200, "text/plain", "OK"); + }); + + server.begin(); + + drawMenu(); +} + +void touchAction() { + if (M5.TP.available()) { + if (!M5.TP.isFingerUp()) { + M5.TP.update(); + + // อ่านค่า X (0-540) และ Y (0-960) + int x = M5.TP.readFingerX(0); + int y = M5.TP.readFingerY(0); + // Serial.printf("X: %d, Y: %d\n", x, y); + if (!submitted) { + if (selectedChoice != 1 && x >= 130 && x <= 229) { + if (selectedChoice > 0) { + defaultSelectButton(selectedChoice); + } + selectedChoice = 1; + selectButton(selectedChoice); + canvas.pushCanvas(0, 0, UPDATE_MODE_DU4); + } + else if (selectedChoice != 2 && x >= 230 && x <= 329) { + if (selectedChoice > 0) { + defaultSelectButton(selectedChoice); + } + selectedChoice = 2; + selectButton(selectedChoice); + canvas.pushCanvas(0, 0, UPDATE_MODE_DU4); + } + else if (selectedChoice != 3 && x >= 330 && x <= 410) { + if (selectedChoice > 0) { + defaultSelectButton(selectedChoice); + } + selectedChoice = 3; + selectButton(selectedChoice); + canvas.pushCanvas(0, 0, UPDATE_MODE_DU4); + } + else if (selectedChoice != 4 && x >= 430 && x <= 510) { + if (selectedChoice > 0) { + defaultSelectButton(selectedChoice); + } + selectedChoice = 4; + selectButton(selectedChoice); + canvas.pushCanvas(0, 0, UPDATE_MODE_DU4); + } + } + } + } +} + +void loop() { + M5.update(); + touchAction(); + delay(100); +} \ No newline at end of file diff --git a/Station5/README.md b/Station5/README.md new file mode 100644 index 0000000..980202d --- /dev/null +++ b/Station5/README.md @@ -0,0 +1 @@ +Reset all stations. \ No newline at end of file