概述
- 本系列博客学习由非官方人员 半颗心脏 潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途。如有不对之处,请留言,本人及时更改。
系列一:ESP32系列模组基础学习系列笔记
1、 爬坑学习新旅程,虚拟机搭建esp32开发环境,打印 “Hellow World”。
2、 巧用eclipes编辑器,官方教程在在Windows下搭建esp32开发环境,打印 “Hellow World”。
3、 认识基本esp32的GPIO接口,开始点亮您的第一盏 LED和中断回调实现按键功能 。
4、体会esp32的强大的定时器功能, 实现定时2s闪烁一盏LED灯。
5、接触实践esp32的pwm宽度脉冲功能, 实现呼吸效果闪烁一盏LED灯。
6、smartConfig和微信airKiss在esp32的实现,一键配网轻松快捷连接路由器。
7、利用GPIO中断做一个按键的短按和长按的回调事件,再也无须担心触发源。
8、esp32上实现本地 UDP 客户端和服务端角色,在局域网内实现通讯。
9、esp32上实现本地 TCP 客户端和服务端角色,可断线重连原路返回数据。
10、乐鑫esp32 SDK编程利用rmt驱动ws2812七彩灯,实现彩虹渐变效果。
11、入门 乐鑫esp-adf 音频框架开发,esp32造一个蓝牙耳机,实现切换歌曲,获取歌曲信息等功能。
12、开源一个微信公众号airkiss配网esp32以及局域网发现功能的工程,分享一个airkiss配网小工具。
13、esp32 内置 dns 服务器,无需外网访问域名返回指定网页。
14、esp32 sdk编程实现门户强制认证,连接esp32热点之后,自动强制弹出指定的登录界面。
15、认识本地离线语音唤醒识别框架 esp-skainet ,实现较低成本的硬件语音本地识别控制。
16、学习本地语音唤醒离线识别框架 esp-skainet ,如何修改唤醒词? 如何自定义命令词?如何做意图动作?
17、全网首发,乐鑫esp32 sdk直连京东微联·小京鱼 · IoT开放平台,实现叮咚音响语音智能控制。
18、入门京东微联·小京鱼的控制面板H5开发,读懂vue语法,做自己的控制页面。
19、重磅开源,如何在微信小程序上ble蓝牙配网esp32,blufi的那些事!
20、一篇好文,开发过程中编译esp32固件太大,无法正常启动?教你如何自定义分区表partitions.csv。
21、 esp32蓝牙配网blufi的高度封装,集成简单、使用简单、容易上手,提高开发效率!
22、讨论下程序员 “青春饭” 那些事,分享在esp32实现多种加密算法md5 |AES CBC-ECB| Sha1 | Sha256 等!
23、安信可 esp32-a1s 音频开发板移植最新 esp-adf 音频框架,小试牛刀如何实现在线文字转语音播放。
25、分享在 esp32 SDK实现冷暖光色温平滑调节的封装,轻松集成到您的项目去。
26、分享下如何在window下使用CMake编译,编译速度提高传统 make 编译一个档次,支持 ESP32 和 ESP32-C3。
27、windows10平台下自带的Linux安装 ESP8266/ESP32 环境,再无需额外安装虚拟机了。
28、熟悉自定义分区表 partition,拉取阿里云对象存储的单片机/图片等较大文件保存在特定的存储位置,并读取出来做完整性校验。
系列二:ESP32-Camera 摄像头开发板系列笔记
1、安信可 ESP32-Cam 摄像头开发板二次开发 C SDK编程,实现MQTT远程拍照传输到私有服务器。
2、安信可 ESP32-Cam 摄像头开发板二次开发 C SDK编程,实现本地视频流监控。
3、安信可 ESP32-Cam 摄像头开发板二次开发 C SDK编程,拍照图片通过有线串口传到上位机PC端。
系列三:ESP32-C3 模组系列笔记
1、【蓝牙Mesh笔记 ①】ESP32-C3 模组上实现天猫精灵蓝牙 BLE Mesh AliGenie 接入,无需WiFi 连接也可以实现天猫精灵语音控制。
系列四:ESP32-S3 模组系列笔记
1、安信可 ESP32-S3 模组上驱动摄像头 OV2640,实现远程拍照并 HTTP 传输到阿里云对象存储OSS,并显示在微信小程序上。。
系列四:ESP32模组系列笔记 LVGL LittlevGL
1、【LittlevGL ESP32 学习笔记 ①】移植最新的 LVGL 到安信可ESP32C3模组,显示一个二维码。
文章目录
- 一、前言
- 整体逻辑思维
- 二、开发中所需掌握的知识点
- 1)自定义分区表
- Name
- Type
- SubType
- offset & size
- 2)代码中读取自定义分区表的内容
- 3)数据完整性校验?
- 三、代码走读
- 3.1 获取阿里云的文件头部信息
- 3.2 下载数据到flash
- 3.3 从flash读取数据做校验
- 另外,不要把我的博客作为学习标准,我的只是笔记,难有疏忽之处,如果有,请指出来,也欢迎留言哈!
一、前言
在开发一些模组+单片机MCU项目发现 ,在使用WiFi模组在开发对接MCU时候,MCU(比如STM32)的固件需要OTA升级时候,但是我们商用经常会遇到这样的问题:
1)希望WiFi模组能拉取MCU的固件时候,并且校验此固件的完整性,可对比MD5数值。
2)希望WiFi模组能拉取完整MCU固件后,再读取出去通过UART/SPI形式发送给MCU。
抑或,我在开发项目时候,因某些原因需要更新本地文件,比如HTTPS的域名证书,都是需要把网络上的较大文件保存在特定的存储位置,并且读取出来做完整性校验,进一步使用。
所以,这一期的代码分享,就带给大家了。愿大家平时摁住急功近利的心,成功需要厚积薄发,做技术也是如此。
整体逻辑思维
二、开发中所需掌握的知识点
1)自定义分区表
既然要保存文件,那么就需要自行处理FLASH里面的分区情况了,我之前的文章也提到这点:
开发过程中编译esp32固件太大,无法正常启动?教你如何自定义分区表partitions.csv。
上述只是针对运行的固件大小说明,那么我们如何自定义存储文件的位置大小呢?先看我定义的分区表:
# Name,
Type, SubType, Offset,
Size, Flags
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
nvs,
data, nvs,
0x9000,
0x4000
otadata,
data, ota,
0xd000,
0x2000
phy_init, data, phy,
0xf000,
0x1000
ota_0,
0,
ota_0,
0x10000, 0xF0000
ota_1,
0,
ota_1,
0x110000,0xF0000
custom_data,
0x40, 0,
,
1024K,
主要的分区表说明,见官方文档:https://docs.espressif.com/projects/esp-idf/zh_CN/v4.3.1/esp32/api-guides/partition-tables.html
分区表中的每一列都有名字,类型(app,data或者其他),子类型,在 flash 中的偏移量(分区的加载地址)和对应的大小。通过名字(Name),类型(Type)和子类型(SubType)就可以读写相应的模块。
Name
Name 字段可以是任何有意义的名称,但不能超过 16 个字符(之后的内容将被截断),该字段对 ESP32 并不是特别重要。
Type
Type 字段可以指定为 app (0) 或者 data (1),也可以直接使用数字 0-254(或者十六进制 0x00-0xFE)。注意,0x00-0x3F 是预留给 esp-idf 的核心功能,如果应用程序需要保存数据,需要在 0x40-0xFE 内添加一个自定义分区类型。
SubType
SubType 字段长度为 8 bit,内容与具体 Type 有关,具体可以参考 ESP-IDF SubType。
offset & size
分区若为指定偏移地址,则会紧跟着前一个分区之后开始。若此分区为首个分区,则将紧跟着分区表开始。
对于 ESP32, app 分区的偏移地址必须 64K(0x10000) 对齐,对于 ESP8266,app 分区的偏移地址必须要与 0x1000 (4K) 对齐。
app 分区的大小和偏移地址可以采用十进制数、以 0x 为前缀的十六进制数,且支持 K 或 M 的倍数单位(分别代表 1024 和 1024*1024 字节)。
2)代码中读取自定义分区表的内容
具体也可以参考:examples/storage/partition_api/partition_ops 具体的例程,下面我也注释了下。
void app_main(void)
{
/*
* This example uses the partition table from ../partitions_example.csv. For reference, its contents are as follows:
*
*
nvs,
data, nvs,
0x9000,
0x6000,
*
phy_init,
data, phy,
0xf000,
0x1000,
*
factory,
app,
factory,
0x10000, 1M,
*
storage,
data, ,
, 0x40000,
*/
// 在分区表找到自定义map的信息,这里是通过 Type这个字段来获取,当然了,你也可以通过SubType获取。
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "storage");
assert(partition != NULL);
static char store_data[] = "ESP-IDF Partition Operations Example (Read, Erase, Write)";
static char read_data[sizeof(store_data)];
// 先擦除 分区
memset(read_data, 0xFF, sizeof(read_data));
ESP_ERROR_CHECK(esp_partition_erase_range(partition, 0, partition->size));
// 开始写入数据
ESP_ERROR_CHECK(esp_partition_write(partition, 0, store_data, sizeof(store_data)));
ESP_LOGI(TAG, "Written data: %s", store_data);
// 读取数据,同时检查对比和写入数据的完整性
ESP_ERROR_CHECK(esp_partition_read(partition, 0, read_data, sizeof(read_data)));
assert(memcmp(store_data, read_data, sizeof(read_data)) == 0);
ESP_LOGI(TAG, "Read data: %s", read_data);
ESP_LOGI(TAG, "Example end");
}
3)数据完整性校验?
在提出这个问题时候,我们必须知道这个从flash的数据和谁对比?我们在通过HTTP请求时,服务器会在 header 返回关于下载文件的完整性数据,比如MD5、Sha256等,以阿里云OSS对象存储为例,是返回这个文件的MD5数值的。
所以,我们本地下载了文件之后做一个MD5数值,然后对比阿里云返回的文件MD5数值,这样就校验了数据的完整性了。另外还可以对比数据的长度。
下面是请求阿里云OSS对象存储的文件的Header内容(不懂HTTP协议的自行百度),可看到有 Content-MD5:
这个字段,我们拿到了这个之后,Base64解码就是二进制的MD5数值了,以及Content-Length
字段。
Accept-Ranges: bytes
Connection: keep-alive
Content-Length: 247817
Content-MD5: BQUk+5KwFdJgPXLrGuQWtQ==
Content-Type: image/png
Date: Sun, 13 Mar 2022 01:37:53 GMT
ETag: "050524FB92B015D2603D72EB1AE416B5"
Last-Modified: Wed, 04 Mar 2020 14:20:02 GMT
Server: AliyunOSS
x-oss-hash-crc64ecma: 16969763075689951783
x-oss-object-type: Normal
x-oss-request-id: 622D4AF1DDEEC0303061A889
x-oss-server-time: 6
x-oss-storage-class: Standard
<contents>
三、代码走读
这里,只分享关键的代码块,不做整个代码的走读,源码获取方法在文章底部。
3.1 获取阿里云的文件头部信息
获取了阿里云的文件头部信息之后, 主要针对的是 Content-MD5
和 Content-Length
这2个字段的数值。
case HTTP_EVENT_ON_HEADER:
ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
//解析到了Header的Content-MD5数值
if (strcmp(evt->header_key, "Content-MD5") == 0)
{
ESP_LOGI(TAG, "HTTP Get Header MD5, key=%s, value=%s", evt->header_key, evt->header_value);
size_t len;
int dst_buf_size = 100;
p_header_md5_value = malloc(dst_buf_size);
// 获取了之后, base64解码这个数值,就得到了具体的MD5数值了
int result = mbedtls_base64_decode(p_header_md5_value, dst_buf_size, &len, (unsigned char *)(evt->header_value), strlen((char *)(evt->header_value)));
if (result != 0)
{
ESP_LOGE(TAG, "fail mbedtls_base64_decode");
}
}
//解析到了Header的Content-Length数值
else if (strcmp(evt->header_key, "Content-Length") == 0)
{
Content_Length = atoi((evt->header_value));
ESP_LOGI(TAG, "HTTP Get Content-Length, key=%d, value=%s", Content_Length, evt->header_value);
}
break;
3.2 下载数据到flash
这里下载数据到flash,以及边做MD5校验 。
while (1)
{
int data_read = esp_http_client_read(client, upgrade_data_buf, FLASH_SECTOR_SIZE);
if (data_read == 0)
{
ESP_LOGI(TAG, "Connection closed,all data received");
break;
}
if (data_read < 0)
{
ESP_LOGE(TAG, "Error: SSL data read error");
break;
}
if (data_read > 0)
{
ESP_LOGI(TAG, "Written image offAdress %d , length :%d", binary_file_len, FLASH_SECTOR_SIZE);
//注意先擦,必须以 1K 扇区的倍数去擦
if (esp_partition_erase_range(find_partition, binary_file_len, FLASH_SECTOR_SIZE) != ESP_OK)
{
ESP_LOGE(TAG, "Erase partition error");
}
//开始写入
if (esp_partition_write(find_partition, binary_file_len, (unsigned char *)upgrade_data_buf, data_read) != ESP_OK)
{
ESP_LOGE(TAG, "Write partition data error");
}
//同时把写入的数据做MD5校验
mbedtls_md5_update(&md5_ctx, (unsigned char *)upgrade_data_buf, data_read);
binary_file_len += data_read;
}
}
ESP_LOGE(TAG, "------------------------------");
ESP_LOGE(TAG, "Get local rev buff MD5 : ");
esp_log_buffer_hex(TAG, md5, 16);
ESP_LOGE(TAG, "Get server buff MD5 : ");
esp_log_buffer_hex(TAG, p_header_md5_value, 16);
ESP_LOGE(TAG, "------------------------------");
//校验MD5是否一致
if (0 == memcmp(md5, p_header_md5_value, sizeof(&p_header_md5_value)))
{
// MD5校验成功
ESP_LOGI(TAG, "MD5 verify successrn");
}
else
{ // MD5校验失败
ESP_LOGE(TAG, "MD5 verify failrn");
}
3.3 从flash读取数据做校验
从flash读取数据做校验,思路:
1)我要每次 4KB 读取数据,先把整个数据的长度除于4096,得到读取的总次数;
2)最后一次可能不到 4KB ,所以当达到最后一次获取数据时候, 判断剩下 buff 多少,再去读取。否则,会读取多余的数据。
//把总的文件大小除于每次取出来的大小,得到取出次数
int length = binary_file_len / FLASH_SECTOR_SIZE;
ESP_LOGI(TAG, "MD5 binary_file_len: %d %d ", FLASH_SECTOR_SIZE, length);
for (int rd_offset = 0; rd_offset < binary_file_len; rd_offset += FLASH_SECTOR_SIZE)
{
//判断为最后一次,则获取剩余的buff
if (++i > length)
{
int offLength = binary_file_len - rd_offset;
ESP_ERROR_CHECK(esp_partition_read(find_partition, rd_offset, flash_read_buff, offLength));
mbedtls_md5_update(&md5_ctx, (unsigned char *)flash_read_buff, offLength);
}
else
{
ESP_ERROR_CHECK(esp_partition_read(find_partition, rd_offset, flash_read_buff, FLASH_SECTOR_SIZE));
mbedtls_md5_update(&md5_ctx, (unsigned char *)flash_read_buff, FLASH_SECTOR_SIZE);
}
}
mbedtls_md5_finish(&md5_ctx, md5);
ESP_LOGE(TAG, "------------------------------");
ESP_LOGE(TAG, "Get local save buff MD5 : ");
esp_log_buffer_hex(TAG, md5, 16);
ESP_LOGE(TAG, "Get server buff MD5 : ");
esp_log_buffer_hex(TAG, p_header_md5_value, 16);
ESP_LOGE(TAG, "------------------------------");
//校验MD5是否一致
if (0 == memcmp(md5, p_header_md5_value, sizeof(&p_header_md5_value)))
{
// MD5校验成功
ESP_LOGI(TAG, "MD5 verify successrn");
}
else
{ // MD5校验失败
ESP_LOGE(TAG, "MD5 verify failrn");
}
源码免费获取:https://github.com/xuhongv/StudyInEsp32/tree/master/26_esp32_s2_s3_https_save_get_file
另外,不要把我的博客作为学习标准,我的只是笔记,难有疏忽之处,如果有,请指出来,也欢迎留言哈!
- 玩转无线物联网带你飞、免费加千人群讨论,群里众多国内ESP开发者,找到你一份归属,免费白嫖。
- QQ讨论一群,点击加群:434878850
- QQ讨论二群,点击加群:623325168
- 个人微信公众号:徐宏blog , 不定时推送干货文章,不推送任何广告。
- 个人邮箱:xuhongv@yeah.net 24小时在线,有发必回复!
- esp8266源代码学习汇总(持续更新,欢迎star):https://github.com/xuhongv/StudyInEsp8266
- esp32源代码学习汇总(持续更新,欢迎star):https://github.com/xuhongv/StudyInEsp32
- 关注下面微信公众号二维码,干货多多,第一时间推送!
最后
以上就是虚拟往事为你收集整理的乐鑫Esp32学习之旅28 熟悉自定义分区表 partition,拉取阿里云OSS对象存储的单片机/图片等较大文件保存在特定的存储位置,并读取出来做完整性校验,保证数据的完整性。(附带源码)一、前言二、开发中所需掌握的知识点三、代码走读的全部内容,希望文章能够帮你解决乐鑫Esp32学习之旅28 熟悉自定义分区表 partition,拉取阿里云OSS对象存储的单片机/图片等较大文件保存在特定的存储位置,并读取出来做完整性校验,保证数据的完整性。(附带源码)一、前言二、开发中所需掌握的知识点三、代码走读所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复