Invoking mgos_invoke_cb in quick succession - fails to pass correct data

#1
  1. My goal is: I’m trying to pass data received in a c callback into my js script
  2. My actions are:
    I’ve created a struct (mgos_esp_now_struct) which holds the data I want to pass:

struct mgos_esp_now_struct {
void (callback)(char, char*, void *);
char mac[13];
char response[30];
void *ud;
};

When the callback is fired, I populate the struct with my data:

void esp_now_recv_cb(const uint8_t *mac_addr, const uint8_t *data,
int data_len) {
sprintf(esp_now_struct.mac, “%.2x%.2x%.2x%.2x%.2x%.2x”, mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);

LOG(LL_INFO, (“RECV %s %.*s”, esp_now_struct.mac, data_len, data));

for (int i=0; i < data_len; ++i) {
esp_now_struct.response[i] = data[i];
}
esp_now_struct.response[data_len] = ‘\0’;
LOG(LL_INFO, (“Invoking esp_now_cb for %s”,esp_now_struct.mac));
mgos_invoke_cb(esp_now_cb, NULL, false);

}

void esp_now_cb(void *arg) {
if (esp_now_struct.callback != NULL) esp_now_struct.callback(esp_now_struct.mac, esp_now_struct.response, esp_now_struct.ud);
}

  1. The result I see is: The callback is called from several sources in quick succession, and although mgos_invoke_cb is invoked each time, the data it passes is only the most recent information.

  2. My expectation & question is: What do I need to ensure that mgos_invoke_cb correctly passes the relevant information to the js callback, rather than just the most recently received info?

I think it might be because I’m statically allocating the original struct at the top of my main.c, so it’s getting over-written each time the original callback is invoked before mgos_invoke_cb gets a chance. But, I’m not sure how to change it so that each time the callback is invoked a new version of the struct is initiated (and then freed in the mgos_invoke_cb.

Any help appreciated!

#2

You are right. The static structure data is overwritten on each invocation.
Allocate the structure in esp_now_recv_cb, pass it to esp_now_cb and free it there.

struct mgos_esp_now_struct {
  void (*callback)(char *, char *, void *);
  char mac[13];
  char response[30];
  void *ud;
};

void esp_now_cb(void *arg) {
  if (arg != NULL) {
    struct mgos_esp_now_struct *esp_now_struct =
        (struct mgos_esp_now_struct *) arg;
    if (esp_now_struct->callback != NULL)
      esp_now_struct->callback(esp_now_struct->mac, esp_now_struct->response,
                               esp_now_struct->ud);
    free(esp_now_struct);
  }
}

void esp_now_recv_cb(const uint8_t *mac_addr, const uint8_t *data,
                     int data_len) {
  struct mgos_esp_now_struct *esp_now_struct =
      (struct mgos_esp_now_struct *) calloc(1, sizeof(*esp_now_struct));
  sprintf(esp_now_struct->mac, "%.2x%.2x%.2x%.2x%.2x%.2x", mac_addr[0],
          mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);

  LOG(LL_INFO, ("RECV %s%.*s", esp_now_struct->mac, data_len, data));

  // for (int i = 0; i < data_len; ++i) {
  //  esp_now_struct.response[i] = data[i];
  //}
  memcpy(esp_now_struct->response, data, data_len);
  esp_now_struct->response[data_len] = '\0';
  LOG(LL_INFO, ("Invoking esp_now_cb for %s", esp_now_struct->mac));
  mgos_invoke_cb(esp_now_cb, esp_now_struct, false);
}
#3

That’s brilliant, many thanks @nliviu. The only thing that’s not now working is that the callback isn’t remembered as the struct is being dynamically allocated. I think I just need to create a seperate (static) struct for the original callback, and keep the Mac/response data separately dynamically allocated as per your example?

#4

I wanted to ask how do you fill the callback field, but I forgot…
Maybe you can keep the callback in a static variable and copy it in the dynamic structure?

#6

Sorry, just realised my error, ignore last post!

#7

That’s working great now, thanks @nliviu. Also increased my knowledge of dynamic allocation considerably in the process!