diff options
-rw-r--r-- | README.md | 111 | ||||
-rw-r--r-- | library.properties | 9 | ||||
-rw-r--r-- | src/diller_client.h | 209 | ||||
-rw-r--r-- | src/diller_core.h | 71 | ||||
-rw-r--r-- | src/diller_serial.h | 45 | ||||
-rw-r--r-- | src/diller_utils.h | 348 | ||||
-rw-r--r-- | src/impl/diller_core_impl.h | 150 | ||||
-rw-r--r-- | src/impl/diller_serial_impl.h | 183 |
8 files changed, 1126 insertions, 0 deletions
diff --git a/README.md b/README.md new file mode 100644 index 0000000..5b14176 --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +# Diller Arduino Library + +Diller is an IoT environment for makers that want an fast and easy way of connecting their projects to the internet. Everything is open source but everything is also hosted so you can either install everything yourself or just ust the instance running at https://trygvis.io/diller. + +A setup consists of a device with a Diller client, a gateway and a web frontend. + +## Features + +# Diller MQTT + +Diller uses MQTT to communicate between the client and the gateway. + +## Device / Property hierarchy + +All messages are published under the `/diller` path. + + /<device id> + /property + /<property id> + /value + /type (retained) + /name (retained) + /description (retained) + +The device id is a globally unique id of the device. The MAC address of devices is a useful identifier. The property id is an device-specific identifier chosen by the device. + +# Diller serial API + +## Network settings + +Request: + +Update or query network settings. If no paramters are given, no changes are done. If ip is set to a blank string, it will use DHCP. + + network [ip=..] [gateway=..] [netmask=..] + +Response: + +The command will always return the current values. + + ok ip=.. gateway=.. netmask=.. netmask=.. + +## Wlan settings + +Request: + +Update or query wlan settings. If no paramters are given, no changes are done. + + wlan [ssid=..] [password=..] + +Response: + +The command will always return the current values. + + ok ssid=.. + +## Properties introspection + +Request: + + properties + +Response: + + ok count=<n> + property key=.. [name=..] [description=..] + +## Change property + +TODO: Implement description? A longer string describing the property. + +Request: + + property id=.. [value=..] [name=..] + +Type examples: + +* `temperature` +* `switch` - boolean switches +* `humidity` +* `rtc` + +Response: + + ok + +The value might not be updated directly, but may be buffered on the device if it is not yet connected. + +## Reset the device + +Request: + + reset + +Response + + ok + +# Example session + +Get the current network configuration to update the Arduino's LCD display + + > network + < ok ip=1.3.3.7 netmask=255.255.255.0 gateway=1.3.3.1 ssid=awesome + +Register the properties. This is done on every boot to keep the server in sync with the firmware's features. Old properties will not be removed. + + > set-property id=temp-0 value=12.3 type=temperature name=Water + < ok + + diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..9b87256 --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=Diller +version=1.0.0 +author=Trygve Laugstøl <trygvis@inamo.no> +maintainer=Trygve Laugstøl <trygvis@inamo.no> +sentence=A IoT toolkit for makers +paragraph=Diller, awesome, yay! +category=Communication +url=https://github.com/trygvis/diller-arduino +architectures=* diff --git a/src/diller_client.h b/src/diller_client.h new file mode 100644 index 0000000..988db98 --- /dev/null +++ b/src/diller_client.h @@ -0,0 +1,209 @@ +#pragma once + +#include <SoftwareSerial.h> +#include <Arduino.h> +#include "diller_utils.h" + +namespace diller { +namespace client { + +using namespace diller::utils; + +enum class diller_cmd : uint8_t { + UNKNOWN, + STATUS, + NETWORK, + WLAN, + PROPERTY, + PROPERTIES +}; + +class diller_event_listener { + public: + virtual void on_diller_event(const key_value_map ¶ms) = 0; +}; + +class client { + public: + client() : listener(nullptr) { + } + + virtual ~client() { + } + + virtual void property(const char *property, const char *value, const char *name) = 0; + virtual void property_value(const char *property, const char *value) = 0; + virtual void property_name(const char *property, const char *name) = 0; + virtual void property_description(const char *property, const char *description) = 0; + + void set_diller_event_listener(diller_event_listener *l) { + listener = l; + } + + protected: + diller_event_listener *listener; +}; + +template<typename io_t, typename key_value_map_t = diller::utils::fixed_size_key_value_map<10>> +class client_software_serial : public client { + public: + client_software_serial() : client(), params(), parser(params) { + } + + void wlan(const char *ssid, const char *password) { + io_t::print("wlan ssid="); + escape(ssid); + io_t::print(" password="); + escape(password); + } + + void property(const char *property, const char *value, const char *name) { + io_t::print("property id="); + escape(property); + io_t::print(" value="); + escape(value); + io_t::print(" name="); + escape(name); + } + + void property_value(const char *property, const char *value) { + io_t::print("property id="); + escape(property); + io_t::print(" value="); + escape(value); + } + + void property_name(const char *property, const char *name) { + io_t::print("property id="); + escape(property); + io_t::print(" name="); + escape(name); + } + + void property_description(const char *property, const char *description) { + io_t::print("property id="); + escape(property); + io_t::print(" description="); + escape(description); + } + + void loop() { + auto x = tty.readline(); + // Serial.print("x="); + // Serial.println(static_cast<int>(x)); + if (x == tty_status::FULL_LINE) { + parser.parse(tty.line); + + process_command(); + } + } + + protected: + void escape(const char *s) { + char c; + while ((c = *s++) != '\0') { + if (c == ' ') { + io_t::print('\\'); + } + io_t::print(c); + } + } + + void process_command() { + if (params.is_empty()) { + return; + } + + if (!listener) { + return; + } + + listener->on_diller_event(params); + params.clear(); + } + + private: + // SoftwareSerial serial; + key_value_map_t params; + diller::utils::diller_parser parser; + diller::utils::tty<io_t, 100, 3000, false> tty; +}; + +void print_diller_event(const key_value_map ¶ms) { + if (!params.size()) { + return; + } + + Serial.print("Diller: "); + Serial.print(params.key(0)); + Serial.print(' '); + + for (uint8_t i = 1; i < params.size(); i++) { + auto key = params.key(i); + auto value = params.value(i); + + Serial.print(key); + if (value) { + Serial.print("="); + Serial.print(value); + } + Serial.print(" "); + } + Serial.println(); +} + +class printing_diller_event_listener : public diller_event_listener { + public: + void on_diller_event(const key_value_map ¶ms) { + print_diller_event(params); + } +}; + +class noop_diller_event_listener : public diller_event_listener { + public: + void on_diller_event(const key_value_map ¶ms) { + auto key = params.key(0); + if (strcmp("network", key) == 0) { + on_network(params); + } else if (strcmp("status", key) == 0) { + on_status(params); + } else if (strcmp("property", key) == 0) { + on_property(params); + } else if (strcmp("properties", key) == 0) { + on_properties(params); + } else if (strcmp("debug", key) == 0) { + on_debug(params); + } else { + on_unknown(params); + } + } + + protected: + virtual void on_network(const key_value_map ¶ms) { + static_cast<void>(params); + } + + virtual void on_status(const key_value_map ¶ms) { + static_cast<void>(params); + } + + virtual void on_property(const key_value_map ¶ms) { + static_cast<void>(params); + } + + virtual void on_properties(const key_value_map ¶ms) { + static_cast<void>(params); + } + + virtual void on_unknown(const key_value_map ¶ms) { + static_cast<void>(params); + } + + virtual void on_debug(const key_value_map ¶ms) { + static_cast<void>(params); + } +}; + +} // namespace client +} // namespace diller + diff --git a/src/diller_core.h b/src/diller_core.h new file mode 100644 index 0000000..a38779c --- /dev/null +++ b/src/diller_core.h @@ -0,0 +1,71 @@ +#pragma once + +#include "diller_utils.h" +#include <Arduino.h> +#include <ESP8266WiFi.h> + +namespace diller { +namespace core { + +using diller::utils::property; + +template<uint8_t max_property_count> +using props = diller::utils::properties<max_property_count>; + +enum class diller_error : uint8_t { + OK, INVAL, NOMEM +}; + +static +const char * to_string(diller_error err) { + switch (err) { + case diller_error::OK: + return "ok"; + case diller_error::INVAL: + return "inval"; + case diller_error::NOMEM: + return "nomem"; + default: + return "unknown"; + } +} + +enum class property_action : uint8_t { + VALUE, + NAME, + DESCRIPTION, +}; + +class property_action_listener { + public: + virtual void on_property_action(const property *, property_action) = 0; +}; + +template<uint8_t number_of_properties> +class core { + public: + core(const String &mqtt_host, int mqtt_port); + void set_property_action_listener(property_action_listener *); + + void setup(); + void loop(); + + bool connected() const; + + diller_error cmd_property(const char *id, const char *value, const char *name); + props<number_of_properties> properties; + + const String mqtt_host; + const int mqtt_port = 1883; + String mac; + String client_id; + + private: + void callback(char* topic_, byte* payload, unsigned int length); + property_action_listener *property_action_listener_; +}; + +} // namespace core +} // namespace diller + +#include "impl/diller_core_impl.h" diff --git a/src/diller_serial.h b/src/diller_serial.h new file mode 100644 index 0000000..a9a2de4 --- /dev/null +++ b/src/diller_serial.h @@ -0,0 +1,45 @@ +#pragma once + +#include "diller_utils.h" +#include <Arduino.h> +#include <ESP8266WiFi.h> + +namespace diller { +namespace serial { + +using diller::utils::property; +using diller::core::diller_error; +using diller::core::property_action; + +template<typename d_core, typename io_t> +class diller_serial : protected diller::core::property_action_listener { + public: + diller_serial(d_core &diller) : diller(diller), params(), diller_parser(params) { + } + + void setup(); + void loop(); + + private: + void process_command(); + void on_property_action(const property *, property_action); + + void cmd_network(); + void cmd_wlan(); + void cmd_wlan(const char* ssid, const char* password); + void cmd_property(const char *id, const char *value, const char *name); + void cmd_list_properties(); + void show_status(wl_status_t wl_status); + + d_core &diller; + diller::utils::fixed_size_key_value_map<10> params; + diller::utils::diller_parser diller_parser; + diller::utils::tty<io_t, 100, 1000> tty; + + static const bool send_wlan_password = false; +}; + +} // namespace serial +} // namespace diller + +#include "impl/diller_serial_impl.h" diff --git a/src/diller_utils.h b/src/diller_utils.h new file mode 100644 index 0000000..5b1224d --- /dev/null +++ b/src/diller_utils.h @@ -0,0 +1,348 @@ +#pragma once + +#include <Arduino.h> + +namespace diller { +namespace utils { + +enum class tty_status : uint8_t { + NEED_MORE, + TIMEOUT, + EMPTY_LINE, + FULL_LINE, + LINE_OVERFLOW, +}; + +class serial_io { + public: + static auto available() -> decltype(Serial.available()) { + return Serial.available(); + } + + static auto read() -> decltype(Serial.read()) { + return Serial.read(); + } + + template<typename A> + static auto print(A a) -> decltype(Serial.print(a)) { + return Serial.print(a); + } + + template<typename A> + static auto println(A a) -> decltype(Serial.println(a)) { + return Serial.println(a); + } +}; + +template<typename instance> +class software_serial_io { + public: + static auto available() -> decltype(instance::software_serial.available()) { + return instance::software_serial.available(); + } + + static auto read() -> decltype(instance::software_serial.read()) { + return instance::software_serial.read(); + } + + template<typename A> + static auto print(A a) -> decltype(instance::software_serial.print(a)) { + return instance::software_serial.print(a); + } + + template<typename A> + static auto println(A a) -> decltype(instance::software_serial.println(a)) { + return instance::software_serial.println(a); + } +}; + +template<typename io, uint8_t buffer_size, int timeout_ms_, bool debug_ = false> +class tty { + public: + char line[buffer_size]; + uint8_t size = 0; + const bool debug = debug_; + const unsigned long timeout_ms = timeout_ms_; + + void init(bool reset = false) { + if (reset) { + line[0] = '\0'; + } + + size = 0; + last_char = 0; + } + + void quote(const String &s) { + Serial.print(s.c_str()); + } + + void quote(const char *str) { + Serial.print(str); + } + + void escape(const char *str) { + Serial.print(str); + } + + tty_status readline() { + while (io::available()) { + int c = io::read(); + + if (debug) { + Serial.print("read: c="); + Serial.println(c, HEX); + } + + if (c == '\n') { + // ignore + last_char = millis(); + } else if (c == '\r') { + line[size] = '\0'; + auto status = size > 0 ? tty_status::FULL_LINE : tty_status::EMPTY_LINE; + init(); + + if (debug) { + Serial.print("debug src=tty state=\"new line\" size="); + Serial.print(size, DEC); + Serial.print(" line="); + quote(line); + Serial.println(); + } + + return status; + } else { + if (size == buffer_size - 1) { + + if (debug) { + Serial.print("debug src=tty state=overflow size="); + Serial.print(size, DEC); + Serial.print(" line="); + quote(line); + Serial.println(); + } + init(); + + return tty_status::LINE_OVERFLOW; + } + line[size++] = static_cast<char>(c); + last_char = millis(); + } + } + + unsigned long now = millis(); + + if (last_char > 0 && now >= (last_char + timeout_ms)) { + if (debug) { + line[size] = '\0'; + Serial.print("debug src=tty state=\"timeout\" line="); + quote(line); + Serial.println(); + } + + init(true); + return tty_status::TIMEOUT; + } + + return tty_status::NEED_MORE; + } + private: + unsigned long last_char = 0; +}; + +class key_value_map { + public: + virtual void clear(); + virtual void put(char *key, char *value) = 0; + virtual const char *key(uint8_t index) const = 0; + virtual const char *value(uint8_t index) const = 0; + virtual const char *find(const char *key) const = 0; + virtual uint8_t size() const = 0; + virtual bool is_empty() const = 0; + virtual bool is_full() const = 0; +}; + +template<int max_args> +class fixed_size_key_value_map : public key_value_map { + public: + uint8_t size_; + static_assert(max_args <= 256, "max_args is too big"); + char *keys[max_args]; + char *values[max_args]; + + void clear() { + size_ = 0; + } + + void put(char *key, char *value) { + if (size_ < max_args) { + keys[size_] = key; + values[size_] = value; + size_++; + } + } + + const char *key(uint8_t index) const { + return keys[index]; + } + + const char *value(uint8_t index) const { + return values[index]; + } + + const char *find(const char *key) const { + for (int i = 0; i < size_; i++) { + if (strcmp(keys[i], key) == 0) { + return values[i]; + } + } + + return nullptr; + } + + uint8_t size() const { + return size_; + } + + bool is_empty() const { + return size_ >= max_args; + } + + bool is_full() const { + return size_ >= max_args; + } +}; + +class diller_parser { + public: + diller_parser(key_value_map &map) : map(map) { + } + + void parse(char *line) { + char *c = line; + map.clear(); + + while (*c != '\0') { + while (*c == ' ') { + c++; + } + if (*c == '\0') { + break; + } + char *key = c, + *value = nullptr; + + while (*c != ' ' && *c != '\0') { + if (*c == '=') { + *c = '\0'; + c++; + value = c; + } + + c++; + } + + map.put(key, value); + + if (*c == '\0' || map.is_full()) { + break; + } + + *c++ = '\0'; + } + } + protected: + key_value_map ↦ +}; + +class property { + public: + property() : new_(true) { + } + + const String &id() const { + return id_; + } + + const String &value() const { + return value_; + } + + const String &name() const { + return name_; + } + + const String &description() const { + return description_; + } + + bool dirty() const { + return dirty_; + } + + void init(const String &id) { + id_ = id; + dirty_ = false; + } + + bool is_new() const { + return new_; + } + + void set_old() { + new_ = false; + } + + private: + String id_; + boolean dirty_; + boolean new_; + + String value_; + String name_; + String description_; +}; + +template<size_t max_property_count> +class properties { + public: + properties() : size_(0) { + } + + property* operator[](uint8_t i) { + if (i >= size_) { + return nullptr; + } + + return &properties_[i]; + } + + property* find(const char *id) { + for (int i = 0; i < size_; i++) { + if (properties_[i].id() == id) { + return &properties_[i]; + } + } + + if (size_ < max_property_count) { + auto p = &properties_[size_]; + size_++; + p->init(id); + return p; + } + + return nullptr; + } + + uint8_t size() { + return size_; + } + + private: + property properties_[max_property_count]; + uint8_t size_; +}; + +} // namespace utils +} // namespace diller + diff --git a/src/impl/diller_core_impl.h b/src/impl/diller_core_impl.h new file mode 100644 index 0000000..1246e59 --- /dev/null +++ b/src/impl/diller_core_impl.h @@ -0,0 +1,150 @@ +#include <Arduino.h> +#include <ESP8266WiFi.h> +#include <PubSubClient.h> + +extern WiFiClient wifi_client; +extern PubSubClient mqtt_client; + +namespace diller { +namespace core { + +using diller::utils::tty_status; +using diller::utils::property; + +// core::core + +template<uint8_t number_of_properties> +core<number_of_properties>::core(const String &mqtt_host, int mqtt_port) : + mqtt_host(mqtt_host), mqtt_port(mqtt_port), property_action_listener_(nullptr) { + mac = WiFi.macAddress(); + mac.toLowerCase(); + client_id = "diller-" + mac; +} + +template<uint8_t number_of_properties> +void core<number_of_properties>::callback(char* topic_, byte* payload, unsigned int length) { + Serial.print("got message on "); + Serial.println(topic_); + Serial.println("payload"); + Serial.write(payload, length); + Serial.println(); + + String prefix = "/diller/" + mac + "/property/"; + String topic(topic_); + if (!topic.startsWith(prefix)) { + Serial.print("debug bad-prefix: "); + Serial.println(topic); + return; + } + + topic.remove(0, prefix.length()); + property *p = nullptr; + for (auto i = 0; i < properties.size(); i++) { + auto *x = properties[i]; + if (topic.startsWith(x->id())) { + p = x; + break; + } + } + + if (p) { + topic.remove(0, p->id().length() + 1); + + Serial.print("debug property="); + Serial.print(p->id()); + Serial.print(", topic="); + Serial.println(topic); + + property_action a; + + if (topic == "value") { + Serial.println("debug action=value"); + a = property_action::VALUE; + } else if (topic == "name") { + Serial.println("debug action=name"); + a = property_action::NAME; + } else if (topic == "description") { + Serial.println("debug action=description"); + a = property_action::DESCRIPTION; + } else { + return; + } + + if (property_action_listener_) { + property_action_listener_->on_property_action(p, a); + } + } else { + Serial.print("debug unknown-property"); + } +} + +template<uint8_t number_of_properties> +bool core<number_of_properties>::connected() const { + return mqtt_client.connected(); +} + +template<uint8_t number_of_properties> +void core<number_of_properties>::setup() { + mqtt_client.setServer(mqtt_host.c_str(), mqtt_port); + mqtt_client.setCallback([this](char* topic_, byte* payload, unsigned int length) { + callback(topic_, payload, length); + }); +} + +template<uint8_t number_of_properties> +void core<number_of_properties>::loop() { + auto wl_status = WiFi.status(); + + if (wl_status == WL_CONNECTED) { + if (!mqtt_client.loop()) { + Serial.println("status mqtt=connecting"); + if (mqtt_client.connect(client_id.c_str())) { + Serial.println("status mqtt=connected"); + } else { + Serial.println("status mqtt=disconnected"); + } + } + } +} + +template<uint8_t number_of_properties> +diller_error core<number_of_properties>::cmd_property(const char *id, const char *value, const char *name) { + if (!id) { + return diller_error::INVAL; + } + + auto p = properties.find(id); + + if (!p) { + return diller_error::NOMEM; + } + + if (value) { + String topic = "/diller/" + mac + "/property/" + id + "/value"; + mqtt_client.publish(topic.c_str(), value); + } + + if (name) { + String topic = "/diller/" + mac + "/property/" + id + "/name"; + mqtt_client.publish(topic.c_str(), name); + } + + if (p->is_new()) { + String topic = "/diller/" + mac + "/property/" + id + "/#"; + mqtt_client.subscribe(topic.c_str()); + Serial.print("debug subscribing="); + Serial.println(topic); + + p->set_old(); + } + + return diller_error::OK; +} + +template<uint8_t number_of_properties> +void core<number_of_properties>::set_property_action_listener(property_action_listener *l) { + this->property_action_listener_ = l; +} + +} // namespace core +} // namespace diller diff --git a/src/impl/diller_serial_impl.h b/src/impl/diller_serial_impl.h new file mode 100644 index 0000000..47c18b5 --- /dev/null +++ b/src/impl/diller_serial_impl.h @@ -0,0 +1,183 @@ +// #include <Arduino.h> +#include <ESP8266WiFi.h> +// #include <PubSubClient.h> + +extern WiFiClient wifi_client; +extern PubSubClient mqtt_client; + +namespace diller { +namespace serial { + +using diller::utils::tty_status; + +template<typename d_core, typename io_t> +void diller_serial<d_core, io_t>::cmd_network() { + Serial.print("ok ip="); + WiFi.localIP().printTo(Serial); + Serial.print(" gateway="); + WiFi.gatewayIP().printTo(Serial); + Serial.print(" netmask="); + WiFi.subnetMask().printTo(Serial); + Serial.println(); +} + +template<typename d_core, typename io_t> +void diller_serial<d_core, io_t>::cmd_wlan() { + Serial.print("ok ssid="); + tty.quote(WiFi.SSID()); + if (send_wlan_password) { + Serial.print(" password="); + Serial.print(WiFi.psk()); + } + + Serial.print(" mac="); + Serial.println(diller.mac); +} + +template<typename d_core, typename io_t> +void diller_serial<d_core, io_t>::cmd_wlan(const char* ssid, const char* password) { + WiFi.begin(ssid, password); + Serial.println("ok"); +} + +template<typename d_core, typename io_t> +void diller_serial<d_core, io_t>::cmd_property(const char *id, const char *value, const char *name) { + auto ret = diller.cmd_property(id, value, name); + + if (ret != diller_error::OK) { + Serial.print("fail error="); + Serial.println(to_string(ret)); + } +} + +template<typename d_core, typename io_t> +void diller_serial<d_core, io_t>::cmd_list_properties() { + Serial.print("ok count="); + Serial.println(diller.properties.size()); + for (auto i = 0; i < diller.properties.size(); i++) { + auto p = diller.properties[i]; + Serial.print("property id="); + Serial.print(p->id()); + Serial.print(" name="); + Serial.print(p->name()); + Serial.print(" description="); + Serial.print(p->description()); + Serial.println(); + } +} + +template<typename d_core, typename io_t> +void diller_serial<d_core, io_t>::show_status(wl_status_t wl_status) { + const char *wl_status_s; + if (wl_status == WL_IDLE_STATUS) { + wl_status_s = "idle"; + } else if (wl_status == WL_NO_SSID_AVAIL) { + wl_status_s = "no-ssid"; + } else if (wl_status == WL_SCAN_COMPLETED) { + wl_status_s = "scan-completed"; + } else if (wl_status == WL_CONNECTED) { + wl_status_s = "connected"; + } else if (wl_status == WL_CONNECT_FAILED) { + wl_status_s = "connect-failed"; + } else if (wl_status == WL_CONNECTION_LOST) { + wl_status_s = "connection-lost"; + } else if (wl_status == WL_DISCONNECTED) { + wl_status_s = "disconnected"; + } else { + wl_status_s = "unknown"; + } + + const char *mqtt_status_s; + if (diller.connected()) { + mqtt_status_s = "connected"; + } else { + mqtt_status_s = "disconnected"; + } + + Serial.print("status wlan="); + Serial.print(wl_status_s); + Serial.print(" mqtt="); + Serial.println(mqtt_status_s); +} + +template<typename d_core, typename io_t> +void diller_serial<d_core, io_t>::process_command() { + if (params.is_empty()) { + return; + } + + auto cmd = params.key(0); + + if (strcmp("network", cmd) == 0) { + if (params.size() == 1) { + cmd_network(); + } else { + Serial.println("fail error=invalid_argument"); + } + } else if (strcmp("wlan", cmd) == 0) { + if (params.size() == 1) { + cmd_wlan(); + } else if (params.size() == 3) { + const char* ssid = params.find("ssid"); + const char* password = params.find("password"); + cmd_wlan(ssid, password); + } else { + Serial.println("fail error=invalid_argument"); + } + } else if (strcmp("property", cmd) == 0) { + auto id = params.find("id"); + auto value = params.find("value"); + auto name = params.find("name"); + cmd_property(id, value, name); + } else if (strcmp("list-properties", cmd) == 0) { + cmd_list_properties(); + } else { + Serial.print("fail error=unknown_command cmd="); + tty.escape(cmd); + Serial.println(); + } +} + +template<typename d_core, typename io_t> +void diller_serial<d_core, io_t>::setup() { + diller.set_property_action_listener(this); +} + +template<typename d_core, typename io_t> +void diller_serial<d_core, io_t>::loop() { + if (tty.readline() == tty_status::FULL_LINE) { + diller_parser.parse(tty.line); + + process_command(); + } + + static auto last_wl_status = WiFi.status(); + auto wl_status = WiFi.status(); + + static auto last_status = millis(); + auto now = millis(); + static const auto show_status_interval = 5000; + + if (last_wl_status != wl_status) { + show_status(wl_status); + last_wl_status = wl_status; + last_status = now; + } else if (now > (last_status + show_status_interval)) { + show_status(wl_status); + last_status = now; + } +} + +template<typename d_core, typename io_t> +void diller_serial<d_core, io_t>::on_property_action(const property *p, property_action) { + Serial.print("property id="); + tty.quote(p->id()); + Serial.print(" value="); + tty.quote(p->value()); + Serial.print(" description="); + tty.quote(p->description()); + Serial.println(); +} + +} // namespace serial +} // namespace diller |