如何读取 arduino 串口上的 GSM AT 命令响应以获得长响应?
How to read GSM AT command response on arduino Serial port for long response?
我有 Arduino UNO 和一个 Sim800L 模块,我知道读取串行的方法,它类似于 ,但是当我执行此功能时:
String GetRegData()
{
Serial.println("Get nearby antenna info ...");
SIM800L.print("AT+CNETSCAN=1\r");
delay(1000);
SIM800L.print("AT+CNETSCAN\r");
delay(1000);
String buffer2;
while (SIM800L.available())
{
char c = SIM800L.read();
Serial.print(c);
buffer2.concat(c);
delay(10);
}
//Serial.println();
return buffer2;
}
输出为:
AT+CMGF=1
OK
AT+CNMI=2,2,0,0,0
OK
AT+CNETSCAN=1
OK
AT+CNETSCAN
Operator:"XXXX",MCC:XXX,MNC:XX,Rxlev:XX,Cellid:XX,Arfcn:XX,Lac:q5er32xlAair32xlAacrseifcca,Nvdc00
当我在此设备上手动执行此命令时,此 AT 命令 (AT+CNETSCAN) 应扫描所有附近的天线并以多行打印一些信息(根据数据表) (SIM800L) ,我得到多行这样的:
Operator:"XXXX",MCC:XXX,MNC:XX,Rxlev:XX,Cellid:XX,Arfcn:XX,Lac:XX,Bsic:XX
Operator:"XXXX",MCC:XXX,MNC:XX,Rxlev:XX,Cellid:XX,Arfcn:XX,Lac:XX,Bsic:XX
Operator:"XXXX",MCC:XXX,MNC:XX,Rxlev:XX,Cellid:XX,Arfcn:XX,Lac:XX,Bsic:XX
OK
但是我不知道当我以编程方式执行时会发生什么,它一团糟,我尝试更改波特率并更改接收方法并逐个读取字符,我确实在接收之间设置了延迟,我试图做 if
或 for
而不是 while
但运气不好。
我想接收每一行之间有延迟,它破坏了串行可用性,但我不知道该怎么办!任何帮助将不胜感激。
顺便说一句,我的设置函数:
void setup() {
SIM800L.begin(9600);
Serial.begin(9600);
delay(3000);
SIM800L.print("AT+CMGF=1\r");
delay(100);
SIM800L.print("AT+CNMI=2,2,0,0,0\r");
delay(100);
GetRegData();
delay(1000);
}
Ps:整个东西(Arduino+Sim800L)工作正常,没有错误,Simcard 已解锁,可以发送接收短信和电话,等等
两个提示:
- 删除接收函数中的所有延迟(),它停止处理每个字符 10 毫秒,这可能会导致长 运行
上的串行缓冲区溢出
- 去掉 String class,读入一个足够大的字符缓冲区以获取最大的预期消息。可以在 stackexchange 上找到示例。
如果您使用双向 com,serial.available() 会很方便,但您需要自己检查结束符或其他外部信号。
通过花费数小时思考整个过程,我想出了一个主意:
在串行端口上等待 10 秒,得到它吐出的内容!
NO 串口断线 NO while (SIM800L.available() != 0)
elapsedMillis
是库,这是代码:
String buffer2;
elapsedMillis timeElapsed;
unsigned int interval = 10000;
while(timeElapsed < interval){
if (SIM800L.available() != 0){ // for ignoring the NULLs
char c = SIM800L.read();
Serial.print(c);
buffer2.concat(c);
}
}
虽然这个过程中有一些亮点:
1-旧循环没有得到全部输出的主要原因是,SIM800L.available()
在多行接收期间变为零!
2- String
数据类型或 SIM800L.read();
不要做错任何事情,理论上它们没有读取设备输出(如 64 字节)字符的限制,它们全部读取
3- 有时电子设备的行为很奇怪 Sim800L 又旧又便宜!
4- 如果您要通过 SMS 发送此数据(网络天线扫描),请注意 SMS 最大字符限制,并考虑到服务提供商将向您收取大约每发送 150 个字符的费用!并从响应中删除 \r\n
个字符,并在 SIM800L 上发送短信之前删除 运行 AT+CSMP=17,167,0,0
否则它将发送空白短信!
5- Sim800L 数据表说它在端口 115200 上效果最好 BUT 它也是快为这个设备!将其波特率设置为 9600.
根据 OP 的回答,问题在于 serial.available()
returning 0 并使循环过早结束。
这可能是因为两次连续 serial.read()
调用之间的延迟太低,并且等待一些副场景(例如在更多行中呈现的 AT 响应)是不够的。
增加延迟是错误的,因为响应是逐个字符读取的,这种更改会影响总时间。可以做的是在总 字符间延迟 后退出循环。以下示例使用了 200ms
的值:
#define MAX_DELAY_MS 200
#define LOOP_DELAY_MS 10
String GetRegData()
{
int intercharTime = 0;
Serial.println("Get nearby antenna info ...");
SIM800L.print("AT+CNETSCAN=1\r");
delay(1000);
SIM800L.print("AT+CNETSCAN\r");
delay(1000);
String buffer2;
while ( intercharTime < MAX_DELAY_MS )
{
if( SIM800L.available() )
{
int c = SIM800L.read();
/* read() return -1 on fail. Make sure it read something before using c */
if( c != -1 )
{
Serial.print( (char)c );
buffer2.concat( (char)c );
/* Something received: reset intercharTime */
intercharTime = 0;
}
}
else
{
intercharTime += LOOP_DELAY_MS;
}
delay(LOOP_DELAY_MS);
}
//Serial.println();
return buffer2;
}
变化:
勾选intercharTime
退出循环。每次 serial.available()
returned 0 时增加。每次接收到字符时重置。这样我们在 MAX_DELAY_MS
毫秒后退出循环。
read()
returns 是一个整数,所以我把它放在一个整数变量上,在需要时将它转换为 char
。此外它 returns -1 失败。最好在处理可能无效的数据之前检查 return 值。
- 我还用定义替换了
10ms
延迟。这样就很容易玩几对 LOOP_DELAY_MS
和 MAX_DELAY_MS
来找到最合适的。
我有 Arduino UNO 和一个 Sim800L 模块,我知道读取串行的方法,它类似于
String GetRegData()
{
Serial.println("Get nearby antenna info ...");
SIM800L.print("AT+CNETSCAN=1\r");
delay(1000);
SIM800L.print("AT+CNETSCAN\r");
delay(1000);
String buffer2;
while (SIM800L.available())
{
char c = SIM800L.read();
Serial.print(c);
buffer2.concat(c);
delay(10);
}
//Serial.println();
return buffer2;
}
输出为:
AT+CMGF=1
OK
AT+CNMI=2,2,0,0,0
OK
AT+CNETSCAN=1
OK
AT+CNETSCAN
Operator:"XXXX",MCC:XXX,MNC:XX,Rxlev:XX,Cellid:XX,Arfcn:XX,Lac:q5er32xlAair32xlAacrseifcca,Nvdc00
当我在此设备上手动执行此命令时,此 AT 命令 (AT+CNETSCAN) 应扫描所有附近的天线并以多行打印一些信息(根据数据表) (SIM800L) ,我得到多行这样的:
Operator:"XXXX",MCC:XXX,MNC:XX,Rxlev:XX,Cellid:XX,Arfcn:XX,Lac:XX,Bsic:XX
Operator:"XXXX",MCC:XXX,MNC:XX,Rxlev:XX,Cellid:XX,Arfcn:XX,Lac:XX,Bsic:XX
Operator:"XXXX",MCC:XXX,MNC:XX,Rxlev:XX,Cellid:XX,Arfcn:XX,Lac:XX,Bsic:XX
OK
但是我不知道当我以编程方式执行时会发生什么,它一团糟,我尝试更改波特率并更改接收方法并逐个读取字符,我确实在接收之间设置了延迟,我试图做 if
或 for
而不是 while
但运气不好。
我想接收每一行之间有延迟,它破坏了串行可用性,但我不知道该怎么办!任何帮助将不胜感激。
顺便说一句,我的设置函数:
void setup() {
SIM800L.begin(9600);
Serial.begin(9600);
delay(3000);
SIM800L.print("AT+CMGF=1\r");
delay(100);
SIM800L.print("AT+CNMI=2,2,0,0,0\r");
delay(100);
GetRegData();
delay(1000);
}
Ps:整个东西(Arduino+Sim800L)工作正常,没有错误,Simcard 已解锁,可以发送接收短信和电话,等等
两个提示:
- 删除接收函数中的所有延迟(),它停止处理每个字符 10 毫秒,这可能会导致长 运行 上的串行缓冲区溢出
- 去掉 String class,读入一个足够大的字符缓冲区以获取最大的预期消息。可以在 stackexchange 上找到示例。
如果您使用双向 com,serial.available() 会很方便,但您需要自己检查结束符或其他外部信号。
通过花费数小时思考整个过程,我想出了一个主意:
在串行端口上等待 10 秒,得到它吐出的内容!
NO 串口断线 NO while (SIM800L.available() != 0)
elapsedMillis
是库,这是代码:
String buffer2;
elapsedMillis timeElapsed;
unsigned int interval = 10000;
while(timeElapsed < interval){
if (SIM800L.available() != 0){ // for ignoring the NULLs
char c = SIM800L.read();
Serial.print(c);
buffer2.concat(c);
}
}
虽然这个过程中有一些亮点:
1-旧循环没有得到全部输出的主要原因是,SIM800L.available()
在多行接收期间变为零!
2- String
数据类型或 SIM800L.read();
不要做错任何事情,理论上它们没有读取设备输出(如 64 字节)字符的限制,它们全部读取
3- 有时电子设备的行为很奇怪 Sim800L 又旧又便宜!
4- 如果您要通过 SMS 发送此数据(网络天线扫描),请注意 SMS 最大字符限制,并考虑到服务提供商将向您收取大约每发送 150 个字符的费用!并从响应中删除 \r\n
个字符,并在 SIM800L 上发送短信之前删除 运行 AT+CSMP=17,167,0,0
否则它将发送空白短信!
5- Sim800L 数据表说它在端口 115200 上效果最好 BUT 它也是快为这个设备!将其波特率设置为 9600.
根据 OP 的回答,问题在于 serial.available()
returning 0 并使循环过早结束。
这可能是因为两次连续 serial.read()
调用之间的延迟太低,并且等待一些副场景(例如在更多行中呈现的 AT 响应)是不够的。
增加延迟是错误的,因为响应是逐个字符读取的,这种更改会影响总时间。可以做的是在总 字符间延迟 后退出循环。以下示例使用了 200ms
的值:
#define MAX_DELAY_MS 200
#define LOOP_DELAY_MS 10
String GetRegData()
{
int intercharTime = 0;
Serial.println("Get nearby antenna info ...");
SIM800L.print("AT+CNETSCAN=1\r");
delay(1000);
SIM800L.print("AT+CNETSCAN\r");
delay(1000);
String buffer2;
while ( intercharTime < MAX_DELAY_MS )
{
if( SIM800L.available() )
{
int c = SIM800L.read();
/* read() return -1 on fail. Make sure it read something before using c */
if( c != -1 )
{
Serial.print( (char)c );
buffer2.concat( (char)c );
/* Something received: reset intercharTime */
intercharTime = 0;
}
}
else
{
intercharTime += LOOP_DELAY_MS;
}
delay(LOOP_DELAY_MS);
}
//Serial.println();
return buffer2;
}
变化:
-
勾选
intercharTime
退出循环。每次serial.available()
returned 0 时增加。每次接收到字符时重置。这样我们在MAX_DELAY_MS
毫秒后退出循环。read()
returns 是一个整数,所以我把它放在一个整数变量上,在需要时将它转换为char
。此外它 returns -1 失败。最好在处理可能无效的数据之前检查 return 值。- 我还用定义替换了
10ms
延迟。这样就很容易玩几对LOOP_DELAY_MS
和MAX_DELAY_MS
来找到最合适的。