Hands on with DECT NR+ API release v2.0

Hands on with DECT NR+ API release v2.0

As the demand for reliable, low-latency, and power-efficient wireless communication grows in IoT and industrial automation, DECT NR+ is quickly emerging as a compelling solution.

While Nordic’s nRF Connect SDK provides the “Hello DECT” sample, implementing IPv6 over NR+, many developers are left wondering how the underlying DECT NR+ API actually works.

In this blog post, we will walk you through a hands-on demo application using two nRF9151 development kits and the latest NR+ firmware (v2.0.0), focusing on the new DLC/MAC API and showing the power saving options available.

Agents to accelerate embedded development. Whether you’re new to DECT NR+ or looking to dig deeper into its API, this guide aims to make the technology, and the development process, more accessible.

Prerequisites

If you want to duplicate this guide, or play with the code, you will need:

For how to program the DK’s, please see the DevZone blog: Getting started with NR+ PHY.

NR+ basics

NR+ is an ETSI radio standard for IoT, both for massive mesh networks and low latency communications. It is already seeing growing adaptation in audio use cases and in factory and process automation use cases all of which demand predictable latency and interference free radio channel. Massive networks can be implemented with mesh networking support. For more information on the technology please see the following resources

Nordic NR+ firmware

Nordic NR+ firmware v2.0.0 comes in two flavors, PHY and DLC/MAC access. Each variant is provided as a separate firmware file and comes with its own distinct API.

The NR+ PHY API offers low-level access, requiring developers to implement their own MAC layer, some aspects of which are necessary for technology certification to operate in the DECT radio band. This approach provides significant flexibility, but also requires more development effort. The DevZone blog post Getting started with NR+ PHY explains the low level PHY access in more detail.

With version 2.0.0, Nordic has introduced a new, higher-level DLC/MAC API, which simplifies development by providing a ready-made MAC protocol layer. The PHY API remains available and is largely unchanged from previous releases. This blog post focuses on the new DLC/MAC API and its power-saving features, which set Nordic’s implementation apart from other NR+ stacks currently available from partners.

Within the NR+ stack, the MAC layer is responsible for network advertisement, link establishment and device association. It serves as the central network management layer, in addition to handling access control. As of version v2.0.0, the Nordic NR+ firmware supports only a star network topology. The central device, referred to as FT (fixed terminal) in NR+ parlance, advertises the network availability with beacons. Child devices, PT devices (portable terminal) in NR+, find the FT beacons, associate to it and then receive information in those beacons when the FT device is listening for the child’s transmission uplink. In the Nordic implementation, the PT devices are not listening all the time for downlink messages from the FT  but instead receive a notification in beacons if a message is buffered for the PT device at the FT. The PT will then poll for the message. Uplink transmissions can be fast, if the FT is listening all the time, but downlink is slower, depending on the beacon advertisement period.

This is shown in the sequence graph below

 Sequence graph for uplink and downlink send

DLC layer implements segmentation and re-assembly for messages over the MAC link. There are different service levels, transparent sending, acknowledged sending and for the segmentation. In a star network the routing layer of DLC standard is not needed. If a convergence layer is needed, it can be implemented on top of the DECT API in Zephyr.

Architecture of the demo software

The demo software started small and simple to show the beacon period impact and power save operation. But there was a surprising amount of parameters and commands needed. It is a shell-controlled application that provides textual feedback on the shell terminal.

The main point for software development is that the DECT API callbacks are in Interrupt service context (ISR), requiring them to be fast non-blocking operations. To facilitate this the software architecture has a DECT Adapter that maps those callbacks to a message queue for application event handlers to process. The Adapter also hides the DECT specific enumerations from the application.

The software architecture of this demo is shown in the figure below

 Software architecture

The application handles shell commands, controls the operation and processes the resulting events. There are callbacks on operation completion and application events for data processing notifications from the adapter. The asynchronous operation completion is handled in the prepare_wait, wait_for_prepared_operation and complete_wait functions and semaphores.

The DECT Adapter’s main function is to map the DECT API callbacks into message queue events. This separation prevents dependency leakage and (hopefully) simplifies maintenance.

Device roles and operation

The FT device scans channels (SCAN command) and on user command starts FT beaconing and waiting for associations on a free channel (FT <channel> command). The other device is started in PT mode on the channel (PT <channel>). Currently only one FT and one PT is supported, although it should be simple to add support for more PT devices (association list, show list to direct send to individual device). The user can also scan if there are multiple FT devices and select the wanted FT device (first PT_SCAN, then PT  <channel>). FT and PT commands can be given with the channel number (carrier number in NR+ standards) for faster network start and finding and association. With very long beacon periods the advertisement and PT listening on different channels can take considerable time.

In connected mode the device can send messages (SEND <ASCII>) to the other device. There is an additional command STOP to return to initial state and some advanced commands to  highlight the operation parameters: PERIOD, POWERSAVE and ACTIVETIME. The period controls the beacon period of the FT device (stop, adjust period and restart, also on PT to adjust the listening period per channel if scanning more channels). POWERSAVE is demonstrated how it impacts the power consumption as measured with the Power Profiler Kit. LIMIT command can be used to try different values when a channel is deemed free or busy.

Main functions

The majority of the functionality is the radio link setup, as shown in the table below

(Recall that FT is the parent and PT is the child device)

Function

Description

nrf_modem_dect_mac_callback_set

Initialise. Register op and ntf callback structs

nrf_modem_dect_control_systemmode_set

Initialise. Set DECT system mode (MAC / none)

nrf_modem_dect_control_functional_mode_set

Initialise. Activate or deactivate the modem

nrf_modem_dect_control_configure

Initialise.  Set TX power, MCS, RSSI target, long RD ID, power save usage configuration

nrf_modem_dect_mac_association

PT Send association request to FT

nrf_modem_dect_mac_association_release

Release an existing association

nrf_modem_dect_mac_cluster_beacon_receive

PT Subscribe to FT cluster beacon
(learns random access schedule)

nrf_modem_dect_mac_cluster_beacon_receive_stop

PT Stop cluster beacon subscription

nrf_modem_dect_mac_cluster_configure

FT Configure cluster (channel, beacon period, RACH, association limits)

nrf_modem_dect_mac_network_beacon_configure

FT Configure and start optional network beacon

nrf_modem_dect_mac_network_scan

PT Scan for FT network and cluster beacons

nrf_modem_dect_mac_network_scan_stop

PT Stop ongoing network scan

nrf_modem_dect_mac_rssi_scan

FT Scan RSSI across channels (free channel selection)

nrf_modem_dect_mac_rssi_scan_stop

FT Stop ongoing RSSI scan

nrf_modem_dect_mac_cluster_info

Request current cluster info (channel load etc.)

nrf_modem_dect_mac_neighbor_info

Request info about a specific neighbor by long RD ID

nrf_modem_dect_mac_neighbor_list

Request list of all known neighbors

nrf_modem_dect_dlc_data_tx

Transmit DLC data to a neighbor

nrf_modem_dect_dlc_data_discard

Discard a pending DLC transmission by transaction ID

Notice there are only two functions for the data transmission: nrf_modem_dect_dlc_data_txand nrf_modem_dect_dlc_data_discard, a discard function to cancel a transmission.

The DLC implementation supports acknowledgements with segmentation and reassembly of messages over the radio link.  The rest of the functions establish the radio link. They do initializations, scans and association control. There are operation callbacks and notifications. Notifications are asynchronous modem-initiated callbacks passing data from different events to the higher controlling layer, shown in the table below:

control_functional_mode

Functional mode set operation callback

control_configure

Configuration operation callback

control_systemmode

Systemmode set operation callback

association

Association request callback

association_release

Association release request callback

cluster_beacon_receive

Cluster beacon reception request callback

cluster_beacon_receive_stop

Cluster beacon reception stop request callback

cluster_configure

Cluster configuration request callback

cluster_info

Cluster info request callback

neighbor_info

Neighbor info request callback

neighbor_list

Neighbor list request callback

dlc_data_tx

DLC data transmission send callback

dlc_data_discard

DLC data discard request callback

network_beacon_configure

Network beacon configuration request callback

network_scan

Network scan request callback

network_scan_stop

Network scan stop request callback

rssi_scan

RSSI scan request callback

rssi_scan_stop

RSSI scan stop request callback

association_ntf

FT receives an association request from a PT

association_release_ntf

Association released by peer or modem

cluster_ch_load_change_ntf

Cluster channel busy% crosses the configured threshold

neighbor_inactivity_ntf

A neighbor has been inactive longer than the configured timer

neighbor_paging_failure_ntf

Paging a neighbor failed

rssi_scan_ntf

Per-channel RSSI result during an RSSI scan (one per channel)

cluster_beacon_ntf

A cluster beacon is received over the air

cluster_beacon_rx_failure_ntf

Consecutive cluster beacon misses exceed configured threshold

ipv6_config_update_ntf

IPv6 address configuration updated

network_beacon_ntf

A network beacon is received over the air

capability_ntf

Modem reports its PHY capabilities (bands, max MCS)

dlc_data_rx_ntf

DLC data received from a neighbor

dlc_flow_control_ntf

DLC flow control status change

It is a bit scary looking (hence our approach with AI Agents) so let’s define the minimal flow for clarity.

The control flow in this demo for the FT device is shown in the figure below, showing the functions called and the state transition events. Association acceptance is automatic if there is memory left in the modem.

 FT flow

 The PT flow is shown in the figure below

 PT flow

There is an optional network scan state, listing found networks (and cluster beacons) on all channels. This was kept for later use of the code. The key thing is to start the beacon receive from the FT and then associate to the FT device. A beacon must be received for the modem to have the radio parameters for how to contact the FT device.

The image below shows a common failure mode. FT tried to send data to the PT, but the PT has dropped out of radio the network so the sending fails.

Note that the timeouts are long, NRF_MODEM_DECT_DLC_SDU_LIFETIME_60_S, those can be controlled in the DECT adapter’s custer_configure_ft and association_request functions with thedlc_sdu_lifetime parameter.


FT showing failed transmission

The FT device is sticky to stay in the beaconing state and accept associations. On the PT side there is fallback (a worker) that is kicked off if there are multiple DLC TX errors, association release or loss of beacon reception. It stops possible ongoing work, re-initializes and associates on the same channel – the assumption is that the FT stays there. This is rudimentary and very poorly tested too!

This re-subscribe is shown in the image below. The key is to check the callbacks status values, and for example the notification cluster_beacon_rx_failure_ntf which signals that the FT signal has not been seen. How soon the notification is fired can be configured in nrf_modem_dect_mac_association_params.


PT recovery from lost beacons

The image below shows retries as done by the modem implementation. There are short Listen-Before-Talk reception peaks, where the channel is being measured to check if it is free for the transmission to start. The higher peaks are the transmission attempts, with increasing power as no response is received. Each TX peak is followed by the ACK listening window. In the end transmission did succeed. If a transmission fails, a failed status is eventually returned in dlc_data_tx callback.

 Retries

Power behavior

This demo was developed with the goal of showing the power consumption impact of beacon period control and the power save parameters. Power save command controls the DECT API nrf_modem_dect_control_configure_params.power_save directly. The results of this are presented in this section.

Basic operation, no power save

The image below shows the power consumption of the PT device without power save for network scan and association – the high peak. Scan is active listening, at around 50mA power consumption level. Then the association is done, TX peak at 260mA followed by a short response reception window at 50mA. Since this is the first communication, the TX power is not adjusted. After the association period, reception peaks can be seen. These are the beacon receptions from the

FT.


Scan and association, no power save

Power save features in practice

The image below shows similar a association with power save enabled. There is the scan window at around 50mA (and before that the processing of the user command). Association TX peak is once again around 260 mA.


PT association with power save

But after this, the power usage drops significantly, the radio is shut down and power consumption is around 2.2mA as shown in the image below. The application does not implement any power save optimizations and so the application core is running, meaning further power savings are possible. The periodic spikes are once again the beacon reception windows when the radio is powered back on for reception.


Idle in power save mode

Once in the association state, the TX power can be adjusted. As shown in the image below, the TX peak power usage is controlled, and in this instance is around 70mA. The 50mA peak earlier is the beacon reception window. This TX power adjustment is done based on the nrf_modem_dect_control_configure_params params.expected_mcs1_rx_rssi_level parameter.

The power levels are tracked in association based on reported transmission power in the NR+ PHY header and on the measured reception power. This is then used to calculate the TX power needed to meet the communicated expected level for reception.


Adjusted send TX power in association

Power save mode on the FT side is also possible, as shown in the image below. The FT beacons are only part of the beacon period, as can be seen in the reception window. The rest of the period, the radio can be powered down. This is controlled with the command ACTIVETIME  and is placed in the cluster_config.rach_configuration.config.fill.percentageparameter in the DECT API. The modem implementation then adjusts the advertised random access window that is sent in cluster beacons and the PT devices wait for the next window if sending uplink. This of course impacts the latency for communication.

Note that the FT implementation can not power down the radio as deep as a PT device can; the idle power consumption is not as low as for PT devices.


FT power save

Learnings

AI learning

To me, this was a personal introduction to AI Agent programming. As I understand, AI is a chat sandbox, and an AI Agent can be given permissions to access folders and run commands on the user’s machine. I used Claude, but other AI Agents should work similarly. Based on some guidance from friends and the Internet, I started with this approach:

  1. Agent to make itself some new skills

  1. Agent to make a Plan (iterate)

  1. Agent to generate code from the plan

  1. Agent to add feature and test (iterate, update plan, update skills)

It was simple to instruct the AI Agent to create itself some re-usable skills. For example, a build skill using the right west environment and commands – and to flash the merged.hex after some confusion. Also a really useful skill is flashing devices, reset and verifying there is a terminal response. I used nrfutil for that. If there was no response, run nrfutil recover and reflash. “Make a flashing skill, there are 2 devices in USB port that can be programmed with nrfutil command”.

The AI could also run a test script, to give the command and look for the correct response from the devices. I believe these skills are largely AI Agent independent, seemed to be markdown documentation and bash and python scripts, but of course heavily dependent on the host computer and environment.

The planning phase seems essential. The plan is a markdown document, edited by me and the Agent. What commands to do, what HW is used, what nRF Connect SDK version and so on. An architecturally significant requirement is the ISR-context protection with the event queue. In planning, the AI also asks clarifications and builds better context for the task. The code generation is done in a second, but still the developer needs time to understand it. I fear to think what would have been the output without planning, and how many iterations and code reviews it would have taken. Asking about an issue can automatically generate fix change in code, and not always in the right place. I learned to ask for “analysis no fix”. Maybe it was test driven design I ended up using, adding operations and commands one at a time and checking the outcome after the initial plan implementation.

At times the Agent felt so human, noticing missing error code handling or dead code from an earlier change. To me it was surprising that the Agent might fix an issue but sometimes leave dead code and parameters into the source files. So human! It is good to ask the Agent to analyze the code for weaknesses and dead code every now and then.

I was surprised how easy it was to hit the usage limits. And the Agent can stop in the middle of a change being done -- this is not nice for a developer. I strongly recommend a paid subscription, but even then there are limits that are all too easy to hit as I learned. This was worsened by the skills usage, that makes the Agent process more messages and data than strictly needed as it also parsed the outputs. Of course, skills are bash scripts so running them by hand is an option, but it is so much easier to command “build, flash and test”.

The real learning for me was to improve the header documentation in the future. For example, if the function is for specific role, or if the function starts generating notifications or events and what is the actual flow of separate function calls. When we work with the technology we easily assume everybody knows this, but an AI Agent for sure does not know. But I got surprisingly good results after I made it analyze the sample application and libraries in the SDK.

Other learning

A mysterious USB dependency had me puzzled for a long time. Seems that a display that acts as a USB hub impacts the USB speed also of more directly connected devices. I was stuck when the software worked at home, but not at work. Then removing the external display at work, that acts as a USB hub too, brought the operation back to what I was seeing at home. The devices are in both cases connected with an external USB adapter. Speed configuration changes on the PC USB controller when the display is added? Of course, this also strongly hints at the weakness of my code.

Closing

In summary, this project aimed to not only demystify the DECT API, but also highlight the evolving role of AI Agents in embedded development. By combining hands-on experimentation with the nRF9151 DK and leveraging AI-driven planning and automation, both the technical implementation and the learning process was simplified.

I hope this walkthrough helps others get started with DECT NR+ and inspires confidence in using AI Agents as collaborative partners in future embedded projects.