ESP8266从char数组获取垃圾数据

时间:2018-12-07 21:19:20

标签: c++ arduino iot esp8266

老实说,我在这里很茫然。我正在尝试将用户通过发布请求发送的SSID和密码存储在Flash EEPROM部分中。为此,我将从发布请求发送的数据转换为char数组并将其索引到EEPROM。 SSID可以正常运行,但是密码在进入EEPROM之前总是以垃圾数据结尾。 这是有问题的代码:

// Recieve data from the HTTP server
void changeConfig(String parameter, String value){
  int memoffset = 0;
  if(parameter == "ssid")
    memoffset = 0;
  else if(parameter == "pass")
    memoffset = 32;
  else
    return;
  #ifdef DEBUG
  Serial.println("Updating Data");
  Serial.print("Param: ");
  Serial.println(parameter);
  Serial.print("Value: ");
  Serial.println(value);
  #endif
  EEPROM.begin(64);
  char _data[sizeof(value)];
  value.toCharArray(_data, sizeof(value));
  for(int i = memoffset; i < memoffset + sizeof(value); i++)
  {
    #ifdef DEBUG
      Serial.print("addr ");
      Serial.print(i);
      Serial.print(" data ");
      Serial.println(_data[i]);
      #endif 
      EEPROM.write(i,_data[i]);
  }
  EEPROM.end();
}

和串行监视器输出:
发布参数:ssid,值:NetworkName
更新数据
参数:ssid
值:NetworkName
地址0数据N
地址1数据e
加法器2数据t
加法器3数据w
地址4数据
addr 5数据r
加法器6数据k
加法器7数据N
地址8数据a
地址9数据m
addr 10数据e
addr 11数据␀
张贴参数:通过,值:Networkpass
更新数据
参数:通过
值:Networkpass
addr 32数据|
addr 33数据(
地址34数据
addr 35数据?
加法器36数据L
addr 37数据␛
addr 38数据�
addr 39数据?
addr 40数据␁
addr 41数据␀
addr 42数据␀
addr 43数据␀

如您所见,当POST参数的名称为ssid时,它可以正常工作。另一方面,通过pass时,char数组只是充满了乱码。任何见解都会有所帮助。我在arduino环境中使用platformio。具有1M闪存的通用ESP01。 提前致谢。

2 个答案:

答案 0 :(得分:0)

您的代码有两个问题。

首先,您使用的sizeof错误。 Sizeof返回String对象的大小,但是您正在尝试获取所包含字符串的长度。 Sizeof不是正确的工具,相反,您应该使用String提供的任何API来读取字符串的大小。

下一个问题是偏移量的使用。以下代码段都是错误的:

char _data[sizeof(value)];
value.toCharArray(_data, sizeof(value));
for(int i = memoffset; i < memoffset + sizeof(value); i++)
{
  ...
  EEPROM.write(i,_data[i]);

您的i以32的偏移量开头,因此您尝试访问_data数组中索引为32的元素。但是_data会存储从索引0开始的字符,由于数组的长度实际上是12(通过访问索引为32的元素,所以String的sizeof总是12),所以您显然超出了它的范围在那儿找到垃圾(在C ++中,这称为未定义行为)。

最后但并非最不重要的一点是,C ++是一种极其复杂的语言,无法通过“反复试验”来学习。相反,您需要系统地学习,最好使用一本优秀的C ++书籍。这些列表可在此处找到:The Definitive C++ Book Guide and List

答案 1 :(得分:-1)

您使用的sizeof()错误。

sizeof() tells you the size of the object, at compile time.

尝试此实验-运行以下代码:

#include <Arduino.h>

void setup() {
  String x("");
  String y("abc");
  String z("abcdef");

  Serial.begin(115200);

  delay(1000);

  Serial.println(sizeof(x));
  Serial.println(sizeof(y));
  Serial.println(sizeof(z));
}

void loop() {
}

在我的ESP8266上,输出:

12
12
12

那是因为使用此开发环境需要12个字节来表示String对象(在不同的CPU和编译器上可能有所不同)。 String类动态分配存储空间,因此sizeof不能告诉您字符串本身有多长时间,只有对象的编译时大小。

对于String类,应使用其length()方法。您的台词:

char _data[sizeof(value)];
value.toCharArray(_data, sizeof(value));
for(int i = memoffset; i < memoffset + sizeof(value); i++)

应写为

char _data[value.length()];
value.toCharArray(_data, value.length());
for(int i = memoffset; i < memoffset + value.length(); i++)

有关更多信息,请参见documentation on the String class

您可能仍然会遇到字符串终止符的问题。 C和C ++以空字符'\ 0'终止char数组字符串,这为字符串的长度增加了一个额外的字节。因此,您的代码更有可能是:

void changeConfig(String parameter, String value){
  int memoffset = 0;
  if(parameter == "ssid")
    memoffset = 0;
  else if(parameter == "pass")
    memoffset = 33;
  else
    return;
  #ifdef DEBUG
  Serial.println("Updating Data");
  Serial.print("Param: ");
  Serial.println(parameter);
  Serial.print("Value: ");
  Serial.println(value);
  #endif
  EEPROM.begin(66);
  char _data[value.length() + 1];
  value.toCharArray(_data, value.length() + 1);
  for(int i = memoffset; i < memoffset + value.length() + 1; i++)
  {
    #ifdef DEBUG
      Serial.print("addr ");
      Serial.print(i);
      Serial.print(" data ");
      Serial.println(_data[i]);
      #endif 
      EEPROM.write(i,_data[i]);
  }
  EEPROM.end();
}

允许字符串终止符在32个字符的SSID和密码中正常工作。但是,破坏代码的根本问题是sizeof的错误使用。