#include <Arduino.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// ==============================
// Pin Configuration
// ==============================
const int voltagePotPin = 34;
const int currentPotPin = 35;
const int frequencyPotPin = 32;
const int enableSwitchPin = 33;
const int relayPin = 25;
const int statusLedPin = 26;
const int buzzerPin = 27;
const int lcdSdaPin = 21;
const int lcdSclPin = 22;
// ==============================
// LCD Configuration
// ==============================
const uint8_t lcdAddress = 0x27;
const uint8_t lcdColumns = 16;
const uint8_t lcdRows = 2;
LiquidCrystal_I2C lcd(lcdAddress, lcdColumns, lcdRows);
// ==============================
// Relay Configuration
// ==============================
// Most relay modules are active LOW.
// Change this to false if your relay is active HIGH.
const bool relayActiveLow = true;
// ==============================
// Protection Limits
// ==============================
const float voltageMin = 207.0;
const float voltageMax = 253.0;
const float currentMin = 0.2;
const float currentMax = 8.0;
const float frequencyMin = 49.0;
const float frequencyMax = 51.0;
// ==============================
// Potentiometer Simulation Ranges
// ==============================
const float voltageSimulationMin = 180.0;
const float voltageSimulationMax = 260.0;
const float currentSimulationMin = 0.0;
const float currentSimulationMax = 10.0;
const float frequencySimulationMin = 45.0;
const float frequencySimulationMax = 55.0;
// ==============================
// Timing
// ==============================
const unsigned long displayUpdateInterval = 500;
unsigned long lastDisplayUpdate = 0;
// ==============================
// System State
// ==============================
float voltageValue = 0.0;
float currentValue = 0.0;
float frequencyValue = 0.0;
bool protectionEnabled = false;
bool systemHealthy = false;
bool relayState = false;
bool faultState = false;
String faultMessage = "NORMAL";
// ==============================
// Function Declarations
// ==============================
float readVoltage();
float readCurrent();
float readFrequency();
bool checkProtection();
void updateRelay(bool shouldTurnOn);
void updateIndicators();
void updateDisplay();
void displayNormalState();
void displayFaultState();
void clearLcdLine(uint8_t row);
void printCentered(uint8_t row, const String &message);
// ==============================
// Setup
// ==============================
void setup() {
// Initialize serial communication.
Serial.begin(115200);
// Configure ADC pins.
pinMode(voltagePotPin, INPUT);
pinMode(currentPotPin, INPUT);
pinMode(frequencyPotPin, INPUT);
// Configure slide switch.
pinMode(enableSwitchPin, INPUT_PULLUP);
// Configure outputs.
pinMode(relayPin, OUTPUT);
pinMode(statusLedPin, OUTPUT);
pinMode(buzzerPin, OUTPUT);
// Start with everything OFF.
updateRelay(false);
digitalWrite(statusLedPin, LOW);
digitalWrite(buzzerPin, LOW);
// Configure ADC resolution.
analogReadResolution(12);
// Configure I2C.
Wire.begin(lcdSdaPin, lcdSclPin);
// Initialize LCD.
lcd.init();
lcd.backlight();
lcd.clear();
printCentered(0, "RKE DATA OPE");
printCentered(1, "Starting...");
delay(1500);
lcd.clear();
Serial.println("RKE Data OPE Protection System Started");
}
// ==============================
// Main Loop
// ==============================
void loop() {
// Read all simulated electrical parameters.
voltageValue = readVoltage();
currentValue = readCurrent();
frequencyValue = readFrequency();
// Read protection enable switch.
protectionEnabled = digitalRead(enableSwitchPin) == LOW;
// Evaluate protection conditions.
systemHealthy = checkProtection();
// Control relay and indicators.
if (!protectionEnabled) {
faultState = false;
faultMessage = "DISABLED";
updateRelay(false);
digitalWrite(statusLedPin, LOW);
digitalWrite(buzzerPin, LOW);
} else if (systemHealthy) {
faultState = false;
faultMessage = "NORMAL";
updateRelay(true);
digitalWrite(statusLedPin, HIGH);
digitalWrite(buzzerPin, LOW);
} else {
faultState = true;
updateRelay(false);
digitalWrite(statusLedPin, LOW);
digitalWrite(buzzerPin, HIGH);
}
// Update LCD at a controlled interval.
if (millis() - lastDisplayUpdate >= displayUpdateInterval) {
lastDisplayUpdate = millis();
updateDisplay();
}
// Debug output.
Serial.print("Voltage: ");
Serial.print(voltageValue, 1);
Serial.print(" V | Current: ");
Serial.print(currentValue, 2);
Serial.print(" A | Frequency: ");
Serial.print(frequencyValue, 2);
Serial.print(" Hz | Protection: ");
Serial.print(protectionEnabled ? "ON" : "OFF");
Serial.print(" | Relay: ");
Serial.println(relayState ? "ON" : "OFF");
delay(50);
}
// ==============================
// Read Voltage
// ==============================
float readVoltage() {
/*
Reads voltage potentiometer and maps
its ADC value to the simulated AC voltage range.
*/
int adcValue = analogRead(voltagePotPin);
float voltage = map(
adcValue,
0,
4095,
(int)(voltageSimulationMin * 10),
(int)(voltageSimulationMax * 10)
) / 10.0;
return voltage;
}
// ==============================
// Read Current
// ==============================
float readCurrent() {
/*
Reads current potentiometer and maps
its ADC value to the simulated current range.
*/
int adcValue = analogRead(currentPotPin);
float current = map(
adcValue,
0,
4095,
(int)(currentSimulationMin * 100),
(int)(currentSimulationMax * 100)
) / 100.0;
return current;
}
// ==============================
// Read Frequency
// ==============================
float readFrequency() {
/*
Reads frequency potentiometer and maps
its ADC value to the simulated AC frequency.
*/
int adcValue = analogRead(frequencyPotPin);
float frequency = map(
adcValue,
0,
4095,
(int)(frequencySimulationMin * 100),
(int)(frequencySimulationMax * 100)
) / 100.0;
return frequency;
}
// ==============================
// Protection Check
// ==============================
bool checkProtection() {
/*
Checks voltage, current and frequency
against their configured protection limits.
*/
if (voltageValue < voltageMin) {
faultMessage = "UNDER VOLT";
return false;
}
if (voltageValue > voltageMax) {
faultMessage = "OVER VOLT";
return false;
}
if (currentValue < currentMin) {
faultMessage = "UNDER CUR";
return false;
}
if (currentValue > currentMax) {
faultMessage = "OVER CUR";
return false;
}
if (frequencyValue < frequencyMin) {
faultMessage = "UNDER FREQ";
return false;
}
if (frequencyValue > frequencyMax) {
faultMessage = "OVER FREQ";
return false;
}
return true;
}
// ==============================
// Relay Control
// ==============================
void updateRelay(bool shouldTurnOn) {
/*
Controls the relay while handling
active-low or active-high relay modules.
*/
relayState = shouldTurnOn;
if (relayActiveLow) {
digitalWrite(relayPin, shouldTurnOn ? LOW : HIGH);
} else {
digitalWrite(relayPin, shouldTurnOn ? HIGH : LOW);
}
}
// ==============================
// LCD Update
// ==============================
void updateDisplay() {
/*
Updates the LCD with electrical values
and the current protection state.
*/
if (faultState) {
displayFaultState();
return;
}
displayNormalState();
}
// ==============================
// Normal LCD Screen
// ==============================
void displayNormalState() {
/*
Displays voltage, current and frequency
during normal or disabled operation.
*/
clearLcdLine(0);
clearLcdLine(1);
lcd.setCursor(0, 0);
lcd.print("V:");
lcd.print(voltageValue, 0);
lcd.print(" I:");
lcd.print(currentValue, 1);
lcd.print("A");
lcd.setCursor(0, 1);
lcd.print("F:");
lcd.print(frequencyValue, 1);
lcd.print(" ");
if (!protectionEnabled) {
lcd.print("DISABLE");
} else if (relayState) {
lcd.print("LOAD ON");
} else {
lcd.print("LOAD OFF");
}
}
// ==============================
// Fault LCD Screen
// ==============================
void displayFaultState() {
/*
Displays the detected protection fault
and forces the user to inspect the values.
*/
clearLcdLine(0);
clearLcdLine(1);
lcd.setCursor(0, 0);
lcd.print("FAULT:");
lcd.print(faultMessage);
lcd.setCursor(0, 1);
lcd.print("V");
lcd.print(voltageValue, 0);
lcd.print(" I");
lcd.print(currentValue, 1);
lcd.print(" F");
lcd.print(frequencyValue, 0);
}
// ==============================
// Clear LCD Line
// ==============================
void clearLcdLine(uint8_t row) {
/*
Clears one LCD row before writing new data.
*/
lcd.setCursor(0, row);
lcd.print(" ");
}
// ==============================
// Center LCD Text
// ==============================
void printCentered(uint8_t row, const String &message) {
/*
Prints a short message centered on the LCD.
*/
clearLcdLine(row);
int startPosition = (16 - message.length()) / 2;
if (startPosition < 0) {
startPosition = 0;
}
lcd.setCursor(startPosition, row);
lcd.print(message.substring(0, 16));
}AC Voltage
AC Current
AC Frequency