mirror of
https://github.com/78/xiaozhi-esp32.git
synced 2026-01-14 09:17:20 +08:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3ced7709c6 | ||
|
|
5fd9e4273e | ||
|
|
b53b6a1943 | ||
|
|
38157aa180 | ||
|
|
1bacf40cd4 | ||
|
|
99aa15822b | ||
|
|
1ffc5190b6 | ||
|
|
73dbeb4b9a | ||
|
|
1e94e884b8 | ||
|
|
b35bf0c344 |
49
.github/workflows/build.yml
vendored
49
.github/workflows/build.yml
vendored
@ -14,10 +14,10 @@ permissions:
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
name: Determine boards to build
|
||||
name: Determine variants to build
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
boards: ${{ steps.select.outputs.boards }}
|
||||
variants: ${{ steps.select.outputs.variants }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@ -28,30 +28,30 @@ jobs:
|
||||
run: sudo apt-get update && sudo apt-get install -y jq
|
||||
|
||||
- id: list
|
||||
name: Get all board list
|
||||
name: Get all variant list
|
||||
run: |
|
||||
echo "all_boards=$(python scripts/release.py --list-boards --json)" >> $GITHUB_OUTPUT
|
||||
echo "all_variants=$(python scripts/release.py --list-boards --json)" >> $GITHUB_OUTPUT
|
||||
|
||||
- id: select
|
||||
name: Select boards based on changes
|
||||
name: Select variants based on changes
|
||||
env:
|
||||
ALL_BOARDS: ${{ steps.list.outputs.all_boards }}
|
||||
ALL_VARIANTS: ${{ steps.list.outputs.all_variants }}
|
||||
run: |
|
||||
EVENT_NAME="${{ github.event_name }}"
|
||||
|
||||
# For push to main branch, build all boards
|
||||
# push 到 main 分支,编译全部变体
|
||||
if [[ "$EVENT_NAME" == "push" ]]; then
|
||||
echo "boards=$ALL_BOARDS" >> $GITHUB_OUTPUT
|
||||
echo "variants=$ALL_VARIANTS" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# For pull_request
|
||||
# pull_request 场景
|
||||
BASE_SHA="${{ github.event.pull_request.base.sha }}"
|
||||
HEAD_SHA="${{ github.event.pull_request.head.sha }}"
|
||||
echo "Base: $BASE_SHA, Head: $HEAD_SHA"
|
||||
|
||||
CHANGED=$(git diff --name-only $BASE_SHA $HEAD_SHA || true)
|
||||
echo "Changed files:\n$CHANGED"
|
||||
echo -e "Changed files:\n$CHANGED"
|
||||
|
||||
NEED_ALL=0
|
||||
declare -A AFFECTED
|
||||
@ -60,6 +60,10 @@ jobs:
|
||||
NEED_ALL=1
|
||||
fi
|
||||
|
||||
if [[ "$file" == main/boards/common/* ]]; then
|
||||
NEED_ALL=1
|
||||
fi
|
||||
|
||||
if [[ "$file" == main/boards/* ]]; then
|
||||
board=$(echo "$file" | cut -d '/' -f3)
|
||||
AFFECTED[$board]=1
|
||||
@ -67,24 +71,25 @@ jobs:
|
||||
done <<< "$CHANGED"
|
||||
|
||||
if [[ "$NEED_ALL" -eq 1 ]]; then
|
||||
echo "boards=$ALL_BOARDS" >> $GITHUB_OUTPUT
|
||||
echo "variants=$ALL_VARIANTS" >> $GITHUB_OUTPUT
|
||||
else
|
||||
if [[ ${#AFFECTED[@]} -eq 0 ]]; then
|
||||
echo "boards=[]" >> $GITHUB_OUTPUT
|
||||
echo "variants=[]" >> $GITHUB_OUTPUT
|
||||
else
|
||||
JSON=$(printf '%s\n' "${!AFFECTED[@]}" | sort -u | jq -R -s -c 'split("\n")[:-1]')
|
||||
echo "boards=$JSON" >> $GITHUB_OUTPUT
|
||||
BOARDS_JSON=$(printf '%s\n' "${!AFFECTED[@]}" | sort -u | jq -R -s -c 'split("\n")[:-1]')
|
||||
FILTERED=$(echo "$ALL_VARIANTS" | jq -c --argjson boards "$BOARDS_JSON" 'map(select(.board as $b | $boards | index($b)))')
|
||||
echo "variants=$FILTERED" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
fi
|
||||
|
||||
build:
|
||||
name: Build ${{ matrix.board }}
|
||||
name: Build ${{ matrix.name }}
|
||||
needs: prepare
|
||||
if: ${{ needs.prepare.outputs.boards != '[]' }}
|
||||
if: ${{ needs.prepare.outputs.variants != '[]' }}
|
||||
strategy:
|
||||
fail-fast: false # 单个 board 失败不影响其它 board
|
||||
fail-fast: false # 单个变体失败不影响其它变体
|
||||
matrix:
|
||||
board: ${{ fromJson(needs.prepare.outputs.boards) }}
|
||||
include: ${{ fromJson(needs.prepare.outputs.variants) }}
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: espressif/idf:release-v5.4
|
||||
@ -92,15 +97,15 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build current board
|
||||
- name: Build current variant
|
||||
shell: bash
|
||||
run: |
|
||||
source $IDF_PATH/export.sh
|
||||
python scripts/release.py ${{ matrix.board }}
|
||||
python scripts/release.py ${{ matrix.board }} --name ${{ matrix.name }}
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: xiaozhi_${{ matrix.board }}_${{ github.sha }}.bin
|
||||
name: xiaozhi_${{ matrix.name }}_${{ github.sha }}.bin
|
||||
path: build/merged-binary.bin
|
||||
if-no-files-found: error
|
||||
if-no-files-found: error
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(PROJECT_VER "1.9.0")
|
||||
set(PROJECT_VER "1.9.4")
|
||||
|
||||
# Add this line to disable the specific warning
|
||||
add_compile_options(-Wno-missing-field-initializers)
|
||||
|
||||
@ -428,24 +428,24 @@ 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 LCD_JD9853
|
||||
default AUDIO_BOARD_LCD_JD9853
|
||||
help
|
||||
屏幕类型选择
|
||||
config LCD_JD9853
|
||||
config AUDIO_BOARD_LCD_JD9853
|
||||
bool "JD9853, 分辨率320*172"
|
||||
config LCD_ST7789
|
||||
config AUDIO_BOARD_LCD_ST7789
|
||||
bool "ST7789, 分辨率240*320"
|
||||
endchoice
|
||||
|
||||
@ -508,7 +508,10 @@ config USE_AUDIO_PROCESSOR
|
||||
config USE_DEVICE_AEC
|
||||
bool "Enable Device-Side AEC"
|
||||
default n
|
||||
depends on USE_AUDIO_PROCESSOR && (BOARD_TYPE_ESP_BOX_3 || BOARD_TYPE_ESP_BOX || BOARD_TYPE_ESP_BOX_LITE || BOARD_TYPE_LICHUANG_DEV || BOARD_TYPE_ESP32S3_KORVO2_V3 || BOARD_TYPE_ESP32S3_Touch_AMOLED_1_75 || BOARD_TYPE_ESP32S3_Touch_AMOLED_2_06 || BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_4B || BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_XC || BOARD_TYPE_ESP_S3_LCD_EV_Board_2)
|
||||
depends on USE_AUDIO_PROCESSOR && (BOARD_TYPE_ESP_BOX_3 || BOARD_TYPE_ESP_BOX || BOARD_TYPE_ESP_BOX_LITE || BOARD_TYPE_LICHUANG_DEV \
|
||||
|| BOARD_TYPE_ESP32S3_KORVO2_V3 || BOARD_TYPE_ESP32S3_Touch_AMOLED_1_75 || BOARD_TYPE_ESP32S3_Touch_AMOLED_2_06 \
|
||||
|| BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_4B || BOARD_TYPE_ESP32P4_WIFI6_Touch_LCD_XC || BOARD_TYPE_ESP_S3_LCD_EV_Board_2 \
|
||||
|| BOARD_TYPE_ECHOEAR)
|
||||
help
|
||||
因为性能不够,不建议和微信聊天界面风格同时开启
|
||||
|
||||
|
||||
@ -332,6 +332,9 @@ void Application::Start() {
|
||||
/* Setup the display */
|
||||
auto display = board.GetDisplay();
|
||||
|
||||
// Print board name/version info
|
||||
display->SetChatMessage("system", SystemInfo::GetUserAgent().c_str());
|
||||
|
||||
/* Setup the audio service */
|
||||
auto codec = board.GetAudioCodec();
|
||||
audio_service_.Initialize(codec);
|
||||
@ -349,6 +352,12 @@ void Application::Start() {
|
||||
};
|
||||
audio_service_.SetCallbacks(callbacks);
|
||||
|
||||
// Start the main event loop task with priority 3
|
||||
xTaskCreate([](void* arg) {
|
||||
((Application*)arg)->MainEventLoop();
|
||||
vTaskDelete(NULL);
|
||||
}, "main_event_loop", 2048 * 4, this, 3, &main_event_loop_task_handle_);
|
||||
|
||||
/* Start the clock timer to update the status bar */
|
||||
esp_timer_start_periodic(clock_timer_handle_, 1000000);
|
||||
|
||||
@ -523,9 +532,6 @@ void Application::Schedule(std::function<void()> callback) {
|
||||
// If other tasks need to access the websocket or chat state,
|
||||
// they should use Schedule to call this function
|
||||
void Application::MainEventLoop() {
|
||||
// Raise the priority of the main event loop to avoid being interrupted by background tasks (which has priority 2)
|
||||
vTaskPrioritySet(NULL, 3);
|
||||
|
||||
while (true) {
|
||||
auto bits = xEventGroupWaitBits(event_group_, MAIN_EVENT_SCHEDULE |
|
||||
MAIN_EVENT_SEND_AUDIO |
|
||||
@ -737,11 +743,20 @@ bool Application::CanEnterSleepMode() {
|
||||
}
|
||||
|
||||
void Application::SendMcpMessage(const std::string& payload) {
|
||||
Schedule([this, payload]() {
|
||||
if (protocol_) {
|
||||
if (protocol_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure you are using main thread to send MCP message
|
||||
if (xTaskGetCurrentTaskHandle() == main_event_loop_task_handle_) {
|
||||
ESP_LOGI(TAG, "Send MCP message in main thread");
|
||||
protocol_->SendMcpMessage(payload);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Send MCP message in sub thread");
|
||||
Schedule([this, payload = std::move(payload)]() {
|
||||
protocol_->SendMcpMessage(payload);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Application::SetAecMode(AecMode mode) {
|
||||
|
||||
@ -82,6 +82,7 @@ private:
|
||||
bool aborted_ = false;
|
||||
int clock_ticks_ = 0;
|
||||
TaskHandle_t check_new_version_task_handle_ = nullptr;
|
||||
TaskHandle_t main_event_loop_task_handle_ = nullptr;
|
||||
|
||||
void OnWakeWordDetected();
|
||||
void CheckNewVersion(Ota& ota);
|
||||
@ -89,4 +90,19 @@ private:
|
||||
void SetListeningMode(ListeningMode mode);
|
||||
};
|
||||
|
||||
|
||||
class TaskPriorityReset {
|
||||
public:
|
||||
TaskPriorityReset(BaseType_t priority) {
|
||||
original_priority_ = uxTaskPriorityGet(NULL);
|
||||
vTaskPrioritySet(NULL, priority);
|
||||
}
|
||||
~TaskPriorityReset() {
|
||||
vTaskPrioritySet(NULL, original_priority_);
|
||||
}
|
||||
|
||||
private:
|
||||
BaseType_t original_priority_;
|
||||
};
|
||||
|
||||
#endif // _APPLICATION_H_
|
||||
|
||||
@ -100,11 +100,11 @@ void AudioService::Start() {
|
||||
|
||||
#if CONFIG_USE_AUDIO_PROCESSOR
|
||||
/* Start the audio input task */
|
||||
xTaskCreatePinnedToCore([](void* arg) {
|
||||
xTaskCreate([](void* arg) {
|
||||
AudioService* audio_service = (AudioService*)arg;
|
||||
audio_service->AudioInputTask();
|
||||
vTaskDelete(NULL);
|
||||
}, "audio_input", 2048 * 3, this, 8, &audio_input_task_handle_, 1);
|
||||
}, "audio_input", 2048 * 3, this, 8, &audio_input_task_handle_);
|
||||
|
||||
/* Start the audio output task */
|
||||
xTaskCreate([](void* arg) {
|
||||
|
||||
@ -3,32 +3,42 @@
|
||||
AdcBatteryMonitor::AdcBatteryMonitor(adc_unit_t adc_unit, adc_channel_t adc_channel, float upper_resistor, float lower_resistor, gpio_num_t charging_pin)
|
||||
: charging_pin_(charging_pin) {
|
||||
|
||||
// Initialize charging pin
|
||||
gpio_config_t gpio_cfg = {
|
||||
.pin_bit_mask = 1ULL << charging_pin,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
ESP_ERROR_CHECK(gpio_config(&gpio_cfg));
|
||||
// Initialize charging pin (only if it's not NC)
|
||||
if (charging_pin_ != GPIO_NUM_NC) {
|
||||
gpio_config_t gpio_cfg = {
|
||||
.pin_bit_mask = 1ULL << charging_pin,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
ESP_ERROR_CHECK(gpio_config(&gpio_cfg));
|
||||
}
|
||||
|
||||
// Initialize ADC battery estimation
|
||||
adc_battery_estimation_t adc_cfg = {
|
||||
.internal = {
|
||||
.adc_unit = adc_unit,
|
||||
.adc_bitwidth = ADC_BITWIDTH_12,
|
||||
.adc_bitwidth = ADC_BITWIDTH_DEFAULT,
|
||||
.adc_atten = ADC_ATTEN_DB_12,
|
||||
},
|
||||
.adc_channel = adc_channel,
|
||||
.upper_resistor = upper_resistor,
|
||||
.lower_resistor = lower_resistor
|
||||
};
|
||||
adc_cfg.charging_detect_cb = [](void *user_data) -> bool {
|
||||
AdcBatteryMonitor *self = (AdcBatteryMonitor *)user_data;
|
||||
return gpio_get_level(self->charging_pin_) == 1;
|
||||
};
|
||||
adc_cfg.charging_detect_user_data = this;
|
||||
|
||||
// 在ADC配置部分进行条件设置
|
||||
if (charging_pin_ != GPIO_NUM_NC) {
|
||||
adc_cfg.charging_detect_cb = [](void *user_data) -> bool {
|
||||
AdcBatteryMonitor *self = (AdcBatteryMonitor *)user_data;
|
||||
return gpio_get_level(self->charging_pin_) == 1;
|
||||
};
|
||||
adc_cfg.charging_detect_user_data = this;
|
||||
} else {
|
||||
// 不设置回调,让adc_battery_estimation库使用软件估算
|
||||
adc_cfg.charging_detect_cb = nullptr;
|
||||
adc_cfg.charging_detect_user_data = nullptr;
|
||||
}
|
||||
adc_battery_estimation_handle_ = adc_battery_estimation_create(&adc_cfg);
|
||||
|
||||
// Initialize timer
|
||||
@ -48,12 +58,29 @@ AdcBatteryMonitor::~AdcBatteryMonitor() {
|
||||
if (adc_battery_estimation_handle_) {
|
||||
ESP_ERROR_CHECK(adc_battery_estimation_destroy(adc_battery_estimation_handle_));
|
||||
}
|
||||
|
||||
if (timer_handle_) {
|
||||
esp_timer_stop(timer_handle_);
|
||||
esp_timer_delete(timer_handle_);
|
||||
}
|
||||
}
|
||||
|
||||
bool AdcBatteryMonitor::IsCharging() {
|
||||
bool is_charging = false;
|
||||
ESP_ERROR_CHECK(adc_battery_estimation_get_charging_state(adc_battery_estimation_handle_, &is_charging));
|
||||
return is_charging;
|
||||
// 优先使用adc_battery_estimation库的功能
|
||||
if (adc_battery_estimation_handle_ != nullptr) {
|
||||
bool is_charging = false;
|
||||
esp_err_t err = adc_battery_estimation_get_charging_state(adc_battery_estimation_handle_, &is_charging);
|
||||
if (err == ESP_OK) {
|
||||
return is_charging;
|
||||
}
|
||||
}
|
||||
|
||||
// 回退到GPIO读取或返回默认值
|
||||
if (charging_pin_ != GPIO_NUM_NC) {
|
||||
return gpio_get_level(charging_pin_) == 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AdcBatteryMonitor::IsDischarging() {
|
||||
@ -61,9 +88,17 @@ bool AdcBatteryMonitor::IsDischarging() {
|
||||
}
|
||||
|
||||
uint8_t AdcBatteryMonitor::GetBatteryLevel() {
|
||||
// 如果句柄无效,返回默认值
|
||||
if (adc_battery_estimation_handle_ == nullptr) {
|
||||
return 100;
|
||||
}
|
||||
|
||||
float capacity = 0;
|
||||
ESP_ERROR_CHECK(adc_battery_estimation_get_capacity(adc_battery_estimation_handle_, &capacity));
|
||||
return capacity;
|
||||
esp_err_t err = adc_battery_estimation_get_capacity(adc_battery_estimation_handle_, &capacity);
|
||||
if (err != ESP_OK) {
|
||||
return 100; // 出错时返回默认值
|
||||
}
|
||||
return (uint8_t)capacity;
|
||||
}
|
||||
|
||||
void AdcBatteryMonitor::OnChargingStatusChanged(std::function<void(bool)> callback) {
|
||||
|
||||
@ -519,12 +519,13 @@ private:
|
||||
|
||||
void InitializeSpi()
|
||||
{
|
||||
const spi_bus_config_t bus_config = TAIJIPI_ST77916_PANEL_BUS_QSPI_CONFIG(QSPI_PIN_NUM_LCD_PCLK,
|
||||
QSPI_PIN_NUM_LCD_DATA0,
|
||||
QSPI_PIN_NUM_LCD_DATA1,
|
||||
QSPI_PIN_NUM_LCD_DATA2,
|
||||
QSPI_PIN_NUM_LCD_DATA3,
|
||||
QSPI_LCD_H_RES * 80 * sizeof(uint16_t));
|
||||
spi_bus_config_t bus_config = TAIJIPI_ST77916_PANEL_BUS_QSPI_CONFIG(QSPI_PIN_NUM_LCD_PCLK,
|
||||
QSPI_PIN_NUM_LCD_DATA0,
|
||||
QSPI_PIN_NUM_LCD_DATA1,
|
||||
QSPI_PIN_NUM_LCD_DATA2,
|
||||
QSPI_PIN_NUM_LCD_DATA3,
|
||||
QSPI_LCD_H_RES * 80 * sizeof(uint16_t));
|
||||
// bus_config.isr_cpu_id = ESP_INTR_CPU_AFFINITY_1;
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(QSPI_LCD_HOST, &bus_config, SPI_DMA_CH_AUTO));
|
||||
}
|
||||
|
||||
@ -562,11 +563,7 @@ private:
|
||||
|
||||
#if USE_LVGL_DEFAULT
|
||||
display_ = new SpiLcdDisplay(panel_io, panel,
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, {
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
.emoji_font = font_emoji_64_init(),
|
||||
});
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY);
|
||||
#else
|
||||
display_ = new anim::EmoteDisplay(panel, panel_io);
|
||||
#endif
|
||||
|
||||
@ -111,8 +111,8 @@ static void InitializeGraphics(esp_lcd_panel_handle_t panel, gfx_handle_t* engin
|
||||
};
|
||||
|
||||
gfx_cfg.task.task_stack_caps = MALLOC_CAP_DEFAULT;
|
||||
gfx_cfg.task.task_affinity = 0;
|
||||
gfx_cfg.task.task_priority = 5;
|
||||
gfx_cfg.task.task_affinity = 1;
|
||||
gfx_cfg.task.task_priority = 1;
|
||||
gfx_cfg.task.task_stack = 20 * 1024;
|
||||
|
||||
*engine_handle = gfx_emote_init(&gfx_cfg);
|
||||
@ -303,6 +303,7 @@ bool EmoteEngine::OnFlushIoReady(esp_lcd_panel_io_handle_t panel_io,
|
||||
esp_lcd_panel_io_event_data_t* edata,
|
||||
void* user_ctx)
|
||||
{
|
||||
gfx_emote_flush_ready(user_ctx, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -313,7 +314,6 @@ void EmoteEngine::OnFlush(gfx_handle_t handle, int x_start, int y_start,
|
||||
if (panel) {
|
||||
esp_lcd_panel_draw_bitmap(panel, x_start, y_start, x_end, y_end, color_data);
|
||||
}
|
||||
gfx_emote_flush_ready(handle, true);
|
||||
}
|
||||
|
||||
// EmoteDisplay implementation
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include "adc_pdm_audio_codec.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <esp_timer.h>
|
||||
#include <driver/i2c.h>
|
||||
#include <driver/i2c_master.h>
|
||||
#include <driver/i2s_tdm.h>
|
||||
@ -11,6 +12,7 @@
|
||||
#include "hal/rtc_io_hal.h"
|
||||
#include "hal/gpio_ll.h"
|
||||
#include "settings.h"
|
||||
#include "config.h"
|
||||
|
||||
static const char TAG[] = "AdcPdmAudioCodec";
|
||||
|
||||
@ -71,7 +73,7 @@ AdcPdmAudioCodec::AdcPdmAudioCodec(int input_sample_rate, int output_sample_rate
|
||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle_, NULL));
|
||||
|
||||
i2s_pdm_tx_config_t pdm_cfg_default = BSP_I2S_DUPLEX_MONO_CFG((uint32_t)output_sample_rate, pdm_speak_p);
|
||||
pdm_cfg_default.clk_cfg.up_sample_fs = output_sample_rate / 100;
|
||||
pdm_cfg_default.clk_cfg.up_sample_fs = AUDIO_PDM_UPSAMPLE_FS;
|
||||
pdm_cfg_default.slot_cfg.sd_scale = I2S_PDM_SIG_SCALING_MUL_4;
|
||||
pdm_cfg_default.slot_cfg.hp_scale = I2S_PDM_SIG_SCALING_MUL_4;
|
||||
pdm_cfg_default.slot_cfg.lp_scale = I2S_PDM_SIG_SCALING_MUL_4;
|
||||
@ -112,10 +114,27 @@ AdcPdmAudioCodec::AdcPdmAudioCodec(int input_sample_rate, int output_sample_rate
|
||||
esp_rom_gpio_connect_out_signal(pdm_speak_n, I2SO_SD_OUT_IDX, 1, 0); //反转输出 SD OUT 信号
|
||||
gpio_set_drive_capability(pdm_speak_n, GPIO_DRIVE_CAP_0);
|
||||
}
|
||||
|
||||
// 初始化输出定时器
|
||||
esp_timer_create_args_t output_timer_args = {
|
||||
.callback = &AdcPdmAudioCodec::OutputTimerCallback,
|
||||
.arg = this,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "output_timer"
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_timer_create(&output_timer_args, &output_timer_));
|
||||
|
||||
ESP_LOGI(TAG, "AdcPdmAudioCodec initialized");
|
||||
}
|
||||
|
||||
AdcPdmAudioCodec::~AdcPdmAudioCodec() {
|
||||
// 删除定时器
|
||||
if (output_timer_) {
|
||||
esp_timer_stop(output_timer_);
|
||||
esp_timer_delete(output_timer_);
|
||||
output_timer_ = nullptr;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(esp_codec_dev_close(output_dev_));
|
||||
esp_codec_dev_delete(output_dev_);
|
||||
ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_));
|
||||
@ -161,11 +180,27 @@ void AdcPdmAudioCodec::EnableOutput(bool enable) {
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_codec_dev_open(output_dev_, &fs));
|
||||
ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(output_dev_, output_volume_));
|
||||
|
||||
// 强制按板卡配置重配PDM TX时钟,覆盖第三方库在set_fmt中的默认up_sample_fs
|
||||
// 若通道已启用,先禁用再重配,最后再启用
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(i2s_channel_disable(tx_handle_));
|
||||
i2s_pdm_tx_clk_config_t clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG((uint32_t)output_sample_rate_);
|
||||
clk_cfg.up_sample_fs = AUDIO_PDM_UPSAMPLE_FS;
|
||||
ESP_ERROR_CHECK(i2s_channel_reconfig_pdm_tx_clock(tx_handle_, &clk_cfg));
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
||||
if(pa_ctrl_pin_ != GPIO_NUM_NC){
|
||||
gpio_set_level(pa_ctrl_pin_, 1);
|
||||
}
|
||||
// 启用输出时启动定时器
|
||||
if (output_timer_) {
|
||||
esp_timer_start_once(output_timer_, TIMER_TIMEOUT_US);
|
||||
}
|
||||
|
||||
} else {
|
||||
// 禁用输出时停止定时器
|
||||
if (output_timer_) {
|
||||
esp_timer_stop(output_timer_);
|
||||
}
|
||||
if(pa_ctrl_pin_ != GPIO_NUM_NC){
|
||||
gpio_set_level(pa_ctrl_pin_, 0);
|
||||
}
|
||||
@ -183,6 +218,11 @@ int AdcPdmAudioCodec::Read(int16_t* dest, int samples) {
|
||||
int AdcPdmAudioCodec::Write(const int16_t* data, int samples) {
|
||||
if (output_enabled_) {
|
||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_codec_dev_write(output_dev_, (void*)data, samples * sizeof(int16_t)));
|
||||
// 重置输出定时器
|
||||
if (output_timer_) {
|
||||
esp_timer_stop(output_timer_);
|
||||
esp_timer_start_once(output_timer_, TIMER_TIMEOUT_US);
|
||||
}
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
@ -195,9 +235,15 @@ void AdcPdmAudioCodec::Start() {
|
||||
output_volume_ = 10;
|
||||
}
|
||||
|
||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
||||
|
||||
EnableInput(true);
|
||||
EnableOutput(true);
|
||||
ESP_LOGI(TAG, "Audio codec started");
|
||||
}
|
||||
|
||||
// 定时器回调函数实现
|
||||
void AdcPdmAudioCodec::OutputTimerCallback(void* arg) {
|
||||
AdcPdmAudioCodec* codec = static_cast<AdcPdmAudioCodec*>(arg);
|
||||
if (codec && codec->output_enabled_) {
|
||||
codec->EnableOutput(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
#include <esp_codec_dev.h>
|
||||
#include <esp_codec_dev_defaults.h>
|
||||
#include <esp_timer.h>
|
||||
|
||||
class AdcPdmAudioCodec : public AudioCodec {
|
||||
private:
|
||||
@ -12,6 +13,13 @@ private:
|
||||
esp_codec_dev_handle_t input_dev_ = nullptr;
|
||||
gpio_num_t pa_ctrl_pin_ = GPIO_NUM_NC;
|
||||
|
||||
// 定时器相关成员变量
|
||||
esp_timer_handle_t output_timer_ = nullptr;
|
||||
static constexpr uint64_t TIMER_TIMEOUT_US = 120000; // 120ms = 120000us
|
||||
|
||||
// 定时器回调函数
|
||||
static void OutputTimerCallback(void* arg);
|
||||
|
||||
virtual int Read(int16_t* dest, int samples) override;
|
||||
virtual int Write(const int16_t* data, int samples) override;
|
||||
|
||||
|
||||
@ -6,6 +6,9 @@
|
||||
#define AUDIO_INPUT_SAMPLE_RATE 16000
|
||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||
|
||||
// 配置PDM上采样fs参数(取值范围<=480)。部分设备在441时表现更稳定
|
||||
#define AUDIO_PDM_UPSAMPLE_FS 441
|
||||
|
||||
#define AUDIO_ADC_MIC_CHANNEL 2
|
||||
#define AUDIO_PDM_SPEAK_P_GPIO GPIO_NUM_6
|
||||
#define AUDIO_PDM_SPEAK_N_GPIO GPIO_NUM_7
|
||||
|
||||
@ -25,7 +25,6 @@
|
||||
"CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=2048",
|
||||
"CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y",
|
||||
"CONFIG_NEWLIB_NANO_FORMAT=y",
|
||||
"CONFIG_MMAP_FILE_NAME_LENGTH=25",
|
||||
"CONFIG_ESP_CONSOLE_NONE=y",
|
||||
"CONFIG_USE_ESP_WAKE_WORD=y",
|
||||
"CONFIG_COMPILER_OPTIMIZATION_SIZE=y"
|
||||
|
||||
@ -48,6 +48,8 @@ EmojiPlayer::EmojiPlayer(esp_lcd_panel_handle_t panel, esp_lcd_panel_io_handle_t
|
||||
.task = ANIM_PLAYER_INIT_CONFIG()
|
||||
};
|
||||
|
||||
player_cfg.task.task_priority = 1;
|
||||
player_cfg.task.task_stack = 4096;
|
||||
player_handle_ = anim_player_init(&player_cfg);
|
||||
|
||||
const esp_lcd_panel_io_callbacks_t cbs = {
|
||||
|
||||
@ -38,6 +38,8 @@ public:
|
||||
|
||||
virtual void SetEmotion(const char* emotion) override;
|
||||
virtual void SetStatus(const char* status) override;
|
||||
virtual void SetChatMessage(const char* role, const char* content) override {}
|
||||
|
||||
anim::EmojiPlayer* GetPlayer()
|
||||
{
|
||||
return player_.get();
|
||||
|
||||
@ -397,11 +397,6 @@ public:
|
||||
InitializeSpi();
|
||||
InitializeLcdDisplay();
|
||||
InitializeTools();
|
||||
|
||||
DeviceStateEventManager::GetInstance().RegisterStateChangeCallback([this](DeviceState previous_state, DeviceState current_state) {
|
||||
ESP_LOGD(TAG, "Device state changed from %d to %d", previous_state, current_state);
|
||||
this->GetAudioCodec()->EnableOutput(current_state == kDeviceStateSpeaking);
|
||||
});
|
||||
}
|
||||
|
||||
virtual AudioCodec* GetAudioCodec() override
|
||||
|
||||
@ -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_
|
||||
@ -5,6 +5,7 @@
|
||||
#include "button.h"
|
||||
#include "config.h"
|
||||
#include "i2c_device.h"
|
||||
#include "assets/lang_config.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <esp_lcd_panel_vendor.h>
|
||||
@ -16,6 +17,16 @@
|
||||
#include "esp32_camera.h"
|
||||
|
||||
#define TAG "esp32s3_korvo2_v3"
|
||||
/* ADC Buttons */
|
||||
typedef enum {
|
||||
BSP_ADC_BUTTON_REC,
|
||||
BSP_ADC_BUTTON_VOL_MUTE,
|
||||
BSP_ADC_BUTTON_PLAY,
|
||||
BSP_ADC_BUTTON_SET,
|
||||
BSP_ADC_BUTTON_VOL_DOWN,
|
||||
BSP_ADC_BUTTON_VOL_UP,
|
||||
BSP_ADC_BUTTON_NUM
|
||||
} bsp_adc_button_t;
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_20_4);
|
||||
LV_FONT_DECLARE(font_awesome_20_4);
|
||||
@ -45,6 +56,10 @@ static const ili9341_lcd_init_cmd_t vendor_specific_init[] = {
|
||||
class Esp32S3Korvo2V3Board : public WifiBoard {
|
||||
private:
|
||||
Button boot_button_;
|
||||
Button* adc_button_[BSP_ADC_BUTTON_NUM];
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
adc_oneshot_unit_handle_t bsp_adc_handle = NULL;
|
||||
#endif
|
||||
i2c_master_bus_handle_t i2c_bus_;
|
||||
LcdDisplay* display_;
|
||||
esp_io_expander_handle_t io_expander_ = NULL;
|
||||
@ -131,7 +146,103 @@ private:
|
||||
ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO));
|
||||
}
|
||||
|
||||
void ChangeVol(int val) {
|
||||
auto codec = GetAudioCodec();
|
||||
auto volume = codec->output_volume() + val;
|
||||
if (volume > 100) {
|
||||
volume = 100;
|
||||
}
|
||||
if (volume < 0) {
|
||||
volume = 0;
|
||||
}
|
||||
codec->SetOutputVolume(volume);
|
||||
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
|
||||
}
|
||||
|
||||
void MuteVol() {
|
||||
auto codec = GetAudioCodec();
|
||||
auto volume = codec->output_volume();
|
||||
if (volume > 1) {
|
||||
volume = 0;
|
||||
} else {
|
||||
volume = 50;
|
||||
}
|
||||
codec->SetOutputVolume(volume);
|
||||
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
|
||||
}
|
||||
|
||||
void InitializeButtons() {
|
||||
button_adc_config_t adc_cfg = {};
|
||||
adc_cfg.adc_channel = ADC_CHANNEL_4; // ADC1 channel 0 is GPIO5
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
const adc_oneshot_unit_init_cfg_t init_config1 = {
|
||||
.unit_id = ADC_UNIT_1,
|
||||
};
|
||||
adc_oneshot_new_unit(&init_config1, &bsp_adc_handle);
|
||||
adc_cfg.adc_handle = &bsp_adc_handle;
|
||||
#endif
|
||||
adc_cfg.button_index = BSP_ADC_BUTTON_REC;
|
||||
adc_cfg.min = 2310; // middle is 2410mV
|
||||
adc_cfg.max = 2510;
|
||||
adc_button_[0] = new AdcButton(adc_cfg);
|
||||
|
||||
adc_cfg.button_index = BSP_ADC_BUTTON_VOL_MUTE;
|
||||
adc_cfg.min = 1880; // middle is 1980mV
|
||||
adc_cfg.max = 2080;
|
||||
adc_button_[1] = new AdcButton(adc_cfg);
|
||||
|
||||
adc_cfg.button_index = BSP_ADC_BUTTON_PLAY;
|
||||
adc_cfg.min = 1550; // middle is 1650mV
|
||||
adc_cfg.max = 1750;
|
||||
adc_button_[2] = new AdcButton(adc_cfg);
|
||||
|
||||
adc_cfg.button_index = BSP_ADC_BUTTON_SET;
|
||||
adc_cfg.min = 1015; // middle is 1115mV
|
||||
adc_cfg.max = 1215;
|
||||
adc_button_[3] = new AdcButton(adc_cfg);
|
||||
|
||||
adc_cfg.button_index = BSP_ADC_BUTTON_VOL_DOWN;
|
||||
adc_cfg.min = 720; // middle is 820mV
|
||||
adc_cfg.max = 920;
|
||||
adc_button_[4] = new AdcButton(adc_cfg);
|
||||
|
||||
adc_cfg.button_index = BSP_ADC_BUTTON_VOL_UP;
|
||||
adc_cfg.min = 280; // middle is 380mV
|
||||
adc_cfg.max = 480;
|
||||
adc_button_[5] = new AdcButton(adc_cfg);
|
||||
|
||||
auto volume_up_button = adc_button_[BSP_ADC_BUTTON_VOL_UP];
|
||||
volume_up_button->OnClick([this]() {ChangeVol(10);});
|
||||
volume_up_button->OnLongPress([this]() {
|
||||
GetAudioCodec()->SetOutputVolume(100);
|
||||
GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
|
||||
});
|
||||
|
||||
auto volume_down_button = adc_button_[BSP_ADC_BUTTON_VOL_DOWN];
|
||||
volume_down_button->OnClick([this]() {ChangeVol(-10);});
|
||||
volume_down_button->OnLongPress([this]() {
|
||||
GetAudioCodec()->SetOutputVolume(0);
|
||||
GetDisplay()->ShowNotification(Lang::Strings::MUTED);
|
||||
});
|
||||
|
||||
auto volume_mute_button = adc_button_[BSP_ADC_BUTTON_VOL_MUTE];
|
||||
volume_mute_button->OnClick([this]() {MuteVol();});
|
||||
|
||||
auto play_button = adc_button_[BSP_ADC_BUTTON_PLAY];
|
||||
play_button->OnClick([this]() {
|
||||
ESP_LOGI(TAG, " TODO %s:%d\n", __func__, __LINE__);
|
||||
});
|
||||
|
||||
auto set_button = adc_button_[BSP_ADC_BUTTON_SET];
|
||||
set_button->OnClick([this]() {
|
||||
ESP_LOGI(TAG, "TODO %s:%d\n", __func__, __LINE__);
|
||||
});
|
||||
|
||||
auto rec_button = adc_button_[BSP_ADC_BUTTON_REC];
|
||||
rec_button->OnClick([this]() {
|
||||
ESP_LOGI(TAG, "TODO %s:%d\n", __func__, __LINE__);
|
||||
});
|
||||
boot_button_.OnClick([this]() {});
|
||||
boot_button_.OnClick([this]() {
|
||||
auto& app = Application::GetInstance();
|
||||
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
|
||||
|
||||
@ -173,8 +173,8 @@ private:
|
||||
{
|
||||
esp_lcd_touch_handle_t tp;
|
||||
esp_lcd_touch_config_t tp_cfg = {
|
||||
.x_max = DISPLAY_WIDTH,
|
||||
.y_max = DISPLAY_HEIGHT,
|
||||
.x_max = DISPLAY_HEIGHT,
|
||||
.y_max = DISPLAY_WIDTH,
|
||||
.rst_gpio_num = GPIO_NUM_NC, // Shared with LCD reset
|
||||
.int_gpio_num = GPIO_NUM_NC,
|
||||
.levels = {
|
||||
|
||||
@ -389,7 +389,7 @@ void Otto::ShakeLeg(int steps, int period, int dir) {
|
||||
int homes[SERVO_COUNT] = {90, 90, 90, 90, HAND_HOME_POSITION, 180 - HAND_HOME_POSITION};
|
||||
|
||||
// Changes in the parameters if left leg is chosen
|
||||
if (dir == -1) {
|
||||
if (dir == 1) {
|
||||
shake_leg1[2] = 180 - 35;
|
||||
shake_leg1[3] = 180 - 58;
|
||||
shake_leg2[2] = 180 - 120;
|
||||
|
||||
@ -21,8 +21,8 @@
|
||||
|
||||
#define TAG "SURFERC3114TFT"
|
||||
|
||||
LV_FONT_DECLARE(font_puhui_16_4);
|
||||
LV_FONT_DECLARE(font_awesome_16_4);
|
||||
LV_FONT_DECLARE(font_puhui_20_4);
|
||||
LV_FONT_DECLARE(font_awesome_20_4);
|
||||
|
||||
class SurferC3114TFT : public WifiBoard {
|
||||
private:
|
||||
@ -148,8 +148,8 @@ private:
|
||||
display_ = new SpiLcdDisplay(panel_io_, panel_,
|
||||
DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY,
|
||||
{
|
||||
.text_font = &font_puhui_16_4,
|
||||
.icon_font = &font_awesome_16_4,
|
||||
.text_font = &font_puhui_20_4,
|
||||
.icon_font = &font_awesome_20_4,
|
||||
.emoji_font = font_emoji_32_init(),
|
||||
});
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@
|
||||
|
||||
|
||||
|
||||
#ifdef CONFIG_LCD_JD9853
|
||||
#ifdef CONFIG_AUDIO_BOARD_LCD_JD9853
|
||||
#define LCD_TYPE_JD9853_SERIAL
|
||||
#define DISPLAY_WIDTH 320
|
||||
#define DISPLAY_HEIGHT 172
|
||||
@ -76,7 +76,7 @@
|
||||
#define DISPLAY_OFFSET_Y 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LCD_ST7789
|
||||
#ifdef CONFIG_AUDIO_BOARD_LCD_ST7789
|
||||
#define LCD_TYPE_ST7789_SERIAL
|
||||
#define DISPLAY_WIDTH 240
|
||||
#define DISPLAY_HEIGHT 320
|
||||
@ -92,4 +92,4 @@
|
||||
|
||||
|
||||
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
#endif // _BOARD_CONFIG_H_
|
||||
@ -3,7 +3,6 @@
|
||||
#include "lcd_display.h"
|
||||
|
||||
#include <vector>
|
||||
#include <font_awesome_symbols.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_err.h>
|
||||
#include <esp_lvgl_port.h>
|
||||
@ -350,4 +349,4 @@ CustomLcdDisplay::CustomLcdDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_p
|
||||
}
|
||||
|
||||
SetupUI();
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,6 +117,9 @@ SpiLcdDisplay::SpiLcdDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_h
|
||||
ESP_LOGI(TAG, "Initialize LVGL port");
|
||||
lvgl_port_cfg_t port_cfg = ESP_LVGL_PORT_INIT_CONFIG();
|
||||
port_cfg.task_priority = 1;
|
||||
#if CONFIG_SOC_CPU_CORES_NUM > 1
|
||||
port_cfg.task_affinity = 1;
|
||||
#endif
|
||||
lvgl_port_init(&port_cfg);
|
||||
|
||||
ESP_LOGI(TAG, "Adding LCD display");
|
||||
@ -178,7 +181,9 @@ RgbLcdDisplay::RgbLcdDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_h
|
||||
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 = 50;
|
||||
#if CONFIG_SOC_CPU_CORES_NUM > 1
|
||||
port_cfg.task_affinity = 1;
|
||||
#endif
|
||||
lvgl_port_init(&port_cfg);
|
||||
|
||||
ESP_LOGI(TAG, "Adding LCD display");
|
||||
@ -237,6 +242,10 @@ MipiLcdDisplay::MipiLcdDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel
|
||||
|
||||
ESP_LOGI(TAG, "Initialize LVGL port");
|
||||
lvgl_port_cfg_t port_cfg = ESP_LVGL_PORT_INIT_CONFIG();
|
||||
port_cfg.task_priority = 1;
|
||||
#if CONFIG_SOC_CPU_CORES_NUM > 1
|
||||
port_cfg.task_affinity = 1;
|
||||
#endif
|
||||
lvgl_port_init(&port_cfg);
|
||||
|
||||
ESP_LOGI(TAG, "Adding LCD display");
|
||||
@ -636,9 +645,6 @@ void LcdDisplay::SetPreviewImage(const lv_img_dsc_t* img_dsc) {
|
||||
// Create the image object inside the bubble
|
||||
lv_obj_t* preview_image = lv_image_create(img_bubble);
|
||||
|
||||
// Create the image object inside the bubble
|
||||
lv_obj_t* preview_image = lv_image_create(img_bubble);
|
||||
|
||||
// Copy the image descriptor and data to avoid source data changes
|
||||
lv_img_dsc_t* copied_img_dsc = (lv_img_dsc_t*)heap_caps_malloc(sizeof(lv_img_dsc_t), MALLOC_CAP_8BIT);
|
||||
if (copied_img_dsc == nullptr) {
|
||||
|
||||
@ -23,6 +23,9 @@ 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;
|
||||
#if CONFIG_SOC_CPU_CORES_NUM > 1
|
||||
port_cfg.task_affinity = 1;
|
||||
#endif
|
||||
lvgl_port_init(&port_cfg);
|
||||
|
||||
ESP_LOGI(TAG, "Adding OLED display");
|
||||
|
||||
@ -13,9 +13,9 @@ 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.2
|
||||
78/esp-wifi-connect: ~2.6.1
|
||||
78/esp-opus-encoder: ~2.4.1
|
||||
78/esp-ml307: ~3.3.1
|
||||
78/esp-ml307: ~3.3.7
|
||||
78/xiaozhi-fonts: ~1.5.2
|
||||
espressif/led_strip: ~3.0.1
|
||||
espressif/esp_codec_dev: ~1.4.0
|
||||
@ -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
|
||||
|
||||
@ -27,5 +27,4 @@ extern "C" void app_main(void)
|
||||
// Launch the application
|
||||
auto& app = Application::GetInstance();
|
||||
app.Start();
|
||||
app.MainEventLoop();
|
||||
}
|
||||
|
||||
@ -16,8 +16,6 @@
|
||||
|
||||
#define TAG "MCP"
|
||||
|
||||
#define DEFAULT_TOOLCALL_STACK_SIZE 6144
|
||||
|
||||
McpServer::McpServer() {
|
||||
}
|
||||
|
||||
@ -100,6 +98,9 @@ void McpServer::AddCommonTools() {
|
||||
Property("question", kPropertyTypeString)
|
||||
}),
|
||||
[camera](const PropertyList& properties) -> ReturnValue {
|
||||
// Lower the priority to do the camera capture
|
||||
TaskPriorityReset priority_reset(1);
|
||||
|
||||
if (!camera->Capture()) {
|
||||
return "{\"success\": false, \"message\": \"Failed to capture photo\"}";
|
||||
}
|
||||
@ -235,13 +236,7 @@ void McpServer::ParseMessage(const cJSON* json) {
|
||||
ReplyError(id_int, "Invalid arguments");
|
||||
return;
|
||||
}
|
||||
auto stack_size = cJSON_GetObjectItem(params, "stackSize");
|
||||
if (stack_size != nullptr && !cJSON_IsNumber(stack_size)) {
|
||||
ESP_LOGE(TAG, "tools/call: Invalid stackSize");
|
||||
ReplyError(id_int, "Invalid stackSize");
|
||||
return;
|
||||
}
|
||||
DoToolCall(id_int, std::string(tool_name->valuestring), tool_arguments, stack_size ? stack_size->valueint : DEFAULT_TOOLCALL_STACK_SIZE);
|
||||
DoToolCall(id_int, std::string(tool_name->valuestring), tool_arguments);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Method not implemented: %s", method_str.c_str());
|
||||
ReplyError(id_int, "Method not implemented: " + method_str);
|
||||
@ -316,7 +311,7 @@ void McpServer::GetToolsList(int id, const std::string& cursor) {
|
||||
ReplyResult(id, json);
|
||||
}
|
||||
|
||||
void McpServer::DoToolCall(int id, const std::string& tool_name, const cJSON* tool_arguments, int stack_size) {
|
||||
void McpServer::DoToolCall(int id, const std::string& tool_name, const cJSON* tool_arguments) {
|
||||
auto tool_iter = std::find_if(tools_.begin(), tools_.end(),
|
||||
[&tool_name](const McpTool* tool) {
|
||||
return tool->name() == tool_name;
|
||||
@ -358,15 +353,9 @@ void McpServer::DoToolCall(int id, const std::string& tool_name, const cJSON* to
|
||||
return;
|
||||
}
|
||||
|
||||
// Start a task to receive data with stack size
|
||||
esp_pthread_cfg_t cfg = esp_pthread_get_default_config();
|
||||
cfg.thread_name = "tool_call";
|
||||
cfg.stack_size = stack_size;
|
||||
cfg.prio = 1;
|
||||
esp_pthread_set_cfg(&cfg);
|
||||
|
||||
// Use a thread to call the tool to avoid blocking the main thread
|
||||
tool_call_thread_ = std::thread([this, id, tool_iter, arguments = std::move(arguments)]() {
|
||||
// Use main thread to call the tool
|
||||
auto& app = Application::GetInstance();
|
||||
app.Schedule([this, id, tool_iter, arguments = std::move(arguments)]() {
|
||||
try {
|
||||
ReplyResult(id, (*tool_iter)->Call(arguments));
|
||||
} catch (const std::exception& e) {
|
||||
@ -374,5 +363,4 @@ void McpServer::DoToolCall(int id, const std::string& tool_name, const cJSON* to
|
||||
ReplyError(id, e.what());
|
||||
}
|
||||
});
|
||||
tool_call_thread_.detach();
|
||||
}
|
||||
@ -285,10 +285,9 @@ private:
|
||||
void ReplyError(int id, const std::string& message);
|
||||
|
||||
void GetToolsList(int id, const std::string& cursor);
|
||||
void DoToolCall(int id, const std::string& tool_name, const cJSON* tool_arguments, int stack_size);
|
||||
void DoToolCall(int id, const std::string& tool_name, const cJSON* tool_arguments);
|
||||
|
||||
std::vector<McpTool*> tools_;
|
||||
std::thread tool_call_thread_;
|
||||
};
|
||||
|
||||
#endif // MCP_SERVER_H
|
||||
|
||||
@ -51,11 +51,9 @@ std::string Ota::GetCheckVersionUrl() {
|
||||
|
||||
std::unique_ptr<Http> Ota::SetupHttp() {
|
||||
auto& board = Board::GetInstance();
|
||||
auto app_desc = esp_app_get_description();
|
||||
|
||||
auto network = board.GetNetwork();
|
||||
auto http = network->CreateHttp(0);
|
||||
auto user_agent = std::string(BOARD_NAME "/") + app_desc->version;
|
||||
auto user_agent = SystemInfo::GetUserAgent();
|
||||
http->SetHeader("Activation-Version", has_serial_number_ ? "2" : "1");
|
||||
http->SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
|
||||
http->SetHeader("Client-Id", board.GetUuid());
|
||||
|
||||
@ -47,6 +47,12 @@ std::string SystemInfo::GetChipModelName() {
|
||||
return std::string(CONFIG_IDF_TARGET);
|
||||
}
|
||||
|
||||
std::string SystemInfo::GetUserAgent() {
|
||||
auto app_desc = esp_app_get_description();
|
||||
auto user_agent = std::string(BOARD_NAME "/") + app_desc->version;
|
||||
return user_agent;
|
||||
}
|
||||
|
||||
esp_err_t SystemInfo::PrintTaskCpuUsage(TickType_t xTicksToWait) {
|
||||
#define ARRAY_SIZE_OFFSET 5
|
||||
TaskStatus_t *start_array = NULL, *end_array = NULL;
|
||||
|
||||
@ -13,6 +13,7 @@ public:
|
||||
static size_t GetFreeHeapSize();
|
||||
static std::string GetMacAddress();
|
||||
static std::string GetChipModelName();
|
||||
static std::string GetUserAgent();
|
||||
static esp_err_t PrintTaskCpuUsage(TickType_t xTicksToWait);
|
||||
static void PrintTaskList();
|
||||
static void PrintHeapStats();
|
||||
|
||||
@ -3,107 +3,186 @@ import os
|
||||
import json
|
||||
import zipfile
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
# 切换到项目根目录
|
||||
os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
# Switch to project root directory
|
||||
os.chdir(Path(__file__).resolve().parent.parent)
|
||||
|
||||
def get_board_type():
|
||||
with open("build/compile_commands.json") as f:
|
||||
################################################################################
|
||||
# Common utility functions
|
||||
################################################################################
|
||||
|
||||
def get_board_type_from_compile_commands() -> Optional[str]:
|
||||
"""Parse the current compiled BOARD_TYPE from build/compile_commands.json"""
|
||||
compile_file = Path("build/compile_commands.json")
|
||||
if not compile_file.exists():
|
||||
return None
|
||||
with compile_file.open() as f:
|
||||
data = json.load(f)
|
||||
for item in data:
|
||||
if not item["file"].endswith("main.cc"):
|
||||
continue
|
||||
command = item["command"]
|
||||
# extract -DBOARD_TYPE=xxx
|
||||
board_type = command.split("-DBOARD_TYPE=\\\"")[1].split("\\\"")[0].strip()
|
||||
return board_type
|
||||
for item in data:
|
||||
if not item["file"].endswith("main.cc"):
|
||||
continue
|
||||
cmd = item["command"]
|
||||
if "-DBOARD_TYPE=\\\"" in cmd:
|
||||
return cmd.split("-DBOARD_TYPE=\\\"")[1].split("\\\"")[0].strip()
|
||||
return None
|
||||
|
||||
def get_project_version():
|
||||
with open("CMakeLists.txt") as f:
|
||||
|
||||
def get_project_version() -> Optional[str]:
|
||||
"""Read set(PROJECT_VER "x.y.z") from root CMakeLists.txt"""
|
||||
with Path("CMakeLists.txt").open() as f:
|
||||
for line in f:
|
||||
if line.startswith("set(PROJECT_VER"):
|
||||
return line.split("\"")[1].split("\"")[0].strip()
|
||||
return line.split("\"")[1]
|
||||
return None
|
||||
|
||||
def merge_bin():
|
||||
|
||||
def merge_bin() -> None:
|
||||
if os.system("idf.py merge-bin") != 0:
|
||||
print("merge bin failed")
|
||||
print("merge-bin failed", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
def zip_bin(board_type, project_version):
|
||||
if not os.path.exists("releases"):
|
||||
os.makedirs("releases")
|
||||
output_path = f"releases/v{project_version}_{board_type}.zip"
|
||||
if os.path.exists(output_path):
|
||||
os.remove(output_path)
|
||||
with zipfile.ZipFile(output_path, 'w', compression=zipfile.ZIP_DEFLATED) as zipf:
|
||||
|
||||
def zip_bin(name: str, version: str) -> None:
|
||||
"""Zip build/merged-binary.bin to releases/v{version}_{name}.zip"""
|
||||
out_dir = Path("releases")
|
||||
out_dir.mkdir(exist_ok=True)
|
||||
output_path = out_dir / f"v{version}_{name}.zip"
|
||||
|
||||
if output_path.exists():
|
||||
output_path.unlink()
|
||||
|
||||
with zipfile.ZipFile(output_path, "w", compression=zipfile.ZIP_DEFLATED) as zipf:
|
||||
zipf.write("build/merged-binary.bin", arcname="merged-binary.bin")
|
||||
print(f"zip bin to {output_path} done")
|
||||
|
||||
|
||||
def release_current():
|
||||
merge_bin()
|
||||
board_type = get_board_type()
|
||||
print("board type:", board_type)
|
||||
project_version = get_project_version()
|
||||
print("project version:", project_version)
|
||||
zip_bin(board_type, project_version)
|
||||
################################################################################
|
||||
# board / variant related functions
|
||||
################################################################################
|
||||
|
||||
def get_all_board_types():
|
||||
board_configs = {}
|
||||
with open("main/CMakeLists.txt", encoding='utf-8') as f:
|
||||
lines = f.readlines()
|
||||
for i, line in enumerate(lines):
|
||||
# 查找 if(CONFIG_BOARD_TYPE_*) 行
|
||||
if "if(CONFIG_BOARD_TYPE_" in line:
|
||||
config_name = line.strip().split("if(")[1].split(")")[0]
|
||||
# 查找下一行的 set(BOARD_TYPE "xxx")
|
||||
next_line = lines[i + 1].strip()
|
||||
_BOARDS_DIR = Path("main/boards")
|
||||
|
||||
|
||||
def _collect_variants(config_filename: str = "config.json") -> list[dict[str, str]]:
|
||||
"""Traverse all boards under main/boards, collect variant information.
|
||||
|
||||
Return example:
|
||||
[{"board": "bread-compact-ml307", "name": "bread-compact-ml307"}, ...]
|
||||
"""
|
||||
variants: list[dict[str, str]] = []
|
||||
for board_path in _BOARDS_DIR.iterdir():
|
||||
if not board_path.is_dir():
|
||||
continue
|
||||
if board_path.name == "common":
|
||||
continue
|
||||
cfg_path = board_path / config_filename
|
||||
if not cfg_path.exists():
|
||||
print(f"[WARN] {cfg_path} does not exist, skip", file=sys.stderr)
|
||||
continue
|
||||
try:
|
||||
with cfg_path.open() as f:
|
||||
cfg = json.load(f)
|
||||
for build in cfg.get("builds", []):
|
||||
variants.append({"board": board_path.name, "name": build["name"]})
|
||||
except Exception as e:
|
||||
print(f"[ERROR] 解析 {cfg_path} 失败: {e}", file=sys.stderr)
|
||||
return variants
|
||||
|
||||
|
||||
def _parse_board_config_map() -> dict[str, str]:
|
||||
"""Build the mapping of CONFIG_BOARD_TYPE_xxx and board_type from main/CMakeLists.txt"""
|
||||
cmake_file = Path("main/CMakeLists.txt")
|
||||
mapping: dict[str, str] = {}
|
||||
lines = cmake_file.read_text(encoding="utf-8").splitlines()
|
||||
for idx, line in enumerate(lines):
|
||||
if "if(CONFIG_BOARD_TYPE_" in line:
|
||||
config_name = line.strip().split("if(")[1].split(")")[0]
|
||||
if idx + 1 < len(lines):
|
||||
next_line = lines[idx + 1].strip()
|
||||
if next_line.startswith("set(BOARD_TYPE"):
|
||||
board_type = next_line.split('"')[1]
|
||||
board_configs[config_name] = board_type
|
||||
return board_configs
|
||||
mapping[config_name] = board_type
|
||||
return mapping
|
||||
|
||||
def release(board_type, board_config, config_filename="config.json"):
|
||||
config_path = f"main/boards/{board_type}/{config_filename}"
|
||||
if not os.path.exists(config_path):
|
||||
print(f"跳过 {board_type} 因为 {config_filename} 不存在")
|
||||
|
||||
def _find_board_config(board_type: str) -> Optional[str]:
|
||||
"""Find the corresponding CONFIG_BOARD_TYPE_xxx for the given board_type"""
|
||||
for config, b_type in _parse_board_config_map().items():
|
||||
if b_type == board_type:
|
||||
return config
|
||||
return None
|
||||
|
||||
################################################################################
|
||||
# Check board_type in CMakeLists
|
||||
################################################################################
|
||||
|
||||
def _board_type_exists(board_type: str) -> bool:
|
||||
cmake_file = Path("main/CMakeLists.txt")
|
||||
pattern = f'set(BOARD_TYPE "{board_type}")'
|
||||
return pattern in cmake_file.read_text(encoding="utf-8")
|
||||
|
||||
################################################################################
|
||||
# Compile implementation
|
||||
################################################################################
|
||||
|
||||
def release(board_type: str, config_filename: str = "config.json", *, filter_name: Optional[str] = None) -> None:
|
||||
"""Compile and package all/specified variants of the specified board_type
|
||||
|
||||
Args:
|
||||
board_type: directory name under main/boards
|
||||
config_filename: config.json name (default: config.json)
|
||||
filter_name: if specified, only compile the build["name"] that matches
|
||||
"""
|
||||
cfg_path = _BOARDS_DIR / board_type / config_filename
|
||||
if not cfg_path.exists():
|
||||
print(f"[WARN] {cfg_path} 不存在,跳过 {board_type}")
|
||||
return
|
||||
|
||||
# Print Project Version
|
||||
project_version = get_project_version()
|
||||
print(f"Project Version: {project_version}", config_path)
|
||||
print(f"Project Version: {project_version} ({cfg_path})")
|
||||
|
||||
with cfg_path.open() as f:
|
||||
cfg = json.load(f)
|
||||
target = cfg["target"]
|
||||
|
||||
builds = cfg.get("builds", [])
|
||||
if filter_name:
|
||||
builds = [b for b in builds if b["name"] == filter_name]
|
||||
if not builds:
|
||||
print(f"[ERROR] 未在 {board_type} 的 {config_filename} 中找到变体 {filter_name}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
with open(config_path, "r") as f:
|
||||
config = json.load(f)
|
||||
target = config["target"]
|
||||
builds = config["builds"]
|
||||
|
||||
for build in builds:
|
||||
name = build["name"]
|
||||
if not name.startswith(board_type):
|
||||
raise ValueError(f"name {name} 必须以 {board_type} 开头")
|
||||
output_path = f"releases/v{project_version}_{name}.zip"
|
||||
if os.path.exists(output_path):
|
||||
print(f"跳过 {board_type} 因为 {output_path} 已存在")
|
||||
raise ValueError(f"build.name {name} 必须以 {board_type} 开头")
|
||||
|
||||
output_path = Path("releases") / f"v{project_version}_{name}.zip"
|
||||
if output_path.exists():
|
||||
print(f"跳过 {name} 因为 {output_path} 已存在")
|
||||
continue
|
||||
|
||||
sdkconfig_append = [f"{board_config}=y"]
|
||||
for append in build.get("sdkconfig_append", []):
|
||||
sdkconfig_append.append(append)
|
||||
# Process sdkconfig_append
|
||||
board_type_config = _find_board_config(board_type)
|
||||
sdkconfig_append = [f"{board_type_config}=y"]
|
||||
sdkconfig_append.extend(build.get("sdkconfig_append", []))
|
||||
|
||||
print("-" * 80)
|
||||
print(f"name: {name}")
|
||||
print(f"target: {target}")
|
||||
for append in sdkconfig_append:
|
||||
print(f"sdkconfig_append: {append}")
|
||||
# unset IDF_TARGET
|
||||
for item in sdkconfig_append:
|
||||
print(f"sdkconfig_append: {item}")
|
||||
|
||||
os.environ.pop("IDF_TARGET", None)
|
||||
|
||||
# Call set-target
|
||||
if os.system(f"idf.py set-target {target}") != 0:
|
||||
print("set-target failed")
|
||||
print("set-target failed", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Append sdkconfig
|
||||
with open("sdkconfig", "a") as f:
|
||||
with Path("sdkconfig").open("a") as f:
|
||||
f.write("\n")
|
||||
for append in sdkconfig_append:
|
||||
f.write(f"{append}\n")
|
||||
@ -111,43 +190,72 @@ def release(board_type, board_config, config_filename="config.json"):
|
||||
if os.system(f"idf.py -DBOARD_NAME={name} build") != 0:
|
||||
print("build failed")
|
||||
sys.exit(1)
|
||||
# Call merge-bin
|
||||
if os.system("idf.py merge-bin") != 0:
|
||||
print("merge-bin failed")
|
||||
sys.exit(1)
|
||||
# Zip bin
|
||||
|
||||
# merge-bin
|
||||
merge_bin()
|
||||
|
||||
# Zip
|
||||
zip_bin(name, project_version)
|
||||
print("-" * 80)
|
||||
|
||||
################################################################################
|
||||
# CLI entry
|
||||
################################################################################
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("board", nargs="?", default=None, help="板子类型或 all")
|
||||
parser.add_argument("-c", "--config", default="config.json", help="指定 config 文件名,默认 config.json")
|
||||
parser.add_argument("--list-boards", action="store_true", help="列出所有支持的 board 列表")
|
||||
parser.add_argument("--list-boards", action="store_true", help="列出所有支持的 board 及变体列表")
|
||||
parser.add_argument("--json", action="store_true", help="配合 --list-boards,JSON 格式输出")
|
||||
parser.add_argument("--name", help="指定变体名称,仅编译匹配的变体")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# List mode
|
||||
if args.list_boards:
|
||||
board_configs = get_all_board_types()
|
||||
boards = list(board_configs.values())
|
||||
variants = _collect_variants(config_filename=args.config)
|
||||
if args.json:
|
||||
print(json.dumps(boards))
|
||||
print(json.dumps(variants))
|
||||
else:
|
||||
for board in boards:
|
||||
print(board)
|
||||
for v in variants:
|
||||
print(f"{v['board']}: {v['name']}")
|
||||
sys.exit(0)
|
||||
|
||||
if args.board:
|
||||
board_configs = get_all_board_types()
|
||||
found = False
|
||||
for board_config, board_type in board_configs.items():
|
||||
if args.board == 'all' or board_type == args.board:
|
||||
release(board_type, board_config, config_filename=args.config)
|
||||
found = True
|
||||
if not found:
|
||||
print(f"未找到板子类型: {args.board}")
|
||||
print("可用的板子类型:")
|
||||
for board_type in board_configs.values():
|
||||
print(f" {board_type}")
|
||||
# Current directory firmware packaging mode
|
||||
if args.board is None:
|
||||
merge_bin()
|
||||
curr_board_type = get_board_type_from_compile_commands()
|
||||
if curr_board_type is None:
|
||||
print("未能从 compile_commands.json 解析 board_type", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
project_ver = get_project_version()
|
||||
zip_bin(curr_board_type, project_ver)
|
||||
sys.exit(0)
|
||||
|
||||
# Compile mode
|
||||
board_type_input: str = args.board
|
||||
name_filter: str | None = args.name
|
||||
|
||||
# Check board_type in CMakeLists
|
||||
if board_type_input != "all" and not _board_type_exists(board_type_input):
|
||||
print(f"[ERROR] main/CMakeLists.txt 中未找到 board_type {board_type_input}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
variants_all = _collect_variants(config_filename=args.config)
|
||||
|
||||
# Filter board_type list
|
||||
target_board_types: set[str]
|
||||
if board_type_input == "all":
|
||||
target_board_types = {v["board"] for v in variants_all}
|
||||
else:
|
||||
release_current()
|
||||
target_board_types = {board_type_input}
|
||||
|
||||
for bt in sorted(target_board_types):
|
||||
if not _board_type_exists(bt):
|
||||
print(f"[ERROR] main/CMakeLists.txt 中未找到 board_type {bt}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
cfg_path = _BOARDS_DIR / bt / args.config
|
||||
if bt == board_type_input and not cfg_path.exists():
|
||||
print(f"开发板 {bt} 未定义 {args.config} 配置文件,跳过")
|
||||
sys.exit(0)
|
||||
release(bt, config_filename=args.config, filter_name=name_filter if bt == board_type_input else None)
|
||||
|
||||
@ -54,7 +54,6 @@ 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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user