#include #include #include #include // ล็อกช่องสัญญาณให้ตรงกับฝั่ง Slave (ใช้ Channel 1) const int WIFI_CHANNEL = 1; int vh_tens, vh_units, vm_tens, vm_units, vs_tens, vs_units; // ====================================================================== // ตัวแปรสำหรับจำลองเวลา // ====================================================================== int sim_hour = 0; // ตั้งชั่วโมงเริ่มต้น (0-23) int sim_min = 0; // ตั้งนาทีเริ่มต้น (0-59) int sim_sec = 0; // ตั้งวินาทีเริ่มต้น (0-59) int vpro=1,vnum=0; String inputString,vstrx; unsigned long lastTimeUpdate = 0, vmin,vmin2; String vname = "101"; // ====================================================================== // โครงสร้างข้อมูลที่ใช้ส่ง (ตรงกับฝั่งรับ 100%) // ====================================================================== typedef struct struct_message { uint8_t h_tens[7]; // ชั่วโมง หลักสิบ uint8_t h_units[7]; // ชั่วโมง หลักหน่วย uint8_t m_tens[7]; // นาที หลักสิบ uint8_t m_units[7]; // นาที หลักหน่วย uint8_t s_tens[7]; // วินาที หลักสิบ uint8_t s_units[7]; // วินาที หลักหน่วย (เปลี่ยนจาก byte เป็น uint8_t เพื่อความสม่ำเสมอ) char namex[32]; // ✅ แก้ไขจาก String เป็น char array ป้องกันบอร์ดแครช } struct_message; struct_message timeData; // Broadcast MAC Address (ส่งกระจายให้สเลฟทุกตัวรับพร้อมกัน) uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; esp_now_peer_info_t peerInfo; unsigned long lastSendTime = 0; const uint8_t datax[24][7] = { {1, 1, 1, 1, 1, 1, 0}, // 0 {0, 1, 1, 0, 0, 0, 0}, // 1 {1, 1, 0, 1, 1, 0, 1}, // 2 {1, 1, 1, 1, 0, 0, 1}, // 3 {0, 1, 1, 0, 0, 1, 1}, // 4 {1, 0,0, 0, 0, 0, 0}, // 5 {1, 1, 1, 1, 1, 1, 0}, // 0 {0, 1, 1, 0, 0, 0, 0}, // 1 {1, 1, 0, 1, 1, 0, 1}, // 2 {1, 1, 1, 1, 0, 0, 1}, // 3 {0, 1, 1, 0, 0, 1, 1}, // 4 {0, 1,0, 0, 0, 0, 0}, // 5 {1, 1, 1, 1, 1, 1, 0}, // 0 {0, 1, 1, 0, 0, 0, 0}, // 1 {1, 1, 0, 1, 1, 0, 1}, // 2 {1, 1, 1, 1, 0, 0, 1}, // 3 {0, 1, 1, 0, 0, 1, 1}, // 4 {0, 0,1, 0, 0, 0, 0}, // 5 {1, 1, 1, 1, 1, 1, 0}, // 0 {0, 1, 1, 0, 0, 0, 0}, // 1 {1, 1, 0, 1, 1, 0, 1}, // 2 {1, 1, 1, 1, 0, 0, 1}, // 3 {0, 1, 1, 0, 0, 1, 1}, // 4 {0, 0,0, 1, 0, 0, 0} // 5 }; const uint8_t datay[24][7] = { {1, 1, 1, 1, 1, 1, 0}, // 0 {0, 1, 1, 0, 0, 0, 0}, // 1 {1, 1, 0, 1, 1, 0, 1}, // 2 {1, 1, 1, 1, 0, 0, 1}, // 3 {0, 1, 1, 0, 0, 1, 1}, // 4 {1, 0, 1, 1, 0, 1, 1}, // 5 {1, 1, 1, 1, 1, 1, 0}, // 0 {0, 1, 1, 0, 0, 0, 0}, // 1 {1, 1, 0, 1, 1, 0, 1}, // 2 {1, 1, 1, 1, 0, 0, 1}, // 3 {0, 1, 1, 0, 0, 1, 1}, // 4 {1, 0, 1, 1, 0, 1, 1}, // 5 {1, 1, 1, 1, 1, 1, 0}, // 0 {0, 1, 1, 0, 0, 0, 0}, // 1 {1, 1, 0, 1, 1, 0, 1}, // 2 {1, 1, 1, 1, 0, 0, 1}, // 3 {0, 1, 1, 0, 0, 1, 1}, // 4 {1, 0, 1, 1, 0, 1, 1}, // 5 {1, 1, 1, 1, 1, 1, 0}, // 0 {0, 1, 1, 0, 0, 0, 0}, // 1 {1, 1, 0, 1, 1, 0, 1}, // 2 {1, 1, 1, 1, 0, 0, 1}, // 3 {0, 1, 1, 0, 0, 1, 1}, // 4 {1, 0, 1, 1, 0, 1, 1} // 5 }; const uint8_t digitSegments[10][7] = { {1, 1, 1, 1, 1, 1, 0}, // 0 {0, 1, 1, 0, 0, 0, 0}, // 1 {1, 1, 0, 1, 1, 0, 1}, // 2 {1, 1, 1, 1, 0, 0, 1}, // 3 {0, 1, 1, 0, 0, 1, 1}, // 4 {1, 0, 1, 1, 0, 1, 1}, // 5 {1, 0, 1, 1, 1, 1, 1}, // 6 {1, 1, 1, 0, 0, 0, 0}, // 7 {1, 1, 1, 1, 1, 1, 1}, // 8 {1, 1, 1, 1, 0, 1, 1} // 9 }; // ====================================================================== // Callback เมื่อทำการส่งข้อมูลผ่าน ESP-NOW // ====================================================================== #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) void OnDataSent(const wifi_tx_info_t *info, esp_now_send_status_t status) { #else void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { #endif // Serial.print("Send Status: "); // Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Success" : "Fail"); } void setup() { Serial.begin(115200); Serial2.begin(9600, SERIAL_8N1, 16, 17); Serial.println("Starting Master Node (Fixed Channel)..."); // 1. ตั้งค่า Wi-Fi โหมด Station WiFi.mode(WIFI_STA); // 2. บังคับล็อกช่องสัญญาณวิทยุของตัวส่งให้เป็น Channel 1 ทันที esp_wifi_set_channel(WIFI_CHANNEL, WIFI_SECOND_CHAN_NONE); // 3. เริ่มต้นระบบ ESP-NOW if (esp_now_init() != ESP_OK) { Serial.println("Error initializing ESP-NOW"); return; } // 4. ลงทะเบียนฟังก์ชันตอนส่งข้อมูล esp_now_register_send_cb((esp_now_send_cb_t)OnDataSent); // 5. ตั้งค่าโปรไฟล์ของผู้รับ (Peer) แบบ Broadcast memset(&peerInfo, 0, sizeof(peerInfo)); memcpy(peerInfo.peer_addr, broadcastAddress, 6); peerInfo.channel = WIFI_CHANNEL; peerInfo.encrypt = false; // 6. เพิ่มข้อมูลผู้รับเข้าระบบ if (esp_now_add_peer(&peerInfo) != ESP_OK){ Serial.println("Failed to add peer"); return; } // ตั้งค่าชื่อใน struct ไว้ล่วงหน้าตอนเริ่มต้น vname.toCharArray(timeData.namex, sizeof(timeData.namex)); Serial.print("Master Node Started on Channel: "); Serial.println(WIFI_CHANNEL); } void prog1() { unsigned long currentMillis = millis(); // -------------------------------------------------------------------- // ส่วนที่ 1: อัปเดตเวลาจำลองภายในบอร์ดทุกๆ 1 วินาที (1000ms) // -------------------------------------------------------------------- if (currentMillis - lastTimeUpdate >= 1000) { lastTimeUpdate += 1000; sim_sec++; if (sim_sec >= 60) { sim_sec = 0; sim_min++; if (sim_min >= 60) { sim_min = 0; sim_hour++; if (sim_hour >= 24) { sim_hour = 0; } } } // หั่นแยกตัวเลขแต่ละหลัก vh_tens = sim_hour / 10; vh_units = sim_hour % 10; vm_tens = sim_min / 10; vm_units = sim_min % 10; vs_tens = sim_sec / 10; vs_units = sim_sec % 10; // พิมพ์เวลาปัจจุบันออกทาง Serial Monitor Serial.printf("Simulated Time: %02d:%02d:%02d\n", sim_hour, sim_min, sim_sec); // ✅ แก้ไข: ใช้ memcpy ในการคัดลอกค่า Array ขนาด 7 ไบต์เข้าสู่ struct memcpy(timeData.h_tens, digitSegments[vh_tens], 7); memcpy(timeData.h_units, digitSegments[vh_units], 7); memcpy(timeData.m_tens, digitSegments[vm_tens], 7); memcpy(timeData.m_units, digitSegments[vm_units], 7); memcpy(timeData.s_tens, digitSegments[vs_tens], 7); memcpy(timeData.s_units, digitSegments[vs_units], 7); } // -------------------------------------------------------------------- // ส่วนที่ 2: สั่งส่งข้อมูลเวลาออกไปหา Slave ทุกๆ 200 มิลลิวินาที // -------------------------------------------------------------------- if (currentMillis - lastSendTime >= 200) { lastSendTime = currentMillis; // ยิงสัญญาณออกอากาศ (Broadcast) ไปที่ Channel 1 ตรงๆ esp_now_send(broadcastAddress, (uint8_t *) &timeData, sizeof(timeData)); } } void prog2() { if ( millis()-vmin>1000) { vmin=millis(); vnum=vnum+1; if (vnum>3) vnum=0; memcpy(timeData.h_tens, datax[vnum*6+0], 7); memcpy(timeData.h_units, datax[vnum*6+1], 7); memcpy(timeData.m_tens, datax[vnum*6+2], 7); memcpy(timeData.m_units, datax[vnum*6+3], 7); memcpy(timeData.s_tens, datax[vnum*6+4], 7); memcpy(timeData.s_units, datax[vnum*6+5], 7); } if ( millis()-vmin2>200) { vmin2=millis(); // ยิงสัญญาณออกอากาศ (Broadcast) ไปที่ Channel 1 ตรงๆ esp_now_send(broadcastAddress, (uint8_t *) &timeData, sizeof(timeData)); } } void loop() { if (vpro==1) prog1(); if (vpro==2) prog2(); if (Serial2.available()) serial2Event(); } void serial2Event() { while (Serial2.available()) { char inChar = (char)Serial2.read(); // ถ้าเจออักขระขึ้นบรรทัดใหม่แสดงว่าจบข้อความ if (inChar == '\n' || inChar == '\r') { if (inputString.length() > 0) { inputString.trim(); // ตัดช่องว่าง หรือ \r ที่อาจค้างอยู่ทิ้ง // แสดงข้อความที่ได้รับออกหน้าจอคอมพิวเตอร์ Serial.print("Received: "); Serial.println(inputString); if (inputString[0] == 'S' && inputString[1]=='H' ) { String vhh=inputString.substring(2, 7); Serial.print("vhh="); Serial.println(vhh); sim_hour= vhh.toInt(); } if (inputString[0] == 'S' && inputString[1]=='M' ) { String vhh=inputString.substring(2, 7); Serial.print("vmm="); Serial.println(vhh); sim_min= vhh.toInt(); } if (inputString[0] == 'S' && inputString[1]=='S' ) { String vhh=inputString.substring(2, 7); Serial.print("vss="); Serial.println(vhh); sim_sec= vhh.toInt(); } // ตรงนี้สามารถนำ vstrx ไปทำเงื่อนไข if-else ต่อได้เลย if (inputString == "AA") {vpro=1; Serial.println("--> Action: Counter reached 10!"); } if (inputString == "AB") {vpro=2; Serial.println("--> Action: Counter reached 10!"); } inputString = ""; // เคลียร์ค่าเพื่อรอรับข้อความถัดไป } } else { inputString += inChar; // สะสมตัวอักษรไว้ใน String } } }