# 芯片的选择
为什么选择esp32,因为我觉得它性能强悍,双核架构,240MHZ主频,有强大的算力,支持我进行离线语音识别。
本项目没有使用额外的语音识别模块,就一个麦克风采集声音,esp32用来解析和分析音频数据。
# 板子的踩的坑
我是第一次画在工厂打出来样品的板子,真的感谢立创EDA,给提供这么好的平台,知道了强电不能敷铜等等等。真的是我技术不到家的原因,我原本焊上去5个灯,结果就亮了两个,我把剩下的三个拆了,可能我拆坏了,把这三个灯调换下位置,还是不会亮,最后我果断把后三个灯拆了,后续会把这三个灯珠补齐。
# 硬件的设计
## 1.主要的IC
1.ESP32-WROOM 一款乐鑫研发的32位芯片,内置WIFI,蓝牙,240HZ的主频等等
2.MSM261S4030H0R 一个I2S接口的麦克风,用来进行语音输入
3.CP2102N-A01-GQFN28R 一个USB下载芯片
4.WS2812E 5个LED灯珠(主要是考虑esp32内置了RMT发射器,发送的波形 刚好满足 WS2812的时序)
5.AMS1117-3.3_C347222 5V转3.3V
6.HK4100F-DC5V-SHG 继电器
........
还有就不一一列举了
## 2.硬件的整体框架
整个板子的主控是ESP32。
GPIO18 产生RMT波形,发送到WS2812,从而点亮LED灯。
GPIO32 33 25 是esp32的I2S接口,接到麦克风对应的引脚
GPIO4 16 接入继电器,用来驱动强电
GPIO0 EN脚 外接了两个键盘
剩下的都是一些下载电路,和电源部分
# 软件的设计
## 1.开发环境
Ubuntu18.0.4
Vscode
esp-idf 3.2
## 2.环境的搭建
由于开发环境是在Linux平台下编译的,所以我选择了Ubuntu,可以从乐鑫的官方文档中,
看到安装步骤,一步一步搭建,
具体的网址-->:[https://docs.espressif.com/projects/esp-idf/zh_CN/v3.3.2/get-started/linux-setup.html](https://docs.espressif.com/projects/esp-idf/zh_CN/v3.3.2/get-started/linux-setup.html)
\*\*第一步 :\*\*就是获取工具链,eps32的架构是xtensa架构的,所以需要下载交叉编译的编译器,连接器等等\,获取完工具链把它添加到环境变量。
\*\*第二步:\*\*就是获取esp\-idf了,由于国内的网络环境,从github获取代码比较慢,可以去采取合理的方法下载,注意克隆仓库的时候要加 \-\-recursive 这个参数,如:
```
git clone -b v3.3.2 --recursive https://github.com/espressif/esp-idf.git
```
这个参数是克隆全部的,不然会造成克隆过来的仓库不完整。
下来把esp-idf的路径,添加到环境变量。
运行
```
python -m pip install --user -r $IDF_PATH/requirements.txt
```
安装python库,因为idf有些驱动是用python写的脚本。
**第三步:** 获取阿里云的SDK,不过我没用去阿里云获取,我在乐鑫的仓库中发现了,他们完成了底层的硬件接口和阿里云飞燕平台对接的库,运行:
```
git clone https://github.com/espressif/esp-ali-smartliving.git
```
编译 ali-smartliving-device-sdk-c 库
在 esp-ali-smartliving 目录下执行:
```
cd ali-smartliving-device-sdk-c
make reconfig (选择SDK平台,选择esp32)
make menuconfig (选择相关功能配置,默认不需要修改,该步骤可以省略)
make (生成相关头文件和库文件)
```
乐鑫还给了示例工程,我是在工程是进行修改。
**第四步:** 安装Vscode ,Vscode免费,有强大的插件功能,Vscode也发行了Linux版本,用来开发esp32我感觉是最好的选择。
**这几步下来,所有一切东西全部免费开源,不用担心版权原因。**
#### 总的来说在Linux下环境搭建比较麻烦,不像Keil之类的IDE,通过自己一步一步设置编译工具链,一步步下来,那种感觉是在Keil之类的IDE是感受不到的。
## 3.软件的整体架构
1\. 阿里云对生活物联网平台提供了SDK,本软件整体架构就是基于阿里云的SDK和乐鑫的esp\-idf。
2.esp32是基于 freeRTOS操作系统,这就方便了我们以任务的形式 进行并发执行。
下图是我画的大概流程,两个任务,期中主任务分布着某些小任务
![image.png](//image.lceda.cn/pullimage/tBbnRFHuLPaeAtV61zdj5TaGNM5XJ5K1d8vbd2xW.png)
语音识别的固件来源与乐鑫音频开发框架esp-adf,可以进行离线语音识别,支持语音唤醒,
乐鑫的语音识别,乐鑫提拱了,编译好的库,这些库都是乐鑫自己训练好的库,CNN卷积神经网络,大量训练而来的,这里感谢乐鑫提供的库。
说:嗨乐鑫,进入唤醒模式,LED灯会以呼吸灯的形式闪烁,表示进入语音监听模式,说出命令,从而控制开发板,如果5s之内没反应,恢复以前的状态,重新进入语音监控模式。
![image.png](//image.lceda.cn/pullimage/JUclccJz0zOuV1nPdAUaBt7TDtjnHB7vql4W0lIT.png)
图画的比较丑
阿里云监控数据,就不画图了,大致就是解析Json数据,执行相对应的命令。阿里云的SDK里面有专门的API提供调用。
### 外设的驱动设计:
外设有WS2812 麦克风 和继电器
WS2812 这个驱动我搞了好久,把有关RMT部分都放在ws2812.c文件下了,都把它封装成结构体的形式,提供调用,
```
struct led_strip_s {
esp_err_t (*get_hsv)(led_strip_t *strip, uint32_t *h, uint32_t *s, uint32_t *v);
esp_err_t (*get_rgb)(led_strip_t *strip, uint32_t *red, uint32_t *green, uint32_t *blue);
esp_err_t (*set_hsv)(led_strip_t *strip, uint32_t h, uint32_t s, uint32_t v);
esp_err_t (*set_rgb)(led_strip_t *strip, uint32_t red, uint32_t green, uint32_t blue);
esp_err_t (*set_pixel)(led_strip_t *strip, uint32_t index, uint32_t red, uint32_t green, uint32_t blue);
esp_err_t (*refresh)(led_strip_t *strip, uint32_t timeout_ms);
esp_err_t (*clear)(led_strip_t *strip, uint32_t timeout_ms);
esp_err_t (*del)(led_strip_t *strip);
};
// 通过调用这个函数得到一个结构体
led_strip_t *led_strip_new_rmt_ws2812(const led_strip_config_t *config);
下面举个简单的例子
/******example*******/
led_strip_t *g_leds = led_strip_new_rmt_ws2812(&strip_config); // 得到led的结构体
g_leds->g_leds->set_hsv(g_leds, h, s, v); // 调用这个函数设置 灯的hsv属性
```
通过这样设计灯的驱动,操作更简单,便于以后增加和删除功能,代码的灵活性大大提高
麦克风,esp-idf框架下有I2S的库,直接调用,音频部分还要进行,一部分滤波,具体的代码,我是参考esp-adf里面的
```
static void i2s_init(void)
{
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER | I2S_MODE_RX, // the mode must be set according to DSP configuration
.sample_rate = 16000, // must be the same as DSP configuration
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // must be the same as DSP configuration
.bits_per_sample = 32, // must be the same as DSP configuration
.communication_format = I2S_COMM_FORMAT_I2S,
.dma_buf_count = 3,
.dma_buf_len = 300,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL2,
};
i2s_pin_config_t pin_config = {
.bck_io_num = BOARD_DMIC_I2S_SCK, // IIS_SCLK
.ws_io_num = BOARD_DMIC_I2S_WS, // IIS_LCLK
.data_out_num = -1, // IIS_DSIN
.data_in_num = BOARD_DMIC_I2S_SDO // IIS_DOUT
};
i2s_driver_install(1, &i2s_config, 0, NULL);
i2s_set_pin(1, &pin_config);
i2s_zero_dma_buffer(1);
}
```
继电器 只用到了 GPIO的输出功能,同样esp-idf里面有驱动 :包含 #include "driver/gpio.h"
```
gpio_config_t io_conf;
io_conf.intr_type = GPIO_PIN_INTR_DISABLE;
//set as output mode
io_conf.mode = GPIO_MODE_OUTPUT;
//bit mask of the pins that you want to set,e.g
io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
//disable pull-down mode
io_conf.pull_down_en = 0;
//disable pull-up mode
io_conf.pull_up_en = 0;
//configure GPIO with the given settings
gpio_config(&io_conf);
通过调用
gpio_set_level(GPIO_OUTPUT_IO_16, leavel); 就可以进行很简单的GPIO控制
```
# ESP32分区表的划分
esp32把FLASH的空间进行了划分,具体有关分区表的介绍,参考:[https://docs.espressif.com/projects/esp-idf/zh_CN/v3.3.2/api-guides/partition-tables.html](https://docs.espressif.com/projects/esp-idf/zh_CN/v3.3.2/api-guides/partition-tables.html)
文档中有详细的介绍。我们把四元组的信息,烧录在flash的指定位置。
我用的这个模组,是4M的flash,因为要进行语音识别,所以nvs要分的大一点,下一个存的是wifi信息,我还预留了两个OTA升级的空间,不过这个空间太小了,用不上,最后一个是存储阿里云生活物联网平台的四元组信息。
下面代码,就是我的分区表,第一个分区很大,因为要进行语音识别要很大的空间,最后一个分区,存入的是四元组的信息。
```
factory, app, factory, , 3840K
nvs, data, nvs, , 0x4000,
otadata, data, ota, , 0x2000,
phy_init, data, phy, , 0x1000,
ota_0, app, ota_0, , 0x1000,
ota_1, app, ota_1, , 0x1000,
fctry, data, nvs, , 0x4000
```
### 烧录四元组到flash
```
mass_mfg 目录中有一参考配置:single_mfg_config.csv,请拷贝成自己的配置文件,如 my_single_mfg_config.csv。
```
cp single\_mfg\_config\.csv my\_single\_mfg\_config\.csv
```
使用自己的 ProductKey、ProductSecret、DeviceName、DeviceSecret 对 my_single_mfg_config.csv 进行修改:
```
```
key,type,encoding,value
aliyun-key,namespace,,
DeviceName,data,string,config
DeviceSecret,data,string,dsj3RuY74pgCBJ3zczKz1LaLK7RGApqh
ProductKey,data,string,a10BnLLzGv4
ProductSecret,data,string,pVfLpS1u3A9JM0go
```
```
将 config,dsj3RuY74pgCBJ3zczKz1LaLK7RGApqh,a10BnLLzGv4,pVfLpS1u3A9JM0go 修改为你对应的值。
修改完成后,使用如下命令生成对应的 NVS 分区:
```
```
$IDF_PATH/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py --input my_single_mfg_config.csv --output my_single_mfg.bin --size 0x4000
```
```
下载
针对 esp32:
```
```
$IDF_PATH/components/esptool_py/esptool/esptool.py write_flash 0x003f1000 my_single_mfg.bin
注:0x003f1000 这个地址,是存放四元组的起始地址,一定要算对,要跟分区表一致,否则会造成配网失败
```
# 手机配网
**在阿里云生活物联网平台创建产品,具体就是那一套过程,在这块i要注意一点,采用这个东西配网,要选择一键配网**
![image.png](//image.lceda.cn/pullimage/Ai2UKfb43LZJiLbbT1yDRIgoZHHtGd9OIK9fs6iI.png)
然后采用二维码扫描的方式配网,这个二维码的入口在这个地方。扫描这个二维码,一键配网
![image.png](//image.lceda.cn/pullimage/jwdwdgBrNtCEqsd2Zi4EimtXojSwGiB9XBH9oqhr.png)
配网完成,在手机上可以看见这样,就可以用手机控制了
![image.png](//image.lceda.cn/pullimage/7FgU6RaKTYlSpjf8nURO9RwD8GBkywYTIjVBN02I.png)
# 实用性
### 我觉得,后期可以买个灯罩 把板子罩起来,放在桌子上,继电器可以外接,控制个风扇啥的。
### 这个并不完全依赖网络,当网络断开的时候,可以离线控制,方便了使用者使用,后续我会加入OTA升级,实现远程升级
8.26
我发现跑个离线神经网络占了flash的四分之三大小,就剩1M的空间,根本不够存放OTA升级的固件,所以要想OTA升级和离线识别共用,就需要增大flash的容量大小,或许有什么方法能裁剪,我再看看。