Implement CloudService::instance().send() with ACK

Hi @joel ,

Would you be able to provide me with an example of how I would use
CloudService::instance().send() in order to get acknowledgment that the publish has been received by the cloud?

I have watched the Spectra video with you and Simon and understand the basics of how the Cloud_Service Publish works and have replicated your example in my own project but I am having a hard time deciphering the library files to determine what values I need to pass into CloudService::instance().send().

Easiest would be to look at an example from GitHub - particle-iot/tracker-edge: Particle Tracker reference application which heavily uses these libraries.

This code-snippet from the ConfigService uses CloudService to send configuration updates up to the Particle cloud. fw-config-service/config_service.cpp at b6002b9a87ab955180693b133a4741ed98ff8958 · particle-iot/fw-config-service · GitHub

                        cloud_service.beginCommand(CLOUD_CMD_CFG);
                        cloud_service.writer().name("cfg").beginObject();
                        config_write_json(it.root, cloud_service.writer());
                        cloud_service.writer().endObject();
                        if(!cloud_service.send(WITH_ACK, CloudServicePublishFlags::NONE, &ConfigService::config_sync_ack_cb, this, CLOUD_DEFAULT_TIMEOUT_MS, nullptr))
                        {
                            config_sync_pending_object = ⁢
                            // possible config could be updated again before ack
                            // received so save off the hash we sent rather than
                            // simply using the current hash in the callback
                            config_sync_pending_hash = it.hash;
                            break;
                        }

The first line starts a new command. There is a defined constant string CLOUD_CMD_CFG but anything will work here. It goes into the generated JSON as something like {"cmd":"your_command_here"...}.

Then you can begin constructing all the other contents of your message (if any) via cloud_service.writer() (or however you are referencing the CloudService instance). In this case it is defining a cfg object in the JSON, adding some contents to that, then ending the object. So something like {"cmd":"your_command_here","cfg":{...}}. Any valid JSON via the writer is OK here. Just keep in mind you are still bound by the basic Particle.publish limits and the cloud service itself may put in some extra fields (like timestamp and a request id for acknowledgements) so leave a little space.

Once you finish setting up the JSON just hit a .send() call. You provide the level of acknowledge with the Particle Cloud (same as a normal Particle.publish) and can also provide a level of end-to-end acknowledgement. That required extra work on your server side so probably stick with CloudServicePublishFlags::NONE. You can also provide a callback (see ConfigService::config_sync_ack_cb for an example of how that would look) to take action on the success/failure of the publish. The timeout provided only matters for end-to-end acknowledgement (so doesn’t actually matter for this example). The context pointer is null here but lets you pass any extra data into your callback that might be useful.

It is possible for the send to fail immediately (for example if the connection is down, in the middle of another operation, etc). But if it is accepted you should probably set a flag or otherwise mark that you have a pending publish and wait until it is complete via the callback. In this case, the config service marks the hash that is currently pending in publish to handle within the callback. Also lets it defer blasting out more config updates until the current one is complete.

If you end up going for end-to-end acknowledgements (very useful for critical data) there is an ack format your server can send back. The default is to accept those as Particle functions but you can easily set it up so the ack can be sent via webhook responses (more efficient). This requires a couple line linkage in the firmware code and matching webhook defined in your product along with code running server-side.

2 Likes

Thank you very much! @joel