182 lines
5.1 KiB
C++
182 lines
5.1 KiB
C++
#include <Wire.h>
|
||
#include <EEPROM.h>
|
||
|
||
#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;
|
||
}
|
||
}
|
||
}
|