BluetoothSerial's .println() is altering hardware timer interrupt on other core

Kakukk777
Posts: 3
Joined: Sun Apr 25, 2021 11:46 am

BluetoothSerial's .println() is altering hardware timer interrupt on other core

Postby Kakukk777 » Sun Apr 25, 2021 12:33 pm

I have a code in the ESP32 Arduino which is supposed to do the following:
-create two tasks, each pinned to a core
-on core 0 I launch a hardware timer interrupt to signal to the task to toggle a pin every 100 us
-on core 1 I use the SerialBT.println() function from BluetoothSerial.h to send some gibberish on Bluetooth

I measured the pin with an oscilloscope, and it toggles totally irregularly, the frequency changes constantly. HOWEVER, when I delete the SerialBT.println() function, or it's not connected to the pc via bluetooth, or just use Serial.println() instead, it works well and toggles at a constant frequency. I tried many things and I really don't know why it affects something on the other core.

EDIT: After finding the run/stop button on the oscilloscope, I realised that it still mostly toggles at a regular 100us interval, but every after every few toggle it forgets to toggle for a few ms usually. The interval and the pulses between these glitches seem to be irrergular. So the problem remains, but I just added this info.

I also noticed the interruptCounter goes up like it's supposed to during these stops. So it's just the core 0 function somehow not responding to that. My theory is that while the Bluetooth is sending the packets, it needs both cores so the original core 0 task gets suspended. I don't know if this is the case, but of it is, is there anything I can do about it? Maybe change something in the BluetoothSerial library.

  1. #include "BluetoothSerial.h"
  2. #include "esp_task_wdt.h"
  3.  
  4. volatile int interruptCounter = 0;
  5. hw_timer_t * timer = NULL;
  6. portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
  7.  
  8. BluetoothSerial SerialBT;
  9.  
  10. //interrupt routine
  11. void IRAM_ATTR onTimer() {
  12.   portENTER_CRITICAL_ISR(&timerMux);
  13.   interruptCounter++;
  14.   portEXIT_CRITICAL_ISR(&timerMux);
  15.  }
  16.  
  17. void setup() {
  18.  
  19.   Serial.begin(2000000);
  20.  
  21.   SerialBT.begin("ESP32"); //Bluetooth device name
  22.   Serial.println("The device started, now you can pair it with bluetooth!");
  23.  
  24.   pinMode(26, OUTPUT);
  25.  
  26.   disableCore0WDT();
  27.   disableCore1WDT();
  28.  
  29.   xTaskCreatePinnedToCore(
  30.     adc_read,   /* Function to implement the task */
  31.     "adc_read_task", /* Name of the task */
  32.     1024,       /* Stack size in words */
  33.     NULL,       /* Task input parameter */
  34.     1,          /* Priority of the task */
  35.     NULL,       /* Task handle. */
  36.     0);  /* Core where the task should run */
  37.  
  38.   xTaskCreatePinnedToCore(
  39.     bl_send,   /* Function to implement the task */
  40.     "bl_send_task", /* Name of the task */
  41.     1024,       /* Stack size in words */
  42.     NULL,       /* Task input parameter */
  43.     2,          /* Priority of the task */
  44.     NULL,       /* Task handle. */
  45.     1);  /* Core where the task should run */
  46. }
  47.  
  48. void loop() {vTaskDelete(NULL);}
  49.  
  50. static void  adc_read (void *pvParameters )
  51. {
  52.  
  53.   //launch the timer interrupt on core 0
  54.   timer = timerBegin(0, 80, true);
  55.   timerAttachInterrupt(timer, &onTimer, true);
  56.   timerAlarmWrite(timer, 100, true);
  57.   timerAlarmEnable(timer);
  58.  
  59.   for(;;) {
  60.  
  61.     if (interruptCounter > 0) {    
  62.       portENTER_CRITICAL(&timerMux);
  63.       interruptCounter=0;
  64.       portEXIT_CRITICAL(&timerMux);
  65.              
  66.       digitalWrite(26, !digitalRead(26));
  67.     }  
  68.   }
  69. }
  70.  
  71. static void bl_send (void *pvParameters )
  72. {
  73.     for( ;; ) {
  74.       SerialBT.println("1");
  75.     }
  76. }
Last edited by Kakukk777 on Mon Apr 26, 2021 5:07 am, edited 1 time in total.

ESP_Sprite
Posts: 9730
Joined: Thu Nov 26, 2015 4:08 am

Re: BluetoothSerial's .println() is altering hardware timer interrupt on other core

Postby ESP_Sprite » Mon Apr 26, 2021 4:22 am

The BT task and interrupts run at core 0 by default, if memory serves. This happens even if you print to BT on the other core. Presumably the BT interrupts pre-empt your timer interrupt.

Kakukk777
Posts: 3
Joined: Sun Apr 25, 2021 11:46 am

Re: BluetoothSerial's .println() is altering hardware timer interrupt on other core

Postby Kakukk777 » Mon Apr 26, 2021 5:07 am

ESP_Sprite wrote:
Mon Apr 26, 2021 4:22 am
The BT task and interrupts run at core 0 by default, if memory serves. This happens even if you print to BT on the other core. Presumably the BT interrupts pre-empt your timer interrupt.
But, I checked within the interrupt which core it runs on via xPortGetCoreID(), and it runs on the core in which I set it up. The Bluetooth runs much slower on the other core (1kB/s vs 16 kB/s) and I need the higher speed. But even if I put Bluetooth on the other core core the problem still persists, it's just less frequent because of the less frequent sending. I edited the post to add a few things.

Kakukk777
Posts: 3
Joined: Sun Apr 25, 2021 11:46 am

Re: BluetoothSerial's .println() is altering hardware timer interrupt on other core

Postby Kakukk777 » Mon Apr 26, 2021 5:13 am

Is my post gone?

ESP_Sprite
Posts: 9730
Joined: Thu Nov 26, 2015 4:08 am

Re: BluetoothSerial's .println() is altering hardware timer interrupt on other core

Postby ESP_Sprite » Mon Apr 26, 2021 6:19 am

I don't think Arduino allows you to change the core the main BT stack runs on. Where you call SerialBT.begin() or SerialBT.println() does not factor into that equation. I'm pretty sure it always runs on core 0.
Kakukk777 wrote:
Mon Apr 26, 2021 5:13 am
Is my post gone?
No, accounts with <3 posts need to get their post approved before they appear. You should see your posts immediately from now on as you have that amount of posts.

User avatar
Vader_Mester
Posts: 300
Joined: Tue Dec 05, 2017 8:28 pm
Location: Hungary
Contact:

Re: BluetoothSerial's .println() is altering hardware timer interrupt on other core

Postby Vader_Mester » Mon Apr 26, 2021 1:33 pm

Kakukk777 wrote:
Mon Apr 26, 2021 5:07 am
ESP_Sprite wrote:
Mon Apr 26, 2021 4:22 am
The BT task and interrupts run at core 0 by default, if memory serves. This happens even if you print to BT on the other core. Presumably the BT interrupts pre-empt your timer interrupt.
But, I checked within the interrupt which core it runs on via xPortGetCoreID(), and it runs on the core in which I set it up. The Bluetooth runs much slower on the other core (1kB/s vs 16 kB/s) and I need the higher speed. But even if I put Bluetooth on the other core core the problem still persists, it's just less frequent because of the less frequent sending. I edited the post to add a few things.
You should detach BT printing from Core1. Instead of calling SerialBT.println() directly, you can send the meassages to a queue (if memory is enough), then set up a task on Core0, to periodically check if there is a message on the Queue and send it. This way you have control over how data is sent via BT, and control over how often it is called.

You can do it by simply by defining a pointer, and malloc() a piece of memory, in which you store the message. Then you only pass the pointer to the queue, and send it, and make sure to free() it afterwards. By using snprinf(NULL, NULL, ....) it will not write anything anywhere, just returns the size of the meassage you want to send, which you can use to make the malloc() call, so you can take the exact memory size you need, not more, not less :)), and then call snprintf again with the memory pointer and size, so it will actually write the message into it - just be careful to certainly include NULL termination to the end of the buffer.

The task is a simple xQueueReceive(), with portMAX_DELAY in a constant while() loop. You can add a vTaskDelay() inside the loop to controll how quickly should it check for the next message in the queue. I suggest you add at least 1tick wait time, to give enough time for the scheduler to switch, if needed, but you can ommit this, if you want to send what's on the queue right away. xQueueReceive will keep waiting for an item on the queue forever, but it will not block program flow, as it will just nicely put the task to a blocked state alowing others to run.

something like

Code: Select all

btTask(void *pvParams)
{
	while(1)
	{
		char *msgToSend;
		if(xQueueReceive(xBtQ, &msgToSend, portMAX_DELAY == pdTRUE)
		{
			serialBT.println(msgToSend);
		}
		vTaskDelay(1); //1 tick not 1ms
	}
	vTaskDelete(NULL);	//not called ever
}

Code: Select all

task_t coffeeTask()
{
	while(atWork){
		if(!xStreamBufferIsEmpty(mug)){
			coffeeDrink(mug);
		} else {
			xTaskCreate(sBrew, "brew", 9000, &mug, 1, NULL);
			xSemaphoreTake(sCoffeeRdy, portMAX_DELAY);
		}
	}
	vTaskDelete(NULL);
}

Who is online

Users browsing this forum: Google [Bot] and 79 guests