[SOLVED] Struct arrays not possible on arduino... sort of.

I’m a bit confused about creating and using arrays of a custom structure with arduino IDE and visual micro IDE (w/Arduino pluggin & proper linked libraries). One thread says to make my custom structs in a separate .h file and use them that way as a work around for the arduino IDE/Compiler, but MicroVisual on Visual Studio while better in may ways, will seem to compile identical HEX executable code (probably the same compiler and directives). So back to structures and arrays in C++.

While this works as expected:

#include <EEPROM.h>
struct ledTjParameters {
	float slope;      //slope number, diode speccific
	float intercept;  //also diode specific
	char  label[8];  // diode name -which one to the human world
};
const int   numDiodes = 4;  //zero based
byte        CalCheck = 0;

void setup() {
	Serial.begin(9600);
	analogReference(INTERNAL);
	uint32_t sample = 0;
	uint16_t adrPnt = 0;
	for (uint16_t i = 0; i < 1000; i++) {
		sample += analogRead(1);
		Serial.println(sample);
		delay(50);
	}
	//sample = sample /1000;
	float Vref = (1000 * 1024000) / float(sample);
	Vref = Vref / 1000.0;
	sample = 1024000000000 / sample;
	EEPROM.put(adrPnt, Vref);
	adrPnt += sizeof(sample); // = 4
	EEPROM.put(adrPnt, sample);
	adrPnt += sizeof(sample); // = 8
	Serial.print("Vref = ");
	Serial.print(Vref, 6);
	Serial.println("V");
	Serial.print("Integer iRef (microvolts) =");
	Serial.println(sample);

	ledTjParameters ledCL = {
		41.320174f,
		-74.662063f,
		"LT Cree"
	};
	ledTjParameters ledCR = {
		41.320174f,
		-74.662063f,
		"RT Cree"
	};
	ledTjParameters ledAM = {
		41.320174f,
		-74.662063f,
		"Ambient"
	};
	ledTjParameters ledCT = {
		41.320174f,
		-74.662063f,
		"Coolant"
	};
	// put values for LED parameters and store in the correct structures in EEPROM	
	EEPROM.put(adrPnt, ledCL);
	adrPnt += sizeof(ledTjParameters);
	EEPROM.put(adrPnt, ledCR);
	adrPnt += sizeof(ledTjParameters);
	EEPROM.put(adrPnt, ledAM);
	adrPnt += sizeof(ledTjParameters);
	EEPROM.put(adrPnt, ledCT);
	adrPnt += sizeof(ledTjParameters);
	Serial.println(F("ledTj Parameter Values Table:"));
	Serial.println(F("LED\t\t\tSlope\t\t\tIntercept"));

	Serial.print(ledCL.label);
	Serial.print("\t\t\t");
	Serial.print(ledCL.slope, 6);
	Serial.print("\t\t");
	Serial.println(ledCL.intercept, 6);
	if ((ledCL.slope == 41.320174f) || (ledCL.intercept == -74.662063f)){
		CalCheck |= 1 << 0; //set a bit in CalCheck if this diode has not been calibrated
	}
	Serial.print(ledCR.label);
	Serial.print("\t\t\t");
	Serial.print(ledCR.slope, 6);
	Serial.print("\t\t");
	Serial.println(ledCR.intercept, 6);
	if ((ledCR.slope == 41.320174f) || (ledCR.intercept == -74.662063f)){
		CalCheck |= 1 << 1; //set a bit in CalCheck if this diode has not been calibrated
	}
	Serial.print(ledAM.label);
	Serial.print("\t\t\t");
	Serial.print(ledAM.slope, 6);
	Serial.print("\t\t");
	Serial.println(ledAM.intercept, 6);
	if ((ledAM.slope == 41.320174f) || (ledAM.intercept == -74.662063f)){
		CalCheck |= 1 << 2; //set a bit in CalCheck if this diode has not been calibrated
	}
	Serial.print(ledCT.label);
	Serial.print("\t\t\t");
	Serial.print(ledCT.slope, 6);
	Serial.print("\t\t");
	Serial.println(ledCT.intercept, 6);
	if ((ledCT.slope == 41.320174f) || (ledCT.intercept == -74.662063f)){
		CalCheck |= 1 << 3; //set a bit in CalCheck if this diode has not been calibrated
	}

-also its unbearable to read vs: (even on a 4K TV)

#include <EEPROM.h>
#include <Arduino.h>
struct led	{
	float	m = 0.0f;
	float	t = 0.0f;
	char	n[8];
};
led	LED[4];
uint16_t	address = 0;
// the setup function runs once when you press reset or power the board
void setup() {
	float	temp = 0.0f;
	int		offset;
	char	c;
	Serial.begin(9600);
	pinMode(3, INPUT_PULLUP);
	Serial.println("Struct Demo");
	Serial.print("iRef (V): ");
	EEPROM.get(address, temp);
	address += 8;
	Serial.println(temp,6);
	Serial.println("Led Parameter Table from EEPROM:");
	Serial.println("Name\t\t|\t\tSlope\t\t|\t\tIntercept");

	//get led parameter values from EEPROM
	for (int i = 0; i < 4; i++)
	{
		
		EEPROM.get(address, temp);
		delay(5);
		LED[i].m = temp;
		address += sizeof(temp);
		EEPROM.get(address, temp);
		delay(5);
		LED[i].t = temp;
		address += sizeof(temp);
		offset = address + 8;
		for (int j = address; j < offset; j++)
		{
			EEPROM.get(j, c);
			LED[i].n[j] = c;
		}
		address = offset;						//	address should be @ start of next "LED"
	}
	// Print led parameter values from SRAM
	for (int i = 0; i < 4; i++)
	{
		//	print name 1st
		for (int u = 0; u < 8; u++)
		{
			Serial.print(LED[i].n[u]);
		}
		//	now print other two parameters
		Serial.print("\t\t\t\t");
		Serial.print(LED[i].m,3);
		Serial.print("\t\t\t\t");
		Serial.println(LED[i].t,3);
		delay(5);
	}
	Serial.println("Struct Demo Complete. Dumping EEPROM:");

Which is much easier to read but when the sketch tries to display the values retrieved from EEPROM to the serial monitor (after line 53 in the second sketch), the printout is nothing like the first example. Perhaps I forgot how to count in my for loops? (the second sketch prints correctly but things are missing and out of order)
Do I have to do something goofy like enclose my structs in an .h file for the second sketch to work?
A tutorial online with c structures and some way of making it an array with pointers and renaming things after the & symbol using ->subVariable instead of .subVariable in assignments or Serial.print statements, but I can’t really get my head round it. Anyone know of a simpler way to use an array of custom struct(s)??
Thanks
Michael

Update:
Throughout the thread I discovered that the problem was a few too many/missing increments in my for loop to retrieve the values of an array of a custom structure of multiple LED parameters.
Whandall in post #27 suggested using EEPROM.get(address, LED); which will retrieve the whole array of structures from eeprom… in one line!
Just as simple to update the array of structures with EEPROM.put(address, LED);
Keep in mind the size of your EEPROM vs the size of your struct array, ie x = (sizeof(LED)) * array size;

struct ledTjParameters {
float slope; //slope number, diode speccific
float intercept; //also diode specific
char label[8]; // diode name -which one to the human world
};

ledTjParameters allLEDs = {
{41.320174f, -74.662063f, "LT Cree"},
{41.320174f, -74.662063f, "RT Cree"},
{41.320174f, -74.662063f, "Ambient"},
{41.320174f, -74.662063f, "Coolant"}
};

rinkrides:
Do I have to do something goofy like enclose my structs in an .h file for the second sketch to work?

I think you do.

The Arduino system makes assumptions to simplify things for newcomers and I think it can't get its head around structs unless they are in a .h file - which is not a big deal provided you know it is required.

By the way I am one of the least expert people here as far as the C/C++ language is concerned. I only use it under duress :slight_smile:

...R

How to avoid the quirks of the IDE sketch file pre-preprocessing

As usual, Nick, you have some very useful advice and have explored corners of the envelope that I hope never to see :slight_smile:

In this case, however, your solution seems to abandon rather than extend the "standard" Arduino way of doing things and IMHO would cause a lot of confusion for a newbie.

...R

Well …

Struct arrays not possible on arduino

Yes, struct arrays are possible. Arduino IDE uses C++ (or C++11 these days). So “not possible on Arduino” is an overreach.

I agree. However AFAIK you can use them without abandoning setup() and loop() and the other familiar bits.

...R

Nick showed how one MAY abandon setup() and loop(), not that one MUST abandon setup() and loop() to get the other advantages.

@Jaba - Elegant solution and it would likely work in a calibrate function that has a for loop.

@Nick - Thank you for the insightful link, I did not know that info existed. Arduino as a platform is a great idea, it got me into C/C++ programming much deeper than I thought possible, but it seems I was hitting this invisible wall every time I tried to stretch my reach further than the examples with any sort of clever coding most of the time. The Parallax Propeller was a great idea (more of an older programmers toy really), but you had to learn SPIN language (unless you already knew assembly -even PASM “Propeller Assembly Model”) seems a bit quirky, but all that is for another forum. They to realized the propeller would be a more attractive platform IF it would support C/C++, but I think it was wrong language from the start, & C support came too late.

But why all the quirks with Arduino’s pre processor? I get the Arduino scope on C programming, the functions make it easy to understand programming C which is a massive language which may not all be supported by an ATmega328. I’m glad the gurus are around to help guide others. I try as well in the electronics area when I have spare time as my hobbie background is more heavily on the Electronics Engineering side. Discrete is WAY faster than a processor but it has it’s place, as the processor is MUCH more flexible and easier/faster to reconfigure for different tasks.

vaj4088:
Nick showed how one MAY abandon setup() and loop(), not that one MUST abandon setup() and loop() to get the other advantages.

I am well aware of that. I was just trying to prompt him to generate a solution that uses setup() and loop() as it would probably be more suitable for a newbie :slight_smile:

...R

Thanks for that link. I’ve created projects with one “sketch” file (ino) and multiple .cpp files each with its own header file, building the H-files as one would in any C++ IDE:

#ifndef SOMETHING_H
#define SOMETHING_H

class Something {
   private:
      ...
   public:
      Something (...);
      void myMethod();
};

#endif

and then let the cpp-file contain the implementations, such as

#include "Something.h"

Something::Something(...) {...}
void Something::myMethod() {....}

Is it correctly understood that I must include all such header files as well as system header files such as <Wire.h> etc, in the ino file? I don’t think I have seen any problems not doing so, but that may have been a coincidence.

I always assumed that the makefile for Arduino projects would include all the libraries of the Arduino installation, since the linker will at any rate include code for those used only. Maybe I have a simplistic view of how Arduino IDE works?

:slight_smile:

rinkrides:
But why all the quirks with Arduino's pre processor?

They are trying to do two main things:

  • Generate automatic function prototypes for you, so you don't need to teach newbies what they are, or teach them to declare a function before calling it.
  • Avoid having to have complex "project" files which list the libraries you want in this particular sketch (eg. Wire, SPI, LCDdisplay, etc.). By having an #include line in the main sketch the IDE can deduce what libraries to include.

thought i had a *handle on this, guess there’s much more to it than I thought.

So i only would use the .ino as a library declaration file and move all the rest of the code to the .cpp file with a different name (which is also #included in the .ino)? That’s it?
The layout of the sketch can remain the same with the setup() and loop()?!

Making classes are only if your doing a library right?

One last question. Tried it your way with the .cpp file instead and things look right, compiler error saying that EEPROM.get(address, temp);

Is there something else I need to do to reference other libraries from within the .cpp?

Thank you for the guidance.

cppStyleErrors.txt (5.92 KB)

which is also #included in the .ino

No. Any .cpp files in the "project" directory (ie, the same folder as the .ino file) are automatically compiled. Don't #include them. In the IDE you achieve this by just making a new "tab" in the project.

Making classes are only if your doing a library right?

Make classes wherever you want to. A lot of libraries are made as classes to keep the namespace pollution low, but you can use classes in your own project.

It's not an arquino problem, it's a C++ problem. C++ these days has a syntax for initialising arrays with a constructor, but it's pretty appalling.

One last question. Tried it your way with the .cpp file instead and things look right, compiler error saying that EEPROM.get(address, temp);

'EEPROM' was not declared in this scope

Is there something else I need to do to reference other libraries from within the .cpp?

Dont include the .cpp file in the .ino, got it.
#include any libraries the .cpp uses in the .ino, check.
but still got complainer errors saying the library that i included in my .ino like normal is now "not declared" in this scope

rinkrides:
One last question. Tried it your way with the .cpp file instead and things look right, compiler error saying that EEPROM.get(address, temp);

'EEPROM' was not declared in this scope

Is there something else I need to do to reference other libraries from within the .cpp?

Dont include the .cpp file in the .ino, got it.
#include any libraries the .cpp uses in the .ino, check.
but still got complainer errors saying the library that i included in my .ino like normal is now "not declared" in this scope

You must also include the libraries that the cpp uses, in the cpp. :slight_smile:

You need to #include your library in two places:

  • The .ino file so that the corresponding .cpp file is compiled.
  • In the file where you actually use it (if this is different) so it knows the declarations for it.

. . . So the .ino & .cpp method work fine,can even use the int main() as normal C++ (i guess, beyond me ATM).

However, for whatever reasons (and probably a discussion for another thread), even using the .cpp method did not resolve the quirk in my sketch, even though it did compile and upload. The sketch seemed to just hang when it hit the first “for” loop. The first for loop runs 4 times, gathering all the values for the array of the custom struct. Went back to basics, unrolling the for loop that gathered the data from the internal EEPROM. Once the for loop was unrolled all the data was stored in SRAM correctly and printed correctly, which lead me to think this:

Something with pre-processor (maybe), a quirk inside the for loop model (more likely), or a bug within the EEPROM library(likely)

It seems that the for loop either floods the EEPROM library with requests and it simply cant keep up (EE read access ~3mS), and i had tried delay(5/10/25); but that had no effects on the for loop. this may be the case with other libraries if one tries to retrieve data in the manner of:

#include <EEPEOM.h>
 struct led  {
  float m = 0.0f;
  float t = 0.0f;
  char  n[8];
};
led LED[4];
uint16_t  address = 0;

void setup(){
 for(int i = 0; i <= 3; i++){
    EEPROM.get(address, temp);  // 1st float location
    LED[i].m = temp;
    address += sizeof(float);
    EEPROM.get(address, temp);  // 2nd float location
    LED[i].t = temp;
    address += sizeof(float);
    for(int j = 0; j < 9; j++){
      EEPROM.get(address, tmpChar);
      LED[i].n[j] = tmpChar;       //get 8 char's
      address++;
    }
  }
}
void loop(){
}

For reasons I don’t understand, the above compiles and uploads but “hangs” or I dunno, but it doesn’t produce anything else one the sketch hits that first for loop. But this works fine:

#include <EEPEOM.h>
struct led  {
 float m = 0.0f;
 float t = 0.0f;
 char  n[8];
};
led LED[4];
uint16_t  address = 0;

void setup(){
EEPROM.get(address, LED[a].m);
 address += sizeof(float);
 EEPROM.get(address, LED[a].t);
 address += sizeof(float);   //should be at 1st char
 EEPROM.get(address, LED[a].n[b]);
 b++;
 address++;
 EEPROM.get(address, LED[a].n[b]);
 b++;
 address++;
 EEPROM.get(address, LED[a].n[b]);
 b++;
 address++;
 EEPROM.get(address, LED[a].n[b]);
 b++;
 address++;
 EEPROM.get(address, LED[a].n[b]);
 b++;
 address++;
 EEPROM.get(address, LED[a].n[b]);
 b++;
 address++;
 EEPROM.get(address, LED[a].n[b]);
 b++;
 address++;
 EEPROM.get(address, LED[a].n[b]);

 b = 0;
 address++;
 a++;
}
void loop(){

}

^Producces this on the serial terminal:
Struct Demo
iRef (V): 1.087965
Led Parameter Table from EEPROM:
Name | Slope | Intercept

LT Cree 41.320 -74.662
RT Cree 41.320 -74.662
Ambient 41.320 -74.662
Coolant 41.320 -74.662
Struct Demo Complete.

It just raises further questions because the whole reason of the OP was to find a work around for the struct array of
led LED[3]; not working in either the .ino or .cpp methods. Fact is, at least with the for loop, an array of structures IS legal even with the pre-processor inside a .ino file. I think the for loop may have a bug (or overloaded?) where you cannot populate an array of struct from within the for loop, it just doesn’t work ie:

EEPROM.get(address, LED[a].n[b]);
                   OR
EEPROM.get(address, temp);
LED[a].n[b] = temp;

I guess nobody else had this problem with arduino. And am I then correct in assuming that the .cpp method would be more “powerful” way of C++ coding on the arduino? Or is it explicitly as your link suggested, so that the pre-processor won’t try to make prototypes of your “illegal” code?

I’ll deal with an extra 120 lines of code if that’s the way it must be, I don’t care about compact, I care about it working as it should. I would love to take a college course on C++ programming (the standard way), I simply don’t have the time right now. I need to get my prototype LED car headlights readied for torture testing as I’m planning on a kickstarter project path with these quite soon. It’s entirely my own design, auto FOG light and factory beam pattern, liquid cooled, universal design heat sink so 9006/HB3/HB1,ect… These headlight bulbs are for cars/trucks/motorcycles, are drop in replacements for the factory bulbs, controlled by Arduino (or ATmega’s at least running Arduino compiled code).
At least I’m past this programming hurdle, thanks to all here for the help and guidance, as always karma for the helpful:)

Michael