Passing string value out of multi data type structure? [Solved]

I was able to successfully record data into a structure and display that data both on the serial monitor and a client webpage display. There was 1 analog input value and 4 digital input values. The digital inputs all display either '0' or '1' since the values are showing the digitalRead result with no manipulation.
My next step is to convert the digital reads to a 'char' value to read 'ON' for a 0 [LOW] and 'OFF' for a 1 [HIGH].

I modified the structure data types from 'int8_t' to 'char' and the code compiled w/o errors
but when I look at the values on the serial monitor there is a small square instead of the character string associated with ON and OFF.

I feel like the issue is within the 'void addHistoricalData' and that the conversion is getting
lost or incorrectly interpreted. I did see an example on Google that used a separate
definition of the structure data as data.name:

#include <iostream>

using namespace std;

struct Data {
  int i;
  float f;
  char c;
};

int main() {
  Data data;
  data.i = 10;
  data.f = 3.14;
  data.c = 'a';

  cout << data.i << endl;
  cout << data.f << endl;
  cout << data.c << endl;

  return 0;
}
boolean sensor_read_1 = false;
boolean sensor_read_2 = false;
boolean sensor_read_3 = false;
boolean sensor_read_4 = false;

char lockout[4] = "OFF";
char aqua_cmd[4] = "OFF";
char blr_cmd[4] = "OFF";
char blr_stat[4] = "OFF";

struct BoilerData {
  int16_t tank_temp;
  char lockout;
  char aqua_cmd;
  char blr_cmd;
  char blr_stat;
};
constexpr size_t maxCycleCount = 16;
BoilerData historicalData[maxCycleCount];

size_t nextHistoricalEntry = 0;
bool HistoricalDataIsFull = false;

void addHistoricalData(int16_t tank_temp, char lockout_state, char aqua_state, char cmd_state, char run_stat) {
  historicalData[nextHistoricalEntry].tank_temp = tank_temp;
  historicalData[nextHistoricalEntry].lockout = lockout_state;
  historicalData[nextHistoricalEntry].aqua_cmd = aqua_state;
  historicalData[nextHistoricalEntry].blr_cmd = cmd_state;
  historicalData[nextHistoricalEntry].blr_stat = run_stat;

  nextHistoricalEntry = (nextHistoricalEntry + 1) % maxCycleCount; 

  if (nextHistoricalEntry == 0) HistoricalDataIsFull = true;  
}
void printHistoricalDataAtIndex(size_t idx) {
  Serial.print(idx + 1);  // for display start numbering at 1 (in C++ arrays starts at index 0)
  Serial.write('\t');
  Serial.print(historicalData[idx].tank_temp);
  Serial.write("           ");
  Serial.print(historicalData[idx].lockout);
  Serial.write("           ");
  Serial.print(historicalData[idx].aqua_cmd);
  Serial.write("           ");
  Serial.print(historicalData[idx].blr_cmd);
  Serial.write("           ");
  Serial.println(historicalData[idx].blr_stat);
}
void printHistoricalData() {

  Serial.println(F("\n-----------------------------"));
  // print the number of valid samples
  if (HistoricalDataIsFull) Serial.print(maxCycleCount);
  else Serial.print(nextHistoricalEntry);

  Serial.println(F(" Historical Data sample(s)"));
  Serial.println(F("-----------------------------"));
  Serial.println(F("#\tsensor1 \ Lockout\ Aquastat \ Boiler Cmd \ Boiler Status"));
  if (HistoricalDataIsFull) {
    for (size_t i = nextHistoricalEntry; i < nextHistoricalEntry + maxCycleCount; ++i) {
      printHistoricalDataAtIndex(i % maxCycleCount);  // Ensure the index stays within bounds
    }
  } else {
    for (size_t i = 0; i < nextHistoricalEntry; ++i) {
      printHistoricalDataAtIndex(i);
    }
  }
}
void setup() {
  Serial.begin(115200);

  pinMode(kill_boiler_relay, OUTPUT);
  pinMode(system_alarm_LED, OUTPUT);

  pinMode(blr_current_relay, INPUT_PULLUP);
  pinMode(zone_pump_wifi_input, INPUT_PULLUP);      
  pinMode(aquastat_run_command, INPUT_PULLUP); 
 
void loop() {

  current_Millis = millis();  //get the number of milliseconds since the program started
  UNO_run_cycle_start = millis();
  actualTime = now();  // retrieve UNO system timestamp

  int16_t tempSensor1 = analogRead(A0);
  int8_t boiler_lockout_state = digitalRead(zone_pump_wifi_input);
  int8_t aquastat_state = digitalRead(aquastat_run_command);
  int8_t blr_cmd_state = digitalRead(kill_boiler_relay);
  int8_t blr_run_state = digitalRead(blr_current_relay);
 
  if (boiler_lockout_state == HIGH) {
    strcpy(lockout, "OFF");
    sensor_read_1 = false;
  }
  if (boiler_lockout_state == LOW && sensor_read_1 == false) {
    strcpy(lockout, "ON");
    addHistoricalData(tempSensor1, lockout, aqua_cmd, blr_cmd, blr_run_state);
    sensor_read_1 = true;
  }
  if (aquastat_state == HIGH) {
    strcpy(aqua_cmd, "OFF");
    sensor_read_2 = false;
  }
  if (aquastat_state == LOW && sensor_read_2 == false) {
    strcpy(aqua_cmd, "ON");
    addHistoricalData(tempSensor1, lockout, aqua_cmd, blr_cmd, blr_stat);
    sensor_read_2 = true;
  }
  if (blr_cmd_state == HIGH) {
    strcpy(blr_cmd, "OFF");
    sensor_read_3 = false;
  }
  if (blr_cmd_state == LOW && sensor_read_3 == false) {
    strcpy(blr_cmd, "ON");
    addHistoricalData(tempSensor1, lockout, aqua_cmd, blr_cmd, blr_stat);
    sensor_read_3 = true;
  }
  if (blr_run_state == HIGH) {
    strcpy(blr_stat, "OFF");
    sensor_read_4 = false;
  }
  if (blr_run_state == LOW && sensor_read_4 == false) {
    strcpy(blr_stat, "ON");
    addHistoricalData(tempSensor1, lockout, aqua_cmd, blr_cmd, blr_stat);
    sensor_read_4 = true;
  }
  delay(1000);
  printHistoricalData();
  delay(1000);

  int16_t Temp_Sensor_1 = A0;  
  tank_tmp = analogRead(Temp_Sensor_1);  

  const int B = 4275;          // B value of the thermistor;  +- 275 = .6 C (1.0 F)
  const uint32_t R0 = 100000;  // R0 = 100k; int type uint32_t required (unsigned 32 bit)
  float R1 = 1023.0 / tank_tmp - 1.0;
  R1 = R0 * R1;
  tnk_temp = 1.0 / (log(R1 / R0) / B + 1 / 298.15) - 273.15;
  int tank_temp = (tnk_temp * 9 / 5) + 32 - tmp_adj_1;

  if (current_Millis - startMillis >= LED_period)  //test whether the period has elapsed
  {
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
    startMillis = current_Millis;
  }
}

you are defining lockout as char array, which in the low level is just a pointer to an adress containing your data. Also you've set your update function and structure to only accept single char and not arrays. an easier way would be to set you structure to hold char arrays of 3 characters and just using if else or {condition ? "ON" : "OFF" } as to set the value in the function instead of giving tons of overhead to the function buy pointing it to multiple arrays that that needs to be setup and accessed.

Hence you don't get the requested data but instead a blank character meaning your memory adress doesn't correspond to anything in ASCII

You can't assign the character arrays to the structure variables with the simple = operator. Try using strcpy( ).

/*
void addHistoricalData(int16_t tank_temp, char lockout_state, char aqua_state, char cmd_state, char run_stat) {
  historicalData[nextHistoricalEntry].tank_temp = tank_temp;
  historicalData[nextHistoricalEntry].lockout = lockout_state;
  historicalData[nextHistoricalEntry].aqua_cmd = aqua_state;
  historicalData[nextHistoricalEntry].blr_cmd = cmd_state;
  historicalData[nextHistoricalEntry].blr_stat = run_stat;

  nextHistoricalEntry = (nextHistoricalEntry + 1) % maxCycleCount;

  if (nextHistoricalEntry == 0) HistoricalDataIsFull = true;
}
*/

void addHistoricalData(int16_t tank_temp, char lockout_state, char aqua_state, char cmd_state, char run_stat) {
  historicalData[nextHistoricalEntry].tank_temp = tank_temp;
  strcpy(historicalData[nextHistoricalEntry].lockout, lockout_state);
  strcpy(historicalData[nextHistoricalEntry].aqua_cmd, aqua_state);
  strcpy(historicalData[nextHistoricalEntry].blr_cmd, cmd_state);
  strcpy(historicalData[nextHistoricalEntry].blr_stat, run_stat);

  nextHistoricalEntry = (nextHistoricalEntry + 1) % maxCycleCount;

  if (nextHistoricalEntry == 0) HistoricalDataIsFull = true;
}

Bingo!!!

Thank you Delta_G !!!! :white_check_mark: :white_check_mark: :white_check_mark:

I realized after adding the asterisk [*] to the char function something weird happened.
When changes of states on the digital inputs occurred instead of changing just the next record it overwrote the existing records digital values so all the records had the same value as the last record. The analog input was not affected.

???

Using the strcpy() mod you suggested gave me the small square blocks again. I also tried it with and without the * behind char in the void function. Unfortunately when I copied the data in serial monitor these small blocks were gone so I added a screenshot.

-----------------------------
2 Historical Data sample(s)
-----------------------------
#	sensor1      Lockout      Aquastat      Boiler Cmd      Boiler Status
1	333                                            
2	333                                            

-----------------------------
2 Historical Data sample(s)
-----------------------------
#	sensor1      Lockout      Aquastat      Boiler Cmd      Boiler Status
1	333                                            
2	333                                            

-----------------------------
2 Historical Data sample(s)
-----------------------------
#	sensor1      Lockout      Aquastat      Boiler Cmd      Boiler Status
1	333                                            
2	333                                            

Please bear with me. I am a little out of sorts right now trying to understand things. I did get your friend analogy though.
I thought all this data being recorded was under one structure:

struct BoilerData {
  int16_t tank_temp;
  char lockout;
  char aqua_cmd;
  char blr_cmd;
  char blr_stat;
};

Are you saying I need to create individual arrays inside BoilerData for these individual character strings?

I won't lie it is still a little fuzzy around the edges what you are trying to drive home.

I need to go in and just start trying different things with these individual arrays and if I can get one to work they should all subsequently work. So the theory goes...lol.

thanks for the assist...

After two hours of racking my old geezer brain I am cashing it in for today. You reach a point where things start to run together and that eureka moment gets further away.

I understand the jist of what you are explaining. The relationship of pointers and arrays is an important one and since I have just got started using arrays the nuance is still evading me.

I need to find a good tutorial on arrays and pointers and hopefully things will gel. Right now I am going to use ones and zeros in my webpage display and later on do the On/Off stuff.

Appreciate your sharing your knowledge with me. With a little luck I will conquer this obstacle. :+1:

Ok, I have spent too many hours on this. Its embarrassing. I even read a tutorial on arrays and pointers and understand the concept of both [C Programming: Absolute Beginner's Guide]. Although it did not get into structures in much detail.

Here is a scaled down super slim edition of the code where I am only trying to record one digital input and have it print out 'OFF' or 'ON' on the monitor.
This iteration does that but overwrites existing records with the new value. I have tried strcpy() and using the dereferencing operator *.
One area I remain confused about is the assignment of char to 'test' both
inside the struct TestData and outside the struct. It is yellow outside the struct and white
inside the struct. Is both declarations working in unison or are they totally separate in function. To my erroneous way of thinking the char definition inside the struct is all that is needed. But that is wrong. The latest value of the digital input is still going to the memory locations used by previous records. Why?
Hopefully my error is a simple one and easily corrected. I could be wrong. ...lol...


```cpp
const int test_input = 3;
int8_t test_input_state = 0;
boolean sensor_read_1 = false;
char test[4] = "OFF";

struct TestData {
  char* test;
};

constexpr size_t maxCycleCount = 4;
TestData historicalData[maxCycleCount];
size_t nextHistoricalEntry = 0;
bool HistoricalDataIsFull = false;

void addHistoricalData(char* ON_OFF) {
  historicalData[nextHistoricalEntry].test = ON_OFF;
  nextHistoricalEntry = (nextHistoricalEntry + 1) % maxCycleCount;
  if (nextHistoricalEntry == 0) HistoricalDataIsFull = true;
}
void printHistoricalDataAtIndex(size_t idx) {
  Serial.print(idx + 1);
  Serial.write('\t');
  Serial.println(historicalData[idx].test);
}
void printHistoricalData() {
  Serial.println(F("\n-----------------------------"));
  if (HistoricalDataIsFull) Serial.print(maxCycleCount);
  else Serial.print(nextHistoricalEntry);
  Serial.println(F(" Historical Data sample(s)"));
  Serial.println(F("-----------------------------"));
  Serial.println(F("# \Data Value"));
  if (HistoricalDataIsFull) {
    for (size_t i = nextHistoricalEntry; i < nextHistoricalEntry + maxCycleCount; ++i) {
      printHistoricalDataAtIndex(i % maxCycleCount);
    }
  } else {
    for (size_t i = 0; i < nextHistoricalEntry; ++i) {
      printHistoricalDataAtIndex(i);
    }
  }
}
void setup() {
  Serial.begin(115200);
  pinMode(test_input, INPUT_PULLUP);
}
void loop() {
  test_input_state = digitalRead(test_input);
  if (test_input_state == HIGH) {
    strcpy(test, "OFF");
    addHistoricalData(test);
    sensor_read_1 = false;
  }
  if (test_input_state == LOW && sensor_read_1 == false) {
    strcpy(test, "ON");
    addHistoricalData(test);
    sensor_read_1 = true;
  }
  delay(1000);
  printHistoricalData();
  delay(2000);
}

OK, is this an improvement? But now I have two data values in each record instead of just one for each time the input changes state.
The values are not overwriting anymore though. :+1: :+1

char test_OFF[4] = "OFF";
char test_ON[4] = "ON";

struct TestData {
  char*  test_OFF;
  char*  test_ON;
};

constexpr size_t maxCycleCount = 4;
TestData historicalData[maxCycleCount];
size_t nextHistoricalEntry = 0;
bool HistoricalDataIsFull = false;

void addHistoricalData(char* OFF_ON) {
  historicalData[nextHistoricalEntry].test_OFF = OFF_ON;
  historicalData[nextHistoricalEntry].test_ON = OFF_ON;
  nextHistoricalEntry = (nextHistoricalEntry + 1) % maxCycleCount;
  if (nextHistoricalEntry == 0) HistoricalDataIsFull = true;
}
void printHistoricalDataAtIndex(size_t idx) {
  Serial.print(idx + 1);
  Serial.write('\t');
  Serial.println(historicalData[idx].test_OFF);
  Serial.println(historicalData[idx].test_ON);
}

Well, the situation seems to be devolving either from my utter stupidity or general state of incompetence.

Making your suggested char array assignment in the struct has allowed a value to pass from 'test' out of the dig input monitoring if statement in the void loop() to the void addHistorical Data.

When printed out on the serial monitor it shows 'ONN' not 'ON' and the other values remain a '0.' I never can generate an 'OFF' state even after changing the pinmode assignment to 'INPUT' from 'INPUT_PULLUP' and adding the requisite external pullup resistor attached to 5 volt pin. I though the input might be floating. I even moved char test down to the void loop().
So things remain wonky .... :cry:


```cpp
const int test_input = 3;
int8_t test_input_state = 0;
boolean sensor_read_1 = false;

struct TestData {
  char test[5];
};

constexpr size_t maxCycleCount = 4;
TestData historicalData[maxCycleCount];
size_t nextHistoricalEntry = 0;
bool HistoricalDataIsFull = false;

void addHistoricalData(char OFF_ON) {
  strcpy(historicalData[nextHistoricalEntry].test, OFF_ON);
  nextHistoricalEntry = (nextHistoricalEntry + 1) % maxCycleCount;
  if (nextHistoricalEntry == 0) HistoricalDataIsFull = true;
}
void printHistoricalDataAtIndex(size_t idx) {
  Serial.print(idx + 1);
  Serial.write('\t');
  Serial.println(historicalData[idx].test);
  
}
void printHistoricalData() {
  Serial.println(F("\n-----------------------------"));
  if (HistoricalDataIsFull) Serial.print(maxCycleCount);
  else Serial.print(nextHistoricalEntry);
  Serial.println(F(" Historical Data sample(s)"));
  Serial.println(F("-----------------------------"));
  Serial.println(F("# \Data Value"));
  if (HistoricalDataIsFull) {
    for (size_t i = nextHistoricalEntry; i < nextHistoricalEntry + maxCycleCount; ++i) {
      printHistoricalDataAtIndex(i % maxCycleCount);
    }
  } else {
    for (size_t i = 0; i < nextHistoricalEntry; ++i) {
      printHistoricalDataAtIndex(i);
    }
  }
}
void setup() {
  Serial.begin(115200);
  pinMode(test_input, INPUT);
}
void loop() {
  char test;
  test_input_state = digitalRead(test_input);
  if (test_input_state == HIGH) {
    strcpy(test,"OFF"); 
    addHistoricalData(test);
    sensor_read_1 = false;
  }
  if (test_input_state == LOW && sensor_read_1 == false) {
    strcpy(test,"ON");
    addHistoricalData(test);
    sensor_read_1 = true;
  }
  delay(1000);
  printHistoricalData();
  delay(2000);
}

Screenshot of serial monitor output:

This is really perturbing. I specifically referenced your reply when I responded and it still does not attach the response to you. Why does it do that?

Please see response 21.

OMG......OMG.......OMG......OMG......OMG......OMG.......OMG......OMG......
OMG......OMG.......OMG......OMG......OMG......OMG.......OMG......OMG......
OMG......OMG.......OMG......OMG......OMG......OMG.......OMG......OMG......
OMG......OMG.......OMG......OMG......OMG......OMG.......OMG......OMG......

Sometimes it pays to be a glutton for punishment. I feel like I just finished a phreakin'
marathon.

It works. It actually is working!!!!

My appreciation of your willingness to bear with me cannot be overstated.

May the coding gods look favorably on you forever and always.
I gotta go crack open a bottle of vino. ...lol...

p.s. now I will try to add 'timestamping' to the structure. But I promise to
leave you alone. :upside_down_face:

That clarification was excellent. I already understood most of what you were attempting to convey. Spending a couple hours yesterday in the C Beginners Book helped me grasp things better about pointers, arrays, structures, and the infamous "Heap."

Arrays and structures have to be one of the backbones for C code. They allow data to be manipulated and consolidated so well that there are endless applications to using them.

Thanks again bud....

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