diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 1f904fb..d1758bd 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -158,6 +158,13 @@ elseif(CONFIG_BOARD_TYPE_LICHUANG_DEV_C3) set(BUILTIN_TEXT_FONT font_puhui_basic_20_4) set(BUILTIN_ICON_FONT font_awesome_20_4) set(DEFAULT_EMOJI_COLLECTION twemoji_32) +elseif(CONFIG_BOARD_TYPE_RYMCU_BIGSMART) + set(MANUFACTURER "rymcu") + set(BOARD_TYPE "bigsmart") + set(BOARD_NAME "rymcu-bigsmart") + set(BUILTIN_TEXT_FONT font_noto_basic_20_4) + set(BUILTIN_ICON_FONT font_awesome_20_4) + set(DEFAULT_EMOJI_COLLECTION noto-emoji_128) elseif(CONFIG_BOARD_TYPE_EDA_TV_PRO) set(MANUFACTURER "lceda-course-examples") set(BOARD_TYPE "eda-tv-pro") diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index c3e20a8..ffaf7c0 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -215,6 +215,9 @@ choice BOARD_TYPE config BOARD_TYPE_LICHUANG_DEV_C3 bool "立创·实战派 ESP32-C3" depends on IDF_TARGET_ESP32C3 + config BOARD_TYPE_RYMCU_BIGSMART + bool "RYMCU BigSmart" + depends on IDF_TARGET_ESP32S3 config BOARD_TYPE_EDA_TV_PRO bool "EDA课程案例 EDA-TV-Pro" depends on IDF_TARGET_ESP32S3 @@ -711,7 +714,7 @@ choice DISPLAY_STYLE config USE_EMOTE_MESSAGE_STYLE bool "Emote animation style" depends on BOARD_TYPE_ESP_BOX || BOARD_TYPE_ESP_BOX_3 \ - || BOARD_TYPE_ESP_VOCAT || BOARD_TYPE_LICHUANG_DEV_S3 \ + || BOARD_TYPE_ESP_VOCAT || BOARD_TYPE_LICHUANG_DEV_S3 || BOARD_TYPE_RYMCU_BIGSMART \ || BOARD_TYPE_ESP_SENSAIRSHUTTLE endchoice @@ -808,7 +811,7 @@ 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_S3 || BOARD_TYPE_ESP_KORVO2_V3 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75|| BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75C || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_83\ + || BOARD_TYPE_LICHUANG_DEV_S3 || BOARD_TYPE_RYMCU_BIGSMART || BOARD_TYPE_ESP_KORVO2_V3 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75|| BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75C || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_83\ || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_2_06 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3 \ || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B \ || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_4C || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4C || BOARD_TYPE_ESP_S3_LCD_EV_Board_2 || BOARD_TYPE_YUNLIAO_S3 \ diff --git a/main/boards/rymcu/bigsmart/README.md b/main/boards/rymcu/bigsmart/README.md new file mode 100644 index 0000000..c6394f7 --- /dev/null +++ b/main/boards/rymcu/bigsmart/README.md @@ -0,0 +1,31 @@ +# RYMCU BigSmart + +该目录为 `RYMCU BigSmart` 开发板适配,并按以下硬件资源完成映射: + +- 主控:ESP32-S3-WROOM-1-N16R8 +- 显示:ST7789(320x240,SPI) +- 触摸:GT911(I2C) +- 音频:ES8311 + ES7210(I2S + I2C) +- IO扩展:PCA9557(I2C 地址 `0x19`) +- 摄像头:GC0308(DVP) + +参考硬件文档: + +- https://github.com/rymcu/BigSmart-Open/blob/main/docs/rymcu-bigsmart-hardware.md + +## 编译 + +```bash +idf.py set-target esp32s3 +idf.py menuconfig +``` + +在菜单中选择: + +`Xiaozhi Assistant -> Board Type -> RYMCU BigSmart` + +然后执行: + +```bash +idf.py build +``` diff --git a/main/boards/rymcu/bigsmart/config.h b/main/boards/rymcu/bigsmart/config.h new file mode 100644 index 0000000..0be2916 --- /dev/null +++ b/main/boards/rymcu/bigsmart/config.h @@ -0,0 +1,69 @@ +#ifndef _BOARD_CONFIG_H_ +#define _BOARD_CONFIG_H_ + +#include + +#define AUDIO_INPUT_SAMPLE_RATE 24000 +#define AUDIO_OUTPUT_SAMPLE_RATE 24000 + +#define AUDIO_INPUT_REFERENCE true + +#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_38 +#define AUDIO_I2S_GPIO_WS GPIO_NUM_13 +#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_14 +#define AUDIO_I2S_GPIO_DIN GPIO_NUM_12 +#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_45 + +#define AUDIO_CODEC_USE_PCA9557 +#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_1 +#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_2 +#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR +#define AUDIO_CODEC_ES7210_ADDR 0x82 + +#define BUILTIN_LED_GPIO GPIO_NUM_NC +#define RGB_LED_GPIO GPIO_NUM_43 +#define BOOT_BUTTON_GPIO GPIO_NUM_0 +#define CUSTOM_BUTTON_GPIO GPIO_NUM_10 +#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_NC +#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_NC + +#define DISPLAY_WIDTH 320 +#define DISPLAY_HEIGHT 240 +#define DISPLAY_MIRROR_X true +#define DISPLAY_MIRROR_Y false +#define DISPLAY_SWAP_XY true + +#define DISPLAY_OFFSET_X 0 +#define DISPLAY_OFFSET_Y 0 + +#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_42 +#define DISPLAY_BACKLIGHT_OUTPUT_INVERT true + +#define TOUCH_INT_GPIO GPIO_NUM_NC +#define TOUCH_RST_GPIO GPIO_NUM_NC + +/* Camera pins */ +#define CAMERA_PIN_PWDN GPIO_NUM_NC +#define CAMERA_PIN_RESET GPIO_NUM_NC +#define CAMERA_PIN_XCLK GPIO_NUM_5 +#define CAMERA_PIN_SIOD GPIO_NUM_1 +#define CAMERA_PIN_SIOC GPIO_NUM_2 + +#define CAMERA_PIN_D7 GPIO_NUM_9 +#define CAMERA_PIN_D6 GPIO_NUM_4 +#define CAMERA_PIN_D5 GPIO_NUM_6 +#define CAMERA_PIN_D4 GPIO_NUM_15 +#define CAMERA_PIN_D3 GPIO_NUM_17 +#define CAMERA_PIN_D2 GPIO_NUM_8 +#define CAMERA_PIN_D1 GPIO_NUM_18 +#define CAMERA_PIN_D0 GPIO_NUM_16 +#define CAMERA_PIN_VSYNC GPIO_NUM_44 +#define CAMERA_PIN_HREF GPIO_NUM_46 +#define CAMERA_PIN_PCLK GPIO_NUM_7 + +#define XCLK_FREQ_HZ 16000000 + +#define BATTERY_CHARGING_PIN GPIO_NUM_3 +#define BATTERY_ADC_PIN GPIO_NUM_11 + +#endif // _BOARD_CONFIG_H_ diff --git a/main/boards/rymcu/bigsmart/config.json b/main/boards/rymcu/bigsmart/config.json new file mode 100644 index 0000000..a67be5a --- /dev/null +++ b/main/boards/rymcu/bigsmart/config.json @@ -0,0 +1,12 @@ +{ + "manufacturer": "rymcu", + "target": "esp32s3", + "builds": [ + { + "name": "bigsmart", + "sdkconfig_append": [ + "CONFIG_USE_DEVICE_AEC=y" + ] + } + ] +} diff --git a/main/boards/rymcu/bigsmart/rymcu_bigsmart_board.cc b/main/boards/rymcu/bigsmart/rymcu_bigsmart_board.cc new file mode 100644 index 0000000..474fc6f --- /dev/null +++ b/main/boards/rymcu/bigsmart/rymcu_bigsmart_board.cc @@ -0,0 +1,284 @@ +#include "wifi_board.h" +#include "codecs/box_audio_codec.h" +#include "display/lcd_display.h" +#include "display/emote_display.h" +#include "application.h" +#include "button.h" +#include "config.h" +#include "i2c_device.h" +#include "esp32_camera.h" +#include "mcp_server.h" + +#include +#include +#include +#include +#include +#include +#include + +#define TAG "RymcuBigsmartBoard" + +class Pca9557 : public I2cDevice { +public: + Pca9557(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) { + WriteReg(0x01, 0x03); + WriteReg(0x03, 0xf8); + } + + void SetOutputState(uint8_t bit, uint8_t level) { + uint8_t data = ReadReg(0x01); + data = (data & ~(1 << bit)) | (level << bit); + WriteReg(0x01, data); + } +}; + +class CustomAudioCodec : public BoxAudioCodec { +private: + Pca9557* pca9557_; + +public: + CustomAudioCodec(i2c_master_bus_handle_t i2c_bus, Pca9557* pca9557) + : BoxAudioCodec(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, + GPIO_NUM_NC, + AUDIO_CODEC_ES8311_ADDR, + AUDIO_CODEC_ES7210_ADDR, + AUDIO_INPUT_REFERENCE), + pca9557_(pca9557) { + } + + virtual void EnableOutput(bool enable) override { + BoxAudioCodec::EnableOutput(enable); + if (enable) { + pca9557_->SetOutputState(1, 1); + } else { + pca9557_->SetOutputState(1, 0); + } + } +}; + +class RymcuBigsmartBoard : public WifiBoard { +private: + i2c_master_bus_handle_t i2c_bus_; + Button boot_button_; + Display* display_; + Pca9557* pca9557_; + Esp32Camera* camera_; + + void InitializeI2c() { + i2c_master_bus_config_t i2c_bus_cfg = { + .i2c_port = (i2c_port_t)1, + .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_)); + + pca9557_ = new Pca9557(i2c_bus_, 0x19); + } + + void InitializeSpi() { + spi_bus_config_t buscfg = {}; + buscfg.mosi_io_num = GPIO_NUM_40; + buscfg.miso_io_num = GPIO_NUM_NC; + buscfg.sclk_io_num = GPIO_NUM_41; + 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(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO)); + } + + void InitializeButtons() { + boot_button_.OnClick([this]() { + auto& app = Application::GetInstance(); + if (app.GetDeviceState() == kDeviceStateStarting) { + EnterWifiConfigMode(); + return; + } + app.ToggleChatState(); + }); + +#if CONFIG_USE_DEVICE_AEC + boot_button_.OnDoubleClick([this]() { + auto& app = Application::GetInstance(); + if (app.GetDeviceState() == kDeviceStateIdle) { + app.SetAecMode(app.GetAecMode() == kAecOff ? kAecOnDeviceSide : kAecOff); + } + }); +#endif + } + + void InitializeSt7789Display() { + esp_lcd_panel_io_handle_t panel_io = nullptr; + esp_lcd_panel_handle_t panel = nullptr; + ESP_LOGD(TAG, "Install panel IO"); + esp_lcd_panel_io_spi_config_t io_config = {}; + io_config.cs_gpio_num = GPIO_NUM_NC; + io_config.dc_gpio_num = GPIO_NUM_39; + io_config.spi_mode = 2; + io_config.pclk_hz = 80 * 1000 * 1000; + 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(SPI3_HOST, &io_config, &panel_io)); + + 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_lcd_panel_reset(panel); + pca9557_->SetOutputState(0, 0); + + esp_lcd_panel_init(panel); + esp_lcd_panel_invert_color(panel, true); + esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY); + esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); + esp_lcd_panel_disp_on_off(panel, true); + +#if CONFIG_USE_EMOTE_MESSAGE_STYLE + display_ = new emote::EmoteDisplay(panel, panel_io, DISPLAY_WIDTH, DISPLAY_HEIGHT); +#else + 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); +#endif + } + + void InitializeTouch() { + esp_lcd_touch_handle_t tp; + esp_lcd_touch_config_t tp_cfg = { + .x_max = DISPLAY_HEIGHT, + .y_max = DISPLAY_WIDTH, + .rst_gpio_num = TOUCH_RST_GPIO, + .int_gpio_num = TOUCH_INT_GPIO, + .levels = { + .reset = 0, + .interrupt = 0, + }, + .flags = { + .swap_xy = 1, + .mirror_x = 1, + .mirror_y = 0, + }, + }; + esp_lcd_panel_io_handle_t tp_io_handle = NULL; + esp_lcd_panel_io_i2c_config_t tp_io_config = { + .dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS, + .control_phase_bytes = 1, + .dc_bit_offset = 0, + .lcd_cmd_bits = 16, + .flags = { + .disable_control_phase = 1, + } + }; + tp_io_config.scl_speed_hz = 400000; + + ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(i2c_bus_, &tp_io_config, &tp_io_handle)); + ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_gt911(tp_io_handle, &tp_cfg, &tp)); + assert(tp); + + const lvgl_port_touch_cfg_t touch_cfg = { + .disp = lv_display_get_default(), + .handle = tp, + }; + + if (touch_cfg.disp) { + lvgl_port_add_touch(&touch_cfg); + } else { + ESP_LOGE(TAG, "Touch display is not initialized"); + } + } + + void InitializeCamera() { + pca9557_->SetOutputState(2, 0); + + camera_config_t config = {}; + config.ledc_channel = LEDC_CHANNEL_2; + config.ledc_timer = LEDC_TIMER_2; + 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 = -1; + config.pin_sccb_scl = CAMERA_PIN_SIOC; + config.sccb_i2c_port = 1; + 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); + } + + void InitializeTools() { + auto& mcp_server = McpServer::GetInstance(); + mcp_server.AddTool("self.system.reconfigure_wifi", + "End this conversation and enter WiFi configuration mode.\n" + "**CAUTION** You must ask the user to confirm this action.", + PropertyList(), [this](const PropertyList& properties) { + EnterWifiConfigMode(); + return true; + }); + } + +public: + RymcuBigsmartBoard() : boot_button_(BOOT_BUTTON_GPIO) { + InitializeI2c(); + InitializeSpi(); + InitializeSt7789Display(); + InitializeTouch(); + InitializeButtons(); + InitializeCamera(); + InitializeTools(); + + GetBacklight()->RestoreBrightness(); + } + + virtual AudioCodec* GetAudioCodec() override { + static CustomAudioCodec audio_codec(i2c_bus_, pca9557_); + return &audio_codec; + } + + virtual Display* GetDisplay() override { + return display_; + } + + virtual Backlight* GetBacklight() override { + static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT); + return &backlight; + } + + virtual Camera* GetCamera() override { + return camera_; + } +}; + +DECLARE_BOARD(RymcuBigsmartBoard);