ESP8266 Non-OS SDK开发探坑之四-用户非易失参数安全存储到flash

Starting with ESP8266 — Light a LED

Starting with ESP8266 (2)–Touch to control relay status-circuit design & electronic components selection

Starting with ESP8266(3) — Touch to control Relay-Programming & PCB design

原博客链接:

http://www.straka.cn/blog/start-esp8266-4-flash-parameter-securely-save-load/

由于ESP8266系统可以自动保存系统参数到flash完成上电自动选择wifi工作模式和wifi连接参数等,但用户有时也需要保存一些非易失的数据,这就需要用户将信息写入flash,如果进一步考虑,写入flash的时候必须整扇区(4kb)擦除,然后再写入,所以存在一定的时长,如果为了数据完整性、安全性考虑,就必须考虑写入的时候突然掉电的风险。官方对这个问题是有说明的。见文档:

https://www.espressif.com/zh-hans/support/download/documents?keys=&field_type_tid%5B%5D=14

ESP8266 Flash 读写说明

本文先讨论了用户参数存放区域问题,然后调用系统api进行数据安全读写。

首先根据官方的文档

ESP8266 SDK 入门指南

或者结合博文【ESP8266开发入坑1—-点亮LED】提到的flash布局说明,

不支持云端升级(即NON-FOTA)的用户数据在eagle.irom0text.bin之后,而改文件的大小和保存地址在指南里有:

结合这两个文件,我们就可以计算出用户参数区的首地址:

比如512-non-fota,用户数据区首地址 = eagle.irom0text.bin的首地址 0x10000 + 大小 368*1024 = 0x6C000,

而实际用的是扇区数而不是字节地址数,所以需要0x6C000/4096 = 0x6C,

这里计算注意16-10进制转换。同理可以计算出fota布局下的用户参数区地址。

而用户参数区的大小除了首地址外还需要结尾地址决定,这个其实就是RF-CAL区的首地址,也就是blank.bin的存放地址。

我这里都算好了,并利用 user_rf_cal_sector_set里对flash布局的swtich顺便完成了该地址计算:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

uint32 ICACHE_FLASH_ATTR

user_rf_cal_sector_set(void)

{

enum flash_size_map size_map = system_get_flash_size_map();

uint32 rf_cal_sec = 0;

bool bFota;

#ifdef FOTA

bFota = true;

#else

bFota = false;

#endif

switch (size_map) {

case FLASH_SIZE_4M_MAP_256_256:

rf_cal_sec = 128 - 5;

UserParamStartSect = bFota? 0x3C:0x6C;

break;

case FLASH_SIZE_8M_MAP_512_512:

rf_cal_sec = 256 - 5;

UserParamStartSect = bFota? 0x7C:0xCC;

break;

case FLASH_SIZE_16M_MAP_512_512:

rf_cal_sec = 512 - 5;

UserParamStartSect = bFota? 0x7C:0xD0;

break;

case FLASH_SIZE_16M_MAP_1024_1024:

rf_cal_sec = 512 - 5;

UserParamStartSect = bFota? 0xFC:0xD0;

break;

case FLASH_SIZE_32M_MAP_512_512:

rf_cal_sec = 1024 - 5;

UserParamStartSect = bFota? 0x7C:0xD0;

break;

case FLASH_SIZE_32M_MAP_1024_1024:

rf_cal_sec = 1024 - 5;

UserParamStartSect = bFota? 0xFC:0xD0;

break;

case FLASH_SIZE_64M_MAP_1024_1024:

rf_cal_sec = 2048 - 5;

UserParamStartSect = bFota? 0xFC:0xD0;

break;

case FLASH_SIZE_128M_MAP_1024_1024:

rf_cal_sec = 4096 - 5;

UserParamStartSect = bFota? 0xFC:0xD0;

break;

default:

rf_cal_sec = 0;

UserParamStartSect = 0;

break;

}

return rf_cal_sec;

}

好了,有了参数保存地址,我们再看看参数的保存和加载吧。

官方SDK API文档有说明其保护机制:

https://www.espressif.com/sites/default/files/2C-ESP8266_Non_OS_SDK_API_Reference__CN.pdf

这个官方有API的我就不造轮子了,如果有更多要求的可以自己实现。

这里我就定义了通用方法放到FlashParam头和实现文件中

先是定义要保存的参数结构体:

1

2

3

4

5

6

7

8

9

10

11

struct FlashProtectParam{

uint16 ConfHolder;

//uint32 ConfMagic;

uint8 WorkStatus;

uint8 Domain;  //0: use ip,  1:use domain

uint16 RemotePort;

union{

struct ip_addr IP;

uint8 Domain[DOMAIN_LEN+1];

}RemoteAddr;

};

其中ConfHolder是方便调试用的, 在user_config.h里有定义这个宏:

1

#define CONF_HOLDER         0x2A15

每当修改完代码烧录到芯片,如果修改了CONF_HOLDER宏的值,那么从flash读取参数的时候先比对ConfHolder,不一样就会重置系统参数,或者将宏的参数保存,如果不修改,那么就不用重新配置,新烧录的程序继续用之前芯片保存的数据。

所以加载的代码就复杂些了。

WorkStatus的每一位标记一个工作状态信息,方便上电恢复工作状态,这里我的定义是:

1

2

3

4

5

6

7

#define WEB_SERV_BIT 0x01

#define TCP_SERV_BIT 0x02

#define MQTT_CLIENT_BIT 0x04

#define WIFI_CONF_BIT 0x10

#define REMOTE_SERV_CONF_BIT 0x20

#define MQTT_CONF_BIT 0x40

分别定义了是否开启web server 、tcp client需要连接的远程服务器信息是否配置等。

RemoteAddr保存了tcp-client需要连接的远程服务器地址信息,RemotePort保存了tcp-client需要连接的远程服务器端口信息,Domain标记远程服务器信息是域名还是ip,其实这个标记有点多余,因为可以对RemodeAddr进行STR->IP的转换或验证,如果失败就按域名进行DNS解析。

后文探坑7就对DNS解析做了解释。

flash参数的加载:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

bool ICACHE_FLASH_ATTR

LoadFlashProtParam(){

if(!system_param_load(SYS_PROTECT_SECT,0, &stFlashProtParam, sizeof(struct FlashProtectParam))){

stFlashProtParam.WorkStatus = WEB_SERV_BIT;

system_param_save_with_protect(SYS_PROTECT_SECT,&stFlashProtParam,sizeof(struct FlashProtectParam));

TRACE("load flash protected param failed!\r\n");

}

if(stFlashProtParam.ConfHolder != CONF_HOLDER){

stFlashProtParam.ConfHolder = CONF_HOLDER;

stFlashProtParam.WorkStatus = WEB_SERV_BIT;

system_param_save_with_protect(SYS_PROTECT_SECT,&stFlashProtParam,sizeof(struct FlashProtectParam));

wifi_set_opmode(SOFTAP_MODE); //

struct softap_config config;

wifi_softap_get_config(&config); // Get config first.

//generate special ssid to avoid conflict

uint8 ssid[32];

os_memset(ssid, 0, 32);

os_memcpy(ssid, "ESP_", 4);

uint8 APMac[6];

wifi_get_macaddr(SOFTAP_IF,APMac);

uint8 i;

for(i=0;i<6;i++){

uint8 tmp = APMac[i]%62;

if(tmp<26){

ssid[4+i] = tmp+'a';

}else if(tmp<52){

ssid[4+i] = tmp+'A'-26;

}else{

ssid[4+i] = tmp+'0'-52;

}

}

os_memset(config.ssid, 0, 32);

os_memcpy(config.ssid, ssid, os_strlen(ssid));

os_memset(config.password, 0, 64);

os_memcpy(config.password, INIT_AP_PASSWD, sizeof(INIT_AP_PASSWD));

config.authmode = AUTH_WPA_WPA2_PSK;

config.ssid_len = 0;// or its actual length, read until string end

config.max_connection = 4; // how many stations can connect to ESP8266 softAP at most.max 4

//config.channel = 1; //support 1~13

//config.ssid_hidden = 0;//default 0

//config.beacon_interval = 100;//100~60000ms, default 100

wifi_softap_set_config(&config);// Set ESP8266 softap config, ensure to call this func after softap enabled, execute immediatelly

}

TRACE("flash protected param:\r\nWork Status:%02x\r\n",stFlashProtParam.WorkStatus);

if(stFlashProtParam.Domain){

TRACE("remote domain:%s:%d\r\n",stFlashProtParam.RemoteAddr.Domain,stFlashProtParam.RemotePort);

}else{

TRACE("remote:"IPSTR":%d",IP2STR(&stFlashProtParam.RemoteAddr.IP.addr),stFlashProtParam.RemotePort);

}

return true;

}

函数如上文完成了CONF_HOLDER的比对和重置,并多此一举的对ssid进行了设置,主要是为了防止固定ssid的重合导致多个设备无法区分,所以根据mac地址映射到了26个字母大小写和10个数字的可视字符,当然base64编码也可以。

密码当然就设成固定的初始密码,如大家有别的需求可以自行替换。

参数保存特别简单:

1

2

3

4

bool ICACHE_FLASH_ATTR

SaveFlashProtParam(){

return system_param_save_with_protect(SYS_PROTECT_SECT,&stFlashProtParam,sizeof(struct FlashProtectParam));

}

stFlashProtParam毕竟全局都要用为了方便就弄成了全局变量。

此外,为了大家DIY方便,我将自定义的flash写入方法封装了方便的版本,主要是考虑到原始的FLASH读写接口是按扇区来的,读取还好,写入需要整扇区擦除,然后写入,那么,很容易造成误删数据,所以该函数就将每次擦除和写入操作合并,先读取出整个扇区保存到临时对象,然后修改临时对象,然后正扇区写入,这里有些问题,比如内存空间问题,需要一下子开辟整扇区4KB的栈或堆空间,而且效率也是个问题,用前需要细致考虑下。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

SpiFlashOpResult ICACHE_FLASH_ATTR

SPIFlashEraseWrite(uint32 sect,uint32 offset,uint32 *src, uint32 size){

if(offset+size>4096){

return SPI_FLASH_RESULT_ERR;

}

uint32 data[4096];

/*

uint32 *data = (uint32 *)os_malloc(4096);

if(data==NULL) return SPI_FLASH_RESULT_ERR;

*/

SpiFlashOpResult ret;

ret = spi_flash_read(sect*4096,data,4096);

if(ret!=SPI_FLASH_RESULT_OK) return ret;

os_memcpy(data+offset,src,size);

spi_flash_erase_sector(sect);

return spi_flash_write(sect*4096,data,4096);

}

代码见:

https://github.com/atp798/BlogStraka/tree/master/ESP8266_NONOS_SDK-2.2.1-WebServer/

原博客:

http://www.straka.cn/blog/start-esp8266-4-flash-parameter-securely-save-load/

ESP8266 Non-OS SDK开发探坑之四-用户非易失参数安全存储到flash相关推荐

  1. GD32F130之FMC用户非易失存储器

    广义上的EEPROM分类 PROM:即Programmable ROM的缩写,也就是可编程ROM.可编程的意思通俗说就是可以写入自定义的数据.PROM出厂后,用户可以进行"一次"数 ...

  2. 【ESP8266】NONOS SDK开发,发送HTTP请求

    网络方面不是很懂,可能描述有一点不准确. 主要是通过ESP8266,在NONOS-SDK环境下,用URL地址,发出HTTP请求,接收并处理信息. 假设已经大致了解厂家提供的SDK,以及Eclipse开 ...

  3. 【ESP8266】NONOS SDK开发,串口发送、接收与中断

    ESP8266感觉要成现在物联网的黑马了,集成WiFi功能.能编程,还便宜,确实挺6的. 就是能提供的资料太少,感觉官网上的远远不够用,这个串口收发与中断折腾了我是在太久,在这里小的就分享一下自己的经 ...

  4. WiFi开发|ESP8266模组SDK开发项目之智能开关

    智能开关项目 1. 智能开关 智能开关由WiFi模组和继电器组成:本实例中利用ESP8266模组作为一个TCP Server,通过STA模式将ESP8266连接到路由器中:PC或者手机连接到相同的路由 ...

  5. WiFi开发|ESP8266模组SDK开发之FOTA

    ESP8266模组SDK开发之远程固件升级 1. FOTA FOTA(Firmware Over-The-Air)移动终端的空中下载软件升级,指通过云端升级技术,为具有连网功能的设备:例如手机.平板电 ...

  6. ESP8266 Non-OS SDK 开发之旅 基础篇① 初识 Non-OS SDK,史上超级详细手把手教小白20分钟快速搭建SDK软件开发环境,完成第一个例子Hello World!

    文章目录 1.前言 2. SDK概述 2.1 SDK使用流程 2.2 ESP8266 HDK -- 硬件开发工具 2.3 ESP8266 SDK -- 软件开发工具包 2.3.1 Non-OS SDK ...

  7. iPhone开发进阶(1) --- 深入理解iPhone OS/SDK与Objective-C 2.0

    iPhone开发进阶(1) --- 深入理解iPhone OS/SDK与Objective-C 2.0 工欲善其事,必先利其器.在开发iPhone应用程序的时候,深入理解iPhone OS/SDK与O ...

  8. WiFi开发|ESP8266模组SDK开发之SNTP协议

    ESP8266模组SDK开发之SNTP协议 1. NTP和SNTP NTP 是网络时间协议(Network Time Protocol),是用来同步网络设备(如计算机.手机)的时间的协议 SNTP由N ...

  9. WiFi开发|ESP8266模组SDK开发之Station和AP连接

    ESP8266模组SDK开发之Station和AP连接 WiFi是由无线接入点AP(Access Point) .站点(Station)等组成的无线网络. STA:Station(站点),每个连接到无 ...

最新文章

  1. TensorRT Samples: MNIST API
  2. java同时执行同一个方法吗_java 返回结果的同时执行另一个方法
  3. Maven中使用tomcat:run 出现错误 org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException...
  4. lazada本地店怎么开通,需要什么条件?
  5. MVCC在MySQL的InnoDB中的实现
  6. 【spring源码分析】IOC容器初始化(二)
  7. LinearLayout和RelativeLayout 区别
  8. [课程相关]homework-06
  9. verilog 生成块_如何高效的编写Verilog——终极版
  10. 愿望满足系统 1020 分支与循环控制
  11. 出差费用管理模块的几个问题
  12. fabric8 java api,kubectl apply -f equivalent in fabric8 java api | 易学教程
  13. kali字典_Web渗透测试——暴力破解字典制作工具的使用2
  14. 排队系统拥塞控制的位置
  15. Python制作简单的学生成绩管理系统
  16. 说话人识别中的数据预处理和数据增强
  17. 【CTFSHOW】web入门 NodeJS
  18. java blueprint_OSGI Blueprint入门之四
  19. 小波分析——1. 初识小波分析
  20. 维特智能姿态传感器WT901C-485调试流程

热门文章

  1. Webp转成其他图片格式
  2. WORD系列教程-多级编号制作合同
  3. 养生保健指南杂志养生保健指南杂志社养生保健指南编辑部2022年第46期目录
  4. 【题库】上海市学校心理咨询师考试-普通心理学-考点解析 2.2 神经系统
  5. 后端配合前端上下箭头调整记录顺序
  6. 流量卡之家:华为禁令殃及高通,下调全年智能手机出货预估1亿部
  7. 侠盗飞车罪恶都市完全秘籍
  8. 【Python网络爬虫】企查查高级搜索及批量查询接口爬虫
  9. 如何通过虚拟资料赚取人生中的第一桶金。
  10. Linux下rsync设置+inotify设置文件同步