Click here to Skip to main content
16,022,205 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I have a bit of embedded code. It sits and monitors for incoming serial data on a thread.

It then decodes that serial data into data structures that it puts in a queue for retrieval by another thread.

This is so serial data does not overwhelm the application. It's quite important because the application can run on devices with slow screens, even e-ink.

This works flawlessly in my current rendition of my PC GPU/CPU monitor widget.

However, I want to make a version where I can configure what's displayed on the screen.

There can be zero or more items on the screen, and the user can change what those items are - basically on the fly.

This creates an issue where I need to have variable sized data in a queue, or break up the data into fixed length structures (might be possible) and use multiple queue entries for a screen.

I could just store pointers in a queue, but then I have to malloc and free quite a bit, and I don't want to have to test how many days that will run without crashing due to heap frag.

I'm not necessarily looking for code at all, and i didn't post much code here. I'm looking for ideas on how to structure this data.

Several of these (a variable amount selected by the user) make up one screen.

C++
typedef struct screen_packet {
    char format[16]; // the format string
    float value_max; // max value
    uint8_t icon[16*16*2]; // icon bitmap
    uint16_t colors[2]; // the gradient colors
    bool hsv_color; // true if colors are HSV
    float value; // the current value
} screen_packet_t;


This only works for fixed length structures, but I basically need something like this - replace read_status_t with screen_packet_t here because that's what I am getting at.

C++
//Waiting for UART event.
if (xQueueReceive(serial_queue, (void *)&event, portMAX_DELAY)) {
    switch (event.type) {
    //Event of UART receving data
    /*We'd better handler data event fast, there would be much more data events than
    other types of events. If we take too much time on data event, the queue might
    be full.*/
    case UART_DATA:
        p=dtmp+size; 
        s = uart_read_bytes(UART_NUM_0, p, event.size, portMAX_DELAY);
        if(s>0) {
            size+=s;
            //ESP_LOGI(TAG, "[SERIAL DATA RECEIVED]");
            //puts("[Serial data received]");
            if(size>=sizeof(read_status_t)) {
                memcpy(&status,p,sizeof(read_status_t));
                if(size>sizeof(read_status_t)) {
                    memmove(dtmp,dtmp+sizeof(read_status_t),size-sizeof(read_status_t));
                    size-=sizeof(read_status_t);
                    --size;
                }
                xQueueSend(serial_out_queue,&status,portMAX_DELAY);
                //ESP_LOGI(TAG, "[PACKET POSTED]");
                //puts("[Packet posted]");
            }
        }
        break;


What I have tried:

I was receiving a count of data structures as one serial byte. Then I would take that count and read "count" data structures off that serial line. That works, until I involve a queue with variable sized items. Necessary because the approach I just mentioned doesn't handle slow screens. It gets overwhelmed.

It looks kinda like this:
C++
count = serial_getch();
if(count==-1) {
    break;
}
if(connected ==false || incoming_packets.size()!=count) {
    refresh = true;
}
incoming_packets.clear(true);
incoming_packets.reserve(count);
entry = ui_screen_entries;
for(int i = 0;i<count;++i) {
    if(sizeof(screen_packet_t)!=serial_read_bytes((uint8_t*)&packet,sizeof(packet))) {
        while(serial_getch()!=-1);
        goto done;
    }
    if(!refresh && entry!=nullptr) {
        if(0!=strcmp(entry->value_format,packet.format)) {
            refresh = true;
        }
        entry=entry->next;
    }
    incoming_packets.push_back(packet);
}
Posted
Updated 18-Jul-24 9:27am
v2

1 solution

I'm not sure I understand all of your constraints, but here are two suggestions.

The first is an article I wrote about two queue templates, one for one-way queues and the other for two-way queues. The queue links are embedded in the queue elements themselves. And if a queue's elements derive from a common base class, it can contain polymorphic elements. However, the elements are usually allocated from object pools whose sizes only increase if the pool is close to running out of blocks, so this might not solve your malloc/free problem:

Robust C++: Queue Templates[^]

The second suggestion is to just use one big circular buffer, filling it with the variable-length elements. Each element would specify its total size, along with that of each of its variable-length fields. This avoids the malloc/free problem if the size of the buffer can be hard-coded, but removing an element from the middle of the queue would be problematic.
 
Share this answer
 
Comments
honey the codewitch 20-Jul-24 0:29am    
FreeRTOS has a built in thread safe circular buffer for uneven length items, but I'm not sure it's appropriate here.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900