#include <WiFi.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
// Adafruit IO credentials
#define IO_USERNAME "BenSmiling" // Your Adafruit IO username
#define IO_KEY "aio_ZdwN60vmbTT3QaYyHt6VRdxzuB5F" // Your Adafruit IO key
// Wi-Fi credentials
#define WLAN_SSID "Wokwi-GUEST" // Your Wi-Fi SSID
#define WLAN_PASS "" // Your Wi-Fi password
// Pin assignments for various components
const int ledPin = 13; // LED pin for status indication
const int nepaInputPin = 12; // Input pin for NEPA system (could be a sensor or switch)
const int nepaOutputPin = 0; // Output pin to control NEPA system
const int relay1Pin = 16; // Relay 1 control pin
const int relay2Pin = 4; // Relay 2 control pin
const int sensor1Pin = 34; // Analog sensor 1 pin (ACS712)
const int sensor2Pin = 35; // Analog sensor 2 pin (ACS712)
// Constant value for sensor calibration (ACS712 sensitivity of 185mV per ampere)
const float sensorMultiplier = 0.185; // Sensitivity factor for ACS712 in V/A
const float sensorOffset = 0; // Voltage offset at 0A (ACS712 at 0A is 2.5V)
// Create a WiFiClient object for MQTT communication
WiFiClient client;
// Initialize MQTT client using Adafruit IO credentials
Adafruit_MQTT_Client mqtt(&client, "io.adafruit.com", 1883, IO_USERNAME, IO_KEY);
// Subscription feeds (to receive data from Adafruit IO)
Adafruit_MQTT_Subscribe powerControl = Adafruit_MQTT_Subscribe(&mqtt, IO_USERNAME "/feeds/ACTIVE_POWER");
Adafruit_MQTT_Subscribe relayStateControl = Adafruit_MQTT_Subscribe(&mqtt, IO_USERNAME "/feeds/RELAY_STATES");
// Publication feeds (to send data to Adafruit IO)
Adafruit_MQTT_Publish nepaStatusFeed = Adafruit_MQTT_Publish(&mqtt, IO_USERNAME "/feeds/NEPA_STATUS");
Adafruit_MQTT_Publish outputPowerFeed = Adafruit_MQTT_Publish(&mqtt, IO_USERNAME "/feeds/OUTPUT_POWER");
// Variables to track previous sensor readings to avoid duplicate publishing
float lastSensor1Value = -1; // Last value of sensor 1
float lastSensor2Value = -1; // Last value of sensor 2
unsigned long lastSensorReadTime = 0; // Store the last time sensor data was read
const unsigned long sensorReadInterval = 5000; // 5 seconds interval (5000 milliseconds)
void setup() {
// Start serial communication for debugging
Serial.begin(115200);
// Set up pin modes for various components
pinMode(nepaInputPin, INPUT); // NEPA input (sensor or switch)
pinMode(nepaOutputPin, OUTPUT); // NEPA output (control signal)
pinMode(relay1Pin, OUTPUT); // Relay 1 control pin
pinMode(relay2Pin, OUTPUT); // Relay 2 control pin
pinMode(ledPin, OUTPUT); // LED for status indication
// Connect to Wi-Fi
WiFi.begin(WLAN_SSID, WLAN_PASS); // Connect to Wi-Fi network
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print("."); // Print dots to indicate connection attempt
}
Serial.println("\nWiFi connected"); // Confirm Wi-Fi connection
// Subscribe to the power control and relay state control feeds
mqtt.subscribe(&powerControl);
mqtt.subscribe(&relayStateControl);
// Connect to Adafruit IO MQTT server
while (!mqtt.connected()) {
Serial.print("Connecting to Adafruit IO...");
if (mqtt.connect()) {
Serial.println("connected!"); // Confirm successful MQTT connection
} else {
Serial.println("failed, retrying...");
delay(5000); // Retry connection every 5 seconds if unsuccessful
}
}
}
void loop() {
// Process MQTT packets and check for any new messages or commands
mqtt.processPackets(1000);
// Get the current time
unsigned long currentMillis = millis();
// Check if it's time to read the sensors (every 5 seconds)
if (currentMillis - lastSensorReadTime >= sensorReadInterval) {
// It's time to read the sensors, update the last read time
lastSensorReadTime = currentMillis;
// Read sensor values from the analog pins and convert to amperes
float sensor1Raw = analogRead(sensor1Pin); // Read sensor 1 raw value
float sensor2Raw = analogRead(sensor2Pin); // Read sensor 2 raw value
// Convert the raw values to voltage
float sensor1Voltage = (sensor1Raw * (3.3 / 4095.0)); // Convert to voltage (0-3.3V)
float sensor2Voltage = (sensor2Raw * (3.3 / 4095.0)); // Convert to voltage (0-3.3V)
// Convert the voltage to current (using the ACS712 sensitivity)
float sensor1Current = (sensor1Voltage - sensorOffset) / sensorMultiplier;
float sensor2Current = (sensor2Voltage - sensorOffset) / sensorMultiplier;
// Print sensor values to serial monitor for debugging
Serial.println("Sensor 1 Voltage: " + String(sensor1Voltage) + " V, Current: " + String(sensor1Current) + " A");
Serial.println("Sensor 2 Voltage: " + String(sensor2Voltage) + " V, Current: " + String(sensor2Current) + " A");
// Only publish if either of the sensor values has changed since the last read
if (mqtt.connected()) {
if (sensor1Current != lastSensor1Value || sensor2Current != lastSensor2Value) {
// Prepare the message with both sensor values
String message = String(sensor1Current, 3) + ", " + String(sensor2Current, 3);
// Publish the sensor data to Adafruit IO
if (!outputPowerFeed.publish(message.c_str())) {
Serial.println("Failed to publish OUTPUT_POWER"); // Error if publish fails
} else {
Serial.println("Published OUTPUT_POWER: " + message); // Success message
lastSensor1Value = sensor1Current; // Update last sensor values
lastSensor2Value = sensor2Current; // Update last sensor values
}
} else {
Serial.println("Sensor values unchanged — not publishing."); // No change in data
}
}
}
// NEW: Track the NEPA input state and publish status if it changes
static int lastNepainputState = -1; // Variable to store the last state of NEPA input pin
int currentNepainputState = digitalRead(nepaInputPin); // Read current state of NEPA input
// Only publish if the state of the NEPA input has changed
if (currentNepainputState != lastNepainputState) {
lastNepainputState = currentNepainputState; // Update the last state
// Get the current timestamp for logging
unsigned long currentTime = millis();
String timestamp = String(currentTime);
// Publish the new state of NEPA to Adafruit IO with timestamp
if (mqtt.connected()) {
String message = String(currentNepainputState == HIGH ? "NEPA_ON" : "NEPA_OFF") + ", " + timestamp;
if (!nepaStatusFeed.publish(message.c_str())) {
Serial.println("Failed to publish NEPA status with timestamp"); // Error message
} else {
Serial.println("Published NEPA status with timestamp: " + message); // Success message
}
}
}
// Handle power control and relay state control commands from Adafruit IO feeds
if (powerControl.lastread != NULL) {
String command = (char *)powerControl.lastread;
Serial.print("Received command: ");
Serial.println(command);
// Control NEPA output based on received commands (Does NOT affect NEPA status publishing)
if (command == "grid") {
digitalWrite(nepaOutputPin, LOW); // Switch to grid power
Serial.println("SWITCHED TO GRID POWER");
} else if (command == "solar") {
digitalWrite(nepaOutputPin, HIGH); // Switch to solar power
Serial.println("SWITCHED TO SOLAR POWER");
} else if (command == "auto") {
digitalWrite(nepaOutputPin, !digitalRead(nepaInputPin)); // Auto control based on input pin
Serial.println("SWITCHED TO AUTO POWER");
} else {
digitalWrite(nepaOutputPin, !digitalRead(nepaInputPin)); // Default auto control
Serial.println("SWITCHED TO DEFAULT AUTO POWER");
}
}
// Handle relay state control commands from Adafruit IO feed
if (relayStateControl.lastread != NULL) {
int relayCommand = atoi((char *)relayStateControl.lastread); // Convert command to integer
Serial.print("Received relay command: ");
Serial.println(relayCommand);
// Control relays based on received command
switch (relayCommand) {
case 0:
digitalWrite(relay1Pin, LOW); // Both relays OFF
digitalWrite(relay2Pin, LOW);
Serial.println("Both relays OFF");
break;
case 1:
digitalWrite(relay1Pin, HIGH); // Relay 1 ON, Relay 2 OFF
digitalWrite(relay2Pin, LOW);
Serial.println("Relay 1 ON, Relay 2 OFF");
break;
case 2:
digitalWrite(relay1Pin, LOW); // Relay 1 OFF, Relay 2 ON
digitalWrite(relay2Pin, HIGH);
Serial.println("Relay 1 OFF, Relay 2 ON");
break;
case 3:
default:
digitalWrite(relay1Pin, HIGH); // Both relays ON by default
digitalWrite(relay2Pin, HIGH);
Serial.println("Both relays ON (default)");
break;
}
}
}