Suspending a thread also suspends another randomly

Hello,

I've been working on a project using nRF52832 + NCS 2.1.1 including BLE and some sensors : MAX86150, MAX30205, MP34DT05, LIS3DH

I'm sending data of these sensors through BLE on demand i.e. requesting by sending a char code on a write characteristic for example to request PPG data (from MAX86150) I send 0x01 and for ECG (from MAX86150) I send 0x02. I have two separate threads one is the main thread (thread_main) which controls data requests, system and sensors shutdown, LED control and the other thread  (thread0):

void thread_main(void) {
	while(1) {
		// Check the input from client and manage the data send
		if (meas_value != 0xFF) {
			if (meas_value == 0x00) {
				printk("cancel any calculation\n");
				if (meas_value_current == 0x01) { 		// PPG + Force
					max86150_setPA_IR(&max86150_dev, 0);
					max86150_setPA_Red(&max86150_dev, 0);
					max86150_shutdown(&max86150_dev);
					suspend_thread0 = true;
				}else if (meas_value_current == 0x02) { // ECG
					max86150_shutdown(&max86150_dev);
					suspend_thread0 = true;
				}else if (meas_value_current == 0x03) { // PCG
					nrfx_pdm_stop();
				}else if (meas_value_current == 0x04) { // TEMP
					// max30205_shutdown(&max30205_dev);
					suspend_thread0 = true;
				}else if (meas_value_current == 0x05) { // Accel
					suspend_thread0 = true;
				}
				meas_value_current = 0xFF;
			}
			else if (meas_value == 0x01) {
				printk("cal PPG\n");
				MAX86150_config.slot[0] = MAX86150_SLOT_IR_LED1;
				MAX86150_config.slot[1] = MAX86150_SLOT_RED_LED2;
				MAX86150_config.slot[2] = 0;
				MAX86150_config.slot[3] = 0;
				MAX86150_config.IR_LED_PA = 0x1E;
				MAX86150_config.RED_LED_PA = 0x1E;
				max86150_init(&max86150_dev, &MAX86150_config, &MAX86150_data);
				// max86150_wakeup(&max86150_dev);
				k_timer_start(&thread0_timer, K_MSEC(1000), K_MSEC(0));
				meas_value_current = 0x01;
				wake_to_sleep_time = 0;
			}
			else if (meas_value == 0x02) {
				printk("cal ECG\n");
				MAX86150_config.slot[0] = MAX86150_SLOT_ECG;
				MAX86150_config.slot[1] = 0;
				MAX86150_config.slot[2] = 0;
				MAX86150_config.slot[3] = 0;
				MAX86150_config.ECG_sample_rate = ECG_SAMPLE_RATE_400;
				MAX86150_config.ECG_PGA_gain = ECG_PGA_GAIN_8;
				MAX86150_config.ECG_IA_gain = ECG_IA_GAIN_9_5;
				max86150_init(&max86150_dev, &MAX86150_config, &MAX86150_data);
				// max86150_wakeup(&max86150_dev);
				k_timer_start(&thread0_timer, K_MSEC(1000), K_MSEC(0));
				meas_value_current = 0x02;
				wake_to_sleep_time = 0;
			}
			else if (meas_value == 0x03) {
				printk("cal PCG\n");
				nrfx_pdm_start();
				meas_value_current = 0x03;
				wake_to_sleep_time = 0;
			}
			else if (meas_value == 0x04) {
				printk("cal TEMP\n");
				k_timer_start(&thread0_timer, K_MSEC(1000), K_MSEC(0));
				meas_value_current = 0x04;
				wake_to_sleep_time = 0;
			}
			else if (meas_value == 0x05) {
				printk("cal Accel\n");
				k_timer_start(&thread0_timer, K_MSEC(1000), K_MSEC(0));
				meas_value_current = 0x05;
				wake_to_sleep_time = 0;
			}
			else {
				printk("do nothing\n");
				meas_value_current = 0xFF;
			}
			meas_value = 0xFF;
		}

		// If disconnect condition is fulfilled, disconnect the BLE
		if (disconnect_ble) {
			printk("disconnecting ble\n");
			bt_conn_disconnect(my_conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
			disconnect_ble = false;
		}

		// Put the lis3dh device to suspend mode
		if (lis3dh_drv_off) {
			printk("turning off lis3dh\n");
			pm_device_action_run(lis3dh_dev, PM_DEVICE_ACTION_SUSPEND);
		}

		// If system off condition is fulfilled, put it to the sleep mode
		if (system_off) {
			// turning off all leds if somehow one is on
			led_off(led_ind_pwm, 0);
			max86150_shutdown(&max86150_dev);
			

			printk("turning off the system\n");
			pm_state_force(0u, &(struct pm_state_info){PM_STATE_SOFT_OFF, 0, 0});
			k_sleep(K_SECONDS(2U));	
		}

		// Stop the measurement thread (thread0) whenever needed
		if (suspend_thread0) {
			printk("suspending thread0\n");
			k_thread_suspend(thread0_id);
			suspend_thread0 = false;
		}

		// Measure accelerometer to detect any inactivity
		struct sensor_value accel[3];

		sensor_sample_fetch(lis3dh_dev);
		sensor_channel_get(lis3dh_dev, SENSOR_CHAN_ACCEL_XYZ, accel);

		float x_accel_new = sensor_value_to_double(&accel[0]);
		float y_accel_new = sensor_value_to_double(&accel[1]);
		float z_accel_new = sensor_value_to_double(&accel[2]);

		if (abs(x_accel - x_accel_new) > 0.5 |
			abs(y_accel - y_accel_new) > 0.5 |
			abs(z_accel - z_accel_new) > 0.5)
		{
			wake_to_sleep_time = 0;
		}else{
			wake_to_sleep_time++;
			if (wake_to_sleep_time >= WAKE_DURATION) {
				k_timer_start(&before_system_off, K_MSEC(0), K_MSEC(0));
				wake_to_sleep_time = 0;
			}
		}
		x_accel = x_accel_new;
		y_accel = y_accel_new;
		z_accel = z_accel_new;

		// LED Pulsing
		if (led_pulsing) {
			led_on(led_ind_pwm, 0);
			k_msleep(50);
			led_off(led_ind_pwm, 0);
			k_msleep(50);
			led_on(led_ind_pwm, 0);
			k_msleep(50);
			led_off(led_ind_pwm, 0);
			k_msleep(50);
		}

		// Debug LED
		// gpio_pin_toggle_dt(&led_pin);
		printk("main loop running...\n");
		k_msleep(1000);

	}
}

The other thread (thread0) manages the data acquisition and sending of sensors (except for the PDM mic which has it's own callback):

void thread0(void) {
	while(1) {

		if (meas_value_current == 0x01) { // PPG + Force
			max86150_fetch_data(&max86150_dev, &MAX86150_config, &MAX86150_data);

			uint16_t ir_data = (MAX86150_data.channel_data[0]) >> 2;
			uint16_t red_data = (MAX86150_data.channel_data[1]) >> 2;

			float filtered_force = 0, sum_force = 0;
			float new_force = read_Force();
			force_array[force_idx++] = new_force;
			force_idx %= FORCE_WINDOW_SIZE;

			for (int j=0; j<FORCE_WINDOW_SIZE; j++) {
				sum_force += force_array[j];
			}
			filtered_force = (float)sum_force / FORCE_WINDOW_SIZE;
			

			// filtered_force = new_force;

			epf_buf[epf_count++] = red_data;
			epf_buf[epf_count++] = red_data >> 8;
			epf_buf[epf_count++] = ir_data;
			epf_buf[epf_count++] = ir_data >> 8;
			epf_buf[epf_count++] = 0;
			epf_buf[epf_count++] = 0;
			epf_buf[epf_count++] = (char) filtered_force;
			epf_buf[epf_count++] = (filtered_force - epf_buf[epf_count-1]) * 100;;
	
			if (epf_count >= EPF_BUF_SIZE) {
				send_notify(epf_buf, sizeof(epf_buf));
				epf_count = 0;
			}
			k_msleep(5);
		}
		else if (meas_value_current == 0x02) {// ECG
			max86150_fetch_data(&max86150_dev, &MAX86150_config, &MAX86150_data);

			int16_t ecg_data = (int16_t)(MAX86150_data.channel_data[0] >> 2);
				
			epf_buf[epf_count++] = 0;
			epf_buf[epf_count++] = 0;
			epf_buf[epf_count++] = 0;
			epf_buf[epf_count++] = 0;
			epf_buf[epf_count++] = ecg_data;
			epf_buf[epf_count++] = ecg_data >> 8;
			epf_buf[epf_count++] = 0;
			epf_buf[epf_count++] = 0;
	
			if (epf_count >= EPF_BUF_SIZE) {
				send_notify(epf_buf, sizeof(epf_buf));
				epf_count = 0;
			}
			k_msleep(5);
		}
		else if (meas_value_current == 0x04) {// Temperature
			float temp;
			uint8_t buf[2];

			// max30205_oneshot_read(&max30205_dev, &temp);
			temp = 32.5;

			buf[0] = (char) temp;
			buf[1] = (temp - buf[0]) * 100;

			send_notify(buf, sizeof(buf));
			k_msleep(1000);
		}
		else if (meas_value_current == 0x05) {// Accelerometer
			struct sensor_value accel[3];
			uint8_t buf[6];

			sensor_sample_fetch(lis3dh_dev);
			sensor_channel_get(lis3dh_dev, SENSOR_CHAN_ACCEL_XYZ, accel);

			float x_accel = sensor_value_to_double(&accel[0]);
			float y_accel = sensor_value_to_double(&accel[1]);
			float z_accel = sensor_value_to_double(&accel[2]);
			
			buf[0] = (char) x_accel;
			buf[1] = (x_accel - buf[0]) * 100;
			buf[2] = (char) y_accel;
			buf[3] = (y_accel - buf[2]) * 100;
			buf[4] = (char) z_accel;
			buf[5] = (z_accel - buf[4]) * 100;

			send_notify(buf, sizeof(buf));
			k_msleep(1000);
		}
		else {
			k_msleep(1000);
		}
	}
}

The thread_main is always running but the thread0 is suspended most of the time and is resumed on demand (when data is requested). As mentioned above, resuming and suspending thread0 is done in the thread_main. The problem is suspending thread0 is also sometimes suspending the thread_main. This happens very randomly and I haven't found any pattern to it's occurrence. I don't know exactly whats going on here. If I remove the k_thread_suspend(thread0_id) from thread_main loop and leave thread0 running forever (by having a 1000ms delay in it when there is nothing to do) then it won't occur anymore.I've attached the whole project file.

Thanks in advance,
Hossein

edit : The project file has been removed to convert this topic to public

Parents
  • Hi Susheel,

    Thanks for your response. I'm sorry I send you the wrong project file I'll send the right one.

    Susheel Nuguru said:
    How do you know that suspending one thread is suspending main thread aswell? I mean how did you conclude that the main was suspended ?

    I'm checking the log on the RTT console, the printk("main loop running...\n") line is not show up anymore also changing the request code and shutting down the device doesn't work.

    Not sure if the main thread is suspended but I know that it won't run anymore maybe it is blocked somehow?

    Susheel Nuguru said:
    How are you waking up thread0? I do not see any related code to resume thread0 after it is suspended


    Waking up thread0 is done in a timer callback, now I see maybe that's the problem huh?
    Actually I turn on this timer in the main thread as you can see below and after 1 second the timer will resume the thread.

    void thread0_timer_cb(struct k_timer *timer) {
    	printk("thread0 timer cb\n");
    	k_thread_resume(thread0_id);
    }


    as an example when ppg is requested the timer will be turned on so thread0 can be resumed:

    else if (meas_value == 0x01) {
    	printk("cal PPG\n");
    	MAX86150_config.slot[0] = MAX86150_SLOT_IR_LED1;
    	MAX86150_config.slot[1] = MAX86150_SLOT_RED_LED2;
    	MAX86150_config.slot[2] = 0;
    	MAX86150_config.slot[3] = 0;
    	MAX86150_config.IR_LED_PA = 0x1E;
    	MAX86150_config.RED_LED_PA = 0x1E;
    	max86150_init(&max86150_dev, &MAX86150_config, &MAX86150_data);
    	// max86150_wakeup(&max86150_dev);
    	k_timer_start(&thread0_timer, K_MSEC(1000), K_MSEC(0));
    	meas_value_current = 0x01; 
    	wake_to_sleep_time = 0;
    }


    Susheel Nuguru said:
    Are you sure that thread0_id value is not corrupted over time when the issue happens?


    It might be the case but I don't know where, maybe in the timer callback?

    If you want to reproduce it's not that easy I may have to change the code cause I'm not using the nRF52 DK, we have a custom made board.

Reply
  • Hi Susheel,

    Thanks for your response. I'm sorry I send you the wrong project file I'll send the right one.

    Susheel Nuguru said:
    How do you know that suspending one thread is suspending main thread aswell? I mean how did you conclude that the main was suspended ?

    I'm checking the log on the RTT console, the printk("main loop running...\n") line is not show up anymore also changing the request code and shutting down the device doesn't work.

    Not sure if the main thread is suspended but I know that it won't run anymore maybe it is blocked somehow?

    Susheel Nuguru said:
    How are you waking up thread0? I do not see any related code to resume thread0 after it is suspended


    Waking up thread0 is done in a timer callback, now I see maybe that's the problem huh?
    Actually I turn on this timer in the main thread as you can see below and after 1 second the timer will resume the thread.

    void thread0_timer_cb(struct k_timer *timer) {
    	printk("thread0 timer cb\n");
    	k_thread_resume(thread0_id);
    }


    as an example when ppg is requested the timer will be turned on so thread0 can be resumed:

    else if (meas_value == 0x01) {
    	printk("cal PPG\n");
    	MAX86150_config.slot[0] = MAX86150_SLOT_IR_LED1;
    	MAX86150_config.slot[1] = MAX86150_SLOT_RED_LED2;
    	MAX86150_config.slot[2] = 0;
    	MAX86150_config.slot[3] = 0;
    	MAX86150_config.IR_LED_PA = 0x1E;
    	MAX86150_config.RED_LED_PA = 0x1E;
    	max86150_init(&max86150_dev, &MAX86150_config, &MAX86150_data);
    	// max86150_wakeup(&max86150_dev);
    	k_timer_start(&thread0_timer, K_MSEC(1000), K_MSEC(0));
    	meas_value_current = 0x01; 
    	wake_to_sleep_time = 0;
    }


    Susheel Nuguru said:
    Are you sure that thread0_id value is not corrupted over time when the issue happens?


    It might be the case but I don't know where, maybe in the timer callback?

    If you want to reproduce it's not that easy I may have to change the code cause I'm not using the nRF52 DK, we have a custom made board.

Children
No Data
Related