v1.6.6: Set MCP as default IoT Protocol (#690)
This commit is contained in:
parent
0c83263762
commit
d80f94387a
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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"
|
||||
}
|
||||
}
|
||||
@ -50,6 +50,9 @@
|
||||
|
||||
"VOLUME": "音量 ",
|
||||
"MUTED": "ミュートされています",
|
||||
"MAX_VOLUME": "最大音量"
|
||||
"MAX_VOLUME": "最大音量",
|
||||
|
||||
"RTC_MODE_OFF": "AEC 無効",
|
||||
"RTC_MODE_ON": "AEC 有効"
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,6 +50,9 @@
|
||||
|
||||
"VOLUME":"音量 ",
|
||||
"MUTED":"已静音",
|
||||
"MAX_VOLUME":"最大音量"
|
||||
"MAX_VOLUME":"最大音量",
|
||||
|
||||
"RTC_MODE_OFF":"AEC 关闭",
|
||||
"RTC_MODE_ON":"AEC 开启"
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,6 +50,9 @@
|
||||
|
||||
"VOLUME": "音量 ",
|
||||
"MUTED": "已靜音",
|
||||
"MAX_VOLUME": "最大音量"
|
||||
"MAX_VOLUME": "最大音量",
|
||||
|
||||
"RTC_MODE_OFF": "AEC 關閉",
|
||||
"RTC_MODE_ON": "AEC 開啟"
|
||||
}
|
||||
}
|
||||
|
||||
@ -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_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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_
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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_
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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_
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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_
|
||||
|
||||
44
main/boards/common/lamp_controller.h
Normal file
44
main/boards/common/lamp_controller.h
Normal 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__
|
||||
@ -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);
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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);
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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);
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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"));
|
||||
}
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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_
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -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);
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
@ -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:
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user