v1.6.6: Set MCP as default IoT Protocol (#690)

This commit is contained in:
Xiaoxia
2025-05-27 14:58:49 +08:00
committed by GitHub
parent 0c83263762
commit d80f94387a
46 changed files with 524 additions and 428 deletions

View File

@ -48,4 +48,8 @@
#define DISPLAY_MIRROR_X true
#define DISPLAY_MIRROR_Y true
// A MCP Test: Control a lamp
#define LAMP_GPIO GPIO_NUM_18
#endif // _BOARD_CONFIG_H_

View File

@ -4,6 +4,8 @@
#include "application.h"
#include "button.h"
#include "config.h"
#include "mcp_server.h"
#include "lamp_controller.h"
#include "iot/thing_manager.h"
#include "led/single_led.h"
#include "display/oled_display.h"
@ -133,9 +135,13 @@ private:
// 物联网初始化,添加对 AI 可见设备
void InitializeIot() {
#if CONFIG_IOT_PROTOCOL_XIAOZHI
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("Lamp"));
#elif CONFIG_IOT_PROTOCOL_MCP
static LampController lamp(LAMP_GPIO);
#endif
}
public:

View File

@ -5,6 +5,8 @@
#include "application.h"
#include "button.h"
#include "config.h"
#include "mcp_server.h"
#include "lamp_controller.h"
#include "iot/thing_manager.h"
#include "led/single_led.h"
#include "assets/lang_config.h"
@ -154,9 +156,13 @@ private:
// 物联网初始化,添加对 AI 可见设备
void InitializeIot() {
#if CONFIG_IOT_PROTOCOL_XIAOZHI
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("Lamp"));
#elif CONFIG_IOT_PROTOCOL_MCP
static LampController lamp(LAMP_GPIO);
#endif
}
public:

View File

@ -53,4 +53,7 @@
#define ML307_TX_PIN GPIO_NUM_12
// A MCP Test: Control a lamp
#define LAMP_GPIO GPIO_NUM_18
#endif // _BOARD_CONFIG_H_

View File

@ -5,6 +5,8 @@
#include "application.h"
#include "button.h"
#include "config.h"
#include "mcp_server.h"
#include "lamp_controller.h"
#include "iot/thing_manager.h"
#include "led/single_led.h"
@ -149,10 +151,14 @@ private:
// 物联网初始化,添加对 AI 可见设备
void InitializeIot() {
#if CONFIG_IOT_PROTOCOL_XIAOZHI
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("Screen"));
thing_manager.AddThing(iot::CreateThing("Lamp"));
#elif CONFIG_IOT_PROTOCOL_MCP
static LampController lamp(LAMP_GPIO);
#endif
}
public:

View File

@ -282,4 +282,8 @@
#define DISPLAY_SPI_MODE 0
#endif
// A MCP Test: Control a lamp
#define LAMP_GPIO GPIO_NUM_18
#endif // _BOARD_CONFIG_H_

View File

@ -5,6 +5,8 @@
#include "application.h"
#include "button.h"
#include "config.h"
#include "mcp_server.h"
#include "lamp_controller.h"
#include "iot/thing_manager.h"
#include "led/single_led.h"
#include "assets/lang_config.h"
@ -150,11 +152,15 @@ private:
});
}
// 物联网初始化,添加对 AI 可见设备
// 物联网初始化,逐步迁移到 MCP 协议
void InitializeIot() {
#if CONFIG_IOT_PROTOCOL_XIAOZHI
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("Lamp"));
#elif CONFIG_IOT_PROTOCOL_MCP
static LampController lamp(LAMP_GPIO);
#endif
}
public:

View File

@ -52,4 +52,8 @@
#define DISPLAY_MIRROR_X true
#define DISPLAY_MIRROR_Y true
// A MCP Test: Control a lamp
#define LAMP_GPIO GPIO_NUM_18
#endif // _BOARD_CONFIG_H_

View File

@ -0,0 +1,44 @@
#ifndef __LAMP_CONTROLLER_H__
#define __LAMP_CONTROLLER_H__
#include "mcp_server.h"
class LampController {
private:
bool power_ = false;
gpio_num_t gpio_num_;
public:
LampController(gpio_num_t gpio_num) : gpio_num_(gpio_num) {
gpio_config_t config = {
.pin_bit_mask = (1ULL << gpio_num_),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
ESP_ERROR_CHECK(gpio_config(&config));
gpio_set_level(gpio_num_, 0);
auto& mcp_server = McpServer::GetInstance();
mcp_server.AddTool("self.lamp.get_state", "Get the power state of the lamp", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
return power_ ? "{\"power\": true}" : "{\"power\": false}";
});
mcp_server.AddTool("self.lamp.turn_on", "Turn on the lamp", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
power_ = true;
gpio_set_level(gpio_num_, 1);
return true;
});
mcp_server.AddTool("self.lamp.turn_off", "Turn off the lamp", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
power_ = false;
gpio_set_level(gpio_num_, 0);
return true;
});
}
};
#endif // __LAMP_CONTROLLER_H__

View File

@ -203,6 +203,9 @@ std::string WifiBoard::GetDeviceStatusJson() {
* "type": "wifi",
* "ssid": "Xiaozhi",
* "rssi": -60
* },
* "chip": {
* "temperature": 25
* }
* }
*/
@ -255,6 +258,14 @@ std::string WifiBoard::GetDeviceStatusJson() {
}
cJSON_AddItemToObject(root, "network", network);
// Chip
float esp32temp = 0.0f;
if (board.GetTemperature(esp32temp)) {
auto chip = cJSON_CreateObject();
cJSON_AddNumberToObject(chip, "temperature", esp32temp);
cJSON_AddItemToObject(root, "chip", chip);
}
auto json_str = cJSON_PrintUnformatted(root);
std::string json(json_str);
cJSON_free(json_str);

View File

@ -82,6 +82,15 @@ private:
}
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 InitializeIli9341Display() {

View File

@ -82,6 +82,15 @@ private:
}
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 InitializeIli9341Display() {

View File

@ -1,98 +0,0 @@
/*
ESP-SparkBot 的底座
https://gitee.com/esp-friends/esp_sparkbot/tree/master/example/tank/c2_tracked_chassis
*/
#include "sdkconfig.h"
#include "iot/thing.h"
#include "board.h"
#include <driver/gpio.h>
#include <driver/uart.h>
#include <esp_log.h>
#include <cstring>
#include "boards/esp-sparkbot/config.h"
#define TAG "Chassis"
namespace iot {
class Chassis : public Thing {
private:
light_mode_t light_mode_ = LIGHT_MODE_ALWAYS_ON;
void SendUartMessage(const char * command_str) {
uint8_t len = strlen(command_str);
uart_write_bytes(ECHO_UART_PORT_NUM, command_str, len);
ESP_LOGI(TAG, "Sent command: %s", command_str);
}
void InitializeEchoUart() {
uart_config_t uart_config = {
.baud_rate = ECHO_UART_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
int intr_alloc_flags = 0;
ESP_ERROR_CHECK(uart_driver_install(ECHO_UART_PORT_NUM, BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags));
ESP_ERROR_CHECK(uart_param_config(ECHO_UART_PORT_NUM, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(ECHO_UART_PORT_NUM, UART_ECHO_TXD, UART_ECHO_RXD, UART_ECHO_RTS, UART_ECHO_CTS));
SendUartMessage("w2");
}
public:
Chassis() : Thing("Chassis", "小机器人的底座:有履带可以移动;可以调整灯光效果"), light_mode_(LIGHT_MODE_ALWAYS_ON) {
InitializeEchoUart();
// 定义设备的属性
properties_.AddNumberProperty("light_mode", "灯光效果编号", [this]() -> int {
return (light_mode_ - 2 <= 0) ? 1 : light_mode_ - 2;
});
// 定义设备可以被远程执行的指令
methods_.AddMethod("GoForward", "向前走", ParameterList(), [this](const ParameterList& parameters) {
SendUartMessage("x0.0 y1.0");
});
methods_.AddMethod("GoBack", "向后退", ParameterList(), [this](const ParameterList& parameters) {
SendUartMessage("x0.0 y-1.0");
});
methods_.AddMethod("TurnLeft", "向左转", ParameterList(), [this](const ParameterList& parameters) {
SendUartMessage("x-1.0 y0.0");
});
methods_.AddMethod("TurnRight", "向右转", ParameterList(), [this](const ParameterList& parameters) {
SendUartMessage("x1.0 y0.0");
});
methods_.AddMethod("Dance", "跳舞", ParameterList(), [this](const ParameterList& parameters) {
SendUartMessage("d1");
light_mode_ = LIGHT_MODE_MAX;
});
methods_.AddMethod("SwitchLightMode", "打开灯", ParameterList({
Parameter("lightmode", "1到6之间的整数", kValueTypeNumber, true)
}), [this](const ParameterList& parameters) {
char command_str[5] = {'w', 0, 0};
char mode = static_cast<char>(parameters["lightmode"].number()) + 2;
ESP_LOGI(TAG, "Input Light Mode: %c", (mode + '0'));
if (mode >= 3 && mode <= 8) {
command_str[1] = mode + '0';
SendUartMessage(command_str);
}
});
}
};
} // namespace iot
DECLARE_THING(Chassis);

View File

@ -5,13 +5,15 @@
#include "application.h"
#include "button.h"
#include "config.h"
#include "iot/thing_manager.h"
#include "mcp_server.h"
#include <wifi_station.h>
#include <esp_log.h>
#include <esp_lcd_panel_vendor.h>
#include <driver/i2c_master.h>
#include <driver/spi_common.h>
#include <driver/uart.h>
#include <cstring>
#include "esp32_camera.h"
@ -48,6 +50,7 @@ private:
Button boot_button_;
Display* display_;
Esp32Camera* camera_;
light_mode_t light_mode_ = LIGHT_MODE_ALWAYS_ON;
void InitializeI2c() {
// Initialize I2C peripheral
@ -132,8 +135,8 @@ private:
camera_config.pin_reset = SPARKBOT_CAMERA_RESET;
camera_config.pin_xclk = SPARKBOT_CAMERA_XCLK;
camera_config.pin_pclk = SPARKBOT_CAMERA_PCLK;
camera_config.pin_sscb_sda = SPARKBOT_CAMERA_SIOD;
camera_config.pin_sscb_scl = SPARKBOT_CAMERA_SIOC;
camera_config.pin_sccb_sda = SPARKBOT_CAMERA_SIOD;
camera_config.pin_sccb_scl = SPARKBOT_CAMERA_SIOC;
camera_config.pin_d0 = SPARKBOT_CAMERA_D0;
camera_config.pin_d1 = SPARKBOT_CAMERA_D1;
@ -163,12 +166,86 @@ private:
camera_ = new Esp32Camera(camera_config);
}
// 物联网初始化,添加对 AI 可见设备
void InitializeIot() {
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("Screen"));
thing_manager.AddThing(iot::CreateThing("Chassis"));
/*
ESP-SparkBot 的底座
https://gitee.com/esp-friends/esp_sparkbot/tree/master/example/tank/c2_tracked_chassis
*/
void InitializeEchoUart() {
uart_config_t uart_config = {
.baud_rate = ECHO_UART_BAUD_RATE,
.data_bits = UART_DATA_8_BITS,
.parity = UART_PARITY_DISABLE,
.stop_bits = UART_STOP_BITS_1,
.flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
.source_clk = UART_SCLK_DEFAULT,
};
int intr_alloc_flags = 0;
ESP_ERROR_CHECK(uart_driver_install(ECHO_UART_PORT_NUM, BUF_SIZE * 2, 0, 0, NULL, intr_alloc_flags));
ESP_ERROR_CHECK(uart_param_config(ECHO_UART_PORT_NUM, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(ECHO_UART_PORT_NUM, UART_ECHO_TXD, UART_ECHO_RXD, UART_ECHO_RTS, UART_ECHO_CTS));
SendUartMessage("w2");
}
void SendUartMessage(const char * command_str) {
uint8_t len = strlen(command_str);
uart_write_bytes(ECHO_UART_PORT_NUM, command_str, len);
ESP_LOGI(TAG, "Sent command: %s", command_str);
}
void InitializeTools() {
auto& mcp_server = McpServer::GetInstance();
// 定义设备的属性
mcp_server.AddTool("self.chassis.get_light_mode", "获取灯光效果编号", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
if (light_mode_ < 2) {
return 1;
} else {
return light_mode_ - 2;
}
});
mcp_server.AddTool("self.chassis.go_forward", "前进", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
SendUartMessage("x0.0 y1.0");
return true;
});
mcp_server.AddTool("self.chassis.go_back", "后退", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
SendUartMessage("x0.0 y-1.0");
return true;
});
mcp_server.AddTool("self.chassis.turn_left", "向左转", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
SendUartMessage("x-1.0 y0.0");
return true;
});
mcp_server.AddTool("self.chassis.turn_right", "向右转", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
SendUartMessage("x1.0 y0.0");
return true;
});
mcp_server.AddTool("self.chassis.dance", "跳舞", PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
SendUartMessage("d1");
light_mode_ = LIGHT_MODE_MAX;
return true;
});
mcp_server.AddTool("self.chassis.switch_light_mode", "打开灯光效果", PropertyList({
Property("light_mode", kPropertyTypeInteger, 1, 6)
}), [this](const PropertyList& properties) -> ReturnValue {
char command_str[5] = {'w', 0, 0};
char mode = static_cast<light_mode_t>(properties["light_mode"].value<int>());
ESP_LOGI(TAG, "Switch Light Mode: %c", (mode + '0'));
if (mode >= 3 && mode <= 8) {
command_str[1] = mode + '0';
SendUartMessage(command_str);
return true;
}
throw std::runtime_error("Invalid light mode");
});
}
public:
@ -177,8 +254,9 @@ public:
InitializeSpi();
InitializeDisplay();
InitializeButtons();
InitializeIot();
InitializeCamera();
InitializeEchoUart();
InitializeTools();
GetBacklight()->RestoreBrightness();
}

View File

@ -1,32 +0,0 @@
#include <freertos/FreeRTOS.h>
#include <freertos/timers.h>
#include <freertos/task.h>
#include <esp_log.h>
#include "board.h"
#include "boards/common/wifi_board.h"
#include "boards/esp32-s3-touch-amoled-1.8/config.h"
#include "iot/thing.h"
#define TAG "BoardControl"
namespace iot {
class BoardControl : public Thing {
public:
BoardControl() : Thing("BoardControl", "当前 AI 机器人管理和控制") {
// 修改重新配网
methods_.AddMethod("ResetWifiConfiguration", "重新配网", ParameterList(),
[this](const ParameterList& parameters) {
ESP_LOGI(TAG, "ResetWifiConfiguration");
auto board = static_cast<WifiBoard*>(&Board::GetInstance());
if (board && board->GetBoardType() == "wifi") {
board->ResetWifiConfiguration();
}
});
}
};
} // namespace iot
DECLARE_THING(BoardControl);

View File

@ -7,7 +7,7 @@
#include "application.h"
#include "button.h"
#include "led/single_led.h"
#include "iot/thing_manager.h"
#include "mcp_server.h"
#include "config.h"
#include "power_save_timer.h"
#include "axp2101.h"
@ -288,13 +288,16 @@ private:
ESP_LOGI(TAG, "Touch panel initialized successfully");
}
// 物联网初始化,添加对 AI 可见设备
void InitializeIot() {
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("Screen"));
thing_manager.AddThing(iot::CreateThing("Battery"));
thing_manager.AddThing(iot::CreateThing("BoardControl"));
// 初始化工具
void InitializeTools() {
auto &mcp_server = McpServer::GetInstance();
mcp_server.AddTool("self.system.reconfigure_wifi",
"Reboot the device and enter WiFi configuration mode.\n"
"**CAUTION** You must ask the user to confirm this action.",
PropertyList(), [this](const PropertyList& properties) {
ResetWifiConfiguration();
return true;
});
}
public:
@ -308,7 +311,7 @@ public:
InitializeSH8601Display();
InitializeTouch();
InitializeButtons();
InitializeIot();
InitializeTools();
}
virtual AudioCodec* GetAudioCodec() override {

View File

@ -1,31 +0,0 @@
#include <freertos/FreeRTOS.h>
#include <freertos/timers.h>
#include <freertos/task.h>
#include <esp_log.h>
#include "board.h"
#include "boards/common/wifi_board.h"
#include "iot/thing.h"
#define TAG "BoardControl"
namespace iot {
class BoardControl : public Thing {
public:
BoardControl() : Thing("BoardControl", "当前 AI 机器人管理和控制") {
// 修改重新配网
methods_.AddMethod("ResetWifiConfiguration", "重新配网", ParameterList(),
[this](const ParameterList& parameters) {
ESP_LOGI(TAG, "ResetWifiConfiguration");
auto board = static_cast<WifiBoard*>(&Board::GetInstance());
if (board && board->GetBoardType() == "wifi") {
board->ResetWifiConfiguration();
}
});
}
};
} // namespace iot
DECLARE_THING(BoardControl);

View File

@ -5,8 +5,7 @@
#include "application.h"
#include "button.h"
#include "config.h"
#include "iot/thing_manager.h"
#include "mcp_server.h"
#include <esp_log.h>
#include "i2c_device.h"
@ -233,13 +232,16 @@ private:
});
}
// 物联网初始化,添加对 AI 可见设备
void InitializeIot() {
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("Screen"));
thing_manager.AddThing(iot::CreateThing("Battery"));
thing_manager.AddThing(iot::CreateThing("BoardControl"));
// 初始化工具
void InitializeTools() {
auto &mcp_server = McpServer::GetInstance();
mcp_server.AddTool("self.system.reconfigure_wifi",
"Reboot the device and enter WiFi configuration mode.\n"
"**CAUTION** You must ask the user to confirm this action.",
PropertyList(), [this](const PropertyList& properties) {
ResetWifiConfiguration();
return true;
});
}
public:
@ -257,7 +259,7 @@ public:
esp_restart();
}
InitializeButtons();
InitializeIot();
InitializeTools();
GetBacklight()->RestoreBrightness();
}

View File

@ -140,6 +140,15 @@ private:
}
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 InitializeIli9341Display() {

View File

@ -224,7 +224,6 @@ private:
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("Screen"));
// thing_manager.AddThing(iot::CreateThing("Lamp"));
thing_manager.AddThing(iot::CreateThing("Battery"));
}

View File

@ -3,7 +3,6 @@
#include "application.h"
#include "button.h"
#include "config.h"
#include "iot/thing_manager.h"
#include "led/circular_strip.h"
#include "led_strip_control.h"
@ -54,12 +53,8 @@ private:
// 物联网初始化,添加对 AI 可见设备
void InitializeIot() {
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
led_strip_ = new CircularStrip(BUILTIN_LED_GPIO, 8);
auto led_strip_control = new LedStripControl(led_strip_);
thing_manager.AddThing(led_strip_control);
new LedStripControl(led_strip_);
}
public:

View File

@ -1,5 +1,6 @@
#include "led_strip_control.h"
#include "settings.h"
#include "mcp_server.h"
#include <esp_log.h>
#define TAG "LedStripControl"
@ -22,102 +23,108 @@ StripColor LedStripControl::RGBToColor(int red, int green, int blue) {
}
LedStripControl::LedStripControl(CircularStrip* led_strip)
: Thing("LedStripControl", "LED 灯带控制一共有8个灯珠"), led_strip_(led_strip) {
: led_strip_(led_strip) {
// 从设置中读取亮度等级
Settings settings("led_strip");
brightness_level_ = settings.GetInt("brightness", 4); // 默认等级4
led_strip_->SetBrightness(LevelToBrightness(brightness_level_), 4);
// 定义设备的属性
properties_.AddNumberProperty("brightness", "对话时的亮度等级(0-8)", [this]() -> int {
return brightness_level_;
});
auto& mcp_server = McpServer::GetInstance();
mcp_server.AddTool("self.led_strip.get_brightness",
"Get the brightness of the led strip (0-8)",
PropertyList(), [this](const PropertyList& properties) -> ReturnValue {
return brightness_level_;
});
// 定义设备可以被远程执行的指令
methods_.AddMethod("SetBrightness", "设置对话时的亮度等级", ParameterList({
Parameter("level", "亮度等级(0-8)", kValueTypeNumber, true)
}), [this](const ParameterList& parameters) {
int level = static_cast<int>(parameters["level"].number());
ESP_LOGI(TAG, "Set LedStrip brightness level to %d", level);
if (level < 0) level = 0;
if (level > 8) level = 8;
brightness_level_ = level;
led_strip_->SetBrightness(LevelToBrightness(brightness_level_), 4);
// 保存设置
Settings settings("led_strip", true);
settings.SetInt("brightness", brightness_level_);
});
mcp_server.AddTool("self.led_strip.set_brightness",
"Set the brightness of the led strip (0-8)",
PropertyList({
Property("level", kPropertyTypeInteger, 0, 8)
}), [this](const PropertyList& properties) -> ReturnValue {
int level = properties["level"].value<int>();
ESP_LOGI(TAG, "Set LedStrip brightness level to %d", level);
brightness_level_ = level;
led_strip_->SetBrightness(LevelToBrightness(brightness_level_), 4);
methods_.AddMethod("SetSingleColor", "设置单个灯颜色", ParameterList({
Parameter("index", "灯珠索引0-7", kValueTypeNumber, true),
Parameter("red", "红色0-255", kValueTypeNumber, true),
Parameter("green", "绿色0-255", kValueTypeNumber, true),
Parameter("blue", "蓝色0-255", kValueTypeNumber, true)
}), [this](const ParameterList& parameters) {
int index = parameters["index"].number();
StripColor color = RGBToColor(
parameters["red"].number(),
parameters["green"].number(),
parameters["blue"].number()
);
ESP_LOGI(TAG, "Set led strip single color %d to %d, %d, %d",
index, color.red, color.green, color.blue);
led_strip_->SetSingleColor(index, color);
});
// 保存设置
Settings settings("led_strip", true);
settings.SetInt("brightness", brightness_level_);
methods_.AddMethod("SetAllColor", "设置所有灯颜色", ParameterList({
Parameter("red", "红色0-255", kValueTypeNumber, true),
Parameter("green", "绿色0-255", kValueTypeNumber, true),
Parameter("blue", "蓝色0-255", kValueTypeNumber, true)
}), [this](const ParameterList& parameters) {
StripColor color = RGBToColor(
parameters["red"].number(),
parameters["green"].number(),
parameters["blue"].number()
);
ESP_LOGI(TAG, "Set led strip color to %d, %d, %d",
color.red, color.green, color.blue
);
led_strip_->SetAllColor(color);
});
return true;
});
methods_.AddMethod("Blink", "闪烁动画", ParameterList({
Parameter("red", "红色0-255", kValueTypeNumber, true),
Parameter("green", "绿色0-255", kValueTypeNumber, true),
Parameter("blue", "蓝色0-255", kValueTypeNumber, true),
Parameter("interval", "间隔(ms)", kValueTypeNumber, true)
}), [this](const ParameterList& parameters) {
int interval = parameters["interval"].number();
StripColor color = RGBToColor(
parameters["red"].number(),
parameters["green"].number(),
parameters["blue"].number()
);
ESP_LOGI(TAG, "Blink led strip with color %d, %d, %d, interval %dms",
color.red, color.green, color.blue, interval);
led_strip_->Blink(color, interval);
});
mcp_server.AddTool("self.led_strip.set_single_color",
"Set the color of a single led.",
PropertyList({
Property("index", kPropertyTypeInteger, 0, 7),
Property("red", kPropertyTypeInteger, 0, 255),
Property("green", kPropertyTypeInteger, 0, 255),
Property("blue", kPropertyTypeInteger, 0, 255)
}), [this](const PropertyList& properties) -> ReturnValue {
int index = properties["index"].value<int>();
int red = properties["red"].value<int>();
int green = properties["green"].value<int>();
int blue = properties["blue"].value<int>();
ESP_LOGI(TAG, "Set led strip single color %d to %d, %d, %d",
index, red, green, blue);
led_strip_->SetSingleColor(index, RGBToColor(red, green, blue));
return true;
});
mcp_server.AddTool("self.led_strip.set_all_color",
"Set the color of all leds.",
PropertyList({
Property("red", kPropertyTypeInteger, 0, 255),
Property("green", kPropertyTypeInteger, 0, 255),
Property("blue", kPropertyTypeInteger, 0, 255)
}), [this](const PropertyList& properties) -> ReturnValue {
int red = properties["red"].value<int>();
int green = properties["green"].value<int>();
int blue = properties["blue"].value<int>();
ESP_LOGI(TAG, "Set led strip all color to %d, %d, %d",
red, green, blue);
led_strip_->SetAllColor(RGBToColor(red, green, blue));
return true;
});
mcp_server.AddTool("self.led_strip.blink",
"Blink the led strip. (闪烁)",
PropertyList({
Property("red", kPropertyTypeInteger, 0, 255),
Property("green", kPropertyTypeInteger, 0, 255),
Property("blue", kPropertyTypeInteger, 0, 255),
Property("interval", kPropertyTypeInteger, 0, 1000)
}), [this](const PropertyList& properties) -> ReturnValue {
int red = properties["red"].value<int>();
int green = properties["green"].value<int>();
int blue = properties["blue"].value<int>();
int interval = properties["interval"].value<int>();
ESP_LOGI(TAG, "Blink led strip with color %d, %d, %d, interval %dms",
red, green, blue, interval);
led_strip_->Blink(RGBToColor(red, green, blue), interval);
return true;
});
mcp_server.AddTool("self.led_strip.scroll",
"Scroll the led strip. (跑马灯)",
PropertyList({
Property("red", kPropertyTypeInteger, 0, 255),
Property("green", kPropertyTypeInteger, 0, 255),
Property("blue", kPropertyTypeInteger, 0, 255),
Property("length", kPropertyTypeInteger, 1, 7),
Property("interval", kPropertyTypeInteger, 0, 1000)
}), [this](const PropertyList& properties) -> ReturnValue {
int red = properties["red"].value<int>();
int green = properties["green"].value<int>();
int blue = properties["blue"].value<int>();
int interval = properties["interval"].value<int>();
int length = properties["length"].value<int>();
ESP_LOGI(TAG, "Scroll led strip with color %d, %d, %d, length %d, interval %dms",
red, green, blue, length, interval);
StripColor low = RGBToColor(4, 4, 4);
StripColor high = RGBToColor(red, green, blue);
led_strip_->Scroll(low, high, length, interval);
return true;
});
methods_.AddMethod("Scroll", "跑马灯动画", ParameterList({
Parameter("red", "红色0-255", kValueTypeNumber, true),
Parameter("green", "绿色0-255", kValueTypeNumber, true),
Parameter("blue", "蓝色0-255", kValueTypeNumber, true),
Parameter("length", "滚动条长度1-7", kValueTypeNumber, true),
Parameter("interval", "间隔(ms)", kValueTypeNumber, true)
}), [this](const ParameterList& parameters) {
int interval = parameters["interval"].number();
int length = parameters["length"].number();
StripColor low = RGBToColor(4, 4, 4);
StripColor high = RGBToColor(
parameters["red"].number(),
parameters["green"].number(),
parameters["blue"].number()
);
ESP_LOGI(TAG, "Scroll led strip with color %d, %d, %d, length %d, interval %dms",
high.red, high.green, high.blue, length, interval);
led_strip_->Scroll(low, high, length, interval);
});
}

View File

@ -1,12 +1,9 @@
#ifndef LED_STRIP_CONTROL_H
#define LED_STRIP_CONTROL_H
#include "iot/thing.h"
#include "led/circular_strip.h"
using namespace iot;
class LedStripControl : public Thing {
class LedStripControl {
private:
CircularStrip* led_strip_;
int brightness_level_; // 亮度等级 (0-8)

View File

@ -116,6 +116,15 @@ private:
}
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() {

View File

@ -40,4 +40,8 @@
#define DISPLAY_OFFSET_Y 0
#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
// A MCP Test: Control a lamp
#define LAMP_GPIO GPIO_NUM_18
#endif // _BOARD_CONFIG_H_

View File

@ -7,6 +7,8 @@
#include "button.h"
#include "config.h"
#include "power_save_timer.h"
#include "mcp_server.h"
#include "lamp_controller.h"
#include "iot/thing_manager.h"
#include "led/single_led.h"
#include "assets/lang_config.h"
@ -195,11 +197,15 @@ private:
// 物联网初始化,添加对 AI 可见设备
void InitializeIot() {
#if CONFIG_IOT_PROTOCOL_XIAOZHI
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("Screen"));
thing_manager.AddThing(iot::CreateThing("Lamp"));
thing_manager.AddThing(iot::CreateThing("Battery"));
#elif CONFIG_IOT_PROTOCOL_MCP
static LampController lamp(LAMP_GPIO);
#endif
}

View File

@ -1,32 +0,0 @@
#include <freertos/FreeRTOS.h>
#include <freertos/timers.h>
#include <freertos/task.h>
#include <esp_log.h>
#include "board.h"
#include "boards/common/wifi_board.h"
#include "boards/waveshare-s3-touch-amoled-1.75/config.h"
#include "iot/thing.h"
#define TAG "BoardControl"
namespace iot {
class BoardControl : public Thing {
public:
BoardControl() : Thing("BoardControl", "当前 AI 机器人管理和控制") {
// 修改重新配网
methods_.AddMethod("ResetWifiConfiguration", "重新配网", ParameterList(),
[this](const ParameterList& parameters) {
ESP_LOGI(TAG, "ResetWifiConfiguration");
auto board = static_cast<WifiBoard*>(&Board::GetInstance());
if (board && board->GetBoardType() == "wifi") {
board->ResetWifiConfiguration();
}
});
}
};
} // namespace iot
DECLARE_THING(BoardControl);

View File

@ -7,7 +7,7 @@
#include "application.h"
#include "button.h"
#include "led/single_led.h"
#include "iot/thing_manager.h"
#include "mcp_server.h"
#include "config.h"
#include "power_save_timer.h"
#include "axp2101.h"
@ -221,7 +221,17 @@ private:
if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
ResetWifiConfiguration();
}
app.ToggleChatState(); });
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 InitializeSH8601Display() {
@ -294,13 +304,16 @@ private:
ESP_LOGI(TAG, "Touch panel initialized successfully");
}
// 物联网初始化,添加对 AI 可见设备
void InitializeIot() {
auto &thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("Screen"));
thing_manager.AddThing(iot::CreateThing("Battery"));
thing_manager.AddThing(iot::CreateThing("BoardControl"));
// 初始化工具
void InitializeTools() {
auto &mcp_server = McpServer::GetInstance();
mcp_server.AddTool("self.system.reconfigure_wifi",
"Reboot the device and enter WiFi configuration mode.\n"
"**CAUTION** You must ask the user to confirm this action.",
PropertyList(), [this](const PropertyList& properties) {
ResetWifiConfiguration();
return true;
});
}
public:
@ -313,7 +326,7 @@ public:
InitializeSH8601Display();
InitializeTouch();
InitializeButtons();
InitializeIot();
InitializeTools();
}
virtual AudioCodec* GetAudioCodec() override {

View File

@ -4,7 +4,7 @@
#include "application.h"
#include "button.h"
#include "led/single_led.h"
#include "iot/thing_manager.h"
#include "mcp_server.h"
#include "settings.h"
#include "config.h"
#include "power_save_timer.h"
@ -142,14 +142,32 @@ private:
});
}
// 物联网初始化,添加对 AI 可见设备
void InitializeIot() {
void InitializeTools() {
Settings settings("vendor");
press_to_talk_enabled_ = settings.GetInt("press_to_talk", 0) != 0;
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("PressToTalk"));
#if CONFIG_IOT_PROTOCOL_XIAOZHI
#error "XiaoZhi 协议不支持"
#elif CONFIG_IOT_PROTOCOL_MCP
auto& mcp_server = McpServer::GetInstance();
mcp_server.AddTool("self.set_press_to_talk",
"Switch between press to talk mode (长按说话) and click to talk mode (单击说话).\n"
"The mode can be `press_to_talk` or `click_to_talk`.",
PropertyList({
Property("mode", kPropertyTypeString)
}),
[this](const PropertyList& properties) -> ReturnValue {
auto mode = properties["mode"].value<std::string>();
if (mode == "press_to_talk") {
SetPressToTalkEnabled(true);
return true;
} else if (mode == "click_to_talk") {
SetPressToTalkEnabled(false);
return true;
}
throw std::runtime_error("Invalid mode: " + mode);
});
#endif
}
public:
@ -161,7 +179,7 @@ public:
InitializeSsd1306Display();
InitializeButtons();
InitializePowerSaveTimer();
InitializeIot();
InitializeTools();
}
virtual Led* GetLed() override {
@ -194,30 +212,3 @@ public:
};
DECLARE_BOARD(XminiC3Board);
namespace iot {
class PressToTalk : public Thing {
public:
PressToTalk() : Thing("PressToTalk", "控制对话模式,一种是长按对话,一种是单击后连续对话。") {
// 定义设备的属性
properties_.AddBooleanProperty("enabled", "true 表示长按说话模式false 表示单击说话模式", []() -> bool {
auto board = static_cast<XminiC3Board*>(&Board::GetInstance());
return board->IsPressToTalkEnabled();
});
// 定义设备可以被远程执行的指令
methods_.AddMethod("SetEnabled", "启用或禁用长按说话模式,调用前需要经过用户确认", ParameterList({
Parameter("enabled", "true 表示长按说话模式false 表示单击说话模式", kValueTypeBoolean, true)
}), [](const ParameterList& parameters) {
bool enabled = parameters["enabled"].boolean();
auto board = static_cast<XminiC3Board*>(&Board::GetInstance());
board->SetPressToTalkEnabled(enabled);
});
}
};
} // namespace iot
DECLARE_THING(PressToTalk);

View File

@ -1,29 +0,0 @@
#include "iot/thing.h"
#include "board.h"
#include <esp_log.h>
#define TAG "Temperature"
namespace iot {
class Temperature : public Thing {
private:
float esp32temp = 0.0f;
public:
Temperature() : Thing("Temperature", "芯片温度管理") {
// 定义设备的属性
properties_.AddNumberProperty("temp", "当前芯片温度", [this]() -> float {
auto& board = Board::GetInstance();
if (board.GetTemperature(esp32temp)) {
return esp32temp;
}
return 0;
});
}
};
} // namespace iot
DECLARE_THING(Temperature);

View File

@ -180,10 +180,12 @@ private:
}
void InitializeIot() {
#if CONFIG_IOT_PROTOCOL_XIAOZHI
auto& thing_manager = iot::ThingManager::GetInstance();
thing_manager.AddThing(iot::CreateThing("Speaker"));
thing_manager.AddThing(iot::CreateThing("Screen"));
thing_manager.AddThing(iot::CreateThing("Battery"));
#endif
}
public: