This is an excellent and popular definition of a conglomerate type database (structure) that we can create using struct keyword.
I don't know how popular it is
From my classroom experience.
Your single line definition contains the important features of a structure -- user-defined type and the tag (typename).
my 15 seconds of fame then
No, your line is not correct. Think of e.g. a pixel on a screen; its position is defined in e.g.
struct POINT
{
int x;
int y;
};
Both types are the same.
The definition of a structure could be written as:
A structure is a way to group together data items/variables of similar/dissimilar types.
You're missing the point that they should be related.
There is nothing between similar and dissimilar, so that part of your phrase is irrelevant.
My book/work based knowledge is up to that on the definition of a structure type database that I create using struct keyword.
post #20 @J-M-L has enumerated the definition of a structure covering wider scope.
Your example of post #25 is a structure that contains two members of similar types.
struct POINT
{
int x;
int y;
};
The following is an example of a structure that contains 3 members of dissimilar types:
struct Data
{
int state;
float frequency;
char myData[5];
};
do you mean that in my code, task4 has missed a bunch of value changes that have occurred inside the struct, as task4 happens so infrequently compared to the other tasks? and in order to print every value change that has occurred in the struct, a semaphore/ mutex must be used.
here is an example with a Mutex when you access the critical resource that is the current slot in the array.
#if CONFIG_FREERTOS_UNICORE
static const BaseType_t app_cpu = 0;
#else
static const BaseType_t app_cpu = 1;
#endif
struct t_Record {
byte taskID;
long randomValue;
unsigned long chrono;
} ;
const size_t maxRecords = 100;
t_Record records[maxRecords];
size_t currentRecord = 0;
SemaphoreHandle_t recordMutex = nullptr;
void Task1(void *argp) {
size_t myIndex;
while (true) {
if (xSemaphoreTake(recordMutex, 10) == pdTRUE ) {
if (currentRecord < maxRecords) {
myIndex = currentRecord++;
records[myIndex].taskID = 1;
records[myIndex].randomValue = random(-1000, 1001);
records[myIndex].chrono = millis();
}
xSemaphoreGive(recordMutex);
}
vTaskDelay(20); //execute this task every 20ms
}
}
void Task2(void *argp) {
size_t myIndex;
while (true) {
if (xSemaphoreTake(recordMutex, 10) == pdTRUE ) {
if (currentRecord < maxRecords) {
myIndex = currentRecord++;
records[myIndex].taskID = 2;
records[myIndex].randomValue = random(-2000, 2001);
records[myIndex].chrono = millis();
}
xSemaphoreGive(recordMutex);
}
vTaskDelay(60); //execute this task every 60ms
}
}
void setup() {
Serial.begin(115200); Serial.println();
recordMutex = xSemaphoreCreateMutex();
// Task1 to run forever
xTaskCreatePinnedToCore(
Task1, // Function to be called
"Task1", // Name of task
1024, // Stack size (bytes in ESP32, words in FreeRTOS)
NULL, // Parameter to pass to function
1, // Task priority (0 to configMAX_PRIORITIES - 1)
NULL, // Task handle
app_cpu); // Run on one core
// Task2 to run forever
xTaskCreatePinnedToCore(
Task2, // Function to be called
"Task2", // Name of task
1024, // Stack size (bytes in ESP32, words in FreeRTOS)
NULL, // Parameter to pass to function
1, // Task priority (0 to configMAX_PRIORITIES - 1)
NULL, // Task handle
app_cpu); // Run on one core
}
void loop() {
static size_t lastIndex = 0;
size_t currentRecordCopy = 0;
if (xSemaphoreTake(recordMutex, 10) == pdTRUE ) {
currentRecordCopy = currentRecord;
xSemaphoreGive(recordMutex);
if (lastIndex != currentRecordCopy) {
for (size_t i = lastIndex; i < currentRecordCopy; i++) {
Serial.print(i); Serial.write('\t');
Serial.print(records[i].taskID); Serial.write('\t');
Serial.print(records[i].randomValue); Serial.write('\t');
Serial.println(records[i].chrono);
}
}
lastIndex = currentRecordCopy;
}
}
totally untested, so not sure this works but that should give you an idea
How do you categorise these circled keywords? I don't know what they are. At first, I thought it was a datatype but it can't be because sometimes they have a datatype assigned to them e.g. 'const'.
I'd appreciate it if someone can explain them to me.
Thank you

you could have asked the question in the other thread. ➜ merging (and answering there)
So those are indeed types - either part of standard C++ like size_t (std::size_t - cppreference.com) or custom types offered by the Espressif or FreeRTOS APIs. for example BaseType_t
is defined in task.h as
typedef BaseType_t (*TaskHookFunction_t)( void * );
same goes for the semaphore stuff, it's part of the API.
Reading the doc on the mutex or semaphore would give you the high level view on what you need to know and how to use those.
My 2 cents
Queue handle and structure
QueueHandle_t xQ_eData;
struct stu_eData
{
float oTemperature = 0.0f;
float oHumidity = 0.0f;
float oPressure = 0.0f;
// for outside aqi???????????????????????? what does the RPi send an int or float???
float Temperature = 0.0f;
float Pressure = 0.0f;
float Humidity = 0.0f;
float IAQ = 0.0f; // Index Air Quality
float RM0 = 0.0f; // Remaining Moisture from sensor 0
//float PM2 = 0.0f; // particles in air
float WS = 0.0f; // wind speed
String WD = ""; // wind direction
float RF = 0.0f; // rainfall
//float WSV = 0.0f; // weather station volts
//float WSC = 0.0f; // weather station current
//float WSP = 0.0f; // weather station power
float WindChill = 0.0f; //windchill
float DewPoint = 0.0f; //dew point or dew index
int SunRiseHr = 0; // sunrise hour
int SunRiseMin = 0; //sunrise minute
int SunSetHr = 0; //sunset hour
int SunSetMin = 0; //sunset minute
int DuskHr = 0; //dusk
int DuskMin = 0; //dusk
int DawnHr = 0; // dawn
int DawnMin = 0; // dawn
int TransitHr = 0; // 'noon' time
int TransitMin = 0; // 'noon' time
double azimuth = 0.0f; // Sun's azimuth, in degrees
double elevation = 0.0f; // Sun's elevation, in degrees
float CO2 = 0.0f;
float PressureH = 0.0f;
float PressureL = 10000.0f;
int cngPress = 0; // pressure change 0= no change, -1 slow change +1 fast change
} x_eData; // environmental data
In setup()
the queue is assigned a pointer and configured to xQ_eData = xQueueCreate( 1, sizeof(stu_eData) );
send a queue copy of the structure
This task writes directly into the structure created in global memory. Note the use of a semaphore to prevent all other access to the structure during structure access, xSemaphoreTake ( sema_eData, portMAX_DELAY );
.
void fFindDewPointWithHumidity( void *pvParameters )
{
float temperature = 0.0f;
for ( ;; )
{
xEventGroupWaitBits (eg, evtDewPoint, pdTRUE, pdTRUE, portMAX_DELAY );
temperature = (x_eData.oTemperature - 32) / 1.8f; // Celsius (°C) = (Fahrenheit - 32) / 1.8 convert to C
xSemaphoreTake( sema_eData, portMAX_DELAY );
x_eData.DewPoint = log(x_eData.oHumidity / 100) + (17.62 * temperature) / (243.12 + temperature);
x_eData.DewPoint = 243.12 * x_eData.DewPoint / (17.62 - x_eData.DewPoint);
x_eData.DewPoint = (x_eData.DewPoint * 1.8f) + 32.0f; // convert back to F
xSemaphoreGive ( sema_eData );
}
vTaskDelete( NULL );
}
This task uses a copy of the structure to access data that may be changing. Note how the structure is prevented from being updated during the actual structure copy, xSemaphoreTake( sema_eData, portMAX_DELAY ); with a semaphore.
void fDoTheDisplayThing( void * parameter )
{
float *ptr = CollectionPressure;
int yIncrement = 18;
int CurrentY = 20;
int CurrentX = 5;
String temp1 = "";
temp1.reserve(10);
String temp2 = "";
temp2.reserve(10);
int boxSpacing = 80;
size_t item_size;
for (;;)
{
xEventGroupWaitBits (eg, evtDisplayUpdate, pdTRUE, pdTRUE, portMAX_DELAY );
xSemaphoreTake( sema_eData, portMAX_DELAY );
struct stu_eData px_eData = x_eData;
xSemaphoreGive ( sema_eData );
CurrentY = 20;
display.init();
//display.setFont(&FreeMonoBold9pt7b);
display.setFont(&FreeMono9pt7b);
//u8g2Fonts.setFont(u8g2_font_helvB08_tf);
display.setTextColor(GxEPD_BLACK);
display.setFullWindow();
display.fillScreen(GxEPD_WHITE); // set the background to white (fill the buffer with value for white)
display.setCursor( CurrentX, CurrentY );
// first line
display.drawRect( CurrentX, CurrentY , 70, 55, GxEPD_BLACK);
display.drawBitmap( CurrentX + 10, CurrentY + 5, temperature_icon16x16, 16, 16, GxEPD_BLACK);
display.setCursor( CurrentX + 30, CurrentY + 15 );
//display.print( char(223) + "F" );
display.print( "F" );
display.setCursor( CurrentX + 10, CurrentY + 40);
display.print( String(px_eData.oTemperature) );
display.drawRect( CurrentX + boxSpacing, CurrentY , 70, 55, GxEPD_BLACK);
display.setCursor( CurrentX + 90, CurrentY + 15 );
display.print( "R.H.");
display.setCursor( CurrentX + 90, CurrentY + 35 );
display.print( String((int)px_eData.oHumidity) + "%" );
display.setCursor( CurrentX, CurrentY + 40);
display.drawRect( CurrentX + (boxSpacing * 2 ), CurrentY , 70, 55, GxEPD_BLACK);
display.setCursor( CurrentX + 163, CurrentY + 15 );
display.print( "Dew Pt" );
display.setCursor( CurrentX + 165, CurrentY + 35 );
display.print( String(px_eData.DewPoint) );
display.drawRect( CurrentX + (boxSpacing * 3 ), CurrentY , 70, 55, GxEPD_BLACK);
display.setCursor( CurrentX + 246, CurrentY + 15 );
display.print( "AQI" );
display.setCursor( CurrentX + 246, CurrentY + 35 );
display.print( String(int(px_eData.IAQ)) + "%" );
display.drawRect( CurrentX + (boxSpacing * 4 ), CurrentY , 70, 55, GxEPD_BLACK);
display.setCursor( CurrentX + 327, CurrentY + 15 );
display.print( "R.M." );
display.setCursor( CurrentX + 327, CurrentY + 35 );
display.print( String(int(px_eData.RM0)) + "%" );
// end of first line
if ( px_eData.SunRiseMin < 10 )
{
temp1.concat( "0" + String(px_eData.SunRiseMin) );
} else {
temp1.concat( String(px_eData.SunRiseMin) );
}
if ( px_eData.SunSetMin < 10 )
{
temp2.concat( "0" + String(px_eData.SunSetMin) );
} else {
temp2.concat( String(px_eData.SunSetMin) );
}
CurrentY += yIncrement;
CurrentY += yIncrement;
CurrentY += yIncrement;
CurrentY += yIncrement;
display.setCursor( CurrentX, CurrentY );
display.print( "Wind: " );
CurrentY += yIncrement;
display.setCursor( CurrentX, CurrentY );
display.print( "Speed " + String(px_eData.WS) + "KPH, Dir " + String(px_eData.WD) + " Chill " + String(px_eData.WindChill) + "F" );
CurrentY += yIncrement;
display.drawRect( CurrentX, CurrentY , 70, 55, GxEPD_BLACK);
addsun( 35, CurrentY + 30 , Small, SmallIcon );
display.setCursor( CurrentX + 5, CurrentY + 15 );
display.print( "0" + String(px_eData.SunRiseHr) + ":" + temp1 );
display.setCursor( CurrentX + 5, CurrentY + 50 );
display.print( String(px_eData.SunSetHr) + ":" + temp2 );
display.drawRect( CurrentX + boxSpacing, CurrentY , 70, 55, GxEPD_BLACK);
addraindrop(CurrentX + 110, CurrentY + 15, 7);
display.setCursor( CurrentX + 90, CurrentY + 35 );
display.print( String(px_eData.RF) );
display.setCursor( CurrentX + 100, CurrentY + 50 );
display.print( "mm" );
display.drawRect( CurrentX + (boxSpacing * 2 ), CurrentY , 70, 55, GxEPD_BLACK);
display.setCursor( CurrentX + 177, CurrentY + 15 );
display.print( "C02" );
display.setCursor( CurrentX + 165, CurrentY + 35 );
display.print( String(int(px_eData.CO2)) );
display.setCursor( CurrentX + 165, CurrentY + 50 );
display.print( "PPM" );
//make graph
xSemaphoreTake( sema_CollectPressure, portMAX_DELAY );
CurrentY += yIncrement * 6;
display.setCursor( CurrentX, CurrentY); //set cursor position
//display.drawLine( CurrentX, CurrentY, CurrentX + 200, CurrentY, GxEPD_BLACK);
//int BaseLine = (int)CollectionPressure[0];
int BaseLine = (int)*ptr;
int offsetX = 0;
for ( int j = 0; j < BufferCount; j++ )
{
if ( *(ptr + j) != 0.0f )
{
//int yAdj = BaseLine - (int)CollectionPressure[j];
int yAdj = BaseLine - (int)*(ptr + j);
display.setCursor( CurrentX + offsetX, CurrentY + yAdj );
display.print( "-" );
offsetX += 5;
// log_i( "pressure %f item %d", CollectionPressure[j], j );
}
}
CurrentY += yIncrement;
display.setCursor( CurrentX, CurrentY );
display.print( String(px_eData.oPressure) + "mmHg" );
int Xone = 48;
int Yone = 59;
CurrentY += yIncrement;
display.setCursor( CurrentX, CurrentY );
display.print( PressureRateOfChange() );
xSemaphoreGive( sema_CollectPressure );
temp2 = "";
temp1 = "";
//
display.display(false); // full update
display.hibernate();
//log_i( "DoTheBME280Thing high watermark % d", uxTaskGetStackHighWaterMark( NULL ) );
} //for (;;)
vTaskDelete( NULL );
} //void fDoTheDisplayTHing( void * parameter )
Oh and see how the display task takes info from the copied structure and uses that info to be displayed? Sort of like printing to the Serial monitor. While the copied structure is being displayed the other tasks can be updating the global structure.
The right-most word prior to the variable name is the data type.
Words like unsigned, long, short, double etc. are type modifiers.
Words like const, volatile and mutable are type qualifiers.
Worlds like static, extern, register are storage classes.
Another version with semaphore and queue. I think it accomplishes the intent of OP's Post #17 --- although that intent still seems rather odd to me.
Compiles but untested:
#include "Arduino.h"
SemaphoreHandle_t recordMutex;
xQueueHandle recordQueue;
struct Struct_Data {
int frequency; //Task2's variable
unsigned short int avg_val; //Task3's variable
};
Struct_Data loggedInfo;
void Task1(void *argp) {
int buttom_state;
Struct_Data localInfo;
while (1) {
buttom_state = rand() % 2; //generate a random int between 0 and 1 (i.e. 0 or 1)
if (buttom_state == 1) {
xSemaphoreTake(recordMutex, portMAX_DELAY);
localInfo = loggedInfo;
xSemaphoreGive(recordMutex);
xQueueSendToBack(recordQueue, &localInfo, portMAX_DELAY);
}
vTaskDelay(200); //execute this task every 200ms
}
}
void Task2(void *argp) {
int anlg_freq;
while (1) {
xSemaphoreTake(recordMutex, portMAX_DELAY);
anlg_freq = 500 + (rand() % (1000 - 500 + 1)); //randomly set frequency to an int between 500 to 1000
loggedInfo.frequency = anlg_freq;
xSemaphoreGive(recordMutex);
vTaskDelay(1000); //execute this periodic task every second
}
}
void Task3(void *argp) {
int filter_analg_val;
int new_analg_val;
while (1) {
xSemaphoreTake(recordMutex, portMAX_DELAY);
new_analg_val = 0;
for (int i = 0; i <= 3; i++) { //take 4 readings
int analg_val = 0 + (rand() % (100 - 0 + 1)); //randomly set analg_val to an int between 0 to 100
new_analg_val = new_analg_val + analg_val;
}
filter_analg_val = new_analg_val / 4; //average of the last 4 readings
loggedInfo.avg_val = filter_analg_val;
xSemaphoreGive(recordMutex);
vTaskDelay(40); //execute this task every 40ms
}
}
//Task4 - print values to the serial port ONLY when state=1;
void Task4(void *argp) {
Struct_Data localInfo;
while (1) {
xQueueReceive(recordQueue, &localInfo, portMAX_DELAY);
Serial.print(localInfo.frequency);
Serial.print(",");
Serial.println(localInfo.avg_val);
}
}
void setup() {
Serial.begin(115200);
delay(1000);
recordMutex = xSemaphoreCreateMutex();
xSemaphoreGive(recordMutex);
recordQueue = xQueueCreate(10, sizeof(Struct_Data));
// Task1 to run forever
xTaskCreatePinnedToCore(
Task1, // Function to be called
"Task1", // Name of task
1024, // Stack size (bytes in ESP32, words in FreeRTOS)
NULL, // Parameter to pass to function
1, // Task priority (0 to configMAX_PRIORITIES - 1)
NULL, // Task handle
ARDUINO_RUNNING_CORE); // Run on one core
// Task2 to run forever
xTaskCreatePinnedToCore(
Task2, // Function to be called
"Task2", // Name of task
1024, // Stack size (bytes in ESP32, words in FreeRTOS)
NULL, // Parameter to pass to function
1, // Task priority (0 to configMAX_PRIORITIES - 1)
NULL, // Task handle
ARDUINO_RUNNING_CORE); // Run on one core
// Task3 to run forever
xTaskCreatePinnedToCore(
Task3, // Function to be called
"Task3", // Name of task
1024, // Stack size (bytes in ESP32, words in FreeRTOS)
NULL, // Parameter to pass to function
1, // Task priority (0 to configMAX_PRIORITIES - 1)
NULL, // Task handle
ARDUINO_RUNNING_CORE); // Run on one core
// Task4 to run forever
xTaskCreatePinnedToCore(
Task4, // Function to be called
"Task4", // Name of task
1024, // Stack size (bytes in ESP32, words in FreeRTOS)
NULL, // Parameter to pass to function
1, // Task priority (0 to configMAX_PRIORITIES - 1)
NULL, // Task handle
ARDUINO_RUNNING_CORE); // Run on one core
}
void loop() {
vTaskDelete(NULL);
}
Hey. Good job with the queue and semaphore use.
a bit more complicated than that, for example const char * const myPtr;
you read right to left so this is a constant pointer (*) to a character that is constant
Hmm, didn't see that one in the screenshot.
OK - missed the point that you were specific to @cinnamonroll's code.
makes sense in that context.