# 卡皮巴拉触摸玩偶

## 例程简介

ESP-Spot 体积小巧，内置扬声器、麦克风和电池，可轻松嵌入毛绒玩具中，让智能玩具“活”起来！本次 Demo 视频重点展示了如何将扣子智能语音对话与 ESP32-S3 触摸传感器结合，打造一款真正具有“情感交互”的 AI 玩具。

<div align="center">
<img src="../../../_static/spot-cover.jpg" width="70%">
</div>

# 视频展示

[用触摸交互升级大模型 AI 玩具【ESP-SPOT】](https://www.bilibili.com/video/BV1ekRAYVEZ1)

视频包含组装过程、成品演示、以及原理讲解

# 硬件接线

ESP-Spot 上板也可作为一块带音频的 ESP32 开发板单独使用，VIN 可以允许 3.3->5V 供电，如果需要默认使能 3V3 电源域，请将 VBUS 上拉，VBAT 接 3.3V。

**注意：当 PREP_VCC_CTL 引脚（GPIO6） 拉高时 CODEC_3V3 使能，音频编解码芯片工作。 也就是说，只有当 GPIO6 拉高时，音频功能才会正常工作**

引脚布局如下

<div align="center">
<img src="../../../_static/spot-pin.png" width="70%">
</div>

ESP32-S3 模组自带触摸传感器功能，技术细节请参考：：[ESP32-S3 数据手册](https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_cn.pdf)


# 预备知识

## 获取扣子账号

- 本例程默认使用 Coze 对接大模型进行语音交互，首先需要在[Coze文档中](https://bytedance.larkoffice.com/docx/Da6qd87pQodvNrxdFYrcnzMxnsh)申请 `Access token` 和 `BOT ID` 账号
- 更多的 Websocket 文档可以参考 [双向流式对话事件](https://www.coze.cn/open/docs/developer_guides/streaming_chat_event)

- 若想更换大模型对接平台，请替换 Coze 组件 [components/esp_coze_lib](./components/esp_coze_lib) 后更新 [main.c](./main/main.c) 中对应的逻辑

## 更多触摸交互逻辑

- 目前的触摸交互逻辑是：通过触摸对应部位，播放预设的音频内容或调用对应的应用逻辑
- 后续可升级为：通过触摸触发不同的事件，并发送至大模型进行处理，实现更智能、更加丰富的语音交互体验。详情请参考 Coze 文档：[手动提交对话内容](https://www.coze.cn/open/docs/developer_guides/streaming_chat_event#46f6a7d0)

## 本地音频

- 本例程在 `/tools/audio_tone.bin` 和 `/components/audio_flash_tone/` 目录下已经帮助用户生成了例程所需的 bin 文件和音频文件在 flash 中地址的源代码文件。

- 如果用户需要生成自己的 `audio_tone.bin`，则需要执行 `mk_audio_bin.py` 脚本（位于 $ADF_PATH/tools/audio_tone/mk_audio_tone.py），并且指定相关文件的路径。

- 源 MP3 文件在 `tone_mp3_folder` 文件夹中，生成的 C 文件、H 文件以及二进制 bin 文件都存放在此目录下。

   ```
   python3 $ADF_PATH/tools/audio_tone/mk_audio_tone.py -f ./ -r tone_mp3_folder
   ```

- 请使用 *python3 $ADF_PATH/tools/audio_tone/mk_audio_tone.py --help* 查看更多脚本信息。

- 本例程默认的 `audio_tone.bin` 包含如下音频文件：

   ```c
      "flash://tone/0_belly_1.mp3",
      "flash://tone/1_belly_2.mp3",
      "flash://tone/2_belly_3.mp3",
      "flash://tone/3_belly_4.mp3",
      "flash://tone/4_bread_1.mp3",
      "flash://tone/5_bread_2.mp3",
      "flash://tone/6_capybara_song_1.mp3",
      "flash://tone/7_hat_1.mp3",
      "flash://tone/8_neck_1.mp3",
      "flash://tone/9_neck_2.mp3",
      "flash://tone/10_reverse.mp3",
      "flash://tone/11_screaming.mp3",
      "flash://tone/12_shake.mp3",
      "flash://tone/13_touch_nose.mp3",
      "flash://tone/14_woohoo.mp3",
   ```
- 本地音频在 `partition_flash_tone.csv` 中地址配置如下，用户可以根据自己的项目 flash 分区灵活配置地址：
   ```
   flashTone,data,  0x04,  0x720000 , 500K,
   ```


## 动作和姿态识别

- 硬件方面采用 BMI270 传感器，用于识别不同的动作和姿态，后续将提供优化后的 BMI270 固件，以实现更精确、稳定的动作识别

## IDF 默认分支

- 本例程支持 IDF release/v5.4 及以后的分支。

# 配置工程

## 开发板

- 默认使用搭载 ESP32-S3 模组的 ``ESP-SPOT`` 开发板，配置项为 `CONFIG_ESP32_S3_SPOT_BOARD=y`
- **硬件初始化代码位于本仓库中的 [example/adf/components/audio_board](../components/audio_board) ，编译前将其替换掉 ADF 路径中的 `$ADF_PATH/components/audio_board`**

## 音频文件
- **如果 MP3 音频总数量大于 `9`，则需要修改 `$ADF_PATH/components/audio_stream` 目录下的 `tone_stream.c` 文件，可以直接使用本仓库中的 [example/adf/components/tone_stream.c](../components/tone_stream.c) 替换 ADF 路径下的原文件**
- 本仓库的中的 [tone_stream.c](../components/tone_stream.c) 改了什么：将 `_tone_open(audio_element_handle_t self)` 函数中 `char find_num[2]` 数组的长度修改为 `char find_num[3]`。这样可以确保函数在解析双位数的音频文件名（例如 10.mp3、11.mp3）时不会发生字符串截断错误，正确识别全部音频文件


## 触摸功能

- 本例程默认使用 GPIO 3、9、13、14 引脚作触摸，分别对应卡皮巴拉玩偶的不同部位，触发不同的语音交互功能：
  - GPIO3（鼻子）：短按触发一种语音交互，长按触发另一种语音交互。
  - GPIO9（帽子）：第一次按下触发语音对话，第二次按下播放《卡皮巴拉之歌》并点亮灯环。
  - GPIO13（肚子）：每次按下依次切换到下一阶段的语音交互，共有五种不同交互。
  - GPIO14（脖子）：作为开启/关闭大模型语音交互的总开关。

- 如需更换触摸引脚，请在 `main.c` 中修改 `TOUCH_CHANNEL_1` 及相关宏定义。注意参考 [ESP32-S3 数据手册](https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_cn.pdf)
 选择支持触摸功能的 IO

- 触摸灵敏度可通过以下宏定义进行调整，阈值越小，感应越灵敏：
  - `LIGHT_TOUCH_THRESHOLD`：设置轻触的触发阈值。
  - `HEAVY_TOUCH_THRESHOLD`：设置重触的触发阈值。

- 短按和长按的判定时间可在 `touch_task` 中进行配置
   ```
   const button_config_t btn_cfg = {
         .short_press_time = 300,       // 触发短按时长（ms）
         .long_press_time = 2000,       // 触发长按时长（ms）
      };
   ```

## LED 灯环

- 本例程使用 WS2812 灯环，相关配置如下：
  - `CONFIG_LED_GPIO_INPUT`：控制灯环的引脚，默认为 `GPIO45`，可通过 `idf.py menuconfig` 进行配置
  - `CONFIG_LED_COUNT`：用于设置灯环的 LED 数量，默认为 16 个，同样可在 `idf.py menuconfig` 中进行配置

## 扣子大模型鉴权

- 将获取到的 `Access token` 和 `BOT ID` 信息填入 `Menuconfig->Example Configuration` 中。`Access token` 默认是以 `pat_` 开头的

## Wi-Fi 信息
- 將 wifi 信息填入 `Menuconfig->>Example Configuration` 中


## 编译和下载

请先编译版本并烧录到开发板上，然后运行 monitor 工具来查看串口输出（替换 PORT 为端口名称）：

```
idf.py -p PORT flash monitor
```

**此外，本例程还需烧录 `/tools/audio_tone.bin` 到 `partition_flash_tone.csv` 的 `flashTone` 分区，请使用如下命令:**

```
esptool.py --chip esp32s3 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 40m --flash_size detect 0x720000 ./tools/audio_tone.bin
```

有关配置和使用 ESP-IDF 生成项目的完整步骤，请参阅 [《ESP-IDF 编程指南》](https://docs.espressif.com/projects/esp-idf/zh_CN/release-v5.3/esp32/index.html)。


## 如何使用

### 功能和用法

- 例程开始运行后，如果事前没有烧录 `/tools/audio_tone.bin` 到 `partition_flash_tone.csv ` 的 `flashTone` 分区，例程将会报错，请参考 [编译和下载](#编译和下载) 的说明进行烧录

## 故障排除

- 如果遇到下方错误，请把按照 [编译和下载](#编译和下载) 的说明烧录 `/tools/audio_tone.bin` 到 `partition_flash_tone.csv ` 的 `flashTone` 分区。
   ```c
   E (481) TONE_PARTITION: Not flash tone partition
   E (481) AUDIO_ELEMENT: [tone] AEL_STATUS_ERROR_OPEN,-1
   W (491) AUDIO_ELEMENT: [tone] audio_element_on_cmd_error,7
   E (501) TONE_PARTITION: /repo/adfs/bugfix/esp-adf-internal/components/tone_partition/tone_partition.c:204 (tone_partition_deinit): Got NULL Pointer
   W (511) AUDIO_ELEMENT: IN-[mp3] AEL_IO_ABORT
   E (511) MP3_DECODER: failed to read audio data (line 119)
   W (521) AUDIO_ELEMENT: [mp3] AEL_IO_ABORT, -3
   W (531) AUDIO_ELEMENT: IN-[i2s] AEL_IO_ABORT
   ```

- 如果遇到下方错误，通常是由于网络波动导致数据未能及时上传。此时可以尝试更换网络，或通过配置参数提升 ESP32-S3 的 Wi-Fi 性能。具体优化方法可参考乐鑫官方文档：[如何提高 Wi-Fi 性能](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32s3/api-guides/wifi.html#how-to-improve-wi-fi-performance)。当前上传使用的音频格式为 `PCM`，扣子即将支持 `Opus` 编码格式，可有效降低带宽占用
   ```
   E (144192) coze_chat: Audio data send to queue failed
   E (144222) coze_chat: Audio data send to queue failed
   E (144243) coze_chat: Audio data send to queue failed
   E (144267) coze_chat: Audio data send to queue failed
   E (144288) coze_chat: Audio data send to queue failed
   E (144309) coze_chat: Audio data send to queue failed
   E (144347) coze_chat: Audio data send to queue failed
   E (144368) coze_chat: Audio data send to queue failed
   E (144399) coze_chat: Audio data send to queue failed
   ```


## 技术支持
请按照下面的链接获取技术支持：

- 技术支持参见 [esp32.com](https://esp32.com/viewforum.php?f=20) 论坛
- 故障和新功能需求，请创建 [GitHub issue](https://github.com/espressif/esp-adf/issues)

我们会尽快回复。
