本帖最后由 阿源玩电子 于 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);
- }
- }
- #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.实验现象
|
|