Hello! Apologies if my ticket subject is a little unclear or if the terminology isn't correct. I'm currently trying to investigate an issue that causes my device to get stuck/freeze at some point between main() and a Bluetooth thread that writes using bt_nus_send(). I've built my code mostly around the bluetooth peripheral_uart example in Zephyr.
My environment:
- NRF Connect SDK in VSCode - Version 2.1.1
- Zephyr v3.1.99
The objective of my code is to:
- Establish a Bluetooth connection so that data can be sent to that connection.
- Process accelerometer data in main() and if a condition is met (e.g. tilting a specific axis to align with the gravity vector), update a data buffer,
- When the accelerometer condition is met, allow CPU resources by using k_msleep or k_thread_join in main() to allow a ble_write_thread to use bt_nus_send() to push the data buffer through the device's TX Characteristic.
- Once the ble_write_thread has sent data, the code will return to main to await another trigger from the accelerometer.
The problem:
I've tried to trace down the issue by placing a bunch of print statements in key areas of my code, including within the processes within bt_nus_send. As a result, I believe there is an issue that happens either at the moment or shortly after the thread priority switches to/from the ble_write_thread and main. I've attached some relevant code that includes my print statements so that I can illustrate the issue a bit better. I'm not certain whether the problem lies with how I'm handling my threads, and whether there's an issue with the way I'm moving between threads, or an issue with the way I've set prioritization, or something else entirely.
/* Function for writing messages from peripheral to central (mobile device, or other) */ void ble_write_thread(void) { /* Don't go any further until BLE is initialized */ k_sem_take(&ble_init_ok, K_FOREVER); //int i = 0; for (;;) { if ((current_conn != 0) && (databuf != 0)) { bt_nus_send(NULL, databuf, 10); printk("\nMessage Sent!"); k_msleep(100); //this is where the thread priority is passed back to main() printk("Returned to ble_write_thread\n"); } } } //defining the thread K_THREAD_DEFINE(ble_write_thread_id, STACKSIZE, ble_write_thread, NULL, NULL, NULL, PRIORITY, 0, 0);
void main(void) { const struct device *cons = DEVICE_DT_GET(DT_CHOSEN(zephyr_console)); if (!device_is_ready(cons)) { printk("%s: device not ready.\n", cons->name); return; } //to store results from tilt detect function double tilt[] = {0, 0, 0}; double *tilt_ptr = punches; int last_tilt_cnt = 0; err = uart_init(); if (err) { error(); } if (IS_ENABLED(CONFIG_BT_NUS_SECURITY_ENABLED)) { err = bt_conn_auth_cb_register(&conn_auth_callbacks); if (err) { printk("Failed to register authorization callbacks.\n"); return; } err = bt_conn_auth_info_cb_register(&conn_auth_info_callbacks); if (err) { printk("Failed to register authorization info callbacks.\n"); return; } } err = bt_enable(NULL); if (err) { error(); } printk("Bluetooth initialized\n"); k_sem_give(&ble_init_ok); if (IS_ENABLED(CONFIG_SETTINGS)) { settings_load(); } err = bt_nus_init(&nus_cb); if (err) { LOG_ERR("Failed to initialize UART service (err: %d)", err); return; } err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd)); if (err) { LOG_ERR("Advertising failed to start (err %d)", err); return; } // Create structs for holding the accelerometer/gyro data static struct sensor_value accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z; double xaccel, yaccel, zaccel, xgyro, ygyro, zgyro; // Create struct for SPI accelerometer/gyro const struct device *spi_dev = get_spi_device(); if (spi_dev == NULL) { return; } /* Section for permanent loop in main */ for (;;) { timestamp_ms = k_uptime_get(); if ((sample == true) && (timestamp_ms - last_sample_timestamp_ms >= SAMPLE_TIME_MS)) { sample = false; } if (sample == false) { /* Use the Zephyr Sensor API enums to fetch data */ sensor_sample_fetch_chan(spi_dev, SENSOR_CHAN_ACCEL_XYZ); sensor_channel_get(spi_dev, SENSOR_CHAN_ACCEL_X, &accel_x); sensor_channel_get(spi_dev, SENSOR_CHAN_ACCEL_Y, &accel_y); sensor_channel_get(spi_dev, SENSOR_CHAN_ACCEL_Z, &accel_z); sensor_sample_fetch_chan(spi_dev, SENSOR_CHAN_GYRO_XYZ); sensor_channel_get(spi_dev, SENSOR_CHAN_GYRO_X, &gyro_x); sensor_channel_get(spi_dev, SENSOR_CHAN_GYRO_Y, &gyro_y); sensor_channel_get(spi_dev, SENSOR_CHAN_GYRO_Z, &gyro_z); } //function to detect tilt using processed accelerometer and gyroscope data tiltDetect(buffer_ptr, xaccel, yaccel, zaccel, xgyro, ygyro, zgyro, xaccel_sma); /* Noteworthy section here for Devzone ticket ****************************************************************************************************************************** */ if (tilt[0] - last_tilt_cnt == 1) { last_tilt_cnt = tilt[0]; sprintf(punchbuf_ptr, "%d", last_tilt_cnt); /* Allow CPU resources for lower priority ble thread to sent data */ //k_msleep(100); // alternatively, use k_thread_join k_thread_join(ble_write_thread_id, K_MSEC(100)); printk("returned to main thread now\n"); } last_sample_timestamp_ms = timestamp_ms; } }
Here's my prj.conf:
# Enable the UART driver CONFIG_UART_ASYNC_API=y CONFIG_NRFX_UARTE0=y CONFIG_SERIAL=y # Enable GPIO CONFIG_GPIO=y # Make sure printk is printing to the UART console CONFIG_CONSOLE=y CONFIG_UART_CONSOLE=y CONFIG_HEAP_MEM_POOL_SIZE=2048 CONFIG_BT=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_DEVICE_NAME="DEVICE_TEST" CONFIG_BT_DEVICE_APPEARANCE=833 CONFIG_BT_MAX_CONN=1 CONFIG_BT_MAX_PAIRED=1 # Enable the NUS service CONFIG_BT_NUS=y # Enable bonding CONFIG_BT_SETTINGS=y CONFIG_FLASH=y CONFIG_FLASH_PAGE_LAYOUT=y CONFIG_FLASH_MAP=y CONFIG_NVS=y CONFIG_SETTINGS=y # Enable DK LED and Buttons library CONFIG_DK_LIBRARY=y # This example requires more workqueue stack CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 # assert will cause soft resets, so set to N for now CONFIG_ASSERT=n CONFIG_BOARD_ENABLE_DCDC=n # Enable RTT to replace UART CONFIG_RTT_CONSOLE=y CONFIG_STDOUT_CONSOLE=y CONFIG_UART_CONSOLE=n CONFIG_USE_SEGGER_RTT=y CONFIG_SHELL_BACKEND_RTT=y # SYSTEM OFF PRJ.CONF CONFIG_PM=y # Required to disable default behavior of deep sleep on timeout CONFIG_PM_DEVICE=y # Optional select RAM retention (nRF52 only) #CONFIG_APP_RETENTION=y # ACCEL/GYRO PRJ.CONF CONFIG_I2C=y CONFIG_SPI=y CONFIG_SENSOR=y # Allow math.h CONFIG_NEWLIB_LIBC=y # Floating point support CONFIG_CBPRINTF_FP_SUPPORT=y # Sensor specific configurations CONFIG_LSM6DSL=y CONFIG_LSM6DSL_TRIGGER_NONE=y CONFIG_LSM6DSL_ACCEL_ODR=8 CONFIG_LSM6DSL_ACCEL_FS=16 CONFIG_LSM6DSL_GYRO_FS=1000 CONFIG_LSM6DSL_GYRO_ODR=8 CONFIG_LSM6DSL_ENABLE_TEMP=y # Config NFC pins as GPIO CONFIG_NFCT_PINS_AS_GPIOS=y
The RTT output of the issue would look something like below, before freezing. Note that the program gets stuck when it cannot return to the main thread. Sometimes this occurs after only 2 messages sent, and sometimes well over 50 messages can be sent before this occurs. At this point, the device isn't "frozen" perse, because I can disconnect the Bluetooth connection, and the RTT output will say "Disconnected". However, it is clear that the program has still not returned to main at this point - but if I wait for 5-10 of minutes, I'll see "returned to main thread" slowly trickle into the RTT console before everything resumes normally.
00> *** Booting Zephyr OS build v3.1.99-ncs1 *** 00> RESET REASON : 12 00> Bluetooth initialized 00> Found device "lsm6dsl@0", getting sensor data 00> Connected XX:XX:XX:XX:XX:XX (public) 00> Message Sent! 00> returned to main thread now 00> 00> returned to ble_write_thread 00> Message Sent! 00> returned to main thread now 00> 00> returned to ble_write_thread 00> Message Sent!
Any advice or insight into what might be causing this issue would be greatly appreciated!