most compact way to store string-constants in an array of custom struct

Hi everybody,

it took me some time to know how to deal with strings and arrays

I have defined struct for onewire-temperature-sensors

typedef struct DS18B20_Label_t {
  DeviceAddress Adress;
  char Name[128];
  char Acronym_AoC[17];
} DS18B20_Label_t;

and then an array of this structured type

DS18B20_Label_t DS18B20_Label[NoOfoneWireDevices];

As it is an array of struct assignind string-constants / byte-constants like at initialising seems not possible. Or if it is poosible I don't know yet.

DS18B20_Label.Name[0] = {"Sensor placed at ..."}

So I assign the stringconstants by using the strncpy-function

void initialiseDS18B20_Labels() {
  int i = 0;

  strncpy(DS18B20_Label[i].Name        ,"Pufferspeicher unten",sizeof(DS18B20_Label[i].Name));
  strncpy(DS18B20_Label[i].Acronym_AoC ,"PuffFB",sizeof(DS18B20_Label[i].Acronym_AoC)); 
  Store8ByteAdress(DS18B20_Label[i].Adress, 0x28, 0xB2, 0xCB, 0x5A, 0x08, 0x00, 0x00, 0xC3);
  i++;
  
  strncpy(DS18B20_Label[i].Name        ,"Pufferspeicher 20 cm ",sizeof(DS18B20_Label[i].Name));
  strncpy(DS18B20_Label[i].Acronym_AoC ,"Puff1OG", sizeof(DS18B20_Label[i].Acronym_AoC));  
  Store8ByteAdress(DS18B20_Label[i].Adress, 0x28, 0xFC, 0xD1, 0x36, 0x06, 0x00, 0x00, 0x12);

similar thing with the sensor-adresses which are an 8 element array of byte
I wrote a little function to be able to assign it in one line of code

void Store8ByteAdress(uint8_t* deviceAddress, byte b0, byte b1, byte b2, byte b3, byte b4, byte b5, byte b6, byte b7) {
   deviceAddress[0] = b0;
   deviceAddress[1] = b1;
   deviceAddress[2] = b2;
   deviceAddress[3] = b3;
   deviceAddress[4] = b4;
   deviceAddress[5] = b5;
   deviceAddress[6] = b6;
   deviceAddress[7] = b7;
}

//call:
Store8ByteAdress(DS18B20_Label[i].Adress, 0x28, 0xB2, 0xCB, 0x5A, 0x08, 0x00, 0x00, 0xC3);

The strings and adresses are defined at compile-time.
Is there a even more compact way to assign the strings and the onewire-adresses?

best regards Stefan

"Pufferspeicher unten" , "PuffFB",......
Are those going to be permanent or may change?

Can ?? you change it to

typedef struct DS18B20_Label_t {
  DeviceAddress Adress;
  char * pointer_Name;
  char * pointer_Acronym_AoC;
} DS18B20_Label_t;

??

static table initialization can save a lot of code and improve maintainability

struct MyStruct_s {
    int  arr [10];
    const char abr  [5];
    const char desc [20];
};

MyStruct_s myStructs [] = {
    { { 1, 2, 3 },           "abc" , "abracdabra" },
    { { 1, 2, 3, 4, 5, 6 },  "efg" , "now is the time" },
    { { },                   "e tu", "a" },
};

or have a look here and remember that structs and classes are very similar:

https://www.tutorialkart.com/cpp/cpp-array-of-objects/

==> char Name[128];

also, if this is a Uno class board, look at Progmem for static data instead of using ram.

jimakoskx:
"Pufferspeicher unten" , "PuffFB",......
Are those going to be permanent or may change?

Can ?? you change it to

typedef struct DS18B20_Label_t {

DeviceAddress Adress;
char * pointer_Name;
char * pointer_Acronym_AoC;
} DS18B20_Label_t;

as I wrote the content is defined at compile-time = constant during runtime
maybe a later more advanced version want to change it at runtime.
So yes I can change but how does the handling then looks like?
I just read in all sensors-adresses into an array and then do compare with the acronyms like that

float GetTempByAcronym(const char* p_PointerToAcronym) {
  float Temperature = -999; // initialise with value indicating "not found"

  for (int i = 0; i <= DS18B20DeviceCount; i++) {
    if (  strcmp(DS18B20_Label[i].Acronym_AoC, p_PointerToAcronym) == 0 ) {
      Temperature = DS18B20.getTempC(DS18B20_Label[i].Adress);
      break;
    }
  }
  return Temperature;
}

How would the code for this look like when using pointers inside the struct.
And as the struct has only pointers the real content has to be defined anyway

I don't understand the code-example

struct MyStruct_s {
    int  arr [10];
    const char abr  [5];
    const char desc [20];
};

MyStruct_s myStructs [] = {
    { { 1, 2, 3 },           "abc" , "abracdabra" },
    { { 1, 2, 3, 4, 5, 6 },  "efg" , "now is the time" },
    { { },                   "e tu", "a" },
};

are these two different examples that are related or unrelated to each other?
Would you mind to show how your code would look like for the following content?

Name        ,"Fußboden unter dem Pufferspeicher",sizeof(DS18B20_Label[i].Name));
Acronym_AoC ,"PuffFB",sizeof(DS18B20_Label[i].Acronym_AoC)); 
Adress, 0x28, 0xB2, 0xCB, 0x5A, 0x08, 0x00, 0x00, 0xC3);
  
Name        ,"Pufferspeicher 20 cm ",sizeof(DS18B20_Label[i].Name));
Acronym_AoC ,"Puff1OG", sizeof(DS18B20_Label[i].Acronym_AoC));  
Adress, 0x28, 0xFC, 0xD1, 0x36, 0x06, 0x00, 0x00, 0x12);

best regards Stefan

struct MyStruct_s {
    int  arr [10];
    const char *abr;
    const char *desc;
};

MyStruct_s myStructs [] = {
    { { 1, 2, 3 },           "abc" , "abracdabra" },
    { { 1, 2, 3, 4, 5, 6 },  "efg" , "now is the time" },
    { { },                   "e tu", "a" },

    { {0x28, 0xB2, 0xCB, 0x5A, 0x08, 0x00, 0x00, 0xC3 },
                "PuffFB", "Fußboden unter dem Pufferspeicher" },

    { { 0x28, 0xFC, 0xD1, 0x36, 0x06, 0x00, 0x00, 0x12},
                "Puff1OG", "Pufferspeicher 20 cm "},
};

so the compiler locates the strings somewhere in memory and sets the struct ptrs to the lcoation

MyStruct_s myStructs [] = {
    { {0x28, 0xB2, 0xCB, 0x5A, 0x08, 0x00, 0x00, 0xC3 },
                "PuffFB", "Fußboden unter dem Pufferspeicher" },

    { { 0x28, 0xFC, 0xD1, 0x36, 0x06, 0x00, 0x00, 0x12},
                "Puff1OG", "Pufferspeicher 20 cm "},
};

so the compiler locates the strings somewhere in memory and sets the struct ptrs to the location

aha ... --- .... ---- i can recognise my example-data but I have no idea how to use the stored data to finally retrieve the temperature of the right sensor-adress based on the sensors acronym
Is your post meant as an implicit request to become an expert about pointers to an array of pointers of pointers or would you mind
adding the code how to use it in my example-code to finally retrieve the temperature of the right sensor-adress based on the sensors acronym?
best regards Stefan

Is your post meant as an implicit request to become an expert about pointers to an array of pointers of pointers or

? the only pointers are to strings

this compiles

#include <string.h>


// -----------------------------------------------------------------------------
struct DS18B20_s  {
    float getTempC (int arr[]);
};

DS18B20_s ds18B20;

// -----------------------------------------------------------------------------
struct MyStruct_s {
    int         address [10];
    const char *acronym;
    const char *name;
};

MyStruct_s myStructs [] = {
    { {0x28, 0xB2, 0xCB, 0x5A, 0x08, 0x00, 0x00, 0xC3 },
                "PuffFB", "Fußboden unter dem Pufferspeicher" },

    { { 0x28, 0xFC, 0xD1, 0x36, 0x06, 0x00, 0x00, 0x12},
                "Puff1OG", "Pufferspeicher 20 cm "},
};

#define N_MyStruct   (sizeof(myStructs)/sizeof(MyStruct_s))

// -----------------------------------------------------------------------------
#define NO_VALUE    -999.0
float
GetTempByAcronym (
    const char* acronym )
{
  MyStruct_s  *p = myStructs;

  for (int i = 0; i <= N_MyStruct; i++, p++) {
    if (! strcmp (p->acronym, acronym))
      return ds18B20.getTempC (p->address);
  }

  return NO_VALUE;
}

i believe this is obvious to you, that the code becomes fairly simple and sensor configuration using tables is simply a clerical task not requiring any coding skill.

the title says what’s important – Algorithms + DataStructures = Programs

it’s common practice to only Capitalize constants

Hi gcj,

thank you for the example-code. -- ... --- ... --- Aha.

What I write below is in no way meant as critic to you. I thank you for taking time to answer to me.
I just want to answer with honest feedback about my thoughts reading your code.

Uh wow ! a lot more to learn about syntactical specialties the C++-language provides
(compared to languages like SPIN, or pascal)

I stay with my version.

first if al It is really OK by me if you don't provide such a demo-code.
It does compile. But I still don't understand the use of it as a real demo-example that retrievs a temperature-value and prints it to the serial monitor is missing.

The code adds a lot of pointer-complexity but is not more compact than my version.
I don't mind if my code-version has 5 or 6 lines of code more than your verson.

if it would have boiled down to something similiar like

Name = "Fußboden unter dem Pufferspeicher";
Acronym_AoC = "PuffFB"
Adress = {0x28, 0xB2, 0xCB, 0x5A, 0x08, 0x00, 0x00, 0xC3};

I would have taken it.

again what I write here is in no way meant as critic to you. It justs states my point of view.

Maybe this is due to an effect I call "the experts blindness to beginners difficulties". You are an expert about pointers and syntactical constructions like

strcmp (p->acronym, acronym))
      return ds18B20.getTempC (p->address);

I am not. I decide to spent time learning about this to later in the future.
Right now I stay with my code-version .
Thank you all for your suggestions.

best regards Stefan

StefanL38:
a lot more to learn about syntactical specialties the C++-language provides (compared to languages like SPIN, or pascal)

pascal has pointers

would you mind adding the code how to use it in my example-code to finally retrieve the temperature of the right sensor-adress based on the sensors acronym?

StefanL38:
But I still don't understand the use of it as a real demo-example that retrievs a temperature-value and prints it to the serial monitor is missing.

you asked for "retrieve the temperature of the right sensor-adress based on the sensors acronym?"

StefanL38:
The code adds a lot of pointer-complexity but is not more compact than my version.

once you understand them, pointers simplify code. They are what made C suitable for writing an Operating System previously only written in assembly and why it is so prevalent on platforms for controlling hardware

StefanL38:
Maybe this is due to an effect I call "the experts blindness to beginners difficulties". You are an expert about pointers and syntactical constructions like ...

just trying to make you aware of possibilities .. not impress you

Algorithms + DataStructures = Programs is about the use of pointers in a language (Pascal) to demonstrate the use of lists and trees to implement algorithms efficiently. while you may think of pointers as advanced, i often wonder how you might write code without them

my goto language is AWK (which has no pointers).

So,,if i understood ...

You want to write on Serial moitor something like
PuffFB
that means the sensor of (kitchen etc) (P.S.better use english for us to understand)
Fußboden unter dem Pufferspeicher

and translating this according to your mapping in some address
giving back the temperature from specific address sensor?

jimakoskx:
So,,if i understood ...

You want to write on Serial moitor something like
PuffFB
that means the sensor of (kitchen etc) (P.S.better use english for us to understand)
Fußboden unter dem Pufferspeicher

and translating this according to your mapping in some address
giving back the temperature from specific address sensor?

Yes with englisch words: there is a 500 Liter "water-tank working as a thermal "buffer" containing water of my heating system. The thermal energy is generated through burning wood-pellets.
No my goal is to monitor and record temperatures of the heating system at various spots
-bottom of buffer-tank,
-4 different heights along the height of the buffer-tank,
top of buffer-tank

  • temperature potable-water cold (PWC) at entrance of heatexchanger for potable water hot (PWH)
    and exit of heatexchanger PWH,
  • temperature of PWH-C (circulation
  • temperature of heating flow
  • temperature of heating return
  • temperature of buffer-load flow
  • temperature of buffer-load return
    etc. etc,

each onewire-sensor has a worldwide unique-identifier as a 48bit-number "burned in" at manufacturing
those numbers do identify but are uncomfortable to use.

Hence the definition of temperature-labels containing

  • the 48bit-adress as UID a
  • long description for information purposes
  • and a short acronym for access in programming

At starting up the program scans all sensor-UIDs putting them into an array.

I want to access the temperature of each sensor through the acronym that is defined for each sensor
WIth the acronym I can identify the right index-number inside the array to request the temperature.

This means it doesn't matter if the index-number changes through adding / removing sensors.

Try to find match between defined ayconym and acronym given as a paremeter
if acronym is found use corresponding sensor-adress
and do the command getTemp(SensorAdress) retrieves the right temperature-value

This is done by this function

float GetTempByAcronym(const char* p_PointerToAcronym) {
  float Temperature = -999; // initialise with value indicating "not found"

  for (int i = 0; i <= DS18B20DeviceCount; i++) {
    if (  strcmp(DS18B20_Label[i].Acronym_AoC, p_PointerToAcronym) == 0 ) {
      Temperature = DS18B20.getTempC(DS18B20_Label[i].Adress);
      break;
    }
  }
  return Temperature;
}

If you have a more clever idea how to retrieve the right temperature feel free to suggest.

best regards Stefan

StefanL38:
If you have a more clever idea how to retrieve the right temperature feel free to suggest.

Using a char array for your "short acronym" is wasteful of memory and time-consuming to search. Use an enum or enum class instead.

DS18B20 Tips from Experience:

  • Don't use parasite power, especially with lots of sensors over long cable lengths. Provide the sensors with a proper power supply.

  • I really like these sensors, but they take a long time to do the temperature conversion (~750ms at highest resolution setting). So, you should take proper precautions to make sure your system does not become non-responsive to user inputs … especially with as many sensors as you're planning to use:

-- Use the non-blocking technique available with the Arduino Temperature Control Library. It's best to read through its source code to understand this technique.

-- Instruct all sensors to start their conversion at the same time … this takes no longer than the conversion of a single sensor. So, this saves a bunch of time with multiple sensors.

-- If properly set up for non-blocking, the library's conversion routine will return immediately. So, use a millis() timer to determine when the temperature conversion is complete. Do useful work (i.e. check user input, update displays, etc) during this period … don't just delay.

-- Once the conversion timer finishes, poll each of the sensors (by address) for its reading.

Question:
The 8-byte sensor addresses are essentially arbitrary. The library will find them for you in numerical order. How will you know by address which sensor is in which location?

Question:
The 8-byte sensor addresses are essentially arbitrary. The library will find them for you in numerical order. How will you know by address which sensor is in which location?

I run a small testprogram that prints the temperature and the sensoradress in a way that could be copy & pasted directly into the code

best regards Stefan