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(){}