Arduino UNO + SIM800L - 将数据发送到服务器

3
我使用arduino UNO板与modem sim800l,希望将数据发送到mysql服务器,每10秒钟发送一次。在arduino IDE中打开串行监视器时,一切都正常工作(数据被保存到mysql数据库中),但是当关闭串行监视器时,它就无法工作。
我希望将我的系统远程连接到移动电源,但仅在打开arduino IDE串行监视器时才能正常工作。
如何编辑代码以使微控制器在未连接到计算机和未打开串行监视器的情况下工作?
#include <SoftwareSerial.h>
SoftwareSerial gprsSerial(7, 8);

void setup()
{
  gprsSerial.begin(19200);
  Serial.begin(19200);

  Serial.println("Config SIM900...");
  delay(2000);
  Serial.println("Done!...");
  gprsSerial.flush();
  Serial.flush();

  // attach or detach from GPRS service 
  gprsSerial.println("AT+CGATT?");
  delay(100);
  toSerial();


  // bearer settings
  gprsSerial.println("AT+SAPBR=3,1,\"CONTYPE\",\"GPRS\"");
  delay(2000);
  toSerial();

  // bearer settings
  gprsSerial.println("AT+SAPBR=3,1,\"APN\",\"internet\"");
  delay(2000);
  toSerial();

  // bearer settings
  gprsSerial.println("AT+SAPBR=1,1");
  delay(2000);
  toSerial();
}


void loop()
{
   // initialize http service
   gprsSerial.println("AT+HTTPINIT");
   delay(2000); 
   toSerial();

   // set http param value
   gprsSerial.println("AT+HTTPPARA=\"URL\",\"http://server.net/test.php?data1=2&data2=3\""); 
   delay(2000);
   toSerial();

   // set http action type 0 = GET, 1 = POST, 2 = HEAD
   gprsSerial.println("AT+HTTPACTION=0");
   delay(6000);
   toSerial();

   // read server response
   gprsSerial.println("AT+HTTPREAD"); 
   delay(1000);
   toSerial();

   gprsSerial.println("");
   gprsSerial.println("AT+HTTPTERM");
   toSerial();
   delay(300);

   gprsSerial.println("");
   delay(10000);
}

void toSerial()
{
  while(gprsSerial.available()!=0)
  {
    Serial.write(gprsSerial.read());
  }
}

你已经打开了这个问题,请编辑它而不是提出新的问题。(或者至少删除那个问题!) - Roberto Caboni
你在这里使用的语言是没有C++标准库的C++。C标签根本不适用。 - Antti Haapala -- Слава Україні
2
请停止像这样使用 delay。您必须读取解析从调制解调器接收到的响应。 - hlovdal
@RichardChambers 感谢您的全面回答,我非常感激。明天我会着手处理并告诉您是否有效。目前我还没有使用您的建议,但我知道在按下Arduino UNO微控制器上的RESET按钮后,系统会启动并工作,但有时会停止工作,需要再次按下RESET按钮才能正常工作。 - Marcin
1
嗯,这种行为确实支持了一个想法,即你的程序中的某些部分由于需要某些资源而进入了等待状态。RESET 按钮可以重新启动系统,然后你的程序就可以自由运行,直到它再次需要某些资源并需要等待,直到它可用。我怀疑在你的情况下,由于没有设备从串口消耗字符,所以资源是串行输出缓冲区空间。我很想知道串行输出缓冲区有多大,你写出了多少字节。你能在Setup()中打印 Serial.availableForWrite() 的值吗? - Richard Chambers
显示剩余5条评论
1个回答

2
我不确定Serial的底层实现,但可能会发生一种无限等待Serial.println()的情况。从阅读Serial.flush()文档来看,它也可能导致串行输出无限等待才能返回。由于Serial.println()似乎使用Serial.write()功能来完成其任务,所以我认为如果某个时刻没有设备从串行端口读取数据,则写入缓冲区已满并导致Serial.println()阻塞。请参见https://www.arduino.cc/reference/en/language/functions/communication/serial/write/

注意事项和警告

自Arduino IDE 1.0以来,串行传输是异步的。如果传输缓冲区中有足够的空闲空间,则Serial.write()将在任何字符通过串行传输之前返回。如果传输缓冲区已满,则Serial.write()将阻塞,直到缓冲区中有足够的空间。要避免对Serial.write()进行阻塞调用,可以首先使用availableForWrite()检查传输缓冲区中的空闲空间。

请参见https://www.arduino.cc/reference/en/language/functions/communication/serial/flush/Serial.flush()函数的解释,其中指出:

Serial.flush()

描述

等待传出串行数据的传输完成。(在Arduino 1.0之前,这将删除任何缓冲的传入串行数据。)

flush()继承自Stream实用程序类。

请参阅此文章https://www.baldengineer.com/when-do-you-use-the-arduinos-to-use-serial-flush.html,其中提到:

Serial.flush()是什么?

从Serial.flush(在此页面上找到的Arduino参考资料)中可以看出:

等待传出串行数据的传输完成。

该语句的关键是“传出”。许多人认为Serial.flush()会清空“传入”缓冲区,但它只会暂停程序,直到刷新传输缓冲区。

我建议在使用Serial.println()输出之前使用Serial.availableForWrite()函数进行检查,如果可用字节数表明写缓冲区已满,则跳过输出。
可能最好的方法是在Setup()函数中,在执行Serial.begin()后检查写缓冲区大小,并将其存储在全局变量中,然后使用它来检查写缓冲区是否正在被清空。
参见https://www.instructables.com/id/Arduino-Serial/,其中提到:
步骤3:命令:AvailableForWrite() 描述 获取可用于写入串行缓冲区而不阻止写操作的字节数(字符)。 语法 Serial.availableForWrite()
另请参见https://www.arduino.cc/reference/en/language/functions/communication/serial/availableforwrite/ 还有if (Serial),可用于检查端口是否可用。但我怀疑它更多是检查请求的端口是否可用,而不是端口是否实际上连接了另一端的设备。
还有Serial.available()https://www.arduino.cc/reference/en/language/functions/communication/serial/available/ 建议采取以下措施:
首先,我认为Setup()函数中的Serial.flush()是不必要的,可以安全地删除。虽然这使得在Setup()期间控制台输出立即可见,但它确实引入了等待远程情况,在该情况下,没有设备从串行线读取以清空输出缓冲区。
我还建议在每行使用Serial.println()之前先检查缓冲区可用字节数,如下所示:Serial.availableForWrite()
int firstAvailableForWrite = 0;

void Setup ()
{
    // set up everything you do then add the following statement
    firstAvailableForWrite = Serial.availableForWrite();
}

那么无论您要在哪里进行写操作,都需要使用类似以下示例的if语句进行修改:

if (Serial.availableForWrite() >= firstAvailableForWrite) Serial.println("Config SIM900...");

您可以创建一个类似以下示例的函数:

或者您也可以创建一个类似以下示例的函数:

int serialPrintLineIfAvailable (char *aszLine)
{
    int iCount = 0;
    if (Serial.availableForWrite() >= firstAvailableForWrite) iCount = Serial.println(aszLine);

    return iCount;
}

那么无论您想在哪里使用 Serial.println(),您都可以使用 serialPrintLineIfAvailable() 替换它,例如:serialPrintLineIfAvailable("配置SIM900...");


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接