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
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
46 changed files with 524 additions and 428 deletions

View File

@ -4,7 +4,7 @@
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)
set(PROJECT_VER "1.6.5")
set(PROJECT_VER "1.6.6")
# Add this line to disable the specific warning
add_compile_options(-Wno-missing-field-initializers)

View File

@ -158,10 +158,10 @@ choice BOARD_TYPE
bool "乐鑫ESP S3 LCD EV Board开发板"
config BOARD_TYPE_ZHENGCHEN_1_54TFT_WIFI
bool "征辰科技1.54(WIFI)"
config BOARD_TYPE_MINSI_K08_DUAL
bool "敏思科技K08(DUAL)"
config BOARD_TYPE_ZHENGCHEN_1_54TFT_ML307
bool "征辰科技1.54(ML307)"
config BOARD_TYPE_MINSI_K08_DUAL
bool "敏思科技K08(DUAL)"
config BOARD_TYPE_ESP32_S3_1_54_MUMA
bool "Spotpear ESP32-S3-1.54-MUMA"
endchoice
@ -284,7 +284,7 @@ config USE_SERVER_AEC
choice IOT_PROTOCOL
prompt "IoT Protocol"
default IOT_PROTOCOL_XIAOZHI
default IOT_PROTOCOL_MCP
help
IoT 协议,用于获取设备状态与发送控制指令
config IOT_PROTOCOL_MCP

View File

@ -44,6 +44,14 @@ Application::Application() {
event_group_ = xEventGroupCreate();
background_task_ = new BackgroundTask(4096 * 7);
#if CONFIG_USE_DEVICE_AEC
aec_mode_ = kAecOnDeviceSide;
#elif CONFIG_USE_SERVER_AEC
aec_mode_ = kAecOnServerSide;
#else
aec_mode_ = kAecOff;
#endif
#if CONFIG_USE_AUDIO_PROCESSOR
audio_processor_ = std::make_unique<AfeAudioProcessor>();
#else
@ -285,7 +293,7 @@ void Application::ToggleChatState() {
return;
}
SetListeningMode(realtime_chat_enabled_ ? kListeningModeRealtime : kListeningModeAutoStop);
SetListeningMode(aec_mode_ == kAecOff ? kListeningModeAutoStop : kListeningModeRealtime);
});
} else if (device_state_ == kDeviceStateSpeaking) {
Schedule([this]() {
@ -358,8 +366,8 @@ void Application::Start() {
auto codec = board.GetAudioCodec();
opus_decoder_ = std::make_unique<OpusDecoderWrapper>(codec->output_sample_rate(), 1, OPUS_FRAME_DURATION_MS);
opus_encoder_ = std::make_unique<OpusEncoderWrapper>(16000, 1, OPUS_FRAME_DURATION_MS);
if (realtime_chat_enabled_) {
ESP_LOGI(TAG, "Realtime chat enabled, setting opus encoder complexity to 0");
if (aec_mode_ != kAecOff) {
ESP_LOGI(TAG, "AEC mode: %d, setting opus encoder complexity to 0", aec_mode_);
opus_encoder_->SetComplexity(0);
} else if (board.GetBoardType() == "ml307") {
ESP_LOGI(TAG, "ML307 board detected, setting opus encoder complexity to 5");
@ -404,6 +412,11 @@ void Application::Start() {
// Initialize the protocol
display->SetStatus(Lang::Strings::LOADING_PROTOCOL);
// Add MCP common tools before initializing the protocol
#if CONFIG_IOT_PROTOCOL_MCP
McpServer::GetInstance().AddCommonTools();
#endif
if (ota_.HasMqttConfig()) {
protocol_ = std::make_unique<MqttProtocol>();
} else if (ota_.HasWebsocketConfig()) {
@ -608,7 +621,7 @@ void Application::Start() {
// Set the chat state to wake word detected
protocol_->SendWakeWordDetected(wake_word);
ESP_LOGI(TAG, "Wake word detected: %s", wake_word.c_str());
SetListeningMode(realtime_chat_enabled_ ? kListeningModeRealtime : kListeningModeAutoStop);
SetListeningMode(aec_mode_ == kAecOff ? kListeningModeAutoStop : kListeningModeRealtime);
} else if (device_state_ == kDeviceStateSpeaking) {
AbortSpeaking(kAbortReasonWakeWordDetected);
} else if (device_state_ == kDeviceStateActivating) {
@ -1002,3 +1015,30 @@ void Application::SendMcpMessage(const std::string& payload) {
}
});
}
void Application::SetAecMode(AecMode mode) {
aec_mode_ = mode;
Schedule([this]() {
auto& board = Board::GetInstance();
auto display = board.GetDisplay();
switch (aec_mode_) {
case kAecOff:
audio_processor_->EnableDeviceAec(false);
display->ShowNotification(Lang::Strings::RTC_MODE_OFF);
break;
case kAecOnServerSide:
audio_processor_->EnableDeviceAec(false);
display->ShowNotification(Lang::Strings::RTC_MODE_ON);
break;
case kAecOnDeviceSide:
audio_processor_->EnableDeviceAec(true);
display->ShowNotification(Lang::Strings::RTC_MODE_ON);
break;
}
// If the AEC mode is changed, close the audio channel
if (protocol_ && protocol_->IsAudioChannelOpened()) {
protocol_->CloseAudioChannel();
}
});
}

View File

@ -30,6 +30,12 @@
#define SEND_AUDIO_EVENT (1 << 1)
#define CHECK_NEW_VERSION_DONE_EVENT (1 << 2)
enum AecMode {
kAecOff,
kAecOnDeviceSide,
kAecOnServerSide,
};
enum DeviceState {
kDeviceStateUnknown,
kDeviceStateStarting,
@ -73,6 +79,8 @@ public:
void PlaySound(const std::string_view& sound);
bool CanEnterSleepMode();
void SendMcpMessage(const std::string& payload);
void SetAecMode(AecMode mode);
AecMode GetAecMode() const { return aec_mode_; }
private:
Application();
@ -90,11 +98,8 @@ private:
esp_timer_handle_t clock_timer_handle_ = nullptr;
volatile DeviceState device_state_ = kDeviceStateUnknown;
ListeningMode listening_mode_ = kListeningModeAutoStop;
#if CONFIG_USE_DEVICE_AEC || CONFIG_USE_SERVER_AEC
bool realtime_chat_enabled_ = true;
#else
bool realtime_chat_enabled_ = false;
#endif
AecMode aec_mode_ = kAecOff;
bool aborted_ = false;
bool voice_detected_ = false;
bool busy_decoding_audio_ = false;

View File

@ -51,6 +51,9 @@
"VOLUME": "Volume ",
"MUTED": "Muted",
"MAX_VOLUME": "Max volume"
"MAX_VOLUME": "Max volume",
"RTC_MODE_OFF": "AEC Off",
"RTC_MODE_ON": "AEC On"
}
}

View File

@ -50,6 +50,9 @@
"VOLUME": "音量 ",
"MUTED": "ミュートされています",
"MAX_VOLUME": "最大音量"
"MAX_VOLUME": "最大音量",
"RTC_MODE_OFF": "AEC 無効",
"RTC_MODE_ON": "AEC 有効"
}
}

View File

@ -50,6 +50,9 @@
"VOLUME":"音量 ",
"MUTED":"已静音",
"MAX_VOLUME":"最大音量"
"MAX_VOLUME":"最大音量",
"RTC_MODE_OFF":"AEC 关闭",
"RTC_MODE_ON":"AEC 开启"
}
}

View File

@ -50,6 +50,9 @@
"VOLUME": "音量 ",
"MUTED": "已靜音",
"MAX_VOLUME": "最大音量"
"MAX_VOLUME": "最大音量",
"RTC_MODE_OFF": "AEC 關閉",
"RTC_MODE_ON": "AEC 開啟"
}
}

View File

@ -26,27 +26,26 @@ void AfeAudioProcessor::Initialize(AudioCodec* codec) {
char* ns_model_name = esp_srmodel_filter(models, ESP_NSNET_PREFIX, NULL);
afe_config_t* afe_config = afe_config_init(input_format.c_str(), NULL, AFE_TYPE_VC, AFE_MODE_HIGH_PERF);
#ifdef CONFIG_USE_DEVICE_AEC
afe_config->aec_init = true;
afe_config->aec_mode = AEC_MODE_VOIP_HIGH_PERF;
#else
afe_config->aec_init = false;
#endif
afe_config->vad_mode = VAD_MODE_0;
afe_config->vad_min_noise_ms = 100;
afe_config->ns_init = true;
afe_config->ns_model_name = ns_model_name;
afe_config->afe_ns_mode = AFE_NS_MODE_NET;
#ifdef CONFIG_USE_DEVICE_AEC
afe_config->vad_init = false;
#else
afe_config->vad_init = true;
afe_config->vad_mode = VAD_MODE_0;
afe_config->vad_min_noise_ms = 100;
#endif
afe_config->afe_perferred_core = 1;
afe_config->afe_perferred_priority = 1;
afe_config->agc_init = false;
afe_config->memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM;
#ifdef CONFIG_USE_DEVICE_AEC
afe_config->aec_init = true;
afe_config->vad_init = false;
#else
afe_config->aec_init = false;
afe_config->vad_init = true;
#endif
afe_iface_ = esp_afe_handle_from_config(afe_config);
afe_data_ = afe_iface_->create_from_config(afe_config);
@ -136,4 +135,18 @@ void AfeAudioProcessor::AudioProcessorTask() {
output_callback_(std::vector<int16_t>(res->data, res->data + res->data_size / sizeof(int16_t)));
}
}
}
}
void AfeAudioProcessor::EnableDeviceAec(bool enable) {
if (enable) {
#if CONFIG_USE_DEVICE_AEC
afe_iface_->disable_vad(afe_data_);
afe_iface_->enable_aec(afe_data_);
#else
ESP_LOGE(TAG, "Device AEC is not supported");
#endif
} else {
afe_iface_->disable_aec(afe_data_);
afe_iface_->enable_vad(afe_data_);
}
}

View File

@ -26,6 +26,7 @@ public:
void OnOutput(std::function<void(std::vector<int16_t>&& data)> callback) override;
void OnVadStateChange(std::function<void(bool speaking)> callback) override;
size_t GetFeedSize() override;
void EnableDeviceAec(bool enable) override;
private:
EventGroupHandle_t event_group_ = nullptr;

View File

@ -19,6 +19,7 @@ public:
virtual void OnOutput(std::function<void(std::vector<int16_t>&& data)> callback) = 0;
virtual void OnVadStateChange(std::function<void(bool speaking)> callback) = 0;
virtual size_t GetFeedSize() = 0;
virtual void EnableDeviceAec(bool enable) = 0;
};
#endif

View File

@ -41,4 +41,10 @@ size_t DummyAudioProcessor::GetFeedSize() {
}
// 返回一个固定的帧大小,比如 30ms 的数据
return 30 * codec_->input_sample_rate() / 1000;
}
}
void DummyAudioProcessor::EnableDeviceAec(bool enable) {
if (enable) {
ESP_LOGE(TAG, "Device AEC is not supported");
}
}

View File

@ -20,6 +20,7 @@ public:
void OnOutput(std::function<void(std::vector<int16_t>&& data)> callback) override;
void OnVadStateChange(std::function<void(bool speaking)> callback) override;
size_t GetFeedSize() override;
void EnableDeviceAec(bool enable) override;
private:
AudioCodec* codec_ = nullptr;

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:

View File

@ -16,7 +16,6 @@
#define TAG "MCP"
McpServer::McpServer() {
AddCommonTools();
}
McpServer::~McpServer() {
@ -27,6 +26,10 @@ McpServer::~McpServer() {
}
void McpServer::AddCommonTools() {
// To speed up the response time, we add the common tools to the beginning of
// the tools list to utilize the prompt cache.
// Backup the original tools list and restore it after adding the common tools.
auto original_tools = std::move(tools_);
auto& board = Board::GetInstance();
AddTool("self.get_device_status",
@ -96,9 +99,18 @@ void McpServer::AddCommonTools() {
return camera->Explain(question);
});
}
// Restore the original tools list to the end of the tools list
tools_.insert(tools_.end(), original_tools.begin(), original_tools.end());
}
void McpServer::AddTool(McpTool* tool) {
// Prevent adding duplicate tools
if (std::find_if(tools_.begin(), tools_.end(), [tool](const McpTool* t) { return t->name() == tool->name(); }) != tools_.end()) {
ESP_LOGW(TAG, "Tool %s already added", tool->name().c_str());
return;
}
ESP_LOGI(TAG, "Add tool: %s", tool->name().c_str());
tools_.push_back(tool);
}

View File

@ -255,6 +255,7 @@ public:
return instance;
}
void AddCommonTools();
void AddTool(McpTool* tool);
void AddTool(const std::string& name, const std::string& description, const PropertyList& properties, std::function<ReturnValue(const PropertyList&)> callback);
void ParseMessage(const cJSON* json);
@ -264,7 +265,6 @@ private:
McpServer();
~McpServer();
void AddCommonTools();
void ParseCapabilities(const cJSON* capabilities);
void ReplyResult(int id, const std::string& result);