Weird Problem With Keypad and Struct [Solved]

I'm writing a sketch that takes the input from a 4x4 keypad and a potentiometer, and stores it in a Struct that it sends to other Arduinos using an nRF24L01. The number keys toggle between two states, so I've stored the current state of each key in a bool array. There is a second array that stores settings when *[Number] is pressed. The letter keys A-D are four options for a single setting, and are stored as a char. Speed and direction for driving a motor are stored as bytes.

There are 8 settings on the number keys, and 4 settings on *[Number], which is the size of the relevant array.

One of these arrays works as it should, but the other stores what appear to be random numbers in the last element, and also the subsequent elements that haven't been declared (I hope that makes sense!). I've tried everything I can think of to trace this problem. If I remove the affected array, it affects the other one. If I change the size of the array, the problem still affects the last element.

This is the relevant code, that I've stripped out everything else from:

//*******KEYPAD*******
#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
//define the cymbols on the buttons of the keypads
char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {6, 7, 8 ,9}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {2, 3, 4, 5}; //connect to the column pinouts of the keypad

//initialize an instance of class NewKeypad
Keypad keys = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);
bool keyAlt = false;
byte keyNo;

//*******DATA STRUCTURE*******
struct Tx_Data {
  byte speed;
  byte direction;
  char fyTrack;
  bool points[8]; //Set to number of points (8 points = points[8])
  bool sections[4]; //Set to number of sections (4 sections = sections[4])
};
struct Tx_Data controlData;
byte maxPoints = sizeof(controlData.points);
byte maxSections = sizeof(controlData.sections);

void setup() {
  Serial.begin(9600);
  Serial.print(F("Max Points: "));
  Serial.println(maxPoints);
  Serial.print(F("Max Sections: "));
  Serial.println(maxSections);
}

void loop() {
  //*******READ KEYPAD*******
  char keyPressed = keys.getKey();

  if (keyPressed) {
    Serial.print(keyPressed);
    Serial.print("(");
    Serial.print(byte(keyPressed));
    Serial.print(")");

    //Letter keys
    if (keyPressed == 'A' || keyPressed == 'B' || keyPressed == 'C' || keyPressed == 'D') {
      controlData.fyTrack = char(keyPressed);
    } else {
      //Number keys
      keyNo = byte(keyPressed) - 48;
      if (keyPressed == '*') {
        keyAlt = true;
      } else {
        if (keyAlt == true) {
          if (keyNo <= maxSections) {
            controlData.sections[keyNo] = !controlData.sections[keyNo];
          }
        } else {
          if (keyNo <= maxPoints) {
            controlData.points[keyNo] = !controlData.points[keyNo];
          }
        }
        keyAlt = false;
      }
    }

    Serial.print(F(" FY:"));
    Serial.print(controlData.fyTrack);

    Serial.print(F("  S3:"));
    Serial.print(controlData.sections[3]);
    Serial.print(F("  S4:"));
    Serial.print(controlData.sections[4]);
    Serial.print(F("  S5:"));
    Serial.print(controlData.sections[4]);

    Serial.print(F("   P7:"));
    Serial.print(controlData.points[7]);
    Serial.print(F(" P8:"));
    Serial.print(controlData.points[8]);
    Serial.print(F(" P9:"));
    Serial.println(controlData.points[9]);

  } //End of keyPressed()

}

This is what appears on the serial monitor:

Max Points: 8
Max Sections: 4
A(65) FY:A S3:0 S4:176 S5:176 P7:0 P8:0 P9:0
B(66) FY:B S3:0 S4:106 S5:106 P7:0 P8:0 P9:0
7(55) FY:B S3:0 S4:166 S5:166 P7:1 P8:0 P9:0
7(55) FY:B S3:0 S4:29 S5:29 P7:0 P8:0 P9:0
8(56) FY:B S3:0 S4:236 S5:236 P7:0 P8:1 P9:0
8(56) FY:B S3:0 S4:106 S5:106 P7:0 P8:0 P9:0
*(42) FY:B S3:0 S4:186 S5:186 P7:0 P8:0 P9:0
3(51) FY:B S3:1 S4:13 S5:13 P7:0 P8:0 P9:0
*(42) FY:B S3:1 S4:188 S5:188 P7:0 P8:0 P9:0
3(51) FY:B S3:0 S4:107 S5:107 P7:0 P8:0 P9:0

Note that S4 and S5 display an apparently random number that changes each time the Struct is updated. S5 and P9 don't actually exist, but S5 displays the same number!

I've tried this on a different make of Uno with just the keypad connected, and it makes no difference.

I'm totally baffled by this, and can't think of anything else to try. Does anyone have an answer?

Suggest you initialize your variables to zero at the onset.

      controlData.fyTrack = char(keyPressed);

Why do you think it is necessary to convert a char to a char to store it in a char?

     keyNo = byte(keyPressed) - 48;

No.

     keyNo = byte(keyPressed) - '0';

Yes.

   Serial.print(F("  S4:"));
    Serial.print(controlData.sections[4]);
    Serial.print(F("  S5:"));
    Serial.print(controlData.sections[4]);

The sections array only has 4 elements, numbered 0 to 3.

PaulS:

      controlData.fyTrack = char(keyPressed);

Why do you think it is necessary to convert a char to a char to store it in a char?

That was tried is desperation, and I forgot to change it back when it didn't make any difference :smiley: . I knew it didn't make sense!

PaulS:

     keyNo = byte(keyPressed) - 48;

No.

     keyNo = byte(keyPressed) - '0';

Yes.

I don't understand the reason for this. Either way seems to produce the same result.

PaulS:

   Serial.print(F("  S4:"));

Serial.print(controlData.sections[4]);
   Serial.print(F("  S5:"));
   Serial.print(controlData.sections[4]);



The sections array only has 4 elements, numbered 0 to 3.

I'm an idiot! I've spent hours trying to find the problem, and this fixed it! My excuse is that I haven't needed to use arrays before in anything I've written for Arduinos. I was probably thinking of some other language I've used in the past, but don't know which one!!!!

Anyway, thank you very much :).

I've changed the declarations to the following:

struct Tx_Data {
  byte speed;
  byte direction;
  char fyTrack;
  bool points[9]; //Set to number of points + 1 (8 points = points[9])
  bool sections[5]; //Set to number of sections + 1 (4 sections = sections[5])
};
struct Tx_Data controlData;
byte maxPoints = sizeof(controlData.points) - 1;
byte maxSections = sizeof(controlData.sections) - 1;

It makes it clearer if I keep the button number the same as the array element, and I want to be able to use 0 to send a reset instruction to change everything back to default.

I don't understand the reason for this. Either way seems to produce the same result.

Of course they do, because the ASCII value of '0' is 48. But, the value of '0' is more intuitive than the value 48, in some cases. Not so much here, but suppose that you are writing code to receive serial data to control a robot, and the user should send 'S' to stop the robot.

The user looks at the code, to figure out what to send, and sees:

if(serialVal == 83)
{
   Stop();
}

Or, the user sees

if(serialVal == 'S')
{
   Stop();
}

Which one will make the user turn to the Serial Monitor app and start sending keystrokes, and which will cause the user to curse the idiot that made figuring out what to send so complicated?