I2CMotor/examples/Slave/Slave.ino
2025-05-18 13:38:58 +03:00

170 lines
4.1 KiB
C++

#include <Wire.h>
#include <EEPROM.h>
#define EEPROM_SIZE 64
#define EEPROM_ADDR_LOC 0
#define EEPROM_NAME_LOC 1
#define DEVICE_NAME_LEN 24
#define DEFAULT_I2C_ADDR 0x08
// Motor pins
#define EN1 0
#define IN1 1
#define IN2 2
#define IN3 4
#define IN4 5
#define EN2 6
char deviceName[DEVICE_NAME_LEN] = "Motor";
uint8_t i2cAddress = DEFAULT_I2C_ADDR;
// I2C command codes
enum Command : uint8_t {
CMD_SET_ADDR = 0x01,
CMD_SET_NAME = 0x02,
CMD_MOTOR_CTRL = 0x10,
CMD_BOTH_CTRL = 0x11,
};
// Store address to EEPROM
void storeAddressToEEPROM(uint8_t addr) {
EEPROM.write(EEPROM_ADDR_LOC, addr);
EEPROM.commit();
}
// Read address from EEPROM (with fallback to default)
uint8_t readAddressFromEEPROM() {
uint8_t addr = EEPROM.read(EEPROM_ADDR_LOC);
if (addr == 0xFF || addr == 0x00) { // Uninitialized or invalid
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';
}
// Helper: Set motor speed and direction
void setMotor(uint8_t enPin, uint8_t in1, uint8_t in2, uint8_t speed, uint8_t dir) {
analogWrite(enPin, speed);
if (dir == 0) {
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);
} else {
digitalWrite(in1, LOW);
digitalWrite(in2, HIGH);
}
}
// Stop motor
void stopMotor(uint8_t enPin, uint8_t in1, uint8_t in2) {
analogWrite(enPin, 0);
digitalWrite(in1, LOW);
digitalWrite(in2, LOW);
}
// I2C receive handler
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) { // Valid 7-bit address
storeAddressToEEPROM(newAddr);
ESP.restart(); // Or NVIC_SystemReset() on STM32, or just reset the MCU as needed
}
}
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;
}
case CMD_MOTOR_CTRL: {
if (Wire.available() >= 3) {
uint8_t motor_id = Wire.read(); // 1: MotorA, 2: MotorB
uint8_t speed_pct = Wire.read(); // 0-100
uint8_t dir = Wire.read(); // 0: FWD, 1: REV
uint8_t pwm = map(speed_pct, 0, 100, 0, 255);
if (motor_id == 1) { // MotorA
setMotor(EN1, IN1, IN2, pwm, dir);
} else if (motor_id == 2) { // MotorB
setMotor(EN2, IN3, IN4, pwm, dir);
}
}
break;
}
case CMD_BOTH_CTRL: {
if (Wire.available() >= 4) {
uint8_t speedA = Wire.read();
uint8_t dirA = Wire.read();
uint8_t speedB = Wire.read();
uint8_t dirB = Wire.read();
uint8_t pwmA = map(speedA, 0, 100, 0, 255);
uint8_t pwmB = map(speedB, 0, 100, 0, 255);
setMotor(EN1, IN1, IN2, pwmA, dirA);
setMotor(EN2, IN3, IN4, pwmB, dirB);
}
break;
}
default: break;
}
}
// I2C request handler
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);
// Motor pins setup
pinMode(EN1, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(EN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
Wire.begin(i2cAddress);
Wire.onReceive(onReceive);
Wire.onRequest(onRequest);
Serial.print("I2C Slave started at address 0x");
Serial.println(i2cAddress, HEX);
Serial.print("Device name: ");
Serial.println(deviceName);
}
void loop() {
// Nothing here
}