diff --git a/main/boards/esp-hi/README.md b/main/boards/esp-hi/README.md
new file mode 100644
index 00000000..8921f1f7
--- /dev/null
+++ b/main/boards/esp-hi/README.md
@@ -0,0 +1,51 @@
+# ESP-Hi
+
+## 简介
+
+
+
+ESP-Hi 是 ESP Friends 开源的一款基于 ESP32C3 的超**低成本** AI 对话机器人。ESP-Hi 集成了一个0.96寸的彩屏,用于显示表情,**机器狗已实现数十种动作**。通过对 ESP32-C3 外设的充分挖掘,仅需最少的板级硬件即可实现拾音和发声,同步优化了软件,降低内存与 Flash 占用,在资源受限的情况下同时实现了**唤醒词检测**与多种外设驱动。硬件详情等可查看[立创开源项目](https://oshwhub.com/esp-college/esp-hi)。
+
+## WebUI
+
+ESP-Hi x 小智内置了一个控制身体运动的 WebUI,请将手机与 ESP-Hi 连接到同一个 Wi-Fi 下,手机访问 `http://esp-hi.local/` 以使用。
+
+如需禁用,请取消 `ESP_HI_WEB_CONTROL_ENABLED`,即取消勾选 `Component config` → `Servo Dog Configuration` → `Web Control` → `Enable ESP-HI Web Control`。
+
+## 配置、编译命令
+
+由于 ESP-Hi 需要配置较多的 sdkconfig 选项,推荐使用编译脚本编译。
+
+**编译**
+
+```bash
+python ./scripts/release.py esp-hi
+```
+
+如需手动编译,请参考 `esp-hi/config.json` 修改 menuconfig 对应选项。
+
+**烧录**
+
+```bash
+idf.py flash
+```
+
+
+> [!TIP]
+>
+> **舵机控制会占用 ESP-Hi 的 USB Type-C 接口**,导致无法连接电脑(无法烧录/查看运行日志)。如遇此情况,请按以下提示操作:
+>
+> **烧录**
+>
+> 1. 断开 ESP-Hi 的电源,只留头部,不要连接身体。
+> 2. 按住 ESP-Hi 的按钮并连接电脑。
+>
+> 此时,ESP-Hi (ESP32C3) 应当处于烧录模式,可以使用电脑烧录程序。烧录完成后,可能需要重新插拔电源。
+>
+> **查看 log**
+>
+> 请设置 `CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y`,即 `Component config` → `ESP System Settings` → `Channel for console output` 选择 `USB Serial/JTAG Controller`。这同时会禁用舵机控制功能。
diff --git a/main/boards/esp-hi/config.json b/main/boards/esp-hi/config.json
index f432c446..aaaf5527 100644
--- a/main/boards/esp-hi/config.json
+++ b/main/boards/esp-hi/config.json
@@ -31,7 +31,7 @@
"CONFIG_NEWLIB_NANO_FORMAT=y",
"CONFIG_MMAP_FILE_NAME_LENGTH=25",
"CONFIG_ESP_CONSOLE_NONE=y",
- "CONFIG_IOT_PROTOCOL_XIAOZHI=y"
+ "CONFIG_IOT_PROTOCOL_MCP=y"
]
}
]
diff --git a/main/boards/esp-hi/dog_action extra.cc b/main/boards/esp-hi/dog_action extra.cc
deleted file mode 100644
index 59b3ab84..00000000
--- a/main/boards/esp-hi/dog_action extra.cc
+++ /dev/null
@@ -1,64 +0,0 @@
-#include "iot/thing.h"
-#include "board.h"
-#include "audio_codec.h"
-
-#include
-#include
-#include "servo_dog_ctrl.h"
-
-#define TAG "Message"
-
-namespace iot {
-
-class DogAction_extra : public Thing {
-private:
- bool is_moving_ = false;
-
- void InitializePlayer()
- {
- ESP_LOGI(TAG, "Dog action initialized");
- }
-
-public:
- DogAction_extra() : Thing("DogAction_extra", "机器人扩展动作控制")
- {
- InitializePlayer();
-
- // 定义设备的属性
- properties_.AddBooleanProperty("is_moving", "机器人是否正在移动", [this]() -> bool {
- return is_moving_;
- });
-
- // 定义设备可以被远程执行的指令
- methods_.AddMethod("retract_legs", "机器人收回腿部", ParameterList(), [this](const ParameterList & parameters) {
- is_moving_ = true;
- servo_dog_ctrl_send(DOG_STATE_RETRACT_LEGS, NULL);
- });
-
- methods_.AddMethod("stop", "立即停止机器人当前动作", ParameterList(), [this](const ParameterList & parameters) {
- if (is_moving_) {
- is_moving_ = false;
- servo_dog_ctrl_send(DOG_STATE_IDLE, NULL);
- }
- });
-
- methods_.AddMethod("shake_hand", "机器人做握手动作", ParameterList(), [this](const ParameterList & parameters) {
- is_moving_ = true;
- servo_dog_ctrl_send(DOG_STATE_SHAKE_HAND, NULL);
- });
-
- methods_.AddMethod("shake_back_legs", "机器人伸懒腰", ParameterList(), [this](const ParameterList & parameters) {
- is_moving_ = true;
- servo_dog_ctrl_send(DOG_STATE_SHAKE_BACK_LEGS, NULL);
- });
-
- methods_.AddMethod("jump_forward", "机器人向前跳跃", ParameterList(), [this](const ParameterList & parameters) {
- is_moving_ = true;
- servo_dog_ctrl_send(DOG_STATE_JUMP_FORWARD, NULL);
- });
- }
-};
-
-} // namespace iot
-
-DECLARE_THING(DogAction_extra);
diff --git a/main/boards/esp-hi/dog_action_basic.cc b/main/boards/esp-hi/dog_action_basic.cc
deleted file mode 100644
index 39765d9a..00000000
--- a/main/boards/esp-hi/dog_action_basic.cc
+++ /dev/null
@@ -1,75 +0,0 @@
-#include "iot/thing.h"
-#include "board.h"
-#include "audio_codec.h"
-
-#include
-#include
-#include "servo_dog_ctrl.h"
-
-#define TAG "Message"
-
-namespace iot {
-
-class DogAction_basic : public Thing {
-private:
- bool is_moving_ = false;
-
- void InitializePlayer()
- {
- ESP_LOGI(TAG, "Dog action initialized");
- }
-
-public:
- DogAction_basic() : Thing("DogAction_basic", "机器人基础动作控制")
- {
- InitializePlayer();
-
- // 定义设备的属性
- properties_.AddBooleanProperty("is_moving", "机器人是否正在移动", [this]() -> bool {
- return is_moving_;
- });
-
- // 定义设备可以被远程执行的指令
- methods_.AddMethod("forward", "机器人向前移动", ParameterList(), [this](const ParameterList & parameters) {
- is_moving_ = true;
- servo_dog_ctrl_send(DOG_STATE_FORWARD, NULL);
- });
-
- methods_.AddMethod("backward", "机器人向后移动", ParameterList(), [this](const ParameterList & parameters) {
- is_moving_ = true;
- servo_dog_ctrl_send(DOG_STATE_BACKWARD, NULL);
- });
-
- methods_.AddMethod("sway_back_forth", "机器人做前后摇摆动作", ParameterList(), [this](const ParameterList & parameters) {
- is_moving_ = true;
- servo_dog_ctrl_send(DOG_STATE_SWAY_BACK_FORTH, NULL);
- });
-
- methods_.AddMethod("turn_left", "机器人向左转", ParameterList(), [this](const ParameterList & parameters) {
- is_moving_ = true;
- servo_dog_ctrl_send(DOG_STATE_TURN_LEFT, NULL);
- });
-
- methods_.AddMethod("turn_right", "机器人向右转", ParameterList(), [this](const ParameterList & parameters) {
- is_moving_ = true;
- servo_dog_ctrl_send(DOG_STATE_TURN_RIGHT, NULL);
- });
-
- methods_.AddMethod("lay_down", "机器人趴下", ParameterList(), [this](const ParameterList & parameters) {
- is_moving_ = true;
- servo_dog_ctrl_send(DOG_STATE_LAY_DOWN, NULL);
- });
-
- methods_.AddMethod("sway", "机器人做左右摇摆动作", ParameterList(), [this](const ParameterList & parameters) {
- is_moving_ = true;
- dog_action_args_t args = {
- .repeat_count = 4,
- };
- servo_dog_ctrl_send(DOG_STATE_SWAY, &args);
- });
- }
-};
-
-} // namespace iot
-
-DECLARE_THING(DogAction_basic);
diff --git a/main/boards/esp-hi/dog_light.cc b/main/boards/esp-hi/dog_light.cc
deleted file mode 100644
index 5d6d2fc5..00000000
--- a/main/boards/esp-hi/dog_light.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-#include "iot/thing.h"
-#include "board.h"
-#include "audio_codec.h"
-
-#include
-#include
-#include "driver/rmt_tx.h"
-#include "led_strip.h"
-
-#define TAG "Light"
-
-static led_strip_handle_t led_strip;
-
-static const led_strip_config_t bsp_strip_config = {
- .strip_gpio_num = GPIO_NUM_8,
- .max_leds = 4,
- .led_model = LED_MODEL_WS2812,
- .flags = {
- .invert_out = false
- }
-};
-
-static const led_strip_rmt_config_t bsp_rmt_config = {
- .clk_src = RMT_CLK_SRC_DEFAULT,
- .resolution_hz = 10 * 1000 * 1000,
- .flags = {
- .with_dma = false
- }
-};
-
-esp_err_t bsp_led_init()
-{
- ESP_LOGI(TAG, "BLINK_GPIO setting %d", bsp_strip_config.strip_gpio_num);
-
- ESP_ERROR_CHECK(led_strip_new_rmt_device(&bsp_strip_config, &bsp_rmt_config, &led_strip));
- led_strip_set_pixel(led_strip, 0, 0x00, 0x00, 0x00);
- led_strip_set_pixel(led_strip, 1, 0x00, 0x00, 0x00);
- led_strip_set_pixel(led_strip, 2, 0x00, 0x00, 0x00);
- led_strip_set_pixel(led_strip, 3, 0x00, 0x00, 0x00);
- led_strip_refresh(led_strip);
-
- return ESP_OK;
-}
-
-esp_err_t bsp_led_rgb_set(uint8_t r, uint8_t g, uint8_t b)
-{
- esp_err_t ret = ESP_OK;
-
- ret |= led_strip_set_pixel(led_strip, 0, r, g, b);
- ret |= led_strip_set_pixel(led_strip, 1, r, g, b);
- ret |= led_strip_set_pixel(led_strip, 2, r, g, b);
- ret |= led_strip_set_pixel(led_strip, 3, r, g, b);
- ret |= led_strip_refresh(led_strip);
- return ret;
-}
-
-namespace iot {
-class DogLight : public Thing {
-private:
- bool power_ = false;
-
- void InitializeGpio()
- {
- bsp_led_init();
- bsp_led_rgb_set(0x00, 0x00, 0x00);
- ESP_LOGI(TAG, "lamp InitializeGpio");
- }
-
-public:
- DogLight() : Thing("DogLight", "机器人头灯"), power_(false)
- {
- InitializeGpio();
-
- properties_.AddBooleanProperty("power", "灯是否打开", [this]() -> bool {
- return power_;
- });
-
- methods_.AddMethod("TurnOn", "打开灯", ParameterList(), [this](const ParameterList & parameters) {
- power_ = true;
- bsp_led_rgb_set(0xFF, 0xFF, 0xFF);
- ESP_LOGI(TAG, "lamp TurnOn");
- });
-
- methods_.AddMethod("TurnOff", "关闭灯", ParameterList(), [this](const ParameterList & parameters) {
- power_ = false;
- bsp_led_rgb_set(0x00, 0x00, 0x00);
- ESP_LOGI(TAG, "lamp TurnOff");
- });
-
- methods_.AddMethod("SetRGB", "设置RGB颜色",
- ParameterList({
- Parameter("r", "红色值(0-255)", kValueTypeNumber, true),
- Parameter("g", "绿色值(0-255)", kValueTypeNumber, true),
- Parameter("b", "蓝色值(0-255)", kValueTypeNumber, true)
- }), [this](const ParameterList & parameters) {
- int r = parameters["r"].number();
- int g = parameters["g"].number();
- int b = parameters["b"].number();
-
- r = std::max(0, std::min(255, r));
- g = std::max(0, std::min(255, g));
- b = std::max(0, std::min(255, b));
-
- power_ = true;
- bsp_led_rgb_set(r, g, b);
- ESP_LOGI(TAG, "lamp SetRGB: r=%d, g=%d, b=%d", r, g, b);
- });
- }
-};
-
-} // namespace iot
-
-DECLARE_THING(DogLight);
diff --git a/main/boards/esp-hi/esp_hi.cc b/main/boards/esp-hi/esp_hi.cc
index c6bb75b8..9ca851da 100644
--- a/main/boards/esp-hi/esp_hi.cc
+++ b/main/boards/esp-hi/esp_hi.cc
@@ -3,7 +3,7 @@
#include "application.h"
#include "button.h"
#include "config.h"
-#include "iot/thing_manager.h"
+#include "mcp_server.h"
#include
#include
#include
@@ -21,8 +21,14 @@
#include "anim_player.h"
#include "emoji_display.h"
#include "servo_dog_ctrl.h"
+#include "led_strip.h"
+#include "driver/rmt_tx.h"
+#include "sdkconfig.h"
+
+#ifdef CONFIG_ESP_HI_WEB_CONTROL_ENABLED
#include "esp_hi_web_control.h"
+#endif //CONFIG_ESP_HI_WEB_CONTROL_ENABLED
#define TAG "ESP_HI"
@@ -47,6 +53,23 @@ static const ili9341_lcd_init_cmd_t vendor_specific_init[] = {
{0x2C, NULL, 0, 0}, // Memory write
};
+static const led_strip_config_t bsp_strip_config = {
+ .strip_gpio_num = GPIO_NUM_8,
+ .max_leds = 4,
+ .led_model = LED_MODEL_WS2812,
+ .flags = {
+ .invert_out = false
+ }
+};
+
+static const led_strip_rmt_config_t bsp_rmt_config = {
+ .clk_src = RMT_CLK_SRC_DEFAULT,
+ .resolution_hz = 10 * 1000 * 1000,
+ .flags = {
+ .with_dma = false
+ }
+};
+
class EspHi : public WifiBoard {
private:
Button boot_button_;
@@ -54,7 +77,10 @@ private:
Button move_wake_button_;
anim::EmojiWidget* display_ = nullptr;
bool web_server_initialized_ = false;
+ led_strip_handle_t led_strip_;
+ bool led_on_ = false;
+#ifdef CONFIG_ESP_HI_WEB_CONTROL_ENABLED
static void wifi_event_handler(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data)
{
@@ -72,6 +98,7 @@ private:
}
}
}
+#endif //CONFIG_ESP_HI_WEB_CONTROL_ENABLED
void HandleMoveWakePressDown(int64_t current_time, int64_t &last_trigger_time, int &gesture_state)
{
@@ -154,17 +181,39 @@ private:
HandleMoveWakePressUp(current_time, last_trigger_time, gesture_state);
});
}
+
+ void InitializeLed() {
+ ESP_LOGI(TAG, "BLINK_GPIO setting %d", bsp_strip_config.strip_gpio_num);
+
+ ESP_ERROR_CHECK(led_strip_new_rmt_device(&bsp_strip_config, &bsp_rmt_config, &led_strip_));
+ led_strip_set_pixel(led_strip_, 0, 0x00, 0x00, 0x00);
+ led_strip_set_pixel(led_strip_, 1, 0x00, 0x00, 0x00);
+ led_strip_set_pixel(led_strip_, 2, 0x00, 0x00, 0x00);
+ led_strip_set_pixel(led_strip_, 3, 0x00, 0x00, 0x00);
+ led_strip_refresh(led_strip_);
+ }
+
+ esp_err_t SetLedColor(uint8_t r, uint8_t g, uint8_t b) {
+ esp_err_t ret = ESP_OK;
+
+ ret |= led_strip_set_pixel(led_strip_, 0, r, g, b);
+ ret |= led_strip_set_pixel(led_strip_, 1, r, g, b);
+ ret |= led_strip_set_pixel(led_strip_, 2, r, g, b);
+ ret |= led_strip_set_pixel(led_strip_, 3, r, g, b);
+ ret |= led_strip_refresh(led_strip_);
+ return ret;
+ }
void InitializeIot()
{
ESP_LOGI(TAG, "Initialize Iot");
- auto &thing_manager = iot::ThingManager::GetInstance();
- thing_manager.AddThing(iot::CreateThing("DogLight"));
- thing_manager.AddThing(iot::CreateThing("DogAction_basic"));
- thing_manager.AddThing(iot::CreateThing("DogAction_extra"));
+ InitializeLed();
+ SetLedColor(0x00, 0x00, 0x00);
+#ifdef CONFIG_ESP_HI_WEB_CONTROL_ENABLED
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED,
&wifi_event_handler, this));
+#endif //CONFIG_ESP_HI_WEB_CONTROL_ENABLED
}
void InitializeSpi()
@@ -235,6 +284,107 @@ private:
#endif
}
+ void InitializeTools()
+ {
+ auto& mcp_server = McpServer::GetInstance();
+
+ // 基础动作控制
+ mcp_server.AddTool("self.dog.forward", "机器人向前移动", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
+ servo_dog_ctrl_send(DOG_STATE_FORWARD, NULL);
+ return true;
+ });
+
+ mcp_server.AddTool("self.dog.backward", "机器人向后移动", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
+ servo_dog_ctrl_send(DOG_STATE_BACKWARD, NULL);
+ return true;
+ });
+
+ mcp_server.AddTool("self.dog.sway_back_forth", "机器人做前后摇摆动作", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
+ servo_dog_ctrl_send(DOG_STATE_SWAY_BACK_FORTH, NULL);
+ return true;
+ });
+
+ mcp_server.AddTool("self.dog.turn_left", "机器人向左转", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
+ servo_dog_ctrl_send(DOG_STATE_TURN_LEFT, NULL);
+ return true;
+ });
+
+ mcp_server.AddTool("self.dog.turn_right", "机器人向右转", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
+ servo_dog_ctrl_send(DOG_STATE_TURN_RIGHT, NULL);
+ return true;
+ });
+
+ mcp_server.AddTool("self.dog.lay_down", "机器人趴下", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
+ servo_dog_ctrl_send(DOG_STATE_LAY_DOWN, NULL);
+ return true;
+ });
+
+ mcp_server.AddTool("self.dog.sway", "机器人做左右摇摆动作", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
+ dog_action_args_t args = {
+ .repeat_count = 4,
+ };
+ servo_dog_ctrl_send(DOG_STATE_SWAY, &args);
+ return true;
+ });
+
+ // 扩展动作控制
+ mcp_server.AddTool("self.dog.retract_legs", "机器人收回腿部", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
+ servo_dog_ctrl_send(DOG_STATE_RETRACT_LEGS, NULL);
+ return true;
+ });
+
+ mcp_server.AddTool("self.dog.stop", "立即停止机器人当前动作", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
+ servo_dog_ctrl_send(DOG_STATE_IDLE, NULL);
+ return true;
+ });
+
+ mcp_server.AddTool("self.dog.shake_hand", "机器人做握手动作", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
+ servo_dog_ctrl_send(DOG_STATE_SHAKE_HAND, NULL);
+ return true;
+ });
+
+ mcp_server.AddTool("self.dog.shake_back_legs", "机器人伸懒腰", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
+ servo_dog_ctrl_send(DOG_STATE_SHAKE_BACK_LEGS, NULL);
+ return true;
+ });
+
+ mcp_server.AddTool("self.dog.jump_forward", "机器人向前跳跃", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
+ servo_dog_ctrl_send(DOG_STATE_JUMP_FORWARD, NULL);
+ return true;
+ });
+
+ // 灯光控制
+ mcp_server.AddTool("self.light.get_power", "获取灯是否打开", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
+ return led_on_;
+ });
+
+ mcp_server.AddTool("self.light.turn_on", "打开灯", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
+ SetLedColor(0xFF, 0xFF, 0xFF);
+ led_on_ = true;
+ return true;
+ });
+
+ mcp_server.AddTool("self.light.turn_off", "关闭灯", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
+ SetLedColor(0x00, 0x00, 0x00);
+ led_on_ = false;
+ return true;
+ });
+
+ mcp_server.AddTool("self.light.set_rgb", "设置RGB颜色", PropertyList({
+ Property("r", kPropertyTypeInteger, 0, 255),
+ Property("g", kPropertyTypeInteger, 0, 255),
+ Property("b", kPropertyTypeInteger, 0, 255)
+ }), [this](const PropertyList& properties) -> ReturnValue {
+ int r = properties["r"].value();
+ int g = properties["g"].value();
+ int b = properties["b"].value();
+
+ led_on_ = true;
+ SetLedColor(r, g, b);
+ return true;
+ });
+ }
+
public:
EspHi() : boot_button_(BOOT_BUTTON_GPIO),
audio_wake_button_(AUDIO_WAKE_BUTTON_GPIO),
@@ -244,6 +394,7 @@ public:
InitializeIot();
InitializeSpi();
InitializeLcdDisplay();
+ InitializeTools();
}
virtual AudioCodec* GetAudioCodec() override