4343#include < algorithm>
4444#include " cbor/CBOREncoder.h"
4545#include " utility/watchdog/Watchdog.h"
46+ #include < typeinfo>
4647
4748/* *****************************************************************************
4849 LOCAL MODULE FUNCTIONS
@@ -62,7 +63,6 @@ ArduinoIoTCloudTCP::ArduinoIoTCloudTCP()
6263, _connection_attempt(0 ,0 )
6364, _message_stream(std::bind(&ArduinoIoTCloudTCP::sendMessage, this , std::placeholders::_1))
6465, _thing(&_message_stream)
65- , _thing_id_property{nullptr }
6666, _device(&_message_stream)
6767, _mqtt_data_buf{0 }
6868, _mqtt_data_len{0 }
@@ -76,8 +76,8 @@ ArduinoIoTCloudTCP::ArduinoIoTCloudTCP()
7676, _mqttClient{nullptr }
7777, _deviceTopicOut(" " )
7878, _deviceTopicIn(" " )
79- , _shadowTopicOut (" " )
80- , _shadowTopicIn (" " )
79+ , _messageTopicOut (" " )
80+ , _messageTopicIn (" " )
8181, _dataTopicOut(" " )
8282, _dataTopicIn(" " )
8383#if OTA_ENABLED
@@ -181,24 +181,22 @@ int ArduinoIoTCloudTCP::begin(bool const enable_watchdog, String brokerAddress,
181181 _mqttClient.setUsernamePassword (getDeviceId (), _password);
182182 }
183183#endif
184+
184185 _mqttClient.onMessage (ArduinoIoTCloudTCP::onMessage);
185186 _mqttClient.setKeepAliveInterval (30 * 1000 );
186187 _mqttClient.setConnectionTimeout (1500 );
187188 _mqttClient.setId (getDeviceId ().c_str ());
188189
189190 _deviceTopicOut = getTopic_deviceout ();
190191 _deviceTopicIn = getTopic_devicein ();
191-
192- Property* p;
193- p = new CloudWrapperString (_lib_version);
194- addPropertyToContainer (_device.getPropertyContainer (), *p, " LIB_VERSION" , Permission::Read, -1 );
195- p = new CloudWrapperString (_thing_id);
196- _thing_id_property = &addPropertyToContainer (_device.getPropertyContainer (), *p, " thing_id" , Permission::ReadWrite, -1 ).writeOnDemand ();
192+ _messageTopicIn = getTopic_messagein ();
193+ _messageTopicOut = getTopic_messageout ();
197194
198195 _thing.begin ();
199196 _device.begin ();
200197
201198#if OTA_ENABLED
199+ Property* p;
202200 p = new CloudWrapperBool (_ota_cap);
203201 addPropertyToContainer (_device.getPropertyContainer (), *p, " OTA_CAP" , Permission::Read, -1 );
204202 p = new CloudWrapperInt (_ota_error);
@@ -322,9 +320,16 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_ConnectMqttBroker()
322320{
323321 if (_mqttClient.connect (_brokerAddress.c_str (), _brokerPort))
324322 {
325- DEBUG_VERBOSE (" ArduinoIoTCloudTCP::%s connected to %s:%d" , __FUNCTION__, _brokerAddress.c_str (), _brokerPort);
323+ /* Subscribe to message topic to receive commands */
324+ _mqttClient.subscribe (_messageTopicIn);
325+
326+ /* Temoporarly subscribe to device topic to receive OTA properties */
327+ _mqttClient.subscribe (_deviceTopicIn);
328+
326329 /* Reconfigure timers for next state */
327330 _connection_attempt.begin (AIOT_CONFIG_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms, AIOT_CONFIG_MAX_DEVICE_TOPIC_SUBSCRIBE_RETRY_DELAY_ms);
331+
332+ DEBUG_VERBOSE (" ArduinoIoTCloudTCP::%s connected to %s:%d" , __FUNCTION__, _brokerAddress.c_str (), _brokerPort);
328333 return State::Connected;
329334 }
330335
@@ -413,7 +418,6 @@ ArduinoIoTCloudTCP::State ArduinoIoTCloudTCP::handle_Disconnect()
413418 _thing.handleMessage (&message);
414419 _device.handleMessage (&message);
415420
416- _thing_id = " xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ;
417421 DEBUG_INFO (" Disconnected from Arduino IoT Cloud" );
418422 execCloudEventCallback (ArduinoIoTCloudEvent::DISCONNECT);
419423
@@ -440,68 +444,101 @@ void ArduinoIoTCloudTCP::handleMessage(int length)
440444 /* Topic for OTA properties and device configuration */
441445 if (_deviceTopicIn == topic) {
442446 CBORDecoder::decode (_device.getPropertyContainer (), (uint8_t *)bytes, length);
443-
444- /* Temporary check to avoid flooding device state machine with usless messages */
445- if (_thing_id_property->isDifferentFromCloud ()) {
446- _thing_id_property->fromCloudToLocal ();
447-
448- if (!_thing_id.length ()) {
449- /* Send message to device state machine to inform we have received a null thing-id */
450- _thing_id = " xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ;
451- Message message;
452- message = { DeviceRegisteredCmdId };
453- _device.handleMessage (&message);
454- } else {
455- if (_device.isAttached ()) {
456- detachThing ();
457- }
458- if (!_device.isAttached ()) {
459- attachThing ();
460- }
461- }
462- }
463447 }
464448
465449 /* Topic for user input data */
466450 if (_dataTopicIn == topic) {
467451 CBORDecoder::decode (_thing.getPropertyContainer (), (uint8_t *)bytes, length);
468452 }
469453
470- /* Topic for sync Thing last values on connect */
471- if (_shadowTopicIn == topic) {
472- DEBUG_VERBOSE (" ArduinoIoTCloudTCP::%s [%d] last values received" , __FUNCTION__, millis ());
473- /* Decode last values property array */
474- CBORDecoder::decode (_thing.getPropertyContainer (), (uint8_t *)bytes, length, true );
475- /* Unlock thing state machine waiting last values */
476- Message message = { LastValuesUpdateCmdId };
477- _thing.handleMessage (&message);
478- /* Call ArduinoIoTCloud sync user callback*/
479- execCloudEventCallback (ArduinoIoTCloudEvent::SYNC);
454+ /* Topic for device commands */
455+ if (_messageTopicIn == topic) {
456+ CommandDown command;
457+ DEBUG_VERBOSE (" ArduinoIoTCloudTCP::%s [%d] received %d bytes" , __FUNCTION__, millis (), length);
458+ CBORMessageDecoder decoder;
459+
460+ size_t buffer_length = length;
461+ if (decoder.decode ((Message*)&command, bytes, buffer_length) != Decoder::Status::Error) {
462+ DEBUG_VERBOSE (" ArduinoIoTCloudTCP::%s [%d] received command id %d" , __FUNCTION__, millis (), command.c .id );
463+ switch (command.c .id )
464+ {
465+ case CommandId::ThingUpdateCmdId:
466+ {
467+ DEBUG_VERBOSE (" ArduinoIoTCloudTCP::%s [%d] device configuration received" , __FUNCTION__, millis ());
468+ String new_thing_id = String (command.thingUpdateCmd .params .thing_id );
469+
470+ if (!new_thing_id.length ()) {
471+ /* Send message to device state machine to inform we have received a null thing-id */
472+ _thing_id = " xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ;
473+ Message message;
474+ message = { DeviceRegisteredCmdId };
475+ _device.handleMessage (&message);
476+ } else {
477+ if (_device.isAttached () && _thing_id != new_thing_id) {
478+ detachThing ();
479+ }
480+ if (!_device.isAttached ()) {
481+ attachThing (new_thing_id);
482+ }
483+ }
484+ }
485+ break ;
486+
487+ case CommandId::LastValuesUpdateCmdId:
488+ {
489+ DEBUG_VERBOSE (" ArduinoIoTCloudTCP::%s [%d] last values received" , __FUNCTION__, millis ());
490+ CBORDecoder::decode (_thing.getPropertyContainer (),
491+ (uint8_t *)command.lastValuesUpdateCmd .params .last_values ,
492+ command.lastValuesUpdateCmd .params .length , true );
493+ _thing.handleMessage ((Message*)&command);
494+ execCloudEventCallback (ArduinoIoTCloudEvent::SYNC);
495+
496+ /*
497+ * NOTE: in this current version properties are not properly integrated with the new paradigm of
498+ * modeling the messages with C structs. The current CBOR library allocates an array in the heap
499+ * thus we need to delete it after decoding it with the old CBORDecoder
500+ */
501+ free (command.lastValuesUpdateCmd .params .last_values );
502+ }
503+ break ;
504+
505+ default :
506+ break ;
507+ }
508+ }
480509 }
481510}
482511
483512void ArduinoIoTCloudTCP::sendMessage (Message * msg)
484513{
485- switch (msg->id )
486- {
487- case DeviceBeginCmdId:
488- sendDevicePropertiesToCloud ();
489- break ;
514+ uint8_t data[MQTT_TRANSMIT_BUFFER_SIZE];
515+ size_t bytes_encoded = sizeof (data);
516+ CBORMessageEncoder encoder;
490517
491- case ThingBeginCmdId:
492- requestThingId ();
493- break ;
518+ switch (msg->id ) {
519+ case PropertiesUpdateCmdId:
520+ return sendPropertyContainerToCloud (_dataTopicOut,
521+ _thing.getPropertyContainer (),
522+ _thing.getPropertyContainerIndex ());
523+ break ;
494524
495- case LastValuesBeginCmdId:
496- requestLastValue ();
497- break ;
525+ #if OTA_ENABLED
526+ case DeviceBeginCmdId:
527+ sendDevicePropertyToCloud (" OTA_CAP" );
528+ sendDevicePropertyToCloud (" OTA_ERROR" );
529+ sendDevicePropertyToCloud (" OTA_SHA256" );
530+ break ;
531+ #endif
498532
499- case PropertiesUpdateCmdId :
500- sendThingPropertiesToCloud () ;
501- break ;
533+ default :
534+ break ;
535+ }
502536
503- default :
504- break ;
537+ if (encoder.encode (msg, data, bytes_encoded) == Encoder::Status::Complete &&
538+ bytes_encoded > 0 ) {
539+ write (_messageTopicOut, data, bytes_encoded);
540+ } else {
541+ DEBUG_ERROR (" error encoding %d" , msg->id );
505542 }
506543}
507544
@@ -525,29 +562,6 @@ void ArduinoIoTCloudTCP::sendPropertyContainerToCloud(String const topic, Proper
525562 }
526563}
527564
528- void ArduinoIoTCloudTCP::sendThingPropertiesToCloud ()
529- {
530- sendPropertyContainerToCloud (_dataTopicOut, _thing.getPropertyContainer (), _thing.getPropertyContainerIndex ());
531- }
532-
533- void ArduinoIoTCloudTCP::sendDevicePropertiesToCloud ()
534- {
535- PropertyContainer ro_device_property_container;
536- unsigned int last_device_property_index = 0 ;
537-
538- std::list<String> ro_device_property_list {" LIB_VERSION" , " OTA_CAP" , " OTA_ERROR" , " OTA_SHA256" };
539- std::for_each (ro_device_property_list.begin (),
540- ro_device_property_list.end (),
541- [this , &ro_device_property_container ] (String const & name)
542- {
543- Property* p = getProperty (this ->_device .getPropertyContainer (), name);
544- if (p != nullptr )
545- addPropertyToContainer (ro_device_property_container, *p, p->name (), p->isWriteableByCloud () ? Permission::ReadWrite : Permission::Read);
546- }
547- );
548- sendPropertyContainerToCloud (_deviceTopicOut, ro_device_property_container, last_device_property_index);
549- }
550-
551565#if OTA_ENABLED
552566void ArduinoIoTCloudTCP::sendDevicePropertyToCloud (String const name)
553567{
@@ -563,28 +577,9 @@ void ArduinoIoTCloudTCP::sendDevicePropertyToCloud(String const name)
563577}
564578#endif
565579
566- void ArduinoIoTCloudTCP::requestLastValue ()
567- {
568- // Send the getLastValues CBOR message to the cloud
569- // [{0: "r:m", 3: "getLastValues"}] = 81 A2 00 63 72 3A 6D 03 6D 67 65 74 4C 61 73 74 56 61 6C 75 65 73
570- // Use http://cbor.me to easily generate CBOR encoding
571- const uint8_t CBOR_REQUEST_LAST_VALUE_MSG[] = { 0x81 , 0xA2 , 0x00 , 0x63 , 0x72 , 0x3A , 0x6D , 0x03 , 0x6D , 0x67 , 0x65 , 0x74 , 0x4C , 0x61 , 0x73 , 0x74 , 0x56 , 0x61 , 0x6C , 0x75 , 0x65 , 0x73 };
572- write (_shadowTopicOut, CBOR_REQUEST_LAST_VALUE_MSG, sizeof (CBOR_REQUEST_LAST_VALUE_MSG));
573- }
574-
575- void ArduinoIoTCloudTCP::requestThingId ()
576- {
577- if (!_mqttClient.subscribe (_deviceTopicIn))
578- {
579- /* If device_id is wrong the board can't connect to the broker so this condition
580- * should never happen.
581- */
582- DEBUG_ERROR (" ArduinoIoTCloudTCP::%s could not subscribe to %s" , __FUNCTION__, _deviceTopicIn.c_str ());
583- }
584- }
585-
586- void ArduinoIoTCloudTCP::attachThing ()
580+ void ArduinoIoTCloudTCP::attachThing (String thingId)
587581{
582+ _thing_id = thingId;
588583
589584 _dataTopicIn = getTopic_datain ();
590585 _dataTopicOut = getTopic_dataout ();
@@ -595,15 +590,6 @@ void ArduinoIoTCloudTCP::attachThing()
595590 return ;
596591 }
597592
598- _shadowTopicIn = getTopic_shadowin ();
599- _shadowTopicOut = getTopic_shadowout ();
600- if (!_mqttClient.subscribe (_shadowTopicIn)) {
601- DEBUG_ERROR (" ArduinoIoTCloudTCP::%s could not subscribe to %s" , __FUNCTION__, _shadowTopicIn.c_str ());
602- DEBUG_ERROR (" Check your thing configuration, and press the reset button on your board." );
603- _thing_id = " xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ;
604- return ;
605- }
606-
607593 Message message;
608594 message = { DeviceAttachedCmdId };
609595 _device.handleMessage (&message);
@@ -620,11 +606,6 @@ void ArduinoIoTCloudTCP::detachThing()
620606 return ;
621607 }
622608
623- if (!_mqttClient.unsubscribe (_shadowTopicIn)) {
624- DEBUG_ERROR (" ArduinoIoTCloudTCP::%s could not unsubscribe from %s" , __FUNCTION__, _shadowTopicIn.c_str ());
625- return ;
626- }
627-
628609 Message message;
629610 message = { DeviceDetachedCmdId };
630611 _device.handleMessage (&message);
0 commit comments