如何在AtMega2560中解析ESP模块发来的逗号分隔串口数据
问题描述
我用Arduino开发的内置WiFi的AtMega2560已基本正常工作。ESP模块从ThingSpeak获取各温度传感器读数,将其以逗号分隔的字符串通过串口发送给AtMega2560,现在需要解析这个字符串,把每个读数分配给单独的变量。以下是ESP模块、AtMega2560的代码及输出示例,求解决方案。
ESP模块代码
#include <SPI.h> #include <Wire.h> #include "ThingSpeak.h" #include <ESP8266WiFi.h> const char ssid[] = "Beep"; // 你的网络SSID(名称) const char pass[] = "Bap"; // 你的网络密码 WiFiClient client; //---------通道详情---------// unsigned long counterChannelNumber1 = 0000000; // 通道ID(当前) unsigned long counterChannelNumber2 = 0000000; // 通道ID(当前) unsigned long counterChannelNumber3 = 0000000; // 通道ID(当前) const char * myCounterReadAPIKey1 = "REPLACEME"; // 读取API密钥(需要修改) const char * myCounterReadAPIKey2 = "REPLACEME"; // 读取API密钥(需要修改) const char * myCounterReadAPIKey3 = "REPLACEME"; // 读取API密钥(需要修改) const int FieldNumber1 = 1; // 要读取的字段 const int FieldNumber2 = 2; // 要读取的字段 const int FieldNumber3 = 3; // 要读取的字段 const int FieldNumber4 = 4; // 要读取的字段 const int FieldNumber5 = 5; // 要读取的字段 const int FieldNumber6 = 6; // 要读取的字段 const int FieldNumber7 = 7; // 要读取的字段 const int FieldNumber8 = 8; // 要读取的字段 //-------------------------------// void setup() { Serial.begin(115200); WiFi.mode(WIFI_STA); ThingSpeak.begin(client); Serial.println("[ON]"); } void loop() { //----------------- 网络连接 -----------------// if (WiFi.status() != WL_CONNECTED) { Serial.print("Connecting to "); Serial.print(ssid); Serial.println(" ...."); while (WiFi.status() != WL_CONNECTED) { WiFi.begin(ssid, pass); delay(5000); } Serial.println("Connected to Wi-Fi Succesfully."); } //----------------- 房间1 -----------------// long Room1 = ThingSpeak.readLongField(counterChannelNumber1, FieldNumber1, myCounterReadAPIKey1); int statusCode = ThingSpeak.getLastReadStatus(); if (statusCode != 200) { Serial.println("Unable to read channel / No internet connection"); } //----------------- 房间1结束 -----------------// //----------------- 房间2 -----------------// long Room2 = ThingSpeak.readLongField(counterChannelNumber1, FieldNumber2, myCounterReadAPIKey1); statusCode = ThingSpeak.getLastReadStatus(); if (statusCode != 200) { Serial.println("Unable to read channel / No internet connection"); } //----------------- 房间2结束 -----------------// //----------------- 房间3 -----------------// long Room3 = ThingSpeak.readLongField(counterChannelNumber1, FieldNumber3, myCounterReadAPIKey1); statusCode = ThingSpeak.getLastReadStatus(); if (statusCode != 200) { Serial.println("Unable to read channel / No internet connection"); } //----------------- 房间3结束 -----------------// //----------------- 房间4 -----------------// long Room4 = ThingSpeak.readLongField(counterChannelNumber1, FieldNumber4, myCounterReadAPIKey1); statusCode = ThingSpeak.getLastReadStatus(); if (statusCode != 200) { Serial.println("Unable to read channel / No internet connection"); } //----------------- 房间4结束 -----------------// //----------------- 房间5 -----------------// long Room5 = ThingSpeak.readLongField(counterChannelNumber1, FieldNumber5, myCounterReadAPIKey1); statusCode = ThingSpeak.getLastReadStatus(); if (statusCode != 200) { Serial.println("Unable to read channel / No internet connection"); } //----------------- 房间5结束 -----------------// //----------------- 房间6 -----------------// long Room6 = ThingSpeak.readLongField(counterChannelNumber1, FieldNumber6, myCounterReadAPIKey1); statusCode = ThingSpeak.getLastReadStatus(); if (statusCode != 200) { Serial.println("Unable to read channel / No internet connection"); } //----------------- 房间6结束 -----------------// //----------------- 房间7 -----------------// long Room7 = ThingSpeak.readLongField(counterChannelNumber1, FieldNumber7, myCounterReadAPIKey1); statusCode = ThingSpeak.getLastReadStatus(); if (statusCode != 200) { Serial.println("Unable to read channel / No internet connection"); } //----------------- 房间7结束 -----------------// //----------------- 房间8 -----------------// long Room8 = ThingSpeak.readLongField(counterChannelNumber1, FieldNumber8, myCounterReadAPIKey1); statusCode = ThingSpeak.getLastReadStatus(); if (statusCode != 200) { Serial.println("Unable to read channel / No internet connection"); } //----------------- 房间8结束 -----------------// Serial.print(Room1); Serial.print(","); Serial.print(Room2); Serial.print(","); Serial.print(Room3); Serial.print(","); Serial.print(Room4); Serial.print(","); Serial.print(Room5); Serial.print(","); Serial.print(Room6); Serial.print(","); Serial.print(Room7); Serial.print(","); Serial.print(Room8); Serial.print(",\n"); delay(10000); }
AtMega2560代码
char *strings[8]; // strtok()后存储各片段的指针数组 char *ptr = NULL; float room1t, room2t, room3t, room4t, room5t, room6t, room7t, room8t; String inString; char raw; void setup() { Serial.begin(115200); Serial3.begin(115200); } void loop() { while (Serial3.available()) { raw = Serial3.read(); Serial.write(raw); inString += raw; } }
输出示例
Connected to Wi-Fi Succesfully. 23,23,22,23,23,23,22,23, 23,23,22,23,23,23,22,23, 23,23,22,23,23,23,22,23, 23,23,22,23,23,23,22,23,
解决方案
1. 优化ESP模块的输出格式
当前ESP输出的字符串末尾多了一个逗号,会导致解析时出现空值。修改ESP代码中最后的输出部分,去掉末尾多余的逗号,用换行符作为完整数据的结束标记:
// 替换原输出代码 Serial.print(Room1); Serial.print(","); Serial.print(Room2); Serial.print(","); Serial.print(Room3); Serial.print(","); Serial.print(Room4); Serial.print(","); Serial.print(Room5); Serial.print(","); Serial.print(Room6); Serial.print(","); Serial.print(Room7); Serial.print(","); Serial.println(Room8); // 用println输出最后一个值,自动添加换行符
修改后输出的每一行数据格式为 23,23,22,23,23,23,22,23,无多余逗号,方便后续解析。
2. 修改AtMega2560代码实现解析
在AtMega2560中检测换行符,当接收到完整一行数据时进行分割,将每个温度值转换为float类型并赋值给对应变量。修改后的代码如下:
float room1t, room2t, room3t, room4t, room5t, room6t, room7t, room8t; String inString; void setup() { Serial.begin(115200); Serial3.begin(115200); } void loop() { while (Serial3.available()) { char raw = Serial3.read(); // 读到换行符时,开始解析整行数据 if (raw == '\n') { Serial.println("\n===== 解析到的数据 ====="); // 将String转换为char数组,供strtok分割使用 char inputBuffer[100]; inString.toCharArray(inputBuffer, sizeof(inputBuffer)); // 第一次分割,逗号为分隔符 char* token = strtok(inputBuffer, ","); int index = 0; // 循环分割所有字段 while (token != NULL && index < 8) { // 将字符串转换为float并赋值给对应变量 switch(index) { case 0: room1t = atof(token); break; case 1: room2t = atof(token); break; case 2: room3t = atof(token); break; case 3: room4t = atof(token); break; case 4: room5t = atof(token); break; case 5: room6t = atof(token); break; case 6: room7t = atof(token); break; case 7: room8t = atof(token); break; } // 输出解析结果 Serial.print("房间"); Serial.print(index+1); Serial.print("温度: "); Serial.println(atof(token)); // 继续分割下一个字段 token = strtok(NULL, ","); index++; } // 清空字符串,准备接收下一行数据 inString = ""; } else { // 过滤回车符,避免Windows风格换行干扰 if (raw != '\r') { inString += raw; Serial.write(raw); } } } }
代码说明
- 以换行符作为完整数据的结束标记,确保每次解析的是一组完整的温度数据;
- 使用
strtok函数分割逗号分隔的字符串; - 通过
atof函数将分割后的字符串转换为float类型温度值; - 用switch-case将每个值分配给对应房间的温度变量;
- 解析完成后清空
inString,准备接收下一组数据。
内容的提问来源于stack exchange,提问作者Tristan Fivaz
相关产品推荐
相关产品推荐

