commit 939a70aba586d14b4e71518ea4c8f5430a0f1068 Author: fatimaxsen Date: Sat Apr 26 12:55:20 2025 +0300 adding the files ig 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..6a07faa --- /dev/null +++ b/examples/Master/Master.ino @@ -0,0 +1,20 @@ +#include "I2CStepper.h" + +I2CStepper stepper(0x55); + +void setup() { + stepper.begin(); + // Optionally scan for devices + uint8_t found[10]; + uint8_t n = stepper.scanDevices(found, 10); + Serial.print("Found "); Serial.print(n); Serial.println(" I2C devices."); +} + +void loop() { + stepper.setSpeed(50, 1); // Move at 50% speed, forward + delay(2000); + stepper.setSpeed(50, 0); // Move at 50% speed, backward + delay(2000); + stepper.stop(); + delay(1000); +} diff --git a/examples/Slave/Slave.ino b/examples/Slave/Slave.ino new file mode 100644 index 0000000..9089f29 --- /dev/null +++ b/examples/Slave/Slave.ino @@ -0,0 +1,90 @@ +#include +#include + +// Stepper pin definitions (adjust as needed) +#define DIR_PIN 5 +#define STEP_PIN 2 +#define EN_PIN 8 + +#define ADDR_EEPROM_LOC 0 +#define EEPROM_SIZE 64 + +#define CMD_CHANGE_ADDR 0xAA + +volatile uint8_t i2c_addr = 0x55; // Default I2C address + +// Reads the I2C address from EEPROM, returns default if not set +uint8_t getI2CAddress(uint8_t defaultAddr = 0x55) { + uint8_t addr = EEPROM.read(ADDR_EEPROM_LOC); + if (addr == 0xFF || addr == 0) addr = defaultAddr; + return addr; +} + +// Writes new I2C address to EEPROM and restarts the MCU +void setI2CAddress(uint8_t newAddr) { + EEPROM.write(ADDR_EEPROM_LOC, newAddr); + EEPROM.commit(); + Serial.print("Address changed to: 0x"); + Serial.println(newAddr, HEX); + delay(100); + ESP.restart(); // Use NVIC_SystemReset() for STM32, or asm volatile (" jmp 0"); for AVR +} + +// Stepper control function: move a fixed number of steps +void moveSteps(uint16_t steps, uint8_t direction, uint16_t speedDelay) { + digitalWrite(DIR_PIN, direction); + for (uint16_t i = 0; i < steps; i++) { + digitalWrite(STEP_PIN, HIGH); + delayMicroseconds(speedDelay); + digitalWrite(STEP_PIN, LOW); + delayMicroseconds(speedDelay); + } +} + +// I2C receive event handler +void receiveEvent(int howMany) { + if (howMany < 1) return; + uint8_t cmd = Wire.read(); + + if (cmd == CMD_CHANGE_ADDR) { + if (Wire.available() >= 1) { + uint8_t newAddr = Wire.read(); + setI2CAddress(newAddr); + while (Wire.available()) Wire.read(); // Clear buffer + } + return; + } + + // If not a command, treat as speed/direction + uint8_t speed = cmd; + if (Wire.available() < 1) return; + uint8_t direction = Wire.read(); + + // Map speed (0-100) to step delay (us): 100 = fast, 1 = slow + uint16_t speedDelay = map(speed, 1, 100, 2000, 200); // Tune as needed + + // Move a fixed number of steps for demonstration + moveSteps(200, direction, speedDelay); // 200 steps = 1 rev for 1.8° stepper +} + +void setup() { + pinMode(DIR_PIN, OUTPUT); + pinMode(STEP_PIN, OUTPUT); + pinMode(EN_PIN, OUTPUT); + digitalWrite(EN_PIN, LOW); // Enable driver + + Serial.begin(115200); + + EEPROM.begin(EEPROM_SIZE); + i2c_addr = getI2CAddress(0x55); + + Serial.print("Using I2C address: 0x"); + Serial.println(i2c_addr, HEX); + + Wire.begin(i2c_addr); + Wire.onReceive(receiveEvent); +} + +void loop() { + // Nothing needed here +} 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..7a92e88 --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=FABLAB Stepper +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..9bc0488 --- /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(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/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/I2CStepper.cpp b/src/I2CStepper.cpp new file mode 100644 index 0000000..e4b7cea --- /dev/null +++ b/src/I2CStepper.cpp @@ -0,0 +1,100 @@ +#include "I2CStepper.h" +#include "I2CCommands.h" + +#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266) +#define IS_ARDUINO +#endif + +// Define your stepper pins here for the slave +#define EN_PIN 8 +#define DIR_PIN 5 +#define STEP_PIN 2 + +static uint8_t currentAddress = 0x55; // Default address + +I2CStepper::I2CStepper(uint8_t address) : _address(address) {} + +void I2CStepper::begin() { + Wire.begin(_address); +#ifdef IS_ARDUINO + pinMode(EN_PIN, OUTPUT); + pinMode(DIR_PIN, OUTPUT); + pinMode(STEP_PIN, OUTPUT); + digitalWrite(EN_PIN, LOW); // Enable driver + // Register receive handler for slave + Wire.onReceive([this](int numBytes){ this->handleI2CCommand(); }); +#endif +} + +void I2CStepper::setSpeed(uint8_t speed, uint8_t direction) { + // Send speed/direction to slave + if (_address == 0) return; + if (speed > 100) speed = 100; + direction = (direction == 1) ? 1 : 0; + Wire.beginTransmission(_address); + Wire.write(speed); // 0-100 (percent) + Wire.write(direction); // 0 or 1 + Wire.endTransmission(); +} + +void I2CStepper::stop() { + setSpeed(0, 0); +} + +uint8_t I2CStepper::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 (foundAddresses && count < maxDevices) { + foundAddresses[count] = addr; + } + count++; + } + } + return count; +} + +void I2CStepper::setAddress(uint8_t newAddress) { + _address = newAddress; +} + +// ========== SLAVE: I2C Command Handler ========== + +void I2CStepper::handleI2CCommand() { + if (Wire.available() < 1) return; + uint8_t cmd = Wire.read(); + + if (cmd == CMD_CHANGE_ADDR) { + // Change I2C address + if (Wire.available() >= 1) { + uint8_t newAddr = Wire.read(); + currentAddress = newAddr; + Wire.end(); // End current I2C + delay(10); + Wire.begin(currentAddress); // Restart with new address + } + return; + } + + // If not a command, treat as speed/direction + uint8_t speed = cmd; + if (Wire.available() < 1) return; + uint8_t direction = Wire.read(); + + // Map speed (0-100) to step delay (us): 100 = fast, 1 = slow + uint16_t speedDelay = map(speed, 1, 100, 2000, 200); // Tune these values + + // Move a fixed number of steps for demonstration (or make this customizable) + moveSteps(200, direction, speedDelay); // 200 steps = 1 rev for 1.8° stepper +} + +void I2CStepper::moveSteps(uint16_t steps, uint8_t direction, uint16_t speedDelay) { + digitalWrite(DIR_PIN, direction); + for (uint16_t i = 0; i < steps; i++) { + digitalWrite(STEP_PIN, HIGH); + delayMicroseconds(speedDelay); + digitalWrite(STEP_PIN, LOW); + delayMicroseconds(speedDelay); + } +} diff --git a/src/I2CStepper.h b/src/I2CStepper.h new file mode 100644 index 0000000..d294f7e --- /dev/null +++ b/src/I2CStepper.h @@ -0,0 +1,24 @@ +#ifndef I2CStepper_h +#define I2CStepper_h + +#include +#include + +// ========== I2CStepper Class ========== + +class I2CStepper { +public: + I2CStepper(uint8_t address = 0x55); + void begin(); + void setSpeed(uint8_t speed, uint8_t direction); + void stop(); + uint8_t scanDevices(uint8_t* foundAddresses, uint8_t maxDevices); + void setAddress(uint8_t newAddress); + void handleI2CCommand(); // Only used on the slave + +private: + uint8_t _address; + void moveSteps(uint16_t steps, uint8_t direction, uint16_t speedDelay); +}; + +#endif