了解如何在兩個ESP8266 NodeMCU控制板之間建立Wi-Fi通信(HTTP)來交換數據而無需連接到互聯網(不需要路由器)。
您需要將一個ESP8266設置為接入點(Server),將另一個ESP8266設置為STA(Client)。然後,服務器和客戶端將通過HTTP請求交換數據(傳感器讀數)。我們將使用Arduino IDE對ESP8266開發板進行編程。
在此示例中,我們將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 / DHT22或DS18B20)來使用任何其他感測器。
原理圖,示意圖
如下圖所示,將ESP8266連接到BME280感測器。
BME280 | ESP8266 |
VIN / VCC | 3.3伏 |
地線 | 地線 |
SCL | GPIO 5 (D1) |
SDA | GPIO 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板上。
Pin | ESP8266 |
Vin | 3.3V |
GND | GND |
SCL | GPIO 5 (D1) |
SDA | GPIO 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開發板彼此通信就是這樣溝通。
總結
出於演示目的,我們展示了如何發送BME280感測器讀數,但是您可以使用任何其他感測器或發送任何其他數據。其他推薦的傳感器:
- ESP8266 DHT11或DHT22(指南)
- ESP8266 DS18B20(指南)
- DHT11,DHT22,DS18B20,BME280
我們為ESP32提供了一個類似的教程,您可能會覺得有用:
- 兩板之間的ESP32客戶端-服務器Wi-Fi通信
希望您喜歡本教程。我們正在準備更多類似的教程。因此,請繼續關注並訂閱我們的博客!
謝謝閱讀。