Deciphering a class of structures(?)

Hi,

Some time ago I had a collaborator who did an excellent job creating code for a remote datalogger. They have since disappeared, I have recently rejuvenated the project, and I have a problem understanding a fundamental piece of code that is used to create the array in which variable names and value pairs are stored.

Ultimately, I want to create the currData array on initialization such that it respects sensor channels and respective data feeds that I #define as true / false in the header (here I just present the Dallas temperature sensors, and SAMD temp).

If anyone could clarify how keyvalue works (for example keyvalue(“Dallas1”), is passed to currData(1).key is it?), and could perhaps suggest a means of assigning the list of fields on compile, I’d be very happy.

Thanks a Lot!

#include <OneWire.h>                  // include the library for the Dallas one wire protocol
#include <DallasTemperature.h>        // include the library for the DS18B20 Dallas temperature sensors 

#include <TemperatureZero.h>    //temperature of SAMD chip

//////////////////////////////////////////////
// DS18B20 temperature sensors
//////////////////////////////////////////////
//Resolution and time to read each sensor:
//9-bit resolution 0.5°C 93.75 ms
//10-bit resolution 0.25°C 187.5 ms
//11-bit resolution 0.125°C 375 ms
//12-bit resolution 0.0625°C 750 ms
const int DS18B20bit = 12; //bit depth of temp sensors
const int tempOutputRes = 4; // precision of stored values
const int delayTime = 500; //was 50

#define ONE_WIRE_BUS_PIN 5       // Digital pin for the DS18B20 Dallas sensor bus 

OneWire oneWire(ONE_WIRE_BUS_PIN);    // Setup a oneWire instance to communicate with any OneWire devices
DallasTemperature DS_sens(&oneWire);  // Pass our oneWire reference to Dallas Temperature.

// declaration of variables; (addresses of all DS18B20 temp sensors that we use)
// the address of every single DS18B20 Dallas temperature sensor can be find out with the help of our DS18B20_AddressCheck code
const byte DS18B20address[7][8] {
  {0x28, 0xD7, 0x51, 0x55, 0x0A, 0x00, 0x00, 0x34}, //The air temp one
  {0x28, 0x3E, 0x2F, 0x55, 0x0A, 0x00, 0x00, 0x44}, //The old indoor one
  {0x28, 0x86, 0x95, 0x54, 0x0A, 0x00, 0x00, 0x9F}, //The rock temp one
  {0x28, 0x31, 0xFF, 0xFE, 0x0A, 0x00, 0x00, 0x03}, //03
  {0x28, 0x25, 0x40, 0xFF, 0x0A, 0x00, 0x00, 0x2B}, //2B
  {0x28, 0x23, 0x00, 0xFF, 0x0A, 0x00, 0x00, 0x70}, //70
  {0x28, 0xBF, 0x8D, 0xFE, 0x0A, 0x00, 0x00, 0x14}  //14
};


#define KV_DT1            0
#define KV_DT2            1
#define KV_DT3            2
#define KV_DT4            3
#define KV_DT5            4
#define KV_DT6            5
#define KV_SAMDT          6

#define NUM_KEYVALS       7

//////////////////////////////////////////////
// MKR temperature
//////////////////////////////////////////////
TemperatureZero TempZero;            //  initialize a tempZero object

/*
  Structure of data names and values... this part puzzles me

  measurements are stored as key value pairs in the keyvalue object. Both are character arrays 10 bytes long so they are uniform.
*/
struct keyvalue {
  const static uint8_t KEYVAL_STRING_SIZE = 10; // I thought this should be the first argument 
  char key [KEYVAL_STRING_SIZE]; //name //why char and not char*?
  char val [KEYVAL_STRING_SIZE]; //data

  keyvalue(char* k) {                   //one instance here
    if (sizeof(k) < KEYVAL_STRING_SIZE)
    {
      strcpy(key, k);               
      char v [] = "";
      strcpy(val, v);
    }
    else keyvalue();

  }
  keyvalue() {                //and another here, what?... this is clearing?
    char k [] = "";
    //char v [] = "";
    strcpy(key, k);
    strcpy(val, k);
  }

};

//  array of keyvalues where somehow the name gets passed to currData(X).key
//  in fact, I want to assign this array on compile to include more or less sensors
//  #defining the keyvalue positions, then adding their names to this array, and manually
//  #define-ing the NUM_KEYVALS is awkward
//  In fact I have > 30 sensors that vary depending on the purpose of each logger
keyvalue currData[] = {keyvalue("Dallas1"),
                       keyvalue("Dallas2"),
                       keyvalue("Dallas3"),
                       keyvalue("Dallas4"),
                       keyvalue("Dallas5"),
                       keyvalue("Dallas6"),
                       keyvalue("SAMD1"),
                      };

void setup() {



}

void loop() {

  readAllSensors(currData);
  delay(2000);
}

/*
  readAllSensors()

  Calls functions to read all sensors
*/
void readAllSensors( keyvalue* dataArray) {

  readDALLAS( dataArray[KV_DT1].val,
              dataArray[KV_DT2].val,
              dataArray[KV_DT3].val,
              dataArray[KV_DT4].val,
              dataArray[KV_DT5].val,
              dataArray[KV_DT6].val,
              dataArray[KV_DT1].KEYVAL_STRING_SIZE);


  readSAMDTemp(  dataArray[KV_SAMDT].val,
                 dataArray[KV_SAMDT].KEYVAL_STRING_SIZE);

  for (int i = 0; i < NUM_KEYVALS; i++)
  {
    cleanString(dataArray[i].val, sizeof(dataArray[i].val));
  }

}

/*
  cleanString()

  Takes a character array and its size and replaces it with a version that doesn't include any spaces in front of it.
*/

void cleanString(char* str, uint8_t size)
{
  char newStr [size] = {};

  uint8_t index = 0;
  bool started = false;

  for (int i = 0; i < size; i++)
  {
    if (started == false) {
      if (str[i] != ' ' && str[i] != '\t' && str[i] != '\n') {
        started = true;
        newStr[index] = str[i];
        index++;
      }
    }
    else
    {
      newStr[index] = str[i];
      index++;
    }
  }
  memset(str, 0, size);

  strcpy(str, newStr);
}

/*
  float2char()

  Takes a floating point number and formats it as a string
*/
char* float2char(float floatVal, int precision, char* output)
{
  char strFormat[6];

  sprintf(strFormat, "%s%d%s", "%0.", precision, "f");
  sprintf(output, strFormat, floatVal);

  return (output);
}

//////////////////////////////////////////////
// DS18B20 temperature sensors
//////////////////////////////////////////////
void readDALLAS(  char* value1,
                  char* value2,
                  char* value3,
                  char* value4,
                  char* value5,
                  char* value6,
                  uint8_t size)     //  value is the char array that will store the wetness
{
  memset( value1, 0, size);  //  clears the array before storing data
  memset( value2, 0, size);  //  clears the array before storing data
  memset( value3, 0, size);  //  clears the array before storing data
  memset( value4, 0, size);  //  clears the array before storing data
  memset( value5, 0, size);  //  clears the array before storing data
  memset( value6, 0, size);  //  clears the array before storing data


  DS_sens.begin();  // Initialize the Temperature measurement library

  Serial.print("Number of DS18B20 sensors found on bus = ");

  Serial.println(DS_sens.getDeviceCount());

  int DS18B20sensors = sizeof (DS18B20address) / sizeof (DS18B20address[0]); //Number of sensors addressed
  // set the resolution (Can be 9 to 12 bits .. lower is faster)
  for (int i = 0; i < DS18B20sensors; i++) {
    DS_sens.setResolution(DS18B20address[i], DS18B20bit);
  }

  // Command all devices on DALLAS bus to read temperature and add to datastring
  Serial.println("Reading DS18B20s");


  DS_sens.requestTemperatures();

  float dval[DS18B20sensors - 1];
  for (int i = 0; i < DS18B20sensors; i++) {
    dval[i] = DS_sens.getTempC(DS18B20address[0]);
  }


  float2char(dval[0], 3, value1);
  float2char(dval[1], 3, value2);
  float2char(dval[2], 3, value3);
  float2char(dval[3], 3, value4);
  float2char(dval[4], 3, value5);
  float2char(dval[5], 3, value6);
  Serial.println("DS18B20 sensor poll completed");
}


void readSAMDTemp( char* value, uint8_t size)     //  value is the char array that will store the wetness
{
  Serial.println("Reading SAMD");


  memset( value, 0, size);  //  clears the array before storing data

  //  start up the sensor, wait a bit, then take a measurement.
  TempZero = TemperatureZero();

  TempZero.init();
  delay(500);
  float temperature = TempZero.readInternalTemperature();
  delay(500);
  TempZero.disable(); //saves ~60uA in standby

  float2char(temperature, 3, value);
#if verboseOutput  //  basic output
  Serial.print(F("SAMD internal temp: "));
  Serial.print(value);
  Serial.println(F("degC."));
#endif
}
  memset(str, 0, size);

  strcpy(str, newStr);

Wasteful. strcpy() will completely overwrite anything that is in 'str', it's pointless to clear it with memset().

in C++ a struct is basically a class so you have 2 constructors, one that takes an argument [keyvalue(char* k)] and one that does not [keyvalue()] and the compiler figures out which one you want based on if there is an argument present or not.

As for that structure, it seems very odd since you are storying the temperature as a string all the time. Your functions to do the reading also need to know how many sensors are present so you aren’t really creating any sort of generic storage structure. If you change the array, you would have to change your readDALLAS() and readSAMDTemp() functions to match.

  float dval[DS18B20sensors - 1];
  for (int i = 0; i < DS18B20sensors; i++) {
    dval[i] = DS_sens.getTempC(DS18B20address[0]);
  }

This is assigning outside the array ‘dval’ on the last iteration.

  keyvalue(char* k) {                   //one instance here
    if (sizeof(k) < KEYVAL_STRING_SIZE)

That pains me.

Thanks All,

That's clearer blh64. And nice catch on the DS18B20 error... in fact I should've stripped that out before posting, lucky I didn't!

I'm not sure why they used the memset -> strcpy lines, I assume it's in case the newstr is somehow empty?

kerryleith:
Thanks All,

That's clearer blh64. And nice catch on the DS18B20 error... in fact I should've stripped that out before posting, lucky I didn't!

I'm not sure why they used the memset -> strcpy lines, I assume it's in case the newstr is somehow empty?

That is why you always post a the complete code or at least a complete example. Many details would be missed otherwise.
As to an empty newstr, it would copy just fine. strcpy() ensures the destination has a nul character at the end

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.