Compare commits

..

11 Commits

Author SHA1 Message Date
MoeCinnamo 67868619dc close 2025-12-09 17:22:52 +08:00
MoeCinnamo 8bd2780688 Upgrade Playlist Features 2025-12-09 17:20:01 +08:00
MoeCinnamo 577990de69 Annotate unused code for now. 2025-09-15 23:03:48 +08:00
MoeCinnamo aaf2f8dc36 Update music player 2025-09-15 22:11:20 +08:00
MoeCinnamo 7d7f5eae3d Update to 2.0.1 2025-09-15 22:04:01 +08:00
MoeCinnamo 5c43129024 1 2025-09-14 13:19:18 +08:00
MoeCinnamo 63e404d610 Update to 2.0.0 2025-09-13 23:40:38 +08:00
MoeCinnamo 5a929f5b06 Reduce unnecessary talk before playing music. 2025-09-12 16:15:44 +08:00
MoeCinnamo 78f6d82c48 remove PSRAM log 2025-09-12 15:07:34 +08:00
MoeCinnamo f763ae05dc psram size log set 2025-09-12 14:57:04 +08:00
MoeCinnamo 68d6539130 add PSRAM detecting 2025-09-12 14:42:50 +08:00
8624 changed files with 91210 additions and 2533759 deletions
+111
View File
@@ -0,0 +1,111 @@
name: Build Boards
on:
push:
branches:
- main
- ci/* # for ci test
pull_request:
branches:
- main
permissions:
contents: read
jobs:
prepare:
name: Determine variants to build
runs-on: ubuntu-latest
outputs:
variants: ${{ steps.select.outputs.variants }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install jq
run: sudo apt-get update && sudo apt-get install -y jq
- id: list
name: Get all variant list
run: |
echo "all_variants=$(python scripts/release.py --list-boards --json)" >> $GITHUB_OUTPUT
- id: select
name: Select variants based on changes
env:
ALL_VARIANTS: ${{ steps.list.outputs.all_variants }}
run: |
EVENT_NAME="${{ github.event_name }}"
# push 到 main 分支,编译全部变体
if [[ "$EVENT_NAME" == "push" ]]; then
echo "variants=$ALL_VARIANTS" >> $GITHUB_OUTPUT
exit 0
fi
# pull_request 场景
BASE_SHA="${{ github.event.pull_request.base.sha }}"
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
echo "Base: $BASE_SHA, Head: $HEAD_SHA"
CHANGED=$(git diff --name-only $BASE_SHA $HEAD_SHA || true)
echo -e "Changed files:\n$CHANGED"
NEED_ALL=0
declare -A AFFECTED
while IFS= read -r file; do
if [[ "$file" == main/* && "$file" != main/boards/* ]]; then
NEED_ALL=1
fi
if [[ "$file" == main/boards/common/* ]]; then
NEED_ALL=1
fi
if [[ "$file" == main/boards/* ]]; then
board=$(echo "$file" | cut -d '/' -f3)
AFFECTED[$board]=1
fi
done <<< "$CHANGED"
if [[ "$NEED_ALL" -eq 1 ]]; then
echo "variants=$ALL_VARIANTS" >> $GITHUB_OUTPUT
else
if [[ ${#AFFECTED[@]} -eq 0 ]]; then
echo "variants=[]" >> $GITHUB_OUTPUT
else
BOARDS_JSON=$(printf '%s\n' "${!AFFECTED[@]}" | sort -u | jq -R -s -c 'split("\n")[:-1]')
FILTERED=$(echo "$ALL_VARIANTS" | jq -c --argjson boards "$BOARDS_JSON" 'map(select(.board as $b | $boards | index($b)))')
echo "variants=$FILTERED" >> $GITHUB_OUTPUT
fi
fi
build:
name: Build ${{ matrix.name }}
needs: prepare
if: ${{ needs.prepare.outputs.variants != '[]' }}
strategy:
fail-fast: false # 单个变体失败不影响其它变体
matrix:
include: ${{ fromJson(needs.prepare.outputs.variants) }}
runs-on: ubuntu-latest
container:
image: espressif/idf:release-v5.4
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build current variant
shell: bash
run: |
source $IDF_PATH/export.sh
python scripts/release.py ${{ matrix.board }} --name ${{ matrix.name }}
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: xiaozhi_${{ matrix.name }}_${{ github.sha }}.bin
path: build/merged-binary.bin
if-no-files-found: error
-1
View File
@@ -13,7 +13,6 @@ main/assets/lang_config.h
main/mmap_generate_emoji.h main/mmap_generate_emoji.h
.DS_Store .DS_Store
.cache .cache
main/mmap_generate_emoji.h
*.pyc *.pyc
*.bin *.bin
mmap_generate_*.h mmap_generate_*.h
Executable → Regular
+4 -1
View File
@@ -4,11 +4,14 @@
# CMakeLists in this exact order for cmake to work correctly # CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
set(PROJECT_VER "1.9.1") set(PROJECT_VER "2.0.3")
# Add this line to disable the specific warning # Add this line to disable the specific warning
add_compile_options(-Wno-missing-field-initializers) add_compile_options(-Wno-missing-field-initializers)
# Fix Windows command line length limit
set(CMAKE_NINJA_FORCE_RESPONSE_FILE 1)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(xiaozhi) project(xiaozhi)
+20 -62
View File
@@ -1,73 +1,33 @@
# 超级小智-ESP32 # An MCP-based Chatbot | 一个基于 MCP 的聊天机器人
(中文 | English(编写中) | 日本語(编写中))
基于 https://github.com/78/xiaozhi-esp32 改良的船新版本 (中文 | [English](README_en.md) | [日本語](README_ja.md)
## 视频
👉 [人类:给 AI 装摄像头 vs AI:当场发现主人三天没洗头【bilibili】](https://www.bilibili.com/video/BV1bpjgzKEhd/)
👉 [手工打造你的 AI 女友,新手入门教程【bilibili】](https://www.bilibili.com/video/BV1XnmFYLEJN/)
## 介绍
## 💡介绍
这是一个由虾哥开源的 ESP32 项目,以 MIT 许可证发布,允许任何人免费使用,或用于商业用途。 这是一个由虾哥开源的 ESP32 项目,以 MIT 许可证发布,允许任何人免费使用,或用于商业用途。
我们希望通过这个项目,能够帮助大家了解 AI 硬件开发,将当下飞速发展的大语言模型应用到实际的硬件设备中。 我们希望通过这个项目,能够帮助大家了解 AI 硬件开发,将当下飞速发展的大语言模型应用到实际的硬件设备中。
如果你有任何想法或建议,请随时提出 Issues 或加入 QQ 群:暂无,Telegram群:暂无。 如果你有任何想法或建议,请随时提出 Issues 或加入 QQ 群:1011329060
项目主要贡献者:小霜霜Meow(抖音、B站UP)、空白泡泡糖果(B站UP),硅灵造物科技(B站UP) 项目其它贡献者:@zhubinsheng
贡献者说明:引入部分其它贡献者在其它项目上的代码,并进行了部分修改。 贡献者说明:引入部分其它贡献者在其它项目上的代码,并进行了部分修改。
音乐服务器相关源码提供者(为爱发电):小霜霜Meow 音乐服务器相关源码提供者(为爱发电):小霜霜Meow
感谢群友 cz 提供音乐服务器
音乐服务器源码请见 https://github.com/IntelligentlyEverything/MeowMusicServer 音乐服务器源码请见 https://github.com/IntelligentlyEverything/MeowMusicServer
### ❕注意事项
1. 如果小智说找不到歌曲怎么办?
进入[小智后台](https://xiaozhi.me/),找到对应设备,修改角色配置
- 选择 DeepSeekV3 大语言模型
- 在人物介绍中填入
- 收到音乐相关的需求时,只使用 MPC tool self.music.play_song 工具,同时禁止使用 search_music 功能。
2. 内置API调用失败怎么办?
请查看具体错误代码后,加入QQ群:865754861,或电报群 http://t.me/MeowMusicServer 给出错误代码和日志,等待我们修复。
### ⚙️已支持硬件芯片系列
- ESP32
- ESP32-S2
- ESP32-S3
- ESP32-C2
- ESP32-C3
- ESP32-C5
- ESP32-C6
- ESP32-C61
- ESP32-H2
- ESP32-H4
- ESP32-H21
- ESP32-P4
❕大部分硬件由于没有进行完整测试,可能会存在一些问题,属于正常现象,具体可提交issues进行反馈。
### 项目改动范围
新增:
- main/boards/common/music.h
- main/boards/common/esp32_music.h
- main/boards/common/esp32_music.cc
修改:
- main/audio/audio_codec.h
- main/audio/audio_codec.cc
- main/audio/audio_service.h
- main/audio/audio_service.cc
- main/boards/common/board.h
- main/boards/common/board.cc
- main/display/display.h
- main/display/display.cc
- main/display/lcd_display.h
- main/display/lcd_display.cc
- main/application.h
- main/application.cc
- main/idf_component.yml
- main/mcp_server.cc
### 基于 MCP 控制万物 ### 基于 MCP 控制万物
小智 AI 聊天机器人作为一个语音交互入口,利用 Qwen / DeepSeek 等大模型的 AI 能力,通过 MCP 协议实现多端控制。 小智 AI 聊天机器人作为一个语音交互入口,利用 Qwen / DeepSeek 等大模型的 AI 能力,通过 MCP 协议实现多端控制。
![通过MCP控制万物](docs/mcp-based-graph.jpg) ![通过MCP控制万物](docs/mcp-based-graph.jpg)
@@ -86,8 +46,6 @@
- 支持 ESP32-C3、ESP32-S3、ESP32-P4 芯片平台 - 支持 ESP32-C3、ESP32-S3、ESP32-P4 芯片平台
- 通过设备端 MCP 实现设备控制(音量、灯光、电机、GPIO 等) - 通过设备端 MCP 实现设备控制(音量、灯光、电机、GPIO 等)
- 通过云端 MCP 扩展大模型能力(智能家居控制、PC桌面操作、知识搜索、邮件收发等) - 通过云端 MCP 扩展大模型能力(智能家居控制、PC桌面操作、知识搜索、邮件收发等)
本项目新增功能:
- 新增音乐播放功能,支持播放本地音乐(开发中,敬请期待)、云端音乐(完善中)。
## 硬件 ## 硬件
@@ -204,10 +162,10 @@
## Star History ## Star History
<a href="https://star-history.com/#IntelligentlyEverything/xiaozhi-esp32&Date"> <a href="https://star-history.com/#78/xiaozhi-esp32&Date">
<picture> <picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=IntelligentlyEverything/xiaozhi-esp32&type=Date&theme=dark" /> <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=IntelligentlyEverything/xiaozhi-esp32&type=Date" /> <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=IntelligentlyEverything/xiaozhi-esp32&type=Date" /> <img alt="Star History Chart" src="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date" />
</picture> </picture>
</a> </a>
+157
View File
@@ -0,0 +1,157 @@
# An MCP-based Chatbot
(English | [中文](README.md) | [日本語](README_ja.md))
## Video
👉 [Human: Give AI a camera vs AI: Instantly finds out the owner hasn't washed hair for three days【bilibili】](https://www.bilibili.com/video/BV1bpjgzKEhd/)
👉 [Handcraft your AI girlfriend, beginner's guide【bilibili】](https://www.bilibili.com/video/BV1XnmFYLEJN/)
## Introduction
This is an open-source ESP32 project, released under the MIT license, allowing anyone to use it for free, including for commercial purposes.
We hope this project helps everyone understand AI hardware development and apply rapidly evolving large language models to real hardware devices.
If you have any ideas or suggestions, please feel free to raise Issues or join the QQ group: 1011329060
### Control Everything with MCP
As a voice interaction entry, the XiaoZhi AI chatbot leverages the AI capabilities of large models like Qwen / DeepSeek, and achieves multi-terminal control via the MCP protocol.
![Control everything via MCP](docs/mcp-based-graph.jpg)
### Features Implemented
- Wi-Fi / ML307 Cat.1 4G
- Offline voice wake-up [ESP-SR](https://github.com/espressif/esp-sr)
- Supports two communication protocols ([Websocket](docs/websocket.md) or MQTT+UDP)
- Uses OPUS audio codec
- Voice interaction based on streaming ASR + LLM + TTS architecture
- Speaker recognition, identifies the current speaker [3D Speaker](https://github.com/modelscope/3D-Speaker)
- OLED / LCD display, supports emoji display
- Battery display and power management
- Multi-language support (Chinese, English, Japanese)
- Supports ESP32-C3, ESP32-S3, ESP32-P4 chip platforms
- Device-side MCP for device control (Speaker, LED, Servo, GPIO, etc.)
- Cloud-side MCP to extend large model capabilities (smart home control, PC desktop operation, knowledge search, email, etc.)
## Hardware
### Breadboard DIY Practice
See the Feishu document tutorial:
👉 ["XiaoZhi AI Chatbot Encyclopedia"](https://ccnphfhqs21z.feishu.cn/wiki/F5krwD16viZoF0kKkvDcrZNYnhb?from=from_copylink)
Breadboard demo:
![Breadboard Demo](docs/v1/wiring2.jpg)
### Supports 70+ Open Source Hardware (Partial List)
- <a href="https://oshwhub.com/li-chuang-kai-fa-ban/li-chuang-shi-zhan-pai-esp32-s3-kai-fa-ban" target="_blank" title="LiChuang ESP32-S3 Development Board">LiChuang ESP32-S3 Development Board</a>
- <a href="https://github.com/espressif/esp-box" target="_blank" title="Espressif ESP32-S3-BOX3">Espressif ESP32-S3-BOX3</a>
- <a href="https://docs.m5stack.com/zh_CN/core/CoreS3" target="_blank" title="M5Stack CoreS3">M5Stack CoreS3</a>
- <a href="https://docs.m5stack.com/en/atom/Atomic%20Echo%20Base" target="_blank" title="AtomS3R + Echo Base">M5Stack AtomS3R + Echo Base</a>
- <a href="https://gf.bilibili.com/item/detail/1108782064" target="_blank" title="Magic Button 2.4">Magic Button 2.4</a>
- <a href="https://www.waveshare.net/shop/ESP32-S3-Touch-AMOLED-1.8.htm" target="_blank" title="Waveshare ESP32-S3-Touch-AMOLED-1.8">Waveshare ESP32-S3-Touch-AMOLED-1.8</a>
- <a href="https://github.com/Xinyuan-LilyGO/T-Circle-S3" target="_blank" title="LILYGO T-Circle-S3">LILYGO T-Circle-S3</a>
- <a href="https://oshwhub.com/tenclass01/xmini_c3" target="_blank" title="XiaGe Mini C3">XiaGe Mini C3</a>
- <a href="https://oshwhub.com/movecall/cuican-ai-pendant-lights-up-y" target="_blank" title="Movecall CuiCan ESP32S3">CuiCan AI Pendant</a>
- <a href="https://github.com/WMnologo/xingzhi-ai" target="_blank" title="WMnologo-Xingzhi-1.54">WMnologo-Xingzhi-1.54TFT</a>
- <a href="https://www.seeedstudio.com/SenseCAP-Watcher-W1-A-p-5979.html" target="_blank" title="SenseCAP Watcher">SenseCAP Watcher</a>
- <a href="https://www.bilibili.com/video/BV1BHJtz6E2S/" target="_blank" title="ESP-HI Low Cost Robot Dog">ESP-HI Low Cost Robot Dog</a>
<div style="display: flex; justify-content: space-between;">
<a href="docs/v1/lichuang-s3.jpg" target="_blank" title="LiChuang ESP32-S3 Development Board">
<img src="docs/v1/lichuang-s3.jpg" width="240" />
</a>
<a href="docs/v1/espbox3.jpg" target="_blank" title="Espressif ESP32-S3-BOX3">
<img src="docs/v1/espbox3.jpg" width="240" />
</a>
<a href="docs/v1/m5cores3.jpg" target="_blank" title="M5Stack CoreS3">
<img src="docs/v1/m5cores3.jpg" width="240" />
</a>
<a href="docs/v1/atoms3r.jpg" target="_blank" title="AtomS3R + Echo Base">
<img src="docs/v1/atoms3r.jpg" width="240" />
</a>
<a href="docs/v1/magiclick.jpg" target="_blank" title="Magic Button 2.4">
<img src="docs/v1/magiclick.jpg" width="240" />
</a>
<a href="docs/v1/waveshare.jpg" target="_blank" title="Waveshare ESP32-S3-Touch-AMOLED-1.8">
<img src="docs/v1/waveshare.jpg" width="240" />
</a>
<a href="docs/v1/lilygo-t-circle-s3.jpg" target="_blank" title="LILYGO T-Circle-S3">
<img src="docs/v1/lilygo-t-circle-s3.jpg" width="240" />
</a>
<a href="docs/v1/xmini-c3.jpg" target="_blank" title="XiaGe Mini C3">
<img src="docs/v1/xmini-c3.jpg" width="240" />
</a>
<a href="docs/v1/movecall-cuican-esp32s3.jpg" target="_blank" title="CuiCan">
<img src="docs/v1/movecall-cuican-esp32s3.jpg" width="240" />
</a>
<a href="docs/v1/wmnologo_xingzhi_1.54.jpg" target="_blank" title="WMnologo-Xingzhi-1.54">
<img src="docs/v1/wmnologo_xingzhi_1.54.jpg" width="240" />
</a>
<a href="docs/v1/sensecap_watcher.jpg" target="_blank" title="SenseCAP Watcher">
<img src="docs/v1/sensecap_watcher.jpg" width="240" />
</a>
<a href="docs/v1/esp-hi.jpg" target="_blank" title="ESP-HI Low Cost Robot Dog">
<img src="docs/v1/esp-hi.jpg" width="240" />
</a>
</div>
## Software
### Firmware Flashing
For beginners, it is recommended to use the firmware that can be flashed without setting up a development environment.
The firmware connects to the official [xiaozhi.me](https://xiaozhi.me) server by default. Personal users can register an account to use the Qwen real-time model for free.
👉 [Beginner's Firmware Flashing Guide](https://ccnphfhqs21z.feishu.cn/wiki/Zpz4wXBtdimBrLk25WdcXzxcnNS)
### Development Environment
- Cursor or VSCode
- Install ESP-IDF plugin, select SDK version 5.4 or above
- Linux is better than Windows for faster compilation and fewer driver issues
- This project uses Google C++ code style, please ensure compliance when submitting code
### Developer Documentation
- [Custom Board Guide](main/boards/README.md) - Learn how to create custom boards for XiaoZhi AI
- [MCP Protocol IoT Control Usage](docs/mcp-usage.md) - Learn how to control IoT devices via MCP protocol
- [MCP Protocol Interaction Flow](docs/mcp-protocol.md) - Device-side MCP protocol implementation
- [A detailed WebSocket communication protocol document](docs/websocket.md)
## Large Model Configuration
If you already have a XiaoZhi AI chatbot device and have connected to the official server, you can log in to the [xiaozhi.me](https://xiaozhi.me) console for configuration.
👉 [Backend Operation Video Tutorial (Old Interface)](https://www.bilibili.com/video/BV1jUCUY2EKM/)
## Related Open Source Projects
For server deployment on personal computers, refer to the following open-source projects:
- [xinnan-tech/xiaozhi-esp32-server](https://github.com/xinnan-tech/xiaozhi-esp32-server) Python server
- [joey-zhou/xiaozhi-esp32-server-java](https://github.com/joey-zhou/xiaozhi-esp32-server-java) Java server
- [AnimeAIChat/xiaozhi-server-go](https://github.com/AnimeAIChat/xiaozhi-server-go) Golang server
Other client projects using the XiaoZhi communication protocol:
- [huangjunsen0406/py-xiaozhi](https://github.com/huangjunsen0406/py-xiaozhi) Python client
- [TOM88812/xiaozhi-android-client](https://github.com/TOM88812/xiaozhi-android-client) Android client
## Star History
<a href="https://star-history.com/#78/xiaozhi-esp32&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date" />
</picture>
</a>
+157
View File
@@ -0,0 +1,157 @@
# MCP ベースのチャットボット
(日本語 | [中文](README.md) | [English](README_en.md)
## 動画
👉 [人間:AIにカメラを装着 vs AI:その場で飼い主が3日間髪を洗っていないことを発見【bilibili】](https://www.bilibili.com/video/BV1bpjgzKEhd/)
👉 [手作りでAIガールフレンドを作る、初心者入門チュートリアル【bilibili】](https://www.bilibili.com/video/BV1XnmFYLEJN/)
## イントロダクション
これはエビ兄さんがオープンソースで公開しているESP32プロジェクトで、MITライセンスのもと、誰でも無料で、商用利用も可能です。
このプロジェクトを通じて、AIハードウェア開発を理解し、急速に進化する大規模言語モデルを実際のハードウェアデバイスに応用できるようになることを目指しています。
ご意見やご提案があれば、いつでもIssueを提出するか、QQグループ:1011329060 にご参加ください。
### MCPであらゆるものを制御
シャオジーAIチャットボットは音声インタラクションの入口として、Qwen / DeepSeekなどの大規模モデルのAI能力を活用し、MCPプロトコルを通じてマルチエンド制御を実現します。
![MCPであらゆるものを制御](docs/mcp-based-graph.jpg)
### 実装済み機能
- Wi-Fi / ML307 Cat.1 4G
- オフライン音声ウェイクアップ [ESP-SR](https://github.com/espressif/esp-sr)
- 2種類の通信プロトコルに対応([Websocket](docs/websocket.md) または MQTT+UDP
- OPUSオーディオコーデックを採用
- ストリーミングASR + LLM + TTSアーキテクチャに基づく音声インタラクション
- 話者認識、現在話している人を識別 [3D Speaker](https://github.com/modelscope/3D-Speaker)
- OLED / LCDディスプレイ、表情表示対応
- バッテリー表示と電源管理
- 多言語対応(中国語、英語、日本語)
- ESP32-C3、ESP32-S3、ESP32-P4チッププラットフォーム対応
- デバイス側MCPによるデバイス制御(音量・明るさ調整、アクション制御など)
- クラウド側MCPで大規模モデル能力を拡張(スマートホーム制御、PCデスクトップ操作、知識検索、メール送受信など)
## ハードウェア
### ブレッドボード手作り実践
Feishuドキュメントチュートリアルをご覧ください:
👉 [「シャオジーAIチャットボット百科事典」](https://ccnphfhqs21z.feishu.cn/wiki/F5krwD16viZoF0kKkvDcrZNYnhb?from=from_copylink)
ブレッドボードのデモ:
![ブレッドボードデモ](docs/v1/wiring2.jpg)
### 70種類以上のオープンソースハードウェアに対応(一部のみ表示)
- <a href="https://oshwhub.com/li-chuang-kai-fa-ban/li-chuang-shi-zhan-pai-esp32-s3-kai-fa-ban" target="_blank" title="立創・実戦派 ESP32-S3 開発ボード">立創・実戦派 ESP32-S3 開発ボード</a>
- <a href="https://github.com/espressif/esp-box" target="_blank" title="楽鑫 ESP32-S3-BOX3">楽鑫 ESP32-S3-BOX3</a>
- <a href="https://docs.m5stack.com/zh_CN/core/CoreS3" target="_blank" title="M5Stack CoreS3">M5Stack CoreS3</a>
- <a href="https://docs.m5stack.com/en/atom/Atomic%20Echo%20Base" target="_blank" title="AtomS3R + Echo Base">M5Stack AtomS3R + Echo Base</a>
- <a href="https://gf.bilibili.com/item/detail/1108782064" target="_blank" title="マジックボタン2.4">マジックボタン2.4</a>
- <a href="https://www.waveshare.net/shop/ESP32-S3-Touch-AMOLED-1.8.htm" target="_blank" title="微雪電子 ESP32-S3-Touch-AMOLED-1.8">微雪電子 ESP32-S3-Touch-AMOLED-1.8</a>
- <a href="https://github.com/Xinyuan-LilyGO/T-Circle-S3" target="_blank" title="LILYGO T-Circle-S3">LILYGO T-Circle-S3</a>
- <a href="https://oshwhub.com/tenclass01/xmini_c3" target="_blank" title="エビ兄さん Mini C3">エビ兄さん Mini C3</a>
- <a href="https://oshwhub.com/movecall/cuican-ai-pendant-lights-up-y" target="_blank" title="Movecall CuiCan ESP32S3">CuiCan AIペンダント</a>
- <a href="https://github.com/WMnologo/xingzhi-ai" target="_blank" title="無名科技Nologo-星智-1.54">無名科技Nologo-星智-1.54TFT</a>
- <a href="https://www.seeedstudio.com/SenseCAP-Watcher-W1-A-p-5979.html" target="_blank" title="SenseCAP Watcher">SenseCAP Watcher</a>
- <a href="https://www.bilibili.com/video/BV1BHJtz6E2S/" target="_blank" title="ESP-HI 超低コストロボット犬">ESP-HI 超低コストロボット犬</a>
<div style="display: flex; justify-content: space-between;">
<a href="docs/v1/lichuang-s3.jpg" target="_blank" title="立創・実戦派 ESP32-S3 開発ボード">
<img src="docs/v1/lichuang-s3.jpg" width="240" />
</a>
<a href="docs/v1/espbox3.jpg" target="_blank" title="楽鑫 ESP32-S3-BOX3">
<img src="docs/v1/espbox3.jpg" width="240" />
</a>
<a href="docs/v1/m5cores3.jpg" target="_blank" title="M5Stack CoreS3">
<img src="docs/v1/m5cores3.jpg" width="240" />
</a>
<a href="docs/v1/atoms3r.jpg" target="_blank" title="AtomS3R + Echo Base">
<img src="docs/v1/atoms3r.jpg" width="240" />
</a>
<a href="docs/v1/magiclick.jpg" target="_blank" title="マジックボタン2.4">
<img src="docs/v1/magiclick.jpg" width="240" />
</a>
<a href="docs/v1/waveshare.jpg" target="_blank" title="微雪電子 ESP32-S3-Touch-AMOLED-1.8">
<img src="docs/v1/waveshare.jpg" width="240" />
</a>
<a href="docs/v1/lilygo-t-circle-s3.jpg" target="_blank" title="LILYGO T-Circle-S3">
<img src="docs/v1/lilygo-t-circle-s3.jpg" width="240" />
</a>
<a href="docs/v1/xmini-c3.jpg" target="_blank" title="エビ兄さん Mini C3">
<img src="docs/v1/xmini-c3.jpg" width="240" />
</a>
<a href="docs/v1/movecall-cuican-esp32s3.jpg" target="_blank" title="CuiCan">
<img src="docs/v1/movecall-cuican-esp32s3.jpg" width="240" />
</a>
<a href="docs/v1/wmnologo_xingzhi_1.54.jpg" target="_blank" title="無名科技Nologo-星智-1.54">
<img src="docs/v1/wmnologo_xingzhi_1.54.jpg" width="240" />
</a>
<a href="docs/v1/sensecap_watcher.jpg" target="_blank" title="SenseCAP Watcher">
<img src="docs/v1/sensecap_watcher.jpg" width="240" />
</a>
<a href="docs/v1/esp-hi.jpg" target="_blank" title="ESP-HI 超低コストロボット犬">
<img src="docs/v1/esp-hi.jpg" width="240" />
</a>
</div>
## ソフトウェア
### ファームウェア書き込み
初心者の方は、まず開発環境を構築せずに書き込み可能なファームウェアを使用することをおすすめします。
ファームウェアはデフォルトで公式 [xiaozhi.me](https://xiaozhi.me) サーバーに接続します。個人ユーザーはアカウント登録でQwenリアルタイムモデルを無料で利用できます。
👉 [初心者向けファームウェア書き込みガイド](https://ccnphfhqs21z.feishu.cn/wiki/Zpz4wXBtdimBrLk25WdcXzxcnNS)
### 開発環境
- Cursor または VSCode
- ESP-IDFプラグインをインストールし、SDKバージョン5.4以上を選択
- LinuxはWindowsよりも優れており、コンパイルが速く、ドライバの問題も少ない
- 本プロジェクトはGoogle C++コードスタイルを採用、コード提出時は準拠を確認してください
### 開発者ドキュメント
- [カスタム開発ボードガイド](main/boards/README.md) - シャオジーAI用のカスタム開発ボード作成方法
- [MCPプロトコルIoT制御使用法](docs/mcp-usage.md) - MCPプロトコルでIoTデバイスを制御する方法
- [MCPプロトコルインタラクションフロー](docs/mcp-protocol.md) - デバイス側MCPプロトコルの実装方法
- [詳細なWebSocket通信プロトコルドキュメント](docs/websocket.md)
## 大規模モデル設定
すでにシャオジーAIチャットボットデバイスをお持ちで、公式サーバーに接続済みの場合は、[xiaozhi.me](https://xiaozhi.me) コンソールで設定できます。
👉 [バックエンド操作ビデオチュートリアル(旧インターフェース)](https://www.bilibili.com/video/BV1jUCUY2EKM/)
## 関連オープンソースプロジェクト
個人PCでサーバーをデプロイする場合は、以下のオープンソースプロジェクトを参照してください:
- [xinnan-tech/xiaozhi-esp32-server](https://github.com/xinnan-tech/xiaozhi-esp32-server) Pythonサーバー
- [joey-zhou/xiaozhi-esp32-server-java](https://github.com/joey-zhou/xiaozhi-esp32-server-java) Javaサーバー
- [AnimeAIChat/xiaozhi-server-go](https://github.com/AnimeAIChat/xiaozhi-server-go) Golangサーバー
シャオジー通信プロトコルを利用した他のクライアントプロジェクト:
- [huangjunsen0406/py-xiaozhi](https://github.com/huangjunsen0406/py-xiaozhi) Pythonクライアント
- [TOM88812/xiaozhi-android-client](https://github.com/TOM88812/xiaozhi-android-client) Androidクライアント
## スター履歴
<a href="https://star-history.com/#78/xiaozhi-esp32&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=78/xiaozhi-esp32&type=Date" />
</picture>
</a>
+228
View File
@@ -0,0 +1,228 @@
# ESP32服务器地址配置指南
## 📍 **配置文件位置**
```
xiaozhi-esp32_music/main/server_config.h
```
## 🔧 **如何修改服务器地址**
### **步骤1:查看您的服务器IP地址**
#### **Windows系统**
打开命令提示符(CMD),输入:
```cmd
ipconfig
```
查找"IPv4 地址",例如:`192.168.1.100`
#### **Linux/Mac系统**
打开终端,输入:
```bash
ifconfig
# 或
ip addr
```
查找局域网IP地址。
---
### **步骤2:编辑配置文件**
打开文件:
```
d:\esp32-music-server\Meow\MeowEmbeddedMusicServer\xiaozhi-esp32_music\xiaozhi-esp32_music\main\server_config.h
```
找到这一行:
```cpp
#define MUSIC_SERVER_URL "http://192.168.1.100:2233"
```
`192.168.1.100` 替换为您的服务器IP地址。
---
## 🌐 **配置示例**
### **示例1:本地局域网(推荐)**
服务器和ESP32在同一个WiFi网络中:
```cpp
#define MUSIC_SERVER_URL "http://192.168.1.100:2233"
```
**优点**
- ✅ 速度快
- ✅ 延迟低
- ✅ 不需要公网IP
---
### **示例2:公网IP**
如果您有公网IP或使用花生壳等内网穿透:
```cpp
#define MUSIC_SERVER_URL "http://123.45.67.89:2233"
```
**注意**
- ⚠️ 确保路由器端口转发2233端口
- ⚠️ 注意服务器安全
---
### **示例3:域名**
如果您有域名:
```cpp
#define MUSIC_SERVER_URL "http://your-music-server.com:2233"
```
**注意**
- ⚠️ 确保域名解析正确
- ⚠️ 如果使用HTTPS,改为`https://`
---
### **示例4:使用原作者在线服务器**
```cpp
#define MUSIC_SERVER_URL "http://http-embedded-music.miao-lab.top:2233"
```
**说明**
- ✅ 无需自己搭建服务器
- ⚠️ 依赖外部服务可用性
- ⚠️ 无法使用设备绑定等个性化功能
---
## 🔍 **如何测试服务器地址是否正确**
### **方法1:浏览器测试**
在浏览器中访问:
```
http://您的服务器IP:2233
```
应该看到Meow Music的Web界面。
---
### **方法2curl测试**
```bash
curl http://您的服务器IP:2233/api/search?song=江南
```
应该返回JSON格式的搜索结果。
---
## 📝 **完整配置检查清单**
- [ ] 确认服务器正在运行(`go run .`
- [ ] 确认服务器端口是2233
- [ ] 确认ESP32和服务器在同一网络(或有公网连接)
- [ ] 修改`server_config.h`中的IP地址
- [ ] 保存文件
- [ ] 重新编译ESP32固件(`idf.py build`
- [ ] 烧录到ESP32`idf.py flash`
- [ ] 测试连接
---
## 🐛 **常见问题**
### **问题1ESP32无法连接服务器**
**现象**
```
[Esp32Music] Failed to connect to music API
```
**排查**
1. 检查服务器是否运行
2. 检查IP地址是否正确
3. 检查ESP32是否连接WiFi
4. Ping服务器IP测试网络连通性
---
### **问题2:地址写错了**
**现象**
```
[Esp32Music] HTTP GET failed with status code: 404
```
**解决**
- 检查URL格式是否正确
- 确保有`http://`前缀
- 确保端口号是`:2233`
---
### **问题3:防火墙阻止**
**现象**
- ESP32无法连接
- 但浏览器可以访问
**解决(Windows**
```
控制面板 → Windows防火墙 → 允许应用通过防火墙
→ 找到Go程序 → 允许专用和公用网络
```
---
## 🎯 **推荐配置**
### **开发测试阶段**
使用局域网IP
```cpp
#define MUSIC_SERVER_URL "http://192.168.1.100:2233"
```
### **生产环境**
使用域名:
```cpp
#define MUSIC_SERVER_URL "http://music.your-domain.com:2233"
```
---
## 💡 **高级技巧**
### **使用环境变量(未来功能)**
可以考虑在ESP32端添加NVS配置,通过Web界面修改服务器地址,无需重新编译。
### **mDNS服务发现(未来功能)**
可以使用mDNS实现服务器自动发现:
```
http://meow-music.local:2233
```
---
## 📞 **技术支持**
如有问题,请加入:
**喵波音律QQ交流群:865754861**
---
**配置完成后,记得重新编译并烧录固件!** 🚀
Executable
BIN
View File
Binary file not shown.
-659
View File
@@ -1,659 +0,0 @@
dependencies:
78/esp-ml307:
component_hash: 97a1eeb5274d841bbfbe50ef6b6976f028b0f53e57aee95eda57f49048d02fb9
dependencies:
- name: idf
require: private
version: '>=5.3'
source:
registry_url: https://components.espressif.com/
type: service
version: 3.3.0
78/esp-opus:
component_hash: 8182b733f071d7bfe1e837f4c9f8649a63e4c937177f089e65772880c02f2e17
dependencies:
- name: idf
require: private
version: '>=5.0'
source:
registry_url: https://components.espressif.com
type: service
version: 1.0.5
78/esp-opus-encoder:
component_hash: b80e5b6d6dc4bf6b0bf1a3729f52b80500e9e6b1003b3e827b78ba738283a296
dependencies:
- name: 78/esp-opus
registry_url: https://components.espressif.com
require: private
version: ^1.0.5
- name: idf
require: private
version: '>=5.3'
source:
registry_url: https://components.espressif.com/
type: service
version: 2.4.1
78/esp-wifi-connect:
component_hash: 752e62d469a341495368b6a3f812afdbe37569ea5b4460f4eedec42253118e8c
dependencies:
- name: idf
require: private
version: '>=5.3'
source:
registry_url: https://components.espressif.com/
type: service
version: 2.5.2
78/esp_lcd_nv3023:
component_hash: fa88abfc19a312eb5e6f2ffa187e0a9faf67e01e758bfb979d3f9d92561a494f
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
- name: idf
require: private
version: '>=4.4'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.0
78/xiaozhi-fonts:
component_hash: d9cc3aad735638e558cee514103b25d2cb60afc78ef53709ffeea443b4d1c835
dependencies:
- name: idf
require: private
version: '>=5.3'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.5.1
chmorgan/esp-libhelix-mp3:
component_hash: cbb76089dc2c5749f7b470e2e70aedc44c9da519e04eb9a67d4c7ec275229e53
dependencies:
- name: idf
require: private
version: '>=4.1.0'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.3
espressif/adc_battery_estimation:
component_hash: a63de81f60adddd5204ed8f286754f1186f16a2fc0f2119152a9d36771f9526a
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: '*'
- name: idf
require: private
version: '>=5.0'
source:
registry_url: https://components.espressif.com/
type: service
version: 0.2.0
espressif/adc_mic:
component_hash: ca6ef69bba4dc0d3e0ce53937635ed2734d38ac947881da81b8526e6851a71a4
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: '*'
- name: espressif/esp_codec_dev
registry_url: https://components.espressif.com
require: private
version: ^1.3.*
- name: idf
require: private
version: '>=5.0'
source:
registry_url: https://components.espressif.com/
type: service
version: 0.2.1
espressif/button:
component_hash: f53face2ab21fa0ffaf4cf0f6e513d393f56df6586bb2ad1146120f03f19ee05
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: '*'
- name: idf
require: private
version: '>=4.0'
source:
registry_url: https://components.espressif.com/
type: service
version: 4.1.3
espressif/cmake_utilities:
component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f
dependencies:
- name: idf
require: private
version: '>=4.1'
source:
registry_url: https://components.espressif.com
type: service
version: 0.5.3
espressif/dl_fft:
component_hash: 7dadbd644c0d7ba4733cc3726ec4cff6edf27b043725e1115861dec1609a3d28
dependencies:
- name: idf
require: private
version: '>=5.0'
source:
registry_url: https://components.espressif.com
type: service
version: 0.3.1
espressif/esp-dsp:
component_hash: 619639efc18cfa361a9e423739b9b0ffc14991effc6c027f955c2f2c3bf1754b
dependencies:
- name: idf
require: private
version: '>=4.2'
source:
registry_url: https://components.espressif.com
type: service
version: 1.6.0
espressif/esp-sr:
component_hash: 12733d9b4aef5d5e295f35c4671835d605992d00583fcd2f8d21166f62c6b071
dependencies:
- name: espressif/dl_fft
registry_url: https://components.espressif.com
require: private
version: '>=0.2.0'
- name: espressif/esp-dsp
registry_url: https://components.espressif.com
require: private
version: 1.6.0
- name: idf
require: private
version: '>=5.0'
source:
registry_url: https://components.espressif.com/
type: service
version: 2.1.5
espressif/esp32-camera:
component_hash: b209707f12aff1683c5c9dfaff3fa60f0eca687f2bf74f133cdef68fbae86028
dependencies:
- name: espressif/esp_jpeg
registry_url: https://components.espressif.com
require: public
version: ^1.3.0
- name: idf
require: private
version: '>=5.1'
source:
registry_url: https://components.espressif.com/
type: service
version: 2.1.2
espressif/esp_codec_dev:
component_hash: 3a1df01ee46d64c55155341bb6f4834db33db7dbd9b637e476e1eab64d01295a
dependencies:
- name: idf
require: private
version: '>=4.0'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.4.0
espressif/esp_io_expander:
component_hash: fbc0de4082fc7c207dc98a93a0ed83d1a929a4ecee83cc567afb406f3ee6d4a6
dependencies:
- name: idf
require: private
version: '>=4.4.2'
source:
registry_url: https://components.espressif.com
type: service
version: 1.0.1
espressif/esp_io_expander_tca9554:
component_hash: 8a9e3a7dbef589a54f814ff22e9121da86bce19d0e1ccffa53f5df6bb346a140
dependencies:
- name: espressif/esp_io_expander
registry_url: https://components.espressif.com
require: public
version: ^1.0.1
- name: idf
require: private
version: '>=5.2'
source:
registry_url: https://components.espressif.com/
type: service
version: 2.0.0
espressif/esp_io_expander_tca95xx_16bit:
component_hash: f0c87a480d3e341fcfa3f7d00890270da1325a294a7d26ddeb5af5fb3ce8ba54
dependencies:
- name: espressif/esp_io_expander
registry_url: https://components.espressif.com
require: public
version: ^1.0.1
- name: idf
require: private
version: '>=5.2'
source:
registry_url: https://components.espressif.com/
type: service
version: 2.0.0
espressif/esp_jpeg:
component_hash: defb83669293cbf86d0fa86b475ba5517aceed04ed70db435388c151ab37b5d7
dependencies:
- name: idf
require: private
version: '>=5.0'
source:
registry_url: https://components.espressif.com
type: service
version: 1.3.1
espressif/esp_lcd_axs15231b:
component_hash: 654890185757826d1d23925afa4bccf874acd7f15aa6fadca1bc0ef408b2d273
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
- name: espressif/esp_lcd_touch
registry_url: https://components.espressif.com
require: public
version: ^1
- name: idf
require: private
version: '>5.0.4,!=5.1.1'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.1
espressif/esp_lcd_gc9a01:
component_hash: 09ddc8f5c9d718a659ffa30a4040e1ae75077e68492c38aec8c2beaaa7b3a9bc
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
- name: idf
require: private
version: '>=4.4'
source:
registry_url: https://components.espressif.com/
type: service
version: 2.0.1
espressif/esp_lcd_ili9341:
component_hash: 31f1b793aa2110dd2ae071c21ccbff0a4eb20d9a4ee40b6294c0dc0ad9552c4e
dependencies:
- name: idf
require: private
version: '>=4.4'
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
source:
registry_url: https://components.espressif.com/
type: service
version: 1.2.0
espressif/esp_lcd_panel_io_additions:
component_hash: 213d5c4a3c322a48c9f993d6bc2372206d34af95db34e87a3e02601bdcad7ce8
dependencies:
- name: idf
require: private
version: '>=4.4.2'
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
- name: espressif/esp_io_expander
registry_url: https://components.espressif.com
require: public
version: ^1
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.1
espressif/esp_lcd_spd2010:
component_hash: 8637c97feeb8d83ba637599f6bf4ef30e83595486930e7df17ffa24f3fef1242
dependencies:
- name: idf
require: private
version: '>5.0.4,!=5.1.1'
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.2
espressif/esp_lcd_st77916:
component_hash: 5fa0f8b1274576d4484e2b8d9358e2a5d09c721511bef0dce6a55b4206b5f0e9
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
- name: idf
require: private
version: '>5.0.4,!=5.1.1'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.1
espressif/esp_lcd_st7796:
component_hash: 00ab2872c56a4037e6d1efda104ef0da00c5296865f8f938cc7cc2fc3e99f962
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
- name: idf
require: private
version: '>=4.4'
source:
registry_url: https://components.espressif.com/
type: service
targets:
- esp32
- esp32s2
- esp32s3
- esp32p4
version: 1.3.2
espressif/esp_lcd_touch:
component_hash: 779b4ba2464a3ae85681e4b860caa5fdc35801458c23f3039ee761bae7f442a4
dependencies:
- name: idf
require: private
version: '>=4.4.2'
source:
registry_url: https://components.espressif.com
type: service
version: 1.1.2
espressif/esp_lcd_touch_cst816s:
component_hash: 51dfa9cee38d8a20f4b625d7f89120dff08c8557e613e09dfb31f53d48648235
dependencies:
- name: espressif/esp_lcd_touch
registry_url: https://components.espressif.com
require: public
version: ^1.0.4
- name: idf
require: private
version: '>=4.4.2'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.1.0
espressif/esp_lcd_touch_ft5x06:
component_hash: abaec05f46a793549b60afdda9eff86e3c8e87782c8c169007911232388d2858
dependencies:
- name: espressif/esp_lcd_touch
registry_url: https://components.espressif.com
require: public
version: ^1.0.4
- name: idf
require: private
version: '>=4.4.2'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.7
espressif/esp_lcd_touch_gt1151:
component_hash: 69dd86cd5fad2442e3bf15c8c4ddddbe9e8739bdb26cd5f2c5249601b4c3cc38
dependencies:
- name: idf
require: private
version: '>=4.4.2'
- name: espressif/esp_lcd_touch
registry_url: https://components.espressif.com
require: public
version: ^1.0.4
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.5~2
espressif/esp_lcd_touch_gt911:
component_hash: acc1c184358aa29ef72506f618c9c76a8cc2bf12af38a2bff3d44d84f3a08857
dependencies:
- name: espressif/esp_lcd_touch
registry_url: https://components.espressif.com
require: public
version: ^1.1.0
- name: idf
require: private
version: '>=4.4.2'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.1.3
espressif/esp_lvgl_port:
component_hash: e720c95cf0667554a204591bb5fade4655fb2990465557041200fa44b5bc7556
dependencies:
- name: idf
require: private
version: '>=4.4'
- name: lvgl/lvgl
registry_url: https://components.espressif.com
require: public
version: '>=8,<10'
source:
registry_url: https://components.espressif.com/
type: service
version: 2.6.0
espressif/esp_mmap_assets:
component_hash: 9b6eea296488103300f31a9e0fe9096f830b99341332a0dfccd7e3ce43814302
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
- name: idf
require: private
version: '>=5.0'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.3.1~2
espressif/esp_new_jpeg:
component_hash: 7c07831bbf11d15f84f42dc0e127094c78063b03d0dce688495773656f998d2b
dependencies: []
source:
registry_url: https://components.espressif.com
type: service
targets:
- esp32
- esp32s2
- esp32s3
- esp32p4
- esp32c2
- esp32c3
- esp32c5
- esp32c6
version: 0.5.1
espressif/freetype:
component_hash: cd5e2d8458e6e8d73f1120ac474467cabb669d8ea4b25050bf6a348c1e89225e
dependencies:
- name: idf
require: private
version: '>=4.4'
source:
registry_url: https://components.espressif.com
type: service
version: 2.13.3~1
espressif/knob:
component_hash: a389d980693ad195b2160de22a72f3391694230188ab16b8f3c7ec4410a7c417
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
- name: idf
require: private
version: '>=4.4.1'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.0
espressif/led_strip:
component_hash: 223998f10cae6d81f2ad2dd3c1103c2221be298c708e37917482b0153f3ec64e
dependencies:
- name: idf
require: private
version: '>=5.0'
source:
registry_url: https://components.espressif.com/
type: service
version: 3.0.1~1
espressif2022/esp_emote_gfx:
component_hash: c95160503ccd2ce4c3cf4194888005ca14f0f47a4cd0bb3f6d5f003733a2c3de
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
- name: espressif/esp_new_jpeg
registry_url: https://components.espressif.com
require: private
version: 0.5.*
- name: espressif/freetype
registry_url: https://components.espressif.com
require: private
version: 2.*
- name: idf
require: private
version: '>=5.0'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.0~2
espressif2022/image_player:
component_hash: 8d09974862ba5323c0c97b77deb3be81ca7bd83e60f77265504c086cd69cc5cf
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
- name: idf
require: private
version: '>=5.0'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.1.0~1
idf:
source:
type: idf
version: 5.5.0
lvgl/lvgl:
component_hash: b702d642e03e95928046d5c6726558e6444e112420c77efa5fdb6650b0a13c5d
dependencies: []
source:
registry_url: https://components.espressif.com/
type: service
version: 9.3.0
tny-robotics/sh1106-esp-idf:
component_hash: 7be190d7c58cd635adf8b74b3a841f8245f2f82f36d135a64d8e53cfab39124d
dependencies: []
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.0
txp666/otto-emoji-gif-component:
component_hash: 22580fe159d3fae35f1c44634e883726f503424eb0c96bc1ee9b2cf9e32b6f2f
dependencies:
- name: idf
require: private
version: '>=5.4.0'
- name: lvgl/lvgl
registry_url: https://components.espressif.com
require: private
version: '>=9.0'
source:
registry_url: https://components.espressif.com/
type: service
targets:
- esp32s3
- esp32
- esp32c3
- esp32c6
- esp32h2
- esp32p4
version: 1.0.2
waveshare/esp_lcd_sh8601:
component_hash: 1a2829e9db4e0515bad6ce83368f199dec60519fbba40251d6c8249f5d070ae1
dependencies:
- name: espressif/cmake_utilities
registry_url: https://components.espressif.com
require: private
version: 0.*
- name: idf
require: private
version: '>=5.3'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.2
waveshare/esp_lcd_touch_cst9217:
component_hash: c5da844e73ab7b7e02938901e71f38deed94b26976045fb4517e7fc1e8dc4db5
dependencies:
- name: espressif/esp_lcd_touch
registry_url: https://components.espressif.com
require: public
version: ^1.1.0
- name: idf
require: private
version: '>=4.4.2'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.3
wvirgil123/sscma_client:
component_hash: bf7d4a1f157303f4850d0b9b465c733ce38e8689ff40ac4743a44520524c41d5
dependencies:
- name: espressif/esp_io_expander
registry_url: https://components.espressif.com
require: private
version: ^1.0.1
- name: idf
require: private
version: '>=4.4.2'
source:
registry_url: https://components.espressif.com/
type: service
version: 1.0.2
direct_dependencies:
- 78/esp-ml307
- 78/esp-opus-encoder
- 78/esp-wifi-connect
- 78/esp_lcd_nv3023
- 78/xiaozhi-fonts
- chmorgan/esp-libhelix-mp3
- espressif/adc_battery_estimation
- espressif/adc_mic
- espressif/button
- espressif/esp-sr
- espressif/esp32-camera
- espressif/esp_codec_dev
- espressif/esp_io_expander_tca9554
- espressif/esp_io_expander_tca95xx_16bit
- espressif/esp_lcd_axs15231b
- espressif/esp_lcd_gc9a01
- espressif/esp_lcd_ili9341
- espressif/esp_lcd_panel_io_additions
- espressif/esp_lcd_spd2010
- espressif/esp_lcd_st77916
- espressif/esp_lcd_st7796
- espressif/esp_lcd_touch_cst816s
- espressif/esp_lcd_touch_ft5x06
- espressif/esp_lcd_touch_gt1151
- espressif/esp_lcd_touch_gt911
- espressif/esp_lvgl_port
- espressif/esp_mmap_assets
- espressif/knob
- espressif/led_strip
- espressif2022/esp_emote_gfx
- espressif2022/image_player
- idf
- lvgl/lvgl
- tny-robotics/sh1106-esp-idf
- txp666/otto-emoji-gif-component
- waveshare/esp_lcd_sh8601
- waveshare/esp_lcd_touch_cst9217
- wvirgil123/sscma_client
manifest_hash: 1af3184e8179db5dd2d5a0a78e420c425305f4b1d7378a0340ce6fbbffd67c14
target: esp32s3
version: 2.0.0
Executable → Regular
View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Executable
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+451 -45
View File
@@ -1,3 +1,4 @@
# Define source files
set(SOURCES "audio/audio_codec.cc" set(SOURCES "audio/audio_codec.cc"
"audio/audio_service.cc" "audio/audio_service.cc"
"audio/codecs/no_audio_codec.cc" "audio/codecs/no_audio_codec.cc"
@@ -14,76 +15,168 @@ set(SOURCES "audio/audio_codec.cc"
"display/display.cc" "display/display.cc"
"display/lcd_display.cc" "display/lcd_display.cc"
"display/oled_display.cc" "display/oled_display.cc"
"display/lvgl_display/lvgl_display.cc"
"display/emote_display.cc"
"display/lvgl_display/emoji_collection.cc"
"display/lvgl_display/lvgl_theme.cc"
"display/lvgl_display/lvgl_font.cc"
"display/lvgl_display/lvgl_image.cc"
"display/lvgl_display/gif/lvgl_gif.cc"
"display/lvgl_display/gif/gifdec.c"
"display/lvgl_display/jpg/image_to_jpeg.cpp"
"display/lvgl_display/jpg/jpeg_encoder.cpp"
"protocols/protocol.cc" "protocols/protocol.cc"
"protocols/mqtt_protocol.cc" "protocols/mqtt_protocol.cc"
"protocols/websocket_protocol.cc" "protocols/websocket_protocol.cc"
"mcp_server.cc" "mcp_server.cc"
"system_info.cc" "system_info.cc"
"application.cc" "application.cc"
"alarm_manager.cc"
"ota.cc" "ota.cc"
"settings.cc" "settings.cc"
"device_state_event.cc" "device_state_event.cc"
"device_manager.cc"
"assets.cc"
"main.cc" "main.cc"
) )
set(INCLUDE_DIRS "." "display" "audio" "protocols") set(INCLUDE_DIRS "." "display" "display/lvgl_display" "display/lvgl_display/jpg" "audio" "protocols")
# 添加板级公共文件 # Add board common files
file(GLOB BOARD_COMMON_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/common/*.cc) file(GLOB BOARD_COMMON_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/common/*.cc)
list(APPEND SOURCES ${BOARD_COMMON_SOURCES}) list(APPEND SOURCES ${BOARD_COMMON_SOURCES})
list(APPEND INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/boards/common) list(APPEND INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/boards/common)
# 根据 BOARD_TYPE 配置添加对应的板级文件 idf_build_get_property(build_components BUILD_COMPONENTS)
# Function to find component dynamically by pattern
function(find_component_by_pattern PATTERN COMPONENT_VAR PATH_VAR)
foreach(COMPONENT ${build_components})
if(COMPONENT MATCHES "${PATTERN}")
set(${COMPONENT_VAR} ${COMPONENT} PARENT_SCOPE)
idf_component_get_property(COMPONENT_PATH ${COMPONENT} COMPONENT_DIR)
set(${PATH_VAR} "${COMPONENT_PATH}" PARENT_SCOPE)
break()
endif()
endforeach()
endfunction()
# Set default BUILTIN_TEXT_FONT and BUILTIN_ICON_FONT
set(BUILTIN_TEXT_FONT font_puhui_14_1)
set(BUILTIN_ICON_FONT font_awesome_14_1)
# Add board files according to BOARD_TYPE
# Set default assets if the board uses partition table V2
if(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI) if(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI)
set(BOARD_TYPE "bread-compact-wifi") set(BOARD_TYPE "bread-compact-wifi")
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
set(BUILTIN_ICON_FONT font_awesome_14_1)
elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_ML307) elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_ML307)
set(BOARD_TYPE "bread-compact-ml307") set(BOARD_TYPE "bread-compact-ml307")
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
set(BUILTIN_ICON_FONT font_awesome_14_1)
elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_ESP32) elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_ESP32)
set(BOARD_TYPE "bread-compact-esp32") set(BOARD_TYPE "bread-compact-esp32")
elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_ESP32_LCD) elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_ESP32_LCD)
set(BOARD_TYPE "bread-compact-esp32-lcd") set(BOARD_TYPE "bread-compact-esp32-lcd")
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
set(BUILTIN_ICON_FONT font_awesome_14_1)
elseif(CONFIG_BOARD_TYPE_DF_K10) elseif(CONFIG_BOARD_TYPE_DF_K10)
set(BOARD_TYPE "df-k10") set(BOARD_TYPE "df-k10")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_DF_S3_AI_CAM) elseif(CONFIG_BOARD_TYPE_DF_S3_AI_CAM)
set(BOARD_TYPE "df-s3-ai-cam") set(BOARD_TYPE "df-s3-ai-cam")
elseif(CONFIG_BOARD_TYPE_ESP_BOX_3) elseif(CONFIG_BOARD_TYPE_ESP_BOX_3)
set(BOARD_TYPE "esp-box-3") set(BOARD_TYPE "esp-box-3")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP_BOX) elseif(CONFIG_BOARD_TYPE_ESP_BOX)
set(BOARD_TYPE "esp-box") set(BOARD_TYPE "esp-box")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP_BOX_LITE) elseif(CONFIG_BOARD_TYPE_ESP_BOX_LITE)
set(BOARD_TYPE "esp-box-lite") set(BOARD_TYPE "esp-box-lite")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_KEVIN_BOX_1) elseif(CONFIG_BOARD_TYPE_KEVIN_BOX_1)
set(BOARD_TYPE "kevin-box-1") set(BOARD_TYPE "kevin-box-1")
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
set(BUILTIN_ICON_FONT font_awesome_14_1)
elseif(CONFIG_BOARD_TYPE_KEVIN_BOX_2) elseif(CONFIG_BOARD_TYPE_KEVIN_BOX_2)
set(BOARD_TYPE "kevin-box-2") set(BOARD_TYPE "kevin-box-2")
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
set(BUILTIN_ICON_FONT font_awesome_14_1)
elseif(CONFIG_BOARD_TYPE_KEVIN_C3) elseif(CONFIG_BOARD_TYPE_KEVIN_C3)
set(BOARD_TYPE "kevin-c3") set(BOARD_TYPE "kevin-c3")
elseif(CONFIG_BOARD_TYPE_KEVIN_SP_V3_DEV) elseif(CONFIG_BOARD_TYPE_KEVIN_SP_V3_DEV)
set(BOARD_TYPE "kevin-sp-v3-dev") set(BOARD_TYPE "kevin-sp-v3-dev")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_KEVIN_SP_V4_DEV) elseif(CONFIG_BOARD_TYPE_KEVIN_SP_V4_DEV)
set(BOARD_TYPE "kevin-sp-v4-dev") set(BOARD_TYPE "kevin-sp-v4-dev")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_KEVIN_YUYING_313LCD) elseif(CONFIG_BOARD_TYPE_KEVIN_YUYING_313LCD)
set(BOARD_TYPE "kevin-yuying-313lcd") set(BOARD_TYPE "kevin-yuying-313lcd")
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
set(BUILTIN_ICON_FONT font_awesome_30_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_LICHUANG_DEV) elseif(CONFIG_BOARD_TYPE_LICHUANG_DEV)
set(BOARD_TYPE "lichuang-dev") set(BOARD_TYPE "lichuang-dev")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_LICHUANG_C3_DEV) elseif(CONFIG_BOARD_TYPE_LICHUANG_C3_DEV)
set(BOARD_TYPE "lichuang-c3-dev") set(BOARD_TYPE "lichuang-c3-dev")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_MAGICLICK_2P4) elseif(CONFIG_BOARD_TYPE_MAGICLICK_2P4)
set(BOARD_TYPE "magiclick-2p4") set(BOARD_TYPE "magiclick-2p4")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_MAGICLICK_2P5) elseif(CONFIG_BOARD_TYPE_MAGICLICK_2P5)
set(BOARD_TYPE "magiclick-2p5") set(BOARD_TYPE "magiclick-2p5")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_MAGICLICK_C3) elseif(CONFIG_BOARD_TYPE_MAGICLICK_C3)
set(BOARD_TYPE "magiclick-c3") set(BOARD_TYPE "magiclick-c3")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_MAGICLICK_C3_V2) elseif(CONFIG_BOARD_TYPE_MAGICLICK_C3_V2)
set(BOARD_TYPE "magiclick-c3-v2") set(BOARD_TYPE "magiclick-c3-v2")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_M5STACK_CORE_S3) elseif(CONFIG_BOARD_TYPE_M5STACK_CORE_S3)
set(BOARD_TYPE "m5stack-core-s3") set(BOARD_TYPE "m5stack-core-s3")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_M5STACK_CORE_TAB5) elseif(CONFIG_BOARD_TYPE_M5STACK_CORE_TAB5)
set(BOARD_TYPE "m5stack-tab5") set(BOARD_TYPE "m5stack-tab5")
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
set(BUILTIN_ICON_FONT font_awesome_30_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ATOMS3_ECHO_BASE) elseif(CONFIG_BOARD_TYPE_ATOMS3_ECHO_BASE)
set(BOARD_TYPE "atoms3-echo-base") set(BOARD_TYPE "atoms3-echo-base")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_ATOMS3R_ECHO_BASE) elseif(CONFIG_BOARD_TYPE_ATOMS3R_ECHO_BASE)
set(BOARD_TYPE "atoms3r-echo-base") set(BOARD_TYPE "atoms3r-echo-base")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_ATOMS3R_CAM_M12_ECHO_BASE) elseif(CONFIG_BOARD_TYPE_ATOMS3R_CAM_M12_ECHO_BASE)
set(BOARD_TYPE "atoms3r-cam-m12-echo-base") set(BOARD_TYPE "atoms3r-cam-m12-echo-base")
elseif(CONFIG_BOARD_TYPE_ATOM_ECHOS3R) elseif(CONFIG_BOARD_TYPE_ATOM_ECHOS3R)
@@ -92,155 +185,352 @@ elseif(CONFIG_BOARD_TYPE_ATOMMATRIX_ECHO_BASE)
set(BOARD_TYPE "atommatrix-echo-base") set(BOARD_TYPE "atommatrix-echo-base")
elseif(CONFIG_BOARD_TYPE_XMINI_C3_V3) elseif(CONFIG_BOARD_TYPE_XMINI_C3_V3)
set(BOARD_TYPE "xmini-c3-v3") set(BOARD_TYPE "xmini-c3-v3")
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
set(BUILTIN_ICON_FONT font_awesome_14_1)
elseif(CONFIG_BOARD_TYPE_XMINI_C3_4G) elseif(CONFIG_BOARD_TYPE_XMINI_C3_4G)
set(BOARD_TYPE "xmini-c3-4g") set(BOARD_TYPE "xmini-c3-4g")
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
set(BUILTIN_ICON_FONT font_awesome_14_1)
elseif(CONFIG_BOARD_TYPE_XMINI_C3) elseif(CONFIG_BOARD_TYPE_XMINI_C3)
set(BOARD_TYPE "xmini-c3") set(BOARD_TYPE "xmini-c3")
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
set(BUILTIN_ICON_FONT font_awesome_14_1)
elseif(CONFIG_BOARD_TYPE_ESP32S3_KORVO2_V3) elseif(CONFIG_BOARD_TYPE_ESP32S3_KORVO2_V3)
set(BOARD_TYPE "esp32s3-korvo2-v3") set(BOARD_TYPE "esp32s3-korvo2-v3")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP_SPARKBOT) elseif(CONFIG_BOARD_TYPE_ESP_SPARKBOT)
set(BOARD_TYPE "esp-sparkbot") set(BOARD_TYPE "esp-sparkbot")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP_SPOT_S3) elseif(CONFIG_BOARD_TYPE_ESP_SPOT_S3)
set(BOARD_TYPE "esp-spot-s3") set(BOARD_TYPE "esp-spot-s3")
elseif(CONFIG_BOARD_TYPE_ESP_HI) elseif(CONFIG_BOARD_TYPE_ESP_HI)
set(BOARD_TYPE "esp-hi") set(BOARD_TYPE "esp-hi")
# Set ESP_HI emoji directory for DEFAULT_ASSETS_EXTRA_FILES
set(DEFAULT_ASSETS_EXTRA_FILES "${CMAKE_BINARY_DIR}/emoji")
elseif(CONFIG_BOARD_TYPE_ECHOEAR) elseif(CONFIG_BOARD_TYPE_ECHOEAR)
set(BOARD_TYPE "echoear") set(BOARD_TYPE "echoear")
set(BUILTIN_TEXT_FONT font_puhui_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32S3_AUDIO_BOARD)
set(BOARD_TYPE "waveshare-s3-audio-board")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_AMOLED_1_8) elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_AMOLED_1_8)
set(BOARD_TYPE "esp32-s3-touch-amoled-1.8") set(BOARD_TYPE "esp32-s3-touch-amoled-1.8")
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
set(BUILTIN_ICON_FONT font_awesome_30_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_AMOLED_2_06) elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_AMOLED_2_06)
set(BOARD_TYPE "waveshare-s3-touch-amoled-2.06") set(BOARD_TYPE "waveshare-s3-touch-amoled-2.06")
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
set(BUILTIN_ICON_FONT font_awesome_30_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_4B)
set(BOARD_TYPE "waveshare-s3-touch-lcd-4b")
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
set(BUILTIN_ICON_FONT font_awesome_30_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_AMOLED_1_75) elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_AMOLED_1_75)
set(BOARD_TYPE "waveshare-s3-touch-amoled-1.75") set(BOARD_TYPE "waveshare-s3-touch-amoled-1.75")
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
set(BUILTIN_ICON_FONT font_awesome_30_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_85C) elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_85C)
set(BOARD_TYPE "esp32-s3-touch-lcd-1.85c") set(BOARD_TYPE "esp32-s3-touch-lcd-1.85c")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_85) elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_85)
set(BOARD_TYPE "esp32-s3-touch-lcd-1.85") set(BOARD_TYPE "esp32-s3-touch-lcd-1.85")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_46) elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_46)
set(BOARD_TYPE "esp32-s3-touch-lcd-1.46") set(BOARD_TYPE "esp32-s3-touch-lcd-1.46")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_3_5) elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_3_5)
set(BOARD_TYPE "esp32-s3-touch-lcd-3.5") set(BOARD_TYPE "esp32-s3-touch-lcd-3.5")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_3_5B) elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_3_5B)
set(BOARD_TYPE "waveshare-s3-touch-lcd-3.5b") set(BOARD_TYPE "waveshare-s3-touch-lcd-3.5b")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_3_49)
set(BOARD_TYPE "waveshare-s3-touch-lcd-3.49")
set(LVGL_TEXT_FONT font_puhui_basic_30_4)
set(LVGL_ICON_FONT font_awesome_30_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32C6_LCD_1_69) elseif(CONFIG_BOARD_TYPE_ESP32C6_LCD_1_69)
set(BOARD_TYPE "waveshare-c6-lcd-1.69") set(BOARD_TYPE "waveshare-c6-lcd-1.69")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32C6_Touch_AMOLED_1_43) elseif(CONFIG_BOARD_TYPE_ESP32C6_Touch_AMOLED_1_43)
set(BOARD_TYPE "waveshare-c6-touch-amoled-1.43") set(BOARD_TYPE "waveshare-c6-touch-amoled-1.43")
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
set(BUILTIN_ICON_FONT font_awesome_30_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32P4_NANO) elseif(CONFIG_BOARD_TYPE_ESP32P4_NANO)
set(BOARD_TYPE "waveshare-p4-nano") set(BOARD_TYPE "waveshare-p4-nano")
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
set(BUILTIN_ICON_FONT font_awesome_30_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_4B) elseif(CONFIG_BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_4B)
set(BOARD_TYPE "waveshare-p4-wifi6-touch-lcd-4b") set(BOARD_TYPE "waveshare-p4-wifi6-touch-lcd-4b")
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
set(BUILTIN_ICON_FONT font_awesome_30_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_XC) elseif(CONFIG_BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_XC)
set(BOARD_TYPE "waveshare-p4-wifi6-touch-lcd-xc") set(BOARD_TYPE "waveshare-p4-wifi6-touch-lcd-xc")
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
set(BUILTIN_ICON_FONT font_awesome_30_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI_LCD) elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI_LCD)
set(BOARD_TYPE "bread-compact-wifi-lcd") set(BOARD_TYPE "bread-compact-wifi-lcd")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_TUDOUZI) elseif(CONFIG_BOARD_TYPE_TUDOUZI)
set(BOARD_TYPE "tudouzi") set(BOARD_TYPE "tudouzi")
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
set(BUILTIN_ICON_FONT font_awesome_14_1)
elseif(CONFIG_BOARD_TYPE_LILYGO_T_CIRCLE_S3) elseif(CONFIG_BOARD_TYPE_LILYGO_T_CIRCLE_S3)
set(BOARD_TYPE "lilygo-t-circle-s3") set(BOARD_TYPE "lilygo-t-circle-s3")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3_V1_0_V1_1) elseif(CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3_V1_0_V1_1)
set(BOARD_TYPE "lilygo-t-cameraplus-s3") set(BOARD_TYPE "lilygo-t-cameraplus-s3")
elseif(CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3_V1_2) set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BOARD_TYPE "lilygo-t-cameraplus-s3") set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_LILYGO_T_DISPLAY_S3_PRO_MVSRLORA) elseif(CONFIG_BOARD_TYPE_LILYGO_T_DISPLAY_S3_PRO_MVSRLORA)
set(BOARD_TYPE "lilygo-t-display-s3-pro-mvsrlora") set(BOARD_TYPE "lilygo-t-display-s3-pro-mvsrlora")
elseif(CONFIG_BOARD_TYPE_LILYGO_T_DISPLAY_S3_PRO_MVSRLORA_NO_BATTERY) set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BOARD_TYPE "lilygo-t-display-s3-pro-mvsrlora") set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_MOVECALL_MOJI_ESP32S3) elseif(CONFIG_BOARD_TYPE_MOVECALL_MOJI_ESP32S3)
set(BOARD_TYPE "movecall-moji-esp32s3") set(BOARD_TYPE "movecall-moji-esp32s3")
elseif(CONFIG_BOARD_TYPE_MOVECALL_CUICAN_ESP32S3) set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_MOVECALL_CUICAN_ESP32S3)
set(BOARD_TYPE "movecall-cuican-esp32s3") set(BOARD_TYPE "movecall-cuican-esp32s3")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3) elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3)
set(BOARD_TYPE "atk-dnesp32s3") set(BOARD_TYPE "atk-dnesp32s3")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX) elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX)
set(BOARD_TYPE "atk-dnesp32s3-box") set(BOARD_TYPE "atk-dnesp32s3-box")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX0) elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX0)
set(BOARD_TYPE "atk-dnesp32s3-box0") set(BOARD_TYPE "atk-dnesp32s3-box0")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX2_WIFI) elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX2_WIFI)
set(BOARD_TYPE "atk-dnesp32s3-box2-wifi") set(BOARD_TYPE "atk-dnesp32s3-box2-wifi")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX2_4G) elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX2_4G)
set(BOARD_TYPE "atk-dnesp32s3-box2-4g") set(BOARD_TYPE "atk-dnesp32s3-box2-4g")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3M_WIFI) elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3M_WIFI)
set(BOARD_TYPE "atk-dnesp32s3m-wifi") set(BOARD_TYPE "atk-dnesp32s3m-wifi")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3M_4G) elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3M_4G)
set(BOARD_TYPE "atk-dnesp32s3m-4g") set(BOARD_TYPE "atk-dnesp32s3m-4g")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_DU_CHATX) elseif(CONFIG_BOARD_TYPE_DU_CHATX)
set(BOARD_TYPE "du-chatx") set(BOARD_TYPE "du-chatx")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_ESP32S3_Taiji_Pi) elseif(CONFIG_BOARD_TYPE_ESP32S3_Taiji_Pi)
set(BOARD_TYPE "taiji-pi-s3") set(BOARD_TYPE "taiji-pi-s3")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_0_85TFT_WIFI) elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_0_85TFT_WIFI)
set(BOARD_TYPE "xingzhi-cube-0.85tft-wifi") set(BOARD_TYPE "xingzhi-cube-0.85tft-wifi")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_0_85TFT_ML307) elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_0_85TFT_ML307)
set(BOARD_TYPE "xingzhi-cube-0.85tft-ml307") set(BOARD_TYPE "xingzhi-cube-0.85tft-ml307")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_0_96OLED_WIFI) elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_0_96OLED_WIFI)
set(BOARD_TYPE "xingzhi-cube-0.96oled-wifi") set(BOARD_TYPE "xingzhi-cube-0.96oled-wifi")
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
set(BUILTIN_ICON_FONT font_awesome_14_1)
elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_0_96OLED_ML307) elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_0_96OLED_ML307)
set(BOARD_TYPE "xingzhi-cube-0.96oled-ml307") set(BOARD_TYPE "xingzhi-cube-0.96oled-ml307")
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
set(BUILTIN_ICON_FONT font_awesome_14_1)
elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_1_54TFT_WIFI) elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_1_54TFT_WIFI)
set(BOARD_TYPE "xingzhi-cube-1.54tft-wifi") set(BOARD_TYPE "xingzhi-cube-1.54tft-wifi")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_1_54TFT_ML307) elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_1_54TFT_ML307)
set(BOARD_TYPE "xingzhi-cube-1.54tft-ml307") set(BOARD_TYPE "xingzhi-cube-1.54tft-ml307")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_SENSECAP_WATCHER) elseif(CONFIG_BOARD_TYPE_SENSECAP_WATCHER)
set(BOARD_TYPE "sensecap-watcher") set(BOARD_TYPE "sensecap-watcher")
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_DOIT_S3_AIBOX) elseif(CONFIG_BOARD_TYPE_DOIT_S3_AIBOX)
set(BOARD_TYPE "doit-s3-aibox") set(BOARD_TYPE "doit-s3-aibox")
elseif(CONFIG_BOARD_TYPE_MIXGO_NOVA) elseif(CONFIG_BOARD_TYPE_MIXGO_NOVA)
set(BOARD_TYPE "mixgo-nova") set(BOARD_TYPE "mixgo-nova")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_GENJUTECH_S3_1_54TFT) elseif(CONFIG_BOARD_TYPE_GENJUTECH_S3_1_54TFT)
set(BOARD_TYPE "genjutech-s3-1.54tft") set(BOARD_TYPE "genjutech-s3-1.54tft")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32_CGC) elseif(CONFIG_BOARD_TYPE_ESP32_CGC)
set(BOARD_TYPE "esp32-cgc") set(BOARD_TYPE "esp32-cgc")
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
set(BUILTIN_ICON_FONT font_awesome_14_1)
elseif(CONFIG_BOARD_TYPE_ESP32_CGC_144) elseif(CONFIG_BOARD_TYPE_ESP32_CGC_144)
set(BOARD_TYPE "esp32-cgc-144") set(BOARD_TYPE "esp32-cgc-144")
set(BUILTIN_TEXT_FONT font_puhui_basic_14_1)
set(BUILTIN_ICON_FONT font_awesome_14_1)
elseif(CONFIG_BOARD_TYPE_ESP_S3_LCD_EV_Board) elseif(CONFIG_BOARD_TYPE_ESP_S3_LCD_EV_Board)
set(BOARD_TYPE "esp-s3-lcd-ev-board") set(BOARD_TYPE "esp-s3-lcd-ev-board")
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
set(BUILTIN_ICON_FONT font_awesome_30_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP_S3_LCD_EV_Board_2) elseif(CONFIG_BOARD_TYPE_ESP_S3_LCD_EV_Board_2)
set(BOARD_TYPE "esp-s3-lcd-ev-board-2") set(BOARD_TYPE "esp-s3-lcd-ev-board-2")
set(BUILTIN_TEXT_FONT font_puhui_basic_30_4)
set(BUILTIN_ICON_FONT font_awesome_30_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ZHENGCHEN_1_54TFT_WIFI) elseif(CONFIG_BOARD_TYPE_ZHENGCHEN_1_54TFT_WIFI)
set(BOARD_TYPE "zhengchen-1.54tft-wifi") set(BOARD_TYPE "zhengchen-1.54tft-wifi")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_MINSI_K08_DUAL) elseif(CONFIG_BOARD_TYPE_MINSI_K08_DUAL)
set(BOARD_TYPE "minsi-k08-dual") set(BOARD_TYPE "minsi-k08-dual")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ZHENGCHEN_1_54TFT_ML307) elseif(CONFIG_BOARD_TYPE_ZHENGCHEN_1_54TFT_ML307)
set(BOARD_TYPE "zhengchen-1.54tft-ml307") set(BOARD_TYPE "zhengchen-1.54tft-ml307")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_ESP32_S3_1_54_MUMA) elseif(CONFIG_BOARD_TYPE_ESP32_S3_1_54_MUMA)
set(BOARD_TYPE "sp-esp32-s3-1.54-muma") set(BOARD_TYPE "sp-esp32-s3-1.54-muma")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_ESP32_S3_1_28_BOX) elseif(CONFIG_BOARD_TYPE_ESP32_S3_1_28_BOX)
set(BOARD_TYPE "sp-esp32-s3-1.28-box") set(BOARD_TYPE "sp-esp32-s3-1.28-box")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_OTTO_ROBOT) elseif(CONFIG_BOARD_TYPE_OTTO_ROBOT)
set(BOARD_TYPE "otto-robot") set(BOARD_TYPE "otto-robot")
set(BUILTIN_TEXT_FONT font_puhui_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
elseif(CONFIG_BOARD_TYPE_ELECTRON_BOT) elseif(CONFIG_BOARD_TYPE_ELECTRON_BOT)
set(BOARD_TYPE "electron-bot") set(BOARD_TYPE "electron-bot")
set(BUILTIN_TEXT_FONT font_puhui_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI_CAM) elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI_CAM)
set(BOARD_TYPE "bread-compact-wifi-s3cam") set(BOARD_TYPE "bread-compact-wifi-s3cam")
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
set(BUILTIN_ICON_FONT font_awesome_16_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_JIUCHUAN) elseif(CONFIG_BOARD_TYPE_JIUCHUAN)
set(BOARD_TYPE "jiuchuan-s3") set(BOARD_TYPE "jiuchuan-s3")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_LABPLUS_MPYTHON_V3) elseif(CONFIG_BOARD_TYPE_LABPLUS_MPYTHON_V3)
set(BOARD_TYPE "labplus-mpython-v3") set(BOARD_TYPE "labplus-mpython-v3")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_LABPLUS_LEDONG_V2) elseif(CONFIG_BOARD_TYPE_LABPLUS_LEDONG_V2)
set(BOARD_TYPE "labplus-ledong-v2") set(BOARD_TYPE "labplus-ledong-v2")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
elseif(CONFIG_BOARD_TYPE_SURFER_C3_1_14TFT) elseif(CONFIG_BOARD_TYPE_SURFER_C3_1_14TFT)
set(BOARD_TYPE "surfer-c3-1.14tft") set(BOARD_TYPE "surfer-c3-1.14tft")
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
elseif(CONFIG_BOARD_TYPE_YUNLIAO_S3)
set(BOARD_TYPE "yunliao-s3")
set(LVGL_TEXT_FONT ${FONT_PUHUI_BASIC_20_4})
set(LVGL_ICON_FONT ${FONT_AWESOME_20_4})
set(DEFAULT_ASSETS ${ASSETS_XIAOZHI_PUHUI_COMMON_20_4_EMOJI_64})
elseif(CONFIG_BOARD_TYPE_JINAO_S3)
set(BOARD_TYPE "jinao-s3")
set(LVGL_TEXT_FONT ${FONT_PUHUI_BASIC_20_4})
set(LVGL_ICON_FONT ${FONT_AWESOME_20_4})
set(DEFAULT_ASSETS ${ASSETS_XIAOZHI_PUHUI_COMMON_20_4_EMOJI_64})
set(BUILTIN_TEXT_FONT font_puhui_basic_20_4)
set(BUILTIN_ICON_FONT font_awesome_20_4)
set(DEFAULT_EMOJI_COLLECTION twemoji_64)
endif() endif()
file(GLOB BOARD_SOURCES file(GLOB BOARD_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.c ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.c
) )
list(APPEND SOURCES ${BOARD_SOURCES}) list(APPEND SOURCES ${BOARD_SOURCES})
# Select audio processor according to Kconfig
if(CONFIG_USE_AUDIO_PROCESSOR) if(CONFIG_USE_AUDIO_PROCESSOR)
list(APPEND SOURCES "audio/processors/afe_audio_processor.cc") list(APPEND SOURCES "audio/processors/afe_audio_processor.cc")
else() else()
list(APPEND SOURCES "audio/processors/no_audio_processor.cc") list(APPEND SOURCES "audio/processors/no_audio_processor.cc")
endif() endif()
if(CONFIG_USE_AFE_WAKE_WORD) if(CONFIG_IDF_TARGET_ESP32S3 OR CONFIG_IDF_TARGET_ESP32P4)
list(APPEND SOURCES "audio/wake_words/afe_wake_word.cc") list(APPEND SOURCES "audio/wake_words/afe_wake_word.cc")
elseif(CONFIG_USE_ESP_WAKE_WORD)
list(APPEND SOURCES "audio/wake_words/esp_wake_word.cc")
elseif(CONFIG_USE_CUSTOM_WAKE_WORD)
list(APPEND SOURCES "audio/wake_words/custom_wake_word.cc") list(APPEND SOURCES "audio/wake_words/custom_wake_word.cc")
else()
list(APPEND SOURCES "audio/wake_words/esp_wake_word.cc")
endif() endif()
# 根据Kconfig选择语言目录 # Select language directory according to Kconfig
if(CONFIG_LANGUAGE_ZH_CN) if(CONFIG_LANGUAGE_ZH_CN)
set(LANG_DIR "zh-CN") set(LANG_DIR "zh-CN")
elseif(CONFIG_LANGUAGE_ZH_TW) elseif(CONFIG_LANGUAGE_ZH_TW)
@@ -287,13 +577,13 @@ elseif(CONFIG_LANGUAGE_RO_RO)
set(LANG_DIR "ro-RO") set(LANG_DIR "ro-RO")
endif() endif()
# 定义生成路径 # Define generation path
set(LANG_JSON "${CMAKE_CURRENT_SOURCE_DIR}/assets/locales/${LANG_DIR}/language.json") set(LANG_JSON "${CMAKE_CURRENT_SOURCE_DIR}/assets/locales/${LANG_DIR}/language.json")
set(LANG_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/assets/lang_config.h") set(LANG_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/assets/lang_config.h")
file(GLOB LANG_SOUNDS ${CMAKE_CURRENT_SOURCE_DIR}/assets/locales/${LANG_DIR}/*.ogg) file(GLOB LANG_SOUNDS ${CMAKE_CURRENT_SOURCE_DIR}/assets/locales/${LANG_DIR}/*.ogg)
file(GLOB COMMON_SOUNDS ${CMAKE_CURRENT_SOURCE_DIR}/assets/common/*.ogg) file(GLOB COMMON_SOUNDS ${CMAKE_CURRENT_SOURCE_DIR}/assets/common/*.ogg)
# 如果目标芯片是 ESP32,则排除特定文件 # If target chip is ESP32, exclude specific files to avoid build errors
if(CONFIG_IDF_TARGET_ESP32) if(CONFIG_IDF_TARGET_ESP32)
list(REMOVE_ITEM SOURCES "audio/codecs/box_audio_codec.cc" list(REMOVE_ITEM SOURCES "audio/codecs/box_audio_codec.cc"
"audio/codecs/es8388_audio_codec.cc" "audio/codecs/es8388_audio_codec.cc"
@@ -308,16 +598,17 @@ idf_component_register(SRCS ${SOURCES}
WHOLE_ARCHIVE WHOLE_ARCHIVE
) )
# 使用 target_compile_definitions 来定义 BOARD_TYPE, BOARD_NAME # Use target_compile_definitions to define BOARD_TYPE, BOARD_NAME
# 如果 BOARD_NAME 为空,则使用 BOARD_TYPE # If BOARD_NAME is empty, use BOARD_TYPE
if(NOT BOARD_NAME) if(NOT BOARD_NAME)
set(BOARD_NAME ${BOARD_TYPE}) set(BOARD_NAME ${BOARD_TYPE})
endif() endif()
target_compile_definitions(${COMPONENT_LIB} target_compile_definitions(${COMPONENT_LIB}
PRIVATE BOARD_TYPE=\"${BOARD_TYPE}\" BOARD_NAME=\"${BOARD_NAME}\" PRIVATE BOARD_TYPE=\"${BOARD_TYPE}\" BOARD_NAME=\"${BOARD_NAME}\"
PRIVATE BUILTIN_TEXT_FONT=${BUILTIN_TEXT_FONT} BUILTIN_ICON_FONT=${BUILTIN_ICON_FONT}
) )
# 添加生成规则 # Add generation rules
add_custom_command( add_custom_command(
OUTPUT ${LANG_HEADER} OUTPUT ${LANG_HEADER}
COMMAND python ${PROJECT_DIR}/scripts/gen_lang.py COMMAND python ${PROJECT_DIR}/scripts/gen_lang.py
@@ -329,15 +620,27 @@ add_custom_command(
COMMENT "Generating ${LANG_DIR} language config" COMMENT "Generating ${LANG_DIR} language config"
) )
# 强制建立生成依赖 # Force build generation dependencies
add_custom_target(lang_header ALL add_custom_target(lang_header ALL
DEPENDS ${LANG_HEADER} DEPENDS ${LANG_HEADER}
) )
# Find ESP-SR component dynamically
find_component_by_pattern("espressif__esp-sr" ESP_SR_COMPONENT ESP_SR_COMPONENT_PATH)
if(ESP_SR_COMPONENT_PATH)
set(ESP_SR_MODEL_PATH "${ESP_SR_COMPONENT_PATH}/model")
endif()
# Find xiaozhi-fonts component dynamically
find_component_by_pattern("xiaozhi-fonts" XIAOZHI_FONTS_COMPONENT XIAOZHI_FONTS_COMPONENT_PATH)
if(XIAOZHI_FONTS_COMPONENT_PATH)
set(XIAOZHI_FONTS_PATH "${XIAOZHI_FONTS_COMPONENT_PATH}")
endif()
if(CONFIG_BOARD_TYPE_ESP_HI) if(CONFIG_BOARD_TYPE_ESP_HI)
set(URL "https://github.com/espressif2022/image_player/raw/main/test_apps/test_8bit") set(URL "https://github.com/espressif2022/image_player/raw/main/test_apps/test_8bit")
set(SPIFFS_DIR "${CMAKE_BINARY_DIR}/emoji") set(EMOJI_DIR "${CMAKE_BINARY_DIR}/emoji")
file(MAKE_DIRECTORY ${SPIFFS_DIR}) file(MAKE_DIRECTORY ${EMOJI_DIR})
# List all files to download # List all files to download
set(FILES_TO_DOWNLOAD "") set(FILES_TO_DOWNLOAD "")
@@ -353,9 +656,9 @@ list(APPEND FILES_TO_DOWNLOAD "panic_return.aaf" "wake.aaf")
foreach(FILENAME IN LISTS FILES_TO_DOWNLOAD) foreach(FILENAME IN LISTS FILES_TO_DOWNLOAD)
set(REMOTE_FILE "${URL}/${FILENAME}") set(REMOTE_FILE "${URL}/${FILENAME}")
set(LOCAL_FILE "${SPIFFS_DIR}/${FILENAME}") set(LOCAL_FILE "${EMOJI_DIR}/${FILENAME}")
# 检查本地文件是否存在 # Check if local file exists
if(EXISTS ${LOCAL_FILE}) if(EXISTS ${LOCAL_FILE})
message(STATUS "File ${FILENAME} already exists, skipping download") message(STATUS "File ${FILENAME} already exists, skipping download")
else() else()
@@ -369,31 +672,134 @@ foreach(FILENAME IN LISTS FILES_TO_DOWNLOAD)
endif() endif()
endforeach() endforeach()
spiffs_create_partition_assets(
assets_A
${SPIFFS_DIR}
FLASH_IN_PROJECT
MMAP_FILE_SUPPORT_FORMAT ".aaf"
)
endif() endif()
if(CONFIG_BOARD_TYPE_ECHOEAR)
idf_build_get_property(build_components BUILD_COMPONENTS) # Function to build default assets based on configuration
foreach(COMPONENT ${build_components}) function(build_default_assets_bin)
if(COMPONENT MATCHES "esp_emote_gfx" OR COMPONENT MATCHES "espressif2022__esp_emote_gfx") # Set output path for generated assets.bin
set(EMOTE_GFX_COMPONENT ${COMPONENT}) set(GENERATED_ASSETS_BIN "${CMAKE_BINARY_DIR}/generated_assets.bin")
idf_component_get_property(EMOTE_GFX_COMPONENT_PATH ${EMOTE_GFX_COMPONENT} COMPONENT_DIR)
set(SPIFFS_DIR "${EMOTE_GFX_COMPONENT_PATH}/emoji_normal") # Prepare arguments for build script
break() set(BUILD_ARGS
"--sdkconfig" "${SDKCONFIG}"
"--output" "${GENERATED_ASSETS_BIN}"
)
# Add builtin text font if defined
if(BUILTIN_TEXT_FONT)
list(APPEND BUILD_ARGS "--builtin_text_font" "${BUILTIN_TEXT_FONT}")
endif() endif()
endforeach()
spiffs_create_partition_assets( # Add default emoji collection if defined
assets_A if(DEFAULT_EMOJI_COLLECTION)
${SPIFFS_DIR} list(APPEND BUILD_ARGS "--emoji_collection" "${DEFAULT_EMOJI_COLLECTION}")
FLASH_IN_PROJECT endif()
MMAP_FILE_SUPPORT_FORMAT ".aaf, ttf, bin"
IMPORT_INC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE} # Add default assets extra files if defined
) if(DEFAULT_ASSETS_EXTRA_FILES)
list(APPEND BUILD_ARGS "--extra_files" "${DEFAULT_ASSETS_EXTRA_FILES}")
endif()
list(APPEND BUILD_ARGS "--esp_sr_model_path" "${ESP_SR_MODEL_PATH}")
list(APPEND BUILD_ARGS "--xiaozhi_fonts_path" "${XIAOZHI_FONTS_PATH}")
# Create custom command to build assets
add_custom_command(
OUTPUT ${GENERATED_ASSETS_BIN}
COMMAND python ${PROJECT_DIR}/scripts/build_default_assets.py ${BUILD_ARGS}
DEPENDS
${SDKCONFIG}
${PROJECT_DIR}/scripts/build_default_assets.py
COMMENT "Building default assets.bin based on configuration"
VERBATIM
)
# Create target for generated assets
add_custom_target(generated_default_assets ALL
DEPENDS ${GENERATED_ASSETS_BIN}
)
# Set the generated file path in parent scope
set(GENERATED_ASSETS_LOCAL_FILE ${GENERATED_ASSETS_BIN} PARENT_SCOPE)
message(STATUS "Default assets build configured: ${GENERATED_ASSETS_BIN}")
endfunction()
# Function to get local assets file path (handles both URL and local file)
function(get_assets_local_file assets_source assets_local_file_var)
# Check if it's a URL (starts with http:// or https://)
if(assets_source MATCHES "^https?://")
# It's a URL, download it
get_filename_component(ASSETS_FILENAME "${assets_source}" NAME)
set(ASSETS_LOCAL_FILE "${CMAKE_BINARY_DIR}/${ASSETS_FILENAME}")
set(ASSETS_TEMP_FILE "${CMAKE_BINARY_DIR}/${ASSETS_FILENAME}.tmp")
# Check if local file exists
if(EXISTS ${ASSETS_LOCAL_FILE})
message(STATUS "Assets file ${ASSETS_FILENAME} already exists, skipping download")
else()
message(STATUS "Downloading ${ASSETS_FILENAME}")
# Clean up any existing temp file
if(EXISTS ${ASSETS_TEMP_FILE})
file(REMOVE ${ASSETS_TEMP_FILE})
endif()
# Download to temporary file first
file(DOWNLOAD ${assets_source} ${ASSETS_TEMP_FILE}
STATUS DOWNLOAD_STATUS)
list(GET DOWNLOAD_STATUS 0 STATUS_CODE)
if(NOT STATUS_CODE EQUAL 0)
# Clean up temp file on failure
if(EXISTS ${ASSETS_TEMP_FILE})
file(REMOVE ${ASSETS_TEMP_FILE})
endif()
message(FATAL_ERROR "Failed to download ${ASSETS_FILENAME} from ${assets_source}")
endif()
# Move temp file to final location (atomic operation)
file(RENAME ${ASSETS_TEMP_FILE} ${ASSETS_LOCAL_FILE})
message(STATUS "Successfully downloaded ${ASSETS_FILENAME}")
endif()
else()
# It's a local file path
if(IS_ABSOLUTE "${assets_source}")
set(ASSETS_LOCAL_FILE "${assets_source}")
else()
set(ASSETS_LOCAL_FILE "${CMAKE_CURRENT_SOURCE_DIR}/${assets_source}")
endif()
# Check if local file exists
if(NOT EXISTS ${ASSETS_LOCAL_FILE})
message(FATAL_ERROR "Assets file not found: ${ASSETS_LOCAL_FILE}")
endif()
message(STATUS "Using assets file: ${ASSETS_LOCAL_FILE}")
endif()
set(${assets_local_file_var} ${ASSETS_LOCAL_FILE} PARENT_SCOPE)
endfunction()
partition_table_get_partition_info(size "--partition-name assets" "size")
partition_table_get_partition_info(offset "--partition-name assets" "offset")
if ("${size}" AND "${offset}")
# Flash assets based on configuration
if(CONFIG_FLASH_DEFAULT_ASSETS)
# Build default assets based on configuration
build_default_assets_bin()
esptool_py_flash_to_partition(flash "assets" "${GENERATED_ASSETS_LOCAL_FILE}")
message(STATUS "Generated default assets flash configured: ${GENERATED_ASSETS_LOCAL_FILE} -> assets partition")
elseif(CONFIG_FLASH_CUSTOM_ASSETS)
# Flash custom assets
get_assets_local_file("${CONFIG_CUSTOM_ASSETS_FILE}" ASSETS_LOCAL_FILE)
esptool_py_flash_to_partition(flash "assets" "${ASSETS_LOCAL_FILE}")
message(STATUS "Custom assets flash configured: ${ASSETS_LOCAL_FILE} -> assets partition")
elseif(CONFIG_FLASH_NONE_ASSETS)
message(STATUS "Assets flashing disabled (FLASH_NONE_ASSETS)")
endif()
else()
message(STATUS "Assets partition not found, using v1 partition table")
endif() endif()
+163 -92
View File
@@ -6,6 +6,27 @@ config OTA_URL
help help
The application will access this URL to check for new firmwares and server address. The application will access this URL to check for new firmwares and server address.
choice
prompt "Flash Assets"
default FLASH_DEFAULT_ASSETS
help
Select the assets to flash.
config FLASH_NONE_ASSETS
bool "Do not flash assets"
config FLASH_DEFAULT_ASSETS
bool "Flash Default Assets"
config FLASH_CUSTOM_ASSETS
bool "Flash Custom Assets"
endchoice
config CUSTOM_ASSETS_FILE
depends on FLASH_CUSTOM_ASSETS
string "Custom Assets File"
default "assets.bin"
help
The custom assets file to flash.
It can be a local file relative to the project directory or a remote url.
choice choice
prompt "Default Language" prompt "Default Language"
@@ -65,37 +86,37 @@ choice BOARD_TYPE
help help
Board type. 开发板类型 Board type. 开发板类型
config BOARD_TYPE_BREAD_COMPACT_WIFI config BOARD_TYPE_BREAD_COMPACT_WIFI
bool "面包板新版接线(WiFi" bool "Bread Compact WiFi (面包板)"
depends on IDF_TARGET_ESP32S3 depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_BREAD_COMPACT_WIFI_LCD config BOARD_TYPE_BREAD_COMPACT_WIFI_LCD
bool "面包板新版接线(WiFi+ LCD" bool "Bread Compact WiFi + LCD (面包板)"
depends on IDF_TARGET_ESP32S3 depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_BREAD_COMPACT_WIFI_CAM config BOARD_TYPE_BREAD_COMPACT_WIFI_CAM
bool "面包板新版接线(WiFi+ LCD + Camera" bool "Bread Compact WiFi + LCD + Camera (面包板)"
depends on IDF_TARGET_ESP32S3 depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_BREAD_COMPACT_ML307 config BOARD_TYPE_BREAD_COMPACT_ML307
bool "面包板新版接线(ML307 AT" bool "Bread Compact ML307/EC801E (面包板 4G)"
depends on IDF_TARGET_ESP32S3 depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_BREAD_COMPACT_ESP32 config BOARD_TYPE_BREAD_COMPACT_ESP32
bool "面包板(WiFi ESP32 DevKit" bool "Bread Compact ESP32 DevKit (面包板)"
depends on IDF_TARGET_ESP32 depends on IDF_TARGET_ESP32
config BOARD_TYPE_BREAD_COMPACT_ESP32_LCD config BOARD_TYPE_BREAD_COMPACT_ESP32_LCD
bool "面包板(WiFi+ LCD ESP32 DevKit" bool "Bread Compact ESP32 DevKit + LCD (面包板)"
depends on IDF_TARGET_ESP32 depends on IDF_TARGET_ESP32
config BOARD_TYPE_XMINI_C3_V3 config BOARD_TYPE_XMINI_C3_V3
bool "虾哥 Mini C3 V3" bool "Xmini C3 V3"
depends on IDF_TARGET_ESP32C3 depends on IDF_TARGET_ESP32C3
config BOARD_TYPE_XMINI_C3_4G config BOARD_TYPE_XMINI_C3_4G
bool "虾哥 Mini C3 4G" bool "Xmini C3 4G"
depends on IDF_TARGET_ESP32C3 depends on IDF_TARGET_ESP32C3
config BOARD_TYPE_XMINI_C3 config BOARD_TYPE_XMINI_C3
bool "虾哥 Mini C3" bool "Xmini C3"
depends on IDF_TARGET_ESP32C3 depends on IDF_TARGET_ESP32C3
config BOARD_TYPE_ESP32S3_KORVO2_V3 config BOARD_TYPE_ESP32S3_KORVO2_V3
bool "ESP32S3_KORVO2_V3开发板" bool "ESP32S3 KORVO2 V3"
depends on IDF_TARGET_ESP32S3 depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ESP_SPARKBOT config BOARD_TYPE_ESP_SPARKBOT
bool "ESP-SparkBot开发板" bool "ESP-SparkBot"
depends on IDF_TARGET_ESP32S3 depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ESP_SPOT_S3 config BOARD_TYPE_ESP_SPOT_S3
bool "ESP-Spot-S3" bool "ESP-Spot-S3"
@@ -125,10 +146,10 @@ choice BOARD_TYPE
bool "Kevin C3" bool "Kevin C3"
depends on IDF_TARGET_ESP32C3 depends on IDF_TARGET_ESP32C3
config BOARD_TYPE_KEVIN_SP_V3_DEV config BOARD_TYPE_KEVIN_SP_V3_DEV
bool "Kevin SP V3开发板" bool "Kevin SP V3"
depends on IDF_TARGET_ESP32S3 depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_KEVIN_SP_V4_DEV config BOARD_TYPE_KEVIN_SP_V4_DEV
bool "Kevin SP V4开发板" bool "Kevin SP V4"
depends on IDF_TARGET_ESP32S3 depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ESP32_CGC config BOARD_TYPE_ESP32_CGC
bool "ESP32 CGC" bool "ESP32 CGC"
@@ -137,13 +158,13 @@ choice BOARD_TYPE
bool "ESP32 CGC 144" bool "ESP32 CGC 144"
depends on IDF_TARGET_ESP32 depends on IDF_TARGET_ESP32
config BOARD_TYPE_KEVIN_YUYING_313LCD config BOARD_TYPE_KEVIN_YUYING_313LCD
bool "鱼鹰科技3.13LCD开发板" bool "鱼鹰科技 3.13LCD"
depends on IDF_TARGET_ESP32S3 depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_LICHUANG_DEV config BOARD_TYPE_LICHUANG_DEV
bool "立创·实战派ESP32-S3开发板" bool "立创·实战派 ESP32-S3"
depends on IDF_TARGET_ESP32S3 depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_LICHUANG_C3_DEV config BOARD_TYPE_LICHUANG_C3_DEV
bool "立创·实战派ESP32-C3开发板" bool "立创·实战派 ESP32-C3"
depends on IDF_TARGET_ESP32C3 depends on IDF_TARGET_ESP32C3
config BOARD_TYPE_DF_K10 config BOARD_TYPE_DF_K10
bool "DFRobot 行空板 k10" bool "DFRobot 行空板 k10"
@@ -194,6 +215,8 @@ choice BOARD_TYPE
bool "Waveshare ESP32-S3-Touch-AMOLED-2.06" bool "Waveshare ESP32-S3-Touch-AMOLED-2.06"
config BOARD_TYPE_ESP32S3_Touch_AMOLED_1_75 config BOARD_TYPE_ESP32S3_Touch_AMOLED_1_75
bool "Waveshare ESP32-S3-Touch-AMOLED-1.75" bool "Waveshare ESP32-S3-Touch-AMOLED-1.75"
config BOARD_TYPE_ESP32S3_Touch_LCD_4B
bool "Waveshare ESP32-S3-Touch-LCD-4B"
depends on IDF_TARGET_ESP32S3 depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ESP32S3_Touch_LCD_1_85C config BOARD_TYPE_ESP32S3_Touch_LCD_1_85C
bool "Waveshare ESP32-S3-Touch-LCD-1.85C" bool "Waveshare ESP32-S3-Touch-LCD-1.85C"
@@ -210,6 +233,9 @@ choice BOARD_TYPE
config BOARD_TYPE_ESP32C6_Touch_AMOLED_1_43 config BOARD_TYPE_ESP32C6_Touch_AMOLED_1_43
bool "Waveshare ESP32-C6-Touch-AMOLOED-1.43" bool "Waveshare ESP32-C6-Touch-AMOLOED-1.43"
depends on IDF_TARGET_ESP32C6 depends on IDF_TARGET_ESP32C6
config BOARD_TYPE_ESP32S3_Touch_LCD_3_49
bool "Waveshare ESP32-S3-Touch-LCD-3.49"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ESP32S3_Touch_LCD_3_5 config BOARD_TYPE_ESP32S3_Touch_LCD_3_5
bool "Waveshare ESP32-S3-Touch-LCD-3.5" bool "Waveshare ESP32-S3-Touch-LCD-3.5"
depends on IDF_TARGET_ESP32S3 depends on IDF_TARGET_ESP32S3
@@ -347,16 +373,20 @@ choice BOARD_TYPE
depends on IDF_TARGET_ESP32S3 depends on IDF_TARGET_ESP32S3
depends on IDF_TARGET_ESP32S3 depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_SURFER_C3_1_14TFT config BOARD_TYPE_SURFER_C3_1_14TFT
bool "Surfer-C3-1-14TFT" bool "Surfer-C3-1.14TFT"
depends on IDF_TARGET_ESP32C3 depends on IDF_TARGET_ESP32C3
config BOARD_TYPE_YUNLIAO_S3
bool "小智云聊-S3"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_JINAO_S3
bool "极脑ESP32-S3开发板"
depends on IDF_TARGET_ESP32S3
endchoice endchoice
choice ESP_S3_LCD_EV_Board_Version_TYPE choice ESP_S3_LCD_EV_Board_Version_TYPE
depends on BOARD_TYPE_ESP_S3_LCD_EV_Board depends on BOARD_TYPE_ESP_S3_LCD_EV_Board
prompt "EV_BOARD Type" prompt "EV_BOARD Type"
default ESP_S3_LCD_EV_Board_1p4 default ESP_S3_LCD_EV_Board_1p4
help
开发板硬件版本型号选择
config ESP_S3_LCD_EV_Board_1p4 config ESP_S3_LCD_EV_Board_1p4
bool "乐鑫ESP32_S3_LCD_EV_Board-MB_V1.4" bool "乐鑫ESP32_S3_LCD_EV_Board-MB_V1.4"
config ESP_S3_LCD_EV_Board_1p5 config ESP_S3_LCD_EV_Board_1p5
@@ -368,13 +398,13 @@ choice DISPLAY_OLED_TYPE
prompt "OLED Type" prompt "OLED Type"
default OLED_SSD1306_128X32 default OLED_SSD1306_128X32
help help
OLED 屏幕类型选择 OLED Monochrome Display Type
config OLED_SSD1306_128X32 config OLED_SSD1306_128X32
bool "SSD1306, 分辨率128*32" bool "SSD1306 128*32"
config OLED_SSD1306_128X64 config OLED_SSD1306_128X64
bool "SSD1306, 分辨率128*64" bool "SSD1306 128*64"
config OLED_SH1106_128X64 config OLED_SH1106_128X64
bool "SH1106, 分辨率128*64" bool "SH1106 128*64"
endchoice endchoice
choice DISPLAY_LCD_TYPE choice DISPLAY_LCD_TYPE
@@ -382,37 +412,37 @@ choice DISPLAY_LCD_TYPE
prompt "LCD Type" prompt "LCD Type"
default LCD_ST7789_240X320 default LCD_ST7789_240X320
help help
屏幕类型选择 LCD Display Type
config LCD_ST7789_240X320 config LCD_ST7789_240X320
bool "ST7789, 分辨率240*320, IPS" bool "ST7789 240*320, IPS"
config LCD_ST7789_240X320_NO_IPS config LCD_ST7789_240X320_NO_IPS
bool "ST7789, 分辨率240*320, IPS" bool "ST7789 240*320, Non-IPS"
config LCD_ST7789_170X320 config LCD_ST7789_170X320
bool "ST7789, 分辨率170*320" bool "ST7789 170*320"
config LCD_ST7789_172X320 config LCD_ST7789_172X320
bool "ST7789, 分辨率172*320" bool "ST7789 172*320"
config LCD_ST7789_240X280 config LCD_ST7789_240X280
bool "ST7789, 分辨率240*280" bool "ST7789 240*280"
config LCD_ST7789_240X240 config LCD_ST7789_240X240
bool "ST7789, 分辨率240*240" bool "ST7789 240*240"
config LCD_ST7789_240X240_7PIN config LCD_ST7789_240X240_7PIN
bool "ST7789, 分辨率240*240, 7PIN" bool "ST7789 240*240, 7PIN"
config LCD_ST7789_240X135 config LCD_ST7789_240X135
bool "ST7789, 分辨率240*135" bool "ST7789 240*135"
config LCD_ST7735_128X160 config LCD_ST7735_128X160
bool "ST7735, 分辨率128*160" bool "ST7735 128*160"
config LCD_ST7735_128X128 config LCD_ST7735_128X128
bool "ST7735, 分辨率128*128" bool "ST7735 128*128"
config LCD_ST7796_320X480 config LCD_ST7796_320X480
bool "ST7796, 分辨率320*480 IPS" bool "ST7796 320*480 IPS"
config LCD_ST7796_320X480_NO_IPS config LCD_ST7796_320X480_NO_IPS
bool "ST7796, 分辨率320*480, IPS" bool "ST7796 320*480, Non-IPS"
config LCD_ILI9341_240X320 config LCD_ILI9341_240X320
bool "ILI9341, 分辨率240*320" bool "ILI9341 240*320"
config LCD_ILI9341_240X320_NO_IPS config LCD_ILI9341_240X320_NO_IPS
bool "ILI9341, 分辨率240*320, IPS" bool "ILI9341 240*320, Non-IPS"
config LCD_GC9A01_240X240 config LCD_GC9A01_240X240
bool "GC9A01, 分辨率240*240, 圆屏" bool "GC9A01 240*240 Circle"
config LCD_TYPE_800_1280_10_1_INCH config LCD_TYPE_800_1280_10_1_INCH
bool "Waveshare 101M-8001280-IPS-CT-K Display" bool "Waveshare 101M-8001280-IPS-CT-K Display"
config LCD_TYPE_800_1280_10_1_INCH_A config LCD_TYPE_800_1280_10_1_INCH_A
@@ -422,7 +452,7 @@ choice DISPLAY_LCD_TYPE
config LCD_TYPE_720_720_4_INCH config LCD_TYPE_720_720_4_INCH
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-4C with 720*720 4inch round display" bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-4C with 720*720 4inch round display"
config LCD_CUSTOM config LCD_CUSTOM
bool "自定义屏幕参数" bool "Custom LCD (自定义屏幕参数)"
endchoice endchoice
choice DISPLAY_ESP32S3_KORVO2_V3 choice DISPLAY_ESP32S3_KORVO2_V3
@@ -430,11 +460,11 @@ choice DISPLAY_ESP32S3_KORVO2_V3
prompt "ESP32S3_KORVO2_V3 LCD Type" prompt "ESP32S3_KORVO2_V3 LCD Type"
default ESP32S3_KORVO2_V3_LCD_ST7789 default ESP32S3_KORVO2_V3_LCD_ST7789
help help
屏幕类型选择 LCD Display Type
config ESP32S3_KORVO2_V3_LCD_ST7789 config ESP32S3_KORVO2_V3_LCD_ST7789
bool "ST7789, 分辨率240*280" bool "ST7789 240*280"
config ESP32S3_KORVO2_V3_LCD_ILI9341 config ESP32S3_KORVO2_V3_LCD_ILI9341
bool "ILI9341, 分辨率240*320" bool "ILI9341 240*320"
endchoice endchoice
choice DISPLAY_ESP32S3_AUDIO_BOARD choice DISPLAY_ESP32S3_AUDIO_BOARD
@@ -442,53 +472,75 @@ choice DISPLAY_ESP32S3_AUDIO_BOARD
prompt "ESP32S3_AUDIO_BOARD LCD Type" prompt "ESP32S3_AUDIO_BOARD LCD Type"
default AUDIO_BOARD_LCD_JD9853 default AUDIO_BOARD_LCD_JD9853
help help
屏幕类型选择 LCD Display Type
config AUDIO_BOARD_LCD_JD9853 config AUDIO_BOARD_LCD_JD9853
bool "JD9853, 分辨率320*172" bool "JD9853 320*172"
config AUDIO_BOARD_LCD_ST7789 config AUDIO_BOARD_LCD_ST7789
bool "ST7789, 分辨率240*320" bool "ST7789 240*320"
endchoice endchoice
config USE_WECHAT_MESSAGE_STYLE choice DISPLAY_STYLE
bool "Enable WeChat Message Style" prompt "Select display style"
default n default USE_DEFAULT_MESSAGE_STYLE
help help
使用微信聊天界面风格 Select display style for Xiaozhi device
config USE_ESP_WAKE_WORD config USE_DEFAULT_MESSAGE_STYLE
bool "Enable Wake Word Detection (without AFE)" bool "Enable default message style"
default n
depends on IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C5 || IDF_TARGET_ESP32C6 || (IDF_TARGET_ESP32 && SPIRAM)
help
支持 ESP32 C3、ESP32 C5 与 ESP32 C6,增加ESP32支持(需要开启PSRAM)
config USE_AFE_WAKE_WORD config USE_WECHAT_MESSAGE_STYLE
bool "Enable Wake Word Detection (AFE)" bool "Enable WeChat Message Style"
default y
depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM
help
需要 ESP32 S3 与 PSRAM 支持
config USE_CUSTOM_WAKE_WORD config USE_EMOTE_MESSAGE_STYLE
bool "Enable Custom Wake Word Detection" bool "Emote animation style"
default n depends on BOARD_TYPE_ESP_BOX_3 || BOARD_TYPE_ECHOEAR
depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM && (!USE_AFE_WAKE_WORD) endchoice
choice WAKE_WORD_TYPE
prompt "Wake Word Implementation Type"
default USE_AFE_WAKE_WORD if (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM
default WAKE_WORD_DISABLED
help help
需要 ESP32 S3 与 PSRAM 支持 Choose the type of wake word implementation to use
config WAKE_WORD_DISABLED
bool "Disabled"
help
Disable wake word detection
config USE_ESP_WAKE_WORD
bool "Wakenet model without AFE"
depends on IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C5 || IDF_TARGET_ESP32C6 || (IDF_TARGET_ESP32 && SPIRAM)
help
Support ESP32 C3、ESP32 C5 与 ESP32 C6, and (ESP32 with PSRAM)
config USE_AFE_WAKE_WORD
bool "Wakenet model with AFE"
depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM
help
Support AEC if available, requires ESP32 S3 and PSRAM
config USE_CUSTOM_WAKE_WORD
bool "Multinet model (Custom Wake Word)"
depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM
help
Requires ESP32 S3 and PSRAM
endchoice
config CUSTOM_WAKE_WORD config CUSTOM_WAKE_WORD
string "Custom Wake Word" string "Custom Wake Word"
default "xiao tu dou" default "xiao tu dou"
depends on USE_CUSTOM_WAKE_WORD depends on USE_CUSTOM_WAKE_WORD
help help
自定义唤醒词,中文用拼音表示,每个字之间用空格隔开 Custom Wake Word, use pinyin for Chinese, separated by spaces
config CUSTOM_WAKE_WORD_DISPLAY config CUSTOM_WAKE_WORD_DISPLAY
string "Custom Wake Word Display" string "Custom Wake Word Display"
default "小土豆" default "小土豆"
depends on USE_CUSTOM_WAKE_WORD depends on USE_CUSTOM_WAKE_WORD
help help
唤醒后发送给服务器的问候语 Greeting sent to the server after wake word detection
config CUSTOM_WAKE_WORD_THRESHOLD config CUSTOM_WAKE_WORD_THRESHOLD
int "Custom Wake Word Threshold (%)" int "Custom Wake Word Threshold (%)"
@@ -496,64 +548,83 @@ config CUSTOM_WAKE_WORD_THRESHOLD
range 1 99 range 1 99
depends on USE_CUSTOM_WAKE_WORD depends on USE_CUSTOM_WAKE_WORD
help help
自定义唤醒词阈值,范围1-99,越小越敏感,默认10 Custom Wake Word Threshold, range 1-99, the smaller the more sensitive, default 20
config SEND_WAKE_WORD_DATA
bool "Send Wake Word Data"
default y
depends on USE_AFE_WAKE_WORD || USE_CUSTOM_WAKE_WORD
help
Send wake word data to the server as the first message of the conversation and wait for response
config USE_AUDIO_PROCESSOR config USE_AUDIO_PROCESSOR
bool "Enable Audio Noise Reduction" bool "Enable Audio Noise Reduction"
default y default y
depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM
help help
需要 ESP32 S3 PSRAM 支持 Requires ESP32 S3 and PSRAM
config USE_DEVICE_AEC config USE_DEVICE_AEC
bool "Enable Device-Side AEC" bool "Enable Device-Side AEC"
default n default n
depends on USE_AUDIO_PROCESSOR && (BOARD_TYPE_ESP_BOX_3 || BOARD_TYPE_ESP_BOX || BOARD_TYPE_ESP_BOX_LITE || BOARD_TYPE_LICHUANG_DEV || BOARD_TYPE_ESP32S3_KORVO2_V3 || BOARD_TYPE_ESP32S3_Touch_AMOLED_1_75 || BOARD_TYPE_ESP32S3_Touch_AMOLED_2_06 || BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_4B || BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_XC || BOARD_TYPE_ESP_S3_LCD_EV_Board_2) depends on USE_AUDIO_PROCESSOR && (BOARD_TYPE_ESP_BOX_3 || BOARD_TYPE_ESP_BOX || BOARD_TYPE_ESP_BOX_LITE \
|| BOARD_TYPE_LICHUANG_DEV || BOARD_TYPE_ESP32S3_KORVO2_V3 || BOARD_TYPE_ESP32S3_Touch_AMOLED_1_75 \
|| BOARD_TYPE_ESP32S3_Touch_AMOLED_2_06 || BOARD_TYPE_ESP32S3_Touch_LCD_4B || BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_4B \
|| BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_XC || BOARD_TYPE_ESP_S3_LCD_EV_Board_2 || BOARD_TYPE_YUNLIAO_S3 \
|| BOARD_TYPE_ECHOEAR || BOARD_TYPE_ESP32S3_Touch_LCD_3_49)
help help
因为性能不够,不建议和微信聊天界面风格同时开启 To work properly, device-side AEC requires a clean output reference path from the speaker signal and physical acoustic isolation between the microphone and speaker.
config USE_SERVER_AEC config USE_SERVER_AEC
bool "Enable Server-Side AEC (Unstable)" bool "Enable Server-Side AEC (Unstable)"
default n default n
depends on USE_AUDIO_PROCESSOR depends on USE_AUDIO_PROCESSOR
help help
启用服务器端 AEC,需要服务器支持 To work perperly, server-side AEC requires server support
config USE_AUDIO_DEBUGGER config USE_AUDIO_DEBUGGER
bool "Enable Audio Debugger" bool "Enable Audio Debugger"
default n default n
help help
启用音频调试功能,通过UDP发送音频数据 Enable audio debugger, send audio data through UDP to the host machine
config USE_ACOUSTIC_WIFI_PROVISIONING
bool "Enable Acoustic WiFi Provisioning"
default n
help
启用声波配网功能,使用音频信号传输 WiFi 配置数据
config AUDIO_DEBUG_UDP_SERVER config AUDIO_DEBUG_UDP_SERVER
string "Audio Debug UDP Server Address" string "Audio Debug UDP Server Address"
default "192.168.2.100:8000" default "192.168.2.100:8000"
depends on USE_AUDIO_DEBUGGER depends on USE_AUDIO_DEBUGGER
help help
UDP服务器地址,格式: IP:PORT,用于接收音频调试数据 UDP server address, format: IP:PORT, used to receive audio debugging data
config USE_ACOUSTIC_WIFI_PROVISIONING
bool "Enable Acoustic WiFi Provisioning"
default n
help
Enable acoustic WiFi provisioning, use audio signal to transmit WiFi configuration data
config RECEIVE_CUSTOM_MESSAGE config RECEIVE_CUSTOM_MESSAGE
bool "Enable Custom Message Reception" bool "Enable Custom Message Reception"
default n default n
help help
启用接收自定义消息功能,允许设备接收来自服务器的自定义消息(最好通过 MQTT 协议) Enable custom message reception, allow the device to receive custom messages from the server (preferably through the MQTT protocol)
choice I2S_TYPE_TAIJIPI_S3 menu TAIJIPAI_S3_CONFIG
depends on BOARD_TYPE_ESP32S3_Taiji_Pi depends on BOARD_TYPE_ESP32S3_Taiji_Pi
prompt "taiji-pi-S3 I2S Type" choice I2S_TYPE_TAIJIPI_S3
default TAIJIPAI_I2S_TYPE_STD prompt "taiji-pi-S3 I2S Type"
default TAIJIPAI_I2S_TYPE_STD
help
I2S 类型选择
config TAIJIPAI_I2S_TYPE_STD
bool "I2S Type STD"
config TAIJIPAI_I2S_TYPE_PDM
bool "I2S Type PDM"
endchoice
config I2S_USE_2SLOT
bool "Enable I2S 2 Slot"
default n
help help
I2S 类型选择 启动双声道
config TAIJIPAI_I2S_TYPE_STD endmenu
bool "I2S Type STD"
config TAIJIPAI_I2S_TYPE_PDM
bool "I2S Type PDM"
endchoice
endmenu endmenu
+671
View File
@@ -0,0 +1,671 @@
#include "alarm_manager.h"
#include <esp_log.h>
#include <esp_timer.h>
#include <cJSON.h>
#include <time.h>
#include <sys/time.h>
#include <sstream>
#include <iomanip>
#include <algorithm>
#define TAG "AlarmManager"
#define ALARM_SETTINGS_NAMESPACE "alarms"
AlarmManager::AlarmManager()
: initialized_(false), next_alarm_id_(1),
default_snooze_minutes_(5), default_max_snooze_count_(3) {
}
AlarmManager::~AlarmManager() {
Cleanup();
}
void AlarmManager::Initialize() {
if (initialized_) {
return;
}
ESP_LOGI(TAG, "Initializing Alarm Manager");
// 初始化设置存储
settings_ = std::make_unique<Settings>(ALARM_SETTINGS_NAMESPACE, true);
// 从存储中加载闹钟
LoadAlarmsFromStorage();
// 获取下一个闹钟ID
next_alarm_id_ = settings_->GetInt("next_id", 1);
initialized_ = true;
ESP_LOGI(TAG, "Alarm Manager initialized with %d alarms", alarms_.size());
}
void AlarmManager::Cleanup() {
if (!initialized_) {
return;
}
ESP_LOGI(TAG, "Cleaning up Alarm Manager");
// 停止所有活动的闹钟
StopAllActiveAlarms();
// 保存数据
SaveAlarmsToStorage();
// 清理资源
{
std::lock_guard<std::mutex> lock(alarms_mutex_);
alarms_.clear();
}
settings_.reset();
initialized_ = false;
}
int AlarmManager::AddAlarm(int hour, int minute, AlarmRepeatMode repeat_mode,
const std::string& label, const std::string& music_name) {
if (!initialized_) {
ESP_LOGE(TAG, "AlarmManager not initialized");
return -1;
}
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
ESP_LOGE(TAG, "Invalid time: %02d:%02d", hour, minute);
return -1;
}
std::lock_guard<std::mutex> lock(alarms_mutex_);
auto alarm = std::make_unique<AlarmItem>();
alarm->id = GetNextAlarmId();
alarm->hour = hour;
alarm->minute = minute;
alarm->repeat_mode = repeat_mode;
alarm->label = label;
alarm->music_name = music_name;
alarm->status = kAlarmEnabled;
alarm->snooze_minutes = default_snooze_minutes_;
alarm->max_snooze_count = default_max_snooze_count_;
// 设置星期掩码
switch (repeat_mode) {
case kAlarmDaily:
alarm->weekdays_mask = 0b1111111; // 每天
break;
case kAlarmWeekdays:
alarm->weekdays_mask = 0b0111110; // 周一到周五
break;
case kAlarmWeekends:
alarm->weekdays_mask = 0b1000001; // 周六周日
break;
default:
alarm->weekdays_mask = 0; // 一次性或自定义
break;
}
int alarm_id = alarm->id;
alarms_.push_back(std::move(alarm));
// 保存到存储
SaveAlarmToStorage(*alarms_.back());
settings_->SetInt("next_id", next_alarm_id_);
ESP_LOGI(TAG, "Added alarm %d: %02d:%02d, repeat=%d, label='%s', music='%s'",
alarm_id, hour, minute, repeat_mode, label.c_str(), music_name.c_str());
return alarm_id;
}
bool AlarmManager::RemoveAlarm(int alarm_id) {
if (!initialized_) {
return false;
}
std::lock_guard<std::mutex> lock(alarms_mutex_);
auto it = std::find_if(alarms_.begin(), alarms_.end(),
[alarm_id](const auto& alarm) { return alarm->id == alarm_id; });
if (it != alarms_.end()) {
ESP_LOGI(TAG, "Removing alarm %d", alarm_id);
alarms_.erase(it);
RemoveAlarmFromStorage(alarm_id);
return true;
}
ESP_LOGW(TAG, "Alarm %d not found for removal", alarm_id);
return false;
}
bool AlarmManager::EnableAlarm(int alarm_id, bool enabled) {
if (!initialized_) {
return false;
}
std::lock_guard<std::mutex> lock(alarms_mutex_);
auto it = std::find_if(alarms_.begin(), alarms_.end(),
[alarm_id](const auto& alarm) { return alarm->id == alarm_id; });
if (it != alarms_.end()) {
(*it)->status = enabled ? kAlarmEnabled : kAlarmDisabled;
SaveAlarmToStorage(**it);
ESP_LOGI(TAG, "Alarm %d %s", alarm_id, enabled ? "enabled" : "disabled");
return true;
}
return false;
}
bool AlarmManager::ModifyAlarm(int alarm_id, int hour, int minute, AlarmRepeatMode repeat_mode,
const std::string& label, const std::string& music_name) {
if (!initialized_) {
return false;
}
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
ESP_LOGE(TAG, "Invalid time: %02d:%02d", hour, minute);
return false;
}
std::lock_guard<std::mutex> lock(alarms_mutex_);
auto it = std::find_if(alarms_.begin(), alarms_.end(),
[alarm_id](const auto& alarm) { return alarm->id == alarm_id; });
if (it != alarms_.end()) {
(*it)->hour = hour;
(*it)->minute = minute;
(*it)->repeat_mode = repeat_mode;
(*it)->label = label;
(*it)->music_name = music_name;
// 重新设置星期掩码
switch (repeat_mode) {
case kAlarmDaily:
(*it)->weekdays_mask = 0b1111111;
break;
case kAlarmWeekdays:
(*it)->weekdays_mask = 0b0111110;
break;
case kAlarmWeekends:
(*it)->weekdays_mask = 0b1000001;
break;
default:
break;
}
SaveAlarmToStorage(**it);
ESP_LOGI(TAG, "Modified alarm %d: %02d:%02d", alarm_id, hour, minute);
return true;
}
return false;
}
std::vector<AlarmItem> AlarmManager::GetAllAlarms() const {
if (!initialized_) {
return {};
}
std::lock_guard<std::mutex> lock(alarms_mutex_);
std::vector<AlarmItem> result;
for (const auto& alarm : alarms_) {
result.push_back(*alarm);
}
return result;
}
AlarmItem* AlarmManager::GetAlarm(int alarm_id) {
if (!initialized_) {
return nullptr;
}
std::lock_guard<std::mutex> lock(alarms_mutex_);
auto it = std::find_if(alarms_.begin(), alarms_.end(),
[alarm_id](const auto& alarm) { return alarm->id == alarm_id; });
return (it != alarms_.end()) ? it->get() : nullptr;
}
std::vector<AlarmItem> AlarmManager::GetActiveAlarms() const {
if (!initialized_) {
return {};
}
std::lock_guard<std::mutex> lock(alarms_mutex_);
std::vector<AlarmItem> result;
for (const auto& alarm : alarms_) {
if (alarm->status == kAlarmTriggered || alarm->status == kAlarmSnoozed) {
result.push_back(*alarm);
}
}
return result;
}
std::string AlarmManager::GetNextAlarmInfo() const {
if (!initialized_) {
return "闹钟管理器未初始化";
}
std::lock_guard<std::mutex> lock(alarms_mutex_);
// 查找下一个要触发的闹钟
AlarmItem* next_alarm = nullptr;
int64_t current_time = GetCurrentTimeInMinutes();
int current_weekday = GetCurrentWeekday();
int64_t min_time_diff = 24 * 60 * 7; // 一周的分钟数
for (const auto& alarm : alarms_) {
if (alarm->status != kAlarmEnabled) {
continue;
}
// 计算今天和明天的时间差
int64_t alarm_time = alarm->hour * 60 + alarm->minute;
for (int day_offset = 0; day_offset < 7; day_offset++) {
int check_weekday = (current_weekday + day_offset) % 7;
if (day_offset == 0 && alarm_time <= current_time) {
continue; // 今天已经过了
}
if (alarm->repeat_mode == kAlarmOnce && day_offset > 0) {
continue; // 一次性闹钟只检查今天
}
if (IsWeekdayActive(*alarm, check_weekday)) {
int64_t time_diff = day_offset * 24 * 60 + alarm_time - current_time;
if (day_offset == 0) {
time_diff = alarm_time - current_time;
}
if (time_diff < min_time_diff) {
min_time_diff = time_diff;
next_alarm = alarm.get();
}
break;
}
}
}
if (!next_alarm) {
return "无活动闹钟";
}
// 格式化下一个闹钟信息
std::ostringstream oss;
oss << "下个闹钟: " << FormatTime(next_alarm->hour, next_alarm->minute);
if (min_time_diff < 24 * 60) {
int hours = min_time_diff / 60;
int minutes = min_time_diff % 60;
if (hours > 0) {
oss << " (" << hours << "小时" << minutes << "分钟后)";
} else {
oss << " (" << minutes << "分钟后)";
}
} else {
int days = min_time_diff / (24 * 60);
oss << " (" << days << "天后)";
}
if (!next_alarm->label.empty()) {
oss << " - " << next_alarm->label;
}
return oss.str();
}
bool AlarmManager::SnoozeAlarm(int alarm_id) {
if (!initialized_) {
return false;
}
std::lock_guard<std::mutex> lock(alarms_mutex_);
auto it = std::find_if(alarms_.begin(), alarms_.end(),
[alarm_id](const auto& alarm) { return alarm->id == alarm_id; });
if (it != alarms_.end() && (*it)->status == kAlarmTriggered) {
if ((*it)->snooze_count < (*it)->max_snooze_count) {
(*it)->status = kAlarmSnoozed;
(*it)->snooze_count++;
(*it)->next_snooze_time = esp_timer_get_time() / 1000000 + (*it)->snooze_minutes * 60;
ESP_LOGI(TAG, "Snoozed alarm %d for %d minutes (count: %d/%d)",
alarm_id, (*it)->snooze_minutes, (*it)->snooze_count, (*it)->max_snooze_count);
if (on_alarm_snoozed_) {
on_alarm_snoozed_(**it);
}
return true;
} else {
ESP_LOGI(TAG, "Alarm %d exceeded max snooze count, stopping", alarm_id);
StopAlarm(alarm_id);
}
}
return false;
}
bool AlarmManager::StopAlarm(int alarm_id) {
if (!initialized_) {
return false;
}
std::lock_guard<std::mutex> lock(alarms_mutex_);
auto it = std::find_if(alarms_.begin(), alarms_.end(),
[alarm_id](const auto& alarm) { return alarm->id == alarm_id; });
if (it != alarms_.end() &&
((*it)->status == kAlarmTriggered || (*it)->status == kAlarmSnoozed)) {
AlarmStatus old_status = (*it)->status;
(*it)->status = ((*it)->repeat_mode == kAlarmOnce) ? kAlarmDisabled : kAlarmEnabled;
(*it)->snooze_count = 0;
(*it)->next_snooze_time = 0;
ESP_LOGI(TAG, "Stopped alarm %d", alarm_id);
if (on_alarm_stopped_) {
on_alarm_stopped_(**it);
}
return true;
}
return false;
}
void AlarmManager::StopAllActiveAlarms() {
if (!initialized_) {
return;
}
std::lock_guard<std::mutex> lock(alarms_mutex_);
for (auto& alarm : alarms_) {
if (alarm->status == kAlarmTriggered || alarm->status == kAlarmSnoozed) {
alarm->status = (alarm->repeat_mode == kAlarmOnce) ? kAlarmDisabled : kAlarmEnabled;
alarm->snooze_count = 0;
alarm->next_snooze_time = 0;
if (on_alarm_stopped_) {
on_alarm_stopped_(*alarm);
}
}
}
ESP_LOGI(TAG, "Stopped all active alarms");
}
void AlarmManager::CheckAlarms() {
if (!initialized_) {
return;
}
int64_t current_time_seconds = esp_timer_get_time() / 1000000;
int64_t current_time_minutes = GetCurrentTimeInMinutes();
std::lock_guard<std::mutex> lock(alarms_mutex_);
for (auto& alarm : alarms_) {
// 检查贪睡闹钟
if (alarm->status == kAlarmSnoozed &&
current_time_seconds >= alarm->next_snooze_time) {
alarm->status = kAlarmTriggered;
alarm->next_snooze_time = 0;
ESP_LOGI(TAG, "Snooze ended for alarm %d, triggering again", alarm->id);
if (on_alarm_triggered_) {
on_alarm_triggered_(*alarm);
}
continue;
}
// 检查正常闹钟触发
if (alarm->status != kAlarmEnabled) {
continue;
}
int64_t alarm_time_minutes = alarm->hour * 60 + alarm->minute;
// 检查是否是触发时间(精确到分钟)
if (alarm_time_minutes != current_time_minutes) {
continue;
}
// 检查是否应该在今天触发
if (!ShouldTriggerToday(*alarm)) {
continue;
}
// 防止重复触发(同一分钟内)
int64_t current_time_in_seconds = esp_timer_get_time() / 1000000;
if (alarm->last_triggered_time > 0 &&
(current_time_in_seconds - alarm->last_triggered_time) < 60) {
continue;
}
// 触发闹钟
alarm->status = kAlarmTriggered;
alarm->last_triggered_time = current_time_in_seconds;
alarm->snooze_count = 0;
ESP_LOGI(TAG, "Triggering alarm %d: %02d:%02d - %s",
alarm->id, alarm->hour, alarm->minute, alarm->label.c_str());
if (on_alarm_triggered_) {
on_alarm_triggered_(*alarm);
}
}
}
void AlarmManager::SetAlarmTriggeredCallback(AlarmTriggeredCallback callback) {
on_alarm_triggered_ = callback;
}
void AlarmManager::SetAlarmSnoozeCallback(AlarmSnoozeCallback callback) {
on_alarm_snoozed_ = callback;
}
void AlarmManager::SetAlarmStopCallback(AlarmStopCallback callback) {
on_alarm_stopped_ = callback;
}
void AlarmManager::SetDefaultSnoozeMinutes(int minutes) {
default_snooze_minutes_ = std::max(1, std::min(60, minutes));
}
void AlarmManager::SetDefaultMaxSnoozeCount(int count) {
default_max_snooze_count_ = std::max(0, std::min(10, count));
}
// 静态工具方法
std::string AlarmManager::FormatTime(int hour, int minute) {
std::ostringstream oss;
oss << std::setfill('0') << std::setw(2) << hour
<< ":" << std::setfill('0') << std::setw(2) << minute;
return oss.str();
}
std::string AlarmManager::FormatAlarmTime(const AlarmItem& alarm) {
std::ostringstream oss;
oss << FormatTime(alarm.hour, alarm.minute);
switch (alarm.repeat_mode) {
case kAlarmOnce:
oss << " (一次)";
break;
case kAlarmDaily:
oss << " (每日)";
break;
case kAlarmWeekdays:
oss << " (工作日)";
break;
case kAlarmWeekends:
oss << " (周末)";
break;
case kAlarmCustom:
oss << " (自定义)";
break;
}
return oss.str();
}
bool AlarmManager::IsWeekdayActive(const AlarmItem& alarm, int weekday) {
if (alarm.repeat_mode == kAlarmOnce) {
return true; // 一次性闹钟在任何一天都可以触发
}
return (alarm.weekdays_mask & (1 << weekday)) != 0;
}
// 私有方法实现
void AlarmManager::LoadAlarmsFromStorage() {
std::lock_guard<std::mutex> lock(alarms_mutex_);
alarms_.clear();
// 读取闹钟数量
int alarm_count = settings_->GetInt("count", 0);
ESP_LOGI(TAG, "Loading %d alarms from storage", alarm_count);
for (int i = 0; i < alarm_count; i++) {
std::string alarm_key = "alarm_" + std::to_string(i);
std::string alarm_json = settings_->GetString(alarm_key);
if (alarm_json.empty()) {
continue;
}
cJSON* json = cJSON_Parse(alarm_json.c_str());
if (!json) {
ESP_LOGW(TAG, "Failed to parse alarm JSON: %s", alarm_key.c_str());
continue;
}
auto alarm = std::make_unique<AlarmItem>();
// 解析JSON数据
if (auto id = cJSON_GetObjectItem(json, "id")) alarm->id = id->valueint;
if (auto hour = cJSON_GetObjectItem(json, "hour")) alarm->hour = hour->valueint;
if (auto minute = cJSON_GetObjectItem(json, "minute")) alarm->minute = minute->valueint;
if (auto repeat = cJSON_GetObjectItem(json, "repeat")) alarm->repeat_mode = (AlarmRepeatMode)repeat->valueint;
if (auto mask = cJSON_GetObjectItem(json, "weekdays")) alarm->weekdays_mask = mask->valueint;
if (auto status = cJSON_GetObjectItem(json, "status")) alarm->status = (AlarmStatus)status->valueint;
if (auto label = cJSON_GetObjectItem(json, "label")) alarm->label = label->valuestring;
if (auto music = cJSON_GetObjectItem(json, "music")) alarm->music_name = music->valuestring;
if (auto snooze_min = cJSON_GetObjectItem(json, "snooze_minutes")) alarm->snooze_minutes = snooze_min->valueint;
if (auto max_snooze = cJSON_GetObjectItem(json, "max_snooze")) alarm->max_snooze_count = max_snooze->valueint;
// 重置运行时状态
alarm->snooze_count = 0;
alarm->last_triggered_time = 0;
alarm->next_snooze_time = 0;
if (alarm->status == kAlarmTriggered || alarm->status == kAlarmSnoozed) {
alarm->status = kAlarmEnabled;
}
alarms_.push_back(std::move(alarm));
cJSON_Delete(json);
}
ESP_LOGI(TAG, "Loaded %d alarms successfully", alarms_.size());
}
void AlarmManager::SaveAlarmsToStorage() {
if (!settings_) {
return;
}
std::lock_guard<std::mutex> lock(alarms_mutex_);
// 保存闹钟数量
settings_->SetInt("count", alarms_.size());
// 保存每个闹钟
for (size_t i = 0; i < alarms_.size(); i++) {
std::string alarm_key = "alarm_" + std::to_string(i);
SaveAlarmToStorage(*alarms_[i]);
}
ESP_LOGI(TAG, "Saved %d alarms to storage", alarms_.size());
}
void AlarmManager::SaveAlarmToStorage(const AlarmItem& alarm) {
if (!settings_) {
return;
}
cJSON* json = cJSON_CreateObject();
cJSON_AddNumberToObject(json, "id", alarm.id);
cJSON_AddNumberToObject(json, "hour", alarm.hour);
cJSON_AddNumberToObject(json, "minute", alarm.minute);
cJSON_AddNumberToObject(json, "repeat", alarm.repeat_mode);
cJSON_AddNumberToObject(json, "weekdays", alarm.weekdays_mask);
cJSON_AddNumberToObject(json, "status", alarm.status);
cJSON_AddStringToObject(json, "label", alarm.label.c_str());
cJSON_AddStringToObject(json, "music", alarm.music_name.c_str());
cJSON_AddNumberToObject(json, "snooze_minutes", alarm.snooze_minutes);
cJSON_AddNumberToObject(json, "max_snooze", alarm.max_snooze_count);
char* json_string = cJSON_PrintUnformatted(json);
// 找到这个闹钟在数组中的位置
auto it = std::find_if(alarms_.begin(), alarms_.end(),
[&alarm](const auto& a) { return a->id == alarm.id; });
if (it != alarms_.end()) {
size_t index = std::distance(alarms_.begin(), it);
std::string alarm_key = "alarm_" + std::to_string(index);
settings_->SetString(alarm_key, json_string);
}
cJSON_free(json_string);
cJSON_Delete(json);
}
void AlarmManager::RemoveAlarmFromStorage(int alarm_id) {
// 重新保存所有闹钟(简单实现)
SaveAlarmsToStorage();
}
int AlarmManager::GetNextAlarmId() {
return next_alarm_id_++;
}
bool AlarmManager::ShouldTriggerToday(const AlarmItem& alarm) const {
if (alarm.repeat_mode == kAlarmOnce) {
return true;
}
int weekday = GetCurrentWeekday();
return IsWeekdayActive(alarm, weekday);
}
int64_t AlarmManager::GetCurrentTimeInMinutes() const {
time_t now;
time(&now);
struct tm* timeinfo = localtime(&now);
return timeinfo->tm_hour * 60 + timeinfo->tm_min;
}
int AlarmManager::GetCurrentWeekday() const {
time_t now;
time(&now);
struct tm* timeinfo = localtime(&now);
return timeinfo->tm_wday; // 0=周日, 1=周一, ..., 6=周六
}
+140
View File
@@ -0,0 +1,140 @@
#ifndef ALARM_MANAGER_H
#define ALARM_MANAGER_H
#include <string>
#include <vector>
#include <memory>
#include <functional>
#include <esp_timer.h>
#include "settings.h"
// 闹钟重复模式
enum AlarmRepeatMode {
kAlarmOnce = 0, // 一次性闹钟
kAlarmDaily = 1, // 每日重复
kAlarmWeekdays = 2, // 工作日(周一到周五)
kAlarmWeekends = 3, // 周末(周六周日)
kAlarmCustom = 4 // 自定义星期(使用weekdays_mask)
};
// 闹钟状态
enum AlarmStatus {
kAlarmEnabled = 0, // 启用
kAlarmDisabled = 1, // 禁用
kAlarmTriggered = 2, // 已触发(等待贪睡或关闭)
kAlarmSnoozed = 3 // 贪睡中
};
// 闹钟项结构
struct AlarmItem {
int id; // 闹钟ID
int hour; // 小时 (0-23)
int minute; // 分钟 (0-59)
AlarmRepeatMode repeat_mode; // 重复模式
uint8_t weekdays_mask; // 星期掩码 (bit0=周日, bit1=周一, ..., bit6=周六)
AlarmStatus status; // 闹钟状态
std::string label; // 闹钟标签/备注
std::string music_name; // 指定的音乐名称(空则使用默认铃声)
int snooze_count; // 当前贪睡次数
int max_snooze_count; // 最大贪睡次数 (默认3次)
int snooze_minutes; // 贪睡间隔(分钟,默认5分钟)
int64_t last_triggered_time; // 上次触发时间戳(避免重复触发)
int64_t next_snooze_time; // 下次贪睡时间戳
AlarmItem() : id(0), hour(0), minute(0), repeat_mode(kAlarmOnce),
weekdays_mask(0), status(kAlarmEnabled), label(""),
music_name(""), snooze_count(0), max_snooze_count(3),
snooze_minutes(5), last_triggered_time(0), next_snooze_time(0) {}
};
// 闹钟触发回调类型
using AlarmTriggeredCallback = std::function<void(const AlarmItem& alarm)>;
using AlarmSnoozeCallback = std::function<void(const AlarmItem& alarm)>;
using AlarmStopCallback = std::function<void(const AlarmItem& alarm)>;
class AlarmManager {
public:
static AlarmManager& GetInstance() {
static AlarmManager instance;
return instance;
}
// 删除拷贝构造函数和赋值运算符
AlarmManager(const AlarmManager&) = delete;
AlarmManager& operator=(const AlarmManager&) = delete;
// 初始化和清理
void Initialize();
void Cleanup();
// 闹钟管理
int AddAlarm(int hour, int minute, AlarmRepeatMode repeat_mode = kAlarmOnce,
const std::string& label = "", const std::string& music_name = "");
bool RemoveAlarm(int alarm_id);
bool EnableAlarm(int alarm_id, bool enabled = true);
bool ModifyAlarm(int alarm_id, int hour, int minute, AlarmRepeatMode repeat_mode = kAlarmOnce,
const std::string& label = "", const std::string& music_name = "");
// 查询功能
std::vector<AlarmItem> GetAllAlarms() const;
AlarmItem* GetAlarm(int alarm_id);
std::vector<AlarmItem> GetActiveAlarms() const;
std::string GetNextAlarmInfo() const; // 获取下一个闹钟的信息字符串
// 贪睡和停止
bool SnoozeAlarm(int alarm_id);
bool StopAlarm(int alarm_id);
void StopAllActiveAlarms();
// 时间检查 (由Application的CLOCK_TICK调用)
void CheckAlarms();
// 回调设置
void SetAlarmTriggeredCallback(AlarmTriggeredCallback callback);
void SetAlarmSnoozeCallback(AlarmSnoozeCallback callback);
void SetAlarmStopCallback(AlarmStopCallback callback);
// 配置设置
void SetDefaultSnoozeMinutes(int minutes);
void SetDefaultMaxSnoozeCount(int count);
// 时间工具
static std::string FormatTime(int hour, int minute);
static std::string FormatAlarmTime(const AlarmItem& alarm);
static bool IsWeekdayActive(const AlarmItem& alarm, int weekday); // 0=周日, 1=周一, ..., 6=周六
private:
AlarmManager();
~AlarmManager();
// 内部方法
void LoadAlarmsFromStorage();
void SaveAlarmsToStorage();
void SaveAlarmToStorage(const AlarmItem& alarm);
void RemoveAlarmFromStorage(int alarm_id);
int GetNextAlarmId();
bool ShouldTriggerToday(const AlarmItem& alarm) const;
int64_t GetCurrentTimeInMinutes() const; // 获取当前时间的分钟数(从午夜开始)
int GetCurrentWeekday() const; // 获取当前星期几
// 成员变量
std::vector<std::unique_ptr<AlarmItem>> alarms_;
std::unique_ptr<Settings> settings_;
bool initialized_;
int next_alarm_id_;
// 配置
int default_snooze_minutes_;
int default_max_snooze_count_;
// 回调函数
AlarmTriggeredCallback on_alarm_triggered_;
AlarmSnoozeCallback on_alarm_snoozed_;
AlarmStopCallback on_alarm_stopped_;
// 互斥锁保护
mutable std::mutex alarms_mutex_;
};
#endif // ALARM_MANAGER_H
+428 -82
View File
@@ -7,8 +7,12 @@
#include "websocket_protocol.h" #include "websocket_protocol.h"
#include "assets/lang_config.h" #include "assets/lang_config.h"
#include "mcp_server.h" #include "mcp_server.h"
#include "assets.h"
#include "settings.h"
#include <cstring> #include <cstring>
#include <cstdlib>
#include <vector>
#include <esp_log.h> #include <esp_log.h>
#include <cJSON.h> #include <cJSON.h>
#include <driver/gpio.h> #include <driver/gpio.h>
@@ -49,7 +53,7 @@ Application::Application() {
esp_timer_create_args_t clock_timer_args = { esp_timer_create_args_t clock_timer_args = {
.callback = [](void* arg) { .callback = [](void* arg) {
Application* app = (Application*)arg; Application* app = (Application*)arg;
app->OnClockTimer(); xEventGroupSetBits(app->event_group_, MAIN_EVENT_CLOCK_TICK);
}, },
.arg = this, .arg = this,
.dispatch_method = ESP_TIMER_TASK, .dispatch_method = ESP_TIMER_TASK,
@@ -67,6 +71,57 @@ Application::~Application() {
vEventGroupDelete(event_group_); vEventGroupDelete(event_group_);
} }
void Application::CheckAssetsVersion() {
auto& board = Board::GetInstance();
auto display = board.GetDisplay();
auto& assets = Assets::GetInstance();
if (!assets.partition_valid()) {
ESP_LOGW(TAG, "Assets partition is disabled for board %s", BOARD_NAME);
return;
}
Settings settings("assets", true);
// Check if there is a new assets need to be downloaded
std::string download_url = settings.GetString("download_url");
if (!download_url.empty()) {
settings.EraseKey("download_url");
char message[256];
snprintf(message, sizeof(message), Lang::Strings::FOUND_NEW_ASSETS, download_url.c_str());
Alert(Lang::Strings::LOADING_ASSETS, message, "cloud_arrow_down", Lang::Sounds::OGG_UPGRADE);
// Wait for the audio service to be idle for 3 seconds
vTaskDelay(pdMS_TO_TICKS(3000));
SetDeviceState(kDeviceStateUpgrading);
board.SetPowerSaveMode(false);
display->SetChatMessage("system", Lang::Strings::PLEASE_WAIT);
bool success = assets.Download(download_url, [display](int progress, size_t speed) -> void {
std::thread([display, progress, speed]() {
char buffer[32];
snprintf(buffer, sizeof(buffer), "%d%% %uKB/s", progress, speed / 1024);
display->SetChatMessage("system", buffer);
}).detach();
});
board.SetPowerSaveMode(true);
vTaskDelay(pdMS_TO_TICKS(1000));
if (!success) {
Alert(Lang::Strings::ERROR, Lang::Strings::DOWNLOAD_ASSETS_FAILED, "circle_xmark", Lang::Sounds::OGG_EXCLAMATION);
vTaskDelay(pdMS_TO_TICKS(2000));
return;
}
}
// Apply assets
assets.Apply();
display->SetChatMessage("system", "");
display->SetEmotion("microchip_ai");
}
void Application::CheckNewVersion(Ota& ota) { void Application::CheckNewVersion(Ota& ota) {
const int MAX_RETRY = 10; const int MAX_RETRY = 10;
int retry_count = 0; int retry_count = 0;
@@ -103,43 +158,10 @@ void Application::CheckNewVersion(Ota& ota) {
retry_delay = 10; // 重置重试延迟时间 retry_delay = 10; // 重置重试延迟时间
if (ota.HasNewVersion()) { if (ota.HasNewVersion()) {
Alert(Lang::Strings::OTA_UPGRADE, Lang::Strings::UPGRADING, "download", Lang::Sounds::OGG_UPGRADE); if (UpgradeFirmware(ota)) {
vTaskDelay(pdMS_TO_TICKS(3000));
SetDeviceState(kDeviceStateUpgrading);
std::string message = std::string(Lang::Strings::NEW_VERSION) + ota.GetFirmwareVersion();
display->SetChatMessage("system", message.c_str());
board.SetPowerSaveMode(false);
audio_service_.Stop();
vTaskDelay(pdMS_TO_TICKS(1000));
bool upgrade_success = ota.StartUpgrade([display](int progress, size_t speed) {
std::thread([display, progress, speed]() {
char buffer[32];
snprintf(buffer, sizeof(buffer), "%d%% %uKB/s", progress, speed / 1024);
display->SetChatMessage("system", buffer);
}).detach();
});
if (!upgrade_success) {
// Upgrade failed, restart audio service and continue running
ESP_LOGE(TAG, "Firmware upgrade failed, restarting audio service and continuing operation...");
audio_service_.Start(); // Restart audio service
board.SetPowerSaveMode(true); // Restore power save mode
Alert(Lang::Strings::ERROR, Lang::Strings::UPGRADE_FAILED, "circle_xmark", Lang::Sounds::OGG_EXCLAMATION);
vTaskDelay(pdMS_TO_TICKS(3000));
// Continue to normal operation (don't break, just fall through)
} else {
// Upgrade success, reboot immediately
ESP_LOGI(TAG, "Firmware upgrade successful, rebooting...");
display->SetChatMessage("system", "Upgrade successful, rebooting...");
vTaskDelay(pdMS_TO_TICKS(1000)); // Brief pause to show message
Reboot();
return; // This line will never be reached after reboot return; // This line will never be reached after reboot
} }
// If upgrade failed, continue to normal operation (don't break, just fall through)
} }
// No new version, mark the current version as valid // No new version, mark the current version as valid
@@ -332,6 +354,9 @@ void Application::Start() {
/* Setup the display */ /* Setup the display */
auto display = board.GetDisplay(); auto display = board.GetDisplay();
// Print board name/version info
display->SetChatMessage("system", SystemInfo::GetUserAgent().c_str());
/* Setup the audio service */ /* Setup the audio service */
auto codec = board.GetAudioCodec(); auto codec = board.GetAudioCodec();
audio_service_.Initialize(codec); audio_service_.Initialize(codec);
@@ -349,6 +374,12 @@ void Application::Start() {
}; };
audio_service_.SetCallbacks(callbacks); audio_service_.SetCallbacks(callbacks);
// Start the main event loop task with priority 3
xTaskCreate([](void* arg) {
((Application*)arg)->MainEventLoop();
vTaskDelete(NULL);
}, "main_event_loop", 2048 * 4, this, 3, &main_event_loop_task_handle_);
/* Start the clock timer to update the status bar */ /* Start the clock timer to update the status bar */
esp_timer_start_periodic(clock_timer_handle_, 1000000); esp_timer_start_periodic(clock_timer_handle_, 1000000);
@@ -358,6 +389,9 @@ void Application::Start() {
// Update the status bar immediately to show the network state // Update the status bar immediately to show the network state
display->UpdateStatusBar(true); display->UpdateStatusBar(true);
// Check for new assets version
CheckAssetsVersion();
// Check for new firmware version or get the MQTT broker address // Check for new firmware version or get the MQTT broker address
Ota ota; Ota ota;
CheckNewVersion(ota); CheckNewVersion(ota);
@@ -366,7 +400,9 @@ void Application::Start() {
display->SetStatus(Lang::Strings::LOADING_PROTOCOL); display->SetStatus(Lang::Strings::LOADING_PROTOCOL);
// Add MCP common tools before initializing the protocol // Add MCP common tools before initializing the protocol
McpServer::GetInstance().AddCommonTools(); auto& mcp_server = McpServer::GetInstance();
mcp_server.AddCommonTools();
mcp_server.AddUserOnlyTools();
if (ota.HasMqttConfig()) { if (ota.HasMqttConfig()) {
protocol_ = std::make_unique<MqttProtocol>(); protocol_ = std::make_unique<MqttProtocol>();
@@ -496,6 +532,7 @@ void Application::Start() {
}); });
bool protocol_started = protocol_->Start(); bool protocol_started = protocol_->Start();
SystemInfo::PrintHeapStats();
SetDeviceState(kDeviceStateIdle); SetDeviceState(kDeviceStateIdle);
has_server_time_ = ota.HasServerTime(); has_server_time_ = ota.HasServerTime();
@@ -503,26 +540,25 @@ void Application::Start() {
std::string message = std::string(Lang::Strings::VERSION) + ota.GetCurrentVersion(); std::string message = std::string(Lang::Strings::VERSION) + ota.GetCurrentVersion();
display->ShowNotification(message.c_str()); display->ShowNotification(message.c_str());
display->SetChatMessage("system", ""); display->SetChatMessage("system", "");
// 初始化闹钟管理器
auto& alarm_manager = AlarmManager::GetInstance();
alarm_manager.Initialize();
// 设置闹钟回调
alarm_manager.SetAlarmTriggeredCallback([this](const AlarmItem& alarm) {
Schedule([this, alarm]() { OnAlarmTriggered(alarm); });
});
alarm_manager.SetAlarmSnoozeCallback([this](const AlarmItem& alarm) {
Schedule([this, alarm]() { OnAlarmSnoozed(alarm); });
});
alarm_manager.SetAlarmStopCallback([this](const AlarmItem& alarm) {
Schedule([this, alarm]() { OnAlarmStopped(alarm); });
});
// Play the success sound to indicate the device is ready // Play the success sound to indicate the device is ready
audio_service_.PlaySound(Lang::Sounds::OGG_SUCCESS); audio_service_.PlaySound(Lang::Sounds::OGG_SUCCESS);
} }
// Print heap stats
SystemInfo::PrintHeapStats();
}
void Application::OnClockTimer() {
clock_ticks_++;
auto display = Board::GetInstance().GetDisplay();
display->UpdateStatusBar();
// Print the debug info every 10 seconds
if (clock_ticks_ % 10 == 0) {
// SystemInfo::PrintTaskCpuUsage(pdMS_TO_TICKS(1000));
// SystemInfo::PrintTaskList();
SystemInfo::PrintHeapStats();
}
} }
// Add a async task to MainLoop // Add a async task to MainLoop
@@ -538,15 +574,14 @@ void Application::Schedule(std::function<void()> callback) {
// If other tasks need to access the websocket or chat state, // If other tasks need to access the websocket or chat state,
// they should use Schedule to call this function // they should use Schedule to call this function
void Application::MainEventLoop() { void Application::MainEventLoop() {
// Raise the priority of the main event loop to avoid being interrupted by background tasks (which has priority 2)
vTaskPrioritySet(NULL, 3);
while (true) { while (true) {
auto bits = xEventGroupWaitBits(event_group_, MAIN_EVENT_SCHEDULE | auto bits = xEventGroupWaitBits(event_group_, MAIN_EVENT_SCHEDULE |
MAIN_EVENT_SEND_AUDIO | MAIN_EVENT_SEND_AUDIO |
MAIN_EVENT_WAKE_WORD_DETECTED | MAIN_EVENT_WAKE_WORD_DETECTED |
MAIN_EVENT_VAD_CHANGE | MAIN_EVENT_VAD_CHANGE |
MAIN_EVENT_CLOCK_TICK |
MAIN_EVENT_ERROR, pdTRUE, pdFALSE, portMAX_DELAY); MAIN_EVENT_ERROR, pdTRUE, pdFALSE, portMAX_DELAY);
if (bits & MAIN_EVENT_ERROR) { if (bits & MAIN_EVENT_ERROR) {
SetDeviceState(kDeviceStateIdle); SetDeviceState(kDeviceStateIdle);
Alert(Lang::Strings::ERROR, last_error_message_.c_str(), "circle_xmark", Lang::Sounds::OGG_EXCLAMATION); Alert(Lang::Strings::ERROR, last_error_message_.c_str(), "circle_xmark", Lang::Sounds::OGG_EXCLAMATION);
@@ -554,7 +589,7 @@ void Application::MainEventLoop() {
if (bits & MAIN_EVENT_SEND_AUDIO) { if (bits & MAIN_EVENT_SEND_AUDIO) {
while (auto packet = audio_service_.PopPacketFromSendQueue()) { while (auto packet = audio_service_.PopPacketFromSendQueue()) {
if (!protocol_->SendAudio(std::move(packet))) { if (protocol_ && !protocol_->SendAudio(std::move(packet))) {
break; break;
} }
} }
@@ -579,6 +614,29 @@ void Application::MainEventLoop() {
task(); task();
} }
} }
if (bits & MAIN_EVENT_CLOCK_TICK) {
clock_ticks_++;
auto display = Board::GetInstance().GetDisplay();
display->UpdateStatusBar();
display->OnClockTimer();
// 检查闹钟(每秒检查一次)
auto& alarm_manager = AlarmManager::GetInstance();
alarm_manager.CheckAlarms();
// 更新音乐播放进度(每秒更新一次)
if (is_music_playing_) {
UpdateMusicProgress();
}
// Print the debug info every 10 seconds
if (clock_ticks_ % 10 == 0) {
// SystemInfo::PrintTaskCpuUsage(pdMS_TO_TICKS(1000));
// SystemInfo::PrintTaskList();
SystemInfo::PrintHeapStats();
}
}
} }
} }
@@ -600,7 +658,7 @@ void Application::OnWakeWordDetected() {
auto wake_word = audio_service_.GetLastWakeWord(); auto wake_word = audio_service_.GetLastWakeWord();
ESP_LOGI(TAG, "Wake word detected: %s", wake_word.c_str()); ESP_LOGI(TAG, "Wake word detected: %s", wake_word.c_str());
#if CONFIG_USE_AFE_WAKE_WORD || CONFIG_USE_CUSTOM_WAKE_WORD #if CONFIG_SEND_WAKE_WORD_DATA
// Encode and send the wake word data to the server // Encode and send the wake word data to the server
while (auto packet = audio_service_.PopWakeWordPacket()) { while (auto packet = audio_service_.PopWakeWordPacket()) {
protocol_->SendAudio(std::move(packet)); protocol_->SendAudio(std::move(packet));
@@ -623,7 +681,9 @@ void Application::OnWakeWordDetected() {
void Application::AbortSpeaking(AbortReason reason) { void Application::AbortSpeaking(AbortReason reason) {
ESP_LOGI(TAG, "Abort speaking"); ESP_LOGI(TAG, "Abort speaking");
aborted_ = true; aborted_ = true;
protocol_->SendAbortSpeaking(reason); if (protocol_) {
protocol_->SendAbortSpeaking(reason);
}
} }
void Application::SetListeningMode(ListeningMode mode) { void Application::SetListeningMode(ListeningMode mode) {
@@ -648,17 +708,7 @@ void Application::SetDeviceState(DeviceState state) {
auto display = board.GetDisplay(); auto display = board.GetDisplay();
auto led = board.GetLed(); auto led = board.GetLed();
led->OnStateChanged(); led->OnStateChanged();
display->OnStateChanged();
// 当从idle状态变成其他任何状态时,停止音乐播放
if (previous_state == kDeviceStateIdle && state != kDeviceStateIdle) {
auto music = board.GetMusic();
if (music) {
ESP_LOGI(TAG, "Stopping music streaming due to state change: %s -> %s",
STATE_STRINGS[previous_state], STATE_STRINGS[state]);
music->StopStreaming();
}
}
switch (state) { switch (state) {
case kDeviceStateUnknown: case kDeviceStateUnknown:
case kDeviceStateIdle: case kDeviceStateIdle:
@@ -690,11 +740,7 @@ void Application::SetDeviceState(DeviceState state) {
if (listening_mode_ != kListeningModeRealtime) { if (listening_mode_ != kListeningModeRealtime) {
audio_service_.EnableVoiceProcessing(false); audio_service_.EnableVoiceProcessing(false);
// Only AFE wake word can be detected in speaking mode // Only AFE wake word can be detected in speaking mode
#if CONFIG_USE_AFE_WAKE_WORD audio_service_.EnableWakeWordDetection(audio_service_.IsAfeWakeWord());
audio_service_.EnableWakeWordDetection(true);
#else
audio_service_.EnableWakeWordDetection(false);
#endif
} }
audio_service_.ResetDecoder(); audio_service_.ResetDecoder();
break; break;
@@ -706,9 +752,70 @@ void Application::SetDeviceState(DeviceState state) {
void Application::Reboot() { void Application::Reboot() {
ESP_LOGI(TAG, "Rebooting..."); ESP_LOGI(TAG, "Rebooting...");
// Disconnect the audio channel
if (protocol_ && protocol_->IsAudioChannelOpened()) {
protocol_->CloseAudioChannel();
}
protocol_.reset();
audio_service_.Stop();
vTaskDelay(pdMS_TO_TICKS(1000));
esp_restart(); esp_restart();
} }
bool Application::UpgradeFirmware(Ota& ota, const std::string& url) {
auto& board = Board::GetInstance();
auto display = board.GetDisplay();
// Use provided URL or get from OTA object
std::string upgrade_url = url.empty() ? ota.GetFirmwareUrl() : url;
std::string version_info = url.empty() ? ota.GetFirmwareVersion() : "(Manual upgrade)";
// Close audio channel if it's open
if (protocol_ && protocol_->IsAudioChannelOpened()) {
ESP_LOGI(TAG, "Closing audio channel before firmware upgrade");
protocol_->CloseAudioChannel();
}
ESP_LOGI(TAG, "Starting firmware upgrade from URL: %s", upgrade_url.c_str());
Alert(Lang::Strings::OTA_UPGRADE, Lang::Strings::UPGRADING, "download", Lang::Sounds::OGG_UPGRADE);
vTaskDelay(pdMS_TO_TICKS(3000));
SetDeviceState(kDeviceStateUpgrading);
std::string message = std::string(Lang::Strings::NEW_VERSION) + version_info;
display->SetChatMessage("system", message.c_str());
board.SetPowerSaveMode(false);
audio_service_.Stop();
vTaskDelay(pdMS_TO_TICKS(1000));
bool upgrade_success = ota.StartUpgradeFromUrl(upgrade_url, [display](int progress, size_t speed) {
std::thread([display, progress, speed]() {
char buffer[32];
snprintf(buffer, sizeof(buffer), "%d%% %uKB/s", progress, speed / 1024);
display->SetChatMessage("system", buffer);
}).detach();
});
if (!upgrade_success) {
// Upgrade failed, restart audio service and continue running
ESP_LOGE(TAG, "Firmware upgrade failed, restarting audio service and continuing operation...");
audio_service_.Start(); // Restart audio service
board.SetPowerSaveMode(true); // Restore power save mode
Alert(Lang::Strings::ERROR, Lang::Strings::UPGRADE_FAILED, "circle_xmark", Lang::Sounds::OGG_EXCLAMATION);
vTaskDelay(pdMS_TO_TICKS(3000));
return false;
} else {
// Upgrade success, reboot immediately
ESP_LOGI(TAG, "Firmware upgrade successful, rebooting...");
display->SetChatMessage("system", "Upgrade successful, rebooting...");
vTaskDelay(pdMS_TO_TICKS(1000)); // Brief pause to show message
Reboot();
return true;
}
}
void Application::WakeWordInvoke(const std::string& wake_word) { void Application::WakeWordInvoke(const std::string& wake_word) {
if (device_state_ == kDeviceStateIdle) { if (device_state_ == kDeviceStateIdle) {
ToggleChatState(); ToggleChatState();
@@ -748,11 +855,18 @@ bool Application::CanEnterSleepMode() {
} }
void Application::SendMcpMessage(const std::string& payload) { void Application::SendMcpMessage(const std::string& payload) {
Schedule([this, payload]() { if (protocol_ == nullptr) {
if (protocol_) { return;
}
// Make sure you are using main thread to send MCP message
if (xTaskGetCurrentTaskHandle() == main_event_loop_task_handle_) {
protocol_->SendMcpMessage(payload);
} else {
Schedule([this, payload = std::move(payload)]() {
protocol_->SendMcpMessage(payload); protocol_->SendMcpMessage(payload);
} });
}); }
} }
void Application::SetAecMode(AecMode mode) { void Application::SetAecMode(AecMode mode) {
@@ -782,6 +896,10 @@ void Application::SetAecMode(AecMode mode) {
}); });
} }
void Application::PlaySound(const std::string_view& sound) {
audio_service_.PlaySound(sound);
}
// 新增:接收外部音频数据(如音乐播放) // 新增:接收外部音频数据(如音乐播放)
void Application::AddAudioData(AudioStreamPacket&& packet) { void Application::AddAudioData(AudioStreamPacket&& packet) {
auto codec = Board::GetInstance().GetAudioCodec(); auto codec = Board::GetInstance().GetAudioCodec();
@@ -871,6 +989,234 @@ void Application::AddAudioData(AudioStreamPacket&& packet) {
} }
} }
void Application::PlaySound(const std::string_view& sound) { // 随机闹钟音乐列表 - 流行、经典、适合早晨的歌曲
audio_service_.PlaySound(sound); static const std::vector<std::string> DEFAULT_ALARM_SONGS = {
"晴天", "七里香", "青花瓷", "稻香", "彩虹", "告白气球", "说好不哭",
"夜曲", "花海", "简单爱", "听妈妈的话", "东风破", "菊花台",
"起风了", "红豆", "好久不见", "匆匆那年", "老男孩", "那些年",
"小幸运", "成都", "南山南", "演员", "体面", "盗将行", "大鱼",
"新不了情", "月亮代表我的心", "甜蜜蜜", "邓丽君", "我只在乎你",
"友谊之光", "童年", "海阔天空", "光辉岁月", "真的爱你", "喜欢你",
"突然好想你", "情非得已", "温柔", "倔强", "知足", "三个傻瓜",
"恋爱循环", "千本樱", "打上花火", "lemon", "残酷天使的行动纲领",
"鸟笼", "", "青鸟", "closer", "sugar", "shape of you",
"despacito", "perfect", "happier", "someone like you"
};
// 获取随机闹钟音乐
static std::string GetRandomAlarmMusic() {
if (DEFAULT_ALARM_SONGS.empty()) {
return "";
}
// 使用当前时间作为随机种子
srand(esp_timer_get_time() / 1000000);
size_t index = rand() % DEFAULT_ALARM_SONGS.size();
return DEFAULT_ALARM_SONGS[index];
}
// 闹钟回调方法实现
void Application::OnAlarmTriggered(const AlarmItem& alarm) {
ESP_LOGI("Application", "Alarm triggered: %s at %02d:%02d",
alarm.label.c_str(), alarm.hour, alarm.minute);
auto& board = Board::GetInstance();
auto display = board.GetDisplay();
auto music = board.GetMusic();
// 显示闹钟信息
std::string alarm_message = "🎵 闹钟";
if (!alarm.label.empty()) {
alarm_message += "\n" + alarm.label;
}
alarm_message += "\n" + AlarmManager::FormatTime(alarm.hour, alarm.minute);
// 优先在时钟界面显示(如果支持)
display->ShowAlarmOnIdleScreen(alarm_message.c_str());
// 同时设置聊天消息(作为备用)
display->SetChatMessage("system", alarm_message.c_str());
display->SetEmotion("music");
// 确定要播放的音乐
std::string music_to_play;
if (!alarm.music_name.empty()) {
// 使用用户指定的音乐
music_to_play = alarm.music_name;
ESP_LOGI("Application", "Playing user specified alarm music: %s", music_to_play.c_str());
} else {
// 随机选择一首默认闹钟音乐
music_to_play = GetRandomAlarmMusic();
ESP_LOGI("Application", "Playing random alarm music: %s", music_to_play.c_str());
}
// 播放音乐
if (music && !music_to_play.empty()) {
// 更新显示,显示正在播放的歌曲
std::string playing_message = "🎵 正在播放: " + music_to_play;
display->SetChatMessage("system", playing_message.c_str());
// 开始下载并播放音乐
if (music->Download(music_to_play)) {
ESP_LOGI("Application", "Successfully started alarm music: %s", music_to_play.c_str());
// 开始音乐进度跟踪
current_music_name_ = music_to_play;
music_start_time_ms_ = esp_timer_get_time() / 1000; // 转换为毫秒
is_music_playing_ = true;
// 尝试获取真实的歌曲长度
int real_duration = music->GetCurrentSongDurationSeconds();
if (real_duration > 0) {
music_duration_seconds_ = real_duration;
ESP_LOGI("Application", "Got real song duration: %d seconds", real_duration);
}
// 启动进度显示
display->SetMusicProgress(music_to_play.c_str(), 0, music_duration_seconds_, 0.0f);
} else {
ESP_LOGW("Application", "Failed to download alarm music: %s, using fallback", music_to_play.c_str());
// 如果下载失败,播放默认铃声
audio_service_.PlaySound(Lang::Sounds::OGG_VIBRATION);
}
} else {
ESP_LOGW("Application", "Music service not available or no music selected, using default alarm sound");
// 如果没有音乐功能或选择失败,播放默认铃声
audio_service_.PlaySound(Lang::Sounds::OGG_VIBRATION);
}
// 显示闹钟控制提示
std::string control_message = "🎵 说\"贪睡\"延后5分钟,说\"关闭闹钟\"停止音乐";
display->ShowNotification(control_message.c_str());
}
void Application::OnAlarmSnoozed(const AlarmItem& alarm) {
ESP_LOGI("Application", "Alarm snoozed: %s, count: %d/%d",
alarm.label.c_str(), alarm.snooze_count, alarm.max_snooze_count);
auto& board = Board::GetInstance();
auto display = board.GetDisplay();
// 隐藏空闲屏幕上的闹钟信息
display->HideAlarmOnIdleScreen();
// 停止当前播放的音乐
auto music = board.GetMusic();
if (music) {
music->StopStreaming();
}
// 停止音乐进度跟踪并清除音乐界面
is_music_playing_ = false;
display->ClearMusicInfo();
std::string snooze_message = "💤 闹钟已贪睡 " + std::to_string(alarm.snooze_minutes) + " 分钟";
display->SetChatMessage("system", snooze_message.c_str());
display->SetEmotion("neutral");
audio_service_.PlaySound(Lang::Sounds::OGG_SUCCESS);
}
void Application::OnAlarmStopped(const AlarmItem& alarm) {
ESP_LOGI("Application", "Alarm stopped: %s", alarm.label.c_str());
auto& board = Board::GetInstance();
auto display = board.GetDisplay();
// 隐藏空闲屏幕上的闹钟信息
display->HideAlarmOnIdleScreen();
// 停止当前播放的音乐
auto music = board.GetMusic();
if (music) {
music->StopStreaming();
}
// 停止音乐进度跟踪并清除音乐界面
is_music_playing_ = false;
display->ClearMusicInfo();
display->SetChatMessage("system", "✅ 闹钟已关闭");
display->SetEmotion("neutral");
// 显示下一个闹钟信息
auto& alarm_manager = AlarmManager::GetInstance();
std::string next_alarm_info = alarm_manager.GetNextAlarmInfo();
display->ShowNotification(next_alarm_info.c_str());
audio_service_.PlaySound(Lang::Sounds::OGG_SUCCESS);
}
// 获取默认闹钟音乐列表
std::vector<std::string> Application::GetDefaultAlarmMusicList() const {
return DEFAULT_ALARM_SONGS;
}
// 更新音乐播放进度
void Application::UpdateMusicProgress() {
if (!is_music_playing_) {
return;
}
auto& board = Board::GetInstance();
auto music = board.GetMusic();
auto display = board.GetDisplay();
if (!music || !display) {
return;
}
// 从音乐播放器获取真实的播放信息
int real_current_seconds = music->GetCurrentPlayTimeSeconds();
int real_duration_seconds = music->GetCurrentSongDurationSeconds();
float real_progress_percent = music->GetPlayProgress();
// 更新存储的歌曲长度(如果有变化)
if (real_duration_seconds > 0 && real_duration_seconds != music_duration_seconds_) {
music_duration_seconds_ = real_duration_seconds;
ESP_LOGI("Application", "Updated song duration: %d seconds", music_duration_seconds_);
}
// 检查是否播放结束
bool is_still_playing = music->IsDownloading() || (real_current_seconds < real_duration_seconds && real_current_seconds > 0);
if (!is_still_playing && real_current_seconds >= real_duration_seconds && real_duration_seconds > 0) {
is_music_playing_ = false; // 停止跟踪
ESP_LOGI("Application", "Music playback finished: %s (%d/%d seconds)",
current_music_name_.c_str(), real_current_seconds, real_duration_seconds);
// 🎵 音乐播放完毕,自动停止所有活跃的闹钟
auto& alarm_manager = AlarmManager::GetInstance();
auto active_alarms = alarm_manager.GetActiveAlarms();
if (!active_alarms.empty()) {
ESP_LOGI("Application", "Auto-stopping alarms after music finished");
// 停止闹钟
for (const auto& alarm : active_alarms) {
alarm_manager.StopAlarm(alarm.id);
}
// 在界面上显示用户确认消息
auto& board = Board::GetInstance();
auto display = board.GetDisplay();
if (display) {
display->SetChatMessage("user", "我听到你的闹钟啦 ✅");
display->SetEmotion("happy");
}
}
}
// 更新显示(使用真实的播放时间)
if (is_music_playing_) {
display->SetMusicProgress(current_music_name_.c_str(),
real_current_seconds,
real_duration_seconds,
real_progress_percent);
ESP_LOGD("Application", "Music progress: %s - %d/%d seconds (%.1f%%)",
current_music_name_.c_str(), real_current_seconds, real_duration_seconds, real_progress_percent);
} else {
// 播放结束,清除界面
display->ClearMusicInfo();
}
} }
+39 -3
View File
@@ -15,6 +15,8 @@
#include "ota.h" #include "ota.h"
#include "audio_service.h" #include "audio_service.h"
#include "device_state_event.h" #include "device_state_event.h"
#include "alarm_manager.h"
#define MAIN_EVENT_SCHEDULE (1 << 0) #define MAIN_EVENT_SCHEDULE (1 << 0)
#define MAIN_EVENT_SEND_AUDIO (1 << 1) #define MAIN_EVENT_SEND_AUDIO (1 << 1)
@@ -22,6 +24,8 @@
#define MAIN_EVENT_VAD_CHANGE (1 << 3) #define MAIN_EVENT_VAD_CHANGE (1 << 3)
#define MAIN_EVENT_ERROR (1 << 4) #define MAIN_EVENT_ERROR (1 << 4)
#define MAIN_EVENT_CHECK_NEW_VERSION_DONE (1 << 5) #define MAIN_EVENT_CHECK_NEW_VERSION_DONE (1 << 5)
#define MAIN_EVENT_CLOCK_TICK (1 << 6)
enum AecMode { enum AecMode {
kAecOff, kAecOff,
@@ -53,16 +57,20 @@ public:
void StopListening(); void StopListening();
void Reboot(); void Reboot();
void WakeWordInvoke(const std::string& wake_word); void WakeWordInvoke(const std::string& wake_word);
bool UpgradeFirmware(Ota& ota, const std::string& url = "");
bool CanEnterSleepMode(); bool CanEnterSleepMode();
void SendMcpMessage(const std::string& payload); void SendMcpMessage(const std::string& payload);
void SetAecMode(AecMode mode); void SetAecMode(AecMode mode);
AecMode GetAecMode() const { return aec_mode_; } AecMode GetAecMode() const { return aec_mode_; }
void PlaySound(const std::string_view& sound);
// 新增:接收外部音频数据(如音乐播放) // 新增:接收外部音频数据(如音乐播放)
void AddAudioData(AudioStreamPacket&& packet); void AddAudioData(AudioStreamPacket&& packet);
void PlaySound(const std::string_view& sound);
AudioService& GetAudioService() { return audio_service_; } AudioService& GetAudioService() { return audio_service_; }
// 闹钟功能
AlarmManager& GetAlarmManager() { return AlarmManager::GetInstance(); }
std::vector<std::string> GetDefaultAlarmMusicList() const;
private: private:
Application(); Application();
~Application(); ~Application();
@@ -82,12 +90,40 @@ private:
bool aborted_ = false; bool aborted_ = false;
int clock_ticks_ = 0; int clock_ticks_ = 0;
TaskHandle_t check_new_version_task_handle_ = nullptr; TaskHandle_t check_new_version_task_handle_ = nullptr;
TaskHandle_t main_event_loop_task_handle_ = nullptr;
void OnWakeWordDetected(); void OnWakeWordDetected();
void CheckNewVersion(Ota& ota); void CheckNewVersion(Ota& ota);
void CheckAssetsVersion();
void ShowActivationCode(const std::string& code, const std::string& message); void ShowActivationCode(const std::string& code, const std::string& message);
void OnClockTimer();
void SetListeningMode(ListeningMode mode); void SetListeningMode(ListeningMode mode);
// 闹钟相关私有方法
void OnAlarmTriggered(const AlarmItem& alarm);
void OnAlarmSnoozed(const AlarmItem& alarm);
void OnAlarmStopped(const AlarmItem& alarm);
// 音乐进度跟踪相关
void UpdateMusicProgress();
std::string current_music_name_;
int64_t music_start_time_ms_ = 0; // 音乐开始播放的时间戳
int music_duration_seconds_ = 180; // 默认歌曲长度(3分钟)
bool is_music_playing_ = false;
};
class TaskPriorityReset {
public:
TaskPriorityReset(BaseType_t priority) {
original_priority_ = uxTaskPriorityGet(NULL);
vTaskPrioritySet(NULL, priority);
}
~TaskPriorityReset() {
vTaskPrioritySet(NULL, original_priority_);
}
private:
BaseType_t original_priority_;
}; };
#endif // _APPLICATION_H_ #endif // _APPLICATION_H_
+517
View File
@@ -0,0 +1,517 @@
#include "assets.h"
#include "board.h"
#include "display.h"
#include "application.h"
#include "lvgl_theme.h"
#include "emote_display.h"
#include <esp_log.h>
#include <spi_flash_mmap.h>
#include <esp_timer.h>
#include <cbin_font.h>
#define TAG "Assets"
struct mmap_assets_table {
char asset_name[32]; /*!< Name of the asset */
uint32_t asset_size; /*!< Size of the asset */
uint32_t asset_offset; /*!< Offset of the asset */
uint16_t asset_width; /*!< Width of the asset */
uint16_t asset_height; /*!< Height of the asset */
};
Assets::Assets() {
// Initialize the partition
InitializePartition();
}
Assets::~Assets() {
if (mmap_handle_ != 0) {
esp_partition_munmap(mmap_handle_);
}
}
uint32_t Assets::CalculateChecksum(const char* data, uint32_t length) {
uint32_t checksum = 0;
for (uint32_t i = 0; i < length; i++) {
checksum += data[i];
}
return checksum & 0xFFFF;
}
bool Assets::InitializePartition() {
partition_valid_ = false;
checksum_valid_ = false;
assets_.clear();
partition_ = esp_partition_find_first(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, "assets");
if (partition_ == nullptr) {
ESP_LOGI(TAG, "No assets partition found");
return false;
}
int free_pages = spi_flash_mmap_get_free_pages(SPI_FLASH_MMAP_DATA);
uint32_t storage_size = free_pages * 64 * 1024;
ESP_LOGI(TAG, "The storage free size is %ld KB", storage_size / 1024);
ESP_LOGI(TAG, "The partition size is %ld KB", partition_->size / 1024);
if (storage_size < partition_->size) {
ESP_LOGE(TAG, "The free size %ld KB is less than assets partition required %ld KB", storage_size / 1024, partition_->size / 1024);
return false;
}
esp_err_t err = esp_partition_mmap(partition_, 0, partition_->size, ESP_PARTITION_MMAP_DATA, (const void**)&mmap_root_, &mmap_handle_);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to mmap assets partition: %s", esp_err_to_name(err));
return false;
}
partition_valid_ = true;
uint32_t stored_files = *(uint32_t*)(mmap_root_ + 0);
uint32_t stored_chksum = *(uint32_t*)(mmap_root_ + 4);
uint32_t stored_len = *(uint32_t*)(mmap_root_ + 8);
if (stored_len > partition_->size - 12) {
ESP_LOGD(TAG, "The stored_len (0x%lx) is greater than the partition size (0x%lx) - 12", stored_len, partition_->size);
return false;
}
auto start_time = esp_timer_get_time();
uint32_t calculated_checksum = CalculateChecksum(mmap_root_ + 12, stored_len);
auto end_time = esp_timer_get_time();
ESP_LOGI(TAG, "The checksum calculation time is %d ms", int((end_time - start_time) / 1000));
if (calculated_checksum != stored_chksum) {
ESP_LOGE(TAG, "The calculated checksum (0x%lx) does not match the stored checksum (0x%lx)", calculated_checksum, stored_chksum);
return false;
}
checksum_valid_ = true;
for (uint32_t i = 0; i < stored_files; i++) {
auto item = (const mmap_assets_table*)(mmap_root_ + 12 + i * sizeof(mmap_assets_table));
auto asset = Asset{
.size = static_cast<size_t>(item->asset_size),
.offset = static_cast<size_t>(12 + sizeof(mmap_assets_table) * stored_files + item->asset_offset)
};
assets_[item->asset_name] = asset;
}
return checksum_valid_;
}
bool Assets::Apply() {
void* ptr = nullptr;
size_t size = 0;
if (!GetAssetData("index.json", ptr, size)) {
ESP_LOGE(TAG, "The index.json file is not found");
return false;
}
cJSON* root = cJSON_ParseWithLength(static_cast<char*>(ptr), size);
if (root == nullptr) {
ESP_LOGE(TAG, "The index.json file is not valid");
return false;
}
cJSON* version = cJSON_GetObjectItem(root, "version");
if (cJSON_IsNumber(version)) {
if (version->valuedouble > 1) {
ESP_LOGE(TAG, "The assets version %d is not supported, please upgrade the firmware", version->valueint);
return false;
}
}
cJSON* srmodels = cJSON_GetObjectItem(root, "srmodels");
if (cJSON_IsString(srmodels)) {
std::string srmodels_file = srmodels->valuestring;
if (GetAssetData(srmodels_file, ptr, size)) {
if (models_list_ != nullptr) {
esp_srmodel_deinit(models_list_);
models_list_ = nullptr;
}
models_list_ = srmodel_load(static_cast<uint8_t*>(ptr));
if (models_list_ != nullptr) {
auto& app = Application::GetInstance();
app.GetAudioService().SetModelsList(models_list_);
} else {
ESP_LOGE(TAG, "Failed to load srmodels.bin");
}
} else {
ESP_LOGE(TAG, "The srmodels file %s is not found", srmodels_file.c_str());
}
}
#ifdef HAVE_LVGL
auto& theme_manager = LvglThemeManager::GetInstance();
auto light_theme = theme_manager.GetTheme("light");
auto dark_theme = theme_manager.GetTheme("dark");
cJSON* font = cJSON_GetObjectItem(root, "text_font");
if (cJSON_IsString(font)) {
std::string fonts_text_file = font->valuestring;
if (GetAssetData(fonts_text_file, ptr, size)) {
auto text_font = std::make_shared<LvglCBinFont>(ptr);
if (text_font->font() == nullptr) {
ESP_LOGE(TAG, "Failed to load fonts.bin");
return false;
}
if (light_theme != nullptr) {
light_theme->set_text_font(text_font);
}
if (dark_theme != nullptr) {
dark_theme->set_text_font(text_font);
}
} else {
ESP_LOGE(TAG, "The font file %s is not found", fonts_text_file.c_str());
}
}
cJSON* emoji_collection = cJSON_GetObjectItem(root, "emoji_collection");
if (cJSON_IsArray(emoji_collection)) {
auto custom_emoji_collection = std::make_shared<EmojiCollection>();
int emoji_count = cJSON_GetArraySize(emoji_collection);
for (int i = 0; i < emoji_count; i++) {
cJSON* emoji = cJSON_GetArrayItem(emoji_collection, i);
if (cJSON_IsObject(emoji)) {
cJSON* name = cJSON_GetObjectItem(emoji, "name");
cJSON* file = cJSON_GetObjectItem(emoji, "file");
cJSON* eaf = cJSON_GetObjectItem(emoji, "eaf");
if (cJSON_IsString(name) && cJSON_IsString(file) && (NULL== eaf)) {
if (!GetAssetData(file->valuestring, ptr, size)) {
ESP_LOGE(TAG, "Emoji %s image file %s is not found", name->valuestring, file->valuestring);
continue;
}
custom_emoji_collection->AddEmoji(name->valuestring, new LvglRawImage(ptr, size));
}
}
}
if (light_theme != nullptr) {
light_theme->set_emoji_collection(custom_emoji_collection);
}
if (dark_theme != nullptr) {
dark_theme->set_emoji_collection(custom_emoji_collection);
}
}
cJSON* skin = cJSON_GetObjectItem(root, "skin");
if (cJSON_IsObject(skin)) {
cJSON* light_skin = cJSON_GetObjectItem(skin, "light");
if (cJSON_IsObject(light_skin) && light_theme != nullptr) {
cJSON* text_color = cJSON_GetObjectItem(light_skin, "text_color");
cJSON* background_color = cJSON_GetObjectItem(light_skin, "background_color");
cJSON* background_image = cJSON_GetObjectItem(light_skin, "background_image");
if (cJSON_IsString(text_color)) {
light_theme->set_text_color(LvglTheme::ParseColor(text_color->valuestring));
}
if (cJSON_IsString(background_color)) {
light_theme->set_background_color(LvglTheme::ParseColor(background_color->valuestring));
light_theme->set_chat_background_color(LvglTheme::ParseColor(background_color->valuestring));
}
if (cJSON_IsString(background_image)) {
if (!GetAssetData(background_image->valuestring, ptr, size)) {
ESP_LOGE(TAG, "The background image file %s is not found", background_image->valuestring);
return false;
}
auto background_image = std::make_shared<LvglCBinImage>(ptr);
light_theme->set_background_image(background_image);
}
}
cJSON* dark_skin = cJSON_GetObjectItem(skin, "dark");
if (cJSON_IsObject(dark_skin) && dark_theme != nullptr) {
cJSON* text_color = cJSON_GetObjectItem(dark_skin, "text_color");
cJSON* background_color = cJSON_GetObjectItem(dark_skin, "background_color");
cJSON* background_image = cJSON_GetObjectItem(dark_skin, "background_image");
if (cJSON_IsString(text_color)) {
dark_theme->set_text_color(LvglTheme::ParseColor(text_color->valuestring));
}
if (cJSON_IsString(background_color)) {
dark_theme->set_background_color(LvglTheme::ParseColor(background_color->valuestring));
dark_theme->set_chat_background_color(LvglTheme::ParseColor(background_color->valuestring));
}
if (cJSON_IsString(background_image)) {
if (!GetAssetData(background_image->valuestring, ptr, size)) {
ESP_LOGE(TAG, "The background image file %s is not found", background_image->valuestring);
return false;
}
auto background_image = std::make_shared<LvglCBinImage>(ptr);
dark_theme->set_background_image(background_image);
}
}
}
auto display = Board::GetInstance().GetDisplay();
ESP_LOGI(TAG, "Refreshing display theme...");
auto current_theme = display->GetTheme();
if (current_theme != nullptr) {
display->SetTheme(current_theme);
}
#elif defined(CONFIG_USE_EMOTE_MESSAGE_STYLE)
auto &board = Board::GetInstance();
auto display = board.GetDisplay();
auto emote_display = dynamic_cast<emote::EmoteDisplay*>(display);
cJSON* font = cJSON_GetObjectItem(root, "text_font");
if (cJSON_IsString(font)) {
std::string fonts_text_file = font->valuestring;
if (GetAssetData(fonts_text_file, ptr, size)) {
auto text_font = std::make_shared<LvglCBinFont>(ptr);
if (text_font->font() == nullptr) {
ESP_LOGE(TAG, "Failed to load fonts.bin");
return false;
}
if (emote_display) {
emote_display->AddTextFont(text_font);
}
} else {
ESP_LOGE(TAG, "The font file %s is not found", fonts_text_file.c_str());
}
}
cJSON* emoji_collection = cJSON_GetObjectItem(root, "emoji_collection");
if (cJSON_IsArray(emoji_collection)) {
int emoji_count = cJSON_GetArraySize(emoji_collection);
if (emote_display) {
for (int i = 0; i < emoji_count; i++) {
cJSON* icon = cJSON_GetArrayItem(emoji_collection, i);
if (cJSON_IsObject(icon)) {
cJSON* name = cJSON_GetObjectItem(icon, "name");
cJSON* file = cJSON_GetObjectItem(icon, "file");
if (cJSON_IsString(name) && cJSON_IsString(file)) {
if (GetAssetData(file->valuestring, ptr, size)) {
cJSON* eaf = cJSON_GetObjectItem(icon, "eaf");
bool lack_value = false;
bool loop_value = false;
int fps_value = 0;
if (cJSON_IsObject(eaf)) {
cJSON* lack = cJSON_GetObjectItem(eaf, "lack");
cJSON* loop = cJSON_GetObjectItem(eaf, "loop");
cJSON* fps = cJSON_GetObjectItem(eaf, "fps");
lack_value = lack ? cJSON_IsTrue(lack) : false;
loop_value = loop ? cJSON_IsTrue(loop) : false;
fps_value = fps ? fps->valueint : 0;
emote_display->AddEmojiData(name->valuestring, ptr, size,
static_cast<uint8_t>(fps_value),
loop_value, lack_value);
}
} else {
ESP_LOGE(TAG, "Emoji \"%10s\" image file %s is not found", name->valuestring, file->valuestring);
}
}
}
}
}
}
cJSON* icon_collection = cJSON_GetObjectItem(root, "icon_collection");
if (cJSON_IsArray(icon_collection)) {
if (emote_display) {
int icon_count = cJSON_GetArraySize(icon_collection);
for (int i = 0; i < icon_count; i++) {
cJSON* icon = cJSON_GetArrayItem(icon_collection, i);
if (cJSON_IsObject(icon)) {
cJSON* name = cJSON_GetObjectItem(icon, "name");
cJSON* file = cJSON_GetObjectItem(icon, "file");
if (cJSON_IsString(name) && cJSON_IsString(file)) {
if (GetAssetData(file->valuestring, ptr, size)) {
emote_display->AddIconData(name->valuestring, ptr, size);
} else {
ESP_LOGE(TAG, "Icon \"%10s\" image file %s is not found", name->valuestring, file->valuestring);
}
}
}
}
}
}
cJSON* layout_json = cJSON_GetObjectItem(root, "layout");
if (cJSON_IsArray(layout_json)) {
int layout_count = cJSON_GetArraySize(layout_json);
for (int i = 0; i < layout_count; i++) {
cJSON* layout_item = cJSON_GetArrayItem(layout_json, i);
if (cJSON_IsObject(layout_item)) {
cJSON* name = cJSON_GetObjectItem(layout_item, "name");
cJSON* align = cJSON_GetObjectItem(layout_item, "align");
cJSON* x = cJSON_GetObjectItem(layout_item, "x");
cJSON* y = cJSON_GetObjectItem(layout_item, "y");
cJSON* width = cJSON_GetObjectItem(layout_item, "width");
cJSON* height = cJSON_GetObjectItem(layout_item, "height");
if (cJSON_IsString(name) && cJSON_IsString(align) && cJSON_IsNumber(x) && cJSON_IsNumber(y)) {
int width_val = cJSON_IsNumber(width) ? width->valueint : 0;
int height_val = cJSON_IsNumber(height) ? height->valueint : 0;
if (emote_display) {
emote_display->AddLayoutData(name->valuestring, align->valuestring,
x->valueint, y->valueint, width_val, height_val);
}
} else {
ESP_LOGW(TAG, "Invalid layout item %d: missing required fields", i);
}
}
}
}
#endif
cJSON_Delete(root);
return true;
}
bool Assets::Download(std::string url, std::function<void(int progress, size_t speed)> progress_callback) {
ESP_LOGI(TAG, "Downloading new version of assets from %s", url.c_str());
// 取消当前资源分区的内存映射
if (mmap_handle_ != 0) {
esp_partition_munmap(mmap_handle_);
mmap_handle_ = 0;
mmap_root_ = nullptr;
}
checksum_valid_ = false;
assets_.clear();
// 下载新的资源文件
auto network = Board::GetInstance().GetNetwork();
auto http = network->CreateHttp(0);
if (!http->Open("GET", url)) {
ESP_LOGE(TAG, "Failed to open HTTP connection");
return false;
}
if (http->GetStatusCode() != 200) {
ESP_LOGE(TAG, "Failed to get assets, status code: %d", http->GetStatusCode());
return false;
}
size_t content_length = http->GetBodyLength();
if (content_length == 0) {
ESP_LOGE(TAG, "Failed to get content length");
return false;
}
if (content_length > partition_->size) {
ESP_LOGE(TAG, "Assets file size (%u) is larger than partition size (%lu)", content_length, partition_->size);
return false;
}
// 定义扇区大小为4KB(ESP32的标准扇区大小)
const size_t SECTOR_SIZE = esp_partition_get_main_flash_sector_size();
// 计算需要擦除的扇区数量
size_t sectors_to_erase = (content_length + SECTOR_SIZE - 1) / SECTOR_SIZE; // 向上取整
size_t total_erase_size = sectors_to_erase * SECTOR_SIZE;
ESP_LOGI(TAG, "Sector size: %u, content length: %u, sectors to erase: %u, total erase size: %u",
SECTOR_SIZE, content_length, sectors_to_erase, total_erase_size);
// 写入新的资源文件到分区,一边erase一边写入
char buffer[512];
size_t total_written = 0;
size_t recent_written = 0;
size_t current_sector = 0;
auto last_calc_time = esp_timer_get_time();
while (true) {
int ret = http->Read(buffer, sizeof(buffer));
if (ret < 0) {
ESP_LOGE(TAG, "Failed to read HTTP data: %s", esp_err_to_name(ret));
return false;
}
if (ret == 0) {
break;
}
// 检查是否需要擦除新的扇区
size_t write_end_offset = total_written + ret;
size_t needed_sectors = (write_end_offset + SECTOR_SIZE - 1) / SECTOR_SIZE;
// 擦除需要的新扇区
while (current_sector < needed_sectors) {
size_t sector_start = current_sector * SECTOR_SIZE;
size_t sector_end = (current_sector + 1) * SECTOR_SIZE;
// 确保擦除范围不超过分区大小
if (sector_end > partition_->size) {
ESP_LOGE(TAG, "Sector end (%u) exceeds partition size (%lu)", sector_end, partition_->size);
return false;
}
ESP_LOGD(TAG, "Erasing sector %u (offset: %u, size: %u)", current_sector, sector_start, SECTOR_SIZE);
esp_err_t err = esp_partition_erase_range(partition_, sector_start, SECTOR_SIZE);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to erase sector %u at offset %u: %s", current_sector, sector_start, esp_err_to_name(err));
return false;
}
current_sector++;
}
// 写入数据到分区
esp_err_t err = esp_partition_write(partition_, total_written, buffer, ret);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to write to assets partition at offset %u: %s", total_written, esp_err_to_name(err));
return false;
}
total_written += ret;
recent_written += ret;
// 计算进度和速度
if (esp_timer_get_time() - last_calc_time >= 1000000 || total_written == content_length || ret == 0) {
size_t progress = total_written * 100 / content_length;
size_t speed = recent_written; // 每秒的字节数
ESP_LOGI(TAG, "Progress: %u%% (%u/%u), Speed: %u B/s, Sectors erased: %u",
progress, total_written, content_length, speed, current_sector);
if (progress_callback) {
progress_callback(progress, speed);
}
last_calc_time = esp_timer_get_time();
recent_written = 0; // 重置最近写入的字节数
}
}
http->Close();
if (total_written != content_length) {
ESP_LOGE(TAG, "Downloaded size (%u) does not match expected size (%u)", total_written, content_length);
return false;
}
ESP_LOGI(TAG, "Assets download completed, total written: %u bytes, total sectors erased: %u",
total_written, current_sector);
// 重新初始化资源分区
if (!InitializePartition()) {
ESP_LOGE(TAG, "Failed to re-initialize assets partition");
return false;
}
return true;
}
bool Assets::GetAssetData(const std::string& name, void*& ptr, size_t& size) {
auto asset = assets_.find(name);
if (asset == assets_.end()) {
return false;
}
auto data = (const char*)(mmap_root_ + asset->second.offset);
if (data[0] != 'Z' || data[1] != 'Z') {
ESP_LOGE(TAG, "The asset %s is not valid with magic %02x%02x", name.c_str(), data[0], data[1]);
return false;
}
ptr = static_cast<void*>(const_cast<char*>(data + 2));
size = asset->second.size;
return true;
}
+52
View File
@@ -0,0 +1,52 @@
#ifndef ASSETS_H
#define ASSETS_H
#include <map>
#include <string>
#include <functional>
#include <cJSON.h>
#include <esp_partition.h>
#include <model_path.h>
struct Asset {
size_t size;
size_t offset;
};
class Assets {
public:
static Assets& GetInstance() {
static Assets instance;
return instance;
}
~Assets();
bool Download(std::string url, std::function<void(int progress, size_t speed)> progress_callback);
bool Apply();
bool GetAssetData(const std::string& name, void*& ptr, size_t& size);
inline bool partition_valid() const { return partition_valid_; }
inline bool checksum_valid() const { return checksum_valid_; }
inline std::string default_assets_url() const { return default_assets_url_; }
private:
Assets();
Assets(const Assets&) = delete;
Assets& operator=(const Assets&) = delete;
bool InitializePartition();
uint32_t CalculateChecksum(const char* data, uint32_t length);
const esp_partition_t* partition_ = nullptr;
esp_partition_mmap_handle_t mmap_handle_ = 0;
const char* mmap_root_ = nullptr;
bool partition_valid_ = false;
bool checksum_valid_ = false;
std::string default_assets_url_;
srmodel_list_t* models_list_ = nullptr;
std::map<std::string, Asset> assets_;
};
#endif
+52 -47
View File
@@ -1,63 +1,68 @@
// Auto-generated language config // Auto-generated language config
// Language: zh-CN with en-US fallback // Language: en-US with en-US fallback
#pragma once #pragma once
#include <string_view> #include <string_view>
#ifndef zh_cn #ifndef en_us
#define zh_cn // 預設語言 #define en_us // 預設語言
#endif #endif
namespace Lang { namespace Lang {
// 语言元数据 // 语言元数据
constexpr const char* CODE = "zh-CN"; constexpr const char* CODE = "en-US";
// 字符串资源 (en-US as fallback for missing keys) // 字符串资源 (en-US as fallback for missing keys)
namespace Strings { namespace Strings {
constexpr const char* ACCESS_VIA_BROWSER = ",浏览器访问 "; constexpr const char* ACCESS_VIA_BROWSER = " Config URL: ";
constexpr const char* ACTIVATION = "激活设备"; constexpr const char* ACTIVATION = "Activation";
constexpr const char* BATTERY_CHARGING = "正在充电"; constexpr const char* BATTERY_CHARGING = "Charging";
constexpr const char* BATTERY_FULL = "电量已满"; constexpr const char* BATTERY_FULL = "Battery full";
constexpr const char* BATTERY_LOW = "电量不足"; constexpr const char* BATTERY_LOW = "Low battery";
constexpr const char* BATTERY_NEED_CHARGE = "电量低,请充电"; constexpr const char* BATTERY_NEED_CHARGE = "Low battery, please charge";
constexpr const char* CHECKING_NEW_VERSION = "检查新版本..."; constexpr const char* CHECKING_NEW_VERSION = "Checking for new version...";
constexpr const char* CHECK_NEW_VERSION_FAILED = "检查新版本失败,将在 %d 秒后重试:%s"; constexpr const char* CHECK_NEW_VERSION_FAILED = "Check for new version failed, will retry in %d seconds: %s";
constexpr const char* CONNECTED_TO = "已连接 "; constexpr const char* CONNECTED_TO = "Connected to ";
constexpr const char* CONNECTING = "连接中..."; constexpr const char* CONNECTING = "Connecting...";
constexpr const char* CONNECTION_SUCCESSFUL = "Connection Successful"; constexpr const char* CONNECTION_SUCCESSFUL = "Connection Successful";
constexpr const char* CONNECT_TO = "连接 "; constexpr const char* CONNECT_TO = "Connect to ";
constexpr const char* CONNECT_TO_HOTSPOT = "手机连接热点 "; constexpr const char* CONNECT_TO_HOTSPOT = "Hotspot: ";
constexpr const char* DETECTING_MODULE = "检测模组..."; constexpr const char* DETECTING_MODULE = "Detecting module...";
constexpr const char* ENTERING_WIFI_CONFIG_MODE = "进入配网模式..."; constexpr const char* DOWNLOAD_ASSETS_FAILED = "Failed to download assets";
constexpr const char* ERROR = "错误"; constexpr const char* ENTERING_WIFI_CONFIG_MODE = "Entering Wi-Fi configuration mode...";
constexpr const char* INFO = "信息"; constexpr const char* ERROR = "Error";
constexpr const char* INITIALIZING = "正在初始化..."; constexpr const char* FOUND_NEW_ASSETS = "Found new assets: %s";
constexpr const char* LISTENING = "聆听中..."; constexpr const char* HELLO_MY_FRIEND = "Hello, my friend!";
constexpr const char* LOADING_PROTOCOL = "登录服务器..."; constexpr const char* INFO = "Information";
constexpr const char* MAX_VOLUME = "最大音量"; constexpr const char* INITIALIZING = "Initializing...";
constexpr const char* MUTED = "已静音"; constexpr const char* LISTENING = "Listening...";
constexpr const char* NEW_VERSION = "新版本 "; constexpr const char* LOADING_ASSETS = "Loading assets...";
constexpr const char* OTA_UPGRADE = "OTA 升级"; constexpr const char* LOADING_PROTOCOL = "Logging in...";
constexpr const char* PIN_ERROR = "请插入 SIM 卡"; constexpr const char* MAX_VOLUME = "Max volume";
constexpr const char* REGISTERING_NETWORK = "等待网络..."; constexpr const char* MUTED = "Muted";
constexpr const char* REG_ERROR = "无法接入网络,请检查流量卡状态"; constexpr const char* NEW_VERSION = "New version ";
constexpr const char* RTC_MODE_OFF = "AEC 关闭"; constexpr const char* OTA_UPGRADE = "OTA Upgrade";
constexpr const char* RTC_MODE_ON = "AEC 开启"; constexpr const char* PIN_ERROR = "Please insert SIM card";
constexpr const char* SCANNING_WIFI = "扫描 Wi-Fi..."; constexpr const char* PLEASE_WAIT = "Please wait...";
constexpr const char* SERVER_ERROR = "发送失败,请检查网络"; constexpr const char* REGISTERING_NETWORK = "Waiting for network...";
constexpr const char* SERVER_NOT_CONNECTED = "无法连接服务,请稍后再试"; constexpr const char* REG_ERROR = "Unable to access network, please check SIM card status";
constexpr const char* SERVER_NOT_FOUND = "正在寻找可用服务"; constexpr const char* RTC_MODE_OFF = "AEC Off";
constexpr const char* SERVER_TIMEOUT = "等待响应超时"; constexpr const char* RTC_MODE_ON = "AEC On";
constexpr const char* SPEAKING = "说话中..."; constexpr const char* SCANNING_WIFI = "Scanning Wi-Fi...";
constexpr const char* STANDBY = "待命"; constexpr const char* SERVER_ERROR = "Sending failed, please check the network";
constexpr const char* SWITCH_TO_4G_NETWORK = "切换到 4G..."; constexpr const char* SERVER_NOT_CONNECTED = "Unable to connect to service, please try again later";
constexpr const char* SWITCH_TO_WIFI_NETWORK = "切换到 Wi-Fi..."; constexpr const char* SERVER_NOT_FOUND = "Looking for available service";
constexpr const char* UPGRADE_FAILED = "升级失败"; constexpr const char* SERVER_TIMEOUT = "Waiting for response timeout";
constexpr const char* UPGRADING = "正在升级系统..."; constexpr const char* SPEAKING = "Speaking...";
constexpr const char* VERSION = "版本 "; constexpr const char* STANDBY = "Standby";
constexpr const char* VOLUME = "音量 "; constexpr const char* SWITCH_TO_4G_NETWORK = "Switching to 4G...";
constexpr const char* WARNING = "警告"; constexpr const char* SWITCH_TO_WIFI_NETWORK = "Switching to Wi-Fi...";
constexpr const char* WIFI_CONFIG_MODE = "配网模式"; constexpr const char* UPGRADE_FAILED = "Upgrade failed";
constexpr const char* UPGRADING = "System is upgrading...";
constexpr const char* VERSION = "Ver ";
constexpr const char* VOLUME = "Volume ";
constexpr const char* WARNING = "Warning";
constexpr const char* WIFI_CONFIG_MODE = "Wi-Fi Configuration Mode";
} }
// 音效资源 (en-US as fallback for missing audio files) // 音效资源 (en-US as fallback for missing audio files)
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"ar-SA" "type": "ar-SA"
}, },
"strings": { "strings": {
"WARNING":"تحذير", "WARNING": "تحذير",
"INFO":"معلومات", "INFO": "معلومات",
"ERROR":"خطأ", "ERROR": "خطأ",
"VERSION": "الإصدار ", "VERSION": "الإصدار ",
"LOADING_PROTOCOL":"الاتصال بالخادم...", "LOADING_PROTOCOL": "الاتصال بالخادم...",
"INITIALIZING":"التهيئة...", "INITIALIZING": "التهيئة...",
"PIN_ERROR":"يرجى إدخال بطاقة SIM", "PIN_ERROR": "يرجى إدخال بطاقة SIM",
"REG_ERROR":"لا يمكن الوصول إلى الشبكة، يرجى التحقق من حالة بطاقة البيانات", "REG_ERROR": "لا يمكن الوصول إلى الشبكة، يرجى التحقق من حالة بطاقة البيانات",
"DETECTING_MODULE":"اكتشاف الوحدة...", "DETECTING_MODULE": "اكتشاف الوحدة...",
"REGISTERING_NETWORK":"انتظار الشبكة...", "REGISTERING_NETWORK": "انتظار الشبكة...",
"CHECKING_NEW_VERSION":"فحص الإصدار الجديد...", "CHECKING_NEW_VERSION": "فحص الإصدار الجديد...",
"CHECK_NEW_VERSION_FAILED":"فشل فحص الإصدار الجديد، سيتم المحاولة خلال %d ثانية: %s", "CHECK_NEW_VERSION_FAILED": "فشل فحص الإصدار الجديد، سيتم المحاولة خلال %d ثانية: %s",
"SWITCH_TO_WIFI_NETWORK":"التبديل إلى Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "التبديل إلى Wi-Fi...",
"SWITCH_TO_4G_NETWORK":"التبديل إلى 4G...", "SWITCH_TO_4G_NETWORK": "التبديل إلى 4G...",
"STANDBY": "في الانتظار",
"STANDBY":"في الانتظار", "CONNECT_TO": "الاتصال بـ ",
"CONNECT_TO":"الاتصال بـ ", "CONNECTING": "جاري الاتصال...",
"CONNECTING":"جاري الاتصال...", "CONNECTED_TO": "متصل بـ ",
"CONNECTED_TO":"متصل بـ ", "LISTENING": "الاستماع...",
"SPEAKING": "التحدث...",
"LISTENING":"الاستماع...", "SERVER_NOT_FOUND": "البحث عن خدمة متاحة",
"SPEAKING":"التحدث...", "SERVER_NOT_CONNECTED": "لا يمكن الاتصال بالخدمة، يرجى المحاولة لاحقاً",
"SERVER_TIMEOUT": "انتهت مهلة الاستجابة",
"SERVER_NOT_FOUND":"البحث عن خدمة متاحة", "SERVER_ERROR": "فشل الإرسال، يرجى التحقق من الشبكة",
"SERVER_NOT_CONNECTED":ا يمكن الاتصال بالخدمة، يرجى المحاولة لاحقاً", "CONNECT_TO_HOTSPOT": "اتصل الهاتف بنقطة الاتصال ",
"SERVER_TIMEOUT":"انتهت مهلة الاستجابة", "ACCESS_VIA_BROWSER": "،الوصول عبر المتصفح ",
"SERVER_ERROR":"فشل الإرسال، يرجى التحقق من الشبكة", "WIFI_CONFIG_MODE": "وضع تكوين الشبكة",
"ENTERING_WIFI_CONFIG_MODE": "الدخول في وضع تكوين الشبكة...",
"CONNECT_TO_HOTSPOT":"اتصل الهاتف بنقطة الاتصال ", "SCANNING_WIFI": "فحص Wi-Fi...",
"ACCESS_VIA_BROWSER":"،الوصول عبر المتصفح ",
"WIFI_CONFIG_MODE":"وضع تكوين الشبكة",
"ENTERING_WIFI_CONFIG_MODE":"الدخول في وضع تكوين الشبكة...",
"SCANNING_WIFI":"فحص Wi-Fi...",
"NEW_VERSION": "إصدار جديد ", "NEW_VERSION": "إصدار جديد ",
"OTA_UPGRADE":"تحديث OTA", "OTA_UPGRADE": "تحديث OTA",
"UPGRADING":"تحديث النظام...", "UPGRADING": "تحديث النظام...",
"UPGRADE_FAILED":"فشل التحديث", "UPGRADE_FAILED": "فشل التحديث",
"ACTIVATION":"تفعيل الجهاز", "ACTIVATION": "تفعيل الجهاز",
"BATTERY_LOW": "البطارية منخفضة",
"BATTERY_LOW":"البطارية منخفضة", "BATTERY_CHARGING": "جاري الشحن",
"BATTERY_CHARGING":"جاري الشحن", "BATTERY_FULL": "البطارية ممتلئة",
"BATTERY_FULL":"البطارية ممتلئة", "BATTERY_NEED_CHARGE": "البطارية منخفضة، يرجى الشحن",
"BATTERY_NEED_CHARGE":"البطارية منخفضة، يرجى الشحن", "VOLUME": "الصوت ",
"MUTED": "صامت",
"VOLUME":"الصوت ", "MAX_VOLUME": "أقصى صوت",
"MUTED":"صامت", "RTC_MODE_OFF": "AEC مُوقف",
"MAX_VOLUME":"أقصى صوت", "RTC_MODE_ON": "AEC مُشغل",
"DOWNLOAD_ASSETS_FAILED": "فشل في تنزيل الموارد",
"RTC_MODE_OFF":"AEC مُوقف", "LOADING_ASSETS": "جاري تحميل الموارد...",
"RTC_MODE_ON":"AEC مُشغل" "PLEASE_WAIT": "يرجى الانتظار...",
"FOUND_NEW_ASSETS": "تم العثور على موارد جديدة: %s",
"HELLO_MY_FRIEND": "مرحباً، صديقي!"
} }
} }
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"cs-CZ" "type": "cs-CZ"
}, },
"strings": { "strings": {
"WARNING":"Varování", "WARNING": "Varování",
"INFO":"Informace", "INFO": "Informace",
"ERROR":"Chyba", "ERROR": "Chyba",
"VERSION": "Verze ", "VERSION": "Verze ",
"LOADING_PROTOCOL":"Připojování k serveru...", "LOADING_PROTOCOL": "Připojování k serveru...",
"INITIALIZING":"Inicializace...", "INITIALIZING": "Inicializace...",
"PIN_ERROR":"Prosím vložte SIM kartu", "PIN_ERROR": "Prosím vložte SIM kartu",
"REG_ERROR":"Nelze se připojit k síti, zkontrolujte stav datové karty", "REG_ERROR": "Nelze se připojit k síti, zkontrolujte stav datové karty",
"DETECTING_MODULE":"Detekce modulu...", "DETECTING_MODULE": "Detekce modulu...",
"REGISTERING_NETWORK":"Čekání na síť...", "REGISTERING_NETWORK": "Čekání na síť...",
"CHECKING_NEW_VERSION":"Kontrola nové verze...", "CHECKING_NEW_VERSION": "Kontrola nové verze...",
"CHECK_NEW_VERSION_FAILED":"Kontrola nové verze selhala, opakování za %d sekund: %s", "CHECK_NEW_VERSION_FAILED": "Kontrola nové verze selhala, opakování za %d sekund: %s",
"SWITCH_TO_WIFI_NETWORK":"Přepínání na Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "Přepínání na Wi-Fi...",
"SWITCH_TO_4G_NETWORK":"Přepínání na 4G...", "SWITCH_TO_4G_NETWORK": "Přepínání na 4G...",
"STANDBY": "Pohotovost",
"STANDBY":"Pohotovost", "CONNECT_TO": "Připojit k ",
"CONNECT_TO":"Připojit k ", "CONNECTING": "Připojování...",
"CONNECTING":"Připojování...", "CONNECTED_TO": "Připojeno k ",
"CONNECTED_TO":"Připojeno k ", "LISTENING": "Naslouchání...",
"SPEAKING": "Mluvení...",
"LISTENING":"Naslouchání...", "SERVER_NOT_FOUND": "Hledání dostupné služby",
"SPEAKING":"Mluvení...", "SERVER_NOT_CONNECTED": "Nelze se připojit ke službě, zkuste to později",
"SERVER_TIMEOUT": "Čas odpovědi vypršel",
"SERVER_NOT_FOUND":"Hledání dostupné služby", "SERVER_ERROR": "Odeslání selhalo, zkontrolujte síť",
"SERVER_NOT_CONNECTED":"Nelze se připojit ke službě, zkuste to později", "CONNECT_TO_HOTSPOT": "Připojte telefon k hotspotu ",
"SERVER_TIMEOUT":"Čas odpovědi vypršel", "ACCESS_VIA_BROWSER": "přístup přes prohlížeč ",
"SERVER_ERROR":"Odeslání selhalo, zkontrolujte síť", "WIFI_CONFIG_MODE": "Režim konfigurace sí",
"ENTERING_WIFI_CONFIG_MODE": "Vstup do režimu konfigurace sítě...",
"CONNECT_TO_HOTSPOT":"Připojte telefon k hotspotu ", "SCANNING_WIFI": "Skenování Wi-Fi...",
"ACCESS_VIA_BROWSER":"přístup přes prohlížeč ",
"WIFI_CONFIG_MODE":"Režim konfigurace sítě",
"ENTERING_WIFI_CONFIG_MODE":"Vstup do režimu konfigurace sítě...",
"SCANNING_WIFI":"Skenování Wi-Fi...",
"NEW_VERSION": "Nová verze ", "NEW_VERSION": "Nová verze ",
"OTA_UPGRADE":"OTA upgrade", "OTA_UPGRADE": "OTA upgrade",
"UPGRADING":"Aktualizace systému...", "UPGRADING": "Aktualizace systému...",
"UPGRADE_FAILED":"Upgrade selhal", "UPGRADE_FAILED": "Upgrade selhal",
"ACTIVATION":"Aktivace zařízení", "ACTIVATION": "Aktivace zařízení",
"BATTERY_LOW": "Slabá baterie",
"BATTERY_LOW":"Slabá baterie", "BATTERY_CHARGING": "Nabíjení",
"BATTERY_CHARGING":"Nabíjení", "BATTERY_FULL": "Baterie plná",
"BATTERY_FULL":"Baterie plná", "BATTERY_NEED_CHARGE": "Slabá baterie, prosím nabijte",
"BATTERY_NEED_CHARGE":"Slabá baterie, prosím nabijte", "VOLUME": "Hlasitost ",
"MUTED": "Ztlumeno",
"VOLUME":"Hlasitost ", "MAX_VOLUME": "Maximální hlasitost",
"MUTED":"Ztlumeno", "RTC_MODE_OFF": "AEC vypnuto",
"MAX_VOLUME":"Maximální hlasitost", "RTC_MODE_ON": "AEC zapnuto",
"DOWNLOAD_ASSETS_FAILED": "Nepodařilo se stáhnout prostředky",
"RTC_MODE_OFF":"AEC vypnuto", "LOADING_ASSETS": "Načítání prostředků...",
"RTC_MODE_ON":"AEC zapnuto" "PLEASE_WAIT": "Prosím čekejte...",
"FOUND_NEW_ASSETS": "Nalezeny nové prostředky: %s",
"HELLO_MY_FRIEND": "Ahoj, můj příteli!"
} }
} }
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"de-DE" "type": "de-DE"
}, },
"strings": { "strings": {
"WARNING":"Warnung", "WARNING": "Warnung",
"INFO":"Information", "INFO": "Information",
"ERROR":"Fehler", "ERROR": "Fehler",
"VERSION": "Version ", "VERSION": "Version ",
"LOADING_PROTOCOL":"Verbindung zum Server...", "LOADING_PROTOCOL": "Verbindung zum Server...",
"INITIALIZING":"Initialisierung...", "INITIALIZING": "Initialisierung...",
"PIN_ERROR":"Bitte SIM-Karte einlegen", "PIN_ERROR": "Bitte SIM-Karte einlegen",
"REG_ERROR":"Netzwerkverbindung fehlgeschlagen, bitte Datenkartenstatus prüfen", "REG_ERROR": "Netzwerkverbindung fehlgeschlagen, bitte Datenkartenstatus prüfen",
"DETECTING_MODULE":"Modul erkennen...", "DETECTING_MODULE": "Modul erkennen...",
"REGISTERING_NETWORK":"Auf Netzwerk warten...", "REGISTERING_NETWORK": "Auf Netzwerk warten...",
"CHECKING_NEW_VERSION":"Neue Version prüfen...", "CHECKING_NEW_VERSION": "Neue Version prüfen...",
"CHECK_NEW_VERSION_FAILED":"Neue Version prüfen fehlgeschlagen, Wiederholung in %d Sekunden: %s", "CHECK_NEW_VERSION_FAILED": "Neue Version prüfen fehlgeschlagen, Wiederholung in %d Sekunden: %s",
"SWITCH_TO_WIFI_NETWORK":"Zu Wi-Fi wechseln...", "SWITCH_TO_WIFI_NETWORK": "Zu Wi-Fi wechseln...",
"SWITCH_TO_4G_NETWORK":"Zu 4G wechseln...", "SWITCH_TO_4G_NETWORK": "Zu 4G wechseln...",
"STANDBY": "Bereitschaft",
"STANDBY":"Bereitschaft", "CONNECT_TO": "Verbinden zu ",
"CONNECT_TO":"Verbinden zu ", "CONNECTING": "Verbindung wird hergestellt...",
"CONNECTING":"Verbindung wird hergestellt...", "CONNECTED_TO": "Verbunden mit ",
"CONNECTED_TO":"Verbunden mit ", "LISTENING": "Zuhören...",
"SPEAKING": "Sprechen...",
"LISTENING":"Zuhören...", "SERVER_NOT_FOUND": "Verfügbaren Service suchen",
"SPEAKING":"Sprechen...", "SERVER_NOT_CONNECTED": "Service-Verbindung fehlgeschlagen, bitte später versuchen",
"SERVER_TIMEOUT": "Antwort-Timeout",
"SERVER_NOT_FOUND":"Verfügbaren Service suchen", "SERVER_ERROR": "Senden fehlgeschlagen, bitte Netzwerk prüfen",
"SERVER_NOT_CONNECTED":"Service-Verbindung fehlgeschlagen, bitte später versuchen", "CONNECT_TO_HOTSPOT": "Handy mit Hotspot verbinden ",
"SERVER_TIMEOUT":"Antwort-Timeout", "ACCESS_VIA_BROWSER": "Browser öffnen ",
"SERVER_ERROR":"Senden fehlgeschlagen, bitte Netzwerk prüfen", "WIFI_CONFIG_MODE": "Netzwerkkonfigurationsmodus",
"ENTERING_WIFI_CONFIG_MODE": "Netzwerkkonfigurationsmodus eingeben...",
"CONNECT_TO_HOTSPOT":"Handy mit Hotspot verbinden ", "SCANNING_WIFI": "Wi-Fi scannen...",
"ACCESS_VIA_BROWSER":"Browser öffnen ",
"WIFI_CONFIG_MODE":"Netzwerkkonfigurationsmodus",
"ENTERING_WIFI_CONFIG_MODE":"Netzwerkkonfigurationsmodus eingeben...",
"SCANNING_WIFI":"Wi-Fi scannen...",
"NEW_VERSION": "Neue Version ", "NEW_VERSION": "Neue Version ",
"OTA_UPGRADE":"OTA-Upgrade", "OTA_UPGRADE": "OTA-Upgrade",
"UPGRADING":"System wird aktualisiert...", "UPGRADING": "System wird aktualisiert...",
"UPGRADE_FAILED":"Upgrade fehlgeschlagen", "UPGRADE_FAILED": "Upgrade fehlgeschlagen",
"ACTIVATION":"Gerät aktivieren", "ACTIVATION": "Gerät aktivieren",
"BATTERY_LOW": "Niedriger Batteriestand",
"BATTERY_LOW":"Niedriger Batteriestand", "BATTERY_CHARGING": "Wird geladen",
"BATTERY_CHARGING":"Wird geladen", "BATTERY_FULL": "Batterie voll",
"BATTERY_FULL":"Batterie voll", "BATTERY_NEED_CHARGE": "Niedriger Batteriestand, bitte aufladen",
"BATTERY_NEED_CHARGE":"Niedriger Batteriestand, bitte aufladen", "VOLUME": "Lautstärke ",
"MUTED": "Stummgeschaltet",
"VOLUME":"Lautstärke ", "MAX_VOLUME": "Maximale Lautstärke",
"MUTED":"Stummgeschaltet", "RTC_MODE_OFF": "AEC aus",
"MAX_VOLUME":"Maximale Lautstärke", "RTC_MODE_ON": "AEC ein",
"DOWNLOAD_ASSETS_FAILED": "Fehler beim Herunterladen der Ressourcen",
"RTC_MODE_OFF":"AEC aus", "LOADING_ASSETS": "Ressourcen werden geladen...",
"RTC_MODE_ON":"AEC ein" "PLEASE_WAIT": "Bitte warten...",
"FOUND_NEW_ASSETS": "Neue Ressourcen gefunden: %s",
"HELLO_MY_FRIEND": "Hallo, mein Freund!"
} }
} }
+6 -9
View File
@@ -17,43 +17,40 @@
"CHECK_NEW_VERSION_FAILED": "Check for new version failed, will retry in %d seconds: %s", "CHECK_NEW_VERSION_FAILED": "Check for new version failed, will retry in %d seconds: %s",
"SWITCH_TO_WIFI_NETWORK": "Switching to Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "Switching to Wi-Fi...",
"SWITCH_TO_4G_NETWORK": "Switching to 4G...", "SWITCH_TO_4G_NETWORK": "Switching to 4G...",
"STANDBY": "Standby", "STANDBY": "Standby",
"CONNECT_TO": "Connect to ", "CONNECT_TO": "Connect to ",
"CONNECTING": "Connecting...", "CONNECTING": "Connecting...",
"CONNECTION_SUCCESSFUL": "Connection Successful", "CONNECTION_SUCCESSFUL": "Connection Successful",
"CONNECTED_TO": "Connected to ", "CONNECTED_TO": "Connected to ",
"LISTENING": "Listening...", "LISTENING": "Listening...",
"SPEAKING": "Speaking...", "SPEAKING": "Speaking...",
"SERVER_NOT_FOUND": "Looking for available service", "SERVER_NOT_FOUND": "Looking for available service",
"SERVER_NOT_CONNECTED": "Unable to connect to service, please try again later", "SERVER_NOT_CONNECTED": "Unable to connect to service, please try again later",
"SERVER_TIMEOUT": "Waiting for response timeout", "SERVER_TIMEOUT": "Waiting for response timeout",
"SERVER_ERROR": "Sending failed, please check the network", "SERVER_ERROR": "Sending failed, please check the network",
"CONNECT_TO_HOTSPOT": "Hotspot: ", "CONNECT_TO_HOTSPOT": "Hotspot: ",
"ACCESS_VIA_BROWSER": " Config URL: ", "ACCESS_VIA_BROWSER": " Config URL: ",
"WIFI_CONFIG_MODE": "Wi-Fi Configuration Mode", "WIFI_CONFIG_MODE": "Wi-Fi Configuration Mode",
"ENTERING_WIFI_CONFIG_MODE": "Entering Wi-Fi configuration mode...", "ENTERING_WIFI_CONFIG_MODE": "Entering Wi-Fi configuration mode...",
"SCANNING_WIFI": "Scanning Wi-Fi...", "SCANNING_WIFI": "Scanning Wi-Fi...",
"NEW_VERSION": "New version ", "NEW_VERSION": "New version ",
"OTA_UPGRADE": "OTA Upgrade", "OTA_UPGRADE": "OTA Upgrade",
"UPGRADING": "System is upgrading...", "UPGRADING": "System is upgrading...",
"UPGRADE_FAILED": "Upgrade failed", "UPGRADE_FAILED": "Upgrade failed",
"ACTIVATION": "Activation", "ACTIVATION": "Activation",
"BATTERY_LOW": "Low battery", "BATTERY_LOW": "Low battery",
"BATTERY_CHARGING": "Charging", "BATTERY_CHARGING": "Charging",
"BATTERY_FULL": "Battery full", "BATTERY_FULL": "Battery full",
"BATTERY_NEED_CHARGE": "Low battery, please charge", "BATTERY_NEED_CHARGE": "Low battery, please charge",
"VOLUME": "Volume ", "VOLUME": "Volume ",
"MUTED": "Muted", "MUTED": "Muted",
"MAX_VOLUME": "Max volume", "MAX_VOLUME": "Max volume",
"RTC_MODE_OFF": "AEC Off", "RTC_MODE_OFF": "AEC Off",
"RTC_MODE_ON": "AEC On" "RTC_MODE_ON": "AEC On",
"PLEASE_WAIT": "Please wait...",
"FOUND_NEW_ASSETS": "Found new assets: %s",
"DOWNLOAD_ASSETS_FAILED": "Failed to download assets",
"LOADING_ASSETS": "Loading assets...",
"HELLO_MY_FRIEND": "Hello, my friend!"
} }
} }
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"es-ES" "type": "es-ES"
}, },
"strings": { "strings": {
"WARNING":"Advertencia", "WARNING": "Advertencia",
"INFO":"Información", "INFO": "Información",
"ERROR":"Error", "ERROR": "Error",
"VERSION": "Versión ", "VERSION": "Versión ",
"LOADING_PROTOCOL":"Conectando al servidor...", "LOADING_PROTOCOL": "Conectando al servidor...",
"INITIALIZING":"Inicializando...", "INITIALIZING": "Inicializando...",
"PIN_ERROR":"Por favor inserte la tarjeta SIM", "PIN_ERROR": "Por favor inserte la tarjeta SIM",
"REG_ERROR":"No se puede acceder a la red, verifique el estado de la tarjeta de datos", "REG_ERROR": "No se puede acceder a la red, verifique el estado de la tarjeta de datos",
"DETECTING_MODULE":"Detectando módulo...", "DETECTING_MODULE": "Detectando módulo...",
"REGISTERING_NETWORK":"Esperando red...", "REGISTERING_NETWORK": "Esperando red...",
"CHECKING_NEW_VERSION":"Verificando nueva versión...", "CHECKING_NEW_VERSION": "Verificando nueva versión...",
"CHECK_NEW_VERSION_FAILED":"Error al verificar nueva versión, reintentando en %d segundos: %s", "CHECK_NEW_VERSION_FAILED": "Error al verificar nueva versión, reintentando en %d segundos: %s",
"SWITCH_TO_WIFI_NETWORK":"Cambiando a Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "Cambiando a Wi-Fi...",
"SWITCH_TO_4G_NETWORK":"Cambiando a 4G...", "SWITCH_TO_4G_NETWORK": "Cambiando a 4G...",
"STANDBY": "En espera",
"STANDBY":"En espera", "CONNECT_TO": "Conectar a ",
"CONNECT_TO":"Conectar a ", "CONNECTING": "Conectando...",
"CONNECTING":"Conectando...", "CONNECTED_TO": "Conectado a ",
"CONNECTED_TO":"Conectado a ", "LISTENING": "Escuchando...",
"SPEAKING": "Hablando...",
"LISTENING":"Escuchando...", "SERVER_NOT_FOUND": "Buscando servicio disponible",
"SPEAKING":"Hablando...", "SERVER_NOT_CONNECTED": "No se puede conectar al servicio, inténtelo más tarde",
"SERVER_TIMEOUT": "Tiempo de espera agotado",
"SERVER_NOT_FOUND":"Buscando servicio disponible", "SERVER_ERROR": "Error de envío, verifique la red",
"SERVER_NOT_CONNECTED":"No se puede conectar al servicio, inténtelo más tarde", "CONNECT_TO_HOTSPOT": "Conectar teléfono al punto de acceso ",
"SERVER_TIMEOUT":"Tiempo de espera agotado", "ACCESS_VIA_BROWSER": "acceder mediante navegador ",
"SERVER_ERROR":"Error de envío, verifique la red", "WIFI_CONFIG_MODE": "Modo configuración de red",
"ENTERING_WIFI_CONFIG_MODE": "Entrando en modo configuración de red...",
"CONNECT_TO_HOTSPOT":"Conectar teléfono al punto de acceso ", "SCANNING_WIFI": "Escaneando Wi-Fi...",
"ACCESS_VIA_BROWSER":"acceder mediante navegador ",
"WIFI_CONFIG_MODE":"Modo configuración de red",
"ENTERING_WIFI_CONFIG_MODE":"Entrando en modo configuración de red...",
"SCANNING_WIFI":"Escaneando Wi-Fi...",
"NEW_VERSION": "Nueva versión ", "NEW_VERSION": "Nueva versión ",
"OTA_UPGRADE":"Actualización OTA", "OTA_UPGRADE": "Actualización OTA",
"UPGRADING":"Actualizando sistema...", "UPGRADING": "Actualizando sistema...",
"UPGRADE_FAILED":"Actualización fallida", "UPGRADE_FAILED": "Actualización fallida",
"ACTIVATION":"Activación del dispositivo", "ACTIVATION": "Activación del dispositivo",
"BATTERY_LOW": "Batería baja",
"BATTERY_LOW":"Batería baja", "BATTERY_CHARGING": "Cargando",
"BATTERY_CHARGING":"Cargando", "BATTERY_FULL": "Batería llena",
"BATTERY_FULL":"Batería llena", "BATTERY_NEED_CHARGE": "Batería baja, por favor cargar",
"BATTERY_NEED_CHARGE":"Batería baja, por favor cargar", "VOLUME": "Volumen ",
"MUTED": "Silenciado",
"VOLUME":"Volumen ", "MAX_VOLUME": "Volumen máximo",
"MUTED":"Silenciado", "RTC_MODE_OFF": "AEC desactivado",
"MAX_VOLUME":"Volumen máximo", "RTC_MODE_ON": "AEC activado",
"DOWNLOAD_ASSETS_FAILED": "Error al descargar recursos",
"RTC_MODE_OFF":"AEC desactivado", "LOADING_ASSETS": "Cargando recursos...",
"RTC_MODE_ON":"AEC activado" "PLEASE_WAIT": "Por favor espere...",
"FOUND_NEW_ASSETS": "Encontrados nuevos recursos: %s",
"HELLO_MY_FRIEND": "¡Hola, mi amigo!"
} }
} }
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"fi-FI" "type": "fi-FI"
}, },
"strings": { "strings": {
"WARNING":"Varoitus", "WARNING": "Varoitus",
"INFO":"Tieto", "INFO": "Tieto",
"ERROR":"Virhe", "ERROR": "Virhe",
"VERSION": "Versio ", "VERSION": "Versio ",
"LOADING_PROTOCOL":"Yhdistetään palvelimeen...", "LOADING_PROTOCOL": "Yhdistetään palvelimeen...",
"INITIALIZING":"Alustetaan...", "INITIALIZING": "Alustetaan...",
"PIN_ERROR":"Ole hyvä ja aseta SIM-kortti", "PIN_ERROR": "Ole hyvä ja aseta SIM-kortti",
"REG_ERROR":"Ei voi muodostaa yhteyttä verkkoon, tarkista datakortin tila", "REG_ERROR": "Ei voi muodostaa yhteyttä verkkoon, tarkista datakortin tila",
"DETECTING_MODULE":"Tunnistetaan moduuli...", "DETECTING_MODULE": "Tunnistetaan moduuli...",
"REGISTERING_NETWORK":"Odotetaan verkkoa...", "REGISTERING_NETWORK": "Odotetaan verkkoa...",
"CHECKING_NEW_VERSION":"Tarkistetaan uutta versiota...", "CHECKING_NEW_VERSION": "Tarkistetaan uutta versiota...",
"CHECK_NEW_VERSION_FAILED":"Uuden version tarkistus epäonnistui, yritetään uudelleen %d sekunnin kuluttua: %s", "CHECK_NEW_VERSION_FAILED": "Uuden version tarkistus epäonnistui, yritetään uudelleen %d sekunnin kuluttua: %s",
"SWITCH_TO_WIFI_NETWORK":"Vaihdetaan Wi-Fi:hin...", "SWITCH_TO_WIFI_NETWORK": "Vaihdetaan Wi-Fi:hin...",
"SWITCH_TO_4G_NETWORK":"Vaihdetaan 4G:hen...", "SWITCH_TO_4G_NETWORK": "Vaihdetaan 4G:hen...",
"STANDBY": "Valmiustila",
"STANDBY":"Valmiustila", "CONNECT_TO": "Yhdistä ",
"CONNECT_TO":"Yhdistä ", "CONNECTING": "Yhdistetään...",
"CONNECTING":"Yhdistetään...", "CONNECTED_TO": "Yhdistetty ",
"CONNECTED_TO":"Yhdistetty ", "LISTENING": "Kuunnellaan...",
"SPEAKING": "Puhutaan...",
"LISTENING":"Kuunnellaan...", "SERVER_NOT_FOUND": "Etsitään käytettävissä olevaa palvelua",
"SPEAKING":"Puhutaan...", "SERVER_NOT_CONNECTED": "Ei voi yhdistää palveluun, yritä myöhemmin",
"SERVER_TIMEOUT": "Vastauksen aikakatkaisu",
"SERVER_NOT_FOUND":"Etsitään käytettävissä olevaa palvelua", "SERVER_ERROR": "Lähetys epäonnistui, tarkista verkko",
"SERVER_NOT_CONNECTED":"Ei voi yhdistää palveluun, yritä myöhemmin", "CONNECT_TO_HOTSPOT": "Yhdistä puhelin hotspottiin ",
"SERVER_TIMEOUT":"Vastauksen aikakatkaisu", "ACCESS_VIA_BROWSER": "pääsy selaimen kautta ",
"SERVER_ERROR":"Lähetys epäonnistui, tarkista verkko", "WIFI_CONFIG_MODE": "Verkon konfigurointitila",
"ENTERING_WIFI_CONFIG_MODE": "Siirrytään verkon konfigurointitilaan...",
"CONNECT_TO_HOTSPOT":"Yhdistä puhelin hotspottiin ", "SCANNING_WIFI": "Skannataan Wi-Fi...",
"ACCESS_VIA_BROWSER":"pääsy selaimen kautta ",
"WIFI_CONFIG_MODE":"Verkon konfigurointitila",
"ENTERING_WIFI_CONFIG_MODE":"Siirrytään verkon konfigurointitilaan...",
"SCANNING_WIFI":"Skannataan Wi-Fi...",
"NEW_VERSION": "Uusi versio ", "NEW_VERSION": "Uusi versio ",
"OTA_UPGRADE":"OTA-päivitys", "OTA_UPGRADE": "OTA-päivitys",
"UPGRADING":"Päivitetään järjestelmää...", "UPGRADING": "Päivitetään järjestelmää...",
"UPGRADE_FAILED":"Päivitys epäonnistui", "UPGRADE_FAILED": "Päivitys epäonnistui",
"ACTIVATION":"Laitteen aktivointi", "ACTIVATION": "Laitteen aktivointi",
"BATTERY_LOW": "Akku vähissä",
"BATTERY_LOW":"Akku vähissä", "BATTERY_CHARGING": "Ladataan",
"BATTERY_CHARGING":"Ladataan", "BATTERY_FULL": "Akku täynnä",
"BATTERY_FULL":"Akku täynnä", "BATTERY_NEED_CHARGE": "Akku vähissä, ole hyvä ja lataa",
"BATTERY_NEED_CHARGE":"Akku vähissä, ole hyvä ja lataa", "VOLUME": "Äänenvoimakkuus ",
"MUTED": "Mykistetty",
"VOLUME":"Äänenvoimakkuus ", "MAX_VOLUME": "Maksimi äänenvoimakkuus",
"MUTED":"Mykistetty", "RTC_MODE_OFF": "AEC pois päältä",
"MAX_VOLUME":"Maksimi äänenvoimakkuus", "RTC_MODE_ON": "AEC päällä",
"DOWNLOAD_ASSETS_FAILED": "Resurssien lataaminen epäonnistui",
"RTC_MODE_OFF":"AEC pois päältä", "LOADING_ASSETS": "Ladataan resursseja...",
"RTC_MODE_ON":"AEC päällä" "PLEASE_WAIT": "Odota hetki...",
"FOUND_NEW_ASSETS": "Löydetty uusia resursseja: %s",
"HELLO_MY_FRIEND": "Hei, ystäväni!"
} }
} }
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"fr-FR" "type": "fr-FR"
}, },
"strings": { "strings": {
"WARNING":"Avertissement", "WARNING": "Avertissement",
"INFO":"Information", "INFO": "Information",
"ERROR":"Erreur", "ERROR": "Erreur",
"VERSION": "Version ", "VERSION": "Version ",
"LOADING_PROTOCOL":"Connexion au serveur...", "LOADING_PROTOCOL": "Connexion au serveur...",
"INITIALIZING":"Initialisation...", "INITIALIZING": "Initialisation...",
"PIN_ERROR":"Veuillez insérer la carte SIM", "PIN_ERROR": "Veuillez insérer la carte SIM",
"REG_ERROR":"Impossible d'accéder au réseau, veuillez vérifier l'état de la carte de données", "REG_ERROR": "Impossible d'accéder au réseau, veuillez vérifier l'état de la carte de données",
"DETECTING_MODULE":"Détection du module...", "DETECTING_MODULE": "Détection du module...",
"REGISTERING_NETWORK":"En attente du réseau...", "REGISTERING_NETWORK": "En attente du réseau...",
"CHECKING_NEW_VERSION":"Vérification de nouvelle version...", "CHECKING_NEW_VERSION": "Vérification de nouvelle version...",
"CHECK_NEW_VERSION_FAILED":"Échec de vérification de nouvelle version, nouvelle tentative dans %d secondes : %s", "CHECK_NEW_VERSION_FAILED": "Échec de vérification de nouvelle version, nouvelle tentative dans %d secondes : %s",
"SWITCH_TO_WIFI_NETWORK":"Basculer vers Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "Basculer vers Wi-Fi...",
"SWITCH_TO_4G_NETWORK":"Basculer vers 4G...", "SWITCH_TO_4G_NETWORK": "Basculer vers 4G...",
"STANDBY": "En attente",
"STANDBY":"En attente", "CONNECT_TO": "Se connecter à ",
"CONNECT_TO":"Se connecter à ", "CONNECTING": "Connexion en cours...",
"CONNECTING":"Connexion en cours...", "CONNECTED_TO": "Connecté à ",
"CONNECTED_TO":"Connecté à ", "LISTENING": "Écoute...",
"SPEAKING": "Parole...",
"LISTENING":"Écoute...", "SERVER_NOT_FOUND": "Recherche d'un service disponible",
"SPEAKING":"Parole...", "SERVER_NOT_CONNECTED": "Impossible de se connecter au service, veuillez réessayer plus tard",
"SERVER_TIMEOUT": "Délai d'attente de réponse",
"SERVER_NOT_FOUND":"Recherche d'un service disponible", "SERVER_ERROR": "Échec d'envoi, veuillez vérifier le réseau",
"SERVER_NOT_CONNECTED":"Impossible de se connecter au service, veuillez réessayer plus tard", "CONNECT_TO_HOTSPOT": "Connecter le téléphone au point d'accès ",
"SERVER_TIMEOUT":"Délai d'attente de réponse", "ACCESS_VIA_BROWSER": "accéder via le navigateur ",
"SERVER_ERROR":"Échec d'envoi, veuillez vérifier le réseau", "WIFI_CONFIG_MODE": "Mode configuration réseau",
"ENTERING_WIFI_CONFIG_MODE": "Entrer en mode configuration réseau...",
"CONNECT_TO_HOTSPOT":"Connecter le téléphone au point d'accès ", "SCANNING_WIFI": "Scan Wi-Fi...",
"ACCESS_VIA_BROWSER":"accéder via le navigateur ",
"WIFI_CONFIG_MODE":"Mode configuration réseau",
"ENTERING_WIFI_CONFIG_MODE":"Entrer en mode configuration réseau...",
"SCANNING_WIFI":"Scan Wi-Fi...",
"NEW_VERSION": "Nouvelle version ", "NEW_VERSION": "Nouvelle version ",
"OTA_UPGRADE":"Mise à jour OTA", "OTA_UPGRADE": "Mise à jour OTA",
"UPGRADING":"Mise à jour du système...", "UPGRADING": "Mise à jour du système...",
"UPGRADE_FAILED":"Échec de mise à jour", "UPGRADE_FAILED": "Échec de mise à jour",
"ACTIVATION":"Activation de l'appareil", "ACTIVATION": "Activation de l'appareil",
"BATTERY_LOW": "Batterie faible",
"BATTERY_LOW":"Batterie faible", "BATTERY_CHARGING": "En charge",
"BATTERY_CHARGING":"En charge", "BATTERY_FULL": "Batterie pleine",
"BATTERY_FULL":"Batterie pleine", "BATTERY_NEED_CHARGE": "Batterie faible, veuillez charger",
"BATTERY_NEED_CHARGE":"Batterie faible, veuillez charger", "VOLUME": "Volume ",
"MUTED": "Muet",
"VOLUME":"Volume ", "MAX_VOLUME": "Volume maximum",
"MUTED":"Muet", "RTC_MODE_OFF": "AEC désactivé",
"MAX_VOLUME":"Volume maximum", "RTC_MODE_ON": "AEC activé",
"DOWNLOAD_ASSETS_FAILED": "Échec du téléchargement des ressources",
"RTC_MODE_OFF":"AEC désactivé", "LOADING_ASSETS": "Chargement des ressources...",
"RTC_MODE_ON":"AEC activé" "PLEASE_WAIT": "Veuillez patienter...",
"FOUND_NEW_ASSETS": "Nouvelles ressources trouvées: %s",
"HELLO_MY_FRIEND": "Bonjour, mon ami !"
} }
} }
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"hi-IN" "type": "hi-IN"
}, },
"strings": { "strings": {
"WARNING":"चेतावनी", "WARNING": "चेतावनी",
"INFO":"जानकारी", "INFO": "जानकारी",
"ERROR":"त्रुटि", "ERROR": "त्रुटि",
"VERSION": "संस्करण ", "VERSION": "संस्करण ",
"LOADING_PROTOCOL":"सर्वर से कनेक्ट हो रहे हैं...", "LOADING_PROTOCOL": "सर्वर से कनेक्ट हो रहे हैं...",
"INITIALIZING":"आरंभीकरण...", "INITIALIZING": "आरंभीकरण...",
"PIN_ERROR":"कृपया सिम कार्ड डालें", "PIN_ERROR": "कृपया सिम कार्ड डालें",
"REG_ERROR":"नेटवर्क तक पहुंच नहीं हो सकती, कृपया डेटा कार्ड स्थिति जांचें", "REG_ERROR": "नेटवर्क तक पहुंच नहीं हो सकती, कृपया डेटा कार्ड स्थिति जांचें",
"DETECTING_MODULE":"मॉड्यूल का पता लगाया जा रहा है...", "DETECTING_MODULE": "मॉड्यूल का पता लगाया जा रहा है...",
"REGISTERING_NETWORK":"नेटवर्क की प्रतीक्षा...", "REGISTERING_NETWORK": "नेटवर्क की प्रतीक्षा...",
"CHECKING_NEW_VERSION":"नया संस्करण जाँच रहे हैं...", "CHECKING_NEW_VERSION": "नया संस्करण जाँच रहे हैं...",
"CHECK_NEW_VERSION_FAILED":"नया संस्करण जाँचना असफल, %d सेकंड में पुनः प्रयास: %s", "CHECK_NEW_VERSION_FAILED": "नया संस्करण जाँचना असफल, %d सेकंड में पुनः प्रयास: %s",
"SWITCH_TO_WIFI_NETWORK":"Wi-Fi पर स्विच कर रहे हैं...", "SWITCH_TO_WIFI_NETWORK": "Wi-Fi पर स्विच कर रहे हैं...",
"SWITCH_TO_4G_NETWORK":"4G पर स्विच कर रहे हैं...", "SWITCH_TO_4G_NETWORK": "4G पर स्विच कर रहे हैं...",
"STANDBY": "स्टैंडबाय",
"STANDBY":"स्टैंडबाय", "CONNECT_TO": "कनेक्ट करें ",
"CONNECT_TO":"कनेक्ट करें ", "CONNECTING": "कनेक्ट हो रहे हैं...",
"CONNECTING":"कनेक्ट हो रहे हैं...", "CONNECTED_TO": "कनेक्ट हो गए ",
"CONNECTED_TO":"कनेक्ट हो गए ", "LISTENING": "सुन रहे हैं...",
"SPEAKING": "बोल रहे हैं...",
"LISTENING":"सुन रहे हैं...", "SERVER_NOT_FOUND": "उपलब्ध सेवा खोज रहे हैं",
"SPEAKING":"बोल रहे हैं...", "SERVER_NOT_CONNECTED": "सेवा से कनेक्ट नहीं हो सकते, कृपया बाद में कोशिश करें",
"SERVER_TIMEOUT": "प्रतिक्रिया का समय समाप्त",
"SERVER_NOT_FOUND":"उपलब्ध सेवा खोज रहे हैं", "SERVER_ERROR": "भेजना असफल, कृपया नेटवर्क जांचें",
"SERVER_NOT_CONNECTED":"सेवा से कनेक्ट नहीं हो सकते, कृपया बाद में कोशिश करें", "CONNECT_TO_HOTSPOT": "फोन को हॉटस्पॉट से कनेक्ट करें ",
"SERVER_TIMEOUT":"प्रतिक्रिया का समय समाप्त", "ACCESS_VIA_BROWSER": ",ब्राउज़र के माध्यम से पहुंचें ",
"SERVER_ERROR":"भेजना असफल, कृपया नेटवर्क जांचें", "WIFI_CONFIG_MODE": "नेटवर्क कॉन्फ़िगरेशन मोड",
"ENTERING_WIFI_CONFIG_MODE": "नेटवर्क कॉन्फ़िगरेशन मोड में प्रवेश...",
"CONNECT_TO_HOTSPOT":"फोन को हॉटस्पॉट से कनेक्ट करें ", "SCANNING_WIFI": "Wi-Fi स्कैन कर रहे हैं...",
"ACCESS_VIA_BROWSER":",ब्राउज़र के माध्यम से पहुंचें ",
"WIFI_CONFIG_MODE":"नेटवर्क कॉन्फ़िगरेशन मोड",
"ENTERING_WIFI_CONFIG_MODE":"नेटवर्क कॉन्फ़िगरेशन मोड में प्रवेश...",
"SCANNING_WIFI":"Wi-Fi स्कैन कर रहे हैं...",
"NEW_VERSION": "नया संस्करण ", "NEW_VERSION": "नया संस्करण ",
"OTA_UPGRADE":"OTA अपग्रेड", "OTA_UPGRADE": "OTA अपग्रेड",
"UPGRADING":"सिस्टम अपग्रेड हो रहा है...", "UPGRADING": "सिस्टम अपग्रेड हो रहा है...",
"UPGRADE_FAILED":"अपग्रेड असफल", "UPGRADE_FAILED": "अपग्रेड असफल",
"ACTIVATION":"डिवाइस सक्रियण", "ACTIVATION": "डिवाइस सक्रियण",
"BATTERY_LOW": "बैटरी कम",
"BATTERY_LOW":"बैटरी कम", "BATTERY_CHARGING": "चार्ज हो रही है",
"BATTERY_CHARGING":"चार्ज हो रही है", "BATTERY_FULL": "बैटरी फुल",
"BATTERY_FULL":"बैटरी फुल", "BATTERY_NEED_CHARGE": "बैटरी कम है, कृपया चार्ज करें",
"BATTERY_NEED_CHARGE":"बैटरी कम है, कृपया चार्ज करें", "VOLUME": "आवाज़ ",
"MUTED": "म्यूट",
"VOLUME":"आवाज़ ", "MAX_VOLUME": "अधिकतम आवाज़",
"MUTED":"म्यूट", "RTC_MODE_OFF": "AEC बंद",
"MAX_VOLUME":"अधिकतम आवाज़", "RTC_MODE_ON": "AEC चालू",
"DOWNLOAD_ASSETS_FAILED": "संसाधन डाउनलोड करने में विफल",
"RTC_MODE_OFF":"AEC बंद", "LOADING_ASSETS": "संसाधन लोड हो रहे हैं...",
"RTC_MODE_ON":"AEC चालू" "PLEASE_WAIT": "कृपया प्रतीक्षा करें...",
"FOUND_NEW_ASSETS": "नए संसाधन मिले: %s",
"HELLO_MY_FRIEND": "नमस्ते, मेरे दोस्त!"
} }
} }
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"id-ID" "type": "id-ID"
}, },
"strings": { "strings": {
"WARNING":"Peringatan", "WARNING": "Peringatan",
"INFO":"Informasi", "INFO": "Informasi",
"ERROR":"Kesalahan", "ERROR": "Kesalahan",
"VERSION": "Versi ", "VERSION": "Versi ",
"LOADING_PROTOCOL":"Menghubungkan ke server...", "LOADING_PROTOCOL": "Menghubungkan ke server...",
"INITIALIZING":"Menginisialisasi...", "INITIALIZING": "Menginisialisasi...",
"PIN_ERROR":"Silakan masukkan kartu SIM", "PIN_ERROR": "Silakan masukkan kartu SIM",
"REG_ERROR":"Tidak dapat mengakses jaringan, periksa status kartu data", "REG_ERROR": "Tidak dapat mengakses jaringan, periksa status kartu data",
"DETECTING_MODULE":"Mendeteksi modul...", "DETECTING_MODULE": "Mendeteksi modul...",
"REGISTERING_NETWORK":"Menunggu jaringan...", "REGISTERING_NETWORK": "Menunggu jaringan...",
"CHECKING_NEW_VERSION":"Memeriksa versi baru...", "CHECKING_NEW_VERSION": "Memeriksa versi baru...",
"CHECK_NEW_VERSION_FAILED":"Pemeriksaan versi baru gagal, mencoba lagi dalam %d detik: %s", "CHECK_NEW_VERSION_FAILED": "Pemeriksaan versi baru gagal, mencoba lagi dalam %d detik: %s",
"SWITCH_TO_WIFI_NETWORK":"Beralih ke Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "Beralih ke Wi-Fi...",
"SWITCH_TO_4G_NETWORK":"Beralih ke 4G...", "SWITCH_TO_4G_NETWORK": "Beralih ke 4G...",
"STANDBY": "Siaga",
"STANDBY":"Siaga", "CONNECT_TO": "Hubungkan ke ",
"CONNECT_TO":"Hubungkan ke ", "CONNECTING": "Menghubungkan...",
"CONNECTING":"Menghubungkan...", "CONNECTED_TO": "Terhubung ke ",
"CONNECTED_TO":"Terhubung ke ", "LISTENING": "Mendengarkan...",
"SPEAKING": "Berbicara...",
"LISTENING":"Mendengarkan...", "SERVER_NOT_FOUND": "Mencari layanan yang tersedia",
"SPEAKING":"Berbicara...", "SERVER_NOT_CONNECTED": "Tidak dapat terhubung ke layanan, coba lagi nanti",
"SERVER_TIMEOUT": "Waktu respons habis",
"SERVER_NOT_FOUND":"Mencari layanan yang tersedia", "SERVER_ERROR": "Pengiriman gagal, periksa jaringan",
"SERVER_NOT_CONNECTED":"Tidak dapat terhubung ke layanan, coba lagi nanti", "CONNECT_TO_HOTSPOT": "Hubungkan ponsel ke hotspot ",
"SERVER_TIMEOUT":"Waktu respons habis", "ACCESS_VIA_BROWSER": "akses melalui browser ",
"SERVER_ERROR":"Pengiriman gagal, periksa jaringan", "WIFI_CONFIG_MODE": "Mode konfigurasi jaringan",
"ENTERING_WIFI_CONFIG_MODE": "Memasuki mode konfigurasi jaringan...",
"CONNECT_TO_HOTSPOT":"Hubungkan ponsel ke hotspot ", "SCANNING_WIFI": "Memindai Wi-Fi...",
"ACCESS_VIA_BROWSER":"akses melalui browser ",
"WIFI_CONFIG_MODE":"Mode konfigurasi jaringan",
"ENTERING_WIFI_CONFIG_MODE":"Memasuki mode konfigurasi jaringan...",
"SCANNING_WIFI":"Memindai Wi-Fi...",
"NEW_VERSION": "Versi baru ", "NEW_VERSION": "Versi baru ",
"OTA_UPGRADE":"Pembaruan OTA", "OTA_UPGRADE": "Pembaruan OTA",
"UPGRADING":"Memperbarui sistem...", "UPGRADING": "Memperbarui sistem...",
"UPGRADE_FAILED":"Pembaruan gagal", "UPGRADE_FAILED": "Pembaruan gagal",
"ACTIVATION":"Aktivasi perangkat", "ACTIVATION": "Aktivasi perangkat",
"BATTERY_LOW": "Baterai lemah",
"BATTERY_LOW":"Baterai lemah", "BATTERY_CHARGING": "Mengisi",
"BATTERY_CHARGING":"Mengisi", "BATTERY_FULL": "Baterai penuh",
"BATTERY_FULL":"Baterai penuh", "BATTERY_NEED_CHARGE": "Baterai lemah, silakan isi",
"BATTERY_NEED_CHARGE":"Baterai lemah, silakan isi", "VOLUME": "Volume ",
"MUTED": "Bisu",
"VOLUME":"Volume ", "MAX_VOLUME": "Volume maksimum",
"MUTED":"Bisu", "RTC_MODE_OFF": "AEC mati",
"MAX_VOLUME":"Volume maksimum", "RTC_MODE_ON": "AEC nyala",
"DOWNLOAD_ASSETS_FAILED": "Gagal mengunduh aset",
"RTC_MODE_OFF":"AEC mati", "LOADING_ASSETS": "Memuat aset...",
"RTC_MODE_ON":"AEC nyala" "PLEASE_WAIT": "Mohon tunggu...",
"FOUND_NEW_ASSETS": "Ditemukan aset baru: %s",
"HELLO_MY_FRIEND": "Halo, teman saya!"
} }
} }
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"it-IT" "type": "it-IT"
}, },
"strings": { "strings": {
"WARNING":"Avviso", "WARNING": "Avviso",
"INFO":"Informazione", "INFO": "Informazione",
"ERROR":"Errore", "ERROR": "Errore",
"VERSION": "Versione ", "VERSION": "Versione ",
"LOADING_PROTOCOL":"Connessione al server...", "LOADING_PROTOCOL": "Connessione al server...",
"INITIALIZING":"Inizializzazione...", "INITIALIZING": "Inizializzazione...",
"PIN_ERROR":"Inserire la scheda SIM", "PIN_ERROR": "Inserire la scheda SIM",
"REG_ERROR":"Impossibile accedere alla rete, controllare lo stato della scheda dati", "REG_ERROR": "Impossibile accedere alla rete, controllare lo stato della scheda dati",
"DETECTING_MODULE":"Rilevamento modulo...", "DETECTING_MODULE": "Rilevamento modulo...",
"REGISTERING_NETWORK":"In attesa della rete...", "REGISTERING_NETWORK": "In attesa della rete...",
"CHECKING_NEW_VERSION":"Controllo nuova versione...", "CHECKING_NEW_VERSION": "Controllo nuova versione...",
"CHECK_NEW_VERSION_FAILED":"Controllo nuova versione fallito, riprovo tra %d secondi: %s", "CHECK_NEW_VERSION_FAILED": "Controllo nuova versione fallito, riprovo tra %d secondi: %s",
"SWITCH_TO_WIFI_NETWORK":"Passaggio a Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "Passaggio a Wi-Fi...",
"SWITCH_TO_4G_NETWORK":"Passaggio a 4G...", "SWITCH_TO_4G_NETWORK": "Passaggio a 4G...",
"STANDBY": "In attesa",
"STANDBY":"In attesa", "CONNECT_TO": "Connetti a ",
"CONNECT_TO":"Connetti a ", "CONNECTING": "Connessione...",
"CONNECTING":"Connessione...", "CONNECTED_TO": "Connesso a ",
"CONNECTED_TO":"Connesso a ", "LISTENING": "In ascolto...",
"SPEAKING": "Parlando...",
"LISTENING":"In ascolto...", "SERVER_NOT_FOUND": "Ricerca servizio disponibile",
"SPEAKING":"Parlando...", "SERVER_NOT_CONNECTED": "Impossibile connettersi al servizio, riprovare più tardi",
"SERVER_TIMEOUT": "Timeout risposta",
"SERVER_NOT_FOUND":"Ricerca servizio disponibile", "SERVER_ERROR": "Invio fallito, controllare la rete",
"SERVER_NOT_CONNECTED":"Impossibile connettersi al servizio, riprovare più tardi", "CONNECT_TO_HOTSPOT": "Connetti telefono al hotspot ",
"SERVER_TIMEOUT":"Timeout risposta", "ACCESS_VIA_BROWSER": "accedi tramite browser ",
"SERVER_ERROR":"Invio fallito, controllare la rete", "WIFI_CONFIG_MODE": "Modalità configurazione rete",
"ENTERING_WIFI_CONFIG_MODE": "Entrata in modalità configurazione rete...",
"CONNECT_TO_HOTSPOT":"Connetti telefono al hotspot ", "SCANNING_WIFI": "Scansione Wi-Fi...",
"ACCESS_VIA_BROWSER":"accedi tramite browser ",
"WIFI_CONFIG_MODE":"Modalità configurazione rete",
"ENTERING_WIFI_CONFIG_MODE":"Entrata in modalità configurazione rete...",
"SCANNING_WIFI":"Scansione Wi-Fi...",
"NEW_VERSION": "Nuova versione ", "NEW_VERSION": "Nuova versione ",
"OTA_UPGRADE":"Aggiornamento OTA", "OTA_UPGRADE": "Aggiornamento OTA",
"UPGRADING":"Aggiornamento sistema...", "UPGRADING": "Aggiornamento sistema...",
"UPGRADE_FAILED":"Aggiornamento fallito", "UPGRADE_FAILED": "Aggiornamento fallito",
"ACTIVATION":"Attivazione dispositivo", "ACTIVATION": "Attivazione dispositivo",
"BATTERY_LOW": "Batteria scarica",
"BATTERY_LOW":"Batteria scarica", "BATTERY_CHARGING": "In carica",
"BATTERY_CHARGING":"In carica", "BATTERY_FULL": "Batteria piena",
"BATTERY_FULL":"Batteria piena", "BATTERY_NEED_CHARGE": "Batteria scarica, ricaricare",
"BATTERY_NEED_CHARGE":"Batteria scarica, ricaricare", "VOLUME": "Volume ",
"MUTED": "Silenziato",
"VOLUME":"Volume ", "MAX_VOLUME": "Volume massimo",
"MUTED":"Silenziato", "RTC_MODE_OFF": "AEC disattivato",
"MAX_VOLUME":"Volume massimo", "RTC_MODE_ON": "AEC attivato",
"DOWNLOAD_ASSETS_FAILED": "Impossibile scaricare le risorse",
"RTC_MODE_OFF":"AEC disattivato", "LOADING_ASSETS": "Caricamento risorse...",
"RTC_MODE_ON":"AEC attivato" "PLEASE_WAIT": "Attendere prego...",
"FOUND_NEW_ASSETS": "Trovate nuove risorse: %s",
"HELLO_MY_FRIEND": "Ciao, amico mio!"
} }
} }
+6 -9
View File
@@ -17,42 +17,39 @@
"CHECK_NEW_VERSION_FAILED": "更新確認に失敗しました。%d 秒後に再試行します: %s", "CHECK_NEW_VERSION_FAILED": "更新確認に失敗しました。%d 秒後に再試行します: %s",
"SWITCH_TO_WIFI_NETWORK": "Wi-Fiに切り替え中...", "SWITCH_TO_WIFI_NETWORK": "Wi-Fiに切り替え中...",
"SWITCH_TO_4G_NETWORK": "4Gに切り替え中...", "SWITCH_TO_4G_NETWORK": "4Gに切り替え中...",
"STANDBY": "待機中", "STANDBY": "待機中",
"CONNECT_TO": "接続先 ", "CONNECT_TO": "接続先 ",
"CONNECTING": "接続中...", "CONNECTING": "接続中...",
"CONNECTED_TO": "接続完了 ", "CONNECTED_TO": "接続完了 ",
"LISTENING": "リスニング中...", "LISTENING": "リスニング中...",
"SPEAKING": "話しています...", "SPEAKING": "話しています...",
"SERVER_NOT_FOUND": "利用可能なサーバーを探しています", "SERVER_NOT_FOUND": "利用可能なサーバーを探しています",
"SERVER_NOT_CONNECTED": "サーバーに接続できません。後でもう一度お試しください", "SERVER_NOT_CONNECTED": "サーバーに接続できません。後でもう一度お試しください",
"SERVER_TIMEOUT": "応答待機時間が終了しました", "SERVER_TIMEOUT": "応答待機時間が終了しました",
"SERVER_ERROR": "送信に失敗しました。ネットワークを確認してください", "SERVER_ERROR": "送信に失敗しました。ネットワークを確認してください",
"CONNECT_TO_HOTSPOT": "スマートフォンをWi-Fi ", "CONNECT_TO_HOTSPOT": "スマートフォンをWi-Fi ",
"ACCESS_VIA_BROWSER": " に接続し、ブラウザでアクセスしてください ", "ACCESS_VIA_BROWSER": " に接続し、ブラウザでアクセスしてください ",
"WIFI_CONFIG_MODE": "ネットワーク設定モード", "WIFI_CONFIG_MODE": "ネットワーク設定モード",
"ENTERING_WIFI_CONFIG_MODE": "ネットワーク設定中...", "ENTERING_WIFI_CONFIG_MODE": "ネットワーク設定中...",
"SCANNING_WIFI": "Wi-Fiをスキャン中...", "SCANNING_WIFI": "Wi-Fiをスキャン中...",
"NEW_VERSION": "新しいバージョン ", "NEW_VERSION": "新しいバージョン ",
"OTA_UPGRADE": "OTAアップグレード", "OTA_UPGRADE": "OTAアップグレード",
"UPGRADING": "システムをアップグレード中...", "UPGRADING": "システムをアップグレード中...",
"UPGRADE_FAILED": "アップグレード失敗", "UPGRADE_FAILED": "アップグレード失敗",
"ACTIVATION": "デバイスをアクティベート", "ACTIVATION": "デバイスをアクティベート",
"BATTERY_LOW": "バッテリーが少なくなっています", "BATTERY_LOW": "バッテリーが少なくなっています",
"BATTERY_CHARGING": "充電中", "BATTERY_CHARGING": "充電中",
"BATTERY_FULL": "バッテリー満タン", "BATTERY_FULL": "バッテリー満タン",
"BATTERY_NEED_CHARGE": "バッテリーが低下しています。充電してください", "BATTERY_NEED_CHARGE": "バッテリーが低下しています。充電してください",
"VOLUME": "音量 ", "VOLUME": "音量 ",
"MUTED": "ミュートされています", "MUTED": "ミュートされています",
"MAX_VOLUME": "最大音量", "MAX_VOLUME": "最大音量",
"RTC_MODE_OFF": "AEC 無効", "RTC_MODE_OFF": "AEC 無効",
"RTC_MODE_ON": "AEC 有効" "RTC_MODE_ON": "AEC 有効",
"DOWNLOAD_ASSETS_FAILED": "アセットのダウンロードに失敗しました",
"LOADING_ASSETS": "アセットを読み込み中...",
"PLEASE_WAIT": "お待ちください...",
"FOUND_NEW_ASSETS": "新しいアセットが見つかりました: %s",
"HELLO_MY_FRIEND": "こんにちは、友達!"
} }
} }
+6 -9
View File
@@ -17,43 +17,40 @@
"CHECK_NEW_VERSION_FAILED": "새 버전 확인에 실패했습니다. %d초 후에 다시 시도합니다: %s", "CHECK_NEW_VERSION_FAILED": "새 버전 확인에 실패했습니다. %d초 후에 다시 시도합니다: %s",
"SWITCH_TO_WIFI_NETWORK": "Wi-Fi로 전환 중...", "SWITCH_TO_WIFI_NETWORK": "Wi-Fi로 전환 중...",
"SWITCH_TO_4G_NETWORK": "4G로 전환 중...", "SWITCH_TO_4G_NETWORK": "4G로 전환 중...",
"STANDBY": "대기", "STANDBY": "대기",
"CONNECT_TO": "연결 대상: ", "CONNECT_TO": "연결 대상: ",
"CONNECTING": "연결 중...", "CONNECTING": "연결 중...",
"CONNECTION_SUCCESSFUL": "연결 성공", "CONNECTION_SUCCESSFUL": "연결 성공",
"CONNECTED_TO": "연결됨: ", "CONNECTED_TO": "연결됨: ",
"LISTENING": "듣는 중...", "LISTENING": "듣는 중...",
"SPEAKING": "말하는 중...", "SPEAKING": "말하는 중...",
"SERVER_NOT_FOUND": "사용 가능한 서비스를 찾는 중", "SERVER_NOT_FOUND": "사용 가능한 서비스를 찾는 중",
"SERVER_NOT_CONNECTED": "서비스에 연결할 수 없습니다. 나중에 다시 시도하세요", "SERVER_NOT_CONNECTED": "서비스에 연결할 수 없습니다. 나중에 다시 시도하세요",
"SERVER_TIMEOUT": "응답 대기 시간 초과", "SERVER_TIMEOUT": "응답 대기 시간 초과",
"SERVER_ERROR": "전송 실패, 네트워크를 확인하세요", "SERVER_ERROR": "전송 실패, 네트워크를 확인하세요",
"CONNECT_TO_HOTSPOT": "핫스팟: ", "CONNECT_TO_HOTSPOT": "핫스팟: ",
"ACCESS_VIA_BROWSER": " 설정 URL: ", "ACCESS_VIA_BROWSER": " 설정 URL: ",
"WIFI_CONFIG_MODE": "Wi-Fi 설정 모드", "WIFI_CONFIG_MODE": "Wi-Fi 설정 모드",
"ENTERING_WIFI_CONFIG_MODE": "Wi-Fi 설정 모드 진입 중...", "ENTERING_WIFI_CONFIG_MODE": "Wi-Fi 설정 모드 진입 중...",
"SCANNING_WIFI": "Wi-Fi 스캔 중...", "SCANNING_WIFI": "Wi-Fi 스캔 중...",
"NEW_VERSION": "새 버전 ", "NEW_VERSION": "새 버전 ",
"OTA_UPGRADE": "OTA 업그레이드", "OTA_UPGRADE": "OTA 업그레이드",
"UPGRADING": "시스템 업그레이드 중...", "UPGRADING": "시스템 업그레이드 중...",
"UPGRADE_FAILED": "업그레이드 실패", "UPGRADE_FAILED": "업그레이드 실패",
"ACTIVATION": "활성화", "ACTIVATION": "활성화",
"BATTERY_LOW": "배터리 부족", "BATTERY_LOW": "배터리 부족",
"BATTERY_CHARGING": "충전 중", "BATTERY_CHARGING": "충전 중",
"BATTERY_FULL": "배터리 완충", "BATTERY_FULL": "배터리 완충",
"BATTERY_NEED_CHARGE": "배터리 부족, 충전하세요", "BATTERY_NEED_CHARGE": "배터리 부족, 충전하세요",
"VOLUME": "볼륨 ", "VOLUME": "볼륨 ",
"MUTED": "음소거", "MUTED": "음소거",
"MAX_VOLUME": "최대 볼륨", "MAX_VOLUME": "최대 볼륨",
"RTC_MODE_OFF": "AEC 끄기", "RTC_MODE_OFF": "AEC 끄기",
"RTC_MODE_ON": "AEC 켜기" "RTC_MODE_ON": "AEC 켜기",
"DOWNLOAD_ASSETS_FAILED": "에셋 다운로드 실패",
"LOADING_ASSETS": "에셋 로딩 중...",
"PLEASE_WAIT": "잠시 기다려 주세요...",
"FOUND_NEW_ASSETS": "새로운 에셋을 발견했습니다: %s",
"HELLO_MY_FRIEND": "안녕하세요, 친구!"
} }
} }
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"pl-PL" "type": "pl-PL"
}, },
"strings": { "strings": {
"WARNING":"Ostrzeżenie", "WARNING": "Ostrzeżenie",
"INFO":"Informacja", "INFO": "Informacja",
"ERROR":"Błąd", "ERROR": "Błąd",
"VERSION": "Wersja ", "VERSION": "Wersja ",
"LOADING_PROTOCOL":"Łączenie z serwerem...", "LOADING_PROTOCOL": "Łączenie z serwerem...",
"INITIALIZING":"Inicjalizacja...", "INITIALIZING": "Inicjalizacja...",
"PIN_ERROR":"Proszę włożyć kartę SIM", "PIN_ERROR": "Proszę włożyć kartę SIM",
"REG_ERROR":"Nie można uzyskać dostępu do sieci, sprawdź stan karty danych", "REG_ERROR": "Nie można uzyskać dostępu do sieci, sprawdź stan karty danych",
"DETECTING_MODULE":"Wykrywanie modułu...", "DETECTING_MODULE": "Wykrywanie modułu...",
"REGISTERING_NETWORK":"Oczekiwanie na sieć...", "REGISTERING_NETWORK": "Oczekiwanie na sieć...",
"CHECKING_NEW_VERSION":"Sprawdzanie nowej wersji...", "CHECKING_NEW_VERSION": "Sprawdzanie nowej wersji...",
"CHECK_NEW_VERSION_FAILED":"Sprawdzanie nowej wersji nie powiodło się, ponowna próba za %d sekund: %s", "CHECK_NEW_VERSION_FAILED": "Sprawdzanie nowej wersji nie powiodło się, ponowna próba za %d sekund: %s",
"SWITCH_TO_WIFI_NETWORK":"Przełączanie na Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "Przełączanie na Wi-Fi...",
"SWITCH_TO_4G_NETWORK":"Przełączanie na 4G...", "SWITCH_TO_4G_NETWORK": "Przełączanie na 4G...",
"STANDBY": "Gotowość",
"STANDBY":"Gotowość", "CONNECT_TO": "Połącz z ",
"CONNECT_TO":"Połącz z ", "CONNECTING": "Łączenie...",
"CONNECTING":"Łączenie...", "CONNECTED_TO": "Połączono z ",
"CONNECTED_TO":"Połączono z ", "LISTENING": "Słuchanie...",
"SPEAKING": "Mówienie...",
"LISTENING":"Słuchanie...", "SERVER_NOT_FOUND": "Szukanie dostępnej usługi",
"SPEAKING":"Mówienie...", "SERVER_NOT_CONNECTED": "Nie można połączyć się z usługą, spróbuj ponownie później",
"SERVER_TIMEOUT": "Przekroczono czas oczekiwania na odpowiedź",
"SERVER_NOT_FOUND":"Szukanie dostępnej usługi", "SERVER_ERROR": "Wysyłanie nie powiodło się, sprawdź sieć",
"SERVER_NOT_CONNECTED":"Nie można połączyć się z usługą, spróbuj ponownie później", "CONNECT_TO_HOTSPOT": "Podłącz telefon do hotspotu ",
"SERVER_TIMEOUT":"Przekroczono czas oczekiwania na odpowiedź", "ACCESS_VIA_BROWSER": "dostęp przez przeglądarkę ",
"SERVER_ERROR":"Wysyłanie nie powiodło się, sprawdź sieć", "WIFI_CONFIG_MODE": "Tryb konfiguracji sieci",
"ENTERING_WIFI_CONFIG_MODE": "Wchodzenie w tryb konfiguracji sieci...",
"CONNECT_TO_HOTSPOT":"Podłącz telefon do hotspotu ", "SCANNING_WIFI": "Skanowanie Wi-Fi...",
"ACCESS_VIA_BROWSER":"dostęp przez przeglądarkę ",
"WIFI_CONFIG_MODE":"Tryb konfiguracji sieci",
"ENTERING_WIFI_CONFIG_MODE":"Wchodzenie w tryb konfiguracji sieci...",
"SCANNING_WIFI":"Skanowanie Wi-Fi...",
"NEW_VERSION": "Nowa wersja ", "NEW_VERSION": "Nowa wersja ",
"OTA_UPGRADE":"Aktualizacja OTA", "OTA_UPGRADE": "Aktualizacja OTA",
"UPGRADING":"Aktualizacja systemu...", "UPGRADING": "Aktualizacja systemu...",
"UPGRADE_FAILED":"Aktualizacja nie powiodła się", "UPGRADE_FAILED": "Aktualizacja nie powiodła się",
"ACTIVATION":"Aktywacja urządzenia", "ACTIVATION": "Aktywacja urządzenia",
"BATTERY_LOW": "Niski poziom baterii",
"BATTERY_LOW":"Niski poziom baterii", "BATTERY_CHARGING": "Ładowanie",
"BATTERY_CHARGING":"Ładowanie", "BATTERY_FULL": "Bateria pełna",
"BATTERY_FULL":"Bateria pełna", "BATTERY_NEED_CHARGE": "Niski poziom baterii, proszę naładować",
"BATTERY_NEED_CHARGE":"Niski poziom baterii, proszę naładować", "VOLUME": "Głośność ",
"MUTED": "Wyciszony",
"VOLUME":"Głośność ", "MAX_VOLUME": "Maksymalna głośność",
"MUTED":"Wyciszony", "RTC_MODE_OFF": "AEC wyłączony",
"MAX_VOLUME":"Maksymalna głośność", "RTC_MODE_ON": "AEC włączony",
"DOWNLOAD_ASSETS_FAILED": "Nie udało się pobrać zasobów",
"RTC_MODE_OFF":"AEC wyłączony", "LOADING_ASSETS": "Ładowanie zasobów...",
"RTC_MODE_ON":"AEC włączony" "PLEASE_WAIT": "Proszę czekać...",
"FOUND_NEW_ASSETS": "Znaleziono nowe zasoby: %s",
"HELLO_MY_FRIEND": "Cześć, mój przyjacielu!"
} }
} }
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"pt-PT" "type": "pt-PT"
}, },
"strings": { "strings": {
"WARNING":"Aviso", "WARNING": "Aviso",
"INFO":"Informação", "INFO": "Informação",
"ERROR":"Erro", "ERROR": "Erro",
"VERSION": "Versão ", "VERSION": "Versão ",
"LOADING_PROTOCOL":"Ligando ao servidor...", "LOADING_PROTOCOL": "Ligando ao servidor...",
"INITIALIZING":"A inicializar...", "INITIALIZING": "A inicializar...",
"PIN_ERROR":"Por favor insira o cartão SIM", "PIN_ERROR": "Por favor insira o cartão SIM",
"REG_ERROR":"Não é possível aceder à rede, verifique o estado do cartão de dados", "REG_ERROR": "Não é possível aceder à rede, verifique o estado do cartão de dados",
"DETECTING_MODULE":"A detectar módulo...", "DETECTING_MODULE": "A detectar módulo...",
"REGISTERING_NETWORK":"À espera da rede...", "REGISTERING_NETWORK": "À espera da rede...",
"CHECKING_NEW_VERSION":"A verificar nova versão...", "CHECKING_NEW_VERSION": "A verificar nova versão...",
"CHECK_NEW_VERSION_FAILED":"Falha na verificação de nova versão, nova tentativa em %d segundos: %s", "CHECK_NEW_VERSION_FAILED": "Falha na verificação de nova versão, nova tentativa em %d segundos: %s",
"SWITCH_TO_WIFI_NETWORK":"A mudar para Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "A mudar para Wi-Fi...",
"SWITCH_TO_4G_NETWORK":"A mudar para 4G...", "SWITCH_TO_4G_NETWORK": "A mudar para 4G...",
"STANDBY": "Em espera",
"STANDBY":"Em espera", "CONNECT_TO": "Ligar a ",
"CONNECT_TO":"Ligar a ", "CONNECTING": "A ligar...",
"CONNECTING":"A ligar...", "CONNECTED_TO": "Ligado a ",
"CONNECTED_TO":"Ligado a ", "LISTENING": "A escutar...",
"SPEAKING": "A falar...",
"LISTENING":"A escutar...", "SERVER_NOT_FOUND": "A procurar serviço disponível",
"SPEAKING":"A falar...", "SERVER_NOT_CONNECTED": "Não é possível ligar ao serviço, tente mais tarde",
"SERVER_TIMEOUT": "Tempo limite de resposta",
"SERVER_NOT_FOUND":"A procurar serviço disponível", "SERVER_ERROR": "Falha no envio, verifique a rede",
"SERVER_NOT_CONNECTED":"Não é possível ligar ao serviço, tente mais tarde", "CONNECT_TO_HOTSPOT": "Ligue o telefone ao hotspot ",
"SERVER_TIMEOUT":"Tempo limite de resposta", "ACCESS_VIA_BROWSER": "aceder através do navegador ",
"SERVER_ERROR":"Falha no envio, verifique a rede", "WIFI_CONFIG_MODE": "Modo de configuração de rede",
"ENTERING_WIFI_CONFIG_MODE": "A entrar no modo de configuração de rede...",
"CONNECT_TO_HOTSPOT":"Ligue o telefone ao hotspot ", "SCANNING_WIFI": "A procurar Wi-Fi...",
"ACCESS_VIA_BROWSER":"aceder através do navegador ",
"WIFI_CONFIG_MODE":"Modo de configuração de rede",
"ENTERING_WIFI_CONFIG_MODE":"A entrar no modo de configuração de rede...",
"SCANNING_WIFI":"A procurar Wi-Fi...",
"NEW_VERSION": "Nova versão ", "NEW_VERSION": "Nova versão ",
"OTA_UPGRADE":"Atualização OTA", "OTA_UPGRADE": "Atualização OTA",
"UPGRADING":"A atualizar sistema...", "UPGRADING": "A atualizar sistema...",
"UPGRADE_FAILED":"Atualização falhada", "UPGRADE_FAILED": "Atualização falhada",
"ACTIVATION":"Ativação do dispositivo", "ACTIVATION": "Ativação do dispositivo",
"BATTERY_LOW": "Bateria fraca",
"BATTERY_LOW":"Bateria fraca", "BATTERY_CHARGING": "A carregar",
"BATTERY_CHARGING":"A carregar", "BATTERY_FULL": "Bateria cheia",
"BATTERY_FULL":"Bateria cheia", "BATTERY_NEED_CHARGE": "Bateria fraca, por favor carregue",
"BATTERY_NEED_CHARGE":"Bateria fraca, por favor carregue", "VOLUME": "Volume ",
"MUTED": "Silenciado",
"VOLUME":"Volume ", "MAX_VOLUME": "Volume máximo",
"MUTED":"Silenciado", "RTC_MODE_OFF": "AEC desligado",
"MAX_VOLUME":"Volume máximo", "RTC_MODE_ON": "AEC ligado",
"DOWNLOAD_ASSETS_FAILED": "Falha ao descarregar recursos",
"RTC_MODE_OFF":"AEC desligado", "LOADING_ASSETS": "A carregar recursos...",
"RTC_MODE_ON":"AEC ligado" "PLEASE_WAIT": "Por favor aguarde...",
"FOUND_NEW_ASSETS": "Encontrados novos recursos: %s",
"HELLO_MY_FRIEND": "Olá, meu amigo!"
} }
} }
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"ro-RO" "type": "ro-RO"
}, },
"strings": { "strings": {
"WARNING":"Avertisment", "WARNING": "Avertisment",
"INFO":"Informație", "INFO": "Informație",
"ERROR":"Eroare", "ERROR": "Eroare",
"VERSION": "Versiune ", "VERSION": "Versiune ",
"LOADING_PROTOCOL":"Se conectează la server...", "LOADING_PROTOCOL": "Se conectează la server...",
"INITIALIZING":"Se inițializează...", "INITIALIZING": "Se inițializează...",
"PIN_ERROR":"Vă rugăm să introduceți cardul SIM", "PIN_ERROR": "Vă rugăm să introduceți cardul SIM",
"REG_ERROR":"Nu se poate accesa rețeaua, verificați starea cardului de date", "REG_ERROR": "Nu se poate accesa rețeaua, verificați starea cardului de date",
"DETECTING_MODULE":"Se detectează modulul...", "DETECTING_MODULE": "Se detectează modulul...",
"REGISTERING_NETWORK":"Se așteaptă rețeaua...", "REGISTERING_NETWORK": "Se așteaptă rețeaua...",
"CHECKING_NEW_VERSION":"Se verifică versiunea nouă...", "CHECKING_NEW_VERSION": "Se verifică versiunea nouă...",
"CHECK_NEW_VERSION_FAILED":"Verificarea versiunii noi a eșuat, se reîncearcă în %d secunde: %s", "CHECK_NEW_VERSION_FAILED": "Verificarea versiunii noi a eșuat, se reîncearcă în %d secunde: %s",
"SWITCH_TO_WIFI_NETWORK":"Se comută la Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "Se comută la Wi-Fi...",
"SWITCH_TO_4G_NETWORK":"Se comută la 4G...", "SWITCH_TO_4G_NETWORK": "Se comută la 4G...",
"STANDBY": "În așteptare",
"STANDBY":"În așteptare", "CONNECT_TO": "Conectare la ",
"CONNECT_TO":"Conectare la ", "CONNECTING": "Se conectează...",
"CONNECTING":"Se conectează...", "CONNECTED_TO": "Conectat la ",
"CONNECTED_TO":"Conectat la ", "LISTENING": "Se ascultă...",
"SPEAKING": "Se vorbește...",
"LISTENING":"Se ascultă...", "SERVER_NOT_FOUND": "Se caută serviciul disponibil",
"SPEAKING":"Se vorbește...", "SERVER_NOT_CONNECTED": "Nu se poate conecta la serviciu, încercați mai târziu",
"SERVER_TIMEOUT": "Timpul de răspuns a expirat",
"SERVER_NOT_FOUND":"Se caută serviciul disponibil", "SERVER_ERROR": "Trimiterea a eșuat, verificați rețeaua",
"SERVER_NOT_CONNECTED":"Nu se poate conecta la serviciu, încercați mai târziu", "CONNECT_TO_HOTSPOT": "Conectați telefonul la hotspot ",
"SERVER_TIMEOUT":"Timpul de răspuns a expirat", "ACCESS_VIA_BROWSER": "accesați prin browser ",
"SERVER_ERROR":"Trimiterea a eșuat, verificați rețeaua", "WIFI_CONFIG_MODE": "Modul de configurare rețea",
"ENTERING_WIFI_CONFIG_MODE": "Se intră în modul de configurare rețea...",
"CONNECT_TO_HOTSPOT":"Conectați telefonul la hotspot ", "SCANNING_WIFI": "Se scanează Wi-Fi...",
"ACCESS_VIA_BROWSER":"accesați prin browser ",
"WIFI_CONFIG_MODE":"Modul de configurare rețea",
"ENTERING_WIFI_CONFIG_MODE":"Se intră în modul de configurare rețea...",
"SCANNING_WIFI":"Se scanează Wi-Fi...",
"NEW_VERSION": "Versiune nouă ", "NEW_VERSION": "Versiune nouă ",
"OTA_UPGRADE":"Actualizare OTA", "OTA_UPGRADE": "Actualizare OTA",
"UPGRADING":"Se actualizează sistemul...", "UPGRADING": "Se actualizează sistemul...",
"UPGRADE_FAILED":"Actualizarea a eșuat", "UPGRADE_FAILED": "Actualizarea a eșuat",
"ACTIVATION":"Activarea dispozitivului", "ACTIVATION": "Activarea dispozitivului",
"BATTERY_LOW": "Baterie scăzută",
"BATTERY_LOW":"Baterie scăzută", "BATTERY_CHARGING": "Se încarcă",
"BATTERY_CHARGING":"Se încarcă", "BATTERY_FULL": "Baterie plină",
"BATTERY_FULL":"Baterie plină", "BATTERY_NEED_CHARGE": "Baterie scăzută, vă rugăm să încărcați",
"BATTERY_NEED_CHARGE":"Baterie scăzută, vă rugăm să încărcați", "VOLUME": "Volum ",
"MUTED": "Silențios",
"VOLUME":"Volum ", "MAX_VOLUME": "Volum maxim",
"MUTED":"Silențios", "RTC_MODE_OFF": "AEC oprit",
"MAX_VOLUME":"Volum maxim", "RTC_MODE_ON": "AEC pornit",
"DOWNLOAD_ASSETS_FAILED": "Eșec la descărcarea resurselor",
"RTC_MODE_OFF":"AEC oprit", "LOADING_ASSETS": "Se încarcă resursele...",
"RTC_MODE_ON":"AEC pornit" "PLEASE_WAIT": "Vă rugăm să așteptați...",
"FOUND_NEW_ASSETS": "S-au găsit resurse noi: %s",
"HELLO_MY_FRIEND": "Salut, prietenul meu!"
} }
} }
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"ru-RU" "type": "ru-RU"
}, },
"strings": { "strings": {
"WARNING":"Предупреждение", "WARNING": "Предупреждение",
"INFO":"Информация", "INFO": "Информация",
"ERROR":"Ошибка", "ERROR": "Ошибка",
"VERSION": "Версия ", "VERSION": "Версия ",
"LOADING_PROTOCOL":"Подключение к серверу...", "LOADING_PROTOCOL": "Подключение к серверу...",
"INITIALIZING":"Инициализация...", "INITIALIZING": "Инициализация...",
"PIN_ERROR":"Пожалуйста, вставьте SIM-карту", "PIN_ERROR": "Пожалуйста, вставьте SIM-карту",
"REG_ERROR":"Невозможно подключиться к сети, проверьте состояние карты данных", "REG_ERROR": "Невозможно подключиться к сети, проверьте состояние карты данных",
"DETECTING_MODULE":"Обнаружение модуля...", "DETECTING_MODULE": "Обнаружение модуля...",
"REGISTERING_NETWORK":"Ожидание сети...", "REGISTERING_NETWORK": "Ожидание сети...",
"CHECKING_NEW_VERSION":"Проверка новой версии...", "CHECKING_NEW_VERSION": "Проверка новой версии...",
"CHECK_NEW_VERSION_FAILED":"Ошибка проверки новой версии, повтор через %d секунд: %s", "CHECK_NEW_VERSION_FAILED": "Ошибка проверки новой версии, повтор через %d секунд: %s",
"SWITCH_TO_WIFI_NETWORK":"Переключение на Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "Переключение на Wi-Fi...",
"SWITCH_TO_4G_NETWORK":"Переключение на 4G...", "SWITCH_TO_4G_NETWORK": "Переключение на 4G...",
"STANDBY": "Ожидание",
"STANDBY":"Ожидание", "CONNECT_TO": "Подключение к ",
"CONNECT_TO":"Подключение к ", "CONNECTING": "Подключение...",
"CONNECTING":"Подключение...", "CONNECTED_TO": "Подключено к ",
"CONNECTED_TO":одключено к ", "LISTENING": рослушивание...",
"SPEAKING": "Говорение...",
"LISTENING":рослушивание...", "SERVER_NOT_FOUND": оиск доступного сервиса",
"SPEAKING":"Говорение...", "SERVER_NOT_CONNECTED": "Невозможно подключиться к сервису, попробуйте позже",
"SERVER_TIMEOUT": "Тайм-аут ответа",
"SERVER_NOT_FOUND":"Поиск доступного сервиса", "SERVER_ERROR": "Ошибка отправки, проверьте сеть",
"SERVER_NOT_CONNECTED":"Невозможно подключиться к сервису, попробуйте позже", "CONNECT_TO_HOTSPOT": "Подключите телефон к точке доступа ",
"SERVER_TIMEOUT":"Тайм-аут ответа", "ACCESS_VIA_BROWSER": ",доступ через браузер ",
"SERVER_ERROR":"Ошибка отправки, проверьте сеть", "WIFI_CONFIG_MODE": "Режим настройки сети",
"ENTERING_WIFI_CONFIG_MODE": "Вход в режим настройки сети...",
"CONNECT_TO_HOTSPOT":"Подключите телефон к точке доступа ", "SCANNING_WIFI": "Сканирование Wi-Fi...",
"ACCESS_VIA_BROWSER":",доступ через браузер ",
"WIFI_CONFIG_MODE":"Режим настройки сети",
"ENTERING_WIFI_CONFIG_MODE":"Вход в режим настройки сети...",
"SCANNING_WIFI":"Сканирование Wi-Fi...",
"NEW_VERSION": "Новая версия ", "NEW_VERSION": "Новая версия ",
"OTA_UPGRADE":"Обновление OTA", "OTA_UPGRADE": "Обновление OTA",
"UPGRADING":"Обновление системы...", "UPGRADING": "Обновление системы...",
"UPGRADE_FAILED":"Обновление не удалось", "UPGRADE_FAILED": "Обновление не удалось",
"ACTIVATION":"Активация устройства", "ACTIVATION": "Активация устройства",
"BATTERY_LOW": "Низкий заряд батареи",
"BATTERY_LOW":"Низкий заряд батареи", "BATTERY_CHARGING": "Зарядка",
"BATTERY_CHARGING":"Зарядка", "BATTERY_FULL": "Батарея полная",
"BATTERY_FULL":"Батарея полная", "BATTERY_NEED_CHARGE": "Низкий заряд, пожалуйста, зарядите",
"BATTERY_NEED_CHARGE":"Низкий заряд, пожалуйста, зарядите", "VOLUME": "Громкость ",
"MUTED": "Звук отключен",
"VOLUME":"Громкость ", "MAX_VOLUME": "Максимальная громкость",
"MUTED":"Звук отключен", "RTC_MODE_OFF": "AEC выключен",
"MAX_VOLUME":"Максимальная громкость", "RTC_MODE_ON": "AEC включен",
"DOWNLOAD_ASSETS_FAILED": "Не удалось загрузить ресурсы",
"RTC_MODE_OFF":"AEC выключен", "LOADING_ASSETS": "Загрузка ресурсов...",
"RTC_MODE_ON":"AEC включен" "PLEASE_WAIT": "Пожалуйста, подождите...",
"FOUND_NEW_ASSETS": "Найдены новые ресурсы: %s",
"HELLO_MY_FRIEND": "Привет, мой друг!"
} }
} }
+6 -9
View File
@@ -17,43 +17,40 @@
"CHECK_NEW_VERSION_FAILED": "การตรวจสอบเวอร์ชันใหม่ล้มเหลว จะลองใหม่ใน %d วินาที: %s", "CHECK_NEW_VERSION_FAILED": "การตรวจสอบเวอร์ชันใหม่ล้มเหลว จะลองใหม่ใน %d วินาที: %s",
"SWITCH_TO_WIFI_NETWORK": "กำลังเปลี่ยนเป็น Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "กำลังเปลี่ยนเป็น Wi-Fi...",
"SWITCH_TO_4G_NETWORK": "กำลังเปลี่ยนเป็น 4G...", "SWITCH_TO_4G_NETWORK": "กำลังเปลี่ยนเป็น 4G...",
"STANDBY": "พร้อม", "STANDBY": "พร้อม",
"CONNECT_TO": "เชื่อมต่อกับ ", "CONNECT_TO": "เชื่อมต่อกับ ",
"CONNECTING": "กำลังเชื่อมต่อ...", "CONNECTING": "กำลังเชื่อมต่อ...",
"CONNECTION_SUCCESSFUL": "เชื่อมต่อสำเร็จ", "CONNECTION_SUCCESSFUL": "เชื่อมต่อสำเร็จ",
"CONNECTED_TO": "เชื่อมต่อกับ ", "CONNECTED_TO": "เชื่อมต่อกับ ",
"LISTENING": "กำลังฟัง...", "LISTENING": "กำลังฟัง...",
"SPEAKING": "กำลังพูด...", "SPEAKING": "กำลังพูด...",
"SERVER_NOT_FOUND": "กำลังค้นหาบริการที่ใช้งานได้", "SERVER_NOT_FOUND": "กำลังค้นหาบริการที่ใช้งานได้",
"SERVER_NOT_CONNECTED": "ไม่สามารถเชื่อมต่อกับบริการได้ กรุณาลองใหม่ในภายหลัง", "SERVER_NOT_CONNECTED": "ไม่สามารถเชื่อมต่อกับบริการได้ กรุณาลองใหม่ในภายหลัง",
"SERVER_TIMEOUT": "หมดเวลารอการตอบกลับ", "SERVER_TIMEOUT": "หมดเวลารอการตอบกลับ",
"SERVER_ERROR": "การส่งข้อมูลล้มเหลว กรุณาตรวจสอบเครือข่าย", "SERVER_ERROR": "การส่งข้อมูลล้มเหลว กรุณาตรวจสอบเครือข่าย",
"CONNECT_TO_HOTSPOT": "ฮอตสปอต: ", "CONNECT_TO_HOTSPOT": "ฮอตสปอต: ",
"ACCESS_VIA_BROWSER": " URL การตั้งค่า: ", "ACCESS_VIA_BROWSER": " URL การตั้งค่า: ",
"WIFI_CONFIG_MODE": "โหมดการตั้งค่า Wi-Fi", "WIFI_CONFIG_MODE": "โหมดการตั้งค่า Wi-Fi",
"ENTERING_WIFI_CONFIG_MODE": "กำลังเข้าสู่โหมดการตั้งค่า Wi-Fi...", "ENTERING_WIFI_CONFIG_MODE": "กำลังเข้าสู่โหมดการตั้งค่า Wi-Fi...",
"SCANNING_WIFI": "กำลังสแกน Wi-Fi...", "SCANNING_WIFI": "กำลังสแกน Wi-Fi...",
"NEW_VERSION": "เวอร์ชันใหม่ ", "NEW_VERSION": "เวอร์ชันใหม่ ",
"OTA_UPGRADE": "การอัปเกรด OTA", "OTA_UPGRADE": "การอัปเกรด OTA",
"UPGRADING": "ระบบกำลังอัปเกรด...", "UPGRADING": "ระบบกำลังอัปเกรด...",
"UPGRADE_FAILED": "การอัปเกรดล้มเหลว", "UPGRADE_FAILED": "การอัปเกรดล้มเหลว",
"ACTIVATION": "การเปิดใช้งาน", "ACTIVATION": "การเปิดใช้งาน",
"BATTERY_LOW": "แบตเตอรี่ต่ำ", "BATTERY_LOW": "แบตเตอรี่ต่ำ",
"BATTERY_CHARGING": "กำลังชาร์จ", "BATTERY_CHARGING": "กำลังชาร์จ",
"BATTERY_FULL": "แบตเตอรี่เต็ม", "BATTERY_FULL": "แบตเตอรี่เต็ม",
"BATTERY_NEED_CHARGE": "แบตเตอรี่ต่ำ กรุณาชาร์จ", "BATTERY_NEED_CHARGE": "แบตเตอรี่ต่ำ กรุณาชาร์จ",
"VOLUME": "เสียง ", "VOLUME": "เสียง ",
"MUTED": "ปิดเสียง", "MUTED": "ปิดเสียง",
"MAX_VOLUME": "เสียงสูงสุด", "MAX_VOLUME": "เสียงสูงสุด",
"RTC_MODE_OFF": "ปิด AEC", "RTC_MODE_OFF": "ปิด AEC",
"RTC_MODE_ON": "เปิด AEC" "RTC_MODE_ON": "เปิด AEC",
"DOWNLOAD_ASSETS_FAILED": "ดาวน์โหลดทรัพยากรล้มเหลว",
"LOADING_ASSETS": "กำลังโหลดทรัพยากร...",
"PLEASE_WAIT": "กรุณารอสักครู่...",
"FOUND_NEW_ASSETS": "พบทรัพยากรใหม่: %s",
"HELLO_MY_FRIEND": "สวัสดี เพื่อนของฉัน!"
} }
} }
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"tr-TR" "type": "tr-TR"
}, },
"strings": { "strings": {
"WARNING":"Uyarı", "WARNING": "Uyarı",
"INFO":"Bilgi", "INFO": "Bilgi",
"ERROR":"Hata", "ERROR": "Hata",
"VERSION": "Sürüm ", "VERSION": "Sürüm ",
"LOADING_PROTOCOL":"Sunucuya bağlanıyor...", "LOADING_PROTOCOL": "Sunucuya bağlanıyor...",
"INITIALIZING":"Başlatılıyor...", "INITIALIZING": "Başlatılıyor...",
"PIN_ERROR":"Lütfen SIM kartı takın", "PIN_ERROR": "Lütfen SIM kartı takın",
"REG_ERROR":"Ağa erişilemiyor, veri kartı durumunu kontrol edin", "REG_ERROR": "Ağa erişilemiyor, veri kartı durumunu kontrol edin",
"DETECTING_MODULE":"Modül algılanıyor...", "DETECTING_MODULE": "Modül algılanıyor...",
"REGISTERING_NETWORK":"Ağ bekleniyor...", "REGISTERING_NETWORK": "Ağ bekleniyor...",
"CHECKING_NEW_VERSION":"Yeni sürüm kontrol ediliyor...", "CHECKING_NEW_VERSION": "Yeni sürüm kontrol ediliyor...",
"CHECK_NEW_VERSION_FAILED":"Yeni sürüm kontrolü başarısız, %d saniye sonra tekrar denenecek: %s", "CHECK_NEW_VERSION_FAILED": "Yeni sürüm kontrolü başarısız, %d saniye sonra tekrar denenecek: %s",
"SWITCH_TO_WIFI_NETWORK":"Wi-Fi'ye geçiliyor...", "SWITCH_TO_WIFI_NETWORK": "Wi-Fi'ye geçiliyor...",
"SWITCH_TO_4G_NETWORK":"4G'ye geçiliyor...", "SWITCH_TO_4G_NETWORK": "4G'ye geçiliyor...",
"STANDBY": "Bekleme",
"STANDBY":"Bekleme", "CONNECT_TO": "Bağlan ",
"CONNECT_TO":"Bağlan ", "CONNECTING": "Bağlanıyor...",
"CONNECTING":"Bağlanıyor...", "CONNECTED_TO": "Bağlandı ",
"CONNECTED_TO":"Bağlandı ", "LISTENING": "Dinleniyor...",
"SPEAKING": "Konuşuluyor...",
"LISTENING":"Dinleniyor...", "SERVER_NOT_FOUND": "Mevcut hizmet aranıyor",
"SPEAKING":"Konuşuluyor...", "SERVER_NOT_CONNECTED": "Hizmete bağlanılamıyor, lütfen daha sonra deneyin",
"SERVER_TIMEOUT": "Yanıt zaman aşımı",
"SERVER_NOT_FOUND":"Mevcut hizmet aranıyor", "SERVER_ERROR": "Gönderme başarısız, ağı kontrol edin",
"SERVER_NOT_CONNECTED":"Hizmete bağlanılamıyor, lütfen daha sonra deneyin", "CONNECT_TO_HOTSPOT": "Telefonu hotspot'a bağlayın ",
"SERVER_TIMEOUT":"Yanıt zaman aşımı", "ACCESS_VIA_BROWSER": "tarayıcı üzerinden erişin ",
"SERVER_ERROR":"nderme başarısız, ağı kontrol edin", "WIFI_CONFIG_MODE": "Ağ yapılandırma modu",
"ENTERING_WIFI_CONFIG_MODE": "Ağ yapılandırma moduna giriliyor...",
"CONNECT_TO_HOTSPOT":"Telefonu hotspot'a bağlayın ", "SCANNING_WIFI": "Wi-Fi taranıyor...",
"ACCESS_VIA_BROWSER":"tarayıcı üzerinden erişin ",
"WIFI_CONFIG_MODE":"Ağ yapılandırma modu",
"ENTERING_WIFI_CONFIG_MODE":"Ağ yapılandırma moduna giriliyor...",
"SCANNING_WIFI":"Wi-Fi taranıyor...",
"NEW_VERSION": "Yeni sürüm ", "NEW_VERSION": "Yeni sürüm ",
"OTA_UPGRADE":"OTA güncelleme", "OTA_UPGRADE": "OTA güncelleme",
"UPGRADING":"Sistem güncelleniyor...", "UPGRADING": "Sistem güncelleniyor...",
"UPGRADE_FAILED":"Güncelleme başarısız", "UPGRADE_FAILED": "Güncelleme başarısız",
"ACTIVATION":"Cihaz aktivasyonu", "ACTIVATION": "Cihaz aktivasyonu",
"BATTERY_LOW": "Pil düşük",
"BATTERY_LOW":"Pil düşük", "BATTERY_CHARGING": "Şarj oluyor",
"BATTERY_CHARGING":"Şarj oluyor", "BATTERY_FULL": "Pil dolu",
"BATTERY_FULL":"Pil dolu", "BATTERY_NEED_CHARGE": "Pil düşük, lütfen şarj edin",
"BATTERY_NEED_CHARGE":"Pil düşük, lütfen şarj edin", "VOLUME": "Ses ",
"MUTED": "Sessiz",
"VOLUME":"Ses ", "MAX_VOLUME": "Maksimum ses",
"MUTED":"Sessiz", "RTC_MODE_OFF": "AEC kapalı",
"MAX_VOLUME":"Maksimum ses", "RTC_MODE_ON": "AEC açık",
"DOWNLOAD_ASSETS_FAILED": "Varlıklar indirilemedi",
"RTC_MODE_OFF":"AEC kapalı", "LOADING_ASSETS": "Varlıklar yükleniyor...",
"RTC_MODE_ON":"AEC açık" "PLEASE_WAIT": "Lütfen bekleyin...",
"FOUND_NEW_ASSETS": "Yeni varlıklar bulundu: %s",
"HELLO_MY_FRIEND": "Merhaba, arkadaşım!"
} }
} }
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"uk-UA" "type": "uk-UA"
}, },
"strings": { "strings": {
"WARNING":"Попередження", "WARNING": "Попередження",
"INFO":"Інформація", "INFO": "Інформація",
"ERROR":"Помилка", "ERROR": "Помилка",
"VERSION": "Версія ", "VERSION": "Версія ",
"LOADING_PROTOCOL":"Підключення до сервера...", "LOADING_PROTOCOL": "Підключення до сервера...",
"INITIALIZING":"Ініціалізація...", "INITIALIZING": "Ініціалізація...",
"PIN_ERROR":"Будь ласка, вставте SIM-карту", "PIN_ERROR": "Будь ласка, вставте SIM-карту",
"REG_ERROR":"Неможливо отримати доступ до мережі, перевірте стан карти даних", "REG_ERROR": "Неможливо отримати доступ до мережі, перевірте стан карти даних",
"DETECTING_MODULE":"Виявлення модуля...", "DETECTING_MODULE": "Виявлення модуля...",
"REGISTERING_NETWORK":"Очікування мережі...", "REGISTERING_NETWORK": "Очікування мережі...",
"CHECKING_NEW_VERSION":"Перевірка нової версії...", "CHECKING_NEW_VERSION": "Перевірка нової версії...",
"CHECK_NEW_VERSION_FAILED":"Перевірка нової версії не вдалася, повтор через %d секунд: %s", "CHECK_NEW_VERSION_FAILED": "Перевірка нової версії не вдалася, повтор через %d секунд: %s",
"SWITCH_TO_WIFI_NETWORK":"Перемикання на Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "Перемикання на Wi-Fi...",
"SWITCH_TO_4G_NETWORK":"Перемикання на 4G...", "SWITCH_TO_4G_NETWORK": "Перемикання на 4G...",
"STANDBY": "Очікування",
"STANDBY":"Очікування", "CONNECT_TO": "Підключитися до ",
"CONNECT_TO":"Підключитися до ", "CONNECTING": "Підключення...",
"CONNECTING":"Підключення...", "CONNECTED_TO": "Підключено до ",
"CONNECTED_TO":ідключено до ", "LISTENING": рослуховування...",
"SPEAKING": "Говоріння...",
"LISTENING":рослуховування...", "SERVER_NOT_FOUND": ошук доступного сервісу",
"SPEAKING":"Говоріння...", "SERVER_NOT_CONNECTED": "Неможливо підключитися до сервісу, спробуйте пізніше",
"SERVER_TIMEOUT": "Час очікування відповіді",
"SERVER_NOT_FOUND":"Пошук доступного сервісу", "SERVER_ERROR": "Помилка відправки, перевірте мережу",
"SERVER_NOT_CONNECTED":"Неможливо підключитися до сервісу, спробуйте пізніше", "CONNECT_TO_HOTSPOT": "Підключіть телефон до точки доступу ",
"SERVER_TIMEOUT":"Час очікування відповіді", "ACCESS_VIA_BROWSER": ",доступ через браузер ",
"SERVER_ERROR":"Помилка відправки, перевірте мережу", "WIFI_CONFIG_MODE": "Режим налаштування мережі",
"ENTERING_WIFI_CONFIG_MODE": "Вхід у режим налаштування мережі...",
"CONNECT_TO_HOTSPOT":"Підключіть телефон до точки доступу ", "SCANNING_WIFI": "Сканування Wi-Fi...",
"ACCESS_VIA_BROWSER":",доступ через браузер ",
"WIFI_CONFIG_MODE":"Режим налаштування мережі",
"ENTERING_WIFI_CONFIG_MODE":"Вхід у режим налаштування мережі...",
"SCANNING_WIFI":"Сканування Wi-Fi...",
"NEW_VERSION": "Нова версія ", "NEW_VERSION": "Нова версія ",
"OTA_UPGRADE":"Оновлення OTA", "OTA_UPGRADE": "Оновлення OTA",
"UPGRADING":"Оновлення системи...", "UPGRADING": "Оновлення системи...",
"UPGRADE_FAILED":"Оновлення не вдалося", "UPGRADE_FAILED": "Оновлення не вдалося",
"ACTIVATION":"Активація пристрою", "ACTIVATION": "Активація пристрою",
"BATTERY_LOW": "Низький заряд батареї",
"BATTERY_LOW":"Низький заряд батареї", "BATTERY_CHARGING": "Зарядка",
"BATTERY_CHARGING":"Зарядка", "BATTERY_FULL": "Батарея повна",
"BATTERY_FULL":"Батарея повна", "BATTERY_NEED_CHARGE": "Низький заряд, будь ласка, зарядіть",
"BATTERY_NEED_CHARGE":"Низький заряд, будь ласка, зарядіть", "VOLUME": "Гучність ",
"MUTED": "Звук вимкнено",
"VOLUME":"Гучність ", "MAX_VOLUME": "Максимальна гучність",
"MUTED":"Звук вимкнено", "RTC_MODE_OFF": "AEC вимкнено",
"MAX_VOLUME":"Максимальна гучність", "RTC_MODE_ON": "AEC увімкнено",
"DOWNLOAD_ASSETS_FAILED": "Не вдалося завантажити ресурси",
"RTC_MODE_OFF":"AEC вимкнено", "LOADING_ASSETS": "Завантаження ресурсів...",
"RTC_MODE_ON":"AEC увімкнено" "PLEASE_WAIT": "Будь ласка, зачекайте...",
"FOUND_NEW_ASSETS": "Знайдено нові ресурси: %s",
"HELLO_MY_FRIEND": "Привіт, мій друже!"
} }
} }
+6 -9
View File
@@ -17,43 +17,40 @@
"CHECK_NEW_VERSION_FAILED": "Kiểm tra phiên bản mới thất bại, sẽ thử lại sau %d giây: %s", "CHECK_NEW_VERSION_FAILED": "Kiểm tra phiên bản mới thất bại, sẽ thử lại sau %d giây: %s",
"SWITCH_TO_WIFI_NETWORK": "Đang chuyển sang Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "Đang chuyển sang Wi-Fi...",
"SWITCH_TO_4G_NETWORK": "Đang chuyển sang 4G...", "SWITCH_TO_4G_NETWORK": "Đang chuyển sang 4G...",
"STANDBY": "Chờ", "STANDBY": "Chờ",
"CONNECT_TO": "Kết nối đến ", "CONNECT_TO": "Kết nối đến ",
"CONNECTING": "Đang kết nối...", "CONNECTING": "Đang kết nối...",
"CONNECTION_SUCCESSFUL": "Kết nối thành công", "CONNECTION_SUCCESSFUL": "Kết nối thành công",
"CONNECTED_TO": "Đã kết nối đến ", "CONNECTED_TO": "Đã kết nối đến ",
"LISTENING": "Đang lắng nghe...", "LISTENING": "Đang lắng nghe...",
"SPEAKING": "Đang nói...", "SPEAKING": "Đang nói...",
"SERVER_NOT_FOUND": "Đang tìm dịch vụ khả dụng", "SERVER_NOT_FOUND": "Đang tìm dịch vụ khả dụng",
"SERVER_NOT_CONNECTED": "Không thể kết nối đến dịch vụ, vui lòng thử lại sau", "SERVER_NOT_CONNECTED": "Không thể kết nối đến dịch vụ, vui lòng thử lại sau",
"SERVER_TIMEOUT": "Hết thời gian chờ phản hồi", "SERVER_TIMEOUT": "Hết thời gian chờ phản hồi",
"SERVER_ERROR": "Gửi thất bại, vui lòng kiểm tra mạng", "SERVER_ERROR": "Gửi thất bại, vui lòng kiểm tra mạng",
"CONNECT_TO_HOTSPOT": "Điểm phát sóng: ", "CONNECT_TO_HOTSPOT": "Điểm phát sóng: ",
"ACCESS_VIA_BROWSER": " URL cấu hình: ", "ACCESS_VIA_BROWSER": " URL cấu hình: ",
"WIFI_CONFIG_MODE": "Chế độ cấu hình Wi-Fi", "WIFI_CONFIG_MODE": "Chế độ cấu hình Wi-Fi",
"ENTERING_WIFI_CONFIG_MODE": "Đang vào chế độ cấu hình Wi-Fi...", "ENTERING_WIFI_CONFIG_MODE": "Đang vào chế độ cấu hình Wi-Fi...",
"SCANNING_WIFI": "Đang quét Wi-Fi...", "SCANNING_WIFI": "Đang quét Wi-Fi...",
"NEW_VERSION": "Phiên bản mới ", "NEW_VERSION": "Phiên bản mới ",
"OTA_UPGRADE": "Nâng cấp OTA", "OTA_UPGRADE": "Nâng cấp OTA",
"UPGRADING": "Hệ thống đang nâng cấp...", "UPGRADING": "Hệ thống đang nâng cấp...",
"UPGRADE_FAILED": "Nâng cấp thất bại", "UPGRADE_FAILED": "Nâng cấp thất bại",
"ACTIVATION": "Kích hoạt", "ACTIVATION": "Kích hoạt",
"BATTERY_LOW": "Pin yếu", "BATTERY_LOW": "Pin yếu",
"BATTERY_CHARGING": "Đang sạc", "BATTERY_CHARGING": "Đang sạc",
"BATTERY_FULL": "Pin đầy", "BATTERY_FULL": "Pin đầy",
"BATTERY_NEED_CHARGE": "Pin yếu, vui lòng sạc", "BATTERY_NEED_CHARGE": "Pin yếu, vui lòng sạc",
"VOLUME": "Âm lượng ", "VOLUME": "Âm lượng ",
"MUTED": "Tắt tiếng", "MUTED": "Tắt tiếng",
"MAX_VOLUME": "Âm lượng tối đa", "MAX_VOLUME": "Âm lượng tối đa",
"RTC_MODE_OFF": "Tắt AEC", "RTC_MODE_OFF": "Tắt AEC",
"RTC_MODE_ON": "Bật AEC" "RTC_MODE_ON": "Bật AEC",
"DOWNLOAD_ASSETS_FAILED": "Tải xuống tài nguyên thất bại",
"LOADING_ASSETS": "Đang tải tài nguyên...",
"PLEASE_WAIT": "Vui lòng đợi...",
"FOUND_NEW_ASSETS": "Tìm thấy tài nguyên mới: %s",
"HELLO_MY_FRIEND": "Xin chào, bạn của tôi!"
} }
} }
+47 -50
View File
@@ -1,58 +1,55 @@
{ {
"language": { "language": {
"type" :"zh-CN" "type": "zh-CN"
}, },
"strings": { "strings": {
"WARNING":"警告", "WARNING": "警告",
"INFO":"信息", "INFO": "信息",
"ERROR":"错误", "ERROR": "错误",
"VERSION": "版本 ", "VERSION": "版本 ",
"LOADING_PROTOCOL":"登录服务器...", "LOADING_PROTOCOL": "登录服务器...",
"INITIALIZING":"正在初始化...", "INITIALIZING": "正在初始化...",
"PIN_ERROR":"请插入 SIM 卡", "PIN_ERROR": "请插入 SIM 卡",
"REG_ERROR":"无法接入网络,请检查流量卡状态", "REG_ERROR": "无法接入网络,请检查流量卡状态",
"DETECTING_MODULE":"检测模组...", "DETECTING_MODULE": "检测模组...",
"REGISTERING_NETWORK":"等待网络...", "REGISTERING_NETWORK": "等待网络...",
"CHECKING_NEW_VERSION":"检查新版本...", "CHECKING_NEW_VERSION": "检查新版本...",
"CHECK_NEW_VERSION_FAILED":"检查新版本失败,将在 %d 秒后重试:%s", "CHECK_NEW_VERSION_FAILED": "检查新版本失败,将在 %d 秒后重试:%s",
"SWITCH_TO_WIFI_NETWORK":"切换到 Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "切换到 Wi-Fi...",
"SWITCH_TO_4G_NETWORK":"切换到 4G...", "SWITCH_TO_4G_NETWORK": "切换到 4G...",
"STANDBY": "待命",
"STANDBY":"待命", "CONNECT_TO": "连接 ",
"CONNECT_TO":"连接 ", "CONNECTING": "连接中...",
"CONNECTING":"连接中...", "CONNECTED_TO": "连接 ",
"CONNECTED_TO":"已连接 ", "LISTENING": "聆听中...",
"SPEAKING": "说话中...",
"LISTENING":"聆听中...", "SERVER_NOT_FOUND": "正在寻找可用服务",
"SPEAKING":"说话中...", "SERVER_NOT_CONNECTED": "无法连接服务,请稍后再试",
"SERVER_TIMEOUT": "等待响应超时",
"SERVER_NOT_FOUND":"正在寻找可用服务", "SERVER_ERROR": "发送失败,请检查网络",
"SERVER_NOT_CONNECTED":"无法连接服务,请稍后再试", "CONNECT_TO_HOTSPOT": "手机连接热点 ",
"SERVER_TIMEOUT":"等待响应超时", "ACCESS_VIA_BROWSER": ",浏览器访问 ",
"SERVER_ERROR":"发送失败,请检查网络", "WIFI_CONFIG_MODE": "配网模式",
"ENTERING_WIFI_CONFIG_MODE": "进入配网模式...",
"CONNECT_TO_HOTSPOT":"手机连接热点 ", "SCANNING_WIFI": "扫描 Wi-Fi...",
"ACCESS_VIA_BROWSER":",浏览器访问 ",
"WIFI_CONFIG_MODE":"配网模式",
"ENTERING_WIFI_CONFIG_MODE":"进入配网模式...",
"SCANNING_WIFI":"扫描 Wi-Fi...",
"NEW_VERSION": "新版本 ", "NEW_VERSION": "新版本 ",
"OTA_UPGRADE":"OTA 升级", "OTA_UPGRADE": "OTA 升级",
"UPGRADING":"正在升级系统...", "UPGRADING": "正在升级系统...",
"UPGRADE_FAILED":"升级失败", "UPGRADE_FAILED": "升级失败",
"ACTIVATION":"激活设备", "ACTIVATION": "激活设备",
"BATTERY_LOW": "电量不足",
"BATTERY_LOW":"电量不足", "BATTERY_CHARGING": "正在充电",
"BATTERY_CHARGING":"正在充电", "BATTERY_FULL": "电量已满",
"BATTERY_FULL":"电量已满", "BATTERY_NEED_CHARGE": "电量低,请充电",
"BATTERY_NEED_CHARGE":"电量低,请充电", "VOLUME": "音量 ",
"MUTED": "已静音",
"VOLUME":"音量 ", "MAX_VOLUME": "最大音量",
"MUTED":"已静音", "RTC_MODE_OFF": "AEC 关闭",
"MAX_VOLUME":"最大音量", "RTC_MODE_ON": "AEC 开启",
"DOWNLOAD_ASSETS_FAILED": "下载资源失败",
"RTC_MODE_OFF":"AEC 关闭", "LOADING_ASSETS": "加载资源...",
"RTC_MODE_ON":"AEC 开启" "PLEASE_WAIT": "请稍候...",
"FOUND_NEW_ASSETS": "发现新资源: %s",
"HELLO_MY_FRIEND": "你好,我的朋友!"
} }
} }
+6 -9
View File
@@ -17,42 +17,39 @@
"CHECK_NEW_VERSION_FAILED": "檢查新版本失敗,將在 %d 秒後重試:%s", "CHECK_NEW_VERSION_FAILED": "檢查新版本失敗,將在 %d 秒後重試:%s",
"SWITCH_TO_WIFI_NETWORK": "切換到 Wi-Fi...", "SWITCH_TO_WIFI_NETWORK": "切換到 Wi-Fi...",
"SWITCH_TO_4G_NETWORK": "切換到 4G...", "SWITCH_TO_4G_NETWORK": "切換到 4G...",
"STANDBY": "待命", "STANDBY": "待命",
"CONNECT_TO": "連接 ", "CONNECT_TO": "連接 ",
"CONNECTING": "連接中...", "CONNECTING": "連接中...",
"CONNECTED_TO": "已連接 ", "CONNECTED_TO": "已連接 ",
"LISTENING": "聆聽中...", "LISTENING": "聆聽中...",
"SPEAKING": "說話中...", "SPEAKING": "說話中...",
"SERVER_NOT_FOUND": "正在尋找可用服務", "SERVER_NOT_FOUND": "正在尋找可用服務",
"SERVER_NOT_CONNECTED": "無法連接服務,請稍後再試", "SERVER_NOT_CONNECTED": "無法連接服務,請稍後再試",
"SERVER_TIMEOUT": "等待響應超時", "SERVER_TIMEOUT": "等待響應超時",
"SERVER_ERROR": "發送失敗,請檢查網絡", "SERVER_ERROR": "發送失敗,請檢查網絡",
"CONNECT_TO_HOTSPOT": "手機連接WiFi ", "CONNECT_TO_HOTSPOT": "手機連接WiFi ",
"ACCESS_VIA_BROWSER": ",瀏覽器訪問 ", "ACCESS_VIA_BROWSER": ",瀏覽器訪問 ",
"WIFI_CONFIG_MODE": "網路設定模式", "WIFI_CONFIG_MODE": "網路設定模式",
"ENTERING_WIFI_CONFIG_MODE": "正在設定網路...", "ENTERING_WIFI_CONFIG_MODE": "正在設定網路...",
"SCANNING_WIFI": "掃描 Wi-Fi...", "SCANNING_WIFI": "掃描 Wi-Fi...",
"NEW_VERSION": "新版本 ", "NEW_VERSION": "新版本 ",
"OTA_UPGRADE": "OTA 升級", "OTA_UPGRADE": "OTA 升級",
"UPGRADING": "正在升級系統...", "UPGRADING": "正在升級系統...",
"UPGRADE_FAILED": "升級失敗", "UPGRADE_FAILED": "升級失敗",
"ACTIVATION": "啟用設備", "ACTIVATION": "啟用設備",
"BATTERY_LOW": "電量不足", "BATTERY_LOW": "電量不足",
"BATTERY_CHARGING": "正在充電", "BATTERY_CHARGING": "正在充電",
"BATTERY_FULL": "電量已滿", "BATTERY_FULL": "電量已滿",
"BATTERY_NEED_CHARGE": "電量低,請充電", "BATTERY_NEED_CHARGE": "電量低,請充電",
"VOLUME": "音量 ", "VOLUME": "音量 ",
"MUTED": "已靜音", "MUTED": "已靜音",
"MAX_VOLUME": "最大音量", "MAX_VOLUME": "最大音量",
"RTC_MODE_OFF": "AEC 關閉", "RTC_MODE_OFF": "AEC 關閉",
"RTC_MODE_ON": "AEC 開啟" "RTC_MODE_ON": "AEC 開啟",
"DOWNLOAD_ASSETS_FAILED": "下載資源失敗",
"LOADING_ASSETS": "載入資源...",
"PLEASE_WAIT": "請稍候...",
"FOUND_NEW_ASSETS": "發現新資源: %s",
"HELLO_MY_FRIEND": "你好,我的朋友!"
} }
} }
+28 -31
View File
@@ -8,43 +8,41 @@
#define TAG "AudioCodec" #define TAG "AudioCodec"
AudioCodec::AudioCodec(){ AudioCodec::AudioCodec() {
} }
AudioCodec::~AudioCodec(){ AudioCodec::~AudioCodec() {
} }
void AudioCodec::OutputData(std::vector<int16_t> &data){ void AudioCodec::OutputData(std::vector<int16_t>& data) {
Write(data.data(), data.size()); Write(data.data(), data.size());
} }
bool AudioCodec::InputData(std::vector<int16_t> &data){ bool AudioCodec::InputData(std::vector<int16_t>& data) {
int samples = Read(data.data(), data.size()); int samples = Read(data.data(), data.size());
if (samples > 0){ if (samples > 0) {
return true; return true;
} }
return false; return false;
} }
void AudioCodec::Start(){ void AudioCodec::Start() {
Settings settings("audio", false); Settings settings("audio", false);
output_volume_ = settings.GetInt("output_volume", output_volume_); output_volume_ = settings.GetInt("output_volume", output_volume_);
if (output_volume_ <= 0){ if (output_volume_ <= 0) {
ESP_LOGW(TAG, "Output volume value (%d) is too small, setting to default (10)", output_volume_); ESP_LOGW(TAG, "Output volume value (%d) is too small, setting to default (10)", output_volume_);
output_volume_ = 10; output_volume_ = 10;
} }
// 保存原始输出采样率 // 保存原始输出采样率
if (original_output_sample_rate_ == 0){ if (original_output_sample_rate_ == 0){
original_output_sample_rate_ = output_sample_rate_; original_output_sample_rate_ = output_sample_rate_;
ESP_LOGI(TAG, "Saved original output sample rate: %d Hz", original_output_sample_rate_); ESP_LOGI(TAG, "Saved original output sample rate: %d Hz", original_output_sample_rate_);
} }
if (tx_handle_ != nullptr) {
if (tx_handle_ != nullptr){
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_)); ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
} }
if (rx_handle_ != nullptr){ if (rx_handle_ != nullptr) {
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle_)); ESP_ERROR_CHECK(i2s_channel_enable(rx_handle_));
} }
@@ -53,7 +51,7 @@ void AudioCodec::Start(){
ESP_LOGI(TAG, "Audio codec started"); ESP_LOGI(TAG, "Audio codec started");
} }
void AudioCodec::SetOutputVolume(int volume){ void AudioCodec::SetOutputVolume(int volume) {
output_volume_ = volume; output_volume_ = volume;
ESP_LOGI(TAG, "Set output volume to %d", output_volume_); ESP_LOGI(TAG, "Set output volume to %d", output_volume_);
@@ -61,45 +59,45 @@ void AudioCodec::SetOutputVolume(int volume){
settings.SetInt("output_volume", output_volume_); settings.SetInt("output_volume", output_volume_);
} }
void AudioCodec::EnableInput(bool enable){ void AudioCodec::EnableInput(bool enable) {
if (enable == input_enabled_){ if (enable == input_enabled_) {
return; return;
} }
input_enabled_ = enable; input_enabled_ = enable;
ESP_LOGI(TAG, "Set input enable to %s", enable ? "true" : "false"); ESP_LOGI(TAG, "Set input enable to %s", enable ? "true" : "false");
} }
void AudioCodec::EnableOutput(bool enable){ void AudioCodec::EnableOutput(bool enable) {
if (enable == output_enabled_){ if (enable == output_enabled_) {
return; return;
} }
output_enabled_ = enable; output_enabled_ = enable;
ESP_LOGI(TAG, "Set output enable to %s", enable ? "true" : "false"); ESP_LOGI(TAG, "Set output enable to %s", enable ? "true" : "false");
} }
bool AudioCodec::SetOutputSampleRate(int sample_rate){ bool AudioCodec::SetOutputSampleRate(int sample_rate) {
// 特殊处理:如果传入 -1,表示重置到原始采样率 // 特殊处理:如果传入 -1,表示重置到原始采样率
if (sample_rate == -1){ if (sample_rate == -1) {
if (original_output_sample_rate_ > 0){ if (original_output_sample_rate_ > 0) {
sample_rate = original_output_sample_rate_; sample_rate = original_output_sample_rate_;
ESP_LOGI(TAG, "Resetting to original output sample rate: %d Hz", sample_rate); ESP_LOGI(TAG, "Resetting to original output sample rate: %d Hz", sample_rate);
}else{ } else {
ESP_LOGW(TAG, "Original sample rate not available, cannot reset"); ESP_LOGW(TAG, "Original sample rate not available, cannot reset");
return false; return false;
} }
} }
if (sample_rate <= 0 || sample_rate > 192000){ if (sample_rate <= 0 || sample_rate > 192000) {
ESP_LOGE(TAG, "Invalid sample rate: %d", sample_rate); ESP_LOGE(TAG, "Invalid sample rate: %d", sample_rate);
return false; return false;
} }
if (output_sample_rate_ == sample_rate){ if (output_sample_rate_ == sample_rate) {
ESP_LOGI(TAG, "Sample rate already set to %d Hz", sample_rate); ESP_LOGI(TAG, "Sample rate already set to %d Hz", sample_rate);
return true; return true;
} }
if (tx_handle_ == nullptr){ if (tx_handle_ == nullptr) {
ESP_LOGW(TAG, "TX handle is null, only updating sample rate variable"); ESP_LOGW(TAG, "TX handle is null, only updating sample rate variable");
output_sample_rate_ = sample_rate; output_sample_rate_ = sample_rate;
return true; return true;
@@ -110,14 +108,13 @@ bool AudioCodec::SetOutputSampleRate(int sample_rate){
// 先尝试禁用 I2S 通道(如果已启用的话) // 先尝试禁用 I2S 通道(如果已启用的话)
bool was_enabled = false; bool was_enabled = false;
esp_err_t disable_ret = i2s_channel_disable(tx_handle_); esp_err_t disable_ret = i2s_channel_disable(tx_handle_);
if (disable_ret == ESP_OK){ if (disable_ret == ESP_OK) {
was_enabled = true; was_enabled = true;
ESP_LOGI(TAG, "Disabled I2S TX channel for reconfiguration"); ESP_LOGI(TAG, "Disabled I2S TX channel for reconfiguration");
} } else if (disable_ret == ESP_ERR_INVALID_STATE) {
else if (disable_ret == ESP_ERR_INVALID_STATE){
// 通道可能已经是禁用状态,这是正常的 // 通道可能已经是禁用状态,这是正常的
ESP_LOGI(TAG, "I2S TX channel was already disabled"); ESP_LOGI(TAG, "I2S TX channel was already disabled");
}else{ } else {
ESP_LOGW(TAG, "Failed to disable I2S TX channel: %s", esp_err_to_name(disable_ret)); ESP_LOGW(TAG, "Failed to disable I2S TX channel: %s", esp_err_to_name(disable_ret));
} }
@@ -135,17 +132,17 @@ bool AudioCodec::SetOutputSampleRate(int sample_rate){
// 重新启用通道(无论之前是什么状态,现在都需要启用以便播放音频) // 重新启用通道(无论之前是什么状态,现在都需要启用以便播放音频)
esp_err_t enable_ret = i2s_channel_enable(tx_handle_); esp_err_t enable_ret = i2s_channel_enable(tx_handle_);
if (enable_ret != ESP_OK){ if (enable_ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to enable I2S TX channel: %s", esp_err_to_name(enable_ret)); ESP_LOGE(TAG, "Failed to enable I2S TX channel: %s", esp_err_to_name(enable_ret));
}else{ } else {
ESP_LOGI(TAG, "Enabled I2S TX channel"); ESP_LOGI(TAG, "Enabled I2S TX channel");
} }
if (ret == ESP_OK){ if (ret == ESP_OK) {
output_sample_rate_ = sample_rate; output_sample_rate_ = sample_rate;
ESP_LOGI(TAG, "Successfully changed output sample rate to %d Hz", sample_rate); ESP_LOGI(TAG, "Successfully changed output sample rate to %d Hz", sample_rate);
return true; return true;
}else{ } else {
ESP_LOGE(TAG, "Failed to change sample rate to %d Hz: %s", sample_rate, esp_err_to_name(ret)); ESP_LOGE(TAG, "Failed to change sample rate to %d Hz: %s", sample_rate, esp_err_to_name(ret));
return false; return false;
} }
+2 -1
View File
@@ -5,13 +5,14 @@
#include <vector> #include <vector>
#include <functional> #include <functional>
#include <model_path.h>
#include "audio_codec.h" #include "audio_codec.h"
class AudioProcessor { class AudioProcessor {
public: public:
virtual ~AudioProcessor() = default; virtual ~AudioProcessor() = default;
virtual void Initialize(AudioCodec* codec, int frame_duration_ms) = 0; virtual void Initialize(AudioCodec* codec, int frame_duration_ms, srmodel_list_t* models_list) = 0;
virtual void Feed(std::vector<int16_t>&& data) = 0; virtual void Feed(std::vector<int16_t>&& data) = 0;
virtual void Start() = 0; virtual void Start() = 0;
virtual void Stop() = 0; virtual void Stop() = 0;
+45 -28
View File
@@ -8,12 +8,11 @@
#include "processors/no_audio_processor.h" #include "processors/no_audio_processor.h"
#endif #endif
#if CONFIG_USE_AFE_WAKE_WORD #if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4
#include "wake_words/afe_wake_word.h" #include "wake_words/afe_wake_word.h"
#elif CONFIG_USE_ESP_WAKE_WORD
#include "wake_words/esp_wake_word.h"
#elif CONFIG_USE_CUSTOM_WAKE_WORD
#include "wake_words/custom_wake_word.h" #include "wake_words/custom_wake_word.h"
#else
#include "wake_words/esp_wake_word.h"
#endif #endif
#define TAG "AudioService" #define TAG "AudioService"
@@ -50,16 +49,6 @@ void AudioService::Initialize(AudioCodec* codec) {
audio_processor_ = std::make_unique<NoAudioProcessor>(); audio_processor_ = std::make_unique<NoAudioProcessor>();
#endif #endif
#if CONFIG_USE_AFE_WAKE_WORD
wake_word_ = std::make_unique<AfeWakeWord>();
#elif CONFIG_USE_ESP_WAKE_WORD
wake_word_ = std::make_unique<EspWakeWord>();
#elif CONFIG_USE_CUSTOM_WAKE_WORD
wake_word_ = std::make_unique<CustomWakeWord>();
#else
wake_word_ = nullptr;
#endif
audio_processor_->OnOutput([this](std::vector<int16_t>&& data) { audio_processor_->OnOutput([this](std::vector<int16_t>&& data) {
PushTaskToEncodeQueue(kAudioTaskTypeEncodeToSendQueue, std::move(data)); PushTaskToEncodeQueue(kAudioTaskTypeEncodeToSendQueue, std::move(data));
}); });
@@ -71,14 +60,6 @@ void AudioService::Initialize(AudioCodec* codec) {
} }
}); });
if (wake_word_) {
wake_word_->OnWakeWordDetected([this](const std::string& wake_word) {
if (callbacks_.on_wake_word_detected) {
callbacks_.on_wake_word_detected(wake_word);
}
});
}
esp_timer_create_args_t audio_power_timer_args = { esp_timer_create_args_t audio_power_timer_args = {
.callback = [](void* arg) { .callback = [](void* arg) {
AudioService* audio_service = (AudioService*)arg; AudioService* audio_service = (AudioService*)arg;
@@ -104,14 +85,14 @@ void AudioService::Start() {
AudioService* audio_service = (AudioService*)arg; AudioService* audio_service = (AudioService*)arg;
audio_service->AudioInputTask(); audio_service->AudioInputTask();
vTaskDelete(NULL); vTaskDelete(NULL);
}, "audio_input", 2048 * 3, this, 8, &audio_input_task_handle_, 1); }, "audio_input", 2048 * 3, this, 8, &audio_input_task_handle_, 0);
/* Start the audio output task */ /* Start the audio output task */
xTaskCreate([](void* arg) { xTaskCreate([](void* arg) {
AudioService* audio_service = (AudioService*)arg; AudioService* audio_service = (AudioService*)arg;
audio_service->AudioOutputTask(); audio_service->AudioOutputTask();
vTaskDelete(NULL); vTaskDelete(NULL);
}, "audio_output", 2048 * 2, this, 3, &audio_output_task_handle_); }, "audio_output", 2048 * 2, this, 4, &audio_output_task_handle_);
#else #else
/* Start the audio input task */ /* Start the audio input task */
xTaskCreate([](void* arg) { xTaskCreate([](void* arg) {
@@ -125,7 +106,7 @@ void AudioService::Start() {
AudioService* audio_service = (AudioService*)arg; AudioService* audio_service = (AudioService*)arg;
audio_service->AudioOutputTask(); audio_service->AudioOutputTask();
vTaskDelete(NULL); vTaskDelete(NULL);
}, "audio_output", 2048, this, 3, &audio_output_task_handle_); }, "audio_output", 2048, this, 4, &audio_output_task_handle_);
#endif #endif
/* Start the opus codec task */ /* Start the opus codec task */
@@ -479,7 +460,7 @@ void AudioService::EnableWakeWordDetection(bool enable) {
ESP_LOGD(TAG, "%s wake word detection", enable ? "Enabling" : "Disabling"); ESP_LOGD(TAG, "%s wake word detection", enable ? "Enabling" : "Disabling");
if (enable) { if (enable) {
if (!wake_word_initialized_) { if (!wake_word_initialized_) {
if (!wake_word_->Initialize(codec_)) { if (!wake_word_->Initialize(codec_, models_list_)) {
ESP_LOGE(TAG, "Failed to initialize wake word"); ESP_LOGE(TAG, "Failed to initialize wake word");
return; return;
} }
@@ -497,7 +478,7 @@ void AudioService::EnableVoiceProcessing(bool enable) {
ESP_LOGD(TAG, "%s voice processing", enable ? "Enabling" : "Disabling"); ESP_LOGD(TAG, "%s voice processing", enable ? "Enabling" : "Disabling");
if (enable) { if (enable) {
if (!audio_processor_initialized_) { if (!audio_processor_initialized_) {
audio_processor_->Initialize(codec_, OPUS_FRAME_DURATION_MS); audio_processor_->Initialize(codec_, OPUS_FRAME_DURATION_MS, models_list_);
audio_processor_initialized_ = true; audio_processor_initialized_ = true;
} }
@@ -528,7 +509,7 @@ void AudioService::EnableAudioTesting(bool enable) {
void AudioService::EnableDeviceAec(bool enable) { void AudioService::EnableDeviceAec(bool enable) {
ESP_LOGI(TAG, "%s device AEC", enable ? "Enabling" : "Disabling"); ESP_LOGI(TAG, "%s device AEC", enable ? "Enabling" : "Disabling");
if (!audio_processor_initialized_) { if (!audio_processor_initialized_) {
audio_processor_->Initialize(codec_, OPUS_FRAME_DURATION_MS); audio_processor_->Initialize(codec_, OPUS_FRAME_DURATION_MS, models_list_);
audio_processor_initialized_ = true; audio_processor_initialized_ = true;
} }
@@ -668,6 +649,42 @@ void AudioService::CheckAndUpdateAudioPowerState() {
} }
} }
void AudioService::SetModelsList(srmodel_list_t* models_list) {
models_list_ = models_list;
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4
if (esp_srmodel_filter(models_list_, ESP_MN_PREFIX, NULL) != nullptr) {
wake_word_ = std::make_unique<CustomWakeWord>();
} else if (esp_srmodel_filter(models_list_, ESP_WN_PREFIX, NULL) != nullptr) {
wake_word_ = std::make_unique<AfeWakeWord>();
} else {
wake_word_ = nullptr;
}
#else
if (esp_srmodel_filter(models_list_, ESP_WN_PREFIX, NULL) != nullptr) {
wake_word_ = std::make_unique<EspWakeWord>();
} else {
wake_word_ = nullptr;
}
#endif
if (wake_word_) {
wake_word_->OnWakeWordDetected([this](const std::string& wake_word) {
if (callbacks_.on_wake_word_detected) {
callbacks_.on_wake_word_detected(wake_word);
}
});
}
}
bool AudioService::IsAfeWakeWord() {
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4
return wake_word_ != nullptr && dynamic_cast<AfeWakeWord*>(wake_word_.get()) != nullptr;
#else
return false;
#endif
}
void AudioService::UpdateOutputTimestamp() { void AudioService::UpdateOutputTimestamp() {
last_output_time_ = std::chrono::steady_clock::now(); last_output_time_ = std::chrono::steady_clock::now();
} }
+4 -1
View File
@@ -11,6 +11,7 @@
#include <freertos/task.h> #include <freertos/task.h>
#include <freertos/event_groups.h> #include <freertos/event_groups.h>
#include <esp_timer.h> #include <esp_timer.h>
#include <model_path.h>
#include <opus_encoder.h> #include <opus_encoder.h>
#include <opus_decoder.h> #include <opus_decoder.h>
@@ -93,6 +94,7 @@ public:
bool IsIdle(); bool IsIdle();
bool IsWakeWordRunning() const { return xEventGroupGetBits(event_group_) & AS_EVENT_WAKE_WORD_RUNNING; } bool IsWakeWordRunning() const { return xEventGroupGetBits(event_group_) & AS_EVENT_WAKE_WORD_RUNNING; }
bool IsAudioProcessorRunning() const { return xEventGroupGetBits(event_group_) & AS_EVENT_AUDIO_PROCESSOR_RUNNING; } bool IsAudioProcessorRunning() const { return xEventGroupGetBits(event_group_) & AS_EVENT_AUDIO_PROCESSOR_RUNNING; }
bool IsAfeWakeWord();
void EnableWakeWordDetection(bool enable); void EnableWakeWordDetection(bool enable);
void EnableVoiceProcessing(bool enable); void EnableVoiceProcessing(bool enable);
@@ -106,7 +108,7 @@ public:
void PlaySound(const std::string_view& sound); void PlaySound(const std::string_view& sound);
bool ReadAudioData(std::vector<int16_t>& data, int sample_rate, int samples); bool ReadAudioData(std::vector<int16_t>& data, int sample_rate, int samples);
void ResetDecoder(); void ResetDecoder();
void SetModelsList(srmodel_list_t* models_list);
void UpdateOutputTimestamp(); void UpdateOutputTimestamp();
private: private:
@@ -121,6 +123,7 @@ private:
OpusResampler reference_resampler_; OpusResampler reference_resampler_;
OpusResampler output_resampler_; OpusResampler output_resampler_;
DebugStatistics debug_statistics_; DebugStatistics debug_statistics_;
srmodel_list_t* models_list_ = nullptr;
EventGroupHandle_t event_group_; EventGroupHandle_t event_group_;
+20 -8
View File
@@ -6,13 +6,14 @@
Es8388AudioCodec::Es8388AudioCodec(void* i2c_master_handle, i2c_port_t i2c_port, int input_sample_rate, int output_sample_rate, Es8388AudioCodec::Es8388AudioCodec(void* i2c_master_handle, i2c_port_t i2c_port, int input_sample_rate, int output_sample_rate,
gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din, gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din,
gpio_num_t pa_pin, uint8_t es8388_addr) { gpio_num_t pa_pin, uint8_t es8388_addr, bool input_reference) {
duplex_ = true; // 是否双工 duplex_ = true; // 是否双工
input_reference_ = false; // 是否使用参考输入,实现回声消除 input_reference_ = input_reference; // 是否使用参考输入,实现回声消除
input_channels_ = 1; // 输入通道数 input_channels_ = input_reference_ ? 2 : 1; // 输入通道数
input_sample_rate_ = input_sample_rate; input_sample_rate_ = input_sample_rate;
output_sample_rate_ = output_sample_rate; output_sample_rate_ = output_sample_rate;
pa_pin_ = pa_pin; CreateDuplexChannels(mclk, bclk, ws, dout, din); pa_pin_ = pa_pin;
CreateDuplexChannels(mclk, bclk, ws, dout, din);
// Do initialize of related interface: data_if, ctrl_if and gpio_if // Do initialize of related interface: data_if, ctrl_if and gpio_if
audio_codec_i2s_cfg_t i2s_cfg = { audio_codec_i2s_cfg_t i2s_cfg = {
@@ -144,13 +145,21 @@ void Es8388AudioCodec::EnableInput(bool enable) {
if (enable) { if (enable) {
esp_codec_dev_sample_info_t fs = { esp_codec_dev_sample_info_t fs = {
.bits_per_sample = 16, .bits_per_sample = 16,
.channel = 1, .channel = (uint8_t) input_channels_,
.channel_mask = 0, .channel_mask = ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0),
.sample_rate = (uint32_t)input_sample_rate_, .sample_rate = (uint32_t)input_sample_rate_,
.mclk_multiple = 0, .mclk_multiple = 0,
}; };
if (input_reference_) {
fs.channel_mask |= ESP_CODEC_DEV_MAKE_CHANNEL_MASK(1);
}
ESP_ERROR_CHECK(esp_codec_dev_open(input_dev_, &fs)); ESP_ERROR_CHECK(esp_codec_dev_open(input_dev_, &fs));
ESP_ERROR_CHECK(esp_codec_dev_set_in_gain(input_dev_, 24.0)); if (input_reference_) {
uint8_t gain = (11 << 4) + 0;
ctrl_if_->write_reg(ctrl_if_, 0x09, 1, &gain, 1);
}else{
ESP_ERROR_CHECK(esp_codec_dev_set_in_gain(input_dev_, 24.0));
}
} else { } else {
ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_)); ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_));
} }
@@ -175,6 +184,9 @@ void Es8388AudioCodec::EnableOutput(bool enable) {
// Set analog output volume to 0dB, default is -45dB // Set analog output volume to 0dB, default is -45dB
uint8_t reg_val = 30; // 0dB uint8_t reg_val = 30; // 0dB
if(input_reference_){
reg_val = 27;
}
uint8_t regs[] = { 46, 47, 48, 49 }; // HP_LVOL, HP_RVOL, SPK_LVOL, SPK_RVOL uint8_t regs[] = { 46, 47, 48, 49 }; // HP_LVOL, HP_RVOL, SPK_LVOL, SPK_RVOL
for (uint8_t reg : regs) { for (uint8_t reg : regs) {
ctrl_if_->write_reg(ctrl_if_, reg, 1, &reg_val, 1); ctrl_if_->write_reg(ctrl_if_, reg, 1, &reg_val, 1);
@@ -200,7 +212,7 @@ int Es8388AudioCodec::Read(int16_t* dest, int samples) {
} }
int Es8388AudioCodec::Write(const int16_t* data, int samples) { int Es8388AudioCodec::Write(const int16_t* data, int samples) {
if (output_enabled_) { if (output_enabled_ && output_dev_ && data != nullptr) {
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_codec_dev_write(output_dev_, (void*)data, samples * sizeof(int16_t))); ESP_ERROR_CHECK_WITHOUT_ABORT(esp_codec_dev_write(output_dev_, (void*)data, samples * sizeof(int16_t)));
} }
return samples; return samples;
+1 -1
View File
@@ -29,7 +29,7 @@ private:
public: public:
Es8388AudioCodec(void* i2c_master_handle, i2c_port_t i2c_port, int input_sample_rate, int output_sample_rate, Es8388AudioCodec(void* i2c_master_handle, i2c_port_t i2c_port, int input_sample_rate, int output_sample_rate,
gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din, gpio_num_t mclk, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din,
gpio_num_t pa_pin, uint8_t es8388_addr); gpio_num_t pa_pin, uint8_t es8388_addr, bool input_reference = false);
virtual ~Es8388AudioCodec(); virtual ~Es8388AudioCodec();
virtual void SetOutputVolume(int volume) override; virtual void SetOutputVolume(int volume) override;
+78
View File
@@ -214,6 +214,84 @@ NoAudioCodecSimplex::NoAudioCodecSimplex(int input_sample_rate, int output_sampl
ESP_LOGI(TAG, "Simplex channels created"); ESP_LOGI(TAG, "Simplex channels created");
} }
NoAudioCodecSimplexPdm::NoAudioCodecSimplexPdm(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, i2s_std_slot_mask_t spk_slot_mask,gpio_num_t mic_sck, gpio_num_t mic_din) {
duplex_ = false;
input_sample_rate_ = input_sample_rate;
output_sample_rate_ = output_sample_rate;
// Create a new channel for speaker
i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG((i2s_port_t)1, I2S_ROLE_MASTER);
tx_chan_cfg.dma_desc_num = AUDIO_CODEC_DMA_DESC_NUM;
tx_chan_cfg.dma_frame_num = AUDIO_CODEC_DMA_FRAME_NUM;
tx_chan_cfg.auto_clear_after_cb = true;
tx_chan_cfg.auto_clear_before_cb = false;
tx_chan_cfg.intr_priority = 0;
ESP_ERROR_CHECK(i2s_new_channel(&tx_chan_cfg, &tx_handle_, NULL));
i2s_std_config_t tx_std_cfg = {
.clk_cfg = {
.sample_rate_hz = (uint32_t)output_sample_rate_,
.clk_src = I2S_CLK_SRC_DEFAULT,
.mclk_multiple = I2S_MCLK_MULTIPLE_256,
#ifdef I2S_HW_VERSION_2
.ext_clk_freq_hz = 0,
#endif
},
.slot_cfg = {
.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT,
.slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO,
.slot_mode = I2S_SLOT_MODE_MONO,
.slot_mask = spk_slot_mask,
.ws_width = I2S_DATA_BIT_WIDTH_32BIT,
.ws_pol = false,
.bit_shift = true,
#ifdef I2S_HW_VERSION_2
.left_align = true,
.big_endian = false,
.bit_order_lsb = false
#endif
},
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = spk_bclk,
.ws = spk_ws,
.dout = spk_dout,
.din = I2S_GPIO_UNUSED,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false,
},
},
};
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &tx_std_cfg));
#if SOC_I2S_SUPPORTS_PDM_RX
// Create a new channel for MIC in PDM mode
i2s_chan_config_t rx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG((i2s_port_t)0, I2S_ROLE_MASTER);
ESP_ERROR_CHECK(i2s_new_channel(&rx_chan_cfg, NULL, &rx_handle_));
i2s_pdm_rx_config_t pdm_rx_cfg = {
.clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG((uint32_t)input_sample_rate_),
/* The data bit-width of PDM mode is fixed to 16 */
.slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO),
.gpio_cfg = {
.clk = mic_sck,
.din = mic_din,
.invert_flags = {
.clk_inv = false,
},
},
};
ESP_ERROR_CHECK(i2s_channel_init_pdm_rx_mode(rx_handle_, &pdm_rx_cfg));
#else
ESP_LOGE(TAG, "PDM is not supported");
#endif
ESP_LOGI(TAG, "Simplex channels created");
}
NoAudioCodecSimplexPdm::NoAudioCodecSimplexPdm(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, gpio_num_t mic_sck, gpio_num_t mic_din) { NoAudioCodecSimplexPdm::NoAudioCodecSimplexPdm(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, gpio_num_t mic_sck, gpio_num_t mic_din) {
duplex_ = false; duplex_ = false;
input_sample_rate_ = input_sample_rate; input_sample_rate_ = input_sample_rate;
+1
View File
@@ -32,6 +32,7 @@ public:
class NoAudioCodecSimplexPdm : public NoAudioCodec { class NoAudioCodecSimplexPdm : public NoAudioCodec {
public: public:
NoAudioCodecSimplexPdm(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, gpio_num_t mic_sck, gpio_num_t mic_din); NoAudioCodecSimplexPdm(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, gpio_num_t mic_sck, gpio_num_t mic_din);
NoAudioCodecSimplexPdm(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, i2s_std_slot_mask_t spk_slot_mask, gpio_num_t mic_sck, gpio_num_t mic_din);
int Read(int16_t* dest, int samples); int Read(int16_t* dest, int samples);
}; };
+8 -4
View File
@@ -10,7 +10,7 @@ AfeAudioProcessor::AfeAudioProcessor()
event_group_ = xEventGroupCreate(); event_group_ = xEventGroupCreate();
} }
void AfeAudioProcessor::Initialize(AudioCodec* codec, int frame_duration_ms) { void AfeAudioProcessor::Initialize(AudioCodec* codec, int frame_duration_ms, srmodel_list_t* models_list) {
codec_ = codec; codec_ = codec;
frame_samples_ = frame_duration_ms * 16000 / 1000; frame_samples_ = frame_duration_ms * 16000 / 1000;
@@ -27,7 +27,13 @@ void AfeAudioProcessor::Initialize(AudioCodec* codec, int frame_duration_ms) {
input_format.push_back('R'); input_format.push_back('R');
} }
srmodel_list_t *models = esp_srmodel_init("model"); srmodel_list_t *models;
if (models_list == nullptr) {
models = esp_srmodel_init("model");
} else {
models = models_list;
}
char* ns_model_name = esp_srmodel_filter(models, ESP_NSNET_PREFIX, NULL); char* ns_model_name = esp_srmodel_filter(models, ESP_NSNET_PREFIX, NULL);
char* vad_model_name = esp_srmodel_filter(models, ESP_VADN_PREFIX, NULL); char* vad_model_name = esp_srmodel_filter(models, ESP_VADN_PREFIX, NULL);
@@ -47,8 +53,6 @@ void AfeAudioProcessor::Initialize(AudioCodec* codec, int frame_duration_ms) {
afe_config->ns_init = false; afe_config->ns_init = false;
} }
afe_config->afe_perferred_core = 1;
afe_config->afe_perferred_priority = 1;
afe_config->agc_init = false; afe_config->agc_init = false;
afe_config->memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM; afe_config->memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM;
+1 -1
View File
@@ -18,7 +18,7 @@ public:
AfeAudioProcessor(); AfeAudioProcessor();
~AfeAudioProcessor(); ~AfeAudioProcessor();
void Initialize(AudioCodec* codec, int frame_duration_ms) override; void Initialize(AudioCodec* codec, int frame_duration_ms, srmodel_list_t* models_list) override;
void Feed(std::vector<int16_t>&& data) override; void Feed(std::vector<int16_t>&& data) override;
void Start() override; void Start() override;
void Stop() override; void Stop() override;
+1 -1
View File
@@ -3,7 +3,7 @@
#define TAG "NoAudioProcessor" #define TAG "NoAudioProcessor"
void NoAudioProcessor::Initialize(AudioCodec* codec, int frame_duration_ms) { void NoAudioProcessor::Initialize(AudioCodec* codec, int frame_duration_ms, srmodel_list_t* models_list) {
codec_ = codec; codec_ = codec;
frame_samples_ = frame_duration_ms * 16000 / 1000; frame_samples_ = frame_duration_ms * 16000 / 1000;
} }
+1 -1
View File
@@ -12,7 +12,7 @@ public:
NoAudioProcessor() = default; NoAudioProcessor() = default;
~NoAudioProcessor() = default; ~NoAudioProcessor() = default;
void Initialize(AudioCodec* codec, int frame_duration_ms) override; void Initialize(AudioCodec* codec, int frame_duration_ms, srmodel_list_t* models_list) override;
void Feed(std::vector<int16_t>&& data) override; void Feed(std::vector<int16_t>&& data) override;
void Start() override; void Start() override;
void Stop() override; void Stop() override;
+2 -1
View File
@@ -5,13 +5,14 @@
#include <vector> #include <vector>
#include <functional> #include <functional>
#include <model_path.h>
#include "audio_codec.h" #include "audio_codec.h"
class WakeWord { class WakeWord {
public: public:
virtual ~WakeWord() = default; virtual ~WakeWord() = default;
virtual bool Initialize(AudioCodec* codec) = 0; virtual bool Initialize(AudioCodec* codec, srmodel_list_t* models_list) = 0;
virtual void Feed(const std::vector<int16_t>& data) = 0; virtual void Feed(const std::vector<int16_t>& data) = 0;
virtual void OnWakeWordDetected(std::function<void(const std::string& wake_word)> callback) = 0; virtual void OnWakeWordDetected(std::function<void(const std::string& wake_word)> callback) = 0;
virtual void Start() = 0; virtual void Start() = 0;
+7 -2
View File
@@ -36,11 +36,16 @@ AfeWakeWord::~AfeWakeWord() {
vEventGroupDelete(event_group_); vEventGroupDelete(event_group_);
} }
bool AfeWakeWord::Initialize(AudioCodec* codec) { bool AfeWakeWord::Initialize(AudioCodec* codec, srmodel_list_t* models_list) {
codec_ = codec; codec_ = codec;
int ref_num = codec_->input_reference() ? 1 : 0; int ref_num = codec_->input_reference() ? 1 : 0;
models_ = esp_srmodel_init("model"); if (models_list == nullptr) {
models_ = esp_srmodel_init("model");
} else {
models_ = models_list;
}
if (models_ == nullptr || models_->num == -1) { if (models_ == nullptr || models_->num == -1) {
ESP_LOGE(TAG, "Failed to initialize wakenet model"); ESP_LOGE(TAG, "Failed to initialize wakenet model");
return false; return false;
+1 -1
View File
@@ -24,7 +24,7 @@ public:
AfeWakeWord(); AfeWakeWord();
~AfeWakeWord(); ~AfeWakeWord();
bool Initialize(AudioCodec* codec); bool Initialize(AudioCodec* codec, srmodel_list_t* models_list);
void Feed(const std::vector<int16_t>& data); void Feed(const std::vector<int16_t>& data);
void OnWakeWordDetected(std::function<void(const std::string& wake_word)> callback); void OnWakeWordDetected(std::function<void(const std::string& wake_word)> callback);
void Start(); void Start();
+85 -20
View File
@@ -1,11 +1,13 @@
#include "custom_wake_word.h" #include "custom_wake_word.h"
#include "audio_service.h" #include "audio_service.h"
#include "system_info.h" #include "system_info.h"
#include "assets.h"
#include <esp_log.h> #include <esp_log.h>
#include "esp_mn_iface.h" #include <esp_mn_iface.h>
#include "esp_mn_models.h" #include <esp_mn_models.h>
#include "esp_mn_speech_commands.h" #include <esp_mn_speech_commands.h>
#include <cJSON.h>
#define TAG "CustomWakeWord" #define TAG "CustomWakeWord"
@@ -34,29 +36,90 @@ CustomWakeWord::~CustomWakeWord() {
} }
} }
bool CustomWakeWord::Initialize(AudioCodec* codec) { void CustomWakeWord::ParseWakenetModelConfig() {
codec_ = codec; // Read index.json
auto& assets = Assets::GetInstance();
void* ptr = nullptr;
size_t size = 0;
if (!assets.GetAssetData("index.json", ptr, size)) {
ESP_LOGE(TAG, "Failed to read index.json");
return;
}
cJSON* root = cJSON_ParseWithLength(static_cast<char*>(ptr), size);
if (root == nullptr) {
ESP_LOGE(TAG, "Failed to parse index.json");
return;
}
cJSON* multinet_model = cJSON_GetObjectItem(root, "multinet_model");
if (cJSON_IsObject(multinet_model)) {
cJSON* language = cJSON_GetObjectItem(multinet_model, "language");
cJSON* duration = cJSON_GetObjectItem(multinet_model, "duration");
cJSON* threshold = cJSON_GetObjectItem(multinet_model, "threshold");
cJSON* commands = cJSON_GetObjectItem(multinet_model, "commands");
if (cJSON_IsString(language)) {
language_ = language->valuestring;
}
if (cJSON_IsNumber(duration)) {
duration_ = duration->valueint;
}
if (cJSON_IsNumber(threshold)) {
threshold_ = threshold->valuedouble;
}
if (cJSON_IsArray(commands)) {
for (int i = 0; i < cJSON_GetArraySize(commands); i++) {
cJSON* command = cJSON_GetArrayItem(commands, i);
if (cJSON_IsObject(command)) {
cJSON* command_name = cJSON_GetObjectItem(command, "command");
cJSON* text = cJSON_GetObjectItem(command, "text");
cJSON* action = cJSON_GetObjectItem(command, "action");
if (cJSON_IsString(command_name) && cJSON_IsString(text) && cJSON_IsString(action)) {
commands_.push_back({command_name->valuestring, text->valuestring, action->valuestring});
ESP_LOGI(TAG, "Command: %s, Text: %s, Action: %s", command_name->valuestring, text->valuestring, action->valuestring);
}
}
}
}
}
cJSON_Delete(root);
}
bool CustomWakeWord::Initialize(AudioCodec* codec, srmodel_list_t* models_list) {
codec_ = codec;
commands_.clear();
if (models_list == nullptr) {
language_ = "cn";
models_ = esp_srmodel_init("model");
#ifdef CONFIG_CUSTOM_WAKE_WORD
threshold_ = CONFIG_CUSTOM_WAKE_WORD_THRESHOLD / 100.0f;
commands_.push_back({CONFIG_CUSTOM_WAKE_WORD, CONFIG_CUSTOM_WAKE_WORD_DISPLAY, "wake"});
#endif
} else {
models_ = models_list;
ParseWakenetModelConfig();
}
models_ = esp_srmodel_init("model");
if (models_ == nullptr || models_->num == -1) { if (models_ == nullptr || models_->num == -1) {
ESP_LOGE(TAG, "Failed to initialize wakenet model"); ESP_LOGE(TAG, "Failed to initialize wakenet model");
return false; return false;
} }
// 初始化 multinet (命令词识别) // 初始化 multinet (命令词识别)
mn_name_ = esp_srmodel_filter(models_, ESP_MN_PREFIX, ESP_MN_CHINESE); mn_name_ = esp_srmodel_filter(models_, ESP_MN_PREFIX, language_.c_str());
if (mn_name_ == nullptr) { if (mn_name_ == nullptr) {
ESP_LOGE(TAG, "Failed to initialize multinet, mn_name is nullptr"); ESP_LOGE(TAG, "Failed to initialize multinet, mn_name is nullptr");
ESP_LOGI(TAG, "Please refer to https://pcn7cs20v8cr.feishu.cn/wiki/CpQjwQsCJiQSWSkYEvrcxcbVnwh to add custom wake word"); ESP_LOGI(TAG, "Please refer to https://pcn7cs20v8cr.feishu.cn/wiki/CpQjwQsCJiQSWSkYEvrcxcbVnwh to add custom wake word");
return false; return false;
} }
ESP_LOGI(TAG, "multinet: %s", mn_name_);
multinet_ = esp_mn_handle_from_name(mn_name_); multinet_ = esp_mn_handle_from_name(mn_name_);
multinet_model_data_ = multinet_->create(mn_name_, 3000); // 3 秒超时 multinet_model_data_ = multinet_->create(mn_name_, duration_);
multinet_->set_det_threshold(multinet_model_data_, CONFIG_CUSTOM_WAKE_WORD_THRESHOLD / 100.0f); multinet_->set_det_threshold(multinet_model_data_, threshold_);
esp_mn_commands_clear(); esp_mn_commands_clear();
esp_mn_commands_add(1, CONFIG_CUSTOM_WAKE_WORD); for (int i = 0; i < commands_.size(); i++) {
esp_mn_commands_add(i + 1, commands_[i].command.c_str());
}
esp_mn_commands_update(); esp_mn_commands_update();
multinet_->print_active_speech_commands(multinet_model_data_); multinet_->print_active_speech_commands(multinet_model_data_);
@@ -99,16 +162,18 @@ void CustomWakeWord::Feed(const std::vector<int16_t>& data) {
return; return;
} else if (mn_state == ESP_MN_STATE_DETECTED) { } else if (mn_state == ESP_MN_STATE_DETECTED) {
esp_mn_results_t *mn_result = multinet_->get_results(multinet_model_data_); esp_mn_results_t *mn_result = multinet_->get_results(multinet_model_data_);
ESP_LOGI(TAG, "Custom wake word detected: command_id=%d, string=%s, prob=%f", for (int i = 0; i < mn_result->num && running_; i++) {
mn_result->command_id[0], mn_result->string, mn_result->prob[0]); ESP_LOGI(TAG, "Custom wake word detected: command_id=%d, string=%s, prob=%f",
mn_result->command_id[i], mn_result->string, mn_result->prob[i]);
auto& command = commands_[mn_result->command_id[i] - 1];
if (command.action == "wake") {
last_detected_wake_word_ = command.text;
running_ = false;
if (mn_result->command_id[0] == 1) { if (wake_word_detected_callback_) {
last_detected_wake_word_ = CONFIG_CUSTOM_WAKE_WORD_DISPLAY; wake_word_detected_callback_(last_detected_wake_word_);
} }
running_ = false; }
if (wake_word_detected_callback_) {
wake_word_detected_callback_(last_detected_wake_word_);
} }
multinet_->clean(multinet_model_data_); multinet_->clean(multinet_model_data_);
} else if (mn_state == ESP_MN_STATE_TIMEOUT) { } else if (mn_state == ESP_MN_STATE_TIMEOUT) {
+12 -1
View File
@@ -22,7 +22,7 @@ public:
CustomWakeWord(); CustomWakeWord();
~CustomWakeWord(); ~CustomWakeWord();
bool Initialize(AudioCodec* codec); bool Initialize(AudioCodec* codec, srmodel_list_t* models_list);
void Feed(const std::vector<int16_t>& data); void Feed(const std::vector<int16_t>& data);
void OnWakeWordDetected(std::function<void(const std::string& wake_word)> callback); void OnWakeWordDetected(std::function<void(const std::string& wake_word)> callback);
void Start(); void Start();
@@ -33,11 +33,21 @@ public:
const std::string& GetLastDetectedWakeWord() const { return last_detected_wake_word_; } const std::string& GetLastDetectedWakeWord() const { return last_detected_wake_word_; }
private: private:
struct Command {
std::string command;
std::string text;
std::string action;
};
// multinet 相关成员变量 // multinet 相关成员变量
esp_mn_iface_t* multinet_ = nullptr; esp_mn_iface_t* multinet_ = nullptr;
model_iface_data_t* multinet_model_data_ = nullptr; model_iface_data_t* multinet_model_data_ = nullptr;
srmodel_list_t *models_ = nullptr; srmodel_list_t *models_ = nullptr;
char* mn_name_ = nullptr; char* mn_name_ = nullptr;
std::string language_ = "cn";
int duration_ = 3000;
float threshold_ = 0.2;
std::deque<Command> commands_;
std::function<void(const std::string& wake_word)> wake_word_detected_callback_; std::function<void(const std::string& wake_word)> wake_word_detected_callback_;
AudioCodec* codec_ = nullptr; AudioCodec* codec_ = nullptr;
@@ -53,6 +63,7 @@ private:
std::condition_variable wake_word_cv_; std::condition_variable wake_word_cv_;
void StoreWakeWordData(const std::vector<int16_t>& data); void StoreWakeWordData(const std::vector<int16_t>& data);
void ParseWakenetModelConfig();
}; };
#endif #endif
+7 -2
View File
@@ -14,10 +14,15 @@ EspWakeWord::~EspWakeWord() {
} }
} }
bool EspWakeWord::Initialize(AudioCodec* codec) { bool EspWakeWord::Initialize(AudioCodec* codec, srmodel_list_t* models_list) {
codec_ = codec; codec_ = codec;
wakenet_model_ = esp_srmodel_init("model"); if (models_list == nullptr) {
wakenet_model_ = esp_srmodel_init("model");
} else {
wakenet_model_ = models_list;
}
if (wakenet_model_ == nullptr || wakenet_model_->num == -1) { if (wakenet_model_ == nullptr || wakenet_model_->num == -1) {
ESP_LOGE(TAG, "Failed to initialize wakenet model"); ESP_LOGE(TAG, "Failed to initialize wakenet model");
return false; return false;
+1 -1
View File
@@ -18,7 +18,7 @@ public:
EspWakeWord(); EspWakeWord();
~EspWakeWord(); ~EspWakeWord();
bool Initialize(AudioCodec* codec); bool Initialize(AudioCodec* codec, srmodel_list_t* models_list);
void Feed(const std::vector<int16_t>& data); void Feed(const std::vector<int16_t>& data);
void OnWakeWordDetected(std::function<void(const std::string& wake_word)> callback); void OnWakeWordDetected(std::function<void(const std::string& wake_word)> callback);
void Start(); void Start();
BIN
View File
Binary file not shown.
+2 -11
View File
@@ -98,7 +98,7 @@ mkdir main/boards/my-custom-board
"sdkconfig_append": [ "sdkconfig_append": [
// 额外需要的编译配置 // 额外需要的编译配置
"CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y", "CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/8m.csv\"" "CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/8m.csv\""
] ]
} }
] ]
@@ -131,10 +131,6 @@ mkdir main/boards/my-custom-board
#define TAG "MyCustomBoard" #define TAG "MyCustomBoard"
// 声明字体
LV_FONT_DECLARE(font_puhui_16_4);
LV_FONT_DECLARE(font_awesome_16_4);
class MyCustomBoard : public WifiBoard { class MyCustomBoard : public WifiBoard {
private: private:
i2c_master_bus_handle_t codec_i2c_bus_; i2c_master_bus_handle_t codec_i2c_bus_;
@@ -212,12 +208,7 @@ private:
display_ = new SpiLcdDisplay(panel_io, panel, display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y,
DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
{
.text_font = &font_puhui_16_4,
.icon_font = &font_awesome_16_4,
.emoji_font = font_emoji_32_init(),
});
} }
// MCP Tools 初始化 // MCP Tools 初始化
@@ -20,10 +20,6 @@
#define TAG "atk_dnesp32s3_box" #define TAG "atk_dnesp32s3_box"
LV_FONT_DECLARE(font_puhui_20_4);
LV_FONT_DECLARE(font_awesome_20_4);
class ATK_NoAudioCodecDuplex : public NoAudioCodec { class ATK_NoAudioCodecDuplex : public NoAudioCodec {
public: public:
ATK_NoAudioCodecDuplex(int input_sample_rate, int output_sample_rate, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din) { ATK_NoAudioCodecDuplex(int input_sample_rate, int output_sample_rate, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din) {
@@ -84,7 +80,6 @@ public:
} }
}; };
class XL9555_IN : public I2cDevice { class XL9555_IN : public I2cDevice {
public: public:
XL9555_IN(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) { XL9555_IN(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) {
@@ -243,16 +238,7 @@ private:
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel, true)); ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel, true));
display_ = new SpiLcdDisplay(panel_io, panel, display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
{
.text_font = &font_puhui_20_4,
.icon_font = &font_awesome_20_4,
#if CONFIG_USE_WECHAT_MESSAGE_STYLE
.emoji_font = font_emoji_32_init(),
#else
.emoji_font = DISPLAY_HEIGHT >= 240 ? font_emoji_64_init() : font_emoji_32_init(),
#endif
});
} }
void InitializeButtons() { void InitializeButtons() {
@@ -20,9 +20,6 @@
#define TAG "atk_dnesp32s3_box0" #define TAG "atk_dnesp32s3_box0"
LV_FONT_DECLARE(font_puhui_20_4);
LV_FONT_DECLARE(font_awesome_20_4);
class atk_dnesp32s3_box0 : public WifiBoard { class atk_dnesp32s3_box0 : public WifiBoard {
private: private:
i2c_master_bus_handle_t i2c_bus_; i2c_master_bus_handle_t i2c_bus_;
@@ -294,7 +291,6 @@ private:
GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME); GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
}); });
} }
void InitializeSt7789Display() { void InitializeSt7789Display() {
@@ -325,12 +321,7 @@ private:
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
display_ = new SpiLcdDisplay(panel_io, panel, display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
{
.text_font = &font_puhui_20_4,
.icon_font = &font_awesome_20_4,
.emoji_font = DISPLAY_HEIGHT >= 240 ? font_emoji_64_init() : font_emoji_32_init(),
});
} }
public: public:
@@ -21,9 +21,6 @@
#define TAG "atk_dnesp32s3_box2_4g" #define TAG "atk_dnesp32s3_box2_4g"
LV_FONT_DECLARE(font_puhui_20_4);
LV_FONT_DECLARE(font_awesome_20_4);
class atk_dnesp32s3_box2_4g : public DualNetworkBoard { class atk_dnesp32s3_box2_4g : public DualNetworkBoard {
private: private:
i2c_master_bus_handle_t i2c_bus_; i2c_master_bus_handle_t i2c_bus_;
@@ -412,12 +409,7 @@ private:
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
display_ = new SpiLcdDisplay(panel_io, panel, display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
{
.text_font = &font_puhui_20_4,
.icon_font = &font_awesome_20_4,
.emoji_font = DISPLAY_HEIGHT >= 240 ? font_emoji_64_init() : font_emoji_32_init(),
});
} }
public: public:
@@ -21,9 +21,6 @@
#define TAG "atk_dnesp32s3_box2_wifi" #define TAG "atk_dnesp32s3_box2_wifi"
LV_FONT_DECLARE(font_puhui_20_4);
LV_FONT_DECLARE(font_awesome_20_4);
class atk_dnesp32s3_box2_wifi : public WifiBoard { class atk_dnesp32s3_box2_wifi : public WifiBoard {
private: private:
i2c_master_bus_handle_t i2c_bus_; i2c_master_bus_handle_t i2c_bus_;
@@ -392,12 +389,7 @@ private:
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
display_ = new SpiLcdDisplay(panel_io, panel, display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
{
.text_font = &font_puhui_20_4,
.icon_font = &font_awesome_20_4,
.emoji_font = DISPLAY_HEIGHT >= 240 ? font_emoji_64_init() : font_emoji_32_init(),
});
} }
public: public:
+1 -14
View File
@@ -16,9 +16,6 @@
#define TAG "atk_dnesp32s3" #define TAG "atk_dnesp32s3"
LV_FONT_DECLARE(font_puhui_20_4);
LV_FONT_DECLARE(font_awesome_20_4);
class XL9555 : public I2cDevice { class XL9555 : public I2cDevice {
public: public:
XL9555(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) { XL9555(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) {
@@ -47,7 +44,6 @@ public:
} }
}; };
class atk_dnesp32s3 : public WifiBoard { class atk_dnesp32s3 : public WifiBoard {
private: private:
i2c_master_bus_handle_t i2c_bus_; i2c_master_bus_handle_t i2c_bus_;
@@ -131,16 +127,7 @@ private:
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY); esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
display_ = new SpiLcdDisplay(panel_io, panel, display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
{
.text_font = &font_puhui_20_4,
.icon_font = &font_awesome_20_4,
#if CONFIG_USE_WECHAT_MESSAGE_STYLE
.emoji_font = font_emoji_32_init(),
#else
.emoji_font = DISPLAY_HEIGHT >= 240 ? font_emoji_64_init() : font_emoji_32_init(),
#endif
});
} }
// 初始化摄像头:ov2640 // 初始化摄像头:ov2640
@@ -20,9 +20,6 @@
#define TAG "atk_dnesp32s3m_4g" #define TAG "atk_dnesp32s3m_4g"
LV_FONT_DECLARE(font_puhui_16_4);
LV_FONT_DECLARE(font_awesome_16_4);
class atk_dnesp32s3m_4g : public Ml307Board { class atk_dnesp32s3m_4g : public Ml307Board {
private: private:
i2c_master_bus_handle_t i2c_bus_; i2c_master_bus_handle_t i2c_bus_;
@@ -167,12 +164,7 @@ private:
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
display_ = new SpiLcdDisplay(panel_io, panel, display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
{
.text_font = &font_puhui_16_4,
.icon_font = &font_awesome_16_4,
.emoji_font = font_emoji_32_init(),
});
} }
public: public:
@@ -19,9 +19,6 @@
#define TAG "atk_dnesp32s3m_wifi" #define TAG "atk_dnesp32s3m_wifi"
LV_FONT_DECLARE(font_puhui_16_4);
LV_FONT_DECLARE(font_awesome_16_4);
class atk_dnesp32s3m_wifi : public WifiBoard { class atk_dnesp32s3m_wifi : public WifiBoard {
private: private:
i2c_master_bus_handle_t i2c_bus_; i2c_master_bus_handle_t i2c_bus_;
@@ -177,12 +174,7 @@ private:
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
display_ = new SpiLcdDisplay(panel_io, panel, display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
{
.text_font = &font_puhui_16_4,
.icon_font = &font_awesome_16_4,
.emoji_font = font_emoji_32_init(),
});
} }
public: public:
+1 -1
View File
@@ -22,7 +22,7 @@ idf.py menuconfig
分别配置如下选项: 分别配置如下选项:
- `Xiaozhi Assistant``Board Type` → 选择 `AtomEchoS3R` - `Xiaozhi Assistant``Board Type` → 选择 `AtomEchoS3R`
- `Partition Table``Custom partition CSV file` → 删除原有内容,输入 `partitions/v1/8m.csv` - `Partition Table``Custom partition CSV file` → 删除原有内容,输入 `partitions/v2/8m.csv`
- `Serial flasher config``Flash size` → 选择 `8 MB` - `Serial flasher config``Flash size` → 选择 `8 MB`
- `Component config``ESP PSRAM``Support for external, SPI-connected RAM``SPI RAM config` → 选择 `Octal Mode PSRAM` - `Component config``ESP PSRAM``Support for external, SPI-connected RAM``SPI RAM config` → 选择 `Octal Mode PSRAM`
-1
View File
@@ -12,7 +12,6 @@
#define TAG "AtomEchoS3R" #define TAG "AtomEchoS3R"
class AtomEchoS3rBaseBoard : public WifiBoard { class AtomEchoS3rBaseBoard : public WifiBoard {
private: private:
i2c_master_bus_handle_t i2c_bus_; i2c_master_bus_handle_t i2c_bus_;
+1 -1
View File
@@ -5,7 +5,7 @@
"name": "atom-echos3r", "name": "atom-echos3r",
"sdkconfig_append": [ "sdkconfig_append": [
"CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y", "CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/8m.csv\"" "CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/8m.csv\""
] ]
} }
] ]
@@ -18,18 +18,6 @@ idf.py menuconfig
Xiaozhi Assistant -> Board Type -> AtomMatrix + Echo Base Xiaozhi Assistant -> Board Type -> AtomMatrix + Echo Base
``` ```
**修改 flash 大小:**
```
Serial flasher config -> Flash size -> 4 MB
```
**修改分区表:**
```
Partition Table -> Custom partition CSV file -> partitions/v1/4m.csv
```
**编译:** **编译:**
```bash ```bash
@@ -33,7 +33,6 @@ public:
} }
}; };
class AtomMatrixEchoBaseBoard : public WifiBoard { class AtomMatrixEchoBaseBoard : public WifiBoard {
private: private:
i2c_master_bus_handle_t i2c_bus_; i2c_master_bus_handle_t i2c_bus_;
@@ -87,7 +86,6 @@ private:
pi4ioe_->SetSpeakerMute(false); pi4ioe_->SetSpeakerMute(false);
} }
void InitializeButtons() { void InitializeButtons() {
face_button_.OnClick([this]() { face_button_.OnClick([this]() {
@@ -4,8 +4,6 @@
{ {
"name": "atommatrix-echo-base", "name": "atommatrix-echo-base",
"sdkconfig_append": [ "sdkconfig_append": [
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/4m.csv\""
] ]
} }
] ]
+1 -1
View File
@@ -33,7 +33,7 @@ Serial flasher config -> Flash size -> 8 MB
**修改分区表:** **修改分区表:**
``` ```
Partition Table -> Custom partition CSV file -> partitions/v1/8m.csv Partition Table -> Custom partition CSV file -> partitions/v2/8m.csv
``` ```
**关闭片外 PSRAM** **关闭片外 PSRAM**
@@ -16,9 +16,6 @@
#define TAG "AtomS3+EchoBase" #define TAG "AtomS3+EchoBase"
LV_FONT_DECLARE(font_puhui_16_4);
LV_FONT_DECLARE(font_awesome_16_4);
static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = { static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = {
// {cmd, { data }, data_size, delay_ms} // {cmd, { data }, data_size, delay_ms}
{0xfe, (uint8_t[]){0x00}, 0, 0}, {0xfe, (uint8_t[]){0x00}, 0, 0},
@@ -176,14 +173,8 @@ private:
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle)); ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
display_ = new SpiLcdDisplay(io_handle, panel_handle, display_ = new SpiLcdDisplay(io_handle, panel_handle,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
{
.text_font = &font_puhui_16_4,
.icon_font = &font_awesome_16_4,
.emoji_font = font_emoji_32_init(),
});
} }
void InitializeButtons() { void InitializeButtons() {
+1 -2
View File
@@ -5,9 +5,8 @@
"name": "atoms3-echo-base", "name": "atoms3-echo-base",
"sdkconfig_append": [ "sdkconfig_append": [
"CONFIG_SPIRAM=n", "CONFIG_SPIRAM=n",
"CONFIG_USE_AFE=n",
"CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y", "CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/8m.csv\"" "CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/8m.csv\""
] ]
} }
] ]
@@ -32,7 +32,7 @@ idf.py menuconfig
- `Xiaozhi Assistant``Board Type` → 选择 `AtomS3R CAM/M12 + Echo Base` - `Xiaozhi Assistant``Board Type` → 选择 `AtomS3R CAM/M12 + Echo Base`
- `Xiaozhi Assistant``IoT Protocol` → 选择 `MCP协议` 可开启摄像头识别功能 - `Xiaozhi Assistant``IoT Protocol` → 选择 `MCP协议` 可开启摄像头识别功能
- `Partition Table``Custom partition CSV file` → 删除原有内容,输入 `partitions/v1/8m.csv` - `Partition Table``Custom partition CSV file` → 删除原有内容,输入 `partitions/v2/8m.csv`
- `Serial flasher config``Flash size` → 选择 `8 MB` - `Serial flasher config``Flash size` → 选择 `8 MB`
`S` 保存,按 `Q` 退出。 `S` 保存,按 `Q` 退出。
@@ -5,7 +5,7 @@
"name": "atoms3r-cam-m12-echo-base", "name": "atoms3r-cam-m12-echo-base",
"sdkconfig_append": [ "sdkconfig_append": [
"CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y", "CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/8m.csv\"" "CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/8m.csv\""
] ]
} }
] ]
+1 -1
View File
@@ -27,7 +27,7 @@ Serial flasher config -> Flash size -> 8 MB
**修改分区表:** **修改分区表:**
``` ```
Partition Table -> Custom partition CSV file -> partitions/v1/8m.csv Partition Table -> Custom partition CSV file -> partitions/v2/8m.csv
``` ```
**修改 psram 配置:** **修改 psram 配置:**
@@ -23,9 +23,6 @@
#define PI4IOE_REG_IO_OUT 0x05 #define PI4IOE_REG_IO_OUT 0x05
#define PI4IOE_REG_IO_PULLUP 0x0D #define PI4IOE_REG_IO_PULLUP 0x0D
LV_FONT_DECLARE(font_puhui_16_4);
LV_FONT_DECLARE(font_awesome_16_4);
class Pi4ioe : public I2cDevice { class Pi4ioe : public I2cDevice {
public: public:
Pi4ioe(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) { Pi4ioe(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) {
@@ -114,6 +111,7 @@ private:
Display* display_ = nullptr; Display* display_ = nullptr;
Button boot_button_; Button boot_button_;
bool is_echo_base_connected_ = false; bool is_echo_base_connected_ = false;
void InitializeI2c() { void InitializeI2c() {
// Initialize I2C peripheral // Initialize I2C peripheral
i2c_master_bus_config_t i2c_bus_cfg = { i2c_master_bus_config_t i2c_bus_cfg = {
@@ -254,12 +252,7 @@ private:
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true)); ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
display_ = new SpiLcdDisplay(io_handle, panel_handle, display_ = new SpiLcdDisplay(io_handle, panel_handle,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
{
.text_font = &font_puhui_16_4,
.icon_font = &font_awesome_16_4,
.emoji_font = font_emoji_32_init(),
});
} }
void InitializeButtons() { void InitializeButtons() {
+1 -1
View File
@@ -5,7 +5,7 @@
"name": "atoms3r-echo-base", "name": "atoms3r-echo-base",
"sdkconfig_append": [ "sdkconfig_append": [
"CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y", "CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/8m.csv\"" "CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/8m.csv\""
] ]
} }
] ]
@@ -34,6 +34,8 @@
#define ASR_BUTTON_GPIO GPIO_NUM_19 #define ASR_BUTTON_GPIO GPIO_NUM_19
#define BUILTIN_LED_GPIO GPIO_NUM_2 #define BUILTIN_LED_GPIO GPIO_NUM_2
#define ML307_RX_PIN GPIO_NUM_16
#define ML307_TX_PIN GPIO_NUM_17
#ifdef CONFIG_LCD_ST7789_240X240_7PIN #ifdef CONFIG_LCD_ST7789_240X240_7PIN
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_22 #define DISPLAY_BACKLIGHT_PIN GPIO_NUM_22
@@ -4,8 +4,6 @@
{ {
"name": "bread-compact-esp32-lcd", "name": "bread-compact-esp32-lcd",
"sdkconfig_append": [ "sdkconfig_append": [
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/4m.csv\"",
"LCD_ST7789_240X240_7PIN=y" "LCD_ST7789_240X240_7PIN=y"
] ]
} }
@@ -1,4 +1,4 @@
#include "wifi_board.h" #include "dual_network_board.h"
#include "codecs/no_audio_codec.h" #include "codecs/no_audio_codec.h"
#include "display/lcd_display.h" #include "display/lcd_display.h"
#include "system_reset.h" #include "system_reset.h"
@@ -58,10 +58,7 @@ static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = {
#define TAG "ESP32-LCD-MarsbearSupport" #define TAG "ESP32-LCD-MarsbearSupport"
LV_FONT_DECLARE(font_puhui_14_1); class CompactWifiBoardLCD : public DualNetworkBoard {
LV_FONT_DECLARE(font_awesome_14_1);
class CompactWifiBoardLCD : public WifiBoard {
private: private:
Button boot_button_; Button boot_button_;
Button touch_button_; Button touch_button_;
@@ -115,7 +112,6 @@ private:
esp_lcd_panel_reset(panel); esp_lcd_panel_reset(panel);
esp_lcd_panel_init(panel); esp_lcd_panel_init(panel);
esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR); esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR);
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY); esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
@@ -124,16 +120,9 @@ private:
panel_config.vendor_config = &gc9107_vendor_config; panel_config.vendor_config = &gc9107_vendor_config;
#endif #endif
display_ = new SpiLcdDisplay(panel_io, panel, display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
{
.text_font = &font_puhui_14_1,
.icon_font = &font_awesome_14_1,
.emoji_font = font_emoji_32_init(),
});
} }
void InitializeButtons() { void InitializeButtons() {
// 配置 GPIO // 配置 GPIO
@@ -148,13 +137,25 @@ private:
boot_button_.OnClick([this]() { boot_button_.OnClick([this]() {
auto& app = Application::GetInstance(); auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) { if (GetNetworkType() == NetworkType::WIFI) {
ResetWifiConfiguration(); if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
// cast to WifiBoard
auto& wifi_board = static_cast<WifiBoard&>(GetCurrentBoard());
wifi_board.ResetWifiConfiguration();
}
} }
gpio_set_level(BUILTIN_LED_GPIO, 1); gpio_set_level(BUILTIN_LED_GPIO, 1);
app.ToggleChatState(); app.ToggleChatState();
}); });
boot_button_.OnDoubleClick([this]() {
auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateStarting || app.GetDeviceState() == kDeviceStateWifiConfiguring) {
SwitchNetworkType();
}
});
asr_button_.OnClick([this]() { asr_button_.OnClick([this]() {
std::string wake_word="你好小智"; std::string wake_word="你好小智";
Application::GetInstance().WakeWordInvoke(wake_word); Application::GetInstance().WakeWordInvoke(wake_word);
@@ -174,6 +175,7 @@ private:
public: public:
CompactWifiBoardLCD() : CompactWifiBoardLCD() :
DualNetworkBoard(ML307_TX_PIN, ML307_RX_PIN),
boot_button_(BOOT_BUTTON_GPIO), touch_button_(TOUCH_BUTTON_GPIO), asr_button_(ASR_BUTTON_GPIO) { boot_button_(BOOT_BUTTON_GPIO), touch_button_(TOUCH_BUTTON_GPIO), asr_button_(ASR_BUTTON_GPIO) {
InitializeSpi(); InitializeSpi();
InitializeLcdDisplay(); InitializeLcdDisplay();
-12
View File
@@ -18,18 +18,6 @@ idf.py menuconfig
Xiaozhi Assistant -> Board Type -> 面包板 ESP32 DevKit Xiaozhi Assistant -> Board Type -> 面包板 ESP32 DevKit
``` ```
**修改 flash 大小:**
```
Serial flasher config -> Flash size -> 4 MB
```
**修改分区表:**
```
Partition Table -> Custom partition CSV file -> partitions/v1/4m.csv
```
**编译:** **编译:**
```bash ```bash
+3
View File
@@ -33,6 +33,9 @@
#define ASR_BUTTON_GPIO GPIO_NUM_19 #define ASR_BUTTON_GPIO GPIO_NUM_19
#define BUILTIN_LED_GPIO GPIO_NUM_2 #define BUILTIN_LED_GPIO GPIO_NUM_2
#define ML307_RX_PIN GPIO_NUM_16
#define ML307_TX_PIN GPIO_NUM_17
#define DISPLAY_SDA_PIN GPIO_NUM_4 #define DISPLAY_SDA_PIN GPIO_NUM_4
#define DISPLAY_SCL_PIN GPIO_NUM_15 #define DISPLAY_SCL_PIN GPIO_NUM_15
#define DISPLAY_WIDTH 128 #define DISPLAY_WIDTH 128
@@ -4,16 +4,12 @@
{ {
"name": "bread-compact-esp32", "name": "bread-compact-esp32",
"sdkconfig_append": [ "sdkconfig_append": [
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/4m.csv\"",
"CONFIG_OLED_SSD1306_128X64=y" "CONFIG_OLED_SSD1306_128X64=y"
] ]
}, },
{ {
"name": "bread-compact-esp32-128x32", "name": "bread-compact-esp32-128x32",
"sdkconfig_append": [ "sdkconfig_append": [
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/4m.csv\"",
"CONFIG_OLED_SSD1306_128X32=y" "CONFIG_OLED_SSD1306_128X32=y"
] ]
} }
@@ -1,4 +1,4 @@
#include "wifi_board.h" #include "dual_network_board.h"
#include "codecs/no_audio_codec.h" #include "codecs/no_audio_codec.h"
#include "system_reset.h" #include "system_reset.h"
#include "application.h" #include "application.h"
@@ -17,11 +17,7 @@
#define TAG "ESP32-MarsbearSupport" #define TAG "ESP32-MarsbearSupport"
LV_FONT_DECLARE(font_puhui_14_1); class CompactWifiBoard : public DualNetworkBoard {
LV_FONT_DECLARE(font_awesome_14_1);
class CompactWifiBoard : public WifiBoard {
private: private:
Button boot_button_; Button boot_button_;
Button touch_button_; Button touch_button_;
@@ -92,8 +88,7 @@ private:
ESP_LOGI(TAG, "Turning display on"); ESP_LOGI(TAG, "Turning display on");
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_, true)); ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_, true));
display_ = new OledDisplay(panel_io_, panel_, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, display_ = new OledDisplay(panel_io_, panel_, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
{&font_puhui_14_1, &font_awesome_14_1});
} }
void InitializeButtons() { void InitializeButtons() {
@@ -110,13 +105,25 @@ private:
boot_button_.OnClick([this]() { boot_button_.OnClick([this]() {
auto& app = Application::GetInstance(); auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) { if (GetNetworkType() == NetworkType::WIFI) {
ResetWifiConfiguration(); if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
// cast to WifiBoard
auto& wifi_board = static_cast<WifiBoard&>(GetCurrentBoard());
wifi_board.ResetWifiConfiguration();
}
} }
gpio_set_level(BUILTIN_LED_GPIO, 1); gpio_set_level(BUILTIN_LED_GPIO, 1);
app.ToggleChatState(); app.ToggleChatState();
}); });
boot_button_.OnDoubleClick([this]() {
auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateStarting || app.GetDeviceState() == kDeviceStateWifiConfiguring) {
SwitchNetworkType();
}
});
asr_button_.OnClick([this]() { asr_button_.OnClick([this]() {
std::string wake_word="你好小智"; std::string wake_word="你好小智";
Application::GetInstance().WakeWordInvoke(wake_word); Application::GetInstance().WakeWordInvoke(wake_word);
@@ -138,7 +145,7 @@ private:
} }
public: public:
CompactWifiBoard() : boot_button_(BOOT_BUTTON_GPIO), touch_button_(TOUCH_BUTTON_GPIO), asr_button_(ASR_BUTTON_GPIO) CompactWifiBoard() : DualNetworkBoard(ML307_TX_PIN, ML307_RX_PIN), boot_button_(BOOT_BUTTON_GPIO), touch_button_(TOUCH_BUTTON_GPIO), asr_button_(ASR_BUTTON_GPIO)
{ {
InitializeDisplayI2c(); InitializeDisplayI2c();
InitializeSsd1306Display(); InitializeSsd1306Display();
@@ -18,9 +18,6 @@
#define TAG "CompactMl307Board" #define TAG "CompactMl307Board"
LV_FONT_DECLARE(font_puhui_14_1);
LV_FONT_DECLARE(font_awesome_14_1);
class CompactMl307Board : public DualNetworkBoard { class CompactMl307Board : public DualNetworkBoard {
private: private:
i2c_master_bus_handle_t display_i2c_bus_; i2c_master_bus_handle_t display_i2c_bus_;
@@ -92,8 +89,7 @@ private:
ESP_LOGI(TAG, "Turning display on"); ESP_LOGI(TAG, "Turning display on");
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_, true)); ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_, true));
display_ = new OledDisplay(panel_io_, panel_, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, display_ = new OledDisplay(panel_io_, panel_, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
{&font_puhui_14_1, &font_awesome_14_1});
} }
void InitializeButtons() { void InitializeButtons() {
@@ -60,9 +60,6 @@ static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = {
#define TAG "CompactWifiBoardLCD" #define TAG "CompactWifiBoardLCD"
LV_FONT_DECLARE(font_puhui_16_4);
LV_FONT_DECLARE(font_awesome_16_4);
class CompactWifiBoardLCD : public WifiBoard { class CompactWifiBoardLCD : public WifiBoard {
private: private:
@@ -115,7 +112,6 @@ private:
esp_lcd_panel_reset(panel); esp_lcd_panel_reset(panel);
esp_lcd_panel_init(panel); esp_lcd_panel_init(panel);
esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR); esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR);
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY); esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
@@ -124,20 +120,9 @@ private:
panel_config.vendor_config = &gc9107_vendor_config; panel_config.vendor_config = &gc9107_vendor_config;
#endif #endif
display_ = new SpiLcdDisplay(panel_io, panel, display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
{
.text_font = &font_puhui_16_4,
.icon_font = &font_awesome_16_4,
#if CONFIG_USE_WECHAT_MESSAGE_STYLE
.emoji_font = font_emoji_32_init(),
#else
.emoji_font = DISPLAY_HEIGHT >= 240 ? font_emoji_64_init() : font_emoji_32_init(),
#endif
});
} }
void InitializeButtons() { void InitializeButtons() {
boot_button_.OnClick([this]() { boot_button_.OnClick([this]() {
auto& app = Application::GetInstance(); auto& app = Application::GetInstance();
@@ -61,9 +61,6 @@ static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = {
#define TAG "CompactWifiBoardS3Cam" #define TAG "CompactWifiBoardS3Cam"
LV_FONT_DECLARE(font_puhui_16_4);
LV_FONT_DECLARE(font_awesome_16_4);
class CompactWifiBoardS3Cam : public WifiBoard { class CompactWifiBoardS3Cam : public WifiBoard {
private: private:
@@ -117,7 +114,6 @@ private:
esp_lcd_panel_reset(panel); esp_lcd_panel_reset(panel);
esp_lcd_panel_init(panel); esp_lcd_panel_init(panel);
esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR); esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR);
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY); esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
@@ -126,16 +122,7 @@ private:
panel_config.vendor_config = &gc9107_vendor_config; panel_config.vendor_config = &gc9107_vendor_config;
#endif #endif
display_ = new SpiLcdDisplay(panel_io, panel, display_ = new SpiLcdDisplay(panel_io, panel,
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
{
.text_font = &font_puhui_16_4,
.icon_font = &font_awesome_16_4,
#if CONFIG_USE_WECHAT_MESSAGE_STYLE
.emoji_font = font_emoji_32_init(),
#else
.emoji_font = DISPLAY_HEIGHT >= 240 ? font_emoji_64_init() : font_emoji_32_init(),
#endif
});
} }
void InitializeCamera() { void InitializeCamera() {
@@ -22,9 +22,6 @@
#define TAG "CompactWifiBoard" #define TAG "CompactWifiBoard"
LV_FONT_DECLARE(font_puhui_14_1);
LV_FONT_DECLARE(font_awesome_14_1);
class CompactWifiBoard : public WifiBoard { class CompactWifiBoard : public WifiBoard {
private: private:
i2c_master_bus_handle_t display_i2c_bus_; i2c_master_bus_handle_t display_i2c_bus_;
@@ -101,8 +98,7 @@ private:
ESP_LOGI(TAG, "Turning display on"); ESP_LOGI(TAG, "Turning display on");
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_, true)); ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_, true));
display_ = new OledDisplay(panel_io_, panel_, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, display_ = new OledDisplay(panel_io_, panel_, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
{&font_puhui_14_1, &font_awesome_14_1});
} }
void InitializeButtons() { void InitializeButtons() {
+20 -9
View File
@@ -2,9 +2,9 @@
#include "system_info.h" #include "system_info.h"
#include "settings.h" #include "settings.h"
#include "display/display.h" #include "display/display.h"
#include "display/oled_display.h"
#include "assets/lang_config.h" #include "assets/lang_config.h"
#include "esp32_music.h" #include "esp32_music.h"
#include <esp_log.h> #include <esp_log.h>
#include <esp_ota_ops.h> #include <esp_ota_ops.h>
#include <esp_chip_info.h> #include <esp_chip_info.h>
@@ -14,7 +14,6 @@
Board::Board() { Board::Board() {
music_ = nullptr; // 先初始化为空指针 music_ = nullptr; // 先初始化为空指针
Settings settings("board", true); Settings settings("board", true);
uuid_ = settings.GetString("uuid"); uuid_ = settings.GetString("uuid");
if (uuid_.empty()) { if (uuid_.empty()) {
@@ -27,7 +26,6 @@ Board::Board() {
music_ = new Esp32Music(); music_ = new Esp32Music();
ESP_LOGI(TAG, "Music player initialized for all boards"); ESP_LOGI(TAG, "Music player initialized for all boards");
} }
Board::~Board() { Board::~Board() {
if (music_) { if (music_) {
delete music_; delete music_;
@@ -35,7 +33,6 @@ Board::~Board() {
ESP_LOGI(TAG, "Music player destroyed"); ESP_LOGI(TAG, "Music player destroyed");
} }
} }
std::string Board::GenerateUuid() { std::string Board::GenerateUuid() {
// UUID v4 需要 16 字节的随机数据 // UUID v4 需要 16 字节的随机数据
uint8_t uuid[16]; uint8_t uuid[16];
@@ -76,16 +73,15 @@ Camera* Board::GetCamera() {
return nullptr; return nullptr;
} }
Music* Board::GetMusic() {
return music_;
}
Led* Board::GetLed() { Led* Board::GetLed() {
static NoLed led; static NoLed led;
return &led; return &led;
} }
Music* Board::GetMusic() {
return music_;
}
std::string Board::GetJson() { std::string Board::GetSystemInfoJson() {
/* /*
{ {
"version": 2, "version": 2,
@@ -173,6 +169,21 @@ std::string Board::GetJson() {
json += R"("label":")" + std::string(ota_partition->label) + R"(")"; json += R"("label":")" + std::string(ota_partition->label) + R"(")";
json += R"(},)"; json += R"(},)";
// Append display info
auto display = GetDisplay();
if (display) {
json += R"("display":{)";
if (dynamic_cast<OledDisplay*>(display)) {
json += R"("monochrome":)" + std::string("true") + R"(,)";
} else {
json += R"("monochrome":)" + std::string("false") + R"(,)";
}
json += R"("width":)" + std::to_string(display->width()) + R"(,)";
json += R"("height":)" + std::to_string(display->height()) + R"(,)";
json.pop_back(); // Remove the last comma
}
json += R"(},)";
json += R"("board":)" + GetBoardJson(); json += R"("board":)" + GetBoardJson();
// Close the JSON object // Close the JSON object
+4 -3
View File
@@ -11,6 +11,7 @@
#include "led/led.h" #include "led/led.h"
#include "backlight.h" #include "backlight.h"
#include "camera.h" #include "camera.h"
#include "assets.h"
#include "music.h" #include "music.h"
void* create_board(); void* create_board();
@@ -37,21 +38,21 @@ public:
return *instance; return *instance;
} }
virtual ~Board(); virtual ~Board(); // 改为非默认析构函数,用于清理 music_
virtual std::string GetBoardType() = 0; virtual std::string GetBoardType() = 0;
virtual std::string GetUuid() { return uuid_; } virtual std::string GetUuid() { return uuid_; }
virtual Backlight* GetBacklight() { return nullptr; } virtual Backlight* GetBacklight() { return nullptr; }
virtual Led* GetLed(); virtual Led* GetLed();
virtual Music* GetMusic();
virtual AudioCodec* GetAudioCodec() = 0; virtual AudioCodec* GetAudioCodec() = 0;
virtual bool GetTemperature(float& esp32temp); virtual bool GetTemperature(float& esp32temp);
virtual Display* GetDisplay(); virtual Display* GetDisplay();
virtual Camera* GetCamera(); virtual Camera* GetCamera();
virtual Music* GetMusic();
virtual NetworkInterface* GetNetwork() = 0; virtual NetworkInterface* GetNetwork() = 0;
virtual void StartNetwork() = 0; virtual void StartNetwork() = 0;
virtual const char* GetNetworkStateIcon() = 0; virtual const char* GetNetworkStateIcon() = 0;
virtual bool GetBatteryLevel(int &level, bool& charging, bool& discharging); virtual bool GetBatteryLevel(int &level, bool& charging, bool& discharging);
virtual std::string GetJson(); virtual std::string GetSystemInfoJson();
virtual void SetPowerSaveMode(bool enabled) = 0; virtual void SetPowerSaveMode(bool enabled) = 0;
virtual std::string GetBoardJson() = 0; virtual std::string GetBoardJson() = 0;
virtual std::string GetDeviceStatusJson() = 0; virtual std::string GetDeviceStatusJson() = 0;
+24 -66
View File
@@ -3,10 +3,11 @@
#include "display.h" #include "display.h"
#include "board.h" #include "board.h"
#include "system_info.h" #include "system_info.h"
#include "lvgl_display.h"
#include "jpg/image_to_jpeg.h"
#include <esp_log.h> #include <esp_log.h>
#include <esp_heap_caps.h> #include <esp_heap_caps.h>
#include <img_converters.h>
#include <cstring> #include <cstring>
#define TAG "Esp32Camera" #define TAG "Esp32Camera"
@@ -23,48 +24,6 @@ Esp32Camera::Esp32Camera(const camera_config_t& config) {
if (s->id.PID == GC0308_PID) { if (s->id.PID == GC0308_PID) {
s->set_hmirror(s, 0); // 这里控制摄像头镜像 写1镜像 写0不镜像 s->set_hmirror(s, 0); // 这里控制摄像头镜像 写1镜像 写0不镜像
} }
// 初始化预览图片的内存
memset(&preview_image_, 0, sizeof(preview_image_));
preview_image_.header.magic = LV_IMAGE_HEADER_MAGIC;
preview_image_.header.cf = LV_COLOR_FORMAT_RGB565;
preview_image_.header.flags = 0;
switch (config.frame_size) {
case FRAMESIZE_SVGA:
preview_image_.header.w = 800;
preview_image_.header.h = 600;
break;
case FRAMESIZE_VGA:
preview_image_.header.w = 640;
preview_image_.header.h = 480;
break;
case FRAMESIZE_QVGA:
preview_image_.header.w = 320;
preview_image_.header.h = 240;
break;
case FRAMESIZE_128X128:
preview_image_.header.w = 128;
preview_image_.header.h = 128;
break;
case FRAMESIZE_240X240:
preview_image_.header.w = 240;
preview_image_.header.h = 240;
break;
default:
ESP_LOGE(TAG, "Unsupported frame size: %d, image preview will not be shown", config.frame_size);
preview_image_.data_size = 0;
preview_image_.data = nullptr;
return;
}
preview_image_.header.stride = preview_image_.header.w * 2;
preview_image_.data_size = preview_image_.header.w * preview_image_.header.h * 2;
preview_image_.data = (uint8_t*)heap_caps_malloc(preview_image_.data_size, MALLOC_CAP_SPIRAM);
if (preview_image_.data == nullptr) {
ESP_LOGE(TAG, "Failed to allocate memory for preview image");
return;
}
} }
Esp32Camera::~Esp32Camera() { Esp32Camera::~Esp32Camera() {
@@ -72,10 +31,6 @@ Esp32Camera::~Esp32Camera() {
esp_camera_fb_return(fb_); esp_camera_fb_return(fb_);
fb_ = nullptr; fb_ = nullptr;
} }
if (preview_image_.data) {
heap_caps_free((void*)preview_image_.data);
preview_image_.data = nullptr;
}
esp_camera_deinit(); esp_camera_deinit();
} }
@@ -89,6 +44,7 @@ bool Esp32Camera::Capture() {
encoder_thread_.join(); encoder_thread_.join();
} }
auto start_time = esp_timer_get_time();
int frames_to_get = 2; int frames_to_get = 2;
// Try to get a stable frame // Try to get a stable frame
for (int i = 0; i < frames_to_get; i++) { for (int i = 0; i < frames_to_get; i++) {
@@ -101,31 +57,32 @@ bool Esp32Camera::Capture() {
return false; return false;
} }
} }
auto end_time = esp_timer_get_time();
ESP_LOGI(TAG, "Camera captured %d frames in %d ms", frames_to_get, int((end_time - start_time) / 1000));
// 如果预览图片 buffer 为空,则跳过预览
// 但仍返回 true,因为此时图像可以上传至服务器
if (preview_image_.data_size == 0) {
ESP_LOGW(TAG, "Skip preview because of unsupported frame size");
return true;
}
if (preview_image_.data == nullptr) {
ESP_LOGE(TAG, "Preview image data is not initialized");
return true;
}
// 显示预览图片 // 显示预览图片
auto display = Board::GetInstance().GetDisplay(); auto display = dynamic_cast<LvglDisplay*>(Board::GetInstance().GetDisplay());
if (display != nullptr) { if (display != nullptr) {
auto data = (uint8_t*)heap_caps_malloc(fb_->len, MALLOC_CAP_SPIRAM);
if (data == nullptr) {
ESP_LOGE(TAG, "Failed to allocate memory for preview image");
return false;
}
auto src = (uint16_t*)fb_->buf; auto src = (uint16_t*)fb_->buf;
auto dst = (uint16_t*)preview_image_.data; auto dst = (uint16_t*)data;
size_t pixel_count = fb_->len / 2; size_t pixel_count = fb_->len / 2;
for (size_t i = 0; i < pixel_count; i++) { for (size_t i = 0; i < pixel_count; i++) {
// 交换每个16位字内的字节 // 交换每个16位字内的字节
dst[i] = __builtin_bswap16(src[i]); dst[i] = __builtin_bswap16(src[i]);
} }
display->SetPreviewImage(&preview_image_);
auto image = std::make_unique<LvglAllocatedImage>(data, fb_->len, fb_->width, fb_->height, fb_->width * 2, LV_COLOR_FORMAT_RGB565);
display->SetPreviewImage(std::move(image));
} }
return true; return true;
} }
bool Esp32Camera::SetHMirror(bool enabled) { bool Esp32Camera::SetHMirror(bool enabled) {
sensor_t *s = esp_camera_sensor_get(); sensor_t *s = esp_camera_sensor_get();
if (s == nullptr) { if (s == nullptr) {
@@ -185,19 +142,20 @@ bool Esp32Camera::SetVFlip(bool enabled) {
*/ */
std::string Esp32Camera::Explain(const std::string& question) { std::string Esp32Camera::Explain(const std::string& question) {
if (explain_url_.empty()) { if (explain_url_.empty()) {
return "{\"success\": false, \"message\": \"Image explain URL or token is not set\"}"; throw std::runtime_error("Image explain URL or token is not set");
} }
// 创建局部的 JPEG 队列, 40 entries is about to store 512 * 40 = 20480 bytes of JPEG data // 创建局部的 JPEG 队列, 40 entries is about to store 512 * 40 = 20480 bytes of JPEG data
QueueHandle_t jpeg_queue = xQueueCreate(40, sizeof(JpegChunk)); QueueHandle_t jpeg_queue = xQueueCreate(40, sizeof(JpegChunk));
if (jpeg_queue == nullptr) { if (jpeg_queue == nullptr) {
ESP_LOGE(TAG, "Failed to create JPEG queue"); ESP_LOGE(TAG, "Failed to create JPEG queue");
return "{\"success\": false, \"message\": \"Failed to create JPEG queue\"}"; throw std::runtime_error("Failed to create JPEG queue");
} }
// We spawn a thread to encode the image to JPEG // We spawn a thread to encode the image to JPEG using optimized encoder (cost about 500ms and 8KB SRAM)
encoder_thread_ = std::thread([this, jpeg_queue]() { encoder_thread_ = std::thread([this, jpeg_queue]() {
frame2jpg_cb(fb_, 80, [](void* arg, size_t index, const void* data, size_t len) -> unsigned int { image_to_jpeg_cb(fb_->buf, fb_->len, fb_->width, fb_->height, fb_->format, 80,
[](void* arg, size_t index, const void* data, size_t len) -> size_t {
auto jpeg_queue = (QueueHandle_t)arg; auto jpeg_queue = (QueueHandle_t)arg;
JpegChunk chunk = { JpegChunk chunk = {
.data = (uint8_t*)heap_caps_aligned_alloc(16, len, MALLOC_CAP_SPIRAM), .data = (uint8_t*)heap_caps_aligned_alloc(16, len, MALLOC_CAP_SPIRAM),
@@ -235,7 +193,7 @@ std::string Esp32Camera::Explain(const std::string& question) {
} }
} }
vQueueDelete(jpeg_queue); vQueueDelete(jpeg_queue);
return "{\"success\": false, \"message\": \"Failed to connect to explain URL\"}"; throw std::runtime_error("Failed to connect to explain URL");
} }
{ {
@@ -288,7 +246,7 @@ std::string Esp32Camera::Explain(const std::string& question) {
if (http->GetStatusCode() != 200) { if (http->GetStatusCode() != 200) {
ESP_LOGE(TAG, "Failed to upload photo, status code: %d", http->GetStatusCode()); ESP_LOGE(TAG, "Failed to upload photo, status code: %d", http->GetStatusCode());
return "{\"success\": false, \"message\": \"Failed to upload photo\"}"; throw std::runtime_error("Failed to upload photo");
} }
std::string result = http->ReadAll(); std::string result = http->ReadAll();

Some files were not shown because too many files have changed in this diff Show More