[FreeRTOS] Cómo recibir múltiples tipos de datos en una misma cola

336 views
Skip to first unread message

Gaspar Santamarina

unread,
Oct 23, 2019, 12:00:41 PM10/23/19
to embebidos32@
Buenos días,

Desde una tarea en FreeRTOS necesito procesar datos generados por distintos tipos de sensores (cada uno con su respectiva tarea); cada sensor almacena los datos en un tipo de estructura distinto al de los demás.
Por simplicidad, lo ideal sería tener una única cola donde recibir las mediciones de todos los sensores y en lo posible por valor, no por referencia. Cómo debería implementarlo?.

Una de las posibles soluciones sería enviar, junto con el dato, un ID que identifique a cada sensor. De esta forma, en la tarea encargada de procesar los datos, leo primero el ID y en base a este decido cuantos bytes leo de la cola y cómo procesar el dato. Al crear la cola, definiría los datos a almacenar de tipo byte.

Alguna sugerencia?.

Saludos,
Gaspar.

Ivan Camilo Aranda C.

unread,
Oct 23, 2019, 12:30:23 PM10/23/19
to embeb...@googlegroups.com
Hola Gaspar, nosotros en su momento presentamos algo similar, como tal no pasabamos el dato sino un puntero que apuntaba a una sección del Heap.

Espero te pueda servir.

Saludos.

--
-- Recibiste este mensaje porque estás suscripto al Grupo Google Embebidos32. Para postear en este grupo, escribe un email a embeb...@googlegroups.com. Para des-suscribirte, envía un email a embebidos32...@googlegroups.com. Para más opciones, visita el sitio del grupo en https://groups.google.com/d/forum/embebidos32?hl=es
---
Has recibido este mensaje porque estás suscrito al grupo "Embebidos32" de Grupos de Google.
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a embebidos32...@googlegroups.com.
Para ver esta conversación en el sitio web, visita https://groups.google.com/d/msgid/embebidos32/CACtv%2Bif0hV4V5XOt96o1RH3zeD8zhMeUga8tXWOUeCKqAAs_uw%40mail.gmail.com.


--
IVAN CAMILO ARANDA CASAS
P Please consider the environment before printing this e-mail  

Pablo Slavkin - disenioconingenio

unread,
Oct 23, 2019, 12:43:00 PM10/23/19
to embeb...@googlegroups.com
podes hacer un 'union' con todas las estructuras y pasar eso a la cola
y decidir cual usar en funcion del id comun a todas.

--
> > <https://groups.google.com/d/msgid/embebidos32/CACtv%2Bif0hV4V5XOt96o1RH3zeD8zhMeUga8tXWOUeCKqAAs_uw%40mail.gmail.com?utm_medium=email&utm_source=footer>
> > .
> >
>
>
> --
> *IVAN CAMILO ARANDA CASAS*
> P Please consider the environment before printing this e-mail
>
> --
> -- Recibiste este mensaje porque estás suscripto al Grupo Google Embebidos32. Para postear en este grupo, escribe un email a embeb...@googlegroups.com. Para des-suscribirte, envía un email a embebidos32...@googlegroups.com. Para más opciones, visita el sitio del grupo en https://groups.google.com/d/forum/embebidos32?hl=es
> ---
> Has recibido este mensaje porque estás suscrito al grupo "Embebidos32" de Grupos de Google.
> Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a embebidos32...@googlegroups.com.
> Para ver este debate en la Web, visita https://groups.google.com/d/msgid/embebidos32/CAEPo-4VFurjetx_9p8qZ0i5jF1nx%3DeCN2dqE%2BV92gFU7--EDwQ%40mail.gmail.com.

Franco Bucafusco

unread,
Oct 23, 2019, 12:49:22 PM10/23/19
to embeb...@googlegroups.com
Gaspar

  Lo mas facil es lo que decis. Si la medicion es un float, ponele, declaras una estructura (el float y el codigo como decis vos), y luego los elementos de la cola los escalas al tamaño de la misma. Si los datos de los sensores son de tamaño variable, podes hacer lo mismo (un array en vez de un float), y escalar para el peor caso, o lo que dice Ivan. Todo depende de los recursos que tengas y de tus necesidades, claro.

    sds!

--------------------------------------------------------- Franco Bucafusco ---------------------------------------------------------


Gaspar Santamarina

unread,
Oct 23, 2019, 2:44:52 PM10/23/19
to embebidos32@
El mié., 23 oct. 2019 a las 16:30, Ivan Camilo Aranda C. (<kmiloa...@gmail.com>) escribió:
Hola Gaspar, nosotros en su momento presentamos algo similar, como tal no pasabamos el dato sino un puntero que apuntaba a una sección del Heap.

No entiendo como implementar esto. Sería usar un buffer circular para cada sensor y enviar a la cola un ID del sensor y un puntero al valor (estructura) escrito en el buffer?

Saludos,
Gaspar.

carlos cabas

unread,
Oct 23, 2019, 10:58:48 PM10/23/19
to embeb...@googlegroups.com
Hola Gaspar.
No sé si entendí bien pero acá te envío un ejemplo de como utilizar un
espacio de memoria para almacenar diferentes tipos de datos y luego
recuperarlos por casting.

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"

typedef union{
int readed;
float sens2;
} dato;

typedef struct{
char id;
dato value;
}pkt;

xQueueHandle cola;

void Task_sens1()
{
pkt lectura;
lectura.id = 1;
lectura.value.readed = 2500;
while(1){
xQueueSend(cola, &lectura, portMAX_DELAY);
printf("Datos int enviado\r\n");
vTaskDelay(500/portTICK_PERIOD_MS);
}
}

void Task_sens2()
{
pkt lectura;
lectura.id = 2;
lectura.value.readed = 3.5;
while(1){
xQueueSend(cola, &lectura, portMAX_DELAY);
printf("Datos float enviado\r\n");
vTaskDelay(500/portTICK_PERIOD_MS);
}
}

void Task_Proc()
{
pkt lectura;
while(1){
xQueueReceive(cola, &lectura, portMAX_DELAY);
if(lectura.id == 1)
printf("entero %i, recibido\r\n", (int)lectura.value.readed);
else if(lectura.id == 2)
printf("flotante %f, recibido\r\n", (float)lectura.value.readed);
}
}

void app_main()
{
cola = xQueueCreate(1, sizeof(pkt));
xTaskCreate(Task_sens1, "task1", 2000, NULL, 3, NULL);
xTaskCreate(Task_sens2, "task2", 2000, NULL, 2, NULL);
xTaskCreate(Task_Proc, "task3", 2000, NULL, 1, NULL);
}

La salida de este código es:
Datos int enviado
Datos float enviado
entero 2500, recibido
flotante 3.000000, recibido


Saludos y feliz noche.

El mié., 23 oct. 2019 a las 11:00, Gaspar Santamarina
(<gasp...@gmail.com>) escribió:
>
> --
> -- Recibiste este mensaje porque estás suscripto al Grupo Google Embebidos32. Para postear en este grupo, escribe un email a embeb...@googlegroups.com. Para des-suscribirte, envía un email a embebidos32...@googlegroups.com. Para más opciones, visita el sitio del grupo en https://groups.google.com/d/forum/embebidos32?hl=es
> ---
> Has recibido este mensaje porque estás suscrito al grupo "Embebidos32" de Grupos de Google.
> Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a embebidos32...@googlegroups.com.
> Para ver esta conversación en el sitio web, visita https://groups.google.com/d/msgid/embebidos32/CACtv%2Bif0hV4V5XOt96o1RH3zeD8zhMeUga8tXWOUeCKqAAs_uw%40mail.gmail.com.



--
Cordialmente;

Carlos Cabas Meriño
Ingeniero Electrónico
Esp. Automatización Industrial (UBA)
Técnico Profesional en Contabilidad y Finanzas

carlos cabas

unread,
Oct 23, 2019, 11:25:38 PM10/23/19
to embeb...@googlegroups.com
Hola gaspar, en el código anterior tengo un error (por eso de andar
copiando y pegando), acá el código corregido:

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"

typedef union{
int readed;
float sens2;
} dato;

typedef struct{
char id;
dato value;
}pkt;

xQueueHandle cola;

void Task_sens1()
{
pkt lectura;
lectura.id = 1;
lectura.value.readed = 2500;
while(1){
xQueueSend(cola, &lectura, portMAX_DELAY);
printf("Datos int enviado\r\n");
vTaskDelay(500/portTICK_PERIOD_MS);
}
}

void Task_sens2()
{
pkt lectura;
lectura.id = 2;
lectura.value.sens2 = 3.5;
while(1){
xQueueSend(cola, &lectura, portMAX_DELAY);
printf("Datos float enviado\r\n");
vTaskDelay(500/portTICK_PERIOD_MS);
}
}

void Task_Proc()
{
pkt lectura;
while(1){
xQueueReceive(cola, &lectura, portMAX_DELAY);
if(lectura.id == 1)
printf("entero %i, recibido\r\n", lectura.value.readed);
else if(lectura.id == 2)
printf("flotante %f, recibido\r\n", lectura.value.sens2);
}
}


void app_main()
{
cola = xQueueCreate(1, sizeof(pkt));
xTaskCreate(Task_sens1, "task1", 2000, NULL, 3, NULL);
xTaskCreate(Task_sens2, "task2", 2000, NULL, 2, NULL);
xTaskCreate(Task_Proc, "task3", 2000, NULL, 1, NULL);
}

Salida:
Datos int enviado
Datos float enviado
entero 2500, recibido
flotante 3.500000, recibido

El mié., 23 oct. 2019 a las 11:00, Gaspar Santamarina
(<gasp...@gmail.com>) escribió:
>
> --
> -- Recibiste este mensaje porque estás suscripto al Grupo Google Embebidos32. Para postear en este grupo, escribe un email a embeb...@googlegroups.com. Para des-suscribirte, envía un email a embebidos32...@googlegroups.com. Para más opciones, visita el sitio del grupo en https://groups.google.com/d/forum/embebidos32?hl=es
> ---
> Has recibido este mensaje porque estás suscrito al grupo "Embebidos32" de Grupos de Google.
> Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a embebidos32...@googlegroups.com.
> Para ver esta conversación en el sitio web, visita https://groups.google.com/d/msgid/embebidos32/CACtv%2Bif0hV4V5XOt96o1RH3zeD8zhMeUga8tXWOUeCKqAAs_uw%40mail.gmail.com.



--
Cordialmente;

Carlos Cabas Meriño
Ingeniero Electrónico
Est. Posgrado Automatización Industrial (UBA)

Santiago Germino

unread,
Oct 24, 2019, 3:12:48 AM10/24/19
to Embebidos32
Hola Gaspar!


Por simplicidad, lo ideal sería tener una única cola donde recibir las mediciones de todos los sensores y en lo posible por valor, no por referencia. Cómo debería implementarlo?.

Siendo C, te referis a pasarlo por copia y no por puntero?

Te propondria un cambio de diseño ya que pareciese que queres solucionar un problema que no tenes, metiendo codigo que no necesitas! O sea, en nombre de la simplicidad, nunca se agrega mas codigo del necesario!

Por que no usar una cola por sensor y que cada tarea de adquisicion haga un lock de su propia cola? Si la cola es compartida, todas las tareas deberian hacer un lock a la misma cola. Ademas, para que perder un byte de memoria por valor adquirido para usarlo como ID, cuando podrias guardar cada cosa en su cola correspondiente?

Tambien te podrias plantear la necesidad misma de usar una estructura como tipo de dato para los valores del sensor: la tarea toma un dato a intervalos que ya conoces y cada sensor generalmente mide un escalar (temperatura, presión, etc). Si el sensor fuese un acelerometro, podes crear una cola por cada escalar x, y, z. Generalizando, podes hacer que cada sensor indique cuantos escalares necesita y le inicias un array de n colas para ese sensor.

Me parece que asi como tenes tareas para capturar datos, tambien deberias tener las correspondientes para procesarlos. Una sola tarea de procesamiento con un arbol gigante de "cases" o "ifs"  es un criadero de bugs.

Saludos!
Santiago.

Gaspar Santamarina

unread,
Oct 24, 2019, 9:46:40 AM10/24/19
to embebidos32@
El jue., 24 oct. 2019 a las 3:25, carlos cabas (<ing.car...@gmail.com>) escribió:
Hola gaspar, en el código anterior tengo un error (por eso de andar
copiando y pegando), acá el código corregido:

Gracias por tu respuesta Carlos.  El problema que veo con esta solución es que mi estructura pkt sería muy grande porque debería tener todas las estructuras de datos (algunos sensores necesitan almacenar hasta 10 uint32, otros son combinaciones de varios tipos de datos distintos) y por cada medición estaría usando una memoria igual al espacio requerido por todos los sensores. Quizás podría funcionar para estructuras de datos mas pequeñas, pero en mi caso no creo que me sea util.

El jue., 24 oct. 2019 a las 7:12, Santiago Germino (<royc...@gmail.com>) escribió:
Hola Gaspar!

Hola Santiago!

Siendo C, te referis a pasarlo por copia y no por puntero?

Correcto!
 
Por que no usar una cola por sensor y que cada tarea de adquisicion haga un lock de su propia cola? Si la cola es compartida, todas las tareas deberian hacer un lock a la misma cola.

Creo que esta sería la solución mas simple, pero quería ver si había una forma sencilla de usar una misma cola para todos los sensores. Me pareció que mejoraría la escalabilidad de la arquitectura, permitiéndome agregar facilmente mas sensores en el futuro.
 
Ademas, para que perder un byte de memoria por valor adquirido para usarlo como ID, cuando podrias guardar cada cosa en su cola correspondiente?

Por el tamaño de las estructuras de datos, el espacio que requiere un ID (un byte) sería despreciable.
 
Tambien te podrias plantear la necesidad misma de usar una estructura como tipo de dato para los valores del sensor:

Así es!, cada sensor tiene su propia estructura de datos. Ese es el problema, la cola necesita almacenar distintos tipos de estructuras y de variados tamaños.
 
la tarea toma un dato a intervalos que ya conoces y cada sensor generalmente mide un escalar (temperatura, presión, etc). Si el sensor fuese un acelerometro, podes crear una cola por cada escalar x, y, z. Generalizando, podes hacer que cada sensor indique cuantos escalares necesita y le inicias un array de n colas para ese sensor.

Me parece demasiado engorroso, preferiría hacer una cola para cada sensor en lugar de hacer una cola para cada una de las variables que lee el sensor.
 
Me parece que asi como tenes tareas para capturar datos, tambien deberias tener las correspondientes para procesarlos. Una sola tarea de procesamiento con un arbol gigante de "cases" o "ifs"  es un criadero de bugs.

 La tarea encargada de "procesar" los datos básicamente es un broker: recibe los datos de los sensores y los envía a un servidor remoto. En caso de que se haya perdido la conexión con el servidor, almacena los datos en una SD y cuando se reconecta reenvía este backup local al servidor. La diferencia de procesamiento es la forma en la que se arma el POST HTTP para cada sensor. En definitiva, esta tarea broker leería los datos de la cola y dependiendo de qué sensor sean, arma el string a enviar y se lo pasa a una tarea encargada de manejar el módulo GPRS. Por esta similitud en el "procesamiento" de los datos adquiridos es que consideré usar una única tarea para el broker.

Saludos,
Gaspar.


carlos cabas

unread,
Oct 24, 2019, 11:15:12 AM10/24/19
to embeb...@googlegroups.com
Hola Gaspar.

Respecto a esto:  El problema que veo con esta solución es que mi estructura pkt sería muy grande porque debería tener todas las estructuras de datos (algunos sensores necesitan almacenar hasta 10 uint32, otros son combinaciones de varios tipos de datos distintos) y por cada medición estaría usando una memoria igual al espacio requerido por todos los sensores.

Realmente estarías usando un espacio de memoria del máximo tamaño de las estructuras que estarías usando, por ello el uso de Union, que básicamente lo que hace es utilizar el mismo espacio de memoria para diferentes tipos de datos y su tamaño sería el tamaño mas grande del tipo de datos que pertenezca a la Union.

por ejemplo una union de este tipo:

union {
      char dato1;
      char vec[10] ;
      int dato2;   
}

no utilizaría la suma un espacio de memoria igual a (sizeof(char) + sizeof(int) + sizeof(vec))..... simplemente utilizaría el espacio del tipo de dato mas grande, en este caso 10 bytes del tipo de dato del vector "vec".

Discupla si soy yo el que no está entendiendo el problema, pero bueno, Saludos Feliz tarde.


--
-- Recibiste este mensaje porque estás suscripto al Grupo Google Embebidos32. Para postear en este grupo, escribe un email a embeb...@googlegroups.com. Para des-suscribirte, envía un email a embebidos32...@googlegroups.com. Para más opciones, visita el sitio del grupo en https://groups.google.com/d/forum/embebidos32?hl=es
---
Has recibido este mensaje porque estás suscrito al grupo "Embebidos32" de Grupos de Google.
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a embebidos32...@googlegroups.com.

Ulises Bigliati

unread,
Oct 24, 2019, 11:17:52 AM10/24/19
to embeb...@googlegroups.com
Buenas, para mí es el enfoque correcto:
una cola por sensor y que cada tarea de adquisicion haga un lock de su propia cola? Si la cola es compartida, todas las tareas deberian hacer un lock a la misma cola.
Si cada tipo sensor es una tarea diferente, cada tarea tiene su propia cola de salida de mensajes y si la necesita, también de entrada.

Podría ser diferente el criterio si tuvieras una línea de n dispositivos de la misma especie, de los cuales, incluso podrías desconocer a priori su cantidad (ej: un bus 1-wire). En un caso así, una tarea podría reportar el estado del bus completo en una única cola.


El 24/10/19 a las 04:12, Santiago Germino escribió:
--
-- Recibiste este mensaje porque estás suscripto al Grupo Google Embebidos32. Para postear en este grupo, escribe un email a embeb...@googlegroups.com. Para des-suscribirte, envía un email a embebidos32...@googlegroups.com. Para más opciones, visita el sitio del grupo en https://groups.google.com/d/forum/embebidos32?hl=es
---
Has recibido este mensaje porque estás suscrito al grupo "Embebidos32" de Grupos de Google.
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a embebidos32...@googlegroups.com.

Gaspar Santamarina

unread,
Oct 24, 2019, 12:03:27 PM10/24/19
to embebidos32@
El jue., 24 oct. 2019 a las 15:15, carlos cabas (<ing.car...@gmail.com>) escribió:
Realmente estarías usando un espacio de memoria del máximo tamaño de las estructuras que estarías usando, por ello el uso de Union, que básicamente lo que hace es utilizar el mismo espacio de memoria para diferentes tipos de datos y su tamaño sería el tamaño mas grande del tipo de datos que pertenezca a la Union.

No conocía el funcionamiento de las union, mala mía. Ahora si me cierra mas!. Osea que tendría algo así:

typedef struct{
   int val1;
   int val2;
   float val3;
} sensor1data;

typedef struct{
   uint8_t val1;
   float val2;
} sensor2data;

typedef union{
   sensor1data sens1;
   sensor2data sens2;

} dato;

typedef struct{
   char id;
   dato value;
} pkt;

Y para acceder a los valores haría, por ejemplo: lectura.value.sens1.val1

Tendría que analizar el tamaño de cada estructura de datos para ver si no desperdicio mucha memoria, porque si tengo un sensor que me genera dos enteros y otro que me genera veinte enteros creo que no se justificaría.


El jue., 24 oct. 2019 a las 15:17, Ulises Bigliati (<ulisesb...@gmail.com>) escribió:
Podría ser diferente el criterio si tuvieras una línea de n dispositivos de la misma especie, de los cuales, incluso podrías desconocer a priori su cantidad (ej: un bus 1-wire). En un caso así, una tarea podría reportar el estado del bus completo en una única cola.

Este es el caso de uno de los sensores: basicamente es un array de sensores de concentración de gases que me devuelven don tensiones, una de trabajo y otra de correción. Como tomo la medición del array completo en un mismo instante de tiempo, pienso almacenar todo en una única estructura y agregarle su respectivo timestamp.

Saludos,
Gaspar.

Alejandro Celery

unread,
Oct 25, 2019, 7:06:47 AM10/25/19
to Embebidos32
Hola Gaspar!

La respuesta es más bien conceptual: Si copiar es barato, copiá el dato. Si copiar es caro, pasá punteros. Hay una regla de dedo que define barato como "3 words" (para un CPU de 32 bits, serían 3 int32_t). Eso en cuanto a copiar punteros o el dato.
Otro concepto es que si copiás punteros te tenés que asegurar que nadie te sobreescriba el dato al que estás apuntando hasta que lo uses vos. Si copiás el dato no tenés este problema.
Por último para mi es mucho más sencillo conceptualmente tener una sola tarea que procese datos y una sola cola donde poner los datos a procesar. Además, cuando tengas que debuggear eso tenés que revisar en un solo lugar cuál fue el dato que se recibió. No hay ningún problema en que muchas tareas escriban a una misma cola dado que las operaciones de cola de FreeRTOS son atómicas.

Ahora fijate que Santiago te dijo algo diferente, lo "simple" es una cuestión de cada uno a veces. Vos fijate lo que a vos te parezca mejor.

En cuanto a tu problema particular y si usar uniones o no, vas a tener que encontrar una solución para tu caso.

En cuanto a que no conocías las uniones, te recomiendo que leas el libro de C de Kernighan & Ritchie, se lee super rápido (salvo que te pongas a hacer todos los ejercicios) y te da una muy buena idea del lenguaje C entero y de las partes más útiles de la librería estándar de C.

Saludos y éxitos con tu proyecto!

Gaspar Santamarina

unread,
Oct 25, 2019, 2:36:11 PM10/25/19
to embebidos32@
El vie., 25 oct. 2019 a las 11:06, Alejandro Celery (<alejand...@gmail.com>) escribió:
Otro concepto es que si copiás punteros te tenés que asegurar que nadie te sobreescriba el dato al que estás apuntando hasta que lo uses vos. Si copiás el dato no tenés este problema.

Es por esto que prefiero pasar el dato por valor: el dato queda almacenado en la cola y me olvido de implementar mi propio buffer donde almacenar los datos de forma temporal (las mediciones se hacen con mayor frecuencia que con la que se envían al servidor; se envía un paquete de mediciones, no una medición por vez)
 
Por último para mi es mucho más sencillo conceptualmente tener una sola tarea que procese datos y una sola cola donde poner los datos a procesar. Además, cuando tengas que debuggear eso tenés que revisar en un solo lugar cuál fue el dato que se recibió.

Si, yo también preferiría hacerlo así. 

En cuanto a que no conocías las uniones, te recomiendo que leas el libro de C de Kernighan & Ritchie, se lee super rápido (salvo que te pongas a hacer todos los ejercicios) y te da una muy buena idea del lenguaje C entero y de las partes más útiles de la librería estándar de C.

Gracias por la recomendación!, ya estuve pispeando varias cosas que no tenía 100% claras. Me viene de 10 para ver en qué cosas ando medio flojo
.
Saludos y éxitos con tu proyecto!

Gracias Alejandro!

Saludos,
Gaspar.
Reply all
Reply to author
Forward
0 new messages