esp32自行编译micropython固件并支持smartconfig方法 以及代码保护

esp32自行编译micropython固件并支持smartconfig方法 以及代码保护

2023-06-12

阅读时长: 5 分钟

micropython 自己编译固件有几个好处

  • 只加入自己需要的模块降低硬件资源占用
  • .py文件可以修改中中间码,并烧录到固件内部,
    • 可以方便量产
    • 可一定程度保护代码
    • 更加省略了编译过程节省内存

我这里 只记录 添加 smartconfig的方法,并以S2 mini为例,其他esp32 同理。
简中网络上,几乎没有相关资料,有一些资料也是错的或无法使用。所以这里单独记录分享一下。 你可以直接下载 我编译好的
https://github.com/joyanhui/file.leiyanhui.com/blob/main/esp32/

准备

  • linux系统,我这里是pve lxc运行的ubuntu22.04
  • 科学工具 因为需要拉github
  • 基本的linux基础

环境配置 和 依赖

修改到国内源,这个自己处理

apt install git wget curl make gcc cmake rsync
# ppa安装pythone 3.11  Ubuntu自带的版本太低
apt install software-properties-common
add-apt-repository ppa:deadsnakes/ppa
apt install python3.11 python-is-python3
# pip3
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
python get-pip.py --force-reinstall
pip install mpy-cross-v6

克隆代码

<br>1<br> <br>mkdir mpy-bin && cd mpy-bin<br>

esp-idf

注意 不同的esp32平台 micropython支持的esp-idf版本不同,详情参考 https://github.com/micropython/micropython/tree/master/ports/esp32#readme
linux小白 一步一步操作,注意里面的警告信息

<br>1<br>2<br>3<br>4<br> <br>git clone -b v4.4.5 --recursive https://github.com/espressif/esp-idf.git esp-idf-v4.4.5<br>cd esp-idf-v4.4.5/<br>git checkout v4.4.5<br>git submodule update --init --recursive<br>

处理环境变量

<br>1<br>2<br> <br>./install.sh <br>source export.sh<br>

micropython

本文基于 1.20 版本,如果新版失效,请 直接去下载 https://github.com/micropython/micropython/releases/download/v1.20.0/micropython-1.20.0.zip 不要 git clone最新的

<br>1<br>2<br>3<br>4<br>5<br> <br>cd ..<br>git clone https://github.com/micropython/micropython.git<br>cd micropython<br>make -C mpy-cross<br>cd ports/esp32<br>

测试默认编译micropython固件

<br>1<br>2<br> <br>make submodules<br>make # make BOARD=LOLIN_S2_MINI<br>

直接make的话 是 esp32 ,我这里是 s2 ,并且是 esp32 s2 mini开发板 所以添加参数 BOARD=LOLIN_S2_MINI 这样就可以编译 了,编译后 会有提示 ./build-XXXX/ firmware.bin 这个文件是合并后的,可以直接烧到0x1000

推送到 nas

我这里推送到nas里面,方便其他机器使用

<br>1<br> <br>rsync -avzut --progress ./build-LOLIN_S2_MINI/firmware.bin root@10.1.1.200:/mnt/nas/temp/firmware-my.bin <br>

烧录

在连接esp32的机器上从nas复制bin文件过来然后下入

<br>1<br>2<br> <br>esptool --port COM3 erase_flash #擦除<br>esptool --port COM3 --baud 1000000 write_flash -z 0x1000 firmware-my.bin # 写入从nas 拷贝过来的 新固件<br>

烧录完成后,重启板,thonny 链接上,输入 help(‘modules’) 查看模块

添加 smartconfig模块

<br>1<br> <br>cd ~/mpy-bin/micropython/ports/esp32<br>

需要两个地方,1是 添加一个smarconfig.c文件,2 是修改还 ports/esp32/main/CMakeLists.txt 把 smarconfig.c 加进去

CMakeLists.txt

查看 cat ~/mpy-bin/micropython/ports/esp32/main/CMakeLists.txt 目前版本1.20版本 需要修改的内容在 54-92行 格式如下

<br>1<br>2<br>3<br>4<br>5<br>6<br> <br>set(MICROPY_SOURCE_PORT<br> main.c .... machine_sdcard.c ...<br>)<br>

我们需要在 machine_sdcard.c 后面添加一个 smarconfig.c,然后在 machine_sdcard.c的同目录下 创建一个 smarconfig.c (内容查看后文)
修改后的 CMakeLists.txt 内容格式如下

<br>1<br>2<br>3<br>4<br>5<br>6<br>7<br> <br>set(MICROPY_SOURCE_PORT<br> main.c .... machine_sdcard.c smarconfig.c ...<br>)<br>

同理,可以在这里删掉不用的模块

smarconfig.c

<br>1<br> <br>nano ~/mpy-bin/micropython/ports/esp32/machine_sdcard.c<br>

内容参考后文。 找

重新编译

<br>1<br>2<br> <br>make submodules<br>make BOARD=LOLIN_S2_MINI<br>

上级测试

新固件烧录后,烧录完成后,重启板,thonny 链接上,输入 help('modules') 查看模块 已经有 smarconfig 运行一个 main.py 测试,测试代码参考后文。 启动后,找一个支持smarconfig的手机app或者微信小程序 配网测试 我用的微信小程序#小程序://一键配网/047fob5XqOB14hk 复制发送到手机微信,聊天窗口点开即可。配网成功

添加自定义py文件

优点:1 保护代码 2 方便量产 内容单独另起一一个文章 添加自定义py文件到固件

内容

smarconfig.c

<br> 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 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99<br>100<br>101<br>102<br>103<br>104<br>105<br>106<br>107<br>108<br>109<br>110<br>111<br>112<br>113<br>114<br>115<br>116<br>117<br>118<br>119<br>120<br>121<br>122<br>123<br>124<br>125<br>126<br> <br>#include "py/obj.h"<br>#include "py/runtime.h"<br><br>#include <string.h><br>#include <stdlib.h><br>#include "freertos/FreeRTOS.h"<br>#include "freertos/task.h"<br>#include "freertos/event_groups.h"<br>#include "esp_wifi.h"<br>#include "esp_event.h"<br>#include "esp_log.h"<br>#include "esp_smartconfig.h"<br><br>static EventGroupHandle_t s_wifi_event_group;<br><br>static const int ESPTOUCH_DONE_BIT = BIT1;<br>static const char *TAG = "smartconfig";<br><br>static int found_ssid_and_password = false;<br>static uint8_t ssid[33] = {0};<br>static uint8_t password[65] = {0};<br>static uint8_t type = -1;<br>static uint8_t token = 0;<br><br>static void smartconfig_task(void * parm);<br><br>static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)<br>{<br> if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) { ESP_LOGI(TAG, "Scan done"); } else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) { ESP_LOGI(TAG, "Found channel"); } else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) { ESP_LOGI(TAG, "Got SSID and password");<br><br> smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;<br><br> memcpy(ssid, evt->ssid, sizeof(evt->ssid)); memcpy(password, evt->password, sizeof(evt->password)); type = evt->type; token = evt->token;<br><br> ESP_LOGI(TAG, "SSID:%s", ssid); ESP_LOGI(TAG, "PASSWORD:%s", password); ESP_LOGI(TAG, "TYPE:%d", type); ESP_LOGI(TAG, "TOKEN:%d", token);<br><br> found_ssid_and_password = true;<br><br> xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT); }<br>}<br><br>static void initialise_wifi(void)<br>{<br> s_wifi_event_group = xEventGroupCreate();<br><br> ESP_ERROR_CHECK(esp_event_loop_create_default()); ESP_ERROR_CHECK(esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));<br><br> xTaskCreate(smartconfig_task, "smartconfig_task", 4096, NULL, 3, NULL);<br>}<br><br>static void smartconfig_task(void * parm)<br>{<br> EventBits_t uxBits; ESP_ERROR_CHECK(esp_smartconfig_set_type(SC_TYPE_ESPTOUCH_AIRKISS)); smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_smartconfig_start(&cfg));<br><br> while (1) { uxBits = xEventGroupWaitBits(s_wifi_event_group, ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY); if(uxBits & ESPTOUCH_DONE_BIT) { ESP_LOGI(TAG, "smartconfig over"); ESP_ERROR_CHECK(esp_smartconfig_stop()); vTaskDelete(NULL); } }<br>}<br><br>STATIC mp_obj_t smartconfig_start(void)<br>{<br> initialise_wifi(); return mp_const_none;<br>}<br>STATIC MP_DEFINE_CONST_FUN_OBJ_0(smartconfig_start_obj, smartconfig_start);<br><br>STATIC mp_obj_t smartconfig_success(void)<br>{<br> return mp_obj_new_bool(found_ssid_and_password);<br>}<br>STATIC MP_DEFINE_CONST_FUN_OBJ_0(smartconfig_success_obj, smartconfig_success);<br><br>STATIC mp_obj_t smartconfig_info(void)<br>{<br> mp_obj_t info[] = { mp_obj_new_str((const char *)ssid, strlen((const char *)ssid)), mp_obj_new_str((const char *)password, strlen((const char *)password)), mp_obj_new_int(type), mp_obj_new_int(token) };<br><br> return mp_obj_new_tuple(4, info);<br>}<br>STATIC MP_DEFINE_CONST_FUN_OBJ_0(smartconfig_info_obj, smartconfig_info);<br><br>STATIC const mp_rom_map_elem_t smartconfig_module_globals_table[] = {<br> {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_smartconfig)}, {MP_ROM_QSTR(MP_QSTR_start), MP_ROM_PTR(&smartconfig_start_obj)}, {MP_ROM_QSTR(MP_QSTR_success), MP_ROM_PTR(&smartconfig_success_obj)}, {MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&smartconfig_info_obj)}, {MP_ROM_QSTR(MP_QSTR_TYPE_UNKNOWN), MP_ROM_INT(-1)}, {MP_ROM_QSTR(MP_QSTR_TYPE_ESPTOUCH), MP_ROM_INT(SC_TYPE_ESPTOUCH)}, {MP_ROM_QSTR(MP_QSTR_TYPE_AIRKISS), MP_ROM_INT(SC_TYPE_AIRKISS)},<br>};<br><br>STATIC MP_DEFINE_CONST_DICT(smartconfig_module_globals, smartconfig_module_globals_table);<br><br>const mp_obj_module_t smartconfig_user_cmodule = {<br> .base = {&mp_type_module}, .globals = (mp_obj_dict_t *)&smartconfig_module_globals,<br>};<br><br>MP_REGISTER_MODULE(MP_QSTR_smartconfig, smartconfig_user_cmodule);<br>

main.py

<br> 1 2 3 4 5 6 7 8 9<br>10<br>11<br>12<br>13<br>14<br>15<br>16<br>17<br>18<br>19<br>20<br>21<br>22<br>23<br>24<br>25<br>26<br>27<br>28<br>29<br>30<br>31<br>32<br>33<br>34<br>35<br>36<br>37<br>38<br>39<br>40<br>41<br>42<br>43<br>44<br>45<br>46<br>47<br>48<br>49<br>50<br>51<br>52<br>53<br>54<br>55<br>56<br>57<br>58<br>59<br>60<br>61<br>62<br>63<br>64<br>65<br>66<br>67<br>68<br>69<br>70<br>71<br>72<br>73<br>74<br> <br>import network<br>import socket<br>import smartconfig<br>import time<br><br>def inet_pton(ip_str:str):<br> '''convert ip address from string to bytes''' ip_bytes = b'' ip_segs = ip_str.split('.')<br><br> for seg in ip_segs: ip_bytes += int(seg).to_bytes(1, 'little')<br><br> return ip_bytes<br><br>def send_ack(local_ip, local_mac):<br> '''sent ack_done event to phone''' udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) udp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)<br><br> data = smartconfig.info()[3].to_bytes(1, 'little') + local_mac port = 10000 # airkiss port<br><br> if smartconfig.info()[2] == smartconfig.TYPE_ESPTOUCH: data += inet_pton(local_ip) port = 18266 # esptouch port<br><br> print(<br>f"""- sending ack:<br> type: {'esptouch' if smartconfig.info()[2] == smartconfig.TYPE_ESPTOUCH else 'airkiss'} port: {port} data: {data} length: {len(data)}<br>"""<br> )<br><br> for _ in range(20): time.sleep_ms(100) try: udp.sendto(data, ('255.255.255.255', port)) except OSError: pass<br><br> print('- ack was sent')<br><br>station = network.WLAN(network.STA_IF)<br>station.active(True)<br><br>print('- start smartconfig...')<br>smartconfig.start()<br><br>print('- waiting for success...')<br><br>while not smartconfig.success():<br> time.sleep_ms(500)<br><br>print('- got smartconfig info')<br>ssid, password, sc_type, token = smartconfig.info()<br>print(smartconfig.info())<br><br>print('- connecting to wifi...')<br>station.connect(ssid, password)<br><br>while not station.isconnected():<br> time.sleep_ms(500)<br>print('- wifi connected')<br><br>while station.ifconfig()[0] == '0.0.0.0':<br> time.sleep_ms(500)<br>print('- got ip')<br>print(station.ifconfig())<br><br>send_ack(station.ifconfig()[0], station.config('mac'))<br>

Licensed under CC BY-NC-SA 4.0

使用 Hugo 构建
主题 StackJimmy 设计