[MM32硬件] 【灵动微电子MM32F0121测评】9.ESP32-WEB控制

[复制链接]
 楼主| 阿源玩电子 发表于 2025-7-10 23:20 | 显示全部楼层 |阅读模式
本帖最后由 阿源玩电子 于 2025-7-12 11:02 编辑

#申请原创#
基于MM32F0121与ESP32的无线Web控制技术实现

1.项目概述

通过ESP32创建WiFi热点,搭建Web服务器,实现手机浏览器远程控制MM32F0121开发板:

  • 手机网页控制板载LED
  • 实时显示板载电位器电压值
  • 双向串口通信(ESP32 UART2 ↔ MM32F0121 UART2)


2.硬件搭建

ESP32 (UART2)         Mini_F0121-OB (UART2)     

────────────────────────────         

GPIO17 (TX2)    →      RX: PB8                     

GPIO16 (RX2)    ←        TX: PA8                     

                   GND            →      GND                                       


3.无线通信

使用Arduino开发环境,以下为ESP32部分:
  • WiFi名称:ESP32-Control
  • WiFi密码:12345678
  • IP地址:  192.168.4.1
  • 使用一颗板载蓝色LED,来显示运行状态
  • 长时间没有收到电压数据,会显示无数据字样
  • 同时能监控连接WIFI设备的数量

  1. #include <WiFi.h>
  2. #include <WebServer.h>

  3. // 热点配置
  4. const char* ap_ssid = "ESP32-Control";
  5. const char* ap_password = "12345678";
  6. const int statusLed = 2;  // ESP32 板载 LED 引脚用于状态指示

  7. // 串口配置 (UART2: RX=16, TX=17)
  8. #define SERIAL2_RX 16
  9. #define SERIAL2_TX 17
  10. HardwareSerial SerialPort(2);  // 使用 UART2

  11. WebServer server(80);

  12. // 全局变量
  13. String voltageValue = "0.00V";  // 存储电压值
  14. unsigned long lastVoltageUpdate = 0;
  15. bool externalLEDState = false;
  16. unsigned long lastBlinkTime = 0;
  17. bool ledState = LOW;

  18. void handleRoot() {
  19.   String html = R"rawliteral(
  20.     <!DOCTYPE html>
  21.     <html>
  22.     <head>
  23.       <meta charset="UTF-8">
  24.       <title>ESP32 设备控制</title>
  25.       <meta name="viewport" content="width=device-width, initial-scale=1">
  26.       <style>
  27.         body {
  28.           font-family: "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
  29.           max-width: 600px;
  30.           margin: 0 auto;
  31.           padding: 20px;
  32.           background-color: #f5f5f5;
  33.         }
  34.         .card {
  35.           background: white;
  36.           border-radius: 10px;
  37.           box-shadow: 0 4px 8px rgba(0,0,0,0.1);
  38.           padding: 20px;
  39.           margin-bottom: 20px;
  40.         }
  41.         h1 { color: #2c3e50; text-align: center; }
  42.         .btn {
  43.           display: block;
  44.           width: 100%;
  45.           padding: 15px;
  46.           margin: 10px 0;
  47.           background-color: #3498db;
  48.           color: white;
  49.           border: none;
  50.           border-radius: 5px;
  51.           font-size: 18px;
  52.           cursor: pointer;
  53.           transition: background-color 0.3s;
  54.         }
  55.         .btn-on { background-color: #27ae60; }
  56.         .btn-off { background-color: #e74c3c; }
  57.         .btn:hover { opacity: 0.9; }
  58.         .status {
  59.           padding: 15px;
  60.           text-align: center;
  61.           font-size: 20px;
  62.           margin: 15px 0;
  63.           border-radius: 5px;
  64.         }
  65.         .voltage { background-color: #f1c40f; color: #2c3e50; }
  66.         .external-led { background-color: %external_color%; }
  67.         .info {
  68.           text-align: center;
  69.           color: #7f8c8d;
  70.           margin-top: 30px;
  71.           font-size: 14px;
  72.         }
  73.       </style>
  74.       <script>
  75.         function updateVoltage() {
  76.           fetch('/voltage')
  77.             .then(response => response.text())
  78.             .then(data => {
  79.               document.getElementById("voltage-value").innerHTML = data;
  80.               setTimeout(updateVoltage, 2000);
  81.             });
  82.         }
  83.         
  84.         function controlLED(state) {
  85.           fetch('/external/' + state)
  86.             .then(response => response.text())
  87.             .then(data => {
  88.               const statusElem = document.getElementById("external-status");
  89.               statusElem.innerHTML = "状态: " + data.toUpperCase();
  90.               statusElem.className = "status external-led";
  91.               statusElem.style.backgroundColor = state === "on" ? "#27ae60" : "#e74c3c";
  92.             });
  93.         }
  94.         
  95.         window.onload = function() {
  96.           updateVoltage();
  97.         };
  98.       </script>
  99.     </head>
  100.     <body>
  101.       <div class="card">
  102.         <h1>ESP32 设备</h1>
  103.         
  104.         <div class="card">
  105.           <h2>系统状态</h2>
  106.           <div class="status" style="background-color: #3498db; color: white;">
  107.             ESP32  (正常运行)
  108.           </div>
  109.         </div>
  110.         
  111.         <div class="card">
  112.           <h2>Mini_F0121-OB</h2>
  113.           <button class="btn btn-on" onclick="controlLED('on')">打开 LED</button>
  114.           <button class="btn btn-off" onclick="controlLED('off')">关闭 LED</button>
  115.           <div id="external-status" class="status external-led">状态: OFF</div>
  116.         </div>
  117.         
  118.         <div class="card">
  119.           <h2>电压监测</h2>
  120.           <div id="voltage-value" class="status voltage">读取中...</div>
  121.         </div>
  122.       </div>
  123.       
  124.       <div class="info">
  125.         IP: 192.168.4.1 | 连接设备: %client_count%
  126.       </div>
  127.     </body>
  128.     </html>
  129.   )rawliteral";
  130.   
  131.   // 动态替换内容
  132.   html.replace("%external_color%", externalLEDState ? "#27ae60" : "#e74c3c");
  133.   html.replace("%client_count%", String(WiFi.softAPgetStationNum()));
  134.   
  135.   server.send(200, "text/html; charset=utf-8", html);
  136. }

  137. void handleExternalLED(String state) {
  138.   if (state == "on") {
  139.     SerialPort.println("LED_ON");  // 发送控制指令
  140.     externalLEDState = true;
  141.     server.send(200, "text/plain", "on");
  142.     Serial.println("Sent: LED_ON");
  143.   } else if (state == "off") {
  144.     SerialPort.println("LED_OFF"); // 发送控制指令
  145.     externalLEDState = false;
  146.     server.send(200, "text/plain", "off");
  147.     Serial.println("Sent: LED_OFF");
  148.   }
  149. }

  150. // 获取电压值
  151. void handleVoltage() {
  152.   server.send(200, "text/plain", voltageValue);
  153. }

  154. // 处理串口数据
  155. void processSerialData() {
  156.   if (SerialPort.available()) {
  157.     String data = SerialPort.readStringUntil('\n');
  158.     data.trim();
  159.    
  160.     // 电压数据格式: "VOLT:3.25"
  161.     if (data.startsWith("VOLT:")) {
  162.       voltageValue = data.substring(5) + "V";
  163.       lastVoltageUpdate = millis();
  164.       Serial.println("Received voltage: " + voltageValue);
  165.     }
  166.     // 外部 LED 状态反馈: "EXT_LED:ON"
  167.     else if (data.startsWith("EXT_LED:")) {
  168.       externalLEDState = (data.substring(8) == "ON");
  169.       Serial.println("External LED: " + String(externalLEDState ? "ON" : "OFF"));
  170.     }
  171.   }
  172. }


  173. void blinkStatusLED() {
  174.   if (millis() - lastBlinkTime > 1000) {
  175.     lastBlinkTime = millis();
  176.     ledState = !ledState;
  177.     digitalWrite(statusLed, ledState);
  178.   }
  179. }

  180. void setup() {
  181.   // 初始化串口
  182.   Serial.begin(115200);
  183.   SerialPort.begin(9600, SERIAL_8N1, SERIAL2_RX, SERIAL2_TX);
  184.   
  185.   // 初始化状态指示灯
  186.   pinMode(statusLed, OUTPUT);
  187.   digitalWrite(statusLed, LOW);
  188.   
  189.   // 创建热点
  190.   WiFi.softAP(ap_ssid, ap_password);
  191.   delay(100);
  192.   
  193.   Serial.println("\nAccess Point Created!");
  194.   Serial.println("SSID: " + String(ap_ssid));
  195.   Serial.println("Password: " + String(ap_password));
  196.   Serial.println("IP: " + WiFi.softAPIP().toString());
  197.   
  198.   // 设置路由
  199.   server.on("/", handleRoot);
  200.   server.on("/external/on", []() { handleExternalLED("on"); });
  201.   server.on("/external/off", []() { handleExternalLED("off"); });
  202.   server.on("/voltage", handleVoltage);
  203.   
  204.   server.begin();
  205.   Serial.println("HTTP server started");
  206.   
  207.   // 初始闪烁
  208.   digitalWrite(statusLed, HIGH);
  209.   delay(500);
  210.   digitalWrite(statusLed, LOW);
  211. }

  212. void loop() {
  213.   server.handleClient();  // 处理网页请求
  214.   processSerialData();    // 处理串口数据
  215.   blinkStatusLED();       // 更新LED闪烁状态
  216.   
  217.   // 电压数据超时处理
  218.   if (millis() - lastVoltageUpdate > 5000) {
  219.     voltageValue = "无数据";
  220.   }
  221. }

4.MM32F0121部分

  • 使用板载的蓝色LED作为运行指示灯
  • 使用板载的绿色LED作为控制的对象
  • 使用RTOS,TASK1为运行指示灯任务,TASK2为电压检测任务
  • USART2部分配置代码如下
  1. void USART2_Configure(uint32_t Baudrate)
  2. {
  3.     GPIO_InitTypeDef  GPIO_InitStruct;
  4.     NVIC_InitTypeDef  NVIC_InitStruct;
  5.     USART_InitTypeDef USART_InitStruct;

  6.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

  7.     USART_StructInit(&USART_InitStruct);
  8.     USART_InitStruct.USART_BaudRate   = Baudrate;
  9.     USART_InitStruct.USART_WordLength = USART_WordLength_8b;
  10.     USART_InitStruct.USART_StopBits   = USART_StopBits_1;
  11.     USART_InitStruct.USART_Parity     = USART_Parity_No;
  12.     USART_InitStruct.USART_Mode       = USART_Mode_Rx | USART_Mode_Tx;
  13.     USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  14.     USART_Init(USART2, &USART_InitStruct);

  15.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
  16.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);

  17.     GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_3);
  18.     GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_4);

  19.     GPIO_StructInit(&GPIO_InitStruct);
  20.     GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_8;
  21.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
  22.     GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;
  23.     GPIO_Init(GPIOA, &GPIO_InitStruct);

  24.     GPIO_StructInit(&GPIO_InitStruct);
  25.     GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_8;
  26.     GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_FLOATING;
  27.     GPIO_Init(GPIOB, &GPIO_InitStruct);

  28.     NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
  29.     NVIC_InitStruct.NVIC_IRQChannelPriority = 0;
  30.     NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
  31.     NVIC_Init(&NVIC_InitStruct);

  32.     USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);

  33.     USART_Cmd(USART2, ENABLE);
  34. }
  35. void USART_SendString(USART_TypeDef* USARTx, const char* str) {
  36.     while (*str) {
  37.         USART_SendData(USARTx, (uint8_t)(*str++));
  38.         while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
  39.     }
  40. }

  41. void USART_Printf(USART_TypeDef* USARTx, const char* fmt, ...) {
  42.     char buffer[128];  
  43.     va_list args;
  44.     va_start(args, fmt);
  45.     vsnprintf(buffer, sizeof(buffer), fmt, args);  
  46.     va_end(args);
  47.     USART_SendString(USARTx, buffer);  
  48. }

  49. #define MAX_CMD_LEN 32  

  50. char receivedCmd[MAX_CMD_LEN];  
  51. uint8_t cmdIndex = 0;         

  52. void USART2_IRQHandler(void)
  53. {
  54.     if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
  55.     {
  56.         uint8_t data = USART_ReceiveData(USART2);
  57.         
  58.         if(data == '\r' || data == '\n' || cmdIndex >= MAX_CMD_LEN-1)
  59.         {
  60.             receivedCmd[cmdIndex] = '\0';
  61.            
  62.             if(strcmp(receivedCmd, "LED_ON") == 0) {
  63.                                                                 GPIO_WriteBit(GPIOB,GPIO_Pin_15,0);
  64.                 printf("ACK: LED OFF\r\n");
  65.             }
  66.             else if(strcmp(receivedCmd, "LED_OFF") == 0) {
  67.                                                                 GPIO_WriteBit(GPIOB,GPIO_Pin_15,1);
  68.                 printf("ACK: LED ON\r\n");
  69.             }
  70.             cmdIndex = 0;
  71.         }
  72.         else
  73.         {
  74.             receivedCmd[cmdIndex++] = data;
  75.         }
  76.         
  77.         while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
  78.     }
  79. }
  • ADC部分循环采集,循环上报
  1. #include "adc_dsp.h"

  2. uint32_t ADC_InterruptFlag;

  3. void ADC_Configure(void)
  4. {
  5.     ADC_InitTypeDef  ADC_InitStruct;
  6.     GPIO_InitTypeDef GPIO_InitStruct;
  7.     NVIC_InitTypeDef NVIC_InitStruct;

  8.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC, ENABLE);

  9.     ADC_StructInit(&ADC_InitStruct);
  10.     ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;
  11.     ADC_InitStruct.ADC_Prescaler  = ADC_Prescaler_16;
  12.     ADC_InitStruct.ADC_Mode       = ADC_Mode_Continue;
  13.     ADC_InitStruct.ADC_DataAlign  = ADC_DataAlign_Right;
  14.     ADC_Init(ADC1, &ADC_InitStruct);

  15.     ADC_SampleTimeConfig(ADC1, ADC_Channel_3, ADC_SampleTime_240_5);

  16.     ADC_AnyChannelNumCfg(ADC1, 0);
  17.     ADC_AnyChannelSelect(ADC1, ADC_AnyChannel_0, ADC_Channel_3);
  18.     ADC_AnyChannelCmd(ADC1, ENABLE);

  19.     ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
  20.     ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);

  21.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

  22.     /* PA3(POT) */
  23.     GPIO_StructInit(&GPIO_InitStruct);
  24.     GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_3;
  25.     GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
  26.     GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AIN;
  27.     GPIO_Init(GPIOA, &GPIO_InitStruct);

  28.     NVIC_InitStruct.NVIC_IRQChannel = ADC_COMP_IRQn;
  29.     NVIC_InitStruct.NVIC_IRQChannelPriority = 0x00;
  30.     NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
  31.     NVIC_Init(&NVIC_InitStruct);

  32.     ADC_Cmd(ADC1, ENABLE);

  33. }


  34. void ADC_InternalVoltageSensor_Sample(void)
  35. {
  36.           float    Voltage;
  37.     uint16_t ConversionValue = 0;
  38.       ADC_SoftwareStartConvCmd(ADC1, ENABLE);
  39.     while (1)
  40.     {

  41.         if (0 != ADC_InterruptFlag)
  42.         {
  43.         ConversionValue = (float)ADC_GetChannelConvertedValue(ADC1, ADC_Channel_3);
  44.                                  Voltage = (float)ConversionValue *(float)3.3 / (float)4096.0;
  45.                                 printf("\r\nVDDA = %0.2fV", Voltage);
  46.                                 USART_Printf(USART2,"\r\nVOLT:%0.1f",Voltage);
  47.                                 vTaskDelay(1);                                         
  48.         }
  49.     }
  50. }

  51. void ADC_COMP_IRQHandler(void)
  52. {
  53.     if (RESET != ADC_GetITStatus(ADC1, ADC_IT_EOC))
  54.     {
  55.         ADC_InterruptFlag = 1;

  56.         ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
  57.     }
  58. }
5.实验现象

LED控制.gif 电压上传.gif



ESP32_WEB.zip

2.67 KB, 下载次数: 0

MM32F0121C6PV_09ESP32.zip

6.53 MB, 下载次数: 0

AdaMaYun 发表于 2025-7-31 17:28 | 显示全部楼层
ESP32-WEB控制
您需要登录后才可以回帖 登录 | 注册

本版积分规则

12

主题

33

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部