diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index c6b3c0be..8c18ba57 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -690,13 +690,6 @@ config RECEIVE_CUSTOM_MESSAGE help Enable custom message reception, allow the device to receive custom messages from the server (preferably through the MQTT protocol) -config OTTO_ROBOT_USE_CAMERA - bool "Enable Otto Robot Camera" - default n - depends on BOARD_TYPE_OTTO_ROBOT - help - Enable Otto Robot Camera - menu "Camera Configuration" depends on !IDF_TARGET_ESP32 diff --git a/main/boards/otto-robot/config.h b/main/boards/otto-robot/config.h index 8a275e3c..8e8945f7 100644 --- a/main/boards/otto-robot/config.h +++ b/main/boards/otto-robot/config.h @@ -2,36 +2,124 @@ #define _BOARD_CONFIG_H_ #include +#include -#if CONFIG_OTTO_ROBOT_USE_CAMERA -#define POWER_CHARGE_DETECT_PIN GPIO_NUM_NC -#define POWER_ADC_UNIT ADC_UNIT_1 -#define POWER_ADC_CHANNEL ADC_CHANNEL_1 +struct HardwareConfig { + gpio_num_t power_charge_detect_pin; + adc_unit_t power_adc_unit; + adc_channel_t power_adc_channel; + + gpio_num_t right_leg_pin; + gpio_num_t right_foot_pin; + gpio_num_t left_leg_pin; + gpio_num_t left_foot_pin; + gpio_num_t left_hand_pin; + gpio_num_t right_hand_pin; + + int audio_input_sample_rate; + int audio_output_sample_rate; + bool audio_use_simplex; + + gpio_num_t audio_i2s_gpio_ws; + gpio_num_t audio_i2s_gpio_bclk; + gpio_num_t audio_i2s_gpio_din; + gpio_num_t audio_i2s_gpio_dout; + + gpio_num_t audio_i2s_mic_gpio_ws; + gpio_num_t audio_i2s_mic_gpio_sck; + gpio_num_t audio_i2s_mic_gpio_din; + gpio_num_t audio_i2s_spk_gpio_dout; + gpio_num_t audio_i2s_spk_gpio_bclk; + gpio_num_t audio_i2s_spk_gpio_lrck; + + gpio_num_t display_backlight_pin; + gpio_num_t display_mosi_pin; + gpio_num_t display_clk_pin; + gpio_num_t display_dc_pin; + gpio_num_t display_rst_pin; + gpio_num_t display_cs_pin; + + gpio_num_t i2c_sda_pin; + gpio_num_t i2c_scl_pin; +}; -#define RIGHT_LEG_PIN GPIO_NUM_43 -#define RIGHT_FOOT_PIN GPIO_NUM_44 -#define LEFT_LEG_PIN GPIO_NUM_5 -#define LEFT_FOOT_PIN GPIO_NUM_6 -#define LEFT_HAND_PIN GPIO_NUM_4 -#define RIGHT_HAND_PIN GPIO_NUM_7 +constexpr HardwareConfig CAMERA_VERSION_CONFIG = { + .power_charge_detect_pin = GPIO_NUM_NC, + .power_adc_unit = ADC_UNIT_1, + .power_adc_channel = ADC_CHANNEL_1, + + .right_leg_pin = GPIO_NUM_43, + .right_foot_pin = GPIO_NUM_44, + .left_leg_pin = GPIO_NUM_5, + .left_foot_pin = GPIO_NUM_6, + .left_hand_pin = GPIO_NUM_4, + .right_hand_pin = GPIO_NUM_7, + + .audio_input_sample_rate = 16000, + .audio_output_sample_rate = 16000, + .audio_use_simplex = false, + + .audio_i2s_gpio_ws = GPIO_NUM_40, + .audio_i2s_gpio_bclk = GPIO_NUM_42, + .audio_i2s_gpio_din = GPIO_NUM_41, + .audio_i2s_gpio_dout = GPIO_NUM_39, + + .audio_i2s_mic_gpio_ws = GPIO_NUM_NC, + .audio_i2s_mic_gpio_sck = GPIO_NUM_NC, + .audio_i2s_mic_gpio_din = GPIO_NUM_NC, + .audio_i2s_spk_gpio_dout = GPIO_NUM_NC, + .audio_i2s_spk_gpio_bclk = GPIO_NUM_NC, + .audio_i2s_spk_gpio_lrck = GPIO_NUM_NC, + + .display_backlight_pin = GPIO_NUM_38, + .display_mosi_pin = GPIO_NUM_45, + .display_clk_pin = GPIO_NUM_48, + .display_dc_pin = GPIO_NUM_47, + .display_rst_pin = GPIO_NUM_1, + .display_cs_pin = GPIO_NUM_NC, + + .i2c_sda_pin = GPIO_NUM_15, + .i2c_scl_pin = GPIO_NUM_16, +}; -#define AUDIO_INPUT_SAMPLE_RATE 16000 -#define AUDIO_OUTPUT_SAMPLE_RATE 16000 - -#define AUDIO_I2S_GPIO_WS GPIO_NUM_40 -#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_42 -#define AUDIO_I2S_GPIO_DIN GPIO_NUM_41 -#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_39 - -#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_38 -#define DISPLAY_MOSI_PIN GPIO_NUM_45 -#define DISPLAY_CLK_PIN GPIO_NUM_48 -#define DISPLAY_DC_PIN GPIO_NUM_47 -#define DISPLAY_RST_PIN GPIO_NUM_1 -#define DISPLAY_CS_PIN GPIO_NUM_NC -/* Camera PINs*/ -#define I2C_SDA_PIN GPIO_NUM_15 -#define I2C_SCL_PIN GPIO_NUM_16 +constexpr HardwareConfig NON_CAMERA_VERSION_CONFIG = { + .power_charge_detect_pin = GPIO_NUM_21, + .power_adc_unit = ADC_UNIT_2, + .power_adc_channel = ADC_CHANNEL_3, + + .right_leg_pin = GPIO_NUM_39, + .right_foot_pin = GPIO_NUM_38, + .left_leg_pin = GPIO_NUM_17, + .left_foot_pin = GPIO_NUM_18, + .left_hand_pin = GPIO_NUM_8, + .right_hand_pin = GPIO_NUM_12, + + .audio_input_sample_rate = 16000, + .audio_output_sample_rate = 24000, + .audio_use_simplex = true, + + .audio_i2s_gpio_ws = GPIO_NUM_NC, + .audio_i2s_gpio_bclk = GPIO_NUM_NC, + .audio_i2s_gpio_din = GPIO_NUM_NC, + .audio_i2s_gpio_dout = GPIO_NUM_NC, + + .audio_i2s_mic_gpio_ws = GPIO_NUM_4, + .audio_i2s_mic_gpio_sck = GPIO_NUM_5, + .audio_i2s_mic_gpio_din = GPIO_NUM_6, + .audio_i2s_spk_gpio_dout = GPIO_NUM_7, + .audio_i2s_spk_gpio_bclk = GPIO_NUM_15, + .audio_i2s_spk_gpio_lrck = GPIO_NUM_16, + + .display_backlight_pin = GPIO_NUM_3, + .display_mosi_pin = GPIO_NUM_10, + .display_clk_pin = GPIO_NUM_9, + .display_dc_pin = GPIO_NUM_46, + .display_rst_pin = GPIO_NUM_11, + .display_cs_pin = GPIO_NUM_12, + + .i2c_sda_pin = GPIO_NUM_NC, + .i2c_scl_pin = GPIO_NUM_NC, +}; #define CAMERA_XCLK (GPIO_NUM_3) #define CAMERA_PCLK (GPIO_NUM_10) @@ -45,49 +133,12 @@ #define CAMERA_D5 (GPIO_NUM_9) #define CAMERA_D6 (GPIO_NUM_46) #define CAMERA_D7 (GPIO_NUM_8) - #define CAMERA_PWDN (GPIO_NUM_NC) #define CAMERA_RESET (GPIO_NUM_NC) - #define CAMERA_XCLK_FREQ (16000000) #define LEDC_TIMER (LEDC_TIMER_0) #define LEDC_CHANNEL (LEDC_CHANNEL_0) -#define CAMERA_SIOD (GPIO_NUM_NC) -#define CAMERA_SIOC (GPIO_NUM_NC) - -#else -#define POWER_CHARGE_DETECT_PIN GPIO_NUM_21 -#define POWER_ADC_UNIT ADC_UNIT_2 -#define POWER_ADC_CHANNEL ADC_CHANNEL_3 - -#define RIGHT_LEG_PIN GPIO_NUM_39 -#define RIGHT_FOOT_PIN GPIO_NUM_38 -#define LEFT_LEG_PIN GPIO_NUM_17 -#define LEFT_FOOT_PIN GPIO_NUM_18 -#define LEFT_HAND_PIN GPIO_NUM_8 -#define RIGHT_HAND_PIN GPIO_NUM_12 - -#define AUDIO_INPUT_SAMPLE_RATE 16000 -#define AUDIO_OUTPUT_SAMPLE_RATE 24000 -#define AUDIO_I2S_METHOD_SIMPLEX - -#define AUDIO_I2S_MIC_GPIO_WS GPIO_NUM_4 -#define AUDIO_I2S_MIC_GPIO_SCK GPIO_NUM_5 -#define AUDIO_I2S_MIC_GPIO_DIN GPIO_NUM_6 -#define AUDIO_I2S_SPK_GPIO_DOUT GPIO_NUM_7 -#define AUDIO_I2S_SPK_GPIO_BCLK GPIO_NUM_15 -#define AUDIO_I2S_SPK_GPIO_LRCK GPIO_NUM_16 - -#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_3 -#define DISPLAY_MOSI_PIN GPIO_NUM_10 -#define DISPLAY_CLK_PIN GPIO_NUM_9 -#define DISPLAY_DC_PIN GPIO_NUM_46 -#define DISPLAY_RST_PIN GPIO_NUM_11 -#define DISPLAY_CS_PIN GPIO_NUM_12 - -#endif - #define LCD_TYPE_ST7789_SERIAL #define DISPLAY_WIDTH 240 #define DISPLAY_HEIGHT 240 @@ -103,5 +154,4 @@ #define BOOT_BUTTON_GPIO GPIO_NUM_0 - -#endif // _BOARD_CONFIG_H_ +#endif diff --git a/main/boards/otto-robot/config.json b/main/boards/otto-robot/config.json index fc65140b..a32bc814 100644 --- a/main/boards/otto-robot/config.json +++ b/main/boards/otto-robot/config.json @@ -4,17 +4,10 @@ { "name": "otto-robot", "sdkconfig_append": [ - "CONFIG_HTTPD_WS_SUPPORT=y" - ] - }, - { - "name": "otto-robot-camera", - "sdkconfig_append": [ - "CONFIG_OTTO_ROBOT_USE_CAMERA=y", + "CONFIG_HTTPD_WS_SUPPORT=y", "CONFIG_CAMERA_OV2640=y", "CONFIG_CAMERA_OV2640_AUTO_DETECT_DVP_INTERFACE_SENSOR=y", - "CONFIG_CAMERA_OV2640_DVP_YUV422_240X240_25FPS=y", - "CONFIG_HTTPD_WS_SUPPORT=y" + "CONFIG_CAMERA_OV2640_DVP_YUV422_240X240_25FPS=y" ] } ] diff --git a/main/boards/otto-robot/otto_controller.cc b/main/boards/otto-robot/otto_controller.cc index f4a2f551..cb041013 100644 --- a/main/boards/otto-robot/otto_controller.cc +++ b/main/boards/otto-robot/otto_controller.cc @@ -232,25 +232,7 @@ private: } } } - - // 安全检查:防止左右腿脚同时做大幅度动作 - const int LARGE_MOVEMENT_THRESHOLD = 40; // 大幅度动作阈值:40度 - bool left_leg_large = abs(servo_target[LEFT_LEG] - current_positions[LEFT_LEG]) >= LARGE_MOVEMENT_THRESHOLD; - bool right_leg_large = abs(servo_target[RIGHT_LEG] - current_positions[RIGHT_LEG]) >= LARGE_MOVEMENT_THRESHOLD; - bool left_foot_large = abs(servo_target[LEFT_FOOT] - current_positions[LEFT_FOOT]) >= LARGE_MOVEMENT_THRESHOLD; - bool right_foot_large = abs(servo_target[RIGHT_FOOT] - current_positions[RIGHT_FOOT]) >= LARGE_MOVEMENT_THRESHOLD; - - if (left_leg_large && right_leg_large) { - ESP_LOGW(TAG, "检测到左右腿同时大幅度动作,限制右腿动作"); - // 保持右腿在原位置 - servo_target[RIGHT_LEG] = current_positions[RIGHT_LEG]; - } - if (left_foot_large && right_foot_large) { - ESP_LOGW(TAG, "检测到左右脚同时大幅度动作,限制右脚动作"); - // 保持右脚在原位置 - servo_target[RIGHT_FOOT] = current_positions[RIGHT_FOOT]; - } - + // 获取移动速度(短键名 "v",默认1000毫秒) int speed = 1000; cJSON* speed_item = cJSON_GetObjectItem(action_item, "v"); @@ -511,12 +493,22 @@ private: } public: - OttoController() { - otto_.Init(LEFT_LEG_PIN, RIGHT_LEG_PIN, LEFT_FOOT_PIN, RIGHT_FOOT_PIN, LEFT_HAND_PIN, - RIGHT_HAND_PIN); + OttoController(const HardwareConfig& hw_config) { + otto_.Init( + hw_config.left_leg_pin, + hw_config.right_leg_pin, + hw_config.left_foot_pin, + hw_config.right_foot_pin, + hw_config.left_hand_pin, + hw_config.right_hand_pin + ); - has_hands_ = (LEFT_HAND_PIN != -1 && RIGHT_HAND_PIN != -1); + has_hands_ = (hw_config.left_hand_pin != GPIO_NUM_NC && hw_config.right_hand_pin != GPIO_NUM_NC); ESP_LOGI(TAG, "Otto机器人初始化%s手部舵机", has_hands_ ? "带" : "不带"); + ESP_LOGI(TAG, "舵机引脚配置: LL=%d, RL=%d, LF=%d, RF=%d, LH=%d, RH=%d", + hw_config.left_leg_pin, hw_config.right_leg_pin, + hw_config.left_foot_pin, hw_config.right_foot_pin, + hw_config.left_hand_pin, hw_config.right_hand_pin); LoadTrimsFromNVS(); @@ -849,9 +841,9 @@ public: static OttoController* g_otto_controller = nullptr; -void InitializeOttoController() { +void InitializeOttoController(const HardwareConfig& hw_config) { if (g_otto_controller == nullptr) { - g_otto_controller = new OttoController(); + g_otto_controller = new OttoController(hw_config); ESP_LOGI(TAG, "Otto控制器已初始化并注册MCP工具"); } } diff --git a/main/boards/otto-robot/otto_robot.cc b/main/boards/otto-robot/otto_robot.cc index 1a3644a4..210e3904 100644 --- a/main/boards/otto-robot/otto_robot.cc +++ b/main/boards/otto-robot/otto_robot.cc @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -25,7 +26,7 @@ #define TAG "OttoRobot" -extern void InitializeOttoController(); +extern void InitializeOttoController(const HardwareConfig& hw_config); class OttoRobot : public WifiBoard { private: @@ -33,20 +34,105 @@ private: PowerManager* power_manager_; Button boot_button_; WebSocketControlServer* ws_control_server_; -#if CONFIG_OTTO_ROBOT_USE_CAMERA + HardwareConfig hw_config_; + AudioCodec* audio_codec_; i2c_master_bus_handle_t i2c_bus_; Esp32Camera *camera_; -#endif + bool has_camera_; + + bool DetectHardwareVersion() { + ledc_timer_config_t ledc_timer = { + .speed_mode = LEDC_LOW_SPEED_MODE, + .duty_resolution = LEDC_TIMER_2_BIT, + .timer_num = LEDC_TIMER, + .freq_hz = CAMERA_XCLK_FREQ, + .clk_cfg = LEDC_AUTO_CLK, + }; + esp_err_t ret = ledc_timer_config(&ledc_timer); + if (ret != ESP_OK) { + return false; + } + + ledc_channel_config_t ledc_channel = { + .gpio_num = CAMERA_XCLK, + .speed_mode = LEDC_LOW_SPEED_MODE, + .channel = LEDC_CHANNEL, + .intr_type = LEDC_INTR_DISABLE, + .timer_sel = LEDC_TIMER, + .duty = 2, + .hpoint = 0, + }; + ret = ledc_channel_config(&ledc_channel); + if (ret != ESP_OK) { + return false; + } + + vTaskDelay(pdMS_TO_TICKS(100)); + i2c_master_bus_config_t i2c_bus_cfg = { + .i2c_port = I2C_NUM_0, + .sda_io_num = CAMERA_VERSION_CONFIG.i2c_sda_pin, + .scl_io_num = CAMERA_VERSION_CONFIG.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, + }, + }; + + ret = i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_); + if (ret != ESP_OK) { + ledc_stop(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL, 0); + return false; + } + const uint8_t camera_addresses[] = {0x30, 0x3C, 0x21, 0x60}; + bool camera_found = false; + + for (size_t i = 0; i < sizeof(camera_addresses); i++) { + uint8_t addr = camera_addresses[i]; + i2c_device_config_t dev_cfg = { + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + .device_address = addr, + .scl_speed_hz = 100000, + }; + + i2c_master_dev_handle_t dev_handle; + ret = i2c_master_bus_add_device(i2c_bus_, &dev_cfg, &dev_handle); + if (ret == ESP_OK) { + uint8_t reg_addr = 0x0A; + uint8_t data[2]; + ret = i2c_master_transmit_receive(dev_handle, ®_addr, 1, data, 2, 200); + if (ret == ESP_OK) { + camera_found = true; + i2c_master_bus_rm_device(dev_handle); + break; + } + i2c_master_bus_rm_device(dev_handle); + } + } + + if (!camera_found) { + i2c_del_master_bus(i2c_bus_); + i2c_bus_ = nullptr; + ledc_stop(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL, 0); + } + return camera_found; + } + void InitializePowerManager() { - power_manager_ = - new PowerManager(POWER_CHARGE_DETECT_PIN, POWER_ADC_UNIT, POWER_ADC_CHANNEL); + power_manager_ = new PowerManager( + hw_config_.power_charge_detect_pin, + hw_config_.power_adc_unit, + hw_config_.power_adc_channel + ); } void InitializeSpi() { spi_bus_config_t buscfg = {}; - buscfg.mosi_io_num = DISPLAY_MOSI_PIN; + buscfg.mosi_io_num = hw_config_.display_mosi_pin; buscfg.miso_io_num = GPIO_NUM_NC; - buscfg.sclk_io_num = DISPLAY_CLK_PIN; + buscfg.sclk_io_num = hw_config_.display_clk_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); @@ -56,10 +142,9 @@ private: void InitializeLcdDisplay() { 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 = DISPLAY_CS_PIN; - io_config.dc_gpio_num = DISPLAY_DC_PIN; + io_config.cs_gpio_num = hw_config_.display_cs_pin; + io_config.dc_gpio_num = hw_config_.display_dc_pin; io_config.spi_mode = DISPLAY_SPI_MODE; io_config.pclk_hz = 40 * 1000 * 1000; io_config.trans_queue_depth = 10; @@ -67,9 +152,8 @@ private: 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 = DISPLAY_RST_PIN; + panel_config.reset_gpio_num = hw_config_.display_rst_pin; panel_config.rgb_ele_order = DISPLAY_RGB_ORDER; panel_config.bits_per_pixel = 16; @@ -99,19 +183,21 @@ private: } void InitializeOttoController() { - ESP_LOGI(TAG, "初始化Otto机器人MCP控制器"); - ::InitializeOttoController(); + ::InitializeOttoController(hw_config_); } + +public: + const HardwareConfig& GetHardwareConfig() const { + return hw_config_; + } + +private: void InitializeWebSocketControlServer() { - ESP_LOGI(TAG, "初始化WebSocket控制服务器"); ws_control_server_ = new WebSocketControlServer(); if (!ws_control_server_->Start(8080)) { - ESP_LOGE(TAG, "Failed to start WebSocket control server"); delete ws_control_server_; ws_control_server_ = nullptr; - } else { - ESP_LOGI(TAG, "WebSocket control server started on port 8080"); } } @@ -122,116 +208,139 @@ private: InitializeWebSocketControlServer(); } -#if CONFIG_OTTO_ROBOT_USE_CAMERA - void InitializeI2c() { - // Initialize I2C peripheral - i2c_master_bus_config_t i2c_bus_cfg = { - .i2c_port = I2C_NUM_0, - .sda_io_num = I2C_SDA_PIN, - .scl_io_num = 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, + bool InitializeCamera() { + if (!has_camera_ || i2c_bus_ == nullptr) { + return false; + } + + try { + static esp_cam_ctlr_dvp_pin_config_t dvp_pin_config = { + .data_width = CAM_CTLR_DATA_WIDTH_8, + .data_io = { + [0] = CAMERA_D0, + [1] = CAMERA_D1, + [2] = CAMERA_D2, + [3] = CAMERA_D3, + [4] = CAMERA_D4, + [5] = CAMERA_D5, + [6] = CAMERA_D6, + [7] = CAMERA_D7, }, - }; - ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_)); + .vsync_io = CAMERA_VSYNC, + .de_io = CAMERA_HSYNC, + .pclk_io = CAMERA_PCLK, + .xclk_io = CAMERA_XCLK, + }; + + esp_video_init_sccb_config_t sccb_config = { + .init_sccb = false, + .i2c_handle = i2c_bus_, + .freq = 100000, + }; + + esp_video_init_dvp_config_t dvp_config = { + .sccb_config = sccb_config, + .reset_pin = CAMERA_RESET, + .pwdn_pin = CAMERA_PWDN, + .dvp_pin = dvp_pin_config, + .xclk_freq = CAMERA_XCLK_FREQ, + }; + + esp_video_init_config_t video_config = { + .dvp = &dvp_config, + }; + + camera_ = new Esp32Camera(video_config); + camera_->SetVFlip(true); + return true; + } catch (...) { + camera_ = nullptr; + return false; + } + } + + void InitializeAudioCodec() { + if (hw_config_.audio_use_simplex) { + audio_codec_ = new NoAudioCodecSimplex( + hw_config_.audio_input_sample_rate, + hw_config_.audio_output_sample_rate, + hw_config_.audio_i2s_spk_gpio_bclk, + hw_config_.audio_i2s_spk_gpio_lrck, + hw_config_.audio_i2s_spk_gpio_dout, + hw_config_.audio_i2s_mic_gpio_sck, + hw_config_.audio_i2s_mic_gpio_ws, + hw_config_.audio_i2s_mic_gpio_din + ); + } else { + audio_codec_ = new NoAudioCodecDuplex( + hw_config_.audio_input_sample_rate, + hw_config_.audio_output_sample_rate, + hw_config_.audio_i2s_gpio_bclk, + hw_config_.audio_i2s_gpio_ws, + hw_config_.audio_i2s_gpio_dout, + hw_config_.audio_i2s_gpio_din + ); + } } - void InitializeCamera() { - // DVP pin configuration - static esp_cam_ctlr_dvp_pin_config_t dvp_pin_config = { - .data_width = CAM_CTLR_DATA_WIDTH_8, - .data_io = { - [0] = CAMERA_D0, - [1] = CAMERA_D1, - [2] = CAMERA_D2, - [3] = CAMERA_D3, - [4] = CAMERA_D4, - [5] = CAMERA_D5, - [6] = CAMERA_D6, - [7] = CAMERA_D7, - }, - .vsync_io = CAMERA_VSYNC, - .de_io = CAMERA_HSYNC, - .pclk_io = CAMERA_PCLK, - .xclk_io = CAMERA_XCLK, - }; - - // 复用 I2C 总线 - esp_video_init_sccb_config_t sccb_config = { - .init_sccb = false, // 不初始化新的 SCCB,使用现有的 I2C 总线 - .i2c_handle = i2c_bus_, // 使用现有的 I2C 总线句柄 - .freq = 100000, // 100kHz - }; - - // DVP configuration - esp_video_init_dvp_config_t dvp_config = { - .sccb_config = sccb_config, - .reset_pin = CAMERA_RESET, - .pwdn_pin = CAMERA_PWDN, - .dvp_pin = dvp_pin_config, - .xclk_freq = CAMERA_XCLK_FREQ, - }; - - // Main video configuration - esp_video_init_config_t video_config = { - .dvp = &dvp_config, - }; - - camera_ = new Esp32Camera(video_config); - // camera_->SetHMirror(true); - camera_->SetVFlip(true); - } -#endif public: - OttoRobot() : boot_button_(BOOT_BUTTON_GPIO) { + OttoRobot() : boot_button_(BOOT_BUTTON_GPIO), + audio_codec_(nullptr), + i2c_bus_(nullptr), + camera_(nullptr), + has_camera_(false) { + + has_camera_ = DetectHardwareVersion(); + + if (has_camera_) + hw_config_ = CAMERA_VERSION_CONFIG; + else + hw_config_ = NON_CAMERA_VERSION_CONFIG; + + InitializeSpi(); InitializeLcdDisplay(); -#if CONFIG_OTTO_ROBOT_USE_CAMERA - InitializeI2c(); - InitializeCamera(); -#endif InitializeButtons(); InitializePowerManager(); + InitializeAudioCodec(); + + if (has_camera_) { + if (!InitializeCamera()) { + has_camera_ = false; + } + } + InitializeOttoController(); ws_control_server_ = nullptr; GetBacklight()->RestoreBrightness(); } virtual AudioCodec *GetAudioCodec() override { -#ifdef AUDIO_I2S_METHOD_SIMPLEX - static NoAudioCodecSimplex audio_codec( - AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE, - AUDIO_I2S_SPK_GPIO_BCLK, AUDIO_I2S_SPK_GPIO_LRCK, - AUDIO_I2S_SPK_GPIO_DOUT, AUDIO_I2S_MIC_GPIO_SCK, AUDIO_I2S_MIC_GPIO_WS, - AUDIO_I2S_MIC_GPIO_DIN); -#else - static NoAudioCodecDuplex audio_codec( - AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE, AUDIO_I2S_GPIO_BCLK, - AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN); -#endif - return &audio_codec; + return audio_codec_; } - virtual Display* GetDisplay() override { return display_; } + virtual Display* GetDisplay() override { + return display_; + } virtual Backlight* GetBacklight() override { - static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT); - return &backlight; + static PwmBacklight* backlight = nullptr; + if (backlight == nullptr) { + backlight = new PwmBacklight(hw_config_.display_backlight_pin, DISPLAY_BACKLIGHT_OUTPUT_INVERT); + } + return backlight; } + virtual bool GetBatteryLevel(int& level, bool& charging, bool& discharging) override { charging = power_manager_->IsCharging(); discharging = !charging; level = power_manager_->GetBatteryLevel(); return true; } -#if CONFIG_OTTO_ROBOT_USE_CAMERA - virtual Camera *GetCamera() override { return camera_; } -#endif + + virtual Camera *GetCamera() override { + return has_camera_ ? camera_ : nullptr; + } }; DECLARE_BOARD(OttoRobot);