[FreeRTOS] Publish task makes other task crash

Arkaik
Posts: 13
Joined: Mon Jun 12, 2017 12:36 pm

[FreeRTOS] Publish task makes other task crash

Postby Arkaik » Wed Jun 14, 2017 9:57 am

Hi guys,

I'm currently trying to acquire data from an inertial measurement unit and to publish those data through mqtt.
To do this, I have one periodic task launched on sensor interrupt (at 119Hz) which acquire data from the sensor and send it into a queue and a publish task which gets data from the queue and send it through mqtt.

The acquire task (T1) is high priority and the second task (T2) is lower priority.

The acquisition works well as the publication. My problem is that when I integrate both tasks, the publishing tasks makes the sensor to crash.

I explain my logic :
When the program starts, it initialise all the ressources (mongoose mqtt, gpio, queue, etc...) then it define an ISR on a gpio to launch the acquisition task each time it receives a rising edge.
At the end of the first execution of T1, T2 is started, the only thing this task do is getting data from queue, encode it and send it through mqtt inside an infinite loop.

The program works for few periods and then crash. What is strange is that when it crashes, I have no more interruption on my gpio so my acquisition task is not called again.

If I don't launch the publishing task at the end of the first T1 execution, the program runs indefinitely without crashing (except when the queue is full but that's normal). So I think that the publish task is the problem but I don't see why and how it could stop my sensor sending me interrupts.
I have checked with an oscilloscope and indeed, my sensor doesn't send any interrupt.

I'm programming on ESP32DevKitC and my sensor is a LSM9DS1 embedded on mikroe 9DoF click breackout board.

I give you my code :

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "driver/i2c.h"
#include "driver/gpio.h"
#include "mongoose.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "lwip/inet.h"
#include "lwip/ip4_addr.h"
#include "esp_event.h"
#include "mpu.h"
#include "LSM9DS1_Reg.h"
#include "base64.h"

// Defines for WiFi
#define SSID     "<SSID>"
#define PASSWORD "<PASSWORD>"

//Define for publish event
#define PUBLISH_BIT 0x01

//Queue message struct
struct sampleMessage
{
	int messageId;
	float gyrData[3];
	float accData[3];
};

// Task functions
void acqTask();
void publishTask();

//Networking functions
esp_err_t wifi_event_cb(void *ctx, system_event_t *event);
void mongooseTask(void *data);
void mongoose_event_cb(struct mg_connection *nc, int ev, void *evData);
char *mongoose_eventToString(int ev);
char *mgStrToStr(struct mg_str mgStr);

//Encoding functions
void encodeMessage(struct sampleMessage msg, char * buffer);

//Broker informations
const char *s_topic = "/test/slot1";
const char * s_serverAddr = "IP:PORT";
struct mg_mqtt_topic_expression s_topic_expr = {"/test/slot1", 0};
struct mg_connection * mqttConnection = NULL;

//Wifi network informations
ip4_addr_t net_ip;
ip4_addr_t net_gw;
ip4_addr_t net_msk;
bool b_isConnected = false;

//I2C Bus
i2c_port_t i2cBus = I2C_NUM_0;

//GPIO pins
int acqPin = GPIO_NUM_4;

//Task handlers
static TaskHandle_t acqTask_handler = NULL;
static TaskHandle_t publishTask_handler = NULL;

//Sample Queue
QueueHandle_t sampleQueue_handler = NULL;

//Sensors scale values
float accScale, gyrScale, magScale;

void gpio_isr_acq_handler(void * arg)
{
	xTaskCreate(&acqTask, "acqTask", 2048, NULL, configMAX_PRIORITIES-1, &acqTask_handler);
}

void app_main(void)
{
	ESP_ERROR_CHECK(esp_event_loop_init(wifi_event_cb, NULL));
	
	/////////////////////// Wifi connection //////////////////////
	nvs_flash_init();
	tcpip_adapter_init();	
	
	wifi_init_config_t wifiConfig = WIFI_INIT_CONFIG_DEFAULT();
	
	ESP_ERROR_CHECK(esp_wifi_init(&wifiConfig));
	ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
	ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
	
	wifi_config_t sta_config = {
		.sta = 
		{
			.ssid = SSID,
			.password = PASSWORD,
			.bssid_set = 0
		}
	};
	ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &sta_config));
	ESP_ERROR_CHECK(esp_wifi_start());
	ESP_ERROR_CHECK(esp_wifi_connect());
	
	while(!b_isConnected)
	{}
	
	// GPIO config
	gpio_config_t io_conf;
	
	//Acq pin
	io_conf.intr_type = GPIO_PIN_INTR_POSEDGE;
	io_conf.pin_bit_mask = (1 << acqPin);
	io_conf.mode = GPIO_MODE_INPUT;
	io_conf.pull_up_en = 0;
	io_conf.pull_down_en = 1;
	
	gpio_config(&io_conf);
	
	// I2C config
	i2c_master_init(i2cBus, 18, 19);
	
	accSetDataRate(i2cBus, A_ODR_119_HZ);
	accScale = accSetScale(i2cBus, A_SCALE_2G);
	accEnableOutputs(i2cBus);
	
	gyrSetDataRate(i2cBus, G_ODR_119_HZ);
	gyrScale = gyrSetScale(i2cBus, G_SCALE_245DPS);
	gyrEnableOutputs(i2cBus);
	
	enableMultipleByteAccess(i2cBus);
	
	setFifoThreshold(i2cBus, 2);
	enableFifoThreshold(i2cBus);
	setFifoMode(i2cBus, FIFO_MODE_CONTINUOUS);
	enableFifo(i2cBus);
	
	setInt1Source(i2cBus, INT1_SRC_FTH);
	setInt2Source(i2cBus, INT2_SRC_OVR);
	
	//Create communication queue
	sampleQueue_handler = xQueueCreate(100, sizeof(struct sampleMessage));

	//ISR callbacks
	gpio_install_isr_service(0);
	gpio_isr_handler_add(acqPin, gpio_isr_acq_handler, NULL);
	
	clearFifo(i2cBus);
}

void acqTask()
{
	float acc_xyz[3];
	float gyr_xyz[3];
	static int sampleCount = 0;

	readGyr(i2cBus, gyrScale, &gyr_xyz[0]);
	readAcc(i2cBus, accScale, &acc_xyz[0]);

	struct sampleMessage msg;
	
	msg.messageId = sampleCount;
	memcpy(&msg.gyrData, gyr_xyz, sizeof(gyr_xyz));
	memcpy(&msg.accData, acc_xyz, sizeof(acc_xyz));
	
	if(sampleQueue_handler != 0)
	{
		// Send an unsigned long.  Wait for 10 ticks for space to become available if necessary.
		if(xQueueSend(sampleQueue_handler, (void *) &msg, ( TickType_t ) 10) != pdPASS)
		{
			printf("[Error] Failed to send message to queue\n");
		}
		else
		{
			sampleCount += 1;
		}
	}

	if(publishTask_handler == NULL)
	{
		xTaskCreate(&publishTask, "publishTask", 10000, NULL, configMAX_PRIORITIES-2, &publishTask_handler);
	}
	
	vTaskDelete(NULL);
}

void publishTask()
{
	char encodingBuffer[500];
	char b64_message[2400];
	
	struct sampleMessage receivedMessage;

	while(true)
	{	
		if(sampleQueue_handler != 0)
		{
			// Receive a message on the created queue. Block indefinitely until a message is available.
			if(xQueueReceive(sampleQueue_handler, &receivedMessage, ( TickType_t ) 10))
			{
				//Format and encode message to base64
				encodeMessage(receivedMessage, encodingBuffer);
				
				mg_mqtt_publish(mqttConnection, s_topic, 50, 0, encodingBuffer, strlen(encodingBuffer));
			}
			else
			{
				printf("[Error] Failed to receive message from queue\n\n");
			}
		}
	}
	vTaskDelete(NULL);
}

esp_err_t wifi_event_cb(void *ctx, system_event_t *event)
{
	if(event->event_id == SYSTEM_EVENT_STA_GOT_IP)
	{
		net_ip = event->event_info.got_ip.ip_info.ip;
		net_gw = event->event_info.got_ip.ip_info.gw;
		net_msk = event->event_info.got_ip.ip_info.netmask;
		b_isConnected = true;
		
		printf("Connected to %s\n", SSID);
		printf("\tIP: %s\n", inet_ntoa(net_ip));
		printf("\tNetmask: %s\n", inet_ntoa(net_msk));
		printf("\tGateway: %s\n\n", inet_ntoa(net_gw));
		
		xTaskCreatePinnedToCore(&mongooseTask, "mongooseTask", 20000, NULL, 5, NULL,0);
	}
	return ESP_OK;
}

// FreeRTOS task to start Mongoose.
void mongooseTask(void *data)
{
	printf("Mongoose task starting\n");
	struct mg_mgr mgr;
	printf("Mongoose: Starting setup\n");
	mg_mgr_init(&mgr, NULL);
	printf("Mongoose: Succesfully inited\n");
	printf("Mongoose: Starting connection to %s\n", s_serverAddr);
	mqttConnection = mg_connect(&mgr, s_serverAddr, mongoose_event_cb);
	
	if (mqttConnection == NULL)
	{
		printf("Mongoose: Impossible to connect to %s\n", s_serverAddr);
		vTaskDelete(NULL);
		return;
	}
	printf("Mongoose: Successfully connected\n");
	
	mg_set_protocol_mqtt(mqttConnection);
	mg_send_mqtt_handshake(mqttConnection, "dummy");
	
	while(true)
	{
		mg_mgr_poll(&mgr, 1000);
	}
}

void mongoose_event_cb(struct mg_connection *nc, int ev, void *evData)
{}

This is a really strange behaviour and I really don't know how this is even possible, I could understand if the program crashed and was not able to interpret the interrupt but here, there is no more interrupt...

If someone already has such a problem I would be glad to have some advices ;)
Thanks in advance.

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

Re: [FreeRTOS] Publish task makes other task crash

Postby ESP_Sprite » Wed Jun 14, 2017 2:26 pm

Very simple: Any FreeRTOS function that does not end in FromISR can not be called from an ISR, or else Weird Stuff (like what you experienced) happens. Instead, create the task when you init the ESP32 and let it wait on a semaphore or something. In the interrupt, give that semaphore (using a *FromISR function) and let the task do its thing.

Arkaik
Posts: 13
Joined: Mon Jun 12, 2017 12:36 pm

Re: [FreeRTOS] Publish task makes other task crash

Postby Arkaik » Wed Jun 14, 2017 3:54 pm

Thanks for your response.

I did not understand that, I thought the ISR was just the function which encapsulates the xTaskCreate. Good thing to know ^^

From what you said I changed my code to use *FromISR with queue's functions and I unlock the publish task with an event group bit.

So now I don't have any FreeRTOS function without an FromISR at the end.
Indeed, the program do not crash anymore, however, I don't receive any publication anymore. I'll try to investigate to understand why mqtt is not publishing.

Also is that possible to clear the publish task watchdog? Because after some time, the watchdogs is triggered as I'm never deleting the publishing task.

Code: Select all

Task watchdog got triggered. The following tasks did not feed the watchdog in time:
Tasks currently running:
CPU 0: publishTask
Thank you very much for your help freeRTOS becomes clearer and clearer ;)

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

Re: [FreeRTOS] Publish task makes other task crash

Postby ESP_Sprite » Wed Jun 14, 2017 7:51 pm

In theory, the watchdog should only kick in when you don't release control back to FreeRTOS for a pretty long while. Releasing control back to FreeRTOS happens by waiting for something, and for example your queue receive call should do that. Is there somewhere in your publish thread where the CPU can spin indefinitely without waiting for anything maybe?

Arkaik
Posts: 13
Joined: Mon Jun 12, 2017 12:36 pm

Re: [FreeRTOS] Publish task makes other task crash

Postby Arkaik » Thu Jun 15, 2017 7:37 am

Hi,

I don't think there is an infinite spin in my task, all my actions are inside a while(true) but as you said there is the xQueueReceiveFromISR which should block the task.

Moreover, my acquisition task has higher priority than the publish task so the publish task is preempted to let the acquisition task execute, freeRTOS doesn't consider this is giving it control back?

Code: Select all

void publishTask()
{
	char encodingBuffer[500];
	char b64_message[2400];
	
	struct sampleMessage receivedMessage;

	//Wait for PUBLISH_RDY bit to be set by acquisition task
	xEventGroupWaitBits(publishRdyEventGroup, PUBLISH_RDY, pdTRUE, pdFALSE, portMAX_DELAY);
	
	while(true)
	{	
		if(sampleQueue_handler != 0 && !xQueueIsQueueEmptyFromISR(sampleQueue_handler))
		{
			// Receive a message on the created queue.
			if(xQueueReceiveFromISR(sampleQueue_handler, &receivedMessage, NULL))
			{
				//Format and encode message to base64
				encodeMessage(receivedMessage, encodingBuffer);
				
				mg_mqtt_publish(mqttConnection, s_topic, 50, 0, "a", 1);//encodingBuffer, strlen(encodingBuffer));
			}
			else
			{
				printf("[Error] Failed to receive message from queue\n\n");
			}
		}
	}
	vTaskDelete(NULL);
}

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

Re: [FreeRTOS] Publish task makes other task crash

Postby ESP_Sprite » Thu Jun 15, 2017 9:14 am

Why do you use the fromISR functions there, when clearly you're in a task context and not an ISR? They're perfectly usable from a task in theory, but because they are supposed to be called in an interrupt (which doesn't have the ability to wait for another task), they will never block/wait.

Arkaik
Posts: 13
Joined: Mon Jun 12, 2017 12:36 pm

Re: [FreeRTOS] Publish task makes other task crash

Postby Arkaik » Thu Jun 15, 2017 2:27 pm

Why do you use the fromISR functions there, when clearly you're in a task context and not an ISR?
Because I'm stupid ^^ Basically the task was created from ISR (When I was creating it from acquisition task), it changed and I forgot to modify this....

Sorry for the useless post, everything seems to work fine now.

Thank you for your help ;)

Edit :
I finally still have a problem ^^.
I changed my code a little to publish only every 50 samples. When there is not 50 samples yet, the publish task only encode and add messages to a buffer and when the buffer contains 50 samples, it publish all of them in one publication.

It seems to work but after one publication and few samples the same error than before happens.
My acquisition task is not called anymore and I don't have interruptions anymore.

I firstly thought it was a ressources problem because I'm using big buffers but I increased the publish task stack size to 50000 and the problem still happens.

Code: Select all

void acqTask()
{
	float acc_xyz[3];
	float gyr_xyz[3];
	static int sampleCount = 0;
	BaseType_t xHigherPriorityTaskWoken = pdFALSE;
	
	readGyr(i2cBus, gyrScale, &gyr_xyz[0]);
	readAcc(i2cBus, accScale, &acc_xyz[0]);

	struct sampleMessage msg;
	
	msg.messageId = sampleCount;
	memcpy(&msg.gyrData, gyr_xyz, sizeof(gyr_xyz));
	memcpy(&msg.accData, acc_xyz, sizeof(acc_xyz));
	
	if(sampleQueue_handler != 0)
	{
		// Send an unsigned long.  Wait for 10 ticks for space to become available if necessary.
		if(xQueueSendFromISR(sampleQueue_handler, (void *) &msg, NULL) != pdPASS)
		{
			printf("[Error] Failed to send message to queue\n");
		}
		else
		{
			sampleCount += 1;
		}
	}

	xEventGroupSetBitsFromISR(publishRdyEventGroup, PUBLISH_RDY, &xHigherPriorityTaskWoken);

	vTaskDelete(NULL);
}

void publishTask()
{
	char encodingBuffer[500];
	char b64_message[10000];
	int sampleCount = 0;
	
	struct sampleMessage receivedMessage;
	
	b64_message[0]='\0';

	//Wait for PUBLISH_RDY bit to be set by acquisition task
	xEventGroupWaitBits(publishRdyEventGroup, PUBLISH_RDY, pdTRUE, pdFALSE, portMAX_DELAY);

	while(true)
	{	
		if(sampleCount < 49)
		{
			if(sampleQueue_handler != 0)
			{
				// Receive a message on the created queue.
				if(xQueueReceive(sampleQueue_handler, &receivedMessage, ( TickType_t ) 10))
				{
					//Format and encode message to base64
					encodeMessage(receivedMessage, encodingBuffer);

					//Add encoded message to the encoded message stack
					strcat(b64_message, encodingBuffer);
					sampleCount += 1;
				}
				else
				{
					printf("[Error] Failed to receive message from queue\n\n");
				}
			}
		}
		else
		{
			mg_mqtt_publish(mqttConnection, s_topic, 50, 0, b64_message, strlen(b64_message));
			b64_message[0]='\0';
			sampleCount = 0;
		}
	}

	vTaskDelete(NULL);
}

Who is online

Users browsing this forum: No registered users and 64 guests