#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Preferences.h>
#include <esp_timer.h>
Preferences pref;
// Declaration for SSD1306 display connected using I2C
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
portMUX_TYPE muxTimer = portMUX_INITIALIZER_UNLOCKED;
portMUX_TYPE muxIgnition = portMUX_INITIALIZER_UNLOCKED;
portMUX_TYPE muxLightSwitch = portMUX_INITIALIZER_UNLOCKED;
portMUX_TYPE muxEncoder = portMUX_INITIALIZER_UNLOCKED;
volatile boolean bolTimerExpired = false;
volatile boolean bolStartTheTimer = false;
volatile boolean bolUpdateDisplayDelay = false;
volatile boolean bolUpdateDisplayIgnition = false;
volatile boolean bolUpdateDisplayLights = false;
volatile boolean bolUpdateProgressBar = false;
volatile uint8_t intKeyTurnedOn = 0;
volatile uint8_t intKeyTurnedOff = 0;
volatile uint8_t intLightsTurnedOn = 0;
volatile uint8_t intLightsTurnedOff = 0;
volatile uint8_t intCounter = 1;
volatile uint8_t intReset = 0;
volatile uint8_t intMaxDelay = 0;
volatile boolean bolHideData = false;
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
static const uint8_t intDelayIncrement = 5; //30;
// Generated with Lopka
static const unsigned char PROGMEM image_Pin_star_bits[] = {0x92,0x54,0x38,0xfe,0x38,0x54,0x92};
static const unsigned char PROGMEM image_Charging_lightning_mask_bits[] = {0x01,0x00,0x02,0x00,0x04,0x00,0x08,0x00,0x1e,0x00,0x3c,0x00,0x08,0x00,0x10,0x00,0x20,0x00,0x40,0x00};
int pinLightsRelay = 16;
int pinSetDelayControl = 32;
int pinIgnitionVolts = 34;
int pinLightVolts = 35;
int pinEncoderCLK = 26;
int pinEncoderDT = 27;
int pinEncoderSW = 33;
int pinDisplaySCL = 22;
int pinDisplaySDA = 21;
int lastIgnMillis = 0;
int lastSwMillis = 0;
int lastEncMillis = 0;
// Progress bar update variables
static const int intProgressWidth = 112;
uint32_t intTotalTicks = 0;
uint32_t intNextTick = 0;
int intTickCounter = 0;
static void timer0_callback(void* arg);
esp_timer_handle_t timer0;
uint64_t timerStartingTime=esp_timer_get_time();
uint64_t timerCurTime=esp_timer_get_time();
// Timer callback
void IRAM_ATTR timer0_callback(void* arg)
{
portENTER_CRITICAL_ISR(&muxTimer);
bolTimerExpired = true;
Serial.println("Timer Expired");
portEXIT_CRITICAL_ISR(&muxTimer);
}
void IRAM_ATTR isrEncoderCLK(){
if(millis() - lastEncMillis > 10){ // Software debouncing buton
portENTER_CRITICAL_ISR(&muxEncoder);
int dtValue = digitalRead(pinEncoderDT);
if (dtValue == HIGH) {
intCounter++; // Clockwise
}
if (dtValue == LOW) {
intCounter--; // intCounterclockwise
}
intCounter = (intCounter == 255)?(0):(intCounter);
intCounter = (intCounter >= intMaxDelay)?(intMaxDelay):(intCounter);
pref.begin("LightsOut",false);
pref.putUInt("TimerDelay", intCounter);
bolUpdateDisplayDelay = true;
portEXIT_CRITICAL_ISR(&muxEncoder);
}
lastEncMillis = millis();
}
void IRAM_ATTR isrEncoderSW(){
if(millis() - lastEncMillis > 10){ // Software debouncing buton
portENTER_CRITICAL_ISR(&muxEncoder);
pref.begin("LightsOut",false);
pref.putUInt("TimerDelay", intCounter);
Serial.println(pref.getUInt("TimerDelay"));
portEXIT_CRITICAL_ISR(&muxEncoder);
}
lastEncMillis = millis();
// REMOVE - HideData is a convenient place to trigger functions for test
bolHideData = true;
}
void IRAM_ATTR isrIgnition(){
if(millis() - lastIgnMillis > 10){ // Software debouncing buton
portENTER_CRITICAL_ISR(&muxIgnition);
if (digitalRead(pinIgnitionVolts) == LOW) {
Serial.println("Rising edge detected");
intKeyTurnedOn = 1;
intKeyTurnedOff = 0;
digitalWrite(pinLightsRelay, HIGH);
} else {
Serial.println("Falling edge detected");
intKeyTurnedOff = 1;
intKeyTurnedOn = 0;
digitalWrite(pinLightsRelay, HIGH);
bolStartTheTimer = true;
}
bolUpdateDisplayIgnition = true;
portEXIT_CRITICAL_ISR(&muxIgnition);
}
lastIgnMillis = millis();
}
void IRAM_ATTR isrLightSwitch(){
if(millis() - lastSwMillis > 10){ // Software debouncing buton
portENTER_CRITICAL_ISR(&muxLightSwitch);
if (digitalRead(pinLightVolts) == LOW) {
Serial.println("Light Switch is ON");
intLightsTurnedOn = 1;
intLightsTurnedOff = 0;
digitalWrite(pinLightsRelay, HIGH);
bolStartTheTimer = true;
} else {
Serial.println("Light Switch is OFF");
intLightsTurnedOn = 0;
intLightsTurnedOff = 1;
digitalWrite(pinLightsRelay, HIGH);
}
bolUpdateDisplayLights = true;
portEXIT_CRITICAL_ISR(&muxLightSwitch);
}
lastSwMillis = millis();
}
void ShowDelay()
{
display.setTextSize(1);
display.setTextColor(WHITE);
int totalSeconds = intCounter * 30;
int seconds = totalSeconds % 60;
int minutes = totalSeconds / 60;
char buffer[15];
sprintf(buffer, "%02d:%02d", minutes, seconds);
display.fillRect(43, 31, 80, 12, 0); // blackout
display.setCursor(43, 35);
display.println(buffer);
display.display();
}
void ShowClearAll(){
display.fillRect(42, 33, 80, 12, 0); // blackout
display.fillRect(97, 20, 22, 12, 0); // blackout
display.fillRect(31, 20, 22, 12, 0); // blackout
display.display();
}
void ShowHome()
{
display.setTextSize(1);
display.setTextColor(WHITE);
display.drawRoundRect(1, 1, 126, 62, 3, 1);
display.drawLine(1, 15, 126, 15, 1);
display.setTextWrap(false);
display.setCursor(22, 5);
display.print("Lights... OUT!");
display.setCursor(6, 36);
display.print("Delay:");
display.setCursor(43, 35);
display.print("10:00");
display.drawRect(6, 45, 116, 15, 1);
display.fillRect(8, 47, 112, 11, 1);
display.setCursor(7, 22);
display.print("Key: Lights:");
display.drawBitmap(100, 22, image_Pin_star_bits, 7, 7, 1);
display.drawBitmap(31, 21, image_Charging_lightning_mask_bits, 9, 10, 1);
}
void loop()
{
if(bolUpdateDisplayDelay){
ShowDelay();
bolUpdateDisplayDelay = false;
}
if(bolUpdateDisplayIgnition){
if(intKeyTurnedOn){
display.drawBitmap(31, 21, image_Charging_lightning_mask_bits, 9, 10, 1);
}else {
display.fillRect(31, 20, 22, 12, 0); // blackout
}
display.display();
bolUpdateDisplayIgnition = false;
}
if(bolUpdateDisplayLights){
if(intLightsTurnedOn){
display.drawBitmap(100, 22, image_Pin_star_bits, 7, 7, 1);
}else {
display.fillRect(97, 20, 22, 12, 0); // blackout
}
display.display();
bolUpdateDisplayLights = false;
}
if(bolHideData){
ShowClearAll();
bolHideData=0;
}
if(bolTimerExpired){
if (digitalRead(pinIgnitionVolts) == LOW){
digitalWrite(pinLightsRelay, LOW);
}
bolTimerExpired = false;
}
if(bolStartTheTimer){
intTotalTicks = intCounter * intDelayIncrement * uS_TO_S_FACTOR;
Serial.print("Start timer totalticks: ");
Serial.println(intTotalTicks);
esp_timer_stop(timer0);
esp_timer_start_once(timer0, intTotalTicks);
timerStartingTime=esp_timer_get_time();
Serial.print("Start timer get time: ");
Serial.println(timerStartingTime);
intNextTick = intTotalTicks;
intTickCounter = 0;
bolStartTheTimer = false;
bolUpdateProgressBar = true;
}
if(bolUpdateProgressBar){
timerCurTime=esp_timer_get_time();
Serial.print("Update bar get time: ");
Serial.println(timerStartingTime);
Serial.println(timerCurTime);
Serial.println((timerCurTime - timerStartingTime)/1000000);
if(esp_timer_get_time() <= intNextTick){
intTickCounter++;
intNextTick = intNextTick - (intNextTick/intProgressWidth);
display.fillRect(8, 47, intProgressWidth - intTickCounter, 11, 1);
display.display();
}
if (intTickCounter >= intProgressWidth){
bolUpdateProgressBar = false;
display.fillRect(8, 47, intProgressWidth, 11, 1);
display.display();
}
}
}
void setup()
{
Serial.begin(9600);
pinMode(pinLightsRelay, OUTPUT);
pinMode(pinSetDelayControl, INPUT);
pinMode(pinIgnitionVolts, INPUT_PULLUP);
attachInterrupt(pinIgnitionVolts, isrIgnition, CHANGE);
pinMode(pinLightVolts, INPUT_PULLUP);
attachInterrupt(pinLightVolts, isrLightSwitch, CHANGE);
pinMode(pinEncoderDT, INPUT);
pinMode(pinEncoderCLK, INPUT);
attachInterrupt(digitalPinToInterrupt(pinEncoderCLK), isrEncoderCLK, FALLING);
pinMode(pinEncoderSW, INPUT_PULLUP);
attachInterrupt(pinEncoderSW, isrEncoderSW, FALLING);
// initialize the OLED object
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;); // Don't proceed, loop forever
}
pref.begin("LightsOut", false);
pref.putUInt("MaxDelay", 40);
intMaxDelay = pref.getUInt("MaxDelay", 40);
intCounter = pref.getUInt("TimerDelay");
display.clearDisplay();
ShowHome();
display.display();
const esp_timer_create_args_t timer0_args = {
.callback = &timer0_callback,
.name = "DelayTimer"
};
esp_timer_create(&timer0_args, &timer0);
delay(2000);
}