How to Maximize Payload Rotation Rate for Discoverability on the NXP FRDM-MCXW71
Every Hubble beacon broadcasts an encrypted payload, and every so often it discards that payload and builds a new one. That cadence is the payload rotation rate, which is different from the beacon’s advertising interval. While the advertising interval sets how often the radio broadcasts the beacon’s message, the rotation rate sets how often it says something new. For example, Hubble’s Zephyr beacon sample rotates its payload every 6 minutes, producing 240 unique payloads a day against a protocol ceiling of 1,024. In this guide, you’ll learn how to maximize Hubble’s payload rotation rate on the NXP FRDM-MCXW71 and measure the change.
Why Payload Rotation Rate Matters
The payload rotation rate determines how often a Hubble beacon transmits new data. It does not impact how often a beacon broadcasts data in general. Thus, a beacon can have a different rotation rate while its radio still broadcasts every 100-150 ms.
This means that changing the payload rotation rate does not impact your Hubble beacon’s battery life, range, or advertising behavior. Instead, it impacts how your beacon looks to BLE scanners and how fresh your data is.
A higher payload rotation rate means:
- BLE scanners will update more frequently. These scanners routinely suppress repeated identical advertisements, meaning they often don’t log received data until the payload changes. No matter how fast your device broadcasts, a higher payload rotation rate will push BLE scanners to log more data from your Hubble device.
- Data is fresher. If your Hubble device is connected to a sensor that monitors real-time data like temperature or battery level, it will only broadcast new sensor data when the payload rotates. Therefore, a device with a higher payload rotation rate will broadcast fresher data than one with a lower rate.
The Maximum Rotation Rate
Every payload your Hubble beacon builds gets a sequence number, and only 1,024 numbers are available. This limit comes from the 10-bit Sequence Number field defined by Hubble’s Terrestrial advertising packet format.
The sequence numbers reset every time the EID (Ephemeral Identifier) rotates, which happens daily by default.
Therefore, the maximum payload rotation rate is:
86,400 seconds per day ÷ 1,024 sequence numbers per day = 84.375 seconds per payloadWe can then calculate how many unique payloads your beacon will create for every given rotation rate, and how much of the max payload capacity it will use:
| Rotation rate | Payloads per day | Payload capacity usage |
|---|---|---|
| 360 s (sample default) | 240 | 23% |
| 90 s (this tutorial) | 960 | 94% |
| 84.375 s (max) | 1,024 | 100% |
If you increase the payload rotation rate beyond the maximum limit, your beacon will run out of sequence numbers before the day ends. When it runs out, the Hubble firmware will fail, and your beacon will stop advertising.
That is why this tutorial will only instruct you to increase the rotation rate up to 90 s rather than 85 s. With a 90 s rotation rate, your beacon will still use 94% of the max payload capacity while leaving 64 spare sequence numbers for reboots and clock drift.
Getting Started
Before you begin, make sure you can get a Hubble beacon working on your NXP FRDM-MCXW71 board by following our previous tutorial.
In this tutorial, you will continue to work with the Hubble beacon sample application in your Zephyr workspace. Reference that previous tutorial if you need a refresher.
Modifying the Firmware
To increase your payload rotation rate, all you need to do is add one line to your prj.conf file:
CONFIG_HUBBLE_BEACON_SAMPLE_UPDATE_ADV_PERIOD=90Next, build and flash the firmware by running the following commands from your workspace root:
west build -p -b frdm_mcxw71/mcxw716c modules/lib/hubblenetwork-sdk/samples/zephyr/ble-beacon
west flashThat’s it! You’ve now successfully set your payload rotation rate to 90 s. To set a different rate, change the config value, rebuild, and reflash. As long as you stay within the max rate, your Hubble device will rotate its payload accordingly.
Measuring the Rotation Rate
Once you update your Hubble device’s payload rotation rate, you may find that nothing has changed. That’s because if you have a blank payload (as in the Zephyr sample application), or a static payload, you may never be able to observe when your device rotates its payload.
To observe your device’s new rotation rate, you need to add some data to the payload. The easiest things to add are a counter that increments once per rotation and an uptime value that records when the payload is built. The counter proves rotations happen, and the uptime measures the rotation period using the device’s internal clock.
Start by adding the following variables at file scope in your main.c:
// Counts one per payload. Declared static so it keeps its value between
// passes through the loop and starts at zero — including after a reboot,
// unlike the SDK's own sequence counter, which survives in NVS.
static uint16_t rotation_count;
// The six custom bytes carried in every payload:
// two for the counter, four for the uptime.
static uint8_t rotation_payload[6];Then, also in main.c, modify the hubble_ble_advertise_get() call in the main() function to add the appropriate counter and uptime values into the payload and log output. Your for (;;) loop should look like the following:
for (;;) {
// Device uptime in seconds since boot
uint32_t uptime_s = (uint32_t)(k_uptime_get() / 1000);
// Split both the counter and uptime values into single bytes, most significant first,
// so the packet layout doesn't depend on the CPU's byte order.
rotation_payload[0] = (uint8_t)(rotation_count >> 8); // counter, high byte
rotation_payload[1] = (uint8_t)(rotation_count & 0xFF); // counter, low byte
rotation_payload[2] = (uint8_t)(uptime_s >> 24); // uptime, highest byte
rotation_payload[3] = (uint8_t)(uptime_s >> 16);
rotation_payload[4] = (uint8_t)(uptime_s >> 8);
rotation_payload[5] = (uint8_t)(uptime_s & 0xFF); // uptime, lowest byte
// Log before incrementing, so the value printed is the one just packed.
LOG_INF("rotation=%u uptime=%us", rotation_count, uptime_s);
rotation_count++;
// out_len is in/out: set it to the room available, and the SDK replaces
// it with the number of bytes it actually wrote. Reset it every pass.
out_len = HUBBLE_USER_BUFFER_LEN;
// Encrypts the 6 bytes and wraps them in Hubble's 12-byte header.
err = hubble_ble_advertise_get(rotation_payload,
sizeof(rotation_payload),
_hubble_user_buffer, &out_len);
if (err != 0) {
LOG_ERR("Failed to get the advertisement data (err=%d)",
err);
goto end;
}
app_ad[1].data_len = out_len;
app_ad[1].type = BT_DATA_SVC_DATA16;
app_ad[1].data = _hubble_user_buffer;
LOG_DBG("Number of bytes in advertisement: %d", out_len);
err = bt_le_adv_start(
BT_LE_ADV_PARAM(BT_LE_ADV_OPT_USE_NRPA,
BT_GAP_ADV_FAST_INT_MIN_2,
BT_GAP_ADV_FAST_INT_MAX_2, NULL),
app_ad, ARRAY_SIZE(app_ad), NULL, 0);
if (err != 0) {
LOG_ERR("Bluetooth advertisement failed (err %d)", err);
goto end;
}
k_sem_take(&timer_sem, K_FOREVER);
err = bt_le_adv_stop();
if (err != 0) {
LOG_ERR("Bluetooth advertisement stop failed (err %d)",
err);
goto end;
}
}Rebuild and reflash with the same west build and west flash commands from before.
When you connect to your board via serial terminal, you should see measurements of your Hubble device’s rotation rate. Below is an example of what you should see before changing the rotation rate:

And here is what you should see after changing the rotation rate to 90 s:

Next Steps
Payload rotation rate and advertising interval are the two most important factors impacting how findable your Hubble device is. Increasing the rotation rate uses more flash writes for data resolution, while increasing the advertising interval uses more battery life for discoverability. When you maximize both, you ensure your Hubble beacon will broadcast the freshest data from the sensor(s) it is connected to. Register a production device at dash.hubble.com today to get started.
Ready to connect your devices anywhere on Earth? Get started with Hubble Network for free →