mirror of
https://github.com/78/xiaozhi-esp32.git
synced 2026-02-27 14:26:36 +00:00
v2.2.0: Add bread-compact-nt26 board (#1663)
* Refactor application error handling and improve network task logic - Updated error handling for modem initialization failure in Application::Initialize(). - Added new error message for modem initialization in English and Chinese language files. - Simplified lambda captures in NetworkTask to avoid unnecessary references. - Set main task priority in Application::Run() for better performance. * Add support for Bread Compact NT26 board - Introduced new board configuration for Bread Compact NT26 in CMakeLists.txt and Kconfig. - Added board-specific implementation in compact_nt26_board.cc and nt26_board.cc. - Created configuration files for NT26, including config.h and config.json. - Updated dependencies in idf_component.yml to include uart-eth-modem. - Translated error messages in config.h for OLED display type selection to English. - Enhanced display and button initialization logic for NT26 board. * Update project version and improve build configuration - Updated project version from 2.1.0 to 2.2.0 in CMakeLists.txt. - Enabled minimal build configuration to include only essential components. - Updated README files to replace QQ group links with Discord links for community engagement. * Update Bread Compact NT26 board configuration name in config.json * fix compile errors * Update uart-eth-modem dependency format in idf_component.yml * fix esp32 compiling errors * Update CMakeLists.txt to change component dependency from REQUIRES to PRIV_REQUIRES for esp_pm, esp_psram, and esp_driver_gpio * Refactor CMakeLists.txt to explicitly list board common source files and update include directories for better clarity and organization. * Add esp_driver_ppa as a dependency in CMakeLists.txt
This commit is contained in:
@@ -1,14 +1,13 @@
|
||||
# For more information about build system see
|
||||
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(PROJECT_VER "2.1.0")
|
||||
|
||||
# Add this line to disable the specific warning
|
||||
add_compile_options(-Wno-missing-field-initializers)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(xiaozhi)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
|
||||
set(PROJECT_VER "2.2.0")
|
||||
project(xiaozhi)
|
||||
|
||||
@@ -159,7 +159,7 @@ This is an open-source ESP32 project, released under the MIT license, allowing a
|
||||
|
||||
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
|
||||
If you have any ideas or suggestions, please feel free to raise Issues or join our [Discord](https://discord.gg/x3S4jgXHk3) or QQ group: 994694848
|
||||
|
||||
## Star History
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ Feishuドキュメントチュートリアルをご覧ください:
|
||||
|
||||
このプロジェクトを通じて、AIハードウェア開発を理解し、急速に進化する大規模言語モデルを実際のハードウェアデバイスに応用できるようになることを目指しています。
|
||||
|
||||
ご意見やご提案があれば、いつでもIssueを提出するか、QQグループ:1011329060 にご参加ください。
|
||||
ご意見やご提案があれば、いつでもIssueを提出するか、[Discord](https://discord.gg/x3S4jgXHk3) または QQグループ:1011329060 にご参加ください。
|
||||
|
||||
## スター履歴
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ v1 的稳定版本为 1.9.2,可以通过 `git checkout v1` 来切换到 v1 版
|
||||
|
||||
我们希望通过这个项目,能够帮助大家了解 AI 硬件开发,将当下飞速发展的大语言模型应用到实际的硬件设备中。
|
||||
|
||||
如果你有任何想法或建议,请随时提出 Issues 或加入 QQ 群:1011329060
|
||||
如果你有任何想法或建议,请随时提出 Issues 或加入 [Discord](https://discord.gg/x3S4jgXHk3) 或 QQ 群:1011329060
|
||||
|
||||
## Star History
|
||||
|
||||
|
||||
@@ -41,10 +41,29 @@ set(SOURCES "audio/audio_codec.cc"
|
||||
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 SOURCES
|
||||
"boards/common/board.cc"
|
||||
"boards/common/wifi_board.cc"
|
||||
"boards/common/ml307_board.cc"
|
||||
"boards/common/nt26_board.cc"
|
||||
"boards/common/dual_network_board.cc"
|
||||
"boards/common/adc_battery_monitor.cc"
|
||||
"boards/common/afsk_demod.cc"
|
||||
"boards/common/axp2101.cc"
|
||||
"boards/common/backlight.cc"
|
||||
"boards/common/button.cc"
|
||||
"boards/common/esp32_camera.cc"
|
||||
"boards/common/i2c_device.cc"
|
||||
"boards/common/knob.cc"
|
||||
"boards/common/power_save_timer.cc"
|
||||
"boards/common/press_to_talk_mcp_tool.cc"
|
||||
"boards/common/sleep_timer.cc"
|
||||
"boards/common/sy6970.cc"
|
||||
"boards/common/system_reset.cc"
|
||||
)
|
||||
list(APPEND INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/boards/common)
|
||||
|
||||
|
||||
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)
|
||||
@@ -74,6 +93,10 @@ 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_NT26)
|
||||
set(BOARD_TYPE "bread-compact-nt26")
|
||||
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)
|
||||
@@ -732,9 +755,10 @@ if(CONFIG_IDF_TARGET_ESP32)
|
||||
"audio/codecs/es8388_audio_codec.cc"
|
||||
"audio/codecs/es8389_audio_codec.cc"
|
||||
"led/gpio_led.cc"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/boards/common/esp32_camera.cc"
|
||||
"display/lvgl_display/jpg/image_to_jpeg.cpp"
|
||||
"display/lvgl_display/jpg/jpeg_to_image.c"
|
||||
"boards/common/esp32_camera.cc"
|
||||
"boards/common/nt26_board.cc"
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -742,6 +766,21 @@ idf_component_register(SRCS ${SOURCES}
|
||||
EMBED_FILES ${LANG_SOUNDS} ${COMMON_SOUNDS}
|
||||
INCLUDE_DIRS ${INCLUDE_DIRS}
|
||||
WHOLE_ARCHIVE
|
||||
PRIV_REQUIRES
|
||||
esp_pm
|
||||
esp_psram
|
||||
esp_driver_gpio
|
||||
esp_driver_uart
|
||||
esp_driver_spi
|
||||
esp_driver_i2c
|
||||
esp_driver_i2s
|
||||
esp_driver_jpeg
|
||||
esp_driver_ppa
|
||||
esp_app_format
|
||||
app_update
|
||||
spi_flash
|
||||
console
|
||||
efuse
|
||||
)
|
||||
|
||||
# Use target_compile_definitions to define BOARD_TYPE, BOARD_NAME
|
||||
|
||||
@@ -134,6 +134,9 @@ choice BOARD_TYPE
|
||||
config BOARD_TYPE_BREAD_COMPACT_ML307
|
||||
bool "Bread Compact ML307/EC801E (面包板 4G)"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_BREAD_COMPACT_NT26
|
||||
bool "Bread Compact NT26 (面包板 4G)"
|
||||
depends on IDF_TARGET_ESP32S3
|
||||
config BOARD_TYPE_BREAD_COMPACT_ESP32
|
||||
bool "Bread Compact ESP32 DevKit (面包板)"
|
||||
depends on IDF_TARGET_ESP32
|
||||
@@ -497,7 +500,7 @@ choice ESP_S3_LCD_EV_Board_Version_TYPE
|
||||
endchoice
|
||||
|
||||
choice DISPLAY_OLED_TYPE
|
||||
depends on BOARD_TYPE_BREAD_COMPACT_WIFI || BOARD_TYPE_BREAD_COMPACT_ML307 || BOARD_TYPE_BREAD_COMPACT_ESP32 || BOARD_TYPE_HU_087
|
||||
depends on BOARD_TYPE_BREAD_COMPACT_WIFI || BOARD_TYPE_BREAD_COMPACT_ML307 || BOARD_TYPE_BREAD_COMPACT_NT26 || BOARD_TYPE_BREAD_COMPACT_ESP32 || BOARD_TYPE_HU_087
|
||||
prompt "OLED Type"
|
||||
default OLED_SSD1306_128X32
|
||||
help
|
||||
|
||||
@@ -147,8 +147,7 @@ void Application::Initialize() {
|
||||
Alert(Lang::Strings::ERROR, Lang::Strings::REG_ERROR, "triangle_exclamation", Lang::Sounds::OGG_ERR_REG);
|
||||
break;
|
||||
case NetworkEvent::ModemErrorInitFailed:
|
||||
display->SetStatus(Lang::Strings::DETECTING_MODULE);
|
||||
display->SetChatMessage("system", Lang::Strings::DETECTING_MODULE);
|
||||
Alert(Lang::Strings::ERROR, Lang::Strings::MODEM_INIT_ERROR, "triangle_exclamation", Lang::Sounds::OGG_EXCLAMATION);
|
||||
break;
|
||||
case NetworkEvent::ModemErrorTimeout:
|
||||
display->SetStatus(Lang::Strings::REGISTERING_NETWORK);
|
||||
@@ -164,6 +163,9 @@ void Application::Initialize() {
|
||||
}
|
||||
|
||||
void Application::Run() {
|
||||
// Set the priority of the main task to 10
|
||||
vTaskPrioritySet(nullptr, 10);
|
||||
|
||||
const EventBits_t ALL_EVENTS =
|
||||
MAIN_EVENT_SCHEDULE |
|
||||
MAIN_EVENT_SEND_AUDIO |
|
||||
@@ -538,7 +540,7 @@ void Application::InitializeProtocol() {
|
||||
auto text = cJSON_GetObjectItem(root, "text");
|
||||
if (cJSON_IsString(text)) {
|
||||
ESP_LOGI(TAG, "<< %s", text->valuestring);
|
||||
Schedule([this, display, message = std::string(text->valuestring)]() {
|
||||
Schedule([display, message = std::string(text->valuestring)]() {
|
||||
display->SetChatMessage("assistant", message.c_str());
|
||||
});
|
||||
}
|
||||
@@ -547,14 +549,14 @@ void Application::InitializeProtocol() {
|
||||
auto text = cJSON_GetObjectItem(root, "text");
|
||||
if (cJSON_IsString(text)) {
|
||||
ESP_LOGI(TAG, ">> %s", text->valuestring);
|
||||
Schedule([this, display, message = std::string(text->valuestring)]() {
|
||||
Schedule([display, message = std::string(text->valuestring)]() {
|
||||
display->SetChatMessage("user", message.c_str());
|
||||
});
|
||||
}
|
||||
} else if (strcmp(type->valuestring, "llm") == 0) {
|
||||
auto emotion = cJSON_GetObjectItem(root, "emotion");
|
||||
if (cJSON_IsString(emotion)) {
|
||||
Schedule([this, display, emotion_str = std::string(emotion->valuestring)]() {
|
||||
Schedule([display, emotion_str = std::string(emotion->valuestring)]() {
|
||||
display->SetEmotion(emotion_str.c_str());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"INITIALIZING": "Initializing...",
|
||||
"PIN_ERROR": "Please insert SIM card",
|
||||
"REG_ERROR": "Unable to access network, please check SIM card status",
|
||||
"MODEM_INIT_ERROR": "Modem initialization failed",
|
||||
"DETECTING_MODULE": "Detecting module...",
|
||||
"REGISTERING_NETWORK": "Waiting for network...",
|
||||
"CHECKING_NEW_VERSION": "Checking for new version...",
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
"INITIALIZING": "正在初始化...",
|
||||
"PIN_ERROR": "请插入 SIM 卡",
|
||||
"REG_ERROR": "无法接入网络,请检查流量卡状态",
|
||||
"MODEM_INIT_ERROR": "模组初始化失败",
|
||||
"DETECTING_MODULE": "检测模组...",
|
||||
"REGISTERING_NETWORK": "等待网络...",
|
||||
"CHECKING_NEW_VERSION": "检查新版本...",
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
#elif CONFIG_OLED_SSD1306_128X64
|
||||
#define DISPLAY_HEIGHT 64
|
||||
#else
|
||||
#error "未选择 OLED 屏幕类型"
|
||||
#error "OLED display type is not selected"
|
||||
#endif
|
||||
|
||||
#define DISPLAY_MIRROR_X true
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
#elif CONFIG_OLED_SSD1306_128X64
|
||||
#define DISPLAY_HEIGHT 64
|
||||
#else
|
||||
#error "未选择 OLED 屏幕类型"
|
||||
#error "OLED display type is not selected"
|
||||
#endif
|
||||
|
||||
#define DISPLAY_MIRROR_X true
|
||||
|
||||
181
main/boards/bread-compact-nt26/compact_nt26_board.cc
Normal file
181
main/boards/bread-compact-nt26/compact_nt26_board.cc
Normal file
@@ -0,0 +1,181 @@
|
||||
#include "board.h"
|
||||
#include "nt26_board.h"
|
||||
#include "codecs/no_audio_codec.h"
|
||||
#include "display/oled_display.h"
|
||||
#include "application.h"
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
#include "lamp_controller.h"
|
||||
#include "led/single_led.h"
|
||||
#include "assets/lang_config.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <esp_lcd_panel_ops.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
|
||||
#define TAG "CompactNt26Board"
|
||||
|
||||
class CompactNt26Board : public Nt26Board {
|
||||
private:
|
||||
i2c_master_bus_handle_t display_i2c_bus_;
|
||||
esp_lcd_panel_io_handle_t panel_io_ = nullptr;
|
||||
esp_lcd_panel_handle_t panel_ = nullptr;
|
||||
Display* display_ = nullptr;
|
||||
Button boot_button_;
|
||||
Button touch_button_;
|
||||
Button volume_up_button_;
|
||||
Button volume_down_button_;
|
||||
|
||||
void InitializeDisplayI2c() {
|
||||
i2c_master_bus_config_t bus_config = {
|
||||
.i2c_port = (i2c_port_t)0,
|
||||
.sda_io_num = DISPLAY_SDA_PIN,
|
||||
.scl_io_num = DISPLAY_SCL_PIN,
|
||||
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||
.glitch_ignore_cnt = 7,
|
||||
.intr_priority = 0,
|
||||
.trans_queue_depth = 0,
|
||||
.flags = {
|
||||
.enable_internal_pullup = 1,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &display_i2c_bus_));
|
||||
}
|
||||
|
||||
void InitializeSsd1306Display() {
|
||||
// SSD1306 config
|
||||
esp_lcd_panel_io_i2c_config_t io_config = {
|
||||
.dev_addr = 0x3C,
|
||||
.on_color_trans_done = nullptr,
|
||||
.user_ctx = nullptr,
|
||||
.control_phase_bytes = 1,
|
||||
.dc_bit_offset = 6,
|
||||
.lcd_cmd_bits = 8,
|
||||
.lcd_param_bits = 8,
|
||||
.flags = {
|
||||
.dc_low_on_data = 0,
|
||||
.disable_control_phase = 0,
|
||||
},
|
||||
.scl_speed_hz = 400 * 1000,
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c_v2(display_i2c_bus_, &io_config, &panel_io_));
|
||||
|
||||
ESP_LOGI(TAG, "Install SSD1306 driver");
|
||||
esp_lcd_panel_dev_config_t panel_config = {};
|
||||
panel_config.reset_gpio_num = -1;
|
||||
panel_config.bits_per_pixel = 1;
|
||||
|
||||
esp_lcd_panel_ssd1306_config_t ssd1306_config = {
|
||||
.height = static_cast<uint8_t>(DISPLAY_HEIGHT),
|
||||
};
|
||||
panel_config.vendor_config = &ssd1306_config;
|
||||
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1306(panel_io_, &panel_config, &panel_));
|
||||
ESP_LOGI(TAG, "SSD1306 driver installed");
|
||||
|
||||
// Reset the display
|
||||
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_));
|
||||
if (esp_lcd_panel_init(panel_) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to initialize display");
|
||||
display_ = new NoDisplay();
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the display to on
|
||||
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);
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
boot_button_.OnClick([]() {
|
||||
Application::GetInstance().ToggleChatState();
|
||||
});
|
||||
|
||||
touch_button_.OnPressDown([]() {
|
||||
Application::GetInstance().StartListening();
|
||||
});
|
||||
touch_button_.OnPressUp([]() {
|
||||
Application::GetInstance().StopListening();
|
||||
});
|
||||
|
||||
volume_up_button_.OnClick([this]() {
|
||||
auto codec = GetAudioCodec();
|
||||
auto volume = codec->output_volume() + 10;
|
||||
if (volume > 100) {
|
||||
volume = 100;
|
||||
}
|
||||
codec->SetOutputVolume(volume);
|
||||
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
|
||||
});
|
||||
|
||||
volume_up_button_.OnLongPress([this]() {
|
||||
GetAudioCodec()->SetOutputVolume(100);
|
||||
GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
|
||||
});
|
||||
|
||||
volume_down_button_.OnClick([this]() {
|
||||
auto codec = GetAudioCodec();
|
||||
auto volume = codec->output_volume() - 10;
|
||||
if (volume < 0) {
|
||||
volume = 0;
|
||||
}
|
||||
codec->SetOutputVolume(volume);
|
||||
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
|
||||
});
|
||||
|
||||
volume_down_button_.OnLongPress([this]() {
|
||||
GetAudioCodec()->SetOutputVolume(0);
|
||||
GetDisplay()->ShowNotification(Lang::Strings::MUTED);
|
||||
});
|
||||
}
|
||||
|
||||
// 物联网初始化,添加对 AI 可见设备
|
||||
void InitializeTools() {
|
||||
static LampController lamp(LAMP_GPIO);
|
||||
}
|
||||
|
||||
public:
|
||||
CompactNt26Board() :
|
||||
Nt26Board(NT26_TX_PIN, NT26_RX_PIN, NT26_DTR_PIN, NT26_RI_PIN),
|
||||
boot_button_(BOOT_BUTTON_GPIO),
|
||||
touch_button_(TOUCH_BUTTON_GPIO),
|
||||
volume_up_button_(VOLUME_UP_BUTTON_GPIO),
|
||||
volume_down_button_(VOLUME_DOWN_BUTTON_GPIO) {
|
||||
|
||||
InitializeDisplayI2c();
|
||||
InitializeSsd1306Display();
|
||||
InitializeButtons();
|
||||
InitializeTools();
|
||||
}
|
||||
|
||||
virtual void StartNetwork() override {
|
||||
GetDisplay()->SetStatus(Lang::Strings::DETECTING_MODULE);
|
||||
Nt26Board::StartNetwork();
|
||||
}
|
||||
|
||||
virtual Led* GetLed() override {
|
||||
static SingleLed led(BUILTIN_LED_GPIO);
|
||||
return &led;
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override {
|
||||
#ifdef AUDIO_I2S_METHOD_SIMPLEX
|
||||
static NoAudioCodecSimplex audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_I2S_SPK_GPIO_BCLK, AUDIO_I2S_SPK_GPIO_LRCK, AUDIO_I2S_SPK_GPIO_DOUT, AUDIO_I2S_MIC_GPIO_SCK, AUDIO_I2S_MIC_GPIO_WS, AUDIO_I2S_MIC_GPIO_DIN);
|
||||
#else
|
||||
static NoAudioCodecDuplex audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
|
||||
AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN);
|
||||
#endif
|
||||
return &audio_codec;
|
||||
}
|
||||
|
||||
virtual Display* GetDisplay() override {
|
||||
return display_;
|
||||
}
|
||||
};
|
||||
|
||||
DECLARE_BOARD(CompactNt26Board);
|
||||
61
main/boards/bread-compact-nt26/config.h
Normal file
61
main/boards/bread-compact-nt26/config.h
Normal file
@@ -0,0 +1,61 @@
|
||||
#ifndef _BOARD_CONFIG_H_
|
||||
#define _BOARD_CONFIG_H_
|
||||
|
||||
#include <driver/gpio.h>
|
||||
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 16000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
|
||||
// 如果使用 Duplex I2S 模式,请注释下面一行
|
||||
#define AUDIO_I2S_METHOD_SIMPLEX
|
||||
|
||||
#ifdef AUDIO_I2S_METHOD_SIMPLEX
|
||||
|
||||
#define AUDIO_I2S_MIC_GPIO_WS GPIO_NUM_4
|
||||
#define AUDIO_I2S_MIC_GPIO_SCK GPIO_NUM_5
|
||||
#define AUDIO_I2S_MIC_GPIO_DIN GPIO_NUM_6
|
||||
#define AUDIO_I2S_SPK_GPIO_DOUT GPIO_NUM_7
|
||||
#define AUDIO_I2S_SPK_GPIO_BCLK GPIO_NUM_15
|
||||
#define AUDIO_I2S_SPK_GPIO_LRCK GPIO_NUM_16
|
||||
|
||||
#else
|
||||
|
||||
#define AUDIO_I2S_GPIO_WS GPIO_NUM_4
|
||||
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_5
|
||||
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_6
|
||||
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_7
|
||||
|
||||
#endif
|
||||
|
||||
#define BUILTIN_LED_GPIO GPIO_NUM_48
|
||||
#define BOOT_BUTTON_GPIO GPIO_NUM_0
|
||||
#define TOUCH_BUTTON_GPIO GPIO_NUM_47
|
||||
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_40
|
||||
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_39
|
||||
|
||||
#define DISPLAY_SDA_PIN GPIO_NUM_41
|
||||
#define DISPLAY_SCL_PIN GPIO_NUM_42
|
||||
#define DISPLAY_WIDTH 128
|
||||
|
||||
#if CONFIG_OLED_SSD1306_128X32
|
||||
#define DISPLAY_HEIGHT 32
|
||||
#elif CONFIG_OLED_SSD1306_128X64
|
||||
#define DISPLAY_HEIGHT 64
|
||||
#else
|
||||
#error "OLED display type is not selected"
|
||||
#endif
|
||||
|
||||
#define DISPLAY_MIRROR_X true
|
||||
#define DISPLAY_MIRROR_Y true
|
||||
|
||||
|
||||
#define NT26_DTR_PIN GPIO_NUM_9
|
||||
#define NT26_RI_PIN GPIO_NUM_10
|
||||
#define NT26_RX_PIN GPIO_NUM_11
|
||||
#define NT26_TX_PIN GPIO_NUM_12
|
||||
|
||||
|
||||
// A MCP Test: Control a lamp
|
||||
#define LAMP_GPIO GPIO_NUM_18
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
11
main/boards/bread-compact-nt26/config.json
Normal file
11
main/boards/bread-compact-nt26/config.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"target": "esp32s3",
|
||||
"builds": [
|
||||
{
|
||||
"name": "bread-compact-nt26",
|
||||
"sdkconfig_append": [
|
||||
"CONFIG_OLED_SSD1306_128X32=y"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -46,7 +46,7 @@
|
||||
#define DISPLAY_HEIGHT 64
|
||||
#define SH1106
|
||||
#else
|
||||
#error "未选择 OLED 屏幕类型"
|
||||
#error "OLED display type is not selected"
|
||||
#endif
|
||||
|
||||
#define DISPLAY_MIRROR_X true
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#include "ml307_board.h"
|
||||
|
||||
#include "application.h"
|
||||
#include "audio_codec.h"
|
||||
#include "display.h"
|
||||
#include "assets/lang_config.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <esp_timer.h>
|
||||
@@ -66,8 +65,6 @@ void Ml307Board::OnNetworkEvent(NetworkEvent event, const std::string& data) {
|
||||
}
|
||||
|
||||
void Ml307Board::NetworkTask() {
|
||||
auto& application = Application::GetInstance();
|
||||
|
||||
// Notify modem detection started
|
||||
OnNetworkEvent(NetworkEvent::ModemDetecting);
|
||||
|
||||
@@ -92,7 +89,7 @@ void Ml307Board::NetworkTask() {
|
||||
|
||||
// Set up network state change callback
|
||||
// Note: Don't call GetCarrierName() here as it sends AT command and will block ReceiveTask
|
||||
modem_->OnNetworkStateChanged([this, &application](bool network_ready) {
|
||||
modem_->OnNetworkStateChanged([this](bool network_ready) {
|
||||
if (network_ready) {
|
||||
OnNetworkEvent(NetworkEvent::Connected);
|
||||
} else {
|
||||
|
||||
266
main/boards/common/nt26_board.cc
Normal file
266
main/boards/common/nt26_board.cc
Normal file
@@ -0,0 +1,266 @@
|
||||
#include "nt26_board.h"
|
||||
#include "display.h"
|
||||
#include "application.h"
|
||||
#include "audio_codec.h"
|
||||
#include <esp_log.h>
|
||||
#include <font_awesome.h>
|
||||
#include <cJSON.h>
|
||||
|
||||
#define TAG "Nt26Board"
|
||||
|
||||
Nt26Board::Nt26Board(gpio_num_t tx_pin, gpio_num_t rx_pin, gpio_num_t dtr_pin, gpio_num_t ri_pin, gpio_num_t reset_pin)
|
||||
: tx_pin_(tx_pin), rx_pin_(rx_pin), dtr_pin_(dtr_pin), ri_pin_(ri_pin), reset_pin_(reset_pin) {
|
||||
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_IRAM);
|
||||
esp_event_loop_create_default();
|
||||
esp_netif_init();
|
||||
|
||||
// Create PM lock handle
|
||||
esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "nt26_cpu", &pm_lock_cpu_max_);
|
||||
|
||||
// Create network ready timeout timer
|
||||
esp_timer_create_args_t timer_args = {
|
||||
.callback = OnNetworkReadyTimeout,
|
||||
.arg = this,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "nt26_net_timer",
|
||||
.skip_unhandled_events = true
|
||||
};
|
||||
esp_timer_create(&timer_args, &network_ready_timer_);
|
||||
}
|
||||
|
||||
Nt26Board::~Nt26Board() {
|
||||
if (current_power_level_ != PowerSaveLevel::LOW_POWER) {
|
||||
SetPowerSaveLevel(PowerSaveLevel::LOW_POWER);
|
||||
}
|
||||
|
||||
if (network_ready_timer_) {
|
||||
esp_timer_stop(network_ready_timer_);
|
||||
esp_timer_delete(network_ready_timer_);
|
||||
}
|
||||
|
||||
if (modem_) {
|
||||
modem_->Stop();
|
||||
}
|
||||
|
||||
if (pm_lock_cpu_max_) {
|
||||
esp_pm_lock_delete(pm_lock_cpu_max_);
|
||||
}
|
||||
}
|
||||
|
||||
std::string Nt26Board::GetBoardType() {
|
||||
return "nt26";
|
||||
}
|
||||
|
||||
void Nt26Board::OnNetworkEvent(NetworkEvent event, const std::string& data) {
|
||||
if (network_event_callback_) {
|
||||
network_event_callback_(event, data);
|
||||
}
|
||||
}
|
||||
|
||||
void Nt26Board::OnNetworkReadyTimeout(void* arg) {
|
||||
auto* self = static_cast<Nt26Board*>(arg);
|
||||
ESP_LOGW(TAG, "Network ready timeout");
|
||||
self->OnNetworkEvent(NetworkEvent::ModemErrorTimeout, "网络连接超时");
|
||||
}
|
||||
|
||||
void Nt26Board::StartNetwork() {
|
||||
OnNetworkEvent(NetworkEvent::ModemDetecting);
|
||||
|
||||
UartEthModem::Config config = {
|
||||
.uart_num = UART_NUM_1,
|
||||
.baud_rate = 3000000,
|
||||
.tx_pin = tx_pin_,
|
||||
.rx_pin = rx_pin_,
|
||||
.mrdy_pin = dtr_pin_,
|
||||
.srdy_pin = ri_pin_
|
||||
};
|
||||
|
||||
modem_ = std::make_unique<UartEthModem>(config);
|
||||
modem_->SetDebug(false);
|
||||
|
||||
modem_->SetNetworkEventCallback([this](UartEthModem::UartEthModemEvent event) {
|
||||
switch (event) {
|
||||
case UartEthModem::UartEthModemEvent::Connected:
|
||||
esp_timer_stop(network_ready_timer_);
|
||||
OnNetworkEvent(NetworkEvent::Connected);
|
||||
break;
|
||||
case UartEthModem::UartEthModemEvent::Disconnected:
|
||||
OnNetworkEvent(NetworkEvent::Disconnected);
|
||||
break;
|
||||
case UartEthModem::UartEthModemEvent::ErrorNoSim:
|
||||
esp_timer_stop(network_ready_timer_);
|
||||
ScheduleAsyncStop();
|
||||
OnNetworkEvent(NetworkEvent::ModemErrorNoSim);
|
||||
break;
|
||||
case UartEthModem::UartEthModemEvent::ErrorRegistrationDenied:
|
||||
esp_timer_stop(network_ready_timer_);
|
||||
ScheduleAsyncStop();
|
||||
OnNetworkEvent(NetworkEvent::ModemErrorRegDenied);
|
||||
break;
|
||||
case UartEthModem::UartEthModemEvent::Connecting:
|
||||
OnNetworkEvent(NetworkEvent::Connecting);
|
||||
break;
|
||||
case UartEthModem::UartEthModemEvent::ErrorInitFailed:
|
||||
case UartEthModem::UartEthModemEvent::ErrorNoCarrier:
|
||||
esp_timer_stop(network_ready_timer_);
|
||||
ScheduleAsyncStop();
|
||||
OnNetworkEvent(NetworkEvent::ModemErrorInitFailed);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
if (modem_->Start() != ESP_OK) {
|
||||
OnNetworkEvent(NetworkEvent::ModemErrorInitFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
esp_timer_start_once(network_ready_timer_, 30000 * 1000ULL);
|
||||
OnNetworkEvent(NetworkEvent::Connecting);
|
||||
}
|
||||
|
||||
void Nt26Board::ScheduleAsyncStop() {
|
||||
Application::GetInstance().Schedule([this]() {
|
||||
if (modem_) {
|
||||
modem_->Stop();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Nt26Board::SetNetworkEventCallback(NetworkEventCallback callback) {
|
||||
network_event_callback_ = std::move(callback);
|
||||
}
|
||||
|
||||
NetworkInterface* Nt26Board::GetNetwork() {
|
||||
static EspNetwork network;
|
||||
return &network;
|
||||
}
|
||||
|
||||
const char* Nt26Board::GetNetworkStateIcon() {
|
||||
if (modem_ == nullptr || !modem_->IsInitialized()) {
|
||||
return FONT_AWESOME_SIGNAL_OFF;
|
||||
}
|
||||
int csq = modem_->GetSignalStrength();
|
||||
if (csq == 99 || csq == -1) {
|
||||
return FONT_AWESOME_SIGNAL_OFF;
|
||||
} else if (csq >= 0 && csq <= 9) {
|
||||
return FONT_AWESOME_SIGNAL_WEAK;
|
||||
} else if (csq >= 10 && csq <= 14) {
|
||||
return FONT_AWESOME_SIGNAL_FAIR;
|
||||
} else if (csq >= 15 && csq <= 19) {
|
||||
return FONT_AWESOME_SIGNAL_GOOD;
|
||||
} else if (csq >= 20 && csq <= 31) {
|
||||
return FONT_AWESOME_SIGNAL_STRONG;
|
||||
}
|
||||
return FONT_AWESOME_SIGNAL_OFF;
|
||||
}
|
||||
|
||||
void Nt26Board::SetPowerSaveLevel(PowerSaveLevel level) {
|
||||
if (level == current_power_level_) return;
|
||||
|
||||
if (current_power_level_ == PowerSaveLevel::BALANCED ||
|
||||
current_power_level_ == PowerSaveLevel::PERFORMANCE) {
|
||||
if (pm_lock_cpu_max_) {
|
||||
esp_pm_lock_release(pm_lock_cpu_max_);
|
||||
}
|
||||
}
|
||||
|
||||
if (level == PowerSaveLevel::BALANCED || level == PowerSaveLevel::PERFORMANCE) {
|
||||
if (pm_lock_cpu_max_) {
|
||||
esp_pm_lock_acquire(pm_lock_cpu_max_);
|
||||
}
|
||||
}
|
||||
|
||||
current_power_level_ = level;
|
||||
}
|
||||
|
||||
std::string Nt26Board::GetBoardJson() {
|
||||
// Set the board type for OTA
|
||||
std::string board_json = std::string("{\"type\":\"" BOARD_TYPE "\",");
|
||||
board_json += "\"name\":\"" BOARD_NAME "\",";
|
||||
if (modem_) {
|
||||
board_json += "\"revision\":\"" + modem_->GetModuleRevision() + "\",";
|
||||
board_json += "\"carrier\":\"" + modem_->GetCarrierName() + "\",";
|
||||
board_json += "\"csq\":\"" + std::to_string(modem_->GetSignalStrength()) + "\",";
|
||||
board_json += "\"imei\":\"" + modem_->GetImei() + "\",";
|
||||
board_json += "\"iccid\":\"" + modem_->GetIccid() + "\",";
|
||||
board_json += "\"cereg\":" + GetRegistrationState().ToString() + "}";
|
||||
} else {
|
||||
board_json += "\"status\":\"offline\"}";
|
||||
}
|
||||
return board_json;
|
||||
}
|
||||
|
||||
Nt26CeregState Nt26Board::GetRegistrationState() {
|
||||
Nt26CeregState state;
|
||||
if (modem_) {
|
||||
auto cell_info = modem_->GetCellInfo();
|
||||
state.stat = cell_info.stat;
|
||||
state.tac = cell_info.tac;
|
||||
state.ci = cell_info.ci;
|
||||
state.AcT = cell_info.act;
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
std::string Nt26Board::GetDeviceStatusJson() {
|
||||
auto& board = Board::GetInstance();
|
||||
auto root = cJSON_CreateObject();
|
||||
|
||||
// Audio speaker
|
||||
auto audio_speaker = cJSON_CreateObject();
|
||||
auto audio_codec = board.GetAudioCodec();
|
||||
if (audio_codec) {
|
||||
cJSON_AddNumberToObject(audio_speaker, "volume", audio_codec->output_volume());
|
||||
}
|
||||
cJSON_AddItemToObject(root, "audio_speaker", audio_speaker);
|
||||
|
||||
// Screen
|
||||
auto backlight = board.GetBacklight();
|
||||
auto screen = cJSON_CreateObject();
|
||||
if (backlight) {
|
||||
cJSON_AddNumberToObject(screen, "brightness", backlight->brightness());
|
||||
}
|
||||
auto display = board.GetDisplay();
|
||||
if (display && display->height() > 64) {
|
||||
auto theme = display->GetTheme();
|
||||
if (theme != nullptr) {
|
||||
cJSON_AddStringToObject(screen, "theme", theme->name().c_str());
|
||||
}
|
||||
}
|
||||
cJSON_AddItemToObject(root, "screen", screen);
|
||||
|
||||
// Battery
|
||||
int battery_level = 0;
|
||||
bool charging = false, discharging = false;
|
||||
if (board.GetBatteryLevel(battery_level, charging, discharging)) {
|
||||
auto battery = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(battery, "level", battery_level);
|
||||
cJSON_AddBoolToObject(battery, "charging", charging);
|
||||
cJSON_AddItemToObject(root, "battery", battery);
|
||||
}
|
||||
|
||||
// Network
|
||||
auto network = cJSON_CreateObject();
|
||||
cJSON_AddStringToObject(network, "type", "cellular");
|
||||
if (modem_) {
|
||||
cJSON_AddStringToObject(network, "carrier", modem_->GetCarrierName().c_str());
|
||||
int csq = modem_->GetSignalStrength();
|
||||
if (csq == 99 || csq == -1) {
|
||||
cJSON_AddStringToObject(network, "signal", "unknown");
|
||||
} else if (csq >= 0 && csq <= 14) {
|
||||
cJSON_AddStringToObject(network, "signal", "weak");
|
||||
} else if (csq >= 15 && csq <= 24) {
|
||||
cJSON_AddStringToObject(network, "signal", "medium");
|
||||
} else if (csq >= 25 && csq <= 31) {
|
||||
cJSON_AddStringToObject(network, "signal", "strong");
|
||||
}
|
||||
}
|
||||
cJSON_AddItemToObject(root, "network", network);
|
||||
|
||||
auto json_str = cJSON_PrintUnformatted(root);
|
||||
std::string json(json_str);
|
||||
cJSON_free(json_str);
|
||||
cJSON_Delete(root);
|
||||
return json;
|
||||
}
|
||||
62
main/boards/common/nt26_board.h
Normal file
62
main/boards/common/nt26_board.h
Normal file
@@ -0,0 +1,62 @@
|
||||
#ifndef NT26_BOARD_H
|
||||
#define NT26_BOARD_H
|
||||
|
||||
#include <memory>
|
||||
#include <uart_eth_modem.h>
|
||||
#include <esp_network.h>
|
||||
#include <esp_pm.h>
|
||||
#include <esp_timer.h>
|
||||
#include "board.h"
|
||||
|
||||
struct Nt26CeregState {
|
||||
int stat = 0;
|
||||
std::string tac;
|
||||
std::string ci;
|
||||
int AcT = -1;
|
||||
|
||||
std::string ToString() const {
|
||||
std::string json = "{";
|
||||
json += "\"stat\":" + std::to_string(stat);
|
||||
if (!tac.empty()) json += ",\"tac\":\"" + tac + "\"";
|
||||
if (!ci.empty()) json += ",\"ci\":\"" + ci + "\"";
|
||||
if (AcT >= 0) json += ",\"AcT\":" + std::to_string(AcT);
|
||||
json += "}";
|
||||
return json;
|
||||
}
|
||||
};
|
||||
|
||||
class Nt26Board : public Board {
|
||||
protected:
|
||||
std::unique_ptr<UartEthModem> modem_;
|
||||
gpio_num_t tx_pin_;
|
||||
gpio_num_t rx_pin_;
|
||||
gpio_num_t dtr_pin_; // mrdy_pin
|
||||
gpio_num_t ri_pin_; // srdy_pin
|
||||
gpio_num_t reset_pin_;
|
||||
|
||||
NetworkEventCallback network_event_callback_;
|
||||
esp_pm_lock_handle_t pm_lock_cpu_max_ = nullptr;
|
||||
PowerSaveLevel current_power_level_ = PowerSaveLevel::LOW_POWER;
|
||||
esp_timer_handle_t network_ready_timer_ = nullptr;
|
||||
|
||||
virtual std::string GetBoardJson() override;
|
||||
|
||||
void OnNetworkEvent(NetworkEvent event, const std::string& data = "");
|
||||
static void OnNetworkReadyTimeout(void* arg);
|
||||
void ScheduleAsyncStop();
|
||||
|
||||
public:
|
||||
Nt26Board(gpio_num_t tx_pin, gpio_num_t rx_pin, gpio_num_t dtr_pin, gpio_num_t ri_pin, gpio_num_t reset_pin = GPIO_NUM_NC);
|
||||
virtual ~Nt26Board();
|
||||
virtual std::string GetBoardType() override;
|
||||
virtual void StartNetwork() override;
|
||||
virtual void SetNetworkEventCallback(NetworkEventCallback callback) override;
|
||||
virtual NetworkInterface* GetNetwork() override;
|
||||
virtual const char* GetNetworkStateIcon() override;
|
||||
virtual void SetPowerSaveLevel(PowerSaveLevel level) override;
|
||||
virtual AudioCodec* GetAudioCodec() override { return nullptr; }
|
||||
virtual std::string GetDeviceStatusJson() override;
|
||||
Nt26CeregState GetRegistrationState();
|
||||
};
|
||||
|
||||
#endif // NT26_BOARD_H
|
||||
@@ -22,6 +22,10 @@ dependencies:
|
||||
espressif/esp_audio_effects: ~1.2.0
|
||||
espressif/esp_audio_codec: ~2.4.0
|
||||
78/esp-ml307: ~3.5.3
|
||||
78/uart-eth-modem:
|
||||
version: ~0.1.3
|
||||
rules:
|
||||
- if: target not in [esp32]
|
||||
78/xiaozhi-fonts: ~1.5.5
|
||||
espressif/led_strip: ~3.0.1
|
||||
espressif/esp_codec_dev: ~1.5
|
||||
@@ -107,5 +111,5 @@ dependencies:
|
||||
|
||||
## Required IDF version
|
||||
idf:
|
||||
version: '>=5.4.0'
|
||||
version: '>=5.5.2'
|
||||
espressif/esp_lcd_touch_st7123: ^1.0.0
|
||||
|
||||
Reference in New Issue
Block a user