I am currently testing the sample gzll_ack_payload_device
and gzll_ack_payload_host
code for the Gazell protocol. I am building my project using the nRF Connect SDK extension in Visual Studio Code on a Windows machine.
I am using a custom board based on the nRF52840 SoC and modified the code slightly to accommodate a gpio button and LED since I am not using a dev kit. The transmitter board polls the status of the button, turns the LED on or off depending on the button, and sends a data packet via Gazell to the host board. The receiver (host) board changes the status of its LED depending on the packet it receives, and sends an ACK packet back to finish a successful transmission.
I would like to decrease latency by sending packets more quickly. Thus I wanted to change some default parameters on the transmitter board. I started with the timeslot parameter as specified in the docs.
First Problem
I attempted to change the default timeslot parameter using then rf_gzll_set_timeslot_period()
function to 600us. This function is called within the nrf_gzll_disabled()
callback function, but it always returns false no matter what parameter I give it.
Second Problem
I also saw in the API docs the function nrf_gzll_set_datarate()
and attempted to set the datarate to NRF_GZLL_DATARATE_2MBIT
to match with the 600us timeslot. However, that function also fails to set properly. Even within nrf_gzll_disabled()
Summary of Problems
I have attempted to change the following parameters and all of them have failed to apply while Gazell was disabled within nrf_gzll_disabled()
:
- Timeslot, using
nrf_gzll_get_timeslot_period()
- Data rate, using
nrf_gzll_get_datarate()
- Sync lifetime, using
nrf_gzll_get_sync_lifetime()
- Channel selection policy, using
nrf_gzll_set_device_channel_selection_policy()
- Timeslots per channel, using
nrf_gzll_set_timeslots_per_channel_when_device_out_of_sync()
Is there something wrong with the code? All of these parameters could not be set on the transmitter board. I tried setting timeslot and data rate on the receiver board as well, but it doesn't work either.
I followed the docs on Gazell protocol and API documentation. All of these functions were called when Gazell is disabled. According to API documentation, these functions will return false if Gazell was enabled or when passing an improper parameter. Neither of which should have happened,
The main file for the transmitter device is attached below, thanks for reading.
#include <zephyr/kernel.h> #include <nrf_gzll.h> #include <gzll_glue.h> #include <zephyr/drivers/gpio.h> #include <zephyr/logging/log.h> LOG_MODULE_REGISTER(app); /* Pipe 0 is used in this example. */ #define PIPE_NUMBER 0 /* 1-byte payload length is used when transmitting. */ #define TX_PAYLOAD_LENGTH 1 /* Maximum number of transmission attempts */ #define MAX_TX_ATTEMPTS 2 /* Gazell Link Layer TX result structure */ struct gzll_tx_result { bool success; uint32_t pipe; nrf_gzll_device_tx_info_t info; }; /* Payload to send to Host. */ static uint8_t data_payload[TX_PAYLOAD_LENGTH]; /* Placeholder for received ACK payloads from Host. */ static uint8_t ack_payload[TX_PAYLOAD_LENGTH]; #ifdef CONFIG_GZLL_TX_STATISTICS /* Struct containing transmission statistics. */ static nrf_gzll_tx_statistics_t statistics; static nrf_gzll_tx_statistics_t statistics_2; #endif /* Gazell Link Layer TX result message queue */ K_MSGQ_DEFINE(gzll_msgq, sizeof(struct gzll_tx_result), 1, sizeof(uint32_t)); /* Main loop semaphore */ static K_SEM_DEFINE(main_sem, 0, 1); static struct k_work gzll_work; static void gzll_tx_result_handler(struct gzll_tx_result *tx_result); static void gzll_work_handler(struct k_work *work); static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios); static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(DT_ALIAS(sw0), gpios, {0}); int main(void) { LOG_INF("Beginning of main func"); bool result_value; k_work_init(&gzll_work, gzll_work_handler); nrf_gzll_disable(); LOG_INF("Gazell is %s (main func)", nrf_gzll_is_enabled() ? "ENABLED" : "DISABLED"); /* Initialize Gazell Link Layer glue */ result_value = gzll_glue_init(); if (!result_value) { LOG_ERR("Cannot initialize GZLL glue code"); return 0; } /* Initialize the Gazell Link Layer */ result_value = nrf_gzll_init(NRF_GZLL_MODE_DEVICE); if (!result_value) { LOG_ERR("Cannot initialize GZLL"); return 0; } if (nrf_gzll_is_enabled()) LOG_INF("Gzll is enabled"); int ret; if (!gpio_is_ready_dt(&button)) { LOG_ERR("Error: button device %s is not ready\n", button.port->name); return 0; } ret = gpio_pin_configure_dt(&button, GPIO_INPUT); if (ret != 0) { LOG_ERR("Error %d: failed to configure %s pin %d\n", ret, button.port->name, button.pin); return 0; } if (!gpio_is_ready_dt(&led)) { return 0; } ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE); if (ret < 0) { return 0; } #ifdef CONFIG_GZLL_TX_STATISTICS /* Turn on transmission statistics gathering. */ result_value = nrf_gzll_tx_statistics_enable(&statistics); if (!result_value) { LOG_ERR("Cannot enable GZLL TX statistics"); return 0; } #endif data_payload[0] = 1; result_value = nrf_gzll_add_packet_to_tx_fifo(PIPE_NUMBER, data_payload, TX_PAYLOAD_LENGTH); if (!result_value) { LOG_ERR("Cannot add packet to GZLL TX FIFO"); return 0; } result_value = nrf_gzll_enable(); if (!result_value) { LOG_ERR("Cannot enable GZLL"); return 0; } LOG_INF("Gzll ack payload device example started."); uint64_t time = k_uptime_get(); while (true) { if (k_sem_take(&main_sem, K_FOREVER)) { continue; } #ifdef CONFIG_GZLL_TX_STATISTICS if (statistics.packets_num >= 1000) { uint32_t key = irq_lock(); uint64_t curr_time = k_uptime_get(); statistics_2 = statistics; /* Reset statistics buffers. */ nrf_gzll_reset_tx_statistics(); irq_unlock(key); /* Print all transmission statistics. */ LOG_INF(""); LOG_INF("Total transmitted packets: %4u", statistics_2.packets_num); LOG_INF("Total transmission time-outs: %03u", statistics_2.timeouts_num); LOG_INF("Total transmission time: %llu", (curr_time - time)); LOG_INF(""); for (uint8_t i = 0; i < nrf_gzll_get_channel_table_size(); i++) { LOG_INF( "Channel %u: %03u packets transmitted, %03u transmissions failed.", i, statistics_2.channel_packets[i], statistics_2.channel_timeouts[i]); } time = curr_time; } #endif } } static void gzll_device_report_tx(bool success, uint32_t pipe, nrf_gzll_device_tx_info_t *tx_info) { int err; struct gzll_tx_result tx_result; tx_result.success = success; tx_result.pipe = pipe; tx_result.info = *tx_info; err = k_msgq_put(&gzll_msgq, &tx_result, K_NO_WAIT); if (!err) { /* Get work handler to run */ k_work_submit(&gzll_work); } else { LOG_ERR("Cannot put TX result to message queue"); } } void nrf_gzll_device_tx_success(uint32_t pipe, nrf_gzll_device_tx_info_t tx_info) { gzll_device_report_tx(true, pipe, &tx_info); } void nrf_gzll_device_tx_failed(uint32_t pipe, nrf_gzll_device_tx_info_t tx_info) { gzll_device_report_tx(false, pipe, &tx_info); } void nrf_gzll_disabled(void) { LOG_INF("Gzll disabled"); LOG_INF("Current datarate: %d", nrf_gzll_get_datarate()); bool result_value = nrf_gzll_set_datarate(NRF_GZLL_DATARATE_2MBIT); if (!result_value) LOG_ERR("Cannot set datarate"); else LOG_INF("Set datarate: %d", nrf_gzll_get_datarate()); LOG_INF("Current timeslot period: %zu", nrf_gzll_get_timeslot_period()); result_value = nrf_gzll_set_timeslot_period(600); if (!result_value) LOG_ERR("Cannot set timeslot period"); else LOG_INF("Set timeslot period: %zu", nrf_gzll_get_timeslot_period()); LOG_INF("Current TX power: %d", nrf_gzll_get_tx_power()); result_value = nrf_gzll_set_tx_power(NRF_GZLL_TX_POWER_8_DBM); if (!result_value) LOG_ERR("Cannot set TX power"); else LOG_INF("Set TX power: %d", nrf_gzll_get_tx_power()); LOG_INF("Current device channel selection policy: %d", nrf_gzll_get_device_channel_selection_policy()); result_value = nrf_gzll_set_device_channel_selection_policy(NRF_GZLL_DEVICE_CHANNEL_SELECTION_POLICY_USE_CURRENT); if (!result_value) LOG_ERR("Cannot set device channel selection policy"); else LOG_INF("Set device channel selection policy: %d", nrf_gzll_get_device_channel_selection_policy()); LOG_INF("Current timeslots per channel when out of sync: %zu", nrf_gzll_get_timeslots_per_channel_when_device_out_of_sync()); result_value = nrf_gzll_set_timeslots_per_channel_when_device_out_of_sync(2); if (!result_value) LOG_ERR("Cannot set timeslots per channel when out of sync"); else LOG_INF("Set timeslots per channel when out of sync: %zu", nrf_gzll_get_timeslots_per_channel_when_device_out_of_sync()); LOG_INF("Current sync lifetime: %zu", nrf_gzll_get_sync_lifetime()); result_value = nrf_gzll_set_sync_lifetime(100); if (!result_value) LOG_ERR("Cannot set sync lifetime"); else LOG_INF("Set sync lifetime: %zu", nrf_gzll_get_sync_lifetime()); LOG_INF("Current max tx attempts: %zu", nrf_gzll_get_max_tx_attempts()); //nrf_gzll_set_max_tx_attempts(MAX_TX_ATTEMPTS); } void nrf_gzll_host_rx_data_ready(uint32_t pipe, nrf_gzll_host_rx_info_t rx_info) { } static void gzll_work_handler(struct k_work *work) { struct gzll_tx_result tx_result; /* Process message queue */ while (!k_msgq_get(&gzll_msgq, &tx_result, K_NO_WAIT)) { gzll_tx_result_handler(&tx_result); } /* Get main loop to run */ k_sem_give(&main_sem); } static void gzll_tx_result_handler(struct gzll_tx_result *tx_result) { bool result_value; uint32_t ack_payload_length = NRF_GZLL_CONST_MAX_PAYLOAD_LENGTH; if (tx_result->success) { if (tx_result->info.payload_received_in_ack) { /* Pop packet and write first byte of the payload to the GPIO port. */ result_value = nrf_gzll_fetch_packet_from_rx_fifo(tx_result->pipe, ack_payload, &ack_payload_length); if (!result_value) { LOG_ERR("RX fifo error"); } else if (ack_payload_length > 0) { ; } } } else { LOG_ERR("Gazell transmission failed"); } bool button_state = gpio_pin_get_dt(&button); data_payload[0] = button_state; result_value = nrf_gzll_add_packet_to_tx_fifo(PIPE_NUMBER, data_payload, TX_PAYLOAD_LENGTH); if (!result_value) LOG_ERR("TX FIFO error"); gpio_pin_set_dt(&led, button_state); }
Here is an example output from the serial console. Gazell transmission and GPIO functionality work fine.
*** Booting nRF Connect SDK v3.5.99-ncs1 *** I: Beginning of main func I: Gzll disabled I: Current datarate: 1 E: Cannot set datarate I: Current timeslot period: 0 E: Cannot set timeslot period I: Current TX power: 7 I: Set TX power: 0 I: Current device channel selection policy: 0 E: Cannot set device channel selection policy I: Current timeslots per channel when out of sync: 0 E: Cannot set timeslots per channel when out of sync I: Current sync lifetime: 0 E: Cannot set sync lifetime I: Current max tx attempts: 0 I: Gazell is ENABLED (main func) I: Gzll ack payload device example started. I: I: Total transmitted packets: 1000 I: Total transmission time-outs: 001 I: Total transmission time: 6000 I: I: Channel 0: 1000 packets transmitted, 001 transmissions failed. I: Channel 1: 000 packets transmitted, 000 transmissions failed. I: Channel 2: 000 packets transmitted, 000 transmissions failed. I: Channel 3: 000 packets transmitted, 000 transmissions failed. I: Channel 4: 000 packets transmitted, 000 transmissions failed.