/********* Pleasedontcode.com **********
Pleasedontcode thanks you for automatic code generation! Enjoy your code!
- Terms and Conditions:
You have a non-exclusive, revocable, worldwide, royalty-free license
for personal and commercial use. Attribution is optional; modifications
are allowed, but you're responsible for code maintenance. We're not
liable for any loss or damage. For full terms,
please visit pleasedontcode.com/termsandconditions.
- Project: # Relay Controller
- Source Code NOT compiled for: ESP8266 NodeMCU V1.0
- Source Code created on: 2026-02-05 14:55:51
********* Pleasedontcode.com **********/
/****** SYSTEM REQUIREMENTS *****/
/****** SYSTEM REQUIREMENT 1 *****/
/* add i2c gpio expander PCF8574 */
/****** END SYSTEM REQUIREMENTS *****/
#include "PCF8574.h"
#include "Relay.h"
#include "VoiceAssistant.h"
// Create PCF8574 instance with default I2C address 0x20
// Address range: 0x20 to 0x27 depending on A0, A1, A2 pins
PCF8574 expander(0x20);
// Create Relay instance for managing 8 relays
Relay relayController(&expander, 8);
// Create VoiceAssistant instance
VoiceAssistant* voiceAssistant = nullptr;
// WiFi Configuration
// IMPORTANT: Replace these with your actual WiFi credentials
const char* WIFI_SSID = "YourWiFiSSID";
const char* WIFI_PASSWORD = "YourWiFiPassword";
// Google Cloud Speech-to-Text API Configuration
// IMPORTANT: Replace these with your actual Google Cloud credentials
const char* GOOGLE_API_KEY = "YOUR_GOOGLE_CLOUD_API_KEY_HERE";
const char* GOOGLE_PROJECT_ID = "YOUR_GOOGLE_PROJECT_ID_HERE";
// Serial command buffer
String commandBuffer = "";
// Timestamp for periodic status monitoring
unsigned long lastStatusTime = 0;
unsigned long statusInterval = 5000; // Print status every 5 seconds
// Voice command request flag
volatile bool voiceCommandRequested = false;
unsigned long voiceCommandRequestTime = 0;
const unsigned long voiceCommandDebounce = 1000; // Debounce time for voice command requests
// Relay state change log
struct RelayLog {
uint32_t timestamp;
uint8_t relayNum;
bool state; // true = ON, false = OFF
char action[20];
};
// Log buffer for relay state changes (last 10 changes)
RelayLog relayLogs[10];
uint8_t logIndex = 0;
/****** FUNCTION PROTOTYPES *****/
void setup(void);
void loop(void);
void processSerialCommand(String command);
void handleRelayCommand(String command);
void printAvailableCommands(void);
void logRelayStateChange(uint8_t relayNum, bool newState, const char* action);
void printRelayLogs(void);
void printRelayStatusMonitoring(void);
void processVoiceCommand(void);
void initializeVoiceAssistant(void);
void setup(void)
{
// Initialize Serial communication
Serial.begin(115200);
delay(100);
Serial.println("\n\n================================================");
Serial.println(" PCF8574 I2C GPIO Expander with Relays ");
Serial.println(" + Voice Assistant Support ");
Serial.println("================================================");
Serial.println();
// Initialize PCF8574 with I2C address 0x20
// Using default ESP8266 pins: SDA=4 (D2), SCL=5 (D1)
Serial.println("Initializing PCF8574 I2C GPIO Expander...");
if (expander.begin()) {
Serial.println("[SUCCESS] PCF8574 initialized successfully!");
Serial.print("[INFO] I2C Address: 0x");
Serial.println(0x20, HEX);
Serial.println("[INFO] I2C Pins: SDA=4 (D2), SCL=5 (D1)");
// Initialize all relay pins to HIGH (default/inactive state)
expander.writePort(0xFF);
Serial.println("[INFO] All relay pins set to HIGH (inactive)");
delay(500);
// Initialize Relay controller
Serial.println();
Serial.println("Initializing Relay Controller...");
relayController.init();
Serial.println();
Serial.println("[SUCCESS] System fully initialized and ready!");
Serial.println();
// Initialize Voice Assistant with WiFi and Google Cloud API
delay(1000);
Serial.println();
initializeVoiceAssistant();
delay(1000);
// Print help information
printAvailableCommands();
// Initialize log timestamps
lastStatusTime = millis();
} else {
Serial.println("[ERROR] Failed to initialize PCF8574!");
Serial.println("[ERROR] Please check:");
Serial.println(" - I2C connections (SDA=D2/GPIO4, SCL=D1/GPIO5)");
Serial.println(" - PCF8574 I2C address (default 0x20, check A0/A1/A2 pins)");
Serial.println(" - PCF8574 power supply (VCC and GND)");
Serial.println();
Serial.println("System will continue but relay control may not work.");
}
delay(1000);
}
void loop(void)
{
// Check for serial commands
if (Serial.available()) {
char inChar = Serial.read();
// Handle enter key (carriage return or newline)
if (inChar == '\r' || inChar == '\n') {
if (commandBuffer.length() > 0) {
Serial.println(); // Echo newline
Serial.print("[COMMAND] ");
Serial.println(commandBuffer);
processSerialCommand(commandBuffer);
commandBuffer = ""; // Clear buffer
}
}
// Handle backspace
else if (inChar == '\b') {
if (commandBuffer.length() > 0) {
commandBuffer.remove(commandBuffer.length() - 1);
Serial.print(inChar); // Echo backspace
}
}
// Add character to buffer
else if (inChar >= 32 && inChar <= 126) { // Printable ASCII characters
commandBuffer += inChar;
Serial.print(inChar); // Echo character
}
}
// Check if voice command was requested via serial
if (voiceCommandRequested && (millis() - voiceCommandRequestTime >= voiceCommandDebounce)) {
voiceCommandRequested = false;
processVoiceCommand();
}
// Periodic status monitoring and logging
if (millis() - lastStatusTime >= statusInterval) {
lastStatusTime = millis();
printRelayStatusMonitoring();
}
// Small delay to prevent watchdog timeout
delay(10);
}
// Initialize VoiceAssistant with WiFi and Google Cloud API credentials
void initializeVoiceAssistant(void) {
Serial.println("=== Initializing Voice Assistant System ===");
// Create VoiceAssistant instance
voiceAssistant = new VoiceAssistant(&relayController);
if (voiceAssistant == nullptr) {
Serial.println("[ERROR] Failed to allocate memory for VoiceAssistant!");
return;
}
// Begin voice assistant initialization with WiFi and API credentials
if (voiceAssistant->begin(WIFI_SSID, WIFI_PASSWORD, GOOGLE_API_KEY, GOOGLE_PROJECT_ID)) {
Serial.println("[SUCCESS] Voice Assistant initialized successfully!");
// Configure audio settings (16kHz, Mono, 16-bit, 10 second timeout)
voiceAssistant->setAudioConfig(16000, 1, 16, 10000);
// Print voice assistant status
voiceAssistant->printStatus();
voiceAssistant->printConfiguration();
Serial.println("=== Voice Assistant Ready ===");
Serial.println("[INFO] Voice commands are now available!");
Serial.println("[INFO] Type 'voice' command to process voice commands");
Serial.println("[INFO] Type 'vtest ' to test voice parsing");
Serial.println();
} else {
Serial.println("[ERROR] Failed to initialize Voice Assistant!");
Serial.println("[ERROR] WiFi connection failed or API credentials invalid");
Serial.println("[INFO] System will continue with relay control only");
// Clean up on failure
delete voiceAssistant;
voiceAssistant = nullptr;
}
Serial.println("========================================");
Serial.println();
}
// Process voice command from microphone
void processVoiceCommand(void) {
if (voiceAssistant == nullptr) {
Serial.println("[ERROR] Voice Assistant not initialized!");
return;
}
if (!voiceAssistant->isInitialized()) {
Serial.println("[ERROR] Voice Assistant not ready!");
return;
}
Serial.println();
Serial.println("================================================");
Serial.println(" Starting Voice Command Processing ");
Serial.println("================================================");
// Process voice command from microphone
VoiceCommandResult result = voiceAssistant->processVoiceCommand();
Serial.println();
Serial.print("[VOICE] Processing Result: ");
switch(result) {
case VOICE_CMD_SUCCESS:
Serial.println("SUCCESS");
Serial.println("[VOICE] Command executed successfully!");
break;
case VOICE_CMD_WIFI_NOT_CONNECTED:
Serial.println("ERROR - WiFi Not Connected");
Serial.println("[VOICE] Attempting to reconnect to WiFi...");
voiceAssistant->reconnectWiFi();
break;
case VOICE_CMD_API_ERROR:
Serial.println("ERROR - Google Cloud API Error");
Serial.println("[VOICE] Failed to communicate with Google Cloud Speech-to-Text API");
break;
case VOICE_CMD_AUDIO_CAPTURE_FAILED:
Serial.println("ERROR - Audio Capture Failed");
Serial.println("[VOICE] Failed to capture audio from microphone");
break;
case VOICE_CMD_PARSE_FAILED:
Serial.println("ERROR - Command Parse Failed");
Serial.println("[VOICE] Could not parse the recognized speech into a valid command");
break;
case VOICE_CMD_INVALID_RELAY:
Serial.println("ERROR - Invalid Relay Number");
Serial.println("[VOICE] The recognized relay number is out of range (0-7)");
break;
case VOICE_CMD_TIMEOUT:
Serial.println("ERROR - Command Timeout");
Serial.println("[VOICE] Voice command processing timed out");
break;
case VOICE_CMD_BUSY:
Serial.println("ERROR - System Busy");
Serial.println("[VOICE] Voice Assistant is currently busy processing a command");
break;
default:
Serial.println("UNKNOWN ERROR");
break;
}
Serial.println();
Serial.println("================================================");
Serial.println();
// Print current relay status
relayController.printStatus();
// Print voice command log
voiceAssistant->printVoiceCommandLog();
}
// Process serial commands from user
void processSerialCommand(String command) {
// Convert to lowercase for case-insensitive handling
command.toLowerCase();
// Trim whitespace
command.trim();
// Empty command
if (command.length() == 0) {
return;
}
// Help command
if (command == "help" || command == "?") {
printAvailableCommands();
return;
}
// Status command - print current relay states
if (command == "status" || command == "s") {
Serial.println();
relayController.printStatus();
Serial.println();
return;
}
// Logs command - print relay change logs
if (command == "logs" || command == "l") {
Serial.println();
printRelayLogs();
Serial.println();
return;
}
// Clear logs command
if (command == "clrlogs" || command == "cl") {
logIndex = 0;
Serial.println("[INFO] Relay logs cleared");
Serial.println();
return;
}
// Voice Assistant commands
if (voiceAssistant != nullptr) {
// Voice command - process audio from microphone
if (command == "voice" || command == "v") {
voiceCommandRequested = true;
voiceCommandRequestTime = millis();
return;
}
// Voice test command - test voice parsing with a string
if (command.startsWith("vtest ")) {
String testCommand = command.substring(6);
testCommand.trim();
if (testCommand.length() > 0) {
Serial.println();
Serial.println("=== Testing Voice Command Parsing ===");
Serial.print("[VOICE] Test command: ");
Serial.println(testCommand);
VoiceCommandResult result = voiceAssistant->executeCommandFromString(testCommand);
Serial.println();
Serial.print("[VOICE] Parse result: ");
switch(result) {
case VOICE_CMD_SUCCESS:
Serial.println("SUCCESS");
break;
case VOICE_CMD_PARSE_FAILED:
Serial.println("PARSE FAILED");
break;
case VOICE_CMD_INVALID_RELAY:
Serial.println("INVALID RELAY");
break;
default:
Serial.println("ERROR");
break;
}
Serial.println("[VOICE] ====================================");
Serial.println();
relayController.printStatus();
} else {
Serial.println("[ERROR] Please provide a test command. Usage: vtest ");
}
return;
}
// Voice command log command
if (command == "vlogs" || command == "vl") {
voiceAssistant->printVoiceCommandLog();
return;
}
// Clear voice command logs
if (command == "vclrlogs" || command == "vcl") {
voiceAssistant->clearVoiceCommandLog();
Serial.println();
return;
}
// Voice assistant status command
if (command == "vstatus" || command == "vs") {
voiceAssistant->printStatus();
return;
}
// Voice assistant configuration command
if (command == "vconfig" || command == "vc") {
voiceAssistant->printConfiguration();
return;
}
}
// Turn off all relays
if (command == "alloff" || command == "off") {
Serial.println();
relayController.turnOffAll();
logRelayStateChange(255, false, "ALL OFF");
relayController.printStatus();
Serial.println();
return;
}
// Test mode - cycle through all relays
if (command == "test" || command == "t") {
Serial.println();
Serial.println("[TEST] Starting relay test sequence...");
Serial.println("[TEST] Turning ON each relay for 1 second...");
Serial.println();
for (uint8_t i = 0; i < 8; i++) {
Serial.print("[TEST] Testing Relay ");
Serial.println(i);
relayController.turnOn(i);
delay(1000);
relayController.turnOff(i);
delay(200);
}
Serial.println();
Serial.println("[TEST] Relay test sequence completed!");
Serial.println();
return;
}
// Relay control commands
if (command.startsWith("on ") || command.startsWith("off ") ||
command.startsWith("toggle ") || command.startsWith("tog ")) {
handleRelayCommand(command);
return;
}
// Check for single digit commands (quick on/off)
if (command.length() == 1 && isDigit(command[0])) {
uint8_t relayNum = command[0] - '0';
if (relayNum < 8) {
Serial.println();
relayController.toggle(relayNum);
logRelayStateChange(relayNum, relayController.getState(relayNum), "TOGGLE");
relayController.printStatus();
Serial.println();
return;
}
}
// Unknown command
Serial.println("[ERROR] Unknown command. Type 'help' for available commands.");
Serial.println();
}
// Handle relay-specific commands (on/off/toggle)
void handleRelayCommand(String command) {
String action = "";
uint8_t relayNum = 255;
Serial.println();
// Parse "on" commands
if (command.startsWith("on ")) {
action = "ON";
command = command.substring(3);
command.trim();
if (command.length() > 0 && isDigit(command[0])) {
relayNum = command[0] - '0';
}
}
// Parse "off" commands
else if (command.startsWith("off ")) {
action = "OFF";
command = command.substring(4);
command.trim();
if (command.length() > 0 && isDigit(command[0])) {
relayNum = command[0] - '0';
}
}
// Parse "toggle" or "tog" commands
else if (command.startsWith("toggle ")) {
action = "TOGGLE";
command = command.substring(7);
command.trim();
if (command.length() > 0 && isDigit(command[0])) {
relayNum = command[0] - '0';
}
}
else if (command.startsWith("tog ")) {
action = "TOGGLE";
command = command.substring(4);
command.trim();
if (command.length() > 0 && isDigit(command[0])) {
relayNum = command[0] - '0';
}
}
// Execute command if relay number is valid
if (relayNum < 8) {
if (action == "ON") {
relayController.turnOn(relayNum);
logRelayStateChange(relayNum, true, "ON");
}
else if (action == "OFF") {
relayController.turnOff(relayNum);
logRelayStateChange(relayNum, false, "OFF");
}
else if (action == "TOGGLE") {
relayController.toggle(relayNum);
logRelayStateChange(relayNum, relayController.getState(relayNum), "TOGGLE");
}
relayController.printStatus();
} else {
Serial.print("[ERROR] Invalid relay number. Use 0-7. Got: ");
Serial.println(command);
}
Serial.println();
}
// Print available commands
void printAvailableCommands(void) {
Serial.println();
Serial.println("================================================");
Serial.println(" Available Serial Commands ");
Serial.println("================================================");
Serial.println();
Serial.println("Relay Control Commands:");
Serial.println(" on - Turn ON relay n (0-7)");
Serial.println(" off - Turn OFF relay n (0-7)");
Serial.println(" toggle - Toggle relay n (0-7)");
Serial.println(" tog - Toggle relay n (0-7) [shorthand]");
Serial.println(" - Quick toggle relay n (0-7)");
Serial.println(" alloff - Turn OFF all relays");
Serial.println(" off - Turn OFF all relays [shorthand]");
Serial.println();
Serial.println("Voice Assistant Commands (requires WiFi & API credentials):");
Serial.println(" voice, v - Process voice command from microphone");
Serial.println(" vtest - Test voice parsing with command string");
Serial.println(" vlogs, vl - Display voice command logs");
Serial.println(" vclrlogs,vc - Clear voice command logs");
Serial.println(" vstatus, vs - Display voice assistant status");
Serial.println(" vconfig, vc - Display voice assistant configuration");
Serial.println();
Serial.println("System Commands:");
Serial.println(" status, s - Display current relay status");
Serial.println(" logs, l - Display relay change log");
Serial.println(" clrlogs, cl - Clear relay change log");
Serial.println(" test, t - Run relay test sequence");
Serial.println(" help, ? - Display this help message");
Serial.println();
Serial.println("Example: Type 'on 0' to turn ON relay 0");
Serial.println("Example: Type 'vtest turn on relay 3' to test voice parsing");
Serial.println("================================================");
Serial.println();
}
// Log relay state changes
void logRelayStateChange(uint8_t relayNum, bool newState, const char* action) {
// Special handling for "ALL OFF" action
if (relayNum == 255) {
// Log all relays being turned off
for (uint8_t i = 0; i < 8; i++) {
RelayLog& log = relayLogs[logIndex % 10];
log.timestamp = millis();
log.relayNum = i;
log.state = false;
strncpy(log.action, action, sizeof(log.action) - 1);
log.action[sizeof(log.action) - 1] = '\0';
logIndex++;
}
} else {
RelayLog& log = relayLogs[logIndex % 10];
log.timestamp = millis();
log.relayNum = relayNum;
log.state = newState;
strncpy(log.action, action, sizeof(log.action) - 1);
log.action[sizeof(log.action) - 1] = '\0';
logIndex++;
}
}
// Print relay state change logs
void printRelayLogs(void) {
Serial.println("=== Relay Change Log (Last 10 changes) ===");
if (logIndex == 0) {
Serial.println("No relay changes recorded yet.");
} else {
// Calculate start index for circular buffer
uint8_t startIdx = (logIndex >= 10) ? (logIndex % 10) : 0;
uint8_t count = (logIndex >= 10) ? 10 : logIndex;
for (uint8_t i = 0; i < count; i++) {
uint8_t idx = (startIdx + i) % 10;
RelayLog& log = relayLogs[idx];
Serial.print("[");
Serial.print(log.timestamp / 1000);
Serial.print("s] Relay ");
Serial.print(log.relayNum);
Serial.print(": ");
Serial.print(log.state ? "ON" : "OFF");
Serial.print(" (");
Serial.print(log.action);
Serial.println(")");
}
}
Serial.println("===========================================");
}
// Print relay status monitoring with periodic updates
void printRelayStatusMonitoring(void) {
// Calculate uptime
uint32_t uptimeSeconds = millis() / 1000;
uint8_t hours = uptimeSeconds / 3600;
uint8_t minutes = (uptimeSeconds % 3600) / 60;
uint8_t seconds = uptimeSeconds % 60;
Serial.println();
Serial.println("--------- Relay Status Monitor (Periodic) --------");
Serial.print("Uptime: ");
if (hours < 10) Serial.print("0");
Serial.print(hours);
Serial.print(":");
if (minutes < 10) Serial.print("0");
Serial.print(minutes);
Serial.print(":");
if (seconds < 10) Serial.print("0");
Serial.println(seconds);
// Print all relay states
Serial.println("Current Relay States:");
for (uint8_t i = 0; i < 8; i++) {
Serial.print(" Relay ");
Serial.print(i);
Serial.print(": ");
Serial.println(relayController.getState(i) ? "ON " : "OFF");
}
// Print number of logged changes
Serial.print("Total logged relay changes: ");
Serial.println(logIndex);
// Print voice assistant status if available
if (voiceAssistant != nullptr && voiceAssistant->isInitialized()) {
Serial.print("Voice Assistant WiFi: ");
Serial.println(voiceAssistant->isWiFiConnected() ? "CONNECTED" : "DISCONNECTED");
Serial.print("Voice Commands Logged: ");
Serial.println(voiceAssistant->getCommandLogCount());
// Attempt to reconnect WiFi if disconnected
if (!voiceAssistant->isWiFiConnected()) {
Serial.println("[INFO] Attempting WiFi reconnection...");
voiceAssistant->reconnectWiFi();
}
}
Serial.println("--------------------------------------------------");
Serial.println();
}
/* END CODE */