serial and I2C communication between two Arduinos

Hello community

Goal:

I tried to let stepper motors move with an Arduino Mega. BecauseI also use a tft touch display and several sensors, the run() function (AccelStepper library) can't fire often enough. Especially reading touch-input from the tft display seems to slow it all down. So I decided to go with a seperate Arduino Uno that is only for letting the stepper motors run. From the Arduino Mega there are two digitals that tells the Arduino Uno with HIGH/LOW which direction is needed. If both are LOW, no movement is needed. On the other hand Arduino Uno should tell the Arduino Mega which position the motors are over serial. Format to send and receive is a long.

So far:

Robin2's plain flagship example from here works. I could send and receive several longs correctly.

Now when I add the AccelStepper library to the code, the output data is getting weird.

Code on the Arduino Uno (controlling stepper motors):

/* Example sketch to control a 28BYJ-48 stepper motor with ULN2003 driver board, AccelStepper and Arduino UNO: continuous rotation. More info: https://www.makerguides.com */

// Include the AccelStepper library:
#include <AccelStepper.h>

// Motor pin definitions:
#define motorsRightPin1  4      // IN1 on the ULN2003 driver
#define motorsRightPin2  5      // IN1 on the ULN2003 driver
#define motorsRightPin3  6      // IN1 on the ULN2003 driver
#define motorsRightPin4  7      // IN1 on the ULN2003 driver

#define motorsLeftPin1  8      // IN1 on the ULN2003 driver
#define motorsLeftPin2  9      // IN1 on the ULN2003 driver
#define motorsLeftPin3  10      // IN1 on the ULN2003 driver
#define motorsLeftPin4  11      // IN1 on the ULN2003 driver

byte relais = 3;
byte megaInputUp = 12;
byte megaInputDown = 13;
// megaOutput is pin 0 (Serial)

unsigned long millisPrev = 0;
unsigned long millisRelais = 0;
byte motorsError = 0;

int motorsSpeedUp = 1700;
int motorsSpeedDown = 2000;
int motorsAccUp = 500;
int motorsAccDown = 500;
long fullDistance = 74532; // 4096 = eine volle Umdrehung / ~75532 = Fensterhöhe
long motorsDistance = 0;

// Define the AccelStepper interface type; 4 wire motor in half step mode:
#define MotorInterfaceType 8

// Initialize with pin sequence IN1-IN3-IN2-IN4 for using the AccelStepper library with 28BYJ-48 stepper motor:
AccelStepper motorsLeft = AccelStepper(MotorInterfaceType, motorsLeftPin1, motorsLeftPin3, motorsLeftPin2, motorsLeftPin4);
AccelStepper motorsRight = AccelStepper(MotorInterfaceType, motorsRightPin1, motorsRightPin3, motorsRightPin2, motorsRightPin4);

void setup() {
  Serial.begin(9600);
  motorsLeft.setCurrentPosition(0);
  motorsRight.setCurrentPosition(0);
  
  // RELAIS
  pinMode(relais, OUTPUT);
  pinMode(megaInputUp, INPUT);
  pinMode(megaInputDown, INPUT);

  
}

void loop() {


    // FALLS MILLIS WIEDER VON VORNE BEGINNEN
    if (millis() < millisPrev) {
        millisRelais = 0;
    }
    millisPrev = millis();
        

    if (millis() > millisRelais + 120000) {
        if (digitalRead(relais) == HIGH) {
            digitalWrite(relais, LOW); 
            motorsError = 1; // IRGENDWAS STIMMT DANN NICHT.
            millisRelais = millis();
        }
    }


    // RUN MOTORS
    if (motorsError == 0) {
        if (digitalRead(megaInputDown)==HIGH && digitalRead(megaInputUp)==LOW && motorsLeft.currentPosition() != fullDistance) { // Soll runterfahren
            digitalWrite(relais, HIGH);
            motorsLeft.setSpeed(motorsSpeedDown);
            motorsRight.setSpeed(motorsSpeedDown); 
            motorsLeft.setAcceleration(motorsAccDown);
            motorsRight.setAcceleration(motorsAccDown); 
            motorsLeft.moveTo(fullDistance);
            motorsRight.moveTo(-fullDistance);
            motorsLeft.run();
            motorsRight.run();
            millisRelais = millis();
        } else if (digitalRead(megaInputUp)==HIGH && digitalRead(megaInputDown)==LOW && motorsLeft.currentPosition() != 0) { // Soll rauffahren
            digitalWrite(relais, HIGH);            
            motorsLeft.setSpeed(motorsSpeedUp);
            motorsRight.setSpeed(motorsSpeedUp); 
            motorsLeft.setAcceleration(motorsAccUp);
            motorsRight.setAcceleration(motorsAccUp); 
            motorsLeft.moveTo(0);
            motorsRight.moveTo(0);
            motorsLeft.run();
            motorsRight.run();
            millisRelais = millis();
        } else {
            digitalWrite(relais, LOW);
        }
            motorsDistance = motorsLeft.currentPosition();
            Serial.println(motorsDistance);
    }

}

Code on the Arduino Mega (just receiving serial data for now) - congruent to Robin2's example:

// Example 4 - Receive a number as text and convert it to an int

const byte numChars = 32;
char receivedChars[numChars];   // an array to store the received data

boolean newData = false;

long dataNumber = 0;             // new for this version

void setup() {
    Serial1.begin(9600);
    Serial.begin(9600);
    Serial.println("<Arduino is ready>");
}

void loop() {
    recvWithEndMarker();
    showNewNumber();
}

void recvWithEndMarker() {
    static byte ndx = 0;
    char endMarker = '\n';
    char rc;
   
    if (Serial1.available() > 0) {
        rc = Serial1.read();

        if (rc != endMarker) {
            receivedChars[ndx] = rc;
            ndx++;
            if (ndx >= numChars) {
                ndx = numChars - 1;
            }
        }
        else {
            receivedChars[ndx] = '\0'; // terminate the string
            ndx = 0;
            newData = true;
        }
    }
}

void showNewNumber() {
    if (newData == true) {
        dataNumber = 0;             // new for this version
        dataNumber = atol(receivedChars);   // new for this version
        Serial.print("This just in ... ");
        Serial.println(receivedChars);
        Serial.print("Data as Number ... ");    // new for this version
        Serial.println(dataNumber);     // new for this version
        newData = false;
    }
}

The input data in this example should be 0 since motorsLeft.currentPosition() should be 0 as well. But it receives data like '42' '4' 42000' '2' '5' '7'... To me it looks like AccelStepper also sends some data over the serial?!

I hope, someone can help me with this. What am I overlooking?

Best regards, t

I don't see how the Mega is receiving anything from the Uno. The Uno is only sending the motorsDistance variable without start or end markers.

groundFungus:
I don't see how the Mega is receiving anything from the Uno. The Uno is only sending the motorsDistance variable without start or end markers.

Serial.println(motorsDistance);
The new line should be the end marker. The original example worked just like that.

You are right, but println() (no function writeln()). My apologies. I don't know why i thought that you were using start and end markers.

May I suggest that you set up a software serial port to send data to the Mega. Then you can use the hardware serial (USB) and serial monitor to follow the execution of the program and monitor variable values.

groundFungus:
May I suggest that you set up a software serial port to send data to the Mega. Then you can use the hardware serial (USB) and serial monitor to follow the execution of the program and monitor variable values.

I could try.

What I can say is that if I monitor the Uno over USB (with TX, 1 still connected to the Mega), I get the expected values.

Recently it has occurred to me that it is actually easier to send data between Arduinos using I2C. Have a look at this I2C Tutorial

...R

Robin2:
Recently it has occurred to me that it is actually easier to send data between Arduinos using I2C. Have a look at this I2C Tutorial

...R

Thank you! I just tried the I2C solution and it works with your examples. Unfortunately I run into a problem while using an I2C multiplexer on the Mega:

//===================
// Using I2C to send and receive structs between two Arduinos
//   SDA is the data connection and SCL is the clock connection
//   On an Uno  SDA is A4 and SCL is A5
//   On an Mega SDA is 20 and SCL is 21
//   GNDs must also be connected
//===================


        // data to be received

struct I2cRxStruct {
    char textB[16];         // 16 bytes
    int valC;               //  2
    unsigned long valD;     //  4
    byte padding[10];       // 10
                            //------
                            // 32
};

I2cRxStruct rxData;

bool newRxData = false;


        // I2C control stuff
#include <Wire.h>

#define TCAADDR 0x70 // MULTIPLEXER


void tcaselect(uint8_t i) { // FUNCTION FOR MULTIPLEXER
    if (i > 7) return;
    Wire.beginTransmission(TCAADDR);
    Wire.write(1 << i);
    Wire.endTransmission();
}

const byte thisAddress = 9; // these need to be swapped for the other Arduino
const byte otherAddress = 8;



//=================================

void setup() {
    Serial.begin(115200);
    Serial.println("\nStarting I2C Slave demo\n");

    // set up I2C
    tcaselect(4);  // MULTIPLEXER VECTOR HERE?
    Wire.begin(thisAddress); // join i2c bus
    Wire.onReceive(receiveEvent); // register event
}

//============

void loop() {

        // this bit checks if a message has been received
    if (newRxData == true) {
        showNewData();
        newRxData = false;
    }
}


//=============

void showNewData() {

    Serial.print("This just in    ");
    Serial.print(rxData.textB);
    Serial.print(' ');
    Serial.print(rxData.valC);
    Serial.print(' ');
    Serial.println(rxData.valD);
}

//============

        // this function is called by the Wire library when a message is received
void receiveEvent(int numBytesReceived) {

    if (newRxData == false) {
            // copy the data to rxData
        tcaselect(4);  // MULTIPLEXER VECTOR HERE?
        Wire.readBytes( (byte*) &rxData, numBytesReceived);
        newRxData = true;
    }
    else {
            // dump the data
        tcaselect(4);  // MULTIPLEXER VECTOR HERE?
        while(Wire.available() > 0) {
            tcaselect(4);  // MULTIPLEXER VECTOR HERE?
            byte c = Wire.read();
        }
    }
}

I don't receive anything from the Uno (has exactly the same code as in the example and monitoring output works as expected). I just used the multiplexer code stuff like I did for the sensors; see comments in code. But I guess that it may cannot be used the same way? I'd be really grateful for some help here.

I'd remove all those tcaselect(4) calls and eventually remove the multiplexer for a first test. For a bidirectional conversation the controllers should use the primary I2C bus, not any multiplexed channel. A secondary channel should be selected only if no other data are going in or out on the I2C bus and only for the duration of a communication with a slave.

And don't forget to give each Arduino its own unique I2C address!

Thanks for the answer!

DrDiettrich:
I'd remove all those tcaselect(4) calls and eventually remove the multiplexer for a first test.

I did that and it worked.

DrDiettrich:
For a bidirectional conversation the controllers should use the primary I2C bus, not any multiplexed channel.

... Okay... Since I need the multiplexer, neither serial nor I2C works properly for me, I have to ask: Is there any other solution for sending a long from one arduino to another...?

What's the purpose of the multiplexer? You can connect multiple devices to an I2C bus.

tesla4:
Thank you! I just tried the I2C solution and it works with your examples. Unfortunately I run into a problem while using an I2C multiplexer on the Mega:

What do you mean by "I2C multiplexer"?

I can't figure out from your program what you are trying to do. Please describe the requirement in English (rather than code).

...R

Thank you for the answers!

DrDiettrich:
What's the purpose of the multiplexer? You can connect multiple devices to an I2C bus.

Sure. I haven't thought of that and it will probably solve it. :roll_eyes: :slightly_smiling_face:

Robin2:
I can't figure out from your program what you are trying to do. Please describe the requirement in English (rather than code).

Basically I want to send a long (the position of the steppers) from the Arduino Uno to the Arduino Mega. Preferably as often as possible or at least when the motors stop/pause. If DrDiettrich's solution doesn't work (see above) I'd gladly give you more details / describe the whole project.

tesla4:
Basically I want to send a long (the position of the steppers) from the Arduino Uno to the Arduino Mega.

You can easily do that with slight changes to the examples from my Tutorial - I can't understand why you are having a problem?

You don't need a multiplexer (whatever that is) to send a value.

...R

Robin2:
You can easily do that with slight changes to the examples from my Tutorial - I can't understand why you are having a problem?

I'm going to try again, it should work. I'll give feedback as soon as I had the time to test it.

Robin2:
What do you mean by "I2C multiplexer"?

Robin2:
You don't need a multiplexer (whatever that is) to send a value.

I use a 8-channel I2C switch PCA9548A on the Mega. I need data from 4 identical light sensors TSL2591 over I2C. The light sensors can only have two different unique addresses, so with the multiplexer I'm able to read data from all 4 sensors using the "multiplexer addresses". The Uno doesn't need to send data via the multiplexer - of course.

tesla4:
I use a 8-channel I2C switch PCA9548A on the Mega. I need data from 4 identical light sensors TSL2591 over I2C. The light sensors can only have two different unique addresses, so with the multiplexer I'm able to read data from all 4 sensors using the "multiplexer addresses". The Uno doesn't need to send data via the multiplexer - of course.

I do wish you had provided that information in your Original Post.

I have read quickly through the datasheet and I'm guessing that if you send a message to the address of the device itself you can select which of the output channels the I2C signal gets passed to. I presume if the message does not have the address of the device it is passed on.

I'm also guessing that if your other Arduino uses a different I2C address from any of the addresses associated with the multiplexer it should work in harmony with them. However for communication between the Uno and the Mega it may be simplest if the Mega acts as Master and requests data from the Uno.

...R

Thank you for the considerations.

Robin2:
I do wish you had provided that information in your Original Post.

Right. Haven't thought of that because originally I wanted a communication via serial.

Robin2:
However for communication between the Uno and the Mega it may be simplest if the Mega acts as Master and requests data from the Uno.

I'm going to look at the example and try this variant.