双Atmega328p架构GSM-P10电子公告板串口消息截断问题求助
双Atmega328P架构GSM电子公告板消息接收截断问题
系统架构说明
采用两块Atmega328P单片机拆分功能,规避DMD库与SoftwareSerial库的冲突:
- 单片机1:对接SIM800L模块,接收短信后通过串口转发消息
- 单片机2:对接P10显示模块,接收串口消息并滚动显示
当前问题:单片机2接收的消息存在截断,无法完整显示。
单片机1(短信接收与转发)代码
#include <SIM800L.h> SIM800L sim800l(2, 3); //Rx, Tx int LED_PIN = 8; //Enter the phone number of the person whom you want to send sms String reply = "Hey! I have got your message"; void handleSMS(String number, String message) { int start_index = message.indexOf('*'); // message starts with '*' int end_index = message.indexOf('#'); // message ends with '*' //message.trim(); // if message starts with * and ends with # if(start_index == 0 && end_index == (message.length() - 1)) { Serial.println(message); blinkLed(); sim800l.sendSMS(number, reply); } } void setup() { Serial.begin(9600); pinMode(LED_PIN, OUTPUT); sim800l.begin(9600); sim800l.setSMSCallback(handleSMS); } void loop() { sim800l.listen(); } void blinkLed(){ for(int i = 0; i < 3; i++){ digitalWrite(LED_PIN, HIGH); delay(1000); digitalWrite(LED_PIN, LOW); delay(1000); } }
单片机2(消息接收与P10显示)代码
#include <SPI.h> #include "buzzer.h" #include <DMD.h> #include "memory.h" #include <TimerOne.h> #include "SystemFont5x7.h" #include "Arial_black_16.h" #define ROW 3 #define COLUMN 1 #define FONT Arial_Black_16 char message [250] = "Welcome to electronic projects workshop 1234"; char r_msg[250] = ""; bool flag = false; DMD led_module(ROW, COLUMN); void scan_module() { led_module.scanDisplayBySPI(); } void setup() { EEPROM.begin(); Serial.begin(9600); buzzerSetup(); beeps(3); int m_length = EEPROM.read(5); // strcpy(message, ""); String stored_string = read_string(5); char stored_char[stored_string.length() + 1]; stored_string.toCharArray(stored_char, stored_string.length() + 1); // convert message from string to array of char strcpy(message, stored_char); // update p10 with stored message Timer1.initialize(2000); Timer1.attachInterrupt(scan_module); led_module.clearScreen( true ); writeString(5, "Hello people"); } void loop() { Serial.println(read_string(5)); receiveMessage(); led_module.selectFont(FONT); led_module.drawMarquee(message, strlen(message), (32 * ROW), 0); long start = millis(); long timming = start; boolean flag = false; while (!flag) { if ((timming + 20) < millis()) { flag = led_module.stepMarquee(-1, 0); timming = millis(); } } } void receiveMessage() { /* Listens to incoming message through serial interface. It extracts the message if it starts with '*' and ends with '#' */ while(Serial.available()) { String s_msg = Serial.readString(); // s_msg.trim(); int start_index = s_msg.indexOf('*') + 1; int end_index = s_msg.indexOf('#'); // whether message contains * and # and also # comes after * if(start_index > 0 ){ beeps(1); String substr = s_msg.substring(start_index, end_index - 1); int m_len = substr.length() + 1; char m_msg [m_len]; substr.toCharArray(m_msg, m_len); strcpy(message, m_msg); // write to eeprom writeString(5, substr); } } }
问题排查与解决方案
核心问题分析
- 串口接收逻辑缺陷:单片机2的
receiveMessage()中使用Serial.readString(),该函数默认超时后返回,若消息较长或串口传输有延迟,会导致单次读取不完整。 - 消息边界处理错误:
substring(start_index, end_index - 1)多减1,会丢失#前的最后一个字符。 - 主循环阻塞:单片机2的
loop()中,滚动显示的while循环会持续阻塞直到完成,期间无法及时响应串口接收,导致后续字节丢失。
修复步骤
1. 修正串口接收逻辑,按边界完整读取
修改receiveMessage()为逐字节读取,直到收到结束符#:
void receiveMessage() { static String buffer = ""; while(Serial.available()) { char c = Serial.read(); buffer += c; // 检测到结束符#,处理完整消息 if(c == '#'){ int start_index = buffer.indexOf('*'); int end_index = buffer.indexOf('#'); // 验证消息格式:*开头,#结尾且顺序正确 if(start_index == 0 && end_index == buffer.length() - 1){ beeps(1); String substr = buffer.substring(1, end_index); // 取*和#之间的内容 int m_len = substr.length() + 1; char m_msg [m_len]; substr.toCharArray(m_msg, m_len); strcpy(message, m_msg); writeString(5, substr); } // 清空缓冲区,准备接收下一条消息 buffer = ""; } } }
2. 优化主循环,避免阻塞
将滚动显示改为非阻塞方式,确保串口接收及时响应:
// 全局变量记录滚动状态 bool isScrolling = false; long marqueeTimer = 0; void loop() { Serial.println(read_string(5)); receiveMessage(); if(!isScrolling){ led_module.selectFont(FONT); led_module.drawMarquee(message, strlen(message), (32 * ROW), 0); isScrolling = true; marqueeTimer = millis(); } if(isScrolling){ if(millis() - marqueeTimer >= 20){ bool finished = led_module.stepMarquee(-1, 0); marqueeTimer = millis(); if(finished){ isScrolling = false; } } } }
3. 调整单片机1的发送逻辑(可选)
发送消息时确保串口传输完整,避免额外换行干扰:
// 修改handleSMS中的发送逻辑 if(start_index == 0 && end_index == (message.length() - 1)) { Serial.print(message); // 用print代替println,避免额外换行 // 等待串口发送完成 while(Serial.availableForWrite() < message.length()); blinkLed(); sim800l.sendSMS(number, reply); }
内容的提问来源于stack exchange,提问作者Bello Shehu
相关产品推荐
相关产品推荐

