#include <Key.h>
#include <Keypad.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "max6675.h"
// #include <ArduinoBleOTA.h>
// #define DEVICE_NAME "Esp32 Ble" // Ble Device Name
// #define HW_NAME "Esp32 HW"
// #define HW_VER { 1, 1, 0 }
// #define SW_NAME "Esp32 SW"
// #define SW_VER { 1, 3, 0 }
// Oled
#define i2c_Address 0x3C // Oled I2c Address
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// KeyPad
#define ROW_NUM 4 // four rows
#define COLUMN_NUM 4 // three columns
char keys[ROW_NUM][COLUMN_NUM] = {
{ '1', '2', '3', 'A' },
{ '4', '5', '6', 'B' },
{ '7', '8', '9', 'C' },
{ '*', '0', '#', 'D' }
};
byte pin_rows[ROW_NUM] = { 13, 12, 14, 27 }; // GPIO13, GPIO12, GPIO14, GPIO27 connect to the row pins
byte pin_column[COLUMN_NUM] = { 26, 25, 33, 32 }; // GPIO26, GPIO25, GPIO33,32 connect to the column pins
Keypad keypad = Keypad(makeKeymap(keys), pin_rows, pin_column, ROW_NUM, COLUMN_NUM);
// temperature sensor pins
#define BUZZER_PIN 15 // Buzzer Pin
int thermoDO = 19;
int thermoCS = 23;
int thermoCLK = 5;
float temperature = 0.00;
float kwhUsed = 0.00;
// relay pin
#define RELAYPIN 4
// initializing thermocouple
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
// Define initial time remaining and target temperature
unsigned long totalSeconds = 60 * 4;
float targetTemperature = 100.0;
bool startTimer = false;
// time remaining
unsigned long hours = totalSeconds / 3600;
unsigned long minutes = (totalSeconds % 3600) / 60;
unsigned long seconds = (totalSeconds % 60);
int checkPauseStop = 0;
unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
const long interval = 1000; // 1 second
// Acs712 Current
const int sensorIn = 34; // pin where the OUT pin from sensor is connected on Arduino
int mVperAmp = 100; // this is the 5A version of the ACS712 - use 100 for 20A Module and 66 for 30A Module
int Watt = 0;
double Voltage = 0;
double VRMS = 0;
double AmpsRMS = 0;
float totalEnergykWh = 0.0; // Total energy consumed in kWh
float localerror = 0.00; // for error value in current add/sub on line 293
float elapsedTime = 0.0; // Time elapsed in hours
unsigned long previousMillisAcs712 = 0;
bool timeReset = false;
QueueHandle_t keypadQueue;
SemaphoreHandle_t displayMutex;
float kwhUsedValue();
void updateDisplay();
void setup() {
Serial.begin(115200);
// Initialize ble ota
// ArduinoBleOTA.begin(DEVICE_NAME, InternalStorage, HW_NAME, HW_VER, SW_NAME, SW_VER);
// Initialize the OLED display
if (!display.begin(SSD1306_SWITCHCAPVCC, i2c_Address)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;);
}
// Set relay pin output
pinMode(RELAYPIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(RELAYPIN, HIGH);
noTone(BUZZER_PIN);
display.display();
delay(250);
// Clear the buffer.
display.clearDisplay();
updateDisplay();
keypadQueue = xQueueCreate(10, sizeof(char));
displayMutex = xSemaphoreCreateMutex();
// Create tasks to handle parallely
xTaskCreate(keypadTask, "Keypad Task", 2048, NULL, 1, NULL);
xTaskCreate(displayTask, "Display Task", 2048, NULL, 1, NULL);
xTaskCreate(temperatureTask, "Temperature Task", 2048, NULL, 1, NULL);
xTaskCreate(relayTask, "Relay Task", 2048, NULL, 1, NULL);
xTaskCreate(energyTask, "Energy Task", 2048, NULL, 1, NULL);
}
void loop() {
// #ifdef USE_ARDUINO_BLE_LIB
// BLE.poll();
// #endif
// ArduinoBleOTA.pull();
}
// Task for handling keypad input
void keypadTask(void *pvParameters) {
char key;
while (1) {
key = keypad.getKey();
if (key != NO_KEY) {
// Handle the key press
switch (key) {
case '1':
Serial.println("Set timer to 0");
totalSeconds = 0;
hours = 0;
minutes = 0;
seconds = 0;
break;
case '2':
Serial.println("+10 minutes");
totalSeconds += 10 * 60;
hours = totalSeconds / 3600;
minutes = (totalSeconds % 3600) / 60;
seconds = (totalSeconds % 60);
break;
case '3':
Serial.println("+30 minutes");
totalSeconds += 30 * 60;
hours = totalSeconds / 3600;
minutes = (totalSeconds % 3600) / 60;
seconds = (totalSeconds % 60);
break;
case '4':
Serial.println("+60 minutes");
totalSeconds += 60 * 60;
hours = totalSeconds / 3600;
minutes = (totalSeconds % 3600) / 60;
seconds = (totalSeconds % 60);
break;
case '5':
Serial.println("Set target temperature to 0.0");
targetTemperature = 0.0;
break;
case '6':
Serial.println("Set target temperature to 100");
targetTemperature = 100.0;
break;
case '7':
Serial.println("Set target temperature to 200");
targetTemperature = 200.0;
break;
case '8':
Serial.println("Set target temperature to 65");
targetTemperature = 65.0;
break;
case '9':
Serial.println("+5 target temperature");
targetTemperature += 5.0;
break;
case 'A':
Serial.println("-5 target temperature");
targetTemperature -= 5.0;
break;
case 'B':
Serial.println("Reset kWh used");
kwhUsed = 0.0;
break;
case 'C':
Serial.println("Start operation");
startTimer = true;
timeReset =false;
break;
case 'D':
Serial.println("Stop operation");
if (checkPauseStop == 0) {
Serial.println("Pause Operation");
checkPauseStop = 1;
} else if (checkPauseStop == 1) {
Serial.println("Reset Operation");
timeReset =true;
minutes = 0;
hours = 0;
seconds = 0;
checkPauseStop = 0;
noTone(BUZZER_PIN);
}
startTimer = false;
break;
}
}
if (startTimer) {
if (hours > 0 || minutes > 0 || seconds > 0) {
// Check if 1 second has passed
currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
// Reset the previousMillis variable to the current time
previousMillis = currentMillis;
totalSeconds--;
// Decrement the seconds value by 1
if (seconds > 0) {
Serial.println("Decreasing seconds");
seconds--;
} else if (seconds == 0) {
if (minutes > 0 || hours > 0) {
seconds = 59;
if (minutes > 0) {
Serial.println("Decreasing minutes");
minutes--;
} else if (minutes == 0) {
if (hours > 0) {
Serial.println("Decreasing hours");
minutes = 59;
hours--;
}
}
}
}
// If both the hours, minutes, and seconds values reach zero, stop the timer
if (hours == 0 && minutes == 0 && seconds == 0 && timeReset == false) {
Serial.println("Time is up!");
targetTemperature = 65.00;
startTimer = false;
digitalWrite(BUZZER_PIN, LOW);
tone(BUZZER_PIN, 1000);
return;
}
}
}
}
vTaskDelay(50 / portTICK_PERIOD_MS); // Polling interval for the keypad
}
}
// Task to show data on oled
void displayTask(void *pvParameters) {
while (1) {
if (xSemaphoreTake(displayMutex, portMAX_DELAY)) {
updateDisplay();
xSemaphoreGive(displayMutex);
}
vTaskDelay(500 / portTICK_PERIOD_MS); // Update display every 500 ms
}
}
// Task to read temperature
void temperatureTask(void *pvParameters) {
while (1) {
temperature = thermocouple.readCelsius();
vTaskDelay(250 / portTICK_PERIOD_MS); // Read temperature every 250 ms
}
}
// Task to control relay
void relayTask(void *pvParameters) {
while (1) {
if (temperature >= targetTemperature) {
digitalWrite(RELAYPIN, HIGH);
} else {
digitalWrite(RELAYPIN, LOW);
}
vTaskDelay(100 / portTICK_PERIOD_MS); // Check relay every 100 ms
}
}
// Tak to look at kwh value
void energyTask(void *pvParameters) {
while (1) {
kwhUsed = kwhUsedValue();
vTaskDelay(1000 / portTICK_PERIOD_MS); // Update energy every second
}
}
void updateDisplay() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE, BLACK);
// Draw boxes
display.drawRect(0, 0, 63, 31, WHITE); // Box 1 (Time)
display.drawRect(64, 0, 63, 31, WHITE); // Box 2 (Temperature)
display.drawRect(0, 32, 63, 31, WHITE); // Box 3 (Target Temperature)
display.drawRect(64, 32, 63, 31, WHITE); // Box 4 (kWh Used)
// Print the time
display.setCursor(4, 3);
display.print("Time");
display.setCursor(4, 15);
display.print(padNumber(hours));
display.print(":");
display.print(padNumber(minutes));
display.print(":");
display.print(padNumber(seconds));
// Print the temperature
display.setCursor(68, 3);
display.print("Temp");
display.setCursor(68, 15);
display.print(temperature);
display.print(" C");
// Print the target temperature
display.setCursor(4, 35);
display.print("Target");
display.setCursor(4, 46);
display.print(targetTemperature);
display.print(" C");
// Print the amount of kWh used
display.setCursor(68, 35);
display.print("kWh Used");
display.setCursor(68, 46);
display.print(kwhUsed);
// Display the buffer on the OLED display
display.display();
}
float kwhUsedValue() {
Voltage = getVPP();
VRMS = (Voltage / 2.0) * 0.707;// 1 / root 2 is 0.707
AmpsRMS = ((VRMS * 1000) / mVperAmp)-localerror ; // localerror is the error
// note: 1.309 is the empirically established calibration factor for 220V mains voltage
// 220 is the main AC power voltage – this parameter changes locally
Watt = (AmpsRMS * 220 / 1.309);
elapsedTime = (millis() - previousMillisAcs712) / 3600000.0;
// Update total energy consumption in kWh
totalEnergykWh += (Watt * elapsedTime / 1000.0);
// Print the total energy consumed
Serial.print("Total Energy Consumed: ");
Serial.print(totalEnergykWh);
Serial.println(" kWh");
previousMillisAcs712 = millis();
return totalEnergykWh;
}
// ***** function calls ******
float getVPP() {
float result;
int readValue; // value read from the sensor
int maxValue = 0; // store max value here
int minValue = 4096; // store min value here ESP32 ADC resolution
uint32_t start_time = millis();
while ((millis() - start_time) < 1000) {// sample for 1 Sec
readValue = analogRead(sensorIn);
// see if you have a new maxValue
if (readValue > maxValue) {
/*record the maximum sensor value*/
maxValue = readValue;
} else if (readValue < minValue) {
/*record the minimum sensor value*/
minValue = readValue;
}
}
// Subtract min from max
result = ((maxValue - minValue) * 3.3) / 4096.0;
return result;
}
// pad 0 in time
String padNumber(int number)
{
return String(number).length() == 1 ? "0" + String(number) : String(number);
}