※本文將透過ESP32上的藍芽4.2,以BLE MESH架構的資訊收發方式進行資料傳輸,本範例使用之感測器為MPU6050(三軸陀螺儀+三軸加速計感測模組),將接收到的感測器數值作為資料並以MESH架構進行接收與傳輸。

Arduino IDE

首先,要在Arduino IDE上使用ESP32 需先進行開發版的安裝,此部分本文不詳細說明。
詳情請於網路上收尋關鍵字 「Arduino ESP32 開發環境安裝」 即可找到教學。

MESH簡易介紹

MESH是建立在Wi-Fi協議之上的網路協議。MESH允許散佈在較大物理區域(室內和室外)中的眾多設備(稱為節點)在單個WLAN(無線區域網路)下互連。

同時MESH具有自我組織和自我修復的功能,這意味著該網路可以自動構建和維護。

傳統的Wi-Fi網絡架構

在傳統的Wi-Fi網路體系結構中,單個節點(訪問點–通常為路由器)連接到所有其他節點(站)。每個節點都可以使用訪問點相互通信。但是,這僅限於接入點的Wi-Fi覆蓋範圍。每個站點必須在範圍內才能直接連接到接入點。ESP-MESH不會發生這種情況。

傳統Wi-Fi網絡ESP32 ESP8266

ESP-MESH網絡架構

使用ESP-MESH,節點無需連接到中央節點。節點負責彼此中繼傳輸。這允許多個設備分佈在較大的物理區域上。節點可以自組織並彼此動態對話,以確保數據包到達其最終節點目的地。如果從網絡中刪除了任何節點,則它可以自我組織以確保數據包到達其目的地。

ESP-MESH網絡ESP32 ESP8266i

painlessMesh程式庫

painlessMesh庫使我們能夠用一個簡單的程式庫,於ESP32上建立一個MESH傳輸架構的網路。

painlessMesh函式庫所建立的是真正的自組織網路,這意味著不需要中央控制器或路由器。任何由1個或多個節點組成的系統都將自組織為功能齊全的網格。

安裝painlessMesh庫

您可以通過Arduino程式庫管理器安裝painlessMesh。點擊草稿碼>匯入程式庫>管理程式庫。即可開啟程式庫管理器。

於程式庫管理器搜尋欄位搜尋「 painlessmesh 」並安裝該庫。本文正在使用1.4.7版。

該庫還需要其他一些額外程式庫支援。應該會彈出一個新窗口,要求您安裝所有缺少的額外程式庫。
請選擇「全部安裝」。

如果未顯示此窗口或不確定是否已安裝所有額外程式庫,則需要安裝以下3項額外程式庫:

WiFi MESH基本示例(廣播消息)

接著開始使用WiFi MESH,我們首先嘗試該庫的基本範例。本範例創建一個網狀網絡,其中所有板均向所有其他板廣播消息。

我們用四個ESP32對這個範例進行實驗。您可以依自身情況增減ESP32數量。
(該範例對ESP32與ESP8266皆可兼容。)

ESP-MESH painlessMesh基本示例ESP32 ESP8266

程式碼– painlessMesh庫基本示例

將以下程式碼複製到您的Arduino IDE 即可進行上傳並察看結果。(或者點擊檔案>範例>Painless Mesh>basic

#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();
}

在上傳代碼之前,您可以設定 MESH_PREFIX (就像WiFi名稱一樣)和 MESH_PASSWORD (WiFi密碼)。

然後,我們建議您為每個ESP32更改以下欄位,以輕鬆識別發送消息的節點。
例如,對於節點1,更改消息,如下所示:

String msg = "Hi from node 1 ";

程式碼如何運作

首先載入painlessMesh程式庫。

#include "painlessMesh.h"

MESH資訊

然後,添加網格詳細資料。這MESH_PREFIX指網路的名稱。您可以將其更改為任何您喜歡的。

#define MESH_PREFIX "whateverYouLike"

這 MESH_PASSWORD,顧名思義就是網路密碼。您可以將其更改為任何您喜歡的。

#define MESH_PASSWORD "somethingSneaky"

網格中的所有節點應使用相同的 MESH_PREFIX 和 MESH_PASSWORD。

這 MESH_PORT指您要在其上運行網狀服務器的TCP端口。默認值為5555。

#define MESH_PORT 5555

排程器

建議避免使用 delay()在網狀網路代碼中。為了維護網路,需要在後台執行一些任務。使用delay() 會阻止這些任務的發生,並可能導致網路失去穩定性或崩潰。

相反,建議使用 TaskScheduler 運行您在painlessMesh本身中使用的任務。

以下行創建了一個新的 排程器 叫 userScheduler。

Scheduler userScheduler; // to control your personal task

painlessMesh

建立一個 painlessMesh 對象稱為 mesh 來處理網狀網路。

painlessMesh  mesh;

建立任務

建立一個名為 taskSendMessage 負責處理 sendMessage() 只要程序正在執行,便每秒執行一次。

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

發送訊息到網路

這 sendMessage()功能將訊息發送到訊息網路中的所有節點(廣播)。

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

該訊息包含【Hi from node 1 】,以及該ESP32的晶片ID。

String msg = "Hi from node 1";
msg += mesh.getNodeId();

要廣播訊息,只需使用 sendBroadcast() 上的方法,以網路為對象並將訊息作為參數傳遞(msg)發送。

mesh.sendBroadcast(msg);

每次發送新訊息時,代碼都會更改訊息之間的間隔(一到五秒)。

taskSendMessage.setInterval(random(TASK_SECOND * 1, TASK_SECOND * 5));

網路回調函數

接下來,建立多個回調函數,當特定事件在網路上發生時將被啟用。

這 receiveCallback() 函式負責顯示訊息發件人(from)和訊息的內容(msg.c_str())。

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

這 newConnectionCallback()每當有新節點加入網路時,該函數就會執行。目前設定此功能僅顯示新節點的晶片ID。您可以修改功能來執行任何其他任務。

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

這 changedConnectionCallback() 每當網路上的連接發生改變時(節點的加入或離開時),該函數就會執行。

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

這 nodeTimeAdjustedCallback()當網路不同步時,該功能會執行,以便所有節點都同步。並顯示偏移量。

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

setup()中程式碼

在setup()裡面初始化序列埠監控器。

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

用前面定義的WiFi資訊初始化網路。

mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);

將所有回調函數分配給它們相應的事件。

mesh.onReceive(&receivedCallback);
mesh.onNewConnection(&newConnectionCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);

最後,增加 taskSendMessage 功能 userScheduler。調度程序負責在正確的時間處理和運行任務。

userScheduler.addTask(taskSendMessage);

最後,啟用 taskSendMessage,以便程序開始將訊息發送到網路。

taskSendMessage.enable();

要保持網路運行,請增加 mesh.update()到 loop()中。

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

執行結果

將上述的程式碼上傳到所有ESP32上。不要忘記修改訊息以輕鬆識別發送的節點。

將ESP32連接到電腦後,打開每個ESP32的序列埠監控視窗。

您應該看到所有ESP32都收到彼此的訊息。例如,這些是節點1收到的訊息。它從節點2、3和4接收訊息。

ESP-MESH基本示例4板Arduino串行監視器

當網路上發生改變時,您還應該看到其他訊息:節點離開或加入網路時。

ESP MESH基本示例串行監視器更改的連接

使用ESP-MESH交換感測器讀數

在下一個範例中,我們將在4個板之間交換感測器讀數(您可以使用不同數量的板)。每個板都接收其他板的讀數。

ESP-MESH交流BME280傳感器讀數ESP32 ESP8266

例如,我們將交換來自感測器:MPU6050的讀數,不過您也可以使用任何其他感測器。

所需零件

這是此範例所需的零件:

  • 4個ESP32板
  • 4個MPU6050
  • 跳線(杜邦線)

Arduino_JSON程式庫

在此範例中,我們將以JSON格式交換感測器讀數。為了使處理JSON變量更容易,我們將使用Arduino_JSON程式庫。

您可以在Arduino IDE庫管理器中安裝該庫。只需轉到 草稿碼  > 匯入函式庫  > 管理程式庫, 然後按以下方式搜索庫名稱:

電路圖

如下圖所示,將MPU6050感測器連接到ESP32默認的I2C引腳。

程式碼– ESP-MESH廣播感測器讀數

將以下程式碼上傳到您的每個ESP32上,並修改nodeNumber欄位的變數。該程式碼讀取當前X軸、Y軸、Z軸讀數,並將其廣播到網狀網路上的所有ESP32。讀數以JSON字符串的形式發送,其中還包含用於標示發送訊息之ESP32的節點號。

#include "painlessMesh.h"
#include <Wire.h>
#include <MPU6050.h>
#include <Arduino_JSON.h>

#define   MESH_PREFIX     "whateverYouLike" //name for your MESH
#define   MESH_PASSWORD   "somethingSneaky" //password for your MESH
#define   MESH_PORT       5555  //default port

//Number for this node
int nodeNumber = 1;

//MPU6050 Pin/SDA-21,SCL-22
MPU6050 mpu;


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

Scheduler userScheduler; // to control your personal task

painlessMesh  mesh;

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

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

String getReadings () {
  JSONVar jsonReadings;
  Vector normAccel = mpu.readNormalizeAccel();
  Vector normGyro = mpu.readNormalizeGyro();
  jsonReadings["node"] = nodeNumber;
  
  jsonReadings["Accel_X"] = String(normAccel.XAxis);
  jsonReadings["Accel_Y"] = String(normAccel.YAxis);
  jsonReadings["Accel_Z"] = String(normAccel.ZAxis);
  
  jsonReadings["Gyro_X"] = String(normGyro.XAxis);
  jsonReadings["Gyro_Y"] = String(normGyro.YAxis);
  jsonReadings["Gyro_Z"] = String(normGyro.ZAxis);
  
  readings = JSON.stringify(jsonReadings);
  return readings;
}

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

// Needed for painless library
void receivedCallback( uint32_t from, String &msg ) {
  //Serial.printf("Received from %u msg=%s\n", from, msg.c_str());
  Serial.println();
  Serial.println("----------------------------------");
  Serial.printf("Received from %u ", from);
  Serial.println();
  Serial.printf("msg=%s\n",msg.c_str());
  JSONVar myObject = JSON.parse(msg.c_str());
  int node = myObject["node"];
  //三軸加速度(Accel)
  double X = myObject["Accel_X"];
  double Y = myObject["Accel_Y"];
  double Z = myObject["Accel_Z"];
  //三軸陀螺儀(Gyro)
  double GX = myObject["Gyro_X"];
  double GY = myObject["Gyro_Y"];
  double GZ = myObject["Gyro_Z"];
  
  Serial.println("**********************************");
  //三軸加速度(Accel)
  Vector normAccel = mpu.readNormalizeAccel();
  Serial.print(" Accel_X:");
  Serial.print(normAccel.XAxis);
  Serial.print(" Accel_Y:");
  Serial.print(normAccel.YAxis);
  Serial.print(" Accel_Z:");
  Serial.println(normAccel.ZAxis);
  //三軸陀螺儀(Gyro)
  Vector normGyro = mpu.readNormalizeGyro();
  Serial.print(" Gyro_X:");
  Serial.print(normGyro.XAxis);
  Serial.print(" Gyro_Y:");
  Serial.print(normGyro.YAxis);
  Serial.print(" Gyro_Z:");
  Serial.println(normGyro.ZAxis);
}

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);
}

/*******MPU6050三軸加速度(Accel)主程式******/
void MPU_Accel_set(){
  while(!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_2G))
  {
    Serial.println("Could not find a valid MPU6050 sensor, check wiring!");
    delay(500);
  }
  
  switch(mpu.getRange()){
    case MPU6050_RANGE_2G:             
      Serial.println("+/- 2 g"); 
      break;
  }
}

/*******MPU6050三軸陀螺儀(Gyro)主程式******/
void MPU_Gyro_set(){
  while(!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_2G)){
    Serial.println("Could not find a valid MPU6050 sensor, check wiring!");
    delay(500);
  }
  // Set threshold sensivty. Default 3.
  mpu.setThreshold(3);
   
  switch(mpu.getScale()){   
    case MPU6050_SCALE_250DPS:         
    Serial.println("250 dps"); 
    break;
  } 
}

/*************************************/

void setup() {
  Serial.begin(115200);
  MPU_Accel_set();
  MPU_Gyro_set();
  
//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();
  
}

程式碼如何運作

繼續閱讀本節以了解代碼如何運行。

程式庫

首先載入所需的庫: Wire 和 MPU6050用於感測器的連接;painlessMesh處理網狀網路和 Arduino_JSON 輕鬆建立和處理JSON字符串。

#include "painlessMesh.h"
#include <Wire.h>
#include <MPU6050.h>
#include <Arduino_JSON.h>

網路資訊

在以下各行中插入網格詳細信息。

#define   MESH_PREFIX     "whateverYouLike" //name for your MESH
#define   MESH_PASSWORD   "somethingSneaky" //password for your MESH
#define   MESH_PORT       5555  //default port

這 MESH_PREFIX指網路的名稱。您可以將其更改為任何您喜歡的。
這MESH_PASSWORD,就是網路密碼。您可以將其更改為任何您喜歡的。
網路中的所有節點應使用相同的MESH_PREFIX 和 MESH_PASSWORD。
這 MESH_PORT指您要在其上運行網狀服務器的TCP端口。默認值為5555。

MPU6050

建立一個 MPU6050 對象稱為 mpu 在默認的ESP32 I2C引腳上。

//MPU6050 Pin/SDA-21,SCL-22
MPU6050 mpu;

在裡面 nodeNumber變量為您的ESP32個別節點編號。每個板的編號必須不同。

//Number for this node
int nodeNumber = 1;

這 readings 變量將用於保存要發送到其他ESP32的讀數。

String readings;

排程器

以下行創建了一個新的 排程器 叫 userScheduler。

Scheduler userScheduler; // to control your personal task

painlessMesh

建立一個 painlessMesh 對象稱為 mesh 來處理網狀網路。

painlessMesh  mesh;

建立任務(tasks)

建立一個名為 taskSendMessage 負責發送 sendMessage() 只要程序正在執行,便每0.5秒鐘執行一次。
假如今天從Node2、3接收到資料,則Node1序列埠視窗顯示接收到之資料數共為4筆,(Node2、3各兩筆資料)。

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

getReadings()

這 getReadings() 函數從MPU6050感測器讀取當前X軸、Y軸、Z軸讀數,並連接所有訊息,包括稱為JSON變量的節點號 jsonReadings。

JSONVar jsonReadings;
  Vector normAccel = mpu.readNormalizeAccel();
  Vector normGyro = mpu.readNormalizeGyro();
  jsonReadings["node"] = nodeNumber;
  //三軸加速度(Accel)
  jsonReadings["Accel_X"] = String(normAccel.XAxis);
  jsonReadings["Accel_Y"] = String(normAccel.YAxis);
  jsonReadings["Accel_Z"] = String(normAccel.ZAxis);
  //三軸陀螺儀(Gyro)
  jsonReadings["Gyro_X"] = String(normGyro.XAxis);
  jsonReadings["Gyro_Y"] = String(normGyro.YAxis);
  jsonReadings["Gyro_Z"] = String(normGyro.ZAxis);

這 jsonReadings 將該變量轉換為JSON字符串 stringify()並保存在 readings 變量。

readings = JSON.stringify(jsonReadings);

然後該變量由函數回傳。

return readings;

發送消息到網路

這 sendMessage() 函數發送帶有讀數和節點號的JSON字符串(getReadings())到網路中的所有節點(廣播)。

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

網路回傳函數

接下來,創建多個回傳函數,這些事件將在網格上發生某些事件時被執行。

這 receiveCallback() 函數顯示訊息發件人(from)和訊息的內容(msg.c_str())。

void receivedCallback( uint32_t from, String &msg ) {
  Serial.println();
  Serial.println("----------------------------------");
  Serial.printf("Received from %u ", from);
  Serial.println();
  Serial.printf("msg=%s\n",msg.c_str());

該消息採用JSON格式,因此,我們可以按以下方式訪問變量:

  JSONVar myObject = JSON.parse(msg.c_str());
  int node = myObject["node"];
  //三軸加速度(Accel)
  double X = myObject["Accel_X"];
  double Y = myObject["Accel_Y"];
  double Z = myObject["Accel_Z"];
  //三軸陀螺儀(Gyro)
  double GX = myObject["Gyro_X"];
  double GY = myObject["Gyro_Y"];
  double GZ = myObject["Gyro_Z"];

最後,在串行監視器上打印所有信息。

  Serial.println("**********************************");
  //三軸加速度(Accel)
  Vector normAccel = mpu.readNormalizeAccel();
  Serial.print(" Accel_X:");
  Serial.print(normAccel.XAxis);
  Serial.print(" Accel_Y:");
  Serial.print(normAccel.YAxis);
  Serial.print(" Accel_Z:");
  Serial.println(normAccel.ZAxis);
  //三軸陀螺儀(Gyro)
  Vector normGyro = mpu.readNormalizeGyro();
  Serial.print(" Gyro_X:");
  Serial.print(normGyro.XAxis);
  Serial.print(" Gyro_Y:");
  Serial.print(normGyro.YAxis);
  Serial.print(" Gyro_Z:");
  Serial.println(normGyro.ZAxis);

這 newConnectionCallback()每當有新節點加入網路時,該函數就會執行。此功能僅顯示新節點的晶片ID。您可以修改功能以執行任何其他任務。

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

這 changedConnectionCallback() 每當網路上的連接發生改變時(節點的加入或離開時),該函數就會執行。

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

這 nodeTimeAdjustedCallback()當網路調整時間時,該功能會執行,以便所有節點都同步。並顯示偏移量。

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

初始化MPU6050感測器

這 void MPU_Accel_set( ) 、 void MPU_Gyro_set( ) 功能為初始化MPU6050感測器。

/*******MPU6050三軸加速度(Accel)主程式******/
void MPU_Accel_set(){
  while(!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_2G))
  {
    Serial.println("Could not find a valid MPU6050 sensor, check wiring!");
    delay(500);
  }
  
  switch(mpu.getRange()){
    case MPU6050_RANGE_2G:             
      Serial.println("+/- 2 g"); 
      break;
  }
}

/*******MPU6050三軸陀螺儀(Gyro)主程式******/
void MPU_Gyro_set(){
  while(!mpu.begin(MPU6050_SCALE_2000DPS, MPU6050_RANGE_2G)){
    Serial.println("Could not find a valid MPU6050 sensor, check wiring!");
    delay(500);
  }
  // Set threshold sensivty. Default 3.
  mpu.setThreshold(3);
   
  switch(mpu.getScale()){   
    case MPU6050_SCALE_250DPS:         
    Serial.println("250 dps"); 
    break;
  } 
}

setup()

在 setup()裡面初始化序列埠監控器。

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

啟用 MPU_Accel_set( ) 、 MPU_Gyro_set( ) 初始化MPU6050感測器。

  MPU_Accel_set();
  MPU_Gyro_set();

選擇所需的調試訊息類型:

//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

用前面定義的WiFi資訊初始化網路。

mesh.init(MESH_PREFIX, MESH_PASSWORD, &userScheduler, MESH_PORT);

將所有回調函數分配給它們相應的事件。

mesh.onReceive(&receivedCallback);
mesh.onNewConnection(&newConnectionCallback);
mesh.onChangedConnections(&changedConnectionCallback);
mesh.onNodeTimeAdjusted(&nodeTimeAdjustedCallback);

最後,增加 taskSendMessage 功能 userScheduler。調度程序負責在正確的時間處理和運行任務。

userScheduler.addTask(taskSendMessage);

最後,啟用 taskSendMessage,以便程序開始將訊息發送到網路。

taskSendMessage.enable();

要保持網路運行,請增加 mesh.update()到 loop()中。

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

執行結果

將代碼上傳到所有ESP32(每個ESP32具有不同的節點號)後,您應該看到每個ESP32都在接收其他ESP32的訊息。

以下截圖顯示了節點1收到的消息。它接收來自節點2的感測器讀數,並顯示自身感測器讀數。

以下截圖顯示了節點2收到的消息。它接收來自節點1的感測器讀數,並顯示自身感測器讀數。

完成

以上即為本範例所有教學內容。

※本實驗目的為運用光敏電阻感應光源控制LED開關,已達到節約能源的目的;並利用ESP32的WiFi功能結合ThingSpeak和手機App:Line,達到收集光敏電阻數據並及時通知手機App目前LED的狀態。

1.ESP32在Arduino開發環境架設

●由於在Arduino開發環境上架設ESP32之教學文章非常之多,因此這裡就不再多做贅述直接進入第二點。

Ps.請Google搜尋→ESP32環境安裝

2. 使用Arduino撰寫程式碼

※由於Youtube上有許多非常優良的Arduino教學影片,因此這裡便不多做贅述,只會附上程式碼,其餘前置動作,請各位自行上Youtube搜尋囉!

Ps.下方有ThingSpeak和Line的設定教學

以下為Arduino程式碼

※注意 程式碼內需修改的[你的APIKey]會在ThingSpeak教學時取得APIKey。

#include
#include
//請修改以下參數——————————————–
char ssid[] = “SSID”;//輸入要連接之WiFi的名稱
char password[] = “SSID Password”;//輸入要連接之WiFi的密碼
//請修改為你自己的API Key,並將https改為http

String url = “http://api.thingspeak.com/update?api_key=你的APIKey“;
int photocellPin = 39; // 光敏電阻 (photocell) 接在 anallog pin 39 (SVN)
//———————————————————
int photocellVal = 0; // photocell variable
int LED=0;
String LED_status = “OFF”;
int vv;

void setup()
{
  Serial.begin(115200);
  pinMode(LED, OUTPUT); //設定 DIO Pin 2 為 LED 輸出
  pinMode(photocellPin,INPUT);

  Serial.print(“開始連線到無線網路SSID:”);
  Serial.println(ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED)
   {
     Serial.print(“”);
     delay(1000);
   }
   Serial.println(“已連接WiFi”);
}

void loop()
{
// 讀取光敏電阻並輸出到 Serial Port
  photocellVal = analogRead(photocellPin);
  Serial.println(“”);
  Serial.print(“讀取光敏值:”);
  Serial.println(photocellVal);
  delay(100);
  vv=analogRead(photocellPin);

  if (vv<400)
  {
    digitalWrite(LED, HIGH);
    Serial.println(“LED ON”);
    LED_status=”ON”;
  }
  else
  {
    digitalWrite(LED, LOW);
    Serial.println(“LED OFF”);
    LED_status=”OFF”;
  }

   //開始傳送到thingspeak
   Serial.println(“傳送數值至網頁”);
   HTTPClient http;
   //將光敏值以http get參數方式補入網址後方
   String url1 = url + “&field1=” + photocellVal ;
   //http client取得網頁內容
   http.begin(url1);
   int httpCode = http.GET();

   if (httpCode == HTTP_CODE_OK)
    {
      //讀取網頁內容到payload
      String payload = http.getString();
      //將內容顯示出來
      Serial.print(“以上為第”);
      Serial.print(payload);
      Serial.println(“筆資料”);
    }
    else
     {
       //讀取失敗
       Serial.println(“網路傳送失敗”);
     }
   http.end();

   Serial.println(“15秒後再次偵測”);
   delay(15000);//延遲15秒
}

3.Line Notify設定教學

※若以下教學有不懂處可點此→參考連結

第一步:進入LINE Notify網站→點此進入,並點擊右上角登入。

第二步:登入Line帳號。

第三步:點擊右上角[個人頁面]。

第四步:點擊下方[發行權杖]。

第五步:輸入通知機器人姓名及選擇接收通知的聊天室。

第六步:點選下方[複製]並將此權杖妥善保存。

※此權杖離開該介面便無法再找回該權杖,務必妥善保存。

※注意

若於第五步時選擇傳訊的對象是一個群組,那麼要多一個動作:邀請LINE Notify進入群組內,否則會出現此錯誤訊息[此帳號尚未被邀請至已連動的群組]。

4.ThingSpeak創建帳號教學

※若以下教學有不懂處可點此→參考連結

第一步:創建帳號,進入ThingSpeak網站→點此進入,並點擊右上角Sign Up。

第二步:輸入E-mail及用戶名稱,輸入完畢按下方Continue。

Ps. E-mail必須完全輸入正確,其餘隨意即可。

第三步:在標示在標示1打勾,並按下方Continue。

第四步:等待並前往你的E-mail收取認證信件

第五步:找到名稱[Verify Email Address]的信件並點擊紅框處。

第六步:完成第五步便會跳轉到這個畫面,便可回到第四步的畫面點擊Continue。

第七步:輸入密碼和勾選後,點擊下方Continue。

Ps.密碼須包含數字及大小寫英文

第八步:完成ThingSpeak帳號創建,點選OK後會詢問是否用於商業用途,點選紅框選項,並點選

               OK,完成創建帳號。

5.ThingSpeak頻道設定

※若以下教學有不懂處可點此→參考連結

第一步:點選NewChannel進入創建頻道。

第二步:輸入所需資料,輸入完畢後點選最下方Save Channel。

Ps.大部分可忽略,如需增加其他資料請自行勾選。

第三步:點選API Keys。

第四步:複製紅框內APIKey,並到程式碼內的[貼入你的API Key]處貼上。

6.設定 ThingHTTP

※此部分為ThingSpeak傳送信息至Line的設定。

第一步:點擊Apps中的ThingHTTP,並點擊New ThingHTTP按鈕。

第二步:相關欄位設定(參照下表),完成設定後點選最下方Save ThingHTTP。

  • URL:https://notify-api.line.me/api/notify
  • Method:POST
  • Content Type:application/x-www-form-urlencoded
  • Header的name=Authorization
  • Header的value=Bearer 你的Token
    Bearer與Token中間要記得插入一個英文空白
  • Body部份輸入
       message=偵測目前Photocell Vaule為”%%trigger%%”。
    %%trigger%% 代表將來會被後續設定的React觸發的欄位值,稍後設定完成會顯示光敏值。

Ps.此處的Token指的是在Line Notify設定教學中的第六步所取得的權杖。

7.設定React

第一步:點選Apps中的React,並點選New React。

第二步:相關欄位設定(參照下表),完成設定後點選最下方Save React。

  • Condition Type (數值種類):選擇數值類Numeric。
  • 監控頻率(Test Frequency):選擇監控每次新增的值。
  • 監控頻道(if channel):選擇上面建立好的頻道。
  • 監控欄位(field):選擇監控哪一個欄位,本處選擇欄位1(Photocell Value)。
  •  比較方式:數值類比較大小,此處選擇「大於400」時觸發,選is Greater than。
  • 數值:輸入400
  • Action:反應方式選擇ThingHTTP,即條件達到時,要傳送一個ThinhHTTP亦即上面的LINE通知。
  • perform ThingHTTP:選擇剛剛建立好LINE通知的那個ThingHTTP。
  • Options:觸發通知的頻率,兩個選擇分別是1.條件達成後,只發一次通知,或2.每次條件達成都發通知,我們選擇1.條件達成後,只發一次通知。

8.實驗電路圖

所需元件與線材

元件

ESP32*1

LED*1

電阻*1(連接光敏電阻用)

Ps.請視LED狀況決定是否需再外加電阻。

線材

杜邦線*4

單芯線*2 (麵包板接線之常用線材)

Micro USB*1 (ESP32燒綠程式用線)

完成以上步驟後,即可上傳程式碼至ESP32,觀察實驗結果,若光敏值超過400時,LED便會關閉,手機就會收到LINE通知囉。

※因受限於使用的為ThingSpeak免費版功能,因此ThingSpeak需15秒才會做一次數值傳輸,所以於程式碼內設定為間隔15秒才會偵測一次光敏值並傳送數值至ThingSpeak再透過ThingSpeak傳送訊息至Line。

1 軟體 : Arduino ( v1.8.12 ) 會隨版本更新

2 元件及材料 :

ESP 32 、 超音波 、蜂鳴器 、 杜邦線x6、

Micro USB線x1

Arduino

教學 : https://zanrobot.com/uncategorized/3699

下載 : tps://www.arduino.cc/zh/main/software?setlang =cn

超音波蜂鳴器接線圖接

BZ(+) 接P12,
BZ ( – ) 接GND
trigPin 接 5 (GPIO5)
echoPin 接 4 (GPIO4)
Vcc 接 3.3 V

ESP 32 前置

教學 : https://zanrobot.com/uncategorized/3699

程式編碼

const int trigPin = 5;

const int echoPin = 4;

const int ledPin = 12;

const int freq = 2000;

const int ledChannel = 0;

const int resolution = 8;

long duration; int distance;

int safetyDistance;

void setup() { pinMode(trigPin, OUTPUT);

pinMode(echoPin, INPUT);

Serial.begin(115200);

ledcSetup(ledChannel, freq, resolution);

ledcAttachPin(ledPin, ledChannel);

ledcWriteTone(ledChannel, 0);

}

void loop()

{ digitalWrite(trigPin, LOW);

delayMicroseconds(2);

digitalWrite(trigPin, HIGH);

delayMicroseconds(10);

digitalWrite(trigPin, LOW);

duration = pulseIn(echoPin, HIGH);

{ distance= duration*0.034/2;

safetyDistance = distance; if (safetyDistance <= 30){ ledcWriteTone(ledChannel,0);

delay(100);

}

if (safetyDistance < 5){ ledcWriteTone(ledChannel,1730);

delay(100);

}

if (safetyDistance < 20){ ledcWriteTone(ledChannel,1046);

delay(100); }

}

Serial.print(“Distance: “);

Serial.println(distance);

}

影片

1 軟體 : Arduino ( v1.8.12 ) 會隨版本更新

2 元件及材料 :

ESP 32 板子
麵包板x1
杜邦線x4

電線x2
ledx1
220Ω 電阻x1
10k Ω 電阻x1

光敏電阻x1

Arduino

教學 : https://zanrobot.com/uncategorized/3699

下載 : tps://www.arduino.cc/zh/main/software?setlang =cn

接線圖

5V —-> +
GND —-> –
SVP —–> 光敏的端+10k
P13 —-> LED+端(腳長)

ESP 32 前置

教學 : https://zanrobot.com/uncategorized/3699

程式編碼

#include <BluetoothSerial.h>
BluetoothSerial SerialBT;//宣告藍芽物件=SerialBT

void setup() {
// put your setup code here, to run once:
Serial.begin(115200);//序列視窗
SerialBT.begin(“ESP32”);//<<藍芽名稱,請自己記得
pinMode(A0, INPUT);
pinMode(13,OUTPUT);//電燈relay
}
int pr_min = 400;
void loop() {
char analogRea=SerialBT.read();
int pr = analogRead(A0);{
Serial.println(pr);
SerialBT.println(pr);
delay(1000);
}
while(SerialBT.available())
{
char btdata=SerialBT.read();
Serial.print(btdata);//查看藍芽輸入的值
if (btdata==’3′){digitalWrite(13,HIGH);}//開電燈
if (btdata==’4′){digitalWrite(13,LOW);}//關電燈
}
delay(100);
}

手機app

先到play商店下載arduino bluetooth controller

開啟並開啟藍芽選到裝置

點擊裝置會出現此列表

按下

當程式執行成功時將出現光敏阻值
且輸入 3 Led 亮 4 Led關

執行

1 軟體 : Arduino ( v1.8.12 ) 會隨版本更新

2 元件及材料 :

ESP 32 、 伺服馬達 、 杜邦線x3、Micro USB線x1

Arduino

教學 : https://zanrobot.com/uncategorized/3699

下載 : tps://www.arduino.cc/zh/main/software?setlang =cn

伺服馬達接線圖

黃線 P16紅線5V 黑線 GND

ESP 32 前置

教學 : https://zanrobot.com/uncategorized/3699

程式編碼

#include <Servo.h>//宣告
Servo servo;//命名

int pos = 0;

void setup() {
Serial.begin(115200);//序列阜監控視窗
servo.attach(16); //橘色的線接主要是訊號線,接到 p16
}

void loop() {
if(Serial.available()){                 //
int num = Serial.parseInt();     // case 前置 num(數字鍵)

switch(num) {                            //   

case 1 :   //1~9
for(pos = 0; pos < 180; pos += 1) // 一度一度由 0 度旋轉到 180 度

servo.write(pos);
delay(15);
break;

case 2 : // 1~9
for(pos = 180; pos>=1; pos-=1) // 一度一度由 180 度旋轉到 0 度

servo.write(pos);
delay(15);
break;
}
}
}

注意!

上傳時如果出現了下方狀況

伺服ESP32網址:
https://github.com/RoboticsBrno/ServoESP32

解決過程

先到打開伺服esp32網址(上方網址)

將檔案下載

壓縮檔案

找到arduino 並打開資料夾

進入後找到libraries 並打開

將剛剛的壓縮檔放入即可

3 執行

開啟監控視窗

輸入設定的數值( 如 : 我是 case 1 就是輸入 1 )

影片

1 軟體 : Arduino ( v1.8.12 ) 會隨版本更新

2 元件與材料

ESP 32 板子
麵包板x1
杜邦線x6
ULN2003 步進馬達驅動模組
步進馬達x1

3 接上元件

線路圖

In 1p14 In 2p12 In 3p13

In 4p15 VDD5V 、 GND 接 GND

程式碼

#define A 14 //A 相線圈接到 Arduino Pin 14
#define B 12 //B 相線圈接到 Arduino Pin 12
#define A_BAR 13 // A_BAR 相線圈接到 Arduino Pin 13
#define B_BAR 15 // B_BAR 相線圈接到 Arduino Pin 15
#define rr 50 //轉速設定
#include <Stepper.h> //引入 Stepper.h 檔

Stepper stepper(200, A, A_BAR, B, B_BAR);

void setup(){
stepper.setSpeed(rr); //將馬達的速度設定成 50 RPM
}

void loop(){
stepper.step(2048); //順時針 1 圈
delay(2000);
stepper.step(-2048); //逆時針 1 圈
delay(2000);
}

 

 

 

4 執行