【问题标题】:Function having return type of struct return nothing every second time its called返回类型为 struct 的函数在其被调用时不返回任何内容
【发布时间】:2020-03-06 19:58:52
【问题描述】:

我正在编写一个程序来通过 ESP32(Arduino 框架)中的蓝牙接收 SSID 和密码。函数 BTSerialRcvBuffer() 等待蓝牙,当它接收到一个字符串时,它通过 struct Buffer_return 类型的变量返回字符串的基地址和大小。该函数返回 SSID 但不返回密码。我不知道为什么?我是否必须为 Var.rtn_addr 分配内存或为变量 buff1 和 buff2 分配足够的内存?

#include <Arduino.h>
#include <stdlib.h>
#include <BluetoothSerial.h>
#include <WiFi.h>

#define btrcv_buffer_size 256

BluetoothSerial SerialBT;

typedef struct
{
  char *rtn_addr;
  int buff_len;
} Buffer_return;


Buffer_return* BTSerialRcvBuffer() {

  static int i = 0;
  static char rcv_buffer[ btrcv_buffer_size ];
  static Buffer_return Var;

  memset(rcv_buffer,0, btrcv_buffer_size);

  while (!SerialBT.available());
  delayMicroseconds(500);

  while(SerialBT.available()) {
    rcv_buffer[i] = SerialBT.read();
    i++;
  }
  rcv_buffer[i-1] = '\0';
  rcv_buffer[i-2] = '\0';
  SerialBT.flush();

  Var.rtn_addr = rcv_buffer;  //<------------Do I have to allocate memory for Var.rtn_addr?
  Var.buff_len = i-1;

  return &Var;
} 

void WiFiConfig() {

  //WiFi.printDiag(Serial);

  Serial.println("Enter SSID");
  Buffer_return *buff1 = BTSerialRcvBuffer();
  char *ssid = (char*) malloc((buff1->buff_len) * sizeof(char));  
  strcpy(ssid,buff1->rtn_addr);
  Serial.println(ssid);  

  Serial.println("Enter Password");
  Buffer_return *buff2 = BTSerialRcvBuffer();
  char *pass = (char*) malloc((buff2->buff_len) * sizeof(char));  
  strcpy(pass,buff2->rtn_addr);
  Serial.println(pass); 
  //Serial.println(buff2->buff_len);  


  free(ssid)
  free(pass);

  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Establishing connection to WiFi..");
    Serial.printf("Connection status: %d\n", WiFi.status());
  }  
}

void setup() {

  Serial.begin(115200);
  //WiFi.disconnect(true);
  SerialBT.begin("ESP32_INO"); //Bluetooth device name
  WiFi.mode(WIFI_STA);
  Serial.println("The device started, now you can pair it with bluetooth!");

  WiFiConfig();

  Serial.println("Connected to network");
  Serial.println(WiFi.macAddress());
  Serial.println(WiFi.localIP());
}

void loop() {

  }

输出:

Enter SSID
Airtel_5G             <----- prints fine!
Enter Password
                      <----- Problem!
Establishing connection to WiFi..
Connection status: 6
Establishing connection to WiFi..
Connection status: 6
Establishing connection to WiFi..
Connection status: 6

【问题讨论】:

  • while(SerialBT.available()) { rcv_buffer[i] = SerialBT.read(); i++; } rcv_buffer[i-1] = '\0'; rcv_buffer[i-2] = '\0'; 代码在任何时候都有问题i &gt; btrcv_buffer_sizei&lt;2
  • 是的 - 我没有看到任何安全机制来确保仅从字节流中加载完整的协议单元(似乎是以 \r\n 结尾的文本行)。我看不出这是如何可靠地工作的。
  • 而不是在BTSerialRcvBuffer() 中有一个静态缓冲区,然后在WiFiConfig() 中复制它,将缓冲区ssidpass 与最大值一起传递会更安全、更惯用长度作为BTSerialRcvBuffer() 的参数。在这个平台上可能会避免静态和动态内存分配。
  • 关于:SerialBT.flush();flush()' 操作是针对输出流,而不是输入流。

标签: c++ c arduino embedded esp32


【解决方案1】:

代码使用以下序列将输入数据复制到缓冲区。

  Buffer_return *buff1 = BTSerialRcvBuffer();
  char *ssid = (char*) malloc((buff1->buff_len) * sizeof(char));  
  strcpy(ssid,buff1->rtn_addr);

回想一下,字符串是 NUL 终止的,因此分配必须包含额外的字节!。对 malloc 调用的简单更新:

char *ssid = (char*) malloc((buff1->buff_len+1) * sizeof(char));

根据@lundin 的输入,不建议将 malloc 用于 Arduino。最好使用自动分配。

另请参阅:https://arduino.stackexchange.com/questions/682/is-using-malloc-and-free-a-really-bad-idea-on-arduino

  char ssid[buff2->buff_len+1] ;
  strcpy(ssid, buff2->rtn_addr) ;

更新 1:BTSerialRcvBuffer 中的错误

BTSerialRcvBuffer 对许多变量使用静态,包括i。回想一下,静态变量被初始化一次(在程序启动时)。建议从i 中删除“静态” - 修复初始化,因为不需要将其设为静态。

另外,不清楚为什么rcv_buffer的最后2个位置归零?

【讨论】:

  • 仍然存在同样的问题。我尝试分配更多内存,但还是一样。
  • 我建议您使用调试器单步调试代码——这样会更快。见代码中的注释
  • 目前我没有可用的 esp32 调试器。
  • 你能确保 BTSerialRcvBuffer() 中的所有内容都正常吗?
  • 感谢更新。您可能希望以 (i>=2) 和输入中包含 \r 和 \n 为条件进行归零。以防万一。祝项目的其余部分好运!
【解决方案2】:

您的代码中有很多不好的做法和缓慢的函数调用。请记住,这是一个 8 位 MCU,因此速度非常慢。需要解决的一些问题:

  • 不必每次都将 rx 缓冲区归零。只需跟踪其中有多少包含有效数据即可。 256 字节的 memset 调用非常昂贵。
  • 通常的做法是双缓冲 rx 缓冲区,以便一个缓冲区可以同时用于接收,另一个缓冲区用于解码。您不使用中断,因此这可能不是问题。无论如何,256 字节是很多 RAM,因此如果您需要存储这么多数据,双缓冲可能需要更好的 MCU。无论如何,我将在下面使用一个双缓冲示例来展示它是如何完成的。
  • delayMicroseconds(500); 除了将程序挂起 500 毫秒外,没有任何意义。删除它。
  • 错误:在接收期间不检查缓冲区溢出。

#define BT_RXBUF_SIZE 256

const char* BTSerialReceive (size_t* size_rec) 
{
  static char buf1 [BT_RXBUF_SIZE];
  static char buf2 [BT_RXBUF_SIZE];
  static char* buf = buf1;

  buf = (buf == buf1) ? buf2 : buf1; // swap rx buffers


  while (!SerialBT.available())
    ;


  size_t i=0;
  for(; SerialBT.available() && i<BT_RXBUF_SIZE; i++)
  {
    buf[i] = SerialBT.read();
  }
  buf[i] = '\0';

  SerialBT.flush();

  *size_rec = i;
  return buf;
} 

双缓冲区的指针交换消除了对memcpy/strcpy 的需要,这既慢又昂贵。如果您使用 UART 中断,则出于重入原因,您将不得不使用这样的设计。

您必须绝对避免的另一件事是malloc。这是缓慢而无意义的,see this。在使用嵌入式系统时,您必须始终使用固定长度的缓冲区和确定的内存量。摆脱malloc意味着您可以摆脱链接描述文件中的整个堆段,这将释放大量宝贵的RAM。

【讨论】:

  • 我有一种讨厌的感觉,延迟是对协议的尝试,这是一种非常糟糕的尝试确保缓冲区包含结尾带有 \r\n 的完整消息的方法。这将与您期望的一样可靠:((
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-05
  • 2017-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-27
相关资源
最近更新 更多