diff --git a/README.md b/README.md
index 8840e1f..22f3ee7 100644
--- a/README.md
+++ b/README.md
@@ -56,85 +56,44 @@ At **FABLAB Bahrain**, we're revolutionizing the world of IoT devices with our c
### Installation
1. Download or clone this repository.
-2. Copy the `I2CRelay`, `I2CMasterUtils`, and related library folders to your Arduino `libraries` directory.
+2. Copy the `I2CRelay`, and related library folders to your Arduino `libraries` directory.
3. Restart the Arduino IDE.
#### Files
The library is composed of several files, each with a specific role:
-
-1. **I2CMasterUtils.h** [3]
- - This header file defines the `I2CMasterUtils` class which contains static methods for I2C operations such as scanning for devices and changing slave addresses.
-2. **I2CMasterUtils.cpp** [2]
- - Contains the implementation of methods defined in `I2CMasterUtils.h`.
-
-3. **I2CRelay.h** [5]
+1. **I2CRelay.h** [5]
- Defines the `I2CRelay` class, which extends the functionality to control relays over I2C.
-4. **I2CRelay.cpp** [4]
+2. **I2CRelay.cpp** [4]
- Contains the implementation of methods for the `I2CRelay` class.
---
### Versatility
-The I2CMotor offers exceptional versatility, as it can function as a standalone device and can be adapted to be USB-controlled instead of relying solely on I2C communication. This flexibility allows users to easily integrate the motor into a wide range of projects and control environments. Furthermore, the motor’s firmware can be reprogrammed to support Wi-Fi and Bluetooth connectivity, enabling wireless control and making it ideal for modern applications in robotics, automation, and IoT. This adaptability ensures that the I2CMotor can meet diverse requirements and evolve with your project’s needs.
+The I2CRelay library offers broad versatility for automation through its configurable I2C addressing, expandable relay control, and cross-platform compatibility. User-friendly commands and customizable hardware pins simplify integration into diverse projects, from IoT and home automation to industrial control. Its efficient, non-blocking operation and persistent configuration via EEPROM ensure a scalable and reliable relay control system.
---
### API Overview
-- **scanI2C(Stream &output = Serial)**
- - Scans the I2C bus for connected devices and prints their addresses.
-
-- **changeSlaveAddress(uint8_t oldAddr, uint8_t newAddr, Stream &output = Serial)**
- - Sends a command to change the slave address of a device.
+# I2CRelay Library API Overview
-- **parseChangeAddressCommand(const String &input, uint8_t &oldAddr, uint8_t &newAddr)**
- - Parses an input string to extract the old and new slave addresses from a "ch" command.
+| Method / Constructor | Description | Parameters / Usage Example |
+|---------------------------------------------------|-------------------------------------------------------------------------------------------------------------|------------------------------------------------------|
+| `I2CRelay(uint8_t address = 0x08, uint8_t sda = 8, uint8_t scl = 3)` | Create an I2CRelay object with optional I2C address and pin selection. | `I2CRelay relay;`
`I2CRelay relay(0x10, 21, 22);` |
+| `void begin()` | Initialize I2C communication with the configured pins. | `relay.begin();` |
+| `void setRelay(uint8_t relayNum, uint8_t state)` | Turn relay (1–8) ON or OFF. | `relay.setRelay(3, 1); // ON relay 3` |
+| `void setAllRelays(uint8_t state)` | Turn all relays ON or OFF at once. | `relay.setAllRelays(0); // OFF all relays` |
+| `void setDeviceName(const String &name)` | Set the name of the I2C relay device (up to 24 chars). | `relay.setDeviceName("MainPanel");` |
+| `void setI2CAddress(uint8_t newAddr)` | Change the I2C address of the relay slave. | `relay.setI2CAddress(0x12);` |
+| `String getIdentification()` | Query the slave for its identification string (address, type, name, states). | `String id = relay.getIdentification();` |
+| `void handleSerialCommand(const String &cmd)` | Parse and execute a serial command string (e.g., `"r 2 1"`, `"all 0"`). | `relay.handleSerialCommand("r 2 1");` |
-#### Methods
+**Notes:**
+- `relayNum` is 1–8 (user-facing), mapped internally to 0–7.
+- `state` is `1` (ON) or `0` (OFF).
+- All methods are non-blocking except `setAllRelays`, which adds a small delay between commands.
-- **I2CRelay(uint8_t slaveAddress)**
- - Constructor that initializes the `I2CRelay` object with the given slave address.
-
-- **begin()**
- - Initializes the I2C bus for communication.
-
-- **setRelay(uint8_t relayIndex, bool state)**
- - Sets the state of a specific relay on the slave device.
-
-- **scanDevices(uint8_t* foundAddresses, uint8_t maxDevices)**
- - Scans the I2C bus for connected devices and returns their addresses up to `maxDevices`.
-
-- **setAddress(uint8_t newAddress)**
- - Changes the address of the relay slave.
-
-- **handleI2CCommand()**
- - Handles incoming I2C commands.
-
-### Usage Example
-
-To use this library, you would typically include the necessary files and create instances of `I2CMasterUtils` and `I2CRelay`. Here’s a simple example:
-
-```cpp
-#include "I2CMasterUtils.h"
-#include "I2CRelay.h"
-
-void setup() {
- Serial.begin(9600);
-
- I2CMasterUtils::scanI2C();
-
- uint8_t newAddress = 0x54;
- uint8_t oldAddress = CMD_CHANGE_ADDR; // Assuming CMD_CHANGE_ADDR is defined in I2CCommands.h
- I2CMasterUtils::changeSlaveAddress(oldAddress, newAddress);
-}
-
-void loop() {
- // Example usage of I2CRelay
- I2CRelay relay(0x54); // Use the newly assigned address
- relay.begin();
- relay.setRelay(1, HIGH); // Set relay 1 to ON
-}
\ No newline at end of file
diff --git a/examples/Master/Master.ino b/examples/Master/Master.ino
index a83b5de..2be9a79 100644
--- a/examples/Master/Master.ino
+++ b/examples/Master/Master.ino
@@ -1,65 +1,23 @@
-#include
#include
-#include
-I2CRelay relayMaster(0x08); // Default address, can be changed via setAddress()
+I2CRelay relay;
void setup() {
Serial.begin(115200);
- delay(1000);
- relayMaster.begin();
- Serial.println("Enter:");
- Serial.println(" s - to scan I2C bus");
- Serial.println(" ch - to change slave address");
- Serial.println(" - to set relay (e.g. 3 on)");
+ relay.begin();
+ Serial.println("8-Relay I2C Master Ready.");
+ Serial.println("Commands:");
+ Serial.println(" r <0|1> - Set relay n (1-8) OFF/ON");
+ Serial.println(" all <0|1> - Set all relays OFF/ON");
+ Serial.println(" name - Set device name");
+ Serial.println(" addr - Set I2C address (8-119)");
+ Serial.println(" id - Query identification string");
}
void loop() {
if (Serial.available()) {
- String input = Serial.readStringUntil('\n');
- input.trim();
-
- if (input == "s") {
- I2CMasterUtils::scanI2C();
- } else if (input.startsWith("ch")) {
- uint8_t oldAddr, newAddr;
- if (I2CMasterUtils::parseChangeAddressCommand(input, oldAddr, newAddr)) {
- I2CMasterUtils::changeSlaveAddress(oldAddr, newAddr);
- relayMaster.setAddress(newAddr); // Update master's address
- Serial.print("Changed address to 0x");
- Serial.println(newAddr, HEX);
- } else {
- Serial.println("Invalid command format. Use: ch ");
- }
- } else {
- int spaceIndex = input.indexOf(' ');
- if (spaceIndex > 0) {
- String relayStr = input.substring(0, spaceIndex);
- String stateStr = input.substring(spaceIndex + 1);
-
- int relayNum = relayStr.toInt();
- bool state = false;
-
- stateStr.toLowerCase();
- if (stateStr == "on") state = true;
- else if (stateStr == "off") state = false;
- else {
- Serial.println("Invalid state! Use 'on' or 'off'.");
- return;
- }
-
- if (relayNum >= 1 && relayNum <= 8) {
- relayMaster.setRelay(relayNum, state);
- Serial.print("Relay ");
- Serial.print(relayNum);
- Serial.print(" turned ");
- Serial.println(state ? "ON" : "OFF");
- } else {
- Serial.println("Invalid relay number! Use 1-8.");
- }
- } else {
- Serial.println("Invalid command! Format: ");
- }
- }
+ String cmd = Serial.readStringUntil('\n');
+ cmd.trim();
+ relay.handleSerialCommand(cmd);
}
}
diff --git a/examples/Slave/Slave.ino b/examples/Slave/Slave.ino
index f2536ca..cf8eecf 100644
--- a/examples/Slave/Slave.ino
+++ b/examples/Slave/Slave.ino
@@ -1,65 +1,105 @@
#include
#include
-// EEPROM settings
-#define ADDR_EEPROM_LOC 0
-#define EEPROM_SIZE 64
+#define DEFAULT_ADDRESS 0x08
+#define MAX_NAME_LENGTH 24
-// Relay pins: GPIO0 to GPIO7
+// EEPROM Structure
+struct Config {
+ uint8_t address;
+ char deviceName[MAX_NAME_LENGTH+1];
+ uint8_t relayStates;
+};
+
+Config config;
const int relayPins[8] = {0, 1, 2, 3, 4, 5, 6, 7}; // GPIOs 0-7
-uint8_t i2cAddress;
-
-// Unified I2C receive event
-void receiveEvent(int howMany) {
- if (howMany >= 2) {
- uint8_t first = Wire.read();
- uint8_t second = Wire.read();
-
- // Check for address change command
- if (first == 0xAA) {
- // Change I2C address and store in EEPROM
- uint8_t newAddr = second;
- EEPROM.write(ADDR_EEPROM_LOC, newAddr);
- EEPROM.commit();
- Serial.print("I2C address changed to: 0x");
- Serial.println(newAddr, HEX);
- delay(100);
- ESP.restart();
- return;
- }
-
- // Otherwise, treat as relay control
- int relay = first; // Relay number: 1-8
- int state = second; // State: 1=ON, 0=OFF
- if (relay >= 1 && relay <= 8) {
- digitalWrite(relayPins[relay - 1], state ? LOW : HIGH); // Active LOW relays
- }
- }
-}
+void handleCommand(int numBytes);
+void saveConfig();
+void loadConfig();
+void emergencyStop();
void setup() {
Serial.begin(115200);
- EEPROM.begin(EEPROM_SIZE);
-
- // Read I2C address from EEPROM or use default
- i2cAddress = EEPROM.read(ADDR_EEPROM_LOC);
- if (i2cAddress == 0xFF || i2cAddress == 0) i2cAddress = 0x08; // Default address
-
- Serial.print("Using I2C address: 0x");
- Serial.println(i2cAddress, HEX);
-
- // Initialize relay pins
- for (int i = 0; i < 8; i++) {
+ EEPROM.begin(sizeof(Config));
+ loadConfig();
+
+ // Initialize relays
+ for(int i=0; i<8; i++) {
pinMode(relayPins[i], OUTPUT);
- digitalWrite(relayPins[i], HIGH); // Relays OFF initially (active LOW)
+ digitalWrite(relayPins[i], HIGH); // Active-low logic
}
-
- // Start I2C slave
- Wire.begin(i2cAddress);
- Wire.onReceive(receiveEvent);
+
+ Wire.onReceive(handleCommand);
+ Wire.onRequest([]() {
+ String response = String(config.address) + ",8-relay," +
+ config.deviceName + "," +
+ String(config.relayStates, BIN);
+ Wire.print(response);
+ });
+
+ Wire.begin((uint8_t)config.address);
}
-void loop() {
- // Nothing needed here for basic operation
+void loop() { /* Empty */ }
+
+void handleCommand(int numBytes) {
+ uint8_t cmd = Wire.read();
+
+ switch(cmd) {
+ case 0xA0: // Set relay state
+ if(numBytes == 3) {
+ uint8_t relay = Wire.read();
+ uint8_t state = Wire.read();
+ if(relay < 8) {
+ digitalWrite(relayPins[relay], state ? LOW : HIGH);
+ bitWrite(config.relayStates, relay, state);
+ }
+ }
+ break;
+
+ case 0xB0: // Emergency stop
+ emergencyStop();
+ break;
+
+ case 0xC0: // Set address
+ if(numBytes == 2) {
+ config.address = Wire.read();
+ saveConfig();
+ Wire.begin((uint8_t)config.address);
+ }
+ break;
+
+ case 0xD0: // Set device name
+ if(numBytes > 1) {
+ uint8_t i = 0;
+ while(Wire.available() && i < MAX_NAME_LENGTH) {
+ config.deviceName[i++] = Wire.read();
+ }
+ config.deviceName[i] = '\0';
+ saveConfig();
+ }
+ break;
+ }
+}
+
+void emergencyStop() {
+ for(int i=0; i<8; i++) {
+ digitalWrite(relayPins[i], HIGH);
+ }
+ config.relayStates = 0;
+}
+
+void loadConfig() {
+ EEPROM.get(0, config);
+ if(config.address < 0x08 || config.address > 0x77) {
+ config.address = DEFAULT_ADDRESS;
+ strcpy(config.deviceName, "8-Relay Module");
+ saveConfig();
+ }
+}
+
+void saveConfig() {
+ EEPROM.put(0, config);
+ EEPROM.commit();
}
diff --git a/keywords.txt b/keywords.txt
index 0caac64..279f42d 100644
--- a/keywords.txt
+++ b/keywords.txt
@@ -1,16 +1,8 @@
-#######################################
-# Syntax coloring for Relay8I2C Library
-#######################################
-
-# Classes
-RelayI2CMaster KEYWORD1
-I2CMasterUtils KEYWORD1
-
-# Methods
+I2CRelay KEYWORD1
begin KEYWORD2
setRelay KEYWORD2
-scanI2C KEYWORD2
-changeSlaveAddress KEYWORD2
-
-# Constants
-CMD_CHANGE_ADDR LITERAL1
+setAllRelays KEYWORD2
+setDeviceName KEYWORD2
+setI2CAddress KEYWORD2
+getIdentification KEYWORD2
+handleSerialCommand KEYWORD2
diff --git a/library.properties b/library.properties
index 541b5e2..668980d 100644
--- a/library.properties
+++ b/library.properties
@@ -1,9 +1,9 @@
-name=FABLAB Relay
+name=I2CRelay
version=1.0.0
author=Fatima Idrees
maintainer=Fatima Idrees
-sentence=Control DC motors via I2C with scanning and configuration utilities.
-paragraph=Provides easy control of 8-relay modules via I2C communication with dynamic address configuration. Includes master/slave implementation for ESP8266/Arduino platforms.
+sentence=Simple I2C master library for controlling 8-channel relay modules.
+paragraph=I2CRelay allows easy control of up to 8 relays on a remote I2C slave device, with support for device naming, address changes, and identification queries. Designed for ESP32 and Arduino boards with configurable I2C pins.
category=Device Control
-url=https://github.com/fablabbh/I2CMasterUtils
+url=https://git.takeone.bh/fatimaxsen/I2CRelay.git
architectures=*
diff --git a/src/I2CCommands.h b/src/I2CCommands.h
deleted file mode 100644
index 2ae5463..0000000
--- a/src/I2CCommands.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef I2C_COMMANDS_H
-#define I2C_COMMANDS_H
-
-#define CMD_CHANGE_ADDR 0xAA // Use the same value as in your slave
-
-#endif
diff --git a/src/I2CMasterUtils.cpp b/src/I2CMasterUtils.cpp
deleted file mode 100644
index b8b04c4..0000000
--- a/src/I2CMasterUtils.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-#include "I2CMasterUtils.h"
-#include "I2CCommands.h"
-
-void I2CMasterUtils::scanI2C(Stream &output) {
- output.println("Scanning for I2C devices...");
- for (uint8_t addr = 1; addr < 127; addr++) {
- Wire.beginTransmission(addr);
- if (Wire.endTransmission() == 0) {
- output.print("Found device at 0x");
- output.println(addr, HEX);
- }
- }
-}
-
-void I2CMasterUtils::changeSlaveAddress(uint8_t oldAddr, uint8_t newAddr, Stream &output) {
- Wire.beginTransmission(oldAddr);
- Wire.write(CMD_CHANGE_ADDR); // Use the command code from I2CCommands.h
- Wire.write(newAddr);
- Wire.endTransmission();
- output.print("Sent address change command to 0x");
- output.print(oldAddr, HEX);
- output.print(" -> 0x");
- output.println(newAddr, HEX);
-}
-
-bool I2CMasterUtils::parseChangeAddressCommand(const String &input, uint8_t &oldAddr, uint8_t &newAddr) {
- if (!input.startsWith("ch")) return false;
- int o, n;
- int matched = sscanf(input.c_str(), "ch %x %x", &o, &n);
- if (matched == 2) {
- oldAddr = (uint8_t)o;
- newAddr = (uint8_t)n;
- return true;
- }
- return false;
-}
diff --git a/src/I2CMasterUtils.h b/src/I2CMasterUtils.h
deleted file mode 100644
index 5be05b9..0000000
--- a/src/I2CMasterUtils.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef I2CMASTERUTILS_H
-#define I2CMASTERUTILS_H
-
-#include
-#include
-
-class I2CMasterUtils {
-public:
- static void scanI2C(Stream &output = Serial);
- static void changeSlaveAddress(uint8_t oldAddr, uint8_t newAddr, Stream &output = Serial);
- static bool parseChangeAddressCommand(const String &input, uint8_t &oldAddr, uint8_t &newAddr);
-};
-
-#endif
diff --git a/src/I2CRelay.cpp b/src/I2CRelay.cpp
index 770b3c9..2b9ebef 100644
--- a/src/I2CRelay.cpp
+++ b/src/I2CRelay.cpp
@@ -1,35 +1,102 @@
-#include
-#include
#include "I2CRelay.h"
-#include "I2CMasterUtils.h"
-#include "I2CCommands.h"
-I2CRelay::I2CRelay(uint8_t slaveAddress) : _slaveAddress(slaveAddress) {}
-
-void I2CRelay::setAddress(uint8_t newAddress) {
- _slaveAddress = newAddress;
-}
+I2CRelay::I2CRelay(uint8_t address, uint8_t sda, uint8_t scl)
+ : _address(address), _sda(sda), _scl(scl) {}
void I2CRelay::begin() {
- Wire.begin();
+ Wire.begin(_sda, _scl);
}
-void I2CRelay::setRelay(uint8_t relayIndex, bool state) {
- Wire.beginTransmission(_slaveAddress);
- Wire.write(relayIndex);
- Wire.write(state ? 1 : 0);
+void I2CRelay::setRelay(uint8_t relayNum, uint8_t state) {
+ if (relayNum < 1 || relayNum > 8) return;
+ Wire.beginTransmission(_address);
+ Wire.write(0xA0);
+ Wire.write(relayNum - 1); // Map 1-8 to 0-7
+ Wire.write(state);
Wire.endTransmission();
}
-uint8_t I2CRelay::scanDevices(uint8_t* foundAddresses, uint8_t maxDevices) {
- uint8_t count = 0;
- for(uint8_t addr = 1; addr < 127; addr++) {
- Wire.beginTransmission(addr);
- if(Wire.endTransmission() == 0) {
- if(count < maxDevices) {
- foundAddresses[count++] = addr;
- }
- }
+void I2CRelay::setAllRelays(uint8_t state) {
+ for (uint8_t i = 1; i <= 8; i++) {
+ setRelay(i, state);
+ delay(10);
+ }
+}
+
+void I2CRelay::setDeviceName(const String &name) {
+ Wire.beginTransmission(_address);
+ Wire.write(0xD0);
+ for (uint8_t i = 0; i < name.length() && i < 24; i++) {
+ Wire.write(name[i]);
+ }
+ Wire.endTransmission();
+}
+
+void I2CRelay::setI2CAddress(uint8_t newAddr) {
+ if (newAddr < 8 || newAddr > 119) return;
+ Wire.beginTransmission(_address);
+ Wire.write(0xC0);
+ Wire.write(newAddr);
+ Wire.endTransmission();
+}
+
+String I2CRelay::getIdentification() {
+ Wire.requestFrom(_address, (uint8_t)40);
+ String id = "";
+ while (Wire.available()) {
+ char c = Wire.read();
+ id += c;
+ }
+ return id;
+}
+
+void I2CRelay::handleSerialCommand(const String &cmd) {
+ if (cmd.startsWith("r ")) {
+ int space1 = cmd.indexOf(' ');
+ int space2 = cmd.indexOf(' ', space1 + 1);
+ if (space1 > 0 && space2 > space1) {
+ int relay = cmd.substring(space1 + 1, space2).toInt();
+ int state = cmd.substring(space2 + 1).toInt();
+ if (relay >= 1 && relay <= 8 && (state == 0 || state == 1)) {
+ setRelay(relay, state);
+ Serial.print("Relay ");
+ Serial.print(relay);
+ Serial.print(" set to ");
+ Serial.println(state ? "ON" : "OFF");
+ } else {
+ Serial.println("Invalid relay number or state. Use 1-8 and 0/1.");
+ }
+ } else {
+ Serial.println("Usage: r <0|1>");
+ }
+ } else if (cmd.startsWith("all ")) {
+ int state = cmd.substring(4).toInt();
+ if (state == 0 || state == 1) {
+ setAllRelays(state);
+ Serial.print("All relays set to ");
+ Serial.println(state ? "ON" : "OFF");
+ } else {
+ Serial.println("Invalid state for all.");
+ }
+ } else if (cmd.startsWith("name ")) {
+ String name = cmd.substring(5);
+ setDeviceName(name);
+ Serial.print("Device name set to: ");
+ Serial.println(name);
+ } else if (cmd.startsWith("addr ")) {
+ int newAddr = cmd.substring(5).toInt();
+ if (newAddr >= 8 && newAddr <= 119) {
+ setI2CAddress(newAddr);
+ Serial.print("I2C address set to: ");
+ Serial.println(newAddr, HEX);
+ } else {
+ Serial.println("Invalid address.");
+ }
+ } else if (cmd == "id") {
+ String id = getIdentification();
+ Serial.print("ID: ");
+ Serial.println(id);
+ } else {
+ Serial.println("Unknown command.");
}
- return count;
}
diff --git a/src/I2CRelay.h b/src/I2CRelay.h
index 026d97e..2d69406 100644
--- a/src/I2CRelay.h
+++ b/src/I2CRelay.h
@@ -1,20 +1,27 @@
-#ifndef I2CRelay_H
-#define I2CRelay_H
+#ifndef I2CRELAY_H
+#define I2CRELAY_H
#include
#include
class I2CRelay {
public:
- I2CRelay(uint8_t slaveAddress);
+ I2CRelay(uint8_t address = 0x08, uint8_t sda = 8, uint8_t scl = 3);
+
void begin();
- void setRelay(uint8_t relayIndex, bool state);
- uint8_t scanDevices(uint8_t* foundAddresses, uint8_t maxDevices);
- void setAddress(uint8_t newAddress);
- void handleI2CCommand();
-
+ void setRelay(uint8_t relayNum, uint8_t state);
+ void setAllRelays(uint8_t state);
+ void setDeviceName(const String &name);
+ void setI2CAddress(uint8_t newAddr);
+ String getIdentification();
+
+ // Helper for command parsing (optional)
+ void handleSerialCommand(const String &cmd);
+
private:
- uint8_t _slaveAddress;
+ uint8_t _address;
+ uint8_t _sda;
+ uint8_t _scl;
};
#endif