前言

建立一个异步ESP32 Web Server实时接收来自串口的数据,并在ESP32托管网页上实时绘制曲线,Web客户端可发送指令至ESP32。

先前准备

1.ESP32开发板+USB供电线

2.开发环境Arduino+VScode+PlatformIO

开发环境在此不在赘述,google上有相关教程,在此附一个

3.需安装的库

ESPAsyncWebServer 和 AsyncTCP 库。

使用异步Web Server优势:

1.可以处理多个连接

2.当发送请求同时,可以处理其他请求连接

3.处理请求模板更简单

代码架构

主要文件在data文件夹和main.cpp内

data文件夹主要是网页代码,main.cpp为我们所编程的功能。

ESP32 MTTQ发送数据Onenet平台 esp32实时传图_c++

网页文件

<!-- 文档类型声明 -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- 头部信息 -->
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <!-- 页面标题 -->
    <title>远程幅频特性曲线</title>
    <!-- 引入样式表 -->
    <link rel="stylesheet" href="./style.css">
    <!-- 引入 jQuery 库 -->
    <script src="./jq.js"></script>
    <!-- 引入 Chart.js 库 -->
    <script src="./chart.js"></script>
  </head>

  <body>
    <!-- 画布 -->
    <div>
      <canvas id="myChart"></canvas>
    </div>

    <!-- 按钮控件1 -->
    <div class="auto-scan">
      <!-- 文字描述 -->
      <div class="info">
        <p>自动扫频</p>
      </div>
      <!-- 按钮控件1-蓝色 -->
      <div class="toggle-btn blue">
        <!-- 复选框 -->
        <input type="checkbox" id="auto_scan_btn" />
        <!-- 开关标记 -->
        <div class="toggle__indicator" for="auto_scan_btn"></div>
      </div>
    </div>

    <!-- 按钮控件2 -->
    <div class="scales_type">
      <!-- 文字描述 -->
      <div class="info">
        <p>纵坐标刻度类型(对数/线性)</p>
      </div>
      <!-- 按钮控件2-绿色 -->
      <div class="toggle-btn green">
        <!-- 复选框 -->
        <input type="checkbox" id="scales_type_btn"/>
        <!-- 开关标记 -->
        <div class="toggle__indicator" for="scales_type_btn"></div>
      </div>
    </div>

  </body>

  <script>
    // 21个点
    const labels = [
      '100', '10k', '20k', '30k', '40k',
      '50k', '60k', '70k', '80k', '90k',
      '100k', '110k', '120k', '130k', '140k',
      '150k', '160k', '170k', '180k', '190k',
      '200k'
    ];

    const data = {
      labels: labels,
      datasets: [{
        label: 'TestData',
        // data: [66, 73, 97, 69, 51, 57, 28, 69, 36, 91, 
        //       67, 99, 88, 45, 73, 25, 85, 31, 22, 45,
        //       53],
        data: [],
        // 关闭点的下方面积填充
        fill: false,
        // 背景色和线色
        backgroundColor: 'rgb(255, 99, 132,0.5)',
        borderColor: 'rgb(255, 99, 132)',
        // 关闭在数据集的其他点上绘制数据集的活动点
        drawActiveElementsOnTop: false,
        // 张力系数
        tension: 0.5
      }]
    };

    const config = {
      type: 'line', //折线图
      data: data,   //数据来源

      options: {
        // x\y坐标轴设置
        scales: {
          x: {
            title: {
              display: true, //始终保持显示
              text: "频率/Hz"
            }
          },
          y: {
            type: 'logarithmic', //对数坐标轴
            title: {
              display: true,
              text: '增益/dB'
            }
          }
        },
    
        /* 数据改变时的速度 */
        animation: {
          duration: 300,
        },
        /* 其他配置 */
        plugins: {
          /* 标题 */
          title: {
            display: true,
            text: '远程幅频率特性曲线',
            font: {
              size: 30,
            },
          }
        }
      },
    };
  </script>

  <script>
    const host = window.location.hostname;
    const myChart = new Chart(
      document.getElementById('myChart'),
      config
    );

    setInterval(() => {
      $.ajax({
        url: `/api/update`,
        type: `GET`,
        success: (data) => {
          console.log(data)
          myChart.data.datasets[0].data = eval(data);
          myChart.update();
        }
      })
    }, 4000);

  /* 纵坐标刻度 */
  $("#scales_type_btn").click((e) => {
    if (e.target.checked) {
      myChart.config._config.options.scales.y.type = 'linear';
      myChart.update();
    } else {
      myChart.config._config.options.scales.y.type = 'logarithmic';
      myChart.update();
    }
  })

  //扫频开关
  var xhr = new XMLHttpRequest();
  $("#auto_scan_btn").click((e) => {
    if (e.target.checked) {
      xhr.open("GET","/update?output="+e.target.checked, true);
    } else {
      xhr.open("GET","/update?output="+e.target.checked, true);
    }
    xhr.send();
  })

  </script>
</html>

Arduino文件

// Import required libraries
#include "WiFi.h"
#include "ESPAsyncWebServer.h"
#include "SPIFFS.h"

//在端口80上创建AsyncWebServer对象
AsyncWebServer server(80);

void wifiConnect()
{
    const char* wifi_ssid = "dxxy16-402-1";        //SSID
    const char* wifi_password = "dxxy16402";    //密码
    // const char* wifi_ssid = "yogurt";
    // const char* wifi_password = "qwertyuiop";
    // IPAddress AP_local_ip(10,0,10,1);          //IP地址
    // IPAddress AP_gateway(10,0,10,1);           //网关地址
    // IPAddress AP_subnet(255,255,255,0);       //子网掩码
    // const char* AP_ssid = "Yogurt_AP";         //AP SSID
    // const char* AP_password = "12345678";     //AP wifi密码

    //创建AP/STA模式共存
    // WiFi.mode(WIFI_AP_STA);
    // WiFi.softAPConfig(AP_local_ip, AP_gateway, AP_subnet);
    // WiFi.softAP(AP_ssid, AP_password);
    WiFi.begin(wifi_ssid, wifi_password);         //连接WIFI
    Serial.print("Connected");
    //循环,直到连接成功
    while(WiFi.status() != WL_CONNECTED){
    Serial.print(".");
    delay(500);
    }
    Serial.println();
    IPAddress local_IP = WiFi.localIP();
    Serial.print("WIFI is connected,The local IP address is "); //连接成功提示
    Serial.println(WiFi.localIP());                             //输出本地IP地址
}

//读取串口数据
char recvNum[100];
String readUartData()
{
    unsigned short i = Serial1.available(); //获取串口接收数据个数
    unsigned short count = i;
    unsigned short j;
    char temp;
    // char recvNum[100];

    // memset(recvNum, 0, sizeof(recvNum));    //清空我们的目标字符串存储区域
    if(i!=0)
    {
        j = 0;
        memset(recvNum, 0, sizeof(recvNum));    //清空我们的目标字符串存储区域
        Serial.print("The amount of data received by the serial port is:");
        Serial.println(Serial1.available());
        while (i--)
        {
            /* code */
            temp = Serial1.read();
            // Serial.print(temp);
            recvNum[j] = temp;
            j++;
        }
        // Serial.print("Data:");
        // Serial.print(recvNum);
    }
    else
    {
        Serial.println("No data in serial port receiving area!!!");
    }
    return String(recvNum);
}

void spiffsInit()
{
    // 初始化SPIFFS文件系统
    if(!SPIFFS.begin(true)){
        Serial.println("An Error has occurred while mounting SPIFFS");
        return;
    }
}

const char* PARAM_INPUT = "output";
void serverRequest()
{
    // 当服务器收到根"/"URL请求时,将index.html文件发送至客户端
    server.on("/", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        request->send(SPIFFS, "/index.html");
    }); 
    server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        request->send(SPIFFS, "/style.css","text/css");
    });
    server.on("/chart.js", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        request->send(SPIFFS, "/chart.js");
    });
    server.on("/jq.js", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        request->send(SPIFFS, "/jq.js");
    });

    //ESP32服务端将数据传入网页端(客户端)
    server.on("/api/update", HTTP_GET, [](AsyncWebServerRequest *request)
    {
        request->send_P(200, "text/plain", readUartData().c_str());
        // request->send_P(200, "text/plain", String("[66,73,97,69,51,57,28,69,36,91,67,99,88,45,73,25,85,31,22,45,53]").c_str());
    });

    //网页端(客户端)向ESP32服务端发送数据
    server.on("/update",HTTP_GET, [](AsyncWebServerRequest *request)
    {
        String inputMessage;
        if (request->hasParam(PARAM_INPUT)) 
        {
            inputMessage = request->getParam(PARAM_INPUT)->value();
        }
        else 
        {
            inputMessage = "No message sent";
        }
        Serial.println(inputMessage);
        request->send(200, "text/plain", "OK");
    });
}

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

    //初始化SPIFFS文件系统
    spiffsInit();

    //连接wifi
    wifiConnect();

    //web服务器收到请求并处理
    serverRequest();
    server.begin();
}
 
void loop()
{

}

实现效果

ESP32 MTTQ发送数据Onenet平台 esp32实时传图_c++_02


ESP32 MTTQ发送数据Onenet平台 esp32实时传图_html_03