#include #include #define EEPROM_SIZE 64 #define EEPROM_ADDR_LOC 0 // EEPROM location for I2C address #define EEPROM_NAME_LOC 1 // EEPROM location for device name #define DEVICE_NAME_LEN 24 #define DEFAULT_I2C_ADDR 0x08 // Default I2C address char deviceName[DEVICE_NAME_LEN] = "Relay"; uint8_t i2cAddress = DEFAULT_I2C_ADDR; // I2C command codes enum Command : uint8_t { CMD_SET_ADDR = 0x01, CMD_SET_NAME = 0x02, }; // --- RELAY FEATURE ADDITIONS --- const int relayPins[8] = {0, 1, 2, 3, 4, 5, 6, 7}; // GPIOs 0-7 uint8_t relayStates[8] = {0}; // 0=OFF, 1=ON unsigned long relayTimers[8] = {0}; // millis() when to turn off, 0 = no timer enum RelayCommand : uint8_t { CMD_RELAY_SET = 0x10, // [relay#][0|1] (relay# = 1..8) CMD_RELAY_ALL = 0x11, // [0|1] CMD_RELAY_TIMER = 0x12, // [relay#][ms_L][ms_H][ms_U][ms_T] CMD_RELAY_ALL_TIMER = 0x13 // [ms_L][ms_H][ms_U][ms_T] }; // *** INVERTED RELAY LOGIC *** // User relay numbers: 1–8. Array indices: 0–7. void setRelay(uint8_t relayNum, uint8_t state) { if (relayNum >= 1 && relayNum <= 8) { uint8_t idx = relayNum - 1; relayStates[idx] = (state == 1 ? 1 : 0); // Inverted logic: LOW = ON, HIGH = OFF digitalWrite(relayPins[idx], relayStates[idx] ? LOW : HIGH); } } void setAllRelays(uint8_t state) { for (uint8_t i = 1; i <= 8; i++) setRelay(i, state); } // --- END RELAY FEATURE ADDITIONS --- void storeAddressToEEPROM(uint8_t addr) { EEPROM.write(EEPROM_ADDR_LOC, addr); EEPROM.commit(); } uint8_t readAddressFromEEPROM() { uint8_t addr = EEPROM.read(EEPROM_ADDR_LOC); if (addr == 0xFF || addr == 0x00) { addr = DEFAULT_I2C_ADDR; storeAddressToEEPROM(addr); } return addr; } void storeNameToEEPROM(const char* name) { for (int i = 0; i < DEVICE_NAME_LEN; i++) { EEPROM.write(EEPROM_NAME_LOC + i, name[i]); if (name[i] == '\0') break; } EEPROM.commit(); } void readNameFromEEPROM(char* name) { for (int i = 0; i < DEVICE_NAME_LEN; i++) { name[i] = EEPROM.read(EEPROM_NAME_LOC + i); if (name[i] == '\0') break; } name[DEVICE_NAME_LEN - 1] = '\0'; } // Handle data received from master void onReceive(int len) { if (len < 1) return; uint8_t cmd = Wire.read(); switch (cmd) { case CMD_SET_ADDR: if (Wire.available()) { uint8_t newAddr = Wire.read(); if (newAddr > 0x00 && newAddr < 0x78) { storeAddressToEEPROM(newAddr); ESP.restart(); } } break; case CMD_SET_NAME: { int i = 0; while (Wire.available() && i < DEVICE_NAME_LEN - 1) { deviceName[i++] = Wire.read(); } deviceName[i] = '\0'; storeNameToEEPROM(deviceName); break; } // --- RELAY FEATURE ADDITIONS --- case CMD_RELAY_SET: if (Wire.available() >= 2) { uint8_t relayNum = Wire.read(); // Now 1..8 uint8_t state = Wire.read(); // 0|1 setRelay(relayNum, state); if (relayNum >= 1 && relayNum <= 8) relayTimers[relayNum-1] = 0; } break; case CMD_RELAY_ALL: if (Wire.available() >= 1) { uint8_t state = Wire.read(); // 0|1 setAllRelays(state); for (int i = 0; i < 8; i++) relayTimers[i] = 0; } break; case CMD_RELAY_TIMER: if (Wire.available() >= 5) { uint8_t relayNum = Wire.read(); // 1..8 uint32_t ms = Wire.read(); ms |= ((uint32_t)Wire.read()) << 8; ms |= ((uint32_t)Wire.read()) << 16; ms |= ((uint32_t)Wire.read()) << 24; setRelay(relayNum, 1); if (relayNum >= 1 && relayNum <= 8) relayTimers[relayNum-1] = millis() + ms; } break; case CMD_RELAY_ALL_TIMER: if (Wire.available() >= 4) { uint32_t ms = Wire.read(); ms |= ((uint32_t)Wire.read()) << 8; ms |= ((uint32_t)Wire.read()) << 16; ms |= ((uint32_t)Wire.read()) << 24; setAllRelays(1); unsigned long offTime = millis() + ms; for (int i = 0; i < 8; i++) relayTimers[i] = offTime; } break; // --- END RELAY FEATURE ADDITIONS --- default: break; } } // Handle data requested by master void onRequest() { String response = String(i2cAddress, HEX) + ",GENERIC," + deviceName; Wire.write((const uint8_t*)response.c_str(), response.length()); } void setup() { Serial.begin(115200); EEPROM.begin(EEPROM_SIZE); i2cAddress = readAddressFromEEPROM(); readNameFromEEPROM(deviceName); Wire.begin(i2cAddress); Wire.onReceive(onReceive); Wire.onRequest(onRequest); for (int i = 0; i < 8; i++) { pinMode(relayPins[i], OUTPUT); digitalWrite(relayPins[i], HIGH); // Inverted logic: HIGH = OFF at start } Serial.print("I2C Slave started at address 0x"); Serial.println(i2cAddress, HEX); Serial.print("Device name: "); Serial.println(deviceName); } void loop() { unsigned long now = millis(); for (int i = 0; i < 8; i++) { if (relayTimers[i] && now >= relayTimers[i]) { setRelay(i+1, 0); // Use relay number 1..8 relayTimers[i] = 0; } } }