Refactor Otto Robot configuration and initialization (#1534)

* otto v1.4.0 MCP

1.使用MCP协议控制机器人
2.gif继承lcdDisplay,避免修改lcdDisplay

* otto v1.4.1 gif as components

gif as components

* electronBot v1.1.0 mcp

1.增加electronBot支持
2.mcp协议
3.gif 作为组件
4.display子类

* 规范代码

1.规范代码
2.修复切换主题死机bug

* fix(ota): 修复 ottoRobot和electronBot OTA 升级崩溃问题 bug

* 1.增加robot舵机初始位置校准
2.fix(mcp_sever) 超出范围异常捕获类型  bug

* refactor: Update Electron and Otto emoji display implementations

- Removed GIF selection from Kconfig for Electron and Otto boards.
- Updated Electron and Otto bot versions to 2.0.4 in their respective config files.
- Refactored emoji display classes to utilize EmojiCollection for managing emojis.
- Enhanced chat label setup and status display functionality in both classes.
- Cleaned up unused code and improved initialization logging for emoji displays.

* Rename OTTO_ICON_FONT.c to otto_icon_font.c

* Rename OTTO_ICON_FONT.c to otto_icon_font.c

* refactor: Update Otto emoji display configurations and functionalities

- Changed chat label text mode to circular scrolling for both Otto and Electron emoji displays.
- Bumped Otto robot version to 2.0.5 in the configuration file.
- Added new actions for Otto robot including Sit, WhirlwindLeg, Fitness, Greeting, Shy, RadioCalisthenics, MagicCircle, and Showcase.
- Enhanced servo sequence handling and added support for executing custom servo sequences.
- Improved logging and error handling for servo sequence execution.

* refactor: Update chat label long mode for Electron and Otto emoji displays

- Changed chat label text mode from wrap to circular scrolling for both Electron and Otto emoji displays.
- Improved consistency in chat label setup across both implementations.

* Update Otto robot README with new actions and parameters

* Update Otto controller parameters for oscillation settings

- Changed default oscillation period from 500ms to 300ms.
- Increased default steps from 5.0 to 8.0.
- Updated default amplitude from 20 degrees to 0 degrees.
- Enhanced documentation with new examples for oscillation modes and sequences.

* Fix default amplitude initialization in Otto controller to use a single zero instead of two digits.

* chore: update txp666/otto-emoji-gif-component version to 1.0.3 in idf_component.yml

* Refactor Otto controller
- Consolidated movement actions into a unified tool for the Otto robot, allowing for a single action command with various parameters.
- Removed individual movement tools (walk, turn, jump, etc.) and replaced them with a more flexible action system.

* Enhance Otto robot functionality by adding WebSocket control server and IP address retrieval feature. Updated config to support WebSocket, and revised README to include new control options and usage examples.

* Add camera support for Otto Robot board

- Introduced configuration option to enable the Otto Robot camera in Kconfig.
- Updated config.h to define camera-related GPIO pins and settings.
- Modified config.json to include camera configuration.
- Enhanced otto_robot.cc to initialize I2C and camera components when the camera is enabled.
- Adjusted power_manager.h to manage battery updates during camera operations.
- Removed unused SetupChatLabel method from OttoEmojiDisplay class.

* Refactor Otto Robot configuration and initialization

- Removed the camera configuration option from Kconfig and related code.
- Introduced a new HardwareConfig struct to encapsulate hardware pin definitions and settings.
- Updated config.h to define camera and non-camera configurations using the new struct.
- Refactored otto_controller.cc and otto_robot.cc to utilize the HardwareConfig struct for initialization.
- Enhanced camera detection and initialization logic based on hardware version.
- Improved audio codec initialization based on configuration settings.
This commit is contained in:
小鹏
2025-12-08 20:55:23 +08:00
committed by GitHub
parent f9de29519b
commit 11c79a7003
5 changed files with 347 additions and 210 deletions

View File

@@ -690,13 +690,6 @@ config RECEIVE_CUSTOM_MESSAGE
help help
Enable custom message reception, allow the device to receive custom messages from the server (preferably through the MQTT protocol) 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" menu "Camera Configuration"
depends on !IDF_TARGET_ESP32 depends on !IDF_TARGET_ESP32

View File

@@ -2,36 +2,124 @@
#define _BOARD_CONFIG_H_ #define _BOARD_CONFIG_H_
#include <driver/gpio.h> #include <driver/gpio.h>
#include <driver/adc.h>
#if CONFIG_OTTO_ROBOT_USE_CAMERA struct HardwareConfig {
#define POWER_CHARGE_DETECT_PIN GPIO_NUM_NC gpio_num_t power_charge_detect_pin;
#define POWER_ADC_UNIT ADC_UNIT_1 adc_unit_t power_adc_unit;
#define POWER_ADC_CHANNEL ADC_CHANNEL_1 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 constexpr HardwareConfig CAMERA_VERSION_CONFIG = {
#define RIGHT_FOOT_PIN GPIO_NUM_44 .power_charge_detect_pin = GPIO_NUM_NC,
#define LEFT_LEG_PIN GPIO_NUM_5 .power_adc_unit = ADC_UNIT_1,
#define LEFT_FOOT_PIN GPIO_NUM_6 .power_adc_channel = ADC_CHANNEL_1,
#define LEFT_HAND_PIN GPIO_NUM_4
#define RIGHT_HAND_PIN GPIO_NUM_7 .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 constexpr HardwareConfig NON_CAMERA_VERSION_CONFIG = {
#define AUDIO_OUTPUT_SAMPLE_RATE 16000 .power_charge_detect_pin = GPIO_NUM_21,
.power_adc_unit = ADC_UNIT_2,
#define AUDIO_I2S_GPIO_WS GPIO_NUM_40 .power_adc_channel = ADC_CHANNEL_3,
#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_42
#define AUDIO_I2S_GPIO_DIN GPIO_NUM_41 .right_leg_pin = GPIO_NUM_39,
#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_39 .right_foot_pin = GPIO_NUM_38,
.left_leg_pin = GPIO_NUM_17,
#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_38 .left_foot_pin = GPIO_NUM_18,
#define DISPLAY_MOSI_PIN GPIO_NUM_45 .left_hand_pin = GPIO_NUM_8,
#define DISPLAY_CLK_PIN GPIO_NUM_48 .right_hand_pin = GPIO_NUM_12,
#define DISPLAY_DC_PIN GPIO_NUM_47
#define DISPLAY_RST_PIN GPIO_NUM_1 .audio_input_sample_rate = 16000,
#define DISPLAY_CS_PIN GPIO_NUM_NC .audio_output_sample_rate = 24000,
/* Camera PINs*/ .audio_use_simplex = true,
#define I2C_SDA_PIN GPIO_NUM_15
#define I2C_SCL_PIN GPIO_NUM_16 .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_XCLK (GPIO_NUM_3)
#define CAMERA_PCLK (GPIO_NUM_10) #define CAMERA_PCLK (GPIO_NUM_10)
@@ -45,49 +133,12 @@
#define CAMERA_D5 (GPIO_NUM_9) #define CAMERA_D5 (GPIO_NUM_9)
#define CAMERA_D6 (GPIO_NUM_46) #define CAMERA_D6 (GPIO_NUM_46)
#define CAMERA_D7 (GPIO_NUM_8) #define CAMERA_D7 (GPIO_NUM_8)
#define CAMERA_PWDN (GPIO_NUM_NC) #define CAMERA_PWDN (GPIO_NUM_NC)
#define CAMERA_RESET (GPIO_NUM_NC) #define CAMERA_RESET (GPIO_NUM_NC)
#define CAMERA_XCLK_FREQ (16000000) #define CAMERA_XCLK_FREQ (16000000)
#define LEDC_TIMER (LEDC_TIMER_0) #define LEDC_TIMER (LEDC_TIMER_0)
#define LEDC_CHANNEL (LEDC_CHANNEL_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 LCD_TYPE_ST7789_SERIAL
#define DISPLAY_WIDTH 240 #define DISPLAY_WIDTH 240
#define DISPLAY_HEIGHT 240 #define DISPLAY_HEIGHT 240
@@ -103,5 +154,4 @@
#define BOOT_BUTTON_GPIO GPIO_NUM_0 #define BOOT_BUTTON_GPIO GPIO_NUM_0
#endif
#endif // _BOARD_CONFIG_H_

View File

@@ -4,17 +4,10 @@
{ {
"name": "otto-robot", "name": "otto-robot",
"sdkconfig_append": [ "sdkconfig_append": [
"CONFIG_HTTPD_WS_SUPPORT=y" "CONFIG_HTTPD_WS_SUPPORT=y",
]
},
{
"name": "otto-robot-camera",
"sdkconfig_append": [
"CONFIG_OTTO_ROBOT_USE_CAMERA=y",
"CONFIG_CAMERA_OV2640=y", "CONFIG_CAMERA_OV2640=y",
"CONFIG_CAMERA_OV2640_AUTO_DETECT_DVP_INTERFACE_SENSOR=y", "CONFIG_CAMERA_OV2640_AUTO_DETECT_DVP_INTERFACE_SENSOR=y",
"CONFIG_CAMERA_OV2640_DVP_YUV422_240X240_25FPS=y", "CONFIG_CAMERA_OV2640_DVP_YUV422_240X240_25FPS=y"
"CONFIG_HTTPD_WS_SUPPORT=y"
] ]
} }
] ]

View File

@@ -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毫秒 // 获取移动速度(短键名 "v"默认1000毫秒
int speed = 1000; int speed = 1000;
cJSON* speed_item = cJSON_GetObjectItem(action_item, "v"); cJSON* speed_item = cJSON_GetObjectItem(action_item, "v");
@@ -511,12 +493,22 @@ private:
} }
public: public:
OttoController() { OttoController(const HardwareConfig& hw_config) {
otto_.Init(LEFT_LEG_PIN, RIGHT_LEG_PIN, LEFT_FOOT_PIN, RIGHT_FOOT_PIN, LEFT_HAND_PIN, otto_.Init(
RIGHT_HAND_PIN); 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, "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(); LoadTrimsFromNVS();
@@ -849,9 +841,9 @@ public:
static OttoController* g_otto_controller = nullptr; static OttoController* g_otto_controller = nullptr;
void InitializeOttoController() { void InitializeOttoController(const HardwareConfig& hw_config) {
if (g_otto_controller == nullptr) { if (g_otto_controller == nullptr) {
g_otto_controller = new OttoController(); g_otto_controller = new OttoController(hw_config);
ESP_LOGI(TAG, "Otto控制器已初始化并注册MCP工具"); ESP_LOGI(TAG, "Otto控制器已初始化并注册MCP工具");
} }
} }

View File

@@ -1,5 +1,6 @@
#include <driver/i2c_master.h> #include <driver/i2c_master.h>
#include <driver/spi_common.h> #include <driver/spi_common.h>
#include <driver/ledc.h>
#include <esp_lcd_panel_io.h> #include <esp_lcd_panel_io.h>
#include <esp_lcd_panel_ops.h> #include <esp_lcd_panel_ops.h>
#include <esp_lcd_panel_vendor.h> #include <esp_lcd_panel_vendor.h>
@@ -25,7 +26,7 @@
#define TAG "OttoRobot" #define TAG "OttoRobot"
extern void InitializeOttoController(); extern void InitializeOttoController(const HardwareConfig& hw_config);
class OttoRobot : public WifiBoard { class OttoRobot : public WifiBoard {
private: private:
@@ -33,20 +34,105 @@ private:
PowerManager* power_manager_; PowerManager* power_manager_;
Button boot_button_; Button boot_button_;
WebSocketControlServer* ws_control_server_; WebSocketControlServer* ws_control_server_;
#if CONFIG_OTTO_ROBOT_USE_CAMERA HardwareConfig hw_config_;
AudioCodec* audio_codec_;
i2c_master_bus_handle_t i2c_bus_; i2c_master_bus_handle_t i2c_bus_;
Esp32Camera *camera_; 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, &reg_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() { void InitializePowerManager() {
power_manager_ = power_manager_ = new PowerManager(
new PowerManager(POWER_CHARGE_DETECT_PIN, POWER_ADC_UNIT, POWER_ADC_CHANNEL); hw_config_.power_charge_detect_pin,
hw_config_.power_adc_unit,
hw_config_.power_adc_channel
);
} }
void InitializeSpi() { void InitializeSpi() {
spi_bus_config_t buscfg = {}; 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.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.quadwp_io_num = GPIO_NUM_NC;
buscfg.quadhd_io_num = GPIO_NUM_NC; buscfg.quadhd_io_num = GPIO_NUM_NC;
buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t); buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t);
@@ -56,10 +142,9 @@ private:
void InitializeLcdDisplay() { void InitializeLcdDisplay() {
esp_lcd_panel_io_handle_t panel_io = nullptr; esp_lcd_panel_io_handle_t panel_io = nullptr;
esp_lcd_panel_handle_t panel = nullptr; esp_lcd_panel_handle_t panel = nullptr;
ESP_LOGD(TAG, "Install panel IO");
esp_lcd_panel_io_spi_config_t io_config = {}; esp_lcd_panel_io_spi_config_t io_config = {};
io_config.cs_gpio_num = DISPLAY_CS_PIN; io_config.cs_gpio_num = hw_config_.display_cs_pin;
io_config.dc_gpio_num = DISPLAY_DC_PIN; io_config.dc_gpio_num = hw_config_.display_dc_pin;
io_config.spi_mode = DISPLAY_SPI_MODE; io_config.spi_mode = DISPLAY_SPI_MODE;
io_config.pclk_hz = 40 * 1000 * 1000; io_config.pclk_hz = 40 * 1000 * 1000;
io_config.trans_queue_depth = 10; io_config.trans_queue_depth = 10;
@@ -67,9 +152,8 @@ private:
io_config.lcd_param_bits = 8; io_config.lcd_param_bits = 8;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &panel_io)); 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 = {}; 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.rgb_ele_order = DISPLAY_RGB_ORDER;
panel_config.bits_per_pixel = 16; panel_config.bits_per_pixel = 16;
@@ -99,19 +183,21 @@ private:
} }
void InitializeOttoController() { void InitializeOttoController() {
ESP_LOGI(TAG, "初始化Otto机器人MCP控制器"); ::InitializeOttoController(hw_config_);
::InitializeOttoController();
} }
public:
const HardwareConfig& GetHardwareConfig() const {
return hw_config_;
}
private:
void InitializeWebSocketControlServer() { void InitializeWebSocketControlServer() {
ESP_LOGI(TAG, "初始化WebSocket控制服务器");
ws_control_server_ = new WebSocketControlServer(); ws_control_server_ = new WebSocketControlServer();
if (!ws_control_server_->Start(8080)) { if (!ws_control_server_->Start(8080)) {
ESP_LOGE(TAG, "Failed to start WebSocket control server");
delete ws_control_server_; delete ws_control_server_;
ws_control_server_ = nullptr; ws_control_server_ = nullptr;
} else {
ESP_LOGI(TAG, "WebSocket control server started on port 8080");
} }
} }
@@ -122,116 +208,139 @@ private:
InitializeWebSocketControlServer(); InitializeWebSocketControlServer();
} }
#if CONFIG_OTTO_ROBOT_USE_CAMERA bool InitializeCamera() {
void InitializeI2c() { if (!has_camera_ || i2c_bus_ == nullptr) {
// Initialize I2C peripheral return false;
i2c_master_bus_config_t i2c_bus_cfg = { }
.i2c_port = I2C_NUM_0,
.sda_io_num = I2C_SDA_PIN, try {
.scl_io_num = I2C_SCL_PIN, static esp_cam_ctlr_dvp_pin_config_t dvp_pin_config = {
.clk_source = I2C_CLK_SRC_DEFAULT, .data_width = CAM_CTLR_DATA_WIDTH_8,
.glitch_ignore_cnt = 7, .data_io = {
.intr_priority = 0, [0] = CAMERA_D0,
.trans_queue_depth = 0, [1] = CAMERA_D1,
.flags = [2] = CAMERA_D2,
{ [3] = CAMERA_D3,
.enable_internal_pullup = 1, [4] = CAMERA_D4,
[5] = CAMERA_D5,
[6] = CAMERA_D6,
[7] = CAMERA_D7,
}, },
}; .vsync_io = CAMERA_VSYNC,
ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_)); .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: 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(); InitializeSpi();
InitializeLcdDisplay(); InitializeLcdDisplay();
#if CONFIG_OTTO_ROBOT_USE_CAMERA
InitializeI2c();
InitializeCamera();
#endif
InitializeButtons(); InitializeButtons();
InitializePowerManager(); InitializePowerManager();
InitializeAudioCodec();
if (has_camera_) {
if (!InitializeCamera()) {
has_camera_ = false;
}
}
InitializeOttoController(); InitializeOttoController();
ws_control_server_ = nullptr; ws_control_server_ = nullptr;
GetBacklight()->RestoreBrightness(); GetBacklight()->RestoreBrightness();
} }
virtual AudioCodec *GetAudioCodec() override { virtual AudioCodec *GetAudioCodec() override {
#ifdef AUDIO_I2S_METHOD_SIMPLEX return audio_codec_;
static NoAudioCodecSimplex audio_codec(
AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE,
AUDIO_I2S_SPK_GPIO_BCLK, AUDIO_I2S_SPK_GPIO_LRCK,
AUDIO_I2S_SPK_GPIO_DOUT, AUDIO_I2S_MIC_GPIO_SCK, AUDIO_I2S_MIC_GPIO_WS,
AUDIO_I2S_MIC_GPIO_DIN);
#else
static NoAudioCodecDuplex audio_codec(
AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE, AUDIO_I2S_GPIO_BCLK,
AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN);
#endif
return &audio_codec;
} }
virtual Display* GetDisplay() override { return display_; } virtual Display* GetDisplay() override {
return display_;
}
virtual Backlight* GetBacklight() override { virtual Backlight* GetBacklight() override {
static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT); static PwmBacklight* backlight = nullptr;
return &backlight; 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 { virtual bool GetBatteryLevel(int& level, bool& charging, bool& discharging) override {
charging = power_manager_->IsCharging(); charging = power_manager_->IsCharging();
discharging = !charging; discharging = !charging;
level = power_manager_->GetBatteryLevel(); level = power_manager_->GetBatteryLevel();
return true; return true;
} }
#if CONFIG_OTTO_ROBOT_USE_CAMERA
virtual Camera *GetCamera() override { return camera_; } virtual Camera *GetCamera() override {
#endif return has_camera_ ? camera_ : nullptr;
}
}; };
DECLARE_BOARD(OttoRobot); DECLARE_BOARD(OttoRobot);