Device freezes when jumping between main and Bluetooth write thread

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:

  1. Establish a Bluetooth connection so that data can be sent to that connection.
  2. 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, 
  3. 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. 
  4. 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! 

  • Just an updated comment to include additional observed behavior

    This error seems to have a higher chance to occur if I let the code run idly through main for a few minutes without sending a message.

    So if I a) tile device to send a message, b) return to main thread, c) wait for 5-10 minutes, d) tilt device to send a message, this has a very high chance to get stuck before returning to main

    Edit: I managed to replicate the issue while in Debug mode, and when the code got stuck, I clicked Pause, and ended up in cpu_idle.S, line 105, cpsie i



    	/*
    	 * Wait for all memory transactions to complete before entering low
    	 * power state.
    	 */
    	dsb
    
    	/* Enter low power state */
    	wfi
    
    	/*
    	 * Clear PRIMASK and flush instruction buffer to immediately service
    	 * the wake-up interrupt.
    	 */
    	cpsie	i
    	isb
    
    	bx	lr

  • Looking at the following you shared this thread is blocking if the if() statement is not true, so I added an else:

    /* 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");
    		}
    		else
    		{
    		    k_msleep(100); //nothing to do
    		}
    	}
    }

    Likely there are better ways to do this, but at least it avoid it to be blocking.

  • Hello Kenneth! Thanks for the quick reply, I've done as you advised and fixed the blocking behavior within that thread. I also discovered that I had areas in my main() that were blocking as well, which I corrected with the else sleep solution you recommended.

    Once I corrected those, the issue went away! Thanks for figuring it out!

Related