Most data operation–efficient way to get ACK

Hello—I'm trying to improve my publication efficiency without losing data. Following the helpful thread/reply here (Sleep after publish, updates? - #2 by armor) I was able to see a great path for adding a request for acknowledgment. However, I'm now publishing twice where I had been once. My question is probably very simple: (option 1) is it more "data operations efficient" to ask for acknowledgment in the main data being sent or (option 2) is a second publish with request for acknowledgment necessary? That is, could I replace the line inside the while loop with Particle.publish(eventName, data, 60, PRIVATE); and delete its first instance for improved efficiency, or does that risk repeatedly sending a more data-dense transmission and possibly end up costing me more?

Here is my current code

        Serial.println("publishing data");
        Particle.publish(eventName, data, 60, PRIVATE);

        // Hang here till acknowledgment received
        bool success = false;
        while(!success){
          success = Particle.publish("Received", NULL, WITH_ACK); // costs an extra data operation
        }

The full project is open-source and available here for what that's worth. Thanks!

1 Like

To what level do you need an acknowledgement?

That the event was received by the Particle Cloud? That can be determined by the result from Particle.publish, and is just a single data operation.

An end-to-end ACK, for example that the event triggered a webhook and was committed to a database? To do that, you generally have the final destination service issue a function call back to the device to indicate that it received the data, but that is still two data operations, more if retransmission is involved.

I'm not clear what your two publishes in the code above will accomplish. It will double your data operations and still doesn't indicate that the event was acted on by any external service.

1 Like

Thanks, @rickkas7. I do not need end-to-end acknowledgment, just Cloud. All I need is something that lets me know it’s OK to put the Boron to sleep as soon as possible after the data are transmitted. I’d previously used a hard-coded 4 second wait (and no request for acknowledgment) and was still missing some transmissions, presumably from going into ULP sleep before successful transmission.

You need to check the result of Particle.publish. If the result is true then the publish has completed. If false, then it failed. Failure typically occurs within 20 seconds, but in rare cases it could take up to 10 minutes.

If you don't check the result the call is asynchronous, even if using WITH_ACK, which explains why that didn't work with a 4 second delay, which is not long enough if a retransmission is required.

1 Like

OK, thanks, I think I understand. So with the following, modified implementation:

        // Hang here till acknowledgment received
        bool success = false;
        while(!success){
          success = Particle.publish(eventName, data, 60, PRIVATE, WITH_ACK);
        }

where I am publishing the data and waiting for acknowledgment, it will only "try" to publish once and wait for acknowledgment, or will the publish call keep trying as fast as the while loop allows it? In other words, the above seems like the better approach, but am I risking sending the data repeatedly this way? Really appreciate the help!

You don't want the loop to try once. Just check the result.

The reason this works is that Particle.publish() returns a Future object, not actually a bool. If you don't test it, the function generally returns quickly, before the publish occurs.

If you do check the result, you're checking the result of the future, which only completes when the underlying asynchronous operation completes. (It's also possible to do a non-blocking test of a future.)

1 Like

Fascinating. So just publish once but use the while loop to wait to proceed until the Future object changes to true? Have to admit I'd not heard of Future objects until your reply.

Something like the following goes against what I thought I knew about programming since there's no explicit new request, but if I'm interpreting your explanation correctly, the Future object will change once the ACK is received. Am I following correctly?

        bool success = false;
        Serial.println("publishing data");
        success = Particle.publish(eventName, data, 60, PRIVATE, WITH_ACK);
        while(!success){
          // Hang here till acknowledgment received
        }

No loop at all!

Serial.println("publishing data");
bool success = Particle.publish(eventName, data, 60, PRIVATE, WITH_ACK);
Serial.printlnf("publish result %d", success);        
1 Like

Ah, I was really overdoing it :man_facepalming:. Thanks for taking the time to explain this and help with code! I didn't understand this part of your answer previously but do now:

Minor note, maybe based on my includes, but in case anyone else wants to try this, the Serial.printlf() had to be changed to Serial.printlnf() for this to compile.

Thanks again!

I have been making publishes for years and only now understood the implications of the result. This is an interesting detail I completely missed from the docs. Checking the result will block until its value is updated.
What is also interesting here is this comment:

I thought that if I added WITH_ACK, it would check for it. Looks like I shouldn't add the ACK, unless I intend to check the result.
Thanks for the explanation.

1 Like

@supscientist - You may want to consider using this library for your use case:
PublishQueuePosixRK

It has the ACK built in (i.e. doesn't remove it from the queue until ACK is received) and also has the getCanSleep() indicating you can now go back to sleep when true.

bool PublishQueuePosix::getCanSleep() const

bool getCanSleep() const

If a publish is not in progress and the queue is empty, returns true.

If pausePublishing is true, then return true if either the current publish has completed, or not cloud connected.

This is just one of many very nice benefits in this library.

4 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.