Particle subscribe handler data length

I am using Particle.Subscribe and a handler function to listen to events published by my server.

However when the server goes down it sends back a HTML “Server not available” message. When this happens my Photon freezes. I think it is due to the size of the message which I have no control over - maybe causing the stack to overflow?

Is there a size limit on the data returned?
Is there any way I can ignore messages over a certain size?
Any other ideas of what could cause this behavior or how to debug it further?

I’d suspect your code to be part of the trouble, but it’s hard to tell without seeing it.

@joe4465, any chance you can post the message? A webhook response can send back multiple 512byte payloads so that would be the limit on a single message I suspect. However, that message is buffered by the system so there is no buffer overflow. How you parse the message in your event handler may be the issue.

1 Like

Hi,

This is the message sent back:

<!DOCTYPE html>
<html>
<head>
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <style type="text/css">
   html, body, iframe { margin: 0; padding: 0; height: 100%; }
   iframe { display: block; width: 100%; border: none; }
 </style>
<title>Offline for Maintenance</title>
</head>
<body>
 <iframe src="about:blank">
   <p>Application Error</p>
 </iframe>
</body>
</html>

and the event handler:

void Suite::replyHandler(const char *event, const char *data)
{
    Serial.printf("Data received: %s\r\n", data);

    String reply = String(data);

    if(reply.startsWith("s:")) handleSuccessMessage(reply);
    else if(reply.startsWith("e:nf")) handleNotFoundErrors(reply);
    else if(reply.startsWith("e:reg")) handleRegErrors(reply);
    else if(reply.startsWith("e:auth")) handleAuthErrors(reply);
    else if(reply.startsWith("e:data")) handleDataError(reply);
    else return;
}

The issue seems to happen once the Photon has received about 80 messages sent once per second. When normal messages are received the code runs correctly.

I’m running a state machine with transitions triggered by the received messages (or lack of) so if the above looks correct the problem is probably with that.

Thanks

hmm maybe don’t use String.

I found this OTA Updates Failing on Core
Why does the use of String cause heap exhaustion?

So should I be using std::string instead of String? What is the difference between the two?
Or should I be using char[] or char*?

The particle cloud functions seem to use a mixture of const char* and String.

What is the advantages and disadvantages of using the above?

1 Like

I think the potential problems with heap fragmentation in the use of String also exist with std::string since both actually wrap up their own, internal buffers and have to move/reallocate the string if it outgrows the reserved area and since we are dealing with a microcontroler “OS” with (to say the least) less sophisticated garbage collection/heap defragmentation routines you might still end up with a highly fragmented heap preventing your application from allocating a new string or moving an existing string that outgrew its buffer.

This is all no problem on a fully fletched OS, hence you’ll find most programmers on that side to vouch for std::string (as I’d do there too), but on these devices I personally rather go the potentially more cumbersome but safer route, where I have full control over things :wink:

But in any case, if you know what you are doing, you can choose either way, just asses the risks and take precautions.

OK, so you would recommend using “const char*” with legacy C functions to process the information?

My handler function now looks like this:

void Suite::replyHandler(const char* event, const char* data)
{
    Serial.printf("Data received: %s\r\n", data);

    if(strncmp(data, "s:", 2) == 0) handleSuccessMessage(data);
    else if(strncmp(data, "e:nf", 4) == 0) handleNotFoundErrors(data);
    else if(strncmp(data, "e:reg", 5) == 0) handleRegErrors(data);
    else if(strncmp(data, "e:auth", 6) == 0) handleAuthErrors(data);
    else if(strncmp(data, "e:data", 6) == 0) handleDataError(data);
    else _reply = NA;
}

Is this correct?

2 Likes

Looks good, but since we can’t see your functions handle...() it might be an idea to just set an FSM flag and move the actual calls out of the handler and have an FSM outside do the calling.

In this case you’d need to copy *data away.

yep, that is what those handle…() functions do. They set a flag that the state machine checks periodically.

void Suite::handleSuccessMessage(const char* data)
{
    if(strcmp(data, "s:reg1")) _reply = S_REG1;
    else if(strncmp(data, "s:reg2_key", 10))
    {
        _reply = S_REG2;
        _accessKey = extractParameter(data);
    }
    else if(strcmp(data, "s:data1")) _reply = S_DATA1;
    else if(strncmp(data, "s:data2", 7)) _reply = S_DATA2;
    else _reply = NA;
}

I see, that should not pose a problem then.

BTW: Are you intentionally checking your strings to be non-equal to your string literals?

1 Like

ha no, missing a “== 0” on the end of those.

Good spot!

Whilst on the topic of String vs const char*…

Is there a reason

Particle.function() 

forces you to use a handler that takes a String parameter. It would be nice to pass a const char* here as well so it matches with the

Particle.subscribe() 

data handlers.

Secondly is it possible to declare a char* in the header file that gets assigned in the constructor?

i.e.
Suite.h

#ifndef __SUITE_H_
#define __SUITE_H_

class Suite
{
  public:
    Suite(const char* accessKey);
    virtual ~Suite();

  private:
    char* _accessKey;
};

#endif

and Suite.cpp

#ifndef __SUITE_CPP_
#define __SUITE_CPP_

#include "Suite.h"

Suite::Suite(const char* accesskey) : _accessKey(accesskey) {}

Suite::~Suite() {}

#endif

Thanks

This would be something to ask :particle: Particle direct (maybe @mdma), but here my personal guess :wink:

Particle.function() was implemented some generations before Particle.subscribe() and only a while after the Spark Core got into the hands of people who tried to push the boundaries and squeeze every byte out of RAM, the implications of using String excessively became evident and there was no time or call to overhaul Particle.function()

But I might be completely off the beam, with this :stuck_out_tongue_winking_eye:

Sure, you can pass a C string into a constructor, but instead of storing the address you might rather pull a copy of the string and store it in a private field (which could be a char[] or a dynamic buffer, with all the heap implications again).

Thanks that explanation seems to make sense.

Please could explain the second part a bit more, i.e. what is the correct way to have a global char array in a class that is assigned when the constructor is called.

Cheers

I don’t know the history, but for a 1.0 API I would hope to iron out these inconsistencies.

It depends what you’ll do with this string inside your class.

When you mention “a global char array”, you’d have to go with a pointer, but have to make sure, that this string be safe to use at any time. But this somehow counteracts the encapsulation and self-sufficiency idea of OOP classes.

If you only want to pass in a string once so that the class can be constructed based on that string and thereafter owns the string, then you’d do something like this

class myClass
{
  public: 
    myClass(const char* setupInfo);

  private:
    char __setupInfo[MAX_LEN];
}

myClass::myClass(const char* setupInfo)
{
  int len = strncpy(__setupInfo, setupInfo, MAX_LEN);
  if (len < 0 || len >= MAX_LEN)
  { // input string was too long, so manually terminate
    __setupInfo[MAX_LEN-1] = '\0'; 
  };
}

If you want to manipulate the string from outside, you’d need to provide a public (setter) method to do so.
And for reading the contents back, you also provide a (getter) method which should return a const char*.

I’d say these are the most common ways to do this, but there are others too (e.g. dynamic mem allocation) :wink:

1 Like

Cool, thanks that makes sense.

Is there any advantage/disadvantage/difference between defining _setupInfo as char* and char[]?

Yes there is a big difference!

char __setupInfo[MAX_LEN]; already reserves the memory space required to hold MAX_LEN-1 characters, while char* __setupInfo would only reserve a four byte portion to hold a pointer, which might point anywhere (or nowhere), but no space for the string will be reserved.

Which is safer?

If you need to ask, then definetly char[] :wink: