#include <Arduino.h>
const uint8_t LIGHT_PINS[3] = {2, 4, 5}; // LIGHT1..3
const uint8_t TUBE_PINS[4] = {12, 13, 14, 15}; // TUBE1..4
const uint8_t FAN_PINS[4] = {18, 19, 21, 22}; // FAN_S1..4
struct SystemState {
bool lights[3] = {false, false, false};
bool tubes[4] = {false, false, false, false};
uint8_t fanSpeed = 0; // 0=OFF, 1..4
bool fault = false;
};
SystemState state;
String rxLine = "";
unsigned long lastHeartbeatMs = 0;
const unsigned long HEARTBEAT_MS = 10000;
void trimAndUpper(String &s) {
s.trim();
s.toUpperCase();
}
void fanAllOff() {
for (int i = 0; i < 4; i++) {
digitalWrite(FAN_PINS[i], LOW);
}
}
void applyOutputs() {
for (int i = 0; i < 3; i++) {
digitalWrite(LIGHT_PINS[i], state.lights[i] ? HIGH : LOW);
}
for (int i = 0; i < 4; i++) {
digitalWrite(TUBE_PINS[i], state.tubes[i] ? HIGH : LOW);
}
fanAllOff();
if (state.fanSpeed >= 1 && state.fanSpeed <= 4) {
digitalWrite(FAN_PINS[state.fanSpeed - 1], HIGH);
}
}
void sendStatus() {
Serial.print("STATUS FAN=");
Serial.print(state.fanSpeed);
for (int i = 0; i < 3; i++) {
Serial.print(" LIGHT");
Serial.print(i + 1);
Serial.print("=");
Serial.print(state.lights[i] ? 1 : 0);
}
for (int i = 0; i < 4; i++) {
Serial.print(" TUBE");
Serial.print(i + 1);
Serial.print("=");
Serial.print(state.tubes[i] ? 1 : 0);
}
Serial.print(" FAULT=");
Serial.println(state.fault ? 1 : 0);
}
void printHelp() {
Serial.println("AVAILABLE COMMANDS:");
Serial.println("GET STATUS");
Serial.println("SET LIGHT1 ON|OFF");
Serial.println("SET LIGHT2 ON|OFF");
Serial.println("SET LIGHT3 ON|OFF");
Serial.println("SET TUBE1 ON|OFF");
Serial.println("SET TUBE2 ON|OFF");
Serial.println("SET TUBE3 ON|OFF");
Serial.println("SET TUBE4 ON|OFF");
Serial.println("SET FAN OFF");
Serial.println("SET FAN SPEED 1");
Serial.println("SET FAN SPEED 2");
Serial.println("SET FAN SPEED 3");
Serial.println("SET FAN SPEED 4");
Serial.println("RESET");
Serial.println("HELP");
}
void clearAll() {
for (int i = 0; i < 3; i++) state.lights[i] = false;
for (int i = 0; i < 4; i++) state.tubes[i] = false;
state.fanSpeed = 0;
state.fault = false;
applyOutputs();
}
bool setLight(int idx, bool on) {
if (idx < 0 || idx >= 3) return false;
state.lights[idx] = on;
applyOutputs();
Serial.print("OK LIGHT");
Serial.print(idx + 1);
Serial.print(" ");
Serial.println(on ? "ON" : "OFF");
return true;
}
bool setTube(int idx, bool on) {
if (idx < 0 || idx >= 4) return false;
state.tubes[idx] = on;
applyOutputs();
Serial.print("OK TUBE");
Serial.print(idx + 1);
Serial.print(" ");
Serial.println(on ? "ON" : "OFF");
return true;
}
bool setFan(uint8_t speed) {
if (speed > 4) {
Serial.println("ERR FAN_SPEED_RANGE");
return false;
}
fanAllOff();
state.fanSpeed = speed;
applyOutputs();
if (speed == 0) {
Serial.println("OK FAN OFF");
} else {
Serial.print("OK FAN SPEED ");
Serial.println(speed);
}
return true;
}
void processSetDevice(const String &device, const String &value) {
for (int i = 0; i < 3; i++) {
String name = "LIGHT" + String(i + 1);
if (device == name) {
if (value == "ON") setLight(i, true);
else if (value == "OFF") setLight(i, false);
else Serial.println("ERR INVALID_VALUE");
return;
}
}
for (int i = 0; i < 4; i++) {
String name = "TUBE" + String(i + 1);
if (device == name) {
if (value == "ON") setTube(i, true);
else if (value == "OFF") setTube(i, false);
else Serial.println("ERR INVALID_VALUE");
return;
}
}
if (device == "FAN") {
if (value == "OFF") setFan(0);
else Serial.println("ERR USE SET FAN SPEED <1..4> OR SET FAN OFF");
return;
}
Serial.println("ERR INVALID_DEVICE");
}
void processCommand(String line) {
trimAndUpper(line);
if (line.length() == 0) return;
if (line == "GET STATUS") {
sendStatus();
return;
}
if (line == "HELP") {
printHelp();
return;
}
if (line == "RESET") {
clearAll();
Serial.println("OK RESET");
sendStatus();
return;
}
if (line.startsWith("SET FAN SPEED ")) {
String speedStr = line.substring(String("SET FAN SPEED ").length());
speedStr.trim();
int speed = speedStr.toInt();
if (speed < 1 || speed > 4) {
Serial.println("ERR FAN_SPEED_RANGE");
return;
}
setFan((uint8_t)speed);
return;
}
if (line.startsWith("SET ")) {
String rest = line.substring(4);
rest.trim();
int sp = rest.indexOf(' ');
if (sp < 0) {
Serial.println("ERR INVALID_FORMAT");
return;
}
String device = rest.substring(0, sp);
String value = rest.substring(sp + 1);
trimAndUpper(device);
trimAndUpper(value);
processSetDevice(device, value);
return;
}
Serial.println("ERR UNKNOWN_COMMAND");
}
void serialRxTask() {
while (Serial.available()) {
char c = (char)Serial.read();
if (c == '\n' || c == '\r') {
if (rxLine.length() > 0) {
processCommand(rxLine);
rxLine = "";
}
} else {
if (rxLine.length() < 120) {
rxLine += c;
} else {
rxLine = "";
Serial.println("ERR LINE_TOO_LONG");
}
}
}
}
void faultMonitorTask() {
int active = 0;
for (int i = 0; i < 4; i++) {
if (digitalRead(FAN_PINS[i]) == HIGH) active++;
}
if (active > 1) {
state.fault = true;
fanAllOff();
state.fanSpeed = 0;
Serial.println("ERR FAN_INTERLOCK_FAULT");
}
}
void heartbeatTask() {
unsigned long now = millis();
if (now - lastHeartbeatMs >= HEARTBEAT_MS) {
lastHeartbeatMs = now;
sendStatus();
}
}
void setup() {
Serial.begin(115200);
for (int i = 0; i < 3; i++) {
pinMode(LIGHT_PINS[i], OUTPUT);
digitalWrite(LIGHT_PINS[i], LOW);
}
for (int i = 0; i < 4; i++) {
pinMode(TUBE_PINS[i], OUTPUT);
digitalWrite(TUBE_PINS[i], LOW);
}
for (int i = 0; i < 4; i++) {
pinMode(FAN_PINS[i], OUTPUT);
digitalWrite(FAN_PINS[i], LOW);
}
clearAll();
Serial.println("====================================");
Serial.println("ESP32 SMART ROOM POWER CONTROLLER");
Serial.println("READY - Type HELP");
Serial.println("====================================");
sendStatus();
}
void loop() {
serialRxTask();
faultMonitorTask();
heartbeatTask();
}