here original code.
// KY-040 encoder for timer
#define NOT !
#define ASSERTED !
// LED config
#define BlinkHalfPeriod 500UL
#define ChangeAfter 5000UL
#define BlinkPin 13
/*-----( Import needed libraries )-----*/
#include <Wire.h> // Comes with Arduino IDE
// Get the LCD I2C Library here:
// https://bitbucket.org/fmalpartida/new-liquidcrystal/downloads
// Move any other LCD libraries to another folder or delete them
// See Library "Docs" folder for possible commands etc.
// Simple Rotary library will need to be installed via library manager
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <SimpleRotary.h>
/*-----( Declare Constants )-----*/
/* Maximum number of timer minutes we can set. */
const int MAX_MINUTES = 60;
const unsigned long DEBOUNCE_INTERVAL = 100; // msec
typedef class _DBSwitch {
private:
bool state;
bool lastState;
int pinNumber;
unsigned long last_change;
public:
_DBSwitch(int pPinNumber) {
pinNumber = pPinNumber;
state = lastState = false;
last_change = 0;
(void) query();
}
bool query() {
unsigned long now = millis();
bool value = digitalRead(pinNumber);
if ( value != lastState ) {
last_change = now;
} else {
if ( (value != state) && ( last_change + DEBOUNCE_INTERVAL ) < now ) {
state = value;
}
}
lastState = value;
return state;
}
} DBSwitch;
typedef enum OPERATING_STATE {
OST_IDLE,
OST_RESET_PENDING,
OST_SET,
OST_RUNNING,
OST_EXPIRED1,
OST_EXPIRED2
} OPERATING_STATE;
static OPERATING_STATE operating_state = OST_IDLE;
/*-----( Declare objects )-----*/
// set the LCD address to 0x27 for a 16 chars 2 line display
// A FEW use address 0x3F
// Set the pins on the I2C chip used for LCD connections:
LiquidCrystal_I2C lcd(0x27, 16, 2); // Set the LCD I2C address
// if the LCD is parallel then uncomment the line below instead - pins may need to change
// LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address
// set up rotary encoder Pin A, Pin B, Button Pin
SimpleRotary rotary(3, 2, 4);
/*-----( Declare Variables )-----*/
const int RELAY1_PIN = 7;
const int RELAY2_PIN = 8;
//const int KY_CLK_PIN = 2; // KY-040 CLK
//const int KY_DT_PIN = 3; // KY-040 DT
//const int KY_SET_PIN = 4; // KY-040 Pushbutton
const int RESET_PIN = 5;
const int INHIBIT_PIN = 6;
const int SECURITY_PIN = 9;
//int pinA = 3; // Connected to CLK on KY-040
//int pinB = 4; // Connected to DT on KY-040
int encoderPosCount = 30;
int pinALast;
int aVal;
DBSwitch swReset(RESET_PIN);
//DBSwitch swSetTimer(KY_SET_PIN);
bool swResetLast = true;
//bool swSetTimerLast;
bool swInhibitLast;
//boolean bClockwise; // rotation direction
//--
int minutes_remaining = 0;
int seconds_remaining = 0;
unsigned long time_started = 0L;
unsigned long next_tick = 0L;
void setup() {
//LED PIN
pinMode(BlinkPin, OUTPUT);
//pinMode (KY_CLK_PIN, INPUT);
// pinMode (KY_DT_PIN, INPUT);
pinMode (SECURITY_PIN, INPUT_PULLUP);
pinMode (INHIBIT_PIN, INPUT_PULLUP);
swInhibitLast = digitalRead(INHIBIT_PIN);
pinMode (RESET_PIN, INPUT_PULLUP);
//pinMode (KY_SET_PIN, INPUT_PULLUP);
//swSetTimerLast = digitalRead(KY_SET_PIN);
bool sw = swReset.query();
bool signalReset = false;
bool swResetLast = false;
if ( sw && ! swResetLast ) { // toggle
signalReset = true;
}
swResetLast = sw;
pinMode(RELAY1_PIN, OUTPUT);
pinMode(RELAY2_PIN, OUTPUT);
/* Read Pin A
Whatever state it's in will reflect the last position
*/
// pinALast = digitalRead(KY_CLK_PIN);
Serial.begin (9600);
Serial.println("Starting...");
lcd.init(); // initialize the lcd
lcd.backlight(); // finish with backlight on
lcd.setCursor(0, 0); //Start at character 4 on line 0
lcd.print("Starting...");
// Restore timer from EEPROM
reset_relays();
encoderPosCount = timer_load();
timer_restart(encoderPosCount);
setOperatingState(OST_RUNNING); // IDLE
}
void loop() {
// LED control
static uint32_t TimerPeriodes = 0;
static uint32_t TimerBliking = 0;
static bool isBlinkEnable = false;
if ( millis() - TimerPeriodes >= ChangeAfter) {
TimerPeriodes += ChangeAfter;
isBlinkEnable = !isBlinkEnable;
}
if (isBlinkEnable) {
if ( millis() - TimerBliking >= BlinkHalfPeriod) {
TimerBliking += BlinkHalfPeriod;
digitalWrite(BlinkPin, !digitalRead(BlinkPin));
}
} else digitalWrite(BlinkPin, LOW);
//if ( NOT digitalRead(INHIBIT_PIN) ) {
// delay(500);
// return;
//}
unsigned long now = millis();
bool keyLock = ! digitalRead(SECURITY_PIN);
//bool kyKeyPress = digitalRead(KY_SET_PIN);
if ( ! keyLock ) {
//lcd.clear();
lcd.setCursor(0, 1); //Start at character 0 on line 1
lcd.print("Unlocked");
handle_locked_operations(now);
} else {
//lcd.clear();
lcd.setCursor(0, 1); //Start at character 0 on line 1
lcd.print("Locked ");
}
bool resetSignalled = false;
// ****** Handle Reset button ******
bool sw = ASSERTED swReset.query();
if ( sw ) { // Reset held down
if ( NOT swResetLast ) {
// Timer interrupted
lcd.clear();
lcd.setCursor(2, 1); //Start at character 2 on line 1
lcd.print("** RESET **");
delay(1000);
swResetLast = true;
}
return;
}
if ( swResetLast ) { // also return from INHIBIT
resetSignalled = true;
swResetLast = false;
reset_relays();
lcd.clear();
timer_restart(encoderPosCount);
setOperatingState(OST_RUNNING);
}
switch (operating_state) {
case OST_IDLE:
case OST_SET:
handle_timer_set();
// Also check for "SET" button pressed to exit state.
break;
case OST_RUNNING:
if ( now > next_tick ) {
timer_tick_seconds();
timer_display_count();
if (( minutes_remaining == 0) && (seconds_remaining == 0)) {
// FIRE RELAY 1!!!
digitalWrite(RELAY1_PIN, 1);
isBlinkEnable = true;
setOperatingState(OST_EXPIRED1);
timer_restart(1); // One minute EXPIRED count
}
}
break;
case OST_EXPIRED1:
if ( now > next_tick ) {
timer_tick_seconds();
timer_display_count();
if (seconds_remaining == 0) {
// FIRE RELAY 2!!!
digitalWrite(RELAY2_PIN, 1);
setOperatingState(OST_EXPIRED2);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("*** EXPIRED ***");
}
}
break;
case OST_EXPIRED2:
break;
}
}
/**
Process things that can only be done if the security switch is not set.
*/
void handle_locked_operations(const unsigned long now) {
// *** Timer set sw
bool keyLock = ! digitalRead(SECURITY_PIN);
if ( !keyLock) {
byte j;
// detect if rotary encoder button pressed 0 = not pushed, 1 = pushed
j = rotary.push();
// ONLY allow set-mode if SECURITY SWITCH is ON
if ( j == 1 ) { // act on button-release
if ( operating_state == OST_SET ) {
timer_save(encoderPosCount);
timer_restart(encoderPosCount);
setOperatingState(OST_RUNNING);
}
else {
setOperatingState(OST_SET);
display_timer_set();
}
}
//swSetTimerLast = sw;
}
}
/**
Return the relays to their "OFF" settings.
*/
void reset_relays() {
digitalWrite(RELAY1_PIN, 0);
digitalWrite(RELAY2_PIN, 0);
//isBlinkEnable = false;
}
/**
When we are in OST_SET state, we read the encoder and
set the timer.
*/
void handle_timer_set() {
lcd.clear();
lcd.setCursor(0, 0); //Start at character 0 on line 1
lcd.print("Timer set:");
lcd.setCursor(2, 1); //Start at character 2 on line 1
char buf[3];
buf[2] = 0;
lcd.print(format_rj2( buf, encoderPosCount));
lcd.setCursor(5, 1);
lcd.print( " Minutes");
do {
lcd.setCursor(2, 1); //Start at character 2 on line 1
byte i;
// 0 = not turning, 1 = CW, 2 = CCW - you may need to swap these depending on the encoder and pins
i = rotary.rotate();
if ( i == 2 && encoderPosCount < MAX_MINUTES) {
encoderPosCount++;
char buf[3];
buf[2] = 0;
lcd.print(format_rj2( buf, encoderPosCount));
}
if ( i == 1 && encoderPosCount > 1) {
encoderPosCount--;
char buf[3];
buf[2] = 0;
lcd.print(format_rj2( buf, encoderPosCount));
}
}
while (rotary.push() == 0);
timer_save(encoderPosCount);
timer_restart(encoderPosCount);
setOperatingState(OST_RUNNING);
// byte i;
// 0 = not turning, 1 = CW, 2 = CCW
//i = rotary.rotate();
//if (i==1) { // - We're Rotating Clockwise
/// if ( encoderPosCount < MAX_MINUTES )
// encoderPosCount ++;
// else encoderPosCount = MAX_MINUTES;
// }
// if (i==2) {// Otherwise B changed first and we're moving CCW
// if ( encoderPosCount > 1 ) {
// encoderPosCount--;
// }
// }
// display_timer_set();
}
static void display_timer_set() {
//lcd.clear();
lcd.setCursor(0, 0); //Start at character 0 on line 1
lcd.print("Timer set:");
lcd.setCursor(2, 1); //Start at character 2 on line 1
char buf[3];
buf[2] = 0;
//lcd.print(format_rj2( buf, encoderPosCount));
lcd.print( " Minutes");
}
static void setOperatingState(OPERATING_STATE ost) {
if ( ost != operating_state ) {
lcd.clear();
}
operating_state = ost;
}
/**
Restart the time count.
*/
static void timer_restart(int minutes) {
unsigned long now = millis();
time_started = now;
//Serial.print("Timer Restart "); Serial.println(encoderPosCount);
minutes_remaining = minutes; // MINUTES - todo - pull from EEPROM
seconds_remaining = 0;
next_tick = now + 1000; // msec = 1 second
}
/**
Count down 1 second intervals
*/
static void timer_tick_seconds() {
seconds_remaining--;
if ( seconds_remaining < 0 ) {
seconds_remaining = 59;
minutes_remaining--;
}
next_tick = next_tick + 1000; // msec = 1 second
}
/**
Display current time remaining.
*/
static void timer_display_count() {
lcd.setCursor(0, 0); //Start at character 0 on line 0
char buf[3];
if ( minutes_remaining > 0 ) {
lcd.print(format_rj2(buf, minutes_remaining));
lcd.print(" Min. remain");
} else {
lcd.print(format_rj2(buf, seconds_remaining));
lcd.print(" Sec. remain");
}
if ( operating_state == OST_EXPIRED1 ) {
lcd.setCursor(0, 1); //Start at character 0 on line 0
lcd.print("Expired");
}
}
/**
Right-justified format of a 2-digit number
*/
static char* format_rj2(char* buf, int n) {
if ( n > 99 ) { // limit range.
n = n % 100;
}
buf[1] = (n % 10) + '0';
buf[0] = (n / 10) + '0';
buf[2] = 0;
if ( buf[0] == '0') {
buf[0] = ' '; // leading space
}
return buf;
}
//**** EEPROM ****
const int ee_addr0 = 0;
static void timer_save(int count) {
EEPROM.update(ee_addr0, (count >> 8) & 0xff);
EEPROM.update(ee_addr0 + 1, (count) & 0xff);
}
static int timer_load() {
int val = EEPROM.read(ee_addr0) << 8;
val |= EEPROM.read(ee_addr0 + 1);
return val;
}