Radio Driver Recommendations

Hello,

I am in the process of writing a radio driver for a device I’m working with. The device is highly configurable and I am trying to identify the best strategy for exposing some of the configuration to the user. For example, the data rate, channel, preamble length, etc. can be changed to configure the radio. I understand that the netapi has some parameters that can be set through the interface, but I have quite a few parameters that don’t fit into the netapi framework that I’ll need to change. Here are the 3 proposed methods that I believe will expose the configuration to the user, I am wondering which you guys think is best.

  1. Put all configurable parameters in _params_t held in the device descriptor. I don’t like this option since the user basically needs to worry about all of them even if they want to just use them as default. Plus it’s more stored data that doesn’t necessarily need to be stored.
  2. Internally, have a bunch of default attribute((weak)) functions that can be overriden by different areas of the code. This way if someone wants a particular data rate tied to a board, the board can have the override. Or if the application wants to override it, it can.
  3. Simply setup the radio in what I believe to be the most useful default configuration. Then the application is responsible for configuring the radio further AFTER the radio is configured. The radio module would be responsible for providing hooks to configure different aspects. One thing I’m not sure of here is to know when the network stack has finished initializing the radio so it can begin it’s configuration.

Any suggestions or guidance on this matter is appreciated!

Sincerely, Anthony

Just another idea here:

  1. At the end of the netdev init method inside the radio driver, have a single _user_config function that has a weak default implementation internally (which does nothing). This way, the user can override that function and configure any additional settings after the device is in a known state.

Best, Anthony

Hi Anthony,

I am in the process of writing a radio driver for a device I'm working with. The device is highly configurable and I am trying to identify the best strategy for exposing some of the configuration to the user. For example, the data rate, channel, preamble length, etc. can be changed to configure the radio. 1. Put all configurable parameters in <radio>_params_t held in the device descriptor. I don't like this option since the user basically needs to worry about all of them even if they want to just use them as default. Plus it's more stored data that doesn't necessarily need to be stored.

Some parameters might make sense to have them runtime user configurable, e.g., the channel used. Probably for most of them there's a netapi option. If not, it might make sense to just add it.

IMHO, for everything that doesn't need to be changed much, e.g. general radio parameters like preable length, should end up in <radio>_params_t. The usual way here is to have the initialization function read that struct as const parameter, so it can be put to flash (but doesn't have to be).

If you'd like to seperate the device specific parameters (e.g., SPI, gpi-pins) from the radio configuration, you can nest the parameter structs:

typedef struct {   spi_t spi;   ...   const <radioname>_radio_params_t *radio_params; };

Then define a default set of radio_params_t with the most sensible default values.

Make your initialization function use that default if the radio_params field equals NULL.

That way, e.g. in the board config, it is possible to just do

const <radioname>_params_t whatever = {   .spi = SPI; };

Leaving out radio_params implicitly zeroes it.

This way you'll get sensible defaults, but a way to override them, even at runtime. In the default case, all parameters end up in flash.

Kaspar

Kaspar,

Thanks for this! Very clever to put a pointer to a second struct to keep the flexibility without forcing the user to know it.

I’m trying to understand a few things. In the _setup command, how should I copy the default value struct into the overall params if the radio_params pointer member is const. Should I use memcpy? Or should I create a new struct on the stack and initialize it with the correct pointer before copying it to the dev->params?

Also, I’m trying to understand how to achieve your comment: “In the default case, all parameters end up in flash.”

Thanks Again! -Anthony

Hi Anthony,

I'm trying to understand a few things. In the <radio>_setup command, how should I copy the default value struct into the overall params if the radio_params pointer member is const. Should I use memcpy? Or should I create a new struct on the stack and initialize it with the correct pointer before copying it to the dev->params?

Usually the setup() function gets a state struct and the params as parameters, e.g.,

device_setup(device_t *state_struct, const device_params_t *params);

Usually we double all fields that are needed after setup into the state struct, or put a pointer in the state struct pointing to params.

(On some MCU's there's a small speed penalty for reading values from flash. Depending on the device and how often a value is read, that's tolarable.)

Also, I'm trying to understand how to achieve your comment: "In the default case, all parameters end up in flash."

I meant that if the params struct and the "default advanced settings" struct are const, they'd end up in flash, although it is still possible to use runtime-generated versions (in RAM) and use them for initializing the device.

Kaspar