Compare commits
11 Commits
ea48f0a3f0
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 67868619dc | |||
| 8bd2780688 | |||
| 577990de69 | |||
| aaf2f8dc36 | |||
| 7d7f5eae3d | |||
| 5c43129024 | |||
| 63e404d610 | |||
| 5a929f5b06 | |||
| 78f6d82c48 | |||
| f763ae05dc | |||
| 68d6539130 |
@@ -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
|
||||
@@ -13,7 +13,6 @@ main/assets/lang_config.h
|
||||
main/mmap_generate_emoji.h
|
||||
.DS_Store
|
||||
.cache
|
||||
main/mmap_generate_emoji.h
|
||||
*.pyc
|
||||
*.bin
|
||||
mmap_generate_*.h
|
||||
|
||||
Executable → Regular
+4
-1
@@ -4,11 +4,14 @@
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
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_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)
|
||||
project(xiaozhi)
|
||||
|
||||
|
||||
@@ -1,73 +1,33 @@
|
||||
# 超级小智-ESP32
|
||||
(中文 | English(编写中) | 日本語(编写中))
|
||||
# An MCP-based Chatbot | 一个基于 MCP 的聊天机器人
|
||||
|
||||
基于 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 许可证发布,允许任何人免费使用,或用于商业用途。
|
||||
|
||||
我们希望通过这个项目,能够帮助大家了解 AI 硬件开发,将当下飞速发展的大语言模型应用到实际的硬件设备中。
|
||||
|
||||
如果你有任何想法或建议,请随时提出 Issues 或加入 QQ 群:暂无,Telegram群:暂无。
|
||||
如果你有任何想法或建议,请随时提出 Issues 或加入 QQ 群:1011329060
|
||||
|
||||
项目主要贡献者:小霜霜Meow(抖音、B站UP)、空白泡泡糖果(B站UP),硅灵造物科技(B站UP)
|
||||
项目其它贡献者:@zhubinsheng
|
||||
|
||||
贡献者说明:引入部分其它贡献者在其它项目上的代码,并进行了部分修改。
|
||||
|
||||
音乐服务器、相关源码提供者(为爱发电):小霜霜Meow
|
||||
音乐服务器相关源码提供者(为爱发电):小霜霜Meow
|
||||
|
||||
感谢群友 cz 提供音乐服务器
|
||||
|
||||
音乐服务器源码请见 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 控制万物
|
||||
|
||||
小智 AI 聊天机器人作为一个语音交互入口,利用 Qwen / DeepSeek 等大模型的 AI 能力,通过 MCP 协议实现多端控制。
|
||||
|
||||

|
||||
@@ -86,8 +46,6 @@
|
||||
- 支持 ESP32-C3、ESP32-S3、ESP32-P4 芯片平台
|
||||
- 通过设备端 MCP 实现设备控制(音量、灯光、电机、GPIO 等)
|
||||
- 通过云端 MCP 扩展大模型能力(智能家居控制、PC桌面操作、知识搜索、邮件收发等)
|
||||
本项目新增功能:
|
||||
- 新增音乐播放功能,支持播放本地音乐(开发中,敬请期待)、云端音乐(完善中)。
|
||||
|
||||
## 硬件
|
||||
|
||||
@@ -204,10 +162,10 @@
|
||||
|
||||
## Star History
|
||||
|
||||
<a href="https://star-history.com/#IntelligentlyEverything/xiaozhi-esp32&Date">
|
||||
<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=IntelligentlyEverything/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" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=IntelligentlyEverything/xiaozhi-esp32&type=Date" />
|
||||
<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
@@ -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.
|
||||
|
||||

|
||||
|
||||
### 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:
|
||||
|
||||

|
||||
|
||||
### 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
@@ -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プロトコルを通じてマルチエンド制御を実現します。
|
||||
|
||||

|
||||
|
||||
### 実装済み機能
|
||||
|
||||
- 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)
|
||||
|
||||
ブレッドボードのデモ:
|
||||
|
||||

|
||||
|
||||
### 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>
|
||||
@@ -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界面。
|
||||
|
||||
---
|
||||
|
||||
### **方法2:curl测试**
|
||||
|
||||
```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`)
|
||||
- [ ] 测试连接
|
||||
|
||||
---
|
||||
|
||||
## 🐛 **常见问题**
|
||||
|
||||
### **问题1:ESP32无法连接服务器**
|
||||
|
||||
**现象**:
|
||||
```
|
||||
[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
Binary file not shown.
@@ -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
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Executable → Regular
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Executable
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
+451
-45
@@ -1,3 +1,4 @@
|
||||
# Define source files
|
||||
set(SOURCES "audio/audio_codec.cc"
|
||||
"audio/audio_service.cc"
|
||||
"audio/codecs/no_audio_codec.cc"
|
||||
@@ -14,76 +15,168 @@ set(SOURCES "audio/audio_codec.cc"
|
||||
"display/display.cc"
|
||||
"display/lcd_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/mqtt_protocol.cc"
|
||||
"protocols/websocket_protocol.cc"
|
||||
"mcp_server.cc"
|
||||
"system_info.cc"
|
||||
"application.cc"
|
||||
"alarm_manager.cc"
|
||||
"ota.cc"
|
||||
"settings.cc"
|
||||
"device_state_event.cc"
|
||||
"device_manager.cc"
|
||||
"assets.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)
|
||||
list(APPEND SOURCES ${BOARD_COMMON_SOURCES})
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
set(BOARD_TYPE "bread-compact-esp32")
|
||||
elseif(CONFIG_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)
|
||||
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)
|
||||
set(BOARD_TYPE "df-s3-ai-cam")
|
||||
elseif(CONFIG_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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
set(BOARD_TYPE "kevin-c3")
|
||||
elseif(CONFIG_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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
set(BOARD_TYPE "atoms3r-cam-m12-echo-base")
|
||||
elseif(CONFIG_BOARD_TYPE_ATOM_ECHOS3R)
|
||||
@@ -92,155 +185,352 @@ elseif(CONFIG_BOARD_TYPE_ATOMMATRIX_ECHO_BASE)
|
||||
set(BOARD_TYPE "atommatrix-echo-base")
|
||||
elseif(CONFIG_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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
set(BOARD_TYPE "esp-spot-s3")
|
||||
elseif(CONFIG_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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
set(BOARD_TYPE "lilygo-t-cameraplus-s3")
|
||||
elseif(CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3_V1_2)
|
||||
set(BOARD_TYPE "lilygo-t-cameraplus-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_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(BOARD_TYPE "lilygo-t-display-s3-pro-mvsrlora")
|
||||
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_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(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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
set(BOARD_TYPE "doit-s3-aibox")
|
||||
elseif(CONFIG_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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
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()
|
||||
|
||||
file(GLOB BOARD_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.c
|
||||
)
|
||||
list(APPEND SOURCES ${BOARD_SOURCES})
|
||||
|
||||
# Select audio processor according to Kconfig
|
||||
if(CONFIG_USE_AUDIO_PROCESSOR)
|
||||
list(APPEND SOURCES "audio/processors/afe_audio_processor.cc")
|
||||
else()
|
||||
list(APPEND SOURCES "audio/processors/no_audio_processor.cc")
|
||||
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")
|
||||
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")
|
||||
else()
|
||||
list(APPEND SOURCES "audio/wake_words/esp_wake_word.cc")
|
||||
endif()
|
||||
|
||||
# 根据Kconfig选择语言目录
|
||||
# Select language directory according to Kconfig
|
||||
if(CONFIG_LANGUAGE_ZH_CN)
|
||||
set(LANG_DIR "zh-CN")
|
||||
elseif(CONFIG_LANGUAGE_ZH_TW)
|
||||
@@ -287,13 +577,13 @@ elseif(CONFIG_LANGUAGE_RO_RO)
|
||||
set(LANG_DIR "ro-RO")
|
||||
endif()
|
||||
|
||||
# 定义生成路径
|
||||
# Define generation path
|
||||
set(LANG_JSON "${CMAKE_CURRENT_SOURCE_DIR}/assets/locales/${LANG_DIR}/language.json")
|
||||
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 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)
|
||||
list(REMOVE_ITEM SOURCES "audio/codecs/box_audio_codec.cc"
|
||||
"audio/codecs/es8388_audio_codec.cc"
|
||||
@@ -308,16 +598,17 @@ idf_component_register(SRCS ${SOURCES}
|
||||
WHOLE_ARCHIVE
|
||||
)
|
||||
|
||||
# 使用 target_compile_definitions 来定义 BOARD_TYPE, BOARD_NAME
|
||||
# 如果 BOARD_NAME 为空,则使用 BOARD_TYPE
|
||||
# Use target_compile_definitions to define BOARD_TYPE, BOARD_NAME
|
||||
# If BOARD_NAME is empty, use BOARD_TYPE
|
||||
if(NOT BOARD_NAME)
|
||||
set(BOARD_NAME ${BOARD_TYPE})
|
||||
endif()
|
||||
target_compile_definitions(${COMPONENT_LIB}
|
||||
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(
|
||||
OUTPUT ${LANG_HEADER}
|
||||
COMMAND python ${PROJECT_DIR}/scripts/gen_lang.py
|
||||
@@ -329,15 +620,27 @@ add_custom_command(
|
||||
COMMENT "Generating ${LANG_DIR} language config"
|
||||
)
|
||||
|
||||
# 强制建立生成依赖
|
||||
# Force build generation dependencies
|
||||
add_custom_target(lang_header ALL
|
||||
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)
|
||||
set(URL "https://github.com/espressif2022/image_player/raw/main/test_apps/test_8bit")
|
||||
set(SPIFFS_DIR "${CMAKE_BINARY_DIR}/emoji")
|
||||
file(MAKE_DIRECTORY ${SPIFFS_DIR})
|
||||
set(EMOJI_DIR "${CMAKE_BINARY_DIR}/emoji")
|
||||
file(MAKE_DIRECTORY ${EMOJI_DIR})
|
||||
|
||||
# List all 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)
|
||||
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})
|
||||
message(STATUS "File ${FILENAME} already exists, skipping download")
|
||||
else()
|
||||
@@ -369,31 +672,134 @@ foreach(FILENAME IN LISTS FILES_TO_DOWNLOAD)
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
spiffs_create_partition_assets(
|
||||
assets_A
|
||||
${SPIFFS_DIR}
|
||||
FLASH_IN_PROJECT
|
||||
MMAP_FILE_SUPPORT_FORMAT ".aaf"
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CONFIG_BOARD_TYPE_ECHOEAR)
|
||||
|
||||
idf_build_get_property(build_components BUILD_COMPONENTS)
|
||||
foreach(COMPONENT ${build_components})
|
||||
if(COMPONENT MATCHES "esp_emote_gfx" OR COMPONENT MATCHES "espressif2022__esp_emote_gfx")
|
||||
set(EMOTE_GFX_COMPONENT ${COMPONENT})
|
||||
idf_component_get_property(EMOTE_GFX_COMPONENT_PATH ${EMOTE_GFX_COMPONENT} COMPONENT_DIR)
|
||||
set(SPIFFS_DIR "${EMOTE_GFX_COMPONENT_PATH}/emoji_normal")
|
||||
break()
|
||||
# Function to build default assets based on configuration
|
||||
function(build_default_assets_bin)
|
||||
# Set output path for generated assets.bin
|
||||
set(GENERATED_ASSETS_BIN "${CMAKE_BINARY_DIR}/generated_assets.bin")
|
||||
|
||||
# Prepare arguments for build script
|
||||
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()
|
||||
endforeach()
|
||||
|
||||
spiffs_create_partition_assets(
|
||||
assets_A
|
||||
${SPIFFS_DIR}
|
||||
FLASH_IN_PROJECT
|
||||
MMAP_FILE_SUPPORT_FORMAT ".aaf, ttf, bin"
|
||||
IMPORT_INC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}
|
||||
)
|
||||
# Add default emoji collection if defined
|
||||
if(DEFAULT_EMOJI_COLLECTION)
|
||||
list(APPEND BUILD_ARGS "--emoji_collection" "${DEFAULT_EMOJI_COLLECTION}")
|
||||
endif()
|
||||
|
||||
# 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()
|
||||
|
||||
+152
-81
@@ -6,6 +6,27 @@ config OTA_URL
|
||||
help
|
||||
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
|
||||
prompt "Default Language"
|
||||
@@ -65,37 +86,37 @@ choice BOARD_TYPE
|
||||
help
|
||||
Board type. 开发板类型
|
||||
config BOARD_TYPE_BREAD_COMPACT_WIFI
|
||||
bool "面包板新版接线(WiFi)"
|
||||
bool "Bread Compact WiFi (面包板)"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_BREAD_COMPACT_WIFI_LCD
|
||||
bool "面包板新版接线(WiFi)+ LCD"
|
||||
bool "Bread Compact WiFi + LCD (面包板)"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_BREAD_COMPACT_WIFI_CAM
|
||||
bool "面包板新版接线(WiFi)+ LCD + Camera"
|
||||
bool "Bread Compact WiFi + LCD + Camera (面包板)"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_BREAD_COMPACT_ML307
|
||||
bool "面包板新版接线(ML307 AT)"
|
||||
bool "Bread Compact ML307/EC801E (面包板 4G)"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_BREAD_COMPACT_ESP32
|
||||
bool "面包板(WiFi) ESP32 DevKit"
|
||||
bool "Bread Compact ESP32 DevKit (面包板)"
|
||||
depends on IDF_TARGET_ESP32
|
||||
config BOARD_TYPE_BREAD_COMPACT_ESP32_LCD
|
||||
bool "面包板(WiFi+ LCD) ESP32 DevKit"
|
||||
bool "Bread Compact ESP32 DevKit + LCD (面包板)"
|
||||
depends on IDF_TARGET_ESP32
|
||||
config BOARD_TYPE_XMINI_C3_V3
|
||||
bool "虾哥 Mini C3 V3"
|
||||
bool "Xmini C3 V3"
|
||||
depends on IDF_TARGET_ESP32C3
|
||||
config BOARD_TYPE_XMINI_C3_4G
|
||||
bool "虾哥 Mini C3 4G"
|
||||
bool "Xmini C3 4G"
|
||||
depends on IDF_TARGET_ESP32C3
|
||||
config BOARD_TYPE_XMINI_C3
|
||||
bool "虾哥 Mini C3"
|
||||
bool "Xmini C3"
|
||||
depends on IDF_TARGET_ESP32C3
|
||||
config BOARD_TYPE_ESP32S3_KORVO2_V3
|
||||
bool "ESP32S3_KORVO2_V3开发板"
|
||||
bool "ESP32S3 KORVO2 V3"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_ESP_SPARKBOT
|
||||
bool "ESP-SparkBot开发板"
|
||||
bool "ESP-SparkBot"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_ESP_SPOT_S3
|
||||
bool "ESP-Spot-S3"
|
||||
@@ -125,10 +146,10 @@ choice BOARD_TYPE
|
||||
bool "Kevin C3"
|
||||
depends on IDF_TARGET_ESP32C3
|
||||
config BOARD_TYPE_KEVIN_SP_V3_DEV
|
||||
bool "Kevin SP V3开发板"
|
||||
bool "Kevin SP V3"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_KEVIN_SP_V4_DEV
|
||||
bool "Kevin SP V4开发板"
|
||||
bool "Kevin SP V4"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_ESP32_CGC
|
||||
bool "ESP32 CGC"
|
||||
@@ -137,13 +158,13 @@ choice BOARD_TYPE
|
||||
bool "ESP32 CGC 144"
|
||||
depends on IDF_TARGET_ESP32
|
||||
config BOARD_TYPE_KEVIN_YUYING_313LCD
|
||||
bool "鱼鹰科技3.13LCD开发板"
|
||||
bool "鱼鹰科技 3.13LCD"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_LICHUANG_DEV
|
||||
bool "立创·实战派ESP32-S3开发板"
|
||||
bool "立创·实战派 ESP32-S3"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_LICHUANG_C3_DEV
|
||||
bool "立创·实战派ESP32-C3开发板"
|
||||
bool "立创·实战派 ESP32-C3"
|
||||
depends on IDF_TARGET_ESP32C3
|
||||
config BOARD_TYPE_DF_K10
|
||||
bool "DFRobot 行空板 k10"
|
||||
@@ -194,6 +215,8 @@ choice BOARD_TYPE
|
||||
bool "Waveshare ESP32-S3-Touch-AMOLED-2.06"
|
||||
config BOARD_TYPE_ESP32S3_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
|
||||
config BOARD_TYPE_ESP32S3_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
|
||||
bool "Waveshare ESP32-C6-Touch-AMOLOED-1.43"
|
||||
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
|
||||
bool "Waveshare ESP32-S3-Touch-LCD-3.5"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
@@ -347,16 +373,20 @@ choice BOARD_TYPE
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_SURFER_C3_1_14TFT
|
||||
bool "Surfer-C3-1-14TFT"
|
||||
bool "Surfer-C3-1.14TFT"
|
||||
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
|
||||
|
||||
choice ESP_S3_LCD_EV_Board_Version_TYPE
|
||||
depends on BOARD_TYPE_ESP_S3_LCD_EV_Board
|
||||
prompt "EV_BOARD Type"
|
||||
default ESP_S3_LCD_EV_Board_1p4
|
||||
help
|
||||
开发板硬件版本型号选择
|
||||
config ESP_S3_LCD_EV_Board_1p4
|
||||
bool "乐鑫ESP32_S3_LCD_EV_Board-MB_V1.4"
|
||||
config ESP_S3_LCD_EV_Board_1p5
|
||||
@@ -368,13 +398,13 @@ choice DISPLAY_OLED_TYPE
|
||||
prompt "OLED Type"
|
||||
default OLED_SSD1306_128X32
|
||||
help
|
||||
OLED 屏幕类型选择
|
||||
OLED Monochrome Display Type
|
||||
config OLED_SSD1306_128X32
|
||||
bool "SSD1306, 分辨率128*32"
|
||||
bool "SSD1306 128*32"
|
||||
config OLED_SSD1306_128X64
|
||||
bool "SSD1306, 分辨率128*64"
|
||||
bool "SSD1306 128*64"
|
||||
config OLED_SH1106_128X64
|
||||
bool "SH1106, 分辨率128*64"
|
||||
bool "SH1106 128*64"
|
||||
endchoice
|
||||
|
||||
choice DISPLAY_LCD_TYPE
|
||||
@@ -382,37 +412,37 @@ choice DISPLAY_LCD_TYPE
|
||||
prompt "LCD Type"
|
||||
default LCD_ST7789_240X320
|
||||
help
|
||||
屏幕类型选择
|
||||
LCD Display Type
|
||||
config LCD_ST7789_240X320
|
||||
bool "ST7789, 分辨率240*320, IPS"
|
||||
bool "ST7789 240*320, IPS"
|
||||
config LCD_ST7789_240X320_NO_IPS
|
||||
bool "ST7789, 分辨率240*320, 非IPS"
|
||||
bool "ST7789 240*320, Non-IPS"
|
||||
config LCD_ST7789_170X320
|
||||
bool "ST7789, 分辨率170*320"
|
||||
bool "ST7789 170*320"
|
||||
config LCD_ST7789_172X320
|
||||
bool "ST7789, 分辨率172*320"
|
||||
bool "ST7789 172*320"
|
||||
config LCD_ST7789_240X280
|
||||
bool "ST7789, 分辨率240*280"
|
||||
bool "ST7789 240*280"
|
||||
config LCD_ST7789_240X240
|
||||
bool "ST7789, 分辨率240*240"
|
||||
bool "ST7789 240*240"
|
||||
config LCD_ST7789_240X240_7PIN
|
||||
bool "ST7789, 分辨率240*240, 7PIN"
|
||||
bool "ST7789 240*240, 7PIN"
|
||||
config LCD_ST7789_240X135
|
||||
bool "ST7789, 分辨率240*135"
|
||||
bool "ST7789 240*135"
|
||||
config LCD_ST7735_128X160
|
||||
bool "ST7735, 分辨率128*160"
|
||||
bool "ST7735 128*160"
|
||||
config LCD_ST7735_128X128
|
||||
bool "ST7735, 分辨率128*128"
|
||||
bool "ST7735 128*128"
|
||||
config LCD_ST7796_320X480
|
||||
bool "ST7796, 分辨率320*480 IPS"
|
||||
bool "ST7796 320*480 IPS"
|
||||
config LCD_ST7796_320X480_NO_IPS
|
||||
bool "ST7796, 分辨率320*480, 非IPS"
|
||||
bool "ST7796 320*480, Non-IPS"
|
||||
config LCD_ILI9341_240X320
|
||||
bool "ILI9341, 分辨率240*320"
|
||||
bool "ILI9341 240*320"
|
||||
config LCD_ILI9341_240X320_NO_IPS
|
||||
bool "ILI9341, 分辨率240*320, 非IPS"
|
||||
bool "ILI9341 240*320, Non-IPS"
|
||||
config LCD_GC9A01_240X240
|
||||
bool "GC9A01, 分辨率240*240, 圆屏"
|
||||
bool "GC9A01 240*240 Circle"
|
||||
config LCD_TYPE_800_1280_10_1_INCH
|
||||
bool "Waveshare 101M-8001280-IPS-CT-K Display"
|
||||
config LCD_TYPE_800_1280_10_1_INCH_A
|
||||
@@ -422,7 +452,7 @@ choice DISPLAY_LCD_TYPE
|
||||
config LCD_TYPE_720_720_4_INCH
|
||||
bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-4C with 720*720 4inch round display"
|
||||
config LCD_CUSTOM
|
||||
bool "自定义屏幕参数"
|
||||
bool "Custom LCD (自定义屏幕参数)"
|
||||
endchoice
|
||||
|
||||
choice DISPLAY_ESP32S3_KORVO2_V3
|
||||
@@ -430,11 +460,11 @@ choice DISPLAY_ESP32S3_KORVO2_V3
|
||||
prompt "ESP32S3_KORVO2_V3 LCD Type"
|
||||
default ESP32S3_KORVO2_V3_LCD_ST7789
|
||||
help
|
||||
屏幕类型选择
|
||||
LCD Display Type
|
||||
config ESP32S3_KORVO2_V3_LCD_ST7789
|
||||
bool "ST7789, 分辨率240*280"
|
||||
bool "ST7789 240*280"
|
||||
config ESP32S3_KORVO2_V3_LCD_ILI9341
|
||||
bool "ILI9341, 分辨率240*320"
|
||||
bool "ILI9341 240*320"
|
||||
endchoice
|
||||
|
||||
choice DISPLAY_ESP32S3_AUDIO_BOARD
|
||||
@@ -442,53 +472,75 @@ choice DISPLAY_ESP32S3_AUDIO_BOARD
|
||||
prompt "ESP32S3_AUDIO_BOARD LCD Type"
|
||||
default AUDIO_BOARD_LCD_JD9853
|
||||
help
|
||||
屏幕类型选择
|
||||
LCD Display Type
|
||||
config AUDIO_BOARD_LCD_JD9853
|
||||
bool "JD9853, 分辨率320*172"
|
||||
bool "JD9853 320*172"
|
||||
config AUDIO_BOARD_LCD_ST7789
|
||||
bool "ST7789, 分辨率240*320"
|
||||
bool "ST7789 240*320"
|
||||
endchoice
|
||||
|
||||
config USE_WECHAT_MESSAGE_STYLE
|
||||
bool "Enable WeChat Message Style"
|
||||
default n
|
||||
choice DISPLAY_STYLE
|
||||
prompt "Select display style"
|
||||
default USE_DEFAULT_MESSAGE_STYLE
|
||||
help
|
||||
使用微信聊天界面风格
|
||||
Select display style for Xiaozhi device
|
||||
|
||||
config USE_ESP_WAKE_WORD
|
||||
bool "Enable Wake Word Detection (without AFE)"
|
||||
default n
|
||||
config USE_DEFAULT_MESSAGE_STYLE
|
||||
bool "Enable default message style"
|
||||
|
||||
config USE_WECHAT_MESSAGE_STYLE
|
||||
bool "Enable WeChat Message Style"
|
||||
|
||||
config USE_EMOTE_MESSAGE_STYLE
|
||||
bool "Emote animation style"
|
||||
depends on BOARD_TYPE_ESP_BOX_3 || BOARD_TYPE_ECHOEAR
|
||||
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
|
||||
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
|
||||
支持 ESP32 C3、ESP32 C5 与 ESP32 C6,增加ESP32支持(需要开启PSRAM)
|
||||
Support ESP32 C3、ESP32 C5 与 ESP32 C6, and (ESP32 with PSRAM)
|
||||
|
||||
config USE_AFE_WAKE_WORD
|
||||
bool "Enable Wake Word Detection (AFE)"
|
||||
default y
|
||||
config USE_AFE_WAKE_WORD
|
||||
bool "Wakenet model with AFE"
|
||||
depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM
|
||||
help
|
||||
需要 ESP32 S3 与 PSRAM 支持
|
||||
Support AEC if available, requires ESP32 S3 and PSRAM
|
||||
|
||||
config USE_CUSTOM_WAKE_WORD
|
||||
bool "Enable Custom Wake Word Detection"
|
||||
default n
|
||||
depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM && (!USE_AFE_WAKE_WORD)
|
||||
config USE_CUSTOM_WAKE_WORD
|
||||
bool "Multinet model (Custom Wake Word)"
|
||||
depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM
|
||||
help
|
||||
需要 ESP32 S3 与 PSRAM 支持
|
||||
Requires ESP32 S3 and PSRAM
|
||||
|
||||
endchoice
|
||||
|
||||
config CUSTOM_WAKE_WORD
|
||||
string "Custom Wake Word"
|
||||
default "xiao tu dou"
|
||||
depends on USE_CUSTOM_WAKE_WORD
|
||||
help
|
||||
自定义唤醒词,中文用拼音表示,每个字之间用空格隔开
|
||||
Custom Wake Word, use pinyin for Chinese, separated by spaces
|
||||
|
||||
config CUSTOM_WAKE_WORD_DISPLAY
|
||||
string "Custom Wake Word Display"
|
||||
default "小土豆"
|
||||
depends on USE_CUSTOM_WAKE_WORD
|
||||
help
|
||||
唤醒后发送给服务器的问候语
|
||||
Greeting sent to the server after wake word detection
|
||||
|
||||
config CUSTOM_WAKE_WORD_THRESHOLD
|
||||
int "Custom Wake Word Threshold (%)"
|
||||
@@ -496,56 +548,68 @@ config CUSTOM_WAKE_WORD_THRESHOLD
|
||||
range 1 99
|
||||
depends on USE_CUSTOM_WAKE_WORD
|
||||
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
|
||||
bool "Enable Audio Noise Reduction"
|
||||
default y
|
||||
depends on (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM
|
||||
help
|
||||
需要 ESP32 S3 与 PSRAM 支持
|
||||
Requires ESP32 S3 and PSRAM
|
||||
|
||||
config USE_DEVICE_AEC
|
||||
bool "Enable Device-Side AEC"
|
||||
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
|
||||
因为性能不够,不建议和微信聊天界面风格同时开启
|
||||
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
|
||||
bool "Enable Server-Side AEC (Unstable)"
|
||||
default n
|
||||
depends on USE_AUDIO_PROCESSOR
|
||||
help
|
||||
启用服务器端 AEC,需要服务器支持
|
||||
To work perperly, server-side AEC requires server support
|
||||
|
||||
config USE_AUDIO_DEBUGGER
|
||||
bool "Enable Audio Debugger"
|
||||
default n
|
||||
help
|
||||
启用音频调试功能,通过UDP发送音频数据
|
||||
|
||||
config USE_ACOUSTIC_WIFI_PROVISIONING
|
||||
bool "Enable Acoustic WiFi Provisioning"
|
||||
default n
|
||||
help
|
||||
启用声波配网功能,使用音频信号传输 WiFi 配置数据
|
||||
Enable audio debugger, send audio data through UDP to the host machine
|
||||
|
||||
config AUDIO_DEBUG_UDP_SERVER
|
||||
string "Audio Debug UDP Server Address"
|
||||
default "192.168.2.100:8000"
|
||||
depends on USE_AUDIO_DEBUGGER
|
||||
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
|
||||
bool "Enable Custom Message Reception"
|
||||
default n
|
||||
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
|
||||
choice I2S_TYPE_TAIJIPI_S3
|
||||
prompt "taiji-pi-S3 I2S Type"
|
||||
default TAIJIPAI_I2S_TYPE_STD
|
||||
help
|
||||
@@ -554,6 +618,13 @@ choice I2S_TYPE_TAIJIPI_S3
|
||||
bool "I2S Type STD"
|
||||
config TAIJIPAI_I2S_TYPE_PDM
|
||||
bool "I2S Type PDM"
|
||||
endchoice
|
||||
endchoice
|
||||
|
||||
config I2S_USE_2SLOT
|
||||
bool "Enable I2S 2 Slot"
|
||||
default n
|
||||
help
|
||||
启动双声道
|
||||
endmenu
|
||||
|
||||
endmenu
|
||||
@@ -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=周六
|
||||
}
|
||||
@@ -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
|
||||
+426
-80
@@ -7,8 +7,12 @@
|
||||
#include "websocket_protocol.h"
|
||||
#include "assets/lang_config.h"
|
||||
#include "mcp_server.h"
|
||||
#include "assets.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
#include <esp_log.h>
|
||||
#include <cJSON.h>
|
||||
#include <driver/gpio.h>
|
||||
@@ -49,7 +53,7 @@ Application::Application() {
|
||||
esp_timer_create_args_t clock_timer_args = {
|
||||
.callback = [](void* arg) {
|
||||
Application* app = (Application*)arg;
|
||||
app->OnClockTimer();
|
||||
xEventGroupSetBits(app->event_group_, MAIN_EVENT_CLOCK_TICK);
|
||||
},
|
||||
.arg = this,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
@@ -67,6 +71,57 @@ Application::~Application() {
|
||||
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) {
|
||||
const int MAX_RETRY = 10;
|
||||
int retry_count = 0;
|
||||
@@ -103,43 +158,10 @@ void Application::CheckNewVersion(Ota& ota) {
|
||||
retry_delay = 10; // 重置重试延迟时间
|
||||
|
||||
if (ota.HasNewVersion()) {
|
||||
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) + 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();
|
||||
if (UpgradeFirmware(ota)) {
|
||||
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
|
||||
@@ -332,6 +354,9 @@ void Application::Start() {
|
||||
/* Setup the display */
|
||||
auto display = board.GetDisplay();
|
||||
|
||||
// Print board name/version info
|
||||
display->SetChatMessage("system", SystemInfo::GetUserAgent().c_str());
|
||||
|
||||
/* Setup the audio service */
|
||||
auto codec = board.GetAudioCodec();
|
||||
audio_service_.Initialize(codec);
|
||||
@@ -349,6 +374,12 @@ void Application::Start() {
|
||||
};
|
||||
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 */
|
||||
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
|
||||
display->UpdateStatusBar(true);
|
||||
|
||||
// Check for new assets version
|
||||
CheckAssetsVersion();
|
||||
|
||||
// Check for new firmware version or get the MQTT broker address
|
||||
Ota ota;
|
||||
CheckNewVersion(ota);
|
||||
@@ -366,7 +400,9 @@ void Application::Start() {
|
||||
display->SetStatus(Lang::Strings::LOADING_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()) {
|
||||
protocol_ = std::make_unique<MqttProtocol>();
|
||||
@@ -496,6 +532,7 @@ void Application::Start() {
|
||||
});
|
||||
bool protocol_started = protocol_->Start();
|
||||
|
||||
SystemInfo::PrintHeapStats();
|
||||
SetDeviceState(kDeviceStateIdle);
|
||||
|
||||
has_server_time_ = ota.HasServerTime();
|
||||
@@ -503,26 +540,25 @@ void Application::Start() {
|
||||
std::string message = std::string(Lang::Strings::VERSION) + ota.GetCurrentVersion();
|
||||
display->ShowNotification(message.c_str());
|
||||
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
|
||||
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
|
||||
@@ -538,15 +574,14 @@ void Application::Schedule(std::function<void()> callback) {
|
||||
// If other tasks need to access the websocket or chat state,
|
||||
// they should use Schedule to call this function
|
||||
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) {
|
||||
auto bits = xEventGroupWaitBits(event_group_, MAIN_EVENT_SCHEDULE |
|
||||
MAIN_EVENT_SEND_AUDIO |
|
||||
MAIN_EVENT_WAKE_WORD_DETECTED |
|
||||
MAIN_EVENT_VAD_CHANGE |
|
||||
MAIN_EVENT_CLOCK_TICK |
|
||||
MAIN_EVENT_ERROR, pdTRUE, pdFALSE, portMAX_DELAY);
|
||||
|
||||
if (bits & MAIN_EVENT_ERROR) {
|
||||
SetDeviceState(kDeviceStateIdle);
|
||||
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) {
|
||||
while (auto packet = audio_service_.PopPacketFromSendQueue()) {
|
||||
if (!protocol_->SendAudio(std::move(packet))) {
|
||||
if (protocol_ && !protocol_->SendAudio(std::move(packet))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -579,6 +614,29 @@ void Application::MainEventLoop() {
|
||||
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();
|
||||
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
|
||||
while (auto packet = audio_service_.PopWakeWordPacket()) {
|
||||
protocol_->SendAudio(std::move(packet));
|
||||
@@ -623,7 +681,9 @@ void Application::OnWakeWordDetected() {
|
||||
void Application::AbortSpeaking(AbortReason reason) {
|
||||
ESP_LOGI(TAG, "Abort speaking");
|
||||
aborted_ = true;
|
||||
if (protocol_) {
|
||||
protocol_->SendAbortSpeaking(reason);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::SetListeningMode(ListeningMode mode) {
|
||||
@@ -648,17 +708,7 @@ void Application::SetDeviceState(DeviceState state) {
|
||||
auto display = board.GetDisplay();
|
||||
auto led = board.GetLed();
|
||||
led->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();
|
||||
}
|
||||
}
|
||||
|
||||
display->OnStateChanged();
|
||||
switch (state) {
|
||||
case kDeviceStateUnknown:
|
||||
case kDeviceStateIdle:
|
||||
@@ -690,11 +740,7 @@ void Application::SetDeviceState(DeviceState state) {
|
||||
if (listening_mode_ != kListeningModeRealtime) {
|
||||
audio_service_.EnableVoiceProcessing(false);
|
||||
// Only AFE wake word can be detected in speaking mode
|
||||
#if CONFIG_USE_AFE_WAKE_WORD
|
||||
audio_service_.EnableWakeWordDetection(true);
|
||||
#else
|
||||
audio_service_.EnableWakeWordDetection(false);
|
||||
#endif
|
||||
audio_service_.EnableWakeWordDetection(audio_service_.IsAfeWakeWord());
|
||||
}
|
||||
audio_service_.ResetDecoder();
|
||||
break;
|
||||
@@ -706,9 +752,70 @@ void Application::SetDeviceState(DeviceState state) {
|
||||
|
||||
void Application::Reboot() {
|
||||
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();
|
||||
}
|
||||
|
||||
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) {
|
||||
if (device_state_ == kDeviceStateIdle) {
|
||||
ToggleChatState();
|
||||
@@ -748,11 +855,18 @@ bool Application::CanEnterSleepMode() {
|
||||
}
|
||||
|
||||
void Application::SendMcpMessage(const std::string& payload) {
|
||||
Schedule([this, payload]() {
|
||||
if (protocol_) {
|
||||
protocol_->SendMcpMessage(payload);
|
||||
if (protocol_ == nullptr) {
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
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
@@ -15,6 +15,8 @@
|
||||
#include "ota.h"
|
||||
#include "audio_service.h"
|
||||
#include "device_state_event.h"
|
||||
#include "alarm_manager.h"
|
||||
|
||||
|
||||
#define MAIN_EVENT_SCHEDULE (1 << 0)
|
||||
#define MAIN_EVENT_SEND_AUDIO (1 << 1)
|
||||
@@ -22,6 +24,8 @@
|
||||
#define MAIN_EVENT_VAD_CHANGE (1 << 3)
|
||||
#define MAIN_EVENT_ERROR (1 << 4)
|
||||
#define MAIN_EVENT_CHECK_NEW_VERSION_DONE (1 << 5)
|
||||
#define MAIN_EVENT_CLOCK_TICK (1 << 6)
|
||||
|
||||
|
||||
enum AecMode {
|
||||
kAecOff,
|
||||
@@ -53,16 +57,20 @@ public:
|
||||
void StopListening();
|
||||
void Reboot();
|
||||
void WakeWordInvoke(const std::string& wake_word);
|
||||
bool UpgradeFirmware(Ota& ota, const std::string& url = "");
|
||||
bool CanEnterSleepMode();
|
||||
void SendMcpMessage(const std::string& payload);
|
||||
void SetAecMode(AecMode mode);
|
||||
AecMode GetAecMode() const { return aec_mode_; }
|
||||
|
||||
void PlaySound(const std::string_view& sound);
|
||||
// 新增:接收外部音频数据(如音乐播放)
|
||||
void AddAudioData(AudioStreamPacket&& packet);
|
||||
void PlaySound(const std::string_view& sound);
|
||||
AudioService& GetAudioService() { return audio_service_; }
|
||||
|
||||
// 闹钟功能
|
||||
AlarmManager& GetAlarmManager() { return AlarmManager::GetInstance(); }
|
||||
std::vector<std::string> GetDefaultAlarmMusicList() const;
|
||||
|
||||
private:
|
||||
Application();
|
||||
~Application();
|
||||
@@ -82,12 +90,40 @@ private:
|
||||
bool aborted_ = false;
|
||||
int clock_ticks_ = 0;
|
||||
TaskHandle_t check_new_version_task_handle_ = nullptr;
|
||||
TaskHandle_t main_event_loop_task_handle_ = nullptr;
|
||||
|
||||
void OnWakeWordDetected();
|
||||
void CheckNewVersion(Ota& ota);
|
||||
void CheckAssetsVersion();
|
||||
void ShowActivationCode(const std::string& code, const std::string& message);
|
||||
void OnClockTimer();
|
||||
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_
|
||||
|
||||
+517
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -1,63 +1,68 @@
|
||||
// Auto-generated language config
|
||||
// Language: zh-CN with en-US fallback
|
||||
// Language: en-US with en-US fallback
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#ifndef zh_cn
|
||||
#define zh_cn // 預設語言
|
||||
#ifndef en_us
|
||||
#define en_us // 預設語言
|
||||
#endif
|
||||
|
||||
namespace Lang {
|
||||
// 语言元数据
|
||||
constexpr const char* CODE = "zh-CN";
|
||||
constexpr const char* CODE = "en-US";
|
||||
|
||||
// 字符串资源 (en-US as fallback for missing keys)
|
||||
namespace Strings {
|
||||
constexpr const char* ACCESS_VIA_BROWSER = ",浏览器访问 ";
|
||||
constexpr const char* ACTIVATION = "激活设备";
|
||||
constexpr const char* BATTERY_CHARGING = "正在充电";
|
||||
constexpr const char* BATTERY_FULL = "电量已满";
|
||||
constexpr const char* BATTERY_LOW = "电量不足";
|
||||
constexpr const char* BATTERY_NEED_CHARGE = "电量低,请充电";
|
||||
constexpr const char* CHECKING_NEW_VERSION = "检查新版本...";
|
||||
constexpr const char* CHECK_NEW_VERSION_FAILED = "检查新版本失败,将在 %d 秒后重试:%s";
|
||||
constexpr const char* CONNECTED_TO = "已连接 ";
|
||||
constexpr const char* CONNECTING = "连接中...";
|
||||
constexpr const char* ACCESS_VIA_BROWSER = " Config URL: ";
|
||||
constexpr const char* ACTIVATION = "Activation";
|
||||
constexpr const char* BATTERY_CHARGING = "Charging";
|
||||
constexpr const char* BATTERY_FULL = "Battery full";
|
||||
constexpr const char* BATTERY_LOW = "Low battery";
|
||||
constexpr const char* BATTERY_NEED_CHARGE = "Low battery, please charge";
|
||||
constexpr const char* CHECKING_NEW_VERSION = "Checking for new version...";
|
||||
constexpr const char* CHECK_NEW_VERSION_FAILED = "Check for new version failed, will retry in %d seconds: %s";
|
||||
constexpr const char* CONNECTED_TO = "Connected to ";
|
||||
constexpr const char* CONNECTING = "Connecting...";
|
||||
constexpr const char* CONNECTION_SUCCESSFUL = "Connection Successful";
|
||||
constexpr const char* CONNECT_TO = "连接 ";
|
||||
constexpr const char* CONNECT_TO_HOTSPOT = "手机连接热点 ";
|
||||
constexpr const char* DETECTING_MODULE = "检测模组...";
|
||||
constexpr const char* ENTERING_WIFI_CONFIG_MODE = "进入配网模式...";
|
||||
constexpr const char* ERROR = "错误";
|
||||
constexpr const char* INFO = "信息";
|
||||
constexpr const char* INITIALIZING = "正在初始化...";
|
||||
constexpr const char* LISTENING = "聆听中...";
|
||||
constexpr const char* LOADING_PROTOCOL = "登录服务器...";
|
||||
constexpr const char* MAX_VOLUME = "最大音量";
|
||||
constexpr const char* MUTED = "已静音";
|
||||
constexpr const char* NEW_VERSION = "新版本 ";
|
||||
constexpr const char* OTA_UPGRADE = "OTA 升级";
|
||||
constexpr const char* PIN_ERROR = "请插入 SIM 卡";
|
||||
constexpr const char* REGISTERING_NETWORK = "等待网络...";
|
||||
constexpr const char* REG_ERROR = "无法接入网络,请检查流量卡状态";
|
||||
constexpr const char* RTC_MODE_OFF = "AEC 关闭";
|
||||
constexpr const char* RTC_MODE_ON = "AEC 开启";
|
||||
constexpr const char* SCANNING_WIFI = "扫描 Wi-Fi...";
|
||||
constexpr const char* SERVER_ERROR = "发送失败,请检查网络";
|
||||
constexpr const char* SERVER_NOT_CONNECTED = "无法连接服务,请稍后再试";
|
||||
constexpr const char* SERVER_NOT_FOUND = "正在寻找可用服务";
|
||||
constexpr const char* SERVER_TIMEOUT = "等待响应超时";
|
||||
constexpr const char* SPEAKING = "说话中...";
|
||||
constexpr const char* STANDBY = "待命";
|
||||
constexpr const char* SWITCH_TO_4G_NETWORK = "切换到 4G...";
|
||||
constexpr const char* SWITCH_TO_WIFI_NETWORK = "切换到 Wi-Fi...";
|
||||
constexpr const char* UPGRADE_FAILED = "升级失败";
|
||||
constexpr const char* UPGRADING = "正在升级系统...";
|
||||
constexpr const char* VERSION = "版本 ";
|
||||
constexpr const char* VOLUME = "音量 ";
|
||||
constexpr const char* WARNING = "警告";
|
||||
constexpr const char* WIFI_CONFIG_MODE = "配网模式";
|
||||
constexpr const char* CONNECT_TO = "Connect to ";
|
||||
constexpr const char* CONNECT_TO_HOTSPOT = "Hotspot: ";
|
||||
constexpr const char* DETECTING_MODULE = "Detecting module...";
|
||||
constexpr const char* DOWNLOAD_ASSETS_FAILED = "Failed to download assets";
|
||||
constexpr const char* ENTERING_WIFI_CONFIG_MODE = "Entering Wi-Fi configuration mode...";
|
||||
constexpr const char* ERROR = "Error";
|
||||
constexpr const char* FOUND_NEW_ASSETS = "Found new assets: %s";
|
||||
constexpr const char* HELLO_MY_FRIEND = "Hello, my friend!";
|
||||
constexpr const char* INFO = "Information";
|
||||
constexpr const char* INITIALIZING = "Initializing...";
|
||||
constexpr const char* LISTENING = "Listening...";
|
||||
constexpr const char* LOADING_ASSETS = "Loading assets...";
|
||||
constexpr const char* LOADING_PROTOCOL = "Logging in...";
|
||||
constexpr const char* MAX_VOLUME = "Max volume";
|
||||
constexpr const char* MUTED = "Muted";
|
||||
constexpr const char* NEW_VERSION = "New version ";
|
||||
constexpr const char* OTA_UPGRADE = "OTA Upgrade";
|
||||
constexpr const char* PIN_ERROR = "Please insert SIM card";
|
||||
constexpr const char* PLEASE_WAIT = "Please wait...";
|
||||
constexpr const char* REGISTERING_NETWORK = "Waiting for network...";
|
||||
constexpr const char* REG_ERROR = "Unable to access network, please check SIM card status";
|
||||
constexpr const char* RTC_MODE_OFF = "AEC Off";
|
||||
constexpr const char* RTC_MODE_ON = "AEC On";
|
||||
constexpr const char* SCANNING_WIFI = "Scanning Wi-Fi...";
|
||||
constexpr const char* SERVER_ERROR = "Sending failed, please check the network";
|
||||
constexpr const char* SERVER_NOT_CONNECTED = "Unable to connect to service, please try again later";
|
||||
constexpr const char* SERVER_NOT_FOUND = "Looking for available service";
|
||||
constexpr const char* SERVER_TIMEOUT = "Waiting for response timeout";
|
||||
constexpr const char* SPEAKING = "Speaking...";
|
||||
constexpr const char* STANDBY = "Standby";
|
||||
constexpr const char* SWITCH_TO_4G_NETWORK = "Switching to 4G...";
|
||||
constexpr const char* SWITCH_TO_WIFI_NETWORK = "Switching to Wi-Fi...";
|
||||
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)
|
||||
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"ar-SA"
|
||||
"type": "ar-SA"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"تحذير",
|
||||
"INFO":"معلومات",
|
||||
"ERROR":"خطأ",
|
||||
"WARNING": "تحذير",
|
||||
"INFO": "معلومات",
|
||||
"ERROR": "خطأ",
|
||||
"VERSION": "الإصدار ",
|
||||
"LOADING_PROTOCOL":"الاتصال بالخادم...",
|
||||
"INITIALIZING":"التهيئة...",
|
||||
"PIN_ERROR":"يرجى إدخال بطاقة SIM",
|
||||
"REG_ERROR":"لا يمكن الوصول إلى الشبكة، يرجى التحقق من حالة بطاقة البيانات",
|
||||
"DETECTING_MODULE":"اكتشاف الوحدة...",
|
||||
"REGISTERING_NETWORK":"انتظار الشبكة...",
|
||||
"CHECKING_NEW_VERSION":"فحص الإصدار الجديد...",
|
||||
"CHECK_NEW_VERSION_FAILED":"فشل فحص الإصدار الجديد، سيتم المحاولة خلال %d ثانية: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK":"التبديل إلى Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK":"التبديل إلى 4G...",
|
||||
|
||||
"STANDBY":"في الانتظار",
|
||||
"CONNECT_TO":"الاتصال بـ ",
|
||||
"CONNECTING":"جاري الاتصال...",
|
||||
"CONNECTED_TO":"متصل بـ ",
|
||||
|
||||
"LISTENING":"الاستماع...",
|
||||
"SPEAKING":"التحدث...",
|
||||
|
||||
"SERVER_NOT_FOUND":"البحث عن خدمة متاحة",
|
||||
"SERVER_NOT_CONNECTED":"لا يمكن الاتصال بالخدمة، يرجى المحاولة لاحقاً",
|
||||
"SERVER_TIMEOUT":"انتهت مهلة الاستجابة",
|
||||
"SERVER_ERROR":"فشل الإرسال، يرجى التحقق من الشبكة",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"اتصل الهاتف بنقطة الاتصال ",
|
||||
"ACCESS_VIA_BROWSER":"،الوصول عبر المتصفح ",
|
||||
"WIFI_CONFIG_MODE":"وضع تكوين الشبكة",
|
||||
"ENTERING_WIFI_CONFIG_MODE":"الدخول في وضع تكوين الشبكة...",
|
||||
"SCANNING_WIFI":"فحص Wi-Fi...",
|
||||
|
||||
"LOADING_PROTOCOL": "الاتصال بالخادم...",
|
||||
"INITIALIZING": "التهيئة...",
|
||||
"PIN_ERROR": "يرجى إدخال بطاقة SIM",
|
||||
"REG_ERROR": "لا يمكن الوصول إلى الشبكة، يرجى التحقق من حالة بطاقة البيانات",
|
||||
"DETECTING_MODULE": "اكتشاف الوحدة...",
|
||||
"REGISTERING_NETWORK": "انتظار الشبكة...",
|
||||
"CHECKING_NEW_VERSION": "فحص الإصدار الجديد...",
|
||||
"CHECK_NEW_VERSION_FAILED": "فشل فحص الإصدار الجديد، سيتم المحاولة خلال %d ثانية: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK": "التبديل إلى Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK": "التبديل إلى 4G...",
|
||||
"STANDBY": "في الانتظار",
|
||||
"CONNECT_TO": "الاتصال بـ ",
|
||||
"CONNECTING": "جاري الاتصال...",
|
||||
"CONNECTED_TO": "متصل بـ ",
|
||||
"LISTENING": "الاستماع...",
|
||||
"SPEAKING": "التحدث...",
|
||||
"SERVER_NOT_FOUND": "البحث عن خدمة متاحة",
|
||||
"SERVER_NOT_CONNECTED": "لا يمكن الاتصال بالخدمة، يرجى المحاولة لاحقاً",
|
||||
"SERVER_TIMEOUT": "انتهت مهلة الاستجابة",
|
||||
"SERVER_ERROR": "فشل الإرسال، يرجى التحقق من الشبكة",
|
||||
"CONNECT_TO_HOTSPOT": "اتصل الهاتف بنقطة الاتصال ",
|
||||
"ACCESS_VIA_BROWSER": "،الوصول عبر المتصفح ",
|
||||
"WIFI_CONFIG_MODE": "وضع تكوين الشبكة",
|
||||
"ENTERING_WIFI_CONFIG_MODE": "الدخول في وضع تكوين الشبكة...",
|
||||
"SCANNING_WIFI": "فحص Wi-Fi...",
|
||||
"NEW_VERSION": "إصدار جديد ",
|
||||
"OTA_UPGRADE":"تحديث OTA",
|
||||
"UPGRADING":"تحديث النظام...",
|
||||
"UPGRADE_FAILED":"فشل التحديث",
|
||||
"ACTIVATION":"تفعيل الجهاز",
|
||||
|
||||
"BATTERY_LOW":"البطارية منخفضة",
|
||||
"BATTERY_CHARGING":"جاري الشحن",
|
||||
"BATTERY_FULL":"البطارية ممتلئة",
|
||||
"BATTERY_NEED_CHARGE":"البطارية منخفضة، يرجى الشحن",
|
||||
|
||||
"VOLUME":"الصوت ",
|
||||
"MUTED":"صامت",
|
||||
"MAX_VOLUME":"أقصى صوت",
|
||||
|
||||
"RTC_MODE_OFF":"AEC مُوقف",
|
||||
"RTC_MODE_ON":"AEC مُشغل"
|
||||
"OTA_UPGRADE": "تحديث OTA",
|
||||
"UPGRADING": "تحديث النظام...",
|
||||
"UPGRADE_FAILED": "فشل التحديث",
|
||||
"ACTIVATION": "تفعيل الجهاز",
|
||||
"BATTERY_LOW": "البطارية منخفضة",
|
||||
"BATTERY_CHARGING": "جاري الشحن",
|
||||
"BATTERY_FULL": "البطارية ممتلئة",
|
||||
"BATTERY_NEED_CHARGE": "البطارية منخفضة، يرجى الشحن",
|
||||
"VOLUME": "الصوت ",
|
||||
"MUTED": "صامت",
|
||||
"MAX_VOLUME": "أقصى صوت",
|
||||
"RTC_MODE_OFF": "AEC مُوقف",
|
||||
"RTC_MODE_ON": "AEC مُشغل",
|
||||
"DOWNLOAD_ASSETS_FAILED": "فشل في تنزيل الموارد",
|
||||
"LOADING_ASSETS": "جاري تحميل الموارد...",
|
||||
"PLEASE_WAIT": "يرجى الانتظار...",
|
||||
"FOUND_NEW_ASSETS": "تم العثور على موارد جديدة: %s",
|
||||
"HELLO_MY_FRIEND": "مرحباً، صديقي!"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"cs-CZ"
|
||||
"type": "cs-CZ"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"Varování",
|
||||
"INFO":"Informace",
|
||||
"ERROR":"Chyba",
|
||||
"WARNING": "Varování",
|
||||
"INFO": "Informace",
|
||||
"ERROR": "Chyba",
|
||||
"VERSION": "Verze ",
|
||||
"LOADING_PROTOCOL":"Připojování k serveru...",
|
||||
"INITIALIZING":"Inicializace...",
|
||||
"PIN_ERROR":"Prosím vložte SIM kartu",
|
||||
"REG_ERROR":"Nelze se připojit k síti, zkontrolujte stav datové karty",
|
||||
"DETECTING_MODULE":"Detekce modulu...",
|
||||
"REGISTERING_NETWORK":"Čekání na síť...",
|
||||
"CHECKING_NEW_VERSION":"Kontrola nové verze...",
|
||||
"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_4G_NETWORK":"Přepínání na 4G...",
|
||||
|
||||
"STANDBY":"Pohotovost",
|
||||
"CONNECT_TO":"Připojit k ",
|
||||
"CONNECTING":"Připojování...",
|
||||
"CONNECTED_TO":"Připojeno k ",
|
||||
|
||||
"LISTENING":"Naslouchání...",
|
||||
"SPEAKING":"Mluvení...",
|
||||
|
||||
"SERVER_NOT_FOUND":"Hledání dostupné služby",
|
||||
"SERVER_NOT_CONNECTED":"Nelze se připojit ke službě, zkuste to později",
|
||||
"SERVER_TIMEOUT":"Čas odpovědi vypršel",
|
||||
"SERVER_ERROR":"Odeslání selhalo, zkontrolujte síť",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"Připojte telefon k hotspotu ",
|
||||
"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...",
|
||||
|
||||
"LOADING_PROTOCOL": "Připojování k serveru...",
|
||||
"INITIALIZING": "Inicializace...",
|
||||
"PIN_ERROR": "Prosím vložte SIM kartu",
|
||||
"REG_ERROR": "Nelze se připojit k síti, zkontrolujte stav datové karty",
|
||||
"DETECTING_MODULE": "Detekce modulu...",
|
||||
"REGISTERING_NETWORK": "Čekání na síť...",
|
||||
"CHECKING_NEW_VERSION": "Kontrola nové verze...",
|
||||
"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_4G_NETWORK": "Přepínání na 4G...",
|
||||
"STANDBY": "Pohotovost",
|
||||
"CONNECT_TO": "Připojit k ",
|
||||
"CONNECTING": "Připojování...",
|
||||
"CONNECTED_TO": "Připojeno k ",
|
||||
"LISTENING": "Naslouchání...",
|
||||
"SPEAKING": "Mluvení...",
|
||||
"SERVER_NOT_FOUND": "Hledání dostupné služby",
|
||||
"SERVER_NOT_CONNECTED": "Nelze se připojit ke službě, zkuste to později",
|
||||
"SERVER_TIMEOUT": "Čas odpovědi vypršel",
|
||||
"SERVER_ERROR": "Odeslání selhalo, zkontrolujte síť",
|
||||
"CONNECT_TO_HOTSPOT": "Připojte telefon k hotspotu ",
|
||||
"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 ",
|
||||
"OTA_UPGRADE":"OTA upgrade",
|
||||
"UPGRADING":"Aktualizace systému...",
|
||||
"UPGRADE_FAILED":"Upgrade selhal",
|
||||
"ACTIVATION":"Aktivace zařízení",
|
||||
|
||||
"BATTERY_LOW":"Slabá baterie",
|
||||
"BATTERY_CHARGING":"Nabíjení",
|
||||
"BATTERY_FULL":"Baterie plná",
|
||||
"BATTERY_NEED_CHARGE":"Slabá baterie, prosím nabijte",
|
||||
|
||||
"VOLUME":"Hlasitost ",
|
||||
"MUTED":"Ztlumeno",
|
||||
"MAX_VOLUME":"Maximální hlasitost",
|
||||
|
||||
"RTC_MODE_OFF":"AEC vypnuto",
|
||||
"RTC_MODE_ON":"AEC zapnuto"
|
||||
"OTA_UPGRADE": "OTA upgrade",
|
||||
"UPGRADING": "Aktualizace systému...",
|
||||
"UPGRADE_FAILED": "Upgrade selhal",
|
||||
"ACTIVATION": "Aktivace zařízení",
|
||||
"BATTERY_LOW": "Slabá baterie",
|
||||
"BATTERY_CHARGING": "Nabíjení",
|
||||
"BATTERY_FULL": "Baterie plná",
|
||||
"BATTERY_NEED_CHARGE": "Slabá baterie, prosím nabijte",
|
||||
"VOLUME": "Hlasitost ",
|
||||
"MUTED": "Ztlumeno",
|
||||
"MAX_VOLUME": "Maximální hlasitost",
|
||||
"RTC_MODE_OFF": "AEC vypnuto",
|
||||
"RTC_MODE_ON": "AEC zapnuto",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Nepodařilo se stáhnout prostředky",
|
||||
"LOADING_ASSETS": "Načítání prostředků...",
|
||||
"PLEASE_WAIT": "Prosím čekejte...",
|
||||
"FOUND_NEW_ASSETS": "Nalezeny nové prostředky: %s",
|
||||
"HELLO_MY_FRIEND": "Ahoj, můj příteli!"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"de-DE"
|
||||
"type": "de-DE"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"Warnung",
|
||||
"INFO":"Information",
|
||||
"ERROR":"Fehler",
|
||||
"WARNING": "Warnung",
|
||||
"INFO": "Information",
|
||||
"ERROR": "Fehler",
|
||||
"VERSION": "Version ",
|
||||
"LOADING_PROTOCOL":"Verbindung zum Server...",
|
||||
"INITIALIZING":"Initialisierung...",
|
||||
"PIN_ERROR":"Bitte SIM-Karte einlegen",
|
||||
"REG_ERROR":"Netzwerkverbindung fehlgeschlagen, bitte Datenkartenstatus prüfen",
|
||||
"DETECTING_MODULE":"Modul erkennen...",
|
||||
"REGISTERING_NETWORK":"Auf Netzwerk warten...",
|
||||
"CHECKING_NEW_VERSION":"Neue Version prüfen...",
|
||||
"CHECK_NEW_VERSION_FAILED":"Neue Version prüfen fehlgeschlagen, Wiederholung in %d Sekunden: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK":"Zu Wi-Fi wechseln...",
|
||||
"SWITCH_TO_4G_NETWORK":"Zu 4G wechseln...",
|
||||
|
||||
"STANDBY":"Bereitschaft",
|
||||
"CONNECT_TO":"Verbinden zu ",
|
||||
"CONNECTING":"Verbindung wird hergestellt...",
|
||||
"CONNECTED_TO":"Verbunden mit ",
|
||||
|
||||
"LISTENING":"Zuhören...",
|
||||
"SPEAKING":"Sprechen...",
|
||||
|
||||
"SERVER_NOT_FOUND":"Verfügbaren Service suchen",
|
||||
"SERVER_NOT_CONNECTED":"Service-Verbindung fehlgeschlagen, bitte später versuchen",
|
||||
"SERVER_TIMEOUT":"Antwort-Timeout",
|
||||
"SERVER_ERROR":"Senden fehlgeschlagen, bitte Netzwerk prüfen",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"Handy mit Hotspot verbinden ",
|
||||
"ACCESS_VIA_BROWSER":",Browser öffnen ",
|
||||
"WIFI_CONFIG_MODE":"Netzwerkkonfigurationsmodus",
|
||||
"ENTERING_WIFI_CONFIG_MODE":"Netzwerkkonfigurationsmodus eingeben...",
|
||||
"SCANNING_WIFI":"Wi-Fi scannen...",
|
||||
|
||||
"LOADING_PROTOCOL": "Verbindung zum Server...",
|
||||
"INITIALIZING": "Initialisierung...",
|
||||
"PIN_ERROR": "Bitte SIM-Karte einlegen",
|
||||
"REG_ERROR": "Netzwerkverbindung fehlgeschlagen, bitte Datenkartenstatus prüfen",
|
||||
"DETECTING_MODULE": "Modul erkennen...",
|
||||
"REGISTERING_NETWORK": "Auf Netzwerk warten...",
|
||||
"CHECKING_NEW_VERSION": "Neue Version prüfen...",
|
||||
"CHECK_NEW_VERSION_FAILED": "Neue Version prüfen fehlgeschlagen, Wiederholung in %d Sekunden: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK": "Zu Wi-Fi wechseln...",
|
||||
"SWITCH_TO_4G_NETWORK": "Zu 4G wechseln...",
|
||||
"STANDBY": "Bereitschaft",
|
||||
"CONNECT_TO": "Verbinden zu ",
|
||||
"CONNECTING": "Verbindung wird hergestellt...",
|
||||
"CONNECTED_TO": "Verbunden mit ",
|
||||
"LISTENING": "Zuhören...",
|
||||
"SPEAKING": "Sprechen...",
|
||||
"SERVER_NOT_FOUND": "Verfügbaren Service suchen",
|
||||
"SERVER_NOT_CONNECTED": "Service-Verbindung fehlgeschlagen, bitte später versuchen",
|
||||
"SERVER_TIMEOUT": "Antwort-Timeout",
|
||||
"SERVER_ERROR": "Senden fehlgeschlagen, bitte Netzwerk prüfen",
|
||||
"CONNECT_TO_HOTSPOT": "Handy mit Hotspot verbinden ",
|
||||
"ACCESS_VIA_BROWSER": ",Browser öffnen ",
|
||||
"WIFI_CONFIG_MODE": "Netzwerkkonfigurationsmodus",
|
||||
"ENTERING_WIFI_CONFIG_MODE": "Netzwerkkonfigurationsmodus eingeben...",
|
||||
"SCANNING_WIFI": "Wi-Fi scannen...",
|
||||
"NEW_VERSION": "Neue Version ",
|
||||
"OTA_UPGRADE":"OTA-Upgrade",
|
||||
"UPGRADING":"System wird aktualisiert...",
|
||||
"UPGRADE_FAILED":"Upgrade fehlgeschlagen",
|
||||
"ACTIVATION":"Gerät aktivieren",
|
||||
|
||||
"BATTERY_LOW":"Niedriger Batteriestand",
|
||||
"BATTERY_CHARGING":"Wird geladen",
|
||||
"BATTERY_FULL":"Batterie voll",
|
||||
"BATTERY_NEED_CHARGE":"Niedriger Batteriestand, bitte aufladen",
|
||||
|
||||
"VOLUME":"Lautstärke ",
|
||||
"MUTED":"Stummgeschaltet",
|
||||
"MAX_VOLUME":"Maximale Lautstärke",
|
||||
|
||||
"RTC_MODE_OFF":"AEC aus",
|
||||
"RTC_MODE_ON":"AEC ein"
|
||||
"OTA_UPGRADE": "OTA-Upgrade",
|
||||
"UPGRADING": "System wird aktualisiert...",
|
||||
"UPGRADE_FAILED": "Upgrade fehlgeschlagen",
|
||||
"ACTIVATION": "Gerät aktivieren",
|
||||
"BATTERY_LOW": "Niedriger Batteriestand",
|
||||
"BATTERY_CHARGING": "Wird geladen",
|
||||
"BATTERY_FULL": "Batterie voll",
|
||||
"BATTERY_NEED_CHARGE": "Niedriger Batteriestand, bitte aufladen",
|
||||
"VOLUME": "Lautstärke ",
|
||||
"MUTED": "Stummgeschaltet",
|
||||
"MAX_VOLUME": "Maximale Lautstärke",
|
||||
"RTC_MODE_OFF": "AEC aus",
|
||||
"RTC_MODE_ON": "AEC ein",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Fehler beim Herunterladen der Ressourcen",
|
||||
"LOADING_ASSETS": "Ressourcen werden geladen...",
|
||||
"PLEASE_WAIT": "Bitte warten...",
|
||||
"FOUND_NEW_ASSETS": "Neue Ressourcen gefunden: %s",
|
||||
"HELLO_MY_FRIEND": "Hallo, mein Freund!"
|
||||
}
|
||||
}
|
||||
@@ -17,43 +17,40 @@
|
||||
"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_4G_NETWORK": "Switching to 4G...",
|
||||
|
||||
"STANDBY": "Standby",
|
||||
"CONNECT_TO": "Connect to ",
|
||||
"CONNECTING": "Connecting...",
|
||||
"CONNECTION_SUCCESSFUL": "Connection Successful",
|
||||
"CONNECTED_TO": "Connected to ",
|
||||
|
||||
"LISTENING": "Listening...",
|
||||
"SPEAKING": "Speaking...",
|
||||
|
||||
"SERVER_NOT_FOUND": "Looking for available service",
|
||||
"SERVER_NOT_CONNECTED": "Unable to connect to service, please try again later",
|
||||
"SERVER_TIMEOUT": "Waiting for response timeout",
|
||||
"SERVER_ERROR": "Sending failed, please check the network",
|
||||
|
||||
"CONNECT_TO_HOTSPOT": "Hotspot: ",
|
||||
"ACCESS_VIA_BROWSER": " Config URL: ",
|
||||
"WIFI_CONFIG_MODE": "Wi-Fi Configuration Mode",
|
||||
"ENTERING_WIFI_CONFIG_MODE": "Entering Wi-Fi configuration mode...",
|
||||
"SCANNING_WIFI": "Scanning Wi-Fi...",
|
||||
|
||||
"NEW_VERSION": "New version ",
|
||||
"OTA_UPGRADE": "OTA Upgrade",
|
||||
"UPGRADING": "System is upgrading...",
|
||||
"UPGRADE_FAILED": "Upgrade failed",
|
||||
"ACTIVATION": "Activation",
|
||||
|
||||
"BATTERY_LOW": "Low battery",
|
||||
"BATTERY_CHARGING": "Charging",
|
||||
"BATTERY_FULL": "Battery full",
|
||||
"BATTERY_NEED_CHARGE": "Low battery, please charge",
|
||||
|
||||
"VOLUME": "Volume ",
|
||||
"MUTED": "Muted",
|
||||
"MAX_VOLUME": "Max volume",
|
||||
|
||||
"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!"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"es-ES"
|
||||
"type": "es-ES"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"Advertencia",
|
||||
"INFO":"Información",
|
||||
"ERROR":"Error",
|
||||
"WARNING": "Advertencia",
|
||||
"INFO": "Información",
|
||||
"ERROR": "Error",
|
||||
"VERSION": "Versión ",
|
||||
"LOADING_PROTOCOL":"Conectando al servidor...",
|
||||
"INITIALIZING":"Inicializando...",
|
||||
"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",
|
||||
"DETECTING_MODULE":"Detectando módulo...",
|
||||
"REGISTERING_NETWORK":"Esperando red...",
|
||||
"CHECKING_NEW_VERSION":"Verificando nueva versión...",
|
||||
"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_4G_NETWORK":"Cambiando a 4G...",
|
||||
|
||||
"STANDBY":"En espera",
|
||||
"CONNECT_TO":"Conectar a ",
|
||||
"CONNECTING":"Conectando...",
|
||||
"CONNECTED_TO":"Conectado a ",
|
||||
|
||||
"LISTENING":"Escuchando...",
|
||||
"SPEAKING":"Hablando...",
|
||||
|
||||
"SERVER_NOT_FOUND":"Buscando servicio disponible",
|
||||
"SERVER_NOT_CONNECTED":"No se puede conectar al servicio, inténtelo más tarde",
|
||||
"SERVER_TIMEOUT":"Tiempo de espera agotado",
|
||||
"SERVER_ERROR":"Error de envío, verifique la red",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"Conectar teléfono al punto de acceso ",
|
||||
"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...",
|
||||
|
||||
"LOADING_PROTOCOL": "Conectando al servidor...",
|
||||
"INITIALIZING": "Inicializando...",
|
||||
"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",
|
||||
"DETECTING_MODULE": "Detectando módulo...",
|
||||
"REGISTERING_NETWORK": "Esperando red...",
|
||||
"CHECKING_NEW_VERSION": "Verificando nueva versión...",
|
||||
"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_4G_NETWORK": "Cambiando a 4G...",
|
||||
"STANDBY": "En espera",
|
||||
"CONNECT_TO": "Conectar a ",
|
||||
"CONNECTING": "Conectando...",
|
||||
"CONNECTED_TO": "Conectado a ",
|
||||
"LISTENING": "Escuchando...",
|
||||
"SPEAKING": "Hablando...",
|
||||
"SERVER_NOT_FOUND": "Buscando servicio disponible",
|
||||
"SERVER_NOT_CONNECTED": "No se puede conectar al servicio, inténtelo más tarde",
|
||||
"SERVER_TIMEOUT": "Tiempo de espera agotado",
|
||||
"SERVER_ERROR": "Error de envío, verifique la red",
|
||||
"CONNECT_TO_HOTSPOT": "Conectar teléfono al punto de acceso ",
|
||||
"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 ",
|
||||
"OTA_UPGRADE":"Actualización OTA",
|
||||
"UPGRADING":"Actualizando sistema...",
|
||||
"UPGRADE_FAILED":"Actualización fallida",
|
||||
"ACTIVATION":"Activación del dispositivo",
|
||||
|
||||
"BATTERY_LOW":"Batería baja",
|
||||
"BATTERY_CHARGING":"Cargando",
|
||||
"BATTERY_FULL":"Batería llena",
|
||||
"BATTERY_NEED_CHARGE":"Batería baja, por favor cargar",
|
||||
|
||||
"VOLUME":"Volumen ",
|
||||
"MUTED":"Silenciado",
|
||||
"MAX_VOLUME":"Volumen máximo",
|
||||
|
||||
"RTC_MODE_OFF":"AEC desactivado",
|
||||
"RTC_MODE_ON":"AEC activado"
|
||||
"OTA_UPGRADE": "Actualización OTA",
|
||||
"UPGRADING": "Actualizando sistema...",
|
||||
"UPGRADE_FAILED": "Actualización fallida",
|
||||
"ACTIVATION": "Activación del dispositivo",
|
||||
"BATTERY_LOW": "Batería baja",
|
||||
"BATTERY_CHARGING": "Cargando",
|
||||
"BATTERY_FULL": "Batería llena",
|
||||
"BATTERY_NEED_CHARGE": "Batería baja, por favor cargar",
|
||||
"VOLUME": "Volumen ",
|
||||
"MUTED": "Silenciado",
|
||||
"MAX_VOLUME": "Volumen máximo",
|
||||
"RTC_MODE_OFF": "AEC desactivado",
|
||||
"RTC_MODE_ON": "AEC activado",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Error al descargar recursos",
|
||||
"LOADING_ASSETS": "Cargando recursos...",
|
||||
"PLEASE_WAIT": "Por favor espere...",
|
||||
"FOUND_NEW_ASSETS": "Encontrados nuevos recursos: %s",
|
||||
"HELLO_MY_FRIEND": "¡Hola, mi amigo!"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"fi-FI"
|
||||
"type": "fi-FI"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"Varoitus",
|
||||
"INFO":"Tieto",
|
||||
"ERROR":"Virhe",
|
||||
"WARNING": "Varoitus",
|
||||
"INFO": "Tieto",
|
||||
"ERROR": "Virhe",
|
||||
"VERSION": "Versio ",
|
||||
"LOADING_PROTOCOL":"Yhdistetään palvelimeen...",
|
||||
"INITIALIZING":"Alustetaan...",
|
||||
"PIN_ERROR":"Ole hyvä ja aseta SIM-kortti",
|
||||
"REG_ERROR":"Ei voi muodostaa yhteyttä verkkoon, tarkista datakortin tila",
|
||||
"DETECTING_MODULE":"Tunnistetaan moduuli...",
|
||||
"REGISTERING_NETWORK":"Odotetaan verkkoa...",
|
||||
"CHECKING_NEW_VERSION":"Tarkistetaan uutta versiota...",
|
||||
"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_4G_NETWORK":"Vaihdetaan 4G:hen...",
|
||||
|
||||
"STANDBY":"Valmiustila",
|
||||
"CONNECT_TO":"Yhdistä ",
|
||||
"CONNECTING":"Yhdistetään...",
|
||||
"CONNECTED_TO":"Yhdistetty ",
|
||||
|
||||
"LISTENING":"Kuunnellaan...",
|
||||
"SPEAKING":"Puhutaan...",
|
||||
|
||||
"SERVER_NOT_FOUND":"Etsitään käytettävissä olevaa palvelua",
|
||||
"SERVER_NOT_CONNECTED":"Ei voi yhdistää palveluun, yritä myöhemmin",
|
||||
"SERVER_TIMEOUT":"Vastauksen aikakatkaisu",
|
||||
"SERVER_ERROR":"Lähetys epäonnistui, tarkista verkko",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"Yhdistä puhelin hotspottiin ",
|
||||
"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...",
|
||||
|
||||
"LOADING_PROTOCOL": "Yhdistetään palvelimeen...",
|
||||
"INITIALIZING": "Alustetaan...",
|
||||
"PIN_ERROR": "Ole hyvä ja aseta SIM-kortti",
|
||||
"REG_ERROR": "Ei voi muodostaa yhteyttä verkkoon, tarkista datakortin tila",
|
||||
"DETECTING_MODULE": "Tunnistetaan moduuli...",
|
||||
"REGISTERING_NETWORK": "Odotetaan verkkoa...",
|
||||
"CHECKING_NEW_VERSION": "Tarkistetaan uutta versiota...",
|
||||
"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_4G_NETWORK": "Vaihdetaan 4G:hen...",
|
||||
"STANDBY": "Valmiustila",
|
||||
"CONNECT_TO": "Yhdistä ",
|
||||
"CONNECTING": "Yhdistetään...",
|
||||
"CONNECTED_TO": "Yhdistetty ",
|
||||
"LISTENING": "Kuunnellaan...",
|
||||
"SPEAKING": "Puhutaan...",
|
||||
"SERVER_NOT_FOUND": "Etsitään käytettävissä olevaa palvelua",
|
||||
"SERVER_NOT_CONNECTED": "Ei voi yhdistää palveluun, yritä myöhemmin",
|
||||
"SERVER_TIMEOUT": "Vastauksen aikakatkaisu",
|
||||
"SERVER_ERROR": "Lähetys epäonnistui, tarkista verkko",
|
||||
"CONNECT_TO_HOTSPOT": "Yhdistä puhelin hotspottiin ",
|
||||
"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 ",
|
||||
"OTA_UPGRADE":"OTA-päivitys",
|
||||
"UPGRADING":"Päivitetään järjestelmää...",
|
||||
"UPGRADE_FAILED":"Päivitys epäonnistui",
|
||||
"ACTIVATION":"Laitteen aktivointi",
|
||||
|
||||
"BATTERY_LOW":"Akku vähissä",
|
||||
"BATTERY_CHARGING":"Ladataan",
|
||||
"BATTERY_FULL":"Akku täynnä",
|
||||
"BATTERY_NEED_CHARGE":"Akku vähissä, ole hyvä ja lataa",
|
||||
|
||||
"VOLUME":"Äänenvoimakkuus ",
|
||||
"MUTED":"Mykistetty",
|
||||
"MAX_VOLUME":"Maksimi äänenvoimakkuus",
|
||||
|
||||
"RTC_MODE_OFF":"AEC pois päältä",
|
||||
"RTC_MODE_ON":"AEC päällä"
|
||||
"OTA_UPGRADE": "OTA-päivitys",
|
||||
"UPGRADING": "Päivitetään järjestelmää...",
|
||||
"UPGRADE_FAILED": "Päivitys epäonnistui",
|
||||
"ACTIVATION": "Laitteen aktivointi",
|
||||
"BATTERY_LOW": "Akku vähissä",
|
||||
"BATTERY_CHARGING": "Ladataan",
|
||||
"BATTERY_FULL": "Akku täynnä",
|
||||
"BATTERY_NEED_CHARGE": "Akku vähissä, ole hyvä ja lataa",
|
||||
"VOLUME": "Äänenvoimakkuus ",
|
||||
"MUTED": "Mykistetty",
|
||||
"MAX_VOLUME": "Maksimi äänenvoimakkuus",
|
||||
"RTC_MODE_OFF": "AEC pois päältä",
|
||||
"RTC_MODE_ON": "AEC päällä",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Resurssien lataaminen epäonnistui",
|
||||
"LOADING_ASSETS": "Ladataan resursseja...",
|
||||
"PLEASE_WAIT": "Odota hetki...",
|
||||
"FOUND_NEW_ASSETS": "Löydetty uusia resursseja: %s",
|
||||
"HELLO_MY_FRIEND": "Hei, ystäväni!"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"fr-FR"
|
||||
"type": "fr-FR"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"Avertissement",
|
||||
"INFO":"Information",
|
||||
"ERROR":"Erreur",
|
||||
"WARNING": "Avertissement",
|
||||
"INFO": "Information",
|
||||
"ERROR": "Erreur",
|
||||
"VERSION": "Version ",
|
||||
"LOADING_PROTOCOL":"Connexion au serveur...",
|
||||
"INITIALIZING":"Initialisation...",
|
||||
"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",
|
||||
"DETECTING_MODULE":"Détection du module...",
|
||||
"REGISTERING_NETWORK":"En attente du réseau...",
|
||||
"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",
|
||||
"SWITCH_TO_WIFI_NETWORK":"Basculer vers Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK":"Basculer vers 4G...",
|
||||
|
||||
"STANDBY":"En attente",
|
||||
"CONNECT_TO":"Se connecter à ",
|
||||
"CONNECTING":"Connexion en cours...",
|
||||
"CONNECTED_TO":"Connecté à ",
|
||||
|
||||
"LISTENING":"Écoute...",
|
||||
"SPEAKING":"Parole...",
|
||||
|
||||
"SERVER_NOT_FOUND":"Recherche d'un service disponible",
|
||||
"SERVER_NOT_CONNECTED":"Impossible de se connecter au service, veuillez réessayer plus tard",
|
||||
"SERVER_TIMEOUT":"Délai d'attente de réponse",
|
||||
"SERVER_ERROR":"Échec d'envoi, veuillez vérifier le réseau",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"Connecter le téléphone au point d'accès ",
|
||||
"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...",
|
||||
|
||||
"LOADING_PROTOCOL": "Connexion au serveur...",
|
||||
"INITIALIZING": "Initialisation...",
|
||||
"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",
|
||||
"DETECTING_MODULE": "Détection du module...",
|
||||
"REGISTERING_NETWORK": "En attente du réseau...",
|
||||
"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",
|
||||
"SWITCH_TO_WIFI_NETWORK": "Basculer vers Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK": "Basculer vers 4G...",
|
||||
"STANDBY": "En attente",
|
||||
"CONNECT_TO": "Se connecter à ",
|
||||
"CONNECTING": "Connexion en cours...",
|
||||
"CONNECTED_TO": "Connecté à ",
|
||||
"LISTENING": "Écoute...",
|
||||
"SPEAKING": "Parole...",
|
||||
"SERVER_NOT_FOUND": "Recherche d'un service disponible",
|
||||
"SERVER_NOT_CONNECTED": "Impossible de se connecter au service, veuillez réessayer plus tard",
|
||||
"SERVER_TIMEOUT": "Délai d'attente de réponse",
|
||||
"SERVER_ERROR": "Échec d'envoi, veuillez vérifier le réseau",
|
||||
"CONNECT_TO_HOTSPOT": "Connecter le téléphone au point d'accès ",
|
||||
"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 ",
|
||||
"OTA_UPGRADE":"Mise à jour OTA",
|
||||
"UPGRADING":"Mise à jour du système...",
|
||||
"UPGRADE_FAILED":"Échec de mise à jour",
|
||||
"ACTIVATION":"Activation de l'appareil",
|
||||
|
||||
"BATTERY_LOW":"Batterie faible",
|
||||
"BATTERY_CHARGING":"En charge",
|
||||
"BATTERY_FULL":"Batterie pleine",
|
||||
"BATTERY_NEED_CHARGE":"Batterie faible, veuillez charger",
|
||||
|
||||
"VOLUME":"Volume ",
|
||||
"MUTED":"Muet",
|
||||
"MAX_VOLUME":"Volume maximum",
|
||||
|
||||
"RTC_MODE_OFF":"AEC désactivé",
|
||||
"RTC_MODE_ON":"AEC activé"
|
||||
"OTA_UPGRADE": "Mise à jour OTA",
|
||||
"UPGRADING": "Mise à jour du système...",
|
||||
"UPGRADE_FAILED": "Échec de mise à jour",
|
||||
"ACTIVATION": "Activation de l'appareil",
|
||||
"BATTERY_LOW": "Batterie faible",
|
||||
"BATTERY_CHARGING": "En charge",
|
||||
"BATTERY_FULL": "Batterie pleine",
|
||||
"BATTERY_NEED_CHARGE": "Batterie faible, veuillez charger",
|
||||
"VOLUME": "Volume ",
|
||||
"MUTED": "Muet",
|
||||
"MAX_VOLUME": "Volume maximum",
|
||||
"RTC_MODE_OFF": "AEC désactivé",
|
||||
"RTC_MODE_ON": "AEC activé",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Échec du téléchargement des ressources",
|
||||
"LOADING_ASSETS": "Chargement des ressources...",
|
||||
"PLEASE_WAIT": "Veuillez patienter...",
|
||||
"FOUND_NEW_ASSETS": "Nouvelles ressources trouvées: %s",
|
||||
"HELLO_MY_FRIEND": "Bonjour, mon ami !"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"hi-IN"
|
||||
"type": "hi-IN"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"चेतावनी",
|
||||
"INFO":"जानकारी",
|
||||
"ERROR":"त्रुटि",
|
||||
"WARNING": "चेतावनी",
|
||||
"INFO": "जानकारी",
|
||||
"ERROR": "त्रुटि",
|
||||
"VERSION": "संस्करण ",
|
||||
"LOADING_PROTOCOL":"सर्वर से कनेक्ट हो रहे हैं...",
|
||||
"INITIALIZING":"आरंभीकरण...",
|
||||
"PIN_ERROR":"कृपया सिम कार्ड डालें",
|
||||
"REG_ERROR":"नेटवर्क तक पहुंच नहीं हो सकती, कृपया डेटा कार्ड स्थिति जांचें",
|
||||
"DETECTING_MODULE":"मॉड्यूल का पता लगाया जा रहा है...",
|
||||
"REGISTERING_NETWORK":"नेटवर्क की प्रतीक्षा...",
|
||||
"CHECKING_NEW_VERSION":"नया संस्करण जाँच रहे हैं...",
|
||||
"CHECK_NEW_VERSION_FAILED":"नया संस्करण जाँचना असफल, %d सेकंड में पुनः प्रयास: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK":"Wi-Fi पर स्विच कर रहे हैं...",
|
||||
"SWITCH_TO_4G_NETWORK":"4G पर स्विच कर रहे हैं...",
|
||||
|
||||
"STANDBY":"स्टैंडबाय",
|
||||
"CONNECT_TO":"कनेक्ट करें ",
|
||||
"CONNECTING":"कनेक्ट हो रहे हैं...",
|
||||
"CONNECTED_TO":"कनेक्ट हो गए ",
|
||||
|
||||
"LISTENING":"सुन रहे हैं...",
|
||||
"SPEAKING":"बोल रहे हैं...",
|
||||
|
||||
"SERVER_NOT_FOUND":"उपलब्ध सेवा खोज रहे हैं",
|
||||
"SERVER_NOT_CONNECTED":"सेवा से कनेक्ट नहीं हो सकते, कृपया बाद में कोशिश करें",
|
||||
"SERVER_TIMEOUT":"प्रतिक्रिया का समय समाप्त",
|
||||
"SERVER_ERROR":"भेजना असफल, कृपया नेटवर्क जांचें",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"फोन को हॉटस्पॉट से कनेक्ट करें ",
|
||||
"ACCESS_VIA_BROWSER":",ब्राउज़र के माध्यम से पहुंचें ",
|
||||
"WIFI_CONFIG_MODE":"नेटवर्क कॉन्फ़िगरेशन मोड",
|
||||
"ENTERING_WIFI_CONFIG_MODE":"नेटवर्क कॉन्फ़िगरेशन मोड में प्रवेश...",
|
||||
"SCANNING_WIFI":"Wi-Fi स्कैन कर रहे हैं...",
|
||||
|
||||
"LOADING_PROTOCOL": "सर्वर से कनेक्ट हो रहे हैं...",
|
||||
"INITIALIZING": "आरंभीकरण...",
|
||||
"PIN_ERROR": "कृपया सिम कार्ड डालें",
|
||||
"REG_ERROR": "नेटवर्क तक पहुंच नहीं हो सकती, कृपया डेटा कार्ड स्थिति जांचें",
|
||||
"DETECTING_MODULE": "मॉड्यूल का पता लगाया जा रहा है...",
|
||||
"REGISTERING_NETWORK": "नेटवर्क की प्रतीक्षा...",
|
||||
"CHECKING_NEW_VERSION": "नया संस्करण जाँच रहे हैं...",
|
||||
"CHECK_NEW_VERSION_FAILED": "नया संस्करण जाँचना असफल, %d सेकंड में पुनः प्रयास: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK": "Wi-Fi पर स्विच कर रहे हैं...",
|
||||
"SWITCH_TO_4G_NETWORK": "4G पर स्विच कर रहे हैं...",
|
||||
"STANDBY": "स्टैंडबाय",
|
||||
"CONNECT_TO": "कनेक्ट करें ",
|
||||
"CONNECTING": "कनेक्ट हो रहे हैं...",
|
||||
"CONNECTED_TO": "कनेक्ट हो गए ",
|
||||
"LISTENING": "सुन रहे हैं...",
|
||||
"SPEAKING": "बोल रहे हैं...",
|
||||
"SERVER_NOT_FOUND": "उपलब्ध सेवा खोज रहे हैं",
|
||||
"SERVER_NOT_CONNECTED": "सेवा से कनेक्ट नहीं हो सकते, कृपया बाद में कोशिश करें",
|
||||
"SERVER_TIMEOUT": "प्रतिक्रिया का समय समाप्त",
|
||||
"SERVER_ERROR": "भेजना असफल, कृपया नेटवर्क जांचें",
|
||||
"CONNECT_TO_HOTSPOT": "फोन को हॉटस्पॉट से कनेक्ट करें ",
|
||||
"ACCESS_VIA_BROWSER": ",ब्राउज़र के माध्यम से पहुंचें ",
|
||||
"WIFI_CONFIG_MODE": "नेटवर्क कॉन्फ़िगरेशन मोड",
|
||||
"ENTERING_WIFI_CONFIG_MODE": "नेटवर्क कॉन्फ़िगरेशन मोड में प्रवेश...",
|
||||
"SCANNING_WIFI": "Wi-Fi स्कैन कर रहे हैं...",
|
||||
"NEW_VERSION": "नया संस्करण ",
|
||||
"OTA_UPGRADE":"OTA अपग्रेड",
|
||||
"UPGRADING":"सिस्टम अपग्रेड हो रहा है...",
|
||||
"UPGRADE_FAILED":"अपग्रेड असफल",
|
||||
"ACTIVATION":"डिवाइस सक्रियण",
|
||||
|
||||
"BATTERY_LOW":"बैटरी कम",
|
||||
"BATTERY_CHARGING":"चार्ज हो रही है",
|
||||
"BATTERY_FULL":"बैटरी फुल",
|
||||
"BATTERY_NEED_CHARGE":"बैटरी कम है, कृपया चार्ज करें",
|
||||
|
||||
"VOLUME":"आवाज़ ",
|
||||
"MUTED":"म्यूट",
|
||||
"MAX_VOLUME":"अधिकतम आवाज़",
|
||||
|
||||
"RTC_MODE_OFF":"AEC बंद",
|
||||
"RTC_MODE_ON":"AEC चालू"
|
||||
"OTA_UPGRADE": "OTA अपग्रेड",
|
||||
"UPGRADING": "सिस्टम अपग्रेड हो रहा है...",
|
||||
"UPGRADE_FAILED": "अपग्रेड असफल",
|
||||
"ACTIVATION": "डिवाइस सक्रियण",
|
||||
"BATTERY_LOW": "बैटरी कम",
|
||||
"BATTERY_CHARGING": "चार्ज हो रही है",
|
||||
"BATTERY_FULL": "बैटरी फुल",
|
||||
"BATTERY_NEED_CHARGE": "बैटरी कम है, कृपया चार्ज करें",
|
||||
"VOLUME": "आवाज़ ",
|
||||
"MUTED": "म्यूट",
|
||||
"MAX_VOLUME": "अधिकतम आवाज़",
|
||||
"RTC_MODE_OFF": "AEC बंद",
|
||||
"RTC_MODE_ON": "AEC चालू",
|
||||
"DOWNLOAD_ASSETS_FAILED": "संसाधन डाउनलोड करने में विफल",
|
||||
"LOADING_ASSETS": "संसाधन लोड हो रहे हैं...",
|
||||
"PLEASE_WAIT": "कृपया प्रतीक्षा करें...",
|
||||
"FOUND_NEW_ASSETS": "नए संसाधन मिले: %s",
|
||||
"HELLO_MY_FRIEND": "नमस्ते, मेरे दोस्त!"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"id-ID"
|
||||
"type": "id-ID"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"Peringatan",
|
||||
"INFO":"Informasi",
|
||||
"ERROR":"Kesalahan",
|
||||
"WARNING": "Peringatan",
|
||||
"INFO": "Informasi",
|
||||
"ERROR": "Kesalahan",
|
||||
"VERSION": "Versi ",
|
||||
"LOADING_PROTOCOL":"Menghubungkan ke server...",
|
||||
"INITIALIZING":"Menginisialisasi...",
|
||||
"PIN_ERROR":"Silakan masukkan kartu SIM",
|
||||
"REG_ERROR":"Tidak dapat mengakses jaringan, periksa status kartu data",
|
||||
"DETECTING_MODULE":"Mendeteksi modul...",
|
||||
"REGISTERING_NETWORK":"Menunggu jaringan...",
|
||||
"CHECKING_NEW_VERSION":"Memeriksa versi baru...",
|
||||
"CHECK_NEW_VERSION_FAILED":"Pemeriksaan versi baru gagal, mencoba lagi dalam %d detik: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK":"Beralih ke Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK":"Beralih ke 4G...",
|
||||
|
||||
"STANDBY":"Siaga",
|
||||
"CONNECT_TO":"Hubungkan ke ",
|
||||
"CONNECTING":"Menghubungkan...",
|
||||
"CONNECTED_TO":"Terhubung ke ",
|
||||
|
||||
"LISTENING":"Mendengarkan...",
|
||||
"SPEAKING":"Berbicara...",
|
||||
|
||||
"SERVER_NOT_FOUND":"Mencari layanan yang tersedia",
|
||||
"SERVER_NOT_CONNECTED":"Tidak dapat terhubung ke layanan, coba lagi nanti",
|
||||
"SERVER_TIMEOUT":"Waktu respons habis",
|
||||
"SERVER_ERROR":"Pengiriman gagal, periksa jaringan",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"Hubungkan ponsel ke hotspot ",
|
||||
"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...",
|
||||
|
||||
"LOADING_PROTOCOL": "Menghubungkan ke server...",
|
||||
"INITIALIZING": "Menginisialisasi...",
|
||||
"PIN_ERROR": "Silakan masukkan kartu SIM",
|
||||
"REG_ERROR": "Tidak dapat mengakses jaringan, periksa status kartu data",
|
||||
"DETECTING_MODULE": "Mendeteksi modul...",
|
||||
"REGISTERING_NETWORK": "Menunggu jaringan...",
|
||||
"CHECKING_NEW_VERSION": "Memeriksa versi baru...",
|
||||
"CHECK_NEW_VERSION_FAILED": "Pemeriksaan versi baru gagal, mencoba lagi dalam %d detik: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK": "Beralih ke Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK": "Beralih ke 4G...",
|
||||
"STANDBY": "Siaga",
|
||||
"CONNECT_TO": "Hubungkan ke ",
|
||||
"CONNECTING": "Menghubungkan...",
|
||||
"CONNECTED_TO": "Terhubung ke ",
|
||||
"LISTENING": "Mendengarkan...",
|
||||
"SPEAKING": "Berbicara...",
|
||||
"SERVER_NOT_FOUND": "Mencari layanan yang tersedia",
|
||||
"SERVER_NOT_CONNECTED": "Tidak dapat terhubung ke layanan, coba lagi nanti",
|
||||
"SERVER_TIMEOUT": "Waktu respons habis",
|
||||
"SERVER_ERROR": "Pengiriman gagal, periksa jaringan",
|
||||
"CONNECT_TO_HOTSPOT": "Hubungkan ponsel ke hotspot ",
|
||||
"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 ",
|
||||
"OTA_UPGRADE":"Pembaruan OTA",
|
||||
"UPGRADING":"Memperbarui sistem...",
|
||||
"UPGRADE_FAILED":"Pembaruan gagal",
|
||||
"ACTIVATION":"Aktivasi perangkat",
|
||||
|
||||
"BATTERY_LOW":"Baterai lemah",
|
||||
"BATTERY_CHARGING":"Mengisi",
|
||||
"BATTERY_FULL":"Baterai penuh",
|
||||
"BATTERY_NEED_CHARGE":"Baterai lemah, silakan isi",
|
||||
|
||||
"VOLUME":"Volume ",
|
||||
"MUTED":"Bisu",
|
||||
"MAX_VOLUME":"Volume maksimum",
|
||||
|
||||
"RTC_MODE_OFF":"AEC mati",
|
||||
"RTC_MODE_ON":"AEC nyala"
|
||||
"OTA_UPGRADE": "Pembaruan OTA",
|
||||
"UPGRADING": "Memperbarui sistem...",
|
||||
"UPGRADE_FAILED": "Pembaruan gagal",
|
||||
"ACTIVATION": "Aktivasi perangkat",
|
||||
"BATTERY_LOW": "Baterai lemah",
|
||||
"BATTERY_CHARGING": "Mengisi",
|
||||
"BATTERY_FULL": "Baterai penuh",
|
||||
"BATTERY_NEED_CHARGE": "Baterai lemah, silakan isi",
|
||||
"VOLUME": "Volume ",
|
||||
"MUTED": "Bisu",
|
||||
"MAX_VOLUME": "Volume maksimum",
|
||||
"RTC_MODE_OFF": "AEC mati",
|
||||
"RTC_MODE_ON": "AEC nyala",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Gagal mengunduh aset",
|
||||
"LOADING_ASSETS": "Memuat aset...",
|
||||
"PLEASE_WAIT": "Mohon tunggu...",
|
||||
"FOUND_NEW_ASSETS": "Ditemukan aset baru: %s",
|
||||
"HELLO_MY_FRIEND": "Halo, teman saya!"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"it-IT"
|
||||
"type": "it-IT"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"Avviso",
|
||||
"INFO":"Informazione",
|
||||
"ERROR":"Errore",
|
||||
"WARNING": "Avviso",
|
||||
"INFO": "Informazione",
|
||||
"ERROR": "Errore",
|
||||
"VERSION": "Versione ",
|
||||
"LOADING_PROTOCOL":"Connessione al server...",
|
||||
"INITIALIZING":"Inizializzazione...",
|
||||
"PIN_ERROR":"Inserire la scheda SIM",
|
||||
"REG_ERROR":"Impossibile accedere alla rete, controllare lo stato della scheda dati",
|
||||
"DETECTING_MODULE":"Rilevamento modulo...",
|
||||
"REGISTERING_NETWORK":"In attesa della rete...",
|
||||
"CHECKING_NEW_VERSION":"Controllo nuova versione...",
|
||||
"CHECK_NEW_VERSION_FAILED":"Controllo nuova versione fallito, riprovo tra %d secondi: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK":"Passaggio a Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK":"Passaggio a 4G...",
|
||||
|
||||
"STANDBY":"In attesa",
|
||||
"CONNECT_TO":"Connetti a ",
|
||||
"CONNECTING":"Connessione...",
|
||||
"CONNECTED_TO":"Connesso a ",
|
||||
|
||||
"LISTENING":"In ascolto...",
|
||||
"SPEAKING":"Parlando...",
|
||||
|
||||
"SERVER_NOT_FOUND":"Ricerca servizio disponibile",
|
||||
"SERVER_NOT_CONNECTED":"Impossibile connettersi al servizio, riprovare più tardi",
|
||||
"SERVER_TIMEOUT":"Timeout risposta",
|
||||
"SERVER_ERROR":"Invio fallito, controllare la rete",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"Connetti telefono al hotspot ",
|
||||
"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...",
|
||||
|
||||
"LOADING_PROTOCOL": "Connessione al server...",
|
||||
"INITIALIZING": "Inizializzazione...",
|
||||
"PIN_ERROR": "Inserire la scheda SIM",
|
||||
"REG_ERROR": "Impossibile accedere alla rete, controllare lo stato della scheda dati",
|
||||
"DETECTING_MODULE": "Rilevamento modulo...",
|
||||
"REGISTERING_NETWORK": "In attesa della rete...",
|
||||
"CHECKING_NEW_VERSION": "Controllo nuova versione...",
|
||||
"CHECK_NEW_VERSION_FAILED": "Controllo nuova versione fallito, riprovo tra %d secondi: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK": "Passaggio a Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK": "Passaggio a 4G...",
|
||||
"STANDBY": "In attesa",
|
||||
"CONNECT_TO": "Connetti a ",
|
||||
"CONNECTING": "Connessione...",
|
||||
"CONNECTED_TO": "Connesso a ",
|
||||
"LISTENING": "In ascolto...",
|
||||
"SPEAKING": "Parlando...",
|
||||
"SERVER_NOT_FOUND": "Ricerca servizio disponibile",
|
||||
"SERVER_NOT_CONNECTED": "Impossibile connettersi al servizio, riprovare più tardi",
|
||||
"SERVER_TIMEOUT": "Timeout risposta",
|
||||
"SERVER_ERROR": "Invio fallito, controllare la rete",
|
||||
"CONNECT_TO_HOTSPOT": "Connetti telefono al hotspot ",
|
||||
"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 ",
|
||||
"OTA_UPGRADE":"Aggiornamento OTA",
|
||||
"UPGRADING":"Aggiornamento sistema...",
|
||||
"UPGRADE_FAILED":"Aggiornamento fallito",
|
||||
"ACTIVATION":"Attivazione dispositivo",
|
||||
|
||||
"BATTERY_LOW":"Batteria scarica",
|
||||
"BATTERY_CHARGING":"In carica",
|
||||
"BATTERY_FULL":"Batteria piena",
|
||||
"BATTERY_NEED_CHARGE":"Batteria scarica, ricaricare",
|
||||
|
||||
"VOLUME":"Volume ",
|
||||
"MUTED":"Silenziato",
|
||||
"MAX_VOLUME":"Volume massimo",
|
||||
|
||||
"RTC_MODE_OFF":"AEC disattivato",
|
||||
"RTC_MODE_ON":"AEC attivato"
|
||||
"OTA_UPGRADE": "Aggiornamento OTA",
|
||||
"UPGRADING": "Aggiornamento sistema...",
|
||||
"UPGRADE_FAILED": "Aggiornamento fallito",
|
||||
"ACTIVATION": "Attivazione dispositivo",
|
||||
"BATTERY_LOW": "Batteria scarica",
|
||||
"BATTERY_CHARGING": "In carica",
|
||||
"BATTERY_FULL": "Batteria piena",
|
||||
"BATTERY_NEED_CHARGE": "Batteria scarica, ricaricare",
|
||||
"VOLUME": "Volume ",
|
||||
"MUTED": "Silenziato",
|
||||
"MAX_VOLUME": "Volume massimo",
|
||||
"RTC_MODE_OFF": "AEC disattivato",
|
||||
"RTC_MODE_ON": "AEC attivato",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Impossibile scaricare le risorse",
|
||||
"LOADING_ASSETS": "Caricamento risorse...",
|
||||
"PLEASE_WAIT": "Attendere prego...",
|
||||
"FOUND_NEW_ASSETS": "Trovate nuove risorse: %s",
|
||||
"HELLO_MY_FRIEND": "Ciao, amico mio!"
|
||||
}
|
||||
}
|
||||
@@ -17,42 +17,39 @@
|
||||
"CHECK_NEW_VERSION_FAILED": "更新確認に失敗しました。%d 秒後に再試行します: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK": "Wi-Fiに切り替え中...",
|
||||
"SWITCH_TO_4G_NETWORK": "4Gに切り替え中...",
|
||||
|
||||
"STANDBY": "待機中",
|
||||
"CONNECT_TO": "接続先 ",
|
||||
"CONNECTING": "接続中...",
|
||||
"CONNECTED_TO": "接続完了 ",
|
||||
|
||||
"LISTENING": "リスニング中...",
|
||||
"SPEAKING": "話しています...",
|
||||
|
||||
"SERVER_NOT_FOUND": "利用可能なサーバーを探しています",
|
||||
"SERVER_NOT_CONNECTED": "サーバーに接続できません。後でもう一度お試しください",
|
||||
"SERVER_TIMEOUT": "応答待機時間が終了しました",
|
||||
"SERVER_ERROR": "送信に失敗しました。ネットワークを確認してください",
|
||||
|
||||
"CONNECT_TO_HOTSPOT": "スマートフォンをWi-Fi ",
|
||||
"ACCESS_VIA_BROWSER": " に接続し、ブラウザでアクセスしてください ",
|
||||
"WIFI_CONFIG_MODE": "ネットワーク設定モード",
|
||||
"ENTERING_WIFI_CONFIG_MODE": "ネットワーク設定中...",
|
||||
"SCANNING_WIFI": "Wi-Fiをスキャン中...",
|
||||
|
||||
"NEW_VERSION": "新しいバージョン ",
|
||||
"OTA_UPGRADE": "OTAアップグレード",
|
||||
"UPGRADING": "システムをアップグレード中...",
|
||||
"UPGRADE_FAILED": "アップグレード失敗",
|
||||
"ACTIVATION": "デバイスをアクティベート",
|
||||
|
||||
"BATTERY_LOW": "バッテリーが少なくなっています",
|
||||
"BATTERY_CHARGING": "充電中",
|
||||
"BATTERY_FULL": "バッテリー満タン",
|
||||
"BATTERY_NEED_CHARGE": "バッテリーが低下しています。充電してください",
|
||||
|
||||
"VOLUME": "音量 ",
|
||||
"MUTED": "ミュートされています",
|
||||
"MAX_VOLUME": "最大音量",
|
||||
|
||||
"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": "こんにちは、友達!"
|
||||
}
|
||||
}
|
||||
@@ -17,43 +17,40 @@
|
||||
"CHECK_NEW_VERSION_FAILED": "새 버전 확인에 실패했습니다. %d초 후에 다시 시도합니다: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK": "Wi-Fi로 전환 중...",
|
||||
"SWITCH_TO_4G_NETWORK": "4G로 전환 중...",
|
||||
|
||||
"STANDBY": "대기",
|
||||
"CONNECT_TO": "연결 대상: ",
|
||||
"CONNECTING": "연결 중...",
|
||||
"CONNECTION_SUCCESSFUL": "연결 성공",
|
||||
"CONNECTED_TO": "연결됨: ",
|
||||
|
||||
"LISTENING": "듣는 중...",
|
||||
"SPEAKING": "말하는 중...",
|
||||
|
||||
"SERVER_NOT_FOUND": "사용 가능한 서비스를 찾는 중",
|
||||
"SERVER_NOT_CONNECTED": "서비스에 연결할 수 없습니다. 나중에 다시 시도하세요",
|
||||
"SERVER_TIMEOUT": "응답 대기 시간 초과",
|
||||
"SERVER_ERROR": "전송 실패, 네트워크를 확인하세요",
|
||||
|
||||
"CONNECT_TO_HOTSPOT": "핫스팟: ",
|
||||
"ACCESS_VIA_BROWSER": " 설정 URL: ",
|
||||
"WIFI_CONFIG_MODE": "Wi-Fi 설정 모드",
|
||||
"ENTERING_WIFI_CONFIG_MODE": "Wi-Fi 설정 모드 진입 중...",
|
||||
"SCANNING_WIFI": "Wi-Fi 스캔 중...",
|
||||
|
||||
"NEW_VERSION": "새 버전 ",
|
||||
"OTA_UPGRADE": "OTA 업그레이드",
|
||||
"UPGRADING": "시스템 업그레이드 중...",
|
||||
"UPGRADE_FAILED": "업그레이드 실패",
|
||||
"ACTIVATION": "활성화",
|
||||
|
||||
"BATTERY_LOW": "배터리 부족",
|
||||
"BATTERY_CHARGING": "충전 중",
|
||||
"BATTERY_FULL": "배터리 완충",
|
||||
"BATTERY_NEED_CHARGE": "배터리 부족, 충전하세요",
|
||||
|
||||
"VOLUME": "볼륨 ",
|
||||
"MUTED": "음소거",
|
||||
"MAX_VOLUME": "최대 볼륨",
|
||||
|
||||
"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": "안녕하세요, 친구!"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"pl-PL"
|
||||
"type": "pl-PL"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"Ostrzeżenie",
|
||||
"INFO":"Informacja",
|
||||
"ERROR":"Błąd",
|
||||
"WARNING": "Ostrzeżenie",
|
||||
"INFO": "Informacja",
|
||||
"ERROR": "Błąd",
|
||||
"VERSION": "Wersja ",
|
||||
"LOADING_PROTOCOL":"Łączenie z serwerem...",
|
||||
"INITIALIZING":"Inicjalizacja...",
|
||||
"PIN_ERROR":"Proszę włożyć kartę SIM",
|
||||
"REG_ERROR":"Nie można uzyskać dostępu do sieci, sprawdź stan karty danych",
|
||||
"DETECTING_MODULE":"Wykrywanie modułu...",
|
||||
"REGISTERING_NETWORK":"Oczekiwanie na sieć...",
|
||||
"CHECKING_NEW_VERSION":"Sprawdzanie nowej wersji...",
|
||||
"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_4G_NETWORK":"Przełączanie na 4G...",
|
||||
|
||||
"STANDBY":"Gotowość",
|
||||
"CONNECT_TO":"Połącz z ",
|
||||
"CONNECTING":"Łączenie...",
|
||||
"CONNECTED_TO":"Połączono z ",
|
||||
|
||||
"LISTENING":"Słuchanie...",
|
||||
"SPEAKING":"Mówienie...",
|
||||
|
||||
"SERVER_NOT_FOUND":"Szukanie dostępnej usługi",
|
||||
"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_ERROR":"Wysyłanie nie powiodło się, sprawdź sieć",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"Podłącz telefon do hotspotu ",
|
||||
"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...",
|
||||
|
||||
"LOADING_PROTOCOL": "Łączenie z serwerem...",
|
||||
"INITIALIZING": "Inicjalizacja...",
|
||||
"PIN_ERROR": "Proszę włożyć kartę SIM",
|
||||
"REG_ERROR": "Nie można uzyskać dostępu do sieci, sprawdź stan karty danych",
|
||||
"DETECTING_MODULE": "Wykrywanie modułu...",
|
||||
"REGISTERING_NETWORK": "Oczekiwanie na sieć...",
|
||||
"CHECKING_NEW_VERSION": "Sprawdzanie nowej wersji...",
|
||||
"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_4G_NETWORK": "Przełączanie na 4G...",
|
||||
"STANDBY": "Gotowość",
|
||||
"CONNECT_TO": "Połącz z ",
|
||||
"CONNECTING": "Łączenie...",
|
||||
"CONNECTED_TO": "Połączono z ",
|
||||
"LISTENING": "Słuchanie...",
|
||||
"SPEAKING": "Mówienie...",
|
||||
"SERVER_NOT_FOUND": "Szukanie dostępnej usługi",
|
||||
"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_ERROR": "Wysyłanie nie powiodło się, sprawdź sieć",
|
||||
"CONNECT_TO_HOTSPOT": "Podłącz telefon do hotspotu ",
|
||||
"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 ",
|
||||
"OTA_UPGRADE":"Aktualizacja OTA",
|
||||
"UPGRADING":"Aktualizacja systemu...",
|
||||
"UPGRADE_FAILED":"Aktualizacja nie powiodła się",
|
||||
"ACTIVATION":"Aktywacja urządzenia",
|
||||
|
||||
"BATTERY_LOW":"Niski poziom baterii",
|
||||
"BATTERY_CHARGING":"Ładowanie",
|
||||
"BATTERY_FULL":"Bateria pełna",
|
||||
"BATTERY_NEED_CHARGE":"Niski poziom baterii, proszę naładować",
|
||||
|
||||
"VOLUME":"Głośność ",
|
||||
"MUTED":"Wyciszony",
|
||||
"MAX_VOLUME":"Maksymalna głośność",
|
||||
|
||||
"RTC_MODE_OFF":"AEC wyłączony",
|
||||
"RTC_MODE_ON":"AEC włączony"
|
||||
"OTA_UPGRADE": "Aktualizacja OTA",
|
||||
"UPGRADING": "Aktualizacja systemu...",
|
||||
"UPGRADE_FAILED": "Aktualizacja nie powiodła się",
|
||||
"ACTIVATION": "Aktywacja urządzenia",
|
||||
"BATTERY_LOW": "Niski poziom baterii",
|
||||
"BATTERY_CHARGING": "Ładowanie",
|
||||
"BATTERY_FULL": "Bateria pełna",
|
||||
"BATTERY_NEED_CHARGE": "Niski poziom baterii, proszę naładować",
|
||||
"VOLUME": "Głośność ",
|
||||
"MUTED": "Wyciszony",
|
||||
"MAX_VOLUME": "Maksymalna głośność",
|
||||
"RTC_MODE_OFF": "AEC wyłączony",
|
||||
"RTC_MODE_ON": "AEC włączony",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Nie udało się pobrać zasobów",
|
||||
"LOADING_ASSETS": "Ładowanie zasobów...",
|
||||
"PLEASE_WAIT": "Proszę czekać...",
|
||||
"FOUND_NEW_ASSETS": "Znaleziono nowe zasoby: %s",
|
||||
"HELLO_MY_FRIEND": "Cześć, mój przyjacielu!"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"pt-PT"
|
||||
"type": "pt-PT"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"Aviso",
|
||||
"INFO":"Informação",
|
||||
"ERROR":"Erro",
|
||||
"WARNING": "Aviso",
|
||||
"INFO": "Informação",
|
||||
"ERROR": "Erro",
|
||||
"VERSION": "Versão ",
|
||||
"LOADING_PROTOCOL":"Ligando ao servidor...",
|
||||
"INITIALIZING":"A inicializar...",
|
||||
"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",
|
||||
"DETECTING_MODULE":"A detectar módulo...",
|
||||
"REGISTERING_NETWORK":"À espera da rede...",
|
||||
"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",
|
||||
"SWITCH_TO_WIFI_NETWORK":"A mudar para Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK":"A mudar para 4G...",
|
||||
|
||||
"STANDBY":"Em espera",
|
||||
"CONNECT_TO":"Ligar a ",
|
||||
"CONNECTING":"A ligar...",
|
||||
"CONNECTED_TO":"Ligado a ",
|
||||
|
||||
"LISTENING":"A escutar...",
|
||||
"SPEAKING":"A falar...",
|
||||
|
||||
"SERVER_NOT_FOUND":"A procurar serviço disponível",
|
||||
"SERVER_NOT_CONNECTED":"Não é possível ligar ao serviço, tente mais tarde",
|
||||
"SERVER_TIMEOUT":"Tempo limite de resposta",
|
||||
"SERVER_ERROR":"Falha no envio, verifique a rede",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"Ligue o telefone ao hotspot ",
|
||||
"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...",
|
||||
|
||||
"LOADING_PROTOCOL": "Ligando ao servidor...",
|
||||
"INITIALIZING": "A inicializar...",
|
||||
"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",
|
||||
"DETECTING_MODULE": "A detectar módulo...",
|
||||
"REGISTERING_NETWORK": "À espera da rede...",
|
||||
"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",
|
||||
"SWITCH_TO_WIFI_NETWORK": "A mudar para Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK": "A mudar para 4G...",
|
||||
"STANDBY": "Em espera",
|
||||
"CONNECT_TO": "Ligar a ",
|
||||
"CONNECTING": "A ligar...",
|
||||
"CONNECTED_TO": "Ligado a ",
|
||||
"LISTENING": "A escutar...",
|
||||
"SPEAKING": "A falar...",
|
||||
"SERVER_NOT_FOUND": "A procurar serviço disponível",
|
||||
"SERVER_NOT_CONNECTED": "Não é possível ligar ao serviço, tente mais tarde",
|
||||
"SERVER_TIMEOUT": "Tempo limite de resposta",
|
||||
"SERVER_ERROR": "Falha no envio, verifique a rede",
|
||||
"CONNECT_TO_HOTSPOT": "Ligue o telefone ao hotspot ",
|
||||
"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 ",
|
||||
"OTA_UPGRADE":"Atualização OTA",
|
||||
"UPGRADING":"A atualizar sistema...",
|
||||
"UPGRADE_FAILED":"Atualização falhada",
|
||||
"ACTIVATION":"Ativação do dispositivo",
|
||||
|
||||
"BATTERY_LOW":"Bateria fraca",
|
||||
"BATTERY_CHARGING":"A carregar",
|
||||
"BATTERY_FULL":"Bateria cheia",
|
||||
"BATTERY_NEED_CHARGE":"Bateria fraca, por favor carregue",
|
||||
|
||||
"VOLUME":"Volume ",
|
||||
"MUTED":"Silenciado",
|
||||
"MAX_VOLUME":"Volume máximo",
|
||||
|
||||
"RTC_MODE_OFF":"AEC desligado",
|
||||
"RTC_MODE_ON":"AEC ligado"
|
||||
"OTA_UPGRADE": "Atualização OTA",
|
||||
"UPGRADING": "A atualizar sistema...",
|
||||
"UPGRADE_FAILED": "Atualização falhada",
|
||||
"ACTIVATION": "Ativação do dispositivo",
|
||||
"BATTERY_LOW": "Bateria fraca",
|
||||
"BATTERY_CHARGING": "A carregar",
|
||||
"BATTERY_FULL": "Bateria cheia",
|
||||
"BATTERY_NEED_CHARGE": "Bateria fraca, por favor carregue",
|
||||
"VOLUME": "Volume ",
|
||||
"MUTED": "Silenciado",
|
||||
"MAX_VOLUME": "Volume máximo",
|
||||
"RTC_MODE_OFF": "AEC desligado",
|
||||
"RTC_MODE_ON": "AEC ligado",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Falha ao descarregar recursos",
|
||||
"LOADING_ASSETS": "A carregar recursos...",
|
||||
"PLEASE_WAIT": "Por favor aguarde...",
|
||||
"FOUND_NEW_ASSETS": "Encontrados novos recursos: %s",
|
||||
"HELLO_MY_FRIEND": "Olá, meu amigo!"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"ro-RO"
|
||||
"type": "ro-RO"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"Avertisment",
|
||||
"INFO":"Informație",
|
||||
"ERROR":"Eroare",
|
||||
"WARNING": "Avertisment",
|
||||
"INFO": "Informație",
|
||||
"ERROR": "Eroare",
|
||||
"VERSION": "Versiune ",
|
||||
"LOADING_PROTOCOL":"Se conectează la server...",
|
||||
"INITIALIZING":"Se inițializează...",
|
||||
"PIN_ERROR":"Vă rugăm să introduceți cardul SIM",
|
||||
"REG_ERROR":"Nu se poate accesa rețeaua, verificați starea cardului de date",
|
||||
"DETECTING_MODULE":"Se detectează modulul...",
|
||||
"REGISTERING_NETWORK":"Se așteaptă rețeaua...",
|
||||
"CHECKING_NEW_VERSION":"Se verifică versiunea nouă...",
|
||||
"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_4G_NETWORK":"Se comută la 4G...",
|
||||
|
||||
"STANDBY":"În așteptare",
|
||||
"CONNECT_TO":"Conectare la ",
|
||||
"CONNECTING":"Se conectează...",
|
||||
"CONNECTED_TO":"Conectat la ",
|
||||
|
||||
"LISTENING":"Se ascultă...",
|
||||
"SPEAKING":"Se vorbește...",
|
||||
|
||||
"SERVER_NOT_FOUND":"Se caută serviciul disponibil",
|
||||
"SERVER_NOT_CONNECTED":"Nu se poate conecta la serviciu, încercați mai târziu",
|
||||
"SERVER_TIMEOUT":"Timpul de răspuns a expirat",
|
||||
"SERVER_ERROR":"Trimiterea a eșuat, verificați rețeaua",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"Conectați telefonul la hotspot ",
|
||||
"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...",
|
||||
|
||||
"LOADING_PROTOCOL": "Se conectează la server...",
|
||||
"INITIALIZING": "Se inițializează...",
|
||||
"PIN_ERROR": "Vă rugăm să introduceți cardul SIM",
|
||||
"REG_ERROR": "Nu se poate accesa rețeaua, verificați starea cardului de date",
|
||||
"DETECTING_MODULE": "Se detectează modulul...",
|
||||
"REGISTERING_NETWORK": "Se așteaptă rețeaua...",
|
||||
"CHECKING_NEW_VERSION": "Se verifică versiunea nouă...",
|
||||
"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_4G_NETWORK": "Se comută la 4G...",
|
||||
"STANDBY": "În așteptare",
|
||||
"CONNECT_TO": "Conectare la ",
|
||||
"CONNECTING": "Se conectează...",
|
||||
"CONNECTED_TO": "Conectat la ",
|
||||
"LISTENING": "Se ascultă...",
|
||||
"SPEAKING": "Se vorbește...",
|
||||
"SERVER_NOT_FOUND": "Se caută serviciul disponibil",
|
||||
"SERVER_NOT_CONNECTED": "Nu se poate conecta la serviciu, încercați mai târziu",
|
||||
"SERVER_TIMEOUT": "Timpul de răspuns a expirat",
|
||||
"SERVER_ERROR": "Trimiterea a eșuat, verificați rețeaua",
|
||||
"CONNECT_TO_HOTSPOT": "Conectați telefonul la hotspot ",
|
||||
"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ă ",
|
||||
"OTA_UPGRADE":"Actualizare OTA",
|
||||
"UPGRADING":"Se actualizează sistemul...",
|
||||
"UPGRADE_FAILED":"Actualizarea a eșuat",
|
||||
"ACTIVATION":"Activarea dispozitivului",
|
||||
|
||||
"BATTERY_LOW":"Baterie scăzută",
|
||||
"BATTERY_CHARGING":"Se încarcă",
|
||||
"BATTERY_FULL":"Baterie plină",
|
||||
"BATTERY_NEED_CHARGE":"Baterie scăzută, vă rugăm să încărcați",
|
||||
|
||||
"VOLUME":"Volum ",
|
||||
"MUTED":"Silențios",
|
||||
"MAX_VOLUME":"Volum maxim",
|
||||
|
||||
"RTC_MODE_OFF":"AEC oprit",
|
||||
"RTC_MODE_ON":"AEC pornit"
|
||||
"OTA_UPGRADE": "Actualizare OTA",
|
||||
"UPGRADING": "Se actualizează sistemul...",
|
||||
"UPGRADE_FAILED": "Actualizarea a eșuat",
|
||||
"ACTIVATION": "Activarea dispozitivului",
|
||||
"BATTERY_LOW": "Baterie scăzută",
|
||||
"BATTERY_CHARGING": "Se încarcă",
|
||||
"BATTERY_FULL": "Baterie plină",
|
||||
"BATTERY_NEED_CHARGE": "Baterie scăzută, vă rugăm să încărcați",
|
||||
"VOLUME": "Volum ",
|
||||
"MUTED": "Silențios",
|
||||
"MAX_VOLUME": "Volum maxim",
|
||||
"RTC_MODE_OFF": "AEC oprit",
|
||||
"RTC_MODE_ON": "AEC pornit",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Eșec la descărcarea resurselor",
|
||||
"LOADING_ASSETS": "Se încarcă resursele...",
|
||||
"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!"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"ru-RU"
|
||||
"type": "ru-RU"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"Предупреждение",
|
||||
"INFO":"Информация",
|
||||
"ERROR":"Ошибка",
|
||||
"WARNING": "Предупреждение",
|
||||
"INFO": "Информация",
|
||||
"ERROR": "Ошибка",
|
||||
"VERSION": "Версия ",
|
||||
"LOADING_PROTOCOL":"Подключение к серверу...",
|
||||
"INITIALIZING":"Инициализация...",
|
||||
"PIN_ERROR":"Пожалуйста, вставьте SIM-карту",
|
||||
"REG_ERROR":"Невозможно подключиться к сети, проверьте состояние карты данных",
|
||||
"DETECTING_MODULE":"Обнаружение модуля...",
|
||||
"REGISTERING_NETWORK":"Ожидание сети...",
|
||||
"CHECKING_NEW_VERSION":"Проверка новой версии...",
|
||||
"CHECK_NEW_VERSION_FAILED":"Ошибка проверки новой версии, повтор через %d секунд: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK":"Переключение на Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK":"Переключение на 4G...",
|
||||
|
||||
"STANDBY":"Ожидание",
|
||||
"CONNECT_TO":"Подключение к ",
|
||||
"CONNECTING":"Подключение...",
|
||||
"CONNECTED_TO":"Подключено к ",
|
||||
|
||||
"LISTENING":"Прослушивание...",
|
||||
"SPEAKING":"Говорение...",
|
||||
|
||||
"SERVER_NOT_FOUND":"Поиск доступного сервиса",
|
||||
"SERVER_NOT_CONNECTED":"Невозможно подключиться к сервису, попробуйте позже",
|
||||
"SERVER_TIMEOUT":"Тайм-аут ответа",
|
||||
"SERVER_ERROR":"Ошибка отправки, проверьте сеть",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"Подключите телефон к точке доступа ",
|
||||
"ACCESS_VIA_BROWSER":",доступ через браузер ",
|
||||
"WIFI_CONFIG_MODE":"Режим настройки сети",
|
||||
"ENTERING_WIFI_CONFIG_MODE":"Вход в режим настройки сети...",
|
||||
"SCANNING_WIFI":"Сканирование Wi-Fi...",
|
||||
|
||||
"LOADING_PROTOCOL": "Подключение к серверу...",
|
||||
"INITIALIZING": "Инициализация...",
|
||||
"PIN_ERROR": "Пожалуйста, вставьте SIM-карту",
|
||||
"REG_ERROR": "Невозможно подключиться к сети, проверьте состояние карты данных",
|
||||
"DETECTING_MODULE": "Обнаружение модуля...",
|
||||
"REGISTERING_NETWORK": "Ожидание сети...",
|
||||
"CHECKING_NEW_VERSION": "Проверка новой версии...",
|
||||
"CHECK_NEW_VERSION_FAILED": "Ошибка проверки новой версии, повтор через %d секунд: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK": "Переключение на Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK": "Переключение на 4G...",
|
||||
"STANDBY": "Ожидание",
|
||||
"CONNECT_TO": "Подключение к ",
|
||||
"CONNECTING": "Подключение...",
|
||||
"CONNECTED_TO": "Подключено к ",
|
||||
"LISTENING": "Прослушивание...",
|
||||
"SPEAKING": "Говорение...",
|
||||
"SERVER_NOT_FOUND": "Поиск доступного сервиса",
|
||||
"SERVER_NOT_CONNECTED": "Невозможно подключиться к сервису, попробуйте позже",
|
||||
"SERVER_TIMEOUT": "Тайм-аут ответа",
|
||||
"SERVER_ERROR": "Ошибка отправки, проверьте сеть",
|
||||
"CONNECT_TO_HOTSPOT": "Подключите телефон к точке доступа ",
|
||||
"ACCESS_VIA_BROWSER": ",доступ через браузер ",
|
||||
"WIFI_CONFIG_MODE": "Режим настройки сети",
|
||||
"ENTERING_WIFI_CONFIG_MODE": "Вход в режим настройки сети...",
|
||||
"SCANNING_WIFI": "Сканирование Wi-Fi...",
|
||||
"NEW_VERSION": "Новая версия ",
|
||||
"OTA_UPGRADE":"Обновление OTA",
|
||||
"UPGRADING":"Обновление системы...",
|
||||
"UPGRADE_FAILED":"Обновление не удалось",
|
||||
"ACTIVATION":"Активация устройства",
|
||||
|
||||
"BATTERY_LOW":"Низкий заряд батареи",
|
||||
"BATTERY_CHARGING":"Зарядка",
|
||||
"BATTERY_FULL":"Батарея полная",
|
||||
"BATTERY_NEED_CHARGE":"Низкий заряд, пожалуйста, зарядите",
|
||||
|
||||
"VOLUME":"Громкость ",
|
||||
"MUTED":"Звук отключен",
|
||||
"MAX_VOLUME":"Максимальная громкость",
|
||||
|
||||
"RTC_MODE_OFF":"AEC выключен",
|
||||
"RTC_MODE_ON":"AEC включен"
|
||||
"OTA_UPGRADE": "Обновление OTA",
|
||||
"UPGRADING": "Обновление системы...",
|
||||
"UPGRADE_FAILED": "Обновление не удалось",
|
||||
"ACTIVATION": "Активация устройства",
|
||||
"BATTERY_LOW": "Низкий заряд батареи",
|
||||
"BATTERY_CHARGING": "Зарядка",
|
||||
"BATTERY_FULL": "Батарея полная",
|
||||
"BATTERY_NEED_CHARGE": "Низкий заряд, пожалуйста, зарядите",
|
||||
"VOLUME": "Громкость ",
|
||||
"MUTED": "Звук отключен",
|
||||
"MAX_VOLUME": "Максимальная громкость",
|
||||
"RTC_MODE_OFF": "AEC выключен",
|
||||
"RTC_MODE_ON": "AEC включен",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Не удалось загрузить ресурсы",
|
||||
"LOADING_ASSETS": "Загрузка ресурсов...",
|
||||
"PLEASE_WAIT": "Пожалуйста, подождите...",
|
||||
"FOUND_NEW_ASSETS": "Найдены новые ресурсы: %s",
|
||||
"HELLO_MY_FRIEND": "Привет, мой друг!"
|
||||
}
|
||||
}
|
||||
@@ -17,43 +17,40 @@
|
||||
"CHECK_NEW_VERSION_FAILED": "การตรวจสอบเวอร์ชันใหม่ล้มเหลว จะลองใหม่ใน %d วินาที: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK": "กำลังเปลี่ยนเป็น Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK": "กำลังเปลี่ยนเป็น 4G...",
|
||||
|
||||
"STANDBY": "พร้อม",
|
||||
"CONNECT_TO": "เชื่อมต่อกับ ",
|
||||
"CONNECTING": "กำลังเชื่อมต่อ...",
|
||||
"CONNECTION_SUCCESSFUL": "เชื่อมต่อสำเร็จ",
|
||||
"CONNECTED_TO": "เชื่อมต่อกับ ",
|
||||
|
||||
"LISTENING": "กำลังฟัง...",
|
||||
"SPEAKING": "กำลังพูด...",
|
||||
|
||||
"SERVER_NOT_FOUND": "กำลังค้นหาบริการที่ใช้งานได้",
|
||||
"SERVER_NOT_CONNECTED": "ไม่สามารถเชื่อมต่อกับบริการได้ กรุณาลองใหม่ในภายหลัง",
|
||||
"SERVER_TIMEOUT": "หมดเวลารอการตอบกลับ",
|
||||
"SERVER_ERROR": "การส่งข้อมูลล้มเหลว กรุณาตรวจสอบเครือข่าย",
|
||||
|
||||
"CONNECT_TO_HOTSPOT": "ฮอตสปอต: ",
|
||||
"ACCESS_VIA_BROWSER": " URL การตั้งค่า: ",
|
||||
"WIFI_CONFIG_MODE": "โหมดการตั้งค่า Wi-Fi",
|
||||
"ENTERING_WIFI_CONFIG_MODE": "กำลังเข้าสู่โหมดการตั้งค่า Wi-Fi...",
|
||||
"SCANNING_WIFI": "กำลังสแกน Wi-Fi...",
|
||||
|
||||
"NEW_VERSION": "เวอร์ชันใหม่ ",
|
||||
"OTA_UPGRADE": "การอัปเกรด OTA",
|
||||
"UPGRADING": "ระบบกำลังอัปเกรด...",
|
||||
"UPGRADE_FAILED": "การอัปเกรดล้มเหลว",
|
||||
"ACTIVATION": "การเปิดใช้งาน",
|
||||
|
||||
"BATTERY_LOW": "แบตเตอรี่ต่ำ",
|
||||
"BATTERY_CHARGING": "กำลังชาร์จ",
|
||||
"BATTERY_FULL": "แบตเตอรี่เต็ม",
|
||||
"BATTERY_NEED_CHARGE": "แบตเตอรี่ต่ำ กรุณาชาร์จ",
|
||||
|
||||
"VOLUME": "เสียง ",
|
||||
"MUTED": "ปิดเสียง",
|
||||
"MAX_VOLUME": "เสียงสูงสุด",
|
||||
|
||||
"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": "สวัสดี เพื่อนของฉัน!"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"tr-TR"
|
||||
"type": "tr-TR"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"Uyarı",
|
||||
"INFO":"Bilgi",
|
||||
"ERROR":"Hata",
|
||||
"WARNING": "Uyarı",
|
||||
"INFO": "Bilgi",
|
||||
"ERROR": "Hata",
|
||||
"VERSION": "Sürüm ",
|
||||
"LOADING_PROTOCOL":"Sunucuya bağlanıyor...",
|
||||
"INITIALIZING":"Başlatılıyor...",
|
||||
"PIN_ERROR":"Lütfen SIM kartı takın",
|
||||
"REG_ERROR":"Ağa erişilemiyor, veri kartı durumunu kontrol edin",
|
||||
"DETECTING_MODULE":"Modül algılanıyor...",
|
||||
"REGISTERING_NETWORK":"Ağ bekleniyor...",
|
||||
"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",
|
||||
"SWITCH_TO_WIFI_NETWORK":"Wi-Fi'ye geçiliyor...",
|
||||
"SWITCH_TO_4G_NETWORK":"4G'ye geçiliyor...",
|
||||
|
||||
"STANDBY":"Bekleme",
|
||||
"CONNECT_TO":"Bağlan ",
|
||||
"CONNECTING":"Bağlanıyor...",
|
||||
"CONNECTED_TO":"Bağlandı ",
|
||||
|
||||
"LISTENING":"Dinleniyor...",
|
||||
"SPEAKING":"Konuşuluyor...",
|
||||
|
||||
"SERVER_NOT_FOUND":"Mevcut hizmet aranıyor",
|
||||
"SERVER_NOT_CONNECTED":"Hizmete bağlanılamıyor, lütfen daha sonra deneyin",
|
||||
"SERVER_TIMEOUT":"Yanıt zaman aşımı",
|
||||
"SERVER_ERROR":"Gönderme başarısız, ağı kontrol edin",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"Telefonu hotspot'a bağlayın ",
|
||||
"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...",
|
||||
|
||||
"LOADING_PROTOCOL": "Sunucuya bağlanıyor...",
|
||||
"INITIALIZING": "Başlatılıyor...",
|
||||
"PIN_ERROR": "Lütfen SIM kartı takın",
|
||||
"REG_ERROR": "Ağa erişilemiyor, veri kartı durumunu kontrol edin",
|
||||
"DETECTING_MODULE": "Modül algılanıyor...",
|
||||
"REGISTERING_NETWORK": "Ağ bekleniyor...",
|
||||
"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",
|
||||
"SWITCH_TO_WIFI_NETWORK": "Wi-Fi'ye geçiliyor...",
|
||||
"SWITCH_TO_4G_NETWORK": "4G'ye geçiliyor...",
|
||||
"STANDBY": "Bekleme",
|
||||
"CONNECT_TO": "Bağlan ",
|
||||
"CONNECTING": "Bağlanıyor...",
|
||||
"CONNECTED_TO": "Bağlandı ",
|
||||
"LISTENING": "Dinleniyor...",
|
||||
"SPEAKING": "Konuşuluyor...",
|
||||
"SERVER_NOT_FOUND": "Mevcut hizmet aranıyor",
|
||||
"SERVER_NOT_CONNECTED": "Hizmete bağlanılamıyor, lütfen daha sonra deneyin",
|
||||
"SERVER_TIMEOUT": "Yanıt zaman aşımı",
|
||||
"SERVER_ERROR": "Gönderme başarısız, ağı kontrol edin",
|
||||
"CONNECT_TO_HOTSPOT": "Telefonu hotspot'a bağlayın ",
|
||||
"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 ",
|
||||
"OTA_UPGRADE":"OTA güncelleme",
|
||||
"UPGRADING":"Sistem güncelleniyor...",
|
||||
"UPGRADE_FAILED":"Güncelleme başarısız",
|
||||
"ACTIVATION":"Cihaz aktivasyonu",
|
||||
|
||||
"BATTERY_LOW":"Pil düşük",
|
||||
"BATTERY_CHARGING":"Şarj oluyor",
|
||||
"BATTERY_FULL":"Pil dolu",
|
||||
"BATTERY_NEED_CHARGE":"Pil düşük, lütfen şarj edin",
|
||||
|
||||
"VOLUME":"Ses ",
|
||||
"MUTED":"Sessiz",
|
||||
"MAX_VOLUME":"Maksimum ses",
|
||||
|
||||
"RTC_MODE_OFF":"AEC kapalı",
|
||||
"RTC_MODE_ON":"AEC açık"
|
||||
"OTA_UPGRADE": "OTA güncelleme",
|
||||
"UPGRADING": "Sistem güncelleniyor...",
|
||||
"UPGRADE_FAILED": "Güncelleme başarısız",
|
||||
"ACTIVATION": "Cihaz aktivasyonu",
|
||||
"BATTERY_LOW": "Pil düşük",
|
||||
"BATTERY_CHARGING": "Şarj oluyor",
|
||||
"BATTERY_FULL": "Pil dolu",
|
||||
"BATTERY_NEED_CHARGE": "Pil düşük, lütfen şarj edin",
|
||||
"VOLUME": "Ses ",
|
||||
"MUTED": "Sessiz",
|
||||
"MAX_VOLUME": "Maksimum ses",
|
||||
"RTC_MODE_OFF": "AEC kapalı",
|
||||
"RTC_MODE_ON": "AEC açık",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Varlıklar indirilemedi",
|
||||
"LOADING_ASSETS": "Varlıklar yükleniyor...",
|
||||
"PLEASE_WAIT": "Lütfen bekleyin...",
|
||||
"FOUND_NEW_ASSETS": "Yeni varlıklar bulundu: %s",
|
||||
"HELLO_MY_FRIEND": "Merhaba, arkadaşım!"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"uk-UA"
|
||||
"type": "uk-UA"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"Попередження",
|
||||
"INFO":"Інформація",
|
||||
"ERROR":"Помилка",
|
||||
"WARNING": "Попередження",
|
||||
"INFO": "Інформація",
|
||||
"ERROR": "Помилка",
|
||||
"VERSION": "Версія ",
|
||||
"LOADING_PROTOCOL":"Підключення до сервера...",
|
||||
"INITIALIZING":"Ініціалізація...",
|
||||
"PIN_ERROR":"Будь ласка, вставте SIM-карту",
|
||||
"REG_ERROR":"Неможливо отримати доступ до мережі, перевірте стан карти даних",
|
||||
"DETECTING_MODULE":"Виявлення модуля...",
|
||||
"REGISTERING_NETWORK":"Очікування мережі...",
|
||||
"CHECKING_NEW_VERSION":"Перевірка нової версії...",
|
||||
"CHECK_NEW_VERSION_FAILED":"Перевірка нової версії не вдалася, повтор через %d секунд: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK":"Перемикання на Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK":"Перемикання на 4G...",
|
||||
|
||||
"STANDBY":"Очікування",
|
||||
"CONNECT_TO":"Підключитися до ",
|
||||
"CONNECTING":"Підключення...",
|
||||
"CONNECTED_TO":"Підключено до ",
|
||||
|
||||
"LISTENING":"Прослуховування...",
|
||||
"SPEAKING":"Говоріння...",
|
||||
|
||||
"SERVER_NOT_FOUND":"Пошук доступного сервісу",
|
||||
"SERVER_NOT_CONNECTED":"Неможливо підключитися до сервісу, спробуйте пізніше",
|
||||
"SERVER_TIMEOUT":"Час очікування відповіді",
|
||||
"SERVER_ERROR":"Помилка відправки, перевірте мережу",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"Підключіть телефон до точки доступу ",
|
||||
"ACCESS_VIA_BROWSER":",доступ через браузер ",
|
||||
"WIFI_CONFIG_MODE":"Режим налаштування мережі",
|
||||
"ENTERING_WIFI_CONFIG_MODE":"Вхід у режим налаштування мережі...",
|
||||
"SCANNING_WIFI":"Сканування Wi-Fi...",
|
||||
|
||||
"LOADING_PROTOCOL": "Підключення до сервера...",
|
||||
"INITIALIZING": "Ініціалізація...",
|
||||
"PIN_ERROR": "Будь ласка, вставте SIM-карту",
|
||||
"REG_ERROR": "Неможливо отримати доступ до мережі, перевірте стан карти даних",
|
||||
"DETECTING_MODULE": "Виявлення модуля...",
|
||||
"REGISTERING_NETWORK": "Очікування мережі...",
|
||||
"CHECKING_NEW_VERSION": "Перевірка нової версії...",
|
||||
"CHECK_NEW_VERSION_FAILED": "Перевірка нової версії не вдалася, повтор через %d секунд: %s",
|
||||
"SWITCH_TO_WIFI_NETWORK": "Перемикання на Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK": "Перемикання на 4G...",
|
||||
"STANDBY": "Очікування",
|
||||
"CONNECT_TO": "Підключитися до ",
|
||||
"CONNECTING": "Підключення...",
|
||||
"CONNECTED_TO": "Підключено до ",
|
||||
"LISTENING": "Прослуховування...",
|
||||
"SPEAKING": "Говоріння...",
|
||||
"SERVER_NOT_FOUND": "Пошук доступного сервісу",
|
||||
"SERVER_NOT_CONNECTED": "Неможливо підключитися до сервісу, спробуйте пізніше",
|
||||
"SERVER_TIMEOUT": "Час очікування відповіді",
|
||||
"SERVER_ERROR": "Помилка відправки, перевірте мережу",
|
||||
"CONNECT_TO_HOTSPOT": "Підключіть телефон до точки доступу ",
|
||||
"ACCESS_VIA_BROWSER": ",доступ через браузер ",
|
||||
"WIFI_CONFIG_MODE": "Режим налаштування мережі",
|
||||
"ENTERING_WIFI_CONFIG_MODE": "Вхід у режим налаштування мережі...",
|
||||
"SCANNING_WIFI": "Сканування Wi-Fi...",
|
||||
"NEW_VERSION": "Нова версія ",
|
||||
"OTA_UPGRADE":"Оновлення OTA",
|
||||
"UPGRADING":"Оновлення системи...",
|
||||
"UPGRADE_FAILED":"Оновлення не вдалося",
|
||||
"ACTIVATION":"Активація пристрою",
|
||||
|
||||
"BATTERY_LOW":"Низький заряд батареї",
|
||||
"BATTERY_CHARGING":"Зарядка",
|
||||
"BATTERY_FULL":"Батарея повна",
|
||||
"BATTERY_NEED_CHARGE":"Низький заряд, будь ласка, зарядіть",
|
||||
|
||||
"VOLUME":"Гучність ",
|
||||
"MUTED":"Звук вимкнено",
|
||||
"MAX_VOLUME":"Максимальна гучність",
|
||||
|
||||
"RTC_MODE_OFF":"AEC вимкнено",
|
||||
"RTC_MODE_ON":"AEC увімкнено"
|
||||
"OTA_UPGRADE": "Оновлення OTA",
|
||||
"UPGRADING": "Оновлення системи...",
|
||||
"UPGRADE_FAILED": "Оновлення не вдалося",
|
||||
"ACTIVATION": "Активація пристрою",
|
||||
"BATTERY_LOW": "Низький заряд батареї",
|
||||
"BATTERY_CHARGING": "Зарядка",
|
||||
"BATTERY_FULL": "Батарея повна",
|
||||
"BATTERY_NEED_CHARGE": "Низький заряд, будь ласка, зарядіть",
|
||||
"VOLUME": "Гучність ",
|
||||
"MUTED": "Звук вимкнено",
|
||||
"MAX_VOLUME": "Максимальна гучність",
|
||||
"RTC_MODE_OFF": "AEC вимкнено",
|
||||
"RTC_MODE_ON": "AEC увімкнено",
|
||||
"DOWNLOAD_ASSETS_FAILED": "Не вдалося завантажити ресурси",
|
||||
"LOADING_ASSETS": "Завантаження ресурсів...",
|
||||
"PLEASE_WAIT": "Будь ласка, зачекайте...",
|
||||
"FOUND_NEW_ASSETS": "Знайдено нові ресурси: %s",
|
||||
"HELLO_MY_FRIEND": "Привіт, мій друже!"
|
||||
}
|
||||
}
|
||||
@@ -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",
|
||||
"SWITCH_TO_WIFI_NETWORK": "Đang chuyển sang Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK": "Đang chuyển sang 4G...",
|
||||
|
||||
"STANDBY": "Chờ",
|
||||
"CONNECT_TO": "Kết nối đến ",
|
||||
"CONNECTING": "Đang kết nối...",
|
||||
"CONNECTION_SUCCESSFUL": "Kết nối thành công",
|
||||
"CONNECTED_TO": "Đã kết nối đến ",
|
||||
|
||||
"LISTENING": "Đang lắng nghe...",
|
||||
"SPEAKING": "Đang nói...",
|
||||
|
||||
"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_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",
|
||||
|
||||
"CONNECT_TO_HOTSPOT": "Điểm phát sóng: ",
|
||||
"ACCESS_VIA_BROWSER": " URL cấu hình: ",
|
||||
"WIFI_CONFIG_MODE": "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...",
|
||||
|
||||
"NEW_VERSION": "Phiên bản mới ",
|
||||
"OTA_UPGRADE": "Nâng cấp OTA",
|
||||
"UPGRADING": "Hệ thống đang nâng cấp...",
|
||||
"UPGRADE_FAILED": "Nâng cấp thất bại",
|
||||
"ACTIVATION": "Kích hoạt",
|
||||
|
||||
"BATTERY_LOW": "Pin yếu",
|
||||
"BATTERY_CHARGING": "Đang sạc",
|
||||
"BATTERY_FULL": "Pin đầy",
|
||||
"BATTERY_NEED_CHARGE": "Pin yếu, vui lòng sạc",
|
||||
|
||||
"VOLUME": "Âm lượng ",
|
||||
"MUTED": "Tắt tiếng",
|
||||
"MAX_VOLUME": "Âm lượng tối đa",
|
||||
|
||||
"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!"
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,55 @@
|
||||
{
|
||||
"language": {
|
||||
"type" :"zh-CN"
|
||||
"type": "zh-CN"
|
||||
},
|
||||
"strings": {
|
||||
"WARNING":"警告",
|
||||
"INFO":"信息",
|
||||
"ERROR":"错误",
|
||||
"WARNING": "警告",
|
||||
"INFO": "信息",
|
||||
"ERROR": "错误",
|
||||
"VERSION": "版本 ",
|
||||
"LOADING_PROTOCOL":"登录服务器...",
|
||||
"INITIALIZING":"正在初始化...",
|
||||
"PIN_ERROR":"请插入 SIM 卡",
|
||||
"REG_ERROR":"无法接入网络,请检查流量卡状态",
|
||||
"DETECTING_MODULE":"检测模组...",
|
||||
"REGISTERING_NETWORK":"等待网络...",
|
||||
"CHECKING_NEW_VERSION":"检查新版本...",
|
||||
"CHECK_NEW_VERSION_FAILED":"检查新版本失败,将在 %d 秒后重试:%s",
|
||||
"SWITCH_TO_WIFI_NETWORK":"切换到 Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK":"切换到 4G...",
|
||||
|
||||
"STANDBY":"待命",
|
||||
"CONNECT_TO":"连接 ",
|
||||
"CONNECTING":"连接中...",
|
||||
"CONNECTED_TO":"已连接 ",
|
||||
|
||||
"LISTENING":"聆听中...",
|
||||
"SPEAKING":"说话中...",
|
||||
|
||||
"SERVER_NOT_FOUND":"正在寻找可用服务",
|
||||
"SERVER_NOT_CONNECTED":"无法连接服务,请稍后再试",
|
||||
"SERVER_TIMEOUT":"等待响应超时",
|
||||
"SERVER_ERROR":"发送失败,请检查网络",
|
||||
|
||||
"CONNECT_TO_HOTSPOT":"手机连接热点 ",
|
||||
"ACCESS_VIA_BROWSER":",浏览器访问 ",
|
||||
"WIFI_CONFIG_MODE":"配网模式",
|
||||
"ENTERING_WIFI_CONFIG_MODE":"进入配网模式...",
|
||||
"SCANNING_WIFI":"扫描 Wi-Fi...",
|
||||
|
||||
"LOADING_PROTOCOL": "登录服务器...",
|
||||
"INITIALIZING": "正在初始化...",
|
||||
"PIN_ERROR": "请插入 SIM 卡",
|
||||
"REG_ERROR": "无法接入网络,请检查流量卡状态",
|
||||
"DETECTING_MODULE": "检测模组...",
|
||||
"REGISTERING_NETWORK": "等待网络...",
|
||||
"CHECKING_NEW_VERSION": "检查新版本...",
|
||||
"CHECK_NEW_VERSION_FAILED": "检查新版本失败,将在 %d 秒后重试:%s",
|
||||
"SWITCH_TO_WIFI_NETWORK": "切换到 Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK": "切换到 4G...",
|
||||
"STANDBY": "待命",
|
||||
"CONNECT_TO": "连接 ",
|
||||
"CONNECTING": "连接中...",
|
||||
"CONNECTED_TO": "已连接 ",
|
||||
"LISTENING": "聆听中...",
|
||||
"SPEAKING": "说话中...",
|
||||
"SERVER_NOT_FOUND": "正在寻找可用服务",
|
||||
"SERVER_NOT_CONNECTED": "无法连接服务,请稍后再试",
|
||||
"SERVER_TIMEOUT": "等待响应超时",
|
||||
"SERVER_ERROR": "发送失败,请检查网络",
|
||||
"CONNECT_TO_HOTSPOT": "手机连接热点 ",
|
||||
"ACCESS_VIA_BROWSER": ",浏览器访问 ",
|
||||
"WIFI_CONFIG_MODE": "配网模式",
|
||||
"ENTERING_WIFI_CONFIG_MODE": "进入配网模式...",
|
||||
"SCANNING_WIFI": "扫描 Wi-Fi...",
|
||||
"NEW_VERSION": "新版本 ",
|
||||
"OTA_UPGRADE":"OTA 升级",
|
||||
"UPGRADING":"正在升级系统...",
|
||||
"UPGRADE_FAILED":"升级失败",
|
||||
"ACTIVATION":"激活设备",
|
||||
|
||||
"BATTERY_LOW":"电量不足",
|
||||
"BATTERY_CHARGING":"正在充电",
|
||||
"BATTERY_FULL":"电量已满",
|
||||
"BATTERY_NEED_CHARGE":"电量低,请充电",
|
||||
|
||||
"VOLUME":"音量 ",
|
||||
"MUTED":"已静音",
|
||||
"MAX_VOLUME":"最大音量",
|
||||
|
||||
"RTC_MODE_OFF":"AEC 关闭",
|
||||
"RTC_MODE_ON":"AEC 开启"
|
||||
"OTA_UPGRADE": "OTA 升级",
|
||||
"UPGRADING": "正在升级系统...",
|
||||
"UPGRADE_FAILED": "升级失败",
|
||||
"ACTIVATION": "激活设备",
|
||||
"BATTERY_LOW": "电量不足",
|
||||
"BATTERY_CHARGING": "正在充电",
|
||||
"BATTERY_FULL": "电量已满",
|
||||
"BATTERY_NEED_CHARGE": "电量低,请充电",
|
||||
"VOLUME": "音量 ",
|
||||
"MUTED": "已静音",
|
||||
"MAX_VOLUME": "最大音量",
|
||||
"RTC_MODE_OFF": "AEC 关闭",
|
||||
"RTC_MODE_ON": "AEC 开启",
|
||||
"DOWNLOAD_ASSETS_FAILED": "下载资源失败",
|
||||
"LOADING_ASSETS": "加载资源...",
|
||||
"PLEASE_WAIT": "请稍候...",
|
||||
"FOUND_NEW_ASSETS": "发现新资源: %s",
|
||||
"HELLO_MY_FRIEND": "你好,我的朋友!"
|
||||
}
|
||||
}
|
||||
@@ -17,42 +17,39 @@
|
||||
"CHECK_NEW_VERSION_FAILED": "檢查新版本失敗,將在 %d 秒後重試:%s",
|
||||
"SWITCH_TO_WIFI_NETWORK": "切換到 Wi-Fi...",
|
||||
"SWITCH_TO_4G_NETWORK": "切換到 4G...",
|
||||
|
||||
"STANDBY": "待命",
|
||||
"CONNECT_TO": "連接 ",
|
||||
"CONNECTING": "連接中...",
|
||||
"CONNECTED_TO": "已連接 ",
|
||||
|
||||
"LISTENING": "聆聽中...",
|
||||
"SPEAKING": "說話中...",
|
||||
|
||||
"SERVER_NOT_FOUND": "正在尋找可用服務",
|
||||
"SERVER_NOT_CONNECTED": "無法連接服務,請稍後再試",
|
||||
"SERVER_TIMEOUT": "等待響應超時",
|
||||
"SERVER_ERROR": "發送失敗,請檢查網絡",
|
||||
|
||||
"CONNECT_TO_HOTSPOT": "手機連接WiFi ",
|
||||
"ACCESS_VIA_BROWSER": ",瀏覽器訪問 ",
|
||||
"WIFI_CONFIG_MODE": "網路設定模式",
|
||||
"ENTERING_WIFI_CONFIG_MODE": "正在設定網路...",
|
||||
"SCANNING_WIFI": "掃描 Wi-Fi...",
|
||||
|
||||
"NEW_VERSION": "新版本 ",
|
||||
"OTA_UPGRADE": "OTA 升級",
|
||||
"UPGRADING": "正在升級系統...",
|
||||
"UPGRADE_FAILED": "升級失敗",
|
||||
"ACTIVATION": "啟用設備",
|
||||
|
||||
"BATTERY_LOW": "電量不足",
|
||||
"BATTERY_CHARGING": "正在充電",
|
||||
"BATTERY_FULL": "電量已滿",
|
||||
"BATTERY_NEED_CHARGE": "電量低,請充電",
|
||||
|
||||
"VOLUME": "音量 ",
|
||||
"MUTED": "已靜音",
|
||||
"MAX_VOLUME": "最大音量",
|
||||
|
||||
"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
@@ -8,43 +8,41 @@
|
||||
|
||||
#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());
|
||||
}
|
||||
|
||||
bool AudioCodec::InputData(std::vector<int16_t> &data){
|
||||
bool AudioCodec::InputData(std::vector<int16_t>& data) {
|
||||
int samples = Read(data.data(), data.size());
|
||||
if (samples > 0){
|
||||
if (samples > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void AudioCodec::Start(){
|
||||
void AudioCodec::Start() {
|
||||
Settings settings("audio", false);
|
||||
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_);
|
||||
output_volume_ = 10;
|
||||
}
|
||||
|
||||
// 保存原始输出采样率
|
||||
if (original_output_sample_rate_ == 0){
|
||||
original_output_sample_rate_ = 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_));
|
||||
}
|
||||
|
||||
if (rx_handle_ != nullptr){
|
||||
if (rx_handle_ != nullptr) {
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(rx_handle_));
|
||||
}
|
||||
|
||||
@@ -53,7 +51,7 @@ void AudioCodec::Start(){
|
||||
ESP_LOGI(TAG, "Audio codec started");
|
||||
}
|
||||
|
||||
void AudioCodec::SetOutputVolume(int volume){
|
||||
void AudioCodec::SetOutputVolume(int volume) {
|
||||
output_volume_ = 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_);
|
||||
}
|
||||
|
||||
void AudioCodec::EnableInput(bool enable){
|
||||
if (enable == input_enabled_){
|
||||
void AudioCodec::EnableInput(bool enable) {
|
||||
if (enable == input_enabled_) {
|
||||
return;
|
||||
}
|
||||
input_enabled_ = enable;
|
||||
ESP_LOGI(TAG, "Set input enable to %s", enable ? "true" : "false");
|
||||
}
|
||||
|
||||
void AudioCodec::EnableOutput(bool enable){
|
||||
if (enable == output_enabled_){
|
||||
void AudioCodec::EnableOutput(bool enable) {
|
||||
if (enable == output_enabled_) {
|
||||
return;
|
||||
}
|
||||
output_enabled_ = enable;
|
||||
ESP_LOGI(TAG, "Set output enable to %s", enable ? "true" : "false");
|
||||
}
|
||||
|
||||
bool AudioCodec::SetOutputSampleRate(int sample_rate){
|
||||
bool AudioCodec::SetOutputSampleRate(int sample_rate) {
|
||||
// 特殊处理:如果传入 -1,表示重置到原始采样率
|
||||
if (sample_rate == -1){
|
||||
if (original_output_sample_rate_ > 0){
|
||||
if (sample_rate == -1) {
|
||||
if (original_output_sample_rate_ > 0) {
|
||||
sample_rate = original_output_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");
|
||||
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);
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tx_handle_ == nullptr){
|
||||
if (tx_handle_ == nullptr) {
|
||||
ESP_LOGW(TAG, "TX handle is null, only updating sample rate variable");
|
||||
output_sample_rate_ = sample_rate;
|
||||
return true;
|
||||
@@ -110,14 +108,13 @@ bool AudioCodec::SetOutputSampleRate(int sample_rate){
|
||||
// 先尝试禁用 I2S 通道(如果已启用的话)
|
||||
bool was_enabled = false;
|
||||
esp_err_t disable_ret = i2s_channel_disable(tx_handle_);
|
||||
if (disable_ret == ESP_OK){
|
||||
if (disable_ret == ESP_OK) {
|
||||
was_enabled = true;
|
||||
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");
|
||||
}else{
|
||||
} else {
|
||||
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_);
|
||||
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));
|
||||
}else{
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Enabled I2S TX channel");
|
||||
}
|
||||
|
||||
if (ret == ESP_OK){
|
||||
if (ret == ESP_OK) {
|
||||
output_sample_rate_ = sample_rate;
|
||||
ESP_LOGI(TAG, "Successfully changed output sample rate to %d Hz", sample_rate);
|
||||
return true;
|
||||
}else{
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Failed to change sample rate to %d Hz: %s", sample_rate, esp_err_to_name(ret));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include <model_path.h>
|
||||
#include "audio_codec.h"
|
||||
|
||||
class AudioProcessor {
|
||||
public:
|
||||
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 Start() = 0;
|
||||
virtual void Stop() = 0;
|
||||
|
||||
+45
-28
@@ -8,12 +8,11 @@
|
||||
#include "processors/no_audio_processor.h"
|
||||
#endif
|
||||
|
||||
#if CONFIG_USE_AFE_WAKE_WORD
|
||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32P4
|
||||
#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"
|
||||
#else
|
||||
#include "wake_words/esp_wake_word.h"
|
||||
#endif
|
||||
|
||||
#define TAG "AudioService"
|
||||
@@ -50,16 +49,6 @@ void AudioService::Initialize(AudioCodec* codec) {
|
||||
audio_processor_ = std::make_unique<NoAudioProcessor>();
|
||||
#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) {
|
||||
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 = {
|
||||
.callback = [](void* arg) {
|
||||
AudioService* audio_service = (AudioService*)arg;
|
||||
@@ -104,14 +85,14 @@ void AudioService::Start() {
|
||||
AudioService* audio_service = (AudioService*)arg;
|
||||
audio_service->AudioInputTask();
|
||||
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 */
|
||||
xTaskCreate([](void* arg) {
|
||||
AudioService* audio_service = (AudioService*)arg;
|
||||
audio_service->AudioOutputTask();
|
||||
vTaskDelete(NULL);
|
||||
}, "audio_output", 2048 * 2, this, 3, &audio_output_task_handle_);
|
||||
}, "audio_output", 2048 * 2, this, 4, &audio_output_task_handle_);
|
||||
#else
|
||||
/* Start the audio input task */
|
||||
xTaskCreate([](void* arg) {
|
||||
@@ -125,7 +106,7 @@ void AudioService::Start() {
|
||||
AudioService* audio_service = (AudioService*)arg;
|
||||
audio_service->AudioOutputTask();
|
||||
vTaskDelete(NULL);
|
||||
}, "audio_output", 2048, this, 3, &audio_output_task_handle_);
|
||||
}, "audio_output", 2048, this, 4, &audio_output_task_handle_);
|
||||
#endif
|
||||
|
||||
/* Start the opus codec task */
|
||||
@@ -479,7 +460,7 @@ void AudioService::EnableWakeWordDetection(bool enable) {
|
||||
ESP_LOGD(TAG, "%s wake word detection", enable ? "Enabling" : "Disabling");
|
||||
if (enable) {
|
||||
if (!wake_word_initialized_) {
|
||||
if (!wake_word_->Initialize(codec_)) {
|
||||
if (!wake_word_->Initialize(codec_, models_list_)) {
|
||||
ESP_LOGE(TAG, "Failed to initialize wake word");
|
||||
return;
|
||||
}
|
||||
@@ -497,7 +478,7 @@ void AudioService::EnableVoiceProcessing(bool enable) {
|
||||
ESP_LOGD(TAG, "%s voice processing", enable ? "Enabling" : "Disabling");
|
||||
if (enable) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -528,7 +509,7 @@ void AudioService::EnableAudioTesting(bool enable) {
|
||||
void AudioService::EnableDeviceAec(bool enable) {
|
||||
ESP_LOGI(TAG, "%s device AEC", enable ? "Enabling" : "Disabling");
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
last_output_time_ = std::chrono::steady_clock::now();
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/event_groups.h>
|
||||
#include <esp_timer.h>
|
||||
#include <model_path.h>
|
||||
|
||||
#include <opus_encoder.h>
|
||||
#include <opus_decoder.h>
|
||||
@@ -93,6 +94,7 @@ public:
|
||||
bool IsIdle();
|
||||
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 IsAfeWakeWord();
|
||||
|
||||
void EnableWakeWordDetection(bool enable);
|
||||
void EnableVoiceProcessing(bool enable);
|
||||
@@ -106,7 +108,7 @@ public:
|
||||
void PlaySound(const std::string_view& sound);
|
||||
bool ReadAudioData(std::vector<int16_t>& data, int sample_rate, int samples);
|
||||
void ResetDecoder();
|
||||
|
||||
void SetModelsList(srmodel_list_t* models_list);
|
||||
void UpdateOutputTimestamp();
|
||||
|
||||
private:
|
||||
@@ -121,6 +123,7 @@ private:
|
||||
OpusResampler reference_resampler_;
|
||||
OpusResampler output_resampler_;
|
||||
DebugStatistics debug_statistics_;
|
||||
srmodel_list_t* models_list_ = nullptr;
|
||||
|
||||
EventGroupHandle_t event_group_;
|
||||
|
||||
|
||||
@@ -6,13 +6,14 @@
|
||||
|
||||
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 pa_pin, uint8_t es8388_addr) {
|
||||
gpio_num_t pa_pin, uint8_t es8388_addr, bool input_reference) {
|
||||
duplex_ = true; // 是否双工
|
||||
input_reference_ = false; // 是否使用参考输入,实现回声消除
|
||||
input_channels_ = 1; // 输入通道数
|
||||
input_reference_ = input_reference; // 是否使用参考输入,实现回声消除
|
||||
input_channels_ = input_reference_ ? 2 : 1; // 输入通道数
|
||||
input_sample_rate_ = input_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
|
||||
audio_codec_i2s_cfg_t i2s_cfg = {
|
||||
@@ -144,13 +145,21 @@ void Es8388AudioCodec::EnableInput(bool enable) {
|
||||
if (enable) {
|
||||
esp_codec_dev_sample_info_t fs = {
|
||||
.bits_per_sample = 16,
|
||||
.channel = 1,
|
||||
.channel_mask = 0,
|
||||
.channel = (uint8_t) input_channels_,
|
||||
.channel_mask = ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0),
|
||||
.sample_rate = (uint32_t)input_sample_rate_,
|
||||
.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));
|
||||
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 {
|
||||
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
|
||||
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
|
||||
for (uint8_t reg : regs) {
|
||||
ctrl_if_->write_reg(ctrl_if_, reg, 1, ®_val, 1);
|
||||
@@ -200,7 +212,7 @@ int Es8388AudioCodec::Read(int16_t* dest, 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)));
|
||||
}
|
||||
return samples;
|
||||
|
||||
@@ -29,7 +29,7 @@ private:
|
||||
public:
|
||||
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 pa_pin, uint8_t es8388_addr);
|
||||
gpio_num_t pa_pin, uint8_t es8388_addr, bool input_reference = false);
|
||||
virtual ~Es8388AudioCodec();
|
||||
|
||||
virtual void SetOutputVolume(int volume) override;
|
||||
|
||||
@@ -214,6 +214,84 @@ NoAudioCodecSimplex::NoAudioCodecSimplex(int input_sample_rate, int output_sampl
|
||||
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) {
|
||||
duplex_ = false;
|
||||
input_sample_rate_ = input_sample_rate;
|
||||
|
||||
@@ -32,6 +32,7 @@ public:
|
||||
class NoAudioCodecSimplexPdm : public NoAudioCodec {
|
||||
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, 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);
|
||||
};
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ AfeAudioProcessor::AfeAudioProcessor()
|
||||
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;
|
||||
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');
|
||||
}
|
||||
|
||||
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* 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->afe_perferred_core = 1;
|
||||
afe_config->afe_perferred_priority = 1;
|
||||
afe_config->agc_init = false;
|
||||
afe_config->memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM;
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ public:
|
||||
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 Start() override;
|
||||
void Stop() override;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
#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;
|
||||
frame_samples_ = frame_duration_ms * 16000 / 1000;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ public:
|
||||
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 Start() override;
|
||||
void Stop() override;
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include <model_path.h>
|
||||
#include "audio_codec.h"
|
||||
|
||||
class WakeWord {
|
||||
public:
|
||||
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 OnWakeWordDetected(std::function<void(const std::string& wake_word)> callback) = 0;
|
||||
virtual void Start() = 0;
|
||||
|
||||
@@ -36,11 +36,16 @@ AfeWakeWord::~AfeWakeWord() {
|
||||
vEventGroupDelete(event_group_);
|
||||
}
|
||||
|
||||
bool AfeWakeWord::Initialize(AudioCodec* codec) {
|
||||
bool AfeWakeWord::Initialize(AudioCodec* codec, srmodel_list_t* models_list) {
|
||||
codec_ = codec;
|
||||
int ref_num = codec_->input_reference() ? 1 : 0;
|
||||
|
||||
if (models_list == nullptr) {
|
||||
models_ = esp_srmodel_init("model");
|
||||
} else {
|
||||
models_ = models_list;
|
||||
}
|
||||
|
||||
if (models_ == nullptr || models_->num == -1) {
|
||||
ESP_LOGE(TAG, "Failed to initialize wakenet model");
|
||||
return false;
|
||||
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
AfeWakeWord();
|
||||
~AfeWakeWord();
|
||||
|
||||
bool Initialize(AudioCodec* codec);
|
||||
bool Initialize(AudioCodec* codec, srmodel_list_t* models_list);
|
||||
void Feed(const std::vector<int16_t>& data);
|
||||
void OnWakeWordDetected(std::function<void(const std::string& wake_word)> callback);
|
||||
void Start();
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
#include "custom_wake_word.h"
|
||||
#include "audio_service.h"
|
||||
#include "system_info.h"
|
||||
#include "assets.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include "esp_mn_iface.h"
|
||||
#include "esp_mn_models.h"
|
||||
#include "esp_mn_speech_commands.h"
|
||||
#include <esp_mn_iface.h>
|
||||
#include <esp_mn_models.h>
|
||||
#include <esp_mn_speech_commands.h>
|
||||
#include <cJSON.h>
|
||||
|
||||
|
||||
#define TAG "CustomWakeWord"
|
||||
@@ -34,29 +36,90 @@ CustomWakeWord::~CustomWakeWord() {
|
||||
}
|
||||
}
|
||||
|
||||
bool CustomWakeWord::Initialize(AudioCodec* codec) {
|
||||
codec_ = codec;
|
||||
void CustomWakeWord::ParseWakenetModelConfig() {
|
||||
// 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();
|
||||
}
|
||||
|
||||
if (models_ == nullptr || models_->num == -1) {
|
||||
ESP_LOGE(TAG, "Failed to initialize wakenet model");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 初始化 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) {
|
||||
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");
|
||||
return false;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "multinet: %s", mn_name_);
|
||||
multinet_ = esp_mn_handle_from_name(mn_name_);
|
||||
multinet_model_data_ = multinet_->create(mn_name_, 3000); // 3 秒超时
|
||||
multinet_->set_det_threshold(multinet_model_data_, CONFIG_CUSTOM_WAKE_WORD_THRESHOLD / 100.0f);
|
||||
multinet_model_data_ = multinet_->create(mn_name_, duration_);
|
||||
multinet_->set_det_threshold(multinet_model_data_, threshold_);
|
||||
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();
|
||||
|
||||
multinet_->print_active_speech_commands(multinet_model_data_);
|
||||
@@ -99,17 +162,19 @@ void CustomWakeWord::Feed(const std::vector<int16_t>& data) {
|
||||
return;
|
||||
} else if (mn_state == ESP_MN_STATE_DETECTED) {
|
||||
esp_mn_results_t *mn_result = multinet_->get_results(multinet_model_data_);
|
||||
for (int i = 0; i < mn_result->num && running_; i++) {
|
||||
ESP_LOGI(TAG, "Custom wake word detected: command_id=%d, string=%s, prob=%f",
|
||||
mn_result->command_id[0], mn_result->string, mn_result->prob[0]);
|
||||
|
||||
if (mn_result->command_id[0] == 1) {
|
||||
last_detected_wake_word_ = CONFIG_CUSTOM_WAKE_WORD_DISPLAY;
|
||||
}
|
||||
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 (wake_word_detected_callback_) {
|
||||
wake_word_detected_callback_(last_detected_wake_word_);
|
||||
}
|
||||
}
|
||||
}
|
||||
multinet_->clean(multinet_model_data_);
|
||||
} else if (mn_state == ESP_MN_STATE_TIMEOUT) {
|
||||
ESP_LOGD(TAG, "Command word detection timeout, cleaning state");
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
CustomWakeWord();
|
||||
~CustomWakeWord();
|
||||
|
||||
bool Initialize(AudioCodec* codec);
|
||||
bool Initialize(AudioCodec* codec, srmodel_list_t* models_list);
|
||||
void Feed(const std::vector<int16_t>& data);
|
||||
void OnWakeWordDetected(std::function<void(const std::string& wake_word)> callback);
|
||||
void Start();
|
||||
@@ -33,11 +33,21 @@ public:
|
||||
const std::string& GetLastDetectedWakeWord() const { return last_detected_wake_word_; }
|
||||
|
||||
private:
|
||||
struct Command {
|
||||
std::string command;
|
||||
std::string text;
|
||||
std::string action;
|
||||
};
|
||||
|
||||
// multinet 相关成员变量
|
||||
esp_mn_iface_t* multinet_ = nullptr;
|
||||
model_iface_data_t* multinet_model_data_ = nullptr;
|
||||
srmodel_list_t *models_ = 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_;
|
||||
AudioCodec* codec_ = nullptr;
|
||||
@@ -53,6 +63,7 @@ private:
|
||||
std::condition_variable wake_word_cv_;
|
||||
|
||||
void StoreWakeWordData(const std::vector<int16_t>& data);
|
||||
void ParseWakenetModelConfig();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,10 +14,15 @@ EspWakeWord::~EspWakeWord() {
|
||||
}
|
||||
}
|
||||
|
||||
bool EspWakeWord::Initialize(AudioCodec* codec) {
|
||||
bool EspWakeWord::Initialize(AudioCodec* codec, srmodel_list_t* models_list) {
|
||||
codec_ = codec;
|
||||
|
||||
if (models_list == nullptr) {
|
||||
wakenet_model_ = esp_srmodel_init("model");
|
||||
} else {
|
||||
wakenet_model_ = models_list;
|
||||
}
|
||||
|
||||
if (wakenet_model_ == nullptr || wakenet_model_->num == -1) {
|
||||
ESP_LOGE(TAG, "Failed to initialize wakenet model");
|
||||
return false;
|
||||
|
||||
@@ -18,7 +18,7 @@ public:
|
||||
EspWakeWord();
|
||||
~EspWakeWord();
|
||||
|
||||
bool Initialize(AudioCodec* codec);
|
||||
bool Initialize(AudioCodec* codec, srmodel_list_t* models_list);
|
||||
void Feed(const std::vector<int16_t>& data);
|
||||
void OnWakeWordDetected(std::function<void(const std::string& wake_word)> callback);
|
||||
void Start();
|
||||
|
||||
Vendored
BIN
Binary file not shown.
+2
-11
@@ -98,7 +98,7 @@ mkdir main/boards/my-custom-board
|
||||
"sdkconfig_append": [
|
||||
// 额外需要的编译配置
|
||||
"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"
|
||||
|
||||
// 声明字体
|
||||
LV_FONT_DECLARE(font_puhui_16_4);
|
||||
LV_FONT_DECLARE(font_awesome_16_4);
|
||||
|
||||
class MyCustomBoard : public WifiBoard {
|
||||
private:
|
||||
i2c_master_bus_handle_t codec_i2c_bus_;
|
||||
@@ -212,12 +208,7 @@ private:
|
||||
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,
|
||||
{
|
||||
.text_font = &font_puhui_16_4,
|
||||
.icon_font = &font_awesome_16_4,
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
});
|
||||
DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
}
|
||||
|
||||
// MCP Tools 初始化
|
||||
|
||||
@@ -20,10 +20,6 @@
|
||||
|
||||
#define TAG "atk_dnesp32s3_box"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_20_4);
|
||||
LV_FONT_DECLARE(font_awesome_20_4);
|
||||
|
||||
|
||||
class ATK_NoAudioCodecDuplex : public NoAudioCodec {
|
||||
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) {
|
||||
@@ -84,7 +80,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class XL9555_IN : public I2cDevice {
|
||||
public:
|
||||
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));
|
||||
|
||||
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,
|
||||
{
|
||||
.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
|
||||
});
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
|
||||
#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 {
|
||||
private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
@@ -294,7 +291,6 @@ private:
|
||||
GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
void InitializeSt7789Display() {
|
||||
@@ -325,12 +321,7 @@ private:
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
|
||||
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,
|
||||
{
|
||||
.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(),
|
||||
});
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
|
||||
#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 {
|
||||
private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
@@ -412,12 +409,7 @@ private:
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
|
||||
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,
|
||||
{
|
||||
.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(),
|
||||
});
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -21,9 +21,6 @@
|
||||
|
||||
#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 {
|
||||
private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
@@ -392,12 +389,7 @@ private:
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
|
||||
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,
|
||||
{
|
||||
.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(),
|
||||
});
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
#define TAG "atk_dnesp32s3"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_20_4);
|
||||
LV_FONT_DECLARE(font_awesome_20_4);
|
||||
|
||||
class XL9555 : public I2cDevice {
|
||||
public:
|
||||
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 {
|
||||
private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
@@ -131,16 +127,7 @@ private:
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
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,
|
||||
{
|
||||
.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
|
||||
});
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
}
|
||||
|
||||
// 初始化摄像头:ov2640;
|
||||
|
||||
@@ -20,9 +20,6 @@
|
||||
|
||||
#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 {
|
||||
private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
@@ -167,12 +164,7 @@ private:
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
|
||||
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,
|
||||
{
|
||||
.text_font = &font_puhui_16_4,
|
||||
.icon_font = &font_awesome_16_4,
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
});
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -19,9 +19,6 @@
|
||||
|
||||
#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 {
|
||||
private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
@@ -177,12 +174,7 @@ private:
|
||||
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
|
||||
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,
|
||||
{
|
||||
.text_font = &font_puhui_16_4,
|
||||
.icon_font = &font_awesome_16_4,
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
});
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -22,7 +22,7 @@ idf.py menuconfig
|
||||
分别配置如下选项:
|
||||
|
||||
- `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`
|
||||
- `Component config` → `ESP PSRAM` → `Support for external, SPI-connected RAM` → `SPI RAM config` → 选择 `Octal Mode PSRAM`
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
|
||||
#define TAG "AtomEchoS3R"
|
||||
|
||||
|
||||
class AtomEchoS3rBaseBoard : public WifiBoard {
|
||||
private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"name": "atom-echos3r",
|
||||
"sdkconfig_append": [
|
||||
"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
|
||||
```
|
||||
|
||||
**修改 flash 大小:**
|
||||
|
||||
```
|
||||
Serial flasher config -> Flash size -> 4 MB
|
||||
```
|
||||
|
||||
**修改分区表:**
|
||||
|
||||
```
|
||||
Partition Table -> Custom partition CSV file -> partitions/v1/4m.csv
|
||||
```
|
||||
|
||||
**编译:**
|
||||
|
||||
```bash
|
||||
|
||||
@@ -33,7 +33,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class AtomMatrixEchoBaseBoard : public WifiBoard {
|
||||
private:
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
@@ -87,7 +86,6 @@ private:
|
||||
pi4ioe_->SetSpeakerMute(false);
|
||||
}
|
||||
|
||||
|
||||
void InitializeButtons() {
|
||||
face_button_.OnClick([this]() {
|
||||
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
{
|
||||
"name": "atommatrix-echo-base",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
|
||||
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/4m.csv\""
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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:**
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
#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[] = {
|
||||
// {cmd, { data }, data_size, delay_ms}
|
||||
{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_disp_on_off(panel_handle, true));
|
||||
|
||||
|
||||
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,
|
||||
{
|
||||
.text_font = &font_puhui_16_4,
|
||||
.icon_font = &font_awesome_16_4,
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
});
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
"name": "atoms3-echo-base",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_SPIRAM=n",
|
||||
"CONFIG_USE_AFE=n",
|
||||
"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` → `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`
|
||||
|
||||
按 `S` 保存,按 `Q` 退出。
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"name": "atoms3r-cam-m12-echo-base",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y",
|
||||
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/8m.csv\""
|
||||
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/8m.csv\""
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -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 配置:**
|
||||
|
||||
@@ -23,9 +23,6 @@
|
||||
#define PI4IOE_REG_IO_OUT 0x05
|
||||
#define PI4IOE_REG_IO_PULLUP 0x0D
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_16_4);
|
||||
LV_FONT_DECLARE(font_awesome_16_4);
|
||||
|
||||
class Pi4ioe : public I2cDevice {
|
||||
public:
|
||||
Pi4ioe(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) {
|
||||
@@ -114,6 +111,7 @@ private:
|
||||
Display* display_ = nullptr;
|
||||
Button boot_button_;
|
||||
bool is_echo_base_connected_ = false;
|
||||
|
||||
void InitializeI2c() {
|
||||
// Initialize I2C peripheral
|
||||
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));
|
||||
|
||||
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,
|
||||
{
|
||||
.text_font = &font_puhui_16_4,
|
||||
.icon_font = &font_awesome_16_4,
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
});
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"name": "atoms3r-echo-base",
|
||||
"sdkconfig_append": [
|
||||
"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 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
|
||||
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_22
|
||||
|
||||
@@ -4,8 +4,6 @@
|
||||
{
|
||||
"name": "bread-compact-esp32-lcd",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
|
||||
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/4m.csv\"",
|
||||
"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 "display/lcd_display.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"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_14_1);
|
||||
LV_FONT_DECLARE(font_awesome_14_1);
|
||||
|
||||
class CompactWifiBoardLCD : public WifiBoard {
|
||||
class CompactWifiBoardLCD : public DualNetworkBoard {
|
||||
private:
|
||||
Button boot_button_;
|
||||
Button touch_button_;
|
||||
@@ -115,7 +112,6 @@ private:
|
||||
|
||||
esp_lcd_panel_reset(panel);
|
||||
|
||||
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
@@ -124,16 +120,9 @@ private:
|
||||
panel_config.vendor_config = &gc9107_vendor_config;
|
||||
#endif
|
||||
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,
|
||||
{
|
||||
.text_font = &font_puhui_14_1,
|
||||
.icon_font = &font_awesome_14_1,
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
});
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void InitializeButtons() {
|
||||
|
||||
// 配置 GPIO
|
||||
@@ -148,13 +137,25 @@ private:
|
||||
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (GetNetworkType() == NetworkType::WIFI) {
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
// cast to WifiBoard
|
||||
auto& wifi_board = static_cast<WifiBoard&>(GetCurrentBoard());
|
||||
wifi_board.ResetWifiConfiguration();
|
||||
}
|
||||
}
|
||||
gpio_set_level(BUILTIN_LED_GPIO, 1);
|
||||
app.ToggleChatState();
|
||||
});
|
||||
|
||||
|
||||
boot_button_.OnDoubleClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting || app.GetDeviceState() == kDeviceStateWifiConfiguring) {
|
||||
SwitchNetworkType();
|
||||
}
|
||||
});
|
||||
|
||||
asr_button_.OnClick([this]() {
|
||||
std::string wake_word="你好小智";
|
||||
Application::GetInstance().WakeWordInvoke(wake_word);
|
||||
@@ -174,6 +175,7 @@ private:
|
||||
|
||||
public:
|
||||
CompactWifiBoardLCD() :
|
||||
DualNetworkBoard(ML307_TX_PIN, ML307_RX_PIN),
|
||||
boot_button_(BOOT_BUTTON_GPIO), touch_button_(TOUCH_BUTTON_GPIO), asr_button_(ASR_BUTTON_GPIO) {
|
||||
InitializeSpi();
|
||||
InitializeLcdDisplay();
|
||||
|
||||
@@ -18,18 +18,6 @@ idf.py menuconfig
|
||||
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
|
||||
|
||||
@@ -33,6 +33,9 @@
|
||||
#define ASR_BUTTON_GPIO GPIO_NUM_19
|
||||
#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_SCL_PIN GPIO_NUM_15
|
||||
#define DISPLAY_WIDTH 128
|
||||
|
||||
@@ -4,16 +4,12 @@
|
||||
{
|
||||
"name": "bread-compact-esp32",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
|
||||
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/4m.csv\"",
|
||||
"CONFIG_OLED_SSD1306_128X64=y"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "bread-compact-esp32-128x32",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
|
||||
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/4m.csv\"",
|
||||
"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 "system_reset.h"
|
||||
#include "application.h"
|
||||
@@ -17,11 +17,7 @@
|
||||
|
||||
#define TAG "ESP32-MarsbearSupport"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_14_1);
|
||||
LV_FONT_DECLARE(font_awesome_14_1);
|
||||
|
||||
|
||||
class CompactWifiBoard : public WifiBoard {
|
||||
class CompactWifiBoard : public DualNetworkBoard {
|
||||
private:
|
||||
Button boot_button_;
|
||||
Button touch_button_;
|
||||
@@ -92,8 +88,7 @@ private:
|
||||
ESP_LOGI(TAG, "Turning display on");
|
||||
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,
|
||||
{&font_puhui_14_1, &font_awesome_14_1});
|
||||
display_ = new OledDisplay(panel_io_, panel_, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
@@ -110,13 +105,25 @@ private:
|
||||
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (GetNetworkType() == NetworkType::WIFI) {
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
ResetWifiConfiguration();
|
||||
// cast to WifiBoard
|
||||
auto& wifi_board = static_cast<WifiBoard&>(GetCurrentBoard());
|
||||
wifi_board.ResetWifiConfiguration();
|
||||
}
|
||||
}
|
||||
gpio_set_level(BUILTIN_LED_GPIO, 1);
|
||||
app.ToggleChatState();
|
||||
});
|
||||
|
||||
|
||||
boot_button_.OnDoubleClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting || app.GetDeviceState() == kDeviceStateWifiConfiguring) {
|
||||
SwitchNetworkType();
|
||||
}
|
||||
});
|
||||
|
||||
asr_button_.OnClick([this]() {
|
||||
std::string wake_word="你好小智";
|
||||
Application::GetInstance().WakeWordInvoke(wake_word);
|
||||
@@ -138,7 +145,7 @@ private:
|
||||
}
|
||||
|
||||
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();
|
||||
InitializeSsd1306Display();
|
||||
|
||||
@@ -18,9 +18,6 @@
|
||||
|
||||
#define TAG "CompactMl307Board"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_14_1);
|
||||
LV_FONT_DECLARE(font_awesome_14_1);
|
||||
|
||||
class CompactMl307Board : public DualNetworkBoard {
|
||||
private:
|
||||
i2c_master_bus_handle_t display_i2c_bus_;
|
||||
@@ -92,8 +89,7 @@ private:
|
||||
ESP_LOGI(TAG, "Turning display on");
|
||||
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,
|
||||
{&font_puhui_14_1, &font_awesome_14_1});
|
||||
display_ = new OledDisplay(panel_io_, panel_, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
|
||||
@@ -60,9 +60,6 @@ static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = {
|
||||
|
||||
#define TAG "CompactWifiBoardLCD"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_16_4);
|
||||
LV_FONT_DECLARE(font_awesome_16_4);
|
||||
|
||||
class CompactWifiBoardLCD : public WifiBoard {
|
||||
private:
|
||||
|
||||
@@ -115,7 +112,6 @@ private:
|
||||
|
||||
esp_lcd_panel_reset(panel);
|
||||
|
||||
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
@@ -124,20 +120,9 @@ private:
|
||||
panel_config.vendor_config = &gc9107_vendor_config;
|
||||
#endif
|
||||
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,
|
||||
{
|
||||
.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
|
||||
});
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
|
||||
@@ -61,9 +61,6 @@ static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = {
|
||||
|
||||
#define TAG "CompactWifiBoardS3Cam"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_16_4);
|
||||
LV_FONT_DECLARE(font_awesome_16_4);
|
||||
|
||||
class CompactWifiBoardS3Cam : public WifiBoard {
|
||||
private:
|
||||
|
||||
@@ -117,7 +114,6 @@ private:
|
||||
|
||||
esp_lcd_panel_reset(panel);
|
||||
|
||||
|
||||
esp_lcd_panel_init(panel);
|
||||
esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR);
|
||||
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
|
||||
@@ -126,16 +122,7 @@ private:
|
||||
panel_config.vendor_config = &gc9107_vendor_config;
|
||||
#endif
|
||||
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,
|
||||
{
|
||||
.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
|
||||
});
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
}
|
||||
|
||||
void InitializeCamera() {
|
||||
|
||||
@@ -22,9 +22,6 @@
|
||||
|
||||
#define TAG "CompactWifiBoard"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_14_1);
|
||||
LV_FONT_DECLARE(font_awesome_14_1);
|
||||
|
||||
class CompactWifiBoard : public WifiBoard {
|
||||
private:
|
||||
i2c_master_bus_handle_t display_i2c_bus_;
|
||||
@@ -101,8 +98,7 @@ private:
|
||||
ESP_LOGI(TAG, "Turning display on");
|
||||
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,
|
||||
{&font_puhui_14_1, &font_awesome_14_1});
|
||||
display_ = new OledDisplay(panel_io_, panel_, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
#include "system_info.h"
|
||||
#include "settings.h"
|
||||
#include "display/display.h"
|
||||
#include "display/oled_display.h"
|
||||
#include "assets/lang_config.h"
|
||||
#include "esp32_music.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_chip_info.h>
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
Board::Board() {
|
||||
music_ = nullptr; // 先初始化为空指针
|
||||
|
||||
Settings settings("board", true);
|
||||
uuid_ = settings.GetString("uuid");
|
||||
if (uuid_.empty()) {
|
||||
@@ -27,7 +26,6 @@ Board::Board() {
|
||||
music_ = new Esp32Music();
|
||||
ESP_LOGI(TAG, "Music player initialized for all boards");
|
||||
}
|
||||
|
||||
Board::~Board() {
|
||||
if (music_) {
|
||||
delete music_;
|
||||
@@ -35,7 +33,6 @@ Board::~Board() {
|
||||
ESP_LOGI(TAG, "Music player destroyed");
|
||||
}
|
||||
}
|
||||
|
||||
std::string Board::GenerateUuid() {
|
||||
// UUID v4 需要 16 字节的随机数据
|
||||
uint8_t uuid[16];
|
||||
@@ -76,16 +73,15 @@ Camera* Board::GetCamera() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Music* Board::GetMusic() {
|
||||
return music_;
|
||||
}
|
||||
|
||||
Led* Board::GetLed() {
|
||||
static NoLed led;
|
||||
return &led;
|
||||
}
|
||||
Music* Board::GetMusic() {
|
||||
return music_;
|
||||
}
|
||||
|
||||
std::string Board::GetJson() {
|
||||
std::string Board::GetSystemInfoJson() {
|
||||
/*
|
||||
{
|
||||
"version": 2,
|
||||
@@ -173,6 +169,21 @@ std::string Board::GetJson() {
|
||||
json += R"("label":")" + std::string(ota_partition->label) + 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();
|
||||
|
||||
// Close the JSON object
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "led/led.h"
|
||||
#include "backlight.h"
|
||||
#include "camera.h"
|
||||
#include "assets.h"
|
||||
#include "music.h"
|
||||
|
||||
void* create_board();
|
||||
@@ -37,21 +38,21 @@ public:
|
||||
return *instance;
|
||||
}
|
||||
|
||||
virtual ~Board();
|
||||
virtual ~Board(); // 改为非默认析构函数,用于清理 music_
|
||||
virtual std::string GetBoardType() = 0;
|
||||
virtual std::string GetUuid() { return uuid_; }
|
||||
virtual Backlight* GetBacklight() { return nullptr; }
|
||||
virtual Led* GetLed();
|
||||
virtual Music* GetMusic();
|
||||
virtual AudioCodec* GetAudioCodec() = 0;
|
||||
virtual bool GetTemperature(float& esp32temp);
|
||||
virtual Display* GetDisplay();
|
||||
virtual Camera* GetCamera();
|
||||
virtual Music* GetMusic();
|
||||
virtual NetworkInterface* GetNetwork() = 0;
|
||||
virtual void StartNetwork() = 0;
|
||||
virtual const char* GetNetworkStateIcon() = 0;
|
||||
virtual bool GetBatteryLevel(int &level, bool& charging, bool& discharging);
|
||||
virtual std::string GetJson();
|
||||
virtual std::string GetSystemInfoJson();
|
||||
virtual void SetPowerSaveMode(bool enabled) = 0;
|
||||
virtual std::string GetBoardJson() = 0;
|
||||
virtual std::string GetDeviceStatusJson() = 0;
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
#include "display.h"
|
||||
#include "board.h"
|
||||
#include "system_info.h"
|
||||
#include "lvgl_display.h"
|
||||
#include "jpg/image_to_jpeg.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <esp_heap_caps.h>
|
||||
#include <img_converters.h>
|
||||
#include <cstring>
|
||||
|
||||
#define TAG "Esp32Camera"
|
||||
@@ -23,48 +24,6 @@ Esp32Camera::Esp32Camera(const camera_config_t& config) {
|
||||
if (s->id.PID == GC0308_PID) {
|
||||
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() {
|
||||
@@ -72,10 +31,6 @@ Esp32Camera::~Esp32Camera() {
|
||||
esp_camera_fb_return(fb_);
|
||||
fb_ = nullptr;
|
||||
}
|
||||
if (preview_image_.data) {
|
||||
heap_caps_free((void*)preview_image_.data);
|
||||
preview_image_.data = nullptr;
|
||||
}
|
||||
esp_camera_deinit();
|
||||
}
|
||||
|
||||
@@ -89,6 +44,7 @@ bool Esp32Camera::Capture() {
|
||||
encoder_thread_.join();
|
||||
}
|
||||
|
||||
auto start_time = esp_timer_get_time();
|
||||
int frames_to_get = 2;
|
||||
// Try to get a stable frame
|
||||
for (int i = 0; i < frames_to_get; i++) {
|
||||
@@ -101,31 +57,32 @@ bool Esp32Camera::Capture() {
|
||||
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) {
|
||||
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 dst = (uint16_t*)preview_image_.data;
|
||||
auto dst = (uint16_t*)data;
|
||||
size_t pixel_count = fb_->len / 2;
|
||||
for (size_t i = 0; i < pixel_count; i++) {
|
||||
// 交换每个16位字内的字节
|
||||
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;
|
||||
}
|
||||
|
||||
bool Esp32Camera::SetHMirror(bool enabled) {
|
||||
sensor_t *s = esp_camera_sensor_get();
|
||||
if (s == nullptr) {
|
||||
@@ -185,19 +142,20 @@ bool Esp32Camera::SetVFlip(bool enabled) {
|
||||
*/
|
||||
std::string Esp32Camera::Explain(const std::string& question) {
|
||||
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
|
||||
QueueHandle_t jpeg_queue = xQueueCreate(40, sizeof(JpegChunk));
|
||||
if (jpeg_queue == nullptr) {
|
||||
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]() {
|
||||
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;
|
||||
JpegChunk chunk = {
|
||||
.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);
|
||||
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) {
|
||||
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();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user