/* This is the code for the Arduino keyboard I've made, ELECTRONOOBS 21/08/2019
* Tutorial: https://electronoobs.com/eng_arduino_tut103.php
* Schematic: https://electronoobs.com/eng_arduino_tut103_sch1.php
* Code: https://electronoobs.com/eng_arduino_tut103_code1.php
* PCB GERBERs: https://electronoobs.com/eng_arduino_tut103_gerber1.php
* YouTube channel: https://www.youtube.com/channel/UCjiVhIvGmRZixSzupD0sS9Q
*
* Connect Rx and Tx to any other microcontroller and use serial at 115200 bauds.*/
#ifndef _ADAFRUIT_KEYPAD_H_
#define _ADAFRUIT_KEYPAD_H_
/*
Copyright (c) 2014 Arduino. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef __cplusplus
#ifndef _ADAFRUIT_KEYPAD_RING_BUFFER_
#define _ADAFRUIT_KEYPAD_RING_BUFFER_
#include <stdint.h>
#include <string.h>
// Define constants and variables for buffering incoming serial data. We're
// using a ring buffer (I think), in which head is the index of the location
// to which to write the next incoming character and tail is the index of the
// location from which to read.
#ifndef SERIAL_BUFFER_SIZE
#define SERIAL_BUFFER_SIZE 256
#endif
template <int N> class Adafruit_Keypad_RingbufferN {
public:
uint8_t _aucBuffer[N];
volatile int _iHead;
volatile int _iTail;
public:
Adafruit_Keypad_RingbufferN(void);
void store_char(uint8_t c);
void clear();
int read_char();
int available();
int availableForStore();
int peek();
bool isFull();
private:
int nextIndex(int index);
};
typedef Adafruit_Keypad_RingbufferN<SERIAL_BUFFER_SIZE>
Adafruit_Keypad_Ringbuffer;
template <int N>
Adafruit_Keypad_RingbufferN<N>::Adafruit_Keypad_RingbufferN(void) {
memset(_aucBuffer, 0, N);
clear();
}
template <int N> void Adafruit_Keypad_RingbufferN<N>::store_char(uint8_t c) {
int i = nextIndex(_iHead);
// if we should be storing the received character into the location
// just before the tail (meaning that the head would advance to the
// current location of the tail), we're about to overflow the buffer
// and so we don't write the character or advance the head.
if (i != _iTail) {
_aucBuffer[_iHead] = c;
_iHead = i;
}
}
template <int N> void Adafruit_Keypad_RingbufferN<N>::clear() {
_iHead = 0;
_iTail = 0;
}
template <int N> int Adafruit_Keypad_RingbufferN<N>::read_char() {
if (_iTail == _iHead)
return -1;
uint8_t value = _aucBuffer[_iTail];
_iTail = nextIndex(_iTail);
return value;
}
template <int N> int Adafruit_Keypad_RingbufferN<N>::available() {
int delta = _iHead - _iTail;
if (delta < 0)
return N + delta;
else
return delta;
}
template <int N> int Adafruit_Keypad_RingbufferN<N>::availableForStore() {
if (_iHead >= _iTail)
return N - 1 - _iHead + _iTail;
else
return _iTail - _iHead - 1;
}
template <int N> int Adafruit_Keypad_RingbufferN<N>::peek() {
if (_iTail == _iHead)
return -1;
return _aucBuffer[_iTail];
}
template <int N> int Adafruit_Keypad_RingbufferN<N>::nextIndex(int index) {
return (uint32_t)(index + 1) % N;
}
template <int N> bool Adafruit_Keypad_RingbufferN<N>::isFull() {
return (nextIndex(_iHead) == _iTail);
}
#endif /* _ADAFRUIT_KEYPAD_RING_BUFFER_ */
#endif /* __cplusplus */
#include "Arduino.h"
#include <string.h>
#define makeKeymap(x) ((byte *)x) ///< cast the passed key characters to bytes
#define KEY_JUST_RELEASED (0) ///< key has been released
#define KEY_JUST_PRESSED (1) ///< key has been pressed
/**************************************************************************/
/*!
@brief key event structure
*/
/**************************************************************************/
union keypadEvent {
struct {
uint8_t KEY : 8; ///< the keycode
uint8_t EVENT : 8; ///< the edge
uint8_t ROW : 8; ///< the row number
uint8_t COL : 8; ///< the col number
} bit; ///< bitfield format
uint32_t reg; ///< register format
};
/**************************************************************************/
/*!
@brief Class for interfacing GPIO with a diode-multiplexed keypad
*/
/**************************************************************************/
class Adafruit_Keypad {
public:
Adafruit_Keypad(byte *userKeymap, byte *row, byte *col, int numRows,
int numCols);
~Adafruit_Keypad();
void begin();
void tick();
bool justPressed(byte key, bool clear = true);
bool justReleased(byte key);
bool isPressed(byte key);
bool isReleased(byte key);
int available();
keypadEvent read();
void clear();
private:
byte *_userKeymap;
byte *_row;
byte *_col;
volatile byte *_keystates;
Adafruit_Keypad_Ringbuffer _eventbuf;
int _numRows;
int _numCols;
volatile byte *getKeyState(byte key);
};
#endif
#define _KEY_PRESSED_POS (1)
#define _KEY_PRESSED (1UL << _KEY_PRESSED_POS)
#define _JUST_PRESSED_POS (2)
#define _JUST_PRESSED (1UL << _JUST_PRESSED_POS)
#define _JUST_RELEASED_POS (3)
#define _JUST_RELEASED (1UL << _JUST_RELEASED_POS)
#define _KEYPAD_SETTLING_DELAY 20
/**************************************************************************/
/*!
@brief default constructor
@param userKeymap a multidimensional array of key characters
@param row an array of GPIO pins that are connected to each row of the
keypad
@param col an array of GPIO pins that are connected to each column of the
keypad
@param numRows the number of rows on the keypad
@param numCols the number of columns on the keypad
*/
/**************************************************************************/
Adafruit_Keypad::Adafruit_Keypad(byte *userKeymap, byte *row, byte *col,
int numRows, int numCols) {
_userKeymap = userKeymap;
_row = row;
_col = col;
_numRows = numRows;
_numCols = numCols;
_keystates = NULL;
}
/**************************************************************************/
/*!
@brief default destructor
*/
/**************************************************************************/
Adafruit_Keypad::~Adafruit_Keypad() {
if (_keystates != NULL) {
free((void *)_keystates);
}
}
/**************************************************************************/
/*!
@brief get the state of a key with the given name
@param key the name of the key to be checked
*/
/**************************************************************************/
volatile byte *Adafruit_Keypad::getKeyState(byte key) {
for (int i = 0; i < _numRows * _numCols; i++) {
if (_userKeymap[i] == key) {
return _keystates + i;
}
}
return NULL;
}
/**************************************************************************/
/*!
@brief read the array of switches and place any events in the buffer.
*/
/**************************************************************************/
void Adafruit_Keypad::tick() {
uint8_t evt;
for (int i = 0; i < _numCols; i++) {
digitalWrite(_col[i], HIGH);
}
int i = 0;
for (int c = 0; c < _numCols; c++) {
digitalWrite(_col[c], LOW);
delayMicroseconds(_KEYPAD_SETTLING_DELAY);
for (int r = 0; r < _numRows; r++) {
i = r * _numCols + c;
bool pressed = !digitalRead(_row[r]);
// Serial.print((int)pressed);
volatile byte *state = _keystates + i;
byte currentState = *state;
if (pressed && !(currentState & _KEY_PRESSED)) {
currentState |= (_JUST_PRESSED | _KEY_PRESSED);
evt = KEY_JUST_PRESSED;
_eventbuf.store_char(evt);
_eventbuf.store_char(*(_userKeymap + i));
_eventbuf.store_char(r);
_eventbuf.store_char(c);
} else if (!pressed && (currentState & _KEY_PRESSED)) {
currentState |= _JUST_RELEASED;
currentState &= ~(_KEY_PRESSED);
evt = KEY_JUST_RELEASED;
_eventbuf.store_char(evt);
_eventbuf.store_char(*(_userKeymap + i));
_eventbuf.store_char(r);
_eventbuf.store_char(c);
}
*state = currentState;
}
// Serial.println("");
digitalWrite(_col[c], HIGH);
}
}
/**************************************************************************/
/*!
@brief set all the pin modes and set up variables.
*/
/**************************************************************************/
void Adafruit_Keypad::begin() {
_keystates = (volatile byte *)malloc(_numRows * _numCols);
memset((void *)_keystates, 0, _numRows * _numCols);
for (int i = 0; i < _numCols; i++) {
pinMode(_col[i], OUTPUT);
digitalWrite(_col[i], HIGH);
}
for (int i = 0; i < _numRows; i++) {
pinMode(_row[i], INPUT_PULLUP);
}
}
/**************************************************************************/
/*!
@brief check if the given key has just been pressed since the last tick.
@param key the name of the key to be checked
@param clear whether to reset the state (default yes) post-check
@returns true if it has been pressed, false otherwise.
*/
/**************************************************************************/
bool Adafruit_Keypad::justPressed(byte key, bool clear) {
volatile byte *state = getKeyState(key);
bool val = (*state & _JUST_PRESSED) != 0;
if (clear)
*state &= ~(_JUST_PRESSED);
return val;
}
/**************************************************************************/
/*!
@brief check if the given key has just been released since the last tick.
@param key the name of the key to be checked
@returns true if it has been released, false otherwise.
*/
/**************************************************************************/
bool Adafruit_Keypad::justReleased(byte key) {
volatile byte *state = getKeyState(key);
bool val = (*state & _JUST_RELEASED) != 0;
*state &= ~(_JUST_RELEASED);
return val;
}
/**************************************************************************/
/*!
@brief check if the given key is currently pressed
@param key the name of the key to be checked
@returns true if it is currently pressed, false otherwise.
*/
/**************************************************************************/
bool Adafruit_Keypad::isPressed(byte key) {
return (*getKeyState(key) & _KEY_PRESSED) != 0;
}
/**************************************************************************/
/*!
@brief check if the given key is currently released
@param key the name of the key to be checked
@returns true if it is currently released, false otherwise.
*/
/**************************************************************************/
bool Adafruit_Keypad::isReleased(byte key) {
return (*getKeyState(key) & _KEY_PRESSED) == 0;
}
/**************************************************************************/
/*!
@brief check how many events are in the keypads buffer
@returns the number of events currently in the buffer
*/
/**************************************************************************/
int Adafruit_Keypad::available() {
return (_eventbuf.available() / sizeof(keypadEvent));
}
/**************************************************************************/
/*!
@brief pop the next event off of the FIFO
@returns the next event in the FIFO
*/
/**************************************************************************/
keypadEvent Adafruit_Keypad::read() {
keypadEvent k;
k.bit.EVENT = _eventbuf.read_char();
k.bit.KEY = _eventbuf.read_char();
k.bit.ROW = _eventbuf.read_char();
k.bit.COL = _eventbuf.read_char();
return k;
}
/**************************************************************************/
/*!
@brief Clear out the event buffer and all the key states
*/
/**************************************************************************/
void Adafruit_Keypad::clear() {
_eventbuf.clear();
for (int i = 0; i < _numRows * _numCols; i++)
*(_keystates + i) = 0;
}
//Downlaod here: https://electronoobs.com/eng_Arduino_Adafruit_Keypad.php
const byte ROWS = 4; // rows
const byte COLS = 10; // columns
//define the symbols on the buttons of the keypads
char keys[ROWS][COLS] = {
{'1','2','3','4','5','6','7','8','9','0'},
{'q','w','e','r','t','y','u','i','o','p'},
{'a','s','d','f','g','h','j','k','l','!'},
{'*','-','z','x','c','v','b','n','m',','} //SHIFT = * // OK = - // RETURN = ! // SPACE = ,
};
//Inputs/outputs
byte rowPins[ROWS] = {A0, A1, A2, A3}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {13,12,11,10,9,8,7,6,5,4}; //connect to the column pinouts of the keypad
int send_pin = A2;
int LED = A3;
//Variables
bool SHIFT = false;
bool send_pin_state = false;
//initialize an instance of class NewKeypad
Adafruit_Keypad customKeypad = Adafruit_Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS);
void setup() {
pinMode(send_pin,INPUT_PULLUP);
Serial.begin(115200); //If you change the speed here, the receiver Arduino will also need to change the speed.
customKeypad.begin();
pinMode(LED, OUTPUT);
digitalWrite(LED, SHIFT);
}
void loop() {
/////////First we detect if send button was pressed//////////
if(!digitalRead(send_pin) && !send_pin_state)
{
Serial.print("{"); //Send
send_pin_state = true;
}
if(digitalRead(send_pin) && send_pin_state)
{
send_pin_state = false;
}
////////////////////////////////////////////////////////////
///////Now detect when a keyboad button was pressed/////////
////////////////////////////////////////////////////////////
customKeypad.tick(); //Create the rows pulses
while(customKeypad.available()){ //If a new push button was detected...
keypadEvent e = customKeypad.read(); //Get what cahracter was pressed
if(e.bit.EVENT == KEY_JUST_PRESSED) //Only send the character after we release that button in order to avoid problems...
{
if((char)e.bit.KEY == '*') //If the character is "*", then we've presed the SHIFT key so we activate that variable
{
SHIFT = !SHIFT; //Enable/disable shift
digitalWrite(LED, SHIFT);
}
else
{
if(SHIFT) //If shift is active we send special characters or UPERCASE
{
/////////////////Send special characters///////////////
///////////////////////////////////////////////////////
if((char)e.bit.KEY == '1')
{
Serial.print("'");
}
if((char)e.bit.KEY == '2')
{
Serial.print("@");
}
if((char)e.bit.KEY == '3')
{
Serial.print(",");
}
if((char)e.bit.KEY == '4')
{
Serial.print("$");
}
if((char)e.bit.KEY == '5')
{
Serial.print("%");
}
if((char)e.bit.KEY == '6')
{
Serial.print("?");
}
if((char)e.bit.KEY == '7')
{
Serial.print("!");
}
if((char)e.bit.KEY == '8')
{
Serial.print("&");
}
if((char)e.bit.KEY == '9')
{
Serial.print("(");
}
if((char)e.bit.KEY == '0')
{
Serial.print(")");
}
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
/////////////////Send UPERCASE first row///////////////
///////////////////////////////////////////////////////
if((char)e.bit.KEY == 'q')
{
Serial.print("Q");
}
if((char)e.bit.KEY == 'w')
{
Serial.print("W");
}
if((char)e.bit.KEY == 'e')
{
Serial.print("E");
}
if((char)e.bit.KEY == 'r')
{
Serial.print("R");
}
if((char)e.bit.KEY == 't')
{
Serial.print("T");
}
if((char)e.bit.KEY == 'y')
{
Serial.print("Y");
}
if((char)e.bit.KEY == 'u')
{
Serial.print("U");
}
if((char)e.bit.KEY == 'i')
{
Serial.print("I");
}
if((char)e.bit.KEY == 'o')
{
Serial.print("O");
}
if((char)e.bit.KEY == 'p')
{
Serial.print("P");
}
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
/////////////////Send UPERCASE second row//////////////
///////////////////////////////////////////////////////
if((char)e.bit.KEY == 'a')
{
Serial.print("A");
}
if((char)e.bit.KEY == 's')
{
Serial.print("S");
}
if((char)e.bit.KEY == 'd')
{
Serial.print("D");
}
if((char)e.bit.KEY == 'f')
{
Serial.print("F");
}
if((char)e.bit.KEY == 'g')
{
Serial.print("G");
}
if((char)e.bit.KEY == 'h')
{
Serial.print("H");
}
if((char)e.bit.KEY == 'j')
{
Serial.print("J");
}
if((char)e.bit.KEY == 'k')
{
Serial.print("K");
}
if((char)e.bit.KEY == 'l')
{
Serial.print("L");
}
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
/////////////////Send UPERCASE third row///////////////
///////////////////////////////////////////////////////
if((char)e.bit.KEY == 'z')
{
Serial.print("Z");
}
if((char)e.bit.KEY == 'x')
{
Serial.print("X");
}
if((char)e.bit.KEY == 'c')
{
Serial.print("C");
}
if((char)e.bit.KEY == 'v')
{
Serial.print("V");
}
if((char)e.bit.KEY == 'b')
{
Serial.print("B");
}
if((char)e.bit.KEY == 'n')
{
Serial.print("N");
}
if((char)e.bit.KEY == 'm')
{
Serial.print("M");
}
///////////////////////////////////////////////////////
///////////////////////////////////////////////////////
SHIFT = false; //Finally, after we send the character we disable SHIFT.
}
/////////////end of shift
else //If shift is not enabeled we send the normal character
{
if((char)e.bit.KEY == ',') //"," will send a space
{
Serial.print(" ");
}
else if((char)e.bit.KEY == '!') //"!" will send a ^ for RETURN button
{
Serial.print("^"); //Return
}
else if((char)e.bit.KEY == '-') //"-" will send a - for OK button
{
Serial.print("}"); //OK
}
else
{
Serial.print((char)e.bit.KEY); //If not we just send the pressed character
}
}
/////////////end of NO shift
}
}
}
delay(1); //Small belay between each loop
}