From d74745e5a9c6e33f2beb5cfedd305230bee5e516 Mon Sep 17 00:00:00 2001 From: Gilberto Conti Date: Thu, 19 May 2016 20:29:25 +0200 Subject: [PATCH 001/175] ArduinoCloud base --- ArduinoCloud.h | 6 + ArduinoCloudThing.cpp | 105 ++++ ArduinoCloudThing.h | 37 ++ ArduinoCloudThingBase.cpp | 147 +++++ ArduinoCloudThingBase.h | 110 ++++ lib/Network.cpp | 41 ++ lib/Network.h | 13 + lib/Timer.cpp | 26 + lib/Timer.h | 11 + lib/mqtt/FP.h | 208 +++++++ lib/mqtt/MQTTClient.h | 910 ++++++++++++++++++++++++++++++ lib/mqtt/MQTTConnect.h | 136 +++++ lib/mqtt/MQTTConnectClient.c | 206 +++++++ lib/mqtt/MQTTDeserializePublish.c | 102 ++++ lib/mqtt/MQTTPacket.c | 401 +++++++++++++ lib/mqtt/MQTTPacket.h | 132 +++++ lib/mqtt/MQTTPublish.h | 38 ++ lib/mqtt/MQTTSerializePublish.c | 164 ++++++ lib/mqtt/MQTTSubscribe.h | 39 ++ lib/mqtt/MQTTSubscribeClient.c | 132 +++++ lib/mqtt/MQTTUnsubscribe.h | 38 ++ lib/mqtt/MQTTUnsubscribeClient.c | 101 ++++ 22 files changed, 3103 insertions(+) create mode 100644 ArduinoCloud.h create mode 100644 ArduinoCloudThing.cpp create mode 100644 ArduinoCloudThing.h create mode 100755 ArduinoCloudThingBase.cpp create mode 100755 ArduinoCloudThingBase.h create mode 100644 lib/Network.cpp create mode 100644 lib/Network.h create mode 100644 lib/Timer.cpp create mode 100644 lib/Timer.h create mode 100644 lib/mqtt/FP.h create mode 100644 lib/mqtt/MQTTClient.h create mode 100644 lib/mqtt/MQTTConnect.h create mode 100644 lib/mqtt/MQTTConnectClient.c create mode 100644 lib/mqtt/MQTTDeserializePublish.c create mode 100644 lib/mqtt/MQTTPacket.c create mode 100644 lib/mqtt/MQTTPacket.h create mode 100644 lib/mqtt/MQTTPublish.h create mode 100644 lib/mqtt/MQTTSerializePublish.c create mode 100644 lib/mqtt/MQTTSubscribe.h create mode 100644 lib/mqtt/MQTTSubscribeClient.c create mode 100644 lib/mqtt/MQTTUnsubscribe.h create mode 100644 lib/mqtt/MQTTUnsubscribeClient.c diff --git a/ArduinoCloud.h b/ArduinoCloud.h new file mode 100644 index 000000000..db584257f --- /dev/null +++ b/ArduinoCloud.h @@ -0,0 +1,6 @@ +#ifndef ArduinoCloud_h +#define ArduinoCloud_h + +#include + +#endif diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp new file mode 100644 index 000000000..4a3d9dda9 --- /dev/null +++ b/ArduinoCloudThing.cpp @@ -0,0 +1,105 @@ +#include + +ArduinoCloudThing::ArduinoCloudThing() { +} + +void ArduinoCloudThing::begin(const char* name, const char* username, const char* id, const char* password, Client &client) { + ArduinoCloudThingBase::begin(name, username, id, password); + + this->client = new MQTT::Client(network); + this->network.setClient(&client); + this->options = MQTTPacket_connectData_initializer; + this->client->defaultMessageHandler.attach(this, &ArduinoCloudThing::callback); +} + +boolean ArduinoCloudThing::connect() { + if(!network.connect((char*)SERVER_DOMAIN, SERVER_PORT)) { + return false; + } + + char statusTopic[strlen(username) + strlen(name) + strlen(STATUS_TOPIC) + 3]; // 2 extra bytes for /'s, 1 for null terminator + sprintf(statusTopic, "%s/%s/%s", username, name, STATUS_TOPIC); + // if (cloud_debug) { + // CLOUD_DEBUG_STREAM.print("Will Topic: "); + // CLOUD_DEBUG_STREAM.println(statusTopic); + // } + options.clientID.cstring = (char*)name; + options.username.cstring = (char*)id; + options.password.cstring = (char*)password; + options.keepAliveInterval = 10; + options.willFlag = 0x1; + options.will.topicName.cstring = (char*)statusTopic; + options.will.message.cstring = (char*)OFFLINE_STATUS_PAYLOAD; + options.will.retained = 0x1; + + if (client->connect(options) == 0) { + publish(statusTopic, (char*)ONLINE_STATUS_PAYLOAD, strlen(ONLINE_STATUS_PAYLOAD), true); + return true; + } + + return false; +} + +void ArduinoCloudThing::publish(const char * topic, const char * payload) { + publish(topic, (char*)payload, (unsigned int)strlen(payload)); +} + +void ArduinoCloudThing::publish(const char * topic, const char * payload, unsigned int length, bool retained) { + MQTT::Message message; + message.qos = MQTT::QOS0; + message.retained = retained; + message.dup = false; + message.payload = (void*)payload; + message.payloadlen = length; + client->publish(topic, message); +} + +// Reconnect to the mqtt broker +void ArduinoCloudThing::poll() { + while (!client->isConnected()){ + if (cloud_debug) { + CLOUD_DEBUG_STREAM.println("Connecting to mqtt broker..."); + } + while (!connect()){ + delay(1000); + } + if (cloud_debug) { + CLOUD_DEBUG_STREAM.println("Connected"); + } + mqttSubscribe(); + } + if(!network.connected() && client->isConnected()) { + client->disconnect(); + } + client->yield(); +} + +// Subscribe to all proprie that have RW permission +void ArduinoCloudThing::mqttSubscribe(){ + for (int i=0; ipermission, "RW") == 0) { + String topic = buildTopicProperty(i); + if (cloud_debug) { + CLOUD_DEBUG_STREAM.print("Subscribe to: "); + CLOUD_DEBUG_STREAM.println(topic); + } + client->subscribe(topic.c_str(), MQTT::QOS0, NULL); + } + } +} + +ArduinoCloudThing ArduinoCloudThing::callback(MQTT::MessageData& messageData) { + MQTT::Message &message = messageData.message; + // null terminate topic to create String object + int len = messageData.topicName.lenstring.len; + char topic[len+1]; + memcpy(topic, messageData.topicName.lenstring.data, (size_t)len); + topic[len] = '\0'; + + char * payload = (char *)message.payload; + payload[message.payloadlen] = '\0'; + + updatePropertyFromTopic(topic, payload); + + return *this; +} diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h new file mode 100644 index 000000000..f75b3808c --- /dev/null +++ b/ArduinoCloudThing.h @@ -0,0 +1,37 @@ +#ifndef ArduinoCloudThing_h +#define ArduinoCloudThing_h + +#include +#include +#include +#include "lib/mqtt/MQTTClient.h" +#include "lib/Network.h" + +#ifndef MQTT_BUFFER_SIZE +#define MQTT_BUFFER_SIZE 256 +#endif + +#define MQTTCLIENT_QOS1 0 +#define MQTTCLIENT_QOS2 0 + +class ArduinoCloudThing : public ArduinoCloudThingBase { +public: + ArduinoCloudThing(); + void begin(const char* name, const char* username, const char* id, const char* password, Client &client); + void poll(); + +protected: + virtual void publish(const char * topic, const char* payload); + +private: + ArduinoCloudThing callback(MQTT::MessageData& messageData); + void mqttSubscribe(); + boolean connect(); + void publish(const char * topic, const char * payload, unsigned int length, bool retained = false); + + Network network; + MQTT::Client * client; + MQTTPacket_connectData options; +}; + +#endif diff --git a/ArduinoCloudThingBase.cpp b/ArduinoCloudThingBase.cpp new file mode 100755 index 000000000..c05b29528 --- /dev/null +++ b/ArduinoCloudThingBase.cpp @@ -0,0 +1,147 @@ +#include "ArduinoCloudThingBase.h" + +void ArduinoCloudThingBase::begin(const char* name, const char* username, const char* id, const char* password){ + this->name = name; + this->username = username; + this->id = id; + this->password = password; +} + +void ArduinoCloudThingBase::addProperty(const char* name, const char* datatype, const char* permission){ + addExternalProperty(NULL, name, datatype, permission, "", 0); +} + +void ArduinoCloudThingBase::addProperty(const char* name, const char* datatype, const char* permission, const char* policy){ + addExternalProperty(NULL, name, datatype, permission, policy, 0); +} + +void ArduinoCloudThingBase::addProperty(const char* name, const char* datatype, const char* permission, const char* policy, int lapse){ + addExternalProperty(NULL, name, datatype, permission, policy, lapse); +} + +void ArduinoCloudThingBase::addExternalProperty(const char* other_device_name, const char* name, const char* datatype){ + addExternalProperty(other_device_name, name, datatype, RW, "", 0); +} + +void ArduinoCloudThingBase::addExternalProperty(const char* other_device_name, const char* name, const char* datatype, const char* permission, const char* policy, int lapse){ + // Create property structure + property_conf* prop = new property_conf {name, datatype, permission, other_device_name, policy, "", (long)lapse*1000, 0}; + properties[properties_count] = prop; // Save pointer to scructure + properties_count++; // count the number of properties +} + +void ArduinoCloudThingBase::writeProperty(const char* name, String value){ + writeProperty(name, value.c_str()); +} + +void ArduinoCloudThingBase::writeProperty(const char* name, int value){ + writeProperty(name, String(value).c_str()); +} + +void ArduinoCloudThingBase::writeProperty(const char* name, float value){ + writeProperty(name, String(value).c_str()); +} + +void ArduinoCloudThingBase::writeProperty(const char* name, const char* value){ + int i = discoverProperty(NULL, name); + + if (i == -1) { + // not found, nothing to do + return; + } + + if (properties[i]->value.equals(value) && strcmp(properties[i]->policy, ON_CHANGE) == 0) { + if (cloud_debug) { + CLOUD_DEBUG_STREAM.print("No Changes for "); + CLOUD_DEBUG_STREAM.print(name); + CLOUD_DEBUG_STREAM.print(": "); + CLOUD_DEBUG_STREAM.println(value); + } + return; + } + if ((long)(millis() - properties[i]->runtime) >= properties[i]->lapse) { + properties[i]->value = value; + String topic = buildTopicProperty(i); + + publish(topic.c_str(), value); + + if (cloud_debug) { + CLOUD_DEBUG_STREAM.print("OK, Published "); + CLOUD_DEBUG_STREAM.print(value); + CLOUD_DEBUG_STREAM.print(" on topic: "); + CLOUD_DEBUG_STREAM.println(topic); + } + properties[i]->runtime = millis(); + } +} + +// Get the a property value +String ArduinoCloudThingBase::readProperty(const char* name){ + return readProperty(NULL, name); +} + +String ArduinoCloudThingBase::readProperty(const char* _device_name, const char* name){ + int index = discoverProperty(_device_name, name); + + if (index != -1) { + return String(properties[index]->value); + } + + return String(); +} + +void ArduinoCloudThingBase::push(){ + for (int i = 0; i <= properties_count; i++){ + if (properties[i]->device_name == NULL){ + String topic = buildTopicProperty(i); + + publish(topic.c_str(), properties[i]->value.c_str()); + } + } +} + +// Discovers the property position in the array based +// on the name of the property +int ArduinoCloudThingBase::discoverProperty(const char* device_name, const char* name){ + for (int i = 0; i <= properties_count; i++){ + if (properties[i]->name == name && properties[i]->device_name == device_name){ + return i; + } + } + return -1; +} + +void ArduinoCloudThingBase::updatePropertyFromTopic(const char* topic, const char* value) { + for (int i = 0; i <= properties_count; i++){ + String propertyTopic = buildTopicProperty(i); + + if (strcmp(propertyTopic.c_str(), topic) == 0) { + properties[i]->value = value; + } + } +} + +// Build the topic for a property +String ArduinoCloudThingBase::buildTopicProperty(int property_index){ + String topic; + + topic += username; + topic += '/'; + topic += (properties[property_index]->device_name) ? properties[property_index]->device_name : name; + topic += '/'; + topic += properties[property_index]->name; + + return topic; +} + +void ArduinoCloudThingBase::publish(const char * /*topic*/, const char * /*payload*/) { +} + + +void ArduinoCloudThingBase::enableDebug(){ + cloud_debug = true; +} + +void ArduinoCloudThingBase::disableDebug(){ + cloud_debug = false; +} diff --git a/ArduinoCloudThingBase.h b/ArduinoCloudThingBase.h new file mode 100755 index 000000000..c771855e4 --- /dev/null +++ b/ArduinoCloudThingBase.h @@ -0,0 +1,110 @@ +#ifndef ArduinoCloudThingBase_h +#define ArduinoCloudThingBase_h + +#include +#include "lib/Timer.h" + +// Uncomment to allow the debut output +#define CLOUD_DEBUG_STREAM Serial // SAMD +// #define CLOUD_DEBUG_STREAM SerialUSB // SAM3X Native + +/// Configurations +#define SERVER_DOMAIN "mqtt.arduino.cc" +#define SERVER_PORT 8883 +#define PROPERTIES_MAX 10 + +/// Permission +#define R "R" +#define RW "RW" + +/// Data Type +#define CHARSTRING "charstring" +#define FLOAT "float" +#define INT "int" + +/// Temperature +#define TEMPERATURE_C "celsius" +#define TEMPERATURE_F "fahrenheit" + +/// Distance +#define LENGHT_M "meters" +#define LENGHT_C "centimeters" +#define LENGHT_I "inches" + +/// MIX +#define PERCENTAGE "percentage" +#define ANALOG "analog" +#define LUMEN "lumen" +#define PPM "ppm" // gas part per million +#define STATUS "status" + +//// Polices +#define TIMED "timed" +#define ON_CHANGE "on_change" + +#ifndef MQTT_BUFFER_SIZE +#define MQTT_BUFFER_SIZE 256 +#endif + +#define MQTTCLIENT_QOS1 0 +#define MQTTCLIENT_QOS2 0 + +/// Status +#define STATUS_TOPIC "status" +#define ONLINE_STATUS_PAYLOAD "online" +#define OFFLINE_STATUS_PAYLOAD "offline" + +class ArduinoCloudThingBase { + +public: + void addProperty(const char* name, const char* datatype, const char* permission); + void addProperty(const char* name, const char* datatype, const char* permission, const char* policy); + void addProperty(const char* name, const char* datatype, const char* permission, const char* policy, int lapse); + void addExternalProperty(const char* other_device_name, const char* name, const char* datatype); + + void writeProperty(const char* name, const char* value); + void writeProperty(const char* name, float value); + void writeProperty(const char* name, int value); + void writeProperty(const char* name, String value); + + String readProperty(const char* name); + String readProperty(const char* other_device_name, const char* name); + + void enableDebug(); + void disableDebug(); + void push(); + +protected: + void begin(const char* name, const char* username, const char* id, const char* password); + void addExternalProperty(const char* other_device_name, const char* name, const char* datatype, const char* permission, const char* policy, int lapse); + virtual void publish(const char * topic, const char* payload); + + String buildTopicProperty(int property_index); + int discoverProperty(const char* device_name, const char* name); + + // void addProperty(const char* other_device_name, const char* name, const char* datatype, const char* permission, const char* policy, int lapse); + void updatePropertyFromTopic(const char* topic, const char* value); + + // Object configuration + const char* username; + const char* id; + const char* password; + const char* name; + + // Property configuration + struct property_conf { + const char* name; + const char* datatype; + const char* permission; + const char* device_name; + const char* policy; + String value; + long lapse; + long runtime; + }; + + bool cloud_debug = false; + property_conf* properties[PROPERTIES_MAX]; + int properties_count = 0; +}; +#endif diff --git a/lib/Network.cpp b/lib/Network.cpp new file mode 100644 index 000000000..19bbaff9d --- /dev/null +++ b/lib/Network.cpp @@ -0,0 +1,41 @@ +#include +#include "Network.h" + +void Network::setClient(Client * _client) { + this->client = _client; +} + +int Network::connect(char* hostname, int port) { + return this->client->connect(hostname, port); +} + +int Network::read(unsigned char* buffer, int len, int timeout) { + if(!client->connected()) { + return -1; // return an error + } + + if(this->client->available() == 0) { + return 0; // nothing to read + } + + this->client->setTimeout(timeout); + return this->client->readBytes(buffer, len); +} + +int Network::write(unsigned char* buffer, int len, int timeout) { + if(!client->connected()) { + return -1; // return an error + } + + client->setTimeout(timeout); + return client->write((uint8_t*)buffer, len); +} + +boolean Network::connected() { + return client->connected(); +} + +int Network::disconnect() { + client->stop(); + return 0; +} diff --git a/lib/Network.h b/lib/Network.h new file mode 100644 index 000000000..9a366ec2a --- /dev/null +++ b/lib/Network.h @@ -0,0 +1,13 @@ +#include + +class Network { +private: + Client* client; +public: + void setClient(Client * client); + int connect(char* hostname, int port); + int read(unsigned char* buffer, int len, int timeout); + int write(unsigned char* buffer, int len, int timeout); + boolean connected(); + int disconnect(); +}; diff --git a/lib/Timer.cpp b/lib/Timer.cpp new file mode 100644 index 000000000..37947a89f --- /dev/null +++ b/lib/Timer.cpp @@ -0,0 +1,26 @@ +#include +#include "Timer.h" + +Timer::Timer() { + this->interval_end_ms = 0L; +} + +Timer::Timer(int ms) { + countdown_ms(ms); +} + +bool Timer::expired() { + return (interval_end_ms > 0L) && (millis() >= interval_end_ms); +} + +void Timer::countdown_ms(unsigned long ms) { + interval_end_ms = millis() + ms; +} + +void Timer::countdown(int seconds) { + countdown_ms((unsigned long)seconds * 1000L); +} + +int Timer::left_ms() { + return interval_end_ms - millis(); +} diff --git a/lib/Timer.h b/lib/Timer.h new file mode 100644 index 000000000..67844b8e0 --- /dev/null +++ b/lib/Timer.h @@ -0,0 +1,11 @@ +class Timer { +public: + Timer(); + Timer(int ms); + bool expired(); + void countdown_ms(unsigned long ms); + void countdown(int seconds); + int left_ms(); +private: + unsigned long interval_end_ms; +}; diff --git a/lib/mqtt/FP.h b/lib/mqtt/FP.h new file mode 100644 index 000000000..5ce75dc5a --- /dev/null +++ b/lib/mqtt/FP.h @@ -0,0 +1,208 @@ +/******************************************************************************* + * Copyright (c) 2013, 2014 + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Sam Grove - initial API and implementation and/or initial documentation + * Ian Craggs - added attached and detached member functions + * Sam Grove - removed need for FP.cpp + *******************************************************************************/ + +#ifndef FP_H +#define FP_H + +/** Example using the FP Class with global functions + * @code + * #include "mbed.h" + * #include "FP.h" + * + * FPfp; + * DigitalOut myled(LED1); + * + * void handler(bool value) + * { + * myled = value; + * return; + * } + * + * int main() + * { + * fp.attach(&handler); + * + * while(1) + * { + * fp(1); + * wait(0.2); + * fp(0); + * wait(0.2); + * } + * } + * @endcode + */ + +/** Example using the FP Class with different class member functions + * @code + * #include "mbed.h" + * #include "FP.h" + * + * FPfp; + * DigitalOut myled(LED4); + * + * class Wrapper + * { + * public: + * Wrapper(){} + * + * void handler(bool value) + * { + * myled = value; + * return; + * } + * }; + * + * int main() + * { + * Wrapper wrapped; + * fp.attach(&wrapped, &Wrapper::handler); + * + * while(1) + * { + * fp(1); + * wait(0.2); + * fp(0); + * wait(0.2); + * } + * } + * @endcode + */ + +/** Example using the FP Class with member FP and member function +* @code +* #include "mbed.h" +* #include "FP.h" +* +* DigitalOut myled(LED2); +* +* class Wrapper +* { +* public: +* Wrapper() +* { +* fp.attach(this, &Wrapper::handler); +* } +* +* void handler(bool value) +* { +* myled = value; +* return; +* } +* +* FPfp; +* }; +* +* int main() +* { +* Wrapper wrapped; +* +* while(1) +* { +* wrapped.fp(1); +* wait(0.2); +* wrapped.fp(0); +* wait(0.2); +* } +* } +* @endcode +*/ + +/** + * @class FP + * @brief API for managing Function Pointers + */ +template +class FP +{ +public: + /** Create the FP object - only one callback can be attached to the object, that is + * a member function or a global function, not both at the same time + */ + FP() + { + obj_callback = 0; + c_callback = 0; + } + + /** Add a callback function to the object + * @param item - Address of the initialized object + * @param member - Address of the member function (dont forget the scope that the function is defined in) + */ + template + void attach(T *item, retT (T::*method)(argT)) + { + obj_callback = (FPtrDummy *)(item); + method_callback = (retT (FPtrDummy::*)(argT))(method); + return; + } + + /** Add a callback function to the object + * @param function - The address of a globally defined function + */ + void attach(retT (*function)(argT)) + { + c_callback = function; + } + + /** Invoke the function attached to the class + * @param arg - An argument that is passed into the function handler that is called + * @return The return from the function hanlder called by this class + */ + retT operator()(argT arg) const + { + if( 0 != c_callback ) { + return obj_callback ? (obj_callback->*method_callback)(arg) : (*c_callback)(arg); + } + // return (retT)0; + } + + /** Determine if an callback is currently hooked + * @return 1 if a method is hooked, 0 otherwise + */ + bool attached() + { + return obj_callback || c_callback; + } + + /** Release a function from the callback hook + */ + void detach() + { + obj_callback = 0; + c_callback = 0; + } + +private: + + // empty type used for casting + class FPtrDummy; + + FPtrDummy *obj_callback; + + /** + * @union Funciton + * @brief Member or global callback function + */ + union { + retT (*c_callback)(argT); /*!< Footprint for a global function */ + retT (FPtrDummy::*method_callback)(argT); /*!< Footprint for a member function */ + }; +}; + +#endif diff --git a/lib/mqtt/MQTTClient.h b/lib/mqtt/MQTTClient.h new file mode 100644 index 000000000..08621fccd --- /dev/null +++ b/lib/mqtt/MQTTClient.h @@ -0,0 +1,910 @@ +/******************************************************************************* + * Copyright (c) 2014, 2015 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Ian Craggs - fix for bug 458512 - QoS 2 messages + *******************************************************************************/ + +#if !defined(MQTTCLIENT_H) +#define MQTTCLIENT_H + +#include "FP.h" +#include "MQTTPacket.h" +#include "stdio.h" + +#if !defined(MQTTCLIENT_QOS1) + #define MQTTCLIENT_QOS1 1 +#endif +#if !defined(MQTTCLIENT_QOS2) + #define MQTTCLIENT_QOS2 0 +#endif + +namespace MQTT +{ + + +enum QoS { QOS0, QOS1, QOS2 }; + +// all failure return codes must be negative +enum returnCode { BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 }; + + +struct Message +{ + enum QoS qos; + bool retained; + bool dup; + unsigned short id; + void *payload; + size_t payloadlen; +}; + + +struct MessageData +{ + MessageData(MQTTString &aTopicName, struct Message &aMessage) : message(aMessage), topicName(aTopicName) + { } + + struct Message &message; + MQTTString &topicName; +}; + + +class PacketId +{ +public: + PacketId() + { + next = 0; + } + + int getNext() + { + return next = (next == MAX_PACKET_ID) ? 1 : ++next; + } + +private: + static const int MAX_PACKET_ID = 65535; + int next; +}; + +/** + * @class Client + * @brief blocking, non-threaded MQTT client API + * + * This version of the API blocks on all method calls, until they are complete. This means that only one + * MQTT request can be in process at any one time. + * @param Network a network class which supports send, receive + * @param Timer a timer class with the methods: + */ +template +class Client +{ + +public: + + typedef void (*messageHandler)(MessageData&); + FP defaultMessageHandler; + + /** Construct the client + * @param network - pointer to an instance of the Network class - must be connected to the endpoint + * before calling MQTT connect + * @param limits an instance of the Limit class - to alter limits as required + */ + Client(Network& network, unsigned int command_timeout_ms = 30000); + + /** Set the default message handling callback - used for any message which does not match a subscription message handler + * @param mh - pointer to the callback function + */ + void setDefaultMessageHandler(messageHandler mh) + { + // defaultMessageHandler.attach(mh); + } + + /** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack + * The nework object must be connected to the network endpoint before calling this + * Default connect options are used + * @return success code - + */ + int connect(); + + /** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack + * The nework object must be connected to the network endpoint before calling this + * @param options - connect options + * @return success code - + */ + int connect(MQTTPacket_connectData& options); + + /** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs + * @param topic - the topic to publish to + * @param message - the message to send + * @return success code - + */ + int publish(const char* topicName, Message& message); + + /** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs + * @param topic - the topic to publish to + * @param payload - the data to send + * @param payloadlen - the length of the data + * @param qos - the QoS to send the publish at + * @param retained - whether the message should be retained + * @return success code - + */ + int publish(const char* topicName, void* payload, size_t payloadlen, enum QoS qos = QOS0, bool retained = false); + + /** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs + * @param topic - the topic to publish to + * @param payload - the data to send + * @param payloadlen - the length of the data + * @param id - the packet id used - returned + * @param qos - the QoS to send the publish at + * @param retained - whether the message should be retained + * @return success code - + */ + int publish(const char* topicName, void* payload, size_t payloadlen, unsigned short& id, enum QoS qos = QOS1, bool retained = false); + + /** MQTT Subscribe - send an MQTT subscribe packet and wait for the suback + * @param topicFilter - a topic pattern which can include wildcards + * @param qos - the MQTT QoS to subscribe at + * @param mh - the callback function to be invoked when a message is received for this subscription + * @return success code - + */ + int subscribe(const char* topicFilter, enum QoS qos, messageHandler mh); + + /** MQTT Unsubscribe - send an MQTT unsubscribe packet and wait for the unsuback + * @param topicFilter - a topic pattern which can include wildcards + * @return success code - + */ + int unsubscribe(const char* topicFilter); + + /** MQTT Disconnect - send an MQTT disconnect packet, and clean up any state + * @return success code - + */ + int disconnect(); + + /** A call to this API must be made within the keepAlive interval to keep the MQTT connection alive + * yield can be called if no other MQTT operation is needed. This will also allow messages to be + * received. + * @param timeout_ms the time to wait, in milliseconds + * @return success code - on failure, this means the client has disconnected + */ + int yield(unsigned long timeout_ms = 1000L); + + /** Is the client connected? + * @return flag - is the client connected or not? + */ + bool isConnected() + { + return isconnected; + } + +private: + + int cycle(Timer& timer); + int waitfor(int packet_type, Timer& timer); + int keepalive(); + int publish(int len, Timer& timer, enum QoS qos); + + int decodePacket(int* value, int timeout); + int readPacket(Timer& timer); + int sendPacket(int length, Timer& timer); + int deliverMessage(MQTTString& topicName, Message& message); + bool isTopicMatched(char* topicFilter, MQTTString& topicName); + + Network& ipstack; + unsigned long command_timeout_ms; + + unsigned char sendbuf[MAX_MQTT_PACKET_SIZE]; + unsigned char readbuf[MAX_MQTT_PACKET_SIZE]; + + Timer last_sent, last_received; + unsigned int keepAliveInterval; + bool ping_outstanding; + bool cleansession; + + PacketId packetid; + + struct MessageHandlers + { + const char* topicFilter; + FP fp; + } messageHandlers[MAX_MESSAGE_HANDLERS]; // Message handlers are indexed by subscription topic + + + bool isconnected; + +#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 + unsigned char pubbuf[MAX_MQTT_PACKET_SIZE]; // store the last publish for sending on reconnect + int inflightLen; + unsigned short inflightMsgid; + enum QoS inflightQoS; +#endif + +#if MQTTCLIENT_QOS2 + bool pubrel; + #if !defined(MAX_INCOMING_QOS2_MESSAGES) + #define MAX_INCOMING_QOS2_MESSAGES 10 + #endif + unsigned short incomingQoS2messages[MAX_INCOMING_QOS2_MESSAGES]; + bool isQoS2msgidFree(unsigned short id); + bool useQoS2msgid(unsigned short id); + void freeQoS2msgid(unsigned short id); +#endif + +}; + +} + + +template +MQTT::Client::Client(Network& network, unsigned int command_timeout_ms) : ipstack(network), packetid() +{ + last_sent = Timer(); + last_received = Timer(); + ping_outstanding = false; + for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) + messageHandlers[i].topicFilter = 0; + this->command_timeout_ms = command_timeout_ms; + isconnected = false; + +#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 + inflightMsgid = 0; + inflightQoS = QOS0; +#endif + + +#if MQTTCLIENT_QOS2 + pubrel = false; + for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) + incomingQoS2messages[i] = 0; +#endif +} + +#if MQTTCLIENT_QOS2 +template +bool MQTT::Client::isQoS2msgidFree(unsigned short id) +{ + for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) + { + if (incomingQoS2messages[i] == id) + return false; + } + return true; +} + + +template +bool MQTT::Client::useQoS2msgid(unsigned short id) +{ + for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) + { + if (incomingQoS2messages[i] == 0) + { + incomingQoS2messages[i] = id; + return true; + } + } + return false; +} + + +template +void MQTT::Client::freeQoS2msgid(unsigned short id) +{ + for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) + { + if (incomingQoS2messages[i] == id) + { + incomingQoS2messages[i] = 0; + return; + } + } +} +#endif + + +template +int MQTT::Client::sendPacket(int length, Timer& timer) +{ + int rc = FAILURE, + sent = 0; + + while (sent < length && !timer.expired()) + { + rc = ipstack.write(&sendbuf[sent], length, timer.left_ms()); + if (rc < 0) // there was an error writing the data + break; + sent += rc; + } + if (sent == length) + { + if (this->keepAliveInterval > 0) + last_sent.countdown(this->keepAliveInterval); // record the fact that we have successfully sent the packet + rc = SUCCESS; + } + else + rc = FAILURE; + +#if defined(MQTT_DEBUG) + char printbuf[150]; + DEBUG("Rc %d from sending packet %s\n", rc, MQTTFormat_toServerString(printbuf, sizeof(printbuf), sendbuf, length)); +#endif + return rc; +} + + +template +int MQTT::Client::decodePacket(int* value, int timeout) +{ + unsigned char c; + int multiplier = 1; + int len = 0; + const int MAX_NO_OF_REMAINING_LENGTH_BYTES = 4; + + *value = 0; + do + { + int rc = MQTTPACKET_READ_ERROR; + + if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) + { + rc = MQTTPACKET_READ_ERROR; /* bad data */ + goto exit; + } + rc = ipstack.read(&c, 1, timeout); + if (rc != 1) + goto exit; + *value += (c & 127) * multiplier; + multiplier *= 128; + } while ((c & 128) != 0); +exit: + return len; +} + + +/** + * If any read fails in this method, then we should disconnect from the network, as on reconnect + * the packets can be retried. + * @param timeout the max time to wait for the packet read to complete, in milliseconds + * @return the MQTT packet type, or -1 if none + */ +template +int MQTT::Client::readPacket(Timer& timer) +{ + int rc = FAILURE; + MQTTHeader header = {0}; + int len = 0; + int rem_len = 0; + int read = 0; + + /* 1. read the header byte. This has the packet type in it */ + if (ipstack.read(readbuf, 1, timer.left_ms()) != 1) + goto exit; + + len = 1; + /* 2. read the remaining length. This is variable in itself */ + decodePacket(&rem_len, timer.left_ms()); + len += MQTTPacket_encode(readbuf + 1, rem_len); /* put the original remaining length into the buffer */ + + if (rem_len > (MAX_MQTT_PACKET_SIZE - len)) + { + rc = BUFFER_OVERFLOW; + goto exit; + } + + /* 3. read the rest of the buffer using a callback to supply the rest of the data */ + if (rem_len > 0 && (ipstack.read(readbuf + len, rem_len, timer.left_ms()) != rem_len)) + goto exit; + + header.byte = readbuf[0]; + rc = header.bits.type; + if (this->keepAliveInterval > 0) + last_received.countdown(this->keepAliveInterval); // record the fact that we have successfully received a packet +exit: + +#if defined(MQTT_DEBUG) + if (rc >= 0) + { + char printbuf[50]; + DEBUG("Rc %d from receiving packet %s\n", rc, MQTTFormat_toClientString(printbuf, sizeof(printbuf), readbuf, len)); + } +#endif + return rc; +} + + +// assume topic filter and name is in correct format +// # can only be at end +// + and # can only be next to separator +template +bool MQTT::Client::isTopicMatched(char* topicFilter, MQTTString& topicName) +{ + char* curf = topicFilter; + char* curn = topicName.lenstring.data; + char* curn_end = curn + topicName.lenstring.len; + + while (*curf && curn < curn_end) + { + if (*curn == '/' && *curf != '/') + break; + if (*curf != '+' && *curf != '#' && *curf != *curn) + break; + if (*curf == '+') + { // skip until we meet the next separator, or end of string + char* nextpos = curn + 1; + while (nextpos < curn_end && *nextpos != '/') + nextpos = ++curn + 1; + } + else if (*curf == '#') + curn = curn_end - 1; // skip until end of string + curf++; + curn++; + }; + + return (curn == curn_end) && (*curf == '\0'); +} + + + +template +int MQTT::Client::deliverMessage(MQTTString& topicName, Message& message) +{ + int rc = FAILURE; + + // we have to find the right message handler - indexed by topic + for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) + { + if (messageHandlers[i].topicFilter != 0 && (MQTTPacket_equals(&topicName, (char*)messageHandlers[i].topicFilter) || + isTopicMatched((char*)messageHandlers[i].topicFilter, topicName))) + { + if (messageHandlers[i].fp.attached()) + { + MessageData md(topicName, message); + messageHandlers[i].fp(md); + rc = SUCCESS; + } + } + } + + if (rc == FAILURE && defaultMessageHandler.attached()) + { + MessageData md(topicName, message); + defaultMessageHandler(md); + rc = SUCCESS; + } + + return rc; +} + + + +template +int MQTT::Client::yield(unsigned long timeout_ms) +{ + int rc = SUCCESS; + Timer timer = Timer(); + + timer.countdown_ms(timeout_ms); + while (!timer.expired()) + { + if (cycle(timer) < 0) + { + rc = FAILURE; + break; + } + } + + return rc; +} + + +template +int MQTT::Client::cycle(Timer& timer) +{ + /* get one piece of work off the wire and one pass through */ + + // read the socket, see what work is due + int packet_type = readPacket(timer); + + int len = 0, + rc = SUCCESS; + + switch (packet_type) + { + case FAILURE: + case BUFFER_OVERFLOW: + rc = packet_type; + break; + case CONNACK: + case PUBACK: + case SUBACK: + break; + case PUBLISH: + { + MQTTString topicName = MQTTString_initializer; + Message msg; + if (MQTTDeserialize_publish((unsigned char*)&msg.dup, (int*)&msg.qos, (unsigned char*)&msg.retained, (unsigned short*)&msg.id, &topicName, + (unsigned char**)&msg.payload, (int*)&msg.payloadlen, readbuf, MAX_MQTT_PACKET_SIZE) != 1) + goto exit; +#if MQTTCLIENT_QOS2 + if (msg.qos != QOS2) +#endif + deliverMessage(topicName, msg); +#if MQTTCLIENT_QOS2 + else if (isQoS2msgidFree(msg.id)) + { + if (useQoS2msgid(msg.id)) + deliverMessage(topicName, msg); + else + WARN("Maximum number of incoming QoS2 messages exceeded"); + } +#endif +#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 + if (msg.qos != QOS0) + { + if (msg.qos == QOS1) + len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBACK, 0, msg.id); + else if (msg.qos == QOS2) + len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREC, 0, msg.id); + if (len <= 0) + rc = FAILURE; + else + rc = sendPacket(len, timer); + if (rc == FAILURE) + goto exit; // there was a problem + } + break; +#endif + } +#if MQTTCLIENT_QOS2 + case PUBREC: + case PUBREL: + unsigned short mypacketid; + unsigned char dup, type; + if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1) + rc = FAILURE; + else if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, + (packet_type == PUBREC) ? PUBREL : PUBCOMP, 0, mypacketid)) <= 0) + rc = FAILURE; + else if ((rc = sendPacket(len, timer)) != SUCCESS) // send the PUBREL packet + rc = FAILURE; // there was a problem + if (rc == FAILURE) + goto exit; // there was a problem + if (packet_type == PUBREL) + freeQoS2msgid(mypacketid); + break; + + case PUBCOMP: + break; +#endif + case PINGRESP: + ping_outstanding = false; + break; + } + keepalive(); +exit: + if (rc == SUCCESS) + rc = packet_type; + return rc; +} + + +template +int MQTT::Client::keepalive() +{ + int rc = FAILURE; + + if (keepAliveInterval == 0) + { + rc = SUCCESS; + goto exit; + } + + if (last_sent.expired() || last_received.expired()) + { + if (!ping_outstanding) + { + Timer timer = Timer(1000); + int len = MQTTSerialize_pingreq(sendbuf, MAX_MQTT_PACKET_SIZE); + if (len > 0 && (rc = sendPacket(len, timer)) == SUCCESS) // send the ping packet + ping_outstanding = true; + } + } + +exit: + return rc; +} + + +// only used in single-threaded mode where one command at a time is in process +template +int MQTT::Client::waitfor(int packet_type, Timer& timer) +{ + int rc = FAILURE; + + do + { + if (timer.expired()) + break; // we timed out + } + while ((rc = cycle(timer)) != packet_type); + + return rc; +} + + +template +int MQTT::Client::connect(MQTTPacket_connectData& options) +{ + Timer connect_timer = Timer(command_timeout_ms); + int rc = FAILURE; + int len = 0; + + if (isconnected) // don't send connect packet again if we are already connected + goto exit; + + this->keepAliveInterval = options.keepAliveInterval; + this->cleansession = options.cleansession; + if ((len = MQTTSerialize_connect(sendbuf, MAX_MQTT_PACKET_SIZE, &options)) <= 0) + goto exit; + if ((rc = sendPacket(len, connect_timer)) != SUCCESS) // send the connect packet + goto exit; // there was a problem + + if (this->keepAliveInterval > 0) + last_received.countdown(this->keepAliveInterval); + // this will be a blocking call, wait for the connack + if (waitfor(CONNACK, connect_timer) == CONNACK) + { + unsigned char connack_rc = 255; + bool sessionPresent = false; + if (MQTTDeserialize_connack((unsigned char*)&sessionPresent, &connack_rc, readbuf, MAX_MQTT_PACKET_SIZE) == 1) + rc = connack_rc; + else + rc = FAILURE; + } + else + rc = FAILURE; + +#if MQTTCLIENT_QOS2 + // resend an inflight publish + if (inflightMsgid >0 && inflightQoS == QOS2 && pubrel) + { + if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREL, 0, inflightMsgid)) <= 0) + rc = FAILURE; + else + rc = publish(len, connect_timer, inflightQoS); + } + else +#endif +#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 + if (inflightMsgid > 0) + { + memcpy(sendbuf, pubbuf, MAX_MQTT_PACKET_SIZE); + rc = publish(inflightLen, connect_timer, inflightQoS); + } +#endif + +exit: + if (rc == SUCCESS) + isconnected = true; + return rc; +} + + +template +int MQTT::Client::connect() +{ + MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer; + return connect(default_options); +} + + +template +int MQTT::Client::subscribe(const char* topicFilter, enum QoS qos, messageHandler messageHandler) +{ + int rc = FAILURE; + Timer timer = Timer(command_timeout_ms); + int len = 0; + MQTTString topic = {(char*)topicFilter, 0, 0}; + + if (!isconnected) + goto exit; + + len = MQTTSerialize_subscribe(sendbuf, MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic, (int*)&qos); + if (len <= 0) + goto exit; + if ((rc = sendPacket(len, timer)) != SUCCESS) // send the subscribe packet + goto exit; // there was a problem + + if (waitfor(SUBACK, timer) == SUBACK) // wait for suback + { + int count = 0, grantedQoS = -1; + unsigned short mypacketid; + if (MQTTDeserialize_suback(&mypacketid, 1, &count, &grantedQoS, readbuf, MAX_MQTT_PACKET_SIZE) == 1) + rc = grantedQoS; // 0, 1, 2 or 0x80 + if (rc != 0x80) + { + for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) + { + if (messageHandlers[i].topicFilter == 0) + { + messageHandlers[i].topicFilter = topicFilter; + messageHandlers[i].fp.attach(messageHandler); + rc = 0; + break; + } + } + } + } + else + rc = FAILURE; + +exit: + if (rc != SUCCESS) + isconnected = false; + return rc; +} + + +template +int MQTT::Client::unsubscribe(const char* topicFilter) +{ + int rc = FAILURE; + Timer timer = Timer(command_timeout_ms); + MQTTString topic = {(char*)topicFilter, 0, 0}; + int len = 0; + + if (!isconnected) + goto exit; + + if ((len = MQTTSerialize_unsubscribe(sendbuf, MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic)) <= 0) + goto exit; + if ((rc = sendPacket(len, timer)) != SUCCESS) // send the unsubscribe packet + goto exit; // there was a problem + + if (waitfor(UNSUBACK, timer) == UNSUBACK) + { + unsigned short mypacketid; // should be the same as the packetid above + if (MQTTDeserialize_unsuback(&mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) == 1) + rc = 0; + } + else + rc = FAILURE; + +exit: + if (rc != SUCCESS) + isconnected = false; + return rc; +} + + +template +int MQTT::Client::publish(int len, Timer& timer, enum QoS qos) +{ + int rc; + + if ((rc = sendPacket(len, timer)) != SUCCESS) // send the publish packet + goto exit; // there was a problem + +#if MQTTCLIENT_QOS1 + if (qos == QOS1) + { + if (waitfor(PUBACK, timer) == PUBACK) + { + unsigned short mypacketid; + unsigned char dup, type; + if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1) + rc = FAILURE; + else if (inflightMsgid == mypacketid) + inflightMsgid = 0; + } + else + rc = FAILURE; + } +#elif MQTTCLIENT_QOS2 + else if (qos == QOS2) + { + if (waitfor(PUBCOMP, timer) == PUBCOMP) + { + unsigned short mypacketid; + unsigned char dup, type; + if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1) + rc = FAILURE; + else if (inflightMsgid == mypacketid) + inflightMsgid = 0; + } + else + rc = FAILURE; + } +#endif + +exit: + if (rc != SUCCESS) + isconnected = false; + return rc; +} + + + +template +int MQTT::Client::publish(const char* topicName, void* payload, size_t payloadlen, unsigned short& id, enum QoS qos, bool retained) +{ + int rc = FAILURE; + Timer timer = Timer(command_timeout_ms); + MQTTString topicString = MQTTString_initializer; + int len = 0; + + if (!isconnected) + goto exit; + + topicString.cstring = (char*)topicName; + +#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 + if (qos == QOS1 || qos == QOS2) + id = packetid.getNext(); +#endif + + len = MQTTSerialize_publish(sendbuf, MAX_MQTT_PACKET_SIZE, 0, qos, retained, id, + topicString, (unsigned char*)payload, payloadlen); + if (len <= 0) + goto exit; + +#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 + if (!cleansession) + { + memcpy(pubbuf, sendbuf, len); + inflightMsgid = id; + inflightLen = len; + inflightQoS = qos; +#if MQTTCLIENT_QOS2 + pubrel = false; +#endif + } +#endif + + rc = publish(len, timer, qos); +exit: + return rc; +} + + +template +int MQTT::Client::publish(const char* topicName, void* payload, size_t payloadlen, enum QoS qos, bool retained) +{ + unsigned short id = 0; // dummy - not used for anything + return publish(topicName, payload, payloadlen, id, qos, retained); +} + + +template +int MQTT::Client::publish(const char* topicName, Message& message) +{ + return publish(topicName, message.payload, message.payloadlen, message.qos, message.retained); +} + + +template +int MQTT::Client::disconnect() +{ + int rc = FAILURE; + Timer timer = Timer(command_timeout_ms); // we might wait for incomplete incoming publishes to complete + int len = MQTTSerialize_disconnect(sendbuf, MAX_MQTT_PACKET_SIZE); + if (len > 0) + rc = sendPacket(len, timer); // send the disconnect packet + + isconnected = false; + return rc; +} + + +#endif diff --git a/lib/mqtt/MQTTConnect.h b/lib/mqtt/MQTTConnect.h new file mode 100644 index 000000000..db9025149 --- /dev/null +++ b/lib/mqtt/MQTTConnect.h @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +#ifndef MQTTCONNECT_H_ +#define MQTTCONNECT_H_ + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + + +typedef union +{ + unsigned char all; /**< all connect flags */ +#if defined(REVERSED) + struct + { + unsigned int username : 1; /**< 3.1 user name */ + unsigned int password : 1; /**< 3.1 password */ + unsigned int willRetain : 1; /**< will retain setting */ + unsigned int willQoS : 2; /**< will QoS value */ + unsigned int will : 1; /**< will flag */ + unsigned int cleansession : 1; /**< clean session flag */ + unsigned int : 1; /**< unused */ + } bits; +#else + struct + { + unsigned int : 1; /**< unused */ + unsigned int cleansession : 1; /**< cleansession flag */ + unsigned int will : 1; /**< will flag */ + unsigned int willQoS : 2; /**< will QoS value */ + unsigned int willRetain : 1; /**< will retain setting */ + unsigned int password : 1; /**< 3.1 password */ + unsigned int username : 1; /**< 3.1 user name */ + } bits; +#endif +} MQTTConnectFlags; /**< connect flags byte */ + + + +/** + * Defines the MQTT "Last Will and Testament" (LWT) settings for + * the connect packet. + */ +typedef struct +{ + /** The eyecatcher for this structure. must be MQTW. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 */ + int struct_version; + /** The LWT topic to which the LWT message will be published. */ + MQTTString topicName; + /** The LWT payload. */ + MQTTString message; + /** + * The retained flag for the LWT message (see MQTTAsync_message.retained). + */ + unsigned char retained; + /** + * The quality of service setting for the LWT message (see + * MQTTAsync_message.qos and @ref qos). + */ + char qos; +} MQTTPacket_willOptions; + + +#define MQTTPacket_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 0, {NULL, {0, NULL}}, {NULL, {0, NULL}}, 0, 0 } + + +typedef struct +{ + /** The eyecatcher for this structure. must be MQTC. */ + char struct_id[4]; + /** The version number of this structure. Must be 0 */ + int struct_version; + /** Version of MQTT to be used. 3 = 3.1 4 = 3.1.1 + */ + unsigned char MQTTVersion; + MQTTString clientID; + unsigned short keepAliveInterval; + unsigned char cleansession; + unsigned char willFlag; + MQTTPacket_willOptions will; + MQTTString username; + MQTTString password; +} MQTTPacket_connectData; + +typedef union +{ + unsigned char all; /**< all connack flags */ +#if defined(REVERSED) + struct + { + unsigned int sessionpresent : 1; /**< session present flag */ + unsigned int : 7; /**< unused */ + } bits; +#else + struct + { + unsigned int : 7; /**< unused */ + unsigned int sessionpresent : 1; /**< session present flag */ + } bits; +#endif +} MQTTConnackFlags; /**< connack flags byte */ + +#define MQTTPacket_connectData_initializer { {'M', 'Q', 'T', 'C'}, 0, 4, {NULL, {0, NULL}}, 60, 1, 0, \ + MQTTPacket_willOptions_initializer, {NULL, {0, NULL}}, {NULL, {0, NULL}} } + +DLLExport int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options); +DLLExport int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len); + +DLLExport int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent); +DLLExport int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen); + +DLLExport int MQTTSerialize_disconnect(unsigned char* buf, int buflen); +DLLExport int MQTTSerialize_pingreq(unsigned char* buf, int buflen); + +#endif /* MQTTCONNECT_H_ */ diff --git a/lib/mqtt/MQTTConnectClient.c b/lib/mqtt/MQTTConnectClient.c new file mode 100644 index 000000000..8a9bc902e --- /dev/null +++ b/lib/mqtt/MQTTConnectClient.c @@ -0,0 +1,206 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#include "MQTTPacket.h" + +#include + +/** + * Determines the length of the MQTT connect packet that would be produced using the supplied connect options. + * @param options the options to be used to build the connect packet + * @return the length of buffer needed to contain the serialized version of the packet + */ +int MQTTSerialize_connectLength(MQTTPacket_connectData* options) +{ + int len = 0; + + + if (options->MQTTVersion == 3) + len = 12; /* variable depending on MQTT or MQIsdp */ + else if (options->MQTTVersion == 4) + len = 10; + + len += MQTTstrlen(options->clientID)+2; + if (options->willFlag) + len += MQTTstrlen(options->will.topicName)+2 + MQTTstrlen(options->will.message)+2; + if (options->username.cstring || options->username.lenstring.data) + len += MQTTstrlen(options->username)+2; + if (options->password.cstring || options->password.lenstring.data) + len += MQTTstrlen(options->password)+2; + + return len; +} + + +/** + * Serializes the connect options into the buffer. + * @param buf the buffer into which the packet will be serialized + * @param len the length in bytes of the supplied buffer + * @param options the options to be used to build the connect packet + * @return serialized length, or error if 0 + */ +int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options) +{ + unsigned char *ptr = buf; + MQTTHeader header = {0}; + MQTTConnectFlags flags = {0}; + int len = 0; + int rc = -1; + + if (MQTTPacket_len(len = MQTTSerialize_connectLength(options)) > buflen) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + + header.byte = 0; + header.bits.type = CONNECT; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, len); /* write remaining length */ + + if (options->MQTTVersion == 4) + { + writeCString(&ptr, "MQTT"); + writeChar(&ptr, (char) 4); + } + else + { + writeCString(&ptr, "MQIsdp"); + writeChar(&ptr, (char) 3); + } + + flags.all = 0; + flags.bits.cleansession = options->cleansession; + flags.bits.will = (options->willFlag) ? 1 : 0; + if (flags.bits.will) + { + flags.bits.willQoS = options->will.qos; + flags.bits.willRetain = options->will.retained; + } + + if (options->username.cstring || options->username.lenstring.data) + flags.bits.username = 1; + if (options->password.cstring || options->password.lenstring.data) + flags.bits.password = 1; + + writeChar(&ptr, flags.all); + writeInt(&ptr, options->keepAliveInterval); + writeMQTTString(&ptr, options->clientID); + if (options->willFlag) + { + writeMQTTString(&ptr, options->will.topicName); + writeMQTTString(&ptr, options->will.message); + } + if (flags.bits.username) + writeMQTTString(&ptr, options->username); + if (flags.bits.password) + writeMQTTString(&ptr, options->password); + + rc = ptr - buf; + + exit: + return rc; +} + + +/** + * Deserializes the supplied (wire) buffer into connack data - return code + * @param sessionPresent the session present flag returned (only for MQTT 3.1.1) + * @param connack_rc returned integer value of the connack return code + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param len the length in bytes of the data in the supplied buffer + * @return error code. 1 is success, 0 is failure + */ +int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen; + MQTTConnackFlags flags = {0}; + + header.byte = readChar(&curdata); + if (header.bits.type != CONNACK) + goto exit; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + if (enddata - curdata < 2) + goto exit; + + flags.all = readChar(&curdata); + *sessionPresent = flags.bits.sessionpresent; + *connack_rc = readChar(&curdata); + + rc = 1; +exit: + return rc; +} + + +/** + * Serializes a 0-length packet into the supplied buffer, ready for writing to a socket + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer, to avoid overruns + * @param packettype the message type + * @return serialized length, or error if 0 + */ +int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype) +{ + MQTTHeader header = {0}; + int rc = -1; + unsigned char *ptr = buf; + + if (buflen < 2) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + header.byte = 0; + header.bits.type = packettype; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, 0); /* write remaining length */ + rc = ptr - buf; +exit: + return rc; +} + + +/** + * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer, to avoid overruns + * @return serialized length, or error if 0 + */ +int MQTTSerialize_disconnect(unsigned char* buf, int buflen) +{ + return MQTTSerialize_zero(buf, buflen, DISCONNECT); +} + + +/** + * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer, to avoid overruns + * @return serialized length, or error if 0 + */ +int MQTTSerialize_pingreq(unsigned char* buf, int buflen) +{ + return MQTTSerialize_zero(buf, buflen, PINGREQ); +} diff --git a/lib/mqtt/MQTTDeserializePublish.c b/lib/mqtt/MQTTDeserializePublish.c new file mode 100644 index 000000000..59aff4dda --- /dev/null +++ b/lib/mqtt/MQTTDeserializePublish.c @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#include "MQTTPacket.h" +#include + +#define min(a, b) ((a < b) ? 1 : 0) + +/** + * Deserializes the supplied (wire) buffer into publish data + * @param dup returned integer - the MQTT dup flag + * @param qos returned integer - the MQTT QoS value + * @param retained returned integer - the MQTT retained flag + * @param packetid returned integer - the MQTT packet identifier + * @param topicName returned MQTTString - the MQTT topic in the publish + * @param payload returned byte buffer - the MQTT publish payload + * @param payloadlen returned integer - the length of the MQTT payload + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return error code. 1 is success + */ +int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName, + unsigned char** payload, int* payloadlen, unsigned char* buf, int buflen) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen = 0; + + header.byte = readChar(&curdata); + if (header.bits.type != PUBLISH) + goto exit; + *dup = header.bits.dup; + *qos = header.bits.qos; + *retained = header.bits.retain; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + + if (!readMQTTLenString(topicName, &curdata, enddata) || + enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */ + goto exit; + + if (*qos > 0) + *packetid = readInt(&curdata); + + *payloadlen = enddata - curdata; + *payload = curdata; + rc = 1; +exit: + return rc; +} + + + +/** + * Deserializes the supplied (wire) buffer into an ack + * @param packettype returned integer - the MQTT packet type + * @param dup returned integer - the MQTT dup flag + * @param packetid returned integer - the MQTT packet identifier + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return error code. 1 is success, 0 is failure + */ +int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen; + + header.byte = readChar(&curdata); + *dup = header.bits.dup; + *packettype = header.bits.type; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + + if (enddata - curdata < 2) + goto exit; + *packetid = readInt(&curdata); + + rc = 1; +exit: + return rc; +} + diff --git a/lib/mqtt/MQTTPacket.c b/lib/mqtt/MQTTPacket.c new file mode 100644 index 000000000..2d5c8b732 --- /dev/null +++ b/lib/mqtt/MQTTPacket.c @@ -0,0 +1,401 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Sergio R. Caprile - non-blocking packet read functions for stream transport + *******************************************************************************/ + +#include "MQTTPacket.h" + +#include + +/** + * Encodes the message length according to the MQTT algorithm + * @param buf the buffer into which the encoded data is written + * @param length the length to be encoded + * @return the number of bytes written to buffer + */ +int MQTTPacket_encode(unsigned char* buf, int length) +{ + int rc = 0; + + do + { + char d = length % 128; + length /= 128; + /* if there are more digits to encode, set the top bit of this digit */ + if (length > 0) + d |= 0x80; + buf[rc++] = d; + } while (length > 0); + return rc; +} + + +/** + * Decodes the message length according to the MQTT algorithm + * @param getcharfn pointer to function to read the next character from the data source + * @param value the decoded length returned + * @return the number of bytes read from the socket + */ +int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value) +{ + unsigned char c; + int multiplier = 1; + int len = 0; +#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4 + + *value = 0; + do + { + int rc = MQTTPACKET_READ_ERROR; + + if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) + { + rc = MQTTPACKET_READ_ERROR; /* bad data */ + goto exit; + } + rc = (*getcharfn)(&c, 1); + if (rc != 1) + goto exit; + *value += (c & 127) * multiplier; + multiplier *= 128; + } while ((c & 128) != 0); +exit: + return len; +} + + +int MQTTPacket_len(int rem_len) +{ + rem_len += 1; /* header byte */ + + /* now remaining_length field */ + if (rem_len < 128) + rem_len += 1; + else if (rem_len < 16384) + rem_len += 2; + else if (rem_len < 2097151) + rem_len += 3; + else + rem_len += 4; + return rem_len; +} + + +static unsigned char* bufptr; + +int bufchar(unsigned char* c, int count) +{ + int i; + + for (i = 0; i < count; ++i) + *c = *bufptr++; + return count; +} + + +int MQTTPacket_decodeBuf(unsigned char* buf, int* value) +{ + bufptr = buf; + return MQTTPacket_decode(bufchar, value); +} + + +/** + * Calculates an integer from two bytes read from the input buffer + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @return the integer value calculated + */ +int readInt(unsigned char** pptr) +{ + unsigned char* ptr = *pptr; + int len = 256*(*ptr) + (*(ptr+1)); + *pptr += 2; + return len; +} + + +/** + * Reads one character from the input buffer. + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @return the character read + */ +char readChar(unsigned char** pptr) +{ + char c = **pptr; + (*pptr)++; + return c; +} + + +/** + * Writes one character to an output buffer. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param c the character to write + */ +void writeChar(unsigned char** pptr, char c) +{ + **pptr = c; + (*pptr)++; +} + + +/** + * Writes an integer as 2 bytes to an output buffer. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param anInt the integer to write + */ +void writeInt(unsigned char** pptr, int anInt) +{ + **pptr = (unsigned char)(anInt / 256); + (*pptr)++; + **pptr = (unsigned char)(anInt % 256); + (*pptr)++; +} + + +/** + * Writes a "UTF" string to an output buffer. Converts C string to length-delimited. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param string the C string to write + */ +void writeCString(unsigned char** pptr, const char* string) +{ + int len = strlen(string); + writeInt(pptr, len); + memcpy(*pptr, string, len); + *pptr += len; +} + + +int getLenStringLen(char* ptr) +{ + int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1)); + return len; +} + + +void writeMQTTString(unsigned char** pptr, MQTTString mqttstring) +{ + if (mqttstring.lenstring.len > 0) + { + writeInt(pptr, mqttstring.lenstring.len); + memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len); + *pptr += mqttstring.lenstring.len; + } + else if (mqttstring.cstring) + writeCString(pptr, mqttstring.cstring); + else + writeInt(pptr, 0); +} + + +/** + * @param mqttstring the MQTTString structure into which the data is to be read + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param enddata pointer to the end of the data: do not read beyond + * @return 1 if successful, 0 if not + */ +int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata) +{ + int rc = 0; + + /* the first two bytes are the length of the string */ + if (enddata - (*pptr) > 1) /* enough length to read the integer? */ + { + mqttstring->lenstring.len = readInt(pptr); /* increments pptr to point past length */ + if (&(*pptr)[mqttstring->lenstring.len] <= enddata) + { + mqttstring->lenstring.data = (char*)*pptr; + *pptr += mqttstring->lenstring.len; + rc = 1; + } + } + mqttstring->cstring = NULL; + return rc; +} + + +/** + * Return the length of the MQTTstring - C string if there is one, otherwise the length delimited string + * @param mqttstring the string to return the length of + * @return the length of the string + */ +int MQTTstrlen(MQTTString mqttstring) +{ + int rc = 0; + + if (mqttstring.cstring) + rc = strlen(mqttstring.cstring); + else + rc = mqttstring.lenstring.len; + return rc; +} + + +/** + * Compares an MQTTString to a C string + * @param a the MQTTString to compare + * @param bptr the C string to compare + * @return boolean - equal or not + */ +int MQTTPacket_equals(MQTTString* a, char* bptr) +{ + int alen = 0, + blen = 0; + char *aptr; + + if (a->cstring) + { + aptr = a->cstring; + alen = strlen(a->cstring); + } + else + { + aptr = a->lenstring.data; + alen = a->lenstring.len; + } + blen = strlen(bptr); + + return (alen == blen) && (strncmp(aptr, bptr, alen) == 0); +} + + +/** + * Helper function to read packet data from some source into a buffer + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param getfn pointer to a function which will read any number of bytes from the needed source + * @return integer MQTT packet type, or -1 on error + * @note the whole message must fit into the caller's buffer + */ +int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int)) +{ + int rc = -1; + MQTTHeader header = {0}; + int len = 0; + int rem_len = 0; + + /* 1. read the header byte. This has the packet type in it */ + if ((*getfn)(buf, 1) != 1) + goto exit; + + len = 1; + /* 2. read the remaining length. This is variable in itself */ + MQTTPacket_decode(getfn, &rem_len); + len += MQTTPacket_encode(buf + 1, rem_len); /* put the original remaining length back into the buffer */ + + /* 3. read the rest of the buffer using a callback to supply the rest of the data */ + if((rem_len + len) > buflen) + goto exit; + if ((*getfn)(buf + len, rem_len) != rem_len) + goto exit; + + header.byte = buf[0]; + rc = header.bits.type; +exit: + return rc; +} + +/** + * Decodes the message length according to the MQTT algorithm, non-blocking + * @param trp pointer to a transport structure holding what is needed to solve getting data from it + * @param value the decoded length returned + * @return integer the number of bytes read from the socket, 0 for call again, or -1 on error + */ +static int MQTTPacket_decodenb(MQTTTransport *trp) +{ + unsigned char c; + int rc = MQTTPACKET_READ_ERROR; + + if(trp->len == 0){ /* initialize on first call */ + trp->multiplier = 1; + trp->rem_len = 0; + } + do { + int frc; + if (++(trp->len) > MAX_NO_OF_REMAINING_LENGTH_BYTES) + goto exit; + if ((frc=(*trp->getfn)(trp->sck, &c, 1)) == -1) + goto exit; + if (frc == 0){ + rc = 0; + goto exit; + } + trp->rem_len += (c & 127) * trp->multiplier; + trp->multiplier *= 128; + } while ((c & 128) != 0); + rc = trp->len; +exit: + return rc; +} + +/** + * Helper function to read packet data from some source into a buffer, non-blocking + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param trp pointer to a transport structure holding what is needed to solve getting data from it + * @return integer MQTT packet type, 0 for call again, or -1 on error + * @note the whole message must fit into the caller's buffer + */ +int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp) +{ + int rc = -1, frc; + MQTTHeader header = {0}; + + switch(trp->state){ + default: + trp->state = 0; + /*FALLTHROUGH*/ + case 0: + /* read the header byte. This has the packet type in it */ + if ((frc=(*trp->getfn)(trp->sck, buf, 1)) == -1) + goto exit; + if (frc == 0) + return 0; + trp->len = 0; + ++trp->state; + /*FALLTHROUGH*/ + /* read the remaining length. This is variable in itself */ + case 1: + if((frc=MQTTPacket_decodenb(trp)) == MQTTPACKET_READ_ERROR) + goto exit; + if(frc == 0) + return 0; + trp->len = 1 + MQTTPacket_encode(buf + 1, trp->rem_len); /* put the original remaining length back into the buffer */ + if((trp->rem_len + trp->len) > buflen) + goto exit; + ++trp->state; + /*FALLTHROUGH*/ + case 2: + /* read the rest of the buffer using a callback to supply the rest of the data */ + if ((frc=(*trp->getfn)(trp->sck, buf + trp->len, trp->rem_len)) == -1) + goto exit; + if (frc == 0) + return 0; + trp->rem_len -= frc; + trp->len += frc; + if(trp->rem_len) + return 0; + + header.byte = buf[0]; + rc = header.bits.type; + break; + } + +exit: + trp->state = 0; + return rc; +} + diff --git a/lib/mqtt/MQTTPacket.h b/lib/mqtt/MQTTPacket.h new file mode 100644 index 000000000..6ed32d336 --- /dev/null +++ b/lib/mqtt/MQTTPacket.h @@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +#ifndef MQTTPACKET_H_ +#define MQTTPACKET_H_ + +#if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */ +extern "C" { +#endif + +#if defined(WIN32_DLL) || defined(WIN64_DLL) + #define DLLImport __declspec(dllimport) + #define DLLExport __declspec(dllexport) +#elif defined(LINUX_SO) + #define DLLImport extern + #define DLLExport __attribute__ ((visibility ("default"))) +#else + #define DLLImport + #define DLLExport +#endif + +enum errors +{ + MQTTPACKET_BUFFER_TOO_SHORT = -2, + MQTTPACKET_READ_ERROR = -1, + MQTTPACKET_READ_COMPLETE +}; + +enum msgTypes +{ + CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL, + PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK, + PINGREQ, PINGRESP, DISCONNECT +}; + +/** + * Bitfields for the MQTT header byte. + */ +typedef union +{ + unsigned char byte; /**< the whole byte */ +#if defined(REVERSED) + struct + { + unsigned int type : 4; /**< message type nibble */ + unsigned int dup : 1; /**< DUP flag bit */ + unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ + unsigned int retain : 1; /**< retained flag bit */ + } bits; +#else + struct + { + unsigned int retain : 1; /**< retained flag bit */ + unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ + unsigned int dup : 1; /**< DUP flag bit */ + unsigned int type : 4; /**< message type nibble */ + } bits; +#endif +} MQTTHeader; + +typedef struct +{ + int len; + char* data; +} MQTTLenString; + +typedef struct +{ + char* cstring; + MQTTLenString lenstring; +} MQTTString; + +#define MQTTString_initializer {NULL, {0, NULL}} + +int MQTTstrlen(MQTTString mqttstring); + +#include "MQTTConnect.h" +#include "MQTTPublish.h" +#include "MQTTSubscribe.h" +#include "MQTTUnsubscribe.h" + +int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char type, unsigned char dup, unsigned short packetid); +int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen); + +int MQTTPacket_len(int rem_len); +int MQTTPacket_equals(MQTTString* a, char* b); + +int MQTTPacket_encode(unsigned char* buf, int length); +int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value); +int MQTTPacket_decodeBuf(unsigned char* buf, int* value); + +int readInt(unsigned char** pptr); +char readChar(unsigned char** pptr); +void writeChar(unsigned char** pptr, char c); +void writeInt(unsigned char** pptr, int anInt); +int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata); +void writeCString(unsigned char** pptr, const char* string); +void writeMQTTString(unsigned char** pptr, MQTTString mqttstring); + +DLLExport int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int)); + +typedef struct { + int (*getfn)(void *, unsigned char*, int); /* must return -1 for error, 0 for call again, or the number of bytes read */ + void *sck; /* pointer to whatever the system may use to identify the transport */ + int multiplier; + int rem_len; + int len; + char state; +}MQTTTransport; + +int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp); + +#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */ +} +#endif + + +#endif /* MQTTPACKET_H_ */ diff --git a/lib/mqtt/MQTTPublish.h b/lib/mqtt/MQTTPublish.h new file mode 100644 index 000000000..ebe479dd5 --- /dev/null +++ b/lib/mqtt/MQTTPublish.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +#ifndef MQTTPUBLISH_H_ +#define MQTTPUBLISH_H_ + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + +DLLExport int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid, + MQTTString topicName, unsigned char* payload, int payloadlen); + +DLLExport int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName, + unsigned char** payload, int* payloadlen, unsigned char* buf, int len); + +DLLExport int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid); +DLLExport int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid); +DLLExport int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid); + +#endif /* MQTTPUBLISH_H_ */ diff --git a/lib/mqtt/MQTTSerializePublish.c b/lib/mqtt/MQTTSerializePublish.c new file mode 100644 index 000000000..07648307c --- /dev/null +++ b/lib/mqtt/MQTTSerializePublish.c @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Ian Craggs - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=453144 + *******************************************************************************/ + +#include "MQTTPacket.h" + +#include + + +/** + * Determines the length of the MQTT publish packet that would be produced using the supplied parameters + * @param qos the MQTT QoS of the publish (packetid is omitted for QoS 0) + * @param topicName the topic name to be used in the publish + * @param payloadlen the length of the payload to be sent + * @return the length of buffer needed to contain the serialized version of the packet + */ +int MQTTSerialize_publishLength(int qos, MQTTString topicName, int payloadlen) +{ + int len = 0; + + len += 2 + MQTTstrlen(topicName) + payloadlen; + if (qos > 0) + len += 2; /* packetid */ + return len; +} + + +/** + * Serializes the supplied publish data into the supplied buffer, ready for sending + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param dup integer - the MQTT dup flag + * @param qos integer - the MQTT QoS value + * @param retained integer - the MQTT retained flag + * @param packetid integer - the MQTT packet identifier + * @param topicName MQTTString - the MQTT topic in the publish + * @param payload byte buffer - the MQTT publish payload + * @param payloadlen integer - the length of the MQTT payload + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid, + MQTTString topicName, unsigned char* payload, int payloadlen) +{ + unsigned char *ptr = buf; + MQTTHeader header = {0}; + int rem_len = 0; + int rc = 0; + + if (MQTTPacket_len(rem_len = MQTTSerialize_publishLength(qos, topicName, payloadlen)) > buflen) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + + header.bits.type = PUBLISH; + header.bits.dup = dup; + header.bits.qos = qos; + header.bits.retain = retained; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; + + writeMQTTString(&ptr, topicName); + + if (qos > 0) + writeInt(&ptr, packetid); + + memcpy(ptr, payload, payloadlen); + ptr += payloadlen; + + rc = ptr - buf; + +exit: + return rc; +} + + + +/** + * Serializes the ack packet into the supplied buffer. + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param type the MQTT packet type + * @param dup the MQTT dup flag + * @param packetid the MQTT packet identifier + * @return serialized length, or error if 0 + */ +int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char packettype, unsigned char dup, unsigned short packetid) +{ + MQTTHeader header = {0}; + int rc = 0; + unsigned char *ptr = buf; + + if (buflen < 4) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + header.bits.type = packettype; + header.bits.dup = dup; + header.bits.qos = (packettype == PUBREL) ? 1 : 0; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ + writeInt(&ptr, packetid); + rc = ptr - buf; +exit: + return rc; +} + + +/** + * Serializes a puback packet into the supplied buffer. + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param packetid integer - the MQTT packet identifier + * @return serialized length, or error if 0 + */ +int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid) +{ + return MQTTSerialize_ack(buf, buflen, PUBACK, 0, packetid); +} + + +/** + * Serializes a pubrel packet into the supplied buffer. + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param dup integer - the MQTT dup flag + * @param packetid integer - the MQTT packet identifier + * @return serialized length, or error if 0 + */ +int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid) +{ + return MQTTSerialize_ack(buf, buflen, PUBREL, dup, packetid); +} + + +/** + * Serializes a pubrel packet into the supplied buffer. + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer + * @param packetid integer - the MQTT packet identifier + * @return serialized length, or error if 0 + */ +int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid) +{ + return MQTTSerialize_ack(buf, buflen, PUBCOMP, 0, packetid); +} + + diff --git a/lib/mqtt/MQTTSubscribe.h b/lib/mqtt/MQTTSubscribe.h new file mode 100644 index 000000000..aa9182680 --- /dev/null +++ b/lib/mqtt/MQTTSubscribe.h @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +#ifndef MQTTSUBSCRIBE_H_ +#define MQTTSUBSCRIBE_H_ + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + +DLLExport int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[], int requestedQoSs[]); + +DLLExport int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, + int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int len); + +DLLExport int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs); + +DLLExport int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int len); + + +#endif /* MQTTSUBSCRIBE_H_ */ diff --git a/lib/mqtt/MQTTSubscribeClient.c b/lib/mqtt/MQTTSubscribeClient.c new file mode 100644 index 000000000..e6e55c977 --- /dev/null +++ b/lib/mqtt/MQTTSubscribeClient.c @@ -0,0 +1,132 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#include "MQTTPacket.h" + +#include + +/** + * Determines the length of the MQTT subscribe packet that would be produced using the supplied parameters + * @param count the number of topic filter strings in topicFilters + * @param topicFilters the array of topic filter strings to be used in the publish + * @return the length of buffer needed to contain the serialized version of the packet + */ +int MQTTSerialize_subscribeLength(int count, MQTTString topicFilters[]) +{ + int i; + int len = 2; /* packetid */ + + for (i = 0; i < count; ++i) + len += 2 + MQTTstrlen(topicFilters[i]) + 1; /* length + topic + req_qos */ + return len; +} + + +/** + * Serializes the supplied subscribe data into the supplied buffer, ready for sending + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied bufferr + * @param dup integer - the MQTT dup flag + * @param packetid integer - the MQTT packet identifier + * @param count - number of members in the topicFilters and reqQos arrays + * @param topicFilters - array of topic filter names + * @param requestedQoSs - array of requested QoS + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, int count, + MQTTString topicFilters[], int requestedQoSs[]) +{ + unsigned char *ptr = buf; + MQTTHeader header = {0}; + int rem_len = 0; + int rc = 0; + int i = 0; + + if (MQTTPacket_len(rem_len = MQTTSerialize_subscribeLength(count, topicFilters)) > buflen) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + + header.byte = 0; + header.bits.type = SUBSCRIBE; + header.bits.dup = dup; + header.bits.qos = 1; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; + + writeInt(&ptr, packetid); + + for (i = 0; i < count; ++i) + { + writeMQTTString(&ptr, topicFilters[i]); + writeChar(&ptr, requestedQoSs[i]); + } + + rc = ptr - buf; +exit: + return rc; +} + + + +/** + * Deserializes the supplied (wire) buffer into suback data + * @param packetid returned integer - the MQTT packet identifier + * @param maxcount - the maximum number of members allowed in the grantedQoSs array + * @param count returned integer - number of members in the grantedQoSs array + * @param grantedQoSs returned array of integers - the granted qualities of service + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return error code. 1 is success, 0 is failure + */ +int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int buflen) +{ + MQTTHeader header = {0}; + unsigned char* curdata = buf; + unsigned char* enddata = NULL; + int rc = 0; + int mylen; + + header.byte = readChar(&curdata); + if (header.bits.type != SUBACK) + goto exit; + + curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ + enddata = curdata + mylen; + if (enddata - curdata < 2) + goto exit; + + *packetid = readInt(&curdata); + + *count = 0; + while (curdata < enddata) + { + if (*count > maxcount) + { + rc = -1; + goto exit; + } + grantedQoSs[(*count)++] = readChar(&curdata); + } + + rc = 1; +exit: + return rc; +} + + diff --git a/lib/mqtt/MQTTUnsubscribe.h b/lib/mqtt/MQTTUnsubscribe.h new file mode 100644 index 000000000..355ca9a42 --- /dev/null +++ b/lib/mqtt/MQTTUnsubscribe.h @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +#ifndef MQTTUNSUBSCRIBE_H_ +#define MQTTUNSUBSCRIBE_H_ + +#if !defined(DLLImport) + #define DLLImport +#endif +#if !defined(DLLExport) + #define DLLExport +#endif + +DLLExport int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[]); + +DLLExport int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int max_count, int* count, MQTTString topicFilters[], + unsigned char* buf, int len); + +DLLExport int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid); + +DLLExport int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int len); + +#endif /* MQTTUNSUBSCRIBE_H_ */ diff --git a/lib/mqtt/MQTTUnsubscribeClient.c b/lib/mqtt/MQTTUnsubscribeClient.c new file mode 100644 index 000000000..6dfee7b61 --- /dev/null +++ b/lib/mqtt/MQTTUnsubscribeClient.c @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +#include "MQTTPacket.h" + +#include + +/** + * Determines the length of the MQTT unsubscribe packet that would be produced using the supplied parameters + * @param count the number of topic filter strings in topicFilters + * @param topicFilters the array of topic filter strings to be used in the publish + * @return the length of buffer needed to contain the serialized version of the packet + */ +int MQTTSerialize_unsubscribeLength(int count, MQTTString topicFilters[]) +{ + int i; + int len = 2; /* packetid */ + + for (i = 0; i < count; ++i) + len += 2 + MQTTstrlen(topicFilters[i]); /* length + topic*/ + return len; +} + + +/** + * Serializes the supplied unsubscribe data into the supplied buffer, ready for sending + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @param dup integer - the MQTT dup flag + * @param packetid integer - the MQTT packet identifier + * @param count - number of members in the topicFilters array + * @param topicFilters - array of topic filter names + * @return the length of the serialized data. <= 0 indicates error + */ +int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, + int count, MQTTString topicFilters[]) +{ + unsigned char *ptr = buf; + MQTTHeader header = {0}; + int rem_len = 0; + int rc = -1; + int i = 0; + + if (MQTTPacket_len(rem_len = MQTTSerialize_unsubscribeLength(count, topicFilters)) > buflen) + { + rc = MQTTPACKET_BUFFER_TOO_SHORT; + goto exit; + } + + header.byte = 0; + header.bits.type = UNSUBSCRIBE; + header.bits.dup = dup; + header.bits.qos = 1; + writeChar(&ptr, header.byte); /* write header */ + + ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; + + writeInt(&ptr, packetid); + + for (i = 0; i < count; ++i) + writeMQTTString(&ptr, topicFilters[i]); + + rc = ptr - buf; +exit: + return rc; +} + + +/** + * Deserializes the supplied (wire) buffer into unsuback data + * @param packetid returned integer - the MQTT packet identifier + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return error code. 1 is success, 0 is failure + */ +int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int buflen) +{ + unsigned char type = 0; + unsigned char dup = 0; + int rc = 0; + + rc = MQTTDeserialize_ack(&type, &dup, packetid, buf, buflen); + if (type == UNSUBACK) + rc = 1; + return rc; +} + + From 4b16668c8fe933ee0848b07c828bf47a390b43c0 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 1 Jul 2016 13:46:36 +0200 Subject: [PATCH 002/175] Correct typos --- ArduinoCloudThingBase.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ArduinoCloudThingBase.h b/ArduinoCloudThingBase.h index c771855e4..90f230645 100755 --- a/ArduinoCloudThingBase.h +++ b/ArduinoCloudThingBase.h @@ -27,9 +27,9 @@ #define TEMPERATURE_F "fahrenheit" /// Distance -#define LENGHT_M "meters" -#define LENGHT_C "centimeters" -#define LENGHT_I "inches" +#define LENGTH_M "meters" +#define LENGTH_C "centimeters" +#define LENGTH_I "inches" /// MIX #define PERCENTAGE "percentage" From f6af0a6a97e7dd5433393db50fb5a925b5b5e6e5 Mon Sep 17 00:00:00 2001 From: lorenzo Date: Thu, 21 Jul 2016 15:52:08 +0200 Subject: [PATCH 003/175] remove loop cycle, otherwise if wifi connection drops code get stuck --- ArduinoCloudThing.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 4a3d9dda9..baf71660d 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -19,10 +19,10 @@ boolean ArduinoCloudThing::connect() { char statusTopic[strlen(username) + strlen(name) + strlen(STATUS_TOPIC) + 3]; // 2 extra bytes for /'s, 1 for null terminator sprintf(statusTopic, "%s/%s/%s", username, name, STATUS_TOPIC); - // if (cloud_debug) { - // CLOUD_DEBUG_STREAM.print("Will Topic: "); - // CLOUD_DEBUG_STREAM.println(statusTopic); - // } + if (cloud_debug) { + CLOUD_DEBUG_STREAM.print("Will Topic: "); + CLOUD_DEBUG_STREAM.println(statusTopic); + } options.clientID.cstring = (char*)name; options.username.cstring = (char*)id; options.password.cstring = (char*)password; @@ -31,12 +31,14 @@ boolean ArduinoCloudThing::connect() { options.will.topicName.cstring = (char*)statusTopic; options.will.message.cstring = (char*)OFFLINE_STATUS_PAYLOAD; options.will.retained = 0x1; + Serial.println("connected"); if (client->connect(options) == 0) { publish(statusTopic, (char*)ONLINE_STATUS_PAYLOAD, strlen(ONLINE_STATUS_PAYLOAD), true); return true; } + Serial.println("connection failed"); return false; } @@ -56,11 +58,11 @@ void ArduinoCloudThing::publish(const char * topic, const char * payload, unsign // Reconnect to the mqtt broker void ArduinoCloudThing::poll() { - while (!client->isConnected()){ + if (!client->isConnected()){ if (cloud_debug) { CLOUD_DEBUG_STREAM.println("Connecting to mqtt broker..."); } - while (!connect()){ + if (!connect()){ delay(1000); } if (cloud_debug) { From ffafb20f7cb344873c1a88f74b9c1b7f17e7afd2 Mon Sep 17 00:00:00 2001 From: lorenzo Date: Thu, 21 Jul 2016 15:56:12 +0200 Subject: [PATCH 004/175] removed debug prints --- ArduinoCloudThing.cpp | 10 ++++------ lib/Network.cpp | 2 -- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index baf71660d..a2d2d6a29 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -31,14 +31,12 @@ boolean ArduinoCloudThing::connect() { options.will.topicName.cstring = (char*)statusTopic; options.will.message.cstring = (char*)OFFLINE_STATUS_PAYLOAD; options.will.retained = 0x1; - Serial.println("connected"); - if (client->connect(options) == 0) { - publish(statusTopic, (char*)ONLINE_STATUS_PAYLOAD, strlen(ONLINE_STATUS_PAYLOAD), true); - return true; - } + // if (client->connect(options) == 0) { + // publish(statusTopic, (char*)ONLINE_STATUS_PAYLOAD, strlen(ONLINE_STATUS_PAYLOAD), true); + // return true; + // } - Serial.println("connection failed"); return false; } diff --git a/lib/Network.cpp b/lib/Network.cpp index 19bbaff9d..9cce6fceb 100644 --- a/lib/Network.cpp +++ b/lib/Network.cpp @@ -21,7 +21,6 @@ int Network::read(unsigned char* buffer, int len, int timeout) { this->client->setTimeout(timeout); return this->client->readBytes(buffer, len); } - int Network::write(unsigned char* buffer, int len, int timeout) { if(!client->connected()) { return -1; // return an error @@ -34,7 +33,6 @@ int Network::write(unsigned char* buffer, int len, int timeout) { boolean Network::connected() { return client->connected(); } - int Network::disconnect() { client->stop(); return 0; From 7720f9777ba7e00d2d719d1036f0a57a585f5625 Mon Sep 17 00:00:00 2001 From: lorenzo Date: Thu, 21 Jul 2016 15:57:04 +0200 Subject: [PATCH 005/175] wrong comment --- ArduinoCloudThing.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index a2d2d6a29..1de25c61d 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -19,10 +19,10 @@ boolean ArduinoCloudThing::connect() { char statusTopic[strlen(username) + strlen(name) + strlen(STATUS_TOPIC) + 3]; // 2 extra bytes for /'s, 1 for null terminator sprintf(statusTopic, "%s/%s/%s", username, name, STATUS_TOPIC); - if (cloud_debug) { - CLOUD_DEBUG_STREAM.print("Will Topic: "); - CLOUD_DEBUG_STREAM.println(statusTopic); - } + // if (cloud_debug) { + // CLOUD_DEBUG_STREAM.print("Will Topic: "); + // CLOUD_DEBUG_STREAM.println(statusTopic); + // } options.clientID.cstring = (char*)name; options.username.cstring = (char*)id; options.password.cstring = (char*)password; @@ -32,10 +32,10 @@ boolean ArduinoCloudThing::connect() { options.will.message.cstring = (char*)OFFLINE_STATUS_PAYLOAD; options.will.retained = 0x1; - // if (client->connect(options) == 0) { - // publish(statusTopic, (char*)ONLINE_STATUS_PAYLOAD, strlen(ONLINE_STATUS_PAYLOAD), true); - // return true; - // } + if (client->connect(options) == 0) { + publish(statusTopic, (char*)ONLINE_STATUS_PAYLOAD, strlen(ONLINE_STATUS_PAYLOAD), true); + return true; + } return false; } From 189592c0bb2828e2a3d2053e5ad826d9e590ba98 Mon Sep 17 00:00:00 2001 From: lorenzo Date: Fri, 22 Jul 2016 15:07:04 +0200 Subject: [PATCH 006/175] modified example and removed useless delay --- ArduinoCloudThing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 1de25c61d..034575d4c 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -61,7 +61,7 @@ void ArduinoCloudThing::poll() { CLOUD_DEBUG_STREAM.println("Connecting to mqtt broker..."); } if (!connect()){ - delay(1000); + return; } if (cloud_debug) { CLOUD_DEBUG_STREAM.println("Connected"); From 6b6ccec7e71a770af234cea3a0de6c850cb79518 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Thu, 25 Jan 2018 18:24:52 +0100 Subject: [PATCH 007/175] start new branch with cbor and stuff --- ArduinoCloud.h => ArduinoCloudNew.h | 0 ArduinoCloudThing.cpp | 187 ++++++++++++++++++++-------- ArduinoCloudThing.h | 145 +++++++++++++++++++-- ArduinoCloudThingBase.cpp | 147 ---------------------- ArduinoCloudThingBase.h | 110 ---------------- lib/Network.cpp | 2 +- lib/Network.h | 2 +- 7 files changed, 271 insertions(+), 322 deletions(-) rename ArduinoCloud.h => ArduinoCloudNew.h (100%) delete mode 100755 ArduinoCloudThingBase.cpp delete mode 100755 ArduinoCloudThingBase.h diff --git a/ArduinoCloud.h b/ArduinoCloudNew.h similarity index 100% rename from ArduinoCloud.h rename to ArduinoCloudNew.h diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 034575d4c..ed6a85055 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -1,105 +1,188 @@ +#include #include ArduinoCloudThing::ArduinoCloudThing() { + } -void ArduinoCloudThing::begin(const char* name, const char* username, const char* id, const char* password, Client &client) { - ArduinoCloudThingBase::begin(name, username, id, password); +/* + * begin() should prepare the environment + * connect + */ + +static constexpr char* PLACEHOLDER = "placeholder"; +void ArduinoCloudThing::begin(Client &client) { this->client = new MQTT::Client(network); this->network.setClient(&client); this->options = MQTTPacket_connectData_initializer; this->client->defaultMessageHandler.attach(this, &ArduinoCloudThing::callback); + + // using WiFi client and ECC508 connect to server + while (!connect()) { + Serial.println("Not connected"); + delay(500); + } } -boolean ArduinoCloudThing::connect() { - if(!network.connect((char*)SERVER_DOMAIN, SERVER_PORT)) { +bool ArduinoCloudThing::connect() { + +#ifdef TESTING + return true; +#endif + + if(!network.connect(PLACEHOLDER, 0xDEADBEEF)) { return false; } - char statusTopic[strlen(username) + strlen(name) + strlen(STATUS_TOPIC) + 3]; // 2 extra bytes for /'s, 1 for null terminator - sprintf(statusTopic, "%s/%s/%s", username, name, STATUS_TOPIC); - // if (cloud_debug) { - // CLOUD_DEBUG_STREAM.print("Will Topic: "); - // CLOUD_DEBUG_STREAM.println(statusTopic); - // } - options.clientID.cstring = (char*)name; - options.username.cstring = (char*)id; - options.password.cstring = (char*)password; + options.clientID.cstring = PLACEHOLDER; + options.username.cstring = PLACEHOLDER; + options.password.cstring = PLACEHOLDER; options.keepAliveInterval = 10; options.willFlag = 0x1; - options.will.topicName.cstring = (char*)statusTopic; - options.will.message.cstring = (char*)OFFLINE_STATUS_PAYLOAD; + options.will.topicName.cstring = PLACEHOLDER; + options.will.message.cstring = PLACEHOLDER; options.will.retained = 0x1; - if (client->connect(options) == 0) { - publish(statusTopic, (char*)ONLINE_STATUS_PAYLOAD, strlen(ONLINE_STATUS_PAYLOAD), true); + if (client->connect(options) != 0) { + // set status to ON + status = ON; + addProperty(status, WRITE); + // execute first poll() to syncronize the "manifest" + poll(); + // subscribe to "general" topic + client->subscribe("placeholder", MQTT::QOS0, NULL); return true; } return false; } -void ArduinoCloudThing::publish(const char * topic, const char * payload) { - publish(topic, (char*)payload, (unsigned int)strlen(payload)); -} +void ArduinoCloudThing::publish() { + + bool retained = false; + + CborDynamicOutput output; + CborWriter writer(output); + + writer.writeTag(1); + + for (int i = 0; i < list.size(); i++) { + ArduinoCloudPropertyGeneric *p = list.get(i); + p->append(writer); + } + + unsigned char *buf = output.getData(); + + decodeCBORData(buf, output.getSize()); -void ArduinoCloudThing::publish(const char * topic, const char * payload, unsigned int length, bool retained) { MQTT::Message message; message.qos = MQTT::QOS0; message.retained = retained; message.dup = false; - message.payload = (void*)payload; - message.payloadlen = length; + message.payload = (void*)buf; + message.payloadlen = output.getSize(); client->publish(topic, message); } // Reconnect to the mqtt broker -void ArduinoCloudThing::poll() { +int ArduinoCloudThing::poll() { + +#ifndef TESTING if (!client->isConnected()){ - if (cloud_debug) { - CLOUD_DEBUG_STREAM.println("Connecting to mqtt broker..."); - } if (!connect()){ - return; + return 0; } - if (cloud_debug) { - CLOUD_DEBUG_STREAM.println("Connected"); - } - mqttSubscribe(); } if(!network.connected() && client->isConnected()) { client->disconnect(); } +#endif + + // check if backing storage and cloud has diverged + int diff_in = 0; + int diff_out = 1; + checkNewData(&diff_in , &diff_out); + if (diff_out > 0) { + publish(); + } + if (diff_in > 0) { + update(); + } client->yield(); + return diff_in; } -// Subscribe to all proprie that have RW permission -void ArduinoCloudThing::mqttSubscribe(){ - for (int i=0; ipermission, "RW") == 0) { - String topic = buildTopicProperty(i); - if (cloud_debug) { - CLOUD_DEBUG_STREAM.print("Subscribe to: "); - CLOUD_DEBUG_STREAM.println(topic); - } - client->subscribe(topic.c_str(), MQTT::QOS0, NULL); - } - } +void ArduinoCloudThing::checkNewData(int* new_data_in, int* new_data_out) { + +} + +void ArduinoCloudThing::addPropertyReal(int& property, String name, permissionType permission) { + ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name, permission); + list.add(thing); } +void ArduinoCloudThing::addPropertyReal(bool& property, String name, permissionType permission) { + ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name, permission); + list.add(thing); +} + +void ArduinoCloudThing::addPropertyReal(float& property, String name, permissionType permission) { + ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name, permission); + list.add(thing); +} + + ArduinoCloudThing ArduinoCloudThing::callback(MQTT::MessageData& messageData) { MQTT::Message &message = messageData.message; // null terminate topic to create String object - int len = messageData.topicName.lenstring.len; - char topic[len+1]; - memcpy(topic, messageData.topicName.lenstring.data, (size_t)len); - topic[len] = '\0'; - - char * payload = (char *)message.payload; - payload[message.payloadlen] = '\0'; - updatePropertyFromTopic(topic, payload); + // unwrap message into a structure + uint8_t * payload = (uint8_t*)message.payload; + decodeCBORData(payload, message.payloadlen); return *this; } + +void ArduinoCloudThing::decodeCBORData(uint8_t * payload, size_t length) { + list_shadow.clear(); + + CborInput input(payload, length); + + CborReader reader(input); + CborPropertyListener listener(list_shadow); + reader.SetListener(listener); + reader.Run(); +} + +void CborPropertyListener::OnInteger(int32_t value) { + printf("integer: %d\n", value); +} + +void CborPropertyListener::OnBytes(unsigned char *data, unsigned int size) { + printf("bytes with size: %d", size); +} + +void CborPropertyListener::OnString(String &str) { + printf("string: '%.*s'\n", (int)str.length(), str.c_str()); +} + +void CborPropertyListener::OnArray(unsigned int size) { + printf("array: %d\n", size); +} + +void CborPropertyListener::OnMap(unsigned int size) { + printf("map: %d\n", size); +} + +void CborPropertyListener::OnTag(unsigned int tag) { + printf("tag: %d\n", tag); +} + +void CborPropertyListener::OnSpecial(unsigned int code) { + printf("special: %d\n", code); +} + +void CborPropertyListener::OnError(const char *error) { + printf("error: %s\n", error); +} \ No newline at end of file diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index f75b3808c..727a19609 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -1,37 +1,160 @@ #ifndef ArduinoCloudThing_h #define ArduinoCloudThing_h -#include #include #include #include "lib/mqtt/MQTTClient.h" #include "lib/Network.h" +#include "lib/Timer.h" +#include +#include +#include #ifndef MQTT_BUFFER_SIZE #define MQTT_BUFFER_SIZE 256 #endif -#define MQTTCLIENT_QOS1 0 -#define MQTTCLIENT_QOS2 0 +#define Serial DebugSerial +#define TESTING -class ArduinoCloudThing : public ArduinoCloudThingBase { +//#define MQTTCLIENT_QOS1 0 +//#define MQTTCLIENT_QOS2 0 + +enum permissionType { + READ = 0b01, + WRITE = 0b10, + READWRITE = READ|WRITE, +}; + +enum boolStatus { + ON, + OFF, +}; + +class ArduinoCloudPropertyGeneric +{ public: - ArduinoCloudThing(); - void begin(const char* name, const char* username, const char* id, const char* password, Client &client); - void poll(); + virtual void append(CborWriter &cbor) = 0; + virtual const char* getName() = 0; +}; + +template +class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric +{ +public: + ArduinoCloudProperty(T& _property, String _name, permissionType _permission) : + property(_property), name(_name), permission(_permission) {} + + bool write(T value) { + if (permission != READ) { + property = value; + return true; + } + return false; + } + + T read() { + if (permission != WRITE) { + return property; + } + } + + const char* getName() { + return name.c_str(); + } + + void appendValue(CborWriter &cbor); + + void append(CborWriter &cbor) { + cbor.writeArray(3); + appendValue(cbor); + cbor.writeInt(permission); + cbor.writeString(name); + } + + inline bool operator==(const ArduinoCloudProperty& rhs){ + return (strcmp(getName(), rhs.getName) == 0); + } protected: - virtual void publish(const char * topic, const char* payload); + T& property; + String name; + permissionType permission; +}; + +template <> +inline void ArduinoCloudProperty::appendValue(CborWriter &cbor) { + cbor.writeInt(property); +}; + +template <> +inline void ArduinoCloudProperty::appendValue(CborWriter &cbor) { + //cbor.writeBoolean(property); + cbor.writeInt((int)property); +}; + +template <> +inline void ArduinoCloudProperty::appendValue(CborWriter &cbor) { + //cbor.writeFloat(property); +}; + +template <> +inline void ArduinoCloudProperty::appendValue(CborWriter &cbor) { + cbor.writeString(property); +}; + +template <> +inline void ArduinoCloudProperty::appendValue(CborWriter &cbor) { + cbor.writeString(property); +}; + +#ifndef addProperty +#define addProperty(prop, permission) addPropertyReal(prop, #prop, permission) +#endif + +class ArduinoCloudThing { +public: + ArduinoCloudThing(); + void begin(Client &client); + void addPropertyReal(int& property, String name, permissionType permission); + void addPropertyReal(bool& property, String name, permissionType permission); + void addPropertyReal(float& property, String name, permissionType permission); + void addPropertyReal(void* property, String name, permissionType permission); + // poll should return > 0 if something has changed + int poll(); private: ArduinoCloudThing callback(MQTT::MessageData& messageData); - void mqttSubscribe(); - boolean connect(); - void publish(const char * topic, const char * payload, unsigned int length, bool retained = false); + bool connect(); + void publish(); + + void update(); + void checkNewData(int* new_data_in, int* new_data_out); + void decodeCBORData(uint8_t * payload, size_t length); + + bool status = OFF; + char* topic; + + LinkedList list; + LinkedList list_shadow; Network network; MQTT::Client * client; MQTTPacket_connectData options; }; +class CborPropertyListener : public CborListener { + public: + CborPropertyListener(LinkedList _list) : list(_list) {} + void OnInteger(int32_t value); + void OnBytes(unsigned char *data, unsigned int size); + void OnString(String &str); + void OnArray(unsigned int size); + void OnMap(unsigned int size) ; + void OnTag(uint32_t tag); + void OnSpecial(uint32_t code); + void OnError(const char *error); + LinkedList list; +}; + #endif diff --git a/ArduinoCloudThingBase.cpp b/ArduinoCloudThingBase.cpp deleted file mode 100755 index c05b29528..000000000 --- a/ArduinoCloudThingBase.cpp +++ /dev/null @@ -1,147 +0,0 @@ -#include "ArduinoCloudThingBase.h" - -void ArduinoCloudThingBase::begin(const char* name, const char* username, const char* id, const char* password){ - this->name = name; - this->username = username; - this->id = id; - this->password = password; -} - -void ArduinoCloudThingBase::addProperty(const char* name, const char* datatype, const char* permission){ - addExternalProperty(NULL, name, datatype, permission, "", 0); -} - -void ArduinoCloudThingBase::addProperty(const char* name, const char* datatype, const char* permission, const char* policy){ - addExternalProperty(NULL, name, datatype, permission, policy, 0); -} - -void ArduinoCloudThingBase::addProperty(const char* name, const char* datatype, const char* permission, const char* policy, int lapse){ - addExternalProperty(NULL, name, datatype, permission, policy, lapse); -} - -void ArduinoCloudThingBase::addExternalProperty(const char* other_device_name, const char* name, const char* datatype){ - addExternalProperty(other_device_name, name, datatype, RW, "", 0); -} - -void ArduinoCloudThingBase::addExternalProperty(const char* other_device_name, const char* name, const char* datatype, const char* permission, const char* policy, int lapse){ - // Create property structure - property_conf* prop = new property_conf {name, datatype, permission, other_device_name, policy, "", (long)lapse*1000, 0}; - properties[properties_count] = prop; // Save pointer to scructure - properties_count++; // count the number of properties -} - -void ArduinoCloudThingBase::writeProperty(const char* name, String value){ - writeProperty(name, value.c_str()); -} - -void ArduinoCloudThingBase::writeProperty(const char* name, int value){ - writeProperty(name, String(value).c_str()); -} - -void ArduinoCloudThingBase::writeProperty(const char* name, float value){ - writeProperty(name, String(value).c_str()); -} - -void ArduinoCloudThingBase::writeProperty(const char* name, const char* value){ - int i = discoverProperty(NULL, name); - - if (i == -1) { - // not found, nothing to do - return; - } - - if (properties[i]->value.equals(value) && strcmp(properties[i]->policy, ON_CHANGE) == 0) { - if (cloud_debug) { - CLOUD_DEBUG_STREAM.print("No Changes for "); - CLOUD_DEBUG_STREAM.print(name); - CLOUD_DEBUG_STREAM.print(": "); - CLOUD_DEBUG_STREAM.println(value); - } - return; - } - if ((long)(millis() - properties[i]->runtime) >= properties[i]->lapse) { - properties[i]->value = value; - String topic = buildTopicProperty(i); - - publish(topic.c_str(), value); - - if (cloud_debug) { - CLOUD_DEBUG_STREAM.print("OK, Published "); - CLOUD_DEBUG_STREAM.print(value); - CLOUD_DEBUG_STREAM.print(" on topic: "); - CLOUD_DEBUG_STREAM.println(topic); - } - properties[i]->runtime = millis(); - } -} - -// Get the a property value -String ArduinoCloudThingBase::readProperty(const char* name){ - return readProperty(NULL, name); -} - -String ArduinoCloudThingBase::readProperty(const char* _device_name, const char* name){ - int index = discoverProperty(_device_name, name); - - if (index != -1) { - return String(properties[index]->value); - } - - return String(); -} - -void ArduinoCloudThingBase::push(){ - for (int i = 0; i <= properties_count; i++){ - if (properties[i]->device_name == NULL){ - String topic = buildTopicProperty(i); - - publish(topic.c_str(), properties[i]->value.c_str()); - } - } -} - -// Discovers the property position in the array based -// on the name of the property -int ArduinoCloudThingBase::discoverProperty(const char* device_name, const char* name){ - for (int i = 0; i <= properties_count; i++){ - if (properties[i]->name == name && properties[i]->device_name == device_name){ - return i; - } - } - return -1; -} - -void ArduinoCloudThingBase::updatePropertyFromTopic(const char* topic, const char* value) { - for (int i = 0; i <= properties_count; i++){ - String propertyTopic = buildTopicProperty(i); - - if (strcmp(propertyTopic.c_str(), topic) == 0) { - properties[i]->value = value; - } - } -} - -// Build the topic for a property -String ArduinoCloudThingBase::buildTopicProperty(int property_index){ - String topic; - - topic += username; - topic += '/'; - topic += (properties[property_index]->device_name) ? properties[property_index]->device_name : name; - topic += '/'; - topic += properties[property_index]->name; - - return topic; -} - -void ArduinoCloudThingBase::publish(const char * /*topic*/, const char * /*payload*/) { -} - - -void ArduinoCloudThingBase::enableDebug(){ - cloud_debug = true; -} - -void ArduinoCloudThingBase::disableDebug(){ - cloud_debug = false; -} diff --git a/ArduinoCloudThingBase.h b/ArduinoCloudThingBase.h deleted file mode 100755 index 90f230645..000000000 --- a/ArduinoCloudThingBase.h +++ /dev/null @@ -1,110 +0,0 @@ -#ifndef ArduinoCloudThingBase_h -#define ArduinoCloudThingBase_h - -#include -#include "lib/Timer.h" - -// Uncomment to allow the debut output -#define CLOUD_DEBUG_STREAM Serial // SAMD -// #define CLOUD_DEBUG_STREAM SerialUSB // SAM3X Native - -/// Configurations -#define SERVER_DOMAIN "mqtt.arduino.cc" -#define SERVER_PORT 8883 -#define PROPERTIES_MAX 10 - -/// Permission -#define R "R" -#define RW "RW" - -/// Data Type -#define CHARSTRING "charstring" -#define FLOAT "float" -#define INT "int" - -/// Temperature -#define TEMPERATURE_C "celsius" -#define TEMPERATURE_F "fahrenheit" - -/// Distance -#define LENGTH_M "meters" -#define LENGTH_C "centimeters" -#define LENGTH_I "inches" - -/// MIX -#define PERCENTAGE "percentage" -#define ANALOG "analog" -#define LUMEN "lumen" -#define PPM "ppm" // gas part per million -#define STATUS "status" - -//// Polices -#define TIMED "timed" -#define ON_CHANGE "on_change" - -#ifndef MQTT_BUFFER_SIZE -#define MQTT_BUFFER_SIZE 256 -#endif - -#define MQTTCLIENT_QOS1 0 -#define MQTTCLIENT_QOS2 0 - -/// Status -#define STATUS_TOPIC "status" -#define ONLINE_STATUS_PAYLOAD "online" -#define OFFLINE_STATUS_PAYLOAD "offline" - -class ArduinoCloudThingBase { - -public: - void addProperty(const char* name, const char* datatype, const char* permission); - void addProperty(const char* name, const char* datatype, const char* permission, const char* policy); - void addProperty(const char* name, const char* datatype, const char* permission, const char* policy, int lapse); - void addExternalProperty(const char* other_device_name, const char* name, const char* datatype); - - void writeProperty(const char* name, const char* value); - void writeProperty(const char* name, float value); - void writeProperty(const char* name, int value); - void writeProperty(const char* name, String value); - - String readProperty(const char* name); - String readProperty(const char* other_device_name, const char* name); - - void enableDebug(); - void disableDebug(); - void push(); - -protected: - void begin(const char* name, const char* username, const char* id, const char* password); - void addExternalProperty(const char* other_device_name, const char* name, const char* datatype, const char* permission, const char* policy, int lapse); - virtual void publish(const char * topic, const char* payload); - - String buildTopicProperty(int property_index); - int discoverProperty(const char* device_name, const char* name); - - // void addProperty(const char* other_device_name, const char* name, const char* datatype, const char* permission, const char* policy, int lapse); - void updatePropertyFromTopic(const char* topic, const char* value); - - // Object configuration - const char* username; - const char* id; - const char* password; - const char* name; - - // Property configuration - struct property_conf { - const char* name; - const char* datatype; - const char* permission; - const char* device_name; - const char* policy; - String value; - long lapse; - long runtime; - }; - - bool cloud_debug = false; - property_conf* properties[PROPERTIES_MAX]; - int properties_count = 0; -}; -#endif diff --git a/lib/Network.cpp b/lib/Network.cpp index 9cce6fceb..8be13f509 100644 --- a/lib/Network.cpp +++ b/lib/Network.cpp @@ -30,7 +30,7 @@ int Network::write(unsigned char* buffer, int len, int timeout) { return client->write((uint8_t*)buffer, len); } -boolean Network::connected() { +bool Network::connected() { return client->connected(); } int Network::disconnect() { diff --git a/lib/Network.h b/lib/Network.h index 9a366ec2a..b6927bc40 100644 --- a/lib/Network.h +++ b/lib/Network.h @@ -8,6 +8,6 @@ class Network { int connect(char* hostname, int port); int read(unsigned char* buffer, int len, int timeout); int write(unsigned char* buffer, int len, int timeout); - boolean connected(); + bool connected(); int disconnect(); }; From af18ef1e631379127c521080fc469f46d0c8756b Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 26 Jan 2018 15:42:45 +0100 Subject: [PATCH 008/175] Almost working on SAMD --- ArduinoCloudThing.cpp | 244 +++++--- ArduinoCloudThing.h | 81 ++- lib/Network.cpp | 39 -- lib/Network.h | 13 - lib/Timer.cpp | 26 - lib/Timer.h | 11 - lib/mqtt/FP.h | 208 ------- lib/mqtt/MQTTClient.h | 910 ------------------------------ lib/mqtt/MQTTConnect.h | 136 ----- lib/mqtt/MQTTConnectClient.c | 206 ------- lib/mqtt/MQTTDeserializePublish.c | 102 ---- lib/mqtt/MQTTPacket.c | 401 ------------- lib/mqtt/MQTTPacket.h | 132 ----- lib/mqtt/MQTTPublish.h | 38 -- lib/mqtt/MQTTSerializePublish.c | 164 ------ lib/mqtt/MQTTSubscribe.h | 39 -- lib/mqtt/MQTTSubscribeClient.c | 132 ----- lib/mqtt/MQTTUnsubscribe.h | 38 -- lib/mqtt/MQTTUnsubscribeClient.c | 101 ---- 19 files changed, 222 insertions(+), 2799 deletions(-) delete mode 100644 lib/Network.cpp delete mode 100644 lib/Network.h delete mode 100644 lib/Timer.cpp delete mode 100644 lib/Timer.h delete mode 100644 lib/mqtt/FP.h delete mode 100644 lib/mqtt/MQTTClient.h delete mode 100644 lib/mqtt/MQTTConnect.h delete mode 100644 lib/mqtt/MQTTConnectClient.c delete mode 100644 lib/mqtt/MQTTDeserializePublish.c delete mode 100644 lib/mqtt/MQTTPacket.c delete mode 100644 lib/mqtt/MQTTPacket.h delete mode 100644 lib/mqtt/MQTTPublish.h delete mode 100644 lib/mqtt/MQTTSerializePublish.c delete mode 100644 lib/mqtt/MQTTSubscribe.h delete mode 100644 lib/mqtt/MQTTSubscribeClient.c delete mode 100644 lib/mqtt/MQTTUnsubscribe.h delete mode 100644 lib/mqtt/MQTTUnsubscribeClient.c diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index ed6a85055..306ad48eb 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -1,8 +1,51 @@ #include #include -ArduinoCloudThing::ArduinoCloudThing() { +#ifdef DEBUG_MEMORY +extern "C" char *sbrk(int i); +void PrintFreeRam (void) +{ + char stack_dummy = 0; + Serial.print("Free RAM: "); Serial.println(&stack_dummy - sbrk(0)); +} +#endif +#ifdef ARDUINO_ARCH_SAMD +static void utox8(uint32_t val, char* s) { + for (int i = 0; i < 8; i++) { + int d = val & 0XF; + val = (val >> 4); + + s[7 - i] = d > 9 ? 'A' + d - 10 : '0' + d; + } +} +#endif + +#ifdef USE_ARDUINO_CLOUD + +char MQTT_SERVER[] = "10.130.22.94"; +int MQTT_PORT = 1883; +char GENERAL_TOPIC[] = "/main"; +char MQTT_USER[] = ""; +char MQTT_PASSWORD[] = ""; +char LWT_TOPIC[] = ""; +char LWT_MESSAGE[] = ""; + +#endif + +ArduinoCloudThing::ArduinoCloudThing() { +#ifdef ARDUINO_ARCH_SAMD + #define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x0080A00C) + #define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x0080A040) + #define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x0080A044) + #define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x0080A048) + + utox8(SERIAL_NUMBER_WORD_0, &uuid[0]); + utox8(SERIAL_NUMBER_WORD_1, &uuid[8]); + utox8(SERIAL_NUMBER_WORD_2, &uuid[16]); + utox8(SERIAL_NUMBER_WORD_3, &uuid[24]); + uuid[32] = '\0'; +#endif } /* @@ -10,13 +53,11 @@ ArduinoCloudThing::ArduinoCloudThing() { * connect */ -static constexpr char* PLACEHOLDER = "placeholder"; - void ArduinoCloudThing::begin(Client &client) { - this->client = new MQTT::Client(network); - this->network.setClient(&client); - this->options = MQTTPacket_connectData_initializer; - this->client->defaultMessageHandler.attach(this, &ArduinoCloudThing::callback); + this->client = new MQTTClient(); + this->client->onMessageAdvanced(ArduinoCloudThing::callback); + this->client->begin(MQTT_SERVER, MQTT_PORT, client); + this->client->setParent((void*)this); // using WiFi client and ECC508 connect to server while (!connect()) { @@ -27,136 +68,144 @@ void ArduinoCloudThing::begin(Client &client) { bool ArduinoCloudThing::connect() { -#ifdef TESTING +#ifdef TESTING_PROTOCOL return true; #endif - if(!network.connect(PLACEHOLDER, 0xDEADBEEF)) { - return false; - } - - options.clientID.cstring = PLACEHOLDER; - options.username.cstring = PLACEHOLDER; - options.password.cstring = PLACEHOLDER; - options.keepAliveInterval = 10; - options.willFlag = 0x1; - options.will.topicName.cstring = PLACEHOLDER; - options.will.message.cstring = PLACEHOLDER; - options.will.retained = 0x1; - - if (client->connect(options) != 0) { + if (client->connect(uuid, MQTT_USER, MQTT_PASSWORD) != 0) { // set status to ON status = ON; - addProperty(status, WRITE); - // execute first poll() to syncronize the "manifest" - poll(); + addProperty(status, READ); // subscribe to "general" topic - client->subscribe("placeholder", MQTT::QOS0, NULL); + client->subscribe(GENERAL_TOPIC); return true; } return false; } -void ArduinoCloudThing::publish() { +void ArduinoCloudThing::publish(CborDynamicOutput& output) { bool retained = false; - CborDynamicOutput output; - CborWriter writer(output); + unsigned char *buf = output.getData(); - writer.writeTag(1); +#ifdef TESTING_PROTOCOL + decode(buf, output.getSize()); +#endif + + client->publish(GENERAL_TOPIC, (const char*)buf, output.getSize()); for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); - p->append(writer); + p->updateShadow(); } - - unsigned char *buf = output.getData(); - - decodeCBORData(buf, output.getSize()); - - MQTT::Message message; - message.qos = MQTT::QOS0; - message.retained = retained; - message.dup = false; - message.payload = (void*)buf; - message.payloadlen = output.getSize(); - client->publish(topic, message); } // Reconnect to the mqtt broker int ArduinoCloudThing::poll() { -#ifndef TESTING - if (!client->isConnected()){ +#ifndef TESTING_PROTOCOL + if (!client->connected()){ if (!connect()){ return 0; } } - if(!network.connected() && client->isConnected()) { - client->disconnect(); - } #endif // check if backing storage and cloud has diverged - int diff_in = 0; - int diff_out = 1; - checkNewData(&diff_in , &diff_out); - if (diff_out > 0) { - publish(); + int diff = 0; + CborDynamicOutput output; + diff = checkNewData(output); + if (diff > 0) { + compress(output, diff); + publish(output); } - if (diff_in > 0) { - update(); + +#ifdef DEBUG_MEMORY + PrintFreeRam(); +#endif + + return diff; +} + +void ArduinoCloudThing::compress(CborDynamicOutput& output, int howMany) { + + CborWriter writer(output); + + writer.writeArray(howMany); + + for (int i = 0; i < list.size(); i++) { + ArduinoCloudPropertyGeneric *p = list.get(i); + if (p->newData()) { + p->append(writer); + } } - client->yield(); - return diff_in; } -void ArduinoCloudThing::checkNewData(int* new_data_in, int* new_data_out) { +int ArduinoCloudThing::checkNewData(CborDynamicOutput& output) { + int counter = 0; + for (int i = 0; i < list.size(); i++) { + ArduinoCloudPropertyGeneric *p = list.get(i); + if (p->newData()) { + counter++; + } + } + return counter; +} +bool ArduinoCloudThing::exists(String &name) { + for (int i = 0; i < list.size(); i++) { + ArduinoCloudPropertyGeneric *p = list.get(i); + if (p->getName() == name) { + return true; + } + } + return false; } void ArduinoCloudThing::addPropertyReal(int& property, String name, permissionType permission) { + if (exists(name)) { + return; + } ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name, permission); list.add(thing); } void ArduinoCloudThing::addPropertyReal(bool& property, String name, permissionType permission) { + if (exists(name)) { + return; + } ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name, permission); list.add(thing); } void ArduinoCloudThing::addPropertyReal(float& property, String name, permissionType permission) { + if (exists(name)) { + return; + } ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name, permission); list.add(thing); } - -ArduinoCloudThing ArduinoCloudThing::callback(MQTT::MessageData& messageData) { - MQTT::Message &message = messageData.message; - // null terminate topic to create String object - - // unwrap message into a structure - uint8_t * payload = (uint8_t*)message.payload; - decodeCBORData(payload, message.payloadlen); - - return *this; +void ArduinoCloudThing::callback(MQTTClient *client, char topic[], char bytes[], int length) { + reinterpret_cast(client->getParent())->decode((uint8_t *)bytes, length); } -void ArduinoCloudThing::decodeCBORData(uint8_t * payload, size_t length) { - list_shadow.clear(); - +void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { CborInput input(payload, length); CborReader reader(input); - CborPropertyListener listener(list_shadow); + CborPropertyListener listener(&list); reader.SetListener(listener); reader.Run(); } void CborPropertyListener::OnInteger(int32_t value) { - printf("integer: %d\n", value); + if (currentListIndex < 0) { + return; + } + reinterpret_cast*>(list->get(currentListIndex))->write(value); } void CborPropertyListener::OnBytes(unsigned char *data, unsigned int size) { @@ -164,25 +213,58 @@ void CborPropertyListener::OnBytes(unsigned char *data, unsigned int size) { } void CborPropertyListener::OnString(String &str) { - printf("string: '%.*s'\n", (int)str.length(), str.c_str()); + // if tag arrived, search a string with the same name in the list + if (newElement == true) { + newElement = false; + for (int i = 0; i < list->size(); i++) { + ArduinoCloudPropertyGeneric *p = list->get(i); + if (p->getName() == str) { + currentListIndex = i; + break; + } + if (i == list->size()) { + Serial.println("Property not found, skipping"); + currentListIndex = -1; + } + } + } else { + if (currentListIndex < 0) { + return; + } + reinterpret_cast*>(list->get(currentListIndex))->write(str); + } } void CborPropertyListener::OnArray(unsigned int size) { - printf("array: %d\n", size); + + // prepare for new properties to arrive + if (justStarted == true) { + list_size = size; + justStarted = false; + } } void CborPropertyListener::OnMap(unsigned int size) { - printf("map: %d\n", size); } -void CborPropertyListener::OnTag(unsigned int tag) { - printf("tag: %d\n", tag); +void CborPropertyListener::OnTag(uint32_t tag) { + newElement = true; + list_size--; + if (list_size < 0) { + Serial.println("problem, we got more properties than advertised"); + } } -void CborPropertyListener::OnSpecial(unsigned int code) { - printf("special: %d\n", code); +void CborPropertyListener::OnSpecial(uint32_t code) { + + if (currentListIndex < 0) { + return; + } + if (list->get(currentListIndex)->getPermission() != code) { + Serial.println("permission changed, updating"); + list->get(currentListIndex)->setPermission((permissionType)code); + } } void CborPropertyListener::OnError(const char *error) { - printf("error: %s\n", error); } \ No newline at end of file diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 727a19609..36fe33c82 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -3,9 +3,7 @@ #include #include -#include "lib/mqtt/MQTTClient.h" -#include "lib/Network.h" -#include "lib/Timer.h" +#include #include #include #include @@ -14,8 +12,9 @@ #define MQTT_BUFFER_SIZE 256 #endif -#define Serial DebugSerial -#define TESTING +//#define TESTING_PROTOCOL +#define DEBUG_MEMORY +#define USE_ARDUINO_CLOUD //#define MQTTCLIENT_QOS1 0 //#define MQTTCLIENT_QOS2 0 @@ -35,7 +34,12 @@ class ArduinoCloudPropertyGeneric { public: virtual void append(CborWriter &cbor) = 0; - virtual const char* getName() = 0; + virtual String& getName() = 0; + virtual void setName(String _name) = 0; + virtual void setPermission(permissionType _permission) = 0; + virtual permissionType getPermission() = 0; + virtual bool newData() = 0; + virtual void updateShadow() = 0; }; template @@ -53,23 +57,48 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric return false; } + void updateShadow() { + shadow_property = property; + } + T read() { if (permission != WRITE) { return property; } } - const char* getName() { - return name.c_str(); + String& getName() { + return name; + } + + void setName(String _name) { + name = _name; + } + + void setTag(int _tag) { + tag = _tag; + } + + void setPermission(permissionType _permission) { + permission = _permission; + } + + permissionType getPermission() { + return permission; } void appendValue(CborWriter &cbor); void append(CborWriter &cbor) { - cbor.writeArray(3); - appendValue(cbor); - cbor.writeInt(permission); + writer.writeArray(4); + cbor.writeTag(tag); cbor.writeString(name); + appendValue(cbor); + cbor.writeSpecial(permission); + } + + bool newData() { + return (property != shadow_property); } inline bool operator==(const ArduinoCloudProperty& rhs){ @@ -78,8 +107,11 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric protected: T& property; + T shadow_property; String name; + int tag; permissionType permission; + static int tagIndex; }; template <> @@ -120,32 +152,33 @@ class ArduinoCloudThing { void addPropertyReal(bool& property, String name, permissionType permission); void addPropertyReal(float& property, String name, permissionType permission); void addPropertyReal(void* property, String name, permissionType permission); + void addPropertyReal(String property, String name, permissionType permission); // poll should return > 0 if something has changed int poll(); private: - ArduinoCloudThing callback(MQTT::MessageData& messageData); + static void callback(MQTTClient *client, char topic[], char bytes[], int length); bool connect(); - void publish(); + void publish(CborDynamicOutput& output); void update(); - void checkNewData(int* new_data_in, int* new_data_out); - void decodeCBORData(uint8_t * payload, size_t length); + int checkNewData(CborDynamicOutput& output); + void compress(CborDynamicOutput& output, int howMany); + void decode(uint8_t * payload, size_t length); + + bool exists(String &name); bool status = OFF; - char* topic; + char uuid[33]; LinkedList list; - LinkedList list_shadow; - Network network; - MQTT::Client * client; - MQTTPacket_connectData options; + MQTTClient* client; }; class CborPropertyListener : public CborListener { public: - CborPropertyListener(LinkedList _list) : list(_list) {} + CborPropertyListener(LinkedList *_list) : list(_list) {} void OnInteger(int32_t value); void OnBytes(unsigned char *data, unsigned int size); void OnString(String &str); @@ -154,7 +187,11 @@ class CborPropertyListener : public CborListener { void OnTag(uint32_t tag); void OnSpecial(uint32_t code); void OnError(const char *error); - LinkedList list; + LinkedList *list; + int currentListIndex; + bool justStarted = true; + int list_size = 0; + bool newElement = false; }; #endif diff --git a/lib/Network.cpp b/lib/Network.cpp deleted file mode 100644 index 8be13f509..000000000 --- a/lib/Network.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include "Network.h" - -void Network::setClient(Client * _client) { - this->client = _client; -} - -int Network::connect(char* hostname, int port) { - return this->client->connect(hostname, port); -} - -int Network::read(unsigned char* buffer, int len, int timeout) { - if(!client->connected()) { - return -1; // return an error - } - - if(this->client->available() == 0) { - return 0; // nothing to read - } - - this->client->setTimeout(timeout); - return this->client->readBytes(buffer, len); -} -int Network::write(unsigned char* buffer, int len, int timeout) { - if(!client->connected()) { - return -1; // return an error - } - - client->setTimeout(timeout); - return client->write((uint8_t*)buffer, len); -} - -bool Network::connected() { - return client->connected(); -} -int Network::disconnect() { - client->stop(); - return 0; -} diff --git a/lib/Network.h b/lib/Network.h deleted file mode 100644 index b6927bc40..000000000 --- a/lib/Network.h +++ /dev/null @@ -1,13 +0,0 @@ -#include - -class Network { -private: - Client* client; -public: - void setClient(Client * client); - int connect(char* hostname, int port); - int read(unsigned char* buffer, int len, int timeout); - int write(unsigned char* buffer, int len, int timeout); - bool connected(); - int disconnect(); -}; diff --git a/lib/Timer.cpp b/lib/Timer.cpp deleted file mode 100644 index 37947a89f..000000000 --- a/lib/Timer.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include -#include "Timer.h" - -Timer::Timer() { - this->interval_end_ms = 0L; -} - -Timer::Timer(int ms) { - countdown_ms(ms); -} - -bool Timer::expired() { - return (interval_end_ms > 0L) && (millis() >= interval_end_ms); -} - -void Timer::countdown_ms(unsigned long ms) { - interval_end_ms = millis() + ms; -} - -void Timer::countdown(int seconds) { - countdown_ms((unsigned long)seconds * 1000L); -} - -int Timer::left_ms() { - return interval_end_ms - millis(); -} diff --git a/lib/Timer.h b/lib/Timer.h deleted file mode 100644 index 67844b8e0..000000000 --- a/lib/Timer.h +++ /dev/null @@ -1,11 +0,0 @@ -class Timer { -public: - Timer(); - Timer(int ms); - bool expired(); - void countdown_ms(unsigned long ms); - void countdown(int seconds); - int left_ms(); -private: - unsigned long interval_end_ms; -}; diff --git a/lib/mqtt/FP.h b/lib/mqtt/FP.h deleted file mode 100644 index 5ce75dc5a..000000000 --- a/lib/mqtt/FP.h +++ /dev/null @@ -1,208 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2013, 2014 - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Sam Grove - initial API and implementation and/or initial documentation - * Ian Craggs - added attached and detached member functions - * Sam Grove - removed need for FP.cpp - *******************************************************************************/ - -#ifndef FP_H -#define FP_H - -/** Example using the FP Class with global functions - * @code - * #include "mbed.h" - * #include "FP.h" - * - * FPfp; - * DigitalOut myled(LED1); - * - * void handler(bool value) - * { - * myled = value; - * return; - * } - * - * int main() - * { - * fp.attach(&handler); - * - * while(1) - * { - * fp(1); - * wait(0.2); - * fp(0); - * wait(0.2); - * } - * } - * @endcode - */ - -/** Example using the FP Class with different class member functions - * @code - * #include "mbed.h" - * #include "FP.h" - * - * FPfp; - * DigitalOut myled(LED4); - * - * class Wrapper - * { - * public: - * Wrapper(){} - * - * void handler(bool value) - * { - * myled = value; - * return; - * } - * }; - * - * int main() - * { - * Wrapper wrapped; - * fp.attach(&wrapped, &Wrapper::handler); - * - * while(1) - * { - * fp(1); - * wait(0.2); - * fp(0); - * wait(0.2); - * } - * } - * @endcode - */ - -/** Example using the FP Class with member FP and member function -* @code -* #include "mbed.h" -* #include "FP.h" -* -* DigitalOut myled(LED2); -* -* class Wrapper -* { -* public: -* Wrapper() -* { -* fp.attach(this, &Wrapper::handler); -* } -* -* void handler(bool value) -* { -* myled = value; -* return; -* } -* -* FPfp; -* }; -* -* int main() -* { -* Wrapper wrapped; -* -* while(1) -* { -* wrapped.fp(1); -* wait(0.2); -* wrapped.fp(0); -* wait(0.2); -* } -* } -* @endcode -*/ - -/** - * @class FP - * @brief API for managing Function Pointers - */ -template -class FP -{ -public: - /** Create the FP object - only one callback can be attached to the object, that is - * a member function or a global function, not both at the same time - */ - FP() - { - obj_callback = 0; - c_callback = 0; - } - - /** Add a callback function to the object - * @param item - Address of the initialized object - * @param member - Address of the member function (dont forget the scope that the function is defined in) - */ - template - void attach(T *item, retT (T::*method)(argT)) - { - obj_callback = (FPtrDummy *)(item); - method_callback = (retT (FPtrDummy::*)(argT))(method); - return; - } - - /** Add a callback function to the object - * @param function - The address of a globally defined function - */ - void attach(retT (*function)(argT)) - { - c_callback = function; - } - - /** Invoke the function attached to the class - * @param arg - An argument that is passed into the function handler that is called - * @return The return from the function hanlder called by this class - */ - retT operator()(argT arg) const - { - if( 0 != c_callback ) { - return obj_callback ? (obj_callback->*method_callback)(arg) : (*c_callback)(arg); - } - // return (retT)0; - } - - /** Determine if an callback is currently hooked - * @return 1 if a method is hooked, 0 otherwise - */ - bool attached() - { - return obj_callback || c_callback; - } - - /** Release a function from the callback hook - */ - void detach() - { - obj_callback = 0; - c_callback = 0; - } - -private: - - // empty type used for casting - class FPtrDummy; - - FPtrDummy *obj_callback; - - /** - * @union Funciton - * @brief Member or global callback function - */ - union { - retT (*c_callback)(argT); /*!< Footprint for a global function */ - retT (FPtrDummy::*method_callback)(argT); /*!< Footprint for a member function */ - }; -}; - -#endif diff --git a/lib/mqtt/MQTTClient.h b/lib/mqtt/MQTTClient.h deleted file mode 100644 index 08621fccd..000000000 --- a/lib/mqtt/MQTTClient.h +++ /dev/null @@ -1,910 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014, 2015 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - * Ian Craggs - fix for bug 458512 - QoS 2 messages - *******************************************************************************/ - -#if !defined(MQTTCLIENT_H) -#define MQTTCLIENT_H - -#include "FP.h" -#include "MQTTPacket.h" -#include "stdio.h" - -#if !defined(MQTTCLIENT_QOS1) - #define MQTTCLIENT_QOS1 1 -#endif -#if !defined(MQTTCLIENT_QOS2) - #define MQTTCLIENT_QOS2 0 -#endif - -namespace MQTT -{ - - -enum QoS { QOS0, QOS1, QOS2 }; - -// all failure return codes must be negative -enum returnCode { BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 }; - - -struct Message -{ - enum QoS qos; - bool retained; - bool dup; - unsigned short id; - void *payload; - size_t payloadlen; -}; - - -struct MessageData -{ - MessageData(MQTTString &aTopicName, struct Message &aMessage) : message(aMessage), topicName(aTopicName) - { } - - struct Message &message; - MQTTString &topicName; -}; - - -class PacketId -{ -public: - PacketId() - { - next = 0; - } - - int getNext() - { - return next = (next == MAX_PACKET_ID) ? 1 : ++next; - } - -private: - static const int MAX_PACKET_ID = 65535; - int next; -}; - -/** - * @class Client - * @brief blocking, non-threaded MQTT client API - * - * This version of the API blocks on all method calls, until they are complete. This means that only one - * MQTT request can be in process at any one time. - * @param Network a network class which supports send, receive - * @param Timer a timer class with the methods: - */ -template -class Client -{ - -public: - - typedef void (*messageHandler)(MessageData&); - FP defaultMessageHandler; - - /** Construct the client - * @param network - pointer to an instance of the Network class - must be connected to the endpoint - * before calling MQTT connect - * @param limits an instance of the Limit class - to alter limits as required - */ - Client(Network& network, unsigned int command_timeout_ms = 30000); - - /** Set the default message handling callback - used for any message which does not match a subscription message handler - * @param mh - pointer to the callback function - */ - void setDefaultMessageHandler(messageHandler mh) - { - // defaultMessageHandler.attach(mh); - } - - /** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack - * The nework object must be connected to the network endpoint before calling this - * Default connect options are used - * @return success code - - */ - int connect(); - - /** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack - * The nework object must be connected to the network endpoint before calling this - * @param options - connect options - * @return success code - - */ - int connect(MQTTPacket_connectData& options); - - /** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs - * @param topic - the topic to publish to - * @param message - the message to send - * @return success code - - */ - int publish(const char* topicName, Message& message); - - /** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs - * @param topic - the topic to publish to - * @param payload - the data to send - * @param payloadlen - the length of the data - * @param qos - the QoS to send the publish at - * @param retained - whether the message should be retained - * @return success code - - */ - int publish(const char* topicName, void* payload, size_t payloadlen, enum QoS qos = QOS0, bool retained = false); - - /** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs - * @param topic - the topic to publish to - * @param payload - the data to send - * @param payloadlen - the length of the data - * @param id - the packet id used - returned - * @param qos - the QoS to send the publish at - * @param retained - whether the message should be retained - * @return success code - - */ - int publish(const char* topicName, void* payload, size_t payloadlen, unsigned short& id, enum QoS qos = QOS1, bool retained = false); - - /** MQTT Subscribe - send an MQTT subscribe packet and wait for the suback - * @param topicFilter - a topic pattern which can include wildcards - * @param qos - the MQTT QoS to subscribe at - * @param mh - the callback function to be invoked when a message is received for this subscription - * @return success code - - */ - int subscribe(const char* topicFilter, enum QoS qos, messageHandler mh); - - /** MQTT Unsubscribe - send an MQTT unsubscribe packet and wait for the unsuback - * @param topicFilter - a topic pattern which can include wildcards - * @return success code - - */ - int unsubscribe(const char* topicFilter); - - /** MQTT Disconnect - send an MQTT disconnect packet, and clean up any state - * @return success code - - */ - int disconnect(); - - /** A call to this API must be made within the keepAlive interval to keep the MQTT connection alive - * yield can be called if no other MQTT operation is needed. This will also allow messages to be - * received. - * @param timeout_ms the time to wait, in milliseconds - * @return success code - on failure, this means the client has disconnected - */ - int yield(unsigned long timeout_ms = 1000L); - - /** Is the client connected? - * @return flag - is the client connected or not? - */ - bool isConnected() - { - return isconnected; - } - -private: - - int cycle(Timer& timer); - int waitfor(int packet_type, Timer& timer); - int keepalive(); - int publish(int len, Timer& timer, enum QoS qos); - - int decodePacket(int* value, int timeout); - int readPacket(Timer& timer); - int sendPacket(int length, Timer& timer); - int deliverMessage(MQTTString& topicName, Message& message); - bool isTopicMatched(char* topicFilter, MQTTString& topicName); - - Network& ipstack; - unsigned long command_timeout_ms; - - unsigned char sendbuf[MAX_MQTT_PACKET_SIZE]; - unsigned char readbuf[MAX_MQTT_PACKET_SIZE]; - - Timer last_sent, last_received; - unsigned int keepAliveInterval; - bool ping_outstanding; - bool cleansession; - - PacketId packetid; - - struct MessageHandlers - { - const char* topicFilter; - FP fp; - } messageHandlers[MAX_MESSAGE_HANDLERS]; // Message handlers are indexed by subscription topic - - - bool isconnected; - -#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 - unsigned char pubbuf[MAX_MQTT_PACKET_SIZE]; // store the last publish for sending on reconnect - int inflightLen; - unsigned short inflightMsgid; - enum QoS inflightQoS; -#endif - -#if MQTTCLIENT_QOS2 - bool pubrel; - #if !defined(MAX_INCOMING_QOS2_MESSAGES) - #define MAX_INCOMING_QOS2_MESSAGES 10 - #endif - unsigned short incomingQoS2messages[MAX_INCOMING_QOS2_MESSAGES]; - bool isQoS2msgidFree(unsigned short id); - bool useQoS2msgid(unsigned short id); - void freeQoS2msgid(unsigned short id); -#endif - -}; - -} - - -template -MQTT::Client::Client(Network& network, unsigned int command_timeout_ms) : ipstack(network), packetid() -{ - last_sent = Timer(); - last_received = Timer(); - ping_outstanding = false; - for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) - messageHandlers[i].topicFilter = 0; - this->command_timeout_ms = command_timeout_ms; - isconnected = false; - -#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 - inflightMsgid = 0; - inflightQoS = QOS0; -#endif - - -#if MQTTCLIENT_QOS2 - pubrel = false; - for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) - incomingQoS2messages[i] = 0; -#endif -} - -#if MQTTCLIENT_QOS2 -template -bool MQTT::Client::isQoS2msgidFree(unsigned short id) -{ - for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) - { - if (incomingQoS2messages[i] == id) - return false; - } - return true; -} - - -template -bool MQTT::Client::useQoS2msgid(unsigned short id) -{ - for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) - { - if (incomingQoS2messages[i] == 0) - { - incomingQoS2messages[i] = id; - return true; - } - } - return false; -} - - -template -void MQTT::Client::freeQoS2msgid(unsigned short id) -{ - for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i) - { - if (incomingQoS2messages[i] == id) - { - incomingQoS2messages[i] = 0; - return; - } - } -} -#endif - - -template -int MQTT::Client::sendPacket(int length, Timer& timer) -{ - int rc = FAILURE, - sent = 0; - - while (sent < length && !timer.expired()) - { - rc = ipstack.write(&sendbuf[sent], length, timer.left_ms()); - if (rc < 0) // there was an error writing the data - break; - sent += rc; - } - if (sent == length) - { - if (this->keepAliveInterval > 0) - last_sent.countdown(this->keepAliveInterval); // record the fact that we have successfully sent the packet - rc = SUCCESS; - } - else - rc = FAILURE; - -#if defined(MQTT_DEBUG) - char printbuf[150]; - DEBUG("Rc %d from sending packet %s\n", rc, MQTTFormat_toServerString(printbuf, sizeof(printbuf), sendbuf, length)); -#endif - return rc; -} - - -template -int MQTT::Client::decodePacket(int* value, int timeout) -{ - unsigned char c; - int multiplier = 1; - int len = 0; - const int MAX_NO_OF_REMAINING_LENGTH_BYTES = 4; - - *value = 0; - do - { - int rc = MQTTPACKET_READ_ERROR; - - if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) - { - rc = MQTTPACKET_READ_ERROR; /* bad data */ - goto exit; - } - rc = ipstack.read(&c, 1, timeout); - if (rc != 1) - goto exit; - *value += (c & 127) * multiplier; - multiplier *= 128; - } while ((c & 128) != 0); -exit: - return len; -} - - -/** - * If any read fails in this method, then we should disconnect from the network, as on reconnect - * the packets can be retried. - * @param timeout the max time to wait for the packet read to complete, in milliseconds - * @return the MQTT packet type, or -1 if none - */ -template -int MQTT::Client::readPacket(Timer& timer) -{ - int rc = FAILURE; - MQTTHeader header = {0}; - int len = 0; - int rem_len = 0; - int read = 0; - - /* 1. read the header byte. This has the packet type in it */ - if (ipstack.read(readbuf, 1, timer.left_ms()) != 1) - goto exit; - - len = 1; - /* 2. read the remaining length. This is variable in itself */ - decodePacket(&rem_len, timer.left_ms()); - len += MQTTPacket_encode(readbuf + 1, rem_len); /* put the original remaining length into the buffer */ - - if (rem_len > (MAX_MQTT_PACKET_SIZE - len)) - { - rc = BUFFER_OVERFLOW; - goto exit; - } - - /* 3. read the rest of the buffer using a callback to supply the rest of the data */ - if (rem_len > 0 && (ipstack.read(readbuf + len, rem_len, timer.left_ms()) != rem_len)) - goto exit; - - header.byte = readbuf[0]; - rc = header.bits.type; - if (this->keepAliveInterval > 0) - last_received.countdown(this->keepAliveInterval); // record the fact that we have successfully received a packet -exit: - -#if defined(MQTT_DEBUG) - if (rc >= 0) - { - char printbuf[50]; - DEBUG("Rc %d from receiving packet %s\n", rc, MQTTFormat_toClientString(printbuf, sizeof(printbuf), readbuf, len)); - } -#endif - return rc; -} - - -// assume topic filter and name is in correct format -// # can only be at end -// + and # can only be next to separator -template -bool MQTT::Client::isTopicMatched(char* topicFilter, MQTTString& topicName) -{ - char* curf = topicFilter; - char* curn = topicName.lenstring.data; - char* curn_end = curn + topicName.lenstring.len; - - while (*curf && curn < curn_end) - { - if (*curn == '/' && *curf != '/') - break; - if (*curf != '+' && *curf != '#' && *curf != *curn) - break; - if (*curf == '+') - { // skip until we meet the next separator, or end of string - char* nextpos = curn + 1; - while (nextpos < curn_end && *nextpos != '/') - nextpos = ++curn + 1; - } - else if (*curf == '#') - curn = curn_end - 1; // skip until end of string - curf++; - curn++; - }; - - return (curn == curn_end) && (*curf == '\0'); -} - - - -template -int MQTT::Client::deliverMessage(MQTTString& topicName, Message& message) -{ - int rc = FAILURE; - - // we have to find the right message handler - indexed by topic - for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) - { - if (messageHandlers[i].topicFilter != 0 && (MQTTPacket_equals(&topicName, (char*)messageHandlers[i].topicFilter) || - isTopicMatched((char*)messageHandlers[i].topicFilter, topicName))) - { - if (messageHandlers[i].fp.attached()) - { - MessageData md(topicName, message); - messageHandlers[i].fp(md); - rc = SUCCESS; - } - } - } - - if (rc == FAILURE && defaultMessageHandler.attached()) - { - MessageData md(topicName, message); - defaultMessageHandler(md); - rc = SUCCESS; - } - - return rc; -} - - - -template -int MQTT::Client::yield(unsigned long timeout_ms) -{ - int rc = SUCCESS; - Timer timer = Timer(); - - timer.countdown_ms(timeout_ms); - while (!timer.expired()) - { - if (cycle(timer) < 0) - { - rc = FAILURE; - break; - } - } - - return rc; -} - - -template -int MQTT::Client::cycle(Timer& timer) -{ - /* get one piece of work off the wire and one pass through */ - - // read the socket, see what work is due - int packet_type = readPacket(timer); - - int len = 0, - rc = SUCCESS; - - switch (packet_type) - { - case FAILURE: - case BUFFER_OVERFLOW: - rc = packet_type; - break; - case CONNACK: - case PUBACK: - case SUBACK: - break; - case PUBLISH: - { - MQTTString topicName = MQTTString_initializer; - Message msg; - if (MQTTDeserialize_publish((unsigned char*)&msg.dup, (int*)&msg.qos, (unsigned char*)&msg.retained, (unsigned short*)&msg.id, &topicName, - (unsigned char**)&msg.payload, (int*)&msg.payloadlen, readbuf, MAX_MQTT_PACKET_SIZE) != 1) - goto exit; -#if MQTTCLIENT_QOS2 - if (msg.qos != QOS2) -#endif - deliverMessage(topicName, msg); -#if MQTTCLIENT_QOS2 - else if (isQoS2msgidFree(msg.id)) - { - if (useQoS2msgid(msg.id)) - deliverMessage(topicName, msg); - else - WARN("Maximum number of incoming QoS2 messages exceeded"); - } -#endif -#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 - if (msg.qos != QOS0) - { - if (msg.qos == QOS1) - len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBACK, 0, msg.id); - else if (msg.qos == QOS2) - len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREC, 0, msg.id); - if (len <= 0) - rc = FAILURE; - else - rc = sendPacket(len, timer); - if (rc == FAILURE) - goto exit; // there was a problem - } - break; -#endif - } -#if MQTTCLIENT_QOS2 - case PUBREC: - case PUBREL: - unsigned short mypacketid; - unsigned char dup, type; - if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1) - rc = FAILURE; - else if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, - (packet_type == PUBREC) ? PUBREL : PUBCOMP, 0, mypacketid)) <= 0) - rc = FAILURE; - else if ((rc = sendPacket(len, timer)) != SUCCESS) // send the PUBREL packet - rc = FAILURE; // there was a problem - if (rc == FAILURE) - goto exit; // there was a problem - if (packet_type == PUBREL) - freeQoS2msgid(mypacketid); - break; - - case PUBCOMP: - break; -#endif - case PINGRESP: - ping_outstanding = false; - break; - } - keepalive(); -exit: - if (rc == SUCCESS) - rc = packet_type; - return rc; -} - - -template -int MQTT::Client::keepalive() -{ - int rc = FAILURE; - - if (keepAliveInterval == 0) - { - rc = SUCCESS; - goto exit; - } - - if (last_sent.expired() || last_received.expired()) - { - if (!ping_outstanding) - { - Timer timer = Timer(1000); - int len = MQTTSerialize_pingreq(sendbuf, MAX_MQTT_PACKET_SIZE); - if (len > 0 && (rc = sendPacket(len, timer)) == SUCCESS) // send the ping packet - ping_outstanding = true; - } - } - -exit: - return rc; -} - - -// only used in single-threaded mode where one command at a time is in process -template -int MQTT::Client::waitfor(int packet_type, Timer& timer) -{ - int rc = FAILURE; - - do - { - if (timer.expired()) - break; // we timed out - } - while ((rc = cycle(timer)) != packet_type); - - return rc; -} - - -template -int MQTT::Client::connect(MQTTPacket_connectData& options) -{ - Timer connect_timer = Timer(command_timeout_ms); - int rc = FAILURE; - int len = 0; - - if (isconnected) // don't send connect packet again if we are already connected - goto exit; - - this->keepAliveInterval = options.keepAliveInterval; - this->cleansession = options.cleansession; - if ((len = MQTTSerialize_connect(sendbuf, MAX_MQTT_PACKET_SIZE, &options)) <= 0) - goto exit; - if ((rc = sendPacket(len, connect_timer)) != SUCCESS) // send the connect packet - goto exit; // there was a problem - - if (this->keepAliveInterval > 0) - last_received.countdown(this->keepAliveInterval); - // this will be a blocking call, wait for the connack - if (waitfor(CONNACK, connect_timer) == CONNACK) - { - unsigned char connack_rc = 255; - bool sessionPresent = false; - if (MQTTDeserialize_connack((unsigned char*)&sessionPresent, &connack_rc, readbuf, MAX_MQTT_PACKET_SIZE) == 1) - rc = connack_rc; - else - rc = FAILURE; - } - else - rc = FAILURE; - -#if MQTTCLIENT_QOS2 - // resend an inflight publish - if (inflightMsgid >0 && inflightQoS == QOS2 && pubrel) - { - if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREL, 0, inflightMsgid)) <= 0) - rc = FAILURE; - else - rc = publish(len, connect_timer, inflightQoS); - } - else -#endif -#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 - if (inflightMsgid > 0) - { - memcpy(sendbuf, pubbuf, MAX_MQTT_PACKET_SIZE); - rc = publish(inflightLen, connect_timer, inflightQoS); - } -#endif - -exit: - if (rc == SUCCESS) - isconnected = true; - return rc; -} - - -template -int MQTT::Client::connect() -{ - MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer; - return connect(default_options); -} - - -template -int MQTT::Client::subscribe(const char* topicFilter, enum QoS qos, messageHandler messageHandler) -{ - int rc = FAILURE; - Timer timer = Timer(command_timeout_ms); - int len = 0; - MQTTString topic = {(char*)topicFilter, 0, 0}; - - if (!isconnected) - goto exit; - - len = MQTTSerialize_subscribe(sendbuf, MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic, (int*)&qos); - if (len <= 0) - goto exit; - if ((rc = sendPacket(len, timer)) != SUCCESS) // send the subscribe packet - goto exit; // there was a problem - - if (waitfor(SUBACK, timer) == SUBACK) // wait for suback - { - int count = 0, grantedQoS = -1; - unsigned short mypacketid; - if (MQTTDeserialize_suback(&mypacketid, 1, &count, &grantedQoS, readbuf, MAX_MQTT_PACKET_SIZE) == 1) - rc = grantedQoS; // 0, 1, 2 or 0x80 - if (rc != 0x80) - { - for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) - { - if (messageHandlers[i].topicFilter == 0) - { - messageHandlers[i].topicFilter = topicFilter; - messageHandlers[i].fp.attach(messageHandler); - rc = 0; - break; - } - } - } - } - else - rc = FAILURE; - -exit: - if (rc != SUCCESS) - isconnected = false; - return rc; -} - - -template -int MQTT::Client::unsubscribe(const char* topicFilter) -{ - int rc = FAILURE; - Timer timer = Timer(command_timeout_ms); - MQTTString topic = {(char*)topicFilter, 0, 0}; - int len = 0; - - if (!isconnected) - goto exit; - - if ((len = MQTTSerialize_unsubscribe(sendbuf, MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic)) <= 0) - goto exit; - if ((rc = sendPacket(len, timer)) != SUCCESS) // send the unsubscribe packet - goto exit; // there was a problem - - if (waitfor(UNSUBACK, timer) == UNSUBACK) - { - unsigned short mypacketid; // should be the same as the packetid above - if (MQTTDeserialize_unsuback(&mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) == 1) - rc = 0; - } - else - rc = FAILURE; - -exit: - if (rc != SUCCESS) - isconnected = false; - return rc; -} - - -template -int MQTT::Client::publish(int len, Timer& timer, enum QoS qos) -{ - int rc; - - if ((rc = sendPacket(len, timer)) != SUCCESS) // send the publish packet - goto exit; // there was a problem - -#if MQTTCLIENT_QOS1 - if (qos == QOS1) - { - if (waitfor(PUBACK, timer) == PUBACK) - { - unsigned short mypacketid; - unsigned char dup, type; - if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1) - rc = FAILURE; - else if (inflightMsgid == mypacketid) - inflightMsgid = 0; - } - else - rc = FAILURE; - } -#elif MQTTCLIENT_QOS2 - else if (qos == QOS2) - { - if (waitfor(PUBCOMP, timer) == PUBCOMP) - { - unsigned short mypacketid; - unsigned char dup, type; - if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1) - rc = FAILURE; - else if (inflightMsgid == mypacketid) - inflightMsgid = 0; - } - else - rc = FAILURE; - } -#endif - -exit: - if (rc != SUCCESS) - isconnected = false; - return rc; -} - - - -template -int MQTT::Client::publish(const char* topicName, void* payload, size_t payloadlen, unsigned short& id, enum QoS qos, bool retained) -{ - int rc = FAILURE; - Timer timer = Timer(command_timeout_ms); - MQTTString topicString = MQTTString_initializer; - int len = 0; - - if (!isconnected) - goto exit; - - topicString.cstring = (char*)topicName; - -#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 - if (qos == QOS1 || qos == QOS2) - id = packetid.getNext(); -#endif - - len = MQTTSerialize_publish(sendbuf, MAX_MQTT_PACKET_SIZE, 0, qos, retained, id, - topicString, (unsigned char*)payload, payloadlen); - if (len <= 0) - goto exit; - -#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2 - if (!cleansession) - { - memcpy(pubbuf, sendbuf, len); - inflightMsgid = id; - inflightLen = len; - inflightQoS = qos; -#if MQTTCLIENT_QOS2 - pubrel = false; -#endif - } -#endif - - rc = publish(len, timer, qos); -exit: - return rc; -} - - -template -int MQTT::Client::publish(const char* topicName, void* payload, size_t payloadlen, enum QoS qos, bool retained) -{ - unsigned short id = 0; // dummy - not used for anything - return publish(topicName, payload, payloadlen, id, qos, retained); -} - - -template -int MQTT::Client::publish(const char* topicName, Message& message) -{ - return publish(topicName, message.payload, message.payloadlen, message.qos, message.retained); -} - - -template -int MQTT::Client::disconnect() -{ - int rc = FAILURE; - Timer timer = Timer(command_timeout_ms); // we might wait for incomplete incoming publishes to complete - int len = MQTTSerialize_disconnect(sendbuf, MAX_MQTT_PACKET_SIZE); - if (len > 0) - rc = sendPacket(len, timer); // send the disconnect packet - - isconnected = false; - return rc; -} - - -#endif diff --git a/lib/mqtt/MQTTConnect.h b/lib/mqtt/MQTTConnect.h deleted file mode 100644 index db9025149..000000000 --- a/lib/mqtt/MQTTConnect.h +++ /dev/null @@ -1,136 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - * Xiang Rong - 442039 Add makefile to Embedded C client - *******************************************************************************/ - -#ifndef MQTTCONNECT_H_ -#define MQTTCONNECT_H_ - -#if !defined(DLLImport) - #define DLLImport -#endif -#if !defined(DLLExport) - #define DLLExport -#endif - - -typedef union -{ - unsigned char all; /**< all connect flags */ -#if defined(REVERSED) - struct - { - unsigned int username : 1; /**< 3.1 user name */ - unsigned int password : 1; /**< 3.1 password */ - unsigned int willRetain : 1; /**< will retain setting */ - unsigned int willQoS : 2; /**< will QoS value */ - unsigned int will : 1; /**< will flag */ - unsigned int cleansession : 1; /**< clean session flag */ - unsigned int : 1; /**< unused */ - } bits; -#else - struct - { - unsigned int : 1; /**< unused */ - unsigned int cleansession : 1; /**< cleansession flag */ - unsigned int will : 1; /**< will flag */ - unsigned int willQoS : 2; /**< will QoS value */ - unsigned int willRetain : 1; /**< will retain setting */ - unsigned int password : 1; /**< 3.1 password */ - unsigned int username : 1; /**< 3.1 user name */ - } bits; -#endif -} MQTTConnectFlags; /**< connect flags byte */ - - - -/** - * Defines the MQTT "Last Will and Testament" (LWT) settings for - * the connect packet. - */ -typedef struct -{ - /** The eyecatcher for this structure. must be MQTW. */ - char struct_id[4]; - /** The version number of this structure. Must be 0 */ - int struct_version; - /** The LWT topic to which the LWT message will be published. */ - MQTTString topicName; - /** The LWT payload. */ - MQTTString message; - /** - * The retained flag for the LWT message (see MQTTAsync_message.retained). - */ - unsigned char retained; - /** - * The quality of service setting for the LWT message (see - * MQTTAsync_message.qos and @ref qos). - */ - char qos; -} MQTTPacket_willOptions; - - -#define MQTTPacket_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 0, {NULL, {0, NULL}}, {NULL, {0, NULL}}, 0, 0 } - - -typedef struct -{ - /** The eyecatcher for this structure. must be MQTC. */ - char struct_id[4]; - /** The version number of this structure. Must be 0 */ - int struct_version; - /** Version of MQTT to be used. 3 = 3.1 4 = 3.1.1 - */ - unsigned char MQTTVersion; - MQTTString clientID; - unsigned short keepAliveInterval; - unsigned char cleansession; - unsigned char willFlag; - MQTTPacket_willOptions will; - MQTTString username; - MQTTString password; -} MQTTPacket_connectData; - -typedef union -{ - unsigned char all; /**< all connack flags */ -#if defined(REVERSED) - struct - { - unsigned int sessionpresent : 1; /**< session present flag */ - unsigned int : 7; /**< unused */ - } bits; -#else - struct - { - unsigned int : 7; /**< unused */ - unsigned int sessionpresent : 1; /**< session present flag */ - } bits; -#endif -} MQTTConnackFlags; /**< connack flags byte */ - -#define MQTTPacket_connectData_initializer { {'M', 'Q', 'T', 'C'}, 0, 4, {NULL, {0, NULL}}, 60, 1, 0, \ - MQTTPacket_willOptions_initializer, {NULL, {0, NULL}}, {NULL, {0, NULL}} } - -DLLExport int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options); -DLLExport int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len); - -DLLExport int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent); -DLLExport int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen); - -DLLExport int MQTTSerialize_disconnect(unsigned char* buf, int buflen); -DLLExport int MQTTSerialize_pingreq(unsigned char* buf, int buflen); - -#endif /* MQTTCONNECT_H_ */ diff --git a/lib/mqtt/MQTTConnectClient.c b/lib/mqtt/MQTTConnectClient.c deleted file mode 100644 index 8a9bc902e..000000000 --- a/lib/mqtt/MQTTConnectClient.c +++ /dev/null @@ -1,206 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - *******************************************************************************/ - -#include "MQTTPacket.h" - -#include - -/** - * Determines the length of the MQTT connect packet that would be produced using the supplied connect options. - * @param options the options to be used to build the connect packet - * @return the length of buffer needed to contain the serialized version of the packet - */ -int MQTTSerialize_connectLength(MQTTPacket_connectData* options) -{ - int len = 0; - - - if (options->MQTTVersion == 3) - len = 12; /* variable depending on MQTT or MQIsdp */ - else if (options->MQTTVersion == 4) - len = 10; - - len += MQTTstrlen(options->clientID)+2; - if (options->willFlag) - len += MQTTstrlen(options->will.topicName)+2 + MQTTstrlen(options->will.message)+2; - if (options->username.cstring || options->username.lenstring.data) - len += MQTTstrlen(options->username)+2; - if (options->password.cstring || options->password.lenstring.data) - len += MQTTstrlen(options->password)+2; - - return len; -} - - -/** - * Serializes the connect options into the buffer. - * @param buf the buffer into which the packet will be serialized - * @param len the length in bytes of the supplied buffer - * @param options the options to be used to build the connect packet - * @return serialized length, or error if 0 - */ -int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options) -{ - unsigned char *ptr = buf; - MQTTHeader header = {0}; - MQTTConnectFlags flags = {0}; - int len = 0; - int rc = -1; - - if (MQTTPacket_len(len = MQTTSerialize_connectLength(options)) > buflen) - { - rc = MQTTPACKET_BUFFER_TOO_SHORT; - goto exit; - } - - header.byte = 0; - header.bits.type = CONNECT; - writeChar(&ptr, header.byte); /* write header */ - - ptr += MQTTPacket_encode(ptr, len); /* write remaining length */ - - if (options->MQTTVersion == 4) - { - writeCString(&ptr, "MQTT"); - writeChar(&ptr, (char) 4); - } - else - { - writeCString(&ptr, "MQIsdp"); - writeChar(&ptr, (char) 3); - } - - flags.all = 0; - flags.bits.cleansession = options->cleansession; - flags.bits.will = (options->willFlag) ? 1 : 0; - if (flags.bits.will) - { - flags.bits.willQoS = options->will.qos; - flags.bits.willRetain = options->will.retained; - } - - if (options->username.cstring || options->username.lenstring.data) - flags.bits.username = 1; - if (options->password.cstring || options->password.lenstring.data) - flags.bits.password = 1; - - writeChar(&ptr, flags.all); - writeInt(&ptr, options->keepAliveInterval); - writeMQTTString(&ptr, options->clientID); - if (options->willFlag) - { - writeMQTTString(&ptr, options->will.topicName); - writeMQTTString(&ptr, options->will.message); - } - if (flags.bits.username) - writeMQTTString(&ptr, options->username); - if (flags.bits.password) - writeMQTTString(&ptr, options->password); - - rc = ptr - buf; - - exit: - return rc; -} - - -/** - * Deserializes the supplied (wire) buffer into connack data - return code - * @param sessionPresent the session present flag returned (only for MQTT 3.1.1) - * @param connack_rc returned integer value of the connack return code - * @param buf the raw buffer data, of the correct length determined by the remaining length field - * @param len the length in bytes of the data in the supplied buffer - * @return error code. 1 is success, 0 is failure - */ -int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen) -{ - MQTTHeader header = {0}; - unsigned char* curdata = buf; - unsigned char* enddata = NULL; - int rc = 0; - int mylen; - MQTTConnackFlags flags = {0}; - - header.byte = readChar(&curdata); - if (header.bits.type != CONNACK) - goto exit; - - curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ - enddata = curdata + mylen; - if (enddata - curdata < 2) - goto exit; - - flags.all = readChar(&curdata); - *sessionPresent = flags.bits.sessionpresent; - *connack_rc = readChar(&curdata); - - rc = 1; -exit: - return rc; -} - - -/** - * Serializes a 0-length packet into the supplied buffer, ready for writing to a socket - * @param buf the buffer into which the packet will be serialized - * @param buflen the length in bytes of the supplied buffer, to avoid overruns - * @param packettype the message type - * @return serialized length, or error if 0 - */ -int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype) -{ - MQTTHeader header = {0}; - int rc = -1; - unsigned char *ptr = buf; - - if (buflen < 2) - { - rc = MQTTPACKET_BUFFER_TOO_SHORT; - goto exit; - } - header.byte = 0; - header.bits.type = packettype; - writeChar(&ptr, header.byte); /* write header */ - - ptr += MQTTPacket_encode(ptr, 0); /* write remaining length */ - rc = ptr - buf; -exit: - return rc; -} - - -/** - * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket - * @param buf the buffer into which the packet will be serialized - * @param buflen the length in bytes of the supplied buffer, to avoid overruns - * @return serialized length, or error if 0 - */ -int MQTTSerialize_disconnect(unsigned char* buf, int buflen) -{ - return MQTTSerialize_zero(buf, buflen, DISCONNECT); -} - - -/** - * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket - * @param buf the buffer into which the packet will be serialized - * @param buflen the length in bytes of the supplied buffer, to avoid overruns - * @return serialized length, or error if 0 - */ -int MQTTSerialize_pingreq(unsigned char* buf, int buflen) -{ - return MQTTSerialize_zero(buf, buflen, PINGREQ); -} diff --git a/lib/mqtt/MQTTDeserializePublish.c b/lib/mqtt/MQTTDeserializePublish.c deleted file mode 100644 index 59aff4dda..000000000 --- a/lib/mqtt/MQTTDeserializePublish.c +++ /dev/null @@ -1,102 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - *******************************************************************************/ - -#include "MQTTPacket.h" -#include - -#define min(a, b) ((a < b) ? 1 : 0) - -/** - * Deserializes the supplied (wire) buffer into publish data - * @param dup returned integer - the MQTT dup flag - * @param qos returned integer - the MQTT QoS value - * @param retained returned integer - the MQTT retained flag - * @param packetid returned integer - the MQTT packet identifier - * @param topicName returned MQTTString - the MQTT topic in the publish - * @param payload returned byte buffer - the MQTT publish payload - * @param payloadlen returned integer - the length of the MQTT payload - * @param buf the raw buffer data, of the correct length determined by the remaining length field - * @param buflen the length in bytes of the data in the supplied buffer - * @return error code. 1 is success - */ -int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName, - unsigned char** payload, int* payloadlen, unsigned char* buf, int buflen) -{ - MQTTHeader header = {0}; - unsigned char* curdata = buf; - unsigned char* enddata = NULL; - int rc = 0; - int mylen = 0; - - header.byte = readChar(&curdata); - if (header.bits.type != PUBLISH) - goto exit; - *dup = header.bits.dup; - *qos = header.bits.qos; - *retained = header.bits.retain; - - curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ - enddata = curdata + mylen; - - if (!readMQTTLenString(topicName, &curdata, enddata) || - enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */ - goto exit; - - if (*qos > 0) - *packetid = readInt(&curdata); - - *payloadlen = enddata - curdata; - *payload = curdata; - rc = 1; -exit: - return rc; -} - - - -/** - * Deserializes the supplied (wire) buffer into an ack - * @param packettype returned integer - the MQTT packet type - * @param dup returned integer - the MQTT dup flag - * @param packetid returned integer - the MQTT packet identifier - * @param buf the raw buffer data, of the correct length determined by the remaining length field - * @param buflen the length in bytes of the data in the supplied buffer - * @return error code. 1 is success, 0 is failure - */ -int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen) -{ - MQTTHeader header = {0}; - unsigned char* curdata = buf; - unsigned char* enddata = NULL; - int rc = 0; - int mylen; - - header.byte = readChar(&curdata); - *dup = header.bits.dup; - *packettype = header.bits.type; - - curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ - enddata = curdata + mylen; - - if (enddata - curdata < 2) - goto exit; - *packetid = readInt(&curdata); - - rc = 1; -exit: - return rc; -} - diff --git a/lib/mqtt/MQTTPacket.c b/lib/mqtt/MQTTPacket.c deleted file mode 100644 index 2d5c8b732..000000000 --- a/lib/mqtt/MQTTPacket.c +++ /dev/null @@ -1,401 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - * Sergio R. Caprile - non-blocking packet read functions for stream transport - *******************************************************************************/ - -#include "MQTTPacket.h" - -#include - -/** - * Encodes the message length according to the MQTT algorithm - * @param buf the buffer into which the encoded data is written - * @param length the length to be encoded - * @return the number of bytes written to buffer - */ -int MQTTPacket_encode(unsigned char* buf, int length) -{ - int rc = 0; - - do - { - char d = length % 128; - length /= 128; - /* if there are more digits to encode, set the top bit of this digit */ - if (length > 0) - d |= 0x80; - buf[rc++] = d; - } while (length > 0); - return rc; -} - - -/** - * Decodes the message length according to the MQTT algorithm - * @param getcharfn pointer to function to read the next character from the data source - * @param value the decoded length returned - * @return the number of bytes read from the socket - */ -int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value) -{ - unsigned char c; - int multiplier = 1; - int len = 0; -#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4 - - *value = 0; - do - { - int rc = MQTTPACKET_READ_ERROR; - - if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) - { - rc = MQTTPACKET_READ_ERROR; /* bad data */ - goto exit; - } - rc = (*getcharfn)(&c, 1); - if (rc != 1) - goto exit; - *value += (c & 127) * multiplier; - multiplier *= 128; - } while ((c & 128) != 0); -exit: - return len; -} - - -int MQTTPacket_len(int rem_len) -{ - rem_len += 1; /* header byte */ - - /* now remaining_length field */ - if (rem_len < 128) - rem_len += 1; - else if (rem_len < 16384) - rem_len += 2; - else if (rem_len < 2097151) - rem_len += 3; - else - rem_len += 4; - return rem_len; -} - - -static unsigned char* bufptr; - -int bufchar(unsigned char* c, int count) -{ - int i; - - for (i = 0; i < count; ++i) - *c = *bufptr++; - return count; -} - - -int MQTTPacket_decodeBuf(unsigned char* buf, int* value) -{ - bufptr = buf; - return MQTTPacket_decode(bufchar, value); -} - - -/** - * Calculates an integer from two bytes read from the input buffer - * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned - * @return the integer value calculated - */ -int readInt(unsigned char** pptr) -{ - unsigned char* ptr = *pptr; - int len = 256*(*ptr) + (*(ptr+1)); - *pptr += 2; - return len; -} - - -/** - * Reads one character from the input buffer. - * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned - * @return the character read - */ -char readChar(unsigned char** pptr) -{ - char c = **pptr; - (*pptr)++; - return c; -} - - -/** - * Writes one character to an output buffer. - * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned - * @param c the character to write - */ -void writeChar(unsigned char** pptr, char c) -{ - **pptr = c; - (*pptr)++; -} - - -/** - * Writes an integer as 2 bytes to an output buffer. - * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned - * @param anInt the integer to write - */ -void writeInt(unsigned char** pptr, int anInt) -{ - **pptr = (unsigned char)(anInt / 256); - (*pptr)++; - **pptr = (unsigned char)(anInt % 256); - (*pptr)++; -} - - -/** - * Writes a "UTF" string to an output buffer. Converts C string to length-delimited. - * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned - * @param string the C string to write - */ -void writeCString(unsigned char** pptr, const char* string) -{ - int len = strlen(string); - writeInt(pptr, len); - memcpy(*pptr, string, len); - *pptr += len; -} - - -int getLenStringLen(char* ptr) -{ - int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1)); - return len; -} - - -void writeMQTTString(unsigned char** pptr, MQTTString mqttstring) -{ - if (mqttstring.lenstring.len > 0) - { - writeInt(pptr, mqttstring.lenstring.len); - memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len); - *pptr += mqttstring.lenstring.len; - } - else if (mqttstring.cstring) - writeCString(pptr, mqttstring.cstring); - else - writeInt(pptr, 0); -} - - -/** - * @param mqttstring the MQTTString structure into which the data is to be read - * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned - * @param enddata pointer to the end of the data: do not read beyond - * @return 1 if successful, 0 if not - */ -int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata) -{ - int rc = 0; - - /* the first two bytes are the length of the string */ - if (enddata - (*pptr) > 1) /* enough length to read the integer? */ - { - mqttstring->lenstring.len = readInt(pptr); /* increments pptr to point past length */ - if (&(*pptr)[mqttstring->lenstring.len] <= enddata) - { - mqttstring->lenstring.data = (char*)*pptr; - *pptr += mqttstring->lenstring.len; - rc = 1; - } - } - mqttstring->cstring = NULL; - return rc; -} - - -/** - * Return the length of the MQTTstring - C string if there is one, otherwise the length delimited string - * @param mqttstring the string to return the length of - * @return the length of the string - */ -int MQTTstrlen(MQTTString mqttstring) -{ - int rc = 0; - - if (mqttstring.cstring) - rc = strlen(mqttstring.cstring); - else - rc = mqttstring.lenstring.len; - return rc; -} - - -/** - * Compares an MQTTString to a C string - * @param a the MQTTString to compare - * @param bptr the C string to compare - * @return boolean - equal or not - */ -int MQTTPacket_equals(MQTTString* a, char* bptr) -{ - int alen = 0, - blen = 0; - char *aptr; - - if (a->cstring) - { - aptr = a->cstring; - alen = strlen(a->cstring); - } - else - { - aptr = a->lenstring.data; - alen = a->lenstring.len; - } - blen = strlen(bptr); - - return (alen == blen) && (strncmp(aptr, bptr, alen) == 0); -} - - -/** - * Helper function to read packet data from some source into a buffer - * @param buf the buffer into which the packet will be serialized - * @param buflen the length in bytes of the supplied buffer - * @param getfn pointer to a function which will read any number of bytes from the needed source - * @return integer MQTT packet type, or -1 on error - * @note the whole message must fit into the caller's buffer - */ -int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int)) -{ - int rc = -1; - MQTTHeader header = {0}; - int len = 0; - int rem_len = 0; - - /* 1. read the header byte. This has the packet type in it */ - if ((*getfn)(buf, 1) != 1) - goto exit; - - len = 1; - /* 2. read the remaining length. This is variable in itself */ - MQTTPacket_decode(getfn, &rem_len); - len += MQTTPacket_encode(buf + 1, rem_len); /* put the original remaining length back into the buffer */ - - /* 3. read the rest of the buffer using a callback to supply the rest of the data */ - if((rem_len + len) > buflen) - goto exit; - if ((*getfn)(buf + len, rem_len) != rem_len) - goto exit; - - header.byte = buf[0]; - rc = header.bits.type; -exit: - return rc; -} - -/** - * Decodes the message length according to the MQTT algorithm, non-blocking - * @param trp pointer to a transport structure holding what is needed to solve getting data from it - * @param value the decoded length returned - * @return integer the number of bytes read from the socket, 0 for call again, or -1 on error - */ -static int MQTTPacket_decodenb(MQTTTransport *trp) -{ - unsigned char c; - int rc = MQTTPACKET_READ_ERROR; - - if(trp->len == 0){ /* initialize on first call */ - trp->multiplier = 1; - trp->rem_len = 0; - } - do { - int frc; - if (++(trp->len) > MAX_NO_OF_REMAINING_LENGTH_BYTES) - goto exit; - if ((frc=(*trp->getfn)(trp->sck, &c, 1)) == -1) - goto exit; - if (frc == 0){ - rc = 0; - goto exit; - } - trp->rem_len += (c & 127) * trp->multiplier; - trp->multiplier *= 128; - } while ((c & 128) != 0); - rc = trp->len; -exit: - return rc; -} - -/** - * Helper function to read packet data from some source into a buffer, non-blocking - * @param buf the buffer into which the packet will be serialized - * @param buflen the length in bytes of the supplied buffer - * @param trp pointer to a transport structure holding what is needed to solve getting data from it - * @return integer MQTT packet type, 0 for call again, or -1 on error - * @note the whole message must fit into the caller's buffer - */ -int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp) -{ - int rc = -1, frc; - MQTTHeader header = {0}; - - switch(trp->state){ - default: - trp->state = 0; - /*FALLTHROUGH*/ - case 0: - /* read the header byte. This has the packet type in it */ - if ((frc=(*trp->getfn)(trp->sck, buf, 1)) == -1) - goto exit; - if (frc == 0) - return 0; - trp->len = 0; - ++trp->state; - /*FALLTHROUGH*/ - /* read the remaining length. This is variable in itself */ - case 1: - if((frc=MQTTPacket_decodenb(trp)) == MQTTPACKET_READ_ERROR) - goto exit; - if(frc == 0) - return 0; - trp->len = 1 + MQTTPacket_encode(buf + 1, trp->rem_len); /* put the original remaining length back into the buffer */ - if((trp->rem_len + trp->len) > buflen) - goto exit; - ++trp->state; - /*FALLTHROUGH*/ - case 2: - /* read the rest of the buffer using a callback to supply the rest of the data */ - if ((frc=(*trp->getfn)(trp->sck, buf + trp->len, trp->rem_len)) == -1) - goto exit; - if (frc == 0) - return 0; - trp->rem_len -= frc; - trp->len += frc; - if(trp->rem_len) - return 0; - - header.byte = buf[0]; - rc = header.bits.type; - break; - } - -exit: - trp->state = 0; - return rc; -} - diff --git a/lib/mqtt/MQTTPacket.h b/lib/mqtt/MQTTPacket.h deleted file mode 100644 index 6ed32d336..000000000 --- a/lib/mqtt/MQTTPacket.h +++ /dev/null @@ -1,132 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - * Xiang Rong - 442039 Add makefile to Embedded C client - *******************************************************************************/ - -#ifndef MQTTPACKET_H_ -#define MQTTPACKET_H_ - -#if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */ -extern "C" { -#endif - -#if defined(WIN32_DLL) || defined(WIN64_DLL) - #define DLLImport __declspec(dllimport) - #define DLLExport __declspec(dllexport) -#elif defined(LINUX_SO) - #define DLLImport extern - #define DLLExport __attribute__ ((visibility ("default"))) -#else - #define DLLImport - #define DLLExport -#endif - -enum errors -{ - MQTTPACKET_BUFFER_TOO_SHORT = -2, - MQTTPACKET_READ_ERROR = -1, - MQTTPACKET_READ_COMPLETE -}; - -enum msgTypes -{ - CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL, - PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK, - PINGREQ, PINGRESP, DISCONNECT -}; - -/** - * Bitfields for the MQTT header byte. - */ -typedef union -{ - unsigned char byte; /**< the whole byte */ -#if defined(REVERSED) - struct - { - unsigned int type : 4; /**< message type nibble */ - unsigned int dup : 1; /**< DUP flag bit */ - unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ - unsigned int retain : 1; /**< retained flag bit */ - } bits; -#else - struct - { - unsigned int retain : 1; /**< retained flag bit */ - unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ - unsigned int dup : 1; /**< DUP flag bit */ - unsigned int type : 4; /**< message type nibble */ - } bits; -#endif -} MQTTHeader; - -typedef struct -{ - int len; - char* data; -} MQTTLenString; - -typedef struct -{ - char* cstring; - MQTTLenString lenstring; -} MQTTString; - -#define MQTTString_initializer {NULL, {0, NULL}} - -int MQTTstrlen(MQTTString mqttstring); - -#include "MQTTConnect.h" -#include "MQTTPublish.h" -#include "MQTTSubscribe.h" -#include "MQTTUnsubscribe.h" - -int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char type, unsigned char dup, unsigned short packetid); -int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen); - -int MQTTPacket_len(int rem_len); -int MQTTPacket_equals(MQTTString* a, char* b); - -int MQTTPacket_encode(unsigned char* buf, int length); -int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value); -int MQTTPacket_decodeBuf(unsigned char* buf, int* value); - -int readInt(unsigned char** pptr); -char readChar(unsigned char** pptr); -void writeChar(unsigned char** pptr, char c); -void writeInt(unsigned char** pptr, int anInt); -int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata); -void writeCString(unsigned char** pptr, const char* string); -void writeMQTTString(unsigned char** pptr, MQTTString mqttstring); - -DLLExport int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int)); - -typedef struct { - int (*getfn)(void *, unsigned char*, int); /* must return -1 for error, 0 for call again, or the number of bytes read */ - void *sck; /* pointer to whatever the system may use to identify the transport */ - int multiplier; - int rem_len; - int len; - char state; -}MQTTTransport; - -int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp); - -#ifdef __cplusplus /* If this is a C++ compiler, use C linkage */ -} -#endif - - -#endif /* MQTTPACKET_H_ */ diff --git a/lib/mqtt/MQTTPublish.h b/lib/mqtt/MQTTPublish.h deleted file mode 100644 index ebe479dd5..000000000 --- a/lib/mqtt/MQTTPublish.h +++ /dev/null @@ -1,38 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - * Xiang Rong - 442039 Add makefile to Embedded C client - *******************************************************************************/ - -#ifndef MQTTPUBLISH_H_ -#define MQTTPUBLISH_H_ - -#if !defined(DLLImport) - #define DLLImport -#endif -#if !defined(DLLExport) - #define DLLExport -#endif - -DLLExport int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid, - MQTTString topicName, unsigned char* payload, int payloadlen); - -DLLExport int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName, - unsigned char** payload, int* payloadlen, unsigned char* buf, int len); - -DLLExport int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid); -DLLExport int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid); -DLLExport int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid); - -#endif /* MQTTPUBLISH_H_ */ diff --git a/lib/mqtt/MQTTSerializePublish.c b/lib/mqtt/MQTTSerializePublish.c deleted file mode 100644 index 07648307c..000000000 --- a/lib/mqtt/MQTTSerializePublish.c +++ /dev/null @@ -1,164 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - * Ian Craggs - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=453144 - *******************************************************************************/ - -#include "MQTTPacket.h" - -#include - - -/** - * Determines the length of the MQTT publish packet that would be produced using the supplied parameters - * @param qos the MQTT QoS of the publish (packetid is omitted for QoS 0) - * @param topicName the topic name to be used in the publish - * @param payloadlen the length of the payload to be sent - * @return the length of buffer needed to contain the serialized version of the packet - */ -int MQTTSerialize_publishLength(int qos, MQTTString topicName, int payloadlen) -{ - int len = 0; - - len += 2 + MQTTstrlen(topicName) + payloadlen; - if (qos > 0) - len += 2; /* packetid */ - return len; -} - - -/** - * Serializes the supplied publish data into the supplied buffer, ready for sending - * @param buf the buffer into which the packet will be serialized - * @param buflen the length in bytes of the supplied buffer - * @param dup integer - the MQTT dup flag - * @param qos integer - the MQTT QoS value - * @param retained integer - the MQTT retained flag - * @param packetid integer - the MQTT packet identifier - * @param topicName MQTTString - the MQTT topic in the publish - * @param payload byte buffer - the MQTT publish payload - * @param payloadlen integer - the length of the MQTT payload - * @return the length of the serialized data. <= 0 indicates error - */ -int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid, - MQTTString topicName, unsigned char* payload, int payloadlen) -{ - unsigned char *ptr = buf; - MQTTHeader header = {0}; - int rem_len = 0; - int rc = 0; - - if (MQTTPacket_len(rem_len = MQTTSerialize_publishLength(qos, topicName, payloadlen)) > buflen) - { - rc = MQTTPACKET_BUFFER_TOO_SHORT; - goto exit; - } - - header.bits.type = PUBLISH; - header.bits.dup = dup; - header.bits.qos = qos; - header.bits.retain = retained; - writeChar(&ptr, header.byte); /* write header */ - - ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; - - writeMQTTString(&ptr, topicName); - - if (qos > 0) - writeInt(&ptr, packetid); - - memcpy(ptr, payload, payloadlen); - ptr += payloadlen; - - rc = ptr - buf; - -exit: - return rc; -} - - - -/** - * Serializes the ack packet into the supplied buffer. - * @param buf the buffer into which the packet will be serialized - * @param buflen the length in bytes of the supplied buffer - * @param type the MQTT packet type - * @param dup the MQTT dup flag - * @param packetid the MQTT packet identifier - * @return serialized length, or error if 0 - */ -int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char packettype, unsigned char dup, unsigned short packetid) -{ - MQTTHeader header = {0}; - int rc = 0; - unsigned char *ptr = buf; - - if (buflen < 4) - { - rc = MQTTPACKET_BUFFER_TOO_SHORT; - goto exit; - } - header.bits.type = packettype; - header.bits.dup = dup; - header.bits.qos = (packettype == PUBREL) ? 1 : 0; - writeChar(&ptr, header.byte); /* write header */ - - ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ - writeInt(&ptr, packetid); - rc = ptr - buf; -exit: - return rc; -} - - -/** - * Serializes a puback packet into the supplied buffer. - * @param buf the buffer into which the packet will be serialized - * @param buflen the length in bytes of the supplied buffer - * @param packetid integer - the MQTT packet identifier - * @return serialized length, or error if 0 - */ -int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid) -{ - return MQTTSerialize_ack(buf, buflen, PUBACK, 0, packetid); -} - - -/** - * Serializes a pubrel packet into the supplied buffer. - * @param buf the buffer into which the packet will be serialized - * @param buflen the length in bytes of the supplied buffer - * @param dup integer - the MQTT dup flag - * @param packetid integer - the MQTT packet identifier - * @return serialized length, or error if 0 - */ -int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid) -{ - return MQTTSerialize_ack(buf, buflen, PUBREL, dup, packetid); -} - - -/** - * Serializes a pubrel packet into the supplied buffer. - * @param buf the buffer into which the packet will be serialized - * @param buflen the length in bytes of the supplied buffer - * @param packetid integer - the MQTT packet identifier - * @return serialized length, or error if 0 - */ -int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid) -{ - return MQTTSerialize_ack(buf, buflen, PUBCOMP, 0, packetid); -} - - diff --git a/lib/mqtt/MQTTSubscribe.h b/lib/mqtt/MQTTSubscribe.h deleted file mode 100644 index aa9182680..000000000 --- a/lib/mqtt/MQTTSubscribe.h +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - * Xiang Rong - 442039 Add makefile to Embedded C client - *******************************************************************************/ - -#ifndef MQTTSUBSCRIBE_H_ -#define MQTTSUBSCRIBE_H_ - -#if !defined(DLLImport) - #define DLLImport -#endif -#if !defined(DLLExport) - #define DLLExport -#endif - -DLLExport int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, - int count, MQTTString topicFilters[], int requestedQoSs[]); - -DLLExport int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, - int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int len); - -DLLExport int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs); - -DLLExport int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int len); - - -#endif /* MQTTSUBSCRIBE_H_ */ diff --git a/lib/mqtt/MQTTSubscribeClient.c b/lib/mqtt/MQTTSubscribeClient.c deleted file mode 100644 index e6e55c977..000000000 --- a/lib/mqtt/MQTTSubscribeClient.c +++ /dev/null @@ -1,132 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - *******************************************************************************/ - -#include "MQTTPacket.h" - -#include - -/** - * Determines the length of the MQTT subscribe packet that would be produced using the supplied parameters - * @param count the number of topic filter strings in topicFilters - * @param topicFilters the array of topic filter strings to be used in the publish - * @return the length of buffer needed to contain the serialized version of the packet - */ -int MQTTSerialize_subscribeLength(int count, MQTTString topicFilters[]) -{ - int i; - int len = 2; /* packetid */ - - for (i = 0; i < count; ++i) - len += 2 + MQTTstrlen(topicFilters[i]) + 1; /* length + topic + req_qos */ - return len; -} - - -/** - * Serializes the supplied subscribe data into the supplied buffer, ready for sending - * @param buf the buffer into which the packet will be serialized - * @param buflen the length in bytes of the supplied bufferr - * @param dup integer - the MQTT dup flag - * @param packetid integer - the MQTT packet identifier - * @param count - number of members in the topicFilters and reqQos arrays - * @param topicFilters - array of topic filter names - * @param requestedQoSs - array of requested QoS - * @return the length of the serialized data. <= 0 indicates error - */ -int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, int count, - MQTTString topicFilters[], int requestedQoSs[]) -{ - unsigned char *ptr = buf; - MQTTHeader header = {0}; - int rem_len = 0; - int rc = 0; - int i = 0; - - if (MQTTPacket_len(rem_len = MQTTSerialize_subscribeLength(count, topicFilters)) > buflen) - { - rc = MQTTPACKET_BUFFER_TOO_SHORT; - goto exit; - } - - header.byte = 0; - header.bits.type = SUBSCRIBE; - header.bits.dup = dup; - header.bits.qos = 1; - writeChar(&ptr, header.byte); /* write header */ - - ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; - - writeInt(&ptr, packetid); - - for (i = 0; i < count; ++i) - { - writeMQTTString(&ptr, topicFilters[i]); - writeChar(&ptr, requestedQoSs[i]); - } - - rc = ptr - buf; -exit: - return rc; -} - - - -/** - * Deserializes the supplied (wire) buffer into suback data - * @param packetid returned integer - the MQTT packet identifier - * @param maxcount - the maximum number of members allowed in the grantedQoSs array - * @param count returned integer - number of members in the grantedQoSs array - * @param grantedQoSs returned array of integers - the granted qualities of service - * @param buf the raw buffer data, of the correct length determined by the remaining length field - * @param buflen the length in bytes of the data in the supplied buffer - * @return error code. 1 is success, 0 is failure - */ -int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int buflen) -{ - MQTTHeader header = {0}; - unsigned char* curdata = buf; - unsigned char* enddata = NULL; - int rc = 0; - int mylen; - - header.byte = readChar(&curdata); - if (header.bits.type != SUBACK) - goto exit; - - curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ - enddata = curdata + mylen; - if (enddata - curdata < 2) - goto exit; - - *packetid = readInt(&curdata); - - *count = 0; - while (curdata < enddata) - { - if (*count > maxcount) - { - rc = -1; - goto exit; - } - grantedQoSs[(*count)++] = readChar(&curdata); - } - - rc = 1; -exit: - return rc; -} - - diff --git a/lib/mqtt/MQTTUnsubscribe.h b/lib/mqtt/MQTTUnsubscribe.h deleted file mode 100644 index 355ca9a42..000000000 --- a/lib/mqtt/MQTTUnsubscribe.h +++ /dev/null @@ -1,38 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - * Xiang Rong - 442039 Add makefile to Embedded C client - *******************************************************************************/ - -#ifndef MQTTUNSUBSCRIBE_H_ -#define MQTTUNSUBSCRIBE_H_ - -#if !defined(DLLImport) - #define DLLImport -#endif -#if !defined(DLLExport) - #define DLLExport -#endif - -DLLExport int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, - int count, MQTTString topicFilters[]); - -DLLExport int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int max_count, int* count, MQTTString topicFilters[], - unsigned char* buf, int len); - -DLLExport int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid); - -DLLExport int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int len); - -#endif /* MQTTUNSUBSCRIBE_H_ */ diff --git a/lib/mqtt/MQTTUnsubscribeClient.c b/lib/mqtt/MQTTUnsubscribeClient.c deleted file mode 100644 index 6dfee7b61..000000000 --- a/lib/mqtt/MQTTUnsubscribeClient.c +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2014 IBM Corp. - * - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * and Eclipse Distribution License v1.0 which accompany this distribution. - * - * The Eclipse Public License is available at - * http://www.eclipse.org/legal/epl-v10.html - * and the Eclipse Distribution License is available at - * http://www.eclipse.org/org/documents/edl-v10.php. - * - * Contributors: - * Ian Craggs - initial API and implementation and/or initial documentation - *******************************************************************************/ - -#include "MQTTPacket.h" - -#include - -/** - * Determines the length of the MQTT unsubscribe packet that would be produced using the supplied parameters - * @param count the number of topic filter strings in topicFilters - * @param topicFilters the array of topic filter strings to be used in the publish - * @return the length of buffer needed to contain the serialized version of the packet - */ -int MQTTSerialize_unsubscribeLength(int count, MQTTString topicFilters[]) -{ - int i; - int len = 2; /* packetid */ - - for (i = 0; i < count; ++i) - len += 2 + MQTTstrlen(topicFilters[i]); /* length + topic*/ - return len; -} - - -/** - * Serializes the supplied unsubscribe data into the supplied buffer, ready for sending - * @param buf the raw buffer data, of the correct length determined by the remaining length field - * @param buflen the length in bytes of the data in the supplied buffer - * @param dup integer - the MQTT dup flag - * @param packetid integer - the MQTT packet identifier - * @param count - number of members in the topicFilters array - * @param topicFilters - array of topic filter names - * @return the length of the serialized data. <= 0 indicates error - */ -int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, - int count, MQTTString topicFilters[]) -{ - unsigned char *ptr = buf; - MQTTHeader header = {0}; - int rem_len = 0; - int rc = -1; - int i = 0; - - if (MQTTPacket_len(rem_len = MQTTSerialize_unsubscribeLength(count, topicFilters)) > buflen) - { - rc = MQTTPACKET_BUFFER_TOO_SHORT; - goto exit; - } - - header.byte = 0; - header.bits.type = UNSUBSCRIBE; - header.bits.dup = dup; - header.bits.qos = 1; - writeChar(&ptr, header.byte); /* write header */ - - ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; - - writeInt(&ptr, packetid); - - for (i = 0; i < count; ++i) - writeMQTTString(&ptr, topicFilters[i]); - - rc = ptr - buf; -exit: - return rc; -} - - -/** - * Deserializes the supplied (wire) buffer into unsuback data - * @param packetid returned integer - the MQTT packet identifier - * @param buf the raw buffer data, of the correct length determined by the remaining length field - * @param buflen the length in bytes of the data in the supplied buffer - * @return error code. 1 is success, 0 is failure - */ -int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int buflen) -{ - unsigned char type = 0; - unsigned char dup = 0; - int rc = 0; - - rc = MQTTDeserialize_ack(&type, &dup, packetid, buf, buflen); - if (type == UNSUBACK) - rc = 1; - return rc; -} - - From 00cca220c2a5e92aaac50b79198f3884f8952c68 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 26 Jan 2018 15:50:46 +0100 Subject: [PATCH 009/175] bundle dependencies --- ArduinoCloudThing.h | 10 +- lib/CBOR/.gitignore | 28 + lib/CBOR/CborDecoder.cpp | 938 ++++++++++++++++++ lib/CBOR/CborDecoder.h | 107 ++ lib/CBOR/CborEncoder.cpp | 208 ++++ lib/CBOR/CborEncoder.h | 93 ++ .../Cbor_Serialbuffer_send.ino | 91 ++ .../Cbor_Serialport_sender.ino | 44 + .../Cbor_master_reader/Cbor_master_reader.ino | 39 + .../Cbor_slave_sender/Cbor_slave_sender.ino | 44 + lib/CBOR/Examples/cbortest2/cbortest2.ino | 67 ++ lib/CBOR/LICENSE | 201 ++++ lib/CBOR/README.md | 83 ++ lib/LinkedList/LICENSE.txt | 21 + lib/LinkedList/LinkedList.h | 325 ++++++ lib/LinkedList/README.md | 171 ++++ .../examples/ClassList/ClassList.pde | 81 ++ .../SimpleIntegerList/SimpleIntegerList.pde | 58 ++ lib/LinkedList/keywords.txt | 28 + lib/LinkedList/library.json | 12 + lib/LinkedList/library.properties | 9 + lib/MQTT/CMakeLists.txt | 35 + lib/MQTT/DEVELOPING.md | 4 + lib/MQTT/LICENSE.md | 21 + lib/MQTT/Makefile | 14 + lib/MQTT/README.md | 214 ++++ .../AdafruitHuzzahESP8266.ino | 71 ++ .../AdafruitHuzzahESP8266_SSL.ino | 73 ++ .../ArduinoEthernetShield.ino | 62 ++ .../ArduinoWiFi101/ArduinoWiFi101.ino | 70 ++ .../ArduinoWiFi101_SSL/ArduinoWiFi101_SSL.ino | 75 ++ .../ArduinoWiFiShield/ArduinoWiFiShield.ino | 68 ++ lib/MQTT/examples/ArduinoYun/ArduinoYun.ino | 60 ++ .../ArduinoYun_SSL/ArduinoYun_SSL.ino | 62 ++ .../ESP32DevelopmentBoard.ino | 69 ++ .../ESP32DevelopmentBoard_SSL.ino | 71 ++ lib/MQTT/library.properties | 9 + lib/MQTT/src/MQTTClient.h | 371 +++++++ lib/MQTT/src/lwmqtt/client.c | 615 ++++++++++++ lib/MQTT/src/lwmqtt/helpers.c | 251 +++++ lib/MQTT/src/lwmqtt/helpers.h | 137 +++ lib/MQTT/src/lwmqtt/lwmqtt.h | 373 +++++++ lib/MQTT/src/lwmqtt/packet.c | 742 ++++++++++++++ lib/MQTT/src/lwmqtt/packet.h | 185 ++++ lib/MQTT/src/lwmqtt/string.c | 38 + lib/MQTT/src/system.cpp | 48 + lib/MQTT/src/system.h | 26 + 47 files changed, 6417 insertions(+), 5 deletions(-) create mode 100644 lib/CBOR/.gitignore create mode 100644 lib/CBOR/CborDecoder.cpp create mode 100644 lib/CBOR/CborDecoder.h create mode 100644 lib/CBOR/CborEncoder.cpp create mode 100644 lib/CBOR/CborEncoder.h create mode 100644 lib/CBOR/Examples/Cbor_Serialbuffer_send/Cbor_Serialbuffer_send.ino create mode 100644 lib/CBOR/Examples/Cbor_Serialport_sender/Cbor_Serialport_sender.ino create mode 100644 lib/CBOR/Examples/Cbor_master_reader/Cbor_master_reader.ino create mode 100644 lib/CBOR/Examples/Cbor_slave_sender/Cbor_slave_sender.ino create mode 100644 lib/CBOR/Examples/cbortest2/cbortest2.ino create mode 100644 lib/CBOR/LICENSE create mode 100644 lib/CBOR/README.md create mode 100644 lib/LinkedList/LICENSE.txt create mode 100644 lib/LinkedList/LinkedList.h create mode 100644 lib/LinkedList/README.md create mode 100644 lib/LinkedList/examples/ClassList/ClassList.pde create mode 100644 lib/LinkedList/examples/SimpleIntegerList/SimpleIntegerList.pde create mode 100644 lib/LinkedList/keywords.txt create mode 100644 lib/LinkedList/library.json create mode 100644 lib/LinkedList/library.properties create mode 100644 lib/MQTT/CMakeLists.txt create mode 100644 lib/MQTT/DEVELOPING.md create mode 100644 lib/MQTT/LICENSE.md create mode 100644 lib/MQTT/Makefile create mode 100644 lib/MQTT/README.md create mode 100644 lib/MQTT/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino create mode 100644 lib/MQTT/examples/AdafruitHuzzahESP8266_SSL/AdafruitHuzzahESP8266_SSL.ino create mode 100644 lib/MQTT/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino create mode 100644 lib/MQTT/examples/ArduinoWiFi101/ArduinoWiFi101.ino create mode 100644 lib/MQTT/examples/ArduinoWiFi101_SSL/ArduinoWiFi101_SSL.ino create mode 100644 lib/MQTT/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino create mode 100644 lib/MQTT/examples/ArduinoYun/ArduinoYun.ino create mode 100644 lib/MQTT/examples/ArduinoYun_SSL/ArduinoYun_SSL.ino create mode 100644 lib/MQTT/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino create mode 100644 lib/MQTT/examples/ESP32DevelopmentBoard_SSL/ESP32DevelopmentBoard_SSL.ino create mode 100644 lib/MQTT/library.properties create mode 100644 lib/MQTT/src/MQTTClient.h create mode 100644 lib/MQTT/src/lwmqtt/client.c create mode 100644 lib/MQTT/src/lwmqtt/helpers.c create mode 100644 lib/MQTT/src/lwmqtt/helpers.h create mode 100644 lib/MQTT/src/lwmqtt/lwmqtt.h create mode 100644 lib/MQTT/src/lwmqtt/packet.c create mode 100644 lib/MQTT/src/lwmqtt/packet.h create mode 100644 lib/MQTT/src/lwmqtt/string.c create mode 100644 lib/MQTT/src/system.cpp create mode 100644 lib/MQTT/src/system.h diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 36fe33c82..b96628fc1 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -3,10 +3,10 @@ #include #include -#include -#include -#include -#include +#include "lib/MQTT/src/MQTTClient.h" +#include "lib/LinkedList/LinkedList.h" +#include "lib/CBOR/CborDecoder.h" +#include "lib/CBOR/CborEncoder.h" #ifndef MQTT_BUFFER_SIZE #define MQTT_BUFFER_SIZE 256 @@ -90,7 +90,7 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric void appendValue(CborWriter &cbor); void append(CborWriter &cbor) { - writer.writeArray(4); + cbor.writeArray(4); cbor.writeTag(tag); cbor.writeString(name); appendValue(cbor); diff --git a/lib/CBOR/.gitignore b/lib/CBOR/.gitignore new file mode 100644 index 000000000..b8bd0267b --- /dev/null +++ b/lib/CBOR/.gitignore @@ -0,0 +1,28 @@ +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app diff --git a/lib/CBOR/CborDecoder.cpp b/lib/CBOR/CborDecoder.cpp new file mode 100644 index 000000000..7151ef9bc --- /dev/null +++ b/lib/CBOR/CborDecoder.cpp @@ -0,0 +1,938 @@ +#include "CborDecoder.h" +#include "Arduino.h" + + + +CborInput::CborInput(void *data, int size) { + this->data = (unsigned char *)data; + this->size = size; + this->offset = 0; +} + +CborInput::~CborInput() {} + + +bool CborInput::hasBytes(unsigned int count) { + return size - offset >= count; +} + +unsigned char CborInput::getByte() { + return data[offset++]; +} + +unsigned short CborInput::getShort() { + unsigned short value = ((unsigned short)data[offset] << 8) | ((unsigned short)data[offset + 1]); + offset += 2; + return value; +} + +uint32_t CborInput::getInt() { + uint32_t value = ((uint32_t)data[offset] << 24) | ((uint32_t)data[offset + 1] << 16) | ((uint32_t)data[offset + 2] << 8) | ((uint32_t)data[offset + 3]); + offset += 4; + return value; +} + +uint64_t CborInput::getLong() { + uint64_t value = ((uint64_t)data[offset] << 56) | ((uint64_t)data[offset+1] << 48) | ((uint64_t)data[offset+2] << 40) | ((uint64_t)data[offset+3] << 32) | ((uint64_t)data[offset+4] << 24) | ((uint64_t)data[offset+5] << 16) | ((uint64_t)data[offset+6] << 8) | ((uint64_t)data[offset+7]); + offset += 8; + return value; +} + +void CborInput::getBytes(void *to, int count) { + memcpy(to, data + offset, count); + offset += count; +} + + +CborReader::CborReader(CborInput &input) { + this->input = &input; + this->state = STATE_TYPE; +} + +CborReader::CborReader(CborInput &input, CborListener &listener) { + this->input = &input; + this->listener = &listener; + this->state = STATE_TYPE; +} + + +CborReader::~CborReader() {} + + +void CborReader::SetListener(CborListener &listener) { + this->listener = &listener; +} + +void CborReader::Run() { + uint32_t temp; + while(1) { + if(state == STATE_TYPE) { + if(input->hasBytes(1)) { + unsigned char type = input->getByte(); + unsigned char majorType = type >> 5; + unsigned char minorType = type & 31; + + switch(majorType) { + case 0: // positive integer + if(minorType < 24) { + listener->OnInteger(minorType); + } else if(minorType == 24) { // 1 byte + currentLength = 1; + state = STATE_PINT; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_PINT; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_PINT; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_PINT; + } else { + state = STATE_ERROR; + listener->OnError("invalid integer type"); + } + break; + case 1: // negative integer + if(minorType < 24) { + listener->OnInteger(-1 - minorType); + } else if(minorType == 24) { // 1 byte + currentLength = 1; + state = STATE_NINT; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_NINT; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_NINT; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_NINT; + } else { + state = STATE_ERROR; + listener->OnError("invalid integer type"); + } + break; + case 2: // bytes + if(minorType < 24) { + state = STATE_BYTES_DATA; + currentLength = minorType; + } else if(minorType == 24) { + state = STATE_BYTES_SIZE; + currentLength = 1; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_BYTES_SIZE; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_BYTES_SIZE; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_BYTES_SIZE; + } else { + state = STATE_ERROR; + listener->OnError("invalid bytes type"); + } + break; + case 3: // string + if(minorType < 24) { + state = STATE_STRING_DATA; + currentLength = minorType; + } else if(minorType == 24) { + state = STATE_STRING_SIZE; + currentLength = 1; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_STRING_SIZE; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_STRING_SIZE; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_STRING_SIZE; + } else { + state = STATE_ERROR; + listener->OnError("invalid string type"); + } + break; + case 4: // array + if(minorType < 24) { + listener->OnArray(minorType); + } else if(minorType == 24) { + state = STATE_ARRAY; + currentLength = 1; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_ARRAY; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_ARRAY; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_ARRAY; + } else { + state = STATE_ERROR; + listener->OnError("invalid array type"); + } + break; + case 5: // map + if(minorType < 24) { + listener->OnMap(minorType); + } else if(minorType == 24) { + state = STATE_MAP; + currentLength = 1; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_MAP; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_MAP; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_MAP; + } else { + state = STATE_ERROR; + listener->OnError("invalid array type"); + } + break; + case 6: // tag + if(minorType < 24) { + listener->OnTag(minorType); + } else if(minorType == 24) { + state = STATE_TAG; + currentLength = 1; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_TAG; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_TAG; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_TAG; + } else { + state = STATE_ERROR; + listener->OnError("invalid tag type"); + } + break; + case 7: // special + if(minorType < 24) { + listener->OnSpecial(minorType); + } else if(minorType == 24) { + state = STATE_SPECIAL; + currentLength = 1; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_SPECIAL; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_SPECIAL; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_SPECIAL; + } else { + state = STATE_ERROR; + listener->OnError("invalid special type"); + } + break; + } + } else break; + } else if(state == STATE_PINT) { + if(input->hasBytes(currentLength)) { + switch(currentLength) { + case 1: + listener->OnInteger(input->getByte()); + state = STATE_TYPE; + break; + case 2: + listener->OnInteger(input->getShort()); + state = STATE_TYPE; + break; + case 4: + temp = input->getInt(); + if(temp <= INT_MAX) { + listener->OnInteger(temp); + } else { + listener->OnExtraInteger(temp, 1); + } + state = STATE_TYPE; + break; + case 8: + listener->OnExtraInteger(input->getLong(), 1); + state = STATE_TYPE; + break; + } + } else break; + } else if(state == STATE_NINT) { + if(input->hasBytes(currentLength)) { + switch(currentLength) { + case 1: + listener->OnInteger(-(int32_t)input->getByte()); + state = STATE_TYPE; + break; + case 2: + listener->OnInteger(-(int32_t)input->getShort()); + state = STATE_TYPE; + break; + case 4: + temp = input->getInt(); + if(temp <= INT_MAX) { + listener->OnInteger(-(int32_t) temp); + } else if(temp == 2147483648u) { + listener->OnInteger(INT_MIN); + } else { + listener->OnExtraInteger(temp, -1); + } + state = STATE_TYPE; + break; + case 8: + listener->OnExtraInteger(input->getLong(), -1); + break; + } + } else break; + } else if(state == STATE_BYTES_SIZE) { + if(input->hasBytes(currentLength)) { + switch(currentLength) { + case 1: + currentLength = input->getByte(); + state = STATE_BYTES_DATA; + break; + case 2: + currentLength = input->getShort(); + state = STATE_BYTES_DATA; + break; + case 4: + currentLength = input->getInt(); + state = STATE_BYTES_DATA; + break; + case 8: + state = STATE_ERROR; + listener->OnError("extra long bytes"); + break; + } + } else break; + } else if(state == STATE_BYTES_DATA) { + if(input->hasBytes(currentLength)) { + unsigned char *data = new unsigned char[currentLength]; + input->getBytes(data, currentLength); + state = STATE_TYPE; + listener->OnBytes(data, currentLength); + } else break; + } else if(state == STATE_STRING_SIZE) { + if(input->hasBytes(currentLength)) { + switch(currentLength) { + case 1: + currentLength = input->getByte(); + state = STATE_STRING_DATA; + break; + case 2: + currentLength = input->getShort(); + state = STATE_STRING_DATA; + break; + case 4: + currentLength = input->getInt(); + state = STATE_STRING_DATA; + break; + case 8: + state = STATE_ERROR; + listener->OnError("extra long array"); + break; + } + } else break; + } else if(state == STATE_STRING_DATA) { + if(input->hasBytes(currentLength)) { + unsigned char data[currentLength + 1]; + input->getBytes(data, currentLength); + data[currentLength] = '\0'; + state = STATE_TYPE; + String str = String((char*)data); + listener->OnString(str); + str = ""; + } else break; + } else if(state == STATE_ARRAY) { + if(input->hasBytes(currentLength)) { + switch(currentLength) { + case 1: + listener->OnArray(input->getByte()); + state = STATE_TYPE; + break; + case 2: + listener->OnArray(currentLength = input->getShort()); + state = STATE_TYPE; + break; + case 4: + listener->OnArray(input->getInt()); + state = STATE_TYPE; + break; + case 8: + state = STATE_ERROR; + listener->OnError("extra long array"); + break; + } + } else break; + } else if(state == STATE_MAP) { + if(input->hasBytes(currentLength)) { + switch(currentLength) { + case 1: + listener->OnMap(input->getByte()); + state = STATE_TYPE; + break; + case 2: + listener->OnMap(currentLength = input->getShort()); + state = STATE_TYPE; + break; + case 4: + listener->OnMap(input->getInt()); + state = STATE_TYPE; + break; + case 8: + state = STATE_ERROR; + listener->OnError("extra long map"); + break; + } + } else break; + } else if(state == STATE_TAG) { + if(input->hasBytes(currentLength)) { + switch(currentLength) { + case 1: + listener->OnTag(input->getByte()); + state = STATE_TYPE; + break; + case 2: + listener->OnTag(input->getShort()); + state = STATE_TYPE; + break; + case 4: + listener->OnTag(input->getInt()); + state = STATE_TYPE; + break; + case 8: + listener->OnExtraTag(input->getLong()); + state = STATE_TYPE; + break; + } + } else break; + } else if(state == STATE_SPECIAL) { + if (input->hasBytes(currentLength)) { + switch (currentLength) { + case 1: + listener->OnSpecial(input->getByte()); + state = STATE_TYPE; + break; + case 2: + listener->OnSpecial(input->getShort()); + state = STATE_TYPE; + break; + case 4: + listener->OnSpecial(input->getInt()); + state = STATE_TYPE; + break; + case 8: + listener->OnExtraSpecial(input->getLong()); + state = STATE_TYPE; + break; + } + } else break; + } else if(state == STATE_ERROR) { + break; + } else { + Serial.print("UNKNOWN STATE"); + } + } +} + +void CborReader::GetCborData(String &Cborpackage) { + uint32_t temp; + volatile char commaChar = ','; + int tempint; + + while(1) { + if(state == STATE_TYPE) { + if(input->hasBytes(1)) { + unsigned char type = input->getByte(); + unsigned char majorType = type >> 5; + unsigned char minorType = type & 31; + //Serial.write(majorType); + //Serial.println("minorType:" + minorType); + + switch(majorType) { + case 0: // positive integer + if(minorType < 24) { + listener->OnInteger(minorType); + Cborpackage += minorType ; + Cborpackage += commaChar; + } else if(minorType == 24) { // 1 byte + currentLength = 1; + state = STATE_PINT; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_PINT; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_PINT; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_PINT; + } else { + state = STATE_ERROR; + listener->OnError("invalid integer type"); + } + break; + case 1: // negative integer + if(minorType < 24) { + listener->OnInteger(-1 - minorType); + Cborpackage += -1 -minorType ; + Cborpackage += commaChar; + + } else if(minorType == 24) { // 1 byte + currentLength = 1; + state = STATE_NINT; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_NINT; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_NINT; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_NINT; + } else { + state = STATE_ERROR; + listener->OnError("invalid integer type"); + } + break; + case 2: // bytes + if(minorType < 24) { + state = STATE_BYTES_DATA; + currentLength = minorType; + } else if(minorType == 24) { + state = STATE_BYTES_SIZE; + currentLength = 1; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_BYTES_SIZE; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_BYTES_SIZE; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_BYTES_SIZE; + } else { + state = STATE_ERROR; + listener->OnError("invalid bytes type"); + } + break; + case 3: // string + if(minorType < 24) { + state = STATE_STRING_DATA; + currentLength = minorType; + } else if(minorType == 24) { + state = STATE_STRING_SIZE; + currentLength = 1; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_STRING_SIZE; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_STRING_SIZE; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_STRING_SIZE; + } else { + state = STATE_ERROR; + listener->OnError("invalid string type"); + } + break; + case 4: // array + if(minorType < 24) { + listener->OnArray(minorType); + } else if(minorType == 24) { + state = STATE_ARRAY; + currentLength = 1; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_ARRAY; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_ARRAY; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_ARRAY; + } else { + state = STATE_ERROR; + listener->OnError("invalid array type"); + } + break; + case 5: // map + if(minorType < 24) { + listener->OnMap(minorType); + } else if(minorType == 24) { + state = STATE_MAP; + currentLength = 1; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_MAP; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_MAP; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_MAP; + } else { + state = STATE_ERROR; + listener->OnError("invalid array type"); + } + break; + case 6: // tag + if(minorType < 24) { + listener->OnTag(minorType); + } else if(minorType == 24) { + state = STATE_TAG; + currentLength = 1; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_TAG; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_TAG; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_TAG; + } else { + state = STATE_ERROR; + listener->OnError("invalid tag type"); + } + break; + case 7: // special + if(minorType < 24) { + listener->OnSpecial(minorType); + Serial.print("h"); + } else if(minorType == 24) { + state = STATE_SPECIAL; + currentLength = 1; + } else if(minorType == 25) { // 2 byte + currentLength = 2; + state = STATE_SPECIAL; + } else if(minorType == 26) { // 4 byte + currentLength = 4; + state = STATE_SPECIAL; + } else if(minorType == 27) { // 8 byte + currentLength = 8; + state = STATE_SPECIAL; + } else { + state = STATE_ERROR; + listener->OnError("invalid special type"); + } + break; + } + } else break; + } else if(state == STATE_PINT) { + if(input->hasBytes(currentLength)) { + switch(currentLength) { + case 1: + temp =(int32_t) input->getByte(); + listener->OnInteger(temp); + Cborpackage += temp; + Cborpackage += commaChar; + state = STATE_TYPE; + break; + case 2: + temp =(int32_t) input->getShort(); + listener->OnInteger(temp); + Cborpackage += temp; + Cborpackage += commaChar; + state = STATE_TYPE; + break; + case 4: + temp =(int32_t) input->getInt(); + if(temp <= INT_MAX) { + listener->OnInteger(temp); + Cborpackage += temp; + Cborpackage += commaChar; + + } else { + + listener->OnExtraInteger(temp, 1); + Cborpackage += temp ; + Cborpackage += commaChar; + + } + state = STATE_TYPE; + break; + case 8: + temp =(int32_t) input->getLong(); + listener->OnExtraInteger(temp, 1); + + Cborpackage += temp ; + Cborpackage += commaChar; + state = STATE_TYPE; + break; + } + } else break; + } else if(state == STATE_NINT) { + if(input->hasBytes(currentLength)) { + switch(currentLength) { + case 1: + listener->OnInteger(-(int32_t)input->getByte()); + Cborpackage += (int32_t) input->getByte() ; + Cborpackage += commaChar; + Serial.print("h"); + + state = STATE_TYPE; + break; + case 2: + listener->OnInteger(-(int32_t)input->getShort()); + Cborpackage += (int32_t) input->getShort(); + Cborpackage += commaChar; + Serial.print("h"); + + state = STATE_TYPE; + break; + case 4: + temp = input->getInt(); + if(temp <= INT_MAX) { + listener->OnInteger(-(int32_t) temp); + Cborpackage += (int32_t) temp ; + + Serial.print("h"); + Cborpackage += commaChar; + } else if(temp == 2147483648u) { + listener->OnInteger(INT_MIN); + Serial.print("h"); + + Cborpackage += INT_MIN ; + Cborpackage += commaChar; + } else { + listener->OnExtraInteger(temp, -1); + } + state = STATE_TYPE; + break; + case 8: + listener->OnExtraInteger(input->getLong(), -1); + Serial.print("h"); + //Cborpackage += input->getLong() ; + Cborpackage += commaChar; + + break; + } + } else break; + } else if(state == STATE_BYTES_SIZE) { + if(input->hasBytes(currentLength)) { + switch(currentLength) { + case 1: + currentLength = input->getByte(); + state = STATE_BYTES_DATA; + break; + case 2: + currentLength = input->getShort(); + state = STATE_BYTES_DATA; + break; + case 4: + currentLength = input->getInt(); + state = STATE_BYTES_DATA; + break; + case 8: + state = STATE_ERROR; + listener->OnError("extra long bytes"); + break; + } + } else break; + } else if(state == STATE_BYTES_DATA) { + if(input->hasBytes(currentLength)) { + unsigned char *data = new unsigned char[currentLength]; + input->getBytes(data, currentLength); + state = STATE_TYPE; + listener->OnBytes(data, currentLength); + Cborpackage += String(INT_MIN, DEC) ; + Cborpackage += commaChar; + } else break; + } else if(state == STATE_STRING_SIZE) { + if(input->hasBytes(currentLength)) { + switch(currentLength) { + case 1: + currentLength = input->getByte(); + state = STATE_STRING_DATA; + break; + case 2: + currentLength = input->getShort(); + state = STATE_STRING_DATA; + break; + case 4: + currentLength = input->getInt(); + state = STATE_STRING_DATA; + break; + case 8: + state = STATE_ERROR; + listener->OnError("extra long array"); + break; + } + } else break; + } else if(state == STATE_STRING_DATA) { + if(input->hasBytes(currentLength)) { + unsigned char data[currentLength]; + //Serial.print("currentLength:"); + //Serial.println(currentLength); + input->getBytes(data, currentLength); + state = STATE_TYPE; + String str = (const char *) data; + Cborpackage += (const char *) data; + Cborpackage += commaChar; + listener->OnString(str); + str = ""; + } else break; + } else if(state == STATE_ARRAY) { + if(input->hasBytes(currentLength)) { + switch(currentLength) { + case 1: + listener->OnArray(input->getByte()); + state = STATE_TYPE; + break; + case 2: + listener->OnArray(currentLength = input->getShort()); + state = STATE_TYPE; + break; + case 4: + listener->OnArray(input->getInt()); + state = STATE_TYPE; + break; + case 8: + state = STATE_ERROR; + listener->OnError("extra long array"); + break; + } + } else break; + } else if(state == STATE_MAP) { + if(input->hasBytes(currentLength)) { + switch(currentLength) { + case 1: + listener->OnMap(input->getByte()); + state = STATE_TYPE; + break; + case 2: + listener->OnMap(currentLength = input->getShort()); + state = STATE_TYPE; + break; + case 4: + listener->OnMap(input->getInt()); + state = STATE_TYPE; + break; + case 8: + state = STATE_ERROR; + listener->OnError("extra long map"); + break; + } + } else break; + } else if(state == STATE_TAG) { + if(input->hasBytes(currentLength)) { + switch(currentLength) { + case 1: + listener->OnTag(input->getByte()); + state = STATE_TYPE; + break; + case 2: + listener->OnTag(input->getShort()); + state = STATE_TYPE; + break; + case 4: + listener->OnTag(input->getInt()); + state = STATE_TYPE; + break; + case 8: + listener->OnExtraTag(input->getLong()); + state = STATE_TYPE; + break; + } + } else break; + } else if(state == STATE_SPECIAL) { + if (input->hasBytes(currentLength)) { + switch (currentLength) { + case 1: + listener->OnSpecial(input->getByte()); + state = STATE_TYPE; + break; + case 2: + listener->OnSpecial(input->getShort()); + state = STATE_TYPE; + break; + case 4: + listener->OnSpecial(input->getInt()); + state = STATE_TYPE; + break; + case 8: + listener->OnExtraSpecial(input->getLong()); + state = STATE_TYPE; + break; + } + } else break; + } else if(state == STATE_ERROR) { + break; + } else { + Serial.print("UNKNOWN STATE"); + } + } +} + + +// TEST HANDLERS + +void CborDebugListener::OnInteger(int32_t value) { + Serial.print("integer:"); + Serial.println(value); + //Cborpackage += value; + //Cborpackage += commaChar; +} + +void CborDebugListener::OnBytes(unsigned char *data, unsigned int size) { + Serial.print("bytes with size"); + Serial.println(size); +} + +void CborDebugListener::OnString(String &str) { + //Serial.print("string:"); + //int lastStringLength = str.length(); + //Serial.print(lastStringLength); + //Serial.println(str); +} + +void CborDebugListener::OnArray(unsigned int size) { + Serial.print("array:"); + Serial.println(size); +} + +void CborDebugListener::OnMap(unsigned int size) { + Serial.print("map:"); + Serial.println(size); +} + +void CborDebugListener::OnTag(uint32_t tag) { + Serial.print("tag:"); + Serial.println(tag); + +} + +void CborDebugListener::OnSpecial(uint32_t code) { + Serial.println("special" + code); +} + +void CborDebugListener::OnError(const char *error) { + Serial.print("error:"); + + Serial.println(error); +} + +void CborDebugListener::OnExtraInteger(uint64_t value, int sign) { + if(sign >= 0) { + Serial.println("extra integer: %llu\n" + value); + } else { + Serial.println("extra integer: -%llu\n" + value); + } +} + +void CborDebugListener::OnExtraTag(uint64_t tag) { + Serial.println("extra tag: %llu\n" + tag); +} + +void CborDebugListener::OnExtraSpecial(uint64_t tag) { + Serial.println("extra special: %llu\n" + tag); + +} diff --git a/lib/CBOR/CborDecoder.h b/lib/CBOR/CborDecoder.h new file mode 100644 index 000000000..1c3d46cbf --- /dev/null +++ b/lib/CBOR/CborDecoder.h @@ -0,0 +1,107 @@ +#ifndef CBORDE_H +#define CBORDE_H + +#include "Arduino.h" + +#define INT_MAX 2 +#define INT_MIN 2 + + +typedef enum { + STATE_TYPE, + STATE_PINT, + STATE_NINT, + STATE_BYTES_SIZE, + STATE_BYTES_DATA, + STATE_STRING_SIZE, + STATE_STRING_DATA, + STATE_ARRAY, + STATE_MAP, + STATE_TAG, + STATE_SPECIAL, + STATE_ERROR +} CborReaderState; + +class CborInput { + +public: + CborInput(void *data, int size); + ~CborInput(); + + bool hasBytes(unsigned int count); + unsigned char getByte(); + unsigned short getShort(); + uint32_t getInt(); + uint64_t getLong(); + void getBytes(void *to, int count); +private: + unsigned char *data; + int size; + int offset; +}; + + + + +class CborListener { +public: + virtual void OnInteger(int32_t value) = 0; + virtual void OnBytes(unsigned char *data, unsigned int size) = 0; + virtual void OnString(String &str) = 0; + virtual void OnArray(unsigned int size) = 0; + virtual void OnMap(unsigned int size) = 0; + virtual void OnTag(uint32_t tag) = 0; + virtual void OnSpecial(uint32_t code) = 0; + virtual void OnError(const char *error) = 0; + virtual void OnExtraInteger(uint64_t value, int sign) {} + virtual void OnExtraTag(uint64_t tag) {} + virtual void OnExtraSpecial(uint64_t tag) {} +}; + +class CborDebugListener : public CborListener { +public: + virtual void OnInteger(int32_t value); + virtual void OnBytes(unsigned char *data, unsigned int size); + virtual void OnString(String &str); + virtual void OnArray(unsigned int size); + virtual void OnMap(unsigned int size); + virtual void OnTag(uint32_t tag); + virtual void OnSpecial(uint32_t code); + virtual void OnError(const char *error); + + virtual void OnExtraInteger(uint64_t value, int sign); + virtual void OnExtraTag(uint64_t tag); + virtual void OnExtraSpecial(uint64_t tag); +}; + +class CborReader { +public: + CborReader(CborInput &input); + CborReader(CborInput &input, CborListener &listener); + ~CborReader(); + void Run(); + void SetListener(CborListener &listener); + void GetCborData(String &Cborpackage); +private: + CborListener *listener; + CborInput *input; + CborReaderState state; + unsigned int currentLength; +}; + + +class CborExampleListener : public CborListener { + public: + void OnInteger(int32_t value); + void OnBytes(unsigned char *data, unsigned int size); + void OnString(String &str); + void OnArray(unsigned int size); + void OnMap(unsigned int size) ; + void OnTag(uint32_t tag); + void OnSpecial(uint32_t code); + void OnError(const char *error); + }; + + + +#endif diff --git a/lib/CBOR/CborEncoder.cpp b/lib/CBOR/CborEncoder.cpp new file mode 100644 index 000000000..5baa1752e --- /dev/null +++ b/lib/CBOR/CborEncoder.cpp @@ -0,0 +1,208 @@ +#include "CborEncoder.h" +#include "Arduino.h" +#include + + + + + +CborStaticOutput::CborStaticOutput(const unsigned int capacity) { + this->capacity = capacity; + this->buffer = new unsigned char[capacity]; + this->offset = 0; +} + +CborStaticOutput::~CborStaticOutput() { + delete buffer; +} + +void CborStaticOutput::putByte(unsigned char value) { + if(offset < capacity) { + buffer[offset++] = value; + } else { + Serial.print("buffer overflow error"); + } +} + +void CborStaticOutput::putBytes(const unsigned char *data, const unsigned int size) { + if(offset + size - 1 < capacity) { + memcpy(buffer + offset, data, size); + offset += size; + } else { + Serial.print("buffer overflow error"); + } +} + +CborWriter::CborWriter(CborOutput &output) { + this->output = &output; +} + +CborWriter::~CborWriter() { + +} + +unsigned char *CborStaticOutput::getData() { + return buffer; +} + +unsigned int CborStaticOutput::getSize() { + return offset; +} + +CborDynamicOutput::CborDynamicOutput() { + init(256); +} + +CborDynamicOutput::CborDynamicOutput(const uint32_t initalCapacity) { + init(initalCapacity); +} + +CborDynamicOutput::~CborDynamicOutput() { + delete buffer; +} + +void CborDynamicOutput::init(unsigned int initalCapacity) { + this->capacity = initalCapacity; + this->buffer = new unsigned char[initalCapacity]; + this->offset = 0; +} + + +unsigned char *CborDynamicOutput::getData() { + return buffer; +} + +unsigned int CborDynamicOutput::getSize() { + return offset; +} + +void CborDynamicOutput::putByte(unsigned char value) { + if(offset < capacity) { + buffer[offset++] = value; + } else { + capacity *= 2; + buffer = (unsigned char *) realloc(buffer, capacity); + buffer[offset++] = value; + } +} + +void CborDynamicOutput::putBytes(const unsigned char *data, const unsigned int size) { + while(offset + size > capacity) { + capacity *= 2; + buffer = (unsigned char *) realloc(buffer, capacity); + } + + memcpy(buffer + offset, data, size); + offset += size; +} + +void CborWriter::writeTypeAndValue(uint8_t majorType, const uint32_t value) { + majorType <<= 5; + if(value < 24) { + output->putByte(majorType | value); + } else if(value < 256) { + output->putByte(majorType | 24); + output->putByte(value); + } else if(value < 65536) { + output->putByte(majorType | 25); + output->putByte(value >> 8); + output->putByte(value); + } else { + output->putByte(majorType | 26); + output->putByte(value >> 24); + output->putByte(value >> 16); + output->putByte(value >> 8); + output->putByte(value); + } +} + +void CborWriter::writeTypeAndValue(uint8_t majorType, const uint64_t value) { + majorType <<= 5; + if(value < 24ULL) { + output->putByte(majorType | value); + } else if(value < 256ULL) { + output->putByte(majorType | 24); + output->putByte(value); + } else if(value < 65536ULL) { + output->putByte(majorType | 25); + output->putByte(value >> 8); + } else if(value < 4294967296ULL) { + output->putByte(majorType | 26); + output->putByte(value >> 24); + output->putByte(value >> 16); + output->putByte(value >> 8); + output->putByte(value); + } else { + output->putByte(majorType | 27); + output->putByte(value >> 56); + output->putByte(value >> 48); + output->putByte(value >> 40); + output->putByte(value >> 32); + output->putByte(value >> 24); + output->putByte(value >> 16); + output->putByte(value >> 8); + output->putByte(value); + } +} + +void CborWriter::writeInt(const int value) { + // This will break on 64-bit platforms + writeTypeAndValue(0, (uint32_t)value); +} + +void CborWriter::writeInt(const uint32_t value) { + writeTypeAndValue(0, value); +} + +void CborWriter::writeInt(const uint64_t value) { + writeTypeAndValue(0, value); +} + +void CborWriter::writeInt(const int64_t value) { + if(value < 0) { + writeTypeAndValue(1, (uint64_t) -(value+1)); + } else { + writeTypeAndValue(0, (uint64_t) value); + } +} + +/* +void CborWriter::writeInt(const int32_t value) { + if(value < 0) { + writeTypeAndValue(1, (uint32_t) -(value+1)); + } else { + writeTypeAndValue(0, (uint32_t) value); + } +} +*/ + +void CborWriter::writeBytes(const unsigned char *data, const unsigned int size) { + writeTypeAndValue(2, (uint32_t)size); + output->putBytes(data, size); +} + +void CborWriter::writeString(const char *data, const unsigned int size) { + writeTypeAndValue(3, (uint32_t)size); + output->putBytes((const unsigned char *)data, size); +} + +void CborWriter::writeString(const String str) { + writeTypeAndValue(3, (uint32_t)str.length()); + output->putBytes((const unsigned char *)str.c_str(), str.length()); +} + +void CborWriter::writeArray(const unsigned int size) { + writeTypeAndValue(4, (uint32_t)size); +} + +void CborWriter::writeMap(const unsigned int size) { + writeTypeAndValue(5, (uint32_t)size); +} + +void CborWriter::writeTag(const uint32_t tag) { + writeTypeAndValue(6, tag); +} + +void CborWriter::writeSpecial(const uint32_t special) { + writeTypeAndValue(7, special); +} diff --git a/lib/CBOR/CborEncoder.h b/lib/CBOR/CborEncoder.h new file mode 100644 index 000000000..0e3b5ac20 --- /dev/null +++ b/lib/CBOR/CborEncoder.h @@ -0,0 +1,93 @@ +/* + Copyright 2014-2015 Stanislav Ovsyannikov + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + + + + +#ifndef CBOREN_H +#define CBOREN_H + +#include "Arduino.h" + +class CborOutput { +public: + virtual unsigned char *getData() = 0; + virtual unsigned int getSize() = 0; + virtual void putByte(unsigned char value) = 0; + virtual void putBytes(const unsigned char *data, const unsigned int size) = 0; +}; + +class CborStaticOutput : public CborOutput { +public: + CborStaticOutput(unsigned int capacity); + ~CborStaticOutput(); + virtual unsigned char *getData(); + virtual unsigned int getSize(); + virtual void putByte(unsigned char value); + virtual void putBytes(const unsigned char *data, const unsigned int size); +private: + unsigned char *buffer; + unsigned int capacity; + unsigned int offset; +}; + + +class CborDynamicOutput : public CborOutput { +public: + CborDynamicOutput(); + CborDynamicOutput(uint32_t initalCapacity); + ~CborDynamicOutput(); + + + virtual unsigned char *getData(); + virtual unsigned int getSize(); + virtual void putByte(unsigned char value); + virtual void putBytes(const unsigned char *data, const unsigned int size); +private: + void init(unsigned int initalCapacity); + unsigned char *buffer; + unsigned int capacity; + unsigned int offset; +}; + +class CborWriter { +public: + CborWriter(CborOutput &output); + ~CborWriter(); + + void writeInt(const int value); + //void writeInt(const int32_t value); + void writeInt(const int64_t value); + void writeInt(const uint32_t value); + void writeInt(const uint64_t value); + void writeBytes(const unsigned char *data, const unsigned int size); + void writeString(const char *data, const unsigned int size); + void writeString(const String str); + void writeArray(const unsigned int size); + void writeMap(const unsigned int size); + void writeTag(const uint32_t tag); + void writeSpecial(const uint32_t special); +private: + void writeTypeAndValue(uint8_t majorType, const uint32_t value); + void writeTypeAndValue(uint8_t majorType, const uint64_t value); + CborOutput *output; +}; + +class CborSerializable { +public: + virtual void Serialize(CborWriter &writer) = 0; +}; +#endif diff --git a/lib/CBOR/Examples/Cbor_Serialbuffer_send/Cbor_Serialbuffer_send.ino b/lib/CBOR/Examples/Cbor_Serialbuffer_send/Cbor_Serialbuffer_send.ino new file mode 100644 index 000000000..dab225515 --- /dev/null +++ b/lib/CBOR/Examples/Cbor_Serialbuffer_send/Cbor_Serialbuffer_send.ino @@ -0,0 +1,91 @@ +/* + +Arduino Sketch to show how Send Encode Cbor package via Serial port + +Author: Juanjo Tara +email: j.tara@arduino.cc +date: 24/04/2015 +*/ + + +#include "CborEncoder.h" +#include "SerialBuffer.h" + +#define SERIAL_PORT Serial + +#define BUFFER_SIZE 32 +unsigned char buffer[BUFFER_SIZE]; + +// declare the serial buffer +SerialBuffer serialBuffer; + + +void setup() { + + // set up the buffer storage and maximum size + serialBuffer.buffer = buffer; + serialBuffer.bufferSize = BUFFER_SIZE; + + // reset the buffer + serialBuffer.reset(); + Serial.begin(115200); + randomSeed(analogRead(0)); + +} + +void loop() { + + testSerialPort(); + + int maxBytes = SERIAL_PORT.available(); + while (maxBytes--) { + + byte inputByte = SERIAL_PORT.read(); + + // present the input byte to the serial buffer for decoding + // whenever receive() returns >= 0, there's a complete message + // in the buffer ready for processing at offset zero. + // (return value is message length) + int bufferStatus = serialBuffer.receive(inputByte); + + if (bufferStatus >= 0) { + + // handle message + // ... + // ... + + } + } + delay(100); + +} + +void testSerialPort() { + CborStaticOutput output(32); + CborWriter writer(output); + //Write a Cbor Package with a number and String + int randNumb = random(15); + for(int i = 0; i < randNumb; i++) { + int numb = random(300); + writer.writeInt(numb); + } + //writer.writeInt(0x7F); + //writer.writeInt(0x7E); + //writer.writeInt(0x7D); + //writer.writeString("Hello David"); + //writer.writeInt(321); + + //get length and data of cbor package + unsigned char *datapkg = output.getData(); + int length = output.getSize(); + serialBuffer.startMessage(); + for(int i = 0; i < length; i++) { + serialBuffer.write(datapkg[i]); + } + serialBuffer.endMessage(); + //print in Serial port the Data length and Cbor in binary + //Serial.print("datalength:"); + //Serial.print(output.getSize()); + //Serial.println(output.getSize()); + Serial.write(serialBuffer.buffer, serialBuffer.messageLength()); +} \ No newline at end of file diff --git a/lib/CBOR/Examples/Cbor_Serialport_sender/Cbor_Serialport_sender.ino b/lib/CBOR/Examples/Cbor_Serialport_sender/Cbor_Serialport_sender.ino new file mode 100644 index 000000000..5d838dbca --- /dev/null +++ b/lib/CBOR/Examples/Cbor_Serialport_sender/Cbor_Serialport_sender.ino @@ -0,0 +1,44 @@ +/* + +Arduino Sketch to show how Send Encode Cbor package via Serial port + +Author: Juanjo Tara +email: j.tara@arduino.cc +date: 24/04/2015 +*/ + + +#include "CborEncoder.h" + + + +void setup() { + Serial.begin(9600); + +} + +void loop() { + + testSerialPort(); + delay(10000); + +} + +void testSerialPort() { + CborStaticOutput output(32); + CborWriter writer(output); + //Write a Cbor Package with a number and String + writer.writeInt(124); + writer.writeString("I"); + + //get length and data of cbor package + unsigned int datalength = output.getSize(); + unsigned char *datapkg = output.getData(); + + //print in Serial port the Data length and Cbor in binary + //Serial.print("datalength:"); + Serial.print(datalength); + Serial.write(datapkg,datalength); +} + + diff --git a/lib/CBOR/Examples/Cbor_master_reader/Cbor_master_reader.ino b/lib/CBOR/Examples/Cbor_master_reader/Cbor_master_reader.ino new file mode 100644 index 000000000..a4e72bbb6 --- /dev/null +++ b/lib/CBOR/Examples/Cbor_master_reader/Cbor_master_reader.ino @@ -0,0 +1,39 @@ +/* + +Arduino Sketch to show how decode a Cbor package received from I2c +This sketch must to load in the master Arduino + +Author: Juanjo Tara +email: j.tara@arduino.cc +date: 24/04/2015 +*/ + + +#include +#include + + +void setup() +{ + Wire.begin(); // join i2c bus (address optional for master) + Serial.begin(9600); // start serial for output + +} + +void loop() +{ + Wire.requestFrom(2, 4); // request 6 bytes from slave device #2 + + while (Wire.available()) // slave may send less than requested + { + char c = Wire.read(); // receive a byte as character + Serial.print(c); // print the character + } + + CborInput input(data, size); + CborReader reader(input); + CborExampleListener listener; + reader.SetListener(listener); + reader.Run(); + delay(500); +} diff --git a/lib/CBOR/Examples/Cbor_slave_sender/Cbor_slave_sender.ino b/lib/CBOR/Examples/Cbor_slave_sender/Cbor_slave_sender.ino new file mode 100644 index 000000000..d2dccb294 --- /dev/null +++ b/lib/CBOR/Examples/Cbor_slave_sender/Cbor_slave_sender.ino @@ -0,0 +1,44 @@ +/* + +Arduino Sketch to show how Send Encode Cbor package and sending via I2c as Slave requester +This sketch must to load in the Slave Arduino + +Author: Juanjo Tara +email: j.tara@arduino.cc +date: 24/04/2015 +*/ + + + + +#include "CborEncoder.h" +#include + + +void setup() { + Wire.begin(2); // join i2c bus with address #2 + Wire.onRequest(requestEvent); // register event + Serial.begin(9600); + // writting(); +} + +void loop() { + +} + + +// function that executes whenever data is requested by master +// this function is registered as an event, see setup() +void requestEvent() +{ + + //get length and data of cbor package + unsigned char *datapkg = output.getData(); + int datalength = output.getSize(); + + Serial.print("datalength:"); + Serial.print(datalength); + + Wire.write(*datapkg); // respond with message + +} diff --git a/lib/CBOR/Examples/cbortest2/cbortest2.ino b/lib/CBOR/Examples/cbortest2/cbortest2.ino new file mode 100644 index 000000000..911f95d3c --- /dev/null +++ b/lib/CBOR/Examples/cbortest2/cbortest2.ino @@ -0,0 +1,67 @@ +/* + +Arduino Sketch to show how encode and Decode Cbor package + +Author: Juanjo Tara +email: j.tara@arduino.cc +date: 24/04/2015 +*/ + + + + +#include "CborEncoder.h" +#include "CborDecoder.h" + +//String to save the cbor data +String dt = ""; +String valuetoInt = ""; +unsigned int sizeee; +int valuein; + + +void setup() { + + Serial.begin(9600); + //Serial.print("hola"); + test1(); +} + +void loop() { + + +} + + + +void test1() { + + //Create object and Writer + CborStaticOutput output(32); + CborWriter writer(output); + + //Write a Cbor Package with a number and String + writer.writeInt(124); + writer.writeString("I"); + + sizeee = output.getSize(); + Serial.print("datalength:"); + Serial.println(sizeee); + + delay(1000); + + //Receiver for the Cbor input + CborInput input(output.getData(), output.getSize()); + CborDebugListener listener; + CborReader reader(input); + reader.SetListener(listener); + //Save all the cbor into a String divived by commas + reader.GetCborData(dt); + + + valuetoInt = dt.substring(0, dt.indexOf(',')); + valuein = valuetoInt.toInt(); + + Serial.print(valuetoInt.toInt()); + +} diff --git a/lib/CBOR/LICENSE b/lib/CBOR/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/lib/CBOR/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib/CBOR/README.md b/lib/CBOR/README.md new file mode 100644 index 000000000..dd8602dfa --- /dev/null +++ b/lib/CBOR/README.md @@ -0,0 +1,83 @@ +cbor-cpp +======== + +CBOR C++ serialization library + +Just a simple SAX-like Concise Binary Object Representation (CBOR). + +[http://tools.ietf.org/html/rfc7049](http://tools.ietf.org/html/rfc7049) + +#### Examples + +Writing: + +```C++ + CborDynamicOutput output; + CborWriter writer(output); + + writer.writeTag(123); + writer.writeArray(3); + writer.writeString("hello"); + writer.writeString("world"); + writer.writeInt(321); + + unsigned char *data = output.getData(); + int size = output.getSize(); +``` + +Reading: + +```C++ + class CborExampleListener : public CborListener { + public: + virtual void OnInteger(int32_t value); + virtual void OnBytes(unsigned char *data, unsigned int size); + virtual void OnString(std::string &str); + virtual void OnArray(unsigned int size); + virtual void OnMap(unsigned int size); + virtual void OnTag(uint32_t tag); + virtual void OnSpecial(uint32_t code); + virtual void OnError(const char *error); + }; + + ... + + void CborExampleListener::OnInteger(int32_t value) { + printf("integer: %d\n", value); + } + + void CborExampleListener::OnBytes(unsigned char *data, unsigned int size) { + printf("bytes with size: %d", size); + } + + void CborExampleListener::OnString(string &str) { + printf("string: '%.*s'\n", (int)str.size(), str.c_str()); + } + + void CborExampleListener::OnArray(unsigned int size) { + printf("array: %d\n", size); + } + + void CborExampleListener::OnMap(unsigned int size) { + printf("map: %d\n", size); + } + + void CborExampleListener::OnTag(unsigned int tag) { + printf("tag: %d\n", tag); + } + + void CborExampleListener::OnSpecial(unsigned int code) { + printf("special: %d\n", code); + } + + void CborExampleListener::OnError(const char *error) { + printf("error: %s\n", error); + } + + ... + CborInput input(data, size); + CborReader reader(input); + CborExampleListener listener; + reader.SetListener(listener); + reader.Run(); +``` diff --git a/lib/LinkedList/LICENSE.txt b/lib/LinkedList/LICENSE.txt new file mode 100644 index 000000000..5c02604a0 --- /dev/null +++ b/lib/LinkedList/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Ivan Seidel + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/LinkedList/LinkedList.h b/lib/LinkedList/LinkedList.h new file mode 100644 index 000000000..237849285 --- /dev/null +++ b/lib/LinkedList/LinkedList.h @@ -0,0 +1,325 @@ +/* + LinkedList.h - V1.1 - Generic LinkedList implementation + Works better with FIFO, because LIFO will need to + search the entire List to find the last one; + + For instructions, go to https://github.com/ivanseidel/LinkedList + + Created by Ivan Seidel Gomes, March, 2013. + Released into the public domain. +*/ + + +#ifndef LinkedList_h +#define LinkedList_h + +#include + +template +struct ListNode +{ + T data; + ListNode *next; +}; + +template +class LinkedList{ + +protected: + int _size; + ListNode *root; + ListNode *last; + + // Helps "get" method, by saving last position + ListNode *lastNodeGot; + int lastIndexGot; + // isCached should be set to FALSE + // everytime the list suffer changes + bool isCached; + + ListNode* getNode(int index); + +public: + LinkedList(); + ~LinkedList(); + + /* + Returns current size of LinkedList + */ + virtual int size(); + /* + Adds a T object in the specified index; + Unlink and link the LinkedList correcly; + Increment _size + */ + virtual bool add(int index, T); + /* + Adds a T object in the end of the LinkedList; + Increment _size; + */ + virtual bool add(T); + /* + Adds a T object in the start of the LinkedList; + Increment _size; + */ + virtual bool unshift(T); + /* + Set the object at index, with T; + Increment _size; + */ + virtual bool set(int index, T); + /* + Remove object at index; + If index is not reachable, returns false; + else, decrement _size + */ + virtual T remove(int index); + /* + Remove last object; + */ + virtual T pop(); + /* + Remove first object; + */ + virtual T shift(); + /* + Get the index'th element on the list; + Return Element if accessible, + else, return false; + */ + virtual T get(int index); + + /* + Clear the entire array + */ + virtual void clear(); + +}; + +// Initialize LinkedList with false values +template +LinkedList::LinkedList() +{ + root=NULL; + last=NULL; + _size=0; + + lastNodeGot = root; + lastIndexGot = 0; + isCached = false; +} + +// Clear Nodes and free Memory +template +LinkedList::~LinkedList() +{ + ListNode* tmp; + while(root!=NULL) + { + tmp=root; + root=root->next; + delete tmp; + } + last = NULL; + _size=0; + isCached = false; +} + +/* + Actualy "logic" coding +*/ + +template +ListNode* LinkedList::getNode(int index){ + + int _pos = 0; + ListNode* current = root; + + // Check if the node trying to get is + // immediatly AFTER the previous got one + if(isCached && lastIndexGot <= index){ + _pos = lastIndexGot; + current = lastNodeGot; + } + + while(_pos < index && current){ + current = current->next; + + _pos++; + } + + // Check if the object index got is the same as the required + if(_pos == index){ + isCached = true; + lastIndexGot = index; + lastNodeGot = current; + + return current; + } + + return NULL; +} + +template +int LinkedList::size(){ + return _size; +} + +template +bool LinkedList::add(int index, T _t){ + + if(index >= _size) + return add(_t); + + if(index == 0) + return unshift(_t); + + ListNode *tmp = new ListNode(), + *_prev = getNode(index-1); + tmp->data = _t; + tmp->next = _prev->next; + _prev->next = tmp; + + _size++; + isCached = false; + + return true; +} + +template +bool LinkedList::add(T _t){ + + ListNode *tmp = new ListNode(); + tmp->data = _t; + tmp->next = NULL; + + if(root){ + // Already have elements inserted + last->next = tmp; + last = tmp; + }else{ + // First element being inserted + root = tmp; + last = tmp; + } + + _size++; + isCached = false; + + return true; +} + +template +bool LinkedList::unshift(T _t){ + + if(_size == 0) + return add(_t); + + ListNode *tmp = new ListNode(); + tmp->next = root; + tmp->data = _t; + root = tmp; + + _size++; + isCached = false; + + return true; +} + +template +bool LinkedList::set(int index, T _t){ + // Check if index position is in bounds + if(index < 0 || index >= _size) + return false; + + getNode(index)->data = _t; + return true; +} + +template +T LinkedList::pop(){ + if(_size <= 0) + return T(); + + isCached = false; + + if(_size >= 2){ + ListNode *tmp = getNode(_size - 2); + T ret = tmp->next->data; + delete(tmp->next); + tmp->next = NULL; + last = tmp; + _size--; + return ret; + }else{ + // Only one element left on the list + T ret = root->data; + delete(root); + root = NULL; + last = NULL; + _size = 0; + return ret; + } +} + +template +T LinkedList::shift(){ + if(_size <= 0) + return T(); + + if(_size > 1){ + ListNode *_next = root->next; + T ret = root->data; + delete(root); + root = _next; + _size --; + isCached = false; + + return ret; + }else{ + // Only one left, then pop() + return pop(); + } + +} + +template +T LinkedList::remove(int index){ + if (index < 0 || index >= _size) + { + return T(); + } + + if(index == 0) + return shift(); + + if (index == _size-1) + { + return pop(); + } + + ListNode *tmp = getNode(index - 1); + ListNode *toDelete = tmp->next; + T ret = toDelete->data; + tmp->next = tmp->next->next; + delete(toDelete); + _size--; + isCached = false; + return ret; +} + + +template +T LinkedList::get(int index){ + ListNode *tmp = getNode(index); + + return (tmp ? tmp->data : T()); +} + +template +void LinkedList::clear(){ + while(size() > 0) + shift(); +} + +#endif diff --git a/lib/LinkedList/README.md b/lib/LinkedList/README.md new file mode 100644 index 000000000..bdb16fdbd --- /dev/null +++ b/lib/LinkedList/README.md @@ -0,0 +1,171 @@ +# LinkedList + +This library was developed targeting **`Arduino`** applications. However, works just great with any C++. + +Implementing a buffer for objects takes time. If we are not in the mood, we just create an `array[1000]` with enough size. + +The objective of this library is to create a pattern for projects. +If you need to use a List of: `int`, `float`, `objects`, `Lists` or `Wales`. **This is what you are looking for.** + +With a simple but powerful caching algorithm, you can get subsequent objects much faster than usual. Tested without any problems with Lists bigger than 2000 members. + +## Installation + +1. [Download](https://github.com/ivanseidel/LinkedList/archive/master.zip) the Latest release from gitHub. +2. Unzip and modify the Folder name to "LinkedList" (Remove the '-version') +3. Paste the modified folder on your Library folder (On your `Libraries` folder inside Sketchbooks or Arduino software). +4. Reopen the Arduino software. + +**If you are here, because another Library requires this class, just don't waste time reading bellow. Install and ready.** + +------------------------- + +## Getting started + +### The `LinkedList` class + +In case you don't know what a LinkedList is and what it's used for, take a quick look at [Wikipedia::LinkedList](https://en.wikipedia.org/wiki/Linked_list) before continuing. + +#### To declare a LinkedList object +```c++ +// Instantiate a LinkedList that will hold 'integer' +LinkedList myLinkedList = LinkedList(); + +// Or just this +LinkedList myLinkedList; + +// But if you are instantiating a pointer LinkedList... +LinkedList *myLinkedList = new LinkedList(); + +// If you want a LinkedList with any other type such as 'MyClass' +// Make sure you call delete(MyClass) when you remove! +LinkedList *myLinkedList = new LinkedList(); +``` + +#### Getting the size of the linked list +```c++ +// To get the size of a linked list, make use of the size() method +int theSize = myList.size(); + +// Notice that if it's pointer to the linked list, you should use -> instead +int theSize = myList->size(); +``` + +#### Adding elements + +```c++ +// add(obj) method will insert at the END of the list +myList.add(myObject); + +// add(index, obj) method will try to insert the object at the specified index +myList.add(0, myObject); // Add at the beginning +myList.add(3, myObject); // Add at index 3 + +// unshift(obj) method will insert the object at the beginning +myList.unshift(myObject); +``` + +#### Getting elements + +```c++ +// get(index) will return the element at index +// (notice that the start element is 0, not 1) + +// Get the FIRST element +myObject = myList.get(0); + +// Get the third element +myObject = myList.get(2); + +// Get the LAST element +myObject = myList.get(myList.size() - 1); +``` + +#### Changing elements +```c++ +// set(index, obj) method will change the object at index to obj + +// Change the first element to myObject +myList.set(0, myObject); + +// Change the third element to myObject +myList.set(2, myObject); + +// Change the LAST element of the list +myList.set(myList.size() - 1, myObject); +``` + +#### Removing elements +```c++ +// remove(index) will remove and return the element at index + +// Remove the first object +myList.remove(0); + +// Get and Delete the third element +myDeletedObject = myList.remove(2); + +// pop() will remove and return the LAST element +myDeletedObject = myList.pop(); + +// shift() will remove and return the FIRST element +myDeletedObject = myList.shift(); + +// clear() will erase the entire list, leaving it with 0 elements +// NOTE: Clear wont DELETE/FREE memory from Pointers, if you +// are using Classes/Poiners, manualy delete and free those. +myList.clear(); +``` + +------------------------ + +## Library Reference + +### `ListNode` struct + +- `T` `ListNode::data` - The object data + +- `ListNode` `*next` - Pointer to the next Node + +### `LinkedList` class + +**`boolean` methods returns if succeeded** + +- `LinkedList::LinkedList()` - Constructor. + +- `LinkedList::~LinkedList()` - Destructor. Clear Nodes to minimize memory. Does not free pointer memory. + +- `int` `LinkedList::size()` - Returns the current size of the list. + +- `bool` `LinkedList::add(T)` - Add element T at the END of the list. + +- `bool` `LinkedList::add(int index, T)` - Add element T at `index` of the list. + +- `bool` `LinkedList::unshift(T)` - Add element T at the BEGINNING of the list. + +- `bool` `LinkedList::set(int index, T)` - Set the element at `index` to T. + +- `T` `LinkedList::remove(int index)` - Remove element at `index`. Return the removed element. Does not free pointer memory + +- `T` `LinkedList::pop()` - Remove the LAST element. Return the removed element. + +- `T` `LinkedList::shift()` - Remove the FIRST element. Return the removed element. + +- `T` `LinkedList::get(int index)` - Return the element at `index`. + +- `void` `LinkedList::clear()` - Removes all elements. Does not free pointer memory. + +- **protected** `int` `LinkedList::_size` - Holds the cached size of the list. + +- **protected** `ListNode` `LinkedList::*root` - Holds the root node of the list. + +- **protected** `ListNode` `LinkedList::*last` - Holds the last node of the list. + +- **protected** `ListNode*` `LinkedList::getNode(int index)` - Returns the `index` node of the list. + +### Version History + +* `1.1 (2013-07-20)`: Cache implemented. Getting subsequent objects is now O(N). Before, O(N^2). +* `1.0 (2013-07-20)`: Original release + +![LinkedList](https://d2weczhvl823v0.cloudfront.net/ivanseidel/LinkedList/trend.png) diff --git a/lib/LinkedList/examples/ClassList/ClassList.pde b/lib/LinkedList/examples/ClassList/ClassList.pde new file mode 100644 index 000000000..9a8ea9d99 --- /dev/null +++ b/lib/LinkedList/examples/ClassList/ClassList.pde @@ -0,0 +1,81 @@ +/* + LinkedList Example + Link: http://github.com/ivanseidel/LinkedList + + Example Created by + Tom Stewart, github.com/tastewar + + Edited by: + Ivan Seidel, github.com/ivanseidel +*/ + +#include + +// Let's define a new class +class Animal { + public: + char *name; + bool isMammal; +}; + +char catname[]="kitty"; +char dogname[]="doggie"; +char emuname[]="emu"; + +LinkedList myAnimalList = LinkedList(); + +void setup() +{ + + Serial.begin(9600); + Serial.println("Hello!" ); + + // Create a Cat + Animal *cat = new Animal(); + cat->name = catname; + cat->isMammal = true; + + // Create a dog + Animal *dog = new Animal(); + dog->name = dogname; + dog->isMammal = true; + + // Create a emu + Animal *emu = new Animal(); + emu->name = emuname; + emu->isMammal = false; // just an example; no offense to pig lovers + + // Add animals to list + myAnimalList.add(cat); + myAnimalList.add(emu); + myAnimalList.add(dog); +} + +void loop() { + + Serial.print("There are "); + Serial.print(myAnimalList.size()); + Serial.print(" animals in the list. The mammals are: "); + + int current = 0; + Animal *animal; + for(int i = 0; i < myAnimalList.size(); i++){ + + // Get animal from list + animal = myAnimalList.get(i); + + // If its a mammal, then print it's name + if(animal->isMammal){ + + // Avoid printing spacer on the first element + if(current++) + Serial.print(", "); + + // Print animal name + Serial.print(animal->name); + } + } + Serial.println("."); + + while (true); // nothing else to do, loop forever +} \ No newline at end of file diff --git a/lib/LinkedList/examples/SimpleIntegerList/SimpleIntegerList.pde b/lib/LinkedList/examples/SimpleIntegerList/SimpleIntegerList.pde new file mode 100644 index 000000000..1bcbe9c37 --- /dev/null +++ b/lib/LinkedList/examples/SimpleIntegerList/SimpleIntegerList.pde @@ -0,0 +1,58 @@ +/* + LinkedList Example + Link: http://github.com/ivanseidel/LinkedList + + Example Created by + Tom Stewart, github.com/tastewar + + Edited by: + Ivan Seidel, github.com/ivanseidel +*/ +#include + +LinkedList myList = LinkedList(); + +void setup() +{ + + Serial.begin(9600); + Serial.println("Hello!"); + + // Add some stuff to the list + int k = -240, + l = 123, + m = -2, + n = 222; + myList.add(n); + myList.add(0); + myList.add(l); + myList.add(17); + myList.add(k); + myList.add(m); +} + +void loop() { + + int listSize = myList.size(); + + Serial.print("There are "); + Serial.print(listSize); + Serial.print(" integers in the list. The negative ones are: "); + + // Print Negative numbers + for (int h = 0; h < listSize; h++) { + + // Get value from list + int val = myList.get(h); + + // If the value is negative, print it + if (val < 0) { + Serial.print(" "); + Serial.print(val); + } + } + + while (true); // nothing else to do, loop forever +} + + diff --git a/lib/LinkedList/keywords.txt b/lib/LinkedList/keywords.txt new file mode 100644 index 000000000..3ae496859 --- /dev/null +++ b/lib/LinkedList/keywords.txt @@ -0,0 +1,28 @@ +####################################### +# Syntax Coloring +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +LinkedList KEYWORD1 +ListNode KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +size KEYWORD2 +add KEYWORD2 +unshift KEYWORD2 +set KEYWORD2 +remove KEYWORD2 +pop KEYWORD2 +shift KEYWORD2 +get KEYWORD2 +clear KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### diff --git a/lib/LinkedList/library.json b/lib/LinkedList/library.json new file mode 100644 index 000000000..4179b248d --- /dev/null +++ b/lib/LinkedList/library.json @@ -0,0 +1,12 @@ +{ + "name": "LinkedList", + "keywords": "pattern", + "description": "A fully implemented LinkedList (int, float, objects, Lists or Wales) made to work with Arduino projects", + "repository": + { + "type": "git", + "url": "https://github.com/ivanseidel/LinkedList.git" + }, + "frameworks": "arduino", + "platforms": "*" +} diff --git a/lib/LinkedList/library.properties b/lib/LinkedList/library.properties new file mode 100644 index 000000000..77b1423c0 --- /dev/null +++ b/lib/LinkedList/library.properties @@ -0,0 +1,9 @@ +name=LinkedList +version=1.2.3 +author=Ivan Seidel +maintainer=Ivan Seidel +sentence=A fully implemented LinkedList made to work with Arduino projects +paragraph=The objective of this library is to create a pattern for projects. If you need to use a List of: int, float, objects, Lists or Wales. This is what you are looking for. +category=Data Processing +url=https://github.com/ivanseidel/LinkedList +architectures=* diff --git a/lib/MQTT/CMakeLists.txt b/lib/MQTT/CMakeLists.txt new file mode 100644 index 000000000..8bb1d4b27 --- /dev/null +++ b/lib/MQTT/CMakeLists.txt @@ -0,0 +1,35 @@ +# Uncompilable CMake File to enable project editing with CLion IDE + +cmake_minimum_required(VERSION 2.8.4) +project(arduino-mqtt) + +include_directories( + /Applications/Arduino.app/Contents/Java/hardware/arduino/avr/cores/arduino/ + /Users/256dpi/Development/Arduino/libraries/Ethernet/src + /Users/256dpi/Development/Arduino/libraries/WiFi101/src + /Applications/Arduino.app/Contents/Java/libraries/Bridge/src + /Users/256dpi/Library/Arduino15/packages/esp8266/hardware/esp8266/2.3.0/libraries/ESP8266WiFi/src + /Users/256dpi/Development/Arduino/hardware/espressif/esp32/libraries/WiFi/src + /Users/256dpi/Development/Arduino/hardware/espressif/esp32/libraries/WiFiClientSecure/src) + +include_directories(src/) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") + +set(SOURCE_FILES + examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino + examples/AdafruitHuzzahESP8266_SSL/AdafruitHuzzahESP8266_SSL.ino + examples/ArduinoEthernetShield/ArduinoEthernetShield.ino + examples/ArduinoWiFi101/ArduinoWiFi101.ino + examples/ArduinoWiFi101_SSL/ArduinoWiFi101_SSL.ino + examples/ArduinoWiFiShield/ArduinoWiFiShield.ino + examples/ArduinoYun/ArduinoYun.ino + examples/ArduinoYun_SSL/ArduinoYun_SSL.ino + examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino + examples/ESP32DevelopmentBoard_SSL/ESP32DevelopmentBoard_SSL.ino + src/lwmqtt + src/MQTTClient.h + src/system.cpp + src/system.h) + +add_executable(arduino-mqtt ${SOURCE_FILES}) diff --git a/lib/MQTT/DEVELOPING.md b/lib/MQTT/DEVELOPING.md new file mode 100644 index 000000000..676c00ce9 --- /dev/null +++ b/lib/MQTT/DEVELOPING.md @@ -0,0 +1,4 @@ +# Developing + +- Update version in `library.properties`. +- Create release on GitHub. diff --git a/lib/MQTT/LICENSE.md b/lib/MQTT/LICENSE.md new file mode 100644 index 000000000..325e07cff --- /dev/null +++ b/lib/MQTT/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Joël Gähwiler + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/MQTT/Makefile b/lib/MQTT/Makefile new file mode 100644 index 000000000..0e94cc412 --- /dev/null +++ b/lib/MQTT/Makefile @@ -0,0 +1,14 @@ +all: fmt + +fmt: + clang-format -i src/*.cpp src/*.h -style="{BasedOnStyle: Google, ColumnLimit: 120}" + +update: + rm -rf ./lwmqtt + git clone --branch v0.5.6 https://github.com/256dpi/lwmqtt.git ./lwmqtt + mkdir -p ./src/lwmqtt + cp -r ./lwmqtt/src/*.c ./src/lwmqtt/ + cp -r ./lwmqtt/src/*.h ./src/lwmqtt/ + cp -r ./lwmqtt/include/*.h ./src/lwmqtt/ + rm -rf ./lwmqtt + sed -i '' "s//\"lwmqtt.h\"/g" ./src/lwmqtt/* diff --git a/lib/MQTT/README.md b/lib/MQTT/README.md new file mode 100644 index 000000000..695318eb4 --- /dev/null +++ b/lib/MQTT/README.md @@ -0,0 +1,214 @@ +# arduino-mqtt + +[![Build Status](https://travis-ci.org/256dpi/arduino-mqtt.svg?branch=master)](https://travis-ci.org/256dpi/arduino-mqtt) +[![GitHub release](https://img.shields.io/github/release/256dpi/arduino-mqtt.svg)](https://github.com/256dpi/arduino-mqtt/releases) + +This library bundles the [lwmqtt](https://github.com/256dpi/lwmqtt) client and adds a thin wrapper to get an Arduino like API. + +Download the latest version from the [release](https://github.com/256dpi/arduino-mqtt/releases) section. Or even better use the builtin Library Manager in the Arduino IDE and search for "MQTT". + +## Compatibility + +The following examples show how you can use the library with various Arduino compatible hardware: + +- [Arduino Yun & Yun-Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoYun/ArduinoYun.ino) ([SSL](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoYun_SSL/ArduinoYun_SSL.ino)) +- [Arduino Ethernet Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino) +- [Arduino WiFi Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino) +- [Adafruit HUZZAH ESP8266](https://github.com/256dpi/arduino-mqtt/blob/master/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino) ([SSL](https://github.com/256dpi/arduino-mqtt/blob/master/examples/AdafruitHuzzahESP8266_SSL/AdafruitHuzzahESP8266_SSL.ino)) +- [Arduino/Genuino WiFi101 Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFi101/ArduinoWiFi101.ino) ([SSL](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFi101_SSL/ArduinoWiFi101_SSL.ino)) +- [ESP32 Development Board](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino) ([SSL](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ESP32DevelopmentBoard_SSL/ESP32DevelopmentBoard_SSL.ino)) + +Other shields and boards should also work if they provide a [Client](https://www.arduino.cc/en/Reference/ClientConstructor) based network implementation. + +## Notes + +- The maximum size for packets being published and received is set by default to 128 bytes. To change the buffer sizes, you need to use `MQTTClient client(256)` instead of just `MQTTClient client` on the top of your sketch. The passed value denotes the read and write buffer size. + +- On the ESP8266 it has been reported that an additional `delay(10);` after `client.loop();` fixes many stability issues with WiFi connections. + +- To use the library with shiftr.io, you need to provide the token key (username) and token secret (password) as the second and third argument to `client.connect(name, key, secret)`. + +## Example + +The following example uses an Arduino MKR1000 to connect to shiftr.io. You can check on your device after a successful connection here: https://shiftr.io/try. + +```c++ +#include +#include +#include + +const char ssid[] = "ssid"; +const char pass[] = "pass"; + +WiFiClient net; +MQTTClient client; + +unsigned long lastMillis = 0; + +void setup() { + Serial.begin(115200); + WiFi.begin(ssid, pass); + + // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. + // You need to set the IP address directly. + client.begin("broker.shiftr.io", net); + client.onMessage(messageReceived); + + connect(); +} + +void connect() { + Serial.print("checking wifi..."); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(1000); + } + + Serial.print("\nconnecting..."); + while (!client.connect("arduino", "try", "try")) { + Serial.print("."); + delay(1000); + } + + Serial.println("\nconnected!"); + + client.subscribe("/hello"); + // client.unsubscribe("/hello"); +} + +void loop() { + client.loop(); + + if (!client.connected()) { + connect(); + } + + // publish a message roughly every second. + if (millis() - lastMillis > 1000) { + lastMillis = millis(); + client.publish("/hello", "world"); + } +} + +void messageReceived(String &topic, String &payload) { + Serial.println("incoming: " + topic + " - " + payload); +} +``` + +## API + +Initialize the object using the hostname of the broker, the brokers port (default: `1883`) and the underlying Client class for network transport: + +```c++ +void begin(const char hostname[], Client &client); +void begin(const char hostname[], int port, Client &client); +``` + +- Specify port `8883` when using SSL clients for secure connections. +- Local domain names (e.g. `Computer.local` on OSX) are not supported by Arduino. You need to set the IP address directly. + +The hostname and port can also be changed after calling `begin()`: + +```c++ +void setHost(const char hostname[]); +void setHost(const char hostname[], int port); +``` + +Set a will message (last testament) that gets registered on the broker after connecting: + +```c++ +void setWill(const char topic[]); +void setWill(const char topic[], const char payload[]); +void setWill(const char topic[], const char payload[], bool retained, int qos); +void clearWill(); +``` + +Register a callback to receive messages: + +```c++ +void onMessage(MQTTClientCallbackSimple); +// Callback signature: void messageReceived(String &topic, String &payload) {} + +void onMessageAdvanced(MQTTClientCallbackAdvanced); +// Callback signature: void messageReceived(MQTTClient *client, char[] topic, char payload[], int payload_length) {} +``` + +- The set callback is mostly called during a call to `loop()` but may also be called during a call to `subscribe()`, `unsubscribe()` or `publish() // QoS > 0` if messages have been received before receiving the required acknowledgement. Therefore, it is strongly recommended to not call `subscribe()`, `unsubscribe()` or `publish() // QoS > 0` directly in the callback. + +Set more advanced options: + +```c++ +void setOptions(int keepAlive, bool cleanSession, int timeout); +``` + +- The `keepAlive` option controls the keep alive interval (default: 10). +- The `cleanSession` option controls the session retention on the broker side (default: true). +- The `timeout` option controls the default timeout for all commands in milliseconds (default: 1000). + +Connect to broker using the supplied client id and an optional username and password: + +```c++ +boolean connect(const char clientId[]); +boolean connect(const char clientId[], const char username[]); +boolean connect(const char clientId[], const char username[], const char password[]); +``` + +- This functions returns a boolean that indicates if the connection has been established successfully. + +Publishes a message to the broker with an optional payload: + +```c++ +boolean publish(const String &topic); +boolean publish(const char topic[]); +boolean publish(const String &topic, const String &payload); +boolean publish(const String &topic, const String &payload, bool retained, int qos); +boolean publish(const char topic[], const String &payload); +boolean publish(const char topic[], const String &payload, bool retained, int qos); +boolean publish(const char topic[], const char payload[]); +boolean publish(const char topic[], const char payload[], bool retained, int qos); +boolean publish(const char topic[], const char payload[], int length); +boolean publish(const char topic[], const char payload[], int length, bool retained, int qos); +``` + +Subscribe to a topic: + +```c++ +boolean subscribe(const String &topic); +boolean subscribe(const String &topic, int qos); +boolean subscribe(const char topic[]); +boolean subscribe(const char topic[], int qos); +``` + +Unsubscribe from a topic: + +```c++ +boolean unsubscribe(const String &topic); +boolean unsubscribe(const char topic[]); +``` + +Sends and receives packets: + +```c++ +boolean loop(); +``` + +- This function should be called in every `loop`. + +Check if the client is currently connected: + +```c++ +boolean connected(); +``` + +Access low-level information for debugging: + +```c++ +lwmqtt_err_t lastError(); +lwmqtt_return_code_t returnCode(); +``` + +Disconnect from the broker: + +```c++ +boolean disconnect(); +``` diff --git a/lib/MQTT/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino b/lib/MQTT/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino new file mode 100644 index 000000000..ee6bb938e --- /dev/null +++ b/lib/MQTT/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino @@ -0,0 +1,71 @@ +// This example uses an Adafruit Huzzah ESP8266 +// to connect to shiftr.io. +// +// You can check on your device after a successful +// connection here: https://shiftr.io/try. +// +// by Joël Gähwiler +// https://github.com/256dpi/arduino-mqtt + +#include +#include + +const char ssid[] = "ssid"; +const char pass[] = "pass"; + +WiFiClient net; +MQTTClient client; + +unsigned long lastMillis = 0; + +void connect(); // <- predefine connect() for setup() + +void setup() { + Serial.begin(115200); + WiFi.begin(ssid, pass); + + // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. + // You need to set the IP address directly. + client.begin("broker.shiftr.io", net); + client.onMessage(messageReceived); + + connect(); +} + +void connect() { + Serial.print("checking wifi..."); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(1000); + } + + Serial.print("\nconnecting..."); + while (!client.connect("arduino", "try", "try")) { + Serial.print("."); + delay(1000); + } + + Serial.println("\nconnected!"); + + client.subscribe("/hello"); + // client.unsubscribe("/hello"); +} + +void loop() { + client.loop(); + delay(10); // <- fixes some issues with WiFi stability + + if (!client.connected()) { + connect(); + } + + // publish a message roughly every second. + if (millis() - lastMillis > 1000) { + lastMillis = millis(); + client.publish("/hello", "world"); + } +} + +void messageReceived(String &topic, String &payload) { + Serial.println("incoming: " + topic + " - " + payload); +} diff --git a/lib/MQTT/examples/AdafruitHuzzahESP8266_SSL/AdafruitHuzzahESP8266_SSL.ino b/lib/MQTT/examples/AdafruitHuzzahESP8266_SSL/AdafruitHuzzahESP8266_SSL.ino new file mode 100644 index 000000000..e01213cb8 --- /dev/null +++ b/lib/MQTT/examples/AdafruitHuzzahESP8266_SSL/AdafruitHuzzahESP8266_SSL.ino @@ -0,0 +1,73 @@ +// This example uses an Adafruit Huzzah ESP8266 +// to connect to shiftr.io. +// +// You can check on your device after a successful +// connection here: https://shiftr.io/try. +// +// by Joël Gähwiler +// https://github.com/256dpi/arduino-mqtt + +#include +#include + +const char ssid[] = "ssid"; +const char pass[] = "pass"; + +WiFiClientSecure net; +MQTTClient client; + +unsigned long lastMillis = 0; + +void connect(); // <- predefine connect() for setup() + +void setup() { + Serial.begin(115200); + WiFi.begin(ssid, pass); + + // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. + // You need to set the IP address directly. + // + // MQTT brokers usually use port 8883 for secure connections. + client.begin("broker.shiftr.io", 8883, net); + client.onMessage(messageReceived); + + connect(); +} + +void connect() { + Serial.print("checking wifi..."); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(1000); + } + + Serial.print("\nconnecting..."); + while (!client.connect("arduino", "try", "try")) { + Serial.print("."); + delay(1000); + } + + Serial.println("\nconnected!"); + + client.subscribe("/hello"); + // client.unsubscribe("/hello"); +} + +void loop() { + client.loop(); + delay(10); // <- fixes some issues with WiFi stability + + if (!client.connected()) { + connect(); + } + + // publish a message roughly every second. + if (millis() - lastMillis > 1000) { + lastMillis = millis(); + client.publish("/hello", "world"); + } +} + +void messageReceived(String &topic, String &payload) { + Serial.println("incoming: " + topic + " - " + payload); +} diff --git a/lib/MQTT/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino b/lib/MQTT/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino new file mode 100644 index 000000000..a997c132d --- /dev/null +++ b/lib/MQTT/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino @@ -0,0 +1,62 @@ +// This example uses an Arduino Uno together with +// an Ethernet Shield to connect to shiftr.io. +// +// You can check on your device after a successful +// connection here: https://shiftr.io/try. +// +// by Joël Gähwiler +// https://github.com/256dpi/arduino-mqtt + +#include +#include + +byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; +byte ip[] = {192, 168, 1, 177}; // <- change to match your network + +EthernetClient net; +MQTTClient client; + +unsigned long lastMillis = 0; + +void setup() { + Serial.begin(115200); + Ethernet.begin(mac, ip); + + // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. + // You need to set the IP address directly. + client.begin("broker.shiftr.io", net); + client.onMessage(messageReceived); + + connect(); +} + +void connect() { + Serial.print("connecting..."); + while (!client.connect("arduino", "try", "try")) { + Serial.print("."); + delay(1000); + } + + Serial.println("\nconnected!"); + + client.subscribe("/hello"); + // client.unsubscribe("/hello"); +} + +void loop() { + client.loop(); + + if (!client.connected()) { + connect(); + } + + // publish a message roughly every second. + if (millis() - lastMillis > 1000) { + lastMillis = millis(); + client.publish("/hello", "world"); + } +} + +void messageReceived(String &topic, String &payload) { + Serial.println("incoming: " + topic + " - " + payload); +} diff --git a/lib/MQTT/examples/ArduinoWiFi101/ArduinoWiFi101.ino b/lib/MQTT/examples/ArduinoWiFi101/ArduinoWiFi101.ino new file mode 100644 index 000000000..e7a25fee5 --- /dev/null +++ b/lib/MQTT/examples/ArduinoWiFi101/ArduinoWiFi101.ino @@ -0,0 +1,70 @@ +// This example uses an Arduino/Genuino Zero together with +// a WiFi101 Shield or a MKR1000 to connect to shiftr.io. +// +// IMPORTANT: This example uses the new WiFi101 library. +// +// You can check on your device after a successful +// connection here: https://shiftr.io/try. +// +// by Gilberto Conti +// https://github.com/256dpi/arduino-mqtt + +#include +#include + +const char ssid[] = "ssid"; +const char pass[] = "pass"; + +WiFiClient net; +MQTTClient client; + +unsigned long lastMillis = 0; + +void setup() { + Serial.begin(115200); + WiFi.begin(ssid, pass); + + // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. + // You need to set the IP address directly. + client.begin("broker.shiftr.io", net); + client.onMessage(messageReceived); + + connect(); +} + +void connect() { + Serial.print("checking wifi..."); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(1000); + } + + Serial.print("\nconnecting..."); + while (!client.connect("arduino", "try", "try")) { + Serial.print("."); + delay(1000); + } + + Serial.println("\nconnected!"); + + client.subscribe("/hello"); + // client.unsubscribe("/hello"); +} + +void loop() { + client.loop(); + + if (!client.connected()) { + connect(); + } + + // publish a message roughly every second. + if (millis() - lastMillis > 1000) { + lastMillis = millis(); + client.publish("/hello", "world"); + } +} + +void messageReceived(String &topic, String &payload) { + Serial.println("incoming: " + topic + " - " + payload); +} diff --git a/lib/MQTT/examples/ArduinoWiFi101_SSL/ArduinoWiFi101_SSL.ino b/lib/MQTT/examples/ArduinoWiFi101_SSL/ArduinoWiFi101_SSL.ino new file mode 100644 index 000000000..7d68a9ca5 --- /dev/null +++ b/lib/MQTT/examples/ArduinoWiFi101_SSL/ArduinoWiFi101_SSL.ino @@ -0,0 +1,75 @@ +// This example uses an Arduino/Genuino Zero together with +// a WiFi101 Shield or a MKR1000 to connect to shiftr.io. +// +// IMPORTANT: This example uses the new WiFi101 library. +// +// IMPORTANT: You need to install/update the SSL certificates first: +// https://github.com/arduino-libraries/WiFi101-FirmwareUpdater#to-update-ssl-certificates +// +// You can check on your device after a successful +// connection here: https://shiftr.io/try. +// +// by Gilberto Conti +// https://github.com/256dpi/arduino-mqtt + +#include +#include + +const char ssid[] = "ssid"; +const char pass[] = "pass"; + +WiFiSSLClient net; +MQTTClient client; + +unsigned long lastMillis = 0; + +void setup() { + Serial.begin(115200); + WiFi.begin(ssid, pass); + + // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. + // You need to set the IP address directly. + // + // MQTT brokers usually use port 8883 for secure connections. + client.begin("broker.shiftr.io", 8883, net); + client.onMessage(messageReceived); + + connect(); +} + +void connect() { + Serial.print("checking wifi..."); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(1000); + } + + Serial.print("\nconnecting..."); + while (!client.connect("arduino", "try", "try")) { + Serial.print("."); + delay(1000); + } + + Serial.println("\nconnected!"); + + client.subscribe("/hello"); + // client.unsubscribe("/hello"); +} + +void loop() { + client.loop(); + + if (!client.connected()) { + connect(); + } + + // publish a message roughly every second. + if (millis() - lastMillis > 1000) { + lastMillis = millis(); + client.publish("/hello", "world"); + } +} + +void messageReceived(String &topic, String &payload) { + Serial.println("incoming: " + topic + " - " + payload); +} diff --git a/lib/MQTT/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino b/lib/MQTT/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino new file mode 100644 index 000000000..52f8e273c --- /dev/null +++ b/lib/MQTT/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino @@ -0,0 +1,68 @@ +// This example uses an Arduino Uno together with +// a WiFi Shield to connect to shiftr.io. +// +// You can check on your device after a successful +// connection here: https://shiftr.io/try. +// +// by Joël Gähwiler +// https://github.com/256dpi/arduino-mqtt + +#include +#include + +const char ssid[] = "ssid"; +const char pass[] = "pass"; + +WiFiClient net; +MQTTClient client; + +unsigned long lastMillis = 0; + +void setup() { + Serial.begin(115200); + WiFi.begin(ssid, pass); + + // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. + // You need to set the IP address directly. + client.begin("broker.shiftr.io", net); + client.onMessage(messageReceived); + + connect(); +} + +void connect() { + Serial.print("checking wifi..."); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(1000); + } + + Serial.print("\nconnecting..."); + while (!client.connect("arduino", "try", "try")) { + Serial.print("."); + delay(1000); + } + + Serial.println("\nconnected!"); + + client.subscribe("/hello"); + // client.unsubscribe("/hello"); +} + +void loop() { + client.loop(); + + if (!client.connected()) { + connect(); + } + + // publish a message roughly every second. + if (millis() - lastMillis > 1000) { + lastMillis = millis(); + client.publish("/hello", "world"); + } +} + +void messageReceived(String &topic, String &payload) { + Serial.println("incoming: " + topic + " - " + payload); +} diff --git a/lib/MQTT/examples/ArduinoYun/ArduinoYun.ino b/lib/MQTT/examples/ArduinoYun/ArduinoYun.ino new file mode 100644 index 000000000..ea9e0ad55 --- /dev/null +++ b/lib/MQTT/examples/ArduinoYun/ArduinoYun.ino @@ -0,0 +1,60 @@ +// This example uses an Arduino Yun or a Yun-Shield +// and the MQTTClient to connect to shiftr.io. +// +// You can check on your device after a successful +// connection here: https://shiftr.io/try. +// +// by Joël Gähwiler +// https://github.com/256dpi/arduino-mqtt + +#include +#include +#include + +BridgeClient net; +MQTTClient client; + +unsigned long lastMillis = 0; + +void setup() { + Bridge.begin(); + Serial.begin(115200); + + // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. + // You need to set the IP address directly. + client.begin("broker.shiftr.io", net); + client.onMessage(messageReceived); + + connect(); +} + +void connect() { + Serial.print("connecting..."); + while (!client.connect("arduino", "try", "try")) { + Serial.print("."); + delay(1000); + } + + Serial.println("\nconnected!"); + + client.subscribe("/hello"); + // client.unsubscribe("/hello"); +} + +void loop() { + client.loop(); + + if (!client.connected()) { + connect(); + } + + // publish a message roughly every second. + if (millis() - lastMillis > 1000) { + lastMillis = millis(); + client.publish("/hello", "world"); + } +} + +void messageReceived(String &topic, String &payload) { + Serial.println("incoming: " + topic + " - " + payload); +} diff --git a/lib/MQTT/examples/ArduinoYun_SSL/ArduinoYun_SSL.ino b/lib/MQTT/examples/ArduinoYun_SSL/ArduinoYun_SSL.ino new file mode 100644 index 000000000..baf98c3d5 --- /dev/null +++ b/lib/MQTT/examples/ArduinoYun_SSL/ArduinoYun_SSL.ino @@ -0,0 +1,62 @@ +// This example uses an Arduino Yun or a Yun-Shield +// and the MQTTClient to connect to shiftr.io. +// +// You can check on your device after a successful +// connection here: https://shiftr.io/try. +// +// by Joël Gähwiler +// https://github.com/256dpi/arduino-mqtt + +#include +#include +#include + +BridgeSSLClient net; +MQTTClient client; + +unsigned long lastMillis = 0; + +void setup() { + Bridge.begin(); + Serial.begin(115200); + + // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. + // You need to set the IP address directly. + // + // MQTT brokers usually use port 8883 for secure connections. + client.begin("broker.shiftr.io", 8883, net); + client.onMessage(messageReceived); + + connect(); +} + +void connect() { + Serial.print("connecting..."); + while (!client.connect("arduino", "try", "try")) { + Serial.print("."); + delay(1000); + } + + Serial.println("\nconnected!"); + + client.subscribe("/hello"); + // client.unsubscribe("/hello"); +} + +void loop() { + client.loop(); + + if (!client.connected()) { + connect(); + } + + // publish a message roughly every second. + if (millis() - lastMillis > 1000) { + lastMillis = millis(); + client.publish("/hello", "world"); + } +} + +void messageReceived(String &topic, String &payload) { + Serial.println("incoming: " + topic + " - " + payload); +} diff --git a/lib/MQTT/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino b/lib/MQTT/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino new file mode 100644 index 000000000..09533e90e --- /dev/null +++ b/lib/MQTT/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino @@ -0,0 +1,69 @@ +// This example uses an ESP32 Development Board +// to connect to shiftr.io. +// +// You can check on your device after a successful +// connection here: https://shiftr.io/try. +// +// by Joël Gähwiler +// https://github.com/256dpi/arduino-mqtt + +#include +#include + +const char ssid[] = "ssid"; +const char pass[] = "pass"; + +WiFiClient net; +MQTTClient client; + +unsigned long lastMillis = 0; + +void setup() { + Serial.begin(115200); + WiFi.begin(ssid, pass); + + // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. + // You need to set the IP address directly. + client.begin("broker.shiftr.io", net); + client.onMessage(messageReceived); + + connect(); +} + +void connect() { + Serial.print("checking wifi..."); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(1000); + } + + Serial.print("\nconnecting..."); + while (!client.connect("arduino", "try", "try")) { + Serial.print("."); + delay(1000); + } + + Serial.println("\nconnected!"); + + client.subscribe("/hello"); + // client.unsubscribe("/hello"); +} + +void loop() { + client.loop(); + delay(10); // <- fixes some issues with WiFi stability + + if (!client.connected()) { + connect(); + } + + // publish a message roughly every second. + if (millis() - lastMillis > 1000) { + lastMillis = millis(); + client.publish("/hello", "world"); + } +} + +void messageReceived(String &topic, String &payload) { + Serial.println("incoming: " + topic + " - " + payload); +} diff --git a/lib/MQTT/examples/ESP32DevelopmentBoard_SSL/ESP32DevelopmentBoard_SSL.ino b/lib/MQTT/examples/ESP32DevelopmentBoard_SSL/ESP32DevelopmentBoard_SSL.ino new file mode 100644 index 000000000..f8bdd5fb4 --- /dev/null +++ b/lib/MQTT/examples/ESP32DevelopmentBoard_SSL/ESP32DevelopmentBoard_SSL.ino @@ -0,0 +1,71 @@ +// This example uses an ESP32 Development Board +// to connect to shiftr.io. +// +// You can check on your device after a successful +// connection here: https://shiftr.io/try. +// +// by Joël Gähwiler +// https://github.com/256dpi/arduino-mqtt + +#include +#include + +const char ssid[] = "ssid"; +const char pass[] = "pass"; + +WiFiClientSecure net; +MQTTClient client; + +unsigned long lastMillis = 0; + +void setup() { + Serial.begin(115200); + WiFi.begin(ssid, pass); + + // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. + // You need to set the IP address directly. + // + // MQTT brokers usually use port 8883 for secure connections. + client.begin("broker.shiftr.io", 8883, net); + client.onMessage(messageReceived); + + connect(); +} + +void connect() { + Serial.print("checking wifi..."); + while (WiFi.status() != WL_CONNECTED) { + Serial.print("."); + delay(1000); + } + + Serial.print("\nconnecting..."); + while (!client.connect("arduino", "try", "try")) { + Serial.print("."); + delay(1000); + } + + Serial.println("\nconnected!"); + + client.subscribe("/hello"); + // client.unsubscribe("/hello"); +} + +void loop() { + client.loop(); + delay(10); // <- fixes some issues with WiFi stability + + if (!client.connected()) { + connect(); + } + + // publish a message roughly every second. + if (millis() - lastMillis > 1000) { + lastMillis = millis(); + client.publish("/hello", "world"); + } +} + +void messageReceived(String &topic, String &payload) { + Serial.println("incoming: " + topic + " - " + payload); +} diff --git a/lib/MQTT/library.properties b/lib/MQTT/library.properties new file mode 100644 index 000000000..1bad69be2 --- /dev/null +++ b/lib/MQTT/library.properties @@ -0,0 +1,9 @@ +name=MQTT +version=2.2.2 +author=Joel Gaehwiler +maintainer=Joel Gaehwiler +sentence=MQTT library for Arduino +paragraph=This library bundles the lwmqtt client and adds a thin wrapper to get an Arduino like API. +category=Communication +url=https://github.com/256dpi/arduino-mqtt +architectures=* diff --git a/lib/MQTT/src/MQTTClient.h b/lib/MQTT/src/MQTTClient.h new file mode 100644 index 000000000..a20170d6b --- /dev/null +++ b/lib/MQTT/src/MQTTClient.h @@ -0,0 +1,371 @@ +#ifndef MQTT_CLIENT_H +#define MQTT_CLIENT_H + +#include +#include +#include + +#include "system.h" + +class MQTTClient; + +typedef void (*MQTTClientCallbackSimple)(String &topic, String &payload); +typedef void (*MQTTClientCallbackAdvanced)(MQTTClient *client, char topic[], char bytes[], int length); + +typedef struct { + MQTTClient *client = nullptr; + MQTTClientCallbackSimple simple = nullptr; + MQTTClientCallbackAdvanced advanced = nullptr; +} MQTTClientCallback; + +static void MQTTClientHandler(lwmqtt_client_t *client, void *ref, lwmqtt_string_t topic, lwmqtt_message_t message) { + // get callback + auto cb = (MQTTClientCallback *)ref; + + // null terminate topic + char terminated_topic[topic.len + 1]; + memcpy(terminated_topic, topic.data, topic.len); + terminated_topic[topic.len] = '\0'; + + // null terminate payload if available + if (message.payload != nullptr) { + message.payload[message.payload_len] = '\0'; + } + + // call the advanced callback and return if available + if (cb->advanced != nullptr) { + cb->advanced(cb->client, terminated_topic, (char *)message.payload, (int)message.payload_len); + return; + } + + // return if simple callback is not set + if (cb->simple == nullptr) { + return; + } + + // create topic string + String str_topic = String(terminated_topic); + + // create payload string + String str_payload; + if (message.payload != nullptr) { + str_payload = String((const char *)message.payload); + } + + // call simple callback + cb->simple(str_topic, str_payload); +} + +class MQTTClient { + private: + size_t bufSize = 0; + uint8_t *readBuf = nullptr; + uint8_t *writeBuf = nullptr; + + uint16_t keepAlive = 10; + bool cleanSession = true; + uint32_t timeout = 1000; + + Client *netClient = nullptr; + const char *hostname = nullptr; + int port = 0; + lwmqtt_will_t will = lwmqtt_default_will; + bool hasWill = false; + MQTTClientCallback callback; + + lwmqtt_arduino_network_t network = {nullptr}; + lwmqtt_arduino_timer_t timer1 = {0}; + lwmqtt_arduino_timer_t timer2 = {0}; + lwmqtt_client_t client = {0}; + + void* parent; + + bool _connected = false; + lwmqtt_return_code_t _returnCode = (lwmqtt_return_code_t)0; + lwmqtt_err_t _lastError = (lwmqtt_err_t)0; + + public: + explicit MQTTClient(int bufSize = 128) { + this->bufSize = (size_t)bufSize; + this->readBuf = (uint8_t *)malloc((size_t)bufSize + 1); + this->writeBuf = (uint8_t *)malloc((size_t)bufSize); + } + + ~MQTTClient() { + free(this->readBuf); + free(this->writeBuf); + } + + void* getParent() { + return parent; + } + + void setParent(void* _parent) { + parent = _parent; + } + + void begin(const char hostname[], Client &client) { this->begin(hostname, 1883, client); } + + void begin(const char hostname[], int port, Client &client) { + // set config + this->hostname = hostname; + this->port = port; + this->netClient = &client; + + // initialize client + lwmqtt_init(&this->client, this->writeBuf, this->bufSize, this->readBuf, this->bufSize); + + // set timers + lwmqtt_set_timers(&this->client, &this->timer1, &this->timer2, lwmqtt_arduino_timer_set, lwmqtt_arduino_timer_get); + + // set network + lwmqtt_set_network(&this->client, &this->network, lwmqtt_arduino_network_read, lwmqtt_arduino_network_write); + + // set callback + lwmqtt_set_callback(&this->client, (void *)&this->callback, MQTTClientHandler); + } + + void onMessage(MQTTClientCallbackSimple cb) { + // set callback + this->callback.client = this; + this->callback.simple = cb; + this->callback.advanced = nullptr; + } + + void onMessageAdvanced(MQTTClientCallbackAdvanced cb) { + // set callback + this->callback.client = this; + this->callback.simple = nullptr; + this->callback.advanced = cb; + } + + void setHost(const char hostname[]) { this->setHost(hostname, 1883); } + + void setHost(const char hostname[], int port) { + this->hostname = hostname; + this->port = port; + } + + void setWill(const char topic[]) { this->setWill(topic, ""); } + + void setWill(const char topic[], const char payload[]) { this->setWill(topic, payload, false, 0); } + + void setWill(const char topic[], const char payload[], bool retained, int qos) { + this->hasWill = true; + this->will.topic = lwmqtt_string(topic); + this->will.payload = lwmqtt_string(payload); + this->will.retained = retained; + this->will.qos = (lwmqtt_qos_t)qos; + } + + void clearWill() { this->hasWill = false; } + + void setOptions(int keepAlive, bool cleanSession, int timeout) { + this->keepAlive = (uint16_t)keepAlive; + this->cleanSession = cleanSession; + this->timeout = (uint32_t)timeout; + } + + boolean connect(const char clientId[]) { return this->connect(clientId, nullptr, nullptr); } + + boolean connect(const char clientId[], const char username[]) { return this->connect(clientId, username, nullptr); } + + boolean connect(const char clientId[], const char username[], const char password[]) { + // close left open connection if still connected + if (this->connected()) { + this->close(); + } + + // save client + this->network.client = this->netClient; + + // connect to host + if (this->netClient->connect(this->hostname, (uint16_t)this->port) < 0) { + return false; + } + + // prepare options + lwmqtt_options_t options = lwmqtt_default_options; + options.keep_alive = this->keepAlive; + options.clean_session = this->cleanSession; + options.client_id = lwmqtt_string(clientId); + + // set username and password if available + if (username != nullptr) { + options.username = lwmqtt_string(username); + + if (password != nullptr) { + options.password = lwmqtt_string(password); + } + } + + // prepare will reference + lwmqtt_will_t *will = nullptr; + if (this->hasWill) { + will = &this->will; + } + + // connect to broker + this->_lastError = lwmqtt_connect(&this->client, options, will, &this->_returnCode, this->timeout); + if (this->_lastError != LWMQTT_SUCCESS) { + return this->close(); + } + + // set flag + this->_connected = true; + + return true; + } + + boolean publish(const String &topic) { return this->publish(topic.c_str(), ""); } + + boolean publish(const char topic[]) { return this->publish(topic, ""); } + + boolean publish(const String &topic, const String &payload) { return this->publish(topic.c_str(), payload.c_str()); } + + boolean publish(const String &topic, const String &payload, bool retained, int qos) { + return this->publish(topic.c_str(), payload.c_str(), retained, qos); + } + + boolean publish(const char topic[], const String &payload) { return this->publish(topic, payload.c_str()); } + + boolean publish(const char topic[], const String &payload, bool retained, int qos) { + return this->publish(topic, payload.c_str(), retained, qos); + } + + boolean publish(const char topic[], const char payload[]) { + return this->publish(topic, (char *)payload, (int)strlen(payload)); + } + + boolean publish(const char topic[], const char payload[], bool retained, int qos) { + return this->publish(topic, (char *)payload, (int)strlen(payload), retained, qos); + } + + boolean publish(const char topic[], const char payload[], int length) { + return this->publish(topic, payload, length, false, 0); + } + + boolean publish(const char topic[], const char payload[], int length, bool retained, int qos) { + // return immediately if not connected + if (!this->connected()) { + return false; + } + + // prepare message + lwmqtt_message_t message = lwmqtt_default_message; + message.payload = (uint8_t *)payload; + message.payload_len = (size_t)length; + message.retained = retained; + message.qos = lwmqtt_qos_t(qos); + + // publish message + this->_lastError = lwmqtt_publish(&this->client, lwmqtt_string(topic), message, this->timeout); + if (this->_lastError != LWMQTT_SUCCESS) { + return this->close(); + } + + return true; + } + + boolean subscribe(const String &topic) { return this->subscribe(topic.c_str()); } + + boolean subscribe(const String &topic, int qos) { return this->subscribe(topic.c_str(), qos); } + + boolean subscribe(const char topic[]) { return this->subscribe(topic, 0); } + + boolean subscribe(const char topic[], int qos) { + // return immediately if not connected + if (!this->connected()) { + return false; + } + + // subscribe to topic + this->_lastError = lwmqtt_subscribe_one(&this->client, lwmqtt_string(topic), (lwmqtt_qos_t)qos, this->timeout); + if (this->_lastError != LWMQTT_SUCCESS) { + return this->close(); + } + + return true; + } + + boolean unsubscribe(const String &topic) { return this->unsubscribe(topic.c_str()); } + + boolean unsubscribe(const char topic[]) { + // return immediately if not connected + if (!this->connected()) { + return false; + } + + // unsubscribe from topic + this->_lastError = lwmqtt_unsubscribe_one(&this->client, lwmqtt_string(topic), this->timeout); + if (this->_lastError != LWMQTT_SUCCESS) { + return this->close(); + } + + return true; + } + + boolean loop() { + // return immediately if not connected + if (!this->connected()) { + return false; + } + + // get available bytes on the network + auto available = (size_t)this->netClient->available(); + + // yield if data is available + if (available > 0) { + this->_lastError = lwmqtt_yield(&this->client, available, this->timeout); + if (this->_lastError != LWMQTT_SUCCESS) { + return this->close(); + } + } + + // keep the connection alive + this->_lastError = lwmqtt_keep_alive(&this->client, this->timeout); + if (this->_lastError != LWMQTT_SUCCESS) { + return this->close(); + } + + return true; + } + + boolean connected() { + // a client is connected if the network is connected, a client is available and + // the connection has been properly initiated + return this->netClient != nullptr && this->netClient->connected() == 1 && this->_connected; + } + + lwmqtt_err_t lastError() { return this->_lastError; } + + lwmqtt_return_code_t returnCode() { return this->_returnCode; } + + boolean disconnect() { + // return immediately if not connected anymore + if (!this->connected()) { + return false; + } + + // cleanly disconnect + this->_lastError = lwmqtt_disconnect(&this->client, this->timeout); + + // close + this->close(); + + return this->_lastError == LWMQTT_SUCCESS; + } + + private: + boolean close() { + // set flag + this->_connected = false; + + // close network + this->netClient->stop(); + + return false; + } +}; + +#endif diff --git a/lib/MQTT/src/lwmqtt/client.c b/lib/MQTT/src/lwmqtt/client.c new file mode 100644 index 000000000..8775ab6e1 --- /dev/null +++ b/lib/MQTT/src/lwmqtt/client.c @@ -0,0 +1,615 @@ +#include "packet.h" + +void lwmqtt_init(lwmqtt_client_t *client, uint8_t *write_buf, size_t write_buf_size, uint8_t *read_buf, + size_t read_buf_size) { + client->last_packet_id = 1; + client->keep_alive_interval = 0; + client->pong_pending = false; + + client->write_buf = write_buf; + client->write_buf_size = write_buf_size; + client->read_buf = read_buf; + client->read_buf_size = read_buf_size; + + client->callback = NULL; + client->callback_ref = NULL; + + client->network = NULL; + client->network_read = NULL; + client->network_write = NULL; + + client->keep_alive_timer = NULL; + client->command_timer = NULL; + client->timer_set = NULL; + client->timer_get = NULL; +} + +void lwmqtt_set_network(lwmqtt_client_t *client, void *ref, lwmqtt_network_read_t read, lwmqtt_network_write_t write) { + client->network = ref; + client->network_read = read; + client->network_write = write; +} + +void lwmqtt_set_timers(lwmqtt_client_t *client, void *keep_alive_timer, void *command_timer, lwmqtt_timer_set_t set, + lwmqtt_timer_get_t get) { + client->keep_alive_timer = keep_alive_timer; + client->command_timer = command_timer; + client->timer_set = set; + client->timer_get = get; + + client->timer_set(client->keep_alive_timer, 0); + client->timer_set(client->command_timer, 0); +} + +void lwmqtt_set_callback(lwmqtt_client_t *client, void *ref, lwmqtt_callback_t cb) { + client->callback_ref = ref; + client->callback = cb; +} + +static uint16_t lwmqtt_get_next_packet_id(lwmqtt_client_t *client) { + // check overflow + if (client->last_packet_id == 65535) { + client->last_packet_id = 1; + return 1; + } + + // increment packet id + client->last_packet_id++; + + return client->last_packet_id; +} + +static lwmqtt_err_t lwmqtt_read_from_network(lwmqtt_client_t *client, size_t offset, size_t len) { + // check read buffer capacity + if (client->read_buf_size < offset + len) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // prepare counter + size_t read = 0; + + // read while data is missing + while (read < len) { + // check remaining time + int32_t remaining_time = client->timer_get(client->command_timer); + if (remaining_time <= 0) { + return LWMQTT_NETWORK_TIMEOUT; + } + + // read + size_t partial_read = 0; + lwmqtt_err_t err = client->network_read(client->network, client->read_buf + offset + read, len - read, + &partial_read, (uint32_t)remaining_time); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // increment counter + read += partial_read; + } + + return LWMQTT_SUCCESS; +} + +static lwmqtt_err_t lwmqtt_write_to_network(lwmqtt_client_t *client, size_t offset, size_t len) { + // prepare counter + size_t written = 0; + + // write while data is left + while (written < len) { + // check remaining time + int32_t remaining_time = client->timer_get(client->command_timer); + if (remaining_time <= 0) { + return LWMQTT_NETWORK_TIMEOUT; + } + + // write + size_t partial_write = 0; + lwmqtt_err_t err = client->network_write(client->network, client->write_buf + offset + written, len - written, + &partial_write, (uint32_t)remaining_time); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // increment counter + written += partial_write; + } + + return LWMQTT_SUCCESS; +} + +static lwmqtt_err_t lwmqtt_read_packet_in_buffer(lwmqtt_client_t *client, size_t *read, + lwmqtt_packet_type_t *packet_type) { + // preset packet type + *packet_type = LWMQTT_NO_PACKET; + + // read or wait for header byte + lwmqtt_err_t err = lwmqtt_read_from_network(client, 0, 1); + if (err == LWMQTT_NETWORK_TIMEOUT) { + // this is ok as no data has been read at all + return LWMQTT_SUCCESS; + } else if (err != LWMQTT_SUCCESS) { + return err; + } + + // detect packet type + err = lwmqtt_detect_packet_type(client->read_buf, 1, packet_type); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // prepare variables + size_t len = 0; + uint32_t rem_len = 0; + + do { + // adjust len + len++; + + // read next byte + err = lwmqtt_read_from_network(client, len, 1); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // attempt to detect remaining length + err = lwmqtt_detect_remaining_length(client->read_buf + 1, len, &rem_len); + } while (err == LWMQTT_BUFFER_TOO_SHORT); + + // check final error + if (err != LWMQTT_SUCCESS) { + return err; + } + + // read the rest of the buffer if needed + if (rem_len > 0) { + err = lwmqtt_read_from_network(client, 1 + len, rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + } + + // adjust counter + *read += 1 + len + rem_len; + + return LWMQTT_SUCCESS; +} + +static lwmqtt_err_t lwmqtt_send_packet_in_buffer(lwmqtt_client_t *client, size_t length) { + // write to network + lwmqtt_err_t err = lwmqtt_write_to_network(client, 0, length); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // reset keep alive timer + client->timer_set(client->keep_alive_timer, client->keep_alive_interval); + + return LWMQTT_SUCCESS; +} + +static lwmqtt_err_t lwmqtt_cycle(lwmqtt_client_t *client, size_t *read, lwmqtt_packet_type_t *packet_type) { + // read next packet from the network + lwmqtt_err_t err = lwmqtt_read_packet_in_buffer(client, read, packet_type); + if (err != LWMQTT_SUCCESS) { + return err; + } else if (*packet_type == LWMQTT_NO_PACKET) { + return LWMQTT_SUCCESS; + } + + switch (*packet_type) { + // handle publish packets + case LWMQTT_PUBLISH_PACKET: { + // decode publish packet + bool dup; + uint16_t packet_id; + lwmqtt_string_t topic; + lwmqtt_message_t msg; + err = lwmqtt_decode_publish(client->read_buf, client->read_buf_size, &dup, &packet_id, &topic, &msg); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // call callback if set + if (client->callback != NULL) { + client->callback(client, client->callback_ref, topic, msg); + } + + // break early on qos zero + if (msg.qos == LWMQTT_QOS0) { + break; + } + + // define ack packet + lwmqtt_packet_type_t ack_type = LWMQTT_NO_PACKET; + if (msg.qos == LWMQTT_QOS1) { + ack_type = LWMQTT_PUBACK_PACKET; + } else if (msg.qos == LWMQTT_QOS2) { + ack_type = LWMQTT_PUBREC_PACKET; + } + + // encode ack packet + size_t len; + err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, ack_type, false, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send ack packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + break; + } + + // handle pubrec packets + case LWMQTT_PUBREC_PACKET: { + // decode pubrec packet + bool dup; + uint16_t packet_id; + err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_PUBREC_PACKET, &dup, &packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // encode pubrel packet + size_t len; + err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, LWMQTT_PUBREL_PACKET, 0, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send pubrel packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + break; + } + + // handle pubrel packets + case LWMQTT_PUBREL_PACKET: { + // decode pubrec packet + bool dup; + uint16_t packet_id; + err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_PUBREL_PACKET, &dup, &packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // encode pubcomp packet + size_t len; + err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, LWMQTT_PUBCOMP_PACKET, 0, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send pubcomp packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + break; + } + + // handle pingresp packets + case LWMQTT_PINGRESP_PACKET: { + // set flag + client->pong_pending = false; + + break; + } + + // handle all other packets + default: { break; } + } + + return LWMQTT_SUCCESS; +} + +static lwmqtt_err_t lwmqtt_cycle_until(lwmqtt_client_t *client, lwmqtt_packet_type_t *packet_type, size_t available, + lwmqtt_packet_type_t needle) { + // prepare counter + size_t read = 0; + + // loop until timeout has been reached + do { + // do one cycle + lwmqtt_err_t err = lwmqtt_cycle(client, &read, packet_type); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // return when one packet has been successfully read when no availability has been given + if (needle == LWMQTT_NO_PACKET && available == 0) { + return LWMQTT_SUCCESS; + } + + // otherwise check if needle has been found + if (*packet_type == needle) { + return LWMQTT_SUCCESS; + } + } while (client->timer_get(client->command_timer) > 0 && (available == 0 || read < available)); + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_yield(lwmqtt_client_t *client, size_t available, uint32_t timeout) { + // set command timer + client->timer_set(client->command_timer, timeout); + + // cycle until timeout has been reached + lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; + lwmqtt_err_t err = lwmqtt_cycle_until(client, &packet_type, available, LWMQTT_NO_PACKET); + if (err != LWMQTT_SUCCESS) { + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_connect(lwmqtt_client_t *client, lwmqtt_options_t options, lwmqtt_will_t *will, + lwmqtt_return_code_t *return_code, uint32_t timeout) { + // set command timer + client->timer_set(client->command_timer, timeout); + + // save keep alive interval (take 75% to be a little earlier than actually needed) + client->keep_alive_interval = (uint32_t)(options.keep_alive * 750); + + // set keep alive timer + client->timer_set(client->keep_alive_timer, client->keep_alive_interval); + + // reset pong pending flag + client->pong_pending = false; + + // encode connect packet + size_t len; + lwmqtt_err_t err = lwmqtt_encode_connect(client->write_buf, client->write_buf_size, &len, options, will); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // wait for connack packet + lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; + err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_CONNACK_PACKET); + if (err != LWMQTT_SUCCESS) { + return err; + } else if (packet_type != LWMQTT_CONNACK_PACKET) { + return LWMQTT_MISSING_OR_WRONG_PACKET; + } + + // decode connack packet + bool session_present; + err = lwmqtt_decode_connack(client->read_buf, client->read_buf_size, &session_present, return_code); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // return error if connection was not accepted + if (*return_code != LWMQTT_CONNECTION_ACCEPTED) { + return LWMQTT_CONNECTION_DENIED; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_subscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, lwmqtt_qos_t *qos, + uint32_t timeout) { + // set command timer + client->timer_set(client->command_timer, timeout); + + // encode subscribe packet + size_t len; + lwmqtt_err_t err = lwmqtt_encode_subscribe(client->write_buf, client->write_buf_size, &len, + lwmqtt_get_next_packet_id(client), count, topic_filter, qos); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // wait for suback packet + lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; + err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_SUBACK_PACKET); + if (err != LWMQTT_SUCCESS) { + return err; + } else if (packet_type != LWMQTT_SUBACK_PACKET) { + return LWMQTT_MISSING_OR_WRONG_PACKET; + } + + // decode packet + int suback_count = 0; + lwmqtt_qos_t granted_qos[count]; + uint16_t packet_id; + err = lwmqtt_decode_suback(client->read_buf, client->read_buf_size, &packet_id, count, &suback_count, granted_qos); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // check suback codes + for (int i = 0; i < suback_count; i++) { + if (granted_qos[i] == LWMQTT_QOS_FAILURE) { + return LWMQTT_FAILED_SUBSCRIPTION; + } + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_subscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, lwmqtt_qos_t qos, + uint32_t timeout) { + return lwmqtt_subscribe(client, 1, &topic_filter, &qos, timeout); +} + +lwmqtt_err_t lwmqtt_unsubscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, uint32_t timeout) { + // set command timer + client->timer_set(client->command_timer, timeout); + + // encode unsubscribe packet + size_t len; + lwmqtt_err_t err = lwmqtt_encode_unsubscribe(client->write_buf, client->write_buf_size, &len, + lwmqtt_get_next_packet_id(client), count, topic_filter); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send unsubscribe packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // wait for unsuback packet + lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; + err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_UNSUBACK_PACKET); + if (err != LWMQTT_SUCCESS) { + return err; + } else if (packet_type != LWMQTT_UNSUBACK_PACKET) { + return LWMQTT_MISSING_OR_WRONG_PACKET; + } + + // decode unsuback packet + bool dup; + uint16_t packet_id; + err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_UNSUBACK_PACKET, &dup, &packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_unsubscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, uint32_t timeout) { + return lwmqtt_unsubscribe(client, 1, &topic_filter, timeout); +} + +lwmqtt_err_t lwmqtt_publish(lwmqtt_client_t *client, lwmqtt_string_t topic, lwmqtt_message_t message, + uint32_t timeout) { + // set command timer + client->timer_set(client->command_timer, timeout); + + // add packet id if at least qos 1 + uint16_t packet_id = 0; + if (message.qos == LWMQTT_QOS1 || message.qos == LWMQTT_QOS2) { + packet_id = lwmqtt_get_next_packet_id(client); + } + + // encode publish packet + size_t len = 0; + lwmqtt_err_t err = + lwmqtt_encode_publish(client->write_buf, client->write_buf_size, &len, 0, packet_id, topic, message); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // immediately return on qos zero + if (message.qos == LWMQTT_QOS0) { + return LWMQTT_SUCCESS; + } + + // define ack packet + lwmqtt_packet_type_t ack_type = LWMQTT_NO_PACKET; + if (message.qos == LWMQTT_QOS1) { + ack_type = LWMQTT_PUBACK_PACKET; + } else if (message.qos == LWMQTT_QOS2) { + ack_type = LWMQTT_PUBCOMP_PACKET; + } + + // wait for ack packet + lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; + err = lwmqtt_cycle_until(client, &packet_type, 0, ack_type); + if (err != LWMQTT_SUCCESS) { + return err; + } else if (packet_type != ack_type) { + return LWMQTT_MISSING_OR_WRONG_PACKET; + } + + // decode ack packet + bool dup; + err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, ack_type, &dup, &packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_disconnect(lwmqtt_client_t *client, uint32_t timeout) { + // set command timer + client->timer_set(client->command_timer, timeout); + + // encode disconnect packet + size_t len; + lwmqtt_err_t err = lwmqtt_encode_zero(client->write_buf, client->write_buf_size, &len, LWMQTT_DISCONNECT_PACKET); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send disconnected packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_keep_alive(lwmqtt_client_t *client, uint32_t timeout) { + // set command timer + client->timer_set(client->command_timer, timeout); + + // return immediately if keep alive interval is zero + if (client->keep_alive_interval == 0) { + return LWMQTT_SUCCESS; + } + + // return immediately if no ping is due + if (client->timer_get(client->keep_alive_timer) > 0) { + return LWMQTT_SUCCESS; + } + + // a ping is due + + // fail immediately if a pong is already pending + if (client->pong_pending) { + return LWMQTT_PONG_TIMEOUT; + } + + // encode pingreq packet + size_t len; + lwmqtt_err_t err = lwmqtt_encode_zero(client->write_buf, client->write_buf_size, &len, LWMQTT_PINGREQ_PACKET); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // send packet + err = lwmqtt_send_packet_in_buffer(client, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // set flag + client->pong_pending = true; + + return LWMQTT_SUCCESS; +} diff --git a/lib/MQTT/src/lwmqtt/helpers.c b/lib/MQTT/src/lwmqtt/helpers.c new file mode 100644 index 000000000..8b9d37e5b --- /dev/null +++ b/lib/MQTT/src/lwmqtt/helpers.c @@ -0,0 +1,251 @@ +#include + +#include "helpers.h" + +uint8_t lwmqtt_read_bits(uint8_t byte, int pos, int num) { + return (byte & (uint8_t)((~(0xFF << num)) << pos)) >> pos; +} + +void lwmqtt_write_bits(uint8_t *byte, uint8_t value, int pos, int num) { + *byte = (*byte & ~(uint8_t)((~(0xFF << num)) << pos)) | (value << pos); +} + +lwmqtt_err_t lwmqtt_read_data(uint8_t **buf, const uint8_t *buf_end, uint8_t **data, size_t len) { + // check zero length + if (len == 0) { + *data = NULL; + return LWMQTT_SUCCESS; + } + + // check buffer size + if ((size_t)(buf_end - (*buf)) < len) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // read data + *data = *buf; + + // advance pointer + *buf += len; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_write_data(uint8_t **buf, const uint8_t *buf_end, uint8_t *data, size_t len) { + // check zero length + if (len == 0) { + return LWMQTT_SUCCESS; + } + + // check buffer size + if ((size_t)(buf_end - (*buf)) < len) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // write data + memcpy(*buf, data, len); + + // advance pointer + *buf += len; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_read_num(uint8_t **buf, const uint8_t *buf_end, uint16_t *num) { + // check buffer size + if ((size_t)(buf_end - (*buf)) < 2) { + *num = 0; + return LWMQTT_BUFFER_TOO_SHORT; + } + + // read two byte integer + *num = (uint16_t)256 * (*buf)[0] + (*buf)[1]; + + // adjust pointer + *buf += 2; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_write_num(uint8_t **buf, const uint8_t *buf_end, uint16_t num) { + // check buffer size + if ((size_t)(buf_end - (*buf)) < 2) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // write bytes + (*buf)[0] = (uint8_t)(num / 256); + (*buf)[1] = (uint8_t)(num % 256); + + // adjust pointer + *buf += 2; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_read_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t *str) { + // read length + uint16_t len; + lwmqtt_err_t err = lwmqtt_read_num(buf, buf_end, &len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // read data + err = lwmqtt_read_data(buf, buf_end, (uint8_t **)&str->data, len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // set length + str->len = len; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_write_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t str) { + // write string length + lwmqtt_err_t err = lwmqtt_write_num(buf, buf_end, str.len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write data + err = lwmqtt_write_data(buf, buf_end, (uint8_t *)str.data, str.len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_read_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t *byte) { + // check buffer size + if ((size_t)(buf_end - (*buf)) < 1) { + *byte = 0; + return LWMQTT_BUFFER_TOO_SHORT; + } + + // read byte + *byte = (*buf)[0]; + + // adjust pointer + *buf += 1; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_write_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t byte) { + // check buffer size + if ((size_t)(buf_end - (*buf)) < 1) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // write byte + (*buf)[0] = byte; + + // adjust pointer + *buf += 1; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_varnum_length(uint32_t varnum, int *len) { + if (varnum < 128) { + *len = 1; + return LWMQTT_SUCCESS; + } else if (varnum < 16384) { + *len = 2; + return LWMQTT_SUCCESS; + } else if (varnum < 2097151) { + *len = 3; + return LWMQTT_SUCCESS; + } else if (varnum < 268435455) { + *len = 4; + return LWMQTT_SUCCESS; + } else { + *len = 0; + return LWMQTT_VARNUM_OVERFLOW; + } +} + +lwmqtt_err_t lwmqtt_read_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t *varnum) { + // prepare last byte + uint8_t byte; + + // prepare multiplier + uint32_t multiplier = 1; + + // prepare length + size_t len = 0; + + // initialize number + *varnum = 0; + + // decode variadic number + do { + // increment length + len++; + + // return error if buffer is to small + if ((size_t)(buf_end - (*buf)) < len) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // return error if the length has overflowed + if (len > 4) { + return LWMQTT_VARNUM_OVERFLOW; + } + + // read byte + byte = (*buf)[len - 1]; + + // add byte to number + *varnum += (byte & 127) * multiplier; + + // increase multiplier + multiplier *= 128; + } while ((byte & 128) != 0); + + // adjust pointer + *buf += len; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_write_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t varnum) { + // init len counter + size_t len = 0; + + // encode variadic number + do { + // check overflow + if (len == 4) { + return LWMQTT_VARNUM_OVERFLOW; + } + + // return error if buffer is to small + if ((size_t)(buf_end - (*buf)) < len + 1) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // calculate current byte + uint8_t byte = (uint8_t)(varnum % 128); + + // change remaining length + varnum /= 128; + + // set the top bit of this byte if there are more to encode + if (varnum > 0) { + byte |= 0x80; + } + + // write byte + (*buf)[len++] = byte; + } while (varnum > 0); + + // adjust pointer + *buf += len; + + return LWMQTT_SUCCESS; +} diff --git a/lib/MQTT/src/lwmqtt/helpers.h b/lib/MQTT/src/lwmqtt/helpers.h new file mode 100644 index 000000000..978eaf4a5 --- /dev/null +++ b/lib/MQTT/src/lwmqtt/helpers.h @@ -0,0 +1,137 @@ +#ifndef LWMQTT_HELPERS_H +#define LWMQTT_HELPERS_H + +#include "lwmqtt.h" + +/** + * Reads bits from a byte. + * + * @param byte - The byte to read from. + * @param pos - The position of the first bit. + * @param num - The number of bits to read. + * @return The read bits as a byte. + */ +uint8_t lwmqtt_read_bits(uint8_t byte, int pos, int num); + +/** + * Write bits to a byte. + * + * @param byte - The byte to write bits to. + * @param value - The bits to write as a byte. + * @param pos - The position of the first bit. + * @param num - The number of bits to write. + */ +void lwmqtt_write_bits(uint8_t *byte, uint8_t value, int pos, int num); + +/** + * Reads arbitrary data from the specified buffer. The pointer is incremented by bytes read. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param data - Pointer to beginning of data. + * @param len - The amount of data to read. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_read_data(uint8_t **buf, const uint8_t *buf_end, uint8_t **data, size_t len); + +/** + * Writes arbitrary data to the specified buffer. The pointer is incremented by the bytes written. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param data - Pointer to the to be written data. + * @param len - The amount of data to write. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_write_data(uint8_t **buf, const uint8_t *buf_end, uint8_t *data, size_t len); + +/** + * Reads two byte number from the specified buffer. The pointer is incremented by two. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param num - The read number. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_read_num(uint8_t **buf, const uint8_t *buf_end, uint16_t *num); + +/** + * Writes a two byte number to the specified buffer. The pointer is incremented by two. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param num - The number to write. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_write_num(uint8_t **buf, const uint8_t *buf_end, uint16_t num); + +/** + * Reads a string from the specified buffer into the passed object. The pointer is incremented by the bytes read. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param str - The object into which the data is to be read. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_read_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t *str); + +/** + * Writes a string to the specified buffer. The pointer is incremented by the bytes written. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param str - The string to write. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_write_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t str); + +/** + * Reads one byte from the buffer. The pointer is incremented by one. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param byte - The read byte. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_read_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t *byte); + +/** + * Writes one byte to the specified buffer. The pointer is incremented by one. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param byte - The byte to write. + * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. + */ +lwmqtt_err_t lwmqtt_write_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t byte); + +/** + * Returns the amount of bytes required by the variable number. + * + * @param varnum - The number to check. + * @param len - The required length; + * @return LWMQTT_SUCCESS or LWMQTT_VARNUM_OVERFLOW. + */ +lwmqtt_err_t lwmqtt_varnum_length(uint32_t varnum, int *len); + +/** + * Reads a variable number from the specified buffer. The pointer is incremented by the bytes read. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param varnum - The read varnum. + * @return LWMQTT_SUCCESS, LWMQTT_BUFFER_TOO_SHORT or LWMQTT_VARNUM_OVERFLOW. + */ +lwmqtt_err_t lwmqtt_read_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t *varnum); + +/** + * Writes a variable number to the specified buffer. The pointer is incremented by the bytes written. + * + * @param buf - Pointer to the buffer. + * @param buf_end - Pointer to the end of the buffer. + * @param varnum - The number to write. + * @return LWMQTT_SUCCESS, LWMQTT_BUFFER_TOO_SHORT or LWMQTT_VARNUM_OVERFLOW. + */ +lwmqtt_err_t lwmqtt_write_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t varnum); + +#endif diff --git a/lib/MQTT/src/lwmqtt/lwmqtt.h b/lib/MQTT/src/lwmqtt/lwmqtt.h new file mode 100644 index 000000000..e63ad1004 --- /dev/null +++ b/lib/MQTT/src/lwmqtt/lwmqtt.h @@ -0,0 +1,373 @@ +#ifndef LWMQTT_H +#define LWMQTT_H + +#include +#include +#include + +/** + * The error type used by all exposed APIs. + */ +typedef enum { + LWMQTT_SUCCESS = 0, + LWMQTT_BUFFER_TOO_SHORT = -1, + LWMQTT_VARNUM_OVERFLOW = -2, + LWMQTT_NETWORK_FAILED_CONNECT = -3, + LWMQTT_NETWORK_TIMEOUT = -4, + LWMQTT_NETWORK_FAILED_READ = -5, + LWMQTT_NETWORK_FAILED_WRITE = -6, + LWMQTT_REMAINING_LENGTH_OVERFLOW = -7, + LWMQTT_REMAINING_LENGTH_MISMATCH = -8, + LWMQTT_MISSING_OR_WRONG_PACKET = -9, + LWMQTT_CONNECTION_DENIED = -10, + LWMQTT_FAILED_SUBSCRIPTION = -11, + LWMQTT_SUBACK_ARRAY_OVERFLOW = -12, + LWMQTT_PONG_TIMEOUT = -13, +} lwmqtt_err_t; + +/** + * A common string object. + */ +typedef struct { + uint16_t len; + char *data; +} lwmqtt_string_t; + +/** + * The initializer for string objects. + */ +#define lwmqtt_default_string \ + { 0, NULL } + +/** + * Returns a string object for the passed C string. + * + * @param str - The C string. + * @return A string object. + */ +lwmqtt_string_t lwmqtt_string(const char *str); + +/** + * Compares a string object to a C string. + * + * @param a - The string object to compare. + * @param b - The C string to compare. + * @return Similarity e.g. strcmp(). + */ +int lwmqtt_strcmp(lwmqtt_string_t a, const char *b); + +/** + * The available QOS levels. + */ +typedef enum { LWMQTT_QOS0 = 0, LWMQTT_QOS1 = 1, LWMQTT_QOS2 = 2, LWMQTT_QOS_FAILURE = 128 } lwmqtt_qos_t; + +/** + * The message object used to publish and receive messages. + */ +typedef struct { + lwmqtt_qos_t qos; + bool retained; + uint8_t *payload; + size_t payload_len; +} lwmqtt_message_t; + +/** + * The initializer for message objects. + */ +#define lwmqtt_default_message \ + { LWMQTT_QOS0, false, NULL, 0 } + +/** + * Forward declaration of the client object. + */ +typedef struct lwmqtt_client_t lwmqtt_client_t; + +/** + * The callback used to read from a network object. + * + * The callbacks is expected to read up to the amount of bytes in to the passed buffer. It should block the specified + * timeout and wait for more incoming data. + * + * @param ref - A custom reference. + * @param buf - The buffer. + * @param len - The length of the buffer. + * @param read - Variable that must be set with the amount of read bytes. + * @param timeout - The timeout in milliseconds for the operation. + */ +typedef lwmqtt_err_t (*lwmqtt_network_read_t)(void *ref, uint8_t *buf, size_t len, size_t *read, uint32_t timeout); + +/** + * The callback used to write to a network object. + * + * The callback is expected to write up to the amount of bytes from the passed buffer. It should wait up to the + * specified timeout to write the specified data to the network. + * + * @param ref - A custom reference. + * @param buf - The buffer. + * @param len - The length of the buffer. + * @param sent - Variable that must be set with the amount of written bytes. + * @param timeout - The timeout in milliseconds for the operation. + */ +typedef lwmqtt_err_t (*lwmqtt_network_write_t)(void *ref, uint8_t *buf, size_t len, size_t *sent, uint32_t timeout); + +/** + * The callback used to set a timer. + * + * @param ref - A custom reference. + * @param timeout - The amount of milliseconds until the deadline. + */ +typedef void (*lwmqtt_timer_set_t)(void *ref, uint32_t timeout); + +/** + * The callback used to get a timers value. + * + * @param - A custom reference. + * @return The amount of milliseconds until the deadline. May return negative numbers if the deadline has been reached. + */ +typedef int32_t (*lwmqtt_timer_get_t)(void *ref); + +/** + * The callback used to forward incoming messages. + * + * Note: The callback is mostly executed because of a call to lwmqtt_yield() that processes incoming messages. However, + * it is possible that the callback is also executed during a call to lwmqtt_subscribe(), lwmqtt_publish() or + * lwmqtt_unsubscribe() if incoming messages are received between the required acknowledgements. It is therefore not + * recommended to call any further lwmqtt methods in the callback as this might result in weird call stacks. The + * callback should place the received messages in a queue and dispatch them after the caller has returned. + */ +typedef void (*lwmqtt_callback_t)(lwmqtt_client_t *client, void *ref, lwmqtt_string_t str, lwmqtt_message_t msg); + +/** + * The client object. + */ +struct lwmqtt_client_t { + uint16_t last_packet_id; + uint32_t keep_alive_interval; + bool pong_pending; + + size_t write_buf_size, read_buf_size; + uint8_t *write_buf, *read_buf; + + lwmqtt_callback_t callback; + void *callback_ref; + + void *network; + lwmqtt_network_read_t network_read; + lwmqtt_network_write_t network_write; + + void *keep_alive_timer; + void *command_timer; + lwmqtt_timer_set_t timer_set; + lwmqtt_timer_get_t timer_get; +}; + +/** + * Will initialize the specified client object. + * + * @param client - The client object. + * @param write_buf - The write buffer. + * @param write_buf_size - The write buffer size. + * @param read_buf - The read buffer. + * @param read_buf_size - The read buffer size. + */ +void lwmqtt_init(lwmqtt_client_t *client, uint8_t *write_buf, size_t write_buf_size, uint8_t *read_buf, + size_t read_buf_size); + +/** + * Will set the network reference and callbacks for this client object. + * + * @param client - The client object. + * @param ref - The reference to the network object. + * @param read - The read callback. + * @param write - The write callback. + */ +void lwmqtt_set_network(lwmqtt_client_t *client, void *ref, lwmqtt_network_read_t read, lwmqtt_network_write_t write); + +/** + * Will set the timer references and callbacks for this client object. + * + * @param client - The client object. + * @param keep_alive_timer - The reference to the keep alive timer. + * @param command_timer - The reference to the command timer. + * @param set - The set callback. + * @param get - The get callback. + */ +void lwmqtt_set_timers(lwmqtt_client_t *client, void *keep_alive_timer, void *command_timer, lwmqtt_timer_set_t set, + lwmqtt_timer_get_t get); + +/** + * Will set the callback used to receive incoming messages. + * + * @param client - The client object. + * @param ref - A custom reference that will passed to the callback. + * @param cb - The callback to be called. + */ +void lwmqtt_set_callback(lwmqtt_client_t *client, void *ref, lwmqtt_callback_t cb); + +/** + * The object defining the last will of a client. + */ +typedef struct { + lwmqtt_string_t topic; + lwmqtt_qos_t qos; + bool retained; + lwmqtt_string_t payload; +} lwmqtt_will_t; + +/** + * The default initializer for the will object. + */ +#define lwmqtt_default_will \ + { lwmqtt_default_string, LWMQTT_QOS0, false, lwmqtt_default_string } + +/** + * The object containing the connection options for a client. + */ +typedef struct { + lwmqtt_string_t client_id; + uint16_t keep_alive; + bool clean_session; + lwmqtt_string_t username; + lwmqtt_string_t password; +} lwmqtt_options_t; + +/** + * The default initializer for the options object. + */ +#define lwmqtt_default_options \ + { lwmqtt_default_string, 60, true, lwmqtt_default_string, lwmqtt_default_string } + +/** + * The available return codes transported by the connack packet. + */ +typedef enum { + LWMQTT_CONNECTION_ACCEPTED = 0, + LWMQTT_UNACCEPTABLE_PROTOCOL = 1, + LWMQTT_IDENTIFIER_REJECTED = 2, + LWMQTT_SERVER_UNAVAILABLE = 3, + LWMQTT_BAD_USERNAME_OR_PASSWORD = 4, + LWMQTT_NOT_AUTHORIZED = 5, + LWMQTT_UNKNOWN_RETURN_CODE = 6 +} lwmqtt_return_code_t; + +/** + * Will send a connect packet and wait for a connack response and set the return code. + * + * The network object must already be connected to the server. An error is returned if the broker rejects the + * connection. + * + * @param client - The client object. + * @param options - The options object. + * @param will - The will object. + * @param return_code - The variable that will receive the return code. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_connect(lwmqtt_client_t *client, lwmqtt_options_t options, lwmqtt_will_t *will, + lwmqtt_return_code_t *return_code, uint32_t timeout); + +/** + * Will send a publish packet and wait for all acks to complete. + * + * Note: The message callback might be called with incoming messages as part of this call. + * + * @param client - The client object. + * @param topic - The topic. + * @param message - The message. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_publish(lwmqtt_client_t *client, lwmqtt_string_t topic, lwmqtt_message_t msg, uint32_t timeout); + +/** + * Will send a subscribe packet with multiple topic filters plus QOS levels and wait for the suback to complete. + * + * Note: The message callback might be called with incoming messages as part of this call. + * + * @param client - The client object. + * @param count - The number of topic filters and QOS levels. + * @param topic_filter - The list of topic filters. + * @param qos - The list of QOS levels. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_subscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, lwmqtt_qos_t *qos, + uint32_t timeout); + +/** + * Will send a subscribe packet with a single topic filter plus QOS level and wait for the suback to complete. + * + * Note: The message callback might be called with incoming messages as part of this call. + * + * @param client - The client object. + * @param topic_filter - The topic filter. + * @param qos - The QOS level. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_subscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, lwmqtt_qos_t qos, + uint32_t timeout); + +/** + * Will send an unsubscribe packet with multiple topic filters and wait for the unsuback to complete. + * + * Note: The message callback might be called with incoming messages as part of this call. + * + * @param client - The client object. + * @param count - The number of topic filters. + * @param topic_filter - The topic filter. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_unsubscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, uint32_t timeout); + +/** + * Will send an unsubscribe packet with a single topic filter and wait for the unsuback to complete. + * + * Note: The message callback might be called with incoming messages as part of this call. + * + * @param client - The client object. + * @param topic_filter - The topic filter. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_unsubscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, uint32_t timeout); + +/** + * Will send a disconnect packet and finish the client. + * + * @param client - The client object. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_disconnect(lwmqtt_client_t *client, uint32_t timeout); + +/** + * Will yield control to the client and receive incoming packets from the network. + * + * Applications may peek on the network if there is data available to read before calling yield and potentially block + * until the timeout is reached. Furthermore, applications may specify the amount of bytes available to read in order + * to constrain the yield to only receive packets that are already inflight. + * + * If no availability data is given the yield will return after one packet has been successfully read or the deadline + * has been reached but no single bytes has been received. + * + * Note: The message callback might be called with incoming messages as part of this call. + * + * @param client - The client object. + * @param available - The available bytes to read. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_yield(lwmqtt_client_t *client, size_t available, uint32_t timeout); + +/** + * Will yield control to the client to keep the connection alive. + * + * @param client - The client object. + * @param timeout - The command timeout. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_keep_alive(lwmqtt_client_t *client, uint32_t timeout); + +#endif // LWMQTT_H diff --git a/lib/MQTT/src/lwmqtt/packet.c b/lib/MQTT/src/lwmqtt/packet.c new file mode 100644 index 000000000..5ed579fd2 --- /dev/null +++ b/lib/MQTT/src/lwmqtt/packet.c @@ -0,0 +1,742 @@ +#include "packet.h" + +lwmqtt_err_t lwmqtt_detect_packet_type(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type) { + // set default packet type + *packet_type = LWMQTT_NO_PACKET; + + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // prepare header + uint8_t header; + + // read header + lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // get packet type + *packet_type = (lwmqtt_packet_type_t)lwmqtt_read_bits(header, 4, 4); + + // check if packet type is correct and can be received + switch (*packet_type) { + case LWMQTT_CONNACK_PACKET: + case LWMQTT_PUBLISH_PACKET: + case LWMQTT_PUBACK_PACKET: + case LWMQTT_PUBREC_PACKET: + case LWMQTT_PUBREL_PACKET: + case LWMQTT_PUBCOMP_PACKET: + case LWMQTT_SUBACK_PACKET: + case LWMQTT_UNSUBACK_PACKET: + case LWMQTT_PINGRESP_PACKET: + return LWMQTT_SUCCESS; + default: + *packet_type = LWMQTT_NO_PACKET; + return LWMQTT_MISSING_OR_WRONG_PACKET; + } +} + +lwmqtt_err_t lwmqtt_detect_remaining_length(uint8_t *buf, size_t buf_len, uint32_t *rem_len) { + // prepare pointer + uint8_t *ptr = buf; + + // attempt to decode remaining length + lwmqtt_err_t err = lwmqtt_read_varnum(&ptr, buf + buf_len, rem_len); + if (err == LWMQTT_VARNUM_OVERFLOW) { + *rem_len = 0; + return LWMQTT_REMAINING_LENGTH_OVERFLOW; + } else if (err != LWMQTT_SUCCESS) { + *rem_len = 0; + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_encode_connect(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_options_t options, + lwmqtt_will_t *will) { + // prepare pointers + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // fixed header is 10 + uint32_t rem_len = 10; + + // add client id to remaining length + rem_len += options.client_id.len + 2; + + // add will if present to remaining length + if (will != NULL) { + rem_len += will->topic.len + 2 + will->payload.len + 2; + } + + // add username if present to remaining length + if (options.username.len > 0) { + rem_len += options.username.len + 2; + + // add password if present to remaining length + if (options.password.len > 0) { + rem_len += options.password.len + 2; + } + } + + // check remaining length length + int rem_len_len; + lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); + if (err == LWMQTT_VARNUM_OVERFLOW) { + return LWMQTT_REMAINING_LENGTH_OVERFLOW; + } + + // prepare header + uint8_t header = 0; + lwmqtt_write_bits(&header, LWMQTT_CONNECT_PACKET, 4, 4); + + // write header + err = lwmqtt_write_byte(&buf_ptr, buf_end, header); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write remaining length + err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write version string + err = lwmqtt_write_string(&buf_ptr, buf_end, lwmqtt_string("MQTT")); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write version number + err = lwmqtt_write_byte(&buf_ptr, buf_end, 4); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // prepare flags + uint8_t flags = 0; + + // set clean session + lwmqtt_write_bits(&flags, (uint8_t)(options.clean_session), 1, 1); + + // set will flags if present + if (will != NULL) { + lwmqtt_write_bits(&flags, 1, 2, 1); + lwmqtt_write_bits(&flags, will->qos, 3, 2); + lwmqtt_write_bits(&flags, (uint8_t)(will->retained), 5, 1); + } + + // set username flag if present + if (options.username.len > 0) { + lwmqtt_write_bits(&flags, 1, 6, 1); + + // set password flag if present + if (options.password.len > 0) { + lwmqtt_write_bits(&flags, 1, 7, 1); + } + } + + // write flags + err = lwmqtt_write_byte(&buf_ptr, buf_end, flags); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write keep alive + err = lwmqtt_write_num(&buf_ptr, buf_end, options.keep_alive); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write client id + err = lwmqtt_write_string(&buf_ptr, buf_end, options.client_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write will if present + if (will != NULL) { + // write topic + err = lwmqtt_write_string(&buf_ptr, buf_end, will->topic); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write payload length + err = lwmqtt_write_num(&buf_ptr, buf_end, (uint16_t)will->payload.len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write payload + err = lwmqtt_write_data(&buf_ptr, buf_end, (uint8_t *)will->payload.data, will->payload.len); + if (err != LWMQTT_SUCCESS) { + return err; + } + } + + // write username if present + if (options.username.len > 0) { + err = lwmqtt_write_string(&buf_ptr, buf_end, options.username); + if (err != LWMQTT_SUCCESS) { + return err; + } + } + + // write password if present + if (options.username.len > 0 && options.password.len > 0) { + err = lwmqtt_write_string(&buf_ptr, buf_end, options.password); + if (err != LWMQTT_SUCCESS) { + return err; + } + } + + // set written length + *len = buf_ptr - buf; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_decode_connack(uint8_t *buf, size_t buf_len, bool *session_present, + lwmqtt_return_code_t *return_code) { + // prepare pointers + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // read header + uint8_t header; + lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // check packet type + if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_CONNACK_PACKET) { + return LWMQTT_MISSING_OR_WRONG_PACKET; + } + + // read remaining length + uint32_t rem_len; + err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // check remaining length + if (rem_len != 2) { + return LWMQTT_REMAINING_LENGTH_MISMATCH; + } + + // read flags + uint8_t flags; + err = lwmqtt_read_byte(&buf_ptr, buf_end, &flags); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // read return code + uint8_t raw_return_code; + err = lwmqtt_read_byte(&buf_ptr, buf_end, &raw_return_code); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // get session present + *session_present = lwmqtt_read_bits(flags, 7, 1) == 1; + + // get return code + switch (raw_return_code) { + case 0: + *return_code = LWMQTT_CONNECTION_ACCEPTED; + break; + case 1: + *return_code = LWMQTT_UNACCEPTABLE_PROTOCOL; + break; + case 2: + *return_code = LWMQTT_IDENTIFIER_REJECTED; + break; + case 3: + *return_code = LWMQTT_SERVER_UNAVAILABLE; + break; + case 4: + *return_code = LWMQTT_BAD_USERNAME_OR_PASSWORD; + break; + case 5: + *return_code = LWMQTT_NOT_AUTHORIZED; + break; + default: + *return_code = LWMQTT_UNKNOWN_RETURN_CODE; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_encode_zero(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // write header + uint8_t header = 0; + lwmqtt_write_bits(&header, packet_type, 4, 4); + lwmqtt_err_t err = lwmqtt_write_byte(&buf_ptr, buf_end, header); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write remaining length + err = lwmqtt_write_varnum(&buf_ptr, buf_end, 0); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // set length + *len = buf_ptr - buf; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_decode_ack(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t packet_type, bool *dup, + uint16_t *packet_id) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // read header + uint8_t header = 0; + lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // check packet type + if (lwmqtt_read_bits(header, 4, 4) != packet_type) { + return LWMQTT_MISSING_OR_WRONG_PACKET; + } + + // get dup + *dup = lwmqtt_read_bits(header, 3, 1) == 1; + + // read remaining length + uint32_t rem_len; + err = lwmqtt_read_varnum(&buf_ptr, buf + buf_len, &rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // check remaining length + if (rem_len != 2) { + return LWMQTT_REMAINING_LENGTH_MISMATCH; + } + + // read packet id + err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_encode_ack(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type, bool dup, + uint16_t packet_id) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // prepare header + uint8_t header = 0; + + // set packet type + lwmqtt_write_bits(&header, packet_type, 4, 4); + + // set dup + lwmqtt_write_bits(&header, (uint8_t)(dup), 3, 1); + + // set qos + lwmqtt_write_bits(&header, (uint8_t)(packet_type == LWMQTT_PUBREL_PACKET ? LWMQTT_QOS1 : LWMQTT_QOS0), 1, 2); + + // write header + lwmqtt_err_t err = lwmqtt_write_byte(&buf_ptr, buf_end, header); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write remaining length + err = lwmqtt_write_varnum(&buf_ptr, buf_end, 2); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write packet id + err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // set written length + *len = buf_ptr - buf; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_decode_publish(uint8_t *buf, size_t buf_len, bool *dup, uint16_t *packet_id, lwmqtt_string_t *topic, + lwmqtt_message_t *msg) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // read header + uint8_t header; + lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // check packet type + if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_PUBLISH_PACKET) { + return LWMQTT_MISSING_OR_WRONG_PACKET; + } + + // get dup + *dup = lwmqtt_read_bits(header, 3, 1) == 1; + + // get retained + msg->retained = lwmqtt_read_bits(header, 0, 1) == 1; + + // get qos + switch (lwmqtt_read_bits(header, 1, 2)) { + case 0: + msg->qos = LWMQTT_QOS0; + break; + case 1: + msg->qos = LWMQTT_QOS1; + break; + case 2: + msg->qos = LWMQTT_QOS2; + break; + default: + msg->qos = LWMQTT_QOS0; + break; + } + + // read remaining length + uint32_t rem_len; + err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // check remaining length (topic length) + if (rem_len < 2) { + return LWMQTT_REMAINING_LENGTH_MISMATCH; + } + + // check buffer capacity + if ((uint32_t)(buf_end - buf_ptr) < rem_len) { + return LWMQTT_BUFFER_TOO_SHORT; + } + + // reset buf end + buf_end = buf_ptr + rem_len; + + // read topic + err = lwmqtt_read_string(&buf_ptr, buf_end, topic); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // read packet id if qos is at least 1 + if (msg->qos > 0) { + err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + } else { + *packet_id = 0; + } + + // set payload length + msg->payload_len = buf_end - buf_ptr; + + // read payload + err = lwmqtt_read_data(&buf_ptr, buf_end, &msg->payload, buf_end - buf_ptr); + if (err != LWMQTT_SUCCESS) { + return err; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_encode_publish(uint8_t *buf, size_t buf_len, size_t *len, bool dup, uint16_t packet_id, + lwmqtt_string_t topic, lwmqtt_message_t msg) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // calculate remaining length + uint32_t rem_len = 2 + topic.len + (uint32_t)msg.payload_len; + if (msg.qos > 0) { + rem_len += 2; + } + + // check remaining length length + int rem_len_len; + lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); + if (err == LWMQTT_VARNUM_OVERFLOW) { + return LWMQTT_REMAINING_LENGTH_OVERFLOW; + } + + // prepare header + uint8_t header = 0; + + // set packet type + lwmqtt_write_bits(&header, LWMQTT_PUBLISH_PACKET, 4, 4); + + // set dup + lwmqtt_write_bits(&header, (uint8_t)(dup), 3, 1); + + // set qos + lwmqtt_write_bits(&header, msg.qos, 1, 2); + + // set retained + lwmqtt_write_bits(&header, (uint8_t)(msg.retained), 0, 1); + + // write header + err = lwmqtt_write_byte(&buf_ptr, buf_end, header); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write remaining length + err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write topic + err = lwmqtt_write_string(&buf_ptr, buf_end, topic); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write packet id if qos is at least 1 + if (msg.qos > 0) { + err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + } + + // write payload + err = lwmqtt_write_data(&buf_ptr, buf_end, msg.payload, msg.payload_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // set length + *len = buf_ptr - buf; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_encode_subscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, + lwmqtt_string_t *topic_filters, lwmqtt_qos_t *qos_levels) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // calculate remaining length + uint32_t rem_len = 2; + for (int i = 0; i < count; i++) { + rem_len += 2 + topic_filters[i].len + 1; + } + + // check remaining length length + int rem_len_len; + lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); + if (err == LWMQTT_VARNUM_OVERFLOW) { + return LWMQTT_REMAINING_LENGTH_OVERFLOW; + } + + // prepare header + uint8_t header = 0; + + // set packet type + lwmqtt_write_bits(&header, LWMQTT_SUBSCRIBE_PACKET, 4, 4); + + // set qos + lwmqtt_write_bits(&header, LWMQTT_QOS1, 1, 2); + + // write header + err = lwmqtt_write_byte(&buf_ptr, buf_end, header); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write remaining length + err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write packet id + err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write all subscriptions + for (int i = 0; i < count; i++) { + // write topic + err = lwmqtt_write_string(&buf_ptr, buf_end, topic_filters[i]); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write qos level + err = lwmqtt_write_byte(&buf_ptr, buf_end, (uint8_t)qos_levels[i]); + if (err != LWMQTT_SUCCESS) { + return err; + } + } + + // set length + *len = buf_ptr - buf; + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_decode_suback(uint8_t *buf, size_t buf_len, uint16_t *packet_id, int max_count, int *count, + lwmqtt_qos_t *granted_qos_levels) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // read header + uint8_t header; + lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // check packet type + if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_SUBACK_PACKET) { + return LWMQTT_MISSING_OR_WRONG_PACKET; + } + + // read remaining length + uint32_t rem_len; + err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // check remaining length (packet id + min. one suback code) + if (rem_len < 3) { + return LWMQTT_REMAINING_LENGTH_MISMATCH; + } + + // read packet id + err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // read all suback codes + for (*count = 0; *count < (int)rem_len - 2; (*count)++) { + // check max count + if (*count > max_count) { + return LWMQTT_SUBACK_ARRAY_OVERFLOW; + } + + // read qos level + uint8_t raw_qos_level; + err = lwmqtt_read_byte(&buf_ptr, buf_end, &raw_qos_level); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // set qos level + switch (raw_qos_level) { + case 0: + granted_qos_levels[*count] = LWMQTT_QOS0; + break; + case 1: + granted_qos_levels[*count] = LWMQTT_QOS1; + break; + case 2: + granted_qos_levels[*count] = LWMQTT_QOS2; + break; + default: + granted_qos_levels[*count] = LWMQTT_QOS_FAILURE; + break; + } + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_encode_unsubscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, + lwmqtt_string_t *topic_filters) { + // prepare pointer + uint8_t *buf_ptr = buf; + uint8_t *buf_end = buf + buf_len; + + // calculate remaining length + uint32_t rem_len = 2; + for (int i = 0; i < count; i++) { + rem_len += 2 + topic_filters[i].len; + } + + // check remaining length length + int rem_len_len; + lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); + if (err == LWMQTT_VARNUM_OVERFLOW) { + return LWMQTT_REMAINING_LENGTH_OVERFLOW; + } + + // prepare header + uint8_t header = 0; + + // set packet type + lwmqtt_write_bits(&header, LWMQTT_UNSUBSCRIBE_PACKET, 4, 4); + + // set qos + lwmqtt_write_bits(&header, LWMQTT_QOS1, 1, 2); + + // write header + err = lwmqtt_write_byte(&buf_ptr, buf_end, header); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write remaining length + err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write packet id + err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); + if (err != LWMQTT_SUCCESS) { + return err; + } + + // write topics + for (int i = 0; i < count; i++) { + err = lwmqtt_write_string(&buf_ptr, buf_end, topic_filters[i]); + if (err != LWMQTT_SUCCESS) { + return err; + } + } + + // set length + *len = buf_ptr - buf; + + return LWMQTT_SUCCESS; +} diff --git a/lib/MQTT/src/lwmqtt/packet.h b/lib/MQTT/src/lwmqtt/packet.h new file mode 100644 index 000000000..387a18f8b --- /dev/null +++ b/lib/MQTT/src/lwmqtt/packet.h @@ -0,0 +1,185 @@ +#ifndef LWMQTT_PACKET_H +#define LWMQTT_PACKET_H + +#include "helpers.h" + +/** + * The available packet types. + */ +typedef enum { + LWMQTT_NO_PACKET = 0, + LWMQTT_CONNECT_PACKET = 1, + LWMQTT_CONNACK_PACKET, + LWMQTT_PUBLISH_PACKET, + LWMQTT_PUBACK_PACKET, + LWMQTT_PUBREC_PACKET, + LWMQTT_PUBREL_PACKET, + LWMQTT_PUBCOMP_PACKET, + LWMQTT_SUBSCRIBE_PACKET, + LWMQTT_SUBACK_PACKET, + LWMQTT_UNSUBSCRIBE_PACKET, + LWMQTT_UNSUBACK_PACKET, + LWMQTT_PINGREQ_PACKET, + LWMQTT_PINGRESP_PACKET, + LWMQTT_DISCONNECT_PACKET +} lwmqtt_packet_type_t; + +/** + * Will detect the packet type from the at least one byte long buffer. + * + * @param buf - The buffer from which the packet type will be detected. + * @param buf_len - The length of the specified buffer. + * @param packet_type - The packet type. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_detect_packet_type(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type); + +/** + * Will detect the remaining length form the at least on byte long buffer. + * + * It will return LWMQTT_BUFFER_TOO_SHORT if the buffer is to short and an additional byte should be read from the + * network. In case the remaining length is overflowed it will return LWMQTT_REMAINING_LENGTH_OVERFLOW. + * + * @param buf - The buffer from which the remaining length will be detected. + * @param buf_len - The length of the specified buffer. + * @param rem_len - The detected remaining length. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_detect_remaining_length(uint8_t *buf, size_t buf_len, uint32_t *rem_len); + +/** + * Encodes a connect packet into the supplied buffer. + * + * @param buf - The buffer into which the packet will be encoded. + * @param buf_len - The length of the specified buffer. + * @param len - The encoded length of the packet. + * @param options - The options to be used to build the connect packet. + * @param will - The last will and testament. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_encode_connect(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_options_t options, + lwmqtt_will_t *will); + +/** + * Decodes a connack packet from the supplied buffer. + * + * @param buf - The raw buffer data. + * @param buf_len - The length of the specified buffer. + * @param session_present - The session present flag. + * @param return_code - The return code. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_decode_connack(uint8_t *buf, size_t buf_len, bool *session_present, + lwmqtt_return_code_t *return_code); + +/** + * Encodes a zero (disconnect, pingreq) packet into the supplied buffer. + * + * @param buf - The buffer into which the packet will be encoded. + * @param buf_len - The length of the specified buffer. + * @param len - The encoded length of the packet. + * @param packet_type - The packets type. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_encode_zero(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type); + +/** + * Decodes an ack (puback, pubrec, pubrel, pubcomp, unsuback) packet from the supplied buffer. + * + * @param buf - The raw buffer data. + * @param buf_len - The length of the specified buffer. + * @param packet_type - The packet type. + * @param dup - The dup flag. + * @param packet_id - The packet id. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_decode_ack(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t packet_type, bool *dup, + uint16_t *packet_id); + +/** + * Encodes an ack (puback, pubrec, pubrel, pubcomp) packet into the supplied buffer. + * + * @param buf - The buffer into which the packet will be encoded. + * @param buf_len - The length of the specified buffer. + * @param len - The encoded length of the packet. + * @param packet_type - The packets type. + * @param dup - The dup flag. + * @param packet_id - The packet id. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_encode_ack(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type, bool dup, + uint16_t packet_id); + +/** + * Decodes a publish packet from the supplied buffer. + * + * @param buf - The raw buffer data. + * @param buf_len - The length of the specified buffer. + * @param dup - The dup flag. + * @param packet_id - The packet id. + * @param topic - The topic. + * @parma msg - The message. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_decode_publish(uint8_t *buf, size_t buf_len, bool *dup, uint16_t *packet_id, lwmqtt_string_t *topic, + lwmqtt_message_t *msg); + +/** + * Encodes a publish packet into the supplied buffer. + * + * @param buf - The buffer into which the packet will be encoded. + * @param buf_len - The length of the specified buffer. + * @param len - The encoded length of the packet. + * @param dup - The dup flag. + * @param packet_id - The packet id. + * @param topic - The topic. + * @param msg - The message. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_encode_publish(uint8_t *buf, size_t buf_len, size_t *len, bool dup, uint16_t packet_id, + lwmqtt_string_t topic, lwmqtt_message_t msg); + +/** + * Encodes a subscribe packet into the supplied buffer. + * + * @param buf - The buffer into which the packet will be encoded. + * @param buf_len - The length of the specified buffer. + * @param len - The encoded length of the packet. + * @param packet_id - The packet id. + * @param count - The number of members in the topic_filters and qos_levels array. + * @param topic_filters - The array of topic filter. + * @param qos_levels - The array of requested QoS levels. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_encode_subscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, + lwmqtt_string_t *topic_filters, lwmqtt_qos_t *qos_levels); + +/** + * Decodes a suback packet from the supplied buffer. + * + * @param buf - The raw buffer data. + * @param buf_len - The length of the specified buffer. + * @param packet_id - The packet id. + * @param max_count - The maximum number of members allowed in the granted_qos_levels array. + * @param count - The number of members in the granted_qos_levels array. + * @param granted_qos_levels - The granted QoS levels. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_decode_suback(uint8_t *buf, size_t buf_len, uint16_t *packet_id, int max_count, int *count, + lwmqtt_qos_t *granted_qos_levels); + +/** + * Encodes the supplied unsubscribe data into the supplied buffer, ready for sending + * + * @param buf - The buffer into which the packet will be encoded. + * @param buf_len - The length of the specified buffer. + * @param len - The encoded length of the packet. + * @param packet_id - The packet id. + * @param count - The number of members in the topic_filters array. + * @param topic_filters - The array of topic filters. + * @return An error value. + */ +lwmqtt_err_t lwmqtt_encode_unsubscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, + lwmqtt_string_t *topic_filters); + +#endif // LWMQTT_PACKET_H diff --git a/lib/MQTT/src/lwmqtt/string.c b/lib/MQTT/src/lwmqtt/string.c new file mode 100644 index 000000000..c27dc94e3 --- /dev/null +++ b/lib/MQTT/src/lwmqtt/string.c @@ -0,0 +1,38 @@ +#include + +#include "lwmqtt.h" + +lwmqtt_string_t lwmqtt_string(const char *str) { + // check for null + if (str == NULL) { + return (lwmqtt_string_t){0, NULL}; + } + + // get length + uint16_t len = (uint16_t)strlen(str); + + // check zero length + if (len == 0) { + return (lwmqtt_string_t){0, NULL}; + } + + return (lwmqtt_string_t){len, (char *)str}; +} + +int lwmqtt_strcmp(lwmqtt_string_t a, const char *b) { + // get string of b + lwmqtt_string_t b_str = lwmqtt_string(b); + + // return if both are zero length + if (a.len == 0 && b_str.len == 0) { + return 0; + } + + // return if lengths are different + if (a.len != b_str.len) { + return -1; + } + + // compare memory of same length + return strncmp(a.data, b_str.data, a.len); +} diff --git a/lib/MQTT/src/system.cpp b/lib/MQTT/src/system.cpp new file mode 100644 index 000000000..4d583b4f5 --- /dev/null +++ b/lib/MQTT/src/system.cpp @@ -0,0 +1,48 @@ +#include + +#include "system.h" + +void lwmqtt_arduino_timer_set(void *ref, uint32_t timeout) { + // cast timer reference + auto t = (lwmqtt_arduino_timer_t *)ref; + + // set future end time + t->end = (uint32_t)(millis() + timeout); +} + +int32_t lwmqtt_arduino_timer_get(void *ref) { + // cast timer reference + auto t = (lwmqtt_arduino_timer_t *)ref; + + // get difference to end time + return (int32_t)t->end - (int32_t)millis(); +} + +lwmqtt_err_t lwmqtt_arduino_network_read(void *ref, uint8_t *buffer, size_t len, size_t *read, uint32_t timeout) { + // cast network reference + auto n = (lwmqtt_arduino_network_t *)ref; + + // set timeout + n->client->setTimeout(timeout); + + // read bytes + *read = n->client->readBytes(buffer, len); + if (*read <= 0) { + return LWMQTT_NETWORK_FAILED_READ; + } + + return LWMQTT_SUCCESS; +} + +lwmqtt_err_t lwmqtt_arduino_network_write(void *ref, uint8_t *buffer, size_t len, size_t *sent, uint32_t timeout) { + // cast network reference + auto n = (lwmqtt_arduino_network_t *)ref; + + // write bytes + *sent = n->client->write(buffer, len); + if (*sent <= 0) { + return LWMQTT_NETWORK_FAILED_WRITE; + }; + + return LWMQTT_SUCCESS; +} diff --git a/lib/MQTT/src/system.h b/lib/MQTT/src/system.h new file mode 100644 index 000000000..e51739fb8 --- /dev/null +++ b/lib/MQTT/src/system.h @@ -0,0 +1,26 @@ +#ifndef LWMQTT_ARDUINO_H +#define LWMQTT_ARDUINO_H + +#include +#include + +extern "C" { +#include "lwmqtt/lwmqtt.h" +}; + +typedef struct { + uint32_t end; +} lwmqtt_arduino_timer_t; + +void lwmqtt_arduino_timer_set(void *ref, uint32_t timeout); + +int32_t lwmqtt_arduino_timer_get(void *ref); + +typedef struct { + Client *client; +} lwmqtt_arduino_network_t; + +lwmqtt_err_t lwmqtt_arduino_network_read(void *ref, uint8_t *buf, size_t len, size_t *read, uint32_t timeout); +lwmqtt_err_t lwmqtt_arduino_network_write(void *ref, uint8_t *buf, size_t len, size_t *sent, uint32_t timeout); + +#endif // LWMQTT_ARDUINO_H From 2c41a33b0b23a2c9a7f58312d3713f86fd64317d Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 26 Jan 2018 19:09:45 +0100 Subject: [PATCH 010/175] Change CBOR library (this one uses maps) decode() needs to be rewritten --- ArduinoCloudThing.cpp | 58 +- ArduinoCloudThing.h | 60 +- lib/ArduinoCbor/.gitignore | 2 + lib/ArduinoCbor/README.md | 7 + lib/ArduinoCbor/examples/simple/simple.ino | 37 + lib/ArduinoCbor/library.json | 10 + lib/ArduinoCbor/library.properties | 9 + lib/ArduinoCbor/makefile | 37 + lib/ArduinoCbor/src/ArduinoCbor.h | 22 + lib/ArduinoCbor/src/CborArray.cpp | 32 + lib/ArduinoCbor/src/CborArray.h | 23 + lib/ArduinoCbor/src/CborBuffer.cpp | 46 + lib/ArduinoCbor/src/CborBuffer.h | 27 + lib/ArduinoCbor/src/CborObject.cpp | 47 + lib/ArduinoCbor/src/CborObject.h | 29 + lib/ArduinoCbor/src/CborVariant.cpp | 111 +++ lib/ArduinoCbor/src/CborVariant.h | 37 + lib/ArduinoCbor/src/cbor.h | 118 +++ lib/ArduinoCbor/src/cn-cbor/LICENSE | 21 + lib/ArduinoCbor/src/cn-cbor/README.md | 44 + lib/ArduinoCbor/src/cn-cbor/cn-cbor.c | 285 ++++++ lib/ArduinoCbor/src/cn-cbor/cn-cbor.h | 405 ++++++++ lib/ArduinoCbor/src/cn-cbor/cn-create.c | 184 ++++ lib/ArduinoCbor/src/cn-cbor/cn-encoder.c | 307 ++++++ lib/ArduinoCbor/src/cn-cbor/cn-error.c | 13 + lib/ArduinoCbor/src/cn-cbor/cn-get.c | 62 ++ lib/CBOR/.gitignore | 28 - lib/CBOR/CborDecoder.cpp | 938 ------------------ lib/CBOR/CborDecoder.h | 107 -- lib/CBOR/CborEncoder.cpp | 208 ---- lib/CBOR/CborEncoder.h | 93 -- .../Cbor_Serialbuffer_send.ino | 91 -- .../Cbor_Serialport_sender.ino | 44 - .../Cbor_master_reader/Cbor_master_reader.ino | 39 - .../Cbor_slave_sender/Cbor_slave_sender.ino | 44 - lib/CBOR/Examples/cbortest2/cbortest2.ino | 67 -- lib/CBOR/LICENSE | 201 ---- lib/CBOR/README.md | 83 -- 38 files changed, 1972 insertions(+), 2004 deletions(-) create mode 100644 lib/ArduinoCbor/.gitignore create mode 100644 lib/ArduinoCbor/README.md create mode 100644 lib/ArduinoCbor/examples/simple/simple.ino create mode 100644 lib/ArduinoCbor/library.json create mode 100644 lib/ArduinoCbor/library.properties create mode 100644 lib/ArduinoCbor/makefile create mode 100644 lib/ArduinoCbor/src/ArduinoCbor.h create mode 100644 lib/ArduinoCbor/src/CborArray.cpp create mode 100644 lib/ArduinoCbor/src/CborArray.h create mode 100644 lib/ArduinoCbor/src/CborBuffer.cpp create mode 100644 lib/ArduinoCbor/src/CborBuffer.h create mode 100644 lib/ArduinoCbor/src/CborObject.cpp create mode 100644 lib/ArduinoCbor/src/CborObject.h create mode 100644 lib/ArduinoCbor/src/CborVariant.cpp create mode 100644 lib/ArduinoCbor/src/CborVariant.h create mode 100644 lib/ArduinoCbor/src/cbor.h create mode 100644 lib/ArduinoCbor/src/cn-cbor/LICENSE create mode 100644 lib/ArduinoCbor/src/cn-cbor/README.md create mode 100644 lib/ArduinoCbor/src/cn-cbor/cn-cbor.c create mode 100644 lib/ArduinoCbor/src/cn-cbor/cn-cbor.h create mode 100644 lib/ArduinoCbor/src/cn-cbor/cn-create.c create mode 100644 lib/ArduinoCbor/src/cn-cbor/cn-encoder.c create mode 100644 lib/ArduinoCbor/src/cn-cbor/cn-error.c create mode 100644 lib/ArduinoCbor/src/cn-cbor/cn-get.c delete mode 100644 lib/CBOR/.gitignore delete mode 100644 lib/CBOR/CborDecoder.cpp delete mode 100644 lib/CBOR/CborDecoder.h delete mode 100644 lib/CBOR/CborEncoder.cpp delete mode 100644 lib/CBOR/CborEncoder.h delete mode 100644 lib/CBOR/Examples/Cbor_Serialbuffer_send/Cbor_Serialbuffer_send.ino delete mode 100644 lib/CBOR/Examples/Cbor_Serialport_sender/Cbor_Serialport_sender.ino delete mode 100644 lib/CBOR/Examples/Cbor_master_reader/Cbor_master_reader.ino delete mode 100644 lib/CBOR/Examples/Cbor_slave_sender/Cbor_slave_sender.ino delete mode 100644 lib/CBOR/Examples/cbortest2/cbortest2.ino delete mode 100644 lib/CBOR/LICENSE delete mode 100644 lib/CBOR/README.md diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 306ad48eb..2556b182b 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -84,17 +84,20 @@ bool ArduinoCloudThing::connect() { return false; } -void ArduinoCloudThing::publish(CborDynamicOutput& output) { +void ArduinoCloudThing::publish(CborObject& object) { bool retained = false; - unsigned char *buf = output.getData(); + uint8_t data[1024]; + size_t size = object.encode(data, sizeof(data)); #ifdef TESTING_PROTOCOL - decode(buf, output.getSize()); + decode(data, size); #endif - client->publish(GENERAL_TOPIC, (const char*)buf, output.getSize()); +#ifndef TESTING_PROTOCOL + client->publish(GENERAL_TOPIC, (const char*)data, size); +#endif for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); @@ -115,11 +118,13 @@ int ArduinoCloudThing::poll() { // check if backing storage and cloud has diverged int diff = 0; - CborDynamicOutput output; - diff = checkNewData(output); + + diff = checkNewData(); if (diff > 0) { - compress(output, diff); - publish(output); + CborBuffer buffer(1024); + CborObject object = CborObject(buffer); + compress(object, buffer); + publish(object); } #ifdef DEBUG_MEMORY @@ -129,21 +134,23 @@ int ArduinoCloudThing::poll() { return diff; } -void ArduinoCloudThing::compress(CborDynamicOutput& output, int howMany) { +void ArduinoCloudThing::compress(CborObject& object, CborBuffer& buffer) { - CborWriter writer(output); - - writer.writeArray(howMany); + CborArray array = CborArray(buffer); for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); if (p->newData()) { - p->append(writer); + CborObject child = CborObject(buffer); + p->append(child); + CborVariant variant = CborVariant(buffer, child); + array.add(variant); } } + object.set("a", array); } -int ArduinoCloudThing::checkNewData(CborDynamicOutput& output) { +int ArduinoCloudThing::checkNewData() { int counter = 0; for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); @@ -193,14 +200,23 @@ void ArduinoCloudThing::callback(MQTTClient *client, char topic[], char bytes[], } void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { - CborInput input(payload, length); + /* + CborBuffer buffer(200); + CborVariant variant = buffer.decode(payload, length); + CborArray array = variant.asArray(); + + CborVariant obj = array.get(0); + if (!obj.isValid()) { + return; + } + if (obj.isString()) { - CborReader reader(input); - CborPropertyListener listener(&list); - reader.SetListener(listener); - reader.Run(); + } + */ } +/* + void CborPropertyListener::OnInteger(int32_t value) { if (currentListIndex < 0) { return; @@ -267,4 +283,6 @@ void CborPropertyListener::OnSpecial(uint32_t code) { } void CborPropertyListener::OnError(const char *error) { -} \ No newline at end of file +} + +*/ \ No newline at end of file diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index b96628fc1..4f5793c4e 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -5,8 +5,7 @@ #include #include "lib/MQTT/src/MQTTClient.h" #include "lib/LinkedList/LinkedList.h" -#include "lib/CBOR/CborDecoder.h" -#include "lib/CBOR/CborEncoder.h" +#include "lib/ArduinoCbor/src/ArduinoCbor.h" #ifndef MQTT_BUFFER_SIZE #define MQTT_BUFFER_SIZE 256 @@ -33,7 +32,7 @@ enum boolStatus { class ArduinoCloudPropertyGeneric { public: - virtual void append(CborWriter &cbor) = 0; + virtual void append(CborObject& object) = 0; virtual String& getName() = 0; virtual void setName(String _name) = 0; virtual void setPermission(permissionType _permission) = 0; @@ -87,14 +86,12 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric return permission; } - void appendValue(CborWriter &cbor); + void appendValue(CborObject &cbor); - void append(CborWriter &cbor) { - cbor.writeArray(4); - cbor.writeTag(tag); - cbor.writeString(name); + void append(CborObject &cbor) { + cbor.set("n", name.c_str()); appendValue(cbor); - cbor.writeSpecial(permission); + cbor.set("p", permission); } bool newData() { @@ -115,29 +112,28 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric }; template <> -inline void ArduinoCloudProperty::appendValue(CborWriter &cbor) { - cbor.writeInt(property); +inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { + cbor.set("i", property); }; template <> -inline void ArduinoCloudProperty::appendValue(CborWriter &cbor) { - //cbor.writeBoolean(property); - cbor.writeInt((int)property); +inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { + cbor.set("b", property); }; template <> -inline void ArduinoCloudProperty::appendValue(CborWriter &cbor) { - //cbor.writeFloat(property); +inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { + cbor.set("f", property); }; template <> -inline void ArduinoCloudProperty::appendValue(CborWriter &cbor) { - cbor.writeString(property); +inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { + cbor.set("s", property.c_str()); }; template <> -inline void ArduinoCloudProperty::appendValue(CborWriter &cbor) { - cbor.writeString(property); +inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { + cbor.set("s", property); }; #ifndef addProperty @@ -159,11 +155,11 @@ class ArduinoCloudThing { private: static void callback(MQTTClient *client, char topic[], char bytes[], int length); bool connect(); - void publish(CborDynamicOutput& output); + void publish(CborObject& object); void update(); - int checkNewData(CborDynamicOutput& output); - void compress(CborDynamicOutput& output, int howMany); + int checkNewData(); + void compress(CborObject& object, CborBuffer& buffer); void decode(uint8_t * payload, size_t length); bool exists(String &name); @@ -176,22 +172,4 @@ class ArduinoCloudThing { MQTTClient* client; }; -class CborPropertyListener : public CborListener { - public: - CborPropertyListener(LinkedList *_list) : list(_list) {} - void OnInteger(int32_t value); - void OnBytes(unsigned char *data, unsigned int size); - void OnString(String &str); - void OnArray(unsigned int size); - void OnMap(unsigned int size) ; - void OnTag(uint32_t tag); - void OnSpecial(uint32_t code); - void OnError(const char *error); - LinkedList *list; - int currentListIndex; - bool justStarted = true; - int list_size = 0; - bool newElement = false; -}; - #endif diff --git a/lib/ArduinoCbor/.gitignore b/lib/ArduinoCbor/.gitignore new file mode 100644 index 000000000..8429a6996 --- /dev/null +++ b/lib/ArduinoCbor/.gitignore @@ -0,0 +1,2 @@ +build +gtest diff --git a/lib/ArduinoCbor/README.md b/lib/ArduinoCbor/README.md new file mode 100644 index 000000000..0cf100064 --- /dev/null +++ b/lib/ArduinoCbor/README.md @@ -0,0 +1,7 @@ +# ArduinoCbor + +ArduinoCbor is a C++ library to handle CBOR data structures. +CBOR is a binary format based on the JSON data model. +Internal the [cn-cbor](https://github.com/cabo/cn-cbor) C library is used. + +## Examples diff --git a/lib/ArduinoCbor/examples/simple/simple.ino b/lib/ArduinoCbor/examples/simple/simple.ino new file mode 100644 index 000000000..209d8dfcd --- /dev/null +++ b/lib/ArduinoCbor/examples/simple/simple.ino @@ -0,0 +1,37 @@ +#include + +void setup() { + Serial.begin(115200); + + while(!Serial) { + delay(10); + } +} + + +void loop() { + CborBuffer buffer(200); + CborObject object = CborObject(buffer); + + object.set("string", "value"); + object.set("integer", -1234); + + CborObject child = CborObject(buffer); + child.set("key", "value"); + object.set("object", child); + + CborArray array = CborArray(buffer); + array.add(-1234); + object.set("array", array); + + Serial.print("string value: "); + Serial.println(object.get("string").asString()); + + Serial.print("integer value: "); + Serial.println(object.get("integer").asInteger()); + + Serial.print("child string value: "); + Serial.println(object.get("object").asObject().get("key").asString()); + + delay(1000); +} diff --git a/lib/ArduinoCbor/library.json b/lib/ArduinoCbor/library.json new file mode 100644 index 000000000..8de25aef7 --- /dev/null +++ b/lib/ArduinoCbor/library.json @@ -0,0 +1,10 @@ +{ + "name": "ArduinoCbor", + "keywords": "cbor", + "description": "CBOR library for Arduino", + "repository": { + "type": "git", + "url": "https://github.com/bergos/ArduinoCbor.git" + }, + "frameworks": "arduino" +} diff --git a/lib/ArduinoCbor/library.properties b/lib/ArduinoCbor/library.properties new file mode 100644 index 000000000..d83b35399 --- /dev/null +++ b/lib/ArduinoCbor/library.properties @@ -0,0 +1,9 @@ +name=ArduinoCbor +version=0.0.1 +author=Thomas Bergwinkl (https://www.bergnet.org/people/bergi/card#me) +maintainer=Thomas Bergwinkl (https://www.bergnet.org/people/bergi/card#me) +sentence=CBOR library for Arduino +paragraph=CBOR library for Arduino +category=Other +url=https://github.com/bergos/ArduinoCbor +architectures=* diff --git a/lib/ArduinoCbor/makefile b/lib/ArduinoCbor/makefile new file mode 100644 index 000000000..a9589a98d --- /dev/null +++ b/lib/ArduinoCbor/makefile @@ -0,0 +1,37 @@ +OBJS += \ + build/cn-cbor.o \ + build/cn-create.o \ + build/cn-encoder.o \ + build/cn-error.o \ + build/cn-get.o \ + build/CborArray.o \ + build/CborBuffer.o \ + build/CborObject.o \ + build/CborVariant.o \ + build/test.o + +LIBS += \ + -lgtest \ + -lpthread + +build/%.o: src/cn-cbor/%.c + @mkdir -p build + gcc $(CCFLAGS) -c -o "$@" "$<" -I src + +build/%.o: src/%.cpp + @mkdir -p build + g++ $(CCFLAGS) -c -o "$@" "$<" + +build/%.o: extra/test/%.cpp + @mkdir -p build + g++ $(CCFLAGS) -c -o "$@" "$<" -I src + +all: test + +test: $(OBJS) + g++ -o gtest $(OBJS) $(LIBS) + ./gtest + +clean: + @rm -rf build + @rm -f test diff --git a/lib/ArduinoCbor/src/ArduinoCbor.h b/lib/ArduinoCbor/src/ArduinoCbor.h new file mode 100644 index 000000000..a427492cd --- /dev/null +++ b/lib/ArduinoCbor/src/ArduinoCbor.h @@ -0,0 +1,22 @@ +#ifndef ARDUINO_CBOR_H_ +#define ARDUINO_CBOR_H_ + +#define USE_CBOR_CONTEXT + +#include "cn-cbor/cn-cbor.h" + +#ifndef CBOR_INT_T +#define CBOR_INT_T long +#endif + +class CborArray; +class CborBuffer; +class CborObject; +class CborVariant; + +#include "CborArray.h" +#include "CborBuffer.h" +#include "CborObject.h" +#include "CborVariant.h" + +#endif // ARDUINO_CBOR_H_ diff --git a/lib/ArduinoCbor/src/CborArray.cpp b/lib/ArduinoCbor/src/CborArray.cpp new file mode 100644 index 000000000..2adf5085f --- /dev/null +++ b/lib/ArduinoCbor/src/CborArray.cpp @@ -0,0 +1,32 @@ +#include +#include + +#include "ArduinoCbor.h" + +CborArray::CborArray(CborBuffer& buffer, cn_cbor* raw) : buffer(buffer) { + this->raw = raw; + + if (raw == 0) { + cn_cbor_errback err; + + this->raw = cn_cbor_array_create(&buffer.context, &err); + } +} + +CborVariant CborArray::get(int index) { + return CborVariant(buffer, cn_cbor_index(raw, index)); +} + +void CborArray::add(CborVariant value) { + cn_cbor_errback err; + + cn_cbor_array_append(raw, value.raw, &err); +} + +void CborArray::add(const char* value) { + add(CborVariant(buffer, value)); +} + +void CborArray::add(CBOR_INT_T value) { + add(CborVariant(buffer, value)); +} diff --git a/lib/ArduinoCbor/src/CborArray.h b/lib/ArduinoCbor/src/CborArray.h new file mode 100644 index 000000000..5f51619e2 --- /dev/null +++ b/lib/ArduinoCbor/src/CborArray.h @@ -0,0 +1,23 @@ +#ifndef CBOR_ARRAY_H_ +#define CBOR_ARRAY_H_ + +#include "ArduinoCbor.h" + +class CborArray { + friend class CborVariant; + + public: + CborArray(CborBuffer& buffer, cn_cbor* raw=0); + + CborVariant get(int index); + + void add(CborVariant value); + void add(const char* value); + void add(CBOR_INT_T value); + + protected: + CborBuffer& buffer; + cn_cbor* raw; +}; + +#endif // CBOR_ARRAY_H_ diff --git a/lib/ArduinoCbor/src/CborBuffer.cpp b/lib/ArduinoCbor/src/CborBuffer.cpp new file mode 100644 index 000000000..cc560be5e --- /dev/null +++ b/lib/ArduinoCbor/src/CborBuffer.cpp @@ -0,0 +1,46 @@ +#include "CborBuffer.h" +#include "Arduino.h" + +uint8_t* CborBuffer::calloc(size_t count, size_t size, CborBuffer* context) { + return context->alloc(count * size); +} + +void CborBuffer::free(uint8_t* ptr, CborBuffer* context) { +} + +CborBuffer::CborBuffer(size_t size) { + this->start = new uint8_t[size]; + this->offset = this->start; + this->end = this->start + size; + this->context.calloc_func = (cn_calloc_func)CborBuffer::calloc; + this->context.free_func = (cn_free_func)CborBuffer::free; + this->context.context = this; +} + +CborBuffer::~CborBuffer() { + delete[] this->start; +} + +uint8_t* CborBuffer::alloc(size_t size) { + if (offset + size > end) { + return 0; + } + + uint8_t* data = offset; + + offset += size; + + for (uint8_t* ptr = data; ptr < offset; ptr++) { + *ptr = 0; + } + + return data; +} + +CborVariant CborBuffer::decode(uint8_t* data, size_t size) { + cn_cbor_errback err; + + raw = cn_cbor_decode(data, size, &context, &err); + + return CborVariant(*this, raw); +} diff --git a/lib/ArduinoCbor/src/CborBuffer.h b/lib/ArduinoCbor/src/CborBuffer.h new file mode 100644 index 000000000..41fcffe02 --- /dev/null +++ b/lib/ArduinoCbor/src/CborBuffer.h @@ -0,0 +1,27 @@ +#ifndef CBOR_BUFFER_H_ +#define CBOR_BUFFER_H_ + +#include "ArduinoCbor.h" + +class CborBuffer { + public: + cn_cbor_context context; + + CborBuffer(size_t size); + ~CborBuffer(); + + CborVariant decode(uint8_t* data, size_t size); + + uint8_t* alloc(size_t size); + + protected: + uint8_t* start; + uint8_t* end; + uint8_t* offset; + cn_cbor* raw = NULL; + + static uint8_t* calloc(size_t count, size_t size, CborBuffer* context); + static void free(uint8_t* ptr, CborBuffer* context); +}; + +#endif // CBOR_BUFFER_H_ diff --git a/lib/ArduinoCbor/src/CborObject.cpp b/lib/ArduinoCbor/src/CborObject.cpp new file mode 100644 index 000000000..dd046696e --- /dev/null +++ b/lib/ArduinoCbor/src/CborObject.cpp @@ -0,0 +1,47 @@ +#include "CborObject.h" + +CborObject::CborObject(CborBuffer& buffer, cn_cbor* raw) : buffer(buffer) { + this->raw = raw; + + if (raw == 0) { + cn_cbor_errback err; + + this->raw = cn_cbor_map_create(&buffer.context, &err); + } +} + +CborObject::~CborObject() { + if (!raw->parent) { + cn_cbor_free(raw, &(buffer.context)); + } +} + +CborVariant CborObject::get(const char* key) { + return CborVariant(buffer, cn_cbor_mapget_string(raw, key)); +} + +void CborObject::set(const char* key, CborVariant value) { + cn_cbor_errback err; + + cn_cbor_mapput_string(raw, key, value.raw, &buffer.context, &err); +} + +void CborObject::set(const char* key, const char* value) { + set(key, CborVariant(buffer, value)); +} + +void CborObject::set(const char* key, CBOR_INT_T value) { + set(key, CborVariant(buffer, value)); +} + +void CborObject::set(const char* key, CborObject value) { + set(key, CborVariant(buffer, value)); +} + +void CborObject::set(const char* key, CborArray value) { + set(key, CborVariant(buffer, value)); +} + +size_t CborObject::encode(uint8_t* data, size_t size) { + return cn_cbor_encoder_write(data, 0, size, raw); +} diff --git a/lib/ArduinoCbor/src/CborObject.h b/lib/ArduinoCbor/src/CborObject.h new file mode 100644 index 000000000..c806da240 --- /dev/null +++ b/lib/ArduinoCbor/src/CborObject.h @@ -0,0 +1,29 @@ +#ifndef CBOR_OBJECT_H_ +#define CBOR_OBJECT_H_ + +#include "ArduinoCbor.h" +#include "stdlib.h" + +class CborObject { + friend class CborVariant; + + public: + CborObject(CborBuffer& buffer, cn_cbor* raw=0); + ~CborObject(); + + CborVariant get(const char* key); + + void set(const char* key, CborVariant value); + void set(const char* key, const char* value); + void set(const char* key, CBOR_INT_T value); + void set(const char* key, CborObject value); + void set(const char* key, CborArray value); + + size_t encode(uint8_t* data, size_t size); + + protected: + CborBuffer& buffer; + cn_cbor* raw; +}; + +#endif // CBOR_OBJECT_H_ diff --git a/lib/ArduinoCbor/src/CborVariant.cpp b/lib/ArduinoCbor/src/CborVariant.cpp new file mode 100644 index 000000000..28711ee23 --- /dev/null +++ b/lib/ArduinoCbor/src/CborVariant.cpp @@ -0,0 +1,111 @@ +#include "CborVariant.h" + +CborVariant::CborVariant(CborBuffer& buffer, cn_cbor* raw) : buffer(buffer) { + this->raw = raw; +} + +CborVariant::CborVariant(CborBuffer& buffer, const char* value) : buffer(buffer) { + cn_cbor_errback err; + + raw = cn_cbor_string_create(value, &buffer.context, &err); +} + +CborVariant::CborVariant(CborBuffer& buffer, CBOR_INT_T value) : buffer(buffer) { + cn_cbor_errback err; + + raw = cn_cbor_int_create(value, &buffer.context, &err); +} + +CborVariant::CborVariant(CborBuffer& buffer, CborObject& value) : buffer(buffer) { + this->raw = value.raw; +} + +CborVariant::CborVariant(CborBuffer& buffer, CborArray& value) : buffer(buffer) { + this->raw = value.raw; +} + +int CborVariant::length() { + if (raw == 0) { + return -1; + } + + return raw->length; +} + +bool CborVariant::isValid() { + return raw != 0; +} + +bool CborVariant::isString() { + return isValid() && raw->type == CN_CBOR_BYTES || raw->type == CN_CBOR_TEXT; +} + +bool CborVariant::isInteger() { + return isValid() && raw->type == CN_CBOR_UINT || raw->type == CN_CBOR_INT; +} + +bool CborVariant::isObject() { + return isValid() && raw->type == CN_CBOR_MAP; +} + +bool CborVariant::isArray() { + return isValid() && raw->type == CN_CBOR_ARRAY; +} + +const char* CborVariant::asString() { + if (!isValid()) { + return 0; + } + + if (raw->type != CN_CBOR_BYTES && raw->type != CN_CBOR_TEXT) { + return 0; + } + + if (raw->v.str[raw->length] != 0) { + char* tmp = (char*)buffer.alloc(raw->length + 1); + + for (int i = 0; i < raw->length; i++) { + tmp[i] = raw->v.str[i]; + } + + return tmp; + } + + return raw->v.str; +} + +CBOR_INT_T CborVariant::asInteger() { + if (!isValid()) { + return 0; + } + + if (raw->type == CN_CBOR_UINT) { + return raw->v.uint; + } + + if (raw->type == CN_CBOR_INT) { + return raw->v.sint; + } + + return 0; +} + +CborObject CborVariant::asObject() { + if (isObject()) { + return CborObject(buffer, raw); + } + + return CborObject(buffer); +} + +CborArray CborVariant::asArray() { + if (isArray()) { + return CborArray(buffer, raw); + } + + return CborArray(buffer); +} + +size_t CborVariant::encode(uint8_t* data, size_t size) { + return cn_cbor_encoder_write(data, 0, size, raw); +} diff --git a/lib/ArduinoCbor/src/CborVariant.h b/lib/ArduinoCbor/src/CborVariant.h new file mode 100644 index 000000000..124bf9842 --- /dev/null +++ b/lib/ArduinoCbor/src/CborVariant.h @@ -0,0 +1,37 @@ +#ifndef CBOR_VARIANT_H_ +#define CBOR_VARIANT_H_ + +#include "ArduinoCbor.h" + +class CborVariant { + friend class CborArray; + friend class CborObject; + + public: + CborVariant(CborBuffer& buffer, cn_cbor* raw); + CborVariant(CborBuffer& buffer, const char* value); + CborVariant(CborBuffer& buffer, CBOR_INT_T value); + CborVariant(CborBuffer& buffer, CborObject& value); + CborVariant(CborBuffer& buffer, CborArray& value); + + int length(); + + bool isValid(); + bool isString(); + bool isInteger(); + bool isObject(); + bool isArray(); + + const char* asString(); + CBOR_INT_T asInteger(); + CborObject asObject(); + CborArray asArray(); + + size_t encode(uint8_t* data, size_t size); + + protected: + CborBuffer& buffer; + cn_cbor* raw; +}; + +#endif // CBOR_VARIANT_H_ diff --git a/lib/ArduinoCbor/src/cbor.h b/lib/ArduinoCbor/src/cbor.h new file mode 100644 index 000000000..1859f09ab --- /dev/null +++ b/lib/ArduinoCbor/src/cbor.h @@ -0,0 +1,118 @@ +#ifndef CBOR_PROTOCOL_H__ +#define CBOR_PROTOCOL_H__ + +/* The 8 major types */ +#define MT_UNSIGNED 0 +#define MT_NEGATIVE 1 +#define MT_BYTES 2 +#define MT_TEXT 3 +#define MT_ARRAY 4 +#define MT_MAP 5 +#define MT_TAG 6 +#define MT_PRIM 7 + +/* The initial bytes resulting from those */ +#define IB_UNSIGNED (MT_UNSIGNED << 5) +#define IB_NEGATIVE (MT_NEGATIVE << 5) +#define IB_BYTES (MT_BYTES << 5) +#define IB_TEXT (MT_TEXT << 5) +#define IB_ARRAY (MT_ARRAY << 5) +#define IB_MAP (MT_MAP << 5) +#define IB_TAG (MT_TAG << 5) +#define IB_PRIM (MT_PRIM << 5) + +#define IB_NEGFLAG (IB_NEGATIVE - IB_UNSIGNED) +#define IB_NEGFLAG_AS_BIT(ib) ((ib) >> 5) +#define IB_TEXTFLAG (IB_TEXT - IB_BYTES) + +#define IB_AI(ib) ((ib) & 0x1F) +#define IB_MT(ib) ((ib) >> 5) + +/* Tag numbers handled by this implementation */ +#define TAG_TIME_EPOCH 1 +#define TAG_BIGNUM 2 +#define TAG_BIGNUM_NEG 3 +#define TAG_URI 32 +#define TAG_RE 35 + +/* Initial bytes of those tag numbers */ +#define IB_TIME_EPOCH (IB_TAG | TAG_TIME_EPOCH) +#define IB_BIGNUM (IB_TAG | TAG_BIGNUM) +#define IB_BIGNUM_NEG (IB_TAG | TAG_BIGNUM_NEG) +/* TAG_URI and TAG_RE are non-immediate tags */ + +/* Simple values handled by this implementation */ +#define VAL_FALSE 20 +#define VAL_TRUE 21 +#define VAL_NIL 22 +#define VAL_UNDEF 23 + +/* Initial bytes of those simple values */ +#define IB_FALSE (IB_PRIM | VAL_FALSE) +#define IB_TRUE (IB_PRIM | VAL_TRUE) +#define IB_NIL (IB_PRIM | VAL_NIL) +#define IB_UNDEF (IB_PRIM | VAL_UNDEF) + +/* AI values with more data in head */ +#define AI_1 24 +#define AI_2 25 +#define AI_4 26 +#define AI_8 27 +#define AI_INDEF 31 +#define IB_BREAK (IB_PRIM | AI_INDEF) +/* For */ +#define IB_UNUSED (IB_TAG | AI_INDEF) + +/* Floating point initial bytes */ +#define IB_FLOAT2 (IB_PRIM | AI_2) +#define IB_FLOAT4 (IB_PRIM | AI_4) +#define IB_FLOAT8 (IB_PRIM | AI_8) + +// These definitions are here because they aren't required for the public +// interface, and they were quite confusing in cn-cbor.h + +#ifdef USE_CBOR_CONTEXT +/** + * Allocate enough space for 1 `cn_cbor` structure. + * + * @param[in] ctx The allocation context, or NULL for calloc. + * @return A pointer to a `cn_cbor` or NULL on failure + */ +#define CN_CALLOC(ctx) ((ctx) && (ctx)->calloc_func) ? \ + (ctx)->calloc_func(1, sizeof(cn_cbor), (ctx)->context) : \ + calloc(1, sizeof(cn_cbor)); + +/** + * Free a + * @param free_func [description] + * @return [description] + */ +#define CN_FREE(ptr, ctx) ((ctx) && (ctx)->free_func) ? \ + (ctx)->free_func((ptr), (ctx)->context) : \ + free((ptr)); + +#define CBOR_CONTEXT_PARAM , context +#define CN_CALLOC_CONTEXT() CN_CALLOC(context) +#define CN_CBOR_FREE_CONTEXT(p) CN_FREE(p, context) + +#else + +#define CBOR_CONTEXT_PARAM +#define CN_CALLOC_CONTEXT() CN_CALLOC +#define CN_CBOR_FREE_CONTEXT(p) CN_FREE(p) + +#ifndef CN_CALLOC +#define CN_CALLOC calloc(1, sizeof(cn_cbor)) +#endif + +#ifndef CN_FREE +#define CN_FREE free +#endif + +#endif // USE_CBOR_CONTEXT + +#ifndef UNUSED_PARAM +#define UNUSED_PARAM(p) ((void)&(p)) +#endif + +#endif // CBOR_PROTOCOL_H__ diff --git a/lib/ArduinoCbor/src/cn-cbor/LICENSE b/lib/ArduinoCbor/src/cn-cbor/LICENSE new file mode 100644 index 000000000..df377b191 --- /dev/null +++ b/lib/ArduinoCbor/src/cn-cbor/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Carsten Bormann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/ArduinoCbor/src/cn-cbor/README.md b/lib/ArduinoCbor/src/cn-cbor/README.md new file mode 100644 index 000000000..58208589a --- /dev/null +++ b/lib/ArduinoCbor/src/cn-cbor/README.md @@ -0,0 +1,44 @@ +[![Build Status](https://travis-ci.org/cabo/cn-cbor.png?branch=master)](https://travis-ci.org/cabo/cn-cbor) + +# cn-cbor: A constrained node implementation of CBOR in C + +This is a constrained node implementation of [CBOR](http://cbor.io) in +C that I threw together in 2013, before the publication of +[RFC 7049](http://tools.ietf.org/html/rfc7049), to validate certain +implementability considerations. + +Its API model was inspired by +[nxjson](https://bitbucket.org/yarosla/nxjson). It turns out that +this API model actually works even better with the advantages of the +CBOR format. + +This code has been used in a number of research implementations on +constrained nodes, with resulting code sizes appreciably under 1 KiB +on ARM platforms. + +I always meant to improve the interface some more with certain API +changes, in order to get even closer to 0.5 KiB, but I ran out of +time. So here it is. If I do get around to making these changes, the +API will indeed change a bit, so please be forewarned. + +## Building + +There is a `Simple-Makefile` for playing around, as well as a complete +[`cmake`](http://www.cmake.org)-based build environment. +(You can choose what fits your needs better.) + +Building with `cmake`: + + ./build.sh + +Building including testing: + + ./build.sh all test + +Generating a test coverage report (requires lcov[^1]; result in `build/lcov/index.html`): + + ./build.sh all coveralls coverage_report + +License: MIT + +[^1]: Installation with homebrew: `brew install lcov` diff --git a/lib/ArduinoCbor/src/cn-cbor/cn-cbor.c b/lib/ArduinoCbor/src/cn-cbor/cn-cbor.c new file mode 100644 index 000000000..30cfeb848 --- /dev/null +++ b/lib/ArduinoCbor/src/cn-cbor/cn-cbor.c @@ -0,0 +1,285 @@ +#ifndef CN_CBOR_C +#define CN_CBOR_C + +#ifdef __cplusplus +extern "C" { +#endif +#ifdef EMACS_INDENTATION_HELPER +} /* Duh. */ +#endif + +#include +#include +#include +#include +#include + +#include "cn-cbor.h" +#include "../cbor.h" + +#define CN_CBOR_FAIL(code) do { pb->err = code; goto fail; } while(0) + +void cn_cbor_free(cn_cbor* cb CBOR_CONTEXT) { + cn_cbor* p = cb; + assert(!p || !p->parent); + while (p) { + cn_cbor* p1; + while ((p1 = p->first_child)) { /* go down */ + p = p1; + } + if (!(p1 = p->next)) { /* go up next */ + if ((p1 = p->parent)) + p1->first_child = 0; + } + CN_CBOR_FREE_CONTEXT(p); + p = p1; + } +} + +#ifndef CBOR_NO_FLOAT +static double decode_half(int half) { + int exp = (half >> 10) & 0x1f; + int mant = half & 0x3ff; + double val; + if (exp == 0) val = ldexp(mant, -24); + else if (exp != 31) val = ldexp(mant + 1024, exp - 25); + else val = mant == 0 ? INFINITY : NAN; + return half & 0x8000 ? -val : val; +} +#endif /* CBOR_NO_FLOAT */ + +uint16_t htons(uint16_t v) { + return (v >> 8) | (v << 8); +} +uint32_t htonl(uint32_t v) { + return htons(v >> 16) | (htons((uint16_t) v) << 16); +} + +uint16_t ntohs(uint16_t v) { + return htons(v); +} +uint32_t ntohl(uint32_t v) { + return htonl(v); +} + +/* Fix these if you can't do non-aligned reads */ +#define ntoh8p(p) (*(unsigned char*)(p)) +#define ntoh16p(p) (ntohs(*(unsigned short*)(p))) +#define ntoh32p(p) (ntohl(*(unsigned long*)(p))) +static uint64_t ntoh64p(unsigned char *p) { + uint64_t ret = ntoh32p(p); + ret <<= 32; + ret += ntoh32p(p+4); + return ret; +} + +static cn_cbor_type mt_trans[] = { + CN_CBOR_UINT, CN_CBOR_INT, + CN_CBOR_BYTES, CN_CBOR_TEXT, + CN_CBOR_ARRAY, CN_CBOR_MAP, + CN_CBOR_TAG, CN_CBOR_SIMPLE, +}; + +struct parse_buf { + unsigned char *buf; + unsigned char *ebuf; + cn_cbor_error err; +}; + +#define TAKE(pos, ebuf, n, stmt) \ + if (n > (size_t)(ebuf - pos)) \ + CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_DATA); \ + stmt; \ + pos += n; + +static cn_cbor *decode_item (struct parse_buf *pb CBOR_CONTEXT, cn_cbor* top_parent) { + unsigned char *pos = pb->buf; + unsigned char *ebuf = pb->ebuf; + cn_cbor* parent = top_parent; + int ib; + unsigned int mt; + int ai; + uint64_t val; + cn_cbor* cb = NULL; +#ifndef CBOR_NO_FLOAT + union { + float f; + uint32_t u; + } u32; + union { + double d; + uint64_t u; + } u64; +#endif /* CBOR_NO_FLOAT */ + +again: + TAKE(pos, ebuf, 1, ib = ntoh8p(pos) ); + if (ib == IB_BREAK) { + if (!(parent->flags & CN_CBOR_FL_INDEF)) + CN_CBOR_FAIL(CN_CBOR_ERR_BREAK_OUTSIDE_INDEF); + switch (parent->type) { + case CN_CBOR_BYTES: case CN_CBOR_TEXT: + parent->type += 2; /* CN_CBOR_* -> CN_CBOR_*_CHUNKED */ + break; + case CN_CBOR_MAP: + if (parent->length & 1) + CN_CBOR_FAIL(CN_CBOR_ERR_ODD_SIZE_INDEF_MAP); + default:; + } + goto complete; + } + mt = ib >> 5; + ai = ib & 0x1f; + val = ai; + + cb = CN_CALLOC_CONTEXT(); + if (!cb) + CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_MEMORY); + + cb->type = mt_trans[mt]; + + cb->parent = parent; + if (parent->last_child) { + parent->last_child->next = cb; + } else { + parent->first_child = cb; + } + parent->last_child = cb; + parent->length++; + + switch (ai) { + case AI_1: TAKE(pos, ebuf, 1, val = ntoh8p(pos)) ; break; + case AI_2: TAKE(pos, ebuf, 2, val = ntoh16p(pos)) ; break; + case AI_4: TAKE(pos, ebuf, 4, val = ntoh32p(pos)) ; break; + case AI_8: TAKE(pos, ebuf, 8, val = ntoh64p(pos)) ; break; + case 28: case 29: case 30: CN_CBOR_FAIL(CN_CBOR_ERR_RESERVED_AI); + case AI_INDEF: + if ((mt - MT_BYTES) <= MT_MAP) { + cb->flags |= CN_CBOR_FL_INDEF; + goto push; + } else { + CN_CBOR_FAIL(CN_CBOR_ERR_MT_UNDEF_FOR_INDEF); + } + } + // process content + switch (mt) { + case MT_UNSIGNED: + cb->v.uint = val; /* to do: Overflow check */ + break; + case MT_NEGATIVE: + cb->v.sint = ~val; /* to do: Overflow check */ + break; + case MT_BYTES: case MT_TEXT: + cb->v.str = (char *) pos; + cb->length = val; + TAKE(pos, ebuf, val, ;); + break; + case MT_MAP: + val <<= 1; + /* fall through */ + case MT_ARRAY: + if ((cb->v.count = val)) { + cb->flags |= CN_CBOR_FL_COUNT; + goto push; + } + break; + case MT_TAG: + cb->v.uint = val; + goto push; + case MT_PRIM: + switch (ai) { + case VAL_FALSE: cb->type = CN_CBOR_FALSE; break; + case VAL_TRUE: cb->type = CN_CBOR_TRUE; break; + case VAL_NIL: cb->type = CN_CBOR_NULL; break; + case VAL_UNDEF: cb->type = CN_CBOR_UNDEF; break; + case AI_2: +#ifndef CBOR_NO_FLOAT + cb->type = CN_CBOR_DOUBLE; + cb->v.dbl = decode_half(val); +#else /* CBOR_NO_FLOAT */ + CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); +#endif /* CBOR_NO_FLOAT */ + break; + case AI_4: +#ifndef CBOR_NO_FLOAT + cb->type = CN_CBOR_DOUBLE; + u32.u = val; + cb->v.dbl = u32.f; +#else /* CBOR_NO_FLOAT */ + CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); +#endif /* CBOR_NO_FLOAT */ + break; + case AI_8: +#ifndef CBOR_NO_FLOAT + cb->type = CN_CBOR_DOUBLE; + u64.u = val; + cb->v.dbl = u64.d; +#else /* CBOR_NO_FLOAT */ + CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); +#endif /* CBOR_NO_FLOAT */ + break; + default: cb->v.uint = val; + } + } +fill: /* emulate loops */ + if (parent->flags & CN_CBOR_FL_INDEF) { + if (parent->type == CN_CBOR_BYTES || parent->type == CN_CBOR_TEXT) + if (cb->type != parent->type) + CN_CBOR_FAIL(CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING); + goto again; + } + if (parent->flags & CN_CBOR_FL_COUNT) { + if (--parent->v.count) + goto again; + } + /* so we are done filling parent. */ +complete: /* emulate return from call */ + if (parent == top_parent) { + if (pos != ebuf) /* XXX do this outside */ + CN_CBOR_FAIL(CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED); + pb->buf = pos; + return cb; + } + cb = parent; + parent = parent->parent; + goto fill; +push: /* emulate recursive call */ + parent = cb; + goto again; +fail: + pb->buf = pos; + return 0; +} + +cn_cbor* cn_cbor_decode(const unsigned char* buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp) { + cn_cbor catcher = {CN_CBOR_INVALID, 0, {0}, 0, NULL, NULL, NULL, NULL}; + struct parse_buf pb; + cn_cbor* ret; + + pb.buf = (unsigned char *)buf; + pb.ebuf = (unsigned char *)buf+len; + pb.err = CN_CBOR_NO_ERROR; + ret = decode_item(&pb CBOR_CONTEXT_PARAM, &catcher); + if (ret != NULL) { + /* mark as top node */ + ret->parent = NULL; + } else { + if (catcher.first_child) { + catcher.first_child->parent = 0; + cn_cbor_free(catcher.first_child CBOR_CONTEXT_PARAM); + } +//fail: + if (errp) { + errp->err = pb.err; + errp->pos = pb.buf - (unsigned char *)buf; + } + return NULL; + } + return ret; +} + +#ifdef __cplusplus +} +#endif + +#endif /* CN_CBOR_C */ diff --git a/lib/ArduinoCbor/src/cn-cbor/cn-cbor.h b/lib/ArduinoCbor/src/cn-cbor/cn-cbor.h new file mode 100644 index 000000000..dcee4e106 --- /dev/null +++ b/lib/ArduinoCbor/src/cn-cbor/cn-cbor.h @@ -0,0 +1,405 @@ +/** + * \file + * \brief + * CBOR parsing + */ + +#ifndef CN_CBOR_H +#define CN_CBOR_H + +#ifdef __cplusplus +extern "C" { +#endif +#ifdef EMACS_INDENTATION_HELPER +} /* Duh. */ +#endif + +#include +#include +#include +#include + +/* missing in arduino headers */ +//typedef long ssize_t; + +/** + * All of the different kinds of CBOR values. + */ +typedef enum cn_cbor_type { + /** false */ + CN_CBOR_FALSE, + /** true */ + CN_CBOR_TRUE, + /** null */ + CN_CBOR_NULL, + /** undefined */ + CN_CBOR_UNDEF, + /** Positive integers */ + CN_CBOR_UINT, + /** Negative integers */ + CN_CBOR_INT, + /** Byte string */ + CN_CBOR_BYTES, + /** UTF-8 string */ + CN_CBOR_TEXT, + /** Byte string, in chunks. Each chunk is a child. */ + CN_CBOR_BYTES_CHUNKED, + /** UTF-8 string, in chunks. Each chunk is a child */ + CN_CBOR_TEXT_CHUNKED, + /** Array of CBOR values. Each array element is a child, in order */ + CN_CBOR_ARRAY, + /** Map of key/value pairs. Each key and value is a child, alternating. */ + CN_CBOR_MAP, + /** Tag describing the next value. The next value is the single child. */ + CN_CBOR_TAG, + /** Simple value, other than the defined ones */ + CN_CBOR_SIMPLE, + /** Doubles, floats, and half-floats */ + CN_CBOR_DOUBLE, + /** An error has occurred */ + CN_CBOR_INVALID +} cn_cbor_type; + +/** + * Flags used during parsing. Not useful for consumers of the + * `cn_cbor` structure. + */ +typedef enum cn_cbor_flags { + /** The count field will be used for parsing */ + CN_CBOR_FL_COUNT = 1, + /** An indefinite number of children */ + CN_CBOR_FL_INDEF = 2, + /** Not used yet; the structure must free the v.str pointer when the + structure is freed */ + CN_CBOR_FL_OWNER = 0x80, /* of str */ +} cn_cbor_flags; + +/** + * A CBOR value + */ +typedef struct cn_cbor { + /** The type of value */ + cn_cbor_type type; + /** Flags used at parse time */ + cn_cbor_flags flags; + /** Data associated with the value; different branches of the union are + used depending on the `type` field. */ + union { + /** CN_CBOR_BYTES */ + const uint8_t* bytes; + /** CN_CBOR_TEXT */ + const char* str; + /** CN_CBOR_INT */ + long sint; + /** CN_CBOR_UINT */ + unsigned long uint; + /** CN_CBOR_DOUBLE */ + double dbl; + /** for use during parsing */ + unsigned long count; + } v; /* TBD: optimize immediate */ + /** Number of children. + * @note: for maps, this is 2x the number of entries */ + int length; + /** The first child value */ + struct cn_cbor* first_child; + /** The last child value */ + struct cn_cbor* last_child; + /** The sibling after this one, or NULL if this is the last */ + struct cn_cbor* next; + /** The parent of this value, or NULL if this is the root */ + struct cn_cbor* parent; +} cn_cbor; + +/** + * All of the different kinds of errors + */ +typedef enum cn_cbor_error { + /** No error has occurred */ + CN_CBOR_NO_ERROR, + /** More data was expected while parsing */ + CN_CBOR_ERR_OUT_OF_DATA, + /** Some extra data was left over at the end of parsing */ + CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED, + /** A map should be alternating keys and values. A break was found + when a value was expected */ + CN_CBOR_ERR_ODD_SIZE_INDEF_MAP, + /** A break was found where it wasn't expected */ + CN_CBOR_ERR_BREAK_OUTSIDE_INDEF, + /** Indefinite encoding works for bstrs, strings, arrays, and maps. + A different major type tried to use it. */ + CN_CBOR_ERR_MT_UNDEF_FOR_INDEF, + /** Additional Information values 28-30 are reserved */ + CN_CBOR_ERR_RESERVED_AI, + /** A chunked encoding was used for a string or bstr, and one of the elements + wasn't the expected (string/bstr) type */ + CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING, + /** An invalid parameter was passed to a function */ + CN_CBOR_ERR_INVALID_PARAMETER, + /** Allocation failed */ + CN_CBOR_ERR_OUT_OF_MEMORY, + /** A float was encountered during parse but the library was built without + support for float types. */ + CN_CBOR_ERR_FLOAT_NOT_SUPPORTED +} cn_cbor_error; + +/** + * Strings matching the `cn_cbor_error` conditions. + * + * @todo: turn into a function to make the type safety more clear? + */ +extern const char *cn_cbor_error_str[]; + +/** + * Errors + */ +typedef struct cn_cbor_errback { + /** The position in the input where the erorr happened */ + int pos; + /** The error, or CN_CBOR_NO_ERROR if none */ + cn_cbor_error err; +} cn_cbor_errback; + +#ifdef USE_CBOR_CONTEXT + +/** + * Allocate and zero out memory. `count` elements of `size` are required, + * as for `calloc(3)`. The `context` is the `cn_cbor_context` passed in + * earlier to the CBOR routine. + * + * @param[in] count The number of items to allocate + * @param[in] size The size of each item + * @param[in] context The allocation context + */ +typedef void* (*cn_calloc_func)(size_t count, size_t size, void *context); + +/** + * Free memory previously allocated with a context. If using a pool allocator, + * this function will often be a no-op, but it must be supplied in order to + * prevent the CBOR library from calling `free(3)`. + * + * @note: it may be that this is never needed; if so, it will be removed for + * clarity and speed. + * + * @param context [description] + * @return [description] + */ +typedef void (*cn_free_func)(void *ptr, void *context); + +/** + * The allocation context. + */ +typedef struct cn_cbor_context { + /** The pool `calloc` routine. Must allocate and zero. */ + cn_calloc_func calloc_func; + /** The pool `free` routine. Often a no-op, but required. */ + cn_free_func free_func; + /** Typically, the pool object, to be used when calling `calloc_func` + * and `free_func` */ + void *context; +} cn_cbor_context; + +/** When USE_CBOR_CONTEXT is defined, many functions take an extra `context` + * parameter */ +#define CBOR_CONTEXT , cn_cbor_context *context +/** When USE_CBOR_CONTEXT is defined, some functions take an extra `context` + * parameter at the beginning */ +#define CBOR_CONTEXT_COMMA cn_cbor_context *context, + +#else + +#define CBOR_CONTEXT +#define CBOR_CONTEXT_COMMA + +#endif + +/** + * Decode an array of CBOR bytes into structures. + * + * @param[in] buf The array of bytes to parse + * @param[in] len The number of bytes in the array + * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) + * @param[out] errp Error, if NULL is returned + * @return The parsed CBOR structure, or NULL on error + */ +cn_cbor* cn_cbor_decode(const uint8_t *buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp); + +/** + * Get a value from a CBOR map that has the given string as a key. + * + * @param[in] cb The CBOR map + * @param[in] key The string to look up in the map + * @return The matching value, or NULL if the key is not found + */ +cn_cbor* cn_cbor_mapget_string(const cn_cbor* cb, const char* key); + +/** + * Get a value from a CBOR map that has the given integer as a key. + * + * @param[in] cb The CBOR map + * @param[in] key The int to look up in the map + * @return The matching value, or NULL if the key is not found + */ +cn_cbor* cn_cbor_mapget_int(const cn_cbor* cb, int key); + +/** + * Get the item with the given index from a CBOR array. + * + * @param[in] cb The CBOR map + * @param[in] idx The array index + * @return The matching value, or NULL if the index is invalid + */ +cn_cbor* cn_cbor_index(const cn_cbor* cb, unsigned int idx); + +/** + * Free the given CBOR structure. + * You MUST NOT try to free a cn_cbor structure with a parent (i.e., one + * that is not a root in the tree). + * + * @param[in] cb The CBOR value to free. May be NULL, or a root object. + * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) + */ +void cn_cbor_free(cn_cbor* cb CBOR_CONTEXT); + +/** + * Write a CBOR value and all of the child values. + * + * @param[in] buf The buffer into which to write + * @param[in] buf_offset The offset (in bytes) from the beginning of the buffer + * to start writing at + * @param[in] buf_size The total length (in bytes) of the buffer + * @param[in] cb [description] + * @return -1 on fail, or number of bytes written + */ +ssize_t cn_cbor_encoder_write(uint8_t *buf, + size_t buf_offset, + size_t buf_size, + const cn_cbor *cb); + +/** + * Create a CBOR map. + * + * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) + * @param[out] errp Error, if NULL is returned + * @return The created map, or NULL on error + */ +cn_cbor* cn_cbor_map_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp); + +/** + * Create a CBOR byte string. The data in the byte string is *not* owned + * by the CBOR object, so it is not freed automatically. + * + * @param[in] data The data + * @param[in] len The number of bytes of data + * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) + * @param[out] errp Error, if NULL is returned + * @return The created object, or NULL on error + */ +cn_cbor* cn_cbor_data_create(const uint8_t* data, int len + CBOR_CONTEXT, + cn_cbor_errback *errp); + +/** + * Create a CBOR UTF-8 string. The data is not checked for UTF-8 correctness. + * The data being stored in the string is *not* owned the CBOR object, so it is + * not freed automatically. + * + * @note: Do NOT use this function with untrusted data. It calls strlen, and + * relies on proper NULL-termination. + * + * @param[in] data NULL-terminated UTF-8 string + * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) + * @param[out] errp Error, if NULL is returned + * @return The created object, or NULL on error + */ +cn_cbor* cn_cbor_string_create(const char* data + CBOR_CONTEXT, + cn_cbor_errback *errp); + +/** + * Create a CBOR integer (either positive or negative). + * + * @param[in] value the value of the integer + * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) + * @param[out] errp Error, if NULL is returned + * @return The created object, or NULL on error + */ +cn_cbor* cn_cbor_int_create(int64_t value + CBOR_CONTEXT, + cn_cbor_errback *errp); + +/** + * Put a CBOR object into a map with a CBOR object key. Duplicate checks are NOT + * currently performed. + * + * @param[in] cb_map The map to insert into + * @param[in] key The key + * @param[in] cb_value The value + * @param[out] errp Error + * @return True on success + */ +bool cn_cbor_map_put(cn_cbor* cb_map, + cn_cbor *cb_key, cn_cbor *cb_value, + cn_cbor_errback *errp); + +/** + * Put a CBOR object into a map with an integer key. Duplicate checks are NOT + * currently performed. + * + * @param[in] cb_map The map to insert into + * @param[in] key The integer key + * @param[in] cb_value The value + * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) + * @param[out] errp Error + * @return True on success + */ +bool cn_cbor_mapput_int(cn_cbor* cb_map, + int64_t key, cn_cbor* cb_value + CBOR_CONTEXT, + cn_cbor_errback *errp); + +/** + * Put a CBOR object into a map with a string key. Duplicate checks are NOT + * currently performed. + * + * @note: do not call this routine with untrusted string data. It calls + * strlen, and requires a properly NULL-terminated key. + * + * @param[in] cb_map The map to insert into + * @param[in] key The string key + * @param[in] cb_value The value + * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) + * @param[out] errp Error + * @return True on success + */ +bool cn_cbor_mapput_string(cn_cbor* cb_map, + const char* key, cn_cbor* cb_value + CBOR_CONTEXT, + cn_cbor_errback *errp); + +/** + * Create a CBOR array + * + * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) + * @param[out] errp Error, if NULL is returned + * @return The created object, or NULL on error + */ +cn_cbor* cn_cbor_array_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp); + +/** + * Append an item to the end of a CBOR array. + * + * @param[in] cb_array The array into which to insert + * @param[in] cb_value The value to insert + * @param[out] errp Error + * @return True on success + */ +bool cn_cbor_array_append(cn_cbor* cb_array, + cn_cbor* cb_value, + cn_cbor_errback *errp); + +#ifdef __cplusplus +} +#endif + +#endif /* CN_CBOR_H */ diff --git a/lib/ArduinoCbor/src/cn-cbor/cn-create.c b/lib/ArduinoCbor/src/cn-cbor/cn-create.c new file mode 100644 index 000000000..772de3b16 --- /dev/null +++ b/lib/ArduinoCbor/src/cn-cbor/cn-create.c @@ -0,0 +1,184 @@ +#ifndef CN_CREATE_C +#define CN_CREATE_C + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "cn-cbor.h" +#include "../cbor.h" + +#define INIT_CB(v) \ + if (errp) {errp->err = CN_CBOR_NO_ERROR;} \ + (v) = CN_CALLOC_CONTEXT(); \ + if (!(v)) { if (errp) {errp->err = CN_CBOR_ERR_OUT_OF_MEMORY;} return NULL; } + +cn_cbor* cn_cbor_map_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp) +{ + cn_cbor* ret; + INIT_CB(ret); + + ret->type = CN_CBOR_MAP; + ret->flags |= CN_CBOR_FL_COUNT; + + return ret; +} + +cn_cbor* cn_cbor_data_create(const uint8_t* data, int len + CBOR_CONTEXT, + cn_cbor_errback *errp) +{ + cn_cbor* ret; + INIT_CB(ret); + + ret->type = CN_CBOR_BYTES; + ret->length = len; + ret->v.str = (const char*) data; // TODO: add v.ustr to the union? + + return ret; +} + +cn_cbor* cn_cbor_string_create(const char* data + CBOR_CONTEXT, + cn_cbor_errback *errp) +{ + cn_cbor* ret; + INIT_CB(ret); + + ret->type = CN_CBOR_TEXT; + ret->length = strlen(data); + ret->v.str = data; + + return ret; +} + +cn_cbor* cn_cbor_int_create(int64_t value + CBOR_CONTEXT, + cn_cbor_errback *errp) +{ + cn_cbor* ret; + INIT_CB(ret); + + if (value<0) { + ret->type = CN_CBOR_INT; + ret->v.sint = value; + } else { + ret->type = CN_CBOR_UINT; + ret->v.uint = value; + } + + return ret; +} + +static bool _append_kv(cn_cbor *cb_map, cn_cbor *key, cn_cbor *val) +{ + //Connect key and value and insert them into the map. + key->parent = cb_map; + key->next = val; + val->parent = cb_map; + val->next = NULL; + + if(cb_map->last_child) { + cb_map->last_child->next = key; + } else { + cb_map->first_child = key; + } + cb_map->last_child = val; + cb_map->length += 2; + return true; +} + +bool cn_cbor_map_put(cn_cbor* cb_map, + cn_cbor *cb_key, cn_cbor *cb_value, + cn_cbor_errback *errp) +{ + //Make sure input is a map. Otherwise + if(!cb_map || !cb_key || !cb_value || cb_map->type != CN_CBOR_MAP) + { + if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;} + return false; + } + + return _append_kv(cb_map, cb_key, cb_value); +} + +bool cn_cbor_mapput_int(cn_cbor* cb_map, + int64_t key, cn_cbor* cb_value + CBOR_CONTEXT, + cn_cbor_errback *errp) +{ + cn_cbor* cb_key; + + //Make sure input is a map. Otherwise + if(!cb_map || !cb_value || cb_map->type != CN_CBOR_MAP) + { + if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;} + return false; + } + + cb_key = cn_cbor_int_create(key CBOR_CONTEXT_PARAM, errp); + if (!cb_key) { return false; } + return _append_kv(cb_map, cb_key, cb_value); +} + +bool cn_cbor_mapput_string(cn_cbor* cb_map, + const char* key, cn_cbor* cb_value + CBOR_CONTEXT, + cn_cbor_errback *errp) +{ + cn_cbor* cb_key; + + //Make sure input is a map. Otherwise + if(!cb_map || !cb_value || cb_map->type != CN_CBOR_MAP) + { + if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;} + return false; + } + + cb_key = cn_cbor_string_create(key CBOR_CONTEXT_PARAM, errp); + if (!cb_key) { return false; } + return _append_kv(cb_map, cb_key, cb_value); +} + +cn_cbor* cn_cbor_array_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp) +{ + cn_cbor* ret; + INIT_CB(ret); + + ret->type = CN_CBOR_ARRAY; + ret->flags |= CN_CBOR_FL_COUNT; + + return ret; +} + +bool cn_cbor_array_append(cn_cbor* cb_array, + cn_cbor* cb_value, + cn_cbor_errback *errp) +{ + //Make sure input is an array. + if(!cb_array || !cb_value || cb_array->type != CN_CBOR_ARRAY) + { + if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;} + return false; + } + + cb_value->parent = cb_array; + cb_value->next = NULL; + if(cb_array->last_child) { + cb_array->last_child->next = cb_value; + } else { + cb_array->first_child = cb_value; + } + cb_array->last_child = cb_value; + cb_array->length++; + return true; +} + +#ifdef __cplusplus +} +#endif + +#endif /* CN_CBOR_C */ diff --git a/lib/ArduinoCbor/src/cn-cbor/cn-encoder.c b/lib/ArduinoCbor/src/cn-cbor/cn-encoder.c new file mode 100644 index 000000000..ce52ea755 --- /dev/null +++ b/lib/ArduinoCbor/src/cn-cbor/cn-encoder.c @@ -0,0 +1,307 @@ +#ifndef CN_ENCODER_C +#define CN_ENCODER_C + +#ifdef __cplusplus +extern "C" { +#endif +#ifdef EMACS_INDENTATION_HELPER +} /* Duh. */ +#endif + +#include +#include +#include + +#include "cn-cbor.h" +#include "../cbor.h" + +#define hton8p(p) (*(uint8_t*)(p)) +#define hton16p(p) (htons(*(uint16_t*)(p))) +#define hton32p(p) (htonl(*(uint32_t*)(p))) +static uint64_t hton64p(const uint8_t *p) { + /* TODO: does this work on both BE and LE systems? */ + uint64_t ret = hton32p(p); + ret <<= 32; + ret |= hton32p(p+4); + return ret; +} + +typedef struct _write_state +{ + uint8_t *buf; + ssize_t offset; + ssize_t size; +} cn_write_state; + +#define ensure_writable(sz) if ((ws->offset<0) || (ws->offset + (sz) >= ws->size)) { \ + ws->offset = -1; \ + return; \ +} + +#define write_byte_and_data(b, data, sz) \ +ws->buf[ws->offset++] = (b); \ +memcpy(ws->buf+ws->offset, (data), (sz)); \ +ws->offset += sz; + +#define write_byte(b) \ +ws->buf[ws->offset++] = (b); \ + +#define write_byte_ensured(b) \ +ensure_writable(1); \ +write_byte(b); \ + +static uint8_t _xlate[] = { + IB_FALSE, /* CN_CBOR_FALSE */ + IB_TRUE, /* CN_CBOR_TRUE */ + IB_NIL, /* CN_CBOR_NULL */ + IB_UNDEF, /* CN_CBOR_UNDEF */ + IB_UNSIGNED, /* CN_CBOR_UINT */ + IB_NEGATIVE, /* CN_CBOR_INT */ + IB_BYTES, /* CN_CBOR_BYTES */ + IB_TEXT, /* CN_CBOR_TEXT */ + IB_BYTES, /* CN_CBOR_BYTES_CHUNKED */ + IB_TEXT, /* CN_CBOR_TEXT_CHUNKED */ + IB_ARRAY, /* CN_CBOR_ARRAY */ + IB_MAP, /* CN_CBOR_MAP */ + IB_TAG, /* CN_CBOR_TAG */ + IB_PRIM, /* CN_CBOR_SIMPLE */ + 0xFF, /* CN_CBOR_DOUBLE */ + 0xFF /* CN_CBOR_INVALID */ +}; + +static inline bool is_indefinite(const cn_cbor *cb) +{ + return (cb->flags & CN_CBOR_FL_INDEF) != 0; +} + +static void _write_positive(cn_write_state *ws, cn_cbor_type typ, uint64_t val) { + uint8_t ib; + + assert((size_t)typ < sizeof(_xlate)); + + ib = _xlate[typ]; + if (ib == 0xFF) { + ws->offset = -1; + return; + } + + if (val < 24) { + ensure_writable(1); + write_byte(ib | val); + } else if (val < 256) { + ensure_writable(2); + write_byte(ib | 24); + write_byte((uint8_t)val); + } else if (val < 65536) { + uint16_t be16 = (uint16_t)val; + ensure_writable(3); + be16 = hton16p(&be16); + write_byte_and_data(ib | 25, (const void*)&be16, 2); + } else if (val < 0x100000000L) { + uint32_t be32 = (uint32_t)val; + ensure_writable(5); + be32 = hton32p(&be32); + write_byte_and_data(ib | 26, (const void*)&be32, 4); + } else { + uint64_t be64; + ensure_writable(9); + be64 = hton64p((const uint8_t*)&val); + write_byte_and_data(ib | 27, (const void*)&be64, 8); + } +} + +#ifndef CBOR_NO_FLOAT +static void _write_double(cn_write_state *ws, double val) +{ + float float_val = val; + if (float_val == val) { /* 32 bits is enough and we aren't NaN */ + uint32_t be32; + uint16_t be16, u16; + union { + float f; + uint32_t u; + } u32; + u32.f = float_val; + if ((u32.u & 0x1FFF) == 0) { /* worth trying half */ + int s16 = (u32.u >> 16) & 0x8000; + int exp = (u32.u >> 23) & 0xff; + int mant = u32.u & 0x7fffff; + if (exp == 0 && mant == 0) + ; /* 0.0, -0.0 */ + else if (exp >= 113 && exp <= 142) /* normalized */ + s16 += ((exp - 112) << 10) + (mant >> 13); + else if (exp >= 103 && exp < 113) { /* denorm, exp16 = 0 */ + if (mant & ((1 << (126 - exp)) - 1)) + goto float32; /* loss of precision */ + s16 += ((mant + 0x800000) >> (126 - exp)); + } else if (exp == 255 && mant == 0) { /* Inf */ + s16 += 0x7c00; + } else + goto float32; /* loss of range */ + + ensure_writable(3); + u16 = s16; + be16 = hton16p((const uint8_t*)&u16); + + write_byte_and_data(IB_PRIM | 25, (const void*)&be16, 2); + return; + } + float32: + ensure_writable(5); + be32 = hton32p((const uint8_t*)&u32.u); + + write_byte_and_data(IB_PRIM | 26, (const void*)&be32, 4); + + } else if (val != val) { /* NaN -- we always write a half NaN*/ + ensure_writable(3); + write_byte_and_data(IB_PRIM | 25, (const void*)"\x7e\x00", 2); + } else { + uint64_t be64; + /* Copy the same problematic implementation from the decoder. */ + union { + double d; + uint64_t u; + } u64; + + u64.d = val; + + ensure_writable(9); + be64 = hton64p((const uint8_t*)&u64.u); + + write_byte_and_data(IB_PRIM | 27, (const void*)&be64, 8); + + } +} +#endif /* CBOR_NO_FLOAT */ + +// TODO: make public? +typedef void (*cn_visit_func)(const cn_cbor *cb, int depth, void *context); +static void _visit(const cn_cbor *cb, + cn_visit_func visitor, + cn_visit_func breaker, + void *context) +{ + const cn_cbor *p = cb; + int depth = 0; + while (p) + { +visit: + visitor(p, depth, context); + if (p->first_child) { + p = p->first_child; + depth++; + } else{ + // Empty indefinite + if (is_indefinite(p)) { + breaker(p->parent, depth, context); + } + if (p->next) { + p = p->next; + } else { + while (p->parent) { + depth--; + if (is_indefinite(p->parent)) { + breaker(p->parent, depth, context); + } + if (p->parent->next) { + p = p->parent->next; + goto visit; + } + p = p->parent; + } + return; + } + } + } +} + +#define CHECK(st) (st); \ +if (ws->offset < 0) { return; } + +void _encoder_visitor(const cn_cbor *cb, int depth, void *context) +{ + cn_write_state *ws = context; + UNUSED_PARAM(depth); + + switch (cb->type) { + case CN_CBOR_ARRAY: + if (is_indefinite(cb)) { + write_byte_ensured(IB_ARRAY | AI_INDEF); + } else { + CHECK(_write_positive(ws, CN_CBOR_ARRAY, cb->length)); + } + break; + case CN_CBOR_MAP: + if (is_indefinite(cb)) { + write_byte_ensured(IB_MAP | AI_INDEF); + } else { + CHECK(_write_positive(ws, CN_CBOR_MAP, cb->length/2)); + } + break; + case CN_CBOR_BYTES_CHUNKED: + case CN_CBOR_TEXT_CHUNKED: + write_byte_ensured(_xlate[cb->type] | AI_INDEF); + break; + + case CN_CBOR_TEXT: + case CN_CBOR_BYTES: + CHECK(_write_positive(ws, cb->type, cb->length)); + ensure_writable(cb->length); + memcpy(ws->buf+ws->offset, cb->v.str, cb->length); + ws->offset += cb->length; + break; + + case CN_CBOR_FALSE: + case CN_CBOR_TRUE: + case CN_CBOR_NULL: + case CN_CBOR_UNDEF: + write_byte_ensured(_xlate[cb->type]); + break; + + case CN_CBOR_TAG: + case CN_CBOR_UINT: + case CN_CBOR_SIMPLE: + CHECK(_write_positive(ws, cb->type, cb->v.uint)); + break; + + case CN_CBOR_INT: + assert(cb->v.sint < 0); + CHECK(_write_positive(ws, CN_CBOR_INT, ~(cb->v.sint))); + break; + + case CN_CBOR_DOUBLE: +#ifndef CBOR_NO_FLOAT + CHECK(_write_double(ws, cb->v.dbl)); +#endif /* CBOR_NO_FLOAT */ + break; + + case CN_CBOR_INVALID: + ws->offset = -1; + break; + } +} + +void _encoder_breaker(const cn_cbor *cb, int depth, void *context) +{ + cn_write_state *ws = context; + UNUSED_PARAM(cb); + UNUSED_PARAM(depth); + write_byte_ensured(IB_BREAK); +} + +ssize_t cn_cbor_encoder_write(uint8_t *buf, + size_t buf_offset, + size_t buf_size, + const cn_cbor *cb) +{ + cn_write_state ws = { buf, buf_offset, buf_size }; + _visit(cb, _encoder_visitor, _encoder_breaker, &ws); + if (ws.offset < 0) { return -1; } + return ws.offset - buf_offset; +} + +#ifdef __cplusplus +} +#endif + +#endif /* CN_CBOR_C */ diff --git a/lib/ArduinoCbor/src/cn-cbor/cn-error.c b/lib/ArduinoCbor/src/cn-cbor/cn-error.c new file mode 100644 index 000000000..4953cc9b6 --- /dev/null +++ b/lib/ArduinoCbor/src/cn-cbor/cn-error.c @@ -0,0 +1,13 @@ +const char *cn_cbor_error_str[] = { + "CN_CBOR_NO_ERROR", + "CN_CBOR_ERR_OUT_OF_DATA", + "CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED", + "CN_CBOR_ERR_ODD_SIZE_INDEF_MAP", + "CN_CBOR_ERR_BREAK_OUTSIDE_INDEF", + "CN_CBOR_ERR_MT_UNDEF_FOR_INDEF", + "CN_CBOR_ERR_RESERVED_AI", + "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING", + "CN_CBOR_ERR_INVALID_PARAMETER", + "CN_CBOR_ERR_OUT_OF_MEMORY", + "CN_CBOR_ERR_FLOAT_NOT_SUPPORTED" +}; diff --git a/lib/ArduinoCbor/src/cn-cbor/cn-get.c b/lib/ArduinoCbor/src/cn-cbor/cn-get.c new file mode 100644 index 000000000..630b2a71d --- /dev/null +++ b/lib/ArduinoCbor/src/cn-cbor/cn-get.c @@ -0,0 +1,62 @@ +#include +#include +#include + +#include "cn-cbor.h" + +cn_cbor* cn_cbor_mapget_int(const cn_cbor* cb, int key) { + cn_cbor* cp; + assert(cb); + for (cp = cb->first_child; cp && cp->next; cp = cp->next->next) { + switch(cp->type) { + case CN_CBOR_UINT: + if (cp->v.uint == (unsigned long)key) { + return cp->next; + } + case CN_CBOR_INT: + if (cp->v.sint == (long)key) { + return cp->next; + } + break; + default: + ; // skip non-integer keys + } + } + return NULL; +} + +cn_cbor* cn_cbor_mapget_string(const cn_cbor* cb, const char* key) { + cn_cbor *cp; + int keylen; + assert(cb); + assert(key); + keylen = strlen(key); + for (cp = cb->first_child; cp && cp->next; cp = cp->next->next) { + switch(cp->type) { + case CN_CBOR_TEXT: // fall-through + case CN_CBOR_BYTES: + if (keylen != cp->length) { + continue; + } + if (memcmp(key, cp->v.str, keylen) == 0) { + return cp->next; + } + default: + ; // skip non-string keys + } + } + return NULL; +} + +cn_cbor* cn_cbor_index(const cn_cbor* cb, unsigned int idx) { + cn_cbor *cp; + unsigned int i = 0; + assert(cb); + for (cp = cb->first_child; cp; cp = cp->next) { + if (i == idx) { + return cp; + } + i++; + } + return NULL; +} diff --git a/lib/CBOR/.gitignore b/lib/CBOR/.gitignore deleted file mode 100644 index b8bd0267b..000000000 --- a/lib/CBOR/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app diff --git a/lib/CBOR/CborDecoder.cpp b/lib/CBOR/CborDecoder.cpp deleted file mode 100644 index 7151ef9bc..000000000 --- a/lib/CBOR/CborDecoder.cpp +++ /dev/null @@ -1,938 +0,0 @@ -#include "CborDecoder.h" -#include "Arduino.h" - - - -CborInput::CborInput(void *data, int size) { - this->data = (unsigned char *)data; - this->size = size; - this->offset = 0; -} - -CborInput::~CborInput() {} - - -bool CborInput::hasBytes(unsigned int count) { - return size - offset >= count; -} - -unsigned char CborInput::getByte() { - return data[offset++]; -} - -unsigned short CborInput::getShort() { - unsigned short value = ((unsigned short)data[offset] << 8) | ((unsigned short)data[offset + 1]); - offset += 2; - return value; -} - -uint32_t CborInput::getInt() { - uint32_t value = ((uint32_t)data[offset] << 24) | ((uint32_t)data[offset + 1] << 16) | ((uint32_t)data[offset + 2] << 8) | ((uint32_t)data[offset + 3]); - offset += 4; - return value; -} - -uint64_t CborInput::getLong() { - uint64_t value = ((uint64_t)data[offset] << 56) | ((uint64_t)data[offset+1] << 48) | ((uint64_t)data[offset+2] << 40) | ((uint64_t)data[offset+3] << 32) | ((uint64_t)data[offset+4] << 24) | ((uint64_t)data[offset+5] << 16) | ((uint64_t)data[offset+6] << 8) | ((uint64_t)data[offset+7]); - offset += 8; - return value; -} - -void CborInput::getBytes(void *to, int count) { - memcpy(to, data + offset, count); - offset += count; -} - - -CborReader::CborReader(CborInput &input) { - this->input = &input; - this->state = STATE_TYPE; -} - -CborReader::CborReader(CborInput &input, CborListener &listener) { - this->input = &input; - this->listener = &listener; - this->state = STATE_TYPE; -} - - -CborReader::~CborReader() {} - - -void CborReader::SetListener(CborListener &listener) { - this->listener = &listener; -} - -void CborReader::Run() { - uint32_t temp; - while(1) { - if(state == STATE_TYPE) { - if(input->hasBytes(1)) { - unsigned char type = input->getByte(); - unsigned char majorType = type >> 5; - unsigned char minorType = type & 31; - - switch(majorType) { - case 0: // positive integer - if(minorType < 24) { - listener->OnInteger(minorType); - } else if(minorType == 24) { // 1 byte - currentLength = 1; - state = STATE_PINT; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_PINT; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_PINT; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_PINT; - } else { - state = STATE_ERROR; - listener->OnError("invalid integer type"); - } - break; - case 1: // negative integer - if(minorType < 24) { - listener->OnInteger(-1 - minorType); - } else if(minorType == 24) { // 1 byte - currentLength = 1; - state = STATE_NINT; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_NINT; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_NINT; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_NINT; - } else { - state = STATE_ERROR; - listener->OnError("invalid integer type"); - } - break; - case 2: // bytes - if(minorType < 24) { - state = STATE_BYTES_DATA; - currentLength = minorType; - } else if(minorType == 24) { - state = STATE_BYTES_SIZE; - currentLength = 1; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_BYTES_SIZE; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_BYTES_SIZE; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_BYTES_SIZE; - } else { - state = STATE_ERROR; - listener->OnError("invalid bytes type"); - } - break; - case 3: // string - if(minorType < 24) { - state = STATE_STRING_DATA; - currentLength = minorType; - } else if(minorType == 24) { - state = STATE_STRING_SIZE; - currentLength = 1; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_STRING_SIZE; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_STRING_SIZE; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_STRING_SIZE; - } else { - state = STATE_ERROR; - listener->OnError("invalid string type"); - } - break; - case 4: // array - if(minorType < 24) { - listener->OnArray(minorType); - } else if(minorType == 24) { - state = STATE_ARRAY; - currentLength = 1; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_ARRAY; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_ARRAY; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_ARRAY; - } else { - state = STATE_ERROR; - listener->OnError("invalid array type"); - } - break; - case 5: // map - if(minorType < 24) { - listener->OnMap(minorType); - } else if(minorType == 24) { - state = STATE_MAP; - currentLength = 1; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_MAP; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_MAP; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_MAP; - } else { - state = STATE_ERROR; - listener->OnError("invalid array type"); - } - break; - case 6: // tag - if(minorType < 24) { - listener->OnTag(minorType); - } else if(minorType == 24) { - state = STATE_TAG; - currentLength = 1; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_TAG; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_TAG; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_TAG; - } else { - state = STATE_ERROR; - listener->OnError("invalid tag type"); - } - break; - case 7: // special - if(minorType < 24) { - listener->OnSpecial(minorType); - } else if(minorType == 24) { - state = STATE_SPECIAL; - currentLength = 1; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_SPECIAL; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_SPECIAL; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_SPECIAL; - } else { - state = STATE_ERROR; - listener->OnError("invalid special type"); - } - break; - } - } else break; - } else if(state == STATE_PINT) { - if(input->hasBytes(currentLength)) { - switch(currentLength) { - case 1: - listener->OnInteger(input->getByte()); - state = STATE_TYPE; - break; - case 2: - listener->OnInteger(input->getShort()); - state = STATE_TYPE; - break; - case 4: - temp = input->getInt(); - if(temp <= INT_MAX) { - listener->OnInteger(temp); - } else { - listener->OnExtraInteger(temp, 1); - } - state = STATE_TYPE; - break; - case 8: - listener->OnExtraInteger(input->getLong(), 1); - state = STATE_TYPE; - break; - } - } else break; - } else if(state == STATE_NINT) { - if(input->hasBytes(currentLength)) { - switch(currentLength) { - case 1: - listener->OnInteger(-(int32_t)input->getByte()); - state = STATE_TYPE; - break; - case 2: - listener->OnInteger(-(int32_t)input->getShort()); - state = STATE_TYPE; - break; - case 4: - temp = input->getInt(); - if(temp <= INT_MAX) { - listener->OnInteger(-(int32_t) temp); - } else if(temp == 2147483648u) { - listener->OnInteger(INT_MIN); - } else { - listener->OnExtraInteger(temp, -1); - } - state = STATE_TYPE; - break; - case 8: - listener->OnExtraInteger(input->getLong(), -1); - break; - } - } else break; - } else if(state == STATE_BYTES_SIZE) { - if(input->hasBytes(currentLength)) { - switch(currentLength) { - case 1: - currentLength = input->getByte(); - state = STATE_BYTES_DATA; - break; - case 2: - currentLength = input->getShort(); - state = STATE_BYTES_DATA; - break; - case 4: - currentLength = input->getInt(); - state = STATE_BYTES_DATA; - break; - case 8: - state = STATE_ERROR; - listener->OnError("extra long bytes"); - break; - } - } else break; - } else if(state == STATE_BYTES_DATA) { - if(input->hasBytes(currentLength)) { - unsigned char *data = new unsigned char[currentLength]; - input->getBytes(data, currentLength); - state = STATE_TYPE; - listener->OnBytes(data, currentLength); - } else break; - } else if(state == STATE_STRING_SIZE) { - if(input->hasBytes(currentLength)) { - switch(currentLength) { - case 1: - currentLength = input->getByte(); - state = STATE_STRING_DATA; - break; - case 2: - currentLength = input->getShort(); - state = STATE_STRING_DATA; - break; - case 4: - currentLength = input->getInt(); - state = STATE_STRING_DATA; - break; - case 8: - state = STATE_ERROR; - listener->OnError("extra long array"); - break; - } - } else break; - } else if(state == STATE_STRING_DATA) { - if(input->hasBytes(currentLength)) { - unsigned char data[currentLength + 1]; - input->getBytes(data, currentLength); - data[currentLength] = '\0'; - state = STATE_TYPE; - String str = String((char*)data); - listener->OnString(str); - str = ""; - } else break; - } else if(state == STATE_ARRAY) { - if(input->hasBytes(currentLength)) { - switch(currentLength) { - case 1: - listener->OnArray(input->getByte()); - state = STATE_TYPE; - break; - case 2: - listener->OnArray(currentLength = input->getShort()); - state = STATE_TYPE; - break; - case 4: - listener->OnArray(input->getInt()); - state = STATE_TYPE; - break; - case 8: - state = STATE_ERROR; - listener->OnError("extra long array"); - break; - } - } else break; - } else if(state == STATE_MAP) { - if(input->hasBytes(currentLength)) { - switch(currentLength) { - case 1: - listener->OnMap(input->getByte()); - state = STATE_TYPE; - break; - case 2: - listener->OnMap(currentLength = input->getShort()); - state = STATE_TYPE; - break; - case 4: - listener->OnMap(input->getInt()); - state = STATE_TYPE; - break; - case 8: - state = STATE_ERROR; - listener->OnError("extra long map"); - break; - } - } else break; - } else if(state == STATE_TAG) { - if(input->hasBytes(currentLength)) { - switch(currentLength) { - case 1: - listener->OnTag(input->getByte()); - state = STATE_TYPE; - break; - case 2: - listener->OnTag(input->getShort()); - state = STATE_TYPE; - break; - case 4: - listener->OnTag(input->getInt()); - state = STATE_TYPE; - break; - case 8: - listener->OnExtraTag(input->getLong()); - state = STATE_TYPE; - break; - } - } else break; - } else if(state == STATE_SPECIAL) { - if (input->hasBytes(currentLength)) { - switch (currentLength) { - case 1: - listener->OnSpecial(input->getByte()); - state = STATE_TYPE; - break; - case 2: - listener->OnSpecial(input->getShort()); - state = STATE_TYPE; - break; - case 4: - listener->OnSpecial(input->getInt()); - state = STATE_TYPE; - break; - case 8: - listener->OnExtraSpecial(input->getLong()); - state = STATE_TYPE; - break; - } - } else break; - } else if(state == STATE_ERROR) { - break; - } else { - Serial.print("UNKNOWN STATE"); - } - } -} - -void CborReader::GetCborData(String &Cborpackage) { - uint32_t temp; - volatile char commaChar = ','; - int tempint; - - while(1) { - if(state == STATE_TYPE) { - if(input->hasBytes(1)) { - unsigned char type = input->getByte(); - unsigned char majorType = type >> 5; - unsigned char minorType = type & 31; - //Serial.write(majorType); - //Serial.println("minorType:" + minorType); - - switch(majorType) { - case 0: // positive integer - if(minorType < 24) { - listener->OnInteger(minorType); - Cborpackage += minorType ; - Cborpackage += commaChar; - } else if(minorType == 24) { // 1 byte - currentLength = 1; - state = STATE_PINT; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_PINT; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_PINT; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_PINT; - } else { - state = STATE_ERROR; - listener->OnError("invalid integer type"); - } - break; - case 1: // negative integer - if(minorType < 24) { - listener->OnInteger(-1 - minorType); - Cborpackage += -1 -minorType ; - Cborpackage += commaChar; - - } else if(minorType == 24) { // 1 byte - currentLength = 1; - state = STATE_NINT; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_NINT; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_NINT; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_NINT; - } else { - state = STATE_ERROR; - listener->OnError("invalid integer type"); - } - break; - case 2: // bytes - if(minorType < 24) { - state = STATE_BYTES_DATA; - currentLength = minorType; - } else if(minorType == 24) { - state = STATE_BYTES_SIZE; - currentLength = 1; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_BYTES_SIZE; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_BYTES_SIZE; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_BYTES_SIZE; - } else { - state = STATE_ERROR; - listener->OnError("invalid bytes type"); - } - break; - case 3: // string - if(minorType < 24) { - state = STATE_STRING_DATA; - currentLength = minorType; - } else if(minorType == 24) { - state = STATE_STRING_SIZE; - currentLength = 1; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_STRING_SIZE; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_STRING_SIZE; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_STRING_SIZE; - } else { - state = STATE_ERROR; - listener->OnError("invalid string type"); - } - break; - case 4: // array - if(minorType < 24) { - listener->OnArray(minorType); - } else if(minorType == 24) { - state = STATE_ARRAY; - currentLength = 1; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_ARRAY; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_ARRAY; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_ARRAY; - } else { - state = STATE_ERROR; - listener->OnError("invalid array type"); - } - break; - case 5: // map - if(minorType < 24) { - listener->OnMap(minorType); - } else if(minorType == 24) { - state = STATE_MAP; - currentLength = 1; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_MAP; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_MAP; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_MAP; - } else { - state = STATE_ERROR; - listener->OnError("invalid array type"); - } - break; - case 6: // tag - if(minorType < 24) { - listener->OnTag(minorType); - } else if(minorType == 24) { - state = STATE_TAG; - currentLength = 1; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_TAG; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_TAG; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_TAG; - } else { - state = STATE_ERROR; - listener->OnError("invalid tag type"); - } - break; - case 7: // special - if(minorType < 24) { - listener->OnSpecial(minorType); - Serial.print("h"); - } else if(minorType == 24) { - state = STATE_SPECIAL; - currentLength = 1; - } else if(minorType == 25) { // 2 byte - currentLength = 2; - state = STATE_SPECIAL; - } else if(minorType == 26) { // 4 byte - currentLength = 4; - state = STATE_SPECIAL; - } else if(minorType == 27) { // 8 byte - currentLength = 8; - state = STATE_SPECIAL; - } else { - state = STATE_ERROR; - listener->OnError("invalid special type"); - } - break; - } - } else break; - } else if(state == STATE_PINT) { - if(input->hasBytes(currentLength)) { - switch(currentLength) { - case 1: - temp =(int32_t) input->getByte(); - listener->OnInteger(temp); - Cborpackage += temp; - Cborpackage += commaChar; - state = STATE_TYPE; - break; - case 2: - temp =(int32_t) input->getShort(); - listener->OnInteger(temp); - Cborpackage += temp; - Cborpackage += commaChar; - state = STATE_TYPE; - break; - case 4: - temp =(int32_t) input->getInt(); - if(temp <= INT_MAX) { - listener->OnInteger(temp); - Cborpackage += temp; - Cborpackage += commaChar; - - } else { - - listener->OnExtraInteger(temp, 1); - Cborpackage += temp ; - Cborpackage += commaChar; - - } - state = STATE_TYPE; - break; - case 8: - temp =(int32_t) input->getLong(); - listener->OnExtraInteger(temp, 1); - - Cborpackage += temp ; - Cborpackage += commaChar; - state = STATE_TYPE; - break; - } - } else break; - } else if(state == STATE_NINT) { - if(input->hasBytes(currentLength)) { - switch(currentLength) { - case 1: - listener->OnInteger(-(int32_t)input->getByte()); - Cborpackage += (int32_t) input->getByte() ; - Cborpackage += commaChar; - Serial.print("h"); - - state = STATE_TYPE; - break; - case 2: - listener->OnInteger(-(int32_t)input->getShort()); - Cborpackage += (int32_t) input->getShort(); - Cborpackage += commaChar; - Serial.print("h"); - - state = STATE_TYPE; - break; - case 4: - temp = input->getInt(); - if(temp <= INT_MAX) { - listener->OnInteger(-(int32_t) temp); - Cborpackage += (int32_t) temp ; - - Serial.print("h"); - Cborpackage += commaChar; - } else if(temp == 2147483648u) { - listener->OnInteger(INT_MIN); - Serial.print("h"); - - Cborpackage += INT_MIN ; - Cborpackage += commaChar; - } else { - listener->OnExtraInteger(temp, -1); - } - state = STATE_TYPE; - break; - case 8: - listener->OnExtraInteger(input->getLong(), -1); - Serial.print("h"); - //Cborpackage += input->getLong() ; - Cborpackage += commaChar; - - break; - } - } else break; - } else if(state == STATE_BYTES_SIZE) { - if(input->hasBytes(currentLength)) { - switch(currentLength) { - case 1: - currentLength = input->getByte(); - state = STATE_BYTES_DATA; - break; - case 2: - currentLength = input->getShort(); - state = STATE_BYTES_DATA; - break; - case 4: - currentLength = input->getInt(); - state = STATE_BYTES_DATA; - break; - case 8: - state = STATE_ERROR; - listener->OnError("extra long bytes"); - break; - } - } else break; - } else if(state == STATE_BYTES_DATA) { - if(input->hasBytes(currentLength)) { - unsigned char *data = new unsigned char[currentLength]; - input->getBytes(data, currentLength); - state = STATE_TYPE; - listener->OnBytes(data, currentLength); - Cborpackage += String(INT_MIN, DEC) ; - Cborpackage += commaChar; - } else break; - } else if(state == STATE_STRING_SIZE) { - if(input->hasBytes(currentLength)) { - switch(currentLength) { - case 1: - currentLength = input->getByte(); - state = STATE_STRING_DATA; - break; - case 2: - currentLength = input->getShort(); - state = STATE_STRING_DATA; - break; - case 4: - currentLength = input->getInt(); - state = STATE_STRING_DATA; - break; - case 8: - state = STATE_ERROR; - listener->OnError("extra long array"); - break; - } - } else break; - } else if(state == STATE_STRING_DATA) { - if(input->hasBytes(currentLength)) { - unsigned char data[currentLength]; - //Serial.print("currentLength:"); - //Serial.println(currentLength); - input->getBytes(data, currentLength); - state = STATE_TYPE; - String str = (const char *) data; - Cborpackage += (const char *) data; - Cborpackage += commaChar; - listener->OnString(str); - str = ""; - } else break; - } else if(state == STATE_ARRAY) { - if(input->hasBytes(currentLength)) { - switch(currentLength) { - case 1: - listener->OnArray(input->getByte()); - state = STATE_TYPE; - break; - case 2: - listener->OnArray(currentLength = input->getShort()); - state = STATE_TYPE; - break; - case 4: - listener->OnArray(input->getInt()); - state = STATE_TYPE; - break; - case 8: - state = STATE_ERROR; - listener->OnError("extra long array"); - break; - } - } else break; - } else if(state == STATE_MAP) { - if(input->hasBytes(currentLength)) { - switch(currentLength) { - case 1: - listener->OnMap(input->getByte()); - state = STATE_TYPE; - break; - case 2: - listener->OnMap(currentLength = input->getShort()); - state = STATE_TYPE; - break; - case 4: - listener->OnMap(input->getInt()); - state = STATE_TYPE; - break; - case 8: - state = STATE_ERROR; - listener->OnError("extra long map"); - break; - } - } else break; - } else if(state == STATE_TAG) { - if(input->hasBytes(currentLength)) { - switch(currentLength) { - case 1: - listener->OnTag(input->getByte()); - state = STATE_TYPE; - break; - case 2: - listener->OnTag(input->getShort()); - state = STATE_TYPE; - break; - case 4: - listener->OnTag(input->getInt()); - state = STATE_TYPE; - break; - case 8: - listener->OnExtraTag(input->getLong()); - state = STATE_TYPE; - break; - } - } else break; - } else if(state == STATE_SPECIAL) { - if (input->hasBytes(currentLength)) { - switch (currentLength) { - case 1: - listener->OnSpecial(input->getByte()); - state = STATE_TYPE; - break; - case 2: - listener->OnSpecial(input->getShort()); - state = STATE_TYPE; - break; - case 4: - listener->OnSpecial(input->getInt()); - state = STATE_TYPE; - break; - case 8: - listener->OnExtraSpecial(input->getLong()); - state = STATE_TYPE; - break; - } - } else break; - } else if(state == STATE_ERROR) { - break; - } else { - Serial.print("UNKNOWN STATE"); - } - } -} - - -// TEST HANDLERS - -void CborDebugListener::OnInteger(int32_t value) { - Serial.print("integer:"); - Serial.println(value); - //Cborpackage += value; - //Cborpackage += commaChar; -} - -void CborDebugListener::OnBytes(unsigned char *data, unsigned int size) { - Serial.print("bytes with size"); - Serial.println(size); -} - -void CborDebugListener::OnString(String &str) { - //Serial.print("string:"); - //int lastStringLength = str.length(); - //Serial.print(lastStringLength); - //Serial.println(str); -} - -void CborDebugListener::OnArray(unsigned int size) { - Serial.print("array:"); - Serial.println(size); -} - -void CborDebugListener::OnMap(unsigned int size) { - Serial.print("map:"); - Serial.println(size); -} - -void CborDebugListener::OnTag(uint32_t tag) { - Serial.print("tag:"); - Serial.println(tag); - -} - -void CborDebugListener::OnSpecial(uint32_t code) { - Serial.println("special" + code); -} - -void CborDebugListener::OnError(const char *error) { - Serial.print("error:"); - - Serial.println(error); -} - -void CborDebugListener::OnExtraInteger(uint64_t value, int sign) { - if(sign >= 0) { - Serial.println("extra integer: %llu\n" + value); - } else { - Serial.println("extra integer: -%llu\n" + value); - } -} - -void CborDebugListener::OnExtraTag(uint64_t tag) { - Serial.println("extra tag: %llu\n" + tag); -} - -void CborDebugListener::OnExtraSpecial(uint64_t tag) { - Serial.println("extra special: %llu\n" + tag); - -} diff --git a/lib/CBOR/CborDecoder.h b/lib/CBOR/CborDecoder.h deleted file mode 100644 index 1c3d46cbf..000000000 --- a/lib/CBOR/CborDecoder.h +++ /dev/null @@ -1,107 +0,0 @@ -#ifndef CBORDE_H -#define CBORDE_H - -#include "Arduino.h" - -#define INT_MAX 2 -#define INT_MIN 2 - - -typedef enum { - STATE_TYPE, - STATE_PINT, - STATE_NINT, - STATE_BYTES_SIZE, - STATE_BYTES_DATA, - STATE_STRING_SIZE, - STATE_STRING_DATA, - STATE_ARRAY, - STATE_MAP, - STATE_TAG, - STATE_SPECIAL, - STATE_ERROR -} CborReaderState; - -class CborInput { - -public: - CborInput(void *data, int size); - ~CborInput(); - - bool hasBytes(unsigned int count); - unsigned char getByte(); - unsigned short getShort(); - uint32_t getInt(); - uint64_t getLong(); - void getBytes(void *to, int count); -private: - unsigned char *data; - int size; - int offset; -}; - - - - -class CborListener { -public: - virtual void OnInteger(int32_t value) = 0; - virtual void OnBytes(unsigned char *data, unsigned int size) = 0; - virtual void OnString(String &str) = 0; - virtual void OnArray(unsigned int size) = 0; - virtual void OnMap(unsigned int size) = 0; - virtual void OnTag(uint32_t tag) = 0; - virtual void OnSpecial(uint32_t code) = 0; - virtual void OnError(const char *error) = 0; - virtual void OnExtraInteger(uint64_t value, int sign) {} - virtual void OnExtraTag(uint64_t tag) {} - virtual void OnExtraSpecial(uint64_t tag) {} -}; - -class CborDebugListener : public CborListener { -public: - virtual void OnInteger(int32_t value); - virtual void OnBytes(unsigned char *data, unsigned int size); - virtual void OnString(String &str); - virtual void OnArray(unsigned int size); - virtual void OnMap(unsigned int size); - virtual void OnTag(uint32_t tag); - virtual void OnSpecial(uint32_t code); - virtual void OnError(const char *error); - - virtual void OnExtraInteger(uint64_t value, int sign); - virtual void OnExtraTag(uint64_t tag); - virtual void OnExtraSpecial(uint64_t tag); -}; - -class CborReader { -public: - CborReader(CborInput &input); - CborReader(CborInput &input, CborListener &listener); - ~CborReader(); - void Run(); - void SetListener(CborListener &listener); - void GetCborData(String &Cborpackage); -private: - CborListener *listener; - CborInput *input; - CborReaderState state; - unsigned int currentLength; -}; - - -class CborExampleListener : public CborListener { - public: - void OnInteger(int32_t value); - void OnBytes(unsigned char *data, unsigned int size); - void OnString(String &str); - void OnArray(unsigned int size); - void OnMap(unsigned int size) ; - void OnTag(uint32_t tag); - void OnSpecial(uint32_t code); - void OnError(const char *error); - }; - - - -#endif diff --git a/lib/CBOR/CborEncoder.cpp b/lib/CBOR/CborEncoder.cpp deleted file mode 100644 index 5baa1752e..000000000 --- a/lib/CBOR/CborEncoder.cpp +++ /dev/null @@ -1,208 +0,0 @@ -#include "CborEncoder.h" -#include "Arduino.h" -#include - - - - - -CborStaticOutput::CborStaticOutput(const unsigned int capacity) { - this->capacity = capacity; - this->buffer = new unsigned char[capacity]; - this->offset = 0; -} - -CborStaticOutput::~CborStaticOutput() { - delete buffer; -} - -void CborStaticOutput::putByte(unsigned char value) { - if(offset < capacity) { - buffer[offset++] = value; - } else { - Serial.print("buffer overflow error"); - } -} - -void CborStaticOutput::putBytes(const unsigned char *data, const unsigned int size) { - if(offset + size - 1 < capacity) { - memcpy(buffer + offset, data, size); - offset += size; - } else { - Serial.print("buffer overflow error"); - } -} - -CborWriter::CborWriter(CborOutput &output) { - this->output = &output; -} - -CborWriter::~CborWriter() { - -} - -unsigned char *CborStaticOutput::getData() { - return buffer; -} - -unsigned int CborStaticOutput::getSize() { - return offset; -} - -CborDynamicOutput::CborDynamicOutput() { - init(256); -} - -CborDynamicOutput::CborDynamicOutput(const uint32_t initalCapacity) { - init(initalCapacity); -} - -CborDynamicOutput::~CborDynamicOutput() { - delete buffer; -} - -void CborDynamicOutput::init(unsigned int initalCapacity) { - this->capacity = initalCapacity; - this->buffer = new unsigned char[initalCapacity]; - this->offset = 0; -} - - -unsigned char *CborDynamicOutput::getData() { - return buffer; -} - -unsigned int CborDynamicOutput::getSize() { - return offset; -} - -void CborDynamicOutput::putByte(unsigned char value) { - if(offset < capacity) { - buffer[offset++] = value; - } else { - capacity *= 2; - buffer = (unsigned char *) realloc(buffer, capacity); - buffer[offset++] = value; - } -} - -void CborDynamicOutput::putBytes(const unsigned char *data, const unsigned int size) { - while(offset + size > capacity) { - capacity *= 2; - buffer = (unsigned char *) realloc(buffer, capacity); - } - - memcpy(buffer + offset, data, size); - offset += size; -} - -void CborWriter::writeTypeAndValue(uint8_t majorType, const uint32_t value) { - majorType <<= 5; - if(value < 24) { - output->putByte(majorType | value); - } else if(value < 256) { - output->putByte(majorType | 24); - output->putByte(value); - } else if(value < 65536) { - output->putByte(majorType | 25); - output->putByte(value >> 8); - output->putByte(value); - } else { - output->putByte(majorType | 26); - output->putByte(value >> 24); - output->putByte(value >> 16); - output->putByte(value >> 8); - output->putByte(value); - } -} - -void CborWriter::writeTypeAndValue(uint8_t majorType, const uint64_t value) { - majorType <<= 5; - if(value < 24ULL) { - output->putByte(majorType | value); - } else if(value < 256ULL) { - output->putByte(majorType | 24); - output->putByte(value); - } else if(value < 65536ULL) { - output->putByte(majorType | 25); - output->putByte(value >> 8); - } else if(value < 4294967296ULL) { - output->putByte(majorType | 26); - output->putByte(value >> 24); - output->putByte(value >> 16); - output->putByte(value >> 8); - output->putByte(value); - } else { - output->putByte(majorType | 27); - output->putByte(value >> 56); - output->putByte(value >> 48); - output->putByte(value >> 40); - output->putByte(value >> 32); - output->putByte(value >> 24); - output->putByte(value >> 16); - output->putByte(value >> 8); - output->putByte(value); - } -} - -void CborWriter::writeInt(const int value) { - // This will break on 64-bit platforms - writeTypeAndValue(0, (uint32_t)value); -} - -void CborWriter::writeInt(const uint32_t value) { - writeTypeAndValue(0, value); -} - -void CborWriter::writeInt(const uint64_t value) { - writeTypeAndValue(0, value); -} - -void CborWriter::writeInt(const int64_t value) { - if(value < 0) { - writeTypeAndValue(1, (uint64_t) -(value+1)); - } else { - writeTypeAndValue(0, (uint64_t) value); - } -} - -/* -void CborWriter::writeInt(const int32_t value) { - if(value < 0) { - writeTypeAndValue(1, (uint32_t) -(value+1)); - } else { - writeTypeAndValue(0, (uint32_t) value); - } -} -*/ - -void CborWriter::writeBytes(const unsigned char *data, const unsigned int size) { - writeTypeAndValue(2, (uint32_t)size); - output->putBytes(data, size); -} - -void CborWriter::writeString(const char *data, const unsigned int size) { - writeTypeAndValue(3, (uint32_t)size); - output->putBytes((const unsigned char *)data, size); -} - -void CborWriter::writeString(const String str) { - writeTypeAndValue(3, (uint32_t)str.length()); - output->putBytes((const unsigned char *)str.c_str(), str.length()); -} - -void CborWriter::writeArray(const unsigned int size) { - writeTypeAndValue(4, (uint32_t)size); -} - -void CborWriter::writeMap(const unsigned int size) { - writeTypeAndValue(5, (uint32_t)size); -} - -void CborWriter::writeTag(const uint32_t tag) { - writeTypeAndValue(6, tag); -} - -void CborWriter::writeSpecial(const uint32_t special) { - writeTypeAndValue(7, special); -} diff --git a/lib/CBOR/CborEncoder.h b/lib/CBOR/CborEncoder.h deleted file mode 100644 index 0e3b5ac20..000000000 --- a/lib/CBOR/CborEncoder.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - Copyright 2014-2015 Stanislav Ovsyannikov - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - - - - -#ifndef CBOREN_H -#define CBOREN_H - -#include "Arduino.h" - -class CborOutput { -public: - virtual unsigned char *getData() = 0; - virtual unsigned int getSize() = 0; - virtual void putByte(unsigned char value) = 0; - virtual void putBytes(const unsigned char *data, const unsigned int size) = 0; -}; - -class CborStaticOutput : public CborOutput { -public: - CborStaticOutput(unsigned int capacity); - ~CborStaticOutput(); - virtual unsigned char *getData(); - virtual unsigned int getSize(); - virtual void putByte(unsigned char value); - virtual void putBytes(const unsigned char *data, const unsigned int size); -private: - unsigned char *buffer; - unsigned int capacity; - unsigned int offset; -}; - - -class CborDynamicOutput : public CborOutput { -public: - CborDynamicOutput(); - CborDynamicOutput(uint32_t initalCapacity); - ~CborDynamicOutput(); - - - virtual unsigned char *getData(); - virtual unsigned int getSize(); - virtual void putByte(unsigned char value); - virtual void putBytes(const unsigned char *data, const unsigned int size); -private: - void init(unsigned int initalCapacity); - unsigned char *buffer; - unsigned int capacity; - unsigned int offset; -}; - -class CborWriter { -public: - CborWriter(CborOutput &output); - ~CborWriter(); - - void writeInt(const int value); - //void writeInt(const int32_t value); - void writeInt(const int64_t value); - void writeInt(const uint32_t value); - void writeInt(const uint64_t value); - void writeBytes(const unsigned char *data, const unsigned int size); - void writeString(const char *data, const unsigned int size); - void writeString(const String str); - void writeArray(const unsigned int size); - void writeMap(const unsigned int size); - void writeTag(const uint32_t tag); - void writeSpecial(const uint32_t special); -private: - void writeTypeAndValue(uint8_t majorType, const uint32_t value); - void writeTypeAndValue(uint8_t majorType, const uint64_t value); - CborOutput *output; -}; - -class CborSerializable { -public: - virtual void Serialize(CborWriter &writer) = 0; -}; -#endif diff --git a/lib/CBOR/Examples/Cbor_Serialbuffer_send/Cbor_Serialbuffer_send.ino b/lib/CBOR/Examples/Cbor_Serialbuffer_send/Cbor_Serialbuffer_send.ino deleted file mode 100644 index dab225515..000000000 --- a/lib/CBOR/Examples/Cbor_Serialbuffer_send/Cbor_Serialbuffer_send.ino +++ /dev/null @@ -1,91 +0,0 @@ -/* - -Arduino Sketch to show how Send Encode Cbor package via Serial port - -Author: Juanjo Tara -email: j.tara@arduino.cc -date: 24/04/2015 -*/ - - -#include "CborEncoder.h" -#include "SerialBuffer.h" - -#define SERIAL_PORT Serial - -#define BUFFER_SIZE 32 -unsigned char buffer[BUFFER_SIZE]; - -// declare the serial buffer -SerialBuffer serialBuffer; - - -void setup() { - - // set up the buffer storage and maximum size - serialBuffer.buffer = buffer; - serialBuffer.bufferSize = BUFFER_SIZE; - - // reset the buffer - serialBuffer.reset(); - Serial.begin(115200); - randomSeed(analogRead(0)); - -} - -void loop() { - - testSerialPort(); - - int maxBytes = SERIAL_PORT.available(); - while (maxBytes--) { - - byte inputByte = SERIAL_PORT.read(); - - // present the input byte to the serial buffer for decoding - // whenever receive() returns >= 0, there's a complete message - // in the buffer ready for processing at offset zero. - // (return value is message length) - int bufferStatus = serialBuffer.receive(inputByte); - - if (bufferStatus >= 0) { - - // handle message - // ... - // ... - - } - } - delay(100); - -} - -void testSerialPort() { - CborStaticOutput output(32); - CborWriter writer(output); - //Write a Cbor Package with a number and String - int randNumb = random(15); - for(int i = 0; i < randNumb; i++) { - int numb = random(300); - writer.writeInt(numb); - } - //writer.writeInt(0x7F); - //writer.writeInt(0x7E); - //writer.writeInt(0x7D); - //writer.writeString("Hello David"); - //writer.writeInt(321); - - //get length and data of cbor package - unsigned char *datapkg = output.getData(); - int length = output.getSize(); - serialBuffer.startMessage(); - for(int i = 0; i < length; i++) { - serialBuffer.write(datapkg[i]); - } - serialBuffer.endMessage(); - //print in Serial port the Data length and Cbor in binary - //Serial.print("datalength:"); - //Serial.print(output.getSize()); - //Serial.println(output.getSize()); - Serial.write(serialBuffer.buffer, serialBuffer.messageLength()); -} \ No newline at end of file diff --git a/lib/CBOR/Examples/Cbor_Serialport_sender/Cbor_Serialport_sender.ino b/lib/CBOR/Examples/Cbor_Serialport_sender/Cbor_Serialport_sender.ino deleted file mode 100644 index 5d838dbca..000000000 --- a/lib/CBOR/Examples/Cbor_Serialport_sender/Cbor_Serialport_sender.ino +++ /dev/null @@ -1,44 +0,0 @@ -/* - -Arduino Sketch to show how Send Encode Cbor package via Serial port - -Author: Juanjo Tara -email: j.tara@arduino.cc -date: 24/04/2015 -*/ - - -#include "CborEncoder.h" - - - -void setup() { - Serial.begin(9600); - -} - -void loop() { - - testSerialPort(); - delay(10000); - -} - -void testSerialPort() { - CborStaticOutput output(32); - CborWriter writer(output); - //Write a Cbor Package with a number and String - writer.writeInt(124); - writer.writeString("I"); - - //get length and data of cbor package - unsigned int datalength = output.getSize(); - unsigned char *datapkg = output.getData(); - - //print in Serial port the Data length and Cbor in binary - //Serial.print("datalength:"); - Serial.print(datalength); - Serial.write(datapkg,datalength); -} - - diff --git a/lib/CBOR/Examples/Cbor_master_reader/Cbor_master_reader.ino b/lib/CBOR/Examples/Cbor_master_reader/Cbor_master_reader.ino deleted file mode 100644 index a4e72bbb6..000000000 --- a/lib/CBOR/Examples/Cbor_master_reader/Cbor_master_reader.ino +++ /dev/null @@ -1,39 +0,0 @@ -/* - -Arduino Sketch to show how decode a Cbor package received from I2c -This sketch must to load in the master Arduino - -Author: Juanjo Tara -email: j.tara@arduino.cc -date: 24/04/2015 -*/ - - -#include -#include - - -void setup() -{ - Wire.begin(); // join i2c bus (address optional for master) - Serial.begin(9600); // start serial for output - -} - -void loop() -{ - Wire.requestFrom(2, 4); // request 6 bytes from slave device #2 - - while (Wire.available()) // slave may send less than requested - { - char c = Wire.read(); // receive a byte as character - Serial.print(c); // print the character - } - - CborInput input(data, size); - CborReader reader(input); - CborExampleListener listener; - reader.SetListener(listener); - reader.Run(); - delay(500); -} diff --git a/lib/CBOR/Examples/Cbor_slave_sender/Cbor_slave_sender.ino b/lib/CBOR/Examples/Cbor_slave_sender/Cbor_slave_sender.ino deleted file mode 100644 index d2dccb294..000000000 --- a/lib/CBOR/Examples/Cbor_slave_sender/Cbor_slave_sender.ino +++ /dev/null @@ -1,44 +0,0 @@ -/* - -Arduino Sketch to show how Send Encode Cbor package and sending via I2c as Slave requester -This sketch must to load in the Slave Arduino - -Author: Juanjo Tara -email: j.tara@arduino.cc -date: 24/04/2015 -*/ - - - - -#include "CborEncoder.h" -#include - - -void setup() { - Wire.begin(2); // join i2c bus with address #2 - Wire.onRequest(requestEvent); // register event - Serial.begin(9600); - // writting(); -} - -void loop() { - -} - - -// function that executes whenever data is requested by master -// this function is registered as an event, see setup() -void requestEvent() -{ - - //get length and data of cbor package - unsigned char *datapkg = output.getData(); - int datalength = output.getSize(); - - Serial.print("datalength:"); - Serial.print(datalength); - - Wire.write(*datapkg); // respond with message - -} diff --git a/lib/CBOR/Examples/cbortest2/cbortest2.ino b/lib/CBOR/Examples/cbortest2/cbortest2.ino deleted file mode 100644 index 911f95d3c..000000000 --- a/lib/CBOR/Examples/cbortest2/cbortest2.ino +++ /dev/null @@ -1,67 +0,0 @@ -/* - -Arduino Sketch to show how encode and Decode Cbor package - -Author: Juanjo Tara -email: j.tara@arduino.cc -date: 24/04/2015 -*/ - - - - -#include "CborEncoder.h" -#include "CborDecoder.h" - -//String to save the cbor data -String dt = ""; -String valuetoInt = ""; -unsigned int sizeee; -int valuein; - - -void setup() { - - Serial.begin(9600); - //Serial.print("hola"); - test1(); -} - -void loop() { - - -} - - - -void test1() { - - //Create object and Writer - CborStaticOutput output(32); - CborWriter writer(output); - - //Write a Cbor Package with a number and String - writer.writeInt(124); - writer.writeString("I"); - - sizeee = output.getSize(); - Serial.print("datalength:"); - Serial.println(sizeee); - - delay(1000); - - //Receiver for the Cbor input - CborInput input(output.getData(), output.getSize()); - CborDebugListener listener; - CborReader reader(input); - reader.SetListener(listener); - //Save all the cbor into a String divived by commas - reader.GetCborData(dt); - - - valuetoInt = dt.substring(0, dt.indexOf(',')); - valuein = valuetoInt.toInt(); - - Serial.print(valuetoInt.toInt()); - -} diff --git a/lib/CBOR/LICENSE b/lib/CBOR/LICENSE deleted file mode 100644 index 261eeb9e9..000000000 --- a/lib/CBOR/LICENSE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. diff --git a/lib/CBOR/README.md b/lib/CBOR/README.md deleted file mode 100644 index dd8602dfa..000000000 --- a/lib/CBOR/README.md +++ /dev/null @@ -1,83 +0,0 @@ -cbor-cpp -======== - -CBOR C++ serialization library - -Just a simple SAX-like Concise Binary Object Representation (CBOR). - -[http://tools.ietf.org/html/rfc7049](http://tools.ietf.org/html/rfc7049) - -#### Examples - -Writing: - -```C++ - CborDynamicOutput output; - CborWriter writer(output); - - writer.writeTag(123); - writer.writeArray(3); - writer.writeString("hello"); - writer.writeString("world"); - writer.writeInt(321); - - unsigned char *data = output.getData(); - int size = output.getSize(); -``` - -Reading: - -```C++ - class CborExampleListener : public CborListener { - public: - virtual void OnInteger(int32_t value); - virtual void OnBytes(unsigned char *data, unsigned int size); - virtual void OnString(std::string &str); - virtual void OnArray(unsigned int size); - virtual void OnMap(unsigned int size); - virtual void OnTag(uint32_t tag); - virtual void OnSpecial(uint32_t code); - virtual void OnError(const char *error); - }; - - ... - - void CborExampleListener::OnInteger(int32_t value) { - printf("integer: %d\n", value); - } - - void CborExampleListener::OnBytes(unsigned char *data, unsigned int size) { - printf("bytes with size: %d", size); - } - - void CborExampleListener::OnString(string &str) { - printf("string: '%.*s'\n", (int)str.size(), str.c_str()); - } - - void CborExampleListener::OnArray(unsigned int size) { - printf("array: %d\n", size); - } - - void CborExampleListener::OnMap(unsigned int size) { - printf("map: %d\n", size); - } - - void CborExampleListener::OnTag(unsigned int tag) { - printf("tag: %d\n", tag); - } - - void CborExampleListener::OnSpecial(unsigned int code) { - printf("special: %d\n", code); - } - - void CborExampleListener::OnError(const char *error) { - printf("error: %s\n", error); - } - - ... - CborInput input(data, size); - CborReader reader(input); - CborExampleListener listener; - reader.SetListener(listener); - reader.Run(); -``` From 86f53b733654db2c8bb36440eb66efe2f7a6d759 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 16 Feb 2018 12:13:44 +0100 Subject: [PATCH 011/175] use composition to add functionalities --- ArduinoCloudThing.cpp | 109 ++++++++++++++++++++++++++++++++++-------- ArduinoCloudThing.h | 58 +++++++++++++++++++--- 2 files changed, 141 insertions(+), 26 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 2556b182b..c40010541 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -1,7 +1,7 @@ #include #include -#ifdef DEBUG_MEMORY +#if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) extern "C" char *sbrk(int i); void PrintFreeRam (void) { @@ -127,7 +127,7 @@ int ArduinoCloudThing::poll() { publish(object); } -#ifdef DEBUG_MEMORY +#if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) PrintFreeRam(); #endif @@ -140,7 +140,7 @@ void ArduinoCloudThing::compress(CborObject& object, CborBuffer& buffer) { for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); - if (p->newData()) { + if (p->shouldBeUpdated()) { CborObject child = CborObject(buffer); p->append(child); CborVariant variant = CborVariant(buffer, child); @@ -154,7 +154,7 @@ int ArduinoCloudThing::checkNewData() { int counter = 0; for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); - if (p->newData()) { + if (p->shouldBeUpdated()) { counter++; } } @@ -171,28 +171,31 @@ bool ArduinoCloudThing::exists(String &name) { return false; } -void ArduinoCloudThing::addPropertyReal(int& property, String name, permissionType permission) { +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, String name, permissionType permission) { if (exists(name)) { - return; + return *(reinterpret_cast(NULL)); } ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name, permission); list.add(thing); + return *(reinterpret_cast(thing)); } -void ArduinoCloudThing::addPropertyReal(bool& property, String name, permissionType permission) { +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, String name, permissionType permission) { if (exists(name)) { - return; + return *(reinterpret_cast(NULL)); } ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name, permission); list.add(thing); + return *(reinterpret_cast(thing)); } -void ArduinoCloudThing::addPropertyReal(float& property, String name, permissionType permission) { +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, String name, permissionType permission) { if (exists(name)) { - return; + return *(reinterpret_cast(NULL)); } ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name, permission); list.add(thing); + return *(reinterpret_cast(thing)); } void ArduinoCloudThing::callback(MQTTClient *client, char topic[], char bytes[], int length) { @@ -200,19 +203,87 @@ void ArduinoCloudThing::callback(MQTTClient *client, char topic[], char bytes[], } void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { - /* CborBuffer buffer(200); - CborVariant variant = buffer.decode(payload, length); - CborArray array = variant.asArray(); + CborVariant total = buffer.decode(payload, length); + CborArray array = total.asArray(); - CborVariant obj = array.get(0); - if (!obj.isValid()) { - return; - } - if (obj.isString()) { + for (int i=0; ;i++) { + CborVariant variant = array.get(i); + + if (!variant.isValid()) { + break; + } + + CborObject object = variant.asObject(); + + String name = ""; + if (object.get("n").isValid()) { + name = object.get("n").asString(); + // search for the property with the same name + for (int idx = 0; idx < list.size(); idx++) { + ArduinoCloudPropertyGeneric *p = list.get(idx); + if (p->getName() == name) { + currentListIndex = idx; + break; + } + if (idx == list.size()) { + Serial.println("Property not found, skipping"); + currentListIndex = -1; + } + } + } + if (object.get("t").isValid()) { + int tag = object.get("t").asInteger(); + + if (name != "") { + list.get(currentListIndex)->setTag(tag); + } else { + for (int idx = 0; idx < list.size(); idx++) { + ArduinoCloudPropertyGeneric *p = list.get(idx); + if (p->getTag() == tag) { + // if name == "" associate name and tag, otherwise set current list index + currentListIndex = idx; + break; + } + if (idx == list.size()) { + Serial.println("Property not found, skipping"); + currentListIndex = -1; + } + } + } + } + + if (object.get("i").isValid()) { + int value_i = object.get("i").asInteger(); + reinterpret_cast*>(list.get(currentListIndex))->write(value_i); + } + + if (object.get("b").isValid()) { + bool value_b = object.get("b").asInteger(); + reinterpret_cast*>(list.get(currentListIndex))->write(value_b); + } +/* + if (object.get("f").isValid()) { + float value_f = object.get("f").asFloat(); + reinterpret_cast*>(list.get(currentListIndex))->write(value_f); + } +*/ + if (object.get("s").isValid()) { + String value_s = object.get("s").asString(); + reinterpret_cast*>(list.get(currentListIndex))->write(value_s); + } + + if (object.get("p").isValid()) { + permissionType value_p = (permissionType)object.get("p").asInteger(); + list.get(currentListIndex)->setPermission(value_p); + } + + if (list.get(currentListIndex)->newData()) { + // call onUpdate() + list.get(currentListIndex)->callback(); + } } - */ } /* diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 4f5793c4e..13da5890b 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -29,16 +29,31 @@ enum boolStatus { OFF, }; +#define ON_CHANGE -1 + +enum times { + SECONDS = 1, + MINUTES = 60, + HOURS = 3600, + DAYS = 86400, +}; + class ArduinoCloudPropertyGeneric { public: virtual void append(CborObject& object) = 0; virtual String& getName() = 0; virtual void setName(String _name) = 0; + virtual void setTag(int _tag) = 0; + virtual int getTag() = 0; virtual void setPermission(permissionType _permission) = 0; virtual permissionType getPermission() = 0; virtual bool newData() = 0; + virtual bool shouldBeUpdated() = 0; virtual void updateShadow() = 0; + virtual ArduinoCloudPropertyGeneric& onUpdate(void(*fn)(void)) = 0; + virtual ArduinoCloudPropertyGeneric& publishEvery(long seconds) = 0; + void(*callback)(void) = NULL; }; template @@ -78,6 +93,10 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric tag = _tag; } + int getTag() { + return tag; + } + void setPermission(permissionType _permission) { permission = _permission; } @@ -86,18 +105,40 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric return permission; } + ArduinoCloudPropertyGeneric& onUpdate(void(*fn)(void)) { + callback = fn; + return *(reinterpret_cast(this)); + } + void appendValue(CborObject &cbor); void append(CborObject &cbor) { - cbor.set("n", name.c_str()); + if (tag != -1) { + cbor.set("t", tag); + } else { + cbor.set("n", name.c_str()); + } appendValue(cbor); cbor.set("p", permission); + lastUpdated = millis(); + } + + ArduinoCloudPropertyGeneric& publishEvery(long seconds) { + updatePolicy = seconds; + return *(reinterpret_cast(this)); } bool newData() { return (property != shadow_property); } + bool shouldBeUpdated() { + if (updatePolicy == ON_CHANGE) { + return newData(); + } + return (millis() - lastUpdated > updatePolicy * 1000) ; + } + inline bool operator==(const ArduinoCloudProperty& rhs){ return (strcmp(getName(), rhs.getName) == 0); } @@ -106,7 +147,9 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric T& property; T shadow_property; String name; - int tag; + int tag = -1; + long lastUpdated = 0; + long updatePolicy = ON_CHANGE; permissionType permission; static int tagIndex; }; @@ -144,11 +187,11 @@ class ArduinoCloudThing { public: ArduinoCloudThing(); void begin(Client &client); - void addPropertyReal(int& property, String name, permissionType permission); - void addPropertyReal(bool& property, String name, permissionType permission); - void addPropertyReal(float& property, String name, permissionType permission); - void addPropertyReal(void* property, String name, permissionType permission); - void addPropertyReal(String property, String name, permissionType permission); + ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name, permissionType permission); + ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name, permissionType permission); + ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name, permissionType permission); + ArduinoCloudPropertyGeneric& addPropertyReal(void* property, String name, permissionType permission); + ArduinoCloudPropertyGeneric& addPropertyReal(String property, String name, permissionType permission); // poll should return > 0 if something has changed int poll(); @@ -168,6 +211,7 @@ class ArduinoCloudThing { char uuid[33]; LinkedList list; + int currentListIndex = -1; MQTTClient* client; }; From e388049d29bbea3259b1582869f97021ca1dd611 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 16 Feb 2018 14:00:24 +0100 Subject: [PATCH 012/175] Fix encoding --- ArduinoCloudThing.cpp | 95 ++++--------------------------- ArduinoCloudThing.h | 4 +- lib/ArduinoCbor/src/CborArray.cpp | 4 ++ lib/ArduinoCbor/src/CborArray.h | 2 + 4 files changed, 20 insertions(+), 85 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index c40010541..b061378bf 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -22,7 +22,6 @@ static void utox8(uint32_t val, char* s) { #endif #ifdef USE_ARDUINO_CLOUD - char MQTT_SERVER[] = "10.130.22.94"; int MQTT_PORT = 1883; char GENERAL_TOPIC[] = "/main"; @@ -30,7 +29,10 @@ char MQTT_USER[] = ""; char MQTT_PASSWORD[] = ""; char LWT_TOPIC[] = ""; char LWT_MESSAGE[] = ""; +#endif +#ifdef ARDUINO_ARCH_MRAA +#define Serial DebugSerial #endif ArduinoCloudThing::ArduinoCloudThing() { @@ -84,7 +86,7 @@ bool ArduinoCloudThing::connect() { return false; } -void ArduinoCloudThing::publish(CborObject& object) { +void ArduinoCloudThing::publish(CborArray& object) { bool retained = false; @@ -122,7 +124,7 @@ int ArduinoCloudThing::poll() { diff = checkNewData(); if (diff > 0) { CborBuffer buffer(1024); - CborObject object = CborObject(buffer); + CborArray object = CborArray(buffer); compress(object, buffer); publish(object); } @@ -134,9 +136,7 @@ int ArduinoCloudThing::poll() { return diff; } -void ArduinoCloudThing::compress(CborObject& object, CborBuffer& buffer) { - - CborArray array = CborArray(buffer); +void ArduinoCloudThing::compress(CborArray& object, CborBuffer& buffer) { for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); @@ -144,10 +144,9 @@ void ArduinoCloudThing::compress(CborObject& object, CborBuffer& buffer) { CborObject child = CborObject(buffer); p->append(child); CborVariant variant = CborVariant(buffer, child); - array.add(variant); + object.add(variant); } } - object.set("a", array); } int ArduinoCloudThing::checkNewData() { @@ -205,6 +204,7 @@ void ArduinoCloudThing::callback(MQTTClient *client, char topic[], char bytes[], void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { CborBuffer buffer(200); CborVariant total = buffer.decode(payload, length); + CborArray array = total.asArray(); for (int i=0; ;i++) { @@ -214,6 +214,7 @@ void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { break; } + CborObject object = variant.asObject(); String name = ""; @@ -227,7 +228,6 @@ void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { break; } if (idx == list.size()) { - Serial.println("Property not found, skipping"); currentListIndex = -1; } } @@ -235,7 +235,6 @@ void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { if (object.get("t").isValid()) { int tag = object.get("t").asInteger(); - if (name != "") { list.get(currentListIndex)->setTag(tag); } else { @@ -281,79 +280,9 @@ void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { if (list.get(currentListIndex)->newData()) { // call onUpdate() - list.get(currentListIndex)->callback(); - } - } -} - -/* - -void CborPropertyListener::OnInteger(int32_t value) { - if (currentListIndex < 0) { - return; - } - reinterpret_cast*>(list->get(currentListIndex))->write(value); -} - -void CborPropertyListener::OnBytes(unsigned char *data, unsigned int size) { - printf("bytes with size: %d", size); -} - -void CborPropertyListener::OnString(String &str) { - // if tag arrived, search a string with the same name in the list - if (newElement == true) { - newElement = false; - for (int i = 0; i < list->size(); i++) { - ArduinoCloudPropertyGeneric *p = list->get(i); - if (p->getName() == str) { - currentListIndex = i; - break; - } - if (i == list->size()) { - Serial.println("Property not found, skipping"); - currentListIndex = -1; + if (list.get(currentListIndex)->callback != NULL) { + list.get(currentListIndex)->callback(); } } - } else { - if (currentListIndex < 0) { - return; - } - reinterpret_cast*>(list->get(currentListIndex))->write(str); - } -} - -void CborPropertyListener::OnArray(unsigned int size) { - - // prepare for new properties to arrive - if (justStarted == true) { - list_size = size; - justStarted = false; - } -} - -void CborPropertyListener::OnMap(unsigned int size) { -} - -void CborPropertyListener::OnTag(uint32_t tag) { - newElement = true; - list_size--; - if (list_size < 0) { - Serial.println("problem, we got more properties than advertised"); - } -} - -void CborPropertyListener::OnSpecial(uint32_t code) { - - if (currentListIndex < 0) { - return; - } - if (list->get(currentListIndex)->getPermission() != code) { - Serial.println("permission changed, updating"); - list->get(currentListIndex)->setPermission((permissionType)code); } -} - -void CborPropertyListener::OnError(const char *error) { -} - -*/ \ No newline at end of file +} \ No newline at end of file diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 13da5890b..61d00424d 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -198,11 +198,11 @@ class ArduinoCloudThing { private: static void callback(MQTTClient *client, char topic[], char bytes[], int length); bool connect(); - void publish(CborObject& object); + void publish(CborArray& object); void update(); int checkNewData(); - void compress(CborObject& object, CborBuffer& buffer); + void compress(CborArray& object, CborBuffer& buffer); void decode(uint8_t * payload, size_t length); bool exists(String &name); diff --git a/lib/ArduinoCbor/src/CborArray.cpp b/lib/ArduinoCbor/src/CborArray.cpp index 2adf5085f..6fc19f7b2 100644 --- a/lib/ArduinoCbor/src/CborArray.cpp +++ b/lib/ArduinoCbor/src/CborArray.cpp @@ -30,3 +30,7 @@ void CborArray::add(const char* value) { void CborArray::add(CBOR_INT_T value) { add(CborVariant(buffer, value)); } + +size_t CborArray::encode(uint8_t* data, size_t size) { + return cn_cbor_encoder_write(data, 0, size, raw); +} diff --git a/lib/ArduinoCbor/src/CborArray.h b/lib/ArduinoCbor/src/CborArray.h index 5f51619e2..208861fb5 100644 --- a/lib/ArduinoCbor/src/CborArray.h +++ b/lib/ArduinoCbor/src/CborArray.h @@ -15,6 +15,8 @@ class CborArray { void add(const char* value); void add(CBOR_INT_T value); + size_t encode(uint8_t* data, size_t size); + protected: CborBuffer& buffer; cn_cbor* raw; From ebaa1abbd1495e6512574e285485ae52242a9b75 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 16 Feb 2018 14:09:01 +0100 Subject: [PATCH 013/175] Library is now ArduinoCloudThing --- ArduinoCloudNew.h | 6 - ArduinoCloudThing.cpp | 60 +- ArduinoCloudThing.h | 14 +- lib/MQTT/CMakeLists.txt | 35 - lib/MQTT/DEVELOPING.md | 4 - lib/MQTT/LICENSE.md | 21 - lib/MQTT/Makefile | 14 - lib/MQTT/README.md | 214 ----- .../AdafruitHuzzahESP8266.ino | 71 -- .../AdafruitHuzzahESP8266_SSL.ino | 73 -- .../ArduinoEthernetShield.ino | 62 -- .../ArduinoWiFi101/ArduinoWiFi101.ino | 70 -- .../ArduinoWiFi101_SSL/ArduinoWiFi101_SSL.ino | 75 -- .../ArduinoWiFiShield/ArduinoWiFiShield.ino | 68 -- lib/MQTT/examples/ArduinoYun/ArduinoYun.ino | 60 -- .../ArduinoYun_SSL/ArduinoYun_SSL.ino | 62 -- .../ESP32DevelopmentBoard.ino | 69 -- .../ESP32DevelopmentBoard_SSL.ino | 71 -- lib/MQTT/library.properties | 9 - lib/MQTT/src/MQTTClient.h | 371 --------- lib/MQTT/src/lwmqtt/client.c | 615 --------------- lib/MQTT/src/lwmqtt/helpers.c | 251 ------ lib/MQTT/src/lwmqtt/helpers.h | 137 ---- lib/MQTT/src/lwmqtt/lwmqtt.h | 373 --------- lib/MQTT/src/lwmqtt/packet.c | 742 ------------------ lib/MQTT/src/lwmqtt/packet.h | 185 ----- lib/MQTT/src/lwmqtt/string.c | 38 - lib/MQTT/src/system.cpp | 48 -- lib/MQTT/src/system.h | 26 - 29 files changed, 4 insertions(+), 3840 deletions(-) delete mode 100644 ArduinoCloudNew.h delete mode 100644 lib/MQTT/CMakeLists.txt delete mode 100644 lib/MQTT/DEVELOPING.md delete mode 100644 lib/MQTT/LICENSE.md delete mode 100644 lib/MQTT/Makefile delete mode 100644 lib/MQTT/README.md delete mode 100644 lib/MQTT/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino delete mode 100644 lib/MQTT/examples/AdafruitHuzzahESP8266_SSL/AdafruitHuzzahESP8266_SSL.ino delete mode 100644 lib/MQTT/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino delete mode 100644 lib/MQTT/examples/ArduinoWiFi101/ArduinoWiFi101.ino delete mode 100644 lib/MQTT/examples/ArduinoWiFi101_SSL/ArduinoWiFi101_SSL.ino delete mode 100644 lib/MQTT/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino delete mode 100644 lib/MQTT/examples/ArduinoYun/ArduinoYun.ino delete mode 100644 lib/MQTT/examples/ArduinoYun_SSL/ArduinoYun_SSL.ino delete mode 100644 lib/MQTT/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino delete mode 100644 lib/MQTT/examples/ESP32DevelopmentBoard_SSL/ESP32DevelopmentBoard_SSL.ino delete mode 100644 lib/MQTT/library.properties delete mode 100644 lib/MQTT/src/MQTTClient.h delete mode 100644 lib/MQTT/src/lwmqtt/client.c delete mode 100644 lib/MQTT/src/lwmqtt/helpers.c delete mode 100644 lib/MQTT/src/lwmqtt/helpers.h delete mode 100644 lib/MQTT/src/lwmqtt/lwmqtt.h delete mode 100644 lib/MQTT/src/lwmqtt/packet.c delete mode 100644 lib/MQTT/src/lwmqtt/packet.h delete mode 100644 lib/MQTT/src/lwmqtt/string.c delete mode 100644 lib/MQTT/src/system.cpp delete mode 100644 lib/MQTT/src/system.h diff --git a/ArduinoCloudNew.h b/ArduinoCloudNew.h deleted file mode 100644 index db584257f..000000000 --- a/ArduinoCloudNew.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef ArduinoCloud_h -#define ArduinoCloud_h - -#include - -#endif diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index b061378bf..0f32cc727 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -21,16 +21,6 @@ static void utox8(uint32_t val, char* s) { } #endif -#ifdef USE_ARDUINO_CLOUD -char MQTT_SERVER[] = "10.130.22.94"; -int MQTT_PORT = 1883; -char GENERAL_TOPIC[] = "/main"; -char MQTT_USER[] = ""; -char MQTT_PASSWORD[] = ""; -char LWT_TOPIC[] = ""; -char LWT_MESSAGE[] = ""; -#endif - #ifdef ARDUINO_ARCH_MRAA #define Serial DebugSerial #endif @@ -50,46 +40,19 @@ ArduinoCloudThing::ArduinoCloudThing() { #endif } -/* - * begin() should prepare the environment - * connect - */ - -void ArduinoCloudThing::begin(Client &client) { - this->client = new MQTTClient(); - this->client->onMessageAdvanced(ArduinoCloudThing::callback); - this->client->begin(MQTT_SERVER, MQTT_PORT, client); - this->client->setParent((void*)this); - - // using WiFi client and ECC508 connect to server - while (!connect()) { - Serial.println("Not connected"); - delay(500); - } -} -bool ArduinoCloudThing::connect() { +bool ArduinoCloudThing::begin() { #ifdef TESTING_PROTOCOL return true; #endif - if (client->connect(uuid, MQTT_USER, MQTT_PASSWORD) != 0) { - // set status to ON - status = ON; - addProperty(status, READ); - // subscribe to "general" topic - client->subscribe(GENERAL_TOPIC); - return true; - } - - return false; + status = ON; + addProperty(status, READ); } void ArduinoCloudThing::publish(CborArray& object) { - bool retained = false; - uint8_t data[1024]; size_t size = object.encode(data, sizeof(data)); @@ -97,27 +60,14 @@ void ArduinoCloudThing::publish(CborArray& object) { decode(data, size); #endif -#ifndef TESTING_PROTOCOL - client->publish(GENERAL_TOPIC, (const char*)data, size); -#endif - for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); p->updateShadow(); } } -// Reconnect to the mqtt broker int ArduinoCloudThing::poll() { -#ifndef TESTING_PROTOCOL - if (!client->connected()){ - if (!connect()){ - return 0; - } - } -#endif - // check if backing storage and cloud has diverged int diff = 0; @@ -197,10 +147,6 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, return *(reinterpret_cast(thing)); } -void ArduinoCloudThing::callback(MQTTClient *client, char topic[], char bytes[], int length) { - reinterpret_cast(client->getParent())->decode((uint8_t *)bytes, length); -} - void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { CborBuffer buffer(200); CborVariant total = buffer.decode(payload, length); diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 61d00424d..e5abaad3c 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -3,21 +3,13 @@ #include #include -#include "lib/MQTT/src/MQTTClient.h" #include "lib/LinkedList/LinkedList.h" #include "lib/ArduinoCbor/src/ArduinoCbor.h" -#ifndef MQTT_BUFFER_SIZE -#define MQTT_BUFFER_SIZE 256 -#endif - //#define TESTING_PROTOCOL #define DEBUG_MEMORY #define USE_ARDUINO_CLOUD -//#define MQTTCLIENT_QOS1 0 -//#define MQTTCLIENT_QOS2 0 - enum permissionType { READ = 0b01, WRITE = 0b10, @@ -186,7 +178,7 @@ inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { class ArduinoCloudThing { public: ArduinoCloudThing(); - void begin(Client &client); + void begin(); ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name, permissionType permission); ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name, permissionType permission); ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name, permissionType permission); @@ -196,8 +188,6 @@ class ArduinoCloudThing { int poll(); private: - static void callback(MQTTClient *client, char topic[], char bytes[], int length); - bool connect(); void publish(CborArray& object); void update(); @@ -212,8 +202,6 @@ class ArduinoCloudThing { LinkedList list; int currentListIndex = -1; - - MQTTClient* client; }; #endif diff --git a/lib/MQTT/CMakeLists.txt b/lib/MQTT/CMakeLists.txt deleted file mode 100644 index 8bb1d4b27..000000000 --- a/lib/MQTT/CMakeLists.txt +++ /dev/null @@ -1,35 +0,0 @@ -# Uncompilable CMake File to enable project editing with CLion IDE - -cmake_minimum_required(VERSION 2.8.4) -project(arduino-mqtt) - -include_directories( - /Applications/Arduino.app/Contents/Java/hardware/arduino/avr/cores/arduino/ - /Users/256dpi/Development/Arduino/libraries/Ethernet/src - /Users/256dpi/Development/Arduino/libraries/WiFi101/src - /Applications/Arduino.app/Contents/Java/libraries/Bridge/src - /Users/256dpi/Library/Arduino15/packages/esp8266/hardware/esp8266/2.3.0/libraries/ESP8266WiFi/src - /Users/256dpi/Development/Arduino/hardware/espressif/esp32/libraries/WiFi/src - /Users/256dpi/Development/Arduino/hardware/espressif/esp32/libraries/WiFiClientSecure/src) - -include_directories(src/) - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") - -set(SOURCE_FILES - examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino - examples/AdafruitHuzzahESP8266_SSL/AdafruitHuzzahESP8266_SSL.ino - examples/ArduinoEthernetShield/ArduinoEthernetShield.ino - examples/ArduinoWiFi101/ArduinoWiFi101.ino - examples/ArduinoWiFi101_SSL/ArduinoWiFi101_SSL.ino - examples/ArduinoWiFiShield/ArduinoWiFiShield.ino - examples/ArduinoYun/ArduinoYun.ino - examples/ArduinoYun_SSL/ArduinoYun_SSL.ino - examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino - examples/ESP32DevelopmentBoard_SSL/ESP32DevelopmentBoard_SSL.ino - src/lwmqtt - src/MQTTClient.h - src/system.cpp - src/system.h) - -add_executable(arduino-mqtt ${SOURCE_FILES}) diff --git a/lib/MQTT/DEVELOPING.md b/lib/MQTT/DEVELOPING.md deleted file mode 100644 index 676c00ce9..000000000 --- a/lib/MQTT/DEVELOPING.md +++ /dev/null @@ -1,4 +0,0 @@ -# Developing - -- Update version in `library.properties`. -- Create release on GitHub. diff --git a/lib/MQTT/LICENSE.md b/lib/MQTT/LICENSE.md deleted file mode 100644 index 325e07cff..000000000 --- a/lib/MQTT/LICENSE.md +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Joël Gähwiler - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/lib/MQTT/Makefile b/lib/MQTT/Makefile deleted file mode 100644 index 0e94cc412..000000000 --- a/lib/MQTT/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -all: fmt - -fmt: - clang-format -i src/*.cpp src/*.h -style="{BasedOnStyle: Google, ColumnLimit: 120}" - -update: - rm -rf ./lwmqtt - git clone --branch v0.5.6 https://github.com/256dpi/lwmqtt.git ./lwmqtt - mkdir -p ./src/lwmqtt - cp -r ./lwmqtt/src/*.c ./src/lwmqtt/ - cp -r ./lwmqtt/src/*.h ./src/lwmqtt/ - cp -r ./lwmqtt/include/*.h ./src/lwmqtt/ - rm -rf ./lwmqtt - sed -i '' "s//\"lwmqtt.h\"/g" ./src/lwmqtt/* diff --git a/lib/MQTT/README.md b/lib/MQTT/README.md deleted file mode 100644 index 695318eb4..000000000 --- a/lib/MQTT/README.md +++ /dev/null @@ -1,214 +0,0 @@ -# arduino-mqtt - -[![Build Status](https://travis-ci.org/256dpi/arduino-mqtt.svg?branch=master)](https://travis-ci.org/256dpi/arduino-mqtt) -[![GitHub release](https://img.shields.io/github/release/256dpi/arduino-mqtt.svg)](https://github.com/256dpi/arduino-mqtt/releases) - -This library bundles the [lwmqtt](https://github.com/256dpi/lwmqtt) client and adds a thin wrapper to get an Arduino like API. - -Download the latest version from the [release](https://github.com/256dpi/arduino-mqtt/releases) section. Or even better use the builtin Library Manager in the Arduino IDE and search for "MQTT". - -## Compatibility - -The following examples show how you can use the library with various Arduino compatible hardware: - -- [Arduino Yun & Yun-Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoYun/ArduinoYun.ino) ([SSL](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoYun_SSL/ArduinoYun_SSL.ino)) -- [Arduino Ethernet Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino) -- [Arduino WiFi Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino) -- [Adafruit HUZZAH ESP8266](https://github.com/256dpi/arduino-mqtt/blob/master/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino) ([SSL](https://github.com/256dpi/arduino-mqtt/blob/master/examples/AdafruitHuzzahESP8266_SSL/AdafruitHuzzahESP8266_SSL.ino)) -- [Arduino/Genuino WiFi101 Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFi101/ArduinoWiFi101.ino) ([SSL](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFi101_SSL/ArduinoWiFi101_SSL.ino)) -- [ESP32 Development Board](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino) ([SSL](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ESP32DevelopmentBoard_SSL/ESP32DevelopmentBoard_SSL.ino)) - -Other shields and boards should also work if they provide a [Client](https://www.arduino.cc/en/Reference/ClientConstructor) based network implementation. - -## Notes - -- The maximum size for packets being published and received is set by default to 128 bytes. To change the buffer sizes, you need to use `MQTTClient client(256)` instead of just `MQTTClient client` on the top of your sketch. The passed value denotes the read and write buffer size. - -- On the ESP8266 it has been reported that an additional `delay(10);` after `client.loop();` fixes many stability issues with WiFi connections. - -- To use the library with shiftr.io, you need to provide the token key (username) and token secret (password) as the second and third argument to `client.connect(name, key, secret)`. - -## Example - -The following example uses an Arduino MKR1000 to connect to shiftr.io. You can check on your device after a successful connection here: https://shiftr.io/try. - -```c++ -#include -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - client.begin("broker.shiftr.io", net); - client.onMessage(messageReceived); - - connect(); -} - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} -``` - -## API - -Initialize the object using the hostname of the broker, the brokers port (default: `1883`) and the underlying Client class for network transport: - -```c++ -void begin(const char hostname[], Client &client); -void begin(const char hostname[], int port, Client &client); -``` - -- Specify port `8883` when using SSL clients for secure connections. -- Local domain names (e.g. `Computer.local` on OSX) are not supported by Arduino. You need to set the IP address directly. - -The hostname and port can also be changed after calling `begin()`: - -```c++ -void setHost(const char hostname[]); -void setHost(const char hostname[], int port); -``` - -Set a will message (last testament) that gets registered on the broker after connecting: - -```c++ -void setWill(const char topic[]); -void setWill(const char topic[], const char payload[]); -void setWill(const char topic[], const char payload[], bool retained, int qos); -void clearWill(); -``` - -Register a callback to receive messages: - -```c++ -void onMessage(MQTTClientCallbackSimple); -// Callback signature: void messageReceived(String &topic, String &payload) {} - -void onMessageAdvanced(MQTTClientCallbackAdvanced); -// Callback signature: void messageReceived(MQTTClient *client, char[] topic, char payload[], int payload_length) {} -``` - -- The set callback is mostly called during a call to `loop()` but may also be called during a call to `subscribe()`, `unsubscribe()` or `publish() // QoS > 0` if messages have been received before receiving the required acknowledgement. Therefore, it is strongly recommended to not call `subscribe()`, `unsubscribe()` or `publish() // QoS > 0` directly in the callback. - -Set more advanced options: - -```c++ -void setOptions(int keepAlive, bool cleanSession, int timeout); -``` - -- The `keepAlive` option controls the keep alive interval (default: 10). -- The `cleanSession` option controls the session retention on the broker side (default: true). -- The `timeout` option controls the default timeout for all commands in milliseconds (default: 1000). - -Connect to broker using the supplied client id and an optional username and password: - -```c++ -boolean connect(const char clientId[]); -boolean connect(const char clientId[], const char username[]); -boolean connect(const char clientId[], const char username[], const char password[]); -``` - -- This functions returns a boolean that indicates if the connection has been established successfully. - -Publishes a message to the broker with an optional payload: - -```c++ -boolean publish(const String &topic); -boolean publish(const char topic[]); -boolean publish(const String &topic, const String &payload); -boolean publish(const String &topic, const String &payload, bool retained, int qos); -boolean publish(const char topic[], const String &payload); -boolean publish(const char topic[], const String &payload, bool retained, int qos); -boolean publish(const char topic[], const char payload[]); -boolean publish(const char topic[], const char payload[], bool retained, int qos); -boolean publish(const char topic[], const char payload[], int length); -boolean publish(const char topic[], const char payload[], int length, bool retained, int qos); -``` - -Subscribe to a topic: - -```c++ -boolean subscribe(const String &topic); -boolean subscribe(const String &topic, int qos); -boolean subscribe(const char topic[]); -boolean subscribe(const char topic[], int qos); -``` - -Unsubscribe from a topic: - -```c++ -boolean unsubscribe(const String &topic); -boolean unsubscribe(const char topic[]); -``` - -Sends and receives packets: - -```c++ -boolean loop(); -``` - -- This function should be called in every `loop`. - -Check if the client is currently connected: - -```c++ -boolean connected(); -``` - -Access low-level information for debugging: - -```c++ -lwmqtt_err_t lastError(); -lwmqtt_return_code_t returnCode(); -``` - -Disconnect from the broker: - -```c++ -boolean disconnect(); -``` diff --git a/lib/MQTT/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino b/lib/MQTT/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino deleted file mode 100644 index ee6bb938e..000000000 --- a/lib/MQTT/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino +++ /dev/null @@ -1,71 +0,0 @@ -// This example uses an Adafruit Huzzah ESP8266 -// to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void connect(); // <- predefine connect() for setup() - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - client.begin("broker.shiftr.io", net); - client.onMessage(messageReceived); - - connect(); -} - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void loop() { - client.loop(); - delay(10); // <- fixes some issues with WiFi stability - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} diff --git a/lib/MQTT/examples/AdafruitHuzzahESP8266_SSL/AdafruitHuzzahESP8266_SSL.ino b/lib/MQTT/examples/AdafruitHuzzahESP8266_SSL/AdafruitHuzzahESP8266_SSL.ino deleted file mode 100644 index e01213cb8..000000000 --- a/lib/MQTT/examples/AdafruitHuzzahESP8266_SSL/AdafruitHuzzahESP8266_SSL.ino +++ /dev/null @@ -1,73 +0,0 @@ -// This example uses an Adafruit Huzzah ESP8266 -// to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiClientSecure net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void connect(); // <- predefine connect() for setup() - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - // - // MQTT brokers usually use port 8883 for secure connections. - client.begin("broker.shiftr.io", 8883, net); - client.onMessage(messageReceived); - - connect(); -} - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void loop() { - client.loop(); - delay(10); // <- fixes some issues with WiFi stability - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} diff --git a/lib/MQTT/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino b/lib/MQTT/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino deleted file mode 100644 index a997c132d..000000000 --- a/lib/MQTT/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino +++ /dev/null @@ -1,62 +0,0 @@ -// This example uses an Arduino Uno together with -// an Ethernet Shield to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED}; -byte ip[] = {192, 168, 1, 177}; // <- change to match your network - -EthernetClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void setup() { - Serial.begin(115200); - Ethernet.begin(mac, ip); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - client.begin("broker.shiftr.io", net); - client.onMessage(messageReceived); - - connect(); -} - -void connect() { - Serial.print("connecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} diff --git a/lib/MQTT/examples/ArduinoWiFi101/ArduinoWiFi101.ino b/lib/MQTT/examples/ArduinoWiFi101/ArduinoWiFi101.ino deleted file mode 100644 index e7a25fee5..000000000 --- a/lib/MQTT/examples/ArduinoWiFi101/ArduinoWiFi101.ino +++ /dev/null @@ -1,70 +0,0 @@ -// This example uses an Arduino/Genuino Zero together with -// a WiFi101 Shield or a MKR1000 to connect to shiftr.io. -// -// IMPORTANT: This example uses the new WiFi101 library. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Gilberto Conti -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - client.begin("broker.shiftr.io", net); - client.onMessage(messageReceived); - - connect(); -} - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} diff --git a/lib/MQTT/examples/ArduinoWiFi101_SSL/ArduinoWiFi101_SSL.ino b/lib/MQTT/examples/ArduinoWiFi101_SSL/ArduinoWiFi101_SSL.ino deleted file mode 100644 index 7d68a9ca5..000000000 --- a/lib/MQTT/examples/ArduinoWiFi101_SSL/ArduinoWiFi101_SSL.ino +++ /dev/null @@ -1,75 +0,0 @@ -// This example uses an Arduino/Genuino Zero together with -// a WiFi101 Shield or a MKR1000 to connect to shiftr.io. -// -// IMPORTANT: This example uses the new WiFi101 library. -// -// IMPORTANT: You need to install/update the SSL certificates first: -// https://github.com/arduino-libraries/WiFi101-FirmwareUpdater#to-update-ssl-certificates -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Gilberto Conti -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiSSLClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - // - // MQTT brokers usually use port 8883 for secure connections. - client.begin("broker.shiftr.io", 8883, net); - client.onMessage(messageReceived); - - connect(); -} - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} diff --git a/lib/MQTT/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino b/lib/MQTT/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino deleted file mode 100644 index 52f8e273c..000000000 --- a/lib/MQTT/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino +++ /dev/null @@ -1,68 +0,0 @@ -// This example uses an Arduino Uno together with -// a WiFi Shield to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - client.begin("broker.shiftr.io", net); - client.onMessage(messageReceived); - - connect(); -} - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} diff --git a/lib/MQTT/examples/ArduinoYun/ArduinoYun.ino b/lib/MQTT/examples/ArduinoYun/ArduinoYun.ino deleted file mode 100644 index ea9e0ad55..000000000 --- a/lib/MQTT/examples/ArduinoYun/ArduinoYun.ino +++ /dev/null @@ -1,60 +0,0 @@ -// This example uses an Arduino Yun or a Yun-Shield -// and the MQTTClient to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include -#include - -BridgeClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void setup() { - Bridge.begin(); - Serial.begin(115200); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - client.begin("broker.shiftr.io", net); - client.onMessage(messageReceived); - - connect(); -} - -void connect() { - Serial.print("connecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} diff --git a/lib/MQTT/examples/ArduinoYun_SSL/ArduinoYun_SSL.ino b/lib/MQTT/examples/ArduinoYun_SSL/ArduinoYun_SSL.ino deleted file mode 100644 index baf98c3d5..000000000 --- a/lib/MQTT/examples/ArduinoYun_SSL/ArduinoYun_SSL.ino +++ /dev/null @@ -1,62 +0,0 @@ -// This example uses an Arduino Yun or a Yun-Shield -// and the MQTTClient to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include -#include - -BridgeSSLClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void setup() { - Bridge.begin(); - Serial.begin(115200); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - // - // MQTT brokers usually use port 8883 for secure connections. - client.begin("broker.shiftr.io", 8883, net); - client.onMessage(messageReceived); - - connect(); -} - -void connect() { - Serial.print("connecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void loop() { - client.loop(); - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} diff --git a/lib/MQTT/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino b/lib/MQTT/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino deleted file mode 100644 index 09533e90e..000000000 --- a/lib/MQTT/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino +++ /dev/null @@ -1,69 +0,0 @@ -// This example uses an ESP32 Development Board -// to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiClient net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - client.begin("broker.shiftr.io", net); - client.onMessage(messageReceived); - - connect(); -} - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void loop() { - client.loop(); - delay(10); // <- fixes some issues with WiFi stability - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} diff --git a/lib/MQTT/examples/ESP32DevelopmentBoard_SSL/ESP32DevelopmentBoard_SSL.ino b/lib/MQTT/examples/ESP32DevelopmentBoard_SSL/ESP32DevelopmentBoard_SSL.ino deleted file mode 100644 index f8bdd5fb4..000000000 --- a/lib/MQTT/examples/ESP32DevelopmentBoard_SSL/ESP32DevelopmentBoard_SSL.ino +++ /dev/null @@ -1,71 +0,0 @@ -// This example uses an ESP32 Development Board -// to connect to shiftr.io. -// -// You can check on your device after a successful -// connection here: https://shiftr.io/try. -// -// by Joël Gähwiler -// https://github.com/256dpi/arduino-mqtt - -#include -#include - -const char ssid[] = "ssid"; -const char pass[] = "pass"; - -WiFiClientSecure net; -MQTTClient client; - -unsigned long lastMillis = 0; - -void setup() { - Serial.begin(115200); - WiFi.begin(ssid, pass); - - // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported by Arduino. - // You need to set the IP address directly. - // - // MQTT brokers usually use port 8883 for secure connections. - client.begin("broker.shiftr.io", 8883, net); - client.onMessage(messageReceived); - - connect(); -} - -void connect() { - Serial.print("checking wifi..."); - while (WiFi.status() != WL_CONNECTED) { - Serial.print("."); - delay(1000); - } - - Serial.print("\nconnecting..."); - while (!client.connect("arduino", "try", "try")) { - Serial.print("."); - delay(1000); - } - - Serial.println("\nconnected!"); - - client.subscribe("/hello"); - // client.unsubscribe("/hello"); -} - -void loop() { - client.loop(); - delay(10); // <- fixes some issues with WiFi stability - - if (!client.connected()) { - connect(); - } - - // publish a message roughly every second. - if (millis() - lastMillis > 1000) { - lastMillis = millis(); - client.publish("/hello", "world"); - } -} - -void messageReceived(String &topic, String &payload) { - Serial.println("incoming: " + topic + " - " + payload); -} diff --git a/lib/MQTT/library.properties b/lib/MQTT/library.properties deleted file mode 100644 index 1bad69be2..000000000 --- a/lib/MQTT/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=MQTT -version=2.2.2 -author=Joel Gaehwiler -maintainer=Joel Gaehwiler -sentence=MQTT library for Arduino -paragraph=This library bundles the lwmqtt client and adds a thin wrapper to get an Arduino like API. -category=Communication -url=https://github.com/256dpi/arduino-mqtt -architectures=* diff --git a/lib/MQTT/src/MQTTClient.h b/lib/MQTT/src/MQTTClient.h deleted file mode 100644 index a20170d6b..000000000 --- a/lib/MQTT/src/MQTTClient.h +++ /dev/null @@ -1,371 +0,0 @@ -#ifndef MQTT_CLIENT_H -#define MQTT_CLIENT_H - -#include -#include -#include - -#include "system.h" - -class MQTTClient; - -typedef void (*MQTTClientCallbackSimple)(String &topic, String &payload); -typedef void (*MQTTClientCallbackAdvanced)(MQTTClient *client, char topic[], char bytes[], int length); - -typedef struct { - MQTTClient *client = nullptr; - MQTTClientCallbackSimple simple = nullptr; - MQTTClientCallbackAdvanced advanced = nullptr; -} MQTTClientCallback; - -static void MQTTClientHandler(lwmqtt_client_t *client, void *ref, lwmqtt_string_t topic, lwmqtt_message_t message) { - // get callback - auto cb = (MQTTClientCallback *)ref; - - // null terminate topic - char terminated_topic[topic.len + 1]; - memcpy(terminated_topic, topic.data, topic.len); - terminated_topic[topic.len] = '\0'; - - // null terminate payload if available - if (message.payload != nullptr) { - message.payload[message.payload_len] = '\0'; - } - - // call the advanced callback and return if available - if (cb->advanced != nullptr) { - cb->advanced(cb->client, terminated_topic, (char *)message.payload, (int)message.payload_len); - return; - } - - // return if simple callback is not set - if (cb->simple == nullptr) { - return; - } - - // create topic string - String str_topic = String(terminated_topic); - - // create payload string - String str_payload; - if (message.payload != nullptr) { - str_payload = String((const char *)message.payload); - } - - // call simple callback - cb->simple(str_topic, str_payload); -} - -class MQTTClient { - private: - size_t bufSize = 0; - uint8_t *readBuf = nullptr; - uint8_t *writeBuf = nullptr; - - uint16_t keepAlive = 10; - bool cleanSession = true; - uint32_t timeout = 1000; - - Client *netClient = nullptr; - const char *hostname = nullptr; - int port = 0; - lwmqtt_will_t will = lwmqtt_default_will; - bool hasWill = false; - MQTTClientCallback callback; - - lwmqtt_arduino_network_t network = {nullptr}; - lwmqtt_arduino_timer_t timer1 = {0}; - lwmqtt_arduino_timer_t timer2 = {0}; - lwmqtt_client_t client = {0}; - - void* parent; - - bool _connected = false; - lwmqtt_return_code_t _returnCode = (lwmqtt_return_code_t)0; - lwmqtt_err_t _lastError = (lwmqtt_err_t)0; - - public: - explicit MQTTClient(int bufSize = 128) { - this->bufSize = (size_t)bufSize; - this->readBuf = (uint8_t *)malloc((size_t)bufSize + 1); - this->writeBuf = (uint8_t *)malloc((size_t)bufSize); - } - - ~MQTTClient() { - free(this->readBuf); - free(this->writeBuf); - } - - void* getParent() { - return parent; - } - - void setParent(void* _parent) { - parent = _parent; - } - - void begin(const char hostname[], Client &client) { this->begin(hostname, 1883, client); } - - void begin(const char hostname[], int port, Client &client) { - // set config - this->hostname = hostname; - this->port = port; - this->netClient = &client; - - // initialize client - lwmqtt_init(&this->client, this->writeBuf, this->bufSize, this->readBuf, this->bufSize); - - // set timers - lwmqtt_set_timers(&this->client, &this->timer1, &this->timer2, lwmqtt_arduino_timer_set, lwmqtt_arduino_timer_get); - - // set network - lwmqtt_set_network(&this->client, &this->network, lwmqtt_arduino_network_read, lwmqtt_arduino_network_write); - - // set callback - lwmqtt_set_callback(&this->client, (void *)&this->callback, MQTTClientHandler); - } - - void onMessage(MQTTClientCallbackSimple cb) { - // set callback - this->callback.client = this; - this->callback.simple = cb; - this->callback.advanced = nullptr; - } - - void onMessageAdvanced(MQTTClientCallbackAdvanced cb) { - // set callback - this->callback.client = this; - this->callback.simple = nullptr; - this->callback.advanced = cb; - } - - void setHost(const char hostname[]) { this->setHost(hostname, 1883); } - - void setHost(const char hostname[], int port) { - this->hostname = hostname; - this->port = port; - } - - void setWill(const char topic[]) { this->setWill(topic, ""); } - - void setWill(const char topic[], const char payload[]) { this->setWill(topic, payload, false, 0); } - - void setWill(const char topic[], const char payload[], bool retained, int qos) { - this->hasWill = true; - this->will.topic = lwmqtt_string(topic); - this->will.payload = lwmqtt_string(payload); - this->will.retained = retained; - this->will.qos = (lwmqtt_qos_t)qos; - } - - void clearWill() { this->hasWill = false; } - - void setOptions(int keepAlive, bool cleanSession, int timeout) { - this->keepAlive = (uint16_t)keepAlive; - this->cleanSession = cleanSession; - this->timeout = (uint32_t)timeout; - } - - boolean connect(const char clientId[]) { return this->connect(clientId, nullptr, nullptr); } - - boolean connect(const char clientId[], const char username[]) { return this->connect(clientId, username, nullptr); } - - boolean connect(const char clientId[], const char username[], const char password[]) { - // close left open connection if still connected - if (this->connected()) { - this->close(); - } - - // save client - this->network.client = this->netClient; - - // connect to host - if (this->netClient->connect(this->hostname, (uint16_t)this->port) < 0) { - return false; - } - - // prepare options - lwmqtt_options_t options = lwmqtt_default_options; - options.keep_alive = this->keepAlive; - options.clean_session = this->cleanSession; - options.client_id = lwmqtt_string(clientId); - - // set username and password if available - if (username != nullptr) { - options.username = lwmqtt_string(username); - - if (password != nullptr) { - options.password = lwmqtt_string(password); - } - } - - // prepare will reference - lwmqtt_will_t *will = nullptr; - if (this->hasWill) { - will = &this->will; - } - - // connect to broker - this->_lastError = lwmqtt_connect(&this->client, options, will, &this->_returnCode, this->timeout); - if (this->_lastError != LWMQTT_SUCCESS) { - return this->close(); - } - - // set flag - this->_connected = true; - - return true; - } - - boolean publish(const String &topic) { return this->publish(topic.c_str(), ""); } - - boolean publish(const char topic[]) { return this->publish(topic, ""); } - - boolean publish(const String &topic, const String &payload) { return this->publish(topic.c_str(), payload.c_str()); } - - boolean publish(const String &topic, const String &payload, bool retained, int qos) { - return this->publish(topic.c_str(), payload.c_str(), retained, qos); - } - - boolean publish(const char topic[], const String &payload) { return this->publish(topic, payload.c_str()); } - - boolean publish(const char topic[], const String &payload, bool retained, int qos) { - return this->publish(topic, payload.c_str(), retained, qos); - } - - boolean publish(const char topic[], const char payload[]) { - return this->publish(topic, (char *)payload, (int)strlen(payload)); - } - - boolean publish(const char topic[], const char payload[], bool retained, int qos) { - return this->publish(topic, (char *)payload, (int)strlen(payload), retained, qos); - } - - boolean publish(const char topic[], const char payload[], int length) { - return this->publish(topic, payload, length, false, 0); - } - - boolean publish(const char topic[], const char payload[], int length, bool retained, int qos) { - // return immediately if not connected - if (!this->connected()) { - return false; - } - - // prepare message - lwmqtt_message_t message = lwmqtt_default_message; - message.payload = (uint8_t *)payload; - message.payload_len = (size_t)length; - message.retained = retained; - message.qos = lwmqtt_qos_t(qos); - - // publish message - this->_lastError = lwmqtt_publish(&this->client, lwmqtt_string(topic), message, this->timeout); - if (this->_lastError != LWMQTT_SUCCESS) { - return this->close(); - } - - return true; - } - - boolean subscribe(const String &topic) { return this->subscribe(topic.c_str()); } - - boolean subscribe(const String &topic, int qos) { return this->subscribe(topic.c_str(), qos); } - - boolean subscribe(const char topic[]) { return this->subscribe(topic, 0); } - - boolean subscribe(const char topic[], int qos) { - // return immediately if not connected - if (!this->connected()) { - return false; - } - - // subscribe to topic - this->_lastError = lwmqtt_subscribe_one(&this->client, lwmqtt_string(topic), (lwmqtt_qos_t)qos, this->timeout); - if (this->_lastError != LWMQTT_SUCCESS) { - return this->close(); - } - - return true; - } - - boolean unsubscribe(const String &topic) { return this->unsubscribe(topic.c_str()); } - - boolean unsubscribe(const char topic[]) { - // return immediately if not connected - if (!this->connected()) { - return false; - } - - // unsubscribe from topic - this->_lastError = lwmqtt_unsubscribe_one(&this->client, lwmqtt_string(topic), this->timeout); - if (this->_lastError != LWMQTT_SUCCESS) { - return this->close(); - } - - return true; - } - - boolean loop() { - // return immediately if not connected - if (!this->connected()) { - return false; - } - - // get available bytes on the network - auto available = (size_t)this->netClient->available(); - - // yield if data is available - if (available > 0) { - this->_lastError = lwmqtt_yield(&this->client, available, this->timeout); - if (this->_lastError != LWMQTT_SUCCESS) { - return this->close(); - } - } - - // keep the connection alive - this->_lastError = lwmqtt_keep_alive(&this->client, this->timeout); - if (this->_lastError != LWMQTT_SUCCESS) { - return this->close(); - } - - return true; - } - - boolean connected() { - // a client is connected if the network is connected, a client is available and - // the connection has been properly initiated - return this->netClient != nullptr && this->netClient->connected() == 1 && this->_connected; - } - - lwmqtt_err_t lastError() { return this->_lastError; } - - lwmqtt_return_code_t returnCode() { return this->_returnCode; } - - boolean disconnect() { - // return immediately if not connected anymore - if (!this->connected()) { - return false; - } - - // cleanly disconnect - this->_lastError = lwmqtt_disconnect(&this->client, this->timeout); - - // close - this->close(); - - return this->_lastError == LWMQTT_SUCCESS; - } - - private: - boolean close() { - // set flag - this->_connected = false; - - // close network - this->netClient->stop(); - - return false; - } -}; - -#endif diff --git a/lib/MQTT/src/lwmqtt/client.c b/lib/MQTT/src/lwmqtt/client.c deleted file mode 100644 index 8775ab6e1..000000000 --- a/lib/MQTT/src/lwmqtt/client.c +++ /dev/null @@ -1,615 +0,0 @@ -#include "packet.h" - -void lwmqtt_init(lwmqtt_client_t *client, uint8_t *write_buf, size_t write_buf_size, uint8_t *read_buf, - size_t read_buf_size) { - client->last_packet_id = 1; - client->keep_alive_interval = 0; - client->pong_pending = false; - - client->write_buf = write_buf; - client->write_buf_size = write_buf_size; - client->read_buf = read_buf; - client->read_buf_size = read_buf_size; - - client->callback = NULL; - client->callback_ref = NULL; - - client->network = NULL; - client->network_read = NULL; - client->network_write = NULL; - - client->keep_alive_timer = NULL; - client->command_timer = NULL; - client->timer_set = NULL; - client->timer_get = NULL; -} - -void lwmqtt_set_network(lwmqtt_client_t *client, void *ref, lwmqtt_network_read_t read, lwmqtt_network_write_t write) { - client->network = ref; - client->network_read = read; - client->network_write = write; -} - -void lwmqtt_set_timers(lwmqtt_client_t *client, void *keep_alive_timer, void *command_timer, lwmqtt_timer_set_t set, - lwmqtt_timer_get_t get) { - client->keep_alive_timer = keep_alive_timer; - client->command_timer = command_timer; - client->timer_set = set; - client->timer_get = get; - - client->timer_set(client->keep_alive_timer, 0); - client->timer_set(client->command_timer, 0); -} - -void lwmqtt_set_callback(lwmqtt_client_t *client, void *ref, lwmqtt_callback_t cb) { - client->callback_ref = ref; - client->callback = cb; -} - -static uint16_t lwmqtt_get_next_packet_id(lwmqtt_client_t *client) { - // check overflow - if (client->last_packet_id == 65535) { - client->last_packet_id = 1; - return 1; - } - - // increment packet id - client->last_packet_id++; - - return client->last_packet_id; -} - -static lwmqtt_err_t lwmqtt_read_from_network(lwmqtt_client_t *client, size_t offset, size_t len) { - // check read buffer capacity - if (client->read_buf_size < offset + len) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // prepare counter - size_t read = 0; - - // read while data is missing - while (read < len) { - // check remaining time - int32_t remaining_time = client->timer_get(client->command_timer); - if (remaining_time <= 0) { - return LWMQTT_NETWORK_TIMEOUT; - } - - // read - size_t partial_read = 0; - lwmqtt_err_t err = client->network_read(client->network, client->read_buf + offset + read, len - read, - &partial_read, (uint32_t)remaining_time); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // increment counter - read += partial_read; - } - - return LWMQTT_SUCCESS; -} - -static lwmqtt_err_t lwmqtt_write_to_network(lwmqtt_client_t *client, size_t offset, size_t len) { - // prepare counter - size_t written = 0; - - // write while data is left - while (written < len) { - // check remaining time - int32_t remaining_time = client->timer_get(client->command_timer); - if (remaining_time <= 0) { - return LWMQTT_NETWORK_TIMEOUT; - } - - // write - size_t partial_write = 0; - lwmqtt_err_t err = client->network_write(client->network, client->write_buf + offset + written, len - written, - &partial_write, (uint32_t)remaining_time); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // increment counter - written += partial_write; - } - - return LWMQTT_SUCCESS; -} - -static lwmqtt_err_t lwmqtt_read_packet_in_buffer(lwmqtt_client_t *client, size_t *read, - lwmqtt_packet_type_t *packet_type) { - // preset packet type - *packet_type = LWMQTT_NO_PACKET; - - // read or wait for header byte - lwmqtt_err_t err = lwmqtt_read_from_network(client, 0, 1); - if (err == LWMQTT_NETWORK_TIMEOUT) { - // this is ok as no data has been read at all - return LWMQTT_SUCCESS; - } else if (err != LWMQTT_SUCCESS) { - return err; - } - - // detect packet type - err = lwmqtt_detect_packet_type(client->read_buf, 1, packet_type); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // prepare variables - size_t len = 0; - uint32_t rem_len = 0; - - do { - // adjust len - len++; - - // read next byte - err = lwmqtt_read_from_network(client, len, 1); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // attempt to detect remaining length - err = lwmqtt_detect_remaining_length(client->read_buf + 1, len, &rem_len); - } while (err == LWMQTT_BUFFER_TOO_SHORT); - - // check final error - if (err != LWMQTT_SUCCESS) { - return err; - } - - // read the rest of the buffer if needed - if (rem_len > 0) { - err = lwmqtt_read_from_network(client, 1 + len, rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - } - - // adjust counter - *read += 1 + len + rem_len; - - return LWMQTT_SUCCESS; -} - -static lwmqtt_err_t lwmqtt_send_packet_in_buffer(lwmqtt_client_t *client, size_t length) { - // write to network - lwmqtt_err_t err = lwmqtt_write_to_network(client, 0, length); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // reset keep alive timer - client->timer_set(client->keep_alive_timer, client->keep_alive_interval); - - return LWMQTT_SUCCESS; -} - -static lwmqtt_err_t lwmqtt_cycle(lwmqtt_client_t *client, size_t *read, lwmqtt_packet_type_t *packet_type) { - // read next packet from the network - lwmqtt_err_t err = lwmqtt_read_packet_in_buffer(client, read, packet_type); - if (err != LWMQTT_SUCCESS) { - return err; - } else if (*packet_type == LWMQTT_NO_PACKET) { - return LWMQTT_SUCCESS; - } - - switch (*packet_type) { - // handle publish packets - case LWMQTT_PUBLISH_PACKET: { - // decode publish packet - bool dup; - uint16_t packet_id; - lwmqtt_string_t topic; - lwmqtt_message_t msg; - err = lwmqtt_decode_publish(client->read_buf, client->read_buf_size, &dup, &packet_id, &topic, &msg); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // call callback if set - if (client->callback != NULL) { - client->callback(client, client->callback_ref, topic, msg); - } - - // break early on qos zero - if (msg.qos == LWMQTT_QOS0) { - break; - } - - // define ack packet - lwmqtt_packet_type_t ack_type = LWMQTT_NO_PACKET; - if (msg.qos == LWMQTT_QOS1) { - ack_type = LWMQTT_PUBACK_PACKET; - } else if (msg.qos == LWMQTT_QOS2) { - ack_type = LWMQTT_PUBREC_PACKET; - } - - // encode ack packet - size_t len; - err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, ack_type, false, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send ack packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - break; - } - - // handle pubrec packets - case LWMQTT_PUBREC_PACKET: { - // decode pubrec packet - bool dup; - uint16_t packet_id; - err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_PUBREC_PACKET, &dup, &packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // encode pubrel packet - size_t len; - err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, LWMQTT_PUBREL_PACKET, 0, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send pubrel packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - break; - } - - // handle pubrel packets - case LWMQTT_PUBREL_PACKET: { - // decode pubrec packet - bool dup; - uint16_t packet_id; - err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_PUBREL_PACKET, &dup, &packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // encode pubcomp packet - size_t len; - err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, LWMQTT_PUBCOMP_PACKET, 0, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send pubcomp packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - break; - } - - // handle pingresp packets - case LWMQTT_PINGRESP_PACKET: { - // set flag - client->pong_pending = false; - - break; - } - - // handle all other packets - default: { break; } - } - - return LWMQTT_SUCCESS; -} - -static lwmqtt_err_t lwmqtt_cycle_until(lwmqtt_client_t *client, lwmqtt_packet_type_t *packet_type, size_t available, - lwmqtt_packet_type_t needle) { - // prepare counter - size_t read = 0; - - // loop until timeout has been reached - do { - // do one cycle - lwmqtt_err_t err = lwmqtt_cycle(client, &read, packet_type); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // return when one packet has been successfully read when no availability has been given - if (needle == LWMQTT_NO_PACKET && available == 0) { - return LWMQTT_SUCCESS; - } - - // otherwise check if needle has been found - if (*packet_type == needle) { - return LWMQTT_SUCCESS; - } - } while (client->timer_get(client->command_timer) > 0 && (available == 0 || read < available)); - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_yield(lwmqtt_client_t *client, size_t available, uint32_t timeout) { - // set command timer - client->timer_set(client->command_timer, timeout); - - // cycle until timeout has been reached - lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; - lwmqtt_err_t err = lwmqtt_cycle_until(client, &packet_type, available, LWMQTT_NO_PACKET); - if (err != LWMQTT_SUCCESS) { - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_connect(lwmqtt_client_t *client, lwmqtt_options_t options, lwmqtt_will_t *will, - lwmqtt_return_code_t *return_code, uint32_t timeout) { - // set command timer - client->timer_set(client->command_timer, timeout); - - // save keep alive interval (take 75% to be a little earlier than actually needed) - client->keep_alive_interval = (uint32_t)(options.keep_alive * 750); - - // set keep alive timer - client->timer_set(client->keep_alive_timer, client->keep_alive_interval); - - // reset pong pending flag - client->pong_pending = false; - - // encode connect packet - size_t len; - lwmqtt_err_t err = lwmqtt_encode_connect(client->write_buf, client->write_buf_size, &len, options, will); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // wait for connack packet - lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; - err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_CONNACK_PACKET); - if (err != LWMQTT_SUCCESS) { - return err; - } else if (packet_type != LWMQTT_CONNACK_PACKET) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // decode connack packet - bool session_present; - err = lwmqtt_decode_connack(client->read_buf, client->read_buf_size, &session_present, return_code); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // return error if connection was not accepted - if (*return_code != LWMQTT_CONNECTION_ACCEPTED) { - return LWMQTT_CONNECTION_DENIED; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_subscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, lwmqtt_qos_t *qos, - uint32_t timeout) { - // set command timer - client->timer_set(client->command_timer, timeout); - - // encode subscribe packet - size_t len; - lwmqtt_err_t err = lwmqtt_encode_subscribe(client->write_buf, client->write_buf_size, &len, - lwmqtt_get_next_packet_id(client), count, topic_filter, qos); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // wait for suback packet - lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; - err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_SUBACK_PACKET); - if (err != LWMQTT_SUCCESS) { - return err; - } else if (packet_type != LWMQTT_SUBACK_PACKET) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // decode packet - int suback_count = 0; - lwmqtt_qos_t granted_qos[count]; - uint16_t packet_id; - err = lwmqtt_decode_suback(client->read_buf, client->read_buf_size, &packet_id, count, &suback_count, granted_qos); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check suback codes - for (int i = 0; i < suback_count; i++) { - if (granted_qos[i] == LWMQTT_QOS_FAILURE) { - return LWMQTT_FAILED_SUBSCRIPTION; - } - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_subscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, lwmqtt_qos_t qos, - uint32_t timeout) { - return lwmqtt_subscribe(client, 1, &topic_filter, &qos, timeout); -} - -lwmqtt_err_t lwmqtt_unsubscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, uint32_t timeout) { - // set command timer - client->timer_set(client->command_timer, timeout); - - // encode unsubscribe packet - size_t len; - lwmqtt_err_t err = lwmqtt_encode_unsubscribe(client->write_buf, client->write_buf_size, &len, - lwmqtt_get_next_packet_id(client), count, topic_filter); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send unsubscribe packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // wait for unsuback packet - lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; - err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_UNSUBACK_PACKET); - if (err != LWMQTT_SUCCESS) { - return err; - } else if (packet_type != LWMQTT_UNSUBACK_PACKET) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // decode unsuback packet - bool dup; - uint16_t packet_id; - err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_UNSUBACK_PACKET, &dup, &packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_unsubscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, uint32_t timeout) { - return lwmqtt_unsubscribe(client, 1, &topic_filter, timeout); -} - -lwmqtt_err_t lwmqtt_publish(lwmqtt_client_t *client, lwmqtt_string_t topic, lwmqtt_message_t message, - uint32_t timeout) { - // set command timer - client->timer_set(client->command_timer, timeout); - - // add packet id if at least qos 1 - uint16_t packet_id = 0; - if (message.qos == LWMQTT_QOS1 || message.qos == LWMQTT_QOS2) { - packet_id = lwmqtt_get_next_packet_id(client); - } - - // encode publish packet - size_t len = 0; - lwmqtt_err_t err = - lwmqtt_encode_publish(client->write_buf, client->write_buf_size, &len, 0, packet_id, topic, message); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // immediately return on qos zero - if (message.qos == LWMQTT_QOS0) { - return LWMQTT_SUCCESS; - } - - // define ack packet - lwmqtt_packet_type_t ack_type = LWMQTT_NO_PACKET; - if (message.qos == LWMQTT_QOS1) { - ack_type = LWMQTT_PUBACK_PACKET; - } else if (message.qos == LWMQTT_QOS2) { - ack_type = LWMQTT_PUBCOMP_PACKET; - } - - // wait for ack packet - lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET; - err = lwmqtt_cycle_until(client, &packet_type, 0, ack_type); - if (err != LWMQTT_SUCCESS) { - return err; - } else if (packet_type != ack_type) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // decode ack packet - bool dup; - err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, ack_type, &dup, &packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_disconnect(lwmqtt_client_t *client, uint32_t timeout) { - // set command timer - client->timer_set(client->command_timer, timeout); - - // encode disconnect packet - size_t len; - lwmqtt_err_t err = lwmqtt_encode_zero(client->write_buf, client->write_buf_size, &len, LWMQTT_DISCONNECT_PACKET); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send disconnected packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_keep_alive(lwmqtt_client_t *client, uint32_t timeout) { - // set command timer - client->timer_set(client->command_timer, timeout); - - // return immediately if keep alive interval is zero - if (client->keep_alive_interval == 0) { - return LWMQTT_SUCCESS; - } - - // return immediately if no ping is due - if (client->timer_get(client->keep_alive_timer) > 0) { - return LWMQTT_SUCCESS; - } - - // a ping is due - - // fail immediately if a pong is already pending - if (client->pong_pending) { - return LWMQTT_PONG_TIMEOUT; - } - - // encode pingreq packet - size_t len; - lwmqtt_err_t err = lwmqtt_encode_zero(client->write_buf, client->write_buf_size, &len, LWMQTT_PINGREQ_PACKET); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // send packet - err = lwmqtt_send_packet_in_buffer(client, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // set flag - client->pong_pending = true; - - return LWMQTT_SUCCESS; -} diff --git a/lib/MQTT/src/lwmqtt/helpers.c b/lib/MQTT/src/lwmqtt/helpers.c deleted file mode 100644 index 8b9d37e5b..000000000 --- a/lib/MQTT/src/lwmqtt/helpers.c +++ /dev/null @@ -1,251 +0,0 @@ -#include - -#include "helpers.h" - -uint8_t lwmqtt_read_bits(uint8_t byte, int pos, int num) { - return (byte & (uint8_t)((~(0xFF << num)) << pos)) >> pos; -} - -void lwmqtt_write_bits(uint8_t *byte, uint8_t value, int pos, int num) { - *byte = (*byte & ~(uint8_t)((~(0xFF << num)) << pos)) | (value << pos); -} - -lwmqtt_err_t lwmqtt_read_data(uint8_t **buf, const uint8_t *buf_end, uint8_t **data, size_t len) { - // check zero length - if (len == 0) { - *data = NULL; - return LWMQTT_SUCCESS; - } - - // check buffer size - if ((size_t)(buf_end - (*buf)) < len) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // read data - *data = *buf; - - // advance pointer - *buf += len; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_write_data(uint8_t **buf, const uint8_t *buf_end, uint8_t *data, size_t len) { - // check zero length - if (len == 0) { - return LWMQTT_SUCCESS; - } - - // check buffer size - if ((size_t)(buf_end - (*buf)) < len) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // write data - memcpy(*buf, data, len); - - // advance pointer - *buf += len; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_read_num(uint8_t **buf, const uint8_t *buf_end, uint16_t *num) { - // check buffer size - if ((size_t)(buf_end - (*buf)) < 2) { - *num = 0; - return LWMQTT_BUFFER_TOO_SHORT; - } - - // read two byte integer - *num = (uint16_t)256 * (*buf)[0] + (*buf)[1]; - - // adjust pointer - *buf += 2; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_write_num(uint8_t **buf, const uint8_t *buf_end, uint16_t num) { - // check buffer size - if ((size_t)(buf_end - (*buf)) < 2) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // write bytes - (*buf)[0] = (uint8_t)(num / 256); - (*buf)[1] = (uint8_t)(num % 256); - - // adjust pointer - *buf += 2; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_read_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t *str) { - // read length - uint16_t len; - lwmqtt_err_t err = lwmqtt_read_num(buf, buf_end, &len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // read data - err = lwmqtt_read_data(buf, buf_end, (uint8_t **)&str->data, len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // set length - str->len = len; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_write_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t str) { - // write string length - lwmqtt_err_t err = lwmqtt_write_num(buf, buf_end, str.len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write data - err = lwmqtt_write_data(buf, buf_end, (uint8_t *)str.data, str.len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_read_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t *byte) { - // check buffer size - if ((size_t)(buf_end - (*buf)) < 1) { - *byte = 0; - return LWMQTT_BUFFER_TOO_SHORT; - } - - // read byte - *byte = (*buf)[0]; - - // adjust pointer - *buf += 1; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_write_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t byte) { - // check buffer size - if ((size_t)(buf_end - (*buf)) < 1) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // write byte - (*buf)[0] = byte; - - // adjust pointer - *buf += 1; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_varnum_length(uint32_t varnum, int *len) { - if (varnum < 128) { - *len = 1; - return LWMQTT_SUCCESS; - } else if (varnum < 16384) { - *len = 2; - return LWMQTT_SUCCESS; - } else if (varnum < 2097151) { - *len = 3; - return LWMQTT_SUCCESS; - } else if (varnum < 268435455) { - *len = 4; - return LWMQTT_SUCCESS; - } else { - *len = 0; - return LWMQTT_VARNUM_OVERFLOW; - } -} - -lwmqtt_err_t lwmqtt_read_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t *varnum) { - // prepare last byte - uint8_t byte; - - // prepare multiplier - uint32_t multiplier = 1; - - // prepare length - size_t len = 0; - - // initialize number - *varnum = 0; - - // decode variadic number - do { - // increment length - len++; - - // return error if buffer is to small - if ((size_t)(buf_end - (*buf)) < len) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // return error if the length has overflowed - if (len > 4) { - return LWMQTT_VARNUM_OVERFLOW; - } - - // read byte - byte = (*buf)[len - 1]; - - // add byte to number - *varnum += (byte & 127) * multiplier; - - // increase multiplier - multiplier *= 128; - } while ((byte & 128) != 0); - - // adjust pointer - *buf += len; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_write_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t varnum) { - // init len counter - size_t len = 0; - - // encode variadic number - do { - // check overflow - if (len == 4) { - return LWMQTT_VARNUM_OVERFLOW; - } - - // return error if buffer is to small - if ((size_t)(buf_end - (*buf)) < len + 1) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // calculate current byte - uint8_t byte = (uint8_t)(varnum % 128); - - // change remaining length - varnum /= 128; - - // set the top bit of this byte if there are more to encode - if (varnum > 0) { - byte |= 0x80; - } - - // write byte - (*buf)[len++] = byte; - } while (varnum > 0); - - // adjust pointer - *buf += len; - - return LWMQTT_SUCCESS; -} diff --git a/lib/MQTT/src/lwmqtt/helpers.h b/lib/MQTT/src/lwmqtt/helpers.h deleted file mode 100644 index 978eaf4a5..000000000 --- a/lib/MQTT/src/lwmqtt/helpers.h +++ /dev/null @@ -1,137 +0,0 @@ -#ifndef LWMQTT_HELPERS_H -#define LWMQTT_HELPERS_H - -#include "lwmqtt.h" - -/** - * Reads bits from a byte. - * - * @param byte - The byte to read from. - * @param pos - The position of the first bit. - * @param num - The number of bits to read. - * @return The read bits as a byte. - */ -uint8_t lwmqtt_read_bits(uint8_t byte, int pos, int num); - -/** - * Write bits to a byte. - * - * @param byte - The byte to write bits to. - * @param value - The bits to write as a byte. - * @param pos - The position of the first bit. - * @param num - The number of bits to write. - */ -void lwmqtt_write_bits(uint8_t *byte, uint8_t value, int pos, int num); - -/** - * Reads arbitrary data from the specified buffer. The pointer is incremented by bytes read. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param data - Pointer to beginning of data. - * @param len - The amount of data to read. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_read_data(uint8_t **buf, const uint8_t *buf_end, uint8_t **data, size_t len); - -/** - * Writes arbitrary data to the specified buffer. The pointer is incremented by the bytes written. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param data - Pointer to the to be written data. - * @param len - The amount of data to write. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_write_data(uint8_t **buf, const uint8_t *buf_end, uint8_t *data, size_t len); - -/** - * Reads two byte number from the specified buffer. The pointer is incremented by two. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param num - The read number. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_read_num(uint8_t **buf, const uint8_t *buf_end, uint16_t *num); - -/** - * Writes a two byte number to the specified buffer. The pointer is incremented by two. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param num - The number to write. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_write_num(uint8_t **buf, const uint8_t *buf_end, uint16_t num); - -/** - * Reads a string from the specified buffer into the passed object. The pointer is incremented by the bytes read. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param str - The object into which the data is to be read. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_read_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t *str); - -/** - * Writes a string to the specified buffer. The pointer is incremented by the bytes written. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param str - The string to write. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_write_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t str); - -/** - * Reads one byte from the buffer. The pointer is incremented by one. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param byte - The read byte. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_read_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t *byte); - -/** - * Writes one byte to the specified buffer. The pointer is incremented by one. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param byte - The byte to write. - * @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT. - */ -lwmqtt_err_t lwmqtt_write_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t byte); - -/** - * Returns the amount of bytes required by the variable number. - * - * @param varnum - The number to check. - * @param len - The required length; - * @return LWMQTT_SUCCESS or LWMQTT_VARNUM_OVERFLOW. - */ -lwmqtt_err_t lwmqtt_varnum_length(uint32_t varnum, int *len); - -/** - * Reads a variable number from the specified buffer. The pointer is incremented by the bytes read. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param varnum - The read varnum. - * @return LWMQTT_SUCCESS, LWMQTT_BUFFER_TOO_SHORT or LWMQTT_VARNUM_OVERFLOW. - */ -lwmqtt_err_t lwmqtt_read_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t *varnum); - -/** - * Writes a variable number to the specified buffer. The pointer is incremented by the bytes written. - * - * @param buf - Pointer to the buffer. - * @param buf_end - Pointer to the end of the buffer. - * @param varnum - The number to write. - * @return LWMQTT_SUCCESS, LWMQTT_BUFFER_TOO_SHORT or LWMQTT_VARNUM_OVERFLOW. - */ -lwmqtt_err_t lwmqtt_write_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t varnum); - -#endif diff --git a/lib/MQTT/src/lwmqtt/lwmqtt.h b/lib/MQTT/src/lwmqtt/lwmqtt.h deleted file mode 100644 index e63ad1004..000000000 --- a/lib/MQTT/src/lwmqtt/lwmqtt.h +++ /dev/null @@ -1,373 +0,0 @@ -#ifndef LWMQTT_H -#define LWMQTT_H - -#include -#include -#include - -/** - * The error type used by all exposed APIs. - */ -typedef enum { - LWMQTT_SUCCESS = 0, - LWMQTT_BUFFER_TOO_SHORT = -1, - LWMQTT_VARNUM_OVERFLOW = -2, - LWMQTT_NETWORK_FAILED_CONNECT = -3, - LWMQTT_NETWORK_TIMEOUT = -4, - LWMQTT_NETWORK_FAILED_READ = -5, - LWMQTT_NETWORK_FAILED_WRITE = -6, - LWMQTT_REMAINING_LENGTH_OVERFLOW = -7, - LWMQTT_REMAINING_LENGTH_MISMATCH = -8, - LWMQTT_MISSING_OR_WRONG_PACKET = -9, - LWMQTT_CONNECTION_DENIED = -10, - LWMQTT_FAILED_SUBSCRIPTION = -11, - LWMQTT_SUBACK_ARRAY_OVERFLOW = -12, - LWMQTT_PONG_TIMEOUT = -13, -} lwmqtt_err_t; - -/** - * A common string object. - */ -typedef struct { - uint16_t len; - char *data; -} lwmqtt_string_t; - -/** - * The initializer for string objects. - */ -#define lwmqtt_default_string \ - { 0, NULL } - -/** - * Returns a string object for the passed C string. - * - * @param str - The C string. - * @return A string object. - */ -lwmqtt_string_t lwmqtt_string(const char *str); - -/** - * Compares a string object to a C string. - * - * @param a - The string object to compare. - * @param b - The C string to compare. - * @return Similarity e.g. strcmp(). - */ -int lwmqtt_strcmp(lwmqtt_string_t a, const char *b); - -/** - * The available QOS levels. - */ -typedef enum { LWMQTT_QOS0 = 0, LWMQTT_QOS1 = 1, LWMQTT_QOS2 = 2, LWMQTT_QOS_FAILURE = 128 } lwmqtt_qos_t; - -/** - * The message object used to publish and receive messages. - */ -typedef struct { - lwmqtt_qos_t qos; - bool retained; - uint8_t *payload; - size_t payload_len; -} lwmqtt_message_t; - -/** - * The initializer for message objects. - */ -#define lwmqtt_default_message \ - { LWMQTT_QOS0, false, NULL, 0 } - -/** - * Forward declaration of the client object. - */ -typedef struct lwmqtt_client_t lwmqtt_client_t; - -/** - * The callback used to read from a network object. - * - * The callbacks is expected to read up to the amount of bytes in to the passed buffer. It should block the specified - * timeout and wait for more incoming data. - * - * @param ref - A custom reference. - * @param buf - The buffer. - * @param len - The length of the buffer. - * @param read - Variable that must be set with the amount of read bytes. - * @param timeout - The timeout in milliseconds for the operation. - */ -typedef lwmqtt_err_t (*lwmqtt_network_read_t)(void *ref, uint8_t *buf, size_t len, size_t *read, uint32_t timeout); - -/** - * The callback used to write to a network object. - * - * The callback is expected to write up to the amount of bytes from the passed buffer. It should wait up to the - * specified timeout to write the specified data to the network. - * - * @param ref - A custom reference. - * @param buf - The buffer. - * @param len - The length of the buffer. - * @param sent - Variable that must be set with the amount of written bytes. - * @param timeout - The timeout in milliseconds for the operation. - */ -typedef lwmqtt_err_t (*lwmqtt_network_write_t)(void *ref, uint8_t *buf, size_t len, size_t *sent, uint32_t timeout); - -/** - * The callback used to set a timer. - * - * @param ref - A custom reference. - * @param timeout - The amount of milliseconds until the deadline. - */ -typedef void (*lwmqtt_timer_set_t)(void *ref, uint32_t timeout); - -/** - * The callback used to get a timers value. - * - * @param - A custom reference. - * @return The amount of milliseconds until the deadline. May return negative numbers if the deadline has been reached. - */ -typedef int32_t (*lwmqtt_timer_get_t)(void *ref); - -/** - * The callback used to forward incoming messages. - * - * Note: The callback is mostly executed because of a call to lwmqtt_yield() that processes incoming messages. However, - * it is possible that the callback is also executed during a call to lwmqtt_subscribe(), lwmqtt_publish() or - * lwmqtt_unsubscribe() if incoming messages are received between the required acknowledgements. It is therefore not - * recommended to call any further lwmqtt methods in the callback as this might result in weird call stacks. The - * callback should place the received messages in a queue and dispatch them after the caller has returned. - */ -typedef void (*lwmqtt_callback_t)(lwmqtt_client_t *client, void *ref, lwmqtt_string_t str, lwmqtt_message_t msg); - -/** - * The client object. - */ -struct lwmqtt_client_t { - uint16_t last_packet_id; - uint32_t keep_alive_interval; - bool pong_pending; - - size_t write_buf_size, read_buf_size; - uint8_t *write_buf, *read_buf; - - lwmqtt_callback_t callback; - void *callback_ref; - - void *network; - lwmqtt_network_read_t network_read; - lwmqtt_network_write_t network_write; - - void *keep_alive_timer; - void *command_timer; - lwmqtt_timer_set_t timer_set; - lwmqtt_timer_get_t timer_get; -}; - -/** - * Will initialize the specified client object. - * - * @param client - The client object. - * @param write_buf - The write buffer. - * @param write_buf_size - The write buffer size. - * @param read_buf - The read buffer. - * @param read_buf_size - The read buffer size. - */ -void lwmqtt_init(lwmqtt_client_t *client, uint8_t *write_buf, size_t write_buf_size, uint8_t *read_buf, - size_t read_buf_size); - -/** - * Will set the network reference and callbacks for this client object. - * - * @param client - The client object. - * @param ref - The reference to the network object. - * @param read - The read callback. - * @param write - The write callback. - */ -void lwmqtt_set_network(lwmqtt_client_t *client, void *ref, lwmqtt_network_read_t read, lwmqtt_network_write_t write); - -/** - * Will set the timer references and callbacks for this client object. - * - * @param client - The client object. - * @param keep_alive_timer - The reference to the keep alive timer. - * @param command_timer - The reference to the command timer. - * @param set - The set callback. - * @param get - The get callback. - */ -void lwmqtt_set_timers(lwmqtt_client_t *client, void *keep_alive_timer, void *command_timer, lwmqtt_timer_set_t set, - lwmqtt_timer_get_t get); - -/** - * Will set the callback used to receive incoming messages. - * - * @param client - The client object. - * @param ref - A custom reference that will passed to the callback. - * @param cb - The callback to be called. - */ -void lwmqtt_set_callback(lwmqtt_client_t *client, void *ref, lwmqtt_callback_t cb); - -/** - * The object defining the last will of a client. - */ -typedef struct { - lwmqtt_string_t topic; - lwmqtt_qos_t qos; - bool retained; - lwmqtt_string_t payload; -} lwmqtt_will_t; - -/** - * The default initializer for the will object. - */ -#define lwmqtt_default_will \ - { lwmqtt_default_string, LWMQTT_QOS0, false, lwmqtt_default_string } - -/** - * The object containing the connection options for a client. - */ -typedef struct { - lwmqtt_string_t client_id; - uint16_t keep_alive; - bool clean_session; - lwmqtt_string_t username; - lwmqtt_string_t password; -} lwmqtt_options_t; - -/** - * The default initializer for the options object. - */ -#define lwmqtt_default_options \ - { lwmqtt_default_string, 60, true, lwmqtt_default_string, lwmqtt_default_string } - -/** - * The available return codes transported by the connack packet. - */ -typedef enum { - LWMQTT_CONNECTION_ACCEPTED = 0, - LWMQTT_UNACCEPTABLE_PROTOCOL = 1, - LWMQTT_IDENTIFIER_REJECTED = 2, - LWMQTT_SERVER_UNAVAILABLE = 3, - LWMQTT_BAD_USERNAME_OR_PASSWORD = 4, - LWMQTT_NOT_AUTHORIZED = 5, - LWMQTT_UNKNOWN_RETURN_CODE = 6 -} lwmqtt_return_code_t; - -/** - * Will send a connect packet and wait for a connack response and set the return code. - * - * The network object must already be connected to the server. An error is returned if the broker rejects the - * connection. - * - * @param client - The client object. - * @param options - The options object. - * @param will - The will object. - * @param return_code - The variable that will receive the return code. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_connect(lwmqtt_client_t *client, lwmqtt_options_t options, lwmqtt_will_t *will, - lwmqtt_return_code_t *return_code, uint32_t timeout); - -/** - * Will send a publish packet and wait for all acks to complete. - * - * Note: The message callback might be called with incoming messages as part of this call. - * - * @param client - The client object. - * @param topic - The topic. - * @param message - The message. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_publish(lwmqtt_client_t *client, lwmqtt_string_t topic, lwmqtt_message_t msg, uint32_t timeout); - -/** - * Will send a subscribe packet with multiple topic filters plus QOS levels and wait for the suback to complete. - * - * Note: The message callback might be called with incoming messages as part of this call. - * - * @param client - The client object. - * @param count - The number of topic filters and QOS levels. - * @param topic_filter - The list of topic filters. - * @param qos - The list of QOS levels. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_subscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, lwmqtt_qos_t *qos, - uint32_t timeout); - -/** - * Will send a subscribe packet with a single topic filter plus QOS level and wait for the suback to complete. - * - * Note: The message callback might be called with incoming messages as part of this call. - * - * @param client - The client object. - * @param topic_filter - The topic filter. - * @param qos - The QOS level. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_subscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, lwmqtt_qos_t qos, - uint32_t timeout); - -/** - * Will send an unsubscribe packet with multiple topic filters and wait for the unsuback to complete. - * - * Note: The message callback might be called with incoming messages as part of this call. - * - * @param client - The client object. - * @param count - The number of topic filters. - * @param topic_filter - The topic filter. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_unsubscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, uint32_t timeout); - -/** - * Will send an unsubscribe packet with a single topic filter and wait for the unsuback to complete. - * - * Note: The message callback might be called with incoming messages as part of this call. - * - * @param client - The client object. - * @param topic_filter - The topic filter. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_unsubscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, uint32_t timeout); - -/** - * Will send a disconnect packet and finish the client. - * - * @param client - The client object. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_disconnect(lwmqtt_client_t *client, uint32_t timeout); - -/** - * Will yield control to the client and receive incoming packets from the network. - * - * Applications may peek on the network if there is data available to read before calling yield and potentially block - * until the timeout is reached. Furthermore, applications may specify the amount of bytes available to read in order - * to constrain the yield to only receive packets that are already inflight. - * - * If no availability data is given the yield will return after one packet has been successfully read or the deadline - * has been reached but no single bytes has been received. - * - * Note: The message callback might be called with incoming messages as part of this call. - * - * @param client - The client object. - * @param available - The available bytes to read. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_yield(lwmqtt_client_t *client, size_t available, uint32_t timeout); - -/** - * Will yield control to the client to keep the connection alive. - * - * @param client - The client object. - * @param timeout - The command timeout. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_keep_alive(lwmqtt_client_t *client, uint32_t timeout); - -#endif // LWMQTT_H diff --git a/lib/MQTT/src/lwmqtt/packet.c b/lib/MQTT/src/lwmqtt/packet.c deleted file mode 100644 index 5ed579fd2..000000000 --- a/lib/MQTT/src/lwmqtt/packet.c +++ /dev/null @@ -1,742 +0,0 @@ -#include "packet.h" - -lwmqtt_err_t lwmqtt_detect_packet_type(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type) { - // set default packet type - *packet_type = LWMQTT_NO_PACKET; - - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // prepare header - uint8_t header; - - // read header - lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // get packet type - *packet_type = (lwmqtt_packet_type_t)lwmqtt_read_bits(header, 4, 4); - - // check if packet type is correct and can be received - switch (*packet_type) { - case LWMQTT_CONNACK_PACKET: - case LWMQTT_PUBLISH_PACKET: - case LWMQTT_PUBACK_PACKET: - case LWMQTT_PUBREC_PACKET: - case LWMQTT_PUBREL_PACKET: - case LWMQTT_PUBCOMP_PACKET: - case LWMQTT_SUBACK_PACKET: - case LWMQTT_UNSUBACK_PACKET: - case LWMQTT_PINGRESP_PACKET: - return LWMQTT_SUCCESS; - default: - *packet_type = LWMQTT_NO_PACKET; - return LWMQTT_MISSING_OR_WRONG_PACKET; - } -} - -lwmqtt_err_t lwmqtt_detect_remaining_length(uint8_t *buf, size_t buf_len, uint32_t *rem_len) { - // prepare pointer - uint8_t *ptr = buf; - - // attempt to decode remaining length - lwmqtt_err_t err = lwmqtt_read_varnum(&ptr, buf + buf_len, rem_len); - if (err == LWMQTT_VARNUM_OVERFLOW) { - *rem_len = 0; - return LWMQTT_REMAINING_LENGTH_OVERFLOW; - } else if (err != LWMQTT_SUCCESS) { - *rem_len = 0; - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_encode_connect(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_options_t options, - lwmqtt_will_t *will) { - // prepare pointers - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // fixed header is 10 - uint32_t rem_len = 10; - - // add client id to remaining length - rem_len += options.client_id.len + 2; - - // add will if present to remaining length - if (will != NULL) { - rem_len += will->topic.len + 2 + will->payload.len + 2; - } - - // add username if present to remaining length - if (options.username.len > 0) { - rem_len += options.username.len + 2; - - // add password if present to remaining length - if (options.password.len > 0) { - rem_len += options.password.len + 2; - } - } - - // check remaining length length - int rem_len_len; - lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); - if (err == LWMQTT_VARNUM_OVERFLOW) { - return LWMQTT_REMAINING_LENGTH_OVERFLOW; - } - - // prepare header - uint8_t header = 0; - lwmqtt_write_bits(&header, LWMQTT_CONNECT_PACKET, 4, 4); - - // write header - err = lwmqtt_write_byte(&buf_ptr, buf_end, header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write remaining length - err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write version string - err = lwmqtt_write_string(&buf_ptr, buf_end, lwmqtt_string("MQTT")); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write version number - err = lwmqtt_write_byte(&buf_ptr, buf_end, 4); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // prepare flags - uint8_t flags = 0; - - // set clean session - lwmqtt_write_bits(&flags, (uint8_t)(options.clean_session), 1, 1); - - // set will flags if present - if (will != NULL) { - lwmqtt_write_bits(&flags, 1, 2, 1); - lwmqtt_write_bits(&flags, will->qos, 3, 2); - lwmqtt_write_bits(&flags, (uint8_t)(will->retained), 5, 1); - } - - // set username flag if present - if (options.username.len > 0) { - lwmqtt_write_bits(&flags, 1, 6, 1); - - // set password flag if present - if (options.password.len > 0) { - lwmqtt_write_bits(&flags, 1, 7, 1); - } - } - - // write flags - err = lwmqtt_write_byte(&buf_ptr, buf_end, flags); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write keep alive - err = lwmqtt_write_num(&buf_ptr, buf_end, options.keep_alive); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write client id - err = lwmqtt_write_string(&buf_ptr, buf_end, options.client_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write will if present - if (will != NULL) { - // write topic - err = lwmqtt_write_string(&buf_ptr, buf_end, will->topic); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write payload length - err = lwmqtt_write_num(&buf_ptr, buf_end, (uint16_t)will->payload.len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write payload - err = lwmqtt_write_data(&buf_ptr, buf_end, (uint8_t *)will->payload.data, will->payload.len); - if (err != LWMQTT_SUCCESS) { - return err; - } - } - - // write username if present - if (options.username.len > 0) { - err = lwmqtt_write_string(&buf_ptr, buf_end, options.username); - if (err != LWMQTT_SUCCESS) { - return err; - } - } - - // write password if present - if (options.username.len > 0 && options.password.len > 0) { - err = lwmqtt_write_string(&buf_ptr, buf_end, options.password); - if (err != LWMQTT_SUCCESS) { - return err; - } - } - - // set written length - *len = buf_ptr - buf; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_decode_connack(uint8_t *buf, size_t buf_len, bool *session_present, - lwmqtt_return_code_t *return_code) { - // prepare pointers - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // read header - uint8_t header; - lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check packet type - if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_CONNACK_PACKET) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // read remaining length - uint32_t rem_len; - err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check remaining length - if (rem_len != 2) { - return LWMQTT_REMAINING_LENGTH_MISMATCH; - } - - // read flags - uint8_t flags; - err = lwmqtt_read_byte(&buf_ptr, buf_end, &flags); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // read return code - uint8_t raw_return_code; - err = lwmqtt_read_byte(&buf_ptr, buf_end, &raw_return_code); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // get session present - *session_present = lwmqtt_read_bits(flags, 7, 1) == 1; - - // get return code - switch (raw_return_code) { - case 0: - *return_code = LWMQTT_CONNECTION_ACCEPTED; - break; - case 1: - *return_code = LWMQTT_UNACCEPTABLE_PROTOCOL; - break; - case 2: - *return_code = LWMQTT_IDENTIFIER_REJECTED; - break; - case 3: - *return_code = LWMQTT_SERVER_UNAVAILABLE; - break; - case 4: - *return_code = LWMQTT_BAD_USERNAME_OR_PASSWORD; - break; - case 5: - *return_code = LWMQTT_NOT_AUTHORIZED; - break; - default: - *return_code = LWMQTT_UNKNOWN_RETURN_CODE; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_encode_zero(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // write header - uint8_t header = 0; - lwmqtt_write_bits(&header, packet_type, 4, 4); - lwmqtt_err_t err = lwmqtt_write_byte(&buf_ptr, buf_end, header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write remaining length - err = lwmqtt_write_varnum(&buf_ptr, buf_end, 0); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // set length - *len = buf_ptr - buf; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_decode_ack(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t packet_type, bool *dup, - uint16_t *packet_id) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // read header - uint8_t header = 0; - lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check packet type - if (lwmqtt_read_bits(header, 4, 4) != packet_type) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // get dup - *dup = lwmqtt_read_bits(header, 3, 1) == 1; - - // read remaining length - uint32_t rem_len; - err = lwmqtt_read_varnum(&buf_ptr, buf + buf_len, &rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check remaining length - if (rem_len != 2) { - return LWMQTT_REMAINING_LENGTH_MISMATCH; - } - - // read packet id - err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_encode_ack(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type, bool dup, - uint16_t packet_id) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // prepare header - uint8_t header = 0; - - // set packet type - lwmqtt_write_bits(&header, packet_type, 4, 4); - - // set dup - lwmqtt_write_bits(&header, (uint8_t)(dup), 3, 1); - - // set qos - lwmqtt_write_bits(&header, (uint8_t)(packet_type == LWMQTT_PUBREL_PACKET ? LWMQTT_QOS1 : LWMQTT_QOS0), 1, 2); - - // write header - lwmqtt_err_t err = lwmqtt_write_byte(&buf_ptr, buf_end, header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write remaining length - err = lwmqtt_write_varnum(&buf_ptr, buf_end, 2); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write packet id - err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // set written length - *len = buf_ptr - buf; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_decode_publish(uint8_t *buf, size_t buf_len, bool *dup, uint16_t *packet_id, lwmqtt_string_t *topic, - lwmqtt_message_t *msg) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // read header - uint8_t header; - lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check packet type - if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_PUBLISH_PACKET) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // get dup - *dup = lwmqtt_read_bits(header, 3, 1) == 1; - - // get retained - msg->retained = lwmqtt_read_bits(header, 0, 1) == 1; - - // get qos - switch (lwmqtt_read_bits(header, 1, 2)) { - case 0: - msg->qos = LWMQTT_QOS0; - break; - case 1: - msg->qos = LWMQTT_QOS1; - break; - case 2: - msg->qos = LWMQTT_QOS2; - break; - default: - msg->qos = LWMQTT_QOS0; - break; - } - - // read remaining length - uint32_t rem_len; - err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check remaining length (topic length) - if (rem_len < 2) { - return LWMQTT_REMAINING_LENGTH_MISMATCH; - } - - // check buffer capacity - if ((uint32_t)(buf_end - buf_ptr) < rem_len) { - return LWMQTT_BUFFER_TOO_SHORT; - } - - // reset buf end - buf_end = buf_ptr + rem_len; - - // read topic - err = lwmqtt_read_string(&buf_ptr, buf_end, topic); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // read packet id if qos is at least 1 - if (msg->qos > 0) { - err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - } else { - *packet_id = 0; - } - - // set payload length - msg->payload_len = buf_end - buf_ptr; - - // read payload - err = lwmqtt_read_data(&buf_ptr, buf_end, &msg->payload, buf_end - buf_ptr); - if (err != LWMQTT_SUCCESS) { - return err; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_encode_publish(uint8_t *buf, size_t buf_len, size_t *len, bool dup, uint16_t packet_id, - lwmqtt_string_t topic, lwmqtt_message_t msg) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // calculate remaining length - uint32_t rem_len = 2 + topic.len + (uint32_t)msg.payload_len; - if (msg.qos > 0) { - rem_len += 2; - } - - // check remaining length length - int rem_len_len; - lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); - if (err == LWMQTT_VARNUM_OVERFLOW) { - return LWMQTT_REMAINING_LENGTH_OVERFLOW; - } - - // prepare header - uint8_t header = 0; - - // set packet type - lwmqtt_write_bits(&header, LWMQTT_PUBLISH_PACKET, 4, 4); - - // set dup - lwmqtt_write_bits(&header, (uint8_t)(dup), 3, 1); - - // set qos - lwmqtt_write_bits(&header, msg.qos, 1, 2); - - // set retained - lwmqtt_write_bits(&header, (uint8_t)(msg.retained), 0, 1); - - // write header - err = lwmqtt_write_byte(&buf_ptr, buf_end, header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write remaining length - err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write topic - err = lwmqtt_write_string(&buf_ptr, buf_end, topic); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write packet id if qos is at least 1 - if (msg.qos > 0) { - err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - } - - // write payload - err = lwmqtt_write_data(&buf_ptr, buf_end, msg.payload, msg.payload_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // set length - *len = buf_ptr - buf; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_encode_subscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, - lwmqtt_string_t *topic_filters, lwmqtt_qos_t *qos_levels) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // calculate remaining length - uint32_t rem_len = 2; - for (int i = 0; i < count; i++) { - rem_len += 2 + topic_filters[i].len + 1; - } - - // check remaining length length - int rem_len_len; - lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); - if (err == LWMQTT_VARNUM_OVERFLOW) { - return LWMQTT_REMAINING_LENGTH_OVERFLOW; - } - - // prepare header - uint8_t header = 0; - - // set packet type - lwmqtt_write_bits(&header, LWMQTT_SUBSCRIBE_PACKET, 4, 4); - - // set qos - lwmqtt_write_bits(&header, LWMQTT_QOS1, 1, 2); - - // write header - err = lwmqtt_write_byte(&buf_ptr, buf_end, header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write remaining length - err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write packet id - err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write all subscriptions - for (int i = 0; i < count; i++) { - // write topic - err = lwmqtt_write_string(&buf_ptr, buf_end, topic_filters[i]); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write qos level - err = lwmqtt_write_byte(&buf_ptr, buf_end, (uint8_t)qos_levels[i]); - if (err != LWMQTT_SUCCESS) { - return err; - } - } - - // set length - *len = buf_ptr - buf; - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_decode_suback(uint8_t *buf, size_t buf_len, uint16_t *packet_id, int max_count, int *count, - lwmqtt_qos_t *granted_qos_levels) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // read header - uint8_t header; - lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check packet type - if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_SUBACK_PACKET) { - return LWMQTT_MISSING_OR_WRONG_PACKET; - } - - // read remaining length - uint32_t rem_len; - err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // check remaining length (packet id + min. one suback code) - if (rem_len < 3) { - return LWMQTT_REMAINING_LENGTH_MISMATCH; - } - - // read packet id - err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // read all suback codes - for (*count = 0; *count < (int)rem_len - 2; (*count)++) { - // check max count - if (*count > max_count) { - return LWMQTT_SUBACK_ARRAY_OVERFLOW; - } - - // read qos level - uint8_t raw_qos_level; - err = lwmqtt_read_byte(&buf_ptr, buf_end, &raw_qos_level); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // set qos level - switch (raw_qos_level) { - case 0: - granted_qos_levels[*count] = LWMQTT_QOS0; - break; - case 1: - granted_qos_levels[*count] = LWMQTT_QOS1; - break; - case 2: - granted_qos_levels[*count] = LWMQTT_QOS2; - break; - default: - granted_qos_levels[*count] = LWMQTT_QOS_FAILURE; - break; - } - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_encode_unsubscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, - lwmqtt_string_t *topic_filters) { - // prepare pointer - uint8_t *buf_ptr = buf; - uint8_t *buf_end = buf + buf_len; - - // calculate remaining length - uint32_t rem_len = 2; - for (int i = 0; i < count; i++) { - rem_len += 2 + topic_filters[i].len; - } - - // check remaining length length - int rem_len_len; - lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len); - if (err == LWMQTT_VARNUM_OVERFLOW) { - return LWMQTT_REMAINING_LENGTH_OVERFLOW; - } - - // prepare header - uint8_t header = 0; - - // set packet type - lwmqtt_write_bits(&header, LWMQTT_UNSUBSCRIBE_PACKET, 4, 4); - - // set qos - lwmqtt_write_bits(&header, LWMQTT_QOS1, 1, 2); - - // write header - err = lwmqtt_write_byte(&buf_ptr, buf_end, header); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write remaining length - err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write packet id - err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id); - if (err != LWMQTT_SUCCESS) { - return err; - } - - // write topics - for (int i = 0; i < count; i++) { - err = lwmqtt_write_string(&buf_ptr, buf_end, topic_filters[i]); - if (err != LWMQTT_SUCCESS) { - return err; - } - } - - // set length - *len = buf_ptr - buf; - - return LWMQTT_SUCCESS; -} diff --git a/lib/MQTT/src/lwmqtt/packet.h b/lib/MQTT/src/lwmqtt/packet.h deleted file mode 100644 index 387a18f8b..000000000 --- a/lib/MQTT/src/lwmqtt/packet.h +++ /dev/null @@ -1,185 +0,0 @@ -#ifndef LWMQTT_PACKET_H -#define LWMQTT_PACKET_H - -#include "helpers.h" - -/** - * The available packet types. - */ -typedef enum { - LWMQTT_NO_PACKET = 0, - LWMQTT_CONNECT_PACKET = 1, - LWMQTT_CONNACK_PACKET, - LWMQTT_PUBLISH_PACKET, - LWMQTT_PUBACK_PACKET, - LWMQTT_PUBREC_PACKET, - LWMQTT_PUBREL_PACKET, - LWMQTT_PUBCOMP_PACKET, - LWMQTT_SUBSCRIBE_PACKET, - LWMQTT_SUBACK_PACKET, - LWMQTT_UNSUBSCRIBE_PACKET, - LWMQTT_UNSUBACK_PACKET, - LWMQTT_PINGREQ_PACKET, - LWMQTT_PINGRESP_PACKET, - LWMQTT_DISCONNECT_PACKET -} lwmqtt_packet_type_t; - -/** - * Will detect the packet type from the at least one byte long buffer. - * - * @param buf - The buffer from which the packet type will be detected. - * @param buf_len - The length of the specified buffer. - * @param packet_type - The packet type. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_detect_packet_type(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type); - -/** - * Will detect the remaining length form the at least on byte long buffer. - * - * It will return LWMQTT_BUFFER_TOO_SHORT if the buffer is to short and an additional byte should be read from the - * network. In case the remaining length is overflowed it will return LWMQTT_REMAINING_LENGTH_OVERFLOW. - * - * @param buf - The buffer from which the remaining length will be detected. - * @param buf_len - The length of the specified buffer. - * @param rem_len - The detected remaining length. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_detect_remaining_length(uint8_t *buf, size_t buf_len, uint32_t *rem_len); - -/** - * Encodes a connect packet into the supplied buffer. - * - * @param buf - The buffer into which the packet will be encoded. - * @param buf_len - The length of the specified buffer. - * @param len - The encoded length of the packet. - * @param options - The options to be used to build the connect packet. - * @param will - The last will and testament. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_encode_connect(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_options_t options, - lwmqtt_will_t *will); - -/** - * Decodes a connack packet from the supplied buffer. - * - * @param buf - The raw buffer data. - * @param buf_len - The length of the specified buffer. - * @param session_present - The session present flag. - * @param return_code - The return code. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_decode_connack(uint8_t *buf, size_t buf_len, bool *session_present, - lwmqtt_return_code_t *return_code); - -/** - * Encodes a zero (disconnect, pingreq) packet into the supplied buffer. - * - * @param buf - The buffer into which the packet will be encoded. - * @param buf_len - The length of the specified buffer. - * @param len - The encoded length of the packet. - * @param packet_type - The packets type. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_encode_zero(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type); - -/** - * Decodes an ack (puback, pubrec, pubrel, pubcomp, unsuback) packet from the supplied buffer. - * - * @param buf - The raw buffer data. - * @param buf_len - The length of the specified buffer. - * @param packet_type - The packet type. - * @param dup - The dup flag. - * @param packet_id - The packet id. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_decode_ack(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t packet_type, bool *dup, - uint16_t *packet_id); - -/** - * Encodes an ack (puback, pubrec, pubrel, pubcomp) packet into the supplied buffer. - * - * @param buf - The buffer into which the packet will be encoded. - * @param buf_len - The length of the specified buffer. - * @param len - The encoded length of the packet. - * @param packet_type - The packets type. - * @param dup - The dup flag. - * @param packet_id - The packet id. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_encode_ack(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type, bool dup, - uint16_t packet_id); - -/** - * Decodes a publish packet from the supplied buffer. - * - * @param buf - The raw buffer data. - * @param buf_len - The length of the specified buffer. - * @param dup - The dup flag. - * @param packet_id - The packet id. - * @param topic - The topic. - * @parma msg - The message. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_decode_publish(uint8_t *buf, size_t buf_len, bool *dup, uint16_t *packet_id, lwmqtt_string_t *topic, - lwmqtt_message_t *msg); - -/** - * Encodes a publish packet into the supplied buffer. - * - * @param buf - The buffer into which the packet will be encoded. - * @param buf_len - The length of the specified buffer. - * @param len - The encoded length of the packet. - * @param dup - The dup flag. - * @param packet_id - The packet id. - * @param topic - The topic. - * @param msg - The message. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_encode_publish(uint8_t *buf, size_t buf_len, size_t *len, bool dup, uint16_t packet_id, - lwmqtt_string_t topic, lwmqtt_message_t msg); - -/** - * Encodes a subscribe packet into the supplied buffer. - * - * @param buf - The buffer into which the packet will be encoded. - * @param buf_len - The length of the specified buffer. - * @param len - The encoded length of the packet. - * @param packet_id - The packet id. - * @param count - The number of members in the topic_filters and qos_levels array. - * @param topic_filters - The array of topic filter. - * @param qos_levels - The array of requested QoS levels. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_encode_subscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, - lwmqtt_string_t *topic_filters, lwmqtt_qos_t *qos_levels); - -/** - * Decodes a suback packet from the supplied buffer. - * - * @param buf - The raw buffer data. - * @param buf_len - The length of the specified buffer. - * @param packet_id - The packet id. - * @param max_count - The maximum number of members allowed in the granted_qos_levels array. - * @param count - The number of members in the granted_qos_levels array. - * @param granted_qos_levels - The granted QoS levels. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_decode_suback(uint8_t *buf, size_t buf_len, uint16_t *packet_id, int max_count, int *count, - lwmqtt_qos_t *granted_qos_levels); - -/** - * Encodes the supplied unsubscribe data into the supplied buffer, ready for sending - * - * @param buf - The buffer into which the packet will be encoded. - * @param buf_len - The length of the specified buffer. - * @param len - The encoded length of the packet. - * @param packet_id - The packet id. - * @param count - The number of members in the topic_filters array. - * @param topic_filters - The array of topic filters. - * @return An error value. - */ -lwmqtt_err_t lwmqtt_encode_unsubscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count, - lwmqtt_string_t *topic_filters); - -#endif // LWMQTT_PACKET_H diff --git a/lib/MQTT/src/lwmqtt/string.c b/lib/MQTT/src/lwmqtt/string.c deleted file mode 100644 index c27dc94e3..000000000 --- a/lib/MQTT/src/lwmqtt/string.c +++ /dev/null @@ -1,38 +0,0 @@ -#include - -#include "lwmqtt.h" - -lwmqtt_string_t lwmqtt_string(const char *str) { - // check for null - if (str == NULL) { - return (lwmqtt_string_t){0, NULL}; - } - - // get length - uint16_t len = (uint16_t)strlen(str); - - // check zero length - if (len == 0) { - return (lwmqtt_string_t){0, NULL}; - } - - return (lwmqtt_string_t){len, (char *)str}; -} - -int lwmqtt_strcmp(lwmqtt_string_t a, const char *b) { - // get string of b - lwmqtt_string_t b_str = lwmqtt_string(b); - - // return if both are zero length - if (a.len == 0 && b_str.len == 0) { - return 0; - } - - // return if lengths are different - if (a.len != b_str.len) { - return -1; - } - - // compare memory of same length - return strncmp(a.data, b_str.data, a.len); -} diff --git a/lib/MQTT/src/system.cpp b/lib/MQTT/src/system.cpp deleted file mode 100644 index 4d583b4f5..000000000 --- a/lib/MQTT/src/system.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include - -#include "system.h" - -void lwmqtt_arduino_timer_set(void *ref, uint32_t timeout) { - // cast timer reference - auto t = (lwmqtt_arduino_timer_t *)ref; - - // set future end time - t->end = (uint32_t)(millis() + timeout); -} - -int32_t lwmqtt_arduino_timer_get(void *ref) { - // cast timer reference - auto t = (lwmqtt_arduino_timer_t *)ref; - - // get difference to end time - return (int32_t)t->end - (int32_t)millis(); -} - -lwmqtt_err_t lwmqtt_arduino_network_read(void *ref, uint8_t *buffer, size_t len, size_t *read, uint32_t timeout) { - // cast network reference - auto n = (lwmqtt_arduino_network_t *)ref; - - // set timeout - n->client->setTimeout(timeout); - - // read bytes - *read = n->client->readBytes(buffer, len); - if (*read <= 0) { - return LWMQTT_NETWORK_FAILED_READ; - } - - return LWMQTT_SUCCESS; -} - -lwmqtt_err_t lwmqtt_arduino_network_write(void *ref, uint8_t *buffer, size_t len, size_t *sent, uint32_t timeout) { - // cast network reference - auto n = (lwmqtt_arduino_network_t *)ref; - - // write bytes - *sent = n->client->write(buffer, len); - if (*sent <= 0) { - return LWMQTT_NETWORK_FAILED_WRITE; - }; - - return LWMQTT_SUCCESS; -} diff --git a/lib/MQTT/src/system.h b/lib/MQTT/src/system.h deleted file mode 100644 index e51739fb8..000000000 --- a/lib/MQTT/src/system.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef LWMQTT_ARDUINO_H -#define LWMQTT_ARDUINO_H - -#include -#include - -extern "C" { -#include "lwmqtt/lwmqtt.h" -}; - -typedef struct { - uint32_t end; -} lwmqtt_arduino_timer_t; - -void lwmqtt_arduino_timer_set(void *ref, uint32_t timeout); - -int32_t lwmqtt_arduino_timer_get(void *ref); - -typedef struct { - Client *client; -} lwmqtt_arduino_network_t; - -lwmqtt_err_t lwmqtt_arduino_network_read(void *ref, uint8_t *buf, size_t len, size_t *read, uint32_t timeout); -lwmqtt_err_t lwmqtt_arduino_network_write(void *ref, uint8_t *buf, size_t len, size_t *sent, uint32_t timeout); - -#endif // LWMQTT_ARDUINO_H From ea5aefe408e506e532fac0a6a5fffbc87b37e324 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 16 Feb 2018 16:56:40 +0100 Subject: [PATCH 014/175] use external buffer on poll() --- ArduinoCloudThing.cpp | 15 ++++++--------- ArduinoCloudThing.h | 4 ++-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 0f32cc727..8ed209e4a 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -41,19 +41,14 @@ ArduinoCloudThing::ArduinoCloudThing() { } -bool ArduinoCloudThing::begin() { - -#ifdef TESTING_PROTOCOL - return true; -#endif +void ArduinoCloudThing::begin() { status = ON; addProperty(status, READ); } -void ArduinoCloudThing::publish(CborArray& object) { +int ArduinoCloudThing::publish(CborArray& object, uint8_t* data) { - uint8_t data[1024]; size_t size = object.encode(data, sizeof(data)); #ifdef TESTING_PROTOCOL @@ -64,9 +59,11 @@ void ArduinoCloudThing::publish(CborArray& object) { ArduinoCloudPropertyGeneric *p = list.get(i); p->updateShadow(); } + + return size; } -int ArduinoCloudThing::poll() { +int ArduinoCloudThing::poll(uint8_t* data) { // check if backing storage and cloud has diverged int diff = 0; @@ -76,7 +73,7 @@ int ArduinoCloudThing::poll() { CborBuffer buffer(1024); CborArray object = CborArray(buffer); compress(object, buffer); - publish(object); + diff = publish(object, data); } #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index e5abaad3c..d0abf7e55 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -185,10 +185,10 @@ class ArduinoCloudThing { ArduinoCloudPropertyGeneric& addPropertyReal(void* property, String name, permissionType permission); ArduinoCloudPropertyGeneric& addPropertyReal(String property, String name, permissionType permission); // poll should return > 0 if something has changed - int poll(); + int poll(uint8_t* data); private: - void publish(CborArray& object); + int publish(CborArray& object, uint8_t* data); void update(); int checkNewData(); From 3797b91a71a4b5644a1c865dbcc06d29ed78603d Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 16 Feb 2018 17:18:45 +0100 Subject: [PATCH 015/175] make decode() public --- ArduinoCloudThing.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index d0abf7e55..a1dcf9022 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -186,6 +186,7 @@ class ArduinoCloudThing { ArduinoCloudPropertyGeneric& addPropertyReal(String property, String name, permissionType permission); // poll should return > 0 if something has changed int poll(uint8_t* data); + void decode(uint8_t * payload, size_t length); private: int publish(CborArray& object, uint8_t* data); @@ -193,7 +194,6 @@ class ArduinoCloudThing { void update(); int checkNewData(); void compress(CborArray& object, CborBuffer& buffer); - void decode(uint8_t * payload, size_t length); bool exists(String &name); From 32e8d8d2364903de5c104c8d9b00ce838b78722c Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Wed, 21 Feb 2018 12:43:44 +0100 Subject: [PATCH 016/175] Make permission field composable too --- ArduinoCloudThing.cpp | 32 ++++++++++++++++---------------- ArduinoCloudThing.h | 37 +++++++++++++++++++++++++------------ 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 8ed209e4a..5da8c52d9 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -44,7 +44,7 @@ ArduinoCloudThing::ArduinoCloudThing() { void ArduinoCloudThing::begin() { status = ON; - addProperty(status, READ); + addProperty(status).readOnly(); } int ArduinoCloudThing::publish(CborArray& object, uint8_t* data) { @@ -107,39 +107,39 @@ int ArduinoCloudThing::checkNewData() { return counter; } -bool ArduinoCloudThing::exists(String &name) { +ArduinoCloudPropertyGeneric* ArduinoCloudThing::exists(String &name) { for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); if (p->getName() == name) { - return true; + return p; } } - return false; + return NULL; } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, String name, permissionType permission) { - if (exists(name)) { - return *(reinterpret_cast(NULL)); +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, String name) { + if (ArduinoCloudPropertyGeneric* p = exists(name)) { + return *p; } - ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name, permission); + ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); list.add(thing); return *(reinterpret_cast(thing)); } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, String name, permissionType permission) { - if (exists(name)) { - return *(reinterpret_cast(NULL)); +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, String name) { + if (ArduinoCloudPropertyGeneric* p = exists(name)) { + return *p; } - ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name, permission); + ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); list.add(thing); return *(reinterpret_cast(thing)); } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, String name, permissionType permission) { - if (exists(name)) { - return *(reinterpret_cast(NULL)); +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, String name) { + if (ArduinoCloudPropertyGeneric* p = exists(name)) { + return *p; } - ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name, permission); + ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); list.add(thing); return *(reinterpret_cast(thing)); } diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index a1dcf9022..842906db4 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -36,7 +36,9 @@ class ArduinoCloudPropertyGeneric virtual void append(CborObject& object) = 0; virtual String& getName() = 0; virtual void setName(String _name) = 0; - virtual void setTag(int _tag) = 0; + virtual ArduinoCloudPropertyGeneric& setTag(int _tag) = 0; + virtual ArduinoCloudPropertyGeneric& readOnly() = 0; + virtual ArduinoCloudPropertyGeneric& writeOnly() = 0; virtual int getTag() = 0; virtual void setPermission(permissionType _permission) = 0; virtual permissionType getPermission() = 0; @@ -52,8 +54,8 @@ template class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { public: - ArduinoCloudProperty(T& _property, String _name, permissionType _permission) : - property(_property), name(_name), permission(_permission) {} + ArduinoCloudProperty(T& _property, String _name) : + property(_property), name(_name) {} bool write(T value) { if (permission != READ) { @@ -81,8 +83,9 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric name = _name; } - void setTag(int _tag) { + ArduinoCloudPropertyGeneric& setTag(int _tag) { tag = _tag; + return *(reinterpret_cast(this)); } int getTag() { @@ -93,6 +96,16 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric permission = _permission; } + ArduinoCloudPropertyGeneric& readOnly() { + permission = READ; + return *(reinterpret_cast(this)); + } + + ArduinoCloudPropertyGeneric& writeOnly() { + permission = WRITE; + return *(reinterpret_cast(this)); + } + permissionType getPermission() { return permission; } @@ -142,7 +155,7 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric int tag = -1; long lastUpdated = 0; long updatePolicy = ON_CHANGE; - permissionType permission; + permissionType permission = READWRITE; static int tagIndex; }; @@ -172,18 +185,18 @@ inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { }; #ifndef addProperty -#define addProperty(prop, permission) addPropertyReal(prop, #prop, permission) +#define addProperty(prop) addPropertyReal(prop, #prop) #endif class ArduinoCloudThing { public: ArduinoCloudThing(); void begin(); - ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name, permissionType permission); - ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name, permissionType permission); - ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name, permissionType permission); - ArduinoCloudPropertyGeneric& addPropertyReal(void* property, String name, permissionType permission); - ArduinoCloudPropertyGeneric& addPropertyReal(String property, String name, permissionType permission); + ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name); + ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name); + ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name); + ArduinoCloudPropertyGeneric& addPropertyReal(void* property, String name); + ArduinoCloudPropertyGeneric& addPropertyReal(String property, String name); // poll should return > 0 if something has changed int poll(uint8_t* data); void decode(uint8_t * payload, size_t length); @@ -195,7 +208,7 @@ class ArduinoCloudThing { int checkNewData(); void compress(CborArray& object, CborBuffer& buffer); - bool exists(String &name); + ArduinoCloudPropertyGeneric* exists(String &name); bool status = OFF; char uuid[33]; From e18c499c3c990c512604b0dcb8d5a58ce1951562 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Thu, 22 Feb 2018 16:03:01 +0100 Subject: [PATCH 017/175] Fix write-only properties --- ArduinoCloudThing.cpp | 2 +- ArduinoCloudThing.h | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 5da8c52d9..567567bc5 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -87,7 +87,7 @@ void ArduinoCloudThing::compress(CborArray& object, CborBuffer& buffer) { for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); - if (p->shouldBeUpdated()) { + if (p->shouldBeUpdated() && p->canRead()) { CborObject child = CborObject(buffer); p->append(child); CborVariant variant = CborVariant(buffer, child); diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 842906db4..e26ed8917 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -45,6 +45,7 @@ class ArduinoCloudPropertyGeneric virtual bool newData() = 0; virtual bool shouldBeUpdated() = 0; virtual void updateShadow() = 0; + virtual bool canRead(); virtual ArduinoCloudPropertyGeneric& onUpdate(void(*fn)(void)) = 0; virtual ArduinoCloudPropertyGeneric& publishEvery(long seconds) = 0; void(*callback)(void) = NULL; @@ -58,7 +59,8 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric property(_property), name(_name) {} bool write(T value) { - if (permission != READ) { + /* permissions are intended as seen from cloud */ + if (permission & WRITE) { property = value; return true; } @@ -70,11 +72,16 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric } T read() { - if (permission != WRITE) { + /* permissions are intended as seen from cloud */ + if (permission & READ) { return property; } } + bool canRead() { + return (permission & READ); + } + String& getName() { return name; } @@ -118,6 +125,9 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric void appendValue(CborObject &cbor); void append(CborObject &cbor) { + if (!canRead()) { + return; + } if (tag != -1) { cbor.set("t", tag); } else { From 5e92f672ec8b6167f53491e5dca9d79f65fd954d Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Wed, 30 May 2018 15:52:39 +0200 Subject: [PATCH 018/175] setPermission should return ArduinoCloudPropertyGeneric& to allow composition --- ArduinoCloudThing.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index e26ed8917..1b5ca328f 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -40,7 +40,7 @@ class ArduinoCloudPropertyGeneric virtual ArduinoCloudPropertyGeneric& readOnly() = 0; virtual ArduinoCloudPropertyGeneric& writeOnly() = 0; virtual int getTag() = 0; - virtual void setPermission(permissionType _permission) = 0; + virtual ArduinoCloudPropertyGeneric& setPermission(permissionType _permission) = 0; virtual permissionType getPermission() = 0; virtual bool newData() = 0; virtual bool shouldBeUpdated() = 0; @@ -99,8 +99,9 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric return tag; } - void setPermission(permissionType _permission) { + ArduinoCloudPropertyGeneric& setPermission(permissionType _permission) { permission = _permission; + return *(reinterpret_cast(this)); } ArduinoCloudPropertyGeneric& readOnly() { From fccc594e42eac464daae80e6e7d2b69c90b6b4f0 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Wed, 30 May 2018 18:35:46 +0200 Subject: [PATCH 019/175] Improve stringification --- ArduinoCloudThing.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 1b5ca328f..c29e911ed 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -195,14 +195,15 @@ inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { cbor.set("s", property); }; -#ifndef addProperty -#define addProperty(prop) addPropertyReal(prop, #prop) -#endif +#define NAME_OF( v ) #v class ArduinoCloudThing { public: ArduinoCloudThing(); void begin(); + template ArduinoCloudPropertyGeneric& addProperty(T property) { + return addPropertyReal(property, NAME_OF(property)); + }; ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name); ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name); ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name); From 4f314369b60566d09e6d48f629c5d6cd15e51df1 Mon Sep 17 00:00:00 2001 From: Mattia Bertorello Date: Wed, 27 Jun 2018 18:26:00 +0200 Subject: [PATCH 020/175] Fixed memory leak in CborArray, fixed ssize_t, #define CBOR_INT_T int --- ArduinoCloudThing.cpp | 2 +- lib/ArduinoCbor/src/ArduinoCbor.h | 2 +- lib/ArduinoCbor/src/CborArray.cpp | 10 +++++++++- lib/ArduinoCbor/src/CborArray.h | 3 ++- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 567567bc5..7ce4813e5 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -49,7 +49,7 @@ void ArduinoCloudThing::begin() { int ArduinoCloudThing::publish(CborArray& object, uint8_t* data) { - size_t size = object.encode(data, sizeof(data)); + ssize_t size = object.encode(data, sizeof(data)); #ifdef TESTING_PROTOCOL decode(data, size); diff --git a/lib/ArduinoCbor/src/ArduinoCbor.h b/lib/ArduinoCbor/src/ArduinoCbor.h index a427492cd..cb6e0eb30 100644 --- a/lib/ArduinoCbor/src/ArduinoCbor.h +++ b/lib/ArduinoCbor/src/ArduinoCbor.h @@ -6,7 +6,7 @@ #include "cn-cbor/cn-cbor.h" #ifndef CBOR_INT_T -#define CBOR_INT_T long +#define CBOR_INT_T int #endif class CborArray; diff --git a/lib/ArduinoCbor/src/CborArray.cpp b/lib/ArduinoCbor/src/CborArray.cpp index 6fc19f7b2..ab04ba1be 100644 --- a/lib/ArduinoCbor/src/CborArray.cpp +++ b/lib/ArduinoCbor/src/CborArray.cpp @@ -13,6 +13,14 @@ CborArray::CborArray(CborBuffer& buffer, cn_cbor* raw) : buffer(buffer) { } } +CborArray::~CborArray() { + //Fixed memory leak + if (!raw->parent) { + cn_cbor_free(this->raw, &buffer.context); + } + +} + CborVariant CborArray::get(int index) { return CborVariant(buffer, cn_cbor_index(raw, index)); } @@ -31,6 +39,6 @@ void CborArray::add(CBOR_INT_T value) { add(CborVariant(buffer, value)); } -size_t CborArray::encode(uint8_t* data, size_t size) { +ssize_t CborArray::encode(uint8_t* data, size_t size) { return cn_cbor_encoder_write(data, 0, size, raw); } diff --git a/lib/ArduinoCbor/src/CborArray.h b/lib/ArduinoCbor/src/CborArray.h index 208861fb5..d0a755a7d 100644 --- a/lib/ArduinoCbor/src/CborArray.h +++ b/lib/ArduinoCbor/src/CborArray.h @@ -8,6 +8,7 @@ class CborArray { public: CborArray(CborBuffer& buffer, cn_cbor* raw=0); + ~CborArray(); CborVariant get(int index); @@ -15,7 +16,7 @@ class CborArray { void add(const char* value); void add(CBOR_INT_T value); - size_t encode(uint8_t* data, size_t size); + ssize_t encode(uint8_t* data, size_t size); protected: CborBuffer& buffer; From cd341c60fd7d40185d362b17dc88fad9865f1a54 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Wed, 27 Jun 2018 23:54:28 +0200 Subject: [PATCH 021/175] Remove evil NAME_OF macro --- ArduinoCloudThing.cpp | 2 +- ArduinoCloudThing.h | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 7ce4813e5..42c9dc068 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -44,7 +44,7 @@ ArduinoCloudThing::ArduinoCloudThing() { void ArduinoCloudThing::begin() { status = ON; - addProperty(status).readOnly(); + addPropertyReal(status, "status").readOnly(); } int ArduinoCloudThing::publish(CborArray& object, uint8_t* data) { diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index c29e911ed..b59b9485f 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -195,15 +195,10 @@ inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { cbor.set("s", property); }; -#define NAME_OF( v ) #v - class ArduinoCloudThing { public: ArduinoCloudThing(); void begin(); - template ArduinoCloudPropertyGeneric& addProperty(T property) { - return addPropertyReal(property, NAME_OF(property)); - }; ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name); ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name); ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name); From 2cf58e5fc7054e50955cdfccdefd6e5d207729d4 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Wed, 27 Jun 2018 23:55:05 +0200 Subject: [PATCH 022/175] Add printinfo debug function --- ArduinoCloudThing.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index b59b9485f..9ae12161b 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -45,7 +45,8 @@ class ArduinoCloudPropertyGeneric virtual bool newData() = 0; virtual bool shouldBeUpdated() = 0; virtual void updateShadow() = 0; - virtual bool canRead(); + virtual bool canRead() = 0; + virtual void printinfo() = 0; virtual ArduinoCloudPropertyGeneric& onUpdate(void(*fn)(void)) = 0; virtual ArduinoCloudPropertyGeneric& publishEvery(long seconds) = 0; void(*callback)(void) = NULL; @@ -67,6 +68,10 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric return false; } + void printinfo() { + Serial.println("name: " + name + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(permission)); + } + void updateShadow() { shadow_property = property; } From 57d63fd9589c591ca82243d40ef022deac9fbac3 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Wed, 27 Jun 2018 23:55:44 +0200 Subject: [PATCH 023/175] Remove hardcoded DEBUG_MEMORY define --- ArduinoCloudThing.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 9ae12161b..b68c4761a 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -7,7 +7,7 @@ #include "lib/ArduinoCbor/src/ArduinoCbor.h" //#define TESTING_PROTOCOL -#define DEBUG_MEMORY +//#define DEBUG_MEMORY #define USE_ARDUINO_CLOUD enum permissionType { From c226bdec441af96c674d9fa2a03052c3057f1726 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Thu, 28 Jun 2018 00:58:02 +0200 Subject: [PATCH 024/175] Pass external buffer size explicitely --- ArduinoCloudThing.cpp | 12 ++++++------ ArduinoCloudThing.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 42c9dc068..2809dc004 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -47,12 +47,12 @@ void ArduinoCloudThing::begin() { addPropertyReal(status, "status").readOnly(); } -int ArduinoCloudThing::publish(CborArray& object, uint8_t* data) { +int ArduinoCloudThing::publish(CborArray& object, uint8_t* data, size_t size) { - ssize_t size = object.encode(data, sizeof(data)); + ssize_t len = object.encode(data, size); #ifdef TESTING_PROTOCOL - decode(data, size); + decode(data, len); #endif for (int i = 0; i < list.size(); i++) { @@ -60,10 +60,10 @@ int ArduinoCloudThing::publish(CborArray& object, uint8_t* data) { p->updateShadow(); } - return size; + return len; } -int ArduinoCloudThing::poll(uint8_t* data) { +int ArduinoCloudThing::poll(uint8_t* data, size_t size) { // check if backing storage and cloud has diverged int diff = 0; @@ -73,7 +73,7 @@ int ArduinoCloudThing::poll(uint8_t* data) { CborBuffer buffer(1024); CborArray object = CborArray(buffer); compress(object, buffer); - diff = publish(object, data); + diff = publish(object, data, size); } #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index b68c4761a..da04da573 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -210,11 +210,11 @@ class ArduinoCloudThing { ArduinoCloudPropertyGeneric& addPropertyReal(void* property, String name); ArduinoCloudPropertyGeneric& addPropertyReal(String property, String name); // poll should return > 0 if something has changed - int poll(uint8_t* data); + int poll(uint8_t* data, size_t size); void decode(uint8_t * payload, size_t length); private: - int publish(CborArray& object, uint8_t* data); + int publish(CborArray& object, uint8_t* data, size_t size); void update(); int checkNewData(); From b60bafc9da40026c073cdda71cadcfd57f5a7a71 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Tue, 3 Jul 2018 11:54:52 +0200 Subject: [PATCH 025/175] Proposal: add minimumDelta API Accepts a pointer to the actual value --- ArduinoCloudThing.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index da04da573..0e36cb8c9 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -39,6 +39,7 @@ class ArduinoCloudPropertyGeneric virtual ArduinoCloudPropertyGeneric& setTag(int _tag) = 0; virtual ArduinoCloudPropertyGeneric& readOnly() = 0; virtual ArduinoCloudPropertyGeneric& writeOnly() = 0; + virtual ArduinoCloudPropertyGeneric& minimumDelta(void* delta) = 0; virtual int getTag() = 0; virtual ArduinoCloudPropertyGeneric& setPermission(permissionType _permission) = 0; virtual permissionType getPermission() = 0; @@ -119,6 +120,11 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric return *(reinterpret_cast(this)); } + ArduinoCloudPropertyGeneric& minimumDelta(void* delta) { + minDelta = *(T*)delta; + return *(reinterpret_cast(this)); + } + permissionType getPermission() { return permission; } @@ -150,7 +156,7 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric } bool newData() { - return (property != shadow_property); + return (property != shadow_property && abs(property - shadow_property) > minDelta ); } bool shouldBeUpdated() { @@ -171,6 +177,7 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric int tag = -1; long lastUpdated = 0; long updatePolicy = ON_CHANGE; + T minDelta = 0; permissionType permission = READWRITE; static int tagIndex; }; From 71ed28ed9621c2738bbcfd2e79860885900667e9 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Tue, 21 Aug 2018 19:08:05 +0200 Subject: [PATCH 026/175] Replace ArduinoCBOR with TinyCBOR --- lib/ArduinoCbor/.gitignore | 2 - lib/ArduinoCbor/README.md | 7 - lib/ArduinoCbor/examples/simple/simple.ino | 37 - lib/ArduinoCbor/library.json | 10 - lib/ArduinoCbor/library.properties | 9 - lib/ArduinoCbor/makefile | 37 - lib/ArduinoCbor/src/ArduinoCbor.h | 22 - lib/ArduinoCbor/src/CborArray.cpp | 44 - lib/ArduinoCbor/src/CborArray.h | 26 - lib/ArduinoCbor/src/CborBuffer.cpp | 46 - lib/ArduinoCbor/src/CborBuffer.h | 27 - lib/ArduinoCbor/src/CborObject.cpp | 47 - lib/ArduinoCbor/src/CborObject.h | 29 - lib/ArduinoCbor/src/CborVariant.cpp | 111 -- lib/ArduinoCbor/src/CborVariant.h | 37 - lib/ArduinoCbor/src/cbor.h | 118 -- lib/ArduinoCbor/src/cn-cbor/LICENSE | 21 - lib/ArduinoCbor/src/cn-cbor/README.md | 44 - lib/ArduinoCbor/src/cn-cbor/cn-cbor.c | 285 ---- lib/ArduinoCbor/src/cn-cbor/cn-cbor.h | 405 ----- lib/ArduinoCbor/src/cn-cbor/cn-create.c | 184 --- lib/ArduinoCbor/src/cn-cbor/cn-encoder.c | 307 ---- lib/ArduinoCbor/src/cn-cbor/cn-error.c | 13 - lib/ArduinoCbor/src/cn-cbor/cn-get.c | 62 - lib/tinycbor/cbor.dox | 123 ++ lib/tinycbor/cbor.h | 606 +++++++ lib/tinycbor/cborencoder.c | 645 ++++++++ .../cborencoder_close_container_checked.c | 57 + lib/tinycbor/cborerrorstrings.c | 182 +++ lib/tinycbor/cborinternal_p.h | 161 ++ lib/tinycbor/cborjson.h | 62 + lib/tinycbor/cborparser.c | 1430 +++++++++++++++++ lib/tinycbor/cborparser_dup_string.c | 119 ++ lib/tinycbor/cborpretty.c | 578 +++++++ lib/tinycbor/cborpretty_stdio.c | 87 + lib/tinycbor/cbortojson.c | 699 ++++++++ lib/tinycbor/cborvalidation.c | 666 ++++++++ lib/tinycbor/compilersupport_p.h | 205 +++ lib/tinycbor/open_memstream.c | 106 ++ lib/tinycbor/parsetags.pl | 116 ++ lib/tinycbor/src.pri | 16 + lib/tinycbor/tags.txt | 23 + lib/tinycbor/tinycbor-version.h | 3 + lib/tinycbor/tinycbor.pro | 6 + lib/tinycbor/utf8_p.h | 104 ++ 45 files changed, 5994 insertions(+), 1930 deletions(-) delete mode 100644 lib/ArduinoCbor/.gitignore delete mode 100644 lib/ArduinoCbor/README.md delete mode 100644 lib/ArduinoCbor/examples/simple/simple.ino delete mode 100644 lib/ArduinoCbor/library.json delete mode 100644 lib/ArduinoCbor/library.properties delete mode 100644 lib/ArduinoCbor/makefile delete mode 100644 lib/ArduinoCbor/src/ArduinoCbor.h delete mode 100644 lib/ArduinoCbor/src/CborArray.cpp delete mode 100644 lib/ArduinoCbor/src/CborArray.h delete mode 100644 lib/ArduinoCbor/src/CborBuffer.cpp delete mode 100644 lib/ArduinoCbor/src/CborBuffer.h delete mode 100644 lib/ArduinoCbor/src/CborObject.cpp delete mode 100644 lib/ArduinoCbor/src/CborObject.h delete mode 100644 lib/ArduinoCbor/src/CborVariant.cpp delete mode 100644 lib/ArduinoCbor/src/CborVariant.h delete mode 100644 lib/ArduinoCbor/src/cbor.h delete mode 100644 lib/ArduinoCbor/src/cn-cbor/LICENSE delete mode 100644 lib/ArduinoCbor/src/cn-cbor/README.md delete mode 100644 lib/ArduinoCbor/src/cn-cbor/cn-cbor.c delete mode 100644 lib/ArduinoCbor/src/cn-cbor/cn-cbor.h delete mode 100644 lib/ArduinoCbor/src/cn-cbor/cn-create.c delete mode 100644 lib/ArduinoCbor/src/cn-cbor/cn-encoder.c delete mode 100644 lib/ArduinoCbor/src/cn-cbor/cn-error.c delete mode 100644 lib/ArduinoCbor/src/cn-cbor/cn-get.c create mode 100644 lib/tinycbor/cbor.dox create mode 100644 lib/tinycbor/cbor.h create mode 100644 lib/tinycbor/cborencoder.c create mode 100644 lib/tinycbor/cborencoder_close_container_checked.c create mode 100644 lib/tinycbor/cborerrorstrings.c create mode 100644 lib/tinycbor/cborinternal_p.h create mode 100644 lib/tinycbor/cborjson.h create mode 100644 lib/tinycbor/cborparser.c create mode 100644 lib/tinycbor/cborparser_dup_string.c create mode 100644 lib/tinycbor/cborpretty.c create mode 100644 lib/tinycbor/cborpretty_stdio.c create mode 100644 lib/tinycbor/cbortojson.c create mode 100644 lib/tinycbor/cborvalidation.c create mode 100644 lib/tinycbor/compilersupport_p.h create mode 100644 lib/tinycbor/open_memstream.c create mode 100755 lib/tinycbor/parsetags.pl create mode 100644 lib/tinycbor/src.pri create mode 100644 lib/tinycbor/tags.txt create mode 100644 lib/tinycbor/tinycbor-version.h create mode 100644 lib/tinycbor/tinycbor.pro create mode 100644 lib/tinycbor/utf8_p.h diff --git a/lib/ArduinoCbor/.gitignore b/lib/ArduinoCbor/.gitignore deleted file mode 100644 index 8429a6996..000000000 --- a/lib/ArduinoCbor/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -build -gtest diff --git a/lib/ArduinoCbor/README.md b/lib/ArduinoCbor/README.md deleted file mode 100644 index 0cf100064..000000000 --- a/lib/ArduinoCbor/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# ArduinoCbor - -ArduinoCbor is a C++ library to handle CBOR data structures. -CBOR is a binary format based on the JSON data model. -Internal the [cn-cbor](https://github.com/cabo/cn-cbor) C library is used. - -## Examples diff --git a/lib/ArduinoCbor/examples/simple/simple.ino b/lib/ArduinoCbor/examples/simple/simple.ino deleted file mode 100644 index 209d8dfcd..000000000 --- a/lib/ArduinoCbor/examples/simple/simple.ino +++ /dev/null @@ -1,37 +0,0 @@ -#include - -void setup() { - Serial.begin(115200); - - while(!Serial) { - delay(10); - } -} - - -void loop() { - CborBuffer buffer(200); - CborObject object = CborObject(buffer); - - object.set("string", "value"); - object.set("integer", -1234); - - CborObject child = CborObject(buffer); - child.set("key", "value"); - object.set("object", child); - - CborArray array = CborArray(buffer); - array.add(-1234); - object.set("array", array); - - Serial.print("string value: "); - Serial.println(object.get("string").asString()); - - Serial.print("integer value: "); - Serial.println(object.get("integer").asInteger()); - - Serial.print("child string value: "); - Serial.println(object.get("object").asObject().get("key").asString()); - - delay(1000); -} diff --git a/lib/ArduinoCbor/library.json b/lib/ArduinoCbor/library.json deleted file mode 100644 index 8de25aef7..000000000 --- a/lib/ArduinoCbor/library.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "name": "ArduinoCbor", - "keywords": "cbor", - "description": "CBOR library for Arduino", - "repository": { - "type": "git", - "url": "https://github.com/bergos/ArduinoCbor.git" - }, - "frameworks": "arduino" -} diff --git a/lib/ArduinoCbor/library.properties b/lib/ArduinoCbor/library.properties deleted file mode 100644 index d83b35399..000000000 --- a/lib/ArduinoCbor/library.properties +++ /dev/null @@ -1,9 +0,0 @@ -name=ArduinoCbor -version=0.0.1 -author=Thomas Bergwinkl (https://www.bergnet.org/people/bergi/card#me) -maintainer=Thomas Bergwinkl (https://www.bergnet.org/people/bergi/card#me) -sentence=CBOR library for Arduino -paragraph=CBOR library for Arduino -category=Other -url=https://github.com/bergos/ArduinoCbor -architectures=* diff --git a/lib/ArduinoCbor/makefile b/lib/ArduinoCbor/makefile deleted file mode 100644 index a9589a98d..000000000 --- a/lib/ArduinoCbor/makefile +++ /dev/null @@ -1,37 +0,0 @@ -OBJS += \ - build/cn-cbor.o \ - build/cn-create.o \ - build/cn-encoder.o \ - build/cn-error.o \ - build/cn-get.o \ - build/CborArray.o \ - build/CborBuffer.o \ - build/CborObject.o \ - build/CborVariant.o \ - build/test.o - -LIBS += \ - -lgtest \ - -lpthread - -build/%.o: src/cn-cbor/%.c - @mkdir -p build - gcc $(CCFLAGS) -c -o "$@" "$<" -I src - -build/%.o: src/%.cpp - @mkdir -p build - g++ $(CCFLAGS) -c -o "$@" "$<" - -build/%.o: extra/test/%.cpp - @mkdir -p build - g++ $(CCFLAGS) -c -o "$@" "$<" -I src - -all: test - -test: $(OBJS) - g++ -o gtest $(OBJS) $(LIBS) - ./gtest - -clean: - @rm -rf build - @rm -f test diff --git a/lib/ArduinoCbor/src/ArduinoCbor.h b/lib/ArduinoCbor/src/ArduinoCbor.h deleted file mode 100644 index cb6e0eb30..000000000 --- a/lib/ArduinoCbor/src/ArduinoCbor.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef ARDUINO_CBOR_H_ -#define ARDUINO_CBOR_H_ - -#define USE_CBOR_CONTEXT - -#include "cn-cbor/cn-cbor.h" - -#ifndef CBOR_INT_T -#define CBOR_INT_T int -#endif - -class CborArray; -class CborBuffer; -class CborObject; -class CborVariant; - -#include "CborArray.h" -#include "CborBuffer.h" -#include "CborObject.h" -#include "CborVariant.h" - -#endif // ARDUINO_CBOR_H_ diff --git a/lib/ArduinoCbor/src/CborArray.cpp b/lib/ArduinoCbor/src/CborArray.cpp deleted file mode 100644 index ab04ba1be..000000000 --- a/lib/ArduinoCbor/src/CborArray.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include - -#include "ArduinoCbor.h" - -CborArray::CborArray(CborBuffer& buffer, cn_cbor* raw) : buffer(buffer) { - this->raw = raw; - - if (raw == 0) { - cn_cbor_errback err; - - this->raw = cn_cbor_array_create(&buffer.context, &err); - } -} - -CborArray::~CborArray() { - //Fixed memory leak - if (!raw->parent) { - cn_cbor_free(this->raw, &buffer.context); - } - -} - -CborVariant CborArray::get(int index) { - return CborVariant(buffer, cn_cbor_index(raw, index)); -} - -void CborArray::add(CborVariant value) { - cn_cbor_errback err; - - cn_cbor_array_append(raw, value.raw, &err); -} - -void CborArray::add(const char* value) { - add(CborVariant(buffer, value)); -} - -void CborArray::add(CBOR_INT_T value) { - add(CborVariant(buffer, value)); -} - -ssize_t CborArray::encode(uint8_t* data, size_t size) { - return cn_cbor_encoder_write(data, 0, size, raw); -} diff --git a/lib/ArduinoCbor/src/CborArray.h b/lib/ArduinoCbor/src/CborArray.h deleted file mode 100644 index d0a755a7d..000000000 --- a/lib/ArduinoCbor/src/CborArray.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef CBOR_ARRAY_H_ -#define CBOR_ARRAY_H_ - -#include "ArduinoCbor.h" - -class CborArray { - friend class CborVariant; - - public: - CborArray(CborBuffer& buffer, cn_cbor* raw=0); - ~CborArray(); - - CborVariant get(int index); - - void add(CborVariant value); - void add(const char* value); - void add(CBOR_INT_T value); - - ssize_t encode(uint8_t* data, size_t size); - - protected: - CborBuffer& buffer; - cn_cbor* raw; -}; - -#endif // CBOR_ARRAY_H_ diff --git a/lib/ArduinoCbor/src/CborBuffer.cpp b/lib/ArduinoCbor/src/CborBuffer.cpp deleted file mode 100644 index cc560be5e..000000000 --- a/lib/ArduinoCbor/src/CborBuffer.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "CborBuffer.h" -#include "Arduino.h" - -uint8_t* CborBuffer::calloc(size_t count, size_t size, CborBuffer* context) { - return context->alloc(count * size); -} - -void CborBuffer::free(uint8_t* ptr, CborBuffer* context) { -} - -CborBuffer::CborBuffer(size_t size) { - this->start = new uint8_t[size]; - this->offset = this->start; - this->end = this->start + size; - this->context.calloc_func = (cn_calloc_func)CborBuffer::calloc; - this->context.free_func = (cn_free_func)CborBuffer::free; - this->context.context = this; -} - -CborBuffer::~CborBuffer() { - delete[] this->start; -} - -uint8_t* CborBuffer::alloc(size_t size) { - if (offset + size > end) { - return 0; - } - - uint8_t* data = offset; - - offset += size; - - for (uint8_t* ptr = data; ptr < offset; ptr++) { - *ptr = 0; - } - - return data; -} - -CborVariant CborBuffer::decode(uint8_t* data, size_t size) { - cn_cbor_errback err; - - raw = cn_cbor_decode(data, size, &context, &err); - - return CborVariant(*this, raw); -} diff --git a/lib/ArduinoCbor/src/CborBuffer.h b/lib/ArduinoCbor/src/CborBuffer.h deleted file mode 100644 index 41fcffe02..000000000 --- a/lib/ArduinoCbor/src/CborBuffer.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef CBOR_BUFFER_H_ -#define CBOR_BUFFER_H_ - -#include "ArduinoCbor.h" - -class CborBuffer { - public: - cn_cbor_context context; - - CborBuffer(size_t size); - ~CborBuffer(); - - CborVariant decode(uint8_t* data, size_t size); - - uint8_t* alloc(size_t size); - - protected: - uint8_t* start; - uint8_t* end; - uint8_t* offset; - cn_cbor* raw = NULL; - - static uint8_t* calloc(size_t count, size_t size, CborBuffer* context); - static void free(uint8_t* ptr, CborBuffer* context); -}; - -#endif // CBOR_BUFFER_H_ diff --git a/lib/ArduinoCbor/src/CborObject.cpp b/lib/ArduinoCbor/src/CborObject.cpp deleted file mode 100644 index dd046696e..000000000 --- a/lib/ArduinoCbor/src/CborObject.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "CborObject.h" - -CborObject::CborObject(CborBuffer& buffer, cn_cbor* raw) : buffer(buffer) { - this->raw = raw; - - if (raw == 0) { - cn_cbor_errback err; - - this->raw = cn_cbor_map_create(&buffer.context, &err); - } -} - -CborObject::~CborObject() { - if (!raw->parent) { - cn_cbor_free(raw, &(buffer.context)); - } -} - -CborVariant CborObject::get(const char* key) { - return CborVariant(buffer, cn_cbor_mapget_string(raw, key)); -} - -void CborObject::set(const char* key, CborVariant value) { - cn_cbor_errback err; - - cn_cbor_mapput_string(raw, key, value.raw, &buffer.context, &err); -} - -void CborObject::set(const char* key, const char* value) { - set(key, CborVariant(buffer, value)); -} - -void CborObject::set(const char* key, CBOR_INT_T value) { - set(key, CborVariant(buffer, value)); -} - -void CborObject::set(const char* key, CborObject value) { - set(key, CborVariant(buffer, value)); -} - -void CborObject::set(const char* key, CborArray value) { - set(key, CborVariant(buffer, value)); -} - -size_t CborObject::encode(uint8_t* data, size_t size) { - return cn_cbor_encoder_write(data, 0, size, raw); -} diff --git a/lib/ArduinoCbor/src/CborObject.h b/lib/ArduinoCbor/src/CborObject.h deleted file mode 100644 index c806da240..000000000 --- a/lib/ArduinoCbor/src/CborObject.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef CBOR_OBJECT_H_ -#define CBOR_OBJECT_H_ - -#include "ArduinoCbor.h" -#include "stdlib.h" - -class CborObject { - friend class CborVariant; - - public: - CborObject(CborBuffer& buffer, cn_cbor* raw=0); - ~CborObject(); - - CborVariant get(const char* key); - - void set(const char* key, CborVariant value); - void set(const char* key, const char* value); - void set(const char* key, CBOR_INT_T value); - void set(const char* key, CborObject value); - void set(const char* key, CborArray value); - - size_t encode(uint8_t* data, size_t size); - - protected: - CborBuffer& buffer; - cn_cbor* raw; -}; - -#endif // CBOR_OBJECT_H_ diff --git a/lib/ArduinoCbor/src/CborVariant.cpp b/lib/ArduinoCbor/src/CborVariant.cpp deleted file mode 100644 index 28711ee23..000000000 --- a/lib/ArduinoCbor/src/CborVariant.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include "CborVariant.h" - -CborVariant::CborVariant(CborBuffer& buffer, cn_cbor* raw) : buffer(buffer) { - this->raw = raw; -} - -CborVariant::CborVariant(CborBuffer& buffer, const char* value) : buffer(buffer) { - cn_cbor_errback err; - - raw = cn_cbor_string_create(value, &buffer.context, &err); -} - -CborVariant::CborVariant(CborBuffer& buffer, CBOR_INT_T value) : buffer(buffer) { - cn_cbor_errback err; - - raw = cn_cbor_int_create(value, &buffer.context, &err); -} - -CborVariant::CborVariant(CborBuffer& buffer, CborObject& value) : buffer(buffer) { - this->raw = value.raw; -} - -CborVariant::CborVariant(CborBuffer& buffer, CborArray& value) : buffer(buffer) { - this->raw = value.raw; -} - -int CborVariant::length() { - if (raw == 0) { - return -1; - } - - return raw->length; -} - -bool CborVariant::isValid() { - return raw != 0; -} - -bool CborVariant::isString() { - return isValid() && raw->type == CN_CBOR_BYTES || raw->type == CN_CBOR_TEXT; -} - -bool CborVariant::isInteger() { - return isValid() && raw->type == CN_CBOR_UINT || raw->type == CN_CBOR_INT; -} - -bool CborVariant::isObject() { - return isValid() && raw->type == CN_CBOR_MAP; -} - -bool CborVariant::isArray() { - return isValid() && raw->type == CN_CBOR_ARRAY; -} - -const char* CborVariant::asString() { - if (!isValid()) { - return 0; - } - - if (raw->type != CN_CBOR_BYTES && raw->type != CN_CBOR_TEXT) { - return 0; - } - - if (raw->v.str[raw->length] != 0) { - char* tmp = (char*)buffer.alloc(raw->length + 1); - - for (int i = 0; i < raw->length; i++) { - tmp[i] = raw->v.str[i]; - } - - return tmp; - } - - return raw->v.str; -} - -CBOR_INT_T CborVariant::asInteger() { - if (!isValid()) { - return 0; - } - - if (raw->type == CN_CBOR_UINT) { - return raw->v.uint; - } - - if (raw->type == CN_CBOR_INT) { - return raw->v.sint; - } - - return 0; -} - -CborObject CborVariant::asObject() { - if (isObject()) { - return CborObject(buffer, raw); - } - - return CborObject(buffer); -} - -CborArray CborVariant::asArray() { - if (isArray()) { - return CborArray(buffer, raw); - } - - return CborArray(buffer); -} - -size_t CborVariant::encode(uint8_t* data, size_t size) { - return cn_cbor_encoder_write(data, 0, size, raw); -} diff --git a/lib/ArduinoCbor/src/CborVariant.h b/lib/ArduinoCbor/src/CborVariant.h deleted file mode 100644 index 124bf9842..000000000 --- a/lib/ArduinoCbor/src/CborVariant.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef CBOR_VARIANT_H_ -#define CBOR_VARIANT_H_ - -#include "ArduinoCbor.h" - -class CborVariant { - friend class CborArray; - friend class CborObject; - - public: - CborVariant(CborBuffer& buffer, cn_cbor* raw); - CborVariant(CborBuffer& buffer, const char* value); - CborVariant(CborBuffer& buffer, CBOR_INT_T value); - CborVariant(CborBuffer& buffer, CborObject& value); - CborVariant(CborBuffer& buffer, CborArray& value); - - int length(); - - bool isValid(); - bool isString(); - bool isInteger(); - bool isObject(); - bool isArray(); - - const char* asString(); - CBOR_INT_T asInteger(); - CborObject asObject(); - CborArray asArray(); - - size_t encode(uint8_t* data, size_t size); - - protected: - CborBuffer& buffer; - cn_cbor* raw; -}; - -#endif // CBOR_VARIANT_H_ diff --git a/lib/ArduinoCbor/src/cbor.h b/lib/ArduinoCbor/src/cbor.h deleted file mode 100644 index 1859f09ab..000000000 --- a/lib/ArduinoCbor/src/cbor.h +++ /dev/null @@ -1,118 +0,0 @@ -#ifndef CBOR_PROTOCOL_H__ -#define CBOR_PROTOCOL_H__ - -/* The 8 major types */ -#define MT_UNSIGNED 0 -#define MT_NEGATIVE 1 -#define MT_BYTES 2 -#define MT_TEXT 3 -#define MT_ARRAY 4 -#define MT_MAP 5 -#define MT_TAG 6 -#define MT_PRIM 7 - -/* The initial bytes resulting from those */ -#define IB_UNSIGNED (MT_UNSIGNED << 5) -#define IB_NEGATIVE (MT_NEGATIVE << 5) -#define IB_BYTES (MT_BYTES << 5) -#define IB_TEXT (MT_TEXT << 5) -#define IB_ARRAY (MT_ARRAY << 5) -#define IB_MAP (MT_MAP << 5) -#define IB_TAG (MT_TAG << 5) -#define IB_PRIM (MT_PRIM << 5) - -#define IB_NEGFLAG (IB_NEGATIVE - IB_UNSIGNED) -#define IB_NEGFLAG_AS_BIT(ib) ((ib) >> 5) -#define IB_TEXTFLAG (IB_TEXT - IB_BYTES) - -#define IB_AI(ib) ((ib) & 0x1F) -#define IB_MT(ib) ((ib) >> 5) - -/* Tag numbers handled by this implementation */ -#define TAG_TIME_EPOCH 1 -#define TAG_BIGNUM 2 -#define TAG_BIGNUM_NEG 3 -#define TAG_URI 32 -#define TAG_RE 35 - -/* Initial bytes of those tag numbers */ -#define IB_TIME_EPOCH (IB_TAG | TAG_TIME_EPOCH) -#define IB_BIGNUM (IB_TAG | TAG_BIGNUM) -#define IB_BIGNUM_NEG (IB_TAG | TAG_BIGNUM_NEG) -/* TAG_URI and TAG_RE are non-immediate tags */ - -/* Simple values handled by this implementation */ -#define VAL_FALSE 20 -#define VAL_TRUE 21 -#define VAL_NIL 22 -#define VAL_UNDEF 23 - -/* Initial bytes of those simple values */ -#define IB_FALSE (IB_PRIM | VAL_FALSE) -#define IB_TRUE (IB_PRIM | VAL_TRUE) -#define IB_NIL (IB_PRIM | VAL_NIL) -#define IB_UNDEF (IB_PRIM | VAL_UNDEF) - -/* AI values with more data in head */ -#define AI_1 24 -#define AI_2 25 -#define AI_4 26 -#define AI_8 27 -#define AI_INDEF 31 -#define IB_BREAK (IB_PRIM | AI_INDEF) -/* For */ -#define IB_UNUSED (IB_TAG | AI_INDEF) - -/* Floating point initial bytes */ -#define IB_FLOAT2 (IB_PRIM | AI_2) -#define IB_FLOAT4 (IB_PRIM | AI_4) -#define IB_FLOAT8 (IB_PRIM | AI_8) - -// These definitions are here because they aren't required for the public -// interface, and they were quite confusing in cn-cbor.h - -#ifdef USE_CBOR_CONTEXT -/** - * Allocate enough space for 1 `cn_cbor` structure. - * - * @param[in] ctx The allocation context, or NULL for calloc. - * @return A pointer to a `cn_cbor` or NULL on failure - */ -#define CN_CALLOC(ctx) ((ctx) && (ctx)->calloc_func) ? \ - (ctx)->calloc_func(1, sizeof(cn_cbor), (ctx)->context) : \ - calloc(1, sizeof(cn_cbor)); - -/** - * Free a - * @param free_func [description] - * @return [description] - */ -#define CN_FREE(ptr, ctx) ((ctx) && (ctx)->free_func) ? \ - (ctx)->free_func((ptr), (ctx)->context) : \ - free((ptr)); - -#define CBOR_CONTEXT_PARAM , context -#define CN_CALLOC_CONTEXT() CN_CALLOC(context) -#define CN_CBOR_FREE_CONTEXT(p) CN_FREE(p, context) - -#else - -#define CBOR_CONTEXT_PARAM -#define CN_CALLOC_CONTEXT() CN_CALLOC -#define CN_CBOR_FREE_CONTEXT(p) CN_FREE(p) - -#ifndef CN_CALLOC -#define CN_CALLOC calloc(1, sizeof(cn_cbor)) -#endif - -#ifndef CN_FREE -#define CN_FREE free -#endif - -#endif // USE_CBOR_CONTEXT - -#ifndef UNUSED_PARAM -#define UNUSED_PARAM(p) ((void)&(p)) -#endif - -#endif // CBOR_PROTOCOL_H__ diff --git a/lib/ArduinoCbor/src/cn-cbor/LICENSE b/lib/ArduinoCbor/src/cn-cbor/LICENSE deleted file mode 100644 index df377b191..000000000 --- a/lib/ArduinoCbor/src/cn-cbor/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Carsten Bormann - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/lib/ArduinoCbor/src/cn-cbor/README.md b/lib/ArduinoCbor/src/cn-cbor/README.md deleted file mode 100644 index 58208589a..000000000 --- a/lib/ArduinoCbor/src/cn-cbor/README.md +++ /dev/null @@ -1,44 +0,0 @@ -[![Build Status](https://travis-ci.org/cabo/cn-cbor.png?branch=master)](https://travis-ci.org/cabo/cn-cbor) - -# cn-cbor: A constrained node implementation of CBOR in C - -This is a constrained node implementation of [CBOR](http://cbor.io) in -C that I threw together in 2013, before the publication of -[RFC 7049](http://tools.ietf.org/html/rfc7049), to validate certain -implementability considerations. - -Its API model was inspired by -[nxjson](https://bitbucket.org/yarosla/nxjson). It turns out that -this API model actually works even better with the advantages of the -CBOR format. - -This code has been used in a number of research implementations on -constrained nodes, with resulting code sizes appreciably under 1 KiB -on ARM platforms. - -I always meant to improve the interface some more with certain API -changes, in order to get even closer to 0.5 KiB, but I ran out of -time. So here it is. If I do get around to making these changes, the -API will indeed change a bit, so please be forewarned. - -## Building - -There is a `Simple-Makefile` for playing around, as well as a complete -[`cmake`](http://www.cmake.org)-based build environment. -(You can choose what fits your needs better.) - -Building with `cmake`: - - ./build.sh - -Building including testing: - - ./build.sh all test - -Generating a test coverage report (requires lcov[^1]; result in `build/lcov/index.html`): - - ./build.sh all coveralls coverage_report - -License: MIT - -[^1]: Installation with homebrew: `brew install lcov` diff --git a/lib/ArduinoCbor/src/cn-cbor/cn-cbor.c b/lib/ArduinoCbor/src/cn-cbor/cn-cbor.c deleted file mode 100644 index 30cfeb848..000000000 --- a/lib/ArduinoCbor/src/cn-cbor/cn-cbor.c +++ /dev/null @@ -1,285 +0,0 @@ -#ifndef CN_CBOR_C -#define CN_CBOR_C - -#ifdef __cplusplus -extern "C" { -#endif -#ifdef EMACS_INDENTATION_HELPER -} /* Duh. */ -#endif - -#include -#include -#include -#include -#include - -#include "cn-cbor.h" -#include "../cbor.h" - -#define CN_CBOR_FAIL(code) do { pb->err = code; goto fail; } while(0) - -void cn_cbor_free(cn_cbor* cb CBOR_CONTEXT) { - cn_cbor* p = cb; - assert(!p || !p->parent); - while (p) { - cn_cbor* p1; - while ((p1 = p->first_child)) { /* go down */ - p = p1; - } - if (!(p1 = p->next)) { /* go up next */ - if ((p1 = p->parent)) - p1->first_child = 0; - } - CN_CBOR_FREE_CONTEXT(p); - p = p1; - } -} - -#ifndef CBOR_NO_FLOAT -static double decode_half(int half) { - int exp = (half >> 10) & 0x1f; - int mant = half & 0x3ff; - double val; - if (exp == 0) val = ldexp(mant, -24); - else if (exp != 31) val = ldexp(mant + 1024, exp - 25); - else val = mant == 0 ? INFINITY : NAN; - return half & 0x8000 ? -val : val; -} -#endif /* CBOR_NO_FLOAT */ - -uint16_t htons(uint16_t v) { - return (v >> 8) | (v << 8); -} -uint32_t htonl(uint32_t v) { - return htons(v >> 16) | (htons((uint16_t) v) << 16); -} - -uint16_t ntohs(uint16_t v) { - return htons(v); -} -uint32_t ntohl(uint32_t v) { - return htonl(v); -} - -/* Fix these if you can't do non-aligned reads */ -#define ntoh8p(p) (*(unsigned char*)(p)) -#define ntoh16p(p) (ntohs(*(unsigned short*)(p))) -#define ntoh32p(p) (ntohl(*(unsigned long*)(p))) -static uint64_t ntoh64p(unsigned char *p) { - uint64_t ret = ntoh32p(p); - ret <<= 32; - ret += ntoh32p(p+4); - return ret; -} - -static cn_cbor_type mt_trans[] = { - CN_CBOR_UINT, CN_CBOR_INT, - CN_CBOR_BYTES, CN_CBOR_TEXT, - CN_CBOR_ARRAY, CN_CBOR_MAP, - CN_CBOR_TAG, CN_CBOR_SIMPLE, -}; - -struct parse_buf { - unsigned char *buf; - unsigned char *ebuf; - cn_cbor_error err; -}; - -#define TAKE(pos, ebuf, n, stmt) \ - if (n > (size_t)(ebuf - pos)) \ - CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_DATA); \ - stmt; \ - pos += n; - -static cn_cbor *decode_item (struct parse_buf *pb CBOR_CONTEXT, cn_cbor* top_parent) { - unsigned char *pos = pb->buf; - unsigned char *ebuf = pb->ebuf; - cn_cbor* parent = top_parent; - int ib; - unsigned int mt; - int ai; - uint64_t val; - cn_cbor* cb = NULL; -#ifndef CBOR_NO_FLOAT - union { - float f; - uint32_t u; - } u32; - union { - double d; - uint64_t u; - } u64; -#endif /* CBOR_NO_FLOAT */ - -again: - TAKE(pos, ebuf, 1, ib = ntoh8p(pos) ); - if (ib == IB_BREAK) { - if (!(parent->flags & CN_CBOR_FL_INDEF)) - CN_CBOR_FAIL(CN_CBOR_ERR_BREAK_OUTSIDE_INDEF); - switch (parent->type) { - case CN_CBOR_BYTES: case CN_CBOR_TEXT: - parent->type += 2; /* CN_CBOR_* -> CN_CBOR_*_CHUNKED */ - break; - case CN_CBOR_MAP: - if (parent->length & 1) - CN_CBOR_FAIL(CN_CBOR_ERR_ODD_SIZE_INDEF_MAP); - default:; - } - goto complete; - } - mt = ib >> 5; - ai = ib & 0x1f; - val = ai; - - cb = CN_CALLOC_CONTEXT(); - if (!cb) - CN_CBOR_FAIL(CN_CBOR_ERR_OUT_OF_MEMORY); - - cb->type = mt_trans[mt]; - - cb->parent = parent; - if (parent->last_child) { - parent->last_child->next = cb; - } else { - parent->first_child = cb; - } - parent->last_child = cb; - parent->length++; - - switch (ai) { - case AI_1: TAKE(pos, ebuf, 1, val = ntoh8p(pos)) ; break; - case AI_2: TAKE(pos, ebuf, 2, val = ntoh16p(pos)) ; break; - case AI_4: TAKE(pos, ebuf, 4, val = ntoh32p(pos)) ; break; - case AI_8: TAKE(pos, ebuf, 8, val = ntoh64p(pos)) ; break; - case 28: case 29: case 30: CN_CBOR_FAIL(CN_CBOR_ERR_RESERVED_AI); - case AI_INDEF: - if ((mt - MT_BYTES) <= MT_MAP) { - cb->flags |= CN_CBOR_FL_INDEF; - goto push; - } else { - CN_CBOR_FAIL(CN_CBOR_ERR_MT_UNDEF_FOR_INDEF); - } - } - // process content - switch (mt) { - case MT_UNSIGNED: - cb->v.uint = val; /* to do: Overflow check */ - break; - case MT_NEGATIVE: - cb->v.sint = ~val; /* to do: Overflow check */ - break; - case MT_BYTES: case MT_TEXT: - cb->v.str = (char *) pos; - cb->length = val; - TAKE(pos, ebuf, val, ;); - break; - case MT_MAP: - val <<= 1; - /* fall through */ - case MT_ARRAY: - if ((cb->v.count = val)) { - cb->flags |= CN_CBOR_FL_COUNT; - goto push; - } - break; - case MT_TAG: - cb->v.uint = val; - goto push; - case MT_PRIM: - switch (ai) { - case VAL_FALSE: cb->type = CN_CBOR_FALSE; break; - case VAL_TRUE: cb->type = CN_CBOR_TRUE; break; - case VAL_NIL: cb->type = CN_CBOR_NULL; break; - case VAL_UNDEF: cb->type = CN_CBOR_UNDEF; break; - case AI_2: -#ifndef CBOR_NO_FLOAT - cb->type = CN_CBOR_DOUBLE; - cb->v.dbl = decode_half(val); -#else /* CBOR_NO_FLOAT */ - CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); -#endif /* CBOR_NO_FLOAT */ - break; - case AI_4: -#ifndef CBOR_NO_FLOAT - cb->type = CN_CBOR_DOUBLE; - u32.u = val; - cb->v.dbl = u32.f; -#else /* CBOR_NO_FLOAT */ - CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); -#endif /* CBOR_NO_FLOAT */ - break; - case AI_8: -#ifndef CBOR_NO_FLOAT - cb->type = CN_CBOR_DOUBLE; - u64.u = val; - cb->v.dbl = u64.d; -#else /* CBOR_NO_FLOAT */ - CN_CBOR_FAIL(CN_CBOR_ERR_FLOAT_NOT_SUPPORTED); -#endif /* CBOR_NO_FLOAT */ - break; - default: cb->v.uint = val; - } - } -fill: /* emulate loops */ - if (parent->flags & CN_CBOR_FL_INDEF) { - if (parent->type == CN_CBOR_BYTES || parent->type == CN_CBOR_TEXT) - if (cb->type != parent->type) - CN_CBOR_FAIL(CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING); - goto again; - } - if (parent->flags & CN_CBOR_FL_COUNT) { - if (--parent->v.count) - goto again; - } - /* so we are done filling parent. */ -complete: /* emulate return from call */ - if (parent == top_parent) { - if (pos != ebuf) /* XXX do this outside */ - CN_CBOR_FAIL(CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED); - pb->buf = pos; - return cb; - } - cb = parent; - parent = parent->parent; - goto fill; -push: /* emulate recursive call */ - parent = cb; - goto again; -fail: - pb->buf = pos; - return 0; -} - -cn_cbor* cn_cbor_decode(const unsigned char* buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp) { - cn_cbor catcher = {CN_CBOR_INVALID, 0, {0}, 0, NULL, NULL, NULL, NULL}; - struct parse_buf pb; - cn_cbor* ret; - - pb.buf = (unsigned char *)buf; - pb.ebuf = (unsigned char *)buf+len; - pb.err = CN_CBOR_NO_ERROR; - ret = decode_item(&pb CBOR_CONTEXT_PARAM, &catcher); - if (ret != NULL) { - /* mark as top node */ - ret->parent = NULL; - } else { - if (catcher.first_child) { - catcher.first_child->parent = 0; - cn_cbor_free(catcher.first_child CBOR_CONTEXT_PARAM); - } -//fail: - if (errp) { - errp->err = pb.err; - errp->pos = pb.buf - (unsigned char *)buf; - } - return NULL; - } - return ret; -} - -#ifdef __cplusplus -} -#endif - -#endif /* CN_CBOR_C */ diff --git a/lib/ArduinoCbor/src/cn-cbor/cn-cbor.h b/lib/ArduinoCbor/src/cn-cbor/cn-cbor.h deleted file mode 100644 index dcee4e106..000000000 --- a/lib/ArduinoCbor/src/cn-cbor/cn-cbor.h +++ /dev/null @@ -1,405 +0,0 @@ -/** - * \file - * \brief - * CBOR parsing - */ - -#ifndef CN_CBOR_H -#define CN_CBOR_H - -#ifdef __cplusplus -extern "C" { -#endif -#ifdef EMACS_INDENTATION_HELPER -} /* Duh. */ -#endif - -#include -#include -#include -#include - -/* missing in arduino headers */ -//typedef long ssize_t; - -/** - * All of the different kinds of CBOR values. - */ -typedef enum cn_cbor_type { - /** false */ - CN_CBOR_FALSE, - /** true */ - CN_CBOR_TRUE, - /** null */ - CN_CBOR_NULL, - /** undefined */ - CN_CBOR_UNDEF, - /** Positive integers */ - CN_CBOR_UINT, - /** Negative integers */ - CN_CBOR_INT, - /** Byte string */ - CN_CBOR_BYTES, - /** UTF-8 string */ - CN_CBOR_TEXT, - /** Byte string, in chunks. Each chunk is a child. */ - CN_CBOR_BYTES_CHUNKED, - /** UTF-8 string, in chunks. Each chunk is a child */ - CN_CBOR_TEXT_CHUNKED, - /** Array of CBOR values. Each array element is a child, in order */ - CN_CBOR_ARRAY, - /** Map of key/value pairs. Each key and value is a child, alternating. */ - CN_CBOR_MAP, - /** Tag describing the next value. The next value is the single child. */ - CN_CBOR_TAG, - /** Simple value, other than the defined ones */ - CN_CBOR_SIMPLE, - /** Doubles, floats, and half-floats */ - CN_CBOR_DOUBLE, - /** An error has occurred */ - CN_CBOR_INVALID -} cn_cbor_type; - -/** - * Flags used during parsing. Not useful for consumers of the - * `cn_cbor` structure. - */ -typedef enum cn_cbor_flags { - /** The count field will be used for parsing */ - CN_CBOR_FL_COUNT = 1, - /** An indefinite number of children */ - CN_CBOR_FL_INDEF = 2, - /** Not used yet; the structure must free the v.str pointer when the - structure is freed */ - CN_CBOR_FL_OWNER = 0x80, /* of str */ -} cn_cbor_flags; - -/** - * A CBOR value - */ -typedef struct cn_cbor { - /** The type of value */ - cn_cbor_type type; - /** Flags used at parse time */ - cn_cbor_flags flags; - /** Data associated with the value; different branches of the union are - used depending on the `type` field. */ - union { - /** CN_CBOR_BYTES */ - const uint8_t* bytes; - /** CN_CBOR_TEXT */ - const char* str; - /** CN_CBOR_INT */ - long sint; - /** CN_CBOR_UINT */ - unsigned long uint; - /** CN_CBOR_DOUBLE */ - double dbl; - /** for use during parsing */ - unsigned long count; - } v; /* TBD: optimize immediate */ - /** Number of children. - * @note: for maps, this is 2x the number of entries */ - int length; - /** The first child value */ - struct cn_cbor* first_child; - /** The last child value */ - struct cn_cbor* last_child; - /** The sibling after this one, or NULL if this is the last */ - struct cn_cbor* next; - /** The parent of this value, or NULL if this is the root */ - struct cn_cbor* parent; -} cn_cbor; - -/** - * All of the different kinds of errors - */ -typedef enum cn_cbor_error { - /** No error has occurred */ - CN_CBOR_NO_ERROR, - /** More data was expected while parsing */ - CN_CBOR_ERR_OUT_OF_DATA, - /** Some extra data was left over at the end of parsing */ - CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED, - /** A map should be alternating keys and values. A break was found - when a value was expected */ - CN_CBOR_ERR_ODD_SIZE_INDEF_MAP, - /** A break was found where it wasn't expected */ - CN_CBOR_ERR_BREAK_OUTSIDE_INDEF, - /** Indefinite encoding works for bstrs, strings, arrays, and maps. - A different major type tried to use it. */ - CN_CBOR_ERR_MT_UNDEF_FOR_INDEF, - /** Additional Information values 28-30 are reserved */ - CN_CBOR_ERR_RESERVED_AI, - /** A chunked encoding was used for a string or bstr, and one of the elements - wasn't the expected (string/bstr) type */ - CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING, - /** An invalid parameter was passed to a function */ - CN_CBOR_ERR_INVALID_PARAMETER, - /** Allocation failed */ - CN_CBOR_ERR_OUT_OF_MEMORY, - /** A float was encountered during parse but the library was built without - support for float types. */ - CN_CBOR_ERR_FLOAT_NOT_SUPPORTED -} cn_cbor_error; - -/** - * Strings matching the `cn_cbor_error` conditions. - * - * @todo: turn into a function to make the type safety more clear? - */ -extern const char *cn_cbor_error_str[]; - -/** - * Errors - */ -typedef struct cn_cbor_errback { - /** The position in the input where the erorr happened */ - int pos; - /** The error, or CN_CBOR_NO_ERROR if none */ - cn_cbor_error err; -} cn_cbor_errback; - -#ifdef USE_CBOR_CONTEXT - -/** - * Allocate and zero out memory. `count` elements of `size` are required, - * as for `calloc(3)`. The `context` is the `cn_cbor_context` passed in - * earlier to the CBOR routine. - * - * @param[in] count The number of items to allocate - * @param[in] size The size of each item - * @param[in] context The allocation context - */ -typedef void* (*cn_calloc_func)(size_t count, size_t size, void *context); - -/** - * Free memory previously allocated with a context. If using a pool allocator, - * this function will often be a no-op, but it must be supplied in order to - * prevent the CBOR library from calling `free(3)`. - * - * @note: it may be that this is never needed; if so, it will be removed for - * clarity and speed. - * - * @param context [description] - * @return [description] - */ -typedef void (*cn_free_func)(void *ptr, void *context); - -/** - * The allocation context. - */ -typedef struct cn_cbor_context { - /** The pool `calloc` routine. Must allocate and zero. */ - cn_calloc_func calloc_func; - /** The pool `free` routine. Often a no-op, but required. */ - cn_free_func free_func; - /** Typically, the pool object, to be used when calling `calloc_func` - * and `free_func` */ - void *context; -} cn_cbor_context; - -/** When USE_CBOR_CONTEXT is defined, many functions take an extra `context` - * parameter */ -#define CBOR_CONTEXT , cn_cbor_context *context -/** When USE_CBOR_CONTEXT is defined, some functions take an extra `context` - * parameter at the beginning */ -#define CBOR_CONTEXT_COMMA cn_cbor_context *context, - -#else - -#define CBOR_CONTEXT -#define CBOR_CONTEXT_COMMA - -#endif - -/** - * Decode an array of CBOR bytes into structures. - * - * @param[in] buf The array of bytes to parse - * @param[in] len The number of bytes in the array - * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) - * @param[out] errp Error, if NULL is returned - * @return The parsed CBOR structure, or NULL on error - */ -cn_cbor* cn_cbor_decode(const uint8_t *buf, size_t len CBOR_CONTEXT, cn_cbor_errback *errp); - -/** - * Get a value from a CBOR map that has the given string as a key. - * - * @param[in] cb The CBOR map - * @param[in] key The string to look up in the map - * @return The matching value, or NULL if the key is not found - */ -cn_cbor* cn_cbor_mapget_string(const cn_cbor* cb, const char* key); - -/** - * Get a value from a CBOR map that has the given integer as a key. - * - * @param[in] cb The CBOR map - * @param[in] key The int to look up in the map - * @return The matching value, or NULL if the key is not found - */ -cn_cbor* cn_cbor_mapget_int(const cn_cbor* cb, int key); - -/** - * Get the item with the given index from a CBOR array. - * - * @param[in] cb The CBOR map - * @param[in] idx The array index - * @return The matching value, or NULL if the index is invalid - */ -cn_cbor* cn_cbor_index(const cn_cbor* cb, unsigned int idx); - -/** - * Free the given CBOR structure. - * You MUST NOT try to free a cn_cbor structure with a parent (i.e., one - * that is not a root in the tree). - * - * @param[in] cb The CBOR value to free. May be NULL, or a root object. - * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) - */ -void cn_cbor_free(cn_cbor* cb CBOR_CONTEXT); - -/** - * Write a CBOR value and all of the child values. - * - * @param[in] buf The buffer into which to write - * @param[in] buf_offset The offset (in bytes) from the beginning of the buffer - * to start writing at - * @param[in] buf_size The total length (in bytes) of the buffer - * @param[in] cb [description] - * @return -1 on fail, or number of bytes written - */ -ssize_t cn_cbor_encoder_write(uint8_t *buf, - size_t buf_offset, - size_t buf_size, - const cn_cbor *cb); - -/** - * Create a CBOR map. - * - * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) - * @param[out] errp Error, if NULL is returned - * @return The created map, or NULL on error - */ -cn_cbor* cn_cbor_map_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp); - -/** - * Create a CBOR byte string. The data in the byte string is *not* owned - * by the CBOR object, so it is not freed automatically. - * - * @param[in] data The data - * @param[in] len The number of bytes of data - * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) - * @param[out] errp Error, if NULL is returned - * @return The created object, or NULL on error - */ -cn_cbor* cn_cbor_data_create(const uint8_t* data, int len - CBOR_CONTEXT, - cn_cbor_errback *errp); - -/** - * Create a CBOR UTF-8 string. The data is not checked for UTF-8 correctness. - * The data being stored in the string is *not* owned the CBOR object, so it is - * not freed automatically. - * - * @note: Do NOT use this function with untrusted data. It calls strlen, and - * relies on proper NULL-termination. - * - * @param[in] data NULL-terminated UTF-8 string - * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) - * @param[out] errp Error, if NULL is returned - * @return The created object, or NULL on error - */ -cn_cbor* cn_cbor_string_create(const char* data - CBOR_CONTEXT, - cn_cbor_errback *errp); - -/** - * Create a CBOR integer (either positive or negative). - * - * @param[in] value the value of the integer - * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) - * @param[out] errp Error, if NULL is returned - * @return The created object, or NULL on error - */ -cn_cbor* cn_cbor_int_create(int64_t value - CBOR_CONTEXT, - cn_cbor_errback *errp); - -/** - * Put a CBOR object into a map with a CBOR object key. Duplicate checks are NOT - * currently performed. - * - * @param[in] cb_map The map to insert into - * @param[in] key The key - * @param[in] cb_value The value - * @param[out] errp Error - * @return True on success - */ -bool cn_cbor_map_put(cn_cbor* cb_map, - cn_cbor *cb_key, cn_cbor *cb_value, - cn_cbor_errback *errp); - -/** - * Put a CBOR object into a map with an integer key. Duplicate checks are NOT - * currently performed. - * - * @param[in] cb_map The map to insert into - * @param[in] key The integer key - * @param[in] cb_value The value - * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) - * @param[out] errp Error - * @return True on success - */ -bool cn_cbor_mapput_int(cn_cbor* cb_map, - int64_t key, cn_cbor* cb_value - CBOR_CONTEXT, - cn_cbor_errback *errp); - -/** - * Put a CBOR object into a map with a string key. Duplicate checks are NOT - * currently performed. - * - * @note: do not call this routine with untrusted string data. It calls - * strlen, and requires a properly NULL-terminated key. - * - * @param[in] cb_map The map to insert into - * @param[in] key The string key - * @param[in] cb_value The value - * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) - * @param[out] errp Error - * @return True on success - */ -bool cn_cbor_mapput_string(cn_cbor* cb_map, - const char* key, cn_cbor* cb_value - CBOR_CONTEXT, - cn_cbor_errback *errp); - -/** - * Create a CBOR array - * - * @param[in] CBOR_CONTEXT Allocation context (only if USE_CBOR_CONTEXT is defined) - * @param[out] errp Error, if NULL is returned - * @return The created object, or NULL on error - */ -cn_cbor* cn_cbor_array_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp); - -/** - * Append an item to the end of a CBOR array. - * - * @param[in] cb_array The array into which to insert - * @param[in] cb_value The value to insert - * @param[out] errp Error - * @return True on success - */ -bool cn_cbor_array_append(cn_cbor* cb_array, - cn_cbor* cb_value, - cn_cbor_errback *errp); - -#ifdef __cplusplus -} -#endif - -#endif /* CN_CBOR_H */ diff --git a/lib/ArduinoCbor/src/cn-cbor/cn-create.c b/lib/ArduinoCbor/src/cn-cbor/cn-create.c deleted file mode 100644 index 772de3b16..000000000 --- a/lib/ArduinoCbor/src/cn-cbor/cn-create.c +++ /dev/null @@ -1,184 +0,0 @@ -#ifndef CN_CREATE_C -#define CN_CREATE_C - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#include "cn-cbor.h" -#include "../cbor.h" - -#define INIT_CB(v) \ - if (errp) {errp->err = CN_CBOR_NO_ERROR;} \ - (v) = CN_CALLOC_CONTEXT(); \ - if (!(v)) { if (errp) {errp->err = CN_CBOR_ERR_OUT_OF_MEMORY;} return NULL; } - -cn_cbor* cn_cbor_map_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp) -{ - cn_cbor* ret; - INIT_CB(ret); - - ret->type = CN_CBOR_MAP; - ret->flags |= CN_CBOR_FL_COUNT; - - return ret; -} - -cn_cbor* cn_cbor_data_create(const uint8_t* data, int len - CBOR_CONTEXT, - cn_cbor_errback *errp) -{ - cn_cbor* ret; - INIT_CB(ret); - - ret->type = CN_CBOR_BYTES; - ret->length = len; - ret->v.str = (const char*) data; // TODO: add v.ustr to the union? - - return ret; -} - -cn_cbor* cn_cbor_string_create(const char* data - CBOR_CONTEXT, - cn_cbor_errback *errp) -{ - cn_cbor* ret; - INIT_CB(ret); - - ret->type = CN_CBOR_TEXT; - ret->length = strlen(data); - ret->v.str = data; - - return ret; -} - -cn_cbor* cn_cbor_int_create(int64_t value - CBOR_CONTEXT, - cn_cbor_errback *errp) -{ - cn_cbor* ret; - INIT_CB(ret); - - if (value<0) { - ret->type = CN_CBOR_INT; - ret->v.sint = value; - } else { - ret->type = CN_CBOR_UINT; - ret->v.uint = value; - } - - return ret; -} - -static bool _append_kv(cn_cbor *cb_map, cn_cbor *key, cn_cbor *val) -{ - //Connect key and value and insert them into the map. - key->parent = cb_map; - key->next = val; - val->parent = cb_map; - val->next = NULL; - - if(cb_map->last_child) { - cb_map->last_child->next = key; - } else { - cb_map->first_child = key; - } - cb_map->last_child = val; - cb_map->length += 2; - return true; -} - -bool cn_cbor_map_put(cn_cbor* cb_map, - cn_cbor *cb_key, cn_cbor *cb_value, - cn_cbor_errback *errp) -{ - //Make sure input is a map. Otherwise - if(!cb_map || !cb_key || !cb_value || cb_map->type != CN_CBOR_MAP) - { - if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;} - return false; - } - - return _append_kv(cb_map, cb_key, cb_value); -} - -bool cn_cbor_mapput_int(cn_cbor* cb_map, - int64_t key, cn_cbor* cb_value - CBOR_CONTEXT, - cn_cbor_errback *errp) -{ - cn_cbor* cb_key; - - //Make sure input is a map. Otherwise - if(!cb_map || !cb_value || cb_map->type != CN_CBOR_MAP) - { - if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;} - return false; - } - - cb_key = cn_cbor_int_create(key CBOR_CONTEXT_PARAM, errp); - if (!cb_key) { return false; } - return _append_kv(cb_map, cb_key, cb_value); -} - -bool cn_cbor_mapput_string(cn_cbor* cb_map, - const char* key, cn_cbor* cb_value - CBOR_CONTEXT, - cn_cbor_errback *errp) -{ - cn_cbor* cb_key; - - //Make sure input is a map. Otherwise - if(!cb_map || !cb_value || cb_map->type != CN_CBOR_MAP) - { - if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;} - return false; - } - - cb_key = cn_cbor_string_create(key CBOR_CONTEXT_PARAM, errp); - if (!cb_key) { return false; } - return _append_kv(cb_map, cb_key, cb_value); -} - -cn_cbor* cn_cbor_array_create(CBOR_CONTEXT_COMMA cn_cbor_errback *errp) -{ - cn_cbor* ret; - INIT_CB(ret); - - ret->type = CN_CBOR_ARRAY; - ret->flags |= CN_CBOR_FL_COUNT; - - return ret; -} - -bool cn_cbor_array_append(cn_cbor* cb_array, - cn_cbor* cb_value, - cn_cbor_errback *errp) -{ - //Make sure input is an array. - if(!cb_array || !cb_value || cb_array->type != CN_CBOR_ARRAY) - { - if (errp) {errp->err = CN_CBOR_ERR_INVALID_PARAMETER;} - return false; - } - - cb_value->parent = cb_array; - cb_value->next = NULL; - if(cb_array->last_child) { - cb_array->last_child->next = cb_value; - } else { - cb_array->first_child = cb_value; - } - cb_array->last_child = cb_value; - cb_array->length++; - return true; -} - -#ifdef __cplusplus -} -#endif - -#endif /* CN_CBOR_C */ diff --git a/lib/ArduinoCbor/src/cn-cbor/cn-encoder.c b/lib/ArduinoCbor/src/cn-cbor/cn-encoder.c deleted file mode 100644 index ce52ea755..000000000 --- a/lib/ArduinoCbor/src/cn-cbor/cn-encoder.c +++ /dev/null @@ -1,307 +0,0 @@ -#ifndef CN_ENCODER_C -#define CN_ENCODER_C - -#ifdef __cplusplus -extern "C" { -#endif -#ifdef EMACS_INDENTATION_HELPER -} /* Duh. */ -#endif - -#include -#include -#include - -#include "cn-cbor.h" -#include "../cbor.h" - -#define hton8p(p) (*(uint8_t*)(p)) -#define hton16p(p) (htons(*(uint16_t*)(p))) -#define hton32p(p) (htonl(*(uint32_t*)(p))) -static uint64_t hton64p(const uint8_t *p) { - /* TODO: does this work on both BE and LE systems? */ - uint64_t ret = hton32p(p); - ret <<= 32; - ret |= hton32p(p+4); - return ret; -} - -typedef struct _write_state -{ - uint8_t *buf; - ssize_t offset; - ssize_t size; -} cn_write_state; - -#define ensure_writable(sz) if ((ws->offset<0) || (ws->offset + (sz) >= ws->size)) { \ - ws->offset = -1; \ - return; \ -} - -#define write_byte_and_data(b, data, sz) \ -ws->buf[ws->offset++] = (b); \ -memcpy(ws->buf+ws->offset, (data), (sz)); \ -ws->offset += sz; - -#define write_byte(b) \ -ws->buf[ws->offset++] = (b); \ - -#define write_byte_ensured(b) \ -ensure_writable(1); \ -write_byte(b); \ - -static uint8_t _xlate[] = { - IB_FALSE, /* CN_CBOR_FALSE */ - IB_TRUE, /* CN_CBOR_TRUE */ - IB_NIL, /* CN_CBOR_NULL */ - IB_UNDEF, /* CN_CBOR_UNDEF */ - IB_UNSIGNED, /* CN_CBOR_UINT */ - IB_NEGATIVE, /* CN_CBOR_INT */ - IB_BYTES, /* CN_CBOR_BYTES */ - IB_TEXT, /* CN_CBOR_TEXT */ - IB_BYTES, /* CN_CBOR_BYTES_CHUNKED */ - IB_TEXT, /* CN_CBOR_TEXT_CHUNKED */ - IB_ARRAY, /* CN_CBOR_ARRAY */ - IB_MAP, /* CN_CBOR_MAP */ - IB_TAG, /* CN_CBOR_TAG */ - IB_PRIM, /* CN_CBOR_SIMPLE */ - 0xFF, /* CN_CBOR_DOUBLE */ - 0xFF /* CN_CBOR_INVALID */ -}; - -static inline bool is_indefinite(const cn_cbor *cb) -{ - return (cb->flags & CN_CBOR_FL_INDEF) != 0; -} - -static void _write_positive(cn_write_state *ws, cn_cbor_type typ, uint64_t val) { - uint8_t ib; - - assert((size_t)typ < sizeof(_xlate)); - - ib = _xlate[typ]; - if (ib == 0xFF) { - ws->offset = -1; - return; - } - - if (val < 24) { - ensure_writable(1); - write_byte(ib | val); - } else if (val < 256) { - ensure_writable(2); - write_byte(ib | 24); - write_byte((uint8_t)val); - } else if (val < 65536) { - uint16_t be16 = (uint16_t)val; - ensure_writable(3); - be16 = hton16p(&be16); - write_byte_and_data(ib | 25, (const void*)&be16, 2); - } else if (val < 0x100000000L) { - uint32_t be32 = (uint32_t)val; - ensure_writable(5); - be32 = hton32p(&be32); - write_byte_and_data(ib | 26, (const void*)&be32, 4); - } else { - uint64_t be64; - ensure_writable(9); - be64 = hton64p((const uint8_t*)&val); - write_byte_and_data(ib | 27, (const void*)&be64, 8); - } -} - -#ifndef CBOR_NO_FLOAT -static void _write_double(cn_write_state *ws, double val) -{ - float float_val = val; - if (float_val == val) { /* 32 bits is enough and we aren't NaN */ - uint32_t be32; - uint16_t be16, u16; - union { - float f; - uint32_t u; - } u32; - u32.f = float_val; - if ((u32.u & 0x1FFF) == 0) { /* worth trying half */ - int s16 = (u32.u >> 16) & 0x8000; - int exp = (u32.u >> 23) & 0xff; - int mant = u32.u & 0x7fffff; - if (exp == 0 && mant == 0) - ; /* 0.0, -0.0 */ - else if (exp >= 113 && exp <= 142) /* normalized */ - s16 += ((exp - 112) << 10) + (mant >> 13); - else if (exp >= 103 && exp < 113) { /* denorm, exp16 = 0 */ - if (mant & ((1 << (126 - exp)) - 1)) - goto float32; /* loss of precision */ - s16 += ((mant + 0x800000) >> (126 - exp)); - } else if (exp == 255 && mant == 0) { /* Inf */ - s16 += 0x7c00; - } else - goto float32; /* loss of range */ - - ensure_writable(3); - u16 = s16; - be16 = hton16p((const uint8_t*)&u16); - - write_byte_and_data(IB_PRIM | 25, (const void*)&be16, 2); - return; - } - float32: - ensure_writable(5); - be32 = hton32p((const uint8_t*)&u32.u); - - write_byte_and_data(IB_PRIM | 26, (const void*)&be32, 4); - - } else if (val != val) { /* NaN -- we always write a half NaN*/ - ensure_writable(3); - write_byte_and_data(IB_PRIM | 25, (const void*)"\x7e\x00", 2); - } else { - uint64_t be64; - /* Copy the same problematic implementation from the decoder. */ - union { - double d; - uint64_t u; - } u64; - - u64.d = val; - - ensure_writable(9); - be64 = hton64p((const uint8_t*)&u64.u); - - write_byte_and_data(IB_PRIM | 27, (const void*)&be64, 8); - - } -} -#endif /* CBOR_NO_FLOAT */ - -// TODO: make public? -typedef void (*cn_visit_func)(const cn_cbor *cb, int depth, void *context); -static void _visit(const cn_cbor *cb, - cn_visit_func visitor, - cn_visit_func breaker, - void *context) -{ - const cn_cbor *p = cb; - int depth = 0; - while (p) - { -visit: - visitor(p, depth, context); - if (p->first_child) { - p = p->first_child; - depth++; - } else{ - // Empty indefinite - if (is_indefinite(p)) { - breaker(p->parent, depth, context); - } - if (p->next) { - p = p->next; - } else { - while (p->parent) { - depth--; - if (is_indefinite(p->parent)) { - breaker(p->parent, depth, context); - } - if (p->parent->next) { - p = p->parent->next; - goto visit; - } - p = p->parent; - } - return; - } - } - } -} - -#define CHECK(st) (st); \ -if (ws->offset < 0) { return; } - -void _encoder_visitor(const cn_cbor *cb, int depth, void *context) -{ - cn_write_state *ws = context; - UNUSED_PARAM(depth); - - switch (cb->type) { - case CN_CBOR_ARRAY: - if (is_indefinite(cb)) { - write_byte_ensured(IB_ARRAY | AI_INDEF); - } else { - CHECK(_write_positive(ws, CN_CBOR_ARRAY, cb->length)); - } - break; - case CN_CBOR_MAP: - if (is_indefinite(cb)) { - write_byte_ensured(IB_MAP | AI_INDEF); - } else { - CHECK(_write_positive(ws, CN_CBOR_MAP, cb->length/2)); - } - break; - case CN_CBOR_BYTES_CHUNKED: - case CN_CBOR_TEXT_CHUNKED: - write_byte_ensured(_xlate[cb->type] | AI_INDEF); - break; - - case CN_CBOR_TEXT: - case CN_CBOR_BYTES: - CHECK(_write_positive(ws, cb->type, cb->length)); - ensure_writable(cb->length); - memcpy(ws->buf+ws->offset, cb->v.str, cb->length); - ws->offset += cb->length; - break; - - case CN_CBOR_FALSE: - case CN_CBOR_TRUE: - case CN_CBOR_NULL: - case CN_CBOR_UNDEF: - write_byte_ensured(_xlate[cb->type]); - break; - - case CN_CBOR_TAG: - case CN_CBOR_UINT: - case CN_CBOR_SIMPLE: - CHECK(_write_positive(ws, cb->type, cb->v.uint)); - break; - - case CN_CBOR_INT: - assert(cb->v.sint < 0); - CHECK(_write_positive(ws, CN_CBOR_INT, ~(cb->v.sint))); - break; - - case CN_CBOR_DOUBLE: -#ifndef CBOR_NO_FLOAT - CHECK(_write_double(ws, cb->v.dbl)); -#endif /* CBOR_NO_FLOAT */ - break; - - case CN_CBOR_INVALID: - ws->offset = -1; - break; - } -} - -void _encoder_breaker(const cn_cbor *cb, int depth, void *context) -{ - cn_write_state *ws = context; - UNUSED_PARAM(cb); - UNUSED_PARAM(depth); - write_byte_ensured(IB_BREAK); -} - -ssize_t cn_cbor_encoder_write(uint8_t *buf, - size_t buf_offset, - size_t buf_size, - const cn_cbor *cb) -{ - cn_write_state ws = { buf, buf_offset, buf_size }; - _visit(cb, _encoder_visitor, _encoder_breaker, &ws); - if (ws.offset < 0) { return -1; } - return ws.offset - buf_offset; -} - -#ifdef __cplusplus -} -#endif - -#endif /* CN_CBOR_C */ diff --git a/lib/ArduinoCbor/src/cn-cbor/cn-error.c b/lib/ArduinoCbor/src/cn-cbor/cn-error.c deleted file mode 100644 index 4953cc9b6..000000000 --- a/lib/ArduinoCbor/src/cn-cbor/cn-error.c +++ /dev/null @@ -1,13 +0,0 @@ -const char *cn_cbor_error_str[] = { - "CN_CBOR_NO_ERROR", - "CN_CBOR_ERR_OUT_OF_DATA", - "CN_CBOR_ERR_NOT_ALL_DATA_CONSUMED", - "CN_CBOR_ERR_ODD_SIZE_INDEF_MAP", - "CN_CBOR_ERR_BREAK_OUTSIDE_INDEF", - "CN_CBOR_ERR_MT_UNDEF_FOR_INDEF", - "CN_CBOR_ERR_RESERVED_AI", - "CN_CBOR_ERR_WRONG_NESTING_IN_INDEF_STRING", - "CN_CBOR_ERR_INVALID_PARAMETER", - "CN_CBOR_ERR_OUT_OF_MEMORY", - "CN_CBOR_ERR_FLOAT_NOT_SUPPORTED" -}; diff --git a/lib/ArduinoCbor/src/cn-cbor/cn-get.c b/lib/ArduinoCbor/src/cn-cbor/cn-get.c deleted file mode 100644 index 630b2a71d..000000000 --- a/lib/ArduinoCbor/src/cn-cbor/cn-get.c +++ /dev/null @@ -1,62 +0,0 @@ -#include -#include -#include - -#include "cn-cbor.h" - -cn_cbor* cn_cbor_mapget_int(const cn_cbor* cb, int key) { - cn_cbor* cp; - assert(cb); - for (cp = cb->first_child; cp && cp->next; cp = cp->next->next) { - switch(cp->type) { - case CN_CBOR_UINT: - if (cp->v.uint == (unsigned long)key) { - return cp->next; - } - case CN_CBOR_INT: - if (cp->v.sint == (long)key) { - return cp->next; - } - break; - default: - ; // skip non-integer keys - } - } - return NULL; -} - -cn_cbor* cn_cbor_mapget_string(const cn_cbor* cb, const char* key) { - cn_cbor *cp; - int keylen; - assert(cb); - assert(key); - keylen = strlen(key); - for (cp = cb->first_child; cp && cp->next; cp = cp->next->next) { - switch(cp->type) { - case CN_CBOR_TEXT: // fall-through - case CN_CBOR_BYTES: - if (keylen != cp->length) { - continue; - } - if (memcmp(key, cp->v.str, keylen) == 0) { - return cp->next; - } - default: - ; // skip non-string keys - } - } - return NULL; -} - -cn_cbor* cn_cbor_index(const cn_cbor* cb, unsigned int idx) { - cn_cbor *cp; - unsigned int i = 0; - assert(cb); - for (cp = cb->first_child; cp; cp = cp->next) { - if (i == idx) { - return cp; - } - i++; - } - return NULL; -} diff --git a/lib/tinycbor/cbor.dox b/lib/tinycbor/cbor.dox new file mode 100644 index 000000000..0bf5c54c2 --- /dev/null +++ b/lib/tinycbor/cbor.dox @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +/** + * \mainpage + * The TinyCBOR $(VERSION) library is a small CBOR encoder and decoder library, + * optimized for very fast operation with very small footprint. The main encoder + * and decoder functions do not allocate memory. + * + * TinyCBOR is divided into the following groups of functions and structures: + * - \ref CborGlobals + * - \ref CborEncoding + * - \ref CborParsing + * - \ref CborPretty + * - \ref CborToJson + */ + +/** + * \file + * The is the main header in TinyCBOR and defines the constants used by most functions + * as well as the structures for encoding (CborEncoder) and decoding (CborValue). + * + * \sa + */ + +/** + * \file + * The file contains the routines that are used to convert a CBOR + * data stream into JSON. + * + * \sa + */ + +/** + * \defgroup CborGlobals Global constants + * \brief Constants used by all TinyCBOR function groups. + */ + +/** + * \addtogroup CborGlobals + * @{ + */ + +/** + * \var size_t CborIndefiniteLength + * + * This variable is a constant used to indicate that the length of the map or + * array is not yet determined. It is used in functions + * cbor_encoder_create_map() and cbor_encoder_create_array() + */ + +/** + * \enum CborType + * The CborType enum contains the types known to TinyCBOR. + * + * \value CborIntegerType Type is an integer value, positive, negative or zero + * \value CborByteStringType Type is a string of arbitrary raw bytes + * \value CborTextStringType Type is a text string encoded in UTF-8 + * \value CborArrayType Type is a CBOR array + * \value CborMapType Type is a CBOR map (an associative container with key and value pairs) + * \value CborTagType Type is a CBOR tag (a 64-bit integer describing the item that follows, see CborKnownTags) + * \value CborSimpleType Type is one of the CBOR Simple Types + * \value CborBooleanType Type is a boolean (true or false) + * \value CborNullType Type encodes a null + * \value CborUndefinedType Type encodes an undefined value + * \value CborHalfFloatType Type is an IEEE 754 half precision (16-bit) floating point type + * \value CborFloatType Type is an IEEE 754 single precision (32-bit) floating point type + * \value CborDoubleType Type is an IEEE 754 double precision (64-bit) floating point type + * \value CborInvalidType Type is not valid (this value is used to indicate error conditions) + */ + +/** + * \enum CborKnownTags + * The CborKnownTags enum contains known tags specified in RFC 7049, for use by the application. + * TinyCBOR does not usually interpret the meaning of these tags and does not add them to the + * output stream, unless specifically instructed to do so in functions for that effect. + * + * \value CborDateTimeStringTag Text string contains a date-time encoded in RFC 3339 format, "YYYY-MM-DD hh:mm:ss+zzzz" + * \value CborUnixTime_tTag Number is a Unix time_t quantity, the number of seconds since 1970-01-01 midnight UTC + * \value CborPositiveBignumTag Item is a CBOR byte string encoding a positive integer of arbitrary precision + * \value CborNegativeBignumTag Item is a CBOR byte string encoding a negative integer of arbitrary precision + * \value CborDecimalTag Item is a CBOR array of two integers encoding a fixed-point decimal + * \value CborBigfloatTag Item is a bigfloat + * \value CborExpectedBase64urlTag Item is a CBOR byte string that is expected to be encoded as Base64Url + * \value CborExpectedBase64Tag Item is a CBOR byte string that is expected to be encoded as Base64 + * \value CborExpectedBase16Tag Item is a CBOR byte string that is expected to be encoded as Base16 (also known as "hexdump") + * \value CborUriTag Item is a CBOR text string containing a URI (RFC 3986) or IRI (RFC 3987) + * \value CborBase64urlTag Item is a CBOR text string that was encoded as Base64Url + * \value CborBase64Tag Item is a CBOR text string that was encoded as Base64 + * \value CborRegularExpressionTag Item is a CBOR text string containing a regular expression + * \value CborMimeMessageTag Item is a CBOR text string containing a MIME message (RFC 2045, 2046, 2047, 2822) + * \value CborSignatureTag Item contains CBOR-encoded data. + * This tag is also used as "file magic," marking a file as containing CBOR + */ + +/** + * \typedef CborTag + * This typedef is an unsigned 64-bit integer. Known CBOR tags can be used from the CborKnownTags enum + * but the user application may use other tag values than the ones specified in RFC 7049. + */ + +/** @} */ diff --git a/lib/tinycbor/cbor.h b/lib/tinycbor/cbor.h new file mode 100644 index 000000000..fd145be2e --- /dev/null +++ b/lib/tinycbor/cbor.h @@ -0,0 +1,606 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#ifndef CBOR_H +#define CBOR_H + +#ifndef assert +#include +#endif +#include +#include +#include +#include +#include + +#include "tinycbor-version.h" + +#define TINYCBOR_VERSION ((TINYCBOR_VERSION_MAJOR << 16) | (TINYCBOR_VERSION_MINOR << 8) | TINYCBOR_VERSION_PATCH) + +#ifdef __cplusplus +extern "C" { +#else +#include +#endif + +#ifndef SIZE_MAX +/* Some systems fail to define SIZE_MAX in , even though C99 requires it... + * Conversion from signed to unsigned is defined in 6.3.1.3 (Signed and unsigned integers) p2, + * which says: "the value is converted by repeatedly adding or subtracting one more than the + * maximum value that can be represented in the new type until the value is in the range of the + * new type." + * So -1 gets converted to size_t by adding SIZE_MAX + 1, which results in SIZE_MAX. + */ +# define SIZE_MAX ((size_t)-1) +#endif + +#ifndef CBOR_API +# define CBOR_API +#endif +#ifndef CBOR_PRIVATE_API +# define CBOR_PRIVATE_API +#endif +#ifndef CBOR_INLINE_API +# if defined(__cplusplus) +# define CBOR_INLINE inline +# define CBOR_INLINE_API inline +# else +# define CBOR_INLINE_API static CBOR_INLINE +# if defined(_MSC_VER) +# define CBOR_INLINE __inline +# elif defined(__GNUC__) +# define CBOR_INLINE __inline__ +# elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +# define CBOR_INLINE inline +# else +# define CBOR_INLINE +# endif +# endif +#endif + +typedef enum CborType { + CborIntegerType = 0x00, + CborByteStringType = 0x40, + CborTextStringType = 0x60, + CborArrayType = 0x80, + CborMapType = 0xa0, + CborTagType = 0xc0, + CborSimpleType = 0xe0, + CborBooleanType = 0xf5, + CborNullType = 0xf6, + CborUndefinedType = 0xf7, + CborHalfFloatType = 0xf9, + CborFloatType = 0xfa, + CborDoubleType = 0xfb, + + CborInvalidType = 0xff /* equivalent to the break byte, so it will never be used */ +} CborType; + +typedef uint64_t CborTag; +typedef enum CborKnownTags { + CborDateTimeStringTag = 0, + CborUnixTime_tTag = 1, + CborPositiveBignumTag = 2, + CborNegativeBignumTag = 3, + CborDecimalTag = 4, + CborBigfloatTag = 5, + CborCOSE_Encrypt0Tag = 16, + CborCOSE_Mac0Tag = 17, + CborCOSE_Sign1Tag = 18, + CborExpectedBase64urlTag = 21, + CborExpectedBase64Tag = 22, + CborExpectedBase16Tag = 23, + CborEncodedCborTag = 24, + CborUrlTag = 32, + CborBase64urlTag = 33, + CborBase64Tag = 34, + CborRegularExpressionTag = 35, + CborMimeMessageTag = 36, + CborCOSE_EncryptTag = 96, + CborCOSE_MacTag = 97, + CborCOSE_SignTag = 98, + CborSignatureTag = 55799 +} CborKnownTags; + +/* #define the constants so we can check with #ifdef */ +#define CborDateTimeStringTag CborDateTimeStringTag +#define CborUnixTime_tTag CborUnixTime_tTag +#define CborPositiveBignumTag CborPositiveBignumTag +#define CborNegativeBignumTag CborNegativeBignumTag +#define CborDecimalTag CborDecimalTag +#define CborBigfloatTag CborBigfloatTag +#define CborCOSE_Encrypt0Tag CborCOSE_Encrypt0Tag +#define CborCOSE_Mac0Tag CborCOSE_Mac0Tag +#define CborCOSE_Sign1Tag CborCOSE_Sign1Tag +#define CborExpectedBase64urlTag CborExpectedBase64urlTag +#define CborExpectedBase64Tag CborExpectedBase64Tag +#define CborExpectedBase16Tag CborExpectedBase16Tag +#define CborEncodedCborTag CborEncodedCborTag +#define CborUrlTag CborUrlTag +#define CborBase64urlTag CborBase64urlTag +#define CborBase64Tag CborBase64Tag +#define CborRegularExpressionTag CborRegularExpressionTag +#define CborMimeMessageTag CborMimeMessageTag +#define CborCOSE_EncryptTag CborCOSE_EncryptTag +#define CborCOSE_MacTag CborCOSE_MacTag +#define CborCOSE_SignTag CborCOSE_SignTag +#define CborSignatureTag CborSignatureTag + +/* Error API */ + +typedef enum CborError { + CborNoError = 0, + + /* errors in all modes */ + CborUnknownError, + CborErrorUnknownLength, /* request for length in array, map, or string with indeterminate length */ + CborErrorAdvancePastEOF, + CborErrorIO, + + /* parser errors streaming errors */ + CborErrorGarbageAtEnd = 256, + CborErrorUnexpectedEOF, + CborErrorUnexpectedBreak, + CborErrorUnknownType, /* can only happen in major type 7 */ + CborErrorIllegalType, /* type not allowed here */ + CborErrorIllegalNumber, + CborErrorIllegalSimpleType, /* types of value less than 32 encoded in two bytes */ + + /* parser errors in strict mode parsing only */ + CborErrorUnknownSimpleType = 512, + CborErrorUnknownTag, + CborErrorInappropriateTagForType, + CborErrorDuplicateObjectKeys, + CborErrorInvalidUtf8TextString, + CborErrorExcludedType, + CborErrorExcludedValue, + CborErrorImproperValue, + CborErrorOverlongEncoding, + CborErrorMapKeyNotString, + CborErrorMapNotSorted, + CborErrorMapKeysNotUnique, + + /* encoder errors */ + CborErrorTooManyItems = 768, + CborErrorTooFewItems, + + /* internal implementation errors */ + CborErrorDataTooLarge = 1024, + CborErrorNestingTooDeep, + CborErrorUnsupportedType, + + /* errors in converting to JSON */ + CborErrorJsonObjectKeyIsAggregate = 1280, + CborErrorJsonObjectKeyNotString, + CborErrorJsonNotImplemented, + + CborErrorOutOfMemory = (int) (~0U / 2 + 1), + CborErrorInternalError = (int) (~0U / 2) /* INT_MAX on two's complement machines */ +} CborError; + +CBOR_API const char *cbor_error_string(CborError error); + +/* Encoder API */ +struct CborEncoder +{ + union { + uint8_t *ptr; + ptrdiff_t bytes_needed; + } data; + const uint8_t *end; + size_t remaining; + int flags; +}; +typedef struct CborEncoder CborEncoder; + +static const size_t CborIndefiniteLength = SIZE_MAX; + +CBOR_API void cbor_encoder_init(CborEncoder *encoder, uint8_t *buffer, size_t size, int flags); +CBOR_API CborError cbor_encode_uint(CborEncoder *encoder, uint64_t value); +CBOR_API CborError cbor_encode_int(CborEncoder *encoder, int64_t value); +CBOR_API CborError cbor_encode_negative_int(CborEncoder *encoder, uint64_t absolute_value); +CBOR_API CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value); +CBOR_API CborError cbor_encode_tag(CborEncoder *encoder, CborTag tag); +CBOR_API CborError cbor_encode_text_string(CborEncoder *encoder, const char *string, size_t length); +CBOR_INLINE_API CborError cbor_encode_text_stringz(CborEncoder *encoder, const char *string) +{ return cbor_encode_text_string(encoder, string, strlen(string)); } +CBOR_API CborError cbor_encode_byte_string(CborEncoder *encoder, const uint8_t *string, size_t length); +CBOR_API CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value); + +CBOR_INLINE_API CborError cbor_encode_boolean(CborEncoder *encoder, bool value) +{ return cbor_encode_simple_value(encoder, (int)value - 1 + (CborBooleanType & 0x1f)); } +CBOR_INLINE_API CborError cbor_encode_null(CborEncoder *encoder) +{ return cbor_encode_simple_value(encoder, CborNullType & 0x1f); } +CBOR_INLINE_API CborError cbor_encode_undefined(CborEncoder *encoder) +{ return cbor_encode_simple_value(encoder, CborUndefinedType & 0x1f); } + +CBOR_INLINE_API CborError cbor_encode_half_float(CborEncoder *encoder, const void *value) +{ return cbor_encode_floating_point(encoder, CborHalfFloatType, value); } +CBOR_INLINE_API CborError cbor_encode_float(CborEncoder *encoder, float value) +{ return cbor_encode_floating_point(encoder, CborFloatType, &value); } +CBOR_INLINE_API CborError cbor_encode_double(CborEncoder *encoder, double value) +{ return cbor_encode_floating_point(encoder, CborDoubleType, &value); } + +CBOR_API CborError cbor_encoder_create_array(CborEncoder *encoder, CborEncoder *arrayEncoder, size_t length); +CBOR_API CborError cbor_encoder_create_map(CborEncoder *encoder, CborEncoder *mapEncoder, size_t length); +CBOR_API CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder *containerEncoder); +CBOR_API CborError cbor_encoder_close_container_checked(CborEncoder *encoder, const CborEncoder *containerEncoder); + +CBOR_INLINE_API uint8_t *_cbor_encoder_get_buffer_pointer(const CborEncoder *encoder) +{ + return encoder->data.ptr; +} + +CBOR_INLINE_API size_t cbor_encoder_get_buffer_size(const CborEncoder *encoder, const uint8_t *buffer) +{ + return (size_t)(encoder->data.ptr - buffer); +} + +CBOR_INLINE_API size_t cbor_encoder_get_extra_bytes_needed(const CborEncoder *encoder) +{ + return encoder->end ? 0 : (size_t)encoder->data.bytes_needed; +} + +/* Parser API */ + +enum CborParserIteratorFlags +{ + CborIteratorFlag_IntegerValueTooLarge = 0x01, + CborIteratorFlag_NegativeInteger = 0x02, + CborIteratorFlag_IteratingStringChunks = 0x02, + CborIteratorFlag_UnknownLength = 0x04, + CborIteratorFlag_ContainerIsMap = 0x20 +}; + +struct CborParser +{ + const uint8_t *end; + uint32_t flags; +}; +typedef struct CborParser CborParser; + +struct CborValue +{ + const CborParser *parser; + const uint8_t *ptr; + uint32_t remaining; + uint16_t extra; + uint8_t type; + uint8_t flags; +}; +typedef struct CborValue CborValue; + +CBOR_API CborError cbor_parser_init(const uint8_t *buffer, size_t size, uint32_t flags, CborParser *parser, CborValue *it); + +CBOR_API CborError cbor_value_validate_basic(const CborValue *it); + +CBOR_INLINE_API bool cbor_value_at_end(const CborValue *it) +{ return it->remaining == 0; } +CBOR_INLINE_API const uint8_t *cbor_value_get_next_byte(const CborValue *it) +{ return it->ptr; } +CBOR_API CborError cbor_value_advance_fixed(CborValue *it); +CBOR_API CborError cbor_value_advance(CborValue *it); +CBOR_INLINE_API bool cbor_value_is_container(const CborValue *it) +{ return it->type == CborArrayType || it->type == CborMapType; } +CBOR_API CborError cbor_value_enter_container(const CborValue *it, CborValue *recursed); +CBOR_API CborError cbor_value_leave_container(CborValue *it, const CborValue *recursed); + +CBOR_PRIVATE_API uint64_t _cbor_value_decode_int64_internal(const CborValue *value); +CBOR_INLINE_API uint64_t _cbor_value_extract_int64_helper(const CborValue *value) +{ + return value->flags & CborIteratorFlag_IntegerValueTooLarge ? + _cbor_value_decode_int64_internal(value) : value->extra; +} + +CBOR_INLINE_API bool cbor_value_is_valid(const CborValue *value) +{ return value && value->type != CborInvalidType; } +CBOR_INLINE_API CborType cbor_value_get_type(const CborValue *value) +{ return (CborType)value->type; } + +/* Null & undefined type */ +CBOR_INLINE_API bool cbor_value_is_null(const CborValue *value) +{ return value->type == CborNullType; } +CBOR_INLINE_API bool cbor_value_is_undefined(const CborValue *value) +{ return value->type == CborUndefinedType; } + +/* Booleans */ +CBOR_INLINE_API bool cbor_value_is_boolean(const CborValue *value) +{ return value->type == CborBooleanType; } +CBOR_INLINE_API CborError cbor_value_get_boolean(const CborValue *value, bool *result) +{ + assert(cbor_value_is_boolean(value)); + *result = !!value->extra; + return CborNoError; +} + +/* Simple types */ +CBOR_INLINE_API bool cbor_value_is_simple_type(const CborValue *value) +{ return value->type == CborSimpleType; } +CBOR_INLINE_API CborError cbor_value_get_simple_type(const CborValue *value, uint8_t *result) +{ + assert(cbor_value_is_simple_type(value)); + *result = (uint8_t)value->extra; + return CborNoError; +} + +/* Integers */ +CBOR_INLINE_API bool cbor_value_is_integer(const CborValue *value) +{ return value->type == CborIntegerType; } +CBOR_INLINE_API bool cbor_value_is_unsigned_integer(const CborValue *value) +{ return cbor_value_is_integer(value) && (value->flags & CborIteratorFlag_NegativeInteger) == 0; } +CBOR_INLINE_API bool cbor_value_is_negative_integer(const CborValue *value) +{ return cbor_value_is_integer(value) && (value->flags & CborIteratorFlag_NegativeInteger); } + +CBOR_INLINE_API CborError cbor_value_get_raw_integer(const CborValue *value, uint64_t *result) +{ + assert(cbor_value_is_integer(value)); + *result = _cbor_value_extract_int64_helper(value); + return CborNoError; +} + +CBOR_INLINE_API CborError cbor_value_get_uint64(const CborValue *value, uint64_t *result) +{ + assert(cbor_value_is_unsigned_integer(value)); + *result = _cbor_value_extract_int64_helper(value); + return CborNoError; +} + +CBOR_INLINE_API CborError cbor_value_get_int64(const CborValue *value, int64_t *result) +{ + assert(cbor_value_is_integer(value)); + *result = (int64_t) _cbor_value_extract_int64_helper(value); + if (value->flags & CborIteratorFlag_NegativeInteger) + *result = -*result - 1; + return CborNoError; +} + +CBOR_INLINE_API CborError cbor_value_get_int(const CborValue *value, int *result) +{ + assert(cbor_value_is_integer(value)); + *result = (int) _cbor_value_extract_int64_helper(value); + if (value->flags & CborIteratorFlag_NegativeInteger) + *result = -*result - 1; + return CborNoError; +} + +CBOR_API CborError cbor_value_get_int64_checked(const CborValue *value, int64_t *result); +CBOR_API CborError cbor_value_get_int_checked(const CborValue *value, int *result); + +CBOR_INLINE_API bool cbor_value_is_length_known(const CborValue *value) +{ return (value->flags & CborIteratorFlag_UnknownLength) == 0; } + +/* Tags */ +CBOR_INLINE_API bool cbor_value_is_tag(const CborValue *value) +{ return value->type == CborTagType; } +CBOR_INLINE_API CborError cbor_value_get_tag(const CborValue *value, CborTag *result) +{ + assert(cbor_value_is_tag(value)); + *result = _cbor_value_extract_int64_helper(value); + return CborNoError; +} +CBOR_API CborError cbor_value_skip_tag(CborValue *it); + +/* Strings */ +CBOR_INLINE_API bool cbor_value_is_byte_string(const CborValue *value) +{ return value->type == CborByteStringType; } +CBOR_INLINE_API bool cbor_value_is_text_string(const CborValue *value) +{ return value->type == CborTextStringType; } + +CBOR_INLINE_API CborError cbor_value_get_string_length(const CborValue *value, size_t *length) +{ + uint64_t v; + assert(cbor_value_is_byte_string(value) || cbor_value_is_text_string(value)); + if (!cbor_value_is_length_known(value)) + return CborErrorUnknownLength; + v = _cbor_value_extract_int64_helper(value); + *length = (size_t)v; + if (*length != v) + return CborErrorDataTooLarge; + return CborNoError; +} + +CBOR_PRIVATE_API CborError _cbor_value_copy_string(const CborValue *value, void *buffer, + size_t *buflen, CborValue *next); +CBOR_PRIVATE_API CborError _cbor_value_dup_string(const CborValue *value, void **buffer, + size_t *buflen, CborValue *next); + +CBOR_API CborError cbor_value_calculate_string_length(const CborValue *value, size_t *length); + +CBOR_INLINE_API CborError cbor_value_copy_text_string(const CborValue *value, char *buffer, + size_t *buflen, CborValue *next) +{ + assert(cbor_value_is_text_string(value)); + return _cbor_value_copy_string(value, buffer, buflen, next); +} +CBOR_INLINE_API CborError cbor_value_copy_byte_string(const CborValue *value, uint8_t *buffer, + size_t *buflen, CborValue *next) +{ + assert(cbor_value_is_byte_string(value)); + return _cbor_value_copy_string(value, buffer, buflen, next); +} + +CBOR_INLINE_API CborError cbor_value_dup_text_string(const CborValue *value, char **buffer, + size_t *buflen, CborValue *next) +{ + assert(cbor_value_is_text_string(value)); + return _cbor_value_dup_string(value, (void **)buffer, buflen, next); +} +CBOR_INLINE_API CborError cbor_value_dup_byte_string(const CborValue *value, uint8_t **buffer, + size_t *buflen, CborValue *next) +{ + assert(cbor_value_is_byte_string(value)); + return _cbor_value_dup_string(value, (void **)buffer, buflen, next); +} + +CBOR_API CborError cbor_value_text_string_equals(const CborValue *value, const char *string, bool *result); + +/* Maps and arrays */ +CBOR_INLINE_API bool cbor_value_is_array(const CborValue *value) +{ return value->type == CborArrayType; } +CBOR_INLINE_API bool cbor_value_is_map(const CborValue *value) +{ return value->type == CborMapType; } + +CBOR_INLINE_API CborError cbor_value_get_array_length(const CborValue *value, size_t *length) +{ + uint64_t v; + assert(cbor_value_is_array(value)); + if (!cbor_value_is_length_known(value)) + return CborErrorUnknownLength; + v = _cbor_value_extract_int64_helper(value); + *length = (size_t)v; + if (*length != v) + return CborErrorDataTooLarge; + return CborNoError; +} + +CBOR_INLINE_API CborError cbor_value_get_map_length(const CborValue *value, size_t *length) +{ + uint64_t v; + assert(cbor_value_is_map(value)); + if (!cbor_value_is_length_known(value)) + return CborErrorUnknownLength; + v = _cbor_value_extract_int64_helper(value); + *length = (size_t)v; + if (*length != v) + return CborErrorDataTooLarge; + return CborNoError; +} + +CBOR_API CborError cbor_value_map_find_value(const CborValue *map, const char *string, CborValue *element); + +/* Floating point */ +CBOR_INLINE_API bool cbor_value_is_half_float(const CborValue *value) +{ return value->type == CborHalfFloatType; } +CBOR_API CborError cbor_value_get_half_float(const CborValue *value, void *result); + +CBOR_INLINE_API bool cbor_value_is_float(const CborValue *value) +{ return value->type == CborFloatType; } +CBOR_INLINE_API CborError cbor_value_get_float(const CborValue *value, float *result) +{ + uint32_t data; + assert(cbor_value_is_float(value)); + assert(value->flags & CborIteratorFlag_IntegerValueTooLarge); + data = (uint32_t)_cbor_value_decode_int64_internal(value); + memcpy(result, &data, sizeof(*result)); + return CborNoError; +} + +CBOR_INLINE_API bool cbor_value_is_double(const CborValue *value) +{ return value->type == CborDoubleType; } +CBOR_INLINE_API CborError cbor_value_get_double(const CborValue *value, double *result) +{ + uint64_t data; + assert(cbor_value_is_double(value)); + assert(value->flags & CborIteratorFlag_IntegerValueTooLarge); + data = _cbor_value_decode_int64_internal(value); + memcpy(result, &data, sizeof(*result)); + return CborNoError; +} + +/* Validation API */ + +enum CborValidationFlags { + /* Bit mapping: + * bits 0-7 (8 bits): canonical format + * bits 8-11 (4 bits): canonical format & strict mode + * bits 12-20 (8 bits): strict mode + * bits 21-31 (10 bits): other + */ + + CborValidateShortestIntegrals = 0x0001, + CborValidateShortestFloatingPoint = 0x0002, + CborValidateShortestNumbers = CborValidateShortestIntegrals | CborValidateShortestFloatingPoint, + CborValidateNoIndeterminateLength = 0x0100, + CborValidateMapIsSorted = 0x0200 | CborValidateNoIndeterminateLength, + + CborValidateCanonicalFormat = 0x0fff, + + CborValidateMapKeysAreUnique = 0x1000 | CborValidateMapIsSorted, + CborValidateTagUse = 0x2000, + CborValidateUtf8 = 0x4000, + + CborValidateStrictMode = 0xfff00, + + CborValidateMapKeysAreString = 0x100000, + CborValidateNoUndefined = 0x200000, + CborValidateNoTags = 0x400000, + CborValidateFiniteFloatingPoint = 0x800000, + /* unused = 0x1000000, */ + /* unused = 0x2000000, */ + + CborValidateNoUnknownSimpleTypesSA = 0x4000000, + CborValidateNoUnknownSimpleTypes = 0x8000000 | CborValidateNoUnknownSimpleTypesSA, + CborValidateNoUnknownTagsSA = 0x10000000, + CborValidateNoUnknownTagsSR = 0x20000000 | CborValidateNoUnknownTagsSA, + CborValidateNoUnknownTags = 0x40000000 | CborValidateNoUnknownTagsSR, + + CborValidateCompleteData = (int)0x80000000, + + CborValidateStrictest = (int)~0U, + CborValidateBasic = 0 +}; + +CBOR_API CborError cbor_value_validate(const CborValue *it, uint32_t flags); + +/* Human-readable (dump) API */ + +enum CborPrettyFlags { + CborPrettyNumericEncodingIndicators = 0x01, + CborPrettyTextualEncodingIndicators = 0, + + CborPrettyIndicateIndeterminateLength = 0x02, + CborPrettyIndicateIndetermineLength = CborPrettyIndicateIndeterminateLength, /* deprecated */ + CborPrettyIndicateOverlongNumbers = 0x04, + + CborPrettyShowStringFragments = 0x100, + CborPrettyMergeStringFragments = 0, + + CborPrettyDefaultFlags = CborPrettyIndicateIndeterminateLength +}; + +typedef CborError (*CborStreamFunction)(void *token, const char *fmt, ...) +#ifdef __GNUC__ + __attribute__((__format__(printf, 2, 3))) +#endif +; + +CBOR_API CborError cbor_value_to_pretty_stream(CborStreamFunction streamFunction, void *token, CborValue *value, int flags); + +/* The following API requires a hosted C implementation (uses FILE*) */ +#if !defined(__STDC_HOSTED__) || __STDC_HOSTED__-0 == 1 +CBOR_API CborError cbor_value_to_pretty_advance_flags(FILE *out, CborValue *value, int flags); +CBOR_API CborError cbor_value_to_pretty_advance(FILE *out, CborValue *value); +CBOR_INLINE_API CborError cbor_value_to_pretty(FILE *out, const CborValue *value) +{ + CborValue copy = *value; + return cbor_value_to_pretty_advance_flags(out, ©, CborPrettyDefaultFlags); +} +#endif /* __STDC_HOSTED__ check */ + +#ifdef __cplusplus +} +#endif + +#endif /* CBOR_H */ + diff --git a/lib/tinycbor/cborencoder.c b/lib/tinycbor/cborencoder.c new file mode 100644 index 000000000..adb92fd88 --- /dev/null +++ b/lib/tinycbor/cborencoder.c @@ -0,0 +1,645 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE 1 +#endif +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE 1 +#endif +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" +#include "cborinternal_p.h" +#include "compilersupport_p.h" + +#include +#include + +/** + * \defgroup CborEncoding Encoding to CBOR + * \brief Group of functions used to encode data to CBOR. + * + * CborEncoder is used to encode data into a CBOR stream. The outermost + * CborEncoder is initialized by calling cbor_encoder_init(), with the buffer + * where the CBOR stream will be stored. The outermost CborEncoder is usually + * used to encode exactly one item, most often an array or map. It is possible + * to encode more than one item, but care must then be taken on the decoder + * side to ensure the state is reset after each item was decoded. + * + * Nested CborEncoder objects are created using cbor_encoder_create_array() and + * cbor_encoder_create_map(), later closed with cbor_encoder_close_container() + * or cbor_encoder_close_container_checked(). The pairs of creation and closing + * must be exactly matched and their parameters are always the same. + * + * CborEncoder writes directly to the user-supplied buffer, without extra + * buffering. CborEncoder does not allocate memory and CborEncoder objects are + * usually created on the stack of the encoding functions. + * + * The example below initializes a CborEncoder object with a buffer and encodes + * a single integer. + * + * \code + * uint8_t buf[16]; + * CborEncoder encoder; + * cbor_encoder_init(&encoder, &buf, sizeof(buf), 0); + * cbor_encode_int(&encoder, some_value); + * \endcode + * + * As explained before, usually the outermost CborEncoder object is used to add + * one array or map, which in turn contains multiple elements. The example + * below creates a CBOR map with one element: a key "foo" and a boolean value. + * + * \code + * uint8_t buf[16]; + * CborEncoder encoder, mapEncoder; + * cbor_encoder_init(&encoder, &buf, sizeof(buf), 0); + * cbor_encoder_create_map(&encoder, &mapEncoder, 1); + * cbor_encode_text_stringz(&mapEncoder, "foo"); + * cbor_encode_boolean(&mapEncoder, some_value); + * cbor_encoder_close_container(&encoder, &mapEncoder); + * \endcode + * + *

Error checking and buffer size

+ * + * All functions operating on CborEncoder return a condition of type CborError. + * If the encoding was successful, they return CborNoError. Some functions do + * extra checking on the input provided and may return some other error + * conditions (for example, cbor_encode_simple_value() checks that the type is + * of the correct type). + * + * In addition, all functions check whether the buffer has enough bytes to + * encode the item being appended. If that is not possible, they return + * CborErrorOutOfMemory. + * + * It is possible to continue with the encoding of data past the first function + * that returns CborErrorOutOfMemory. CborEncoder functions will not overrun + * the buffer, but will instead count how many more bytes are needed to + * complete the encoding. At the end, you can obtain that count by calling + * cbor_encoder_get_extra_bytes_needed(). + * + * \section1 Finalizing the encoding + * + * Once all items have been appended and the containers have all been properly + * closed, the user-supplied buffer will contain the CBOR stream and may be + * immediately used. To obtain the size of the buffer, call + * cbor_encoder_get_buffer_size() with the original buffer pointer. + * + * The example below illustrates how one can encode an item with error checking + * and then pass on the buffer for network sending. + * + * \code + * uint8_t buf[16]; + * CborError err; + * CborEncoder encoder, mapEncoder; + * cbor_encoder_init(&encoder, &buf, sizeof(buf), 0); + * err = cbor_encoder_create_map(&encoder, &mapEncoder, 1); + * if (!err) + * return err; + * err = cbor_encode_text_stringz(&mapEncoder, "foo"); + * if (!err) + * return err; + * err = cbor_encode_boolean(&mapEncoder, some_value); + * if (!err) + * return err; + * err = cbor_encoder_close_container_checked(&encoder, &mapEncoder); + * if (!err) + * return err; + * + * size_t len = cbor_encoder_get_buffer_size(&encoder, buf); + * send_payload(buf, len); + * return CborNoError; + * \endcode + * + * Finally, the example below expands on the one above and also + * deals with dynamically growing the buffer if the initial allocation wasn't + * big enough. Note the two places where the error checking was replaced with + * an cbor_assertion, showing where the author assumes no error can occur. + * + * \code + * uint8_t *encode_string_array(const char **strings, int n, size_t *bufsize) + * { + * CborError err; + * CborEncoder encoder, arrayEncoder; + * size_t size = 256; + * uint8_t *buf = NULL; + * + * while (1) { + * int i; + * size_t more_bytes; + * uint8_t *nbuf = realloc(buf, size); + * if (nbuf == NULL) + * goto error; + * buf = nbuf; + * + * cbor_encoder_init(&encoder, &buf, size, 0); + * err = cbor_encoder_create_array(&encoder, &arrayEncoder, n); + * cbor_assert(err); // can't fail, the buffer is always big enough + * + * for (i = 0; i < n; ++i) { + * err = cbor_encode_text_stringz(&arrayEncoder, strings[i]); + * if (err && err != CborErrorOutOfMemory) + * goto error; + * } + * + * err = cbor_encoder_close_container_checked(&encoder, &arrayEncoder); + * cbor_assert(err); // shouldn't fail! + * + * more_bytes = cbor_encoder_get_extra_bytes_needed(encoder); + * if (more_size) { + * // buffer wasn't big enough, try again + * size += more_bytes; + * continue; + * } + * + * *bufsize = cbor_encoder_get_buffer_size(encoder, buf); + * return buf; + * } + * error: + * free(buf); + * return NULL; + * } + * \endcode + */ + +/** + * \addtogroup CborEncoding + * @{ + */ + +/** + * \struct CborEncoder + * Structure used to encode to CBOR. + */ + +/** + * Initializes a CborEncoder structure \a encoder by pointing it to buffer \a + * buffer of size \a size. The \a flags field is currently unused and must be + * zero. + */ +void cbor_encoder_init(CborEncoder *encoder, uint8_t *buffer, size_t size, int flags) +{ + encoder->data.ptr = buffer; + encoder->end = buffer + size; + encoder->remaining = 2; + encoder->flags = flags; +} + +static inline void put16(void *where, uint16_t v) +{ + v = cbor_htons(v); + memcpy(where, &v, sizeof(v)); +} + +/* Note: Since this is currently only used in situations where OOM is the only + * valid error, we KNOW this to be true. Thus, this function now returns just 'true', + * but if in the future, any function starts returning a non-OOM error, this will need + * to be changed to the test. At the moment, this is done to prevent more branches + * being created in the tinycbor output */ +static inline bool isOomError(CborError err) +{ + (void) err; + return true; +} + +static inline void put32(void *where, uint32_t v) +{ + v = cbor_htonl(v); + memcpy(where, &v, sizeof(v)); +} + +static inline void put64(void *where, uint64_t v) +{ + v = cbor_htonll(v); + memcpy(where, &v, sizeof(v)); +} + +static inline bool would_overflow(CborEncoder *encoder, size_t len) +{ + ptrdiff_t remaining = (ptrdiff_t)encoder->end; + remaining -= remaining ? (ptrdiff_t)encoder->data.ptr : encoder->data.bytes_needed; + remaining -= (ptrdiff_t)len; + return unlikely(remaining < 0); +} + +static inline void advance_ptr(CborEncoder *encoder, size_t n) +{ + if (encoder->end) + encoder->data.ptr += n; + else + encoder->data.bytes_needed += n; +} + +static inline CborError append_to_buffer(CborEncoder *encoder, const void *data, size_t len) +{ + if (would_overflow(encoder, len)) { + if (encoder->end != NULL) { + len -= encoder->end - encoder->data.ptr; + encoder->end = NULL; + encoder->data.bytes_needed = 0; + } + + advance_ptr(encoder, len); + return CborErrorOutOfMemory; + } + + memcpy(encoder->data.ptr, data, len); + encoder->data.ptr += len; + return CborNoError; +} + +static inline CborError append_byte_to_buffer(CborEncoder *encoder, uint8_t byte) +{ + return append_to_buffer(encoder, &byte, 1); +} + +static inline CborError encode_number_no_update(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType) +{ + /* Little-endian would have been so much more convenient here: + * We could just write at the beginning of buf but append_to_buffer + * only the necessary bytes. + * Since it has to be big endian, do it the other way around: + * write from the end. */ + uint64_t buf[2]; + uint8_t *const bufend = (uint8_t *)buf + sizeof(buf); + uint8_t *bufstart = bufend - 1; + put64(buf + 1, ui); /* we probably have a bunch of zeros in the beginning */ + + if (ui < Value8Bit) { + *bufstart += shiftedMajorType; + } else { + uint8_t more = 0; + if (ui > 0xffU) + ++more; + if (ui > 0xffffU) + ++more; + if (ui > 0xffffffffU) + ++more; + bufstart -= (size_t)1 << more; + *bufstart = shiftedMajorType + Value8Bit + more; + } + + return append_to_buffer(encoder, bufstart, bufend - bufstart); +} + +static inline void saturated_decrement(CborEncoder *encoder) +{ + if (encoder->remaining) + --encoder->remaining; +} + +static inline CborError encode_number(CborEncoder *encoder, uint64_t ui, uint8_t shiftedMajorType) +{ + saturated_decrement(encoder); + return encode_number_no_update(encoder, ui, shiftedMajorType); +} + +/** + * Appends the unsigned 64-bit integer \a value to the CBOR stream provided by + * \a encoder. + * + * \sa cbor_encode_negative_int, cbor_encode_int + */ +CborError cbor_encode_uint(CborEncoder *encoder, uint64_t value) +{ + return encode_number(encoder, value, UnsignedIntegerType << MajorTypeShift); +} + +/** + * Appends the negative 64-bit integer whose absolute value is \a + * absolute_value to the CBOR stream provided by \a encoder. + * + * If the value \a absolute_value is zero, this function encodes -2^64. + * + * \sa cbor_encode_uint, cbor_encode_int + */ +CborError cbor_encode_negative_int(CborEncoder *encoder, uint64_t absolute_value) +{ + return encode_number(encoder, absolute_value - 1, NegativeIntegerType << MajorTypeShift); +} + +/** + * Appends the signed 64-bit integer \a value to the CBOR stream provided by + * \a encoder. + * + * \sa cbor_encode_negative_int, cbor_encode_uint + */ +CborError cbor_encode_int(CborEncoder *encoder, int64_t value) +{ + /* adapted from code in RFC 7049 appendix C (pseudocode) */ + uint64_t ui = value >> 63; /* extend sign to whole length */ + uint8_t majorType = ui & 0x20; /* extract major type */ + ui ^= value; /* complement negatives */ + return encode_number(encoder, ui, majorType); +} + +/** + * Appends the CBOR Simple Type of value \a value to the CBOR stream provided by + * \a encoder. + * + * This function may return error CborErrorIllegalSimpleType if the \a value + * variable contains a number that is not a valid simple type. + */ +CborError cbor_encode_simple_value(CborEncoder *encoder, uint8_t value) +{ +#ifndef CBOR_ENCODER_NO_CHECK_USER + /* check if this is a valid simple type */ + if (value >= HalfPrecisionFloat && value <= Break) + return CborErrorIllegalSimpleType; +#endif + return encode_number(encoder, value, SimpleTypesType << MajorTypeShift); +} + +/** + * Appends the floating-point value of type \a fpType and pointed to by \a + * value to the CBOR stream provided by \a encoder. The value of \a fpType must + * be one of CborHalfFloatType, CborFloatType or CborDoubleType, otherwise the + * behavior of this function is undefined. + * + * This function is useful for code that needs to pass through floating point + * values but does not wish to have the actual floating-point code. + * + * \sa cbor_encode_half_float, cbor_encode_float, cbor_encode_double + */ +CborError cbor_encode_floating_point(CborEncoder *encoder, CborType fpType, const void *value) +{ + unsigned size; + uint8_t buf[1 + sizeof(uint64_t)]; + cbor_assert(fpType == CborHalfFloatType || fpType == CborFloatType || fpType == CborDoubleType); + buf[0] = fpType; + + size = 2U << (fpType - CborHalfFloatType); + if (size == 8) + put64(buf + 1, *(const uint64_t*)value); + else if (size == 4) + put32(buf + 1, *(const uint32_t*)value); + else + put16(buf + 1, *(const uint16_t*)value); + saturated_decrement(encoder); + return append_to_buffer(encoder, buf, size + 1); +} + +/** + * Appends the CBOR tag \a tag to the CBOR stream provided by \a encoder. + * + * \sa CborTag + */ +CborError cbor_encode_tag(CborEncoder *encoder, CborTag tag) +{ + /* tags don't count towards the number of elements in an array or map */ + return encode_number_no_update(encoder, tag, TagType << MajorTypeShift); +} + +static CborError encode_string(CborEncoder *encoder, size_t length, uint8_t shiftedMajorType, const void *string) +{ + CborError err = encode_number(encoder, length, shiftedMajorType); + if (err && !isOomError(err)) + return err; + return append_to_buffer(encoder, string, length); +} + +/** + * \fn CborError cbor_encode_text_stringz(CborEncoder *encoder, const char *string) + * + * Appends the null-terminated text string \a string to the CBOR stream + * provided by \a encoder. CBOR requires that \a string be valid UTF-8, but + * TinyCBOR makes no verification of correctness. The terminating null is not + * included in the stream. + * + * \sa cbor_encode_text_string, cbor_encode_byte_string + */ + +/** + * Appends the text string \a string of length \a length to the CBOR stream + * provided by \a encoder. CBOR requires that \a string be valid UTF-8, but + * TinyCBOR makes no verification of correctness. + * + * \sa CborError cbor_encode_text_stringz, cbor_encode_byte_string + */ +CborError cbor_encode_byte_string(CborEncoder *encoder, const uint8_t *string, size_t length) +{ + return encode_string(encoder, length, ByteStringType << MajorTypeShift, string); +} + +/** + * Appends the byte string \a string of length \a length to the CBOR stream + * provided by \a encoder. CBOR byte strings are arbitrary raw data. + * + * \sa cbor_encode_text_stringz, cbor_encode_text_string + */ +CborError cbor_encode_text_string(CborEncoder *encoder, const char *string, size_t length) +{ + return encode_string(encoder, length, TextStringType << MajorTypeShift, string); +} + +#ifdef __GNUC__ +__attribute__((noinline)) +#endif +static CborError create_container(CborEncoder *encoder, CborEncoder *container, size_t length, uint8_t shiftedMajorType) +{ + CborError err; + container->data.ptr = encoder->data.ptr; + container->end = encoder->end; + saturated_decrement(encoder); + container->remaining = length + 1; /* overflow ok on CborIndefiniteLength */ + + cbor_static_assert(((MapType << MajorTypeShift) & CborIteratorFlag_ContainerIsMap) == CborIteratorFlag_ContainerIsMap); + cbor_static_assert(((ArrayType << MajorTypeShift) & CborIteratorFlag_ContainerIsMap) == 0); + container->flags = shiftedMajorType & CborIteratorFlag_ContainerIsMap; + + if (length == CborIndefiniteLength) { + container->flags |= CborIteratorFlag_UnknownLength; + err = append_byte_to_buffer(container, shiftedMajorType + IndefiniteLength); + } else { + if (shiftedMajorType & CborIteratorFlag_ContainerIsMap) + container->remaining += length; + err = encode_number_no_update(container, length, shiftedMajorType); + } + return err; +} + +/** + * Creates a CBOR array in the CBOR stream provided by \a encoder and + * initializes \a arrayEncoder so that items can be added to the array using + * the CborEncoder functions. The array must be terminated by calling either + * cbor_encoder_close_container() or cbor_encoder_close_container_checked() + * with the same \a encoder and \a arrayEncoder parameters. + * + * The number of items inserted into the array must be exactly \a length items, + * otherwise the stream is invalid. If the number of items is not known when + * creating the array, the constant \ref CborIndefiniteLength may be passed as + * length instead. + * + * \sa cbor_encoder_create_map + */ +CborError cbor_encoder_create_array(CborEncoder *encoder, CborEncoder *arrayEncoder, size_t length) +{ + return create_container(encoder, arrayEncoder, length, ArrayType << MajorTypeShift); +} + +/** + * Creates a CBOR map in the CBOR stream provided by \a encoder and + * initializes \a mapEncoder so that items can be added to the map using + * the CborEncoder functions. The map must be terminated by calling either + * cbor_encoder_close_container() or cbor_encoder_close_container_checked() + * with the same \a encoder and \a mapEncoder parameters. + * + * The number of pair of items inserted into the map must be exactly \a length + * items, otherwise the stream is invalid. If the number is not known + * when creating the map, the constant \ref CborIndefiniteLength may be passed as + * length instead. + * + * \b{Implementation limitation:} TinyCBOR cannot encode more than SIZE_MAX/2 + * key-value pairs in the stream. If the length \a length is larger than this + * value (and is not \ref CborIndefiniteLength), this function returns error + * CborErrorDataTooLarge. + * + * \sa cbor_encoder_create_array + */ +CborError cbor_encoder_create_map(CborEncoder *encoder, CborEncoder *mapEncoder, size_t length) +{ + if (length != CborIndefiniteLength && length > SIZE_MAX / 2) + return CborErrorDataTooLarge; + return create_container(encoder, mapEncoder, length, MapType << MajorTypeShift); +} + +/** + * Closes the CBOR container (array or map) provided by \a containerEncoder and + * updates the CBOR stream provided by \a encoder. Both parameters must be the + * same as were passed to cbor_encoder_create_array() or + * cbor_encoder_create_map(). + * + * Since version 0.5, this function verifies that the number of items (or pairs + * of items, in the case of a map) was correct. It is no longer necessary to call + * cbor_encoder_close_container_checked() instead. + * + * \sa cbor_encoder_create_array(), cbor_encoder_create_map() + */ +CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder *containerEncoder) +{ + if (encoder->end) + encoder->data.ptr = containerEncoder->data.ptr; + else + encoder->data.bytes_needed = containerEncoder->data.bytes_needed; + encoder->end = containerEncoder->end; + if (containerEncoder->flags & CborIteratorFlag_UnknownLength) + return append_byte_to_buffer(encoder, BreakByte); + + if (containerEncoder->remaining != 1) + return containerEncoder->remaining == 0 ? CborErrorTooManyItems : CborErrorTooFewItems; + + if (!encoder->end) + return CborErrorOutOfMemory; /* keep the state */ + return CborNoError; +} + +/** + * \fn CborError cbor_encode_boolean(CborEncoder *encoder, bool value) + * + * Appends the boolean value \a value to the CBOR stream provided by \a encoder. + */ + +/** + * \fn CborError cbor_encode_null(CborEncoder *encoder) + * + * Appends the CBOR type representing a null value to the CBOR stream provided + * by \a encoder. + * + * \sa cbor_encode_undefined() + */ + +/** + * \fn CborError cbor_encode_undefined(CborEncoder *encoder) + * + * Appends the CBOR type representing an undefined value to the CBOR stream + * provided by \a encoder. + * + * \sa cbor_encode_null() + */ + +/** + * \fn CborError cbor_encode_half_float(CborEncoder *encoder, const void *value) + * + * Appends the IEEE 754 half-precision (16-bit) floating point value pointed to + * by \a value to the CBOR stream provided by \a encoder. + * + * \sa cbor_encode_floating_point(), cbor_encode_float(), cbor_encode_double() + */ + +/** + * \fn CborError cbor_encode_float(CborEncoder *encoder, float value) + * + * Appends the IEEE 754 single-precision (32-bit) floating point value \a value + * to the CBOR stream provided by \a encoder. + * + * \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_double() + */ + +/** + * \fn CborError cbor_encode_double(CborEncoder *encoder, double value) + * + * Appends the IEEE 754 double-precision (64-bit) floating point value \a value + * to the CBOR stream provided by \a encoder. + * + * \sa cbor_encode_floating_point(), cbor_encode_half_float(), cbor_encode_float() + */ + +/** + * \fn size_t cbor_encoder_get_buffer_size(const CborEncoder *encoder, const uint8_t *buffer) + * + * Returns the total size of the buffer starting at \a buffer after the + * encoding finished without errors. The \a encoder and \a buffer arguments + * must be the same as supplied to cbor_encoder_init(). + * + * If the encoding process had errors, the return value of this function is + * meaningless. If the only errors were CborErrorOutOfMemory, instead use + * cbor_encoder_get_extra_bytes_needed() to find out by how much to grow the + * buffer before encoding again. + * + * See \ref CborEncoding for an example of using this function. + * + * \sa cbor_encoder_init(), cbor_encoder_get_extra_bytes_needed(), CborEncoding + */ + +/** + * \fn size_t cbor_encoder_get_extra_bytes_needed(const CborEncoder *encoder) + * + * Returns how many more bytes the original buffer supplied to + * cbor_encoder_init() needs to be extended by so that no CborErrorOutOfMemory + * condition will happen for the encoding. If the buffer was big enough, this + * function returns 0. The \a encoder must be the original argument as passed + * to cbor_encoder_init(). + * + * This function is usually called after an encoding sequence ended with one or + * more CborErrorOutOfMemory errors, but no other error. If any other error + * happened, the return value of this function is meaningless. + * + * See \ref CborEncoding for an example of using this function. + * + * \sa cbor_encoder_init(), cbor_encoder_get_buffer_size(), CborEncoding + */ + +/** @} */ diff --git a/lib/tinycbor/cborencoder_close_container_checked.c b/lib/tinycbor/cborencoder_close_container_checked.c new file mode 100644 index 000000000..5661e4d53 --- /dev/null +++ b/lib/tinycbor/cborencoder_close_container_checked.c @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#define _BSD_SOURCE 1 +#define _DEFAULT_SOURCE 1 +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" + +/** + * \addtogroup CborEncoding + * @{ + */ + +/** + * @deprecated + * + * Closes the CBOR container (array or map) provided by \a containerEncoder and + * updates the CBOR stream provided by \a encoder. Both parameters must be the + * same as were passed to cbor_encoder_create_array() or + * cbor_encoder_create_map(). + * + * Prior to version 0.5, cbor_encoder_close_container() did not check the + * number of items added. Since that version, it does and now + * cbor_encoder_close_container_checked() is no longer needed. + * + * \sa cbor_encoder_create_array(), cbor_encoder_create_map() + */ +CborError cbor_encoder_close_container_checked(CborEncoder *encoder, const CborEncoder *containerEncoder) +{ + return cbor_encoder_close_container(encoder, containerEncoder); +} + +/** @} */ diff --git a/lib/tinycbor/cborerrorstrings.c b/lib/tinycbor/cborerrorstrings.c new file mode 100644 index 000000000..3fe3a9823 --- /dev/null +++ b/lib/tinycbor/cborerrorstrings.c @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#include "cbor.h" + +#ifndef _ +# define _(msg) msg +#endif + +/** + * \enum CborError + * \ingroup CborGlobals + * The CborError enum contains the possible error values used by the CBOR encoder and decoder. + * + * TinyCBOR functions report success by returning CborNoError, or one error + * condition by returning one of the values below. One exception is the + * out-of-memory condition (CborErrorOutOfMemory), which the functions for \ref + * CborEncoding may report in bit-wise OR with other conditions. + * + * This technique allows code to determine whether the only error condition was + * a lack of buffer space, which may not be a fatal condition if the buffer can + * be resized. Additionally, the functions for \ref CborEncoding may continue + * to be used even after CborErrorOutOfMemory is returned, and instead they + * will simply calculate the extra space needed. + * + * \value CborNoError No error occurred + * \omitvalue CborUnknownError + * \value CborErrorUnknownLength Request for the length of an array, map or string whose length is not provided in the CBOR stream + * \value CborErrorAdvancePastEOF Not enough data in the stream to decode item (decoding would advance past end of stream) + * \value CborErrorIO An I/O error occurred, probably due to an out-of-memory situation + * \value CborErrorGarbageAtEnd Bytes exist past the end of the CBOR stream + * \value CborErrorUnexpectedEOF End of stream reached unexpectedly + * \value CborErrorUnexpectedBreak A CBOR break byte was found where not expected + * \value CborErrorUnknownType An unknown type (future extension to CBOR) was found in the stream + * \value CborErrorIllegalType An invalid type was found while parsing a chunked CBOR string + * \value CborErrorIllegalNumber An illegal initial byte (encoding unspecified additional information) was found + * \value CborErrorIllegalSimpleType An illegal encoding of a CBOR Simple Type of value less than 32 was found + * \omitvalue CborErrorUnknownSimpleType + * \omitvalue CborErrorUnknownTag + * \omitvalue CborErrorInappropriateTagForType + * \omitvalue CborErrorDuplicateObjectKeys + * \value CborErrorInvalidUtf8TextString Illegal UTF-8 encoding found while parsing CBOR Text String + * \value CborErrorTooManyItems Too many items were added to CBOR map or array of pre-determined length + * \value CborErrorTooFewItems Too few items were added to CBOR map or array of pre-determined length + * \value CborErrorDataTooLarge Data item size exceeds TinyCBOR's implementation limits + * \value CborErrorNestingTooDeep Data item nesting exceeds TinyCBOR's implementation limits + * \omitvalue CborErrorUnsupportedType + * \value CborErrorJsonObjectKeyIsAggregate Conversion to JSON failed because the key in a map is a CBOR map or array + * \value CborErrorJsonObjectKeyNotString Conversion to JSON failed because the key in a map is not a text string + * \value CborErrorOutOfMemory During CBOR encoding, the buffer provided is insufficient for encoding the data item; + * in other situations, TinyCBOR failed to allocate memory + * \value CborErrorInternalError An internal error occurred in TinyCBOR + */ + +/** + * \ingroup CborGlobals + * Returns the error string corresponding to the CBOR error condition \a error. + */ +const char *cbor_error_string(CborError error) +{ + switch (error) { + case CborNoError: + return ""; + + case CborUnknownError: + return _("unknown error"); + + case CborErrorOutOfMemory: + return _("out of memory/need more memory"); + + case CborErrorUnknownLength: + return _("unknown length (attempted to get the length of a map/array/string of indeterminate length"); + + case CborErrorAdvancePastEOF: + return _("attempted to advance past EOF"); + + case CborErrorIO: + return _("I/O error"); + + case CborErrorGarbageAtEnd: + return _("garbage after the end of the content"); + + case CborErrorUnexpectedEOF: + return _("unexpected end of data"); + + case CborErrorUnexpectedBreak: + return _("unexpected 'break' byte"); + + case CborErrorUnknownType: + return _("illegal byte (encodes future extension type)"); + + case CborErrorIllegalType: + return _("mismatched string type in chunked string"); + + case CborErrorIllegalNumber: + return _("illegal initial byte (encodes unspecified additional information)"); + + case CborErrorIllegalSimpleType: + return _("illegal encoding of simple type smaller than 32"); + + case CborErrorUnknownSimpleType: + return _("unknown simple type"); + + case CborErrorUnknownTag: + return _("unknown tag"); + + case CborErrorInappropriateTagForType: + return _("inappropriate tag for type"); + + case CborErrorDuplicateObjectKeys: + return _("duplicate keys in object"); + + case CborErrorInvalidUtf8TextString: + return _("invalid UTF-8 content in string"); + + case CborErrorExcludedType: + return _("excluded type found"); + + case CborErrorExcludedValue: + return _("excluded value found"); + + case CborErrorImproperValue: + case CborErrorOverlongEncoding: + return _("value encoded in non-canonical form"); + + case CborErrorMapKeyNotString: + case CborErrorJsonObjectKeyNotString: + return _("key in map is not a string"); + + case CborErrorMapNotSorted: + return _("map is not sorted"); + + case CborErrorMapKeysNotUnique: + return _("map keys are not unique"); + + case CborErrorTooManyItems: + return _("too many items added to encoder"); + + case CborErrorTooFewItems: + return _("too few items added to encoder"); + + case CborErrorDataTooLarge: + return _("internal error: data too large"); + + case CborErrorNestingTooDeep: + return _("internal error: too many nested containers found in recursive function"); + + case CborErrorUnsupportedType: + return _("unsupported type"); + + case CborErrorJsonObjectKeyIsAggregate: + return _("conversion to JSON failed: key in object is an array or map"); + + case CborErrorJsonNotImplemented: + return _("conversion to JSON failed: open_memstream unavailable"); + + case CborErrorInternalError: + return _("internal error"); + } + return cbor_error_string(CborUnknownError); +} diff --git a/lib/tinycbor/cborinternal_p.h b/lib/tinycbor/cborinternal_p.h new file mode 100644 index 000000000..a85a92972 --- /dev/null +++ b/lib/tinycbor/cborinternal_p.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#ifndef CBORINTERNAL_P_H +#define CBORINTERNAL_P_H + +#include "compilersupport_p.h" + +#ifndef CBOR_NO_FLOATING_POINT +# include +# include +#else +# ifndef CBOR_NO_HALF_FLOAT_TYPE +# define CBOR_NO_HALF_FLOAT_TYPE 1 +# endif +#endif + +#ifndef CBOR_NO_HALF_FLOAT_TYPE +# ifdef __F16C__ +# include +static inline unsigned short encode_half(double val) +{ + return _cvtss_sh((float)val, 3); +} +static inline double decode_half(unsigned short half) +{ + return _cvtsh_ss(half); +} +# else +/* software implementation of float-to-fp16 conversions */ +static inline unsigned short encode_half(double val) +{ + uint64_t v; + int sign, exp, mant; + memcpy(&v, &val, sizeof(v)); + sign = v >> 63 << 15; + exp = (v >> 52) & 0x7ff; + mant = v << 12 >> 12 >> (53-11); /* keep only the 11 most significant bits of the mantissa */ + exp -= 1023; + if (exp == 1024) { + /* infinity or NaN */ + exp = 16; + mant >>= 1; + } else if (exp >= 16) { + /* overflow, as largest number */ + exp = 15; + mant = 1023; + } else if (exp >= -14) { + /* regular normal */ + } else if (exp >= -24) { + /* subnormal */ + mant |= 1024; + mant >>= -(exp + 14); + exp = -15; + } else { + /* underflow, make zero */ + return 0; + } + + /* safe cast here as bit operations above guarantee not to overflow */ + return (unsigned short)(sign | ((exp + 15) << 10) | mant); +} + +/* this function was copied & adapted from RFC 7049 Appendix D */ +static inline double decode_half(unsigned short half) +{ + int exp = (half >> 10) & 0x1f; + int mant = half & 0x3ff; + double val; + if (exp == 0) val = ldexp(mant, -24); + else if (exp != 31) val = ldexp(mant + 1024, exp - 25); + else val = mant == 0 ? INFINITY : NAN; + return half & 0x8000 ? -val : val; +} +# endif +#endif /* CBOR_NO_HALF_FLOAT_TYPE */ + +#ifndef CBOR_INTERNAL_API +# define CBOR_INTERNAL_API +#endif + +#ifndef CBOR_PARSER_MAX_RECURSIONS +# define CBOR_PARSER_MAX_RECURSIONS 1024 +#endif + +/* + * CBOR Major types + * Encoded in the high 3 bits of the descriptor byte + * See http://tools.ietf.org/html/rfc7049#section-2.1 + */ +typedef enum CborMajorTypes { + UnsignedIntegerType = 0U, + NegativeIntegerType = 1U, + ByteStringType = 2U, + TextStringType = 3U, + ArrayType = 4U, + MapType = 5U, /* a.k.a. object */ + TagType = 6U, + SimpleTypesType = 7U +} CborMajorTypes; + +/* + * CBOR simple and floating point types + * Encoded in the low 8 bits of the descriptor byte when the + * Major Type is 7. + */ +typedef enum CborSimpleTypes { + FalseValue = 20, + TrueValue = 21, + NullValue = 22, + UndefinedValue = 23, + SimpleTypeInNextByte = 24, /* not really a simple type */ + HalfPrecisionFloat = 25, /* ditto */ + SinglePrecisionFloat = 26, /* ditto */ + DoublePrecisionFloat = 27, /* ditto */ + Break = 31 +} CborSimpleTypes; + +enum { + SmallValueBitLength = 5U, + SmallValueMask = (1U << SmallValueBitLength) - 1, /* 31 */ + Value8Bit = 24U, + Value16Bit = 25U, + Value32Bit = 26U, + Value64Bit = 27U, + IndefiniteLength = 31U, + + MajorTypeShift = SmallValueBitLength, + MajorTypeMask = (int) (~0U << MajorTypeShift), + + BreakByte = (unsigned)Break | (SimpleTypesType << MajorTypeShift) +}; + +CBOR_INTERNAL_API CborError CBOR_INTERNAL_API_CC _cbor_value_extract_number(const uint8_t **ptr, const uint8_t *end, uint64_t *len); +CBOR_INTERNAL_API CborError CBOR_INTERNAL_API_CC _cbor_value_prepare_string_iteration(CborValue *it); +CBOR_INTERNAL_API CborError CBOR_INTERNAL_API_CC _cbor_value_get_string_chunk(const CborValue *value, const void **bufferptr, + size_t *len, CborValue *next); + + +#endif /* CBORINTERNAL_P_H */ diff --git a/lib/tinycbor/cborjson.h b/lib/tinycbor/cborjson.h new file mode 100644 index 000000000..8ff27b920 --- /dev/null +++ b/lib/tinycbor/cborjson.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#ifndef CBORJSON_H +#define CBORJSON_H + +#include "cbor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Conversion to JSON */ +enum CborToJsonFlags +{ + CborConvertAddMetadata = 1, + CborConvertTagsToObjects = 2, + CborConvertIgnoreTags = 0, + + CborConvertObeyByteStringTags = 0, + CborConvertByteStringsToBase64Url = 4, + + CborConvertRequireMapStringKeys = 0, + CborConvertStringifyMapKeys = 8, + + CborConvertDefaultFlags = 0 +}; + +CBOR_API CborError cbor_value_to_json_advance(FILE *out, CborValue *value, int flags); +CBOR_INLINE_API CborError cbor_value_to_json(FILE *out, const CborValue *value, int flags) +{ + CborValue copy = *value; + return cbor_value_to_json_advance(out, ©, flags); +} + +#ifdef __cplusplus +} +#endif + +#endif /* CBORJSON_H */ + diff --git a/lib/tinycbor/cborparser.c b/lib/tinycbor/cborparser.c new file mode 100644 index 000000000..45de2226a --- /dev/null +++ b/lib/tinycbor/cborparser.c @@ -0,0 +1,1430 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE 1 +#endif +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE 1 +#endif +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" +#include "cborinternal_p.h" +#include "compilersupport_p.h" + +#include + +/** + * \defgroup CborParsing Parsing CBOR streams + * \brief Group of functions used to parse CBOR streams. + * + * TinyCBOR provides functions for pull-based stream parsing of a CBOR-encoded + * payload. The main data type for the parsing is a CborValue, which behaves + * like an iterator and can be used to extract the encoded data. It is first + * initialized with a call to cbor_parser_init() and is usually used to extract + * exactly one item, most often an array or map. + * + * Nested CborValue objects can be parsed using cbor_value_enter_container(). + * Each call to cbor_value_enter_container() must be matched by a call to + * cbor_value_leave_container(), with the exact same parameters. + * + * The example below initializes a CborParser object, begins the parsing with a + * CborValue and decodes a single integer: + * + * \code + * int extract_int(const uint8_t *buffer, size_t len) + * { + * CborParser parser; + * CborValue value; + * int result; + * cbor_parser_init(buffer, len, 0, &parser, &value); + * cbor_value_get_int(&value, &result); + * return result; + * } + * \endcode + * + * The code above does no error checking, which means it assumes the data comes + * from a source trusted to send one properly-encoded integer. The following + * example does the exact same operation, but includes error checking and + * returns 0 on parsing failure: + * + * \code + * int extract_int(const uint8_t *buffer, size_t len) + * { + * CborParser parser; + * CborValue value; + * int result; + * if (cbor_parser_init(buffer, len, 0, &parser, &value) != CborNoError) + * return 0; + * if (!cbor_value_is_integer(&value) || + * cbor_value_get_int(&value, &result) != CborNoError) + * return 0; + * return result; + * } + * \endcode + * + * Note, in the example above, that one can't distinguish a parsing failure + * from an encoded value of zero. Reporting a parsing error is left as an + * exercise to the reader. + * + * The code above does not execute a range-check either: it is possible that + * the value decoded from the CBOR stream encodes a number larger than what can + * be represented in a variable of type \c{int}. If detecting that case is + * important, the code should call cbor_value_get_int_checked() instead. + * + *

Memory and parsing constraints

+ * + * TinyCBOR is designed to run with little memory and with minimal overhead. + * Except where otherwise noted, the parser functions always run on constant + * time (O(1)), do not recurse and never allocate memory (thus, stack usage is + * bounded and is O(1)). + * + *

Error handling and preconditions

+ * + * All functions operating on a CborValue return a CborError condition, with + * CborNoError standing for the normal situation in which no parsing error + * occurred. All functions may return parsing errors in case the stream cannot + * be decoded properly, be it due to corrupted data or due to reaching the end + * of the input buffer. + * + * Error conditions must not be ignored. All decoder functions have undefined + * behavior if called after an error has been reported, and may crash. + * + * Some functions are also documented to have preconditions, like + * cbor_value_get_int() requiring that the input be an integral value. + * Violation of preconditions also results in undefined behavior and the + * program may crash. + */ + +/** + * \addtogroup CborParsing + * @{ + */ + +/** + * \struct CborValue + * + * This type contains one value parsed from the CBOR stream. Each CborValue + * behaves as an iterator in a StAX-style parser. + * + * \if privatedocs + * Implementation details: the CborValue contains these fields: + * \list + * \li ptr: pointer to the actual data + * \li flags: flags from the decoder + * \li extra: partially decoded integer value (0, 1 or 2 bytes) + * \li remaining: remaining items in this collection after this item or UINT32_MAX if length is unknown + * \endlist + * \endif + */ + +static inline uint16_t get16(const uint8_t *ptr) +{ + uint16_t result; + memcpy(&result, ptr, sizeof(result)); + return cbor_ntohs(result); +} + +static inline uint32_t get32(const uint8_t *ptr) +{ + uint32_t result; + memcpy(&result, ptr, sizeof(result)); + return cbor_ntohl(result); +} + +static inline uint64_t get64(const uint8_t *ptr) +{ + uint64_t result; + memcpy(&result, ptr, sizeof(result)); + return cbor_ntohll(result); +} + +CborError CBOR_INTERNAL_API_CC _cbor_value_extract_number(const uint8_t **ptr, const uint8_t *end, uint64_t *len) +{ + size_t bytesNeeded; + uint8_t additional_information = **ptr & SmallValueMask; + ++*ptr; + if (additional_information < Value8Bit) { + *len = additional_information; + return CborNoError; + } + if (unlikely(additional_information > Value64Bit)) + return CborErrorIllegalNumber; + + bytesNeeded = (size_t)(1 << (additional_information - Value8Bit)); + if (unlikely(bytesNeeded > (size_t)(end - *ptr))) { + return CborErrorUnexpectedEOF; + } else if (bytesNeeded == 1) { + *len = (uint8_t)(*ptr)[0]; + } else if (bytesNeeded == 2) { + *len = get16(*ptr); + } else if (bytesNeeded == 4) { + *len = get32(*ptr); + } else { + *len = get64(*ptr); + } + *ptr += bytesNeeded; + return CborNoError; +} + +static CborError extract_length(const CborParser *parser, const uint8_t **ptr, size_t *len) +{ + uint64_t v; + CborError err = _cbor_value_extract_number(ptr, parser->end, &v); + if (err) { + *len = 0; + return err; + } + + *len = (size_t)v; + if (v != *len) + return CborErrorDataTooLarge; + return CborNoError; +} + +static bool is_fixed_type(uint8_t type) +{ + return type != CborTextStringType && type != CborByteStringType && type != CborArrayType && + type != CborMapType; +} + +static CborError preparse_value(CborValue *it) +{ + const CborParser *parser = it->parser; + it->type = CborInvalidType; + + /* are we at the end? */ + if (it->ptr == parser->end) + return CborErrorUnexpectedEOF; + + uint8_t descriptor = *it->ptr; + uint8_t type = descriptor & MajorTypeMask; + it->type = type; + it->flags = 0; + it->extra = (descriptor &= SmallValueMask); + + if (descriptor > Value64Bit) { + if (unlikely(descriptor != IndefiniteLength)) + return type == CborSimpleType ? CborErrorUnknownType : CborErrorIllegalNumber; + if (likely(!is_fixed_type(type))) { + /* special case */ + it->flags |= CborIteratorFlag_UnknownLength; + it->type = type; + return CborNoError; + } + return type == CborSimpleType ? CborErrorUnexpectedBreak : CborErrorIllegalNumber; + } + + size_t bytesNeeded = descriptor < Value8Bit ? 0 : (1 << (descriptor - Value8Bit)); + if (bytesNeeded + 1 > (size_t)(parser->end - it->ptr)) + return CborErrorUnexpectedEOF; + + uint8_t majortype = type >> MajorTypeShift; + if (majortype == NegativeIntegerType) { + it->flags |= CborIteratorFlag_NegativeInteger; + it->type = CborIntegerType; + } else if (majortype == SimpleTypesType) { + switch (descriptor) { + case FalseValue: + it->extra = false; + it->type = CborBooleanType; + break; + + case SinglePrecisionFloat: + case DoublePrecisionFloat: + it->flags |= CborIteratorFlag_IntegerValueTooLarge; + /* fall through */ + case TrueValue: + case NullValue: + case UndefinedValue: + case HalfPrecisionFloat: + it->type = *it->ptr; + break; + + case SimpleTypeInNextByte: + it->extra = (uint8_t)it->ptr[1]; +#ifndef CBOR_PARSER_NO_STRICT_CHECKS + if (unlikely(it->extra < 32)) { + it->type = CborInvalidType; + return CborErrorIllegalSimpleType; + } +#endif + break; + + case 28: + case 29: + case 30: + case Break: + cbor_assert(false); /* these conditions can't be reached */ + return CborErrorUnexpectedBreak; + } + return CborNoError; + } + + /* try to decode up to 16 bits */ + if (descriptor < Value8Bit) + return CborNoError; + + if (descriptor == Value8Bit) + it->extra = (uint8_t)it->ptr[1]; + else if (descriptor == Value16Bit) + it->extra = get16(it->ptr + 1); + else + it->flags |= CborIteratorFlag_IntegerValueTooLarge; /* Value32Bit or Value64Bit */ + return CborNoError; +} + +static CborError preparse_next_value_nodecrement(CborValue *it) +{ + if (it->remaining == UINT32_MAX && it->ptr != it->parser->end && *it->ptr == (uint8_t)BreakByte) { + /* end of map or array */ + ++it->ptr; + it->type = CborInvalidType; + it->remaining = 0; + return CborNoError; + } + + return preparse_value(it); +} + +static CborError preparse_next_value(CborValue *it) +{ + if (it->remaining != UINT32_MAX) { + /* don't decrement the item count if the current item is tag: they don't count */ + if (it->type != CborTagType && --it->remaining == 0) { + it->type = CborInvalidType; + return CborNoError; + } + } + return preparse_next_value_nodecrement(it); +} + +static CborError advance_internal(CborValue *it) +{ + uint64_t length; + CborError err = _cbor_value_extract_number(&it->ptr, it->parser->end, &length); + cbor_assert(err == CborNoError); + + if (it->type == CborByteStringType || it->type == CborTextStringType) { + cbor_assert(length == (size_t)length); + cbor_assert((it->flags & CborIteratorFlag_UnknownLength) == 0); + it->ptr += length; + } + + return preparse_next_value(it); +} + +/** \internal + * + * Decodes the CBOR integer value when it is larger than the 16 bits available + * in value->extra. This function requires that value->flags have the + * CborIteratorFlag_IntegerValueTooLarge flag set. + * + * This function is also used to extract single- and double-precision floating + * point values (SinglePrecisionFloat == Value32Bit and DoublePrecisionFloat == + * Value64Bit). + */ +uint64_t _cbor_value_decode_int64_internal(const CborValue *value) +{ + cbor_assert(value->flags & CborIteratorFlag_IntegerValueTooLarge || + value->type == CborFloatType || value->type == CborDoubleType); + + /* since the additional information can only be Value32Bit or Value64Bit, + * we just need to test for the one bit those two options differ */ + cbor_assert((*value->ptr & SmallValueMask) == Value32Bit || (*value->ptr & SmallValueMask) == Value64Bit); + if ((*value->ptr & 1) == (Value32Bit & 1)) + return get32(value->ptr + 1); + + cbor_assert((*value->ptr & SmallValueMask) == Value64Bit); + return get64(value->ptr + 1); +} + +/** + * Initializes the CBOR parser for parsing \a size bytes beginning at \a + * buffer. Parsing will use flags set in \a flags. The iterator to the first + * element is returned in \a it. + * + * The \a parser structure needs to remain valid throughout the decoding + * process. It is not thread-safe to share one CborParser among multiple + * threads iterating at the same time, but the object can be copied so multiple + * threads can iterate. + */ +CborError cbor_parser_init(const uint8_t *buffer, size_t size, uint32_t flags, CborParser *parser, CborValue *it) +{ + memset(parser, 0, sizeof(*parser)); + parser->end = buffer + size; + parser->flags = flags; + it->parser = parser; + it->ptr = buffer; + it->remaining = 1; /* there's one type altogether, usually an array or map */ + return preparse_value(it); +} + +/** + * \fn bool cbor_value_at_end(const CborValue *it) + * + * Returns true if \a it has reached the end of the iteration, usually when + * advancing after the last item in an array or map. + * + * In the case of the outermost CborValue object, this function returns true + * after decoding a single element. A pointer to the first byte of the + * remaining data (if any) can be obtained with cbor_value_get_next_byte(). + * + * \sa cbor_value_advance(), cbor_value_is_valid(), cbor_value_get_next_byte() + */ + +/** + * \fn const uint8_t *cbor_value_get_next_byte(const CborValue *it) + * + * Returns a pointer to the next byte that would be decoded if this CborValue + * object were advanced. + * + * This function is useful if cbor_value_at_end() returns true for the + * outermost CborValue: the pointer returned is the first byte of the data + * remaining in the buffer, if any. Code can decide whether to begin decoding a + * new CBOR data stream from this point, or parse some other data appended to + * the same buffer. + * + * This function may be used even after a parsing error. If that occurred, + * then this function returns a pointer to where the parsing error occurred. + * Note that the error recovery is not precise and the pointer may not indicate + * the exact byte containing bad data. + * + * \sa cbor_value_at_end() + */ + +/** + * \fn bool cbor_value_is_valid(const CborValue *it) + * + * Returns true if the iterator \a it contains a valid value. Invalid iterators + * happen when iteration reaches the end of a container (see \ref + * cbor_value_at_end()) or when a search function resulted in no matches. + * + * \sa cbor_value_advance(), cbor_value_at_end(), cbor_value_get_type() + */ + +/** + * Performs a basic validation of the CBOR stream pointed by \a it and returns + * the error it found. If no error was found, it returns CborNoError and the + * application can iterate over the items with certainty that no other errors + * will appear during parsing. + * + * A basic validation checks for: + * \list + * \li absence of undefined additional information bytes; + * \li well-formedness of all numbers, lengths, and simple values; + * \li string contents match reported sizes; + * \li arrays and maps contain the number of elements they are reported to have; + * \endlist + * + * For further checks, see cbor_value_validate(). + * + * This function has the same timing and memory requirements as + * cbor_value_advance(). + * + * \sa cbor_value_validate(), cbor_value_advance() + */ +CborError cbor_value_validate_basic(const CborValue *it) +{ + CborValue value = *it; + return cbor_value_advance(&value); +} + +/** + * Advances the CBOR value \a it by one fixed-size position. Fixed-size types + * are: integers, tags, simple types (including boolean, null and undefined + * values) and floating point types. + * + * If the type is not of fixed size, this function has undefined behavior. Code + * must be sure that the current type is one of the fixed-size types before + * calling this function. This function is provided because it can guarantee + * that it runs in constant time (O(1)). + * + * If the caller is not able to determine whether the type is fixed or not, code + * can use the cbor_value_advance() function instead. + * + * \sa cbor_value_at_end(), cbor_value_advance(), cbor_value_enter_container(), cbor_value_leave_container() + */ +CborError cbor_value_advance_fixed(CborValue *it) +{ + cbor_assert(it->type != CborInvalidType); + cbor_assert(is_fixed_type(it->type)); + if (!it->remaining) + return CborErrorAdvancePastEOF; + return advance_internal(it); +} + +static CborError advance_recursive(CborValue *it, int nestingLevel) +{ + CborError err; + CborValue recursed; + + if (is_fixed_type(it->type)) + return advance_internal(it); + + if (!cbor_value_is_container(it)) { + size_t len = SIZE_MAX; + return _cbor_value_copy_string(it, NULL, &len, it); + } + + /* map or array */ + if (nestingLevel == 0) + return CborErrorNestingTooDeep; + + err = cbor_value_enter_container(it, &recursed); + if (err) + return err; + while (!cbor_value_at_end(&recursed)) { + err = advance_recursive(&recursed, nestingLevel - 1); + if (err) + return err; + } + return cbor_value_leave_container(it, &recursed); +} + + +/** + * Advances the CBOR value \a it by one element, skipping over containers. + * Unlike cbor_value_advance_fixed(), this function can be called on a CBOR + * value of any type. However, if the type is a container (map or array) or a + * string with a chunked payload, this function will not run in constant time + * and will recurse into itself (it will run on O(n) time for the number of + * elements or chunks and will use O(n) memory for the number of nested + * containers). + * + * The number of recursions can be limited at compile time to avoid stack + * exhaustion in constrained systems. + * + * \sa cbor_value_at_end(), cbor_value_advance_fixed(), cbor_value_enter_container(), cbor_value_leave_container() + */ +CborError cbor_value_advance(CborValue *it) +{ + cbor_assert(it->type != CborInvalidType); + if (!it->remaining) + return CborErrorAdvancePastEOF; + return advance_recursive(it, CBOR_PARSER_MAX_RECURSIONS); +} + +/** + * \fn bool cbor_value_is_tag(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR tag. + * + * \sa cbor_value_get_tag(), cbor_value_skip_tag() + */ + +/** + * \fn CborError cbor_value_get_tag(const CborValue *value, CborTag *result) + * + * Retrieves the CBOR tag value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to a CBOR tag value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_tag is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_tag() + */ + +/** + * Advances the CBOR value \a it until it no longer points to a tag. If \a it is + * already not pointing to a tag, then this function returns it unchanged. + * + * This function does not run in constant time: it will run on O(n) for n being + * the number of tags. It does use constant memory (O(1) memory requirements). + * + * \sa cbor_value_advance_fixed(), cbor_value_advance() + */ +CborError cbor_value_skip_tag(CborValue *it) +{ + while (cbor_value_is_tag(it)) { + CborError err = cbor_value_advance_fixed(it); + if (err) + return err; + } + return CborNoError; +} + +/** + * \fn bool cbor_value_is_container(const CborValue *it) + * + * Returns true if the \a it value is a container and requires recursion in + * order to decode (maps and arrays), false otherwise. + */ + +/** + * Creates a CborValue iterator pointing to the first element of the container + * represented by \a it and saves it in \a recursed. The \a it container object + * needs to be kept and passed again to cbor_value_leave_container() in order + * to continue iterating past this container. + * + * The \a it CborValue iterator must point to a container. + * + * \sa cbor_value_is_container(), cbor_value_leave_container(), cbor_value_advance() + */ +CborError cbor_value_enter_container(const CborValue *it, CborValue *recursed) +{ + cbor_assert(cbor_value_is_container(it)); + *recursed = *it; + + if (it->flags & CborIteratorFlag_UnknownLength) { + recursed->remaining = UINT32_MAX; + ++recursed->ptr; + } else { + uint64_t len; + CborError err = _cbor_value_extract_number(&recursed->ptr, recursed->parser->end, &len); + cbor_assert(err == CborNoError); + + recursed->remaining = (uint32_t)len; + if (recursed->remaining != len || len == UINT32_MAX) { + /* back track the pointer to indicate where the error occurred */ + recursed->ptr = it->ptr; + return CborErrorDataTooLarge; + } + if (recursed->type == CborMapType) { + /* maps have keys and values, so we need to multiply by 2 */ + if (recursed->remaining > UINT32_MAX / 2) { + /* back track the pointer to indicate where the error occurred */ + recursed->ptr = it->ptr; + return CborErrorDataTooLarge; + } + recursed->remaining *= 2; + } + if (len == 0) { + /* the case of the empty container */ + recursed->type = CborInvalidType; + return CborNoError; + } + } + return preparse_next_value_nodecrement(recursed); +} + +/** + * Updates \a it to point to the next element after the container. The \a + * recursed object needs to point to the element obtained either by advancing + * the last element of the container (via cbor_value_advance(), + * cbor_value_advance_fixed(), a nested cbor_value_leave_container(), or the \c + * next pointer from cbor_value_copy_string() or cbor_value_dup_string()). + * + * The \a it and \a recursed parameters must be the exact same as passed to + * cbor_value_enter_container(). + * + * \sa cbor_value_enter_container(), cbor_value_at_end() + */ +CborError cbor_value_leave_container(CborValue *it, const CborValue *recursed) +{ + cbor_assert(cbor_value_is_container(it)); + cbor_assert(recursed->type == CborInvalidType); + it->ptr = recursed->ptr; + return preparse_next_value(it); +} + + +/** + * \fn CborType cbor_value_get_type(const CborValue *value) + * + * Returns the type of the CBOR value that the iterator \a value points to. If + * \a value does not point to a valid value, this function returns \ref + * CborInvalidType. + * + * TinyCBOR also provides functions to test directly if a given CborValue object + * is of a given type, like cbor_value_is_text_string() and cbor_value_is_null(). + * + * \sa cbor_value_is_valid() + */ + +/** + * \fn bool cbor_value_is_null(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR null type. + * + * \sa cbor_value_is_valid(), cbor_value_is_undefined() + */ + +/** + * \fn bool cbor_value_is_undefined(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR undefined type. + * + * \sa cbor_value_is_valid(), cbor_value_is_null() + */ + +/** + * \fn bool cbor_value_is_boolean(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR boolean + * type (true or false). + * + * \sa cbor_value_is_valid(), cbor_value_get_boolean() + */ + +/** + * \fn CborError cbor_value_get_boolean(const CborValue *value, bool *result) + * + * Retrieves the boolean value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to a boolean value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_boolean is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_boolean() + */ + +/** + * \fn bool cbor_value_is_simple_type(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR Simple Type + * type (other than true, false, null and undefined). + * + * \sa cbor_value_is_valid(), cbor_value_get_simple_type() + */ + +/** + * \fn CborError cbor_value_get_simple_type(const CborValue *value, uint8_t *result) + * + * Retrieves the CBOR Simple Type value that \a value points to and stores it + * in \a result. If the iterator \a value does not point to a simple_type + * value, the behavior is undefined, so checking with \ref cbor_value_get_type + * or with \ref cbor_value_is_simple_type is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_simple_type() + */ + +/** + * \fn bool cbor_value_is_integer(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR integer + * type. + * + * \sa cbor_value_is_valid(), cbor_value_get_int, cbor_value_get_int64, cbor_value_get_uint64, cbor_value_get_raw_integer + */ + +/** + * \fn bool cbor_value_is_unsigned_integer(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR unsigned + * integer type (positive values or zero). + * + * \sa cbor_value_is_valid(), cbor_value_get_uint64() + */ + +/** + * \fn bool cbor_value_is_negative_integer(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR negative + * integer type. + * + * \sa cbor_value_is_valid(), cbor_value_get_int, cbor_value_get_int64, cbor_value_get_raw_integer + */ + +/** + * \fn CborError cbor_value_get_int(const CborValue *value, int *result) + * + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an integer value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_integer is recommended. + * + * Note that this function does not do range-checking: integral values that do + * not fit in a variable of type \c{int} are silently truncated to fit. Use + * cbor_value_get_int_checked() if that is not acceptable. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_integer() + */ + +/** + * \fn CborError cbor_value_get_int64(const CborValue *value, int64_t *result) + * + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an integer value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_integer is recommended. + * + * Note that this function does not do range-checking: integral values that do + * not fit in a variable of type \c{int64_t} are silently truncated to fit. Use + * cbor_value_get_int64_checked() that is not acceptable. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_integer() + */ + +/** + * \fn CborError cbor_value_get_uint64(const CborValue *value, uint64_t *result) + * + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an unsigned integer + * value, the behavior is undefined, so checking with \ref cbor_value_get_type + * or with \ref cbor_value_is_unsigned_integer is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_unsigned_integer() + */ + +/** + * \fn CborError cbor_value_get_raw_integer(const CborValue *value, uint64_t *result) + * + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an integer value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_integer is recommended. + * + * This function is provided because CBOR negative integers can assume values + * that cannot be represented with normal 64-bit integer variables. + * + * If the integer is unsigned (that is, if cbor_value_is_unsigned_integer() + * returns true), then \a result will contain the actual value. If the integer + * is negative, then \a result will contain the absolute value of that integer, + * minus one. That is, \c {actual = -result - 1}. On architectures using two's + * complement for representation of negative integers, it is equivalent to say + * that \a result will contain the bitwise negation of the actual value. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_integer() + */ + +/** + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an integer value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_integer is recommended. + * + * Unlike \ref cbor_value_get_int64(), this function performs a check to see if the + * stored integer fits in \a result without data loss. If the number is outside + * the valid range for the data type, this function returns the recoverable + * error CborErrorDataTooLarge. In that case, use either + * cbor_value_get_uint64() (if the number is positive) or + * cbor_value_get_raw_integer(). + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_integer(), cbor_value_get_int64() + */ +CborError cbor_value_get_int64_checked(const CborValue *value, int64_t *result) +{ + uint64_t v; + cbor_assert(cbor_value_is_integer(value)); + v = _cbor_value_extract_int64_helper(value); + + /* Check before converting, as the standard says (C11 6.3.1.3 paragraph 3): + * "[if] the new type is signed and the value cannot be represented in it; either the + * result is implementation-defined or an implementation-defined signal is raised." + * + * The range for int64_t is -2^63 to 2^63-1 (int64_t is required to be + * two's complement, C11 7.20.1.1 paragraph 3), which in CBOR is + * represented the same way, differing only on the "sign bit" (the major + * type). + */ + + if (unlikely(v > (uint64_t)INT64_MAX)) + return CborErrorDataTooLarge; + + *result = v; + if (value->flags & CborIteratorFlag_NegativeInteger) + *result = -*result - 1; + return CborNoError; +} + +/** + * Retrieves the CBOR integer value that \a value points to and stores it in \a + * result. If the iterator \a value does not point to an integer value, the + * behavior is undefined, so checking with \ref cbor_value_get_type or with + * \ref cbor_value_is_integer is recommended. + * + * Unlike \ref cbor_value_get_int(), this function performs a check to see if the + * stored integer fits in \a result without data loss. If the number is outside + * the valid range for the data type, this function returns the recoverable + * error CborErrorDataTooLarge. In that case, use one of the other integer + * functions to obtain the value. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_integer(), cbor_value_get_int64(), + * cbor_value_get_uint64(), cbor_value_get_int64_checked(), cbor_value_get_raw_integer() + */ +CborError cbor_value_get_int_checked(const CborValue *value, int *result) +{ + uint64_t v; + cbor_assert(cbor_value_is_integer(value)); + v = _cbor_value_extract_int64_helper(value); + + /* Check before converting, as the standard says (C11 6.3.1.3 paragraph 3): + * "[if] the new type is signed and the value cannot be represented in it; either the + * result is implementation-defined or an implementation-defined signal is raised." + * + * But we can convert from signed to unsigned without fault (paragraph 2). + * + * The range for int is implementation-defined and int is not guaranteed to use + * two's complement representation (although int32_t is). + */ + + if (value->flags & CborIteratorFlag_NegativeInteger) { + if (unlikely(v > (unsigned) -(INT_MIN + 1))) + return CborErrorDataTooLarge; + + *result = (int)v; + *result = -*result - 1; + } else { + if (unlikely(v > (uint64_t)INT_MAX)) + return CborErrorDataTooLarge; + + *result = (int)v; + } + return CborNoError; + +} + +/** + * \fn bool cbor_value_is_length_known(const CborValue *value) + * + * Returns true if the length of this type is known without calculation. That + * is, if the length of this CBOR string, map or array is encoded in the data + * stream, this function returns true. If the length is not encoded, it returns + * false. + * + * If the length is known, code can call cbor_value_get_string_length(), + * cbor_value_get_array_length() or cbor_value_get_map_length() to obtain the + * length. If the length is not known but is necessary, code can use the + * cbor_value_calculate_string_length() function (no equivalent function is + * provided for maps and arrays). + */ + +/** + * \fn bool cbor_value_is_text_string(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR text + * string. CBOR text strings are UTF-8 encoded and usually contain + * human-readable text. + * + * \sa cbor_value_is_valid(), cbor_value_get_string_length(), cbor_value_calculate_string_length(), + * cbor_value_copy_text_string(), cbor_value_dup_text_string() + */ + +/** + * \fn bool cbor_value_is_byte_string(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR text + * string. CBOR byte strings are binary data with no specified encoding or + * format. + * + * \sa cbor_value_is_valid(), cbor_value_get_string_length(), cbor_value_calculate_string_length(), + * cbor_value_copy_byte_string(), cbor_value_dup_byte_string() + */ + +/** + * \fn CborError cbor_value_get_string_length(const CborValue *value, size_t *length) + * + * Extracts the length of the byte or text string that \a value points to and + * stores it in \a result. If the iterator \a value does not point to a text + * string or a byte string, the behaviour is undefined, so checking with \ref + * cbor_value_get_type, with \ref cbor_value_is_text_string or \ref + * cbor_value_is_byte_string is recommended. + * + * If the length of this string is not encoded in the CBOR data stream, this + * function will return the recoverable error CborErrorUnknownLength. You may + * also check whether that is the case by using cbor_value_is_length_known(). + * + * If the length of the string is required but the length was not encoded, use + * cbor_value_calculate_string_length(), but note that that function does not + * run in constant time. + * + * \note On 32-bit platforms, this function will return error condition of \ref + * CborErrorDataTooLarge if the stream indicates a length that is too big to + * fit in 32-bit. + * + * \sa cbor_value_is_valid(), cbor_value_is_length_known(), cbor_value_calculate_string_length() + */ + +/** + * Calculates the length of the byte or text string that \a value points to and + * stores it in \a len. If the iterator \a value does not point to a text + * string or a byte string, the behaviour is undefined, so checking with \ref + * cbor_value_get_type, with \ref cbor_value_is_text_string or \ref + * cbor_value_is_byte_string is recommended. + * + * This function is different from cbor_value_get_string_length() in that it + * calculates the length even for strings sent in chunks. For that reason, this + * function may not run in constant time (it will run in O(n) time on the + * number of chunks). It does use constant memory (O(1)). + * + * \note On 32-bit platforms, this function will return error condition of \ref + * CborErrorDataTooLarge if the stream indicates a length that is too big to + * fit in 32-bit. + * + * \sa cbor_value_get_string_length(), cbor_value_copy_text_string(), cbor_value_copy_byte_string(), cbor_value_is_length_known() + */ +CborError cbor_value_calculate_string_length(const CborValue *value, size_t *len) +{ + *len = SIZE_MAX; + return _cbor_value_copy_string(value, NULL, len, NULL); +} + +static inline void prepare_string_iteration(CborValue *it) +{ + if (!cbor_value_is_length_known(it)) { + /* chunked string: we're before the first chunk; + * advance to the first chunk */ + ++it->ptr; + it->flags |= CborIteratorFlag_IteratingStringChunks; + } +} + +CborError CBOR_INTERNAL_API_CC _cbor_value_prepare_string_iteration(CborValue *it) +{ + cbor_assert((it->flags & CborIteratorFlag_IteratingStringChunks) == 0); + prepare_string_iteration(it); + + /* are we at the end? */ + if (it->ptr == it->parser->end) + return CborErrorUnexpectedEOF; + return CborNoError; +} + +static CborError get_string_chunk(CborValue *it, const void **bufferptr, size_t *len) +{ + CborError err; + + /* Possible states: + * length known | iterating | meaning + * no | no | before the first chunk of a chunked string + * yes | no | at a non-chunked string + * no | yes | second or later chunk + * yes | yes | after a non-chunked string + */ + if (it->flags & CborIteratorFlag_IteratingStringChunks) { + /* already iterating */ + if (cbor_value_is_length_known(it)) { + /* if the length was known, it wasn't chunked, so finish iteration */ + goto last_chunk; + } + } else { + prepare_string_iteration(it); + } + + /* are we at the end? */ + if (it->ptr == it->parser->end) + return CborErrorUnexpectedEOF; + + if (*it->ptr == BreakByte) { + /* last chunk */ + ++it->ptr; +last_chunk: + *bufferptr = NULL; + *len = 0; + return preparse_next_value(it); + } else if ((uint8_t)(*it->ptr & MajorTypeMask) == it->type) { + err = extract_length(it->parser, &it->ptr, len); + if (err) + return err; + if (*len > (size_t)(it->parser->end - it->ptr)) + return CborErrorUnexpectedEOF; + + *bufferptr = it->ptr; + it->ptr += *len; + } else { + return CborErrorIllegalType; + } + + it->flags |= CborIteratorFlag_IteratingStringChunks; + return CborNoError; +} + +CborError CBOR_INTERNAL_API_CC +_cbor_value_get_string_chunk(const CborValue *value, const void **bufferptr, + size_t *len, CborValue *next) +{ + CborValue tmp; + if (!next) + next = &tmp; + *next = *value; + return get_string_chunk(next, bufferptr, len); +} + +/* We return uintptr_t so that we can pass memcpy directly as the iteration + * function. The choice is to optimize for memcpy, which is used in the base + * parser API (cbor_value_copy_string), while memcmp is used in convenience API + * only. */ +typedef uintptr_t (*IterateFunction)(char *, const uint8_t *, size_t); + +static uintptr_t iterate_noop(char *dest, const uint8_t *src, size_t len) +{ + (void)dest; + (void)src; + (void)len; + return true; +} + +static uintptr_t iterate_memcmp(char *s1, const uint8_t *s2, size_t len) +{ + return memcmp(s1, (const char *)s2, len) == 0; +} + +static uintptr_t iterate_memcpy(char *dest, const uint8_t *src, size_t len) +{ + return (uintptr_t)memcpy(dest, src, len); +} + +static CborError iterate_string_chunks(const CborValue *value, char *buffer, size_t *buflen, + bool *result, CborValue *next, IterateFunction func) +{ + CborError err; + CborValue tmp; + size_t total = 0; + const void *ptr; + + cbor_assert(cbor_value_is_byte_string(value) || cbor_value_is_text_string(value)); + if (!next) + next = &tmp; + *next = *value; + *result = true; + + while (1) { + size_t newTotal; + size_t chunkLen; + err = get_string_chunk(next, &ptr, &chunkLen); + if (err) + return err; + if (!ptr) + break; + + if (unlikely(add_check_overflow(total, chunkLen, &newTotal))) + return CborErrorDataTooLarge; + + if (*result && *buflen >= newTotal) + *result = !!func(buffer + total, (const uint8_t *)ptr, chunkLen); + else + *result = false; + + total = newTotal; + } + + /* is there enough room for the ending NUL byte? */ + if (*result && *buflen > total) { + uint8_t nul[] = { 0 }; + *result = !!func(buffer + total, nul, 1); + } + *buflen = total; + return CborNoError; +} + +/** + * \fn CborError cbor_value_copy_text_string(const CborValue *value, char *buffer, size_t *buflen, CborValue *next) + * + * Copies the string pointed to by \a value into the buffer provided at \a buffer + * of \a buflen bytes. If \a buffer is a NULL pointer, this function will not + * copy anything and will only update the \a next value. + * + * If the iterator \a value does not point to a text string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_text_string is recommended. + * + * If the provided buffer length was too small, this function returns an error + * condition of \ref CborErrorOutOfMemory. If you need to calculate the length + * of the string in order to preallocate a buffer, use + * cbor_value_calculate_string_length(). + * + * On success, this function sets the number of bytes copied to \c{*buflen}. If + * the buffer is large enough, this function will insert a null byte after the + * last copied byte, to facilitate manipulation of text strings. That byte is + * not included in the returned value of \c{*buflen}. If there was no space for + * the terminating null, no error is returned, so callers must check the value + * of *buflen after the call, before relying on the '\0'; if it has not been + * changed by the call, there is no '\0'-termination on the buffer's contents. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. If \a value points to the last item, then \a next will be + * invalid. + * + * This function may not run in constant time (it will run in O(n) time on the + * number of chunks). It requires constant memory (O(1)). + * + * \note This function does not perform UTF-8 validation on the incoming text + * string. + * + * \sa cbor_value_get_text_string_chunk() cbor_value_dup_text_string(), cbor_value_copy_byte_string(), cbor_value_get_string_length(), cbor_value_calculate_string_length() + */ + +/** + * \fn CborError cbor_value_copy_byte_string(const CborValue *value, uint8_t *buffer, size_t *buflen, CborValue *next) + * + * Copies the string pointed by \a value into the buffer provided at \a buffer + * of \a buflen bytes. If \a buffer is a NULL pointer, this function will not + * copy anything and will only update the \a next value. + * + * If the iterator \a value does not point to a byte string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_byte_string is recommended. + * + * If the provided buffer length was too small, this function returns an error + * condition of \ref CborErrorOutOfMemory. If you need to calculate the length + * of the string in order to preallocate a buffer, use + * cbor_value_calculate_string_length(). + * + * On success, this function sets the number of bytes copied to \c{*buflen}. If + * the buffer is large enough, this function will insert a null byte after the + * last copied byte, to facilitate manipulation of null-terminated strings. + * That byte is not included in the returned value of \c{*buflen}. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. If \a value points to the last item, then \a next will be + * invalid. + * + * This function may not run in constant time (it will run in O(n) time on the + * number of chunks). It requires constant memory (O(1)). + * + * \sa cbor_value_get_byte_string_chunk(), cbor_value_dup_text_string(), cbor_value_copy_text_string(), cbor_value_get_string_length(), cbor_value_calculate_string_length() + */ + +CborError _cbor_value_copy_string(const CborValue *value, void *buffer, + size_t *buflen, CborValue *next) +{ + bool copied_all; + CborError err = iterate_string_chunks(value, (char*)buffer, buflen, &copied_all, next, + buffer ? iterate_memcpy : iterate_noop); + return err ? err : + copied_all ? CborNoError : CborErrorOutOfMemory; +} + +/** + * Compares the entry \a value with the string \a string and stores the result + * in \a result. If the value is different from \a string \a result will + * contain \c false. + * + * The entry at \a value may be a tagged string. If \a value is not a string or + * a tagged string, the comparison result will be false. + * + * CBOR requires text strings to be encoded in UTF-8, but this function does + * not validate either the strings in the stream or the string \a string to be + * matched. Moreover, comparison is done on strict codepoint comparison, + * without any Unicode normalization. + * + * This function may not run in constant time (it will run in O(n) time on the + * number of chunks). It requires constant memory (O(1)). + * + * \sa cbor_value_skip_tag(), cbor_value_copy_text_string() + */ +CborError cbor_value_text_string_equals(const CborValue *value, const char *string, bool *result) +{ + size_t len; + CborValue copy = *value; + CborError err = cbor_value_skip_tag(©); + if (err) + return err; + if (!cbor_value_is_text_string(©)) { + *result = false; + return CborNoError; + } + + len = strlen(string); + return iterate_string_chunks(©, CONST_CAST(char *, string), &len, result, NULL, iterate_memcmp); +} + +/** + * \fn bool cbor_value_is_array(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR array. + * + * \sa cbor_value_is_valid(), cbor_value_is_map() + */ + +/** + * \fn CborError cbor_value_get_array_length(const CborValue *value, size_t *length) + * + * Extracts the length of the CBOR array that \a value points to and stores it + * in \a result. If the iterator \a value does not point to a CBOR array, the + * behaviour is undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_array is recommended. + * + * If the length of this array is not encoded in the CBOR data stream, this + * function will return the recoverable error CborErrorUnknownLength. You may + * also check whether that is the case by using cbor_value_is_length_known(). + * + * \note On 32-bit platforms, this function will return error condition of \ref + * CborErrorDataTooLarge if the stream indicates a length that is too big to + * fit in 32-bit. + * + * \sa cbor_value_is_valid(), cbor_value_is_length_known() + */ + +/** + * \fn bool cbor_value_is_map(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR map. + * + * \sa cbor_value_is_valid(), cbor_value_is_array() + */ + +/** + * \fn CborError cbor_value_get_map_length(const CborValue *value, size_t *length) + * + * Extracts the length of the CBOR map that \a value points to and stores it in + * \a result. If the iterator \a value does not point to a CBOR map, the + * behaviour is undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_map is recommended. + * + * If the length of this map is not encoded in the CBOR data stream, this + * function will return the recoverable error CborErrorUnknownLength. You may + * also check whether that is the case by using cbor_value_is_length_known(). + * + * \note On 32-bit platforms, this function will return error condition of \ref + * CborErrorDataTooLarge if the stream indicates a length that is too big to + * fit in 32-bit. + * + * \sa cbor_value_is_valid(), cbor_value_is_length_known() + */ + +/** + * Attempts to find the value in map \a map that corresponds to the text string + * entry \a string. If the iterator \a value does not point to a CBOR map, the + * behaviour is undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_map is recommended. + * + * If the item is found, it is stored in \a result. If no item is found + * matching the key, then \a result will contain an element of type \ref + * CborInvalidType. Matching is performed using + * cbor_value_text_string_equals(), so tagged strings will also match. + * + * This function has a time complexity of O(n) where n is the number of + * elements in the map to be searched. In addition, this function is has O(n) + * memory requirement based on the number of nested containers (maps or arrays) + * found as elements of this map. + * + * \sa cbor_value_is_valid(), cbor_value_text_string_equals(), cbor_value_advance() + */ +CborError cbor_value_map_find_value(const CborValue *map, const char *string, CborValue *element) +{ + CborError err; + size_t len = strlen(string); + cbor_assert(cbor_value_is_map(map)); + err = cbor_value_enter_container(map, element); + if (err) + goto error; + + while (!cbor_value_at_end(element)) { + /* find the non-tag so we can compare */ + err = cbor_value_skip_tag(element); + if (err) + goto error; + if (cbor_value_is_text_string(element)) { + bool equals; + size_t dummyLen = len; + err = iterate_string_chunks(element, CONST_CAST(char *, string), &dummyLen, + &equals, element, iterate_memcmp); + if (err) + goto error; + if (equals) + return preparse_value(element); + } else { + /* skip this key */ + err = cbor_value_advance(element); + if (err) + goto error; + } + + /* skip this value */ + err = cbor_value_skip_tag(element); + if (err) + goto error; + err = cbor_value_advance(element); + if (err) + goto error; + } + + /* not found */ + element->type = CborInvalidType; + return CborNoError; + +error: + element->type = CborInvalidType; + return err; +} + +/** + * \fn bool cbor_value_is_float(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR + * single-precision floating point (32-bit). + * + * \sa cbor_value_is_valid(), cbor_value_is_double(), cbor_value_is_half_float() + */ + +/** + * \fn CborError cbor_value_get_float(const CborValue *value, float *result) + * + * Retrieves the CBOR single-precision floating point (32-bit) value that \a + * value points to and stores it in \a result. If the iterator \a value does + * not point to a single-precision floating point value, the behavior is + * undefined, so checking with \ref cbor_value_get_type or with \ref + * cbor_value_is_float is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_float(), cbor_value_get_double() + */ + +/** + * \fn bool cbor_value_is_double(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR + * double-precision floating point (64-bit). + * + * \sa cbor_value_is_valid(), cbor_value_is_float(), cbor_value_is_half_float() + */ + +/** + * \fn CborError cbor_value_get_double(const CborValue *value, float *result) + * + * Retrieves the CBOR double-precision floating point (64-bit) value that \a + * value points to and stores it in \a result. If the iterator \a value does + * not point to a double-precision floating point value, the behavior is + * undefined, so checking with \ref cbor_value_get_type or with \ref + * cbor_value_is_double is recommended. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_double(), cbor_value_get_float() + */ + +/** + * \fn bool cbor_value_is_half_float(const CborValue *value) + * + * Returns true if the iterator \a value is valid and points to a CBOR + * single-precision floating point (16-bit). + * + * \sa cbor_value_is_valid(), cbor_value_is_double(), cbor_value_is_float() + */ + +/** + * Retrieves the CBOR half-precision floating point (16-bit) value that \a + * value points to and stores it in \a result. If the iterator \a value does + * not point to a half-precision floating point value, the behavior is + * undefined, so checking with \ref cbor_value_get_type or with \ref + * cbor_value_is_half_float is recommended. + * + * Note: since the C language does not have a standard type for half-precision + * floating point, this function takes a \c{void *} as a parameter for the + * storage area, which must be at least 16 bits wide. + * + * \sa cbor_value_get_type(), cbor_value_is_valid(), cbor_value_is_half_float(), cbor_value_get_float() + */ +CborError cbor_value_get_half_float(const CborValue *value, void *result) +{ + uint16_t v; + cbor_assert(cbor_value_is_half_float(value)); + + /* size has been computed already */ + v = get16(value->ptr + 1); + memcpy(result, &v, sizeof(v)); + return CborNoError; +} + +/** @} */ diff --git a/lib/tinycbor/cborparser_dup_string.c b/lib/tinycbor/cborparser_dup_string.c new file mode 100644 index 000000000..061c5ac77 --- /dev/null +++ b/lib/tinycbor/cborparser_dup_string.c @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2016 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#ifndef _BSD_SOURCE +#define _BSD_SOURCE 1 +#endif +#ifndef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE 1 +#endif +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" +#include "compilersupport_p.h" +#include + +/** + * \fn CborError cbor_value_dup_text_string(const CborValue *value, char **buffer, size_t *buflen, CborValue *next) + * + * Allocates memory for the string pointed by \a value and copies it into this + * buffer. The pointer to the buffer is stored in \a buffer and the number of + * bytes copied is stored in \a buflen (those variables must not be NULL). + * + * If the iterator \a value does not point to a text string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_text_string is recommended. + * + * If \c malloc returns a NULL pointer, this function will return error + * condition \ref CborErrorOutOfMemory. + * + * On success, \c{*buffer} will contain a valid pointer that must be freed by + * calling \c{free()}. This is the case even for zero-length strings. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. If \a value points to the last item, then \a next will be + * invalid. + * + * This function may not run in constant time (it will run in O(n) time on the + * number of chunks). It requires constant memory (O(1)) in addition to the + * malloc'ed block. + * + * \note This function does not perform UTF-8 validation on the incoming text + * string. + * + * \sa cbor_value_get_text_string_chunk(), cbor_value_copy_text_string(), cbor_value_dup_byte_string() + */ + +/** + * \fn CborError cbor_value_dup_byte_string(const CborValue *value, uint8_t **buffer, size_t *buflen, CborValue *next) + * + * Allocates memory for the string pointed by \a value and copies it into this + * buffer. The pointer to the buffer is stored in \a buffer and the number of + * bytes copied is stored in \a buflen (those variables must not be NULL). + * + * If the iterator \a value does not point to a byte string, the behaviour is + * undefined, so checking with \ref cbor_value_get_type or \ref + * cbor_value_is_byte_string is recommended. + * + * If \c malloc returns a NULL pointer, this function will return error + * condition \ref CborErrorOutOfMemory. + * + * On success, \c{*buffer} will contain a valid pointer that must be freed by + * calling \c{free()}. This is the case even for zero-length strings. + * + * The \a next pointer, if not null, will be updated to point to the next item + * after this string. If \a value points to the last item, then \a next will be + * invalid. + * + * This function may not run in constant time (it will run in O(n) time on the + * number of chunks). It requires constant memory (O(1)) in addition to the + * malloc'ed block. + * + * \sa cbor_value_get_text_string_chunk(), cbor_value_copy_byte_string(), cbor_value_dup_text_string() + */ +CborError _cbor_value_dup_string(const CborValue *value, void **buffer, size_t *buflen, CborValue *next) +{ + CborError err; + cbor_assert(buffer); + cbor_assert(buflen); + *buflen = SIZE_MAX; + err = _cbor_value_copy_string(value, NULL, buflen, NULL); + if (err) + return err; + + ++*buflen; + *buffer = malloc(*buflen); + if (!*buffer) { + /* out of memory */ + return CborErrorOutOfMemory; + } + err = _cbor_value_copy_string(value, *buffer, buflen, next); + if (err) { + free(*buffer); + return err; + } + return CborNoError; +} diff --git a/lib/tinycbor/cborpretty.c b/lib/tinycbor/cborpretty.c new file mode 100644 index 000000000..b8825deee --- /dev/null +++ b/lib/tinycbor/cborpretty.c @@ -0,0 +1,578 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#define _BSD_SOURCE 1 +#define _DEFAULT_SOURCE 1 +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" +#include "cborinternal_p.h" +#include "compilersupport_p.h" +#include "utf8_p.h" + +#include +#include + +/** + * \defgroup CborPretty Converting CBOR to text + * \brief Group of functions used to convert CBOR to text form. + * + * This group contains two functions that can be used to convert a \ref + * CborValue object to a text representation. This module attempts to follow + * the recommendations from RFC 7049 section 6 "Diagnostic Notation", though it + * has a few differences. They are noted below. + * + * TinyCBOR does not provide a way to convert from the text representation back + * to encoded form. To produce a text form meant to be parsed, CborToJson is + * recommended instead. + * + * Either of the functions in this section will attempt to convert exactly one + * CborValue object to text. Those functions may return any error documented + * for the functions for CborParsing. In addition, if the C standard library + * stream functions return with error, the text conversion will return with + * error CborErrorIO. + * + * These functions also perform UTF-8 validation in CBOR text strings. If they + * encounter a sequence of bytes that is not permitted in UTF-8, they will return + * CborErrorInvalidUtf8TextString. That includes encoding of surrogate points + * in UTF-8. + * + * \warning The output type produced by these functions is not guaranteed to + * remain stable. A future update of TinyCBOR may produce different output for + * the same input and parsers may be unable to handle it. + * + * \sa CborParsing, CborToJson, cbor_parser_init() + */ + +/** + * \addtogroup CborPretty + * @{ + *

Text format

+ * + * As described in RFC 7049 section 6 "Diagnostic Notation", the format is + * largely borrowed from JSON, but modified to suit CBOR's different data + * types. TinyCBOR makes further modifications to distinguish different, but + * similar values. + * + * CBOR values are currently encoded as follows: + * \par Integrals (unsigned and negative) + * Base-10 (decimal) text representation of the value + * \par Byte strings: + * "h'" followed by the Base16 (hex) representation of the binary data, followed by an ending quote (') + * \par Text strings: + * C-style escaped string in quotes, with C11/C++11 escaping of Unicode codepoints above U+007F. + * \par Tags: + * Tag value, with the tagged value in parentheses. No special encoding of the tagged value is performed. + * \par Simple types: + * "simple(nn)" where \c nn is the simple value + * \par Null: + * \c null + * \par Undefined: + * \c undefined + * \par Booleans: + * \c true or \c false + * \par Floating point: + * If NaN or infinite, the actual words \c NaN or \c infinite. + * Otherwise, the decimal representation with as many digits as necessary to ensure no loss of information. + * By default, float values are suffixed by "f" and half-float values suffixed by "f16" (doubles have no suffix). + * If the CborPrettyNumericEncodingIndicators flag is active, the values instead are encoded following the + * Section 6 recommended encoding indicators: float values are suffixed with "_2" and half-float with "_1". + * A decimal point is always present. + * \par Arrays: + * Comma-separated list of elements, enclosed in square brackets ("[" and "]"). + * \par Maps: + * Comma-separated list of key-value pairs, with the key and value separated + * by a colon (":"), enclosed in curly braces ("{" and "}"). + * + * The CborPrettyFlags enumerator contains flags to control some aspects of the + * encoding: + * \par String fragmentation + * When the CborPrettyShowStringFragments option is active, text and byte + * strings that are transmitted in fragments are shown instead inside + * parentheses ("(" and ")") with no preceding number and each fragment is + * displayed individually. If a tag precedes the string, then the output + * will contain a double set of parentheses. If the option is not active, + * the fragments are merged together and the display will not show any + * difference from a string transmitted with determinate length. + * \par Encoding indicators + * Numbers and lengths in CBOR can be encoded in multiple representations. + * If the CborPrettyIndicateOverlongNumbers option is active, numbers + * and lengths that are transmitted in a longer encoding than necessary + * will be indicated, by appending an underscore ("_") to either the + * number or the opening bracket or brace, followed by a number + * indicating the CBOR additional information: 0 for 1 byte, 1 for 2 + * bytes, 2 for 4 bytes and 3 for 8 bytes. + * If the CborPrettyIndicateIndeterminateLength option is active, maps, + * arrays and strings encoded with indeterminate length will be marked by + * an underscore after the opening bracket or brace or the string (if not + * showing fragments), without a number after it. + */ + +/** + * \enum CborPrettyFlags + * The CborPrettyFlags enum contains flags that control the conversion of CBOR to text format. + * + * \value CborPrettyNumericEncodingIndicators Use numeric encoding indicators instead of textual for float and half-float. + * \value CborPrettyTextualEncodingIndicators Use textual encoding indicators for float ("f") and half-float ("f16"). + * \value CborPrettyIndicateIndeterminateLength (default) Indicate when a map or array has indeterminate length. + * \value CborPrettyIndicateOverlongNumbers Indicate when a number or length was encoded with more bytes than needed. + * \value CborPrettyShowStringFragments If the byte or text string is transmitted in chunks, show each individually. + * \value CborPrettyMergeStringFragment Merge all chunked byte or text strings and display them in a single entry. + * \value CborPrettyDefaultFlags Default conversion flags. + */ + +#ifndef CBOR_NO_FLOATING_POINT +static inline bool convertToUint64(double v, uint64_t *absolute) +{ + double supremum; + v = fabs(v); + + /* C11 standard section 6.3.1.4 "Real floating and integer" says: + * + * 1 When a finite value of real floating type is converted to an integer + * type other than _Bool, the fractional part is discarded (i.e., the + * value is truncated toward zero). If the value of the integral part + * cannot be represented by the integer type, the behavior is undefined. + * + * So we must perform a range check that v <= UINT64_MAX, but we can't use + * UINT64_MAX + 1.0 because the standard continues: + * + * 2 When a value of integer type is converted to a real floating type, if + * the value being converted can be represented exactly in the new type, + * it is unchanged. If the value being converted is in the range of + * values that can be represented but cannot be represented exactly, the + * result is either the nearest higher or nearest lower representable + * value, chosen in an implementation-defined manner. + */ + supremum = -2.0 * INT64_MIN; /* -2 * (- 2^63) == 2^64 */ + if (v >= supremum) + return false; + + /* Now we can convert, these two conversions cannot be UB */ + *absolute = v; + return *absolute == v; +} +#endif + +static void printRecursionLimit(CborStreamFunction stream, void *out) +{ + stream(out, ""); +} + +static CborError hexDump(CborStreamFunction stream, void *out, const void *ptr, size_t n) +{ + const uint8_t *buffer = (const uint8_t *)ptr; + CborError err = CborNoError; + while (n-- && !err) + err = stream(out, "%02" PRIx8, *buffer++); + + return err; +} + +/* This function decodes buffer as UTF-8 and prints as escaped UTF-16. + * On UTF-8 decoding error, it returns CborErrorInvalidUtf8TextString */ +static CborError utf8EscapedDump(CborStreamFunction stream, void *out, const void *ptr, size_t n) +{ + const uint8_t *buffer = (const uint8_t *)ptr; + const uint8_t * const end = buffer + n; + CborError err = CborNoError; + + while (buffer < end && !err) { + uint32_t uc = get_utf8(&buffer, end); + if (uc == ~0U) + return CborErrorInvalidUtf8TextString; + + if (uc < 0x80) { + /* single-byte UTF-8 */ + unsigned char escaped = (unsigned char)uc; + if (uc < 0x7f && uc >= 0x20 && uc != '\\' && uc != '"') { + err = stream(out, "%c", (char)uc); + continue; + } + + /* print as an escape sequence */ + switch (uc) { + case '"': + case '\\': + break; + case '\b': + escaped = 'b'; + break; + case '\f': + escaped = 'f'; + break; + case '\n': + escaped = 'n'; + break; + case '\r': + escaped = 'r'; + break; + case '\t': + escaped = 't'; + break; + default: + goto print_utf16; + } + err = stream(out, "\\%c", escaped); + continue; + } + + /* now print the sequence */ + if (uc > 0xffffU) { + /* needs surrogate pairs */ + err = stream(out, "\\u%04" PRIX32 "\\u%04" PRIX32, + (uc >> 10) + 0xd7c0, /* high surrogate */ + (uc % 0x0400) + 0xdc00); + } else { +print_utf16: + /* no surrogate pair needed */ + err = stream(out, "\\u%04" PRIX32, uc); + } + } + return err; +} + +static const char *resolve_indicator(const uint8_t *ptr, const uint8_t *end, int flags) +{ + static const char indicators[8][3] = { + "_0", "_1", "_2", "_3", + "", "", "", /* these are not possible */ + "_" + }; + const char *no_indicator = indicators[5]; /* empty string */ + uint8_t additional_information; + uint8_t expected_information; + uint64_t value; + CborError err; + + if (ptr == end) + return NULL; /* CborErrorUnexpectedEOF */ + + additional_information = (*ptr & SmallValueMask); + if (additional_information < Value8Bit) + return no_indicator; + + /* determine whether to show anything */ + if ((flags & CborPrettyIndicateIndeterminateLength) && + additional_information == IndefiniteLength) + return indicators[IndefiniteLength - Value8Bit]; + if ((flags & CborPrettyIndicateOverlongNumbers) == 0) + return no_indicator; + + err = _cbor_value_extract_number(&ptr, end, &value); + if (err) + return NULL; /* CborErrorUnexpectedEOF */ + + expected_information = Value8Bit - 1; + if (value >= Value8Bit) + ++expected_information; + if (value > 0xffU) + ++expected_information; + if (value > 0xffffU) + ++expected_information; + if (value > 0xffffffffU) + ++expected_information; + return expected_information == additional_information ? + no_indicator : + indicators[additional_information - Value8Bit]; +} + +static const char *get_indicator(const CborValue *it, int flags) +{ + return resolve_indicator(it->ptr, it->parser->end, flags); +} + +static CborError value_to_pretty(CborStreamFunction stream, void *out, CborValue *it, int flags, int recursionsLeft); +static CborError container_to_pretty(CborStreamFunction stream, void *out, CborValue *it, CborType containerType, + int flags, int recursionsLeft) +{ + const char *comma = ""; + CborError err = CborNoError; + + if (!recursionsLeft) { + printRecursionLimit(stream, out); + return err; /* do allow the dumping to continue */ + } + + while (!cbor_value_at_end(it) && !err) { + err = stream(out, "%s", comma); + comma = ", "; + + if (!err) + err = value_to_pretty(stream, out, it, flags, recursionsLeft); + + if (containerType == CborArrayType) + continue; + + /* map: that was the key, so get the value */ + if (!err) + err = stream(out, ": "); + if (!err) + err = value_to_pretty(stream, out, it, flags, recursionsLeft); + } + return err; +} + +static CborError value_to_pretty(CborStreamFunction stream, void *out, CborValue *it, int flags, int recursionsLeft) +{ + CborError err = CborNoError; + CborType type = cbor_value_get_type(it); + switch (type) { + case CborArrayType: + case CborMapType: { + /* recursive type */ + CborValue recursed; + const char *indicator = get_indicator(it, flags); + const char *space = *indicator ? " " : indicator; + + err = stream(out, "%c%s%s", type == CborArrayType ? '[' : '{', indicator, space); + if (err) + return err; + + err = cbor_value_enter_container(it, &recursed); + if (err) { + it->ptr = recursed.ptr; + return err; /* parse error */ + } + err = container_to_pretty(stream, out, &recursed, type, flags, recursionsLeft - 1); + if (err) { + it->ptr = recursed.ptr; + return err; /* parse error */ + } + err = cbor_value_leave_container(it, &recursed); + if (err) + return err; /* parse error */ + + return stream(out, type == CborArrayType ? "]" : "}"); + } + + case CborIntegerType: { + uint64_t val; + cbor_value_get_raw_integer(it, &val); /* can't fail */ + + if (cbor_value_is_unsigned_integer(it)) { + err = stream(out, "%" PRIu64, val); + } else { + /* CBOR stores the negative number X as -1 - X + * (that is, -1 is stored as 0, -2 as 1 and so forth) */ + if (++val) { /* unsigned overflow may happen */ + err = stream(out, "-%" PRIu64, val); + } else { + /* overflown + * 0xffff`ffff`ffff`ffff + 1 = + * 0x1`0000`0000`0000`0000 = 18446744073709551616 (2^64) */ + err = stream(out, "-18446744073709551616"); + } + } + if (!err) + err = stream(out, "%s", get_indicator(it, flags)); + break; + } + + case CborByteStringType: + case CborTextStringType: { + size_t n = 0; + const void *ptr; + bool showingFragments = (flags & CborPrettyShowStringFragments) && !cbor_value_is_length_known(it); + const char *separator = ""; + char close = '\''; + char open[3] = "h'"; + const char *indicator = NULL; + + if (type == CborTextStringType) { + close = open[0] = '"'; + open[1] = '\0'; + } + + if (showingFragments) { + err = stream(out, "(_ "); + if (!err) + err = _cbor_value_prepare_string_iteration(it); + } else { + err = stream(out, "%s", open); + } + + while (!err) { + if (showingFragments || indicator == NULL) { + /* any iteration, except the second for a non-chunked string */ + indicator = resolve_indicator(it->ptr, it->parser->end, flags); + } + + err = _cbor_value_get_string_chunk(it, &ptr, &n, it); + if (!ptr) + break; + + if (!err && showingFragments) + err = stream(out, "%s%s", separator, open); + if (!err) + err = (type == CborByteStringType ? + hexDump(stream, out, ptr, n) : + utf8EscapedDump(stream, out, ptr, n)); + if (!err && showingFragments) { + err = stream(out, "%c%s", close, indicator); + separator = ", "; + } + } + + if (!err) { + if (showingFragments) + err = stream(out, ")"); + else + err = stream(out, "%c%s", close, indicator); + } + return err; + } + + case CborTagType: { + CborTag tag; + cbor_value_get_tag(it, &tag); /* can't fail */ + err = stream(out, "%" PRIu64 "%s(", tag, get_indicator(it, flags)); + if (!err) + err = cbor_value_advance_fixed(it); + if (!err && recursionsLeft) + err = value_to_pretty(stream, out, it, flags, recursionsLeft - 1); + else if (!err) + printRecursionLimit(stream, out); + if (!err) + err = stream(out, ")"); + return err; + } + + case CborSimpleType: { + /* simple types can't fail and can't have overlong encoding */ + uint8_t simple_type; + cbor_value_get_simple_type(it, &simple_type); + err = stream(out, "simple(%" PRIu8 ")", simple_type); + break; + } + + case CborNullType: + err = stream(out, "null"); + break; + + case CborUndefinedType: + err = stream(out, "undefined"); + break; + + case CborBooleanType: { + bool val; + cbor_value_get_boolean(it, &val); /* can't fail */ + err = stream(out, val ? "true" : "false"); + break; + } + +#ifndef CBOR_NO_FLOATING_POINT + case CborDoubleType: { + const char *suffix; + double val; + int r; + uint64_t ival; + + if (false) { + float f; + case CborFloatType: + cbor_value_get_float(it, &f); + val = f; + suffix = flags & CborPrettyNumericEncodingIndicators ? "_2" : "f"; + } else if (false) { + uint16_t f16; + case CborHalfFloatType: +#ifndef CBOR_NO_HALF_FLOAT_TYPE + cbor_value_get_half_float(it, &f16); + val = decode_half(f16); + suffix = flags & CborPrettyNumericEncodingIndicators ? "_1" : "f16"; +#else + (void)f16; + err = CborErrorUnsupportedType; + break; +#endif + } else { + cbor_value_get_double(it, &val); + suffix = ""; + } + + if ((flags & CborPrettyNumericEncodingIndicators) == 0) { + r = fpclassify(val); + if (r == FP_NAN || r == FP_INFINITE) + suffix = ""; + } + + if (convertToUint64(val, &ival)) { + /* this double value fits in a 64-bit integer, so show it as such + * (followed by a floating point suffix, to disambiguate) */ + err = stream(out, "%s%" PRIu64 ".%s", val < 0 ? "-" : "", ival, suffix); + } else { + /* this number is definitely not a 64-bit integer */ + err = stream(out, "%." DBL_DECIMAL_DIG_STR "g%s", val, suffix); + } + break; + } +#else + case CborDoubleType: + case CborFloatType: + case CborHalfFloatType: + err = CborErrorUnsupportedType; + break; +#endif /* !CBOR_NO_FLOATING_POINT */ + + case CborInvalidType: + err = stream(out, "invalid"); + if (err) + return err; + return CborErrorUnknownType; + } + + if (!err) + err = cbor_value_advance_fixed(it); + return err; +} + +/** + * Converts the current CBOR type pointed by \a value to its textual + * representation and writes it to the stream by calling the \a streamFunction. + * If an error occurs, this function returns an error code similar to + * \ref CborParsing. + * + * The textual representation can be controlled by the \a flags parameter (see + * \ref CborPrettyFlags for more information). + * + * If no error ocurred, this function advances \a value to the next element. + * Often, concatenating the text representation of multiple elements can be + * done by appending a comma to the output stream in between calls to this + * function. + * + * The \a streamFunction function will be called with the \a token value as the + * first parameter and a printf-style format string as the second, with a variable + * number of further parameters. + * + * \sa cbor_value_to_pretty(), cbor_value_to_json_advance() + */ +CborError cbor_value_to_pretty_stream(CborStreamFunction streamFunction, void *token, CborValue *value, int flags) +{ + return value_to_pretty(streamFunction, token, value, flags, CBOR_PARSER_MAX_RECURSIONS); +} + +/** @} */ diff --git a/lib/tinycbor/cborpretty_stdio.c b/lib/tinycbor/cborpretty_stdio.c new file mode 100644 index 000000000..20131850c --- /dev/null +++ b/lib/tinycbor/cborpretty_stdio.c @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#include "cbor.h" +#include +#include + +static CborError cbor_fprintf(void *out, const char *fmt, ...) +{ + int n; + + va_list list; + va_start(list, fmt); + n = vfprintf((FILE *)out, fmt, list); + va_end(list); + + return n < 0 ? CborErrorIO : CborNoError; +} + +/** + * \fn CborError cbor_value_to_pretty(FILE *out, const CborValue *value) + * + * Converts the current CBOR type pointed to by \a value to its textual + * representation and writes it to the \a out stream. If an error occurs, this + * function returns an error code similar to CborParsing. + * + * \sa cbor_value_to_pretty_advance(), cbor_value_to_json_advance() + */ + +/** + * Converts the current CBOR type pointed to by \a value to its textual + * representation and writes it to the \a out stream. If an error occurs, this + * function returns an error code similar to CborParsing. + * + * If no error ocurred, this function advances \a value to the next element. + * Often, concatenating the text representation of multiple elements can be + * done by appending a comma to the output stream in between calls to this + * function. + * + * \sa cbor_value_to_pretty(), cbor_value_to_pretty_stream(), cbor_value_to_json_advance() + */ +CborError cbor_value_to_pretty_advance(FILE *out, CborValue *value) +{ + return cbor_value_to_pretty_stream(cbor_fprintf, out, value, CborPrettyDefaultFlags); +} + +/** + * Converts the current CBOR type pointed to by \a value to its textual + * representation and writes it to the \a out stream. If an error occurs, this + * function returns an error code similar to CborParsing. + * + * The textual representation can be controlled by the \a flags parameter (see + * CborPrettyFlags for more information). + * + * If no error ocurred, this function advances \a value to the next element. + * Often, concatenating the text representation of multiple elements can be + * done by appending a comma to the output stream in between calls to this + * function. + * + * \sa cbor_value_to_pretty_stream(), cbor_value_to_pretty(), cbor_value_to_json_advance() + */ +CborError cbor_value_to_pretty_advance_flags(FILE *out, CborValue *value, int flags) +{ + return cbor_value_to_pretty_stream(cbor_fprintf, out, value, flags); +} + diff --git a/lib/tinycbor/cbortojson.c b/lib/tinycbor/cbortojson.c new file mode 100644 index 000000000..5a1a2e5dc --- /dev/null +++ b/lib/tinycbor/cbortojson.c @@ -0,0 +1,699 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#define _BSD_SOURCE 1 +#define _DEFAULT_SOURCE 1 +#define _GNU_SOURCE 1 +#define _POSIX_C_SOURCE 200809L +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" +#include "cborjson.h" +#include "cborinternal_p.h" +#include "compilersupport_p.h" + +#include +#include +#include +#include + +/** + * \defgroup CborToJson Converting CBOR to JSON + * \brief Group of functions used to convert CBOR to JSON. + * + * This group contains two functions that can be used to convert a \ref + * CborValue object to an equivalent JSON representation. This module attempts + * to follow the recommendations from RFC 7049 section 4.1 "Converting from + * CBOR to JSON", though it has a few differences. They are noted below. + * + * These functions produce a "minified" JSON output, with no spacing, + * indentation or line breaks. If those are necessary, they need to be applied + * in a post-processing phase. + * + * Note that JSON cannot support all CBOR types with fidelity, so the + * conversion is usually lossy. For that reason, TinyCBOR supports adding a set + * of metadata JSON values that can be used by a JSON-to-CBOR converter to + * restore the original data types. + * + * The TinyCBOR library does not provide a way to convert from JSON + * representation back to encoded form. However, it provides a tool called + * \c json2cbor which can be used for that purpose. That tool supports the + * metadata format that these functions may produce. + * + * Either of the functions in this section will attempt to convert exactly one + * CborValue object to JSON. Those functions may return any error documented + * for the functions for CborParsing. In addition, if the C standard library + * stream functions return with error, the text conversion will return with + * error CborErrorIO. + * + * These functions also perform UTF-8 validation in CBOR text strings. If they + * encounter a sequence of bytes that is not permitted in UTF-8, they will return + * CborErrorInvalidUtf8TextString. That includes encoding of surrogate points + * in UTF-8. + * + * \warning The metadata produced by these functions is not guaranteed to + * remain stable. A future update of TinyCBOR may produce different output for + * the same input and parsers may be unable to handle it. + * + * \sa CborParsing, CborPretty, cbor_parser_init() + */ + +/** + * \addtogroup CborToJson + * @{ + *

Conversion limitations

+ * + * When converting from CBOR to JSON, there may be information loss. This + * section lists the possible scenarios. + * + * \par Number precision: + * ALL JSON numbers, due to its JavaScript heritage, are IEEE 754 + * double-precision floating point. This means JSON is not capable of + * representing all integers numbers outside the range [-(253)+1, + * 253-1] and is not capable of representing NaN or infinite. If the + * CBOR data contains a number outside the valid range, the conversion will + * lose precision. If the input was NaN or infinite, the result of the + * conversion will be the JSON null value. In addition, the distinction between + * half-, single- and double-precision is lost. + * + * \par + * If enabled, the original value and original type are stored in the metadata. + * + * \par Non-native types: + * CBOR's type system is richer than JSON's, which means some data values + * cannot be represented when converted to JSON. The conversion silently turns + * them into strings: CBOR simple types become "simple(nn)" where \c nn is the + * simple type's value, with the exception of CBOR undefined, which becomes + * "undefined", while CBOR byte strings are converted to an Base16, Base64, or + * Base64url encoding + * + * \par + * If enabled, the original type is stored in the metadata. + * + * \par Presence of tags: + * JSON has no support for tagged values, so by default tags are dropped when + * converting to JSON. However, if the CborConvertObeyByteStringTags option is + * active (default), then certain known tags are honored and are used to format + * the conversion of the tagged byte string to JSON. + * + * \par + * If the CborConvertTagsToObjects option is active, then the tag and the + * tagged value are converted to a JSON object. Otherwise, if enabled, the + * last (innermost) tag is stored in the metadata. + * + * \par Non-string keys in maps: + * JSON requires all Object keys to be strings, while CBOR does not. By + * default, if a non-string key is found, the conversion fails with error + * CborErrorJsonObjectKeyNotString. If the CborConvertStringifyMapKeys option + * is active, then the conversion attempts to create a string representation + * using CborPretty. Note that the \c json2cbor tool is not able to parse this + * back to the original form. + * + * \par Duplicate keys in maps: + * Neither JSON nor CBOR allow duplicated keys, but current TinyCBOR does not + * validate that this is the case. If there are duplicated keys in the input, + * they will be repeated in the output, which many JSON tools may flag as + * invalid. In addition to that, if the CborConvertStringifyMapKeys option is + * active, it is possible that a non-string key in a CBOR map will be converted + * to a string form that is identical to another key. + * + * \par + * When metadata support is active, the conversion will add extra key-value + * pairs to the JSON output so it can store the metadata. It is possible that + * the keys for the metadata clash with existing keys in the JSON map. + */ + +extern FILE *open_memstream(char **bufptr, size_t *sizeptr); + +enum ConversionStatusFlags { + TypeWasNotNative = 0x100, /* anything but strings, boolean, null, arrays and maps */ + TypeWasTagged = 0x200, + NumberPrecisionWasLost = 0x400, + NumberWasNaN = 0x800, + NumberWasInfinite = 0x1000, + NumberWasNegative = 0x2000, /* only used with NumberWasInifite or NumberWasTooBig */ + + FinalTypeMask = 0xff +}; + +typedef struct ConversionStatus { + CborTag lastTag; + uint64_t originalNumber; + int flags; +} ConversionStatus; + +static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status); + +static CborError dump_bytestring_base16(char **result, CborValue *it) +{ + static const char characters[] = "0123456789abcdef"; + size_t i; + size_t n = 0; + uint8_t *buffer; + CborError err = cbor_value_calculate_string_length(it, &n); + if (err) + return err; + + /* a Base16 (hex) output is twice as big as our buffer */ + buffer = (uint8_t *)malloc(n * 2 + 1); + *result = (char *)buffer; + + /* let cbor_value_copy_byte_string know we have an extra byte for the terminating NUL */ + ++n; + err = cbor_value_copy_byte_string(it, buffer + n - 1, &n, it); + cbor_assert(err == CborNoError); + + for (i = 0; i < n; ++i) { + uint8_t byte = buffer[n + i]; + buffer[2*i] = characters[byte >> 4]; + buffer[2*i + 1] = characters[byte & 0xf]; + } + return CborNoError; +} + +static CborError generic_dump_base64(char **result, CborValue *it, const char alphabet[65]) +{ + size_t n = 0, i; + uint8_t *buffer, *out, *in; + CborError err = cbor_value_calculate_string_length(it, &n); + if (err) + return err; + + /* a Base64 output (untruncated) has 4 bytes for every 3 in the input */ + size_t len = (n + 5) / 3 * 4; + out = buffer = (uint8_t *)malloc(len + 1); + *result = (char *)buffer; + + /* we read our byte string at the tail end of the buffer + * so we can do an in-place conversion while iterating forwards */ + in = buffer + len - n; + + /* let cbor_value_copy_byte_string know we have an extra byte for the terminating NUL */ + ++n; + err = cbor_value_copy_byte_string(it, in, &n, it); + cbor_assert(err == CborNoError); + + uint_least32_t val = 0; + for (i = 0; n - i >= 3; i += 3) { + /* read 3 bytes x 8 bits = 24 bits */ + if (false) { +#ifdef __GNUC__ + } else if (i) { + __builtin_memcpy(&val, in + i - 1, sizeof(val)); + val = cbor_ntohl(val); +#endif + } else { + val = (in[i] << 16) | (in[i + 1] << 8) | in[i + 2]; + } + + /* write 4 chars x 6 bits = 24 bits */ + *out++ = alphabet[(val >> 18) & 0x3f]; + *out++ = alphabet[(val >> 12) & 0x3f]; + *out++ = alphabet[(val >> 6) & 0x3f]; + *out++ = alphabet[val & 0x3f]; + } + + /* maybe 1 or 2 bytes left */ + if (n - i) { + /* we can read in[i + 1] even if it's past the end of the string because + * we know (by construction) that it's a NUL byte */ +#ifdef __GNUC__ + uint16_t val16; + __builtin_memcpy(&val16, in + i, sizeof(val16)); + val = cbor_ntohs(val16); +#else + val = (in[i] << 8) | in[i + 1]; +#endif + val <<= 8; + + /* the 65th character in the alphabet is our filler: either '=' or '\0' */ + out[4] = '\0'; + out[3] = alphabet[64]; + if (n - i == 2) { + /* write the third char in 3 chars x 6 bits = 18 bits */ + out[2] = alphabet[(val >> 6) & 0x3f]; + } else { + out[2] = alphabet[64]; /* filler */ + } + out[1] = alphabet[(val >> 12) & 0x3f]; + out[0] = alphabet[(val >> 18) & 0x3f]; + } else { + out[0] = '\0'; + } + + return CborNoError; +} + +static CborError dump_bytestring_base64(char **result, CborValue *it) +{ + static const char alphabet[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef" + "ghijklmn" "opqrstuv" "wxyz0123" "456789+/" "="; + return generic_dump_base64(result, it, alphabet); +} + +static CborError dump_bytestring_base64url(char **result, CborValue *it) +{ + static const char alphabet[] = "ABCDEFGH" "IJKLMNOP" "QRSTUVWX" "YZabcdef" + "ghijklmn" "opqrstuv" "wxyz0123" "456789-_"; + return generic_dump_base64(result, it, alphabet); +} + +static CborError add_value_metadata(FILE *out, CborType type, const ConversionStatus *status) +{ + int flags = status->flags; + if (flags & TypeWasTagged) { + /* extract the tagged type, which may be JSON native */ + type = flags & FinalTypeMask; + flags &= ~(FinalTypeMask | TypeWasTagged); + + if (fprintf(out, "\"tag\":\"%" PRIu64 "\"%s", status->lastTag, + flags & ~TypeWasTagged ? "," : "") < 0) + return CborErrorIO; + } + + if (!flags) + return CborNoError; + + /* print at least the type */ + if (fprintf(out, "\"t\":%d", type) < 0) + return CborErrorIO; + + if (flags & NumberWasNaN) + if (fprintf(out, ",\"v\":\"nan\"") < 0) + return CborErrorIO; + if (flags & NumberWasInfinite) + if (fprintf(out, ",\"v\":\"%sinf\"", flags & NumberWasNegative ? "-" : "") < 0) + return CborErrorIO; + if (flags & NumberPrecisionWasLost) + if (fprintf(out, ",\"v\":\"%c%" PRIx64 "\"", flags & NumberWasNegative ? '-' : '+', + status->originalNumber) < 0) + return CborErrorIO; + if (type == CborSimpleType) + if (fprintf(out, ",\"v\":%d", (int)status->originalNumber) < 0) + return CborErrorIO; + return CborNoError; +} + +static CborError find_tagged_type(CborValue *it, CborTag *tag, CborType *type) +{ + CborError err = CborNoError; + *type = cbor_value_get_type(it); + while (*type == CborTagType) { + cbor_value_get_tag(it, tag); /* can't fail */ + err = cbor_value_advance_fixed(it); + if (err) + return err; + + *type = cbor_value_get_type(it); + } + return err; +} + +static CborError tagged_value_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status) +{ + CborTag tag; + CborError err; + + if (flags & CborConvertTagsToObjects) { + cbor_value_get_tag(it, &tag); /* can't fail */ + err = cbor_value_advance_fixed(it); + if (err) + return err; + + if (fprintf(out, "{\"tag%" PRIu64 "\":", tag) < 0) + return CborErrorIO; + + CborType type = cbor_value_get_type(it); + err = value_to_json(out, it, flags, type, status); + if (err) + return err; + if (flags & CborConvertAddMetadata && status->flags) { + if (fprintf(out, ",\"tag%" PRIu64 "$cbor\":{", tag) < 0 || + add_value_metadata(out, type, status) != CborNoError || + fputc('}', out) < 0) + return CborErrorIO; + } + if (fputc('}', out) < 0) + return CborErrorIO; + status->flags = TypeWasNotNative | CborTagType; + return CborNoError; + } + + CborType type; + err = find_tagged_type(it, &status->lastTag, &type); + if (err) + return err; + tag = status->lastTag; + + /* special handling of byte strings? */ + if (type == CborByteStringType && (flags & CborConvertByteStringsToBase64Url) == 0 && + (tag == CborNegativeBignumTag || tag == CborExpectedBase16Tag || tag == CborExpectedBase64Tag)) { + char *str; + char *pre = ""; + + if (tag == CborNegativeBignumTag) { + pre = "~"; + err = dump_bytestring_base64url(&str, it); + } else if (tag == CborExpectedBase64Tag) { + err = dump_bytestring_base64(&str, it); + } else { /* tag == CborExpectedBase16Tag */ + err = dump_bytestring_base16(&str, it); + } + if (err) + return err; + err = fprintf(out, "\"%s%s\"", pre, str) < 0 ? CborErrorIO : CborNoError; + free(str); + status->flags = TypeWasNotNative | TypeWasTagged | CborByteStringType; + return err; + } + + /* no special handling */ + err = value_to_json(out, it, flags, type, status); + status->flags |= TypeWasTagged | type; + return err; +} + +static CborError stringify_map_key(char **key, CborValue *it, int flags, CborType type) +{ + (void)flags; /* unused */ + (void)type; /* unused */ +#ifdef WITHOUT_OPEN_MEMSTREAM + (void)key; /* unused */ + (void)it; /* unused */ + return CborErrorJsonNotImplemented; +#else + size_t size; + + FILE *memstream = open_memstream(key, &size); + if (memstream == NULL) + return CborErrorOutOfMemory; /* could also be EMFILE, but it's unlikely */ + CborError err = cbor_value_to_pretty_advance(memstream, it); + + if (unlikely(fclose(memstream) < 0 || *key == NULL)) + return CborErrorInternalError; + return err; +#endif +} + +static CborError array_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status) +{ + const char *comma = ""; + while (!cbor_value_at_end(it)) { + if (fprintf(out, "%s", comma) < 0) + return CborErrorIO; + comma = ","; + + CborError err = value_to_json(out, it, flags, cbor_value_get_type(it), status); + if (err) + return err; + } + return CborNoError; +} + +static CborError map_to_json(FILE *out, CborValue *it, int flags, ConversionStatus *status) +{ + const char *comma = ""; + CborError err; + while (!cbor_value_at_end(it)) { + char *key; + if (fprintf(out, "%s", comma) < 0) + return CborErrorIO; + comma = ","; + + CborType keyType = cbor_value_get_type(it); + if (likely(keyType == CborTextStringType)) { + size_t n = 0; + err = cbor_value_dup_text_string(it, &key, &n, it); + } else if (flags & CborConvertStringifyMapKeys) { + err = stringify_map_key(&key, it, flags, keyType); + } else { + return CborErrorJsonObjectKeyNotString; + } + if (err) + return err; + + /* first, print the key */ + if (fprintf(out, "\"%s\":", key) < 0) { + free(key); + return CborErrorIO; + } + + /* then, print the value */ + CborType valueType = cbor_value_get_type(it); + err = value_to_json(out, it, flags, valueType, status); + + /* finally, print any metadata we may have */ + if (flags & CborConvertAddMetadata) { + if (!err && keyType != CborTextStringType) { + if (fprintf(out, ",\"%s$keycbordump\":true", key) < 0) + err = CborErrorIO; + } + if (!err && status->flags) { + if (fprintf(out, ",\"%s$cbor\":{", key) < 0 || + add_value_metadata(out, valueType, status) != CborNoError || + fputc('}', out) < 0) + err = CborErrorIO; + } + } + + free(key); + if (err) + return err; + } + return CborNoError; +} + +static CborError value_to_json(FILE *out, CborValue *it, int flags, CborType type, ConversionStatus *status) +{ + CborError err; + status->flags = 0; + + switch (type) { + case CborArrayType: + case CborMapType: { + /* recursive type */ + CborValue recursed; + err = cbor_value_enter_container(it, &recursed); + if (err) { + it->ptr = recursed.ptr; + return err; /* parse error */ + } + if (fputc(type == CborArrayType ? '[' : '{', out) < 0) + return CborErrorIO; + + err = (type == CborArrayType) ? + array_to_json(out, &recursed, flags, status) : + map_to_json(out, &recursed, flags, status); + if (err) { + it->ptr = recursed.ptr; + return err; /* parse error */ + } + + if (fputc(type == CborArrayType ? ']' : '}', out) < 0) + return CborErrorIO; + err = cbor_value_leave_container(it, &recursed); + if (err) + return err; /* parse error */ + + status->flags = 0; /* reset, there are never conversion errors for us */ + return CborNoError; + } + + case CborIntegerType: { + double num; /* JS numbers are IEEE double precision */ + uint64_t val; + cbor_value_get_raw_integer(it, &val); /* can't fail */ + num = (double)val; + + if (cbor_value_is_negative_integer(it)) { + num = -num - 1; /* convert to negative */ + if ((uint64_t)(-num - 1) != val) { + status->flags = NumberPrecisionWasLost | NumberWasNegative; + status->originalNumber = val; + } + } else { + if ((uint64_t)num != val) { + status->flags = NumberPrecisionWasLost; + status->originalNumber = val; + } + } + if (fprintf(out, "%.0f", num) < 0) /* this number has no fraction, so no decimal points please */ + return CborErrorIO; + break; + } + + case CborByteStringType: + case CborTextStringType: { + char *str; + if (type == CborByteStringType) { + err = dump_bytestring_base64url(&str, it); + status->flags = TypeWasNotNative; + } else { + size_t n = 0; + err = cbor_value_dup_text_string(it, &str, &n, it); + } + if (err) + return err; + err = (fprintf(out, "\"%s\"", str) < 0) ? CborErrorIO : CborNoError; + free(str); + return err; + } + + case CborTagType: + return tagged_value_to_json(out, it, flags, status); + + case CborSimpleType: { + uint8_t simple_type; + cbor_value_get_simple_type(it, &simple_type); /* can't fail */ + status->flags = TypeWasNotNative; + status->originalNumber = simple_type; + if (fprintf(out, "\"simple(%" PRIu8 ")\"", simple_type) < 0) + return CborErrorIO; + break; + } + + case CborNullType: + if (fprintf(out, "null") < 0) + return CborErrorIO; + break; + + case CborUndefinedType: + status->flags = TypeWasNotNative; + if (fprintf(out, "\"undefined\"") < 0) + return CborErrorIO; + break; + + case CborBooleanType: { + bool val; + cbor_value_get_boolean(it, &val); /* can't fail */ + if (fprintf(out, val ? "true" : "false") < 0) + return CborErrorIO; + break; + } + +#ifndef CBOR_NO_FLOATING_POINT + case CborDoubleType: { + double val; + if (false) { + float f; + case CborFloatType: + status->flags = TypeWasNotNative; + cbor_value_get_float(it, &f); + val = f; + } else if (false) { + uint16_t f16; + case CborHalfFloatType: +# ifndef CBOR_NO_HALF_FLOAT_TYPE + status->flags = TypeWasNotNative; + cbor_value_get_half_float(it, &f16); + val = decode_half(f16); +# else + (void)f16; + err = CborErrorUnsupportedType; + break; +# endif + } else { + cbor_value_get_double(it, &val); + } + + int r = fpclassify(val); + if (r == FP_NAN || r == FP_INFINITE) { + if (fprintf(out, "null") < 0) + return CborErrorIO; + status->flags |= r == FP_NAN ? NumberWasNaN : + NumberWasInfinite | (val < 0 ? NumberWasNegative : 0); + } else { + uint64_t ival = (uint64_t)fabs(val); + if ((double)ival == fabs(val)) { + /* print as integer so we get the full precision */ + r = fprintf(out, "%s%" PRIu64, val < 0 ? "-" : "", ival); + status->flags |= TypeWasNotNative; /* mark this integer number as a double */ + } else { + /* this number is definitely not a 64-bit integer */ + r = fprintf(out, "%." DBL_DECIMAL_DIG_STR "g", val); + } + if (r < 0) + return CborErrorIO; + } + break; + } +#else + case CborDoubleType: + case CborFloatType: + case CborHalfFloatType: + err = CborErrorUnsupportedType; + break; +#endif /* !CBOR_NO_FLOATING_POINT */ + + case CborInvalidType: + return CborErrorUnknownType; + } + + return cbor_value_advance_fixed(it); +} + +/** + * \enum CborToJsonFlags + * The CborToJsonFlags enum contains flags that control the conversion of CBOR to JSON. + * + * \value CborConvertAddMetadata Adds metadata to facilitate restoration of the original CBOR data. + * \value CborConvertTagsToObjects Converts CBOR tags to JSON objects + * \value CborConvertIgnoreTags (default) Ignore CBOR tags, except for byte strings + * \value CborConvertObeyByteStringTags (default) Honor formatting of CBOR byte strings if so tagged + * \value CborConvertByteStringsToBase64Url Force the conversion of all CBOR byte strings to Base64url encoding, despite any tags + * \value CborConvertRequireMapStringKeys (default) Require CBOR map keys to be strings, failing the conversion if they are not + * \value CborConvertStringifyMapKeys Convert non-string keys in CBOR maps to a string form + * \value CborConvertDefaultFlags Default conversion flags. + */ + +/** + * \fn CborError cbor_value_to_json(FILE *out, const CborValue *value, int flags) + * + * Converts the current CBOR type pointed to by \a value to JSON and writes that + * to the \a out stream. If an error occurs, this function returns an error + * code similar to CborParsing. The \a flags parameter indicates one or more of + * the flags from CborToJsonFlags that control the conversion. + * + * \sa cbor_value_to_json_advance(), cbor_value_to_pretty() + */ + +/** + * Converts the current CBOR type pointed to by \a value to JSON and writes that + * to the \a out stream. If an error occurs, this function returns an error + * code similar to CborParsing. The \a flags parameter indicates one or more of + * the flags from CborToJsonFlags that control the conversion. + * + * If no error ocurred, this function advances \a value to the next element. + * + * \sa cbor_value_to_json(), cbor_value_to_pretty_advance() + */ +CborError cbor_value_to_json_advance(FILE *out, CborValue *value, int flags) +{ + ConversionStatus status; + return value_to_json(out, value, flags, cbor_value_get_type(value), &status); +} + +/** @} */ diff --git a/lib/tinycbor/cborvalidation.c b/lib/tinycbor/cborvalidation.c new file mode 100644 index 000000000..08c35117b --- /dev/null +++ b/lib/tinycbor/cborvalidation.c @@ -0,0 +1,666 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#define _BSD_SOURCE 1 +#define _DEFAULT_SOURCE 1 +#ifndef __STDC_LIMIT_MACROS +# define __STDC_LIMIT_MACROS 1 +#endif + +#include "cbor.h" +#include "cborinternal_p.h" +#include "compilersupport_p.h" +#include "utf8_p.h" + +#include + +#ifndef CBOR_NO_FLOATING_POINT +# include +# include +#endif + + +#ifndef CBOR_PARSER_MAX_RECURSIONS +# define CBOR_PARSER_MAX_RECURSIONS 1024 +#endif + +/** + * \addtogroup CborParsing + * @{ + */ + +/** + * \enum CborValidationFlags + * The CborValidationFlags enum contains flags that control the validation of a + * CBOR stream. + * + * \value CborValidateBasic Validates only the syntactic correctedness of the stream. + * \value CborValidateCanonical Validates that the stream is in canonical format, according to + * RFC 7049 section 3.9. + * \value CborValidateStrictMode Performs strict validation, according to RFC 7049 section 3.10. + * \value CborValidateStrictest Attempt to perform the strictest validation we know of. + * + * \value CborValidateShortestIntegrals (Canonical) Validate that integral numbers and lengths are + * enconded in their shortest form possible. + * \value CborValidateShortestFloatingPoint (Canonical) Validate that floating-point numbers are encoded + * in their shortest form possible. + * \value CborValidateShortestNumbers (Canonical) Validate both integral and floating-point numbers + * are in their shortest form possible. + * \value CborValidateNoIndeterminateLength (Canonical) Validate that no string, array or map uses + * indeterminate length encoding. + * \value CborValidateMapIsSorted (Canonical & Strict mode) Validate that map keys appear in + * sorted order. + * \value CborValidateMapKeysAreUnique (Strict mode) Validate that map keys are unique. + * \value CborValidateTagUse (Strict mode) Validate that known tags are used with the + * correct types. This does not validate that the content of + * those types is syntactically correct. For example, this + * option validates that tag 1 (DateTimeString) is used with + * a Text String, but it does not validate that the string is + * a valid date/time representation. + * \value CborValidateUtf8 (Strict mode) Validate that text strings are appropriately + * encoded in UTF-8. + * \value CborValidateMapKeysAreString Validate that all map keys are text strings. + * \value CborValidateNoUndefined Validate that no elements of type "undefined" are present. + * \value CborValidateNoTags Validate that no tags are used. + * \value CborValidateFiniteFloatingPoint Validate that all floating point numbers are finite (no NaN or + * infinities are allowed). + * \value CborValidateCompleteData Validate that the stream is complete and there is no more data + * in the buffer. + * \value CborValidateNoUnknownSimpleTypesSA Validate that all Standards Action simple types are registered + * with IANA. + * \value CborValidateNoUnknownSimpleTypes Validate that all simple types used are registered with IANA. + * \value CborValidateNoUnknownTagsSA Validate that all Standard Actions tags are registered with IANA. + * \value CborValidateNoUnknownTagsSR Validate that all Standard Actions and Specification Required tags + * are registered with IANA (see below for limitations). + * \value CborValidateNoUnkonwnTags Validate that all tags are registered with IANA + * (see below for limitations). + * + * \par Simple type registry + * The CBOR specification requires that registration for use of the first 19 + * simple types must be done by way of Standards Action. The rest of the simple + * types only require a specification. The official list can be obtained from + * https://www.iana.org/assignments/cbor-simple-values/cbor-simple-values.xhtml. + * + * \par + * There are no registered simple types recognized by this release of TinyCBOR + * (beyond those defined by RFC 7049). + * + * \par Tag registry + * The CBOR specification requires that registration for use of the first 23 + * tags must be done by way of Standards Action. The next up to tag 255 only + * require a specification. Finally, all other tags can be registered on a + * first-come-first-serve basis. The official list can be ontained from + * https://www.iana.org/assignments/cbor-tags/cbor-tags.xhtml. + * + * \par + * Given the variability of this list, TinyCBOR cannot recognize all tags + * registered with IANA. Instead, the implementation only recognizes tags + * that are backed by an RFC. + * + * \par + * These are the tags known to the current TinyCBOR release: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TagData ItemSemantics
0UTF-8 text stringStandard date/time string
1integerEpoch-based date/time
2byte stringPositive bignum
3byte stringNegative bignum
4arrayDecimal fraction
5arrayBigfloat
16arrayCOSE Single Recipient Encrypted Data Object (RFC 8152)
17arrayCOSE Mac w/o Recipients Object (RFC 8152)
18arrayCOSE Single Signer Data Object (RFC 8162)
21byte string, array, mapExpected conversion to base64url encoding
22byte string, array, mapExpected conversion to base64 encoding
23byte string, array, mapExpected conversion to base16 encoding
24byte stringEncoded CBOR data item
32UTF-8 text stringURI
33UTF-8 text stringbase64url
34UTF-8 text stringbase64
35UTF-8 text stringRegular expression
36UTF-8 text stringMIME message
96arrayCOSE Encrypted Data Object (RFC 8152)
97arrayCOSE MACed Data Object (RFC 8152)
98arrayCOSE Signed Data Object (RFC 8152)
55799anySelf-describe CBOR
+ */ + +struct KnownTagData { uint32_t tag; uint32_t types; }; +static const struct KnownTagData knownTagData[] = { + { 0, (uint32_t)CborTextStringType }, + { 1, (uint32_t)(CborIntegerType+1) }, + { 2, (uint32_t)CborByteStringType }, + { 3, (uint32_t)CborByteStringType }, + { 4, (uint32_t)CborArrayType }, + { 5, (uint32_t)CborArrayType }, + { 16, (uint32_t)CborArrayType }, + { 17, (uint32_t)CborArrayType }, + { 18, (uint32_t)CborArrayType }, + { 21, (uint32_t)CborByteStringType | ((uint32_t)CborArrayType << 8) | ((uint32_t)CborMapType << 16) }, + { 22, (uint32_t)CborByteStringType | ((uint32_t)CborArrayType << 8) | ((uint32_t)CborMapType << 16) }, + { 23, (uint32_t)CborByteStringType | ((uint32_t)CborArrayType << 8) | ((uint32_t)CborMapType << 16) }, + { 24, (uint32_t)CborByteStringType }, + { 32, (uint32_t)CborTextStringType }, + { 33, (uint32_t)CborTextStringType }, + { 34, (uint32_t)CborTextStringType }, + { 35, (uint32_t)CborTextStringType }, + { 36, (uint32_t)CborTextStringType }, + { 96, (uint32_t)CborArrayType }, + { 97, (uint32_t)CborArrayType }, + { 98, (uint32_t)CborArrayType }, + { 55799, 0U } +}; + +static CborError validate_value(CborValue *it, uint32_t flags, int recursionLeft); + +static inline CborError validate_utf8_string(const void *ptr, size_t n) +{ + const uint8_t *buffer = (const uint8_t *)ptr; + const uint8_t * const end = buffer + n; + while (buffer < end) { + uint32_t uc = get_utf8(&buffer, end); + if (uc == ~0U) + return CborErrorInvalidUtf8TextString; + } + return CborNoError; +} + +static inline CborError validate_simple_type(uint8_t simple_type, uint32_t flags) +{ + /* At current time, all known simple types are those from RFC 7049, + * which are parsed by the parser into different CBOR types. + * That means that if we've got here, the type is unknown */ + if (simple_type < 32) + return (flags & CborValidateNoUnknownSimpleTypesSA) ? CborErrorUnknownSimpleType : CborNoError; + return (flags & CborValidateNoUnknownSimpleTypes) == CborValidateNoUnknownSimpleTypes ? + CborErrorUnknownSimpleType : CborNoError; +} + +static inline CborError validate_number(const CborValue *it, CborType type, uint32_t flags) +{ + CborError err = CborNoError; + const uint8_t *ptr = it->ptr; + size_t bytesUsed, bytesNeeded; + uint64_t value; + + if ((flags & CborValidateShortestIntegrals) == 0) + return err; + if (type >= CborHalfFloatType && type <= CborDoubleType) + return err; /* checked elsewhere */ + + err = _cbor_value_extract_number(&ptr, it->parser->end, &value); + if (err) + return err; + + bytesUsed = (size_t)(ptr - it->ptr - 1); + bytesNeeded = 0; + if (value >= Value8Bit) + ++bytesNeeded; + if (value > 0xffU) + ++bytesNeeded; + if (value > 0xffffU) + bytesNeeded += 2; + if (value > 0xffffffffU) + bytesNeeded += 4; + if (bytesNeeded < bytesUsed) + return CborErrorOverlongEncoding; + return CborNoError; +} + +static inline CborError validate_tag(CborValue *it, CborTag tag, uint32_t flags, int recursionLeft) +{ + CborType type = cbor_value_get_type(it); + const size_t knownTagCount = sizeof(knownTagData) / sizeof(knownTagData[0]); + const struct KnownTagData *tagData = knownTagData; + const struct KnownTagData * const knownTagDataEnd = knownTagData + knownTagCount; + + if (!recursionLeft) + return CborErrorNestingTooDeep; + if (flags & CborValidateNoTags) + return CborErrorExcludedType; + + /* find the tag data, if any */ + for ( ; tagData != knownTagDataEnd; ++tagData) { + if (tagData->tag < tag) + continue; + if (tagData->tag > tag) + tagData = NULL; + break; + } + if (tagData == knownTagDataEnd) + tagData = NULL; + + if (flags & CborValidateNoUnknownTags && !tagData) { + /* tag not found */ + if (flags & CborValidateNoUnknownTagsSA && tag < 24) + return CborErrorUnknownTag; + if ((flags & CborValidateNoUnknownTagsSR) == CborValidateNoUnknownTagsSR && tag < 256) + return CborErrorUnknownTag; + if ((flags & CborValidateNoUnknownTags) == CborValidateNoUnknownTags) + return CborErrorUnknownTag; + } + + if (flags & CborValidateTagUse && tagData && tagData->types) { + uint32_t allowedTypes = tagData->types; + + /* correct Integer so it's not zero */ + if (type == CborIntegerType) + type = (CborType)(type + 1); + + while (allowedTypes) { + if ((uint8_t)(allowedTypes & 0xff) == type) + break; + allowedTypes >>= 8; + } + if (!allowedTypes) + return CborErrorInappropriateTagForType; + } + + return validate_value(it, flags, recursionLeft); +} + +#ifndef CBOR_NO_FLOATING_POINT +static inline CborError validate_floating_point(CborValue *it, CborType type, uint32_t flags) +{ + CborError err; + int r; + double val; + float valf; + uint16_t valf16; + + if (type != CborDoubleType) { + if (type == CborFloatType) { + err = cbor_value_get_float(it, &valf); + val = valf; + } else { +# ifdef CBOR_NO_HALF_FLOAT_TYPE + (void)valf16; + return CborErrorUnsupportedType; +# else + err = cbor_value_get_half_float(it, &valf16); + val = decode_half(valf16); +# endif + } + } else { + err = cbor_value_get_double(it, &val); + } + cbor_assert(err == CborNoError); /* can't fail */ + + r = fpclassify(val); + if (r == FP_NAN || r == FP_INFINITE) { + if (flags & CborValidateFiniteFloatingPoint) + return CborErrorExcludedValue; + if (flags & CborValidateShortestFloatingPoint) { + if (type == CborDoubleType) + return CborErrorOverlongEncoding; +# ifndef CBOR_NO_HALF_FLOAT_TYPE + if (type == CborFloatType) + return CborErrorOverlongEncoding; + if (r == FP_NAN && valf16 != 0x7e00) + return CborErrorImproperValue; + if (r == FP_INFINITE && valf16 != 0x7c00 && valf16 != 0xfc00) + return CborErrorImproperValue; +# endif + } + } + + if (flags & CborValidateShortestFloatingPoint && type > CborHalfFloatType) { + if (type == CborDoubleType) { + valf = (float)val; + if ((double)valf == val) + return CborErrorOverlongEncoding; + } +# ifndef CBOR_NO_HALF_FLOAT_TYPE + if (type == CborFloatType) { + valf16 = encode_half(valf); + if (valf == decode_half(valf16)) + return CborErrorOverlongEncoding; + } +# endif + } + + return CborNoError; +} +#endif + +static CborError validate_container(CborValue *it, int containerType, uint32_t flags, int recursionLeft) +{ + CborError err; + const uint8_t *previous = NULL; + const uint8_t *previous_end = NULL; + + if (!recursionLeft) + return CborErrorNestingTooDeep; + + while (!cbor_value_at_end(it)) { + const uint8_t *current = cbor_value_get_next_byte(it); + + if (containerType == CborMapType) { + if (flags & CborValidateMapKeysAreString) { + CborType type = cbor_value_get_type(it); + if (type == CborTagType) { + /* skip the tags */ + CborValue copy = *it; + err = cbor_value_skip_tag(©); + if (err) + return err; + type = cbor_value_get_type(©); + } + if (type != CborTextStringType) + return CborErrorMapKeyNotString; + } + } + + err = validate_value(it, flags, recursionLeft); + if (err) + return err; + + if (containerType != CborMapType) + continue; + + if (flags & CborValidateMapIsSorted) { + if (previous) { + uint64_t len1, len2; + const uint8_t *ptr; + + /* extract the two lengths */ + ptr = previous; + _cbor_value_extract_number(&ptr, it->parser->end, &len1); + ptr = current; + _cbor_value_extract_number(&ptr, it->parser->end, &len2); + + if (len1 > len2) + return CborErrorMapNotSorted; + if (len1 == len2) { + size_t bytelen1 = (size_t)(previous_end - previous); + size_t bytelen2 = (size_t)(it->ptr - current); + int r = memcmp(previous, current, bytelen1 <= bytelen2 ? bytelen1 : bytelen2); + + if (r == 0 && bytelen1 != bytelen2) + r = bytelen1 < bytelen2 ? -1 : +1; + if (r > 0) + return CborErrorMapNotSorted; + if (r == 0 && (flags & CborValidateMapKeysAreUnique) == CborValidateMapKeysAreUnique) + return CborErrorMapKeysNotUnique; + } + } + + previous = current; + previous_end = it->ptr; + } + + /* map: that was the key, so get the value */ + err = validate_value(it, flags, recursionLeft); + if (err) + return err; + } + return CborNoError; +} + +static CborError validate_value(CborValue *it, uint32_t flags, int recursionLeft) +{ + CborError err; + CborType type = cbor_value_get_type(it); + + if (cbor_value_is_length_known(it)) { + err = validate_number(it, type, flags); + if (err) + return err; + } else { + if (flags & CborValidateNoIndeterminateLength) + return CborErrorUnknownLength; + } + + switch (type) { + case CborArrayType: + case CborMapType: { + /* recursive type */ + CborValue recursed; + err = cbor_value_enter_container(it, &recursed); + if (!err) + err = validate_container(&recursed, type, flags, recursionLeft - 1); + if (err) { + it->ptr = recursed.ptr; + return err; + } + err = cbor_value_leave_container(it, &recursed); + if (err) + return err; + return CborNoError; + } + + case CborIntegerType: { + uint64_t val; + err = cbor_value_get_raw_integer(it, &val); + cbor_assert(err == CborNoError); /* can't fail */ + + break; + } + + case CborByteStringType: + case CborTextStringType: { + size_t n = 0; + const void *ptr; + + err = _cbor_value_prepare_string_iteration(it); + if (err) + return err; + + while (1) { + err = validate_number(it, type, flags); + if (err) + return err; + + err = _cbor_value_get_string_chunk(it, &ptr, &n, it); + if (err) + return err; + if (!ptr) + break; + + if (type == CborTextStringType && flags & CborValidateUtf8) { + err = validate_utf8_string(ptr, n); + if (err) + return err; + } + } + + return CborNoError; + } + + case CborTagType: { + CborTag tag; + err = cbor_value_get_tag(it, &tag); + cbor_assert(err == CborNoError); /* can't fail */ + + err = cbor_value_advance_fixed(it); + if (err) + return err; + err = validate_tag(it, tag, flags, recursionLeft - 1); + if (err) + return err; + + return CborNoError; + } + + case CborSimpleType: { + uint8_t simple_type; + err = cbor_value_get_simple_type(it, &simple_type); + cbor_assert(err == CborNoError); /* can't fail */ + err = validate_simple_type(simple_type, flags); + if (err) + return err; + break; + } + + case CborNullType: + case CborBooleanType: + break; + + case CborUndefinedType: + if (flags & CborValidateNoUndefined) + return CborErrorExcludedType; + break; + + case CborHalfFloatType: + case CborFloatType: + case CborDoubleType: { +#ifdef CBOR_NO_FLOATING_POINT + return CborErrorUnsupportedType; +#else + err = validate_floating_point(it, type, flags); + if (err) + return err; + break; +#endif /* !CBOR_NO_FLOATING_POINT */ + } + + case CborInvalidType: + return CborErrorUnknownType; + } + + err = cbor_value_advance_fixed(it); + return err; +} + +/** + * Performs a full validation, controlled by the \a flags options, of the CBOR + * stream pointed by \a it and returns the error it found. If no error was + * found, it returns CborNoError and the application can iterate over the items + * with certainty that no errors will appear during parsing. + * + * If \a flags is CborValidateBasic, the result should be the same as + * cbor_value_validate_basic(). + * + * This function has the same timing and memory requirements as + * cbor_value_advance() and cbor_value_validate_basic(). + * + * \sa CborValidationFlags, cbor_value_validate_basic(), cbor_value_advance() + */ +CborError cbor_value_validate(const CborValue *it, uint32_t flags) +{ + CborValue value = *it; + CborError err = validate_value(&value, flags, CBOR_PARSER_MAX_RECURSIONS); + if (err) + return err; + if (flags & CborValidateCompleteData && it->ptr != it->parser->end) + return CborErrorGarbageAtEnd; + return CborNoError; +} + +/** + * @} + */ diff --git a/lib/tinycbor/compilersupport_p.h b/lib/tinycbor/compilersupport_p.h new file mode 100644 index 000000000..2b9491d34 --- /dev/null +++ b/lib/tinycbor/compilersupport_p.h @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#ifndef COMPILERSUPPORT_H +#define COMPILERSUPPORT_H + +#include "cbor.h" + +#ifndef _BSD_SOURCE +# define _BSD_SOURCE +#endif +#ifndef _DEFAULT_SOURCE +# define _DEFAULT_SOURCE +#endif +#ifndef assert +# include +#endif +#include +#include +#include + +#ifndef __cplusplus +# include +#endif + +#if __STDC_VERSION__ >= 201112L || __cplusplus >= 201103L || __cpp_static_assert >= 200410 +# define cbor_static_assert(x) static_assert(x, #x) +#elif !defined(__cplusplus) && defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 406) && (__STDC_VERSION__ > 199901L) +# define cbor_static_assert(x) _Static_assert(x, #x) +#else +# define cbor_static_assert(x) ((void)sizeof(char[2*!!(x) - 1])) +#endif +#if __STDC_VERSION__ >= 199901L || defined(__cplusplus) +/* inline is a keyword */ +#else +/* use the definition from cbor.h */ +# define inline CBOR_INLINE +#endif + +#ifdef NDEBUG +# define cbor_assert(cond) do { if (!(cond)) unreachable(); } while (0) +#else +# define cbor_assert(cond) assert(cond) +#endif + +#ifndef STRINGIFY +#define STRINGIFY(x) STRINGIFY2(x) +#endif +#define STRINGIFY2(x) #x + +#if !defined(UINT32_MAX) || !defined(INT64_MAX) +/* C89? We can define UINT32_MAX portably, but not INT64_MAX */ +# error "Your system has stdint.h but that doesn't define UINT32_MAX or INT64_MAX" +#endif + +#ifndef DBL_DECIMAL_DIG +/* DBL_DECIMAL_DIG is C11 */ +# define DBL_DECIMAL_DIG 17 +#endif +#define DBL_DECIMAL_DIG_STR STRINGIFY(DBL_DECIMAL_DIG) + +#if defined(__GNUC__) && defined(__i386__) && !defined(__iamcu__) +# define CBOR_INTERNAL_API_CC __attribute__((regparm(3))) +#elif defined(_MSC_VER) && defined(_M_IX86) +# define CBOR_INTERNAL_API_CC __fastcall +#else +# define CBOR_INTERNAL_API_CC +#endif + +#ifndef __has_builtin +# define __has_builtin(x) 0 +#endif + +#if (defined(__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 403)) || \ + (__has_builtin(__builtin_bswap64) && __has_builtin(__builtin_bswap32)) +# if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +# define cbor_ntohll __builtin_bswap64 +# define cbor_htonll __builtin_bswap64 +# define cbor_ntohl __builtin_bswap32 +# define cbor_htonl __builtin_bswap32 +# ifdef __INTEL_COMPILER +# define cbor_ntohs _bswap16 +# define cbor_htons _bswap16 +# elif (__GNUC__ * 100 + __GNUC_MINOR__ >= 608) || __has_builtin(__builtin_bswap16) +# define cbor_ntohs __builtin_bswap16 +# define cbor_htons __builtin_bswap16 +# else +# define cbor_ntohs(x) (((uint16_t)x >> 8) | ((uint16_t)x << 8)) +# define cbor_htons cbor_ntohs +# endif +# else +# define cbor_ntohll +# define cbor_htonll +# define cbor_ntohl +# define cbor_htonl +# define cbor_ntohs +# define cbor_htons +# endif +#elif defined(__sun) +# include +#elif defined(_MSC_VER) +/* MSVC, which implies Windows, which implies little-endian and sizeof(long) == 4 */ +# include +# define cbor_ntohll _byteswap_uint64 +# define cbor_htonll _byteswap_uint64 +# define cbor_ntohl _byteswap_ulong +# define cbor_htonl _byteswap_ulong +# define cbor_ntohs _byteswap_ushort +# define cbor_htons _byteswap_ushort +#endif +#ifndef cbor_ntohs +# include +# define cbor_ntohs ntohs +# define cbor_htons htons +#endif +#ifndef cbor_ntohl +# include +# define cbor_ntohl ntohl +# define cbor_htonl htonl +#endif +#ifndef cbor_ntohll +# define cbor_ntohll ntohll +# define cbor_htonll htonll +/* ntohll isn't usually defined */ +# ifndef ntohll +# if (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ + (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN) || \ + (defined(BYTE_ORDER) && defined(BIG_ENDIAN) && BYTE_ORDER == BIG_ENDIAN) || \ + (defined(_BIG_ENDIAN) && !defined(_LITTLE_ENDIAN)) || (defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)) || \ + defined(__ARMEB__) || defined(__MIPSEB__) || defined(__s390__) || defined(__sparc__) +# define ntohll +# define htonll +# elif (defined(__BYTE_ORDER__) && defined(__ORDER_LITTLE_ENDIAN__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ + (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN) || \ + (defined(BYTE_ORDER) && defined(LITTLE_ENDIAN) && BYTE_ORDER == LITTLE_ENDIAN) || \ + defined(_LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__MIPSEL__) || \ + defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__) || defined(__amd64) +# define ntohll(x) ((ntohl((uint32_t)(x)) * UINT64_C(0x100000000)) + (ntohl((x) >> 32))) +# define htonll ntohll +# else +# error "Unable to determine byte order!" +# endif +# endif +#endif + + +#ifdef __cplusplus +# define CONST_CAST(t, v) const_cast(v) +#else +/* C-style const_cast without triggering a warning with -Wcast-qual */ +# define CONST_CAST(t, v) (t)(uintptr_t)(v) +#endif + +#ifdef __GNUC__ +#ifndef likely +# define likely(x) __builtin_expect(!!(x), 1) +#endif +#ifndef unlikely +# define unlikely(x) __builtin_expect(!!(x), 0) +#endif +# define unreachable() __builtin_unreachable() +#elif defined(_MSC_VER) +# define likely(x) (x) +# define unlikely(x) (x) +# define unreachable() __assume(0) +#else +# define likely(x) (x) +# define unlikely(x) (x) +# define unreachable() do {} while (0) +#endif + +static inline bool add_check_overflow(size_t v1, size_t v2, size_t *r) +{ +#if ((defined(__GNUC__) && (__GNUC__ >= 5)) && !defined(__INTEL_COMPILER)) || __has_builtin(__builtin_add_overflow) + return __builtin_add_overflow(v1, v2, r); +#else + /* unsigned additions are well-defined */ + *r = v1 + v2; + return v1 > v1 + v2; +#endif +} + +#endif /* COMPILERSUPPORT_H */ + diff --git a/lib/tinycbor/open_memstream.c b/lib/tinycbor/open_memstream.c new file mode 100644 index 000000000..0c4f96314 --- /dev/null +++ b/lib/tinycbor/open_memstream.c @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2015 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#define _BSD_SOURCE 1 +#define _DEFAULT_SOURCE 1 +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include + +#include + +typedef ssize_t RetType; +typedef size_t LenType; + +#include "compilersupport_p.h" + +struct Buffer +{ + char **ptr; + size_t *len; + size_t alloc; +}; + +static RetType write_to_buffer(void *cookie, const char *data, LenType len) +{ + struct Buffer *b = (struct Buffer *)cookie; + char *ptr = *b->ptr; + size_t newsize; + + errno = EFBIG; + if (unlikely(add_check_overflow(*b->len, len, &newsize))) + return -1; + + if (newsize > b->alloc) { + // make room + size_t newalloc = newsize + newsize / 2 + 1; // give 50% more room + ptr = realloc(ptr, newalloc); + if (ptr == NULL) + return -1; + b->alloc = newalloc; + *b->ptr = ptr; + } + + memcpy(ptr + *b->len, data, len); + *b->len = newsize; + return len; +} + +static int close_buffer(void *cookie) +{ + struct Buffer *b = (struct Buffer *)cookie; + if (*b->ptr) + (*b->ptr)[*b->len] = '\0'; + free(b); + return 0; +} + +FILE *open_memstream(char **bufptr, size_t *lenptr) +{ + struct Buffer *b = (struct Buffer *)malloc(sizeof(struct Buffer)); + if (b == NULL) + return NULL; + b->alloc = 0; + b->len = lenptr; + b->ptr = bufptr; + *bufptr = NULL; + *lenptr = 0; + +#ifdef __APPLE__ + return funopen(b, NULL, write_to_buffer, NULL, close_buffer); +#elif __GLIBC__ + static const cookie_io_functions_t vtable = { + NULL, + write_to_buffer, + NULL, + close_buffer + }; + return fopencookie(b, "w", vtable); +#endif +} + diff --git a/lib/tinycbor/parsetags.pl b/lib/tinycbor/parsetags.pl new file mode 100755 index 000000000..fbb182993 --- /dev/null +++ b/lib/tinycbor/parsetags.pl @@ -0,0 +1,116 @@ +#!/usr/bin/perl -l +## Copyright (C) 2017 Intel Corporation +## +## Permission is hereby granted, free of charge, to any person obtaining a copy +## of this software and associated documentation files (the "Software"), to deal +## in the Software without restriction, including without limitation the rights +## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +## copies of the Software, and to permit persons to whom the Software is +## furnished to do so, subject to the following conditions: +## +## The above copyright notice and this permission notice shall be included in +## all copies or substantial portions of the Software. +## +## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +## THE SOFTWARE. +## +use strict; +my $fname = shift @ARGV + or die("Usage: parsetags.pl tags.txt"); +open TAGS, "<", $fname + or die("Cannot open $fname: $!"); + +my %typedescriptions = ( + "Integer" => "integer", + "ByteString" => "byte string", + "TextString" => "UTF-8 text string", + "Array" => "array", + "Map" => "map", + "Tag" => "tag", # shouldn't happen + "Simple" => "any simple type", + "Boolean" => "boolean", + "Null" => "null", + "Undefined" => "undefined", + "HalfFloat" => "IEEE 754 half-precision floating point", + "Float" => "IEEE 754 single-precision floating point", + "Double" => "IEEE 754 double-precision floating point" +); + +my %tags; +while () { + s/\s*#.*$//; + next if /^$/; + chomp; + + die("Could not parse line \"$_\"") + unless /^(\d+);(\w+);([\w,]*);(.*)$/; + $tags{$1}{id} = $2; + $tags{$1}{semantic} = $4; + my @types = split(',', $3); + $tags{$1}{types} = \@types; +} +close TAGS or die; + +my @tagnumbers = sort { $a <=> $b } keys %tags; + +print "==== HTML listing ===="; +print "\n \n \n \n \n "; +for my $n (@tagnumbers) { + print " "; + print " "; + + my @types = @{$tags{$n}{types}}; + @types = map { $typedescriptions{$_}; } @types; + unshift @types, "any" + if (scalar @types == 0); + printf " \n", join(', ', @types); + printf " \n", $tags{$n}{semantic}; + print " "; +} +print "
TagData ItemSemantics
$n%s%s
"; + +print "\n==== enum listing for cbor.h ====\n"; +printf "typedef enum CborKnownTags {"; +my $comma = ""; +for my $n (@tagnumbers) { + printf "%s\n Cbor%sTag%s = %d", $comma, + $tags{$n}{id}, + ' ' x (23 - length($tags{$n}{id})), + $n; + $comma = ","; +} +print "\n} CborKnownTags;"; +print "\n/* #define the constants so we can check with #ifdef */"; +for my $n (@tagnumbers) { + printf "#define Cbor%sTag Cbor%sTag\n", $tags{$n}{id}, $tags{$n}{id}; +} + +print "\n==== search table ====\n"; +print "struct KnownTagData { uint32_t tag; uint32_t types; };"; +printf "static const struct KnownTagData knownTagData[] = {"; +$comma = ""; +for my $n (@tagnumbers) { + my @types = @{$tags{$n}{types}}; + + my $typemask; + my $shift = 0; + for my $type (@types) { + die("Too many match types for tag $n") if $shift == 32; + my $actualtype = "Cbor${type}Type"; + $actualtype = "($actualtype+1)" if $type eq "Integer"; + $typemask .= " | " if $typemask ne ""; + $typemask .= "((uint32_t)$actualtype << $shift)" if $shift; + $typemask .= "(uint32_t)$actualtype" unless $shift; + $shift += 8; + } + $typemask = "0U" if $typemask eq ""; + + printf "%s\n { %d, %s }", $comma, $n, $typemask; + $comma = ","; +} +print "\n};"; diff --git a/lib/tinycbor/src.pri b/lib/tinycbor/src.pri new file mode 100644 index 000000000..01887aa49 --- /dev/null +++ b/lib/tinycbor/src.pri @@ -0,0 +1,16 @@ +SOURCES += \ + $$PWD/cborencoder.c \ + $$PWD/cborencoder_close_container_checked.c \ + $$PWD/cborerrorstrings.c \ + $$PWD/cborparser.c \ + $$PWD/cborparser_dup_string.c \ + $$PWD/cborpretty.c \ + $$PWD/cbortojson.c \ + $$PWD/cborvalidation.c \ + +HEADERS += $$PWD/cbor.h $$PWD/tinycbor-version.h + +QMAKE_CFLAGS *= $$QMAKE_CFLAGS_SPLIT_SECTIONS +QMAKE_LFLAGS *= $$QMAKE_LFLAGS_GCSECTIONS +INCLUDEPATH += $$PWD +CONFIG(release, debug|release): DEFINES += NDEBUG diff --git a/lib/tinycbor/tags.txt b/lib/tinycbor/tags.txt new file mode 100644 index 000000000..ef78cfb5a --- /dev/null +++ b/lib/tinycbor/tags.txt @@ -0,0 +1,23 @@ +# Tag number; Tag ID; Applicable types (comma-separated); Semantics +0;DateTimeString;TextString;Standard date/time string +1;UnixTime_t;Integer;Epoch-based date/time +2;PositiveBignum;ByteString;Positive bignum +3;NegativeBignum;ByteString;Negative bignum +4;Decimal;Array;Decimal fraction +5;Bigfloat;Array;Bigfloat +16;COSE_Encrypt0;Array;COSE Single Recipient Encrypted Data Object (RFC 8152) +17;COSE_Mac0;Array;COSE Mac w/o Recipients Object (RFC 8152) +18;COSE_Sign1;Array;COSE Single Signer Data Object (RFC 8162) +21;ExpectedBase64url;ByteString,Array,Map;Expected conversion to base64url encoding +22;ExpectedBase64;ByteString,Array,Map;Expected conversion to base64 encoding +23;ExpectedBase16;ByteString,Array,Map;Expected conversion to base16 encoding +24;EncodedCbor;ByteString;Encoded CBOR data item +32;Url;TextString;URI +33;Base64url;TextString;base64url +34;Base64;TextString;base64 +35;RegularExpression;TextString;Regular expression +36;MimeMessage;TextString;MIME message +96;COSE_Encrypt;Array;COSE Encrypted Data Object (RFC 8152) +97;COSE_Mac;Array;COSE MACed Data Object (RFC 8152) +98;COSE_Sign;Array;COSE Signed Data Object (RFC 8152) +55799;Signature;;Self-describe CBOR diff --git a/lib/tinycbor/tinycbor-version.h b/lib/tinycbor/tinycbor-version.h new file mode 100644 index 000000000..c57d7fa24 --- /dev/null +++ b/lib/tinycbor/tinycbor-version.h @@ -0,0 +1,3 @@ +#define TINYCBOR_VERSION_MAJOR 0 +#define TINYCBOR_VERSION_MINOR 5 +#define TINYCBOR_VERSION_PATCH 2 diff --git a/lib/tinycbor/tinycbor.pro b/lib/tinycbor/tinycbor.pro new file mode 100644 index 000000000..980dfd1f0 --- /dev/null +++ b/lib/tinycbor/tinycbor.pro @@ -0,0 +1,6 @@ +TEMPLATE = lib +CONFIG += static +CONFIG -= qt +DESTDIR = ../lib + +include(src.pri) diff --git a/lib/tinycbor/utf8_p.h b/lib/tinycbor/utf8_p.h new file mode 100644 index 000000000..577e54043 --- /dev/null +++ b/lib/tinycbor/utf8_p.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2017 Intel Corporation +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in +** all copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +** THE SOFTWARE. +** +****************************************************************************/ + +#ifndef CBOR_UTF8_H +#define CBOR_UTF8_H + +#include "compilersupport_p.h" + +#include + +static inline uint32_t get_utf8(const uint8_t **buffer, const uint8_t *end) +{ + int charsNeeded; + uint32_t uc, min_uc; + uint8_t b; + ptrdiff_t n = end - *buffer; + if (n == 0) + return ~0U; + + uc = *(*buffer)++; + if (uc < 0x80) { + /* single-byte UTF-8 */ + return uc; + } + + /* multi-byte UTF-8, decode it */ + if (unlikely(uc <= 0xC1)) + return ~0U; + if (uc < 0xE0) { + /* two-byte UTF-8 */ + charsNeeded = 2; + min_uc = 0x80; + uc &= 0x1f; + } else if (uc < 0xF0) { + /* three-byte UTF-8 */ + charsNeeded = 3; + min_uc = 0x800; + uc &= 0x0f; + } else if (uc < 0xF5) { + /* four-byte UTF-8 */ + charsNeeded = 4; + min_uc = 0x10000; + uc &= 0x07; + } else { + return ~0U; + } + + if (n < charsNeeded - 1) + return ~0U; + + /* first continuation character */ + b = *(*buffer)++; + if ((b & 0xc0) != 0x80) + return ~0U; + uc <<= 6; + uc |= b & 0x3f; + + if (charsNeeded > 2) { + /* second continuation character */ + b = *(*buffer)++; + if ((b & 0xc0) != 0x80) + return ~0U; + uc <<= 6; + uc |= b & 0x3f; + + if (charsNeeded > 3) { + /* third continuation character */ + b = *(*buffer)++; + if ((b & 0xc0) != 0x80) + return ~0U; + uc <<= 6; + uc |= b & 0x3f; + } + } + + /* overlong sequence? surrogate pair? out or range? */ + if (uc < min_uc || uc - 0xd800U < 2048U || uc > 0x10ffff) + return ~0U; + + return uc; +} + +#endif /* CBOR_UTF8_H */ From d94fb0023068e159012b01afd192be6dd7c5b506 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Tue, 21 Aug 2018 19:09:28 +0200 Subject: [PATCH 027/175] Squash #2 and fixup String and types handling TOTALLY UNTESTED --- ArduinoCloudThing.cpp | 229 ++++++++++++++++++++++++------------------ ArduinoCloudThing.h | 78 ++++++++------ 2 files changed, 177 insertions(+), 130 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 2809dc004..103d19609 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -47,22 +47,6 @@ void ArduinoCloudThing::begin() { addPropertyReal(status, "status").readOnly(); } -int ArduinoCloudThing::publish(CborArray& object, uint8_t* data, size_t size) { - - ssize_t len = object.encode(data, size); - -#ifdef TESTING_PROTOCOL - decode(data, len); -#endif - - for (int i = 0; i < list.size(); i++) { - ArduinoCloudPropertyGeneric *p = list.get(i); - p->updateShadow(); - } - - return len; -} - int ArduinoCloudThing::poll(uint8_t* data, size_t size) { // check if backing storage and cloud has diverged @@ -70,30 +54,52 @@ int ArduinoCloudThing::poll(uint8_t* data, size_t size) { diff = checkNewData(); if (diff > 0) { - CborBuffer buffer(1024); - CborArray object = CborArray(buffer); - compress(object, buffer); - diff = publish(object, data, size); + CborError err; + CborEncoder encoder, arrayEncoder; + cbor_encoder_init(&encoder, data, size, 0); + // create a cbor array containing the property that should be updated. + err = cbor_encoder_create_array(&encoder, &arrayEncoder, CborIndefiniteLength); + if (err) { + Serial.println(cbor_error_string(err)); + return -1; + } + for (int i = 0; i < list.size(); i++) { + ArduinoCloudPropertyGeneric *p = list.get(i); + // If a property should be updated and has read permission from the Cloud point of view + if (p->shouldBeUpdated() && p->canRead()) { + // create a cbor object for the property and automatically add it into array + p->append(&arrayEncoder); + } + } + + err = cbor_encoder_close_container(&encoder, &arrayEncoder); + // update properties shadow values, in order to check if variable has changed since last publish + + for (int i = 0; i < list.size(); i++) { + ArduinoCloudPropertyGeneric *p = list.get(i); + p->updateShadow(); + } + // return the number of byte of the CBOR encoded array + return cbor_encoder_get_buffer_size(&encoder, data); } #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) PrintFreeRam(); #endif - + // If nothing has to be sent, return diff, that is 0 in this case return diff; } -void ArduinoCloudThing::compress(CborArray& object, CborBuffer& buffer) { - +// It return the index of the property, inside the local array, with the name passed as parameter. (-1 if it does not exist.) +int ArduinoCloudThing::findPropertyByName(String &name) { for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); - if (p->shouldBeUpdated() && p->canRead()) { - CborObject child = CborObject(buffer); - p->append(child); - CborVariant variant = CborVariant(buffer, child); - object.add(variant); + // Check the property existance just comparing its name with existent ones + if (p->getName() == name) { + return i; } } + return -1; } int ArduinoCloudThing::checkNewData() { @@ -144,88 +150,113 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, return *(reinterpret_cast(thing)); } -void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { - CborBuffer buffer(200); - CborVariant total = buffer.decode(payload, length); +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(String& property, String name) { + if (ArduinoCloudPropertyGeneric* p = exists(name)) { + return *p; + } + ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); + list.add(thing); + return *(reinterpret_cast(thing)); +} - CborArray array = total.asArray(); +void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { + CborError err; + CborParser parser; + CborValue recursedMap, propValue, dataArray; + int propId; String propType, propName; + + err = cbor_parser_init(payload, length, 0, &parser, &dataArray); + if (err) { + return; + } - for (int i=0; ;i++) { - CborVariant variant = array.get(i); + // parse cbor data only if a cbor array is received. + if (dataArray.type != CborArrayType) + return; + + // main loop through the cbor array elements + while (!cbor_value_at_end(&dataArray)) { + // parse cbor object + cbor_value_enter_container(&dataArray, &recursedMap); + CborType type = cbor_value_get_type(&recursedMap); + if (type != CborMapType) { + // stop the decode when 1st item thai is not a cbor map is found. + cbor_value_advance(&dataArray); + continue; + } else { + + while (!cbor_value_at_end(&recursedMap)) { + // if the current element is not a cbor object as expected, skip it and go ahead. + if (cbor_value_get_type(&recursedMap) != CborMapType) { + cbor_value_advance(&recursedMap); + continue; + } - if (!variant.isValid()) { - break; - } + CborValue name; + // chechk for the if the a property has a name, if yes Cbor value name will properly updated + cbor_value_map_find_value(&recursedMap, "n", &name); + // check if a property has a name, of string type, if not do nothin and skip curtrent property + if (name.type != CborTextStringType) { + cbor_value_advance(&recursedMap); + continue; + } + // get the property name from cbor map as char* string + char *nameVal; size_t nameValSize; + err = cbor_value_dup_text_string(&name, &nameVal, &nameValSize, NULL); + if (err) { + break; // couldn't get the value of the field + } + // get the name of the received property as String object + propName = String(nameVal); + // used to avoid memory leaks (cbor_value_dup_text_string automatically perform a malloc) + free(nameVal); + // Search for the index of the device property with that name + propId = findPropertyByName(propName); + // If property does not exist, skip it and do nothing. + if (propId < 0) { + cbor_value_advance(&recursedMap); + continue; + } - CborObject object = variant.asObject(); + ArduinoCloudPropertyGeneric* property = list.get(propId); + // Check for the property type, write method internally check for the permission - String name = ""; - if (object.get("n").isValid()) { - name = object.get("n").asString(); - // search for the property with the same name - for (int idx = 0; idx < list.size(); idx++) { - ArduinoCloudPropertyGeneric *p = list.get(idx); - if (p->getName() == name) { - currentListIndex = idx; - break; + if (propValue.type == CborDoubleType) { + double val; + // get the value of the property as a double + cbor_value_get_double(&propValue, &val); + reinterpret_cast*>(property)->write((float)val); } - if (idx == list.size()) { - currentListIndex = -1; + // if no key proper key was found, do nothing + if (propValue.type == CborIntegerType) { + int val; + cbor_value_get_int(&propValue, &val); + reinterpret_cast*>(property)->write(val); } - } - } - - if (object.get("t").isValid()) { - int tag = object.get("t").asInteger(); - if (name != "") { - list.get(currentListIndex)->setTag(tag); - } else { - for (int idx = 0; idx < list.size(); idx++) { - ArduinoCloudPropertyGeneric *p = list.get(idx); - if (p->getTag() == tag) { - // if name == "" associate name and tag, otherwise set current list index - currentListIndex = idx; - break; - } - if (idx == list.size()) { - Serial.println("Property not found, skipping"); - currentListIndex = -1; + if (propValue.type == CborBooleanType) { + bool val; + cbor_value_get_boolean(&propValue, &val); + reinterpret_cast*>(property)->write(val); + } + if (propValue.type == CborTextStringType) { + char *val; size_t valSize; + err = cbor_value_dup_text_string(&propValue, &val, &valSize, &propValue); + // Char* string transformed into array + reinterpret_cast*>(property)->write(String((char*)val)); + free(val); + } + // If the property has been changed call its callback + if (property->newData()) { + if (property->callback != NULL) { + property->callback(); } } + // Continue to scan the cbor map + cbor_value_advance(&recursedMap); } } - - if (object.get("i").isValid()) { - int value_i = object.get("i").asInteger(); - reinterpret_cast*>(list.get(currentListIndex))->write(value_i); - } - - if (object.get("b").isValid()) { - bool value_b = object.get("b").asInteger(); - reinterpret_cast*>(list.get(currentListIndex))->write(value_b); - } -/* - if (object.get("f").isValid()) { - float value_f = object.get("f").asFloat(); - reinterpret_cast*>(list.get(currentListIndex))->write(value_f); - } -*/ - if (object.get("s").isValid()) { - String value_s = object.get("s").asString(); - reinterpret_cast*>(list.get(currentListIndex))->write(value_s); - } - - if (object.get("p").isValid()) { - permissionType value_p = (permissionType)object.get("p").asInteger(); - list.get(currentListIndex)->setPermission(value_p); - } - - if (list.get(currentListIndex)->newData()) { - // call onUpdate() - if (list.get(currentListIndex)->callback != NULL) { - list.get(currentListIndex)->callback(); - } - } + // Leave the current cbor object, and advance to the next one + err = cbor_value_leave_container(&dataArray, &recursedMap); } } \ No newline at end of file diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 0e36cb8c9..635a85d7f 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -4,11 +4,7 @@ #include #include #include "lib/LinkedList/LinkedList.h" -#include "lib/ArduinoCbor/src/ArduinoCbor.h" - -//#define TESTING_PROTOCOL -//#define DEBUG_MEMORY -#define USE_ARDUINO_CLOUD +#include "lib/tinycbor/cbor.h" enum permissionType { READ = 0b01, @@ -33,7 +29,7 @@ enum times { class ArduinoCloudPropertyGeneric { public: - virtual void append(CborObject& object) = 0; + virtual void append(CborEncoder* encoder) = 0; virtual String& getName() = 0; virtual void setName(String _name) = 0; virtual ArduinoCloudPropertyGeneric& setTag(int _tag) = 0; @@ -57,8 +53,10 @@ template class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { public: - ArduinoCloudProperty(T& _property, String _name) : - property(_property), name(_name) {} + ArduinoCloudProperty(T& _property, String _name) : + property(_property), name(_name) + { + } bool write(T value) { /* permissions are intended as seen from cloud */ @@ -134,19 +132,23 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric return *(reinterpret_cast(this)); } - void appendValue(CborObject &cbor); + void appendValue(CborEncoder* mapEncoder); - void append(CborObject &cbor) { + void append(CborEncoder* encoder) { if (!canRead()) { return; } + CborEncoder mapEncoder; + cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); if (tag != -1) { - cbor.set("t", tag); + cbor_encode_text_stringz(&mapEncoder, "t"); + cbor_encode_int(&mapEncoder, tag); } else { - cbor.set("n", name.c_str()); + cbor_encode_text_stringz(&mapEncoder, "n"); + cbor_encode_text_stringz(&mapEncoder, name.c_str()); } - appendValue(cbor); - cbor.set("p", permission); + appendValue(&mapEncoder); + cbor_encoder_close_container(encoder, &mapEncoder); lastUpdated = millis(); } @@ -156,7 +158,7 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric } bool newData() { - return (property != shadow_property && abs(property - shadow_property) > minDelta ); + return (property != shadow_property); } bool shouldBeUpdated() { @@ -167,9 +169,10 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric } inline bool operator==(const ArduinoCloudProperty& rhs){ - return (strcmp(getName(), rhs.getName) == 0); + return (strcmp(getName(), rhs.getName()) == 0); } + protected: T& property; T shadow_property; @@ -177,34 +180,49 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric int tag = -1; long lastUpdated = 0; long updatePolicy = ON_CHANGE; - T minDelta = 0; + T minDelta; permissionType permission = READWRITE; static int tagIndex; }; template <> -inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { - cbor.set("i", property); +inline bool ArduinoCloudProperty::newData() { + return (property != shadow_property && abs(property - shadow_property) > minDelta ); +} + +template <> +inline bool ArduinoCloudProperty::newData() { + return (property != shadow_property && abs(property - shadow_property) > minDelta ); +} + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_int(mapEncoder, property); }; template <> -inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { - cbor.set("b", property); +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_boolean(mapEncoder, property); }; template <> -inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { - cbor.set("f", property); +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_float(mapEncoder, property); }; template <> -inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { - cbor.set("s", property.c_str()); +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, property.c_str()); }; template <> -inline void ArduinoCloudProperty::appendValue(CborObject &cbor) { - cbor.set("s", property); +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, property->c_str()); +}; + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, property); }; class ArduinoCloudThing { @@ -215,17 +233,15 @@ class ArduinoCloudThing { ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name); ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name); ArduinoCloudPropertyGeneric& addPropertyReal(void* property, String name); - ArduinoCloudPropertyGeneric& addPropertyReal(String property, String name); + ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name); // poll should return > 0 if something has changed int poll(uint8_t* data, size_t size); void decode(uint8_t * payload, size_t length); private: - int publish(CborArray& object, uint8_t* data, size_t size); - void update(); int checkNewData(); - void compress(CborArray& object, CborBuffer& buffer); + int findPropertyByName(String &name); ArduinoCloudPropertyGeneric* exists(String &name); From 9d7cd84a9dc189cfd351eb888aa20afc9381e81c Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 24 Aug 2018 18:30:57 +0200 Subject: [PATCH 028/175] Restore "v" as key for value --- ArduinoCloudThing.cpp | 2 ++ ArduinoCloudThing.h | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 103d19609..fbeb11daf 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -222,6 +222,8 @@ void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { ArduinoCloudPropertyGeneric* property = list.get(propId); // Check for the property type, write method internally check for the permission + cbor_value_map_find_value(&recursedMap, "v", &propValue); + if (propValue.type == CborDoubleType) { double val; // get the value of the property as a double diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 635a85d7f..8960bb0f3 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -197,31 +197,37 @@ inline bool ArduinoCloudProperty::newData() { template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "v"); cbor_encode_int(mapEncoder, property); }; template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "v"); cbor_encode_boolean(mapEncoder, property); }; template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "v"); cbor_encode_float(mapEncoder, property); }; template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "v"); cbor_encode_text_stringz(mapEncoder, property.c_str()); }; template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "v"); cbor_encode_text_stringz(mapEncoder, property->c_str()); }; template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "v"); cbor_encode_text_stringz(mapEncoder, property); }; From fe468ab67e23498d4afd15e1d329cb0e8fa83977 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 24 Aug 2018 18:31:41 +0200 Subject: [PATCH 029/175] Instantiate shadow on addProperty --- ArduinoCloudThing.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index fbeb11daf..5015ba89e 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -129,6 +129,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, S } ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); list.add(thing); + thing->shadow_property = -1; return *(reinterpret_cast(thing)); } @@ -138,6 +139,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, } ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); list.add(thing); + thing->shadow_property = !property; return *(reinterpret_cast(thing)); } @@ -147,6 +149,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, } ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); list.add(thing); + thing->shadow_property = property - 1.0f; return *(reinterpret_cast(thing)); } @@ -156,6 +159,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(String& property } ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); list.add(thing); + thing->shadow_property = ""; return *(reinterpret_cast(thing)); } From f4c0ef3f1282e4dc53b17c12bda68f2915be0260 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 24 Aug 2018 18:32:34 +0200 Subject: [PATCH 030/175] Start adding unit tests --- ArduinoCloudThing.h | 62 +++++++++++++++++++++++---------------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 8960bb0f3..648e2cfbf 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -43,12 +43,39 @@ class ArduinoCloudPropertyGeneric virtual bool shouldBeUpdated() = 0; virtual void updateShadow() = 0; virtual bool canRead() = 0; - virtual void printinfo() = 0; + virtual void printinfo(Stream& stream) = 0; virtual ArduinoCloudPropertyGeneric& onUpdate(void(*fn)(void)) = 0; virtual ArduinoCloudPropertyGeneric& publishEvery(long seconds) = 0; void(*callback)(void) = NULL; }; +class ArduinoCloudThing { +public: + ArduinoCloudThing(); + void begin(); + ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name); + ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name); + ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name); + ArduinoCloudPropertyGeneric& addPropertyReal(void* property, String name); + ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name); + // poll should return > 0 if something has changed + int poll(uint8_t* data, size_t size); + void decode(uint8_t * payload, size_t length); + +private: + void update(); + int checkNewData(); + int findPropertyByName(String &name); + + ArduinoCloudPropertyGeneric* exists(String &name); + + bool status = OFF; + char uuid[33]; + + LinkedList list; + int currentListIndex = -1; +}; + template class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { @@ -67,8 +94,8 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric return false; } - void printinfo() { - Serial.println("name: " + name + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(permission)); + void printinfo(Stream& stream) { + stream.println("name: " + name + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(permission)); } void updateShadow() { @@ -183,6 +210,8 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric T minDelta; permissionType permission = READWRITE; static int tagIndex; + + friend ArduinoCloudThing; }; template <> @@ -231,31 +260,4 @@ inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { cbor_encode_text_stringz(mapEncoder, property); }; -class ArduinoCloudThing { -public: - ArduinoCloudThing(); - void begin(); - ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name); - ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name); - ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name); - ArduinoCloudPropertyGeneric& addPropertyReal(void* property, String name); - ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name); - // poll should return > 0 if something has changed - int poll(uint8_t* data, size_t size); - void decode(uint8_t * payload, size_t length); - -private: - void update(); - int checkNewData(); - int findPropertyByName(String &name); - - ArduinoCloudPropertyGeneric* exists(String &name); - - bool status = OFF; - char uuid[33]; - - LinkedList list; - int currentListIndex = -1; -}; - #endif From 3f8cfbcdec0c4bba17edca96ec5d6f2dbeda28a7 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Sat, 25 Aug 2018 00:23:56 +0200 Subject: [PATCH 031/175] add writeOnly test --- ArduinoCloudThing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 5015ba89e..cffac6797 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -106,7 +106,7 @@ int ArduinoCloudThing::checkNewData() { int counter = 0; for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); - if (p->shouldBeUpdated()) { + if (p->shouldBeUpdated() && p->canRead()) { counter++; } } From dd146251cbffa32351e62866622c7440bf6ea24c Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Sat, 25 Aug 2018 01:54:24 +0200 Subject: [PATCH 032/175] Fix delta stuff --- ArduinoCloudThing.cpp | 2 ++ ArduinoCloudThing.h | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index cffac6797..47528f83c 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -130,6 +130,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, S ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); list.add(thing); thing->shadow_property = -1; + thing->minDelta = 0; return *(reinterpret_cast(thing)); } @@ -149,6 +150,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, } ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); list.add(thing); + thing->minDelta = 0.0f; thing->shadow_property = property - 1.0f; return *(reinterpret_cast(thing)); } diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 648e2cfbf..9faf150c6 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -192,7 +192,7 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric if (updatePolicy == ON_CHANGE) { return newData(); } - return (millis() - lastUpdated > updatePolicy * 1000) ; + return ((millis() - lastUpdated) > (updatePolicy * 1000)) ; } inline bool operator==(const ArduinoCloudProperty& rhs){ @@ -216,12 +216,12 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric template <> inline bool ArduinoCloudProperty::newData() { - return (property != shadow_property && abs(property - shadow_property) > minDelta ); + return (property != shadow_property && abs(property - shadow_property) >= minDelta ); } template <> inline bool ArduinoCloudProperty::newData() { - return (property != shadow_property && abs(property - shadow_property) > minDelta ); + return (property != shadow_property && abs(property - shadow_property) >= minDelta ); } template <> From 188e4f9cf5063ab6e0afbc8fd161d810d68eb560 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Tue, 28 Aug 2018 00:27:46 +0200 Subject: [PATCH 033/175] Proposal: fix map and array length No functional change, some tests need to be modified --- ArduinoCloudThing.cpp | 6 +++--- ArduinoCloudThing.h | 11 +++-------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 47528f83c..bbe370883 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -56,14 +56,14 @@ int ArduinoCloudThing::poll(uint8_t* data, size_t size) { if (diff > 0) { CborError err; CborEncoder encoder, arrayEncoder; - cbor_encoder_init(&encoder, data, size, 0); + cbor_encoder_init(&encoder, data, size, 0); // create a cbor array containing the property that should be updated. - err = cbor_encoder_create_array(&encoder, &arrayEncoder, CborIndefiniteLength); + err = cbor_encoder_create_array(&encoder, &arrayEncoder, diff); if (err) { Serial.println(cbor_error_string(err)); return -1; } - for (int i = 0; i < list.size(); i++) { + for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); // If a property should be updated and has read permission from the Cloud point of view if (p->shouldBeUpdated() && p->canRead()) { diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 9faf150c6..1d5ac2818 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -166,14 +166,15 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric return; } CborEncoder mapEncoder; - cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); + cbor_encoder_create_map(encoder, &mapEncoder, 2); if (tag != -1) { cbor_encode_text_stringz(&mapEncoder, "t"); cbor_encode_int(&mapEncoder, tag); } else { cbor_encode_text_stringz(&mapEncoder, "n"); - cbor_encode_text_stringz(&mapEncoder, name.c_str()); + cbor_encode_text_string(&mapEncoder, name.c_str(), name.length()); } + cbor_encode_text_stringz(&mapEncoder, "v"); appendValue(&mapEncoder); cbor_encoder_close_container(encoder, &mapEncoder); lastUpdated = millis(); @@ -226,37 +227,31 @@ inline bool ArduinoCloudProperty::newData() { template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "v"); cbor_encode_int(mapEncoder, property); }; template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "v"); cbor_encode_boolean(mapEncoder, property); }; template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "v"); cbor_encode_float(mapEncoder, property); }; template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "v"); cbor_encode_text_stringz(mapEncoder, property.c_str()); }; template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "v"); cbor_encode_text_stringz(mapEncoder, property->c_str()); }; template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "v"); cbor_encode_text_stringz(mapEncoder, property); }; From 89b8f076aa173ac9db2292e35c52bf22a92db040 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Mon, 3 Sep 2018 17:29:14 +0200 Subject: [PATCH 034/175] Remove composition APIs --- ArduinoCloudThing.cpp | 23 +++++++++++++------ ArduinoCloudThing.h | 52 +++++-------------------------------------- 2 files changed, 21 insertions(+), 54 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index bbe370883..60f7d30df 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -44,7 +44,7 @@ ArduinoCloudThing::ArduinoCloudThing() { void ArduinoCloudThing::begin() { status = ON; - addPropertyReal(status, "status").readOnly(); + addPropertyReal(status, "status", READ); } int ArduinoCloudThing::poll(uint8_t* data, size_t size) { @@ -123,45 +123,54 @@ ArduinoCloudPropertyGeneric* ArduinoCloudThing::exists(String &name) { return NULL; } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, String name) { +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, String name, permissionType _permission, long seconds, void(*fn)(void), int minDelta) { if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); list.add(thing); thing->shadow_property = -1; - thing->minDelta = 0; + thing->minDelta = minDelta; + thing->permission = _permission; + thing->minDelta = minDelta; + thing->updatePolicy = seconds; return *(reinterpret_cast(thing)); } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, String name) { +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, String name, permissionType _permission, long seconds, void(*fn)(void)) { if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); list.add(thing); thing->shadow_property = !property; + thing->permission = _permission; + thing->updatePolicy = seconds; return *(reinterpret_cast(thing)); } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, String name) { +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, String name, permissionType _permission, long seconds, void(*fn)(void), float minDelta) { if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); list.add(thing); - thing->minDelta = 0.0f; + thing->permission = _permission; + thing->minDelta = minDelta; + thing->updatePolicy = seconds; thing->shadow_property = property - 1.0f; return *(reinterpret_cast(thing)); } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(String& property, String name) { +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(String& property, String name, permissionType _permission, long seconds, void(*fn)(void)) { if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); list.add(thing); thing->shadow_property = ""; + thing->permission = _permission; + thing->updatePolicy = seconds; return *(reinterpret_cast(thing)); } diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 1d5ac2818..6d6493830 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -31,21 +31,13 @@ class ArduinoCloudPropertyGeneric public: virtual void append(CborEncoder* encoder) = 0; virtual String& getName() = 0; - virtual void setName(String _name) = 0; - virtual ArduinoCloudPropertyGeneric& setTag(int _tag) = 0; - virtual ArduinoCloudPropertyGeneric& readOnly() = 0; - virtual ArduinoCloudPropertyGeneric& writeOnly() = 0; - virtual ArduinoCloudPropertyGeneric& minimumDelta(void* delta) = 0; virtual int getTag() = 0; - virtual ArduinoCloudPropertyGeneric& setPermission(permissionType _permission) = 0; virtual permissionType getPermission() = 0; virtual bool newData() = 0; virtual bool shouldBeUpdated() = 0; virtual void updateShadow() = 0; virtual bool canRead() = 0; virtual void printinfo(Stream& stream) = 0; - virtual ArduinoCloudPropertyGeneric& onUpdate(void(*fn)(void)) = 0; - virtual ArduinoCloudPropertyGeneric& publishEvery(long seconds) = 0; void(*callback)(void) = NULL; }; @@ -53,11 +45,11 @@ class ArduinoCloudThing { public: ArduinoCloudThing(); void begin(); - ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name); - ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name); - ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name); - ArduinoCloudPropertyGeneric& addPropertyReal(void* property, String name); - ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name); + ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, int minDelta = 0); + ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL); + ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f); + ArduinoCloudPropertyGeneric& addPropertyReal(void* property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL); + ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL); // poll should return > 0 if something has changed int poll(uint8_t* data, size_t size); void decode(uint8_t * payload, size_t length); @@ -117,48 +109,14 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric return name; } - void setName(String _name) { - name = _name; - } - - ArduinoCloudPropertyGeneric& setTag(int _tag) { - tag = _tag; - return *(reinterpret_cast(this)); - } - int getTag() { return tag; } - ArduinoCloudPropertyGeneric& setPermission(permissionType _permission) { - permission = _permission; - return *(reinterpret_cast(this)); - } - - ArduinoCloudPropertyGeneric& readOnly() { - permission = READ; - return *(reinterpret_cast(this)); - } - - ArduinoCloudPropertyGeneric& writeOnly() { - permission = WRITE; - return *(reinterpret_cast(this)); - } - - ArduinoCloudPropertyGeneric& minimumDelta(void* delta) { - minDelta = *(T*)delta; - return *(reinterpret_cast(this)); - } - permissionType getPermission() { return permission; } - ArduinoCloudPropertyGeneric& onUpdate(void(*fn)(void)) { - callback = fn; - return *(reinterpret_cast(this)); - } - void appendValue(CborEncoder* mapEncoder); void append(CborEncoder* encoder) { From c3df6f41c5ff05135459c8a77106ff8c4350b133 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 7 Sep 2018 15:09:12 +0200 Subject: [PATCH 035/175] add minDelta to all function signatures --- ArduinoCloudThing.cpp | 4 ++-- ArduinoCloudThing.h | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 60f7d30df..d8980f88f 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -137,7 +137,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, S return *(reinterpret_cast(thing)); } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, String name, permissionType _permission, long seconds, void(*fn)(void)) { +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, String name, permissionType _permission, long seconds, void(*fn)(void), bool minDelta) { if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } @@ -162,7 +162,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, return *(reinterpret_cast(thing)); } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(String& property, String name, permissionType _permission, long seconds, void(*fn)(void)) { +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(String& property, String name, permissionType _permission, long seconds, void(*fn)(void), String mindelta) { if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 6d6493830..2739e4c26 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -46,10 +46,9 @@ class ArduinoCloudThing { ArduinoCloudThing(); void begin(); ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, int minDelta = 0); - ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL); + ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, bool minDelta = false); ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f); - ArduinoCloudPropertyGeneric& addPropertyReal(void* property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL); - ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL); + ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, String minDelta = ""); // poll should return > 0 if something has changed int poll(uint8_t* data, size_t size); void decode(uint8_t * payload, size_t length); From d5edc24e6015b1ab4ba08a354c94861c6d684c6c Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 7 Sep 2018 16:22:01 +0200 Subject: [PATCH 036/175] overload addProperty(float...double) --- ArduinoCloudThing.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 2739e4c26..567cadba2 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -48,6 +48,9 @@ class ArduinoCloudThing { ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, int minDelta = 0); ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, bool minDelta = false); ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f); + ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, double minDelta = 0.0f) { + return addPropertyReal(property, name, _permission, seconds, fn, (float)minDelta); + } ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, String minDelta = ""); // poll should return > 0 if something has changed int poll(uint8_t* data, size_t size); From f646f9c6be4ce74a24d196d75febba0eae16fe38 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 7 Sep 2018 16:43:30 +0200 Subject: [PATCH 037/175] Revert "overload addProperty(float...double)" This reverts commit 236422433e6ebd70fadf6327d2e2328bd9f05538. --- ArduinoCloudThing.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 567cadba2..2739e4c26 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -48,9 +48,6 @@ class ArduinoCloudThing { ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, int minDelta = 0); ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, bool minDelta = false); ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f); - ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, double minDelta = 0.0f) { - return addPropertyReal(property, name, _permission, seconds, fn, (float)minDelta); - } ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, String minDelta = ""); // poll should return > 0 if something has changed int poll(uint8_t* data, size_t size); From c79748b19923a454eeccaab423f51879f9d84de8 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Fri, 7 Sep 2018 16:56:08 +0200 Subject: [PATCH 038/175] Fix callback not being populated --- ArduinoCloudThing.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index d8980f88f..a0de6321a 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -134,6 +134,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, S thing->permission = _permission; thing->minDelta = minDelta; thing->updatePolicy = seconds; + thing->callback = fn; return *(reinterpret_cast(thing)); } @@ -146,6 +147,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, thing->shadow_property = !property; thing->permission = _permission; thing->updatePolicy = seconds; + thing->callback = fn; return *(reinterpret_cast(thing)); } @@ -159,6 +161,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, thing->minDelta = minDelta; thing->updatePolicy = seconds; thing->shadow_property = property - 1.0f; + thing->callback = fn; return *(reinterpret_cast(thing)); } @@ -171,6 +174,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(String& property thing->shadow_property = ""; thing->permission = _permission; thing->updatePolicy = seconds; + thing->callback = fn; return *(reinterpret_cast(thing)); } From d6e90f5930d0ccdad813c2b8781fb1fe5d7e52b9 Mon Sep 17 00:00:00 2001 From: Martino Facchin Date: Mon, 10 Sep 2018 12:30:45 +0200 Subject: [PATCH 039/175] Handle errors in cbor_value_advance Fixes infinite loops on input like unsigned char buf[] = {0x81, 0xff, 0xA2, 0x61, 0x6E, 0x64, 0x74, 0x65, 0x73, 0x74, 0x61, 0x76, 0x7, 0x0}; thing.decode((uint8_t*)buf, sizeof(buf)); --- ArduinoCloudThing.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index a0de6321a..654819ef1 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -200,14 +200,20 @@ void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { CborType type = cbor_value_get_type(&recursedMap); if (type != CborMapType) { // stop the decode when 1st item thai is not a cbor map is found. - cbor_value_advance(&dataArray); + CborError err = cbor_value_advance(&dataArray); + if (err != CborNoError) { + break; + } continue; } else { while (!cbor_value_at_end(&recursedMap)) { // if the current element is not a cbor object as expected, skip it and go ahead. if (cbor_value_get_type(&recursedMap) != CborMapType) { - cbor_value_advance(&recursedMap); + CborError err = cbor_value_advance(&recursedMap); + if (err != CborNoError) { + break; + } continue; } From 1d504ca57326a0bea99be76e5934194aae863c1a Mon Sep 17 00:00:00 2001 From: Simone Marchisio Date: Tue, 11 Sep 2018 11:50:51 +0200 Subject: [PATCH 040/175] restored CBOR decode function --- ArduinoCloudThing.cpp | 110 ++++++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 47 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 654819ef1..5e4508953 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -185,47 +185,48 @@ void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { int propId; String propType, propName; err = cbor_parser_init(payload, length, 0, &parser, &dataArray); - if (err) { + if(err) { + //Serial.println("Error in the parser creation."); + //Serial.println(cbor_error_string(err)); return; } // parse cbor data only if a cbor array is received. - if (dataArray.type != CborArrayType) + if(dataArray.type != CborArrayType) return; // main loop through the cbor array elements - while (!cbor_value_at_end(&dataArray)) { - // parse cbor object + while (!cbor_value_at_end(&dataArray)) { + + // parse cbor object cbor_value_enter_container(&dataArray, &recursedMap); + CborType type = cbor_value_get_type(&recursedMap); if (type != CborMapType) { // stop the decode when 1st item thai is not a cbor map is found. - CborError err = cbor_value_advance(&dataArray); - if (err != CborNoError) { - break; - } + cbor_value_advance(&dataArray); continue; - } else { + } else { + while (!cbor_value_at_end(&recursedMap)) { - // if the current element is not a cbor object as expected, skip it and go ahead. - if (cbor_value_get_type(&recursedMap) != CborMapType) { - CborError err = cbor_value_advance(&recursedMap); - if (err != CborNoError) { - break; - } + + // if the current element is not a cbor object as expected, skip it and go ahead. + if(cbor_value_get_type(&recursedMap) != CborMapType) { + cbor_value_advance(&recursedMap); continue; } CborValue name; // chechk for the if the a property has a name, if yes Cbor value name will properly updated cbor_value_map_find_value(&recursedMap, "n", &name); - // check if a property has a name, of string type, if not do nothin and skip curtrent property + + // check if a property has a name, of string type, if not do nothin and skip curtrent property if (name.type != CborTextStringType) { cbor_value_advance(&recursedMap); continue; } - + // get the property name from cbor map as char* string char *nameVal; size_t nameValSize; err = cbor_value_dup_text_string(&name, &nameVal, &nameValSize, NULL); @@ -236,52 +237,67 @@ void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { propName = String(nameVal); // used to avoid memory leaks (cbor_value_dup_text_string automatically perform a malloc) free(nameVal); - // Search for the index of the device property with that name + + // Search for the index of the device property with that name propId = findPropertyByName(propName); // If property does not exist, skip it and do nothing. if (propId < 0) { cbor_value_advance(&recursedMap); continue; } - + ArduinoCloudPropertyGeneric* property = list.get(propId); // Check for the property type, write method internally check for the permission - - cbor_value_map_find_value(&recursedMap, "v", &propValue); - - if (propValue.type == CborDoubleType) { - double val; - // get the value of the property as a double - cbor_value_get_double(&propValue, &val); - reinterpret_cast*>(property)->write((float)val); - } - // if no key proper key was found, do nothing - if (propValue.type == CborIntegerType) { - int val; - cbor_value_get_int(&propValue, &val); - reinterpret_cast*>(property)->write(val); - } - if (propValue.type == CborBooleanType) { - bool val; - cbor_value_get_boolean(&propValue, &val); - reinterpret_cast*>(property)->write(val); - } - if (propValue.type == CborTextStringType) { - char *val; size_t valSize; - err = cbor_value_dup_text_string(&propValue, &val, &valSize, &propValue); - // Char* string transformed into array - reinterpret_cast*>(property)->write(String((char*)val)); - free(val); + propType = property->getType(); + + if (propType == "FLOAT" && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { + if (propValue.type == CborDoubleType) { + double val; + // get the value of the property as a double + cbor_value_get_double(&propValue, &val); + ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + p->write((float)val); + } + } else if (propType == "INT" && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { + // if no key proper key was found, do nothing + if (propValue.type == CborIntegerType) { + int val; + cbor_value_get_int(&propValue, &val); + ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + p->write(val); + } else if (propValue.type == CborDoubleType) { + // If a double value is received, a cast to int is performed(so it is still accepted) + double val; + cbor_value_get_double(&propValue, &val); + ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + p->write((int)val); + } + } else if (propType == "BOOL" && !cbor_value_map_find_value(&recursedMap, "vb", &propValue)) { + if (propValue.type == CborBooleanType) { + bool val; + cbor_value_get_boolean(&propValue, &val); + ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + p->write(val); + } + } else if (propType == "STRING" && !cbor_value_map_find_value(&recursedMap, "vs", &propValue)){ + if (propValue.type == CborTextStringType) { + char *val; size_t valSize; + err = cbor_value_dup_text_string(&propValue, &val, &valSize, &propValue); + ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + // Char* string transformed into array + p->write(String((char*)val)); + free(val); + } } // If the property has been changed call its callback if (property->newData()) { if (property->callback != NULL) { property->callback(); } - } + } // Continue to scan the cbor map cbor_value_advance(&recursedMap); - } + } } // Leave the current cbor object, and advance to the next one err = cbor_value_leave_container(&dataArray, &recursedMap); From 873f91d4124c08c1c5602ed8c024684b86bc8f9c Mon Sep 17 00:00:00 2001 From: Simone Marchisio Date: Tue, 11 Sep 2018 13:04:39 +0200 Subject: [PATCH 041/175] Updated the ArduinoCloudThing library: added getType method for property and fixed property encoding, cbor library refactoring --- ArduinoCloudThing.h | 288 ++++++++++-------- lib/tinycbor/cbor-lib.h | 6 + lib/tinycbor/parsetags.pl | 116 ------- lib/tinycbor/src.pri | 16 - lib/tinycbor/{ => src}/cbor.dox | 0 lib/tinycbor/{ => src}/cbor.h | 0 lib/tinycbor/{ => src}/cborencoder.c | 0 .../cborencoder_close_container_checked.c | 0 lib/tinycbor/{ => src}/cborerrorstrings.c | 0 lib/tinycbor/{ => src}/cborinternal_p.h | 0 lib/tinycbor/{ => src}/cborjson.h | 0 lib/tinycbor/{ => src}/cborparser.c | 0 .../{ => src}/cborparser_dup_string.c | 0 lib/tinycbor/{ => src}/cborpretty.c | 0 lib/tinycbor/{ => src}/cborpretty_stdio.c | 0 lib/tinycbor/{ => src}/cbortojson.c | 0 lib/tinycbor/{ => src}/cborvalidation.c | 0 lib/tinycbor/{ => src}/compilersupport_p.h | 0 lib/tinycbor/{ => src}/open_memstream.c | 0 lib/tinycbor/{ => src}/tags.txt | 0 lib/tinycbor/{ => src}/tinycbor-version.h | 0 lib/tinycbor/{ => src}/utf8_p.h | 0 lib/tinycbor/tinycbor.pro | 6 - 23 files changed, 163 insertions(+), 269 deletions(-) create mode 100644 lib/tinycbor/cbor-lib.h delete mode 100755 lib/tinycbor/parsetags.pl delete mode 100644 lib/tinycbor/src.pri rename lib/tinycbor/{ => src}/cbor.dox (100%) rename lib/tinycbor/{ => src}/cbor.h (100%) rename lib/tinycbor/{ => src}/cborencoder.c (100%) rename lib/tinycbor/{ => src}/cborencoder_close_container_checked.c (100%) rename lib/tinycbor/{ => src}/cborerrorstrings.c (100%) rename lib/tinycbor/{ => src}/cborinternal_p.h (100%) rename lib/tinycbor/{ => src}/cborjson.h (100%) rename lib/tinycbor/{ => src}/cborparser.c (100%) rename lib/tinycbor/{ => src}/cborparser_dup_string.c (100%) rename lib/tinycbor/{ => src}/cborpretty.c (100%) rename lib/tinycbor/{ => src}/cborpretty_stdio.c (100%) rename lib/tinycbor/{ => src}/cbortojson.c (100%) rename lib/tinycbor/{ => src}/cborvalidation.c (100%) rename lib/tinycbor/{ => src}/compilersupport_p.h (100%) rename lib/tinycbor/{ => src}/open_memstream.c (100%) rename lib/tinycbor/{ => src}/tags.txt (100%) rename lib/tinycbor/{ => src}/tinycbor-version.h (100%) rename lib/tinycbor/{ => src}/utf8_p.h (100%) delete mode 100644 lib/tinycbor/tinycbor.pro diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 2739e4c26..731a81912 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -4,7 +4,10 @@ #include #include #include "lib/LinkedList/LinkedList.h" -#include "lib/tinycbor/cbor.h" +#include "lib/tinycbor/cbor-lib.h" + +// definition of the default property update policy +static const int ON_CHANGE = -1; enum permissionType { READ = 0b01, @@ -17,7 +20,13 @@ enum boolStatus { OFF, }; -#define ON_CHANGE -1 +// allowed property types +enum propertyType { + INT, + FLOAT, + BOOL, + STRING +}; enum times { SECONDS = 1, @@ -26,150 +35,151 @@ enum times { DAYS = 86400, }; -class ArduinoCloudPropertyGeneric -{ -public: - virtual void append(CborEncoder* encoder) = 0; - virtual String& getName() = 0; - virtual int getTag() = 0; - virtual permissionType getPermission() = 0; - virtual bool newData() = 0; - virtual bool shouldBeUpdated() = 0; - virtual void updateShadow() = 0; - virtual bool canRead() = 0; - virtual void printinfo(Stream& stream) = 0; - void(*callback)(void) = NULL; +class ArduinoCloudPropertyGeneric { + public: + virtual void append(CborEncoder* encoder) = 0; + virtual String& getName() = 0; + virtual int getTag() = 0; + virtual propertyType getType() = 0; + virtual permissionType getPermission() = 0; + virtual bool newData() = 0; + virtual bool shouldBeUpdated() = 0; + virtual void updateShadow() = 0; + virtual bool canRead() = 0; + virtual void printinfo(Stream& stream) = 0; + void(*callback)(void) = NULL; }; class ArduinoCloudThing { -public: - ArduinoCloudThing(); - void begin(); - ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, int minDelta = 0); - ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, bool minDelta = false); - ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f); - ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, String minDelta = ""); - // poll should return > 0 if something has changed - int poll(uint8_t* data, size_t size); - void decode(uint8_t * payload, size_t length); - -private: - void update(); - int checkNewData(); - int findPropertyByName(String &name); - - ArduinoCloudPropertyGeneric* exists(String &name); - - bool status = OFF; - char uuid[33]; - - LinkedList list; - int currentListIndex = -1; + public: + ArduinoCloudThing(); + void begin(); + // overload for different type of properties + ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, int minDelta = 0); + ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, bool minDelta = false); + ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f); + ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, String minDelta = ""); + // poll should return > 0 if something has changed + int poll(uint8_t* data, size_t size); + // decode a CBOR payload received from the Cloud. + void decode(uint8_t * payload, size_t length); + bool hasAllReadProperties(); + + private: + void update(); + int checkNewData(); + // return the index of that property in the linked list, -1 if it does not exist. + int findPropertyByName(String &name); + // return the pointer to the desired property, NULL if it does not exist. + ArduinoCloudPropertyGeneric* exists(String &name); + + bool status = OFF; + char uuid[33]; + int currentListIndex = -1; + LinkedList list; + }; +// ArduinoCloudProperty declaration and definition. It inherits from ArduinoCloudPropertyGeneric interface. template -class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric -{ -public: - ArduinoCloudProperty(T& _property, String _name) : - property(_property), name(_name) - { +class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { + public: + ArduinoCloudProperty(T& _property, String _name) : + property(_property), name(_name) + { + } + + bool write(T value) { + /* permissions are intended as seen from cloud */ + if (permission & WRITE) { + property = value; + return true; + } + return false; } - bool write(T value) { - /* permissions are intended as seen from cloud */ - if (permission & WRITE) { - property = value; - return true; + void printinfo(Stream& stream) { + stream.println("name: " + name + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(permission)); } - return false; - } - void printinfo(Stream& stream) { - stream.println("name: " + name + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(permission)); - } - - void updateShadow() { - shadow_property = property; - } + void updateShadow() { + shadow_property = property; + } - T read() { - /* permissions are intended as seen from cloud */ - if (permission & READ) { - return property; + T read() { + /* permissions are intended as seen from cloud */ + if (permission & READ) { + return property; + } } - } - bool canRead() { - return (permission & READ); - } + bool canRead() { + return (permission & READ); + } - String& getName() { - return name; - } + String& getName() { + return name; + } - int getTag() { - return tag; - } + int getTag() { + return tag; + } - permissionType getPermission() { - return permission; - } + permissionType getPermission() { + return permission; + } - void appendValue(CborEncoder* mapEncoder); + void appendValue(CborEncoder* mapEncoder); + + void append(CborEncoder* encoder) { + if (!canRead()) { + return; + } + CborEncoder mapEncoder; + cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); + if (tag != -1) { + cbor_encode_text_stringz(&mapEncoder, "t"); + cbor_encode_int(&mapEncoder, tag); + } else { + cbor_encode_text_stringz(&mapEncoder, "n"); + cbor_encode_text_stringz(&mapEncoder, name.c_str()); + } + appendValue(&mapEncoder); + cbor_encoder_close_container(encoder, &mapEncoder); + lastUpdated = millis(); + } - void append(CborEncoder* encoder) { - if (!canRead()) { - return; + bool newData() { + return (property != shadow_property); } - CborEncoder mapEncoder; - cbor_encoder_create_map(encoder, &mapEncoder, 2); - if (tag != -1) { - cbor_encode_text_stringz(&mapEncoder, "t"); - cbor_encode_int(&mapEncoder, tag); - } else { - cbor_encode_text_stringz(&mapEncoder, "n"); - cbor_encode_text_string(&mapEncoder, name.c_str(), name.length()); + + bool shouldBeUpdated() { + if (updatePolicy == ON_CHANGE) { + return newData(); + } + return ((millis() - lastUpdated) > (updatePolicy * 1000)) ; } - cbor_encode_text_stringz(&mapEncoder, "v"); - appendValue(&mapEncoder); - cbor_encoder_close_container(encoder, &mapEncoder); - lastUpdated = millis(); - } - - ArduinoCloudPropertyGeneric& publishEvery(long seconds) { - updatePolicy = seconds; - return *(reinterpret_cast(this)); - } - - bool newData() { - return (property != shadow_property); - } - - bool shouldBeUpdated() { - if (updatePolicy == ON_CHANGE) { - return newData(); + + inline bool operator==(const ArduinoCloudProperty& rhs){ + return (getName() == rhs.getName()); } - return ((millis() - lastUpdated) > (updatePolicy * 1000)) ; - } - - inline bool operator==(const ArduinoCloudProperty& rhs){ - return (strcmp(getName(), rhs.getName()) == 0); - } - - -protected: - T& property; - T shadow_property; - String name; - int tag = -1; - long lastUpdated = 0; - long updatePolicy = ON_CHANGE; - T minDelta; - permissionType permission = READWRITE; - static int tagIndex; - - friend ArduinoCloudThing; + + propertyType getType(); + + protected: + T& property; + T shadow_property; + String name; + int tag = -1; + long lastUpdated = 0; + long updatePolicy = ON_CHANGE; + T minDelta; + permissionType permission = READWRITE; + static int tagIndex; + + // In this way it is possible to set shadow_property(protected), from ArduinoCloudThing, + // when a new property is added. + friend ArduinoCloudThing; }; template <> @@ -182,34 +192,50 @@ inline bool ArduinoCloudProperty::newData() { return (property != shadow_property && abs(property - shadow_property) >= minDelta ); } +// Different appendValue function for different property typer, because the CBOR encoder and message format template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "v"); cbor_encode_int(mapEncoder, property); }; template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "vb"); cbor_encode_boolean(mapEncoder, property); }; template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "v"); cbor_encode_float(mapEncoder, property); }; template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "vs"); cbor_encode_text_stringz(mapEncoder, property.c_str()); }; +// Return property type template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, property->c_str()); -}; +inline propertyType ArduinoCloudProperty::getType() { + return INT; +} template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, property); -}; +inline propertyType ArduinoCloudProperty::getType() { + return FLOAT; +} + +template <> +inline propertyType ArduinoCloudProperty::getType() { + return BOOL; +} + +template <> +inline propertyType ArduinoCloudProperty::getType() { + return STRING; +} #endif diff --git a/lib/tinycbor/cbor-lib.h b/lib/tinycbor/cbor-lib.h new file mode 100644 index 000000000..0ac58a17d --- /dev/null +++ b/lib/tinycbor/cbor-lib.h @@ -0,0 +1,6 @@ +#ifndef CBOR_LIB_H +#define CBOR_LIB_H + +#include "src/cbor.h" + +#endif \ No newline at end of file diff --git a/lib/tinycbor/parsetags.pl b/lib/tinycbor/parsetags.pl deleted file mode 100755 index fbb182993..000000000 --- a/lib/tinycbor/parsetags.pl +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/perl -l -## Copyright (C) 2017 Intel Corporation -## -## Permission is hereby granted, free of charge, to any person obtaining a copy -## of this software and associated documentation files (the "Software"), to deal -## in the Software without restriction, including without limitation the rights -## to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -## copies of the Software, and to permit persons to whom the Software is -## furnished to do so, subject to the following conditions: -## -## The above copyright notice and this permission notice shall be included in -## all copies or substantial portions of the Software. -## -## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -## IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -## FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -## AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -## LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -## OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -## THE SOFTWARE. -## -use strict; -my $fname = shift @ARGV - or die("Usage: parsetags.pl tags.txt"); -open TAGS, "<", $fname - or die("Cannot open $fname: $!"); - -my %typedescriptions = ( - "Integer" => "integer", - "ByteString" => "byte string", - "TextString" => "UTF-8 text string", - "Array" => "array", - "Map" => "map", - "Tag" => "tag", # shouldn't happen - "Simple" => "any simple type", - "Boolean" => "boolean", - "Null" => "null", - "Undefined" => "undefined", - "HalfFloat" => "IEEE 754 half-precision floating point", - "Float" => "IEEE 754 single-precision floating point", - "Double" => "IEEE 754 double-precision floating point" -); - -my %tags; -while () { - s/\s*#.*$//; - next if /^$/; - chomp; - - die("Could not parse line \"$_\"") - unless /^(\d+);(\w+);([\w,]*);(.*)$/; - $tags{$1}{id} = $2; - $tags{$1}{semantic} = $4; - my @types = split(',', $3); - $tags{$1}{types} = \@types; -} -close TAGS or die; - -my @tagnumbers = sort { $a <=> $b } keys %tags; - -print "==== HTML listing ===="; -print "\n \n \n \n \n "; -for my $n (@tagnumbers) { - print " "; - print " "; - - my @types = @{$tags{$n}{types}}; - @types = map { $typedescriptions{$_}; } @types; - unshift @types, "any" - if (scalar @types == 0); - printf " \n", join(', ', @types); - printf " \n", $tags{$n}{semantic}; - print " "; -} -print "
TagData ItemSemantics
$n%s%s
"; - -print "\n==== enum listing for cbor.h ====\n"; -printf "typedef enum CborKnownTags {"; -my $comma = ""; -for my $n (@tagnumbers) { - printf "%s\n Cbor%sTag%s = %d", $comma, - $tags{$n}{id}, - ' ' x (23 - length($tags{$n}{id})), - $n; - $comma = ","; -} -print "\n} CborKnownTags;"; -print "\n/* #define the constants so we can check with #ifdef */"; -for my $n (@tagnumbers) { - printf "#define Cbor%sTag Cbor%sTag\n", $tags{$n}{id}, $tags{$n}{id}; -} - -print "\n==== search table ====\n"; -print "struct KnownTagData { uint32_t tag; uint32_t types; };"; -printf "static const struct KnownTagData knownTagData[] = {"; -$comma = ""; -for my $n (@tagnumbers) { - my @types = @{$tags{$n}{types}}; - - my $typemask; - my $shift = 0; - for my $type (@types) { - die("Too many match types for tag $n") if $shift == 32; - my $actualtype = "Cbor${type}Type"; - $actualtype = "($actualtype+1)" if $type eq "Integer"; - $typemask .= " | " if $typemask ne ""; - $typemask .= "((uint32_t)$actualtype << $shift)" if $shift; - $typemask .= "(uint32_t)$actualtype" unless $shift; - $shift += 8; - } - $typemask = "0U" if $typemask eq ""; - - printf "%s\n { %d, %s }", $comma, $n, $typemask; - $comma = ","; -} -print "\n};"; diff --git a/lib/tinycbor/src.pri b/lib/tinycbor/src.pri deleted file mode 100644 index 01887aa49..000000000 --- a/lib/tinycbor/src.pri +++ /dev/null @@ -1,16 +0,0 @@ -SOURCES += \ - $$PWD/cborencoder.c \ - $$PWD/cborencoder_close_container_checked.c \ - $$PWD/cborerrorstrings.c \ - $$PWD/cborparser.c \ - $$PWD/cborparser_dup_string.c \ - $$PWD/cborpretty.c \ - $$PWD/cbortojson.c \ - $$PWD/cborvalidation.c \ - -HEADERS += $$PWD/cbor.h $$PWD/tinycbor-version.h - -QMAKE_CFLAGS *= $$QMAKE_CFLAGS_SPLIT_SECTIONS -QMAKE_LFLAGS *= $$QMAKE_LFLAGS_GCSECTIONS -INCLUDEPATH += $$PWD -CONFIG(release, debug|release): DEFINES += NDEBUG diff --git a/lib/tinycbor/cbor.dox b/lib/tinycbor/src/cbor.dox similarity index 100% rename from lib/tinycbor/cbor.dox rename to lib/tinycbor/src/cbor.dox diff --git a/lib/tinycbor/cbor.h b/lib/tinycbor/src/cbor.h similarity index 100% rename from lib/tinycbor/cbor.h rename to lib/tinycbor/src/cbor.h diff --git a/lib/tinycbor/cborencoder.c b/lib/tinycbor/src/cborencoder.c similarity index 100% rename from lib/tinycbor/cborencoder.c rename to lib/tinycbor/src/cborencoder.c diff --git a/lib/tinycbor/cborencoder_close_container_checked.c b/lib/tinycbor/src/cborencoder_close_container_checked.c similarity index 100% rename from lib/tinycbor/cborencoder_close_container_checked.c rename to lib/tinycbor/src/cborencoder_close_container_checked.c diff --git a/lib/tinycbor/cborerrorstrings.c b/lib/tinycbor/src/cborerrorstrings.c similarity index 100% rename from lib/tinycbor/cborerrorstrings.c rename to lib/tinycbor/src/cborerrorstrings.c diff --git a/lib/tinycbor/cborinternal_p.h b/lib/tinycbor/src/cborinternal_p.h similarity index 100% rename from lib/tinycbor/cborinternal_p.h rename to lib/tinycbor/src/cborinternal_p.h diff --git a/lib/tinycbor/cborjson.h b/lib/tinycbor/src/cborjson.h similarity index 100% rename from lib/tinycbor/cborjson.h rename to lib/tinycbor/src/cborjson.h diff --git a/lib/tinycbor/cborparser.c b/lib/tinycbor/src/cborparser.c similarity index 100% rename from lib/tinycbor/cborparser.c rename to lib/tinycbor/src/cborparser.c diff --git a/lib/tinycbor/cborparser_dup_string.c b/lib/tinycbor/src/cborparser_dup_string.c similarity index 100% rename from lib/tinycbor/cborparser_dup_string.c rename to lib/tinycbor/src/cborparser_dup_string.c diff --git a/lib/tinycbor/cborpretty.c b/lib/tinycbor/src/cborpretty.c similarity index 100% rename from lib/tinycbor/cborpretty.c rename to lib/tinycbor/src/cborpretty.c diff --git a/lib/tinycbor/cborpretty_stdio.c b/lib/tinycbor/src/cborpretty_stdio.c similarity index 100% rename from lib/tinycbor/cborpretty_stdio.c rename to lib/tinycbor/src/cborpretty_stdio.c diff --git a/lib/tinycbor/cbortojson.c b/lib/tinycbor/src/cbortojson.c similarity index 100% rename from lib/tinycbor/cbortojson.c rename to lib/tinycbor/src/cbortojson.c diff --git a/lib/tinycbor/cborvalidation.c b/lib/tinycbor/src/cborvalidation.c similarity index 100% rename from lib/tinycbor/cborvalidation.c rename to lib/tinycbor/src/cborvalidation.c diff --git a/lib/tinycbor/compilersupport_p.h b/lib/tinycbor/src/compilersupport_p.h similarity index 100% rename from lib/tinycbor/compilersupport_p.h rename to lib/tinycbor/src/compilersupport_p.h diff --git a/lib/tinycbor/open_memstream.c b/lib/tinycbor/src/open_memstream.c similarity index 100% rename from lib/tinycbor/open_memstream.c rename to lib/tinycbor/src/open_memstream.c diff --git a/lib/tinycbor/tags.txt b/lib/tinycbor/src/tags.txt similarity index 100% rename from lib/tinycbor/tags.txt rename to lib/tinycbor/src/tags.txt diff --git a/lib/tinycbor/tinycbor-version.h b/lib/tinycbor/src/tinycbor-version.h similarity index 100% rename from lib/tinycbor/tinycbor-version.h rename to lib/tinycbor/src/tinycbor-version.h diff --git a/lib/tinycbor/utf8_p.h b/lib/tinycbor/src/utf8_p.h similarity index 100% rename from lib/tinycbor/utf8_p.h rename to lib/tinycbor/src/utf8_p.h diff --git a/lib/tinycbor/tinycbor.pro b/lib/tinycbor/tinycbor.pro deleted file mode 100644 index 980dfd1f0..000000000 --- a/lib/tinycbor/tinycbor.pro +++ /dev/null @@ -1,6 +0,0 @@ -TEMPLATE = lib -CONFIG += static -CONFIG -= qt -DESTDIR = ../lib - -include(src.pri) From 5080d9df81acbce84ddc8534d33206d2a4b23973 Mon Sep 17 00:00:00 2001 From: Simone Marchisio Date: Tue, 11 Sep 2018 15:06:28 +0200 Subject: [PATCH 042/175] Updated poll and addPropertyReal --- ArduinoCloudThing.cpp | 125 +++++++++++++++++++++++------------------- ArduinoCloudThing.h | 3 +- 2 files changed, 70 insertions(+), 58 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 5e4508953..20dd1d12b 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -15,7 +15,6 @@ static void utox8(uint32_t val, char* s) { for (int i = 0; i < 8; i++) { int d = val & 0XF; val = (val >> 4); - s[7 - i] = d > 9 ? 'A' + d - 10 : '0' + d; } } @@ -42,27 +41,31 @@ ArduinoCloudThing::ArduinoCloudThing() { void ArduinoCloudThing::begin() { - status = ON; addPropertyReal(status, "status", READ); } + int ArduinoCloudThing::poll(uint8_t* data, size_t size) { // check if backing storage and cloud has diverged + // time interval may be elapsed or property may be changed int diff = 0; + // are there some changed prperies??? diff = checkNewData(); if (diff > 0) { CborError err; CborEncoder encoder, arrayEncoder; + cbor_encoder_init(&encoder, data, size, 0); // create a cbor array containing the property that should be updated. - err = cbor_encoder_create_array(&encoder, &arrayEncoder, diff); + err = cbor_encoder_create_array(&encoder, &arrayEncoder, CborIndefiniteLength); if (err) { Serial.println(cbor_error_string(err)); return -1; } + for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); // If a property should be updated and has read permission from the Cloud point of view @@ -73,35 +76,24 @@ int ArduinoCloudThing::poll(uint8_t* data, size_t size) { } err = cbor_encoder_close_container(&encoder, &arrayEncoder); - // update properties shadow values, in order to check if variable has changed since last publish + // update properties shadow values, in order to check if variable has changed since last publish for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); p->updateShadow(); } - // return the number of byte of the CBOR encoded array + + // return the number of byte of the CBOR encoded array return cbor_encoder_get_buffer_size(&encoder, data); } #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) PrintFreeRam(); #endif - // If nothing has to be sent, return diff, that is 0 in this case + // If nothing has to be sent, return diff, that is 0 in this case return diff; } -// It return the index of the property, inside the local array, with the name passed as parameter. (-1 if it does not exist.) -int ArduinoCloudThing::findPropertyByName(String &name) { - for (int i = 0; i < list.size(); i++) { - ArduinoCloudPropertyGeneric *p = list.get(i); - // Check the property existance just comparing its name with existent ones - if (p->getName() == name) { - return i; - } - } - return -1; -} - int ArduinoCloudThing::checkNewData() { int counter = 0; for (int i = 0; i < list.size(); i++) { @@ -110,12 +102,14 @@ int ArduinoCloudThing::checkNewData() { counter++; } } + // return number of props that has to be updated return counter; } ArduinoCloudPropertyGeneric* ArduinoCloudThing::exists(String &name) { for (int i = 0; i < list.size(); i++) { ArduinoCloudPropertyGeneric *p = list.get(i); + // Check the property existance just comparing its name with existent ones if (p->getName() == name) { return p; } @@ -123,66 +117,85 @@ ArduinoCloudPropertyGeneric* ArduinoCloudThing::exists(String &name) { return NULL; } +// It return the index of the property, inside the local array, with the name passed as parameter. (-1 if it does not exist.) +int ArduinoCloudThing::findPropertyByName(String &name) { + for (int i = 0; i < list.size(); i++) { + ArduinoCloudPropertyGeneric *p = list.get(i); + // Check the property existance just comparing its name with existent ones + if (p->getName() == name) { + return i; + } + } + return -1; +} + ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, String name, permissionType _permission, long seconds, void(*fn)(void), int minDelta) { if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } - ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); - list.add(thing); - thing->shadow_property = -1; - thing->minDelta = minDelta; - thing->permission = _permission; - thing->minDelta = minDelta; - thing->updatePolicy = seconds; - thing->callback = fn; - return *(reinterpret_cast(thing)); + // If a property with ythis name does not exist, create it into thing + ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, name); + // Initialize property data members, this is a friend class of ArduinoCloudProperty + propertyObj->shadow_property = -1; + propertyObj->minDelta = minDelta; + propertyObj->permission = _permission; + propertyObj->updatePolicy = seconds; + propertyObj->callback = fn; + // Add the new property to the thin properties list + list.add(propertyObj); + // Return the new property as a generic one + return *(reinterpret_cast(propertyObj)); } ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, String name, permissionType _permission, long seconds, void(*fn)(void), bool minDelta) { if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } - ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); - list.add(thing); - thing->shadow_property = !property; - thing->permission = _permission; - thing->updatePolicy = seconds; - thing->callback = fn; - return *(reinterpret_cast(thing)); + ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, name); + propertyObj->shadow_property = !property; + propertyObj->permission = _permission; + propertyObj->updatePolicy = seconds; + propertyObj->callback = fn; + list.add(propertyObj); + return *(reinterpret_cast(propertyObj)); } ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, String name, permissionType _permission, long seconds, void(*fn)(void), float minDelta) { if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } - ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); - list.add(thing); - thing->permission = _permission; - thing->minDelta = minDelta; - thing->updatePolicy = seconds; - thing->shadow_property = property - 1.0f; - thing->callback = fn; - return *(reinterpret_cast(thing)); + ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, name); + propertyObj->shadow_property = property + 0.5f; + propertyObj->permission = _permission; + propertyObj->minDelta = minDelta; + propertyObj->updatePolicy = seconds; + propertyObj->callback = fn; + list.add(propertyObj); + return *(reinterpret_cast(propertyObj)); } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(String& property, String name, permissionType _permission, long seconds, void(*fn)(void), String mindelta) { +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(String& property, String name, permissionType _permission, long seconds, void(*fn)(void), String minDelta) { if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } - ArduinoCloudProperty *thing = new ArduinoCloudProperty(property, name); - list.add(thing); - thing->shadow_property = ""; - thing->permission = _permission; - thing->updatePolicy = seconds; - thing->callback = fn; - return *(reinterpret_cast(thing)); + ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, name); + propertyObj->shadow_property = property + "x"; + propertyObj->permission = _permission; + propertyObj->updatePolicy = seconds; + propertyObj->callback = fn; + list.add(propertyObj); + return *(reinterpret_cast(propertyObj)); } -void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { + +void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { + CborError err; CborParser parser; CborValue recursedMap, propValue, dataArray; - int propId; String propType, propName; + int propId; + String propName; + propertyType propType; err = cbor_parser_init(payload, length, 0, &parser, &dataArray); if(err) { @@ -250,7 +263,7 @@ void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { // Check for the property type, write method internally check for the permission propType = property->getType(); - if (propType == "FLOAT" && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { + if (propType == FLOAT && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { if (propValue.type == CborDoubleType) { double val; // get the value of the property as a double @@ -258,7 +271,7 @@ void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; p->write((float)val); } - } else if (propType == "INT" && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { + } else if (propType == INT && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { // if no key proper key was found, do nothing if (propValue.type == CborIntegerType) { int val; @@ -272,14 +285,14 @@ void ArduinoCloudThing::decode(uint8_t * payload, size_t length) { ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; p->write((int)val); } - } else if (propType == "BOOL" && !cbor_value_map_find_value(&recursedMap, "vb", &propValue)) { + } else if (propType == BOOL && !cbor_value_map_find_value(&recursedMap, "vb", &propValue)) { if (propValue.type == CborBooleanType) { bool val; cbor_value_get_boolean(&propValue, &val); ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; p->write(val); } - } else if (propType == "STRING" && !cbor_value_map_find_value(&recursedMap, "vs", &propValue)){ + } else if (propType == STRING && !cbor_value_map_find_value(&recursedMap, "vs", &propValue)){ if (propValue.type == CborTextStringType) { char *val; size_t valSize; err = cbor_value_dup_text_string(&propValue, &val, &valSize, &propValue); diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 731a81912..560e81065 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -54,7 +54,7 @@ class ArduinoCloudThing { public: ArduinoCloudThing(); void begin(); - // overload for different type of properties + // overload, with default arguments, of different type of properties ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, int minDelta = 0); ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, bool minDelta = false); ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f); @@ -63,7 +63,6 @@ class ArduinoCloudThing { int poll(uint8_t* data, size_t size); // decode a CBOR payload received from the Cloud. void decode(uint8_t * payload, size_t length); - bool hasAllReadProperties(); private: void update(); From 892861ad5ee713b6b28c30818bc534b122118a5b Mon Sep 17 00:00:00 2001 From: Simone Marchisio Date: Tue, 11 Sep 2018 17:21:44 +0200 Subject: [PATCH 043/175] Updated int shadow_property assignment, modified test (all passed), because CBOR map has to be declared of infinite length --- ArduinoCloudThing.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 20dd1d12b..626a77435 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -60,9 +60,9 @@ int ArduinoCloudThing::poll(uint8_t* data, size_t size) { cbor_encoder_init(&encoder, data, size, 0); // create a cbor array containing the property that should be updated. - err = cbor_encoder_create_array(&encoder, &arrayEncoder, CborIndefiniteLength); + err = cbor_encoder_create_array(&encoder, &arrayEncoder, diff); if (err) { - Serial.println(cbor_error_string(err)); + //Serial.println(cbor_error_string(err)); return -1; } @@ -136,7 +136,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, S // If a property with ythis name does not exist, create it into thing ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, name); // Initialize property data members, this is a friend class of ArduinoCloudProperty - propertyObj->shadow_property = -1; + propertyObj->shadow_property = property + 1; propertyObj->minDelta = minDelta; propertyObj->permission = _permission; propertyObj->updatePolicy = seconds; From 0d1f285b0822a50b8e37c226b1a6ddae24f7fe61 Mon Sep 17 00:00:00 2001 From: Simone Marchisio Date: Wed, 12 Sep 2018 12:01:04 +0200 Subject: [PATCH 044/175] Update status property assignment and fixed tests --- ArduinoCloudThing.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 560e81065..9caa877e0 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -16,8 +16,8 @@ enum permissionType { }; enum boolStatus { - ON, - OFF, + ON = true, + OFF = false, }; // allowed property types From b5c6a5fad25b0d3d259dc1da64918044efa2b912 Mon Sep 17 00:00:00 2001 From: Simone Marchisio Date: Wed, 12 Sep 2018 15:17:07 +0200 Subject: [PATCH 045/175] Added some decode tests --- ArduinoCloudThing.cpp | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 626a77435..647a25b1c 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -214,19 +214,26 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { // parse cbor object cbor_value_enter_container(&dataArray, &recursedMap); - CborType type = cbor_value_get_type(&recursedMap); - if (type != CborMapType) { + if (cbor_value_get_type(&recursedMap) != CborMapType) { // stop the decode when 1st item thai is not a cbor map is found. - cbor_value_advance(&dataArray); + err = cbor_value_advance(&dataArray); + // avoid infinite loop if it is not possible to advance to the next array value + if (err != CborNoError) { + break; + } + // go to the next element continue; } else { - while (!cbor_value_at_end(&recursedMap)) { // if the current element is not a cbor object as expected, skip it and go ahead. if(cbor_value_get_type(&recursedMap) != CborMapType) { - cbor_value_advance(&recursedMap); + + err = cbor_value_advance(&recursedMap); + if (err != CborNoError) { + break; + } continue; } @@ -234,9 +241,13 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { // chechk for the if the a property has a name, if yes Cbor value name will properly updated cbor_value_map_find_value(&recursedMap, "n", &name); - // check if a property has a name, of string type, if not do nothin and skip curtrent property + // check if a property has a name, of string type, if not do nothing and skip curtrent property if (name.type != CborTextStringType) { - cbor_value_advance(&recursedMap); + err = cbor_value_advance(&recursedMap); + // problem to advance to the next array object + if (err != CborNoError) + break; + continue; } @@ -307,12 +318,20 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { if (property->callback != NULL) { property->callback(); } - } + } + // Continue to scan the cbor map - cbor_value_advance(&recursedMap); + err = cbor_value_advance(&recursedMap); + if (err != CborNoError) { + break; + } } } + + if (err != CborNoError) + break; // Leave the current cbor object, and advance to the next one - err = cbor_value_leave_container(&dataArray, &recursedMap); + cbor_value_leave_container(&dataArray, &recursedMap); + } } \ No newline at end of file From 8c4dd38951d0b5fbc31b38369fa095ea15d03d32 Mon Sep 17 00:00:00 2001 From: Fabrizio Mirabito Date: Tue, 25 Sep 2018 15:30:55 +0200 Subject: [PATCH 046/175] fix integer type into a double --- ArduinoCloudThing.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 647a25b1c..135cd1475 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -281,6 +281,21 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { cbor_value_get_double(&propValue, &val); ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; p->write((float)val); + } else if (propValue.type == CborIntegerType) { + int val; + cbor_value_get_int(&propValue, &val); + ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + p->write((float)val); + } else if (propValue.type == CborFloatType) { + float val; + cbor_value_get_float(&propValue, &val); + ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + p->write(val); + } else if (propValue.type == CborHalfFloatType) { + float val; + cbor_value_get_half_float(&propValue, &val); + ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + p->write(val); } } else if (propType == INT && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { // if no key proper key was found, do nothing @@ -295,6 +310,16 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { cbor_value_get_double(&propValue, &val); ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; p->write((int)val); + } else if (propValue.type == CborFloatType) { + float val; + cbor_value_get_float(&propValue, &val); + ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + p->write((int)val); + } else if (propValue.type == CborHalfFloatType) { + float val; + cbor_value_get_half_float(&propValue, &val); + ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + p->write((int)val); } } else if (propType == BOOL && !cbor_value_map_find_value(&recursedMap, "vb", &propValue)) { if (propValue.type == CborBooleanType) { From 0a6ff990195b766eecb15f4df01aa04095f7561b Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sat, 13 Oct 2018 13:14:35 +0200 Subject: [PATCH 047/175] Extracting class ArduinoCloudPropertyGeneric into separate header file --- ArduinoCloudPropertyGeneric.h | 34 +++++++++++++++++++++++++++++ ArduinoCloudThing.h | 40 ++++++----------------------------- 2 files changed, 41 insertions(+), 33 deletions(-) create mode 100644 ArduinoCloudPropertyGeneric.h diff --git a/ArduinoCloudPropertyGeneric.h b/ArduinoCloudPropertyGeneric.h new file mode 100644 index 000000000..f0aadff4d --- /dev/null +++ b/ArduinoCloudPropertyGeneric.h @@ -0,0 +1,34 @@ +#ifndef ARDUINO_CLOUD_PROPERTY_GENERIC_H_ +#define ARDUINO_CLOUD_PROPERTY_GENERIC_H_ + +#include "lib/tinycbor/cbor-lib.h" + +typedef enum { + READ = 0b01, + WRITE = 0b10, + READWRITE = READ|WRITE, +} permissionType; + +typedef enum { + INT, + FLOAT, + BOOL, + STRING +} propertyType; + +class ArduinoCloudPropertyGeneric { + public: + virtual void append(CborEncoder* encoder) = 0; + virtual String& getName() = 0; + virtual int getTag() = 0; + virtual propertyType getType() = 0; + virtual permissionType getPermission() = 0; + virtual bool newData() = 0; + virtual bool shouldBeUpdated() = 0; + virtual void updateShadow() = 0; + virtual bool canRead() = 0; + virtual void printinfo(Stream& stream) = 0; + void(*callback)(void) = NULL; +}; + +#endif /* ARDUINO_CLOUD_PROPERTY_GENERIC_H_ */ diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 9caa877e0..702b4bda6 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -1,33 +1,22 @@ -#ifndef ArduinoCloudThing_h -#define ArduinoCloudThing_h +#ifndef ARDUINO_CLOUD_THING_H_ +#define ARDUINO_CLOUD_THING_H_ #include #include -#include "lib/LinkedList/LinkedList.h" + +#include "ArduinoCloudPropertyGeneric.h" + #include "lib/tinycbor/cbor-lib.h" +#include "lib/LinkedList/LinkedList.h" // definition of the default property update policy static const int ON_CHANGE = -1; -enum permissionType { - READ = 0b01, - WRITE = 0b10, - READWRITE = READ|WRITE, -}; - enum boolStatus { ON = true, OFF = false, }; -// allowed property types -enum propertyType { - INT, - FLOAT, - BOOL, - STRING -}; - enum times { SECONDS = 1, MINUTES = 60, @@ -35,21 +24,6 @@ enum times { DAYS = 86400, }; -class ArduinoCloudPropertyGeneric { - public: - virtual void append(CborEncoder* encoder) = 0; - virtual String& getName() = 0; - virtual int getTag() = 0; - virtual propertyType getType() = 0; - virtual permissionType getPermission() = 0; - virtual bool newData() = 0; - virtual bool shouldBeUpdated() = 0; - virtual void updateShadow() = 0; - virtual bool canRead() = 0; - virtual void printinfo(Stream& stream) = 0; - void(*callback)(void) = NULL; -}; - class ArduinoCloudThing { public: ArduinoCloudThing(); @@ -237,4 +211,4 @@ inline propertyType ArduinoCloudProperty::getType() { return STRING; } -#endif +#endif /* ARDUINO_CLOUD_THING_H_ */ From 862fdd7b9dde5775cf6be5d7a7dc91c74c9a8ac0 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sat, 13 Oct 2018 13:25:22 +0200 Subject: [PATCH 048/175] Extracting ArduinoCloudProperty class into dedicated header file --- ArduinoCloudProperty.hpp | 168 +++++++++++++++++++++++++++++++++++++++ ArduinoCloudThing.h | 162 +------------------------------------ 2 files changed, 169 insertions(+), 161 deletions(-) create mode 100644 ArduinoCloudProperty.hpp diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp new file mode 100644 index 000000000..475e62ec7 --- /dev/null +++ b/ArduinoCloudProperty.hpp @@ -0,0 +1,168 @@ +#ifndef ARDUINO_CLOUD_PROPERTY_H_ +#define ARDUINO_CLOUD_PROPERTY_H_ + +#include "ArduinoCloudPropertyGeneric.h" + +class ArduinoCloudThing; /* Forward declaration to satisfy compiler for this line "friend ArduinoCloudThing" */ + +// definition of the default property update policy +static const int ON_CHANGE = -1; + +template +class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { + public: + ArduinoCloudProperty(T& _property, String _name) : + property(_property), name(_name) + { + } + + bool write(T value) { + /* permissions are intended as seen from cloud */ + if (permission & WRITE) { + property = value; + return true; + } + return false; + } + + void printinfo(Stream& stream) { + stream.println("name: " + name + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(permission)); + } + + void updateShadow() { + shadow_property = property; + } + + T read() { + /* permissions are intended as seen from cloud */ + if (permission & READ) { + return property; + } + } + + bool canRead() { + return (permission & READ); + } + + String& getName() { + return name; + } + + int getTag() { + return tag; + } + + permissionType getPermission() { + return permission; + } + + void appendValue(CborEncoder* mapEncoder); + + void append(CborEncoder* encoder) { + if (!canRead()) { + return; + } + CborEncoder mapEncoder; + cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); + if (tag != -1) { + cbor_encode_text_stringz(&mapEncoder, "t"); + cbor_encode_int(&mapEncoder, tag); + } else { + cbor_encode_text_stringz(&mapEncoder, "n"); + cbor_encode_text_stringz(&mapEncoder, name.c_str()); + } + appendValue(&mapEncoder); + cbor_encoder_close_container(encoder, &mapEncoder); + lastUpdated = millis(); + } + + bool newData() { + return (property != shadow_property); + } + + bool shouldBeUpdated() { + if (updatePolicy == ON_CHANGE) { + return newData(); + } + return ((millis() - lastUpdated) > (updatePolicy * 1000)) ; + } + + inline bool operator==(const ArduinoCloudProperty& rhs){ + return (getName() == rhs.getName()); + } + + propertyType getType(); + + protected: + T& property; + T shadow_property; + String name; + int tag = -1; + long lastUpdated = 0; + long updatePolicy = ON_CHANGE; + T minDelta; + permissionType permission = READWRITE; + static int tagIndex; + + // In this way it is possible to set shadow_property(protected), from ArduinoCloudThing, + // when a new property is added. + friend ArduinoCloudThing; +}; + +template <> +inline bool ArduinoCloudProperty::newData() { + return (property != shadow_property && abs(property - shadow_property) >= minDelta ); +} + +template <> +inline bool ArduinoCloudProperty::newData() { + return (property != shadow_property && abs(property - shadow_property) >= minDelta ); +} + +// Different appendValue function for different property typer, because the CBOR encoder and message format +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "v"); + cbor_encode_int(mapEncoder, property); +}; + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "vb"); + cbor_encode_boolean(mapEncoder, property); +}; + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "v"); + cbor_encode_float(mapEncoder, property); +}; + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "vs"); + cbor_encode_text_stringz(mapEncoder, property.c_str()); +}; + +// Return property type +template <> +inline propertyType ArduinoCloudProperty::getType() { + return INT; +} + +template <> +inline propertyType ArduinoCloudProperty::getType() { + return FLOAT; +} + +template <> +inline propertyType ArduinoCloudProperty::getType() { + return BOOL; +} + +template <> +inline propertyType ArduinoCloudProperty::getType() { + return STRING; +} + +#endif /* ARDUINO_CLOUD_PROPERTY_H_ */ diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 702b4bda6..ec4869f25 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -4,14 +4,12 @@ #include #include +#include "ArduinoCloudProperty.hpp" #include "ArduinoCloudPropertyGeneric.h" #include "lib/tinycbor/cbor-lib.h" #include "lib/LinkedList/LinkedList.h" -// definition of the default property update policy -static const int ON_CHANGE = -1; - enum boolStatus { ON = true, OFF = false, @@ -53,162 +51,4 @@ class ArduinoCloudThing { }; -// ArduinoCloudProperty declaration and definition. It inherits from ArduinoCloudPropertyGeneric interface. -template -class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { - public: - ArduinoCloudProperty(T& _property, String _name) : - property(_property), name(_name) - { - } - - bool write(T value) { - /* permissions are intended as seen from cloud */ - if (permission & WRITE) { - property = value; - return true; - } - return false; - } - - void printinfo(Stream& stream) { - stream.println("name: " + name + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(permission)); - } - - void updateShadow() { - shadow_property = property; - } - - T read() { - /* permissions are intended as seen from cloud */ - if (permission & READ) { - return property; - } - } - - bool canRead() { - return (permission & READ); - } - - String& getName() { - return name; - } - - int getTag() { - return tag; - } - - permissionType getPermission() { - return permission; - } - - void appendValue(CborEncoder* mapEncoder); - - void append(CborEncoder* encoder) { - if (!canRead()) { - return; - } - CborEncoder mapEncoder; - cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); - if (tag != -1) { - cbor_encode_text_stringz(&mapEncoder, "t"); - cbor_encode_int(&mapEncoder, tag); - } else { - cbor_encode_text_stringz(&mapEncoder, "n"); - cbor_encode_text_stringz(&mapEncoder, name.c_str()); - } - appendValue(&mapEncoder); - cbor_encoder_close_container(encoder, &mapEncoder); - lastUpdated = millis(); - } - - bool newData() { - return (property != shadow_property); - } - - bool shouldBeUpdated() { - if (updatePolicy == ON_CHANGE) { - return newData(); - } - return ((millis() - lastUpdated) > (updatePolicy * 1000)) ; - } - - inline bool operator==(const ArduinoCloudProperty& rhs){ - return (getName() == rhs.getName()); - } - - propertyType getType(); - - protected: - T& property; - T shadow_property; - String name; - int tag = -1; - long lastUpdated = 0; - long updatePolicy = ON_CHANGE; - T minDelta; - permissionType permission = READWRITE; - static int tagIndex; - - // In this way it is possible to set shadow_property(protected), from ArduinoCloudThing, - // when a new property is added. - friend ArduinoCloudThing; -}; - -template <> -inline bool ArduinoCloudProperty::newData() { - return (property != shadow_property && abs(property - shadow_property) >= minDelta ); -} - -template <> -inline bool ArduinoCloudProperty::newData() { - return (property != shadow_property && abs(property - shadow_property) >= minDelta ); -} - -// Different appendValue function for different property typer, because the CBOR encoder and message format -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "v"); - cbor_encode_int(mapEncoder, property); -}; - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "vb"); - cbor_encode_boolean(mapEncoder, property); -}; - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "v"); - cbor_encode_float(mapEncoder, property); -}; - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "vs"); - cbor_encode_text_stringz(mapEncoder, property.c_str()); -}; - -// Return property type -template <> -inline propertyType ArduinoCloudProperty::getType() { - return INT; -} - -template <> -inline propertyType ArduinoCloudProperty::getType() { - return FLOAT; -} - -template <> -inline propertyType ArduinoCloudProperty::getType() { - return BOOL; -} - -template <> -inline propertyType ArduinoCloudProperty::getType() { - return STRING; -} - #endif /* ARDUINO_CLOUD_THING_H_ */ From 292f8b24465bc5f7467b6fdc7ce1b77a35702669 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sat, 13 Oct 2018 13:53:15 +0200 Subject: [PATCH 049/175] Extracting template function implementation into .ipp file --- ArduinoCloudProperty.hpp | 156 +++++++-------------------------------- ArduinoCloudProperty.ipp | 112 ++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 131 deletions(-) create mode 100644 ArduinoCloudProperty.ipp diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 475e62ec7..e42025f67 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -11,85 +11,33 @@ static const int ON_CHANGE = -1; template class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { public: - ArduinoCloudProperty(T& _property, String _name) : - property(_property), name(_name) - { - } - - bool write(T value) { - /* permissions are intended as seen from cloud */ - if (permission & WRITE) { - property = value; - return true; - } - return false; - } - - void printinfo(Stream& stream) { - stream.println("name: " + name + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(permission)); - } - - void updateShadow() { - shadow_property = property; - } - - T read() { - /* permissions are intended as seen from cloud */ - if (permission & READ) { - return property; - } - } - - bool canRead() { - return (permission & READ); - } - - String& getName() { - return name; - } - - int getTag() { - return tag; - } - - permissionType getPermission() { - return permission; - } + ArduinoCloudProperty(T& _property, String _name); + + bool write(T value); + + void printinfo(Stream& stream); + + inline void updateShadow() { shadow_property = property; } + + T read(); + + inline bool canRead() { return (permission & READ); } + + inline String& getName() { return name; } + + inline int getTag() { return tag; } + + permissionType getPermission() { return permission; } void appendValue(CborEncoder* mapEncoder); - void append(CborEncoder* encoder) { - if (!canRead()) { - return; - } - CborEncoder mapEncoder; - cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); - if (tag != -1) { - cbor_encode_text_stringz(&mapEncoder, "t"); - cbor_encode_int(&mapEncoder, tag); - } else { - cbor_encode_text_stringz(&mapEncoder, "n"); - cbor_encode_text_stringz(&mapEncoder, name.c_str()); - } - appendValue(&mapEncoder); - cbor_encoder_close_container(encoder, &mapEncoder); - lastUpdated = millis(); - } - - bool newData() { - return (property != shadow_property); - } - - bool shouldBeUpdated() { - if (updatePolicy == ON_CHANGE) { - return newData(); - } - return ((millis() - lastUpdated) > (updatePolicy * 1000)) ; - } - - inline bool operator==(const ArduinoCloudProperty& rhs){ - return (getName() == rhs.getName()); - } + void append(CborEncoder* encoder); + + inline bool newData() { return (property != shadow_property); } + + bool shouldBeUpdated(); + + inline bool operator == (const ArduinoCloudProperty& rhs) { return (getName() == rhs.getName()); } propertyType getType(); @@ -109,60 +57,6 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { friend ArduinoCloudThing; }; -template <> -inline bool ArduinoCloudProperty::newData() { - return (property != shadow_property && abs(property - shadow_property) >= minDelta ); -} - -template <> -inline bool ArduinoCloudProperty::newData() { - return (property != shadow_property && abs(property - shadow_property) >= minDelta ); -} - -// Different appendValue function for different property typer, because the CBOR encoder and message format -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "v"); - cbor_encode_int(mapEncoder, property); -}; - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "vb"); - cbor_encode_boolean(mapEncoder, property); -}; - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "v"); - cbor_encode_float(mapEncoder, property); -}; - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "vs"); - cbor_encode_text_stringz(mapEncoder, property.c_str()); -}; - -// Return property type -template <> -inline propertyType ArduinoCloudProperty::getType() { - return INT; -} - -template <> -inline propertyType ArduinoCloudProperty::getType() { - return FLOAT; -} - -template <> -inline propertyType ArduinoCloudProperty::getType() { - return BOOL; -} - -template <> -inline propertyType ArduinoCloudProperty::getType() { - return STRING; -} +#include "ArduinoCloudProperty.ipp" #endif /* ARDUINO_CLOUD_PROPERTY_H_ */ diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp new file mode 100644 index 000000000..feef7e92b --- /dev/null +++ b/ArduinoCloudProperty.ipp @@ -0,0 +1,112 @@ +template +ArduinoCloudProperty::ArduinoCloudProperty(T& _property, String _name) : + property(_property), name(_name) + { + } + +template +bool ArduinoCloudProperty::write(T value) { + /* permissions are intended as seen from cloud */ + if (permission & WRITE) { + property = value; + return true; + } + return false; +} + +template +void ArduinoCloudProperty::printinfo(Stream& stream) { + stream.println("name: " + name + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(permission)); +} + +template +T ArduinoCloudProperty::read() { + /* permissions are intended as seen from cloud */ + if (permission & READ) { + return property; + } +} + +template +void ArduinoCloudProperty::append(CborEncoder* encoder) { + if (!canRead()) { + return; + } + CborEncoder mapEncoder; + cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); + if (tag != -1) { + cbor_encode_text_stringz(&mapEncoder, "t"); + cbor_encode_int(&mapEncoder, tag); + } + else { + cbor_encode_text_stringz(&mapEncoder, "n"); + cbor_encode_text_stringz(&mapEncoder, name.c_str()); + } + appendValue(&mapEncoder); + cbor_encoder_close_container(encoder, &mapEncoder); + lastUpdated = millis(); +} + +template +bool ArduinoCloudProperty::shouldBeUpdated() { + if (updatePolicy == ON_CHANGE) { + return newData(); + } + return ((millis() - lastUpdated) > (updatePolicy * 1000)) ; +} + +template <> +inline bool ArduinoCloudProperty::newData() { + return (property != shadow_property && abs(property - shadow_property) >= minDelta ); +} + +template <> +inline bool ArduinoCloudProperty::newData() { + return (property != shadow_property && abs(property - shadow_property) >= minDelta ); +} + +// Different appendValue function for different property typer, because the CBOR encoder and message format +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "v"); + cbor_encode_int(mapEncoder, property); +}; + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "vb"); + cbor_encode_boolean(mapEncoder, property); +}; + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "v"); + cbor_encode_float(mapEncoder, property); +}; + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { + cbor_encode_text_stringz(mapEncoder, "vs"); + cbor_encode_text_stringz(mapEncoder, property.c_str()); +}; + +// Return property type +template <> +inline propertyType ArduinoCloudProperty::getType() { + return INT; +} + +template <> +inline propertyType ArduinoCloudProperty::getType() { + return FLOAT; +} + +template <> +inline propertyType ArduinoCloudProperty::getType() { + return BOOL; +} + +template <> +inline propertyType ArduinoCloudProperty::getType() { + return STRING; +} From 47837686551d847ac700861b019c769aa30e4cdc Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sat, 13 Oct 2018 14:01:47 +0200 Subject: [PATCH 050/175] Rearranging function order a bit so that functions o similiar functionality are grouped together --- ArduinoCloudProperty.hpp | 24 ++++++++-------------- ArduinoCloudProperty.ipp | 44 ++++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 37 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index e42025f67..67d0f7938 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -14,33 +14,27 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { ArduinoCloudProperty(T& _property, String _name); bool write(T value); + T read (); void printinfo(Stream& stream); - inline void updateShadow() { shadow_property = property; } + inline void updateShadow () { shadow_property = property; } + bool shouldBeUpdated(); + inline bool newData () { return (property != shadow_property); } - T read(); inline bool canRead() { return (permission & READ); } - inline String& getName() { return name; } - - inline int getTag() { return tag; } - - permissionType getPermission() { return permission; } + inline String& getName () { return name; } + inline int getTag () { return tag; } + inline permissionType getPermission() { return permission; } + propertyType getType (); + void append (CborEncoder* encoder); void appendValue(CborEncoder* mapEncoder); - void append(CborEncoder* encoder); - - inline bool newData() { return (property != shadow_property); } - - bool shouldBeUpdated(); - inline bool operator == (const ArduinoCloudProperty& rhs) { return (getName() == rhs.getName()); } - propertyType getType(); - protected: T& property; T shadow_property; diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index feef7e92b..744e3fa56 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -14,11 +14,6 @@ bool ArduinoCloudProperty::write(T value) { return false; } -template -void ArduinoCloudProperty::printinfo(Stream& stream) { - stream.println("name: " + name + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(permission)); -} - template T ArduinoCloudProperty::read() { /* permissions are intended as seen from cloud */ @@ -28,23 +23,8 @@ T ArduinoCloudProperty::read() { } template -void ArduinoCloudProperty::append(CborEncoder* encoder) { - if (!canRead()) { - return; - } - CborEncoder mapEncoder; - cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); - if (tag != -1) { - cbor_encode_text_stringz(&mapEncoder, "t"); - cbor_encode_int(&mapEncoder, tag); - } - else { - cbor_encode_text_stringz(&mapEncoder, "n"); - cbor_encode_text_stringz(&mapEncoder, name.c_str()); - } - appendValue(&mapEncoder); - cbor_encoder_close_container(encoder, &mapEncoder); - lastUpdated = millis(); +void ArduinoCloudProperty::printinfo(Stream& stream) { + stream.println("name: " + name + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(permission)); } template @@ -65,6 +45,26 @@ inline bool ArduinoCloudProperty::newData() { return (property != shadow_property && abs(property - shadow_property) >= minDelta ); } +template +void ArduinoCloudProperty::append(CborEncoder* encoder) { + if (!canRead()) { + return; + } + CborEncoder mapEncoder; + cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); + if (tag != -1) { + cbor_encode_text_stringz(&mapEncoder, "t"); + cbor_encode_int(&mapEncoder, tag); + } + else { + cbor_encode_text_stringz(&mapEncoder, "n"); + cbor_encode_text_stringz(&mapEncoder, name.c_str()); + } + appendValue(&mapEncoder); + cbor_encoder_close_container(encoder, &mapEncoder); + lastUpdated = millis(); +} + // Different appendValue function for different property typer, because the CBOR encoder and message format template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { From 7583551ae7b60e352e30efac7358d2c61d4b76df Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sat, 13 Oct 2018 14:08:53 +0200 Subject: [PATCH 051/175] Using functions canRead/canWrite inside template functions --- ArduinoCloudProperty.hpp | 3 ++- ArduinoCloudProperty.ipp | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 67d0f7938..47f3c9c96 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -23,7 +23,8 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { inline bool newData () { return (property != shadow_property); } - inline bool canRead() { return (permission & READ); } + inline bool canWrite() { return (permission & WRITE); } + inline bool canRead () { return (permission & READ); } inline String& getName () { return name; } inline int getTag () { return tag; } diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 744e3fa56..653e9c7bb 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -7,7 +7,7 @@ ArduinoCloudProperty::ArduinoCloudProperty(T& _property, String _name) : template bool ArduinoCloudProperty::write(T value) { /* permissions are intended as seen from cloud */ - if (permission & WRITE) { + if (canWrite()) { property = value; return true; } @@ -17,9 +17,13 @@ bool ArduinoCloudProperty::write(T value) { template T ArduinoCloudProperty::read() { /* permissions are intended as seen from cloud */ - if (permission & READ) { + if (canRead()) { return property; } + /* FIXME: What happens if we can not read? Compiler should complain there + * because there is no return value in case of the canRead() evaluating + * to false + */ } template From 727d7219850ed182099dcf389adabc6491851b4c Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sat, 13 Oct 2018 14:25:57 +0200 Subject: [PATCH 052/175] Using keyword 'override' with virtual functions in order to allow the compiler to check if all virtual functions are indeed truly overwritten in the derived class ArduinoCloudProperty --- ArduinoCloudProperty.hpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 47f3c9c96..1813da34e 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -11,28 +11,28 @@ static const int ON_CHANGE = -1; template class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { public: + ArduinoCloudProperty(T& _property, String _name); bool write(T value); T read (); - void printinfo(Stream& stream); - - inline void updateShadow () { shadow_property = property; } - bool shouldBeUpdated(); - inline bool newData () { return (property != shadow_property); } + virtual void printinfo(Stream& stream) override; + virtual void updateShadow () override { shadow_property = property; } + virtual bool shouldBeUpdated() override; + virtual bool newData () override { return (property != shadow_property); } - inline bool canWrite() { return (permission & WRITE); } - inline bool canRead () { return (permission & READ); } + inline bool canWrite() { return (permission & WRITE); } + virtual bool canRead () override { return (permission & READ); } - inline String& getName () { return name; } - inline int getTag () { return tag; } - inline permissionType getPermission() { return permission; } - propertyType getType (); + virtual String& getName () override { return name; } + virtual int getTag () override { return tag; } + virtual permissionType getPermission() override { return permission; } + virtual propertyType getType () override; - void append (CborEncoder* encoder); - void appendValue(CborEncoder* mapEncoder); + virtual void append (CborEncoder* encoder) override; + void appendValue(CborEncoder* mapEncoder); inline bool operator == (const ArduinoCloudProperty& rhs) { return (getName() == rhs.getName()); } From baa438eedc8ffa7526863e24ad90b09cf7494e5d Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sat, 13 Oct 2018 14:44:54 +0200 Subject: [PATCH 053/175] Adding const qualifiers in order to allow the compiler to identify methods where internal state is modified although that should not happen --- ArduinoCloudProperty.hpp | 20 ++++++++++---------- ArduinoCloudProperty.ipp | 14 +++++++------- ArduinoCloudPropertyGeneric.h | 14 +++++++------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 1813da34e..1028ca21e 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -19,22 +19,22 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { virtual void printinfo(Stream& stream) override; - virtual void updateShadow () override { shadow_property = property; } - virtual bool shouldBeUpdated() override; - virtual bool newData () override { return (property != shadow_property); } + virtual void updateShadow () override { shadow_property = property; } + virtual bool shouldBeUpdated() const override; + virtual bool newData () const override { return (property != shadow_property); }; - inline bool canWrite() { return (permission & WRITE); } - virtual bool canRead () override { return (permission & READ); } + inline bool canWrite() const { return (permission & WRITE); } + virtual bool canRead () const override { return (permission & READ); } - virtual String& getName () override { return name; } - virtual int getTag () override { return tag; } - virtual permissionType getPermission() override { return permission; } - virtual propertyType getType () override; + virtual String const & getName () const override { return name; } + virtual int getTag () const override { return tag; } + virtual permissionType getPermission() const override { return permission; } + virtual propertyType getType () const override; virtual void append (CborEncoder* encoder) override; void appendValue(CborEncoder* mapEncoder); - inline bool operator == (const ArduinoCloudProperty& rhs) { return (getName() == rhs.getName()); } + inline bool operator == (ArduinoCloudProperty const & rhs) const { return (getName() == rhs.getName()); } protected: T& property; diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 653e9c7bb..8d347ece6 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -32,7 +32,7 @@ void ArduinoCloudProperty::printinfo(Stream& stream) { } template -bool ArduinoCloudProperty::shouldBeUpdated() { +bool ArduinoCloudProperty::shouldBeUpdated() const { if (updatePolicy == ON_CHANGE) { return newData(); } @@ -40,12 +40,12 @@ bool ArduinoCloudProperty::shouldBeUpdated() { } template <> -inline bool ArduinoCloudProperty::newData() { +inline bool ArduinoCloudProperty::newData() const { return (property != shadow_property && abs(property - shadow_property) >= minDelta ); } template <> -inline bool ArduinoCloudProperty::newData() { +inline bool ArduinoCloudProperty::newData() const { return (property != shadow_property && abs(property - shadow_property) >= minDelta ); } @@ -96,21 +96,21 @@ inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { // Return property type template <> -inline propertyType ArduinoCloudProperty::getType() { +inline propertyType ArduinoCloudProperty::getType() const { return INT; } template <> -inline propertyType ArduinoCloudProperty::getType() { +inline propertyType ArduinoCloudProperty::getType() const { return FLOAT; } template <> -inline propertyType ArduinoCloudProperty::getType() { +inline propertyType ArduinoCloudProperty::getType() const { return BOOL; } template <> -inline propertyType ArduinoCloudProperty::getType() { +inline propertyType ArduinoCloudProperty::getType() const { return STRING; } diff --git a/ArduinoCloudPropertyGeneric.h b/ArduinoCloudPropertyGeneric.h index f0aadff4d..a9ae952f8 100644 --- a/ArduinoCloudPropertyGeneric.h +++ b/ArduinoCloudPropertyGeneric.h @@ -19,14 +19,14 @@ typedef enum { class ArduinoCloudPropertyGeneric { public: virtual void append(CborEncoder* encoder) = 0; - virtual String& getName() = 0; - virtual int getTag() = 0; - virtual propertyType getType() = 0; - virtual permissionType getPermission() = 0; - virtual bool newData() = 0; - virtual bool shouldBeUpdated() = 0; + virtual String const & getName() const = 0; + virtual int getTag() const = 0; + virtual propertyType getType() const = 0; + virtual permissionType getPermission() const = 0; + virtual bool newData() const = 0; + virtual bool shouldBeUpdated() const = 0; virtual void updateShadow() = 0; - virtual bool canRead() = 0; + virtual bool canRead() const = 0; virtual void printinfo(Stream& stream) = 0; void(*callback)(void) = NULL; }; From 6b6f79a01d72fc9033fd6f0143b0c84d7012bf97 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sat, 13 Oct 2018 18:06:24 +0200 Subject: [PATCH 054/175] Initialisation of all properties take place within ctor class ArduinoCloudThing does not longer need to be a friend --- ArduinoCloudProperty.hpp | 17 +++++------- ArduinoCloudProperty.ipp | 14 +++++++--- ArduinoCloudPropertyGeneric.h | 6 +++++ ArduinoCloudThing.cpp | 49 +++++++++++------------------------ 4 files changed, 37 insertions(+), 49 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 1028ca21e..0137c5d5d 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -3,8 +3,6 @@ #include "ArduinoCloudPropertyGeneric.h" -class ArduinoCloudThing; /* Forward declaration to satisfy compiler for this line "friend ArduinoCloudThing" */ - // definition of the default property update policy static const int ON_CHANGE = -1; @@ -12,7 +10,7 @@ template class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { public: - ArduinoCloudProperty(T& _property, String _name); + ArduinoCloudProperty(T& _property, T const _shadow_property, String const & _name, T const _minDelta, permissionType const _permission, long const _updatePolicy, void(*fn)(void)); bool write(T value); T read (); @@ -36,20 +34,17 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { inline bool operator == (ArduinoCloudProperty const & rhs) const { return (getName() == rhs.getName()); } - protected: + private: + T& property; T shadow_property; String name; - int tag = -1; - long lastUpdated = 0; - long updatePolicy = ON_CHANGE; T minDelta; permissionType permission = READWRITE; - static int tagIndex; + long updatePolicy = ON_CHANGE; + int tag = -1; + long lastUpdated = 0; - // In this way it is possible to set shadow_property(protected), from ArduinoCloudThing, - // when a new property is added. - friend ArduinoCloudThing; }; #include "ArduinoCloudProperty.ipp" diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 8d347ece6..3b9ccb26c 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -1,8 +1,14 @@ template -ArduinoCloudProperty::ArduinoCloudProperty(T& _property, String _name) : - property(_property), name(_name) - { - } +ArduinoCloudProperty::ArduinoCloudProperty(T& _property, T const _shadow_property, String const & _name, T const _minDelta, permissionType const _permission, long const _updatePolicy, void(*fn)(void)) : + ArduinoCloudPropertyGeneric(fn), + property (_property ), + shadow_property(_shadow_property), + name (_name ), + minDelta (_minDelta ), + permission (_permission ), + updatePolicy (_updatePolicy ) +{ +} template bool ArduinoCloudProperty::write(T value) { diff --git a/ArduinoCloudPropertyGeneric.h b/ArduinoCloudPropertyGeneric.h index a9ae952f8..5a9f19b86 100644 --- a/ArduinoCloudPropertyGeneric.h +++ b/ArduinoCloudPropertyGeneric.h @@ -18,6 +18,11 @@ typedef enum { class ArduinoCloudPropertyGeneric { public: + + ArduinoCloudPropertyGeneric(void(*fn)(void)) : callback(fn) + { + } + virtual void append(CborEncoder* encoder) = 0; virtual String const & getName() const = 0; virtual int getTag() const = 0; @@ -28,6 +33,7 @@ class ArduinoCloudPropertyGeneric { virtual void updateShadow() = 0; virtual bool canRead() const = 0; virtual void printinfo(Stream& stream) = 0; + void(*callback)(void) = NULL; }; diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 135cd1475..def38a12f 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -134,13 +134,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, S return *p; } // If a property with ythis name does not exist, create it into thing - ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, name); - // Initialize property data members, this is a friend class of ArduinoCloudProperty - propertyObj->shadow_property = property + 1; - propertyObj->minDelta = minDelta; - propertyObj->permission = _permission; - propertyObj->updatePolicy = seconds; - propertyObj->callback = fn; + ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, property + 1, name, minDelta, _permission, seconds, fn); // Add the new property to the thin properties list list.add(propertyObj); // Return the new property as a generic one @@ -151,11 +145,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } - ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, name); - propertyObj->shadow_property = !property; - propertyObj->permission = _permission; - propertyObj->updatePolicy = seconds; - propertyObj->callback = fn; + ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, !property, name, false /* = minDelta */, _permission, seconds, fn); list.add(propertyObj); return *(reinterpret_cast(propertyObj)); } @@ -164,12 +154,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } - ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, name); - propertyObj->shadow_property = property + 0.5f; - propertyObj->permission = _permission; - propertyObj->minDelta = minDelta; - propertyObj->updatePolicy = seconds; - propertyObj->callback = fn; + ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, property + 0.5f, name, minDelta, _permission, seconds, fn); list.add(propertyObj); return *(reinterpret_cast(propertyObj)); } @@ -178,18 +163,14 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(String& property if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } - ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, name); - propertyObj->shadow_property = property + "x"; - propertyObj->permission = _permission; - propertyObj->updatePolicy = seconds; - propertyObj->callback = fn; + ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, property + "x", name, "" /* = minDelta */, _permission, seconds, fn); list.add(propertyObj); return *(reinterpret_cast(propertyObj)); } void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { - + CborError err; CborParser parser; CborValue recursedMap, propValue, dataArray; @@ -209,7 +190,7 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { return; // main loop through the cbor array elements - while (!cbor_value_at_end(&dataArray)) { + while (!cbor_value_at_end(&dataArray)) { // parse cbor object cbor_value_enter_container(&dataArray, &recursedMap); @@ -247,10 +228,10 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { // problem to advance to the next array object if (err != CborNoError) break; - + continue; } - + // get the property name from cbor map as char* string char *nameVal; size_t nameValSize; err = cbor_value_dup_text_string(&name, &nameVal, &nameValSize, NULL); @@ -269,11 +250,11 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { cbor_value_advance(&recursedMap); continue; } - + ArduinoCloudPropertyGeneric* property = list.get(propId); // Check for the property type, write method internally check for the permission propType = property->getType(); - + if (propType == FLOAT && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { if (propValue.type == CborDoubleType) { double val; @@ -328,7 +309,7 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; p->write(val); } - } else if (propType == STRING && !cbor_value_map_find_value(&recursedMap, "vs", &propValue)){ + } else if (propType == STRING && !cbor_value_map_find_value(&recursedMap, "vs", &propValue)){ if (propValue.type == CborTextStringType) { char *val; size_t valSize; err = cbor_value_dup_text_string(&propValue, &val, &valSize, &propValue); @@ -350,13 +331,13 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { if (err != CborNoError) { break; } - } + } } - if (err != CborNoError) + if (err != CborNoError) break; // Leave the current cbor object, and advance to the next one cbor_value_leave_container(&dataArray, &recursedMap); - + } -} \ No newline at end of file +} From 78836faefef9342ea77e41c73809e8d6e0cfe512 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sun, 14 Oct 2018 13:17:53 +0200 Subject: [PATCH 055/175] Moving common class members up into the base class ArduinoCloudPropertyGeneric --- ArduinoCloudProperty.hpp | 15 +++----------- ArduinoCloudProperty.ipp | 38 +++++++---------------------------- ArduinoCloudPropertyGeneric.h | 31 ++++++++++++++++++++++++---- ArduinoCloudThing.cpp | 8 ++++---- 4 files changed, 41 insertions(+), 51 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 0137c5d5d..aa9443beb 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -3,14 +3,11 @@ #include "ArduinoCloudPropertyGeneric.h" -// definition of the default property update policy -static const int ON_CHANGE = -1; - template class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { public: - ArduinoCloudProperty(T& _property, T const _shadow_property, String const & _name, T const _minDelta, permissionType const _permission, long const _updatePolicy, void(*fn)(void)); + ArduinoCloudProperty(T& _property, T const _shadow_property, String const & _name, propertyType const property_type, T const _minDelta, permissionType const _permission, long const _updatePolicy, void(*fn)(void)); bool write(T value); T read (); @@ -21,13 +18,10 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { virtual bool shouldBeUpdated() const override; virtual bool newData () const override { return (property != shadow_property); }; - inline bool canWrite() const { return (permission & WRITE); } - virtual bool canRead () const override { return (permission & READ); } + inline bool canWrite() const { return (getPermission() & WRITE); } + virtual bool canRead () const override { return (getPermission() & READ); } - virtual String const & getName () const override { return name; } virtual int getTag () const override { return tag; } - virtual permissionType getPermission() const override { return permission; } - virtual propertyType getType () const override; virtual void append (CborEncoder* encoder) override; void appendValue(CborEncoder* mapEncoder); @@ -38,10 +32,7 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { T& property; T shadow_property; - String name; T minDelta; - permissionType permission = READWRITE; - long updatePolicy = ON_CHANGE; int tag = -1; long lastUpdated = 0; diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 3b9ccb26c..5b10d34e9 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -1,12 +1,9 @@ template -ArduinoCloudProperty::ArduinoCloudProperty(T& _property, T const _shadow_property, String const & _name, T const _minDelta, permissionType const _permission, long const _updatePolicy, void(*fn)(void)) : - ArduinoCloudPropertyGeneric(fn), +ArduinoCloudProperty::ArduinoCloudProperty(T& _property, T const _shadow_property, String const & _name, propertyType const property_type, T const _minDelta, permissionType const _permission, long const _updatePolicy, void(*fn)(void)) : + ArduinoCloudPropertyGeneric(_name, property_type, _permission, _updatePolicy, fn), property (_property ), shadow_property(_shadow_property), - name (_name ), - minDelta (_minDelta ), - permission (_permission ), - updatePolicy (_updatePolicy ) + minDelta (_minDelta ) { } @@ -34,15 +31,15 @@ T ArduinoCloudProperty::read() { template void ArduinoCloudProperty::printinfo(Stream& stream) { - stream.println("name: " + name + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(permission)); + stream.println("name: " + getName() + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(getPermission())); } template bool ArduinoCloudProperty::shouldBeUpdated() const { - if (updatePolicy == ON_CHANGE) { + if (getUpdatePolicy() == ON_CHANGE) { return newData(); } - return ((millis() - lastUpdated) > (updatePolicy * 1000)) ; + return ((millis() - lastUpdated) > (getUpdatePolicy() * 1000)) ; } template <> @@ -68,7 +65,7 @@ void ArduinoCloudProperty::append(CborEncoder* encoder) { } else { cbor_encode_text_stringz(&mapEncoder, "n"); - cbor_encode_text_stringz(&mapEncoder, name.c_str()); + cbor_encode_text_stringz(&mapEncoder, getName().c_str()); } appendValue(&mapEncoder); cbor_encoder_close_container(encoder, &mapEncoder); @@ -99,24 +96,3 @@ inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { cbor_encode_text_stringz(mapEncoder, "vs"); cbor_encode_text_stringz(mapEncoder, property.c_str()); }; - -// Return property type -template <> -inline propertyType ArduinoCloudProperty::getType() const { - return INT; -} - -template <> -inline propertyType ArduinoCloudProperty::getType() const { - return FLOAT; -} - -template <> -inline propertyType ArduinoCloudProperty::getType() const { - return BOOL; -} - -template <> -inline propertyType ArduinoCloudProperty::getType() const { - return STRING; -} diff --git a/ArduinoCloudPropertyGeneric.h b/ArduinoCloudPropertyGeneric.h index 5a9f19b86..73e3c8275 100644 --- a/ArduinoCloudPropertyGeneric.h +++ b/ArduinoCloudPropertyGeneric.h @@ -16,18 +16,33 @@ typedef enum { STRING } propertyType; +// definition of the default property update policy +static const int ON_CHANGE = -1; + class ArduinoCloudPropertyGeneric { public: - ArduinoCloudPropertyGeneric(void(*fn)(void)) : callback(fn) + ArduinoCloudPropertyGeneric(String const & name, + propertyType const property_type, + permissionType const permission, + long const update_policy, + void(*fn)(void)) + : _name(name), + _property_type(property_type), + _permission(permission), + _update_policy(_update_policy), + callback(fn) { } + inline String const & getName () const { return _name; } + inline propertyType getType () const { return _property_type; } + inline permissionType getPermission () const { return _permission; } + inline long getUpdatePolicy() const { return _update_policy; } + + virtual void append(CborEncoder* encoder) = 0; - virtual String const & getName() const = 0; virtual int getTag() const = 0; - virtual propertyType getType() const = 0; - virtual permissionType getPermission() const = 0; virtual bool newData() const = 0; virtual bool shouldBeUpdated() const = 0; virtual void updateShadow() = 0; @@ -35,6 +50,14 @@ class ArduinoCloudPropertyGeneric { virtual void printinfo(Stream& stream) = 0; void(*callback)(void) = NULL; + + private: + + String _name; + propertyType _property_type; + permissionType _permission; + long _update_policy; + }; #endif /* ARDUINO_CLOUD_PROPERTY_GENERIC_H_ */ diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index def38a12f..090c0abf8 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -134,7 +134,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, S return *p; } // If a property with ythis name does not exist, create it into thing - ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, property + 1, name, minDelta, _permission, seconds, fn); + ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, property + 1, name, INT, minDelta, _permission, seconds, fn); // Add the new property to the thin properties list list.add(propertyObj); // Return the new property as a generic one @@ -145,7 +145,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } - ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, !property, name, false /* = minDelta */, _permission, seconds, fn); + ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, !property, name, BOOL, false /* = minDelta */, _permission, seconds, fn); list.add(propertyObj); return *(reinterpret_cast(propertyObj)); } @@ -154,7 +154,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } - ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, property + 0.5f, name, minDelta, _permission, seconds, fn); + ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, property + 0.5f, name, FLOAT, minDelta, _permission, seconds, fn); list.add(propertyObj); return *(reinterpret_cast(propertyObj)); } @@ -163,7 +163,7 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(String& property if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } - ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, property + "x", name, "" /* = minDelta */, _permission, seconds, fn); + ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, property + "x", name, STRING, "" /* = minDelta */, _permission, seconds, fn); list.add(propertyObj); return *(reinterpret_cast(propertyObj)); } From 04a1f583bb95863b5a7fb72b499f5d63810ff4c3 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sun, 14 Oct 2018 13:20:25 +0200 Subject: [PATCH 056/175] Moving functions canWrite and canRead up into the base class - they do not need to be virtual --- ArduinoCloudProperty.hpp | 3 --- ArduinoCloudPropertyGeneric.h | 7 +++++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index aa9443beb..41fe7a7c6 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -18,9 +18,6 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { virtual bool shouldBeUpdated() const override; virtual bool newData () const override { return (property != shadow_property); }; - inline bool canWrite() const { return (getPermission() & WRITE); } - virtual bool canRead () const override { return (getPermission() & READ); } - virtual int getTag () const override { return tag; } virtual void append (CborEncoder* encoder) override; diff --git a/ArduinoCloudPropertyGeneric.h b/ArduinoCloudPropertyGeneric.h index 73e3c8275..aa517d544 100644 --- a/ArduinoCloudPropertyGeneric.h +++ b/ArduinoCloudPropertyGeneric.h @@ -6,7 +6,7 @@ typedef enum { READ = 0b01, WRITE = 0b10, - READWRITE = READ|WRITE, + READWRITE = READ | WRITE, } permissionType; typedef enum { @@ -40,13 +40,16 @@ class ArduinoCloudPropertyGeneric { inline permissionType getPermission () const { return _permission; } inline long getUpdatePolicy() const { return _update_policy; } + inline bool canWrite () const { return (_permission & WRITE); } + virtual bool canRead () const { return (_permission & READ); } + + virtual void append(CborEncoder* encoder) = 0; virtual int getTag() const = 0; virtual bool newData() const = 0; virtual bool shouldBeUpdated() const = 0; virtual void updateShadow() = 0; - virtual bool canRead() const = 0; virtual void printinfo(Stream& stream) = 0; void(*callback)(void) = NULL; From ff820ec9f1b76b2208d868303342cdab5aa65e2f Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sun, 14 Oct 2018 13:23:33 +0200 Subject: [PATCH 057/175] Moving comparison operator up in base class module and out of the class - since the operator can access the relevant class content via provided interface functions there is no need to be a member of the class --- ArduinoCloudProperty.hpp | 2 -- ArduinoCloudPropertyGeneric.h | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 41fe7a7c6..01015411b 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -23,8 +23,6 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { virtual void append (CborEncoder* encoder) override; void appendValue(CborEncoder* mapEncoder); - inline bool operator == (ArduinoCloudProperty const & rhs) const { return (getName() == rhs.getName()); } - private: T& property; diff --git a/ArduinoCloudPropertyGeneric.h b/ArduinoCloudPropertyGeneric.h index aa517d544..9b1117611 100644 --- a/ArduinoCloudPropertyGeneric.h +++ b/ArduinoCloudPropertyGeneric.h @@ -43,8 +43,6 @@ class ArduinoCloudPropertyGeneric { inline bool canWrite () const { return (_permission & WRITE); } virtual bool canRead () const { return (_permission & READ); } - - virtual void append(CborEncoder* encoder) = 0; virtual int getTag() const = 0; virtual bool newData() const = 0; @@ -63,4 +61,6 @@ class ArduinoCloudPropertyGeneric { }; +inline bool operator == (ArduinoCloudPropertyGeneric const & lhs, ArduinoCloudPropertyGeneric const & rhs) { return (lhs.getName() == rhs.getName()); } + #endif /* ARDUINO_CLOUD_PROPERTY_GENERIC_H_ */ From 9583053f9165361ea488472b800f9267906b42f2 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sun, 14 Oct 2018 13:31:39 +0200 Subject: [PATCH 058/175] Moving function shouldBeUpdated as well as member variable _last_updated up in base class --- ArduinoCloudProperty.hpp | 3 +-- ArduinoCloudProperty.ipp | 10 +--------- ArduinoCloudPropertyGeneric.h | 12 +++++++++++- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 01015411b..d78b59068 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -15,7 +15,6 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { virtual void printinfo(Stream& stream) override; virtual void updateShadow () override { shadow_property = property; } - virtual bool shouldBeUpdated() const override; virtual bool newData () const override { return (property != shadow_property); }; virtual int getTag () const override { return tag; } @@ -29,7 +28,7 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { T shadow_property; T minDelta; int tag = -1; - long lastUpdated = 0; + }; diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 5b10d34e9..565dd1583 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -34,14 +34,6 @@ void ArduinoCloudProperty::printinfo(Stream& stream) { stream.println("name: " + getName() + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(getPermission())); } -template -bool ArduinoCloudProperty::shouldBeUpdated() const { - if (getUpdatePolicy() == ON_CHANGE) { - return newData(); - } - return ((millis() - lastUpdated) > (getUpdatePolicy() * 1000)) ; -} - template <> inline bool ArduinoCloudProperty::newData() const { return (property != shadow_property && abs(property - shadow_property) >= minDelta ); @@ -69,7 +61,7 @@ void ArduinoCloudProperty::append(CborEncoder* encoder) { } appendValue(&mapEncoder); cbor_encoder_close_container(encoder, &mapEncoder); - lastUpdated = millis(); + _last_updated = millis(); } // Different appendValue function for different property typer, because the CBOR encoder and message format diff --git a/ArduinoCloudPropertyGeneric.h b/ArduinoCloudPropertyGeneric.h index 9b1117611..13aff2633 100644 --- a/ArduinoCloudPropertyGeneric.h +++ b/ArduinoCloudPropertyGeneric.h @@ -31,6 +31,7 @@ class ArduinoCloudPropertyGeneric { _property_type(property_type), _permission(permission), _update_policy(_update_policy), + _last_updated(0), callback(fn) { } @@ -46,10 +47,16 @@ class ArduinoCloudPropertyGeneric { virtual void append(CborEncoder* encoder) = 0; virtual int getTag() const = 0; virtual bool newData() const = 0; - virtual bool shouldBeUpdated() const = 0; virtual void updateShadow() = 0; virtual void printinfo(Stream& stream) = 0; + bool shouldBeUpdated() + { + if (_update_policy == ON_CHANGE) return newData(); + else return ((millis() - _last_updated) > (_update_policy * 1000)); + } + + void(*callback)(void) = NULL; private: @@ -59,6 +66,9 @@ class ArduinoCloudPropertyGeneric { permissionType _permission; long _update_policy; + protected: + long _last_updated; + }; inline bool operator == (ArduinoCloudPropertyGeneric const & lhs, ArduinoCloudPropertyGeneric const & rhs) { return (lhs.getName() == rhs.getName()); } From a1da1a6ef40c742c3b375f8ee203835eec91c465 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sun, 14 Oct 2018 16:23:30 +0200 Subject: [PATCH 059/175] Moving function 'append' to base class and implementing function appendValue as virtual function instead. Depending on the desired property type the correct appendValue function is called - see template method pattern --- ArduinoCloudProperty.hpp | 11 ++--------- ArduinoCloudProperty.ipp | 20 -------------------- ArduinoCloudPropertyGeneric.h | 26 ++++++++++++++++++++++++-- 3 files changed, 26 insertions(+), 31 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index d78b59068..ca53b5d4d 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -14,22 +14,15 @@ class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { virtual void printinfo(Stream& stream) override; - virtual void updateShadow () override { shadow_property = property; } + virtual void updateShadow () override { shadow_property = property; } virtual bool newData () const override { return (property != shadow_property); }; - - virtual int getTag () const override { return tag; } - - virtual void append (CborEncoder* encoder) override; - void appendValue(CborEncoder* mapEncoder); + virtual void appendValue (CborEncoder* mapEncoder) override; private: T& property; T shadow_property; T minDelta; - int tag = -1; - - }; #include "ArduinoCloudProperty.ipp" diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 565dd1583..394f95b59 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -44,26 +44,6 @@ inline bool ArduinoCloudProperty::newData() const { return (property != shadow_property && abs(property - shadow_property) >= minDelta ); } -template -void ArduinoCloudProperty::append(CborEncoder* encoder) { - if (!canRead()) { - return; - } - CborEncoder mapEncoder; - cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); - if (tag != -1) { - cbor_encode_text_stringz(&mapEncoder, "t"); - cbor_encode_int(&mapEncoder, tag); - } - else { - cbor_encode_text_stringz(&mapEncoder, "n"); - cbor_encode_text_stringz(&mapEncoder, getName().c_str()); - } - appendValue(&mapEncoder); - cbor_encoder_close_container(encoder, &mapEncoder); - _last_updated = millis(); -} - // Different appendValue function for different property typer, because the CBOR encoder and message format template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { diff --git a/ArduinoCloudPropertyGeneric.h b/ArduinoCloudPropertyGeneric.h index 13aff2633..d07c23921 100644 --- a/ArduinoCloudPropertyGeneric.h +++ b/ArduinoCloudPropertyGeneric.h @@ -32,6 +32,7 @@ class ArduinoCloudPropertyGeneric { _permission(permission), _update_policy(_update_policy), _last_updated(0), + _tag(-1), callback(fn) { } @@ -40,15 +41,17 @@ class ArduinoCloudPropertyGeneric { inline propertyType getType () const { return _property_type; } inline permissionType getPermission () const { return _permission; } inline long getUpdatePolicy() const { return _update_policy; } + inline int getTag () const { return _tag; } inline bool canWrite () const { return (_permission & WRITE); } virtual bool canRead () const { return (_permission & READ); } - virtual void append(CborEncoder* encoder) = 0; - virtual int getTag() const = 0; + + virtual bool newData() const = 0; virtual void updateShadow() = 0; virtual void printinfo(Stream& stream) = 0; + virtual void appendValue(CborEncoder* mapEncoder) = 0; bool shouldBeUpdated() { @@ -56,6 +59,24 @@ class ArduinoCloudPropertyGeneric { else return ((millis() - _last_updated) > (_update_policy * 1000)); } + void append(CborEncoder * encoder) { + if (!canRead()) { + return; + } + CborEncoder mapEncoder; + cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); + if (_tag != -1) { + cbor_encode_text_stringz(&mapEncoder, "t"); + cbor_encode_int(&mapEncoder, _tag); + } + else { + cbor_encode_text_stringz(&mapEncoder, "n"); + cbor_encode_text_stringz(&mapEncoder, getName().c_str()); + } + appendValue(&mapEncoder); + cbor_encoder_close_container(encoder, &mapEncoder); + _last_updated = millis(); + } void(*callback)(void) = NULL; @@ -65,6 +86,7 @@ class ArduinoCloudPropertyGeneric { propertyType _property_type; permissionType _permission; long _update_policy; + int _tag; protected: long _last_updated; From cd6ff59de6b367e04b62bee20f72b7bf9a169eb3 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sun, 14 Oct 2018 16:24:34 +0200 Subject: [PATCH 060/175] long _last_updated no longer needs to be protected but can be private since it is only accessed within its own class --- ArduinoCloudPropertyGeneric.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ArduinoCloudPropertyGeneric.h b/ArduinoCloudPropertyGeneric.h index d07c23921..2023dafd9 100644 --- a/ArduinoCloudPropertyGeneric.h +++ b/ArduinoCloudPropertyGeneric.h @@ -86,10 +86,8 @@ class ArduinoCloudPropertyGeneric { propertyType _property_type; permissionType _permission; long _update_policy; - int _tag; - - protected: long _last_updated; + int _tag; }; From 66d3ac00b1142e367e5690f5148613e5eb020fba Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sun, 14 Oct 2018 16:27:26 +0200 Subject: [PATCH 061/175] Function canRead must no longer be virtual --- ArduinoCloudPropertyGeneric.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ArduinoCloudPropertyGeneric.h b/ArduinoCloudPropertyGeneric.h index 2023dafd9..b86d49710 100644 --- a/ArduinoCloudPropertyGeneric.h +++ b/ArduinoCloudPropertyGeneric.h @@ -44,7 +44,7 @@ class ArduinoCloudPropertyGeneric { inline int getTag () const { return _tag; } inline bool canWrite () const { return (_permission & WRITE); } - virtual bool canRead () const { return (_permission & READ); } + inline bool canRead () const { return (_permission & READ); } From 2701c7535803d64bdaf33436a4fdbc2a473ecfca Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sun, 14 Oct 2018 16:47:54 +0200 Subject: [PATCH 062/175] Separating runtime polymorphism (virtual) from static polymorphism (templates) - step 1 - extract implementation for property type bool --- ArduinoCloudProperty.ipp | 6 ---- ArduinoCloudPropertyBool.h | 64 ++++++++++++++++++++++++++++++++++++++ ArduinoCloudThing.cpp | 5 +-- ArduinoCloudThing.h | 2 +- 4 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 ArduinoCloudPropertyBool.h diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 394f95b59..eb6487178 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -51,12 +51,6 @@ inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { cbor_encode_int(mapEncoder, property); }; -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "vb"); - cbor_encode_boolean(mapEncoder, property); -}; - template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { cbor_encode_text_stringz(mapEncoder, "v"); diff --git a/ArduinoCloudPropertyBool.h b/ArduinoCloudPropertyBool.h new file mode 100644 index 000000000..7e655b877 --- /dev/null +++ b/ArduinoCloudPropertyBool.h @@ -0,0 +1,64 @@ +#ifndef ARDUINO_CLOUD_PROPERTY_BOOL_H_ +#define ARDUINO_CLOUD_PROPERTY_BOOL_H_ + +#include "ArduinoCloudPropertyGeneric.h" + +class ArduinoCloudPropertyBool : public ArduinoCloudPropertyGeneric { + +public: + ArduinoCloudPropertyBool(bool & property, + String const & name, + permissionType const permission, + long const update_policy, + void(*fn)(void)) : + ArduinoCloudPropertyGeneric(name, BOOL, permission, update_policy, fn), + _property(property), + _shadow_property(!property) + { + } + + virtual bool newData() const override { + return (_property != _shadow_property); + } + + virtual void updateShadow() override { + _shadow_property = _property; + } + + virtual void printinfo(Stream& stream) override { + stream.println("name: " + getName() + " value: " + String(_property) + " shadow: " + String(_shadow_property) + " permission: " + String(getPermission())); + } + + virtual void appendValue(CborEncoder* mapEncoder) override { + cbor_encode_text_stringz(mapEncoder, "vb"); + cbor_encode_boolean(mapEncoder, _property); + }; + + bool write(bool const value) { + /* permissions are intended as seen from cloud */ + if (canWrite()) { + _property = value; + return true; + } + return false; + } + + bool read() const { + /* permissions are intended as seen from cloud */ + if (canRead()) { + return _property; + } + /* FIXME: What happens if we can not read? Compiler should complain there + * because there is no return value in case of the canRead() evaluating + * to false + */ + } + +private: + + bool & _property; + bool _shadow_property; + +}; + +#endif /* ARDUINO_CLOUD_PROPERTY_BOOL_H_ */ diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 090c0abf8..7f78164dd 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -1,5 +1,6 @@ #include #include +#include #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) extern "C" char *sbrk(int i); @@ -141,11 +142,11 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, S return *(reinterpret_cast(propertyObj)); } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, String name, permissionType _permission, long seconds, void(*fn)(void), bool minDelta) { +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, String name, permissionType permission, long seconds, void(*fn)(void), bool minDelta) { if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } - ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, !property, name, BOOL, false /* = minDelta */, _permission, seconds, fn); + ArduinoCloudPropertyBool *propertyObj = new ArduinoCloudPropertyBool(property, name, permission, seconds, fn); list.add(propertyObj); return *(reinterpret_cast(propertyObj)); } diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index ec4869f25..2bebd1f7a 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -28,7 +28,7 @@ class ArduinoCloudThing { void begin(); // overload, with default arguments, of different type of properties ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, int minDelta = 0); - ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, bool minDelta = false); + ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name, permissionType permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, bool minDelta = false); ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f); ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, String minDelta = ""); // poll should return > 0 if something has changed From 13ee571caed1e3c1b3655eb390abc29de54c98c0 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sun, 14 Oct 2018 16:48:06 +0200 Subject: [PATCH 063/175] Separating runtime polymorphism (virtual) from static polymorphism (templates) - step 2 - extract implementation for property type String --- ArduinoCloudProperty.ipp | 6 ---- ArduinoCloudPropertyString.h | 64 ++++++++++++++++++++++++++++++++++++ ArduinoCloudThing.cpp | 5 +-- ArduinoCloudThing.h | 2 +- 4 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 ArduinoCloudPropertyString.h diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index eb6487178..db234dc62 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -56,9 +56,3 @@ inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { cbor_encode_text_stringz(mapEncoder, "v"); cbor_encode_float(mapEncoder, property); }; - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "vs"); - cbor_encode_text_stringz(mapEncoder, property.c_str()); -}; diff --git a/ArduinoCloudPropertyString.h b/ArduinoCloudPropertyString.h new file mode 100644 index 000000000..608b5bf3b --- /dev/null +++ b/ArduinoCloudPropertyString.h @@ -0,0 +1,64 @@ +#ifndef ARDUINO_CLOUD_PROPERTY_STRING_H_ +#define ARDUINO_CLOUD_PROPERTY_STRING_H_ + +#include "ArduinoCloudPropertyGeneric.h" + +class ArduinoCloudPropertyString : public ArduinoCloudPropertyGeneric { + +public: + ArduinoCloudPropertyString(String & property, + String const & name, + permissionType const permission, + long const update_policy, + void(*fn)(void)) : + ArduinoCloudPropertyGeneric(name, STRING, permission, update_policy, fn), + _property(property), + _shadow_property(property + "x") + { + } + + virtual bool newData() const override { + return (_property != _shadow_property); + } + + virtual void updateShadow() override { + _shadow_property = _property; + } + + virtual void printinfo(Stream& stream) override { + stream.println("name: " + getName() + " value: " + String(_property) + " shadow: " + String(_shadow_property) + " permission: " + String(getPermission())); + } + + virtual void appendValue(CborEncoder* mapEncoder) override { + cbor_encode_text_stringz(mapEncoder, "vs"); + cbor_encode_text_stringz(mapEncoder, _property.c_str()); + }; + + bool write(bool const value) { + /* permissions are intended as seen from cloud */ + if (canWrite()) { + _property = value; + return true; + } + return false; + } + + bool read() const { + /* permissions are intended as seen from cloud */ + if (canRead()) { + return _property; + } + /* FIXME: What happens if we can not read? Compiler should complain there + * because there is no return value in case of the canRead() evaluating + * to false + */ + } + +private: + + String & _property; + String _shadow_property; + +}; + +#endif /* ARDUINO_CLOUD_PROPERTY_STRING_H_ */ diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 7f78164dd..261049392 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) extern "C" char *sbrk(int i); @@ -160,11 +161,11 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, return *(reinterpret_cast(propertyObj)); } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(String& property, String name, permissionType _permission, long seconds, void(*fn)(void), String minDelta) { +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(String& property, String name, permissionType permission, long seconds, void(*fn)(void), String minDelta) { if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } - ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, property + "x", name, STRING, "" /* = minDelta */, _permission, seconds, fn); + ArduinoCloudPropertyString *propertyObj = new ArduinoCloudPropertyString(property, name, permission, seconds, fn); list.add(propertyObj); return *(reinterpret_cast(propertyObj)); } diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 2bebd1f7a..44cca0fac 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -30,7 +30,7 @@ class ArduinoCloudThing { ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, int minDelta = 0); ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name, permissionType permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, bool minDelta = false); ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f); - ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, String minDelta = ""); + ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name, permissionType permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, String minDelta = ""); // poll should return > 0 if something has changed int poll(uint8_t* data, size_t size); // decode a CBOR payload received from the Cloud. From f1fedbb3858010508f05603164beece8de08f632 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sun, 14 Oct 2018 16:52:54 +0200 Subject: [PATCH 064/175] Separating runtime polymorphism (virtual) from static polymorphism (templates) - step 3 - extract implementation for property type String --- ArduinoCloudProperty.ipp | 12 ------- ArduinoCloudPropertyInt.h | 67 +++++++++++++++++++++++++++++++++++++++ ArduinoCloudThing.cpp | 5 +-- 3 files changed, 70 insertions(+), 14 deletions(-) create mode 100644 ArduinoCloudPropertyInt.h diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index db234dc62..09bb448a2 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -34,23 +34,11 @@ void ArduinoCloudProperty::printinfo(Stream& stream) { stream.println("name: " + getName() + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(getPermission())); } -template <> -inline bool ArduinoCloudProperty::newData() const { - return (property != shadow_property && abs(property - shadow_property) >= minDelta ); -} - template <> inline bool ArduinoCloudProperty::newData() const { return (property != shadow_property && abs(property - shadow_property) >= minDelta ); } -// Different appendValue function for different property typer, because the CBOR encoder and message format -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "v"); - cbor_encode_int(mapEncoder, property); -}; - template <> inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { cbor_encode_text_stringz(mapEncoder, "v"); diff --git a/ArduinoCloudPropertyInt.h b/ArduinoCloudPropertyInt.h new file mode 100644 index 000000000..3b567fe31 --- /dev/null +++ b/ArduinoCloudPropertyInt.h @@ -0,0 +1,67 @@ +#ifndef ARDUINO_CLOUD_PROPERTY_INT_H_ +#define ARDUINO_CLOUD_PROPERTY_INT_H_ + +#include "ArduinoCloudPropertyGeneric.h" + +class ArduinoCloudPropertyInt : public ArduinoCloudPropertyGeneric { + +public: + ArduinoCloudPropertyInt(int & property, + int const min_delta, + String const & name, + permissionType const permission, + long const update_policy, + void(*fn)(void)) : + ArduinoCloudPropertyGeneric(name, STRING, permission, update_policy, fn), + _property(property), + _shadow_property(property + 1), + _min_delta(min_delta) + { + } + + virtual bool newData() const override { + return (_property != _shadow_property && abs(_property - _shadow_property) >= _min_delta); + } + + virtual void updateShadow() override { + _shadow_property = _property; + } + + virtual void printinfo(Stream& stream) override { + stream.println("name: " + getName() + " value: " + String(_property) + " shadow: " + String(_shadow_property) + " permission: " + String(getPermission())); + } + + virtual void appendValue(CborEncoder* mapEncoder) override { + cbor_encode_text_stringz(mapEncoder, "v"); + cbor_encode_int(mapEncoder, _property); + }; + + bool write(bool const value) { + /* permissions are intended as seen from cloud */ + if (canWrite()) { + _property = value; + return true; + } + return false; + } + + bool read() const { + /* permissions are intended as seen from cloud */ + if (canRead()) { + return _property; + } + /* FIXME: What happens if we can not read? Compiler should complain there + * because there is no return value in case of the canRead() evaluating + * to false + */ + } + +private: + + int & _property; + int _shadow_property; + int _min_delta; + +}; + +#endif /* ARDUINO_CLOUD_PROPERTY_INT_H_ */ diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 261049392..8e6852c8f 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -131,12 +132,12 @@ int ArduinoCloudThing::findPropertyByName(String &name) { return -1; } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, String name, permissionType _permission, long seconds, void(*fn)(void), int minDelta) { +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, String name, permissionType permission, long seconds, void(*fn)(void), int minDelta) { if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } // If a property with ythis name does not exist, create it into thing - ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, property + 1, name, INT, minDelta, _permission, seconds, fn); + ArduinoCloudPropertyInt *propertyObj = new ArduinoCloudPropertyInt(property, minDelta, name, permission, seconds, fn); // Add the new property to the thin properties list list.add(propertyObj); // Return the new property as a generic one From db525fd26ed9f81b9028db41a50045187e4d7ae0 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sun, 14 Oct 2018 17:00:30 +0200 Subject: [PATCH 065/175] Separating runtime polymorphism (virtual) from static polymorphism (templates) - step 4 - extract implementation for property type float and removing class ArduinoCloudProperty --- ArduinoCloudProperty.hpp | 30 ----------------- ArduinoCloudProperty.ipp | 46 ------------------------- ArduinoCloudPropertyFloat.h | 67 +++++++++++++++++++++++++++++++++++++ ArduinoCloudPropertyInt.h | 2 +- ArduinoCloudThing.cpp | 25 +++++++------- ArduinoCloudThing.h | 1 - 6 files changed, 81 insertions(+), 90 deletions(-) delete mode 100644 ArduinoCloudProperty.hpp delete mode 100644 ArduinoCloudProperty.ipp create mode 100644 ArduinoCloudPropertyFloat.h diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp deleted file mode 100644 index ca53b5d4d..000000000 --- a/ArduinoCloudProperty.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef ARDUINO_CLOUD_PROPERTY_H_ -#define ARDUINO_CLOUD_PROPERTY_H_ - -#include "ArduinoCloudPropertyGeneric.h" - -template -class ArduinoCloudProperty : public ArduinoCloudPropertyGeneric { - public: - - ArduinoCloudProperty(T& _property, T const _shadow_property, String const & _name, propertyType const property_type, T const _minDelta, permissionType const _permission, long const _updatePolicy, void(*fn)(void)); - - bool write(T value); - T read (); - - virtual void printinfo(Stream& stream) override; - - virtual void updateShadow () override { shadow_property = property; } - virtual bool newData () const override { return (property != shadow_property); }; - virtual void appendValue (CborEncoder* mapEncoder) override; - - private: - - T& property; - T shadow_property; - T minDelta; -}; - -#include "ArduinoCloudProperty.ipp" - -#endif /* ARDUINO_CLOUD_PROPERTY_H_ */ diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp deleted file mode 100644 index 09bb448a2..000000000 --- a/ArduinoCloudProperty.ipp +++ /dev/null @@ -1,46 +0,0 @@ -template -ArduinoCloudProperty::ArduinoCloudProperty(T& _property, T const _shadow_property, String const & _name, propertyType const property_type, T const _minDelta, permissionType const _permission, long const _updatePolicy, void(*fn)(void)) : - ArduinoCloudPropertyGeneric(_name, property_type, _permission, _updatePolicy, fn), - property (_property ), - shadow_property(_shadow_property), - minDelta (_minDelta ) -{ -} - -template -bool ArduinoCloudProperty::write(T value) { - /* permissions are intended as seen from cloud */ - if (canWrite()) { - property = value; - return true; - } - return false; -} - -template -T ArduinoCloudProperty::read() { - /* permissions are intended as seen from cloud */ - if (canRead()) { - return property; - } - /* FIXME: What happens if we can not read? Compiler should complain there - * because there is no return value in case of the canRead() evaluating - * to false - */ -} - -template -void ArduinoCloudProperty::printinfo(Stream& stream) { - stream.println("name: " + getName() + " value: " + String(property) + " shadow: " + String(shadow_property) + " permission: " + String(getPermission())); -} - -template <> -inline bool ArduinoCloudProperty::newData() const { - return (property != shadow_property && abs(property - shadow_property) >= minDelta ); -} - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder* mapEncoder) { - cbor_encode_text_stringz(mapEncoder, "v"); - cbor_encode_float(mapEncoder, property); -}; diff --git a/ArduinoCloudPropertyFloat.h b/ArduinoCloudPropertyFloat.h new file mode 100644 index 000000000..569394dad --- /dev/null +++ b/ArduinoCloudPropertyFloat.h @@ -0,0 +1,67 @@ +#ifndef ARDUINO_CLOUD_PROPERTY_FLOAT_H_ +#define ARDUINO_CLOUD_PROPERTY_FLOAT_H_ + +#include "ArduinoCloudPropertyGeneric.h" + +class ArduinoCloudPropertyFloat : public ArduinoCloudPropertyGeneric { + +public: + ArduinoCloudPropertyFloat(float & property, + float const min_delta, + String const & name, + permissionType const permission, + long const update_policy, + void(*fn)(void)) : + ArduinoCloudPropertyGeneric(name, FLOAT, permission, update_policy, fn), + _property(property), + _shadow_property(property + 0.5f), + _min_delta(min_delta) + { + } + + virtual bool newData() const override { + return (_property != _shadow_property && abs(_property - _shadow_property) >= _min_delta); + } + + virtual void updateShadow() override { + _shadow_property = _property; + } + + virtual void printinfo(Stream& stream) override { + stream.println("name: " + getName() + " value: " + String(_property) + " shadow: " + String(_shadow_property) + " permission: " + String(getPermission())); + } + + virtual void appendValue(CborEncoder* mapEncoder) override { + cbor_encode_text_stringz(mapEncoder, "v"); + cbor_encode_float(mapEncoder, _property); + }; + + bool write(bool const value) { + /* permissions are intended as seen from cloud */ + if (canWrite()) { + _property = value; + return true; + } + return false; + } + + bool read() const { + /* permissions are intended as seen from cloud */ + if (canRead()) { + return _property; + } + /* FIXME: What happens if we can not read? Compiler should complain there + * because there is no return value in case of the canRead() evaluating + * to false + */ + } + +private: + + float & _property; + float _shadow_property; + float _min_delta; + +}; + +#endif /* ARDUINO_CLOUD_PROPERTY_FLOAT_H_ */ diff --git a/ArduinoCloudPropertyInt.h b/ArduinoCloudPropertyInt.h index 3b567fe31..1504be543 100644 --- a/ArduinoCloudPropertyInt.h +++ b/ArduinoCloudPropertyInt.h @@ -12,7 +12,7 @@ class ArduinoCloudPropertyInt : public ArduinoCloudPropertyGeneric { permissionType const permission, long const update_policy, void(*fn)(void)) : - ArduinoCloudPropertyGeneric(name, STRING, permission, update_policy, fn), + ArduinoCloudPropertyGeneric(name, INT, permission, update_policy, fn), _property(property), _shadow_property(property + 1), _min_delta(min_delta) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 8e6852c8f..2aed48a0f 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) @@ -153,11 +154,11 @@ ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, return *(reinterpret_cast(propertyObj)); } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, String name, permissionType _permission, long seconds, void(*fn)(void), float minDelta) { +ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, String name, permissionType permission, long seconds, void(*fn)(void), float minDelta) { if (ArduinoCloudPropertyGeneric* p = exists(name)) { return *p; } - ArduinoCloudProperty *propertyObj = new ArduinoCloudProperty(property, property + 0.5f, name, FLOAT, minDelta, _permission, seconds, fn); + ArduinoCloudPropertyFloat *propertyObj = new ArduinoCloudPropertyFloat(property, minDelta, name, permission, seconds, fn); list.add(propertyObj); return *(reinterpret_cast(propertyObj)); } @@ -263,22 +264,22 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { double val; // get the value of the property as a double cbor_value_get_double(&propValue, &val); - ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + ArduinoCloudPropertyFloat* p = (ArduinoCloudPropertyFloat*) property; p->write((float)val); } else if (propValue.type == CborIntegerType) { int val; cbor_value_get_int(&propValue, &val); - ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + ArduinoCloudPropertyFloat* p = (ArduinoCloudPropertyFloat*) property; p->write((float)val); } else if (propValue.type == CborFloatType) { float val; cbor_value_get_float(&propValue, &val); - ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + ArduinoCloudPropertyFloat* p = (ArduinoCloudPropertyFloat*) property; p->write(val); } else if (propValue.type == CborHalfFloatType) { float val; cbor_value_get_half_float(&propValue, &val); - ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + ArduinoCloudPropertyFloat * p = (ArduinoCloudPropertyFloat*) property; p->write(val); } } else if (propType == INT && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { @@ -286,37 +287,37 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { if (propValue.type == CborIntegerType) { int val; cbor_value_get_int(&propValue, &val); - ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + ArduinoCloudPropertyInt* p = (ArduinoCloudPropertyInt*) property; p->write(val); } else if (propValue.type == CborDoubleType) { // If a double value is received, a cast to int is performed(so it is still accepted) double val; cbor_value_get_double(&propValue, &val); - ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + ArduinoCloudPropertyInt* p = (ArduinoCloudPropertyInt*) property; p->write((int)val); } else if (propValue.type == CborFloatType) { float val; cbor_value_get_float(&propValue, &val); - ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + ArduinoCloudPropertyInt* p = (ArduinoCloudPropertyInt*) property; p->write((int)val); } else if (propValue.type == CborHalfFloatType) { float val; cbor_value_get_half_float(&propValue, &val); - ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + ArduinoCloudPropertyInt* p = (ArduinoCloudPropertyInt*) property; p->write((int)val); } } else if (propType == BOOL && !cbor_value_map_find_value(&recursedMap, "vb", &propValue)) { if (propValue.type == CborBooleanType) { bool val; cbor_value_get_boolean(&propValue, &val); - ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + ArduinoCloudPropertyBool* p = (ArduinoCloudPropertyBool*) property; p->write(val); } } else if (propType == STRING && !cbor_value_map_find_value(&recursedMap, "vs", &propValue)){ if (propValue.type == CborTextStringType) { char *val; size_t valSize; err = cbor_value_dup_text_string(&propValue, &val, &valSize, &propValue); - ArduinoCloudProperty* p = (ArduinoCloudProperty*) property; + ArduinoCloudPropertyString* p = (ArduinoCloudPropertyString*) property; // Char* string transformed into array p->write(String((char*)val)); free(val); diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 44cca0fac..0a94794ee 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -4,7 +4,6 @@ #include #include -#include "ArduinoCloudProperty.hpp" #include "ArduinoCloudPropertyGeneric.h" #include "lib/tinycbor/cbor-lib.h" From 8323f8962cef62dc64eb877c9a8ba6e6dedd4216 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sun, 14 Oct 2018 17:52:12 +0200 Subject: [PATCH 066/175] Method 'shouldBeUpdated' can also be 'const' --- ArduinoCloudPropertyGeneric.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ArduinoCloudPropertyGeneric.h b/ArduinoCloudPropertyGeneric.h index b86d49710..7ba13fa52 100644 --- a/ArduinoCloudPropertyGeneric.h +++ b/ArduinoCloudPropertyGeneric.h @@ -53,7 +53,7 @@ class ArduinoCloudPropertyGeneric { virtual void printinfo(Stream& stream) = 0; virtual void appendValue(CborEncoder* mapEncoder) = 0; - bool shouldBeUpdated() + bool shouldBeUpdated() const { if (_update_policy == ON_CHANGE) return newData(); else return ((millis() - _last_updated) > (_update_policy * 1000)); From 85899553e370537a312729cab4cd52977d332652 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Mon, 15 Oct 2018 11:19:21 +0200 Subject: [PATCH 067/175] Adding template class ArduinoCloudProperty which should replace the runtime-polymorphism implementation since it consumes by far less lines of code --- ArduinoCloudProperty.hpp | 168 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 ArduinoCloudProperty.hpp diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp new file mode 100644 index 000000000..258a5ac61 --- /dev/null +++ b/ArduinoCloudProperty.hpp @@ -0,0 +1,168 @@ +#ifndef ARDUINO_CLOUD_PROPERTY_HPP_ +#define ARDUINO_CLOUD_PROPERTY_HPP_ + +enum class Permission { + Read, Write, ReadWrite +}; + +enum class Type { + Bool, Int, Float, String +}; + +enum class UpdatePolicy { + OnChange, TimeInterval +}; + +template +class ArduinoCloudProperty { +public: + + ArduinoCloudProperty(String const & name, T & property, Permission const permission, UpdatePolicy const update_policy); + + bool read (T * val) const; + + inline void publishEvery(unsigned long const seconds ) { _update_interval_sec = seconds; } + inline void setMinDelta (T const min_delta) { _min_delta_property_val = min_delta; } + + inline String name () const { return _name; } + Type type () const; + inline bool canRead () const { return (_permission == Permission::Read ) || (_permission == Permission::ReadWrite); } + inline bool canWrite() const { return (_permission == Permission::Write) || (_permission == Permission::ReadWrite); } + + bool shouldBeUpdated() const; + void append(CborEncoder * encoder); + +private: + + String _name; + T & _property, + _shadow_property; + Permission _permission; + UpdatePolicy _update_policy; + /* Variables used for update_policy OnChange */ + T _min_delta_property_val; + /* Variables used for update policy TimeInterval */ + unsigned long _last_updated, + _update_interval_sec; + + void appendValue(CborEncoder * mapEncoder) const; + bool isValueDifferent(T const lhs, T const rhs) const; + +}; + +template +inline bool operator == (ArduinoCloudProperty const & lhs, ArduinoCloudProperty const & rhs) { return (lhs.name() == rhs.name()); } + +template +ArduinoCloudProperty::ArduinoCloudProperty(String const & name, T & property, Permission const permission, UpdatePolicy const update_policy) +: _name(name), + _property(property), + _shadow_property(property), + _permission(permission), + _update_policy(update_policy), + _last_updated(0), + _update_interval_sec(0) +{ +} + +template +bool ArduinoCloudProperty::read(T * val) const { + if(!canRead()) return false; + *val = _property; + return true; +} + +template <> +inline Type ArduinoCloudProperty::type() const { + return Type::Bool; +} + +template <> +inline Type ArduinoCloudProperty::type() const { + return Type::Int; +} + +template <> +inline Type ArduinoCloudProperty::type() const { + return Type::Float; +} + +template <> +inline Type ArduinoCloudProperty::type() const { + return Type::String; +} + +template +bool ArduinoCloudProperty::shouldBeUpdated() const { + if (_update_policy == UpdatePolicy::OnChange) { + return isValueDifferent(_property, _shadow_property); + } + else if(_update_policy == UpdatePolicy::TimeInterval) { + return ((millis() - _last_updated) > (_update_interval_sec * 1000)); + } + else { + return false; + } +} + +template +void ArduinoCloudProperty::append(CborEncoder * encoder) { + if (canRead()) { + CborEncoder mapEncoder; + + cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); + cbor_encode_text_stringz(&mapEncoder, "n"); + cbor_encode_text_stringz(&mapEncoder, _name.c_str()); + appendValue(&mapEncoder); + cbor_encoder_close_container(encoder, &mapEncoder); + + _shadow_property = _property; + _last_updated = millis(); + } +} + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { + cbor_encode_text_stringz(mapEncoder, "vb"); + cbor_encode_boolean(mapEncoder, _property); +} + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { + cbor_encode_text_stringz(mapEncoder, "v"); + cbor_encode_int(mapEncoder, _property); +} + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { + cbor_encode_text_stringz(mapEncoder, "v"); + cbor_encode_float(mapEncoder, _property); +} + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { + cbor_encode_text_stringz(mapEncoder, "vs"); + cbor_encode_text_stringz(mapEncoder, _property.c_str()); +} + +template <> +inline bool ArduinoCloudProperty::isValueDifferent(bool const lhs, bool const rhs) const { + return (lhs != rhs); +} + +template <> +inline bool ArduinoCloudProperty::isValueDifferent(int const lhs, int const rhs) const { + return (lhs != rhs) && (abs(lhs - rhs) >= _min_delta_property_val); +} + +template <> +inline bool ArduinoCloudProperty::isValueDifferent(float const lhs, float const rhs) const { + return (lhs != rhs) && (abs(lhs - rhs) >= _min_delta_property_val); +} + +template <> +inline bool ArduinoCloudProperty::isValueDifferent(String const lhs, String const rhs) const { + return (lhs != rhs); +} + +#endif /* ARDUINO_CLOUD_PROPERTY_HPP_ */ From 6e04616de08b5253be037d411b67904945a1b9f6 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Mon, 15 Oct 2018 15:09:29 +0200 Subject: [PATCH 068/175] Replacing runtime-polymorphism implementation spread over different files with a template implementation which is considerable more compact and avoids runtime overhead --- ArduinoCloudProperty.hpp | 92 +++++++++++--- ArduinoCloudPropertyBool.h | 64 ---------- ArduinoCloudPropertyContainer.hpp | 62 ++++++++++ ArduinoCloudPropertyFloat.h | 67 ---------- ArduinoCloudPropertyGeneric.h | 96 --------------- ArduinoCloudPropertyInt.h | 67 ---------- ArduinoCloudPropertyString.h | 64 ---------- ArduinoCloudThing.cpp | 197 +++++++++++------------------- ArduinoCloudThing.h | 27 ++-- 9 files changed, 216 insertions(+), 520 deletions(-) delete mode 100644 ArduinoCloudPropertyBool.h create mode 100644 ArduinoCloudPropertyContainer.hpp delete mode 100644 ArduinoCloudPropertyFloat.h delete mode 100644 ArduinoCloudPropertyGeneric.h delete mode 100644 ArduinoCloudPropertyInt.h delete mode 100644 ArduinoCloudPropertyString.h diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 258a5ac61..e7c983ef4 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -1,6 +1,8 @@ #ifndef ARDUINO_CLOUD_PROPERTY_HPP_ #define ARDUINO_CLOUD_PROPERTY_HPP_ +#include "lib/tinycbor/cbor-lib.h" + enum class Permission { Read, Write, ReadWrite }; @@ -13,37 +15,47 @@ enum class UpdatePolicy { OnChange, TimeInterval }; +typedef void(*UpdateCallbackFunc)(void); + + template class ArduinoCloudProperty { public: - ArduinoCloudProperty(String const & name, T & property, Permission const permission, UpdatePolicy const update_policy); + ArduinoCloudProperty(T & property, String const & name, Permission const permission); - bool read (T * val) const; + bool write(T const val); + bool read (T * val) const; - inline void publishEvery(unsigned long const seconds ) { _update_interval_sec = seconds; } - inline void setMinDelta (T const min_delta) { _min_delta_property_val = min_delta; } + void onUpdate (UpdateCallbackFunc func ); + void publishOnChange(T const min_delta_property); + void publishEvery (unsigned long const seconds ); inline String name () const { return _name; } Type type () const; inline bool canRead () const { return (_permission == Permission::Read ) || (_permission == Permission::ReadWrite); } inline bool canWrite() const { return (_permission == Permission::Write) || (_permission == Permission::ReadWrite); } - bool shouldBeUpdated() const; - void append(CborEncoder * encoder); + bool shouldBeUpdated () const; + void execCallbackOnChange (); + + void append (CborEncoder * encoder); private: - String _name; - T & _property, - _shadow_property; - Permission _permission; - UpdatePolicy _update_policy; + T & _property, + _shadow_property; + String _name; + Permission _permission; + UpdateCallbackFunc _update_callback_func; + + UpdatePolicy _update_policy; + bool _has_been_updated_once; /* Variables used for update_policy OnChange */ - T _min_delta_property_val; + T _min_delta_property; /* Variables used for update policy TimeInterval */ - unsigned long _last_updated, - _update_interval_sec; + unsigned long _last_updated, + _update_interval_sec; void appendValue(CborEncoder * mapEncoder) const; bool isValueDifferent(T const lhs, T const rhs) const; @@ -54,17 +66,27 @@ template inline bool operator == (ArduinoCloudProperty const & lhs, ArduinoCloudProperty const & rhs) { return (lhs.name() == rhs.name()); } template -ArduinoCloudProperty::ArduinoCloudProperty(String const & name, T & property, Permission const permission, UpdatePolicy const update_policy) -: _name(name), - _property(property), +ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, Permission const permission) +: _property(property), _shadow_property(property), + _name(name), _permission(permission), - _update_policy(update_policy), + _update_callback_func(NULL), + _update_policy(UpdatePolicy::OnChange), + _has_been_updated_once(false), _last_updated(0), _update_interval_sec(0) { } +template +bool ArduinoCloudProperty::write(T const val) { + if(!canWrite()) return false; + _property = val; + /* _shadow_property is not updated so there will be an update the next time around */ + return true; +} + template bool ArduinoCloudProperty::read(T * val) const { if(!canRead()) return false; @@ -72,6 +94,24 @@ bool ArduinoCloudProperty::read(T * val) const { return true; } +template +void ArduinoCloudProperty::onUpdate(UpdateCallbackFunc func) { + _update_callback_func = func; +} + +template +void ArduinoCloudProperty::publishOnChange(T const min_delta_property) { + _update_policy = UpdatePolicy::OnChange; + _min_delta_property = min_delta_property; +} + +template +void ArduinoCloudProperty::publishEvery(unsigned long const seconds) { + _update_policy = UpdatePolicy::TimeInterval; + _update_interval_sec = seconds; +} + + template <> inline Type ArduinoCloudProperty::type() const { return Type::Bool; @@ -94,6 +134,8 @@ inline Type ArduinoCloudProperty::type() const { template bool ArduinoCloudProperty::shouldBeUpdated() const { + if(!_has_been_updated_once) return true; + if (_update_policy == UpdatePolicy::OnChange) { return isValueDifferent(_property, _shadow_property); } @@ -105,6 +147,15 @@ bool ArduinoCloudProperty::shouldBeUpdated() const { } } +template +void ArduinoCloudProperty::execCallbackOnChange() { + if(isValueDifferent(_property, _shadow_property)) { + if(_update_callback_func != NULL) { + _update_callback_func(); + } + } +} + template void ArduinoCloudProperty::append(CborEncoder * encoder) { if (canRead()) { @@ -117,6 +168,7 @@ void ArduinoCloudProperty::append(CborEncoder * encoder) { cbor_encoder_close_container(encoder, &mapEncoder); _shadow_property = _property; + _has_been_updated_once = true; _last_updated = millis(); } } @@ -152,12 +204,12 @@ inline bool ArduinoCloudProperty::isValueDifferent(bool const lhs, bool co template <> inline bool ArduinoCloudProperty::isValueDifferent(int const lhs, int const rhs) const { - return (lhs != rhs) && (abs(lhs - rhs) >= _min_delta_property_val); + return (lhs != rhs) && (abs(lhs - rhs) >= _min_delta_property); } template <> inline bool ArduinoCloudProperty::isValueDifferent(float const lhs, float const rhs) const { - return (lhs != rhs) && (abs(lhs - rhs) >= _min_delta_property_val); + return (lhs != rhs) && (abs(lhs - rhs) >= _min_delta_property); } template <> diff --git a/ArduinoCloudPropertyBool.h b/ArduinoCloudPropertyBool.h deleted file mode 100644 index 7e655b877..000000000 --- a/ArduinoCloudPropertyBool.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef ARDUINO_CLOUD_PROPERTY_BOOL_H_ -#define ARDUINO_CLOUD_PROPERTY_BOOL_H_ - -#include "ArduinoCloudPropertyGeneric.h" - -class ArduinoCloudPropertyBool : public ArduinoCloudPropertyGeneric { - -public: - ArduinoCloudPropertyBool(bool & property, - String const & name, - permissionType const permission, - long const update_policy, - void(*fn)(void)) : - ArduinoCloudPropertyGeneric(name, BOOL, permission, update_policy, fn), - _property(property), - _shadow_property(!property) - { - } - - virtual bool newData() const override { - return (_property != _shadow_property); - } - - virtual void updateShadow() override { - _shadow_property = _property; - } - - virtual void printinfo(Stream& stream) override { - stream.println("name: " + getName() + " value: " + String(_property) + " shadow: " + String(_shadow_property) + " permission: " + String(getPermission())); - } - - virtual void appendValue(CborEncoder* mapEncoder) override { - cbor_encode_text_stringz(mapEncoder, "vb"); - cbor_encode_boolean(mapEncoder, _property); - }; - - bool write(bool const value) { - /* permissions are intended as seen from cloud */ - if (canWrite()) { - _property = value; - return true; - } - return false; - } - - bool read() const { - /* permissions are intended as seen from cloud */ - if (canRead()) { - return _property; - } - /* FIXME: What happens if we can not read? Compiler should complain there - * because there is no return value in case of the canRead() evaluating - * to false - */ - } - -private: - - bool & _property; - bool _shadow_property; - -}; - -#endif /* ARDUINO_CLOUD_PROPERTY_BOOL_H_ */ diff --git a/ArduinoCloudPropertyContainer.hpp b/ArduinoCloudPropertyContainer.hpp new file mode 100644 index 000000000..8d2538937 --- /dev/null +++ b/ArduinoCloudPropertyContainer.hpp @@ -0,0 +1,62 @@ +#ifndef ARDUINO_CLOUD_PROPERTY_CONTAINER_H_ +#define ARDUINO_CLOUD_PROPERTY_CONTAINER_H_ + +#include "ArduinoCloudProperty.hpp" + +#include "lib/tinycbor/cbor-lib.h" +#include "lib/LinkedList/LinkedList.h" + +template +class ArduinoCloudPropertyContainer { +public: + + void add(ArduinoCloudProperty * property_obj); + ArduinoCloudProperty * operator [] (String const & name); + int cntNumberOfPropertiesWhichShouldBeUpdated(); + void appendIfPropertyShouldBeUpdated(CborEncoder * arrayEncoder); + +private: + + LinkedList *> _list; + +}; + +template +void ArduinoCloudPropertyContainer::add(ArduinoCloudProperty * property_obj) { + _list.add(property_obj); +} + +template +ArduinoCloudProperty * ArduinoCloudPropertyContainer::operator [] (String const & name) { + for (int i = 0; i < _list.size(); i++) { + ArduinoCloudProperty * p = _list.get(i); + if (p->name() == name) return p; + } + return 0; +} + +template +int ArduinoCloudPropertyContainer::cntNumberOfPropertiesWhichShouldBeUpdated() { + int should_be_updated_cnt = 0; + + for (int i = 0; i < _list.size(); i++) { + ArduinoCloudProperty * p = _list.get(i); + if (p->shouldBeUpdated() && p->canRead()) { + should_be_updated_cnt++; + } + } + + return should_be_updated_cnt; +} + +template +void ArduinoCloudPropertyContainer::appendIfPropertyShouldBeUpdated(CborEncoder * arrayEncoder) { + for (int i = 0; i < _list.size(); i++) { + ArduinoCloudProperty * p = _list.get(i); + if (p->shouldBeUpdated() && p->canRead()) { + p->append(arrayEncoder); + } + } +} + +#endif /* ARDUINO_CLOUD_PROPERTY_CONTAINER_H_ */ diff --git a/ArduinoCloudPropertyFloat.h b/ArduinoCloudPropertyFloat.h deleted file mode 100644 index 569394dad..000000000 --- a/ArduinoCloudPropertyFloat.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef ARDUINO_CLOUD_PROPERTY_FLOAT_H_ -#define ARDUINO_CLOUD_PROPERTY_FLOAT_H_ - -#include "ArduinoCloudPropertyGeneric.h" - -class ArduinoCloudPropertyFloat : public ArduinoCloudPropertyGeneric { - -public: - ArduinoCloudPropertyFloat(float & property, - float const min_delta, - String const & name, - permissionType const permission, - long const update_policy, - void(*fn)(void)) : - ArduinoCloudPropertyGeneric(name, FLOAT, permission, update_policy, fn), - _property(property), - _shadow_property(property + 0.5f), - _min_delta(min_delta) - { - } - - virtual bool newData() const override { - return (_property != _shadow_property && abs(_property - _shadow_property) >= _min_delta); - } - - virtual void updateShadow() override { - _shadow_property = _property; - } - - virtual void printinfo(Stream& stream) override { - stream.println("name: " + getName() + " value: " + String(_property) + " shadow: " + String(_shadow_property) + " permission: " + String(getPermission())); - } - - virtual void appendValue(CborEncoder* mapEncoder) override { - cbor_encode_text_stringz(mapEncoder, "v"); - cbor_encode_float(mapEncoder, _property); - }; - - bool write(bool const value) { - /* permissions are intended as seen from cloud */ - if (canWrite()) { - _property = value; - return true; - } - return false; - } - - bool read() const { - /* permissions are intended as seen from cloud */ - if (canRead()) { - return _property; - } - /* FIXME: What happens if we can not read? Compiler should complain there - * because there is no return value in case of the canRead() evaluating - * to false - */ - } - -private: - - float & _property; - float _shadow_property; - float _min_delta; - -}; - -#endif /* ARDUINO_CLOUD_PROPERTY_FLOAT_H_ */ diff --git a/ArduinoCloudPropertyGeneric.h b/ArduinoCloudPropertyGeneric.h deleted file mode 100644 index 7ba13fa52..000000000 --- a/ArduinoCloudPropertyGeneric.h +++ /dev/null @@ -1,96 +0,0 @@ -#ifndef ARDUINO_CLOUD_PROPERTY_GENERIC_H_ -#define ARDUINO_CLOUD_PROPERTY_GENERIC_H_ - -#include "lib/tinycbor/cbor-lib.h" - -typedef enum { - READ = 0b01, - WRITE = 0b10, - READWRITE = READ | WRITE, -} permissionType; - -typedef enum { - INT, - FLOAT, - BOOL, - STRING -} propertyType; - -// definition of the default property update policy -static const int ON_CHANGE = -1; - -class ArduinoCloudPropertyGeneric { - public: - - ArduinoCloudPropertyGeneric(String const & name, - propertyType const property_type, - permissionType const permission, - long const update_policy, - void(*fn)(void)) - : _name(name), - _property_type(property_type), - _permission(permission), - _update_policy(_update_policy), - _last_updated(0), - _tag(-1), - callback(fn) - { - } - - inline String const & getName () const { return _name; } - inline propertyType getType () const { return _property_type; } - inline permissionType getPermission () const { return _permission; } - inline long getUpdatePolicy() const { return _update_policy; } - inline int getTag () const { return _tag; } - - inline bool canWrite () const { return (_permission & WRITE); } - inline bool canRead () const { return (_permission & READ); } - - - - virtual bool newData() const = 0; - virtual void updateShadow() = 0; - virtual void printinfo(Stream& stream) = 0; - virtual void appendValue(CborEncoder* mapEncoder) = 0; - - bool shouldBeUpdated() const - { - if (_update_policy == ON_CHANGE) return newData(); - else return ((millis() - _last_updated) > (_update_policy * 1000)); - } - - void append(CborEncoder * encoder) { - if (!canRead()) { - return; - } - CborEncoder mapEncoder; - cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); - if (_tag != -1) { - cbor_encode_text_stringz(&mapEncoder, "t"); - cbor_encode_int(&mapEncoder, _tag); - } - else { - cbor_encode_text_stringz(&mapEncoder, "n"); - cbor_encode_text_stringz(&mapEncoder, getName().c_str()); - } - appendValue(&mapEncoder); - cbor_encoder_close_container(encoder, &mapEncoder); - _last_updated = millis(); - } - - void(*callback)(void) = NULL; - - private: - - String _name; - propertyType _property_type; - permissionType _permission; - long _update_policy; - long _last_updated; - int _tag; - -}; - -inline bool operator == (ArduinoCloudPropertyGeneric const & lhs, ArduinoCloudPropertyGeneric const & rhs) { return (lhs.getName() == rhs.getName()); } - -#endif /* ARDUINO_CLOUD_PROPERTY_GENERIC_H_ */ diff --git a/ArduinoCloudPropertyInt.h b/ArduinoCloudPropertyInt.h deleted file mode 100644 index 1504be543..000000000 --- a/ArduinoCloudPropertyInt.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef ARDUINO_CLOUD_PROPERTY_INT_H_ -#define ARDUINO_CLOUD_PROPERTY_INT_H_ - -#include "ArduinoCloudPropertyGeneric.h" - -class ArduinoCloudPropertyInt : public ArduinoCloudPropertyGeneric { - -public: - ArduinoCloudPropertyInt(int & property, - int const min_delta, - String const & name, - permissionType const permission, - long const update_policy, - void(*fn)(void)) : - ArduinoCloudPropertyGeneric(name, INT, permission, update_policy, fn), - _property(property), - _shadow_property(property + 1), - _min_delta(min_delta) - { - } - - virtual bool newData() const override { - return (_property != _shadow_property && abs(_property - _shadow_property) >= _min_delta); - } - - virtual void updateShadow() override { - _shadow_property = _property; - } - - virtual void printinfo(Stream& stream) override { - stream.println("name: " + getName() + " value: " + String(_property) + " shadow: " + String(_shadow_property) + " permission: " + String(getPermission())); - } - - virtual void appendValue(CborEncoder* mapEncoder) override { - cbor_encode_text_stringz(mapEncoder, "v"); - cbor_encode_int(mapEncoder, _property); - }; - - bool write(bool const value) { - /* permissions are intended as seen from cloud */ - if (canWrite()) { - _property = value; - return true; - } - return false; - } - - bool read() const { - /* permissions are intended as seen from cloud */ - if (canRead()) { - return _property; - } - /* FIXME: What happens if we can not read? Compiler should complain there - * because there is no return value in case of the canRead() evaluating - * to false - */ - } - -private: - - int & _property; - int _shadow_property; - int _min_delta; - -}; - -#endif /* ARDUINO_CLOUD_PROPERTY_INT_H_ */ diff --git a/ArduinoCloudPropertyString.h b/ArduinoCloudPropertyString.h deleted file mode 100644 index 608b5bf3b..000000000 --- a/ArduinoCloudPropertyString.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef ARDUINO_CLOUD_PROPERTY_STRING_H_ -#define ARDUINO_CLOUD_PROPERTY_STRING_H_ - -#include "ArduinoCloudPropertyGeneric.h" - -class ArduinoCloudPropertyString : public ArduinoCloudPropertyGeneric { - -public: - ArduinoCloudPropertyString(String & property, - String const & name, - permissionType const permission, - long const update_policy, - void(*fn)(void)) : - ArduinoCloudPropertyGeneric(name, STRING, permission, update_policy, fn), - _property(property), - _shadow_property(property + "x") - { - } - - virtual bool newData() const override { - return (_property != _shadow_property); - } - - virtual void updateShadow() override { - _shadow_property = _property; - } - - virtual void printinfo(Stream& stream) override { - stream.println("name: " + getName() + " value: " + String(_property) + " shadow: " + String(_shadow_property) + " permission: " + String(getPermission())); - } - - virtual void appendValue(CborEncoder* mapEncoder) override { - cbor_encode_text_stringz(mapEncoder, "vs"); - cbor_encode_text_stringz(mapEncoder, _property.c_str()); - }; - - bool write(bool const value) { - /* permissions are intended as seen from cloud */ - if (canWrite()) { - _property = value; - return true; - } - return false; - } - - bool read() const { - /* permissions are intended as seen from cloud */ - if (canRead()) { - return _property; - } - /* FIXME: What happens if we can not read? Compiler should complain there - * because there is no return value in case of the canRead() evaluating - * to false - */ - } - -private: - - String & _property; - String _shadow_property; - -}; - -#endif /* ARDUINO_CLOUD_PROPERTY_STRING_H_ */ diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 2aed48a0f..df9ae5b75 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -1,9 +1,6 @@ #include + #include -#include -#include -#include -#include #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) extern "C" char *sbrk(int i); @@ -46,7 +43,7 @@ ArduinoCloudThing::ArduinoCloudThing() { void ArduinoCloudThing::begin() { status = ON; - addPropertyReal(status, "status", READ); + addProperty(status, "status", Permission::Read); } @@ -56,8 +53,11 @@ int ArduinoCloudThing::poll(uint8_t* data, size_t size) { // time interval may be elapsed or property may be changed int diff = 0; - // are there some changed prperies??? - diff = checkNewData(); + diff += _bool_property_list.cntNumberOfPropertiesWhichShouldBeUpdated (); + diff += _int_property_list.cntNumberOfPropertiesWhichShouldBeUpdated (); + diff += _float_property_list.cntNumberOfPropertiesWhichShouldBeUpdated (); + diff += _string_property_list.cntNumberOfPropertiesWhichShouldBeUpdated(); + if (diff > 0) { CborError err; CborEncoder encoder, arrayEncoder; @@ -70,22 +70,13 @@ int ArduinoCloudThing::poll(uint8_t* data, size_t size) { return -1; } - for (int i = 0; i < list.size(); i++) { - ArduinoCloudPropertyGeneric *p = list.get(i); - // If a property should be updated and has read permission from the Cloud point of view - if (p->shouldBeUpdated() && p->canRead()) { - // create a cbor object for the property and automatically add it into array - p->append(&arrayEncoder); - } - } + _bool_property_list.appendIfPropertyShouldBeUpdated (&arrayEncoder); + _int_property_list.appendIfPropertyShouldBeUpdated (&arrayEncoder); + _float_property_list.appendIfPropertyShouldBeUpdated (&arrayEncoder); + _string_property_list.appendIfPropertyShouldBeUpdated(&arrayEncoder); - err = cbor_encoder_close_container(&encoder, &arrayEncoder); - // update properties shadow values, in order to check if variable has changed since last publish - for (int i = 0; i < list.size(); i++) { - ArduinoCloudPropertyGeneric *p = list.get(i); - p->updateShadow(); - } + err = cbor_encoder_close_container(&encoder, &arrayEncoder); // return the number of byte of the CBOR encoded array return cbor_encoder_get_buffer_size(&encoder, data); @@ -98,81 +89,38 @@ int ArduinoCloudThing::poll(uint8_t* data, size_t size) { return diff; } -int ArduinoCloudThing::checkNewData() { - int counter = 0; - for (int i = 0; i < list.size(); i++) { - ArduinoCloudPropertyGeneric *p = list.get(i); - if (p->shouldBeUpdated() && p->canRead()) { - counter++; - } - } - // return number of props that has to be updated - return counter; -} - -ArduinoCloudPropertyGeneric* ArduinoCloudThing::exists(String &name) { - for (int i = 0; i < list.size(); i++) { - ArduinoCloudPropertyGeneric *p = list.get(i); - // Check the property existance just comparing its name with existent ones - if (p->getName() == name) { - return p; - } - } - return NULL; -} - -// It return the index of the property, inside the local array, with the name passed as parameter. (-1 if it does not exist.) -int ArduinoCloudThing::findPropertyByName(String &name) { - for (int i = 0; i < list.size(); i++) { - ArduinoCloudPropertyGeneric *p = list.get(i); - // Check the property existance just comparing its name with existent ones - if (p->getName() == name) { - return i; - } - } - return -1; +ArduinoCloudProperty & ArduinoCloudThing::addProperty(bool & property, String const & name, Permission const permission) { + ArduinoCloudProperty * property_opj = _bool_property_list[name]; + if(!property_opj) { + property_opj = new ArduinoCloudProperty(property, name, permission); + } + return (*property_opj); } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(int& property, String name, permissionType permission, long seconds, void(*fn)(void), int minDelta) { - if (ArduinoCloudPropertyGeneric* p = exists(name)) { - return *p; - } - // If a property with ythis name does not exist, create it into thing - ArduinoCloudPropertyInt *propertyObj = new ArduinoCloudPropertyInt(property, minDelta, name, permission, seconds, fn); - // Add the new property to the thin properties list - list.add(propertyObj); - // Return the new property as a generic one - return *(reinterpret_cast(propertyObj)); -} - -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(bool& property, String name, permissionType permission, long seconds, void(*fn)(void), bool minDelta) { - if (ArduinoCloudPropertyGeneric* p = exists(name)) { - return *p; - } - ArduinoCloudPropertyBool *propertyObj = new ArduinoCloudPropertyBool(property, name, permission, seconds, fn); - list.add(propertyObj); - return *(reinterpret_cast(propertyObj)); +ArduinoCloudProperty & ArduinoCloudThing::addProperty(int & property, String const & name, Permission const permission) { + ArduinoCloudProperty * property_opj = _int_property_list[name]; + if(!property_opj) { + property_opj = new ArduinoCloudProperty(property, name, permission); + } + return (*property_opj); } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(float& property, String name, permissionType permission, long seconds, void(*fn)(void), float minDelta) { - if (ArduinoCloudPropertyGeneric* p = exists(name)) { - return *p; - } - ArduinoCloudPropertyFloat *propertyObj = new ArduinoCloudPropertyFloat(property, minDelta, name, permission, seconds, fn); - list.add(propertyObj); - return *(reinterpret_cast(propertyObj)); +ArduinoCloudProperty & ArduinoCloudThing::addProperty(float & property, String const & name, Permission const permission) { + ArduinoCloudProperty * property_opj = _float_property_list[name]; + if(!property_opj) { + property_opj = new ArduinoCloudProperty(property, name, permission); + } + return (*property_opj); } -ArduinoCloudPropertyGeneric& ArduinoCloudThing::addPropertyReal(String& property, String name, permissionType permission, long seconds, void(*fn)(void), String minDelta) { - if (ArduinoCloudPropertyGeneric* p = exists(name)) { - return *p; - } - ArduinoCloudPropertyString *propertyObj = new ArduinoCloudPropertyString(property, name, permission, seconds, fn); - list.add(propertyObj); - return *(reinterpret_cast(propertyObj)); +ArduinoCloudProperty & ArduinoCloudThing::addProperty(String & property, String const & name, Permission const permission) { + ArduinoCloudProperty * property_opj = _string_property_list[name]; + if(!property_opj) { + property_opj = new ArduinoCloudProperty(property, name, permission); + } + return (*property_opj); } - void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { CborError err; @@ -180,7 +128,7 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { CborValue recursedMap, propValue, dataArray; int propId; String propName; - propertyType propType; + Type propType; err = cbor_parser_init(payload, length, 0, &parser, &dataArray); if(err) { @@ -247,88 +195,81 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { // used to avoid memory leaks (cbor_value_dup_text_string automatically perform a malloc) free(nameVal); - // Search for the index of the device property with that name - propId = findPropertyByName(propName); + // Search for the device property with that name + ArduinoCloudProperty * bool_property = _bool_property_list [propName]; + ArduinoCloudProperty * int_property = _int_property_list [propName]; + ArduinoCloudProperty * float_property = _float_property_list [propName]; + ArduinoCloudProperty * string_property = _string_property_list[propName]; + // If property does not exist, skip it and do nothing. - if (propId < 0) { - cbor_value_advance(&recursedMap); - continue; + if((bool_property == 0) && (int_property == 0) && (float_property == 0) && (string_property == 0)) + { + cbor_value_advance(&recursedMap); + continue; } - ArduinoCloudPropertyGeneric* property = list.get(propId); - // Check for the property type, write method internally check for the permission - propType = property->getType(); + if (bool_property == 0) propType = Type::Bool; + else if(int_property == 0) propType = Type::Int; + else if(float_property == 0) propType = Type::Float; + else if(string_property == 0) propType = Type::String; - if (propType == FLOAT && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { + if (propType == Type::Float && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { if (propValue.type == CborDoubleType) { double val; // get the value of the property as a double cbor_value_get_double(&propValue, &val); - ArduinoCloudPropertyFloat* p = (ArduinoCloudPropertyFloat*) property; - p->write((float)val); + float_property->write(static_cast(val)); } else if (propValue.type == CborIntegerType) { int val; cbor_value_get_int(&propValue, &val); - ArduinoCloudPropertyFloat* p = (ArduinoCloudPropertyFloat*) property; - p->write((float)val); + float_property->write(static_cast(val)); } else if (propValue.type == CborFloatType) { float val; cbor_value_get_float(&propValue, &val); - ArduinoCloudPropertyFloat* p = (ArduinoCloudPropertyFloat*) property; - p->write(val); + float_property->write(val); } else if (propValue.type == CborHalfFloatType) { float val; cbor_value_get_half_float(&propValue, &val); - ArduinoCloudPropertyFloat * p = (ArduinoCloudPropertyFloat*) property; - p->write(val); + float_property->write(val); } - } else if (propType == INT && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { + float_property->execCallbackOnChange(); + } else if (propType == Type::Int && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { // if no key proper key was found, do nothing if (propValue.type == CborIntegerType) { int val; cbor_value_get_int(&propValue, &val); - ArduinoCloudPropertyInt* p = (ArduinoCloudPropertyInt*) property; - p->write(val); + int_property->write(val); } else if (propValue.type == CborDoubleType) { // If a double value is received, a cast to int is performed(so it is still accepted) double val; cbor_value_get_double(&propValue, &val); - ArduinoCloudPropertyInt* p = (ArduinoCloudPropertyInt*) property; - p->write((int)val); + int_property->write(static_cast(val)); } else if (propValue.type == CborFloatType) { float val; cbor_value_get_float(&propValue, &val); - ArduinoCloudPropertyInt* p = (ArduinoCloudPropertyInt*) property; - p->write((int)val); + int_property->write(static_cast(val)); } else if (propValue.type == CborHalfFloatType) { float val; cbor_value_get_half_float(&propValue, &val); - ArduinoCloudPropertyInt* p = (ArduinoCloudPropertyInt*) property; - p->write((int)val); + int_property->write(static_cast(val)); } - } else if (propType == BOOL && !cbor_value_map_find_value(&recursedMap, "vb", &propValue)) { + int_property->execCallbackOnChange(); + } else if (propType == Type::Bool && !cbor_value_map_find_value(&recursedMap, "vb", &propValue)) { if (propValue.type == CborBooleanType) { bool val; cbor_value_get_boolean(&propValue, &val); - ArduinoCloudPropertyBool* p = (ArduinoCloudPropertyBool*) property; - p->write(val); + bool_property->write(val); + bool_property->execCallbackOnChange(); } - } else if (propType == STRING && !cbor_value_map_find_value(&recursedMap, "vs", &propValue)){ + } else if (propType == Type::String && !cbor_value_map_find_value(&recursedMap, "vs", &propValue)){ if (propValue.type == CborTextStringType) { char *val; size_t valSize; err = cbor_value_dup_text_string(&propValue, &val, &valSize, &propValue); - ArduinoCloudPropertyString* p = (ArduinoCloudPropertyString*) property; - // Char* string transformed into array - p->write(String((char*)val)); + string_property->write(static_cast(val)); + string_property->execCallbackOnChange(); free(val); } } - // If the property has been changed call its callback - if (property->newData()) { - if (property->callback != NULL) { - property->callback(); - } - } // Continue to scan the cbor map err = cbor_value_advance(&recursedMap); diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 0a94794ee..7967a3a05 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -4,9 +4,9 @@ #include #include -#include "ArduinoCloudPropertyGeneric.h" +#include "ArduinoCloudProperty.hpp" +#include "ArduinoCloudPropertyContainer.hpp" -#include "lib/tinycbor/cbor-lib.h" #include "lib/LinkedList/LinkedList.h" enum boolStatus { @@ -25,11 +25,12 @@ class ArduinoCloudThing { public: ArduinoCloudThing(); void begin(); - // overload, with default arguments, of different type of properties - ArduinoCloudPropertyGeneric& addPropertyReal(int& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, int minDelta = 0); - ArduinoCloudPropertyGeneric& addPropertyReal(bool& property, String name, permissionType permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, bool minDelta = false); - ArduinoCloudPropertyGeneric& addPropertyReal(float& property, String name, permissionType _permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, float minDelta = 0.0f); - ArduinoCloudPropertyGeneric& addPropertyReal(String& property, String name, permissionType permission = READWRITE, long seconds = ON_CHANGE, void(*fn)(void) = NULL, String minDelta = ""); + + ArduinoCloudProperty & addProperty(bool & property, String const & name, Permission const permission); + ArduinoCloudProperty & addProperty(int & property, String const & name, Permission const permission); + ArduinoCloudProperty & addProperty(float & property, String const & name, Permission const permission); + ArduinoCloudProperty & addProperty(String & property, String const & name, Permission const permission); + // poll should return > 0 if something has changed int poll(uint8_t* data, size_t size); // decode a CBOR payload received from the Cloud. @@ -37,16 +38,14 @@ class ArduinoCloudThing { private: void update(); - int checkNewData(); - // return the index of that property in the linked list, -1 if it does not exist. - int findPropertyByName(String &name); - // return the pointer to the desired property, NULL if it does not exist. - ArduinoCloudPropertyGeneric* exists(String &name); bool status = OFF; char uuid[33]; - int currentListIndex = -1; - LinkedList list; + + ArduinoCloudPropertyContainer _bool_property_list; + ArduinoCloudPropertyContainer _int_property_list; + ArduinoCloudPropertyContainer _float_property_list; + ArduinoCloudPropertyContainer _string_property_list; }; From cf6a2a5a3d85195d39b2f9c7a006d4142a16ebe7 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Mon, 15 Oct 2018 16:35:02 +0200 Subject: [PATCH 069/175] Fixing a couple of bugs in the new refactored implementation - now all tests pass --- ArduinoCloudProperty.hpp | 24 ++++++++-------------- ArduinoCloudPropertyContainer.hpp | 4 ++-- ArduinoCloudThing.cpp | 33 +++++++++++++++++-------------- 3 files changed, 28 insertions(+), 33 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index e7c983ef4..b1205bf16 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -24,17 +24,16 @@ class ArduinoCloudProperty { ArduinoCloudProperty(T & property, String const & name, Permission const permission); - bool write(T const val); - bool read (T * val) const; + bool writeByCloud(T const val); void onUpdate (UpdateCallbackFunc func ); void publishOnChange(T const min_delta_property); void publishEvery (unsigned long const seconds ); - inline String name () const { return _name; } - Type type () const; - inline bool canRead () const { return (_permission == Permission::Read ) || (_permission == Permission::ReadWrite); } - inline bool canWrite() const { return (_permission == Permission::Write) || (_permission == Permission::ReadWrite); } + inline String name () const { return _name; } + Type type () const; + inline bool isReadableByCloud () const { return (_permission == Permission::Read ) || (_permission == Permission::ReadWrite); } + inline bool isWriteableByCloud() const { return (_permission == Permission::Write) || (_permission == Permission::ReadWrite); } bool shouldBeUpdated () const; void execCallbackOnChange (); @@ -80,20 +79,13 @@ ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, } template -bool ArduinoCloudProperty::write(T const val) { - if(!canWrite()) return false; +bool ArduinoCloudProperty::writeByCloud(T const val) { + if(!isWriteableByCloud()) return false; _property = val; /* _shadow_property is not updated so there will be an update the next time around */ return true; } -template -bool ArduinoCloudProperty::read(T * val) const { - if(!canRead()) return false; - *val = _property; - return true; -} - template void ArduinoCloudProperty::onUpdate(UpdateCallbackFunc func) { _update_callback_func = func; @@ -158,7 +150,7 @@ void ArduinoCloudProperty::execCallbackOnChange() { template void ArduinoCloudProperty::append(CborEncoder * encoder) { - if (canRead()) { + if (isReadableByCloud()) { CborEncoder mapEncoder; cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); diff --git a/ArduinoCloudPropertyContainer.hpp b/ArduinoCloudPropertyContainer.hpp index 8d2538937..dfdaf736c 100644 --- a/ArduinoCloudPropertyContainer.hpp +++ b/ArduinoCloudPropertyContainer.hpp @@ -41,7 +41,7 @@ int ArduinoCloudPropertyContainer::cntNumberOfPropertiesWhichShouldBeUpdated( for (int i = 0; i < _list.size(); i++) { ArduinoCloudProperty * p = _list.get(i); - if (p->shouldBeUpdated() && p->canRead()) { + if (p->shouldBeUpdated() && p->isReadableByCloud()) { should_be_updated_cnt++; } } @@ -53,7 +53,7 @@ template void ArduinoCloudPropertyContainer::appendIfPropertyShouldBeUpdated(CborEncoder * arrayEncoder) { for (int i = 0; i < _list.size(); i++) { ArduinoCloudProperty * p = _list.get(i); - if (p->shouldBeUpdated() && p->canRead()) { + if (p->shouldBeUpdated() && p->isReadableByCloud()) { p->append(arrayEncoder); } } diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index df9ae5b75..baaba2f1b 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -75,7 +75,6 @@ int ArduinoCloudThing::poll(uint8_t* data, size_t size) { _float_property_list.appendIfPropertyShouldBeUpdated (&arrayEncoder); _string_property_list.appendIfPropertyShouldBeUpdated(&arrayEncoder); - err = cbor_encoder_close_container(&encoder, &arrayEncoder); // return the number of byte of the CBOR encoded array @@ -93,6 +92,7 @@ ArduinoCloudProperty & ArduinoCloudThing::addProperty(bool & property, Str ArduinoCloudProperty * property_opj = _bool_property_list[name]; if(!property_opj) { property_opj = new ArduinoCloudProperty(property, name, permission); + _bool_property_list.add(property_opj); } return (*property_opj); } @@ -101,6 +101,7 @@ ArduinoCloudProperty & ArduinoCloudThing::addProperty(int & property, Strin ArduinoCloudProperty * property_opj = _int_property_list[name]; if(!property_opj) { property_opj = new ArduinoCloudProperty(property, name, permission); + _int_property_list.add(property_opj); } return (*property_opj); } @@ -109,6 +110,7 @@ ArduinoCloudProperty & ArduinoCloudThing::addProperty(float & property, ArduinoCloudProperty * property_opj = _float_property_list[name]; if(!property_opj) { property_opj = new ArduinoCloudProperty(property, name, permission); + _float_property_list.add(property_opj); } return (*property_opj); } @@ -117,6 +119,7 @@ ArduinoCloudProperty & ArduinoCloudThing::addProperty(String & property, ArduinoCloudProperty * property_opj = _string_property_list[name]; if(!property_opj) { property_opj = new ArduinoCloudProperty(property, name, permission); + _string_property_list.add(property_opj); } return (*property_opj); } @@ -208,29 +211,29 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { continue; } - if (bool_property == 0) propType = Type::Bool; - else if(int_property == 0) propType = Type::Int; - else if(float_property == 0) propType = Type::Float; - else if(string_property == 0) propType = Type::String; + if (bool_property != 0) propType = Type::Bool; + else if(int_property != 0) propType = Type::Int; + else if(float_property != 0) propType = Type::Float; + else if(string_property != 0) propType = Type::String; if (propType == Type::Float && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { if (propValue.type == CborDoubleType) { double val; // get the value of the property as a double cbor_value_get_double(&propValue, &val); - float_property->write(static_cast(val)); + float_property->writeByCloud(static_cast(val)); } else if (propValue.type == CborIntegerType) { int val; cbor_value_get_int(&propValue, &val); - float_property->write(static_cast(val)); + float_property->writeByCloud(static_cast(val)); } else if (propValue.type == CborFloatType) { float val; cbor_value_get_float(&propValue, &val); - float_property->write(val); + float_property->writeByCloud(val); } else if (propValue.type == CborHalfFloatType) { float val; cbor_value_get_half_float(&propValue, &val); - float_property->write(val); + float_property->writeByCloud(val); } float_property->execCallbackOnChange(); } else if (propType == Type::Int && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { @@ -238,34 +241,34 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { if (propValue.type == CborIntegerType) { int val; cbor_value_get_int(&propValue, &val); - int_property->write(val); + int_property->writeByCloud(val); } else if (propValue.type == CborDoubleType) { // If a double value is received, a cast to int is performed(so it is still accepted) double val; cbor_value_get_double(&propValue, &val); - int_property->write(static_cast(val)); + int_property->writeByCloud(static_cast(val)); } else if (propValue.type == CborFloatType) { float val; cbor_value_get_float(&propValue, &val); - int_property->write(static_cast(val)); + int_property->writeByCloud(static_cast(val)); } else if (propValue.type == CborHalfFloatType) { float val; cbor_value_get_half_float(&propValue, &val); - int_property->write(static_cast(val)); + int_property->writeByCloud(static_cast(val)); } int_property->execCallbackOnChange(); } else if (propType == Type::Bool && !cbor_value_map_find_value(&recursedMap, "vb", &propValue)) { if (propValue.type == CborBooleanType) { bool val; cbor_value_get_boolean(&propValue, &val); - bool_property->write(val); + bool_property->writeByCloud(val); bool_property->execCallbackOnChange(); } } else if (propType == Type::String && !cbor_value_map_find_value(&recursedMap, "vs", &propValue)){ if (propValue.type == CborTextStringType) { char *val; size_t valSize; err = cbor_value_dup_text_string(&propValue, &val, &valSize, &propValue); - string_property->write(static_cast(val)); + string_property->writeByCloud(static_cast(val)); string_property->execCallbackOnChange(); free(val); } From 3caca6d9a4994e196e93a69a13959034a87115b6 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 16 Oct 2018 07:20:02 +0200 Subject: [PATCH 070/175] Make the methods onUpdate/publishOnChange/publishEvery composable by returning a reference to the instance of the object --- ArduinoCloudProperty.hpp | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index b1205bf16..6459d4ac3 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -26,12 +26,12 @@ class ArduinoCloudProperty { bool writeByCloud(T const val); - void onUpdate (UpdateCallbackFunc func ); - void publishOnChange(T const min_delta_property); - void publishEvery (unsigned long const seconds ); + /* Composable configuration of the ArduinoCloudProperty class */ + ArduinoCloudProperty & onUpdate (UpdateCallbackFunc func ); + ArduinoCloudProperty & publishOnChange(T const min_delta_property); + ArduinoCloudProperty & publishEvery (unsigned long const seconds ); inline String name () const { return _name; } - Type type () const; inline bool isReadableByCloud () const { return (_permission == Permission::Read ) || (_permission == Permission::ReadWrite); } inline bool isWriteableByCloud() const { return (_permission == Permission::Write) || (_permission == Permission::ReadWrite); } @@ -87,41 +87,23 @@ bool ArduinoCloudProperty::writeByCloud(T const val) { } template -void ArduinoCloudProperty::onUpdate(UpdateCallbackFunc func) { +ArduinoCloudProperty & ArduinoCloudProperty::onUpdate(UpdateCallbackFunc func) { _update_callback_func = func; + return (*this); } template -void ArduinoCloudProperty::publishOnChange(T const min_delta_property) { +ArduinoCloudProperty & ArduinoCloudProperty::publishOnChange(T const min_delta_property) { _update_policy = UpdatePolicy::OnChange; _min_delta_property = min_delta_property; + return (*this); } template -void ArduinoCloudProperty::publishEvery(unsigned long const seconds) { +ArduinoCloudProperty & ArduinoCloudProperty::publishEvery(unsigned long const seconds) { _update_policy = UpdatePolicy::TimeInterval; _update_interval_sec = seconds; -} - - -template <> -inline Type ArduinoCloudProperty::type() const { - return Type::Bool; -} - -template <> -inline Type ArduinoCloudProperty::type() const { - return Type::Int; -} - -template <> -inline Type ArduinoCloudProperty::type() const { - return Type::Float; -} - -template <> -inline Type ArduinoCloudProperty::type() const { - return Type::String; + return (*this); } template From e3e98b9412b74a31e9ef3dba560bf7012c3c135e Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 16 Oct 2018 08:28:21 +0200 Subject: [PATCH 071/175] Rewriting ArduinoCloudPropertyContainer in order to provide a considerable cleaner interface --- ArduinoCloudProperty.hpp | 2 + ArduinoCloudPropertyContainer.cpp | 27 +++++++++++ ArduinoCloudPropertyContainer.hpp | 81 +++++++++++++------------------ ArduinoCloudPropertyContainer.ipp | 42 ++++++++++++++++ ArduinoCloudThing.cpp | 76 ++++++++++++++--------------- ArduinoCloudThing.h | 6 +-- 6 files changed, 144 insertions(+), 90 deletions(-) create mode 100644 ArduinoCloudPropertyContainer.cpp create mode 100644 ArduinoCloudPropertyContainer.ipp diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 6459d4ac3..12fa3eecc 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -1,6 +1,8 @@ #ifndef ARDUINO_CLOUD_PROPERTY_HPP_ #define ARDUINO_CLOUD_PROPERTY_HPP_ +#include + #include "lib/tinycbor/cbor-lib.h" enum class Permission { diff --git a/ArduinoCloudPropertyContainer.cpp b/ArduinoCloudPropertyContainer.cpp new file mode 100644 index 000000000..6587ef6ac --- /dev/null +++ b/ArduinoCloudPropertyContainer.cpp @@ -0,0 +1,27 @@ +#include "ArduinoCloudPropertyContainer.hpp" + +bool ArduinoCloudPropertyContainer::isPropertyInContainer(Type const type, String const & name) { + if (type == Type::Bool ) return isPropertyInList(_bool_property_list, name); + else if (type == Type::Int ) return isPropertyInList(_int_property_list, name); + else if (type == Type::Float ) return isPropertyInList(_float_property_list, name); + else if (type == Type::String) return isPropertyInList(_string_property_list, name); + else return false; +} + +int ArduinoCloudPropertyContainer::getNumOfChangedProperties() { + int num_changes_properties = 0; + + num_changes_properties += getNumOfChangedProperties(_bool_property_list ); + num_changes_properties += getNumOfChangedProperties(_int_property_list ); + num_changes_properties += getNumOfChangedProperties(_float_property_list ); + num_changes_properties += getNumOfChangedProperties(_string_property_list); + + return num_changes_properties; +} + +void ArduinoCloudPropertyContainer::appendChangedProperties(CborEncoder * arrayEncoder) { + appendChangedProperties (_bool_property_list, arrayEncoder); + appendChangedProperties (_int_property_list, arrayEncoder); + appendChangedProperties (_float_property_list, arrayEncoder); + appendChangedProperties(_string_property_list, arrayEncoder); +} diff --git a/ArduinoCloudPropertyContainer.hpp b/ArduinoCloudPropertyContainer.hpp index dfdaf736c..486922850 100644 --- a/ArduinoCloudPropertyContainer.hpp +++ b/ArduinoCloudPropertyContainer.hpp @@ -1,62 +1,49 @@ -#ifndef ARDUINO_CLOUD_PROPERTY_CONTAINER_H_ -#define ARDUINO_CLOUD_PROPERTY_CONTAINER_H_ +#ifndef ARDUINO_CLOUD_PROPERTY_CONTAINER_NEW_H_ +#define ARDUINO_CLOUD_PROPERTY_CONTAINER_NEW_H_ #include "ArduinoCloudProperty.hpp" #include "lib/tinycbor/cbor-lib.h" #include "lib/LinkedList/LinkedList.h" -template class ArduinoCloudPropertyContainer { public: - void add(ArduinoCloudProperty * property_obj); - ArduinoCloudProperty * operator [] (String const & name); - int cntNumberOfPropertiesWhichShouldBeUpdated(); - void appendIfPropertyShouldBeUpdated(CborEncoder * arrayEncoder); + bool isPropertyInContainer (Type const type, String const & name); + int getNumOfChangedProperties(); + void appendChangedProperties (CborEncoder * arrayEncoder); + + inline ArduinoCloudProperty * getPropertyBool (String const & name) { return getProperty(_bool_property_list, name); } + inline ArduinoCloudProperty * getPropertyInt (String const & name) { return getProperty(_int_property_list, name); } + inline ArduinoCloudProperty * getPropertyFloat (String const & name) { return getProperty(_float_property_list, name); } + inline ArduinoCloudProperty * getPropertyString(String const & name) { return getProperty(_string_property_list, name); } + + inline void addProperty(ArduinoCloudProperty * property_obj) { _bool_property_list.add (property_obj); } + inline void addProperty(ArduinoCloudProperty * property_obj) { _int_property_list.add (property_obj); } + inline void addProperty(ArduinoCloudProperty * property_obj) { _float_property_list.add (property_obj); } + inline void addProperty(ArduinoCloudProperty * property_obj) { _string_property_list.add(property_obj); } private: - LinkedList *> _list; + LinkedList *> _bool_property_list; + LinkedList *> _int_property_list; + LinkedList *> _float_property_list; + LinkedList *> _string_property_list; + + template + bool isPropertyInList(LinkedList *> & list, String const & name); + + template + ArduinoCloudProperty * getProperty(LinkedList *> & list, String const & name); + + template + int getNumOfChangedProperties(LinkedList *> & list); + + template + void appendChangedProperties(LinkedList *> & list, CborEncoder * arrayEncoder); }; -template -void ArduinoCloudPropertyContainer::add(ArduinoCloudProperty * property_obj) { - _list.add(property_obj); -} - -template -ArduinoCloudProperty * ArduinoCloudPropertyContainer::operator [] (String const & name) { - for (int i = 0; i < _list.size(); i++) { - ArduinoCloudProperty * p = _list.get(i); - if (p->name() == name) return p; - } - return 0; -} - -template -int ArduinoCloudPropertyContainer::cntNumberOfPropertiesWhichShouldBeUpdated() { - int should_be_updated_cnt = 0; - - for (int i = 0; i < _list.size(); i++) { - ArduinoCloudProperty * p = _list.get(i); - if (p->shouldBeUpdated() && p->isReadableByCloud()) { - should_be_updated_cnt++; - } - } - - return should_be_updated_cnt; -} - -template -void ArduinoCloudPropertyContainer::appendIfPropertyShouldBeUpdated(CborEncoder * arrayEncoder) { - for (int i = 0; i < _list.size(); i++) { - ArduinoCloudProperty * p = _list.get(i); - if (p->shouldBeUpdated() && p->isReadableByCloud()) { - p->append(arrayEncoder); - } - } -} - -#endif /* ARDUINO_CLOUD_PROPERTY_CONTAINER_H_ */ +#include "ArduinoCloudPropertyContainer.ipp" + +#endif /* ARDUINO_CLOUD_PROPERTY_CONTAINER_NEW_H_ */ diff --git a/ArduinoCloudPropertyContainer.ipp b/ArduinoCloudPropertyContainer.ipp new file mode 100644 index 000000000..851d8b37a --- /dev/null +++ b/ArduinoCloudPropertyContainer.ipp @@ -0,0 +1,42 @@ + +template +bool ArduinoCloudPropertyContainer::isPropertyInList(LinkedList *> & list, String const & name) { + for (int i = 0; i < list.size(); i++) { + ArduinoCloudProperty * p = list.get(i); + if (p->name() == name) return true; + } + return false; +} + +template +ArduinoCloudProperty * ArduinoCloudPropertyContainer::getProperty(LinkedList *> & list, String const & name) { + for (int i = 0; i < list.size(); i++) { + ArduinoCloudProperty * p = list.get(i); + if (p->name() == name) return p; + } + return 0; +} + +template +int ArduinoCloudPropertyContainer::getNumOfChangedProperties(LinkedList *> & list) { + int num_changes_properties = 0; + + for (int i = 0; i < list.size(); i++) { + ArduinoCloudProperty * p = list.get(i); + if (p->shouldBeUpdated() && p->isReadableByCloud()) { + num_changes_properties++; + } + } + + return num_changes_properties; +} + +template +void ArduinoCloudPropertyContainer::appendChangedProperties(LinkedList *> & list, CborEncoder * arrayEncoder) { + for (int i = 0; i < list.size(); i++) { + ArduinoCloudProperty * p = list.get(i); + if (p->shouldBeUpdated() && p->isReadableByCloud()) { + p->append(arrayEncoder); + } + } +} diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index baaba2f1b..470598cdb 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -51,29 +51,21 @@ int ArduinoCloudThing::poll(uint8_t* data, size_t size) { // check if backing storage and cloud has diverged // time interval may be elapsed or property may be changed - int diff = 0; + int const num_changed_properties = _property_cont.getNumOfChangedProperties(); - diff += _bool_property_list.cntNumberOfPropertiesWhichShouldBeUpdated (); - diff += _int_property_list.cntNumberOfPropertiesWhichShouldBeUpdated (); - diff += _float_property_list.cntNumberOfPropertiesWhichShouldBeUpdated (); - diff += _string_property_list.cntNumberOfPropertiesWhichShouldBeUpdated(); - - if (diff > 0) { + if (num_changed_properties > 0) { CborError err; CborEncoder encoder, arrayEncoder; cbor_encoder_init(&encoder, data, size, 0); // create a cbor array containing the property that should be updated. - err = cbor_encoder_create_array(&encoder, &arrayEncoder, diff); + err = cbor_encoder_create_array(&encoder, &arrayEncoder, num_changed_properties); if (err) { //Serial.println(cbor_error_string(err)); return -1; } - _bool_property_list.appendIfPropertyShouldBeUpdated (&arrayEncoder); - _int_property_list.appendIfPropertyShouldBeUpdated (&arrayEncoder); - _float_property_list.appendIfPropertyShouldBeUpdated (&arrayEncoder); - _string_property_list.appendIfPropertyShouldBeUpdated(&arrayEncoder); + _property_cont.appendChangedProperties(&arrayEncoder); err = cbor_encoder_close_container(&encoder, &arrayEncoder); @@ -85,43 +77,51 @@ int ArduinoCloudThing::poll(uint8_t* data, size_t size) { PrintFreeRam(); #endif // If nothing has to be sent, return diff, that is 0 in this case - return diff; + return num_changed_properties; } ArduinoCloudProperty & ArduinoCloudThing::addProperty(bool & property, String const & name, Permission const permission) { - ArduinoCloudProperty * property_opj = _bool_property_list[name]; - if(!property_opj) { - property_opj = new ArduinoCloudProperty(property, name, permission); - _bool_property_list.add(property_opj); + if(_property_cont.isPropertyInContainer(Type::Bool, name)) { + return (*_property_cont.getPropertyBool(name)); + } + else { + ArduinoCloudProperty *property_opj = new ArduinoCloudProperty(property, name, permission); + _property_cont.addProperty(property_opj); + return (*property_opj); } - return (*property_opj); } ArduinoCloudProperty & ArduinoCloudThing::addProperty(int & property, String const & name, Permission const permission) { - ArduinoCloudProperty * property_opj = _int_property_list[name]; - if(!property_opj) { - property_opj = new ArduinoCloudProperty(property, name, permission); - _int_property_list.add(property_opj); + if(_property_cont.isPropertyInContainer(Type::Int, name)) { + return (*_property_cont.getPropertyInt(name)); + } + else { + ArduinoCloudProperty * property_opj = new ArduinoCloudProperty(property, name, permission); + _property_cont.addProperty(property_opj); + return (*property_opj); } - return (*property_opj); } -ArduinoCloudProperty & ArduinoCloudThing::addProperty(float & property, String const & name, Permission const permission) { - ArduinoCloudProperty * property_opj = _float_property_list[name]; - if(!property_opj) { - property_opj = new ArduinoCloudProperty(property, name, permission); - _float_property_list.add(property_opj); +ArduinoCloudProperty & ArduinoCloudThing::addProperty(float & property, String const & name, Permission const permission) { + if(_property_cont.isPropertyInContainer(Type::Float, name)) { + return (*_property_cont.getPropertyFloat(name)); + } + else { + ArduinoCloudProperty * property_opj = new ArduinoCloudProperty(property, name, permission); + _property_cont.addProperty(property_opj); + return (*property_opj); } - return (*property_opj); } ArduinoCloudProperty & ArduinoCloudThing::addProperty(String & property, String const & name, Permission const permission) { - ArduinoCloudProperty * property_opj = _string_property_list[name]; - if(!property_opj) { - property_opj = new ArduinoCloudProperty(property, name, permission); - _string_property_list.add(property_opj); + if(_property_cont.isPropertyInContainer(Type::String, name)) { + return (*_property_cont.getPropertyString(name)); + } + else { + ArduinoCloudProperty * property_opj = new ArduinoCloudProperty(property, name, permission); + _property_cont.addProperty(property_opj); + return (*property_opj); } - return (*property_opj); } void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { @@ -199,10 +199,10 @@ void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { free(nameVal); // Search for the device property with that name - ArduinoCloudProperty * bool_property = _bool_property_list [propName]; - ArduinoCloudProperty * int_property = _int_property_list [propName]; - ArduinoCloudProperty * float_property = _float_property_list [propName]; - ArduinoCloudProperty * string_property = _string_property_list[propName]; + ArduinoCloudProperty * bool_property = _property_cont.getPropertyBool (propName); + ArduinoCloudProperty * int_property = _property_cont.getPropertyInt (propName); + ArduinoCloudProperty * float_property = _property_cont.getPropertyFloat (propName); + ArduinoCloudProperty * string_property = _property_cont.getPropertyString(propName); // If property does not exist, skip it and do nothing. if((bool_property == 0) && (int_property == 0) && (float_property == 0) && (string_property == 0)) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 7967a3a05..8ea6cfd9a 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -42,11 +42,7 @@ class ArduinoCloudThing { bool status = OFF; char uuid[33]; - ArduinoCloudPropertyContainer _bool_property_list; - ArduinoCloudPropertyContainer _int_property_list; - ArduinoCloudPropertyContainer _float_property_list; - ArduinoCloudPropertyContainer _string_property_list; - + ArduinoCloudPropertyContainer _property_cont; }; #endif /* ARDUINO_CLOUD_THING_H_ */ From bbc50a4c4499fa54bf183de9f225664f50b64fe3 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 16 Oct 2018 08:42:23 +0200 Subject: [PATCH 072/175] Adding new update policy OnChangeRateLimited where data is published on the change of the value but only, if a given minimum amount of time has passed between two consecutive updates --- ArduinoCloudProperty.hpp | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 12fa3eecc..c758b8b6b 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -14,7 +14,7 @@ enum class Type { }; enum class UpdatePolicy { - OnChange, TimeInterval + OnChange, OnChangeRateLimited, TimeInterval }; typedef void(*UpdateCallbackFunc)(void); @@ -29,9 +29,11 @@ class ArduinoCloudProperty { bool writeByCloud(T const val); /* Composable configuration of the ArduinoCloudProperty class */ - ArduinoCloudProperty & onUpdate (UpdateCallbackFunc func ); - ArduinoCloudProperty & publishOnChange(T const min_delta_property); - ArduinoCloudProperty & publishEvery (unsigned long const seconds ); + ArduinoCloudProperty & onUpdate (UpdateCallbackFunc func ); + ArduinoCloudProperty & setMinDelta (T const min_delta_property ); + ArduinoCloudProperty & publishOnChange ( ); + ArduinoCloudProperty & publishOnChangeRateLimited(unsigned long const min_time_between_updates_milliseconds); + ArduinoCloudProperty & publishEvery (unsigned long const seconds ); inline String name () const { return _name; } inline bool isReadableByCloud () const { return (_permission == Permission::Read ) || (_permission == Permission::ReadWrite); } @@ -52,8 +54,10 @@ class ArduinoCloudProperty { UpdatePolicy _update_policy; bool _has_been_updated_once; - /* Variables used for update_policy OnChange */ + /* Variables used for update_policy OnChange/OnChangeRateLimited */ T _min_delta_property; + /* Variables used OnChangeRateLimited */ + unsigned long _min_time_between_updates_milliseconds; /* Variables used for update policy TimeInterval */ unsigned long _last_updated, _update_interval_sec; @@ -75,6 +79,8 @@ ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, _update_callback_func(NULL), _update_policy(UpdatePolicy::OnChange), _has_been_updated_once(false), + _min_delta_property(T(0)), + _min_time_between_updates_milliseconds(0), _last_updated(0), _update_interval_sec(0) { @@ -95,12 +101,24 @@ ArduinoCloudProperty & ArduinoCloudProperty::onUpdate(UpdateCallbackFunc f } template -ArduinoCloudProperty & ArduinoCloudProperty::publishOnChange(T const min_delta_property) { - _update_policy = UpdatePolicy::OnChange; +ArduinoCloudProperty & ArduinoCloudProperty::setMinDelta(T const min_delta_property) { _min_delta_property = min_delta_property; return (*this); } +template +ArduinoCloudProperty & ArduinoCloudProperty::publishOnChange() { + _update_policy = UpdatePolicy::OnChange; + return (*this); +} + +template +ArduinoCloudProperty & ArduinoCloudProperty::publishOnChangeRateLimited(unsigned long const min_time_between_updates_milliseconds) { + _update_policy = UpdatePolicy::OnChangeRateLimited; + _min_time_between_updates_milliseconds = min_time_between_updates_milliseconds; + return (*this); +} + template ArduinoCloudProperty & ArduinoCloudProperty::publishEvery(unsigned long const seconds) { _update_policy = UpdatePolicy::TimeInterval; @@ -115,6 +133,9 @@ bool ArduinoCloudProperty::shouldBeUpdated() const { if (_update_policy == UpdatePolicy::OnChange) { return isValueDifferent(_property, _shadow_property); } + else if(_update_policy == UpdatePolicy::OnChangeRateLimited) { + return (isValueDifferent(_property, _shadow_property) && ((millis() - _last_updated) > (_min_time_between_updates_milliseconds))); + } else if(_update_policy == UpdatePolicy::TimeInterval) { return ((millis() - _last_updated) > (_update_interval_sec * 1000)); } From ebf8eb00be8db4a899d25d40804928431e50d808 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Wed, 17 Oct 2018 07:53:51 +0200 Subject: [PATCH 073/175] Simplifying configuration via composition and adding const qualifiers where feasible --- ArduinoCloudProperty.hpp | 31 +++++++------------------------ ArduinoCloudThing.cpp | 18 +++++++++--------- ArduinoCloudThing.h | 33 ++++++++++++++------------------- 3 files changed, 30 insertions(+), 52 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index c758b8b6b..7a835114d 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -14,7 +14,7 @@ enum class Type { }; enum class UpdatePolicy { - OnChange, OnChangeRateLimited, TimeInterval + OnChange, TimeInterval }; typedef void(*UpdateCallbackFunc)(void); @@ -29,11 +29,9 @@ class ArduinoCloudProperty { bool writeByCloud(T const val); /* Composable configuration of the ArduinoCloudProperty class */ - ArduinoCloudProperty & onUpdate (UpdateCallbackFunc func ); - ArduinoCloudProperty & setMinDelta (T const min_delta_property ); - ArduinoCloudProperty & publishOnChange ( ); - ArduinoCloudProperty & publishOnChangeRateLimited(unsigned long const min_time_between_updates_milliseconds); - ArduinoCloudProperty & publishEvery (unsigned long const seconds ); + ArduinoCloudProperty & onUpdate (UpdateCallbackFunc func); + ArduinoCloudProperty & publishOnChange(T const min_delta_property, unsigned long const min_time_between_updates_milliseconds = 0); + ArduinoCloudProperty & publishEvery (unsigned long const seconds); inline String name () const { return _name; } inline bool isReadableByCloud () const { return (_permission == Permission::Read ) || (_permission == Permission::ReadWrite); } @@ -54,9 +52,8 @@ class ArduinoCloudProperty { UpdatePolicy _update_policy; bool _has_been_updated_once; - /* Variables used for update_policy OnChange/OnChangeRateLimited */ + /* Variables used for update_policy OnChange */ T _min_delta_property; - /* Variables used OnChangeRateLimited */ unsigned long _min_time_between_updates_milliseconds; /* Variables used for update policy TimeInterval */ unsigned long _last_updated, @@ -101,20 +98,9 @@ ArduinoCloudProperty & ArduinoCloudProperty::onUpdate(UpdateCallbackFunc f } template -ArduinoCloudProperty & ArduinoCloudProperty::setMinDelta(T const min_delta_property) { - _min_delta_property = min_delta_property; - return (*this); -} - -template -ArduinoCloudProperty & ArduinoCloudProperty::publishOnChange() { +ArduinoCloudProperty & ArduinoCloudProperty::publishOnChange(T const min_delta_property, unsigned long const min_time_between_updates_milliseconds) { _update_policy = UpdatePolicy::OnChange; - return (*this); -} - -template -ArduinoCloudProperty & ArduinoCloudProperty::publishOnChangeRateLimited(unsigned long const min_time_between_updates_milliseconds) { - _update_policy = UpdatePolicy::OnChangeRateLimited; + _min_delta_property = min_delta_property; _min_time_between_updates_milliseconds = min_time_between_updates_milliseconds; return (*this); } @@ -131,9 +117,6 @@ bool ArduinoCloudProperty::shouldBeUpdated() const { if(!_has_been_updated_once) return true; if (_update_policy == UpdatePolicy::OnChange) { - return isValueDifferent(_property, _shadow_property); - } - else if(_update_policy == UpdatePolicy::OnChangeRateLimited) { return (isValueDifferent(_property, _shadow_property) && ((millis() - _last_updated) > (_min_time_between_updates_milliseconds))); } else if(_update_policy == UpdatePolicy::TimeInterval) { diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 470598cdb..31a5f4600 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -32,22 +32,22 @@ ArduinoCloudThing::ArduinoCloudThing() { #define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x0080A044) #define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x0080A048) - utox8(SERIAL_NUMBER_WORD_0, &uuid[0]); - utox8(SERIAL_NUMBER_WORD_1, &uuid[8]); - utox8(SERIAL_NUMBER_WORD_2, &uuid[16]); - utox8(SERIAL_NUMBER_WORD_3, &uuid[24]); - uuid[32] = '\0'; + utox8(SERIAL_NUMBER_WORD_0, &_uuid[0]); + utox8(SERIAL_NUMBER_WORD_1, &_uuid[8]); + utox8(SERIAL_NUMBER_WORD_2, &_uuid[16]); + utox8(SERIAL_NUMBER_WORD_3, &_uuid[24]); + _uuid[32] = '\0'; #endif } void ArduinoCloudThing::begin() { - status = ON; - addProperty(status, "status", Permission::Read); + _status = ON; + addProperty(_status, "status", Permission::Read); } -int ArduinoCloudThing::poll(uint8_t* data, size_t size) { +int ArduinoCloudThing::poll(uint8_t * data, size_t const size) { // check if backing storage and cloud has diverged // time interval may be elapsed or property may be changed @@ -124,7 +124,7 @@ ArduinoCloudProperty & ArduinoCloudThing::addProperty(String & property, } } -void ArduinoCloudThing::decode(uint8_t *payload, size_t length) { +void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const length) { CborError err; CborParser parser; diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 8ea6cfd9a..93b61ab8d 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -1,25 +1,21 @@ #ifndef ARDUINO_CLOUD_THING_H_ #define ARDUINO_CLOUD_THING_H_ -#include -#include - #include "ArduinoCloudProperty.hpp" #include "ArduinoCloudPropertyContainer.hpp" #include "lib/LinkedList/LinkedList.h" -enum boolStatus { - ON = true, - OFF = false, -}; +/* Constants for backwards compatibility */ -enum times { - SECONDS = 1, - MINUTES = 60, - HOURS = 3600, - DAYS = 86400, -}; +static bool ON = true; +static bool OFF = false; + +static long const ON_CHANGE = -1; +static long const SECONDS = 1; +static long const MINUTES = 60; +static long const HOURS = 3600; +static long const DAYS = 86400; class ArduinoCloudThing { public: @@ -32,17 +28,16 @@ class ArduinoCloudThing { ArduinoCloudProperty & addProperty(String & property, String const & name, Permission const permission); // poll should return > 0 if something has changed - int poll(uint8_t* data, size_t size); + int poll(uint8_t * data, size_t const size); // decode a CBOR payload received from the Cloud. - void decode(uint8_t * payload, size_t length); + void decode(uint8_t const * const payload, size_t const length); private: - void update(); - - bool status = OFF; - char uuid[33]; + bool _status = OFF; + char _uuid[33]; ArduinoCloudPropertyContainer _property_cont; + }; #endif /* ARDUINO_CLOUD_THING_H_ */ From f1f47c4fc2a68de63a1062aaab562d337b96ed97 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Wed, 17 Oct 2018 07:55:39 +0200 Subject: [PATCH 074/175] Extracting ArduinoCloudProperty template implementation into *.ipp file --- ArduinoCloudProperty.hpp | 130 +-------------------------------------- ArduinoCloudProperty.ipp | 130 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 129 deletions(-) create mode 100644 ArduinoCloudProperty.ipp diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 7a835114d..efee0c4a9 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -67,134 +67,6 @@ class ArduinoCloudProperty { template inline bool operator == (ArduinoCloudProperty const & lhs, ArduinoCloudProperty const & rhs) { return (lhs.name() == rhs.name()); } -template -ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, Permission const permission) -: _property(property), - _shadow_property(property), - _name(name), - _permission(permission), - _update_callback_func(NULL), - _update_policy(UpdatePolicy::OnChange), - _has_been_updated_once(false), - _min_delta_property(T(0)), - _min_time_between_updates_milliseconds(0), - _last_updated(0), - _update_interval_sec(0) -{ -} - -template -bool ArduinoCloudProperty::writeByCloud(T const val) { - if(!isWriteableByCloud()) return false; - _property = val; - /* _shadow_property is not updated so there will be an update the next time around */ - return true; -} - -template -ArduinoCloudProperty & ArduinoCloudProperty::onUpdate(UpdateCallbackFunc func) { - _update_callback_func = func; - return (*this); -} - -template -ArduinoCloudProperty & ArduinoCloudProperty::publishOnChange(T const min_delta_property, unsigned long const min_time_between_updates_milliseconds) { - _update_policy = UpdatePolicy::OnChange; - _min_delta_property = min_delta_property; - _min_time_between_updates_milliseconds = min_time_between_updates_milliseconds; - return (*this); -} - -template -ArduinoCloudProperty & ArduinoCloudProperty::publishEvery(unsigned long const seconds) { - _update_policy = UpdatePolicy::TimeInterval; - _update_interval_sec = seconds; - return (*this); -} - -template -bool ArduinoCloudProperty::shouldBeUpdated() const { - if(!_has_been_updated_once) return true; - - if (_update_policy == UpdatePolicy::OnChange) { - return (isValueDifferent(_property, _shadow_property) && ((millis() - _last_updated) > (_min_time_between_updates_milliseconds))); - } - else if(_update_policy == UpdatePolicy::TimeInterval) { - return ((millis() - _last_updated) > (_update_interval_sec * 1000)); - } - else { - return false; - } -} - -template -void ArduinoCloudProperty::execCallbackOnChange() { - if(isValueDifferent(_property, _shadow_property)) { - if(_update_callback_func != NULL) { - _update_callback_func(); - } - } -} - -template -void ArduinoCloudProperty::append(CborEncoder * encoder) { - if (isReadableByCloud()) { - CborEncoder mapEncoder; - - cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); - cbor_encode_text_stringz(&mapEncoder, "n"); - cbor_encode_text_stringz(&mapEncoder, _name.c_str()); - appendValue(&mapEncoder); - cbor_encoder_close_container(encoder, &mapEncoder); - - _shadow_property = _property; - _has_been_updated_once = true; - _last_updated = millis(); - } -} - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_text_stringz(mapEncoder, "vb"); - cbor_encode_boolean(mapEncoder, _property); -} - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_text_stringz(mapEncoder, "v"); - cbor_encode_int(mapEncoder, _property); -} - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_text_stringz(mapEncoder, "v"); - cbor_encode_float(mapEncoder, _property); -} - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_text_stringz(mapEncoder, "vs"); - cbor_encode_text_stringz(mapEncoder, _property.c_str()); -} - -template <> -inline bool ArduinoCloudProperty::isValueDifferent(bool const lhs, bool const rhs) const { - return (lhs != rhs); -} - -template <> -inline bool ArduinoCloudProperty::isValueDifferent(int const lhs, int const rhs) const { - return (lhs != rhs) && (abs(lhs - rhs) >= _min_delta_property); -} - -template <> -inline bool ArduinoCloudProperty::isValueDifferent(float const lhs, float const rhs) const { - return (lhs != rhs) && (abs(lhs - rhs) >= _min_delta_property); -} - -template <> -inline bool ArduinoCloudProperty::isValueDifferent(String const lhs, String const rhs) const { - return (lhs != rhs); -} +#include "ArduinoCloudProperty.ipp" #endif /* ARDUINO_CLOUD_PROPERTY_HPP_ */ diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp new file mode 100644 index 000000000..5ecc567a1 --- /dev/null +++ b/ArduinoCloudProperty.ipp @@ -0,0 +1,130 @@ + +template +ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, Permission const permission) +: _property(property), + _shadow_property(property), + _name(name), + _permission(permission), + _update_callback_func(NULL), + _update_policy(UpdatePolicy::OnChange), + _has_been_updated_once(false), + _min_delta_property(T(0)), + _min_time_between_updates_milliseconds(0), + _last_updated(0), + _update_interval_sec(0) +{ +} + +template +bool ArduinoCloudProperty::writeByCloud(T const val) { + if(!isWriteableByCloud()) return false; + _property = val; + /* _shadow_property is not updated so there will be an update the next time around */ + return true; +} + +template +ArduinoCloudProperty & ArduinoCloudProperty::onUpdate(UpdateCallbackFunc func) { + _update_callback_func = func; + return (*this); +} + +template +ArduinoCloudProperty & ArduinoCloudProperty::publishOnChange(T const min_delta_property, unsigned long const min_time_between_updates_milliseconds) { + _update_policy = UpdatePolicy::OnChange; + _min_delta_property = min_delta_property; + _min_time_between_updates_milliseconds = min_time_between_updates_milliseconds; + return (*this); +} + +template +ArduinoCloudProperty & ArduinoCloudProperty::publishEvery(unsigned long const seconds) { + _update_policy = UpdatePolicy::TimeInterval; + _update_interval_sec = seconds; + return (*this); +} + +template +bool ArduinoCloudProperty::shouldBeUpdated() const { + if(!_has_been_updated_once) return true; + + if (_update_policy == UpdatePolicy::OnChange) { + return (isValueDifferent(_property, _shadow_property) && ((millis() - _last_updated) > (_min_time_between_updates_milliseconds))); + } + else if(_update_policy == UpdatePolicy::TimeInterval) { + return ((millis() - _last_updated) > (_update_interval_sec * 1000)); + } + else { + return false; + } +} + +template +void ArduinoCloudProperty::execCallbackOnChange() { + if(isValueDifferent(_property, _shadow_property)) { + if(_update_callback_func != NULL) { + _update_callback_func(); + } + } +} + +template +void ArduinoCloudProperty::append(CborEncoder * encoder) { + if (isReadableByCloud()) { + CborEncoder mapEncoder; + + cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); + cbor_encode_text_stringz(&mapEncoder, "n"); + cbor_encode_text_stringz(&mapEncoder, _name.c_str()); + appendValue(&mapEncoder); + cbor_encoder_close_container(encoder, &mapEncoder); + + _shadow_property = _property; + _has_been_updated_once = true; + _last_updated = millis(); + } +} + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { + cbor_encode_text_stringz(mapEncoder, "vb"); + cbor_encode_boolean(mapEncoder, _property); +} + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { + cbor_encode_text_stringz(mapEncoder, "v"); + cbor_encode_int(mapEncoder, _property); +} + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { + cbor_encode_text_stringz(mapEncoder, "v"); + cbor_encode_float(mapEncoder, _property); +} + +template <> +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { + cbor_encode_text_stringz(mapEncoder, "vs"); + cbor_encode_text_stringz(mapEncoder, _property.c_str()); +} + +template <> +inline bool ArduinoCloudProperty::isValueDifferent(bool const lhs, bool const rhs) const { + return (lhs != rhs); +} + +template <> +inline bool ArduinoCloudProperty::isValueDifferent(int const lhs, int const rhs) const { + return (lhs != rhs) && (abs(lhs - rhs) >= _min_delta_property); +} + +template <> +inline bool ArduinoCloudProperty::isValueDifferent(float const lhs, float const rhs) const { + return (lhs != rhs) && (abs(lhs - rhs) >= _min_delta_property); +} + +template <> +inline bool ArduinoCloudProperty::isValueDifferent(String const lhs, String const rhs) const { + return (lhs != rhs); +} From 07b21c04ff255d1f6fc1cfd8eb0b6b1515a30512 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Wed, 17 Oct 2018 11:06:43 +0200 Subject: [PATCH 075/175] Extracting usage isWritableMethodByCloud method into 'decode' method in order to make it better visible for the user of the library that the properties are only updated if they are writable --- ArduinoCloudProperty.hpp | 2 +- ArduinoCloudProperty.ipp | 10 ++++----- ArduinoCloudThing.cpp | 44 +++++++++++++++++++++++++++++----------- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index efee0c4a9..ae47a4a5b 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -26,7 +26,7 @@ class ArduinoCloudProperty { ArduinoCloudProperty(T & property, String const & name, Permission const permission); - bool writeByCloud(T const val); + void writeByCloud(T const val); /* Composable configuration of the ArduinoCloudProperty class */ ArduinoCloudProperty & onUpdate (UpdateCallbackFunc func); diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 5ecc567a1..9c65e4ffb 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -16,11 +16,11 @@ ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, } template -bool ArduinoCloudProperty::writeByCloud(T const val) { - if(!isWriteableByCloud()) return false; - _property = val; - /* _shadow_property is not updated so there will be an update the next time around */ - return true; +void ArduinoCloudProperty::writeByCloud(T const val) { + if(isWriteableByCloud()) { + _property = val; + /* _shadow_property is not updated so there will be an update the next time around */ + } } template diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 31a5f4600..9e432004c 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -221,19 +221,27 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt double val; // get the value of the property as a double cbor_value_get_double(&propValue, &val); - float_property->writeByCloud(static_cast(val)); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(static_cast(val)); + } } else if (propValue.type == CborIntegerType) { int val; cbor_value_get_int(&propValue, &val); - float_property->writeByCloud(static_cast(val)); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(static_cast(val)); + } } else if (propValue.type == CborFloatType) { float val; cbor_value_get_float(&propValue, &val); - float_property->writeByCloud(val); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(val); + } } else if (propValue.type == CborHalfFloatType) { float val; cbor_value_get_half_float(&propValue, &val); - float_property->writeByCloud(val); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(val); + } } float_property->execCallbackOnChange(); } else if (propType == Type::Int && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { @@ -241,35 +249,47 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt if (propValue.type == CborIntegerType) { int val; cbor_value_get_int(&propValue, &val); - int_property->writeByCloud(val); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(val); + } } else if (propValue.type == CborDoubleType) { // If a double value is received, a cast to int is performed(so it is still accepted) double val; cbor_value_get_double(&propValue, &val); - int_property->writeByCloud(static_cast(val)); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(static_cast(val)); + } } else if (propValue.type == CborFloatType) { float val; cbor_value_get_float(&propValue, &val); - int_property->writeByCloud(static_cast(val)); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(static_cast(val)); + } } else if (propValue.type == CborHalfFloatType) { float val; cbor_value_get_half_float(&propValue, &val); - int_property->writeByCloud(static_cast(val)); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(static_cast(val)); + } } int_property->execCallbackOnChange(); } else if (propType == Type::Bool && !cbor_value_map_find_value(&recursedMap, "vb", &propValue)) { if (propValue.type == CborBooleanType) { bool val; cbor_value_get_boolean(&propValue, &val); - bool_property->writeByCloud(val); - bool_property->execCallbackOnChange(); + if(bool_property->isWriteableByCloud()) { + bool_property->writeByCloud(val); + bool_property->execCallbackOnChange(); + } } } else if (propType == Type::String && !cbor_value_map_find_value(&recursedMap, "vs", &propValue)){ if (propValue.type == CborTextStringType) { char *val; size_t valSize; err = cbor_value_dup_text_string(&propValue, &val, &valSize, &propValue); - string_property->writeByCloud(static_cast(val)); - string_property->execCallbackOnChange(); + if(string_property->isWriteableByCloud()) { + string_property->writeByCloud(static_cast(val)); + string_property->execCallbackOnChange(); + } free(val); } } From 555fc2441f13fc47e525e6dff5cbbceaead541d2 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Wed, 17 Oct 2018 11:09:08 +0200 Subject: [PATCH 076/175] Removing no longer needed variable propId --- ArduinoCloudThing.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 9e432004c..76c59d3dc 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -129,7 +129,6 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt CborError err; CborParser parser; CborValue recursedMap, propValue, dataArray; - int propId; String propName; Type propType; From b28c0d7463f3c43d01c84f3b08829307333a7e21 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Wed, 17 Oct 2018 11:15:55 +0200 Subject: [PATCH 077/175] Renaming method 'poll' to 'encode' to better reflect what it is doing and also to be more symmetric to the 'decode' method --- ArduinoCloudThing.cpp | 2 +- ArduinoCloudThing.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 76c59d3dc..d07cd60a8 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -47,7 +47,7 @@ void ArduinoCloudThing::begin() { } -int ArduinoCloudThing::poll(uint8_t * data, size_t const size) { +int ArduinoCloudThing::encode(uint8_t * data, size_t const size) { // check if backing storage and cloud has diverged // time interval may be elapsed or property may be changed diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 93b61ab8d..116a75c23 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -27,8 +27,8 @@ class ArduinoCloudThing { ArduinoCloudProperty & addProperty(float & property, String const & name, Permission const permission); ArduinoCloudProperty & addProperty(String & property, String const & name, Permission const permission); - // poll should return > 0 if something has changed - int poll(uint8_t * data, size_t const size); + // encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer + int encode(uint8_t * data, size_t const size); // decode a CBOR payload received from the Cloud. void decode(uint8_t const * const payload, size_t const length); From 721e47f51d788c48d8fbb8103326daa8647dc0ec Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 17 Oct 2018 19:03:45 +0200 Subject: [PATCH 078/175] Bugfix - Corrected wrong order in subtraction in test code --- ArduinoCloudProperty.ipp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 9c65e4ffb..20cd78f6f 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -49,7 +49,7 @@ bool ArduinoCloudProperty::shouldBeUpdated() const { if(!_has_been_updated_once) return true; if (_update_policy == UpdatePolicy::OnChange) { - return (isValueDifferent(_property, _shadow_property) && ((millis() - _last_updated) > (_min_time_between_updates_milliseconds))); + return (isValueDifferent(_property, _shadow_property) && ((millis() - _last_updated) >= (_min_time_between_updates_milliseconds))); } else if(_update_policy == UpdatePolicy::TimeInterval) { return ((millis() - _last_updated) > (_update_interval_sec * 1000)); From 89ff0a51644d5027564db0a4128c81ade488698d Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Thu, 18 Oct 2018 08:45:01 +0200 Subject: [PATCH 079/175] Changing name of method 'addProperty' to 'addPropertyReal' in order to be compliant with the calling convention in class ArduinoIoTCloud --- ArduinoCloudThing.cpp | 10 +++++----- ArduinoCloudThing.h | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index d07cd60a8..fcc4779d5 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -43,7 +43,7 @@ ArduinoCloudThing::ArduinoCloudThing() { void ArduinoCloudThing::begin() { _status = ON; - addProperty(_status, "status", Permission::Read); + addPropertyReal(_status, "status", Permission::Read); } @@ -80,7 +80,7 @@ int ArduinoCloudThing::encode(uint8_t * data, size_t const size) { return num_changed_properties; } -ArduinoCloudProperty & ArduinoCloudThing::addProperty(bool & property, String const & name, Permission const permission) { +ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(bool & property, String const & name, Permission const permission) { if(_property_cont.isPropertyInContainer(Type::Bool, name)) { return (*_property_cont.getPropertyBool(name)); } @@ -91,7 +91,7 @@ ArduinoCloudProperty & ArduinoCloudThing::addProperty(bool & property, Str } } -ArduinoCloudProperty & ArduinoCloudThing::addProperty(int & property, String const & name, Permission const permission) { +ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(int & property, String const & name, Permission const permission) { if(_property_cont.isPropertyInContainer(Type::Int, name)) { return (*_property_cont.getPropertyInt(name)); } @@ -102,7 +102,7 @@ ArduinoCloudProperty & ArduinoCloudThing::addProperty(int & property, Strin } } -ArduinoCloudProperty & ArduinoCloudThing::addProperty(float & property, String const & name, Permission const permission) { +ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(float & property, String const & name, Permission const permission) { if(_property_cont.isPropertyInContainer(Type::Float, name)) { return (*_property_cont.getPropertyFloat(name)); } @@ -113,7 +113,7 @@ ArduinoCloudProperty & ArduinoCloudThing::addProperty(float & property, S } } -ArduinoCloudProperty & ArduinoCloudThing::addProperty(String & property, String const & name, Permission const permission) { +ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(String & property, String const & name, Permission const permission) { if(_property_cont.isPropertyInContainer(Type::String, name)) { return (*_property_cont.getPropertyString(name)); } diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 116a75c23..05ecc69f2 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -22,10 +22,10 @@ class ArduinoCloudThing { ArduinoCloudThing(); void begin(); - ArduinoCloudProperty & addProperty(bool & property, String const & name, Permission const permission); - ArduinoCloudProperty & addProperty(int & property, String const & name, Permission const permission); - ArduinoCloudProperty & addProperty(float & property, String const & name, Permission const permission); - ArduinoCloudProperty & addProperty(String & property, String const & name, Permission const permission); + ArduinoCloudProperty & addPropertyReal(bool & property, String const & name, Permission const permission); + ArduinoCloudProperty & addPropertyReal(int & property, String const & name, Permission const permission); + ArduinoCloudProperty & addPropertyReal(float & property, String const & name, Permission const permission); + ArduinoCloudProperty & addPropertyReal(String & property, String const & name, Permission const permission); // encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer int encode(uint8_t * data, size_t const size); From df0c632f470a7c99c1ca8052beda6cf398dc4865 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Sat, 20 Oct 2018 17:34:26 +0200 Subject: [PATCH 080/175] Renaming milliseconds to millis wherever used as an variable appendix --- ArduinoCloudProperty.hpp | 8 ++++---- ArduinoCloudProperty.ipp | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index ae47a4a5b..2ef255651 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -30,7 +30,7 @@ class ArduinoCloudProperty { /* Composable configuration of the ArduinoCloudProperty class */ ArduinoCloudProperty & onUpdate (UpdateCallbackFunc func); - ArduinoCloudProperty & publishOnChange(T const min_delta_property, unsigned long const min_time_between_updates_milliseconds = 0); + ArduinoCloudProperty & publishOnChange(T const min_delta_property, unsigned long const min_time_between_updates_millis = 0); ArduinoCloudProperty & publishEvery (unsigned long const seconds); inline String name () const { return _name; } @@ -54,10 +54,10 @@ class ArduinoCloudProperty { bool _has_been_updated_once; /* Variables used for update_policy OnChange */ T _min_delta_property; - unsigned long _min_time_between_updates_milliseconds; + unsigned long _min_time_between_updates_millis; /* Variables used for update policy TimeInterval */ - unsigned long _last_updated, - _update_interval_sec; + unsigned long _last_updated_millis, + _update_interval_millis; void appendValue(CborEncoder * mapEncoder) const; bool isValueDifferent(T const lhs, T const rhs) const; diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 20cd78f6f..4388442bb 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -9,9 +9,9 @@ ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, _update_policy(UpdatePolicy::OnChange), _has_been_updated_once(false), _min_delta_property(T(0)), - _min_time_between_updates_milliseconds(0), - _last_updated(0), - _update_interval_sec(0) + _min_time_between_updates_millis(0), + _last_updated_millis(0), + _update_interval_millis(0) { } @@ -30,17 +30,17 @@ ArduinoCloudProperty & ArduinoCloudProperty::onUpdate(UpdateCallbackFunc f } template -ArduinoCloudProperty & ArduinoCloudProperty::publishOnChange(T const min_delta_property, unsigned long const min_time_between_updates_milliseconds) { +ArduinoCloudProperty & ArduinoCloudProperty::publishOnChange(T const min_delta_property, unsigned long const min_time_between_updates_millis) { _update_policy = UpdatePolicy::OnChange; _min_delta_property = min_delta_property; - _min_time_between_updates_milliseconds = min_time_between_updates_milliseconds; + _min_time_between_updates_millis = min_time_between_updates_millis; return (*this); } template ArduinoCloudProperty & ArduinoCloudProperty::publishEvery(unsigned long const seconds) { _update_policy = UpdatePolicy::TimeInterval; - _update_interval_sec = seconds; + _update_interval_millis = (seconds * 1000); return (*this); } @@ -49,10 +49,10 @@ bool ArduinoCloudProperty::shouldBeUpdated() const { if(!_has_been_updated_once) return true; if (_update_policy == UpdatePolicy::OnChange) { - return (isValueDifferent(_property, _shadow_property) && ((millis() - _last_updated) >= (_min_time_between_updates_milliseconds))); + return (isValueDifferent(_property, _shadow_property) && ((millis() - _last_updated_millis) >= (_min_time_between_updates_millis))); } else if(_update_policy == UpdatePolicy::TimeInterval) { - return ((millis() - _last_updated) > (_update_interval_sec * 1000)); + return ((millis() - _last_updated_millis) >= _update_interval_millis); } else { return false; @@ -81,7 +81,7 @@ void ArduinoCloudProperty::append(CborEncoder * encoder) { _shadow_property = _property; _has_been_updated_once = true; - _last_updated = millis(); + _last_updated_millis = millis(); } } From 220b84619067122c43f3469dc4740b93975c77a6 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Thu, 25 Oct 2018 19:56:01 +0200 Subject: [PATCH 081/175] Fixing some typos as suggested by the pull request comments made by AlbyIanna --- ArduinoCloudProperty.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 2ef255651..e4b32de83 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -52,10 +52,10 @@ class ArduinoCloudProperty { UpdatePolicy _update_policy; bool _has_been_updated_once; - /* Variables used for update_policy OnChange */ + /* Variables used for UpdatePolicy::OnChange */ T _min_delta_property; unsigned long _min_time_between_updates_millis; - /* Variables used for update policy TimeInterval */ + /* Variables used for UpdatePolicy::TimeInterval */ unsigned long _last_updated_millis, _update_interval_millis; From a10d9c0a47e01fb1560b00bb0ead6d91ce5480c8 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Thu, 15 Nov 2018 14:40:29 +0100 Subject: [PATCH 082/175] Reducing the overall payload size by replacing the string key with a number key using https://tools.ietf.org/html/rfc8428#section-6 --- ArduinoCloudProperty.hpp | 39 +++++++++++++++++++++++++++++++++++++++ ArduinoCloudProperty.ipp | 21 ++++++++++++++++----- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index e4b32de83..ed5b98c1b 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -1,10 +1,18 @@ #ifndef ARDUINO_CLOUD_PROPERTY_HPP_ #define ARDUINO_CLOUD_PROPERTY_HPP_ +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + #include #include "lib/tinycbor/cbor-lib.h" +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + enum class Permission { Read, Write, ReadWrite }; @@ -17,8 +25,31 @@ enum class UpdatePolicy { OnChange, TimeInterval }; +/* Source: https://tools.ietf.org/html/rfc8428#section-6 */ +enum class CborIntegerMapKey : int +{ + BaseVersion = -1, /* bver */ + BaseName = -2, /* bn */ + BaseTime = -3, /* bt */ + BaseUnit = -4, /* bu */ + BaseValue = -5, /* bv */ + BaseSum = -6, /* bs */ + Name = 0, /* n */ + Unit = 1, /* u */ + Value = 2, /* v */ + StringValue = 3, /* vs */ + BooleanValue = 4, /* vb */ + Sum = 5, /* s */ + Time = 6, /* t */ + UpdateTime = 7, /* ut */ + DataValue = 8 /* vd */ +}; + typedef void(*UpdateCallbackFunc)(void); +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ template class ArduinoCloudProperty { @@ -64,9 +95,17 @@ class ArduinoCloudProperty { }; +/****************************************************************************** + * PROTOTYPE FREE FUNCTIONs + ******************************************************************************/ + template inline bool operator == (ArduinoCloudProperty const & lhs, ArduinoCloudProperty const & rhs) { return (lhs.name() == rhs.name()); } +/****************************************************************************** + * TEMPLATE IMPLEMENTATION + ******************************************************************************/ + #include "ArduinoCloudProperty.ipp" #endif /* ARDUINO_CLOUD_PROPERTY_HPP_ */ diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 4388442bb..d3cd664d1 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -1,3 +1,6 @@ +/****************************************************************************** + * CTOR/DTOR + ******************************************************************************/ template ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, Permission const permission) @@ -15,6 +18,10 @@ ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, { } +/****************************************************************************** + * PUBLIC MEMBER FUNCTIONS + ******************************************************************************/ + template void ArduinoCloudProperty::writeByCloud(T const val) { if(isWriteableByCloud()) { @@ -74,7 +81,7 @@ void ArduinoCloudProperty::append(CborEncoder * encoder) { CborEncoder mapEncoder; cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); - cbor_encode_text_stringz(&mapEncoder, "n"); + cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Name)); cbor_encode_text_stringz(&mapEncoder, _name.c_str()); appendValue(&mapEncoder); cbor_encoder_close_container(encoder, &mapEncoder); @@ -85,27 +92,31 @@ void ArduinoCloudProperty::append(CborEncoder * encoder) { } } +/****************************************************************************** + * PRIVATE MEMBER FUNCTIONS + ******************************************************************************/ + template <> inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_text_stringz(mapEncoder, "vb"); + cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); cbor_encode_boolean(mapEncoder, _property); } template <> inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_text_stringz(mapEncoder, "v"); + cbor_encode_int(mapEncoder, static_cast(CborIntegerMapKey::Value)); cbor_encode_int(mapEncoder, _property); } template <> inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_text_stringz(mapEncoder, "v"); + cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); cbor_encode_float(mapEncoder, _property); } template <> inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_text_stringz(mapEncoder, "vs"); + cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::StringValue)); cbor_encode_text_stringz(mapEncoder, _property.c_str()); } From c7ac3789048d7c183c4ff98043b3a98e141cca22 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Thu, 15 Nov 2018 18:23:15 +0100 Subject: [PATCH 083/175] Squashing multiple commits concerning the refactoring of the test code -> Fixing test case 'beginAddStatusProperty' due to previous change in the encoding of the values -> Deleting build.sh script - accidentially committed at an earlier commit -> Adjusting test case 'addThingAndChangeValue' after changing encoding of name/type from strings to CborIntegerMapKeys -> Adjusting test case 'decodeBuffer' after changing encoding of name/type from strings to CborIntegerMapKeys -> Removing unnecessary trailing '0' -> Adjusting test case 'decodeProperties' after changing encoding of name/type from strings to CborIntegerMapKeys -> Bugfix: Converting CBOR Half Float Types to Float has been incorrectly implemented -> Adding separator comments for better readabiltiy --- ArduinoCloudPropertyContainer.cpp | 8 ++++++++ ArduinoCloudPropertyContainer.hpp | 12 ++++++++++++ ArduinoCloudPropertyContainer.ipp | 3 +++ ArduinoCloudThing.cpp | 32 +++++++++++++++++++++++++++++-- ArduinoCloudThing.h | 12 +++++++++++- 5 files changed, 64 insertions(+), 3 deletions(-) diff --git a/ArduinoCloudPropertyContainer.cpp b/ArduinoCloudPropertyContainer.cpp index 6587ef6ac..b62a92526 100644 --- a/ArduinoCloudPropertyContainer.cpp +++ b/ArduinoCloudPropertyContainer.cpp @@ -1,5 +1,13 @@ +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + #include "ArduinoCloudPropertyContainer.hpp" +/****************************************************************************** + * PUBLIC MEMBER FUNCTIONS + ******************************************************************************/ + bool ArduinoCloudPropertyContainer::isPropertyInContainer(Type const type, String const & name) { if (type == Type::Bool ) return isPropertyInList(_bool_property_list, name); else if (type == Type::Int ) return isPropertyInList(_int_property_list, name); diff --git a/ArduinoCloudPropertyContainer.hpp b/ArduinoCloudPropertyContainer.hpp index 486922850..c19779fc8 100644 --- a/ArduinoCloudPropertyContainer.hpp +++ b/ArduinoCloudPropertyContainer.hpp @@ -1,11 +1,19 @@ #ifndef ARDUINO_CLOUD_PROPERTY_CONTAINER_NEW_H_ #define ARDUINO_CLOUD_PROPERTY_CONTAINER_NEW_H_ +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + #include "ArduinoCloudProperty.hpp" #include "lib/tinycbor/cbor-lib.h" #include "lib/LinkedList/LinkedList.h" +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + class ArduinoCloudPropertyContainer { public: @@ -44,6 +52,10 @@ class ArduinoCloudPropertyContainer { }; +/****************************************************************************** + * TEMPLATE IMPLEMENTATION + ******************************************************************************/ + #include "ArduinoCloudPropertyContainer.ipp" #endif /* ARDUINO_CLOUD_PROPERTY_CONTAINER_NEW_H_ */ diff --git a/ArduinoCloudPropertyContainer.ipp b/ArduinoCloudPropertyContainer.ipp index 851d8b37a..b3efc423f 100644 --- a/ArduinoCloudPropertyContainer.ipp +++ b/ArduinoCloudPropertyContainer.ipp @@ -1,3 +1,6 @@ +/****************************************************************************** + * PRIVATE MEMBER FUNCTIONS + ******************************************************************************/ template bool ArduinoCloudPropertyContainer::isPropertyInList(LinkedList *> & list, String const & name) { diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index fcc4779d5..2774f3b82 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -1,7 +1,28 @@ +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + #include #include +#include + +/****************************************************************************** + * PRIVATE FREE FUNCTIONS + ******************************************************************************/ + +/* Source Idea from https://tools.ietf.org/html/rfc7049 : Page: 50 */ +double convertCborHalfFloatToDouble(uint16_t const half_val) { + int exp = (half_val >> 10) & 0x1f; + int mant = half_val & 0x3ff; + double val; + if (exp == 0) val = ldexp(mant, -24); + else if (exp != 31) val = ldexp(mant + 1024, exp - 25); + else val = mant == 0 ? INFINITY : NAN; + return half_val & 0x8000 ? -val : val; +} + #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) extern "C" char *sbrk(int i); void PrintFreeRam (void) @@ -25,6 +46,10 @@ static void utox8(uint32_t val, char* s) { #define Serial DebugSerial #endif +/****************************************************************************** + * CTOR/DTOR + ******************************************************************************/ + ArduinoCloudThing::ArduinoCloudThing() { #ifdef ARDUINO_ARCH_SAMD #define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x0080A00C) @@ -40,6 +65,9 @@ ArduinoCloudThing::ArduinoCloudThing() { #endif } +/****************************************************************************** + * PUBLIC MEMBER FUNCTIONS + ******************************************************************************/ void ArduinoCloudThing::begin() { _status = ON; @@ -236,10 +264,10 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt float_property->writeByCloud(val); } } else if (propValue.type == CborHalfFloatType) { - float val; + uint16_t val; cbor_value_get_half_float(&propValue, &val); if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(val); + float_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); } } float_property->execCallbackOnChange(); diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 05ecc69f2..67c7a7d6c 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -1,12 +1,18 @@ #ifndef ARDUINO_CLOUD_THING_H_ #define ARDUINO_CLOUD_THING_H_ +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + #include "ArduinoCloudProperty.hpp" #include "ArduinoCloudPropertyContainer.hpp" #include "lib/LinkedList/LinkedList.h" -/* Constants for backwards compatibility */ +/****************************************************************************** + * CONSTANTS + ******************************************************************************/ static bool ON = true; static bool OFF = false; @@ -17,6 +23,10 @@ static long const MINUTES = 60; static long const HOURS = 3600; static long const DAYS = 86400; +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + class ArduinoCloudThing { public: ArduinoCloudThing(); From b378673f9f7c61eb8dcf2c0a582dcc9ec7d963dc Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Fri, 16 Nov 2018 11:30:16 +0100 Subject: [PATCH 084/175] Of course the conversion from cbor half float also needs to be fixed in the int property case --- ArduinoCloudThing.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 2774f3b82..44b8d0f89 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -12,7 +12,7 @@ * PRIVATE FREE FUNCTIONS ******************************************************************************/ -/* Source Idea from https://tools.ietf.org/html/rfc7049 : Page: 50 */ +/* Source Idea from https://tools.ietf.org/html/rfc7049 : Page: 50 */ double convertCborHalfFloatToDouble(uint16_t const half_val) { int exp = (half_val >> 10) & 0x1f; int mant = half_val & 0x3ff; @@ -293,10 +293,10 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt int_property->writeByCloud(static_cast(val)); } } else if (propValue.type == CborHalfFloatType) { - float val; + uint16_t val; cbor_value_get_half_float(&propValue, &val); if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(static_cast(val)); + int_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); } } int_property->execCallbackOnChange(); From a7903306ed996612d3f8536ec12c42237af99c8e Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Wed, 12 Dec 2018 11:10:02 +0100 Subject: [PATCH 085/175] Implementing first draft of state machine based CBOR decoding --- ArduinoCloudThing.cpp | 408 ++++++++++++++++++++++++++++-------------- 1 file changed, 274 insertions(+), 134 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 44b8d0f89..ef8e3625a 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -2,7 +2,7 @@ * INCLUDE ******************************************************************************/ -#include +//#include #include @@ -156,7 +156,7 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt CborError err; CborParser parser; - CborValue recursedMap, propValue, dataArray; + CborValue recursed_map, propValue, dataArray; String propName; Type propType; @@ -175,9 +175,9 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt while (!cbor_value_at_end(&dataArray)) { // parse cbor object - cbor_value_enter_container(&dataArray, &recursedMap); + cbor_value_enter_container(&dataArray, &recursed_map); - if (cbor_value_get_type(&recursedMap) != CborMapType) { + if (cbor_value_get_type(&recursed_map) != CborMapType) { // stop the decode when 1st item thai is not a cbor map is found. err = cbor_value_advance(&dataArray); // avoid infinite loop if it is not possible to advance to the next array value @@ -188,151 +188,291 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt continue; } else { - while (!cbor_value_at_end(&recursedMap)) { - // if the current element is not a cbor object as expected, skip it and go ahead. - if(cbor_value_get_type(&recursedMap) != CborMapType) { - - err = cbor_value_advance(&recursedMap); - if (err != CborNoError) { - break; - } - continue; - } - - CborValue name; - // chechk for the if the a property has a name, if yes Cbor value name will properly updated - cbor_value_map_find_value(&recursedMap, "n", &name); - - // check if a property has a name, of string type, if not do nothing and skip curtrent property - if (name.type != CborTextStringType) { - err = cbor_value_advance(&recursedMap); - // problem to advance to the next array object - if (err != CborNoError) - break; - - continue; - } - - // get the property name from cbor map as char* string - char *nameVal; size_t nameValSize; - err = cbor_value_dup_text_string(&name, &nameVal, &nameValSize, NULL); - if (err) { - break; // couldn't get the value of the field - } - // get the name of the received property as String object - propName = String(nameVal); - // used to avoid memory leaks (cbor_value_dup_text_string automatically perform a malloc) - free(nameVal); - - // Search for the device property with that name - ArduinoCloudProperty * bool_property = _property_cont.getPropertyBool (propName); - ArduinoCloudProperty * int_property = _property_cont.getPropertyInt (propName); - ArduinoCloudProperty * float_property = _property_cont.getPropertyFloat (propName); - ArduinoCloudProperty * string_property = _property_cont.getPropertyString(propName); - - // If property does not exist, skip it and do nothing. - if((bool_property == 0) && (int_property == 0) && (float_property == 0) && (string_property == 0)) + String property_name; + CborIntegerMapKey property_value_type; + CborValue recursed_map_save; + + enum class ParserState { + EnterPropertyMap, + PropertyNameLabel, + PropertyName, + PropertyValueLabel, + PropertyValue, + LeavePropertyMap, + Error + }; + ParserState current_state = ParserState::EnterPropertyMap, + next_state = ParserState::EnterPropertyMap; + + while (!cbor_value_at_end(&recursed_map)) { + +// // if the current element is not a cbor object as expected, skip it and go ahead. +// if(cbor_value_get_type(&recursedMap) != CborMapType) { +// +// err = cbor_value_advance(&recursedMap); +// if (err != CborNoError) { +// break; +// } +// continue; +// } + + do { - cbor_value_advance(&recursedMap); - continue; - } - - if (bool_property != 0) propType = Type::Bool; - else if(int_property != 0) propType = Type::Int; - else if(float_property != 0) propType = Type::Float; - else if(string_property != 0) propType = Type::String; - - if (propType == Type::Float && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { - if (propValue.type == CborDoubleType) { - double val; - // get the value of the property as a double - cbor_value_get_double(&propValue, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(static_cast(val)); - } - } else if (propValue.type == CborIntegerType) { - int val; - cbor_value_get_int(&propValue, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(static_cast(val)); - } - } else if (propValue.type == CborFloatType) { - float val; - cbor_value_get_float(&propValue, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(val); - } - } else if (propValue.type == CborHalfFloatType) { - uint16_t val; - cbor_value_get_half_float(&propValue, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); - } + current_state = next_state; + + switch(current_state) { + /* ParserState::EnterPropertyMap *****************************************/ + case ParserState::EnterPropertyMap: { + next_state = ParserState::Error; + + if(cbor_value_get_type(&recursed_map) == CborMapType) { + recursed_map_save = recursed_map; + if(cbor_value_enter_container(&recursed_map_save, &recursed_map) == CborNoError) { + next_state = ParserState::PropertyNameLabel; + } } - float_property->execCallbackOnChange(); - } else if (propType == Type::Int && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { - // if no key proper key was found, do nothing - if (propValue.type == CborIntegerType) { - int val; - cbor_value_get_int(&propValue, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(val); - } - } else if (propValue.type == CborDoubleType) { - // If a double value is received, a cast to int is performed(so it is still accepted) - double val; - cbor_value_get_double(&propValue, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(static_cast(val)); - } - } else if (propValue.type == CborFloatType) { - float val; - cbor_value_get_float(&propValue, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(static_cast(val)); - } - } else if (propValue.type == CborHalfFloatType) { - uint16_t val; - cbor_value_get_half_float(&propValue, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); + } + break; + /* ParserState::PropertyNameLabel ****************************************/ + case ParserState::PropertyNameLabel: { + next_state = ParserState::Error; + + if(cbor_value_is_text_string(&recursed_map)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(&recursed_map, &val, &val_size, &recursed_map) == CborNoError) { + if(strcmp(val, "n") == 0) { + next_state = ParserState::PropertyName; } + free(val); + } } - int_property->execCallbackOnChange(); - } else if (propType == Type::Bool && !cbor_value_map_find_value(&recursedMap, "vb", &propValue)) { - if (propValue.type == CborBooleanType) { - bool val; - cbor_value_get_boolean(&propValue, &val); - if(bool_property->isWriteableByCloud()) { - bool_property->writeByCloud(val); - bool_property->execCallbackOnChange(); - } + } + break; + /* ParserState::PropertyName *********************************************/ + case ParserState::PropertyName: { + next_state = ParserState::Error; + + if(cbor_value_is_text_string(&recursed_map)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(&recursed_map, &val, &val_size, &recursed_map) == CborNoError) { + property_name = String(val); + free(val); + next_state = ParserState::PropertyValueLabel; + } } - } else if (propType == Type::String && !cbor_value_map_find_value(&recursedMap, "vs", &propValue)){ - if (propValue.type == CborTextStringType) { - char *val; size_t valSize; - err = cbor_value_dup_text_string(&propValue, &val, &valSize, &propValue); - if(string_property->isWriteableByCloud()) { - string_property->writeByCloud(static_cast(val)); - string_property->execCallbackOnChange(); - } + } + break; + /* ParserState::PropertyValueLabel ***************************************/ + case ParserState::PropertyValueLabel: { + next_state = ParserState::Error; + + if(cbor_value_is_text_string(&recursed_map)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(&recursed_map, &val, &val_size, &recursed_map) == CborNoError) { + if(strcmp(val, "v" ) == 0) property_value_type = CborIntegerMapKey::Value; + else if(strcmp(val, "vs") == 0) property_value_type = CborIntegerMapKey::StringValue; + else if(strcmp(val, "vb") == 0) property_value_type = CborIntegerMapKey::BooleanValue; free(val); + next_state = ParserState::PropertyValue; + } + } + } + break; + /* ParserState::PropertyValue ********************************************/ + case ParserState::PropertyValue: { + next_state = ParserState::Error; + + /* TODO */ + + if(property_value_type == CborIntegerMapKey::BooleanValue) { + ArduinoCloudProperty * bool_property = _property_cont.getPropertyBool(property_name); + if(bool_property) + { + bool val = false; + if(cbor_value_get_boolean(&recursed_map, &val) == CborNoError) { + if(bool_property->isWriteableByCloud()) { + bool_property->writeByCloud(val); + bool_property->execCallbackOnChange(); + } + } + } + } + + if(cbor_value_advance(&recursed_map) == CborNoError) { + next_state = ParserState::LeavePropertyMap; } - } - // Continue to scan the cbor map - err = cbor_value_advance(&recursedMap); - if (err != CborNoError) { - break; - } + /* TODO */ + + + } + break; + /* ParserState::LeavePropertyMap *****************************************/ + case ParserState::LeavePropertyMap: { + next_state = ParserState::Error; + + if(cbor_value_leave_container(&recursed_map_save, &recursed_map) == CborNoError) { + next_state = ParserState::EnterPropertyMap; + } + } + break; + /* ParserState::Error ****************************************************/ + case ParserState::Error: { + return; + } + break; + } + } while(current_state != next_state); + + /* RETRIEVE THE NAME */ + + /* THE NEW WAY [{0: "test", ... */ + + /* Now we should have arrived at the first entry of the map - the key entry - this should be a + * integer and we should try to retrieve it. + */ + + + + /* THE OLD WAY [{"n": "test", ... */ + +// // chechk for the if the a property has a name, if yes Cbor value name will properly updated +// cbor_value_map_find_value(&recursedMap, "n", &name); +// +// // check if a property has a name, of string type, if not do nothing and skip curtrent property +// if (name.type != CborTextStringType) { +// err = cbor_value_advance(&recursedMap); +// // problem to advance to the next array object +// if (err != CborNoError) +// break; +// +// continue; +// } +// +// // get the property name from cbor map as char* string +// char *nameVal; size_t nameValSize; +// err = cbor_value_dup_text_string(&name, &nameVal, &nameValSize, NULL); +// if (err) { +// break; // couldn't get the value of the field +// } +// // get the name of the received property as String object +// propName = String(nameVal); +// // used to avoid memory leaks (cbor_value_dup_text_string automatically perform a malloc) +// free(nameVal); +// +// +// // Search for the device property with that name +// ArduinoCloudProperty * bool_property = _property_cont.getPropertyBool (propName); +// ArduinoCloudProperty * int_property = _property_cont.getPropertyInt (propName); +// ArduinoCloudProperty * float_property = _property_cont.getPropertyFloat (propName); +// ArduinoCloudProperty * string_property = _property_cont.getPropertyString(propName); +// +// // If property does not exist, skip it and do nothing. +// if((bool_property == 0) && (int_property == 0) && (float_property == 0) && (string_property == 0)) +// { +// cbor_value_advance(&recursedMap); +// continue; +// } +// +// if (bool_property != 0) propType = Type::Bool; +// else if(int_property != 0) propType = Type::Int; +// else if(float_property != 0) propType = Type::Float; +// else if(string_property != 0) propType = Type::String; +// +// if (propType == Type::Float && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { +// if (propValue.type == CborDoubleType) { +// double val; +// // get the value of the property as a double +// cbor_value_get_double(&propValue, &val); +// if(float_property->isWriteableByCloud()) { +// float_property->writeByCloud(static_cast(val)); +// } +// } else if (propValue.type == CborIntegerType) { +// int val; +// cbor_value_get_int(&propValue, &val); +// if(float_property->isWriteableByCloud()) { +// float_property->writeByCloud(static_cast(val)); +// } +// } else if (propValue.type == CborFloatType) { +// float val; +// cbor_value_get_float(&propValue, &val); +// if(float_property->isWriteableByCloud()) { +// float_property->writeByCloud(val); +// } +// } else if (propValue.type == CborHalfFloatType) { +// uint16_t val; +// cbor_value_get_half_float(&propValue, &val); +// if(float_property->isWriteableByCloud()) { +// float_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); +// } +// } +// float_property->execCallbackOnChange(); +// } else if (propType == Type::Int && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { +// // if no key proper key was found, do nothing +// if (propValue.type == CborIntegerType) { +// int val; +// cbor_value_get_int(&propValue, &val); +// if(int_property->isWriteableByCloud()) { +// int_property->writeByCloud(val); +// } +// } else if (propValue.type == CborDoubleType) { +// // If a double value is received, a cast to int is performed(so it is still accepted) +// double val; +// cbor_value_get_double(&propValue, &val); +// if(int_property->isWriteableByCloud()) { +// int_property->writeByCloud(static_cast(val)); +// } +// } else if (propValue.type == CborFloatType) { +// float val; +// cbor_value_get_float(&propValue, &val); +// if(int_property->isWriteableByCloud()) { +// int_property->writeByCloud(static_cast(val)); +// } +// } else if (propValue.type == CborHalfFloatType) { +// uint16_t val; +// cbor_value_get_half_float(&propValue, &val); +// if(int_property->isWriteableByCloud()) { +// int_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); +// } +// } +// int_property->execCallbackOnChange(); +// } else if (propType == Type::Bool && !cbor_value_map_find_value(&recursedMap, "vb", &propValue)) { +// if (propValue.type == CborBooleanType) { +// bool val; +// cbor_value_get_boolean(&propValue, &val); +// if(bool_property->isWriteableByCloud()) { +// bool_property->writeByCloud(val); +// bool_property->execCallbackOnChange(); +// } +// } +// } else if (propType == Type::String && !cbor_value_map_find_value(&recursedMap, "vs", &propValue)){ +// if (propValue.type == CborTextStringType) { +// char *val; size_t valSize; +// err = cbor_value_dup_text_string(&propValue, &val, &valSize, &propValue); +// if(string_property->isWriteableByCloud()) { +// string_property->writeByCloud(static_cast(val)); +// string_property->execCallbackOnChange(); +// } +// free(val); +// } +// } +// +// // Continue to scan the cbor map +// err = cbor_value_advance(&recursedMap); +// if (err != CborNoError) { +// break; +// } } } if (err != CborNoError) break; // Leave the current cbor object, and advance to the next one - cbor_value_leave_container(&dataArray, &recursedMap); + cbor_value_leave_container(&dataArray, &recursed_map); } } From 35082a6345fdb7f85aff7a07095702071f987f16 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Wed, 12 Dec 2018 17:14:01 +0100 Subject: [PATCH 086/175] More simplification of the CBOR parser - but not guaranteed to work in all test cases - test code needed --- ArduinoCloudThing.cpp | 519 +++++++++++++++++------------------------- 1 file changed, 212 insertions(+), 307 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index ef8e3625a..fbbad02bb 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -154,325 +154,230 @@ ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(String & prope void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const length) { - CborError err; - CborParser parser; - CborValue recursed_map, propValue, dataArray; - String propName; - Type propType; - - err = cbor_parser_init(payload, length, 0, &parser, &dataArray); - if(err) { - //Serial.println("Error in the parser creation."); - //Serial.println(cbor_error_string(err)); - return; - } - - // parse cbor data only if a cbor array is received. - if(dataArray.type != CborArrayType) - return; - - // main loop through the cbor array elements - while (!cbor_value_at_end(&dataArray)) { - - // parse cbor object - cbor_value_enter_container(&dataArray, &recursed_map); - - if (cbor_value_get_type(&recursed_map) != CborMapType) { - // stop the decode when 1st item thai is not a cbor map is found. - err = cbor_value_advance(&dataArray); - // avoid infinite loop if it is not possible to advance to the next array value - if (err != CborNoError) { - break; + CborParser parser; + CborValue data_array, recursed_map; + + if(cbor_parser_init(payload, length, 0, &parser, &data_array) != CborNoError) + return; + + if(data_array.type != CborArrayType) + return; + + if(cbor_value_enter_container(&data_array, &recursed_map) != CborNoError) + return; + + while(!cbor_value_at_end(&data_array)) { + + String property_name; + CborIntegerMapKey property_value_type; + CborValue recursed_map_save; + + enum class ParserState { + EnterPropertyMap, + PropertyNameLabel, + PropertyName, + PropertyValueLabel, + PropertyValue, + LeavePropertyMap, + Error + }; + ParserState current_state = ParserState::EnterPropertyMap, + next_state = ParserState::EnterPropertyMap; + + while (!cbor_value_at_end(&recursed_map)) { + + do + { + current_state = next_state; + + switch(current_state) { + /* ParserState::EnterPropertyMap *****************************************/ + case ParserState::EnterPropertyMap: { + next_state = ParserState::Error; + + if(cbor_value_get_type(&recursed_map) == CborMapType) { + recursed_map_save = recursed_map; + if(cbor_value_enter_container(&recursed_map_save, &recursed_map) == CborNoError) { + next_state = ParserState::PropertyNameLabel; + } + } + } + break; + /* ParserState::PropertyNameLabel ****************************************/ + case ParserState::PropertyNameLabel: { + next_state = ParserState::Error; + + if(cbor_value_is_text_string(&recursed_map)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(&recursed_map, &val, &val_size, &recursed_map) == CborNoError) { + if(strcmp(val, "n") == 0) { + next_state = ParserState::PropertyName; + } + free(val); } - // go to the next element - continue; - - } else { - - String property_name; - CborIntegerMapKey property_value_type; - CborValue recursed_map_save; - - enum class ParserState { - EnterPropertyMap, - PropertyNameLabel, - PropertyName, - PropertyValueLabel, - PropertyValue, - LeavePropertyMap, - Error - }; - ParserState current_state = ParserState::EnterPropertyMap, - next_state = ParserState::EnterPropertyMap; - - while (!cbor_value_at_end(&recursed_map)) { - -// // if the current element is not a cbor object as expected, skip it and go ahead. -// if(cbor_value_get_type(&recursedMap) != CborMapType) { -// -// err = cbor_value_advance(&recursedMap); -// if (err != CborNoError) { -// break; -// } -// continue; -// } - - do - { - current_state = next_state; - - switch(current_state) { - /* ParserState::EnterPropertyMap *****************************************/ - case ParserState::EnterPropertyMap: { - next_state = ParserState::Error; - - if(cbor_value_get_type(&recursed_map) == CborMapType) { - recursed_map_save = recursed_map; - if(cbor_value_enter_container(&recursed_map_save, &recursed_map) == CborNoError) { - next_state = ParserState::PropertyNameLabel; - } - } + } + } + break; + /* ParserState::PropertyName *********************************************/ + case ParserState::PropertyName: { + next_state = ParserState::Error; + + if(cbor_value_is_text_string(&recursed_map)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(&recursed_map, &val, &val_size, &recursed_map) == CborNoError) { + property_name = String(val); + free(val); + next_state = ParserState::PropertyValueLabel; + } + } + } + break; + /* ParserState::PropertyValueLabel ***************************************/ + case ParserState::PropertyValueLabel: { + next_state = ParserState::Error; + + if(cbor_value_is_text_string(&recursed_map)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(&recursed_map, &val, &val_size, &recursed_map) == CborNoError) { + if(strcmp(val, "v" ) == 0) property_value_type = CborIntegerMapKey::Value; + else if(strcmp(val, "vs") == 0) property_value_type = CborIntegerMapKey::StringValue; + else if(strcmp(val, "vb") == 0) property_value_type = CborIntegerMapKey::BooleanValue; + free(val); + next_state = ParserState::PropertyValue; + } + } + } + break; + /* ParserState::PropertyValue ********************************************/ + case ParserState::PropertyValue: { + next_state = ParserState::Error; + + /* VALUE PROPERTY ******************************************************/ + if(property_value_type == CborIntegerMapKey::Value) { + ArduinoCloudProperty * int_property = _property_cont.getPropertyInt (property_name); + ArduinoCloudProperty * float_property = _property_cont.getPropertyFloat(property_name); + + /* INT PROPERTY ******************************************************/ + if(int_property) { + if (recursed_map.type == CborIntegerType) { + int val = 0; + cbor_value_get_int(&recursed_map, &val); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(val); } - break; - /* ParserState::PropertyNameLabel ****************************************/ - case ParserState::PropertyNameLabel: { - next_state = ParserState::Error; - - if(cbor_value_is_text_string(&recursed_map)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(&recursed_map, &val, &val_size, &recursed_map) == CborNoError) { - if(strcmp(val, "n") == 0) { - next_state = ParserState::PropertyName; - } - free(val); - } - } + } else if (recursed_map.type == CborDoubleType) { + double val = 0.0; + cbor_value_get_double(&recursed_map, &val); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(static_cast(val)); } - break; - /* ParserState::PropertyName *********************************************/ - case ParserState::PropertyName: { - next_state = ParserState::Error; - - if(cbor_value_is_text_string(&recursed_map)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(&recursed_map, &val, &val_size, &recursed_map) == CborNoError) { - property_name = String(val); - free(val); - next_state = ParserState::PropertyValueLabel; - } - } + } else if (recursed_map.type == CborFloatType) { + float val = 0.0f; + cbor_value_get_float(&recursed_map, &val); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(static_cast(val)); } - break; - /* ParserState::PropertyValueLabel ***************************************/ - case ParserState::PropertyValueLabel: { - next_state = ParserState::Error; - - if(cbor_value_is_text_string(&recursed_map)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(&recursed_map, &val, &val_size, &recursed_map) == CborNoError) { - if(strcmp(val, "v" ) == 0) property_value_type = CborIntegerMapKey::Value; - else if(strcmp(val, "vs") == 0) property_value_type = CborIntegerMapKey::StringValue; - else if(strcmp(val, "vb") == 0) property_value_type = CborIntegerMapKey::BooleanValue; - free(val); - next_state = ParserState::PropertyValue; - } - } + } else if (recursed_map.type == CborHalfFloatType) { + uint16_t val = 0; + cbor_value_get_half_float(&recursed_map, &val); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); } - break; - /* ParserState::PropertyValue ********************************************/ - case ParserState::PropertyValue: { - next_state = ParserState::Error; - - /* TODO */ - - if(property_value_type == CborIntegerMapKey::BooleanValue) { - ArduinoCloudProperty * bool_property = _property_cont.getPropertyBool(property_name); - if(bool_property) - { - bool val = false; - if(cbor_value_get_boolean(&recursed_map, &val) == CborNoError) { - if(bool_property->isWriteableByCloud()) { - bool_property->writeByCloud(val); - bool_property->execCallbackOnChange(); - } - } - } - } - - if(cbor_value_advance(&recursed_map) == CborNoError) { - next_state = ParserState::LeavePropertyMap; - } - - /* TODO */ - + } + int_property->execCallbackOnChange(); + } + /* FLOAT PROPERTY ****************************************************/ + if(float_property) { + if (recursed_map.type == CborDoubleType) { + double val = 0.0; + cbor_value_get_double(&recursed_map, &val); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(static_cast(val)); } - break; - /* ParserState::LeavePropertyMap *****************************************/ - case ParserState::LeavePropertyMap: { - next_state = ParserState::Error; - - if(cbor_value_leave_container(&recursed_map_save, &recursed_map) == CborNoError) { - next_state = ParserState::EnterPropertyMap; - } + } else if (recursed_map.type == CborIntegerType) { + int val = 0; + cbor_value_get_int(&recursed_map, &val); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(static_cast(val)); } - break; - /* ParserState::Error ****************************************************/ - case ParserState::Error: { - return; + } else if (recursed_map.type == CborFloatType) { + float val = 0.0f; + cbor_value_get_float(&recursed_map, &val); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(val); } - break; + } else if (recursed_map.type == CborHalfFloatType) { + uint16_t val = 0; + cbor_value_get_half_float(&recursed_map, &val); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); } - } while(current_state != next_state); - - /* RETRIEVE THE NAME */ - - /* THE NEW WAY [{0: "test", ... */ - - /* Now we should have arrived at the first entry of the map - the key entry - this should be a - * integer and we should try to retrieve it. - */ - - - - /* THE OLD WAY [{"n": "test", ... */ - -// // chechk for the if the a property has a name, if yes Cbor value name will properly updated -// cbor_value_map_find_value(&recursedMap, "n", &name); -// -// // check if a property has a name, of string type, if not do nothing and skip curtrent property -// if (name.type != CborTextStringType) { -// err = cbor_value_advance(&recursedMap); -// // problem to advance to the next array object -// if (err != CborNoError) -// break; -// -// continue; -// } -// -// // get the property name from cbor map as char* string -// char *nameVal; size_t nameValSize; -// err = cbor_value_dup_text_string(&name, &nameVal, &nameValSize, NULL); -// if (err) { -// break; // couldn't get the value of the field -// } -// // get the name of the received property as String object -// propName = String(nameVal); -// // used to avoid memory leaks (cbor_value_dup_text_string automatically perform a malloc) -// free(nameVal); -// -// -// // Search for the device property with that name -// ArduinoCloudProperty * bool_property = _property_cont.getPropertyBool (propName); -// ArduinoCloudProperty * int_property = _property_cont.getPropertyInt (propName); -// ArduinoCloudProperty * float_property = _property_cont.getPropertyFloat (propName); -// ArduinoCloudProperty * string_property = _property_cont.getPropertyString(propName); -// -// // If property does not exist, skip it and do nothing. -// if((bool_property == 0) && (int_property == 0) && (float_property == 0) && (string_property == 0)) -// { -// cbor_value_advance(&recursedMap); -// continue; -// } -// -// if (bool_property != 0) propType = Type::Bool; -// else if(int_property != 0) propType = Type::Int; -// else if(float_property != 0) propType = Type::Float; -// else if(string_property != 0) propType = Type::String; -// -// if (propType == Type::Float && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { -// if (propValue.type == CborDoubleType) { -// double val; -// // get the value of the property as a double -// cbor_value_get_double(&propValue, &val); -// if(float_property->isWriteableByCloud()) { -// float_property->writeByCloud(static_cast(val)); -// } -// } else if (propValue.type == CborIntegerType) { -// int val; -// cbor_value_get_int(&propValue, &val); -// if(float_property->isWriteableByCloud()) { -// float_property->writeByCloud(static_cast(val)); -// } -// } else if (propValue.type == CborFloatType) { -// float val; -// cbor_value_get_float(&propValue, &val); -// if(float_property->isWriteableByCloud()) { -// float_property->writeByCloud(val); -// } -// } else if (propValue.type == CborHalfFloatType) { -// uint16_t val; -// cbor_value_get_half_float(&propValue, &val); -// if(float_property->isWriteableByCloud()) { -// float_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); -// } -// } -// float_property->execCallbackOnChange(); -// } else if (propType == Type::Int && !cbor_value_map_find_value(&recursedMap, "v", &propValue)) { -// // if no key proper key was found, do nothing -// if (propValue.type == CborIntegerType) { -// int val; -// cbor_value_get_int(&propValue, &val); -// if(int_property->isWriteableByCloud()) { -// int_property->writeByCloud(val); -// } -// } else if (propValue.type == CborDoubleType) { -// // If a double value is received, a cast to int is performed(so it is still accepted) -// double val; -// cbor_value_get_double(&propValue, &val); -// if(int_property->isWriteableByCloud()) { -// int_property->writeByCloud(static_cast(val)); -// } -// } else if (propValue.type == CborFloatType) { -// float val; -// cbor_value_get_float(&propValue, &val); -// if(int_property->isWriteableByCloud()) { -// int_property->writeByCloud(static_cast(val)); -// } -// } else if (propValue.type == CborHalfFloatType) { -// uint16_t val; -// cbor_value_get_half_float(&propValue, &val); -// if(int_property->isWriteableByCloud()) { -// int_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); -// } -// } -// int_property->execCallbackOnChange(); -// } else if (propType == Type::Bool && !cbor_value_map_find_value(&recursedMap, "vb", &propValue)) { -// if (propValue.type == CborBooleanType) { -// bool val; -// cbor_value_get_boolean(&propValue, &val); -// if(bool_property->isWriteableByCloud()) { -// bool_property->writeByCloud(val); -// bool_property->execCallbackOnChange(); -// } -// } -// } else if (propType == Type::String && !cbor_value_map_find_value(&recursedMap, "vs", &propValue)){ -// if (propValue.type == CborTextStringType) { -// char *val; size_t valSize; -// err = cbor_value_dup_text_string(&propValue, &val, &valSize, &propValue); -// if(string_property->isWriteableByCloud()) { -// string_property->writeByCloud(static_cast(val)); -// string_property->execCallbackOnChange(); -// } -// free(val); -// } -// } -// -// // Continue to scan the cbor map -// err = cbor_value_advance(&recursedMap); -// if (err != CborNoError) { -// break; -// } + } + float_property->execCallbackOnChange(); } - } + } + + /* BOOL PROPERTY *******************************************************/ + if(property_value_type == CborIntegerMapKey::BooleanValue) { + ArduinoCloudProperty * bool_property = _property_cont.getPropertyBool(property_name); + if(bool_property) + { + bool val = false; + if(cbor_value_get_boolean(&recursed_map, &val) == CborNoError) { + if(bool_property->isWriteableByCloud()) { + bool_property->writeByCloud(val); + bool_property->execCallbackOnChange(); + } + } + } + } + /* STRING PROPERTY *****************************************************/ + if(property_value_type == CborIntegerMapKey::StringValue) { + ArduinoCloudProperty * string_property = _property_cont.getPropertyString(property_name); + if(string_property) + { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(&recursed_map, &val, &val_size, &recursed_map) == CborNoError) { + if(string_property->isWriteableByCloud()) { + string_property->writeByCloud(static_cast(val)); + string_property->execCallbackOnChange(); + } + free(val); + } + } + } + + + if(cbor_value_advance(&recursed_map) == CborNoError) { + next_state = ParserState::LeavePropertyMap; + } - if (err != CborNoError) - break; - // Leave the current cbor object, and advance to the next one - cbor_value_leave_container(&dataArray, &recursed_map); + /* TODO */ + + } + break; + /* ParserState::LeavePropertyMap *****************************************/ + case ParserState::LeavePropertyMap: { + next_state = ParserState::Error; + + if(cbor_value_leave_container(&recursed_map_save, &recursed_map) == CborNoError) { + next_state = ParserState::EnterPropertyMap; + } + } + break; + /* ParserState::Error ****************************************************/ + case ParserState::Error: { + return; + } + break; + } + } while(current_state != next_state); } + } } From b3d2ff28baecd0bd1246bc31b5e9a06da4c94b6b Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 18 Dec 2018 07:34:24 +0100 Subject: [PATCH 087/175] Adding minimal 'Arduino.h' file which stubs the required methods in order to enable offline testing without an Arduino board --- ArduinoCloudThing.cpp | 168 ++++++++++++++++++++++-------------------- ArduinoCloudThing.h | 12 ++- 2 files changed, 100 insertions(+), 80 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index fbbad02bb..6d87521fe 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -2,7 +2,7 @@ * INCLUDE ******************************************************************************/ -//#include +#include #include @@ -46,11 +46,89 @@ static void utox8(uint32_t val, char* s) { #define Serial DebugSerial #endif +void extractProperty(ArduinoCloudProperty * int_property, CborValue * cbor_value) +{ + if (cbor_value->type == CborIntegerType) { + int val = 0; + cbor_value_get_int(cbor_value, &val); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(val); + } + } else if (cbor_value->type == CborDoubleType) { + double val = 0.0; + cbor_value_get_double(cbor_value, &val); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(static_cast(val)); + } + } else if (cbor_value->type == CborFloatType) { + float val = 0.0f; + cbor_value_get_float(cbor_value, &val); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(static_cast(val)); + } + } else if (cbor_value->type == CborHalfFloatType) { + uint16_t val = 0; + cbor_value_get_half_float(cbor_value, &val); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); + } + } +} + +void extractProperty(ArduinoCloudProperty * float_property, CborValue * cbor_value) { + if (cbor_value->type == CborDoubleType) { + double val = 0.0; + cbor_value_get_double(cbor_value, &val); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(static_cast(val)); + } + } else if (cbor_value->type == CborIntegerType) { + int val = 0; + cbor_value_get_int(cbor_value, &val); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(static_cast(val)); + } + } else if (cbor_value->type == CborFloatType) { + float val = 0.0f; + cbor_value_get_float(cbor_value, &val); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(val); + } + } else if (cbor_value->type == CborHalfFloatType) { + uint16_t val = 0; + cbor_value_get_half_float(cbor_value, &val); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); + } + } +} + +void extractProperty(ArduinoCloudProperty * bool_property, CborValue * cbor_value) { + bool val = false; + if(cbor_value_get_boolean(cbor_value, &val) == CborNoError) { + if(bool_property->isWriteableByCloud()) { + bool_property->writeByCloud(val); + } + } +} + +void extractProperty(ArduinoCloudProperty * string_property, CborValue * cbor_value) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(cbor_value, &val, &val_size, cbor_value) == CborNoError) { + if(string_property->isWriteableByCloud()) { + string_property->writeByCloud(static_cast(val)); + } + free(val); + } +} + /****************************************************************************** * CTOR/DTOR ******************************************************************************/ -ArduinoCloudThing::ArduinoCloudThing() { +ArduinoCloudThing::ArduinoCloudThing(CloudProtocol const cloud_protocol) +: _cloud_protocol(cloud_protocol) { #ifdef ARDUINO_ARCH_SAMD #define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x0080A00C) #define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x0080A040) @@ -196,7 +274,6 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt next_state = ParserState::Error; if(cbor_value_get_type(&recursed_map) == CborMapType) { - recursed_map_save = recursed_map; if(cbor_value_enter_container(&recursed_map_save, &recursed_map) == CborNoError) { next_state = ParserState::PropertyNameLabel; } @@ -262,104 +339,37 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt /* INT PROPERTY ******************************************************/ if(int_property) { - if (recursed_map.type == CborIntegerType) { - int val = 0; - cbor_value_get_int(&recursed_map, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(val); - } - } else if (recursed_map.type == CborDoubleType) { - double val = 0.0; - cbor_value_get_double(&recursed_map, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(static_cast(val)); - } - } else if (recursed_map.type == CborFloatType) { - float val = 0.0f; - cbor_value_get_float(&recursed_map, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(static_cast(val)); - } - } else if (recursed_map.type == CborHalfFloatType) { - uint16_t val = 0; - cbor_value_get_half_float(&recursed_map, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); - } - } + extractProperty(int_property, &recursed_map); int_property->execCallbackOnChange(); } - /* FLOAT PROPERTY ****************************************************/ if(float_property) { - if (recursed_map.type == CborDoubleType) { - double val = 0.0; - cbor_value_get_double(&recursed_map, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(static_cast(val)); - } - } else if (recursed_map.type == CborIntegerType) { - int val = 0; - cbor_value_get_int(&recursed_map, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(static_cast(val)); - } - } else if (recursed_map.type == CborFloatType) { - float val = 0.0f; - cbor_value_get_float(&recursed_map, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(val); - } - } else if (recursed_map.type == CborHalfFloatType) { - uint16_t val = 0; - cbor_value_get_half_float(&recursed_map, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); - } - } + extractProperty(float_property, &recursed_map); float_property->execCallbackOnChange(); } } - /* BOOL PROPERTY *******************************************************/ if(property_value_type == CborIntegerMapKey::BooleanValue) { ArduinoCloudProperty * bool_property = _property_cont.getPropertyBool(property_name); - if(bool_property) - { - bool val = false; - if(cbor_value_get_boolean(&recursed_map, &val) == CborNoError) { - if(bool_property->isWriteableByCloud()) { - bool_property->writeByCloud(val); - bool_property->execCallbackOnChange(); - } - } + if(bool_property) { + extractProperty(bool_property, &recursed_map); + bool_property->execCallbackOnChange(); } } /* STRING PROPERTY *****************************************************/ if(property_value_type == CborIntegerMapKey::StringValue) { ArduinoCloudProperty * string_property = _property_cont.getPropertyString(property_name); - if(string_property) - { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(&recursed_map, &val, &val_size, &recursed_map) == CborNoError) { - if(string_property->isWriteableByCloud()) { - string_property->writeByCloud(static_cast(val)); - string_property->execCallbackOnChange(); - } - free(val); - } + if(string_property) { + extractProperty(string_property, &recursed_map); + string_property->execCallbackOnChange(); } } if(cbor_value_advance(&recursed_map) == CborNoError) { next_state = ParserState::LeavePropertyMap; + /* FIXME: EXTRACING A STRING PROPERTY AUTOMATICALLY ADVANCES ... */ } - - /* TODO */ - - } break; /* ParserState::LeavePropertyMap *****************************************/ diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 67c7a7d6c..53a68c535 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -10,6 +10,15 @@ #include "lib/LinkedList/LinkedList.h" +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +enum class CloudProtocol { + V1, /* [{"n": "test", "vb": true}] */ + V2 /* [{0: "test", 4: true}] */ +}; + /****************************************************************************** * CONSTANTS ******************************************************************************/ @@ -29,7 +38,7 @@ static long const DAYS = 86400; class ArduinoCloudThing { public: - ArduinoCloudThing(); + ArduinoCloudThing(CloudProtocol const cloud_protocol = CloudProtocol::V1); void begin(); ArduinoCloudProperty & addPropertyReal(bool & property, String const & name, Permission const permission); @@ -44,6 +53,7 @@ class ArduinoCloudThing { private: + CloudProtocol const _cloud_protocol; bool _status = OFF; char _uuid[33]; ArduinoCloudPropertyContainer _property_cont; From f980cfbfe0cbcf6cee510c058a74a7f000d0335f Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 18 Dec 2018 10:34:34 +0100 Subject: [PATCH 088/175] Support for CloudProtocol::V1/V2 implemented for boolean values --- ArduinoCloudProperty.hpp | 13 +++++++++++-- ArduinoCloudProperty.ipp | 19 +++++++++++-------- ArduinoCloudPropertyContainer.cpp | 10 +++++----- ArduinoCloudPropertyContainer.hpp | 4 ++-- ArduinoCloudPropertyContainer.ipp | 4 ++-- ArduinoCloudThing.cpp | 2 +- ArduinoCloudThing.h | 9 --------- 7 files changed, 32 insertions(+), 29 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index ed5b98c1b..f39a27d5b 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -13,6 +13,15 @@ * TYPEDEF ******************************************************************************/ +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +enum class CloudProtocol { + V1, /* [{"n": "test", "vb": true}] */ + V2 /* [{0: "test", 4: true}] */ +}; + enum class Permission { Read, Write, ReadWrite }; @@ -71,7 +80,7 @@ class ArduinoCloudProperty { bool shouldBeUpdated () const; void execCallbackOnChange (); - void append (CborEncoder * encoder); + void append (CborEncoder * encoder, CloudProtocol const cloud_protocol); private: @@ -90,7 +99,7 @@ class ArduinoCloudProperty { unsigned long _last_updated_millis, _update_interval_millis; - void appendValue(CborEncoder * mapEncoder) const; + void appendValue(CborEncoder * mapEncoder, CloudProtocol const cloud_protocol) const; bool isValueDifferent(T const lhs, T const rhs) const; }; diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index d3cd664d1..95cf01b2f 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -76,14 +76,16 @@ void ArduinoCloudProperty::execCallbackOnChange() { } template -void ArduinoCloudProperty::append(CborEncoder * encoder) { +void ArduinoCloudProperty::append(CborEncoder * encoder, CloudProtocol const cloud_protocol) { if (isReadableByCloud()) { CborEncoder mapEncoder; cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); - cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Name)); + + if (cloud_protocol == CloudProtocol::V1) cbor_encode_text_stringz(&mapEncoder, "n"); + else if(cloud_protocol == CloudProtocol::V2) cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::Name)); cbor_encode_text_stringz(&mapEncoder, _name.c_str()); - appendValue(&mapEncoder); + appendValue(&mapEncoder, cloud_protocol); cbor_encoder_close_container(encoder, &mapEncoder); _shadow_property = _property; @@ -97,25 +99,26 @@ void ArduinoCloudProperty::append(CborEncoder * encoder) { ******************************************************************************/ template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder, CloudProtocol const cloud_protocol) const { + if (cloud_protocol == CloudProtocol::V1) cbor_encode_text_stringz(mapEncoder, "vb"); + else if(cloud_protocol == CloudProtocol::V2) cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); cbor_encode_boolean(mapEncoder, _property); } template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder, CloudProtocol const cloud_protocol) const { cbor_encode_int(mapEncoder, static_cast(CborIntegerMapKey::Value)); cbor_encode_int(mapEncoder, _property); } template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder, CloudProtocol const cloud_protocol) const { cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); cbor_encode_float(mapEncoder, _property); } template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder, CloudProtocol const cloud_protocol) const { cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::StringValue)); cbor_encode_text_stringz(mapEncoder, _property.c_str()); } diff --git a/ArduinoCloudPropertyContainer.cpp b/ArduinoCloudPropertyContainer.cpp index b62a92526..543ffccfe 100644 --- a/ArduinoCloudPropertyContainer.cpp +++ b/ArduinoCloudPropertyContainer.cpp @@ -27,9 +27,9 @@ int ArduinoCloudPropertyContainer::getNumOfChangedProperties() { return num_changes_properties; } -void ArduinoCloudPropertyContainer::appendChangedProperties(CborEncoder * arrayEncoder) { - appendChangedProperties (_bool_property_list, arrayEncoder); - appendChangedProperties (_int_property_list, arrayEncoder); - appendChangedProperties (_float_property_list, arrayEncoder); - appendChangedProperties(_string_property_list, arrayEncoder); +void ArduinoCloudPropertyContainer::appendChangedProperties(CborEncoder * arrayEncoder, CloudProtocol const cloud_protocol) { + appendChangedProperties (_bool_property_list, arrayEncoder, cloud_protocol); + appendChangedProperties (_int_property_list, arrayEncoder, cloud_protocol); + appendChangedProperties (_float_property_list, arrayEncoder, cloud_protocol); + appendChangedProperties(_string_property_list, arrayEncoder, cloud_protocol); } diff --git a/ArduinoCloudPropertyContainer.hpp b/ArduinoCloudPropertyContainer.hpp index c19779fc8..7c3a5e952 100644 --- a/ArduinoCloudPropertyContainer.hpp +++ b/ArduinoCloudPropertyContainer.hpp @@ -19,7 +19,7 @@ class ArduinoCloudPropertyContainer { bool isPropertyInContainer (Type const type, String const & name); int getNumOfChangedProperties(); - void appendChangedProperties (CborEncoder * arrayEncoder); + void appendChangedProperties (CborEncoder * arrayEncoder, CloudProtocol const cloud_protocol); inline ArduinoCloudProperty * getPropertyBool (String const & name) { return getProperty(_bool_property_list, name); } inline ArduinoCloudProperty * getPropertyInt (String const & name) { return getProperty(_int_property_list, name); } @@ -48,7 +48,7 @@ class ArduinoCloudPropertyContainer { int getNumOfChangedProperties(LinkedList *> & list); template - void appendChangedProperties(LinkedList *> & list, CborEncoder * arrayEncoder); + void appendChangedProperties(LinkedList *> & list, CborEncoder * arrayEncoder, CloudProtocol const cloud_protocol); }; diff --git a/ArduinoCloudPropertyContainer.ipp b/ArduinoCloudPropertyContainer.ipp index b3efc423f..6a58f5ba3 100644 --- a/ArduinoCloudPropertyContainer.ipp +++ b/ArduinoCloudPropertyContainer.ipp @@ -35,11 +35,11 @@ int ArduinoCloudPropertyContainer::getNumOfChangedProperties(LinkedList -void ArduinoCloudPropertyContainer::appendChangedProperties(LinkedList *> & list, CborEncoder * arrayEncoder) { +void ArduinoCloudPropertyContainer::appendChangedProperties(LinkedList *> & list, CborEncoder * arrayEncoder, CloudProtocol const cloud_protocol) { for (int i = 0; i < list.size(); i++) { ArduinoCloudProperty * p = list.get(i); if (p->shouldBeUpdated() && p->isReadableByCloud()) { - p->append(arrayEncoder); + p->append(arrayEncoder, cloud_protocol); } } } diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 6d87521fe..0d6a6e076 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -171,7 +171,7 @@ int ArduinoCloudThing::encode(uint8_t * data, size_t const size) { return -1; } - _property_cont.appendChangedProperties(&arrayEncoder); + _property_cont.appendChangedProperties(&arrayEncoder, _cloud_protocol); err = cbor_encoder_close_container(&encoder, &arrayEncoder); diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 53a68c535..b16c9b3af 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -10,15 +10,6 @@ #include "lib/LinkedList/LinkedList.h" -/****************************************************************************** - * TYPEDEF - ******************************************************************************/ - -enum class CloudProtocol { - V1, /* [{"n": "test", "vb": true}] */ - V2 /* [{0: "test", 4: true}] */ -}; - /****************************************************************************** * CONSTANTS ******************************************************************************/ From ce885d1debe96bbcd22d8c44e10b284405f67298 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 18 Dec 2018 10:51:10 +0100 Subject: [PATCH 089/175] Adding 'encode' test for int property --- ArduinoCloudProperty.ipp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 95cf01b2f..41d4458c9 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -107,7 +107,8 @@ inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder, Cl template <> inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder, CloudProtocol const cloud_protocol) const { - cbor_encode_int(mapEncoder, static_cast(CborIntegerMapKey::Value)); + if (cloud_protocol == CloudProtocol::V1) cbor_encode_text_stringz(mapEncoder, "v"); + else if(cloud_protocol == CloudProtocol::V2) cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); cbor_encode_int(mapEncoder, _property); } From bf40b74d18fe69fe6fa0542486781a6f4ec9ca88 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 18 Dec 2018 11:29:41 +0100 Subject: [PATCH 090/175] Implementing test and production code for encoding float properties for both protocols --- ArduinoCloudProperty.ipp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 41d4458c9..3633ef80a 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -114,7 +114,8 @@ inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder, Clo template <> inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder, CloudProtocol const cloud_protocol) const { - cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); + if (cloud_protocol == CloudProtocol::V1) cbor_encode_text_stringz(mapEncoder, "v"); + else if(cloud_protocol == CloudProtocol::V2) cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); cbor_encode_float(mapEncoder, _property); } From 1627d5613290eeb40b7d857c9fbca59c25da2117 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Mon, 7 Jan 2019 11:16:57 +0100 Subject: [PATCH 091/175] Implementation of first draft of encode complete --- ArduinoCloudThing.cpp | 290 ++++++++++++++++++++++-------------------- 1 file changed, 150 insertions(+), 140 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 0d6a6e076..9e09c32df 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -233,161 +233,171 @@ ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(String & prope void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const length) { CborParser parser; - CborValue data_array, recursed_map; + CborValue array_iter, + map_iter, + value_iter; - if(cbor_parser_init(payload, length, 0, &parser, &data_array) != CborNoError) + if(cbor_parser_init(payload, length, 0, &parser, &array_iter) != CborNoError) return; - if(data_array.type != CborArrayType) + if(array_iter.type != CborArrayType) return; - if(cbor_value_enter_container(&data_array, &recursed_map) != CborNoError) + if(cbor_value_enter_container(&array_iter, &map_iter) != CborNoError) return; - while(!cbor_value_at_end(&data_array)) { - - String property_name; - CborIntegerMapKey property_value_type; - CborValue recursed_map_save; - - enum class ParserState { - EnterPropertyMap, - PropertyNameLabel, - PropertyName, - PropertyValueLabel, - PropertyValue, - LeavePropertyMap, - Error - }; - ParserState current_state = ParserState::EnterPropertyMap, - next_state = ParserState::EnterPropertyMap; - - while (!cbor_value_at_end(&recursed_map)) { - - do - { - current_state = next_state; - - switch(current_state) { - /* ParserState::EnterPropertyMap *****************************************/ - case ParserState::EnterPropertyMap: { - next_state = ParserState::Error; - - if(cbor_value_get_type(&recursed_map) == CborMapType) { - if(cbor_value_enter_container(&recursed_map_save, &recursed_map) == CborNoError) { - next_state = ParserState::PropertyNameLabel; - } - } + String property_name; + CborIntegerMapKey property_value_type; + + enum class MapParserState { + EnterPropertyMap, + PropertyNameLabel, + PropertyName, + PropertyValueLabel, + PropertyValue, + LeavePropertyMap, + Complete, + Error + }; + MapParserState current_state = MapParserState::EnterPropertyMap, + next_state = MapParserState::EnterPropertyMap; + + do + { + current_state = next_state; + + switch(current_state) { + /* MapParserState::EnterPropertyMap *****************************************/ + case MapParserState::EnterPropertyMap: { + next_state = MapParserState::Error; + + if(cbor_value_get_type(&map_iter) == CborMapType) { + if(cbor_value_enter_container(&map_iter, &value_iter) == CborNoError) { + next_state = MapParserState::PropertyNameLabel; } - break; - /* ParserState::PropertyNameLabel ****************************************/ - case ParserState::PropertyNameLabel: { - next_state = ParserState::Error; - - if(cbor_value_is_text_string(&recursed_map)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(&recursed_map, &val, &val_size, &recursed_map) == CborNoError) { - if(strcmp(val, "n") == 0) { - next_state = ParserState::PropertyName; - } - free(val); - } + } + } + break; + /* MapParserState::PropertyNameLabel ****************************************/ + case MapParserState::PropertyNameLabel: { + next_state = MapParserState::Error; + + if(cbor_value_is_text_string(&value_iter)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(&value_iter, &val, &val_size, &value_iter) == CborNoError) { + if(strcmp(val, "n") == 0) { + next_state = MapParserState::PropertyName; } + free(val); } - break; - /* ParserState::PropertyName *********************************************/ - case ParserState::PropertyName: { - next_state = ParserState::Error; - - if(cbor_value_is_text_string(&recursed_map)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(&recursed_map, &val, &val_size, &recursed_map) == CborNoError) { - property_name = String(val); - free(val); - next_state = ParserState::PropertyValueLabel; - } - } + } + } + break; + /* MapParserState::PropertyName *********************************************/ + case MapParserState::PropertyName: { + next_state = MapParserState::Error; + + if(cbor_value_is_text_string(&value_iter)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(&value_iter, &val, &val_size, &value_iter) == CborNoError) { + property_name = String(val); + free(val); + next_state = MapParserState::PropertyValueLabel; } - break; - /* ParserState::PropertyValueLabel ***************************************/ - case ParserState::PropertyValueLabel: { - next_state = ParserState::Error; - - if(cbor_value_is_text_string(&recursed_map)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(&recursed_map, &val, &val_size, &recursed_map) == CborNoError) { - if(strcmp(val, "v" ) == 0) property_value_type = CborIntegerMapKey::Value; - else if(strcmp(val, "vs") == 0) property_value_type = CborIntegerMapKey::StringValue; - else if(strcmp(val, "vb") == 0) property_value_type = CborIntegerMapKey::BooleanValue; - free(val); - next_state = ParserState::PropertyValue; - } - } + } + } + break; + /* MapParserState::PropertyValueLabel ***************************************/ + case MapParserState::PropertyValueLabel: { + next_state = MapParserState::Error; + + if(cbor_value_is_text_string(&value_iter)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(&value_iter, &val, &val_size, &value_iter) == CborNoError) { + if(strcmp(val, "v" ) == 0) property_value_type = CborIntegerMapKey::Value; + else if(strcmp(val, "vs") == 0) property_value_type = CborIntegerMapKey::StringValue; + else if(strcmp(val, "vb") == 0) property_value_type = CborIntegerMapKey::BooleanValue; + free(val); + next_state = MapParserState::PropertyValue; } - break; - /* ParserState::PropertyValue ********************************************/ - case ParserState::PropertyValue: { - next_state = ParserState::Error; - - /* VALUE PROPERTY ******************************************************/ - if(property_value_type == CborIntegerMapKey::Value) { - ArduinoCloudProperty * int_property = _property_cont.getPropertyInt (property_name); - ArduinoCloudProperty * float_property = _property_cont.getPropertyFloat(property_name); - - /* INT PROPERTY ******************************************************/ - if(int_property) { - extractProperty(int_property, &recursed_map); - int_property->execCallbackOnChange(); - } - /* FLOAT PROPERTY ****************************************************/ - if(float_property) { - extractProperty(float_property, &recursed_map); - float_property->execCallbackOnChange(); - } - } - /* BOOL PROPERTY *******************************************************/ - if(property_value_type == CborIntegerMapKey::BooleanValue) { - ArduinoCloudProperty * bool_property = _property_cont.getPropertyBool(property_name); - if(bool_property) { - extractProperty(bool_property, &recursed_map); - bool_property->execCallbackOnChange(); - } - } - /* STRING PROPERTY *****************************************************/ - if(property_value_type == CborIntegerMapKey::StringValue) { - ArduinoCloudProperty * string_property = _property_cont.getPropertyString(property_name); - if(string_property) { - extractProperty(string_property, &recursed_map); - string_property->execCallbackOnChange(); - } - } - - - if(cbor_value_advance(&recursed_map) == CborNoError) { - next_state = ParserState::LeavePropertyMap; - /* FIXME: EXTRACING A STRING PROPERTY AUTOMATICALLY ADVANCES ... */ - } + } + } + break; + /* MapParserState::PropertyValue ********************************************/ + case MapParserState::PropertyValue: { + next_state = MapParserState::Error; + + /* VALUE PROPERTY ******************************************************/ + if(property_value_type == CborIntegerMapKey::Value) { + ArduinoCloudProperty * int_property = _property_cont.getPropertyInt (property_name); + ArduinoCloudProperty * float_property = _property_cont.getPropertyFloat(property_name); + + /* INT PROPERTY ******************************************************/ + if(int_property) { + extractProperty(int_property, &value_iter); + int_property->execCallbackOnChange(); } - break; - /* ParserState::LeavePropertyMap *****************************************/ - case ParserState::LeavePropertyMap: { - next_state = ParserState::Error; - - if(cbor_value_leave_container(&recursed_map_save, &recursed_map) == CborNoError) { - next_state = ParserState::EnterPropertyMap; - } + /* FLOAT PROPERTY ****************************************************/ + if(float_property) { + extractProperty(float_property, &value_iter); + float_property->execCallbackOnChange(); } - break; - /* ParserState::Error ****************************************************/ - case ParserState::Error: { - return; + } + /* BOOL PROPERTY *******************************************************/ + if(property_value_type == CborIntegerMapKey::BooleanValue) { + ArduinoCloudProperty * bool_property = _property_cont.getPropertyBool(property_name); + if(bool_property) { + extractProperty(bool_property, &value_iter); + bool_property->execCallbackOnChange(); } - break; + } + /* STRING PROPERTY *****************************************************/ + if(property_value_type == CborIntegerMapKey::StringValue) { + ArduinoCloudProperty * string_property = _property_cont.getPropertyString(property_name); + if(string_property) { + extractProperty(string_property, &value_iter); + string_property->execCallbackOnChange(); } - } while(current_state != next_state); + } + + /* It is not necessary to advance it we have a string property + * because extracting it automatically advances the iterator. + */ + if(property_value_type != CborIntegerMapKey::StringValue) { + if(cbor_value_advance(&value_iter) == CborNoError) { + next_state = MapParserState::LeavePropertyMap; + } + } } - } + break; + /* MapParserState::LeavePropertyMap *****************************************/ + case MapParserState::LeavePropertyMap: { + next_state = MapParserState::Error; + + if(cbor_value_leave_container(&map_iter, &value_iter) == CborNoError) { + if(!cbor_value_at_end(&map_iter)) { + next_state = MapParserState::EnterPropertyMap; + } + else { + next_state = MapParserState::Complete; + } + + } + } + break; + /* MapParserState::Complete ****************************************************/ + case MapParserState::Complete: { + /* Nothing to do */ + } + break; + /* MapParserState::Error ****************************************************/ + case MapParserState::Error: { + return; + } + break; + } + } while(current_state != next_state && current_state != MapParserState::Complete); } From d8607005e8ac77bee810c2fb60a43b997d14e7ce Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Mon, 7 Jan 2019 12:22:36 +0100 Subject: [PATCH 092/175] Since we do have a definite 'end' state of the do while loop we can simplify the loop condition --- ArduinoCloudThing.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 9e09c32df..fb197e650 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -260,12 +260,10 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt Error }; MapParserState current_state = MapParserState::EnterPropertyMap, - next_state = MapParserState::EnterPropertyMap; + next_state; - do + while(current_state != MapParserState::Complete) { - current_state = next_state; - switch(current_state) { /* MapParserState::EnterPropertyMap *****************************************/ case MapParserState::EnterPropertyMap: { @@ -399,5 +397,7 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt } break; } - } while(current_state != next_state && current_state != MapParserState::Complete); + + current_state = next_state; + } } From bcb6360689ef1b5982e32f18c0e8ae9caaa528d0 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 22 Jan 2019 07:29:56 +0100 Subject: [PATCH 093/175] Supporting both encoding protocols for String --- ArduinoCloudProperty.ipp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 3633ef80a..401ed9e55 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -121,7 +121,8 @@ inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder, C template <> inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder, CloudProtocol const cloud_protocol) const { - cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::StringValue)); + if (cloud_protocol == CloudProtocol::V1) cbor_encode_text_stringz(mapEncoder, "vs"); + else if(cloud_protocol == CloudProtocol::V2) cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::StringValue)); cbor_encode_text_stringz(mapEncoder, _property.c_str()); } From 18ddbe6b6e63acbb07a93464414654862a7a2ca8 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Wed, 23 Jan 2019 08:37:47 +0100 Subject: [PATCH 094/175] Fixing bug when decoding multiple strings --- ArduinoCloudThing.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index fb197e650..e13832a52 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -337,11 +337,19 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt if(int_property) { extractProperty(int_property, &value_iter); int_property->execCallbackOnChange(); + + if(cbor_value_advance(&value_iter) == CborNoError) { + next_state = MapParserState::LeavePropertyMap; + } } /* FLOAT PROPERTY ****************************************************/ if(float_property) { extractProperty(float_property, &value_iter); float_property->execCallbackOnChange(); + + if(cbor_value_advance(&value_iter) == CborNoError) { + next_state = MapParserState::LeavePropertyMap; + } } } /* BOOL PROPERTY *******************************************************/ @@ -350,6 +358,10 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt if(bool_property) { extractProperty(bool_property, &value_iter); bool_property->execCallbackOnChange(); + + if(cbor_value_advance(&value_iter) == CborNoError) { + next_state = MapParserState::LeavePropertyMap; + } } } /* STRING PROPERTY *****************************************************/ @@ -358,14 +370,10 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt if(string_property) { extractProperty(string_property, &value_iter); string_property->execCallbackOnChange(); - } - } - /* It is not necessary to advance it we have a string property - * because extracting it automatically advances the iterator. - */ - if(property_value_type != CborIntegerMapKey::StringValue) { - if(cbor_value_advance(&value_iter) == CborNoError) { + /* It is not necessary to advance it we have a string property + * because extracting it automatically advances the iterator. + */ next_state = MapParserState::LeavePropertyMap; } } From 14401a03b57efae4d9e658d8427aef924e33e225 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Wed, 23 Jan 2019 12:37:49 +0100 Subject: [PATCH 095/175] Adding method 'getInitialMinDeltaPropertyValue' which provides the initial type dependend value for the member variable _min_delta_property --- ArduinoCloudProperty.hpp | 2 ++ ArduinoCloudProperty.ipp | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index f39a27d5b..ab233a2e3 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -102,6 +102,8 @@ class ArduinoCloudProperty { void appendValue(CborEncoder * mapEncoder, CloudProtocol const cloud_protocol) const; bool isValueDifferent(T const lhs, T const rhs) const; + T getInitialMinDeltaPropertyValue() const; + }; /****************************************************************************** diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 401ed9e55..07e8c6d68 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -11,7 +11,7 @@ ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, _update_callback_func(NULL), _update_policy(UpdatePolicy::OnChange), _has_been_updated_once(false), - _min_delta_property(T(0)), + _min_delta_property(getInitialMinDeltaPropertyValue()), _min_time_between_updates_millis(0), _last_updated_millis(0), _update_interval_millis(0) @@ -145,3 +145,23 @@ template <> inline bool ArduinoCloudProperty::isValueDifferent(String const lhs, String const rhs) const { return (lhs != rhs); } + +template <> +inline bool ArduinoCloudProperty::getInitialMinDeltaPropertyValue() const { + return false; +} + +template <> +inline int ArduinoCloudProperty::getInitialMinDeltaPropertyValue() const { + return 0; +} + +template <> +inline float ArduinoCloudProperty::getInitialMinDeltaPropertyValue() const { + return 0.0f; +} + +template <> +inline String ArduinoCloudProperty::getInitialMinDeltaPropertyValue() const { + return String(""); +} \ No newline at end of file From f74487abbcd95aa3095394e8066e2e487166163d Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Thu, 24 Jan 2019 08:03:51 +0100 Subject: [PATCH 096/175] Adding decoding implementation for CloudProtocol::V2 --- ArduinoCloudThing.cpp | 63 ++++++++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index e13832a52..d3ef17e4e 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -280,14 +280,29 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt case MapParserState::PropertyNameLabel: { next_state = MapParserState::Error; - if(cbor_value_is_text_string(&value_iter)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(&value_iter, &val, &val_size, &value_iter) == CborNoError) { - if(strcmp(val, "n") == 0) { - next_state = MapParserState::PropertyName; + if(_cloud_protocol == CloudProtocol::V1) { + if(cbor_value_is_text_string(&value_iter)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(&value_iter, &val, &val_size, &value_iter) == CborNoError) { + if(strcmp(val, "n") == 0) { + next_state = MapParserState::PropertyName; + } + free(val); + } + } + } + + if(_cloud_protocol == CloudProtocol::V2) { + if(cbor_value_is_integer(&value_iter)) { + int val = 0; + if(cbor_value_get_int(&value_iter, &val) == CborNoError) { + if(val == static_cast(CborIntegerMapKey::Name)) { + if(cbor_value_advance(&value_iter) == CborNoError) { + next_state = MapParserState::PropertyName; + } + } } - free(val); } } } @@ -311,15 +326,31 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt case MapParserState::PropertyValueLabel: { next_state = MapParserState::Error; - if(cbor_value_is_text_string(&value_iter)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(&value_iter, &val, &val_size, &value_iter) == CborNoError) { - if(strcmp(val, "v" ) == 0) property_value_type = CborIntegerMapKey::Value; - else if(strcmp(val, "vs") == 0) property_value_type = CborIntegerMapKey::StringValue; - else if(strcmp(val, "vb") == 0) property_value_type = CborIntegerMapKey::BooleanValue; - free(val); - next_state = MapParserState::PropertyValue; + if(_cloud_protocol == CloudProtocol::V1) { + if(cbor_value_is_text_string(&value_iter)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(&value_iter, &val, &val_size, &value_iter) == CborNoError) { + if (strcmp(val, "v" ) == 0) property_value_type = CborIntegerMapKey::Value; + else if(strcmp(val, "vs") == 0) property_value_type = CborIntegerMapKey::StringValue; + else if(strcmp(val, "vb") == 0) property_value_type = CborIntegerMapKey::BooleanValue; + free(val); + next_state = MapParserState::PropertyValue; + } + } + } + + if(_cloud_protocol == CloudProtocol::V2) { + if(cbor_value_is_integer(&value_iter)) { + int val = 0; + if(cbor_value_get_int(&value_iter, &val) == CborNoError) { + if (val == static_cast(CborIntegerMapKey::Value )) property_value_type = CborIntegerMapKey::Value; + else if(val == static_cast(CborIntegerMapKey::StringValue )) property_value_type = CborIntegerMapKey::StringValue; + else if(val == static_cast(CborIntegerMapKey::BooleanValue)) property_value_type = CborIntegerMapKey::BooleanValue; + if(cbor_value_advance(&value_iter) == CborNoError) { + next_state = MapParserState::PropertyValue; + } + } } } } From 661abb419a9ee1030a6853dfd66ca09a3158543b Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Mon, 4 Feb 2019 16:20:40 +0100 Subject: [PATCH 097/175] Adding implementation and test code for decoding a base tag property --- ArduinoCloudThing.cpp | 89 +++++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 28 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index d3ef17e4e..3c8254888 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -246,38 +246,42 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt if(cbor_value_enter_container(&array_iter, &map_iter) != CborNoError) return; - String property_name; + String base_name, + property_name; CborIntegerMapKey property_value_type; enum class MapParserState { - EnterPropertyMap, - PropertyNameLabel, + EnterMap, + MapKey, + BaseName, + BaseTime, + Time, PropertyName, - PropertyValueLabel, + PropertyType, PropertyValue, - LeavePropertyMap, + LeaveMap, Complete, Error }; - MapParserState current_state = MapParserState::EnterPropertyMap, + MapParserState current_state = MapParserState::EnterMap, next_state; while(current_state != MapParserState::Complete) { switch(current_state) { - /* MapParserState::EnterPropertyMap *****************************************/ - case MapParserState::EnterPropertyMap: { + /* MapParserState::EnterMap *****************************************/ + case MapParserState::EnterMap: { next_state = MapParserState::Error; if(cbor_value_get_type(&map_iter) == CborMapType) { if(cbor_value_enter_container(&map_iter, &value_iter) == CborNoError) { - next_state = MapParserState::PropertyNameLabel; + next_state = MapParserState::MapKey; } } } break; - /* MapParserState::PropertyNameLabel ****************************************/ - case MapParserState::PropertyNameLabel: { + /* MapParserState::MapKey ****************************************/ + case MapParserState::MapKey: { next_state = MapParserState::Error; if(_cloud_protocol == CloudProtocol::V1) { @@ -285,9 +289,10 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt char * val = 0; size_t val_size = 0; if(cbor_value_dup_text_string(&value_iter, &val, &val_size, &value_iter) == CborNoError) { - if(strcmp(val, "n") == 0) { - next_state = MapParserState::PropertyName; - } + if (strcmp(val, "n" ) == 0) { next_state = MapParserState::PropertyName; } + else if(strcmp(val, "bn") == 0) { next_state = MapParserState::BaseName; } + else if(strcmp(val, "bt") == 0) { next_state = MapParserState::BaseTime; } + else if(strcmp(val, "t" ) == 0) { next_state = MapParserState::Time; } free(val); } } @@ -297,16 +302,44 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt if(cbor_value_is_integer(&value_iter)) { int val = 0; if(cbor_value_get_int(&value_iter, &val) == CborNoError) { - if(val == static_cast(CborIntegerMapKey::Name)) { - if(cbor_value_advance(&value_iter) == CborNoError) { - next_state = MapParserState::PropertyName; - } + if(cbor_value_advance(&value_iter) == CborNoError) { + if (val == static_cast(CborIntegerMapKey::Name )) { next_state = MapParserState::PropertyName; } + else if(val == static_cast(CborIntegerMapKey::BaseName)) { next_state = MapParserState::BaseName; } + else if(val == static_cast(CborIntegerMapKey::BaseTime)) { next_state = MapParserState::BaseTime; } + else if(val == static_cast(CborIntegerMapKey::Time )) { next_state = MapParserState::Time; } } } } } } break; + /* MapParserState::BaseName *****************************************/ + case MapParserState::BaseName: { + next_state = MapParserState::Error; + + if(cbor_value_is_text_string(&value_iter)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(&value_iter, &val, &val_size, &value_iter) == CborNoError) { + base_name = String(val); + free(val); + next_state = MapParserState::MapKey; + } + } + } + break; + + /* MapParserState::BaseTime *****************************************/ + case MapParserState::BaseTime: { + /* TODO */ + } + break; + + /* MapParserState::Time *****************************************/ + case MapParserState::Time : { + /* TODO */ + } + break; /* MapParserState::PropertyName *********************************************/ case MapParserState::PropertyName: { next_state = MapParserState::Error; @@ -317,13 +350,13 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt if(cbor_value_dup_text_string(&value_iter, &val, &val_size, &value_iter) == CborNoError) { property_name = String(val); free(val); - next_state = MapParserState::PropertyValueLabel; + next_state = MapParserState::PropertyType; } } } break; - /* MapParserState::PropertyValueLabel ***************************************/ - case MapParserState::PropertyValueLabel: { + /* MapParserState::PropertyType ***************************************/ + case MapParserState::PropertyType: { next_state = MapParserState::Error; if(_cloud_protocol == CloudProtocol::V1) { @@ -370,7 +403,7 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt int_property->execCallbackOnChange(); if(cbor_value_advance(&value_iter) == CborNoError) { - next_state = MapParserState::LeavePropertyMap; + next_state = MapParserState::LeaveMap; } } /* FLOAT PROPERTY ****************************************************/ @@ -379,7 +412,7 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt float_property->execCallbackOnChange(); if(cbor_value_advance(&value_iter) == CborNoError) { - next_state = MapParserState::LeavePropertyMap; + next_state = MapParserState::LeaveMap; } } } @@ -391,7 +424,7 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt bool_property->execCallbackOnChange(); if(cbor_value_advance(&value_iter) == CborNoError) { - next_state = MapParserState::LeavePropertyMap; + next_state = MapParserState::LeaveMap; } } } @@ -405,18 +438,18 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt /* It is not necessary to advance it we have a string property * because extracting it automatically advances the iterator. */ - next_state = MapParserState::LeavePropertyMap; + next_state = MapParserState::LeaveMap; } } } break; - /* MapParserState::LeavePropertyMap *****************************************/ - case MapParserState::LeavePropertyMap: { + /* MapParserState::LeaveMap *****************************************/ + case MapParserState::LeaveMap: { next_state = MapParserState::Error; if(cbor_value_leave_container(&map_iter, &value_iter) == CborNoError) { if(!cbor_value_at_end(&map_iter)) { - next_state = MapParserState::EnterPropertyMap; + next_state = MapParserState::EnterMap; } else { next_state = MapParserState::Complete; From 9e2a63ed32c3bbb937bae56294a58ccba8b66fba Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Mon, 4 Feb 2019 16:44:38 +0100 Subject: [PATCH 098/175] Adding implementation and test code for decoding a base time map entry --- ArduinoCloudThing.cpp | 49 ++++++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 3c8254888..a5f28d04b 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -248,6 +248,8 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt String base_name, property_name; + double time = 0.0, + base_time = 0.0; CborIntegerMapKey property_value_type; enum class MapParserState { @@ -256,7 +258,7 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt BaseName, BaseTime, Time, - PropertyName, + Name, PropertyType, PropertyValue, LeaveMap, @@ -289,10 +291,10 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt char * val = 0; size_t val_size = 0; if(cbor_value_dup_text_string(&value_iter, &val, &val_size, &value_iter) == CborNoError) { - if (strcmp(val, "n" ) == 0) { next_state = MapParserState::PropertyName; } - else if(strcmp(val, "bn") == 0) { next_state = MapParserState::BaseName; } - else if(strcmp(val, "bt") == 0) { next_state = MapParserState::BaseTime; } - else if(strcmp(val, "t" ) == 0) { next_state = MapParserState::Time; } + if (strcmp(val, "n" ) == 0) { next_state = MapParserState::Name; } + else if(strcmp(val, "bn") == 0) { next_state = MapParserState::BaseName; } + else if(strcmp(val, "bt") == 0) { next_state = MapParserState::BaseTime; } + else if(strcmp(val, "t" ) == 0) { next_state = MapParserState::Time; } free(val); } } @@ -303,10 +305,10 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt int val = 0; if(cbor_value_get_int(&value_iter, &val) == CborNoError) { if(cbor_value_advance(&value_iter) == CborNoError) { - if (val == static_cast(CborIntegerMapKey::Name )) { next_state = MapParserState::PropertyName; } - else if(val == static_cast(CborIntegerMapKey::BaseName)) { next_state = MapParserState::BaseName; } - else if(val == static_cast(CborIntegerMapKey::BaseTime)) { next_state = MapParserState::BaseTime; } - else if(val == static_cast(CborIntegerMapKey::Time )) { next_state = MapParserState::Time; } + if (val == static_cast(CborIntegerMapKey::Name )) { next_state = MapParserState::Name; } + else if(val == static_cast(CborIntegerMapKey::BaseName)) { next_state = MapParserState::BaseName; } + else if(val == static_cast(CborIntegerMapKey::BaseTime)) { next_state = MapParserState::BaseTime; } + else if(val == static_cast(CborIntegerMapKey::Time )) { next_state = MapParserState::Time; } } } } @@ -331,17 +333,36 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt /* MapParserState::BaseTime *****************************************/ case MapParserState::BaseTime: { - /* TODO */ + next_state = MapParserState::Error; + + if(cbor_value_is_double(&value_iter)) { + double val = 0.0; + if(cbor_value_get_double(&value_iter, &val) == CborNoError) { + base_time = val; + if(cbor_value_advance(&value_iter) == CborNoError) { + next_state = MapParserState::MapKey; + } + } + } } break; - /* MapParserState::Time *****************************************/ case MapParserState::Time : { - /* TODO */ + next_state = MapParserState::Error; + + if(cbor_value_is_double(&value_iter)) { + double val = 0.0; + if(cbor_value_get_double(&value_iter, &val) == CborNoError) { + time = val; + if(cbor_value_advance(&value_iter) == CborNoError) { + next_state = MapParserState::MapKey; + } + } + } } break; - /* MapParserState::PropertyName *********************************************/ - case MapParserState::PropertyName: { + /* MapParserState::Name *********************************************/ + case MapParserState::Name: { next_state = MapParserState::Error; if(cbor_value_is_text_string(&value_iter)) { From d570fe48f290a4f91e940babd7aa628383a37250 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Mon, 4 Feb 2019 17:13:18 +0100 Subject: [PATCH 099/175] Refactoring state machine for better readability --- ArduinoCloudThing.cpp | 141 +++++++++++++++++++++--------------------- ArduinoCloudThing.h | 52 +++++++++++----- 2 files changed, 105 insertions(+), 88 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index a5f28d04b..ab8e8d0fc 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -252,84 +252,15 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt base_time = 0.0; CborIntegerMapKey property_value_type; - enum class MapParserState { - EnterMap, - MapKey, - BaseName, - BaseTime, - Time, - Name, - PropertyType, - PropertyValue, - LeaveMap, - Complete, - Error - }; MapParserState current_state = MapParserState::EnterMap, next_state; while(current_state != MapParserState::Complete) { switch(current_state) { - /* MapParserState::EnterMap *****************************************/ - case MapParserState::EnterMap: { - next_state = MapParserState::Error; - - if(cbor_value_get_type(&map_iter) == CborMapType) { - if(cbor_value_enter_container(&map_iter, &value_iter) == CborNoError) { - next_state = MapParserState::MapKey; - } - } - } - break; - /* MapParserState::MapKey ****************************************/ - case MapParserState::MapKey: { - next_state = MapParserState::Error; - - if(_cloud_protocol == CloudProtocol::V1) { - if(cbor_value_is_text_string(&value_iter)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(&value_iter, &val, &val_size, &value_iter) == CborNoError) { - if (strcmp(val, "n" ) == 0) { next_state = MapParserState::Name; } - else if(strcmp(val, "bn") == 0) { next_state = MapParserState::BaseName; } - else if(strcmp(val, "bt") == 0) { next_state = MapParserState::BaseTime; } - else if(strcmp(val, "t" ) == 0) { next_state = MapParserState::Time; } - free(val); - } - } - } - - if(_cloud_protocol == CloudProtocol::V2) { - if(cbor_value_is_integer(&value_iter)) { - int val = 0; - if(cbor_value_get_int(&value_iter, &val) == CborNoError) { - if(cbor_value_advance(&value_iter) == CborNoError) { - if (val == static_cast(CborIntegerMapKey::Name )) { next_state = MapParserState::Name; } - else if(val == static_cast(CborIntegerMapKey::BaseName)) { next_state = MapParserState::BaseName; } - else if(val == static_cast(CborIntegerMapKey::BaseTime)) { next_state = MapParserState::BaseTime; } - else if(val == static_cast(CborIntegerMapKey::Time )) { next_state = MapParserState::Time; } - } - } - } - } - } - break; - /* MapParserState::BaseName *****************************************/ - case MapParserState::BaseName: { - next_state = MapParserState::Error; - - if(cbor_value_is_text_string(&value_iter)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(&value_iter, &val, &val_size, &value_iter) == CborNoError) { - base_name = String(val); - free(val); - next_state = MapParserState::MapKey; - } - } - } - break; + case MapParserState::EnterMap: next_state = handle_EnterMap(&map_iter, &value_iter ); break; + case MapParserState::MapKey : next_state = handle_MapKey (&value_iter ); break; + case MapParserState::BaseName: next_state = handle_BaseName(&value_iter, &base_name); break; /* MapParserState::BaseTime *****************************************/ case MapParserState::BaseTime: { @@ -494,3 +425,69 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt current_state = next_state; } } + +/****************************************************************************** + * PRIVATE MEMBER FUNCTIONS + ******************************************************************************/ + +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_EnterMap(CborValue * map_iter, CborValue * value_iter) { + MapParserState next_state = MapParserState::Error; + + if(cbor_value_get_type(map_iter) == CborMapType) { + if(cbor_value_enter_container(map_iter, value_iter) == CborNoError) { + next_state = MapParserState::MapKey; + } + } + + return next_state; +} + +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * value_iter) { + MapParserState next_state = MapParserState::Error; + + if(_cloud_protocol == CloudProtocol::V1) { + if(cbor_value_is_text_string(value_iter)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { + if (strcmp(val, "n" ) == 0) { next_state = MapParserState::Name; } + else if(strcmp(val, "bn") == 0) { next_state = MapParserState::BaseName; } + else if(strcmp(val, "bt") == 0) { next_state = MapParserState::BaseTime; } + else if(strcmp(val, "t" ) == 0) { next_state = MapParserState::Time; } + free(val); + } + } + } + + if(_cloud_protocol == CloudProtocol::V2) { + if(cbor_value_is_integer(value_iter)) { + int val = 0; + if(cbor_value_get_int(value_iter, &val) == CborNoError) { + if(cbor_value_advance(value_iter) == CborNoError) { + if (val == static_cast(CborIntegerMapKey::Name )) { next_state = MapParserState::Name; } + else if(val == static_cast(CborIntegerMapKey::BaseName)) { next_state = MapParserState::BaseName; } + else if(val == static_cast(CborIntegerMapKey::BaseTime)) { next_state = MapParserState::BaseTime; } + else if(val == static_cast(CborIntegerMapKey::Time )) { next_state = MapParserState::Time; } + } + } + } + } + + return next_state; +} + +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseName(CborValue * value_iter, String * base_name) { + MapParserState next_state = MapParserState::Error; + + if(cbor_value_is_text_string(value_iter)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { + *base_name = String(val); + free(val); + next_state = MapParserState::MapKey; + } + } + + return next_state; +} diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index b16c9b3af..aa0bbeae3 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -28,27 +28,47 @@ static long const DAYS = 86400; ******************************************************************************/ class ArduinoCloudThing { - public: - ArduinoCloudThing(CloudProtocol const cloud_protocol = CloudProtocol::V1); - void begin(); - ArduinoCloudProperty & addPropertyReal(bool & property, String const & name, Permission const permission); - ArduinoCloudProperty & addPropertyReal(int & property, String const & name, Permission const permission); - ArduinoCloudProperty & addPropertyReal(float & property, String const & name, Permission const permission); - ArduinoCloudProperty & addPropertyReal(String & property, String const & name, Permission const permission); +public: + ArduinoCloudThing(CloudProtocol const cloud_protocol = CloudProtocol::V1); - // encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer - int encode(uint8_t * data, size_t const size); - // decode a CBOR payload received from the Cloud. - void decode(uint8_t const * const payload, size_t const length); + void begin(); - private: + ArduinoCloudProperty & addPropertyReal(bool & property, String const & name, Permission const permission); + ArduinoCloudProperty & addPropertyReal(int & property, String const & name, Permission const permission); + ArduinoCloudProperty & addPropertyReal(float & property, String const & name, Permission const permission); + ArduinoCloudProperty & addPropertyReal(String & property, String const & name, Permission const permission); - CloudProtocol const _cloud_protocol; - bool _status = OFF; - char _uuid[33]; - ArduinoCloudPropertyContainer _property_cont; + /* encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer */ + int encode(uint8_t * data, size_t const size); + /* decode a CBOR payload received from the cloud */ + void decode(uint8_t const * const payload, size_t const length); + +private: + + CloudProtocol const _cloud_protocol; + bool _status = OFF; + char _uuid[33]; + ArduinoCloudPropertyContainer _property_cont; + + enum class MapParserState { + EnterMap, + MapKey, + BaseName, + BaseTime, + Time, + Name, + PropertyType, + PropertyValue, + LeaveMap, + Complete, + Error + }; + + MapParserState handle_EnterMap(CborValue * map_iter, CborValue * value_iter); + MapParserState handle_MapKey (CborValue * value_iter); + MapParserState handle_BaseName(CborValue * value_iter, String * base_name); }; #endif /* ARDUINO_CLOUD_THING_H_ */ From 5533454787cfbbbf9938dbee8c707326f9f046b7 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 5 Feb 2019 08:23:40 +0100 Subject: [PATCH 100/175] Extracting all state code into dedicated functions for better readability --- ArduinoCloudThing.cpp | 332 +++++++++++++++++++++--------------------- ArduinoCloudThing.h | 13 +- 2 files changed, 177 insertions(+), 168 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index ab8e8d0fc..e5c6b2a0c 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -250,176 +250,25 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt property_name; double time = 0.0, base_time = 0.0; - CborIntegerMapKey property_value_type; + CborIntegerMapKey property_type; MapParserState current_state = MapParserState::EnterMap, next_state; - while(current_state != MapParserState::Complete) - { - switch(current_state) { - case MapParserState::EnterMap: next_state = handle_EnterMap(&map_iter, &value_iter ); break; - case MapParserState::MapKey : next_state = handle_MapKey (&value_iter ); break; - case MapParserState::BaseName: next_state = handle_BaseName(&value_iter, &base_name); break; - - /* MapParserState::BaseTime *****************************************/ - case MapParserState::BaseTime: { - next_state = MapParserState::Error; - - if(cbor_value_is_double(&value_iter)) { - double val = 0.0; - if(cbor_value_get_double(&value_iter, &val) == CborNoError) { - base_time = val; - if(cbor_value_advance(&value_iter) == CborNoError) { - next_state = MapParserState::MapKey; - } - } - } - } - break; - /* MapParserState::Time *****************************************/ - case MapParserState::Time : { - next_state = MapParserState::Error; - - if(cbor_value_is_double(&value_iter)) { - double val = 0.0; - if(cbor_value_get_double(&value_iter, &val) == CborNoError) { - time = val; - if(cbor_value_advance(&value_iter) == CborNoError) { - next_state = MapParserState::MapKey; - } - } - } - } - break; - /* MapParserState::Name *********************************************/ - case MapParserState::Name: { - next_state = MapParserState::Error; - - if(cbor_value_is_text_string(&value_iter)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(&value_iter, &val, &val_size, &value_iter) == CborNoError) { - property_name = String(val); - free(val); - next_state = MapParserState::PropertyType; - } - } - } - break; - /* MapParserState::PropertyType ***************************************/ - case MapParserState::PropertyType: { - next_state = MapParserState::Error; - - if(_cloud_protocol == CloudProtocol::V1) { - if(cbor_value_is_text_string(&value_iter)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(&value_iter, &val, &val_size, &value_iter) == CborNoError) { - if (strcmp(val, "v" ) == 0) property_value_type = CborIntegerMapKey::Value; - else if(strcmp(val, "vs") == 0) property_value_type = CborIntegerMapKey::StringValue; - else if(strcmp(val, "vb") == 0) property_value_type = CborIntegerMapKey::BooleanValue; - free(val); - next_state = MapParserState::PropertyValue; - } - } - } - - if(_cloud_protocol == CloudProtocol::V2) { - if(cbor_value_is_integer(&value_iter)) { - int val = 0; - if(cbor_value_get_int(&value_iter, &val) == CborNoError) { - if (val == static_cast(CborIntegerMapKey::Value )) property_value_type = CborIntegerMapKey::Value; - else if(val == static_cast(CborIntegerMapKey::StringValue )) property_value_type = CborIntegerMapKey::StringValue; - else if(val == static_cast(CborIntegerMapKey::BooleanValue)) property_value_type = CborIntegerMapKey::BooleanValue; - if(cbor_value_advance(&value_iter) == CborNoError) { - next_state = MapParserState::PropertyValue; - } - } - } - } - } - break; - /* MapParserState::PropertyValue ********************************************/ - case MapParserState::PropertyValue: { - next_state = MapParserState::Error; - - /* VALUE PROPERTY ******************************************************/ - if(property_value_type == CborIntegerMapKey::Value) { - ArduinoCloudProperty * int_property = _property_cont.getPropertyInt (property_name); - ArduinoCloudProperty * float_property = _property_cont.getPropertyFloat(property_name); - - /* INT PROPERTY ******************************************************/ - if(int_property) { - extractProperty(int_property, &value_iter); - int_property->execCallbackOnChange(); - - if(cbor_value_advance(&value_iter) == CborNoError) { - next_state = MapParserState::LeaveMap; - } - } - /* FLOAT PROPERTY ****************************************************/ - if(float_property) { - extractProperty(float_property, &value_iter); - float_property->execCallbackOnChange(); - - if(cbor_value_advance(&value_iter) == CborNoError) { - next_state = MapParserState::LeaveMap; - } - } - } - /* BOOL PROPERTY *******************************************************/ - if(property_value_type == CborIntegerMapKey::BooleanValue) { - ArduinoCloudProperty * bool_property = _property_cont.getPropertyBool(property_name); - if(bool_property) { - extractProperty(bool_property, &value_iter); - bool_property->execCallbackOnChange(); - - if(cbor_value_advance(&value_iter) == CborNoError) { - next_state = MapParserState::LeaveMap; - } - } - } - /* STRING PROPERTY *****************************************************/ - if(property_value_type == CborIntegerMapKey::StringValue) { - ArduinoCloudProperty * string_property = _property_cont.getPropertyString(property_name); - if(string_property) { - extractProperty(string_property, &value_iter); - string_property->execCallbackOnChange(); - - /* It is not necessary to advance it we have a string property - * because extracting it automatically advances the iterator. - */ - next_state = MapParserState::LeaveMap; - } - } - } - break; - /* MapParserState::LeaveMap *****************************************/ - case MapParserState::LeaveMap: { - next_state = MapParserState::Error; - - if(cbor_value_leave_container(&map_iter, &value_iter) == CborNoError) { - if(!cbor_value_at_end(&map_iter)) { - next_state = MapParserState::EnterMap; - } - else { - next_state = MapParserState::Complete; - } + while(current_state != MapParserState::Complete) { - } - } - break; - /* MapParserState::Complete ****************************************************/ - case MapParserState::Complete: { - /* Nothing to do */ - } - break; - /* MapParserState::Error ****************************************************/ - case MapParserState::Error: { - return; - } - break; + switch(current_state) { + case MapParserState::EnterMap : next_state = handle_EnterMap (&map_iter, &value_iter ); break; + case MapParserState::MapKey : next_state = handle_MapKey (&value_iter ); break; + case MapParserState::BaseName : next_state = handle_BaseName (&value_iter, &base_name ); break; + case MapParserState::BaseTime : next_state = handle_BaseTime (&value_iter, &base_time ); break; + case MapParserState::Time : next_state = handle_Time (&value_iter, &time ); break; + case MapParserState::Name : next_state = handle_PropertyName (&value_iter, &property_name ); break; + case MapParserState::PropertyType : next_state = handle_PropertyType (&value_iter, &property_type ); break; + case MapParserState::PropertyValue: next_state = handle_PropertyValue(&value_iter, property_name, property_type); break; + case MapParserState::LeaveMap : next_state = handle_LeaveMap (&map_iter, &value_iter ); break; + case MapParserState::Complete : /* Nothing to do */ break; + case MapParserState::Error : return; break; } current_state = next_state; @@ -491,3 +340,156 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseName(CborValue * return next_state; } + +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseTime(CborValue * value_iter, double * base_time) { + MapParserState next_state = MapParserState::Error; + + if(cbor_value_is_double(value_iter)) { + double val = 0.0; + if(cbor_value_get_double(value_iter, &val) == CborNoError) { + *base_time = val; + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::MapKey; + } + } + } + + return next_state; +} + +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * value_iter, double * time) { + MapParserState next_state = MapParserState::Error; + + if(cbor_value_is_double(value_iter)) { + double val = 0.0; + if(cbor_value_get_double(value_iter, &val) == CborNoError) { + *time = val; + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::MapKey; + } + } + } + + return next_state; +} + +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_PropertyName(CborValue * value_iter, String * property_name) { + MapParserState next_state = MapParserState::Error; + + if(cbor_value_is_text_string(value_iter)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { + *property_name = String(val); + free(val); + next_state = MapParserState::PropertyType; + } + } + + return next_state; +} + +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_PropertyType(CborValue * value_iter, CborIntegerMapKey * property_type) { + MapParserState next_state = MapParserState::Error; + + if(_cloud_protocol == CloudProtocol::V1) { + if(cbor_value_is_text_string(value_iter)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { + if (strcmp(val, "v" ) == 0) *property_type = CborIntegerMapKey::Value; + else if(strcmp(val, "vs") == 0) *property_type = CborIntegerMapKey::StringValue; + else if(strcmp(val, "vb") == 0) *property_type = CborIntegerMapKey::BooleanValue; + free(val); + next_state = MapParserState::PropertyValue; + } + } + } + + if(_cloud_protocol == CloudProtocol::V2) { + if(cbor_value_is_integer(value_iter)) { + int val = 0; + if(cbor_value_get_int(value_iter, &val) == CborNoError) { + if (val == static_cast(CborIntegerMapKey::Value )) *property_type = CborIntegerMapKey::Value; + else if(val == static_cast(CborIntegerMapKey::StringValue )) *property_type = CborIntegerMapKey::StringValue; + else if(val == static_cast(CborIntegerMapKey::BooleanValue)) *property_type = CborIntegerMapKey::BooleanValue; + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::PropertyValue; + } + } + } + } + + return next_state; +} + +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_PropertyValue(CborValue * value_iter, String const & property_name, CborIntegerMapKey const property_type) { + MapParserState next_state = MapParserState::Error; + + /* VALUE PROPERTY ******************************************************/ + if(property_type == CborIntegerMapKey::Value) { + ArduinoCloudProperty * int_property = _property_cont.getPropertyInt (property_name); + ArduinoCloudProperty * float_property = _property_cont.getPropertyFloat(property_name); + + /* INT PROPERTY ******************************************************/ + if(int_property) { + extractProperty(int_property, value_iter); + int_property->execCallbackOnChange(); + + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::LeaveMap; + } + } + /* FLOAT PROPERTY ****************************************************/ + if(float_property) { + extractProperty(float_property, value_iter); + float_property->execCallbackOnChange(); + + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::LeaveMap; + } + } + } + /* BOOL PROPERTY *******************************************************/ + if(property_type == CborIntegerMapKey::BooleanValue) { + ArduinoCloudProperty * bool_property = _property_cont.getPropertyBool(property_name); + if(bool_property) { + extractProperty(bool_property, value_iter); + bool_property->execCallbackOnChange(); + + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::LeaveMap; + } + } + } + /* STRING PROPERTY *****************************************************/ + if(property_type == CborIntegerMapKey::StringValue) { + ArduinoCloudProperty * string_property = _property_cont.getPropertyString(property_name); + if(string_property) { + extractProperty(string_property, value_iter); + string_property->execCallbackOnChange(); + + /* It is not necessary to advance it we have a string property + * because extracting it automatically advances the iterator. + */ + next_state = MapParserState::LeaveMap; + } + } + + return next_state; +} + +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * map_iter, CborValue * value_iter) { + MapParserState next_state = MapParserState::Error; + + if(cbor_value_leave_container(map_iter, value_iter) == CborNoError) { + if(!cbor_value_at_end(map_iter)) { + next_state = MapParserState::EnterMap; + } + else { + next_state = MapParserState::Complete; + } + } + + return next_state; +} diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index aa0bbeae3..5f2c5544c 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -66,9 +66,16 @@ class ArduinoCloudThing { Error }; - MapParserState handle_EnterMap(CborValue * map_iter, CborValue * value_iter); - MapParserState handle_MapKey (CborValue * value_iter); - MapParserState handle_BaseName(CborValue * value_iter, String * base_name); + MapParserState handle_EnterMap (CborValue * map_iter, CborValue * value_iter); + MapParserState handle_MapKey (CborValue * value_iter); + MapParserState handle_BaseName (CborValue * value_iter, String * base_name); + MapParserState handle_BaseTime (CborValue * value_iter, double * base_time); + MapParserState handle_Time (CborValue * value_iter, double * time); + MapParserState handle_PropertyName (CborValue * value_iter, String * property_name); + MapParserState handle_PropertyType (CborValue * value_iter, CborIntegerMapKey * property_type); + MapParserState handle_PropertyValue(CborValue * value_iter, String const & property_name, CborIntegerMapKey const property_type); + MapParserState handle_LeaveMap (CborValue * map_iter, CborValue * value_iter); + }; #endif /* ARDUINO_CLOUD_THING_H_ */ From c6b176e34b089ca16718c98c98d769e126f70adc Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 5 Feb 2019 08:27:31 +0100 Subject: [PATCH 101/175] Rearranging function ordering for better readability --- ArduinoCloudThing.cpp | 194 ++++++++++++++++++++++-------------------- 1 file changed, 104 insertions(+), 90 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index e5c6b2a0c..b893d5596 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -9,19 +9,18 @@ #include /****************************************************************************** - * PRIVATE FREE FUNCTIONS + * PRIVATE FREE FUNCTION PROTOTYPES ******************************************************************************/ -/* Source Idea from https://tools.ietf.org/html/rfc7049 : Page: 50 */ -double convertCborHalfFloatToDouble(uint16_t const half_val) { - int exp = (half_val >> 10) & 0x1f; - int mant = half_val & 0x3ff; - double val; - if (exp == 0) val = ldexp(mant, -24); - else if (exp != 31) val = ldexp(mant + 1024, exp - 25); - else val = mant == 0 ? INFINITY : NAN; - return half_val & 0x8000 ? -val : val; -} +double convertCborHalfFloatToDouble(uint16_t const half_val); +void extractProperty (ArduinoCloudProperty * int_property, CborValue * cbor_value); +void extractProperty (ArduinoCloudProperty * float_property, CborValue * cbor_value); +void extractProperty (ArduinoCloudProperty * bool_property, CborValue * cbor_value); +void extractProperty (ArduinoCloudProperty * string_property, CborValue * cbor_value); + +/****************************************************************************** + * DEBUG FUNCTIONS + ******************************************************************************/ #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) extern "C" char *sbrk(int i); @@ -46,83 +45,6 @@ static void utox8(uint32_t val, char* s) { #define Serial DebugSerial #endif -void extractProperty(ArduinoCloudProperty * int_property, CborValue * cbor_value) -{ - if (cbor_value->type == CborIntegerType) { - int val = 0; - cbor_value_get_int(cbor_value, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(val); - } - } else if (cbor_value->type == CborDoubleType) { - double val = 0.0; - cbor_value_get_double(cbor_value, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(static_cast(val)); - } - } else if (cbor_value->type == CborFloatType) { - float val = 0.0f; - cbor_value_get_float(cbor_value, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(static_cast(val)); - } - } else if (cbor_value->type == CborHalfFloatType) { - uint16_t val = 0; - cbor_value_get_half_float(cbor_value, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); - } - } -} - -void extractProperty(ArduinoCloudProperty * float_property, CborValue * cbor_value) { - if (cbor_value->type == CborDoubleType) { - double val = 0.0; - cbor_value_get_double(cbor_value, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(static_cast(val)); - } - } else if (cbor_value->type == CborIntegerType) { - int val = 0; - cbor_value_get_int(cbor_value, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(static_cast(val)); - } - } else if (cbor_value->type == CborFloatType) { - float val = 0.0f; - cbor_value_get_float(cbor_value, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(val); - } - } else if (cbor_value->type == CborHalfFloatType) { - uint16_t val = 0; - cbor_value_get_half_float(cbor_value, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); - } - } -} - -void extractProperty(ArduinoCloudProperty * bool_property, CborValue * cbor_value) { - bool val = false; - if(cbor_value_get_boolean(cbor_value, &val) == CborNoError) { - if(bool_property->isWriteableByCloud()) { - bool_property->writeByCloud(val); - } - } -} - -void extractProperty(ArduinoCloudProperty * string_property, CborValue * cbor_value) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(cbor_value, &val, &val_size, cbor_value) == CborNoError) { - if(string_property->isWriteableByCloud()) { - string_property->writeByCloud(static_cast(val)); - } - free(val); - } -} - /****************************************************************************** * CTOR/DTOR ******************************************************************************/ @@ -148,8 +70,8 @@ ArduinoCloudThing::ArduinoCloudThing(CloudProtocol const cloud_protocol) ******************************************************************************/ void ArduinoCloudThing::begin() { - _status = ON; - addPropertyReal(_status, "status", Permission::Read); + _status = ON; + addPropertyReal(_status, "status", Permission::Read); } @@ -493,3 +415,95 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * return next_state; } + +/****************************************************************************** + * PRIVATE FREE FUNCTIONS + ******************************************************************************/ + +/* Source Idea from https://tools.ietf.org/html/rfc7049 : Page: 50 */ +double convertCborHalfFloatToDouble(uint16_t const half_val) { + int exp = (half_val >> 10) & 0x1f; + int mant = half_val & 0x3ff; + double val; + if (exp == 0) val = ldexp(mant, -24); + else if (exp != 31) val = ldexp(mant + 1024, exp - 25); + else val = mant == 0 ? INFINITY : NAN; + return half_val & 0x8000 ? -val : val; +} + +void extractProperty(ArduinoCloudProperty * int_property, CborValue * cbor_value) { + if (cbor_value->type == CborIntegerType) { + int val = 0; + cbor_value_get_int(cbor_value, &val); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(val); + } + } else if (cbor_value->type == CborDoubleType) { + double val = 0.0; + cbor_value_get_double(cbor_value, &val); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(static_cast(val)); + } + } else if (cbor_value->type == CborFloatType) { + float val = 0.0f; + cbor_value_get_float(cbor_value, &val); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(static_cast(val)); + } + } else if (cbor_value->type == CborHalfFloatType) { + uint16_t val = 0; + cbor_value_get_half_float(cbor_value, &val); + if(int_property->isWriteableByCloud()) { + int_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); + } + } +} + +void extractProperty(ArduinoCloudProperty * float_property, CborValue * cbor_value) { + if (cbor_value->type == CborDoubleType) { + double val = 0.0; + cbor_value_get_double(cbor_value, &val); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(static_cast(val)); + } + } else if (cbor_value->type == CborIntegerType) { + int val = 0; + cbor_value_get_int(cbor_value, &val); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(static_cast(val)); + } + } else if (cbor_value->type == CborFloatType) { + float val = 0.0f; + cbor_value_get_float(cbor_value, &val); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(val); + } + } else if (cbor_value->type == CborHalfFloatType) { + uint16_t val = 0; + cbor_value_get_half_float(cbor_value, &val); + if(float_property->isWriteableByCloud()) { + float_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); + } + } +} + +void extractProperty(ArduinoCloudProperty * bool_property, CborValue * cbor_value) { + bool val = false; + if(cbor_value_get_boolean(cbor_value, &val) == CborNoError) { + if(bool_property->isWriteableByCloud()) { + bool_property->writeByCloud(val); + } + } +} + +void extractProperty(ArduinoCloudProperty * string_property, CborValue * cbor_value) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(cbor_value, &val, &val_size, cbor_value) == CborNoError) { + if(string_property->isWriteableByCloud()) { + string_property->writeByCloud(static_cast(val)); + } + free(val); + } +} + From 350d61f3e3839ce6c64ec951e5933812f5f49963 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 5 Feb 2019 12:21:07 +0100 Subject: [PATCH 102/175] Rewriting parser in such way that the order of the map entries is no longer important, as long as there is only one CBOR key per map --- ArduinoCloudThing.cpp | 384 +++++++++++++++++------------------------- ArduinoCloudThing.h | 59 ++++++- 2 files changed, 209 insertions(+), 234 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index b893d5596..ed19cdcde 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -8,16 +8,6 @@ #include -/****************************************************************************** - * PRIVATE FREE FUNCTION PROTOTYPES - ******************************************************************************/ - -double convertCborHalfFloatToDouble(uint16_t const half_val); -void extractProperty (ArduinoCloudProperty * int_property, CborValue * cbor_value); -void extractProperty (ArduinoCloudProperty * float_property, CborValue * cbor_value); -void extractProperty (ArduinoCloudProperty * bool_property, CborValue * cbor_value); -void extractProperty (ArduinoCloudProperty * string_property, CborValue * cbor_value); - /****************************************************************************** * DEBUG FUNCTIONS ******************************************************************************/ @@ -77,35 +67,35 @@ void ArduinoCloudThing::begin() { int ArduinoCloudThing::encode(uint8_t * data, size_t const size) { - // check if backing storage and cloud has diverged - // time interval may be elapsed or property may be changed - int const num_changed_properties = _property_cont.getNumOfChangedProperties(); + // check if backing storage and cloud has diverged + // time interval may be elapsed or property may be changed + int const num_changed_properties = _property_cont.getNumOfChangedProperties(); - if (num_changed_properties > 0) { - CborError err; - CborEncoder encoder, arrayEncoder; + if (num_changed_properties > 0) { + CborError err; + CborEncoder encoder, arrayEncoder; - cbor_encoder_init(&encoder, data, size, 0); - // create a cbor array containing the property that should be updated. - err = cbor_encoder_create_array(&encoder, &arrayEncoder, num_changed_properties); - if (err) { - //Serial.println(cbor_error_string(err)); - return -1; - } + cbor_encoder_init(&encoder, data, size, 0); + // create a cbor array containing the property that should be updated. + err = cbor_encoder_create_array(&encoder, &arrayEncoder, num_changed_properties); + if (err) { + //Serial.println(cbor_error_string(err)); + return -1; + } - _property_cont.appendChangedProperties(&arrayEncoder, _cloud_protocol); + _property_cont.appendChangedProperties(&arrayEncoder, _cloud_protocol); - err = cbor_encoder_close_container(&encoder, &arrayEncoder); + err = cbor_encoder_close_container(&encoder, &arrayEncoder); - // return the number of byte of the CBOR encoded array - return cbor_encoder_get_buffer_size(&encoder, data); - } + // return the number of byte of the CBOR encoded array + return cbor_encoder_get_buffer_size(&encoder, data); + } #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) - PrintFreeRam(); + PrintFreeRam(); #endif - // If nothing has to be sent, return diff, that is 0 in this case - return num_changed_properties; + // If nothing has to be sent, return diff, that is 0 in this case + return num_changed_properties; } ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(bool & property, String const & name, Permission const permission) { @@ -168,29 +158,25 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt if(cbor_value_enter_container(&array_iter, &map_iter) != CborNoError) return; - String base_name, - property_name; - double time = 0.0, - base_time = 0.0; - CborIntegerMapKey property_type; - + MapData map_data; MapParserState current_state = MapParserState::EnterMap, next_state; while(current_state != MapParserState::Complete) { switch(current_state) { - case MapParserState::EnterMap : next_state = handle_EnterMap (&map_iter, &value_iter ); break; - case MapParserState::MapKey : next_state = handle_MapKey (&value_iter ); break; - case MapParserState::BaseName : next_state = handle_BaseName (&value_iter, &base_name ); break; - case MapParserState::BaseTime : next_state = handle_BaseTime (&value_iter, &base_time ); break; - case MapParserState::Time : next_state = handle_Time (&value_iter, &time ); break; - case MapParserState::Name : next_state = handle_PropertyName (&value_iter, &property_name ); break; - case MapParserState::PropertyType : next_state = handle_PropertyType (&value_iter, &property_type ); break; - case MapParserState::PropertyValue: next_state = handle_PropertyValue(&value_iter, property_name, property_type); break; - case MapParserState::LeaveMap : next_state = handle_LeaveMap (&map_iter, &value_iter ); break; - case MapParserState::Complete : /* Nothing to do */ break; - case MapParserState::Error : return; break; + case MapParserState::EnterMap : next_state = handle_EnterMap (&map_iter, &value_iter ); break; + case MapParserState::MapKey : next_state = handle_MapKey (&value_iter ); break; + case MapParserState::BaseName : next_state = handle_BaseName (&value_iter, &map_data ); break; + case MapParserState::BaseTime : next_state = handle_BaseTime (&value_iter, &map_data ); break; + case MapParserState::Time : next_state = handle_Time (&value_iter, &map_data ); break; + case MapParserState::Name : next_state = handle_Name (&value_iter, &map_data ); break; + case MapParserState::Value : next_state = handle_Value (&value_iter, &map_data); break; + case MapParserState::StringValue : next_state = handle_StringValue (&value_iter, &map_data ); break; + case MapParserState::BooleanValue : next_state = handle_BooleanValue (&value_iter, &map_data ); break; + case MapParserState::LeaveMap : next_state = handle_LeaveMap (&map_iter, &value_iter, &map_data ); break; + case MapParserState::Complete : /* Nothing to do */ break; + case MapParserState::Error : return; break; } current_state = next_state; @@ -216,29 +202,40 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_EnterMap(CborValue * ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * value_iter) { MapParserState next_state = MapParserState::Error; - if(_cloud_protocol == CloudProtocol::V1) { - if(cbor_value_is_text_string(value_iter)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { - if (strcmp(val, "n" ) == 0) { next_state = MapParserState::Name; } - else if(strcmp(val, "bn") == 0) { next_state = MapParserState::BaseName; } - else if(strcmp(val, "bt") == 0) { next_state = MapParserState::BaseTime; } - else if(strcmp(val, "t" ) == 0) { next_state = MapParserState::Time; } - free(val); + if(cbor_value_at_end(value_iter)) { + next_state = MapParserState::LeaveMap; + } + else { + if(_cloud_protocol == CloudProtocol::V1) { + if(cbor_value_is_text_string(value_iter)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { + if (strcmp(val, "n" ) == 0) next_state = MapParserState::Name; + else if(strcmp(val, "v" ) == 0) next_state = MapParserState::Value; + else if(strcmp(val, "vs") == 0) next_state = MapParserState::StringValue; + else if(strcmp(val, "vb") == 0) next_state = MapParserState::BooleanValue; + else if(strcmp(val, "bn") == 0) next_state = MapParserState::BaseName; + else if(strcmp(val, "bt") == 0) next_state = MapParserState::BaseTime; + else if(strcmp(val, "t" ) == 0) next_state = MapParserState::Time; + free(val); + } } } - } - if(_cloud_protocol == CloudProtocol::V2) { - if(cbor_value_is_integer(value_iter)) { - int val = 0; - if(cbor_value_get_int(value_iter, &val) == CborNoError) { - if(cbor_value_advance(value_iter) == CborNoError) { - if (val == static_cast(CborIntegerMapKey::Name )) { next_state = MapParserState::Name; } - else if(val == static_cast(CborIntegerMapKey::BaseName)) { next_state = MapParserState::BaseName; } - else if(val == static_cast(CborIntegerMapKey::BaseTime)) { next_state = MapParserState::BaseTime; } - else if(val == static_cast(CborIntegerMapKey::Time )) { next_state = MapParserState::Time; } + if(_cloud_protocol == CloudProtocol::V2) { + if(cbor_value_is_integer(value_iter)) { + int val = 0; + if(cbor_value_get_int(value_iter, &val) == CborNoError) { + if(cbor_value_advance(value_iter) == CborNoError) { + if (val == static_cast(CborIntegerMapKey::Name )) next_state = MapParserState::Name; + else if(val == static_cast(CborIntegerMapKey::Value )) next_state = MapParserState::Value; + else if(val == static_cast(CborIntegerMapKey::StringValue )) next_state = MapParserState::StringValue; + else if(val == static_cast(CborIntegerMapKey::BooleanValue)) next_state = MapParserState::BooleanValue; + else if(val == static_cast(CborIntegerMapKey::BaseName )) next_state = MapParserState::BaseName; + else if(val == static_cast(CborIntegerMapKey::BaseTime )) next_state = MapParserState::BaseTime; + else if(val == static_cast(CborIntegerMapKey::Time )) next_state = MapParserState::Time; + } } } } @@ -247,14 +244,14 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * v return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseName(CborValue * value_iter, String * base_name) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseName(CborValue * value_iter, MapData * map_data) { MapParserState next_state = MapParserState::Error; if(cbor_value_is_text_string(value_iter)) { char * val = 0; size_t val_size = 0; if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { - *base_name = String(val); + map_data->base_name.set(String(val)); free(val); next_state = MapParserState::MapKey; } @@ -263,13 +260,13 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseName(CborValue * return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseTime(CborValue * value_iter, double * base_time) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseTime(CborValue * value_iter, MapData * map_data) { MapParserState next_state = MapParserState::Error; if(cbor_value_is_double(value_iter)) { double val = 0.0; if(cbor_value_get_double(value_iter, &val) == CborNoError) { - *base_time = val; + map_data->base_time.set(val); if(cbor_value_advance(value_iter) == CborNoError) { next_state = MapParserState::MapKey; } @@ -279,13 +276,13 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseTime(CborValue * return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * value_iter, double * time) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * value_iter, MapData * map_data) { MapParserState next_state = MapParserState::Error; if(cbor_value_is_double(value_iter)) { double val = 0.0; if(cbor_value_get_double(value_iter, &val) == CborNoError) { - *time = val; + map_data->time.set(val); if(cbor_value_advance(value_iter) == CborNoError) { next_state = MapParserState::MapKey; } @@ -295,114 +292,132 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * val return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_PropertyName(CborValue * value_iter, String * property_name) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Name(CborValue * value_iter, MapData * map_data) { MapParserState next_state = MapParserState::Error; if(cbor_value_is_text_string(value_iter)) { char * val = 0; size_t val_size = 0; if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { - *property_name = String(val); + map_data->name.set(val); free(val); - next_state = MapParserState::PropertyType; + next_state = MapParserState::MapKey; } } return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_PropertyType(CborValue * value_iter, CborIntegerMapKey * property_type) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Value(CborValue * value_iter, MapData * map_data) { MapParserState next_state = MapParserState::Error; - if(_cloud_protocol == CloudProtocol::V1) { - if(cbor_value_is_text_string(value_iter)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { - if (strcmp(val, "v" ) == 0) *property_type = CborIntegerMapKey::Value; - else if(strcmp(val, "vs") == 0) *property_type = CborIntegerMapKey::StringValue; - else if(strcmp(val, "vb") == 0) *property_type = CborIntegerMapKey::BooleanValue; - free(val); - next_state = MapParserState::PropertyValue; - } + if (value_iter->type == CborIntegerType) { + int val = 0; + if(cbor_value_get_int(value_iter, &val) == CborNoError) { + map_data->int_val.set(val); + } + } + else if (value_iter->type == CborDoubleType) { + double val = 0.0; + if(cbor_value_get_double(value_iter, &val) == CborNoError) { + map_data->float_val.set(static_cast(val)); + } + } + else if (value_iter->type == CborFloatType) { + float val = 0.0f; + if(cbor_value_get_float(value_iter, &val) == CborNoError) { + map_data->float_val.set(val); + } + } + else if (value_iter->type == CborHalfFloatType) { + uint16_t val = 0; + if(cbor_value_get_half_float(value_iter, &val) == CborNoError) { + map_data->float_val.set(static_cast(convertCborHalfFloatToDouble(val))); } } - if(_cloud_protocol == CloudProtocol::V2) { - if(cbor_value_is_integer(value_iter)) { - int val = 0; - if(cbor_value_get_int(value_iter, &val) == CborNoError) { - if (val == static_cast(CborIntegerMapKey::Value )) *property_type = CborIntegerMapKey::Value; - else if(val == static_cast(CborIntegerMapKey::StringValue )) *property_type = CborIntegerMapKey::StringValue; - else if(val == static_cast(CborIntegerMapKey::BooleanValue)) *property_type = CborIntegerMapKey::BooleanValue; - if(cbor_value_advance(value_iter) == CborNoError) { - next_state = MapParserState::PropertyValue; - } - } + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::MapKey; + } + + return next_state; +} + +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_StringValue(CborValue * value_iter, MapData * map_data) { + MapParserState next_state = MapParserState::Error; + + if(cbor_value_is_text_string(value_iter)) { + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { + map_data->str_val.set(String(val)); + free(val); + next_state = MapParserState::MapKey; } } return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_PropertyValue(CborValue * value_iter, String const & property_name, CborIntegerMapKey const property_type) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BooleanValue(CborValue * value_iter, MapData * map_data) { MapParserState next_state = MapParserState::Error; - /* VALUE PROPERTY ******************************************************/ - if(property_type == CborIntegerMapKey::Value) { - ArduinoCloudProperty * int_property = _property_cont.getPropertyInt (property_name); - ArduinoCloudProperty * float_property = _property_cont.getPropertyFloat(property_name); + bool val = false; + if(cbor_value_get_boolean(value_iter, &val) == CborNoError) { + map_data->bool_val.set(val); + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::MapKey; + } + } - /* INT PROPERTY ******************************************************/ - if(int_property) { - extractProperty(int_property, value_iter); - int_property->execCallbackOnChange(); + return next_state; +} - if(cbor_value_advance(value_iter) == CborNoError) { - next_state = MapParserState::LeaveMap; +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * map_iter, CborValue * value_iter, MapData const * const map_data) { + MapParserState next_state = MapParserState::Error; + + /* Update the property containers depending on the parsed data */ + + if(map_data->name.isSet()) + { + /* Value (Integer) */ + if(map_data->int_val.isSet()) { + ArduinoCloudProperty* int_property = _property_cont.getPropertyInt(map_data->name.get()); + if(int_property && int_property->isWriteableByCloud()) { + int_property->writeByCloud(map_data->int_val.get()); + int_property->execCallbackOnChange(); } } - /* FLOAT PROPERTY ****************************************************/ - if(float_property) { - extractProperty(float_property, value_iter); - float_property->execCallbackOnChange(); - if(cbor_value_advance(value_iter) == CborNoError) { - next_state = MapParserState::LeaveMap; + /* Value (float) */ + if(map_data->float_val.isSet()) { + ArduinoCloudProperty* float_property = _property_cont.getPropertyFloat(map_data->name.get()); + if(float_property && float_property->isWriteableByCloud()) { + float_property->writeByCloud(map_data->float_val.get()); + float_property->execCallbackOnChange(); } } - } - /* BOOL PROPERTY *******************************************************/ - if(property_type == CborIntegerMapKey::BooleanValue) { - ArduinoCloudProperty * bool_property = _property_cont.getPropertyBool(property_name); - if(bool_property) { - extractProperty(bool_property, value_iter); - bool_property->execCallbackOnChange(); - if(cbor_value_advance(value_iter) == CborNoError) { - next_state = MapParserState::LeaveMap; + /* Value (String) */ + if(map_data->str_val.isSet()) { + ArduinoCloudProperty* string_property = _property_cont.getPropertyString(map_data->name.get()); + if(string_property && string_property->isWriteableByCloud()) { + string_property->writeByCloud(map_data->str_val.get()); + string_property->execCallbackOnChange(); } } - } - /* STRING PROPERTY *****************************************************/ - if(property_type == CborIntegerMapKey::StringValue) { - ArduinoCloudProperty * string_property = _property_cont.getPropertyString(property_name); - if(string_property) { - extractProperty(string_property, value_iter); - string_property->execCallbackOnChange(); - - /* It is not necessary to advance it we have a string property - * because extracting it automatically advances the iterator. - */ - next_state = MapParserState::LeaveMap; + + /* Value (bool) */ + if(map_data->bool_val.isSet()) { + ArduinoCloudProperty* bool_property = _property_cont.getPropertyBool(map_data->name.get()); + if(bool_property && bool_property->isWriteableByCloud()) { + bool_property->writeByCloud(map_data->bool_val.get()); + bool_property->execCallbackOnChange(); + } } } - return next_state; -} - -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * map_iter, CborValue * value_iter) { - MapParserState next_state = MapParserState::Error; + /* Transition into the next map if available, otherwise finish */ if(cbor_value_leave_container(map_iter, value_iter) == CborNoError) { if(!cbor_value_at_end(map_iter)) { @@ -416,12 +431,8 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * return next_state; } -/****************************************************************************** - * PRIVATE FREE FUNCTIONS - ******************************************************************************/ - /* Source Idea from https://tools.ietf.org/html/rfc7049 : Page: 50 */ -double convertCborHalfFloatToDouble(uint16_t const half_val) { +double ArduinoCloudThing::convertCborHalfFloatToDouble(uint16_t const half_val) { int exp = (half_val >> 10) & 0x1f; int mant = half_val & 0x3ff; double val; @@ -430,80 +441,3 @@ double convertCborHalfFloatToDouble(uint16_t const half_val) { else val = mant == 0 ? INFINITY : NAN; return half_val & 0x8000 ? -val : val; } - -void extractProperty(ArduinoCloudProperty * int_property, CborValue * cbor_value) { - if (cbor_value->type == CborIntegerType) { - int val = 0; - cbor_value_get_int(cbor_value, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(val); - } - } else if (cbor_value->type == CborDoubleType) { - double val = 0.0; - cbor_value_get_double(cbor_value, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(static_cast(val)); - } - } else if (cbor_value->type == CborFloatType) { - float val = 0.0f; - cbor_value_get_float(cbor_value, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(static_cast(val)); - } - } else if (cbor_value->type == CborHalfFloatType) { - uint16_t val = 0; - cbor_value_get_half_float(cbor_value, &val); - if(int_property->isWriteableByCloud()) { - int_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); - } - } -} - -void extractProperty(ArduinoCloudProperty * float_property, CborValue * cbor_value) { - if (cbor_value->type == CborDoubleType) { - double val = 0.0; - cbor_value_get_double(cbor_value, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(static_cast(val)); - } - } else if (cbor_value->type == CborIntegerType) { - int val = 0; - cbor_value_get_int(cbor_value, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(static_cast(val)); - } - } else if (cbor_value->type == CborFloatType) { - float val = 0.0f; - cbor_value_get_float(cbor_value, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(val); - } - } else if (cbor_value->type == CborHalfFloatType) { - uint16_t val = 0; - cbor_value_get_half_float(cbor_value, &val); - if(float_property->isWriteableByCloud()) { - float_property->writeByCloud(static_cast(convertCborHalfFloatToDouble(val))); - } - } -} - -void extractProperty(ArduinoCloudProperty * bool_property, CborValue * cbor_value) { - bool val = false; - if(cbor_value_get_boolean(cbor_value, &val) == CborNoError) { - if(bool_property->isWriteableByCloud()) { - bool_property->writeByCloud(val); - } - } -} - -void extractProperty(ArduinoCloudProperty * string_property, CborValue * cbor_value) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(cbor_value, &val, &val_size, cbor_value) == CborNoError) { - if(string_property->isWriteableByCloud()) { - string_property->writeByCloud(static_cast(val)); - } - free(val); - } -} - diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 5f2c5544c..d3bc985f2 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -59,22 +59,63 @@ class ArduinoCloudThing { BaseTime, Time, Name, - PropertyType, - PropertyValue, + Value, + StringValue, + BooleanValue, LeaveMap, Complete, Error }; + template + class MapEntry { + public: + + MapEntry() : _is_set(false) { } + + void set(T const & entry) { + _entry = entry; + _is_set = true; + } + + bool isSet() const { + return _is_set; + } + + T const get() const { + return _entry; + } + + private: + + T _entry; + bool _is_set; + + }; + + typedef struct { + MapEntry base_name; + MapEntry base_time; + MapEntry name; + MapEntry int_val; + MapEntry float_val; + MapEntry str_val; + MapEntry bool_val; + MapEntry time; + } MapData; + MapParserState handle_EnterMap (CborValue * map_iter, CborValue * value_iter); MapParserState handle_MapKey (CborValue * value_iter); - MapParserState handle_BaseName (CborValue * value_iter, String * base_name); - MapParserState handle_BaseTime (CborValue * value_iter, double * base_time); - MapParserState handle_Time (CborValue * value_iter, double * time); - MapParserState handle_PropertyName (CborValue * value_iter, String * property_name); - MapParserState handle_PropertyType (CborValue * value_iter, CborIntegerMapKey * property_type); - MapParserState handle_PropertyValue(CborValue * value_iter, String const & property_name, CborIntegerMapKey const property_type); - MapParserState handle_LeaveMap (CborValue * map_iter, CborValue * value_iter); + MapParserState handle_BaseName (CborValue * value_iter, MapData * map_data); + MapParserState handle_BaseTime (CborValue * value_iter, MapData * map_data); + MapParserState handle_Time (CborValue * value_iter, MapData * map_data); + MapParserState handle_Name (CborValue * value_iter, MapData * map_data); + MapParserState handle_Value (CborValue * value_iter, MapData * map_data); + MapParserState handle_StringValue (CborValue * value_iter, MapData * map_data); + MapParserState handle_BooleanValue (CborValue * value_iter, MapData * map_data); + MapParserState handle_LeaveMap (CborValue * map_iter, CborValue * value_iter, MapData const * const map_data); + + static double convertCborHalfFloatToDouble(uint16_t const half_val); }; From ff214f8fe3f5e0e4903d83e85212ada84074efde Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 5 Feb 2019 12:33:13 +0100 Subject: [PATCH 103/175] In case of a unkown map key we now just advance to the next map key instead of cancelling the parse process --- ArduinoCloudThing.cpp | 100 +++++++++++++++++++++++++++--------------- ArduinoCloudThing.h | 9 +++- 2 files changed, 71 insertions(+), 38 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index ed19cdcde..78e7d94c6 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -165,18 +165,20 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt while(current_state != MapParserState::Complete) { switch(current_state) { - case MapParserState::EnterMap : next_state = handle_EnterMap (&map_iter, &value_iter ); break; - case MapParserState::MapKey : next_state = handle_MapKey (&value_iter ); break; - case MapParserState::BaseName : next_state = handle_BaseName (&value_iter, &map_data ); break; - case MapParserState::BaseTime : next_state = handle_BaseTime (&value_iter, &map_data ); break; - case MapParserState::Time : next_state = handle_Time (&value_iter, &map_data ); break; - case MapParserState::Name : next_state = handle_Name (&value_iter, &map_data ); break; - case MapParserState::Value : next_state = handle_Value (&value_iter, &map_data); break; - case MapParserState::StringValue : next_state = handle_StringValue (&value_iter, &map_data ); break; + case MapParserState::EnterMap : next_state = handle_EnterMap (&map_iter, &value_iter ); break; + case MapParserState::MapKey : next_state = handle_MapKey (&value_iter ); break; + case MapParserState::UndefinedKey : next_state = handle_UndefinedKey (&value_iter ); break; + case MapParserState::BaseVersion : next_state = handle_BaseVersion (&value_iter, &map_data ); break; + case MapParserState::BaseName : next_state = handle_BaseName (&value_iter, &map_data ); break; + case MapParserState::BaseTime : next_state = handle_BaseTime (&value_iter, &map_data ); break; + case MapParserState::Time : next_state = handle_Time (&value_iter, &map_data ); break; + case MapParserState::Name : next_state = handle_Name (&value_iter, &map_data ); break; + case MapParserState::Value : next_state = handle_Value (&value_iter, &map_data ); break; + case MapParserState::StringValue : next_state = handle_StringValue (&value_iter, &map_data ); break; case MapParserState::BooleanValue : next_state = handle_BooleanValue (&value_iter, &map_data ); break; - case MapParserState::LeaveMap : next_state = handle_LeaveMap (&map_iter, &value_iter, &map_data ); break; - case MapParserState::Complete : /* Nothing to do */ break; - case MapParserState::Error : return; break; + case MapParserState::LeaveMap : next_state = handle_LeaveMap (&map_iter, &value_iter, &map_data); break; + case MapParserState::Complete : /* Nothing to do */ break; + case MapParserState::Error : return; break; } current_state = next_state; @@ -211,13 +213,15 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * v char * val = 0; size_t val_size = 0; if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { - if (strcmp(val, "n" ) == 0) next_state = MapParserState::Name; - else if(strcmp(val, "v" ) == 0) next_state = MapParserState::Value; - else if(strcmp(val, "vs") == 0) next_state = MapParserState::StringValue; - else if(strcmp(val, "vb") == 0) next_state = MapParserState::BooleanValue; - else if(strcmp(val, "bn") == 0) next_state = MapParserState::BaseName; - else if(strcmp(val, "bt") == 0) next_state = MapParserState::BaseTime; - else if(strcmp(val, "t" ) == 0) next_state = MapParserState::Time; + if (strcmp(val, "n" ) == 0) next_state = MapParserState::Name; + else if(strcmp(val, "bver") == 0) next_state = MapParserState::BaseVersion; + else if(strcmp(val, "bn" ) == 0) next_state = MapParserState::BaseName; + else if(strcmp(val, "bt" ) == 0) next_state = MapParserState::BaseTime; + else if(strcmp(val, "v" ) == 0) next_state = MapParserState::Value; + else if(strcmp(val, "vs" ) == 0) next_state = MapParserState::StringValue; + else if(strcmp(val, "vb" ) == 0) next_state = MapParserState::BooleanValue; + else if(strcmp(val, "t" ) == 0) next_state = MapParserState::Time; + else next_state = MapParserState::UndefinedKey; free(val); } } @@ -229,12 +233,14 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * v if(cbor_value_get_int(value_iter, &val) == CborNoError) { if(cbor_value_advance(value_iter) == CborNoError) { if (val == static_cast(CborIntegerMapKey::Name )) next_state = MapParserState::Name; + else if(val == static_cast(CborIntegerMapKey::BaseVersion )) next_state = MapParserState::BaseVersion; + else if(val == static_cast(CborIntegerMapKey::BaseName )) next_state = MapParserState::BaseName; + else if(val == static_cast(CborIntegerMapKey::BaseTime )) next_state = MapParserState::BaseTime; else if(val == static_cast(CborIntegerMapKey::Value )) next_state = MapParserState::Value; else if(val == static_cast(CborIntegerMapKey::StringValue )) next_state = MapParserState::StringValue; else if(val == static_cast(CborIntegerMapKey::BooleanValue)) next_state = MapParserState::BooleanValue; - else if(val == static_cast(CborIntegerMapKey::BaseName )) next_state = MapParserState::BaseName; - else if(val == static_cast(CborIntegerMapKey::BaseTime )) next_state = MapParserState::BaseTime; else if(val == static_cast(CborIntegerMapKey::Time )) next_state = MapParserState::Time; + else next_state = MapParserState::UndefinedKey; } } } @@ -244,6 +250,28 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * v return next_state; } +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_UndefinedKey(CborValue * value_iter) { + MapParserState next_state = MapParserState::Error; + + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::MapKey; + } + + return next_state; +} + +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseVersion(CborValue * value_iter, MapData * map_data) { + MapParserState next_state = MapParserState::Error; + + /* TODO: Parse BaseVersion */ + + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::MapKey; + } + + return next_state; +} + ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseName(CborValue * value_iter, MapData * map_data) { MapParserState next_state = MapParserState::Error; @@ -276,22 +304,6 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseTime(CborValue * return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * value_iter, MapData * map_data) { - MapParserState next_state = MapParserState::Error; - - if(cbor_value_is_double(value_iter)) { - double val = 0.0; - if(cbor_value_get_double(value_iter, &val) == CborNoError) { - map_data->time.set(val); - if(cbor_value_advance(value_iter) == CborNoError) { - next_state = MapParserState::MapKey; - } - } - } - - return next_state; -} - ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Name(CborValue * value_iter, MapData * map_data) { MapParserState next_state = MapParserState::Error; @@ -373,6 +385,22 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BooleanValue(CborVal return next_state; } +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * value_iter, MapData * map_data) { + MapParserState next_state = MapParserState::Error; + + if(cbor_value_is_double(value_iter)) { + double val = 0.0; + if(cbor_value_get_double(value_iter, &val) == CborNoError) { + map_data->time.set(val); + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::MapKey; + } + } + } + + return next_state; +} + ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * map_iter, CborValue * value_iter, MapData const * const map_data) { MapParserState next_state = MapParserState::Error; diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index d3bc985f2..a3ba3bdaa 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -55,13 +55,15 @@ class ArduinoCloudThing { enum class MapParserState { EnterMap, MapKey, + UndefinedKey, + BaseVersion, BaseName, BaseTime, - Time, Name, Value, StringValue, BooleanValue, + Time, LeaveMap, Complete, Error @@ -94,6 +96,7 @@ class ArduinoCloudThing { }; typedef struct { + MapEntry base_version; MapEntry base_name; MapEntry base_time; MapEntry name; @@ -106,13 +109,15 @@ class ArduinoCloudThing { MapParserState handle_EnterMap (CborValue * map_iter, CborValue * value_iter); MapParserState handle_MapKey (CborValue * value_iter); + MapParserState handle_UndefinedKey (CborValue * value_iter); + MapParserState handle_BaseVersion (CborValue * value_iter, MapData * map_data); MapParserState handle_BaseName (CborValue * value_iter, MapData * map_data); MapParserState handle_BaseTime (CborValue * value_iter, MapData * map_data); - MapParserState handle_Time (CborValue * value_iter, MapData * map_data); MapParserState handle_Name (CborValue * value_iter, MapData * map_data); MapParserState handle_Value (CborValue * value_iter, MapData * map_data); MapParserState handle_StringValue (CborValue * value_iter, MapData * map_data); MapParserState handle_BooleanValue (CborValue * value_iter, MapData * map_data); + MapParserState handle_Time (CborValue * value_iter, MapData * map_data); MapParserState handle_LeaveMap (CborValue * map_iter, CborValue * value_iter, MapData const * const map_data); static double convertCborHalfFloatToDouble(uint16_t const half_val); From 41fb58c9fb6185a7aef3500cdee1ba1fa40aee4c Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 5 Feb 2019 12:38:51 +0100 Subject: [PATCH 104/175] Implementing parsing for base version --- ArduinoCloudThing.cpp | 20 ++++++++++++-------- ArduinoCloudThing.h | 15 +++------------ 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 78e7d94c6..a1f9ff479 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -263,10 +263,14 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_UndefinedKey(CborVal ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseVersion(CborValue * value_iter, MapData * map_data) { MapParserState next_state = MapParserState::Error; - /* TODO: Parse BaseVersion */ - - if(cbor_value_advance(value_iter) == CborNoError) { - next_state = MapParserState::MapKey; + if(cbor_value_is_integer(value_iter)) { + int val = 0; + if(cbor_value_get_int(value_iter, &val) == CborNoError) { + map_data->base_version.set(val); + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::MapKey; + } + } } return next_state; @@ -323,25 +327,25 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Name(CborValue * val ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Value(CborValue * value_iter, MapData * map_data) { MapParserState next_state = MapParserState::Error; - if (value_iter->type == CborIntegerType) { + if(value_iter->type == CborIntegerType) { int val = 0; if(cbor_value_get_int(value_iter, &val) == CborNoError) { map_data->int_val.set(val); } } - else if (value_iter->type == CborDoubleType) { + else if(value_iter->type == CborDoubleType) { double val = 0.0; if(cbor_value_get_double(value_iter, &val) == CborNoError) { map_data->float_val.set(static_cast(val)); } } - else if (value_iter->type == CborFloatType) { + else if(value_iter->type == CborFloatType) { float val = 0.0f; if(cbor_value_get_float(value_iter, &val) == CborNoError) { map_data->float_val.set(val); } } - else if (value_iter->type == CborHalfFloatType) { + else if(value_iter->type == CborHalfFloatType) { uint16_t val = 0; if(cbor_value_get_half_float(value_iter, &val) == CborNoError) { map_data->float_val.set(static_cast(convertCborHalfFloatToDouble(val))); diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index a3ba3bdaa..8178eb794 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -75,18 +75,9 @@ class ArduinoCloudThing { MapEntry() : _is_set(false) { } - void set(T const & entry) { - _entry = entry; - _is_set = true; - } - - bool isSet() const { - return _is_set; - } - - T const get() const { - return _entry; - } + inline void set (T const & entry) { _entry = entry; _is_set = true; } + inline bool isSet() const { return _is_set; } + inline T const get () const { return _entry; } private: From 3374ec1c911eac593ffee4fae47d3464cf152f1f Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 5 Feb 2019 12:45:42 +0100 Subject: [PATCH 105/175] Making sure the map data is reset when entering a new map --- ArduinoCloudThing.cpp | 37 +++++++++++++++++++++++++------------ ArduinoCloudThing.h | 7 +++++-- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index a1f9ff479..4adbb4739 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -165,17 +165,17 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt while(current_state != MapParserState::Complete) { switch(current_state) { - case MapParserState::EnterMap : next_state = handle_EnterMap (&map_iter, &value_iter ); break; - case MapParserState::MapKey : next_state = handle_MapKey (&value_iter ); break; - case MapParserState::UndefinedKey : next_state = handle_UndefinedKey (&value_iter ); break; - case MapParserState::BaseVersion : next_state = handle_BaseVersion (&value_iter, &map_data ); break; - case MapParserState::BaseName : next_state = handle_BaseName (&value_iter, &map_data ); break; - case MapParserState::BaseTime : next_state = handle_BaseTime (&value_iter, &map_data ); break; - case MapParserState::Time : next_state = handle_Time (&value_iter, &map_data ); break; - case MapParserState::Name : next_state = handle_Name (&value_iter, &map_data ); break; - case MapParserState::Value : next_state = handle_Value (&value_iter, &map_data ); break; - case MapParserState::StringValue : next_state = handle_StringValue (&value_iter, &map_data ); break; - case MapParserState::BooleanValue : next_state = handle_BooleanValue (&value_iter, &map_data ); break; + case MapParserState::EnterMap : next_state = handle_EnterMap (&map_iter, &value_iter, &map_data); break; + case MapParserState::MapKey : next_state = handle_MapKey (&value_iter ); break; + case MapParserState::UndefinedKey : next_state = handle_UndefinedKey (&value_iter ); break; + case MapParserState::BaseVersion : next_state = handle_BaseVersion (&value_iter, &map_data ); break; + case MapParserState::BaseName : next_state = handle_BaseName (&value_iter, &map_data ); break; + case MapParserState::BaseTime : next_state = handle_BaseTime (&value_iter, &map_data ); break; + case MapParserState::Time : next_state = handle_Time (&value_iter, &map_data ); break; + case MapParserState::Name : next_state = handle_Name (&value_iter, &map_data ); break; + case MapParserState::Value : next_state = handle_Value (&value_iter, &map_data ); break; + case MapParserState::StringValue : next_state = handle_StringValue (&value_iter, &map_data ); break; + case MapParserState::BooleanValue : next_state = handle_BooleanValue (&value_iter, &map_data ); break; case MapParserState::LeaveMap : next_state = handle_LeaveMap (&map_iter, &value_iter, &map_data); break; case MapParserState::Complete : /* Nothing to do */ break; case MapParserState::Error : return; break; @@ -189,11 +189,12 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt * PRIVATE MEMBER FUNCTIONS ******************************************************************************/ -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_EnterMap(CborValue * map_iter, CborValue * value_iter) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_EnterMap(CborValue * map_iter, CborValue * value_iter, MapData * map_data) { MapParserState next_state = MapParserState::Error; if(cbor_value_get_type(map_iter) == CborMapType) { if(cbor_value_enter_container(map_iter, value_iter) == CborNoError) { + resetMapData(map_data); next_state = MapParserState::MapKey; } } @@ -463,6 +464,18 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * return next_state; } +void ArduinoCloudThing::resetMapData(MapData * map_data) { + map_data->base_version.reset(); + map_data->base_name.reset (); + map_data->base_time.reset (); + map_data->name.reset (); + map_data->int_val.reset (); + map_data->float_val.reset (); + map_data->str_val.reset (); + map_data->bool_val.reset (); + map_data->time.reset (); +} + /* Source Idea from https://tools.ietf.org/html/rfc7049 : Page: 50 */ double ArduinoCloudThing::convertCborHalfFloatToDouble(uint16_t const half_val) { int exp = (half_val >> 10) & 0x1f; diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 8178eb794..78eb9660f 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -76,8 +76,10 @@ class ArduinoCloudThing { MapEntry() : _is_set(false) { } inline void set (T const & entry) { _entry = entry; _is_set = true; } + inline T const get () const { return _entry; } + inline bool isSet() const { return _is_set; } - inline T const get () const { return _entry; } + inline bool reset() { _is_set = false; } private: @@ -98,7 +100,7 @@ class ArduinoCloudThing { MapEntry time; } MapData; - MapParserState handle_EnterMap (CborValue * map_iter, CborValue * value_iter); + MapParserState handle_EnterMap (CborValue * map_iter, CborValue * value_iter, MapData * map_data); MapParserState handle_MapKey (CborValue * value_iter); MapParserState handle_UndefinedKey (CborValue * value_iter); MapParserState handle_BaseVersion (CborValue * value_iter, MapData * map_data); @@ -111,6 +113,7 @@ class ArduinoCloudThing { MapParserState handle_Time (CborValue * value_iter, MapData * map_data); MapParserState handle_LeaveMap (CborValue * map_iter, CborValue * value_iter, MapData const * const map_data); + static void resetMapData (MapData * map_data); static double convertCborHalfFloatToDouble(uint16_t const half_val); }; From ef597e1045f4286223743d6b15522b7b309a669e Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 5 Feb 2019 15:00:50 +0100 Subject: [PATCH 106/175] Bugfix: Parsing BaseTime regardless if its a double or an int --- ArduinoCloudThing.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 4adbb4739..22d477003 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -300,12 +300,20 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseTime(CborValue * double val = 0.0; if(cbor_value_get_double(value_iter, &val) == CborNoError) { map_data->base_time.set(val); - if(cbor_value_advance(value_iter) == CborNoError) { - next_state = MapParserState::MapKey; - } } } + if(cbor_value_is_integer(value_iter)) { + int val = 0; + if(cbor_value_get_int(value_iter, &val) == CborNoError) { + map_data->base_time.set(static_cast(val)); + } + } + + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::MapKey; + } + return next_state; } From 20c1f435bf9dc178dd2d45049c9112c887370625 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 5 Feb 2019 15:07:20 +0100 Subject: [PATCH 107/175] Bugfix - taking into account that the CBOR library might collapse any datatype into a smaller version --- ArduinoCloudThing.cpp | 45 ++++++++++++++++++++++++++----------------- ArduinoCloudThing.h | 3 +-- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 22d477003..3e55aad34 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -310,6 +310,8 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseTime(CborValue * } } + /* TODO: Check for half float / float */ + if(cbor_value_advance(value_iter) == CborNoError) { next_state = MapParserState::MapKey; } @@ -339,25 +341,25 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Value(CborValue * va if(value_iter->type == CborIntegerType) { int val = 0; if(cbor_value_get_int(value_iter, &val) == CborNoError) { - map_data->int_val.set(val); + map_data->val.set(static_cast(val)); } } else if(value_iter->type == CborDoubleType) { double val = 0.0; if(cbor_value_get_double(value_iter, &val) == CborNoError) { - map_data->float_val.set(static_cast(val)); + map_data->val.set(static_cast(val)); } } else if(value_iter->type == CborFloatType) { float val = 0.0f; if(cbor_value_get_float(value_iter, &val) == CborNoError) { - map_data->float_val.set(val); + map_data->val.set(val); } } else if(value_iter->type == CborHalfFloatType) { uint16_t val = 0; if(cbor_value_get_half_float(value_iter, &val) == CborNoError) { - map_data->float_val.set(static_cast(convertCborHalfFloatToDouble(val))); + map_data->val.set(static_cast(convertCborHalfFloatToDouble(val))); } } @@ -405,12 +407,22 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * val double val = 0.0; if(cbor_value_get_double(value_iter, &val) == CborNoError) { map_data->time.set(val); - if(cbor_value_advance(value_iter) == CborNoError) { - next_state = MapParserState::MapKey; - } } } + if(cbor_value_is_integer(value_iter)) { + int val = 0; + if(cbor_value_get_int(value_iter, &val) == CborNoError) { + map_data->time.set(static_cast(val)); + } + } + + /* TODO: Check for half float / float */ + + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::MapKey; + } + return next_state; } @@ -421,20 +433,18 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * if(map_data->name.isSet()) { - /* Value (Integer) */ - if(map_data->int_val.isSet()) { - ArduinoCloudProperty* int_property = _property_cont.getPropertyInt(map_data->name.get()); + /* Value (Integer/Float/Double/Half-Float) */ + if(map_data->val.isSet()) { + ArduinoCloudProperty * int_property = _property_cont.getPropertyInt (map_data->name.get()); + ArduinoCloudProperty * float_property = _property_cont.getPropertyFloat(map_data->name.get()); + if(int_property && int_property->isWriteableByCloud()) { - int_property->writeByCloud(map_data->int_val.get()); + int_property->writeByCloud(static_cast(map_data->val.get())); /* Val is internally stored as float */ int_property->execCallbackOnChange(); } - } - /* Value (float) */ - if(map_data->float_val.isSet()) { - ArduinoCloudProperty* float_property = _property_cont.getPropertyFloat(map_data->name.get()); if(float_property && float_property->isWriteableByCloud()) { - float_property->writeByCloud(map_data->float_val.get()); + float_property->writeByCloud(map_data->val.get()); float_property->execCallbackOnChange(); } } @@ -477,8 +487,7 @@ void ArduinoCloudThing::resetMapData(MapData * map_data) { map_data->base_name.reset (); map_data->base_time.reset (); map_data->name.reset (); - map_data->int_val.reset (); - map_data->float_val.reset (); + map_data->val.reset (); map_data->str_val.reset (); map_data->bool_val.reset (); map_data->time.reset (); diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 78eb9660f..996fdef0f 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -93,8 +93,7 @@ class ArduinoCloudThing { MapEntry base_name; MapEntry base_time; MapEntry name; - MapEntry int_val; - MapEntry float_val; + MapEntry val; MapEntry str_val; MapEntry bool_val; MapEntry time; From ee503e490c967ba68a22690b2ca34cd85128e425 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 5 Feb 2019 15:21:25 +0100 Subject: [PATCH 108/175] Bugfix - All numeric data types are excepted for BaseTime and Time --- ArduinoCloudThing.cpp | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 3e55aad34..0fc2a101c 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -296,6 +296,13 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseName(CborValue * ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseTime(CborValue * value_iter, MapData * map_data) { MapParserState next_state = MapParserState::Error; + if(cbor_value_is_integer(value_iter)) { + int val = 0; + if(cbor_value_get_int(value_iter, &val) == CborNoError) { + map_data->base_time.set(static_cast(val)); + } + } + if(cbor_value_is_double(value_iter)) { double val = 0.0; if(cbor_value_get_double(value_iter, &val) == CborNoError) { @@ -303,14 +310,19 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseTime(CborValue * } } - if(cbor_value_is_integer(value_iter)) { - int val = 0; - if(cbor_value_get_int(value_iter, &val) == CborNoError) { + if(cbor_value_is_float(value_iter)) { + float val = 0.0; + if(cbor_value_get_float(value_iter, &val) == CborNoError) { map_data->base_time.set(static_cast(val)); } } - /* TODO: Check for half float / float */ + if(cbor_value_is_half_float(value_iter)) { + uint16_t val = 0; + if(cbor_value_get_half_float(value_iter, &val) == CborNoError) { + map_data->base_time.set(static_cast(convertCborHalfFloatToDouble(val))); + } + } if(cbor_value_advance(value_iter) == CborNoError) { next_state = MapParserState::MapKey; @@ -403,6 +415,13 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BooleanValue(CborVal ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * value_iter, MapData * map_data) { MapParserState next_state = MapParserState::Error; + if(cbor_value_is_integer(value_iter)) { + int val = 0; + if(cbor_value_get_int(value_iter, &val) == CborNoError) { + map_data->time.set(static_cast(val)); + } + } + if(cbor_value_is_double(value_iter)) { double val = 0.0; if(cbor_value_get_double(value_iter, &val) == CborNoError) { @@ -410,14 +429,19 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * val } } - if(cbor_value_is_integer(value_iter)) { - int val = 0; - if(cbor_value_get_int(value_iter, &val) == CborNoError) { + if(cbor_value_is_float(value_iter)) { + float val = 0.0; + if(cbor_value_get_float(value_iter, &val) == CborNoError) { map_data->time.set(static_cast(val)); } } - /* TODO: Check for half float / float */ + if(cbor_value_is_half_float(value_iter)) { + uint16_t val = 0; + if(cbor_value_get_half_float(value_iter, &val) == CborNoError) { + map_data->time.set(static_cast(convertCborHalfFloatToDouble(val))); + } + } if(cbor_value_advance(value_iter) == CborNoError) { next_state = MapParserState::MapKey; From dcbd179cd530d9eb5e70b5cf9b67f55485f3ee22 Mon Sep 17 00:00:00 2001 From: lxrobotics Date: Tue, 5 Feb 2019 18:53:25 +0100 Subject: [PATCH 109/175] Bugfix + test code - if a property is manipulated in its onChange callback function to its origin state the change is still propagated to the cloud --- ArduinoCloudProperty.hpp | 5 +++-- ArduinoCloudProperty.ipp | 12 +++++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index ab233a2e3..8c8ac5f7b 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -77,7 +77,7 @@ class ArduinoCloudProperty { inline bool isReadableByCloud () const { return (_permission == Permission::Read ) || (_permission == Permission::ReadWrite); } inline bool isWriteableByCloud() const { return (_permission == Permission::Write) || (_permission == Permission::ReadWrite); } - bool shouldBeUpdated () const; + bool shouldBeUpdated (); void execCallbackOnChange (); void append (CborEncoder * encoder, CloudProtocol const cloud_protocol); @@ -91,7 +91,8 @@ class ArduinoCloudProperty { UpdateCallbackFunc _update_callback_func; UpdatePolicy _update_policy; - bool _has_been_updated_once; + bool _has_been_updated_once, + _has_been_modified_in_callback; /* Variables used for UpdatePolicy::OnChange */ T _min_delta_property; unsigned long _min_time_between_updates_millis; diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 07e8c6d68..9fb333bca 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -11,6 +11,7 @@ ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, _update_callback_func(NULL), _update_policy(UpdatePolicy::OnChange), _has_been_updated_once(false), + _has_been_modified_in_callback(false), _min_delta_property(getInitialMinDeltaPropertyValue()), _min_time_between_updates_millis(0), _last_updated_millis(0), @@ -52,9 +53,14 @@ ArduinoCloudProperty & ArduinoCloudProperty::publishEvery(unsigned long co } template -bool ArduinoCloudProperty::shouldBeUpdated() const { +bool ArduinoCloudProperty::shouldBeUpdated() { if(!_has_been_updated_once) return true; + if(_has_been_modified_in_callback) { + _has_been_modified_in_callback = false; + return true; + } + if (_update_policy == UpdatePolicy::OnChange) { return (isValueDifferent(_property, _shadow_property) && ((millis() - _last_updated_millis) >= (_min_time_between_updates_millis))); } @@ -72,6 +78,10 @@ void ArduinoCloudProperty::execCallbackOnChange() { if(_update_callback_func != NULL) { _update_callback_func(); } + + if(!isValueDifferent(_property, _shadow_property)) { + _has_been_modified_in_callback = true; + } } } From f11f9a9103b4a839a0d4210ef8738f073cc7eec4 Mon Sep 17 00:00:00 2001 From: Cristian Maglie Date: Wed, 6 Feb 2019 11:44:31 +0100 Subject: [PATCH 110/175] Added license --- ArduinoCloudProperty.hpp | 17 +++++++++++++++++ ArduinoCloudProperty.ipp | 17 +++++++++++++++++ ArduinoCloudPropertyContainer.cpp | 17 +++++++++++++++++ ArduinoCloudPropertyContainer.hpp | 17 +++++++++++++++++ ArduinoCloudPropertyContainer.ipp | 17 +++++++++++++++++ ArduinoCloudThing.cpp | 17 +++++++++++++++++ ArduinoCloudThing.h | 17 +++++++++++++++++ 7 files changed, 119 insertions(+) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 8c8ac5f7b..76820da88 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -1,3 +1,20 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + #ifndef ARDUINO_CLOUD_PROPERTY_HPP_ #define ARDUINO_CLOUD_PROPERTY_HPP_ diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 9fb333bca..40b1c3e5d 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -1,3 +1,20 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + /****************************************************************************** * CTOR/DTOR ******************************************************************************/ diff --git a/ArduinoCloudPropertyContainer.cpp b/ArduinoCloudPropertyContainer.cpp index 543ffccfe..045ebd3e9 100644 --- a/ArduinoCloudPropertyContainer.cpp +++ b/ArduinoCloudPropertyContainer.cpp @@ -1,3 +1,20 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + /****************************************************************************** * INCLUDE ******************************************************************************/ diff --git a/ArduinoCloudPropertyContainer.hpp b/ArduinoCloudPropertyContainer.hpp index 7c3a5e952..3831850e8 100644 --- a/ArduinoCloudPropertyContainer.hpp +++ b/ArduinoCloudPropertyContainer.hpp @@ -1,3 +1,20 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + #ifndef ARDUINO_CLOUD_PROPERTY_CONTAINER_NEW_H_ #define ARDUINO_CLOUD_PROPERTY_CONTAINER_NEW_H_ diff --git a/ArduinoCloudPropertyContainer.ipp b/ArduinoCloudPropertyContainer.ipp index 6a58f5ba3..cd9c85242 100644 --- a/ArduinoCloudPropertyContainer.ipp +++ b/ArduinoCloudPropertyContainer.ipp @@ -1,3 +1,20 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + /****************************************************************************** * PRIVATE MEMBER FUNCTIONS ******************************************************************************/ diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 0fc2a101c..12c1e0485 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -1,3 +1,20 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + /****************************************************************************** * INCLUDE ******************************************************************************/ diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 996fdef0f..9cb22498d 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -1,3 +1,20 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + #ifndef ARDUINO_CLOUD_THING_H_ #define ARDUINO_CLOUD_THING_H_ From b3fc51f1347dd6858ce8d2a613fdd1651fff837b Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Fri, 8 Feb 2019 14:36:22 +0100 Subject: [PATCH 111/175] Disable assert for target build (#15) * Release 1.2.2 * Implementing test for verifying correct behaviour in case of a undefined CBOR map key * Adding code coverage analysis of code under unit test as a post build step * Disabling asserts for non-host/target build * Fixing CI build - lcov needs to be installed before it can be used --- lib/tinycbor/src/cbor.h | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/tinycbor/src/cbor.h b/lib/tinycbor/src/cbor.h index fd145be2e..3eb52e41f 100644 --- a/lib/tinycbor/src/cbor.h +++ b/lib/tinycbor/src/cbor.h @@ -329,7 +329,9 @@ CBOR_INLINE_API bool cbor_value_is_boolean(const CborValue *value) { return value->type == CborBooleanType; } CBOR_INLINE_API CborError cbor_value_get_boolean(const CborValue *value, bool *result) { +#ifdef HOST_BUILD assert(cbor_value_is_boolean(value)); +#endif *result = !!value->extra; return CborNoError; } @@ -339,7 +341,9 @@ CBOR_INLINE_API bool cbor_value_is_simple_type(const CborValue *value) { return value->type == CborSimpleType; } CBOR_INLINE_API CborError cbor_value_get_simple_type(const CborValue *value, uint8_t *result) { +#ifdef HOST_BUILD assert(cbor_value_is_simple_type(value)); +#endif *result = (uint8_t)value->extra; return CborNoError; } @@ -354,21 +358,27 @@ CBOR_INLINE_API bool cbor_value_is_negative_integer(const CborValue *value) CBOR_INLINE_API CborError cbor_value_get_raw_integer(const CborValue *value, uint64_t *result) { +#ifdef HOST_BUILD assert(cbor_value_is_integer(value)); +#endif *result = _cbor_value_extract_int64_helper(value); return CborNoError; } CBOR_INLINE_API CborError cbor_value_get_uint64(const CborValue *value, uint64_t *result) { +#ifdef HOST_BUILD assert(cbor_value_is_unsigned_integer(value)); +#endif *result = _cbor_value_extract_int64_helper(value); return CborNoError; } CBOR_INLINE_API CborError cbor_value_get_int64(const CborValue *value, int64_t *result) { +#ifdef HOST_BUILD assert(cbor_value_is_integer(value)); +#endif *result = (int64_t) _cbor_value_extract_int64_helper(value); if (value->flags & CborIteratorFlag_NegativeInteger) *result = -*result - 1; @@ -377,7 +387,9 @@ CBOR_INLINE_API CborError cbor_value_get_int64(const CborValue *value, int64_t * CBOR_INLINE_API CborError cbor_value_get_int(const CborValue *value, int *result) { +#ifdef HOST_BUILD assert(cbor_value_is_integer(value)); +#endif *result = (int) _cbor_value_extract_int64_helper(value); if (value->flags & CborIteratorFlag_NegativeInteger) *result = -*result - 1; @@ -395,7 +407,9 @@ CBOR_INLINE_API bool cbor_value_is_tag(const CborValue *value) { return value->type == CborTagType; } CBOR_INLINE_API CborError cbor_value_get_tag(const CborValue *value, CborTag *result) { +#ifdef HOST_BUILD assert(cbor_value_is_tag(value)); +#endif *result = _cbor_value_extract_int64_helper(value); return CborNoError; } @@ -410,7 +424,9 @@ CBOR_INLINE_API bool cbor_value_is_text_string(const CborValue *value) CBOR_INLINE_API CborError cbor_value_get_string_length(const CborValue *value, size_t *length) { uint64_t v; +#ifdef HOST_BUILD assert(cbor_value_is_byte_string(value) || cbor_value_is_text_string(value)); +#endif if (!cbor_value_is_length_known(value)) return CborErrorUnknownLength; v = _cbor_value_extract_int64_helper(value); @@ -430,26 +446,34 @@ CBOR_API CborError cbor_value_calculate_string_length(const CborValue *value, si CBOR_INLINE_API CborError cbor_value_copy_text_string(const CborValue *value, char *buffer, size_t *buflen, CborValue *next) { +#ifdef HOST_BUILD assert(cbor_value_is_text_string(value)); +#endif return _cbor_value_copy_string(value, buffer, buflen, next); } CBOR_INLINE_API CborError cbor_value_copy_byte_string(const CborValue *value, uint8_t *buffer, size_t *buflen, CborValue *next) { +#ifdef HOST_BUILD assert(cbor_value_is_byte_string(value)); +#endif return _cbor_value_copy_string(value, buffer, buflen, next); } CBOR_INLINE_API CborError cbor_value_dup_text_string(const CborValue *value, char **buffer, size_t *buflen, CborValue *next) { +#ifdef HOST_BUILD assert(cbor_value_is_text_string(value)); +#endif return _cbor_value_dup_string(value, (void **)buffer, buflen, next); } CBOR_INLINE_API CborError cbor_value_dup_byte_string(const CborValue *value, uint8_t **buffer, size_t *buflen, CborValue *next) { +#ifdef HOST_BUILD assert(cbor_value_is_byte_string(value)); +#endif return _cbor_value_dup_string(value, (void **)buffer, buflen, next); } @@ -464,7 +488,9 @@ CBOR_INLINE_API bool cbor_value_is_map(const CborValue *value) CBOR_INLINE_API CborError cbor_value_get_array_length(const CborValue *value, size_t *length) { uint64_t v; +#ifdef HOST_BUILD assert(cbor_value_is_array(value)); +#endif if (!cbor_value_is_length_known(value)) return CborErrorUnknownLength; v = _cbor_value_extract_int64_helper(value); @@ -477,7 +503,9 @@ CBOR_INLINE_API CborError cbor_value_get_array_length(const CborValue *value, si CBOR_INLINE_API CborError cbor_value_get_map_length(const CborValue *value, size_t *length) { uint64_t v; +#ifdef HOST_BUILD assert(cbor_value_is_map(value)); +#endif if (!cbor_value_is_length_known(value)) return CborErrorUnknownLength; v = _cbor_value_extract_int64_helper(value); @@ -499,8 +527,10 @@ CBOR_INLINE_API bool cbor_value_is_float(const CborValue *value) CBOR_INLINE_API CborError cbor_value_get_float(const CborValue *value, float *result) { uint32_t data; +#ifdef HOST_BUILD assert(cbor_value_is_float(value)); assert(value->flags & CborIteratorFlag_IntegerValueTooLarge); +#endif data = (uint32_t)_cbor_value_decode_int64_internal(value); memcpy(result, &data, sizeof(*result)); return CborNoError; @@ -511,8 +541,10 @@ CBOR_INLINE_API bool cbor_value_is_double(const CborValue *value) CBOR_INLINE_API CborError cbor_value_get_double(const CborValue *value, double *result) { uint64_t data; +#ifdef HOST_BUILD assert(cbor_value_is_double(value)); assert(value->flags & CborIteratorFlag_IntegerValueTooLarge); +#endif data = _cbor_value_decode_int64_internal(value); memcpy(result, &data, sizeof(*result)); return CborNoError; From f47d0f2726442c706a6a8199987ec3d71446191b Mon Sep 17 00:00:00 2001 From: Mattia Bertorello Date: Fri, 8 Feb 2019 12:51:09 +0100 Subject: [PATCH 112/175] Add automatically decode for V1 and V2 cloud protocol Remove the fixed protocol decision in the decode method Now the library can decode the V1 or V2 without configuration --- ArduinoCloudThing.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 12c1e0485..c899e1398 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -224,10 +224,7 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * v if(cbor_value_at_end(value_iter)) { next_state = MapParserState::LeaveMap; - } - else { - if(_cloud_protocol == CloudProtocol::V1) { - if(cbor_value_is_text_string(value_iter)) { + } else if(cbor_value_is_text_string(value_iter)) { char * val = 0; size_t val_size = 0; if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { @@ -242,11 +239,7 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * v else next_state = MapParserState::UndefinedKey; free(val); } - } - } - - if(_cloud_protocol == CloudProtocol::V2) { - if(cbor_value_is_integer(value_iter)) { + } else if (cbor_value_is_integer(value_iter)) { int val = 0; if(cbor_value_get_int(value_iter, &val) == CborNoError) { if(cbor_value_advance(value_iter) == CborNoError) { @@ -261,10 +254,7 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * v else next_state = MapParserState::UndefinedKey; } } - } - } } - return next_state; } From 8b6a40f6c17bba0f822d5a01e19707981862dd69 Mon Sep 17 00:00:00 2001 From: Mattia Bertorello Date: Fri, 8 Feb 2019 12:53:00 +0100 Subject: [PATCH 113/175] Change protocol in encode from V1 to V2 --- ArduinoCloudThing.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 9cb22498d..7a5c4c18a 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -47,7 +47,7 @@ static long const DAYS = 86400; class ArduinoCloudThing { public: - ArduinoCloudThing(CloudProtocol const cloud_protocol = CloudProtocol::V1); + ArduinoCloudThing(CloudProtocol const cloud_protocol = CloudProtocol::V2); void begin(); From 92360bb7c61e3309e5640d1564e890ad30cd8686 Mon Sep 17 00:00:00 2001 From: Mattia Bertorello Date: Fri, 8 Feb 2019 15:09:32 +0100 Subject: [PATCH 114/175] Add some comments to explain which protocol version the decode will use --- ArduinoCloudThing.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index c899e1398..b98ecc555 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -224,6 +224,9 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * v if(cbor_value_at_end(value_iter)) { next_state = MapParserState::LeaveMap; + // If the key is a string means that the Map use the string keys (protocol V1) + // https://tools.ietf.org/html/rfc8428#section-4.3 + // example [{"n": "temperature", "v": 25}] } else if(cbor_value_is_text_string(value_iter)) { char * val = 0; size_t val_size = 0; @@ -239,6 +242,8 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * v else next_state = MapParserState::UndefinedKey; free(val); } + // If the key is a number means that the Map use the CBOR Label (protocol V2) + // example [{0: "temperature", 2: 25}] } else if (cbor_value_is_integer(value_iter)) { int val = 0; if(cbor_value_get_int(value_iter, &val) == CborNoError) { From cbe49da79ef1699bfedef57546c01c2afa96db9f Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Tue, 12 Feb 2019 10:30:34 +0100 Subject: [PATCH 115/175] Removing enum class CloudProtocol since the 'decode' function decodes both V1/V2 and encoding will only happen in V2 anymore. Decoding for V2 shall be removed at a later point in time (#19) --- ArduinoCloudProperty.hpp | 9 ++------- ArduinoCloudProperty.ipp | 32 +++++++++++++------------------ ArduinoCloudPropertyContainer.cpp | 10 +++++----- ArduinoCloudPropertyContainer.hpp | 4 ++-- ArduinoCloudPropertyContainer.ipp | 4 ++-- ArduinoCloudThing.cpp | 23 ++++++++++++---------- ArduinoCloudThing.h | 5 ++--- 7 files changed, 39 insertions(+), 48 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 76820da88..19e41645e 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -34,11 +34,6 @@ * TYPEDEF ******************************************************************************/ -enum class CloudProtocol { - V1, /* [{"n": "test", "vb": true}] */ - V2 /* [{0: "test", 4: true}] */ -}; - enum class Permission { Read, Write, ReadWrite }; @@ -97,7 +92,7 @@ class ArduinoCloudProperty { bool shouldBeUpdated (); void execCallbackOnChange (); - void append (CborEncoder * encoder, CloudProtocol const cloud_protocol); + void append (CborEncoder * encoder); private: @@ -117,7 +112,7 @@ class ArduinoCloudProperty { unsigned long _last_updated_millis, _update_interval_millis; - void appendValue(CborEncoder * mapEncoder, CloudProtocol const cloud_protocol) const; + void appendValue(CborEncoder * mapEncoder) const; bool isValueDifferent(T const lhs, T const rhs) const; T getInitialMinDeltaPropertyValue() const; diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 40b1c3e5d..c3b0fccc1 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -103,16 +103,14 @@ void ArduinoCloudProperty::execCallbackOnChange() { } template -void ArduinoCloudProperty::append(CborEncoder * encoder, CloudProtocol const cloud_protocol) { +void ArduinoCloudProperty::append(CborEncoder * encoder) { if (isReadableByCloud()) { CborEncoder mapEncoder; - cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); - - if (cloud_protocol == CloudProtocol::V1) cbor_encode_text_stringz(&mapEncoder, "n"); - else if(cloud_protocol == CloudProtocol::V2) cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::Name)); - cbor_encode_text_stringz(&mapEncoder, _name.c_str()); - appendValue(&mapEncoder, cloud_protocol); + cbor_encoder_create_map (encoder, &mapEncoder, CborIndefiniteLength); + cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::Name)); + cbor_encode_text_stringz (&mapEncoder, _name.c_str()); + appendValue (&mapEncoder); cbor_encoder_close_container(encoder, &mapEncoder); _shadow_property = _property; @@ -126,30 +124,26 @@ void ArduinoCloudProperty::append(CborEncoder * encoder, CloudProtocol const ******************************************************************************/ template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder, CloudProtocol const cloud_protocol) const { - if (cloud_protocol == CloudProtocol::V1) cbor_encode_text_stringz(mapEncoder, "vb"); - else if(cloud_protocol == CloudProtocol::V2) cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { + cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); cbor_encode_boolean(mapEncoder, _property); } template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder, CloudProtocol const cloud_protocol) const { - if (cloud_protocol == CloudProtocol::V1) cbor_encode_text_stringz(mapEncoder, "v"); - else if(cloud_protocol == CloudProtocol::V2) cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { + cbor_encode_int(mapEncoder, static_cast(CborIntegerMapKey::Value)); cbor_encode_int(mapEncoder, _property); } template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder, CloudProtocol const cloud_protocol) const { - if (cloud_protocol == CloudProtocol::V1) cbor_encode_text_stringz(mapEncoder, "v"); - else if(cloud_protocol == CloudProtocol::V2) cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { + cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); cbor_encode_float(mapEncoder, _property); } template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder, CloudProtocol const cloud_protocol) const { - if (cloud_protocol == CloudProtocol::V1) cbor_encode_text_stringz(mapEncoder, "vs"); - else if(cloud_protocol == CloudProtocol::V2) cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::StringValue)); +inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { + cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::StringValue)); cbor_encode_text_stringz(mapEncoder, _property.c_str()); } diff --git a/ArduinoCloudPropertyContainer.cpp b/ArduinoCloudPropertyContainer.cpp index 045ebd3e9..de4ed42c3 100644 --- a/ArduinoCloudPropertyContainer.cpp +++ b/ArduinoCloudPropertyContainer.cpp @@ -44,9 +44,9 @@ int ArduinoCloudPropertyContainer::getNumOfChangedProperties() { return num_changes_properties; } -void ArduinoCloudPropertyContainer::appendChangedProperties(CborEncoder * arrayEncoder, CloudProtocol const cloud_protocol) { - appendChangedProperties (_bool_property_list, arrayEncoder, cloud_protocol); - appendChangedProperties (_int_property_list, arrayEncoder, cloud_protocol); - appendChangedProperties (_float_property_list, arrayEncoder, cloud_protocol); - appendChangedProperties(_string_property_list, arrayEncoder, cloud_protocol); +void ArduinoCloudPropertyContainer::appendChangedProperties(CborEncoder * arrayEncoder) { + appendChangedProperties (_bool_property_list, arrayEncoder); + appendChangedProperties (_int_property_list, arrayEncoder); + appendChangedProperties (_float_property_list, arrayEncoder); + appendChangedProperties(_string_property_list, arrayEncoder); } diff --git a/ArduinoCloudPropertyContainer.hpp b/ArduinoCloudPropertyContainer.hpp index 3831850e8..4e02119f5 100644 --- a/ArduinoCloudPropertyContainer.hpp +++ b/ArduinoCloudPropertyContainer.hpp @@ -36,7 +36,7 @@ class ArduinoCloudPropertyContainer { bool isPropertyInContainer (Type const type, String const & name); int getNumOfChangedProperties(); - void appendChangedProperties (CborEncoder * arrayEncoder, CloudProtocol const cloud_protocol); + void appendChangedProperties (CborEncoder * arrayEncoder); inline ArduinoCloudProperty * getPropertyBool (String const & name) { return getProperty(_bool_property_list, name); } inline ArduinoCloudProperty * getPropertyInt (String const & name) { return getProperty(_int_property_list, name); } @@ -65,7 +65,7 @@ class ArduinoCloudPropertyContainer { int getNumOfChangedProperties(LinkedList *> & list); template - void appendChangedProperties(LinkedList *> & list, CborEncoder * arrayEncoder, CloudProtocol const cloud_protocol); + void appendChangedProperties(LinkedList *> & list, CborEncoder * arrayEncoder); }; diff --git a/ArduinoCloudPropertyContainer.ipp b/ArduinoCloudPropertyContainer.ipp index cd9c85242..be17f2650 100644 --- a/ArduinoCloudPropertyContainer.ipp +++ b/ArduinoCloudPropertyContainer.ipp @@ -52,11 +52,11 @@ int ArduinoCloudPropertyContainer::getNumOfChangedProperties(LinkedList -void ArduinoCloudPropertyContainer::appendChangedProperties(LinkedList *> & list, CborEncoder * arrayEncoder, CloudProtocol const cloud_protocol) { +void ArduinoCloudPropertyContainer::appendChangedProperties(LinkedList *> & list, CborEncoder * arrayEncoder) { for (int i = 0; i < list.size(); i++) { ArduinoCloudProperty * p = list.get(i); if (p->shouldBeUpdated() && p->isReadableByCloud()) { - p->append(arrayEncoder, cloud_protocol); + p->append(arrayEncoder); } } } diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index b98ecc555..3f05633dc 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -56,8 +56,7 @@ static void utox8(uint32_t val, char* s) { * CTOR/DTOR ******************************************************************************/ -ArduinoCloudThing::ArduinoCloudThing(CloudProtocol const cloud_protocol) -: _cloud_protocol(cloud_protocol) { +ArduinoCloudThing::ArduinoCloudThing() { #ifdef ARDUINO_ARCH_SAMD #define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x0080A00C) #define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x0080A040) @@ -100,7 +99,7 @@ int ArduinoCloudThing::encode(uint8_t * data, size_t const size) { return -1; } - _property_cont.appendChangedProperties(&arrayEncoder, _cloud_protocol); + _property_cont.appendChangedProperties(&arrayEncoder); err = cbor_encoder_close_container(&encoder, &arrayEncoder); @@ -224,10 +223,12 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * v if(cbor_value_at_end(value_iter)) { next_state = MapParserState::LeaveMap; - // If the key is a string means that the Map use the string keys (protocol V1) - // https://tools.ietf.org/html/rfc8428#section-4.3 - // example [{"n": "temperature", "v": 25}] - } else if(cbor_value_is_text_string(value_iter)) { + } + /* If the key is a string means that the Map use the string keys (protocol V1) + * https://tools.ietf.org/html/rfc8428#section-4.3 + * Example [{"n": "temperature", "v": 25}] + */ + else if(cbor_value_is_text_string(value_iter)) { char * val = 0; size_t val_size = 0; if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { @@ -242,9 +243,11 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * v else next_state = MapParserState::UndefinedKey; free(val); } - // If the key is a number means that the Map use the CBOR Label (protocol V2) - // example [{0: "temperature", 2: 25}] - } else if (cbor_value_is_integer(value_iter)) { + } + /* If the key is a number means that the Map use the CBOR Label (protocol V2) + * Example [{0: "temperature", 2: 25}] + */ + else if (cbor_value_is_integer(value_iter)) { int val = 0; if(cbor_value_get_int(value_iter, &val) == CborNoError) { if(cbor_value_advance(value_iter) == CborNoError) { diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 7a5c4c18a..c4330ffd2 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -47,7 +47,7 @@ static long const DAYS = 86400; class ArduinoCloudThing { public: - ArduinoCloudThing(CloudProtocol const cloud_protocol = CloudProtocol::V2); + ArduinoCloudThing(); void begin(); @@ -64,7 +64,6 @@ class ArduinoCloudThing { private: - CloudProtocol const _cloud_protocol; bool _status = OFF; char _uuid[33]; ArduinoCloudPropertyContainer _property_cont; @@ -96,7 +95,7 @@ class ArduinoCloudThing { inline T const get () const { return _entry; } inline bool isSet() const { return _is_set; } - inline bool reset() { _is_set = false; } + inline void reset() { _is_set = false; } private: From c6899edad16695a0b91a04173d2a359fb72139f3 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Tue, 12 Feb 2019 10:48:57 +0100 Subject: [PATCH 116/175] Reapplying changes of PR #11 (refactoring) (8d773a4) ... (#20) * Removing enum class CloudProtocol since the 'decode' function decodes both V1/V2 and encoding will only happen in V2 anymore. Decoding for V2 shall be removed at a later point in time * Reapplying changes of PR #11 (refactoring)(8d773a4c2d8360fedd2cf6b881630baf98338cc6) which somehow got lost in the merges --- ArduinoCloudThing.cpp | 259 ++++++++++++++++++------------------------ ArduinoCloudThing.h | 42 ++++--- 2 files changed, 137 insertions(+), 164 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 3f05633dc..7ebe42b4d 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -87,31 +87,30 @@ int ArduinoCloudThing::encode(uint8_t * data, size_t const size) { // time interval may be elapsed or property may be changed int const num_changed_properties = _property_cont.getNumOfChangedProperties(); - if (num_changed_properties > 0) { - CborError err; - CborEncoder encoder, arrayEncoder; - - cbor_encoder_init(&encoder, data, size, 0); - // create a cbor array containing the property that should be updated. - err = cbor_encoder_create_array(&encoder, &arrayEncoder, num_changed_properties); - if (err) { - //Serial.println(cbor_error_string(err)); - return -1; - } + if(num_changed_properties > 0) { + CborEncoder encoder, arrayEncoder; - _property_cont.appendChangedProperties(&arrayEncoder); + cbor_encoder_init(&encoder, data, size, 0); - err = cbor_encoder_close_container(&encoder, &arrayEncoder); + if(cbor_encoder_create_array(&encoder, &arrayEncoder, num_changed_properties) != CborNoError) { + return -1; + } - // return the number of byte of the CBOR encoded array - return cbor_encoder_get_buffer_size(&encoder, data); - } + _property_cont.appendChangedProperties(&arrayEncoder); + + if(cbor_encoder_close_container(&encoder, &arrayEncoder) != CborNoError) { + return -1; + } #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) - PrintFreeRam(); +PrintFreeRam(); #endif - // If nothing has to be sent, return diff, that is 0 in this case - return num_changed_properties; + int const bytes_encoded = cbor_encoder_get_buffer_size(&encoder, data); + return bytes_encoded; + } + else { + return num_changed_properties; + } } ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(bool & property, String const & name, Permission const permission) { @@ -174,7 +173,7 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt if(cbor_value_enter_container(&array_iter, &map_iter) != CborNoError) return; - MapData map_data; + CborMapData map_data; MapParserState current_state = MapParserState::EnterMap, next_state; @@ -205,12 +204,12 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt * PRIVATE MEMBER FUNCTIONS ******************************************************************************/ -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_EnterMap(CborValue * map_iter, CborValue * value_iter, MapData * map_data) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_EnterMap(CborValue * map_iter, CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; if(cbor_value_get_type(map_iter) == CborMapType) { if(cbor_value_enter_container(map_iter, value_iter) == CborNoError) { - resetMapData(map_data); + map_data->reset(); next_state = MapParserState::MapKey; } } @@ -229,40 +228,41 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * v * Example [{"n": "temperature", "v": 25}] */ else if(cbor_value_is_text_string(value_iter)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { - if (strcmp(val, "n" ) == 0) next_state = MapParserState::Name; - else if(strcmp(val, "bver") == 0) next_state = MapParserState::BaseVersion; - else if(strcmp(val, "bn" ) == 0) next_state = MapParserState::BaseName; - else if(strcmp(val, "bt" ) == 0) next_state = MapParserState::BaseTime; - else if(strcmp(val, "v" ) == 0) next_state = MapParserState::Value; - else if(strcmp(val, "vs" ) == 0) next_state = MapParserState::StringValue; - else if(strcmp(val, "vb" ) == 0) next_state = MapParserState::BooleanValue; - else if(strcmp(val, "t" ) == 0) next_state = MapParserState::Time; - else next_state = MapParserState::UndefinedKey; - free(val); - } + char * val = 0; + size_t val_size = 0; + if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { + if (strcmp(val, "n" ) == 0) next_state = MapParserState::Name; + else if(strcmp(val, "bver") == 0) next_state = MapParserState::BaseVersion; + else if(strcmp(val, "bn" ) == 0) next_state = MapParserState::BaseName; + else if(strcmp(val, "bt" ) == 0) next_state = MapParserState::BaseTime; + else if(strcmp(val, "v" ) == 0) next_state = MapParserState::Value; + else if(strcmp(val, "vs" ) == 0) next_state = MapParserState::StringValue; + else if(strcmp(val, "vb" ) == 0) next_state = MapParserState::BooleanValue; + else if(strcmp(val, "t" ) == 0) next_state = MapParserState::Time; + else next_state = MapParserState::UndefinedKey; + free(val); + } } /* If the key is a number means that the Map use the CBOR Label (protocol V2) * Example [{0: "temperature", 2: 25}] */ else if (cbor_value_is_integer(value_iter)) { - int val = 0; - if(cbor_value_get_int(value_iter, &val) == CborNoError) { - if(cbor_value_advance(value_iter) == CborNoError) { - if (val == static_cast(CborIntegerMapKey::Name )) next_state = MapParserState::Name; - else if(val == static_cast(CborIntegerMapKey::BaseVersion )) next_state = MapParserState::BaseVersion; - else if(val == static_cast(CborIntegerMapKey::BaseName )) next_state = MapParserState::BaseName; - else if(val == static_cast(CborIntegerMapKey::BaseTime )) next_state = MapParserState::BaseTime; - else if(val == static_cast(CborIntegerMapKey::Value )) next_state = MapParserState::Value; - else if(val == static_cast(CborIntegerMapKey::StringValue )) next_state = MapParserState::StringValue; - else if(val == static_cast(CborIntegerMapKey::BooleanValue)) next_state = MapParserState::BooleanValue; - else if(val == static_cast(CborIntegerMapKey::Time )) next_state = MapParserState::Time; - else next_state = MapParserState::UndefinedKey; - } - } + int val = 0; + if(cbor_value_get_int(value_iter, &val) == CborNoError) { + if(cbor_value_advance(value_iter) == CborNoError) { + if (val == static_cast(CborIntegerMapKey::Name )) next_state = MapParserState::Name; + else if(val == static_cast(CborIntegerMapKey::BaseVersion )) next_state = MapParserState::BaseVersion; + else if(val == static_cast(CborIntegerMapKey::BaseName )) next_state = MapParserState::BaseName; + else if(val == static_cast(CborIntegerMapKey::BaseTime )) next_state = MapParserState::BaseTime; + else if(val == static_cast(CborIntegerMapKey::Value )) next_state = MapParserState::Value; + else if(val == static_cast(CborIntegerMapKey::StringValue )) next_state = MapParserState::StringValue; + else if(val == static_cast(CborIntegerMapKey::BooleanValue)) next_state = MapParserState::BooleanValue; + else if(val == static_cast(CborIntegerMapKey::Time )) next_state = MapParserState::Time; + else next_state = MapParserState::UndefinedKey; + } + } } + return next_state; } @@ -276,13 +276,14 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_UndefinedKey(CborVal return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseVersion(CborValue * value_iter, MapData * map_data) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseVersion(CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; if(cbor_value_is_integer(value_iter)) { int val = 0; if(cbor_value_get_int(value_iter, &val) == CborNoError) { map_data->base_version.set(val); + if(cbor_value_advance(value_iter) == CborNoError) { next_state = MapParserState::MapKey; } @@ -292,7 +293,7 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseVersion(CborValu return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseName(CborValue * value_iter, MapData * map_data) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseName(CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; if(cbor_value_is_text_string(value_iter)) { @@ -308,45 +309,22 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseName(CborValue * return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseTime(CborValue * value_iter, MapData * map_data) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseTime(CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; - if(cbor_value_is_integer(value_iter)) { - int val = 0; - if(cbor_value_get_int(value_iter, &val) == CborNoError) { - map_data->base_time.set(static_cast(val)); - } - } - - if(cbor_value_is_double(value_iter)) { - double val = 0.0; - if(cbor_value_get_double(value_iter, &val) == CborNoError) { - map_data->base_time.set(val); - } - } - - if(cbor_value_is_float(value_iter)) { - float val = 0.0; - if(cbor_value_get_float(value_iter, &val) == CborNoError) { - map_data->base_time.set(static_cast(val)); - } - } + double val = 0.0; + if(ifNumericConvertToDouble(value_iter, &val)) { + map_data->base_time.set(val); - if(cbor_value_is_half_float(value_iter)) { - uint16_t val = 0; - if(cbor_value_get_half_float(value_iter, &val) == CborNoError) { - map_data->base_time.set(static_cast(convertCborHalfFloatToDouble(val))); + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::MapKey; } } - if(cbor_value_advance(value_iter) == CborNoError) { - next_state = MapParserState::MapKey; - } - return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Name(CborValue * value_iter, MapData * map_data) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Name(CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; if(cbor_value_is_text_string(value_iter)) { @@ -362,42 +340,22 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Name(CborValue * val return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Value(CborValue * value_iter, MapData * map_data) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Value(CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; - if(value_iter->type == CborIntegerType) { - int val = 0; - if(cbor_value_get_int(value_iter, &val) == CborNoError) { - map_data->val.set(static_cast(val)); - } - } - else if(value_iter->type == CborDoubleType) { - double val = 0.0; - if(cbor_value_get_double(value_iter, &val) == CborNoError) { - map_data->val.set(static_cast(val)); - } - } - else if(value_iter->type == CborFloatType) { - float val = 0.0f; - if(cbor_value_get_float(value_iter, &val) == CborNoError) { - map_data->val.set(val); - } - } - else if(value_iter->type == CborHalfFloatType) { - uint16_t val = 0; - if(cbor_value_get_half_float(value_iter, &val) == CborNoError) { - map_data->val.set(static_cast(convertCborHalfFloatToDouble(val))); - } - } + double val = 0.0; + if(ifNumericConvertToDouble(value_iter, &val)) { + map_data->val.set(val); - if(cbor_value_advance(value_iter) == CborNoError) { - next_state = MapParserState::MapKey; + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::MapKey; + } } return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_StringValue(CborValue * value_iter, MapData * map_data) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_StringValue(CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; if(cbor_value_is_text_string(value_iter)) { @@ -413,12 +371,13 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_StringValue(CborValu return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BooleanValue(CborValue * value_iter, MapData * map_data) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BooleanValue(CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; bool val = false; if(cbor_value_get_boolean(value_iter, &val) == CborNoError) { map_data->bool_val.set(val); + if(cbor_value_advance(value_iter) == CborNoError) { next_state = MapParserState::MapKey; } @@ -427,45 +386,22 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BooleanValue(CborVal return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * value_iter, MapData * map_data) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; - if(cbor_value_is_integer(value_iter)) { - int val = 0; - if(cbor_value_get_int(value_iter, &val) == CborNoError) { - map_data->time.set(static_cast(val)); - } - } - - if(cbor_value_is_double(value_iter)) { - double val = 0.0; - if(cbor_value_get_double(value_iter, &val) == CborNoError) { - map_data->time.set(val); - } - } - - if(cbor_value_is_float(value_iter)) { - float val = 0.0; - if(cbor_value_get_float(value_iter, &val) == CborNoError) { - map_data->time.set(static_cast(val)); - } - } + double val = 0.0; + if(ifNumericConvertToDouble(value_iter, &val)) { + map_data->time.set(val); - if(cbor_value_is_half_float(value_iter)) { - uint16_t val = 0; - if(cbor_value_get_half_float(value_iter, &val) == CborNoError) { - map_data->time.set(static_cast(convertCborHalfFloatToDouble(val))); + if(cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::MapKey; } } - if(cbor_value_advance(value_iter) == CborNoError) { - next_state = MapParserState::MapKey; - } - return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * map_iter, CborValue * value_iter, MapData const * const map_data) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * map_iter, CborValue * value_iter, CborMapData const * const map_data) { MapParserState next_state = MapParserState::Error; /* Update the property containers depending on the parsed data */ @@ -521,15 +457,38 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * return next_state; } -void ArduinoCloudThing::resetMapData(MapData * map_data) { - map_data->base_version.reset(); - map_data->base_name.reset (); - map_data->base_time.reset (); - map_data->name.reset (); - map_data->val.reset (); - map_data->str_val.reset (); - map_data->bool_val.reset (); - map_data->time.reset (); +bool ArduinoCloudThing::ifNumericConvertToDouble(CborValue * value_iter, double * numeric_val) { + + if(cbor_value_is_integer(value_iter)) { + int val = 0; + if(cbor_value_get_int(value_iter, &val) == CborNoError) { + *numeric_val = static_cast(val); + return true; + } + } + else if(cbor_value_is_double(value_iter)) { + double val = 0.0; + if(cbor_value_get_double(value_iter, &val) == CborNoError) { + *numeric_val = val; + return true; + } + } + else if(cbor_value_is_float(value_iter)) { + float val = 0.0; + if(cbor_value_get_float(value_iter, &val) == CborNoError) { + *numeric_val = static_cast(val); + return true; + } + } + else if(cbor_value_is_half_float(value_iter)) { + uint16_t val = 0; + if(cbor_value_get_half_float(value_iter, &val) == CborNoError) { + *numeric_val = static_cast(convertCborHalfFloatToDouble(val)); + return true; + } + } + + return false; } /* Source Idea from https://tools.ietf.org/html/rfc7049 : Page: 50 */ diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index c4330ffd2..1cb8eb98f 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -104,7 +104,21 @@ class ArduinoCloudThing { }; - typedef struct { + class CborMapData { + + public: + + void reset() { + base_version.reset(); + base_name.reset (); + base_time.reset (); + name.reset (); + val.reset (); + str_val.reset (); + bool_val.reset (); + time.reset (); + } + MapEntry base_version; MapEntry base_name; MapEntry base_time; @@ -113,22 +127,22 @@ class ArduinoCloudThing { MapEntry str_val; MapEntry bool_val; MapEntry time; - } MapData; + }; - MapParserState handle_EnterMap (CborValue * map_iter, CborValue * value_iter, MapData * map_data); + MapParserState handle_EnterMap (CborValue * map_iter, CborValue * value_iter, CborMapData * map_data); MapParserState handle_MapKey (CborValue * value_iter); MapParserState handle_UndefinedKey (CborValue * value_iter); - MapParserState handle_BaseVersion (CborValue * value_iter, MapData * map_data); - MapParserState handle_BaseName (CborValue * value_iter, MapData * map_data); - MapParserState handle_BaseTime (CborValue * value_iter, MapData * map_data); - MapParserState handle_Name (CborValue * value_iter, MapData * map_data); - MapParserState handle_Value (CborValue * value_iter, MapData * map_data); - MapParserState handle_StringValue (CborValue * value_iter, MapData * map_data); - MapParserState handle_BooleanValue (CborValue * value_iter, MapData * map_data); - MapParserState handle_Time (CborValue * value_iter, MapData * map_data); - MapParserState handle_LeaveMap (CborValue * map_iter, CborValue * value_iter, MapData const * const map_data); - - static void resetMapData (MapData * map_data); + MapParserState handle_BaseVersion (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_BaseName (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_BaseTime (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_Name (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_Value (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_StringValue (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_BooleanValue (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_Time (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_LeaveMap (CborValue * map_iter, CborValue * value_iter, CborMapData const * const map_data); + + static bool ifNumericConvertToDouble (CborValue * value_iter, double * numeric_val); static double convertCborHalfFloatToDouble(uint16_t const half_val); }; From e4a610d5a07f1bd111db2db11c1d3f37ff7c55e2 Mon Sep 17 00:00:00 2001 From: ilcato Date: Fri, 8 Feb 2019 15:12:36 +0100 Subject: [PATCH 117/175] Implementation of the shadow Thing feature on the device --- ArduinoCloudProperty.hpp | 29 +++++++++++-- ArduinoCloudProperty.ipp | 59 ++++++++++++++++++++++++-- ArduinoCloudPropertyContainer.cpp | 12 ++++++ ArduinoCloudPropertyContainer.hpp | 4 ++ ArduinoCloudPropertyContainer.ipp | 15 +++++++ ArduinoCloudThing.cpp | 69 +++++++++++++++++++++++++------ ArduinoCloudThing.h | 44 ++++++++++++++------ 7 files changed, 201 insertions(+), 31 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 19e41645e..5e28478fb 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -82,6 +82,7 @@ class ArduinoCloudProperty { /* Composable configuration of the ArduinoCloudProperty class */ ArduinoCloudProperty & onUpdate (UpdateCallbackFunc func); + ArduinoCloudProperty & onSync (void (*func)(ArduinoCloudProperty property)); ArduinoCloudProperty & publishOnChange(T const min_delta_property, unsigned long const min_time_between_updates_millis = 0); ArduinoCloudProperty & publishEvery (unsigned long const seconds); @@ -91,26 +92,48 @@ class ArduinoCloudProperty { bool shouldBeUpdated (); void execCallbackOnChange (); - + void execCallbackOnSync (); + void forceCallbackOnChange (); + void setPreviousCloudChangeTimestamp (unsigned long cloudChangeTime); + void setLastCloudChangeTimestamp (unsigned long cloudChangeTime); + void setLastLocalChangeTimestamp (unsigned long localChangeTime); + unsigned long getPreviousCloudChangeTimestamp(); + unsigned long getLastCloudChangeTimestamp(); + unsigned long getLastLocalChangeTimestamp(); + void setPropertyValue (T const val); + void setCloudShadowValue (T const val); + T getCloudShadowValue (); + void setLocalShadowValue (T const val); + T getLocalShadowValue (); + void updateTime (unsigned long changeEventTime); + bool isChangedLocally (); void append (CborEncoder * encoder); private: T & _property, - _shadow_property; + _cloud_shadow_property, + _local_shadow_property; String _name; Permission _permission; UpdateCallbackFunc _update_callback_func; + void (*_sync_callback_func)(ArduinoCloudProperty property); UpdatePolicy _update_policy; bool _has_been_updated_once, - _has_been_modified_in_callback; + _has_been_modified_in_callback; /* Variables used for UpdatePolicy::OnChange */ T _min_delta_property; unsigned long _min_time_between_updates_millis; /* Variables used for UpdatePolicy::TimeInterval */ unsigned long _last_updated_millis, _update_interval_millis; + /* Variables used for reconnection sync*/ + unsigned long _last_change_timestamp; + unsigned long _last_local_change_timestamp; + unsigned long _last_cloud_change_timestamp; + unsigned long _previous_cloud_change_timestamp; + void appendValue(CborEncoder * mapEncoder) const; bool isValueDifferent(T const lhs, T const rhs) const; diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index c3b0fccc1..3d0c5f8ca 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -23,6 +23,7 @@ template ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, Permission const permission) : _property(property), _shadow_property(property), + _local_shadow_property(property), _name(name), _permission(permission), _update_callback_func(NULL), @@ -32,7 +33,9 @@ ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, _min_delta_property(getInitialMinDeltaPropertyValue()), _min_time_between_updates_millis(0), _last_updated_millis(0), - _update_interval_millis(0) + _update_interval_millis(0), + _local_change_timestamp(0), + _last_change_timestamp(0) { } @@ -44,7 +47,7 @@ template void ArduinoCloudProperty::writeByCloud(T const val) { if(isWriteableByCloud()) { _property = val; - /* _shadow_property is not updated so there will be an update the next time around */ + /* _shadow_property and local_shadow_property are not updated so there will be an update the next time around */ } } @@ -89,13 +92,41 @@ bool ArduinoCloudProperty::shouldBeUpdated() { } } +template +bool ArduinoCloudProperty::isAfterLastChange(unsigned long cloudChangeTime) { + if(!_has_been_updated_once) return true; + + if(_local_change_timestamp > _last_change_timestamp) { + return (cloudChangeTime > _local_change_timestamp); + } else { + return (cloudChangeTime > _last_change_timestamp); + } +} + +template +void ArduinoCloudProperty::setLastChangeTime(unsigned long cloudChangeTime) { + if(_local_change_timestamp > _last_change_timestamp) { + (cloudChangeTime > _local_change_timestamp) ? _last_change_timestamp = cloudChangeTime : _last_change_timestamp = _local_change_timestamp; + } else { + if(cloudChangeTime > _last_change_timestamp) _last_change_timestamp = cloudChangeTime; + } +} + + +template +void ArduinoCloudProperty::forceCallbackOnChange() { + if(_update_callback_func != NULL) { + _update_callback_func(); + } +} + template void ArduinoCloudProperty::execCallbackOnChange() { if(isValueDifferent(_property, _shadow_property)) { if(_update_callback_func != NULL) { _update_callback_func(); } - + if(!isValueDifferent(_property, _shadow_property)) { _has_been_modified_in_callback = true; } @@ -114,11 +145,33 @@ void ArduinoCloudProperty::append(CborEncoder * encoder) { cbor_encoder_close_container(encoder, &mapEncoder); _shadow_property = _property; + _local_shadow_property = _property; _has_been_updated_once = true; _last_updated_millis = millis(); } } +template +boolean ArduinoCloudProperty::isChangedLocally() { + return isValueDifferent(_property, _local_shadow_property); +} + +template +void ArduinoCloudProperty::updateTime(unsigned long changeEventTime) { + if (isReadableByCloud()) { + _local_shadow_property = _property; + _local_change_timestamp = changeEventTime; + } +} + +template +void ArduinoCloudProperty::setShadowValue(T const val) { + if(isWriteableByCloud()) { + _shadow_property = val; + } +} + + /****************************************************************************** * PRIVATE MEMBER FUNCTIONS ******************************************************************************/ diff --git a/ArduinoCloudPropertyContainer.cpp b/ArduinoCloudPropertyContainer.cpp index de4ed42c3..e1fd04a2b 100644 --- a/ArduinoCloudPropertyContainer.cpp +++ b/ArduinoCloudPropertyContainer.cpp @@ -50,3 +50,15 @@ void ArduinoCloudPropertyContainer::appendChangedProperties(CborEncoder * arrayE appendChangedProperties (_float_property_list, arrayEncoder); appendChangedProperties(_string_property_list, arrayEncoder); } + + +int ArduinoCloudPropertyContainer::updateTimestampOnChangedProperties(unsigned long changeEventTime) { + int num_changes_properties = 0; + + num_changes_properties += updateTimestampOnChangedProperties(_bool_property_list, changeEventTime); + num_changes_properties += updateTimestampOnChangedProperties(_int_property_list, changeEventTime); + num_changes_properties += updateTimestampOnChangedProperties(_float_property_list, changeEventTime); + num_changes_properties += updateTimestampOnChangedProperties(_string_property_list, changeEventTime); + + return num_changes_properties; +} diff --git a/ArduinoCloudPropertyContainer.hpp b/ArduinoCloudPropertyContainer.hpp index 4e02119f5..36f2b566c 100644 --- a/ArduinoCloudPropertyContainer.hpp +++ b/ArduinoCloudPropertyContainer.hpp @@ -36,6 +36,7 @@ class ArduinoCloudPropertyContainer { bool isPropertyInContainer (Type const type, String const & name); int getNumOfChangedProperties(); + int updateTimestampOnChangedProperties(unsigned long changeEventTime); void appendChangedProperties (CborEncoder * arrayEncoder); inline ArduinoCloudProperty * getPropertyBool (String const & name) { return getProperty(_bool_property_list, name); } @@ -63,6 +64,9 @@ class ArduinoCloudPropertyContainer { template int getNumOfChangedProperties(LinkedList *> & list); + + template + int updateTimestampOnChangedProperties(LinkedList *> & list, unsigned long changeEventTime); template void appendChangedProperties(LinkedList *> & list, CborEncoder * arrayEncoder); diff --git a/ArduinoCloudPropertyContainer.ipp b/ArduinoCloudPropertyContainer.ipp index be17f2650..7b469a673 100644 --- a/ArduinoCloudPropertyContainer.ipp +++ b/ArduinoCloudPropertyContainer.ipp @@ -60,3 +60,18 @@ void ArduinoCloudPropertyContainer::appendChangedProperties(LinkedList +int ArduinoCloudPropertyContainer::updateTimestampOnChangedProperties(LinkedList *> & list, unsigned long changeEventTime) { + int num_changes_properties = 0; + + for (int i = 0; i < list.size(); i++) { + ArduinoCloudProperty * p = list.get(i); + if (p->isChangedLocally() && p->isReadableByCloud()) { + p->updateTime(changeEventTime); + } + } + + return num_changes_properties; +} + diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 7ebe42b4d..7235c946d 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -80,6 +80,9 @@ void ArduinoCloudThing::begin() { addPropertyReal(_status, "status", Permission::Read); } +int ArduinoCloudThing::updateTimestampOnChangedProperties(unsigned long changeEventTime) { + return _property_cont.updateTimestampOnChangedProperties(changeEventTime); +} int ArduinoCloudThing::encode(uint8_t * data, size_t const size) { @@ -157,7 +160,9 @@ ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(String & prope } } -void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const length) { +void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const length, bool syncMessage) { + + _syncMessage = syncMessage; CborParser parser; CborValue array_iter, @@ -209,7 +214,7 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_EnterMap(CborValue * if(cbor_value_get_type(map_iter) == CborMapType) { if(cbor_value_enter_container(map_iter, value_iter) == CborNoError) { - map_data->reset(); + resetMapDataNotBase(map_data); next_state = MapParserState::MapKey; } } @@ -404,6 +409,14 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * val ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * map_iter, CborValue * value_iter, CborMapData const * const map_data) { MapParserState next_state = MapParserState::Error; + //computer the cloud event change time + unsigned long cloudChangeEventTime = 0; + if(map_data->base_time.isSet()){ + cloudChangeEventTime = (unsigned long)(map_data->base_time.get()); + } + if(map_data->time.isSet()){ + cloudChangeEventTime += (unsigned long)map_data->time.get(); + } /* Update the property containers depending on the parsed data */ if(map_data->name.isSet()) @@ -414,13 +427,25 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * ArduinoCloudProperty * float_property = _property_cont.getPropertyFloat(map_data->name.get()); if(int_property && int_property->isWriteableByCloud()) { - int_property->writeByCloud(static_cast(map_data->val.get())); /* Val is internally stored as float */ - int_property->execCallbackOnChange(); + if(_syncMessage){ + int_property->setLastCloudChangeTimestamp(cloudChangeEventTime); + int_property->setCloudShadowValue(map_data->val.get()); + int_property->execCallbackOnSync(); + } else { + int_property->writeByCloud(map_data->val.get()); + int_property->execCallbackOnChange(); + } } if(float_property && float_property->isWriteableByCloud()) { - float_property->writeByCloud(map_data->val.get()); - float_property->execCallbackOnChange(); + if(_syncMessage){ + float_property->setLastCloudChangeTimestamp(cloudChangeEventTime); + float_property->setCloudShadowValue(map_data->val.get()); + float_property->execCallbackOnSync(); + } else { + float_property->writeByCloud(map_data->val.get()); + float_property->execCallbackOnChange(); + } } } @@ -428,8 +453,14 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * if(map_data->str_val.isSet()) { ArduinoCloudProperty* string_property = _property_cont.getPropertyString(map_data->name.get()); if(string_property && string_property->isWriteableByCloud()) { - string_property->writeByCloud(map_data->str_val.get()); - string_property->execCallbackOnChange(); + if(_syncMessage){ + string_property->setLastCloudChangeTimestamp(cloudChangeEventTime); + string_property->setCloudShadowValue(map_data->str_val.get()); + string_property->execCallbackOnSync(); + } else { + string_property->writeByCloud(map_data->str_val.get()); + string_property->execCallbackOnChange(); + } } } @@ -437,8 +468,14 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * if(map_data->bool_val.isSet()) { ArduinoCloudProperty* bool_property = _property_cont.getPropertyBool(map_data->name.get()); if(bool_property && bool_property->isWriteableByCloud()) { - bool_property->writeByCloud(map_data->bool_val.get()); - bool_property->execCallbackOnChange(); + if(_syncMessage){ + bool_property->setLastCloudChangeTimestamp(cloudChangeEventTime); + bool_property->setCloudShadowValue(map_data->bool_val.get()); + bool_property->execCallbackOnSync(); + } else { + bool_property->writeByCloud(map_data->bool_val.get()); + bool_property->execCallbackOnChange(); + } } } } @@ -460,8 +497,8 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * bool ArduinoCloudThing::ifNumericConvertToDouble(CborValue * value_iter, double * numeric_val) { if(cbor_value_is_integer(value_iter)) { - int val = 0; - if(cbor_value_get_int(value_iter, &val) == CborNoError) { + uint64_t val = 0; + if(cbor_value_get_uint64(value_iter, &val) == CborNoError) { *numeric_val = static_cast(val); return true; } @@ -491,6 +528,14 @@ bool ArduinoCloudThing::ifNumericConvertToDouble(CborValue * value_iter, double return false; } +void ArduinoCloudThing::resetMapDataNotBase(CborMapData * map_data) { + map_data->name.reset (); + map_data->val.reset (); + map_data->str_val.reset (); + map_data->bool_val.reset (); + map_data->time.reset (); +} + /* Source Idea from https://tools.ietf.org/html/rfc7049 : Page: 50 */ double ArduinoCloudThing::convertCborHalfFloatToDouble(uint16_t const half_val) { int exp = (half_val >> 10) & 0x1f; diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 1cb8eb98f..93835348f 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -40,6 +40,31 @@ static long const MINUTES = 60; static long const HOURS = 3600; static long const DAYS = 86400; +/****************************************************************************** + * SYNCHRONIZATION CALLBACKS + ******************************************************************************/ + +#define AUTO_SYNC onAutoSync +template +void onAutoSync(ArduinoCloudProperty property) { + if( property.getLastCloudChangeTimestamp() > property.getLastLocalChangeTimestamp()){ + property.setPropertyValue(property.getCloudShadowValue()); + property.forceCallbackOnChange(); + } +} + +#define FORCE_CLOUD_SYNC onForceCloudSync +template +void onForceCloudSync(ArduinoCloudProperty property) { + property.setPropertyValue(property.getCloudShadowValue()); + property.forceCallbackOnChange(); +} + +#define FORCE_DEVICE_SYNC onForceDeviceSync // The device property value is already the correct one. The cloud property value will be synchronized at the next update cycle. +template +void onForceDeviceSync(ArduinoCloudProperty property) { +} + /****************************************************************************** * CLASS DECLARATION ******************************************************************************/ @@ -56,10 +81,13 @@ class ArduinoCloudThing { ArduinoCloudProperty & addPropertyReal(float & property, String const & name, Permission const permission); ArduinoCloudProperty & addPropertyReal(String & property, String const & name, Permission const permission); + // compute the timestamp of the local properties changes + int updateTimestampOnChangedProperties(unsigned long changeEventTime); /* encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer */ int encode(uint8_t * data, size_t const size); + int encode(ArduinoCloudPropertyContainer *property_cont, uint8_t * data, size_t const size); /* decode a CBOR payload received from the cloud */ - void decode(uint8_t const * const payload, size_t const length); + void decode(uint8_t const * const payload, size_t const length, bool syncMessage = false); private: @@ -67,6 +95,7 @@ class ArduinoCloudThing { bool _status = OFF; char _uuid[33]; ArduinoCloudPropertyContainer _property_cont; + bool _syncMessage; enum class MapParserState { EnterMap, @@ -107,18 +136,6 @@ class ArduinoCloudThing { class CborMapData { public: - - void reset() { - base_version.reset(); - base_name.reset (); - base_time.reset (); - name.reset (); - val.reset (); - str_val.reset (); - bool_val.reset (); - time.reset (); - } - MapEntry base_version; MapEntry base_name; MapEntry base_time; @@ -142,6 +159,7 @@ class ArduinoCloudThing { MapParserState handle_Time (CborValue * value_iter, CborMapData * map_data); MapParserState handle_LeaveMap (CborValue * map_iter, CborValue * value_iter, CborMapData const * const map_data); + static void resetMapDataNotBase (CborMapData * map_data); static bool ifNumericConvertToDouble (CborValue * value_iter, double * numeric_val); static double convertCborHalfFloatToDouble(uint16_t const half_val); From 6cd2b0b85f0ebfb9dca4e7a4aa0b59fb2900f585 Mon Sep 17 00:00:00 2001 From: ilcato Date: Fri, 8 Feb 2019 15:39:05 +0100 Subject: [PATCH 118/175] fixing ArduinoCloudProperty::isChangedLocally() return type --- ArduinoCloudProperty.ipp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 3d0c5f8ca..5b4b2b120 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -152,7 +152,7 @@ void ArduinoCloudProperty::append(CborEncoder * encoder) { } template -boolean ArduinoCloudProperty::isChangedLocally() { +bool ArduinoCloudProperty::isChangedLocally() { return isValueDifferent(_property, _local_shadow_property); } From 8810cbc77745aef9e0dc4292bfe26017f0330c93 Mon Sep 17 00:00:00 2001 From: ilcato Date: Tue, 19 Feb 2019 10:13:04 +0100 Subject: [PATCH 119/175] definition of sychronization callback moved into ArduinoCloudThing, test cases completed --- ArduinoCloudProperty.ipp | 115 ++++++++++++++++++++++++++++----------- 1 file changed, 82 insertions(+), 33 deletions(-) diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 5b4b2b120..ecb8c998e 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -22,11 +22,12 @@ template ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, Permission const permission) : _property(property), - _shadow_property(property), + _cloud_shadow_property(property), _local_shadow_property(property), _name(name), _permission(permission), _update_callback_func(NULL), + _sync_callback_func(NULL), _update_policy(UpdatePolicy::OnChange), _has_been_updated_once(false), _has_been_modified_in_callback(false), @@ -34,8 +35,9 @@ ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, _min_time_between_updates_millis(0), _last_updated_millis(0), _update_interval_millis(0), - _local_change_timestamp(0), - _last_change_timestamp(0) + _last_local_change_timestamp(0), + _last_cloud_change_timestamp(0), + _previous_cloud_change_timestamp(0) { } @@ -47,7 +49,7 @@ template void ArduinoCloudProperty::writeByCloud(T const val) { if(isWriteableByCloud()) { _property = val; - /* _shadow_property and local_shadow_property are not updated so there will be an update the next time around */ + /* _cloud_shadow_property and local_shadow_property are not updated so there will be an update the next time around */ } } @@ -57,6 +59,12 @@ ArduinoCloudProperty & ArduinoCloudProperty::onUpdate(UpdateCallbackFunc f return (*this); } +template +ArduinoCloudProperty & ArduinoCloudProperty::onSync(void (*func)(ArduinoCloudProperty property)) { + _sync_callback_func = func; + return (*this); +} + template ArduinoCloudProperty & ArduinoCloudProperty::publishOnChange(T const min_delta_property, unsigned long const min_time_between_updates_millis) { _update_policy = UpdatePolicy::OnChange; @@ -82,7 +90,7 @@ bool ArduinoCloudProperty::shouldBeUpdated() { } if (_update_policy == UpdatePolicy::OnChange) { - return (isValueDifferent(_property, _shadow_property) && ((millis() - _last_updated_millis) >= (_min_time_between_updates_millis))); + return (isValueDifferent(_property, _cloud_shadow_property) && ((millis() - _last_updated_millis) >= (_min_time_between_updates_millis))); } else if(_update_policy == UpdatePolicy::TimeInterval) { return ((millis() - _last_updated_millis) >= _update_interval_millis); @@ -92,27 +100,6 @@ bool ArduinoCloudProperty::shouldBeUpdated() { } } -template -bool ArduinoCloudProperty::isAfterLastChange(unsigned long cloudChangeTime) { - if(!_has_been_updated_once) return true; - - if(_local_change_timestamp > _last_change_timestamp) { - return (cloudChangeTime > _local_change_timestamp); - } else { - return (cloudChangeTime > _last_change_timestamp); - } -} - -template -void ArduinoCloudProperty::setLastChangeTime(unsigned long cloudChangeTime) { - if(_local_change_timestamp > _last_change_timestamp) { - (cloudChangeTime > _local_change_timestamp) ? _last_change_timestamp = cloudChangeTime : _last_change_timestamp = _local_change_timestamp; - } else { - if(cloudChangeTime > _last_change_timestamp) _last_change_timestamp = cloudChangeTime; - } -} - - template void ArduinoCloudProperty::forceCallbackOnChange() { if(_update_callback_func != NULL) { @@ -122,17 +109,24 @@ void ArduinoCloudProperty::forceCallbackOnChange() { template void ArduinoCloudProperty::execCallbackOnChange() { - if(isValueDifferent(_property, _shadow_property)) { + if(isValueDifferent(_property, _cloud_shadow_property)) { if(_update_callback_func != NULL) { _update_callback_func(); } - if(!isValueDifferent(_property, _shadow_property)) { + if(!isValueDifferent(_property, _cloud_shadow_property)) { _has_been_modified_in_callback = true; } } } +template +void ArduinoCloudProperty::execCallbackOnSync() { + if(_sync_callback_func != NULL) { + _sync_callback_func(*this); + } +} + template void ArduinoCloudProperty::append(CborEncoder * encoder) { if (isReadableByCloud()) { @@ -144,8 +138,8 @@ void ArduinoCloudProperty::append(CborEncoder * encoder) { appendValue (&mapEncoder); cbor_encoder_close_container(encoder, &mapEncoder); - _shadow_property = _property; - _local_shadow_property = _property; + _cloud_shadow_property = _property; + _has_been_updated_once = true; _last_updated_millis = millis(); } @@ -160,17 +154,72 @@ template void ArduinoCloudProperty::updateTime(unsigned long changeEventTime) { if (isReadableByCloud()) { _local_shadow_property = _property; - _local_change_timestamp = changeEventTime; + _last_local_change_timestamp = changeEventTime; + } +} + +template +void ArduinoCloudProperty::setPropertyValue(T const val) { + if(isWriteableByCloud()) { + _property = val; } } template -void ArduinoCloudProperty::setShadowValue(T const val) { +void ArduinoCloudProperty::setCloudShadowValue(T const val) { if(isWriteableByCloud()) { - _shadow_property = val; + _cloud_shadow_property = val; } } +template +void ArduinoCloudProperty::setLocalShadowValue(T const val) { + if(isWriteableByCloud()) { + _local_shadow_property = val; + } +} + +template +T ArduinoCloudProperty::getCloudShadowValue() { + return _cloud_shadow_property; +} + +template +T ArduinoCloudProperty::getLocalShadowValue() { + return _local_shadow_property; +} + +template +void ArduinoCloudProperty::setPreviousCloudChangeTimestamp(unsigned long cloudChangeEventTime) { + _previous_cloud_change_timestamp = cloudChangeEventTime; +} + +template +void ArduinoCloudProperty::setLastCloudChangeTimestamp(unsigned long cloudChangeEventTime) { + _previous_cloud_change_timestamp = _last_cloud_change_timestamp; + _last_cloud_change_timestamp = cloudChangeEventTime; +} + +template +void ArduinoCloudProperty::setLastLocalChangeTimestamp(unsigned long localChangeEventTime) { + _last_local_change_timestamp = localChangeEventTime; +} + +template +unsigned long ArduinoCloudProperty::getPreviousCloudChangeTimestamp() { + return _previous_cloud_change_timestamp; +} + +template +unsigned long ArduinoCloudProperty::getLastCloudChangeTimestamp() { + return _last_cloud_change_timestamp; +} + +template +unsigned long ArduinoCloudProperty::getLastLocalChangeTimestamp() { + return _last_local_change_timestamp; +} + /****************************************************************************** * PRIVATE MEMBER FUNCTIONS From 0f0b1a40700fa678977ce653351471cdf88ac48d Mon Sep 17 00:00:00 2001 From: ilcato Date: Wed, 20 Feb 2019 09:53:35 +0100 Subject: [PATCH 120/175] Fixed cbor decod for negative ints. Added specific unit test. --- ArduinoCloudThing.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 7235c946d..dfedfdf8d 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -497,8 +497,8 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * bool ArduinoCloudThing::ifNumericConvertToDouble(CborValue * value_iter, double * numeric_val) { if(cbor_value_is_integer(value_iter)) { - uint64_t val = 0; - if(cbor_value_get_uint64(value_iter, &val) == CborNoError) { + int64_t val = 0; + if(cbor_value_get_int64(value_iter, &val) == CborNoError) { *numeric_val = static_cast(val); return true; } From e3146210dcbf89e9868c5efef8f88b0846f4e3e9 Mon Sep 17 00:00:00 2001 From: ilcato Date: Wed, 20 Feb 2019 10:08:30 +0100 Subject: [PATCH 121/175] Refactoring reset map data --- ArduinoCloudThing.cpp | 10 +--------- ArduinoCloudThing.h | 9 ++++++++- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index dfedfdf8d..fa42b5ae4 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -214,7 +214,7 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_EnterMap(CborValue * if(cbor_value_get_type(map_iter) == CborMapType) { if(cbor_value_enter_container(map_iter, value_iter) == CborNoError) { - resetMapDataNotBase(map_data); + map_data->resetNotBase(); next_state = MapParserState::MapKey; } } @@ -528,14 +528,6 @@ bool ArduinoCloudThing::ifNumericConvertToDouble(CborValue * value_iter, double return false; } -void ArduinoCloudThing::resetMapDataNotBase(CborMapData * map_data) { - map_data->name.reset (); - map_data->val.reset (); - map_data->str_val.reset (); - map_data->bool_val.reset (); - map_data->time.reset (); -} - /* Source Idea from https://tools.ietf.org/html/rfc7049 : Page: 50 */ double ArduinoCloudThing::convertCborHalfFloatToDouble(uint16_t const half_val) { int exp = (half_val >> 10) & 0x1f; diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 93835348f..0d1feb5db 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -144,6 +144,14 @@ class ArduinoCloudThing { MapEntry str_val; MapEntry bool_val; MapEntry time; + + void resetNotBase() { + name.reset (); + val.reset (); + str_val.reset (); + bool_val.reset (); + time.reset (); + } }; MapParserState handle_EnterMap (CborValue * map_iter, CborValue * value_iter, CborMapData * map_data); @@ -159,7 +167,6 @@ class ArduinoCloudThing { MapParserState handle_Time (CborValue * value_iter, CborMapData * map_data); MapParserState handle_LeaveMap (CborValue * map_iter, CborValue * value_iter, CborMapData const * const map_data); - static void resetMapDataNotBase (CborMapData * map_data); static bool ifNumericConvertToDouble (CborValue * value_iter, double * numeric_val); static double convertCborHalfFloatToDouble(uint16_t const half_val); From 8272275423b2ba041655216b9f9f3d9d1f33e066 Mon Sep 17 00:00:00 2001 From: ilcato Date: Thu, 21 Feb 2019 18:02:17 +0100 Subject: [PATCH 122/175] Reanme of the standard sync callbacks. --- ArduinoCloudThing.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 0d1feb5db..115edd51e 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -44,7 +44,7 @@ static long const DAYS = 86400; * SYNCHRONIZATION CALLBACKS ******************************************************************************/ -#define AUTO_SYNC onAutoSync +#define MOST_RECENT_WINS onAutoSync template void onAutoSync(ArduinoCloudProperty property) { if( property.getLastCloudChangeTimestamp() > property.getLastLocalChangeTimestamp()){ @@ -53,14 +53,14 @@ void onAutoSync(ArduinoCloudProperty property) { } } -#define FORCE_CLOUD_SYNC onForceCloudSync +#define CLOUD_WINS onForceCloudSync template void onForceCloudSync(ArduinoCloudProperty property) { property.setPropertyValue(property.getCloudShadowValue()); property.forceCallbackOnChange(); } -#define FORCE_DEVICE_SYNC onForceDeviceSync // The device property value is already the correct one. The cloud property value will be synchronized at the next update cycle. +#define DEVICE_WINS onForceDeviceSync // The device property value is already the correct one. The cloud property value will be synchronized at the next update cycle. template void onForceDeviceSync(ArduinoCloudProperty property) { } From 335c16e8cfda5a4eef5f0e1c76d5dae2191ab3de Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 25 Feb 2019 06:23:06 +0100 Subject: [PATCH 123/175] Incorporating changes suggested by @per1234. --- ArduinoCloudProperty.hpp | 2 +- ArduinoCloudProperty.ipp | 3 +-- ArduinoCloudThing.cpp | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 5e28478fb..3b94fd82b 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -121,7 +121,7 @@ class ArduinoCloudProperty { UpdatePolicy _update_policy; bool _has_been_updated_once, - _has_been_modified_in_callback; + _has_been_modified_in_callback; /* Variables used for UpdatePolicy::OnChange */ T _min_delta_property; unsigned long _min_time_between_updates_millis; diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index ecb8c998e..0603683c6 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -113,7 +113,6 @@ void ArduinoCloudProperty::execCallbackOnChange() { if(_update_callback_func != NULL) { _update_callback_func(); } - if(!isValueDifferent(_property, _cloud_shadow_property)) { _has_been_modified_in_callback = true; } @@ -287,4 +286,4 @@ inline float ArduinoCloudProperty::getInitialMinDeltaPropertyValue() cons template <> inline String ArduinoCloudProperty::getInitialMinDeltaPropertyValue() const { return String(""); -} \ No newline at end of file +} diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index fa42b5ae4..60c9c9715 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -409,7 +409,7 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * val ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * map_iter, CborValue * value_iter, CborMapData const * const map_data) { MapParserState next_state = MapParserState::Error; - //computer the cloud event change time + //compute the cloud event change time unsigned long cloudChangeEventTime = 0; if(map_data->base_time.isSet()){ cloudChangeEventTime = (unsigned long)(map_data->base_time.get()); @@ -427,7 +427,7 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * ArduinoCloudProperty * float_property = _property_cont.getPropertyFloat(map_data->name.get()); if(int_property && int_property->isWriteableByCloud()) { - if(_syncMessage){ + if(_syncMessage) { int_property->setLastCloudChangeTimestamp(cloudChangeEventTime); int_property->setCloudShadowValue(map_data->val.get()); int_property->execCallbackOnSync(); From 230f66df9f215c1649fbfcad8a88c7e07970eed6 Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 26 Feb 2019 06:51:21 +0100 Subject: [PATCH 124/175] Apply suggestions from @per1234 code review Co-Authored-By: ilcato --- ArduinoCloudThing.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 60c9c9715..0ee0e4adb 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -429,7 +429,7 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * if(int_property && int_property->isWriteableByCloud()) { if(_syncMessage) { int_property->setLastCloudChangeTimestamp(cloudChangeEventTime); - int_property->setCloudShadowValue(map_data->val.get()); + int_property->setCloudShadowValue(map_data->val.get()); int_property->execCallbackOnSync(); } else { int_property->writeByCloud(map_data->val.get()); @@ -438,9 +438,9 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * } if(float_property && float_property->isWriteableByCloud()) { - if(_syncMessage){ + if(_syncMessage) { float_property->setLastCloudChangeTimestamp(cloudChangeEventTime); - float_property->setCloudShadowValue(map_data->val.get()); + float_property->setCloudShadowValue(map_data->val.get()); float_property->execCallbackOnSync(); } else { float_property->writeByCloud(map_data->val.get()); @@ -453,9 +453,9 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * if(map_data->str_val.isSet()) { ArduinoCloudProperty* string_property = _property_cont.getPropertyString(map_data->name.get()); if(string_property && string_property->isWriteableByCloud()) { - if(_syncMessage){ + if(_syncMessage) { string_property->setLastCloudChangeTimestamp(cloudChangeEventTime); - string_property->setCloudShadowValue(map_data->str_val.get()); + string_property->setCloudShadowValue(map_data->str_val.get()); string_property->execCallbackOnSync(); } else { string_property->writeByCloud(map_data->str_val.get()); @@ -468,9 +468,9 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * if(map_data->bool_val.isSet()) { ArduinoCloudProperty* bool_property = _property_cont.getPropertyBool(map_data->name.get()); if(bool_property && bool_property->isWriteableByCloud()) { - if(_syncMessage){ + if(_syncMessage) { bool_property->setLastCloudChangeTimestamp(cloudChangeEventTime); - bool_property->setCloudShadowValue(map_data->bool_val.get()); + bool_property->setCloudShadowValue(map_data->bool_val.get()); bool_property->execCallbackOnSync(); } else { bool_property->writeByCloud(map_data->bool_val.get()); From c2d041e816668b96d6a0dafebbdaaf1e3c261744 Mon Sep 17 00:00:00 2001 From: ilcato Date: Fri, 22 Mar 2019 12:33:22 +0100 Subject: [PATCH 125/175] Complete removal of SenML V1 protocol (string based keys) --- ArduinoCloudThing.cpp | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 0ee0e4adb..0730ca205 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -228,27 +228,7 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * v if(cbor_value_at_end(value_iter)) { next_state = MapParserState::LeaveMap; } - /* If the key is a string means that the Map use the string keys (protocol V1) - * https://tools.ietf.org/html/rfc8428#section-4.3 - * Example [{"n": "temperature", "v": 25}] - */ - else if(cbor_value_is_text_string(value_iter)) { - char * val = 0; - size_t val_size = 0; - if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { - if (strcmp(val, "n" ) == 0) next_state = MapParserState::Name; - else if(strcmp(val, "bver") == 0) next_state = MapParserState::BaseVersion; - else if(strcmp(val, "bn" ) == 0) next_state = MapParserState::BaseName; - else if(strcmp(val, "bt" ) == 0) next_state = MapParserState::BaseTime; - else if(strcmp(val, "v" ) == 0) next_state = MapParserState::Value; - else if(strcmp(val, "vs" ) == 0) next_state = MapParserState::StringValue; - else if(strcmp(val, "vb" ) == 0) next_state = MapParserState::BooleanValue; - else if(strcmp(val, "t" ) == 0) next_state = MapParserState::Time; - else next_state = MapParserState::UndefinedKey; - free(val); - } - } - /* If the key is a number means that the Map use the CBOR Label (protocol V2) + /* The Map use the CBOR Label (protocol V2) * Example [{0: "temperature", 2: 25}] */ else if (cbor_value_is_integer(value_iter)) { From bc34d243cda8e5c9f1ca2bf0792bf73609bbcd1e Mon Sep 17 00:00:00 2001 From: ilcato Date: Mon, 25 Mar 2019 17:22:51 +0100 Subject: [PATCH 126/175] Removed unused internal state property --- ArduinoCloudThing.cpp | 2 -- ArduinoCloudThing.h | 1 - 2 files changed, 3 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 0730ca205..fa4ce058c 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -76,8 +76,6 @@ ArduinoCloudThing::ArduinoCloudThing() { ******************************************************************************/ void ArduinoCloudThing::begin() { - _status = ON; - addPropertyReal(_status, "status", Permission::Read); } int ArduinoCloudThing::updateTimestampOnChangedProperties(unsigned long changeEventTime) { diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 115edd51e..c35921c1d 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -92,7 +92,6 @@ class ArduinoCloudThing { private: - bool _status = OFF; char _uuid[33]; ArduinoCloudPropertyContainer _property_cont; bool _syncMessage; From 31ef2910c2dc4c38b9df8d38134176f31a177d43 Mon Sep 17 00:00:00 2001 From: per1234 Date: Mon, 8 Apr 2019 22:08:55 -0700 Subject: [PATCH 127/175] Auto format code The Artistic Style code formatter tool was used with Arduino's example_formatter.conf configuration file: https://github.com/arduino/Arduino/blob/master/build/shared/examples_formatter.conf --- ArduinoCloudProperty.hpp | 185 ++++++++++---------- ArduinoCloudProperty.ipp | 91 +++++----- ArduinoCloudPropertyContainer.cpp | 30 ++-- ArduinoCloudPropertyContainer.hpp | 98 ++++++----- ArduinoCloudPropertyContainer.ipp | 10 +- ArduinoCloudThing.cpp | 282 +++++++++++++++--------------- ArduinoCloudThing.h | 199 +++++++++++---------- 7 files changed, 471 insertions(+), 424 deletions(-) diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 3b94fd82b..0505d65cc 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -19,7 +19,7 @@ #define ARDUINO_CLOUD_PROPERTY_HPP_ /****************************************************************************** - * INCLUDE + INCLUDE ******************************************************************************/ #include @@ -27,11 +27,11 @@ #include "lib/tinycbor/cbor-lib.h" /****************************************************************************** - * TYPEDEF + TYPEDEF ******************************************************************************/ /****************************************************************************** - * TYPEDEF + TYPEDEF ******************************************************************************/ enum class Permission { @@ -47,110 +47,117 @@ enum class UpdatePolicy { }; /* Source: https://tools.ietf.org/html/rfc8428#section-6 */ -enum class CborIntegerMapKey : int -{ - BaseVersion = -1, /* bver */ - BaseName = -2, /* bn */ - BaseTime = -3, /* bt */ - BaseUnit = -4, /* bu */ - BaseValue = -5, /* bv */ - BaseSum = -6, /* bs */ - Name = 0, /* n */ - Unit = 1, /* u */ - Value = 2, /* v */ - StringValue = 3, /* vs */ - BooleanValue = 4, /* vb */ - Sum = 5, /* s */ - Time = 6, /* t */ - UpdateTime = 7, /* ut */ - DataValue = 8 /* vd */ +enum class CborIntegerMapKey : int { + BaseVersion = -1, /* bver */ + BaseName = -2, /* bn */ + BaseTime = -3, /* bt */ + BaseUnit = -4, /* bu */ + BaseValue = -5, /* bv */ + BaseSum = -6, /* bs */ + Name = 0, /* n */ + Unit = 1, /* u */ + Value = 2, /* v */ + StringValue = 3, /* vs */ + BooleanValue = 4, /* vb */ + Sum = 5, /* s */ + Time = 6, /* t */ + UpdateTime = 7, /* ut */ + DataValue = 8 /* vd */ }; typedef void(*UpdateCallbackFunc)(void); /****************************************************************************** - * CLASS DECLARATION + CLASS DECLARATION ******************************************************************************/ template class ArduinoCloudProperty { -public: - - ArduinoCloudProperty(T & property, String const & name, Permission const permission); - - void writeByCloud(T const val); - - /* Composable configuration of the ArduinoCloudProperty class */ - ArduinoCloudProperty & onUpdate (UpdateCallbackFunc func); - ArduinoCloudProperty & onSync (void (*func)(ArduinoCloudProperty property)); - ArduinoCloudProperty & publishOnChange(T const min_delta_property, unsigned long const min_time_between_updates_millis = 0); - ArduinoCloudProperty & publishEvery (unsigned long const seconds); - - inline String name () const { return _name; } - inline bool isReadableByCloud () const { return (_permission == Permission::Read ) || (_permission == Permission::ReadWrite); } - inline bool isWriteableByCloud() const { return (_permission == Permission::Write) || (_permission == Permission::ReadWrite); } - - bool shouldBeUpdated (); - void execCallbackOnChange (); - void execCallbackOnSync (); - void forceCallbackOnChange (); - void setPreviousCloudChangeTimestamp (unsigned long cloudChangeTime); - void setLastCloudChangeTimestamp (unsigned long cloudChangeTime); - void setLastLocalChangeTimestamp (unsigned long localChangeTime); - unsigned long getPreviousCloudChangeTimestamp(); - unsigned long getLastCloudChangeTimestamp(); - unsigned long getLastLocalChangeTimestamp(); - void setPropertyValue (T const val); - void setCloudShadowValue (T const val); - T getCloudShadowValue (); - void setLocalShadowValue (T const val); - T getLocalShadowValue (); - void updateTime (unsigned long changeEventTime); - bool isChangedLocally (); - void append (CborEncoder * encoder); - -private: - - T & _property, - _cloud_shadow_property, - _local_shadow_property; - String _name; - Permission _permission; - UpdateCallbackFunc _update_callback_func; - void (*_sync_callback_func)(ArduinoCloudProperty property); - - UpdatePolicy _update_policy; - bool _has_been_updated_once, - _has_been_modified_in_callback; - /* Variables used for UpdatePolicy::OnChange */ - T _min_delta_property; - unsigned long _min_time_between_updates_millis; - /* Variables used for UpdatePolicy::TimeInterval */ - unsigned long _last_updated_millis, - _update_interval_millis; - /* Variables used for reconnection sync*/ - unsigned long _last_change_timestamp; - unsigned long _last_local_change_timestamp; - unsigned long _last_cloud_change_timestamp; - unsigned long _previous_cloud_change_timestamp; - - - void appendValue(CborEncoder * mapEncoder) const; - bool isValueDifferent(T const lhs, T const rhs) const; - - T getInitialMinDeltaPropertyValue() const; + public: + + ArduinoCloudProperty(T & property, String const & name, Permission const permission); + + void writeByCloud(T const val); + + /* Composable configuration of the ArduinoCloudProperty class */ + ArduinoCloudProperty & onUpdate(UpdateCallbackFunc func); + ArduinoCloudProperty & onSync(void (*func)(ArduinoCloudProperty property)); + ArduinoCloudProperty & publishOnChange(T const min_delta_property, unsigned long const min_time_between_updates_millis = 0); + ArduinoCloudProperty & publishEvery(unsigned long const seconds); + + inline String name() const { + return _name; + } + inline bool isReadableByCloud() const { + return (_permission == Permission::Read) || (_permission == Permission::ReadWrite); + } + inline bool isWriteableByCloud() const { + return (_permission == Permission::Write) || (_permission == Permission::ReadWrite); + } + + bool shouldBeUpdated(); + void execCallbackOnChange(); + void execCallbackOnSync(); + void forceCallbackOnChange(); + void setPreviousCloudChangeTimestamp(unsigned long cloudChangeTime); + void setLastCloudChangeTimestamp(unsigned long cloudChangeTime); + void setLastLocalChangeTimestamp(unsigned long localChangeTime); + unsigned long getPreviousCloudChangeTimestamp(); + unsigned long getLastCloudChangeTimestamp(); + unsigned long getLastLocalChangeTimestamp(); + void setPropertyValue(T const val); + void setCloudShadowValue(T const val); + T getCloudShadowValue(); + void setLocalShadowValue(T const val); + T getLocalShadowValue(); + void updateTime(unsigned long changeEventTime); + bool isChangedLocally(); + void append(CborEncoder * encoder); + + private: + + T & _property, + _cloud_shadow_property, + _local_shadow_property; + String _name; + Permission _permission; + UpdateCallbackFunc _update_callback_func; + void (*_sync_callback_func)(ArduinoCloudProperty property); + + UpdatePolicy _update_policy; + bool _has_been_updated_once, + _has_been_modified_in_callback; + /* Variables used for UpdatePolicy::OnChange */ + T _min_delta_property; + unsigned long _min_time_between_updates_millis; + /* Variables used for UpdatePolicy::TimeInterval */ + unsigned long _last_updated_millis, + _update_interval_millis; + /* Variables used for reconnection sync*/ + unsigned long _last_change_timestamp; + unsigned long _last_local_change_timestamp; + unsigned long _last_cloud_change_timestamp; + unsigned long _previous_cloud_change_timestamp; + + + void appendValue(CborEncoder * mapEncoder) const; + bool isValueDifferent(T const lhs, T const rhs) const; + + T getInitialMinDeltaPropertyValue() const; }; /****************************************************************************** - * PROTOTYPE FREE FUNCTIONs + PROTOTYPE FREE FUNCTIONs ******************************************************************************/ template -inline bool operator == (ArduinoCloudProperty const & lhs, ArduinoCloudProperty const & rhs) { return (lhs.name() == rhs.name()); } +inline bool operator == (ArduinoCloudProperty const & lhs, ArduinoCloudProperty const & rhs) { + return (lhs.name() == rhs.name()); +} /****************************************************************************** - * TEMPLATE IMPLEMENTATION + TEMPLATE IMPLEMENTATION ******************************************************************************/ #include "ArduinoCloudProperty.ipp" diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp index 0603683c6..80981a987 100644 --- a/ArduinoCloudProperty.ipp +++ b/ArduinoCloudProperty.ipp @@ -16,38 +16,37 @@ // /****************************************************************************** - * CTOR/DTOR + CTOR/DTOR ******************************************************************************/ template ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, Permission const permission) -: _property(property), - _cloud_shadow_property(property), - _local_shadow_property(property), - _name(name), - _permission(permission), - _update_callback_func(NULL), - _sync_callback_func(NULL), - _update_policy(UpdatePolicy::OnChange), - _has_been_updated_once(false), - _has_been_modified_in_callback(false), - _min_delta_property(getInitialMinDeltaPropertyValue()), - _min_time_between_updates_millis(0), - _last_updated_millis(0), - _update_interval_millis(0), - _last_local_change_timestamp(0), - _last_cloud_change_timestamp(0), - _previous_cloud_change_timestamp(0) -{ + : _property(property), + _cloud_shadow_property(property), + _local_shadow_property(property), + _name(name), + _permission(permission), + _update_callback_func(NULL), + _sync_callback_func(NULL), + _update_policy(UpdatePolicy::OnChange), + _has_been_updated_once(false), + _has_been_modified_in_callback(false), + _min_delta_property(getInitialMinDeltaPropertyValue()), + _min_time_between_updates_millis(0), + _last_updated_millis(0), + _update_interval_millis(0), + _last_local_change_timestamp(0), + _last_cloud_change_timestamp(0), + _previous_cloud_change_timestamp(0) { } /****************************************************************************** - * PUBLIC MEMBER FUNCTIONS + PUBLIC MEMBER FUNCTIONS ******************************************************************************/ template void ArduinoCloudProperty::writeByCloud(T const val) { - if(isWriteableByCloud()) { + if (isWriteableByCloud()) { _property = val; /* _cloud_shadow_property and local_shadow_property are not updated so there will be an update the next time around */ } @@ -82,38 +81,38 @@ ArduinoCloudProperty & ArduinoCloudProperty::publishEvery(unsigned long co template bool ArduinoCloudProperty::shouldBeUpdated() { - if(!_has_been_updated_once) return true; + if (!_has_been_updated_once) { + return true; + } - if(_has_been_modified_in_callback) { + if (_has_been_modified_in_callback) { _has_been_modified_in_callback = false; return true; } - if (_update_policy == UpdatePolicy::OnChange) { + if (_update_policy == UpdatePolicy::OnChange) { return (isValueDifferent(_property, _cloud_shadow_property) && ((millis() - _last_updated_millis) >= (_min_time_between_updates_millis))); - } - else if(_update_policy == UpdatePolicy::TimeInterval) { + } else if (_update_policy == UpdatePolicy::TimeInterval) { return ((millis() - _last_updated_millis) >= _update_interval_millis); - } - else { + } else { return false; } } template void ArduinoCloudProperty::forceCallbackOnChange() { - if(_update_callback_func != NULL) { + if (_update_callback_func != NULL) { _update_callback_func(); } } template void ArduinoCloudProperty::execCallbackOnChange() { - if(isValueDifferent(_property, _cloud_shadow_property)) { - if(_update_callback_func != NULL) { + if (isValueDifferent(_property, _cloud_shadow_property)) { + if (_update_callback_func != NULL) { _update_callback_func(); } - if(!isValueDifferent(_property, _cloud_shadow_property)) { + if (!isValueDifferent(_property, _cloud_shadow_property)) { _has_been_modified_in_callback = true; } } @@ -121,7 +120,7 @@ void ArduinoCloudProperty::execCallbackOnChange() { template void ArduinoCloudProperty::execCallbackOnSync() { - if(_sync_callback_func != NULL) { + if (_sync_callback_func != NULL) { _sync_callback_func(*this); } } @@ -131,14 +130,14 @@ void ArduinoCloudProperty::append(CborEncoder * encoder) { if (isReadableByCloud()) { CborEncoder mapEncoder; - cbor_encoder_create_map (encoder, &mapEncoder, CborIndefiniteLength); - cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::Name)); - cbor_encode_text_stringz (&mapEncoder, _name.c_str()); - appendValue (&mapEncoder); + cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); + cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Name)); + cbor_encode_text_stringz(&mapEncoder, _name.c_str()); + appendValue(&mapEncoder); cbor_encoder_close_container(encoder, &mapEncoder); _cloud_shadow_property = _property; - + _has_been_updated_once = true; _last_updated_millis = millis(); } @@ -159,21 +158,21 @@ void ArduinoCloudProperty::updateTime(unsigned long changeEventTime) { template void ArduinoCloudProperty::setPropertyValue(T const val) { - if(isWriteableByCloud()) { + if (isWriteableByCloud()) { _property = val; } } template void ArduinoCloudProperty::setCloudShadowValue(T const val) { - if(isWriteableByCloud()) { + if (isWriteableByCloud()) { _cloud_shadow_property = val; } } template void ArduinoCloudProperty::setLocalShadowValue(T const val) { - if(isWriteableByCloud()) { + if (isWriteableByCloud()) { _local_shadow_property = val; } } @@ -221,30 +220,30 @@ unsigned long ArduinoCloudProperty::getLastLocalChangeTimestamp() { /****************************************************************************** - * PRIVATE MEMBER FUNCTIONS + PRIVATE MEMBER FUNCTIONS ******************************************************************************/ template <> inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); + cbor_encode_int(mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); cbor_encode_boolean(mapEncoder, _property); } template <> inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int(mapEncoder, static_cast(CborIntegerMapKey::Value)); + cbor_encode_int(mapEncoder, static_cast(CborIntegerMapKey::Value)); cbor_encode_int(mapEncoder, _property); } template <> inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); + cbor_encode_int(mapEncoder, static_cast(CborIntegerMapKey::Value)); cbor_encode_float(mapEncoder, _property); } template <> inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::StringValue)); + cbor_encode_int(mapEncoder, static_cast(CborIntegerMapKey::StringValue)); cbor_encode_text_stringz(mapEncoder, _property.c_str()); } diff --git a/ArduinoCloudPropertyContainer.cpp b/ArduinoCloudPropertyContainer.cpp index e1fd04a2b..2c4682c66 100644 --- a/ArduinoCloudPropertyContainer.cpp +++ b/ArduinoCloudPropertyContainer.cpp @@ -16,37 +16,43 @@ // /****************************************************************************** - * INCLUDE + INCLUDE ******************************************************************************/ #include "ArduinoCloudPropertyContainer.hpp" /****************************************************************************** - * PUBLIC MEMBER FUNCTIONS + PUBLIC MEMBER FUNCTIONS ******************************************************************************/ bool ArduinoCloudPropertyContainer::isPropertyInContainer(Type const type, String const & name) { - if (type == Type::Bool ) return isPropertyInList(_bool_property_list, name); - else if (type == Type::Int ) return isPropertyInList(_int_property_list, name); - else if (type == Type::Float ) return isPropertyInList(_float_property_list, name); - else if (type == Type::String) return isPropertyInList(_string_property_list, name); - else return false; + if (type == Type::Bool) { + return isPropertyInList(_bool_property_list, name); + } else if (type == Type::Int) { + return isPropertyInList(_int_property_list, name); + } else if (type == Type::Float) { + return isPropertyInList(_float_property_list, name); + } else if (type == Type::String) { + return isPropertyInList(_string_property_list, name); + } else { + return false; + } } int ArduinoCloudPropertyContainer::getNumOfChangedProperties() { int num_changes_properties = 0; - num_changes_properties += getNumOfChangedProperties(_bool_property_list ); - num_changes_properties += getNumOfChangedProperties(_int_property_list ); - num_changes_properties += getNumOfChangedProperties(_float_property_list ); + num_changes_properties += getNumOfChangedProperties(_bool_property_list); + num_changes_properties += getNumOfChangedProperties(_int_property_list); + num_changes_properties += getNumOfChangedProperties(_float_property_list); num_changes_properties += getNumOfChangedProperties(_string_property_list); return num_changes_properties; } void ArduinoCloudPropertyContainer::appendChangedProperties(CborEncoder * arrayEncoder) { - appendChangedProperties (_bool_property_list, arrayEncoder); - appendChangedProperties (_int_property_list, arrayEncoder); + appendChangedProperties (_bool_property_list, arrayEncoder); + appendChangedProperties (_int_property_list, arrayEncoder); appendChangedProperties (_float_property_list, arrayEncoder); appendChangedProperties(_string_property_list, arrayEncoder); } diff --git a/ArduinoCloudPropertyContainer.hpp b/ArduinoCloudPropertyContainer.hpp index 36f2b566c..6dc02a185 100644 --- a/ArduinoCloudPropertyContainer.hpp +++ b/ArduinoCloudPropertyContainer.hpp @@ -19,7 +19,7 @@ #define ARDUINO_CLOUD_PROPERTY_CONTAINER_NEW_H_ /****************************************************************************** - * INCLUDE + INCLUDE ******************************************************************************/ #include "ArduinoCloudProperty.hpp" @@ -28,53 +28,69 @@ #include "lib/LinkedList/LinkedList.h" /****************************************************************************** - * CLASS DECLARATION + CLASS DECLARATION ******************************************************************************/ class ArduinoCloudPropertyContainer { -public: - - bool isPropertyInContainer (Type const type, String const & name); - int getNumOfChangedProperties(); - int updateTimestampOnChangedProperties(unsigned long changeEventTime); - void appendChangedProperties (CborEncoder * arrayEncoder); - - inline ArduinoCloudProperty * getPropertyBool (String const & name) { return getProperty(_bool_property_list, name); } - inline ArduinoCloudProperty * getPropertyInt (String const & name) { return getProperty(_int_property_list, name); } - inline ArduinoCloudProperty * getPropertyFloat (String const & name) { return getProperty(_float_property_list, name); } - inline ArduinoCloudProperty * getPropertyString(String const & name) { return getProperty(_string_property_list, name); } - - inline void addProperty(ArduinoCloudProperty * property_obj) { _bool_property_list.add (property_obj); } - inline void addProperty(ArduinoCloudProperty * property_obj) { _int_property_list.add (property_obj); } - inline void addProperty(ArduinoCloudProperty * property_obj) { _float_property_list.add (property_obj); } - inline void addProperty(ArduinoCloudProperty * property_obj) { _string_property_list.add(property_obj); } - -private: - - LinkedList *> _bool_property_list; - LinkedList *> _int_property_list; - LinkedList *> _float_property_list; - LinkedList *> _string_property_list; - - template - bool isPropertyInList(LinkedList *> & list, String const & name); - - template - ArduinoCloudProperty * getProperty(LinkedList *> & list, String const & name); - - template - int getNumOfChangedProperties(LinkedList *> & list); - - template - int updateTimestampOnChangedProperties(LinkedList *> & list, unsigned long changeEventTime); - - template - void appendChangedProperties(LinkedList *> & list, CborEncoder * arrayEncoder); + public: + + bool isPropertyInContainer(Type const type, String const & name); + int getNumOfChangedProperties(); + int updateTimestampOnChangedProperties(unsigned long changeEventTime); + void appendChangedProperties(CborEncoder * arrayEncoder); + + inline ArduinoCloudProperty * getPropertyBool(String const & name) { + return getProperty(_bool_property_list, name); + } + inline ArduinoCloudProperty * getPropertyInt(String const & name) { + return getProperty(_int_property_list, name); + } + inline ArduinoCloudProperty * getPropertyFloat(String const & name) { + return getProperty(_float_property_list, name); + } + inline ArduinoCloudProperty * getPropertyString(String const & name) { + return getProperty(_string_property_list, name); + } + + inline void addProperty(ArduinoCloudProperty * property_obj) { + _bool_property_list.add(property_obj); + } + inline void addProperty(ArduinoCloudProperty * property_obj) { + _int_property_list.add(property_obj); + } + inline void addProperty(ArduinoCloudProperty * property_obj) { + _float_property_list.add(property_obj); + } + inline void addProperty(ArduinoCloudProperty * property_obj) { + _string_property_list.add(property_obj); + } + + private: + + LinkedList *> _bool_property_list; + LinkedList *> _int_property_list; + LinkedList *> _float_property_list; + LinkedList *> _string_property_list; + + template + bool isPropertyInList(LinkedList *> & list, String const & name); + + template + ArduinoCloudProperty * getProperty(LinkedList *> & list, String const & name); + + template + int getNumOfChangedProperties(LinkedList *> & list); + + template + int updateTimestampOnChangedProperties(LinkedList *> & list, unsigned long changeEventTime); + + template + void appendChangedProperties(LinkedList *> & list, CborEncoder * arrayEncoder); }; /****************************************************************************** - * TEMPLATE IMPLEMENTATION + TEMPLATE IMPLEMENTATION ******************************************************************************/ #include "ArduinoCloudPropertyContainer.ipp" diff --git a/ArduinoCloudPropertyContainer.ipp b/ArduinoCloudPropertyContainer.ipp index 7b469a673..e5026281e 100644 --- a/ArduinoCloudPropertyContainer.ipp +++ b/ArduinoCloudPropertyContainer.ipp @@ -16,14 +16,16 @@ // /****************************************************************************** - * PRIVATE MEMBER FUNCTIONS + PRIVATE MEMBER FUNCTIONS ******************************************************************************/ template bool ArduinoCloudPropertyContainer::isPropertyInList(LinkedList *> & list, String const & name) { for (int i = 0; i < list.size(); i++) { ArduinoCloudProperty * p = list.get(i); - if (p->name() == name) return true; + if (p->name() == name) { + return true; + } } return false; } @@ -32,7 +34,9 @@ template ArduinoCloudProperty * ArduinoCloudPropertyContainer::getProperty(LinkedList *> & list, String const & name) { for (int i = 0; i < list.size(); i++) { ArduinoCloudProperty * p = list.get(i); - if (p->name() == name) return p; + if (p->name() == name) { + return p; + } } return 0; } diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index fa4ce058c..b9b9f71df 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -16,7 +16,7 @@ // /****************************************************************************** - * INCLUDE + INCLUDE ******************************************************************************/ #include @@ -26,53 +26,52 @@ #include /****************************************************************************** - * DEBUG FUNCTIONS + DEBUG FUNCTIONS ******************************************************************************/ #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) extern "C" char *sbrk(int i); -void PrintFreeRam (void) -{ - char stack_dummy = 0; - Serial.print("Free RAM: "); Serial.println(&stack_dummy - sbrk(0)); +void PrintFreeRam(void) { + char stack_dummy = 0; + Serial.print("Free RAM: "); Serial.println(&stack_dummy - sbrk(0)); } #endif #ifdef ARDUINO_ARCH_SAMD static void utox8(uint32_t val, char* s) { - for (int i = 0; i < 8; i++) { - int d = val & 0XF; - val = (val >> 4); - s[7 - i] = d > 9 ? 'A' + d - 10 : '0' + d; - } + for (int i = 0; i < 8; i++) { + int d = val & 0XF; + val = (val >> 4); + s[7 - i] = d > 9 ? 'A' + d - 10 : '0' + d; + } } #endif #ifdef ARDUINO_ARCH_MRAA -#define Serial DebugSerial + #define Serial DebugSerial #endif /****************************************************************************** - * CTOR/DTOR + CTOR/DTOR ******************************************************************************/ ArduinoCloudThing::ArduinoCloudThing() { -#ifdef ARDUINO_ARCH_SAMD - #define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x0080A00C) - #define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x0080A040) - #define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x0080A044) - #define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x0080A048) - - utox8(SERIAL_NUMBER_WORD_0, &_uuid[0]); - utox8(SERIAL_NUMBER_WORD_1, &_uuid[8]); - utox8(SERIAL_NUMBER_WORD_2, &_uuid[16]); - utox8(SERIAL_NUMBER_WORD_3, &_uuid[24]); - _uuid[32] = '\0'; -#endif + #ifdef ARDUINO_ARCH_SAMD +#define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x0080A00C) +#define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x0080A040) +#define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x0080A044) +#define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x0080A048) + + utox8(SERIAL_NUMBER_WORD_0, &_uuid[0]); + utox8(SERIAL_NUMBER_WORD_1, &_uuid[8]); + utox8(SERIAL_NUMBER_WORD_2, &_uuid[16]); + utox8(SERIAL_NUMBER_WORD_3, &_uuid[24]); + _uuid[32] = '\0'; + #endif } /****************************************************************************** - * PUBLIC MEMBER FUNCTIONS + PUBLIC MEMBER FUNCTIONS ******************************************************************************/ void ArduinoCloudThing::begin() { @@ -88,37 +87,35 @@ int ArduinoCloudThing::encode(uint8_t * data, size_t const size) { // time interval may be elapsed or property may be changed int const num_changed_properties = _property_cont.getNumOfChangedProperties(); - if(num_changed_properties > 0) { + if (num_changed_properties > 0) { CborEncoder encoder, arrayEncoder; cbor_encoder_init(&encoder, data, size, 0); - if(cbor_encoder_create_array(&encoder, &arrayEncoder, num_changed_properties) != CborNoError) { + if (cbor_encoder_create_array(&encoder, &arrayEncoder, num_changed_properties) != CborNoError) { return -1; } _property_cont.appendChangedProperties(&arrayEncoder); - if(cbor_encoder_close_container(&encoder, &arrayEncoder) != CborNoError) { + if (cbor_encoder_close_container(&encoder, &arrayEncoder) != CborNoError) { return -1; } -#if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) -PrintFreeRam(); -#endif + #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) + PrintFreeRam(); + #endif int const bytes_encoded = cbor_encoder_get_buffer_size(&encoder, data); return bytes_encoded; - } - else { + } else { return num_changed_properties; } } ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(bool & property, String const & name, Permission const permission) { - if(_property_cont.isPropertyInContainer(Type::Bool, name)) { + if (_property_cont.isPropertyInContainer(Type::Bool, name)) { return (*_property_cont.getPropertyBool(name)); - } - else { + } else { ArduinoCloudProperty *property_opj = new ArduinoCloudProperty(property, name, permission); _property_cont.addProperty(property_opj); return (*property_opj); @@ -126,10 +123,9 @@ ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(bool & property, } ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(int & property, String const & name, Permission const permission) { - if(_property_cont.isPropertyInContainer(Type::Int, name)) { + if (_property_cont.isPropertyInContainer(Type::Int, name)) { return (*_property_cont.getPropertyInt(name)); - } - else { + } else { ArduinoCloudProperty * property_opj = new ArduinoCloudProperty(property, name, permission); _property_cont.addProperty(property_opj); return (*property_opj); @@ -137,10 +133,9 @@ ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(int & property, S } ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(float & property, String const & name, Permission const permission) { - if(_property_cont.isPropertyInContainer(Type::Float, name)) { + if (_property_cont.isPropertyInContainer(Type::Float, name)) { return (*_property_cont.getPropertyFloat(name)); - } - else { + } else { ArduinoCloudProperty * property_opj = new ArduinoCloudProperty(property, name, permission); _property_cont.addProperty(property_opj); return (*property_opj); @@ -148,10 +143,9 @@ ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(float & propert } ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(String & property, String const & name, Permission const permission) { - if(_property_cont.isPropertyInContainer(Type::String, name)) { + if (_property_cont.isPropertyInContainer(Type::String, name)) { return (*_property_cont.getPropertyString(name)); - } - else { + } else { ArduinoCloudProperty * property_opj = new ArduinoCloudProperty(property, name, permission); _property_cont.addProperty(property_opj); return (*property_opj); @@ -167,36 +161,39 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt map_iter, value_iter; - if(cbor_parser_init(payload, length, 0, &parser, &array_iter) != CborNoError) + if (cbor_parser_init(payload, length, 0, &parser, &array_iter) != CborNoError) { return; + } - if(array_iter.type != CborArrayType) + if (array_iter.type != CborArrayType) { return; + } - if(cbor_value_enter_container(&array_iter, &map_iter) != CborNoError) + if (cbor_value_enter_container(&array_iter, &map_iter) != CborNoError) { return; + } CborMapData map_data; MapParserState current_state = MapParserState::EnterMap, next_state; - while(current_state != MapParserState::Complete) { - - switch(current_state) { - case MapParserState::EnterMap : next_state = handle_EnterMap (&map_iter, &value_iter, &map_data); break; - case MapParserState::MapKey : next_state = handle_MapKey (&value_iter ); break; - case MapParserState::UndefinedKey : next_state = handle_UndefinedKey (&value_iter ); break; - case MapParserState::BaseVersion : next_state = handle_BaseVersion (&value_iter, &map_data ); break; - case MapParserState::BaseName : next_state = handle_BaseName (&value_iter, &map_data ); break; - case MapParserState::BaseTime : next_state = handle_BaseTime (&value_iter, &map_data ); break; - case MapParserState::Time : next_state = handle_Time (&value_iter, &map_data ); break; - case MapParserState::Name : next_state = handle_Name (&value_iter, &map_data ); break; - case MapParserState::Value : next_state = handle_Value (&value_iter, &map_data ); break; - case MapParserState::StringValue : next_state = handle_StringValue (&value_iter, &map_data ); break; - case MapParserState::BooleanValue : next_state = handle_BooleanValue (&value_iter, &map_data ); break; - case MapParserState::LeaveMap : next_state = handle_LeaveMap (&map_iter, &value_iter, &map_data); break; - case MapParserState::Complete : /* Nothing to do */ break; - case MapParserState::Error : return; break; + while (current_state != MapParserState::Complete) { + + switch (current_state) { + case MapParserState::EnterMap : next_state = handle_EnterMap(&map_iter, &value_iter, &map_data); break; + case MapParserState::MapKey : next_state = handle_MapKey(&value_iter); break; + case MapParserState::UndefinedKey : next_state = handle_UndefinedKey(&value_iter); break; + case MapParserState::BaseVersion : next_state = handle_BaseVersion(&value_iter, &map_data); break; + case MapParserState::BaseName : next_state = handle_BaseName(&value_iter, &map_data); break; + case MapParserState::BaseTime : next_state = handle_BaseTime(&value_iter, &map_data); break; + case MapParserState::Time : next_state = handle_Time(&value_iter, &map_data); break; + case MapParserState::Name : next_state = handle_Name(&value_iter, &map_data); break; + case MapParserState::Value : next_state = handle_Value(&value_iter, &map_data); break; + case MapParserState::StringValue : next_state = handle_StringValue(&value_iter, &map_data); break; + case MapParserState::BooleanValue : next_state = handle_BooleanValue(&value_iter, &map_data); break; + case MapParserState::LeaveMap : next_state = handle_LeaveMap(&map_iter, &value_iter, &map_data); break; + case MapParserState::Complete : /* Nothing to do */ break; + case MapParserState::Error : return; break; } current_state = next_state; @@ -204,14 +201,14 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt } /****************************************************************************** - * PRIVATE MEMBER FUNCTIONS + PRIVATE MEMBER FUNCTIONS ******************************************************************************/ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_EnterMap(CborValue * map_iter, CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; - if(cbor_value_get_type(map_iter) == CborMapType) { - if(cbor_value_enter_container(map_iter, value_iter) == CborNoError) { + if (cbor_value_get_type(map_iter) == CborMapType) { + if (cbor_value_enter_container(map_iter, value_iter) == CborNoError) { map_data->resetNotBase(); next_state = MapParserState::MapKey; } @@ -223,27 +220,37 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_EnterMap(CborValue * ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * value_iter) { MapParserState next_state = MapParserState::Error; - if(cbor_value_at_end(value_iter)) { + if (cbor_value_at_end(value_iter)) { next_state = MapParserState::LeaveMap; } /* The Map use the CBOR Label (protocol V2) - * Example [{0: "temperature", 2: 25}] - */ + Example [{0: "temperature", 2: 25}] + */ else if (cbor_value_is_integer(value_iter)) { - int val = 0; - if(cbor_value_get_int(value_iter, &val) == CborNoError) { - if(cbor_value_advance(value_iter) == CborNoError) { - if (val == static_cast(CborIntegerMapKey::Name )) next_state = MapParserState::Name; - else if(val == static_cast(CborIntegerMapKey::BaseVersion )) next_state = MapParserState::BaseVersion; - else if(val == static_cast(CborIntegerMapKey::BaseName )) next_state = MapParserState::BaseName; - else if(val == static_cast(CborIntegerMapKey::BaseTime )) next_state = MapParserState::BaseTime; - else if(val == static_cast(CborIntegerMapKey::Value )) next_state = MapParserState::Value; - else if(val == static_cast(CborIntegerMapKey::StringValue )) next_state = MapParserState::StringValue; - else if(val == static_cast(CborIntegerMapKey::BooleanValue)) next_state = MapParserState::BooleanValue; - else if(val == static_cast(CborIntegerMapKey::Time )) next_state = MapParserState::Time; - else next_state = MapParserState::UndefinedKey; - } - } + int val = 0; + if (cbor_value_get_int(value_iter, &val) == CborNoError) { + if (cbor_value_advance(value_iter) == CborNoError) { + if (val == static_cast(CborIntegerMapKey::Name)) { + next_state = MapParserState::Name; + } else if (val == static_cast(CborIntegerMapKey::BaseVersion)) { + next_state = MapParserState::BaseVersion; + } else if (val == static_cast(CborIntegerMapKey::BaseName)) { + next_state = MapParserState::BaseName; + } else if (val == static_cast(CborIntegerMapKey::BaseTime)) { + next_state = MapParserState::BaseTime; + } else if (val == static_cast(CborIntegerMapKey::Value)) { + next_state = MapParserState::Value; + } else if (val == static_cast(CborIntegerMapKey::StringValue)) { + next_state = MapParserState::StringValue; + } else if (val == static_cast(CborIntegerMapKey::BooleanValue)) { + next_state = MapParserState::BooleanValue; + } else if (val == static_cast(CborIntegerMapKey::Time)) { + next_state = MapParserState::Time; + } else { + next_state = MapParserState::UndefinedKey; + } + } + } } return next_state; @@ -252,7 +259,7 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_MapKey(CborValue * v ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_UndefinedKey(CborValue * value_iter) { MapParserState next_state = MapParserState::Error; - if(cbor_value_advance(value_iter) == CborNoError) { + if (cbor_value_advance(value_iter) == CborNoError) { next_state = MapParserState::MapKey; } @@ -262,12 +269,12 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_UndefinedKey(CborVal ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseVersion(CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; - if(cbor_value_is_integer(value_iter)) { + if (cbor_value_is_integer(value_iter)) { int val = 0; - if(cbor_value_get_int(value_iter, &val) == CborNoError) { + if (cbor_value_get_int(value_iter, &val) == CborNoError) { map_data->base_version.set(val); - if(cbor_value_advance(value_iter) == CborNoError) { + if (cbor_value_advance(value_iter) == CborNoError) { next_state = MapParserState::MapKey; } } @@ -279,10 +286,10 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseVersion(CborValu ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseName(CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; - if(cbor_value_is_text_string(value_iter)) { + if (cbor_value_is_text_string(value_iter)) { char * val = 0; size_t val_size = 0; - if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { + if (cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { map_data->base_name.set(String(val)); free(val); next_state = MapParserState::MapKey; @@ -296,10 +303,10 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseTime(CborValue * MapParserState next_state = MapParserState::Error; double val = 0.0; - if(ifNumericConvertToDouble(value_iter, &val)) { + if (ifNumericConvertToDouble(value_iter, &val)) { map_data->base_time.set(val); - if(cbor_value_advance(value_iter) == CborNoError) { + if (cbor_value_advance(value_iter) == CborNoError) { next_state = MapParserState::MapKey; } } @@ -310,10 +317,10 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseTime(CborValue * ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Name(CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; - if(cbor_value_is_text_string(value_iter)) { + if (cbor_value_is_text_string(value_iter)) { char * val = 0; size_t val_size = 0; - if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { + if (cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { map_data->name.set(val); free(val); next_state = MapParserState::MapKey; @@ -327,10 +334,10 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Value(CborValue * va MapParserState next_state = MapParserState::Error; double val = 0.0; - if(ifNumericConvertToDouble(value_iter, &val)) { + if (ifNumericConvertToDouble(value_iter, &val)) { map_data->val.set(val); - if(cbor_value_advance(value_iter) == CborNoError) { + if (cbor_value_advance(value_iter) == CborNoError) { next_state = MapParserState::MapKey; } } @@ -341,10 +348,10 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Value(CborValue * va ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_StringValue(CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; - if(cbor_value_is_text_string(value_iter)) { + if (cbor_value_is_text_string(value_iter)) { char * val = 0; size_t val_size = 0; - if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { + if (cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { map_data->str_val.set(String(val)); free(val); next_state = MapParserState::MapKey; @@ -358,10 +365,10 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BooleanValue(CborVal MapParserState next_state = MapParserState::Error; bool val = false; - if(cbor_value_get_boolean(value_iter, &val) == CborNoError) { + if (cbor_value_get_boolean(value_iter, &val) == CborNoError) { map_data->bool_val.set(val); - if(cbor_value_advance(value_iter) == CborNoError) { + if (cbor_value_advance(value_iter) == CborNoError) { next_state = MapParserState::MapKey; } } @@ -373,10 +380,10 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * val MapParserState next_state = MapParserState::Error; double val = 0.0; - if(ifNumericConvertToDouble(value_iter, &val)) { + if (ifNumericConvertToDouble(value_iter, &val)) { map_data->time.set(val); - if(cbor_value_advance(value_iter) == CborNoError) { + if (cbor_value_advance(value_iter) == CborNoError) { next_state = MapParserState::MapKey; } } @@ -389,23 +396,22 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * //compute the cloud event change time unsigned long cloudChangeEventTime = 0; - if(map_data->base_time.isSet()){ + if (map_data->base_time.isSet()) { cloudChangeEventTime = (unsigned long)(map_data->base_time.get()); } - if(map_data->time.isSet()){ + if (map_data->time.isSet()) { cloudChangeEventTime += (unsigned long)map_data->time.get(); } /* Update the property containers depending on the parsed data */ - if(map_data->name.isSet()) - { + if (map_data->name.isSet()) { /* Value (Integer/Float/Double/Half-Float) */ - if(map_data->val.isSet()) { - ArduinoCloudProperty * int_property = _property_cont.getPropertyInt (map_data->name.get()); + if (map_data->val.isSet()) { + ArduinoCloudProperty * int_property = _property_cont.getPropertyInt(map_data->name.get()); ArduinoCloudProperty * float_property = _property_cont.getPropertyFloat(map_data->name.get()); - if(int_property && int_property->isWriteableByCloud()) { - if(_syncMessage) { + if (int_property && int_property->isWriteableByCloud()) { + if (_syncMessage) { int_property->setLastCloudChangeTimestamp(cloudChangeEventTime); int_property->setCloudShadowValue(map_data->val.get()); int_property->execCallbackOnSync(); @@ -415,8 +421,8 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * } } - if(float_property && float_property->isWriteableByCloud()) { - if(_syncMessage) { + if (float_property && float_property->isWriteableByCloud()) { + if (_syncMessage) { float_property->setLastCloudChangeTimestamp(cloudChangeEventTime); float_property->setCloudShadowValue(map_data->val.get()); float_property->execCallbackOnSync(); @@ -428,10 +434,10 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * } /* Value (String) */ - if(map_data->str_val.isSet()) { + if (map_data->str_val.isSet()) { ArduinoCloudProperty* string_property = _property_cont.getPropertyString(map_data->name.get()); - if(string_property && string_property->isWriteableByCloud()) { - if(_syncMessage) { + if (string_property && string_property->isWriteableByCloud()) { + if (_syncMessage) { string_property->setLastCloudChangeTimestamp(cloudChangeEventTime); string_property->setCloudShadowValue(map_data->str_val.get()); string_property->execCallbackOnSync(); @@ -443,10 +449,10 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * } /* Value (bool) */ - if(map_data->bool_val.isSet()) { + if (map_data->bool_val.isSet()) { ArduinoCloudProperty* bool_property = _property_cont.getPropertyBool(map_data->name.get()); - if(bool_property && bool_property->isWriteableByCloud()) { - if(_syncMessage) { + if (bool_property && bool_property->isWriteableByCloud()) { + if (_syncMessage) { bool_property->setLastCloudChangeTimestamp(cloudChangeEventTime); bool_property->setCloudShadowValue(map_data->bool_val.get()); bool_property->execCallbackOnSync(); @@ -460,11 +466,10 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * /* Transition into the next map if available, otherwise finish */ - if(cbor_value_leave_container(map_iter, value_iter) == CborNoError) { - if(!cbor_value_at_end(map_iter)) { + if (cbor_value_leave_container(map_iter, value_iter) == CborNoError) { + if (!cbor_value_at_end(map_iter)) { next_state = MapParserState::EnterMap; - } - else { + } else { next_state = MapParserState::Complete; } } @@ -474,30 +479,27 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * bool ArduinoCloudThing::ifNumericConvertToDouble(CborValue * value_iter, double * numeric_val) { - if(cbor_value_is_integer(value_iter)) { + if (cbor_value_is_integer(value_iter)) { int64_t val = 0; - if(cbor_value_get_int64(value_iter, &val) == CborNoError) { + if (cbor_value_get_int64(value_iter, &val) == CborNoError) { *numeric_val = static_cast(val); return true; } - } - else if(cbor_value_is_double(value_iter)) { + } else if (cbor_value_is_double(value_iter)) { double val = 0.0; - if(cbor_value_get_double(value_iter, &val) == CborNoError) { + if (cbor_value_get_double(value_iter, &val) == CborNoError) { *numeric_val = val; return true; } - } - else if(cbor_value_is_float(value_iter)) { + } else if (cbor_value_is_float(value_iter)) { float val = 0.0; - if(cbor_value_get_float(value_iter, &val) == CborNoError) { + if (cbor_value_get_float(value_iter, &val) == CborNoError) { *numeric_val = static_cast(val); return true; } - } - else if(cbor_value_is_half_float(value_iter)) { + } else if (cbor_value_is_half_float(value_iter)) { uint16_t val = 0; - if(cbor_value_get_half_float(value_iter, &val) == CborNoError) { + if (cbor_value_get_half_float(value_iter, &val) == CborNoError) { *numeric_val = static_cast(convertCborHalfFloatToDouble(val)); return true; } @@ -511,8 +513,12 @@ double ArduinoCloudThing::convertCborHalfFloatToDouble(uint16_t const half_val) int exp = (half_val >> 10) & 0x1f; int mant = half_val & 0x3ff; double val; - if (exp == 0) val = ldexp(mant, -24); - else if (exp != 31) val = ldexp(mant + 1024, exp - 25); - else val = mant == 0 ? INFINITY : NAN; + if (exp == 0) { + val = ldexp(mant, -24); + } else if (exp != 31) { + val = ldexp(mant + 1024, exp - 25); + } else { + val = mant == 0 ? INFINITY : NAN; + } return half_val & 0x8000 ? -val : val; } diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index c35921c1d..5181caa08 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -19,7 +19,7 @@ #define ARDUINO_CLOUD_THING_H_ /****************************************************************************** - * INCLUDE + INCLUDE ******************************************************************************/ #include "ArduinoCloudProperty.hpp" @@ -28,7 +28,7 @@ #include "lib/LinkedList/LinkedList.h" /****************************************************************************** - * CONSTANTS + CONSTANTS ******************************************************************************/ static bool ON = true; @@ -41,13 +41,13 @@ static long const HOURS = 3600; static long const DAYS = 86400; /****************************************************************************** - * SYNCHRONIZATION CALLBACKS + SYNCHRONIZATION CALLBACKS ******************************************************************************/ #define MOST_RECENT_WINS onAutoSync template void onAutoSync(ArduinoCloudProperty property) { - if( property.getLastCloudChangeTimestamp() > property.getLastLocalChangeTimestamp()){ + if (property.getLastCloudChangeTimestamp() > property.getLastLocalChangeTimestamp()) { property.setPropertyValue(property.getCloudShadowValue()); property.forceCallbackOnChange(); } @@ -66,108 +66,117 @@ void onForceDeviceSync(ArduinoCloudProperty property) { } /****************************************************************************** - * CLASS DECLARATION + CLASS DECLARATION ******************************************************************************/ class ArduinoCloudThing { -public: - ArduinoCloudThing(); - - void begin(); - - ArduinoCloudProperty & addPropertyReal(bool & property, String const & name, Permission const permission); - ArduinoCloudProperty & addPropertyReal(int & property, String const & name, Permission const permission); - ArduinoCloudProperty & addPropertyReal(float & property, String const & name, Permission const permission); - ArduinoCloudProperty & addPropertyReal(String & property, String const & name, Permission const permission); - - // compute the timestamp of the local properties changes - int updateTimestampOnChangedProperties(unsigned long changeEventTime); - /* encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer */ - int encode(uint8_t * data, size_t const size); - int encode(ArduinoCloudPropertyContainer *property_cont, uint8_t * data, size_t const size); - /* decode a CBOR payload received from the cloud */ - void decode(uint8_t const * const payload, size_t const length, bool syncMessage = false); - - -private: - - char _uuid[33]; - ArduinoCloudPropertyContainer _property_cont; - bool _syncMessage; - - enum class MapParserState { - EnterMap, - MapKey, - UndefinedKey, - BaseVersion, - BaseName, - BaseTime, - Name, - Value, - StringValue, - BooleanValue, - Time, - LeaveMap, - Complete, - Error - }; - - template - class MapEntry { public: + ArduinoCloudThing(); - MapEntry() : _is_set(false) { } + void begin(); - inline void set (T const & entry) { _entry = entry; _is_set = true; } - inline T const get () const { return _entry; } + ArduinoCloudProperty & addPropertyReal(bool & property, String const & name, Permission const permission); + ArduinoCloudProperty & addPropertyReal(int & property, String const & name, Permission const permission); + ArduinoCloudProperty & addPropertyReal(float & property, String const & name, Permission const permission); + ArduinoCloudProperty & addPropertyReal(String & property, String const & name, Permission const permission); - inline bool isSet() const { return _is_set; } - inline void reset() { _is_set = false; } + // compute the timestamp of the local properties changes + int updateTimestampOnChangedProperties(unsigned long changeEventTime); + /* encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer */ + int encode(uint8_t * data, size_t const size); + int encode(ArduinoCloudPropertyContainer *property_cont, uint8_t * data, size_t const size); + /* decode a CBOR payload received from the cloud */ + void decode(uint8_t const * const payload, size_t const length, bool syncMessage = false); - private: - - T _entry; - bool _is_set; - - }; - class CborMapData { + private: - public: - MapEntry base_version; - MapEntry base_name; - MapEntry base_time; - MapEntry name; - MapEntry val; - MapEntry str_val; - MapEntry bool_val; - MapEntry time; - - void resetNotBase() { - name.reset (); - val.reset (); - str_val.reset (); - bool_val.reset (); - time.reset (); - } - }; - - MapParserState handle_EnterMap (CborValue * map_iter, CborValue * value_iter, CborMapData * map_data); - MapParserState handle_MapKey (CborValue * value_iter); - MapParserState handle_UndefinedKey (CborValue * value_iter); - MapParserState handle_BaseVersion (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_BaseName (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_BaseTime (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_Name (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_Value (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_StringValue (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_BooleanValue (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_Time (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_LeaveMap (CborValue * map_iter, CborValue * value_iter, CborMapData const * const map_data); - - static bool ifNumericConvertToDouble (CborValue * value_iter, double * numeric_val); - static double convertCborHalfFloatToDouble(uint16_t const half_val); + char _uuid[33]; + ArduinoCloudPropertyContainer _property_cont; + bool _syncMessage; + + enum class MapParserState { + EnterMap, + MapKey, + UndefinedKey, + BaseVersion, + BaseName, + BaseTime, + Name, + Value, + StringValue, + BooleanValue, + Time, + LeaveMap, + Complete, + Error + }; + + template + class MapEntry { + public: + + MapEntry() : _is_set(false) { } + + inline void set(T const & entry) { + _entry = entry; + _is_set = true; + } + inline T const get() const { + return _entry; + } + + inline bool isSet() const { + return _is_set; + } + inline void reset() { + _is_set = false; + } + + private: + + T _entry; + bool _is_set; + + }; + + class CborMapData { + + public: + MapEntry base_version; + MapEntry base_name; + MapEntry base_time; + MapEntry name; + MapEntry val; + MapEntry str_val; + MapEntry bool_val; + MapEntry time; + + void resetNotBase() { + name.reset(); + val.reset(); + str_val.reset(); + bool_val.reset(); + time.reset(); + } + }; + + MapParserState handle_EnterMap(CborValue * map_iter, CborValue * value_iter, CborMapData * map_data); + MapParserState handle_MapKey(CborValue * value_iter); + MapParserState handle_UndefinedKey(CborValue * value_iter); + MapParserState handle_BaseVersion(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_BaseName(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_BaseTime(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_Name(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_Value(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_StringValue(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_BooleanValue(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_Time(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_LeaveMap(CborValue * map_iter, CborValue * value_iter, CborMapData const * const map_data); + + static bool ifNumericConvertToDouble(CborValue * value_iter, double * numeric_val); + static double convertCborHalfFloatToDouble(uint16_t const half_val); }; From 9ac82d0a4964438520de600140691bfcc34f343e Mon Sep 17 00:00:00 2001 From: per1234 Date: Tue, 9 Apr 2019 00:45:14 -0700 Subject: [PATCH 128/175] Manually improve some formatting left ugly by the auto format All the breaks were originally aligned. On some lines, this was accomplished by adding spaces before the closing parentheses, but not all lines have one. Because the auto format removes padding inside parentheses, this resulted in the spaces being removed from the lines with the parentheses, but not on the other lines. I chose to make the most minimal fix, but the alternative could be to add spaces outside the parentheses to recover alignment of all the breaks, which is fully compliant with the formatting check. --- ArduinoCloudThing.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index b9b9f71df..857bb39c4 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -192,8 +192,8 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt case MapParserState::StringValue : next_state = handle_StringValue(&value_iter, &map_data); break; case MapParserState::BooleanValue : next_state = handle_BooleanValue(&value_iter, &map_data); break; case MapParserState::LeaveMap : next_state = handle_LeaveMap(&map_iter, &value_iter, &map_data); break; - case MapParserState::Complete : /* Nothing to do */ break; - case MapParserState::Error : return; break; + case MapParserState::Complete : /* Nothing to do */ break; + case MapParserState::Error : return; break; } current_state = next_state; From 95fa08d310f0383f61786b15a11fea0cc25075d1 Mon Sep 17 00:00:00 2001 From: ilcato Date: Sat, 2 Mar 2019 10:46:15 +0100 Subject: [PATCH 129/175] Object properties implementation --- ArduinoCloudProperty.cpp | 159 +++++++++++++++++ ArduinoCloudProperty.hpp | 206 +++++++++++---------- ArduinoCloudProperty.ipp | 288 ------------------------------ ArduinoCloudPropertyContainer.cpp | 70 -------- ArduinoCloudPropertyContainer.hpp | 98 ---------- ArduinoCloudPropertyContainer.ipp | 81 --------- ArduinoCloudThing.cpp | 209 ++++++++++------------ ArduinoCloudThing.h | 190 +++++++------------- valuetypes/CloudBool.h | 82 +++++++++ valuetypes/CloudFloat.h | 100 +++++++++++ valuetypes/CloudInt.h | 130 ++++++++++++++ valuetypes/CloudString.h | 96 ++++++++++ valuetypes/CloudWrapperBool.h | 75 ++++++++ valuetypes/CloudWrapperFloat.h | 77 ++++++++ valuetypes/CloudWrapperInt.h | 75 ++++++++ valuetypes/CloudWrapperString.h | 79 ++++++++ 16 files changed, 1137 insertions(+), 878 deletions(-) create mode 100644 ArduinoCloudProperty.cpp delete mode 100644 ArduinoCloudProperty.ipp delete mode 100644 ArduinoCloudPropertyContainer.cpp delete mode 100644 ArduinoCloudPropertyContainer.hpp delete mode 100644 ArduinoCloudPropertyContainer.ipp create mode 100644 valuetypes/CloudBool.h create mode 100644 valuetypes/CloudFloat.h create mode 100644 valuetypes/CloudInt.h create mode 100644 valuetypes/CloudString.h create mode 100644 valuetypes/CloudWrapperBool.h create mode 100644 valuetypes/CloudWrapperFloat.h create mode 100644 valuetypes/CloudWrapperInt.h create mode 100644 valuetypes/CloudWrapperString.h diff --git a/ArduinoCloudProperty.cpp b/ArduinoCloudProperty.cpp new file mode 100644 index 000000000..c30eca090 --- /dev/null +++ b/ArduinoCloudProperty.cpp @@ -0,0 +1,159 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#include "ArduinoCloudProperty.hpp" + +#ifdef ARDUINO_ARCH_SAMD + #include + extern RTCZero rtc; +#endif + +static unsigned long getTimestamp() { + #ifdef ARDUINO_ARCH_SAMD + return rtc.getEpoch(); + #else + #warning "No RTC available on this architecture - ArduinoIoTCloud will not keep track of local change timestamps ." + return 0; + #endif +} + +/****************************************************************************** + * CTOR/DTOR + ******************************************************************************/ +ArduinoCloudProperty::ArduinoCloudProperty() +: _name(""), + _permission(Permission::Read), + _update_callback_func(nullptr), + _sync_callback_func(nullptr), + _has_been_updated_once(false), + _has_been_modified_in_callback(false), + _min_delta_property(0.0f), + _min_time_between_updates_millis(0), + _last_updated_millis(0), + _update_interval_millis(0), + _last_local_change_timestamp(0), + _last_cloud_change_timestamp(0) +{ +} + +/****************************************************************************** + * PUBLIC MEMBER FUNCTIONS + ******************************************************************************/ +void ArduinoCloudProperty::init(String const name, Permission const permission) { + _name = name; + _permission = permission; +} + +ArduinoCloudProperty & ArduinoCloudProperty::onUpdate(UpdateCallbackFunc func) { + _update_callback_func = func; + return (*this); +} + +ArduinoCloudProperty & ArduinoCloudProperty::onSync(void (*func)(ArduinoCloudProperty & property)) { + _sync_callback_func = func; + return (*this); +} + +ArduinoCloudProperty & ArduinoCloudProperty::publishOnChange(float const min_delta_property, unsigned long const min_time_between_updates_millis) { + _update_policy = UpdatePolicy::OnChange; + _min_delta_property = min_delta_property; + _min_time_between_updates_millis = min_time_between_updates_millis; + return (*this); +} + +ArduinoCloudProperty & ArduinoCloudProperty::publishEvery(unsigned long const seconds) { + _update_policy = UpdatePolicy::TimeInterval; + _update_interval_millis = (seconds * 1000); + return (*this); +} + +bool ArduinoCloudProperty::shouldBeUpdated() { + if(!_has_been_updated_once) return true; + + if(_has_been_modified_in_callback) { + _has_been_modified_in_callback = false; + return true; + } + + if (_update_policy == UpdatePolicy::OnChange) { + return (isDifferentFromCloudShadow() && ((millis() - _last_updated_millis) >= (_min_time_between_updates_millis))); + } + else if(_update_policy == UpdatePolicy::TimeInterval) { + return ((millis() - _last_updated_millis) >= _update_interval_millis); + } + else { + return false; + } +} + +void ArduinoCloudProperty::execCallbackOnChange() { + if(_update_callback_func != NULL) { + _update_callback_func(); + } + if(!isDifferentFromCloudShadow()) { + _has_been_modified_in_callback = true; + } +} + +void ArduinoCloudProperty::execCallbackOnSync() { + if(_sync_callback_func != NULL) { + _sync_callback_func(*this); + } +} + +void ArduinoCloudProperty::append(CborEncoder * encoder) { + if (isReadableByCloud()) { + CborEncoder mapEncoder; + + cbor_encoder_create_map (encoder, &mapEncoder, CborIndefiniteLength); + cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::Name)); + cbor_encode_text_stringz (&mapEncoder, _name.c_str()); + appendValue (&mapEncoder); + cbor_encoder_close_container(encoder, &mapEncoder); + + toShadow(); + + _has_been_updated_once = true; + _last_updated_millis = millis(); + } +} +void ArduinoCloudProperty::updateLocalTimestamp() { + if (isReadableByCloud()) { + _last_local_change_timestamp = getTimestamp(); +// ArduinoCloud.update(); + } +} + +void ArduinoCloudProperty::setLastCloudChangeTimestamp(unsigned long cloudChangeEventTime) { + _last_cloud_change_timestamp = cloudChangeEventTime; +} +void ArduinoCloudProperty::setLastLocalChangeTimestamp(unsigned long localChangeTime) { + _last_local_change_timestamp = localChangeTime; +} + +unsigned long ArduinoCloudProperty::getLastCloudChangeTimestamp() { + return _last_cloud_change_timestamp; +} + +unsigned long ArduinoCloudProperty::getLastLocalChangeTimestamp() { + return _last_local_change_timestamp; +} + + +/****************************************************************************** + * PRIVATE MEMBER FUNCTIONS + ******************************************************************************/ diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 0505d65cc..417aeb459 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -23,7 +23,6 @@ ******************************************************************************/ #include - #include "lib/tinycbor/cbor-lib.h" /****************************************************************************** @@ -31,21 +30,8 @@ ******************************************************************************/ /****************************************************************************** - TYPEDEF + * ENUM ******************************************************************************/ - -enum class Permission { - Read, Write, ReadWrite -}; - -enum class Type { - Bool, Int, Float, String -}; - -enum class UpdatePolicy { - OnChange, TimeInterval -}; - /* Source: https://tools.ietf.org/html/rfc8428#section-6 */ enum class CborIntegerMapKey : int { BaseVersion = -1, /* bver */ @@ -65,101 +51,125 @@ enum class CborIntegerMapKey : int { DataValue = 8 /* vd */ }; +template +class MapEntry { +public: + + MapEntry() : _is_set(false) { } + + inline void set (T const & entry) { _entry = entry; _is_set = true; } + inline T const get () const { return _entry; } + + inline bool isSet() const { return _is_set; } + inline void reset() { _is_set = false; } + +private: + + T _entry; + bool _is_set; + +}; + +class CborMapData { + +public: + MapEntry base_version; + MapEntry base_name; + MapEntry base_time; + MapEntry name; + MapEntry val; + MapEntry str_val; + MapEntry bool_val; + MapEntry time; + + void resetNotBase() { + name.reset (); + val.reset (); + str_val.reset (); + bool_val.reset (); + time.reset (); + } +}; + +enum class Permission { + Read, Write, ReadWrite +}; + +enum class Type { + Bool, Int, Float, String +}; + +enum class UpdatePolicy { + OnChange, TimeInterval +}; + typedef void(*UpdateCallbackFunc)(void); /****************************************************************************** CLASS DECLARATION ******************************************************************************/ -template class ArduinoCloudProperty { - public: - - ArduinoCloudProperty(T & property, String const & name, Permission const permission); - - void writeByCloud(T const val); - - /* Composable configuration of the ArduinoCloudProperty class */ - ArduinoCloudProperty & onUpdate(UpdateCallbackFunc func); - ArduinoCloudProperty & onSync(void (*func)(ArduinoCloudProperty property)); - ArduinoCloudProperty & publishOnChange(T const min_delta_property, unsigned long const min_time_between_updates_millis = 0); - ArduinoCloudProperty & publishEvery(unsigned long const seconds); - - inline String name() const { - return _name; - } - inline bool isReadableByCloud() const { - return (_permission == Permission::Read) || (_permission == Permission::ReadWrite); - } - inline bool isWriteableByCloud() const { - return (_permission == Permission::Write) || (_permission == Permission::ReadWrite); - } - - bool shouldBeUpdated(); - void execCallbackOnChange(); - void execCallbackOnSync(); - void forceCallbackOnChange(); - void setPreviousCloudChangeTimestamp(unsigned long cloudChangeTime); - void setLastCloudChangeTimestamp(unsigned long cloudChangeTime); - void setLastLocalChangeTimestamp(unsigned long localChangeTime); - unsigned long getPreviousCloudChangeTimestamp(); - unsigned long getLastCloudChangeTimestamp(); - unsigned long getLastLocalChangeTimestamp(); - void setPropertyValue(T const val); - void setCloudShadowValue(T const val); - T getCloudShadowValue(); - void setLocalShadowValue(T const val); - T getLocalShadowValue(); - void updateTime(unsigned long changeEventTime); - bool isChangedLocally(); - void append(CborEncoder * encoder); - - private: - - T & _property, - _cloud_shadow_property, - _local_shadow_property; - String _name; - Permission _permission; - UpdateCallbackFunc _update_callback_func; - void (*_sync_callback_func)(ArduinoCloudProperty property); - - UpdatePolicy _update_policy; - bool _has_been_updated_once, - _has_been_modified_in_callback; - /* Variables used for UpdatePolicy::OnChange */ - T _min_delta_property; - unsigned long _min_time_between_updates_millis; - /* Variables used for UpdatePolicy::TimeInterval */ - unsigned long _last_updated_millis, - _update_interval_millis; - /* Variables used for reconnection sync*/ - unsigned long _last_change_timestamp; - unsigned long _last_local_change_timestamp; - unsigned long _last_cloud_change_timestamp; - unsigned long _previous_cloud_change_timestamp; - - - void appendValue(CborEncoder * mapEncoder) const; - bool isValueDifferent(T const lhs, T const rhs) const; - - T getInitialMinDeltaPropertyValue() const; - +public: + ArduinoCloudProperty(); + void init(String const name, Permission const permission); + + /* Composable configuration of the ArduinoCloudProperty class */ + ArduinoCloudProperty & onUpdate (UpdateCallbackFunc func); + ArduinoCloudProperty & onSync (void (*func)(ArduinoCloudProperty &property)); + ArduinoCloudProperty & publishOnChange(float const min_delta_property, unsigned long const min_time_between_updates_millis = 0); + ArduinoCloudProperty & publishEvery (unsigned long const seconds); + + inline String name () const { return _name; } + inline bool isReadableByCloud () const { return (_permission == Permission::Read ) || (_permission == Permission::ReadWrite); } + inline bool isWriteableByCloud() const { return (_permission == Permission::Write) || (_permission == Permission::ReadWrite); } + + bool shouldBeUpdated (); + void execCallbackOnChange (); + void execCallbackOnSync (); + void setLastCloudChangeTimestamp (unsigned long cloudChangeTime); + void setLastLocalChangeTimestamp (unsigned long localChangeTime); + unsigned long getLastCloudChangeTimestamp(); + unsigned long getLastLocalChangeTimestamp(); + + void updateLocalTimestamp (); + void append (CborEncoder * encoder); + + virtual bool isDifferentFromCloudShadow() = 0; + virtual void toShadow() = 0; + virtual void fromCloudShadow() = 0; + virtual void appendValue(CborEncoder * mapEncoder) const = 0; + virtual void setValue(CborMapData const * const map_data) = 0; + virtual void setCloudShadowValue(CborMapData const * const map_data) = 0; + virtual bool isPrimitive() { return false; }; + virtual bool isChangedLocally() { return false; }; +protected: + /* Variables used for UpdatePolicy::OnChange */ + float _min_delta_property; + unsigned long _min_time_between_updates_millis; + +private: + String _name; + Permission _permission; + UpdateCallbackFunc _update_callback_func; + void (*_sync_callback_func)(ArduinoCloudProperty &property); + + UpdatePolicy _update_policy; + bool _has_been_updated_once, + _has_been_modified_in_callback; + /* Variables used for UpdatePolicy::TimeInterval */ + unsigned long _last_updated_millis, + _update_interval_millis; + /* Variables used for reconnection sync*/ + unsigned long _last_local_change_timestamp; + unsigned long _last_cloud_change_timestamp; + }; /****************************************************************************** PROTOTYPE FREE FUNCTIONs ******************************************************************************/ -template -inline bool operator == (ArduinoCloudProperty const & lhs, ArduinoCloudProperty const & rhs) { - return (lhs.name() == rhs.name()); -} - -/****************************************************************************** - TEMPLATE IMPLEMENTATION - ******************************************************************************/ - -#include "ArduinoCloudProperty.ipp" +inline bool operator == (ArduinoCloudProperty const & lhs, ArduinoCloudProperty const & rhs) { return (lhs.name() == rhs.name()); } #endif /* ARDUINO_CLOUD_PROPERTY_HPP_ */ diff --git a/ArduinoCloudProperty.ipp b/ArduinoCloudProperty.ipp deleted file mode 100644 index 80981a987..000000000 --- a/ArduinoCloudProperty.ipp +++ /dev/null @@ -1,288 +0,0 @@ -// -// This file is part of ArduinoCloudThing -// -// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) -// -// This software is released under the GNU General Public License version 3, -// which covers the main part of ArduinoCloudThing. -// The terms of this license can be found at: -// https://www.gnu.org/licenses/gpl-3.0.en.html -// -// You can be released from the requirements of the above licenses by purchasing -// a commercial license. Buying such a license is mandatory if you want to modify or -// otherwise use the software for commercial activities involving the Arduino -// software without disclosing the source code of your own applications. To purchase -// a commercial license, send an email to license@arduino.cc. -// - -/****************************************************************************** - CTOR/DTOR - ******************************************************************************/ - -template -ArduinoCloudProperty::ArduinoCloudProperty(T & property, String const & name, Permission const permission) - : _property(property), - _cloud_shadow_property(property), - _local_shadow_property(property), - _name(name), - _permission(permission), - _update_callback_func(NULL), - _sync_callback_func(NULL), - _update_policy(UpdatePolicy::OnChange), - _has_been_updated_once(false), - _has_been_modified_in_callback(false), - _min_delta_property(getInitialMinDeltaPropertyValue()), - _min_time_between_updates_millis(0), - _last_updated_millis(0), - _update_interval_millis(0), - _last_local_change_timestamp(0), - _last_cloud_change_timestamp(0), - _previous_cloud_change_timestamp(0) { -} - -/****************************************************************************** - PUBLIC MEMBER FUNCTIONS - ******************************************************************************/ - -template -void ArduinoCloudProperty::writeByCloud(T const val) { - if (isWriteableByCloud()) { - _property = val; - /* _cloud_shadow_property and local_shadow_property are not updated so there will be an update the next time around */ - } -} - -template -ArduinoCloudProperty & ArduinoCloudProperty::onUpdate(UpdateCallbackFunc func) { - _update_callback_func = func; - return (*this); -} - -template -ArduinoCloudProperty & ArduinoCloudProperty::onSync(void (*func)(ArduinoCloudProperty property)) { - _sync_callback_func = func; - return (*this); -} - -template -ArduinoCloudProperty & ArduinoCloudProperty::publishOnChange(T const min_delta_property, unsigned long const min_time_between_updates_millis) { - _update_policy = UpdatePolicy::OnChange; - _min_delta_property = min_delta_property; - _min_time_between_updates_millis = min_time_between_updates_millis; - return (*this); -} - -template -ArduinoCloudProperty & ArduinoCloudProperty::publishEvery(unsigned long const seconds) { - _update_policy = UpdatePolicy::TimeInterval; - _update_interval_millis = (seconds * 1000); - return (*this); -} - -template -bool ArduinoCloudProperty::shouldBeUpdated() { - if (!_has_been_updated_once) { - return true; - } - - if (_has_been_modified_in_callback) { - _has_been_modified_in_callback = false; - return true; - } - - if (_update_policy == UpdatePolicy::OnChange) { - return (isValueDifferent(_property, _cloud_shadow_property) && ((millis() - _last_updated_millis) >= (_min_time_between_updates_millis))); - } else if (_update_policy == UpdatePolicy::TimeInterval) { - return ((millis() - _last_updated_millis) >= _update_interval_millis); - } else { - return false; - } -} - -template -void ArduinoCloudProperty::forceCallbackOnChange() { - if (_update_callback_func != NULL) { - _update_callback_func(); - } -} - -template -void ArduinoCloudProperty::execCallbackOnChange() { - if (isValueDifferent(_property, _cloud_shadow_property)) { - if (_update_callback_func != NULL) { - _update_callback_func(); - } - if (!isValueDifferent(_property, _cloud_shadow_property)) { - _has_been_modified_in_callback = true; - } - } -} - -template -void ArduinoCloudProperty::execCallbackOnSync() { - if (_sync_callback_func != NULL) { - _sync_callback_func(*this); - } -} - -template -void ArduinoCloudProperty::append(CborEncoder * encoder) { - if (isReadableByCloud()) { - CborEncoder mapEncoder; - - cbor_encoder_create_map(encoder, &mapEncoder, CborIndefiniteLength); - cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Name)); - cbor_encode_text_stringz(&mapEncoder, _name.c_str()); - appendValue(&mapEncoder); - cbor_encoder_close_container(encoder, &mapEncoder); - - _cloud_shadow_property = _property; - - _has_been_updated_once = true; - _last_updated_millis = millis(); - } -} - -template -bool ArduinoCloudProperty::isChangedLocally() { - return isValueDifferent(_property, _local_shadow_property); -} - -template -void ArduinoCloudProperty::updateTime(unsigned long changeEventTime) { - if (isReadableByCloud()) { - _local_shadow_property = _property; - _last_local_change_timestamp = changeEventTime; - } -} - -template -void ArduinoCloudProperty::setPropertyValue(T const val) { - if (isWriteableByCloud()) { - _property = val; - } -} - -template -void ArduinoCloudProperty::setCloudShadowValue(T const val) { - if (isWriteableByCloud()) { - _cloud_shadow_property = val; - } -} - -template -void ArduinoCloudProperty::setLocalShadowValue(T const val) { - if (isWriteableByCloud()) { - _local_shadow_property = val; - } -} - -template -T ArduinoCloudProperty::getCloudShadowValue() { - return _cloud_shadow_property; -} - -template -T ArduinoCloudProperty::getLocalShadowValue() { - return _local_shadow_property; -} - -template -void ArduinoCloudProperty::setPreviousCloudChangeTimestamp(unsigned long cloudChangeEventTime) { - _previous_cloud_change_timestamp = cloudChangeEventTime; -} - -template -void ArduinoCloudProperty::setLastCloudChangeTimestamp(unsigned long cloudChangeEventTime) { - _previous_cloud_change_timestamp = _last_cloud_change_timestamp; - _last_cloud_change_timestamp = cloudChangeEventTime; -} - -template -void ArduinoCloudProperty::setLastLocalChangeTimestamp(unsigned long localChangeEventTime) { - _last_local_change_timestamp = localChangeEventTime; -} - -template -unsigned long ArduinoCloudProperty::getPreviousCloudChangeTimestamp() { - return _previous_cloud_change_timestamp; -} - -template -unsigned long ArduinoCloudProperty::getLastCloudChangeTimestamp() { - return _last_cloud_change_timestamp; -} - -template -unsigned long ArduinoCloudProperty::getLastLocalChangeTimestamp() { - return _last_local_change_timestamp; -} - - -/****************************************************************************** - PRIVATE MEMBER FUNCTIONS - ******************************************************************************/ - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int(mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); - cbor_encode_boolean(mapEncoder, _property); -} - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int(mapEncoder, static_cast(CborIntegerMapKey::Value)); - cbor_encode_int(mapEncoder, _property); -} - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int(mapEncoder, static_cast(CborIntegerMapKey::Value)); - cbor_encode_float(mapEncoder, _property); -} - -template <> -inline void ArduinoCloudProperty::appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int(mapEncoder, static_cast(CborIntegerMapKey::StringValue)); - cbor_encode_text_stringz(mapEncoder, _property.c_str()); -} - -template <> -inline bool ArduinoCloudProperty::isValueDifferent(bool const lhs, bool const rhs) const { - return (lhs != rhs); -} - -template <> -inline bool ArduinoCloudProperty::isValueDifferent(int const lhs, int const rhs) const { - return (lhs != rhs) && (abs(lhs - rhs) >= _min_delta_property); -} - -template <> -inline bool ArduinoCloudProperty::isValueDifferent(float const lhs, float const rhs) const { - return (lhs != rhs) && (abs(lhs - rhs) >= _min_delta_property); -} - -template <> -inline bool ArduinoCloudProperty::isValueDifferent(String const lhs, String const rhs) const { - return (lhs != rhs); -} - -template <> -inline bool ArduinoCloudProperty::getInitialMinDeltaPropertyValue() const { - return false; -} - -template <> -inline int ArduinoCloudProperty::getInitialMinDeltaPropertyValue() const { - return 0; -} - -template <> -inline float ArduinoCloudProperty::getInitialMinDeltaPropertyValue() const { - return 0.0f; -} - -template <> -inline String ArduinoCloudProperty::getInitialMinDeltaPropertyValue() const { - return String(""); -} diff --git a/ArduinoCloudPropertyContainer.cpp b/ArduinoCloudPropertyContainer.cpp deleted file mode 100644 index 2c4682c66..000000000 --- a/ArduinoCloudPropertyContainer.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// -// This file is part of ArduinoCloudThing -// -// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) -// -// This software is released under the GNU General Public License version 3, -// which covers the main part of ArduinoCloudThing. -// The terms of this license can be found at: -// https://www.gnu.org/licenses/gpl-3.0.en.html -// -// You can be released from the requirements of the above licenses by purchasing -// a commercial license. Buying such a license is mandatory if you want to modify or -// otherwise use the software for commercial activities involving the Arduino -// software without disclosing the source code of your own applications. To purchase -// a commercial license, send an email to license@arduino.cc. -// - -/****************************************************************************** - INCLUDE - ******************************************************************************/ - -#include "ArduinoCloudPropertyContainer.hpp" - -/****************************************************************************** - PUBLIC MEMBER FUNCTIONS - ******************************************************************************/ - -bool ArduinoCloudPropertyContainer::isPropertyInContainer(Type const type, String const & name) { - if (type == Type::Bool) { - return isPropertyInList(_bool_property_list, name); - } else if (type == Type::Int) { - return isPropertyInList(_int_property_list, name); - } else if (type == Type::Float) { - return isPropertyInList(_float_property_list, name); - } else if (type == Type::String) { - return isPropertyInList(_string_property_list, name); - } else { - return false; - } -} - -int ArduinoCloudPropertyContainer::getNumOfChangedProperties() { - int num_changes_properties = 0; - - num_changes_properties += getNumOfChangedProperties(_bool_property_list); - num_changes_properties += getNumOfChangedProperties(_int_property_list); - num_changes_properties += getNumOfChangedProperties(_float_property_list); - num_changes_properties += getNumOfChangedProperties(_string_property_list); - - return num_changes_properties; -} - -void ArduinoCloudPropertyContainer::appendChangedProperties(CborEncoder * arrayEncoder) { - appendChangedProperties (_bool_property_list, arrayEncoder); - appendChangedProperties (_int_property_list, arrayEncoder); - appendChangedProperties (_float_property_list, arrayEncoder); - appendChangedProperties(_string_property_list, arrayEncoder); -} - - -int ArduinoCloudPropertyContainer::updateTimestampOnChangedProperties(unsigned long changeEventTime) { - int num_changes_properties = 0; - - num_changes_properties += updateTimestampOnChangedProperties(_bool_property_list, changeEventTime); - num_changes_properties += updateTimestampOnChangedProperties(_int_property_list, changeEventTime); - num_changes_properties += updateTimestampOnChangedProperties(_float_property_list, changeEventTime); - num_changes_properties += updateTimestampOnChangedProperties(_string_property_list, changeEventTime); - - return num_changes_properties; -} diff --git a/ArduinoCloudPropertyContainer.hpp b/ArduinoCloudPropertyContainer.hpp deleted file mode 100644 index 6dc02a185..000000000 --- a/ArduinoCloudPropertyContainer.hpp +++ /dev/null @@ -1,98 +0,0 @@ -// -// This file is part of ArduinoCloudThing -// -// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) -// -// This software is released under the GNU General Public License version 3, -// which covers the main part of ArduinoCloudThing. -// The terms of this license can be found at: -// https://www.gnu.org/licenses/gpl-3.0.en.html -// -// You can be released from the requirements of the above licenses by purchasing -// a commercial license. Buying such a license is mandatory if you want to modify or -// otherwise use the software for commercial activities involving the Arduino -// software without disclosing the source code of your own applications. To purchase -// a commercial license, send an email to license@arduino.cc. -// - -#ifndef ARDUINO_CLOUD_PROPERTY_CONTAINER_NEW_H_ -#define ARDUINO_CLOUD_PROPERTY_CONTAINER_NEW_H_ - -/****************************************************************************** - INCLUDE - ******************************************************************************/ - -#include "ArduinoCloudProperty.hpp" - -#include "lib/tinycbor/cbor-lib.h" -#include "lib/LinkedList/LinkedList.h" - -/****************************************************************************** - CLASS DECLARATION - ******************************************************************************/ - -class ArduinoCloudPropertyContainer { - public: - - bool isPropertyInContainer(Type const type, String const & name); - int getNumOfChangedProperties(); - int updateTimestampOnChangedProperties(unsigned long changeEventTime); - void appendChangedProperties(CborEncoder * arrayEncoder); - - inline ArduinoCloudProperty * getPropertyBool(String const & name) { - return getProperty(_bool_property_list, name); - } - inline ArduinoCloudProperty * getPropertyInt(String const & name) { - return getProperty(_int_property_list, name); - } - inline ArduinoCloudProperty * getPropertyFloat(String const & name) { - return getProperty(_float_property_list, name); - } - inline ArduinoCloudProperty * getPropertyString(String const & name) { - return getProperty(_string_property_list, name); - } - - inline void addProperty(ArduinoCloudProperty * property_obj) { - _bool_property_list.add(property_obj); - } - inline void addProperty(ArduinoCloudProperty * property_obj) { - _int_property_list.add(property_obj); - } - inline void addProperty(ArduinoCloudProperty * property_obj) { - _float_property_list.add(property_obj); - } - inline void addProperty(ArduinoCloudProperty * property_obj) { - _string_property_list.add(property_obj); - } - - private: - - LinkedList *> _bool_property_list; - LinkedList *> _int_property_list; - LinkedList *> _float_property_list; - LinkedList *> _string_property_list; - - template - bool isPropertyInList(LinkedList *> & list, String const & name); - - template - ArduinoCloudProperty * getProperty(LinkedList *> & list, String const & name); - - template - int getNumOfChangedProperties(LinkedList *> & list); - - template - int updateTimestampOnChangedProperties(LinkedList *> & list, unsigned long changeEventTime); - - template - void appendChangedProperties(LinkedList *> & list, CborEncoder * arrayEncoder); - -}; - -/****************************************************************************** - TEMPLATE IMPLEMENTATION - ******************************************************************************/ - -#include "ArduinoCloudPropertyContainer.ipp" - -#endif /* ARDUINO_CLOUD_PROPERTY_CONTAINER_NEW_H_ */ diff --git a/ArduinoCloudPropertyContainer.ipp b/ArduinoCloudPropertyContainer.ipp deleted file mode 100644 index e5026281e..000000000 --- a/ArduinoCloudPropertyContainer.ipp +++ /dev/null @@ -1,81 +0,0 @@ -// -// This file is part of ArduinoCloudThing -// -// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) -// -// This software is released under the GNU General Public License version 3, -// which covers the main part of ArduinoCloudThing. -// The terms of this license can be found at: -// https://www.gnu.org/licenses/gpl-3.0.en.html -// -// You can be released from the requirements of the above licenses by purchasing -// a commercial license. Buying such a license is mandatory if you want to modify or -// otherwise use the software for commercial activities involving the Arduino -// software without disclosing the source code of your own applications. To purchase -// a commercial license, send an email to license@arduino.cc. -// - -/****************************************************************************** - PRIVATE MEMBER FUNCTIONS - ******************************************************************************/ - -template -bool ArduinoCloudPropertyContainer::isPropertyInList(LinkedList *> & list, String const & name) { - for (int i = 0; i < list.size(); i++) { - ArduinoCloudProperty * p = list.get(i); - if (p->name() == name) { - return true; - } - } - return false; -} - -template -ArduinoCloudProperty * ArduinoCloudPropertyContainer::getProperty(LinkedList *> & list, String const & name) { - for (int i = 0; i < list.size(); i++) { - ArduinoCloudProperty * p = list.get(i); - if (p->name() == name) { - return p; - } - } - return 0; -} - -template -int ArduinoCloudPropertyContainer::getNumOfChangedProperties(LinkedList *> & list) { - int num_changes_properties = 0; - - for (int i = 0; i < list.size(); i++) { - ArduinoCloudProperty * p = list.get(i); - if (p->shouldBeUpdated() && p->isReadableByCloud()) { - num_changes_properties++; - } - } - - return num_changes_properties; -} - -template -void ArduinoCloudPropertyContainer::appendChangedProperties(LinkedList *> & list, CborEncoder * arrayEncoder) { - for (int i = 0; i < list.size(); i++) { - ArduinoCloudProperty * p = list.get(i); - if (p->shouldBeUpdated() && p->isReadableByCloud()) { - p->append(arrayEncoder); - } - } -} - -template -int ArduinoCloudPropertyContainer::updateTimestampOnChangedProperties(LinkedList *> & list, unsigned long changeEventTime) { - int num_changes_properties = 0; - - for (int i = 0; i < list.size(); i++) { - ArduinoCloudProperty * p = list.get(i); - if (p->isChangedLocally() && p->isReadableByCloud()) { - p->updateTime(changeEventTime); - } - } - - return num_changes_properties; -} - diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 857bb39c4..38b73efa4 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -23,8 +23,6 @@ #include -#include - /****************************************************************************** DEBUG FUNCTIONS ******************************************************************************/ @@ -77,83 +75,46 @@ ArduinoCloudThing::ArduinoCloudThing() { void ArduinoCloudThing::begin() { } -int ArduinoCloudThing::updateTimestampOnChangedProperties(unsigned long changeEventTime) { - return _property_cont.updateTimestampOnChangedProperties(changeEventTime); -} - int ArduinoCloudThing::encode(uint8_t * data, size_t const size) { - // check if backing storage and cloud has diverged // time interval may be elapsed or property may be changed - int const num_changed_properties = _property_cont.getNumOfChangedProperties(); - - if (num_changed_properties > 0) { - CborEncoder encoder, arrayEncoder; - - cbor_encoder_init(&encoder, data, size, 0); - - if (cbor_encoder_create_array(&encoder, &arrayEncoder, num_changed_properties) != CborNoError) { - return -1; - } + CborEncoder encoder, arrayEncoder; - _property_cont.appendChangedProperties(&arrayEncoder); + cbor_encoder_init(&encoder, data, size, 0); - if (cbor_encoder_close_container(&encoder, &arrayEncoder) != CborNoError) { - return -1; - } - - #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) - PrintFreeRam(); - #endif - int const bytes_encoded = cbor_encoder_get_buffer_size(&encoder, data); - return bytes_encoded; - } else { - return num_changed_properties; + if(cbor_encoder_create_array(&encoder, &arrayEncoder, CborIndefiniteLength) != CborNoError) { + return -1; } -} -ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(bool & property, String const & name, Permission const permission) { - if (_property_cont.isPropertyInContainer(Type::Bool, name)) { - return (*_property_cont.getPropertyBool(name)); - } else { - ArduinoCloudProperty *property_opj = new ArduinoCloudProperty(property, name, permission); - _property_cont.addProperty(property_opj); - return (*property_opj); + if (appendChangedProperties(&arrayEncoder) < 1) { + return -1; } -} -ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(int & property, String const & name, Permission const permission) { - if (_property_cont.isPropertyInContainer(Type::Int, name)) { - return (*_property_cont.getPropertyInt(name)); - } else { - ArduinoCloudProperty * property_opj = new ArduinoCloudProperty(property, name, permission); - _property_cont.addProperty(property_opj); - return (*property_opj); + if(cbor_encoder_close_container(&encoder, &arrayEncoder) != CborNoError) { + return -1; } -} -ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(float & property, String const & name, Permission const permission) { - if (_property_cont.isPropertyInContainer(Type::Float, name)) { - return (*_property_cont.getPropertyFloat(name)); - } else { - ArduinoCloudProperty * property_opj = new ArduinoCloudProperty(property, name, permission); - _property_cont.addProperty(property_opj); - return (*property_opj); - } +#if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) +PrintFreeRam(); +#endif + int const bytes_encoded = cbor_encoder_get_buffer_size(&encoder, data); + return bytes_encoded; } -ArduinoCloudProperty & ArduinoCloudThing::addPropertyReal(String & property, String const & name, Permission const permission) { - if (_property_cont.isPropertyInContainer(Type::String, name)) { - return (*_property_cont.getPropertyString(name)); - } else { - ArduinoCloudProperty * property_opj = new ArduinoCloudProperty(property, name, permission); - _property_cont.addProperty(property_opj); - return (*property_opj); +ArduinoCloudProperty& ArduinoCloudThing::addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission) { + property.init(name, permission); + if(isPropertyInContainer(name)) { + return (*getProperty(name)); + } + else { + if (property.isPrimitive()) + _numPrimitivesProperties++; + addProperty(&property); + return (property); } } void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const length, bool syncMessage) { - _syncMessage = syncMessage; CborParser parser; @@ -200,6 +161,47 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt } } +bool ArduinoCloudThing::isPropertyInContainer(String const & name) { + for (int i = 0; i < _property_list.size(); i++) { + ArduinoCloudProperty * p = _property_list.get(i); + if (p->name() == name) return true; + } + return false; +} + +int ArduinoCloudThing::appendChangedProperties(CborEncoder * arrayEncoder) { + int appendedProperties = 0; + for (int i = 0; i < _property_list.size(); i++) { + ArduinoCloudProperty * p = _property_list.get(i); + if (p->shouldBeUpdated() && p->isReadableByCloud()) { + p->append(arrayEncoder); + appendedProperties++; + } + } + return appendedProperties; +} + +ArduinoCloudProperty * ArduinoCloudThing::getProperty(String const & name) { + for (int i = 0; i < _property_list.size(); i++) { + ArduinoCloudProperty * p = _property_list.get(i); + if (p->name() == name) return p; + } + return NULL; +} + +void ArduinoCloudThing::updateTimestampOnLocallyChangedProperties() { + if (_numPrimitivesProperties == 0) { + return; + } else { + for (int i = 0; i < _property_list.size(); i++) { + ArduinoCloudProperty * p = _property_list.get(i); + if (p->isPrimitive() && p->isChangedLocally() && p->isReadableByCloud()) { + p->updateLocalTimestamp(); + } + } + } +} + /****************************************************************************** PRIVATE MEMBER FUNCTIONS ******************************************************************************/ @@ -404,62 +406,18 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * } /* Update the property containers depending on the parsed data */ - if (map_data->name.isSet()) { - /* Value (Integer/Float/Double/Half-Float) */ - if (map_data->val.isSet()) { - ArduinoCloudProperty * int_property = _property_cont.getPropertyInt(map_data->name.get()); - ArduinoCloudProperty * float_property = _property_cont.getPropertyFloat(map_data->name.get()); - - if (int_property && int_property->isWriteableByCloud()) { - if (_syncMessage) { - int_property->setLastCloudChangeTimestamp(cloudChangeEventTime); - int_property->setCloudShadowValue(map_data->val.get()); - int_property->execCallbackOnSync(); - } else { - int_property->writeByCloud(map_data->val.get()); - int_property->execCallbackOnChange(); - } - } - - if (float_property && float_property->isWriteableByCloud()) { - if (_syncMessage) { - float_property->setLastCloudChangeTimestamp(cloudChangeEventTime); - float_property->setCloudShadowValue(map_data->val.get()); - float_property->execCallbackOnSync(); - } else { - float_property->writeByCloud(map_data->val.get()); - float_property->execCallbackOnChange(); - } - } - } - - /* Value (String) */ - if (map_data->str_val.isSet()) { - ArduinoCloudProperty* string_property = _property_cont.getPropertyString(map_data->name.get()); - if (string_property && string_property->isWriteableByCloud()) { - if (_syncMessage) { - string_property->setLastCloudChangeTimestamp(cloudChangeEventTime); - string_property->setCloudShadowValue(map_data->str_val.get()); - string_property->execCallbackOnSync(); - } else { - string_property->writeByCloud(map_data->str_val.get()); - string_property->execCallbackOnChange(); - } - } - } - - /* Value (bool) */ - if (map_data->bool_val.isSet()) { - ArduinoCloudProperty* bool_property = _property_cont.getPropertyBool(map_data->name.get()); - if (bool_property && bool_property->isWriteableByCloud()) { - if (_syncMessage) { - bool_property->setLastCloudChangeTimestamp(cloudChangeEventTime); - bool_property->setCloudShadowValue(map_data->bool_val.get()); - bool_property->execCallbackOnSync(); - } else { - bool_property->writeByCloud(map_data->bool_val.get()); - bool_property->execCallbackOnChange(); - } + if(map_data->name.isSet()) + { + ArduinoCloudProperty* property = getProperty(map_data->name.get()); + + if(property && property->isWriteableByCloud()) { + if(_syncMessage) { + property->setLastCloudChangeTimestamp(cloudChangeEventTime); + property->setCloudShadowValue(map_data); + property->execCallbackOnSync(); + } else { + property->setValue(map_data); + property->execCallbackOnChange(); } } } @@ -522,3 +480,18 @@ double ArduinoCloudThing::convertCborHalfFloatToDouble(uint16_t const half_val) } return half_val & 0x8000 ? -val : val; } + +void onAutoSync(ArduinoCloudProperty & property) { + if( property.getLastCloudChangeTimestamp() > property.getLastLocalChangeTimestamp()){ + property.fromCloudShadow(); + property.execCallbackOnChange(); + } +} + +void onForceCloudSync(ArduinoCloudProperty & property) { + property.fromCloudShadow(); + property.execCallbackOnChange(); +} + +void onForceDeviceSync(ArduinoCloudProperty & property) { +} diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 5181caa08..df202a228 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -23,9 +23,11 @@ ******************************************************************************/ #include "ArduinoCloudProperty.hpp" -#include "ArduinoCloudPropertyContainer.hpp" - #include "lib/LinkedList/LinkedList.h" +#include "valuetypes/CloudBool.h" +#include "valuetypes/CloudFloat.h" +#include "valuetypes/CloudInt.h" +#include "valuetypes/CloudString.h" /****************************************************************************** CONSTANTS @@ -44,26 +46,12 @@ static long const DAYS = 86400; SYNCHRONIZATION CALLBACKS ******************************************************************************/ +void onAutoSync(ArduinoCloudProperty & property); #define MOST_RECENT_WINS onAutoSync -template -void onAutoSync(ArduinoCloudProperty property) { - if (property.getLastCloudChangeTimestamp() > property.getLastLocalChangeTimestamp()) { - property.setPropertyValue(property.getCloudShadowValue()); - property.forceCallbackOnChange(); - } -} - +void onForceCloudSync(ArduinoCloudProperty & property); #define CLOUD_WINS onForceCloudSync -template -void onForceCloudSync(ArduinoCloudProperty property) { - property.setPropertyValue(property.getCloudShadowValue()); - property.forceCallbackOnChange(); -} - +void onForceDeviceSync(ArduinoCloudProperty & property); #define DEVICE_WINS onForceDeviceSync // The device property value is already the correct one. The cloud property value will be synchronized at the next update cycle. -template -void onForceDeviceSync(ArduinoCloudProperty property) { -} /****************************************************************************** CLASS DECLARATION @@ -71,112 +59,64 @@ void onForceDeviceSync(ArduinoCloudProperty property) { class ArduinoCloudThing { - public: - ArduinoCloudThing(); - - void begin(); - - ArduinoCloudProperty & addPropertyReal(bool & property, String const & name, Permission const permission); - ArduinoCloudProperty & addPropertyReal(int & property, String const & name, Permission const permission); - ArduinoCloudProperty & addPropertyReal(float & property, String const & name, Permission const permission); - ArduinoCloudProperty & addPropertyReal(String & property, String const & name, Permission const permission); - - // compute the timestamp of the local properties changes - int updateTimestampOnChangedProperties(unsigned long changeEventTime); - /* encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer */ - int encode(uint8_t * data, size_t const size); - int encode(ArduinoCloudPropertyContainer *property_cont, uint8_t * data, size_t const size); - /* decode a CBOR payload received from the cloud */ - void decode(uint8_t const * const payload, size_t const length, bool syncMessage = false); - - - private: - - char _uuid[33]; - ArduinoCloudPropertyContainer _property_cont; - bool _syncMessage; - - enum class MapParserState { - EnterMap, - MapKey, - UndefinedKey, - BaseVersion, - BaseName, - BaseTime, - Name, - Value, - StringValue, - BooleanValue, - Time, - LeaveMap, - Complete, - Error - }; - - template - class MapEntry { - public: - - MapEntry() : _is_set(false) { } - - inline void set(T const & entry) { - _entry = entry; - _is_set = true; - } - inline T const get() const { - return _entry; - } - - inline bool isSet() const { - return _is_set; - } - inline void reset() { - _is_set = false; - } - - private: - - T _entry; - bool _is_set; - - }; - - class CborMapData { - - public: - MapEntry base_version; - MapEntry base_name; - MapEntry base_time; - MapEntry name; - MapEntry val; - MapEntry str_val; - MapEntry bool_val; - MapEntry time; - - void resetNotBase() { - name.reset(); - val.reset(); - str_val.reset(); - bool_val.reset(); - time.reset(); - } - }; - - MapParserState handle_EnterMap(CborValue * map_iter, CborValue * value_iter, CborMapData * map_data); - MapParserState handle_MapKey(CborValue * value_iter); - MapParserState handle_UndefinedKey(CborValue * value_iter); - MapParserState handle_BaseVersion(CborValue * value_iter, CborMapData * map_data); - MapParserState handle_BaseName(CborValue * value_iter, CborMapData * map_data); - MapParserState handle_BaseTime(CborValue * value_iter, CborMapData * map_data); - MapParserState handle_Name(CborValue * value_iter, CborMapData * map_data); - MapParserState handle_Value(CborValue * value_iter, CborMapData * map_data); - MapParserState handle_StringValue(CborValue * value_iter, CborMapData * map_data); - MapParserState handle_BooleanValue(CborValue * value_iter, CborMapData * map_data); - MapParserState handle_Time(CborValue * value_iter, CborMapData * map_data); - MapParserState handle_LeaveMap(CborValue * map_iter, CborValue * value_iter, CborMapData const * const map_data); - - static bool ifNumericConvertToDouble(CborValue * value_iter, double * numeric_val); - static double convertCborHalfFloatToDouble(uint16_t const half_val); +public: + ArduinoCloudThing(); + + void begin(); + + ArduinoCloudProperty & addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission); + + /* encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer */ + int encode(uint8_t * data, size_t const size); + /* decode a CBOR payload received from the cloud */ + void decode(uint8_t const * const payload, size_t const length, bool syncMessage = false); + + bool isPropertyInContainer (String const & name); + int appendChangedProperties (CborEncoder * arrayEncoder); + inline void addProperty (ArduinoCloudProperty * property_obj) { _property_list.add (property_obj); } + ArduinoCloudProperty * getProperty(String const & name); + void updateTimestampOnLocallyChangedProperties(); + +private: + LinkedList _property_list; +/* Keep track of the number of primitive properties in the Thing. If 0 it allows the early exit in updateTimestampOnLocallyChangedProperties() */ + int _numPrimitivesProperties = 0; + + char _uuid[33]; + bool _syncMessage; + + enum class MapParserState { + EnterMap, + MapKey, + UndefinedKey, + BaseVersion, + BaseName, + BaseTime, + Name, + Value, + StringValue, + BooleanValue, + Time, + LeaveMap, + Complete, + Error + }; + + MapParserState handle_EnterMap (CborValue * map_iter, CborValue * value_iter, CborMapData * map_data); + MapParserState handle_MapKey (CborValue * value_iter); + MapParserState handle_UndefinedKey (CborValue * value_iter); + MapParserState handle_BaseVersion (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_BaseName (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_BaseTime (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_Name (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_Value (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_StringValue (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_BooleanValue (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_Time (CborValue * value_iter, CborMapData * map_data); + MapParserState handle_LeaveMap (CborValue * map_iter, CborValue * value_iter, CborMapData const * const map_data); + + static bool ifNumericConvertToDouble (CborValue * value_iter, double * numeric_val); + static double convertCborHalfFloatToDouble(uint16_t const half_val); }; diff --git a/valuetypes/CloudBool.h b/valuetypes/CloudBool.h new file mode 100644 index 000000000..5799a6b68 --- /dev/null +++ b/valuetypes/CloudBool.h @@ -0,0 +1,82 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDBOOL_H_ +#define CLOUDBOOL_H_ + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include +#include "../ArduinoCloudProperty.hpp" + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + +class CloudBool : public ArduinoCloudProperty { +private: + bool _value, + _cloud_shadow_value; +public: + CloudBool() { CloudBool(false); } + CloudBool(bool v) : _value(v), _cloud_shadow_value(v) {} + operator bool() const {return _value;} + virtual bool isDifferentFromCloudShadow() { + return _value != _cloud_shadow_value; + } + virtual void toShadow() { + _cloud_shadow_value = _value; + } + virtual void fromCloudShadow() { + _value = _cloud_shadow_value; + } + virtual void appendValue(CborEncoder * mapEncoder) const { + cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); + cbor_encode_boolean(mapEncoder, _value); + } + virtual void setValue(CborMapData const * const map_data) { + _value = map_data->bool_val.get(); + } + virtual void setCloudShadowValue(CborMapData const * const map_data) { + _cloud_shadow_value = map_data->bool_val.get(); + } + //modifiers + CloudBool& operator=(bool v) { + _value = v; + updateLocalTimestamp(); + return *this; + } + CloudBool& operator=(CloudBool v) { + return operator=((bool)v); + } + //accessors + CloudBool operator!() const {return CloudBool(!_value);} + //friends +}; + + +#endif /* CLOUDBOOL_H_ */ diff --git a/valuetypes/CloudFloat.h b/valuetypes/CloudFloat.h new file mode 100644 index 000000000..fa82f2810 --- /dev/null +++ b/valuetypes/CloudFloat.h @@ -0,0 +1,100 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDFLOAT_H_ +#define CLOUDFLOAT_H_ + +#include + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include +#include "../ArduinoCloudProperty.hpp" + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + +class CloudFloat : public ArduinoCloudProperty { +private: + float _value, + _cloud_shadow_value; +public: + CloudFloat() { CloudFloat(0.0f); } + CloudFloat(float v) : _value(v), _cloud_shadow_value(v) {} + operator float() const {return _value;} + virtual bool isDifferentFromCloudShadow() { + return _value != _cloud_shadow_value && (abs(_value - _cloud_shadow_value) >= ArduinoCloudProperty::_min_delta_property); + } + virtual void toShadow() { + _cloud_shadow_value = _value; + } + virtual void fromCloudShadow() { + _value = _cloud_shadow_value; + } + virtual void appendValue(CborEncoder * mapEncoder) const { + cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); + cbor_encode_float(mapEncoder, _value); + } + virtual void setValue(CborMapData const * const map_data) { + _value = map_data->val.get(); + } + virtual void setCloudShadowValue(CborMapData const * const map_data) { + _cloud_shadow_value = map_data->val.get(); + } + //modifiers + CloudFloat& operator=(float v) { + _value = v; + updateLocalTimestamp(); + return *this; + } + CloudFloat& operator=(CloudFloat v) { + return operator=((float)v); + } + CloudFloat& operator+=(float v) {return operator=(_value+=v);} + CloudFloat& operator-=(float v) {return operator=(_value-=v);} + CloudFloat& operator*=(float v) {return operator=(_value*=v);} + CloudFloat& operator/=(float v) {return operator=(_value/=v);} + CloudFloat& operator++() {return operator=(_value++);} + CloudFloat& operator--() {return operator=(_value--);} + //friends + friend CloudFloat operator+(CloudFloat iw, CloudFloat v) {return iw+=v;} + friend CloudFloat operator+(CloudFloat iw, float v) {return iw+=v;} + friend CloudFloat operator+(float v, CloudFloat iw) {return CloudFloat(v)+=iw;} + friend CloudFloat operator-(CloudFloat iw, CloudFloat v) {return iw-=v;} + friend CloudFloat operator-(CloudFloat iw, float v) {return iw-=v;} + friend CloudFloat operator-(float v, CloudFloat iw) {return CloudFloat(v)-=iw;} + friend CloudFloat operator*(CloudFloat iw, CloudFloat v) {return iw*=v;} + friend CloudFloat operator*(CloudFloat iw, float v) {return iw*=v;} + friend CloudFloat operator*(float v, CloudFloat iw) {return CloudFloat(v)*=iw;} + friend CloudFloat operator/(CloudFloat iw, CloudFloat v) {return iw/=v;} + friend CloudFloat operator/(CloudFloat iw, float v) {return iw/=v;} + friend CloudFloat operator/(float v, CloudFloat iw) {return CloudFloat(v)/=iw;} +}; + + +#endif /* CLOUDFLOAT_H_ */ diff --git a/valuetypes/CloudInt.h b/valuetypes/CloudInt.h new file mode 100644 index 000000000..b5c3d2916 --- /dev/null +++ b/valuetypes/CloudInt.h @@ -0,0 +1,130 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDINT_H_ +#define CLOUDINT_H_ + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include +#include "../ArduinoCloudProperty.hpp" + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + +class CloudInt : public ArduinoCloudProperty { +private: + int _value, + _cloud_shadow_value; +public: + CloudInt() { CloudInt(0); } + CloudInt(int v) : _value(v), _cloud_shadow_value(v) {} + operator int() const {return _value;} + virtual bool isDifferentFromCloudShadow() { + return _value != _cloud_shadow_value && (abs(_value - _cloud_shadow_value) >= ArduinoCloudProperty::_min_delta_property); + } + virtual void toShadow() { + _cloud_shadow_value = _value; + } + virtual void fromCloudShadow() { + _value = _cloud_shadow_value; + } + virtual void appendValue(CborEncoder * mapEncoder) const { + cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); + cbor_encode_int (mapEncoder, _value); + } + virtual void setValue(CborMapData const * const map_data) { + _value = map_data->val.get(); + } + virtual void setCloudShadowValue(CborMapData const * const map_data) { + _cloud_shadow_value = map_data->val.get(); + } + //modifiers + CloudInt& operator=(int v) { + _value = v; + updateLocalTimestamp(); + return *this; + } + CloudInt& operator=(CloudInt v) { + return operator=((int)v); + } + CloudInt& operator+=(int v) {return operator=(_value+=v);} + CloudInt& operator-=(int v) {return operator=(_value-=v);} + CloudInt& operator*=(int v) {return operator=(_value*=v);} + CloudInt& operator/=(int v) {return operator=(_value/=v);} + CloudInt& operator%=(int v) {return operator=(_value%=v);} + CloudInt& operator++() {return operator=(++_value);} + CloudInt& operator--() {return operator=(--_value);} + CloudInt operator++(int) {return CloudInt(_value++);} + CloudInt operator--(int) {return CloudInt(_value--);} + CloudInt& operator&=(int v) {return operator=(_value&=v);} + CloudInt& operator|=(int v) {return operator=(_value|=v);} + CloudInt& operator^=(int v) {return operator=(_value^=v);} + CloudInt& operator<<=(int v) {return operator=(_value<<=v);} + CloudInt& operator>>=(int v) {return operator=(_value>>=v);} + //accessors + CloudInt operator+() const {return CloudInt(+_value);} + CloudInt operator-() const {return CloudInt(-_value);} + CloudInt operator!() const {return CloudInt(!_value);} + CloudInt operator~() const {return CloudInt(~_value);} + //friends + friend CloudInt operator+(CloudInt iw, CloudInt v) {return iw+=v;} + friend CloudInt operator+(CloudInt iw, int v) {return iw+=v;} + friend CloudInt operator+(int v, CloudInt iw) {return CloudInt(v)+=iw;} + friend CloudInt operator-(CloudInt iw, CloudInt v) {return iw-=v;} + friend CloudInt operator-(CloudInt iw, int v) {return iw-=v;} + friend CloudInt operator-(int v, CloudInt iw) {return CloudInt(v)-=iw;} + friend CloudInt operator*(CloudInt iw, CloudInt v) {return iw*=v;} + friend CloudInt operator*(CloudInt iw, int v) {return iw*=v;} + friend CloudInt operator*(int v, CloudInt iw) {return CloudInt(v)*=iw;} + friend CloudInt operator/(CloudInt iw, CloudInt v) {return iw/=v;} + friend CloudInt operator/(CloudInt iw, int v) {return iw/=v;} + friend CloudInt operator/(int v, CloudInt iw) {return CloudInt(v)/=iw;} + friend CloudInt operator%(CloudInt iw, CloudInt v) {return iw%=v;} + friend CloudInt operator%(CloudInt iw, int v) {return iw%=v;} + friend CloudInt operator%(int v, CloudInt iw) {return CloudInt(v)%=iw;} + friend CloudInt operator&(CloudInt iw, CloudInt v) {return iw&=v;} + friend CloudInt operator&(CloudInt iw, int v) {return iw&=v;} + friend CloudInt operator&(int v, CloudInt iw) {return CloudInt(v)&=iw;} + friend CloudInt operator|(CloudInt iw, CloudInt v) {return iw|=v;} + friend CloudInt operator|(CloudInt iw, int v) {return iw|=v;} + friend CloudInt operator|(int v, CloudInt iw) {return CloudInt(v)|=iw;} + friend CloudInt operator^(CloudInt iw, CloudInt v) {return iw^=v;} + friend CloudInt operator^(CloudInt iw, int v) {return iw^=v;} + friend CloudInt operator^(int v, CloudInt iw) {return CloudInt(v)^=iw;} + friend CloudInt operator<<(CloudInt iw, CloudInt v) {return iw<<=v;} + friend CloudInt operator<<(CloudInt iw, int v) {return iw<<=v;} + friend CloudInt operator<<(int v, CloudInt iw) {return CloudInt(v)<<=iw;} + friend CloudInt operator>>(CloudInt iw, CloudInt v) {return iw>>=v;} + friend CloudInt operator>>(CloudInt iw, int v) {return iw>>=v;} + friend CloudInt operator>>(int v, CloudInt iw) {return CloudInt(v)>>=iw;} + +}; + + +#endif /* CLOUDINT_H_ */ diff --git a/valuetypes/CloudString.h b/valuetypes/CloudString.h new file mode 100644 index 000000000..fd1c6d6ec --- /dev/null +++ b/valuetypes/CloudString.h @@ -0,0 +1,96 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDSTRING_H_ +#define CLOUDSTRING_H_ + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include +#include "../ArduinoCloudProperty.hpp" + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + +class CloudString : public ArduinoCloudProperty { +private: + String _value, + _cloud_shadow_value; +public: + CloudString() { CloudString(""); } + CloudString(const char *v) { CloudString(String(v)); } + CloudString(String v) : + _value(v), + _cloud_shadow_value(v) + { + } + virtual bool isDifferentFromCloudShadow() { + return _value != _cloud_shadow_value; + } + virtual void toShadow() { + _cloud_shadow_value = _value; + } + virtual void fromCloudShadow() { + _value = _cloud_shadow_value; + } + virtual void appendValue(CborEncoder * mapEncoder) const { + cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::StringValue)); + cbor_encode_text_stringz(mapEncoder, _value.c_str()); + } + virtual void setValue(CborMapData const * const map_data) { + _value = String(map_data->str_val.get()); + } + virtual void setCloudShadowValue(CborMapData const * const map_data) { + _cloud_shadow_value = map_data->str_val.get(); + } + //modifiers + CloudString& operator=(String v) { + _value = v; + updateLocalTimestamp(); + return *this; + } + CloudString& operator=(const char *v) { + return operator=(String(v)); + } + CloudString& operator+=(String v) { + return operator=(_value += v); + } + bool operator==(const char *c) const + { + return operator==(String(c)); + } + bool operator==(String c) const + { + return _value == c; + } + + //friends +}; + + +#endif /* CLOUDSTRING_H_ */ diff --git a/valuetypes/CloudWrapperBool.h b/valuetypes/CloudWrapperBool.h new file mode 100644 index 000000000..547b81b53 --- /dev/null +++ b/valuetypes/CloudWrapperBool.h @@ -0,0 +1,75 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDWRAPPERBOOL_H_ +#define CLOUDWRAPPERBOOL_H_ + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include +#include "../ArduinoCloudProperty.hpp" + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + +class CloudWrapperBool : public ArduinoCloudProperty { +private: + bool &_primitive_value, + _cloud_shadow_value, + _local_shadow_value; +public: + CloudWrapperBool(bool& v) : _primitive_value(v), _cloud_shadow_value(v), _local_shadow_value(v) {} + virtual bool isDifferentFromCloudShadow() { + return _primitive_value != _cloud_shadow_value; + } + virtual void toShadow() { + _cloud_shadow_value = _local_shadow_value = _primitive_value; + } + virtual void fromCloudShadow() { + _primitive_value = _cloud_shadow_value; + } + virtual void appendValue(CborEncoder * mapEncoder) const { + cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); + cbor_encode_boolean(mapEncoder, _primitive_value); + } + virtual void setValue(CborMapData const * const map_data) { + _primitive_value = map_data->bool_val.get(); + } + virtual void setCloudShadowValue(CborMapData const * const map_data) { + _cloud_shadow_value = map_data->bool_val.get(); + } + virtual bool isPrimitive() { + return true; + } + virtual bool isChangedLocally() { + return _primitive_value != _local_shadow_value; + } +}; + + +#endif /* CLOUDWRAPPERBOOL_H_ */ diff --git a/valuetypes/CloudWrapperFloat.h b/valuetypes/CloudWrapperFloat.h new file mode 100644 index 000000000..9dce81c39 --- /dev/null +++ b/valuetypes/CloudWrapperFloat.h @@ -0,0 +1,77 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDWRAPPERFLOAT_H_ +#define CLOUDWRAPPERFLOAT_H_ + +#include + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include +#include "../ArduinoCloudProperty.hpp" + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + +class CloudWrapperFloat : public ArduinoCloudProperty { +private: + float &_primitive_value, + _cloud_shadow_value, + _local_shadow_value; +public: + CloudWrapperFloat(float& v) : _primitive_value(v), _cloud_shadow_value(v), _local_shadow_value(v) {} + virtual bool isDifferentFromCloudShadow() { + return _primitive_value != _cloud_shadow_value && (abs(_primitive_value - _cloud_shadow_value) >= ArduinoCloudProperty::_min_delta_property); + } + virtual void toShadow() { + _cloud_shadow_value = _local_shadow_value = _primitive_value; + } + virtual void fromCloudShadow() { + _primitive_value = _cloud_shadow_value; + } + virtual void appendValue(CborEncoder * mapEncoder) const { + cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); + cbor_encode_float(mapEncoder, _primitive_value); + } + virtual void setValue(CborMapData const * const map_data) { + _primitive_value = map_data->val.get(); + } + virtual void setCloudShadowValue(CborMapData const * const map_data) { + _cloud_shadow_value = map_data->val.get(); + } + virtual bool isPrimitive() { + return true; + } + virtual bool isChangedLocally() { + return _primitive_value != _local_shadow_value; + } +}; + + +#endif /* CLOUWRAPPERFLOAT_H_ */ diff --git a/valuetypes/CloudWrapperInt.h b/valuetypes/CloudWrapperInt.h new file mode 100644 index 000000000..e6e368b41 --- /dev/null +++ b/valuetypes/CloudWrapperInt.h @@ -0,0 +1,75 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDWRAPPERINT_H_ +#define CLOUDWRAPPERINT_H_ + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include +#include "../ArduinoCloudProperty.hpp" + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + +class CloudWrapperInt : public ArduinoCloudProperty { +private: + int &_primitive_value, + _cloud_shadow_value, + _local_shadow_value; +public: + CloudWrapperInt(int& v) : _primitive_value(v), _cloud_shadow_value(v), _local_shadow_value(v) {} + virtual bool isDifferentFromCloudShadow() { + return _primitive_value != _cloud_shadow_value && (abs(_primitive_value - _cloud_shadow_value) >= ArduinoCloudProperty::_min_delta_property); + } + virtual void toShadow() { + _cloud_shadow_value = _local_shadow_value = _primitive_value; + } + virtual void fromCloudShadow() { + _primitive_value = _cloud_shadow_value; + } + virtual void appendValue(CborEncoder * mapEncoder) const { + cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); + cbor_encode_int (mapEncoder, _primitive_value); + } + virtual void setValue(CborMapData const * const map_data) { + _primitive_value = map_data->val.get(); + } + virtual void setCloudShadowValue(CborMapData const * const map_data) { + _cloud_shadow_value = map_data->val.get(); + } + virtual bool isPrimitive() { + return true; + } + virtual bool isChangedLocally() { + return _primitive_value != _local_shadow_value; + } +}; + + +#endif /* CLOUDWRAPPERINT_H_ */ diff --git a/valuetypes/CloudWrapperString.h b/valuetypes/CloudWrapperString.h new file mode 100644 index 000000000..7f3276826 --- /dev/null +++ b/valuetypes/CloudWrapperString.h @@ -0,0 +1,79 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDWRAPPERSTRING_H_ +#define CLOUDWRAPPERSTRING_H_ + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include +#include "../ArduinoCloudProperty.hpp" + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * TYPEDEF + ******************************************************************************/ + +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + +class CloudWrapperString : public ArduinoCloudProperty { +private: + String &_primitive_value, + _cloud_shadow_value, + _local_shadow_value; +public: + CloudWrapperString(String& v) : + _primitive_value(v), + _cloud_shadow_value(v), + _local_shadow_value(v) { + } + virtual bool isDifferentFromCloudShadow() { + return _primitive_value != _cloud_shadow_value; + } + virtual void toShadow() { + _cloud_shadow_value = _local_shadow_value = _primitive_value; + } + virtual void fromCloudShadow() { + _primitive_value = _cloud_shadow_value; + } + virtual void appendValue(CborEncoder * mapEncoder) const { + cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::StringValue)); + cbor_encode_text_stringz(mapEncoder, _primitive_value.c_str()); + } + virtual void setValue(CborMapData const * const map_data) { + _primitive_value = map_data->str_val.get(); + } + virtual void setCloudShadowValue(CborMapData const * const map_data) { + _cloud_shadow_value = map_data->str_val.get(); + } + virtual bool isPrimitive() { + return true; + } + virtual bool isChangedLocally() { + return _primitive_value != _local_shadow_value; + } +}; + + +#endif /* CLOUDWRAPPERSTRING_H_ */ From 476a56c6a2238d789d2c6cad7fee9ce92809d3a6 Mon Sep 17 00:00:00 2001 From: mirkokurt Date: Tue, 9 Apr 2019 16:51:31 +0200 Subject: [PATCH 130/175] Multi value properties implementation --- ArduinoCloudProperty.cpp | 117 ++++++++--- ArduinoCloudProperty.hpp | 58 ++++-- ArduinoCloudThing.cpp | 187 ++++++++++-------- ArduinoCloudThing.h | 37 ++-- {valuetypes => types}/CloudBool.h | 36 ++-- types/CloudColor.h | 216 +++++++++++++++++++++ {valuetypes => types}/CloudFloat.h | 59 +++--- {valuetypes => types}/CloudInt.h | 40 ++-- types/CloudLocation.h | 100 ++++++++++ {valuetypes => types}/CloudString.h | 45 ++--- {valuetypes => types}/CloudWrapperBool.h | 40 ++-- {valuetypes => types}/CloudWrapperFloat.h | 40 ++-- {valuetypes => types}/CloudWrapperInt.h | 40 ++-- {valuetypes => types}/CloudWrapperString.h | 42 ++-- 14 files changed, 710 insertions(+), 347 deletions(-) rename {valuetypes => types}/CloudBool.h (61%) create mode 100644 types/CloudColor.h rename {valuetypes => types}/CloudFloat.h (57%) rename {valuetypes => types}/CloudInt.h (78%) create mode 100644 types/CloudLocation.h rename {valuetypes => types}/CloudString.h (63%) rename {valuetypes => types}/CloudWrapperBool.h (53%) rename {valuetypes => types}/CloudWrapperFloat.h (52%) rename {valuetypes => types}/CloudWrapperInt.h (51%) rename {valuetypes => types}/CloudWrapperString.h (54%) diff --git a/ArduinoCloudProperty.cpp b/ArduinoCloudProperty.cpp index c30eca090..cd8333963 100644 --- a/ArduinoCloudProperty.cpp +++ b/ArduinoCloudProperty.cpp @@ -46,7 +46,9 @@ ArduinoCloudProperty::ArduinoCloudProperty() _last_updated_millis(0), _update_interval_millis(0), _last_local_change_timestamp(0), - _last_cloud_change_timestamp(0) + _last_cloud_change_timestamp(0), + _encoder(nullptr), + _map_data_list(nullptr) { } @@ -90,7 +92,7 @@ bool ArduinoCloudProperty::shouldBeUpdated() { } if (_update_policy == UpdatePolicy::OnChange) { - return (isDifferentFromCloudShadow() && ((millis() - _last_updated_millis) >= (_min_time_between_updates_millis))); + return (isDifferentFromCloud() && ((millis() - _last_updated_millis) >= (_min_time_between_updates_millis))); } else if(_update_policy == UpdatePolicy::TimeInterval) { return ((millis() - _last_updated_millis) >= _update_interval_millis); @@ -104,7 +106,7 @@ void ArduinoCloudProperty::execCallbackOnChange() { if(_update_callback_func != NULL) { _update_callback_func(); } - if(!isDifferentFromCloudShadow()) { + if(!isDifferentFromCloud()) { _has_been_modified_in_callback = true; } } @@ -115,32 +117,106 @@ void ArduinoCloudProperty::execCallbackOnSync() { } } -void ArduinoCloudProperty::append(CborEncoder * encoder) { - if (isReadableByCloud()) { - CborEncoder mapEncoder; - - cbor_encoder_create_map (encoder, &mapEncoder, CborIndefiniteLength); - cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::Name)); - cbor_encode_text_stringz (&mapEncoder, _name.c_str()); - appendValue (&mapEncoder); - cbor_encoder_close_container(encoder, &mapEncoder); - - toShadow(); - - _has_been_updated_once = true; - _last_updated_millis = millis(); +void ArduinoCloudProperty::append(CborEncoder *encoder) { + _encoder = encoder; + appendAttributesToCloud(); + fromLocalToCloud(); + _has_been_updated_once = true; + _last_updated_millis = millis(); +} + +void ArduinoCloudProperty::appendAttributeReal(bool value, String attributeName) { + appendAttributeName(attributeName, [value](CborEncoder& mapEncoder) { + cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); + cbor_encode_boolean(&mapEncoder, value); + }); +} + +void ArduinoCloudProperty::appendAttributeReal(int value, String attributeName) { + appendAttributeName(attributeName, [value](CborEncoder& mapEncoder) { + cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::Value)); + cbor_encode_int(&mapEncoder, value); + }); +} + +void ArduinoCloudProperty::appendAttributeReal(float value, String attributeName) { + appendAttributeName(attributeName, [value](CborEncoder& mapEncoder) { + cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::Value)); + cbor_encode_float(&mapEncoder, value); + }); +} + +void ArduinoCloudProperty::appendAttributeReal(String value, String attributeName) { + appendAttributeName(attributeName, [value](CborEncoder& mapEncoder) { + cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::StringValue)); + cbor_encode_text_stringz(&mapEncoder, value.c_str()); + }); +} + +void ArduinoCloudProperty::appendAttributeName(String attributeName, std::functionappendValue){ + CborEncoder mapEncoder; + cbor_encoder_create_map(_encoder, &mapEncoder, 2); + cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Name)); + String completeName = _name; + if(attributeName != ""){ + completeName += ":" + attributeName; + } + cbor_encode_text_stringz(&mapEncoder, completeName.c_str()); + appendValue(mapEncoder); + cbor_encoder_close_container(_encoder, &mapEncoder); +} + +void ArduinoCloudProperty::setAttributesFromCloud(LinkedList *map_data_list) { + _map_data_list = map_data_list; + setAttributesFromCloud(); +} + +void ArduinoCloudProperty::setAttributeReal(bool& value, String attributeName) { + setAttributeReal(attributeName, [&value](CborMapData *md) {value = md->bool_val.get(); }); +} + +void ArduinoCloudProperty::setAttributeReal(int& value, String attributeName) { + setAttributeReal(attributeName, [&value](CborMapData *md) { value = md->val.get(); }); +} + +void ArduinoCloudProperty::setAttributeReal(float& value, String attributeName) { + setAttributeReal(attributeName, [&value](CborMapData *md) { value = md->val.get(); }); +} + +void ArduinoCloudProperty::setAttributeReal(String& value, String attributeName) { + setAttributeReal(attributeName, [&value](CborMapData *md) { value = md->str_val.get(); }); +} + +void ArduinoCloudProperty::setAttributeReal(String attributeName, std::functionsetValue) { + for (int i = 0; i < _map_data_list->size(); i++) { + CborMapData *map = _map_data_list->get(i); + if (map != nullptr) { + String an = map->attribute_name.get(); + if (an == attributeName) { + setValue(map); + break; + } + } } } + +String ArduinoCloudProperty::getAttributeName(String propertyName, char separator) { + int colonPos; + String attributeName = ""; + (colonPos = propertyName.indexOf(separator)) != -1 ? attributeName = propertyName.substring(colonPos + 1) : ""; + return attributeName; +} + void ArduinoCloudProperty::updateLocalTimestamp() { if (isReadableByCloud()) { _last_local_change_timestamp = getTimestamp(); -// ArduinoCloud.update(); } } void ArduinoCloudProperty::setLastCloudChangeTimestamp(unsigned long cloudChangeEventTime) { _last_cloud_change_timestamp = cloudChangeEventTime; } + void ArduinoCloudProperty::setLastLocalChangeTimestamp(unsigned long localChangeTime) { _last_local_change_timestamp = localChangeTime; } @@ -152,8 +228,3 @@ unsigned long ArduinoCloudProperty::getLastCloudChangeTimestamp() { unsigned long ArduinoCloudProperty::getLastLocalChangeTimestamp() { return _last_local_change_timestamp; } - - -/****************************************************************************** - * PRIVATE MEMBER FUNCTIONS - ******************************************************************************/ diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 417aeb459..3ef37caf0 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -18,16 +18,27 @@ #ifndef ARDUINO_CLOUD_PROPERTY_HPP_ #define ARDUINO_CLOUD_PROPERTY_HPP_ +#ifdef HOST_BUILD +#define substring(...) substr(__VA_ARGS__) +#define indexOf(x) find(x) +#endif + /****************************************************************************** INCLUDE ******************************************************************************/ + #include +// in order to allow to define its own max and min functions +#undef max +#undef min +#include + #include "lib/tinycbor/cbor-lib.h" +#include "lib/LinkedList/LinkedList.h" -/****************************************************************************** - TYPEDEF - ******************************************************************************/ +#define appendAttribute(x) appendAttributeReal(x, getAttributeName(#x, '.')) +#define setAttribute(x) setAttributeReal(x, getAttributeName(#x, '.')) /****************************************************************************** * ENUM @@ -77,18 +88,11 @@ class CborMapData { MapEntry base_name; MapEntry base_time; MapEntry name; + MapEntry attribute_name; MapEntry val; MapEntry str_val; MapEntry bool_val; MapEntry time; - - void resetNotBase() { - name.reset (); - val.reset (); - str_val.reset (); - bool_val.reset (); - time.reset (); - } }; enum class Permission { @@ -134,22 +138,33 @@ class ArduinoCloudProperty { void updateLocalTimestamp (); void append (CborEncoder * encoder); - - virtual bool isDifferentFromCloudShadow() = 0; - virtual void toShadow() = 0; - virtual void fromCloudShadow() = 0; - virtual void appendValue(CborEncoder * mapEncoder) const = 0; - virtual void setValue(CborMapData const * const map_data) = 0; - virtual void setCloudShadowValue(CborMapData const * const map_data) = 0; + void appendAttributeReal (bool value, String attributeName = ""); + void appendAttributeReal (int value, String attributeName = ""); + void appendAttributeReal (float value, String attributeName = ""); + void appendAttributeReal (String value, String attributeName = ""); + void appendAttributeName (String attributeName, std::functionf); + void setAttributesFromCloud (LinkedList *map_data_list); + void setAttributeReal (bool& value, String attributeName = ""); + void setAttributeReal (int& value, String attributeName = ""); + void setAttributeReal (float& value, String attributeName = ""); + void setAttributeReal (String& value, String attributeName = ""); + void setAttributeReal (String attributeName, std::functionsetValue); + String getAttributeName (String propertyName, char separator); + + virtual bool isDifferentFromCloud() = 0; + virtual void fromCloudToLocal() = 0; + virtual void fromLocalToCloud() = 0; + virtual void appendAttributesToCloud() = 0; + virtual void setAttributesFromCloud() = 0; virtual bool isPrimitive() { return false; }; virtual bool isChangedLocally() { return false; }; protected: /* Variables used for UpdatePolicy::OnChange */ float _min_delta_property; unsigned long _min_time_between_updates_millis; + String _name; private: - String _name; Permission _permission; UpdateCallbackFunc _update_callback_func; void (*_sync_callback_func)(ArduinoCloudProperty &property); @@ -163,7 +178,10 @@ class ArduinoCloudProperty { /* Variables used for reconnection sync*/ unsigned long _last_local_change_timestamp; unsigned long _last_cloud_change_timestamp; - + /* variable to manage property serialization/deserialization on CBOR */ + CborEncoder *_encoder; + LinkedList + *_map_data_list; }; /****************************************************************************** diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 38b73efa4..af1c7f74c 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -29,19 +29,10 @@ #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) extern "C" char *sbrk(int i); -void PrintFreeRam(void) { - char stack_dummy = 0; - Serial.print("Free RAM: "); Serial.println(&stack_dummy - sbrk(0)); -} -#endif - -#ifdef ARDUINO_ARCH_SAMD -static void utox8(uint32_t val, char* s) { - for (int i = 0; i < 8; i++) { - int d = val & 0XF; - val = (val >> 4); - s[7 - i] = d > 9 ? 'A' + d - 10 : '0' + d; - } +void PrintFreeRam (void) +{ + char stack_dummy = 0; + //Serial.print("Free RAM: "); //Serial.println(&stack_dummy - sbrk(0)); } #endif @@ -53,20 +44,13 @@ static void utox8(uint32_t val, char* s) { CTOR/DTOR ******************************************************************************/ -ArduinoCloudThing::ArduinoCloudThing() { - #ifdef ARDUINO_ARCH_SAMD -#define SERIAL_NUMBER_WORD_0 *(volatile uint32_t*)(0x0080A00C) -#define SERIAL_NUMBER_WORD_1 *(volatile uint32_t*)(0x0080A040) -#define SERIAL_NUMBER_WORD_2 *(volatile uint32_t*)(0x0080A044) -#define SERIAL_NUMBER_WORD_3 *(volatile uint32_t*)(0x0080A048) - - utox8(SERIAL_NUMBER_WORD_0, &_uuid[0]); - utox8(SERIAL_NUMBER_WORD_1, &_uuid[8]); - utox8(SERIAL_NUMBER_WORD_2, &_uuid[16]); - utox8(SERIAL_NUMBER_WORD_3, &_uuid[24]); - _uuid[32] = '\0'; - #endif -} +ArduinoCloudThing::ArduinoCloudThing() : +_numPrimitivesProperties(0), +_isSyncMessage(false), +_currentPropertyName(""), +_currentPropertyTime(0), +_currentPropertyBaseTime(0) +{} /****************************************************************************** PUBLIC MEMBER FUNCTIONS @@ -114,8 +98,8 @@ ArduinoCloudProperty& ArduinoCloudThing::addPropertyReal(ArduinoCloudProperty & } } -void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const length, bool syncMessage) { - _syncMessage = syncMessage; +void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const length, bool isSyncMessage) { + _isSyncMessage = isSyncMessage; CborParser parser; CborValue array_iter, @@ -134,27 +118,31 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt return; } - CborMapData map_data; + CborMapData *map_data = nullptr; + + _map_data_list.clear(); + _currentPropertyName = ""; + MapParserState current_state = MapParserState::EnterMap, next_state; - while (current_state != MapParserState::Complete) { - - switch (current_state) { - case MapParserState::EnterMap : next_state = handle_EnterMap(&map_iter, &value_iter, &map_data); break; - case MapParserState::MapKey : next_state = handle_MapKey(&value_iter); break; - case MapParserState::UndefinedKey : next_state = handle_UndefinedKey(&value_iter); break; - case MapParserState::BaseVersion : next_state = handle_BaseVersion(&value_iter, &map_data); break; - case MapParserState::BaseName : next_state = handle_BaseName(&value_iter, &map_data); break; - case MapParserState::BaseTime : next_state = handle_BaseTime(&value_iter, &map_data); break; - case MapParserState::Time : next_state = handle_Time(&value_iter, &map_data); break; - case MapParserState::Name : next_state = handle_Name(&value_iter, &map_data); break; - case MapParserState::Value : next_state = handle_Value(&value_iter, &map_data); break; - case MapParserState::StringValue : next_state = handle_StringValue(&value_iter, &map_data); break; - case MapParserState::BooleanValue : next_state = handle_BooleanValue(&value_iter, &map_data); break; - case MapParserState::LeaveMap : next_state = handle_LeaveMap(&map_iter, &value_iter, &map_data); break; - case MapParserState::Complete : /* Nothing to do */ break; - case MapParserState::Error : return; break; + while(current_state != MapParserState::Complete) { + + switch(current_state) { + case MapParserState::EnterMap : next_state = handle_EnterMap (&map_iter, &value_iter, &map_data); break; + case MapParserState::MapKey : next_state = handle_MapKey (&value_iter ); break; + case MapParserState::UndefinedKey : next_state = handle_UndefinedKey (&value_iter ); break; + case MapParserState::BaseVersion : next_state = handle_BaseVersion (&value_iter, map_data ); break; + case MapParserState::BaseName : next_state = handle_BaseName (&value_iter, map_data ); break; + case MapParserState::BaseTime : next_state = handle_BaseTime (&value_iter, map_data ); break; + case MapParserState::Time : next_state = handle_Time (&value_iter, map_data ); break; + case MapParserState::Name : next_state = handle_Name (&value_iter, map_data ); break; + case MapParserState::Value : next_state = handle_Value (&value_iter, map_data ); break; + case MapParserState::StringValue : next_state = handle_StringValue (&value_iter, map_data ); break; + case MapParserState::BooleanValue : next_state = handle_BooleanValue (&value_iter, map_data ); break; + case MapParserState::LeaveMap : next_state = handle_LeaveMap (&map_iter, &value_iter, map_data); break; + case MapParserState::Complete : /* Nothing to do */ break; + case MapParserState::Error : return; break; } current_state = next_state; @@ -206,12 +194,12 @@ void ArduinoCloudThing::updateTimestampOnLocallyChangedProperties() { PRIVATE MEMBER FUNCTIONS ******************************************************************************/ -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_EnterMap(CborValue * map_iter, CborValue * value_iter, CborMapData * map_data) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_EnterMap(CborValue * map_iter, CborValue * value_iter, CborMapData **map_data) { MapParserState next_state = MapParserState::Error; - if (cbor_value_get_type(map_iter) == CborMapType) { - if (cbor_value_enter_container(map_iter, value_iter) == CborNoError) { - map_data->resetNotBase(); + if(cbor_value_get_type(map_iter) == CborMapType) { + if(cbor_value_enter_container(map_iter, value_iter) == CborNoError) { + *map_data = new CborMapData(); next_state = MapParserState::MapKey; } } @@ -319,12 +307,18 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseTime(CborValue * ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Name(CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; - if (cbor_value_is_text_string(value_iter)) { - char * val = 0; + if(cbor_value_is_text_string(value_iter)) { + char * val = nullptr; size_t val_size = 0; - if (cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { - map_data->name.set(val); + if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { + String name = val; free(val); + map_data->name.set(name); + int colonPos = name.indexOf(":"); + String attribute_name = ""; + if(colonPos != -1) + attribute_name = name.substring(colonPos + 1); + map_data->attribute_name.set(attribute_name); next_state = MapParserState::MapKey; } } @@ -393,41 +387,46 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * val return next_state; } -ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * map_iter, CborValue * value_iter, CborMapData const * const map_data) { +ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * map_iter, CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; - - //compute the cloud event change time - unsigned long cloudChangeEventTime = 0; - if (map_data->base_time.isSet()) { - cloudChangeEventTime = (unsigned long)(map_data->base_time.get()); - } - if (map_data->time.isSet()) { - cloudChangeEventTime += (unsigned long)map_data->time.get(); - } - /* Update the property containers depending on the parsed data */ - if(map_data->name.isSet()) { - ArduinoCloudProperty* property = getProperty(map_data->name.get()); - - if(property && property->isWriteableByCloud()) { - if(_syncMessage) { - property->setLastCloudChangeTimestamp(cloudChangeEventTime); - property->setCloudShadowValue(map_data); - property->execCallbackOnSync(); - } else { - property->setValue(map_data); - property->execCallbackOnChange(); - } + String propertyName; + int colonPos = map_data->name.get().indexOf(":"); + if(colonPos != -1) { + propertyName = map_data->name.get().substring(0, colonPos); + } else { + propertyName = map_data->name.get(); + } + + if (_currentPropertyName != "" && propertyName != _currentPropertyName) { + + /* Update the property containers depending on the parsed data */ + updateProperty(_currentPropertyName, _currentPropertyBaseTime + _currentPropertyTime); + /* Reset current property data */ + freeMapDataList(&_map_data_list); + _map_data_list.clear(); + _currentPropertyBaseTime = 0; + _currentPropertyTime = 0; + } + /* Compute the cloud change event baseTime and Time */ + if(map_data->base_time.isSet()){ + _currentPropertyBaseTime = (unsigned long)(map_data->base_time.get()); + } + if(map_data->time.isSet() && (map_data->time.get() > _currentPropertyTime)){ + _currentPropertyTime = (unsigned long)map_data->time.get(); } + _map_data_list.add(map_data); + _currentPropertyName = propertyName; } /* Transition into the next map if available, otherwise finish */ - - if (cbor_value_leave_container(map_iter, value_iter) == CborNoError) { - if (!cbor_value_at_end(map_iter)) { + if(cbor_value_leave_container(map_iter, value_iter) == CborNoError) { + if(!cbor_value_at_end(map_iter)) { next_state = MapParserState::EnterMap; - } else { + } + else { + updateProperty(_currentPropertyName, _currentPropertyBaseTime + _currentPropertyTime); next_state = MapParserState::Complete; } } @@ -435,6 +434,26 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * return next_state; } +void ArduinoCloudThing::freeMapDataList(LinkedList *map_data_list){ + while(map_data_list->size() > 0){ + CborMapData const * mapData = map_data_list->pop(); + delete mapData; + } +} + +void ArduinoCloudThing::updateProperty(String propertyName, unsigned long cloudChangeEventTime) { + ArduinoCloudProperty* property = getProperty(propertyName); + if(property && property->isWriteableByCloud()) { + property->setLastCloudChangeTimestamp(cloudChangeEventTime); + property->setAttributesFromCloud(&_map_data_list); + if(_isSyncMessage) { + property->execCallbackOnSync(); + } else { + property->fromCloudToLocal(); + property->execCallbackOnChange(); + } + } +} bool ArduinoCloudThing::ifNumericConvertToDouble(CborValue * value_iter, double * numeric_val) { if (cbor_value_is_integer(value_iter)) { @@ -483,13 +502,13 @@ double ArduinoCloudThing::convertCborHalfFloatToDouble(uint16_t const half_val) void onAutoSync(ArduinoCloudProperty & property) { if( property.getLastCloudChangeTimestamp() > property.getLastLocalChangeTimestamp()){ - property.fromCloudShadow(); + property.fromCloudToLocal(); property.execCallbackOnChange(); } } void onForceCloudSync(ArduinoCloudProperty & property) { - property.fromCloudShadow(); + property.fromCloudToLocal(); property.execCallbackOnChange(); } diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index df202a228..01f572351 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -24,10 +24,12 @@ #include "ArduinoCloudProperty.hpp" #include "lib/LinkedList/LinkedList.h" -#include "valuetypes/CloudBool.h" -#include "valuetypes/CloudFloat.h" -#include "valuetypes/CloudInt.h" -#include "valuetypes/CloudString.h" +#include "types/CloudBool.h" +#include "types/CloudFloat.h" +#include "types/CloudInt.h" +#include "types/CloudString.h" +#include "types/CloudLocation.h" +#include "types/CloudColor.h" /****************************************************************************** CONSTANTS @@ -69,22 +71,28 @@ class ArduinoCloudThing { /* encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer */ int encode(uint8_t * data, size_t const size); /* decode a CBOR payload received from the cloud */ - void decode(uint8_t const * const payload, size_t const length, bool syncMessage = false); + void decode(uint8_t const * const payload, size_t const length, bool isSyncMessage = false); bool isPropertyInContainer (String const & name); int appendChangedProperties (CborEncoder * arrayEncoder); inline void addProperty (ArduinoCloudProperty * property_obj) { _property_list.add (property_obj); } ArduinoCloudProperty * getProperty(String const & name); void updateTimestampOnLocallyChangedProperties(); + void updateProperty(String propertyName, unsigned long cloudChangeEventTime); private: - LinkedList _property_list; -/* Keep track of the number of primitive properties in the Thing. If 0 it allows the early exit in updateTimestampOnLocallyChangedProperties() */ - int _numPrimitivesProperties = 0; - - char _uuid[33]; - bool _syncMessage; - + LinkedList _property_list; + /* Keep track of the number of primitive properties in the Thing. If 0 it allows the early exit in updateTimestampOnLocallyChangedProperties() */ + int _numPrimitivesProperties; + /* Indicates the if the message received to be decoded is a response to the getLastValues inquiry */ + bool _isSyncMessage; + /* List of map data that will hold all the attributes of a property */ + LinkedList _map_data_list; + /* Current property name during decoding: use to look for a new property in the senml value array */ + String _currentPropertyName; + unsigned long _currentPropertyBaseTime, + _currentPropertyTime; + enum class MapParserState { EnterMap, MapKey, @@ -102,7 +110,7 @@ class ArduinoCloudThing { Error }; - MapParserState handle_EnterMap (CborValue * map_iter, CborValue * value_iter, CborMapData * map_data); + MapParserState handle_EnterMap (CborValue * map_iter, CborValue * value_iter, CborMapData **map_data); MapParserState handle_MapKey (CborValue * value_iter); MapParserState handle_UndefinedKey (CborValue * value_iter); MapParserState handle_BaseVersion (CborValue * value_iter, CborMapData * map_data); @@ -113,10 +121,11 @@ class ArduinoCloudThing { MapParserState handle_StringValue (CborValue * value_iter, CborMapData * map_data); MapParserState handle_BooleanValue (CborValue * value_iter, CborMapData * map_data); MapParserState handle_Time (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_LeaveMap (CborValue * map_iter, CborValue * value_iter, CborMapData const * const map_data); + MapParserState handle_LeaveMap (CborValue * map_iter, CborValue * value_iter, CborMapData * map_data); static bool ifNumericConvertToDouble (CborValue * value_iter, double * numeric_val); static double convertCborHalfFloatToDouble(uint16_t const half_val); + void freeMapDataList(LinkedList *map_data_list); }; diff --git a/valuetypes/CloudBool.h b/types/CloudBool.h similarity index 61% rename from valuetypes/CloudBool.h rename to types/CloudBool.h index 5799a6b68..973491b92 100644 --- a/valuetypes/CloudBool.h +++ b/types/CloudBool.h @@ -26,43 +26,33 @@ #include "../ArduinoCloudProperty.hpp" /****************************************************************************** - * TYPEDEF + * CLASS DECLARATION ******************************************************************************/ -/****************************************************************************** - * TYPEDEF - ******************************************************************************/ -/****************************************************************************** - * CLASS DECLARATION - ******************************************************************************/ class CloudBool : public ArduinoCloudProperty { private: bool _value, - _cloud_shadow_value; + _cloud_value; public: CloudBool() { CloudBool(false); } - CloudBool(bool v) : _value(v), _cloud_shadow_value(v) {} + CloudBool(bool v) : _value(v), _cloud_value(v) {} operator bool() const {return _value;} - virtual bool isDifferentFromCloudShadow() { - return _value != _cloud_shadow_value; - } - virtual void toShadow() { - _cloud_shadow_value = _value; + virtual bool isDifferentFromCloud() { + return _value != _cloud_value; } - virtual void fromCloudShadow() { - _value = _cloud_shadow_value; + virtual void fromCloudToLocal() { + _value = _cloud_value; } - virtual void appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); - cbor_encode_boolean(mapEncoder, _value); + virtual void fromLocalToCloud() { + _cloud_value = _value; } - virtual void setValue(CborMapData const * const map_data) { - _value = map_data->bool_val.get(); + virtual void appendAttributesToCloud() { + appendAttribute(_value); } - virtual void setCloudShadowValue(CborMapData const * const map_data) { - _cloud_shadow_value = map_data->bool_val.get(); + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); } //modifiers CloudBool& operator=(bool v) { diff --git a/types/CloudColor.h b/types/CloudColor.h new file mode 100644 index 000000000..ddc68fdfc --- /dev/null +++ b/types/CloudColor.h @@ -0,0 +1,216 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDCOLOR_H_ +#define CLOUDCOLOR_H_ + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include +#include +#include "../ArduinoCloudProperty.hpp" + + /****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + +class Color { +public: + float hue, sat, bri; + Color(float h, float s, float b):hue(h), sat(s),bri(b) { + setColorHSB(h, s, b); + } + + bool setColorHSB(float h, float s, float b) { + if (h < 0 || h>360 || s < 0 || s>100 || b < 0 || b>100) { + hue = 0; + sat = 0; + bri = 0; + return false; + } + + hue = h; + sat = s; + bri = b; + return true; + } + + bool setColorRGB(uint8_t R, uint8_t G, uint8_t B) { + if (R < 0 || R>255 || G < 0 || G>255 || B < 0 || B>255) + return false; + float temp[3]; + float max, min, delta; + uint8_t imax, imin; + temp[0] = (float)R / 255; + temp[1] = (float)G / 255; + temp[2] = (float)B / 255; + max = temp[0]; + imax = 0; + min = temp[0]; + imin = 0; + + for (uint8_t j = 0; j < 3; j++) { + + if (temp[j] > max) { + max = temp[j]; + imax = j; + } + if (temp[j] < min) { + min = temp[j]; + imin = j; + } + } + delta = max - min; + if (delta == 0) { + hue = 0; + } + else if (imax == 0) { + uint8_t div = (temp[1] - temp[2]) / delta; + hue = 60 * (div % 6); + } + else if (imax == 1) { + hue = 60 * (((temp[2] - temp[0]) / delta) + 2); + } + else if (imax == 2) { + hue = 60 * (((temp[0] - temp[1]) / delta) + 4); + } + + if (max == 0) { + sat = 0; + } + else { + sat = (delta / max) * 100; + } + + bri = max * 100; + return true; + } + + void getRGB(uint8_t& R, uint8_t& G, uint8_t& B) { + float fC = (bri / 100) * (sat / 100); + float fHPrime = fmod(hue / 60.0, 6); + float fX = fC * (1 - fabs(fmod(fHPrime, 2) - 1)); + float fM = (bri / 100) - fC; + float fR, fG, fB; + + if (0 <= fHPrime && fHPrime < 1) { + fR = fC; + fG = fX; + fB = 0; + } + else if (1 <= fHPrime && fHPrime < 2) { + fR = fX; + fG = fC; + fB = 0; + } + else if (2 <= fHPrime && fHPrime < 3) { + fR = 0; + fG = fC; + fB = fX; + } + else if (3 <= fHPrime && fHPrime < 4) { + fR = 0; + fG = fX; + fB = fC; + } + else if (4 <= fHPrime && fHPrime < 5) { + fR = fX; + fG = 0; + fB = fC; + } + else if (5 <= fHPrime && fHPrime < 6) { + fR = fC; + fG = 0; + fB = fX; + } + else { + fR = 0; + fG = 0; + fB = 0; + } + R = lrint((fR + fM) * 255); + G = lrint((fG + fM) * 255); + B = lrint((fB + fM) * 255); + } + + Color& operator=(Color & aColor) { + hue = aColor.hue; + sat = aColor.sat; + bri = aColor.bri; + return *this; + } + + bool operator==(Color & aColor) { + return hue == aColor.hue && sat == aColor.sat && bri == aColor.bri; + } + + bool operator!=(Color & aColor) { + return !(operator==(aColor)); + } + +}; + +class CloudColor : public ArduinoCloudProperty { +private: + Color _value, + _cloud_value; +public: + CloudColor() : _value(0, 0, 0), _cloud_value(0, 0, 0) {} + CloudColor(float hue, float saturation, float brightness) : _value(hue, saturation, brightness), _cloud_value(hue, saturation, brightness) {} + + virtual bool isDifferentFromCloud() { + + return _value != _cloud_value; + } + + CloudColor& operator=(Color aColor) { + _value.hue = aColor.hue; + _value.sat = aColor.sat; + _value.sat = aColor.bri; + updateLocalTimestamp(); + return *this; + } + + Color getCloudValue() { + return _cloud_value; + } + + Color getValue() { + return _value; + } + + virtual void fromCloudToLocal() { + _value = _cloud_value; + } + virtual void fromLocalToCloud() { + _cloud_value = _value; + } + virtual void appendAttributesToCloud() { + appendAttribute(_value.hue); + appendAttribute(_value.sat); + appendAttribute(_value.bri); + } + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value.hue); + setAttribute(_cloud_value.sat); + setAttribute(_cloud_value.bri); + } +}; + +#endif /* CLOUDCOLOR_H_ */ \ No newline at end of file diff --git a/valuetypes/CloudFloat.h b/types/CloudFloat.h similarity index 57% rename from valuetypes/CloudFloat.h rename to types/CloudFloat.h index fa82f2810..c403980fb 100644 --- a/valuetypes/CloudFloat.h +++ b/types/CloudFloat.h @@ -28,43 +28,33 @@ #include "../ArduinoCloudProperty.hpp" /****************************************************************************** - * TYPEDEF + * CLASS DECLARATION ******************************************************************************/ -/****************************************************************************** - * TYPEDEF - ******************************************************************************/ -/****************************************************************************** - * CLASS DECLARATION - ******************************************************************************/ class CloudFloat : public ArduinoCloudProperty { private: float _value, - _cloud_shadow_value; + _cloud_value; public: CloudFloat() { CloudFloat(0.0f); } - CloudFloat(float v) : _value(v), _cloud_shadow_value(v) {} + CloudFloat(float v) : _value(v), _cloud_value(v) {} operator float() const {return _value;} - virtual bool isDifferentFromCloudShadow() { - return _value != _cloud_shadow_value && (abs(_value - _cloud_shadow_value) >= ArduinoCloudProperty::_min_delta_property); + virtual bool isDifferentFromCloud() { + return _value != _cloud_value && (abs(_value - _cloud_value) >= ArduinoCloudProperty::_min_delta_property); } - virtual void toShadow() { - _cloud_shadow_value = _value; + virtual void fromCloudToLocal() { + _value = _cloud_value; } - virtual void fromCloudShadow() { - _value = _cloud_shadow_value; + virtual void fromLocalToCloud() { + _cloud_value = _value; } - virtual void appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); - cbor_encode_float(mapEncoder, _value); + virtual void appendAttributesToCloud() { + appendAttribute(_value); } - virtual void setValue(CborMapData const * const map_data) { - _value = map_data->val.get(); - } - virtual void setCloudShadowValue(CborMapData const * const map_data) { - _cloud_shadow_value = map_data->val.get(); + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); } //modifiers CloudFloat& operator=(float v) { @@ -79,21 +69,40 @@ class CloudFloat : public ArduinoCloudProperty { CloudFloat& operator-=(float v) {return operator=(_value-=v);} CloudFloat& operator*=(float v) {return operator=(_value*=v);} CloudFloat& operator/=(float v) {return operator=(_value/=v);} - CloudFloat& operator++() {return operator=(_value++);} - CloudFloat& operator--() {return operator=(_value--);} + CloudFloat& operator++() {return operator=(_value+1.0f);} + CloudFloat& operator--() {return operator=(_value-1.0f);} + CloudFloat operator++(int) {float f =_value; operator=(_value+1.0f); return CloudFloat(_value);} + CloudFloat operator--(int) {float f =_value; operator=(_value-1.0f); return CloudFloat(_value);} + //friends friend CloudFloat operator+(CloudFloat iw, CloudFloat v) {return iw+=v;} friend CloudFloat operator+(CloudFloat iw, float v) {return iw+=v;} + friend CloudFloat operator+(CloudFloat iw, int v) {return iw+=(float)v;} + friend CloudFloat operator+(CloudFloat iw, double v) {return iw+=(float)v;} friend CloudFloat operator+(float v, CloudFloat iw) {return CloudFloat(v)+=iw;} + friend CloudFloat operator+(int v, CloudFloat iw) {return CloudFloat(v)+=iw;} + friend CloudFloat operator+(double v, CloudFloat iw) {return CloudFloat(v)+=iw;} friend CloudFloat operator-(CloudFloat iw, CloudFloat v) {return iw-=v;} friend CloudFloat operator-(CloudFloat iw, float v) {return iw-=v;} + friend CloudFloat operator-(CloudFloat iw, int v) {return iw-=(float)v;} + friend CloudFloat operator-(CloudFloat iw, double v) {return iw-=(float)v;} friend CloudFloat operator-(float v, CloudFloat iw) {return CloudFloat(v)-=iw;} + friend CloudFloat operator-(int v, CloudFloat iw) {return CloudFloat(v)-=iw;} + friend CloudFloat operator-(double v, CloudFloat iw) {return CloudFloat(v)-=iw;} friend CloudFloat operator*(CloudFloat iw, CloudFloat v) {return iw*=v;} friend CloudFloat operator*(CloudFloat iw, float v) {return iw*=v;} + friend CloudFloat operator*(CloudFloat iw, int v) {return iw*=(float)v;} + friend CloudFloat operator*(CloudFloat iw, double v) {return iw*=(float)v;} friend CloudFloat operator*(float v, CloudFloat iw) {return CloudFloat(v)*=iw;} + friend CloudFloat operator*(int v, CloudFloat iw) {return CloudFloat(v)*=iw;} + friend CloudFloat operator*(double v, CloudFloat iw) {return CloudFloat(v)*=iw;} friend CloudFloat operator/(CloudFloat iw, CloudFloat v) {return iw/=v;} friend CloudFloat operator/(CloudFloat iw, float v) {return iw/=v;} + friend CloudFloat operator/(CloudFloat iw, int v) {return iw/=(float)v;} + friend CloudFloat operator/(CloudFloat iw, double v) {return iw/=(float)v;} friend CloudFloat operator/(float v, CloudFloat iw) {return CloudFloat(v)/=iw;} + friend CloudFloat operator/(int v, CloudFloat iw) {return CloudFloat(v)/=iw;} + friend CloudFloat operator/(double v, CloudFloat iw) {return CloudFloat(v)/=iw;} }; diff --git a/valuetypes/CloudInt.h b/types/CloudInt.h similarity index 78% rename from valuetypes/CloudInt.h rename to types/CloudInt.h index b5c3d2916..035c27d71 100644 --- a/valuetypes/CloudInt.h +++ b/types/CloudInt.h @@ -26,43 +26,33 @@ #include "../ArduinoCloudProperty.hpp" /****************************************************************************** - * TYPEDEF + * CLASS DECLARATION ******************************************************************************/ -/****************************************************************************** - * TYPEDEF - ******************************************************************************/ -/****************************************************************************** - * CLASS DECLARATION - ******************************************************************************/ class CloudInt : public ArduinoCloudProperty { private: int _value, - _cloud_shadow_value; + _cloud_value; public: CloudInt() { CloudInt(0); } - CloudInt(int v) : _value(v), _cloud_shadow_value(v) {} + CloudInt(int v) : _value(v), _cloud_value(v) {} operator int() const {return _value;} - virtual bool isDifferentFromCloudShadow() { - return _value != _cloud_shadow_value && (abs(_value - _cloud_shadow_value) >= ArduinoCloudProperty::_min_delta_property); - } - virtual void toShadow() { - _cloud_shadow_value = _value; + virtual bool isDifferentFromCloud() { + return _value != _cloud_value && (abs(_value - _cloud_value) >= ArduinoCloudProperty::_min_delta_property); } - virtual void fromCloudShadow() { - _value = _cloud_shadow_value; + virtual void fromCloudToLocal() { + _value = _cloud_value; } - virtual void appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); - cbor_encode_int (mapEncoder, _value); + virtual void fromLocalToCloud() { + _cloud_value = _value; } - virtual void setValue(CborMapData const * const map_data) { - _value = map_data->val.get(); + virtual void appendAttributesToCloud() { + appendAttribute(_value); } - virtual void setCloudShadowValue(CborMapData const * const map_data) { - _cloud_shadow_value = map_data->val.get(); + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); } //modifiers CloudInt& operator=(int v) { @@ -80,8 +70,8 @@ class CloudInt : public ArduinoCloudProperty { CloudInt& operator%=(int v) {return operator=(_value%=v);} CloudInt& operator++() {return operator=(++_value);} CloudInt& operator--() {return operator=(--_value);} - CloudInt operator++(int) {return CloudInt(_value++);} - CloudInt operator--(int) {return CloudInt(_value--);} + CloudInt operator++(int) {int temp =_value; operator=(_value+1); return CloudInt(_value);} + CloudInt operator--(int) {int temp =_value; operator=(_value-1); return CloudInt(_value);} CloudInt& operator&=(int v) {return operator=(_value&=v);} CloudInt& operator|=(int v) {return operator=(_value|=v);} CloudInt& operator^=(int v) {return operator=(_value^=v);} diff --git a/types/CloudLocation.h b/types/CloudLocation.h new file mode 100644 index 000000000..cd6ecd311 --- /dev/null +++ b/types/CloudLocation.h @@ -0,0 +1,100 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDLOCATION_H_ +#define CLOUDLOCATION_H_ + +/****************************************************************************** + * INCLUDE + ******************************************************************************/ + +#include +#include +#include "../ArduinoCloudProperty.hpp" + +/****************************************************************************** + * CLASS DECLARATION + ******************************************************************************/ + + + +class Location { + public: + float lat, + lon; + Location(float lat, float lon) : lat(lat), lon(lon) {} + Location& operator=(Location& aLocation) { + lat = aLocation.lat; + lon = aLocation.lon; + return *this; + } + float operator-(Location& aLocation) { + return sqrt(pow(lat - aLocation.lat, 2) + pow(lon - aLocation.lon, 2)); + } + bool operator==(Location& aLocation) { + return lat == aLocation.lat && lon == aLocation.lon; + } + bool operator!=(Location& aLocation) { + return !(operator==(aLocation)); + } +}; + +class CloudLocation : public ArduinoCloudProperty { +private: + Location _value, + _cloud_value; +public: + CloudLocation() : _value(0, 0), _cloud_value(0, 0) {} + CloudLocation(float lat, float lon) : _value(lat, lon), _cloud_value(lat, lon) {} + virtual bool isDifferentFromCloud() { + float distance = _value - _cloud_value; + return _value != _cloud_value && (abs(distance) >= ArduinoCloudProperty::_min_delta_property); + } + + CloudLocation& operator=(Location aLocation) { + _value.lat = aLocation.lat; + _value.lon = aLocation.lon; + updateLocalTimestamp(); + return *this; + } + + Location getCloudValue() { + return _cloud_value; + } + + Location getValue() { + return _value; + } + + + virtual void fromCloudToLocal() { + _value = _cloud_value; + } + virtual void fromLocalToCloud() { + _cloud_value = _value; + } + virtual void appendAttributesToCloud() { + appendAttribute(_value.lat); + appendAttribute(_value.lon); + } + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value.lat); + setAttribute(_cloud_value.lon); + } +}; + +#endif /* CLOUDLOCATION_H_ */ diff --git a/valuetypes/CloudString.h b/types/CloudString.h similarity index 63% rename from valuetypes/CloudString.h rename to types/CloudString.h index fd1c6d6ec..1b2c8cbc5 100644 --- a/valuetypes/CloudString.h +++ b/types/CloudString.h @@ -26,47 +26,34 @@ #include "../ArduinoCloudProperty.hpp" /****************************************************************************** - * TYPEDEF + * CLASS DECLARATION ******************************************************************************/ -/****************************************************************************** - * TYPEDEF - ******************************************************************************/ -/****************************************************************************** - * CLASS DECLARATION - ******************************************************************************/ class CloudString : public ArduinoCloudProperty { private: String _value, - _cloud_shadow_value; + _cloud_value; public: CloudString() { CloudString(""); } CloudString(const char *v) { CloudString(String(v)); } - CloudString(String v) : - _value(v), - _cloud_shadow_value(v) - { - } - virtual bool isDifferentFromCloudShadow() { - return _value != _cloud_shadow_value; - } - virtual void toShadow() { - _cloud_shadow_value = _value; + CloudString(String v) : _value(v), _cloud_value(v) {} + operator String() const {return _value;} + virtual bool isDifferentFromCloud() { + return _value != _cloud_value; } - virtual void fromCloudShadow() { - _value = _cloud_shadow_value; + virtual void fromCloudToLocal() { + _value = _cloud_value; } - virtual void appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::StringValue)); - cbor_encode_text_stringz(mapEncoder, _value.c_str()); + virtual void fromLocalToCloud() { + _cloud_value = _value; } - virtual void setValue(CborMapData const * const map_data) { - _value = String(map_data->str_val.get()); + virtual void appendAttributesToCloud() { + appendAttribute(_value); } - virtual void setCloudShadowValue(CborMapData const * const map_data) { - _cloud_shadow_value = map_data->str_val.get(); + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); } //modifiers CloudString& operator=(String v) { @@ -88,8 +75,10 @@ class CloudString : public ArduinoCloudProperty { { return _value == c; } - //friends + friend CloudString operator+(CloudString cs, String v) { + return cs+=v; + } }; diff --git a/valuetypes/CloudWrapperBool.h b/types/CloudWrapperBool.h similarity index 53% rename from valuetypes/CloudWrapperBool.h rename to types/CloudWrapperBool.h index 547b81b53..321c936e5 100644 --- a/valuetypes/CloudWrapperBool.h +++ b/types/CloudWrapperBool.h @@ -25,14 +25,6 @@ #include #include "../ArduinoCloudProperty.hpp" -/****************************************************************************** - * TYPEDEF - ******************************************************************************/ - -/****************************************************************************** - * TYPEDEF - ******************************************************************************/ - /****************************************************************************** * CLASS DECLARATION ******************************************************************************/ @@ -40,34 +32,30 @@ class CloudWrapperBool : public ArduinoCloudProperty { private: bool &_primitive_value, - _cloud_shadow_value, - _local_shadow_value; + _cloud_value, + _local_value; public: - CloudWrapperBool(bool& v) : _primitive_value(v), _cloud_shadow_value(v), _local_shadow_value(v) {} - virtual bool isDifferentFromCloudShadow() { - return _primitive_value != _cloud_shadow_value; - } - virtual void toShadow() { - _cloud_shadow_value = _local_shadow_value = _primitive_value; + CloudWrapperBool(bool& v) : _primitive_value(v), _cloud_value(v), _local_value(v) {} + virtual bool isDifferentFromCloud() { + return _primitive_value != _cloud_value; } - virtual void fromCloudShadow() { - _primitive_value = _cloud_shadow_value; + virtual void fromCloudToLocal() { + _primitive_value = _cloud_value; } - virtual void appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); - cbor_encode_boolean(mapEncoder, _primitive_value); + virtual void fromLocalToCloud() { + _cloud_value = _primitive_value; } - virtual void setValue(CborMapData const * const map_data) { - _primitive_value = map_data->bool_val.get(); + virtual void appendAttributesToCloud() { + appendAttribute(_primitive_value); } - virtual void setCloudShadowValue(CborMapData const * const map_data) { - _cloud_shadow_value = map_data->bool_val.get(); + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); } virtual bool isPrimitive() { return true; } virtual bool isChangedLocally() { - return _primitive_value != _local_shadow_value; + return _primitive_value != _local_value; } }; diff --git a/valuetypes/CloudWrapperFloat.h b/types/CloudWrapperFloat.h similarity index 52% rename from valuetypes/CloudWrapperFloat.h rename to types/CloudWrapperFloat.h index 9dce81c39..f00401ec4 100644 --- a/valuetypes/CloudWrapperFloat.h +++ b/types/CloudWrapperFloat.h @@ -27,14 +27,6 @@ #include #include "../ArduinoCloudProperty.hpp" -/****************************************************************************** - * TYPEDEF - ******************************************************************************/ - -/****************************************************************************** - * TYPEDEF - ******************************************************************************/ - /****************************************************************************** * CLASS DECLARATION ******************************************************************************/ @@ -42,34 +34,30 @@ class CloudWrapperFloat : public ArduinoCloudProperty { private: float &_primitive_value, - _cloud_shadow_value, - _local_shadow_value; + _cloud_value, + _local_value; public: - CloudWrapperFloat(float& v) : _primitive_value(v), _cloud_shadow_value(v), _local_shadow_value(v) {} - virtual bool isDifferentFromCloudShadow() { - return _primitive_value != _cloud_shadow_value && (abs(_primitive_value - _cloud_shadow_value) >= ArduinoCloudProperty::_min_delta_property); - } - virtual void toShadow() { - _cloud_shadow_value = _local_shadow_value = _primitive_value; + CloudWrapperFloat(float& v) : _primitive_value(v), _cloud_value(v), _local_value(v) {} + virtual bool isDifferentFromCloud() { + return _primitive_value != _cloud_value && (abs(_primitive_value - _cloud_value) >= ArduinoCloudProperty::_min_delta_property); } - virtual void fromCloudShadow() { - _primitive_value = _cloud_shadow_value; + virtual void fromCloudToLocal() { + _primitive_value = _cloud_value; } - virtual void appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); - cbor_encode_float(mapEncoder, _primitive_value); + virtual void fromLocalToCloud() { + _cloud_value = _primitive_value; } - virtual void setValue(CborMapData const * const map_data) { - _primitive_value = map_data->val.get(); + virtual void appendAttributesToCloud() { + appendAttribute(_primitive_value); } - virtual void setCloudShadowValue(CborMapData const * const map_data) { - _cloud_shadow_value = map_data->val.get(); + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); } virtual bool isPrimitive() { return true; } virtual bool isChangedLocally() { - return _primitive_value != _local_shadow_value; + return _primitive_value != _local_value; } }; diff --git a/valuetypes/CloudWrapperInt.h b/types/CloudWrapperInt.h similarity index 51% rename from valuetypes/CloudWrapperInt.h rename to types/CloudWrapperInt.h index e6e368b41..b6dc6db6f 100644 --- a/valuetypes/CloudWrapperInt.h +++ b/types/CloudWrapperInt.h @@ -25,14 +25,6 @@ #include #include "../ArduinoCloudProperty.hpp" -/****************************************************************************** - * TYPEDEF - ******************************************************************************/ - -/****************************************************************************** - * TYPEDEF - ******************************************************************************/ - /****************************************************************************** * CLASS DECLARATION ******************************************************************************/ @@ -40,34 +32,30 @@ class CloudWrapperInt : public ArduinoCloudProperty { private: int &_primitive_value, - _cloud_shadow_value, - _local_shadow_value; + _cloud_value, + _local_value; public: - CloudWrapperInt(int& v) : _primitive_value(v), _cloud_shadow_value(v), _local_shadow_value(v) {} - virtual bool isDifferentFromCloudShadow() { - return _primitive_value != _cloud_shadow_value && (abs(_primitive_value - _cloud_shadow_value) >= ArduinoCloudProperty::_min_delta_property); - } - virtual void toShadow() { - _cloud_shadow_value = _local_shadow_value = _primitive_value; + CloudWrapperInt(int& v) : _primitive_value(v), _cloud_value(v), _local_value(v) {} + virtual bool isDifferentFromCloud() { + return _primitive_value != _cloud_value && (abs(_primitive_value - _cloud_value) >= ArduinoCloudProperty::_min_delta_property); } - virtual void fromCloudShadow() { - _primitive_value = _cloud_shadow_value; + virtual void fromCloudToLocal() { + _primitive_value = _cloud_value; } - virtual void appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::Value)); - cbor_encode_int (mapEncoder, _primitive_value); + virtual void fromLocalToCloud() { + _cloud_value = _primitive_value; } - virtual void setValue(CborMapData const * const map_data) { - _primitive_value = map_data->val.get(); + virtual void appendAttributesToCloud() { + appendAttribute(_primitive_value); } - virtual void setCloudShadowValue(CborMapData const * const map_data) { - _cloud_shadow_value = map_data->val.get(); + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); } virtual bool isPrimitive() { return true; } virtual bool isChangedLocally() { - return _primitive_value != _local_shadow_value; + return _primitive_value != _local_value; } }; diff --git a/valuetypes/CloudWrapperString.h b/types/CloudWrapperString.h similarity index 54% rename from valuetypes/CloudWrapperString.h rename to types/CloudWrapperString.h index 7f3276826..9d4424dff 100644 --- a/valuetypes/CloudWrapperString.h +++ b/types/CloudWrapperString.h @@ -25,14 +25,6 @@ #include #include "../ArduinoCloudProperty.hpp" -/****************************************************************************** - * TYPEDEF - ******************************************************************************/ - -/****************************************************************************** - * TYPEDEF - ******************************************************************************/ - /****************************************************************************** * CLASS DECLARATION ******************************************************************************/ @@ -40,38 +32,34 @@ class CloudWrapperString : public ArduinoCloudProperty { private: String &_primitive_value, - _cloud_shadow_value, - _local_shadow_value; + _cloud_value, + _local_value; public: CloudWrapperString(String& v) : _primitive_value(v), - _cloud_shadow_value(v), - _local_shadow_value(v) { - } - virtual bool isDifferentFromCloudShadow() { - return _primitive_value != _cloud_shadow_value; + _cloud_value(v), + _local_value(v) { } - virtual void toShadow() { - _cloud_shadow_value = _local_shadow_value = _primitive_value; + virtual bool isDifferentFromCloud() { + return _primitive_value != _cloud_value; } - virtual void fromCloudShadow() { - _primitive_value = _cloud_shadow_value; + virtual void fromCloudToLocal() { + _primitive_value = _cloud_value; } - virtual void appendValue(CborEncoder * mapEncoder) const { - cbor_encode_int (mapEncoder, static_cast(CborIntegerMapKey::StringValue)); - cbor_encode_text_stringz(mapEncoder, _primitive_value.c_str()); + virtual void fromLocalToCloud() { + _cloud_value = _primitive_value; } - virtual void setValue(CborMapData const * const map_data) { - _primitive_value = map_data->str_val.get(); + virtual void appendAttributesToCloud() { + appendAttribute(_primitive_value); } - virtual void setCloudShadowValue(CborMapData const * const map_data) { - _cloud_shadow_value = map_data->str_val.get(); + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); } virtual bool isPrimitive() { return true; } virtual bool isChangedLocally() { - return _primitive_value != _local_shadow_value; + return _primitive_value != _local_value; } }; From 563f6c15d7e390c916de4ff7a07b241ece7b912f Mon Sep 17 00:00:00 2001 From: mirkokurt Date: Mon, 29 Apr 2019 11:58:37 +0200 Subject: [PATCH 131/175] AStyle formatting --- ArduinoCloudProperty.cpp | 97 +++++----- ArduinoCloudProperty.hpp | 205 ++++++++++++---------- ArduinoCloudThing.cpp | 124 ++++++------- ArduinoCloudThing.h | 132 +++++++------- types/CloudBool.h | 78 +++++---- types/CloudColor.h | 351 ++++++++++++++++++------------------- types/CloudFloat.h | 220 +++++++++++++++-------- types/CloudInt.h | 270 +++++++++++++++++++--------- types/CloudLocation.h | 78 ++++----- types/CloudString.h | 102 +++++------ types/CloudWrapperBool.h | 58 +++--- types/CloudWrapperFloat.h | 58 +++--- types/CloudWrapperInt.h | 58 +++--- types/CloudWrapperString.h | 66 +++---- 14 files changed, 1057 insertions(+), 840 deletions(-) diff --git a/ArduinoCloudProperty.cpp b/ArduinoCloudProperty.cpp index cd8333963..7dfdc5b97 100644 --- a/ArduinoCloudProperty.cpp +++ b/ArduinoCloudProperty.cpp @@ -24,36 +24,35 @@ static unsigned long getTimestamp() { #ifdef ARDUINO_ARCH_SAMD - return rtc.getEpoch(); + return rtc.getEpoch(); #else - #warning "No RTC available on this architecture - ArduinoIoTCloud will not keep track of local change timestamps ." - return 0; +#warning "No RTC available on this architecture - ArduinoIoTCloud will not keep track of local change timestamps ." + return 0; #endif } /****************************************************************************** - * CTOR/DTOR + CTOR/DTOR ******************************************************************************/ ArduinoCloudProperty::ArduinoCloudProperty() -: _name(""), - _permission(Permission::Read), - _update_callback_func(nullptr), - _sync_callback_func(nullptr), - _has_been_updated_once(false), - _has_been_modified_in_callback(false), - _min_delta_property(0.0f), - _min_time_between_updates_millis(0), - _last_updated_millis(0), - _update_interval_millis(0), - _last_local_change_timestamp(0), - _last_cloud_change_timestamp(0), - _encoder(nullptr), - _map_data_list(nullptr) -{ + : _name(""), + _permission(Permission::Read), + _update_callback_func(nullptr), + _sync_callback_func(nullptr), + _has_been_updated_once(false), + _has_been_modified_in_callback(false), + _min_delta_property(0.0f), + _min_time_between_updates_millis(0), + _last_updated_millis(0), + _update_interval_millis(0), + _last_local_change_timestamp(0), + _last_cloud_change_timestamp(0), + _encoder(nullptr), + _map_data_list(nullptr) { } /****************************************************************************** - * PUBLIC MEMBER FUNCTIONS + PUBLIC MEMBER FUNCTIONS ******************************************************************************/ void ArduinoCloudProperty::init(String const name, Permission const permission) { _name = name; @@ -84,35 +83,35 @@ ArduinoCloudProperty & ArduinoCloudProperty::publishEvery(unsigned long const se } bool ArduinoCloudProperty::shouldBeUpdated() { - if(!_has_been_updated_once) return true; + if (!_has_been_updated_once) { + return true; + } - if(_has_been_modified_in_callback) { + if (_has_been_modified_in_callback) { _has_been_modified_in_callback = false; return true; } - if (_update_policy == UpdatePolicy::OnChange) { + if (_update_policy == UpdatePolicy::OnChange) { return (isDifferentFromCloud() && ((millis() - _last_updated_millis) >= (_min_time_between_updates_millis))); - } - else if(_update_policy == UpdatePolicy::TimeInterval) { + } else if (_update_policy == UpdatePolicy::TimeInterval) { return ((millis() - _last_updated_millis) >= _update_interval_millis); - } - else { + } else { return false; } } void ArduinoCloudProperty::execCallbackOnChange() { - if(_update_callback_func != NULL) { + if (_update_callback_func != NULL) { _update_callback_func(); } - if(!isDifferentFromCloud()) { + if (!isDifferentFromCloud()) { _has_been_modified_in_callback = true; } } void ArduinoCloudProperty::execCallbackOnSync() { - if(_sync_callback_func != NULL) { + if (_sync_callback_func != NULL) { _sync_callback_func(*this); } } @@ -126,41 +125,41 @@ void ArduinoCloudProperty::append(CborEncoder *encoder) { } void ArduinoCloudProperty::appendAttributeReal(bool value, String attributeName) { - appendAttributeName(attributeName, [value](CborEncoder& mapEncoder) { - cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); + appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) { + cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); cbor_encode_boolean(&mapEncoder, value); }); } void ArduinoCloudProperty::appendAttributeReal(int value, String attributeName) { - appendAttributeName(attributeName, [value](CborEncoder& mapEncoder) { - cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::Value)); + appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) { + cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Value)); cbor_encode_int(&mapEncoder, value); }); } void ArduinoCloudProperty::appendAttributeReal(float value, String attributeName) { - appendAttributeName(attributeName, [value](CborEncoder& mapEncoder) { - cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::Value)); + appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) { + cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Value)); cbor_encode_float(&mapEncoder, value); }); } void ArduinoCloudProperty::appendAttributeReal(String value, String attributeName) { - appendAttributeName(attributeName, [value](CborEncoder& mapEncoder) { - cbor_encode_int (&mapEncoder, static_cast(CborIntegerMapKey::StringValue)); + appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) { + cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::StringValue)); cbor_encode_text_stringz(&mapEncoder, value.c_str()); }); } -void ArduinoCloudProperty::appendAttributeName(String attributeName, std::functionappendValue){ +void ArduinoCloudProperty::appendAttributeName(String attributeName, std::functionappendValue) { CborEncoder mapEncoder; cbor_encoder_create_map(_encoder, &mapEncoder, 2); cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Name)); String completeName = _name; - if(attributeName != ""){ + if (attributeName != "") { completeName += ":" + attributeName; - } + } cbor_encode_text_stringz(&mapEncoder, completeName.c_str()); appendValue(mapEncoder); cbor_encoder_close_container(_encoder, &mapEncoder); @@ -172,19 +171,27 @@ void ArduinoCloudProperty::setAttributesFromCloud(LinkedList *map } void ArduinoCloudProperty::setAttributeReal(bool& value, String attributeName) { - setAttributeReal(attributeName, [&value](CborMapData *md) {value = md->bool_val.get(); }); + setAttributeReal(attributeName, [&value](CborMapData * md) { + value = md->bool_val.get(); + }); } void ArduinoCloudProperty::setAttributeReal(int& value, String attributeName) { - setAttributeReal(attributeName, [&value](CborMapData *md) { value = md->val.get(); }); + setAttributeReal(attributeName, [&value](CborMapData * md) { + value = md->val.get(); + }); } void ArduinoCloudProperty::setAttributeReal(float& value, String attributeName) { - setAttributeReal(attributeName, [&value](CborMapData *md) { value = md->val.get(); }); + setAttributeReal(attributeName, [&value](CborMapData * md) { + value = md->val.get(); + }); } void ArduinoCloudProperty::setAttributeReal(String& value, String attributeName) { - setAttributeReal(attributeName, [&value](CborMapData *md) { value = md->str_val.get(); }); + setAttributeReal(attributeName, [&value](CborMapData * md) { + value = md->str_val.get(); + }); } void ArduinoCloudProperty::setAttributeReal(String attributeName, std::functionsetValue) { diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index 3ef37caf0..aabdea8df 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -19,8 +19,8 @@ #define ARDUINO_CLOUD_PROPERTY_HPP_ #ifdef HOST_BUILD -#define substring(...) substr(__VA_ARGS__) -#define indexOf(x) find(x) + #define substring(...) substr(__VA_ARGS__) + #define indexOf(x) find(x) #endif /****************************************************************************** @@ -38,10 +38,10 @@ #include "lib/LinkedList/LinkedList.h" #define appendAttribute(x) appendAttributeReal(x, getAttributeName(#x, '.')) -#define setAttribute(x) setAttributeReal(x, getAttributeName(#x, '.')) +#define setAttribute(x) setAttributeReal(x, getAttributeName(#x, '.')) /****************************************************************************** - * ENUM + ENUM ******************************************************************************/ /* Source: https://tools.ietf.org/html/rfc8428#section-6 */ enum class CborIntegerMapKey : int { @@ -64,35 +64,44 @@ enum class CborIntegerMapKey : int { template class MapEntry { -public: + public: - MapEntry() : _is_set(false) { } + MapEntry() : _is_set(false) { } - inline void set (T const & entry) { _entry = entry; _is_set = true; } - inline T const get () const { return _entry; } + inline void set(T const & entry) { + _entry = entry; + _is_set = true; + } + inline T const get() const { + return _entry; + } - inline bool isSet() const { return _is_set; } - inline void reset() { _is_set = false; } + inline bool isSet() const { + return _is_set; + } + inline void reset() { + _is_set = false; + } -private: + private: - T _entry; - bool _is_set; + T _entry; + bool _is_set; }; class CborMapData { -public: - MapEntry base_version; - MapEntry base_name; - MapEntry base_time; - MapEntry name; - MapEntry attribute_name; - MapEntry val; - MapEntry str_val; - MapEntry bool_val; - MapEntry time; + public: + MapEntry base_version; + MapEntry base_name; + MapEntry base_time; + MapEntry name; + MapEntry attribute_name; + MapEntry val; + MapEntry str_val; + MapEntry bool_val; + MapEntry time; }; enum class Permission { @@ -114,80 +123,92 @@ typedef void(*UpdateCallbackFunc)(void); ******************************************************************************/ class ArduinoCloudProperty { -public: - ArduinoCloudProperty(); - void init(String const name, Permission const permission); - - /* Composable configuration of the ArduinoCloudProperty class */ - ArduinoCloudProperty & onUpdate (UpdateCallbackFunc func); - ArduinoCloudProperty & onSync (void (*func)(ArduinoCloudProperty &property)); - ArduinoCloudProperty & publishOnChange(float const min_delta_property, unsigned long const min_time_between_updates_millis = 0); - ArduinoCloudProperty & publishEvery (unsigned long const seconds); - - inline String name () const { return _name; } - inline bool isReadableByCloud () const { return (_permission == Permission::Read ) || (_permission == Permission::ReadWrite); } - inline bool isWriteableByCloud() const { return (_permission == Permission::Write) || (_permission == Permission::ReadWrite); } - - bool shouldBeUpdated (); - void execCallbackOnChange (); - void execCallbackOnSync (); - void setLastCloudChangeTimestamp (unsigned long cloudChangeTime); - void setLastLocalChangeTimestamp (unsigned long localChangeTime); - unsigned long getLastCloudChangeTimestamp(); - unsigned long getLastLocalChangeTimestamp(); - - void updateLocalTimestamp (); - void append (CborEncoder * encoder); - void appendAttributeReal (bool value, String attributeName = ""); - void appendAttributeReal (int value, String attributeName = ""); - void appendAttributeReal (float value, String attributeName = ""); - void appendAttributeReal (String value, String attributeName = ""); - void appendAttributeName (String attributeName, std::functionf); - void setAttributesFromCloud (LinkedList *map_data_list); - void setAttributeReal (bool& value, String attributeName = ""); - void setAttributeReal (int& value, String attributeName = ""); - void setAttributeReal (float& value, String attributeName = ""); - void setAttributeReal (String& value, String attributeName = ""); - void setAttributeReal (String attributeName, std::functionsetValue); - String getAttributeName (String propertyName, char separator); - - virtual bool isDifferentFromCloud() = 0; - virtual void fromCloudToLocal() = 0; - virtual void fromLocalToCloud() = 0; - virtual void appendAttributesToCloud() = 0; - virtual void setAttributesFromCloud() = 0; - virtual bool isPrimitive() { return false; }; - virtual bool isChangedLocally() { return false; }; -protected: - /* Variables used for UpdatePolicy::OnChange */ - float _min_delta_property; - unsigned long _min_time_between_updates_millis; - String _name; - -private: - Permission _permission; - UpdateCallbackFunc _update_callback_func; - void (*_sync_callback_func)(ArduinoCloudProperty &property); - - UpdatePolicy _update_policy; - bool _has_been_updated_once, - _has_been_modified_in_callback; - /* Variables used for UpdatePolicy::TimeInterval */ - unsigned long _last_updated_millis, - _update_interval_millis; - /* Variables used for reconnection sync*/ - unsigned long _last_local_change_timestamp; - unsigned long _last_cloud_change_timestamp; - /* variable to manage property serialization/deserialization on CBOR */ - CborEncoder *_encoder; - LinkedList - *_map_data_list; + public: + ArduinoCloudProperty(); + void init(String const name, Permission const permission); + + /* Composable configuration of the ArduinoCloudProperty class */ + ArduinoCloudProperty & onUpdate(UpdateCallbackFunc func); + ArduinoCloudProperty & onSync(void (*func)(ArduinoCloudProperty &property)); + ArduinoCloudProperty & publishOnChange(float const min_delta_property, unsigned long const min_time_between_updates_millis = 0); + ArduinoCloudProperty & publishEvery(unsigned long const seconds); + + inline String name() const { + return _name; + } + inline bool isReadableByCloud() const { + return (_permission == Permission::Read) || (_permission == Permission::ReadWrite); + } + inline bool isWriteableByCloud() const { + return (_permission == Permission::Write) || (_permission == Permission::ReadWrite); + } + + bool shouldBeUpdated(); + void execCallbackOnChange(); + void execCallbackOnSync(); + void setLastCloudChangeTimestamp(unsigned long cloudChangeTime); + void setLastLocalChangeTimestamp(unsigned long localChangeTime); + unsigned long getLastCloudChangeTimestamp(); + unsigned long getLastLocalChangeTimestamp(); + + void updateLocalTimestamp(); + void append(CborEncoder * encoder); + void appendAttributeReal(bool value, String attributeName = ""); + void appendAttributeReal(int value, String attributeName = ""); + void appendAttributeReal(float value, String attributeName = ""); + void appendAttributeReal(String value, String attributeName = ""); + void appendAttributeName(String attributeName, std::functionf); + void setAttributesFromCloud(LinkedList *map_data_list); + void setAttributeReal(bool& value, String attributeName = ""); + void setAttributeReal(int& value, String attributeName = ""); + void setAttributeReal(float& value, String attributeName = ""); + void setAttributeReal(String& value, String attributeName = ""); + void setAttributeReal(String attributeName, std::functionsetValue); + String getAttributeName(String propertyName, char separator); + + virtual bool isDifferentFromCloud() = 0; + virtual void fromCloudToLocal() = 0; + virtual void fromLocalToCloud() = 0; + virtual void appendAttributesToCloud() = 0; + virtual void setAttributesFromCloud() = 0; + virtual bool isPrimitive() { + return false; + }; + virtual bool isChangedLocally() { + return false; + }; + protected: + /* Variables used for UpdatePolicy::OnChange */ + float _min_delta_property; + unsigned long _min_time_between_updates_millis; + String _name; + + private: + Permission _permission; + UpdateCallbackFunc _update_callback_func; + void (*_sync_callback_func)(ArduinoCloudProperty &property); + + UpdatePolicy _update_policy; + bool _has_been_updated_once, + _has_been_modified_in_callback; + /* Variables used for UpdatePolicy::TimeInterval */ + unsigned long _last_updated_millis, + _update_interval_millis; + /* Variables used for reconnection sync*/ + unsigned long _last_local_change_timestamp; + unsigned long _last_cloud_change_timestamp; + /* variable to manage property serialization/deserialization on CBOR */ + CborEncoder *_encoder; + LinkedList + *_map_data_list; }; /****************************************************************************** PROTOTYPE FREE FUNCTIONs ******************************************************************************/ -inline bool operator == (ArduinoCloudProperty const & lhs, ArduinoCloudProperty const & rhs) { return (lhs.name() == rhs.name()); } +inline bool operator == (ArduinoCloudProperty const & lhs, ArduinoCloudProperty const & rhs) { + return (lhs.name() == rhs.name()); +} #endif /* ARDUINO_CLOUD_PROPERTY_HPP_ */ diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index af1c7f74c..d5a52ffed 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -29,10 +29,9 @@ #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) extern "C" char *sbrk(int i); -void PrintFreeRam (void) -{ - char stack_dummy = 0; - //Serial.print("Free RAM: "); //Serial.println(&stack_dummy - sbrk(0)); +void PrintFreeRam(void) { + char stack_dummy = 0; + //Serial.print("Free RAM: "); //Serial.println(&stack_dummy - sbrk(0)); } #endif @@ -44,12 +43,12 @@ void PrintFreeRam (void) CTOR/DTOR ******************************************************************************/ -ArduinoCloudThing::ArduinoCloudThing() : -_numPrimitivesProperties(0), -_isSyncMessage(false), -_currentPropertyName(""), -_currentPropertyTime(0), -_currentPropertyBaseTime(0) +ArduinoCloudThing::ArduinoCloudThing() : + _numPrimitivesProperties(0), + _isSyncMessage(false), + _currentPropertyName(""), + _currentPropertyTime(0), + _currentPropertyBaseTime(0) {} /****************************************************************************** @@ -66,7 +65,7 @@ int ArduinoCloudThing::encode(uint8_t * data, size_t const size) { cbor_encoder_init(&encoder, data, size, 0); - if(cbor_encoder_create_array(&encoder, &arrayEncoder, CborIndefiniteLength) != CborNoError) { + if (cbor_encoder_create_array(&encoder, &arrayEncoder, CborIndefiniteLength) != CborNoError) { return -1; } @@ -74,25 +73,25 @@ int ArduinoCloudThing::encode(uint8_t * data, size_t const size) { return -1; } - if(cbor_encoder_close_container(&encoder, &arrayEncoder) != CborNoError) { + if (cbor_encoder_close_container(&encoder, &arrayEncoder) != CborNoError) { return -1; } -#if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) -PrintFreeRam(); -#endif + #if defined(DEBUG_MEMORY) && defined(ARDUINO_ARCH_SAMD) + PrintFreeRam(); + #endif int const bytes_encoded = cbor_encoder_get_buffer_size(&encoder, data); return bytes_encoded; } ArduinoCloudProperty& ArduinoCloudThing::addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission) { property.init(name, permission); - if(isPropertyInContainer(name)) { + if (isPropertyInContainer(name)) { return (*getProperty(name)); - } - else { - if (property.isPrimitive()) - _numPrimitivesProperties++; + } else { + if (property.isPrimitive()) { + _numPrimitivesProperties++; + } addProperty(&property); return (property); } @@ -126,23 +125,23 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt MapParserState current_state = MapParserState::EnterMap, next_state; - while(current_state != MapParserState::Complete) { - - switch(current_state) { - case MapParserState::EnterMap : next_state = handle_EnterMap (&map_iter, &value_iter, &map_data); break; - case MapParserState::MapKey : next_state = handle_MapKey (&value_iter ); break; - case MapParserState::UndefinedKey : next_state = handle_UndefinedKey (&value_iter ); break; - case MapParserState::BaseVersion : next_state = handle_BaseVersion (&value_iter, map_data ); break; - case MapParserState::BaseName : next_state = handle_BaseName (&value_iter, map_data ); break; - case MapParserState::BaseTime : next_state = handle_BaseTime (&value_iter, map_data ); break; - case MapParserState::Time : next_state = handle_Time (&value_iter, map_data ); break; - case MapParserState::Name : next_state = handle_Name (&value_iter, map_data ); break; - case MapParserState::Value : next_state = handle_Value (&value_iter, map_data ); break; - case MapParserState::StringValue : next_state = handle_StringValue (&value_iter, map_data ); break; - case MapParserState::BooleanValue : next_state = handle_BooleanValue (&value_iter, map_data ); break; - case MapParserState::LeaveMap : next_state = handle_LeaveMap (&map_iter, &value_iter, map_data); break; - case MapParserState::Complete : /* Nothing to do */ break; - case MapParserState::Error : return; break; + while (current_state != MapParserState::Complete) { + + switch (current_state) { + case MapParserState::EnterMap : next_state = handle_EnterMap(&map_iter, &value_iter, &map_data); break; + case MapParserState::MapKey : next_state = handle_MapKey(&value_iter); break; + case MapParserState::UndefinedKey : next_state = handle_UndefinedKey(&value_iter); break; + case MapParserState::BaseVersion : next_state = handle_BaseVersion(&value_iter, map_data); break; + case MapParserState::BaseName : next_state = handle_BaseName(&value_iter, map_data); break; + case MapParserState::BaseTime : next_state = handle_BaseTime(&value_iter, map_data); break; + case MapParserState::Time : next_state = handle_Time(&value_iter, map_data); break; + case MapParserState::Name : next_state = handle_Name(&value_iter, map_data); break; + case MapParserState::Value : next_state = handle_Value(&value_iter, map_data); break; + case MapParserState::StringValue : next_state = handle_StringValue(&value_iter, map_data); break; + case MapParserState::BooleanValue : next_state = handle_BooleanValue(&value_iter, map_data); break; + case MapParserState::LeaveMap : next_state = handle_LeaveMap(&map_iter, &value_iter, map_data); break; + case MapParserState::Complete : /* Nothing to do */ break; + case MapParserState::Error : return; break; } current_state = next_state; @@ -152,7 +151,9 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt bool ArduinoCloudThing::isPropertyInContainer(String const & name) { for (int i = 0; i < _property_list.size(); i++) { ArduinoCloudProperty * p = _property_list.get(i); - if (p->name() == name) return true; + if (p->name() == name) { + return true; + } } return false; } @@ -172,7 +173,9 @@ int ArduinoCloudThing::appendChangedProperties(CborEncoder * arrayEncoder) { ArduinoCloudProperty * ArduinoCloudThing::getProperty(String const & name) { for (int i = 0; i < _property_list.size(); i++) { ArduinoCloudProperty * p = _property_list.get(i); - if (p->name() == name) return p; + if (p->name() == name) { + return p; + } } return NULL; } @@ -186,7 +189,7 @@ void ArduinoCloudThing::updateTimestampOnLocallyChangedProperties() { if (p->isPrimitive() && p->isChangedLocally() && p->isReadableByCloud()) { p->updateLocalTimestamp(); } - } + } } } @@ -197,8 +200,8 @@ void ArduinoCloudThing::updateTimestampOnLocallyChangedProperties() { ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_EnterMap(CborValue * map_iter, CborValue * value_iter, CborMapData **map_data) { MapParserState next_state = MapParserState::Error; - if(cbor_value_get_type(map_iter) == CborMapType) { - if(cbor_value_enter_container(map_iter, value_iter) == CborNoError) { + if (cbor_value_get_type(map_iter) == CborMapType) { + if (cbor_value_enter_container(map_iter, value_iter) == CborNoError) { *map_data = new CborMapData(); next_state = MapParserState::MapKey; } @@ -307,17 +310,18 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_BaseTime(CborValue * ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Name(CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; - if(cbor_value_is_text_string(value_iter)) { + if (cbor_value_is_text_string(value_iter)) { char * val = nullptr; size_t val_size = 0; - if(cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { + if (cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { String name = val; free(val); map_data->name.set(name); int colonPos = name.indexOf(":"); String attribute_name = ""; - if(colonPos != -1) + if (colonPos != -1) { attribute_name = name.substring(colonPos + 1); + } map_data->attribute_name.set(attribute_name); next_state = MapParserState::MapKey; } @@ -389,16 +393,15 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Time(CborValue * val ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * map_iter, CborValue * value_iter, CborMapData * map_data) { MapParserState next_state = MapParserState::Error; - if(map_data->name.isSet()) - { - String propertyName; + if (map_data->name.isSet()) { + String propertyName; int colonPos = map_data->name.get().indexOf(":"); - if(colonPos != -1) { + if (colonPos != -1) { propertyName = map_data->name.get().substring(0, colonPos); } else { propertyName = map_data->name.get(); } - + if (_currentPropertyName != "" && propertyName != _currentPropertyName) { /* Update the property containers depending on the parsed data */ @@ -410,10 +413,10 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * _currentPropertyTime = 0; } /* Compute the cloud change event baseTime and Time */ - if(map_data->base_time.isSet()){ + if (map_data->base_time.isSet()) { _currentPropertyBaseTime = (unsigned long)(map_data->base_time.get()); } - if(map_data->time.isSet() && (map_data->time.get() > _currentPropertyTime)){ + if (map_data->time.isSet() && (map_data->time.get() > _currentPropertyTime)) { _currentPropertyTime = (unsigned long)map_data->time.get(); } _map_data_list.add(map_data); @@ -421,11 +424,10 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * } /* Transition into the next map if available, otherwise finish */ - if(cbor_value_leave_container(map_iter, value_iter) == CborNoError) { - if(!cbor_value_at_end(map_iter)) { + if (cbor_value_leave_container(map_iter, value_iter) == CborNoError) { + if (!cbor_value_at_end(map_iter)) { next_state = MapParserState::EnterMap; - } - else { + } else { updateProperty(_currentPropertyName, _currentPropertyBaseTime + _currentPropertyTime); next_state = MapParserState::Complete; } @@ -434,8 +436,8 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * return next_state; } -void ArduinoCloudThing::freeMapDataList(LinkedList *map_data_list){ - while(map_data_list->size() > 0){ +void ArduinoCloudThing::freeMapDataList(LinkedList *map_data_list) { + while (map_data_list->size() > 0) { CborMapData const * mapData = map_data_list->pop(); delete mapData; } @@ -443,10 +445,10 @@ void ArduinoCloudThing::freeMapDataList(LinkedList *map_data_list void ArduinoCloudThing::updateProperty(String propertyName, unsigned long cloudChangeEventTime) { ArduinoCloudProperty* property = getProperty(propertyName); - if(property && property->isWriteableByCloud()) { + if (property && property->isWriteableByCloud()) { property->setLastCloudChangeTimestamp(cloudChangeEventTime); property->setAttributesFromCloud(&_map_data_list); - if(_isSyncMessage) { + if (_isSyncMessage) { property->execCallbackOnSync(); } else { property->fromCloudToLocal(); @@ -501,7 +503,7 @@ double ArduinoCloudThing::convertCborHalfFloatToDouble(uint16_t const half_val) } void onAutoSync(ArduinoCloudProperty & property) { - if( property.getLastCloudChangeTimestamp() > property.getLastLocalChangeTimestamp()){ + if (property.getLastCloudChangeTimestamp() > property.getLastLocalChangeTimestamp()) { property.fromCloudToLocal(); property.execCallbackOnChange(); } diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 01f572351..b9631acbd 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -61,71 +61,73 @@ void onForceDeviceSync(ArduinoCloudProperty & property); class ArduinoCloudThing { -public: - ArduinoCloudThing(); - - void begin(); - - ArduinoCloudProperty & addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission); - - /* encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer */ - int encode(uint8_t * data, size_t const size); - /* decode a CBOR payload received from the cloud */ - void decode(uint8_t const * const payload, size_t const length, bool isSyncMessage = false); - - bool isPropertyInContainer (String const & name); - int appendChangedProperties (CborEncoder * arrayEncoder); - inline void addProperty (ArduinoCloudProperty * property_obj) { _property_list.add (property_obj); } - ArduinoCloudProperty * getProperty(String const & name); - void updateTimestampOnLocallyChangedProperties(); - void updateProperty(String propertyName, unsigned long cloudChangeEventTime); - -private: - LinkedList _property_list; - /* Keep track of the number of primitive properties in the Thing. If 0 it allows the early exit in updateTimestampOnLocallyChangedProperties() */ - int _numPrimitivesProperties; - /* Indicates the if the message received to be decoded is a response to the getLastValues inquiry */ - bool _isSyncMessage; - /* List of map data that will hold all the attributes of a property */ - LinkedList _map_data_list; - /* Current property name during decoding: use to look for a new property in the senml value array */ - String _currentPropertyName; - unsigned long _currentPropertyBaseTime, - _currentPropertyTime; - - enum class MapParserState { - EnterMap, - MapKey, - UndefinedKey, - BaseVersion, - BaseName, - BaseTime, - Name, - Value, - StringValue, - BooleanValue, - Time, - LeaveMap, - Complete, - Error - }; - - MapParserState handle_EnterMap (CborValue * map_iter, CborValue * value_iter, CborMapData **map_data); - MapParserState handle_MapKey (CborValue * value_iter); - MapParserState handle_UndefinedKey (CborValue * value_iter); - MapParserState handle_BaseVersion (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_BaseName (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_BaseTime (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_Name (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_Value (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_StringValue (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_BooleanValue (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_Time (CborValue * value_iter, CborMapData * map_data); - MapParserState handle_LeaveMap (CborValue * map_iter, CborValue * value_iter, CborMapData * map_data); - - static bool ifNumericConvertToDouble (CborValue * value_iter, double * numeric_val); - static double convertCborHalfFloatToDouble(uint16_t const half_val); - void freeMapDataList(LinkedList *map_data_list); + public: + ArduinoCloudThing(); + + void begin(); + + ArduinoCloudProperty & addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission); + + /* encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer */ + int encode(uint8_t * data, size_t const size); + /* decode a CBOR payload received from the cloud */ + void decode(uint8_t const * const payload, size_t const length, bool isSyncMessage = false); + + bool isPropertyInContainer(String const & name); + int appendChangedProperties(CborEncoder * arrayEncoder); + inline void addProperty(ArduinoCloudProperty * property_obj) { + _property_list.add(property_obj); + } + ArduinoCloudProperty * getProperty(String const & name); + void updateTimestampOnLocallyChangedProperties(); + void updateProperty(String propertyName, unsigned long cloudChangeEventTime); + + private: + LinkedList _property_list; + /* Keep track of the number of primitive properties in the Thing. If 0 it allows the early exit in updateTimestampOnLocallyChangedProperties() */ + int _numPrimitivesProperties; + /* Indicates the if the message received to be decoded is a response to the getLastValues inquiry */ + bool _isSyncMessage; + /* List of map data that will hold all the attributes of a property */ + LinkedList _map_data_list; + /* Current property name during decoding: use to look for a new property in the senml value array */ + String _currentPropertyName; + unsigned long _currentPropertyBaseTime, + _currentPropertyTime; + + enum class MapParserState { + EnterMap, + MapKey, + UndefinedKey, + BaseVersion, + BaseName, + BaseTime, + Name, + Value, + StringValue, + BooleanValue, + Time, + LeaveMap, + Complete, + Error + }; + + MapParserState handle_EnterMap(CborValue * map_iter, CborValue * value_iter, CborMapData **map_data); + MapParserState handle_MapKey(CborValue * value_iter); + MapParserState handle_UndefinedKey(CborValue * value_iter); + MapParserState handle_BaseVersion(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_BaseName(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_BaseTime(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_Name(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_Value(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_StringValue(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_BooleanValue(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_Time(CborValue * value_iter, CborMapData * map_data); + MapParserState handle_LeaveMap(CborValue * map_iter, CborValue * value_iter, CborMapData * map_data); + + static bool ifNumericConvertToDouble(CborValue * value_iter, double * numeric_val); + static double convertCborHalfFloatToDouble(uint16_t const half_val); + void freeMapDataList(LinkedList *map_data_list); }; diff --git a/types/CloudBool.h b/types/CloudBool.h index 973491b92..164595346 100644 --- a/types/CloudBool.h +++ b/types/CloudBool.h @@ -19,53 +19,59 @@ #define CLOUDBOOL_H_ /****************************************************************************** - * INCLUDE + INCLUDE ******************************************************************************/ #include #include "../ArduinoCloudProperty.hpp" /****************************************************************************** - * CLASS DECLARATION + CLASS DECLARATION ******************************************************************************/ class CloudBool : public ArduinoCloudProperty { -private: - bool _value, - _cloud_value; -public: - CloudBool() { CloudBool(false); } - CloudBool(bool v) : _value(v), _cloud_value(v) {} - operator bool() const {return _value;} - virtual bool isDifferentFromCloud() { - return _value != _cloud_value; - } - virtual void fromCloudToLocal() { - _value = _cloud_value; - } - virtual void fromLocalToCloud() { - _cloud_value = _value; - } - virtual void appendAttributesToCloud() { - appendAttribute(_value); - } - virtual void setAttributesFromCloud() { - setAttribute(_cloud_value); - } - //modifiers - CloudBool& operator=(bool v) { - _value = v; - updateLocalTimestamp(); - return *this; - } - CloudBool& operator=(CloudBool v) { - return operator=((bool)v); - } - //accessors - CloudBool operator!() const {return CloudBool(!_value);} - //friends + private: + bool _value, + _cloud_value; + public: + CloudBool() { + CloudBool(false); + } + CloudBool(bool v) : _value(v), _cloud_value(v) {} + operator bool() const { + return _value; + } + virtual bool isDifferentFromCloud() { + return _value != _cloud_value; + } + virtual void fromCloudToLocal() { + _value = _cloud_value; + } + virtual void fromLocalToCloud() { + _cloud_value = _value; + } + virtual void appendAttributesToCloud() { + appendAttribute(_value); + } + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); + } + //modifiers + CloudBool& operator=(bool v) { + _value = v; + updateLocalTimestamp(); + return *this; + } + CloudBool& operator=(CloudBool v) { + return operator=((bool)v); + } + //accessors + CloudBool operator!() const { + return CloudBool(!_value); + } + //friends }; diff --git a/types/CloudColor.h b/types/CloudColor.h index ddc68fdfc..6c6b96e41 100644 --- a/types/CloudColor.h +++ b/types/CloudColor.h @@ -19,198 +19,189 @@ #define CLOUDCOLOR_H_ /****************************************************************************** - * INCLUDE + INCLUDE ******************************************************************************/ #include #include #include "../ArduinoCloudProperty.hpp" - /****************************************************************************** - * CLASS DECLARATION - ******************************************************************************/ +/****************************************************************************** + CLASS DECLARATION + ******************************************************************************/ class Color { -public: - float hue, sat, bri; - Color(float h, float s, float b):hue(h), sat(s),bri(b) { - setColorHSB(h, s, b); - } - - bool setColorHSB(float h, float s, float b) { - if (h < 0 || h>360 || s < 0 || s>100 || b < 0 || b>100) { - hue = 0; - sat = 0; - bri = 0; - return false; - } - - hue = h; - sat = s; - bri = b; - return true; - } - - bool setColorRGB(uint8_t R, uint8_t G, uint8_t B) { - if (R < 0 || R>255 || G < 0 || G>255 || B < 0 || B>255) - return false; - float temp[3]; - float max, min, delta; - uint8_t imax, imin; - temp[0] = (float)R / 255; - temp[1] = (float)G / 255; - temp[2] = (float)B / 255; - max = temp[0]; - imax = 0; - min = temp[0]; - imin = 0; - - for (uint8_t j = 0; j < 3; j++) { - - if (temp[j] > max) { - max = temp[j]; - imax = j; - } - if (temp[j] < min) { - min = temp[j]; - imin = j; - } - } - delta = max - min; - if (delta == 0) { - hue = 0; - } - else if (imax == 0) { - uint8_t div = (temp[1] - temp[2]) / delta; - hue = 60 * (div % 6); - } - else if (imax == 1) { - hue = 60 * (((temp[2] - temp[0]) / delta) + 2); - } - else if (imax == 2) { - hue = 60 * (((temp[0] - temp[1]) / delta) + 4); - } - - if (max == 0) { - sat = 0; - } - else { - sat = (delta / max) * 100; - } - - bri = max * 100; - return true; - } - - void getRGB(uint8_t& R, uint8_t& G, uint8_t& B) { - float fC = (bri / 100) * (sat / 100); - float fHPrime = fmod(hue / 60.0, 6); - float fX = fC * (1 - fabs(fmod(fHPrime, 2) - 1)); - float fM = (bri / 100) - fC; - float fR, fG, fB; - - if (0 <= fHPrime && fHPrime < 1) { - fR = fC; - fG = fX; - fB = 0; - } - else if (1 <= fHPrime && fHPrime < 2) { - fR = fX; - fG = fC; - fB = 0; - } - else if (2 <= fHPrime && fHPrime < 3) { - fR = 0; - fG = fC; - fB = fX; - } - else if (3 <= fHPrime && fHPrime < 4) { - fR = 0; - fG = fX; - fB = fC; - } - else if (4 <= fHPrime && fHPrime < 5) { - fR = fX; - fG = 0; - fB = fC; - } - else if (5 <= fHPrime && fHPrime < 6) { - fR = fC; - fG = 0; - fB = fX; - } - else { - fR = 0; - fG = 0; - fB = 0; - } - R = lrint((fR + fM) * 255); - G = lrint((fG + fM) * 255); - B = lrint((fB + fM) * 255); - } - - Color& operator=(Color & aColor) { - hue = aColor.hue; - sat = aColor.sat; - bri = aColor.bri; - return *this; - } - - bool operator==(Color & aColor) { - return hue == aColor.hue && sat == aColor.sat && bri == aColor.bri; - } - - bool operator!=(Color & aColor) { - return !(operator==(aColor)); - } + public: + float hue, sat, bri; + Color(float h, float s, float b): hue(h), sat(s), bri(b) { + setColorHSB(h, s, b); + } + + bool setColorHSB(float h, float s, float b) { + if (h < 0 || h > 360 || s < 0 || s > 100 || b < 0 || b > 100) { + hue = 0; + sat = 0; + bri = 0; + return false; + } + + hue = h; + sat = s; + bri = b; + return true; + } + + bool setColorRGB(uint8_t R, uint8_t G, uint8_t B) { + if (R < 0 || R > 255 || G < 0 || G > 255 || B < 0 || B > 255) { + return false; + } + float temp[3]; + float max, min, delta; + uint8_t imax, imin; + temp[0] = (float)R / 255; + temp[1] = (float)G / 255; + temp[2] = (float)B / 255; + max = temp[0]; + imax = 0; + min = temp[0]; + imin = 0; + + for (uint8_t j = 0; j < 3; j++) { + + if (temp[j] > max) { + max = temp[j]; + imax = j; + } + if (temp[j] < min) { + min = temp[j]; + imin = j; + } + } + delta = max - min; + if (delta == 0) { + hue = 0; + } else if (imax == 0) { + uint8_t div = (temp[1] - temp[2]) / delta; + hue = 60 * (div % 6); + } else if (imax == 1) { + hue = 60 * (((temp[2] - temp[0]) / delta) + 2); + } else if (imax == 2) { + hue = 60 * (((temp[0] - temp[1]) / delta) + 4); + } + + if (max == 0) { + sat = 0; + } else { + sat = (delta / max) * 100; + } + + bri = max * 100; + return true; + } + + void getRGB(uint8_t& R, uint8_t& G, uint8_t& B) { + float fC = (bri / 100) * (sat / 100); + float fHPrime = fmod(hue / 60.0, 6); + float fX = fC * (1 - fabs(fmod(fHPrime, 2) - 1)); + float fM = (bri / 100) - fC; + float fR, fG, fB; + + if (0 <= fHPrime && fHPrime < 1) { + fR = fC; + fG = fX; + fB = 0; + } else if (1 <= fHPrime && fHPrime < 2) { + fR = fX; + fG = fC; + fB = 0; + } else if (2 <= fHPrime && fHPrime < 3) { + fR = 0; + fG = fC; + fB = fX; + } else if (3 <= fHPrime && fHPrime < 4) { + fR = 0; + fG = fX; + fB = fC; + } else if (4 <= fHPrime && fHPrime < 5) { + fR = fX; + fG = 0; + fB = fC; + } else if (5 <= fHPrime && fHPrime < 6) { + fR = fC; + fG = 0; + fB = fX; + } else { + fR = 0; + fG = 0; + fB = 0; + } + R = lrint((fR + fM) * 255); + G = lrint((fG + fM) * 255); + B = lrint((fB + fM) * 255); + } + + Color& operator=(Color & aColor) { + hue = aColor.hue; + sat = aColor.sat; + bri = aColor.bri; + return *this; + } + + bool operator==(Color & aColor) { + return hue == aColor.hue && sat == aColor.sat && bri == aColor.bri; + } + + bool operator!=(Color & aColor) { + return !(operator==(aColor)); + } }; class CloudColor : public ArduinoCloudProperty { -private: - Color _value, - _cloud_value; -public: - CloudColor() : _value(0, 0, 0), _cloud_value(0, 0, 0) {} - CloudColor(float hue, float saturation, float brightness) : _value(hue, saturation, brightness), _cloud_value(hue, saturation, brightness) {} - - virtual bool isDifferentFromCloud() { - - return _value != _cloud_value; - } - - CloudColor& operator=(Color aColor) { - _value.hue = aColor.hue; - _value.sat = aColor.sat; - _value.sat = aColor.bri; - updateLocalTimestamp(); - return *this; - } - - Color getCloudValue() { - return _cloud_value; - } - - Color getValue() { - return _value; - } - - virtual void fromCloudToLocal() { - _value = _cloud_value; - } - virtual void fromLocalToCloud() { - _cloud_value = _value; - } - virtual void appendAttributesToCloud() { - appendAttribute(_value.hue); - appendAttribute(_value.sat); - appendAttribute(_value.bri); - } - virtual void setAttributesFromCloud() { - setAttribute(_cloud_value.hue); - setAttribute(_cloud_value.sat); - setAttribute(_cloud_value.bri); - } + private: + Color _value, + _cloud_value; + public: + CloudColor() : _value(0, 0, 0), _cloud_value(0, 0, 0) {} + CloudColor(float hue, float saturation, float brightness) : _value(hue, saturation, brightness), _cloud_value(hue, saturation, brightness) {} + + virtual bool isDifferentFromCloud() { + + return _value != _cloud_value; + } + + CloudColor& operator=(Color aColor) { + _value.hue = aColor.hue; + _value.sat = aColor.sat; + _value.sat = aColor.bri; + updateLocalTimestamp(); + return *this; + } + + Color getCloudValue() { + return _cloud_value; + } + + Color getValue() { + return _value; + } + + virtual void fromCloudToLocal() { + _value = _cloud_value; + } + virtual void fromLocalToCloud() { + _cloud_value = _value; + } + virtual void appendAttributesToCloud() { + appendAttribute(_value.hue); + appendAttribute(_value.sat); + appendAttribute(_value.bri); + } + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value.hue); + setAttribute(_cloud_value.sat); + setAttribute(_cloud_value.bri); + } }; #endif /* CLOUDCOLOR_H_ */ \ No newline at end of file diff --git a/types/CloudFloat.h b/types/CloudFloat.h index c403980fb..e04c04936 100644 --- a/types/CloudFloat.h +++ b/types/CloudFloat.h @@ -21,88 +21,168 @@ #include /****************************************************************************** - * INCLUDE + INCLUDE ******************************************************************************/ #include #include "../ArduinoCloudProperty.hpp" /****************************************************************************** - * CLASS DECLARATION + CLASS DECLARATION ******************************************************************************/ class CloudFloat : public ArduinoCloudProperty { -private: - float _value, - _cloud_value; -public: - CloudFloat() { CloudFloat(0.0f); } - CloudFloat(float v) : _value(v), _cloud_value(v) {} - operator float() const {return _value;} - virtual bool isDifferentFromCloud() { - return _value != _cloud_value && (abs(_value - _cloud_value) >= ArduinoCloudProperty::_min_delta_property); - } - virtual void fromCloudToLocal() { - _value = _cloud_value; - } - virtual void fromLocalToCloud() { - _cloud_value = _value; - } - virtual void appendAttributesToCloud() { - appendAttribute(_value); - } - virtual void setAttributesFromCloud() { - setAttribute(_cloud_value); - } - //modifiers - CloudFloat& operator=(float v) { - _value = v; - updateLocalTimestamp(); - return *this; - } - CloudFloat& operator=(CloudFloat v) { - return operator=((float)v); - } - CloudFloat& operator+=(float v) {return operator=(_value+=v);} - CloudFloat& operator-=(float v) {return operator=(_value-=v);} - CloudFloat& operator*=(float v) {return operator=(_value*=v);} - CloudFloat& operator/=(float v) {return operator=(_value/=v);} - CloudFloat& operator++() {return operator=(_value+1.0f);} - CloudFloat& operator--() {return operator=(_value-1.0f);} - CloudFloat operator++(int) {float f =_value; operator=(_value+1.0f); return CloudFloat(_value);} - CloudFloat operator--(int) {float f =_value; operator=(_value-1.0f); return CloudFloat(_value);} + private: + float _value, + _cloud_value; + public: + CloudFloat() { + CloudFloat(0.0f); + } + CloudFloat(float v) : _value(v), _cloud_value(v) {} + operator float() const { + return _value; + } + virtual bool isDifferentFromCloud() { + return _value != _cloud_value && (abs(_value - _cloud_value) >= ArduinoCloudProperty::_min_delta_property); + } + virtual void fromCloudToLocal() { + _value = _cloud_value; + } + virtual void fromLocalToCloud() { + _cloud_value = _value; + } + virtual void appendAttributesToCloud() { + appendAttribute(_value); + } + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); + } + //modifiers + CloudFloat& operator=(float v) { + _value = v; + updateLocalTimestamp(); + return *this; + } + CloudFloat& operator=(CloudFloat v) { + return operator=((float)v); + } + CloudFloat& operator+=(float v) { + return operator=(_value += v); + } + CloudFloat& operator-=(float v) { + return operator=(_value -= v); + } + CloudFloat& operator*=(float v) { + return operator=(_value *= v); + } + CloudFloat& operator/=(float v) { + return operator=(_value /= v); + } + CloudFloat& operator++() { + return operator=(_value + 1.0f); + } + CloudFloat& operator--() { + return operator=(_value - 1.0f); + } + CloudFloat operator++(int) { + float f = _value; + operator=(_value + 1.0f); + return CloudFloat(_value); + } + CloudFloat operator--(int) { + float f = _value; + operator=(_value - 1.0f); + return CloudFloat(_value); + } - //friends - friend CloudFloat operator+(CloudFloat iw, CloudFloat v) {return iw+=v;} - friend CloudFloat operator+(CloudFloat iw, float v) {return iw+=v;} - friend CloudFloat operator+(CloudFloat iw, int v) {return iw+=(float)v;} - friend CloudFloat operator+(CloudFloat iw, double v) {return iw+=(float)v;} - friend CloudFloat operator+(float v, CloudFloat iw) {return CloudFloat(v)+=iw;} - friend CloudFloat operator+(int v, CloudFloat iw) {return CloudFloat(v)+=iw;} - friend CloudFloat operator+(double v, CloudFloat iw) {return CloudFloat(v)+=iw;} - friend CloudFloat operator-(CloudFloat iw, CloudFloat v) {return iw-=v;} - friend CloudFloat operator-(CloudFloat iw, float v) {return iw-=v;} - friend CloudFloat operator-(CloudFloat iw, int v) {return iw-=(float)v;} - friend CloudFloat operator-(CloudFloat iw, double v) {return iw-=(float)v;} - friend CloudFloat operator-(float v, CloudFloat iw) {return CloudFloat(v)-=iw;} - friend CloudFloat operator-(int v, CloudFloat iw) {return CloudFloat(v)-=iw;} - friend CloudFloat operator-(double v, CloudFloat iw) {return CloudFloat(v)-=iw;} - friend CloudFloat operator*(CloudFloat iw, CloudFloat v) {return iw*=v;} - friend CloudFloat operator*(CloudFloat iw, float v) {return iw*=v;} - friend CloudFloat operator*(CloudFloat iw, int v) {return iw*=(float)v;} - friend CloudFloat operator*(CloudFloat iw, double v) {return iw*=(float)v;} - friend CloudFloat operator*(float v, CloudFloat iw) {return CloudFloat(v)*=iw;} - friend CloudFloat operator*(int v, CloudFloat iw) {return CloudFloat(v)*=iw;} - friend CloudFloat operator*(double v, CloudFloat iw) {return CloudFloat(v)*=iw;} - friend CloudFloat operator/(CloudFloat iw, CloudFloat v) {return iw/=v;} - friend CloudFloat operator/(CloudFloat iw, float v) {return iw/=v;} - friend CloudFloat operator/(CloudFloat iw, int v) {return iw/=(float)v;} - friend CloudFloat operator/(CloudFloat iw, double v) {return iw/=(float)v;} - friend CloudFloat operator/(float v, CloudFloat iw) {return CloudFloat(v)/=iw;} - friend CloudFloat operator/(int v, CloudFloat iw) {return CloudFloat(v)/=iw;} - friend CloudFloat operator/(double v, CloudFloat iw) {return CloudFloat(v)/=iw;} + //friends + friend CloudFloat operator+(CloudFloat iw, CloudFloat v) { + return iw += v; + } + friend CloudFloat operator+(CloudFloat iw, float v) { + return iw += v; + } + friend CloudFloat operator+(CloudFloat iw, int v) { + return iw += (float)v; + } + friend CloudFloat operator+(CloudFloat iw, double v) { + return iw += (float)v; + } + friend CloudFloat operator+(float v, CloudFloat iw) { + return CloudFloat(v) += iw; + } + friend CloudFloat operator+(int v, CloudFloat iw) { + return CloudFloat(v) += iw; + } + friend CloudFloat operator+(double v, CloudFloat iw) { + return CloudFloat(v) += iw; + } + friend CloudFloat operator-(CloudFloat iw, CloudFloat v) { + return iw -= v; + } + friend CloudFloat operator-(CloudFloat iw, float v) { + return iw -= v; + } + friend CloudFloat operator-(CloudFloat iw, int v) { + return iw -= (float)v; + } + friend CloudFloat operator-(CloudFloat iw, double v) { + return iw -= (float)v; + } + friend CloudFloat operator-(float v, CloudFloat iw) { + return CloudFloat(v) -= iw; + } + friend CloudFloat operator-(int v, CloudFloat iw) { + return CloudFloat(v) -= iw; + } + friend CloudFloat operator-(double v, CloudFloat iw) { + return CloudFloat(v) -= iw; + } + friend CloudFloat operator*(CloudFloat iw, CloudFloat v) { + return iw *= v; + } + friend CloudFloat operator*(CloudFloat iw, float v) { + return iw *= v; + } + friend CloudFloat operator*(CloudFloat iw, int v) { + return iw *= (float)v; + } + friend CloudFloat operator*(CloudFloat iw, double v) { + return iw *= (float)v; + } + friend CloudFloat operator*(float v, CloudFloat iw) { + return CloudFloat(v) *= iw; + } + friend CloudFloat operator*(int v, CloudFloat iw) { + return CloudFloat(v) *= iw; + } + friend CloudFloat operator*(double v, CloudFloat iw) { + return CloudFloat(v) *= iw; + } + friend CloudFloat operator/(CloudFloat iw, CloudFloat v) { + return iw /= v; + } + friend CloudFloat operator/(CloudFloat iw, float v) { + return iw /= v; + } + friend CloudFloat operator/(CloudFloat iw, int v) { + return iw /= (float)v; + } + friend CloudFloat operator/(CloudFloat iw, double v) { + return iw /= (float)v; + } + friend CloudFloat operator/(float v, CloudFloat iw) { + return CloudFloat(v) /= iw; + } + friend CloudFloat operator/(int v, CloudFloat iw) { + return CloudFloat(v) /= iw; + } + friend CloudFloat operator/(double v, CloudFloat iw) { + return CloudFloat(v) /= iw; + } }; diff --git a/types/CloudInt.h b/types/CloudInt.h index 035c27d71..b497e35a3 100644 --- a/types/CloudInt.h +++ b/types/CloudInt.h @@ -19,100 +19,204 @@ #define CLOUDINT_H_ /****************************************************************************** - * INCLUDE + INCLUDE ******************************************************************************/ #include #include "../ArduinoCloudProperty.hpp" /****************************************************************************** - * CLASS DECLARATION + CLASS DECLARATION ******************************************************************************/ class CloudInt : public ArduinoCloudProperty { -private: - int _value, - _cloud_value; -public: - CloudInt() { CloudInt(0); } - CloudInt(int v) : _value(v), _cloud_value(v) {} - operator int() const {return _value;} - virtual bool isDifferentFromCloud() { - return _value != _cloud_value && (abs(_value - _cloud_value) >= ArduinoCloudProperty::_min_delta_property); - } - virtual void fromCloudToLocal() { - _value = _cloud_value; - } - virtual void fromLocalToCloud() { - _cloud_value = _value; - } - virtual void appendAttributesToCloud() { - appendAttribute(_value); - } - virtual void setAttributesFromCloud() { - setAttribute(_cloud_value); - } - //modifiers - CloudInt& operator=(int v) { - _value = v; - updateLocalTimestamp(); - return *this; - } - CloudInt& operator=(CloudInt v) { - return operator=((int)v); - } - CloudInt& operator+=(int v) {return operator=(_value+=v);} - CloudInt& operator-=(int v) {return operator=(_value-=v);} - CloudInt& operator*=(int v) {return operator=(_value*=v);} - CloudInt& operator/=(int v) {return operator=(_value/=v);} - CloudInt& operator%=(int v) {return operator=(_value%=v);} - CloudInt& operator++() {return operator=(++_value);} - CloudInt& operator--() {return operator=(--_value);} - CloudInt operator++(int) {int temp =_value; operator=(_value+1); return CloudInt(_value);} - CloudInt operator--(int) {int temp =_value; operator=(_value-1); return CloudInt(_value);} - CloudInt& operator&=(int v) {return operator=(_value&=v);} - CloudInt& operator|=(int v) {return operator=(_value|=v);} - CloudInt& operator^=(int v) {return operator=(_value^=v);} - CloudInt& operator<<=(int v) {return operator=(_value<<=v);} - CloudInt& operator>>=(int v) {return operator=(_value>>=v);} - //accessors - CloudInt operator+() const {return CloudInt(+_value);} - CloudInt operator-() const {return CloudInt(-_value);} - CloudInt operator!() const {return CloudInt(!_value);} - CloudInt operator~() const {return CloudInt(~_value);} - //friends - friend CloudInt operator+(CloudInt iw, CloudInt v) {return iw+=v;} - friend CloudInt operator+(CloudInt iw, int v) {return iw+=v;} - friend CloudInt operator+(int v, CloudInt iw) {return CloudInt(v)+=iw;} - friend CloudInt operator-(CloudInt iw, CloudInt v) {return iw-=v;} - friend CloudInt operator-(CloudInt iw, int v) {return iw-=v;} - friend CloudInt operator-(int v, CloudInt iw) {return CloudInt(v)-=iw;} - friend CloudInt operator*(CloudInt iw, CloudInt v) {return iw*=v;} - friend CloudInt operator*(CloudInt iw, int v) {return iw*=v;} - friend CloudInt operator*(int v, CloudInt iw) {return CloudInt(v)*=iw;} - friend CloudInt operator/(CloudInt iw, CloudInt v) {return iw/=v;} - friend CloudInt operator/(CloudInt iw, int v) {return iw/=v;} - friend CloudInt operator/(int v, CloudInt iw) {return CloudInt(v)/=iw;} - friend CloudInt operator%(CloudInt iw, CloudInt v) {return iw%=v;} - friend CloudInt operator%(CloudInt iw, int v) {return iw%=v;} - friend CloudInt operator%(int v, CloudInt iw) {return CloudInt(v)%=iw;} - friend CloudInt operator&(CloudInt iw, CloudInt v) {return iw&=v;} - friend CloudInt operator&(CloudInt iw, int v) {return iw&=v;} - friend CloudInt operator&(int v, CloudInt iw) {return CloudInt(v)&=iw;} - friend CloudInt operator|(CloudInt iw, CloudInt v) {return iw|=v;} - friend CloudInt operator|(CloudInt iw, int v) {return iw|=v;} - friend CloudInt operator|(int v, CloudInt iw) {return CloudInt(v)|=iw;} - friend CloudInt operator^(CloudInt iw, CloudInt v) {return iw^=v;} - friend CloudInt operator^(CloudInt iw, int v) {return iw^=v;} - friend CloudInt operator^(int v, CloudInt iw) {return CloudInt(v)^=iw;} - friend CloudInt operator<<(CloudInt iw, CloudInt v) {return iw<<=v;} - friend CloudInt operator<<(CloudInt iw, int v) {return iw<<=v;} - friend CloudInt operator<<(int v, CloudInt iw) {return CloudInt(v)<<=iw;} - friend CloudInt operator>>(CloudInt iw, CloudInt v) {return iw>>=v;} - friend CloudInt operator>>(CloudInt iw, int v) {return iw>>=v;} - friend CloudInt operator>>(int v, CloudInt iw) {return CloudInt(v)>>=iw;} + private: + int _value, + _cloud_value; + public: + CloudInt() { + CloudInt(0); + } + CloudInt(int v) : _value(v), _cloud_value(v) {} + operator int() const { + return _value; + } + virtual bool isDifferentFromCloud() { + return _value != _cloud_value && (abs(_value - _cloud_value) >= ArduinoCloudProperty::_min_delta_property); + } + virtual void fromCloudToLocal() { + _value = _cloud_value; + } + virtual void fromLocalToCloud() { + _cloud_value = _value; + } + virtual void appendAttributesToCloud() { + appendAttribute(_value); + } + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); + } + //modifiers + CloudInt& operator=(int v) { + _value = v; + updateLocalTimestamp(); + return *this; + } + CloudInt& operator=(CloudInt v) { + return operator=((int)v); + } + CloudInt& operator+=(int v) { + return operator=(_value += v); + } + CloudInt& operator-=(int v) { + return operator=(_value -= v); + } + CloudInt& operator*=(int v) { + return operator=(_value *= v); + } + CloudInt& operator/=(int v) { + return operator=(_value /= v); + } + CloudInt& operator%=(int v) { + return operator=(_value %= v); + } + CloudInt& operator++() { + return operator=(++_value); + } + CloudInt& operator--() { + return operator=(--_value); + } + CloudInt operator++(int) { + int temp = _value; + operator=(_value + 1); + return CloudInt(_value); + } + CloudInt operator--(int) { + int temp = _value; + operator=(_value - 1); + return CloudInt(_value); + } + CloudInt& operator&=(int v) { + return operator=(_value &= v); + } + CloudInt& operator|=(int v) { + return operator=(_value |= v); + } + CloudInt& operator^=(int v) { + return operator=(_value ^= v); + } + CloudInt& operator<<=(int v) { + return operator=(_value <<= v); + } + CloudInt& operator>>=(int v) { + return operator=(_value >>= v); + } + //accessors + CloudInt operator+() const { + return CloudInt(+_value); + } + CloudInt operator-() const { + return CloudInt(-_value); + } + CloudInt operator!() const { + return CloudInt(!_value); + } + CloudInt operator~() const { + return CloudInt(~_value); + } + //friends + friend CloudInt operator+(CloudInt iw, CloudInt v) { + return iw += v; + } + friend CloudInt operator+(CloudInt iw, int v) { + return iw += v; + } + friend CloudInt operator+(int v, CloudInt iw) { + return CloudInt(v) += iw; + } + friend CloudInt operator-(CloudInt iw, CloudInt v) { + return iw -= v; + } + friend CloudInt operator-(CloudInt iw, int v) { + return iw -= v; + } + friend CloudInt operator-(int v, CloudInt iw) { + return CloudInt(v) -= iw; + } + friend CloudInt operator*(CloudInt iw, CloudInt v) { + return iw *= v; + } + friend CloudInt operator*(CloudInt iw, int v) { + return iw *= v; + } + friend CloudInt operator*(int v, CloudInt iw) { + return CloudInt(v) *= iw; + } + friend CloudInt operator/(CloudInt iw, CloudInt v) { + return iw /= v; + } + friend CloudInt operator/(CloudInt iw, int v) { + return iw /= v; + } + friend CloudInt operator/(int v, CloudInt iw) { + return CloudInt(v) /= iw; + } + friend CloudInt operator%(CloudInt iw, CloudInt v) { + return iw %= v; + } + friend CloudInt operator%(CloudInt iw, int v) { + return iw %= v; + } + friend CloudInt operator%(int v, CloudInt iw) { + return CloudInt(v) %= iw; + } + friend CloudInt operator&(CloudInt iw, CloudInt v) { + return iw &= v; + } + friend CloudInt operator&(CloudInt iw, int v) { + return iw &= v; + } + friend CloudInt operator&(int v, CloudInt iw) { + return CloudInt(v) &= iw; + } + friend CloudInt operator|(CloudInt iw, CloudInt v) { + return iw |= v; + } + friend CloudInt operator|(CloudInt iw, int v) { + return iw |= v; + } + friend CloudInt operator|(int v, CloudInt iw) { + return CloudInt(v) |= iw; + } + friend CloudInt operator^(CloudInt iw, CloudInt v) { + return iw ^= v; + } + friend CloudInt operator^(CloudInt iw, int v) { + return iw ^= v; + } + friend CloudInt operator^(int v, CloudInt iw) { + return CloudInt(v) ^= iw; + } + friend CloudInt operator<<(CloudInt iw, CloudInt v) { + return iw <<= v; + } + friend CloudInt operator<<(CloudInt iw, int v) { + return iw <<= v; + } + friend CloudInt operator<<(int v, CloudInt iw) { + return CloudInt(v) <<= iw; + } + friend CloudInt operator>>(CloudInt iw, CloudInt v) { + return iw >>= v; + } + friend CloudInt operator>>(CloudInt iw, int v) { + return iw >>= v; + } + friend CloudInt operator>>(int v, CloudInt iw) { + return CloudInt(v) >>= iw; + } }; diff --git a/types/CloudLocation.h b/types/CloudLocation.h index cd6ecd311..f21253335 100644 --- a/types/CloudLocation.h +++ b/types/CloudLocation.h @@ -19,7 +19,7 @@ #define CLOUDLOCATION_H_ /****************************************************************************** - * INCLUDE + INCLUDE ******************************************************************************/ #include @@ -27,7 +27,7 @@ #include "../ArduinoCloudProperty.hpp" /****************************************************************************** - * CLASS DECLARATION + CLASS DECLARATION ******************************************************************************/ @@ -54,47 +54,47 @@ class Location { }; class CloudLocation : public ArduinoCloudProperty { -private: - Location _value, - _cloud_value; -public: - CloudLocation() : _value(0, 0), _cloud_value(0, 0) {} - CloudLocation(float lat, float lon) : _value(lat, lon), _cloud_value(lat, lon) {} - virtual bool isDifferentFromCloud() { - float distance = _value - _cloud_value; - return _value != _cloud_value && (abs(distance) >= ArduinoCloudProperty::_min_delta_property); - } - - CloudLocation& operator=(Location aLocation) { - _value.lat = aLocation.lat; - _value.lon = aLocation.lon; - updateLocalTimestamp(); - return *this; - } + private: + Location _value, + _cloud_value; + public: + CloudLocation() : _value(0, 0), _cloud_value(0, 0) {} + CloudLocation(float lat, float lon) : _value(lat, lon), _cloud_value(lat, lon) {} + virtual bool isDifferentFromCloud() { + float distance = _value - _cloud_value; + return _value != _cloud_value && (abs(distance) >= ArduinoCloudProperty::_min_delta_property); + } - Location getCloudValue() { - return _cloud_value; - } + CloudLocation& operator=(Location aLocation) { + _value.lat = aLocation.lat; + _value.lon = aLocation.lon; + updateLocalTimestamp(); + return *this; + } - Location getValue() { - return _value; - } + Location getCloudValue() { + return _cloud_value; + } + Location getValue() { + return _value; + } - virtual void fromCloudToLocal() { - _value = _cloud_value; - } - virtual void fromLocalToCloud() { - _cloud_value = _value; - } - virtual void appendAttributesToCloud() { - appendAttribute(_value.lat); - appendAttribute(_value.lon); - } - virtual void setAttributesFromCloud() { - setAttribute(_cloud_value.lat); - setAttribute(_cloud_value.lon); - } + + virtual void fromCloudToLocal() { + _value = _cloud_value; + } + virtual void fromLocalToCloud() { + _cloud_value = _value; + } + virtual void appendAttributesToCloud() { + appendAttribute(_value.lat); + appendAttribute(_value.lon); + } + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value.lat); + setAttribute(_cloud_value.lon); + } }; #endif /* CLOUDLOCATION_H_ */ diff --git a/types/CloudString.h b/types/CloudString.h index 1b2c8cbc5..a1d5bccc1 100644 --- a/types/CloudString.h +++ b/types/CloudString.h @@ -19,66 +19,70 @@ #define CLOUDSTRING_H_ /****************************************************************************** - * INCLUDE + INCLUDE ******************************************************************************/ #include #include "../ArduinoCloudProperty.hpp" /****************************************************************************** - * CLASS DECLARATION + CLASS DECLARATION ******************************************************************************/ class CloudString : public ArduinoCloudProperty { -private: - String _value, - _cloud_value; -public: - CloudString() { CloudString(""); } - CloudString(const char *v) { CloudString(String(v)); } - CloudString(String v) : _value(v), _cloud_value(v) {} - operator String() const {return _value;} - virtual bool isDifferentFromCloud() { - return _value != _cloud_value; - } - virtual void fromCloudToLocal() { - _value = _cloud_value; - } - virtual void fromLocalToCloud() { - _cloud_value = _value; - } - virtual void appendAttributesToCloud() { - appendAttribute(_value); - } - virtual void setAttributesFromCloud() { - setAttribute(_cloud_value); - } - //modifiers - CloudString& operator=(String v) { - _value = v; - updateLocalTimestamp(); - return *this; - } - CloudString& operator=(const char *v) { - return operator=(String(v)); - } - CloudString& operator+=(String v) { - return operator=(_value += v); - } - bool operator==(const char *c) const - { - return operator==(String(c)); - } - bool operator==(String c) const - { - return _value == c; - } - //friends - friend CloudString operator+(CloudString cs, String v) { - return cs+=v; - } + private: + String _value, + _cloud_value; + public: + CloudString() { + CloudString(""); + } + CloudString(const char *v) { + CloudString(String(v)); + } + CloudString(String v) : _value(v), _cloud_value(v) {} + operator String() const { + return _value; + } + virtual bool isDifferentFromCloud() { + return _value != _cloud_value; + } + virtual void fromCloudToLocal() { + _value = _cloud_value; + } + virtual void fromLocalToCloud() { + _cloud_value = _value; + } + virtual void appendAttributesToCloud() { + appendAttribute(_value); + } + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); + } + //modifiers + CloudString& operator=(String v) { + _value = v; + updateLocalTimestamp(); + return *this; + } + CloudString& operator=(const char *v) { + return operator=(String(v)); + } + CloudString& operator+=(String v) { + return operator=(_value += v); + } + bool operator==(const char *c) const { + return operator==(String(c)); + } + bool operator==(String c) const { + return _value == c; + } + //friends + friend CloudString operator+(CloudString cs, String v) { + return cs += v; + } }; diff --git a/types/CloudWrapperBool.h b/types/CloudWrapperBool.h index 321c936e5..b98923cd4 100644 --- a/types/CloudWrapperBool.h +++ b/types/CloudWrapperBool.h @@ -19,44 +19,44 @@ #define CLOUDWRAPPERBOOL_H_ /****************************************************************************** - * INCLUDE + INCLUDE ******************************************************************************/ #include #include "../ArduinoCloudProperty.hpp" /****************************************************************************** - * CLASS DECLARATION + CLASS DECLARATION ******************************************************************************/ class CloudWrapperBool : public ArduinoCloudProperty { -private: - bool &_primitive_value, - _cloud_value, - _local_value; -public: - CloudWrapperBool(bool& v) : _primitive_value(v), _cloud_value(v), _local_value(v) {} - virtual bool isDifferentFromCloud() { - return _primitive_value != _cloud_value; - } - virtual void fromCloudToLocal() { - _primitive_value = _cloud_value; - } - virtual void fromLocalToCloud() { - _cloud_value = _primitive_value; - } - virtual void appendAttributesToCloud() { - appendAttribute(_primitive_value); - } - virtual void setAttributesFromCloud() { - setAttribute(_cloud_value); - } - virtual bool isPrimitive() { - return true; - } - virtual bool isChangedLocally() { - return _primitive_value != _local_value; - } + private: + bool &_primitive_value, + _cloud_value, + _local_value; + public: + CloudWrapperBool(bool& v) : _primitive_value(v), _cloud_value(v), _local_value(v) {} + virtual bool isDifferentFromCloud() { + return _primitive_value != _cloud_value; + } + virtual void fromCloudToLocal() { + _primitive_value = _cloud_value; + } + virtual void fromLocalToCloud() { + _cloud_value = _primitive_value; + } + virtual void appendAttributesToCloud() { + appendAttribute(_primitive_value); + } + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); + } + virtual bool isPrimitive() { + return true; + } + virtual bool isChangedLocally() { + return _primitive_value != _local_value; + } }; diff --git a/types/CloudWrapperFloat.h b/types/CloudWrapperFloat.h index f00401ec4..740831878 100644 --- a/types/CloudWrapperFloat.h +++ b/types/CloudWrapperFloat.h @@ -21,44 +21,44 @@ #include /****************************************************************************** - * INCLUDE + INCLUDE ******************************************************************************/ #include #include "../ArduinoCloudProperty.hpp" /****************************************************************************** - * CLASS DECLARATION + CLASS DECLARATION ******************************************************************************/ class CloudWrapperFloat : public ArduinoCloudProperty { -private: - float &_primitive_value, - _cloud_value, - _local_value; -public: - CloudWrapperFloat(float& v) : _primitive_value(v), _cloud_value(v), _local_value(v) {} - virtual bool isDifferentFromCloud() { - return _primitive_value != _cloud_value && (abs(_primitive_value - _cloud_value) >= ArduinoCloudProperty::_min_delta_property); - } - virtual void fromCloudToLocal() { - _primitive_value = _cloud_value; - } - virtual void fromLocalToCloud() { - _cloud_value = _primitive_value; - } - virtual void appendAttributesToCloud() { - appendAttribute(_primitive_value); - } - virtual void setAttributesFromCloud() { - setAttribute(_cloud_value); - } - virtual bool isPrimitive() { - return true; - } - virtual bool isChangedLocally() { - return _primitive_value != _local_value; - } + private: + float &_primitive_value, + _cloud_value, + _local_value; + public: + CloudWrapperFloat(float& v) : _primitive_value(v), _cloud_value(v), _local_value(v) {} + virtual bool isDifferentFromCloud() { + return _primitive_value != _cloud_value && (abs(_primitive_value - _cloud_value) >= ArduinoCloudProperty::_min_delta_property); + } + virtual void fromCloudToLocal() { + _primitive_value = _cloud_value; + } + virtual void fromLocalToCloud() { + _cloud_value = _primitive_value; + } + virtual void appendAttributesToCloud() { + appendAttribute(_primitive_value); + } + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); + } + virtual bool isPrimitive() { + return true; + } + virtual bool isChangedLocally() { + return _primitive_value != _local_value; + } }; diff --git a/types/CloudWrapperInt.h b/types/CloudWrapperInt.h index b6dc6db6f..dd69f28fa 100644 --- a/types/CloudWrapperInt.h +++ b/types/CloudWrapperInt.h @@ -19,44 +19,44 @@ #define CLOUDWRAPPERINT_H_ /****************************************************************************** - * INCLUDE + INCLUDE ******************************************************************************/ #include #include "../ArduinoCloudProperty.hpp" /****************************************************************************** - * CLASS DECLARATION + CLASS DECLARATION ******************************************************************************/ class CloudWrapperInt : public ArduinoCloudProperty { -private: - int &_primitive_value, - _cloud_value, - _local_value; -public: - CloudWrapperInt(int& v) : _primitive_value(v), _cloud_value(v), _local_value(v) {} - virtual bool isDifferentFromCloud() { - return _primitive_value != _cloud_value && (abs(_primitive_value - _cloud_value) >= ArduinoCloudProperty::_min_delta_property); - } - virtual void fromCloudToLocal() { - _primitive_value = _cloud_value; - } - virtual void fromLocalToCloud() { - _cloud_value = _primitive_value; - } - virtual void appendAttributesToCloud() { - appendAttribute(_primitive_value); - } - virtual void setAttributesFromCloud() { - setAttribute(_cloud_value); - } - virtual bool isPrimitive() { - return true; - } - virtual bool isChangedLocally() { - return _primitive_value != _local_value; - } + private: + int &_primitive_value, + _cloud_value, + _local_value; + public: + CloudWrapperInt(int& v) : _primitive_value(v), _cloud_value(v), _local_value(v) {} + virtual bool isDifferentFromCloud() { + return _primitive_value != _cloud_value && (abs(_primitive_value - _cloud_value) >= ArduinoCloudProperty::_min_delta_property); + } + virtual void fromCloudToLocal() { + _primitive_value = _cloud_value; + } + virtual void fromLocalToCloud() { + _cloud_value = _primitive_value; + } + virtual void appendAttributesToCloud() { + appendAttribute(_primitive_value); + } + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); + } + virtual bool isPrimitive() { + return true; + } + virtual bool isChangedLocally() { + return _primitive_value != _local_value; + } }; diff --git a/types/CloudWrapperString.h b/types/CloudWrapperString.h index 9d4424dff..d404e7563 100644 --- a/types/CloudWrapperString.h +++ b/types/CloudWrapperString.h @@ -19,48 +19,48 @@ #define CLOUDWRAPPERSTRING_H_ /****************************************************************************** - * INCLUDE + INCLUDE ******************************************************************************/ #include #include "../ArduinoCloudProperty.hpp" /****************************************************************************** - * CLASS DECLARATION + CLASS DECLARATION ******************************************************************************/ class CloudWrapperString : public ArduinoCloudProperty { -private: - String &_primitive_value, - _cloud_value, - _local_value; -public: - CloudWrapperString(String& v) : - _primitive_value(v), - _cloud_value(v), - _local_value(v) { - } - virtual bool isDifferentFromCloud() { - return _primitive_value != _cloud_value; - } - virtual void fromCloudToLocal() { - _primitive_value = _cloud_value; - } - virtual void fromLocalToCloud() { - _cloud_value = _primitive_value; - } - virtual void appendAttributesToCloud() { - appendAttribute(_primitive_value); - } - virtual void setAttributesFromCloud() { - setAttribute(_cloud_value); - } - virtual bool isPrimitive() { - return true; - } - virtual bool isChangedLocally() { - return _primitive_value != _local_value; - } + private: + String &_primitive_value, + _cloud_value, + _local_value; + public: + CloudWrapperString(String& v) : + _primitive_value(v), + _cloud_value(v), + _local_value(v) { + } + virtual bool isDifferentFromCloud() { + return _primitive_value != _cloud_value; + } + virtual void fromCloudToLocal() { + _primitive_value = _cloud_value; + } + virtual void fromLocalToCloud() { + _cloud_value = _primitive_value; + } + virtual void appendAttributesToCloud() { + appendAttribute(_primitive_value); + } + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value); + } + virtual bool isPrimitive() { + return true; + } + virtual bool isChangedLocally() { + return _primitive_value != _local_value; + } }; From e7e94dd5975972f00ccca38cf3e4659c979e865b Mon Sep 17 00:00:00 2001 From: mirkokurt Date: Mon, 29 Apr 2019 14:20:06 +0200 Subject: [PATCH 132/175] Improve test coverage --- ArduinoCloudProperty.hpp | 3 --- ArduinoCloudThing.cpp | 3 ++- ArduinoCloudThing.h | 1 + types/CloudColor.h | 9 +++++---- types/CloudWrapperBase.h | 38 ++++++++++++++++++++++++++++++++++++++ types/CloudWrapperBool.h | 4 ++-- types/CloudWrapperFloat.h | 4 ++-- types/CloudWrapperInt.h | 4 ++-- types/CloudWrapperString.h | 4 ++-- 9 files changed, 54 insertions(+), 16 deletions(-) create mode 100644 types/CloudWrapperBase.h diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.hpp index aabdea8df..11758ca70 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.hpp @@ -174,9 +174,6 @@ class ArduinoCloudProperty { virtual bool isPrimitive() { return false; }; - virtual bool isChangedLocally() { - return false; - }; protected: /* Variables used for UpdatePolicy::OnChange */ float _min_delta_property; diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index d5a52ffed..a8d5680d8 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -180,12 +180,13 @@ ArduinoCloudProperty * ArduinoCloudThing::getProperty(String const & name) { return NULL; } +// this function updates the timestamps on the primitive properties that have been modified locally since last cloud synchronization void ArduinoCloudThing::updateTimestampOnLocallyChangedProperties() { if (_numPrimitivesProperties == 0) { return; } else { for (int i = 0; i < _property_list.size(); i++) { - ArduinoCloudProperty * p = _property_list.get(i); + CloudWrapperBase * p = (CloudWrapperBase *)_property_list.get(i); if (p->isPrimitive() && p->isChangedLocally() && p->isReadableByCloud()) { p->updateLocalTimestamp(); } diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index b9631acbd..98103c88d 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -30,6 +30,7 @@ #include "types/CloudString.h" #include "types/CloudLocation.h" #include "types/CloudColor.h" +#include "types/CloudWrapperBase.h" /****************************************************************************** CONSTANTS diff --git a/types/CloudColor.h b/types/CloudColor.h index 6c6b96e41..1611778e9 100644 --- a/types/CloudColor.h +++ b/types/CloudColor.h @@ -68,21 +68,22 @@ class Color { for (uint8_t j = 0; j < 3; j++) { - if (temp[j] > max) { + if (temp[j] >= max) { max = temp[j]; imax = j; } - if (temp[j] < min) { + if (temp[j] <= min) { min = temp[j]; imin = j; } } + delta = max - min; if (delta == 0) { hue = 0; } else if (imax == 0) { - uint8_t div = (temp[1] - temp[2]) / delta; - hue = 60 * (div % 6); + + hue = 60 * fmod((temp[1] - temp[2]) / delta, 6); } else if (imax == 1) { hue = 60 * (((temp[2] - temp[0]) / delta) + 2); } else if (imax == 2) { diff --git a/types/CloudWrapperBase.h b/types/CloudWrapperBase.h new file mode 100644 index 000000000..21d537158 --- /dev/null +++ b/types/CloudWrapperBase.h @@ -0,0 +1,38 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDWRAPPERBASE_H_ +#define CLOUDWRAPPERBASE_H_ + +/****************************************************************************** + INCLUDE + ******************************************************************************/ + +#include +#include "../ArduinoCloudProperty.hpp" + +/****************************************************************************** + CLASS DECLARATION + ******************************************************************************/ + +class CloudWrapperBase : public ArduinoCloudProperty { + public: + virtual bool isChangedLocally() = 0; +}; + + +#endif /* CLOUDWRAPPERBASE_H_ */ diff --git a/types/CloudWrapperBool.h b/types/CloudWrapperBool.h index b98923cd4..483cefc99 100644 --- a/types/CloudWrapperBool.h +++ b/types/CloudWrapperBool.h @@ -23,13 +23,13 @@ ******************************************************************************/ #include -#include "../ArduinoCloudProperty.hpp" +#include "CloudWrapperBase.h" /****************************************************************************** CLASS DECLARATION ******************************************************************************/ -class CloudWrapperBool : public ArduinoCloudProperty { +class CloudWrapperBool : public CloudWrapperBase { private: bool &_primitive_value, _cloud_value, diff --git a/types/CloudWrapperFloat.h b/types/CloudWrapperFloat.h index 740831878..0c69f6df6 100644 --- a/types/CloudWrapperFloat.h +++ b/types/CloudWrapperFloat.h @@ -25,13 +25,13 @@ ******************************************************************************/ #include -#include "../ArduinoCloudProperty.hpp" +#include "CloudWrapperBase.h" /****************************************************************************** CLASS DECLARATION ******************************************************************************/ -class CloudWrapperFloat : public ArduinoCloudProperty { +class CloudWrapperFloat : public CloudWrapperBase { private: float &_primitive_value, _cloud_value, diff --git a/types/CloudWrapperInt.h b/types/CloudWrapperInt.h index dd69f28fa..88c4a2f9a 100644 --- a/types/CloudWrapperInt.h +++ b/types/CloudWrapperInt.h @@ -23,13 +23,13 @@ ******************************************************************************/ #include -#include "../ArduinoCloudProperty.hpp" +#include "CloudWrapperBase.h" /****************************************************************************** CLASS DECLARATION ******************************************************************************/ -class CloudWrapperInt : public ArduinoCloudProperty { +class CloudWrapperInt : public CloudWrapperBase { private: int &_primitive_value, _cloud_value, diff --git a/types/CloudWrapperString.h b/types/CloudWrapperString.h index d404e7563..e384365ad 100644 --- a/types/CloudWrapperString.h +++ b/types/CloudWrapperString.h @@ -23,13 +23,13 @@ ******************************************************************************/ #include -#include "../ArduinoCloudProperty.hpp" +#include "CloudWrapperBase.h" /****************************************************************************** CLASS DECLARATION ******************************************************************************/ -class CloudWrapperString : public ArduinoCloudProperty { +class CloudWrapperString : public CloudWrapperBase { private: String &_primitive_value, _cloud_value, From c338a3e0d79232db6a5fb14332f99bb545923656 Mon Sep 17 00:00:00 2001 From: ilcato Date: Thu, 2 May 2019 10:52:07 +0200 Subject: [PATCH 133/175] Incorporating changes suggested by @lxrobotics --- ArduinoCloudProperty.cpp | 30 +++++++++---------- ...loudProperty.hpp => ArduinoCloudProperty.h | 23 +++++++------- ArduinoCloudThing.h | 10 +++---- types/CloudBool.h | 2 +- types/CloudColor.h | 2 +- types/CloudFloat.h | 2 +- types/CloudInt.h | 2 +- types/CloudLocation.h | 2 +- types/CloudString.h | 2 +- types/CloudWrapperBase.h | 2 +- 10 files changed, 37 insertions(+), 40 deletions(-) rename ArduinoCloudProperty.hpp => ArduinoCloudProperty.h (92%) diff --git a/ArduinoCloudProperty.cpp b/ArduinoCloudProperty.cpp index 7dfdc5b97..22f3599d0 100644 --- a/ArduinoCloudProperty.cpp +++ b/ArduinoCloudProperty.cpp @@ -15,7 +15,7 @@ // a commercial license, send an email to license@arduino.cc. // -#include "ArduinoCloudProperty.hpp" +#include "ArduinoCloudProperty.h" #ifdef ARDUINO_ARCH_SAMD #include @@ -47,7 +47,6 @@ ArduinoCloudProperty::ArduinoCloudProperty() _update_interval_millis(0), _last_local_change_timestamp(0), _last_cloud_change_timestamp(0), - _encoder(nullptr), _map_data_list(nullptr) { } @@ -64,7 +63,7 @@ ArduinoCloudProperty & ArduinoCloudProperty::onUpdate(UpdateCallbackFunc func) { return (*this); } -ArduinoCloudProperty & ArduinoCloudProperty::onSync(void (*func)(ArduinoCloudProperty & property)) { +ArduinoCloudProperty & ArduinoCloudProperty::onSync(SyncCallbackFunc func) { _sync_callback_func = func; return (*this); } @@ -117,44 +116,43 @@ void ArduinoCloudProperty::execCallbackOnSync() { } void ArduinoCloudProperty::append(CborEncoder *encoder) { - _encoder = encoder; - appendAttributesToCloud(); + appendAttributesToCloudReal(encoder); fromLocalToCloud(); _has_been_updated_once = true; _last_updated_millis = millis(); } -void ArduinoCloudProperty::appendAttributeReal(bool value, String attributeName) { +void ArduinoCloudProperty::appendAttributeReal(bool value, String attributeName, CborEncoder *encoder) { appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) { cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::BooleanValue)); cbor_encode_boolean(&mapEncoder, value); - }); + }, encoder); } -void ArduinoCloudProperty::appendAttributeReal(int value, String attributeName) { +void ArduinoCloudProperty::appendAttributeReal(int value, String attributeName, CborEncoder *encoder) { appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) { cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Value)); cbor_encode_int(&mapEncoder, value); - }); + }, encoder); } -void ArduinoCloudProperty::appendAttributeReal(float value, String attributeName) { +void ArduinoCloudProperty::appendAttributeReal(float value, String attributeName, CborEncoder *encoder) { appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) { cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Value)); cbor_encode_float(&mapEncoder, value); - }); + }, encoder); } -void ArduinoCloudProperty::appendAttributeReal(String value, String attributeName) { +void ArduinoCloudProperty::appendAttributeReal(String value, String attributeName, CborEncoder *encoder) { appendAttributeName(attributeName, [value](CborEncoder & mapEncoder) { cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::StringValue)); cbor_encode_text_stringz(&mapEncoder, value.c_str()); - }); + }, encoder); } -void ArduinoCloudProperty::appendAttributeName(String attributeName, std::functionappendValue) { +void ArduinoCloudProperty::appendAttributeName(String attributeName, std::functionappendValue, CborEncoder *encoder) { CborEncoder mapEncoder; - cbor_encoder_create_map(_encoder, &mapEncoder, 2); + cbor_encoder_create_map(encoder, &mapEncoder, 2); cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Name)); String completeName = _name; if (attributeName != "") { @@ -162,7 +160,7 @@ void ArduinoCloudProperty::appendAttributeName(String attributeName, std::functi } cbor_encode_text_stringz(&mapEncoder, completeName.c_str()); appendValue(mapEncoder); - cbor_encoder_close_container(_encoder, &mapEncoder); + cbor_encoder_close_container(encoder, &mapEncoder); } void ArduinoCloudProperty::setAttributesFromCloud(LinkedList *map_data_list) { diff --git a/ArduinoCloudProperty.hpp b/ArduinoCloudProperty.h similarity index 92% rename from ArduinoCloudProperty.hpp rename to ArduinoCloudProperty.h index 11758ca70..79eaf94d6 100644 --- a/ArduinoCloudProperty.hpp +++ b/ArduinoCloudProperty.h @@ -37,7 +37,8 @@ #include "lib/tinycbor/cbor-lib.h" #include "lib/LinkedList/LinkedList.h" -#define appendAttribute(x) appendAttributeReal(x, getAttributeName(#x, '.')) +#define appendAttributesToCloud() appendAttributesToCloudReal(CborEncoder *encoder) +#define appendAttribute(x) appendAttributeReal(x, getAttributeName(#x, '.'), encoder) #define setAttribute(x) setAttributeReal(x, getAttributeName(#x, '.')) /****************************************************************************** @@ -123,13 +124,14 @@ typedef void(*UpdateCallbackFunc)(void); ******************************************************************************/ class ArduinoCloudProperty { + typedef void(*SyncCallbackFunc)(ArduinoCloudProperty &property); public: ArduinoCloudProperty(); void init(String const name, Permission const permission); /* Composable configuration of the ArduinoCloudProperty class */ ArduinoCloudProperty & onUpdate(UpdateCallbackFunc func); - ArduinoCloudProperty & onSync(void (*func)(ArduinoCloudProperty &property)); + ArduinoCloudProperty & onSync(SyncCallbackFunc func); ArduinoCloudProperty & publishOnChange(float const min_delta_property, unsigned long const min_time_between_updates_millis = 0); ArduinoCloudProperty & publishEvery(unsigned long const seconds); @@ -153,11 +155,11 @@ class ArduinoCloudProperty { void updateLocalTimestamp(); void append(CborEncoder * encoder); - void appendAttributeReal(bool value, String attributeName = ""); - void appendAttributeReal(int value, String attributeName = ""); - void appendAttributeReal(float value, String attributeName = ""); - void appendAttributeReal(String value, String attributeName = ""); - void appendAttributeName(String attributeName, std::functionf); + void appendAttributeReal(bool value, String attributeName = "", CborEncoder *encoder = nullptr); + void appendAttributeReal(int value, String attributeName = "", CborEncoder *encoder = nullptr); + void appendAttributeReal(float value, String attributeName = "", CborEncoder *encoder = nullptr); + void appendAttributeReal(String value, String attributeName = "", CborEncoder *encoder = nullptr); + void appendAttributeName(String attributeName, std::functionf, CborEncoder *encoder); void setAttributesFromCloud(LinkedList *map_data_list); void setAttributeReal(bool& value, String attributeName = ""); void setAttributeReal(int& value, String attributeName = ""); @@ -169,7 +171,7 @@ class ArduinoCloudProperty { virtual bool isDifferentFromCloud() = 0; virtual void fromCloudToLocal() = 0; virtual void fromLocalToCloud() = 0; - virtual void appendAttributesToCloud() = 0; + virtual void appendAttributesToCloudReal(CborEncoder *encoder) = 0; virtual void setAttributesFromCloud() = 0; virtual bool isPrimitive() { return false; @@ -194,10 +196,7 @@ class ArduinoCloudProperty { /* Variables used for reconnection sync*/ unsigned long _last_local_change_timestamp; unsigned long _last_cloud_change_timestamp; - /* variable to manage property serialization/deserialization on CBOR */ - CborEncoder *_encoder; - LinkedList - *_map_data_list; + LinkedList * _map_data_list; }; /****************************************************************************** diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 98103c88d..19b901adb 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -22,7 +22,7 @@ INCLUDE ******************************************************************************/ -#include "ArduinoCloudProperty.hpp" +#include "ArduinoCloudProperty.h" #include "lib/LinkedList/LinkedList.h" #include "types/CloudBool.h" #include "types/CloudFloat.h" @@ -76,10 +76,6 @@ class ArduinoCloudThing { bool isPropertyInContainer(String const & name); int appendChangedProperties(CborEncoder * arrayEncoder); - inline void addProperty(ArduinoCloudProperty * property_obj) { - _property_list.add(property_obj); - } - ArduinoCloudProperty * getProperty(String const & name); void updateTimestampOnLocallyChangedProperties(); void updateProperty(String propertyName, unsigned long cloudChangeEventTime); @@ -129,6 +125,10 @@ class ArduinoCloudThing { static bool ifNumericConvertToDouble(CborValue * value_iter, double * numeric_val); static double convertCborHalfFloatToDouble(uint16_t const half_val); void freeMapDataList(LinkedList *map_data_list); + inline void addProperty(ArduinoCloudProperty * property_obj) { + _property_list.add(property_obj); + } + ArduinoCloudProperty * getProperty(String const & name); }; diff --git a/types/CloudBool.h b/types/CloudBool.h index 164595346..4db8d5543 100644 --- a/types/CloudBool.h +++ b/types/CloudBool.h @@ -23,7 +23,7 @@ ******************************************************************************/ #include -#include "../ArduinoCloudProperty.hpp" +#include "../ArduinoCloudProperty.h" /****************************************************************************** CLASS DECLARATION diff --git a/types/CloudColor.h b/types/CloudColor.h index 1611778e9..108ab8bde 100644 --- a/types/CloudColor.h +++ b/types/CloudColor.h @@ -24,7 +24,7 @@ #include #include -#include "../ArduinoCloudProperty.hpp" +#include "../ArduinoCloudProperty.h" /****************************************************************************** CLASS DECLARATION diff --git a/types/CloudFloat.h b/types/CloudFloat.h index e04c04936..56d6a203a 100644 --- a/types/CloudFloat.h +++ b/types/CloudFloat.h @@ -25,7 +25,7 @@ ******************************************************************************/ #include -#include "../ArduinoCloudProperty.hpp" +#include "../ArduinoCloudProperty.h" /****************************************************************************** CLASS DECLARATION diff --git a/types/CloudInt.h b/types/CloudInt.h index b497e35a3..613342ef3 100644 --- a/types/CloudInt.h +++ b/types/CloudInt.h @@ -23,7 +23,7 @@ ******************************************************************************/ #include -#include "../ArduinoCloudProperty.hpp" +#include "../ArduinoCloudProperty.h" /****************************************************************************** CLASS DECLARATION diff --git a/types/CloudLocation.h b/types/CloudLocation.h index f21253335..358df7039 100644 --- a/types/CloudLocation.h +++ b/types/CloudLocation.h @@ -24,7 +24,7 @@ #include #include -#include "../ArduinoCloudProperty.hpp" +#include "../ArduinoCloudProperty.h" /****************************************************************************** CLASS DECLARATION diff --git a/types/CloudString.h b/types/CloudString.h index a1d5bccc1..800a6ddab 100644 --- a/types/CloudString.h +++ b/types/CloudString.h @@ -23,7 +23,7 @@ ******************************************************************************/ #include -#include "../ArduinoCloudProperty.hpp" +#include "../ArduinoCloudProperty.h" /****************************************************************************** CLASS DECLARATION diff --git a/types/CloudWrapperBase.h b/types/CloudWrapperBase.h index 21d537158..3d5910c55 100644 --- a/types/CloudWrapperBase.h +++ b/types/CloudWrapperBase.h @@ -23,7 +23,7 @@ ******************************************************************************/ #include -#include "../ArduinoCloudProperty.hpp" +#include "../ArduinoCloudProperty.h" /****************************************************************************** CLASS DECLARATION From cf355c4fd9471d0da5dbe39c7fee2a13c665a453 Mon Sep 17 00:00:00 2001 From: ilcato Date: Wed, 8 May 2019 12:15:46 +0200 Subject: [PATCH 134/175] Fix CloudColor assignment operator --- types/CloudColor.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/CloudColor.h b/types/CloudColor.h index 108ab8bde..5ddaa69a7 100644 --- a/types/CloudColor.h +++ b/types/CloudColor.h @@ -174,7 +174,7 @@ class CloudColor : public ArduinoCloudProperty { CloudColor& operator=(Color aColor) { _value.hue = aColor.hue; _value.sat = aColor.sat; - _value.sat = aColor.bri; + _value.bri = aColor.bri; updateLocalTimestamp(); return *this; } From e8b82753b9cd944fb5c69433a1e2dcc634b3f762 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Mon, 20 May 2019 08:07:11 +0200 Subject: [PATCH 135/175] Fix compiler warnings Pedantic warnings were disabled in the tinycbor library since this is external code. --- ArduinoCloudProperty.cpp | 28 +++++++++---------- ArduinoCloudProperty.h | 2 +- ArduinoCloudThing.cpp | 6 ++-- ArduinoCloudThing.h | 4 +-- lib/tinycbor/src/cborencoder.c | 5 ++++ .../src/cborencoder_close_container_checked.c | 5 ++++ lib/tinycbor/src/cborerrorstrings.c | 5 ++++ lib/tinycbor/src/cborparser.c | 5 ++++ lib/tinycbor/src/cborparser_dup_string.c | 5 ++++ lib/tinycbor/src/cborpretty.c | 5 ++++ lib/tinycbor/src/cborpretty_stdio.c | 4 +++ lib/tinycbor/src/cbortojson.c | 5 ++++ lib/tinycbor/src/cborvalidation.c | 5 ++++ lib/tinycbor/src/open_memstream.c | 8 ++++-- types/CloudColor.h | 7 +---- types/CloudFloat.h | 2 -- types/CloudInt.h | 2 -- 17 files changed, 71 insertions(+), 32 deletions(-) diff --git a/ArduinoCloudProperty.cpp b/ArduinoCloudProperty.cpp index 22f3599d0..2b8031c0b 100644 --- a/ArduinoCloudProperty.cpp +++ b/ArduinoCloudProperty.cpp @@ -26,7 +26,7 @@ static unsigned long getTimestamp() { #ifdef ARDUINO_ARCH_SAMD return rtc.getEpoch(); #else -#warning "No RTC available on this architecture - ArduinoIoTCloud will not keep track of local change timestamps ." +#pragma message "No RTC available on this architecture - ArduinoIoTCloud will not keep track of local change timestamps ." return 0; #endif } @@ -35,19 +35,19 @@ static unsigned long getTimestamp() { CTOR/DTOR ******************************************************************************/ ArduinoCloudProperty::ArduinoCloudProperty() - : _name(""), - _permission(Permission::Read), - _update_callback_func(nullptr), - _sync_callback_func(nullptr), - _has_been_updated_once(false), - _has_been_modified_in_callback(false), - _min_delta_property(0.0f), - _min_time_between_updates_millis(0), - _last_updated_millis(0), - _update_interval_millis(0), - _last_local_change_timestamp(0), - _last_cloud_change_timestamp(0), - _map_data_list(nullptr) { + : _name(""), + _min_delta_property(0.0f), + _min_time_between_updates_millis(0), + _permission(Permission::Read), + _update_callback_func(nullptr), + _sync_callback_func(nullptr), + _has_been_updated_once(false), + _has_been_modified_in_callback(false), + _last_updated_millis(0), + _update_interval_millis(0), + _last_local_change_timestamp(0), + _last_cloud_change_timestamp(0), + _map_data_list(nullptr) { } /****************************************************************************** diff --git a/ArduinoCloudProperty.h b/ArduinoCloudProperty.h index 79eaf94d6..57f92ab3f 100644 --- a/ArduinoCloudProperty.h +++ b/ArduinoCloudProperty.h @@ -178,9 +178,9 @@ class ArduinoCloudProperty { }; protected: /* Variables used for UpdatePolicy::OnChange */ + String _name; float _min_delta_property; unsigned long _min_time_between_updates_millis; - String _name; private: Permission _permission; diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index a8d5680d8..30df2b707 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -47,8 +47,8 @@ ArduinoCloudThing::ArduinoCloudThing() : _numPrimitivesProperties(0), _isSyncMessage(false), _currentPropertyName(""), - _currentPropertyTime(0), - _currentPropertyBaseTime(0) + _currentPropertyBaseTime(0), + _currentPropertyTime(0) {} /****************************************************************************** @@ -515,5 +515,5 @@ void onForceCloudSync(ArduinoCloudProperty & property) { property.execCallbackOnChange(); } -void onForceDeviceSync(ArduinoCloudProperty & property) { +void onForceDeviceSync(ArduinoCloudProperty & /* property */) { } diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 19b901adb..931725ba4 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -36,8 +36,8 @@ CONSTANTS ******************************************************************************/ -static bool ON = true; -static bool OFF = false; +static bool const ON = true; +static bool const OFF = false; static long const ON_CHANGE = -1; static long const SECONDS = 1; diff --git a/lib/tinycbor/src/cborencoder.c b/lib/tinycbor/src/cborencoder.c index adb92fd88..23a9721ad 100644 --- a/lib/tinycbor/src/cborencoder.c +++ b/lib/tinycbor/src/cborencoder.c @@ -39,6 +39,9 @@ #include #include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + /** * \defgroup CborEncoding Encoding to CBOR * \brief Group of functions used to encode data to CBOR. @@ -642,4 +645,6 @@ CborError cbor_encoder_close_container(CborEncoder *encoder, const CborEncoder * * \sa cbor_encoder_init(), cbor_encoder_get_buffer_size(), CborEncoding */ +#pragma GCC diagnostic pop + /** @} */ diff --git a/lib/tinycbor/src/cborencoder_close_container_checked.c b/lib/tinycbor/src/cborencoder_close_container_checked.c index 5661e4d53..1165d370f 100644 --- a/lib/tinycbor/src/cborencoder_close_container_checked.c +++ b/lib/tinycbor/src/cborencoder_close_container_checked.c @@ -30,6 +30,9 @@ #include "cbor.h" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + /** * \addtogroup CborEncoding * @{ @@ -54,4 +57,6 @@ CborError cbor_encoder_close_container_checked(CborEncoder *encoder, const CborE return cbor_encoder_close_container(encoder, containerEncoder); } +#pragma GCC diagnostic pop + /** @} */ diff --git a/lib/tinycbor/src/cborerrorstrings.c b/lib/tinycbor/src/cborerrorstrings.c index 3fe3a9823..e24be536f 100644 --- a/lib/tinycbor/src/cborerrorstrings.c +++ b/lib/tinycbor/src/cborerrorstrings.c @@ -28,6 +28,9 @@ # define _(msg) msg #endif +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + /** * \enum CborError * \ingroup CborGlobals @@ -180,3 +183,5 @@ const char *cbor_error_string(CborError error) } return cbor_error_string(CborUnknownError); } + +#pragma GCC diagnostic pop diff --git a/lib/tinycbor/src/cborparser.c b/lib/tinycbor/src/cborparser.c index 45de2226a..428159231 100644 --- a/lib/tinycbor/src/cborparser.c +++ b/lib/tinycbor/src/cborparser.c @@ -38,6 +38,9 @@ #include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + /** * \defgroup CborParsing Parsing CBOR streams * \brief Group of functions used to parse CBOR streams. @@ -1427,4 +1430,6 @@ CborError cbor_value_get_half_float(const CborValue *value, void *result) return CborNoError; } +#pragma GCC diagnostic pop + /** @} */ diff --git a/lib/tinycbor/src/cborparser_dup_string.c b/lib/tinycbor/src/cborparser_dup_string.c index 061c5ac77..1360d8b1a 100644 --- a/lib/tinycbor/src/cborparser_dup_string.c +++ b/lib/tinycbor/src/cborparser_dup_string.c @@ -36,6 +36,9 @@ #include "compilersupport_p.h" #include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + /** * \fn CborError cbor_value_dup_text_string(const CborValue *value, char **buffer, size_t *buflen, CborValue *next) * @@ -117,3 +120,5 @@ CborError _cbor_value_dup_string(const CborValue *value, void **buffer, size_t * } return CborNoError; } + +#pragma GCC diagnostic pop diff --git a/lib/tinycbor/src/cborpretty.c b/lib/tinycbor/src/cborpretty.c index b8825deee..2db4b253a 100644 --- a/lib/tinycbor/src/cborpretty.c +++ b/lib/tinycbor/src/cborpretty.c @@ -36,6 +36,9 @@ #include #include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + /** * \defgroup CborPretty Converting CBOR to text * \brief Group of functions used to convert CBOR to text form. @@ -575,4 +578,6 @@ CborError cbor_value_to_pretty_stream(CborStreamFunction streamFunction, void *t return value_to_pretty(streamFunction, token, value, flags, CBOR_PARSER_MAX_RECURSIONS); } +#pragma GCC diagnostic pop + /** @} */ diff --git a/lib/tinycbor/src/cborpretty_stdio.c b/lib/tinycbor/src/cborpretty_stdio.c index 20131850c..0953e5e8d 100644 --- a/lib/tinycbor/src/cborpretty_stdio.c +++ b/lib/tinycbor/src/cborpretty_stdio.c @@ -26,6 +26,9 @@ #include #include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + static CborError cbor_fprintf(void *out, const char *fmt, ...) { int n; @@ -85,3 +88,4 @@ CborError cbor_value_to_pretty_advance_flags(FILE *out, CborValue *value, int fl return cbor_value_to_pretty_stream(cbor_fprintf, out, value, flags); } +#pragma GCC diagnostic pop diff --git a/lib/tinycbor/src/cbortojson.c b/lib/tinycbor/src/cbortojson.c index 5a1a2e5dc..ed6a16666 100644 --- a/lib/tinycbor/src/cbortojson.c +++ b/lib/tinycbor/src/cbortojson.c @@ -40,6 +40,9 @@ #include #include +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + /** * \defgroup CborToJson Converting CBOR to JSON * \brief Group of functions used to convert CBOR to JSON. @@ -696,4 +699,6 @@ CborError cbor_value_to_json_advance(FILE *out, CborValue *value, int flags) return value_to_json(out, value, flags, cbor_value_get_type(value), &status); } +#pragma GCC diagnostic pop + /** @} */ diff --git a/lib/tinycbor/src/cborvalidation.c b/lib/tinycbor/src/cborvalidation.c index 08c35117b..d9445ea27 100644 --- a/lib/tinycbor/src/cborvalidation.c +++ b/lib/tinycbor/src/cborvalidation.c @@ -45,6 +45,9 @@ # define CBOR_PARSER_MAX_RECURSIONS 1024 #endif +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + /** * \addtogroup CborParsing * @{ @@ -661,6 +664,8 @@ CborError cbor_value_validate(const CborValue *it, uint32_t flags) return CborNoError; } +#pragma GCC diagnostic pop + /** * @} */ diff --git a/lib/tinycbor/src/open_memstream.c b/lib/tinycbor/src/open_memstream.c index 0c4f96314..830a67973 100644 --- a/lib/tinycbor/src/open_memstream.c +++ b/lib/tinycbor/src/open_memstream.c @@ -39,6 +39,9 @@ typedef size_t LenType; #include "compilersupport_p.h" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" + struct Buffer { char **ptr; @@ -57,8 +60,8 @@ static RetType write_to_buffer(void *cookie, const char *data, LenType len) return -1; if (newsize > b->alloc) { - // make room - size_t newalloc = newsize + newsize / 2 + 1; // give 50% more room + /* make room */ + size_t newalloc = newsize + newsize / 2 + 1; /* give 50% more room */ ptr = realloc(ptr, newalloc); if (ptr == NULL) return -1; @@ -104,3 +107,4 @@ FILE *open_memstream(char **bufptr, size_t *lenptr) #endif } +#pragma GCC diagnostic pop diff --git a/types/CloudColor.h b/types/CloudColor.h index 5ddaa69a7..cddc69751 100644 --- a/types/CloudColor.h +++ b/types/CloudColor.h @@ -52,19 +52,15 @@ class Color { } bool setColorRGB(uint8_t R, uint8_t G, uint8_t B) { - if (R < 0 || R > 255 || G < 0 || G > 255 || B < 0 || B > 255) { - return false; - } float temp[3]; float max, min, delta; - uint8_t imax, imin; + uint8_t imax; temp[0] = (float)R / 255; temp[1] = (float)G / 255; temp[2] = (float)B / 255; max = temp[0]; imax = 0; min = temp[0]; - imin = 0; for (uint8_t j = 0; j < 3; j++) { @@ -74,7 +70,6 @@ class Color { } if (temp[j] <= min) { min = temp[j]; - imin = j; } } diff --git a/types/CloudFloat.h b/types/CloudFloat.h index 56d6a203a..7608110df 100644 --- a/types/CloudFloat.h +++ b/types/CloudFloat.h @@ -88,12 +88,10 @@ class CloudFloat : public ArduinoCloudProperty { return operator=(_value - 1.0f); } CloudFloat operator++(int) { - float f = _value; operator=(_value + 1.0f); return CloudFloat(_value); } CloudFloat operator--(int) { - float f = _value; operator=(_value - 1.0f); return CloudFloat(_value); } diff --git a/types/CloudInt.h b/types/CloudInt.h index 613342ef3..7093fb05a 100644 --- a/types/CloudInt.h +++ b/types/CloudInt.h @@ -89,12 +89,10 @@ class CloudInt : public ArduinoCloudProperty { return operator=(--_value); } CloudInt operator++(int) { - int temp = _value; operator=(_value + 1); return CloudInt(_value); } CloudInt operator--(int) { - int temp = _value; operator=(_value - 1); return CloudInt(_value); } From 3fb6bc8d6cb69ce6081ea92046c142df23770d94 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Tue, 21 May 2019 15:33:37 +0200 Subject: [PATCH 136/175] Presetting variable next_state with defined value so as to not leave it undefined --- ArduinoCloudThing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 30df2b707..a9450d331 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -123,7 +123,7 @@ void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const lengt _currentPropertyName = ""; MapParserState current_state = MapParserState::EnterMap, - next_state; + next_state = MapParserState::Error; while (current_state != MapParserState::Complete) { From 0e2f78648f7711508259fcfdf52f08a8c9810a62 Mon Sep 17 00:00:00 2001 From: mirkokurt Date: Mon, 27 May 2019 17:24:18 +0200 Subject: [PATCH 137/175] Fixed memory leak during the decode of a cbor message --- ArduinoCloudThing.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index a9450d331..1cdb8b6d7 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -429,7 +429,11 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * if (!cbor_value_at_end(map_iter)) { next_state = MapParserState::EnterMap; } else { + /* Update the property containers depending on the parsed data */ updateProperty(_currentPropertyName, _currentPropertyBaseTime + _currentPropertyTime); + /* Reset last property data */ + freeMapDataList(&_map_data_list); + _map_data_list.clear(); next_state = MapParserState::Complete; } } From ff7b2c97d2907fb407f096f91ad9a092d6deaec5 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Fri, 2 Aug 2019 07:56:54 +0200 Subject: [PATCH 138/175] Correcting semantic usage of overloaded CloudLocation::operator When using the - operator with a location one would typically expect the result being a new location which coordinates are calculated by LocNew.lat = Loc.lat - otherLoc.lat and LocNew.lon = Loc.lon - otherLoc.lon - and not a float value representing the euclidean distance. --- types/CloudLocation.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/types/CloudLocation.h b/types/CloudLocation.h index 358df7039..b673d6f2e 100644 --- a/types/CloudLocation.h +++ b/types/CloudLocation.h @@ -42,8 +42,8 @@ class Location { lon = aLocation.lon; return *this; } - float operator-(Location& aLocation) { - return sqrt(pow(lat - aLocation.lat, 2) + pow(lon - aLocation.lon, 2)); + Location operator-(Location& aLocation) { + return Location(lat - aLocation.lat, lon - aLocation.lon); } bool operator==(Location& aLocation) { return lat == aLocation.lat && lon == aLocation.lon; @@ -51,6 +51,9 @@ class Location { bool operator!=(Location& aLocation) { return !(operator==(aLocation)); } + static float distance(Location& loc1, Location& loc2) { + return sqrt(pow(loc1.lat - loc2.lat, 2) + pow(loc1.lon - loc2.lon, 2)); + } }; class CloudLocation : public ArduinoCloudProperty { @@ -61,7 +64,7 @@ class CloudLocation : public ArduinoCloudProperty { CloudLocation() : _value(0, 0), _cloud_value(0, 0) {} CloudLocation(float lat, float lon) : _value(lat, lon), _cloud_value(lat, lon) {} virtual bool isDifferentFromCloud() { - float distance = _value - _cloud_value; + float const distance = Location::distance(_value, _cloud_value); return _value != _cloud_value && (abs(distance) >= ArduinoCloudProperty::_min_delta_property); } From 9dd4b36ac38e3026c24bdad32c371549868518e1 Mon Sep 17 00:00:00 2001 From: ilcato Date: Thu, 13 Jun 2019 19:22:01 +0200 Subject: [PATCH 139/175] Types for home automation --- types/HomeAutomation/CloudColoredLight.h | 99 +++++++++++++++++++++++ types/HomeAutomation/CloudContactSensor.h | 40 +++++++++ types/HomeAutomation/CloudDimmeredLight.h | 53 ++++++++++++ types/HomeAutomation/CloudLight.h | 40 +++++++++ types/HomeAutomation/CloudMotionSensor.h | 40 +++++++++ types/HomeAutomation/CloudSmartPlug.h | 40 +++++++++ types/HomeAutomation/CloudSwitch.h | 40 +++++++++ 7 files changed, 352 insertions(+) create mode 100644 types/HomeAutomation/CloudColoredLight.h create mode 100644 types/HomeAutomation/CloudContactSensor.h create mode 100644 types/HomeAutomation/CloudDimmeredLight.h create mode 100644 types/HomeAutomation/CloudLight.h create mode 100644 types/HomeAutomation/CloudMotionSensor.h create mode 100644 types/HomeAutomation/CloudSmartPlug.h create mode 100644 types/HomeAutomation/CloudSwitch.h diff --git a/types/HomeAutomation/CloudColoredLight.h b/types/HomeAutomation/CloudColoredLight.h new file mode 100644 index 000000000..fc2ce5fd3 --- /dev/null +++ b/types/HomeAutomation/CloudColoredLight.h @@ -0,0 +1,99 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDCOLOREDLIGHT_H_ +#define CLOUDCOLOREDLIGHT_H_ + +/****************************************************************************** + INCLUDE + ******************************************************************************/ + +#include +#include +#include "../CloudColor.h" + +/****************************************************************************** + CLASS DECLARATION + ******************************************************************************/ + +class ColoredLight : public Color { + public: + bool swi; + ColoredLight(bool swi, float h, float s, float b): swi(swi), Color(h, s, b) { + } + + bool operator==(ColoredLight & aLight) { + return Color::operator==(aLight) && aLight.swi == swi; + } + + bool operator!=(ColoredLight & aLight) { + return !(operator==(aLight)); + } + +}; + +class CloudColoredLight : public CloudColor { + protected: + ColoredLight _value, + _cloud_value; + public: + CloudColoredLight() : _value(false, 0, 0, 0), _cloud_value(false, 0, 0, 0) {} + CloudColoredLight(bool swi, float hue, float saturation, float brightness) : _value(swi, hue, saturation, brightness), _cloud_value(swi, hue, saturation, brightness) {} + + virtual bool isDifferentFromCloud() { + + return _value != _cloud_value; + } + + CloudColoredLight& operator=(ColoredLight aLight) { + _value.swi = aLight.swi; + _value.hue = aLight.hue; + _value.sat = aLight.sat; + _value.bri = aLight.bri; + updateLocalTimestamp(); + return *this; + } + + ColoredLight getCloudValue() { + return _cloud_value; + } + + ColoredLight getValue() { + return _value; + } + + virtual void fromCloudToLocal() { + _value = _cloud_value; + } + virtual void fromLocalToCloud() { + _cloud_value = _value; + } + virtual void appendAttributesToCloud() { + appendAttribute(_value.swi); + appendAttribute(_value.hue); + appendAttribute(_value.sat); + appendAttribute(_value.bri); + } + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value.swi); + setAttribute(_cloud_value.hue); + setAttribute(_cloud_value.sat); + setAttribute(_cloud_value.bri); + } +}; + +#endif /* CLOUDCOLOREDLIGHT_H_ */ \ No newline at end of file diff --git a/types/HomeAutomation/CloudContactSensor.h b/types/HomeAutomation/CloudContactSensor.h new file mode 100644 index 000000000..07f9d967c --- /dev/null +++ b/types/HomeAutomation/CloudContactSensor.h @@ -0,0 +1,40 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDCONTACTSENSOR_H_ +#define CLOUDCONTACTSENSOR_H_ + +/****************************************************************************** + INCLUDE + ******************************************************************************/ + +#include +#include "../CloudBool.h" + +/****************************************************************************** + CLASS DECLARATION + ******************************************************************************/ + + + +class CloudContactSensor : public CloudBool { + private: + public: +}; + + +#endif /* CLOUDCONTACTSENSOR_H_ */ diff --git a/types/HomeAutomation/CloudDimmeredLight.h b/types/HomeAutomation/CloudDimmeredLight.h new file mode 100644 index 000000000..b64ab2366 --- /dev/null +++ b/types/HomeAutomation/CloudDimmeredLight.h @@ -0,0 +1,53 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDDIMMEREDLIGHT_H_ +#define CLOUDDIMMEREDLIGHT_H_ + +/****************************************************************************** + INCLUDE + ******************************************************************************/ + +#include +#include +#include "CloudColoredLight.h" + +/****************************************************************************** + CLASS DECLARATION + ******************************************************************************/ + +class CloudDimmeredLight : public CloudColoredLight { + private: + public: + float getBrightness() { + return _value.bri; + } + bool getSwitch() { + return _value.swi; + } + + virtual void appendAttributesToCloud() { + appendAttribute(_value.swi); + appendAttribute(_value.bri); + } + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value.swi); + setAttribute(_cloud_value.bri); + } +}; + +#endif /* CLOUDDIMMEREDLIGHT_H_ */ \ No newline at end of file diff --git a/types/HomeAutomation/CloudLight.h b/types/HomeAutomation/CloudLight.h new file mode 100644 index 000000000..7995b9f7e --- /dev/null +++ b/types/HomeAutomation/CloudLight.h @@ -0,0 +1,40 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDLIGHT_H_ +#define CLOUDLIGHT_H_ + +/****************************************************************************** + INCLUDE + ******************************************************************************/ + +#include +#include "../CloudBool.h" + +/****************************************************************************** + CLASS DECLARATION + ******************************************************************************/ + + + +class CloudLight : public CloudBool { + private: + public: +}; + + +#endif /* CLOUDLIGHT_H_ */ diff --git a/types/HomeAutomation/CloudMotionSensor.h b/types/HomeAutomation/CloudMotionSensor.h new file mode 100644 index 000000000..4f32cdf41 --- /dev/null +++ b/types/HomeAutomation/CloudMotionSensor.h @@ -0,0 +1,40 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDMOTIONSENSOR_H_ +#define CLOUDMOTIONSENSOR_H_ + +/****************************************************************************** + INCLUDE + ******************************************************************************/ + +#include +#include "../CloudBool.h" + +/****************************************************************************** + CLASS DECLARATION + ******************************************************************************/ + + + +class CloudMotionSensor : public CloudBool { + private: + public: +}; + + +#endif /* CLOUDMOTIONSENSOR_H_ */ diff --git a/types/HomeAutomation/CloudSmartPlug.h b/types/HomeAutomation/CloudSmartPlug.h new file mode 100644 index 000000000..913900cde --- /dev/null +++ b/types/HomeAutomation/CloudSmartPlug.h @@ -0,0 +1,40 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDSMARTPLUG_H_ +#define CLOUDSMARTPLUG_H_ + +/****************************************************************************** + INCLUDE + ******************************************************************************/ + +#include +#include "../CloudBool.h" + +/****************************************************************************** + CLASS DECLARATION + ******************************************************************************/ + + + +class CloudSmartPlug : public CloudBool { + private: + public: +}; + + +#endif /* CLOUDSMARTPLUG_H_ */ diff --git a/types/HomeAutomation/CloudSwitch.h b/types/HomeAutomation/CloudSwitch.h new file mode 100644 index 000000000..9dcd32f8b --- /dev/null +++ b/types/HomeAutomation/CloudSwitch.h @@ -0,0 +1,40 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDSWITCH_H_ +#define CLOUDSWITCH_H_ + +/****************************************************************************** + INCLUDE + ******************************************************************************/ + +#include +#include "../CloudBool.h" + +/****************************************************************************** + CLASS DECLARATION + ******************************************************************************/ + + + +class CloudSwitch : public CloudBool { + private: + public: +}; + + +#endif /* CLOUDSWITCH_H_ */ From c557001a36b5be98629a5b48da74e02f550862a3 Mon Sep 17 00:00:00 2001 From: ilcato Date: Fri, 14 Jun 2019 11:38:36 +0200 Subject: [PATCH 140/175] Automation types rename --- .../CloudColoredLight.h | 0 .../CloudContactSensor.h | 0 .../CloudDimmeredLight.h | 0 .../CloudLight.h | 0 .../CloudMotionSensor.h | 0 .../CloudSmartPlug.h | 0 .../CloudSwitch.h | 0 types/Automation/CloudTemperature.h | 40 +++++++++++++++++++ 8 files changed, 40 insertions(+) rename types/{HomeAutomation => Automation}/CloudColoredLight.h (100%) rename types/{HomeAutomation => Automation}/CloudContactSensor.h (100%) rename types/{HomeAutomation => Automation}/CloudDimmeredLight.h (100%) rename types/{HomeAutomation => Automation}/CloudLight.h (100%) rename types/{HomeAutomation => Automation}/CloudMotionSensor.h (100%) rename types/{HomeAutomation => Automation}/CloudSmartPlug.h (100%) rename types/{HomeAutomation => Automation}/CloudSwitch.h (100%) create mode 100644 types/Automation/CloudTemperature.h diff --git a/types/HomeAutomation/CloudColoredLight.h b/types/Automation/CloudColoredLight.h similarity index 100% rename from types/HomeAutomation/CloudColoredLight.h rename to types/Automation/CloudColoredLight.h diff --git a/types/HomeAutomation/CloudContactSensor.h b/types/Automation/CloudContactSensor.h similarity index 100% rename from types/HomeAutomation/CloudContactSensor.h rename to types/Automation/CloudContactSensor.h diff --git a/types/HomeAutomation/CloudDimmeredLight.h b/types/Automation/CloudDimmeredLight.h similarity index 100% rename from types/HomeAutomation/CloudDimmeredLight.h rename to types/Automation/CloudDimmeredLight.h diff --git a/types/HomeAutomation/CloudLight.h b/types/Automation/CloudLight.h similarity index 100% rename from types/HomeAutomation/CloudLight.h rename to types/Automation/CloudLight.h diff --git a/types/HomeAutomation/CloudMotionSensor.h b/types/Automation/CloudMotionSensor.h similarity index 100% rename from types/HomeAutomation/CloudMotionSensor.h rename to types/Automation/CloudMotionSensor.h diff --git a/types/HomeAutomation/CloudSmartPlug.h b/types/Automation/CloudSmartPlug.h similarity index 100% rename from types/HomeAutomation/CloudSmartPlug.h rename to types/Automation/CloudSmartPlug.h diff --git a/types/HomeAutomation/CloudSwitch.h b/types/Automation/CloudSwitch.h similarity index 100% rename from types/HomeAutomation/CloudSwitch.h rename to types/Automation/CloudSwitch.h diff --git a/types/Automation/CloudTemperature.h b/types/Automation/CloudTemperature.h new file mode 100644 index 000000000..dbc9a2e78 --- /dev/null +++ b/types/Automation/CloudTemperature.h @@ -0,0 +1,40 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDTEMPERATURE_H_ +#define CLOUDTEMPERATURE_H_ + +/****************************************************************************** + INCLUDE + ******************************************************************************/ + +#include +#include "../CloudFloat.h" + +/****************************************************************************** + CLASS DECLARATION + ******************************************************************************/ + + + +class CloudTemperature : public CloudFloat { + private: + public: +}; + + +#endif /* CLOUDTEMPERATURE_H_ */ From ebeb82a4e738b0548dad6f2f61d9c60940830366 Mon Sep 17 00:00:00 2001 From: ilcato Date: Tue, 18 Jun 2019 10:50:27 +0200 Subject: [PATCH 141/175] Added home automation types --- ArduinoCloudThing.h | 10 ++++++++++ types/Automation/CloudContactSensor.h | 3 +++ types/Automation/CloudDimmeredLight.h | 1 - types/Automation/CloudLight.h | 3 +++ types/Automation/CloudMotionSensor.h | 3 +++ types/Automation/CloudSmartPlug.h | 3 +++ types/Automation/CloudSwitch.h | 3 +++ types/Automation/CloudTemperature.h | 3 +++ types/CloudBool.h | 2 +- types/CloudFloat.h | 2 +- 10 files changed, 30 insertions(+), 3 deletions(-) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 931725ba4..183703896 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -32,6 +32,16 @@ #include "types/CloudColor.h" #include "types/CloudWrapperBase.h" +#include "types/automation/CloudColoredLight.h" +#include "types/automation/CloudContactSensor.h" +#include "types/automation/CloudDimmeredLight.h" +#include "types/automation/CloudLight.h" +#include "types/automation/CloudMotionSensor.h" +#include "types/automation/CloudSmartPlug.h" +#include "types/automation/CloudSwitch.h" +#include "types/automation/CloudTemperature.h" + + /****************************************************************************** CONSTANTS ******************************************************************************/ diff --git a/types/Automation/CloudContactSensor.h b/types/Automation/CloudContactSensor.h index 07f9d967c..7de7fb19d 100644 --- a/types/Automation/CloudContactSensor.h +++ b/types/Automation/CloudContactSensor.h @@ -34,6 +34,9 @@ class CloudContactSensor : public CloudBool { private: public: + CloudContactSensor& operator=(bool v) { + CloudBool::operator=(v); + } }; diff --git a/types/Automation/CloudDimmeredLight.h b/types/Automation/CloudDimmeredLight.h index b64ab2366..ad714b605 100644 --- a/types/Automation/CloudDimmeredLight.h +++ b/types/Automation/CloudDimmeredLight.h @@ -39,7 +39,6 @@ class CloudDimmeredLight : public CloudColoredLight { bool getSwitch() { return _value.swi; } - virtual void appendAttributesToCloud() { appendAttribute(_value.swi); appendAttribute(_value.bri); diff --git a/types/Automation/CloudLight.h b/types/Automation/CloudLight.h index 7995b9f7e..64f5ed0b0 100644 --- a/types/Automation/CloudLight.h +++ b/types/Automation/CloudLight.h @@ -34,6 +34,9 @@ class CloudLight : public CloudBool { private: public: + CloudLight& operator=(bool v) { + CloudBool::operator=(v); + } }; diff --git a/types/Automation/CloudMotionSensor.h b/types/Automation/CloudMotionSensor.h index 4f32cdf41..c1ce88310 100644 --- a/types/Automation/CloudMotionSensor.h +++ b/types/Automation/CloudMotionSensor.h @@ -34,6 +34,9 @@ class CloudMotionSensor : public CloudBool { private: public: + CloudMotionSensor& operator=(bool v) { + CloudBool::operator=(v); + } }; diff --git a/types/Automation/CloudSmartPlug.h b/types/Automation/CloudSmartPlug.h index 913900cde..2a07edd26 100644 --- a/types/Automation/CloudSmartPlug.h +++ b/types/Automation/CloudSmartPlug.h @@ -34,6 +34,9 @@ class CloudSmartPlug : public CloudBool { private: public: + CloudSmartPlug& operator=(bool v) { + CloudBool::operator=(v); + } }; diff --git a/types/Automation/CloudSwitch.h b/types/Automation/CloudSwitch.h index 9dcd32f8b..2166be4aa 100644 --- a/types/Automation/CloudSwitch.h +++ b/types/Automation/CloudSwitch.h @@ -34,6 +34,9 @@ class CloudSwitch : public CloudBool { private: public: + CloudSwitch& operator=(bool v) { + CloudBool::operator=(v); + } }; diff --git a/types/Automation/CloudTemperature.h b/types/Automation/CloudTemperature.h index dbc9a2e78..8653e4529 100644 --- a/types/Automation/CloudTemperature.h +++ b/types/Automation/CloudTemperature.h @@ -34,6 +34,9 @@ class CloudTemperature : public CloudFloat { private: public: + CloudTemperature& operator=(float v) { + CloudFloat::operator=(v); + } }; diff --git a/types/CloudBool.h b/types/CloudBool.h index 4db8d5543..9a0295f25 100644 --- a/types/CloudBool.h +++ b/types/CloudBool.h @@ -32,7 +32,7 @@ class CloudBool : public ArduinoCloudProperty { - private: + protected: bool _value, _cloud_value; public: diff --git a/types/CloudFloat.h b/types/CloudFloat.h index 7608110df..6c9a2014d 100644 --- a/types/CloudFloat.h +++ b/types/CloudFloat.h @@ -34,7 +34,7 @@ class CloudFloat : public ArduinoCloudProperty { - private: + protected: float _value, _cloud_value; public: From d2153d30235d8a4a27f66f6f50b640f2ba53e0c0 Mon Sep 17 00:00:00 2001 From: ilcato Date: Tue, 18 Jun 2019 11:18:24 +0200 Subject: [PATCH 142/175] Fixed style formatting --- types/{Automation => automation}/CloudColoredLight.h | 4 ++-- types/{Automation => automation}/CloudContactSensor.h | 1 + types/{Automation => automation}/CloudDimmeredLight.h | 0 types/{Automation => automation}/CloudLight.h | 1 + types/{Automation => automation}/CloudMotionSensor.h | 1 + types/{Automation => automation}/CloudSmartPlug.h | 1 + types/{Automation => automation}/CloudSwitch.h | 1 + types/{Automation => automation}/CloudTemperature.h | 1 + 8 files changed, 8 insertions(+), 2 deletions(-) rename types/{Automation => automation}/CloudColoredLight.h (96%) rename types/{Automation => automation}/CloudContactSensor.h (98%) rename types/{Automation => automation}/CloudDimmeredLight.h (100%) rename types/{Automation => automation}/CloudLight.h (98%) rename types/{Automation => automation}/CloudMotionSensor.h (98%) rename types/{Automation => automation}/CloudSmartPlug.h (98%) rename types/{Automation => automation}/CloudSwitch.h (98%) rename types/{Automation => automation}/CloudTemperature.h (98%) diff --git a/types/Automation/CloudColoredLight.h b/types/automation/CloudColoredLight.h similarity index 96% rename from types/Automation/CloudColoredLight.h rename to types/automation/CloudColoredLight.h index fc2ce5fd3..8657dd68c 100644 --- a/types/Automation/CloudColoredLight.h +++ b/types/automation/CloudColoredLight.h @@ -33,7 +33,7 @@ class ColoredLight : public Color { public: bool swi; - ColoredLight(bool swi, float h, float s, float b): swi(swi), Color(h, s, b) { + ColoredLight(bool swi, float h, float s, float b): Color(h, s, b), swi(swi) { } bool operator==(ColoredLight & aLight) { @@ -49,7 +49,7 @@ class ColoredLight : public Color { class CloudColoredLight : public CloudColor { protected: ColoredLight _value, - _cloud_value; + _cloud_value; public: CloudColoredLight() : _value(false, 0, 0, 0), _cloud_value(false, 0, 0, 0) {} CloudColoredLight(bool swi, float hue, float saturation, float brightness) : _value(swi, hue, saturation, brightness), _cloud_value(swi, hue, saturation, brightness) {} diff --git a/types/Automation/CloudContactSensor.h b/types/automation/CloudContactSensor.h similarity index 98% rename from types/Automation/CloudContactSensor.h rename to types/automation/CloudContactSensor.h index 7de7fb19d..50e381aae 100644 --- a/types/Automation/CloudContactSensor.h +++ b/types/automation/CloudContactSensor.h @@ -36,6 +36,7 @@ class CloudContactSensor : public CloudBool { public: CloudContactSensor& operator=(bool v) { CloudBool::operator=(v); + return *this; } }; diff --git a/types/Automation/CloudDimmeredLight.h b/types/automation/CloudDimmeredLight.h similarity index 100% rename from types/Automation/CloudDimmeredLight.h rename to types/automation/CloudDimmeredLight.h diff --git a/types/Automation/CloudLight.h b/types/automation/CloudLight.h similarity index 98% rename from types/Automation/CloudLight.h rename to types/automation/CloudLight.h index 64f5ed0b0..b28ba9f8a 100644 --- a/types/Automation/CloudLight.h +++ b/types/automation/CloudLight.h @@ -36,6 +36,7 @@ class CloudLight : public CloudBool { public: CloudLight& operator=(bool v) { CloudBool::operator=(v); + return *this; } }; diff --git a/types/Automation/CloudMotionSensor.h b/types/automation/CloudMotionSensor.h similarity index 98% rename from types/Automation/CloudMotionSensor.h rename to types/automation/CloudMotionSensor.h index c1ce88310..5a9c065e4 100644 --- a/types/Automation/CloudMotionSensor.h +++ b/types/automation/CloudMotionSensor.h @@ -36,6 +36,7 @@ class CloudMotionSensor : public CloudBool { public: CloudMotionSensor& operator=(bool v) { CloudBool::operator=(v); + return *this; } }; diff --git a/types/Automation/CloudSmartPlug.h b/types/automation/CloudSmartPlug.h similarity index 98% rename from types/Automation/CloudSmartPlug.h rename to types/automation/CloudSmartPlug.h index 2a07edd26..628ce719e 100644 --- a/types/Automation/CloudSmartPlug.h +++ b/types/automation/CloudSmartPlug.h @@ -36,6 +36,7 @@ class CloudSmartPlug : public CloudBool { public: CloudSmartPlug& operator=(bool v) { CloudBool::operator=(v); + return *this; } }; diff --git a/types/Automation/CloudSwitch.h b/types/automation/CloudSwitch.h similarity index 98% rename from types/Automation/CloudSwitch.h rename to types/automation/CloudSwitch.h index 2166be4aa..6f807d3c8 100644 --- a/types/Automation/CloudSwitch.h +++ b/types/automation/CloudSwitch.h @@ -36,6 +36,7 @@ class CloudSwitch : public CloudBool { public: CloudSwitch& operator=(bool v) { CloudBool::operator=(v); + return *this; } }; diff --git a/types/Automation/CloudTemperature.h b/types/automation/CloudTemperature.h similarity index 98% rename from types/Automation/CloudTemperature.h rename to types/automation/CloudTemperature.h index 8653e4529..a82559813 100644 --- a/types/Automation/CloudTemperature.h +++ b/types/automation/CloudTemperature.h @@ -36,6 +36,7 @@ class CloudTemperature : public CloudFloat { public: CloudTemperature& operator=(float v) { CloudFloat::operator=(v); + return *this; } }; From 5067fa9789833cd3ae7a984d0b5bfb57678fd7e4 Mon Sep 17 00:00:00 2001 From: ilcato Date: Wed, 7 Aug 2019 11:06:02 +0200 Subject: [PATCH 143/175] Manage the SWI attribute on behalf of the widget --- types/automation/CloudColoredLight.h | 8 +++++++- types/automation/CloudDimmeredLight.h | 5 +---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/types/automation/CloudColoredLight.h b/types/automation/CloudColoredLight.h index 8657dd68c..cb3fb6621 100644 --- a/types/automation/CloudColoredLight.h +++ b/types/automation/CloudColoredLight.h @@ -86,7 +86,13 @@ class CloudColoredLight : public CloudColor { appendAttribute(_value.swi); appendAttribute(_value.hue); appendAttribute(_value.sat); - appendAttribute(_value.bri); + // To allow visualization through color widget + if (_value.swi) { + appendAttribute(_value.bri); + } else { + float bri = 0; + appendAttributeReal(bri, getAttributeName(".bri", '.'), encoder); + } } virtual void setAttributesFromCloud() { setAttribute(_cloud_value.swi); diff --git a/types/automation/CloudDimmeredLight.h b/types/automation/CloudDimmeredLight.h index ad714b605..4ad9812ec 100644 --- a/types/automation/CloudDimmeredLight.h +++ b/types/automation/CloudDimmeredLight.h @@ -39,10 +39,7 @@ class CloudDimmeredLight : public CloudColoredLight { bool getSwitch() { return _value.swi; } - virtual void appendAttributesToCloud() { - appendAttribute(_value.swi); - appendAttribute(_value.bri); - } + virtual void setAttributesFromCloud() { setAttribute(_cloud_value.swi); setAttribute(_cloud_value.bri); From 3fe305e688856bc10790802ace29dec77734e2d0 Mon Sep 17 00:00:00 2001 From: ilcato Date: Wed, 7 Aug 2019 15:02:09 +0200 Subject: [PATCH 144/175] Fixed DimmeredLight --- types/automation/CloudColoredLight.h | 2 +- types/automation/CloudDimmeredLight.h | 73 ++++++++++++++++++++++++--- 2 files changed, 68 insertions(+), 7 deletions(-) diff --git a/types/automation/CloudColoredLight.h b/types/automation/CloudColoredLight.h index cb3fb6621..90d620cfa 100644 --- a/types/automation/CloudColoredLight.h +++ b/types/automation/CloudColoredLight.h @@ -47,7 +47,7 @@ class ColoredLight : public Color { }; class CloudColoredLight : public CloudColor { - protected: + private: ColoredLight _value, _cloud_value; public: diff --git a/types/automation/CloudDimmeredLight.h b/types/automation/CloudDimmeredLight.h index 4ad9812ec..80a1f92ba 100644 --- a/types/automation/CloudDimmeredLight.h +++ b/types/automation/CloudDimmeredLight.h @@ -24,20 +24,81 @@ #include #include -#include "CloudColoredLight.h" +#include "../../ArduinoCloudProperty.h" /****************************************************************************** CLASS DECLARATION ******************************************************************************/ +class DimmeredLight { + public: + bool swi; + float bri; + DimmeredLight(bool swi, float bri): swi(swi), bri(bri) { + } + + bool operator==(DimmeredLight & aLight) { + return aLight.swi == swi && aLight.bri == bri; + } + + bool operator!=(DimmeredLight & aLight) { + return !(operator==(aLight)); + } + +}; -class CloudDimmeredLight : public CloudColoredLight { +class CloudDimmeredLight : public ArduinoCloudProperty { private: + DimmeredLight _value, + _cloud_value; + public: - float getBrightness() { - return _value.bri; + CloudDimmeredLight() : _value(false, 0), _cloud_value(false, 0) {} + CloudDimmeredLight(bool swi, float brightness) : _value(swi, brightness), _cloud_value(swi, brightness) {} + + virtual bool isDifferentFromCloud() { + + return _value != _cloud_value; + } + + CloudDimmeredLight& operator=(DimmeredLight aLight) { + _value.swi = aLight.swi; + _value.bri = aLight.bri; + updateLocalTimestamp(); + return *this; + } + + DimmeredLight getCloudValue() { + return _cloud_value; } - bool getSwitch() { - return _value.swi; + + DimmeredLight getValue() { + return _value; + } + + virtual void fromCloudToLocal() { + _value = _cloud_value; + } + virtual void fromLocalToCloud() { + _cloud_value = _value; + } + + virtual void appendAttributesToCloud() { + appendAttribute(_value.swi); + // To allow visualization through color widget + // Start + float hue = 0; + float sat = 0; + appendAttributeReal(hue, getAttributeName(".hue", '.'), encoder); + appendAttributeReal(sat, getAttributeName(".sat", '.'), encoder); + if (_value.swi) { + appendAttribute(_value.bri); + } else { + float bri = 0; + appendAttributeReal(bri, getAttributeName(".bri", '.'), encoder); + } + // should be only: + // appendAttribute(_value.bri); + // end } virtual void setAttributesFromCloud() { From 061268e36fa14bb99acce0b5d82aa15ca58949bc Mon Sep 17 00:00:00 2001 From: ilcato Date: Wed, 7 Aug 2019 15:07:00 +0200 Subject: [PATCH 145/175] Fixed formatting error --- types/automation/CloudDimmeredLight.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/automation/CloudDimmeredLight.h b/types/automation/CloudDimmeredLight.h index 80a1f92ba..b975173ed 100644 --- a/types/automation/CloudDimmeredLight.h +++ b/types/automation/CloudDimmeredLight.h @@ -49,7 +49,7 @@ class DimmeredLight { class CloudDimmeredLight : public ArduinoCloudProperty { private: DimmeredLight _value, - _cloud_value; + _cloud_value; public: CloudDimmeredLight() : _value(false, 0), _cloud_value(false, 0) {} @@ -74,7 +74,7 @@ class CloudDimmeredLight : public ArduinoCloudProperty { DimmeredLight getValue() { return _value; } - + virtual void fromCloudToLocal() { _value = _cloud_value; } From bdb7bfaa45785bc5c439da7cdcf77b7aeefc5341 Mon Sep 17 00:00:00 2001 From: mirkokurt Date: Mon, 19 Aug 2019 12:26:38 +0200 Subject: [PATCH 146/175] Added tests for home automation types --- types/automation/CloudContactSensor.h | 3 +++ types/automation/CloudLight.h | 3 +++ types/automation/CloudMotionSensor.h | 3 +++ types/automation/CloudSmartPlug.h | 3 +++ types/automation/CloudSwitch.h | 3 +++ 5 files changed, 15 insertions(+) diff --git a/types/automation/CloudContactSensor.h b/types/automation/CloudContactSensor.h index 50e381aae..351eaed73 100644 --- a/types/automation/CloudContactSensor.h +++ b/types/automation/CloudContactSensor.h @@ -34,6 +34,9 @@ class CloudContactSensor : public CloudBool { private: public: + operator bool() const { + return _value; + } CloudContactSensor& operator=(bool v) { CloudBool::operator=(v); return *this; diff --git a/types/automation/CloudLight.h b/types/automation/CloudLight.h index b28ba9f8a..358527b37 100644 --- a/types/automation/CloudLight.h +++ b/types/automation/CloudLight.h @@ -34,6 +34,9 @@ class CloudLight : public CloudBool { private: public: + operator bool() const { + return _value; + } CloudLight& operator=(bool v) { CloudBool::operator=(v); return *this; diff --git a/types/automation/CloudMotionSensor.h b/types/automation/CloudMotionSensor.h index 5a9c065e4..3831d3d27 100644 --- a/types/automation/CloudMotionSensor.h +++ b/types/automation/CloudMotionSensor.h @@ -34,6 +34,9 @@ class CloudMotionSensor : public CloudBool { private: public: + operator bool() const { + return _value; + } CloudMotionSensor& operator=(bool v) { CloudBool::operator=(v); return *this; diff --git a/types/automation/CloudSmartPlug.h b/types/automation/CloudSmartPlug.h index 628ce719e..99c7886fb 100644 --- a/types/automation/CloudSmartPlug.h +++ b/types/automation/CloudSmartPlug.h @@ -34,6 +34,9 @@ class CloudSmartPlug : public CloudBool { private: public: + operator bool() const { + return _value; + } CloudSmartPlug& operator=(bool v) { CloudBool::operator=(v); return *this; diff --git a/types/automation/CloudSwitch.h b/types/automation/CloudSwitch.h index 6f807d3c8..d22345d88 100644 --- a/types/automation/CloudSwitch.h +++ b/types/automation/CloudSwitch.h @@ -34,6 +34,9 @@ class CloudSwitch : public CloudBool { private: public: + operator bool() const { + return _value; + } CloudSwitch& operator=(bool v) { CloudBool::operator=(v); return *this; From e1a17e4d01df5557beb8373a67184b8a7211e504 Mon Sep 17 00:00:00 2001 From: ilcato Date: Tue, 27 Aug 2019 18:16:11 +0200 Subject: [PATCH 147/175] Fix automation types --- types/automation/CloudColoredLight.h | 8 +------- types/automation/CloudDimmeredLight.h | 7 +------ 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/types/automation/CloudColoredLight.h b/types/automation/CloudColoredLight.h index 90d620cfa..31edbaf27 100644 --- a/types/automation/CloudColoredLight.h +++ b/types/automation/CloudColoredLight.h @@ -86,13 +86,7 @@ class CloudColoredLight : public CloudColor { appendAttribute(_value.swi); appendAttribute(_value.hue); appendAttribute(_value.sat); - // To allow visualization through color widget - if (_value.swi) { - appendAttribute(_value.bri); - } else { - float bri = 0; - appendAttributeReal(bri, getAttributeName(".bri", '.'), encoder); - } + appendAttribute(_value.bri); } virtual void setAttributesFromCloud() { setAttribute(_cloud_value.swi); diff --git a/types/automation/CloudDimmeredLight.h b/types/automation/CloudDimmeredLight.h index b975173ed..42ead372b 100644 --- a/types/automation/CloudDimmeredLight.h +++ b/types/automation/CloudDimmeredLight.h @@ -90,12 +90,7 @@ class CloudDimmeredLight : public ArduinoCloudProperty { float sat = 0; appendAttributeReal(hue, getAttributeName(".hue", '.'), encoder); appendAttributeReal(sat, getAttributeName(".sat", '.'), encoder); - if (_value.swi) { - appendAttribute(_value.bri); - } else { - float bri = 0; - appendAttributeReal(bri, getAttributeName(".bri", '.'), encoder); - } + appendAttribute(_value.bri); // should be only: // appendAttribute(_value.bri); // end From 8d93e576f6a30238204bceed926ba29fa6d8b31c Mon Sep 17 00:00:00 2001 From: Fabrizio Mirabito Date: Thu, 24 Oct 2019 17:51:22 +0200 Subject: [PATCH 148/175] Add getters and setters to automation types --- types/automation/CloudColoredLight.h | 32 +++++++++++++++++++++++++++ types/automation/CloudDimmeredLight.h | 16 ++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/types/automation/CloudColoredLight.h b/types/automation/CloudColoredLight.h index 31edbaf27..00f9ed809 100644 --- a/types/automation/CloudColoredLight.h +++ b/types/automation/CloudColoredLight.h @@ -76,6 +76,38 @@ class CloudColoredLight : public CloudColor { return _value; } + bool getSwitch() { + return _value.swi; + } + + void setSwitch(bool const swi) { + _value.swi = swi; + } + + float getHue() { + return _value.hue; + } + + void setHue(float const hue) { + _value.hue = hue; + } + + float getSaturation() { + return _value.sat; + } + + void setSaturation(float const sat) { + _value.sat = sat; + } + + float getBrightness() { + return _value.bri; + } + + void setBrightness(float const bri) { + _value.bri = bri; + } + virtual void fromCloudToLocal() { _value = _cloud_value; } diff --git a/types/automation/CloudDimmeredLight.h b/types/automation/CloudDimmeredLight.h index 42ead372b..1ea54298e 100644 --- a/types/automation/CloudDimmeredLight.h +++ b/types/automation/CloudDimmeredLight.h @@ -75,6 +75,22 @@ class CloudDimmeredLight : public ArduinoCloudProperty { return _value; } + float getBrightness() { + return _value.bri; + } + + void setBrightness(float const bri) { + _value.bri = bri; + } + + bool getSwitch() { + return _value.swi; + } + + void setSwitch(bool const swi) { + _value.swi = swi; + } + virtual void fromCloudToLocal() { _value = _cloud_value; } From 4cc870c8163848a4223859ee1fd39ab879ae9602 Mon Sep 17 00:00:00 2001 From: Stefania Date: Mon, 28 Oct 2019 10:29:29 +0100 Subject: [PATCH 149/175] removed dependency lib examples --- .../examples/ClassList/ClassList.pde | 81 ------------------- .../SimpleIntegerList/SimpleIntegerList.pde | 58 ------------- 2 files changed, 139 deletions(-) delete mode 100644 lib/LinkedList/examples/ClassList/ClassList.pde delete mode 100644 lib/LinkedList/examples/SimpleIntegerList/SimpleIntegerList.pde diff --git a/lib/LinkedList/examples/ClassList/ClassList.pde b/lib/LinkedList/examples/ClassList/ClassList.pde deleted file mode 100644 index 9a8ea9d99..000000000 --- a/lib/LinkedList/examples/ClassList/ClassList.pde +++ /dev/null @@ -1,81 +0,0 @@ -/* - LinkedList Example - Link: http://github.com/ivanseidel/LinkedList - - Example Created by - Tom Stewart, github.com/tastewar - - Edited by: - Ivan Seidel, github.com/ivanseidel -*/ - -#include - -// Let's define a new class -class Animal { - public: - char *name; - bool isMammal; -}; - -char catname[]="kitty"; -char dogname[]="doggie"; -char emuname[]="emu"; - -LinkedList myAnimalList = LinkedList(); - -void setup() -{ - - Serial.begin(9600); - Serial.println("Hello!" ); - - // Create a Cat - Animal *cat = new Animal(); - cat->name = catname; - cat->isMammal = true; - - // Create a dog - Animal *dog = new Animal(); - dog->name = dogname; - dog->isMammal = true; - - // Create a emu - Animal *emu = new Animal(); - emu->name = emuname; - emu->isMammal = false; // just an example; no offense to pig lovers - - // Add animals to list - myAnimalList.add(cat); - myAnimalList.add(emu); - myAnimalList.add(dog); -} - -void loop() { - - Serial.print("There are "); - Serial.print(myAnimalList.size()); - Serial.print(" animals in the list. The mammals are: "); - - int current = 0; - Animal *animal; - for(int i = 0; i < myAnimalList.size(); i++){ - - // Get animal from list - animal = myAnimalList.get(i); - - // If its a mammal, then print it's name - if(animal->isMammal){ - - // Avoid printing spacer on the first element - if(current++) - Serial.print(", "); - - // Print animal name - Serial.print(animal->name); - } - } - Serial.println("."); - - while (true); // nothing else to do, loop forever -} \ No newline at end of file diff --git a/lib/LinkedList/examples/SimpleIntegerList/SimpleIntegerList.pde b/lib/LinkedList/examples/SimpleIntegerList/SimpleIntegerList.pde deleted file mode 100644 index 1bcbe9c37..000000000 --- a/lib/LinkedList/examples/SimpleIntegerList/SimpleIntegerList.pde +++ /dev/null @@ -1,58 +0,0 @@ -/* - LinkedList Example - Link: http://github.com/ivanseidel/LinkedList - - Example Created by - Tom Stewart, github.com/tastewar - - Edited by: - Ivan Seidel, github.com/ivanseidel -*/ -#include - -LinkedList myList = LinkedList(); - -void setup() -{ - - Serial.begin(9600); - Serial.println("Hello!"); - - // Add some stuff to the list - int k = -240, - l = 123, - m = -2, - n = 222; - myList.add(n); - myList.add(0); - myList.add(l); - myList.add(17); - myList.add(k); - myList.add(m); -} - -void loop() { - - int listSize = myList.size(); - - Serial.print("There are "); - Serial.print(listSize); - Serial.print(" integers in the list. The negative ones are: "); - - // Print Negative numbers - for (int h = 0; h < listSize; h++) { - - // Get value from list - int val = myList.get(h); - - // If the value is negative, print it - if (val < 0) { - Serial.print(" "); - Serial.print(val); - } - } - - while (true); // nothing else to do, loop forever -} - - From aa06c8b05e0daf8ba26aa24299a6ccb01532a180 Mon Sep 17 00:00:00 2001 From: Fabrizio Mirabito Date: Mon, 28 Oct 2019 12:04:23 +0100 Subject: [PATCH 150/175] Fix dimmed typo --- ArduinoCloudThing.h | 2 +- ...loudDimmeredLight.h => CloudDimmedLight.h} | 30 +++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) rename types/automation/{CloudDimmeredLight.h => CloudDimmedLight.h} (78%) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 183703896..8af0130b5 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -34,7 +34,7 @@ #include "types/automation/CloudColoredLight.h" #include "types/automation/CloudContactSensor.h" -#include "types/automation/CloudDimmeredLight.h" +#include "types/automation/CloudDimmedLight.h" #include "types/automation/CloudLight.h" #include "types/automation/CloudMotionSensor.h" #include "types/automation/CloudSmartPlug.h" diff --git a/types/automation/CloudDimmeredLight.h b/types/automation/CloudDimmedLight.h similarity index 78% rename from types/automation/CloudDimmeredLight.h rename to types/automation/CloudDimmedLight.h index 1ea54298e..9dbfe1204 100644 --- a/types/automation/CloudDimmeredLight.h +++ b/types/automation/CloudDimmedLight.h @@ -15,8 +15,8 @@ // a commercial license, send an email to license@arduino.cc. // -#ifndef CLOUDDIMMEREDLIGHT_H_ -#define CLOUDDIMMEREDLIGHT_H_ +#ifndef CLOUDDIMMEDLIGHT_H_ +#define CLOUDDIMMEDLIGHT_H_ /****************************************************************************** INCLUDE @@ -29,49 +29,49 @@ /****************************************************************************** CLASS DECLARATION ******************************************************************************/ -class DimmeredLight { +class DimmedLight { public: bool swi; float bri; - DimmeredLight(bool swi, float bri): swi(swi), bri(bri) { + DimmedLight(bool swi, float bri): swi(swi), bri(bri) { } - bool operator==(DimmeredLight & aLight) { + bool operator==(DimmedLight & aLight) { return aLight.swi == swi && aLight.bri == bri; } - bool operator!=(DimmeredLight & aLight) { + bool operator!=(DimmedLight & aLight) { return !(operator==(aLight)); } }; -class CloudDimmeredLight : public ArduinoCloudProperty { +class CloudDimmedLight : public ArduinoCloudProperty { private: - DimmeredLight _value, - _cloud_value; + DimmedLight _value, + _cloud_value; public: - CloudDimmeredLight() : _value(false, 0), _cloud_value(false, 0) {} - CloudDimmeredLight(bool swi, float brightness) : _value(swi, brightness), _cloud_value(swi, brightness) {} + CloudDimmedLight() : _value(false, 0), _cloud_value(false, 0) {} + CloudDimmedLight(bool swi, float brightness) : _value(swi, brightness), _cloud_value(swi, brightness) {} virtual bool isDifferentFromCloud() { return _value != _cloud_value; } - CloudDimmeredLight& operator=(DimmeredLight aLight) { + CloudDimmedLight& operator=(DimmedLight aLight) { _value.swi = aLight.swi; _value.bri = aLight.bri; updateLocalTimestamp(); return *this; } - DimmeredLight getCloudValue() { + DimmedLight getCloudValue() { return _cloud_value; } - DimmeredLight getValue() { + DimmedLight getValue() { return _value; } @@ -118,4 +118,4 @@ class CloudDimmeredLight : public ArduinoCloudProperty { } }; -#endif /* CLOUDDIMMEREDLIGHT_H_ */ \ No newline at end of file +#endif /* CLOUDDIMMEDLIGHT_H_ */ \ No newline at end of file From 10acd4f819e82dd580279bf20e9cd692a97dca1e Mon Sep 17 00:00:00 2001 From: ilcato Date: Thu, 7 Nov 2019 22:28:29 +0100 Subject: [PATCH 151/175] First draft TV type support --- types/automation/CloudTelevision.h | 226 +++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 types/automation/CloudTelevision.h diff --git a/types/automation/CloudTelevision.h b/types/automation/CloudTelevision.h new file mode 100644 index 000000000..1267cf44a --- /dev/null +++ b/types/automation/CloudTelevision.h @@ -0,0 +1,226 @@ +// +// This file is part of ArduinoCloudThing +// +// Copyright 2019 ARDUINO SA (http://www.arduino.cc/) +// +// This software is released under the GNU General Public License version 3, +// which covers the main part of ArduinoCloudThing. +// The terms of this license can be found at: +// https://www.gnu.org/licenses/gpl-3.0.en.html +// +// You can be released from the requirements of the above licenses by purchasing +// a commercial license. Buying such a license is mandatory if you want to modify or +// otherwise use the software for commercial activities involving the Arduino +// software without disclosing the source code of your own applications. To purchase +// a commercial license, send an email to license@arduino.cc. +// + +#ifndef CLOUDTELEVISION_H_ +#define CLOUDTELEVISION_H_ + +/****************************************************************************** + INCLUDE + ******************************************************************************/ + +#include +#include "../../ArduinoCloudProperty.h" + +/****************************************************************************** + ENUM + ******************************************************************************/ +enum class PlaybackCommands : u_int8_t { + FastForward = 0, + Next = 1, + Pause = 2, + Play = 3, + Previous = 4, + Rewind = 5, + StartOver = 6, + Stop = 7, + None = 255 +}; +enum class InputValue : u_int8_t { + AUX1 = 0, + AUX2 = 1, + AUX3 = 2, + AUX4 = 3, + AUX5 = 4, + AUX6 = 5, + AUX7 = 6, + BLURAY = 7, + CABLE = 8, + CD = 9, + COAX1 = 10, + COAX2 = 11, + COMPOSITE1 = 12, + DVD = 13, + GAME = 14, + HDRADIO = 15, + HDMI1 = 16, + HDMI2 = 17, + HDMI3 = 18, + HDMI4 = 19, + HDMI5 = 20, + HDMI6 = 21, + HDMI7 = 22, + HDMI8 = 23, + HDMI9 = 24, + HDMI10 = 25, + HDMIARC = 26, + INPUT1 = 27, + INPUT2 = 28, + INPUT3 = 29, + INPUT4 = 30, + INPUT5 = 31, + INPUT6 = 32, + INPUT7 = 33, + INPUT8 = 34, + INPUT9 = 35, + INPUT10 = 36, + IPOD = 37, + LINE1 = 38, + LINE2 = 39, + LINE3 = 40, + LINE4 = 41, + LINE5 = 42, + LINE6 = 43, + LINE7 = 44, + MEDIAPLAYER = 45, + OPTICAL1 = 46, + OPTICAL2 = 47, + PHONO = 48, + PLAYSTATION = 49, + PLAYSTATION3 = 50, + PLAYSTATION4 = 51, + SATELLITE = 52, + SMARTCAST = 53, + TUNER = 54, + TV = 55, + USBDAC = 56, + VIDEO1 = 57, + VIDEO2 = 58, + VIDEO3 = 59, + XBOX = 60 +}; + +/****************************************************************************** + CLASS DECLARATION + ******************************************************************************/ + +class Television { + public: + bool swi; + u_int8_t vol; + bool mut; + PlaybackCommands pbc; + InputValue inp; + u_int16_t cha; + + + Television(bool swi, u_int8_t vol, bool mut, PlaybackCommands pbc, InputValue inp, u_int16_t cha): + swi(swi), + vol(vol), + mut(mut), + pbc(pbc), + inp(inp), + cha(cha) + { + } + + bool operator==(Television & aTV) { + return + aTV.swi == swi && + aTV.vol == vol && + aTV.mut == mut && + aTV.pbc == pbc && + aTV.inp == inp && + aTV.cha == cha; + } + + bool operator!=(Television & aTV) { + return !(operator==(aTV)); + } + +}; + +class CloudTelevision : public ArduinoCloudProperty { + private: + Television _value, + _cloud_value; + public: + CloudTelevision() : _value(false, 0, false, PlaybackCommands::None, InputValue::TV, 0), _cloud_value(false, 0, false, PlaybackCommands::None, InputValue::TV, 0) {} + CloudTelevision(bool swi, u_int8_t vol, bool mut, PlaybackCommands pbc, InputValue inp, u_int16_t cha) : _value(swi, vol, mut, pbc, inp, cha), _cloud_value(swi, vol, mut, pbc, inp, cha) {} + + virtual bool isDifferentFromCloud() { + + return _value != _cloud_value; + } + + CloudTelevision& operator=(Television aTV) { + _value.swi = aTV.swi; + _value.vol = aTV.vol; + _value.mut = aTV.mut; + _value.pbc = aTV.pbc; + _value.inp = aTV.inp; + _value.cha = aTV.cha; + updateLocalTimestamp(); + return *this; + } + + Television getCloudValue() { + return _cloud_value; + } + + Television getValue() { + return _value; + } + + bool getSwitch() { + return _value.swi; + } + + u_int8_t getVolume() { + return _value.vol; + } + + bool getMute() { + return _value.mut; + } + + PlaybackCommands getPlaybackCommand() { + return _value.pbc; + } + + InputValue getInputValue() { + return _value.inp; + } + + u_int16_t getChannel() { + return _value.cha; + } + + virtual void fromCloudToLocal() { + _value = _cloud_value; + } + virtual void fromLocalToCloud() { + _cloud_value = _value; + } + virtual void appendAttributesToCloud() { + appendAttribute(_value.swi); + appendAttribute(_value.vol); + appendAttribute(_value.mut); + appendAttribute((int)_value.pbc); + appendAttribute((int)_value.inp); + appendAttribute(_value.cha); + } + virtual void setAttributesFromCloud() { + setAttribute(_cloud_value.swi); + setAttribute((int&)_cloud_value.vol); + setAttribute(_cloud_value.mut); + setAttribute((int&)_cloud_value.pbc); + setAttribute((int&)_cloud_value.inp); + setAttribute((int&)_cloud_value.cha); + } +}; + +#endif /* CLOUDTELEVISION_H_ */ \ No newline at end of file From bf0c66b1302d23b031fbd312ce4b017ecc6612a3 Mon Sep 17 00:00:00 2001 From: ilcato Date: Thu, 7 Nov 2019 22:34:24 +0100 Subject: [PATCH 152/175] Fixed formatting --- types/automation/CloudTelevision.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/types/automation/CloudTelevision.h b/types/automation/CloudTelevision.h index 1267cf44a..76e277992 100644 --- a/types/automation/CloudTelevision.h +++ b/types/automation/CloudTelevision.h @@ -117,14 +117,7 @@ class Television { u_int16_t cha; - Television(bool swi, u_int8_t vol, bool mut, PlaybackCommands pbc, InputValue inp, u_int16_t cha): - swi(swi), - vol(vol), - mut(mut), - pbc(pbc), - inp(inp), - cha(cha) - { + Television(bool swi, u_int8_t vol, bool mut, PlaybackCommands pbc, InputValue inp, u_int16_t cha): swi(swi), vol(vol), mut(mut), pbc(pbc), inp(inp), cha(cha) { } bool operator==(Television & aTV) { From eef2689f86efbd8a15153f3a716fa67cd25efb17 Mon Sep 17 00:00:00 2001 From: ilcato Date: Tue, 12 Nov 2019 16:57:52 +0100 Subject: [PATCH 153/175] Implement @lxrobotics suggestions --- types/automation/CloudTelevision.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/types/automation/CloudTelevision.h b/types/automation/CloudTelevision.h index 76e277992..596b20b12 100644 --- a/types/automation/CloudTelevision.h +++ b/types/automation/CloudTelevision.h @@ -28,7 +28,7 @@ /****************************************************************************** ENUM ******************************************************************************/ -enum class PlaybackCommands : u_int8_t { +enum class PlaybackCommands : uint8_t { FastForward = 0, Next = 1, Pause = 2, @@ -39,7 +39,7 @@ enum class PlaybackCommands : u_int8_t { Stop = 7, None = 255 }; -enum class InputValue : u_int8_t { +enum class InputValue : uint8_t { AUX1 = 0, AUX2 = 1, AUX3 = 2, @@ -110,17 +110,17 @@ enum class InputValue : u_int8_t { class Television { public: bool swi; - u_int8_t vol; + uint8_t vol; bool mut; PlaybackCommands pbc; InputValue inp; - u_int16_t cha; + uint16_t cha; - Television(bool swi, u_int8_t vol, bool mut, PlaybackCommands pbc, InputValue inp, u_int16_t cha): swi(swi), vol(vol), mut(mut), pbc(pbc), inp(inp), cha(cha) { + Television(bool const swi, uint8_t const vol, bool const mut, PlaybackCommands const pbc, InputValue const inp, uint16_t const cha): swi(swi), vol(vol), mut(mut), pbc(pbc), inp(inp), cha(cha) { } - bool operator==(Television & aTV) { + bool operator==(Television const & aTV) { return aTV.swi == swi && aTV.vol == vol && @@ -130,7 +130,7 @@ class Television { aTV.cha == cha; } - bool operator!=(Television & aTV) { + bool operator!=(Television const & aTV) { return !(operator==(aTV)); } @@ -142,14 +142,14 @@ class CloudTelevision : public ArduinoCloudProperty { _cloud_value; public: CloudTelevision() : _value(false, 0, false, PlaybackCommands::None, InputValue::TV, 0), _cloud_value(false, 0, false, PlaybackCommands::None, InputValue::TV, 0) {} - CloudTelevision(bool swi, u_int8_t vol, bool mut, PlaybackCommands pbc, InputValue inp, u_int16_t cha) : _value(swi, vol, mut, pbc, inp, cha), _cloud_value(swi, vol, mut, pbc, inp, cha) {} + CloudTelevision(bool const swi, uint8_t const vol, bool const mut, PlaybackCommands const pbc, InputValue const inp, uint16_t const cha) : _value(swi, vol, mut, pbc, inp, cha), _cloud_value(swi, vol, mut, pbc, inp, cha) {} virtual bool isDifferentFromCloud() { return _value != _cloud_value; } - CloudTelevision& operator=(Television aTV) { + CloudTelevision& operator=(Television const aTV) { _value.swi = aTV.swi; _value.vol = aTV.vol; _value.mut = aTV.mut; @@ -172,7 +172,7 @@ class CloudTelevision : public ArduinoCloudProperty { return _value.swi; } - u_int8_t getVolume() { + uint8_t getVolume() { return _value.vol; } @@ -188,7 +188,7 @@ class CloudTelevision : public ArduinoCloudProperty { return _value.inp; } - u_int16_t getChannel() { + uint16_t getChannel() { return _value.cha; } From dc7562bf98e36ec74f2f16d6c7832d428c968d69 Mon Sep 17 00:00:00 2001 From: ilcato Date: Wed, 13 Nov 2019 10:48:26 +0100 Subject: [PATCH 154/175] Fixed missing reference to new type --- ArduinoCloudThing.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 8af0130b5..9919573d1 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -40,6 +40,7 @@ #include "types/automation/CloudSmartPlug.h" #include "types/automation/CloudSwitch.h" #include "types/automation/CloudTemperature.h" +#include "types/automation/CloudTelevision.h" /****************************************************************************** From c4ffeef152f5cdb9d5179dfcfc481865cecd8710 Mon Sep 17 00:00:00 2001 From: ilcato Date: Wed, 13 Nov 2019 17:19:32 +0100 Subject: [PATCH 155/175] Use base integer type Internally encode and decode use int types. --- types/automation/CloudTelevision.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/types/automation/CloudTelevision.h b/types/automation/CloudTelevision.h index 596b20b12..396f7cec9 100644 --- a/types/automation/CloudTelevision.h +++ b/types/automation/CloudTelevision.h @@ -28,7 +28,7 @@ /****************************************************************************** ENUM ******************************************************************************/ -enum class PlaybackCommands : uint8_t { +enum class PlaybackCommands : int { FastForward = 0, Next = 1, Pause = 2, @@ -39,7 +39,7 @@ enum class PlaybackCommands : uint8_t { Stop = 7, None = 255 }; -enum class InputValue : uint8_t { +enum class InputValue : int { AUX1 = 0, AUX2 = 1, AUX3 = 2, @@ -110,14 +110,14 @@ enum class InputValue : uint8_t { class Television { public: bool swi; - uint8_t vol; + int vol; bool mut; PlaybackCommands pbc; InputValue inp; - uint16_t cha; + int cha; - Television(bool const swi, uint8_t const vol, bool const mut, PlaybackCommands const pbc, InputValue const inp, uint16_t const cha): swi(swi), vol(vol), mut(mut), pbc(pbc), inp(inp), cha(cha) { + Television(bool const swi, int const vol, bool const mut, PlaybackCommands const pbc, InputValue const inp, int const cha): swi(swi), vol(vol), mut(mut), pbc(pbc), inp(inp), cha(cha) { } bool operator==(Television const & aTV) { @@ -142,7 +142,7 @@ class CloudTelevision : public ArduinoCloudProperty { _cloud_value; public: CloudTelevision() : _value(false, 0, false, PlaybackCommands::None, InputValue::TV, 0), _cloud_value(false, 0, false, PlaybackCommands::None, InputValue::TV, 0) {} - CloudTelevision(bool const swi, uint8_t const vol, bool const mut, PlaybackCommands const pbc, InputValue const inp, uint16_t const cha) : _value(swi, vol, mut, pbc, inp, cha), _cloud_value(swi, vol, mut, pbc, inp, cha) {} + CloudTelevision(bool const swi, int const vol, bool const mut, PlaybackCommands const pbc, InputValue const inp, int const cha) : _value(swi, vol, mut, pbc, inp, cha), _cloud_value(swi, vol, mut, pbc, inp, cha) {} virtual bool isDifferentFromCloud() { @@ -208,11 +208,11 @@ class CloudTelevision : public ArduinoCloudProperty { } virtual void setAttributesFromCloud() { setAttribute(_cloud_value.swi); - setAttribute((int&)_cloud_value.vol); + setAttribute(_cloud_value.vol); setAttribute(_cloud_value.mut); setAttribute((int&)_cloud_value.pbc); setAttribute((int&)_cloud_value.inp); - setAttribute((int&)_cloud_value.cha); + setAttribute(_cloud_value.cha); } }; From 618563654e51b2283ab986a5f7b4ec2a407d35b2 Mon Sep 17 00:00:00 2001 From: Fabrizio Mirabito Date: Wed, 11 Dec 2019 15:54:48 +0100 Subject: [PATCH 156/175] Update local timestamp after setting values --- types/automation/CloudColoredLight.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/types/automation/CloudColoredLight.h b/types/automation/CloudColoredLight.h index 00f9ed809..d5e380118 100644 --- a/types/automation/CloudColoredLight.h +++ b/types/automation/CloudColoredLight.h @@ -82,6 +82,7 @@ class CloudColoredLight : public CloudColor { void setSwitch(bool const swi) { _value.swi = swi; + updateLocalTimestamp(); } float getHue() { @@ -90,6 +91,7 @@ class CloudColoredLight : public CloudColor { void setHue(float const hue) { _value.hue = hue; + updateLocalTimestamp(); } float getSaturation() { @@ -98,6 +100,7 @@ class CloudColoredLight : public CloudColor { void setSaturation(float const sat) { _value.sat = sat; + updateLocalTimestamp(); } float getBrightness() { @@ -106,6 +109,7 @@ class CloudColoredLight : public CloudColor { void setBrightness(float const bri) { _value.bri = bri; + updateLocalTimestamp(); } virtual void fromCloudToLocal() { From 279a98ebdeb95de04f4da1fc71a5be5c8d9afead Mon Sep 17 00:00:00 2001 From: Fabrizio Mirabito Date: Wed, 11 Dec 2019 15:55:10 +0100 Subject: [PATCH 157/175] Add setters for tv switch, mute and volume --- types/automation/CloudTelevision.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/types/automation/CloudTelevision.h b/types/automation/CloudTelevision.h index 396f7cec9..7cf81ff03 100644 --- a/types/automation/CloudTelevision.h +++ b/types/automation/CloudTelevision.h @@ -168,14 +168,29 @@ class CloudTelevision : public ArduinoCloudProperty { return _value; } + void setSwitch(bool const swi) { + _value.swi = swi; + updateLocalTimestamp(); + } + bool getSwitch() { return _value.swi; } + void setSwitch(uint8_t const vol) { + _value.vol = vol; + updateLocalTimestamp(); + } + uint8_t getVolume() { return _value.vol; } + void setMute(bool const mut) { + _value.mut = mut; + updateLocalTimestamp(); + } + bool getMute() { return _value.mut; } From c221d0988f9c44c54fe3594532d0a02a2b07ba06 Mon Sep 17 00:00:00 2001 From: ilcato Date: Mon, 16 Dec 2019 13:48:32 +0100 Subject: [PATCH 158/175] fix_boolean_management (#49) Manage the case to have boolean values received as integers 0/1 in the CBOR message. --- ArduinoCloudProperty.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/ArduinoCloudProperty.cpp b/ArduinoCloudProperty.cpp index 2b8031c0b..64a3dd603 100644 --- a/ArduinoCloudProperty.cpp +++ b/ArduinoCloudProperty.cpp @@ -170,7 +170,18 @@ void ArduinoCloudProperty::setAttributesFromCloud(LinkedList *map void ArduinoCloudProperty::setAttributeReal(bool& value, String attributeName) { setAttributeReal(attributeName, [&value](CborMapData * md) { - value = md->bool_val.get(); + // Manage the case to have boolean values received as integers 0/1 + if (md->bool_val.isSet()) { + value = md->bool_val.get(); + } else if (md->val.isSet()) { + if (md->val.get() == 0) { + value = false; + } else if (md->val.get() == 1) { + value = true; + } else { + /* This should not happen. Leave the previous value */ + } + } }); } From b3899951c0697b6a00282bc59d3c11e49222b2bc Mon Sep 17 00:00:00 2001 From: Mirko Curtolo <47823364+mirkokurt@users.noreply.github.com> Date: Mon, 16 Dec 2019 13:56:46 +0100 Subject: [PATCH 159/175] LoRa support (#48) --- ArduinoCloudProperty.cpp | 58 +++++++++++++++++++++++++++++++------- ArduinoCloudProperty.h | 15 +++++++++- ArduinoCloudThing.cpp | 60 +++++++++++++++++++++++++++++++++++----- ArduinoCloudThing.h | 22 +++++++++++---- types/CloudLocation.h | 1 - 5 files changed, 131 insertions(+), 25 deletions(-) diff --git a/ArduinoCloudProperty.cpp b/ArduinoCloudProperty.cpp index 64a3dd603..91be62200 100644 --- a/ArduinoCloudProperty.cpp +++ b/ArduinoCloudProperty.cpp @@ -47,7 +47,10 @@ ArduinoCloudProperty::ArduinoCloudProperty() _update_interval_millis(0), _last_local_change_timestamp(0), _last_cloud_change_timestamp(0), - _map_data_list(nullptr) { + _map_data_list(nullptr), + _identifier(0), + _attributeIdentifier(0), + _lightPayload(false) { } /****************************************************************************** @@ -115,7 +118,9 @@ void ArduinoCloudProperty::execCallbackOnSync() { } } -void ArduinoCloudProperty::append(CborEncoder *encoder) { +void ArduinoCloudProperty::append(CborEncoder *encoder, bool lightPayload) { + _lightPayload = lightPayload; + _attributeIdentifier = 0; appendAttributesToCloudReal(encoder); fromLocalToCloud(); _has_been_updated_once = true; @@ -151,20 +156,35 @@ void ArduinoCloudProperty::appendAttributeReal(String value, String attributeNam } void ArduinoCloudProperty::appendAttributeName(String attributeName, std::functionappendValue, CborEncoder *encoder) { + if (attributeName != "") { + // when the attribute name string is not empty, the attribute identifier is incremented in order to be encoded in the message if the _lightPayload flag is set + _attributeIdentifier++; + } CborEncoder mapEncoder; cbor_encoder_create_map(encoder, &mapEncoder, 2); cbor_encode_int(&mapEncoder, static_cast(CborIntegerMapKey::Name)); - String completeName = _name; - if (attributeName != "") { - completeName += ":" + attributeName; + + // if _lightPayload is true, the property and attribute identifiers will be encoded instead of the property name + if (_lightPayload) { + // the most significant byte of the identifier to be encoded represent the property identifier + int completeIdentifier = _attributeIdentifier * 256; + // the least significant byte of the identifier to be encoded represent the attribute identifier + completeIdentifier += _identifier; + cbor_encode_int(&mapEncoder, completeIdentifier); + } else { + String completeName = _name; + if (attributeName != "") { + completeName += ":" + attributeName; + } + cbor_encode_text_stringz(&mapEncoder, completeName.c_str()); } - cbor_encode_text_stringz(&mapEncoder, completeName.c_str()); appendValue(mapEncoder); cbor_encoder_close_container(encoder, &mapEncoder); } void ArduinoCloudProperty::setAttributesFromCloud(LinkedList *map_data_list) { _map_data_list = map_data_list; + _attributeIdentifier = 0; setAttributesFromCloud(); } @@ -204,16 +224,30 @@ void ArduinoCloudProperty::setAttributeReal(String& value, String attributeName) } void ArduinoCloudProperty::setAttributeReal(String attributeName, std::functionsetValue) { + if (attributeName != "") { + _attributeIdentifier++; + } for (int i = 0; i < _map_data_list->size(); i++) { CborMapData *map = _map_data_list->get(i); if (map != nullptr) { - String an = map->attribute_name.get(); - if (an == attributeName) { - setValue(map); - break; + if (map->light_payload.isSet() && map->light_payload.get()) { + // if a light payload is detected, the attribute identifier is retrieved from the cbor map and the corresponding attribute is updated + int attid = map->attribute_identifier.get(); + if (attid == _attributeIdentifier) { + setValue(map); + break; + } + } else { + // if a normal payload is detected, the name of the attribute to be updated is extracted directly from the cbor map + String an = map->attribute_name.get(); + if (an == attributeName) { + setValue(map); + break; + } } } } + } String ArduinoCloudProperty::getAttributeName(String propertyName, char separator) { @@ -244,3 +278,7 @@ unsigned long ArduinoCloudProperty::getLastCloudChangeTimestamp() { unsigned long ArduinoCloudProperty::getLastLocalChangeTimestamp() { return _last_local_change_timestamp; } + +void ArduinoCloudProperty::setIdentifier(int identifier) { + _identifier = identifier; +} diff --git a/ArduinoCloudProperty.h b/ArduinoCloudProperty.h index 57f92ab3f..d2c1991bd 100644 --- a/ArduinoCloudProperty.h +++ b/ArduinoCloudProperty.h @@ -98,7 +98,11 @@ class CborMapData { MapEntry base_name; MapEntry base_time; MapEntry name; + MapEntry name_identifier; + MapEntry light_payload; MapEntry attribute_name; + MapEntry attribute_identifier; + MapEntry property_identifier; MapEntry val; MapEntry str_val; MapEntry bool_val; @@ -138,6 +142,9 @@ class ArduinoCloudProperty { inline String name() const { return _name; } + inline int identifier() const { + return _identifier; + } inline bool isReadableByCloud() const { return (_permission == Permission::Read) || (_permission == Permission::ReadWrite); } @@ -152,9 +159,10 @@ class ArduinoCloudProperty { void setLastLocalChangeTimestamp(unsigned long localChangeTime); unsigned long getLastCloudChangeTimestamp(); unsigned long getLastLocalChangeTimestamp(); + void setIdentifier(int identifier); void updateLocalTimestamp(); - void append(CborEncoder * encoder); + void append(CborEncoder * encoder, bool lightPayload); void appendAttributeReal(bool value, String attributeName = "", CborEncoder *encoder = nullptr); void appendAttributeReal(int value, String attributeName = "", CborEncoder *encoder = nullptr); void appendAttributeReal(float value, String attributeName = "", CborEncoder *encoder = nullptr); @@ -197,6 +205,11 @@ class ArduinoCloudProperty { unsigned long _last_local_change_timestamp; unsigned long _last_cloud_change_timestamp; LinkedList * _map_data_list; + /* Store the identifier of the property in the array list */ + int _identifier; + int _attributeIdentifier; + /* Indicates if the property shall be encoded using the identifier instead of the name */ + bool _lightPayload; }; /****************************************************************************** diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 1cdb8b6d7..32dd59668 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -45,6 +45,7 @@ void PrintFreeRam(void) { ArduinoCloudThing::ArduinoCloudThing() : _numPrimitivesProperties(0), + _numProperties(0), _isSyncMessage(false), _currentPropertyName(""), _currentPropertyBaseTime(0), @@ -58,7 +59,8 @@ ArduinoCloudThing::ArduinoCloudThing() : void ArduinoCloudThing::begin() { } -int ArduinoCloudThing::encode(uint8_t * data, size_t const size) { +int ArduinoCloudThing::encode(uint8_t * data, size_t const size, bool lightPayload) { + // check if backing storage and cloud has diverged // time interval may be elapsed or property may be changed CborEncoder encoder, arrayEncoder; @@ -69,7 +71,7 @@ int ArduinoCloudThing::encode(uint8_t * data, size_t const size) { return -1; } - if (appendChangedProperties(&arrayEncoder) < 1) { + if (appendChangedProperties(&arrayEncoder, lightPayload) < 1) { return -1; } @@ -84,7 +86,7 @@ int ArduinoCloudThing::encode(uint8_t * data, size_t const size) { return bytes_encoded; } -ArduinoCloudProperty& ArduinoCloudThing::addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission) { +ArduinoCloudProperty& ArduinoCloudThing::addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission, int propertyIdentifier) { property.init(name, permission); if (isPropertyInContainer(name)) { return (*getProperty(name)); @@ -92,9 +94,11 @@ ArduinoCloudProperty& ArduinoCloudThing::addPropertyReal(ArduinoCloudProperty & if (property.isPrimitive()) { _numPrimitivesProperties++; } - addProperty(&property); + _numProperties++; + addProperty(&property, propertyIdentifier); return (property); } + } void ArduinoCloudThing::decode(uint8_t const * const payload, size_t const length, bool isSyncMessage) { @@ -158,18 +162,19 @@ bool ArduinoCloudThing::isPropertyInContainer(String const & name) { return false; } -int ArduinoCloudThing::appendChangedProperties(CborEncoder * arrayEncoder) { +int ArduinoCloudThing::appendChangedProperties(CborEncoder * arrayEncoder, bool lightPayload) { int appendedProperties = 0; for (int i = 0; i < _property_list.size(); i++) { ArduinoCloudProperty * p = _property_list.get(i); if (p->shouldBeUpdated() && p->isReadableByCloud()) { - p->append(arrayEncoder); + p->append(arrayEncoder, lightPayload); appendedProperties++; } } return appendedProperties; } +//retrieve property by name ArduinoCloudProperty * ArduinoCloudThing::getProperty(String const & name) { for (int i = 0; i < _property_list.size(); i++) { ArduinoCloudProperty * p = _property_list.get(i); @@ -180,6 +185,17 @@ ArduinoCloudProperty * ArduinoCloudThing::getProperty(String const & name) { return NULL; } +//retrieve property by identifier +ArduinoCloudProperty * ArduinoCloudThing::getProperty(int const & pos) { + for (int i = 0; i < _property_list.size(); i++) { + ArduinoCloudProperty * p = _property_list.get(i); + if (p->identifier() == pos) { + return p; + } + } + return NULL; +} + // this function updates the timestamps on the primitive properties that have been modified locally since last cloud synchronization void ArduinoCloudThing::updateTimestampOnLocallyChangedProperties() { if (_numPrimitivesProperties == 0) { @@ -312,6 +328,7 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Name(CborValue * val MapParserState next_state = MapParserState::Error; if (cbor_value_is_text_string(value_iter)) { + // if the value in the cbor message is a string, it corresponds to the name of the property to be updated (int the form [property_name]:[attribute_name]) char * val = nullptr; size_t val_size = 0; if (cbor_value_dup_text_string(value_iter, &val, &val_size, value_iter) == CborNoError) { @@ -326,8 +343,26 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_Name(CborValue * val map_data->attribute_name.set(attribute_name); next_state = MapParserState::MapKey; } + } else if (cbor_value_is_integer(value_iter)) { + // if the value in the cbor message is an integer, a light payload has been used and an integer identifier should be decode in order to retrieve the corresponding property and attribute name to be updated + int val = 0; + if (cbor_value_get_int(value_iter, &val) == CborNoError) { + map_data->light_payload.set(true); + map_data->name_identifier.set(val & 255); + map_data->attribute_identifier.set(val >> 8); + map_data->light_payload.set(true); + String name = getPropertyNameByIdentifier(val); + map_data->name.set(name); + + + if (cbor_value_advance(value_iter) == CborNoError) { + next_state = MapParserState::MapKey; + } + } } + + return next_state; } @@ -404,7 +439,6 @@ ArduinoCloudThing::MapParserState ArduinoCloudThing::handle_LeaveMap(CborValue * } if (_currentPropertyName != "" && propertyName != _currentPropertyName) { - /* Update the property containers depending on the parsed data */ updateProperty(_currentPropertyName, _currentPropertyBaseTime + _currentPropertyTime); /* Reset current property data */ @@ -461,6 +495,18 @@ void ArduinoCloudThing::updateProperty(String propertyName, unsigned long cloudC } } } + +// retrieve the property name by the identifier +String ArduinoCloudThing::getPropertyNameByIdentifier(int propertyIdentifier) { + ArduinoCloudProperty* property; + if (propertyIdentifier > 255) { + property = getProperty(propertyIdentifier & 255); + } else { + property = getProperty(propertyIdentifier); + } + return property->name(); +} + bool ArduinoCloudThing::ifNumericConvertToDouble(CborValue * value_iter, double * numeric_val) { if (cbor_value_is_integer(value_iter)) { diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index 9919573d1..c44eea661 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -77,23 +77,26 @@ class ArduinoCloudThing { ArduinoCloudThing(); void begin(); - - ArduinoCloudProperty & addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission); + //if propertyIdentifier is different from -1, an integer identifier is associated to the added property to be use instead of the property name when the parameter lightPayload is true in the encode method + ArduinoCloudProperty & addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission, int propertyIdentifier = -1); /* encode return > 0 if a property has changed and encodes the changed properties in CBOR format into the provided buffer */ - int encode(uint8_t * data, size_t const size); + /* if lightPayload is true the integer identifier of the property will be encoded in the message instead of the property name in order to reduce the size of the message payload*/ + int encode(uint8_t * data, size_t const size, bool lightPayload = false); /* decode a CBOR payload received from the cloud */ void decode(uint8_t const * const payload, size_t const length, bool isSyncMessage = false); bool isPropertyInContainer(String const & name); - int appendChangedProperties(CborEncoder * arrayEncoder); + int appendChangedProperties(CborEncoder * arrayEncoder, bool lightPayload); void updateTimestampOnLocallyChangedProperties(); void updateProperty(String propertyName, unsigned long cloudChangeEventTime); + String getPropertyNameByIdentifier(int propertyIdentifier); private: LinkedList _property_list; /* Keep track of the number of primitive properties in the Thing. If 0 it allows the early exit in updateTimestampOnLocallyChangedProperties() */ int _numPrimitivesProperties; + int _numProperties; /* Indicates the if the message received to be decoded is a response to the getLastValues inquiry */ bool _isSyncMessage; /* List of map data that will hold all the attributes of a property */ @@ -135,11 +138,18 @@ class ArduinoCloudThing { static bool ifNumericConvertToDouble(CborValue * value_iter, double * numeric_val); static double convertCborHalfFloatToDouble(uint16_t const half_val); - void freeMapDataList(LinkedList *map_data_list); - inline void addProperty(ArduinoCloudProperty * property_obj) { + void freeMapDataList(LinkedList * map_data_list); + inline void addProperty(ArduinoCloudProperty * property_obj, int propertyIdentifier) { + if (propertyIdentifier != -1) { + property_obj->setIdentifier(propertyIdentifier); + } else { + // if property identifier is -1, an incremental value will be assigned as identifier. + property_obj->setIdentifier(_numProperties); + } _property_list.add(property_obj); } ArduinoCloudProperty * getProperty(String const & name); + ArduinoCloudProperty * getProperty(int const & identifier); }; diff --git a/types/CloudLocation.h b/types/CloudLocation.h index b673d6f2e..79d65543d 100644 --- a/types/CloudLocation.h +++ b/types/CloudLocation.h @@ -83,7 +83,6 @@ class CloudLocation : public ArduinoCloudProperty { return _value; } - virtual void fromCloudToLocal() { _value = _cloud_value; } From 12fc6bb6c5af687e17006f09f4034469669555ba Mon Sep 17 00:00:00 2001 From: per1234 Date: Wed, 15 Jan 2020 06:58:28 -0800 Subject: [PATCH 160/175] Fix CloudTelevision volume setter name The volume setter function was incorrectly named setSwitch. --- types/automation/CloudTelevision.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/automation/CloudTelevision.h b/types/automation/CloudTelevision.h index 7cf81ff03..a2ee63b48 100644 --- a/types/automation/CloudTelevision.h +++ b/types/automation/CloudTelevision.h @@ -177,7 +177,7 @@ class CloudTelevision : public ArduinoCloudProperty { return _value.swi; } - void setSwitch(uint8_t const vol) { + void setVolume(uint8_t const vol) { _value.vol = vol; updateLocalTimestamp(); } @@ -231,4 +231,4 @@ class CloudTelevision : public ArduinoCloudProperty { } }; -#endif /* CLOUDTELEVISION_H_ */ \ No newline at end of file +#endif /* CLOUDTELEVISION_H_ */ From 8fe1faca7fef9dd75f897048fea7ecb238655aa8 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Tue, 25 Feb 2020 13:52:20 +0100 Subject: [PATCH 161/175] Adding a function to register a callback function for retrieving a global timestamp. (#55) This is necessary because right now we are relying on the RTC within the SAMD MCU which is instantiated (RTCZero) within the ArduinoIoTCloud library and reference within ArduinoCloudThing via extern devlaration. Due to the extern binding this is a very brittle dependency which can be easily destroyed, it is therefore better to explicitly register a function which provides the time (this can be serviced by the TimeService class available in ArduinoIoTCloud --- ArduinoCloudProperty.cpp | 10 ++++++++-- ArduinoCloudProperty.h | 4 +++- ArduinoCloudThing.cpp | 7 ++++++- ArduinoCloudThing.h | 2 ++ 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/ArduinoCloudProperty.cpp b/ArduinoCloudProperty.cpp index 91be62200..e7406397d 100644 --- a/ArduinoCloudProperty.cpp +++ b/ArduinoCloudProperty.cpp @@ -39,6 +39,7 @@ ArduinoCloudProperty::ArduinoCloudProperty() _min_delta_property(0.0f), _min_time_between_updates_millis(0), _permission(Permission::Read), + _get_time_func{nullptr}, _update_callback_func(nullptr), _sync_callback_func(nullptr), _has_been_updated_once(false), @@ -56,9 +57,10 @@ ArduinoCloudProperty::ArduinoCloudProperty() /****************************************************************************** PUBLIC MEMBER FUNCTIONS ******************************************************************************/ -void ArduinoCloudProperty::init(String const name, Permission const permission) { +void ArduinoCloudProperty::init(String const name, Permission const permission, GetTimeCallbackFunc func) { _name = name; _permission = permission; + _get_time_func = func; } ArduinoCloudProperty & ArduinoCloudProperty::onUpdate(UpdateCallbackFunc func) { @@ -259,7 +261,11 @@ String ArduinoCloudProperty::getAttributeName(String propertyName, char separato void ArduinoCloudProperty::updateLocalTimestamp() { if (isReadableByCloud()) { - _last_local_change_timestamp = getTimestamp(); + if (_get_time_func) { + _last_local_change_timestamp = _get_time_func(); + } else { + _last_local_change_timestamp = getTimestamp(); + } } } diff --git a/ArduinoCloudProperty.h b/ArduinoCloudProperty.h index d2c1991bd..7144fbfdc 100644 --- a/ArduinoCloudProperty.h +++ b/ArduinoCloudProperty.h @@ -122,6 +122,7 @@ enum class UpdatePolicy { }; typedef void(*UpdateCallbackFunc)(void); +typedef unsigned long(*GetTimeCallbackFunc)(); /****************************************************************************** CLASS DECLARATION @@ -131,7 +132,7 @@ class ArduinoCloudProperty { typedef void(*SyncCallbackFunc)(ArduinoCloudProperty &property); public: ArduinoCloudProperty(); - void init(String const name, Permission const permission); + void init(String const name, Permission const permission, GetTimeCallbackFunc func); /* Composable configuration of the ArduinoCloudProperty class */ ArduinoCloudProperty & onUpdate(UpdateCallbackFunc func); @@ -192,6 +193,7 @@ class ArduinoCloudProperty { private: Permission _permission; + GetTimeCallbackFunc _get_time_func; UpdateCallbackFunc _update_callback_func; void (*_sync_callback_func)(ArduinoCloudProperty &property); diff --git a/ArduinoCloudThing.cpp b/ArduinoCloudThing.cpp index 32dd59668..ec3830c9e 100644 --- a/ArduinoCloudThing.cpp +++ b/ArduinoCloudThing.cpp @@ -44,6 +44,7 @@ void PrintFreeRam(void) { ******************************************************************************/ ArduinoCloudThing::ArduinoCloudThing() : + _get_time_func{nullptr}, _numPrimitivesProperties(0), _numProperties(0), _isSyncMessage(false), @@ -59,6 +60,10 @@ ArduinoCloudThing::ArduinoCloudThing() : void ArduinoCloudThing::begin() { } +void ArduinoCloudThing::registerGetTimeCallbackFunc(GetTimeCallbackFunc func) { + _get_time_func = func; +} + int ArduinoCloudThing::encode(uint8_t * data, size_t const size, bool lightPayload) { // check if backing storage and cloud has diverged @@ -87,7 +92,7 @@ int ArduinoCloudThing::encode(uint8_t * data, size_t const size, bool lightPaylo } ArduinoCloudProperty& ArduinoCloudThing::addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission, int propertyIdentifier) { - property.init(name, permission); + property.init(name, permission, _get_time_func); if (isPropertyInContainer(name)) { return (*getProperty(name)); } else { diff --git a/ArduinoCloudThing.h b/ArduinoCloudThing.h index c44eea661..3bd6b31ef 100644 --- a/ArduinoCloudThing.h +++ b/ArduinoCloudThing.h @@ -77,6 +77,7 @@ class ArduinoCloudThing { ArduinoCloudThing(); void begin(); + void registerGetTimeCallbackFunc(GetTimeCallbackFunc func); //if propertyIdentifier is different from -1, an integer identifier is associated to the added property to be use instead of the property name when the parameter lightPayload is true in the encode method ArduinoCloudProperty & addPropertyReal(ArduinoCloudProperty & property, String const & name, Permission const permission, int propertyIdentifier = -1); @@ -93,6 +94,7 @@ class ArduinoCloudThing { String getPropertyNameByIdentifier(int propertyIdentifier); private: + GetTimeCallbackFunc _get_time_func; LinkedList _property_list; /* Keep track of the number of primitive properties in the Thing. If 0 it allows the early exit in updateTimestampOnLocallyChangedProperties() */ int _numPrimitivesProperties; From f9b5a13834cc966d0121a78d8320bfc663c55148 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Thu, 27 Feb 2020 06:32:01 +0100 Subject: [PATCH 162/175] Removal of hidden dependency to RTCZero which is used to retrieve a epoch type timestamp In https://github.com/arduino-libraries/ArduinoCloudThing/pull/55 the possibility for retrieving a epoch type timestamp via a registered callback function has been introduced, the changes in https://github.com/arduino-libraries/ArduinoIoTCloud/pull/93 register such a function which allows us to to remove the hidden dependency to RTCZero altogether --- ArduinoCloudProperty.cpp | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/ArduinoCloudProperty.cpp b/ArduinoCloudProperty.cpp index e7406397d..6dfdd8997 100644 --- a/ArduinoCloudProperty.cpp +++ b/ArduinoCloudProperty.cpp @@ -17,20 +17,10 @@ #include "ArduinoCloudProperty.h" -#ifdef ARDUINO_ARCH_SAMD - #include - extern RTCZero rtc; +#ifndef ARDUINO_ARCH_SAMD + #pragma message "No RTC available on this architecture - ArduinoIoTCloud will not keep track of local change timestamps ." #endif -static unsigned long getTimestamp() { - #ifdef ARDUINO_ARCH_SAMD - return rtc.getEpoch(); - #else -#pragma message "No RTC available on this architecture - ArduinoIoTCloud will not keep track of local change timestamps ." - return 0; - #endif -} - /****************************************************************************** CTOR/DTOR ******************************************************************************/ @@ -263,8 +253,6 @@ void ArduinoCloudProperty::updateLocalTimestamp() { if (isReadableByCloud()) { if (_get_time_func) { _last_local_change_timestamp = _get_time_func(); - } else { - _last_local_change_timestamp = getTimestamp(); } } } From abb0d0d36cde50b177c0adfddf817f838d8f224d Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 3 Jun 2020 15:06:33 +0200 Subject: [PATCH 163/175] Moving all ArduinoCloudThing code into folder 'cbor' --- ArduinoCloudProperty.cpp => cbor/ArduinoCloudProperty.cpp | 0 ArduinoCloudProperty.h => cbor/ArduinoCloudProperty.h | 0 ArduinoCloudThing.cpp => cbor/ArduinoCloudThing.cpp | 0 ArduinoCloudThing.h => cbor/ArduinoCloudThing.h | 0 {lib => cbor/lib}/LinkedList/LICENSE.txt | 0 {lib => cbor/lib}/LinkedList/LinkedList.h | 0 {lib => cbor/lib}/LinkedList/README.md | 0 {lib => cbor/lib}/LinkedList/keywords.txt | 0 {lib => cbor/lib}/LinkedList/library.json | 0 {lib => cbor/lib}/LinkedList/library.properties | 0 {lib => cbor/lib}/tinycbor/cbor-lib.h | 0 {lib => cbor/lib}/tinycbor/src/cbor.dox | 0 {lib => cbor/lib}/tinycbor/src/cbor.h | 0 {lib => cbor/lib}/tinycbor/src/cborencoder.c | 0 .../lib}/tinycbor/src/cborencoder_close_container_checked.c | 0 {lib => cbor/lib}/tinycbor/src/cborerrorstrings.c | 0 {lib => cbor/lib}/tinycbor/src/cborinternal_p.h | 0 {lib => cbor/lib}/tinycbor/src/cborjson.h | 0 {lib => cbor/lib}/tinycbor/src/cborparser.c | 0 {lib => cbor/lib}/tinycbor/src/cborparser_dup_string.c | 0 {lib => cbor/lib}/tinycbor/src/cborpretty.c | 0 {lib => cbor/lib}/tinycbor/src/cborpretty_stdio.c | 0 {lib => cbor/lib}/tinycbor/src/cbortojson.c | 0 {lib => cbor/lib}/tinycbor/src/cborvalidation.c | 0 {lib => cbor/lib}/tinycbor/src/compilersupport_p.h | 0 {lib => cbor/lib}/tinycbor/src/open_memstream.c | 0 {lib => cbor/lib}/tinycbor/src/tags.txt | 0 {lib => cbor/lib}/tinycbor/src/tinycbor-version.h | 0 {lib => cbor/lib}/tinycbor/src/utf8_p.h | 0 {types => cbor/types}/CloudBool.h | 0 {types => cbor/types}/CloudColor.h | 0 {types => cbor/types}/CloudFloat.h | 0 {types => cbor/types}/CloudInt.h | 0 {types => cbor/types}/CloudLocation.h | 0 {types => cbor/types}/CloudString.h | 0 {types => cbor/types}/CloudWrapperBase.h | 0 {types => cbor/types}/CloudWrapperBool.h | 0 {types => cbor/types}/CloudWrapperFloat.h | 0 {types => cbor/types}/CloudWrapperInt.h | 0 {types => cbor/types}/CloudWrapperString.h | 0 {types => cbor/types}/automation/CloudColoredLight.h | 0 {types => cbor/types}/automation/CloudContactSensor.h | 0 {types => cbor/types}/automation/CloudDimmedLight.h | 0 {types => cbor/types}/automation/CloudLight.h | 0 {types => cbor/types}/automation/CloudMotionSensor.h | 0 {types => cbor/types}/automation/CloudSmartPlug.h | 0 {types => cbor/types}/automation/CloudSwitch.h | 0 {types => cbor/types}/automation/CloudTelevision.h | 0 {types => cbor/types}/automation/CloudTemperature.h | 0 49 files changed, 0 insertions(+), 0 deletions(-) rename ArduinoCloudProperty.cpp => cbor/ArduinoCloudProperty.cpp (100%) rename ArduinoCloudProperty.h => cbor/ArduinoCloudProperty.h (100%) rename ArduinoCloudThing.cpp => cbor/ArduinoCloudThing.cpp (100%) rename ArduinoCloudThing.h => cbor/ArduinoCloudThing.h (100%) rename {lib => cbor/lib}/LinkedList/LICENSE.txt (100%) rename {lib => cbor/lib}/LinkedList/LinkedList.h (100%) rename {lib => cbor/lib}/LinkedList/README.md (100%) rename {lib => cbor/lib}/LinkedList/keywords.txt (100%) rename {lib => cbor/lib}/LinkedList/library.json (100%) rename {lib => cbor/lib}/LinkedList/library.properties (100%) rename {lib => cbor/lib}/tinycbor/cbor-lib.h (100%) rename {lib => cbor/lib}/tinycbor/src/cbor.dox (100%) rename {lib => cbor/lib}/tinycbor/src/cbor.h (100%) rename {lib => cbor/lib}/tinycbor/src/cborencoder.c (100%) rename {lib => cbor/lib}/tinycbor/src/cborencoder_close_container_checked.c (100%) rename {lib => cbor/lib}/tinycbor/src/cborerrorstrings.c (100%) rename {lib => cbor/lib}/tinycbor/src/cborinternal_p.h (100%) rename {lib => cbor/lib}/tinycbor/src/cborjson.h (100%) rename {lib => cbor/lib}/tinycbor/src/cborparser.c (100%) rename {lib => cbor/lib}/tinycbor/src/cborparser_dup_string.c (100%) rename {lib => cbor/lib}/tinycbor/src/cborpretty.c (100%) rename {lib => cbor/lib}/tinycbor/src/cborpretty_stdio.c (100%) rename {lib => cbor/lib}/tinycbor/src/cbortojson.c (100%) rename {lib => cbor/lib}/tinycbor/src/cborvalidation.c (100%) rename {lib => cbor/lib}/tinycbor/src/compilersupport_p.h (100%) rename {lib => cbor/lib}/tinycbor/src/open_memstream.c (100%) rename {lib => cbor/lib}/tinycbor/src/tags.txt (100%) rename {lib => cbor/lib}/tinycbor/src/tinycbor-version.h (100%) rename {lib => cbor/lib}/tinycbor/src/utf8_p.h (100%) rename {types => cbor/types}/CloudBool.h (100%) rename {types => cbor/types}/CloudColor.h (100%) rename {types => cbor/types}/CloudFloat.h (100%) rename {types => cbor/types}/CloudInt.h (100%) rename {types => cbor/types}/CloudLocation.h (100%) rename {types => cbor/types}/CloudString.h (100%) rename {types => cbor/types}/CloudWrapperBase.h (100%) rename {types => cbor/types}/CloudWrapperBool.h (100%) rename {types => cbor/types}/CloudWrapperFloat.h (100%) rename {types => cbor/types}/CloudWrapperInt.h (100%) rename {types => cbor/types}/CloudWrapperString.h (100%) rename {types => cbor/types}/automation/CloudColoredLight.h (100%) rename {types => cbor/types}/automation/CloudContactSensor.h (100%) rename {types => cbor/types}/automation/CloudDimmedLight.h (100%) rename {types => cbor/types}/automation/CloudLight.h (100%) rename {types => cbor/types}/automation/CloudMotionSensor.h (100%) rename {types => cbor/types}/automation/CloudSmartPlug.h (100%) rename {types => cbor/types}/automation/CloudSwitch.h (100%) rename {types => cbor/types}/automation/CloudTelevision.h (100%) rename {types => cbor/types}/automation/CloudTemperature.h (100%) diff --git a/ArduinoCloudProperty.cpp b/cbor/ArduinoCloudProperty.cpp similarity index 100% rename from ArduinoCloudProperty.cpp rename to cbor/ArduinoCloudProperty.cpp diff --git a/ArduinoCloudProperty.h b/cbor/ArduinoCloudProperty.h similarity index 100% rename from ArduinoCloudProperty.h rename to cbor/ArduinoCloudProperty.h diff --git a/ArduinoCloudThing.cpp b/cbor/ArduinoCloudThing.cpp similarity index 100% rename from ArduinoCloudThing.cpp rename to cbor/ArduinoCloudThing.cpp diff --git a/ArduinoCloudThing.h b/cbor/ArduinoCloudThing.h similarity index 100% rename from ArduinoCloudThing.h rename to cbor/ArduinoCloudThing.h diff --git a/lib/LinkedList/LICENSE.txt b/cbor/lib/LinkedList/LICENSE.txt similarity index 100% rename from lib/LinkedList/LICENSE.txt rename to cbor/lib/LinkedList/LICENSE.txt diff --git a/lib/LinkedList/LinkedList.h b/cbor/lib/LinkedList/LinkedList.h similarity index 100% rename from lib/LinkedList/LinkedList.h rename to cbor/lib/LinkedList/LinkedList.h diff --git a/lib/LinkedList/README.md b/cbor/lib/LinkedList/README.md similarity index 100% rename from lib/LinkedList/README.md rename to cbor/lib/LinkedList/README.md diff --git a/lib/LinkedList/keywords.txt b/cbor/lib/LinkedList/keywords.txt similarity index 100% rename from lib/LinkedList/keywords.txt rename to cbor/lib/LinkedList/keywords.txt diff --git a/lib/LinkedList/library.json b/cbor/lib/LinkedList/library.json similarity index 100% rename from lib/LinkedList/library.json rename to cbor/lib/LinkedList/library.json diff --git a/lib/LinkedList/library.properties b/cbor/lib/LinkedList/library.properties similarity index 100% rename from lib/LinkedList/library.properties rename to cbor/lib/LinkedList/library.properties diff --git a/lib/tinycbor/cbor-lib.h b/cbor/lib/tinycbor/cbor-lib.h similarity index 100% rename from lib/tinycbor/cbor-lib.h rename to cbor/lib/tinycbor/cbor-lib.h diff --git a/lib/tinycbor/src/cbor.dox b/cbor/lib/tinycbor/src/cbor.dox similarity index 100% rename from lib/tinycbor/src/cbor.dox rename to cbor/lib/tinycbor/src/cbor.dox diff --git a/lib/tinycbor/src/cbor.h b/cbor/lib/tinycbor/src/cbor.h similarity index 100% rename from lib/tinycbor/src/cbor.h rename to cbor/lib/tinycbor/src/cbor.h diff --git a/lib/tinycbor/src/cborencoder.c b/cbor/lib/tinycbor/src/cborencoder.c similarity index 100% rename from lib/tinycbor/src/cborencoder.c rename to cbor/lib/tinycbor/src/cborencoder.c diff --git a/lib/tinycbor/src/cborencoder_close_container_checked.c b/cbor/lib/tinycbor/src/cborencoder_close_container_checked.c similarity index 100% rename from lib/tinycbor/src/cborencoder_close_container_checked.c rename to cbor/lib/tinycbor/src/cborencoder_close_container_checked.c diff --git a/lib/tinycbor/src/cborerrorstrings.c b/cbor/lib/tinycbor/src/cborerrorstrings.c similarity index 100% rename from lib/tinycbor/src/cborerrorstrings.c rename to cbor/lib/tinycbor/src/cborerrorstrings.c diff --git a/lib/tinycbor/src/cborinternal_p.h b/cbor/lib/tinycbor/src/cborinternal_p.h similarity index 100% rename from lib/tinycbor/src/cborinternal_p.h rename to cbor/lib/tinycbor/src/cborinternal_p.h diff --git a/lib/tinycbor/src/cborjson.h b/cbor/lib/tinycbor/src/cborjson.h similarity index 100% rename from lib/tinycbor/src/cborjson.h rename to cbor/lib/tinycbor/src/cborjson.h diff --git a/lib/tinycbor/src/cborparser.c b/cbor/lib/tinycbor/src/cborparser.c similarity index 100% rename from lib/tinycbor/src/cborparser.c rename to cbor/lib/tinycbor/src/cborparser.c diff --git a/lib/tinycbor/src/cborparser_dup_string.c b/cbor/lib/tinycbor/src/cborparser_dup_string.c similarity index 100% rename from lib/tinycbor/src/cborparser_dup_string.c rename to cbor/lib/tinycbor/src/cborparser_dup_string.c diff --git a/lib/tinycbor/src/cborpretty.c b/cbor/lib/tinycbor/src/cborpretty.c similarity index 100% rename from lib/tinycbor/src/cborpretty.c rename to cbor/lib/tinycbor/src/cborpretty.c diff --git a/lib/tinycbor/src/cborpretty_stdio.c b/cbor/lib/tinycbor/src/cborpretty_stdio.c similarity index 100% rename from lib/tinycbor/src/cborpretty_stdio.c rename to cbor/lib/tinycbor/src/cborpretty_stdio.c diff --git a/lib/tinycbor/src/cbortojson.c b/cbor/lib/tinycbor/src/cbortojson.c similarity index 100% rename from lib/tinycbor/src/cbortojson.c rename to cbor/lib/tinycbor/src/cbortojson.c diff --git a/lib/tinycbor/src/cborvalidation.c b/cbor/lib/tinycbor/src/cborvalidation.c similarity index 100% rename from lib/tinycbor/src/cborvalidation.c rename to cbor/lib/tinycbor/src/cborvalidation.c diff --git a/lib/tinycbor/src/compilersupport_p.h b/cbor/lib/tinycbor/src/compilersupport_p.h similarity index 100% rename from lib/tinycbor/src/compilersupport_p.h rename to cbor/lib/tinycbor/src/compilersupport_p.h diff --git a/lib/tinycbor/src/open_memstream.c b/cbor/lib/tinycbor/src/open_memstream.c similarity index 100% rename from lib/tinycbor/src/open_memstream.c rename to cbor/lib/tinycbor/src/open_memstream.c diff --git a/lib/tinycbor/src/tags.txt b/cbor/lib/tinycbor/src/tags.txt similarity index 100% rename from lib/tinycbor/src/tags.txt rename to cbor/lib/tinycbor/src/tags.txt diff --git a/lib/tinycbor/src/tinycbor-version.h b/cbor/lib/tinycbor/src/tinycbor-version.h similarity index 100% rename from lib/tinycbor/src/tinycbor-version.h rename to cbor/lib/tinycbor/src/tinycbor-version.h diff --git a/lib/tinycbor/src/utf8_p.h b/cbor/lib/tinycbor/src/utf8_p.h similarity index 100% rename from lib/tinycbor/src/utf8_p.h rename to cbor/lib/tinycbor/src/utf8_p.h diff --git a/types/CloudBool.h b/cbor/types/CloudBool.h similarity index 100% rename from types/CloudBool.h rename to cbor/types/CloudBool.h diff --git a/types/CloudColor.h b/cbor/types/CloudColor.h similarity index 100% rename from types/CloudColor.h rename to cbor/types/CloudColor.h diff --git a/types/CloudFloat.h b/cbor/types/CloudFloat.h similarity index 100% rename from types/CloudFloat.h rename to cbor/types/CloudFloat.h diff --git a/types/CloudInt.h b/cbor/types/CloudInt.h similarity index 100% rename from types/CloudInt.h rename to cbor/types/CloudInt.h diff --git a/types/CloudLocation.h b/cbor/types/CloudLocation.h similarity index 100% rename from types/CloudLocation.h rename to cbor/types/CloudLocation.h diff --git a/types/CloudString.h b/cbor/types/CloudString.h similarity index 100% rename from types/CloudString.h rename to cbor/types/CloudString.h diff --git a/types/CloudWrapperBase.h b/cbor/types/CloudWrapperBase.h similarity index 100% rename from types/CloudWrapperBase.h rename to cbor/types/CloudWrapperBase.h diff --git a/types/CloudWrapperBool.h b/cbor/types/CloudWrapperBool.h similarity index 100% rename from types/CloudWrapperBool.h rename to cbor/types/CloudWrapperBool.h diff --git a/types/CloudWrapperFloat.h b/cbor/types/CloudWrapperFloat.h similarity index 100% rename from types/CloudWrapperFloat.h rename to cbor/types/CloudWrapperFloat.h diff --git a/types/CloudWrapperInt.h b/cbor/types/CloudWrapperInt.h similarity index 100% rename from types/CloudWrapperInt.h rename to cbor/types/CloudWrapperInt.h diff --git a/types/CloudWrapperString.h b/cbor/types/CloudWrapperString.h similarity index 100% rename from types/CloudWrapperString.h rename to cbor/types/CloudWrapperString.h diff --git a/types/automation/CloudColoredLight.h b/cbor/types/automation/CloudColoredLight.h similarity index 100% rename from types/automation/CloudColoredLight.h rename to cbor/types/automation/CloudColoredLight.h diff --git a/types/automation/CloudContactSensor.h b/cbor/types/automation/CloudContactSensor.h similarity index 100% rename from types/automation/CloudContactSensor.h rename to cbor/types/automation/CloudContactSensor.h diff --git a/types/automation/CloudDimmedLight.h b/cbor/types/automation/CloudDimmedLight.h similarity index 100% rename from types/automation/CloudDimmedLight.h rename to cbor/types/automation/CloudDimmedLight.h diff --git a/types/automation/CloudLight.h b/cbor/types/automation/CloudLight.h similarity index 100% rename from types/automation/CloudLight.h rename to cbor/types/automation/CloudLight.h diff --git a/types/automation/CloudMotionSensor.h b/cbor/types/automation/CloudMotionSensor.h similarity index 100% rename from types/automation/CloudMotionSensor.h rename to cbor/types/automation/CloudMotionSensor.h diff --git a/types/automation/CloudSmartPlug.h b/cbor/types/automation/CloudSmartPlug.h similarity index 100% rename from types/automation/CloudSmartPlug.h rename to cbor/types/automation/CloudSmartPlug.h diff --git a/types/automation/CloudSwitch.h b/cbor/types/automation/CloudSwitch.h similarity index 100% rename from types/automation/CloudSwitch.h rename to cbor/types/automation/CloudSwitch.h diff --git a/types/automation/CloudTelevision.h b/cbor/types/automation/CloudTelevision.h similarity index 100% rename from types/automation/CloudTelevision.h rename to cbor/types/automation/CloudTelevision.h diff --git a/types/automation/CloudTemperature.h b/cbor/types/automation/CloudTemperature.h similarity index 100% rename from types/automation/CloudTemperature.h rename to cbor/types/automation/CloudTemperature.h From 088c2b64031064872ca369baf908cc7f0808e692 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 3 Jun 2020 15:15:36 +0200 Subject: [PATCH 164/175] Moving pulled in cbor folder from ArduinoCloudThing into src --- {cbor => src/cbor}/ArduinoCloudProperty.cpp | 0 {cbor => src/cbor}/ArduinoCloudProperty.h | 0 {cbor => src/cbor}/ArduinoCloudThing.cpp | 0 {cbor => src/cbor}/ArduinoCloudThing.h | 0 {cbor => src/cbor}/lib/LinkedList/LICENSE.txt | 0 {cbor => src/cbor}/lib/LinkedList/LinkedList.h | 0 {cbor => src/cbor}/lib/LinkedList/README.md | 0 {cbor => src/cbor}/lib/LinkedList/keywords.txt | 0 {cbor => src/cbor}/lib/LinkedList/library.json | 0 {cbor => src/cbor}/lib/LinkedList/library.properties | 0 {cbor => src/cbor}/lib/tinycbor/cbor-lib.h | 0 {cbor => src/cbor}/lib/tinycbor/src/cbor.dox | 0 {cbor => src/cbor}/lib/tinycbor/src/cbor.h | 0 {cbor => src/cbor}/lib/tinycbor/src/cborencoder.c | 0 .../cbor}/lib/tinycbor/src/cborencoder_close_container_checked.c | 0 {cbor => src/cbor}/lib/tinycbor/src/cborerrorstrings.c | 0 {cbor => src/cbor}/lib/tinycbor/src/cborinternal_p.h | 0 {cbor => src/cbor}/lib/tinycbor/src/cborjson.h | 0 {cbor => src/cbor}/lib/tinycbor/src/cborparser.c | 0 {cbor => src/cbor}/lib/tinycbor/src/cborparser_dup_string.c | 0 {cbor => src/cbor}/lib/tinycbor/src/cborpretty.c | 0 {cbor => src/cbor}/lib/tinycbor/src/cborpretty_stdio.c | 0 {cbor => src/cbor}/lib/tinycbor/src/cbortojson.c | 0 {cbor => src/cbor}/lib/tinycbor/src/cborvalidation.c | 0 {cbor => src/cbor}/lib/tinycbor/src/compilersupport_p.h | 0 {cbor => src/cbor}/lib/tinycbor/src/open_memstream.c | 0 {cbor => src/cbor}/lib/tinycbor/src/tags.txt | 0 {cbor => src/cbor}/lib/tinycbor/src/tinycbor-version.h | 0 {cbor => src/cbor}/lib/tinycbor/src/utf8_p.h | 0 {cbor => src/cbor}/types/CloudBool.h | 0 {cbor => src/cbor}/types/CloudColor.h | 0 {cbor => src/cbor}/types/CloudFloat.h | 0 {cbor => src/cbor}/types/CloudInt.h | 0 {cbor => src/cbor}/types/CloudLocation.h | 0 {cbor => src/cbor}/types/CloudString.h | 0 {cbor => src/cbor}/types/CloudWrapperBase.h | 0 {cbor => src/cbor}/types/CloudWrapperBool.h | 0 {cbor => src/cbor}/types/CloudWrapperFloat.h | 0 {cbor => src/cbor}/types/CloudWrapperInt.h | 0 {cbor => src/cbor}/types/CloudWrapperString.h | 0 {cbor => src/cbor}/types/automation/CloudColoredLight.h | 0 {cbor => src/cbor}/types/automation/CloudContactSensor.h | 0 {cbor => src/cbor}/types/automation/CloudDimmedLight.h | 0 {cbor => src/cbor}/types/automation/CloudLight.h | 0 {cbor => src/cbor}/types/automation/CloudMotionSensor.h | 0 {cbor => src/cbor}/types/automation/CloudSmartPlug.h | 0 {cbor => src/cbor}/types/automation/CloudSwitch.h | 0 {cbor => src/cbor}/types/automation/CloudTelevision.h | 0 {cbor => src/cbor}/types/automation/CloudTemperature.h | 0 49 files changed, 0 insertions(+), 0 deletions(-) rename {cbor => src/cbor}/ArduinoCloudProperty.cpp (100%) rename {cbor => src/cbor}/ArduinoCloudProperty.h (100%) rename {cbor => src/cbor}/ArduinoCloudThing.cpp (100%) rename {cbor => src/cbor}/ArduinoCloudThing.h (100%) rename {cbor => src/cbor}/lib/LinkedList/LICENSE.txt (100%) rename {cbor => src/cbor}/lib/LinkedList/LinkedList.h (100%) rename {cbor => src/cbor}/lib/LinkedList/README.md (100%) rename {cbor => src/cbor}/lib/LinkedList/keywords.txt (100%) rename {cbor => src/cbor}/lib/LinkedList/library.json (100%) rename {cbor => src/cbor}/lib/LinkedList/library.properties (100%) rename {cbor => src/cbor}/lib/tinycbor/cbor-lib.h (100%) rename {cbor => src/cbor}/lib/tinycbor/src/cbor.dox (100%) rename {cbor => src/cbor}/lib/tinycbor/src/cbor.h (100%) rename {cbor => src/cbor}/lib/tinycbor/src/cborencoder.c (100%) rename {cbor => src/cbor}/lib/tinycbor/src/cborencoder_close_container_checked.c (100%) rename {cbor => src/cbor}/lib/tinycbor/src/cborerrorstrings.c (100%) rename {cbor => src/cbor}/lib/tinycbor/src/cborinternal_p.h (100%) rename {cbor => src/cbor}/lib/tinycbor/src/cborjson.h (100%) rename {cbor => src/cbor}/lib/tinycbor/src/cborparser.c (100%) rename {cbor => src/cbor}/lib/tinycbor/src/cborparser_dup_string.c (100%) rename {cbor => src/cbor}/lib/tinycbor/src/cborpretty.c (100%) rename {cbor => src/cbor}/lib/tinycbor/src/cborpretty_stdio.c (100%) rename {cbor => src/cbor}/lib/tinycbor/src/cbortojson.c (100%) rename {cbor => src/cbor}/lib/tinycbor/src/cborvalidation.c (100%) rename {cbor => src/cbor}/lib/tinycbor/src/compilersupport_p.h (100%) rename {cbor => src/cbor}/lib/tinycbor/src/open_memstream.c (100%) rename {cbor => src/cbor}/lib/tinycbor/src/tags.txt (100%) rename {cbor => src/cbor}/lib/tinycbor/src/tinycbor-version.h (100%) rename {cbor => src/cbor}/lib/tinycbor/src/utf8_p.h (100%) rename {cbor => src/cbor}/types/CloudBool.h (100%) rename {cbor => src/cbor}/types/CloudColor.h (100%) rename {cbor => src/cbor}/types/CloudFloat.h (100%) rename {cbor => src/cbor}/types/CloudInt.h (100%) rename {cbor => src/cbor}/types/CloudLocation.h (100%) rename {cbor => src/cbor}/types/CloudString.h (100%) rename {cbor => src/cbor}/types/CloudWrapperBase.h (100%) rename {cbor => src/cbor}/types/CloudWrapperBool.h (100%) rename {cbor => src/cbor}/types/CloudWrapperFloat.h (100%) rename {cbor => src/cbor}/types/CloudWrapperInt.h (100%) rename {cbor => src/cbor}/types/CloudWrapperString.h (100%) rename {cbor => src/cbor}/types/automation/CloudColoredLight.h (100%) rename {cbor => src/cbor}/types/automation/CloudContactSensor.h (100%) rename {cbor => src/cbor}/types/automation/CloudDimmedLight.h (100%) rename {cbor => src/cbor}/types/automation/CloudLight.h (100%) rename {cbor => src/cbor}/types/automation/CloudMotionSensor.h (100%) rename {cbor => src/cbor}/types/automation/CloudSmartPlug.h (100%) rename {cbor => src/cbor}/types/automation/CloudSwitch.h (100%) rename {cbor => src/cbor}/types/automation/CloudTelevision.h (100%) rename {cbor => src/cbor}/types/automation/CloudTemperature.h (100%) diff --git a/cbor/ArduinoCloudProperty.cpp b/src/cbor/ArduinoCloudProperty.cpp similarity index 100% rename from cbor/ArduinoCloudProperty.cpp rename to src/cbor/ArduinoCloudProperty.cpp diff --git a/cbor/ArduinoCloudProperty.h b/src/cbor/ArduinoCloudProperty.h similarity index 100% rename from cbor/ArduinoCloudProperty.h rename to src/cbor/ArduinoCloudProperty.h diff --git a/cbor/ArduinoCloudThing.cpp b/src/cbor/ArduinoCloudThing.cpp similarity index 100% rename from cbor/ArduinoCloudThing.cpp rename to src/cbor/ArduinoCloudThing.cpp diff --git a/cbor/ArduinoCloudThing.h b/src/cbor/ArduinoCloudThing.h similarity index 100% rename from cbor/ArduinoCloudThing.h rename to src/cbor/ArduinoCloudThing.h diff --git a/cbor/lib/LinkedList/LICENSE.txt b/src/cbor/lib/LinkedList/LICENSE.txt similarity index 100% rename from cbor/lib/LinkedList/LICENSE.txt rename to src/cbor/lib/LinkedList/LICENSE.txt diff --git a/cbor/lib/LinkedList/LinkedList.h b/src/cbor/lib/LinkedList/LinkedList.h similarity index 100% rename from cbor/lib/LinkedList/LinkedList.h rename to src/cbor/lib/LinkedList/LinkedList.h diff --git a/cbor/lib/LinkedList/README.md b/src/cbor/lib/LinkedList/README.md similarity index 100% rename from cbor/lib/LinkedList/README.md rename to src/cbor/lib/LinkedList/README.md diff --git a/cbor/lib/LinkedList/keywords.txt b/src/cbor/lib/LinkedList/keywords.txt similarity index 100% rename from cbor/lib/LinkedList/keywords.txt rename to src/cbor/lib/LinkedList/keywords.txt diff --git a/cbor/lib/LinkedList/library.json b/src/cbor/lib/LinkedList/library.json similarity index 100% rename from cbor/lib/LinkedList/library.json rename to src/cbor/lib/LinkedList/library.json diff --git a/cbor/lib/LinkedList/library.properties b/src/cbor/lib/LinkedList/library.properties similarity index 100% rename from cbor/lib/LinkedList/library.properties rename to src/cbor/lib/LinkedList/library.properties diff --git a/cbor/lib/tinycbor/cbor-lib.h b/src/cbor/lib/tinycbor/cbor-lib.h similarity index 100% rename from cbor/lib/tinycbor/cbor-lib.h rename to src/cbor/lib/tinycbor/cbor-lib.h diff --git a/cbor/lib/tinycbor/src/cbor.dox b/src/cbor/lib/tinycbor/src/cbor.dox similarity index 100% rename from cbor/lib/tinycbor/src/cbor.dox rename to src/cbor/lib/tinycbor/src/cbor.dox diff --git a/cbor/lib/tinycbor/src/cbor.h b/src/cbor/lib/tinycbor/src/cbor.h similarity index 100% rename from cbor/lib/tinycbor/src/cbor.h rename to src/cbor/lib/tinycbor/src/cbor.h diff --git a/cbor/lib/tinycbor/src/cborencoder.c b/src/cbor/lib/tinycbor/src/cborencoder.c similarity index 100% rename from cbor/lib/tinycbor/src/cborencoder.c rename to src/cbor/lib/tinycbor/src/cborencoder.c diff --git a/cbor/lib/tinycbor/src/cborencoder_close_container_checked.c b/src/cbor/lib/tinycbor/src/cborencoder_close_container_checked.c similarity index 100% rename from cbor/lib/tinycbor/src/cborencoder_close_container_checked.c rename to src/cbor/lib/tinycbor/src/cborencoder_close_container_checked.c diff --git a/cbor/lib/tinycbor/src/cborerrorstrings.c b/src/cbor/lib/tinycbor/src/cborerrorstrings.c similarity index 100% rename from cbor/lib/tinycbor/src/cborerrorstrings.c rename to src/cbor/lib/tinycbor/src/cborerrorstrings.c diff --git a/cbor/lib/tinycbor/src/cborinternal_p.h b/src/cbor/lib/tinycbor/src/cborinternal_p.h similarity index 100% rename from cbor/lib/tinycbor/src/cborinternal_p.h rename to src/cbor/lib/tinycbor/src/cborinternal_p.h diff --git a/cbor/lib/tinycbor/src/cborjson.h b/src/cbor/lib/tinycbor/src/cborjson.h similarity index 100% rename from cbor/lib/tinycbor/src/cborjson.h rename to src/cbor/lib/tinycbor/src/cborjson.h diff --git a/cbor/lib/tinycbor/src/cborparser.c b/src/cbor/lib/tinycbor/src/cborparser.c similarity index 100% rename from cbor/lib/tinycbor/src/cborparser.c rename to src/cbor/lib/tinycbor/src/cborparser.c diff --git a/cbor/lib/tinycbor/src/cborparser_dup_string.c b/src/cbor/lib/tinycbor/src/cborparser_dup_string.c similarity index 100% rename from cbor/lib/tinycbor/src/cborparser_dup_string.c rename to src/cbor/lib/tinycbor/src/cborparser_dup_string.c diff --git a/cbor/lib/tinycbor/src/cborpretty.c b/src/cbor/lib/tinycbor/src/cborpretty.c similarity index 100% rename from cbor/lib/tinycbor/src/cborpretty.c rename to src/cbor/lib/tinycbor/src/cborpretty.c diff --git a/cbor/lib/tinycbor/src/cborpretty_stdio.c b/src/cbor/lib/tinycbor/src/cborpretty_stdio.c similarity index 100% rename from cbor/lib/tinycbor/src/cborpretty_stdio.c rename to src/cbor/lib/tinycbor/src/cborpretty_stdio.c diff --git a/cbor/lib/tinycbor/src/cbortojson.c b/src/cbor/lib/tinycbor/src/cbortojson.c similarity index 100% rename from cbor/lib/tinycbor/src/cbortojson.c rename to src/cbor/lib/tinycbor/src/cbortojson.c diff --git a/cbor/lib/tinycbor/src/cborvalidation.c b/src/cbor/lib/tinycbor/src/cborvalidation.c similarity index 100% rename from cbor/lib/tinycbor/src/cborvalidation.c rename to src/cbor/lib/tinycbor/src/cborvalidation.c diff --git a/cbor/lib/tinycbor/src/compilersupport_p.h b/src/cbor/lib/tinycbor/src/compilersupport_p.h similarity index 100% rename from cbor/lib/tinycbor/src/compilersupport_p.h rename to src/cbor/lib/tinycbor/src/compilersupport_p.h diff --git a/cbor/lib/tinycbor/src/open_memstream.c b/src/cbor/lib/tinycbor/src/open_memstream.c similarity index 100% rename from cbor/lib/tinycbor/src/open_memstream.c rename to src/cbor/lib/tinycbor/src/open_memstream.c diff --git a/cbor/lib/tinycbor/src/tags.txt b/src/cbor/lib/tinycbor/src/tags.txt similarity index 100% rename from cbor/lib/tinycbor/src/tags.txt rename to src/cbor/lib/tinycbor/src/tags.txt diff --git a/cbor/lib/tinycbor/src/tinycbor-version.h b/src/cbor/lib/tinycbor/src/tinycbor-version.h similarity index 100% rename from cbor/lib/tinycbor/src/tinycbor-version.h rename to src/cbor/lib/tinycbor/src/tinycbor-version.h diff --git a/cbor/lib/tinycbor/src/utf8_p.h b/src/cbor/lib/tinycbor/src/utf8_p.h similarity index 100% rename from cbor/lib/tinycbor/src/utf8_p.h rename to src/cbor/lib/tinycbor/src/utf8_p.h diff --git a/cbor/types/CloudBool.h b/src/cbor/types/CloudBool.h similarity index 100% rename from cbor/types/CloudBool.h rename to src/cbor/types/CloudBool.h diff --git a/cbor/types/CloudColor.h b/src/cbor/types/CloudColor.h similarity index 100% rename from cbor/types/CloudColor.h rename to src/cbor/types/CloudColor.h diff --git a/cbor/types/CloudFloat.h b/src/cbor/types/CloudFloat.h similarity index 100% rename from cbor/types/CloudFloat.h rename to src/cbor/types/CloudFloat.h diff --git a/cbor/types/CloudInt.h b/src/cbor/types/CloudInt.h similarity index 100% rename from cbor/types/CloudInt.h rename to src/cbor/types/CloudInt.h diff --git a/cbor/types/CloudLocation.h b/src/cbor/types/CloudLocation.h similarity index 100% rename from cbor/types/CloudLocation.h rename to src/cbor/types/CloudLocation.h diff --git a/cbor/types/CloudString.h b/src/cbor/types/CloudString.h similarity index 100% rename from cbor/types/CloudString.h rename to src/cbor/types/CloudString.h diff --git a/cbor/types/CloudWrapperBase.h b/src/cbor/types/CloudWrapperBase.h similarity index 100% rename from cbor/types/CloudWrapperBase.h rename to src/cbor/types/CloudWrapperBase.h diff --git a/cbor/types/CloudWrapperBool.h b/src/cbor/types/CloudWrapperBool.h similarity index 100% rename from cbor/types/CloudWrapperBool.h rename to src/cbor/types/CloudWrapperBool.h diff --git a/cbor/types/CloudWrapperFloat.h b/src/cbor/types/CloudWrapperFloat.h similarity index 100% rename from cbor/types/CloudWrapperFloat.h rename to src/cbor/types/CloudWrapperFloat.h diff --git a/cbor/types/CloudWrapperInt.h b/src/cbor/types/CloudWrapperInt.h similarity index 100% rename from cbor/types/CloudWrapperInt.h rename to src/cbor/types/CloudWrapperInt.h diff --git a/cbor/types/CloudWrapperString.h b/src/cbor/types/CloudWrapperString.h similarity index 100% rename from cbor/types/CloudWrapperString.h rename to src/cbor/types/CloudWrapperString.h diff --git a/cbor/types/automation/CloudColoredLight.h b/src/cbor/types/automation/CloudColoredLight.h similarity index 100% rename from cbor/types/automation/CloudColoredLight.h rename to src/cbor/types/automation/CloudColoredLight.h diff --git a/cbor/types/automation/CloudContactSensor.h b/src/cbor/types/automation/CloudContactSensor.h similarity index 100% rename from cbor/types/automation/CloudContactSensor.h rename to src/cbor/types/automation/CloudContactSensor.h diff --git a/cbor/types/automation/CloudDimmedLight.h b/src/cbor/types/automation/CloudDimmedLight.h similarity index 100% rename from cbor/types/automation/CloudDimmedLight.h rename to src/cbor/types/automation/CloudDimmedLight.h diff --git a/cbor/types/automation/CloudLight.h b/src/cbor/types/automation/CloudLight.h similarity index 100% rename from cbor/types/automation/CloudLight.h rename to src/cbor/types/automation/CloudLight.h diff --git a/cbor/types/automation/CloudMotionSensor.h b/src/cbor/types/automation/CloudMotionSensor.h similarity index 100% rename from cbor/types/automation/CloudMotionSensor.h rename to src/cbor/types/automation/CloudMotionSensor.h diff --git a/cbor/types/automation/CloudSmartPlug.h b/src/cbor/types/automation/CloudSmartPlug.h similarity index 100% rename from cbor/types/automation/CloudSmartPlug.h rename to src/cbor/types/automation/CloudSmartPlug.h diff --git a/cbor/types/automation/CloudSwitch.h b/src/cbor/types/automation/CloudSwitch.h similarity index 100% rename from cbor/types/automation/CloudSwitch.h rename to src/cbor/types/automation/CloudSwitch.h diff --git a/cbor/types/automation/CloudTelevision.h b/src/cbor/types/automation/CloudTelevision.h similarity index 100% rename from cbor/types/automation/CloudTelevision.h rename to src/cbor/types/automation/CloudTelevision.h diff --git a/cbor/types/automation/CloudTemperature.h b/src/cbor/types/automation/CloudTemperature.h similarity index 100% rename from cbor/types/automation/CloudTemperature.h rename to src/cbor/types/automation/CloudTemperature.h From c9edb046c25374b05ffc637724efd87dda807e5d Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 3 Jun 2020 15:16:03 +0200 Subject: [PATCH 165/175] Remove ArduinoCloudThing from the list of dependencies --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 181336ec6..716cb1be1 100644 --- a/library.properties +++ b/library.properties @@ -8,4 +8,4 @@ category=Communication url=https://github.com/arduino-libraries/ArduinoIoTCloud architectures=samd,esp8266 includes=ArduinoIoTCloud.h -depends=Arduino_ConnectionHandler,Arduino_DebugUtils,ArduinoCloudThing,ArduinoMqttClient,ArduinoBearSSL,ArduinoECCX08,RTCZero +depends=Arduino_ConnectionHandler,Arduino_DebugUtils,ArduinoMqttClient,ArduinoBearSSL,ArduinoECCX08,RTCZero From faf2b7e6c06fffc3ea2a47d64363e3d08da9c190 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 3 Jun 2020 15:18:21 +0200 Subject: [PATCH 166/175] Remove ArduinoCloudThing from the list of automatically installed libraries when running CI via GitHub actions --- .github/workflows/compile-examples.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index 26aab9b33..7890c419b 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -18,7 +18,7 @@ jobs: env: # libraries to install for all boards - UNIVERSAL_LIBRARIES: '"ArduinoCloudThing" "Arduino_ConnectionHandler" "Arduino_DebugUtils" "ArduinoMqttClient"' + UNIVERSAL_LIBRARIES: '"Arduino_ConnectionHandler" "Arduino_DebugUtils" "ArduinoMqttClient"' # sketch paths to compile (recursive) for all boards UNIVERSAL_SKETCH_PATHS: '"examples/ArduinoIoTCloud-Advanced" "examples/ArduinoIoTCloud-Basic" "examples/utility/ArduinoIoTCloud_Travis_CI"' From 4c2c5d5ff5ea789e6b0964a4309e94a895bea33a Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 3 Jun 2020 15:24:38 +0200 Subject: [PATCH 167/175] Replacing absolute with relative inclusions which are necessary now that ArduinoCloudThing is no longer a stand-alone library but integrated with ArduinoIoTCloud --- src/ArduinoIoTCloud.h | 11 ++++++----- src/cbor/ArduinoCloudThing.cpp | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/ArduinoIoTCloud.h b/src/ArduinoIoTCloud.h index 23234986c..7933b167c 100644 --- a/src/ArduinoIoTCloud.h +++ b/src/ArduinoIoTCloud.h @@ -24,14 +24,15 @@ #include -#include #include #include -#include "types/CloudWrapperBool.h" -#include "types/CloudWrapperFloat.h" -#include "types/CloudWrapperInt.h" -#include "types/CloudWrapperString.h" +#include "cbor/ArduinoCloudThing.h" + +#include "cbor/types/CloudWrapperBool.h" +#include "cbor/types/CloudWrapperFloat.h" +#include "cbor/types/CloudWrapperInt.h" +#include "cbor/types/CloudWrapperString.h" #include "CloudSerial.h" diff --git a/src/cbor/ArduinoCloudThing.cpp b/src/cbor/ArduinoCloudThing.cpp index ec3830c9e..e52b7e097 100644 --- a/src/cbor/ArduinoCloudThing.cpp +++ b/src/cbor/ArduinoCloudThing.cpp @@ -21,7 +21,7 @@ #include -#include +#include "ArduinoCloudThing.h" /****************************************************************************** DEBUG FUNCTIONS From b216a9c82e2ccd9f019335626fe43818cef49f95 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 3 Jun 2020 15:43:12 +0200 Subject: [PATCH 168/175] Adding latest test code of ArduinoCloudThing --- extras/test/CMakeLists.txt | 32 + extras/test/include/Arduino.h | 27 + extras/test/include/TestUtil.h | 23 + extras/test/src/Arduino.cpp | 27 + extras/test/src/TestUtil.cpp | 40 ++ extras/test/src/test_CloudColor.cpp | 123 ++++ extras/test/src/test_CloudLocation.cpp | 102 +++ extras/test/src/test_addPropertyReal.cpp | 78 +++ extras/test/src/test_callback.cpp | 367 ++++++++++ extras/test/src/test_decode.cpp | 651 ++++++++++++++++++ extras/test/src/test_encode.cpp | 402 +++++++++++ extras/test/src/test_publishEvery.cpp | 63 ++ extras/test/src/test_publishOnChange.cpp | 50 ++ .../src/test_publishOnChangeRateLimit.cpp | 68 ++ extras/test/src/test_readOnly.cpp | 37 + extras/test/src/test_writeOnly.cpp | 30 + src/cbor/ArduinoCloudProperty.h | 2 +- src/cbor/lib/tinycbor/src/cbor.h | 32 +- 18 files changed, 2137 insertions(+), 17 deletions(-) create mode 100644 extras/test/include/Arduino.h create mode 100644 extras/test/include/TestUtil.h create mode 100644 extras/test/src/Arduino.cpp create mode 100644 extras/test/src/TestUtil.cpp create mode 100644 extras/test/src/test_CloudColor.cpp create mode 100644 extras/test/src/test_CloudLocation.cpp create mode 100644 extras/test/src/test_addPropertyReal.cpp create mode 100644 extras/test/src/test_callback.cpp create mode 100644 extras/test/src/test_decode.cpp create mode 100644 extras/test/src/test_encode.cpp create mode 100644 extras/test/src/test_publishEvery.cpp create mode 100644 extras/test/src/test_publishOnChange.cpp create mode 100644 extras/test/src/test_publishOnChangeRateLimit.cpp create mode 100644 extras/test/src/test_readOnly.cpp create mode 100644 extras/test/src/test_writeOnly.cpp diff --git a/extras/test/CMakeLists.txt b/extras/test/CMakeLists.txt index 1dbc7103c..9ea08195f 100644 --- a/extras/test/CMakeLists.txt +++ b/extras/test/CMakeLists.txt @@ -9,6 +9,7 @@ project(testArduinoIoTCloud) ########################################################################## include_directories(include) +include_directories(../../src/cbor) include_directories(../../src/utility/ota) include_directories(external/catch/v2.12.1/include) include_directories(external/fakeit/v2.0.5/include) @@ -24,11 +25,42 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(TEST_TARGET testArduinoIoTCloud) set(TEST_SRCS + src/Arduino.cpp + src/test_main.cpp + src/test_OTALogic.cpp + + src/test_addPropertyReal.cpp + src/test_callback.cpp + src/test_CloudColor.cpp + src/test_CloudLocation.cpp + src/test_decode.cpp + src/test_encode.cpp + src/test_publishEvery.cpp + src/test_publishOnChange.cpp + src/test_publishOnChangeRateLimit.cpp + src/test_readOnly.cpp + src/test_writeOnly.cpp + src/OTATestData.cpp + src/TestUtil.cpp + ../../src/utility/ota/crc.cpp ../../src/utility/ota/OTALogic.cpp + + ../../src/cbor/ArduinoCloudThing.cpp + ../../src/cbor/ArduinoCloudProperty.cpp + ../../src/cbor/lib/tinycbor/src/cborencoder.c + ../../src/cbor/lib/tinycbor/src/cborencoder_close_container_checked.c + ../../src/cbor/lib/tinycbor/src/cborerrorstrings.c + ../../src/cbor/lib/tinycbor/src/cborparser.c + ../../src/cbor/lib/tinycbor/src/cborparser_dup_string.c + ../../src/cbor/lib/tinycbor/src/cborpretty.c + ../../src/cbor/lib/tinycbor/src/cborpretty_stdio.c + ../../src/cbor/lib/tinycbor/src/cbortojson.c + ../../src/cbor/lib/tinycbor/src/cborvalidation.c + ../../src/cbor/lib/tinycbor/src/open_memstream.c ) ########################################################################## diff --git a/extras/test/include/Arduino.h b/extras/test/include/Arduino.h new file mode 100644 index 000000000..6e66167a3 --- /dev/null +++ b/extras/test/include/Arduino.h @@ -0,0 +1,27 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +#ifndef TEST_ARDUINO_H_ +#define TEST_ARDUINO_H_ + +/****************************************************************************** + INCLUDE + ******************************************************************************/ + +#include + +/****************************************************************************** + TYPEDEF + ******************************************************************************/ + +typedef std::string String; + +/****************************************************************************** + FUNCTION PROTOTYPES + ******************************************************************************/ + +void set_millis(unsigned long const millis); +unsigned long millis(); + +#endif /* TEST_ARDUINO_H_ */ diff --git a/extras/test/include/TestUtil.h b/extras/test/include/TestUtil.h new file mode 100644 index 000000000..ea5e5883d --- /dev/null +++ b/extras/test/include/TestUtil.h @@ -0,0 +1,23 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +#ifndef INCLUDE_TESTUTIL_H_ +#define INCLUDE_TESTUTIL_H_ + +/************************************************************************************** + INCLUDE + **************************************************************************************/ + +#include + +#include + +/************************************************************************************** + PROTOTYPES + **************************************************************************************/ + +std::vector encode(ArduinoCloudThing & thing, bool lightPayload = false); +void print(std::vector const & vect); + +#endif /* INCLUDE_TESTUTIL_H_ */ diff --git a/extras/test/src/Arduino.cpp b/extras/test/src/Arduino.cpp new file mode 100644 index 000000000..9287ff399 --- /dev/null +++ b/extras/test/src/Arduino.cpp @@ -0,0 +1,27 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +/****************************************************************************** + INCLUDE + ******************************************************************************/ + +#include + +/****************************************************************************** + GLOBAL VARIABLES + ******************************************************************************/ + +static unsigned long current_millis = 0; + +/****************************************************************************** + PUBLIC FUNCTIONS + ******************************************************************************/ + +void set_millis(unsigned long const millis) { + current_millis = millis; +} + +unsigned long millis() { + return current_millis; +} diff --git a/extras/test/src/TestUtil.cpp b/extras/test/src/TestUtil.cpp new file mode 100644 index 000000000..b37238803 --- /dev/null +++ b/extras/test/src/TestUtil.cpp @@ -0,0 +1,40 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +/************************************************************************************** + INCLUDE + **************************************************************************************/ + +#include + +#include +#include + +/************************************************************************************** + PUBLIC FUNCTIONS + **************************************************************************************/ + +std::vector encode(ArduinoCloudThing & thing, bool lightPayload) { + uint8_t buf[200] = {0}; + int const bytes_buf = thing.encode(buf, 200, lightPayload); + if (bytes_buf == -1) { + return std::vector(); + } else { + return std::vector(buf, buf + bytes_buf); + } +} + +void print(std::vector const & vect) { + for (auto i = vect.begin(); i != vect.end(); i++) { + std::cout << std::uppercase + << std::hex + << std::setw(2) + << std::setfill('0') + << static_cast(*i) + << std::dec + << std::nouppercase + << " "; + } + std::cout << std::endl; +} diff --git a/extras/test/src/test_CloudColor.cpp b/extras/test/src/test_CloudColor.cpp new file mode 100644 index 000000000..f2538a4b3 --- /dev/null +++ b/extras/test/src/test_CloudColor.cpp @@ -0,0 +1,123 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +/************************************************************************************** + INCLUDE + **************************************************************************************/ + +#include + +#include +#include + +/************************************************************************************** + TEST CODE + **************************************************************************************/ + +/************************************************************************************/ +SCENARIO("Arduino Cloud Properties ", "[ArduinoCloudThing::CloudColor]") { + WHEN("Set invalid color HSB") { + GIVEN("CloudProtocol::V2") { + + + CloudColor color_test = CloudColor(0.0, 0.0, 0.0); + + Color value_color_test = color_test.getValue(); + REQUIRE(value_color_test.setColorHSB(500.0, 20.0, 30.0) == false); + + } + } + + WHEN("Set and Get different RGB colors") { + GIVEN("CloudProtocol::V2") { + + uint8_t r, g, b; + + CloudColor color_test = CloudColor(0.0, 0.0, 0.0); + + Color value_color_test = color_test.getValue(); + + value_color_test.setColorRGB(128, 64, 64); + value_color_test.getRGB(r, g, b); + + REQUIRE(r == 128); + REQUIRE(g == 64); + REQUIRE(b == 64); + + value_color_test.setColorRGB(126, 128, 64); + value_color_test.getRGB(r, g, b); + + REQUIRE(r == 126); + REQUIRE(g == 128); + REQUIRE(b == 64); + + value_color_test.setColorRGB(64, 128, 64); + value_color_test.getRGB(r, g, b); + + REQUIRE(r == 64); + REQUIRE(g == 128); + REQUIRE(b == 64); + + value_color_test.setColorRGB(64, 64, 128); + value_color_test.getRGB(r, g, b); + + REQUIRE(r == 64); + REQUIRE(g == 64); + REQUIRE(b == 128); + + value_color_test.setColorRGB(255, 0, 255); + value_color_test.getRGB(r, g, b); + + REQUIRE(r == 255); + REQUIRE(g == 0); + REQUIRE(b == 255); + + value_color_test.setColorRGB(0, 0, 0); + value_color_test.getRGB(r, g, b); + + REQUIRE(r == 0); + REQUIRE(g == 0); + REQUIRE(b == 0); + + value_color_test.setColorRGB(50, 100, 20); + value_color_test.getRGB(r, g, b); + + REQUIRE(r == 50); + REQUIRE(g == 100); + REQUIRE(b == 20); + + value_color_test.setColorRGB(20, 50, 70); + value_color_test.getRGB(r, g, b); + + REQUIRE(r == 20); + REQUIRE(g == 50); + REQUIRE(b == 70); + + } + } + + WHEN("Set HSB colors and get RGB") { + GIVEN("CloudProtocol::V2") { + bool verify; + uint8_t r, g, b; + + CloudColor color_test = CloudColor(0.0, 0.0, 0.0); + + Color value_color_test = color_test.getValue(); + + value_color_test.setColorHSB(240, 50, 50); + value_color_test.getRGB(r, g, b); + verify = r == 64 && g == 64 && b == 128; + + REQUIRE(verify); + + value_color_test.setColorHSB(120, 50, 50); + value_color_test.getRGB(r, g, b); + verify = r == 64 && g == 128 && b == 64; + + REQUIRE(verify); + + } + } +} \ No newline at end of file diff --git a/extras/test/src/test_CloudLocation.cpp b/extras/test/src/test_CloudLocation.cpp new file mode 100644 index 000000000..63c71fd88 --- /dev/null +++ b/extras/test/src/test_CloudLocation.cpp @@ -0,0 +1,102 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +/************************************************************************************** + INCLUDE + **************************************************************************************/ + +#include + +#include + +/************************************************************************************** + TEST CODE + **************************************************************************************/ + +SCENARIO("Tesing cloud type 'Location' Ctor", "[Location::Location]") { + WHEN("A Location(1.0f, 2.0f) is being instantiated") { + Location loc(1.0f, 2.0f); + THEN("The member variable 'lat' should be 1.0f") { + REQUIRE(loc.lat == 1.0f); + } + THEN("The member variable 'lon' should be 2.0f") { + REQUIRE(loc.lon == 2.0f); + } + } +} + +/**************************************************************************************/ + +SCENARIO("Tesing cloud type 'Location' assignment operator", "[Location::operator =]") { + Location loc1(1.0f, 2.0f), + loc2(3.0f, 4.0f); + loc1 = loc2; + WHEN("One location is assigned to the other") { + THEN("The coordinates of the second location should be assigned to the first") { + REQUIRE(loc1.lat == 3.0f); + REQUIRE(loc1.lon == 4.0f); + } + } +} + +/**************************************************************************************/ + +SCENARIO("Tesing cloud type 'Location' operator -", "[Location::operator -]") { + Location loc1(1.0f, 2.0f), + loc2(3.0f, 4.0f); + Location loc3 = loc1 - loc2; + WHEN("One location is subtracted from the other") { + THEN("The result should be calculated according the rule lon3 = lon1 - lon2, lat3 = lat1 - lat2") { + REQUIRE(loc3.lat == loc1.lat - loc2.lat); + REQUIRE(loc3.lon == loc1.lon - loc2.lon); + } + } +} + +/**************************************************************************************/ + +SCENARIO("Tesing cloud type 'Location' comparison operator ==", "[Location::operator ==]") { + Location loc1(1.0f, 2.0f), + loc2(3.0f, 4.0f), + loc3(1.0f, 2.0f); + WHEN("Two locations are identical (lat as well as lon)") { + THEN("The comparison operation should return true") { + REQUIRE((loc1 == loc3) == true); + } + } + + WHEN("Two locations are not identical (either lat or lon do not match)") { + THEN("The comparison operation should return false") { + REQUIRE((loc1 == loc2) == false); + } + } +} + +/**************************************************************************************/ + +SCENARIO("Tesing cloud type 'Location' comparison operator !=", "[Location::operator !=]") { + Location loc1(1.0f, 2.0f), + loc2(3.0f, 4.0f), + loc3(1.0f, 2.0f); + WHEN("Two locations are identical (lat as well as lon)") { + THEN("The comparison operation should return false") { + REQUIRE((loc1 != loc3) == false); + } + } + + WHEN("Two locations are not identical (either lat or lon do not match)") { + THEN("The comparison operation should return true") { + REQUIRE((loc1 != loc2) == true); + } + } +} + +/**************************************************************************************/ + +SCENARIO("Tesing cloud type 'Location' function distance for calculating euclidean 2d distance between two points", "[Location::distance]") { + Location loc1(0.0f, 0.0f), + loc2(1.0f, 1.0f); + + REQUIRE(Location::distance(loc1, loc2) == sqrt(2.0f)); +} \ No newline at end of file diff --git a/extras/test/src/test_addPropertyReal.cpp b/extras/test/src/test_addPropertyReal.cpp new file mode 100644 index 000000000..6d60f8982 --- /dev/null +++ b/extras/test/src/test_addPropertyReal.cpp @@ -0,0 +1,78 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +/************************************************************************************** + INCLUDE + **************************************************************************************/ + +#include + +#include + +/************************************************************************************** + TEST CODE + **************************************************************************************/ + +SCENARIO("The same arduino cloud properties are added multiple times", "[ArduinoCloudThing::addPropertyReal]") { + WHEN("The same bool property is added multiple times") { + ArduinoCloudThing thing; + thing.begin(); + + CloudBool bool_property = false; + + ArduinoCloudProperty * bool_property_ptr_1 = &thing.addPropertyReal(bool_property, "bool_property", Permission::ReadWrite); + ArduinoCloudProperty * bool_property_ptr_2 = &thing.addPropertyReal(bool_property, "bool_property", Permission::ReadWrite); + THEN("No new property is added and the first added property is returned instead of a new one") { + REQUIRE(bool_property_ptr_1 == bool_property_ptr_2); + } + } + + /**************************************************************************************/ + + WHEN("the same int property is added multiple times") { + ArduinoCloudThing thing; + thing.begin(); + + CloudInt int_property = 1; + + ArduinoCloudProperty * int_property_ptr_1 = &thing.addPropertyReal(int_property, "int_property", Permission::ReadWrite); + ArduinoCloudProperty * int_property_ptr_2 = &thing.addPropertyReal(int_property, "int_property", Permission::ReadWrite); + + THEN("No new property is added and the first added property is returned instead of a new one") { + REQUIRE(int_property_ptr_1 == int_property_ptr_2); + } + } + + /**************************************************************************************/ + + WHEN("the same float property is added multiple times") { + ArduinoCloudThing thing; + thing.begin(); + + CloudFloat float_property = 1.0f; + + ArduinoCloudProperty * float_property_ptr_1 = &thing.addPropertyReal(float_property, "float_property", Permission::ReadWrite); + ArduinoCloudProperty * float_property_ptr_2 = &thing.addPropertyReal(float_property, "float_property", Permission::ReadWrite); + + THEN("No new property is added and the first added property is returned instead of a new one") { + REQUIRE(float_property_ptr_1 == float_property_ptr_2); + } + } + + /**************************************************************************************/ + + WHEN("the same String property is added multiple times") { + ArduinoCloudThing thing; + thing.begin(); + + CloudString str_property; + + ArduinoCloudProperty * str_property_ptr_1 = &thing.addPropertyReal(str_property, "str_property", Permission::ReadWrite); + ArduinoCloudProperty * str_property_ptr_2 = &thing.addPropertyReal(str_property, "str_property", Permission::ReadWrite); + + THEN("No new property is added and the first added property is returned instead of a new one") { + REQUIRE(str_property_ptr_1 == str_property_ptr_2); + } + } +} \ No newline at end of file diff --git a/extras/test/src/test_callback.cpp b/extras/test/src/test_callback.cpp new file mode 100644 index 000000000..3a5270b26 --- /dev/null +++ b/extras/test/src/test_callback.cpp @@ -0,0 +1,367 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +/************************************************************************************** + INCLUDE + **************************************************************************************/ + +#include + +#include +#include +#include "types/CloudWrapperBool.h" + +/************************************************************************************** + GLOBAL CONSTANTS + **************************************************************************************/ + +static bool callback_called_protocol_v1 = false; +static bool callback_called_protocol_v2 = false; + +/************************************************************************************** + TEST HELPER FUNCTIONS + **************************************************************************************/ + +void externalCallbackV1() { + callback_called_protocol_v1 = true; +} + +void externalCallbackV2() { + callback_called_protocol_v2 = true; +} + +/************************************************************************************** + TEST CODE + **************************************************************************************/ + +SCENARIO("A callback is registered via 'onUpdate' to be called on property change", "[ArduinoCloudThing::decode]") { + /************************************************************************************/ + + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudInt test = 10; + thing.addPropertyReal(test, "test", Permission::ReadWrite).onUpdate(externalCallbackV2); + + /* [{0: "test", 2: 7}] = 81 A2 00 64 74 65 73 74 02 07 */ + uint8_t const payload[] = {0x81, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0x07}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length); + + REQUIRE(callback_called_protocol_v2 == true); + } + + /************************************************************************************/ +} + +/**************************************************************************************/ + +static CloudBool switch_turned_on = false; +static CloudBool switch_callback_called = false; + +void switch_callback() { + switch_turned_on = false; + switch_callback_called = true; +} + +SCENARIO("A (boolean) property is manipulated in the callback to its origin state", "[ArduinoCloudThing::decode]") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + thing.addPropertyReal(switch_turned_on, "switch_turned_on", Permission::ReadWrite).onUpdate(switch_callback); + + /* [{0: "switch_turned_on", 4: true}] = 81 A2 00 70 73 77 69 74 63 68 5F 74 75 72 6E 65 64 5F 6F 6E 04 F5 */ + uint8_t const payload[] = {0x81, 0xA2, 0x00, 0x70, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x5F, 0x74, 0x75, 0x72, 0x6E, 0x65, 0x64, 0x5F, 0x6F, 0x6E, 0x04, 0xF5}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length); + + REQUIRE(switch_callback_called == true); + + /* Since the property was reset to its origin state in the callback we + expect that on the next call to encode this change is propagated to + the cloud. + */ + + /* [{0: "switch_turned_on", 4: false}] = 9F A2 00 70 73 77 69 74 63 68 5F 74 75 72 6E 65 64 5F 6F 6E 04 F4 FF*/ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x70, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x5F, 0x74, 0x75, 0x72, 0x6E, 0x65, 0x64, 0x5F, 0x6F, 0x6E, 0x04, 0xF4, 0xFF}; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } +} + +/**************************************************************************************/ + +static bool sync_callback_called = false; +static bool change_callback_called = false; + +void auto_sync_callback(ArduinoCloudProperty& property) { + MOST_RECENT_WINS(property); + sync_callback_called = true; +} + +void change_callback() { + change_callback_called = true; +} + +SCENARIO("After a connection/reconnection an incoming cbor payload is processed and the synchronization callback is executed. The sync callback applies the AUTO_SYNC policy (the most recent value between the local one and the cloud one is finally assigned to the property). The onUpdate function is called if the cloud value is the most recent one. In this scenario the most updated value is the cloud one.") { + GIVEN("CloudProtocol::V2") { + CloudBool test = false; + sync_callback_called = false; + change_callback_called = false; + + ArduinoCloudThing thing; + thing.begin(); + + thing.addPropertyReal(test, "test", Permission::ReadWrite).onUpdate(change_callback).onSync(auto_sync_callback); + + test.setLastLocalChangeTimestamp(1550138809); + + /* [{-3: 1550138810.00, 0: "test", 4: true}] = 81 A3 22 FB 41 D7 19 4F 6E 80 00 00 00 64 74 65 73 74 04 F5 */ + uint8_t const payload[] = {0x81, 0xA3, 0x22, 0xFB, 0x41, 0xD7, 0x19, 0x4F, 0x6E, 0x80, 0x00, 0x00, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length, true); + + REQUIRE(sync_callback_called == true); + REQUIRE(change_callback_called == true); + + REQUIRE(test == true); + } +} + +/**************************************************************************************/ + +SCENARIO("After a connection/reconnection an incoming cbor payload is processed and the synchronization callback is executed. The sync callback apply the AUTO_SYNC policy (the most recent value between the local one and the cloud one is finally assigned to the property). The onUpdate function is called if the cloud value is the most recent one. In this scenario the most updated value is the local one.") { + GIVEN("CloudProtocol::V2") { + CloudBool test = true; + sync_callback_called = false; + change_callback_called = false; + + ArduinoCloudThing thing; + thing.begin(); + + thing.addPropertyReal(test, "test", Permission::ReadWrite).onUpdate(change_callback).onSync(auto_sync_callback); + test = false; + test.setLastLocalChangeTimestamp(1550138811); + + /* [{-3: 1550138810.00, 0: "test", 4: true}] = 81 A3 22 FB 41 D7 19 4F 6E 80 00 00 00 64 74 65 73 74 04 F5 */ + uint8_t const payload[] = {0x81, 0xA3, 0x22, 0xFB, 0x41, 0xD7, 0x19, 0x4F, 0x6E, 0x80, 0x00, 0x00, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length, true); + + REQUIRE(sync_callback_called == true); + REQUIRE(change_callback_called == false); + + REQUIRE(test == false); + } +} + +SCENARIO("Primitive property: After a connection/reconnection an incoming cbor payload is processed and the synchronization callback is executed. The sync callback applies the AUTO_SYNC policy (the most recent value between the local one and the cloud one is finally assigned to the property). The onUpdate function is called if the cloud value is the most recent one. In this scenario the most updated value is the cloud one.") { + GIVEN("CloudProtocol::V2") { + bool test = true; + ArduinoCloudProperty *p = new CloudWrapperBool(test); + sync_callback_called = false; + change_callback_called = false; + + ArduinoCloudThing thing; + thing.begin(); + + thing.addPropertyReal(*p, "test", Permission::ReadWrite).onUpdate(change_callback).onSync(auto_sync_callback); + test = false; + thing.updateTimestampOnLocallyChangedProperties(); + //There is no RTC on test execution environment so we force the local timestamp + p->setLastLocalChangeTimestamp(1550138809); + + /* [{-3: 1550138810.00, 0: "test", 4: true}] = 81 A3 22 FB 41 D7 19 4F 6E 80 00 00 00 64 74 65 73 74 04 F5 */ + uint8_t const payload[] = {0x81, 0xA3, 0x22, 0xFB, 0x41, 0xD7, 0x19, 0x4F, 0x6E, 0x80, 0x00, 0x00, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length, true); + + REQUIRE(sync_callback_called == true); + REQUIRE(change_callback_called == true); + + REQUIRE(test == true); + } +} + +/**************************************************************************************/ + +SCENARIO("Primitive property: After a connection/reconnection an incoming cbor payload is processed and the synchronization callback is executed. The sync callback apply the AUTO_SYNC policy (the most recent value between the local one and the cloud one is finally assigned to the property). The onUpdate function is called if the cloud value is the most recent one. In this scenario the most updated value is the local one.") { + GIVEN("CloudProtocol::V2") { + bool test = true; + ArduinoCloudProperty *p = new CloudWrapperBool(test); + sync_callback_called = false; + change_callback_called = false; + + ArduinoCloudThing thing; + thing.begin(); + + thing.addPropertyReal(*p, "test", Permission::ReadWrite).onUpdate(change_callback).onSync(auto_sync_callback); + test = false; + thing.updateTimestampOnLocallyChangedProperties(); + //There is no RTC on test execution environment so we force the local timestamp + p->setLastLocalChangeTimestamp(1550138811); + + /* [{-3: 1550138810.00, 0: "test", 4: true}] = 81 A3 22 FB 41 D7 19 4F 6E 80 00 00 00 64 74 65 73 74 04 F5 */ + uint8_t const payload[] = {0x81, 0xA3, 0x22, 0xFB, 0x41, 0xD7, 0x19, 0x4F, 0x6E, 0x80, 0x00, 0x00, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length, true); + + REQUIRE(sync_callback_called == true); + REQUIRE(change_callback_called == false); + + REQUIRE(test == false); + } +} + +SCENARIO("Object property: After a connection/reconnection an incoming cbor payload is processed and the synchronization callback is executed. The sync callback applies the AUTO_SYNC policy (the most recent value between the local one and the cloud one is finally assigned to the property). The onUpdate function is called if the cloud value is the most recent one. In this scenario the most updated value is the cloud one.") { + GIVEN("CloudProtocol::V2") { + CloudLocation location_test = CloudLocation(0, 1); + sync_callback_called = false; + change_callback_called = false; + + ArduinoCloudThing thing; + thing.begin(); + + thing.addPropertyReal(location_test, "test", Permission::ReadWrite).onUpdate(change_callback).onSync(auto_sync_callback); + location_test.setLastLocalChangeTimestamp(1550138809); + + /* [{-3: 1550138810.00, 0: "test:lat", 3: 2},{0: "test:lon", 3: 3}] = 82 A3 22 FB 41 D7 19 4F 6E 80 00 00 00 68 74 65 73 74 3A 6C 61 74 02 02 A2 00 68 74 65 73 74 3A 6C 6F 6E 02 03*/ + uint8_t const payload[] = { 0x82, 0xA3, 0x22, 0xFB, 0x41, 0xD7, 0x19, 0x4F, 0x6E, 0x80, 0x00, 0x00, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6C, 0x61, 0x74, 0x02, 0x02, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6C, 0x6F, 0x6E, 0x02, 0x03 }; + + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length, true); + + REQUIRE(sync_callback_called == true); + REQUIRE(change_callback_called == true); + + Location location_compare = Location(2, 3); + Location value_location_test = location_test.getValue(); + bool verify = (value_location_test == location_compare); + + REQUIRE(verify); + } +} + +/**************************************************************************************/ + +SCENARIO("Object property: After a connection/reconnection an incoming cbor payload is processed and the synchronization callback is executed. The sync callback apply the AUTO_SYNC policy (the most recent value between the local one and the cloud one is finally assigned to the property). The onUpdate function is called if the cloud value is the most recent one. In this scenario the most updated value is the local one.") { + GIVEN("CloudProtocol::V2") { + CloudLocation location_test = CloudLocation(0, 1); + sync_callback_called = false; + change_callback_called = false; + + ArduinoCloudThing thing; + thing.begin(); + + thing.addPropertyReal(location_test, "test", Permission::ReadWrite).onUpdate(change_callback).onSync(auto_sync_callback); + location_test.setLastLocalChangeTimestamp(1550138811); + + /* [{-3: 1550138810.00, 0: "test:lat", 3: 2},{0: "test:lon", 3: 3}] = 82 A3 22 FB 41 D7 19 4F 6E 80 00 00 00 68 74 65 73 74 3A 6C 61 74 02 02 A2 00 68 74 65 73 74 3A 6C 6F 6E 02 03*/ + uint8_t const payload[] = { 0x82, 0xA3, 0x22, 0xFB, 0x41, 0xD7, 0x19, 0x4F, 0x6E, 0x80, 0x00, 0x00, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6C, 0x61, 0x74, 0x02, 0x02, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6C, 0x6F, 0x6E, 0x02, 0x03 }; + + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length, true); + + REQUIRE(sync_callback_called == true); + REQUIRE(change_callback_called == false); + + Location location_compare = Location(0, 1); + Location value_location_test = location_test.getValue(); + bool verify = (value_location_test == location_compare); + + REQUIRE(verify); + } +} + +/**************************************************************************************/ + +void force_device_sync_callback(ArduinoCloudProperty& property) { + DEVICE_WINS(property); + sync_callback_called = true; +} + +SCENARIO("After a connection/reconnection an incoming cbor payload is processed and the synchronization callback is executed. The sync callback applies the FORCE_DEVICE_SYNC policy (the property keeps the local value and, if the cloud value is different from the local one, the value is propagated to the cloud). The onUpdate function is not executed") { + GIVEN("CloudProtocol::V2") { + + CloudBool test = false; + sync_callback_called = false; + change_callback_called = false; + + ArduinoCloudThing thing; + thing.begin(); + + thing.addPropertyReal(test, "test", Permission::ReadWrite).onUpdate(change_callback).onSync(force_device_sync_callback); + + /* [{-3: 1550138810.00, 0: "test", 4: true}] = 81 A3 22 FB 41 D7 19 4F 6E 80 00 00 00 64 74 65 73 74 04 F5 */ + uint8_t const payload[] = {0x81, 0xA3, 0x22, 0xFB, 0x41, 0xD7, 0x19, 0x4F, 0x6E, 0x80, 0x00, 0x00, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length, true); + + REQUIRE(sync_callback_called == true); + REQUIRE(change_callback_called == false); + + REQUIRE(test == false); + } +} + + +/**************************************************************************************/ + +void force_cloud_sync_callback(ArduinoCloudProperty& property) { + CLOUD_WINS(property); + sync_callback_called = true; +} + +SCENARIO("After a connection/reconnection an incoming cbor payload is processed and the synchronization callback is executed. The sync callback applies the FORCE_CLOUD_SYNC policy (the property always assumes the value incoming from the broker message). The onUpdate function is executed only if the local value of the property was different from the one taken from the incoming message") { + GIVEN("CloudProtocol::V2") { + + CloudBool test = false; + sync_callback_called = false; + change_callback_called = false; + + ArduinoCloudThing thing; + thing.begin(); + + thing.addPropertyReal(test, "test", Permission::ReadWrite).onUpdate(change_callback).onSync(force_cloud_sync_callback); + + /* [{-3: 1550138810.00, 0: "test", 4: true}] = 81 A3 22 FB 41 D7 19 4F 6E 80 00 00 00 64 74 65 73 74 04 F5 */ + uint8_t const payload[] = {0x81, 0xA3, 0x22, 0xFB, 0x41, 0xD7, 0x19, 0x4F, 0x6E, 0x80, 0x00, 0x00, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length, true); + + REQUIRE(sync_callback_called == true); + REQUIRE(change_callback_called == true); + + REQUIRE(test == true); + } +} + +/**************************************************************************************/ + +SCENARIO("After a connection/reconnection an incoming cbor payload is processed. Any synchronization function is passed to the property so the value in the incoming message is discarded") { + GIVEN("CloudProtocol::V2") { + + CloudBool test = false; + sync_callback_called = false; + change_callback_called = false; + + ArduinoCloudThing thing; + thing.begin(); + + thing.addPropertyReal(test, "test", Permission::ReadWrite).onUpdate(change_callback); + + /* [{-3: 1550138810.00, 0: "test", 4: true}] = 81 A3 22 FB 41 D7 19 4F 6E 80 00 00 00 64 74 65 73 74 04 F5 */ + uint8_t const payload[] = {0x81, 0xA3, 0x22, 0xFB, 0x41, 0xD7, 0x19, 0x4F, 0x6E, 0x80, 0x00, 0x00, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length, true); + + REQUIRE(sync_callback_called == false); + REQUIRE(change_callback_called == false); + + REQUIRE(test == false); + } +} diff --git a/extras/test/src/test_decode.cpp b/extras/test/src/test_decode.cpp new file mode 100644 index 000000000..5f521f926 --- /dev/null +++ b/extras/test/src/test_decode.cpp @@ -0,0 +1,651 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +/************************************************************************************** + INCLUDE + **************************************************************************************/ + +#include + +#include +#include +#include "types/CloudWrapperBool.h" +#include "types/CloudWrapperFloat.h" +#include "types/CloudWrapperInt.h" +#include "types/CloudWrapperString.h" +#include "types/automation/CloudColoredLight.h" +#include "types/automation/CloudContactSensor.h" +#include "types/automation/CloudDimmedLight.h" +#include "types/automation/CloudLight.h" +#include "types/automation/CloudMotionSensor.h" +#include "types/automation/CloudSmartPlug.h" +#include "types/automation/CloudSwitch.h" +#include "types/automation/CloudTemperature.h" +#include "types/automation/CloudTelevision.h" + +/************************************************************************************** + TEST CODE + **************************************************************************************/ + +SCENARIO("Arduino Cloud Properties are decoded", "[ArduinoCloudThing::decode]") { + /************************************************************************************/ + + WHEN("A boolean property is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudBool test = true; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{0: "test", 4: false}] = 81 A2 00 64 74 65 73 74 04 F4 */ + uint8_t const payload[] = {0x81, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF4}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length); + + REQUIRE(test == false); + } + } + + /************************************************************************************/ + + WHEN("A boolean property is changed via CBOR message - light payload") { + /*An integer identifier has been encoded instead of the name of the property in order to have a shorter payload*/ + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudBool test = true; + /*The property is added with identifier 1 that will be used instead of the string "test" as property identifier*/ + thing.addPropertyReal(test, "test", Permission::ReadWrite, 1); + + /* [{0: 1, 4: false}] = 81 A2 00 01 04 F4 */ + uint8_t const payload[] = {0x81, 0xA2, 0x00, 0x01, 0x04, 0xF4}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length); + + REQUIRE(test == false); + } + } + + /************************************************************************************/ + + WHEN("A int property is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudInt test = 0; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{0: "test", 2: 7}] = 81 A2 00 64 74 65 73 74 02 07 */ + uint8_t const payload[] = {0x81, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0x07}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length); + + REQUIRE(test == 7); + } + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudInt test = 0; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{0: "test", 2: -7}] = 81 A2 00 64 74 65 73 74 02 26 */ + uint8_t const payload[] = {0x81, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0x26}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length); + + REQUIRE(test == -7); + } + } + + /************************************************************************************/ + + WHEN("A float property is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudFloat test = 0.0f; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{0: "test", 2: 3.1459}] = 81 A2 00 64 74 65 73 74 02 FB 40 09 2A CD 9E 83 E4 26 */ + uint8_t const payload[] = {0x81, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0xFB, 0x40, 0x09, 0x2A, 0xCD, 0x9E, 0x83, 0xE4, 0x26}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length); + + REQUIRE(test == Approx(3.1459).epsilon(0.01)); + } + } + + /************************************************************************************/ + + WHEN("A String property is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudString str_test; + str_test = "test"; + thing.addPropertyReal(str_test, "test", Permission::ReadWrite); + + /* [{0: "test", 3: "testtt"}] = 81 A2 00 64 74 65 73 74 03 66 74 65 73 74 74 74 */ + uint8_t const payload[] = {0x81, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x03, 0x66, 0x74, 0x65, 0x73, 0x74, 0x74, 0x74}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + REQUIRE(str_test == "testtt"); + } + } + + /************************************************************************************/ + + WHEN("A Location property is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudLocation location_test = CloudLocation(0, 1); + thing.addPropertyReal(location_test, "test", Permission::ReadWrite); + + /* [{0: "test:lat", 3: 2},{0: "test:lon", 3: 3}] = 82 A2 00 68 74 65 73 74 3A 6C 61 74 02 02 A2 00 68 74 65 73 74 3A 6C 6F 6E 02 03*/ + uint8_t const payload[] = { 0x82, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6C, 0x61, 0x74, 0x02, 0x02, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6C, 0x6F, 0x6E, 0x02, 0x03 }; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + Location location_compare = Location(2, 3); + Location value_location_test = location_test.getValue(); + REQUIRE(value_location_test.lat == location_compare.lat); + REQUIRE(value_location_test.lon == location_compare.lon); + } + } + + /************************************************************************************/ + + WHEN("A Color property is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudColor color_test = CloudColor(0.0, 0.0, 0.0); + + thing.addPropertyReal(color_test, "test", Permission::ReadWrite); + + /* [{0: "test:hue", 2: 2.0},{0: "test:sat", 2: 2.0},{0: "test:bri", 2: 2.0}] = 83 A2 00 68 74 65 73 74 3A 68 75 65 02 FA 40 00 00 00 A2 00 68 74 65 73 74 3A 73 61 74 02 FA 40 00 00 00 A2 00 68 74 65 73 74 3A 62 72 69 02 FA 40 00 00 00 */ + uint8_t const payload[] = {0x83, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x68, 0x75, 0x65, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x61, 0x74, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x62, 0x72, 0x69, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00 }; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + Color color_compare = Color(2.0, 2.0, 2.0); + Color value_color_test = color_test.getValue(); + bool verify = (value_color_test == color_compare); + REQUIRE(verify); + REQUIRE(value_color_test.hue == color_compare.hue); + REQUIRE(value_color_test.sat == color_compare.sat); + REQUIRE(value_color_test.bri == color_compare.bri); + } + } + + /************************************************************************************/ + + WHEN("A Color property is changed via CBOR message - light payload") { + /*An integer identifier has been encoded instead of the name of the property in order to have a shorter payload*/ + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudColor color_test = CloudColor(0.0, 0.0, 0.0); + + /*The property is added with identifier 1 that will be used instead of the string "test" as property identifier*/ + thing.addPropertyReal(color_test, "test", Permission::ReadWrite, 1); + + /* [{0: 257, 2: 2.0},{0: 513, 2: 2.0},{0: 769, 2: 2.0}] = 83 A2 00 19 01 01 02 FA 40 00 00 00 A2 00 19 02 01 02 FA 40 00 00 00 A2 00 19 03 01 02 FA 40 00 00 00 */ + uint8_t const payload[] = {0x83, 0xA2, 0x00, 0x19, 0x01, 0x01, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x19, 0x02, 0x01, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x19, 0x03, 0x01, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00 }; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + Color color_compare = Color(2.0, 2.0, 2.0); + Color value_color_test = color_test.getValue(); + bool verify = (value_color_test == color_compare); + REQUIRE(verify); + REQUIRE(value_color_test.hue == color_compare.hue); + REQUIRE(value_color_test.sat == color_compare.sat); + REQUIRE(value_color_test.bri == color_compare.bri); + } + } + + /************************************************************************************/ + + WHEN("A ColoredLight property is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudColoredLight color_test = CloudColoredLight(false, 0.0, 0.0, 0.0); + + thing.addPropertyReal(color_test, "test", Permission::ReadWrite); + + /* [{0: "test:swi", 4: true},{0: "test:hue", 2: 2.0},{0: "test:sat", 2: 2.0},{0: "test:bri", 2: 2.0}] = 83 A2 00 68 74 65 73 74 3A 73 77 69 04 F5 //A2 00 68 74 65 73 74 3A 68 75 65 02 FA 40 00 00 00 A2 00 68 74 65 73 74 3A 73 61 74 02 FA 40 00 00 00 A2 00 68 74 65 73 74 3A 62 72 69 02 FA 40 00 00 00 */ + uint8_t const payload[] = {0x84, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x77, 0x69, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x68, 0x75, 0x65, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x61, 0x74, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x62, 0x72, 0x69, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00 }; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + ColoredLight color_compare = ColoredLight(true, 2.0, 2.0, 2.0); + ColoredLight value_color_test = color_test.getValue(); + bool verify = (value_color_test == color_compare); + REQUIRE(verify); + REQUIRE(value_color_test.swi == color_compare.swi); + REQUIRE(value_color_test.hue == color_compare.hue); + REQUIRE(value_color_test.sat == color_compare.sat); + REQUIRE(value_color_test.bri == color_compare.bri); + } + } + + /************************************************************************************/ + + WHEN("A Television property is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudTelevision tv_test = CloudTelevision(false, 0, false, PlaybackCommands::Stop, InputValue::AUX1, 0); + + + thing.addPropertyReal(tv_test, "test", Permission::ReadWrite); + + /* [{0: "test:swi", 4: true},{0: "test:vol", 2: 50},{0: "test:mut", 2: false},{0: "test:pbc", 2: 3},{0: "test:inp", 2: 55},{0: "test:cha", 2: 7}] = 86 A2 00 68 74 65 73 74 3A 73 77 69 04 F5 A2 00 68 74 65 73 74 3A 76 6F 6C 02 18 32 A2 00 68 74 65 73 74 3A 6D 75 74 02 F4 A2 00 68 74 65 73 74 3A 70 62 63 02 03 A2 00 68 74 65 73 74 3A 69 6E 70 02 18 37 A2 00 68 74 65 73 74 3A 63 68 61 02 07 */ + uint8_t const payload[] = {0x86, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x77, 0x69, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x76, 0x6F, 0x6C, 0x02, 0x18, 0x32, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6D, 0x75, 0x74, 0x02, 0xF4, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x70, 0x62, 0x63, 0x02, 0x03, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x69, 0x6E, 0x70, 0x02, 0x18, 0x37, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x63, 0x68, 0x61, 0x02, 0x07 }; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + Television tv_compare = Television(true, 50, false, PlaybackCommands::Play, InputValue::TV, 7); + Television value_tv_test = tv_test.getValue(); + bool verify = (value_tv_test == tv_compare); + REQUIRE(verify); + REQUIRE(value_tv_test.swi == tv_compare.swi); + REQUIRE(value_tv_test.vol == tv_compare.vol); + REQUIRE(value_tv_test.mut == tv_compare.mut); + REQUIRE(value_tv_test.pbc == tv_compare.pbc); + REQUIRE(value_tv_test.inp == tv_compare.inp); + REQUIRE(value_tv_test.cha == tv_compare.cha); + } + } + + /************************************************************************************/ + + WHEN("A DimmedLight property is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudDimmedLight light_test = CloudDimmedLight(false, 0.0); + + thing.addPropertyReal(light_test, "test", Permission::ReadWrite); + + /* [{0: "test:swi", 4: true},{0: "test:bri", 2: 2.0}] = 83 A2 00 68 74 65 73 74 3A 73 77 69 04 F5 //A2 00 68 74 65 73 74 3A 62 72 69 02 FA 40 00 00 00 */ + uint8_t const payload[] = {0x82, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x77, 0x69, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x62, 0x72, 0x69, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + DimmedLight light_compare = DimmedLight(true, 2.0); + DimmedLight value_light_test = light_test.getValue(); + bool verify = (value_light_test == light_compare); + REQUIRE(verify); + REQUIRE(value_light_test.swi == light_compare.swi); + REQUIRE(value_light_test.bri == light_compare.bri); + } + } + + /************************************************************************************/ + + WHEN("A Light property is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudLight light_test; + light_test = false; + + thing.addPropertyReal(light_test, "test", Permission::ReadWrite); + + /* [{0: "test", 4: true}] = 81 A2 00 64 74 65 73 74 04 F5 */ + uint8_t const payload[] = {0x81, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + REQUIRE(light_test == true); + } + } + + /************************************************************************************/ + + WHEN("A ContactSensor property is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudContactSensor contact_test; + contact_test = false; + + thing.addPropertyReal(contact_test, "test", Permission::ReadWrite); + + /* [{0: "test", 4: true}] = 81 A2 00 64 74 65 73 74 04 F5 */ + uint8_t const payload[] = {0x81, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + REQUIRE(contact_test == true); + } + } + + /************************************************************************************/ + + WHEN("A MotionSensor property is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudMotionSensor motion_test; + motion_test = false; + + thing.addPropertyReal(motion_test, "test", Permission::ReadWrite); + + /* [{0: "test", 4: true}] = 81 A2 00 64 74 65 73 74 04 F5 */ + uint8_t const payload[] = {0x81, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + REQUIRE(motion_test == true); + } + } + + /************************************************************************************/ + + WHEN("A SmartPlug property is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudSmartPlug plug_test; + plug_test = false; + + thing.addPropertyReal(plug_test, "test", Permission::ReadWrite); + + /* [{0: "test", 4: true}] = 81 A2 00 64 74 65 73 74 04 F5 */ + uint8_t const payload[] = {0x81, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + REQUIRE(plug_test == true); + } + } + + /************************************************************************************/ + + WHEN("A Switch property is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudSwitch switch_test; + switch_test = false; + + thing.addPropertyReal(switch_test, "test", Permission::ReadWrite); + + /* [{0: "test", 4: true}] = 81 A2 00 64 74 65 73 74 04 F5 */ + uint8_t const payload[] = {0x81, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + REQUIRE(switch_test == true); + } + } + + /************************************************************************************/ + + WHEN("A Temperature property is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudTemperature test; + test = 0.0f; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{0: "test", 2: 3.1459}] = 81 A2 00 64 74 65 73 74 02 FB 40 09 2A CD 9E 83 E4 26 */ + uint8_t const payload[] = {0x81, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0xFB, 0x40, 0x09, 0x2A, 0xCD, 0x9E, 0x83, 0xE4, 0x26}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length); + + REQUIRE(test == Approx(3.1459).epsilon(0.01)); + } + } + + /************************************************************************************/ + + WHEN("Multiple properties is changed via CBOR message") { + GIVEN("CloudProtocol::V2") { + WHEN("Multiple properties of different type are changed via CBOR message") { + ArduinoCloudThing thing; + thing.begin(); + + CloudBool bool_test = false; + CloudInt int_test = 1; + CloudFloat float_test = 2.0f; + CloudString str_test; + str_test = ("str_test"); + + thing.addPropertyReal(bool_test, "bool_test", Permission::ReadWrite); + thing.addPropertyReal(int_test, "int_test", Permission::ReadWrite); + thing.addPropertyReal(float_test, "float_test", Permission::ReadWrite); + thing.addPropertyReal(str_test, "str_test", Permission::ReadWrite); + + /* [{0: "bool_test", 4: true}, {0: "int_test", 2: 10}, {0: "float_test", 2: 20.0}, {0: "str_test", 3: "hello arduino"}] + = 84 A2 00 69 62 6F 6F 6C 5F 74 65 73 74 04 F5 A2 00 68 69 6E 74 5F 74 65 73 74 02 0A A2 00 6A 66 6C 6F 61 74 5F 74 65 73 74 02 F9 4D 00 A2 00 68 73 74 72 5F 74 65 73 74 03 6D 68 65 6C 6C 6F 20 61 72 64 75 69 6E 6F + */ + uint8_t const payload[] = {0x84, 0xA2, 0x00, 0x69, 0x62, 0x6F, 0x6F, 0x6C, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x69, 0x6E, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x02, 0x0A, 0xA2, 0x00, 0x6A, 0x66, 0x6C, 0x6F, 0x61, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x02, 0xF9, 0x4D, 0x00, 0xA2, 0x00, 0x68, 0x73, 0x74, 0x72, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x03, 0x6D, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6E, 0x6F}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + REQUIRE(bool_test == true); + REQUIRE(int_test == 10); + REQUIRE(float_test == Approx(20.0).epsilon(0.01)); + REQUIRE(str_test == "hello arduino"); + } + + /********************************************************************************/ + + WHEN("Multiple properties of different type are synchronized via CBOR message. FORCE_CLOUD_SYNC is passed as synchronization function and as a consequence values contained in the incoming message are stored in the properties") { + ArduinoCloudThing thing; + thing.begin(); + + CloudBool bool_test = false; + CloudInt int_test = 1; + CloudFloat float_test = 2.0f; + CloudString str_test; + str_test = ("str_test"); + + thing.addPropertyReal(bool_test, "bool_test", Permission::ReadWrite).onSync(CLOUD_WINS); + thing.addPropertyReal(int_test, "int_test", Permission::ReadWrite).onSync(CLOUD_WINS); + thing.addPropertyReal(float_test, "float_test", Permission::ReadWrite).onSync(CLOUD_WINS); + thing.addPropertyReal(str_test, "str_test", Permission::ReadWrite).onSync(CLOUD_WINS); + + /* [{0: "bool_test", 4: true}, {0: "int_test", 2: 10}, {0: "float_test", 2: 20.0}, {0: "str_test", 3: "hello arduino"}] + = 84 A2 00 69 62 6F 6F 6C 5F 74 65 73 74 04 F5 A2 00 68 69 6E 74 5F 74 65 73 74 02 0A A2 00 6A 66 6C 6F 61 74 5F 74 65 73 74 02 F9 4D 00 A2 00 68 73 74 72 5F 74 65 73 74 03 6D 68 65 6C 6C 6F 20 61 72 64 75 69 6E 6F + */ + uint8_t const payload[] = {0x84, 0xA2, 0x00, 0x69, 0x62, 0x6F, 0x6F, 0x6C, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x69, 0x6E, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x02, 0x0A, 0xA2, 0x00, 0x6A, 0x66, 0x6C, 0x6F, 0x61, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x02, 0xF9, 0x4D, 0x00, 0xA2, 0x00, 0x68, 0x73, 0x74, 0x72, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x03, 0x6D, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6E, 0x6F}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t), true); + + REQUIRE(bool_test == true); + REQUIRE(int_test == 10); + REQUIRE(float_test == Approx(20.0).epsilon(0.01)); + REQUIRE(str_test == "hello arduino"); + } + + /********************************************************************************/ + + WHEN("Multiple primitive properties of different type are synchronized via CBOR message. FORCE_CLOUD_SYNC is passed as synchronization function and as a consequence values contained in the incoming message are stored in the properties") { + ArduinoCloudThing thing; + thing.begin(); + + int int_test = 1; + bool bool_test = false; + float float_test = 2.0f; + String str_test; + str_test = "str_test"; + + ArduinoCloudProperty *i = new CloudWrapperInt(int_test); + ArduinoCloudProperty *b = new CloudWrapperBool(bool_test); + ArduinoCloudProperty *f = new CloudWrapperFloat(float_test); + ArduinoCloudProperty *s = new CloudWrapperString(str_test); + + thing.addPropertyReal(*b, "bool_test", Permission::ReadWrite).onSync(CLOUD_WINS); + thing.addPropertyReal(*i, "int_test", Permission::ReadWrite).onSync(CLOUD_WINS); + thing.addPropertyReal(*f, "float_test", Permission::ReadWrite).onSync(CLOUD_WINS); + thing.addPropertyReal(*s, "str_test", Permission::ReadWrite).onSync(CLOUD_WINS); + + thing.updateTimestampOnLocallyChangedProperties(); + + /* [{0: "bool_test", 4: true}, {0: "int_test", 2: 10}, {0: "float_test", 2: 20.0}, {0: "str_test", 3: "hello arduino"}] + = 84 A2 00 69 62 6F 6F 6C 5F 74 65 73 74 04 F5 A2 00 68 69 6E 74 5F 74 65 73 74 02 0A A2 00 6A 66 6C 6F 61 74 5F 74 65 73 74 02 F9 4D 00 A2 00 68 73 74 72 5F 74 65 73 74 03 6D 68 65 6C 6C 6F 20 61 72 64 75 69 6E 6F + */ + uint8_t const payload[] = {0x84, 0xA2, 0x00, 0x69, 0x62, 0x6F, 0x6F, 0x6C, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x69, 0x6E, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x02, 0x0A, 0xA2, 0x00, 0x6A, 0x66, 0x6C, 0x6F, 0x61, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x02, 0xF9, 0x4D, 0x00, 0xA2, 0x00, 0x68, 0x73, 0x74, 0x72, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x03, 0x6D, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x61, 0x72, 0x64, 0x75, 0x69, 0x6E, 0x6F}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t), true); + + REQUIRE(bool_test == true); + REQUIRE(int_test == 10); + REQUIRE(float_test == Approx(20.0).epsilon(0.01)); + REQUIRE(str_test == "hello arduino"); + } + + /********************************************************************************/ + + WHEN("Multiple String properties are changed via CBOR message") { + ArduinoCloudThing thing; + thing.begin(); + + CloudString str_1("hello"), + str_2("arduino"), + str_3("cloud"), + str_4("test"); + + thing.addPropertyReal(str_1, "str_1", Permission::ReadWrite); + thing.addPropertyReal(str_2, "str_2", Permission::ReadWrite); + thing.addPropertyReal(str_3, "str_3", Permission::ReadWrite); + thing.addPropertyReal(str_4, "str_4", Permission::ReadWrite); + + /* [{0: "str_1", 3: "I'd like"}, {0: "str_2", 3: "a"}, {0: "str_3", 3: "cup"}, {0: "str_4", 3: "of coffee"}] + = 84 A2 00 65 73 74 72 5F 31 03 68 49 27 64 20 6C 69 6B 65 A2 00 65 73 74 72 5F 32 03 61 61 A2 00 65 73 74 72 5F 33 03 63 63 75 70 A2 00 65 73 74 72 5F 34 03 69 6F 66 20 63 6F 66 66 65 65 + */ + uint8_t const payload[] = {0x84, 0xA2, 0x00, 0x65, 0x73, 0x74, 0x72, 0x5F, 0x31, 0x03, 0x68, 0x49, 0x27, 0x64, 0x20, 0x6C, 0x69, 0x6B, 0x65, 0xA2, 0x00, 0x65, 0x73, 0x74, 0x72, 0x5F, 0x32, 0x03, 0x61, 0x61, 0xA2, 0x00, 0x65, 0x73, 0x74, 0x72, 0x5F, 0x33, 0x03, 0x63, 0x63, 0x75, 0x70, 0xA2, 0x00, 0x65, 0x73, 0x74, 0x72, 0x5F, 0x34, 0x03, 0x69, 0x6F, 0x66, 0x20, 0x63, 0x6F, 0x66, 0x66, 0x65, 0x65}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + REQUIRE(str_1 == "I'd like"); + REQUIRE(str_2 == "a"); + REQUIRE(str_3 == "cup"); + REQUIRE(str_4 == "of coffee"); + } + } + } + + /************************************************************************************/ + + WHEN("A payload containing a CBOR base name is parsed") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudString str = "hello"; + thing.addPropertyReal(str, "test", Permission::ReadWrite); + + /* [{-2: "some-test-base-name", 0: "test", 3: "test"}] = 81 A3 21 73 73 6F 6D 65 2D 74 65 73 74 2D 62 61 73 65 2D 6E 61 6D 65 00 64 74 65 73 74 03 64 74 65 73 74 */ + uint8_t const payload[] = {0x81, 0xA3, 0x21, 0x73, 0x73, 0x6F, 0x6D, 0x65, 0x2D, 0x74, 0x65, 0x73, 0x74, 0x2D, 0x62, 0x61, 0x73, 0x65, 0x2D, 0x6E, 0x61, 0x6D, 0x65, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x03, 0x64, 0x74, 0x65, 0x73, 0x74}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + REQUIRE(str == "test"); + } + } + + /************************************************************************************/ + + WHEN("A payload containing a CBOR base time is parsed") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudInt test = 0; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{-3: 123.456, 0: "test", 2: 1}] = 81 A3 22 FB 40 5E DD 2F 1A 9F BE 77 00 64 74 65 73 74 02 01 */ + uint8_t const payload[] = {0x81, 0xA3, 0x22, 0xFB, 0x40, 0x5E, 0xDD, 0x2F, 0x1A, 0x9F, 0xBE, 0x77, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0x01}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + REQUIRE(test == 1); + } + } + + /************************************************************************************/ + + WHEN("A payload containing a CBOR time is parsed") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudInt test = 0; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{6: 123.456, 0: "test", 2: 1}] = 81 A3 06 FB 40 5E DD 2F 1A 9F BE 77 00 64 74 65 73 74 02 01 */ + uint8_t const payload[] = {0x81, 0xA3, 0x06, 0xFB, 0x40, 0x5E, 0xDD, 0x2F, 0x1A, 0x9F, 0xBE, 0x77, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0x01}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + REQUIRE(test == 1); + } + } + + /************************************************************************************/ + + WHEN("A payload containing a CBOR BaseVersion is parsed") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudInt test = 0; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{-1: 1, 0: "test", 2: 1}] = 81 A3 20 01 00 64 74 65 73 74 02 01 */ + uint8_t const payload[] = {0x81, 0xA3, 0x20, 0x01, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0x01}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + REQUIRE(test == 1); + } + } + + /************************************************************************************/ + + WHEN("A payload containing a CBOR BaseName, BaseTime and Time is parsed") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudInt test = 0; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{-2: "base-name", -3: 654.321, 6: 123.456, 0: "test", 2: 1}] = + 81 A5 21 69 62 61 73 65 2D 6E 61 6D 65 22 FB 40 84 72 91 68 72 B0 21 06 FB 40 5E DD 2F 1A 9F BE 77 00 64 74 65 73 74 02 01 + */ + uint8_t const payload[] = {0x81, 0xA5, 0x21, 0x69, 0x62, 0x61, 0x73, 0x65, 0x2D, 0x6E, 0x61, 0x6D, 0x65, 0x22, 0xFB, 0x40, 0x84, 0x72, 0x91, 0x68, 0x72, 0xB0, 0x21, 0x06, 0xFB, 0x40, 0x5E, 0xDD, 0x2F, 0x1A, 0x9F, 0xBE, 0x77, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0x01}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + REQUIRE(test == 1); + } + } + + /************************************************************************************/ + + WHEN("A payload containing a invalid CBOR key is parsed") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudInt test = 0; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{123: 123, 0: "test", 2: 1}] = 81 A3 18 7B 18 7B 00 64 74 65 73 74 02 01 */ + uint8_t const payload[] = {0x81, 0xA3, 0x18, 0x7B, 0x18, 0x7B, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0x01}; + thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); + + REQUIRE(test == 1); + } + } + + /************************************************************************************/ +} diff --git a/extras/test/src/test_encode.cpp b/extras/test/src/test_encode.cpp new file mode 100644 index 000000000..a9a5c5e56 --- /dev/null +++ b/extras/test/src/test_encode.cpp @@ -0,0 +1,402 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +/************************************************************************************** + INCLUDE + **************************************************************************************/ + +#include + +#include +#include +#include "types/CloudWrapperBool.h" +#include "types/CloudWrapperFloat.h" +#include "types/CloudWrapperInt.h" +#include "types/CloudWrapperString.h" + +/************************************************************************************** + TEST CODE + **************************************************************************************/ + +SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") { + /************************************************************************************/ + + WHEN("A 'bool' property is added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudBool test = true; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{0: "test", 4: true}] = 9F A2 00 64 74 65 73 74 04 F5 FF*/ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5, 0xFF}; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A 'bool' property is added - light payload") { + /*An integer identifier must be instead of the name of the property in order to have a shorter payload*/ + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudBool test = true; + /*The property is added with identifier 1 that will be used instead of the string "test" as property identifier*/ + thing.addPropertyReal(test, "test", Permission::ReadWrite, 1); + + /* [{0: 1, 4: true}] = 9F A2 00 01 04 F5 FF*/ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x01, 0x04, 0xF5, 0xFF}; + std::vector const actual = encode(thing, true); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A 'int' property is added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudInt int_test = 123; + thing.addPropertyReal(int_test, "test", Permission::ReadWrite); + + /* [{0: "test", 3: 123}] = 9F A2 00 64 74 65 73 74 02 18 7B FF */ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0x18, 0x7B, 0xFF}; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A 'float' property is added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudFloat float_test = 3.14159; + thing.addPropertyReal(float_test, "test", Permission::ReadWrite); + + /* [{0: "test", 2: 3.141590118408203}] = 9F A2 00 64 74 65 73 74 02 FA 40 49 0F D0 FF */ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0xFA, 0x40, 0x49, 0x0F, 0xD0, 0xFF}; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A 'String' property is added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudString string_test; + string_test = "test"; + thing.addPropertyReal(string_test, "test", Permission::ReadWrite); + + /* [{0: "test", 3: "test"}] = 9F A2 00 64 74 65 73 74 03 64 74 65 73 74 FF*/ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x03, 0x64, 0x74, 0x65, 0x73, 0x74, 0xFF}; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A 'Location' property is added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudLocation location_test = CloudLocation(2.0f, 3.0f); + thing.addPropertyReal(location_test, "test", Permission::ReadWrite); + + /* [{0: "test:lat", 3: 2},{0: "test:lon", 3: 3}] = 9F A2 00 68 74 65 73 74 3A 6C 61 74 02 FA 40 00 00 00 A2 00 68 74 65 73 74 3A 6C 6F 6E 02 FA 40 40 00 00 FF*/ + std::vector const expected = { 0x9F, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6C, 0x61, 0x74, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6C, 0x6F, 0x6E, 0x02, 0xFA, 0x40, 0x40, 0x00, 0x00, 0xFF }; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A 'Color' property is added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudColor color_test = CloudColor(2.0, 2.0, 2.0); + thing.addPropertyReal(color_test, "test", Permission::ReadWrite); + + /* [{0: "test:hue", 2: 2.0},{0: "test:sat", 2: 2.0},{0: "test:bri", 2: 2.0}] = 9F A2 00 68 74 65 73 74 3A 68 75 65 02 FA 40 00 00 00 A2 00 68 74 65 73 74 3A 73 61 74 02 FA 40 00 00 00 A2 00 68 74 65 73 74 3A 62 72 69 02 FA 40 00 00 00 FF*/ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x68, 0x75, 0x65, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x61, 0x74, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x62, 0x72, 0x69, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xFF }; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A 'Color' property is added - light payload") { + /*An integer identifier must be encoded instead of the name of the property in order to have a shorter payload*/ + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudColor color_test = CloudColor(2.0, 2.0, 2.0); + /*The property is added with identifier 1 that will be used instead of the string "name" as property identifier */ + thing.addPropertyReal(color_test, "test", Permission::ReadWrite, 1); + + /* [{0: 257, 2: 2.0},{0: 513, 2: 2.0},{0: 769, 2: 2.0}] = 9F A2 00 19 01 01 02 FA 40 00 00 00 A2 00 19 02 01 02 FA 40 00 00 00 A2 00 19 03 01 02 FA 40 00 00 00 FF*/ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x19, 0x01, 0x01, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x19, 0x02, 0x01, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x19, 0x03, 0x01, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xFF }; + std::vector const actual = encode(thing, true); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A 'ColoredLight' property is added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudColoredLight color_test = CloudColoredLight(true, 2.0, 2.0, 2.0); + thing.addPropertyReal(color_test, "test", Permission::ReadWrite); + + /* [{0: "test:swi", 4: true},{0: "test:hue", 2: 2.0},{0: "test:sat", 2: 2.0},{0: "test:bri", 2: 2.0}] = 83 A2 00 68 74 65 73 74 3A 73 77 69 04 F5 //A2 00 68 74 65 73 74 3A 68 75 65 02 FA 40 00 00 00 A2 00 68 74 65 73 74 3A 73 61 74 02 FA 40 00 00 00 A2 00 68 74 65 73 74 3A 62 72 69 02 FA 40 00 00 00 FF*/ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x77, 0x69, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x68, 0x75, 0x65, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x61, 0x74, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x62, 0x72, 0x69, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xFF }; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A 'Television' property is added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudTelevision tv_test = CloudTelevision(true, 50, false, PlaybackCommands::Play, InputValue::TV, 7); + thing.addPropertyReal(tv_test, "test", Permission::ReadWrite); + + /* [{0: "test:swi", 4: true},{0: "test:vol", 2: 50},{0: "test:mut", 2: false},{0: "test:pbc", 2: 3},{0: "test:inp", 2: 55},{0: "test:cha", 2: 7}] = 86 A2 00 68 74 65 73 74 3A 73 77 69 04 F5 A2 00 68 74 65 73 74 3A 76 6F 6C 02 18 32 A2 00 68 74 65 73 74 3A 6D 75 74 02 F4 A2 00 68 74 65 73 74 3A 70 62 63 02 03 A2 00 68 74 65 73 74 3A 69 6E 70 02 18 37 A2 00 68 74 65 73 74 3A 63 68 61 02 07 */ + std::vector const expected = {0x86, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x77, 0x69, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x76, 0x6F, 0x6C, 0x02, 0x18, 0x32, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6D, 0x75, 0x74, 0x02, 0xF4, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x70, 0x62, 0x63, 0x02, 0x03, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x69, 0x6E, 0x70, 0x02, 0x18, 0x37, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x63, 0x68, 0x61, 0x02, 0x07 }; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A 'DimmedLight' property is added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudDimmedLight color_test = CloudDimmedLight(true, 2.0); + thing.addPropertyReal(color_test, "test", Permission::ReadWrite); + + /* [{0: "test:swi", 4: true},{0: "test:hue", 2: 0.0},{0: "test:sat", 2: 0.0},{0: "test:bri", 2: 2.0}] = 83 A2 00 68 74 65 73 74 3A 73 77 69 04 F5 //A2 00 68 74 65 73 74 3A 68 75 65 02 FA 00 00 00 00 A2 00 68 74 65 73 74 3A 73 61 74 02 FA 00 00 00 00 A2 00 68 74 65 73 74 3A 62 72 69 02 FA 40 00 00 00 FF*/ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x77, 0x69, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x68, 0x75, 0x65, 0x02, 0xFA, 0x00, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x61, 0x74, 0x02, 0xFA, 0x00, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x62, 0x72, 0x69, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xFF }; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A light property is added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudLight test; + test = true; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{0: "test", 4: true}] = 9F A2 00 64 74 65 73 74 04 F5 FF*/ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5, 0xFF}; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A contact sensor property is added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudContactSensor test; + test = true; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{0: "test", 4: true}] = 9F A2 00 64 74 65 73 74 04 F5 FF*/ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5, 0xFF}; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A motion sensor property is added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudMotionSensor test; + test = true; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{0: "test", 4: true}] = 9F A2 00 64 74 65 73 74 04 F5 FF*/ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5, 0xFF}; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A smart plug property is added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudSmartPlug test; + test = true; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{0: "test", 4: true}] = 9F A2 00 64 74 65 73 74 04 F5 FF*/ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5, 0xFF}; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A Temperature property is added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudTemperature float_test; + float_test = 3.14159; + thing.addPropertyReal(float_test, "test", Permission::ReadWrite); + + /* [{0: "test", 2: 3.141590118408203}] = 9F A2 00 64 74 65 73 74 02 FA 40 49 0F D0 FF */ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0xFA, 0x40, 0x49, 0x0F, 0xD0, 0xFF}; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("A switch property is added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + encode(thing); + + CloudSwitch test; + test = true; + thing.addPropertyReal(test, "test", Permission::ReadWrite); + + /* [{0: "test", 4: true}] = 9F A2 00 64 74 65 73 74 04 F5 FF*/ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5, 0xFF}; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("Multiple properties are added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudInt int_test = 1; + CloudBool bool_test = false; + CloudFloat float_test = 2.0f; + CloudString str_test; + str_test = "str_test"; + + thing.addPropertyReal(int_test, "int_test", Permission::ReadWrite); + thing.addPropertyReal(bool_test, "bool_test", Permission::ReadWrite); + thing.addPropertyReal(float_test, "float_test", Permission::ReadWrite); + thing.addPropertyReal(str_test, "str_test", Permission::ReadWrite); + + /* [{0: "int_test", 2: 1}, {0: "bool_test", 4: false}, {0: "float_test", 2: 2.0}, {0: "str_test", 3: "str_test"}] + = 9F A2 00 68 69 6E 74 5F 74 65 73 74 02 01 A2 00 6A 66 6C 6F 61 74 5F 74 65 73 74 02 FA A2 00 6A 66 6C 6F 61 74 5F 74 65 73 74 02 FA 40 00 00 00 A2 00 68 73 74 72 5F 74 65 73 74 03 68 73 74 72 5F 74 65 73 74 FF + */ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x68, 0x69, 0x6E, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x02, 0x01, 0xA2, 0x00, 0x69, 0x62, 0x6F, 0x6F, 0x6C, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF4, 0xA2, 0x00, 0x6A, 0x66, 0x6C, 0x6F, 0x61, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x73, 0x74, 0x72, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x03, 0x68, 0x73, 0x74, 0x72, 0x5F, 0x74, 0x65, 0x73, 0x74, 0xFF}; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ + + WHEN("Multiple primitive properties are added") { + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + int int_test = 1; + bool bool_test = false; + float float_test = 2.0f; + String str_test; + str_test = "str_test"; + + ArduinoCloudProperty *i = new CloudWrapperInt(int_test); + ArduinoCloudProperty *b = new CloudWrapperBool(bool_test); + ArduinoCloudProperty *f = new CloudWrapperFloat(float_test); + ArduinoCloudProperty *s = new CloudWrapperString(str_test); + + thing.addPropertyReal(*i, "int_test", Permission::ReadWrite); + thing.addPropertyReal(*b, "bool_test", Permission::ReadWrite); + thing.addPropertyReal(*f, "float_test", Permission::ReadWrite); + thing.addPropertyReal(*s, "str_test", Permission::ReadWrite); + + thing.updateTimestampOnLocallyChangedProperties(); + + /* [{0: "int_test", 2: 1}, {0: "bool_test", 4: false}, {0: "float_test", 2: 2.0}, {0: "str_test", 3: "str_test"}] + = 9F A2 00 68 69 6E 74 5F 74 65 73 74 02 01 A2 00 6A 66 6C 6F 61 74 5F 74 65 73 74 02 FA A2 00 6A 66 6C 6F 61 74 5F 74 65 73 74 02 FA 40 00 00 00 A2 00 68 73 74 72 5F 74 65 73 74 03 68 73 74 72 5F 74 65 73 74 FF + */ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x68, 0x69, 0x6E, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x02, 0x01, 0xA2, 0x00, 0x69, 0x62, 0x6F, 0x6F, 0x6C, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF4, 0xA2, 0x00, 0x6A, 0x66, 0x6C, 0x6F, 0x61, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x73, 0x74, 0x72, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x03, 0x68, 0x73, 0x74, 0x72, 0x5F, 0x74, 0x65, 0x73, 0x74, 0xFF}; + std::vector const actual = encode(thing); + REQUIRE(actual == expected); + } + } + + /************************************************************************************/ +} diff --git a/extras/test/src/test_publishEvery.cpp b/extras/test/src/test_publishEvery.cpp new file mode 100644 index 000000000..152ccac73 --- /dev/null +++ b/extras/test/src/test_publishEvery.cpp @@ -0,0 +1,63 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +/************************************************************************************** + INCLUDE + **************************************************************************************/ + +#include + +#include +#include + +/************************************************************************************** + TEST CODE + **************************************************************************************/ + +SCENARIO("A Arduino cloud property is published periodically", "[ArduinoCloudThing::publishEvery]") { + /************************************************************************************/ + + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudBool test = true; + unsigned long const PUBLISH_INTERVAL_SEC = 1 * SECONDS; + + thing.addPropertyReal(test, "test", Permission::ReadWrite).publishEvery(PUBLISH_INTERVAL_SEC); + + WHEN("t = 0 ms, publish interval = 1000 ms, 1st call to 'encode'") { + set_millis(0); + THEN("'encode' should encode the property") { + REQUIRE(encode(thing).size() != 0); + WHEN("t = 999 ms") { + set_millis(999); + THEN("'encode' should not encode the property") { + REQUIRE(encode(thing).size() == 0); + WHEN("t = 1000 ms") { + set_millis(1000); + THEN("'encode' should encode the property") { + REQUIRE(encode(thing).size() != 0); + WHEN("t = 1999 ms") { + set_millis(1999); + THEN("'encode' should not encode the property") { + REQUIRE(encode(thing).size() == 0); + WHEN("t = 2000 ms") { + set_millis(2000); + THEN("'encode' should encode the property") { + REQUIRE(encode(thing).size() != 0); + } + } + } + } + } + } + } + } + } + } + } + + /************************************************************************************/ +} diff --git a/extras/test/src/test_publishOnChange.cpp b/extras/test/src/test_publishOnChange.cpp new file mode 100644 index 000000000..078aaa557 --- /dev/null +++ b/extras/test/src/test_publishOnChange.cpp @@ -0,0 +1,50 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +/************************************************************************************** + INCLUDE + **************************************************************************************/ + +#include + +#include +#include + +/************************************************************************************** + TEST CODE + **************************************************************************************/ + +SCENARIO("A Arduino cloud property is published on value change", "[ArduinoCloudThing::publishOnChange]") { + /************************************************************************************/ + + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudInt test = 10; + int const DELTA = 6; + + thing.addPropertyReal(test, "test", Permission::ReadWrite).publishOnChange(DELTA); + + WHEN("test = 10, delta = 6, the property is encoded for the 1st time") { + THEN("The property should be encoded") { + REQUIRE(encode(thing).size() != 0); + WHEN("test +=4 -> test = 14") { + test += 4; + THEN("Since the increment since the last update (4) is smaller than the delta of 6 the property should not be encoded") { + REQUIRE(encode(thing).size() == 0); + WHEN("test +=4 -> test = 18") { + test += 4; + THEN("Since the increment since the last update (8) is greater than the delta of 6 the property should be encoded") { + REQUIRE(encode(thing).size() != 0); + } + } + } + } + } + } + } + + /************************************************************************************/ +} diff --git a/extras/test/src/test_publishOnChangeRateLimit.cpp b/extras/test/src/test_publishOnChangeRateLimit.cpp new file mode 100644 index 000000000..a794ebef4 --- /dev/null +++ b/extras/test/src/test_publishOnChangeRateLimit.cpp @@ -0,0 +1,68 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +/************************************************************************************** + INCLUDE + **************************************************************************************/ + +#include + +#include +#include + +/************************************************************************************** + TEST CODE + **************************************************************************************/ + +SCENARIO("A Arduino cloud property is published on value change but the update rate is limited", "[ArduinoCloudThing::publishOnChange]") { + /************************************************************************************/ + + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudInt test = 0; + int const MIN_DELTA = 0; + unsigned long const MIN_TIME_BETWEEN_UPDATES_ms = 500; /* No updates faster than 500 ms */ + + thing.addPropertyReal(test, "test", Permission::ReadWrite).publishOnChange(MIN_DELTA, MIN_TIME_BETWEEN_UPDATES_ms); + + WHEN("t = 0 ms, min time between updates = 500 ms, property not modified, 1st call to 'encode'") { + set_millis(0); + THEN("'encode' should encode the property") { + REQUIRE(encode(thing).size() != 0); + WHEN("t = 499 ms, property modified") { + test++; + set_millis(499); + THEN("'encode' should not encode any property") { + REQUIRE(encode(thing).size() == 0); + WHEN("t = 500 ms, property modified") { + test++; + set_millis(500); + THEN("'encode' should encode the property") { + REQUIRE(encode(thing).size() != 0); + WHEN("t = 999 ms, property modified") { + test++; + set_millis(999); + THEN("'encode' should not encode any property") { + REQUIRE(encode(thing).size() == 0); + WHEN("t = 1000 ms, property modified") { + test++; + set_millis(1000); + THEN("'encode' should encode the property") { + REQUIRE(encode(thing).size() != 0); + } + } + } + } + } + } + } + } + } + } + } + + /************************************************************************************/ +} diff --git a/extras/test/src/test_readOnly.cpp b/extras/test/src/test_readOnly.cpp new file mode 100644 index 000000000..9bbda3371 --- /dev/null +++ b/extras/test/src/test_readOnly.cpp @@ -0,0 +1,37 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +/************************************************************************************** + INCLUDE + **************************************************************************************/ + +#include + +#include +#include + +/************************************************************************************** + TEST CODE + **************************************************************************************/ + +SCENARIO("A Arduino cloud property is marked 'read only'", "[ArduinoCloudThing::decode]") { + /************************************************************************************/ + + GIVEN("CloudProtocol::V2") { + ArduinoCloudThing thing; + thing.begin(); + + CloudInt test = 0; + thing.addPropertyReal(test, "test", Permission::Read); + + /* [{0: "test", 2: 7}] = 81 A2 00 64 74 65 73 74 02 07 */ + uint8_t const payload[] = {0x81, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0x07}; + int const payload_length = sizeof(payload) / sizeof(uint8_t); + thing.decode(payload, payload_length); + + REQUIRE(test == 0); + } + + /************************************************************************************/ +} diff --git a/extras/test/src/test_writeOnly.cpp b/extras/test/src/test_writeOnly.cpp new file mode 100644 index 000000000..775283a66 --- /dev/null +++ b/extras/test/src/test_writeOnly.cpp @@ -0,0 +1,30 @@ +/* + Copyright (c) 2019 Arduino. All rights reserved. +*/ + +/************************************************************************************** + INCLUDE + **************************************************************************************/ + +#include + +#include +#include + +/************************************************************************************** + TEST CODE + **************************************************************************************/ + +SCENARIO("A Arduino cloud property is marked 'write only'", "[ArduinoCloudThing::encode]") { + /************************************************************************************/ + + ArduinoCloudThing thing; + thing.begin(); + + CloudInt test = 0; + thing.addPropertyReal(test, "test", Permission::Write); + + REQUIRE(encode(thing).size() == 0); /* Since 'test' is 'write only' it should not be encoded */ + + /************************************************************************************/ +} diff --git a/src/cbor/ArduinoCloudProperty.h b/src/cbor/ArduinoCloudProperty.h index 7144fbfdc..8d961f465 100644 --- a/src/cbor/ArduinoCloudProperty.h +++ b/src/cbor/ArduinoCloudProperty.h @@ -18,7 +18,7 @@ #ifndef ARDUINO_CLOUD_PROPERTY_HPP_ #define ARDUINO_CLOUD_PROPERTY_HPP_ -#ifdef HOST_BUILD +#ifdef HOST #define substring(...) substr(__VA_ARGS__) #define indexOf(x) find(x) #endif diff --git a/src/cbor/lib/tinycbor/src/cbor.h b/src/cbor/lib/tinycbor/src/cbor.h index 3eb52e41f..e81df0ec3 100644 --- a/src/cbor/lib/tinycbor/src/cbor.h +++ b/src/cbor/lib/tinycbor/src/cbor.h @@ -329,7 +329,7 @@ CBOR_INLINE_API bool cbor_value_is_boolean(const CborValue *value) { return value->type == CborBooleanType; } CBOR_INLINE_API CborError cbor_value_get_boolean(const CborValue *value, bool *result) { -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_boolean(value)); #endif *result = !!value->extra; @@ -341,7 +341,7 @@ CBOR_INLINE_API bool cbor_value_is_simple_type(const CborValue *value) { return value->type == CborSimpleType; } CBOR_INLINE_API CborError cbor_value_get_simple_type(const CborValue *value, uint8_t *result) { -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_simple_type(value)); #endif *result = (uint8_t)value->extra; @@ -358,7 +358,7 @@ CBOR_INLINE_API bool cbor_value_is_negative_integer(const CborValue *value) CBOR_INLINE_API CborError cbor_value_get_raw_integer(const CborValue *value, uint64_t *result) { -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_integer(value)); #endif *result = _cbor_value_extract_int64_helper(value); @@ -367,7 +367,7 @@ CBOR_INLINE_API CborError cbor_value_get_raw_integer(const CborValue *value, uin CBOR_INLINE_API CborError cbor_value_get_uint64(const CborValue *value, uint64_t *result) { -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_unsigned_integer(value)); #endif *result = _cbor_value_extract_int64_helper(value); @@ -376,7 +376,7 @@ CBOR_INLINE_API CborError cbor_value_get_uint64(const CborValue *value, uint64_t CBOR_INLINE_API CborError cbor_value_get_int64(const CborValue *value, int64_t *result) { -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_integer(value)); #endif *result = (int64_t) _cbor_value_extract_int64_helper(value); @@ -387,7 +387,7 @@ CBOR_INLINE_API CborError cbor_value_get_int64(const CborValue *value, int64_t * CBOR_INLINE_API CborError cbor_value_get_int(const CborValue *value, int *result) { -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_integer(value)); #endif *result = (int) _cbor_value_extract_int64_helper(value); @@ -407,7 +407,7 @@ CBOR_INLINE_API bool cbor_value_is_tag(const CborValue *value) { return value->type == CborTagType; } CBOR_INLINE_API CborError cbor_value_get_tag(const CborValue *value, CborTag *result) { -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_tag(value)); #endif *result = _cbor_value_extract_int64_helper(value); @@ -424,7 +424,7 @@ CBOR_INLINE_API bool cbor_value_is_text_string(const CborValue *value) CBOR_INLINE_API CborError cbor_value_get_string_length(const CborValue *value, size_t *length) { uint64_t v; -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_byte_string(value) || cbor_value_is_text_string(value)); #endif if (!cbor_value_is_length_known(value)) @@ -446,7 +446,7 @@ CBOR_API CborError cbor_value_calculate_string_length(const CborValue *value, si CBOR_INLINE_API CborError cbor_value_copy_text_string(const CborValue *value, char *buffer, size_t *buflen, CborValue *next) { -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_text_string(value)); #endif return _cbor_value_copy_string(value, buffer, buflen, next); @@ -454,7 +454,7 @@ CBOR_INLINE_API CborError cbor_value_copy_text_string(const CborValue *value, ch CBOR_INLINE_API CborError cbor_value_copy_byte_string(const CborValue *value, uint8_t *buffer, size_t *buflen, CborValue *next) { -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_byte_string(value)); #endif return _cbor_value_copy_string(value, buffer, buflen, next); @@ -463,7 +463,7 @@ CBOR_INLINE_API CborError cbor_value_copy_byte_string(const CborValue *value, ui CBOR_INLINE_API CborError cbor_value_dup_text_string(const CborValue *value, char **buffer, size_t *buflen, CborValue *next) { -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_text_string(value)); #endif return _cbor_value_dup_string(value, (void **)buffer, buflen, next); @@ -471,7 +471,7 @@ CBOR_INLINE_API CborError cbor_value_dup_text_string(const CborValue *value, cha CBOR_INLINE_API CborError cbor_value_dup_byte_string(const CborValue *value, uint8_t **buffer, size_t *buflen, CborValue *next) { -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_byte_string(value)); #endif return _cbor_value_dup_string(value, (void **)buffer, buflen, next); @@ -488,7 +488,7 @@ CBOR_INLINE_API bool cbor_value_is_map(const CborValue *value) CBOR_INLINE_API CborError cbor_value_get_array_length(const CborValue *value, size_t *length) { uint64_t v; -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_array(value)); #endif if (!cbor_value_is_length_known(value)) @@ -503,7 +503,7 @@ CBOR_INLINE_API CborError cbor_value_get_array_length(const CborValue *value, si CBOR_INLINE_API CborError cbor_value_get_map_length(const CborValue *value, size_t *length) { uint64_t v; -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_map(value)); #endif if (!cbor_value_is_length_known(value)) @@ -527,7 +527,7 @@ CBOR_INLINE_API bool cbor_value_is_float(const CborValue *value) CBOR_INLINE_API CborError cbor_value_get_float(const CborValue *value, float *result) { uint32_t data; -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_float(value)); assert(value->flags & CborIteratorFlag_IntegerValueTooLarge); #endif @@ -541,7 +541,7 @@ CBOR_INLINE_API bool cbor_value_is_double(const CborValue *value) CBOR_INLINE_API CborError cbor_value_get_double(const CborValue *value, double *result) { uint64_t data; -#ifdef HOST_BUILD +#ifdef HOST assert(cbor_value_is_double(value)); assert(value->flags & CborIteratorFlag_IntegerValueTooLarge); #endif From 46193a0b1fe38641fc9f159148dbe53bea0a0601 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 3 Jun 2020 16:23:16 +0200 Subject: [PATCH 169/175] Fixing test cases (CBOR encoded byte-array was not consistent with symbolic message) --- extras/test/src/test_decode.cpp | 4 ++-- extras/test/src/test_encode.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/extras/test/src/test_decode.cpp b/extras/test/src/test_decode.cpp index 5f521f926..748770f2e 100644 --- a/extras/test/src/test_decode.cpp +++ b/extras/test/src/test_decode.cpp @@ -250,8 +250,8 @@ SCENARIO("Arduino Cloud Properties are decoded", "[ArduinoCloudThing::decode]") thing.addPropertyReal(tv_test, "test", Permission::ReadWrite); - /* [{0: "test:swi", 4: true},{0: "test:vol", 2: 50},{0: "test:mut", 2: false},{0: "test:pbc", 2: 3},{0: "test:inp", 2: 55},{0: "test:cha", 2: 7}] = 86 A2 00 68 74 65 73 74 3A 73 77 69 04 F5 A2 00 68 74 65 73 74 3A 76 6F 6C 02 18 32 A2 00 68 74 65 73 74 3A 6D 75 74 02 F4 A2 00 68 74 65 73 74 3A 70 62 63 02 03 A2 00 68 74 65 73 74 3A 69 6E 70 02 18 37 A2 00 68 74 65 73 74 3A 63 68 61 02 07 */ - uint8_t const payload[] = {0x86, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x77, 0x69, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x76, 0x6F, 0x6C, 0x02, 0x18, 0x32, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6D, 0x75, 0x74, 0x02, 0xF4, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x70, 0x62, 0x63, 0x02, 0x03, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x69, 0x6E, 0x70, 0x02, 0x18, 0x37, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x63, 0x68, 0x61, 0x02, 0x07 }; + /* [{0: "test:swi", 4: true},{0: "test:vol", 2: 50},{0: "test:mut", 2: false},{0: "test:pbc", 2: 3},{0: "test:inp", 2: 55},{0: "test:cha", 2: 7}] = 9F A2 00 68 74 65 73 74 3A 73 77 69 04 F5 A2 00 68 74 65 73 74 3A 76 6F 6C 02 18 32 A2 00 68 74 65 73 74 3A 6D 75 74 04 F4 A2 00 68 74 65 73 74 3A 70 62 63 02 03 A2 00 68 74 65 73 74 3A 69 6E 70 02 18 37 A2 00 68 74 65 73 74 3A 63 68 61 02 07 FF */ + uint8_t const payload[] = {0x9F, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x77, 0x69, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x76, 0x6F, 0x6C, 0x02, 0x18, 0x32, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6D, 0x75, 0x74, 0x04, 0xF4, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x70, 0x62, 0x63, 0x02, 0x03, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x69, 0x6E, 0x70, 0x02, 0x18, 0x37, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x63, 0x68, 0x61, 0x02, 0x07, 0xFF}; thing.decode(payload, sizeof(payload) / sizeof(uint8_t)); Television tv_compare = Television(true, 50, false, PlaybackCommands::Play, InputValue::TV, 7); diff --git a/extras/test/src/test_encode.cpp b/extras/test/src/test_encode.cpp index a9a5c5e56..57fa131f7 100644 --- a/extras/test/src/test_encode.cpp +++ b/extras/test/src/test_encode.cpp @@ -198,8 +198,8 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") CloudTelevision tv_test = CloudTelevision(true, 50, false, PlaybackCommands::Play, InputValue::TV, 7); thing.addPropertyReal(tv_test, "test", Permission::ReadWrite); - /* [{0: "test:swi", 4: true},{0: "test:vol", 2: 50},{0: "test:mut", 2: false},{0: "test:pbc", 2: 3},{0: "test:inp", 2: 55},{0: "test:cha", 2: 7}] = 86 A2 00 68 74 65 73 74 3A 73 77 69 04 F5 A2 00 68 74 65 73 74 3A 76 6F 6C 02 18 32 A2 00 68 74 65 73 74 3A 6D 75 74 02 F4 A2 00 68 74 65 73 74 3A 70 62 63 02 03 A2 00 68 74 65 73 74 3A 69 6E 70 02 18 37 A2 00 68 74 65 73 74 3A 63 68 61 02 07 */ - std::vector const expected = {0x86, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x77, 0x69, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x76, 0x6F, 0x6C, 0x02, 0x18, 0x32, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6D, 0x75, 0x74, 0x02, 0xF4, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x70, 0x62, 0x63, 0x02, 0x03, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x69, 0x6E, 0x70, 0x02, 0x18, 0x37, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x63, 0x68, 0x61, 0x02, 0x07 }; + /* [{0: "test:swi", 4: true},{0: "test:vol", 2: 50},{0: "test:mut", 2: false},{0: "test:pbc", 2: 3},{0: "test:inp", 2: 55},{0: "test:cha", 2: 7}] = 9F A2 00 68 74 65 73 74 3A 73 77 69 04 F5 A2 00 68 74 65 73 74 3A 76 6F 6C 02 18 32 A2 00 68 74 65 73 74 3A 6D 75 74 04 F4 A2 00 68 74 65 73 74 3A 70 62 63 02 03 A2 00 68 74 65 73 74 3A 69 6E 70 02 18 37 A2 00 68 74 65 73 74 3A 63 68 61 02 07 FF */ + std::vector const expected = {0x9F, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x77, 0x69, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x76, 0x6F, 0x6C, 0x02, 0x18, 0x32, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6D, 0x75, 0x74, 0x04, 0xF4, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x70, 0x62, 0x63, 0x02, 0x03, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x69, 0x6E, 0x70, 0x02, 0x18, 0x37, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x63, 0x68, 0x61, 0x02, 0x07, 0xFF}; std::vector const actual = encode(thing); REQUIRE(actual == expected); } From caed2b636a752cd6d9f988380f38a9e7a478392a Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Wed, 3 Jun 2020 16:32:55 +0200 Subject: [PATCH 170/175] Excluding the library director containing the SingleLinkedList as well as the cbor library --- .github/workflows/unit-tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index f4ba99f01..9eb35c8f7 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -38,7 +38,7 @@ jobs: cd "$BUILD_PATH" sudo apt-get --assume-yes install lcov > /dev/null lcov --directory . --capture --output-file coverage.info - lcov --quiet --remove coverage.info '*/extras/test/*' '/usr/*' --output-file coverage.info + lcov --quiet --remove coverage.info '*/extras/test/*' '/usr/*' '*/src/cbor/lib/*' --output-file coverage.info lcov --list coverage.info - name: Upload coverage report to Codecov From c9d7d272251874dc7ea3866355d8a5e59e81a114 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Thu, 4 Jun 2020 06:34:51 +0200 Subject: [PATCH 171/175] Moving utility/test helper files into subfolder include/util and src/util --- extras/test/CMakeLists.txt | 4 ++-- extras/test/include/{ => util}/OTATestData.h | 0 extras/test/include/{ => util}/TestUtil.h | 0 extras/test/src/test_CloudColor.cpp | 2 +- extras/test/src/test_OTALogic.cpp | 2 +- extras/test/src/test_callback.cpp | 2 +- extras/test/src/test_decode.cpp | 2 +- extras/test/src/test_encode.cpp | 2 +- extras/test/src/test_publishEvery.cpp | 2 +- extras/test/src/test_publishOnChange.cpp | 2 +- extras/test/src/test_publishOnChangeRateLimit.cpp | 2 +- extras/test/src/test_readOnly.cpp | 2 +- extras/test/src/test_writeOnly.cpp | 2 +- extras/test/src/{ => util}/OTATestData.cpp | 2 +- extras/test/src/{ => util}/TestUtil.cpp | 2 +- 15 files changed, 14 insertions(+), 14 deletions(-) rename extras/test/include/{ => util}/OTATestData.h (100%) rename extras/test/include/{ => util}/TestUtil.h (100%) rename extras/test/src/{ => util}/OTATestData.cpp (98%) rename extras/test/src/{ => util}/TestUtil.cpp (97%) diff --git a/extras/test/CMakeLists.txt b/extras/test/CMakeLists.txt index 9ea08195f..cb3d73433 100644 --- a/extras/test/CMakeLists.txt +++ b/extras/test/CMakeLists.txt @@ -43,8 +43,8 @@ set(TEST_SRCS src/test_readOnly.cpp src/test_writeOnly.cpp - src/OTATestData.cpp - src/TestUtil.cpp + src/util/OTATestData.cpp + src/util/TestUtil.cpp ../../src/utility/ota/crc.cpp ../../src/utility/ota/OTALogic.cpp diff --git a/extras/test/include/OTATestData.h b/extras/test/include/util/OTATestData.h similarity index 100% rename from extras/test/include/OTATestData.h rename to extras/test/include/util/OTATestData.h diff --git a/extras/test/include/TestUtil.h b/extras/test/include/util/TestUtil.h similarity index 100% rename from extras/test/include/TestUtil.h rename to extras/test/include/util/TestUtil.h diff --git a/extras/test/src/test_CloudColor.cpp b/extras/test/src/test_CloudColor.cpp index f2538a4b3..aaf50a76e 100644 --- a/extras/test/src/test_CloudColor.cpp +++ b/extras/test/src/test_CloudColor.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include /************************************************************************************** diff --git a/extras/test/src/test_OTALogic.cpp b/extras/test/src/test_OTALogic.cpp index 59c3103f4..f9f669c91 100644 --- a/extras/test/src/test_OTALogic.cpp +++ b/extras/test/src/test_OTALogic.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include diff --git a/extras/test/src/test_callback.cpp b/extras/test/src/test_callback.cpp index 3a5270b26..0a29af4a6 100644 --- a/extras/test/src/test_callback.cpp +++ b/extras/test/src/test_callback.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include "types/CloudWrapperBool.h" diff --git a/extras/test/src/test_decode.cpp b/extras/test/src/test_decode.cpp index 748770f2e..0ecd98227 100644 --- a/extras/test/src/test_decode.cpp +++ b/extras/test/src/test_decode.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include "types/CloudWrapperBool.h" #include "types/CloudWrapperFloat.h" diff --git a/extras/test/src/test_encode.cpp b/extras/test/src/test_encode.cpp index 57fa131f7..ebd384b4f 100644 --- a/extras/test/src/test_encode.cpp +++ b/extras/test/src/test_encode.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include "types/CloudWrapperBool.h" #include "types/CloudWrapperFloat.h" diff --git a/extras/test/src/test_publishEvery.cpp b/extras/test/src/test_publishEvery.cpp index 152ccac73..f0e536c6f 100644 --- a/extras/test/src/test_publishEvery.cpp +++ b/extras/test/src/test_publishEvery.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include /************************************************************************************** diff --git a/extras/test/src/test_publishOnChange.cpp b/extras/test/src/test_publishOnChange.cpp index 078aaa557..799a6e0e4 100644 --- a/extras/test/src/test_publishOnChange.cpp +++ b/extras/test/src/test_publishOnChange.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include /************************************************************************************** diff --git a/extras/test/src/test_publishOnChangeRateLimit.cpp b/extras/test/src/test_publishOnChangeRateLimit.cpp index a794ebef4..c03426c7a 100644 --- a/extras/test/src/test_publishOnChangeRateLimit.cpp +++ b/extras/test/src/test_publishOnChangeRateLimit.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include /************************************************************************************** diff --git a/extras/test/src/test_readOnly.cpp b/extras/test/src/test_readOnly.cpp index 9bbda3371..e7421f228 100644 --- a/extras/test/src/test_readOnly.cpp +++ b/extras/test/src/test_readOnly.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include /************************************************************************************** diff --git a/extras/test/src/test_writeOnly.cpp b/extras/test/src/test_writeOnly.cpp index 775283a66..6a3122e00 100644 --- a/extras/test/src/test_writeOnly.cpp +++ b/extras/test/src/test_writeOnly.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include /************************************************************************************** diff --git a/extras/test/src/OTATestData.cpp b/extras/test/src/util/OTATestData.cpp similarity index 98% rename from extras/test/src/OTATestData.cpp rename to extras/test/src/util/OTATestData.cpp index e229e3bec..449a6795a 100644 --- a/extras/test/src/OTATestData.cpp +++ b/extras/test/src/util/OTATestData.cpp @@ -6,7 +6,7 @@ INCLUDE **************************************************************************************/ -#include +#include #include diff --git a/extras/test/src/TestUtil.cpp b/extras/test/src/util/TestUtil.cpp similarity index 97% rename from extras/test/src/TestUtil.cpp rename to extras/test/src/util/TestUtil.cpp index b37238803..c249b554f 100644 --- a/extras/test/src/TestUtil.cpp +++ b/extras/test/src/util/TestUtil.cpp @@ -6,7 +6,7 @@ INCLUDE **************************************************************************************/ -#include +#include #include #include From 1d8d99ad224ebf9458e4da8923468df19212d20f Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Thu, 4 Jun 2020 06:37:04 +0200 Subject: [PATCH 172/175] Renaming OTATestData.h/.cpp to OTATestDataGenerator.h/.cpp --- extras/test/CMakeLists.txt | 2 +- .../include/util/{OTATestData.h => OTATestDataGenerator.h} | 6 +++--- extras/test/src/test_OTALogic.cpp | 2 +- .../src/util/{OTATestData.cpp => OTATestDataGenerator.cpp} | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) rename extras/test/include/util/{OTATestData.h => OTATestDataGenerator.h} (89%) rename extras/test/src/util/{OTATestData.cpp => OTATestDataGenerator.cpp} (97%) diff --git a/extras/test/CMakeLists.txt b/extras/test/CMakeLists.txt index cb3d73433..0166736ab 100644 --- a/extras/test/CMakeLists.txt +++ b/extras/test/CMakeLists.txt @@ -43,7 +43,7 @@ set(TEST_SRCS src/test_readOnly.cpp src/test_writeOnly.cpp - src/util/OTATestData.cpp + src/util/OTATestDataGenerator.cpp src/util/TestUtil.cpp ../../src/utility/ota/crc.cpp diff --git a/extras/test/include/util/OTATestData.h b/extras/test/include/util/OTATestDataGenerator.h similarity index 89% rename from extras/test/include/util/OTATestData.h rename to extras/test/include/util/OTATestDataGenerator.h index 92f117680..42b8ba36a 100644 --- a/extras/test/include/util/OTATestData.h +++ b/extras/test/include/util/OTATestDataGenerator.h @@ -2,8 +2,8 @@ * Copyright (c) 2020 Arduino. All rights reserved. */ -#ifndef OTA_TEST_DATA_H_ -#define OTA_TEST_DATA_H_ +#ifndef OTA_TEST_DATA_GENERATOR_H_ +#define OTA_TEST_DATA_GENERATOR_H_ /************************************************************************************** INCLUDE @@ -34,4 +34,4 @@ void generate_valid_ota_data(OTAData & ota_data); void generate_invalid_ota_data_crc_wrong(OTAData & ota_data); -#endif /* OTA_TEST_DATA_H_ */ +#endif /* OTA_TEST_DATA_GENERATOR_H_ */ diff --git a/extras/test/src/test_OTALogic.cpp b/extras/test/src/test_OTALogic.cpp index f9f669c91..7765f0396 100644 --- a/extras/test/src/test_OTALogic.cpp +++ b/extras/test/src/test_OTALogic.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include diff --git a/extras/test/src/util/OTATestData.cpp b/extras/test/src/util/OTATestDataGenerator.cpp similarity index 97% rename from extras/test/src/util/OTATestData.cpp rename to extras/test/src/util/OTATestDataGenerator.cpp index 449a6795a..97d470595 100644 --- a/extras/test/src/util/OTATestData.cpp +++ b/extras/test/src/util/OTATestDataGenerator.cpp @@ -6,7 +6,7 @@ INCLUDE **************************************************************************************/ -#include +#include #include From ca3e6a1b79de811b5ed1ab035d2eac614ce24507 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Thu, 4 Jun 2020 06:41:51 +0200 Subject: [PATCH 173/175] Renaming TestUtil to CBORTestUtil and encapsulating its methods in the namespace 'cbor' --- extras/test/CMakeLists.txt | 2 +- .../util/{TestUtil.h => CBORTestUtil.h} | 19 ++++- extras/test/src/test_CloudColor.cpp | 2 +- extras/test/src/test_callback.cpp | 6 +- extras/test/src/test_decode.cpp | 2 +- extras/test/src/test_encode.cpp | 74 +++++++++---------- extras/test/src/test_publishEvery.cpp | 12 +-- extras/test/src/test_publishOnChange.cpp | 8 +- .../src/test_publishOnChangeRateLimit.cpp | 12 +-- extras/test/src/test_readOnly.cpp | 2 +- extras/test/src/test_writeOnly.cpp | 4 +- .../util/{TestUtil.cpp => CBORTestUtil.cpp} | 15 +++- 12 files changed, 92 insertions(+), 66 deletions(-) rename extras/test/include/util/{TestUtil.h => CBORTestUtil.h} (54%) rename extras/test/src/util/{TestUtil.cpp => CBORTestUtil.cpp} (72%) diff --git a/extras/test/CMakeLists.txt b/extras/test/CMakeLists.txt index 0166736ab..554938879 100644 --- a/extras/test/CMakeLists.txt +++ b/extras/test/CMakeLists.txt @@ -43,8 +43,8 @@ set(TEST_SRCS src/test_readOnly.cpp src/test_writeOnly.cpp + src/util/CBORTestUtil.cpp src/util/OTATestDataGenerator.cpp - src/util/TestUtil.cpp ../../src/utility/ota/crc.cpp ../../src/utility/ota/OTALogic.cpp diff --git a/extras/test/include/util/TestUtil.h b/extras/test/include/util/CBORTestUtil.h similarity index 54% rename from extras/test/include/util/TestUtil.h rename to extras/test/include/util/CBORTestUtil.h index ea5e5883d..8aace645e 100644 --- a/extras/test/include/util/TestUtil.h +++ b/extras/test/include/util/CBORTestUtil.h @@ -2,8 +2,8 @@ Copyright (c) 2019 Arduino. All rights reserved. */ -#ifndef INCLUDE_TESTUTIL_H_ -#define INCLUDE_TESTUTIL_H_ +#ifndef INCLUDE_CBOR_TESTUTIL_H_ +#define INCLUDE_CBOR_TESTUTIL_H_ /************************************************************************************** INCLUDE @@ -13,6 +13,13 @@ #include +/************************************************************************************** + NAMESPACE + **************************************************************************************/ + +namespace cbor +{ + /************************************************************************************** PROTOTYPES **************************************************************************************/ @@ -20,4 +27,10 @@ std::vector encode(ArduinoCloudThing & thing, bool lightPayload = false); void print(std::vector const & vect); -#endif /* INCLUDE_TESTUTIL_H_ */ +/************************************************************************************** + NAMESPACE + **************************************************************************************/ + +} /* cbor */ + +#endif /* INCLUDE_CBOR_TESTUTIL_H_ */ diff --git a/extras/test/src/test_CloudColor.cpp b/extras/test/src/test_CloudColor.cpp index aaf50a76e..8d3f2d873 100644 --- a/extras/test/src/test_CloudColor.cpp +++ b/extras/test/src/test_CloudColor.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include /************************************************************************************** diff --git a/extras/test/src/test_callback.cpp b/extras/test/src/test_callback.cpp index 0a29af4a6..f95e4c6da 100644 --- a/extras/test/src/test_callback.cpp +++ b/extras/test/src/test_callback.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include "types/CloudWrapperBool.h" @@ -70,7 +70,7 @@ SCENARIO("A (boolean) property is manipulated in the callback to its origin stat GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); thing.addPropertyReal(switch_turned_on, "switch_turned_on", Permission::ReadWrite).onUpdate(switch_callback); @@ -88,7 +88,7 @@ SCENARIO("A (boolean) property is manipulated in the callback to its origin stat /* [{0: "switch_turned_on", 4: false}] = 9F A2 00 70 73 77 69 74 63 68 5F 74 75 72 6E 65 64 5F 6F 6E 04 F4 FF*/ std::vector const expected = {0x9F, 0xA2, 0x00, 0x70, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x5F, 0x74, 0x75, 0x72, 0x6E, 0x65, 0x64, 0x5F, 0x6F, 0x6E, 0x04, 0xF4, 0xFF}; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } diff --git a/extras/test/src/test_decode.cpp b/extras/test/src/test_decode.cpp index 0ecd98227..9b89bb177 100644 --- a/extras/test/src/test_decode.cpp +++ b/extras/test/src/test_decode.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include "types/CloudWrapperBool.h" #include "types/CloudWrapperFloat.h" diff --git a/extras/test/src/test_encode.cpp b/extras/test/src/test_encode.cpp index ebd384b4f..f81d6701c 100644 --- a/extras/test/src/test_encode.cpp +++ b/extras/test/src/test_encode.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include #include "types/CloudWrapperBool.h" #include "types/CloudWrapperFloat.h" @@ -26,14 +26,14 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudBool test = true; thing.addPropertyReal(test, "test", Permission::ReadWrite); /* [{0: "test", 4: true}] = 9F A2 00 64 74 65 73 74 04 F5 FF*/ std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5, 0xFF}; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -45,7 +45,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudBool test = true; /*The property is added with identifier 1 that will be used instead of the string "test" as property identifier*/ @@ -53,7 +53,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") /* [{0: 1, 4: true}] = 9F A2 00 01 04 F5 FF*/ std::vector const expected = {0x9F, 0xA2, 0x00, 0x01, 0x04, 0xF5, 0xFF}; - std::vector const actual = encode(thing, true); + std::vector const actual = cbor::encode(thing, true); REQUIRE(actual == expected); } } @@ -64,14 +64,14 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudInt int_test = 123; thing.addPropertyReal(int_test, "test", Permission::ReadWrite); /* [{0: "test", 3: 123}] = 9F A2 00 64 74 65 73 74 02 18 7B FF */ std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0x18, 0x7B, 0xFF}; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -82,14 +82,14 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudFloat float_test = 3.14159; thing.addPropertyReal(float_test, "test", Permission::ReadWrite); /* [{0: "test", 2: 3.141590118408203}] = 9F A2 00 64 74 65 73 74 02 FA 40 49 0F D0 FF */ std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0xFA, 0x40, 0x49, 0x0F, 0xD0, 0xFF}; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -100,7 +100,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudString string_test; string_test = "test"; @@ -108,7 +108,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") /* [{0: "test", 3: "test"}] = 9F A2 00 64 74 65 73 74 03 64 74 65 73 74 FF*/ std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x03, 0x64, 0x74, 0x65, 0x73, 0x74, 0xFF}; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -119,14 +119,14 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudLocation location_test = CloudLocation(2.0f, 3.0f); thing.addPropertyReal(location_test, "test", Permission::ReadWrite); /* [{0: "test:lat", 3: 2},{0: "test:lon", 3: 3}] = 9F A2 00 68 74 65 73 74 3A 6C 61 74 02 FA 40 00 00 00 A2 00 68 74 65 73 74 3A 6C 6F 6E 02 FA 40 40 00 00 FF*/ std::vector const expected = { 0x9F, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6C, 0x61, 0x74, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6C, 0x6F, 0x6E, 0x02, 0xFA, 0x40, 0x40, 0x00, 0x00, 0xFF }; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -137,14 +137,14 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudColor color_test = CloudColor(2.0, 2.0, 2.0); thing.addPropertyReal(color_test, "test", Permission::ReadWrite); /* [{0: "test:hue", 2: 2.0},{0: "test:sat", 2: 2.0},{0: "test:bri", 2: 2.0}] = 9F A2 00 68 74 65 73 74 3A 68 75 65 02 FA 40 00 00 00 A2 00 68 74 65 73 74 3A 73 61 74 02 FA 40 00 00 00 A2 00 68 74 65 73 74 3A 62 72 69 02 FA 40 00 00 00 FF*/ std::vector const expected = {0x9F, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x68, 0x75, 0x65, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x61, 0x74, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x62, 0x72, 0x69, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xFF }; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -156,7 +156,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudColor color_test = CloudColor(2.0, 2.0, 2.0); /*The property is added with identifier 1 that will be used instead of the string "name" as property identifier */ @@ -164,7 +164,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") /* [{0: 257, 2: 2.0},{0: 513, 2: 2.0},{0: 769, 2: 2.0}] = 9F A2 00 19 01 01 02 FA 40 00 00 00 A2 00 19 02 01 02 FA 40 00 00 00 A2 00 19 03 01 02 FA 40 00 00 00 FF*/ std::vector const expected = {0x9F, 0xA2, 0x00, 0x19, 0x01, 0x01, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x19, 0x02, 0x01, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x19, 0x03, 0x01, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xFF }; - std::vector const actual = encode(thing, true); + std::vector const actual = cbor::encode(thing, true); REQUIRE(actual == expected); } } @@ -175,14 +175,14 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudColoredLight color_test = CloudColoredLight(true, 2.0, 2.0, 2.0); thing.addPropertyReal(color_test, "test", Permission::ReadWrite); /* [{0: "test:swi", 4: true},{0: "test:hue", 2: 2.0},{0: "test:sat", 2: 2.0},{0: "test:bri", 2: 2.0}] = 83 A2 00 68 74 65 73 74 3A 73 77 69 04 F5 //A2 00 68 74 65 73 74 3A 68 75 65 02 FA 40 00 00 00 A2 00 68 74 65 73 74 3A 73 61 74 02 FA 40 00 00 00 A2 00 68 74 65 73 74 3A 62 72 69 02 FA 40 00 00 00 FF*/ std::vector const expected = {0x9F, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x77, 0x69, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x68, 0x75, 0x65, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x61, 0x74, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x62, 0x72, 0x69, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xFF }; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -193,14 +193,14 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudTelevision tv_test = CloudTelevision(true, 50, false, PlaybackCommands::Play, InputValue::TV, 7); thing.addPropertyReal(tv_test, "test", Permission::ReadWrite); /* [{0: "test:swi", 4: true},{0: "test:vol", 2: 50},{0: "test:mut", 2: false},{0: "test:pbc", 2: 3},{0: "test:inp", 2: 55},{0: "test:cha", 2: 7}] = 9F A2 00 68 74 65 73 74 3A 73 77 69 04 F5 A2 00 68 74 65 73 74 3A 76 6F 6C 02 18 32 A2 00 68 74 65 73 74 3A 6D 75 74 04 F4 A2 00 68 74 65 73 74 3A 70 62 63 02 03 A2 00 68 74 65 73 74 3A 69 6E 70 02 18 37 A2 00 68 74 65 73 74 3A 63 68 61 02 07 FF */ std::vector const expected = {0x9F, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x77, 0x69, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x76, 0x6F, 0x6C, 0x02, 0x18, 0x32, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x6D, 0x75, 0x74, 0x04, 0xF4, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x70, 0x62, 0x63, 0x02, 0x03, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x69, 0x6E, 0x70, 0x02, 0x18, 0x37, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x63, 0x68, 0x61, 0x02, 0x07, 0xFF}; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -211,14 +211,14 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudDimmedLight color_test = CloudDimmedLight(true, 2.0); thing.addPropertyReal(color_test, "test", Permission::ReadWrite); /* [{0: "test:swi", 4: true},{0: "test:hue", 2: 0.0},{0: "test:sat", 2: 0.0},{0: "test:bri", 2: 2.0}] = 83 A2 00 68 74 65 73 74 3A 73 77 69 04 F5 //A2 00 68 74 65 73 74 3A 68 75 65 02 FA 00 00 00 00 A2 00 68 74 65 73 74 3A 73 61 74 02 FA 00 00 00 00 A2 00 68 74 65 73 74 3A 62 72 69 02 FA 40 00 00 00 FF*/ std::vector const expected = {0x9F, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x77, 0x69, 0x04, 0xF5, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x68, 0x75, 0x65, 0x02, 0xFA, 0x00, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x73, 0x61, 0x74, 0x02, 0xFA, 0x00, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x74, 0x65, 0x73, 0x74, 0x3A, 0x62, 0x72, 0x69, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xFF }; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -229,7 +229,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudLight test; test = true; @@ -237,7 +237,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") /* [{0: "test", 4: true}] = 9F A2 00 64 74 65 73 74 04 F5 FF*/ std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5, 0xFF}; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -248,7 +248,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudContactSensor test; test = true; @@ -256,7 +256,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") /* [{0: "test", 4: true}] = 9F A2 00 64 74 65 73 74 04 F5 FF*/ std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5, 0xFF}; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -267,7 +267,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudMotionSensor test; test = true; @@ -275,7 +275,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") /* [{0: "test", 4: true}] = 9F A2 00 64 74 65 73 74 04 F5 FF*/ std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5, 0xFF}; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -286,7 +286,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudSmartPlug test; test = true; @@ -294,7 +294,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") /* [{0: "test", 4: true}] = 9F A2 00 64 74 65 73 74 04 F5 FF*/ std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5, 0xFF}; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -305,7 +305,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudTemperature float_test; float_test = 3.14159; @@ -313,7 +313,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") /* [{0: "test", 2: 3.141590118408203}] = 9F A2 00 64 74 65 73 74 02 FA 40 49 0F D0 FF */ std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x02, 0xFA, 0x40, 0x49, 0x0F, 0xD0, 0xFF}; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -324,7 +324,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") GIVEN("CloudProtocol::V2") { ArduinoCloudThing thing; thing.begin(); - encode(thing); + cbor::encode(thing); CloudSwitch test; test = true; @@ -332,7 +332,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") /* [{0: "test", 4: true}] = 9F A2 00 64 74 65 73 74 04 F5 FF*/ std::vector const expected = {0x9F, 0xA2, 0x00, 0x64, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF5, 0xFF}; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -359,7 +359,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") = 9F A2 00 68 69 6E 74 5F 74 65 73 74 02 01 A2 00 6A 66 6C 6F 61 74 5F 74 65 73 74 02 FA A2 00 6A 66 6C 6F 61 74 5F 74 65 73 74 02 FA 40 00 00 00 A2 00 68 73 74 72 5F 74 65 73 74 03 68 73 74 72 5F 74 65 73 74 FF */ std::vector const expected = {0x9F, 0xA2, 0x00, 0x68, 0x69, 0x6E, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x02, 0x01, 0xA2, 0x00, 0x69, 0x62, 0x6F, 0x6F, 0x6C, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF4, 0xA2, 0x00, 0x6A, 0x66, 0x6C, 0x6F, 0x61, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x73, 0x74, 0x72, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x03, 0x68, 0x73, 0x74, 0x72, 0x5F, 0x74, 0x65, 0x73, 0x74, 0xFF}; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } @@ -393,7 +393,7 @@ SCENARIO("Arduino Cloud Properties are encoded", "[ArduinoCloudThing::encode]") = 9F A2 00 68 69 6E 74 5F 74 65 73 74 02 01 A2 00 6A 66 6C 6F 61 74 5F 74 65 73 74 02 FA A2 00 6A 66 6C 6F 61 74 5F 74 65 73 74 02 FA 40 00 00 00 A2 00 68 73 74 72 5F 74 65 73 74 03 68 73 74 72 5F 74 65 73 74 FF */ std::vector const expected = {0x9F, 0xA2, 0x00, 0x68, 0x69, 0x6E, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x02, 0x01, 0xA2, 0x00, 0x69, 0x62, 0x6F, 0x6F, 0x6C, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x04, 0xF4, 0xA2, 0x00, 0x6A, 0x66, 0x6C, 0x6F, 0x61, 0x74, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x02, 0xFA, 0x40, 0x00, 0x00, 0x00, 0xA2, 0x00, 0x68, 0x73, 0x74, 0x72, 0x5F, 0x74, 0x65, 0x73, 0x74, 0x03, 0x68, 0x73, 0x74, 0x72, 0x5F, 0x74, 0x65, 0x73, 0x74, 0xFF}; - std::vector const actual = encode(thing); + std::vector const actual = cbor::encode(thing); REQUIRE(actual == expected); } } diff --git a/extras/test/src/test_publishEvery.cpp b/extras/test/src/test_publishEvery.cpp index f0e536c6f..0de63ad41 100644 --- a/extras/test/src/test_publishEvery.cpp +++ b/extras/test/src/test_publishEvery.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include /************************************************************************************** @@ -30,23 +30,23 @@ SCENARIO("A Arduino cloud property is published periodically", "[ArduinoCloudThi WHEN("t = 0 ms, publish interval = 1000 ms, 1st call to 'encode'") { set_millis(0); THEN("'encode' should encode the property") { - REQUIRE(encode(thing).size() != 0); + REQUIRE(cbor::encode(thing).size() != 0); WHEN("t = 999 ms") { set_millis(999); THEN("'encode' should not encode the property") { - REQUIRE(encode(thing).size() == 0); + REQUIRE(cbor::encode(thing).size() == 0); WHEN("t = 1000 ms") { set_millis(1000); THEN("'encode' should encode the property") { - REQUIRE(encode(thing).size() != 0); + REQUIRE(cbor::encode(thing).size() != 0); WHEN("t = 1999 ms") { set_millis(1999); THEN("'encode' should not encode the property") { - REQUIRE(encode(thing).size() == 0); + REQUIRE(cbor::encode(thing).size() == 0); WHEN("t = 2000 ms") { set_millis(2000); THEN("'encode' should encode the property") { - REQUIRE(encode(thing).size() != 0); + REQUIRE(cbor::encode(thing).size() != 0); } } } diff --git a/extras/test/src/test_publishOnChange.cpp b/extras/test/src/test_publishOnChange.cpp index 799a6e0e4..a96f508c1 100644 --- a/extras/test/src/test_publishOnChange.cpp +++ b/extras/test/src/test_publishOnChange.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include /************************************************************************************** @@ -29,15 +29,15 @@ SCENARIO("A Arduino cloud property is published on value change", "[ArduinoCloud WHEN("test = 10, delta = 6, the property is encoded for the 1st time") { THEN("The property should be encoded") { - REQUIRE(encode(thing).size() != 0); + REQUIRE(cbor::encode(thing).size() != 0); WHEN("test +=4 -> test = 14") { test += 4; THEN("Since the increment since the last update (4) is smaller than the delta of 6 the property should not be encoded") { - REQUIRE(encode(thing).size() == 0); + REQUIRE(cbor::encode(thing).size() == 0); WHEN("test +=4 -> test = 18") { test += 4; THEN("Since the increment since the last update (8) is greater than the delta of 6 the property should be encoded") { - REQUIRE(encode(thing).size() != 0); + REQUIRE(cbor::encode(thing).size() != 0); } } } diff --git a/extras/test/src/test_publishOnChangeRateLimit.cpp b/extras/test/src/test_publishOnChangeRateLimit.cpp index c03426c7a..ab52afa3c 100644 --- a/extras/test/src/test_publishOnChangeRateLimit.cpp +++ b/extras/test/src/test_publishOnChangeRateLimit.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include /************************************************************************************** @@ -31,27 +31,27 @@ SCENARIO("A Arduino cloud property is published on value change but the update r WHEN("t = 0 ms, min time between updates = 500 ms, property not modified, 1st call to 'encode'") { set_millis(0); THEN("'encode' should encode the property") { - REQUIRE(encode(thing).size() != 0); + REQUIRE(cbor::encode(thing).size() != 0); WHEN("t = 499 ms, property modified") { test++; set_millis(499); THEN("'encode' should not encode any property") { - REQUIRE(encode(thing).size() == 0); + REQUIRE(cbor::encode(thing).size() == 0); WHEN("t = 500 ms, property modified") { test++; set_millis(500); THEN("'encode' should encode the property") { - REQUIRE(encode(thing).size() != 0); + REQUIRE(cbor::encode(thing).size() != 0); WHEN("t = 999 ms, property modified") { test++; set_millis(999); THEN("'encode' should not encode any property") { - REQUIRE(encode(thing).size() == 0); + REQUIRE(cbor::encode(thing).size() == 0); WHEN("t = 1000 ms, property modified") { test++; set_millis(1000); THEN("'encode' should encode the property") { - REQUIRE(encode(thing).size() != 0); + REQUIRE(cbor::encode(thing).size() != 0); } } } diff --git a/extras/test/src/test_readOnly.cpp b/extras/test/src/test_readOnly.cpp index e7421f228..a090d512e 100644 --- a/extras/test/src/test_readOnly.cpp +++ b/extras/test/src/test_readOnly.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include /************************************************************************************** diff --git a/extras/test/src/test_writeOnly.cpp b/extras/test/src/test_writeOnly.cpp index 6a3122e00..187cab4bb 100644 --- a/extras/test/src/test_writeOnly.cpp +++ b/extras/test/src/test_writeOnly.cpp @@ -8,7 +8,7 @@ #include -#include +#include #include /************************************************************************************** @@ -24,7 +24,7 @@ SCENARIO("A Arduino cloud property is marked 'write only'", "[ArduinoCloudThing: CloudInt test = 0; thing.addPropertyReal(test, "test", Permission::Write); - REQUIRE(encode(thing).size() == 0); /* Since 'test' is 'write only' it should not be encoded */ + REQUIRE(cbor::encode(thing).size() == 0); /* Since 'test' is 'write only' it should not be encoded */ /************************************************************************************/ } diff --git a/extras/test/src/util/TestUtil.cpp b/extras/test/src/util/CBORTestUtil.cpp similarity index 72% rename from extras/test/src/util/TestUtil.cpp rename to extras/test/src/util/CBORTestUtil.cpp index c249b554f..2599fc453 100644 --- a/extras/test/src/util/TestUtil.cpp +++ b/extras/test/src/util/CBORTestUtil.cpp @@ -6,11 +6,18 @@ INCLUDE **************************************************************************************/ -#include +#include #include #include +/************************************************************************************** + NAMESPACE + **************************************************************************************/ + +namespace cbor +{ + /************************************************************************************** PUBLIC FUNCTIONS **************************************************************************************/ @@ -38,3 +45,9 @@ void print(std::vector const & vect) { } std::cout << std::endl; } + +/************************************************************************************** + NAMESPACE + **************************************************************************************/ + +} /* cbor */ From ca37b6c4703e2fa59ac34c788dde3f507d039d8a Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Thu, 4 Jun 2020 06:44:58 +0200 Subject: [PATCH 174/175] Renaming OTATestDataGenerator.h/.cpp into OTATestUtil.h/.cpp and encapsulating its methods into namespace OTA --- extras/test/CMakeLists.txt | 2 +- .../{OTATestDataGenerator.h => OTATestUtil.h} | 12 ++++++++++++ extras/test/src/test_OTALogic.cpp | 12 ++++++------ .../{OTATestDataGenerator.cpp => OTATestUtil.cpp} | 15 ++++++++++++++- 4 files changed, 33 insertions(+), 8 deletions(-) rename extras/test/include/util/{OTATestDataGenerator.h => OTATestUtil.h} (72%) rename extras/test/src/util/{OTATestDataGenerator.cpp => OTATestUtil.cpp} (80%) diff --git a/extras/test/CMakeLists.txt b/extras/test/CMakeLists.txt index 554938879..93af710b8 100644 --- a/extras/test/CMakeLists.txt +++ b/extras/test/CMakeLists.txt @@ -44,7 +44,7 @@ set(TEST_SRCS src/test_writeOnly.cpp src/util/CBORTestUtil.cpp - src/util/OTATestDataGenerator.cpp + src/util/OTATestUtil.cpp ../../src/utility/ota/crc.cpp ../../src/utility/ota/OTALogic.cpp diff --git a/extras/test/include/util/OTATestDataGenerator.h b/extras/test/include/util/OTATestUtil.h similarity index 72% rename from extras/test/include/util/OTATestDataGenerator.h rename to extras/test/include/util/OTATestUtil.h index 42b8ba36a..640d422a2 100644 --- a/extras/test/include/util/OTATestDataGenerator.h +++ b/extras/test/include/util/OTATestUtil.h @@ -11,6 +11,13 @@ #include +/************************************************************************************** + NAMESPACE + **************************************************************************************/ + +namespace ota +{ + /************************************************************************************** TYPEDEF **************************************************************************************/ @@ -33,5 +40,10 @@ union OTAData void generate_valid_ota_data(OTAData & ota_data); void generate_invalid_ota_data_crc_wrong(OTAData & ota_data); +/************************************************************************************** + NAMESPACE + **************************************************************************************/ + +} /* ota */ #endif /* OTA_TEST_DATA_GENERATOR_H_ */ diff --git a/extras/test/src/test_OTALogic.cpp b/extras/test/src/test_OTALogic.cpp index 7765f0396..ba3833623 100644 --- a/extras/test/src/test_OTALogic.cpp +++ b/extras/test/src/test_OTALogic.cpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include @@ -27,7 +27,7 @@ using namespace fakeit; TEST HELPER **************************************************************************************/ -void simulateOTABinaryReception(OTALogic & ota_logic, OTAData const & ota_test_data) +void simulateOTABinaryReception(OTALogic & ota_logic, ota::OTAData const & ota_test_data) { uint32_t bytes_written = 0; uint32_t const bytes_to_write = sizeof(uint32_t) + sizeof(uint32_t) + ota_test_data.data.len; @@ -212,8 +212,8 @@ TEST_CASE("Valid OTA data is received ", "[OTALogic]") /* Generate test data */ - OTAData valid_ota_test_data; - generate_valid_ota_data(valid_ota_test_data); + ota::OTAData valid_ota_test_data; + ota::generate_valid_ota_data(valid_ota_test_data); /* Perform test */ @@ -259,8 +259,8 @@ TEST_CASE("Invalid OTA data is received ", "[OTALogic - CRC wrong]") /* Generate test data */ - OTAData invalid_valid_ota_test_data_crc_wrong; - generate_invalid_ota_data_crc_wrong(invalid_valid_ota_test_data_crc_wrong); + ota::OTAData invalid_valid_ota_test_data_crc_wrong; + ota::generate_invalid_ota_data_crc_wrong(invalid_valid_ota_test_data_crc_wrong); /* Perform test */ diff --git a/extras/test/src/util/OTATestDataGenerator.cpp b/extras/test/src/util/OTATestUtil.cpp similarity index 80% rename from extras/test/src/util/OTATestDataGenerator.cpp rename to extras/test/src/util/OTATestUtil.cpp index 97d470595..ac7e26026 100644 --- a/extras/test/src/util/OTATestDataGenerator.cpp +++ b/extras/test/src/util/OTATestUtil.cpp @@ -6,12 +6,19 @@ INCLUDE **************************************************************************************/ -#include +#include #include #include +/************************************************************************************** + NAMESPACE + **************************************************************************************/ + +namespace ota +{ + /************************************************************************************** FUNCTION DEFINITION **************************************************************************************/ @@ -58,3 +65,9 @@ void generate_invalid_ota_data_crc_wrong(OTAData & ota_data) /* Generate CRC */ ota_data.data.crc32 = 0xDEADBEEF; } + +/************************************************************************************** + NAMESPACE + **************************************************************************************/ + +} /* ota */ From d47720f93faf86ce802c4189555f7ab9cfa04655 Mon Sep 17 00:00:00 2001 From: Alexander Entinger Date: Thu, 4 Jun 2020 06:49:23 +0200 Subject: [PATCH 175/175] Refactoring CMakeLists.txt by introducing variables holding the various test sources --- extras/test/CMakeLists.txt | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/extras/test/CMakeLists.txt b/extras/test/CMakeLists.txt index 93af710b8..153f81b62 100644 --- a/extras/test/CMakeLists.txt +++ b/extras/test/CMakeLists.txt @@ -22,13 +22,11 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) ########################################################################## -set(TEST_TARGET testArduinoIoTCloud) +set(TEST_TARGET ${CMAKE_PROJECT_NAME}) -set(TEST_SRCS - src/Arduino.cpp - - src/test_main.cpp +########################################################################## +set(TEST_SRCS src/test_OTALogic.cpp src/test_addPropertyReal.cpp @@ -42,10 +40,14 @@ set(TEST_SRCS src/test_publishOnChangeRateLimit.cpp src/test_readOnly.cpp src/test_writeOnly.cpp +) +set(TEST_UTIL_SRCS src/util/CBORTestUtil.cpp src/util/OTATestUtil.cpp +) +set(TEST_DUT_SRCS ../../src/utility/ota/crc.cpp ../../src/utility/ota/OTALogic.cpp @@ -65,6 +67,16 @@ set(TEST_SRCS ########################################################################## +set(TEST_TARGET_SRCS + src/Arduino.cpp + src/test_main.cpp + ${TEST_SRCS} + ${TEST_UTIL_SRCS} + ${TEST_DUT_SRCS} +) + +########################################################################## + add_compile_definitions(HOST) add_compile_options(-Wall -Wextra -Wpedantic -Werror) @@ -75,7 +87,7 @@ set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "--coverage") add_executable( ${TEST_TARGET} - ${TEST_SRCS} + ${TEST_TARGET_SRCS} ) ##########################################################################