Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: easy-connect-v16 Watchdog FP MQTTPacket RecordType-v-16 watersenor_and_temp_code
Revision 11:3802c82a5ae9, committed 2017-12-07
- Comitter:
- DuyLionTran
- Date:
- Thu Dec 07 17:07:44 2017 +0000
- Parent:
- 10:5d45f805080a
- Child:
- 12:bc4f1214e097
- Commit message:
- version 1.5 MQTT works oke
Changed in this revision
--- a/MQTT.lib Sat Dec 02 04:59:59 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -https://os.mbed.com/users/DuyLionTran/code/MQTT_Test/#0934f0842804
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MQTT/FP.lib Thu Dec 07 17:07:44 2017 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/sam_grove/code/FP/#3c62ba1807ac
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MQTT/MQTTAsync.h Thu Dec 07 17:07:44 2017 +0000
@@ -0,0 +1,607 @@
+/*******************************************************************************
+ * 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
+ *******************************************************************************/
+
+#if !defined(MQTTASYNC_H)
+#define MQTTASYNC_H
+
+#include "FP.h"
+#include "MQTTPacket.h"
+#include "stdio.h"
+
+namespace MQTT
+{
+
+
+enum QoS { QOS0, QOS1, QOS2 };
+
+
+struct Message
+{
+ enum QoS qos;
+ bool retained;
+ bool dup;
+ unsigned short id;
+ void *payload;
+ size_t payloadlen;
+};
+
+
+class PacketId
+{
+public:
+ PacketId();
+
+ int getNext();
+
+private:
+ static const int MAX_PACKET_ID = 65535;
+ int next;
+};
+
+typedef void (*messageHandler)(Message*);
+
+typedef struct limits
+{
+ int MAX_MQTT_PACKET_SIZE; //
+ int MAX_MESSAGE_HANDLERS; // each subscription requires a message handler
+ int MAX_CONCURRENT_OPERATIONS; // each command which runs concurrently can have a result handler, when we are in multi-threaded mode
+ int command_timeout_ms;
+
+ limits()
+ {
+ MAX_MQTT_PACKET_SIZE = 100;
+ MAX_MESSAGE_HANDLERS = 5;
+ MAX_CONCURRENT_OPERATIONS = 1; // 1 indicates single-threaded mode - set to >1 for multithreaded mode
+ command_timeout_ms = 30000;
+ }
+} Limits;
+
+
+/**
+ * @class Async
+ * @brief non-blocking, threaded MQTT client API
+ * @param Network a network class which supports send, receive
+ * @param Timer a timer class with the methods:
+ */
+template<class Network, class Timer, class Thread, class Mutex> class Async
+{
+
+public:
+
+ struct Result
+ {
+ /* success or failure result data */
+ Async<Network, Timer, Thread, Mutex>* client;
+ int rc;
+ };
+
+ typedef void (*resultHandler)(Result*);
+
+ Async(Network* network, const Limits limits = Limits());
+
+ typedef struct
+ {
+ Async* client;
+ Network* network;
+ } connectionLostInfo;
+
+ typedef int (*connectionLostHandlers)(connectionLostInfo*);
+
+ /** Set the connection lost callback - called whenever the connection is lost and we should be connected
+ * @param clh - pointer to the callback function
+ */
+ void setConnectionLostHandler(connectionLostHandlers clh)
+ {
+ connectionLostHandler.attach(clh);
+ }
+
+ /** 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);
+ }
+
+ int connect(resultHandler fn, MQTTPacket_connectData* options = 0);
+
+ template<class T>
+ int connect(void(T::*method)(Result *), MQTTPacket_connectData* options = 0, T *item = 0); // alternative to pass in pointer to member function
+
+ int publish(resultHandler rh, const char* topic, Message* message);
+
+ int subscribe(resultHandler rh, const char* topicFilter, enum QoS qos, messageHandler mh);
+
+ int unsubscribe(resultHandler rh, const char* topicFilter);
+
+ int disconnect(resultHandler rh);
+
+private:
+
+ void run(void const *argument);
+ int cycle(int timeout);
+ int waitfor(int packet_type, Timer& atimer);
+ int keepalive();
+ int findFreeOperation();
+
+ int decodePacket(int* value, int timeout);
+ int readPacket(int timeout);
+ int sendPacket(int length, int timeout);
+ int deliverMessage(MQTTString* topic, Message* message);
+
+ Thread* thread;
+ Network* ipstack;
+
+ Limits limits;
+
+ char* buf;
+ char* readbuf;
+
+ Timer ping_timer, connect_timer;
+ unsigned int keepAliveInterval;
+ bool ping_outstanding;
+
+ PacketId packetid;
+
+ typedef FP<void, Result*> resultHandlerFP;
+ resultHandlerFP connectHandler;
+
+ typedef FP<void, Message*> messageHandlerFP;
+ struct MessageHandlers
+ {
+ const char* topic;
+ messageHandlerFP fp;
+ } *messageHandlers; // Message handlers are indexed by subscription topic
+
+ // how many concurrent operations should we allow? Each one will require a function pointer
+ struct Operations
+ {
+ unsigned short id;
+ resultHandlerFP fp;
+ const char* topic; // if this is a publish, store topic name in case republishing is required
+ Message* message; // for publish,
+ Timer timer; // to check if the command has timed out
+ } *operations; // result handlers are indexed by packet ids
+
+ static void threadfn(void* arg);
+
+ messageHandlerFP defaultMessageHandler;
+
+ typedef FP<int, connectionLostInfo*> connectionLostFP;
+
+ connectionLostFP connectionLostHandler;
+
+};
+
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> void MQTT::Async<Network, Timer, Thread, Mutex>::threadfn(void* arg)
+{
+ ((Async<Network, Timer, Thread, Mutex>*) arg)->run(NULL);
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> MQTT::Async<Network, Timer, Thread, Mutex>::Async(Network* network, Limits limits) : limits(limits), packetid()
+{
+ this->thread = 0;
+ this->ipstack = network;
+ this->ping_timer = Timer();
+ this->ping_outstanding = 0;
+
+ // How to make these memory allocations portable? I was hoping to avoid the heap
+ buf = new char[limits.MAX_MQTT_PACKET_SIZE];
+ readbuf = new char[limits.MAX_MQTT_PACKET_SIZE];
+ this->operations = new struct Operations[limits.MAX_CONCURRENT_OPERATIONS];
+ for (int i = 0; i < limits.MAX_CONCURRENT_OPERATIONS; ++i)
+ operations[i].id = 0;
+ this->messageHandlers = new struct MessageHandlers[limits.MAX_MESSAGE_HANDLERS];
+ for (int i = 0; i < limits.MAX_MESSAGE_HANDLERS; ++i)
+ messageHandlers[i].topic = 0;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::sendPacket(int length, int timeout)
+{
+ int sent = 0;
+
+ while (sent < length)
+ sent += ipstack->write(&buf[sent], length, timeout);
+ if (sent == length)
+ ping_timer.countdown(this->keepAliveInterval); // record the fact that we have successfully sent the packet
+ return sent;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::decodePacket(int* value, int timeout)
+{
+ 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<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::readPacket(int timeout)
+{
+ 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 (ipstack->read(readbuf, 1, timeout) != 1)
+ goto exit;
+
+ len = 1;
+ /* 2. read the remaining length. This is variable in itself */
+ decodePacket(&rem_len, timeout);
+ len += MQTTPacket_encode(readbuf + 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 (ipstack->read(readbuf + len, rem_len, timeout) != rem_len)
+ goto exit;
+
+ header.byte = readbuf[0];
+ rc = header.bits.type;
+exit:
+ return rc;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::deliverMessage(MQTTString* topic, Message* message)
+{
+ int rc = -1;
+
+ // we have to find the right message handler - indexed by topic
+ for (int i = 0; i < limits.MAX_MESSAGE_HANDLERS; ++i)
+ {
+ if (messageHandlers[i].topic != 0 && MQTTPacket_equals(topic, (char*)messageHandlers[i].topic))
+ {
+ messageHandlers[i].fp(message);
+ rc = 0;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::cycle(int timeout)
+{
+ /* get one piece of work off the wire and one pass through */
+
+ // read the socket, see what work is due
+ int packet_type = readPacket(timeout);
+
+ int len, rc;
+ switch (packet_type)
+ {
+ case CONNACK:
+ if (this->thread)
+ {
+ Result res = {this, 0};
+ if (MQTTDeserialize_connack(&res.rc, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1)
+ ;
+ connectHandler(&res);
+ connectHandler.detach(); // only invoke the callback once
+ }
+ break;
+ case PUBACK:
+ if (this->thread)
+ ; //call resultHandler
+ case SUBACK:
+ break;
+ case PUBLISH:
+ MQTTString topicName;
+ Message msg;
+ rc = MQTTDeserialize_publish((int*)&msg.dup, (int*)&msg.qos, (int*)&msg.retained, (int*)&msg.id, &topicName,
+ (char**)&msg.payload, (int*)&msg.payloadlen, readbuf, limits.MAX_MQTT_PACKET_SIZE);;
+ if (msg.qos == QOS0)
+ deliverMessage(&topicName, &msg);
+ break;
+ case PUBREC:
+ int type, dup, mypacketid;
+ if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1)
+ ;
+ // must lock this access against the application thread, if we are multi-threaded
+ len = MQTTSerialize_ack(buf, limits.MAX_MQTT_PACKET_SIZE, PUBREL, 0, mypacketid);
+ rc = sendPacket(len, timeout); // send the PUBREL packet
+ if (rc != len)
+ goto exit; // there was a problem
+
+ break;
+ case PUBCOMP:
+ break;
+ case PINGRESP:
+ ping_outstanding = false;
+ break;
+ }
+ keepalive();
+exit:
+ return packet_type;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::keepalive()
+{
+ int rc = 0;
+
+ if (keepAliveInterval == 0)
+ goto exit;
+
+ if (ping_timer.expired())
+ {
+ if (ping_outstanding)
+ rc = -1;
+ else
+ {
+ int len = MQTTSerialize_pingreq(buf, limits.MAX_MQTT_PACKET_SIZE);
+ rc = sendPacket(len, 1000); // send the ping packet
+ if (rc != len)
+ rc = -1; // indicate there's a problem
+ else
+ ping_outstanding = true;
+ }
+ }
+
+exit:
+ return rc;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> void MQTT::Async<Network, Timer, Thread, Mutex>::run(void const *argument)
+{
+ while (true)
+ cycle(ping_timer.left_ms());
+}
+
+
+// only used in single-threaded mode where one command at a time is in process
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::waitfor(int packet_type, Timer& atimer)
+{
+ int rc = -1;
+
+ do
+ {
+ if (atimer.expired())
+ break; // we timed out
+ }
+ while ((rc = cycle(atimer.left_ms())) != packet_type);
+
+ return rc;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::connect(resultHandler resultHandler, MQTTPacket_connectData* options)
+{
+ connect_timer.countdown(limits.command_timeout_ms);
+
+ MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer;
+ if (options == 0)
+ options = &default_options; // set default options if none were supplied
+
+ this->keepAliveInterval = options->keepAliveInterval;
+ ping_timer.countdown(this->keepAliveInterval);
+ int len = MQTTSerialize_connect(buf, limits.MAX_MQTT_PACKET_SIZE, options);
+ int rc = sendPacket(len, connect_timer.left_ms()); // send the connect packet
+ if (rc != len)
+ goto exit; // there was a problem
+
+ if (resultHandler == 0) // wait until the connack is received
+ {
+ // this will be a blocking call, wait for the connack
+ if (waitfor(CONNACK, connect_timer) == CONNACK)
+ {
+ int connack_rc = -1;
+ if (MQTTDeserialize_connack(&connack_rc, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1)
+ rc = connack_rc;
+ }
+ }
+ else
+ {
+ // set connect response callback function
+ connectHandler.attach(resultHandler);
+
+ // start background thread
+ this->thread = new Thread((void (*)(void const *argument))&MQTT::Async<Network, Timer, Thread, Mutex>::threadfn, (void*)this);
+ }
+
+exit:
+ return rc;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::findFreeOperation()
+{
+ int found = -1;
+ for (int i = 0; i < limits.MAX_CONCURRENT_OPERATIONS; ++i)
+ {
+ if (operations[i].id == 0)
+ {
+ found = i;
+ break;
+ }
+ }
+ return found;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::subscribe(resultHandler resultHandler, const char* topicFilter, enum QoS qos, messageHandler messageHandler)
+{
+ int index = 0;
+ if (this->thread)
+ index = findFreeOperation();
+ Timer& atimer = operations[index].timer;
+
+ atimer.countdown(limits.command_timeout_ms);
+ MQTTString topic = {(char*)topicFilter, 0, 0};
+
+ int len = MQTTSerialize_subscribe(buf, limits.MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic, (int*)&qos);
+ int rc = sendPacket(len, atimer.left_ms()); // send the subscribe packet
+ if (rc != len)
+ goto exit; // there was a problem
+
+ /* wait for suback */
+ if (resultHandler == 0)
+ {
+ // this will block
+ if (waitfor(SUBACK, atimer) == SUBACK)
+ {
+ int count = 0, grantedQoS = -1, mypacketid;
+ if (MQTTDeserialize_suback(&mypacketid, 1, &count, &grantedQoS, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1)
+ rc = grantedQoS; // 0, 1, 2 or 0x80
+ if (rc != 0x80)
+ {
+ for (int i = 0; i < limits.MAX_MESSAGE_HANDLERS; ++i)
+ {
+ if (messageHandlers[i].topic == 0)
+ {
+ messageHandlers[i].topic = topicFilter;
+ messageHandlers[i].fp.attach(messageHandler);
+ rc = 0;
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // set subscribe response callback function
+
+ }
+
+exit:
+ return rc;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::unsubscribe(resultHandler resultHandler, const char* topicFilter)
+{
+ int index = 0;
+ if (this->thread)
+ index = findFreeOperation();
+ Timer& atimer = operations[index].timer;
+
+ atimer.countdown(limits.command_timeout_ms);
+ MQTTString topic = {(char*)topicFilter, 0, 0};
+
+ int len = MQTTSerialize_unsubscribe(buf, limits.MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic);
+ int rc = sendPacket(len, atimer.left_ms()); // send the subscribe packet
+ if (rc != len)
+ goto exit; // there was a problem
+
+ // set unsubscribe response callback function
+
+
+exit:
+ return rc;
+}
+
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::publish(resultHandler resultHandler, const char* topicName, Message* message)
+{
+ int index = 0;
+ if (this->thread)
+ index = findFreeOperation();
+ Timer& atimer = operations[index].timer;
+
+ atimer.countdown(limits.command_timeout_ms);
+ MQTTString topic = {(char*)topicName, 0, 0};
+
+ if (message->qos == QOS1 || message->qos == QOS2)
+ message->id = packetid.getNext();
+
+ int len = MQTTSerialize_publish(buf, limits.MAX_MQTT_PACKET_SIZE, 0, message->qos, message->retained, message->id, topic, (char*)message->payload, message->payloadlen);
+ int rc = sendPacket(len, atimer.left_ms()); // send the subscribe packet
+ if (rc != len)
+ goto exit; // there was a problem
+
+ /* wait for acks */
+ if (resultHandler == 0)
+ {
+ if (message->qos == QOS1)
+ {
+ if (waitfor(PUBACK, atimer) == PUBACK)
+ {
+ int type, dup, mypacketid;
+ if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1)
+ rc = 0;
+ }
+ }
+ else if (message->qos == QOS2)
+ {
+ if (waitfor(PUBCOMP, atimer) == PUBCOMP)
+ {
+ int type, dup, mypacketid;
+ if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1)
+ rc = 0;
+ }
+
+ }
+ }
+ else
+ {
+ // set publish response callback function
+
+ }
+
+exit:
+ return rc;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::disconnect(resultHandler resultHandler)
+{
+ Timer timer = Timer(limits.command_timeout_ms); // we might wait for incomplete incoming publishes to complete
+ int len = MQTTSerialize_disconnect(buf, limits.MAX_MQTT_PACKET_SIZE);
+ int rc = sendPacket(len, timer.left_ms()); // send the disconnect packet
+
+ return (rc == len) ? 0 : -1;
+}
+
+
+
+#endif
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MQTT/MQTTClient.h Thu Dec 07 17:07:44 2017 +0000
@@ -0,0 +1,1052 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 2017 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
+ * Ian Craggs - fix for bug 460389 - send loop uses wrong length
+ * Ian Craggs - fix for bug 464169 - clearing subscriptions
+ * Ian Craggs - fix for bug 464551 - enums and ints can be different size
+ * Mark Sonnentag - fix for bug 475204 - inefficient instantiation of Timer
+ * Ian Craggs - fix for bug 475749 - packetid modified twice
+ * Ian Craggs - add ability to set message handler separately #6
+ *******************************************************************************/
+
+#if !defined(MQTTCLIENT_H)
+#define MQTTCLIENT_H
+
+#include "FP.h"
+#include "MQTTPacket.h"
+#include <stdio.h>
+#include "MQTTLogging.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;
+};
+
+
+struct connackData
+{
+ int rc;
+ bool sessionPresent;
+};
+
+
+struct subackData
+{
+ int grantedQoS;
+};
+
+
+class PacketId
+{
+public:
+ PacketId()
+ {
+ next = 0;
+ }
+
+ int getNext()
+ {
+ return next = (next == MAX_PACKET_ID) ? 1 : next + 1;
+ }
+
+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 Network, class Timer, int MAX_MQTT_PACKET_SIZE = 100, int MAX_MESSAGE_HANDLERS = 5>
+class Client
+{
+
+public:
+
+ typedef void (*messageHandler)(MessageData&);
+
+ /** 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. Set to 0 to remove.
+ */
+ void setDefaultMessageHandler(messageHandler mh)
+ {
+ if (mh != 0)
+ defaultMessageHandler.attach(mh);
+ else
+ defaultMessageHandler.detach();
+ }
+
+ /** Set a message handling callback. This can be used outside of the the subscribe method.
+ * @param topicFilter - a topic pattern which can include wildcards
+ * @param mh - pointer to the callback function. If 0, removes the callback if any
+ */
+ int setMessageHandler(const char* topicFilter, messageHandler 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 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
+ * @param connackData - connack data to be returned
+ * @return success code -
+ */
+ int connect(MQTTPacket_connectData& options, connackData& data);
+
+ /** 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 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
+ * @param
+ * @return success code -
+ */
+ int subscribe(const char* topicFilter, enum QoS qos, messageHandler mh, subackData &data);
+
+ /** 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:
+
+ void closeSession();
+ void cleanSession();
+ 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<void, MessageData&> fp;
+ } messageHandlers[MAX_MESSAGE_HANDLERS]; // Message handlers are indexed by subscription topic
+
+ FP<void, MessageData&> defaultMessageHandler;
+
+ 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<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS>
+void MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::cleanSession()
+{
+ for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
+ messageHandlers[i].topicFilter = 0;
+
+#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
+}
+
+
+template<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS>
+void MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::closeSession()
+{
+ ping_outstanding = false;
+ isconnected = false;
+ if (cleansession)
+ cleanSession();
+}
+
+
+template<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS>
+MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::Client(Network& network, unsigned int command_timeout_ms) : ipstack(network), packetid()
+{
+ this->command_timeout_ms = command_timeout_ms;
+ cleansession = true;
+ closeSession();
+}
+
+
+#if MQTTCLIENT_QOS2
+template<class Network, class Timer, int a, int b>
+bool MQTT::Client<Network, Timer, a, b>::isQoS2msgidFree(unsigned short id)
+{
+ for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i)
+ {
+ if (incomingQoS2messages[i] == id)
+ return false;
+ }
+ return true;
+}
+
+
+template<class Network, class Timer, int a, int b>
+bool MQTT::Client<Network, Timer, a, b>::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<class Network, class Timer, int a, int b>
+void MQTT::Client<Network, Timer, a, b>::freeQoS2msgid(unsigned short id)
+{
+ for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i)
+ {
+ if (incomingQoS2messages[i] == id)
+ {
+ incomingQoS2messages[i] = 0;
+ return;
+ }
+ }
+}
+#endif
+
+
+template<class Network, class Timer, int a, int b>
+int MQTT::Client<Network, Timer, a, b>::sendPacket(int length, Timer& timer)
+{
+ int rc = FAILURE,
+ sent = 0;
+
+ while (sent < length)
+ {
+ rc = ipstack.write(&sendbuf[sent], length - sent, timer.left_ms());
+ if (rc < 0) // there was an error writing the data
+ break;
+ sent += rc;
+ if (timer.expired()) // only check expiry after at least one attempt to write
+ break;
+ }
+ 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\r\n", rc,
+ MQTTFormat_toServerString(printbuf, sizeof(printbuf), sendbuf, length));
+#endif
+ return rc;
+}
+
+
+template<class Network, class Timer, int a, int b>
+int MQTT::Client<Network, Timer, a, b>::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, 0 if none, -1 if error
+ */
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::readPacket(Timer& timer)
+{
+ int rc = FAILURE;
+ MQTTHeader header = {0};
+ int len = 0;
+ int rem_len = 0;
+
+ /* 1. read the header byte. This has the packet type in it */
+ rc = ipstack.read(readbuf, 1, timer.left_ms());
+ if (rc != 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 receiving packet %s\r\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<class Network, class Timer, int a, int b>
+bool MQTT::Client<Network, Timer, a, b>::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<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS>
+int MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::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<class Network, class Timer, int a, int b>
+int MQTT::Client<Network, Timer, a, b>::yield(unsigned long timeout_ms)
+{
+ int rc = SUCCESS;
+ Timer timer;
+
+ timer.countdown_ms(timeout_ms);
+ while (!timer.expired())
+ {
+ if (cycle(timer) < 0)
+ {
+ rc = FAILURE;
+ break;
+ }
+ }
+
+ return rc;
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::cycle(Timer& timer)
+{
+ // get one piece of work off the wire and one pass through
+ int len = 0,
+ rc = SUCCESS;
+
+ int packet_type = readPacket(timer); // read the socket, see what work is due
+
+ switch (packet_type)
+ {
+ default:
+ // no more data to read, unrecoverable. Or read packet fails due to unexpected network error
+ rc = packet_type;
+ goto exit;
+ case NSAPI_ERROR_WOULD_BLOCK:
+ case NSAPI_ERROR_OK: // timed out reading packet
+ break;
+ case CONNACK:
+ case PUBACK:
+ case SUBACK:
+ break;
+ case PUBLISH:
+ {
+ MQTTString topicName = MQTTString_initializer;
+ Message msg;
+ int intQoS;
+ msg.payloadlen = 0; /* this is a size_t, but deserialize publish sets this as int */
+ if (MQTTDeserialize_publish((unsigned char*)&msg.dup, &intQoS, (unsigned char*)&msg.retained, (unsigned short*)&msg.id, &topicName,
+ (unsigned char**)&msg.payload, (int*)&msg.payloadlen, readbuf, MAX_MQTT_PACKET_SIZE) != 1)
+ goto exit;
+ msg.qos = (enum QoS)intQoS;
+#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;
+ }
+
+ if (keepalive() != SUCCESS)
+ //check only keepalive FAILURE status so that previous FAILURE status can be considered as FAULT
+ rc = FAILURE;
+
+exit:
+ if (rc == SUCCESS)
+ rc = packet_type;
+ else if (isconnected)
+ closeSession();
+ return rc;
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::keepalive()
+{
+ int rc = SUCCESS;
+ static Timer ping_sent;
+
+ if (keepAliveInterval == 0)
+ goto exit;
+
+ if (ping_outstanding)
+ {
+ if (ping_sent.expired())
+ {
+ rc = FAILURE; // session failure
+ #if defined(MQTT_DEBUG)
+ DEBUG("PINGRESP not received in keepalive interval\r\n");
+ #endif
+ }
+ }
+ else if (last_sent.expired() || last_received.expired())
+ {
+ 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;
+ ping_sent.countdown(this->keepAliveInterval);
+ }
+ }
+exit:
+ return rc;
+}
+
+
+// only used in single-threaded mode where one command at a time is in process
+template<class Network, class Timer, int a, int b>
+int MQTT::Client<Network, Timer, a, b>::waitfor(int packet_type, Timer& timer)
+{
+ int rc = FAILURE;
+
+ do
+ {
+ if (timer.expired())
+ break; // we timed out
+ rc = cycle(timer);
+ }
+ while (rc != packet_type && rc >= 0);
+
+ return rc;
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::connect(MQTTPacket_connectData& options, connackData& data)
+{
+ Timer connect_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)
+ {
+ data.rc = 0;
+ data.sessionPresent = false;
+ if (MQTTDeserialize_connack((unsigned char*)&data.sessionPresent,
+ (unsigned char*)&data.rc, readbuf, MAX_MQTT_PACKET_SIZE) == 1)
+ rc = data.rc;
+ else
+ rc = FAILURE;
+ }
+ else
+ rc = FAILURE;
+
+#if MQTTCLIENT_QOS2
+ // resend any 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;
+ ping_outstanding = false;
+ }
+ return rc;
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::connect(MQTTPacket_connectData& options)
+{
+ connackData data;
+ return connect(options, data);
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::connect()
+{
+ MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer;
+ return connect(default_options);
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int MAX_MESSAGE_HANDLERS>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, MAX_MESSAGE_HANDLERS>::setMessageHandler(const char* topicFilter, messageHandler messageHandler)
+{
+ int rc = FAILURE;
+ int i = -1;
+
+ // first check for an existing matching slot
+ for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
+ {
+ if (messageHandlers[i].topicFilter != 0 && strcmp(messageHandlers[i].topicFilter, topicFilter) == 0)
+ {
+ if (messageHandler == 0) // remove existing
+ {
+ messageHandlers[i].topicFilter = 0;
+ messageHandlers[i].fp.detach();
+ }
+ rc = SUCCESS; // return i when adding new subscription
+ break;
+ }
+ }
+ // if no existing, look for empty slot (unless we are removing)
+ if (messageHandler != 0) {
+ if (rc == FAILURE)
+ {
+ for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
+ {
+ if (messageHandlers[i].topicFilter == 0)
+ {
+ rc = SUCCESS;
+ break;
+ }
+ }
+ }
+ if (i < MAX_MESSAGE_HANDLERS)
+ {
+ messageHandlers[i].topicFilter = topicFilter;
+ messageHandlers[i].fp.attach(messageHandler);
+ }
+ }
+ return rc;
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int MAX_MESSAGE_HANDLERS>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, MAX_MESSAGE_HANDLERS>::subscribe(const char* topicFilter,
+ enum QoS qos, messageHandler messageHandler, subackData& data)
+{
+ int rc = FAILURE;
+ 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;
+ unsigned short mypacketid;
+ data.grantedQoS = 0;
+ if (MQTTDeserialize_suback(&mypacketid, 1, &count, &data.grantedQoS, readbuf, MAX_MQTT_PACKET_SIZE) == 1)
+ {
+ if (data.grantedQoS != 0x80)
+ rc = setMessageHandler(topicFilter, messageHandler);
+ }
+ }
+ else
+ rc = FAILURE;
+
+exit:
+ if (rc == FAILURE)
+ closeSession();
+ return rc;
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int MAX_MESSAGE_HANDLERS>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, MAX_MESSAGE_HANDLERS>::subscribe(const char* topicFilter, enum QoS qos, messageHandler messageHandler)
+{
+ subackData data;
+ return subscribe(topicFilter, qos, messageHandler, data);
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int MAX_MESSAGE_HANDLERS>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, MAX_MESSAGE_HANDLERS>::unsubscribe(const char* topicFilter)
+{
+ int rc = FAILURE;
+ 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)
+ {
+ // remove the subscription message handler associated with this topic, if there is one
+ setMessageHandler(topicFilter, 0);
+ }
+ }
+ else
+ rc = FAILURE;
+
+exit:
+ if (rc != SUCCESS)
+ closeSession();
+ return rc;
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::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;
+ }
+#endif
+#if 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)
+ closeSession();
+ return rc;
+}
+
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(const char* topicName, void* payload, size_t payloadlen, unsigned short& id, enum QoS qos, bool retained)
+{
+ int rc = FAILURE;
+ 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<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::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<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(const char* topicName, Message& message)
+{
+ return publish(topicName, message.payload, message.payloadlen, message.qos, message.retained);
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::disconnect()
+{
+ int rc = FAILURE;
+ 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
+ closeSession();
+ return rc;
+}
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MQTT/MQTTEthernet.h Thu Dec 07 17:07:44 2017 +0000
@@ -0,0 +1,29 @@
+
+#if !defined(MQTTETHERNET_H)
+#define MQTTETHERNET_H
+
+#include "MQTTmbed.h"
+#include "EthernetInterface.h"
+#include "MQTTSocket.h"
+
+class MQTTEthernet : public MQTTSocket
+{
+public:
+ MQTTEthernet() : MQTTSocket(ð)
+ {
+ eth.connect();
+ }
+
+ EthernetInterface& getEth()
+ {
+ return eth;
+ }
+
+private:
+
+ EthernetInterface eth;
+
+};
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MQTT/MQTTLogging.h Thu Dec 07 17:07:44 2017 +0000
@@ -0,0 +1,39 @@
+#if !defined(MQTT_LOGGING_H)
+#define MQTT_LOGGING_H
+
+#define STREAM stdout
+#if !defined(DEBUG)
+#define DEBUG(...) \
+ {\
+ fprintf(STREAM, "DEBUG: %s L#%d ", __PRETTY_FUNCTION__, __LINE__); \
+ fprintf(STREAM, ##__VA_ARGS__); \
+ fflush(STREAM); \
+ }
+#endif
+#if !defined(LOG)
+#define LOG(...) \
+ {\
+ fprintf(STREAM, "LOG: %s L#%d ", __PRETTY_FUNCTION__, __LINE__); \
+ fprintf(STREAM, ##__VA_ARGS__); \
+ fflush(STREAM); \
+ }
+#endif
+#if !defined(WARN)
+#define WARN(...) \
+ { \
+ fprintf(STREAM, "WARN: %s L#%d ", __PRETTY_FUNCTION__, __LINE__); \
+ fprintf(STREAM, ##__VA_ARGS__); \
+ fflush(STREAM); \
+ }
+#endif
+#if !defined(ERROR)
+#define ERROR(...) \
+ { \
+ fprintf(STREAM, "ERROR: %s L#%d ", __PRETTY_FUNCTION__, __LINE__); \
+ fprintf(STREAM, ##__VA_ARGS__); \
+ fflush(STREAM); \
+ exit(1); \
+ }
+#endif
+
+#endif
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MQTT/MQTTPacket.lib Thu Dec 07 17:07:44 2017 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/teams/mqtt/code/MQTTPacket/#aedcaf7984d5
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MQTT/MQTTSocket.h Thu Dec 07 17:07:44 2017 +0000
@@ -0,0 +1,97 @@
+#if !defined(MQTTSOCKET_H)
+#define MQTTSOCKET_H
+
+#include "MQTTmbed.h"
+#include <EthernetInterface.h>
+#include <Timer.h>
+
+class MQTTSocket
+{
+public:
+ MQTTSocket(EthernetInterface *anet)
+ {
+ net = anet;
+ open = false;
+ }
+
+ int connect(char* hostname, int port, int timeout=1000)
+ {
+ if (open)
+ disconnect();
+ nsapi_error_t rc = mysock.open(net);
+ open = true;
+ mysock.set_blocking(true);
+ mysock.set_timeout((unsigned int)timeout);
+ rc = mysock.connect(hostname, port);
+ mysock.set_blocking(false); // blocking timeouts seem not to work
+ return rc;
+ }
+
+ // common read/write routine, avoiding blocking timeouts
+ int common(unsigned char* buffer, int len, int timeout, bool read)
+ {
+ timer.start();
+ mysock.set_blocking(false); // blocking timeouts seem not to work
+ int bytes = 0;
+ bool first = true;
+ do
+ {
+ if (first)
+ first = false;
+ else
+ wait_ms(timeout < 100 ? timeout : 100);
+ int rc;
+ if (read)
+ rc = mysock.recv((char*)buffer, len);
+ else
+ rc = mysock.send((char*)buffer, len);
+ if (rc < 0)
+ {
+ if (rc != NSAPI_ERROR_WOULD_BLOCK)
+ {
+ bytes = -1;
+ break;
+ }
+ }
+ else
+ bytes += rc;
+ }
+ while (bytes < len && timer.read_ms() < timeout);
+ timer.stop();
+ return bytes;
+ }
+
+ /* returns the number of bytes read, which could be 0.
+ -1 if there was an error on the socket
+ */
+ int read(unsigned char* buffer, int len, int timeout)
+ {
+ return common(buffer, len, timeout, true);
+ }
+
+ int write(unsigned char* buffer, int len, int timeout)
+ {
+ return common(buffer, len, timeout, false);
+ }
+
+ int disconnect()
+ {
+ open = false;
+ return mysock.close();
+ }
+
+ /*bool is_connected()
+ {
+ return mysock.is_connected();
+ }*/
+
+private:
+
+ bool open;
+ TCPSocket mysock;
+ EthernetInterface *net;
+ Timer timer;
+
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/MQTT/MQTTmbed.h Thu Dec 07 17:07:44 2017 +0000
@@ -0,0 +1,48 @@
+#if !defined(MQTT_MBED_H)
+#define MQTT_MBED_H
+
+#include "mbed.h"
+
+class Countdown
+{
+public:
+ Countdown() : t()
+ {
+
+ }
+
+ Countdown(int ms) : t()
+ {
+ countdown_ms(ms);
+ }
+
+
+ bool expired()
+ {
+ return t.read_ms() >= interval_end_ms;
+ }
+
+ void countdown_ms(unsigned long ms)
+ {
+ t.stop();
+ interval_end_ms = ms;
+ t.reset();
+ t.start();
+ }
+
+ void countdown(int seconds)
+ {
+ countdown_ms((unsigned long)seconds * 1000L);
+ }
+
+ int left_ms()
+ {
+ return interval_end_ms - t.read_ms();
+ }
+
+private:
+ Timer t;
+ unsigned long interval_end_ms;
+};
+
+#endif
\ No newline at end of file
--- a/MQTTNetwork.h Sat Dec 02 04:59:59 2017 +0000
+++ b/MQTTNetwork.h Thu Dec 07 17:07:44 2017 +0000
@@ -1,38 +1,40 @@
-#ifndef _MQTTNETWORK_H_
-#define _MQTTNETWORK_H_
-
-#include "NetworkInterface.h"
-
-class MQTTNetwork {
-public:
- MQTTNetwork(NetworkInterface* aNetwork) : network(aNetwork) {
- socket = new TCPSocket();
- }
-
- ~MQTTNetwork() {
- delete socket;
- }
-
- int read(unsigned char* buffer, int len, int timeout) {
- return socket->recv(buffer, len);
- }
-
- int write(unsigned char* buffer, int len, int timeout) {
- return socket->send(buffer, len);
- }
-
- int connect(const char* hostname, int port) {
- socket->open(network);
- return socket->connect(hostname, port);
- }
-
- int disconnect() {
- return socket->close();
- }
-
-private:
- NetworkInterface* network;
- TCPSocket* socket;
-};
-
-#endif // _MQTTNETWORK_H_
+#ifndef _MQTTNETWORK_H_
+#define _MQTTNETWORK_H_
+
+#include "NetworkInterface.h"
+
+class MQTTNetwork {
+public:
+ MQTTNetwork(NetworkInterface* aNetwork) : network(aNetwork) {
+ socket = new TCPSocket();
+ }
+
+ ~MQTTNetwork() {
+ delete socket;
+ }
+
+ int read(unsigned char* buffer, int len, int timeout) {
+ socket->set_timeout(timeout);
+ return socket->recv(buffer, len);
+ }
+
+ int write(unsigned char* buffer, int len, int timeout) {
+ socket->set_timeout(timeout);
+ return socket->send(buffer, len);
+ }
+
+ int connect(const char* hostname, int port) {
+ socket->open(network);
+ return socket->connect(hostname, port);
+ }
+
+ int disconnect() {
+ return socket->close();
+ }
+
+private:
+ NetworkInterface* network;
+ TCPSocket* socket;
+};
+
+#endif // _MQTTNETWORK_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NDefLib/Message.cpp Thu Dec 07 17:07:44 2017 +0000
@@ -0,0 +1,132 @@
+/**
+ ******************************************************************************
+ * @file Message.cpp
+ * @author ST / Central Labs
+ * @version V2.0.0
+ * @date 28 Apr 2017
+ * @brief NDef Message class implementation
+ ******************************************************************************
+ * @attention
+ *
+ * <h2><center>© COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of STMicroelectronics nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+#include <cstdlib>
+#include "NDefLib/Message.h"
+#include "RecordType/EmptyRecord.h"
+#include "RecordType/RecordText.h"
+#include "RecordType/RecordAAR.h"
+#include "RecordType/RecordMimeType.h"
+#include "RecordType/RecordURI.h"
+
+namespace NDefLib {
+
+uint16_t Message::get_byte_length() const {
+ uint16_t lenght = 2; //length size
+
+ if (mRecords.size() == 0) {
+ return lenght + EmptyRecord().get_byte_length();
+ }
+
+ std::vector<Record*>::const_iterator it = mRecords.begin();
+ const std::vector<Record*>::const_iterator end = mRecords.end();
+
+ for (; it != end; ++it) {
+ lenght += (*it)->get_byte_length();
+ } //for
+
+ return lenght;
+} //getByteLenght
+
+uint16_t Message::write(uint8_t *buffer) const {
+
+ const uint16_t length = get_byte_length() - 2;
+ uint16_t offset = 0;
+ buffer[offset++] = (uint8_t) ((length & 0xFF00) >> 8);
+ buffer[offset++] = (uint8_t) ((length & 0x00FF));
+
+ const uint32_t nRecord = mRecords.size();
+
+ if (mRecords.size() == 0) {
+ offset += EmptyRecord().write(buffer + offset);
+ return offset;
+ } //else
+
+ for (uint32_t i = 0; i < nRecord; i++) {
+ Record *r = mRecords[i];
+
+ r->set_as_middle_record();
+ if (i == 0) {
+ r->set_as_first_record();
+ }
+ if (i == nRecord - 1) {
+ r->set_as_last_record();
+ }
+
+ offset += r->write(buffer + offset);
+ } //for
+
+ return offset;
+} //write
+
+void Message::parse_message(const uint8_t * const rawNdefFile, const uint16_t length, Message *msg) {
+ uint16_t offset = 0;
+ Record *r;
+
+ RecordHeader header;
+ do {
+ const uint8_t headerLenght = header.load_header(rawNdefFile + offset);
+ r = RecordText::parse(header, rawNdefFile + offset + headerLenght);
+ if (r == NULL) {
+ r = RecordAAR::parse(header, rawNdefFile + offset + headerLenght);
+ }
+ if (r == NULL) {
+ r = RecordMimeType::parse(header,
+ rawNdefFile + offset + headerLenght);
+ }
+ if (r == NULL) {
+ r = RecordURI::parse(header, rawNdefFile + offset + headerLenght);
+ }
+
+ offset += header.get_record_length();
+ msg->add_record(r);
+ } while (offset < length);
+}
+
+void Message::remove_and_delete_all_record(Message &msg){
+ const uint32_t nRecords =msg.get_N_records();
+ for (uint32_t i =0 ;i<nRecords ;i++) {
+ NDefLib::Record *r = msg[i];
+ delete r;
+ }//for
+ msg.mRecords.clear();
+}//removeAndDeleteAllRecord
+
+} /* namespace NDefLib */
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NDefLib/Message.h Thu Dec 07 17:07:44 2017 +0000
@@ -0,0 +1,142 @@
+/**
+ ******************************************************************************
+ * @file Message.h
+ * @author ST / Central Labs
+ * @version V2.0.0
+ * @date 28 Apr 2017
+ * @brief NDef Message class
+ ******************************************************************************
+ * @attention
+ *
+ * <h2><center>© COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of STMicroelectronics nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+#ifndef NDEFLIB_MESSAGE_H_
+#define NDEFLIB_MESSAGE_H_
+
+#include <algorithm>
+#include <vector>
+
+#include "Record.h"
+
+namespace NDefLib {
+
+/**
+ * Class containing a list of {@link Record}
+ */
+class Message {
+public:
+
+ /**
+ * Add a ndef record to this message.
+ * @param r Record to add
+ */
+ void add_record(Record *r) {
+ mRecords.push_back(r);
+ }
+
+ /**
+ * Remove a ndef record to this message
+ * @param r record to remove
+ */
+ void remove_record(Record *r){
+ mRecords.erase( std::remove( mRecords.begin(), mRecords.end(), r ), mRecords.end() );
+ }
+
+
+ /**
+ * Add all the records in the list to this message.
+ * @param addList List of records to add.
+ */
+ void add_records(const std::vector<Record*> &addList) {
+ mRecords.insert(mRecords.end(), addList.begin(), addList.end());
+ }
+
+ /**
+ * Get the specific record contained by this message, NULL if not a valid index.
+ * @param index Record index.
+ * @return a Record object if present, otherwise NULL
+ */
+ Record* operator[](const uint32_t index)const{
+ if (index >= mRecords.size()) {
+ return NULL;
+ }
+
+ return mRecords[index];
+ }
+
+ /**
+ * Get the number of records in this message.
+ * @return number of records in this message
+ */
+ uint32_t get_N_records() const {
+ return mRecords.size();
+ }
+
+ /**
+ * Length in bytes needed to write this message.
+ * @return number of bytes needed to write this message
+ */
+ uint16_t get_byte_length() const;
+
+ /**
+ * Write message in the provided buffer
+ * @par The first 2 bytes contain the NDEF message length.
+ * @param[out] buffer Buffer the message must be written into.
+ * @return number of bytes written
+ */
+ uint16_t write(uint8_t *buffer) const;
+
+ /**
+ * Create a set of records from a raw buffer adding them to a message object.
+ * @par Message buffer must NOT contain the buffer length in the first two bytes.
+ * @param buffer Buffer containing the message record.
+ * @param bufferLength Buffer length.
+ * @param[in,out] Message message that will contain the new records.
+ */
+ static void parse_message(const uint8_t * const buffer,
+ const uint16_t bufferLength, Message *message);
+
+ /**
+ * Remove all the recrods from the mesasge and delete it
+ * @param msg Message with the records to delete
+ */
+ static void remove_and_delete_all_record(Message &msg);
+
+ virtual ~Message() {
+ }
+
+private:
+ /**
+ * List of records contained by this message.
+ */
+ std::vector<Record*> mRecords;
+};
+
+} /* namespace NDefLib */
+
+#endif /* NDEFLIB_MESSAGE_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NDefLib/NDefNfcTag.h Thu Dec 07 17:07:44 2017 +0000
@@ -0,0 +1,329 @@
+/**
+ ******************************************************************************
+ * @file NDefNfcTag.h
+ * @author ST / Central Labs
+ * @version V2.0.0
+ * @date 28 Apr 2017
+ * @brief Generic interface that a device must implement to use the NDefLib
+ * with async communication
+ ******************************************************************************
+ * @attention
+ *
+ * <h2><center>© COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of STMicroelectronics nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+#ifndef NDEFLIB_NDEFNFCTAG_H_
+#define NDEFLIB_NDEFNFCTAG_H_
+
+#include "NDefLib/Message.h"
+
+namespace NDefLib {
+
+/**
+ * Abstract class used to write/read NDef messages to/from a nfc tag.
+ * This class is made to handle also asynchronous communication with the nfc component.
+ * All the functions in this class will return immediately, when a command ends a proper callback function will
+ * be called.
+ */
+class NDefNfcTag {
+
+public:
+
+ /**
+ * Class that contains all the function called when a command finished.
+ * The default implementation is an empty function.
+ */
+ class Callbacks {
+ public:
+
+ /**
+ * Called when a session is open.
+ * @param tag Tag where the session is open.
+ * @param success True if the operation has success.
+ */
+ virtual void on_session_open(NDefNfcTag *tag,bool success){
+ (void)tag;(void)success;
+ };
+
+ /**
+ * Called when a message is written.
+ * @param tag Tag where the message is written.
+ * @param success True if the operation has success.
+ */
+ virtual void on_message_write(NDefNfcTag *tag,bool success){
+ (void)tag;(void)success;
+
+ };
+
+ /**
+ * Called when a message is read.
+ * @param tag Tag where the message is read.
+ * @param success True if the operation has success.
+ */
+ virtual void on_message_read(NDefNfcTag *tag,bool success,
+ const Message *msg){
+ (void)tag;(void)success; (void)msg;
+ };
+
+
+ /**
+ * Called when a session is closed.
+ * @param tag Tag where the session is closed.
+ * @param success True if the operation has success.
+ */
+ virtual void on_session_close(NDefNfcTag *tag,bool success){
+ (void)tag;(void)success;
+ };
+
+ virtual ~Callbacks(){};
+ };
+
+private:
+
+ /**
+ * Data used to store the callback status during a read/write operation.
+ */
+ struct CallbackStatus{
+
+ /** Object that triggers the callback */
+ NDefNfcTag *callOwner;
+
+ /** Message that the callback is writing/reading */
+ Message *msg;
+ };
+
+public:
+
+ NDefNfcTag():mCallBack(&mDefaultCallBack){}
+
+ /**
+ * Set the callback object.
+ * @param c Object containing the callback.
+ */
+ void set_callback(Callbacks *c){
+ if (c!=NULL) {
+ mCallBack=c;
+ } else {
+ mCallBack=&mDefaultCallBack;
+ }
+ }//setCallBack
+
+
+ /**
+ * Open the communication session with the nfc tag.
+ * @param force Force to open a communication.
+ * @return true if success
+ */
+ virtual bool open_session(bool force = false)=0;
+
+ /**
+ * Close the communication with the nfc tag.
+ * @return true if success
+ */
+ virtual bool close_session()=0;
+
+ /**
+ * Returns true if a communication with the nfc tag is open.
+ * @return true if a communication with the nfc tag is open
+ */
+ virtual bool is_session_open()=0;
+
+ /**
+ * Write a message in the nfc tag.
+ * @par This call will delete the previous message.
+ * @param msg Message to write.
+ * @return true if success
+ */
+ virtual bool write(Message &msg) {
+ if (!is_session_open()) {
+ mCallBack->on_message_write(this,false);
+ return false;
+ }
+
+ const uint16_t length = msg.get_byte_length();
+ uint8_t *buffer = new uint8_t[length];
+ if (buffer==NULL){ //impossible to allocate the buffer
+ mCallBack->on_message_write(this,false);
+ return false;
+ }
+
+ msg.write(buffer);
+
+ mCallBackStatus.callOwner=this;
+ mCallBackStatus.msg=NULL;
+
+ return writeByte(buffer, length,0,NDefNfcTag::onWriteMessageCallback,&mCallBackStatus);
+ }
+
+ /**
+ * Read a message from the tag.
+ * @param[in,out] msg Message object the read records are added to.
+ * @return true if success
+ */
+ virtual bool read(Message *msg) {
+ if (!is_session_open()) {
+ mCallBack->on_message_read(this,false,msg);
+ return false;
+ }
+
+ uint8_t *buffer = new uint8_t[2];
+ if (buffer==NULL) {
+ mCallBack->on_message_read(this,false,msg);
+ return false;
+ }
+
+ mCallBackStatus.callOwner=this;
+ mCallBackStatus.msg=msg;
+ return readByte(0,2,buffer,NDefNfcTag::onReadMessageLength,&mCallBackStatus);
+ }
+
+ virtual ~NDefNfcTag() {}
+
+protected:
+
+ typedef struct CallbackStatus CallbackStatus_t;
+
+ /**
+ * Function that the component will call when a read/write operation is completed
+ * @param internalState Callback internal state data.
+ * @param status True if the operation succeed.
+ * @param buffer Buffer to read/write.
+ * @param length Number of byte read/write.
+ * @return true if the operation had success
+ */
+ typedef bool(*byteOperationCallback_t)(CallbackStatus_t *internalState,
+ bool status,const uint8_t *buffer, uint16_t length);
+
+ /**
+ * Write a sequence of bytes to the NDEF file.
+ * @param buffer Buffer to write.
+ * @param length Number of bytes to write.
+ * @param offset Offset where start to write.
+ * @param callback Function to call when the operation ended.
+ * @param callbackStatus Parameter to pass to the callback function.
+ * @return true if the operation has success
+ */
+ virtual bool writeByte(const uint8_t *buffer, uint16_t length,uint16_t offset,
+ byteOperationCallback_t callback,CallbackStatus_t *callbackStatus)=0;
+
+ /**
+ * Read a sequence of bytes from the NDEF file.
+ * @param byteOffset Read offset in bytes.
+ * @param byteLength Number of bytes to read.
+ * @param[out] buffer Buffer to store the read data into.
+ * @param callback Function to call when the operation ended.
+ * @param callbackStatus Parameter to pass to the callback function.
+ * @return true if the operation has success
+ */
+ virtual bool readByte(const uint16_t byteOffset, const uint16_t byteLength,
+ uint8_t *buffer, byteOperationCallback_t callback,CallbackStatus_t *callbackStatus)=0;
+
+
+ /** object with the user callback */
+ Callbacks *mCallBack;
+private:
+
+ /** object with the current callback status */
+ CallbackStatus_t mCallBackStatus;
+ /** default callback object, all the functions are empty */
+ Callbacks mDefaultCallBack;
+
+ /**
+ * Function called when a write operation completes, it will invoke on_message_write
+ * @param internalState Object that invokes the write operation.
+ * @param status True if the operation had success.
+ * @param buffer Buffer written.
+ * @param length Number of bytes written.
+ * @return true if the write had success
+ */
+ static bool onWriteMessageCallback(CallbackStatus_t *internalState,
+ bool status,const uint8_t *buffer, uint16_t ){
+ delete [] buffer;
+
+ internalState->callOwner->mCallBack->on_message_write(internalState->callOwner,status);
+
+ return status;
+ }
+
+ /**
+ * Function called when a read operation completes.
+ * In this case we read the message length, this function will read all the message
+ * @param internalState Object that invokes the write operation.
+ * @param status true If the operation had success.
+ * @param buffer Buffer read.
+ * @param length Number of bytes read.
+ * @return true if the read had success
+ */
+ static bool onReadMessageLength(CallbackStatus_t *internalState,
+ bool status,const uint8_t *buffer, uint16_t length){
+
+ if (!status || length!=2) {
+ internalState->callOwner->mCallBack->on_message_read(internalState->callOwner,false,internalState->msg);
+ return false;
+ }//if
+
+ length = (((uint16_t) buffer[0]) << 8 | buffer[1]);
+ delete [] buffer;
+
+ uint8_t *readBuffer = new uint8_t[length];
+ if (readBuffer==NULL) {
+ internalState->callOwner->mCallBack->on_message_read(internalState->callOwner,false,internalState->msg);
+ return false;
+ }//readBuffer
+
+ internalState->callOwner->readByte(2,length,readBuffer, &NDefNfcTag::onReadMessageCallback,internalState);
+ return status;
+ }
+
+ /**
+ * Function called when all messages have been read
+ * @param internalState Object that invokes the write operation.
+ * @param status True if the operation had success.
+ * @param buffer Buffer read.
+ * @param length Number of bytes read.
+ * @return true if the read had success
+ */
+ static bool onReadMessageCallback(CallbackStatus_t *internalState,
+ bool status,const uint8_t *buffer, uint16_t length){
+ if(!status) {
+ internalState->callOwner->mCallBack->on_message_read(internalState->callOwner,false,internalState->msg);
+ return false;
+ }
+ Message::parse_message(buffer, length, internalState->msg);
+ delete [] buffer;
+ internalState->callOwner->mCallBack-> on_message_read(internalState->callOwner,true,internalState->msg);
+ return status ;
+ }
+
+}; //class NDefNfcTagASync
+
+}// namespace NDefLib
+
+#endif /* NDEFLIB_NDEFNFCTAG_H_ */
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NDefLib/Record.h Thu Dec 07 17:07:44 2017 +0000
@@ -0,0 +1,161 @@
+/**
+ ******************************************************************************
+ * @file Record.h
+ * @author ST / Central Labs
+ * @version V2.0.0
+ * @date 28 Apr 2017
+ * @brief Generic Record class
+ ******************************************************************************
+ * @attention
+ *
+ * <h2><center>© COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of STMicroelectronics nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+#ifndef NDEFLIB_RECORD_H_
+#define NDEFLIB_RECORD_H_
+#include <stdint.h>
+
+#include "RecordHeader.h"
+
+namespace NDefLib {
+
+/**
+ * Base class for a NDefRecord
+ * @see NFC Data Exchange Format (NDEF) Technical Specification NDEF 1.0
+ */
+class Record {
+public:
+
+ /**
+ * Enum used to identify the record type.
+ */
+ typedef enum {
+ TYPE_UNKNOWN, //!< UNKNOWN record
+ TYPE_TEXT, //!< TEXT
+ TYPE_AAR, //!< Android Archive record
+ TYPE_MIME, //!< generic MIME type
+ TYPE_URI, //!< generic URI
+ TYPE_URI_MAIL, //!< Email URI record
+ TYPE_URI_SMS, //!< SMS URI record
+ TYPE_URI_GEOLOCATION,//!< position URI record
+ TYPE_MIME_VCARD, //!< VCard record
+ TYPE_WIFI_CONF //!< Wifi configuration
+ } RecordType_t;
+
+ Record() {
+ }
+
+ /**
+ * Set the record as the first record in the message.
+ */
+ void set_as_first_record() {
+ mRecordHeader.set_MB(true);
+ }
+
+ /**
+ * Set the record as the last record in the message.
+ */
+ void set_as_last_record() {
+ mRecordHeader.set_ME(true);
+ }
+
+ /**
+ * Check if it is the last record in the message.
+ * @return true if it is the last record in the message
+ */
+ bool is_last_record() const {
+ return mRecordHeader.get_ME();
+ }
+
+ /**
+ * Check if it is the first record in the message.
+ * @return true if it is the fist record in the message
+ */
+ bool is_first_record() const {
+ return mRecordHeader.get_MB();
+ }
+
+ /**
+ * Set the record as generic (not the first one and not the last one)
+ */
+ void set_as_middle_record() {
+ mRecordHeader.set_MB(false);
+ mRecordHeader.set_ME(false);
+ }
+
+ /**
+ * Check if the record is in the middle of a chain.
+ * @return true if is not the fist or the last one
+ */
+ bool is_middle_record() const{
+ return ! (mRecordHeader.get_MB() || mRecordHeader.get_ME());
+ }
+
+ /**
+ * Get tag type.
+ * @par This method should be overridden to return a valid type.
+ * @return tag type
+ */
+ virtual RecordType_t get_type() const {
+ return TYPE_UNKNOWN;
+ } //getType
+
+
+ /**
+ * Get the record header.
+ * @return record header
+ */
+ const RecordHeader& get_header() const{
+ return mRecordHeader;
+ }
+
+ /**
+ * Number of bytes needed to store this record.
+ * @return size of the header + size of the record content
+ */
+ virtual uint16_t get_byte_length() {
+ return mRecordHeader.get_record_length();
+ }
+
+ /**
+ * Write the record content into a buffer.
+ * @param[out] buffer buffer to write the record content into, the buffer size
+ * must be almost {@link Record#getByteLength} bytes.
+ * @return number of written bytes
+ */
+ virtual uint16_t write(uint8_t *buffer)=0;
+
+ virtual ~Record() {
+ };
+
+protected:
+ RecordHeader mRecordHeader;
+};
+
+} /* namespace NDefLib */
+
+#endif /* NDEFLIB_RECORD_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/NDefLib/RecordHeader.h Thu Dec 07 17:07:44 2017 +0000
@@ -0,0 +1,340 @@
+/**
+ ******************************************************************************
+ * @file RecordHeader.h
+ * @author ST / Central Labs
+ * @version V2.0.0
+ * @date 28 Apr 2017
+ * @brief Record header class
+ ******************************************************************************
+ * @attention
+ *
+ * <h2><center>© COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. Neither the name of STMicroelectronics nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+#ifndef NDEFLIB_RECORDHEADER_H_
+#define NDEFLIB_RECORDHEADER_H_
+
+#include <stdint.h>
+
+namespace NDefLib {
+
+/**
+ * Record header class.
+ * @see NFC Data Exchange Format (NDEF) Technical Specification NDEF 1.0
+ */
+class RecordHeader {
+
+public:
+
+ /**
+ * Record type format
+ */
+ typedef enum TypeNameFormat {
+ Empty = 0x00, //!< Empty
+ NFC_well_known = 0x01, //!< NFC_well_known
+ Mime_media_type = 0x02,//!< Mime_media_type
+ Absolute_URI = 0x03, //!< Absolute_URI
+ NFC_external = 0x04, //!< NFC_external
+ Unknown = 0x05, //!< Unknown
+ Unchanged = 0x06, //!< Unchanged
+ Reserved = 0x07 //!< Reserved
+ } TypeNameFormat_t;
+
+
+ RecordHeader() :
+ headerFlags(0), typeLength(0), payloadLength(0) {
+ set_SR(true);
+ }
+
+ /**
+ * Set the 'message begin' flag.
+ * @param value True if the record is the first within the message.
+ */
+ void set_MB(bool value) {
+ if (value) {
+ headerFlags |= 0x80;
+ } else {
+ headerFlags &= ~0x80;
+ }
+ }//set_MB
+
+ /**
+ * Check the 'message begin' flag.
+ * @return true if it is the first record in the message
+ */
+ bool get_MB() const {
+ return (headerFlags & 0x80) != 0;
+ }//get_MB
+
+ /**
+ * Set the 'message end' flag.
+ * @param value True if it is the last record in the message.
+ */
+ void set_ME(bool value) {
+ if (value) {
+ headerFlags |= 0x40;
+ } else {
+ headerFlags &= ~0x40;
+ }
+ }//set_ME
+
+ /**
+ * Check the 'message end' flag.
+ * @return true if it is the last record in the message
+ */
+ bool get_ME() const {
+ return (headerFlags & 0x40) != 0;
+ }//get_ME
+
+ /**
+ * Set the 'Chunk' flag.
+ * @param value True if the record is in the first record chunk or in a middle record
+ * chunk of a chunked payload.
+ */
+ void set_CF(bool value) {
+ if (value) {
+ headerFlags |= 0x20;
+ } else {
+ headerFlags &= ~0x20;
+ }
+ }//set_CF
+
+ /**
+ * Check the 'Chunk' flag value.
+ * @return true if the record is in the first record chunk or in a middle record
+ * chunk of a chunked payload
+ */
+ bool get_CF() const {
+ return (headerFlags & 0x20) != 0;
+ }//get_CF
+
+ /**
+ * Set the 'Short record' flag value.
+ * @param value True if the record size can be encoded with 8 bits.
+ */
+ void set_SR(bool value) {
+ if (value) {
+ headerFlags |= 0x10;
+ } else {
+ headerFlags &= ~0x10;
+ }
+ }//setSR
+
+ /**
+ * Check the 'Short record' flag.
+ * @return true if the short range header format is set
+ */
+ bool get_SR() const {
+ return (headerFlags & 0x10) != 0;
+ }//getSR
+
+ /**
+ * Set the 'ID length' flag.
+ * @param value True if the 'ID length' value is used.
+ */
+ void set_IL(bool value) {
+ if (value) {
+ headerFlags |= 0x08;
+ } else {
+ headerFlags &= ~0x08;
+ }
+ }//setIL
+
+ /**
+ * Check the 'ID length' flag.
+ * @param value True if 'ID length' is set.
+ */
+ bool get_IL() const {
+ return (headerFlags & 0x08) != 0;
+ }//getIL
+
+ /**
+ * Set the type name format field.
+ * @param value Record type name format.
+ */
+ void set_FNT(const TypeNameFormat_t value) {
+ uint8_t temp = (uint8_t) value;
+ temp &= 0x07; //keep the first 3 bits
+ headerFlags &= 0xF8; //clean the fist 3 bits
+ headerFlags |= temp; //set the fist 3 bits
+ }//setFNT
+
+ /**
+ * Get the record type name.
+ * @return type name format of the record
+ */
+ TypeNameFormat_t get_FNT() const {
+ return (TypeNameFormat_t) (headerFlags & 0x07);
+ }
+
+ /**
+ * Set the record payload length.
+ * @par This function will update the SR field as needed.
+ * @param length payload length
+ */
+ void set_payload_length(uint32_t length) {
+ payloadLength = length;
+ set_SR(payloadLength <= 255);
+ }
+
+ /**
+ * Get the payload length.
+ * @return payload length
+ */
+ uint32_t get_payload_length() const {
+ return payloadLength;
+ }
+
+ /**
+ * Set the type length.
+ * @param size.
+ */
+ void set_type_length(uint8_t size) {
+ typeLength = size;
+ }
+
+ /**
+ * Get the type length.
+ * @return type length
+ */
+ uint8_t get_type_length() const {
+ return typeLength;
+ }
+
+ /**
+ * Set the id length.
+ * @param size.
+ */
+ void set_id_length(uint8_t size) {
+ if (size!=0) {
+ idLength = size;
+ set_IL(true);
+ } else {
+ set_IL(false);
+ }
+ }
+
+ /**
+ * Get the id length.
+ * @return id length
+ */
+ uint8_t get_id_length() const {
+ return idLength;
+ }
+
+ /**
+ * Get the number of bytes needed to store this record.
+ * @return 3 or 6
+ */
+ uint16_t get_record_length() const {
+ return (get_SR() ? 3 : 6) + (get_IL() ? idLength : 0)+typeLength + payloadLength;
+ }
+
+ /**
+ * Store the header information in the buffer.
+ * @param[out] outBuffer Buffer to write the header into.
+ * @return number of write bytes
+ */
+ uint8_t write_header(uint8_t *outBuffer) const {
+
+ uint32_t index = 0;
+
+ outBuffer[index++] = headerFlags;
+ outBuffer[index++] = typeLength;
+ if (get_SR()) {
+ outBuffer[index++] = (uint8_t) payloadLength;
+ } else {
+ outBuffer[index++] = (uint8_t) ((payloadLength & 0xFF000000)
+ >> 24);
+ outBuffer[index++] = (uint8_t) ((payloadLength & 0x00FF0000)
+ >> 16);
+ outBuffer[index++] = (uint8_t) ((payloadLength & 0x0000FF00)
+ >> 8);
+ outBuffer[index++] = (uint8_t) (payloadLength & 0x000000FF);
+ } //if-else
+ if (get_IL()) {
+ outBuffer[index++] =idLength;
+ }
+
+ return index;
+ } //writeHeader
+
+ /**
+ * Load an header from a buffer.
+ * @param buffer Buffer to load the header from.
+ * @return number of read bytes
+ */
+ uint16_t load_header(const uint8_t * const buffer) {
+ uint32_t index = 0;
+ headerFlags = buffer[index++];
+ typeLength = buffer[index++];
+ if (get_SR()) {
+ payloadLength = buffer[index++];
+ } else {
+ payloadLength = (((uint32_t) buffer[index + 0]) << 24)
+ | (((uint32_t) buffer[index + 1]) << 16)
+ | (((uint32_t) buffer[index + 2]) << 8)
+ | ((uint32_t) buffer[index + 3]);
+ index += 4;
+ } //if-else
+ if (get_IL()) {
+ idLength = buffer[index++];
+ } else {
+ idLength = 0;
+ }
+
+ return index;
+ } //loadHeader
+
+ /**
+ * Equal operator.
+ * @param other Other object to compare with.
+ * @return true if the two record headers are equal
+ */
+ bool operator==(const RecordHeader &other) const{
+ return (headerFlags==other.headerFlags) &&
+ (typeLength==other.typeLength) &&
+ (payloadLength==other.payloadLength);
+ }
+
+ bool operator!=(const RecordHeader &other) const{
+ return !(*this==other);
+ }
+
+private:
+ uint8_t idLength;
+ uint8_t headerFlags;
+ uint8_t typeLength;
+ uint32_t payloadLength;
+};
+
+} /* namespace NDefLib */
+
+#endif /* NDEFLIB_RECORDHEADER_H_ */
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NDefLib/RecordType.lib Thu Dec 07 17:07:44 2017 +0000 @@ -0,0 +1,1 @@ +NDefLib/RecordType#51d7bdf88981
--- a/easy-connect.lib Sat Dec 02 04:59:59 2017 +0000 +++ b/easy-connect.lib Thu Dec 07 17:07:44 2017 +0000 @@ -1,1 +1,1 @@ -https://os.mbed.com/users/DuyLionTran/code/easy-connect-customized/#309ff512fdc5 +easy-connect#c9094fbda0bd
--- a/main.cpp Sat Dec 02 04:59:59 2017 +0000
+++ b/main.cpp Thu Dec 07 17:07:44 2017 +0000
@@ -3,17 +3,27 @@
***************************************************************/
#include "mbed.h"
#include "easy-connect.h"
+#include "MQTTClient.h"
+#include "NDefLib/NDefNfcTag.h"
+#include "NDefLib/RecordType/RecordURI.h"
#include "MQTTNetwork.h"
#include "MQTTmbed.h"
-#include "MQTTClient.h"
-#include "string.h"
/***************************************************************
* Definitions
***************************************************************/
-#define logMessage printf
-#define MQTTCLIENT_QOS2 (0)
-
+// Configuration values needed to connect to IBM IoT Cloud
+#define ORG MQTT_ORG_ID // connect to ORG.internetofthings.ibmcloud.com/ For a registered connection, replace with your org
+#define ID MQTT_DEVICE_ID // For a registered connection is your device id
+#define AUTH_TOKEN MQTT_DEVICE_PASSWORD // For a registered connection is a device auth-token
+#define DEFAULT_TYPE_NAME MQTT_DEVICE_TYPE // For a registered connection is device type
+
+#define TYPE DEFAULT_TYPE_NAME // For a registered connection, replace with your type
+#define IBM_IOT_PORT MQTT_PORT
+
+#define MQTT_MAX_PACKET_SIZE 400
+#define MQTT_MAX_PAYLOAD_SIZE 300
+
#define SENSOR_1_PIN (A0)
#define SENSOR_2_PIN (A1)
#define SENSOR_3_PIN (A3)
@@ -21,267 +31,243 @@
#define RELAY_1_PIN (D11)
#define RELAY_2_PIN (D12)
-#define ORG "a4nvkh"
-#define DEVICE_TYPE "Nucleo_8266"
-#define DEVICE_ID "PROEVN"
-#define TOKEN "PROEVN2017"
-
-#define MAX_FAIL_ATTEMPT (5)
-
-typedef enum {
- MQTT_SUCCESS = 0,
- MQTT_NETWORK_FAIL = -1,
- MQTT_FAIL = -2
-} mqtt_ret_val;
-
/***************************************************************
* Variables
***************************************************************/
-/* MQTT Varialbles */
-float firmwareVersion = 0.91;
-char server[] = ORG ".messaging.internetofthings.ibmcloud.com";
-char topicCMD[] = "iot-2/cmd/command/fmt/json";
-char topicEvent[] = "iot-2/evt/status_update/fmt/json";
-char authMethod[] = "use-token-auth";
-char token[] = TOKEN;
-char clientId[] = "d:" ORG ":" DEVICE_TYPE ":" DEVICE_ID;
-int ibmPort = 1883;
+char *projectName = "WaterMonitor";
+uint16_t cmdID = 0;
+uint32_t lastRead = 0;
+uint32_t uploadInterval = 5;
-/* Internet Varialbles */
-bool internetState = false;
-uint8_t failAttempt = 0;
-uint16_t cmdID = 0;
-
-/* Time Handles */
-uint32_t uploadPeriod = 6;
-uint32_t lastRead;
+/* MQTT Varialbles */
+static char id[30] = ID; // mac without colons
+static char org[12] = ORG;
+static char type[30] = TYPE;
+static char auth_token[30] = AUTH_TOKEN; // Auth_token is only used in non-quickstart mode
+static int connack_rc = 0; // MQTT connack return code
+static bool netConnecting = false;
+static bool mqttConnecting = false;
+static bool netConnected = false;
+static bool connected = false;
+static int retryAttempt = 0;
+static int connectTimeout = 1000;
+static char subscription_url[MQTT_MAX_PAYLOAD_SIZE];
+char topicCMD[] = "iot-2/cmd/command/fmt/json";
/* Analog Handles */
float ADC_PHVal;
float ADC_DOVal;
-float voltageValue;
/***************************************************************
* Structs/Classess
***************************************************************/
-NetworkInterface* network = easy_connect(true);
-MQTTNetwork mqttNetwork(network);
-MQTT::Client<MQTTNetwork, Countdown> client(mqttNetwork);
+static Serial pc(SERIAL_TX, SERIAL_RX);
-Timer readTime;
-Ticker readADC;
+DigitalOut myled(LED1);
+AnalogIn phSensor(SENSOR_1_PIN);
+AnalogIn DOSensor(SENSOR_2_PIN);
-AnalogIn phSensor(SENSOR_1_PIN);
-AnalogIn DOSensor(SENSOR_2_PIN);
-
-Serial serial(USBTX, USBRX);
+Timer timer;
/***************************************************************
* Unity function definitions
***************************************************************/
-/**
- * @brief Establish a connection to the internet.
- * @retval MQTT_SUCCESS if connected to the internet, MQTT_NETWORK_FAIL if failed to establish the connection
- */
-int MQTT_internetConnect();
-
-/**
- * @brief Connect to the MQTT data and subscribe to the topic.
- * @retval MQTT_SUCCESS if connected to the data and subscribed the topic successfully.
- */
-int MQTT_networkConnect();
-
-/**
- * @brief Connect to the internet and then MQTT network.
- * @retval MQTT_SUCCESS if the 2 above functions succeeded.
- */
-int MQTT_init();
-
-/**
- * @brief Publish a message to the MQTT topic.
- * @param sendMessage[in]: the message to be sent.
- * @retval MQTT_SUCCESS if the message is sent.
- */
-int MQTT_publish(char *sendMessage);
-
-/**
- * @brief Publish the information about the device when start up.
- * @retval None.
- */
-void MQTT_publishDeviceInfo();
-
-/**
- * @brief Read ADC values
- * @retval None.
- */
+void subscribe_cb(MQTT::MessageData & msgMQTT);
+int subscribe(char *pubTopic, MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client);
+int connect(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTNetwork *mqttNetwork, NetworkInterface* network);
+int getConnTimeout(int attemptNumber);
+void attemptConnect(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTNetwork *mqttNetwork, NetworkInterface* network);
+int publish(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client);
void getADC();
-/**
- * @brief Start the timer
- * @retval None.
- */
-void setTimer();
-
-/***************************************************************
- * Callbacks
- ***************************************************************/
-void messageArrived(MQTT::MessageData& md) {
- MQTT::Message &message = md.message;
- logMessage("Message arrived: qos %d, retained %d, dup %d, packetid %d\r\n", message.qos, message.retained, message.dup, message.id);
- logMessage("Payload %.*s\r\n", message.payloadlen, (char*)message.payload);
-}
-
/***************************************************************
* Unity function declarations
- ***************************************************************/
-int MQTT_internetConnect() {
- network = easy_connect(true);
-
- if (!network) {
- printf("Cannot connect to the internet\r\n");
- internetState = false;
- return MQTT_SUCCESS;
- }
- else {
- printf("Reconnected to the internet\r\n");
- internetState = true;
- return MQTT_NETWORK_FAIL;
- }
-}
+ ***************************************************************/
+void subscribe_cb(MQTT::MessageData & msgMQTT) {
+ char msg[MQTT_MAX_PAYLOAD_SIZE];
+ msg[0]='\0';
+ strncat (msg, (char*)msgMQTT.message.payload, msgMQTT.message.payloadlen);
+ printf ("--->>> subscribe_cb msg: %s\n\r", msg);
+}
-int MQTT_networkConnect() {
- logMessage("Connecting to %s:%d\r\n", server, ibmPort);
+int subscribe(char *pubTopic, MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client) {
+ return client->subscribe(pubTopic, MQTT::QOS1, subscribe_cb);
+}
+
+int connect(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTNetwork *mqttNetwork, NetworkInterface* network) {
+ const char* iot_ibm = MQTT_BROKER_URL;
+ char hostname[strlen(org) + strlen(iot_ibm) + 1];
- int rc = MQTT_SUCCESS;
- rc = mqttNetwork.connect(server, ibmPort);
-
+ sprintf(hostname, "%s%s", org, iot_ibm);
+ // Construct clientId - d:org:type:id
+ char clientId[strlen(org) + strlen(type) + strlen(id) + 5];
+ sprintf(clientId, "d:%s:%s:%s", org, type, id);
+ sprintf(subscription_url, "%s.%s/#/device/%s/%s/", org, "internetofthings.ibmcloud.com", id, DEFAULT_TYPE_NAME);
+
+ // Network debug statements
+ LOG("=====================================\n\r");
+ LOG("Nucleo IP ADDRESS: %s\n\r", network->get_ip_address());
+ LOG("Nucleo MAC ADDRESS: %s\n\r", network->get_mac_address());
+ LOG("Server Hostname: %s port: %d\n\r", hostname, IBM_IOT_PORT);
+// for(int i = 0; clientId[i]; i++){ // set lowercase mac
+// clientId[i] = tolower(clientId[i]);
+// }
+ LOG("Client ID: %s\n\r", clientId);
+ LOG("Topic: %s\n\r",MQTT_EVENT_TOPIC);
+ LOG("Subscription URL: %s\n\r", subscription_url);
+ LOG("=====================================\n\r");
+ netConnecting = true;
+ int rc = mqttNetwork->connect(hostname, IBM_IOT_PORT);
if (rc != 0) {
- logMessage("rc from TCP connect is %d\r\n", rc);
- return rc;
- }
-
- MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
- data.MQTTVersion = 3;
- data.clientID.cstring = clientId;
- data.username.cstring = authMethod;
- data.password.cstring = token;
-
- if ((rc = client.connect(data)) != 0) {
- logMessage("rc from MQTT connect is %d\r\n", rc);
- return rc;
- }
-
- if ((rc = client.subscribe(topicCMD, MQTT::QOS0, messageArrived)) != 0) {
- logMessage("rc from MQTT subscribe is %d\r\n", rc);
+ printf("rc from TCP connect is %d\r\n", rc);
return rc;
}
- return rc;
+ printf ("--->TCP Connected\n\r");
+ netConnected = true;
+ netConnecting = false;
+
+ // MQTT Connect
+ mqttConnecting = true;
+ MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
+ data.MQTTVersion = 4;
+ data.struct_version = 0;
+ data.clientID.cstring = clientId;
+ data.keepAliveInterval = MQTT_KEEPALIVE; // in Sec
+ data.username.cstring = "use-token-auth";
+ data.password.cstring = auth_token;
+ printf ("AutToken: %s\n\r", auth_token);
+
+ if ((rc = client->connect(data)) != 0) {
+ printf("rc from MQTT connect is %d\r\n", rc);
+ connack_rc = rc;
+ return rc;
+ }
+ connected = true;
+ printf ("--->MQTT Connected\n\r");
+ if ((rc=subscribe(topicCMD, client)) == 0) {
+ LOG ("--->>>MQTT subscribed to: %s\n\r", topicCMD);
+ } else {
+ LOG ("--->>>ERROR MQTT subscribe : %s\n\r", topicCMD);
+ }
+ mqttConnecting = false;
+ connack_rc = rc;
+ return rc;
}
-int MQTT_init() {
- int ret = MQTT_SUCCESS;
- if (!network) {
- logMessage("Failed to connect to the internet, retrying\r\n");
- ret = MQTT_internetConnect();
- if (ret != MQTT_SUCCESS) {
- return ret;
- }
- }
- internetState = true;
-
- ret = MQTT_networkConnect();
- if (ret != MQTT_SUCCESS) {
- printf("Fail to connect to MQTT or fail to subscribe\r\n");
- internetState = false;
- }
-
- return ret;
+
+int getConnTimeout(int attemptNumber) { // First 10 attempts try within 3 seconds, next 10 attempts retry after every 1 minute
+ // after 20 attempts, retry every 10 minutes
+ return (attemptNumber < 10) ? 3 : (attemptNumber < 20) ? 60 : 600;
}
-int MQTT_publish(char *sendMessage) {
- MQTT::Message msg;
- msg.qos = MQTT::QOS0;
- msg.retained = false;
- msg.dup = false;
- msg.payload = sendMessage;
- msg.payloadlen = strlen(sendMessage);
- return client.publish((char *)topicEvent, msg);
+
+void attemptConnect(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTNetwork *mqttNetwork, NetworkInterface* network) {
+ connected = false;
+
+ while (connect(client, mqttNetwork, network) != MQTT_CONNECTION_ACCEPTED) {
+ if (connack_rc == MQTT_NOT_AUTHORIZED || connack_rc == MQTT_BAD_USERNAME_OR_PASSWORD) {
+ printf ("File: %s, Line: %d Error: %d\n\r",__FILE__,__LINE__, connack_rc);
+ return; // don't reattempt to connect if credentials are wrong
+ }
+ int timeout = getConnTimeout(++retryAttempt);
+ WARN("Retry attempt number %d waiting %d\n", retryAttempt, timeout);
+
+ // if ipstack and client were on the heap we could deconstruct and goto a label where they are constructed
+ // or maybe just add the proper members to do this disconnect and call attemptConnect(...)
+ // this works - reset the system when the retry count gets to a threshold
+ if (retryAttempt == 5)
+ NVIC_SystemReset();
+ else
+ wait(timeout);
+ }
}
-void MQTT_publishDeviceInfo() {
- char greetingMessage[100];
- sprintf(greetingMessage, "{\"Device Name\":\"PROEVN\",\"Firmware Version\":%.2f}", firmwareVersion);
- printf("Sending payload: %s\r\n", greetingMessage);
- if (!MQTT_publish((char *)greetingMessage)) {
- printf("Publish ok\r\n");
- failAttempt = 0;
- }
- else {
- printf("Publish failed\r\n");
- failAttempt++;
- }
-}
+int publish(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client) {
+ MQTT::Message message;
+ const char* pubTopic = MQTT_EVENT_TOPIC;
+
+ char buf[MQTT_MAX_PAYLOAD_SIZE];
+ float adc0;
-void upload() {
- char payload[100];
- sprintf(payload, "{\"Type\":1,\"Command ID\":%d,\"ADC0\":%.2f,\"Voltage 1\":%.2f}", cmdID, ADC_PHVal, voltageValue);
- if (!MQTT_publish(payload)) {
- cmdID++;
- }
+ if (!client->isConnected()) {
+ printf ("---> MQTT DISCONNECTED\n\r"); return MQTT::FAILURE;
+ }
+
+ adc0 = ADC_PHVal;
+
+ sprintf(buf,
+ "{\"Project\":\"%s\",\"Type\":1,\"cmdID\":%d,\"ADC0\":%0.4f}",
+ projectName, cmdID, adc0);
+ message.qos = MQTT::QOS0;
+ message.retained = false;
+ message.dup = false;
+ message.payload = (void*)buf;
+ message.payloadlen = strlen(buf);
+
+ if((message.payloadlen + strlen(pubTopic)+1) >= MQTT_MAX_PACKET_SIZE)
+ printf("message too long!\r\n");
+
+ LOG("Publishing %s\n\r", buf);
+ return client->publish(pubTopic, message);
}
void getADC() {
ADC_PHVal = phSensor.read();
// ADC_DOVal = DOSensor.read();
- voltageValue = (ADC_PHVal * 5.0);
+// voltageValue = (ADC_PHVal * 5.0);
}
-
-void setTimer() {
- readTime.start();
- readADC.attach(&getADC, 1);
-}
-
/***************************************************************
* Main
- ***************************************************************/
-void main(int argc, char* argv[]) {
- serial.baud(115200);
-
- logMessage("IoT Water Monitor project, firmware version is %.2f\r\n", firmwareVersion);
-
- MQTT_init();
- wait(0.5);
- MQTT_publishDeviceInfo();
-
- setTimer();
-
- uploadPeriod = 6;
- printf("Get into loop\r\n");
-
- while(true) {
- if ((uint32_t)(readTime.read() - lastRead) > uploadPeriod) {
- char payload[100];
- sprintf(payload, "{\"Type\":1,\"Command ID\":%d,\"ADC0\":%.2f,\"Voltage 1\":%.2f}", cmdID, ADC_PHVal, voltageValue);
- printf("MQTT publish %s\r\n", payload);
- if (!MQTT_publish(payload)) {
- printf("Publish ok\r\n");
- cmdID++;
- failAttempt = 0;
- }
- else {
- printf("Publish failed\r\n");
- failAttempt++;
- }
- if (failAttempt == MAX_FAIL_ATTEMPT) {
- failAttempt = 0;
- internetState = false;
- }
- lastRead = readTime.read();
- }
- client.yield(100);
- }
-
+ ***************************************************************/
+int main() {
+ pc.baud(115200);
+ timer.start();
+ lastRead = 0;
+ pc.printf("\r\nX-NUCLEO-IDW01M1 mbed Application\r\n");
+ pc.printf("\r\nconnecting to AP\r\n");
+
+ NetworkInterface* network = easy_connect(true);
+ if (!network) {
+ printf ("Error easy_connect\n\r");
+ return -1;
+ }
+ MQTTNetwork mqttNetwork(network);
+ MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE> client(mqttNetwork);
+
+ printf ("ATTEMPT CONNECT\n\r");
+ attemptConnect(&client, &mqttNetwork, network);
+ if (connack_rc == MQTT_NOT_AUTHORIZED || connack_rc == MQTT_BAD_USERNAME_OR_PASSWORD) {
+ printf ("---ERROR line : %d\n\r", __LINE__);
+ while (true)
+ wait(1.0); // Permanent failures - don't retry
+ }
+
+ myled=1;
+ int count = 0;
+ while (true) {
+ getADC();
+ if (connected == true) {
+ if ((uint32_t)(timer.read() - lastRead) >= uploadInterval) { // Publish a message every 3 second
+ if (publish(&client) != MQTT::SUCCESS) {
+ myled=0;
+ count=0;
+ client.disconnect();
+ mqttNetwork.disconnect();
+ attemptConnect(&client, &mqttNetwork, network); // if we have lost the connection
+ } else {
+ myled = 1;
+ count = 0;
+ cmdID++;
+ }
+ lastRead = timer.read();
+ }
+ client.yield(500); // allow the MQTT client to receive subscribe messages and manage keep alive
+ } else if (connected == false) {
+ connected = true;
+ } else {
+ wait(0.5);
+ }
+ }
}
-
-
+
\ No newline at end of file
--- a/mbed-os.lib Sat Dec 02 04:59:59 2017 +0000 +++ b/mbed-os.lib Thu Dec 07 17:07:44 2017 +0000 @@ -1,1 +1,1 @@ -https://github.com/ARMmbed/mbed-os/#2885c1b41e63158cb6faf5f107cd821ae06ef26c +https://github.com/ARMmbed/mbed-os/#5f6572179d66ce4c09d6517b659ac51133cc980d
--- a/mbed_app.json Sat Dec 02 04:59:59 2017 +0000
+++ b/mbed_app.json Thu Dec 07 17:07:44 2017 +0000
@@ -1,69 +1,58 @@
-{
- "config": {
- "network-interface":{
- "help": "options are ETHERNET, WIFI_ESP8266, WIFI_ODIN, WIFI_RTW, MESH_LOWPAN_ND, MESH_THREAD, CELLULAR_ONBOARD",
- "value": "WIFI_ESP8266"
- },
- "mesh_radio_type": {
- "help": "options are ATMEL, MCR20",
- "value": "ATMEL"
- },
- "esp8266-tx": {
- "help": "Pin used as TX (connects to ESP8266 RX)",
- "value": "D1"
- },
- "esp8266-rx": {
- "help": "Pin used as RX (connects to ESP8266 TX)",
- "value": "D0"
- },
- "esp8266-ssid": {
- "value": "\"GreenLantern\""
- },
- "esp8266-password": {
- "value": "\"deAlembert\""
- },
- "esp8266-debug": {
- "value": true
- },
- "lcd-mosi": {
- "value": "D11",
- "macro_name": "LCD_MOSI"
- },
- "lcd-sck": {
- "value": "D13",
- "macro_name": "LCD_SCK"
- },
- "lcd-miso": {
- "value": "D12",
- "macro_name": "LCD_MISO"
- },
- "lcd-a0": {
- "value": "D7",
- "macro_name": "LCD_A0"
- },
- "lcd-ncs": {
- "value": "D10",
- "macro_name": "LCD_NCS"
- }
- },
- "target_overrides": {
- "*": {
- "target.features_add": ["NANOSTACK", "LOWPAN_ROUTER", "COMMON_PAL"],
- "mbed-mesh-api.6lowpan-nd-channel-page": 0,
- "mbed-mesh-api.6lowpan-nd-channel": 12,
- "mbed-trace.enable": 0
- },
- "HEXIWEAR": {
- "esp8266-tx": "PTD3",
- "esp8266-rx": "PTD2"
- },
- "NUCLEO_F401RE": {
- "esp8266-tx": "D8",
- "esp8266-rx": "D2"
- },
- "NUCLEO_F411RE": {
- "esp8266-tx": "D8",
- "esp8266-rx": "D2"
- }
- }
-}
+{
+ "config": {
+ "network-interface":{
+ "help": "options are ETHERNET, WIFI_ESP8266, WIFI_IDW0XX1, WIFI_ODIN, WIFI_RTW, MESH_LOWPAN_ND, MESH_THREAD, CELLULAR_ONBOARD",
+ "value": "WIFI_ESP8266"
+ },
+ "mesh_radio_type": {
+ "help": "options are ATMEL, MCR20",
+ "value": "ATMEL"
+ },
+ "esp8266-tx": {
+ "help": "Pin used as TX (connects to ESP8266 RX)",
+ "value": "D1"
+ },
+ "esp8266-rx": {
+ "help": "Pin used as RX (connects to ESP8266 TX)",
+ "value": "D0"
+ },
+ "esp8266-ssid": {
+ "value": "\"GreenLantern\""
+ },
+ "esp8266-password": {
+ "value": "\"deAlembert\""
+ },
+ "esp8266-debug": {
+ "value": true
+ }
+ },
+ "macros": [ "MQTT_BROKER_URL=\".messaging.internetofthings.ibmcloud.com\"",
+ "MQTT_CLIENT_ID=\"d:a4nvkh:Nucleo_8266:PROEVN\"",
+ "MQTT_ORG_ID=\"a4nvkh\"",
+ "MQTT_USERNAME=\"use-token-auth\"",
+ "MQTT_DEVICE_PASSWORD=\"PROEVN2017\"",
+ "MQTT_EVENT_TOPIC=\"iot-2/evt/status/fmt/json\"",
+ "MQTT_DEVICE_ID=\"PROEVN\"",
+ "MQTT_DEVICE_TYPE=\"Nucleo_8266\"",
+ "MQTT_KEEPALIVE 10",
+ "MQTT_PORT 1883",
+ "MQTT_TLS_PORT 8883"],
+ "target_overrides": {
+ "*": {
+ "target.features_add": ["NANOSTACK", "LOWPAN_ROUTER", "COMMON_PAL"],
+ "mbed-mesh-api.6lowpan-nd-channel-page": 0,
+ "mbed-mesh-api.6lowpan-nd-channel": 12
+ },
+ "K64F": {
+ },
+ "NUCLEO_F401RE": {
+ "wifi-tx": "D8",
+ "wifi-rx": "D2",
+ "target.macros_add": ["IKS01A2"]
+ },
+ "DISCO_L475VG_IOT01A": {
+ },
+ "NUCLEO_L476RG": {
+ }
+ }
+}