From 5dd83fa6eeb15280ad2902e517690d7f76c52f80 Mon Sep 17 00:00:00 2001 From: Lorenzo Trivelli Date: Wed, 28 Jul 2021 09:31:35 +0200 Subject: [PATCH 01/35] Changed links in forms --- Thing.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Thing.h b/Thing.h index 25a6ea3..36a49b6 100644 --- a/Thing.h +++ b/Thing.h @@ -197,7 +197,7 @@ class ThingAction { // 2.11 Action object: A links array (An array of Link objects linking // to one or more representations of an Action resource, each with an // implied default rel=action.) - JsonArray inline_links = obj.createNestedArray("links"); + JsonArray inline_links = obj.createNestedArray("forms"); JsonObject inline_links_prop = inline_links.createNestedObject(); inline_links_prop["href"] = "/things/" + deviceId + "/actions/" + id; } @@ -297,7 +297,7 @@ class ThingItem { // 2.9 Property object: A links array (An array of Link objects linking // to one or more representations of a Property resource, each with an // implied default rel=property.) - JsonArray inline_links = obj.createNestedArray("links"); + JsonArray inline_links = obj.createNestedArray("forms"); JsonObject inline_links_prop = inline_links.createNestedObject(); inline_links_prop["href"] = "/things/" + deviceId + "/" + resourceType + "/" + id; @@ -724,7 +724,7 @@ class ThingDevice { type++; } - JsonArray links = descr.createNestedArray("links"); + JsonArray links = descr.createNestedArray("forms"); { JsonObject links_prop = links.createNestedObject(); links_prop["rel"] = "properties"; From 590f93ad30c4753bf13c926e73049c2ea7e8124a Mon Sep 17 00:00:00 2001 From: Lorenzo Trivelli Date: Thu, 5 Aug 2021 05:59:46 +0200 Subject: [PATCH 02/35] Removed all "global" forms from TD --- Thing.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Thing.h b/Thing.h index 36a49b6..d5bb3e8 100644 --- a/Thing.h +++ b/Thing.h @@ -461,7 +461,7 @@ class ThingEventObject { break; } - data["timestamp"] = timestamp; + //data["timestamp"] = timestamp; } }; @@ -695,10 +695,10 @@ class ThingDevice { #endif } - void serialize(JsonObject descr, String ip, uint16_t port) { + void serialize(JbsonObject descr, String ip, uint16_t port) { descr["id"] = this->id; descr["title"] = this->title; - descr["@context"] = "https://webthings.io/schemas"; + descr["@context"] = " https://www.w3.org/2019/wot/td/v1"; if (this->description != "") { descr["description"] = this->description; @@ -723,17 +723,15 @@ class ThingDevice { typeJson.add(*type); type++; } - + /* JsonArray links = descr.createNestedArray("forms"); { JsonObject links_prop = links.createNestedObject(); - links_prop["rel"] = "properties"; links_prop["href"] = "/things/" + this->id + "/properties"; } { JsonObject links_prop = links.createNestedObject(); - links_prop["rel"] = "actions"; links_prop["href"] = "/things/" + this->id + "/actions"; } @@ -742,11 +740,12 @@ class ThingDevice { links_prop["rel"] = "events"; links_prop["href"] = "/things/" + this->id + "/events"; } + */ #ifndef WITHOUT_WS { JsonObject links_prop = links.createNestedObject(); - links_prop["rel"] = "alternate"; + //links_prop["rel"] = "alternate"; if (port != 80) { char buffer[33]; From e2413bce7fe81dd288e3a7fe77a3c2eaf3bdceaa Mon Sep 17 00:00:00 2001 From: Lorenzo Trivelli Date: Thu, 5 Aug 2021 06:02:05 +0200 Subject: [PATCH 03/35] removed global forms from server --- WiFi101WebThingAdapter.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WiFi101WebThingAdapter.h b/WiFi101WebThingAdapter.h index dc0fc5a..e07b528 100644 --- a/WiFi101WebThingAdapter.h +++ b/WiFi101WebThingAdapter.h @@ -308,7 +308,7 @@ class WebThingAdapter { handleError(); } return; - } else if (uri == deviceBase + "/properties") { + } /*else if (uri == deviceBase + "/properties") { if (method == HTTP_GET || method == HTTP_OPTIONS) { handleThingPropertiesGet(device->firstProperty); } else { @@ -330,7 +330,7 @@ class WebThingAdapter { } else { handleError(); } - return; + return;*/ } else { ThingProperty *property = device->firstProperty; while (property != nullptr) { From a2feda247cf7cdd581fdf53566128d441a6cf1ea Mon Sep 17 00:00:00 2001 From: Citrullin Date: Wed, 11 Aug 2021 13:20:28 +0200 Subject: [PATCH 04/35] Fix misspelling of JsonObject --- Thing.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Thing.h b/Thing.h index d5bb3e8..a9cd53e 100644 --- a/Thing.h +++ b/Thing.h @@ -695,7 +695,7 @@ class ThingDevice { #endif } - void serialize(JbsonObject descr, String ip, uint16_t port) { + void serialize(JsonObject descr, String ip, uint16_t port) { descr["id"] = this->id; descr["title"] = this->title; descr["@context"] = " https://www.w3.org/2019/wot/td/v1"; From 1adc91be3ef32e40b839c5aa099cc9373e813a04 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Wed, 11 Aug 2021 13:27:10 +0200 Subject: [PATCH 05/35] Remove websocket --- ESPWebThingAdapter.h | 144 ---------------------------------------- Thing.h | 153 ------------------------------------------- 2 files changed, 297 deletions(-) diff --git a/ESPWebThingAdapter.h b/ESPWebThingAdapter.h index 6d96fcf..cbe76b4 100644 --- a/ESPWebThingAdapter.h +++ b/ESPWebThingAdapter.h @@ -158,15 +158,6 @@ class WebThingAdapter { void update() { #ifdef ESP8266 MDNS.update(); -#endif -#ifndef WITHOUT_WS - // * Send changed properties as defined in "4.5 propertyStatus message" - // Do this by looping over all devices and properties - ThingDevice *device = this->firstDevice; - while (device != nullptr) { - sendChangedProperties(device); - device = device->next; - } #endif } @@ -178,19 +169,6 @@ class WebThingAdapter { this->lastDevice->next = device; this->lastDevice = device; } - -#ifndef WITHOUT_WS - // Initiate the websocket instance - AsyncWebSocket *ws = new AsyncWebSocket("/things/" + device->id); - device->ws = ws; - // AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventType - // type, void * arg, uint8_t *data, size_t len, ThingDevice* device - ws->onEvent(std::bind( - &WebThingAdapter::handleWS, this, std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, - std::placeholders::_5, std::placeholders::_6, device)); - this->server.addHandler(ws); -#endif } private: @@ -228,118 +206,6 @@ class WebThingAdapter { return false; } -#ifndef WITHOUT_WS - void sendErrorMsg(DynamicJsonDocument &prop, AsyncWebSocketClient &client, - int status, const char *msg) { - prop["error"] = msg; - prop["status"] = status; - String jsonStr; - serializeJson(prop, jsonStr); - client.text(jsonStr.c_str(), jsonStr.length()); - } - - void handleWS(AsyncWebSocket *server, AsyncWebSocketClient *client, - AwsEventType type, void *arg, const uint8_t *rawData, - size_t len, ThingDevice *device) { - if (type == WS_EVT_DISCONNECT || type == WS_EVT_ERROR) { - device->removeEventSubscriptions(client->id()); - return; - } - - // Ignore all others except data packets - if (type != WS_EVT_DATA) - return; - - // Only consider non fragmented data - AwsFrameInfo *info = (AwsFrameInfo *)arg; - if (!info->final || info->index != 0 || info->len != len) - return; - - // Web Thing only specifies text, not binary websocket transfers - if (info->opcode != WS_TEXT) - return; - - // In theory we could just have one websocket for all Things and react on - // the server->url() to route data. Controllers will however establish a - // separate websocket connection for each Thing anyway as of in the spec. - // For now each Thing stores its own Websocket connection object therefore. - - // Parse request - DynamicJsonDocument newProp(SMALL_JSON_DOCUMENT_SIZE); - auto error = deserializeJson(newProp, rawData, len); - if (error) { - sendErrorMsg(newProp, *client, 400, "Invalid json"); - return; - } - - String messageType = newProp["messageType"].as(); - JsonVariant dataVariant = newProp["data"]; - if (!dataVariant.is()) { - sendErrorMsg(newProp, *client, 400, "data must be an object"); - return; - } - - JsonObject data = dataVariant.as(); - - if (messageType == "setProperty") { - for (JsonPair kv : data) { - device->setProperty(kv.key().c_str(), kv.value()); - } - } else if (messageType == "requestAction") { - for (JsonPair kv : data) { - DynamicJsonDocument *actionRequest = - new DynamicJsonDocument(SMALL_JSON_DOCUMENT_SIZE); - - JsonObject actionObj = actionRequest->to(); - JsonObject nested = actionObj.createNestedObject(kv.key()); - - for (JsonPair kvInner : kv.value().as()) { - nested[kvInner.key()] = kvInner.value(); - } - - ThingActionObject *obj = device->requestAction(actionRequest); - if (obj != nullptr) { - obj->setNotifyFunction(std::bind(&ThingDevice::sendActionStatus, - device, std::placeholders::_1)); - device->sendActionStatus(obj); - - obj->start(); - } - } - } else if (messageType == "addEventSubscription") { - for (JsonPair kv : data) { - ThingEvent *event = device->findEvent(kv.key().c_str()); - if (event) { - device->addEventSubscription(client->id(), event->id); - } - } - } - } - - void sendChangedProperties(ThingDevice *device) { - // Prepare one buffer per device - DynamicJsonDocument message(LARGE_JSON_DOCUMENT_SIZE); - message["messageType"] = "propertyStatus"; - JsonObject prop = message.createNestedObject("data"); - bool dataToSend = false; - ThingItem *item = device->firstProperty; - while (item != nullptr) { - ThingDataValue *value = item->changedValueOrNull(); - if (value) { - dataToSend = true; - item->serializeValue(prop); - } - item = item->next; - } - if (dataToSend) { - String jsonStr; - serializeJson(message, jsonStr); - // Inform all connected ws clients of a Thing about changed properties - ((AsyncWebSocket *)device->ws)->textAll(jsonStr); - } - } -#endif - void handleUnknown(AsyncWebServerRequest *request) { if (!verifyHost(request)) { return; @@ -513,11 +379,6 @@ class WebThingAdapter { return; } -#ifndef WITHOUT_WS - obj->setNotifyFunction(std::bind(&ThingDevice::sendActionStatus, device, - std::placeholders::_1)); -#endif - DynamicJsonDocument respBuffer(SMALL_JSON_DOCUMENT_SIZE); JsonObject item = respBuffer.to(); obj->serialize(item, device->id); @@ -624,11 +485,6 @@ class WebThingAdapter { return; } -#ifndef WITHOUT_WS - obj->setNotifyFunction(std::bind(&ThingDevice::sendActionStatus, device, - std::placeholders::_1)); -#endif - DynamicJsonDocument respBuffer(SMALL_JSON_DOCUMENT_SIZE); JsonObject item = respBuffer.to(); obj->serialize(item, device->id); diff --git a/Thing.h b/Thing.h index a9cd53e..9aa6731 100644 --- a/Thing.h +++ b/Thing.h @@ -54,10 +54,6 @@ class ThingActionObject { void (*start_fn)(const JsonVariant &); void (*cancel_fn)(); -#ifndef WITHOUT_WS - std::function notify_fn; -#endif - public: String name; DynamicJsonDocument *actionRequest = nullptr; @@ -76,12 +72,6 @@ class ThingActionObject { generateId(); } -#ifndef WITHOUT_WS - void setNotifyFunction(std::function notify_fn_) { - notify_fn = notify_fn_; - } -#endif - void generateId() { for (uint8_t i = 0; i < 16; ++i) { char c = (char)random('0', 'g'); @@ -115,11 +105,6 @@ class ThingActionObject { void setStatus(const char *s) { status = s; -#ifndef WITHOUT_WS - if (notify_fn != nullptr) { - notify_fn(this); - } -#endif } void start() { @@ -361,67 +346,7 @@ class ThingProperty : public ThingItem { } } }; - -#ifndef WITHOUT_WS -class EventSubscription { -public: - uint32_t id; - EventSubscription *next; - - EventSubscription(uint32_t id_) : id(id_) {} -}; - -class ThingEvent : public ThingItem { -private: - EventSubscription *subscriptions = nullptr; - -public: - ThingEvent(const char *id_, const char *description_, ThingDataType type_, - const char *atType_) - : ThingItem(id_, description_, type_, atType_) {} - - void addSubscription(uint32_t id) { - EventSubscription *sub = new EventSubscription(id); - sub->next = subscriptions; - subscriptions = sub; - } - - void removeSubscription(uint32_t id) { - EventSubscription *curr = subscriptions; - EventSubscription *prev = nullptr; - while (curr != nullptr) { - if (curr->id == id) { - if (prev == nullptr) { - subscriptions = curr->next; - } else { - prev->next = curr->next; - } - - delete curr; - return; - } - - prev = curr; - curr = curr->next; - } - } - - bool isSubscribed(uint32_t id) { - EventSubscription *curr = subscriptions; - while (curr != nullptr) { - if (curr->id == id) { - return true; - } - - curr = curr->next; - } - - return false; - } -}; -#else using ThingEvent = ThingItem; -#endif class ThingEventObject { public: @@ -471,9 +396,6 @@ class ThingDevice { String title; String description; const char **type; -#if !defined(WITHOUT_WS) && (defined(ESP8266) || defined(ESP32)) - AsyncWebSocket *ws = nullptr; -#endif ThingDevice *next = nullptr; ThingProperty *firstProperty = nullptr; ThingAction *firstAction = nullptr; @@ -485,42 +407,8 @@ class ThingDevice { : id(_id), title(_title), type(_type) {} ~ThingDevice() { -#if !defined(WITHOUT_WS) && (defined(ESP8266) || defined(ESP32)) - if (ws) - delete ws; -#endif - } - -#ifndef WITHOUT_WS - void removeEventSubscriptions(uint32_t id) { - ThingEvent *event = firstEvent; - while (event != nullptr) { - event->removeSubscription(id); - event = (ThingEvent *)event->next; - } - } - - void addEventSubscription(uint32_t id, String eventName) { - ThingEvent *event = findEvent(eventName.c_str()); - if (!event) { - return; - } - - event->addSubscription(id); } - void sendActionStatus(ThingActionObject *action) { - DynamicJsonDocument message(LARGE_JSON_DOCUMENT_SIZE); - message["messageType"] = "actionStatus"; - JsonObject prop = message.createNestedObject("data"); - action->serialize(prop, id); - String jsonStr; - serializeJson(message, jsonStr); - // Inform all connected ws clients about action statuses - ((AsyncWebSocket *)ws)->textAll(jsonStr); - } -#endif - ThingProperty *findProperty(const char *id) { ThingProperty *p = this->firstProperty; while (p) { @@ -668,31 +556,6 @@ class ThingDevice { void queueEventObject(ThingEventObject *obj) { obj->next = eventQueue; eventQueue = obj; - -#ifndef WITHOUT_WS - ThingEvent *event = findEvent(obj->name.c_str()); - if (!event) { - return; - } - - // * Send events as defined in "4.7 event message" - DynamicJsonDocument message(SMALL_JSON_DOCUMENT_SIZE); - message["messageType"] = "event"; - JsonObject data = message.createNestedObject("data"); - obj->serialize(data); - String jsonStr; - serializeJson(message, jsonStr); - - // Inform all subscribed ws clients about events - for (AsyncWebSocketClient *client : - ((AsyncWebSocket *)this->ws)->getClients()) { - uint32_t id = client->id(); - - if (event->isSubscribed(id)) { - ((AsyncWebSocket *)this->ws)->text(id, jsonStr); - } - } -#endif } void serialize(JsonObject descr, String ip, uint16_t port) { @@ -742,22 +605,6 @@ class ThingDevice { } */ -#ifndef WITHOUT_WS - { - JsonObject links_prop = links.createNestedObject(); - //links_prop["rel"] = "alternate"; - - if (port != 80) { - char buffer[33]; - itoa(port, buffer, 10); - links_prop["href"] = - "ws://" + ip + ":" + buffer + "/things/" + this->id; - } else { - links_prop["href"] = "ws://" + ip + "/things/" + this->id; - } - } -#endif - ThingProperty *property = this->firstProperty; if (property != nullptr) { JsonObject properties = descr.createNestedObject("properties"); From 30ebfd7971703af663ee3bf51900a453ddcf5915 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Thu, 12 Aug 2021 15:10:07 +0200 Subject: [PATCH 06/35] Move TD to /.well-known/wot-thing-description --- ESPWebThingAdapter.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ESPWebThingAdapter.h b/ESPWebThingAdapter.h index cbe76b4..166f6dc 100644 --- a/ESPWebThingAdapter.h +++ b/ESPWebThingAdapter.h @@ -55,7 +55,7 @@ class WebThingAdapter { } MDNS.addService("webthing", "tcp", port); - MDNS.addServiceTxt("webthing", "tcp", "path", "/"); + MDNS.addServiceTxt("webthing", "tcp", "path", "/.well-known/wot-thing-description"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*"); DefaultHeaders::Instance().addHeader("Access-Control-Allow-Methods", @@ -70,7 +70,7 @@ class WebThingAdapter { this->server.on("/*", HTTP_OPTIONS, std::bind(&WebThingAdapter::handleOptions, this, std::placeholders::_1)); - this->server.on("/", HTTP_GET, + this->server.on("/.well-known/wot-thing-description", HTTP_GET, std::bind(&WebThingAdapter::handleThings, this, std::placeholders::_1)); From 53d9023eb4072609adb6b9743b1b2066fda9c0f1 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Thu, 12 Aug 2021 15:11:14 +0200 Subject: [PATCH 07/35] Remove things array around TD --- ESPWebThingAdapter.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ESPWebThingAdapter.h b/ESPWebThingAdapter.h index 166f6dc..9cf9320 100644 --- a/ESPWebThingAdapter.h +++ b/ESPWebThingAdapter.h @@ -228,16 +228,15 @@ class WebThingAdapter { request->beginResponseStream("application/json"); DynamicJsonDocument buf(LARGE_JSON_DOCUMENT_SIZE); - JsonArray things = buf.to(); + JsonObject thing = buf.to(); ThingDevice *device = this->firstDevice; while (device != nullptr) { - JsonObject descr = things.createNestedObject(); - device->serialize(descr, ip, port); - descr["href"] = "/things/" + device->id; + device->serialize(thing, ip, port); + thing["href"] = "/things/" + device->id; device = device->next; } - serializeJson(things, *response); + serializeJson(thing, *response); request->send(response); } From 924fe56c599a31767ea184cd23778322821a2df6 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Tue, 17 Aug 2021 09:15:18 +0200 Subject: [PATCH 08/35] Remove commented out code --- Thing.h | 2 -- WiFi101WebThingAdapter.h | 26 ++------------------------ 2 files changed, 2 insertions(+), 26 deletions(-) diff --git a/Thing.h b/Thing.h index 9aa6731..6955618 100644 --- a/Thing.h +++ b/Thing.h @@ -385,8 +385,6 @@ class ThingEventObject { data["data"] = *this->getValue().string; break; } - - //data["timestamp"] = timestamp; } }; diff --git a/WiFi101WebThingAdapter.h b/WiFi101WebThingAdapter.h index e07b528..4a2cee3 100644 --- a/WiFi101WebThingAdapter.h +++ b/WiFi101WebThingAdapter.h @@ -308,30 +308,8 @@ class WebThingAdapter { handleError(); } return; - } /*else if (uri == deviceBase + "/properties") { - if (method == HTTP_GET || method == HTTP_OPTIONS) { - handleThingPropertiesGet(device->firstProperty); - } else { - handleError(); - } - return; - } else if (uri == deviceBase + "/actions") { - if (method == HTTP_GET || method == HTTP_OPTIONS) { - handleThingActionsGet(device); - } else if (method == HTTP_POST) { - handleThingActionsPost(device); - } else { - handleError(); - } - return; - } else if (uri == deviceBase + "/events") { - if (method == HTTP_GET || method == HTTP_OPTIONS) { - handleThingEventsGet(device); - } else { - handleError(); - } - return;*/ - } else { + } + } else { ThingProperty *property = device->firstProperty; while (property != nullptr) { String propertyBase = deviceBase + "/properties/" + property->id; From 1a674fa730659354d60c44ea8a02e4dc7b3d0888 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Tue, 17 Aug 2021 09:25:05 +0200 Subject: [PATCH 09/35] Add w3c td v1 and webthings context --- Thing.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Thing.h b/Thing.h index 6955618..8febfa6 100644 --- a/Thing.h +++ b/Thing.h @@ -559,7 +559,9 @@ class ThingDevice { void serialize(JsonObject descr, String ip, uint16_t port) { descr["id"] = this->id; descr["title"] = this->title; - descr["@context"] = " https://www.w3.org/2019/wot/td/v1"; + JsonArray context = descr.createNestedArray("@context"); + context.add("https://www.w3.org./2019/wot/td/v1"); + context.add("https://webthings.io/schemas"); if (this->description != "") { descr["description"] = this->description; From 123a053c05bd9c6087eab59148de803673e895e1 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Tue, 17 Aug 2021 09:42:14 +0200 Subject: [PATCH 10/35] Add things->forms for properties --- Thing.h | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/Thing.h b/Thing.h index 8febfa6..1e66e77 100644 --- a/Thing.h +++ b/Thing.h @@ -586,24 +586,17 @@ class ThingDevice { typeJson.add(*type); type++; } - /* - JsonArray links = descr.createNestedArray("forms"); + + JsonArray forms = descr.createNestedArray("forms"); { - JsonObject links_prop = links.createNestedObject(); - links_prop["href"] = "/things/" + this->id + "/properties"; + JsonObject forms_prop = forms.createNestedObject(); + forms_prop["rel"] = "properties"; + JsonArray context = forms_prop.createNestedArray("op"); + context.add("readallproperties"); + context.add("writeallproperties"); + forms_prop["href"] = "/things/" + this->id + "/properties"; } - { - JsonObject links_prop = links.createNestedObject(); - links_prop["href"] = "/things/" + this->id + "/actions"; - } - - { - JsonObject links_prop = links.createNestedObject(); - links_prop["rel"] = "events"; - links_prop["href"] = "/things/" + this->id + "/events"; - } - */ ThingProperty *property = this->firstProperty; if (property != nullptr) { From d5511fc0e8a18f3edf54cbe316cff0d45e12573b Mon Sep 17 00:00:00 2001 From: Citrullin Date: Tue, 17 Aug 2021 23:12:16 +0200 Subject: [PATCH 11/35] Remove Wifi101 --- WebThingAdapter.h | 1 - WiFi101WebThingAdapter.h | 663 --------------------------------------- 2 files changed, 664 deletions(-) delete mode 100644 WiFi101WebThingAdapter.h diff --git a/WebThingAdapter.h b/WebThingAdapter.h index 2d72fa8..cb76ac8 100644 --- a/WebThingAdapter.h +++ b/WebThingAdapter.h @@ -11,4 +11,3 @@ #pragma once #include "ESPWebThingAdapter.h" -#include "WiFi101WebThingAdapter.h" diff --git a/WiFi101WebThingAdapter.h b/WiFi101WebThingAdapter.h deleted file mode 100644 index 4a2cee3..0000000 --- a/WiFi101WebThingAdapter.h +++ /dev/null @@ -1,663 +0,0 @@ -/** - * WiFi101WebThingAdapter.h - * - * Exposes the Web Thing API based on provided ThingDevices. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -#pragma once - -#if !defined(ESP32) && !defined(ESP8266) - -#include - -#if defined(ARDUINO_SAMD_MKRWIFI1010) || defined(ARDUINO_SAMD_NANO_33_IOT) -#include -#else -#include -#endif - -#include -#include - -#include - -#define WITHOUT_WS 1 -#include "Thing.h" - -#ifndef LARGE_JSON_DOCUMENT_SIZE -#ifdef LARGE_JSON_BUFFERS -#define LARGE_JSON_DOCUMENT_SIZE 4096 -#else -#define LARGE_JSON_DOCUMENT_SIZE 1024 -#endif -#endif - -#ifndef SMALL_JSON_DOCUMENT_SIZE -#ifdef LARGE_JSON_BUFFERS -#define SMALL_JSON_DOCUMENT_SIZE 1024 -#else -#define SMALL_JSON_DOCUMENT_SIZE 256 -#endif -#endif - -static const bool DEBUG = false; - -enum HTTPMethod { - HTTP_ANY, - HTTP_GET, - HTTP_PUT, - HTTP_POST, - HTTP_DELETE, - HTTP_OPTIONS -}; - -enum ReadState { - STATE_READ_METHOD, - STATE_READ_URI, - STATE_DISCARD_HTTP11, - STATE_DISCARD_HEADERS_PRE_HOST, - STATE_READ_HOST, - STATE_DISCARD_HEADERS_POST_HOST, - STATE_READ_CONTENT -}; - -class WebThingAdapter { -public: - WebThingAdapter(String _name, uint32_t _ip, uint16_t _port = 80, - bool _disableHostValidation = false) - : name(_name), port(_port), server(_port), - disableHostValidation(_disableHostValidation), mdns(udp) { - ip = ""; - for (int i = 0; i < 4; i++) { - ip += _ip & 0xff; - if (i < 3) { - ip += '.'; - } - _ip >>= 8; - } - } - - void begin() { - name.toLowerCase(); - - String serviceName = name + "._webthing"; - mdns.begin(WiFi.localIP(), name.c_str()); - // \x06 is the length of the record - mdns.addServiceRecord(serviceName.c_str(), port, MDNSServiceTCP, - "\x06path=/"); - - server.begin(); - } - - void update() { - mdns.run(); - - if (!client) { - WiFiClient client = server.available(); - if (!client) { - return; - } - if (DEBUG) { - Serial.println("New client available"); - } - this->client = client; - } - - if (!client.connected()) { - if (DEBUG) { - Serial.println("Client disconnected"); - } - resetParser(); - client.stop(); - return; - } - - char c = client.read(); - if (c == 255 || c == -1) { - if (state == STATE_READ_CONTENT) { - handleRequest(); - resetParser(); - } - - retries += 1; - if (retries > 5000) { - if (DEBUG) { - Serial.println("Giving up on client"); - } - resetParser(); - client.stop(); - } - return; - } - - switch (state) { - case STATE_READ_METHOD: - if (c == ' ') { - if (methodRaw == "GET") { - method = HTTP_GET; - } else if (methodRaw == "POST") { - method = HTTP_POST; - } else if (methodRaw == "PUT") { - method = HTTP_PUT; - } else if (methodRaw == "DELETE") { - method = HTTP_DELETE; - } else if (methodRaw == "OPTIONS") { - method = HTTP_OPTIONS; - } else { - method = HTTP_ANY; - } - state = STATE_READ_URI; - } else { - methodRaw += c; - } - break; - - case STATE_READ_URI: - if (c == ' ') { - state = STATE_DISCARD_HTTP11; - } else { - uri += c; - } - break; - - case STATE_DISCARD_HTTP11: - if (c == '\r') { - state = STATE_DISCARD_HEADERS_PRE_HOST; - } - break; - - case STATE_DISCARD_HEADERS_PRE_HOST: - if (c == '\r') { - break; - } - if (c == '\n') { - headerRaw = ""; - break; - } - if (c == ':') { - if (headerRaw.equalsIgnoreCase("Host")) { - state = STATE_READ_HOST; - } - break; - } - - headerRaw += c; - break; - - case STATE_READ_HOST: - if (c == '\r') { - returnsAndNewlines = 1; - state = STATE_DISCARD_HEADERS_POST_HOST; - break; - } - if (c == ' ') { - break; - } - host += c; - break; - - case STATE_DISCARD_HEADERS_POST_HOST: - if (c == '\r' || c == '\n') { - returnsAndNewlines += 1; - } else { - returnsAndNewlines = 0; - } - if (returnsAndNewlines == 4) { - state = STATE_READ_CONTENT; - } - break; - - case STATE_READ_CONTENT: - content += c; - break; - } - } - - void addDevice(ThingDevice *device) { - if (this->lastDevice == nullptr) { - this->firstDevice = device; - this->lastDevice = device; - } else { - this->lastDevice->next = device; - this->lastDevice = device; - } - } - -private: - String name, ip; - uint16_t port; - bool disableHostValidation; - WiFiServer server; - WiFiClient client; - WiFiUDP udp; - MDNS mdns; - - ReadState state = STATE_READ_METHOD; - String uri = ""; - HTTPMethod method = HTTP_ANY; - String content = ""; - String methodRaw = ""; - String host = ""; - String headerRaw = ""; - int returnsAndNewlines = 0; - int retries = 0; - - ThingDevice *firstDevice = nullptr, *lastDevice = nullptr; - - bool verifyHost() { - if (disableHostValidation) { - return true; - } - - int colonIndex = host.indexOf(':'); - if (colonIndex >= 0) { - host.remove(colonIndex); - } - if (host.equalsIgnoreCase(name + ".local")) { - return true; - } - if (host == ip) { - return true; - } - if (host == "localhost") { - return true; - } - return false; - } - - void handleRequest() { - if (DEBUG) { - Serial.print("handleRequest: "); - Serial.print("method: "); - Serial.println(method); - Serial.print("uri: "); - Serial.println(uri); - Serial.print("host: "); - Serial.println(host); - Serial.print("content: "); - Serial.println(content); - } - - if (!verifyHost()) { - client.println("HTTP/1.1 403 Forbidden"); - client.println("Connection: close"); - client.println(); - delay(1); - client.stop(); - return; - } - - if (uri == "/") { - handleThings(); - return; - } - - ThingDevice *device = this->firstDevice; - while (device != nullptr) { - String deviceBase = "/things/" + device->id; - - if (uri.startsWith(deviceBase)) { - if (uri == deviceBase) { - if (method == HTTP_GET || method == HTTP_OPTIONS) { - handleThing(device); - } else { - handleError(); - } - return; - } - } else { - ThingProperty *property = device->firstProperty; - while (property != nullptr) { - String propertyBase = deviceBase + "/properties/" + property->id; - if (uri == propertyBase) { - if (method == HTTP_GET || method == HTTP_OPTIONS) { - handleThingPropertyGet(property); - } else if (method == HTTP_PUT) { - handleThingPropertyPut(device, property); - } else { - handleError(); - } - return; - } - property = (ThingProperty *)property->next; - } - - ThingAction *action = device->firstAction; - while (action != nullptr) { - String actionBase = deviceBase + "/actions/" + action->id; - if (uri == actionBase) { - if (method == HTTP_GET || method == HTTP_OPTIONS) { - handleThingActionGet(device, action); - } else if (method == HTTP_POST) { - handleThingActionPost(device, action); - } else { - handleError(); - } - return; - } else if (uri.startsWith(actionBase + "/") && - uri.length() > (actionBase.length() + 1)) { - if (method == HTTP_GET || method == HTTP_OPTIONS) { - handleThingActionIdGet(device, action); - } else if (method == HTTP_DELETE) { - handleThingActionIdDelete(device, action); - } else { - handleError(); - } - return; - } - action = action->next; - } - - ThingEvent *event = device->firstEvent; - while (event != nullptr) { - String eventBase = deviceBase + "/events/" + event->id; - if (uri == eventBase) { - if (method == HTTP_GET || method == HTTP_OPTIONS) { - handleThingEventGet(device, event); - } else { - handleError(); - } - return; - } - event = (ThingEvent *)event->next; - } - } - } - device = device->next; - } - handleError(); - } - - void sendOk() { client.println("HTTP/1.1 200 OK"); } - - void sendCreated() { client.println("HTTP/1.1 201 Created"); } - - void sendNoContent() { client.println("HTTP/1.1 204 No Content"); } - - void sendHeaders() { - client.println("Access-Control-Allow-Origin: *"); - client.println( - "Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS"); - client.println("Access-Control-Allow-Headers: " - "Origin, X-Requested-With, Content-Type, Accept"); - client.println("Content-Type: application/json"); - client.println("Connection: close"); - client.println(); - } - - void handleThings() { - sendOk(); - sendHeaders(); - - DynamicJsonDocument buf(LARGE_JSON_DOCUMENT_SIZE); - JsonArray things = buf.to(); - ThingDevice *device = this->firstDevice; - while (device != nullptr) { - JsonObject descr = things.createNestedObject(); - device->serialize(descr, ip, port); - descr["href"] = "/things/" + device->id; - device = device->next; - } - - serializeJson(things, client); - delay(1); - client.stop(); - } - - void handleThing(ThingDevice *device) { - sendOk(); - sendHeaders(); - - DynamicJsonDocument buf(LARGE_JSON_DOCUMENT_SIZE); - JsonObject descr = buf.to(); - device->serialize(descr, ip, port); - - serializeJson(descr, client); - delay(1); - client.stop(); - } - - void handleThingPropertyGet(ThingItem *item) { - sendOk(); - sendHeaders(); - - DynamicJsonDocument doc(SMALL_JSON_DOCUMENT_SIZE); - JsonObject prop = doc.to(); - item->serializeValue(prop); - serializeJson(prop, client); - delay(1); - client.stop(); - } - - void handleThingActionGet(ThingDevice *device, ThingAction *action) { - sendOk(); - sendHeaders(); - - DynamicJsonDocument doc(LARGE_JSON_DOCUMENT_SIZE); - JsonArray queue = doc.to(); - device->serializeActionQueue(queue, action->id); - serializeJson(queue, client); - delay(1); - client.stop(); - } - - void handleThingActionIdGet(ThingDevice *device, ThingAction *action) { - String base = "/things/" + device->id + "/actions/" + action->id; - String actionId = uri.substring(base.length() + 1); - const char *actionIdC = actionId.c_str(); - const char *slash = strchr(actionIdC, '/'); - - if (slash) { - actionId = actionId.substring(0, slash - actionIdC); - } - - ThingActionObject *obj = device->findActionObject(actionId.c_str()); - if (obj == nullptr) { - handleError(); - return; - } - - sendOk(); - sendHeaders(); - - DynamicJsonDocument doc(SMALL_JSON_DOCUMENT_SIZE); - JsonObject o = doc.to(); - obj->serialize(o, device->id); - serializeJson(o, client); - delay(1); - client.stop(); - } - - void handleThingActionIdDelete(ThingDevice *device, ThingAction *action) { - String base = "/things/" + device->id + "/actions/" + action->id; - String actionId = uri.substring(base.length() + 1); - const char *actionIdC = actionId.c_str(); - const char *slash = strchr(actionIdC, '/'); - - if (slash) { - actionId = actionId.substring(0, slash - actionIdC); - } - - device->removeAction(actionId); - sendNoContent(); - sendHeaders(); - } - - void handleThingActionPost(ThingDevice *device, ThingAction *action) { - DynamicJsonDocument *newBuffer = - new DynamicJsonDocument(SMALL_JSON_DOCUMENT_SIZE); - auto error = deserializeJson(*newBuffer, content); - if (error) { // unable to parse json - handleError(); - delete newBuffer; - return; - } - - JsonObject newAction = newBuffer->as(); - - if (!newAction.containsKey(action->id)) { - handleError(); - delete newBuffer; - return; - } - - ThingActionObject *obj = device->requestAction(newBuffer); - - if (obj == nullptr) { - handleError(); - delete newBuffer; - return; - } - - sendCreated(); - sendHeaders(); - - DynamicJsonDocument respBuffer(SMALL_JSON_DOCUMENT_SIZE); - JsonObject item = respBuffer.to(); - obj->serialize(item, device->id); - serializeJson(item, client); - delay(1); - client.stop(); - - obj->start(); - } - - void handleThingEventGet(ThingDevice *device, ThingItem *item) { - sendOk(); - sendHeaders(); - - DynamicJsonDocument doc(SMALL_JSON_DOCUMENT_SIZE); - JsonArray queue = doc.to(); - device->serializeEventQueue(queue, item->id); - serializeJson(queue, client); - delay(1); - client.stop(); - } - - void handleThingPropertiesGet(ThingItem *rootItem) { - sendOk(); - sendHeaders(); - - DynamicJsonDocument doc(LARGE_JSON_DOCUMENT_SIZE); - JsonObject prop = doc.to(); - ThingItem *item = rootItem; - while (item != nullptr) { - item->serializeValue(prop); - item = item->next; - } - serializeJson(prop, client); - delay(1); - client.stop(); - } - - void handleThingActionsGet(ThingDevice *device) { - sendOk(); - sendHeaders(); - - DynamicJsonDocument doc(LARGE_JSON_DOCUMENT_SIZE); - JsonArray queue = doc.to(); - device->serializeActionQueue(queue); - serializeJson(queue, client); - delay(1); - client.stop(); - } - - void handleThingActionsPost(ThingDevice *device) { - DynamicJsonDocument *newBuffer = - new DynamicJsonDocument(SMALL_JSON_DOCUMENT_SIZE); - auto error = deserializeJson(*newBuffer, content); - if (error) { // unable to parse json - handleError(); - delete newBuffer; - return; - } - - JsonObject newAction = newBuffer->as(); - - if (newAction.size() != 1) { - handleError(); - delete newBuffer; - return; - } - - ThingActionObject *obj = device->requestAction(newBuffer); - - if (obj == nullptr) { - handleError(); - delete newBuffer; - return; - } - - sendCreated(); - sendHeaders(); - - DynamicJsonDocument respBuffer(SMALL_JSON_DOCUMENT_SIZE); - JsonObject item = respBuffer.to(); - obj->serialize(item, device->id); - serializeJson(item, client); - delay(1); - client.stop(); - - obj->start(); - } - - void handleThingEventsGet(ThingDevice *device) { - sendOk(); - sendHeaders(); - - DynamicJsonDocument doc(LARGE_JSON_DOCUMENT_SIZE); - JsonArray queue = doc.to(); - device->serializeEventQueue(queue); - serializeJson(queue, client); - delay(1); - client.stop(); - } - - void handleThingPropertyPut(ThingDevice *device, ThingProperty *property) { - DynamicJsonDocument newBuffer(SMALL_JSON_DOCUMENT_SIZE); - auto error = deserializeJson(newBuffer, content); - if (error) { // unable to parse json - handleError(); - return; - } - JsonObject newProp = newBuffer.as(); - - if (!newProp.containsKey(property->id)) { - handleError(); - return; - } - - device->setProperty(property->id.c_str(), newProp[property->id]); - - sendOk(); - sendHeaders(); - - serializeJson(newProp, client); - delay(1); - client.stop(); - } - - void handleError() { - client.println("HTTP/1.1 400 Bad Request"); - sendHeaders(); - delay(1); - client.stop(); - } - - void resetParser() { - state = STATE_READ_METHOD; - method = HTTP_ANY; - methodRaw = ""; - headerRaw = ""; - host = ""; - uri = ""; - content = ""; - retries = 0; - } -}; - -#endif // neither ESP32 nor ESP8266 defined From 4f7a5a5bd7cb3adc7223ce4a57772510cd27c2c3 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Tue, 17 Aug 2021 23:29:51 +0200 Subject: [PATCH 12/35] Update BPM280 example --- examples/BME280/BME280.ino | 121 +++++++++++++++---------------------- 1 file changed, 50 insertions(+), 71 deletions(-) diff --git a/examples/BME280/BME280.ino b/examples/BME280/BME280.ino index 60543fa..754ed34 100644 --- a/examples/BME280/BME280.ino +++ b/examples/BME280/BME280.ino @@ -1,32 +1,3 @@ -/* - WiFi Web Server LED control via web of things (e.g., WebThings Gateway) - based on WiFi101.h example "Provisioning_WiFiWebServer.ino" - - A simple web server that lets you control an LED via the web. - This sketch will print the IP address of your WiFi device (once connected) - to the Serial monitor. From there, you can open that address in a web browser - to turn on and off the onboard LED. - - You can also connect via the Things Gateway http-on-off-wifi-adapter. - - If the IP address of your shield is yourAddress: - http://yourAddress/H turns the LED on - http://yourAddress/L turns it off - - This example is written for a network using WPA encryption. For - WEP or WPA, change the Wifi.begin() call accordingly. - - Circuit: - * WiFi using Microchip (Atmel) WINC1500 - * LED attached to pin 1 (onboard LED) for SAMW25 - * LED attached to pin 6 for MKR1000 - - created 25 Nov 2012 - by Tom Igoe - - updates: dh, kg 2018 - */ - #define LARGE_JSON_BUFFERS 1 #include @@ -34,7 +5,7 @@ #include #include #include -#include +#include #include #include @@ -45,6 +16,18 @@ #define PIN_STATE_LOW LOW #endif +#define ESP32 + +// TODO: Hardcode your wifi credentials here (and keep it private) +const char *ssid = ""; +const char *password = ""; + +#if defined(LED_BUILTIN) +const int ledPin = LED_BUILTIN; +#else +const int ledPin = 13; // manually configure LED pin +#endif + WebThingAdapter *adapter; const char *bme280Types[] = {"TemperatureSensor", nullptr}; @@ -54,12 +37,16 @@ ThingProperty weatherTemp("temperature", "", NUMBER, "TemperatureProperty"); ThingProperty weatherHum("humidity", "", NUMBER, "LevelProperty"); ThingProperty weatherPres("pressure", "", NUMBER, nullptr); -BME280I2C::Settings - settings(BME280::OSR_X1, BME280::OSR_X1, BME280::OSR_X1, - BME280::Mode_Forced, BME280::StandbyTime_1000ms, - BME280::Filter_Off, BME280::SpiEnable_False, - (BME280I2C::I2CAddr)0x76 // I2C address. I2C specific. - ); +BME280I2C::Settings settings( + BME280::OSR_X1, + BME280::OSR_X1, + BME280::OSR_X1, + BME280::Mode_Forced, + BME280::StandbyTime_1000ms, + BME280::Filter_Off, + BME280::SpiEnable_False, + BME280I2C::I2CAddr_0x76 // I2C address. I2C specific. +); BME280I2C bme(settings); @@ -79,49 +66,41 @@ void readBME280Data() { } void setup() { - // Initialize serial: - // Serial.begin(9600); - - // check for the presence of the shield: - // Serial.print("Configuring WiFi shield/module...\n"); - if (WiFi.status() == WL_NO_SHIELD) { - // Serial.println("WiFi shield not present"); - // don't continue: - while (true) - ; + Serial.println("Initialize..."); + + pinMode(ledPin, OUTPUT); + digitalWrite(ledPin, HIGH); + Serial.begin(115200); + Serial.println(""); + Serial.print("Connecting to \""); + Serial.print(ssid); + Serial.println("\""); + WiFi.begin(ssid, password); + + bool blink = true; + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + digitalWrite(ledPin, blink ? LOW : HIGH); // active low led + blink = !blink; } + digitalWrite(ledPin, HIGH); // active low led - // configure the LED pin for output mode - pinMode(LED_BUILTIN, OUTPUT); + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); Wire.begin(); while (!bme.begin()) { - // Serial.println("Could not find BME280I2C sensor!"); + Serial.println("Could not find BME280I2C sensor!"); delay(1000); } - - // Serial.println("Starting in provisioning mode..."); - // Start in provisioning mode: - // 1) This will try to connect to a previously associated access point. - // 2) If this fails, an access point named "wifi101-XXXX" will be created, - // where XXXX - // is the last 4 digits of the boards MAC address. Once you are connected - // to the access point, you can configure an SSID and password by - // visiting http://wifi101/ - WiFi.beginProvision(); - - while (WiFi.status() != WL_CONNECTED) { - // wait while not connected - - // blink the led to show an unconnected status - digitalWrite(LED_BUILTIN, PIN_STATE_HIGH); - delay(500); - digitalWrite(LED_BUILTIN, PIN_STATE_LOW); - delay(500); - } + Serial.println("BME280I2C connected and initialized."); // connected, make the LED stay on - digitalWrite(LED_BUILTIN, PIN_STATE_HIGH); + //digitalWrite(LED_BUILTIN, PIN_STATE_HIGH); adapter = new WebThingAdapter("weathersensor", WiFi.localIP()); // Set unit for temperature @@ -154,4 +133,4 @@ void setup() { void loop() { readBME280Data(); adapter->update(); -} +} \ No newline at end of file From 4350c1bc4915a79c14a9ba198327d3189c39ea7b Mon Sep 17 00:00:00 2001 From: Citrullin Date: Wed, 18 Aug 2021 16:11:12 +0200 Subject: [PATCH 13/35] Fix Action POST --- ESPWebThingAdapter.h | 36 +++++------------------------------- Thing.h | 6 ++---- 2 files changed, 7 insertions(+), 35 deletions(-) diff --git a/ESPWebThingAdapter.h b/ESPWebThingAdapter.h index 9cf9320..2103b66 100644 --- a/ESPWebThingAdapter.h +++ b/ESPWebThingAdapter.h @@ -339,6 +339,7 @@ class WebThingAdapter { void handleThingActionPost(AsyncWebServerRequest *request, ThingDevice *device, ThingAction *action) { if (!verifyHost(request)) { + Serial.println("Invalid Host"); return; } @@ -351,6 +352,7 @@ class WebThingAdapter { new DynamicJsonDocument(SMALL_JSON_DOCUMENT_SIZE); auto error = deserializeJson(*newBuffer, (const char *)body_data); if (error) { // unable to parse json + Serial.println("Unable to parse JSON"); b_has_body_data = false; memset(body_data, 0, sizeof(body_data)); request->send(500); @@ -360,22 +362,10 @@ class WebThingAdapter { JsonObject newAction = newBuffer->as(); - if (!newAction.containsKey(action->id)) { - b_has_body_data = false; - memset(body_data, 0, sizeof(body_data)); - request->send(400); - delete newBuffer; - return; - } - - ThingActionObject *obj = device->requestAction(newBuffer); - + ThingActionObject *obj = action->create(newBuffer); if (obj == nullptr) { - b_has_body_data = false; - memset(body_data, 0, sizeof(body_data)); - request->send(500); - delete newBuffer; - return; + request->send(404); + return; } DynamicJsonDocument respBuffer(SMALL_JSON_DOCUMENT_SIZE); @@ -466,24 +456,8 @@ class WebThingAdapter { JsonObject newAction = newBuffer->as(); - if (newAction.size() != 1) { - b_has_body_data = false; - memset(body_data, 0, sizeof(body_data)); - request->send(400); - delete newBuffer; - return; - } - ThingActionObject *obj = device->requestAction(newBuffer); - if (obj == nullptr) { - b_has_body_data = false; - memset(body_data, 0, sizeof(body_data)); - request->send(500); - delete newBuffer; - return; - } - DynamicJsonDocument respBuffer(SMALL_JSON_DOCUMENT_SIZE); JsonObject item = respBuffer.to(); obj->serialize(item, device->id); diff --git a/Thing.h b/Thing.h index 1e66e77..54ce5af 100644 --- a/Thing.h +++ b/Thing.h @@ -89,8 +89,7 @@ class ThingActionObject { JsonObject data = obj.createNestedObject(name); JsonObject actionObj = actionRequest->as(); - JsonObject inner = actionObj[name]; - data["input"] = inner["input"]; + data["input"] = actionObj; data["status"] = status; data["timeRequested"] = timeRequested; @@ -111,8 +110,7 @@ class ThingActionObject { setStatus("pending"); JsonObject actionObj = actionRequest->as(); - JsonObject inner = actionObj[name]; - start_fn(inner["input"]); + start_fn(actionObj); finish(); } From 98320fee07e4f680111740ab4afafeb3201b437c Mon Sep 17 00:00:00 2001 From: Citrullin Date: Wed, 18 Aug 2021 18:58:38 +0200 Subject: [PATCH 14/35] Add 500 Error when not able to create ThinActionObject --- ESPWebThingAdapter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ESPWebThingAdapter.h b/ESPWebThingAdapter.h index 2103b66..f9ab9c3 100644 --- a/ESPWebThingAdapter.h +++ b/ESPWebThingAdapter.h @@ -364,7 +364,7 @@ class WebThingAdapter { ThingActionObject *obj = action->create(newBuffer); if (obj == nullptr) { - request->send(404); + request->send(500); return; } From 7d15182657e05790ee92a2da461654c2e02e26d1 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Wed, 18 Aug 2021 19:02:12 +0200 Subject: [PATCH 15/35] LED on when connected in bmp280 example --- examples/BME280/BME280.ino | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/BME280/BME280.ino b/examples/BME280/BME280.ino index 754ed34..f019d5d 100644 --- a/examples/BME280/BME280.ino +++ b/examples/BME280/BME280.ino @@ -100,7 +100,7 @@ void setup() { Serial.println("BME280I2C connected and initialized."); // connected, make the LED stay on - //digitalWrite(LED_BUILTIN, PIN_STATE_HIGH); + digitalWrite(LED_BUILTIN, PIN_STATE_HIGH); adapter = new WebThingAdapter("weathersensor", WiFi.localIP()); // Set unit for temperature From 442bc97c27a3d3dd90027dbae69bfd61cb0be283 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Wed, 18 Aug 2021 19:40:18 +0200 Subject: [PATCH 16/35] Add #ifdef ESP32 for WebThings, instead of ESPWebThing --- ESPWebThingAdapter.h | 3 +-- WebThingAdapter.h | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ESPWebThingAdapter.h b/ESPWebThingAdapter.h index f9ab9c3..79abf37 100644 --- a/ESPWebThingAdapter.h +++ b/ESPWebThingAdapter.h @@ -11,7 +11,7 @@ #pragma once -#if defined(ESP32) || defined(ESP8266) + #include #include @@ -538,4 +538,3 @@ class WebThingAdapter { } }; -#endif // ESP diff --git a/WebThingAdapter.h b/WebThingAdapter.h index cb76ac8..40a9ae7 100644 --- a/WebThingAdapter.h +++ b/WebThingAdapter.h @@ -10,4 +10,6 @@ #pragma once +#if defined(ESP32) || defined(ESP8266) #include "ESPWebThingAdapter.h" +#endif From 23ccab0a89599758dc28695ae727347085f0ed24 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Wed, 18 Aug 2021 19:58:14 +0200 Subject: [PATCH 17/35] Remove all generic /actions/* endpoints --- ESPWebThingAdapter.h | 68 +------------------------------------------- 1 file changed, 1 insertion(+), 67 deletions(-) diff --git a/ESPWebThingAdapter.h b/ESPWebThingAdapter.h index 79abf37..16ed4ec 100644 --- a/ESPWebThingAdapter.h +++ b/ESPWebThingAdapter.h @@ -131,17 +131,7 @@ class WebThingAdapter { std::bind(&WebThingAdapter::handleThingPropertiesGet, this, std::placeholders::_1, device->firstProperty)); - this->server.on((deviceBase + "/actions").c_str(), HTTP_GET, - std::bind(&WebThingAdapter::handleThingActionsGet, this, - std::placeholders::_1, device)); - this->server.on((deviceBase + "/actions").c_str(), HTTP_POST, - std::bind(&WebThingAdapter::handleThingActionsPost, this, - std::placeholders::_1, device), - NULL, - std::bind(&WebThingAdapter::handleBody, this, - std::placeholders::_1, std::placeholders::_2, - std::placeholders::_3, std::placeholders::_4, - std::placeholders::_5)); + this->server.on((deviceBase + "/events").c_str(), HTTP_GET, std::bind(&WebThingAdapter::handleThingEventsGet, this, std::placeholders::_1, device)); @@ -417,62 +407,6 @@ class WebThingAdapter { request->send(response); } - void handleThingActionsGet(AsyncWebServerRequest *request, - ThingDevice *device) { - if (!verifyHost(request)) { - return; - } - AsyncResponseStream *response = - request->beginResponseStream("application/json"); - - DynamicJsonDocument doc(LARGE_JSON_DOCUMENT_SIZE); - JsonArray queue = doc.to(); - device->serializeActionQueue(queue); - serializeJson(queue, *response); - request->send(response); - } - - void handleThingActionsPost(AsyncWebServerRequest *request, - ThingDevice *device) { - if (!verifyHost(request)) { - return; - } - - if (!b_has_body_data) { - request->send(422); // unprocessable entity (b/c no body) - return; - } - - DynamicJsonDocument *newBuffer = - new DynamicJsonDocument(SMALL_JSON_DOCUMENT_SIZE); - auto error = deserializeJson(*newBuffer, (const char *)body_data); - if (error) { // unable to parse json - b_has_body_data = false; - memset(body_data, 0, sizeof(body_data)); - request->send(500); - delete newBuffer; - return; - } - - JsonObject newAction = newBuffer->as(); - - ThingActionObject *obj = device->requestAction(newBuffer); - - DynamicJsonDocument respBuffer(SMALL_JSON_DOCUMENT_SIZE); - JsonObject item = respBuffer.to(); - obj->serialize(item, device->id); - String jsonStr; - serializeJson(item, jsonStr); - AsyncWebServerResponse *response = - request->beginResponse(201, "application/json", jsonStr); - request->send(response); - - b_has_body_data = false; - memset(body_data, 0, sizeof(body_data)); - - obj->start(); - } - void handleThingEventsGet(AsyncWebServerRequest *request, ThingDevice *device) { if (!verifyHost(request)) { From 8c2704b60df0833e23f387b187f9bae44600e0c9 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Thu, 19 Aug 2021 02:45:29 +0200 Subject: [PATCH 18/35] clean memory on 500 error and remove unnecessary code --- ESPWebThingAdapter.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ESPWebThingAdapter.h b/ESPWebThingAdapter.h index 16ed4ec..6f910a5 100644 --- a/ESPWebThingAdapter.h +++ b/ESPWebThingAdapter.h @@ -350,10 +350,9 @@ class WebThingAdapter { return; } - JsonObject newAction = newBuffer->as(); - ThingActionObject *obj = action->create(newBuffer); if (obj == nullptr) { + memset(body_data, 0, sizeof(body_data)); request->send(500); return; } From ad538bd55490973b758b4d4652e726e39d89aa9a Mon Sep 17 00:00:00 2001 From: Citrullin Date: Thu, 19 Aug 2021 02:47:36 +0200 Subject: [PATCH 19/35] Add simple internal LED on/off example --- examples/LED/LED.ino | 108 ++++++++++++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 31 deletions(-) diff --git a/examples/LED/LED.ino b/examples/LED/LED.ino index 1a8bf80..2c596dc 100644 --- a/examples/LED/LED.ino +++ b/examples/LED/LED.ino @@ -1,39 +1,45 @@ /** - * Simple server compliant with Mozilla's proposed WoT API - * Originally based on the HelloServer example - * Tested on ESP8266, ESP32, Arduino boards with WINC1500 modules (shields or - * MKR1000) - * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#define LARGE_JSON_BUFFERS 1 + #include -#include "Thing.h" -#include "WebThingAdapter.h" +#include +#include // TODO: Hardcode your wifi credentials here (and keep it private) -const char *ssid = "public"; +const char *ssid = ""; const char *password = ""; #if defined(LED_BUILTIN) -const int ledPin = LED_BUILTIN; +const int lampPin = LED_BUILTIN; #else -const int ledPin = 13; // manually configure LED pin +const int lampPin = 13; // manually configure LED pin #endif +ThingActionObject *action_generator(DynamicJsonDocument *); + WebThingAdapter *adapter; -const char *ledTypes[] = {"OnOffSwitch", "Light", nullptr}; -ThingDevice led("led", "Built-in LED", ledTypes); -ThingProperty ledOn("on", "", BOOLEAN, "OnOffProperty"); +const char *lampTypes[] = {"OnOffSwitch", "Light", nullptr}; +ThingDevice lamp("lamp123", "My Lamp", lampTypes); + +ThingProperty lampOn("state", "Whether the lamp is turned on", BOOLEAN, + "OnOffProperty"); -bool lastOn = false; +StaticJsonDocument<256> toggleInput; +JsonObject toggleInputObj = toggleInput.to(); +ThingAction toggle("toggle", "Toggle", "toggle the lamp on/off", + "ToggleAction", &toggleInputObj, action_generator); + +bool lastOn = true; void setup(void) { - pinMode(ledPin, OUTPUT); - digitalWrite(ledPin, HIGH); + pinMode(lampPin, OUTPUT); + digitalWrite(lampPin, HIGH); Serial.begin(115200); Serial.println(""); Serial.print("Connecting to \""); @@ -46,40 +52,80 @@ void setup(void) { Serial.println(""); // Wait for connection - bool blink = true; while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); - digitalWrite(ledPin, blink ? LOW : HIGH); // active low led - blink = !blink; } - digitalWrite(ledPin, HIGH); // active low led Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); - adapter = new WebThingAdapter("w25", WiFi.localIP()); + adapter = new WebThingAdapter("led-lamp", WiFi.localIP()); + + lamp.description = "A web connected lamp"; - led.addProperty(&ledOn); - adapter->addDevice(&led); + lampOn.title = "On/Off"; + lamp.addProperty(&lampOn); + + toggleInputObj["type"] = "object"; + JsonObject stateInputProperties = + toggleInputObj.createNestedObject("properties"); + JsonObject stateInput = + stateInputProperties.createNestedObject("state"); + stateInput["type"] = "boolean"; + lamp.addAction(&toggle); + + adapter->addDevice(&lamp); adapter->begin(); + Serial.println("HTTP server started"); Serial.print("http://"); Serial.print(WiFi.localIP()); Serial.print("/things/"); - Serial.println(led.id); + Serial.println(lamp.id); + + // set initial values + ThingPropertyValue initialOn = {.boolean = true}; + lampOn.setValue(initialOn); + (void)lampOn.changedValueOrNull(); + } void loop(void) { adapter->update(); - bool on = ledOn.getValue().boolean; - digitalWrite(ledPin, on ? LOW : HIGH); // active low led - if (on != lastOn) { - Serial.print(led.id); - Serial.print(": "); - Serial.println(on); + bool on = lampOn.getValue().boolean; + if (on) { + digitalWrite(lampPin, HIGH); + } else { + digitalWrite(lampPin, LOW); } - lastOn = on; + + if (lastOn != on) { + lastOn = on; + } +} + +void do_toggle(const JsonVariant &input) { + Serial.println("toggle call"); + + JsonObject inputObj = input.as(); + bool state = inputObj["state"]; + + Serial.print("state: "); + Serial.println(state); + + if (state) { + digitalWrite(lampPin, HIGH); + } else { + digitalWrite(lampPin, LOW); + } + + ThingDataValue value = {.boolean = state}; + lampOn.setValue(value); +} + +ThingActionObject *action_generator(DynamicJsonDocument *input) { + return new ThingActionObject("toggle", input, do_toggle, nullptr); } From cee5954c789c879336fdf2936b1d994ff8f5427e Mon Sep 17 00:00:00 2001 From: Citrullin Date: Fri, 20 Aug 2021 03:04:13 +0200 Subject: [PATCH 20/35] Add OLED Display example --- examples/TextDisplay/TextDisplay.ino | 248 +++++++++++++++++---------- 1 file changed, 158 insertions(+), 90 deletions(-) diff --git a/examples/TextDisplay/TextDisplay.ino b/examples/TextDisplay/TextDisplay.ino index 0a33045..47a27c0 100644 --- a/examples/TextDisplay/TextDisplay.ino +++ b/examples/TextDisplay/TextDisplay.ino @@ -1,126 +1,194 @@ -/********************************************************************* -Web Thing which draws text provided as a property. -Adapted from the Adafruit SSD1306 example: - -This is an example for our Monochrome OLEDs based on SSD1306 drivers - - Pick one up today in the adafruit shop! - ------> http://www.adafruit.com/category/63_98 - -This example is for a 128x64 size display using SPI to communicate -4 or 5 pins are required to inteface - -Adafruit invests time and resources providing this open source code, -please support Adafruit and open-source hardware by purchasing -products from Adafruit! - -Written by Limor Fried/Ladyada for Adafruit Industries. -BSD license, check license.txt for more information -All text above, and the splash screen must be included in any redistribution -*********************************************************************/ - #include #include #include - -const char *ssid = "..."; -const char *password = "..."; - #include #include #include #include + +#define LARGE_JSON_BUFFERS 1 +#define SCREEN_WIDTH 128 // OLED display width, in pixels +#define SCREEN_HEIGHT 64 // OLED display height, in pixels +#define OLED_RESET -1 // Reset pin +Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); + +// TODO: Hardcode your wifi credentials here (and keep it private) +const char *ssid = ""; +const char *password = ""; + +#if defined(LED_BUILTIN) +const int ledPin = LED_BUILTIN; +#else +const int ledPin = 13; // manually configure LED pin +#endif -// If using software SPI (the default case): -#define OLED_MOSI 2 -#define OLED_CLK 16 -#define OLED_DC 0 -#define OLED_CS 13 -#define OLED_RESET 15 -Adafruit_SSD1306 display(OLED_MOSI, OLED_CLK, (int8_t)OLED_DC, OLED_RESET, - OLED_CS); - -/* Uncomment this block to use hardware SPI -#define OLED_DC 6 -#define OLED_CS 7 -#define OLED_RESET 8 -Adafruit_SSD1306 display(OLED_DC, OLED_RESET, (int8_t)OLED_CS); -*/ - -const int textHeight = 8; -const int textWidth = 6; -const int width = 128; -const int height = 64; - +ThingActionObject *action_generator(DynamicJsonDocument *); WebThingAdapter *adapter; -const char *textDisplayTypes[] = {"TextDisplay", nullptr}; -ThingDevice textDisplay("textDisplay", "Text display", textDisplayTypes); -ThingProperty text("text", "", STRING, nullptr); +const char *oledTypes[] = {"OLED Display", nullptr}; +ThingDevice oledThing("oled", "OLED Display", oledTypes); -String lastText = "moz://a iot"; +StaticJsonDocument<256> oledInput; +JsonObject oledInputObj = oledInput.to(); +ThingAction displayAction("display", "Display text", "display a text on OLED", + "displayAction", &oledInputObj, action_generator); -void displayString(const String &str) { - int len = str.length(); - int strWidth = len * textWidth; - int strHeight = textHeight; - int scale = width / strWidth; - if (strHeight > strWidth / 2) { - scale = height / strHeight; - } - int x = width / 2 - strWidth * scale / 2; - int y = height / 2 - strHeight * scale / 2; - - display.clearDisplay(); - display.setTextColor(WHITE); - display.setTextSize(scale); - display.setCursor(x, y); - display.println(str); - display.display(); -} - -void setup() { +void setup(void) { Serial.begin(115200); - - // by default, we'll generate the high voltage from the 3.3v line internally! - // (neat!) - display.begin(SSD1306_SWITCHCAPVCC); - - // display the splashscreen as requested :) + Serial.println("Initialize..."); + display.begin(SSD1306_SWITCHCAPVCC, 0x3C); + display.clearDisplay(); display.display(); - delay(2000); + Serial.println("Clear display..."); + pinMode(ledPin, OUTPUT); + digitalWrite(ledPin, HIGH); + Serial.println(""); + Serial.print("Connecting to \""); + Serial.print(ssid); + Serial.println("\""); #if defined(ESP8266) || defined(ESP32) WiFi.mode(WIFI_STA); #endif WiFi.begin(ssid, password); - Serial.println(""); - - // Wait for connection + + bool blink = true; while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); + digitalWrite(ledPin, blink ? LOW : HIGH); + blink = !blink; } + digitalWrite(ledPin, HIGH); Serial.println(""); Serial.print("Connected to "); Serial.println(ssid); Serial.print("IP address: "); Serial.println(WiFi.localIP()); - adapter = new WebThingAdapter("textdisplayer", WiFi.localIP()); - displayString(lastText); + adapter = new WebThingAdapter("oled-display", WiFi.localIP()); + oledThing.description = "A web connected oled display"; + + + oledInputObj["type"] = "object"; + JsonObject oledInputProperties = + oledInputObj.createNestedObject("properties"); + + + JsonObject headlineInput = + oledInputProperties.createNestedObject("headline"); + headlineInput["type"] = "string"; + + JsonObject subheadlineInput = + oledInputProperties.createNestedObject("subheadline"); + subheadlineInput["type"] = "string"; - ThingPropertyValue value; - value.string = &lastText; - text.setValue(value); + JsonObject bodyInput = + oledInputProperties.createNestedObject("body"); + bodyInput["type"] = "string"; + - textDisplay.addProperty(&text); - adapter->addDevice(&textDisplay); + oledThing.addAction(&displayAction); + + adapter->addDevice(&oledThing); adapter->begin(); + + Serial.println("HTTP server started"); + Serial.print("http://"); + Serial.print(WiFi.localIP()); + Serial.print("/things/"); + Serial.println(oledThing.id); + +} + +#define HEADLINE_MAX_LENGHT 10 +#define SUBHEAD_MAX_LENGTH 23 +#define BODY_MAX_LENGTH 20 + +char headline[HEADLINE_MAX_LENGHT]; +char subheadline[SUBHEAD_MAX_LENGTH]; +char body[BODY_MAX_LENGTH]; + +void clear_buffer(){ + memset(headline, 0, HEADLINE_MAX_LENGHT); + memset(subheadline, 0, SUBHEAD_MAX_LENGTH); + memset(body, 0, BODY_MAX_LENGTH); +} + +int rounds = 0; + +void loop() +{ + if(strlen(headline) > 0 || strlen(subheadline) > 0 || strlen(body) > 0){ + display.clearDisplay(); + delay(250); + display.display(); + display.setCursor(0,0); + display.setTextSize(2); + display.setTextColor(WHITE); + display.println(headline); + + display.setCursor(0,15); + display.setTextSize(1); + display.setTextColor(WHITE); + display.println(subheadline); + display.setCursor(0,30); + display.setTextSize(2); + display.setTextColor(WHITE); + display.println(body); + delay(250); + display.display(); + clear_buffer(); + rounds = 1; + }else if(rounds > 0){ + //To avoid burning, clear after 30 seconds. + if(rounds == 300){ + display.clearDisplay(); + delay(250); + display.display(); + rounds = 0; + }else{ + delay(100); + rounds++; + } + } +} + +void do_display(const JsonVariant &input) { + Serial.println("do display..."); + + JsonObject inputObj = input.as(); + display.clearDisplay(); + display.display(); + String headline_tmp = inputObj["headline"]; + String subheadline_tmp = inputObj["subheadline"]; + String body_tmp = inputObj["body"]; + clear_buffer(); + + size_t length = strlen(headline_tmp.c_str()); + if(length > HEADLINE_MAX_LENGHT){ + Serial.println("Headline is too long"); + return; + }else{ + memcpy(headline, headline_tmp.c_str(), length); + } + length = strlen(subheadline_tmp.c_str()); + if(length > SUBHEAD_MAX_LENGTH){ + Serial.println("Subheadline is too long"); + return; + }else{ + memcpy(subheadline, subheadline_tmp.c_str(), length); + } + length = strlen(body_tmp.c_str()); + if(length > BODY_MAX_LENGTH){ + Serial.println("Body is too long"); + return; + }else{ + memcpy(body, body_tmp.c_str(), length); + } } -void loop() { - adapter->update(); - displayString(lastText); +ThingActionObject *action_generator(DynamicJsonDocument *input) { + return new ThingActionObject("display", input, do_display, nullptr); } From 235957f9bcb9d6e1d29b3c932f476f9c63f84698 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Mon, 23 Aug 2021 04:37:22 +0200 Subject: [PATCH 21/35] use uri: format for id in TD --- Thing.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Thing.h b/Thing.h index 54ce5af..e9d1fb0 100644 --- a/Thing.h +++ b/Thing.h @@ -555,7 +555,7 @@ class ThingDevice { } void serialize(JsonObject descr, String ip, uint16_t port) { - descr["id"] = this->id; + descr["id"] = "uri:" + this->id; descr["title"] = this->title; JsonArray context = descr.createNestedArray("@context"); context.add("https://www.w3.org./2019/wot/td/v1"); From e445abc846e10ac437f808c1f52c9dabeb93fea8 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Mon, 23 Aug 2021 05:08:18 +0200 Subject: [PATCH 22/35] Fix context string --- Thing.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Thing.h b/Thing.h index e9d1fb0..1a19326 100644 --- a/Thing.h +++ b/Thing.h @@ -558,7 +558,7 @@ class ThingDevice { descr["id"] = "uri:" + this->id; descr["title"] = this->title; JsonArray context = descr.createNestedArray("@context"); - context.add("https://www.w3.org./2019/wot/td/v1"); + context.add("https://www.w3.org/2019/wot/td/v1"); context.add("https://webthings.io/schemas"); if (this->description != "") { From 4dabab8e1fb1c8e09b3fece7cd2599e9368f0bc9 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Mon, 23 Aug 2021 05:19:51 +0200 Subject: [PATCH 23/35] Make security an array --- Thing.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Thing.h b/Thing.h index 1a19326..de4a933 100644 --- a/Thing.h +++ b/Thing.h @@ -576,7 +576,8 @@ class ThingDevice { descr.createNestedObject("securityDefinitions"); JsonObject nosecSc = securityDefinitions.createNestedObject("nosec_sc"); nosecSc["scheme"] = "nosec"; - descr["security"] = "nosec_sc"; + JsonArray securityJson = descr.createNestedArray("security"); + securityJson.add("nosec_sc"); JsonArray typeJson = descr.createNestedArray("@type"); const char **type = this->type; From 1acbb2586b34c4e90754fe4a3207a7322c01f898 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Mon, 23 Aug 2021 05:32:51 +0200 Subject: [PATCH 24/35] Add thing->forms only when properties are defined --- Thing.h | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Thing.h b/Thing.h index de4a933..2b682cc 100644 --- a/Thing.h +++ b/Thing.h @@ -585,20 +585,17 @@ class ThingDevice { typeJson.add(*type); type++; } - - JsonArray forms = descr.createNestedArray("forms"); - { + + ThingProperty *property = this->firstProperty; + if (property != nullptr) { + JsonArray forms = descr.createNestedArray("forms"); JsonObject forms_prop = forms.createNestedObject(); forms_prop["rel"] = "properties"; JsonArray context = forms_prop.createNestedArray("op"); context.add("readallproperties"); context.add("writeallproperties"); forms_prop["href"] = "/things/" + this->id + "/properties"; - } - - ThingProperty *property = this->firstProperty; - if (property != nullptr) { JsonObject properties = descr.createNestedObject("properties"); while (property != nullptr) { JsonObject obj = properties.createNestedObject(property->id); From aaed5297ff2c4ed91d9a339cb458a1a37cf1007a Mon Sep 17 00:00:00 2001 From: Citrullin Date: Thu, 26 Aug 2021 01:35:18 +0200 Subject: [PATCH 25/35] Use external LED --- examples/LED/LED.ino | 46 ++++++++++++++++++++------------------------ 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/examples/LED/LED.ino b/examples/LED/LED.ino index 2c596dc..92e84cf 100644 --- a/examples/LED/LED.ino +++ b/examples/LED/LED.ino @@ -7,19 +7,21 @@ #define LARGE_JSON_BUFFERS 1 #include -#include -#include +#include "Thing.h" +#include "WebThingAdapter.h" // TODO: Hardcode your wifi credentials here (and keep it private) const char *ssid = ""; const char *password = ""; #if defined(LED_BUILTIN) -const int lampPin = LED_BUILTIN; +const int ledPin = LED_BUILTIN; #else -const int lampPin = 13; // manually configure LED pin +const int ledPin = 19; // manually configure LED pin #endif +const int externalPin = 19; + ThingActionObject *action_generator(DynamicJsonDocument *); WebThingAdapter *adapter; @@ -35,27 +37,25 @@ JsonObject toggleInputObj = toggleInput.to(); ThingAction toggle("toggle", "Toggle", "toggle the lamp on/off", "ToggleAction", &toggleInputObj, action_generator); -bool lastOn = true; - void setup(void) { - pinMode(lampPin, OUTPUT); - digitalWrite(lampPin, HIGH); + pinMode(ledPin, OUTPUT); + pinMode(externalPin, OUTPUT); + digitalWrite(ledPin, HIGH); Serial.begin(115200); Serial.println(""); Serial.print("Connecting to \""); Serial.print(ssid); Serial.println("\""); -#if defined(ESP8266) || defined(ESP32) - WiFi.mode(WIFI_STA); -#endif WiFi.begin(ssid, password); - Serial.println(""); - - // Wait for connection + + bool blink = true; while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); + digitalWrite(ledPin, blink ? LOW : HIGH); // active low led + blink = !blink; } + digitalWrite(ledPin, HIGH); // active low led Serial.println(""); Serial.print("Connected to "); @@ -64,7 +64,7 @@ void setup(void) { Serial.println(WiFi.localIP()); adapter = new WebThingAdapter("led-lamp", WiFi.localIP()); - lamp.description = "A web connected lamp"; + lamp.description = "A web connected led"; lampOn.title = "On/Off"; lamp.addProperty(&lampOn); @@ -87,19 +87,21 @@ void setup(void) { Serial.println(lamp.id); // set initial values - ThingPropertyValue initialOn = {.boolean = true}; + ThingPropertyValue initialOn = {.boolean = false}; lampOn.setValue(initialOn); + digitalWrite(externalPin, LOW); (void)lampOn.changedValueOrNull(); } - +bool lastOn = false; void loop(void) { adapter->update(); bool on = lampOn.getValue().boolean; + if (on) { - digitalWrite(lampPin, HIGH); + digitalWrite(externalPin, HIGH); } else { - digitalWrite(lampPin, LOW); + digitalWrite(externalPin, LOW); } if (lastOn != on) { @@ -116,12 +118,6 @@ void do_toggle(const JsonVariant &input) { Serial.print("state: "); Serial.println(state); - if (state) { - digitalWrite(lampPin, HIGH); - } else { - digitalWrite(lampPin, LOW); - } - ThingDataValue value = {.boolean = state}; lampOn.setValue(value); } From 3b923ed7d2c289a2447332cbadb727699478f7d4 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Tue, 21 Sep 2021 07:56:01 +0200 Subject: [PATCH 26/35] Add matrixDisplay example --- examples/matrixDisplay/Makefile | 55 +++++++++ examples/matrixDisplay/matrixDisplay.ino | 149 +++++++++++++++++++++++ 2 files changed, 204 insertions(+) create mode 100644 examples/matrixDisplay/Makefile create mode 100644 examples/matrixDisplay/matrixDisplay.ino diff --git a/examples/matrixDisplay/Makefile b/examples/matrixDisplay/Makefile new file mode 100644 index 0000000..d15994e --- /dev/null +++ b/examples/matrixDisplay/Makefile @@ -0,0 +1,55 @@ +#!/usr/bin/make -f +# SPDX-License-Identifier: MPL-2.0 +#{ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/ +#} + +default: all + @echo "# $@: $^" + +top_reldir?=../.. +topdir?=${CURDIR}/${top_reldir} + +-include ${topdir}/Makefile + +AVR_TOOLS_DIR?=${ARDUINO_DIR}/hardware/tools/avr +ARDUINO_DIR?=/usr/share/arduino +ARDMK_DIR?=${ARDUINO_DIR} +arduino_mk?=${ARDMK_DIR}/Arduino.mk + +#{ Config part +VARIANT?=mega +BOARD_TAG=mega2560 +BOARD_SUB= +MCU?=at${BOARD_TAG} +F_CPU?=16000000L +MONITOR_PORT?=$(shell ls /dev/ttyUSB* 2> /dev/null \ + || ls /dev/ttyACM* 2> /dev/null \ + || echo "/dev/TODO/setup/monitor" \ + | sort | head -n1) +#} + +ARCHITECTURE=avr +AVRDUDE_ARD_BAUDRATE?=115200 +MONITOR_BAUDRATE?=${AVRDUDE_ARD_BAUDRATE} +AVRDUDE_ARD_PROGRAMMER?=wiring + +CPPFLAGS+=-I${top_reldir} + +ARDUINO_LIBS += Ethernet SPI +ARDUINO_LIBS += ArduinoMDNS +ARDUINO_LIBS += ArduinoJson +ARDUINO_LIBS += WiFi101 +ARDUINO_LIBS += Wire +ARDUINO_LIBS += Adafruit_GFX +ARDUINO_LIBS += Adafruit_SSD1306 + +-include ${arduino_mk} + +rule/setup: + @echo "Please setup your env to use ${arduino_mk}" + +%: rule/setup + @echo "Please run \"make $@\" again" diff --git a/examples/matrixDisplay/matrixDisplay.ino b/examples/matrixDisplay/matrixDisplay.ino new file mode 100644 index 0000000..a4700b8 --- /dev/null +++ b/examples/matrixDisplay/matrixDisplay.ino @@ -0,0 +1,149 @@ +/** + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#include +#include +#include +#include +#include +#include + +#define LARGE_JSON_BUFFERS 1 + +#define HARDWARE_TYPE MD_MAX72XX::FC16_HW +#define MAX_DEVICES 4 +#define CS_PIN 5 + +MD_Parola myDisplay = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES); + +// TODO: Hardcode your wifi credentials here (and keep it private) +const char *ssid = ""; +const char *password = ""; + +#if defined(LED_BUILTIN) +const int ledPin = LED_BUILTIN; +#else +const int ledPin = 13; // manually configure LED pin +#endif + +ThingActionObject *action_generator(DynamicJsonDocument *); +WebThingAdapter *adapter; + +const char *displayTypes[] = {"Matrix Display", nullptr}; +ThingDevice displayThing("matrixDisplay", "Matrix Display", displayTypes); + +StaticJsonDocument<256> displayInput; +JsonObject displayInputObj = displayInput.to(); +ThingAction displayAction("display", "Display text", "display a text on a Matrix display", + "displayAction", &displayInputObj, action_generator); + +void setup() { + Serial.begin(115200); + Serial.println("Initialize..."); + myDisplay.begin(); + myDisplay.setIntensity(0); + Serial.println("Clear display..."); + myDisplay.displayClear(); + + pinMode(ledPin, OUTPUT); + digitalWrite(ledPin, HIGH); + Serial.println(""); + Serial.print("Connecting to \""); + Serial.print(ssid); + Serial.println("\""); +#if defined(ESP8266) || defined(ESP32) + WiFi.mode(WIFI_STA); +#endif + WiFi.begin(ssid, password); + + bool blink = true; + while (WiFi.status() != WL_CONNECTED) { + delay(500); + Serial.print("."); + digitalWrite(ledPin, blink ? LOW : HIGH); + blink = !blink; + } + digitalWrite(ledPin, HIGH); + + Serial.println(""); + Serial.print("Connected to "); + Serial.println(ssid); + Serial.print("IP address: "); + Serial.println(WiFi.localIP()); + + adapter = new WebThingAdapter("matrix-display", WiFi.localIP()); + displayThing.description = "A web connected matrix display"; + + + displayInputObj["type"] = "object"; + JsonObject displayInputProperties = + displayInputObj.createNestedObject("properties"); + + + JsonObject bodyInput = + displayInputProperties.createNestedObject("body"); + bodyInput["type"] = "string"; + + displayThing.addAction(&displayAction); + + adapter->addDevice(&displayThing); + adapter->begin(); +} + +#define BODY_MAX_LENGTH 200 +#define MAX_WO_SCROLLING 6 + +char body[BODY_MAX_LENGTH]; +bool scrolling = false; + +void clear_buffer(){ + memset(body, 0, BODY_MAX_LENGTH); + scrolling = false; +} + +int rounds = 0; + +void loop() +{ + if(strlen(body) > 0){ + myDisplay.displayClear(); + myDisplay.setTextAlignment(PA_LEFT); + + myDisplay.print(body); + rounds = 1; + clear_buffer(); + }else if(rounds > 0){ + //To avoid burning, clear after 30 seconds. + if(rounds == 300){ + myDisplay.displayClear(); + rounds = 0; + }else{ + delay(100); + rounds++; + } + } +} + +void do_display(const JsonVariant &input) { + Serial.println("do display..."); + clear_buffer(); + + JsonObject inputObj = input.as(); + myDisplay.displayClear(); + String body_tmp = inputObj["body"]; + + size_t length = strlen(body_tmp.c_str()); + if(length > BODY_MAX_LENGTH){ + Serial.println("Body is too long"); + return; + } else{ + memcpy(body, body_tmp.c_str(), length); + } +} + +ThingActionObject *action_generator(DynamicJsonDocument *input) { + return new ThingActionObject("display", input, do_display, nullptr); +} \ No newline at end of file From 088de47ede70f08da8aa9415f116c6c07c24ad69 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Wed, 29 Sep 2021 14:44:12 +0200 Subject: [PATCH 27/35] Remove webthings in context --- Thing.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Thing.h b/Thing.h index 54ce5af..5219b38 100644 --- a/Thing.h +++ b/Thing.h @@ -559,7 +559,6 @@ class ThingDevice { descr["title"] = this->title; JsonArray context = descr.createNestedArray("@context"); context.add("https://www.w3.org./2019/wot/td/v1"); - context.add("https://webthings.io/schemas"); if (this->description != "") { descr["description"] = this->description; From ba0fb0c9901e3f237e2af98fb1f8872d5ca9edce Mon Sep 17 00:00:00 2001 From: Citrullin Date: Thu, 7 Oct 2021 15:39:32 +0200 Subject: [PATCH 28/35] Readme update --- README.md | 91 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index 7c306d0..b907c84 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,13 @@ webthing-arduino ================ -A simple server for the ESP8266, the ESP32, boards with Ethernet, or any +A simple Web of Things Thing Description compatible library, for the ESP32. +Available examples +- [BME280](/examples/BME280) +- [LED](/examples/LED) +- [OLED display](/examples/TextDisplay) + +A simple server for ESP32, boards with Ethernet, or any WiFi101-compatible board that implements Mozilla's proposed Web of Things API. The [LED example](https://github.com/WebThingsIO/webthing-arduino/blob/master/examples/LED) @@ -12,25 +18,12 @@ LED. The [LED Lamp example](https://github.com/WebThingsIO/webthing-arduino/blob/master/examples/LEDLamp) ups the ante by introducing a `level` property to expose a dimmable Light. -## Arduino - -### ESP8266 or ESP32 - -To run on either of these boards, download the Arduino IDE and set it up for -board-specific development. These Adafruit guides explain [how to set up for an -ESP8266](https://learn.adafruit.com/adafruit-feather-huzzah-esp8266/using-arduino-ide) -and [how to set up for an -ESP32](https://learn.adafruit.com/adafruit-huzzah32-esp32-feather/using-with-arduino-ide). -You will also need to download the [ESP Async -WebServer](https://github.com/me-no-dev/ESPAsyncWebServer/) library and unpack -it in your sketchbook's libraries folder. +## Installation -### MKR1000, MKR1010, etc. - -* MKR1000 (and similar): Install the WiFi101 library from the Arduino library - manager. -* MKR1010 (and similar): Install the WiFiNINA library from the Arduino library - manager. +1. Download and install [ArduinoJSON](https://arduinojson.org/v6/doc/installation/) +1. Download and install [ESP Async WebServer](https://github.com/me-no-dev/ESPAsyncWebServer/) +1. Download the code as ZIP file `Code -> Download ZIP` +2. Install the library in the Arduino IDE `Sketch -> Include Library -> Add ZIP Library...` ### Continuing onwards @@ -44,24 +37,53 @@ Next, download this library from the same library manager by searching for ![add zip library and LED example](https://github.com/WebThingsIO/webthing-arduino/raw/master/docs/add-library-open-example.png) -You should be able to upload the example sketch onto your board and use it as a -simple Web Thing. This Web Thing can be talked to using the WoT API or added to -the WebThings Gateway using the "Add Thing by URL" feature. Note that -right now, WiFi101-based Things must be manually added by typing the full URL -to the Web Thing, e.g. `http://192.168.0.103/things/led`. + +You should upload one of the example applications to your board. +The Web of Things Thing Description is available under `/.well-known/wot-thing-description`. +All available actions and properties are described in the Thing Description. If you want to create a Web Thing from scratch, make sure to include both -"Thing.h" and "WebThingAdapter.h" (or "EthernetWebThingAdapter.h", if using an -Ethernet board). You can then add Things and Properties to your board using our -proposed API. +"Thing.h" and "WebThingAdapter.h". +You can then add Actions and Properties to your board. + +**Properties** +```c++ +WebThingAdapter *adapter; + +ThingProperty weatherTemp("temperature", "", NUMBER, "TemperatureProperty"); -## PlatformIO +const char *bme280Types[] = {"TemperatureSensor", nullptr}; +ThingDevice weather("bme280", "BME280 Weather Sensor", bme280Types); -Add the `webthing-arduino` library through PlatformIO's package management -interface. Ensure that you get the latest release by examining the entries -in the version number dropdown list. It may be sorted counter-intuitively. -You may also need to manually add the ArduinoJson and other libraries to -your project. +weather.addProperty(&weatherTemp); +adapter->addDevice(&weather); + +``` + +**Actions** +```c++ +WebThingAdapter *adapter; +ThingActionObject *action_generator(DynamicJsonDocument *); + +StaticJsonDocument<256> oledInput; +JsonObject oledInputObj = oledInput.to(); +ThingAction displayAction("display", "Display text", "display a text on OLED", + "displayAction", &oledInputObj, action_generator); + +const char *displayTypes[] = {"OLED Display", nullptr}; +ThingDevice displayThing("oled", "OLED Display", displayTypes); + +oledThing.addAction(&displayAction); +adapter->addDevice(&displayThing); + +void do_display(const JsonVariant &input){ + println("Action request"); +} + +ThingActionObject *action_generator(DynamicJsonDocument *input) { + return new ThingActionObject("display", input, callback, nullptr); +} +``` ## Example @@ -162,6 +184,3 @@ void loop(void) { #include ``` -# Adding to Gateway - -To add your web thing to the WebThings Gateway, install the "Web Thing" add-on and follow the instructions [here](https://github.com/WebThingsIO/thing-url-adapter#readme). From a962e695fcd2a82645fae3a85b86685680f2a9ae Mon Sep 17 00:00:00 2001 From: Citrullin Date: Thu, 7 Oct 2021 15:40:37 +0200 Subject: [PATCH 29/35] Remove screenshots --- README.md | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/README.md b/README.md index b907c84..52c8ab7 100644 --- a/README.md +++ b/README.md @@ -25,18 +25,7 @@ ups the ante by introducing a `level` property to expose a dimmable Light. 1. Download the code as ZIP file `Code -> Download ZIP` 2. Install the library in the Arduino IDE `Sketch -> Include Library -> Add ZIP Library...` -### Continuing onwards - -Make sure to install the current release of the ArduinoJson library (6) if you -don't have it installed already. - -![ArduinoJson install process](https://github.com/WebThingsIO/webthing-arduino/raw/master/docs/arduinojson.png) - -Next, download this library from the same library manager by searching for -`webthing`. - -![add zip library and LED example](https://github.com/WebThingsIO/webthing-arduino/raw/master/docs/add-library-open-example.png) - +### Start development You should upload one of the example applications to your board. The Web of Things Thing Description is available under `/.well-known/wot-thing-description`. From 064c974665f9bb895c7867d1d25432af56bd4974 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Thu, 7 Oct 2021 15:41:22 +0200 Subject: [PATCH 30/35] Remove incorrect statements --- README.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/README.md b/README.md index 52c8ab7..aa5ce80 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,6 @@ Available examples - [LED](/examples/LED) - [OLED display](/examples/TextDisplay) -A simple server for ESP32, boards with Ethernet, or any -WiFi101-compatible board that implements Mozilla's proposed Web of Things API. -The [LED -example](https://github.com/WebThingsIO/webthing-arduino/blob/master/examples/LED) -exposes an OnOffSwitch named "Built-in LED" which controls the board's built-in -LED. The [LED Lamp -example](https://github.com/WebThingsIO/webthing-arduino/blob/master/examples/LEDLamp) -ups the ante by introducing a `level` property to expose a dimmable Light. - ## Installation 1. Download and install [ArduinoJSON](https://arduinojson.org/v6/doc/installation/) From 586b797737c12c52658f56e23c0898e1f94df78c Mon Sep 17 00:00:00 2001 From: Citrullin Date: Mon, 11 Oct 2021 15:40:27 +0200 Subject: [PATCH 31/35] Add readme --- examples/BME280/README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 examples/BME280/README.md diff --git a/examples/BME280/README.md b/examples/BME280/README.md new file mode 100644 index 0000000..7f57c47 --- /dev/null +++ b/examples/BME280/README.md @@ -0,0 +1,27 @@ +## BME 280 WebThings example + +In order to be able to use this example, you need to install the WebThings library. + +Change the ssid and password to connect to your router + +```c++ +const char *ssid = ""; +const char *password = ""; +``` + +Make sure the BME/BMP 280 has the correct I2C address (0x76) +If it doesn't work or you know you use the other address, change it in + +```c++ +BME280I2C::Settings settings( + BME280::OSR_X1, + BME280::OSR_X1, + BME280::OSR_X1, + BME280::Mode_Forced, + BME280::StandbyTime_1000ms, + BME280::Filter_Off, + BME280::SpiEnable_False, + BME280I2C::I2CAddr_0x76 // I2C address. I2C specific. +); + +``` From e605704e1200bae024fda810ae9a10b5b740d1a2 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Mon, 11 Oct 2021 15:56:59 +0200 Subject: [PATCH 32/35] Matrix Display Readme --- examples/matrixDisplay/README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 examples/matrixDisplay/README.md diff --git a/examples/matrixDisplay/README.md b/examples/matrixDisplay/README.md new file mode 100644 index 0000000..637d55c --- /dev/null +++ b/examples/matrixDisplay/README.md @@ -0,0 +1,32 @@ +## Matrix Display example + +You need to install the WebThings Arduino library in order to use this example. + +Change the ssid and password to connect to your router + +```c++ +const char *ssid = ""; +const char *password = ""; +``` + +If you want to use more than four matrix displays, you can just change the value of `MAX_DEVICES` + +```c++ +#define MAX_DEVICES 4 +``` + +If you use another PIN than PIN 5 as CS, you have to change it in the code before + +```c++ +#define CS_PIN 5 +``` + +The SPI matrix display has to be connected to VSPI of the ESP32. + +| ESP32 Pin | | MAX7219 Matrix | +|-------------|---|----------------| +| VCC (3.3V) | → | VCC (VDD) | +| GND | → | GND | +| D23 (IO23) | → | DIN (MOSI) | +| D5 (IO5) | → | CS (SS) | +| D18 (IO18) | → | CLK | \ No newline at end of file From 19c93655679de98ff38cb517d14c793f5c7bce41 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Mon, 11 Oct 2021 16:00:24 +0200 Subject: [PATCH 33/35] Add LED example readme --- examples/LED/README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 examples/LED/README.md diff --git a/examples/LED/README.md b/examples/LED/README.md new file mode 100644 index 0000000..4fb3ff8 --- /dev/null +++ b/examples/LED/README.md @@ -0,0 +1,16 @@ +## LED WebThings example + +You need to install the WebThings Arduino library in order to use this example. + +Change the ssid and password in order to connect to your router + +```c++ +const char *ssid = ""; +const char *password = ""; +``` + +Connect a LED to PIN 19. Or, if you want to use another PIN, change the PIN accordingly in the code. + +```c++ +const int externalPin = 19; +``` From 4da14e661b0922fd06ace35beccd6423c255a792 Mon Sep 17 00:00:00 2001 From: Citrullin Date: Mon, 11 Oct 2021 16:34:01 +0200 Subject: [PATCH 34/35] Add beginner tutorials to README --- README.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/README.md b/README.md index aa5ce80..89d277e 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,29 @@ Available examples - [LED](/examples/LED) - [OLED display](/examples/TextDisplay) +## Beginner Tutorials + +If you need a more extensive explanation of the usage of this library, we have some step by step tutorials available with more context. + +### WebThings Arduino + Node-WoT + +[Node-WoT](https://github.com/eclipse/thingweb.node-wot) is a Javascript client library to consume Web of Things devices. + +- [Show number of Github forks and stars on Matrix display](https://bind.systems/blog/web-of-things-github-forks-stars/) +- [Run speedtest and show results on an OLED display](https://bind.systems/blog/web-of-things-speedtest/) + +### WebThings Arduino + Node-RED + +[Node-RED](https://nodered.org/) is a drag and drop tool for connecting devices. It requires little Javascript programming knowledge. + +- [Connect temperature sensor to OLED display](https://bind.systems/blog/web-of-things-node-red-temperature-oled/) + +### WebThings Arduino + +- [Blinking LED](https://bind.systems/blog/web-of-things-led/) +- [Matrix display](https://bind.systems/blog/web-of-things-arduino-matrix-display/) +- [OLED display](https://bind.systems/blog/web-of-things-arduino-oled/) + ## Installation 1. Download and install [ArduinoJSON](https://arduinojson.org/v6/doc/installation/) From 12551f64fce7b564b2701e11d4a5e4da17d6e15f Mon Sep 17 00:00:00 2001 From: Citrullin Date: Mon, 11 Oct 2021 17:15:14 +0200 Subject: [PATCH 35/35] Remove Webthings.io schemas in context --- Thing.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Thing.h b/Thing.h index 2b682cc..248bd67 100644 --- a/Thing.h +++ b/Thing.h @@ -559,7 +559,6 @@ class ThingDevice { descr["title"] = this->title; JsonArray context = descr.createNestedArray("@context"); context.add("https://www.w3.org/2019/wot/td/v1"); - context.add("https://webthings.io/schemas"); if (this->description != "") { descr["description"] = this->description;