commit 1f9edb14b48a512966f95e62bf9b84f3e363cc12 Author: fatimaxsen Date: Thu Apr 24 09:34:04 2025 +0300 first upload diff --git a/I2CMasterUtils.cpp b/I2CMasterUtils.cpp new file mode 100644 index 0000000..9bc0488 --- /dev/null +++ b/I2CMasterUtils.cpp @@ -0,0 +1,36 @@ +#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(0xAA); // Command code for address change + 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/I2CMasterUtils.h b/I2CMasterUtils.h new file mode 100644 index 0000000..5be05b9 --- /dev/null +++ b/I2CMasterUtils.h @@ -0,0 +1,14 @@ +#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/LICENSE b/LICENSE new file mode 100644 index 0000000..7008d53 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 FABLAB Bahrain + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..345ab6c --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Relay8I2C Library +**By Fatima @ FABLAB Bahrain** + +![FABLAB Bahrain Logo](https://via.placeholder.com/150x50.png?text=FABLAB+Bahrain) + +Control 8-relay modules via I2C with dynamic address configuration. Designed for ESP8266 and Arduino platforms. + +## Features +- I2C communication protocol +- Dynamic slave address configuration +- Master controller with serial interface +- Active LOW relay support +- EEPROM storage for persistent addressing + +## Installation +1. Download the library ZIP +2. Arduino IDE: Sketch > Include Library > Add .ZIP Library +3. Select the downloaded ZIP file + +## Usage diff --git a/examples/Master/Master.ino b/examples/Master/Master.ino new file mode 100644 index 0000000..bf35a73 --- /dev/null +++ b/examples/Master/Master.ino @@ -0,0 +1,65 @@ +#include +#include +#include + +RelayI2CMaster relayMaster(0x08); // Default address, can be changed via setAddress() + +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)"); +} + +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: "); + } + } + } +} diff --git a/examples/Slave/Slave.ino b/examples/Slave/Slave.ino new file mode 100644 index 0000000..f2536ca --- /dev/null +++ b/examples/Slave/Slave.ino @@ -0,0 +1,65 @@ +#include +#include + +// EEPROM settings +#define ADDR_EEPROM_LOC 0 +#define EEPROM_SIZE 64 + +// Relay pins: GPIO0 to GPIO7 +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 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++) { + pinMode(relayPins[i], OUTPUT); + digitalWrite(relayPins[i], HIGH); // Relays OFF initially (active LOW) + } + + // Start I2C slave + Wire.begin(i2cAddress); + Wire.onReceive(receiveEvent); +} + +void loop() { + // Nothing needed here for basic operation +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..0caac64 --- /dev/null +++ b/keywords.txt @@ -0,0 +1,16 @@ +####################################### +# Syntax coloring for Relay8I2C Library +####################################### + +# Classes +RelayI2CMaster KEYWORD1 +I2CMasterUtils KEYWORD1 + +# Methods +begin KEYWORD2 +setRelay KEYWORD2 +scanI2C KEYWORD2 +changeSlaveAddress KEYWORD2 + +# Constants +CMD_CHANGE_ADDR LITERAL1 diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..ffa5015 --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=I2CMasterUtils +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. +category=Device Control +url=https://github.com/fablabbh/I2CMasterUtils +architectures=* diff --git a/src/I2CCommands.h b/src/I2CCommands.h new file mode 100644 index 0000000..2ae5463 --- /dev/null +++ b/src/I2CCommands.h @@ -0,0 +1,6 @@ +#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 new file mode 100644 index 0000000..b8b04c4 --- /dev/null +++ b/src/I2CMasterUtils.cpp @@ -0,0 +1,36 @@ +#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 new file mode 100644 index 0000000..5be05b9 --- /dev/null +++ b/src/I2CMasterUtils.h @@ -0,0 +1,14 @@ +#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 new file mode 100644 index 0000000..9b62e0d --- /dev/null +++ b/src/I2CRelay.cpp @@ -0,0 +1,35 @@ +#include +#include +#include "I2CRelay.h" +#include "I2CMasterUtils.h" +#include "I2CCommands.h" + +RelayI2CMaster::RelayI2CMaster(uint8_t slaveAddress) : _slaveAddress(slaveAddress) {} + +void RelayI2CMaster::setAddress(uint8_t newAddress) { + _slaveAddress = newAddress; +} + +void RelayI2CMaster::begin() { + Wire.begin(); +} + +void RelayI2CMaster::setRelay(uint8_t relayIndex, bool state) { + Wire.beginTransmission(_slaveAddress); + Wire.write(relayIndex); + Wire.write(state ? 1 : 0); + Wire.endTransmission(); +} + +uint8_t RelayI2CMaster::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; + } + } + } + return count; +} diff --git a/src/I2CRelay.h b/src/I2CRelay.h new file mode 100644 index 0000000..f3190d0 --- /dev/null +++ b/src/I2CRelay.h @@ -0,0 +1,20 @@ +#ifndef RELAY_I2C_MASTER_H +#define RELAY_I2C_MASTER_H + +#include +#include + +class RelayI2CMaster { +public: + RelayI2CMaster(uint8_t slaveAddress); + 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(); + +private: + uint8_t _slaveAddress; +}; + +#endif