【ESP32/ESP8266】TCP异步通讯点灯控制示例程序

【ESP32/ESP8266】TCP异步通讯点灯控制示例程序


  • ✨本示例基于Arduino框架下开发。✨
  • 🌻控制演示
    在这里插入图片描述

🍭TCP异步和同步说明

🧾同步连接,是指服务器端在与第一个访问者请求建立连接并通信时,在此期间如果有第二个访问者请求将会被阻塞。然而通过异步连接,就可以实现服务器可以同时响应多个请求。

🌻所需库说明

  • 🌿esp8266所需库:
  • 📍 ESPAsyncTCP:https://github.com/me-no-dev/ESPAsyncTCP
  • 🌿 esp32所需库:
  • 📍 AsyncTCP: https://github.com/me-no-dev/AsyncTCP
  • 🍃通用库:

-ESPAsyncWebServer: https://github.com/me-no-dev/ESPAsyncWebServer

📢将下载下来的库解压后放置到Arduino安装目录下的对应库文件夹内,例如:C:\Program Files (x86)\Arduino\libraries

📖程序代码

🎈本代码默认定义的是esp8266 GPIO引脚,如需修改为esp32只需修改对应的引脚定义即可。

//引入核心库
#ifdef ESP32
  #include <WiFi.h>
  #include <AsyncTCP.h>//https://github.com/me-no-dev/AsyncTCP
#else
  #include <ESP8266WiFi.h>
  #include <ESPAsyncTCP.h>//https://github.com/me-no-dev/ESPAsyncTCP
#endif

#include <ESPAsyncWebServer.h>//https://github.com/me-no-dev/ESPAsyncWebServer

// 填写自己的WiFi信息
const char* ssid = "***";
const char* password = "***";

const char* PARAM_INPUT_1 = "state"; //为输入状态定义字符串参数

//控制引脚定义
const int LED_OUTPUT  = 2; // GPIO 27 作为输出引脚
const int Push_Button = 14; // GPIO 26 作为输入引脚

// Variables will change:
int ledState = LOW;          // 当前输出引脚状态值
int buttonState;             // 读取当前输入引脚的状态
int lastButtonState = LOW;   // 存储输入引脚上一次的值

// 定义无符号长整型变量,因为以毫秒为单位的时间很快就会变成一个比存储在int中的时间更大的数字。
unsigned long lastDebounceTime = 0;  // 最后一次切换输出引脚时间
unsigned long debounceDelay = 50;    // 消抖时间

// 创建异步服务对象,端口号80
AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<meta charset="utf-8">
  <title>ESP32-ESP8266 Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    h2 {font-size: 3.0rem;}
    p {font-size: 3.0rem;}
    body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
    .switch {position: relative; display: inline-block; width: 120px; height: 68px} 
    .switch input {display: none}
    .slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #FF0000; border-radius: 34px}
    .slider:before {position: absolute; content: ""; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 68px}
    input:checked+.slider {background-color:  #008000}
    input:checked+.slider:before {-webkit-transform: translateX(52px); -ms-transform: translateX(52px); transform: translateX(52px)}
  </style>
</head>
<body>
  <center><h3 style="color: green;">ESP32-ESP8266网页控制界面</center><hr/>
  %BUTTONPLACEHOLDER%
<script>function toggleCheckbox(element) {
  var xhr = new XMLHttpRequest();
  if(element.checked){ xhr.open("GET", "/update?state=1", true); }
  else { xhr.open("GET", "/update?state=0", true); }
  xhr.send();
}

setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var inputChecked;
      var outputStateM;
      if( this.responseText == 1){ 
        inputChecked = true;
        outputStateM = "On";
      }
      else { 
        inputChecked = false;
        outputStateM = "Off";
      }
      document.getElementById("output").checked = inputChecked;
      document.getElementById("outputState").innerHTML = outputStateM;
    }
  };
  xhttp.open("GET", "/state", true);
  xhttp.send();
}, 1000 ) ;
</script>
</body>
</html>
)rawliteral";

// 将网页中的按钮部分替换为占位符
String processor(const String& var){
  //Serial.println(var);
  if(var == "BUTTONPLACEHOLDER"){
    String buttons ="";
    String outputStateValue = outputState();
    buttons+= "<h4 style='color: red;'>输出引脚 - GPIO 2 - 当前状态: <span id=\"outputState\"></span></h4><label class=\"switch\"><input type=\"checkbox\" οnchange=\"toggleCheckbox(this)\" id=\"output\" " + outputStateValue + "><span class=\"slider\"></span></label><br/>";
    return buttons;
  }
  return String();
}

String outputState(){
  if(digitalRead(LED_OUTPUT)){
    return "checked";
  }
  else {
    return "";
  }
  return "";
}

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);

  pinMode(LED_OUTPUT, OUTPUT);
  digitalWrite(LED_OUTPUT, LOW);
  pinMode(Push_Button, INPUT);//按键控制
  
  //连接 Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  //打印IP地址
  Serial.println(WiFi.localIP());

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });

  // Send a GET request to <ESP_IP>/update?state=<inputMessage>
  server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage;
    String inputParam;
    // GET input1 value on <ESP_IP>/update?state=<inputMessage>
    if (request->hasParam(PARAM_INPUT_1)) {
      inputMessage = request->getParam(PARAM_INPUT_1)->value();
      inputParam = PARAM_INPUT_1;
      digitalWrite(LED_OUTPUT, inputMessage.toInt());
      ledState = !ledState;
    }
    else {
      inputMessage = "No message sent";
      inputParam = "none";
    }
    Serial.println(inputMessage);
    request->send(200, "text/plain", "OK");
  });

  // 发送GET请求 <ESP_IP>/state
  server.on("/state", HTTP_GET, [] (AsyncWebServerRequest *request) {
    request->send(200, "text/plain", String(digitalRead(LED_OUTPUT)).c_str());
  });
  // Start server
  server.begin();
}
  
void loop() {
  //读取开关状态到一个局部变量:
  int reading = digitalRead(Push_Button);

//检查一下你是否按了按钮
//(即输入从LOW到HIGH),你已经等待了足够长的时间
//从上次按下按钮开始忽略任何杂音:

  // 如果开关改变,判断是否由于干扰或真的按下:开始计时
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }
//判断按键是否按下
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // 判断按键状态是否改变
    if (reading != buttonState) {
      buttonState = reading;

      // 按钮状态为高时切换LED状态
      if (buttonState == HIGH) {
        ledState = !ledState;
      }
    }
  }

  // 设置初始状态
  digitalWrite(LED_OUTPUT, ledState);

  // 保存状态:
  lastButtonState = reading;
}

🎯程序代码

//引入核心库
#ifdef ESP32
  #include <WiFi.h>
  #include <AsyncTCP.h>//https://github.com/me-no-dev/AsyncTCP
#else
  #include <ESP8266WiFi.h>
  #include <ESPAsyncTCP.h>//https://github.com/me-no-dev/ESPAsyncTCP
#endif

#include <ESPAsyncWebServer.h>//https://github.com/me-no-dev/ESPAsyncWebServer

// 填写自己的WiFi信息
const char* ssid = "***";
const char* password = "***";

const char* PARAM_INPUT_1 = "state"; //为输入状态定义字符串参数

//控制引脚定义
const int LED_OUTPUT  = 2; // GPIO 27 作为输出引脚
const int Push_Button = 14; // GPIO 26 作为输入引脚

// Variables will change:
int ledState = LOW;          // 当前输出引脚状态值
int buttonState;             // 读取当前输入引脚的状态
int lastButtonState = LOW;   // 存储输入引脚上一次的值

// 以下变量是无符号长整型变量,因为以毫秒为单位的时间很快就会变成一个比存储在int中的时间更大的数字。
unsigned long lastDebounceTime = 0;  // 最后一次切换输出引脚时间
unsigned long debounceDelay = 50;    // 消抖时间

// 创建异步服务对象,端口号80
AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
<meta charset="utf-8">
  <title>ESP32-ESP8266 Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    h2 {font-size: 3.0rem;}
    p {font-size: 3.0rem;}
    body {max-width: 600px; margin:0px auto; padding-bottom: 25px;}
    .switch {position: relative; display: inline-block; width: 120px; height: 68px} 
    .switch input {display: none}
    .slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0; background-color: #FF0000; border-radius: 34px}
    .slider:before {position: absolute; content: ""; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #fff; -webkit-transition: .4s; transition: .4s; border-radius: 68px}
    input:checked+.slider {background-color:  #008000}
    input:checked+.slider:before {-webkit-transform: translateX(52px); -ms-transform: translateX(52px); transform: translateX(52px)}
  </style>
</head>
<body>
  <center><h3 style="color: green;">ESP32-ESP8266网页控制界面</center><hr/>
  %BUTTONPLACEHOLDER%
<script>function toggleCheckbox(element) {
  var xhr = new XMLHttpRequest();
  if(element.checked){ xhr.open("GET", "/update?state=1", true); }
  else { xhr.open("GET", "/update?state=0", true); }
  xhr.send();
}

setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var inputChecked;
      var outputStateM;
      if( this.responseText == 1){ 
        inputChecked = true;
        outputStateM = "On";
      }
      else { 
        inputChecked = false;
        outputStateM = "Off";
      }
      document.getElementById("output").checked = inputChecked;
      document.getElementById("outputState").innerHTML = outputStateM;
    }
  };
  xhttp.open("GET", "/state", true);
  xhttp.send();
}, 1000 ) ;
</script>
</body>
</html>
)rawliteral";

// 将网页中的按钮部分替换为占位符
String processor(const String& var){
  //Serial.println(var);
  if(var == "BUTTONPLACEHOLDER"){
    String buttons ="";
    String outputStateValue = outputState();
    buttons+= "<h4 style='color: red;'>输出引脚 - GPIO 2 - 当前状态: <span id=\"outputState\"></span></h4><label class=\"switch\"><input type=\"checkbox\" οnchange=\"toggleCheckbox(this)\" id=\"output\" " + outputStateValue + "><span class=\"slider\"></span></label><br/>";
    return buttons;
  }
  return String();
}

String outputState(){
  if(digitalRead(LED_OUTPUT)){
    return "checked";
  }
  else {
    return "";
  }
  return "";
}

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);

  pinMode(LED_OUTPUT, OUTPUT);
  digitalWrite(LED_OUTPUT, LOW);
  pinMode(Push_Button, INPUT);//按键控制
  
  //连接 Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  //打印IP地址
  Serial.println(WiFi.localIP());

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, processor);
  });

  // Send a GET request to <ESP_IP>/update?state=<inputMessage>
  server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage;
    String inputParam;
    // GET input1 value on <ESP_IP>/update?state=<inputMessage>
    if (request->hasParam(PARAM_INPUT_1)) {
      inputMessage = request->getParam(PARAM_INPUT_1)->value();
      inputParam = PARAM_INPUT_1;
      digitalWrite(LED_OUTPUT, inputMessage.toInt());
      ledState = !ledState;
    }
    else {
      inputMessage = "No message sent";
      inputParam = "none";
    }
    Serial.println(inputMessage);
    request->send(200, "text/plain", "OK");
  });

  // 发送GET请求 <ESP_IP>/state
  server.on("/state", HTTP_GET, [] (AsyncWebServerRequest *request) {
    request->send(200, "text/plain", String(digitalRead(LED_OUTPUT)).c_str());
  });
  // Start server
  server.begin();
}
  
void loop() {
  //读取开关状态到一个局部变量:
  int reading = digitalRead(Push_Button);

//检查一下你是否按了按钮
//(即输入从LOW到HIGH),你已经等待了足够长的时间
//从上次按下按钮开始忽略任何杂音:

  // 如果开关改变,判断是否由于干扰或真的按下:开始计时
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:

    // 判断按键状态是否改变
    if (reading != buttonState) {
      buttonState = reading;

      // 按钮状态为高时切换LED状态
      if (buttonState == HIGH) {
        ledState = !ledState;
      }
    }
  }

  // 设置初始状态
  digitalWrite(LED_OUTPUT, ledState);

  // 保存状态:
  lastButtonState = reading;
}
  • 选择esp8266- nodemcu1.0编译信息
使用 1.0  版本的库 ESP8266WiFi 在文件夹: C:\Users\Administrator\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\libraries\ESP8266WiFi 
使用 1.2.2  版本的库 ESPAsyncTCP 在文件夹: C:\Users\Administrator\Documents\Arduino\libraries\ESPAsyncTCP 
使用 1.2.3  版本的库 ESPAsyncWebServer 在文件夹: C:\Users\Administrator\Documents\Arduino\libraries\ESPAsyncWebServer 
使用 1.0  版本的库 Hash 在文件夹: C:\Users\Administrator\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.0.2\libraries\Hash 
"C:\\Users\\Administrator\\AppData\\Local\\Arduino15\\packages\\esp8266\\tools\\xtensa-lx106-elf-gcc\\3.0.4-gcc10.3-1757bed/bin/xtensa-lx106-elf-size" -A "d:\\arduino\\MyHexDir/ESP_WebServer_Key.ino.elf"
项目使用了 302605 字节,占用了 (28%) 程序存储空间。最大为 1044464 字节。
全局变量使用了30248字节,(36%)的动态内存,余留51672字节局部变量。最大为81920字节。

⛳程序源码资源

链接:https://pan.baidu.com/s/1DKwllDwNbPDmhtBx2kr58g 
提取码:zp22
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
VR(Virtual Reality)即虚拟现实,是一种可以创建和体验虚拟世界的计算机技术。它利用计算机生成一种模拟环境,是一种多源信息融合的、交互式的三维动态视景和实体行为的系统仿真,使用户沉浸到该环境中。VR技术通过模拟人的视觉、听觉、触觉等感觉器官功能,使人能够沉浸在计算机生成的虚拟境界中,并能够通过语言、手势等自然的方式与之进行实时交互,创建了一种适人化的多维信息空间。 VR技术具有以下主要特点: 沉浸感:用户感到作为主角存在于模拟环境中的真实程度。理想的模拟环境应该使用户难以分辨真假,使用户全身心地投入到计算机创建的三维虚拟环境中,该环境中的一切看上去是真的,听上去是真的,动起来是真的,甚至闻起来、尝起来等一切感觉都是真的,如同在现实世界中的感觉一样。 交互性:用户对模拟环境内物体的可操作程度和从环境得到反馈的自然程度(包括实时性)。例如,用户可以用手去直接抓取模拟环境中虚拟的物体,这时手有握着东西的感觉,并可以感觉物体的重量,视野中被抓的物体也能立刻随着手的移动而移动。 构想性:也称想象性,指用户沉浸在多维信息空间中,依靠自己的感知和认知能力获取知识,发挥主观能动性,寻求解答,形成新的概念。此概念不仅是指观念上或语言上的创意,而且可以是指对某些客观存在事物的创造性设想和安排。 VR技术可以应用于各个领域,如游戏、娱乐、教育、医疗、军事、房地产、工业仿真等。随着VR技术的不断发展,它正在改变人们的生活和工作方式,为人们带来全新的体验。
ESP32ESP8266都支持ESP-NOW(ESP8285除外),可以通过ESP-NOW在它们之间进行通信。 以下是ESP32ESP8266之间使用ESP-NOW通讯的步骤: 1. 首先,需要在两个设备上都安装ESP-IDF,并且需要在代码中包含esp_now.h头文件。 2. 在ESP32设备上,需要初始化ESP-NOW: ``` esp_err_t ret = esp_now_init(); if (ret != ESP_OK) { Serial.println("Error initializing ESP-NOW"); } ``` 在ESP8266设备上,需要注册ESP-NOW: ``` if (esp_now_init() != 0) { Serial.println("Error initializing ESP-NOW"); } ``` 3. 在发送端(ESP32ESP8266)上,需要注册接收端的MAC地址: ``` uint8_t mac[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}; // 接收端的MAC地址 esp_now_peer_info_t peer; memcpy(peer.peer_addr, mac, 6); peer.channel = 0; peer.encrypt = false; if (esp_now_add_peer(&peer) != ESP_OK) { Serial.println("Error adding peer"); } ``` 4. 在接收端(ESP32ESP8266)上,需要注册发送端的MAC地址: ``` uint8_t mac[] = {0xAB, 0xCD, 0xEF, 0x12, 0x34, 0x56}; // 发送端的MAC地址 esp_now_peer_info_t peer; memcpy(peer.peer_addr, mac, 6); peer.channel = 0; peer.encrypt = false; if (esp_now_add_peer(&peer) != ESP_OK) { Serial.println("Error adding peer"); } ``` 5. 然后可以通过esp_now_send函数在两个设备之间发送数据: ``` uint8_t data[] = {0x01, 0x23, 0x45, 0x67, 0x89}; // 发送的数据 if (esp_now_send(mac, data, sizeof(data)) != ESP_OK) { Serial.println("Error sending data"); } ``` 6. 在接收端,可以通过esp_now_register_recv_cb函数注册一个回调函数,在接收到数据时调用: ``` void onDataReceived(const uint8_t *mac_addr, const uint8_t *data, int len) { Serial.print("Received data from "); Serial.print(mac_addr[0], HEX); Serial.print(":"); Serial.print(mac_addr[1], HEX); Serial.print(":"); Serial.print(mac_addr[2], HEX); Serial.print(":"); Serial.print(mac_addr[3], HEX); Serial.print(":"); Serial.print(mac_addr[4], HEX); Serial.print(":"); Serial.println(mac_addr[5], HEX); Serial.print("Data: "); for (int i = 0; i < len; i++) { Serial.print(data[i], HEX); Serial.print(" "); } Serial.println(); } if (esp_now_register_recv_cb(onDataReceived) != ESP_OK) { Serial.println("Error registering callback"); } ``` 这样,ESP32ESP8266之间就可以通过ESP-NOW进行通信了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值