Keypad Controller Service Program Includes and  Defines

/*

  keypad-Control.ino

  This is a home automation controller using on-off modilation with RS-232 transmitter at 433 MHz

  The display is Jameco SIC2004A_BLWIT. It came with an I2C adapter already attached.

  The pocessor is a Pololu A-Star mini 32u4 LV

  This has an efficient DC-DC converter to boost the battery voltage to 5V.

  It has a STANDBY pin, high to turn off the convrter.

  This is operated by a battery of between 2.7 and 11.8V. At 4.5V the current draw is 150 MA

  During standby this drops to 45 microamperes. Power is 3 AA batteries.

  Turn-on uses a ball-type tilt sensor. Active on nearly upside-down.

  John Saunders 6/24/2024

*/


#include <Wire.h>

#include <hd44780.h>                       // main hd44780 header

#include <hd44780ioClass/hd44780_I2Cexp.h> // i2c expander i/o class header

#include <EEPROM.h>

hd44780_I2Cexp lcd(0x27 );

//Pins 0,1 are Serial1, 2,3 are I2C

#define COL_1_PIN  4        //Direct connections through the 4x4 matrixed keypad

#define COL_2_PIN  5

#define COL_3_PIN  6

#define COL_4_PIN  7

#define ROW_1_PIN  8

#define ROW_2_PIN  9

#define ROW_3_PIN 10

#define ROW_4_PIN 11

#define PULSE_PIN 12        //For the transmitter unlock pre-pulse

#define KEEP_ALIVE_PIN 13   //To maintain power after the tlt sensor is back off

#define LCD_COLS  20

#define LCD_ROWS   4

#define LONG_TIMEOUT 600      //20 count/sec after the last key-press before auto-turnoff

#define SHORT_TIMEOUT 160

Keypad Controller Service Program Global Variables


const int colPins[4] =  {COL_1_PIN, COL_2_PIN, COL_3_PIN, COL_4_PIN};

const int rowPins[4] = {ROW_1_PIN, ROW_2_PIN, ROW_3_PIN, ROW_4_PIN};


uint8_t alphaKey;    //Set by the A-D keyys

uint8_t decKey;     //Set by the numeric keys

uint8_t numItems;   //Not used

bool tic;           //True if a key has ben depressed

uint8_t alphaKeySave;  //Last transmitted value for 

uint8_t decKeySave;   //same display next time started


int loopCount;      //For auto power-down

int itemList[40] =  //To make the user experirnce independent of the EEPROM order

{

  20,  0, 27, 21, 22, 18, 19,  3,  1,  2,

  11, 10,  4,  5,  6,  7,  8,  9, 35, 36,

  34, 12, 13, 14, 15, 16, 17, 11, 10, 21,

   0, 24, 25, 26, 28, 29, 30, 31, 32, 33

};


//These 4 structs/arrays must be identical to those in EEPROMwrite.ino

struct stats_t {          //The first 5 are read only

  int lastAddr;           //Not used

  int areasStart;         //EEPROM address of area (top line) table 

  int line2Start;         //EEPROM address of line 2 (third line) table

  uint8_t  numItems;      //Not used

  uint8_t decKey = 0;     //These two are written by Keypad-Control

  uint8_t alphaKey = 0;    //before power off,and read back at setup().

};

stats_t stats;


typedef char area_t[18];

area_t areaLine;          //Wil be filled from the EEPROM


struct dispLine_t {

  char offCode;

  char onCode;

  byte areaInx;

  byte lineTwoInx;

  char itemName[16];

};

dispLine_t lineOne;       //Wil be filled from the EEPROM


typedef  char lineTwo_t[18];

lineTwo_t lineTwo;        //Wil be filled from the EEPROM


struct key_t {

  int col_inx;

  int row_inx;

  char keyChar; //Not used

};


const key_t keyVals[16] = {       //The index of this table is the result

  {1, 3, '0'}, {0, 0, '1'}, {1, 0, '2'}, {2, 0, '3'}, {0, 1, '4'},

  {1, 1, '5'}, {2, 1, '6'}, {0, 2, '7'}, {1, 2, '8'}, {2, 2, '9'},

  {3, 0, 'A'}, {3, 1, 'B'}, {3, 2, 'C'}, {3, 3, 'D'}, {0, 3, '*'}, {2, 3, '#'},

};

Keypad Controller Service Program Transmit Function

//There are 38 single character commands, plus 2-character commands with the first=O

void transmit(char actionCode, bool actionType = true) { // true is 2-character)

  int spacing = 7;

  char sendBuff[] = "14L1776     ";

  if (actionType) {

    sendBuff[spacing++] = 'O';

    sendBuff[spacing++] = ',';

  }

  sendBuff[spacing++] = actionCode;

  sendBuff[spacing++] = 13;

  sendBuff[spacing++] = 10;

  sendBuff[spacing] = 0;

  digitalWrite(PULSE_PIN, HIGH);

  delay(20);

  digitalWrite(PULSE_PIN, LOW);

  delay(10);

  Serial1.write(sendBuff);

  alphaKeySave = alphaKey;        //For the bext session

  decKeySave = decKey;

  lcd.setCursor(0, 3);           //Display on the bottom line

  lcd.print(" Code ");

  if (actionType) {

    lcd.print('O');

  }

  lcd.print(actionCode);

  lcd.print(" sent     ");

  delay(300);                     //To avoid repeats, and the some receivers needs this

}

Keypad Controller Service  Program Utility Function

void shutDown(void) {

  stats.decKey = decKeySave;

  stats.alphaKey = alphaKeySave;

  EEPROM.put(1010, stats);

  loopCount = -1;

  digitalWrite(KEEP_ALIVE_PIN, LOW);

}

Keypad Controller Service Program Keypad Function

int getKey(void) {      //130 microsec for no key pressed

  int keyIndex = 16;    //Default output if no key depressed

  int selRow = 4;

  int selCol = 4;

  int i, j;

  for (i = 0; i < 4; i++) {

    digitalWrite(colPins[i], LOW);      //Poll thre columns

    for (j = 0; j < 4; j++) {

      if (digitalRead(rowPins[j]) == LOW) { // A key press goes to 2.4 milliseconds

        selRow = j;

        selCol = i;

        break;          //When a key press is detected

      }

    }

    digitalWrite(colPins[i], HIGH);       //Stop polling is a key is depressed

    if (selRow < 4) break;

  }

  if ((selRow < 4)  && (selCol < 4)) {    //Find the match if a key press detected

    for (int k = 0; k < 16; k++) {

      if ((selRow == keyVals[k].row_inx) && (selCol == keyVals[k].col_inx)) {

        keyIndex = k;

        break;

      }

    }

  }

  return keyIndex;

}

Keypad Controller Service Program EEPROM Function

void getLine(int alpha, int dec) {    //stats must be read before.

  int inx;

  int selInx;

  inx = 10 * alpha + dec;

  selInx = itemList[inx];

  int eepromAddr = sizeof(dispLine_t) * selInx; //Starts at 0

  EEPROM.get(eepromAddr, lineOne);

  eepromAddr = (sizeof(area_t) * lineOne.areaInx) + stats.areasStart;

  EEPROM.get(eepromAddr, areaLine);

  eepromAddr = (sizeof(lineTwo_t) * lineOne.lineTwoInx) + stats.line2Start;

  EEPROM.get(eepromAddr, lineTwo);

}

Keypad Controller Service Program Setup

void setup() {

  int status;

  int i;

  pinMode(KEEP_ALIVE_PIN, OUTPUT);

  digitalWrite(KEEP_ALIVE_PIN, HIGH);      //68 milliseconds fron STBY going low

  delay(100);

  status = lcd.begin(LCD_COLS, LCD_ROWS);

  if (status) // non zero status means it was unsuccesful

  {

    // hd44780 has a fatalError() routine that blinks a led if possible

    // begin() failed so blink error code using the onboard LED if possible

    hd44780::fatalError(status); // does not return

  }

  for (i = 0; i < 4; i++) {

    pinMode(colPins[i], OUTPUT);

    digitalWrite(colPins[i], HIGH);

  }

  for (i = 0; i < 4; i++) {

    pinMode(rowPins[i], INPUT_PULLUP);

  }

  Serial1.begin(2400);

  lcd.home();

  lcd.clear();

  // prints attribute message

  lcd.setCursor(2, 0);

  lcd.print("Universal Control");

  lcd.setCursor(0, 1);

  lcd.print("Made in May 2024 by");

  lcd.setCursor(3, 2);

  lcd.print("John Saunders");

  lcd.setCursor(7, 3);

  lcd.print("Age 90");

  delay(2000);

  EEPROM.get(1010, stats);      //Get EEPROM starting addresses

  alphaKey = stats.alphaKey;

  decKey = stats.decKey;

  numItems = stats.numItems;    //Not used

  loopCount = timeout;

  tic = true;

}

Keypad Controller Service Program  Loop 

void loop() {

  int keyInx;

  if (loopCount > 0) {

    loopCount--;

  }

  if (loopCount == 0)  {

    shutDown();

  }

  /**********************************************************

                    Get Key Press and read EEPROM

   **********************************************************

  */

  keyInx = getKey();


  if ((keyInx > 9) && (keyInx < 14)) {

    alphaKey = keyInx - 10;

    tic = true;

  }

  if ((keyInx >= 0) && (keyInx < 10)) {

    decKey = keyInx;

    tic = true;

  }

  if (tic) {

    getLine(alphaKey, decKey);

  }

  /**********************************************************

                  Display lines 0-2

  **********************************************************

  */

  if (tic) {                //To prevet flickering

    lcd.clear();

    lcd.setCursor(0, 0);

    lcd.print(areaLine);

    lcd.setCursor(0, 1);

    if (lineOne.areaInx < 3) {    //Seiection commands

      lcd.print("Show ");

    }

    else {

      lcd.print("Item=");         //On-off commands

    }

    lcd.print(lineOne.itemName);

    lcd.setCursor(0, 2);

    if (lineOne.areaInx < 3) {

      lcd.print("or *=");

    }

    else {

      lcd.print("   ");

    }

    lcd.print(lineTwo);

  }


  /**********************************************************

     areaInx range  ON Action      Transmit code

     0-1            Selection        2-characters

     2              Selection        1-character

     3-8              On-Off         1-character

  **********************************************************

  */


  const char allOff[] = "W9fC8UJ6";   //Lights visable in living room

  if (keyInx == 14) {      //* Usually OFF

    if (lineOne.areaInx < 2) {

      switch (lineOne.onCode) {   //Special alternate actions

        case'n':

          shutDown();

          break;

        case'p':   //All living room lghts off

          for (int i = 0; i < 8; i++) {

            transmit(allOff[i], false);

            delay(400);

          }

          break;

        case'F':

          transmit('F', true);

        default:

          transmit(lineOne.onCode, true);

          break;

      }

    }

    else {          //normal OFF commands

      transmit(lineOne.offCode, false);

    }

  }

  if (keyInx == 15) {       //# ON or Selection

    if (lineOne.areaInx > 1) {

      transmit(lineOne.onCode, false);

    }

    else {

      transmit(lineOne.offCode, true);

    }

  }

  if (tic) {                //Reset timeout on keypress

    loopCount = timeout;

  }

  tic = false;

  delay(50);

}