關于Arduino架構下開發ESP8266+Blinker應用EEPROM不能正常使用的問題
最近在嘗試用ESP8266+Blinker開發一個語音控制的智能開關,涉及到一些使用者資料的存儲,嘗試加入之前寫的EEPROM代碼後發現原來正常的代碼到這卻不能跑了。
踩了整整一天的坑,網上很多人也遇到了類似的問題,但沒有給出完整的解決方案,在這裡總結一下。
問題的核心是,Blinker庫也使用了EEPROM。
在每次使用EEPROM前都要調用begin方法
這是一個比較隐蔽但非常坑的問題。
先貼一個Arduino的EEPROM例程(删掉了所有注釋),相信這是很多人的EEPROM啟蒙教材:
#include <EEPROM.h>
int address = 0;
byte value;
void setup() {
Serial.begin(115200);
EEPROM.begin(512);
}
void loop() {
value = EEPROM.read(address);
Serial.print(address);
Serial.print("\t");
Serial.print(value, DEC);
Serial.println();
address = address + 1;
if (address == 512) {
address = 0;
}
delay(500);
}
瞅着沒啥問題,運作起來也沒啥問題是吧。
那我們看看Blinker裡邊對EEPROM的操作,挑裡邊
BlinkerESPMQTTAT
這個類來看看,隻關注EEPROM的操作就可以:
class BlinkerESPMQTTAT : public BlinkerApi
{
public :
void begin()
{
BApi::begin();
::delay(100);
EEPROM.begin(BLINKER_EEP_SIZE);
EEPROM.get(BLINKER_EEP_ADDR_SERIALCFG, serialSet);
uint32_t ss_baud = serialSet >> 8 & 0x00FFFFFF;
ss_cfg = serConfig();
if (ss_baud != 300 || ss_baud != 1200 || ss_baud != 2400 ||
ss_baud != 4800 || ss_baud != 9600 || ss_baud != 19200 ||
ss_baud != 38400 || ss_baud != 57600 || ss_baud != 74880 ||
ss_baud != 115200 || ss_baud != 230400 || ss_baud != 250000 ||
ss_baud != 500000 || ss_baud != 1000000 || ss_baud != 2000000)
{
serialSet = BLINKER_SERIAL_DEFAULT;
ss_baud = 9600;
ss_cfg = SERIAL_8N1;
EEPROM.put(BLINKER_EEP_ADDR_SERIALCFG, serialSet);
}
EEPROM.commit();
EEPROM.end(); // 注意這裡!!!!!!!!!!!!!!!!!!!!
Serial.begin(ss_baud, ss_cfg);
Transp.serialBegin(Serial, true);
transport(Transp);
BApi::atBegin();
// strcpy(BApi::_authKey, this->conn.authKey().c_str());
// strcpy(BApi::_deviceName, this->conn.deviceName().c_str());
BApi::loadTimer();
BLINKER_LOG(BLINKER_F("BLINKER_MQTT_AT initialized..."));
}
private :
BlinkerMQTTAT Transp;
};
Blinker庫在調用完EEPROM後,用了
end()
方法!
他在玩一種很新的東西,這會導緻什麼呢,我們去EEPROM的源碼裡看看這個方法做了什麼:
bool EEPROMClass::end() {
bool retval;
if(!_size) {
return false;
}
retval = commit();
if(_data) {
delete[] _data;
}
_data = 0;
_size = 0; // 注意這裡!!!!!!!!!!!!!!!!!!!!
_dirty = false;
return retval;
}
這個方法把
EEPROM::_size
清零了!
這又會導緻什麼呢,我們再看看
EEPROM::read
:
uint8_t EEPROMClass::read(int const address) {
if (address < 0 || (size_t)address >= _size) { // 注意這裡!!!!!!!!!!!!!!!!!!!!
DEBUGV("EEPROMClass::read error, address %d > %d or %d < 0\n", address, _size, address);
return 0;
}
if (!_data) {
DEBUGV("EEPROMClass::read without ::begin\n");
return 0;
}
return _data[address];
}
address
大于
_size
的時候,讀的所有結果都會變成0!
避開Blinker庫使用的EEPROM位址
程式正常運作後,我們檢視Blinker的log會發現:
[7150] =======================================================
[7151] =========== Blinker Auto Control mode init! ===========
[7160] EEPROM address 0-1279 is used for Auto Control!
[7168] ======= PLEASE AVOID USING THESE EEPROM ADDRESS! ======
[7177] =======================================================
[34120]
===========================================================
================== Blinker Timer loaded! ==================
EEPROM address 1536-2431 is used for Blinker Timer!
========= PLEASE AVOID USING THESE EEPROM ADDRESS! ========
===========================================================
可見Blinker庫使用了
0-1279
和
1536-2431
的EEPROM位址,我們使用的時候避開即可。