Communication between 2 UNO's

Hi,

for a project I have 2 arduino UNO's. The first on eis reading out an SD card the second one has to execute the commands (comming from the SD card)

This is the code for the "MASTER" (with sd card):

#include <SD.h>

//CONNECTIONS
File myFile; // instance of a file

// WORKING VALUES
char inputString [100];
char inputChar;

long randNumber;
String filename;

int stringIndex = 0; // String stringIndexing int;
int lineIndex = 1;


void setup(){
  // Open serial communications and wait for port to open:
  Serial.begin(57600);

  Serial.print("Initializing SD card...");
 
  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);

  // see if the card is present and can be initialized:
  if (!SD.begin(4)){
    Serial.println("Initialization failed: card failed, or not present");
    // don't do anything more:
    while (1) ;
  }
  
 Serial.println("Initialization done.");

  OpenFile();
}


void OpenFile() {

  filename ="TEST1.txt";
 
  // Open up the file we're going to log to!
  myFile = SD.open(filename);
  if (! myFile){
    if (DEBUGGING == 1){ 
    Serial.print("error opening File: ");
    Serial.println(filename);
    Serial.println("No such file in directory");
    }
    // Wait forever since we cant write data
    while (1) ;
  }  
}
 

void loop() {
 inputChar = myFile.read(); // Gets one byte from serial buffer
  
  if (inputChar != '\n')
      { // define breaking char here
        if (inputChar == 'Q')  //end of the file
          {
           Serial.println("---END OF FILE---");
            myFile.close();
          }
          else
          {
            inputString[stringIndex] = inputChar; // Store it
            stringIndex++; // Increment where to write next
          }
      }
      else
      {
        Serial.print("line ");
        Serial.print(lineIndex);
        Serial.print(": "); // shows that the program is cycling, for debugging only
        }
        //Serial.println(inputString);


   stringIndex = 0; // clear the value for the next cycle
        lineIndex++;
        memset(inputString, 0, sizeof(inputString));
        
     //Wait until the other UNO repsonse with "1"  (1=ok, 0=nok)
     while(Serial.available() == 0) { }  // There really is nothing between the {} braces
     char x = Serial.read();
      }
}

The code above is working, he send the first line of the file TEST1.txt to the serial monitor

#define DEBUGGING (0) // 0 = no serial output, 1 = serial output for debugging
#define VERBOSE (0) // add to get a lot more serial output.
#define VERSION ("3-rc2") // firmware version
#define BAUD (57600) // How fast is the Arduino talking?
#define MAX_BUF (64) // What is the longest message Arduino can store?
#define STEPS_PER_TURN (200) // depends on your stepper motor. most are 200.
#define MIN_STEP_DELAY (50)
#define MAX_FEEDRATE (500)
#define MIN_FEEDRATE (1)
#define StepsPerUnit (56)   // how many steps per in/mm 
#define DimensionX (4000)     // traverse path for X in mm/in (not steps!) //400
#define DimensionY (6000)     // traverse path for Y in mm/in (not steps!)  //600
#define SendPosAfterMove (0)
#define SendPosWhileMove (1)
#define hwCNC (0)            // accept commands from and send infos to hwCNC (PC-Software)
#define SendCommandBack (0)  // Send command back to console so you know the Arduino got the message
#define HardwareEndSwitches (1) // did we have hardware end limit switches?

#define LimitSwitchHomeX (2) // Pins of
#define LimitSwitchHomeY (4) // the
#define LimitSwitchEndX (7)  // limit
#define LimitSwitchEndY (8)  // switches

boolean has_origin = false;
float act_pos_x = 0.0;
float act_pos_y = 0.0;

#include <Wire.h>
#include <Adafruit_MotorShield.h>


Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_StepperMotor *mX = AFMS.getStepper(STEPS_PER_TURN, 1); // to motor port #1 (M1 and M2)
Adafruit_StepperMotor *mY = AFMS.getStepper(STEPS_PER_TURN, 2); // to motor port #2 (M3 and M4)

char buffer[MAX_BUF]; // where we store the message until we get a ';'
int sofar; // how much is in the buffer

float px, py; // location X/Y

// speeds
float fr=0; // human version
long step_delay; // machine version

// settings
char mode_abs=1; // absolute mode?

void position(float npx,float npy) {
  // here is a good place to add sanity tests
  px=npx;
  py=npy;
}

void release() {
  mX->release();
  mY->release();
}

void output(char *code,float val) {
  Serial.print(code);
  Serial.println(val);
}

void where() {
  output("X",px);
  output("Y",py);
  output("F",fr);
  if (DEBUGGING == 1){ Serial.println(mode_abs?"ABS":"REL");}
}

void help() {
  Serial.println(F("GcodeAFMotorV2"));
  Serial.print("Version ");
  Serial.println(VERSION);
  Serial.println(F("Commands:"));
  Serial.println(F("G00 [X(steps)] [Y(steps)] [F(feedrate)]; - linear move"));
  Serial.println(F("G01 [X(steps)] [Y(steps)] [F(feedrate)]; - linear move"));
  Serial.println(F("G04 P[seconds]; - delay"));
  Serial.println(F("G28; - move to Home-Position/Origin"));
  Serial.println(F("G90; - absolute mode"));
  Serial.println(F("G91; - relative mode"));
  Serial.println(F("G92 [X(steps)] [Y(steps)]; - change logical position"));
  Serial.println(F("M18; - release motors"));
  Serial.println(F("M100; - this help message"));
  Serial.println(F("M114; - report position and feedrate"));
  }

void ready() {
  sofar=0; // clear input buffer
  if (DEBUGGING == 1){ Serial.print(F(">")); }// signal ready to receive input

Serial.print(F("1")); //OK, send next command

}


void setup() {
  
  Serial.begin(BAUD); // open coms
  
  AFMS.begin(); // create with the default frequency 1.6KHz
  
  if (DEBUGGING == 1){help();} // say hello
  position(0,0); // set staring position
  feedrate(500); // set default speed
 
  if (HardwareEndSwitches) {
    // limit switches defined at the beginning 
    pinMode(LimitSwitchHomeX, INPUT);
    pinMode(LimitSwitchHomeY, INPUT);
    pinMode(LimitSwitchEndX, INPUT);
    pinMode(LimitSwitchEndY, INPUT);
 
  }

  
  ready();
}

void loop() {
  // listen for serial commands
  while(Serial.available() > 0) { // if something is available
    char c=Serial.read(); // get it
    if (DEBUGGING == 1){ Serial.print(c);} // repeat it back so I know you got the message
    if(sofar<MAX_BUF) buffer[sofar++]=c; // store it
    if(buffer[sofar-1]==';') break; // entire message received
    
  }

  if(sofar>0 && buffer[sofar-1]==';') {
    // we got a message and it ends with a semicolon
    buffer[sofar]=0; // end the buffer so string functions work right
    if (DEBUGGING == 1){Serial.print(F("\r\n")); }// echo a return character for humans
    processCommand(); // do something with the command
    ready();
  }

}

This code sends a "1" back to ask for the next command, but there is no next command comping.. the serial output shows only 11111111111111111111111...

How can I solve this problem? How can I send back a trigger to ask for the next command? Maybe not through serial, but a digital output (And if so, how do I connect + code?)??

Your debugging prints are sent on the same communication channel as your commands...

Do you also have the console open on the USB?

You should not mix those. Use software serial or I2C for the communication between your UNOs if you want to keep Serial.print for USB debugging

But when I load the code up to the arduino's, I turn off all the serial.prints except to send the commands or to return the "1" ...

It will be very much easier to develop your project if you keep the communication between the Arduinos separate from the communications with the PC. That way you can have debug messages that don't interfere with your data.

You have not told us much about your project. Is it really necessary to use two Arduinos?

And, generally speaking, if I have a project that needs two Arduinos I would choose Arduinos with extra HardwareSerial ports - such as the Mega, Leonardo or Micro.

...R
Serial Input Basics

I thought the communication through I2C is just one way communication, no?

Is it a solution to just send the "1" (ok signal) through I2C and the rest (commands) through the serial monitor?

A second thought is to set a digital port (e.g. Pin 2) high if the command is executed and the other uno can send a new command? Then I have to read out this pin on the 'master'uno

But how do I connect? Do I need a resistor or can I just connect the two pins '2' with each other?

Code:
first uno: digital.read(1)
Second uni: digital.write(2, high)

Just use SoftwareSerial to create an extra serial port on each Uno.

...R

Do you mean one serial for the commands and another one for the "1" (feedback)?

How do I do that?

No get all over the software serial connection

Question is why do you need two arduino for this?

One UNO is used to process the Gcodes, the other to send the Gcodes comming from an SD card

Can you explain how to set up the softserial ??

It's documented here

It's very likely that your second arduino expects data on hardware Serial (pin 0/1) and has a built in protocol to request the next gcode command - for quality of communication and speed I would use also a hardware Serial on the UNO reading

For testing the concept though use the example code on both sides and make sur to connect the virtual Tx to the virtual RX on the other arduino and Rx to Tx

Can this work if I add the following code:

(hardware: add Two wires between D10 & D11 AND D11 & D10

MASTER UNO

#include <SoftwareSerial.h>
// software serial: RX = digital pin 10, TX = digital pin 11
SoftwareSerial portOne(10, 11);

in the setup:

portOne.begin(9600);

in the loop:

while (portOne.available() == 0) { }
char inByte = portOne.read();

SLAVE UNO

#include <SoftwareSerial.h>
// software serial: RX = digital pin 11, TX = digital pin 10
SoftwareSerial portOne(11, 10);

in the setup:

portOne.begin(9600);

in the loop:

while (portOne.available() > 0) {
Serial.write("1");}

BramWerbrouck:
Can this work if I add the following code:

Have you studied the examples in the link I gave you in Reply #3? Simple reliable ways to receive data.

...R

Can this work if I add the following code:

(hardware: add Two wires between D10 & D11 AND D11 & D10

If you do not also connect the grounds, not a snowball's chance.

So it connected as following:
UNO 1 UNO 2
D10 D10
D11 D11
GND GND

and the code:
UNO 1

#include <SD.h>
#include <Wire.h>

#include <SoftwareSerial.h>
// software serial #1: RX = digital pin 10, TX = digital pin 11
SoftwareSerial portOne(10, 11);


#define DEBUGGING (0) // 0 = no serial output, 1 = serial output for debugging


//CONNECTIONS
File myFile; // instance of a file

// WORKING VALUES
char inputString [100];
char inputChar;

long randNumber;
String filename;

int stringIndex = 0; // String stringIndexing int;
int lineIndex = 1;


void setup(){
  // Open serial communications and wait for port to open:
  Serial.begin(57600);

   portOne.begin(9600);

  if (DEBUGGING == 1){Serial.print("Initializing SD card...");}

  pinMode(10, OUTPUT);
  digitalWrite(10, HIGH);

  // see if the card is present and can be initialized:
  if (!SD.begin(4)){
    if (DEBUGGING == 1){Serial.println("Initialization failed: card failed, or not present");}
    // don't do anything more:
    while (1) ;
  }
  
  if (DEBUGGING == 1){ Serial.println("Initialization done.");}

  //OPEN A RANDOM FILE
  OpenFile();

  portOne.listen();
}


void OpenFile() {

  filename = "TEST1.txt";
 
  // Open up the file we're going to log to!
  myFile = SD.open(filename);
  if (! myFile){
    if (DEBUGGING == 1){ 
    Serial.print("error opening File: ");
    Serial.println(filename);
    Serial.println("No such file in directory");
    }
    // Wait forever since we cant write data
    while (1) ;
  }

  if (DEBUGGING == 1){ 
    Serial.print("....Reading ");
    Serial.print(filename);
    Serial.println(":");
    delay(100);
  }
}
 
void loop() {
  
 inputChar = myFile.read(); // Gets one byte from serial buffer
  
  if (inputChar != '\n')
      { // define breaking char here
        if (inputChar == 'Q')  //end of the file
          {
           if (DEBUGGING == 1){  Serial.println("---END OF FILE---"); }
            myFile.close();
          }
          else
          {
            inputString[stringIndex] = inputChar; // Store it
            stringIndex++; // Increment where to write next
          }
      }
      else
      {
       
         if (DEBUGGING == 1){
        Serial.print("line ");
        Serial.print(lineIndex);
        Serial.print(": "); // shows that the program is cycling, for debugging only
        }
 

   stringIndex = 0; // clear the value for the next cycle
        lineIndex++;
        memset(inputString, 0, sizeof(inputString));
        
     //Wait until the other UNO repsonse with "1" //or "0" (1=ok, 0=nok)

      while (portOne.available() > 0) { }
    char inByte = portOne.read();
    if (DEBUGGING == 1){ Serial.write(inByte);}

      }
}

UNO 2:

#define DEBUGGING (1) // 0 = no serial output, 1 = serial output for debugging
#define VERBOSE (0) // add to get a lot more serial output.
#define VERSION ("3-rc2") // firmware version
#define BAUD (57600) // How fast is the Arduino talking?
#define MAX_BUF (64) // What is the longest message Arduino can store?
#define STEPS_PER_TURN (200) // depends on your stepper motor. most are 200.
#define MIN_STEP_DELAY (50)
#define MAX_FEEDRATE (500)
#define MIN_FEEDRATE (1)
#define StepsPerUnit (56)   // how many steps per in/mm 
#define DimensionX (4000)     // traverse path for X in mm/in (not steps!) //400
#define DimensionY (6000)     // traverse path for Y in mm/in (not steps!)  //600
#define SendPosAfterMove (0)
#define SendPosWhileMove (1)
#define hwCNC (0)            // accept commands from and send infos to hwCNC (PC-Software)
#define SendCommandBack (0)  // Send command back to console so you know the Arduino got the message
#define HardwareEndSwitches (1) // did we have hardware end limit switches?
#define LimitSwitchHomeX (2) // Pins of
#define LimitSwitchHomeY (4) // the
#define LimitSwitchEndX (7)  // limit
#define LimitSwitchEndY (8)  // switches

boolean has_origin = false;
float act_pos_x = 0.0;
float act_pos_y = 0.0;

#include <Wire.h>
#include <Adafruit_MotorShield.h>
//#include "utility/Adafruit_PWMServoDriver.h"
#include <SoftwareSerial.h>
// software serial #1: RX = digital pin 11, TX = digital pin 10

SoftwareSerial portOne(11, 10);

Adafruit_MotorShield AFMS = Adafruit_MotorShield();
Adafruit_StepperMotor *mX = AFMS.getStepper(STEPS_PER_TURN, 1); // to motor port #1 (M1 and M2)
Adafruit_StepperMotor *mY = AFMS.getStepper(STEPS_PER_TURN, 2); // to motor port #2 (M3 and M4)

char buffer[MAX_BUF]; // where we store the message until we get a ';'
int sofar; // how much is in the buffer

float px, py; // location X/Y

// speeds
float fr=0; // human version
long step_delay; // machine version

// settings
char mode_abs=1; // absolute mode?


/**
* Set the logical position
* @input npx new position x
* @input npy new position y
*/
void position(float npx,float npy) {
  // here is a good place to add sanity tests
  px=npx;
  py=npy;
}

void release() {
  mX->release();
  mY->release();
}

/**
* write a string followed by a float to the serial line. Convenient for debugging.
* @input code the string.
* @input val the float.
*/
void output(char *code,float val) {
  Serial.print(code);
  Serial.println(val);
}

/**
* print the current position, feedrate, and absolute mode.
*/
void where() {
  output("X",px);
  output("Y",py);
  output("F",fr);
  if (DEBUGGING == 1){ Serial.println(mode_abs?"ABS":"REL");}
}


/**
* display helpful information
*/
void help() {
  Serial.println(F("GcodeAFMotorV2"));
  Serial.print("Version ");
  Serial.println(VERSION);
  Serial.println(F("Commands:"));
  Serial.println(F("G00 [X(steps)] [Y(steps)] [F(feedrate)]; - linear move"));
  Serial.println(F("G01 [X(steps)] [Y(steps)] [F(feedrate)]; - linear move"));
  Serial.println(F("G04 P[seconds]; - delay"));
  Serial.println(F("G28; - move to Home-Position/Origin"));
  Serial.println(F("G90; - absolute mode"));
  Serial.println(F("G91; - relative mode"));
  Serial.println(F("G92 [X(steps)] [Y(steps)]; - change logical position"));
  Serial.println(F("M18; - release motors"));
  Serial.println(F("M100; - this help message"));
  Serial.println(F("M114; - report position and feedrate"));
  }


/**
* prepares the input buffer to receive a new message and tells the serial connected device it is ready for more.
*/
void ready() {
  sofar=0; // clear input buffer
  if (DEBUGGING == 1){ Serial.print(F(">")); }// signal ready to receive input

//Serial.print(F("1")); //OK, send next command

while (portOne.available() > 0) {
    Serial.write("1");
  }

}


/**
* First thing this machine does on startup. Runs only once.
*/
void setup() {
  
  Serial.begin(BAUD); // open coms
  
  AFMS.begin(); // create with the default frequency 1.6KHz
  
  if (DEBUGGING == 1){help();} // say hello
  position(0,0); // set staring position
  feedrate(500); // set default speed
 
  if (HardwareEndSwitches) {
    // limit switches defined at the beginning 
    pinMode(LimitSwitchHomeX, INPUT);
    pinMode(LimitSwitchHomeY, INPUT);
    pinMode(LimitSwitchEndX, INPUT);
    pinMode(LimitSwitchEndY, INPUT); 
  }

portOne.begin(9600);
  
  ready();
}

void loop() {
  // listen for serial commands
  while(Serial.available() > 0) { // if something is available
    char c=Serial.read(); // get it
    if (DEBUGGING == 1){ Serial.print(c);} // repeat it back so I know you got the message
    if(sofar<MAX_BUF) buffer[sofar++]=c; // store it
    if(buffer[sofar-1]==';') break; // entire message received
    
  }

  if(sofar>0 && buffer[sofar-1]==';') {
    // we got a message and it ends with a semicolon
    buffer[sofar]=0; // end the buffer so string functions work right
    if (DEBUGGING == 1){Serial.print(F("\r\n")); }// echo a return character for humans
    processCommand(); // do something with the command
    ready();
  }
  delay(100);

}


void receiveEvent(int howMany) {
  while (1 < Wire.available()) { // loop through all but the last
    char c = Wire.read(); // receive byte as a character
    Serial.print(c);         // print the character
    if(sofar<MAX_BUF) buffer[sofar++]=c;
  }
  int x = Wire.read();    // receive byte as an integer
  Serial.println(x);         // print the integer

processCommand(); // do something with the command
    ready();
}

But no luck... nothing happens, can anyone help me with this please??

BramWerbrouck:
But no luck... nothing happens, can anyone help me with this please??

Start with two VERY simple programs. The first Arduino sends "hello world" to the second Arduino. And the second Arduino sends the received message to the Serial Monitor. Neither program has any code that does anything else.

If you can't get them to work post the two programs here and I will try to help.

When that works reliably you will have working code that can be added to other programs.

...R

What's the deal on UNO2 and Serial.write("1"); in a while() loop checking if something is available from portOne (that you never read) in the ready() function?

Agree with robin start with something super easy and get it to work.

This works:

UNO 1

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(57600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

  Serial.println("UNO 1");

  // set the data rate for the SoftwareSerial port
  mySerial.begin(9600);

}

void loop() { 
    mySerial.write("t");
    delay(2000);
  
}

UNO 2

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

void setup() {
  // Open serial communications and wait for port to open:
  Serial.begin(57600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  Serial.println("UNO 2");

  // set the data rate for the SoftwareSerial port
  mySerial.begin(9600);

}

void loop() { // run over and over
 if (mySerial.available()) {
    Serial.write(mySerial.read());
  }
  if (Serial.available()) {
    mySerial.write(Serial.read());
  }
  
}

But when I want to implement this in my code, it doesn't work anymore, it's just like the second uno writes the whole time something back...

J-M-L:
What's the deal on UNO2 and Serial.write("1");

The ID is the following:

UNO 1 reads a line out of the text file from my SD card and sends it through the serial monitor to UNO 2
UNO 2 executes the command and if he's done, send back "1" so UNO 1 can send the next line... and so on...

UNO 1 reads a line out of the text file from my SD card and sends it through the serial monitor to UNO 2

It does NOT send the data through the serial monitor. It sends it using the SoftwareSerial pins.

PaulS:
It does NOT send the data through the serial monitor. It sends it using the SoftwareSerial pins.

So I should use two soft serial ports??
e.g. pin 10 & 11 and pin 12 & 13 ???