ESP8266兩個控制板之間的Client-Server Wi-Fi通信

了解如何在兩個ESP8266 NodeMCU控制板之間建立Wi-Fi通信(HTTP)來交換數據而無需連接到互聯網(不需要路由器)。

您需要將一個ESP8266設置為接入點(Server),將另一個ESP8266設置為STA(Client)。然後,服務器和客戶端將通過HTTP請求交換數據(傳感器讀數)。我們將使用Arduino IDE對ESP8266開發板進行編程

ESP8266兩板之間的客戶端-服務器Wi-Fi通訊

在此示例中,我們將BME280傳感器的讀數從一塊板發送到另一塊板。接收器將在OLED顯示屏上顯示讀數

如果您擁有ESP32開發板,則可以閱讀本專用指南:ESP32客戶端-服務器Wi-Fi通信

  • ESP8266 Server建立自己的無線網絡(ESP8266 Soft-Access Point)。因此,其他Wi-Fi設備可以連接到該網絡(SSID: ESP8266-AP,密碼: 123456789)。
  • ESP8266客戶端設置為工作站(station)。因此,它可以連接到ESP8266 Server無線網絡。
  • 客戶端可以向Server發出HTTP GET請求,以請求感測器數據或任何其他信息。它只需要使用Server的IP地址就可以在特定路由上發出請求:/溫度, /濕度 及 /壓力
  • Server偵聽傳入的請求,並使用讀數發送適當的回應。
  • 客戶端接收讀數並將其顯示在OLED顯示器上。

例如,ESP8266客戶端通過在Server IP地址上進行請求,然後向Server請求溫度,濕度和壓力。 /temperature, /humidity 和 /pressure,分別為(HTTP GET)。

ESP8266 Server正在偵聽這些路由,並在發出請求後通過HTTP回應發送業應的感測器讀數。

所需零件 

對於本教程,您需要以下部分:

  • 2個ESP8266開發板
  • BME280感測器
  • I2C SSD1306 OLED顯示屏
  • 跳線
  • 背板

安裝庫 

對於本教程,您需要安裝以下庫:

Asynchronous Web Server庫

我們將使用以下庫來處理HTTP請求:

這些庫無法通過程式庫管理員安裝。因此,您需要解壓縮庫並將其移動到Arduino IDE安裝庫文件夾。

或者,您可以轉到 “草稿碼”  >“ 匯入程式庫”  >“ 加入.ZIP程式庫…”, 然後選擇剛下載的庫。

您可能還會喜歡:具有ESP8266的DHT11 / DHT22異步Web服務器

BME280庫

可以通過Arduino Library Manager安裝以下庫。轉到 草稿碼 匯入程式庫管理程式庫, 然後搜索庫名稱。

您可能還會喜歡:帶ESP8266的BME280指南

I2C SSD1306 OLED庫

要與OLED顯示器連接,您需要以下庫。這些可以通過Arduino Library Manager安裝。轉到草稿碼>匯入程式庫>管理程式庫,然後搜索庫名稱。

您可能還會喜歡:帶有ESP8266的SSD1306 OLED顯示屏完整指南

 #1 ESP8266 Server(AP) 

ESP8266 Server是一個接入點(AP),用於偵聽 /temperature 、 /humidity 和 /pressure 網址。當收到對這些URL的請求時,它將發送最新的BME280感測器讀數。

為了進行測試,我們使用的是BME280感測器,但是您可以通過修改幾行程式碼(例如:DHT11 / DHT22DS18B20)來使用任何其他感測器。

原理圖,示意圖

如下圖所示,將ESP8266連接到BME280感測器

ESP8266 BME280接線圖

BME280ESP8266
VIN / VCC3.3伏
地線地線
SCLGPIO 5 (D1)
SDAGPIO 4 (D2)

#1 ESP8266 Server的Arduino草稿碼

將以下程式碼上傳到您的開發板上。

/*
  Terry Lee
  完整說明請參閱 http://honeststore.com.tw/blog/esp8266-wi-fi/
*/

// 載入必須的程式庫
#include <ESP8266WiFi.h> 
#include "ESPAsyncWebServer.h"

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h> // 設定您的開發板AP區網SSID及密碼 const char* ssid = "ESP8266-AP"; const char* password = "123456789"; /*#include #define BME_SCK 18 #define BME_MISO 19 #define BME_MOSI 23 #define BME_CS 5*/ Adafruit_BME280 bme; // I2C //Adafruit_BME280 bme(BME_CS); // hardware SPI //Adafruit_BME280 bme(BME_CS, BME_MOSI, BME_MISO, BME_SCK); // software SPI // 建立異步連線Server物件端口80 AsyncWebServer server(80); String readTemp() { return String(bme.readTemperature()); //return String(1.8 * bme.readTemperature() + 32); } String readHumi() { return String(bme.readHumidity()); } String readPres() { return String(bme.readPressure() / 100.0F); } void setup(){ // Serial port for debugging purposes Serial.begin(115200); Serial.println(); // Setting the ESP as an access point Serial.print("Setting AP (Access Point)…"); // Remove the password parameter, if you want the AP (Access Point) to be open WiFi.softAP(ssid, password); IPAddress IP = WiFi.softAPIP(); Serial.print("AP IP address: "); Serial.println(IP); server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/plain", readTemp().c_str()); }); server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/plain", readHumi().c_str()); }); server.on("/pressure", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/plain", readPres().c_str()); }); bool status; // 預設的設定 // (you can also pass in a Wire library object like &Wire2) status = bme.begin(0x76); if (!status) { Serial.println("Could not find a valid BME280 sensor, check wiring!"); while (1); } // Start server server.begin(); } void loop(){ }

程式碼如何工作

首先須載入必要的程式庫。包括ESP8266WiFi.h 庫和 ESPAsyncWebServer.h 庫來處理傳入的HTTP請求。

#include <ESP8266WiFi.h> 
#include "ESPAsyncWebServer.h"

以及包含以下程式庫以與BME280感測器接口。

#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

在下面變數中,定義您的AP網路憑據:

const char* ssid = "ESP8266-AP";
const char* password = "123456789";

我們將SSID設置為 ESP8266-AP,您也可以使用其他任何名稱。您也可以更改密碼。預設情況下,將其設置為123456789

為BME280感測器建立一個instance  bme。

Adafruit_BME280 bme;

在端口80上建立一個異步Web Server。

AsyncWebServer server(80);

然後,建立三個函數,這些函數以String變數的形式返回溫度,濕度和壓力。

String readTemp() {
  return String(bme.readTemperature());
  //return String(1.8 * bme.readTemperature() + 32);
}

String readHumi() {
  return String(bme.readHumidity());
}

String readPres() {
  return String(bme.readPressure() / 100.0F);
}

在 setup()裡,先初始化序列監視器以進行演示。

Serial.begin(115200);

使用先前定義的SSID名稱和密碼將ESP8266設置為AP。

WiFi.softAP(ssid, password);

然後處理ESP8266監聽傳入請求的路由。

例如,當ESP8266 Server收到請求時, /temperature URL,它發送由 readTemp() 用作字符(這就是為什麼我們使用 c_str() 方法。

server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/plain", readTemp().c_str());
});

同樣的情況,也會發生在當ESP  /humidity 和 /pressure 網址收到請求時。

server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/plain", readHumi().c_str());
});
server.on("/pressure", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/plain", readPres().c_str());
});

以下幾行初始化BME280感測器。

bool status;

// default settings
// (you can also pass in a Wire library object like &Wire2)
status = bme.begin(0x76);
if (!status) {
  Serial.println("Could not find a valid BME280 sensor, check wiring!");
  while (1);
}

最後,啟動服務器。

server.begin();

由於這是異步Web Server,因此本程式碼不會使用到 loop()函數

測試ESP8266 Server

將程式碼上傳到開發板上,然後打開序列監視器。您應該獲得如下信息:

這代表AP已成功設置。

現在,要確保它正在監聽溫度,濕度和壓力請求,您需要連接到它的網絡。

在您的智能手機中,轉到Wi-Fi設置並連接到ESP8266-AP。密碼是123456789

連接到AP後,打開瀏覽器並輸入 192.168.4.1/temperature

您應該在瀏覽器中獲取溫度值:

嘗試使用此URL路徑獲取濕度 192.168.4.1/humidity

最後,前往 192.168.4.1/pressure 網址:

如果您獲得有效的讀數,則表示一切正常。現在,您需要準備另一個ESP8266開發板(客戶端)來為您提出這些請求,並將其顯示在OLED顯示器上。

#2 ESP8266客戶端(Station) 

ESP8266 Client是連接到ESP8266 Server的Wi-Fi站點。客戶端通過在Server上發出HTTP GET請求,從Server請求溫度,濕度和壓力。/temperature, /humidity和 /pressure URL路由。然後,它將讀數顯示在OLED顯示器上。

原理圖,示意圖

如下圖所示,將OLED顯示器連接到ESP8266板上。

ESP32 OLED顯示屏接線示意圖

PinESP8266
Vin3.3V
GNDGND
SCLGPIO 5 (D1)
SDAGPIO 4 (D2)

適用於#2 ESP8266 Client的Arduino Sketch草稿碼

將以下程式碼上傳到ESP8266(客戶端)開發板:

/*
  Terry Lee
  完整說明請參閱 http://honeststore.com.tw/
*/

#include <ESP8266WiFi.h> 
#include <ESP8266HTTPClient.h> 
#include <WiFiClient.h> 

#include <ESP8266WiFiMulti.h> 
ESP8266WiFiMulti WiFiMulti;

const char* ssid = "ESP8266-AP";
const char* password = "123456789";

//您的IP地址或帶URL路徑的域名
const char* serverNameTemp = "http://192.168.4.1/temperature";
const char* serverNameHumi = "http://192.168.4.1/humidity";
const char* serverNamePres = "http://192.168.4.1/pressure";

#include <Wire.h> 
#include <Adafruit_GFX.h> 
#include <Adafruit_SSD1306.h> 

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

String temperature;
String humidity;
String pressure;

unsigned long previousMillis = 0;
const long interval = 5000; 

void setup() {
  Serial.begin(115200);
  Serial.println();
  
  // Address 0x3C for 128x64, you might need to change this value (use an I2C scanner)
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  display.clearDisplay();
  display.setTextColor(WHITE);
 
  WiFi.mode(WIFI_STA);
  WiFiMulti.addAP(ssid, password);
  while((WiFiMulti.run() == WL_CONNECTED)) { 
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("Connected to WiFi");
}

void loop() {
  unsigned long currentMillis = millis();
  
  if(currentMillis - previousMillis >= interval) {
     // Check WiFi connection status
    if ((WiFiMulti.run() == WL_CONNECTED)) {
      temperature = httpGETRequest(serverNameTemp);
      humidity = httpGETRequest(serverNameHumi);
      pressure = httpGETRequest(serverNamePres);
      Serial.println("Temperature: " + temperature + " *C - Humidity: " + humidity + " % - Pressure: " + pressure + " hPa");
      
      display.clearDisplay();
      
      // display temperature
      display.setTextSize(2);
      display.setCursor(0,0);
      display.print("T: ");
      display.print(temperature);
      display.print(" ");
      display.setTextSize(1);
      display.cp437(true);
      display.write(248);
      display.setTextSize(2);
      display.print("C");
      
      // display humidity
      display.setTextSize(2);
      display.setCursor(0, 25);
      display.print("H: ");
      display.print(humidity);
      display.print(" %"); 
      
      // display pressure
      display.setTextSize(2);
      display.setCursor(0, 50);
      display.print("P:");
      display.print(pressure);
      display.setTextSize(1);
      display.setCursor(110, 56);
      display.print("hPa");
           
      display.display();
      
      // save the last HTTP GET Request
      previousMillis = currentMillis;
    }
    else {
      Serial.println("WiFi Disconnected");
    }
  }
}

String httpGETRequest(const char* serverName) {
  WiFiClient client;
  HTTPClient http;
    
  // Your IP address with path or Domain name with URL path 
  http.begin(client, serverName);
  
  // Send HTTP POST request
  int httpResponseCode = http.GET();
  
  String payload = "--"; 
  
  if (httpResponseCode>0) {
    Serial.print("HTTP Response code: ");
    Serial.println(httpResponseCode);
    payload = http.getString();
  }
  else {
    Serial.print("Error code: ");
    Serial.println(httpResponseCode);
  }
  // Free resources
  http.end();

  return payload;
}

程式碼如何工作

包括用於Wi-Fi連接和發出HTTP請求所需的庫:

#include <ESP8266WiFi.h> 
#include <ESP8266HTTPClient.h> 
#include <WiFiClient.h> 

#include <ESP8266WiFiMulti.h> 

您需要建立一個 WiFiMulti instance。

ESP8266WiFiMulti WiFiMulti;

插入ESP8266 Server網路證書。如果您已更改ESP8266 Server中的預設網路憑據,則應在此處進行更改以匹配。

const char* ssid = "ESP8266-AP";
const char* password = "123456789";

然後,保存客戶端將在其中發出HTTP請求的URL。ESP8266 Server具有192.168.4.1 IP地址,我們將加上/temperature, /humidity 和 /pressure 網址。

const char* serverNameTemp = "http://192.168.4.1/temperature";
const char* serverNameHumi = "http://192.168.4.1/humidity";
const char* serverNamePres = "http://192.168.4.1/pressure";

載入OLED顯示器接口的庫:

#include <Wire.h> 
#include <Adafruit_GFX.h> 
#include <Adafruit_SSD1306.h> 

設置OLED顯示尺寸:

#define SCREEN_WIDTH 128  // OLED display width, in pixels
#define SCREEN_HEIGHT 64  // OLED display height, in pixels

建立一個 display 具有您之前定義的大小並具有I2C通信協議的物件。

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

初始化將保存Server檢索的溫度,濕度和壓力讀數的字符串變數。

String temperature;
String humidity;
String pressure;

設置每個請求之間的時間間隔。預設情況下,它設置為5秒,但是您可以將其更改為任何其他時間間隔。

const long interval = 5000; 

在setup()裡,初始化OLED顯示:

// Address 0x3C for 128x64, you might need to change this value (use an I2C scanner)
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
  Serial.println(F("SSD1306 allocation failed"));
  for(;;); // Don't proceed, loop forever
}
display.clearDisplay();
display.setTextColor(WHITE);

注意:如果您的OLED顯示器無法正常工作,請使用I2C掃描儀草圖檢查其I2C地址,並相應地更改程式碼。

將ESP8266客戶端連接到ESP8266 Server網路。

WiFi.mode(WIFI_STA);
WiFiMulti.addAP(ssid, password);
while((WiFiMulti.run() == WL_CONNECTED)) { 
  delay(500);
  Serial.print(".");
}
Serial.println("");
Serial.println("Connected to WiFi");

在 loop()是我們發出HTTP GET請求的地方。我們建立了一個名為httpGETRequest() 接受我們要發出String(字串)請求的URL路徑作為參數。

您可以在項目中使用下列函數來簡化程式碼:

String httpGETRequest(const char* serverName) {
  WiFiClient client;
  HTTPClient http;
    
  // Your IP address with path or Domain name with URL path 
  http.begin(client, serverName);
  
  // Send HTTP POST request
  int httpResponseCode = http.GET();
  
  String payload = "--"; 
  
  if (httpResponseCode>0) {
    Serial.print("HTTP Response code: ");
    Serial.println(httpResponseCode);
    payload = http.getString();
  }
  else {
    Serial.print("Error code: ");
    Serial.println(httpResponseCode);
  }
  // Free resources
  http.end();

  return payload;
}

我們使用該功能從Server獲取溫度,濕度和壓力讀數。

temperature = httpGETRequest(serverNameTemp);
humidity = httpGETRequest(serverNameHumi);
pressure = httpGETRequest(serverNamePres);

在序列監視器中印出這些讀數以進行測試。

Serial.println("Temperature: " + temperature + " *C - Humidity: " + humidity + " % - Pressure: " + pressure + " hPa");

然後,在OLED顯示器中顯示溫度:

display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0,0);
display.print("T: ");
display.print(temperature);
display.print(" ");
display.setTextSize(1);
display.cp437(true);
display.write(248);
display.setTextSize(2);
display.print("C");

濕度:

display.setTextSize(2);
display.setCursor(0, 25);
display.print("H: ");
display.print(humidity);
display.print(" %"); 

最後,壓力讀數:

display.setTextSize(2);
display.setCursor(0, 50);
display.print("P:");
display.print(pressure);
display.setTextSize(1);
display.setCursor(110, 56);
display.print("hPa");

display.display();

我們使用計時器而不是延遲來每x秒發送一個請求。這就是為什麼我們有previousMillis、 currentMillis 變數並使用 millis()功能。我們有一篇文章介紹了計時器和延遲之間的差異,您可能會發現這很有用(或閱讀ESP8266 Timers)。

將草稿碼上傳到#2 ESP8266(客戶端)以測試一切是否正常。

測試ESP8266客戶端

將兩塊板都關閉並加電後,您會看到ESP#2每隔5秒從ESP#1接收一次新的溫度,濕度和壓力讀數。

這是您應該在ESP8266 Client Serial Monitor上看到的內容。

感測器讀數也顯示在OLED中。

就是這樣!兩個ESP8266開發板彼此通信就是這樣溝通。

總結 

在本教程中,我們向您展示了如何使用HTTP請求將數據從一個ESP8266開發板發送到另一塊。如果您需要在兩個或更多主板之間建立無線通信,並且附近沒有路由器,那麼該項目將非常有用。

出於演示目的,我們展示了如何發送BME280感測器讀數,但是您可以使用任何其他感測器或發送任何其他數據。其他推薦的傳感器:

  • ESP8266 DHT11或DHT22(指南)
  • ESP8266 DS18B20(指南)
  • DHT11,DHT22,DS18B20,BME280

我們為ESP32提供了一個類似的教程,您可能會覺得有用:

  • 兩板之間的ESP32客戶端-服務器Wi-Fi通信

希望您喜歡本教程。我們正在準備更多類似的教程。因此,請繼續關注並訂閱我們的博客!

謝謝閱讀。

發佈留言