diff --git a/ESPWebThingAdapter.h b/ESPWebThingAdapter.h index 6d96fcf..6f910a5 100644 --- a/ESPWebThingAdapter.h +++ b/ESPWebThingAdapter.h @@ -11,7 +11,7 @@ #pragma once -#if defined(ESP32) || defined(ESP8266) + #include #include @@ -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)); @@ -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)); @@ -158,15 +148,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 +159,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 +196,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; @@ -362,16 +218,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); } @@ -474,6 +329,7 @@ class WebThingAdapter { void handleThingActionPost(AsyncWebServerRequest *request, ThingDevice *device, ThingAction *action) { if (!verifyHost(request)) { + Serial.println("Invalid Host"); return; } @@ -486,6 +342,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); @@ -493,31 +350,13 @@ class WebThingAdapter { return; } - 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; + memset(body_data, 0, sizeof(body_data)); + request->send(500); + 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); @@ -567,83 +406,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(); - - 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; - } - -#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); - 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)) { @@ -709,4 +471,3 @@ class WebThingAdapter { } }; -#endif // ESP diff --git a/Thing.h b/Thing.h index 25a6ea3..54ce5af 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'); @@ -99,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; @@ -115,19 +104,13 @@ class ThingActionObject { void setStatus(const char *s) { status = s; -#ifndef WITHOUT_WS - if (notify_fn != nullptr) { - notify_fn(this); - } -#endif } void start() { setStatus("pending"); JsonObject actionObj = actionRequest->as(); - JsonObject inner = actionObj[name]; - start_fn(inner["input"]); + start_fn(actionObj); finish(); } @@ -197,7 +180,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 +280,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; @@ -361,67 +344,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: @@ -460,8 +383,6 @@ class ThingEventObject { data["data"] = *this->getValue().string; break; } - - data["timestamp"] = timestamp; } }; @@ -471,9 +392,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 +403,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,37 +552,14 @@ 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) { descr["id"] = this->id; descr["title"] = this->title; - descr["@context"] = "https://webthings.io/schemas"; + 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; @@ -723,41 +584,17 @@ class ThingDevice { typeJson.add(*type); type++; } - - JsonArray links = descr.createNestedArray("links"); - { - 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"; - } - + + JsonArray forms = descr.createNestedArray("forms"); { - JsonObject links_prop = links.createNestedObject(); - links_prop["rel"] = "events"; - links_prop["href"] = "/things/" + this->id + "/events"; + 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"; } -#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) { diff --git a/WebThingAdapter.h b/WebThingAdapter.h index 2d72fa8..40a9ae7 100644 --- a/WebThingAdapter.h +++ b/WebThingAdapter.h @@ -10,5 +10,6 @@ #pragma once +#if defined(ESP32) || defined(ESP8266) #include "ESPWebThingAdapter.h" -#include "WiFi101WebThingAdapter.h" +#endif diff --git a/WiFi101WebThingAdapter.h b/WiFi101WebThingAdapter.h deleted file mode 100644 index dc0fc5a..0000000 --- a/WiFi101WebThingAdapter.h +++ /dev/null @@ -1,685 +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 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 { - 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 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