Cách xây dựng ESP32 Mesh / ESP8266 Mesh Network để gửi dữ liệu cảm biến

Trong bài viết này, cùng tìm hiểu về xây dựng mạng lưới ESP32 Mesh + ESP8266 Mesh. Mạng lưới này cho phép nhiều thiết bị (Node) có thể giao tiếp với nhau thông qua mạng cục bộ không dây, chúng được hỗ trợ trên cả mạch ESP32 lẫn ESP8266.

ESP Mesh là gì?

ESP Mesh được hiểu là một giao thức mạng được xây dựng dựa trên WiFi, cho phép nhiều thiết bị (có thể gọi là Node) ở cách xa nhau có thể kết nối với nhau trong một mạng WLAN (còn gọi là mạng cục bộ không dây).

ESP32 Mesh có khả năng tự tổ chức và phục hồi, nghĩa là hệ thống mạng này sẽ được xây dựng và duy trì tự động.

Kiến trúc mạng WiFi truyền thống

Trong kiến trúc mạng WiFi, một Single Node (điểm truy cập – Ascess point, còn được gọi là bộ định tuyến) sẽ kết nối với tất cả các nodes khác (stations).

Kiến trúc mạng WiFi truyền thống và phạm vi phủ sóng
Kiến trúc mạng WiFi truyền thống và phạm vi phủ sóng

Trong đó, mỗi nodes có thể giao tiếp với nhau bằng điểm truy cập. Tuy nhiên, khả năng này sẽ bị giới hạn tùy vào phạm vi phủ sóng WiFi của điểm truy cập. Các nodes phải nằm trong phạm vi kết nối của điểm truy cập, nhưng với ESP Mesh thì bạn không cần phải lo lắng về vấn đề này.

Kiến trúc mạng ESP Mesh

Trong kiến trúc mạng ESP Mesh, các Node không cần kết nối với 1 nút trung tâm. Các Node này có trách nhiệm chuyển tiếp đường truyền cho nhau, giúp nhiều thiết bị có thể kết nối với nhau và trải rộng trên một khu vực địa lý rộng lớn.

Trong đó, các nodes có thể tự tổ chức và giao tiếp với nhau để đảm bảo gói tin truyền tải được gửi đến đúng node đích cuối cùng. Nếu node nào bị xóa khỏi mạng, chúng cũng có thể tự tổ chức để đảm bảo các gói sẽ được đến đúng đích như mong muốn.

Giới thiệu kiến trúc mạng ESP Mesh
Giới thiệu kiến trúc mạng ESP Mesh

Giới thiệu thư viện painlessMesh

Thư viện painlessMesh cho phép chúng ta khởi tạo một mạng lưới ESP Mesh dựa trên bảng mạch ESP32 và ESP8266 dễ dàng.

Bạn có thể cài đặt thư viện này trong trình quản lý thư viện có sẵn của Arduino, bằng cách click theo thứ tự Tools >> Manage Libraries >> Tìm tên thư viện painlessMesh và cài đặt phiên bản mới nhất.

Tuy nhiên, thư viện này sẽ cần một số phụ thuộc của những thư viện khác. Khi click chọn Install, một cửa sổ mới sẽ hiển thị và yêu cầu bạn cài đặt tất cả các phần phụ thuộc còn thiếu, bạn hãy click chọn Install all:

Cài đặt thư viện painlessMesh để xây dựng ESP Mesh
Cài đặt thư viện painlessMesh để xây dựng ESP Mesh

Nếu trên cửa sổ này không hiển thị, bạn cần tự cài các phần phụ thuộc sau một cách thủ công:

  • ArduinoJson (by bblanchon)
  • TaskScheduler
  • ESPAsyncTCP (ESP8266)
  • AsyncTCP (ESP32)

Nếu bạn đang dùng PlatformIO, bạn cần thêm các dòng code bên dưới vào file platformio.ini để thêm thư viện và thay đổi tốc độ màn hình:

1. Với ESP32:

monitor_speed = 115200
lib_deps = painlessmesh/painlessMesh @ ^1.4.5
    ArduinoJson
    arduinoUnity
    TaskScheduler
    AsyncTCP

2. Với ESP8266

monitor_speed = 115200
lib_deps = painlessmesh/painlessMesh @ ^1.4.5
    ArduinoJson
    TaskScheduler
    ESPAsyncTCP

Chương trình cơ bảnxây dựng ESP Mesh (Tin nhắn quảng bá – Broadcast messages)

Để bắt đầu với mạng ESP Mesh, chúng ta hãy cùng thử nghiệm một ví dụ cơ bản nhất: Tạo một mạng lưới ESP32 Mesh, trong đó tất cả các mạch (Node) có thể kết nối với tất cả các mạch khác.

Tổng quan

Mình đã thử nghiệm dự án này với 4 bảng mạch (2 mạch ESP32 và 2 mạch ESP8266). Bạn có thể tùy chỉnh số lượng này tùy thích, đoạn code mình chia sẻ tương thích với cả ESP32 lẫn ESP8266.

Dưới đây là hình ảnh minh họa mạng lưới ESP32 Mesh + ESP8266 Mesh mà mình tạo:

Dự án xây dựng mạng ESP32 Mesh
Dự án xây dựng mạng ESP Mesh

Chương trình lập trình

Bạn copy đoạn code sau, chỉnh sửa tin nhắn (message) để dễ xác định Node gửi tin nhắn, rồi nạp vào mạch của mình nhé, code này tương thích với cả ESP32 lẫn ESP8266:

#include "painlessMesh.h"

#define   MESH_PREFIX     "whateverYouLike"
#define   MESH_PASSWORD   "somethingSneaky"
#define   MESH_PORT       5555

Scheduler userScheduler; // to control your personal task
painlessMesh  mesh;

// User stub
void sendMessage() ; // Prototype so PlatformIO doesn't complain

Task taskSendMessage( TASK_SECOND * 1 , TASK_FOREVER, &sendMessage );

void sendMessage() {
  String msg = "Hi from node1";
  msg += mesh.getNodeId();
  mesh.sendBroadcast( msg );
  taskSendMessage.setInterval( random( TASK_SECOND * 1, TASK_SECOND * 5 ));
}

// Needed for painless library
void receivedCallback( uint32_t from, String &msg ) {
  Serial.printf("startHere: Received from %u msg=%s\n", from, msg.c_str());
}

void newConnectionCallback(uint32_t nodeId) {
    Serial.printf("--> startHere: New Connection, nodeId = %u\n", nodeId);
}

void changedConnectionCallback() {
  Serial.printf("Changed connections\n");
}

void nodeTimeAdjustedCallback(int32_t offset) {
    Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}

void setup() {
  Serial.begin(115200);

//mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
  mesh.setDebugMsgTypes( ERROR | STARTUP );  // set before init() so that you can see startup messages

  mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
  mesh.onReceive(&receivedCallback);
  mesh.onNewConnection(&newConnectionCallback);
  mesh.onChangedConnections(&changedConnectionCallback);
  mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);

  userScheduler.addTask( taskSendMessage );
  taskSendMessage.enable();
}

void loop() {
  // it will run the user scheduler as well
  mesh.update();
}

Demo

Sau khi nạp code vào mạch, với mỗi bo mạch được kết nối với máy tính, bạn hãy mở kết nối Serial của từng mạch. Bạn có thể sử dụng Serial Monitor, hoặc bạn dùng những phần mềm như PuTTY và mở nhiều cửa sổ cho tất cả các mạch trong hệ thống mạng ESP32 Mesh của bạn.

Bạn sẽ thấy tất cả các mạch sẽ nhận được tin nhắn của nhau. Ví dụ như hình dưới là các tin nhắn từ node 2, 3 và 4 mà node 1 nhận được.

Demo dự án mạng lưới ESP Mesh

Trên màn hình Serial cũng hiển thị các thông báo khi có thay đổi trên mạng lưới ESP Mesh, chẳng hạn như khi 1 mạch rời khỏi hoặc tham gia vào mạng:

Demo dự án mạng lưới ESP Mesh - Thông báo khi có mạch rời khỏi hoặc tham gia mạng
Demo dự án mạng lưới ESP Mesh – Thông báo khi có mạch rời khỏi hoặc tham gia mạng

Chương trình 2 – Đọc và gửi dữ liệu từ cảm biến bằng ESP Mesh

Trong ví dụ này, mời bạn cùng tìm hiểu cách xây dựng mạng lưới để các mạch có thể trao đổi giá trị đọc được từ cảm biến. Trong đó, mạch này sẽ nhận được giá trị mà mạch kia đo được,

Chuẩn bị

  • 4 mạch ESP (ESP32 hoặc ESP8266, hoặc cả 2 đều được)
  • 4 cảm biến nhiệt độ BME280
  • Dây Jumper
  • Breadboard

Thư viện Arduino_JSON

Trong ví dụ này, các mạch sẽ trao đổi giá trị cảm biến ở định dạng JSON. Để xử lý các biến JSON dễ hơn, chúng ta sẽ sử dụng thư viện có tên là Arduino_JSON.

Bạn có thể vào trình quản lý thư viện trong Arduino (như hướng dẫn cài thư viện phía trên) để cài đặt thư viện này.

Trong trường hợp dùng VS Code và PlatformIO, bạn cần đưa các thư viện vào file platformio.ini như sau:

1. ESP32:

monitor_speed = 115200
lib_deps = painlessmesh/painlessMesh @ ^1.4.5
    ArduinoJson
    arduinoUnity
    AsyncTCP
    TaskScheduler
    adafruit/Adafruit Unified Sensor @ ^1.1.4
    adafruit/Adafruit BME280 Library @ ^2.1.2
    arduino-libraries/Arduino_JSON @ ^0.1.0

2. ESP8266

monitor_speed = 115200
lib_deps = painlessmesh/painlessMesh @ ^1.4.5
    ArduinoJson
    TaskScheduler
    ESPAsyncTCP
    adafruit/Adafruit Unified Sensor @ ^1.1.4
    adafruit/Adafruit BME280 Library @ ^2.1.2
    arduino-libraries/Arduino_JSON @ ^0.1.0

Kết nối phần cứng

Kết nối cảm biến BME280 vào các chân I2C của mạch ESP32 / EPS8266 như sơ đồ:

Kết nối phần cứng làm dự án ESP Mesh - ESP32
Kết nối phần cứng làm dự án ESP Mesh - ESP8266

Chương trình mẫu

Đoạn code sau đây sẽ giúp mạch đọc giá trị nhiệt độ, độ ẩm và áp suất từ cảm biến, sau đó gửi tất cả các thông tin này tới những mạch khác thông qua mạng lưới ESP32 Mesh. Các kết quả này được gửi dưới dạng chuỗi JSON và có chứa số node để xác định nó được gửi từ mạch nào:

#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "painlessMesh.h"
#include <Arduino_JSON.h>

// MESH Details
#define   MESH_PREFIX     "RNTMESH" //name for your MESH
#define   MESH_PASSWORD   "MESHpassword" //password for your MESH
#define   MESH_PORT       5555 //default port

//BME object on the default I2C pins
Adafruit_BME280 bme;

//Number for this node
int nodeNumber = 2;

//String to send to other nodes with sensor readings
String readings;

Scheduler userScheduler; // to control your personal task
painlessMesh  mesh;

// User stub
void sendMessage() ; // Prototype so PlatformIO doesn't complain
String getReadings(); // Prototype for sending sensor readings

//Create tasks: to send messages and get readings;
Task taskSendMessage(TASK_SECOND * 5 , TASK_FOREVER, &sendMessage);

String getReadings () {
  JSONVar jsonReadings;
  jsonReadings["node"] = nodeNumber;
  jsonReadings["temp"] = bme.readTemperature();
  jsonReadings["hum"] = bme.readHumidity();
  jsonReadings["pres"] = bme.readPressure()/100.0F;
  readings = JSON.stringify(jsonReadings);
  return readings;
}

void sendMessage () {
  String msg = getReadings();
  mesh.sendBroadcast(msg);
}

//Init BME280
void initBME(){
  if (!bme.begin(0x76)) {
    Serial.println("Could not find a valid BME280 sensor, check wiring!");
    while (1);
  }  
}

// Needed for painless library
void receivedCallback( uint32_t from, String &msg ) {
  Serial.printf("Received from %u msg=%s\n", from, msg.c_str());
  JSONVar myObject = JSON.parse(msg.c_str());
  int node = myObject["node"];
  double temp = myObject["temp"];
  double hum = myObject["hum"];
  double pres = myObject["pres"];
  Serial.print("Node: ");
  Serial.println(node);
  Serial.print("Temperature: ");
  Serial.print(temp);
  Serial.println(" C");
  Serial.print("Humidity: ");
  Serial.print(hum);
  Serial.println(" %");
  Serial.print("Pressure: ");
  Serial.print(pres);
  Serial.println(" hpa");
}

void newConnectionCallback(uint32_t nodeId) {
  Serial.printf("New Connection, nodeId = %u\n", nodeId);
}

void changedConnectionCallback() {
  Serial.printf("Changed connections\n");
}

void nodeTimeAdjustedCallback(int32_t offset) {
  Serial.printf("Adjusted time %u. Offset = %d\n", mesh.getNodeTime(),offset);
}

void setup() {
  Serial.begin(115200);
  
  initBME();

  //mesh.setDebugMsgTypes( ERROR | MESH_STATUS | CONNECTION | SYNC | COMMUNICATION | GENERAL | MSG_TYPES | REMOTE ); // all types on
  mesh.setDebugMsgTypes( ERROR | STARTUP );  // set before init() so that you can see startup messages

  mesh.init( MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT );
  mesh.onReceive(&receivedCallback);
  mesh.onNewConnection(&newConnectionCallback);
  mesh.onChangedConnections(&changedConnectionCallback);
  mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);

  userScheduler.addTask(taskSendMessage);
  taskSendMessage.enable();
}

void loop() {
  // it will run the user scheduler as well
  mesh.update();
}

Bạn hãy nạp code vào các mạch của mình (mỗi mạch có 1 số thứ tự node khác nhau), bạn sẽ thấy mỗi mạch đang nhận được tin nhắn từ tất cả các mạch khác thông qua ESP32 Mesh Network.

Lời kết

Trong bài viết trên, IoTZone đã hướng dẫn bạn chi tiết về mạng lưới EPS32 Mesh, với 2 dự án từ cơ bản đến nâng cao. Chúc bạn thành công! Hiện tại IoTZone có cung cấp đa dạng các mạch EPS32 để phục vụ dự án cho bạn, bạn có thể xem qua tại link sau: ESP32 LoRa Heltec.

IoTZone – Chuyên cung cấp thiết bị điện tử & tài liệu cho Makers

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *