打印
[MM32硬件]

【灵动微电子MM32F0121测评】9.ESP32-WEB控制

[复制链接]
327|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 阿源玩电子 于 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设备的数量

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

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

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

WebServer server(80);

// 全局变量
String voltageValue = "0.00V";  // 存储电压值
unsigned long lastVoltageUpdate = 0;
bool externalLEDState = false;
unsigned long lastBlinkTime = 0;
bool ledState = LOW;

void handleRoot() {
  String html = R"rawliteral(
    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="UTF-8">
      <title>ESP32 设备控制</title>
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <style>
        body {
          font-family: "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
          max-width: 600px;
          margin: 0 auto;
          padding: 20px;
          background-color: #f5f5f5;
        }
        .card {
          background: white;
          border-radius: 10px;
          box-shadow: 0 4px 8px rgba(0,0,0,0.1);
          padding: 20px;
          margin-bottom: 20px;
        }
        h1 { color: #2c3e50; text-align: center; }
        .btn {
          display: block;
          width: 100%;
          padding: 15px;
          margin: 10px 0;
          background-color: #3498db;
          color: white;
          border: none;
          border-radius: 5px;
          font-size: 18px;
          cursor: pointer;
          transition: background-color 0.3s;
        }
        .btn-on { background-color: #27ae60; }
        .btn-off { background-color: #e74c3c; }
        .btn:hover { opacity: 0.9; }
        .status {
          padding: 15px;
          text-align: center;
          font-size: 20px;
          margin: 15px 0;
          border-radius: 5px;
        }
        .voltage { background-color: #f1c40f; color: #2c3e50; }
        .external-led { background-color: %external_color%; }
        .info {
          text-align: center;
          color: #7f8c8d;
          margin-top: 30px;
          font-size: 14px;
        }
      </style>
      <script>
        function updateVoltage() {
          fetch('/voltage')
            .then(response => response.text())
            .then(data => {
              document.getElementById("voltage-value").innerHTML = data;
              setTimeout(updateVoltage, 2000);
            });
        }
        
        function controlLED(state) {
          fetch('/external/' + state)
            .then(response => response.text())
            .then(data => {
              const statusElem = document.getElementById("external-status");
              statusElem.innerHTML = "状态: " + data.toUpperCase();
              statusElem.className = "status external-led";
              statusElem.style.backgroundColor = state === "on" ? "#27ae60" : "#e74c3c";
            });
        }
        
        window.onload = function() {
          updateVoltage();
        };
      </script>
    </head>
    <body>
      <div class="card">
        <h1>ESP32 设备</h1>
        
        <div class="card">
          <h2>系统状态</h2>
          <div class="status" style="background-color: #3498db; color: white;">
            ESP32  (正常运行)
          </div>
        </div>
        
        <div class="card">
          <h2>Mini_F0121-OB</h2>
          <button class="btn btn-on" onclick="controlLED('on')">打开 LED</button>
          <button class="btn btn-off" onclick="controlLED('off')">关闭 LED</button>
          <div id="external-status" class="status external-led">状态: OFF</div>
        </div>
        
        <div class="card">
          <h2>电压监测</h2>
          <div id="voltage-value" class="status voltage">读取中...</div>
        </div>
      </div>
      
      <div class="info">
        IP: 192.168.4.1 | 连接设备: %client_count%
      </div>
    </body>
    </html>
  )rawliteral";
  
  // 动态替换内容
  html.replace("%external_color%", externalLEDState ? "#27ae60" : "#e74c3c");
  html.replace("%client_count%", String(WiFi.softAPgetStationNum()));
  
  server.send(200, "text/html; charset=utf-8", html);
}

void handleExternalLED(String state) {
  if (state == "on") {
    SerialPort.println("LED_ON");  // 发送控制指令
    externalLEDState = true;
    server.send(200, "text/plain", "on");
    Serial.println("Sent: LED_ON");
  } else if (state == "off") {
    SerialPort.println("LED_OFF"); // 发送控制指令
    externalLEDState = false;
    server.send(200, "text/plain", "off");
    Serial.println("Sent: LED_OFF");
  }
}

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

// 处理串口数据
void processSerialData() {
  if (SerialPort.available()) {
    String data = SerialPort.readStringUntil('\n');
    data.trim();
   
    // 电压数据格式: "VOLT:3.25"
    if (data.startsWith("VOLT:")) {
      voltageValue = data.substring(5) + "V";
      lastVoltageUpdate = millis();
      Serial.println("Received voltage: " + voltageValue);
    }
    // 外部 LED 状态反馈: "EXT_LED:ON"
    else if (data.startsWith("EXT_LED:")) {
      externalLEDState = (data.substring(8) == "ON");
      Serial.println("External LED: " + String(externalLEDState ? "ON" : "OFF"));
    }
  }
}


void blinkStatusLED() {
  if (millis() - lastBlinkTime > 1000) {
    lastBlinkTime = millis();
    ledState = !ledState;
    digitalWrite(statusLed, ledState);
  }
}

void setup() {
  // 初始化串口
  Serial.begin(115200);
  SerialPort.begin(9600, SERIAL_8N1, SERIAL2_RX, SERIAL2_TX);
  
  // 初始化状态指示灯
  pinMode(statusLed, OUTPUT);
  digitalWrite(statusLed, LOW);
  
  // 创建热点
  WiFi.softAP(ap_ssid, ap_password);
  delay(100);
  
  Serial.println("\nAccess Point Created!");
  Serial.println("SSID: " + String(ap_ssid));
  Serial.println("Password: " + String(ap_password));
  Serial.println("IP: " + WiFi.softAPIP().toString());
  
  // 设置路由
  server.on("/", handleRoot);
  server.on("/external/on", []() { handleExternalLED("on"); });
  server.on("/external/off", []() { handleExternalLED("off"); });
  server.on("/voltage", handleVoltage);
  
  server.begin();
  Serial.println("HTTP server started");
  
  // 初始闪烁
  digitalWrite(statusLed, HIGH);
  delay(500);
  digitalWrite(statusLed, LOW);
}

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

4.MM32F0121部分

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

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

    USART_StructInit(&USART_InitStruct);
    USART_InitStruct.USART_BaudRate   = Baudrate;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_InitStruct.USART_StopBits   = USART_StopBits_1;
    USART_InitStruct.USART_Parity     = USART_Parity_No;
    USART_InitStruct.USART_Mode       = USART_Mode_Rx | USART_Mode_Tx;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_Init(USART2, &USART_InitStruct);

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_3);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_4);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_8;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_8;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_FLOATING;
    GPIO_Init(GPIOB, &GPIO_InitStruct);

    NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);

    USART_Cmd(USART2, ENABLE);
}
void USART_SendString(USART_TypeDef* USARTx, const char* str) {
    while (*str) {
        USART_SendData(USARTx, (uint8_t)(*str++));
        while (USART_GetFlagStatus(USARTx, USART_FLAG_TC) == RESET);
    }
}

void USART_Printf(USART_TypeDef* USARTx, const char* fmt, ...) {
    char buffer[128];  
    va_list args;
    va_start(args, fmt);
    vsnprintf(buffer, sizeof(buffer), fmt, args);  
    va_end(args);
    USART_SendString(USARTx, buffer);  
}

#define MAX_CMD_LEN 32  

char receivedCmd[MAX_CMD_LEN];  
uint8_t cmdIndex = 0;         

void USART2_IRQHandler(void)
{
    if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
    {
        uint8_t data = USART_ReceiveData(USART2);
        
        if(data == '\r' || data == '\n' || cmdIndex >= MAX_CMD_LEN-1)
        {
            receivedCmd[cmdIndex] = '\0';
           
            if(strcmp(receivedCmd, "LED_ON") == 0) {
                                                                GPIO_WriteBit(GPIOB,GPIO_Pin_15,0);
                printf("ACK: LED OFF\r\n");
            }
            else if(strcmp(receivedCmd, "LED_OFF") == 0) {
                                                                GPIO_WriteBit(GPIOB,GPIO_Pin_15,1);
                printf("ACK: LED ON\r\n");
            }
            cmdIndex = 0;
        }
        else
        {
            receivedCmd[cmdIndex++] = data;
        }
        
        while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET);
    }
}
  • ADC部分循环采集,循环上报
#include "adc_dsp.h"

uint32_t ADC_InterruptFlag;

void ADC_Configure(void)
{
    ADC_InitTypeDef  ADC_InitStruct;
    GPIO_InitTypeDef GPIO_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC, ENABLE);

    ADC_StructInit(&ADC_InitStruct);
    ADC_InitStruct.ADC_Resolution = ADC_Resolution_12b;
    ADC_InitStruct.ADC_Prescaler  = ADC_Prescaler_16;
    ADC_InitStruct.ADC_Mode       = ADC_Mode_Continue;
    ADC_InitStruct.ADC_DataAlign  = ADC_DataAlign_Right;
    ADC_Init(ADC1, &ADC_InitStruct);

    ADC_SampleTimeConfig(ADC1, ADC_Channel_3, ADC_SampleTime_240_5);

    ADC_AnyChannelNumCfg(ADC1, 0);
    ADC_AnyChannelSelect(ADC1, ADC_AnyChannel_0, ADC_Channel_3);
    ADC_AnyChannelCmd(ADC1, ENABLE);

    ADC_ClearFlag(ADC1, ADC_FLAG_EOC);
    ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

    /* PA3(POT) */
    GPIO_StructInit(&GPIO_InitStruct);
    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_3;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_High;
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    NVIC_InitStruct.NVIC_IRQChannel = ADC_COMP_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPriority = 0x00;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);

    ADC_Cmd(ADC1, ENABLE);

}


void ADC_InternalVoltageSensor_Sample(void)
{
          float    Voltage;
    uint16_t ConversionValue = 0;
      ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    while (1)
    {

        if (0 != ADC_InterruptFlag)
        {
        ConversionValue = (float)ADC_GetChannelConvertedValue(ADC1, ADC_Channel_3);
                                 Voltage = (float)ConversionValue *(float)3.3 / (float)4096.0;
                                printf("\r\nVDDA = %0.2fV", Voltage);
                                USART_Printf(USART2,"\r\nVOLT:%0.1f",Voltage);
                                vTaskDelay(1);                                         
        }
    }
}

void ADC_COMP_IRQHandler(void)
{
    if (RESET != ADC_GetITStatus(ADC1, ADC_IT_EOC))
    {
        ADC_InterruptFlag = 1;

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




ESP32_WEB.zip

2.67 KB

MM32F0121C6PV_09ESP32.zip

6.53 MB

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

12

主题

30

帖子

0

粉丝