On 06/11/17 09:15, Stuart Longland wrote:
> I've written some code to regularly sample a value (presently a pulse
> input counter) and POST it to a CoAP server I have written in NodeJS.
>
> This works well, for a while. After a period I start getting
> otCoapNewMessage returning NULL which suggests I'm overrunning a buffer
> somewhere.
For the benefit of discussion, this is the log I see from the device.
The protocol used over CoAP is based on CoRE RD and CoMI. The device
does "simple registration" to my CoRE RD server which then queries the
device's /.well-known/core endpoint.
After doing this, the server endpoint launches some commissioning code
that checks the "commission date" on the device, sees that it's 0
(meaning, not commissioned), then begins inserting some configuration
from a database and a CoAP endpoint for the device to report its samples to.
Also seen here is the NTP broadcast client in action.
Once commissioned, the node begins polling every 5 seconds, reading the
water meter pulse counters and reporting those to the CoAP endpoint. It
waits for the reply before sending the next message.
After a period, it seems there's a drop-off in communications. I'm not
sure what causes this at this time, but immediately afterwards, calls to
otCoapNewMessage return NULL, which is indicated by the log message
"main.c:631 FAILED POST HIST: Failed to create message". That code is here:
> static void schedule_write_response_handler(void *aContext, otCoapHeader *aHeader,
> otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aResult);
> static void schedule_write(otInstance* instance) {
> otCoapHeader request_header;
> otMessage* request = NULL;
> union {
> char c[128];
> uint8_t u[128];
> } buffer;
>
> if (!(schedule_todo & (1 << schedule_point))) {
> /* Move to next point */
> iprintf("%s:%d POST HISTORY: Skip point %d\r\n",
> __FILE__, __LINE__, schedule_point);
> schedule_point++;
> return;
> }
>
> printf("%s:%d POST HISTORY: Prepare point %d time %lu value %f\r\n",
> __FILE__, __LINE__, schedule_point, schedule_current,
> schedule_value[schedule_point]);
>
> /* Prepare the request */
> memset(&schedule_buffer, 0, sizeof(schedule_buffer));
> if (otPlatRandomGetTrue(schedule_buffer.token, sizeof(schedule_buffer.token))
> != OT_ERROR_NONE) {
> iprintf("%s:%d FAILED POST HISTORY: Failed to generate token\r\n",
> __FILE__, __LINE__);
> schedule_todo = 0;
> return;
> }
>
> /* Initialise message header */
> otCoapHeaderInit(&request_header,
> OT_COAP_TYPE_CONFIRMABLE, OT_COAP_CODE_POST);
> otCoapHeaderSetToken(&request_header,
> schedule_buffer.token, sizeof(registration_token));
> otCoapHeaderSetMessageId(&request_header, registration_msgid++);
>
> /* Figure out endpoint */
> const char* p = &schedule_path[1]; /* Skip past slash */
> while (p) {
> const char* slash = index(p, '/'); /* Find next slash */
> if (slash) {
> /* Next segment */
> strncpy(buffer.c, p, slash - p);
> p = &slash[1];
> } else {
> /* Last segment */
> strncpy(buffer.c, p, sizeof(buffer.c));
> p = NULL;
> }
> iprintf("%s:%d POST HISTORY: Path segment %s\r\n",
> __FILE__, __LINE__, buffer.c);
> otCoapHeaderAppendUriPathOptions(&request_header, buffer.c);
> }
>
> /* Final part of path, point ID */
> otCoapHeaderAppendUriPathOptions(&request_header,
> comi_point_config[schedule_point].id);
> otCoapHeaderSetPayloadMarker(&request_header);
>
> /* Prepare request message */
> request = otCoapNewMessage(instance, &request_header);
> if (!request) {
> iprintf("%s:%d FAILED POST HIST: Failed to create message\r\n",
> __FILE__, __LINE__);
> schedule_todo = 0;
> return;
> }
>
> /* History endpoint payload */
> CborEncoder encoder, map_encoder;
> cbor_encoder_init(&encoder, buffer.u, sizeof(buffer.u), 0);
> cbor_encoder_create_map(&encoder, &map_encoder, 2);
>
> cbor_encode_text_stringz(&map_encoder, "vrt-wsh:sample/timestamp");
> cbor_encode_uint(&map_encoder, (uint64_t)schedule_current);
> cbor_encode_text_stringz(&map_encoder, "vrt-wsh:sample/value");
> cbor_encode_float(&map_encoder, schedule_value[schedule_point]);
>
> cbor_encoder_close_container(&encoder, &map_encoder);
> if (otMessageAppend(request, buffer.u,
> cbor_encoder_get_buffer_size(&encoder, buffer.u))
> != OT_ERROR_NONE) {
> iprintf("%s:%d FAILED POST HIST: Failed to create message\r\n",
> __FILE__, __LINE__);
> schedule_todo = 0;
> return;
> }
>
> /* Send request */
> if (otCoapSendRequest(instance, request, &schedule_msginfo,
> schedule_write_response_handler, NULL)
> != OT_ERROR_NONE) {
> iprintf("%s:%d FAILED POST HIST: Failed to send message\r\n",
> __FILE__, __LINE__);
> return;
> }
>
> iprintf("%s:%d History sent for point %d\r\n", __FILE__, __LINE__,
> schedule_point);
> /* Wait for response */
> schedule_state = SCHEDULE_STATE_WRWAIT;
> }
>
> static void schedule_write_response_handler(void *aContext, otCoapHeader *aHeader,
> otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aResult) {
> (void)aContext;
>
> if (aMessage && aMessageInfo && aHeader) {
> otCoapCode code = otCoapHeaderGetCode(aHeader);
> if ((aResult == OT_ERROR_NONE) &&
> (code == OT_COAP_CODE_CREATED)) {
> iprintf("%s:%d History stored for point %d\r\n",
> __FILE__, __LINE__, schedule_point);
>
> /* Move to next point */
> schedule_state = SCHEDULE_STATE_WRITE;
> schedule_todo &= ~(1 << schedule_point);
> schedule_point++;
> iprintf("%s:%d Next point %d, todo %lx\r\n",
> __FILE__, __LINE__,
> schedule_point, schedule_todo);
> } else {
> iprintf("%s:%d FAILED POST HIST: "
> "Bad response (code=0x%02x err=%d).\r\n",
> __FILE__, __LINE__,
> code, aResult);
> schedule_state = SCHEDULE_STATE_INIT;
> schedule_todo = 0;
> schedule_point = 0;
> }
> } else {
> iprintf("%s:%d FAILED POST HIST: No response.\r\n",
> __FILE__, __LINE__);
> schedule_state = SCHEDULE_STATE_INIT;
> schedule_todo = 0;
> schedule_point = 0;
> }
> }
I'm pretty confident the problem is either in the CoAP client, or in my
calling of it that's causing the memory leak as increasing the polling
rate has a substantial impact on the length of time before this starts
occurring.