Prepare JSON Connection Strings#

Using the values set by the custom callback to follow the join state, you can easily print connection/disconnection information using the 1-second timestamping, and compute the network availability ratio.

Note: This is all done in app.c.

Two messages are used:

Initial Connection Message#

{
"device":"8486",
"chip":  "xG25",
"parent":"333a",
"running":     "  0:22:21:17",
"join_states_sec":[0,0,9,1,12]
}

This message is printed in the device console and sent to the Border Router right after connecting.

  • "device" is the device_tag string, formatted from the MAC address of the device. This is a convenient way to identify devices using a shorter string than their entire MAC or IPv6 address. It has also the advantage of being visible in both the MAC and IPv6 addresses. This is fixed and set once only in app.c/app_task().

  • "chip" is the type of the part used, based on the following code (which can easily be extended to support more parts):

#ifdef  _SILICON_LABS_32B_SERIES_1
  #ifdef _SILICON_LABS_32B_SERIES_1_CONFIG_2
    #define CHIP "xG12"
  #endif
#endif

#ifdef  _SILICON_LABS_32B_SERIES_2
  #ifdef _SILICON_LABS_32B_SERIES_2_CONFIG_5
    #define CHIP "xG25"
  #endif
  #ifdef _SILICON_LABS_32B_SERIES_2_CONFIG_8
    #define CHIP "xG28"
  #endif
#endif

#ifndef CHIP
  #define CHIP SL_BOARD_NAME
#endif
  • "parent" is the parent_tag string, formatted from the MAC address of the parent as soon as connection is achieved. This can change over time, when the device selects a different parent, which is normal in a Wi-SUN network depending on the reception conditions. It is set inside the custom callback following join state changes.

  • The "device" and "parent" information allow building the network topology, to compare it to what wsbrd_cli status shows. The topology in this case is using information from the devices, not information retrieved using DBus from wsbrd. Both topology graphs should match.

  • "running" is the amount of time elapsed since the timestamp has been initialized in app.c/app_task(), calling app_timestamp_init(). In the example, it shows a 22+ hour run. Note that the very first value is a days count, required for long-term monitoring in a Wi-SUN network.

  • "join_state_sec" is an array of delays between join state changes, starting with join state 1. Five values are present, each corresponding to the time required to reach the corresponding join state. In the example, we can say that:

    • The device took 9 + 1 + 12 = 22 seconds to connect.

    • No time was spent going to join states 1 and 2, meaning that the device could reuse already existing credentials, thus saving time by skipping the authentication exchanges in steps 1 and 2. This is part of Wi-SUN stack optimizations for faster reconnection. If sl_wisun_clear_credential_cache() is called before connecting, authentication is required to retrieve credentials.

Status Message#

{
"device":"8486",
"chip":  "xG25",
"parent":"333a",
"running":     "  0:22:21:17",
"connected":   "  0:22:20:35",
"disconnected":"no",
"connections": "1",
"availability":"100.00",
"connected_total":   "  0:22:20:35",
"disconnected_total":"  0:00:00:00"
}

This message is printed in the console and sent to the Border Router every auto_send_sec seconds.

By default, auto_send_sec is set to 60 seconds. This can be customized in the source code. It is also possible with the example application to change it remotely using a dedicated CoAP message. Obviously, if the device is not connected, the message is only printed in the console.

  • "device", "chip", and "running" are exactly the same as in the initial connection message.

  • "connected" is the duration of the current connection. This is reset when reconnecting, such that it monitors only the last connection last. It is "no" when not connected.

  • "disconnected" is the duration since the last disconnection. It is "no" when connected.

  • "availability" is the ratio between the connected time and the total time since the first connection. It is computed as 100.0*(connected_total_sec)/(connected_total_sec + . disconnected_total_sec).

  • "connected_total" is the sum of the connected times since the very first connection, used to compute the availability ratio.

  • "disconnected_total" is the sum of the disconnected times since the very first connection, used to compute the availability ratio. When "disconnected_total" is " 0:00:00:00", the availability rate is 100%.

Note: To get a fair value of the availability ratio, disconnection and connection times accumulation only starts on the very first connection. Otherwise, the statistics would be impacted when the Border Router is started after the devices. With this configuration, the availability ratio should be very close to 100% under normal conditions, with devices quickly reconnecting following a disconnection.

If the Border Router is ever stopped and restarted, the devices will reconnect and send their status messages again, all showing a "disconnected" duration in the same range. This is a clear indication that the disconnection was due to the Border Router, and debugging should be oriented towards the Border Router.

When a single device has issues, its next status messages will show a "disconnected" value different from all other nodes. In this situation, debugging should be oriented at the device itself.

If a device is disconnected, printing the messages into its console (and RTT traces) still allows debugging up to a certain level, as long as access to the console or to RTT traces is possible (only in a lab environment in this case):

  • If the messages don't appear in the console, the application is probably stalled and debugging is required, starting with RTT traces (which keep a bit of history on the last traces).

  • If the messages still appear in the console, the application is still running. Further debugging may be possible looking at RTT traces for any error message coming from the Wi-SUN stack.

Code Walkthrough#

Three macros are used to format the first part of both messages and to add JSON start and end strings:

// JSON common format strings
#define START_JSON "{\n"

#define END_JSON   "}\n"

#define DEVICE_PARENT_RUNNING_JSON \
  "\"device\":\"%s\",\n"           \
  "\"chip\":  \"%s\",\n"           \
  "\"parent\":\"%s\",\n"           \
  "\"running\":     \"%s\",\n"

The initial connection message is formatted in _connection_json_string().

char* _connection_json_string () {
  #define CONNECTION_JSON_FORMAT_STR                   \
    START_JSON                                         \
    DEVICE_PARENT_RUNNING_JSON                         \
    "\"join_states_sec\":[%llu,%llu,%llu,%llu,%llu]\n" \
    END_JSON

  char sec_string[20];
  sprintf(sec_string, "%s", now_str());
  snprintf(json_string, SL_WISUN_COAP_RESOURCE_HND_SOCK_BUFF_SIZE,
     CONNECTION_JSON_FORMAT_STR,
     device_tag,
     CHIP,
     parent_tag,
     sec_string,
     app_join_state_delay_sec[1],
     app_join_state_delay_sec[2],
     app_join_state_delay_sec[3],
     app_join_state_delay_sec[4],
     app_join_state_delay_sec[5]
  );
  return json_string;
};

The status message is formatted in _status_json_string().

char* _status_json_string (char * start_text) {
  #define CONNECTED_JSON_FORMAT_STR        \
    "%s"                                   \
    START_JSON                             \
    DEVICE_PARENT_RUNNING_JSON             \
    "\"connected\":   \"%s\",\n"           \
    "\"disconnected\":\"%s\",\n"           \
    "\"connections\": \"%d\",\n"           \
    "\"availability\":\"%6.2f\",\n"        \
    "\"connected_total\":   \"%s\",\n"     \
    "\"disconnected_total\":\"%s\"\n"      \
    END_JSON

  char running_sec_string[20];
  char current_sec_string[20];
  char connected_sec_string[20];
  char disconnected_sec_string[20];

  uint64_t current_state_sec;

  sprintf(running_sec_string, "%s", now_str());

  if (join_state == SL_WISUN_JOIN_STATE_OPERATIONAL) {
    current_state_sec = now_sec() - connection_time_sec;
    sprintf(current_sec_string,     "%s", dhms(current_state_sec));
    sprintf(connected_sec_string,   "%s", dhms(connected_total_sec + current_state_sec));
    sprintf(disconnected_sec_string,"%s", dhms(disconnected_total_sec));
    snprintf(json_string, SL_WISUN_COAP_RESOURCE_HND_SOCK_BUFF_SIZE,
       CONNECTED_JSON_FORMAT_STR,
       start_text,
       device_tag,
       CHIP,
       parent_tag,
       running_sec_string,
       current_sec_string,
       "no",
       connection_count,
       100.0*(connected_total_sec + current_state_sec)/(connected_total_sec + current_state_sec + disconnected_total_sec),
       connected_sec_string,
       disconnected_sec_string
    );
  } else {
    current_state_sec = now_sec() - disconnection_time_sec;
    sprintf(current_sec_string,     "%s", dhms(current_state_sec));
    sprintf(connected_sec_string,   "%s", dhms(connected_total_sec));
    sprintf(disconnected_sec_string,"%s", dhms(disconnected_total_sec + current_state_sec));
    snprintf(json_string, SL_WISUN_COAP_RESOURCE_HND_SOCK_BUFF_SIZE,
       CONNECTED_JSON_FORMAT_STR,
       start_text,
       device_tag,
       CHIP,
       parent_tag,
       running_sec_string,
       "no",
       current_sec_string,
       connection_count,
       100.0*(connected_total_sec)/(connected_total_sec + disconnected_total_sec + current_state_sec),
       connected_sec_string,
       disconnected_sec_string
    );
  }

  return json_string;
}