NodeMCU串口通信触发Exception(9),求ADC数据解析与网页展示修复方案
问题描述
我有一套UI与主板设备,二者通过UART协议通信。已为主板添加基础代码,使其发送格式为ADC1=xxxx ADC2=yyyy的数据。希望仅从主板捕获该格式数据,提取其中的xxxx与yyyy(整数或字符串),并通过NodeMCU将其展示在基础HTML页面中。但运行代码后出现Exception(9)错误,推测可能原因:
- 串口端口写入的数据量过大,导致系统崩溃
- 代码本身存在问题
原代码
#include <ESP8266WiFi.h> #include <ESP8266WebServer.h> int incomingByte = 0; char deneme[] = ""; const char *delp; const char *delp2; /*Put your SSID & Password*/ const char* ssid = "ssid"; // Enter SSID here const char* password = "pass"; //Enter Password here ESP8266WebServer server(80); float Temperature; float Humidity; void setup() { Serial.begin(9600); delay(100); Serial.println("Connecting to "); Serial.println(ssid); //connect to your local wi-fi network WiFi.begin(ssid, password); //check wi-fi is connected to wi-fi network while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected..!"); Serial.print("Got IP: "); Serial.println(WiFi.localIP()); server.on("/", handle_OnConnect); server.onNotFound(handle_NotFound); server.begin(); Serial.println("HTTP server started"); } void loop() { if (Serial.available()) // now we have at least one character in the Serial input buffer { String incomingStr = Serial.readStringUntil('\n'); incomingStr.toCharArray(deneme,32); Serial.println(deneme); delp = strtok (deneme,"-"); delp2 = strtok (NULL,"-"); Serial.println(delp); Serial.println(delp2); } server.handleClient(); delay(500); } void handle_OnConnect() { server.send(200, "text/html", SendHTML(Temperature,Humidity)); } void handle_NotFound(){ server.send(404, "text/plain", "Not found"); } String SendHTML(float Temperaturestat,float Humiditystat){ String ptr = "<!DOCTYPE html> <html>\n"; ptr +="<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n"; ptr +="<title>ESP8266 Weather Report</title>\n"; ptr +="<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n"; ptr +="body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n"; ptr +="p {font-size: 24px;color: #444444;margin-bottom: 10px;}\n"; ptr +="</style>\n"; ptr +="<script>\n"; ptr +="setInterval(loadDoc,200);\n"; ptr +="function loadDoc() {\n"; ptr +="var xhttp = new XMLHttpRequest();\n"; ptr +="xhttp.onreadystatechange = function() {\n"; ptr +="if (this.readyState == 4 && this.status == 200) {\n"; ptr +="document.getElementById(\"webpage\").innerHTML =this.responseText}\n"; ptr +="};\n"; ptr +="xhttp.open(\"GET\", \"/\", true);\n"; ptr +="xhttp.send();\n"; ptr +="}\n"; ptr +="</script>\n"; ptr +="</head>\n"; ptr +="<body>\n"; ptr +="<div id=\"webpage\">\n"; ptr +="<h1>ESP8266 NodeMCU Weather Report</h1>\n"; ptr +="<p>Temperature: "; ptr +=delp; ptr += " NTC</p>"; ptr +="<p>Humidity: "; ptr +=delp2; ptr += " Heatsink</p>"; ptr +="</div>\n"; ptr +="</body>\n"; ptr +="</html>\n"; return ptr; }
错误原因分析
Exception(9)是ESP8266的内存访问越界/栈溢出错误,核心问题集中在内存管理和字符串解析逻辑:
- 数组越界:
char deneme[] = "";定义的是长度仅为1的字符数组,调用incomingStr.toCharArray(deneme,32);时会向超出数组范围的内存写入数据,直接破坏ESP8266内存结构,引发崩溃。 - 解析逻辑错误:使用
strtok(deneme,"-")分割字符串,但主板发送格式的分隔符是空格和=,用-分割完全无法提取目标数据,同时导致delp/delp2成为野指针,生成HTML时访问非法内存。 - 刷新频率过高:
setInterval(loadDoc,200)设置200ms一次HTTP请求,给ESP8266带来过大处理压力,加重内存问题。 - 全局指针风险:
delp和delp2是全局指针,指向的内存(deneme)被覆盖或越界后变为无效指针,访问时触发错误。
解决方案及修正代码
关键改动点
- 扩大
deneme数组容量,避免越界 - 重写字符串解析逻辑,正确提取
ADC1和ADC2的值 - 使用全局变量存储解析后的数值,替代风险指针
- 降低页面刷新频率,减轻ESP8266负载
- 增加数据合法性校验,避免解析错误数据
修正后代码
#include <ESP8266WiFi.h> #include <ESP8266WebServer.h> /*Put your SSID & Password*/ const char* ssid = "ssid"; const char* password = "pass"; ESP8266WebServer server(80); // 用全局变量存储解析后的ADC值 String adc1Value = "0"; String adc2Value = "0"; void setup() { Serial.begin(9600); delay(100); Serial.println("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.print("."); } Serial.println(""); Serial.println("WiFi connected..!"); Serial.print("Got IP: "); Serial.println(WiFi.localIP()); server.on("/", handle_OnConnect); server.onNotFound(handle_NotFound); server.begin(); Serial.println("HTTP server started"); } void loop() { if (Serial.available()) { String incomingStr = Serial.readStringUntil('\n'); // 去掉字符串首尾的空白字符(包括换行、空格) incomingStr.trim(); // 校验数据格式是否符合要求 if (incomingStr.startsWith("ADC1=") && incomingStr.indexOf("ADC2=") != -1) { // 按空格分割成两部分 int spaceIndex = incomingStr.indexOf(' '); if (spaceIndex != -1) { String adc1Part = incomingStr.substring(0, spaceIndex); String adc2Part = incomingStr.substring(spaceIndex + 1); // 提取ADC1的值(=后面的部分) int eqIndex1 = adc1Part.indexOf('='); if (eqIndex1 != -1) { adc1Value = adc1Part.substring(eqIndex1 + 1); } // 提取ADC2的值 int eqIndex2 = adc2Part.indexOf('='); if (eqIndex2 != -1) { adc2Value = adc2Part.substring(eqIndex2 + 1); } Serial.print("Parsed - ADC1: "); Serial.print(adc1Value); Serial.print(" | ADC2: "); Serial.println(adc2Value); } } else { // 格式不符合时打印日志,跳过解析 Serial.println("Invalid data format: " + incomingStr); } } server.handleClient(); // 移除不必要的delay,避免影响HTTP请求处理 } void handle_OnConnect() { server.send(200, "text/html", SendHTML()); } void handle_NotFound(){ server.send(404, "text/plain", "Not found"); } String SendHTML(){ String ptr = "<!DOCTYPE html> <html>\n"; ptr += "<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, user-scalable=no\">\n"; ptr += "<title>ADC Data Display</title>\n"; ptr += "<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}\n"; ptr += "body{margin-top: 50px;} h1 {color: #444444;margin: 50px auto 30px;}\n"; ptr += "p {font-size: 24px;color: #444444;margin-bottom: 10px;}\n"; ptr += "</style>\n"; ptr += "<script>\n"; // 调整刷新间隔为2秒,减轻ESP8266负载 ptr += "setInterval(loadDoc, 2000);\n"; ptr += "function loadDoc() {\n"; ptr += "var xhttp = new XMLHttpRequest();\n"; ptr += "xhttp.onreadystatechange = function() {\n"; ptr += "if (this.readyState == 4 && this.status == 200) {\n"; ptr += "document.getElementById(\"webpage\").innerHTML = this.responseText;\n"; ptr += "}\n"; ptr += "};\n"; ptr += "xhttp.open(\"GET\", \"/\", true);\n"; ptr += "xhttp.send();\n"; ptr += "}\n"; ptr += "</script>\n"; ptr += "</head>\n"; ptr += "<body>\n"; ptr += "<div id=\"webpage\">\n"; ptr += "<h1>NodeMCU ADC Data Display</h1>\n"; ptr += "<p>ADC1 Value: "; ptr += adc1Value; ptr += "</p>"; ptr += "<p>ADC2 Value: "; ptr += adc2Value; ptr += "</p>"; ptr += "</div>\n"; ptr += "</body>\n"; ptr += "</html>\n"; return ptr; }
额外优化建议
- 如果需要数值计算,可将
adc1Value和adc2Value转为int或float类型存储 - 若串口数据发送频繁,可增加串口缓冲区处理逻辑,避免数据丢失
- 可为HTML页面添加简单样式优化,提升显示效果
内容的提问来源于stack exchange,提问作者MrElectronicEngineer
相关产品推荐
相关产品推荐

