Ardu? No!MTobjectsButtons setMTanalogButtons ≫ Apprenticeship for MTanalogButtons

Apprenticeship for MTanalogButtons
Event management for UNO, Nano, Mega

When using MTanalogButtons, you must provide a table of thresholds values that allow you to differentiate between combinations. The program which follows allows you to almost automatically give this table by giving a test program. It also provides information on the quality analog readings.

Configuration

You must of course adapt this program to your assembly, and possibly change the analog pin used, the reference voltage of the converter and console speed. This is done in the first lines of the program.

Apprenticeship program

// This program uses apprenticeship to generate a demo program
// for MTanalogButtons
//
// You need to change a few constants to configure it.
//
// You must give the pin for analog reading
#define PIN A0 // Change A5 to A0..A7 depending on the case
//
// You must choose the voltage reference to use for the conversion
// DEFAULT: for Vcc
// INTERNAL: 1.1V for Nano/Uno
// INTERNAL1V1: 1.1V for Mega
// INTERNAL2V56: 2.56V for Mega
// EXTERNAL: to use AREF
#define REFERENCE DEFAULT
//
// You must give the console transfer rate
#define BAUDS 115200 // 115200 recommended, but you can put 9600
// Don’t forget to set the console to 115200 baud



// *************************** Memorization of ADC values for all buttons
byte reading[128]; // Will contain the possible ADC values for all buttons 

inline void write(word value) // Records that we saw the value passed as an argument (0 to 1023)
{ 
  reading[value/8] = bitSet(reading[value/8], value%8);
}

byte read(word value) // true if the ADC had this value
{ 
  return bitRead(reading[value/8], value%8);
}


// *************************** Global variables
boolean rest; // true for 1023, false for 0 when no button is pressed
word readValue, // Conversion result then key number 
     oldReadValue, oldOldReadValue, // Storage to allow three consecutive identical readings 
     startTime; // To wait as long as a key is pressed; word is enough because we will never exceed 3000
int startIndex = 0, endIndex = 0; // To explore the table and read the limit values
byte nbButtons = 0;
boolean notFirstComma = false;



void setup()
{ 
  // *************************** Initializations 
  Serial.begin(BAUDS); // Also set the console to BAUDS bauds! 
  analogReference(REFERENCE); // Allows you to choose the voltage reference 
  Serial.println(F("/*\nPress all keys, one by one\n")); 
  rest = (analogRead(PIN) > 512); // At rest we have 0 or 1023 

  // *************************** We wait for a key to be pressed 
  do 
  { 
    oldReadValue = readValue; // Saving the previous ADC measurement 
    readValue = analogRead(PIN); // New reading 
  } while ((readValue!=oldReadValue) || // Two identical values must be read (double reading for safety) 
    (rest && (readValue > 1020)) || (!rest && (readValue < 3))); // And we are away from rest 

  // *************************** Search for possible ADC values 
  do // As long as keys are pressed 
  { 
    do // As long as we do not read a good ADC value 
    { 
      oldOldReadValue = oldReadValue; // Saving the previous ADC measurement
      oldReadValue = readValue;
      readValue = analogRead(PIN); // New reading
    } while ((readValue!=oldReadValue) || (readValue!=oldOldReadValue)); // Start again until we have two identical readings
    write(readValue); // We find a value, we save it 

    // Timer management, we exit the loop if we no longer press a key for 3s 
    if (rest) // Depending on whether we have 0 or 1023 without supports 
    { 
      if (analogRead(PIN) < 1020) startTime = millis(); // Reset the timer as long as you press 
    } 
    else if (analogRead(PIN) > 5) startTime = millis(); 
  } while ((word)millis() - startTime < 3000); 


  // *************************** Display of the values read for control 
  // 1 indicates a value seen, 0 a value that ADC does not give 
  for (word line=0; line<16; line++) // 1023 possible values displayed with 16 lines of 64 values  
  { 
    if (line == 0) Serial.print("  0"); // Number of the line displayed on the left 
    else if (line == 1) Serial.print(" 64"); 
    else Serial.print(line*64); 
    Serial.print(" "); // To align the table 
    for (word column=0; column<64; column++) Serial.print(read(line*64+column)); // Values 
    Serial.println(); // End of line 
  } 
  Serial.println(); 


  // *************************** We count the number of buttons. 
  // This is the number of intervals in the table that contain only 0s. 1s indicate a button 
  while (startIndex < 1024) 
  { 
    while (!read(startIndex)) startIndex++; // Go on the first value 1 
    while (read(startIndex) && (startIndex < 1024)) startIndex++; // Go on the first 0 following 
    nbButtons++; 
  } 


  // *************************** Display a number of buttons on the console 
  Serial.print(F("\nI saw ")); 
  Serial.print(--nbButtons); 
  Serial.print(F(" buttons\n*/\n\n")); 


  // *************************** Display the test program 
  // Departure comments, common start 
  Serial.print(F("// This program tests the reading of the set of buttons with MTanalogButtons\n\n" 
  "#include <MTobjects.h> // V1.1.1 See http://arduino.dansetrad.fr/en/MTobjects\n\n" 
  "const byte PIN = ")); 
  Serial.print(PIN); 
  Serial.print(F(";\nconst word THRESHOLDS_TABLE[] = {")); 

  // You must give the table of values 
  // The values are in descending order if at rest we have CAN=1023 
  if (rest) 
  { 
    startIndex = 1023; 
    while (!read(startIndex)) startIndex--; // Go on the last value 
    while (read(startIndex) && (startIndex >= 0)) startIndex--; // Go before the last value 
    while ((startIndex >= 0) && (endIndex >= 0)) 
    { 
      endIndex = startIndex; // We are looking for an interval without a button. It ends at startIndex 
      while (!read(endIndex) && (endIndex >= 0)) endIndex--; // The zone starts at endIndex + 1 
      if (endIndex >= 0) // This is an interval without buttons 
      { 
        if (notFirstComma) Serial.print(F(", ")); // The first time we do not use commas 
        notFirstComma = true; // The other times we will put it 
        Serial.print((startIndex + endIndex + 1)/2); // And we put the middle of the interval 
      } 
      startIndex = endIndex; 
      while (read(startIndex) && (startIndex >= 0)) startIndex--; // Go before the button 
    } 
    Serial.print(F(", 0")); 
  } 
  // The values are in ascending order if at rest we have CAN=0 
  else 
  { 
    startIndex = 0; 
    while (!read(startIndex)) startIndex++; // Go on the first value 
    while (read(startIndex) && (startIndex < 1024)) startIndex++; // Go after the first value 
    while ((startIndex < 1024) && (endIndex < 1024)) 
    { 
      endIndex = startIndex; // We are looking for an interval without a button. It starts at startIndex 
      while (!read(endIndex) && (endIndex < 1024)) endIndex++; // The zone ends at endIndex - 1 
      if (endIndex < 1024) // This is an interval without buttons 
      { 
        if (notFirstComma) Serial.print(F(", ")); // The first time we do not use commas 
        notFirstComma = true; // The other times we will put it 
        Serial.print((startIndex + endIndex - 1)/2); // And we put the middle of the interval 
      } 
      startIndex = endIndex; 
      while (read(startIndex) && (startIndex < 1024)) startIndex++; // We return after the button 
    } 
    Serial.print(F(", 1024")); 
  } 

  // Continuation of the program 
  Serial.print(F("}; // Comparison threshold values\n\n" 

  "void press(byte key)\n" 
    "{\n" 
    " Serial.print(\"Key number \");\n" 
    "Serial.print(key);\n" 
    "Serial.print(\" just pressed \");\n" 
    "}\n" 

    "void release(void)\n" 
    "{\n" 
    " Serial.println(\"and released\");\n" 
    "}\n" 

    "MTanalogButtons Keypad(PIN, THRESHOLDS_TABLE, press, release);\n\n" 


    "void setup()\n" 
    "{\n" 
    "  Serial.begin(")); 
  Serial.print(BAUDS); 
  Serial.print(F("); // Also set the console to ")); 
  Serial.print(BAUDS); 
  Serial.print(F(" baud!\n")); 
  if (REFERENCE != DEFAULT) 
  { 
    Serial.print(F(" analogReference(")); 
    Serial.print(REFERENCE); 
    Serial.print(F(");\n")); 
  } 
  Serial.print(F(" Serial.println(\"Press keys\");\n" 
    "}\n\n" 

    "void loop(){}"));
}


void loop()
{
}

Analysis of result

When you stop pressing the keys for more than 3 seconds, the program first displays a table on the console and the number of buttons. In the following, I used a 20-key analog keypad with an ADC reference voltage of 1.1V and with a Uno. The assembly is as follows:

It is calculated for a 1.1V reference. So I changed line 16:

#define REFERENCE DEFAULT

by

#define INTERNAL REFERENCE

The start of the result is:

  0 1000000000000000000000000000000000000000000000000000000001100000
 64 0000000000000000000000000000000000000000000000000000110000000000
128 0000000000000000000000000000000000000000000000110000000000000000
192 0000000000000000000000000000000000000001100000000000000000000000
256 0000000000000000000000000000000011100000000000000000000000000000
320 0000000000000000000000111000000000000000000000000000000000000000
384 0000000000011100000000000000000000000000000000000000000000000011
448 1000000000000000000000000000000000000000000000011111000000000000
512 0000000000000000000000000000000000011110000000000000000000000000
576 0000000000000000001111000000000000000000000000000000000000000000
640 0111100000000000000000000000000000000000000000011110000000000000
704 0000000000000000000000000001111000000000000000000000000000000000
768 0000000011110000000000000000000000000000000000000011100000000000
832 0000000000000000000000000001111000000000000000000000000000000000
896 0000111100000000000000000000000000000000001111100000000000000000
960 0000000000000000000000000000000000000000000000000000000000000001

The grid indicates what we saw for the ADC output values between 0 and 1023. A "1" indicates that if by pressing the buttons, we obtain the value considered, a “0” indicates that we do not obtain this value. Looking at the table, we see that we can have 0, 57, 58, 116, 117... 1023. "0" corresponds to pressing button no. 0, 57 and 58 corresponds to pressing button no. 1... and 1023 is obtained if no button is pressed.

We will observe that pressing a button gives 1 to 4 possible values. If everything were perfect, we would have packets of only one "1". We can also see that the packs of 1 are very spaced apart, which indicates that we could almost put 4 times as many buttons.

If for a button, we obtained 0000101110000, this would mean that we did not press the key long enough.

We will also notice that the values use almost all the space which means that we can afford more noise and of uncertainties.

This representation allows you to see the quality of the work.

Then follows the number of buttons seen:

I saw 20 buttons

I have a 20-key keypad, everything is fine. If I had less than 20 buttons, it would probably be a forgotten press, or enough dispersion which would make a single sequence of 1 for two different buttons (too much noise or too many buttons). If I had had more than 20 buttons, I would have a sequence like 000000101100000 for a button. Either I wouldn't have pressed long enough, or the editing is too disrupted.

The result program

Finally follows a test program. For example, we can obtain:

// This program tests the reading of the set of buttons with MTanalogButtons

#include  // V1.1.1 See http://arduino.dansetrad.fr/MTobjects

const byte PIN = 14;
const word TABLE_DES_THRESHOLD[] = {982, 920, 880, 840, 797, 754, 710, 665, 619, 571, 523, 471, 421, 369, 316, 260, 204, 145, 87, 28, 0}; // Comparison threshold values

void presses(byte touches)
{ 
Serial.print("Key #"); 
Serial.print(key); 
Serial.print("just pressed");
}
void release(void)
{ 
Serial.println("and released");
}
MTanalogButtons Keypad(PIN, TABLE_DES_THRESHOLDS, presses, release);

void setup()
{ 
Serial.begin(115200); // Also set the console to 115200 baud! 
analogReference(3); 
Serial.println("Press keys");
}

void loop(){}