Underwater ROV, slow rs 485 communication Part 1 of 2

Hey All!

First, I want to thank everyone for there help thus far. Before I jump in to my question/request I want to tell everyone what my project is all about. Please note this is a hobby of mine. I am doing this purely for fun and to explore the deep dark areas of my lake. My ROV consists of 6 brushless thrusters, 4 on the top of the ROV and 2 on the end for propulsion. The 4 on the top are for a controlled decent. At the surface I have an Arduino mega that is attached to a laptop and to another Arduino Mega in the ROV. The surface will analogRead from six pot each for a thruster below. The Arduino below will send the maped reads from the pots above to a Pululo Servo control which will pulse the desired speed to 6 ESC that will control the brushless motors. Each Brushless inrunner (KV2630) is powered by a 12v 5000mah Li-po battery. The Arduino in the ROV is also responsible for reading values from a 3 axis tilt sensor, a light sensor, a temperature/humidity sensor and a pressure sensor. I also expect to add a compass in the near future. The Arduino below collects all the information and transmits it byte by byte back to the surface Arduino via RS485. The surface Arduino then prints the sendor information to the laptop.

I know that was a long winded explanation of what I am doing, but as you can see, i am really excited about this project.

Now to the questions/issue i am having. I find the communication from the surface to the rov and back is slow. When I adjust a pot on the surface there is a lag period. This is also the case with the sensor readings from the rov to the surface. I do not pretend to be an experienced Arduino programmer and I am sure the issue lies in my code. Also it is important to note that I often see the onboard pin 13 led go on, infact i would say it goes around every second or so and it does not really seem to follow any pattern. Once you look at the master code (surface) you will see some code regarding the onboard LED that refects a comunication error. Please note most if not all of the communication code and library is from Nick Gammon's rs485 example and library.

Please let me know if any of you see a way to stream line my programming or if you see a major error that could be the reason why I am seeing slowish communication.

Edit--- please see part 2 of this post to see my Surface and ROV code examples. Seems i have run over the limit of characters per post.

Andrew

Please read part 1 first.

Master (Surface)

#include <RS485_protocol.h>
#include <SoftwareSerial.h>
SoftwareSerial rs485 (10, 11);  // rx pin, tx pin

// Pins
int forePort = A0; //Top fore port thruster PIN for analogRead
int foreStar = A1;
int aftPort = A2;
int aftStar = A3;
int portThruster = A4; // Main port drive thruster pin for analogRead
int starThruster = A5;
const byte ENABLE_PIN = 24; // pin used to set hi and low, transmit or rec
const byte LED_PIN = 13;


//Thrust VAriables
int forePortVal = 1100; //testing levels
int foreStarVal = 1200; 
int aftPortVal = 1300; 
int aftStarVal = 1400; 
int portThrusterVal = 1500; 
int starThrusterVal = 1600; 

byte forePortValHi, foreStarValHi, aftPortValHi, aftStarValHi, portThrusterValHi, starThrusterValHi; // Hibytes for thrust
byte forePortValLo, foreStarValLo, aftPortValLo, aftStarValLo, portThrusterValLo, starThrusterValLo; // Lowbytes for thrust

//Unions for temp humidity and dewpoint
union temperature 
{
  float temp;
  byte b[4];
} t;

union humidity 
{
  float hum;
  byte b[4];
} h;

union dewpoint 
{
  float dew;
  byte b[4];
} d;

//Sensors
int pressureLvl, lightLvl, xAxis, yAxis, zAxis;
float temperature; //for temp and humidity
float humidity; 
float dewpoint;

// callback routines
  
void fWrite (const byte what)
  {
  rs485.write (what);  
  }
  
int fAvailable ()
  {
  return rs485.available ();  
  }

int fRead ()
  {
  return rs485.read ();  
  }

void setup()
{
  Serial.begin(115200);
  rs485.begin (57600);
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  pinMode (LED_PIN, OUTPUT);  // built-in LED
}  // end of setup
  


void loop()
{

  //Thurster pot readings and calibration
  forePortVal = analogRead(forePort); // Reads the value from the pot to forePortVal 
  forePortVal = map(forePortVal, 0, 1023, 4000, 8000); // Calibrates the min max from the pot to correspond with servo pulses 1000 to 2000
  
  //will add other 5 pots
  
  //-------------------------Hi Byte Low Byte creation of variables to be sent over to Slave------
  forePortValHi = highByte(forePortVal);
  forePortValLo = lowByte(forePortVal);
  foreStarValHi = highByte(foreStarVal);
  foreStarValLo = lowByte(foreStarVal);
  aftPortValHi = highByte(aftPortVal);
  aftPortValLo = lowByte(aftPortVal);
  aftStarValHi = highByte(aftStarVal);
  aftStarValLo = lowByte(aftStarVal);
  portThrusterValHi = highByte(portThrusterVal);
  portThrusterValLo = lowByte(portThrusterVal);
  starThrusterValHi = highByte(starThrusterVal);
  starThrusterValLo = lowByte(starThrusterVal);
  
      
  // assemble message
  byte msg [] = { 
     1,    // device 1
     2,    // turn light on
     forePortValHi, // first half of forePortValHi
     forePortValLo,  // second have of level
     foreStarValHi,
     foreStarValLo,
     aftPortValHi,
     aftPortValLo,
     aftStarValHi,
     aftStarValLo,
     portThrusterValHi,
     portThrusterValLo,
     starThrusterValHi, 
     starThrusterValLo    
  };

  // send to slave  
  digitalWrite (ENABLE_PIN, HIGH);  // enable sending
  sendMsg (fWrite, msg, sizeof msg);
  digitalWrite (ENABLE_PIN, LOW);  // disable sending

  // receive response  
  byte buf [24];
  byte received = recvMsg (fAvailable, fRead, buf, sizeof buf);
  
  digitalWrite (LED_PIN, received == 0);  // turn on LED if error    
  
//Create Sensor varaibles from Hi low
pressureLvl = word(buf [2], buf [3]);
lightLvl = word(buf [4], buf [5]);
xAxis = word(buf [6], buf [7]);
yAxis = word(buf [8], buf [9]);
zAxis = word(buf [10], buf [11]);
t.b[0] = buf [12]; // Send bytes from the buffer to the unions
t.b[1] = buf [13];
t.b[2] = buf [14];
t.b[3] = buf [15];
h.b[0] = buf [16];
h.b[1] = buf [17];
h.b[2] = buf [18];
h.b[3] = buf [19];
d.b[0] = buf [20];
d.b[1] = buf [21];
d.b[2] = buf [22];
d.b[3] = buf [23];

temperature = t.temp; // Pull floats from the union
humidity = h.hum;
dewpoint = d.dew;

  Serial.print("Pressure Level = ");
  Serial.print(pressureLvl);
  Serial.print("\t");
  Serial.print("Light Level = ");
  Serial.print(lightLvl);
  Serial.print("\t");
  Serial.print("X Axis = ");
  Serial.print(xAxis);
  Serial.print("\t");  
  Serial.print("y Axis = ");
  Serial.print(yAxis);
  Serial.print("\t");  
  Serial.print("Z Axis = ");
  Serial.print(zAxis);
  Serial.print("\t");  
  Serial.print("Temperature = ");
  Serial.print(temperature);
  Serial.print("\t");  
  Serial.print("Humidity = ");
  Serial.print(humidity);
  Serial.print("\t");  
  Serial.print("Dewpoint = ");
  Serial.println(dewpoint);
}  // end of loop

Slave (ROV)

#include <RS485_protocol.h>
#include <SoftwareSerial.h>
#include <Sensirion.h>
SoftwareSerial rs485 (10, 11);  // rx pin, txpin


// Pins
int pressurePin = A15; //  for pressure sensor
int lightPin = A14; // for light levels
const uint8_t dataPin  =  9; //data pint for temp and humidity
const uint8_t clockPin =  8; // clock pin for temp and humidity
const int xpin = A3;                  // x-axis
const int ypin = A2;                  // y-axis
const int zpin = A1;                  // z-axis
const byte ENABLE_PIN = 24;

//Thrust VAriables
int forePortVal, foreStarVal, aftPortVal, aftStarVal, portThrusterVal, starThrusterVal; //thruster variables

//Sensors VAriables
float temperature; //for temp and humidity
float humidity; 
float dewpoint; 
Sensirion tempSensor = Sensirion(dataPin, clockPin);
int pressureLvl = 0; // for pressure
int lightLvl = 200; // for light level
int xAxis, yAxis, zAxis;
byte hipressureLvl, lopressureLvl, lolightLvl, hilightLvl, loxAxis, hixAxis, loyAxis, hiyAxis, lozAxis, hizAxis, temperature0, temperature1, temperature2, temperature3, humidity0, humidity1, humidity2, humidity3, dewpoint0, dewpoint1, dewpoint2, dewpoint3;

//Unions for temp hum
union temperature 
{
  float temp;
  byte b[4];
} t;

union humidity 
{
  float hum;
  byte b[4];
} h;

union dewpoint 
{
  float dew;
  byte b[4];
} d;

void fWrite (const byte what)
  {
  rs485.write (what);  
  }
  
int fAvailable ()
  {
  return rs485.available ();  
  }

int fRead ()
  {
  return rs485.read ();  
  }

void setup()
{
  Serial.begin(115200); // for testing only
  rs485.begin (57600); // communication 4 RS485
  Serial2.begin(115200); //  Pololu Servo controller
  pinMode (ENABLE_PIN, OUTPUT);  // driver output enable
  digitalWrite (ENABLE_PIN, LOW);  // Sets pin to low 4 rs485

}

void loop()
{
//Sensor readings
//Pressure sensor
pressureLvl = analogRead(pressurePin);

//Light levels
lightLvl = analogRead(lightPin);

//temp and humidity
tempSensor.measure(&temperature, &humidity, &dewpoint);

// X Y and Z Axis readings
xAxis = analogRead(xpin);
yAxis = analogRead(ypin);
zAxis = analogRead(zpin);
t.temp = temperature;
h.hum = humidity;
d.dew = dewpoint;




//Hi Lo byte creation
  hipressureLvl = highByte(pressureLvl); // splits bytes off
  lopressureLvl = lowByte(pressureLvl);
  hilightLvl = highByte(lightLvl); 
  lolightLvl = lowByte(lightLvl);
  hixAxis = highByte(xAxis); 
  loxAxis = lowByte(xAxis); 
  hiyAxis = highByte(yAxis);
  loyAxis = lowByte(yAxis); 
  hizAxis = highByte(zAxis); 
  lozAxis = lowByte(zAxis); 



//Send sensor readings
  byte buf [24];
  
  byte received = recvMsg (fAvailable, fRead, buf, sizeof (buf) - 1);
  
  if (received)
    {
    if (buf [0] != 1)
      return;  // not my device
      
    if (buf [1] != 2)
      return;  // unknown command
    
    byte msg [] = {
       0,  // device 0 (master)
       3,  // turn light on command received
       hipressureLvl,
       lopressureLvl,
       hilightLvl,
       lolightLvl,
       hixAxis,
       loxAxis, 
       hiyAxis,
       loyAxis, 
       hizAxis,
       lozAxis,
       t.b[0],
       t.b[1],
       t.b[2],
       t.b[3],
       h.b[0],
       h.b[1],
       h.b[2],
       h.b[3],
       d.b[0],
       d.b[1],
       d.b[2],
       d.b[3]
      
    };
    
    delay (1); 
    digitalWrite (ENABLE_PIN, HIGH);  // enable sending
    sendMsg (fWrite, msg, sizeof msg);
    digitalWrite (ENABLE_PIN, LOW);  // disable sending
    

    forePortVal = word(buf [2], buf[3]);   // convert the bytes from master
    foreStarVal = word(buf [4], buf[5]);
    aftPortVal = word(buf [6], buf[7]);
    aftStarVal = word(buf [8], buf[9]);
    portThrusterVal = word(buf [10], buf[11]);
    starThrusterVal = word(buf [12], buf[13]);

    Serial.println(forePortVal); // for testing only
    
   }  // end if something received
   
 //Thurster values to Servo Controller  
   set_target(1, forePortVal);
   set_target(2, foreStarVal);
   set_target(3, aftPortVal);
   set_target(4, aftStarVal);
   set_target(5, portThrusterVal);
   set_target(6, starThrusterVal);
   
}  // end of main loop

void set_target(unsigned char servo, unsigned int target)
{
    Serial2.write(0xAA); //start byte
    Serial2.write(0x0C); //device id
    Serial2.write(0x04); //command number
    Serial2.write(servo); //servo number
    Serial2.write(target & 0x7F);
    Serial2.write((target >> 7) & 0x7F);    
}

Thank you
Andrew

The words "slow" and "lag period" let me imagine a wide range of delay magnitudes. I imagine there is a twelve second latency before any action is noticed. I imaging the 100,000 baud is broken into myriad errors and recoveries, delivering only 12,000 baud.

Seems i have run over the limit of characters per post.

You can add attachments.

How to use this forum

Also it is important to note that I often see the onboard pin 13 led go on, infact i would say it goes around every second or so and it does not really seem to follow any pattern.

Why is this important? Is it resetting or something?

it might have something to do with this line:

digitalWrite (LED_PIN, received == 0);  // turn on LED if error

From the code in the other post, it looks like you are sending the control information, than waiting for a response. How much time does the ROV spend sending sensor data back to the surface, and what do you have in place that prevents the sensor data from delaying your control commands?

I'm very distrustful of SoftwareSerial, especially using it to receive critical data at high baud rates while you are trying to do other things like monitor sensors. I suspect that is causing many or all of the receive errors you are seeing. Why not use the hardware serial port on the Arduino in the ROV instead?

Reducing the baud rate may help.

Reducing the baud rate may help.

I agree. Calculate the maximum data you have to send in a given time period then use the lowest baud rate that will achieve that.

How do you know there is a delay between sending the command and the ROV acting?

@AndrewParsons - why did you make a 1/2 and 2/2 thread? Stick to one thread please.

@Nick... One thread, two posts. He indicated a length error due to the two code attachments. He could have used an attachment, but who would have taken the time; attachments are a real pain in help-me topics, better used for exhibit & show 'n tell XD

For me, I use a tablet for the forum responses, attachments are a double-pain.

Ray

@Andrew

What 485 transceivers are you using? AmbiLobe is likely correct, errors are causing retransmits and your bandwidth is being consumed. Is the cable carrying the serial communications the only cable in the sheath? Does this issue only happen when submerged... Or does it also happen when all laid-out on the floor? Is the cable the right impedance for the 485 transceivers? Proper capacitance?

Weird often goes to physical issues or specifications in cabling.

Ray

mrburnette:
@Nick... One thread, two posts.

They are one thread now. Personally I prefer code to be in the post, however if it doesn't fit, I prefer an attachment. I just can't be bothered to copy and paste three or four lots of posts together and hope I end up with what the poster has at his/her end.

I dont kniw what you're using to communicate, but as far as I know, radio waves dont travel awfully well in water. Could this be your problem?

mrburnette was asking :
"Is the cable carrying the serial communications the only cable in the sheath? Does this issue only happen when submerged... Or does it also happen when all laid-out on the floor? Is the cable the right impedance for the 485 transceivers? Proper capacitance?"

Andrew Parsons has not responded yet, so let me imagine a problem area :

The relative permittivity of water is 80. This affects the impedance of the transmission line and the velocity of signals.

Z = sqrt(L/C)
and C is calculated using the permittivity. The water around the cable may change the impedance, causing signal reflections.

@AmbiLobe -

Yes. There is a whole technology dealing with cables based on aerial suspension, buried, and underwater. It is not all about just being waterproof. Electrically the cable needs to be chosen properly; the longer the cable, the more data attenuation, and the more pronounced noise and mismatched impedance issues.

Ray

"There is a whole technology dealing with cables based on aerial suspension, buried, and underwater. It is not all about just being waterproof. Electrically the cable needs to be chosen properly; the longer the cable, the more data attenuation, and the more pronounced noise and mismatched impedance issues."

If the cable impedance underwater is less than in air, then the solution may be to use matched impedance terminating resistors with lower values than in the air. Maybe underwater use 20 ohms and in air use 50 ohms cable termination resistors, for a guess.

@AmbiLobe:

Resistors set the DC impedance but the AC component is more complex. My suggestion to the Op is to go back to basics, ensure that they have a 120 Ohm twisted-pair cable per the Maxi document: Guidelines for Proper Wiring of an RS-485 (TIA/EIA-485-A) Network | Analog Devices As described in the section Characteristic Impedance of Twisted-Pair Wire.

The cable should be specified for underwater use... But the issue is the water-air interface to the surface. Special shielding arrangements in the cable manufacture will minimize this point of mismatch.

The reduction in data rate may actually increase throughput if resends are the a large component of overall bandwidth.

Moving to outbound send using fiber for the data is the obvious gold-plated remedy, but there is no reason that the existing design should not perform adequately, IMO.

Ray

Edit...
References: Geomeasurements by Pulsing TDR Cables and Probes
By Kevin M. O'Connor, Charles M. Dowding

The OP hasn't really given any hardware details on the wiring and hardwire control setups. Was any preliminary testing or hardware setups used to arrive at the current ROV configuration? Previous ROV discussions that might be of interest.

https://www.google.com/search?as_q=ROV&as_epq=&as_oq=&as_eq=&as_nlo=&as_nhi=&lr=&cr=&as_qdr=all&as_sitesearch=http%3A%2F%2Fforum.arduino.cc%2Findex&as_occt=any&safe=images&tbs=&as_filetype=&as_rights=