ชุดใบงานปฏิบัติการ: การสื่อสารไร้สายด้วย ESP-NOW (ESP32)

ระดับ: เริ่มต้น

ใบงานที่ 1: การค้นหา MAC Address และส่งข้อความตัวอักษรพื้นฐาน

วัตถุประสงค์: เพื่อให้ผู้เรียนอ่านค่า MAC Address ของ ESP32 และสามารถส่งข้อความสั้นๆ ระหว่างบอร์ดได้

1. โค้ดสำหรับอ่าน MAC Address (ทำทั้ง 2 บอร์ดเพื่อเอาค่า MAC)

#include <WiFi.h>
void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  Serial.print("MAC Address: ");
  Serial.println(WiFi.macAddress());
}
void loop() {}

2. โค้ดฝั่งผู้ส่ง (Sender)

#include <esp_now.h>
#include <WiFi.h>

// ใส่ MAC Address ของบอร์ดผู้รับที่นี่
uint8_t receiverAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  
  esp_now_peer_info_t peerInfo = {};
  memcpy(peerInfo.peer_addr, receiverAddress, 6);
  peerInfo.channel = 0;  
  peerInfo.encrypt = false;
  
  if (esp_now_add_peer(&peerInfo) != ESP_OK){
    Serial.println("Failed to add peer");
    return;
  }
}

void loop() {
  char message[] = "Hello ESP-NOW!";
  esp_err_t result = esp_now_send(receiverAddress, (uint8_t *) message, sizeof(message));
  
  if (result == ESP_OK) {
    Serial.println("Sent successfully");
  } else {
    Serial.println("Error sending data");
  }
  delay(2000);
}

3. โค้ดฝั่งผู้รับ (Receiver)

#include <esp_now.h>
#include <WiFi.h>

void OnDataRecv(const esp_now_recv_info *info, const uint8_t *incomingData, int len) {
  char message[len + 1];
  memcpy(message, incomingData, len);
  message[len] = '\0';
  Serial.print("Received: ");
  Serial.println(message);
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  
  if (esp_now_init() != ESP_OK) {
    Serial.println("Error initializing ESP-NOW");
    return;
  }
  
  esp_now_register_recv_cb(OnDataRecv);
}

void loop() {}
ระดับ: ง่าย

ใบงานที่ 2: สวิตช์ไร้สายควบคุมเปิด-ปิด LED (Remote Control)

วัตถุประสงค์: เรียนรู้การส่งสถานะ Boolean จากการกดปุ่มทางฝั่ง Sender เพื่อสั่งงาน LED ที่บอร์ด Receiver

1. โค้ดฝั่งผู้ส่ง (Sender - ต่อปุ่มที่ Pin 4)

#include <esp_now.h>
#include <WiFi.h>

uint8_t receiverAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // เปลี่ยนเป็น MAC บอร์ดรับ
const int buttonPin = 4;

void setup() {
  Serial.begin(115200);
  pinMode(buttonPin, INPUT_PULLUP);
  WiFi.mode(WIFI_STA);
  esp_now_init();
  
  esp_now_peer_info_t peerInfo = {};
  memcpy(peerInfo.peer_addr, receiverAddress, 6);
  peerInfo.encrypt = false;
  esp_now_add_peer(&peerInfo);
}

void loop() {
  bool btnState = !digitalRead(buttonPin); // กด = true (HIGH)
  esp_now_send(receiverAddress, (uint8_t *) &btnState, sizeof(btnState));
  delay(100);
}

2. โค้ดฝั่งผู้รับ (Receiver - ต่อ LED ที่ Pin 2)

#include <esp_now.h>
#include <WiFi.h>

const int ledPin = 2;

void OnDataRecv(const esp_now_recv_info *info, const uint8_t *incomingData, int len) {
  bool ledState;
  memcpy(&ledState, incomingData, sizeof(ledState));
  digitalWrite(ledPin, ledState ? HIGH : LOW);
}

void setup() {
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);
  WiFi.mode(WIFI_STA);
  esp_now_init();
  esp_now_register_recv_cb(OnDataRecv);
}

void loop() {}
ระดับ: ปานกลาง

ใบงานที่ 3: ส่งข้อมูลโครงสร้างหลายค่า (Struct Data Package)

วัตถุประสงค์: เรียนรู้การแพ็กข้อมูลหลายชนิด เช่น float, int, char รวมกันใน Struct แล้วส่งผ่าน ESP-NOW (จำลองการส่งค่าเซนเซอร์)

ข้อควรระวัง: โครงสร้าง struct ทั้งฝั่งผู้ส่งและผู้รับจะต้องมีชนิดข้อมูลและลำดับตัวแปรตรงกันทุกประการ

1. โค้ดฝั่งผู้ส่ง (Sender)

#include <esp_now.h>
#include <WiFi.h>

uint8_t receiverAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

typedef struct struct_message {
  int id;
  float temp;
  float hum;
} struct_message;

struct_message myData;

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  esp_now_init();
  
  esp_now_peer_info_t peerInfo = {};
  memcpy(peerInfo.peer_addr, receiverAddress, 6);
  peerInfo.encrypt = false;
  esp_now_add_peer(&peerInfo);
}

void loop() {
  myData.id = 1;
  myData.temp = random(250, 350) / 10.0; // สุ่มค่า 25.0 - 35.0
  myData.hum = random(500, 800) / 10.0;  // สุ่มค่า 50.0 - 80.0
  
  esp_now_send(receiverAddress, (uint8_t *) &myData, sizeof(myData));
  Serial.printf("Sent -> Temp: %.1f, Hum: %.1f\n", myData.temp, myData.hum);
  delay(3000);
}

2. โค้ดฝั่งผู้รับ (Receiver)

#include <esp_now.h>
#include <WiFi.h>

typedef struct struct_message {
  int id;
  float temp;
  float hum;
} struct_message;

struct_message incomingReadings;

void OnDataRecv(const esp_now_recv_info *info, const uint8_t *incomingData, int len) {
  memcpy(&incomingReadings, incomingData, sizeof(incomingReadings));
  Serial.printf("[%d] Temp: %.1f C, Hum: %.1f %%\n", 
                incomingReadings.id, incomingReadings.temp, incomingReadings.hum);
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  esp_now_init();
  esp_now_register_recv_cb(OnDataRecv);
}

void loop() {}
ระดับ: ท้าทาย

ใบงานที่ 4: การสื่อสารแบบสองทาง (Two-Way Communication)

วัตถุประสงค์: ทำให้ ESP32 ทั้งสองบอร์ดทำหน้าที่เป็นทั้งผู้ส่งและผู้รับในตัวเอง (Transceiver) เพื่อรับ-ส่งค่าโต้ตอบกัน

โค้ดสำหรับบอร์ด A (ลงในบอร์ด A และสลับ MAC Address เป็นของบอร์ด B)

#include <esp_now.h>
#include <WiFi.h>

uint8_t peerAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // MAC ของบอร์ดตรงข้าม

typedef struct struct_message {
  char msg[32];
  int count;
} struct_message;

struct_message sendData;
struct_message recvData;

void OnDataRecv(const esp_now_recv_info *info, const uint8_t *incomingData, int len) {
  memcpy(&recvData, incomingData, sizeof(recvData));
  Serial.printf("Received: %s | Counter: %d\n", recvData.msg, recvData.count);
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  esp_now_init();
  esp_now_register_recv_cb(OnDataRecv);
  
  esp_now_peer_info_t peerInfo = {};
  memcpy(peerInfo.peer_addr, peerAddress, 6);
  peerInfo.encrypt = false;
  esp_now_add_peer(&peerInfo);
}

void loop() {
  strcpy(sendData.msg, "Ping from Board A");
  sendData.count++;
  
  esp_now_send(peerAddress, (uint8_t *) &sendData, sizeof(sendData));
  delay(2500);
}
ระดับ: สูงสุด

ใบงานที่ 5: ระบบดาวกระจาย Multi-Node (Many Slaves to One Master)

วัตถุประสงค์: สร้างระบบ Master-Slave โดยมีบอร์ดส่ง (Slave) หลายบอร์ดส่งข้อมูลหาบอร์ดรับศูนย์กลาง (Master) บอร์ดเดียว

1. โค้ดฝั่งบอร์ด Master (รับข้อมูลจากหลาย Slave)

#include <esp_now.h>
#include <WiFi.h>

typedef struct struct_message {
  int nodeID;
  int sensorValue;
} struct_message;

struct_message incomingData;

void OnDataRecv(const esp_now_recv_info *info, const uint8_t *data, int len) {
  memcpy(&incomingData, data, sizeof(incomingData));
  Serial.printf(">> Master Recv from Node [%d] -> Value: %d\n", 
                incomingData.nodeID, incomingData.sensorValue);
}

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  esp_now_init();
  esp_now_register_recv_cb(OnDataRecv);
  Serial.println("Master Ready...");
}

void loop() {}

2. โค้ดฝั่งบอร์ด Slave (กำหนด nodeID ต่างกันในแต่ละบอร์ด)

#include <esp_now.h>
#include <WiFi.h>

uint8_t masterAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; // MAC ของ Master

typedef struct struct_message {
  int nodeID;
  int sensorValue;
} struct_message;

struct_message myData;
const int BOARD_ID = 1; // เปลี่ยนเป็น 2, 3 สำหรับบอร์ดอื่นๆ

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  esp_now_init();
  
  esp_now_peer_info_t peerInfo = {};
  memcpy(peerInfo.peer_addr, masterAddress, 6);
  peerInfo.encrypt = false;
  esp_now_add_peer(&peerInfo);
}

void loop() {
  myData.nodeID = BOARD_ID;
  myData.sensorValue = analogRead(34); // อ่านค่าสร้างข้อมูลจำลอง
  
  esp_now_send(masterAddress, (uint8_t *) &myData, sizeof(myData));
  delay(2000);
}