ESP8266 从字符数组中获取垃圾数据
ESP8266 getting garbage data from char array
老实说,我在这里不知所措。我正在尝试存储用户通过闪存 EEPROM 部分中的 post 请求发送的 SSID 和密码。为此,我将从 post 请求发送的数据转换为字符数组并将其索引到 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();
}
串行监视器输出:
Post 参数:ssid,值:网络名称
更新数据
参数:ssid
值:网络名称
地址 0 数据 N
地址 1 数据 e
地址 2 数据 t
地址 3 数据 w
地址 4 数据 o
地址 5 数据 r
地址 6 数据 k
地址 7 数据 N
地址 8 数据 a
地址 9 数据 m
地址 10 数据 e
地址 11 数据 ␀
Post参数:pass,值:Networkpass
更新数据
参数:通过
值:网络通行证
地址 32 数据 |
地址 33 数据 (
地址 34 数据 �
地址 35 数据 ?
地址 36 数据 L
地址 37 数据 ␛
地址 38 数据 �
地址 39 数据 ?
地址 40 数据 ␁
地址 41 数据 ␀
地址 42 数据 ␀
地址 43 数据 ␀
可以看到,当POST参数的名字是ssid时,是可以正常工作的。另一方面,通过 pass 时,char 数组只是充满了乱码。任何见解都会有所帮助。我在 arduino 环境中使用 platformio。带 1M 闪存的通用 ESP01。
提前致谢。
您使用 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
class 动态分配存储空间,因此 sizeof
无法告诉您字符串本身有多长,只能告诉您对象的编译时大小。
对于String
class,你应该使用它的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
.
的不正确使用
您的代码有两个问题。
首先,您使用 sizeof
不正确。 Sizeof returns String
对象的大小,但您正在尝试获取包含的字符串的长度。 Sizeof 不是正确的工具,您应该使用 API String
提供的任何内容来读取字符串的大小。
下一个问题是您对偏移量的使用。以下代码片段全部错误:
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++ 是一门极其复杂的语言,'trial and error' 学不会。相反,您需要有条不紊地学习,最好使用一本优秀的 C++ 书籍。这些列表可以在这里找到:The Definitive C++ Book Guide and List
老实说,我在这里不知所措。我正在尝试存储用户通过闪存 EEPROM 部分中的 post 请求发送的 SSID 和密码。为此,我将从 post 请求发送的数据转换为字符数组并将其索引到 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();
}
串行监视器输出:
Post 参数:ssid,值:网络名称
更新数据
参数:ssid
值:网络名称
地址 0 数据 N
地址 1 数据 e
地址 2 数据 t
地址 3 数据 w
地址 4 数据 o
地址 5 数据 r
地址 6 数据 k
地址 7 数据 N
地址 8 数据 a
地址 9 数据 m
地址 10 数据 e
地址 11 数据 ␀
Post参数:pass,值:Networkpass
更新数据
参数:通过
值:网络通行证
地址 32 数据 |
地址 33 数据 (
地址 34 数据 �
地址 35 数据 ?
地址 36 数据 L
地址 37 数据 ␛
地址 38 数据 �
地址 39 数据 ?
地址 40 数据 ␁
地址 41 数据 ␀
地址 42 数据 ␀
地址 43 数据 ␀
可以看到,当POST参数的名字是ssid时,是可以正常工作的。另一方面,通过 pass 时,char 数组只是充满了乱码。任何见解都会有所帮助。我在 arduino 环境中使用 platformio。带 1M 闪存的通用 ESP01。 提前致谢。
您使用 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
class 动态分配存储空间,因此 sizeof
无法告诉您字符串本身有多长,只能告诉您对象的编译时大小。
对于String
class,你应该使用它的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
.
您的代码有两个问题。
首先,您使用 sizeof
不正确。 Sizeof returns String
对象的大小,但您正在尝试获取包含的字符串的长度。 Sizeof 不是正确的工具,您应该使用 API String
提供的任何内容来读取字符串的大小。
下一个问题是您对偏移量的使用。以下代码片段全部错误:
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++ 是一门极其复杂的语言,'trial and error' 学不会。相反,您需要有条不紊地学习,最好使用一本优秀的 C++ 书籍。这些列表可以在这里找到:The Definitive C++ Book Guide and List