// https://forum.arduino.cc/t/push-button-on-a-numeric-keypad/1409855/
/*
Most DIY XY-plotter designs seem to use stepper motors with pulleys and belts, direct drive stepper motors,
or servos with hinged arms, to move the pen around. I opted not to suggest how to construct this movement,
but the "plunger" should be at the intersection of the servo arm extensions.
This sketch is untested mechanically other than a mock-up "on the bench" using a Nano-328P, a pushbutton,
a relay, two servos and two potentiometers. It starts with asking for the number of keys on the keypad
and is hard-coded for a maximum 4x4 (16 keys). Then it waits for a pushbutton to store the two servo angles
adjusted with potentiometers to align servo arms to intersect over each key.
Improvements:
- OLED or LCD display
- pause or pushbutton between storing the last angle pair entry and start of safecracking
- replace hard-coded combination example with random combinations
- sequential combination from 0000 to 9999 (or FFFF for a hexadecimal keypad)
- expand size of (key/angle) array to use with a full keyboard (105 keys)
- more plungers for SHIFT, FN, CTRL
*/
#include <Servo.h>
Servo lsrv; // left servo
Servo rsrv; // right servo
byte lsrvpin = 6, rsrvpin = 5, buttonpin = 4, plungerpin = A2, lpot = A0, rpot = A1;
int lval, lvalx, lsrvang, rval, rvalx, rsrvang; // potentiometer and servo values
int lhome = 65, rhome = 115; // home angles for servos point below bottom row of keys
byte keypadsize, keycount, lkeyang[16], rkeyang[16]; // max 4x4 keypad, store servo angles
bool allanglescollected = 0; // signal end of key/angle collection
void setup() {
Serial.begin(115200);
configureservos();
pinMode(LED_BUILTIN, OUTPUT);
pinMode(plungerpin, OUTPUT);
pinMode(buttonpin, INPUT_PULLUP);
getkeypadsize(); // get number of keys to map
while (allanglescollected == 0) { // collect keypad servo angles
adjustservoextensions(); // read potentiometers and move servo arms
readbutton();
if (keycount == keypadsize) {
allanglescollected = 1;
}
}
Serial.print("\n>> All ");
Serial.print(keypadsize);
Serial.println(" keys are mapped to servo angle pairs.");
Serial.println(">> Press button to start safecracking. (Relay activates plunger)");
}
void loop() {
while (digitalRead(buttonpin)) {};
crackthiscombo();
Serial.println(" Press button to re-start safecracking.");
}
void getkeypadsize() {
Serial.print(">> Enter number of keys: ");
while (Serial.available() == 0) {} // empty braces
keypadsize = Serial.parseInt();
Serial.println(keypadsize);
Serial.println(">> Start with '0' key.");
Serial.println(">> Adjust (L)pot/(R)pot so (L)servo/(R)servo arm extensions intersect.");
Serial.println(">> Press button to store servo angle pair.");
}
void crackthiscombo() { // demonstrate pre-programmed combination
byte comboarray[] = {8, 6, 7, 5, 3, 0, 9};
byte comboarraysize = sizeof(comboarray) / sizeof(comboarray[0]);
for (byte index = 0; index < comboarraysize; index++) {
moveplungertokey(comboarray[index]);
activateplunger();
}
}
void moveplungertokey(byte key) {
Serial.print(key);
lsrv.write(lkeyang[key]);
rsrv.write(rkeyang[key]);
}
void activateplunger() {
delay(333); // 1/3 of a second between key presses seems human
digitalWrite(plungerpin, HIGH); // actuate plunger
delay(333); // 1/3 of a second press duration seems human
digitalWrite(plungerpin, LOW); // raise plunger
}
void readbutton() {
if (digitalRead(buttonpin) == 0) { // button pin is LOW when PRESSED
delay(150); // my lazy button debounce
storeservoangles();
keycount++;
}
}
void storeservoangles() {
Serial.print("(");
Serial.print(keycount);
Serial.print(":");
lkeyang[keycount] = lsrvang; // left servo angle
Serial.print(lkeyang[keycount]);
Serial.print(",");
rkeyang[keycount] = rsrvang; // right servo angle
Serial.print(rkeyang[keycount]);
Serial.print(")");
}
void configureservos() {
lsrv.write(lhome); // write servo angle before attaching servo
rsrv.write(rhome); // write servo angle before attaching servo
lsrv.attach(lsrvpin);
rsrv.attach(rsrvpin);
}
void adjustservoextensions() {
lval = analogRead(lpot);
rval = analogRead(rpot);
if ((lval != lvalx) || (rval != rvalx)) {
lvalx = lval;
rvalx = rval;
lsrvang = map(lval, 0, 1023, 0, lhome);
rsrvang = map(rval, 0, 1023, rhome, 180);
lsrv.write(lsrvang);
rsrv.write(rsrvang);
}
delay(15); // delay for servos to arrive
}4
5
6
7
8
9
1
2
3
*
0
#
PLUNGER