add iot framework
This commit is contained in:
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);
|
||||
Reference in New Issue
Block a user