Manually Setting Seqnum/IV_index for a BLE Mesh device

I'm developing a system using BLE Mesh with the intent of having a "collector" node attached to each network that manages provisioning and received published data from connected devices that is aggregated and forward to the cloud.

With this architecture, it is important the the "collector" remains online and available, so I'm using an acknowledged heartbeat message from the end device to allow it to determine if the collector is still there. If the collector fails to respond, the end device re-enters provisioning with the hope that it can be picked up by a neighboring network (networks are close enough that they overlap).

So far, everything works great, but when it "collector" comes back online I'd like to be able to tell the temporarily relocated end devices to return home. The collector is based on the serial example and is able to construct a data block containing the current network state (unicast addr, netkey, appkey, devkey, seqnum, iv_index, and iv_update), and I'm able to deliver this message to the relocated end device using the secondary network.

Issuing a "dsm_clear()" and setting all the dsm attributes seems to get me back to the home network, but while the sequence number appears to apply correctly while processing the message, it resets to 0 on "mesh_stack_device_reset()".

Simplified code (just the seqnum handling, and verified the problem exists in this example):

void set_seqnum() {
  uint32_t seqnum = 154
  uint32_t iv_index = 0
  uint32_t iv_update = 0

  ERROR_CHECK(net_state_iv_index_and_seqnum_block_set(iv_index, iv_update, seqnum));

  /* Reset to Apply */
  mesh_stack_device_reset();
}

Setting a breakpoint on the mesh_stack_device_reset() line, I can inspect m_net_state and m_status from the net_state.c file and am happy to find:

However, if I move the breakpoint to main.c immediately before entering the sd_app_evt_wait() loop, I find:

Sending a broadcast message asking all devices to clear their stored seqnum for the moving end device is an option, but if there is a way to set the seqnum so the device can come back fully synchronized that would be even more ideal and be making less of a security hole. In addition to 154, I also tried 8192 (block size) and 8193 (block size + 1) and had the same results (working on set, then resetting to 0 on reboot).

Thanks,
Jeremy

Parents
  • Hi,

    Regarding the call to mesh_stack_device_reset() in your code, this call should not be needed for implementing the change. The call to net_state_iv_index_and_seqnum_block_set() should be enough to set the new values, and given enough time (writing to flash happens asynchronously and take some time) the new block should end up in flash so that the seqnum continues from that higher address after the next reset.

    The code in net_state.c responsible for allocating the block and storing it to persistent memory is in the function seqnum_block_allocate(), which gets called at the end of net_state_iv_index_and_seqnum_block_set(). I suggest using breakpoints inside seqnum_block_allocate() to check the code flow is as expected and the line with the mesh_config_entry_set() actually getting called with the new seqnum_data.

    Regards,
    Terje

  • Any idea how I should poll that "net_state_is_seqnum_block_ready" function, I'm a bit new to the Nordic SDK? Putting it in a tight while loop it doesn't seem to ever commit, though breakpointing in the "persist_completed_notify" callback and commenting out my reset it does quickly commit if I'm not spinning a while loop.

      /* Wait for Seqnum to commit to flash */
      while (!net_state_is_seqnum_block_ready()) {
        /* Wait, but it never happens! */
      }

    Thanks!
    Jeremy

Reply
  • Any idea how I should poll that "net_state_is_seqnum_block_ready" function, I'm a bit new to the Nordic SDK? Putting it in a tight while loop it doesn't seem to ever commit, though breakpointing in the "persist_completed_notify" callback and commenting out my reset it does quickly commit if I'm not spinning a while loop.

      /* Wait for Seqnum to commit to flash */
      while (!net_state_is_seqnum_block_ready()) {
        /* Wait, but it never happens! */
      }

    Thanks!
    Jeremy

Children
  • Hi,

    Regarding spinlocking, optimization sometimes messes things up although function calls shouldn't be optimized away (unless the function is inline.)

    Could also be related to interrupt level, since the code responsible for the actual flash write needs to run.

    According to the mesh team, you should be able to register an event handler (with nrf_mesh_evt_handler_add) and wait for NRF_MESH_EVT_CONFIG_STABLE.

    This means you should not spinlock in the context from which you call net_state_iv_index_and_seqnum_block_set(), but rather return from that context (potentially after doing other stuff) and handle the final actions (including reset, or flagging for reset that is handled elsewhere) in the callback.

    Regards,
    Terje

  • Thanks again for your help. I eventually got it working polling after "sd_app_evt_wait()" in the main loop, but am going back to hook up an event handler. I'm new to the Nordic stack, and that's a really elegant solution!

  • Hi,

    That is great to hear!

    I guess if you do have a working solution then there is less of a reason to change it, even if improving what you have can also be a good idea.

    It looks like you have gotten your answers related to the issue. I will therefore consider the case closed for now. Feel free to close it completely (by marking an answer as verified answer) at any point you may wish.

    Regards,
    Terje

Related