#include <WiFi.h>
#include "SinricPro.h"
#include "SinricProSwitch.h"
// 1. YOUR GLOBAL CREDENTIALS
#define APP_KEY "93d8976e-30e0-4b9b-b531-b47741f8d611"
#define APP_SECRET "ae0439db-3e6b-4ba1-b1ea-0f2e6ef617e7-62967822-9ac6-4a66-8408-2e85d505359b"
// 2. YOUR THREE UNIQUE DEVICE IDs
#define DEVICE_ID_1 "6a151e22f9b5f15fa7df4d43"
#define DEVICE_ID_2 "6a156316c93c9fd0e0fd7a2c"
#define DEVICE_ID_3 "6a1562d6baa50bf9bf3e0b33"
// 3. INPUT YOUR NETWORK DETAILS
#define WIFI_SSID "Wokwi-GUEST"
#define WIFI_PASS ""
// 4. HARDWARE PIN CONFIGURATIONS
#define RELAY_PIN_1 12
#define SWITCH_PIN_1 14
#define RELAY_PIN_2 27
#define SWITCH_PIN_2 13
#define RELAY_PIN_3 25
#define SWITCH_PIN_3 15
// Global States Tracking
bool powerState1 = false;
bool lastSwitchState1 = false;
bool powerState2 = false;
bool lastSwitchState2 = false;
bool powerState3 = false;
bool lastSwitchState3 = false;
// Debounce Timing Variables (Non-Blocking)
unsigned long lastDebounceTime1 = 0;
unsigned long lastDebounceTime2 = 0;
unsigned long lastDebounceTime3 = 0;
const unsigned long debounceDelay = 50; // 50 milliseconds
// --- CLOUD CALLBACK HANDLERS ---
bool onPowerState1(const String &deviceId, bool &state) {
Serial.printf("Light 1 turned %s via Cloud\r\n", state ? "ON" : "OFF");
powerState1 = state;
digitalWrite(RELAY_PIN_1, powerState1 ? HIGH : LOW);
return true;
}
bool onPowerState2(const String &deviceId, bool &state) {
Serial.printf("Light 2 turned %s via Cloud\r\n", state ? "ON" : "OFF");
powerState2 = state;
digitalWrite(RELAY_PIN_2, powerState2 ? HIGH : LOW);
return true;
}
bool onPowerState3(const String &deviceId, bool &state) {
Serial.printf("Light 3 turned %s via Cloud\r\n", state ? "ON" : "OFF");
powerState3 = state;
digitalWrite(RELAY_PIN_3, powerState3 ? HIGH : LOW);
return true;
}
// --- LOCAL OVERRIDE FUNCTION (OFFLINE-SAFE) ---
void handlePhysicalSwitches() {
unsigned long currentTime = millis();
// 1. Handle Switch 1
bool currentSwitchState1 = digitalRead(SWITCH_PIN_1);
if (currentSwitchState1 != lastSwitchState1 && (currentTime - lastDebounceTime1 > debounceDelay)) {
powerState1 = !powerState1;
digitalWrite(RELAY_PIN_1, powerState1 ? HIGH : LOW); // Instantly toggle hardware
// Only attempt to sync with cloud if Internet is active
if (WiFi.status() == WL_CONNECTED) {
SinricProSwitch& mySwitch1 = SinricPro[DEVICE_ID_1];
mySwitch1.sendPowerStateEvent(powerState1);
}
Serial.printf("Physical Switch 1 toggled. Light is now %s\r\n", powerState1 ? "ON" : "OFF");
lastSwitchState1 = currentSwitchState1;
lastDebounceTime1 = currentTime;
}
// 2. Handle Switch 2
bool currentSwitchState2 = digitalRead(SWITCH_PIN_2);
if (currentSwitchState2 != lastSwitchState2 && (currentTime - lastDebounceTime2 > debounceDelay)) {
powerState2 = !powerState2;
digitalWrite(RELAY_PIN_2, powerState2 ? HIGH : LOW);
if (WiFi.status() == WL_CONNECTED) {
SinricProSwitch& mySwitch2 = SinricPro[DEVICE_ID_2];
mySwitch2.sendPowerStateEvent(powerState2);
}
Serial.printf("Physical Switch 2 toggled. Light is now %s\r\n", powerState2 ? "ON" : "OFF");
lastSwitchState2 = currentSwitchState2;
lastDebounceTime2 = currentTime;
}
// 3. Handle Switch 3
bool currentSwitchState3 = digitalRead(SWITCH_PIN_3);
if (currentSwitchState3 != lastSwitchState3 && (currentTime - lastDebounceTime3 > debounceDelay)) {
powerState3 = !powerState3;
digitalWrite(RELAY_PIN_3, powerState3 ? HIGH : LOW);
if (WiFi.status() == WL_CONNECTED) {
SinricProSwitch& mySwitch3 = SinricPro[DEVICE_ID_3];
mySwitch3.sendPowerStateEvent(powerState3);
}
Serial.printf("Physical Switch 3 toggled. Light is now %s\r\n", powerState3 ? "ON" : "OFF");
lastSwitchState3 = currentSwitchState3;
lastDebounceTime3 = currentTime;
}
}
void setup() {
Serial.begin(115200);
// Set Pin Modes
pinMode(RELAY_PIN_1, OUTPUT); pinMode(SWITCH_PIN_1, INPUT_PULLUP);
pinMode(RELAY_PIN_2, OUTPUT); pinMode(SWITCH_PIN_2, INPUT_PULLUP);
pinMode(RELAY_PIN_3, OUTPUT); pinMode(SWITCH_PIN_3, INPUT_PULLUP);
// Initialize switch states based on current physical wall switch position
lastSwitchState1 = digitalRead(SWITCH_PIN_1);
lastSwitchState2 = digitalRead(SWITCH_PIN_2);
lastSwitchState3 = digitalRead(SWITCH_PIN_3);
// Connect to Network Asynchronously (Does not block code if Wi-Fi is down)
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
WiFi.setSleep(false); // Disables Wi-Fi sleep to completely remove cloud latency
WiFi.begin(WIFI_SSID, WIFI_PASS);
Serial.println("\nSystem Booted! Wi-Fi connecting in background...");
Serial.println("Local Physical Switches are active IMMEDIATELY.");
// Setup Sinric Pro Callbacks
SinricProSwitch& mySwitch1 = SinricPro[DEVICE_ID_1];
mySwitch1.onPowerState(onPowerState1);
SinricProSwitch& mySwitch2 = SinricPro[DEVICE_ID_2];
mySwitch2.onPowerState(onPowerState2);
SinricProSwitch& mySwitch3 = SinricPro[DEVICE_ID_3];
mySwitch3.onPowerState(onPowerState3);
// Begin Sinric Pro processing
SinricPro.begin(APP_KEY, APP_SECRET);
}
void loop() {
// Only process cloud commands if we are actually connected to Wi-Fi
if (WiFi.status() == WL_CONNECTED) {
SinricPro.handle();
}
// Always process physical switches instantly, regardless of internet state
handlePhysicalSwitches();
}