/* demo several 4x4 and 3x4 keyboard scanners * * on ESP8266, full 8 pins, using gpios 15,13,14,12 2,0,4,5 * * note: on the Wemos, gpio15 has an external pulldown * and gpio16 does not have an internal pullup * making both awkward to use * * using the module that has 16 tact buttons, connector at the left, S1 top left, S16 bottom right * the pins are bottom to top : row 4..1, col 1..4 */ int rows[] = {5,4,0,2}; // driven int cols[] = {14,12,13,15}; // input only //#define FIVE_PIN 1 //#define SEVEN_PIN 1 #define FOUR_PIN 1 void setup() { delay(1000); Serial.begin(115200); // set all rows as 0, ready for interrupt sensing rowsSet(0); // set up all column pins as inputs with pullups pinMode(cols[0],INPUT_PULLUP); pinMode(cols[1],INPUT_PULLUP); pinMode(cols[2],INPUT_PULLUP); attachInterrupt(digitalPinToInterrupt(cols[0]), kbd_irq, FALLING); attachInterrupt(digitalPinToInterrupt(cols[1]), kbd_irq, FALLING); attachInterrupt(digitalPinToInterrupt(cols[2]), kbd_irq, FALLING); Serial.println(); } // 0, set all rows low, anything else is a bit pattern of hi/lo // simulate open-drain with pullup void rowsSet(int rowPattern) { for (int r=0; r<4; r++) { if (rowPattern & 1) pinMode(rows[r],INPUT_PULLUP); else { pinMode(rows[r],OUTPUT); digitalWrite(rows[r],0); } rowPattern >>= 1; } } // state machine 0=ready, 1=interrupt detected, 2=interrupt handled waiting for release const int KEY_NONE = 0; const int KEY_DETECTED = 1; const int KEY_PROCESSED = 2; static int key_state; ICACHE_RAM_ATTR void kbd_irq(void) { // inform the main code if (key_state == KEY_NONE) key_state = KEY_DETECTED; } static char lut4[] = "123A456B789C*0#D"; // used in 4x4 keypad static char lut[] = "123456789*0#"; // used with 3x4 kaypad int char_count; // calculator demo // key=0, ignore key=ascii '0'...'9' data key='*' addition key='#' show total void calculator(char key) { if (key) { static int acc = 0, sum=0; if (key>='0' && key<='9') { acc = acc*10+(key&0x0f); } else if (key=='*') { // addition sum += acc; acc = 0; } else if (key=='#') { // finish and show total sum += acc; Serial.println(String("total: ")+String(sum)); sum = acc = 0; } } } #ifdef SEVEN_PIN // check all the column inputs, return a number, 1/2/3=pressed 0=nokey int readColumns() { // return 10 - digitalRead(cols[0]) - 2*digitalRead(cols[1]) - 3*digitalRead(cols[2]) - 4*digitalRead(cols[3]); return 6 - digitalRead(cols[0]) - 2*digitalRead(cols[1]) - 3*digitalRead(cols[2]); } const int NO_KEYS = 0; int rowPattern [] = {~1, ~2, ~4, ~8}; void loop() { // put your main code here, to run repeatedly: int row, col; switch(key_state) { case KEY_NONE: break; case KEY_DETECTED: // just got an interrupt, scan the keys for (row=0; row<4; row++) { rowsSet(rowPattern[row]); delayMicroseconds(10); col = readColumns(); if (col) break; } rowsSet(0); key_state = KEY_PROCESSED; // keyboard scan done, the information is in (row,col) if (col) { Serial.print(String(lut[3*row+col-1])); // <---the key that was pressed if (++char_count >= 16) char_count=0, Serial.println(); } break; case KEY_PROCESSED: // wait for release col = readColumns(); if (col == NO_KEYS) key_state = KEY_NONE; break; } } // NOTE: even though a 7 pin connection allows 2-key rollover, the code example // above does NOT handles 2 keys pressed at the same time. #endif #ifdef FIVE_PIN // column decode these are octal values, groups of 3 bits // check all the column inputs, return the pattern; no key press returns 0b111 int readColumns() { return digitalRead(cols[0]) + 2*digitalRead(cols[1]) + 4*digitalRead(cols[2]); } const int NO_KEYS = 07; int rowPattern [] = {0b11, 0b10, 0b01, 0b00}; // column sense patterns, as the rowPattern is cycled; these are octal values, 3 bits at a time int fiveKeyLUT[] = {06666, 05555, 03333, // 123 07676, 07575, 07373, // 456 07766, 07755, 07733, // 789 07666, 07555, 07333}; // *0# void loop() { // put your main code here, to run repeatedly: int row, col, d; char key = 0; switch(key_state) { case KEY_NONE: break; case KEY_DETECTED: //Serial.println("got key"); // just got an interrupt, scan the keys d = 0; for (row=0; row<4; row++) { //Serial.println(String("scan row ")+String(row)+" using pattern 0b"+String(rowPattern[row],BIN)); rowsSet(rowPattern[row]); delayMicroseconds(10); d = 8*d + readColumns(); // shift left by 3, and read next 3 //Serial.println(String("and rows read as: 0")+String(d,OCT)); } rowsSet(0); key_state = KEY_PROCESSED; // now analyze the column reads //Serial.println(String("final d=")+String(d,OCT)); for (int i=0; i= 16) char_count=0, Serial.println(); } break; case KEY_PROCESSED: // wait for release col = readColumns(); if (col==NO_KEYS) key_state = KEY_NONE; break; } calculator(key); } #endif #ifdef FOUR_PIN // decode these are hex values const int fourKeyLUT[] = { 0xaa, 0x55, 0x00, // 123 0xee, 0xdd, 0xcc, // 456 0xfa, 0xf5, 0xf0, // 789 0xea, 0xd5, 0xc0 // *0# }; // check all the column inputs, return a number, 01/10/00=pressed 11=nokey int readColumns() { return digitalRead(cols[0]) + 2*digitalRead(cols[1]); } const int NO_KEYS = 0x3; int rowPattern [] = {0b11, 0b10, 0b01, 0b00}; void loop() { // put your main code here, to run repeatedly: int row, col, d; char key=0; switch(key_state) { case KEY_NONE: break; case KEY_DETECTED: //Serial.println("got key"); delay(5); //wait for the bounce to settle // just got an interrupt, scan the keys d = 0; for (row=0; row<4; row++) { //Serial.println(String("scan row ")+String(row)+" using pattern 0b"+String(rowPattern[row],BIN)); rowsSet(rowPattern[row]); delayMicroseconds(10); d = 4*d + readColumns(); // shift left by 2, and read next 2 //Serial.println(String("and rows read as: 0x")+String(d,HEX)); } rowsSet(0); key_state = KEY_PROCESSED; // now analyze the column reads // Serial.println(String("final d=")+String(d,HEX)); key = 0; for (int i=0; i= 16) char_count=0, Serial.println(); } break; case KEY_PROCESSED: // wait for release col = readColumns(); if (col==NO_KEYS) key_state = KEY_NONE; break; } calculator(key); } #endif