【问题标题】:ESP8266 getting garbage data from char arrayESP8266 从 char 数组中获取垃圾数据
【发布时间】:2018-12-07 21:19:19
【问题描述】:

老实说,我在这里不知所措。我正在尝试在闪存 EEPROM 部分中存储用户通过发布请求发送的 SSID 和密码。为此,我将从 post 请求发送的数据转换为 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
值:网络名称
addr 0 数据 N
地址 1 数据 e
地址 2 数据 t
地址 3 数据 w
addr 4 数据 o
地址 5 数据 r
地址 6 数据 k
地址 7 数据 N
地址 8 数据 a
地址 9 数据 m
地址 10 数据 e
地址 11 数据 ␀
发布参数:pass,值:Networkpass
更新数据
参数:通过
价值:网络通行证
地址 32 数据 |
地址 33 数据 (
地址 34 数据 �
addr 35 数据?
addr 36 数据 L
地址 37 数据 ␛
地址 38 数据 �
addr 39 数据?
地址 40 数据 ␁
地址 41 数据 ␀
地址 42 数据 ␀
地址 43 数据 ␀

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

【问题讨论】:

  • 我不确定 memoffset 应该做什么。当您打印 value 时,很明显它是写在那里没有任何偏移的。另外,我不知道“字符串”是什么,但我很难相信 sizeof() 是你需要的。
  • 使用 memoffset 是因为 SSID 占用了 EEPROM 的前 32 个字节(0-31),而密码则占用了接下来的 32 个字节(32-63)。 String 是一个类,很像 char 数组,但它可以在没有大小的情况下进行初始化,它是接收到 POST 请求时从 HTTP 服务器回调返回的类型。 Size of 用于将字符串“转换”为 char 数组,因为这是写入 EEPROM 所需要的
  • 但是您也在使用偏移量读取您的值!您我从偏移量开始,并且您正在访问偏移量处的值。这不可能。
  • @Daniel -- 您应该在调试输出中打印了sizeof(value)。当您说char _data[sizeof(value)]; 时,这可能会让您更清楚地表明出了什么问题。您会看到,无论value 包含什么字符串数据,sizeof 都不会改变。

标签: c++ arduino iot esp8266


【解决方案1】:

您的代码有两个问题。

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

下一个问题是您对偏移量的使用。以下代码sn -p 全部错误:

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(String 的 sizeof 始终为 12),通过访问索引为 32 的元素,你超出了它的界限,显然在那里找到了垃圾(用 C++ 的说法,它被称为未定义的行为)。

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

【讨论】:

  • 谢谢,来自高级语言,有时我应该使用哪个函数令人困惑。另外哇,我一定有一个巨大的脑残,没有发现试图读取 i dex 数组。感谢您的清晰解释!
【解决方案2】:

您错误地使用了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

这是因为使用此开发环境来表示String 对象需要 12 个字节(在不同的 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

【讨论】:

  • 感谢您的解释!
猜你喜欢
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-04
  • 1970-01-01
  • 1970-01-01
  • 2019-08-03
  • 2014-07-20
相关资源
最近更新 更多