Compare commits
No commits in common. "main" and "v0.1.0" have entirely different histories.
142
README.md
142
README.md
@ -1,99 +1,91 @@
|
||||
# Empowering Innovation
|
||||
|
||||
**Unlocking Unlimited Possibilities with Our Modular IoT Devices**
|
||||
|
||||
At **FABLAB Bahrain**, we're revolutionizing the world of IoT devices with our cutting-edge, ESP32C3-based modules. Not only do they come pre-programmed as I2C slave devices, ready to be controlled via a simple interface, but they're also designed to be fully programmable and reconfigurable by end-users themselves. Our comprehensive library provides an intuitive way for developers to customize and fine-tune their products, whether it's changing addresses or integrating BLE or WiFi connectivity. But that's not all - we've also made it possible for users to upload their own firmware, empowering them to transform our modules into anything they can imagine. From smart home automation to industrial control systems, our devices are the perfect building blocks for your next innovation project. With our modular design and open-source approach, you'll have complete freedom to shape your IoT vision
|
||||
|
||||
# I2C Communication Library
|
||||
|
||||
**I2CRelay** is an 8-relay module built around the ESP32-C3 Super Mini, featuring an I2C interface that allows it to be controlled by another MCU, enabling easy integration into complex control systems. Additionally, when paired with an ESP32-C3-based controller, it can leverage Wi-Fi and Bluetooth LE connectivity, expanding control options beyond wired I2C communication for wireless operation and enhanced flexibility in IoT and robotics applications. This combination of compact size, low power consumption, and versatile communication protocols makes the module indispensable for remote and automated control of multiple devices, offering scalable and reliable switching solutions for smart homes, industrial automation, and advanced robotics.
|
||||
|
||||
This library provides functionalities for I2C communication, specifically designed for controlling devices that support the I2C protocol. It includes utilities for scanning I2C buses, changing slave addresses, and handling commands.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Technical Drawings](#technical_drawings)
|
||||
2. [Features](#Features)
|
||||
3. [Hardware Components Used](#hardware_components_used)
|
||||
4. [Installation](#installation)
|
||||
5. [Versatility](#versatility)
|
||||
6. [Usage](#usage)
|
||||
7. [API Overview](#api-overview)
|
||||
8. [Example Serial Commands](#example-serial-commands)
|
||||
9. [Notes](#notes)
|
||||
10. [Credits](#credits)
|
||||
11. [License](#license)
|
||||
12. [Project Image](#project-image)
|
||||
1. [Overview](#overview)
|
||||
2. [Files](#files)
|
||||
3. [Class: I2CMasterUtils](#class-i2cmastertools)
|
||||
4. [Class: I2CRelay](#class-i2crelay)
|
||||
5. [Usage Example](#usage-example)
|
||||
|
||||
---
|
||||
## Overview
|
||||
|
||||
### Technical Drawings
|
||||
The library provides functionalities for I2C communication, specifically designed for controlling devices that support the I2C protocol. It includes utilities for scanning I2C buses, changing slave addresses, and handling commands.
|
||||
|
||||
---
|
||||
## Files
|
||||
|
||||
### Key Features of the 8-Relay Module with ESP32-C3 Super Mini
|
||||
The library is composed of several files, each with a specific role:
|
||||
|
||||
- **8-Channel Relay Control:** Enables switching of up to 8 high-power devices independently.
|
||||
- **ESP32-C3 Super Mini Integration:** Compact, powerful RISC-V processor with built-in Wi-Fi and Bluetooth 5.0 for versatile connectivity.
|
||||
- **I2C Communication Interface:** Allows easy control from another MCU with minimal wiring and reliable data transfer.
|
||||
- **Wireless Control Capability:** Supports Wi-Fi and Bluetooth when paired with an ESP32-C3-based controller, enabling remote and flexible device management.
|
||||
- **Low Power Consumption:** Efficient power usage suitable for battery-powered and IoT applications.
|
||||
- **Compact and Lightweight Design:** Ideal for space-constrained projects and embedded systems.
|
||||
- **High Reliability and Safety:** Optocoupler isolation and built-in protection circuits to safeguard both the relay module and connected devices.
|
||||
- **Wide Application Range:** Perfect for home automation, industrial control, robotics, and smart IoT solutions.
|
||||
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`.
|
||||
|
||||
### Hardware Components Used
|
||||
|
||||
- 8-Channel Relay Module.
|
||||
- Esp32C3 Super Mini.
|
||||
- Flywheel.
|
||||
|
||||
---
|
||||
|
||||
### Installation
|
||||
|
||||
1. Download or clone this repository.
|
||||
2. Copy the `I2CRelay`, and related library folders to your Arduino `libraries` directory.
|
||||
3. Restart the Arduino IDE.
|
||||
|
||||
#### Files
|
||||
|
||||
The library is composed of two files, each with a specific role:
|
||||
|
||||
1. **I2CRelay.h**
|
||||
3. **I2CRelay.h** [5]
|
||||
- Defines the `I2CRelay` class, which extends the functionality to control relays over I2C.
|
||||
|
||||
2. **I2CRelay.cpp**
|
||||
4. **I2CRelay.cpp** [4]
|
||||
- Contains the implementation of methods for the `I2CRelay` class.
|
||||
|
||||
---
|
||||
## Class: I2CMasterUtils
|
||||
|
||||
### Versatility
|
||||
### Static Methods
|
||||
|
||||
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.
|
||||
- **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.
|
||||
|
||||
### 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.
|
||||
|
||||
| Function | Description | Example Usage |
|
||||
|--------------------------------------------------------------------------|-----------------------------------------------------------------------------|-----------------------------------------------|
|
||||
| **I2CRelay(uint8_t sda, uint8_t scl)** | Constructor: set I2C SDA/SCL pins (optional, defaults to board pins) | `I2CRelay relay(8, 3);` |
|
||||
| **void begin()** | Initialize the I2C bus | `relay.begin();` |
|
||||
| **void scan(Stream &out = Serial)** | Scan and print all I2C devices on the bus | `relay.scan();` |
|
||||
| **void setSlaveAddress(uint8_t oldAddr, uint8_t newAddr, Stream &out = Serial)** | Change the slave’s I2C address | `relay.setSlaveAddress(0x08, 0x09);` |
|
||||
| **void setSlaveName(uint8_t addr, const char* name, Stream &out = Serial)** | Change the slave’s device name | `relay.setSlaveName(0x08, "MyRelay");` |
|
||||
| **String requestSlaveInfo(uint8_t addr)** | Get info string from the slave | `String info = relay.requestSlaveInfo(0x08);` |
|
||||
| **bool setRelay(uint8_t addr, uint8_t relayNum, uint8_t state)** | Set relay (1–8) ON/OFF (`state`: 1=ON, 0=OFF) | `relay.setRelay(0x08, 3, 1);` |
|
||||
| **bool setAllRelays(uint8_t addr, uint8_t state)** | Set all relays ON/OFF (`state`: 1=ON, 0=OFF) | `relay.setAllRelays(0x08, 0);` |
|
||||
| **bool setRelayTimer(uint8_t addr, uint8_t relayNum, uint32_t ms)** | Set relay (1–8) ON for given ms, then auto-OFF | `relay.setRelayTimer(0x08, 2, 2000);` |
|
||||
| **bool setAllRelaysTimer(uint8_t addr, uint32_t ms)** | Set all relays ON for given ms, then auto-OFF | `relay.setAllRelaysTimer(0x08, 3000);` |
|
||||
## Class: I2CRelay
|
||||
|
||||
### Parameter Notes
|
||||
### Methods
|
||||
|
||||
- **addr**: I2C address of the slave (e.g. `0x08`)
|
||||
- **relayNum**: Relay number **1–8**
|
||||
- **state**: `1` = ON, `0` = OFF
|
||||
- **ms**: Duration in milliseconds
|
||||
- **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
|
||||
}
|
@ -1,72 +1,65 @@
|
||||
#include <Wire.h>
|
||||
#include <I2CRelay.h>
|
||||
#include <I2CMasterUtils.h>
|
||||
|
||||
I2CRelay relay(8, 3); // Use your SDA/SCL pins
|
||||
I2CRelay relayMaster(0x08); // Default address, can be changed via setAddress()
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
relay.begin();
|
||||
|
||||
uint8_t addr = 0x08;
|
||||
|
||||
// 1. Scan the I2C bus
|
||||
Serial.println("=== I2C Scan ===");
|
||||
relay.scan();
|
||||
|
||||
// 2. Get info from the slave
|
||||
Serial.println("=== Request Info ===");
|
||||
String info = relay.requestSlaveInfo(addr);
|
||||
Serial.print("Info: ");
|
||||
Serial.println(info);
|
||||
|
||||
// 3. Change the slave name
|
||||
Serial.println("=== Change Name ===");
|
||||
relay.setSlaveName(addr, "FirstRelay");
|
||||
delay(100);
|
||||
Serial.println("Name changed.");
|
||||
|
||||
// 4. Turn relay 1 ON
|
||||
Serial.println("=== Turn Relay 1 ON ===");
|
||||
relay.setRelay(addr, 1, 1); // 1 = ON
|
||||
delay(1500);
|
||||
|
||||
// 5. Turn relay 1 OFF
|
||||
Serial.println("=== Turn Relay 1 OFF ===");
|
||||
relay.setRelay(addr, 1, 0); // 0 = OFF
|
||||
delay(500);
|
||||
|
||||
// 6. Turn all relays ON
|
||||
Serial.println("=== Turn All Relays ON ===");
|
||||
relay.setAllRelays(addr, 1); // 1 = ON
|
||||
delay(500);
|
||||
|
||||
// 7. Turn all relays OFF
|
||||
Serial.println("=== Turn All Relays OFF ===");
|
||||
relay.setAllRelays(addr, 0); // 0 = OFF
|
||||
delay(500);
|
||||
|
||||
// 8. Turn relay 2 ON for 2 seconds
|
||||
Serial.println("=== Relay 2 ON for 2 seconds ===");
|
||||
relay.setRelayTimer(addr, 2, 2000); // relay 2, 2000 ms
|
||||
delay(2500);
|
||||
|
||||
// 9. Turn all relays ON for 3 seconds
|
||||
Serial.println("=== All relays ON for 3 seconds ===");
|
||||
relay.setAllRelaysTimer(addr, 3000); // 3000 ms
|
||||
delay(3500);
|
||||
|
||||
// 10. Change slave address from 0x08 to 0x09
|
||||
relay.setSlaveAddress(0x08, 0x10);
|
||||
addr = 0x10; // Use new address for further commands
|
||||
delay(1500);
|
||||
|
||||
// 11. Confirm new address by requesting info
|
||||
info = relay.requestSlaveInfo(addr);
|
||||
Serial.print("Info: ");
|
||||
Serial.println(info);
|
||||
|
||||
Serial.println("=== Test Complete ===");
|
||||
delay(1000);
|
||||
relayMaster.begin();
|
||||
Serial.println("Enter:");
|
||||
Serial.println(" s - to scan I2C bus");
|
||||
Serial.println(" ch <old> <new> - to change slave address");
|
||||
Serial.println(" <relay> <on/off> - to set relay (e.g. 3 on)");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Nothing here
|
||||
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 <old_addr> <new_addr>");
|
||||
}
|
||||
} 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: <relay_number> <on/off>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,181 +1,65 @@
|
||||
#include <Wire.h>
|
||||
#include <EEPROM.h>
|
||||
|
||||
// EEPROM settings
|
||||
#define ADDR_EEPROM_LOC 0
|
||||
#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 ---
|
||||
// Relay pins: GPIO0 to GPIO7
|
||||
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]
|
||||
};
|
||||
uint8_t i2cAddress;
|
||||
|
||||
// *** 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);
|
||||
}
|
||||
}
|
||||
// Unified I2C receive event
|
||||
void receiveEvent(int howMany) {
|
||||
if (howMany >= 2) {
|
||||
uint8_t first = Wire.read();
|
||||
uint8_t second = Wire.read();
|
||||
|
||||
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);
|
||||
// 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();
|
||||
}
|
||||
|
||||
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);
|
||||
Serial.print("I2C address changed to: 0x");
|
||||
Serial.println(newAddr, HEX);
|
||||
delay(100);
|
||||
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;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle data requested by master
|
||||
void onRequest() {
|
||||
String response = String(i2cAddress, HEX) + ",GENERIC," + deviceName;
|
||||
Wire.write((const uint8_t*)response.c_str(), response.length());
|
||||
// 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);
|
||||
|
||||
i2cAddress = readAddressFromEEPROM();
|
||||
readNameFromEEPROM(deviceName);
|
||||
// Read I2C address from EEPROM or use default
|
||||
i2cAddress = EEPROM.read(ADDR_EEPROM_LOC);
|
||||
if (i2cAddress == 0xFF || i2cAddress == 0) i2cAddress = 0x08; // Default address
|
||||
|
||||
Wire.begin(i2cAddress);
|
||||
Wire.onReceive(onReceive);
|
||||
Wire.onRequest(onRequest);
|
||||
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); // Inverted logic: HIGH = OFF at start
|
||||
digitalWrite(relayPins[i], HIGH); // Relays OFF initially (active LOW)
|
||||
}
|
||||
|
||||
Serial.print("I2C Slave started at address 0x");
|
||||
Serial.println(i2cAddress, HEX);
|
||||
Serial.print("Device name: ");
|
||||
Serial.println(deviceName);
|
||||
// Start I2C slave
|
||||
Wire.begin(i2cAddress);
|
||||
Wire.onReceive(receiveEvent);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
// Nothing needed here for basic operation
|
||||
}
|
||||
|
22
keywords.txt
22
keywords.txt
@ -1,10 +1,16 @@
|
||||
I2CRelay KEYWORD1
|
||||
#######################################
|
||||
# Syntax coloring for Relay8I2C Library
|
||||
#######################################
|
||||
|
||||
# Classes
|
||||
RelayI2CMaster KEYWORD1
|
||||
I2CMasterUtils KEYWORD1
|
||||
|
||||
# Methods
|
||||
begin KEYWORD2
|
||||
scan KEYWORD2
|
||||
setSlaveAddress KEYWORD2
|
||||
setSlaveName KEYWORD2
|
||||
requestSlaveInfo KEYWORD2
|
||||
setRelay KEYWORD2
|
||||
setAllRelays KEYWORD2
|
||||
setRelayTimer KEYWORD2
|
||||
setAllRelaysTimer KEYWORD2
|
||||
scanI2C KEYWORD2
|
||||
changeSlaveAddress KEYWORD2
|
||||
|
||||
# Constants
|
||||
CMD_CHANGE_ADDR LITERAL1
|
||||
|
@ -1,9 +1,9 @@
|
||||
name=I2CRelay
|
||||
name=FABLAB Relay
|
||||
version=1.0.0
|
||||
author=Fatima Idrees <fatimaxidrees@gmail.com>
|
||||
maintainer=Fatima Idrees <fatimaxidrees@gmail.com>
|
||||
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.
|
||||
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://git.takeone.bh/fatimaxsen/I2CRelay.git
|
||||
url=https://github.com/fablabbh/I2CMasterUtils
|
||||
architectures=*
|
||||
|
6
src/I2CCommands.h
Normal file
6
src/I2CCommands.h
Normal file
@ -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
|
36
src/I2CMasterUtils.cpp
Normal file
36
src/I2CMasterUtils.cpp
Normal file
@ -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;
|
||||
}
|
14
src/I2CMasterUtils.h
Normal file
14
src/I2CMasterUtils.h
Normal file
@ -0,0 +1,14 @@
|
||||
#ifndef I2CMASTERUTILS_H
|
||||
#define I2CMASTERUTILS_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
|
||||
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
|
119
src/I2CRelay.cpp
119
src/I2CRelay.cpp
@ -1,110 +1,35 @@
|
||||
#include <Wire.h>
|
||||
#include <Arduino.h>
|
||||
#include "I2CRelay.h"
|
||||
#include "I2CMasterUtils.h"
|
||||
#include "I2CCommands.h"
|
||||
|
||||
// Command codes
|
||||
#define CMD_SET_ADDR 0x01
|
||||
#define CMD_SET_NAME 0x02
|
||||
#define CMD_RELAY_SET 0x10
|
||||
#define CMD_RELAY_ALL 0x11
|
||||
#define CMD_RELAY_TIMER 0x12
|
||||
#define CMD_RELAY_ALL_TIMER 0x13
|
||||
I2CRelay::I2CRelay(uint8_t slaveAddress) : _slaveAddress(slaveAddress) {}
|
||||
|
||||
I2CRelay::I2CRelay(uint8_t sda, uint8_t scl)
|
||||
: _sda(sda), _scl(scl) {}
|
||||
void I2CRelay::setAddress(uint8_t newAddress) {
|
||||
_slaveAddress = newAddress;
|
||||
}
|
||||
|
||||
void I2CRelay::begin() {
|
||||
Wire.begin(_sda, _scl);
|
||||
Wire.begin();
|
||||
}
|
||||
|
||||
void I2CRelay::scan(Stream &out) {
|
||||
out.println("Scanning I2C bus...");
|
||||
void I2CRelay::setRelay(uint8_t relayIndex, bool state) {
|
||||
Wire.beginTransmission(_slaveAddress);
|
||||
Wire.write(relayIndex);
|
||||
Wire.write(state ? 1 : 0);
|
||||
Wire.endTransmission();
|
||||
}
|
||||
|
||||
uint8_t I2CRelay::scanDevices(uint8_t* foundAddresses, uint8_t maxDevices) {
|
||||
uint8_t count = 0;
|
||||
for (uint8_t address = 1; address < 127; address++) {
|
||||
Wire.beginTransmission(address);
|
||||
uint8_t error = Wire.endTransmission();
|
||||
if (error == 0) {
|
||||
out.print("I2C device found at address 0x");
|
||||
if (address < 16) out.print("0");
|
||||
out.print(address, HEX);
|
||||
out.println(" !");
|
||||
count++;
|
||||
}
|
||||
delay(2);
|
||||
}
|
||||
if (count == 0) {
|
||||
out.println("No I2C devices found.");
|
||||
} else {
|
||||
out.print("Scan complete, devices found: ");
|
||||
out.println(count);
|
||||
}
|
||||
out.println();
|
||||
}
|
||||
|
||||
void I2CRelay::setSlaveAddress(uint8_t oldAddr, uint8_t newAddr, Stream &out) {
|
||||
Wire.beginTransmission(oldAddr);
|
||||
Wire.write(CMD_SET_ADDR);
|
||||
Wire.write(newAddr);
|
||||
Wire.endTransmission();
|
||||
out.print("Sent address change command: 0x");
|
||||
out.print(oldAddr, HEX);
|
||||
out.print(" -> 0x");
|
||||
out.println(newAddr, HEX);
|
||||
delay(1200); // Give slave time to restart
|
||||
}
|
||||
|
||||
void I2CRelay::setSlaveName(uint8_t addr, const char* newName, Stream &out) {
|
||||
for(uint8_t addr = 1; addr < 127; addr++) {
|
||||
Wire.beginTransmission(addr);
|
||||
Wire.write(CMD_SET_NAME);
|
||||
Wire.write((const uint8_t*)newName, strlen(newName));
|
||||
Wire.endTransmission();
|
||||
out.print("Sent name change command to 0x");
|
||||
out.print(addr, HEX);
|
||||
out.print(": ");
|
||||
out.println(newName);
|
||||
if(Wire.endTransmission() == 0) {
|
||||
if(count < maxDevices) {
|
||||
foundAddresses[count++] = addr;
|
||||
}
|
||||
|
||||
String I2CRelay::requestSlaveInfo(uint8_t addr) {
|
||||
Wire.requestFrom(addr, (uint8_t)40);
|
||||
String response = "";
|
||||
while (Wire.available()) {
|
||||
char c = Wire.read();
|
||||
response += c;
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
// Relay control
|
||||
bool I2CRelay::setRelay(uint8_t addr, uint8_t relay, bool on) {
|
||||
Wire.beginTransmission(addr);
|
||||
Wire.write(CMD_RELAY_SET);
|
||||
Wire.write(relay);
|
||||
Wire.write(on ? 1 : 0);
|
||||
return Wire.endTransmission() == 0;
|
||||
}
|
||||
|
||||
bool I2CRelay::setAllRelays(uint8_t addr, bool on) {
|
||||
Wire.beginTransmission(addr);
|
||||
Wire.write(CMD_RELAY_ALL);
|
||||
Wire.write(on ? 1 : 0);
|
||||
return Wire.endTransmission() == 0;
|
||||
}
|
||||
|
||||
bool I2CRelay::setRelayTimer(uint8_t addr, uint8_t relay, uint32_t ms) {
|
||||
Wire.beginTransmission(addr);
|
||||
Wire.write(CMD_RELAY_TIMER);
|
||||
Wire.write(relay);
|
||||
Wire.write((uint8_t)(ms & 0xFF));
|
||||
Wire.write((uint8_t)((ms >> 8) & 0xFF));
|
||||
Wire.write((uint8_t)((ms >> 16) & 0xFF));
|
||||
Wire.write((uint8_t)((ms >> 24) & 0xFF));
|
||||
return Wire.endTransmission() == 0;
|
||||
}
|
||||
|
||||
bool I2CRelay::setAllRelaysTimer(uint8_t addr, uint32_t ms) {
|
||||
Wire.beginTransmission(addr);
|
||||
Wire.write(CMD_RELAY_ALL_TIMER);
|
||||
Wire.write((uint8_t)(ms & 0xFF));
|
||||
Wire.write((uint8_t)((ms >> 8) & 0xFF));
|
||||
Wire.write((uint8_t)((ms >> 16) & 0xFF));
|
||||
Wire.write((uint8_t)((ms >> 24) & 0xFF));
|
||||
return Wire.endTransmission() == 0;
|
||||
return count;
|
||||
}
|
||||
|
@ -1,27 +1,20 @@
|
||||
#ifndef I2CRELAY_H
|
||||
#define I2CRELAY_H
|
||||
#ifndef I2CRelay_H
|
||||
#define I2CRelay_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
|
||||
class I2CRelay {
|
||||
public:
|
||||
I2CRelay(uint8_t sda = SDA, uint8_t scl = SCL);
|
||||
|
||||
I2CRelay(uint8_t slaveAddress);
|
||||
void begin();
|
||||
void scan(Stream &out = Serial);
|
||||
void setSlaveAddress(uint8_t oldAddr, uint8_t newAddr, Stream &out = Serial);
|
||||
void setSlaveName(uint8_t addr, const char* newName, Stream &out = Serial);
|
||||
String requestSlaveInfo(uint8_t addr);
|
||||
|
||||
// Relay control
|
||||
bool setRelay(uint8_t addr, uint8_t relay, bool on);
|
||||
bool setAllRelays(uint8_t addr, bool on);
|
||||
bool setRelayTimer(uint8_t addr, uint8_t relay, uint32_t ms);
|
||||
bool setAllRelaysTimer(uint8_t addr, uint32_t ms);
|
||||
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 _sda, _scl;
|
||||
uint8_t _slaveAddress;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user