The program will ask for a pin when you want to enable the circuit and if entered correctly close the circuit via a relay, if false it will leave the relay open disabling the circuit.
Parts List
- Arduino Mini Pro (5V 16Mhz but anyother Arduino will work as well)
- USB-FTTL Breakout board to program the arduino with (only necessary when there is no onboard USB plug)
- 4x4 Membrane keypad
- 20x4 LCD
- 1k Ohm resistor
- 4x 10k Ohm resistor
- 0,1nF Capacitor
- 10k Ohm potentiometer
- Relay board for switching highcurrent circuit (for this tutorial I'll be using a ready made board Keyes_SR1y)
- 5V power supply (for this tutorial I'm using a 240V AC to 5V DC 420mA smartphone charger which I disassembled and connected directly to the power plug for the controlled circuit)
- Jumper wires
For building the Hardware box (not necessary to complete this tutorial)
- Generic Black Casing
- A plexi glas plate to cover the hole for the LCD
- Power cable that drives the hardware to protect with the circuit locker
- Prototype PCB board to solder everything onto
For Simulating the high current circuit
- an LED (for this tutorial I'm using a big green LED)
- 330 Ohm Resistor
Setting up the Circuit
Connecting the LCD
- VSS - connect to GND
- VDD - This supplies power to the LCD. Connect to the 5V VCC. Parallel we connect the 0,1nF capacitor to VCC and GND to smooth out any electric peaks when switching on the power.
- V0 - this controls the contrast of the LCD. Connect it to the middle pin of the potentionmeter. The other two pins of the potentiometer are connected to 5V VCC and GND respectively.
- RS - The register shift signal. It is used to signal the LCD to shift the written date to the display. Connect it to digital pin 2.
- RW - The read/write signal. We are only writing to the LCD and do not need to read from it so we simply set this pin to GND. (Signal Low)
- E - this is the enable signal of the LCD. Connect it to digital pin 3.
- D0-D3 - since we are only going to use 4bit character mode we will not need these pins.
- D4-D7 - these connection transfer the data to the LCD. Connect these to digital pin 4-7.
- A - this is the anode of the backlight LED. Connect this to the one end of the 1k Ohm resistor. Connect the other end of the resistor to the analog pin A2 which we will use as a digital output pin to control the backlight of the LCD via the Arduino.
- K - this is the kathode of the backlight LED. Connect it to GND.
Connecting the Keypad
A pull down resistor ensures that the signal on the pin gets pulled to 0V (LOW) even though there might be small currents induced by environment interference which can potentially shift the pins to HIGH. The pull down resistor uses the fact that electricity always flows along the path of least resistance. Since a pin that has no signal applied to it has a very high resistance the current flows through the pull down resistor to ground ensuring a level of 0V. When the pin is read its resistance is lower then that of the resistor and the current flows through the pin.
Once you have everything setup you can use the "Test the Keypad" sketch to test whether everything is working.
Connecting the Relay
Connect the "+" pin from the relay board to the 5V from the power supply and "-" to GND. The signal pin "S" is connected to pin A3. Although A3 is an analog pin it can still be used as a digital one. Instead of connecting an acutal high current circuit to the relay we will instead connect an LED to make the opening and closing visible.The relay has two connection types NC (Normally Closed) and NO (Normally Open). To understand their meaning it is important to know that a relay is an electromagnetic switch which opens or closes depending on whether a signal is applied to it or not.
Take a look at the simplified schematic on the left. The relay consists of a spindle and a switch which gets triggered by the magnetic field induced by the spindle.
The circuit for the spindle is interrupted by a transistor. The transistor becomes conductive when a signal is applied to S. When the spindle becomes conductive the current flowing through it induces a magnetic field which flips the switch on the right side closing the circuit between NO and GND and at the same time opening the circuit between NC and GND.
This explains the two operation modes of the relay:
NC (Normally Closed) - The relay is closed when there is no signal on S. When the signal turns HIGH the relay opens the switch and the circuit is interrupted.
NO (Normally Open) - The relay is open when there is no signal on S. When a signal is applied to S the relay closes the circuit and current can flow.
Since I want to control when the circuit is closed I'm going to use NO mode. Connect the cathode (the shorter leg, the side that is flattened) of the LED to the NO connection of the relay and connect the GND of the relay to GND. Connect the anode of the LED to the one end of the 330 Ohm resistor. The other end of the resistor you connect to 5V VCC. We have now a circuit which is open since the relay has not been switched on yet and we are in NO mode.
Do not use the above simplified schematic to setup your own relay circuit. If you do not protect against backflowing currents caused by the inductive nature of the spindle it might destroy your Arduino.
To test that we have done our setup correctly run the "Test the Relay" sketch. If everything is correct the relay should switch every 5 seconds turning the LED on and off.
Arduino Sketches
Test the LCD
The sketch sets up the necessary pins for the LCD and creates the LiquidCrystal object passing the pins in the constructor. It then prints out the message "Setup and Running" and prints the time since when the sketch started running. To calculate the time it uses the millies() seconds which returns the milliseconds since the sketch started running. Bare in mind since the time is given in milliseconds and it is saved to an unsigned long it will eventually overflow and restart by zero. According to the documentation this will occur around the fifties day.
/** * Copyright (C) 2015 Stefan Langer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses. * * This program uses parts of the arduino core libraries published by Arduino LLC * which are released under the LGPL v2.1 or later. * http://www.gnu.org/licenses/lgpl * * All hardware, documentation and files distributed with this software that do * not fall under the GPL are distributed under the * Creative Commons Attribution-ShareAlike 4.0 International License * http://creativecommons.org/licenses/by-sa/4.0 */ #include <LiquidCrystal.h> // define the pins to use for the LCD #define RS_PIN 2 #define EN_PIN 3 #define D0_PIN 4 #define D1_PIN 5 #define D2_PIN 6 #define D3_PIN 7 #define LCD_BACKLED 16 // Analog pin A0 // CONSTANTS for managing time #define SECOND 1000 // millis in one second #define MINUTE 60000 // millis in one minute #define HOUR 3600000 // millis in one hour #define DAY 86400000 // millis in one day // alias for datatpyes #define ulong unsigned long // initialize LCD with predefined pins LiquidCrystal lcd(RS_PIN, EN_PIN, D0_PIN, D1_PIN, D2_PIN, D3_PIN); // helper function to print time on the lcd. void printActiveTime() { ulong time = millis(); ulong days = time / DAY; ulong hours = (time / HOUR) % 24; ulong minutes = (time / MINUTE) % 60; ulong seconds = (time / SECOND) % 60; int dIdx = 1; if (days >= 10) dIdx = 0; lcd.setCursor(2 + dIdx, 2); lcd.print(days); lcd.print('d'); if (hours < 10) lcd.print('0'); lcd.print(hours); lcd.print('h'); if (minutes < 10) lcd.print('0'); lcd.print(minutes); lcd.print('m'); if (seconds < 10) lcd.print('0'); lcd.print(seconds); lcd.print('s'); } // Setup backlight led pin and start LCD // print status to LCD once we are done void setup() { pinMode(LCD_BACKLED, OUTPUT); digitalWrite(LCD_BACKLED, HIGH); lcd.begin(20, 4); lcd.print("Setup and Running!"); lcd.setCursor(0, 1); lcd.print("Running since:"); } // print current time to the lcd void loop() { printActiveTime(); delay(200); }
TestLCD sketch on GitHub
Output running the TestLCD sketch
Test the Keypad
The keypad is a simple grid of wires for rows and columns where each keypad lies on an intersection which is a button that closes the circuit when pressed.
Create a new sketch, name it TestKeypad and copy the following code to it.
/** * Copyright (C) 2015 Stefan Langer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses * * This program uses parts of the arduino core libraries published by Arduino LLC * which are released under the LGPL v2.1 or later. * http://www.gnu.org/licenses/lgpl * * All hardware, documentation and files distributed with this software that do * not fall under the GPL are distributed under the * Creative Commons Attribution-ShareAlike 4.0 International License * http://creativecommons.org/licenses/by-sa/4.0 */ // define the pins used for the rows #define ROW1 15 #define ROW2 14 #define ROW3 13 #define ROW4 12 // define the pins used to read the state of the columns #define COL1 11 #define COL2 10 #define COL3 9 #define COL4 8 #define uint unsigned int // helper constructs to easily manage the rows and columns const uint ROWS[] = {ROW1, ROW2, ROW3, ROW4}; const uint COLS[] = {COL1, COL2, COL3, COL4}; // matrix representation of the keypad // the coordinate of each key is (row, column) const char LETTERS[][4] = {{'1', '2', '3', 'A'}, {'4', '5', '6', 'B'}, {'7', '8', '9', 'C'}, {'*', '0', '#', 'D'} }; /** * Returns the currently pressed key. * * @return the pressed key or -1 if * no key press was registered. */ char getPressedKey() { for (int ri = 0; ri < 4; ri++) { digitalWrite(ROWS[ri], HIGH); for (int c = 0; c < 4; c++) { if (HIGH == digitalRead(COLS[c])) { return LETTERS[ri][c]; } } digitalWrite(ROWS[ri], LOW); } return -1; } void setup() { // setup row pins for output and col pins for input // and set each one to LOW. for (int i = 0; i < 4; i++) { pinMode(ROWS[i], OUTPUT); pinMode(COLS[i], INPUT); digitalWrite(ROWS[i], LOW); digitalWrite(COLS[i], LOW); } // start serial montior for debugging purposes Serial.begin(9600); Serial.println("Initialized and ready to go!"); } void loop() { // read the current key press and if // available print it to the serial // monitor char key = getPressedKey(); if (key != -1) { Serial.print("Got key press: "); Serial.println(key); } // prevent double clicks delay(200); }
Video running the TestKeypad sketch
Test the Relay
It does so by configuring the relay pin for output and then simply sets it to HIGH to close the relay and to LOW to open it. When the relay is closed the circuit with the LED is closed and the LED lights up. When the relay is open the LED circuit is interrupted and it turns off. If you listen closley you can hear a click sound coming from the relay when it flips the switch.
Create a new sketch, name it TestRelay and copy the following code to it.
/** * Copyright (C) 2015 Stefan Langer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see http://www.gnu.org/licenses * * This program uses parts of the arduino core libraries published by Arduino LLC * which are released under the LGPL v2.1 or later. * http://www.gnu.org/licenses/lgpl * * All hardware, documentation and files distributed with this software that do * not fall under the GPL are distributed under the * Creative Commons Attribution-ShareAlike 4.0 International License * http://creativecommons.org/licenses/by-sa/4.0 */ // define the relais pin #define RELAIS_PIN 17 void setup() { pinMode(RELAIS_PIN, OUTPUT); digitalWrite(RELAIS_PIN, LOW); } // turn relais pin HIGH wait 5 seconds then turn it LOW // and wait another 5 seconds void loop() { digitalWrite(RELAIS_PIN, HIGH); delay(5000); digitalWrite(RELAIS_PIN, LOW); delay(5000); }
Video running the TestRelay sketch
Saving the PIN to EEPROM
Side Note
Assuming that we set the pin each day once. This would mean we could write 100.000 times to the same address location in EEPROM until it fails.Doing the math:
100.000 / 365 = 273,973 (rounded up)
this gives us 273 years until EEPROM fails. Since we are not expecting the pin to change that often we are more then fine to use EEPROM for storing the data.
In order to not burn through those precious cycles I recommend to do the actual writing in a separate function and simply implement a dummy code that simply puts debug output to the serial port during developement. Once you are confident that your code is doing what it is supposed to do I would replace this with the actual code writing to EEPROM reducing the needed cycles during development to a minimum. This can be fairly easy automated with #define and #ifdef, #else, #endif.
#define DEBUG #ifdef DEBUG Serial.println("Put code to execute during development here!"); #else // Actual code to write to EEPROM EEPROM.write(0, save_struct); #endif
I will use the EEPROM library delivered with the Arduino IDE to write and read to and from this memory. The code to write to the EEPROM is very simply since I'm just going to write each character as a single byte to memory.
/** * Writes the specified password to EEPROM. * * @param pwd Password to store in EEPROM */ void writeToEEPROM(String pwd) { // make sure you are not writing passwords longer // then 511 characters. if(pwd.length() >= 512) return; // Debug output Serial.print("Writing "); Serial.print(pwd); Serial.println(" to EEPROM!"); for(int i=0;i<pwd.length();i++) { EEPROM.update(i,pwd[i]); } EEPROM.update(pwd.length(),255); }
Reading the EEPROM is the opposite. Read in all values from EEPROM using byte read(int address) until we reach a value that is equal to 255.
/** * Initialize the password from EEPROM. * * @return The password or empty string when no password has been stored yet. */ String initializePasswordFromEEPROM() { int value; String retVal = ""; for(int i=0; i<512;i++) { value=EEPROM.read(i); if(value == 255) { break; }else { retVal += (char)value; } } // Debug output Serial.print("Read "); Serial.print(retVal); Serial.println(" from EEPROM!"); return retVal; }
Once that code is in place we simply have to initialize the secret from EEPROM in our setup routine and then always write it back to EEPROM when we change it.
The Circuit Locker
The application uses the LCD to communicate with the user and the keypad for entering the pin and navigating the menus. The user can change the pin and it is written to the EEPROM of the microcrontroller to persist it across restarts.
Create a new sketch, name it CircuitLocker and copy the following code to the sketch. The code can also be downloaded from Github.
/**
* Copyright (C) 2015 Stefan Langer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses
*
* This program uses parts of the arduino core libraries published by Arduino LLC
* which are released under the LGPL v2.1 or later.
* http://www.gnu.org/licenses/lgpl
*
* All hardware, documentation and files distributed with this software that do
* not fall under the GPL are distributed under the
* Creative Commons Attribution-ShareAlike 4.0 International License
* http://creativecommons.org/licenses/by-sa/4.0
*/
#include <avr/pgmspace.h>
#include <EEPROM.h>
#include <LiquidCrystal.h>
// pins for the LCD
#define RS_PIN 2
#define EN_PIN 3
#define D0_PIN 4
#define D1_PIN 5
#define D2_PIN 6
#define D3_PIN 7
#define LCD_BACKLED 16
// the relay pin
#define RELAIS_PIN 17
// aliases for datatypes
#define uint unsigned int
#define ulong unsigned long
// cycles to count befor shutting off display
#define TIMEOUT 150 // approximately 30 seconds
// CONSTANTS for time management
#define SECOND 1000 // millis in one second
#define MINUTE 60000 // millis in one minute
#define HOUR 3600000 // millis in one hour
#define DAY 86400000 // millis in one day
// keypad row pins
#define ROW1 15
#define ROW2 14
#define ROW3 13
#define ROW4 12
// keypad column pins
#define COL1 11
#define COL2 10
#define COL3 9
#define COL4 8
const uint ROWS[] = {ROW1, ROW2, ROW3, ROW4};
const uint COLS[] = {COL1, COL2, COL3, COL4};
const char LETTERS[][4] = {{'D', 'C', 'B', 'A'},
{'#', '9', '6', '3'},
{'0', '8', '5', '2'},
{'*', '7', '4', '1'}
};
// CONSTANTS used to store the customized lock characters to the LCD
#define LOCK_GLYPH 0
#define UNLOCK_GLYPH 1
// defines the locked lock character
byte lockGlyph[8] = {
B00000,
B01110,
B10001,
B10001,
B11111,
B11111,
B11111,
B00000
};
// defines the unlocked lock character
byte unlockGlyph[8] = {
B00000,
B01110,
B00001,
B00001,
B11111,
B11111,
B11111,
B00000
};
LiquidCrystal lcd(RS_PIN, EN_PIN, D0_PIN, D1_PIN, D2_PIN, D3_PIN);
String secret;
boolean circuitLocked, isDisplayOn;
int displayTimeout;
/**
* Prints the lock state to the first character of the last line.
* The lock state is represented by a lock which is either closed
* or open depending on whether the circuit is locked or unlocked.
* <em>This method is only executed when the display is turned on</em>
*/
void printLockState() {
if (!isDisplayOn) return;
lcd.setCursor(0, 3);
if (circuitLocked) lcd.write(byte(LOCK_GLYPH));
else lcd.write(byte(UNLOCK_GLYPH));
}
/**
* Prints the status line <pre>
* <LockStatus> 0d00h00m00s
* </pre>
* to the last line of the LCD.
* <em>This method is only executed when the display is turned on</em>
*/
void printStatusLine() {
if (!isDisplayOn) return;
ulong time = millis();
ulong days = time / DAY;
ulong hours = (time / HOUR) % 24;
ulong minutes = (time / MINUTE) % 60;
ulong seconds = (time / SECOND) % 60;
printLockState();
int dIdx = 1;
if (days >= 10)
dIdx = 0;
lcd.setCursor(8 + dIdx, 3);
lcd.print(days);
lcd.print('d');
if (hours < 10)
lcd.print('0');
lcd.print(hours);
lcd.print('h');
if (minutes < 10)
lcd.print('0');
lcd.print(minutes);
lcd.print('m');
if (seconds < 10)
lcd.print('0');
lcd.print(seconds);
lcd.print('s');
}
/**
* Prints the option text <pre>
* Options:
* C - Change Pin
* D - Un/Lock circuit
* </pre>
* plus the status line to the LCD.
* <em>This method is only executed when the display is turned on</em>
*/
void printOptions() {
if (!isDisplayOn) return;
lcd.clear();
lcd.print("Options");
lcd.setCursor(0, 1);
lcd.print(" C - Change Pin");
lcd.setCursor(0, 2);
lcd.print(" D - ");
if (circuitLocked) lcd.print("Unlock circuit");
else lcd.print("Lock circuit");
printStatusLine();
}
/**
* Prints the text <pre>
* Enter pin:
* Press D to enter!
* </pre>
* plus the status line to the LCD.
*/
void printEnterCmd(boolean newPin) {
lcd.clear();
lcd.print(newPin? "Enter new pin" : "Enter pin:");
lcd.setCursor(0, 1);
lcd.print("Press D to enter!");
printStatusLine();
}
/**
* Returns the currently pressed key.
* To find out the current key press we iterate through the row pins
* and set each pin to HIGH. While the pin is HIGH we iterate through
* the column pins and read their signal. When we find the column
* that reads HIGH we get the pressed letter by using the index of the
* current row and the index of the current column.
* <code>key = LETTERS[rowIdx][colIdx]</code>
*
* @return the pressed key or -1 if
* no key press was registered.
*/
char getPressedKey() {
for (int ri = 0; ri < 4; ri++) {
digitalWrite(ROWS[ri], HIGH);
for (int c = 0; c < 4; c++) {
if (HIGH == digitalRead(COLS[c])) {
digitalWrite(ROWS[ri], LOW);
delay(50);
return LETTERS[ri][c];
}
}
digitalWrite(ROWS[ri], LOW);
}
delay(50);
return -1;
}
/**
* Reads in a numeric password from the keypad using {@link getPressedKey}.
* 'D' stops entry and returns the currently entered password-
* '*' deletes the last entered character
* '#' clears the complete entry
* All other keys are append to the password.
* Additionally updates the status in the LCD using {@link printStatusLine}.
*
* @param line the line in the LCD to echo the entered key presses to.
*
* @return the entered password
*/
String enterPassword(int line) {
String pin = "";
delay(100);
for (;;) { /*ever*/
char key = getPressedKey();
switch (key) {
case -1:
break;
case 'D':
return pin;
case '#':
pin.remove(0);
lcd.setCursor(0, line);
lcd.print(" ");
break;
case '*':
if (pin.length() >= 1)
pin.remove(pin.length() - 1);
lcd.setCursor(pin.length(), line);
lcd.print(" ");
break;
default:
lcd.setCursor(pin.length(), line);
pin += key;
lcd.print(key);
break;
}
printStatusLine();
// prevent double clicks
delay(200);
}
}
/**
* Asks for a password and validates it against the currently set one.
*
* @param line the line in the LCD to echo the password to
*
* @return <code>true</code> when password matches <code>false</code> otherwise.
*/
boolean enterAndValidate(int line) {
String pin = enterPassword(line);
return secret.compareTo(pin) == 0;
}
/**
* Initiates the password change algorithm:
* Check whether there is a currently set password
* - Password is not set
* a) Read the new password from keypad using {@link enterPassword(int)}
* b) Store the password to EEPROM using {@linnk changeAndStorePassword(String)}
* c) Print status message to LCD and exit
* - A password is set
* a) Ask for the set password and validate it using {@link enterAndValidate(int)}
* b) Password is correct
* - Read the new password from keypad using {@link enterPassword(int)}
* - Store the password to EEPROM using {@linnk changeAndStorePassword(String)}
* - Print status message to LCD and exit
* c) Password is incorrect
* - Print status message to LCD and exit without changing the pin
*/
void changePassword() {
String newPin;
lcd.clear();
if (isSecretSet()) {
printEnterCmd(false);
if (enterAndValidate(2)) {
lcd.clear();
printEnterCmd(true);
delay(300);
changeAndStorePassword(enterPassword(2));
} else {
lcd.clear();
lcd.print("Invalid pin entered!");
lcd.setCursor(0, 1);
lcd.print("Pin was not changed!");
}
printStatusLine();
delay(5000);
}
else {
printEnterCmd(true);
changeAndStorePassword(enterPassword(2));
}
}
/**
* Set the specified pin as the new password and writes it to EEPROM
* to ensure its persistents accross restarts.
* Before writing it to EEPROM the user is asked whether he really wants to change
* the password.
* On success the circuit is locked.
*/
void changeAndStorePassword(String pwd) {
lcd.clear();
lcd.print("Change pin to");
lcd.setCursor(0, 1);
lcd.print(pwd);
lcd.print("?");
lcd.setCursor(0, 2);
lcd.print("Yes - Press A");
lcd.setCursor(0, 3);
lcd.print("No - Press other key");
delay(200);
for (;;) { /*ever*/
switch (getPressedKey()) {
case 'A':
secret = pwd;
writeToEEPROM(pwd);
lockCircuit();
lcd.clear();
lcd.print("New pin set and");
lcd.setCursor(0, 1);
lcd.print("written to memory!");
printStatusLine();
delay(5000);
return;
case -1:
break;
default:
lcd.clear();
lcd.print("Pin was not changed!");
lcd.setCursor(0, 1);
printStatusLine();
delay(5000);
return;
}
}
}
/**
* Writes the specified password to EEPROM.
*
* @param pwd Password to store in EEPROM
*/
void writeToEEPROM(String pwd) {
// Debug output
Serial.print("Writing ");
Serial.print(pwd);
Serial.println(" to EEPROM!");
for(int i=0;i<pwd.length();i++) {
EEPROM.update(i,pwd[i]);
}
EEPROM.update(pwd.length(),255);
}
/**
* Initialize the password from EEPROM.
*
* @return The password or empty string when no password has been stored yet.
*/
String initializePasswordFromEEPROM() {
int value;
String retVal = "";
for(int i=0; i<512;i++) {
value=EEPROM.read(i);
if(value == 255) {
break;
}else {
retVal += (char)value;
}
}
// Debug output
Serial.print("Read ");
Serial.print(retVal);
Serial.println(" from EEPROM!");
return retVal;
}
/**
* Returns whether a password has been set.
* Password has not been set when the internal secret is the empty string.
*
* @return <code>true</code> when a secret is set <code>false</code> otherwise.
*/
boolean isSecretSet() {
return secret.length() > 0;
}
/**
* Opens the relay by setting relay pin to HIGH, unlocking the circuit.
* Prints status to LCD.
*/
void unlockCircuit() {
circuitLocked = false;
digitalWrite(RELAIS_PIN, HIGH);
lcd.clear();
lcd.print("Circuit unlocked!");
printStatusLine();
}
/**
* Closes the relay by setting relay pin to LOW, locking the circuit.
* Prints status to LCD.
*/
void lockCircuit() {
circuitLocked = true;
digitalWrite(RELAIS_PIN, LOW);
lcd.clear();
lcd.print("Circuit locked!");
printStatusLine();
}
/**
* Turns off the backlight led by setting the backlight pin to LOW
* and turns off the display using {@link LiquidCrystal#noDisplay()}.
*/
void turnOffDisplay() {
isDisplayOn = false;
digitalWrite(LCD_BACKLED, LOW);
lcd.noDisplay();
}
/**
* Turns on the backlight led by setting the backlight pin to HIGH
* and turns on the display using {@link LiquidCrystal#display()}.
*/
void turnOnDisplay() {
isDisplayOn = true;
displayTimeout = 0;
digitalWrite(LCD_BACKLED, HIGH);
lcd.display();
}
void setup() {
// setup pins
pinMode(LCD_BACKLED, OUTPUT);
pinMode(RELAIS_PIN, OUTPUT);
digitalWrite(RELAIS_PIN, LOW);
for (int i = 0; i < 4; i++) {
pinMode(ROWS[i], OUTPUT);
pinMode(COLS[i], INPUT);
digitalWrite(ROWS[i], LOW);
digitalWrite(COLS[i], LOW);
}
circuitLocked = true;
displayTimeout = 0;
// read previously stored password from EEPROM
secret = initializePasswordFromEEPROM();
// create the special lock symbols for LCD
lcd.createChar(LOCK_GLYPH, lockGlyph);
lcd.createChar(UNLOCK_GLYPH, unlockGlyph);
// Initialize serial monitor for debugging
Serial.begin(9600);
// Initialize lcd and turn on display
lcd.begin(20, 4);
turnOnDisplay();
// print status to lcd
lcd.print("Initialized and");
lcd.setCursor(0, 1);
lcd.print("ready to go!");
// print debug information to serial monitor
Serial.println("Initialized and ready to go!");
// wait 3 seconds and print the option menu
delay(3000);
printOptions();
}
void loop() {
// when display is on check whether the display timeout counter has
// reached TIMEOUT if it has reached TIMEOUT turn off the display
// when not then increment the display timeout counter.
if (isDisplayOn) {
if (displayTimeout >= TIMEOUT) {
turnOffDisplay();
} else {
displayTimeout++;
}
}
// see if we have a key press
// when C is pressed initiate changePassword procedure.
// when D is pressed initiate un/lock procedure.
// if display is off activate it on any keypress
char key = getPressedKey();
switch (key) {
case 'C':
if (!isDisplayOn) turnOnDisplay();
changePassword();
printOptions();
break;
case 'D':
if (!isDisplayOn) turnOnDisplay();
if (circuitLocked) {
printEnterCmd(false);
if (enterAndValidate(2)) {
unlockCircuit();
} else {
lcd.clear();
lcd.print("Invalid pin entered!");
lcd.setCursor(0, 1);
lcd.print("Circuit remains");
lcd.setCursor(0, 2);
lcd.print("locked!");
printStatusLine();
}
}
else {
lockCircuit();
}
delay(5000);
printOptions();
break;
case -1:
break;
default:
if (!isDisplayOn) turnOnDisplay();
}
// print status to last line of LCD and wait a
// short while to prevent double key presses
printStatusLine();
// Debug output
Serial.print("EEPROM password: ");
Serial.println(initializePasswordFromEEPROM());
delay(200);
}
CircuitLocker sketch on GitHubCreating the Hardware
The Circuit Locker in Action
Resources
- Arduino IDE the IDE to develop Arduino programs with
- LiquidCrystal library available as part of the Arduino IDE
- EEPROM library available as part of the Arduino IDE
- Fritzing an application developed by the University of Applied Science Potsdam to document circuits.
- CircuitLocker Github repository containing all the code from this tutorial.
Summary
- using a keypad to get HEX input,
- print output to an LCD using the LiquidCrystal library,
- use the EEPROM library to write and read from EEPROM
- use a relay to control high current circuits from your Arduino.
Feel free to use the circuit locker app for your own projects but please be so kind to give me proper credit.
I hope you enjoyed it
Stefan Langer
Circuit Locker and Tutorial by Stefan Langer is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.