Hi there,
I have a project in progress, where I want an Arduino (at the moment Mega2650, in the final project it will be a nano) to send commands to multiple ATtiny84. Each ATtiny has to control a 28BYJ-48 with ULN2003. Also a hall sensor is attached to the ATtiny for finding zero possition of the steppers. The steppers should turn only in one direction and can stop at 12 evenly distributed positions. For fine-tuning there can be set an offset of zero position.
Possible commands: find zero pos, goto pos x, increment offset, return address, return position, ...
ChatGPT was really helpful to get as far as I've been come.
But now I need help from real intelligent beings.
- I've tested basic control of steppers with the ATtiny
- I've tested basic communication from Arduino to ATtiny via I2C
- I've got a combination of both working... somehow, but not fully
I use a switch case statement for the received commands.
I always read 3 bytes and have a write buffer of 5 bytes which I read/set at the beginning of receiveEvent.
The first commands in the switch statement work perfectly fine, but at 0x20 they start not to work. 0x21 works somehow, but I think the position to go to is not correct. The rest from there one seams to do nothing.
All requests result in 0.
Firstly I had the commands starting with 0x4? assigned to 0xA? and higher and I thought that this could be the problem. But no.
When I had the ledIndicators turned on (at the moment they are commented out), my debugging led blinked for the above written working commands. It looks like there is nothing coming on at the ATtiny using the broken commands.
Has someone an idea where I can dig into a bit more to find the error?
Thanks a lot!
slave.ino
// - - - I N C L U D E S - - -
#include <Wire.h>
#include <EEPROM.h>
#include <avr/wdt.h>
// - - - V A R I A B L E S - - -
// eeprom defines
#define EEPROM_I2C_ADDRESS 1
#define EEPROM_OFFSET 2
#define EEPROM_DIRECTION 3
// pin defines
#define LED_PIN 10
#define SENSOR_PIN 9
#define SDA_PIN 6
#define SCL_PIN 4
#define RESET_PIN 7
// i2c defines
#define POS_BASE 0x10
#define POS_MAX (POS_BASE + POS_COUNT - 1)
#define DEFAULT_ADDRESS 0x10
// movement defines
#define STEPS_PER_REV 2048
#define STEP_DELAY 2
#define POS_COUNT 12
// motor constant arrays
const int motorPins[4] = {0, 1, 2, 3};
const uint8_t halfStepSequence[8][4] = {
{1, 0, 0, 0},
{1, 1, 0, 0},
{0, 1, 0, 0},
{0, 1, 1, 0},
{0, 0, 1, 0},
{0, 0, 1, 1},
{0, 0, 0, 1},
{1, 0, 0, 1}
};
// movement constants and variables
volatile short stepperPos = 0;
volatile short posToStep = 0;
volatile unsigned long lastStepTime = 0;
volatile float stepsPerPos = 170.6666;
volatile short stepsOffset = 0;
volatile bool forward = false;
volatile bool gotoZero = false;
// other variables
volatile uint8_t readBuffer[3] = {0, 0, 0};
volatile uint8_t writeBuffer[5] = {0, 0, 0, 0, 0};
volatile uint16_t resetTimer = 0;
volatile uint8_t address = 0;
volatile bool saveConfig = false;
// - - - S T A T U S F U N C T I O N S - - -
// led status indicator
void ledIndicator(short count, short timeOff, short timeOn) {
delay(timeOff);
for (uint8_t i = 0; i < count; i++) {
delay(timeOff);
digitalWrite(LED_PIN, HIGH);
delay(timeOn);
digitalWrite(LED_PIN, LOW);
}
delay(timeOff * 2);
}
// - - - M O T O R M O V E M E N T - - -
// make one step forward or backward regarding 'forward' boolean
void stepMotor() {
// set pins
for (uint8_t pin = 0; pin < 4; pin++) {
digitalWrite(motorPins[pin], halfStepSequence[stepperPos % 8][pin]);
}
// make one step forwards or backwards depending on direction preventing overflow with modulo
if (forward) {
stepperPos = (stepperPos + 1) % STEPS_PER_REV;
} else {
stepperPos = (stepperPos - 1 + STEPS_PER_REV) % STEPS_PER_REV;
}
}
// stop motor by setting every pin to low
void stopMotor() {
for (uint8_t pin = 0; pin < 4; pin++) {
digitalWrite(motorPins[pin], LOW);
}
}
// - - - C O M M U N I C A T I O N - - -
// receive commands
void receiveEvent(int howMany) {
readBuffer[0] = Wire.read();
readBuffer[1] = Wire.read();
readBuffer[2] = Wire.read();
writeBuffer[0] = 0;
writeBuffer[1] = 0;
writeBuffer[2] = 0;
writeBuffer[3] = 0;
writeBuffer[4] = 0;
ledIndicator(1, 300, 300);
switch (readBuffer[0]) {
// movement commands
// stop motor
case 0x00:
//ledIndicator(1, 300, 300);
posToStep = stepperPos;
break;
// find zero position
case 0x01:
//ledIndicator(1, 300, 300);
posToStep = -1;
break;
// set stepper pos to go to by calculating steps for searched position
case POS_BASE ... POS_MAX:
//ledIndicator(1, 300, 300);
short endPos = readBuffer[0] - POS_BASE;
posToStep = floor(endPos * stepsPerPos) + stepsOffset;
break;
// reserved for request current position
case 0x20:
//ledIndicator(1, 300, 300);
writeBuffer[0] = stepperPos;
writeBuffer[1] = 0;
writeBuffer[2] = 0;
writeBuffer[3] = 0;
writeBuffer[4] = 0;
break;
// set stepper pos to go to
case 0x21:
//ledIndicator(1, 300, 300);
posToStep = ((readBuffer[1] << 8) | readBuffer[2]);
break;
// movement configuration
// reverse stepper direction
case 0x40:
//ledIndicator(1, 300, 300);
forward = !forward;
break;
// offset zero position
case 0x41:
//ledIndicator(1, 300, 300);
stepsOffset += 8;
posToStep += stepsOffset;
break;
// reset offset
case 0x42:
//ledIndicator(1, 300, 300);
stepsOffset = 0;
break;
// communication commands
// request current variables
case 0x50:
//ledIndicator(1, 300, 300);
writeBuffer[0] = address;
writeBuffer[1] = highByte(stepperPos);
writeBuffer[2] = lowByte(stepperPos);
writeBuffer[3] = stepsOffset;
writeBuffer[4] = forward;
break;
// set address
case 0x51:
//ledIndicator(1, 300, 300);
if (readBuffer[1] >= 0x08 && readBuffer[1] <= 0x77) {
address = readBuffer[1];
}
break;
// save configuration
case 0x59:
//ledIndicator(1, 300, 300);
saveConfig = true;
break;
}
}
void requestEvent() {
Wire.write(writeBuffer[0]);
Wire.write(writeBuffer[1]);
Wire.write(writeBuffer[2]);
Wire.write(writeBuffer[3]);
Wire.write(writeBuffer[4]);
}
// - - - B A S E F U N C T I O N S - - -
// startup sequence
void setup() {
// Disable watchdog on startup if triggered by a watchdog reset
MCUSR &= ~(1 << WDRF);
wdt_disable();
pinMode(LED_PIN, OUTPUT);
ledIndicator(1, 250, 3000);
// initialize pins
for (uint8_t i = 0; i < 4; i++) {
pinMode(motorPins[i], OUTPUT);
}
pinMode(SENSOR_PIN, INPUT);
pinMode(RESET_PIN, INPUT_PULLUP);
ledIndicator(1, 250, 250);
// initalize variables
stepsPerPos = STEPS_PER_REV / POS_COUNT;
stepsOffset = EEPROM.read(EEPROM_OFFSET);
forward = EEPROM.read(EEPROM_DIRECTION) > 0;
posToStep = 0;
ledIndicator(1, 250, 250);
// initialize communication
uint8_t i2cAddress = EEPROM.read(EEPROM_I2C_ADDRESS);
if (i2cAddress < 0x08 || i2cAddress > 0x77) {
i2cAddress = DEFAULT_ADDRESS;
EEPROM.update(EEPROM_I2C_ADDRESS, i2cAddress);
}
Wire.begin(i2cAddress);
Wire.onReceive(receiveEvent);
Wire.onRequest(requestEvent);
ledIndicator(5, 250, 250);
}
// main loop
void loop() {
//TinyWireS_stop_check();
// check reset
if (!digitalRead(RESET_PIN)) {
digitalWrite(LED_PIN, HIGH);
if (resetTimer == 0) {
resetTimer = millis();
}
if (millis() - resetTimer >= 5000) {
wdt_enable(WDTO_15MS);
while (true) {}
}
} else {
resetTimer = 0;
digitalWrite(LED_PIN, LOW);
}
// save configuration
if (saveConfig) {
EEPROM.update(EEPROM_I2C_ADDRESS, address);
EEPROM.update(EEPROM_OFFSET, stepsOffset);
EEPROM.update(EEPROM_DIRECTION, forward ? 1 : 0);
saveConfig = false;
}
// check hall sensor
if (!digitalRead(SENSOR_PIN)) {
digitalWrite(LED_PIN, HIGH);
if (posToStep < 0) {
stepperPos = 0;
posToStep = 0;
}
} else {
digitalWrite(LED_PIN, LOW);
}
// step forward if neccessary
if (millis() - lastStepTime >= STEP_DELAY) {
lastStepTime = millis();
// Nur bei Bewegung: einen Schritt ausführen
if (stepperPos != posToStep) {
stepMotor();
} else {
stopMotor();
}
}
}
master.ino
#include <Wire.h>
uint8_t slaveAddress = 0x10; // Aktive I2C-Adresse
void setup() {
Serial.begin(9600);
Serial.write("\n\n\n\n\n\n");
Serial.write("enter command:\n");
Wire.begin();
delay(5000);
}
void loop2() {
Wire.beginTransmission(slaveAddress);
Wire.write(0x10);
Wire.endTransmission(false);
//delay(500);
Wire.beginTransmission(slaveAddress);
Wire.write(0x11);
Wire.endTransmission(false);
//delay(10);
Wire.requestFrom(slaveAddress, 2);
byte high = Wire.read();
byte low = Wire.read();
Serial.println((high << 8) | low);
delay(500);
}
void loop() {
if (Serial.available()) {
String cmd = Serial.readStringUntil('\n');
cmd.trim(); // Whitespace entfernen
Serial.print(cmd);
if (cmd == "s") {
Wire.beginTransmission(slaveAddress);
Wire.write(0x00);
Wire.write(0xFF);
Wire.write(0xFF);
Wire.endTransmission(false);
delay(100);
} else if (cmd == "z") {
Wire.beginTransmission(slaveAddress);
Wire.write(0x01);
Wire.write(0xFF);
Wire.write(0xFF);
Wire.endTransmission(false);
delay(100);
} else if (cmd.startsWith("p ")) {
int pos = cmd.substring(2).toInt();
if (pos >= 0 && pos < 12) {
Serial.print(": ");
Serial.print(pos);
Wire.beginTransmission(slaveAddress);
Wire.write(0x10 + pos);
Wire.write(0xFF);
Wire.write(0xFF);
Wire.endTransmission(false);
delay(100);
}
} else if (cmd == "p") {
Wire.beginTransmission(slaveAddress);
Wire.write(0x20);
Wire.write(0xFF);
Wire.write(0xFF);
Wire.endTransmission(false);
delay(100);
Wire.requestFrom(slaveAddress, 5);
byte high = Wire.read();
byte low = Wire.read();
Serial.print(": ");
Serial.println((high << 8) | low);
Serial.println(Wire.read());
Serial.println(Wire.read());
Serial.println(Wire.read());
delay(100);
} else if (cmd.startsWith("q ")) {
int pos = cmd.substring(2).toInt();
if (pos >= 0 && pos < 2048) {
Serial.print(": ");
Serial.print(pos);
Serial.print(" (");
Serial.print(highByte(pos));
Serial.print(", ");
Serial.print(lowByte(pos));
Serial.print(")");
Wire.beginTransmission(slaveAddress);
Wire.write(0x21);
Wire.write(highByte(pos));
Wire.write(lowByte(pos));
Wire.endTransmission(false);
delay(100);
}
} else if (cmd == "r") {
Wire.beginTransmission(slaveAddress);
Wire.write(0x40);
Wire.write(0xFF);
Wire.write(0xFF);
Wire.endTransmission(false);
delay(100);
} else if (cmd == "+") {
Wire.beginTransmission(slaveAddress);
Wire.write(0x41);
Wire.write(0xFF);
Wire.write(0xFF);
Wire.endTransmission(false);
delay(100);
} else if (cmd == "x") {
Wire.beginTransmission(slaveAddress);
Wire.write(0x42);
Wire.write(0xFF);
Wire.write(0xFF);
Wire.endTransmission(false);
delay(100);
} else if (cmd == "?") {
Wire.beginTransmission(slaveAddress);
Wire.write(0x50);
Wire.write(0xFF);
Wire.write(0xFF);
Wire.endTransmission(false);
delay(100);
Wire.requestFrom(slaveAddress, 5);
Serial.print(": ");
Serial.println(Wire.read());
Serial.println(Wire.read());
Serial.println(Wire.read());
Serial.println(Wire.read());
Serial.println(Wire.read());
delay(100);
} else if (cmd.startsWith("a ")) {
int adr = (int) strtol(cmd.substring(2).c_str(), nullptr, 0);
if (adr >= 0x08 && adr <= 0x77) {
Serial.print(": ");
Serial.print(adr);
Wire.beginTransmission(slaveAddress);
Wire.write(0x51);
Wire.write(adr);
Wire.write(0xFF);
Wire.endTransmission(false);
delay(100);
}
} else if (cmd == "w") {
Wire.beginTransmission(slaveAddress);
Wire.write(0x59);
Wire.write(0xFF);
Wire.write(0xFF);
Wire.endTransmission(false);
delay(100);
} else if (cmd.startsWith("0x")) {
byte hex = strtol(cmd.c_str(), nullptr, 0);
Wire.beginTransmission(slaveAddress);
Wire.write(hex);
Wire.write(0xFF);
Wire.write(0xFF);
Wire.endTransmission(false);
delay(100);
Wire.requestFrom(slaveAddress, 5);
Serial.println(Wire.read());
Serial.println(Wire.read());
Serial.println(Wire.read());
Serial.println(Wire.read());
Serial.println(Wire.read());
delay(100);
}
Serial.println();
}
}