add iot framework
This commit is contained in:
parent
43b1046df5
commit
d31901e9e5
@ -4,7 +4,7 @@
|
|||||||
# CMakeLists in this exact order for cmake to work correctly
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
cmake_minimum_required(VERSION 3.16)
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
set(PROJECT_VER "0.9.6")
|
set(PROJECT_VER "0.9.7")
|
||||||
|
|
||||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
project(xiaozhi)
|
project(xiaozhi)
|
||||||
|
|||||||
@ -9,6 +9,8 @@ set(SOURCES "audio_codecs/audio_codec.cc"
|
|||||||
"protocols/protocol.cc"
|
"protocols/protocol.cc"
|
||||||
"protocols/mqtt_protocol.cc"
|
"protocols/mqtt_protocol.cc"
|
||||||
"protocols/websocket_protocol.cc"
|
"protocols/websocket_protocol.cc"
|
||||||
|
"iot/thing.cc"
|
||||||
|
"iot/thing_manager.cc"
|
||||||
"system_info.cc"
|
"system_info.cc"
|
||||||
"application.cc"
|
"application.cc"
|
||||||
"ota.cc"
|
"ota.cc"
|
||||||
@ -19,6 +21,10 @@ set(SOURCES "audio_codecs/audio_codec.cc"
|
|||||||
|
|
||||||
set(INCLUDE_DIRS "." "display" "audio_codecs" "protocols" "audio_processing")
|
set(INCLUDE_DIRS "." "display" "audio_codecs" "protocols" "audio_processing")
|
||||||
|
|
||||||
|
# 添加 IOT 相关文件
|
||||||
|
file(GLOB IOT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/iot/things/*.cc)
|
||||||
|
list(APPEND SOURCES ${IOT_SOURCES})
|
||||||
|
|
||||||
# 字体
|
# 字体
|
||||||
file(GLOB FONT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/fonts/*.c)
|
file(GLOB FONT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/fonts/*.c)
|
||||||
list(APPEND SOURCES ${FONT_SOURCES})
|
list(APPEND SOURCES ${FONT_SOURCES})
|
||||||
@ -44,6 +50,8 @@ elseif(CONFIG_BOARD_TYPE_KEVIN_C3)
|
|||||||
set(BOARD_TYPE "kevin-c3")
|
set(BOARD_TYPE "kevin-c3")
|
||||||
elseif(CONFIG_BOARD_TYPE_LICHUANG_DEV)
|
elseif(CONFIG_BOARD_TYPE_LICHUANG_DEV)
|
||||||
set(BOARD_TYPE "lichuang-dev")
|
set(BOARD_TYPE "lichuang-dev")
|
||||||
|
elseif(CONFIG_BOARD_TYPE_TERRENCE_C3_DEV)
|
||||||
|
set(BOARD_TYPE "terrence-c3-dev")
|
||||||
endif()
|
endif()
|
||||||
file(GLOB BOARD_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc)
|
file(GLOB BOARD_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc)
|
||||||
list(APPEND SOURCES ${BOARD_SOURCES})
|
list(APPEND SOURCES ${BOARD_SOURCES})
|
||||||
@ -55,6 +63,7 @@ endif()
|
|||||||
idf_component_register(SRCS ${SOURCES}
|
idf_component_register(SRCS ${SOURCES}
|
||||||
EMBED_FILES "assets/err_reg.p3" "assets/err_pin.p3" "assets/err_wificonfig.p3"
|
EMBED_FILES "assets/err_reg.p3" "assets/err_pin.p3" "assets/err_wificonfig.p3"
|
||||||
INCLUDE_DIRS ${INCLUDE_DIRS}
|
INCLUDE_DIRS ${INCLUDE_DIRS}
|
||||||
|
WHOLE_ARCHIVE
|
||||||
)
|
)
|
||||||
|
|
||||||
# 使用 target_compile_definitions 来定义 BOARD_TYPE
|
# 使用 target_compile_definitions 来定义 BOARD_TYPE
|
||||||
|
|||||||
@ -1,10 +1,13 @@
|
|||||||
#include "application.h"
|
#include "application.h"
|
||||||
|
#include "board.h"
|
||||||
|
#include "display.h"
|
||||||
#include "system_info.h"
|
#include "system_info.h"
|
||||||
#include "ml307_ssl_transport.h"
|
#include "ml307_ssl_transport.h"
|
||||||
#include "audio_codec.h"
|
#include "audio_codec.h"
|
||||||
#include "mqtt_protocol.h"
|
#include "mqtt_protocol.h"
|
||||||
#include "websocket_protocol.h"
|
#include "websocket_protocol.h"
|
||||||
#include "font_awesome_symbols.h"
|
#include "font_awesome_symbols.h"
|
||||||
|
#include "iot/thing_manager.h"
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
@ -183,8 +186,6 @@ void Application::StopListening() {
|
|||||||
|
|
||||||
void Application::Start() {
|
void Application::Start() {
|
||||||
auto& board = Board::GetInstance();
|
auto& board = Board::GetInstance();
|
||||||
board.Initialize();
|
|
||||||
|
|
||||||
auto builtin_led = board.GetBuiltinLed();
|
auto builtin_led = board.GetBuiltinLed();
|
||||||
builtin_led->SetBlue();
|
builtin_led->SetBlue();
|
||||||
builtin_led->StartContinuousBlink(100);
|
builtin_led->StartContinuousBlink(100);
|
||||||
@ -308,18 +309,22 @@ void Application::Start() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
protocol_->OnAudioChannelOpened([this, codec, &board]() {
|
protocol_->OnAudioChannelOpened([this, codec, &board]() {
|
||||||
|
board.SetPowerSaveMode(false);
|
||||||
if (protocol_->server_sample_rate() != codec->output_sample_rate()) {
|
if (protocol_->server_sample_rate() != codec->output_sample_rate()) {
|
||||||
ESP_LOGW(TAG, "服务器的音频采样率 %d 与设备输出的采样率 %d 不一致,重采样后可能会失真",
|
ESP_LOGW(TAG, "服务器的音频采样率 %d 与设备输出的采样率 %d 不一致,重采样后可能会失真",
|
||||||
protocol_->server_sample_rate(), codec->output_sample_rate());
|
protocol_->server_sample_rate(), codec->output_sample_rate());
|
||||||
}
|
}
|
||||||
SetDecodeSampleRate(protocol_->server_sample_rate());
|
SetDecodeSampleRate(protocol_->server_sample_rate());
|
||||||
board.SetPowerSaveMode(false);
|
// 物联网设备描述符
|
||||||
|
last_iot_states_.clear();
|
||||||
|
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||||
|
protocol_->SendIotDescriptors(thing_manager.GetDescriptorsJson());
|
||||||
});
|
});
|
||||||
protocol_->OnAudioChannelClosed([this, &board]() {
|
protocol_->OnAudioChannelClosed([this, &board]() {
|
||||||
|
board.SetPowerSaveMode(true);
|
||||||
Schedule([this]() {
|
Schedule([this]() {
|
||||||
SetChatState(kChatStateIdle);
|
SetChatState(kChatStateIdle);
|
||||||
});
|
});
|
||||||
board.SetPowerSaveMode(true);
|
|
||||||
});
|
});
|
||||||
protocol_->OnIncomingJson([this, display](const cJSON* root) {
|
protocol_->OnIncomingJson([this, display](const cJSON* root) {
|
||||||
// Parse JSON data
|
// Parse JSON data
|
||||||
@ -363,6 +368,15 @@ void Application::Start() {
|
|||||||
if (emotion != NULL) {
|
if (emotion != NULL) {
|
||||||
display->SetEmotion(emotion->valuestring);
|
display->SetEmotion(emotion->valuestring);
|
||||||
}
|
}
|
||||||
|
} else if (strcmp(type->valuestring, "iot") == 0) {
|
||||||
|
auto commands = cJSON_GetObjectItem(root, "commands");
|
||||||
|
if (commands != NULL) {
|
||||||
|
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||||
|
for (int i = 0; i < cJSON_GetArraySize(commands); ++i) {
|
||||||
|
auto command = cJSON_GetArrayItem(commands, i);
|
||||||
|
thing_manager.Invoke(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -557,6 +571,7 @@ void Application::SetChatState(ChatState state) {
|
|||||||
#if CONFIG_IDF_TARGET_ESP32S3
|
#if CONFIG_IDF_TARGET_ESP32S3
|
||||||
audio_processor_.Start();
|
audio_processor_.Start();
|
||||||
#endif
|
#endif
|
||||||
|
UpdateIotStates();
|
||||||
break;
|
break;
|
||||||
case kChatStateSpeaking:
|
case kChatStateSpeaking:
|
||||||
builtin_led->SetGreen();
|
builtin_led->SetGreen();
|
||||||
@ -591,3 +606,12 @@ void Application::SetDecodeSampleRate(int sample_rate) {
|
|||||||
output_resampler_.Configure(opus_decode_sample_rate_, codec->output_sample_rate());
|
output_resampler_.Configure(opus_decode_sample_rate_, codec->output_sample_rate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::UpdateIotStates() {
|
||||||
|
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||||
|
auto states = thing_manager.GetStatesJson();
|
||||||
|
if (states != last_iot_states_) {
|
||||||
|
last_iot_states_ = states;
|
||||||
|
protocol_->SendIotStates(states);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -4,18 +4,16 @@
|
|||||||
#include <freertos/FreeRTOS.h>
|
#include <freertos/FreeRTOS.h>
|
||||||
#include <freertos/event_groups.h>
|
#include <freertos/event_groups.h>
|
||||||
#include <freertos/task.h>
|
#include <freertos/task.h>
|
||||||
#include <opus.h>
|
|
||||||
|
#include <string>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <condition_variable>
|
|
||||||
|
|
||||||
#include "opus_encoder.h"
|
#include <opus_encoder.h>
|
||||||
#include "opus_decoder.h"
|
#include <opus_decoder.h>
|
||||||
#include "opus_resampler.h"
|
#include <opus_resampler.h>
|
||||||
|
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
#include "display.h"
|
|
||||||
#include "board.h"
|
|
||||||
#include "ota.h"
|
#include "ota.h"
|
||||||
#include "background_task.h"
|
#include "background_task.h"
|
||||||
|
|
||||||
@ -58,6 +56,7 @@ public:
|
|||||||
void ToggleChatState();
|
void ToggleChatState();
|
||||||
void StartListening();
|
void StartListening();
|
||||||
void StopListening();
|
void StopListening();
|
||||||
|
void UpdateIotStates();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Application();
|
Application();
|
||||||
@ -75,6 +74,7 @@ private:
|
|||||||
volatile ChatState chat_state_ = kChatStateUnknown;
|
volatile ChatState chat_state_ = kChatStateUnknown;
|
||||||
bool keep_listening_ = false;
|
bool keep_listening_ = false;
|
||||||
bool aborted_ = false;
|
bool aborted_ = false;
|
||||||
|
std::string last_iot_states_;
|
||||||
|
|
||||||
// Audio encode / decode
|
// Audio encode / decode
|
||||||
BackgroundTask background_task_;
|
BackgroundTask background_task_;
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include "button.h"
|
#include "button.h"
|
||||||
#include "led.h"
|
#include "led.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "iot/thing_manager.h"
|
||||||
|
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
#include <driver/i2c_master.h>
|
#include <driver/i2c_master.h>
|
||||||
@ -59,8 +60,7 @@ private:
|
|||||||
});
|
});
|
||||||
|
|
||||||
volume_up_button_.OnLongPress([this]() {
|
volume_up_button_.OnLongPress([this]() {
|
||||||
auto codec = GetAudioCodec();
|
GetAudioCodec()->SetOutputVolume(100);
|
||||||
codec->SetOutputVolume(100);
|
|
||||||
GetDisplay()->ShowNotification("最大音量");
|
GetDisplay()->ShowNotification("最大音量");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -75,12 +75,18 @@ private:
|
|||||||
});
|
});
|
||||||
|
|
||||||
volume_down_button_.OnLongPress([this]() {
|
volume_down_button_.OnLongPress([this]() {
|
||||||
auto codec = GetAudioCodec();
|
GetAudioCodec()->SetOutputVolume(0);
|
||||||
codec->SetOutputVolume(0);
|
|
||||||
GetDisplay()->ShowNotification("已静音");
|
GetDisplay()->ShowNotification("已静音");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 物联网初始化,添加对 AI 可见设备
|
||||||
|
void InitializeIot() {
|
||||||
|
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||||
|
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||||
|
thing_manager.AddThing(iot::CreateThing("Lamp"));
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CompactMl307Board() : Ml307Board(ML307_TX_PIN, ML307_RX_PIN, 4096),
|
CompactMl307Board() : Ml307Board(ML307_TX_PIN, ML307_RX_PIN, 4096),
|
||||||
boot_button_(BOOT_BUTTON_GPIO),
|
boot_button_(BOOT_BUTTON_GPIO),
|
||||||
@ -88,17 +94,12 @@ public:
|
|||||||
volume_up_button_(VOLUME_UP_BUTTON_GPIO),
|
volume_up_button_(VOLUME_UP_BUTTON_GPIO),
|
||||||
volume_down_button_(VOLUME_DOWN_BUTTON_GPIO),
|
volume_down_button_(VOLUME_DOWN_BUTTON_GPIO),
|
||||||
system_reset_(RESET_NVS_BUTTON_GPIO, RESET_FACTORY_BUTTON_GPIO) {
|
system_reset_(RESET_NVS_BUTTON_GPIO, RESET_FACTORY_BUTTON_GPIO) {
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Initialize() override {
|
|
||||||
ESP_LOGI(TAG, "Initializing CompactMl307Board");
|
|
||||||
// Check if the reset button is pressed
|
// Check if the reset button is pressed
|
||||||
system_reset_.CheckButtons();
|
system_reset_.CheckButtons();
|
||||||
|
|
||||||
InitializeDisplayI2c();
|
InitializeDisplayI2c();
|
||||||
InitializeButtons();
|
InitializeButtons();
|
||||||
|
InitializeIot();
|
||||||
Ml307Board::Initialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Led* GetBuiltinLed() override {
|
virtual Led* GetBuiltinLed() override {
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
#include "button.h"
|
#include "button.h"
|
||||||
#include "led.h"
|
#include "led.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "iot/thing_manager.h"
|
||||||
|
|
||||||
#include <wifi_station.h>
|
#include <wifi_station.h>
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
@ -64,8 +65,7 @@ private:
|
|||||||
});
|
});
|
||||||
|
|
||||||
volume_up_button_.OnLongPress([this]() {
|
volume_up_button_.OnLongPress([this]() {
|
||||||
auto codec = GetAudioCodec();
|
GetAudioCodec()->SetOutputVolume(100);
|
||||||
codec->SetOutputVolume(100);
|
|
||||||
GetDisplay()->ShowNotification("最大音量");
|
GetDisplay()->ShowNotification("最大音量");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -80,30 +80,31 @@ private:
|
|||||||
});
|
});
|
||||||
|
|
||||||
volume_down_button_.OnLongPress([this]() {
|
volume_down_button_.OnLongPress([this]() {
|
||||||
auto codec = GetAudioCodec();
|
GetAudioCodec()->SetOutputVolume(0);
|
||||||
codec->SetOutputVolume(0);
|
|
||||||
GetDisplay()->ShowNotification("已静音");
|
GetDisplay()->ShowNotification("已静音");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 物联网初始化,添加对 AI 可见设备
|
||||||
|
void InitializeIot() {
|
||||||
|
auto& thing_manager = iot::ThingManager::GetInstance();
|
||||||
|
thing_manager.AddThing(iot::CreateThing("Speaker"));
|
||||||
|
thing_manager.AddThing(iot::CreateThing("Lamp"));
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CompactWifiBoard() :
|
CompactWifiBoard() :
|
||||||
boot_button_(BOOT_BUTTON_GPIO),
|
boot_button_(BOOT_BUTTON_GPIO),
|
||||||
touch_button_(TOUCH_BUTTON_GPIO, 1),
|
touch_button_(TOUCH_BUTTON_GPIO),
|
||||||
volume_up_button_(VOLUME_UP_BUTTON_GPIO),
|
volume_up_button_(VOLUME_UP_BUTTON_GPIO),
|
||||||
volume_down_button_(VOLUME_DOWN_BUTTON_GPIO),
|
volume_down_button_(VOLUME_DOWN_BUTTON_GPIO),
|
||||||
system_reset_(RESET_NVS_BUTTON_GPIO, RESET_FACTORY_BUTTON_GPIO) {
|
system_reset_(RESET_NVS_BUTTON_GPIO, RESET_FACTORY_BUTTON_GPIO) {
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Initialize() override {
|
|
||||||
ESP_LOGI(TAG, "Initializing CompactWifiBoard");
|
|
||||||
// Check if the reset button is pressed
|
// Check if the reset button is pressed
|
||||||
system_reset_.CheckButtons();
|
system_reset_.CheckButtons();
|
||||||
|
|
||||||
InitializeDisplayI2c();
|
InitializeDisplayI2c();
|
||||||
InitializeButtons();
|
InitializeButtons();
|
||||||
|
InitializeIot();
|
||||||
WifiBoard::Initialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Led* GetBuiltinLed() override {
|
virtual Led* GetBuiltinLed() override {
|
||||||
|
|||||||
@ -6,7 +6,10 @@
|
|||||||
#include <esp_ota_ops.h>
|
#include <esp_ota_ops.h>
|
||||||
#include <esp_chip_info.h>
|
#include <esp_chip_info.h>
|
||||||
|
|
||||||
// static const char *TAG = "Board";
|
#define TAG "Board"
|
||||||
|
|
||||||
|
Board::Board() {
|
||||||
|
}
|
||||||
|
|
||||||
bool Board::GetBatteryLevel(int &level, bool& charging) {
|
bool Board::GetBatteryLevel(int &level, bool& charging) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -19,7 +19,7 @@ private:
|
|||||||
virtual std::string GetBoardJson() = 0;
|
virtual std::string GetBoardJson() = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Board() = default;
|
Board();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static Board& GetInstance() {
|
static Board& GetInstance() {
|
||||||
@ -30,7 +30,6 @@ public:
|
|||||||
return *instance;
|
return *instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void Initialize() = 0;
|
|
||||||
virtual void StartNetwork() = 0;
|
virtual void StartNetwork() = 0;
|
||||||
virtual ~Board() = default;
|
virtual ~Board() = default;
|
||||||
virtual Led* GetBuiltinLed() = 0;
|
virtual Led* GetBuiltinLed() = 0;
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#include "ml307_board.h"
|
#include "ml307_board.h"
|
||||||
|
|
||||||
#include "application.h"
|
#include "application.h"
|
||||||
|
#include "display.h"
|
||||||
#include "font_awesome_symbols.h"
|
#include "font_awesome_symbols.h"
|
||||||
|
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
@ -72,10 +74,9 @@ void Ml307Board::WaitForNetworkReady() {
|
|||||||
ESP_LOGI(TAG, "ML307 Module: %s", module_name.c_str());
|
ESP_LOGI(TAG, "ML307 Module: %s", module_name.c_str());
|
||||||
ESP_LOGI(TAG, "ML307 IMEI: %s", imei.c_str());
|
ESP_LOGI(TAG, "ML307 IMEI: %s", imei.c_str());
|
||||||
ESP_LOGI(TAG, "ML307 ICCID: %s", iccid.c_str());
|
ESP_LOGI(TAG, "ML307 ICCID: %s", iccid.c_str());
|
||||||
}
|
|
||||||
|
|
||||||
void Ml307Board::Initialize() {
|
// Close all previous connections
|
||||||
ESP_LOGI(TAG, "Initializing Ml307Board");
|
modem_.ResetConnections();
|
||||||
}
|
}
|
||||||
|
|
||||||
Http* Ml307Board::CreateHttp() {
|
Http* Ml307Board::CreateHttp() {
|
||||||
|
|||||||
@ -13,7 +13,6 @@ protected:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
Ml307Board(gpio_num_t tx_pin, gpio_num_t rx_pin, size_t rx_buffer_size = 4096);
|
Ml307Board(gpio_num_t tx_pin, gpio_num_t rx_pin, size_t rx_buffer_size = 4096);
|
||||||
virtual void Initialize() override;
|
|
||||||
virtual void StartNetwork() override;
|
virtual void StartNetwork() override;
|
||||||
virtual Http* CreateHttp() override;
|
virtual Http* CreateHttp() override;
|
||||||
virtual WebSocket* CreateWebSocket() override;
|
virtual WebSocket* CreateWebSocket() override;
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
#include "wifi_board.h"
|
#include "wifi_board.h"
|
||||||
|
|
||||||
|
#include "display.h"
|
||||||
#include "application.h"
|
#include "application.h"
|
||||||
#include "system_info.h"
|
#include "system_info.h"
|
||||||
#include "font_awesome_symbols.h"
|
#include "font_awesome_symbols.h"
|
||||||
@ -70,10 +72,6 @@ void WifiBoard::StartNetwork() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WifiBoard::Initialize() {
|
|
||||||
ESP_LOGI(TAG, "Initializing WifiBoard");
|
|
||||||
}
|
|
||||||
|
|
||||||
Http* WifiBoard::CreateHttp() {
|
Http* WifiBoard::CreateHttp() {
|
||||||
return new EspHttp();
|
return new EspHttp();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,6 @@ protected:
|
|||||||
virtual std::string GetBoardJson() override;
|
virtual std::string GetBoardJson() override;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual void Initialize() override;
|
|
||||||
virtual void StartNetwork() override;
|
virtual void StartNetwork() override;
|
||||||
virtual Http* CreateHttp() override;
|
virtual Http* CreateHttp() override;
|
||||||
virtual WebSocket* CreateWebSocket() override;
|
virtual WebSocket* CreateWebSocket() override;
|
||||||
|
|||||||
@ -41,13 +41,8 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
EspBox3Board() : boot_button_(BOOT_BUTTON_GPIO) {
|
EspBox3Board() : boot_button_(BOOT_BUTTON_GPIO) {
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Initialize() override {
|
|
||||||
ESP_LOGI(TAG, "Initializing EspBox3Board");
|
|
||||||
InitializeI2c();
|
InitializeI2c();
|
||||||
InitializeButtons();
|
InitializeButtons();
|
||||||
WifiBoard::Initialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Led* GetBuiltinLed() override {
|
virtual Led* GetBuiltinLed() override {
|
||||||
|
|||||||
@ -122,18 +122,12 @@ public:
|
|||||||
boot_button_(BOOT_BUTTON_GPIO),
|
boot_button_(BOOT_BUTTON_GPIO),
|
||||||
volume_up_button_(VOLUME_UP_BUTTON_GPIO),
|
volume_up_button_(VOLUME_UP_BUTTON_GPIO),
|
||||||
volume_down_button_(VOLUME_DOWN_BUTTON_GPIO) {
|
volume_down_button_(VOLUME_DOWN_BUTTON_GPIO) {
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Initialize() override {
|
|
||||||
ESP_LOGI(TAG, "Initializing KevinBoxBoard");
|
|
||||||
InitializeDisplayI2c();
|
InitializeDisplayI2c();
|
||||||
InitializeCodecI2c();
|
InitializeCodecI2c();
|
||||||
MountStorage();
|
MountStorage();
|
||||||
Enable4GModule();
|
Enable4GModule();
|
||||||
|
|
||||||
InitializeButtons();
|
InitializeButtons();
|
||||||
|
|
||||||
Ml307Board::Initialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Led* GetBuiltinLed() override {
|
virtual Led* GetBuiltinLed() override {
|
||||||
|
|||||||
@ -163,10 +163,6 @@ public:
|
|||||||
boot_button_(BOOT_BUTTON_GPIO),
|
boot_button_(BOOT_BUTTON_GPIO),
|
||||||
volume_up_button_(VOLUME_UP_BUTTON_GPIO),
|
volume_up_button_(VOLUME_UP_BUTTON_GPIO),
|
||||||
volume_down_button_(VOLUME_DOWN_BUTTON_GPIO) {
|
volume_down_button_(VOLUME_DOWN_BUTTON_GPIO) {
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Initialize() override {
|
|
||||||
ESP_LOGI(TAG, "Initializing KevinBoxBoard");
|
|
||||||
InitializeDisplayI2c();
|
InitializeDisplayI2c();
|
||||||
InitializeCodecI2c();
|
InitializeCodecI2c();
|
||||||
axp2101_ = new Axp2101(codec_i2c_bus_, AXP2101_I2C_ADDR);
|
axp2101_ = new Axp2101(codec_i2c_bus_, AXP2101_I2C_ADDR);
|
||||||
@ -176,8 +172,6 @@ public:
|
|||||||
|
|
||||||
InitializeButtons();
|
InitializeButtons();
|
||||||
InitializePowerSaveTimer();
|
InitializePowerSaveTimer();
|
||||||
|
|
||||||
Ml307Board::Initialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Led* GetBuiltinLed() override {
|
virtual Led* GetBuiltinLed() override {
|
||||||
|
|||||||
@ -49,17 +49,9 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
KevinBoxBoard() :
|
KevinBoxBoard() : boot_button_(BOOT_BUTTON_GPIO) {
|
||||||
boot_button_(BOOT_BUTTON_GPIO) {
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Initialize() override {
|
|
||||||
ESP_LOGI(TAG, "Initializing KevinBoxBoard");
|
|
||||||
|
|
||||||
InitializeCodecI2c();
|
InitializeCodecI2c();
|
||||||
InitializeButtons();
|
InitializeButtons();
|
||||||
|
|
||||||
WifiBoard::Initialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Led* GetBuiltinLed() override {
|
virtual Led* GetBuiltinLed() override {
|
||||||
|
|||||||
@ -121,15 +121,10 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
LichuangDevBoard() : boot_button_(BOOT_BUTTON_GPIO) {
|
LichuangDevBoard() : boot_button_(BOOT_BUTTON_GPIO) {
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Initialize() override {
|
|
||||||
ESP_LOGI(TAG, "Initializing LichuangDevBoard");
|
|
||||||
InitializeI2c();
|
InitializeI2c();
|
||||||
InitializeSpi();
|
InitializeSpi();
|
||||||
InitializeSt7789Display();
|
InitializeSt7789Display();
|
||||||
InitializeButtons();
|
InitializeButtons();
|
||||||
WifiBoard::Initialize();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual Led* GetBuiltinLed() override {
|
virtual Led* GetBuiltinLed() override {
|
||||||
|
|||||||
38
main/iot/sample_interface.json
Normal file
38
main/iot/sample_interface.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "lamp",
|
||||||
|
"description": "A lamp",
|
||||||
|
"properties": {
|
||||||
|
"power": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether the lamp is on or off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"TurnOn": {
|
||||||
|
"description": "Turns the lamp on"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "speaker",
|
||||||
|
"description": "当前 AI 机器人的扬声器",
|
||||||
|
"properties": {
|
||||||
|
"volume": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "当前扬声器的音量(0-100)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"methods": {
|
||||||
|
"SetVolume": {
|
||||||
|
"description": "设置当前扬声器的音量",
|
||||||
|
"parameters": {
|
||||||
|
"volume": {
|
||||||
|
"type": "number",
|
||||||
|
"description": "The volume of the speaker (0-100)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
77
main/iot/thing.cc
Normal file
77
main/iot/thing.cc
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#include "thing.h"
|
||||||
|
#include "application.h"
|
||||||
|
|
||||||
|
#include <esp_log.h>
|
||||||
|
|
||||||
|
#define TAG "Thing"
|
||||||
|
|
||||||
|
|
||||||
|
namespace iot {
|
||||||
|
|
||||||
|
static std::map<std::string, std::function<Thing*()>>* thing_creators = nullptr;
|
||||||
|
|
||||||
|
void RegisterThing(const std::string& type, std::function<Thing*()> creator) {
|
||||||
|
if (thing_creators == nullptr) {
|
||||||
|
thing_creators = new std::map<std::string, std::function<Thing*()>>();
|
||||||
|
}
|
||||||
|
(*thing_creators)[type] = creator;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thing* CreateThing(const std::string& type) {
|
||||||
|
auto creator = thing_creators->find(type);
|
||||||
|
if (creator == thing_creators->end()) {
|
||||||
|
ESP_LOGE(TAG, "Thing type not found: %s", type.c_str());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return creator->second();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Thing::GetDescriptorJson() {
|
||||||
|
std::string json_str = "{";
|
||||||
|
json_str += "\"name\":\"" + name_ + "\",";
|
||||||
|
json_str += "\"description\":\"" + description_ + "\",";
|
||||||
|
json_str += "\"properties\":" + properties_.GetDescriptorJson() + ",";
|
||||||
|
json_str += "\"methods\":" + methods_.GetDescriptorJson();
|
||||||
|
json_str += "}";
|
||||||
|
return json_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Thing::GetStateJson() {
|
||||||
|
std::string json_str = "{";
|
||||||
|
json_str += "\"name\":\"" + name_ + "\",";
|
||||||
|
json_str += "\"state\":" + properties_.GetStateJson();
|
||||||
|
json_str += "}";
|
||||||
|
return json_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thing::Invoke(const cJSON* command) {
|
||||||
|
auto method_name = cJSON_GetObjectItem(command, "method");
|
||||||
|
auto input_params = cJSON_GetObjectItem(command, "parameters");
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto& method = methods_[method_name->valuestring];
|
||||||
|
for (auto& param : method.parameters()) {
|
||||||
|
auto input_param = cJSON_GetObjectItem(input_params, param.name().c_str());
|
||||||
|
if (param.required() && input_param == nullptr) {
|
||||||
|
throw std::runtime_error("Parameter " + param.name() + " is required");
|
||||||
|
}
|
||||||
|
if (param.type() == kValueTypeNumber) {
|
||||||
|
param.set_number(input_param->valueint);
|
||||||
|
} else if (param.type() == kValueTypeString) {
|
||||||
|
param.set_string(input_param->valuestring);
|
||||||
|
} else if (param.type() == kValueTypeBoolean) {
|
||||||
|
param.set_boolean(input_param->valueint == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Application::GetInstance().Schedule([&method]() {
|
||||||
|
method.Invoke();
|
||||||
|
});
|
||||||
|
} catch (const std::runtime_error& e) {
|
||||||
|
ESP_LOGE(TAG, "Method not found: %s", method_name->valuestring);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace iot
|
||||||
300
main/iot/thing.h
Normal file
300
main/iot/thing.h
Normal file
@ -0,0 +1,300 @@
|
|||||||
|
#ifndef THING_H
|
||||||
|
#define THING_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <cJSON.h>
|
||||||
|
|
||||||
|
namespace iot {
|
||||||
|
|
||||||
|
enum ValueType {
|
||||||
|
kValueTypeBoolean,
|
||||||
|
kValueTypeNumber,
|
||||||
|
kValueTypeString
|
||||||
|
};
|
||||||
|
|
||||||
|
class Property {
|
||||||
|
private:
|
||||||
|
std::string name_;
|
||||||
|
std::string description_;
|
||||||
|
ValueType type_;
|
||||||
|
std::function<bool()> boolean_getter_;
|
||||||
|
std::function<int()> number_getter_;
|
||||||
|
std::function<std::string()> string_getter_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Property(const std::string& name, const std::string& description, std::function<bool()> getter) :
|
||||||
|
name_(name), description_(description), type_(kValueTypeBoolean), boolean_getter_(getter) {}
|
||||||
|
Property(const std::string& name, const std::string& description, std::function<int()> getter) :
|
||||||
|
name_(name), description_(description), type_(kValueTypeNumber), number_getter_(getter) {}
|
||||||
|
Property(const std::string& name, const std::string& description, std::function<std::string()> getter) :
|
||||||
|
name_(name), description_(description), type_(kValueTypeString), string_getter_(getter) {}
|
||||||
|
|
||||||
|
const std::string& name() const { return name_; }
|
||||||
|
const std::string& description() const { return description_; }
|
||||||
|
ValueType type() const { return type_; }
|
||||||
|
|
||||||
|
bool boolean() const { return boolean_getter_(); }
|
||||||
|
int number() const { return number_getter_(); }
|
||||||
|
std::string string() const { return string_getter_(); }
|
||||||
|
|
||||||
|
std::string GetDescriptorJson() {
|
||||||
|
std::string json_str = "{";
|
||||||
|
json_str += "\"description\":\"" + description_ + "\",";
|
||||||
|
if (type_ == kValueTypeBoolean) {
|
||||||
|
json_str += "\"type\":\"boolean\"";
|
||||||
|
} else if (type_ == kValueTypeNumber) {
|
||||||
|
json_str += "\"type\":\"number\"";
|
||||||
|
} else if (type_ == kValueTypeString) {
|
||||||
|
json_str += "\"type\":\"string\"";
|
||||||
|
}
|
||||||
|
json_str += "}";
|
||||||
|
return json_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetStateJson() {
|
||||||
|
if (type_ == kValueTypeBoolean) {
|
||||||
|
return boolean_getter_() ? "true" : "false";
|
||||||
|
} else if (type_ == kValueTypeNumber) {
|
||||||
|
return std::to_string(number_getter_());
|
||||||
|
} else if (type_ == kValueTypeString) {
|
||||||
|
return "\"" + string_getter_() + "\"";
|
||||||
|
}
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class PropertyList {
|
||||||
|
private:
|
||||||
|
std::vector<Property> properties_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
PropertyList() = default;
|
||||||
|
PropertyList(const std::vector<Property>& properties) : properties_(properties) {}
|
||||||
|
|
||||||
|
void AddBooleanProperty(const std::string& name, const std::string& description, std::function<bool()> getter) {
|
||||||
|
properties_.push_back(Property(name, description, getter));
|
||||||
|
}
|
||||||
|
void AddNumberProperty(const std::string& name, const std::string& description, std::function<int()> getter) {
|
||||||
|
properties_.push_back(Property(name, description, getter));
|
||||||
|
}
|
||||||
|
void AddStringProperty(const std::string& name, const std::string& description, std::function<std::string()> getter) {
|
||||||
|
properties_.push_back(Property(name, description, getter));
|
||||||
|
}
|
||||||
|
|
||||||
|
const Property& operator[](const std::string& name) const {
|
||||||
|
for (auto& property : properties_) {
|
||||||
|
if (property.name() == name) {
|
||||||
|
return property;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Property not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetDescriptorJson() {
|
||||||
|
std::string json_str = "{";
|
||||||
|
for (auto& property : properties_) {
|
||||||
|
json_str += "\"" + property.name() + "\":" + property.GetDescriptorJson() + ",";
|
||||||
|
}
|
||||||
|
if (json_str.back() == ',') {
|
||||||
|
json_str.pop_back();
|
||||||
|
}
|
||||||
|
json_str += "}";
|
||||||
|
return json_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetStateJson() {
|
||||||
|
std::string json_str = "{";
|
||||||
|
for (auto& property : properties_) {
|
||||||
|
json_str += "\"" + property.name() + "\":" + property.GetStateJson() + ",";
|
||||||
|
}
|
||||||
|
if (json_str.back() == ',') {
|
||||||
|
json_str.pop_back();
|
||||||
|
}
|
||||||
|
json_str += "}";
|
||||||
|
return json_str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Parameter {
|
||||||
|
private:
|
||||||
|
std::string name_;
|
||||||
|
std::string description_;
|
||||||
|
ValueType type_;
|
||||||
|
bool required_;
|
||||||
|
bool boolean_;
|
||||||
|
int number_;
|
||||||
|
std::string string_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Parameter(const std::string& name, const std::string& description, ValueType type, bool required = true) :
|
||||||
|
name_(name), description_(description), type_(type), required_(required) {}
|
||||||
|
|
||||||
|
const std::string& name() const { return name_; }
|
||||||
|
const std::string& description() const { return description_; }
|
||||||
|
ValueType type() const { return type_; }
|
||||||
|
bool required() const { return required_; }
|
||||||
|
|
||||||
|
bool boolean() const { return boolean_; }
|
||||||
|
int number() const { return number_; }
|
||||||
|
const std::string& string() const { return string_; }
|
||||||
|
|
||||||
|
void set_boolean(bool value) { boolean_ = value; }
|
||||||
|
void set_number(int value) { number_ = value; }
|
||||||
|
void set_string(const std::string& value) { string_ = value; }
|
||||||
|
|
||||||
|
std::string GetDescriptorJson() {
|
||||||
|
std::string json_str = "{";
|
||||||
|
json_str += "\"description\":\"" + description_ + "\",";
|
||||||
|
if (type_ == kValueTypeBoolean) {
|
||||||
|
json_str += "\"type\":\"boolean\"";
|
||||||
|
} else if (type_ == kValueTypeNumber) {
|
||||||
|
json_str += "\"type\":\"number\"";
|
||||||
|
} else if (type_ == kValueTypeString) {
|
||||||
|
json_str += "\"type\":\"string\"";
|
||||||
|
}
|
||||||
|
json_str += "}";
|
||||||
|
return json_str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ParameterList {
|
||||||
|
private:
|
||||||
|
std::vector<Parameter> parameters_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ParameterList() = default;
|
||||||
|
ParameterList(const std::vector<Parameter>& parameters) : parameters_(parameters) {}
|
||||||
|
void AddParameter(const Parameter& parameter) {
|
||||||
|
parameters_.push_back(parameter);
|
||||||
|
}
|
||||||
|
|
||||||
|
const Parameter& operator[](const std::string& name) const {
|
||||||
|
for (auto& parameter : parameters_) {
|
||||||
|
if (parameter.name() == name) {
|
||||||
|
return parameter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Parameter not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterator
|
||||||
|
auto begin() { return parameters_.begin(); }
|
||||||
|
auto end() { return parameters_.end(); }
|
||||||
|
|
||||||
|
std::string GetDescriptorJson() {
|
||||||
|
std::string json_str = "{";
|
||||||
|
for (auto& parameter : parameters_) {
|
||||||
|
json_str += "\"" + parameter.name() + "\":" + parameter.GetDescriptorJson() + ",";
|
||||||
|
}
|
||||||
|
if (json_str.back() == ',') {
|
||||||
|
json_str.pop_back();
|
||||||
|
}
|
||||||
|
json_str += "}";
|
||||||
|
return json_str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Method {
|
||||||
|
private:
|
||||||
|
std::string name_;
|
||||||
|
std::string description_;
|
||||||
|
ParameterList parameters_;
|
||||||
|
std::function<void(const ParameterList&)> callback_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Method(const std::string& name, const std::string& description, const ParameterList& parameters, std::function<void(const ParameterList&)> callback) :
|
||||||
|
name_(name), description_(description), parameters_(parameters), callback_(callback) {}
|
||||||
|
|
||||||
|
const std::string& name() const { return name_; }
|
||||||
|
const std::string& description() const { return description_; }
|
||||||
|
ParameterList& parameters() { return parameters_; }
|
||||||
|
|
||||||
|
std::string GetDescriptorJson() {
|
||||||
|
std::string json_str = "{";
|
||||||
|
json_str += "\"description\":\"" + description_ + "\",";
|
||||||
|
json_str += "\"parameters\":" + parameters_.GetDescriptorJson();
|
||||||
|
json_str += "}";
|
||||||
|
return json_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Invoke() {
|
||||||
|
callback_(parameters_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class MethodList {
|
||||||
|
private:
|
||||||
|
std::vector<Method> methods_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MethodList() = default;
|
||||||
|
MethodList(const std::vector<Method>& methods) : methods_(methods) {}
|
||||||
|
|
||||||
|
void AddMethod(const std::string& name, const std::string& description, const ParameterList& parameters, std::function<void(const ParameterList&)> callback) {
|
||||||
|
methods_.push_back(Method(name, description, parameters, callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
Method& operator[](const std::string& name) {
|
||||||
|
for (auto& method : methods_) {
|
||||||
|
if (method.name() == name) {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Method not found: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetDescriptorJson() {
|
||||||
|
std::string json_str = "{";
|
||||||
|
for (auto& method : methods_) {
|
||||||
|
json_str += "\"" + method.name() + "\":" + method.GetDescriptorJson() + ",";
|
||||||
|
}
|
||||||
|
if (json_str.back() == ',') {
|
||||||
|
json_str.pop_back();
|
||||||
|
}
|
||||||
|
json_str += "}";
|
||||||
|
return json_str;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class Thing {
|
||||||
|
public:
|
||||||
|
Thing(const std::string& name, const std::string& description) :
|
||||||
|
name_(name), description_(description) {}
|
||||||
|
virtual ~Thing() = default;
|
||||||
|
|
||||||
|
virtual std::string GetDescriptorJson();
|
||||||
|
virtual std::string GetStateJson();
|
||||||
|
virtual void Invoke(const cJSON* command);
|
||||||
|
|
||||||
|
const std::string& name() const { return name_; }
|
||||||
|
const std::string& description() const { return description_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
PropertyList properties_;
|
||||||
|
MethodList methods_;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string name_;
|
||||||
|
std::string description_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
void RegisterThing(const std::string& type, std::function<Thing*()> creator);
|
||||||
|
Thing* CreateThing(const std::string& type);
|
||||||
|
|
||||||
|
#define DECLARE_THING(TypeName) \
|
||||||
|
static iot::Thing* Create##TypeName() { \
|
||||||
|
return new iot::TypeName(); \
|
||||||
|
} \
|
||||||
|
static bool Register##TypeNameHelper = []() { \
|
||||||
|
RegisterThing(#TypeName, Create##TypeName); \
|
||||||
|
return true; \
|
||||||
|
}();
|
||||||
|
|
||||||
|
} // namespace iot
|
||||||
|
|
||||||
|
#endif // THING_H
|
||||||
47
main/iot/thing_manager.cc
Normal file
47
main/iot/thing_manager.cc
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include "thing_manager.h"
|
||||||
|
|
||||||
|
#include <esp_log.h>
|
||||||
|
|
||||||
|
#define TAG "ThingManager"
|
||||||
|
|
||||||
|
namespace iot {
|
||||||
|
|
||||||
|
void ThingManager::AddThing(Thing* thing) {
|
||||||
|
things_.push_back(thing);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ThingManager::GetDescriptorsJson() {
|
||||||
|
std::string json_str = "[";
|
||||||
|
for (auto& thing : things_) {
|
||||||
|
json_str += thing->GetDescriptorJson() + ",";
|
||||||
|
}
|
||||||
|
if (json_str.back() == ',') {
|
||||||
|
json_str.pop_back();
|
||||||
|
}
|
||||||
|
json_str += "]";
|
||||||
|
return json_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ThingManager::GetStatesJson() {
|
||||||
|
std::string json_str = "[";
|
||||||
|
for (auto& thing : things_) {
|
||||||
|
json_str += thing->GetStateJson() + ",";
|
||||||
|
}
|
||||||
|
if (json_str.back() == ',') {
|
||||||
|
json_str.pop_back();
|
||||||
|
}
|
||||||
|
json_str += "]";
|
||||||
|
return json_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThingManager::Invoke(const cJSON* command) {
|
||||||
|
auto name = cJSON_GetObjectItem(command, "name");
|
||||||
|
for (auto& thing : things_) {
|
||||||
|
if (thing->name() == name->valuestring) {
|
||||||
|
thing->Invoke(command);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace iot
|
||||||
41
main/iot/thing_manager.h
Normal file
41
main/iot/thing_manager.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#ifndef THING_MANAGER_H
|
||||||
|
#define THING_MANAGER_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "thing.h"
|
||||||
|
|
||||||
|
#include <cJSON.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace iot {
|
||||||
|
|
||||||
|
class ThingManager {
|
||||||
|
public:
|
||||||
|
static ThingManager& GetInstance() {
|
||||||
|
static ThingManager instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
ThingManager(const ThingManager&) = delete;
|
||||||
|
ThingManager& operator=(const ThingManager&) = delete;
|
||||||
|
|
||||||
|
void AddThing(Thing* thing);
|
||||||
|
|
||||||
|
std::string GetDescriptorsJson();
|
||||||
|
std::string GetStatesJson();
|
||||||
|
void Invoke(const cJSON* command);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ThingManager() = default;
|
||||||
|
~ThingManager() = default;
|
||||||
|
|
||||||
|
std::vector<Thing*> things_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace iot
|
||||||
|
|
||||||
|
#endif // THING_MANAGER_H
|
||||||
54
main/iot/things/lamp.cc
Normal file
54
main/iot/things/lamp.cc
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include "iot/thing.h"
|
||||||
|
#include "board.h"
|
||||||
|
#include "audio_codec.h"
|
||||||
|
|
||||||
|
#include <driver/gpio.h>
|
||||||
|
#include <esp_log.h>
|
||||||
|
|
||||||
|
#define TAG "Lamp"
|
||||||
|
|
||||||
|
namespace iot {
|
||||||
|
|
||||||
|
// 这里仅定义 Lamp 的属性和方法,不包含具体的实现
|
||||||
|
class Lamp : public Thing {
|
||||||
|
private:
|
||||||
|
gpio_num_t gpio_num_ = GPIO_NUM_18;
|
||||||
|
bool power_ = false;
|
||||||
|
|
||||||
|
void InitializeGpio() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Lamp() : Thing("Lamp", "一个测试用的灯"), power_(false) {
|
||||||
|
InitializeGpio();
|
||||||
|
|
||||||
|
// 定义设备的属性
|
||||||
|
properties_.AddBooleanProperty("power", "灯是否打开", [this]() -> bool {
|
||||||
|
return power_;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 定义设备可以被远程执行的指令
|
||||||
|
methods_.AddMethod("TurnOn", "打开灯", ParameterList(), [this](const ParameterList& parameters) {
|
||||||
|
power_ = true;
|
||||||
|
gpio_set_level(gpio_num_, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
methods_.AddMethod("TurnOff", "关闭灯", ParameterList(), [this](const ParameterList& parameters) {
|
||||||
|
power_ = false;
|
||||||
|
gpio_set_level(gpio_num_, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace iot
|
||||||
|
|
||||||
|
DECLARE_THING(Lamp);
|
||||||
33
main/iot/things/speaker.cc
Normal file
33
main/iot/things/speaker.cc
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#include "iot/thing.h"
|
||||||
|
#include "board.h"
|
||||||
|
#include "audio_codec.h"
|
||||||
|
|
||||||
|
#include <esp_log.h>
|
||||||
|
|
||||||
|
#define TAG "Speaker"
|
||||||
|
|
||||||
|
namespace iot {
|
||||||
|
|
||||||
|
// 这里仅定义 Speaker 的属性和方法,不包含具体的实现
|
||||||
|
class Speaker : public Thing {
|
||||||
|
public:
|
||||||
|
Speaker() : Thing("Speaker", "当前 AI 机器人的扬声器") {
|
||||||
|
// 定义设备的属性
|
||||||
|
properties_.AddNumberProperty("volume", "当前音量值", [this]() -> int {
|
||||||
|
auto codec = Board::GetInstance().GetAudioCodec();
|
||||||
|
return codec->output_volume();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 定义设备可以被远程执行的指令
|
||||||
|
methods_.AddMethod("SetVolume", "设置音量", ParameterList({
|
||||||
|
Parameter("volume", "0到100之间的整数", kValueTypeNumber, true)
|
||||||
|
}), [this](const ParameterList& parameters) {
|
||||||
|
auto codec = Board::GetInstance().GetAudioCodec();
|
||||||
|
codec->SetOutputVolume(static_cast<uint8_t>(parameters["volume"].number()));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace iot
|
||||||
|
|
||||||
|
DECLARE_THING(Speaker);
|
||||||
@ -57,3 +57,14 @@ void Protocol::SendStopListening() {
|
|||||||
std::string message = "{\"session_id\":\"" + session_id_ + "\",\"type\":\"listen\",\"state\":\"stop\"}";
|
std::string message = "{\"session_id\":\"" + session_id_ + "\",\"type\":\"listen\",\"state\":\"stop\"}";
|
||||||
SendText(message);
|
SendText(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Protocol::SendIotDescriptors(const std::string& descriptors) {
|
||||||
|
std::string message = "{\"session_id\":\"" + session_id_ + "\",\"type\":\"iot\",\"descriptors\":" + descriptors + "}";
|
||||||
|
SendText(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Protocol::SendIotStates(const std::string& states) {
|
||||||
|
std::string message = "{\"session_id\":\"" + session_id_ + "\",\"type\":\"iot\",\"states\":" + states + "}";
|
||||||
|
SendText(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -45,6 +45,8 @@ public:
|
|||||||
virtual void SendStartListening(ListeningMode mode);
|
virtual void SendStartListening(ListeningMode mode);
|
||||||
virtual void SendStopListening();
|
virtual void SendStopListening();
|
||||||
virtual void SendAbortSpeaking(AbortReason reason);
|
virtual void SendAbortSpeaking(AbortReason reason);
|
||||||
|
virtual void SendIotDescriptors(const std::string& descriptors);
|
||||||
|
virtual void SendIotStates(const std::string& states);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
std::function<void(const cJSON* root)> on_incoming_json_;
|
std::function<void(const cJSON* root)> on_incoming_json_;
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||||
|
CONFIG_COMPILER_CXX_EXCEPTIONS_EMG_POOL_SIZE=1024
|
||||||
|
|
||||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF=y
|
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF=y
|
||||||
CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y
|
CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y
|
||||||
CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS=y
|
CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS=y
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user