Arduino-keypad

By barber408
// 2025 Richard E Barber

#include <Arduino.h>

// ---- Wiring (columns and rows) ----
// Columns: driven OUTPUT, idle HIGH, one pulled LOW per scan step
#define C0 2
#define C1 3
#define C2 4
#define C3 5
// Rows: read as INPUT_PULLUP (idle HIGH, go LOW when pressed with active column)
#define R0 6
#define R1 7
#define R2 8
#define R3 9

// If your keypad part outputs HIGH on press instead of shorting, set to 1
#define PRESSED_READS_HIGH 0

// ---- BCD bus (UNO analog pins used as digital) ----
// A0..A3 = digital 14..17
// We'll drive these with digitalWrite(14..17, 0/1).
// (No external latch required; we latch in software.)
byte g_latchedCode = 0;   // last latched 4-bit code
byte g_anyPressed  = 0;   // 1 while any key is currently held

// ---- Helpers that avoid arrays (iCircuit likes this better) ----
static byte colPin(byte idx) {
  if (idx == 0) return C0;
  if (idx == 1) return C1;
  if (idx == 2) return C2;
  return C3; // idx == 3
}
static byte rowPin(byte idx) {
  if (idx == 0) return R0;
  if (idx == 1) return R1;
  if (idx == 2) return R2;
  return R3; // idx == 3
}

// Key lookup without 2D array initializers
static char keyAt(byte r, byte c) {
  // Row-major:  { 1 2 3 A ; 4 5 6 B ; 7 8 9 C ; * 0 # D }
  if (r == 0) {
    if (c == 0) return '1';
    if (c == 1) return '2';
    if (c == 2) return '3';
    return 'A';
  } else if (r == 1) {
    if (c == 0) return '4';
    if (c == 1) return '5';
    if (c == 2) return '6';
    return 'B';
  } else if (r == 2) {
    if (c == 0) return '7';
    if (c == 1) return '8';
    if (c == 2) return '9';
    return 'C';
  } else { // r == 3
    if (c == 0) return '*';
    if (c == 1) return '0';
    if (c == 2) return '#';
    return 'D';
  }
}

// Map a key char to a 4-bit code (0x0..0xF)
byte codeForKey(char k) {
  if (k == '0') return 0x0;
  if (k == '1') return 0x1;
  if (k == '2') return 0x2;
  if (k == '3') return 0x3;
  if (k == '4') return 0x4;
  if (k == '5') return 0x5;
  if (k == '6') return 0x6;
  if (k == '7') return 0x7;
  if (k == '8') return 0x8;
  if (k == '9') return 0x9;
  if (k == 'A') return 0xA;
  if (k == 'B') return 0xB;
  if (k == 'C') return 0xC;
  if (k == 'D') return 0xD;
  if (k == '*') return 0xE;
  if (k == '#') return 0xF;
  return 0x0;
}

// Drive the 4-bit nibble on A0..A3 (pins 14..17), very parser-friendly
void outputBCD(byte code) {
  int b;

  b = 0;
  if (code & 0x01) b = 1;
  digitalWrite(14, b);   // A0 (LSB)

  b = 0;
  if (code & 0x02) b = 1;
  digitalWrite(15, b);   // A1

  b = 0;
  if (code & 0x04) b = 1;
  digitalWrite(16, b);   // A2

  b = 0;
  if (code & 0x08) b = 1;
  digitalWrite(17, b);   // A3 (MSB)
}

void setup() {
  // Console
  Serial.begin(9600);
  Serial.println("4x4 kpd scanner to BCD.");

  // Columns as outputs, idle HIGH
  pinMode(C0, OUTPUT); digitalWrite(C0, HIGH);
  pinMode(C1, OUTPUT); digitalWrite(C1, HIGH);
  pinMode(C2, OUTPUT); digitalWrite(C2, HIGH);
  pinMode(C3, OUTPUT); digitalWrite(C3, HIGH);

  // Rows as inputs with pullups
  pinMode(R0, INPUT_PULLUP);
  pinMode(R1, INPUT_PULLUP);
  pinMode(R2, INPUT_PULLUP);
  pinMode(R3, INPUT_PULLUP);

  // BCD bus pins (A0..A3 as 14..17)
  pinMode(14, OUTPUT);
  pinMode(15, OUTPUT);
  pinMode(16, OUTPUT);
  pinMode(17, OUTPUT);

  // Start bus at 0000 (latched)
  g_latchedCode = 0x0;
  outputBCD(g_latchedCode);
}

void driveAllColsHigh() {
  digitalWrite(C0, HIGH);
  digitalWrite(C1, HIGH);
  digitalWrite(C2, HIGH);
  digitalWrite(C3, HIGH);
}

void loop() {
  // Scan each column LOW one at a time
  for (byte c = 0; c < 4; c++) {
    driveAllColsHigh();
    digitalWrite(colPin(c), LOW);

    // small settle (avoid microsecond API for iCircuit)
    delay(1);

    // Read each row
    for (byte r = 0; r < 4; r++) {
      int v = digitalRead(rowPin(r));
      byte pressed = (PRESSED_READS_HIGH ? (v == HIGH) : (v == LOW));
      if (pressed) {
        char k = keyAt(r, c);

        // Only latch on the rising edge (first frame of a new press)
        if (!g_anyPressed) {
          byte code = codeForKey(k);
          g_latchedCode = code;
          outputBCD(g_latchedCode);

          Serial.print("Key press: ");
          Serial.print(k);
          Serial.print(" -> BCD 0x");
          Serial.println((int)code, HEX);

          g_anyPressed = 1;
        }

        // crude debounce so the console doesn't spam
        delay(50);
      }
    }

    // pacing between columns
    delay(1);
  }

  // Detect release: if no key is currently detected, clear the "pressed" flag
  // (bus remains latched with last value)
  byte anyNow = 0;
  for (byte c = 0; c < 4; c++) {
    // quick re-scan (very short) to check if any key is down
    driveAllColsHigh();
    digitalWrite(colPin(c), LOW);
    delay(1);
    for (byte r = 0; r < 4; r++) {
      int v = digitalRead(rowPin(r));
      byte pressed = (PRESSED_READS_HIGH ? (v == HIGH) : (v == LOW));
      if (pressed) {
        anyNow = 1;
      }
    }
    if (anyNow) break;
  }
  if (!anyNow) {
    g_anyPressed = 0;  // ready to latch next new press
  }
}