用M5StickC-Plus实现语音控制的灯板
用M5StickC-Plus实现语音控制的灯板, 使用了Edge-Impulse和Arduino, 以及ESP-IDF
标签
嵌入式系统
TinyML
esp-idf
Edge Impulse
M5StickC
james
更新2022-09-06
987

大家好, 我是james, 一名hacker.  平时喜欢研究一些可编程的小东西.

1. 板卡介绍
2022年夏季硬禾学堂暑期练的第二款板卡是来自M5Stack的M5Stick-C-Plus.  这款板卡非常小巧, 带有一片1.14英寸的小屏幕, 可以当做手表用.  用过M5Stack产品的朋友都知道M5Stack的板卡完成度很高, 有漂亮的外壳, 可以快速应用到量产产品中去, 同时M5Stack的板卡都使用了ESP32的处理器芯片, 这种芯片价格便宜, 自带Wifi和蓝牙, 是做IoT物联网项目的好材料. 整个板卡接口丰富, 带有一排hat connector还有一个grove接口.  橘黄色的外壳颜色也很醒目. 是一款很好的开发板.


2. 实现功能
在本期暑假练活动中, 我主要学习了ESP32的开发, 了解了ESP32处理芯片拥有的各项功能.  在开发的过程中, 我发现使用ESP-IDF开发还是很方便的, 因为IDF用到了我比较熟悉的CMake构建工具, GCC编译器, 还有类似Kconfig的配置工具.特别是跨平台的特点让我能够在macos和windows都使用同一开发流程.

本期实现了第一个小项目, 语音控制灯板.

3. 设计思路
语音控制灯板有两种设计思路, 一种是使用云服务, 比如一些语音识别服务, 把音频数据上传后交给云端进行识别, 返回结果, 另一种是使用tinyml利用边缘计算执行"推断".  我使用的是后一种方法, 因为这种方法经过几次funpack活动,已经很熟悉了, 这里的熟悉是指使用edge-impulse工具生成推断所需的模型, 至于算法本身已经被edge-impulse封装好了, 参数配置都是通过页面图形化操作的,非常容易.

那么难点在哪儿呢? 首先edge-impulse并不直接提供M5StickC-Plus对应的数据采集固件, 默认提供的是为名为ESP-EYE的开发板开发的采集固件(https://github.com/edgeimpulse/firmware-espressif-esp32), ESP-EYE是espressif官方的开发板, 虽然和M5StickC-Plus一样使用ESP32芯片, 但是板卡之间的区别还是带来了一些小麻烦, 主要是这个固件无法直接使用, 也就无法直接烧录开始训练或者下载edge-impulse构建的推断firmware, 具体来说需要修改麦克风相关的一些配置代码.

其次, 需要修改就需要重新编译构建这个固件. 其实这个固件本身也是一个应用edge-impulse-sdk的例子, 但是如果按照这个例子修改, 则需要进行一些重构工作, 因为edge-impulse-sdk分为ingestion, inference, model, platform等部分, 这几个部分相互有耦合.  解决这些耦合关系就需要对edge-impulse-sdk的结构进行理解, 然后进行合理重构, 这也是一个相对困难的地方.

最后, 使用M5StickC-Plus来开发一些有用的程序, 还需要使用到M5StickC-Plus的SDK, 然而, 把这个SDK和ESP-EYE的数据采集SDK糅合在一起, 并不能通过直接复制粘贴到一个文件夹来实现, 幸运的是, IDF提供了一个很好的框架, 只需要正确理解IDF的component框架就能将arduino, M5StickC-Plus SDK和edge-impulse-sdk集成到一起.

4. 主要步骤
难点解决了, 剩下的工作就是按部就班的开发TinyML程序, 主要包括以下步骤:
- 编译烧录采集固件
- 使用edge-impulse采集数据
- 选择合适的模型和参数
- 使用数据训练模型
- 打包下载模型数据文件
- 集成模型数据文件到项目
- 开发应用功能, 主要是控制灯板的代码逻辑和模型推断相关的模型应用逻辑
- 编译烧录测试

5. 软件实现
以下介绍软件实现, 如前所述, 项目将多个组件集成到了一起, 以下将挑选一些代码进行讲解, 详细代码可以参考以下链接:

- 使用HSPI的M5StackC-Plus SDK修改版本
  https://github.com/picospuch/M5StickC-Plus.git
- 主要代码包含重构的edge-impulse-sdk和应用实现程序
  https://github.com/picospuch/eetree-funpack-workshop/tree/phase-summer2022

 

流程图:

Fh-B2DVoAtMys4vzIao8WhVRRcua

 

(1) ESP32-IDF配置

FrfTweuOwFrYxDCzHLC7Cy4XTMYm

(2) 推断部分关键代码

// function in loop
void tinyml_run_impulse(void)
{
    switch(state) {
        case INFERENCE_STOPPED:
            // nothing to do
            return;
        case INFERENCE_WAITING:
            if(ei_read_timer_ms() < (last_inference_ts + 2000)) {
                return;
            }
            state = INFERENCE_SAMPLING;
            ei_microphone_inference_reset_buffers();
            break;
        case INFERENCE_SAMPLING:
            // wait for data to be collected through callback
            if (ei_microphone_inference_is_recording()) {
                return;
            }
            state = INFERENCE_DATA_READY;
            break;
            // nothing to do, just continue to inference provcessing below
        case INFERENCE_DATA_READY:
        default:
            break;
    }

    signal_t signal;

    signal.total_length = continuous_mode ? EI_CLASSIFIER_SLICE_SIZE : EI_CLASSIFIER_RAW_SAMPLE_COUNT;
    signal.get_data = &ei_microphone_inference_get_data;

    // run the impulse: DSP, neural network and the Anomaly algorithm
    ei_impulse_result_t result = { 0 };
    EI_IMPULSE_ERROR ei_error;
    if(continuous_mode == true) {
        ei_error = run_classifier_continuous(&signal, &result, debug_mode);
    }
    else {
        ei_error = run_classifier(&signal, &result, debug_mode);
    }
    if (ei_error != EI_IMPULSE_OK) {
        ei_printf("Failed to run impulse (%d)", ei_error);
        return;
    }

    if(continuous_mode == true) {
        if(++print_results >= (EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW >> 1)) {
          display_results(&result);
            print_results = 0;
        }
    }
    else {
      display_results(&result);
    }

    if(continuous_mode == true) {
        state = INFERENCE_SAMPLING;
    }
    else {
        ei_printf("Starting inferencing in 2 seconds...\n");
        last_inference_ts = ei_read_timer_ms();
        state = INFERENCE_WAITING;
    }
}

(3) 灯板部分关键代码

inline void Render() {
  int n = 3; // n = refresh-freq / frame-freq
  SPI.beginTransaction(SPISettings(spi_speed, MSBFIRST, SPI_MODE0));
  digitalWrite(25, HIGH);

  if (!leds_on)
    return;
  for (int j = 0; j < n; ++j) {
    if (!lcd_direction_mode)
      {
        for (int i = 0; i < 8; ++i) {
          // row, then col
          digitalWrite(25, LOW);
          SPI.transfer(1 << i);
          SPI.transfer(LEDS[i]);
          digitalWrite(25, HIGH);
        }
      }
    else // Render method 2. Optional
      {
        int i = 0;
        for (; i < 8; ++i) {
          digitalWrite(25, LOW);
          SPI.transfer(1 << i);
          SPI.transfer(LEDS[7 - i]);
          digitalWrite(25, HIGH);
        }
        digitalWrite(25, LOW);
        SPI.transfer(1 << i);
        SPI.transfer(0);
        digitalWrite(25, HIGH);
      }
  }
  SPI.endTransaction();
  ++fps_counted_frames;
}

具体代码讲解详见视频.

6. 硬件实现
LED训练板需要3个GPIO才能驱动, 但是如果用麦克风, M5StackC-Plus就无法使用G0, 所以hat connector只能提供2个GPIO, 剩下的一个G33从Grove接口引出.

具体接线如下:

'''
| M5StickC-Plus | LED训练板 |
|---------------+-----------|
| GND           | GND       |
| 3V3           | VCC       |
| G36/G25       | RCLK      |
| G26           | ->D       |
| G33           | SRCLK     |
'''

FkHwABQXdMNECEhYJU9jJcr2rrVYFrwlaD_co3EBYyBTWN7QHejCHvC1

 


7. 心得体会
- ESP32有用很多粉丝是很好解释的, 价格亲民, 围绕芯片有很强的配套软件开发体系, 最关键的是开源.
- ESP32和RP2040是我目前最喜欢的两种MCU, ESP32的蓝牙和Wifi的能力在做本项目的时候还没有充分利用, 或许RP2040接个mic也能实现本期的项目.  但是在IoT时代, 连接的重要性不言而喻, 将来一定会有集成蓝牙或者Wifi或者其他连接协议的RP2040系列芯片, 就像默认集成了USB一样.

8. 其他说明
感谢硬禾学堂举办本期2022暑期练活动,让我有机会比较深入了解ESP32,让我能在业余时间参与更多有趣项目的学习,也感谢群的小伙伴提供很多种实现题目功能的思路,感谢各大佬的代码铺垫, 要不然没法快速完成这个小任务, 感谢大家一路的折腾与陪伴,谢谢!

附件为可以烧录测试的固件, 请使用esptool.py烧录测试.

附件下载
an-esp-project.bin
esptool.py esp32 -p <串口名称> -b 1500000 --before=default_reset --after=hard_reset write_flash --flash_mode dio --flash_freq 40m --flash_size 4MB 0x1000 bootloader/bootloader.bin 0x10000 an-esp-project.bin 0x8000 partition_table/partition-table.bin
bootloader.bin
partition-table.bin
团队介绍
james
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号