ESP32那些事儿(三):纵览全局之系统设计

     搞过嵌入式的同学们都知道,当我们开始一个新的平台后,我们首先想到的是系统层面的东西,这就体现了嵌入式开发中系统工程师的作用了,BTW,Android平台除外,Google已经伺候的太好了。
    ESP32实际上相对简单,一些例如Bringup的流程就省了。下面我就把我的思考过程写下来。

    一、Build系统
    ESP32的Build系统比较简单,留给开发者需要修改的也比较少。
    一个项目典型的目录结构是:
    project
    --------main
    --------components
    Build大概的原理是,将esp-idf的components和本项目的components拷贝到一起,代码模块收集完成后,和main下面的代码一起编译成Bin文件,其实就是静态链接到一起了。

    二、分区设计
    分区设计需要在项目初始阶段考虑好,否则量产或者试产后更改代价比较大,甚至可能很难升级。因为只升级APP分区可以通过OTA等方式在线升级,整个的烧写至少需要串口连接。
    ESP32文档中有对分区的类型的详细描述:
     https://esp-idf.readthedocs.io/en/latest/api-guides/partition-tables.html
    
    分区类型及子类型: 

类型
子类型
app
factory

ota_0

。。。。

ota_15

test
data
ota

phy

nvs
    创建分区表的时候,第一个要注意ESP32的默认分区及地址,其中nvs和phy_init是必须有的,如果支持ota升级,otadata分区也是必须有的,factory分区其实就是app分区,地址是从10000开始的。

# Espressif ESP32 Partition Table
# Name,   Type, SubType, Offset,  Size
nvs,      data, nvs,     0x9000,  0x6000
otadata,  data, ota,     0xd000,  0x2000
phy_init, data, phy,     0xf000,  0x1000

    要分析清楚项目的需求,比方说有没有需要可写的配置参数分区,保存自有数据的分区,例如音频文件等,还有没有一些临时的文件分区等。
    我们项目使用的一个典型分区表如下:

   1 # Name,   Type, SubType, Offset,   Size
   2 # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
   3 nvs,      data, nvs,     0x9000,  0x4000
   4 otadata,  data, ota,     0xd000,  0x2000
   5 phy_init, data, phy,     0xf000,  0x1000
   6 ota_0,    0,    ota_0,   0x10000,  1728K
   7 ota_1,    0,    ota_1,   ,         1728K
   8 config,   data, nvs,     ,         64K
   9 audio,    data, fat,     ,         448K
  10 coredump, data, coredump,,         64K

    从分区表上看,我们支持OTA升级,ota_0和ota_1是双备份分区,我们需要一个保存配置的cofig分区,还有一个保存音频文件的audio分区,最后的coredump分区是为了debug使用,当系统crash后,会把crash的栈dump到此分区。
        
    三、Bootloader及系统启动流程
    在做分区设计的时候,就需要研究Bootloader的功能,因为要做OTA,所以要保证OTA的绝对可靠,不允许某些情况下升级失败后,系统变砖的情况,
    可以看到我们有ota_0和ota_1双备份分区,假设我们在使用ota_0分区的时候开始升级新版本,一个健壮的升级逻辑需要保证 :
    1、升级是写到ota_1分区,如果升级出错,再启动应该进入到ota_0分区,则不会有问题;
    2、升级是写到ota_1分区, 如果升级写bin文件成功,假设bin文件本身有问题,升级完成启动进入ota_1分区,启动失败,此时必须能够重新启动进入ota_0分区。
    bootloader需要保证以上逻辑才能保证OTA的绝对可靠,那ESP32是否支持呢?否则我们需要自己实现以上逻辑。
    那我们就看看bootloader的启动流程吧,来验证我们的想法。
    bootloader启动的逻辑在esp-idf/components/bootloader/subproject/main/bootloader_start.c中
    
    主要函数有bootloader_main(), get_selected_boot_partition(),  load_boot_image() 等函数,ESP32的bootloader的启动逻辑如下:
    1、get_selected_boot_partition() 函数 otadata 分区和分区表决策出当前的分区 Index
      
    ESP32那些事儿(三):纵览全局之系统设计_第1张图片
    2、load_boot_image()以上一步返回的分区Index为起点,如果此分区可以Boot,则启动此分区,如果不能启动则以以下的顺序轮番尝试各个分区,直到找到一个可以启动的分区:
    首先尝试返回Index的分区;
    其次依次递减ota分区的index,尝试启动这些分区;
    直到递减到factory分区;
    然后从初始Index+1分区开始,一直到ota index最高的分区;

    从上面的启动顺序来看,ESP32已经想的比较周到了,我们上面的分区设计在OTA的情况也是比较健壮的了。So, Continue!

    四、OTA升级
    ota的升级的API在如下的文档链接中有描述:
     https://esp-idf.readthedocs.io/en/latest/api-reference/system/ota.html?highlight=ota
    
    OTA本身的话基本上逻辑比较简单,找一个合适的时机检查版本,如果有新版本就下载写到一个新的分区。
    如果需要灰度升级、版本维护那就是服务端的逻辑了,有兴趣的也可以探讨一下。我们也在服务端实现了一个非常简单的管理逻辑,灰度升级、版本状态管理、黑白名单、升级及下载统计等。
    另外一个需要解决的问题是,ESP32的设备通常交互比较简单,不像手机那样可以清楚的知道正在下载,正在升级,当前版本等;如何知道每个设备使用的那个版本、升级状态如何,这个需要做一些简单的设计。

    五、Debug及调试方案设计
    开发阶段的调试可以通过串口或者JTAG来进行,那么试产或者量产后,设备可能没有串口了,有严重问题怎么办呢,在系统设计阶段也要稍作考虑。
    其实大部分的Bug可以通过测试复现来解决。往往是一些Crash的问题都是偶现的,我们加了一个coredump分区,一些crash的问题可以通过dump这个分区来后续收集,不过这个是可选的。


  六、IOT系统整体架构考虑
    基于ESP32的IOT设备,肯定都是要联网交互的,一个典型的IOT设备会有一个手机移动端的APP和之匹配,那么这个交互如何设计呢?
    一般来讲有两个思路:1、基于局域网或者点对点直接通讯的;2、通过云端来通讯;如果有运营和远程控制的需求,一般会选择第二个方案。
    具体的协议来讲也大致有两个方向:1、采用物联网大热的MQTT协议;2、自定义协议,底层一般基于HTTP/HTTPS.



你可能感兴趣的:(ESP32那些事儿(三):纵览全局之系统设计)