Monitoring nRF9160 data usage with connectivity statistics

Monitoring nRF9160 data usage with connectivity statistics

This blog posts explains how to use the nRF9160 connectivity statistics to monitor data usage.

One of the biggest cost factors when operating a cellular IoT product are data transfers. Not only are prices for IoT connectivity multiple magnitudes more expensive to what we are used from smartphone contracts, but transmitting data also requires a lot of energy. The longer the devices needs to transmit a payload the more likely it is also that the connection deteriorates (especially when the device is moving) and re-transmits need to happen. Therefore it is important to pay close attention to the amount of data your product is sending from the beginning. Having knowledge about the data usage profile of your application at hand also becomes important when picking the right connectivity partner.

While it is possible to infer a device's data consumption on the terminating endpoint, this information is not accurate, because it can observe successfully incoming messages. It can also become challenging to cover all endpoints, for example Firmware over the Air updates are typically downloaded via HTTPs from a web server and not through MQTT.

Therefore it is very valuable to use the Connectivity statistics the nRF9160 modem provides: it is a set of AT commands which configure the modem to start collecting statistics and to retrieve these statistics:

  1. use AT%XCONNSTAT=1 to tell the modem to start collecting connectivity statistics
  2. use AT%XCONNSTAT? to read the current connectivity statistics

In an application this can be implemented as follows:

1. After starting the application send the AT%XCONNSTAT=1 AT command once:

#include <modem/at_cmd.h>

int err = at_cmd_write("AT%XCONNSTAT=1", NULL, 0, NULL);
if (err != 0) {
	printk("Could not enable connection statistics, error: %d\n", err);

2. Implement a worker that regularly queries the connectivity statistics:

static struct k_delayed_work connstat_work;

static int query_modem(const char *cmd, char *buf, size_t buf_len) { ... }

static void connstat_work_fn(struct k_work *work)
	query_modem("AT%XCONNSTAT?", connStatBuffer, sizeof(connStatBuffer));
	// NOTE: k_uptime_get_32() cannot hold a system uptime time larger than approximately 50 days
	printk("Connection stats: %s | Uptime: %d seconds\n", connStatBuffer, k_uptime_get_32() / 1000);
	// Schedule next run
	k_delayed_work_submit(&connstat_work, K_SECONDS(60));

k_delayed_work_init(&connstat_work, connstat_work_fn);
k_delayed_work_submit(&connstat_work, K_SECONDS(60));

Note: You can see a full diff of how I added this to one of my applications here.

This is how the output will look like:

Connection stats: %XCONNSTAT: 0,0,14,16,748,134 | Uptime: 5041 seconds

Now you have access to the connectivity statistics, and can for example publish this to the cloud every hour so you can collect precise data consumption usage from your devices. Together with the uptime information collected on the device you will be able to develop a very good understand of what the typical data usage per day, week and month will be for your devices.

Caution: do not use the connectivity statistics in applications which use the LwM2M carrier library. This library manages the collection of connectivity statistics and will turn them on and off on its behalf. If your application interferes with this statistics collection it will result in incorrect measurements in the carrier's device management solution.