Compare commits

...

6 Commits

Author SHA1 Message Date
Johnson
3a3dfc003e Fix build error with esp_emote_gfx (#1159)
'bool' is defined in header '<stdbool.h>'; this is probably fixable by adding '#include <stdbool.h>'
   11 | #include "decoder/gfx_aaf_format.h"
2025-09-04 15:18:22 +08:00
Xiaoxia
fc355605f5 fix compiling errors (#1161) 2025-09-04 15:17:32 +08:00
Xiaoxia
5d3f597137 Bump to v1.9.0 (#1157)
* update v2 partition table readme

* feat: Add user only tool

* Add image cache

* smaller cache and buffer, more heap

* use MAIN_EVENT_CLOCK_TICK to avoid audio glitches

* fix: esp_psram_get_size not found in c3

* Bump to 1.9.0
2025-09-04 12:30:26 +08:00
ggc121238
3e37551923 add waveshare-s3-audio-board (#1139)
* add waveshare-s3-audio-board

* Modify the product link in the readme of the waveshare esp32s3-audio-board

* Modify reset time
2025-09-01 10:50:39 +08:00
Xiaoxia
d09537ed5c Add V2 parition tables (#1137)
* v1.9.0: update font icons, add mqtt reconnect

* Add v2 parition tables
2025-08-29 09:04:23 +08:00
Create123
86921f4862 Add M5Stack AtomEchoS3R Board. (#1123)
Signed-off-by: hlym123 <lwylwt@qq.com>
2025-08-26 14:05:10 +08:00
59 changed files with 1447 additions and 142 deletions

View File

@@ -4,7 +4,7 @@
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
set(PROJECT_VER "1.8.9")
set(PROJECT_VER "1.9.0")
# Add this line to disable the specific warning
add_compile_options(-Wno-missing-field-initializers)

View File

@@ -86,6 +86,8 @@ elseif(CONFIG_BOARD_TYPE_ATOMS3R_ECHO_BASE)
set(BOARD_TYPE "atoms3r-echo-base")
elseif(CONFIG_BOARD_TYPE_ATOMS3R_CAM_M12_ECHO_BASE)
set(BOARD_TYPE "atoms3r-cam-m12-echo-base")
elseif(CONFIG_BOARD_TYPE_ATOM_ECHOS3R)
set(BOARD_TYPE "atom-echos3r")
elseif(CONFIG_BOARD_TYPE_ATOMMATRIX_ECHO_BASE)
set(BOARD_TYPE "atommatrix-echo-base")
elseif(CONFIG_BOARD_TYPE_XMINI_C3_V3)
@@ -104,6 +106,8 @@ elseif(CONFIG_BOARD_TYPE_ESP_HI)
set(BOARD_TYPE "esp-hi")
elseif(CONFIG_BOARD_TYPE_ECHOEAR)
set(BOARD_TYPE "echoear")
elseif(CONFIG_BOARD_TYPE_ESP32S3_AUDIO_BOARD)
set(BOARD_TYPE "waveshare-s3-audio-board")
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_AMOLED_1_8)
set(BOARD_TYPE "esp32-s3-touch-amoled-1.8")
elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_AMOLED_2_06)
@@ -394,4 +398,4 @@ spiffs_create_partition_assets(
MMAP_FILE_SUPPORT_FORMAT ".aaf, ttf, bin"
IMPORT_INC_PATH ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}
)
endif()
endif()

View File

@@ -178,9 +178,15 @@ choice BOARD_TYPE
config BOARD_TYPE_ATOMS3R_CAM_M12_ECHO_BASE
bool "AtomS3R CAM/M12 + Echo Base"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ATOM_ECHOS3R
bool "AtomEchoS3R"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ATOMMATRIX_ECHO_BASE
bool "AtomMatrix + Echo Base"
depends on IDF_TARGET_ESP32
config BOARD_TYPE_ESP32S3_AUDIO_BOARD
bool "Waveshare ESP32-S3-Audio-Board"
depends on IDF_TARGET_ESP32S3
config BOARD_TYPE_ESP32S3_Touch_AMOLED_1_8
bool "Waveshare ESP32-S3-Touch-AMOLED-1.8"
depends on IDF_TARGET_ESP32S3
@@ -422,15 +428,27 @@ endchoice
choice DISPLAY_ESP32S3_KORVO2_V3
depends on BOARD_TYPE_ESP32S3_KORVO2_V3
prompt "ESP32S3_KORVO2_V3 LCD Type"
default LCD_ST7789
default ESP32S3_KORVO2_V3_LCD_ST7789
help
屏幕类型选择
config LCD_ST7789
config ESP32S3_KORVO2_V3_LCD_ST7789
bool "ST7789, 分辨率240*280"
config LCD_ILI9341
config ESP32S3_KORVO2_V3_LCD_ILI9341
bool "ILI9341, 分辨率240*320"
endchoice
choice DISPLAY_ESP32S3_AUDIO_BOARD
depends on BOARD_TYPE_ESP32S3_AUDIO_BOARD
prompt "ESP32S3_AUDIO_BOARD LCD Type"
default AUDIO_BOARD_LCD_JD9853
help
屏幕类型选择
config AUDIO_BOARD_LCD_JD9853
bool "JD9853, 分辨率320*172"
config AUDIO_BOARD_LCD_ST7789
bool "ST7789, 分辨率240*320"
endchoice
config USE_WECHAT_MESSAGE_STYLE
bool "Enable WeChat Message Style"
default n

View File

@@ -5,7 +5,6 @@
#include "audio_codec.h"
#include "mqtt_protocol.h"
#include "websocket_protocol.h"
#include "font_awesome_symbols.h"
#include "assets/lang_config.h"
#include "mcp_server.h"
@@ -14,6 +13,7 @@
#include <cJSON.h>
#include <driver/gpio.h>
#include <arpa/inet.h>
#include <font_awesome.h>
#define TAG "Application"
@@ -49,7 +49,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,
@@ -87,7 +87,7 @@ void Application::CheckNewVersion(Ota& ota) {
char buffer[256];
snprintf(buffer, sizeof(buffer), Lang::Strings::CHECK_NEW_VERSION_FAILED, retry_delay, ota.GetCheckVersionUrl().c_str());
Alert(Lang::Strings::ERROR, buffer, "sad", Lang::Sounds::OGG_EXCLAMATION);
Alert(Lang::Strings::ERROR, buffer, "cloud_slash", Lang::Sounds::OGG_EXCLAMATION);
ESP_LOGW(TAG, "Check new version failed, retry in %d seconds (%d/%d)", retry_delay, retry_count, MAX_RETRY);
for (int i = 0; i < retry_delay; i++) {
@@ -103,13 +103,12 @@ void Application::CheckNewVersion(Ota& ota) {
retry_delay = 10; // 重置重试延迟时间
if (ota.HasNewVersion()) {
Alert(Lang::Strings::OTA_UPGRADE, Lang::Strings::UPGRADING, "happy", Lang::Sounds::OGG_UPGRADE);
Alert(Lang::Strings::OTA_UPGRADE, Lang::Strings::UPGRADING, "download", Lang::Sounds::OGG_UPGRADE);
vTaskDelay(pdMS_TO_TICKS(3000));
SetDeviceState(kDeviceStateUpgrading);
display->SetIcon(FONT_AWESOME_DOWNLOAD);
std::string message = std::string(Lang::Strings::NEW_VERSION) + ota.GetFirmwareVersion();
display->SetChatMessage("system", message.c_str());
@@ -130,7 +129,7 @@ void Application::CheckNewVersion(Ota& ota) {
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, "sad", Lang::Sounds::OGG_EXCLAMATION);
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 {
@@ -195,7 +194,7 @@ void Application::ShowActivationCode(const std::string& code, const std::string&
}};
// This sentence uses 9KB of SRAM, so we need to wait for it to finish
Alert(Lang::Strings::ACTIVATION, message.c_str(), "happy", Lang::Sounds::OGG_ACTIVATION);
Alert(Lang::Strings::ACTIVATION, message.c_str(), "link", Lang::Sounds::OGG_ACTIVATION);
for (const auto& digit : code) {
auto it = std::find_if(digit_sounds.begin(), digit_sounds.end(),
@@ -207,7 +206,7 @@ void Application::ShowActivationCode(const std::string& code, const std::string&
}
void Application::Alert(const char* status, const char* message, const char* emotion, const std::string_view& sound) {
ESP_LOGW(TAG, "Alert %s: %s [%s]", status, message, emotion);
ESP_LOGW(TAG, "Alert [%s] %s: %s", emotion, status, message);
auto display = Board::GetInstance().GetDisplay();
display->SetStatus(status);
display->SetEmotion(emotion);
@@ -378,6 +377,10 @@ void Application::Start() {
protocol_ = std::make_unique<MqttProtocol>();
}
protocol_->OnConnected([this]() {
DismissAlert();
});
protocol_->OnNetworkError([this](const std::string& message) {
last_error_message_ = message;
xEventGroupSetBits(event_group_, MAIN_EVENT_ERROR);
@@ -493,6 +496,8 @@ void Application::Start() {
});
bool protocol_started = protocol_->Start();
// Print heap stats
SystemInfo::PrintHeapStats();
SetDeviceState(kDeviceStateIdle);
has_server_time_ = ota.HasServerTime();
@@ -503,23 +508,6 @@ void Application::Start() {
// 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
@@ -543,10 +531,12 @@ void Application::MainEventLoop() {
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(), "sad", Lang::Sounds::OGG_EXCLAMATION);
Alert(Lang::Strings::ERROR, last_error_message_.c_str(), "circle_xmark", Lang::Sounds::OGG_EXCLAMATION);
}
if (bits & MAIN_EVENT_SEND_AUDIO) {
@@ -576,6 +566,19 @@ void Application::MainEventLoop() {
task();
}
}
if (bits & MAIN_EVENT_CLOCK_TICK) {
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();
}
}
}
}

View File

@@ -9,7 +9,6 @@
#include <string>
#include <mutex>
#include <deque>
#include <vector>
#include <memory>
#include "protocol.h"
@@ -17,12 +16,15 @@
#include "audio_service.h"
#include "device_state_event.h"
#define MAIN_EVENT_SCHEDULE (1 << 0)
#define MAIN_EVENT_SEND_AUDIO (1 << 1)
#define MAIN_EVENT_WAKE_WORD_DETECTED (1 << 2)
#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,
@@ -84,7 +86,6 @@ private:
void OnWakeWordDetected();
void CheckNewVersion(Ota& ota);
void ShowActivationCode(const std::string& code, const std::string& message);
void OnClockTimer();
void SetListeningMode(ListeningMode mode);
};

View File

@@ -111,7 +111,7 @@ void AudioService::Start() {
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 +125,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 */

View File

@@ -0,0 +1,45 @@
# AtomEchoS3R
## 简介
AtomEchoS3R 是 M5Stack 推出的基于 ESP32-S3-PICO-1-N8R8 的物联网可编程控制器,采用了 ES8311 单声道音频解码器、MEMS 麦克风和 NS4150B 功率放大器的集成方案。
开发版**不带屏幕、不带额外按键**,需要使用语音唤醒。必要时,需要使用 `idf.py monitor` 查看 log 以确定运行状态。
## 配置、编译命令
**配置编译目标为 ESP32S3**
```bash
idf.py set-target esp32s3
```
**打开 menuconfig 并配置**
```bash
idf.py menuconfig
```
分别配置如下选项:
- `Xiaozhi Assistant``Board Type` → 选择 `AtomEchoS3R`
- `Partition Table``Custom partition CSV file` → 删除原有内容,输入 `partitions/v1/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`
`S` 保存,按 `Q` 退出。
**编译**
```bash
idf.py build
```
**烧录**
将 AtomEchoS3R 连接到电脑,按住侧面 RESET 按键,直到 RESET 按键下方绿灯闪烁。
```bash
idf.py flash
```
烧录完毕后,按一下 RESET 按钮重启设备。

View File

@@ -0,0 +1,92 @@
#include "wifi_board.h"
#include "codecs/es8311_audio_codec.h"
#include "application.h"
#include "button.h"
#include "config.h"
#include "i2c_device.h"
#include "assets/lang_config.h"
#include <esp_log.h>
#include <driver/i2c_master.h>
#include <wifi_station.h>
#define TAG "AtomEchoS3R"
class AtomEchoS3rBaseBoard : public WifiBoard {
private:
i2c_master_bus_handle_t i2c_bus_;
Button boot_button_;
void InitializeI2c() {
// Initialize I2C peripheral
i2c_master_bus_config_t i2c_bus_cfg = {
.i2c_port = I2C_NUM_0,
.sda_io_num = AUDIO_CODEC_I2C_SDA_PIN,
.scl_io_num = AUDIO_CODEC_I2C_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(&i2c_bus_cfg, &i2c_bus_));
}
void I2cDetect() {
uint8_t address;
printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
for (int i = 0; i < 128; i += 16) {
printf("%02x: ", i);
for (int j = 0; j < 16; j++) {
fflush(stdout);
address = i + j;
esp_err_t ret = i2c_master_probe(i2c_bus_, address, pdMS_TO_TICKS(200));
if (ret == ESP_OK) {
printf("%02x ", address);
} else if (ret == ESP_ERR_TIMEOUT) {
printf("UU ");
} else {
printf("-- ");
}
}
printf("\r\n");
}
}
void InitializeButtons() {
boot_button_.OnClick([this]() {
auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
ResetWifiConfiguration();
}
app.ToggleChatState();
});
}
public:
AtomEchoS3rBaseBoard() : boot_button_(USER_BUTTON_GPIO) {
InitializeI2c();
I2cDetect();
InitializeButtons();
}
virtual AudioCodec* GetAudioCodec() override {
static Es8311AudioCodec audio_codec(
i2c_bus_,
I2C_NUM_0,
AUDIO_INPUT_SAMPLE_RATE,
AUDIO_OUTPUT_SAMPLE_RATE,
AUDIO_I2S_GPIO_MCLK,
AUDIO_I2S_GPIO_BCLK,
AUDIO_I2S_GPIO_WS,
AUDIO_I2S_GPIO_DOUT,
AUDIO_I2S_GPIO_DIN,
AUDIO_CODEC_GPIO_PA,
AUDIO_CODEC_ES8311_ADDR,
false);
return &audio_codec;
}
};
DECLARE_BOARD(AtomEchoS3rBaseBoard);

View File

@@ -0,0 +1,29 @@
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
// AtomEchoS3R Board configuration
#include <driver/gpio.h>
#define AUDIO_INPUT_REFERENCE true
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_11
#define AUDIO_I2S_GPIO_WS GPIO_NUM_3
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_17
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_4
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_48
#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_45
#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_0
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
#define AUDIO_CODEC_GPIO_PA GPIO_NUM_18
#define BUILTIN_LED_GPIO GPIO_NUM_NC
#define USER_BUTTON_GPIO GPIO_NUM_41
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_NC
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_NC
#endif // _BOARD_CONFIG_H_

View File

@@ -0,0 +1,12 @@
{
"target": "esp32s3",
"builds": [
{
"name": "atom-echos3r",
"sdkconfig_append": [
"CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y",
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v1/8m.csv\""
]
}
]
}

View File

@@ -111,7 +111,7 @@ private:
InitializeButtons();
GetBacklight()->SetBrightness(100);
display_->SetStatus(Lang::Strings::ERROR);
display_->SetEmotion("sad");
display_->SetEmotion("triangle_exclamation");
display_->SetChatMessage("system", "Echo Base\nnot connected");
while (1) {

View File

@@ -177,7 +177,7 @@ private:
InitializeButtons();
GetBacklight()->SetBrightness(100);
display_->SetStatus(Lang::Strings::ERROR);
display_->SetEmotion("sad");
display_->SetEmotion("triangle_exclamation");
display_->SetChatMessage("system", "Echo Base\nnot connected");
while (1) {

View File

@@ -89,6 +89,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,6 +102,8 @@ 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因为此时图像可以上传至服务器

View File

@@ -2,11 +2,11 @@
#include "application.h"
#include "display.h"
#include "font_awesome_symbols.h"
#include "assets/lang_config.h"
#include <esp_log.h>
#include <esp_timer.h>
#include <font_awesome.h>
#include <opus_encoder.h>
static const char *TAG = "Ml307Board";
@@ -50,9 +50,9 @@ void Ml307Board::StartNetwork() {
while (true) {
auto result = modem_->WaitForNetworkReady();
if (result == NetworkStatus::ErrorInsertPin) {
application.Alert(Lang::Strings::ERROR, Lang::Strings::PIN_ERROR, "sad", Lang::Sounds::OGG_ERR_PIN);
application.Alert(Lang::Strings::ERROR, Lang::Strings::PIN_ERROR, "triangle_exclamation", Lang::Sounds::OGG_ERR_PIN);
} else if (result == NetworkStatus::ErrorRegistrationDenied) {
application.Alert(Lang::Strings::ERROR, Lang::Strings::REG_ERROR, "sad", Lang::Sounds::OGG_ERR_REG);
application.Alert(Lang::Strings::ERROR, Lang::Strings::REG_ERROR, "triangle_exclamation", Lang::Sounds::OGG_ERR_REG);
} else {
break;
}
@@ -80,13 +80,13 @@ const char* Ml307Board::GetNetworkStateIcon() {
if (csq == -1) {
return FONT_AWESOME_SIGNAL_OFF;
} else if (csq >= 0 && csq <= 14) {
return FONT_AWESOME_SIGNAL_1;
return FONT_AWESOME_SIGNAL_WEAK;
} else if (csq >= 15 && csq <= 19) {
return FONT_AWESOME_SIGNAL_2;
return FONT_AWESOME_SIGNAL_FAIR;
} else if (csq >= 20 && csq <= 24) {
return FONT_AWESOME_SIGNAL_3;
return FONT_AWESOME_SIGNAL_GOOD;
} else if (csq >= 25 && csq <= 31) {
return FONT_AWESOME_SIGNAL_4;
return FONT_AWESOME_SIGNAL_STRONG;
}
ESP_LOGW(TAG, "Invalid CSQ: %d", csq);

View File

@@ -3,7 +3,6 @@
#include "display.h"
#include "application.h"
#include "system_info.h"
#include "font_awesome_symbols.h"
#include "settings.h"
#include "assets/lang_config.h"
@@ -12,6 +11,7 @@
#include <esp_network.h>
#include <esp_log.h>
#include <font_awesome.h>
#include <wifi_station.h>
#include <wifi_configuration_ap.h>
#include <ssid_manager.h>
@@ -49,7 +49,7 @@ void WifiBoard::EnterWifiConfigMode() {
hint += "\n\n";
// 播报配置 WiFi 的提示
application.Alert(Lang::Strings::WIFI_CONFIG_MODE, hint.c_str(), "", Lang::Sounds::OGG_WIFICONFIG);
application.Alert(Lang::Strings::WIFI_CONFIG_MODE, hint.c_str(), "gear", Lang::Sounds::OGG_WIFICONFIG);
#if CONFIG_USE_ACOUSTIC_WIFI_PROVISIONING
auto display = Board::GetInstance().GetDisplay();
@@ -124,7 +124,7 @@ const char* WifiBoard::GetNetworkStateIcon() {
}
auto& wifi_station = WifiStation::GetInstance();
if (!wifi_station.IsConnected()) {
return FONT_AWESOME_WIFI_OFF;
return FONT_AWESOME_WIFI_SLASH;
}
int8_t rssi = wifi_station.GetRssi();
if (rssi >= -60) {

View File

@@ -3,7 +3,6 @@
#include "display/lcd_display.h"
#include "esp_lcd_ili9341.h"
#include "led_control.h"
#include "font_awesome_symbols.h"
#include "application.h"
#include "button.h"
#include "config.h"

View File

@@ -1,12 +1,12 @@
#include "electron_emoji_display.h"
#include <esp_log.h>
#include <font_awesome.h>
#include <algorithm>
#include <cstring>
#include <string>
#include "font_awesome_symbols.h"
#define TAG "ElectronEmojiDisplay"

View File

@@ -2,7 +2,6 @@
#include "codecs/box_audio_codec.h"
#include "display/lcd_display.h"
#include "esp_lcd_ili9341.h"
#include "font_awesome_symbols.h"
#include "application.h"
#include "button.h"
#include "config.h"

View File

@@ -2,7 +2,6 @@
#include "box_audio_codec_lite.h"
#include "display/lcd_display.h"
#include "esp_lcd_ili9341.h"
#include "font_awesome_symbols.h"
#include "application.h"
#include "button.h"
#include "config.h"

View File

@@ -2,7 +2,6 @@
#include "codecs/box_audio_codec.h"
#include "display/lcd_display.h"
#include "esp_lcd_ili9341.h"
#include "font_awesome_symbols.h"
#include "application.h"
#include "button.h"
#include "config.h"

View File

@@ -1,7 +1,6 @@
#include "wifi_board.h"
#include "codecs/es8311_audio_codec.h"
#include "display/lcd_display.h"
#include "font_awesome_symbols.h"
#include "application.h"
#include "button.h"
#include "config.h"

View File

@@ -1,7 +1,6 @@
#include "wifi_board.h"
#include "display/lcd_display.h"
#include "esp_lcd_sh8601.h"
#include "font_awesome_symbols.h"
#include "codecs/es8311_audio_codec.h"
#include "application.h"

View File

@@ -26,7 +26,7 @@
#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_NC
#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_NC
#ifdef CONFIG_LCD_ST7789
#ifdef CONFIG_ESP32S3_KORVO2_V3_LCD_ST7789
#define DISPLAY_SDA_PIN GPIO_NUM_NC
#define DISPLAY_SCL_PIN GPIO_NUM_NC
#define DISPLAY_WIDTH 280
@@ -40,7 +40,7 @@
#define DISPLAY_OFFSET_Y 0
#endif
#ifdef CONFIG_LCD_ILI9341
#ifdef CONFIG_ESP32S3_KORVO2_V3_LCD_ILI9341
#define LCD_TYPE_ILI9341_SERIAL
#define DISPLAY_SDA_PIN GPIO_NUM_NC
#define DISPLAY_SCL_PIN GPIO_NUM_NC
@@ -78,4 +78,4 @@
#define CAMERA_PIN_PCLK 11
#define XCLK_FREQ_HZ 20000000
#endif // _BOARD_CONFIG_H_
#endif // _BOARD_CONFIG_H_

View File

@@ -2,7 +2,6 @@
#include "tab5_audio_codec.h"
#include "display/lcd_display.h"
#include "esp_lcd_ili9881c.h"
#include "font_awesome_symbols.h"
#include "font_emoji.h"
#include "application.h"
#include "button.h"

View File

@@ -5,7 +5,6 @@
#include "button.h"
#include "led/circular_strip.h"
#include "config.h"
#include "font_awesome_symbols.h"
#include "assets/lang_config.h"
#include <esp_lcd_panel_vendor.h>

View File

@@ -6,7 +6,6 @@
#include "led/circular_strip.h"
#include "config.h"
#include "assets/lang_config.h"
#include "font_awesome_symbols.h"
#include <esp_lcd_panel_vendor.h>
#include <wifi_station.h>

View File

@@ -6,7 +6,6 @@
#include "led/single_led.h"
#include "config.h"
#include "power_save_timer.h"
#include "font_awesome_symbols.h"
#include <wifi_station.h>
#include <esp_log.h>

View File

@@ -6,7 +6,6 @@
#include "led/single_led.h"
#include "config.h"
#include "power_save_timer.h"
#include "font_awesome_symbols.h"
#include <wifi_station.h>
#include <esp_log.h>

View File

@@ -1,13 +1,13 @@
#include "otto_emoji_display.h"
#include <esp_log.h>
#include <font_awesome.h>
#include <algorithm>
#include <cstring>
#include <string>
#include "display/lcd_display.h"
#include "font_awesome_symbols.h"
#define TAG "OttoEmojiDisplay"

View File

@@ -3,7 +3,6 @@
#include "wifi_board.h"
#include "sensecap_audio_codec.h"
#include "display/lcd_display.h"
#include "font_awesome_symbols.h"
#include "application.h"
#include "knob.h"
#include "config.h"

View File

@@ -8,7 +8,6 @@
#include "power_save_timer.h"
#include "axp2101.h"
#include "assets/lang_config.h"
#include "font_awesome_symbols.h"
#include <esp_log.h>
#include <driver/gpio.h>

View File

@@ -0,0 +1,3 @@
新增 微雪 开发板: ESP32-S3-AUDIO-Board
产品链接:
https://www.waveshare.net/shop/ESP32-S3-AUDIO-Board.htm

View File

@@ -0,0 +1,95 @@
#ifndef _BOARD_CONFIG_H_
#define _BOARD_CONFIG_H_
#include <driver/gpio.h>
#include <driver/spi_master.h>
#define AUDIO_INPUT_SAMPLE_RATE 24000
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
#define BOOT_BUTTON_GPIO GPIO_NUM_0
#define BUILTIN_LED_GPIO GPIO_NUM_38
#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_12
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_13
#define AUDIO_I2S_GPIO_WS GPIO_NUM_14
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_15
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_16
#define AUDIO_CODEC_PA_PIN GPIO_NUM_NC
#define AUDIO_INPUT_REFERENCE true
#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR
#define AUDIO_CODEC_ES7210_ADDR ES7210_CODEC_DEFAULT_ADDR
#define I2C_SCL_IO GPIO_NUM_10
#define I2C_SDA_IO GPIO_NUM_11
#define I2C_ADDRESS ESP_IO_EXPANDER_I2C_TCA9555_ADDRESS_000
#define DISPLAY_SDA_PIN I2C_SDA_IO
#define DISPLAY_SCL_PIN I2C_SCL_IO
#define DISPLAY_MISO_PIN GPIO_NUM_8
#define DISPLAY_MOSI_PIN GPIO_NUM_9
#define DISPLAY_SCLK_PIN GPIO_NUM_4
#define DISPLAY_CS_PIN GPIO_NUM_3
#define DISPLAY_DC_PIN GPIO_NUM_7
#define DISPLAY_RESET_PIN GPIO_NUM_NC
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_5
#define DISPLAY_SPI_SCLK_HZ (20 * 1000 * 1000)
/* Camera pins */
#define CAMERA_PIN_PWDN -1
#define CAMERA_PIN_RESET -1
#define CAMERA_PIN_XCLK 43
#define CAMERA_PIN_SIOD -1
#define CAMERA_PIN_SIOC -1
#define CAMERA_PIN_D7 48
#define CAMERA_PIN_D6 47
#define CAMERA_PIN_D5 46
#define CAMERA_PIN_D4 45
#define CAMERA_PIN_D3 39
#define CAMERA_PIN_D2 18
#define CAMERA_PIN_D1 17
#define CAMERA_PIN_D0 2
#define CAMERA_PIN_VSYNC 21
#define CAMERA_PIN_HREF 1
#define CAMERA_PIN_PCLK 44
#define XCLK_FREQ_HZ 20000000
#ifdef CONFIG_AUDIO_BOARD_LCD_JD9853
#define LCD_TYPE_JD9853_SERIAL
#define DISPLAY_WIDTH 320
#define DISPLAY_HEIGHT 172
#define DISPLAY_SWAP_XY true
#define DISPLAY_MIRROR_X false
#define DISPLAY_MIRROR_Y true
#define DISPLAY_INVERT_COLOR true
#define BACKLIGHT_INVERT false
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#endif
#ifdef CONFIG_AUDIO_BOARD_LCD_ST7789
#define LCD_TYPE_ST7789_SERIAL
#define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 320
#define DISPLAY_SWAP_XY false
#define DISPLAY_MIRROR_X false
#define DISPLAY_MIRROR_Y false
#define DISPLAY_INVERT_COLOR true
#define BACKLIGHT_INVERT false
#define DISPLAY_OFFSET_X 0
#define DISPLAY_OFFSET_Y 0
#endif
#endif // _BOARD_CONFIG_H_

View File

@@ -0,0 +1,9 @@
{
"target": "esp32s3",
"builds": [
{
"name": "waveshare-s3-audio-board",
"sdkconfig_append": []
}
]
}

View File

@@ -0,0 +1,247 @@
#include "wifi_board.h"
#include "codecs/box_audio_codec.h"
#include "display/lcd_display.h"
#include "system_reset.h"
#include "application.h"
#include "button.h"
#include "config.h"
#include <esp_log.h>
#include "i2c_device.h"
#include <driver/i2c_master.h>
#include <driver/ledc.h>
#include <wifi_station.h>
#include <esp_lcd_panel_io.h>
#include <esp_lcd_panel_ops.h>
#include <esp_lcd_st77916.h>
#include <esp_timer.h>
#include "esp_io_expander_tca95xx_16bit.h"
#include "esp32_camera.h"
#include "led/circular_strip.h"
#include "esp_lcd_jd9853.h"
#define TAG "waveshare_lcd_1_85c"
#define LCD_OPCODE_WRITE_CMD (0x02ULL)
#define LCD_OPCODE_READ_CMD (0x0BULL)
#define LCD_OPCODE_WRITE_COLOR (0x32ULL)
LV_FONT_DECLARE(font_puhui_14_1);
LV_FONT_DECLARE(font_awesome_14_1);
class CustomBoard : public WifiBoard {
private:
Button boot_button_;
i2c_master_bus_handle_t i2c_bus_;
esp_io_expander_handle_t io_expander = NULL;
LcdDisplay* display_;
Esp32Camera* camera_;
void InitializeI2c() {
// Initialize I2C peripheral
i2c_master_bus_config_t i2c_bus_cfg = {
.i2c_port = (i2c_port_t)0,
.sda_io_num = I2C_SDA_IO,
.scl_io_num = I2C_SCL_IO,
.clk_source = I2C_CLK_SRC_DEFAULT,
};
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_));
}
void InitializeTca9555(void)
{
esp_err_t ret = esp_io_expander_new_i2c_tca95xx_16bit(i2c_bus_, I2C_ADDRESS, &io_expander);
if(ret != ESP_OK)
ESP_LOGE(TAG, "TCA9554 create returned error"); // 打印引脚状态
ret = esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1 | IO_EXPANDER_PIN_NUM_8|IO_EXPANDER_PIN_NUM_5|IO_EXPANDER_PIN_NUM_6, IO_EXPANDER_OUTPUT); // 设置引脚 EXIO0 和 EXIO1 模式为输出
ESP_ERROR_CHECK(ret);
ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 1); // 复位 LCD 与 TouchPad
ESP_ERROR_CHECK(ret);
vTaskDelay(pdMS_TO_TICKS(10));
ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 0); // 复位 LCD 与 TouchPad
ESP_ERROR_CHECK(ret);
vTaskDelay(pdMS_TO_TICKS(10));
ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 1); // 复位 LCD 与 TouchPad
ESP_ERROR_CHECK(ret);
ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_8, 1); // 启用喇叭功放
ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_5, false); // 复位摄像头
vTaskDelay(pdMS_TO_TICKS(5));
ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_6, true);
vTaskDelay(pdMS_TO_TICKS(5));
ESP_ERROR_CHECK(ret);
}
void InitializeSpi() {
spi_bus_config_t buscfg = {};
buscfg.mosi_io_num = DISPLAY_MOSI_PIN;
buscfg.miso_io_num = GPIO_NUM_NC;
buscfg.sclk_io_num = DISPLAY_SCLK_PIN;
buscfg.quadwp_io_num = GPIO_NUM_NC;
buscfg.quadhd_io_num = GPIO_NUM_NC;
buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t);
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO));
}
void InitializeSt7789Display() {
esp_lcd_panel_io_handle_t panel_io = nullptr;
esp_lcd_panel_handle_t panel = nullptr;
// 液晶屏控制IO初始化
ESP_LOGD(TAG, "Install panel IO");
esp_lcd_panel_io_spi_config_t io_config = {};
io_config.cs_gpio_num = DISPLAY_CS_PIN;
io_config.dc_gpio_num = DISPLAY_DC_PIN;
io_config.spi_mode = 0;
io_config.pclk_hz = DISPLAY_SPI_SCLK_HZ;
io_config.trans_queue_depth = 10;
io_config.lcd_cmd_bits = 8;
io_config.lcd_param_bits = 8;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &panel_io));
// 初始化液晶屏驱动芯片ST7789
ESP_LOGD(TAG, "Install LCD driver");
esp_lcd_panel_dev_config_t panel_config = {};
panel_config.reset_gpio_num = GPIO_NUM_NC;
panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB;
panel_config.bits_per_pixel = 16;
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel));
ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY));
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y));
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR));
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(),
});
}
void InitializeJd9853Display() {
esp_lcd_panel_io_handle_t panel_io = nullptr;
esp_lcd_panel_handle_t panel = nullptr;
// 液晶屏控制IO初始化
ESP_LOGD(TAG, "Install panel IO");
esp_lcd_panel_io_spi_config_t io_config = {};
io_config.cs_gpio_num = DISPLAY_CS_PIN;
io_config.dc_gpio_num = DISPLAY_DC_PIN;
io_config.spi_mode = 0;
io_config.pclk_hz = DISPLAY_SPI_SCLK_HZ;
io_config.trans_queue_depth = 10;
io_config.lcd_cmd_bits = 8;
io_config.lcd_param_bits = 8;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &panel_io));
// 初始化液晶屏驱动芯片JD9853
ESP_LOGD(TAG, "Install LCD driver");
esp_lcd_panel_dev_config_t panel_config = {};
panel_config.reset_gpio_num = GPIO_NUM_NC;
panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB;
panel_config.bits_per_pixel = 16;
//ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
ESP_ERROR_CHECK(esp_lcd_new_panel_jd9853(panel_io, &panel_config, &panel));
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel));
ESP_ERROR_CHECK(esp_lcd_panel_init(panel));
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel, true));
ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel, 0, 34));
ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel, true, false));
ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(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_14_1,
.icon_font = &font_awesome_14_1,
.emoji_font = font_emoji_32_init(),
});
}
void InitializeButtons() {
boot_button_.OnClick([this]() {
auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
ResetWifiConfiguration();
}
app.ToggleChatState();
});
}
void InitializeCamera() {
camera_config_t config = {};
config.ledc_channel = LEDC_CHANNEL_2; // LEDC通道选择 用于生成XCLK时钟 但是S3不用
config.ledc_timer = LEDC_TIMER_2; // LEDC timer选择 用于生成XCLK时钟 但是S3不用
config.pin_d0 = CAMERA_PIN_D0;
config.pin_d1 = CAMERA_PIN_D1;
config.pin_d2 = CAMERA_PIN_D2;
config.pin_d3 = CAMERA_PIN_D3;
config.pin_d4 = CAMERA_PIN_D4;
config.pin_d5 = CAMERA_PIN_D5;
config.pin_d6 = CAMERA_PIN_D6;
config.pin_d7 = CAMERA_PIN_D7;
config.pin_xclk = CAMERA_PIN_XCLK;
config.pin_pclk = CAMERA_PIN_PCLK;
config.pin_vsync = CAMERA_PIN_VSYNC;
config.pin_href = CAMERA_PIN_HREF;
config.pin_sccb_sda = CAMERA_PIN_SIOD; // 这里如果写-1 表示使用已经初始化的I2C接口
config.pin_sccb_scl = CAMERA_PIN_SIOC;
config.sccb_i2c_port = 0; // 这里如果写1 默认使用I2C1
config.pin_pwdn = CAMERA_PIN_PWDN;
config.pin_reset = CAMERA_PIN_RESET;
config.xclk_freq_hz = XCLK_FREQ_HZ;
config.pixel_format = PIXFORMAT_RGB565;
config.frame_size = FRAMESIZE_QVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
config.fb_location = CAMERA_FB_IN_PSRAM;
config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
camera_ = new Esp32Camera(config);
camera_->SetVFlip(1);
}
public:
CustomBoard() :
boot_button_(BOOT_BUTTON_GPIO) {
InitializeI2c();
InitializeTca9555();
InitializeSpi();
InitializeButtons();
#ifdef LCD_TYPE_JD9853_SERIAL
InitializeJd9853Display();
#else
InitializeSt7789Display();
#endif
InitializeCamera();
GetBacklight()->RestoreBrightness();
}
virtual Led* GetLed() override {
static CircularStrip led(BUILTIN_LED_GPIO, 6);
return &led;
}
virtual AudioCodec* GetAudioCodec() override {
static BoxAudioCodec audio_codec(i2c_bus_, AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
AUDIO_I2S_GPIO_MCLK, AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN, AUDIO_CODEC_PA_PIN, AUDIO_CODEC_ES8311_ADDR, AUDIO_CODEC_ES7210_ADDR, AUDIO_INPUT_REFERENCE);
return &audio_codec;
}
virtual Display* GetDisplay() override {
return display_;
}
virtual Backlight* GetBacklight() override {
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, BACKLIGHT_INVERT);
return &backlight;
}
virtual Camera* GetCamera() override {
return camera_;
}
};
DECLARE_BOARD(CustomBoard);

View File

@@ -0,0 +1,460 @@
#include <stdio.h>
#include "esp_lcd_jd9853.h"
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdlib.h>
#include <sys/cdefs.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_lcd_panel_interface.h"
#include "esp_lcd_panel_io.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_commands.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "esp_check.h"
static const char *TAG = "JD9853";
static esp_err_t panel_jd9853_del(esp_lcd_panel_t *panel);
static esp_err_t panel_jd9853_reset(esp_lcd_panel_t *panel);
static esp_err_t panel_jd9853_init(esp_lcd_panel_t *panel);
static esp_err_t panel_jd9853_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data);
static esp_err_t panel_jd9853_invert_color(esp_lcd_panel_t *panel, bool invert_color_data);
static esp_err_t panel_jd9853_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y);
static esp_err_t panel_jd9853_swap_xy(esp_lcd_panel_t *panel, bool swap_axes);
static esp_err_t panel_jd9853_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap);
static esp_err_t panel_jd9853_disp_on_off(esp_lcd_panel_t *panel, bool off);
typedef struct
{
esp_lcd_panel_t base;
esp_lcd_panel_io_handle_t io;
int reset_gpio_num;
bool reset_level;
int x_gap;
int y_gap;
uint8_t fb_bits_per_pixel;
uint8_t madctl_val; // save current value of LCD_CMD_MADCTL register
uint8_t colmod_val; // save current value of LCD_CMD_COLMOD register
const jd9853_lcd_init_cmd_t *init_cmds;
uint16_t init_cmds_size;
} jd9853_panel_t;
esp_err_t esp_lcd_new_panel_jd9853(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel)
{
esp_err_t ret = ESP_OK;
jd9853_panel_t *jd9853 = NULL;
gpio_config_t io_conf = {0};
ESP_GOTO_ON_FALSE(io && panel_dev_config && ret_panel, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
jd9853 = (jd9853_panel_t *)calloc(1, sizeof(jd9853_panel_t));
ESP_GOTO_ON_FALSE(jd9853, ESP_ERR_NO_MEM, err, TAG, "no mem for jd9853 panel");
if (panel_dev_config->reset_gpio_num >= 0)
{
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = 1ULL << panel_dev_config->reset_gpio_num;
ESP_GOTO_ON_ERROR(gpio_config(&io_conf), err, TAG, "configure GPIO for RST line failed");
}
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
switch (panel_dev_config->color_space)
{
case ESP_LCD_COLOR_SPACE_RGB:
jd9853->madctl_val = 0;
break;
case ESP_LCD_COLOR_SPACE_BGR:
jd9853->madctl_val |= LCD_CMD_BGR_BIT;
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported color space");
break;
}
#else
switch (panel_dev_config->rgb_endian)
{
case LCD_RGB_ENDIAN_RGB:
jd9853->madctl_val = 0;
break;
case LCD_RGB_ENDIAN_BGR:
jd9853->madctl_val |= LCD_CMD_BGR_BIT;
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported rgb endian");
break;
}
#endif
switch (panel_dev_config->bits_per_pixel)
{
case 16: // RGB565
jd9853->colmod_val = 0x55;
jd9853->fb_bits_per_pixel = 16;
break;
case 18: // RGB666
jd9853->colmod_val = 0x66;
// each color component (R/G/B) should occupy the 6 high bits of a byte, which means 3 full bytes are required for a pixel
jd9853->fb_bits_per_pixel = 24;
break;
default:
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "unsupported pixel width");
break;
}
jd9853->io = io;
jd9853->reset_gpio_num = panel_dev_config->reset_gpio_num;
jd9853->reset_level = panel_dev_config->flags.reset_active_high;
if (panel_dev_config->vendor_config)
{
jd9853->init_cmds = ((jd9853_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds;
jd9853->init_cmds_size = ((jd9853_vendor_config_t *)panel_dev_config->vendor_config)->init_cmds_size;
}
jd9853->base.del = panel_jd9853_del;
jd9853->base.reset = panel_jd9853_reset;
jd9853->base.init = panel_jd9853_init;
jd9853->base.draw_bitmap = panel_jd9853_draw_bitmap;
jd9853->base.invert_color = panel_jd9853_invert_color;
jd9853->base.set_gap = panel_jd9853_set_gap;
jd9853->base.mirror = panel_jd9853_mirror;
jd9853->base.swap_xy = panel_jd9853_swap_xy;
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
jd9853->base.disp_off = panel_jd9853_disp_on_off;
#else
jd9853->base.disp_on_off = panel_jd9853_disp_on_off;
#endif
*ret_panel = &(jd9853->base);
ESP_LOGD(TAG, "new jd9853 panel @%p", jd9853);
// ESP_LOGI(TAG, "LCD panel create success, version: %d.%d.%d", ESP_LCD_jd9853_VER_MAJOR, ESP_LCD_jd9853_VER_MINOR,
// ESP_LCD_jd9853_VER_PATCH);
return ESP_OK;
err:
if (jd9853)
{
if (panel_dev_config->reset_gpio_num >= 0)
{
gpio_reset_pin(panel_dev_config->reset_gpio_num);
}
free(jd9853);
}
return ret;
}
static esp_err_t panel_jd9853_del(esp_lcd_panel_t *panel)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
if (jd9853->reset_gpio_num >= 0)
{
gpio_reset_pin(jd9853->reset_gpio_num);
}
ESP_LOGD(TAG, "del jd9853 panel @%p", jd9853);
free(jd9853);
return ESP_OK;
}
static esp_err_t panel_jd9853_reset(esp_lcd_panel_t *panel)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
esp_lcd_panel_io_handle_t io = jd9853->io;
// perform hardware reset
if (jd9853->reset_gpio_num >= 0)
{
gpio_set_level(jd9853->reset_gpio_num, jd9853->reset_level);
vTaskDelay(pdMS_TO_TICKS(10));
gpio_set_level(jd9853->reset_gpio_num, !jd9853->reset_level);
vTaskDelay(pdMS_TO_TICKS(10));
}
else
{ // perform software reset
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SWRESET, NULL, 0), TAG, "send command failed");
vTaskDelay(pdMS_TO_TICKS(20)); // spec, wait at least 5ms before sending new command
}
return ESP_OK;
}
typedef struct
{
uint8_t cmd;
uint8_t data[16];
uint8_t data_bytes; // Length of data in above data array; 0xFF = end of cmds.
} lcd_init_cmd_t;
// static const jd9853_lcd_init_cmd_t vendor_specific_init_default[] = {
// // {cmd, { data }, data_size, delay_ms}
// /* Power contorl B, power control = 0, DC_ENA = 1 */
// {0xCF, (uint8_t []){0x00, 0xAA, 0XE0}, 3, 0},
// /* Power on sequence control,
// * cp1 keeps 1 frame, 1st frame enable
// * vcl = 0, ddvdh=3, vgh=1, vgl=2
// * DDVDH_ENH=1
// */
// {0xED, (uint8_t []){0x67, 0x03, 0X12, 0X81}, 4, 0},
// /* Driver timing control A,
// * non-overlap=default +1
// * EQ=default - 1, CR=default
// * pre-charge=default - 1
// */
// {0xE8, (uint8_t []){0x8A, 0x01, 0x78}, 3, 0},
// /* Power control A, Vcore=1.6V, DDVDH=5.6V */
// {0xCB, (uint8_t []){0x39, 0x2C, 0x00, 0x34, 0x02}, 5, 0},
// /* Pump ratio control, DDVDH=2xVCl */
// {0xF7, (uint8_t []){0x20}, 1, 0},
// {0xF7, (uint8_t []){0x20}, 1, 0},
// /* Driver timing control, all=0 unit */
// {0xEA, (uint8_t []){0x00, 0x00}, 2, 0},
// /* Power control 1, GVDD=4.75V */
// {0xC0, (uint8_t []){0x23}, 1, 0},
// /* Power control 2, DDVDH=VCl*2, VGH=VCl*7, VGL=-VCl*3 */
// {0xC1, (uint8_t []){0x11}, 1, 0},
// /* VCOM control 1, VCOMH=4.025V, VCOML=-0.950V */
// {0xC5, (uint8_t []){0x43, 0x4C}, 2, 0},
// /* VCOM control 2, VCOMH=VMH-2, VCOML=VML-2 */
// {0xC7, (uint8_t []){0xA0}, 1, 0},
// /* Frame rate control, f=fosc, 70Hz fps */
// {0xB1, (uint8_t []){0x00, 0x1B}, 2, 0},
// /* Enable 3G, disabled */
// {0xF2, (uint8_t []){0x00}, 1, 0},
// /* Gamma set, curve 1 */
// {0x26, (uint8_t []){0x01}, 1, 0},
// /* Positive gamma correction */
// {0xE0, (uint8_t []){0x1F, 0x36, 0x36, 0x3A, 0x0C, 0x05, 0x4F, 0X87, 0x3C, 0x08, 0x11, 0x35, 0x19, 0x13, 0x00}, 15, 0},
// /* Negative gamma correction */
// {0xE1, (uint8_t []){0x00, 0x09, 0x09, 0x05, 0x13, 0x0A, 0x30, 0x78, 0x43, 0x07, 0x0E, 0x0A, 0x26, 0x2C, 0x1F}, 15, 0},
// /* Entry mode set, Low vol detect disabled, normal display */
// {0xB7, (uint8_t []){0x07}, 1, 0},
// /* Display function control */
// {0xB6, (uint8_t []){0x08, 0x82, 0x27}, 3, 0},
// };
static const jd9853_lcd_init_cmd_t vendor_specific_init_default[] = {
{0x11, (uint8_t []){ 0x00 }, 0, 120},
{0xDF, (uint8_t[]){0x98, 0x53}, 2, 0},
{0xDF, (uint8_t[]){0x98, 0x53}, 2, 0},
{0xB2, (uint8_t[]){0x23}, 1, 0},
{0xB7, (uint8_t[]){0x00, 0x47, 0x00, 0x6F}, 4, 0},
{0xBB, (uint8_t[]){0x1C, 0x1A, 0x55, 0x73, 0x63, 0xF0}, 6, 0},
{0xC0, (uint8_t[]){0x44, 0xA4}, 2, 0},
{0xC1, (uint8_t[]){0x16}, 1, 0},
{0xC3, (uint8_t[]){0x7D, 0x07, 0x14, 0x06, 0xCF, 0x71, 0x72, 0x77}, 8, 0},
{0xC4, (uint8_t[]){0x00, 0x00, 0xA0, 0x79, 0x0B, 0x0A, 0x16, 0x79, 0x0B, 0x0A, 0x16, 0x82}, 12, 0}, // 00=60Hz 06=57Hz 08=51Hz, LN=320 Line
{0xC8, (uint8_t[]){0x3F, 0x32, 0x29, 0x29, 0x27, 0x2B, 0x27, 0x28, 0x28, 0x26, 0x25, 0x17, 0x12, 0x0D, 0x04, 0x00, 0x3F, 0x32, 0x29, 0x29, 0x27, 0x2B, 0x27, 0x28, 0x28, 0x26, 0x25, 0x17, 0x12, 0x0D, 0x04, 0x00}, 32, 0}, // SET_R_GAMMA
{0xD0, (uint8_t[]){0x04, 0x06, 0x6B, 0x0F, 0x00}, 5, 0},
{0xD7, (uint8_t[]){0x00, 0x30}, 2, 0},
{0xE6, (uint8_t[]){0x14}, 1, 0},
{0xDE, (uint8_t[]){0x01}, 1, 0},
{0xB7, (uint8_t[]){0x03, 0x13, 0xEF, 0x35, 0x35}, 5, 0},
{0xC1, (uint8_t[]){0x14, 0x15, 0xC0}, 3, 0},
{0xC2, (uint8_t[]){0x06, 0x3A}, 2, 0},
{0xC4, (uint8_t[]){0x72, 0x12}, 2, 0},
{0xBE, (uint8_t[]){0x00}, 1, 0},
{0xDE, (uint8_t[]){0x02}, 1, 0},
{0xE5, (uint8_t[]){0x00, 0x02, 0x00}, 3, 0},
{0xE5, (uint8_t[]){0x01, 0x02, 0x00}, 3, 0},
{0xDE, (uint8_t[]){0x00}, 1, 0},
{0x35, (uint8_t[]){0x00}, 1, 0},
{0x3A, (uint8_t[]){0x05}, 1, 0}, // 06=RGB66605=RGB565
{0x2A, (uint8_t[]){0x00, 0x22, 0x00, 0xCD}, 4, 0}, // Start_X=34, End_X=205
{0x2B, (uint8_t[]){0x00, 0x00, 0x01, 0x3F}, 4, 0}, // Start_Y=0, End_Y=319
{0xDE, (uint8_t[]){0x02}, 1, 0},
{0xE5, (uint8_t[]){0x00, 0x02, 0x00}, 3, 0},
{0xDE, (uint8_t[]){0x00}, 1, 0},
{0x29, (uint8_t []){ 0x00 }, 0, 0},
};
static esp_err_t panel_jd9853_init(esp_lcd_panel_t *panel)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
esp_lcd_panel_io_handle_t io = jd9853->io;
// LCD goes into sleep mode and display will be turned off after power on reset, exit sleep mode first
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_SLPOUT, NULL, 0), TAG, "send command failed");
vTaskDelay(pdMS_TO_TICKS(100));
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){
jd9853->madctl_val,
},
1),
TAG, "send command failed");
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_COLMOD, (uint8_t[]){
jd9853->colmod_val,
},
1),
TAG, "send command failed");
const jd9853_lcd_init_cmd_t *init_cmds = NULL;
uint16_t init_cmds_size = 0;
if (jd9853->init_cmds)
{
init_cmds = jd9853->init_cmds;
init_cmds_size = jd9853->init_cmds_size;
}
else
{
init_cmds = vendor_specific_init_default;
init_cmds_size = sizeof(vendor_specific_init_default) / sizeof(jd9853_lcd_init_cmd_t);
}
bool is_cmd_overwritten = false;
for (int i = 0; i < init_cmds_size; i++)
{
// Check if the command has been used or conflicts with the internal
switch (init_cmds[i].cmd)
{
case LCD_CMD_MADCTL:
is_cmd_overwritten = true;
jd9853->madctl_val = ((uint8_t *)init_cmds[i].data)[0];
break;
case LCD_CMD_COLMOD:
is_cmd_overwritten = true;
jd9853->colmod_val = ((uint8_t *)init_cmds[i].data)[0];
break;
default:
is_cmd_overwritten = false;
break;
}
if (is_cmd_overwritten)
{
ESP_LOGW(TAG, "The %02Xh command has been used and will be overwritten by external initialization sequence", init_cmds[i].cmd);
}
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, init_cmds[i].cmd, init_cmds[i].data, init_cmds[i].data_bytes), TAG, "send command failed");
vTaskDelay(pdMS_TO_TICKS(init_cmds[i].delay_ms));
}
ESP_LOGD(TAG, "send init commands success");
return ESP_OK;
}
static esp_err_t panel_jd9853_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
assert((x_start < x_end) && (y_start < y_end) && "start position must be smaller than end position");
esp_lcd_panel_io_handle_t io = jd9853->io;
x_start += jd9853->x_gap;
x_end += jd9853->x_gap;
y_start += jd9853->y_gap;
y_end += jd9853->y_gap;
// define an area of frame memory where MCU can access
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_CASET, (uint8_t[]){
(x_start >> 8) & 0xFF,
x_start & 0xFF,
((x_end - 1) >> 8) & 0xFF,
(x_end - 1) & 0xFF,
},
4),
TAG, "send command failed");
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_RASET, (uint8_t[]){
(y_start >> 8) & 0xFF,
y_start & 0xFF,
((y_end - 1) >> 8) & 0xFF,
(y_end - 1) & 0xFF,
},
4),
TAG, "send command failed");
// transfer frame buffer
size_t len = (x_end - x_start) * (y_end - y_start) * jd9853->fb_bits_per_pixel / 8;
esp_lcd_panel_io_tx_color(io, LCD_CMD_RAMWR, color_data, len);
return ESP_OK;
}
static esp_err_t panel_jd9853_invert_color(esp_lcd_panel_t *panel, bool invert_color_data)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
esp_lcd_panel_io_handle_t io = jd9853->io;
int command = 0;
if (invert_color_data)
{
command = LCD_CMD_INVON;
}
else
{
command = LCD_CMD_INVOFF;
}
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
return ESP_OK;
}
static esp_err_t panel_jd9853_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
esp_lcd_panel_io_handle_t io = jd9853->io;
if (mirror_x)
{
jd9853->madctl_val |= LCD_CMD_MX_BIT;
}
else
{
jd9853->madctl_val &= ~LCD_CMD_MX_BIT;
}
if (mirror_y)
{
jd9853->madctl_val |= LCD_CMD_MY_BIT;
}
else
{
jd9853->madctl_val &= ~LCD_CMD_MY_BIT;
}
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){jd9853->madctl_val}, 1), TAG, "send command failed");
return ESP_OK;
}
static esp_err_t panel_jd9853_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
esp_lcd_panel_io_handle_t io = jd9853->io;
if (swap_axes)
{
jd9853->madctl_val |= LCD_CMD_MV_BIT;
}
else
{
jd9853->madctl_val &= ~LCD_CMD_MV_BIT;
}
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, LCD_CMD_MADCTL, (uint8_t[]){jd9853->madctl_val}, 1), TAG, "send command failed");
return ESP_OK;
}
static esp_err_t panel_jd9853_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
jd9853->x_gap = x_gap;
jd9853->y_gap = y_gap;
return ESP_OK;
}
static esp_err_t panel_jd9853_disp_on_off(esp_lcd_panel_t *panel, bool on_off)
{
jd9853_panel_t *jd9853 = __containerof(panel, jd9853_panel_t, base);
esp_lcd_panel_io_handle_t io = jd9853->io;
int command = 0;
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
on_off = !on_off;
#endif
if (on_off)
{
command = LCD_CMD_DISPON;
}
else
{
command = LCD_CMD_DISPOFF;
}
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_tx_param(io, command, NULL, 0), TAG, "send command failed");
return ESP_OK;
}

View File

@@ -0,0 +1,102 @@
/*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief ESP LCD: jd9853
*/
#pragma once
#include "hal/spi_ll.h"
#include "esp_lcd_panel_vendor.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief LCD panel initialization commands.
*
*/
typedef struct {
int cmd; /*<! The specific LCD command */
const void *data; /*<! Buffer that holds the command specific data */
size_t data_bytes; /*<! Size of `data` in memory, in bytes */
unsigned int delay_ms; /*<! Delay in milliseconds after this command */
} jd9853_lcd_init_cmd_t;
/**
* @brief LCD panel vendor configuration.
*
* @note This structure needs to be passed to the `vendor_config` field in `esp_lcd_panel_dev_config_t`.
*
*/
typedef struct {
const jd9853_lcd_init_cmd_t *init_cmds; /*!< Pointer to initialization commands array. Set to NULL if using default commands.
* The array should be declared as `static const` and positioned outside the function.
* Please refer to `vendor_specific_init_default` in source file.
*/
uint16_t init_cmds_size; /*<! Number of commands in above array */
} jd9853_vendor_config_t;
/**
* @brief Create LCD panel for model jd9853
*
* @note Vendor specific initialization can be different between manufacturers, should consult the LCD supplier for initialization sequence code.
*
* @param[in] io LCD panel IO handle
* @param[in] panel_dev_config general panel device configuration
* @param[out] ret_panel Returned LCD panel handle
* @return
* - ESP_ERR_INVALID_ARG if parameter is invalid
* - ESP_ERR_NO_MEM if out of memory
* - ESP_OK on success
*/
esp_err_t esp_lcd_new_panel_jd9853(const esp_lcd_panel_io_handle_t io, const esp_lcd_panel_dev_config_t *panel_dev_config, esp_lcd_panel_handle_t *ret_panel);
/**
* @brief LCD panel bus configuration structure
*
* @param[in] sclk SPI clock pin number
* @param[in] mosi SPI MOSI pin number
* @param[in] max_trans_sz Maximum transfer size in bytes
*
*/
#define JD9853_PANEL_BUS_SPI_CONFIG(sclk, mosi, max_trans_sz) \
{ \
.sclk_io_num = sclk, \
.mosi_io_num = mosi, \
.miso_io_num = -1, \
.quadhd_io_num = -1, \
.quadwp_io_num = -1, \
.max_transfer_sz = max_trans_sz, \
}
/**
* @brief LCD panel IO configuration structure
*
* @param[in] cs SPI chip select pin number
* @param[in] dc SPI data/command pin number
* @param[in] cb Callback function when SPI transfer is done
* @param[in] cb_ctx Callback function context
*
*/
#define JD9853_PANEL_IO_SPI_CONFIG(cs, dc, callback, callback_ctx) \
{ \
.cs_gpio_num = cs, \
.dc_gpio_num = dc, \
.spi_mode = 0, \
.pclk_hz = 40 * 1000 * 1000, \
.trans_queue_depth = 10, \
.on_color_trans_done = callback, \
.user_ctx = callback_ctx, \
.lcd_cmd_bits = 8, \
.lcd_param_bits = 8, \
}
#ifdef __cplusplus
}
#endif

View File

@@ -1,7 +1,6 @@
#include "wifi_board.h"
#include "display/lcd_display.h"
#include "esp_lcd_sh8601.h"
#include "font_awesome_symbols.h"
#include "codecs/box_audio_codec.h"
#include "application.h"

View File

@@ -1,7 +1,6 @@
#include "wifi_board.h"
#include "display/lcd_display.h"
#include "esp_lcd_sh8601.h"
#include "font_awesome_symbols.h"
#include "codecs/box_audio_codec.h"
#include "application.h"

View File

@@ -8,7 +8,6 @@
#include "settings.h"
#include "config.h"
#include "sleep_timer.h"
#include "font_awesome_symbols.h"
#include "adc_battery_monitor.h"
#include "press_to_talk_mcp_tool.h"

View File

@@ -8,7 +8,6 @@
#include "settings.h"
#include "config.h"
#include "power_save_timer.h"
#include "font_awesome_symbols.h"
#include "adc_battery_monitor.h"
#include "press_to_talk_mcp_tool.h"

View File

@@ -8,7 +8,6 @@
#include "settings.h"
#include "config.h"
#include "power_save_timer.h"
#include "font_awesome_symbols.h"
#include "press_to_talk_mcp_tool.h"
#include <wifi_station.h>

View File

@@ -3,11 +3,11 @@
#include <string>
#include <cstdlib>
#include <cstring>
#include <font_awesome.h>
#include "display.h"
#include "board.h"
#include "application.h"
#include "font_awesome_symbols.h"
#include "audio_codec.h"
#include "settings.h"
#include "assets/lang_config.h"
@@ -105,7 +105,7 @@ void Display::UpdateStatusBar(bool update_all) {
// 如果静音状态改变,则更新图标
if (codec->output_volume() == 0 && !muted_) {
muted_ = true;
lv_label_set_text(mute_label_, FONT_AWESOME_VOLUME_MUTE);
lv_label_set_text(mute_label_, FONT_AWESOME_VOLUME_XMARK);
} else if (codec->output_volume() > 0 && muted_) {
muted_ = false;
lv_label_set_text(mute_label_, "");
@@ -136,13 +136,13 @@ void Display::UpdateStatusBar(bool update_all) {
const char* icon = nullptr;
if (board.GetBatteryLevel(battery_level, charging, discharging)) {
if (charging) {
icon = FONT_AWESOME_BATTERY_CHARGING;
icon = FONT_AWESOME_BATTERY_BOLT;
} else {
const char* levels[] = {
FONT_AWESOME_BATTERY_EMPTY, // 0-19%
FONT_AWESOME_BATTERY_1, // 20-39%
FONT_AWESOME_BATTERY_2, // 40-59%
FONT_AWESOME_BATTERY_3, // 60-79%
FONT_AWESOME_BATTERY_QUARTER, // 20-39%
FONT_AWESOME_BATTERY_HALF, // 40-59%
FONT_AWESOME_BATTERY_THREE_QUARTERS, // 60-79%
FONT_AWESOME_BATTERY_FULL, // 80-99%
FONT_AWESOME_BATTERY_FULL, // 100%
};
@@ -196,50 +196,11 @@ void Display::UpdateStatusBar(bool update_all) {
void Display::SetEmotion(const char* emotion) {
struct Emotion {
const char* icon;
const char* text;
};
static const std::vector<Emotion> emotions = {
{FONT_AWESOME_EMOJI_NEUTRAL, "neutral"},
{FONT_AWESOME_EMOJI_HAPPY, "happy"},
{FONT_AWESOME_EMOJI_LAUGHING, "laughing"},
{FONT_AWESOME_EMOJI_FUNNY, "funny"},
{FONT_AWESOME_EMOJI_SAD, "sad"},
{FONT_AWESOME_EMOJI_ANGRY, "angry"},
{FONT_AWESOME_EMOJI_CRYING, "crying"},
{FONT_AWESOME_EMOJI_LOVING, "loving"},
{FONT_AWESOME_EMOJI_EMBARRASSED, "embarrassed"},
{FONT_AWESOME_EMOJI_SURPRISED, "surprised"},
{FONT_AWESOME_EMOJI_SHOCKED, "shocked"},
{FONT_AWESOME_EMOJI_THINKING, "thinking"},
{FONT_AWESOME_EMOJI_WINKING, "winking"},
{FONT_AWESOME_EMOJI_COOL, "cool"},
{FONT_AWESOME_EMOJI_RELAXED, "relaxed"},
{FONT_AWESOME_EMOJI_DELICIOUS, "delicious"},
{FONT_AWESOME_EMOJI_KISSY, "kissy"},
{FONT_AWESOME_EMOJI_CONFIDENT, "confident"},
{FONT_AWESOME_EMOJI_SLEEPY, "sleepy"},
{FONT_AWESOME_EMOJI_SILLY, "silly"},
{FONT_AWESOME_EMOJI_CONFUSED, "confused"}
};
// 查找匹配的表情
std::string_view emotion_view(emotion);
auto it = std::find_if(emotions.begin(), emotions.end(),
[&emotion_view](const Emotion& e) { return e.text == emotion_view; });
DisplayLockGuard lock(this);
if (emotion_label_ == nullptr) {
return;
}
// 如果找到匹配的表情就显示对应图标否则显示默认的neutral表情
if (it != emotions.end()) {
lv_label_set_text(emotion_label_, it->icon);
const char* utf8 = font_awesome_get_utf8(emotion);
if (utf8 != nullptr) {
SetIcon(utf8);
} else {
lv_label_set_text(emotion_label_, FONT_AWESOME_EMOJI_NEUTRAL);
SetIcon(FONT_AWESOME_NEUTRAL);
}
}

View File

@@ -1,15 +1,15 @@
#include "lcd_display.h"
#include "assets/lang_config.h"
#include "settings.h"
#include <vector>
#include <algorithm>
#include <font_awesome_symbols.h>
#include <font_awesome.h>
#include <esp_log.h>
#include <esp_err.h>
#include <esp_lvgl_port.h>
#include <esp_heap_caps.h>
#include "assets/lang_config.h"
#include <esp_psram.h>
#include <cstring>
#include "settings.h"
#include "board.h"
@@ -102,10 +102,21 @@ SpiLcdDisplay::SpiLcdDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_h
ESP_LOGI(TAG, "Initialize LVGL library");
lv_init();
#if CONFIG_SPIRAM
// lv image cache, currently only PNG is supported
size_t psram_size_mb = esp_psram_get_size() / 1024 / 1024;
if (psram_size_mb >= 8) {
lv_image_cache_resize(2 * 1024 * 1024, true);
ESP_LOGI(TAG, "Use 2MB of PSRAM for image cache");
} else if (psram_size_mb >= 2) {
lv_image_cache_resize(512 * 1024, true);
ESP_LOGI(TAG, "Use 512KB of PSRAM for image cache");
}
#endif
ESP_LOGI(TAG, "Initialize LVGL port");
lvgl_port_cfg_t port_cfg = ESP_LVGL_PORT_INIT_CONFIG();
port_cfg.task_priority = 1;
port_cfg.timer_period_ms = 40;
lvgl_port_init(&port_cfg);
ESP_LOGI(TAG, "Adding LCD display");
@@ -367,7 +378,7 @@ void LcdDisplay::SetupUI() {
emotion_label_ = lv_label_create(status_bar_);
lv_obj_set_style_text_font(emotion_label_, &font_awesome_30_4, 0);
lv_obj_set_style_text_color(emotion_label_, current_theme_.text, 0);
lv_label_set_text(emotion_label_, FONT_AWESOME_AI_CHIP);
lv_label_set_text(emotion_label_, FONT_AWESOME_MICROCHIP_AI);
lv_obj_set_style_margin_right(emotion_label_, 5, 0); // 添加右边距,与后面的元素分隔
notification_label_ = lv_label_create(status_bar_);
@@ -621,7 +632,7 @@ void LcdDisplay::SetPreviewImage(const lv_img_dsc_t* img_dsc) {
// 设置自定义属性标记气泡类型
lv_obj_set_user_data(img_bubble, (void*)"image");
// Create the image object inside the bubble
lv_obj_t* preview_image = lv_image_create(img_bubble);
@@ -744,7 +755,7 @@ void LcdDisplay::SetupUI() {
emotion_label_ = lv_label_create(content_);
lv_obj_set_style_text_font(emotion_label_, &font_awesome_30_4, 0);
lv_obj_set_style_text_color(emotion_label_, current_theme_.text, 0);
lv_label_set_text(emotion_label_, FONT_AWESOME_AI_CHIP);
lv_label_set_text(emotion_label_, FONT_AWESOME_MICROCHIP_AI);
preview_image_ = lv_image_create(content_);
lv_obj_set_size(preview_image_, width_ * 0.5, height_ * 0.5);
@@ -816,8 +827,10 @@ void LcdDisplay::SetPreviewImage(const lv_img_dsc_t* img_dsc) {
if (img_dsc != nullptr) {
// 设置图片源并显示预览图片
lv_image_set_src(preview_image_, img_dsc);
// zoom factor 0.5
lv_image_set_scale(preview_image_, 128 * width_ / img_dsc->header.w);
if (img_dsc->header.w > 0) {
// zoom factor 0.5
lv_image_set_scale(preview_image_, 128 * width_ / img_dsc->header.w);
}
lv_obj_remove_flag(preview_image_, LV_OBJ_FLAG_HIDDEN);
// 隐藏emotion_label_
if (emotion_label_ != nullptr) {
@@ -867,6 +880,13 @@ void LcdDisplay::SetEmotion(const char* emotion) {
std::string_view emotion_view(emotion);
auto it = std::find_if(emotions.begin(), emotions.end(),
[&emotion_view](const Emotion& e) { return e.text == emotion_view; });
if (fonts_.emoji_font == nullptr || it == emotions.end()) {
const char* utf8 = font_awesome_get_utf8(emotion);
if (utf8 != nullptr) {
SetIcon(utf8);
}
return;
}
DisplayLockGuard lock(this);
if (emotion_label_ == nullptr) {

View File

@@ -1,5 +1,4 @@
#include "oled_display.h"
#include "font_awesome_symbols.h"
#include "assets/lang_config.h"
#include <string>
@@ -8,6 +7,7 @@
#include <esp_log.h>
#include <esp_err.h>
#include <esp_lvgl_port.h>
#include <font_awesome.h>
#define TAG "OledDisplay"
@@ -23,7 +23,6 @@ OledDisplay::OledDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handl
lvgl_port_cfg_t port_cfg = ESP_LVGL_PORT_INIT_CONFIG();
port_cfg.task_priority = 1;
port_cfg.task_stack = 6144;
port_cfg.timer_period_ms = 40;
lvgl_port_init(&port_cfg);
ESP_LOGI(TAG, "Adding OLED display");
@@ -157,7 +156,7 @@ void OledDisplay::SetupUI_128x64() {
emotion_label_ = lv_label_create(content_left_);
lv_obj_set_style_text_font(emotion_label_, &font_awesome_30_1, 0);
lv_label_set_text(emotion_label_, FONT_AWESOME_AI_CHIP);
lv_label_set_text(emotion_label_, FONT_AWESOME_MICROCHIP_AI);
lv_obj_center(emotion_label_);
lv_obj_set_style_pad_top(emotion_label_, 8, 0);
@@ -249,7 +248,7 @@ void OledDisplay::SetupUI_128x32() {
emotion_label_ = lv_label_create(content_);
lv_obj_set_style_text_font(emotion_label_, &font_awesome_30_1, 0);
lv_label_set_text(emotion_label_, FONT_AWESOME_AI_CHIP);
lv_label_set_text(emotion_label_, FONT_AWESOME_MICROCHIP_AI);
lv_obj_center(emotion_label_);
/* Right side */

View File

@@ -13,10 +13,10 @@ dependencies:
espressif/esp_io_expander_tca9554: ==2.0.0
espressif/esp_lcd_panel_io_additions: ^1.0.1
78/esp_lcd_nv3023: ~1.0.0
78/esp-wifi-connect: ~2.5.1
78/esp-wifi-connect: ~2.5.2
78/esp-opus-encoder: ~2.4.1
78/esp-ml307: ~3.2.8
78/xiaozhi-fonts: ~1.4.0
78/esp-ml307: ~3.3.1
78/xiaozhi-fonts: ~1.5.2
espressif/led_strip: ~3.0.1
espressif/esp_codec_dev: ~1.4.0
espressif/esp-sr: ~2.1.5
@@ -32,7 +32,7 @@ dependencies:
esp_lvgl_port: ~2.6.0
espressif/esp_io_expander_tca95xx_16bit: ^2.0.0
espressif2022/image_player: ==1.1.0~1
espressif2022/esp_emote_gfx: ^1.0.0
espressif2022/esp_emote_gfx: ==1.0.0~2
espressif/adc_mic: ^0.2.1
espressif/esp_mmap_assets: '>=1.2'
txp666/otto-emoji-gif-component: ~1.0.2

View File

@@ -29,12 +29,17 @@ McpServer::~McpServer() {
}
void McpServer::AddCommonTools() {
// To speed up the response time, we add the common tools to the beginning of
// *Important* To speed up the response time, we add the common tools to the beginning of
// the tools list to utilize the prompt cache.
// **重要** 为了提升响应速度,我们把常用的工具放在前面,利用 prompt cache 的特性。
// Backup the original tools list and restore it after adding the common tools.
auto original_tools = std::move(tools_);
auto& board = Board::GetInstance();
// Do not add custom tools here.
// Custom tools must be added in the board's InitializeTools function.
AddTool("self.get_device_status",
"Provides the real-time information of the device, including the current status of the audio speaker, screen, battery, network, etc.\n"
"Use this tool for: \n"
@@ -122,6 +127,12 @@ void McpServer::AddTool(const std::string& name, const std::string& description,
AddTool(new McpTool(name, description, properties, callback));
}
void McpServer::AddUserOnlyTool(const std::string& name, const std::string& description, const PropertyList& properties, std::function<ReturnValue(const PropertyList&)> callback) {
auto tool = new McpTool(name, description, properties, callback);
tool->set_user_only(true);
AddTool(tool);
}
void McpServer::ParseMessage(const std::string& message) {
cJSON* json = cJSON_Parse(message.c_str());
if (json == nullptr) {

View File

@@ -177,6 +177,7 @@ private:
std::string description_;
PropertyList properties_;
std::function<ReturnValue(const PropertyList&)> callback_;
bool user_only_ = false;
public:
McpTool(const std::string& name,
@@ -188,9 +189,11 @@ public:
properties_(properties),
callback_(callback) {}
void set_user_only(bool user_only) { user_only_ = user_only; }
inline const std::string& name() const { return name_; }
inline const std::string& description() const { return description_; }
inline const PropertyList& properties() const { return properties_; }
inline bool user_only() const { return user_only_; }
std::string to_json() const {
std::vector<std::string> required = properties_.GetRequired();
@@ -214,6 +217,15 @@ public:
}
cJSON_AddItemToObject(json, "inputSchema", input_schema);
// Add audience annotation if the tool is user only (invisible to AI)
if (user_only_) {
cJSON *annotations = cJSON_CreateObject();
cJSON *audience = cJSON_CreateArray();
cJSON_AddItemToArray(audience, cJSON_CreateString("user"));
cJSON_AddItemToObject(annotations, "audience", audience);
cJSON_AddItemToObject(json, "annotations", annotations);
}
char *json_str = cJSON_PrintUnformatted(json);
std::string result(json_str);
@@ -259,6 +271,7 @@ public:
void AddCommonTools();
void AddTool(McpTool* tool);
void AddTool(const std::string& name, const std::string& description, const PropertyList& properties, std::function<ReturnValue(const PropertyList&)> callback);
void AddUserOnlyTool(const std::string& name, const std::string& description, const PropertyList& properties, std::function<ReturnValue(const PropertyList&)> callback);
void ParseMessage(const cJSON* json);
void ParseMessage(const std::string& message);

View File

@@ -12,11 +12,33 @@
MqttProtocol::MqttProtocol() {
event_group_handle_ = xEventGroupCreate();
// Initialize reconnect timer
esp_timer_create_args_t reconnect_timer_args = {
.callback = [](void* arg) {
MqttProtocol* protocol = (MqttProtocol*)arg;
auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateIdle) {
ESP_LOGI(TAG, "Reconnecting to MQTT server");
app.Schedule([protocol]() {
protocol->StartMqttClient(false);
});
}
},
.arg = this,
};
esp_timer_create(&reconnect_timer_args, &reconnect_timer_);
}
MqttProtocol::~MqttProtocol() {
ESP_LOGI(TAG, "MqttProtocol deinit");
vEventGroupDelete(event_group_handle_);
if (reconnect_timer_ != nullptr) {
esp_timer_stop(reconnect_timer_);
esp_timer_delete(reconnect_timer_);
}
if (event_group_handle_ != nullptr) {
vEventGroupDelete(event_group_handle_);
}
}
bool MqttProtocol::Start() {
@@ -50,7 +72,18 @@ bool MqttProtocol::StartMqttClient(bool report_error) {
mqtt_->SetKeepAlive(keepalive_interval);
mqtt_->OnDisconnected([this]() {
ESP_LOGI(TAG, "Disconnected from endpoint");
if (on_disconnected_ != nullptr) {
on_disconnected_();
}
ESP_LOGI(TAG, "MQTT disconnected, schedule reconnect in %d seconds", MQTT_RECONNECT_INTERVAL_MS / 1000);
esp_timer_start_once(reconnect_timer_, MQTT_RECONNECT_INTERVAL_MS * 1000);
});
mqtt_->OnConnected([this]() {
if (on_connected_ != nullptr) {
on_connected_();
}
esp_timer_stop(reconnect_timer_);
});
mqtt_->OnMessage([this](const std::string& topic, const std::string& payload) {

View File

@@ -9,6 +9,7 @@
#include <mbedtls/aes.h>
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
#include <esp_timer.h>
#include <functional>
#include <string>
@@ -16,7 +17,7 @@
#include <mutex>
#define MQTT_PING_INTERVAL_SECONDS 90
#define MQTT_RECONNECT_INTERVAL_MS 10000
#define MQTT_RECONNECT_INTERVAL_MS 60000
#define MQTT_PROTOCOL_SERVER_HELLO_EVENT (1 << 0)
@@ -45,6 +46,7 @@ private:
int udp_port_;
uint32_t local_sequence_;
uint32_t remote_sequence_;
esp_timer_handle_t reconnect_timer_;
bool StartMqttClient(bool report_error=false);
void ParseServerHello(const cJSON* root);

View File

@@ -24,6 +24,14 @@ void Protocol::OnNetworkError(std::function<void(const std::string& message)> ca
on_network_error_ = callback;
}
void Protocol::OnConnected(std::function<void()> callback) {
on_connected_ = callback;
}
void Protocol::OnDisconnected(std::function<void()> callback) {
on_disconnected_ = callback;
}
void Protocol::SetError(const std::string& message) {
error_occurred_ = true;
if (on_network_error_ != nullptr) {

View File

@@ -60,6 +60,8 @@ public:
void OnAudioChannelOpened(std::function<void()> callback);
void OnAudioChannelClosed(std::function<void()> callback);
void OnNetworkError(std::function<void(const std::string& message)> callback);
void OnConnected(std::function<void()> callback);
void OnDisconnected(std::function<void()> callback);
virtual bool Start() = 0;
virtual bool OpenAudioChannel() = 0;
@@ -78,6 +80,8 @@ protected:
std::function<void()> on_audio_channel_opened_;
std::function<void()> on_audio_channel_closed_;
std::function<void(const std::string& message)> on_network_error_;
std::function<void()> on_connected_;
std::function<void()> on_disconnected_;
int server_sample_rate_ = 24000;
int server_frame_duration_ = 60;

9
partitions/v2/16m.csv Normal file
View File

@@ -0,0 +1,9 @@
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000,
otadata, data, ota, 0xd000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000,
model, data, spiffs, 0x10000, 0xF0000,
ota_0, app, ota_0, 0x100000, 4M,
ota_1, app, ota_1, 0x500000, 4M,
assets, data, spiffs, 0x900000, 7M
1 # ESP-IDF Partition Table
2 # Name, Type, SubType, Offset, Size, Flags
3 nvs, data, nvs, 0x9000, 0x4000,
4 otadata, data, ota, 0xd000, 0x2000,
5 phy_init, data, phy, 0xf000, 0x1000,
6 model, data, spiffs, 0x10000, 0xF0000,
7 ota_0, app, ota_0, 0x100000, 4M,
8 ota_1, app, ota_1, 0x500000, 4M,
9 assets, data, spiffs, 0x900000, 7M

9
partitions/v2/16m_c3.csv Normal file
View File

@@ -0,0 +1,9 @@
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000,
otadata, data, ota, 0xd000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000,
model, data, spiffs, 0x10000, 0xF0000,
ota_0, app, ota_0, 0x100000, 4M,
ota_1, app, ota_1, 0x500000, 4M,
assets, data, spiffs, 0x900000, 4000K
1 # ESP-IDF Partition Table
2 # Name, Type, SubType, Offset, Size, Flags
3 nvs, data, nvs, 0x9000, 0x4000,
4 otadata, data, ota, 0xd000, 0x2000,
5 phy_init, data, phy, 0xf000, 0x1000,
6 model, data, spiffs, 0x10000, 0xF0000,
7 ota_0, app, ota_0, 0x100000, 4M,
8 ota_1, app, ota_1, 0x500000, 4M,
9 assets, data, spiffs, 0x900000, 4000K

10
partitions/v2/32m.csv Normal file
View File

@@ -0,0 +1,10 @@
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvsfactory, data, nvs, , 200K,
nvs, data, nvs, , 840K,
otadata, data, ota, , 0x2000,
phy_init, data, phy, , 0x1000,
model, data, spiffs, , 0xF0000,
ota_0, app, ota_0, 0x200000, 4M,
ota_1, app, ota_1, 0x600000, 4M,
assets, data, spiffs, 0xA00000, 16M
1 # ESP-IDF Partition Table
2 # Name, Type, SubType, Offset, Size, Flags
3 nvsfactory, data, nvs, , 200K,
4 nvs, data, nvs, , 840K,
5 otadata, data, ota, , 0x2000,
6 phy_init, data, phy, , 0x1000,
7 model, data, spiffs, , 0xF0000,
8 ota_0, app, ota_0, 0x200000, 4M,
9 ota_1, app, ota_1, 0x600000, 4M,
10 assets, data, spiffs, 0xA00000, 16M

9
partitions/v2/8m.csv Normal file
View File

@@ -0,0 +1,9 @@
# ESP-IDF Partition Table
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x4000,
otadata, data, ota, 0xd000, 0x2000,
phy_init, data, phy, 0xf000, 0x1000,
model, data, spiffs, 0x10000, 0xF0000,
ota_0, app, ota_0, 0x100000, 3M,
ota_1, app, ota_1, 0x400000, 3M,
assets, data, spiffs, 0x700000, 1M
1 # ESP-IDF Partition Table
2 # Name, Type, SubType, Offset, Size, Flags
3 nvs, data, nvs, 0x9000, 0x4000,
4 otadata, data, ota, 0xd000, 0x2000,
5 phy_init, data, phy, 0xf000, 0x1000,
6 model, data, spiffs, 0x10000, 0xF0000,
7 ota_0, app, ota_0, 0x100000, 3M,
8 ota_1, app, ota_1, 0x400000, 3M,
9 assets, data, spiffs, 0x700000, 1M

107
partitions/v2/README.md Normal file
View File

@@ -0,0 +1,107 @@
# Version 2 Partition Table
This version introduces significant improvements over v1 by adding an `assets` partition to support network-loadable content and optimizing partition layouts for different flash sizes.
## Key Changes from v1
### Major Improvements
1. **Added Assets Partition**: New `assets` partition for network-loadable content
2. **Replaced Model Partition**: The old `model` partition (960KB) is replaced with a larger `assets` partition
3. **Optimized App Partitions**: Reduced application partition sizes to accommodate assets
4. **Enhanced Flexibility**: Support for dynamic content updates without reflashing
### Assets Partition Features
The `assets` partition stores:
- **Wake word models**: Customizable wake word models that can be loaded from the network
- **Theme files**: Complete theming system including:
- Fonts (text and icon fonts)
- Audio effects and sound files
- Background images and UI elements
- Custom emoji packs
- Language configuration files
- **Dynamic Content**: All content can be updated over-the-air via HTTP downloads
## Partition Layout Comparison
### v1 Layout (16MB)
- `nvs`: 16KB (non-volatile storage)
- `otadata`: 8KB (OTA data)
- `phy_init`: 4KB (PHY initialization data)
- `model`: 960KB (model storage - fixed content)
- `ota_0`: 6MB (application partition 0)
- `ota_1`: 6MB (application partition 1)
### v2 Layout (16MB)
- `nvs`: 16KB (non-volatile storage)
- `otadata`: 8KB (OTA data)
- `phy_init`: 4KB (PHY initialization data)
- `ota_0`: 4MB (application partition 0)
- `ota_1`: 4MB (application partition 1)
- `assets`: 8MB (network-loadable assets)
## Available Configurations
### 8MB Flash Devices (`8m.csv`)
- `nvs`: 16KB
- `otadata`: 8KB
- `phy_init`: 4KB
- `ota_0`: 3MB
- `ota_1`: 3MB
- `assets`: 2MB
### 16MB Flash Devices (`16m.csv`) - Standard
- `nvs`: 16KB
- `otadata`: 8KB
- `phy_init`: 4KB
- `ota_0`: 4MB
- `ota_1`: 4MB
- `assets`: 8MB
### 16MB Flash Devices (`16m_c3.csv`) - ESP32-C3 Optimized
- `nvs`: 16KB
- `otadata`: 8KB
- `phy_init`: 4KB
- `ota_0`: 4MB
- `ota_1`: 4MB
- `assets`: 4MB (4000K - limited by available mmap pages)
### 32MB Flash Devices (`32m.csv`)
- `nvsfactory`: 200KB
- `nvs`: 840KB
- `otadata`: 8KB
- `phy_init`: 4KB
- `ota_0`: 4MB
- `ota_1`: 4MB
- `assets`: 16MB
## Benefits
1. **Dynamic Content Management**: Users can download and update wake word models, themes, and other assets without reflashing the device
2. **Reduced App Size**: Application partitions are optimized, allowing more space for dynamic content
3. **Enhanced Customization**: Support for custom themes, wake words, and language packs enhances user experience
4. **Network Flexibility**: Assets can be updated independently of the main application firmware
5. **Better Resource Utilization**: Efficient use of flash memory with configurable asset storage
6. **OTA Asset Updates**: Assets can be updated over-the-air via HTTP downloads
## Technical Details
- **Partition Type**: Assets partition uses `spiffs` subtype for SPIFFS filesystem compatibility
- **Memory Mapping**: Assets are memory-mapped for efficient access during runtime
- **Checksum Validation**: Built-in integrity checking ensures asset data validity
- **Progressive Download**: Assets can be downloaded progressively with progress tracking
- **Fallback Support**: Graceful fallback to default assets if network updates fail
## Migration from v1
When upgrading from v1 to v2:
1. **Backup Important Data**: Ensure any important data in the old `model` partition is backed up
2. **Flash New Partition Table**: Use the appropriate v2 partition table for your flash size
3. **Download Assets**: The device will automatically download required assets on first boot
4. **Verify Functionality**: Ensure all features work correctly with the new partition layout
## Usage Notes
- The `assets` partition size varies by configuration to optimize for different flash sizes
- ESP32-C3 devices use a smaller assets partition (4MB) due to limited available mmap pages in the system
- 32MB devices get the largest assets partition (16MB) for maximum content storage
- All partition tables maintain proper alignment for optimal flash performance

View File

@@ -39,6 +39,10 @@ CONFIG_UART_ISR_IN_IRAM=y
# Fix ESP_SSL error
CONFIG_MBEDTLS_SSL_RENEGOTIATION=n
# ESP32 Camera
CONFIG_CAMERA_NO_AFFINITY=y
CONFIG_CAMERA_DMA_BUFFER_SIZE_MAX=8192
# LVGL 9.2.2
CONFIG_LV_OS_NONE=y
@@ -49,6 +53,8 @@ CONFIG_LV_USE_CLIB_SPRINTF=y
CONFIG_LV_USE_IMGFONT=y
CONFIG_LV_USE_ASSERT_STYLE=y
CONFIG_LV_USE_GIF=y
CONFIG_LV_USE_LODEPNG=y
CONFIG_LV_USE_TJPGD=y
# Use compressed font
CONFIG_LV_FONT_FMT_TXT_LARGE=y

View File

@@ -13,7 +13,6 @@ CONFIG_SPIRAM_MEMTEST=n
CONFIG_MBEDTLS_EXTERNAL_MEM_ALLOC=y
CONFIG_ESP32S3_INSTRUCTION_CACHE_32KB=y
CONFIG_ESP32S3_DATA_CACHE_64KB=y
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
CONFIG_SR_WN_WN9_NIHAOXIAOZHI_TTS=y