What is the maximum serial communication distance using a Mega - Uno on CAT6a cable

I am a bit confused, I was under the impression if I used the standard tx0, rx0 pins on the mega - the rx, tx pins on the Uno - with no additional hardware or different software protocols the maximum transmission distance should be somewhere around 15m.

Related forum post

Last night I conducted a test and managed 160m with no lost communications? How is this possible?

I used shielded CAT6 cable, a Baud rate of 9600

Project explanation: I am creating a Submersible ROV. There will be a PC running a Human interface program in Processing(.org). This will transmit and receive control information to a Arduino Mega over the standard USB serial connection. This Arduino Mega then retransmits these controls and receives feedback from the submersible ROV. The ROV will have 2 different Arduino Uno. This transmission distance will be 30-100m.

Further explanation

Sketch for the Mega


void setup() {
  Serial.begin(9600);  // enables the serial connection. Make sure it's the same on both sides (here in the Arduino IDE and in the Processing program)
  Serial1.begin(9600);  // enables the serial connection. Make sure it's the same on both sides (here in the Arduino Mega IDE sketch and in the Arduino Uno1 IDE sketch)
  Serial2.begin(9600);  // enables the serial connection. Make sure it's the same on both sides (here in the Arduino Mega IDE sketch and in the Arduino Uno2 IDE sketch)
}

/*
tx & rx is with respect to the Arduino Mega. Arduino Mega receives lowercase char on serial port 0 from the PC processing program.
It transmits these lower case char on serial port 1 or 2 to the Arduino Unos in the ROV. The Arduino Unos in the ROV then transmits a Upper 
case char in responce to the recieved command. The Arduino Mega recieves this on serial port 1 or 2 and retransmits it on serial port 0 to the PC processing program.
*/

//tx_ functions to send the char recieved from the PC processing program to the ROV Arduino Uno1 or Uno2 program
//rx_ functions to send the char recieved from the ROV Arduino Uno1 or Uno2 program to the PC processing program

void tx_driveUp() {
  //Serial.write('A');
  Serial1.write('a');
}
void rx_driveUp() {
  Serial.write('A');
  //Serial1.write('a');
}
void tx_releaseDriveUp() {
  //Serial.write('Z');
  Serial1.write('z');
}
void rx_releaseDriveUp() {
  Serial.write('Z');
  //Serial1.write('z');
}
void tx_driveDown() {
  //Serial.write('B');
  Serial1.write('b');
}
void rx_driveDown() {
  Serial.write('B');
  //Serial1.write('b');
}
void tx_releaseDriveDown() {
  //Serial.write('Y');
  Serial1.write('y');
}
void rx_releaseDriveDown() {
  Serial.write('Y');
  //Serial1.write('y');
}
void tx_driveForward() {
  //Serial.write('C');
  Serial1.write('c');
}
void rx_driveForward() {
  Serial.write('C');
  //Serial1.write('c');
}
void tx_releaseDriveForward() {
  //Serial.write('X');
  Serial1.write('x');
}
void rx_releaseDriveForward() {
  Serial.write('X');
  //Serial1.write('x');
}
void tx_driveBackward() {
  //Serial.write('D');
  Serial1.write('d');
}
void rx_driveBackward() {
  Serial.write('D');
  //Serial1.write('d');
}
void tx_releaseDriveBackward() {
  //Serial.write('W');
  Serial1.write('w');
}
void rx_releaseDriveBackward() {
  Serial.write('W');
  //Serial1.write('w');
}
void tx_driveLeft() {
  //Serial.write('E');
  Serial1.write('e');
}
void rx_driveLeft() {
  Serial.write('E');
  //Serial1.write('e');
}
void tx_releaseDriveLeft() {
  //Serial.write('V');
  Serial1.write('v');
}
void rx_releaseDriveLeft() {
  Serial.write('V');
  //Serial1.write('v');
}
void tx_driveRight() {
  //Serial.write('F');
  Serial1.write('f');
}
void rx_driveRight() {
  Serial.write('F');
  //Serial1.write('f');
}
void tx_releaseDriveRight() {
  //Serial.write('U');
  Serial1.write('u');
}
void rx_releaseDriveRight() {
  Serial.write('U');
  //Serial1.write('u');
}
void tx_clampClose() {
  //Serial.write('G');
  Serial2.write('g');
}
void rx_clampClose() {
  Serial.write('G');
  //Serial1.write('g');
}
void tx_releaseClampClose() {
  //Serial.write('T');
  Serial2.write('t');
}
void rx_releaseClampClose() {
  Serial.write('T');
  //Serial1.write('t');
}
void tx_clampOpen() {
  //Serial.write('H');
  Serial2.write('h');
}
void rx_clampOpen() {
  Serial.write('H');
  //Serial1.write('h');
}
void tx_releaseClampOpen() {
  //Serial.write('S');
  Serial2.write('s');
}
void rx_releaseClampOpen() {
  Serial.write('S');
  //Serial1.write('s');
}
void tx_lightsOn() {
  //Serial.write('I');
  Serial2.write('i');
}
void rx_lightsOn() {
  Serial.write('I');
  //Serial1.write('i');
}
void tx_releaseLightsOn() {
  //Serial.write('R');
  Serial2.write('r');
}
void rx_releaseLightsOn() {
  Serial.write('R');
  //Serial1.write('r');
}
void tx_lightsOff() {
  //Serial.write('J');
  Serial2.write('j');
}
void rx_lightsOff() {
  Serial.write('J');
  //Serial1.write('j');
}
void tx_releaseLightsOff() {
  //Serial.write('Q');
  Serial2.write('q');
}
void rx_releaseLightsOff() {
  Serial.write('Q');
  //Serial1.write('q');
}
//countinuously watches the recieved serial char for appropriate cases and runs the appropriate function
void loop() {
  char incomingChar = 0;  // for incoming serial data

  if (Serial.available() > 0) {
    incomingChar = Serial.read();
    switch (incomingChar) {
        //-----------------------------ROV Submersible Drive Up/Down
      case 'a':
        tx_driveUp();
        break;
      case 'z':  //ROV Submersible Stop Driving Up/Down
        tx_releaseDriveUp();
        break;
      case 'b':
        tx_driveDown();
        break;
      case 'y':  //ROV Submersible Stop Driving Up/Down
        tx_releaseDriveDown();
        break;
        //-----------------------------ROV Submersible Drive Forward/Backward
      case 'c':
        tx_driveForward();
        break;
      case 'x':  //ROV Submersible Stop Driving Forward
        tx_releaseDriveForward();
        break;
      case 'd':
        tx_driveBackward();
        break;
      case 'w':  //ROV Submersible Stop Driving Backward
        tx_releaseDriveBackward();
        break;
        //-----------------------------ROV Submersible Drive Left/Right
      case 'e':
        tx_driveLeft();
        break;
      case 'v':  //ROV Submersible Stop Driving Left
        tx_releaseDriveLeft();
        break;
      case 'f':
        tx_driveRight();
        break;
      case 'u':  //ROV Submersible Stop Driving Right
        tx_releaseDriveRight();
        break;
        //-----------------------------ROV Submersible Clamp Open/Close
      case 'g':
        tx_clampClose();
        break;
      case 't':  //ROV Submersible Clamp Stop OClosing
        tx_releaseClampClose();
        break;
      case 'h':
        tx_clampOpen();
        break;
      case 's':  //ROV Submersible Clamp Stop Opening
        tx_releaseClampOpen();
        break;
        //-----------------------------ROV Submersible Lights On/Off
      case 'i':
        tx_lightsOn();
        break;
      case 'r':  //ROV Submersible Lights Stop On
        tx_releaseLightsOn();
        break;
      case 'j':
        tx_lightsOff();
        break;
      case 'q':  //ROV Submersible Lights Stop Off
        tx_releaseLightsOff();
        break;

      default:
        Serial.write('N');  // BLACK
        break;
    }
    /* not really needed but in case you want to do things in the loop forwardon receiving new chars. if (incomingChar == 0) then no new char was received, 
  otherwise later in the loop you still have the char received*/
  } else incomingChar = 0;  

  if (Serial1.available() > 0) {
    incomingChar = Serial1.read();
    switch (incomingChar) {
        //-----------------------------ROV Submersible Drive Up/Down
      case 'A':
        rx_driveUp();
        break;
      case 'Z':  //ROV Submersible Stop Driving Up/Down
        rx_releaseDriveUp();
        break;
      case 'B':
        rx_driveDown();
        break;
      case 'Y':  //ROV Submersible Stop Driving Up/Down
        rx_releaseDriveDown();
        break;
        //-----------------------------ROV Submersible Drive Forward/Backward
      case 'C':
        rx_driveForward();
        break;
      case 'X':  //ROV Submersible Stop Driving Forward
        rx_releaseDriveForward();
        break;
      case 'D':
        rx_driveBackward();
        break;
      case 'W':  //ROV Submersible Stop Driving Backward
        rx_releaseDriveBackward();
        break;
        //-----------------------------ROV Submersible Drive Left/Right
      case 'E':
        rx_driveLeft();
        break;
      case 'V':  //ROV Submersible Stop Driving Left
        rx_releaseDriveLeft();
        break;
      case 'F':
        rx_driveRight();
        break;
      case 'U':  //ROV Submersible Stop Driving Right
        rx_releaseDriveRight();
        break;
        //-----------------------------ROV Submersible Clamp Open/Close
/*      case 'G':
        rx_clampClose();
        break;
      case 'T':  //ROV Submersible Clamp Stop OClosing
        rx_releaseClampClose();
        break;
      case 'H':
        rx_clampOpen();
        break;
      case 'S':  //ROV Submersible Clamp Stop Opening
        rx_releaseClampOpen();
        break;
        //-----------------------------ROV Submersible Lights On/Off
      case 'I':
        rx_lightsOn();
        break;
      case 'R':  //ROV Submersible Lights Stop On
        rx_releaseLightsOn();
        break;
      case 'J':
        rx_lightsOff();
        break;
      case 'Q':  //ROV Submersible Lights Stop Off
        rx_releaseLightsOff();
        break;

      default:
        Serial.write('N');  // BLACK
        break;
*/
    }
    /* not really needed but in case you want to do things in the loop forwardon receiving new chars. if (incomingChar == 0) then no new char was received, 
  otherwise later in the loop you still have the char received*/
  } else incomingChar = 0;  

  if (Serial2.available() > 0) {
    incomingChar = Serial2.read();
    switch (incomingChar) {
/*      
        //-----------------------------ROV Submersible Drive Up/Down
      case 'A':
        rx_driveUp();
        break;
      case 'Z':  //ROV Submersible Stop Driving Up/Down
        rx_releaseDriveUp();
        break;
      case 'B':
        rx_driveDown();
        break;
      case 'Y':  //ROV Submersible Stop Driving Up/Down
        rx_releaseDriveDown();
        break;
        //-----------------------------ROV Submersible Drive Forward/Backward
      case 'C':
        rx_driveForward();
        break;
      case 'X':  //ROV Submersible Stop Driving Forward
        rx_releaseDriveForward();
        break;
      case 'D':
        rx_driveBackward();
        break;
      case 'W':  //ROV Submersible Stop Driving Backward
        rx_releaseDriveBackward();
        break;
        //-----------------------------ROV Submersible Drive Left/Right
      case 'E':
        rx_driveLeft();
        break;
      case 'V':  //ROV Submersible Stop Driving Left
        rx_releaseDriveLeft();
        break;
      case 'F':
        rx_driveRight();
        break;
      case 'U':  //ROV Submersible Stop Driving Right
        rx_releaseDriveRight();
        break;
*/        
        //-----------------------------ROV Submersible Clamp Open/Close
      case 'G':
        rx_clampClose();
        break;
      case 'T':  //ROV Submersible Clamp Stop OClosing
        rx_releaseClampClose();
        break;
      case 'H':
        rx_clampOpen();
        break;
      case 'S':  //ROV Submersible Clamp Stop Opening
        rx_releaseClampOpen();
        break;
        //-----------------------------ROV Submersible Lights On/Off
      case 'I':
        rx_lightsOn();
        break;
      case 'R':  //ROV Submersible Lights Stop On
        rx_releaseLightsOn();
        break;
      case 'J':
        rx_lightsOff();
        break;
      case 'Q':  //ROV Submersible Lights Stop Off
        rx_releaseLightsOff();
        break;

      default:
        Serial.write('N');  // BLACK
        break;
    }
    /* not really needed but in case you want to do things in the loop forwardon receiving new chars. if (incomingChar == 0) then no new char was received, 
  otherwise later in the loop you still have the char received*/
  } else incomingChar = 0;  
}

Sketch for the Uno (currently mirrored on both Uno)


void setup() {
  Serial.begin(9600);  // enables the serial connection. Make sure it's the same on both sides (here in the Arduino IDE and in the Processing program)

  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
}
//Char sent to the processing program via usb serial to visulise transmit/recieve
void driveUp() {
  digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on (HIGH is the voltage level)
  Serial.write('A');
}
void releaseDriveUp() {
  digitalWrite(LED_BUILTIN, LOW);   // turn the LED off by making the voltage LOW
  Serial.write('Z');
}
void driveDown() {
  digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on (HIGH is the voltage level)
  Serial.write('B');
}
void releaseDriveDown() {
  digitalWrite(LED_BUILTIN, LOW);   // turn the LED off by making the voltage LOW
  Serial.write('Y');
}
void driveForward() {
  digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on (HIGH is the voltage level)
  Serial.write('C');
}
void releaseDriveForward() {
  digitalWrite(LED_BUILTIN, LOW);   // turn the LED off by making the voltage LOW
  Serial.write('X');
}
void driveBackward() {
  digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on (HIGH is the voltage level)
  Serial.write('D');
}
void releaseDriveBackward() {
  digitalWrite(LED_BUILTIN, LOW);   // turn the LED off by making the voltage LOW
  Serial.write('W');
}
void driveLeft() {
  digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on (HIGH is the voltage level)
  Serial.write('E');
}
void releaseDriveLeft() {
  digitalWrite(LED_BUILTIN, LOW);   // turn the LED off by making the voltage LOW
  Serial.write('V');
}
void driveRight() {
  digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on (HIGH is the voltage level)
  Serial.write('F');
}
void releaseDriveRight() {
  digitalWrite(LED_BUILTIN, LOW);   // turn the LED off by making the voltage LOW
  Serial.write('U');
}
void clampClose() {
  digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on (HIGH is the voltage level)
  Serial.write('G');
}
void releaseClampClose() {
  digitalWrite(LED_BUILTIN, LOW);   // turn the LED off by making the voltage LOW
  Serial.write('T');
}
void clampOpen() {
  digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on (HIGH is the voltage level)
  Serial.write('H');
}
void releaseClampOpen() {
  digitalWrite(LED_BUILTIN, LOW);   // turn the LED off by making the voltage LOW
  Serial.write('S');
}
void lightsOn() {
  digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on (HIGH is the voltage level)
  Serial.write('I');
}
void releaseLightsOn() {
  digitalWrite(LED_BUILTIN, LOW);   // turn the LED off by making the voltage LOW
  Serial.write('R');
}
void lightsOff() {
  digitalWrite(LED_BUILTIN, HIGH);  // turn the LED on (HIGH is the voltage level)
  Serial.write('J');
}
void releaseLightsOff() {
  digitalWrite(LED_BUILTIN, LOW);   // turn the LED off by making the voltage LOW
  Serial.write('Q');
}
//countinuously watches the recieved serial char for appropriate cases and runs the appropriate function
void loop() {
  char incomingChar = 0;  // for incoming serial data

  if (Serial.available() > 0) {
    incomingChar = Serial.read();
    switch (incomingChar) {
        //-----------------------------ROV Submersible Drive Up/Down
      case 'a':
        driveUp();
        break;
      case 'z':  //ROV Submersible Stop Driving Up/Down
        releaseDriveUp();
        break;
      case 'b':
        driveDown();
        break;
      case 'y':  //ROV Submersible Stop Driving Up/Down
        releaseDriveDown();
        break;
        //-----------------------------ROV Submersible Drive Forward/Backward
      case 'c':
        driveForward();
        break;
      case 'x':  //ROV Submersible Stop Driving Forward
        releaseDriveForward();
        break;
      case 'd':
        driveBackward();
        break;
      case 'w':  //ROV Submersible Stop Driving Backward
        releaseDriveBackward();
        break;
        //-----------------------------ROV Submersible Drive Left/Right
      case 'e':
        driveLeft();
        break;
      case 'v':  //ROV Submersible Stop Driving Left
        releaseDriveLeft();
        break;
      case 'f':
        driveRight();
        break;
      case 'u':  //ROV Submersible Stop Driving Right
        releaseDriveRight();
        break;
        //-----------------------------ROV Submersible Clamp Open/Close
      case 'g':
        clampClose();
        break;
      case 't':  //ROV Submersible Clamp Stop OClosing
        releaseClampClose();
        break;
      case 'h':
        clampOpen();
        break;
      case 's':  //ROV Submersible Clamp Stop Opening
        releaseClampOpen();
        break;
        //-----------------------------ROV Submersible Lights On/Off
      case 'i':
        lightsOn();
        break;
      case 'r':  //ROV Submersible Lights Stop On
        releaseLightsOn();
        break;
      case 'j':
        lightsOff();
        break;
      case 'q':  //ROV Submersible Lights Stop Off
        releaseLightsOff();
        break;

      default:
        Serial.write('N');  // BLACK
        break;
    }
    /* not really needed but in case you want to do things in the loop forwardon receiving new chars. if (incomingChar == 0) then no new char was received, 
  otherwise later in the loop you still have the char received*/
  } else incomingChar = 0;
}

Processing Code

import processing.serial.*;

// The serial port:
Serial mySerialPort;

color fillVal = color(0); //sets up initial colour for program window - black/blank

//setting a condition on a keypress to eliminate the continued press of a key when holding a button
boolean driveUp = false;
boolean driveDown = false;
boolean driveForward = false;
boolean driveBackward = false;
boolean driveLeft = false;
boolean driveRight = false;
boolean clampClose = false;
boolean clampOpen = false;
boolean lightsOn = false;
boolean lightsOff = false;

String year, month, day, hour, minute, second, millis;

float txCharCount = 0;
float rxCharCount = 1; //- off set to zero the serial count

void setup() {
  size(100, 100);//program indication window size
  noStroke();
  background(255);

  printArray(Serial.list()); // do this to find your arduino
  // Open the port you are using at the rate you want:
  mySerialPort = new Serial(this, Serial.list()[2], 9600);// this port 2 should correspond to the com port the arduino is plugged into - example: 2 = COM6
  mySerialPort.clear();
}
void draw() {
  background(fillVal);//sets window background colour
}

void keyPressed() {

  //Setting timestamp format
  year = nf(year(), 4);
  month = nf(month(), 2);
  day = nf(day(), 2);
  hour = nf(hour(), 2);
  minute = nf(minute(), 2);
  second = nf(second(), 2);
  millis = nf(millis(), 4);

  //ROV Submersible Drive Up/Down
  if (key == 'w' || key == 'W') {
    if ( !driveUp) {
      driveUp = true;
      mySerialPort.write('a');
      txCharCount ++;
      println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'a' - driveUp");
    }
  } else if (key == 's' || key == 'S') {
    if ( !driveDown) {
      driveDown = true;
      mySerialPort.write('b');
      txCharCount ++;
      println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'b' - driveDown");
    }
  }
  //ROV Submersible Drive Forward/Backward
  else if (keyCode == UP) {
    if ( !driveForward) {
      driveForward = true;
      mySerialPort.write('c');
      txCharCount ++;
      println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'c' - driveForward");
    }
  } else if (keyCode == DOWN) {
    if ( !driveBackward) {
      driveBackward = true;
      mySerialPort.write('d');
      txCharCount ++;
      println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'd' - driveBackward");
    }
  }
  //ROV Submersible Drive Left/Right
  else if (keyCode == LEFT) {
    if ( !driveLeft) {
      driveLeft = true;
      mySerialPort.write('e');
      txCharCount ++;
      println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'e' - driveLeft");
    }
  } else if (keyCode == RIGHT) {
    if ( !driveRight) {
      driveRight = true;
      mySerialPort.write('f');
      txCharCount ++;
      println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'f' - driveRight");
    }
  }
  //ROV Submersible Clamp Open/Close
  else if (key == 'd' || key == 'D') {
    if ( !clampClose) {
      clampClose = true;
      mySerialPort.write('g');
      txCharCount ++;
      println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'g' - clampClose");
    }
  } else if (key == 'a' || key == 'A') {
    if ( !clampOpen) {
      clampOpen = true;
      mySerialPort.write('h');
      txCharCount ++;
      println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'h' - clampOpen");
    }
  }
  //ROV Submersible Lights On/Off
  else if (key == 'e' || key == 'E') {
    if ( !lightsOn) {
      lightsOn = true;
      mySerialPort.write('i');
      txCharCount ++;
      println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'i' - lightsOn");
    }
  } else if (key == 'q' || key == 'Q') {
    if ( !lightsOff) {
      lightsOff = true;
      mySerialPort.write('j');
      txCharCount ++;
      println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'j' - lightsOff");
    }
  }
    float reliability = rxCharCount/txCharCount*100;
    println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+" Communications   - "+txCharCount+" tx"+" : "+rxCharCount+" rx     " + nf(reliability, 2, 1)+ "% ");
    
}

//The keyReleased() function is called once every time a key is released. This essentially resets the motors being controlled by the arduino stopping them from spinning - the b char that is sent releases the motors from use

void keyReleased() {

  //Setting timestamp format
  year = nf(year(), 4);
  month = nf(month(), 2);
  day = nf(day(), 2);
  hour = nf(hour(), 2);
  minute = nf(minute(), 2);
  second = nf(second(), 2);
  millis = nf(millis(), 4);

  //ROV Submersible Stop Going Up/Down
  if (key == 'w' || key == 'W') {
    driveUp = false;
    mySerialPort.write('z');
    //txCharCount ++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'z' - stop driveUp");
  } else if (key == 's' || key == 'S') {
    driveDown = false;
    mySerialPort.write('y');
    //txCharCount ++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'y' - stop driveDown");
  }
  //ROV Submersible Stop Driving Forward/Backward
  else if (keyCode == UP) {
    driveForward = false;
    mySerialPort.write('x');
    //txCharCount ++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'x' - stop driveForward");
  } else if (keyCode == DOWN) {
    driveBackward = false;
    mySerialPort.write('w');
    //txCharCount ++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'w' - stop driveBackward");
  }
  //ROV Submersible Stop Driving Left/Right
  else if (keyCode == LEFT) {
    driveLeft = false;
    mySerialPort.write('v');
    //txCharCount ++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'v' - stop driveLeft");
  } else if (keyCode == RIGHT) {
    driveRight = false;
    mySerialPort.write('u');
    //txCharCount ++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'u' - stop driveRight");
  }
  //ROV Submersible Clamp Stop Opening/Closing
  else if (key == 'd' || key == 'D') {
    clampClose = false;
    mySerialPort.write('t');
    //txCharCount ++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 't' - stop clampClose");
  } else if (key == 'a' || key == 'A') {
    clampOpen = false;
    mySerialPort.write('s');
    //txCharCount ++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 's' - stop clampOpen");
  }
  //ROV Submersible Lights Stop On/Off
  else if (key == 'e' || key == 'E') {
    lightsOn = false;
    mySerialPort.write('r');
    //txCharCount ++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'r' - stop lightsOn");
  } else if (key == 'q' || key == 'Q') {
    lightsOff = false;
    mySerialPort.write('q');
    //txCharCount ++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " PC Transmit: 'q' - stop lightsOff");
  }
}

//assigns the background colours of the program window
void serialEvent(Serial p) {

  //Setting timestamp format
  year = nf(year(), 4);
  month = nf(month(), 2);
  day = nf(day(), 2);
  hour = nf(hour(), 2);
  minute = nf(minute(), 2);
  second = nf(second(), 2);
  millis = nf(millis(), 4);
  


  switch(p.read()) {
  case 'A':
    fillVal = #FF416C;//Vibrant Red - Drive Up
    rxCharCount++;
    println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+" ROV Receive: 'A' - driveUp");
    break;
  case 'B':
    fillVal = #F9C74F;//Vibrant Yellow - Drive Down
    rxCharCount++;
    println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+" ROV Receive: 'B' - driveDown");
    break;
  case 'C':
    fillVal = #0077B6;//Vibrant Blue - Drive Forward
    rxCharCount++;
    println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+" ROV Receive: 'C' - driveForward");
    break;
  case 'D':
    fillVal = #2AA877;//Vibrant Green - Drive Backward
    rxCharCount++;
    println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+" ROV Receive: 'D' - driveDown");
    break;
  case 'E':
    fillVal = #FF6F00;//Vibrant Orange - Drive Left
    rxCharCount++;
    println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+" ROV Receive: 'E' - driveLeft");
    break;
  case 'F':
    fillVal = #7A3B69;//Vibrant Purple - Drive Right
    rxCharCount++;
    println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+" ROV Receive: 'F' - driveRight");
    break;
  case 'G':
    fillVal = #00A7B5;//Vibrant Teal - Clamp Close
    rxCharCount++;
    println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+" ROV Receive: 'G' - clampClose");
    break;
  case 'H':
    fillVal = #FF66A1;//Vibrant Pink - Clamp Open
    rxCharCount++;
    println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+" ROV Receive: 'H' - clampOpen");
    break;
  case 'I':
    fillVal = #C2E812;//Vibrant Lime Green - Lights On
    rxCharCount++;
    println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+" ROV Receive: 'I' - lightsOn");
    break;
  case 'J':
    fillVal = #00C2B1;//Vibrant Turquoise - Lights Off
    rxCharCount++;
    println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+" ROV Receive: 'J' - lightsOff");
    break;

    /*-------------------------------------------------------------------------------------------------------------------*/

/*  case 'Q':
    //fillVal = #FF416C;//Vibrant Red - Drive Up
    rxCharCount++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " ROV Receive: 'Q' - releaseLightsOff");
    break;
  case 'R':
    //fillVal = #F9C74F;//Vibrant Yellow - Drive Down
    rxCharCount++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " ROV Receive: 'R' - releaseLightsOn");
    break;
  case 'S':
    //fillVal = #0077B6;//Vibrant Blue - Drive Forward
    rxCharCount++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " ROV Receive: 'S' - releaseClampOpen");
    break;
  case 'T':
    //fillVal = #2AA877;//Vibrant Green - Drive Backward
    rxCharCount++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " ROV Receive: 'T' - releaseClampClose");
    break;
  case 'U':
    //fillVal = #FF6F00;//Vibrant Orange - Drive Left
    rxCharCount++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " ROV Receive: 'U' - releaseDriveRight");
    break;
  case 'V':
    //fillVal = #7A3B69;//Vibrant Purple - Drive Right
    rxCharCount++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " ROV Receive: 'V' - releaseDriveLeft");
    break;
  case 'W':
    //fillVal = #00A7B5;//Vibrant Teal - Clamp Close
    rxCharCount++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " ROV Receive: 'W' - releaseDriveDown");
    break;
  case 'X':
    //fillVal = #FF66A1;//Vibrant Pink - Clamp Open
    rxCharCount++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " ROV Receive: 'X' - releaseDriveForward");
    break;
  case 'Y':
    //fillVal = #C2E812;//Vibrant Lime Green - Lights On
    rxCharCount++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " ROV Receive: 'Y' - releaseDriveDown");
    break;
  case 'Z':
    //fillVal = #00C2B1;//Vibrant Turquoise - Lights Off
    rxCharCount++;
    //println(year+"/"+month+"/"+day+" "+hour+":"+minute+":"+second+"."+millis+ " ROV Receive: 'Z' - releaseDriveUp");
    break;
*/
    default:
    fillVal = #000000;//Black
    break;
  }
}

Extra question for anyone in the Processing 'know': I seem to have hit a limit with the Case switch for my char. I didnt notice loss of char however when the case breaks out it should call the default case and fill the background black - when I include another 5+ char Cases - similarly to what is commented out. the default is not called and the background stays the colour of the last keypress

15 meters is the distance when using RS232, not when using TTL signals.

Luck?

The RS232 standard is for a distance of 50ft at 9600 baud to work.

In practice much longer runs will work, depending on the cable type etc .......

1 Like

Ok so TTL should have a maximum of around 100m and perhaps because I am running 9600 I am getting a bit further. So if increase to 115200 I would expect some losses?

There is a difference between the guaranteed maximum usable distance, and the actual maximum distance under favorable (if not real-word realistic) conditions. Your use of shielded CAT6 cable may also make a difference, depending on which particular conductors you used, if the shield is ground or not, etc.

You might want to look into RS-485, it is designed for much longer distances than RS-232.

What probably saved you is that you're using shielded cable.

But I would not rely on it unless you want it to end up like the Titan.

2 Likes

Shielding was ungrounded - I only used this as it is what I had lying around. I had two lengths one around 20m so to actually achieve the 160 all wires were joined together - so far from ideal and yet....

But yes, so what I have observed is not crazy but in the grey area of it can work but do not bet your life on it

Yeah my intention was to get some MAX485 modules and run RS485 but I decided to see what distance I could get with what I currently have and I was taken a bit by surprise.

I was also going to trial PJON bitbang - although I haven't done any reading I just came across someone mentioning this in a forum post, perhaps it is not appropriate.

Another point about CAT6 cable, pairs of the conductors are twisted together, so which particular wires you used can have an effect on the results.

My opinion is that when the RS232 cabling standard was established, there were no cables with the technical quality of the current CAT6 cables.

According to the EIA/TIA standard, the CAT6 cable can carry 1Gigabit of data up to a distance of 100m.

So as these cables have a low loss rate even at high frequencies, we can get good distances using it. But anyway it is outside the RS232 standards.

Category.....Standard Bandwidth.....Max Data Rate
Cat6............250MHz (up to 550).......1000Mbps
Cat6A.........500MHz (up to 550)......10Gbps
Cat7............600MHz..........................10Gbps
Cat8............2000MHz......................25Gbps or 40Gbps

TIA/EIA-232-F Standard

RS232 conveys data over a simple unterminated, multiconductor cable at rates up to 20kB. The RS232 standard specifies the electrical characteristics
and connector for an all encompassing point-to-point modem interface.

1 Like

True enough, but the standard was based on the ability of the transistor circuits of that day to detect the voltage transition from + to - and from - to + in a timely basis. The capacitance of the cable pairs contributed to the problem.

1 Like

If I understand your codes correctly, the Processing application sends a character to the Mega when a key is pressed and another character when that key is released. The Mega forwards that to the Uno and the Uno returns the uppercase version of the character to the Mega and the Mega passes it back to the Processing application.

If so, you will only received uppercase characters in the Processing application and if you have all possible cases covered in the switch and hence there will be no default.

The colour does not change when you e.g. release 'w' because you send 'z' to the Mega, get back 'Z' from the Mega and don't set the colour in case 'Z'.

You are confusing an electrical specification to the capability under various conditions.

The 15m specification is like the "guaranteed success" value under a stated set of conditions. You essentially picked a set of conditions that allowed a longer distance for a limited trial. Your statement of "no problems" was likely for very limited testing. If as you mentioned you try to apply this for an ROV (which I assume has a motor of some sort) you will likely have issues, maybe only periodic but some issues are likely.

Making decisions based on limited testing is kind of like Russian Roulette. Surviving a few spins makes one think the gun is empty.

2 Likes

I used to wire 'networks' and in the early days a lot of them were central servers with RS232 connections to 'terminals'.

The standard cable was nothing fancy, screened maybe.

However much of the so called quality of twisted pair cables, such as CAT6, is to do with the evenness and pitch of the twists, which when the receivers are differential types, allows a lot of external and inter cable interference to be cancelled.

Standard RS232 circuits dont use differential receivers so dont gain most of the benefits of good twisted pair cables.

And I used to manage large data networks and had a consulting data communications business. There were high speed data rate cables, but very few business wanted to pay the price, so they settled for slower data rates.
Where distances were too great, limited distance modems were used to extend the communications to many hundreds of feet. In one case I had to use the PBX wiring from building to building because the customer neglected to include data communications when they designed the banking complex. Or forgot that the drive-up across the street needed data communication.
So, there are, still today, options to do long distance communications between Arduino, if necessary.

Yup. RS485. Haven't seen a single thing in this thread that prevents it as a solution, other than the OP's expectation that it might not be required. But the application is ripe for a multidrop system such as RS485, running at 115200 or higher, with many more ROVs potentially.

@evanlyons You can use your Cat6, or a more appropriate wire. Be sure to read up on how to implement RS485 correctly, and you'll be away to the races. Start here:

RS-485 Basics Series

Yep, I remember them.

It may not work at all. The lower the Baud rate, the longer the cable tolerated. Cable capacitance is a major issue.

Told you! (told somebody, in a similar thread, anyway.)

There is no spec for the distance that a "5V CMOS Level Signal" can travel over wires.
The transmitter can provide substantial current, and the receiver needs essentially none. In addition, the UART protocol is quite robust; it detects "edges", but doesn't actually sample the value until what it expects is the "middle" of the bit, which is going to correct for lot of the signal degradation one might expect from long cables (The rs232 spec doesn't take this into account either, since it's not exclusively a UART protocol. That probably has something to do with the "success" of many standard-violating terminal installations in the old times.)

The main problem you might encounter is that a long cable could pick up electrical "noise" from the environment, perhaps enough to damage the signal.
Or you might run into significant differences in "ground" potential, leading to excessive currents in the GND wire.

And I guess eventually, the bits will smear out into something that's not recognizable (it'd be "interesting" to look at the signals you have with a scope.)
I suppose that at some point you ought to worry about "physics", by the wavelength of a 9600Hz signal is about 30,000 meters, so ... maybe not. (Purists will argue that there are significant harmonics in a 5V square wave, but I'll argue that that's what the UART "sample the middle part of the bit" helps with...)

It would probably be "interesting" to try to analyze such transmission from a physics PoV, and then compare to actual measurements. But that part of physics was one of my weak points :frowning: (But, waving hands: the big worry is supposed to be cable capacitance. Cat cable is supposed to be about 6nF/100m, so your 160m cable has about 10nF of capacitance. The time constant for 100nF being driven by 5V@20mA is therefore C*V/I = 2.5us. Bit time of a 9600Hz signal is 104us. Seems like it should work fine.

Yup, but cable capacitance disappears when you correctly terminate the cable.
Leo..

You are absolutely right, I feel a bit embarrassed to not have seen this as the answer.... Code now changed and working as expected. - I removed the default state and included a black Fill to each keyrelease capital char... thanks for saving me from my assumptions!!