Lookup table usage guidance

Hi,
My project is to build an efficient user interface for a DSP guitar amp modelling system.
The system can be controlled via MIDI but the program change codes look nothing like the memory location names the user sees. I.e., preset E3 = program change 18, preset V4 = program change 87.
Using an A to Z and 1 to 4 keypad will allow the me to directly enter the desired memory location.
I have a table of appropriate values for each character that will generate unique values for the program change messages. What I don't have is a way read the associated values from the table.
I've read post after post about arrays and lookup tables but none of them explain the nuts and bolts of the code to access the values.
I need to know how to get the value 16 when the E key is pressed and the value 2 when the 3 key is pressed.
Attached is my code that at least reliably returns the keypad characters.
Thank you for any and all guidance you may be able to provide.
Mark

// rig_pad_4x4_C
// mchambers 12/1/22
// trying to get lookup table working
// can print keypad characters, but not their value from the array

#include "Keypad.h"

// Constants won't change:
const byte ROWS = 4; // number of rows
const byte COLS = 4; // number of columns
char keys[ROWS][COLS] = {
  {'A', 'B', 'C', 'D'},
  {'E', 'F', 'G', 'H'},
  {'I', 'J', 'K', 'L'},
  {'1', '2', '3', '4'}
};

struct LUT {
  char Key;
  int Val;
} keyLUT[30] = {
  {"A", 0},
  {"B", 4},
  {"C", 8},
  {"D", 12},
  {"E", 16},
  {"F", 20},
  {"G", 24},
  {"H", 28},
  {"I", 32},
  {"J", 36},
  {"K", 40},
  {"L", 44},
  {"M", 48},
  {"N", 52},
  {"O", 56},
  {"P", 60},
  {"Q", 64},
  {"R", 68},
  {"S", 72},
  {"T", 76},
  {"U", 80},
  {"V", 84},
  {"W", 88},
  {"X", 92},
  {"Y", 96},
  {"Z", 100},
  {"1", 0},
  {"2", 1},
  {"3", 2},
  {"4", 3}
};

// Variables will change:
char alphaKey = 0;  // 1st rig character
int numKey = 0;    // 2nd rig character
// using 128 as current PC could be 0
int lastPC = 128;   // previous MIDI program change value
int currentPC = 128; // current MIDI program change value
int alphaVal = 128;
int numVal = 128;

byte rowPins[ROWS] = {A0, A1, A2, A3};  // row pinouts of the keypad
byte colPins[COLS] = {2, 3, 4, 5};      // column pinouts of the keypad
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

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

void loop()
{
  char alphaKey = keypad.getKey();
  if (alphaKey != NO_KEY)
    Serial.println(alphaKey);
//  for (int i = 0; i < 30; i++) {
//    if (alphaKey == keyLUT[i].Key) {
//      Serial.println(keyLUT[i].Val);
//      alphaVal = (keyLUT[i].Val);
    }
//  }
//  int numKey = keypad.getKey();
//  if (numKey != NO_KEY)
//    Serial.println(numKey);
//  for (int i = 0; i < 30; i++) {
//    if (numKey == keyLUT[i].Key) {
//      Serial.println(keyLUT[i].Val);
//      numVal = (keyLUT[i].Val);
//    }
//  }
  // need to get lookup table working first
  //  int currentPC = alphaVal + numVal;
  //  if (currentPC != lastPC) {
  //    Serial.println(currentPC);
//}

First: in your struct the characters should be surrounded by ' ' not " ".
Or you should reserve space for the '\0' character.

Second:
Why a lookup table?
You can do:

y = (int(x)-int('A'))*4;
if (int(x)<35){
   y=int(x)-int('1');
}

Here you use the utf8 value that represent the characters (asci values are often the same).

Something else to consider, the keys array used for the keypad does not need to have actual ASCII characters, you can use any value that will fit in a char (a signed 8-bit integer), except for zero, because zero is used by the library to indicate that no key has been pressed. With the values you are using, set each key value to 1 more than the number you have in the keyLUT array, then whenever a key is pressed just subtract 1 to get the value you need.

1 Like

No need for a lookup table where a little math will do.

void loop()
{
  static char alphaKey = 'A';
  static char digitKey = '1';

  bool presetChanged = false;

  char key = keypad.getKey();
  if (key >= 'A' && key <= 'Z')
  {
    if (key != alphaKey)
      presetChanged = true;
    alphaKey = key;
  }

  if (key >= '1' && key <= '4')
  {
    if (key != digitKey)
        presetChanged = true;
    digitKey = key;
  }

  if (presetChanged)
  {
    Serial.print(alphaKey);
    Serial.println(digitKey);
    unsigned preset = (alphaKey - 'A') * 4 + (digitKey - '1');
    // Sent the MIDI command to set the new preset

    // Debug
    Serial.print("New preset number: ");
    Serial.println(preset);
  }

Thank you all for your help.
Now I need to try out the new methods you've proposed.

david_2018,

Can you point me to an example of the proper syntax for doing what you suggest?
When I tried it, using my admittedly beginner coding skills, I got a bunch of error messages as shown below.

// rig_pad_4x4_D
// mchambers 12/24/22
// putting desired values into the keypad CHARs instead of using a lookup table
// doesn't work; only returns second digit of 2 digit chars
// if I use " instead of ' (per Arduino reference) I get a bunch of
// "too many initializers for 'char [4]" messages

#include "Keypad.h"

// Constants won't change
const byte ROWS = 4; // number of rows
const byte COLS = 4; // number of columns
char keys[ROWS][COLS] = { // char* instead of char ?
  "1", "5", "9", "13",
  "17", "21", "25", "29",
  "33", "37", "41", "45",
  "1", "2", "3", "4"
};
byte rowPins[ROWS] = {A0, A1, A2, A3};  // row pinouts of the keypad
byte colPins[COLS] = {2, 3, 4, 5};      // column pinouts of the keypad
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

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

void loop()
{
  char alphaKey = keypad.getKey();
  if (alphaKey != NO_KEY)
    Serial.println(alphaKey);
}

Thanks

johnwasser,

I have the expanded version of your code working nicely; thank you again for your help.

// rig_pad_8x4_A
// mchambers 1/2/23
// based on John Wasser's code 12/5/22
// expanded keypad to 8x4
// It works, but I want it to only send the new preset when
// both alpha and digit keys have been pressed, i.e., direct entry change

#include "Keypad.h"
#include <MIDI.h>

MIDI_CREATE_DEFAULT_INSTANCE();

// Constants won't change
int channel = 1;    // midi channel
const byte ROWS = 8; // number of rows
const byte COLS = 4; // number of columns
char keys[ROWS][COLS] = {
  {'A', 'B', 'C', 'D'},
  {'E', 'F', 'G', 'H'},
  {'I', 'J', 'K', 'L'},
  {'M', 'N', 'O', 'P'},
  {'Q', 'R', 'S', 'T'},
  {'U', 'V', 'W', 'X'},
  {'Y', 'Z', '#', '*'},
  {'1', '2', '3', '4'}
};

byte rowPins[ROWS] = {9, 8, 7, 6, 5, 4, 3, 2};
byte colPins[COLS] = {A0, A1, A2, A3};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

void setup()
{
  //  Serial.begin(9600); // for debug
  MIDI.begin(MIDI_CHANNEL_OMNI); // listen on all channels
}

void loop()
{
  static char alphaKey = 'Z';
  static char digitKey = '3';

  bool presetChanged = false;

  char key = keypad.getKey();
  if (key >= 'A' && key <= 'Z')
  {
    if (key != alphaKey)
      presetChanged = true;
    alphaKey = key;
  }

  if (key >= '1' && key <= '4')
  {
    if (key != digitKey)
      presetChanged = true;
    digitKey = key;
  }

  if (presetChanged)
  {
    //    Serial.print(alphaKey); // for debug
    //    Serial.println(digitKey); // for debug
    unsigned preset = (alphaKey - 'A') * 4 + (digitKey - '1');

    //    Serial.print("New preset number: "); // for debug
    //    Serial.println(preset); // for debug

    // Send the MIDI command to set the new preset
    MIDI.sendProgramChange(preset, channel);
  }
}

I would like to modify the sketch so that it waits for the digit key to be pressed before sending the MIDI program change command. I suspect that a key-press counter anded with the presetChange state will do that.
To that end, I'm wondering if you can suggest a course of study that will help me improve my Arduino coding skills; i.e., help me learn to fish vs handing me the fish.

Mark

The rows have to be embraced, it's a 2D initialiser.

And you want characters. Single characters 'X' and so forth.

a7

So when a digit is entered, send the new preset if either the letter or digit has changed.

void loop() {
  static char oldAlphaKey = ' ';
  static char oldDigitKey = ' ';

  static char newAlphaKey = ' ';
  static char newDigitKey = ' ';

  char key = keypad.getKey();

  if (key >= 'A' && key <= 'Z') {
    newAlphaKey = key;
  }

  if (key >= '1' && key <= '4') {
    newDigitKey = key;

    // Has the preset changed?
    if ((newAlphaKey != oldAlphaKey) || (newDigitKey != oldDigitKey)) {
      oldAlphaKey = newAlphaKey;
      oldDigitKey = newDigitKey;

      //    Serial.print(alphaKey); // for debug
      //    Serial.println(digitKey); // for debug
      unsigned preset = (newAlphaKey - 'A') * 4 + (newDigitKey - '1');

      //    Serial.print("New preset number: "); // for debug
      //    Serial.println(preset); // for debug

      // Send the MIDI command to set the new preset
      MIDI.sendProgramChange(preset, channel);
    }
  }
}

The code works great, thank you.

What makes the sketch save only the AlphaKey for reuse?
I.e., when keys A and 3 are pressed and then 4 is pressed, the next higher preset is sent.
But when keys A and 3 are pressed and then B is pressed, nothing is sent.

The code. Put your finger on the code and trace it as you read every line.

You only save when you send, and you only send when the key is a digit between 1 and 4.

You've got your { braces } in the wrong place a few times.

Uncomment some of the serial printing and/or add more and r7n the code until you see how it is flowing. If that flow is not what you want, rewrite what the if statements are controlling.

a7

You said:

Do you want it to wait for the digit key or NOT wait for the digit key?!?

I was asking a question about how the code works, not complaining about how it works.

Thank you again for your help.

This part:

  if (key >= '1' && key <= '4') {
    newDigitKey = key;

    // Has the preset changed?
    if ((newAlphaKey != oldAlphaKey) || (newDigitKey != oldDigitKey)) {

The "Send a preset if either part has changed." is part of the "Is the key a digit?" because you said it should wait for a digit before sending.

It does remember the digit so if you press A 3 3 it will only send the preset "A3" once.

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