struct programming guidance

I need some help on how to go about this:
I have a Mega with an attached 4D systems uLCD-43PT screen. The screen has been programmed with a qwerty keyboard and a separate numeric keypad.

I want the programming and set up to do is have a user input a person's name and then their cell phone number... up to 10 separate names and associated numbers.

Now, I have it to where the screen will pass the keyboard/keypad values to the Mega over serial, and for the Mega to make sense of it, to a degree. Now each key press sends its value over one at a time.

The intent is to read each of these characters - lets start with the name - and build a string.

The number will then go in as a string as well, not an int, or long or double, etc.

Since I want to be able to enter and store up to 10 individual collections of names and numbers I'm leaning more towards using struct's than just multi-dimensional arrays.

Reading the keys into something like String name; or String name; is one thing

however, my grasp of using structures is about novice... so there will be a learning curve here.
What I was thinking would look something like:

struct peer {
int record;
String name;
String number;
};

typedef struct peer Peer;

Peer aPeer;
aPeer.record = 1;
aPeer.name = 'Some Name';
aPeer.number = '1234567890';

Hello,

There is a good tutorial about structs here: Data structures - C++ Tutorials

Also I suggest that you use char arrays instead of Strings, and by the way strings must be inside double quotes: "string", not 'string'

dtmacdonald76,
What is your final goal with the data? SQLITE? Cloud storage? JSON?

Jesse

guix - yes, character arrays would be better, thank you!
Jesse - this data is just intended to be stored locally on the device, either in EEPROM or in the form of a text file on an SD card. It will be displayed in on menu screens and the data will be used elsewhere within the program to send notifications when a waypoint is reached as well as distance and direction to the next one. I'll be using the TinyGPS library for a lot of that work.

dtmacdonald76:
Reading the keys into something like String name; or String name; is one thing

however, my grasp of using structures is about novice… so there will be a learning curve here.
What I was thinking would look something like:

Have you considered going straight to a C++ class? Your problem is pretty much, what the class was invented for. The learning curve is slightly longer than a struct but the resulting programme should be easier to read, maintain and extend.

Whether you use a struct or a class, you don’t know what length name and number are at compile time. Either, you have to embed a fixed length array in the struct, or allocate the memory at run time, then remember to free it when you are finished. Using a class, you can automate dynamic memory allocations, fairly easily.

//a smart struct
class Record {
  //by using the public access declaration and being careful,
  // we can avoid using RAM for get/set method pointers 
  public:
    uint8_t id;
    char* name;
    char* number;
    
  //constructor and destructor
  // dynamically allocate memory at run time
    Record(uint8_t recordID, char* aName, char* aNumber);
    ~Record(void);
};

//constructor, called with the 'new' keyword
Record::Record(uint8_t recordID, char* aName, char* aNumber) {
  id = recordID;

  //allocate memory for the string
  // remember to +1 for the zero terminator
  uint8_t length = strlen(aName);
  name = (char*) malloc(length +1);
  if(name != NULL) { 
    strcpy(name, aName);
  }
  else {
	  //out of memory
  }

  length = strlen(aNumber);
  number = (char*) malloc(length +1);
  if(number != NULL) {
    strcpy(number, aNumber);
  }
  else { 
	  //out of memory
  }
}
    
//destructor, called with the 'delete' keyword
Record::~Record(void) {
  //good practice,
  // free the last allocation first
  // helps avoid heap fragmentation
  if(number != NULL) {
    free(number);
  }
  
  if(name != NULL) {
    free(name);
  }
}

//--- globals ---

//a container for 10 records
const uint8_t MAX_RECORDS = 10;
Record* records[MAX_RECORDS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
uint8_t recordCount = 0;   			//number of records in the array

Record* thisRecord;         	//an anchor for the current record

char nameBuffer[32];     		//fixed input buffer for names
char numberBuffer[16];  		//fixed input buffer for numbers

char stringBuffer[80];  //some space for output strings

void setup(void) {
  Serial.begin(9600);
}

void loop(void) {
        
	for(uint8_t i = 0; i < MAX_RECORDS; i++) {
		//gather input into the input buffers...
		
		sprintf(stringBuffer, "create record %d\r\n", i);
                Serial.print(stringBuffer);
                
		sprintf(nameBuffer, "John Smith%d", i);
		sprintf(numberBuffer, "012345678%d", i);
		 
		//add the new record
		thisRecord = new Record(recordCount++, nameBuffer, numberBuffer);
		records[thisRecord->id] = thisRecord;
	}
	
	//iterrate the records
	for(uint8_t i = 0; i < MAX_RECORDS; i++) {
		thisRecord = records[i];
		sprintf(stringBuffer, "id %d\tName:%s\tNumber:%s\r\n", 
                           thisRecord->id, thisRecord->name, thisRecord->number);

                Serial.print(stringBuffer);
	}
	
	sprintf(stringBuffer, "recordCount = %d\r\n", recordCount);
	Serial.print(stringBuffer);

	//remove records
	do{
		thisRecord = records[--recordCount];
		sprintf(stringBuffer, "Delete record %d\r\n", thisRecord->id);
                Serial.print(stringBuffer);
		records[thisRecord->id] = NULL;
		delete thisRecord;
	} while(recordCount);  
}

Wow, MattS, I think that might just fit the bill, thank you so much! I’m still just parley passing from the “novice” into the “intermediate” category when it comes to programming and I would have NEVER gotten anything even resembling your code on my own!

That being said, I have come across a new, more basic issue…two actually…grrr (isn’t that they way these things always go?):

  1. I have it worked out where reading the key pressed on the qwerty keyboard on the uLCD-43PT equates to the proper letter, both upper and lower, including “space”, fundamentally I’m stuck on writing those char values into a peerName char array.

char letter; //this is the genieArduino translated qwerty keypressed numerical values into
// their matching letters
char peerName[13]; // the char array, 12 characters long (how I set up the display…space was
// limited) the genieArduino function is defined as
// genieWriteStr(unit_16t, char*), thus char* will call peerName;

I set up a basic function:
void buildPeerNames(){
for( int i = 0; i < 13; i++){
peerName = letter;

  • }*
    then under the genieEventHandler() when I use genieWriteStr(27, peerName); whatever letter I type fills all 12 available spaces of array and displays in the ‘string’ box. So, if I type ‘q’, I get:
    “qqqqqqqqqqqq”.
    This is wrong for obvious reasons. Now, in C I I know how to use printf and scanf to manually fill an array element by element, but translating that into the Arduino environment using the genie libraries is getting my a bit frustrated.
    The other problem I think is power to the screen, or (please dear God don’t be) a memory leak with my Ardy. The more I fiddle with the coding and upload to the board, the less reliable the screen becomes about staying on when powered from the arduino itself. had this issue when I was attempting to use a Due before reverting back to the Mega. So I’ll try to separate the power as much as possible through a breadboard and see if that helps… So much more to go in the project that basically hinges on figuring out this one procedure.

dtmacdonald76:
::::SNIP::::
Jesse - this data is just intended to be stored locally on the device, either in EEPROM or in the form of a text file on an SD card. It will be displayed in on menu screens and the data will be used elsewhere within the program to send notifications when a waypoint is reached as well as distance and direction to the next one. I’ll be using the TinyGPS library for a lot of that work.

Okay dtmacdonald76,
I looked up TinyGPS which is based on NMEA GPS, which Wikipedia tells me it is a type of CSV.

I’m not convince you want to use the C++ library for two reasons:

  1. tinyGPS is functional, not object-oriented. True is has some elements of OO, such as <<, but it remains functional, so the program flow might be overwhelming as it goes back and forth.

  2. you’ve got to learn alot of C++

I’m not saying don’t use C++. That is your decision. However, you are going to have your hands full with the GPS and storage (where ever you end up putting the data).

My advice: plan your hurdles early, and decide how much time you need for each hurdle, and decide how much time you need to back out (if you don’t make each time hurdle).

I’m also not convinced that a Struct is the way to go. My thinking is that your going to be handling data so fast, the best thing to do is to put it directly into tinyGPS, and only save every 10th or 12th reading for the user interface.

Those are my thoughts. If you see other factors, this might be a good time to hash them out. There seem to be plenty of opinions.

Jesse

dtmacdonald76:
Wow, MattS, I think that might just fit the bill, thank you so much! I'm still just parley passing from the "novice" into the "intermediate" category when it comes to programming and I would have NEVER gotten anything even resembling your code on my own!

No problem and thanks. There is nothing too difficult about my example but yes, if you don't know the answer, you don't know the answer (!)

As I said to someone else recently. Programming in C/C++ is mainly a matter of learning how to break your problems down. If you are struggling with something you think should be simple, it is almost certainly the case, you have failed to break your problem down into small enough pieces. Step away from the keyboard, pick up a pencil and some paper, try it the old fashioned way, by drawing a flow chart or writing out the pseudo code. It will often, very quickly, show where you are assuming the computer is doing something it isn't. When you have broken the problem down into small enough pieces, the code to solve those much smaller problems, should almost write itself.

That being said, I have come across a new, more basic issue...two actually...grrr (isn't that they way these things always go?):

See paragraph above :wink:

...This is wrong for obvious reasons. Now, in C I I know how to use printf and scanf to manually fill an array element by element, but translating that into the Arduino environment using the genie libraries is getting my a bit frustrated.

Think about what scanf is doing for you.

Read a key, append the input to a temporary array (we need an index for that), detect the terminator character (we need a terminating character), iterate the format array and expand the input array, into the output array (we need to copy to the output array), returning the output array.

You need to do all but one of these things too. Luckily, you don't need to expand the input array, which is the difficult bit. Additionally, you also need to limit the length of the input but that's easy enough, once we have broken the problem down and realised we need an index value for appending to the input array, (lets call it the tail of the array,) which can be compared to a constant maximum and reset to zero. By now, you probably already know, a feature of C is strings must be terminated by a a zero (,so let's not forget to do that too). Check those pesky, zero based, C array boundaries.

Voila! We have ourselves a simple input buffer. Now I am looking at the code, I can see using a constant within the function, is not entirely necessary. Losing the constant would make the function general purpose, but I will leave you to figure that out.

const char KEY_ENTER = 0x0D;   //the code sent by the device when the enter key is typed

const uint8_t MAX_INPUT = 13;
char peerName[MAX_INPUT] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

char* buildInputName(char* targetString, char letter) { 
  //An input buffer
  static char inputBuffer[MAX_INPUT];
  static uint8_t inputTail = 0;
  
  if(letter != KEY_ENTER) {
    //append to the buffer
    inputBuffer[inputTail++] = letter;
  }
  else {
    //enter key, terminate the input string and copy to the target  
    inputBuffer[inputTail++] = 0;
    strcpy(targetString, inputBuffer);

    //reset the input buffer
    inputTail = 0;
    inputBuffer[inputTail] = 0;
  }
  
  if(inputTail >= MAX_INPUT) {
    //no more space in buffer
    inputTail--;
  }

  return targetString;
}

The other problem I think is power to the screen,

My advice. Don't guess. It's a waste of your time and often, whatever it is you least understand, erroneously gets the finger of blame. Get yourself a multimeter, preferably one which goes down to micro-amps and records maximum readings. Measure.

or (please dear God don't be) a memory leak with my Ardy.

The way to avoid memory leaks is to write test harness code for your functions, and test incrementally and often.

So much more to go in the project that basically hinges on figuring out this one procedure.

Well you have certainly picked yourself a challenging project. Keep your questions pointed and I am sure the community will help you out.

Thanks for the help! I'll give those suggestions a go and see where it ends up!