6 motors controlled via ethernet with game controller

Anything to do with programing the Arduino Platform.
Scanro
Posts: 22
Joined: Aug 15th, 2019, 4:30 am

6 motors controlled via ethernet with game controller

Post by Scanro »

Hello everyone,

My friends and I are making a ROV with 6 brushless motors, each has their own ESC. My job is to figure out the electronics and therefor also the code, but to be honest then I am quite new to coding and I find this very hard to do. 4 of the motors will control forward/backward and then the turning movement ofc and then 2 that control the up and down.

The main idea was to control it with a ethernet connection to a computer that has a xbox controller or Logitech joystick connected to it or maybe set up another arduino configuration on land that could handle the xbox controller/Logitech joystick inputs and send them via ethernet to the ROV. The ethernet connection is because we are making it for a competition where we need to make it go out beyond a USB connection capabilities.

Later a pressure sensor, temperature sensor and maybe a GYRO will be added, but for now it is the controlling the motors that are the main issue.

The hardware I currently have are a Arduino mega, ethernet shield and a leonardo and then for now a couple of ESC and brushless motors.

I would greatly appriciate some help with the coding and thanks in advance :)
Rasmus
User avatar
Bennachie
Posts: 113
Joined: Jul 6th, 2018, 11:38 am

Re: 6 motors controlled via ethernet with game controller

Post by Bennachie »

Hi Rasmus,

I think you are over complicating things by using Ethernet. My suggestion would be two Arduinos connected via two Maxim MAX488 chips. One Arduino to read the joystick or gamepad (there are various libraries already written to help you with this) and the other arduino to control the motor controllers. Communications between the Arduinos can easily be done using the EasyTransfer library. The MAX488 chips allow the data to be transferred up to 1km via 4 wires if you use full duplex communications, or 2 wires if you use half duplex, which is more complex, so stick to 4 wire comms if you can.

I don't have time right now but I have circuit diagrams and code that I can share with you if you want. I don't want to post the code directly on the forum yet as it is unfinished.
Scanro
Posts: 22
Joined: Aug 15th, 2019, 4:30 am

Re: 6 motors controlled via ethernet with game controller

Post by Scanro »

Bennachie wrote:Hi Rasmus,

I think you are over complicating things by using Ethernet. My suggestion would be two Arduinos connected via two Maxim MAX488 chips. One Arduino to read the joystick or gamepad (there are various libraries already written to help you with this) and the other arduino to control the motor controllers. Communications between the Arduinos can easily be done using the EasyTransfer library. The MAX488 chips allow the data to be transferred up to 1km via 4 wires if you use full duplex communications, or 2 wires if you use half duplex, which is more complex, so stick to 4 wire comms if you can.

I don't have time right now but I have circuit diagrams and code that I can share with you if you want. I don't want to post the code directly on the forum yet as it is unfinished.
Hi, I really appreciate the input and I think that you are correct that I am over complicating it. I would love if you shared the code and diagrams with me, many thanks :)
I see that there are a few different model numbers when it comes to the MAX488. like MAX488ESA+, MAX477EPA+ and so on, do you happen to know if it matters which model I get? again thanks for the help :)

Rasmus
User avatar
Bennachie
Posts: 113
Joined: Jul 6th, 2018, 11:38 am

Re: 6 motors controlled via ethernet with game controller

Post by Bennachie »

You want the MAX488CPA which is the 8 pin DIP packaged version. The MAX488EPA would also do, but might be slightly more expensive as it has a larger operating temperature range.

The 8 pin DIP is the version which you can plug in to a standard chip socket with 0.1inch pin spacing for use on standard prototyping board.

They are readily available from RS Components (Radio Shack in the USA) or such like.
Scanro
Posts: 22
Joined: Aug 15th, 2019, 4:30 am

Re: 6 motors controlled via ethernet with game controller

Post by Scanro »

Bennachie wrote:You want the MAX488CPA which is the 8 pin DIP packaged version. The MAX488EPA would also do, but might be slightly more expensive as it has a larger operating temperature range.

The 8 pin DIP is the version which you can plug in to a standard chip socket with 0.1inch pin spacing for use on standard prototyping board.

They are readily available from RS Components (Radio Shack in the USA) or such like.
Thanks again, now I just need to figure out the code :)
User avatar
Bennachie
Posts: 113
Joined: Jul 6th, 2018, 11:38 am

Re: 6 motors controlled via ethernet with game controller

Post by Bennachie »

I'm away from home right now, but give me a few days and I'll give you something to get started with.
Scanro
Posts: 22
Joined: Aug 15th, 2019, 4:30 am

Re: 6 motors controlled via ethernet with game controller

Post by Scanro »

Bennachie wrote:I'm away from home right now, but give me a few days and I'll give you something to get started with.
Sorry for the late reply, life came in the way. While I wait I will also try and look into it :)
User avatar
Bennachie
Posts: 113
Joined: Jul 6th, 2018, 11:38 am

Re: 6 motors controlled via ethernet with game controller

Post by Bennachie »

Firstly, here's the circuit diagram that I am working to. It may help you to understand the following code.

Image
User avatar
Bennachie
Posts: 113
Joined: Jul 6th, 2018, 11:38 am

Re: 6 motors controlled via ethernet with game controller

Post by Bennachie »

Now the code for the topside controller.

This is incomplete, but it works. You will have to modify it to read your game controller instead of my analogue joystick. You may find the Arduino Joystick library useful for that.

You'll also have to install the EasyTransfer library, and the NewLiquidCrystal library by fmalpardita if you plan to use I2C liquid crystal displays.

Code: Select all

// ///////////////////// //
//                       //
//      ROV Console      //
//  Bennachie  20/08/19  //
//                       //
// ///////////////////// //

// This code was designed for the fmalpardita library (NewLiquidCrystal)

#include <EasyTransfer.h>
#include <Wire.h>
#define baudRate 115200    // Serial port baud rate
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR 0x27 //Address for LCD display. Usually is 0x27 or 0x32.

LiquidCrystal_I2C lcd(I2C_ADDR,2,1,0,4,5,6,7); //Set up library to talk to LCD.

//Create two objects for EasyTransfer.
EasyTransfer ETin, ETout; 


struct RECEIVE_DATA_STRUCTURE{
  //Put your variable definitions here for the data you want to RECEIVE
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  int16_t heading;
  int16_t depth;
  int16_t battery;

};

struct SEND_DATA_STRUCTURE{
  //Put your variable definitions here for the data you want to SEND
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  int16_t portvert;
  int16_t stbdvert;
  int16_t portfwd;
  int16_t stbdfwd;
  int16_t portaft;
  int16_t stbdaft;
  int16_t lights;
  int16_t pan;
  int16_t tilt;
};

//Give a name to the group of data
RECEIVE_DATA_STRUCTURE rxdata;
SEND_DATA_STRUCTURE txdata;

//Declare global variables
int slider = 511;
int xaxis = 511;
int yaxis = 511;
int zaxis = 511;
int portfwd1;
int stbdfwd1;
int portaft1;
int stbdaft1;
int16_t portvert;
int16_t stbdvert;
int16_t portfwd;
int16_t stbdfwd;
int16_t portaft;
int16_t stbdaft;
int16_t lights = 0;
int16_t pan = 89;
int16_t tilt = 89;

int lowval = 44;
int midval = 89;
int highval = 179;
float ptspeed = 2;

void setup(){
  Serial.begin(115200);
  //start the EasyTransfer library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc.
  ETin.begin(details(rxdata), &Serial);
  ETout.begin(details(txdata), &Serial);

  //Set up pins to read buttons on joystick
  pinMode(A4,INPUT_PULLUP);
  pinMode(A5,INPUT_PULLUP);
  pinMode(A6,INPUT_PULLUP);
  pinMode(A7,INPUT_PULLUP);
  pinMode(A8,INPUT_PULLUP);
  pinMode(A9,INPUT_PULLUP);
  pinMode(A10,INPUT_PULLUP);
  pinMode(A11,INPUT_PULLUP);
  pinMode(A12,INPUT_PULLUP);
  pinMode(A13,INPUT_PULLUP);
  pinMode(A14,INPUT_PULLUP);

  //Set pin for on board indicator LED
  pinMode(13, OUTPUT);  

  //Initialise the LCD
  lcd.begin (16, 2);
  lcd.setBacklightPin(3,POSITIVE);
  //lcd.setBacklight(HIGH); // NOTE: You can turn the backlight off by setting it to LOW instead of HIGH
  lcd.setBacklight(LOW);
  //Print a splash screen on the LCD
  lcd.clear(); // clean screen and sets cursor to (0,0)
  Serial.begin(baudRate);
  lcd.setBacklight(HIGH); //turn on backlight
  
  lcd.setCursor(0, 0); //First line
  lcd.print("Console_1");
  lcd.setCursor(0, 1); //Second line
  lcd.print("Console 19/07/19");
  delay(2000);
  lcd.clear();
  
}

void loop(){

//Read values from the joystick axes
slider = analogRead(A3);
xaxis = analogRead(A0);
yaxis = analogRead(A1);
zaxis = analogRead(A2);

//Scale the verts
portvert = (map(slider, 0, 1023, 0, 179));
stbdvert = (map(slider, 0, 1023, 0, 179));

//send vert values out to EasyTransfer
txdata.portvert = portvert;
txdata.stbdvert = stbdvert;

//Calculate value for portaft
portaft1 = (((zaxis) - (xaxis) - (yaxis)) + 1023);
if (portaft1 >= 1023){
  portaft1 = 1023;
}
if (portaft1 <= 0){
  portaft1 = 0;
}

//Calculate value for stbdaft
stbdaft1 = (((xaxis) - (yaxis) - (zaxis)) + 1023);
if (stbdaft1 >= 1023){
  stbdaft1 = 1023;
}
if (stbdaft1 <= 0 ){
  stbdaft1 = 0;
}

//Calculate value for portfwd
portfwd1 = (((xaxis) - (yaxis) + (zaxis)));
if (portfwd1 >= 1023){
  portfwd1 = 1023;
}
if (portfwd1 <= 0){
  portfwd1 = 0;
}

//Calculate value for stbdfwd
stbdfwd1 = (2048 - ((xaxis) + (yaxis) + (zaxis)));
if (stbdfwd1 >= 1023){
  stbdfwd1 = 1023;      
}
if (stbdfwd1 <= 0){
  stbdfwd1 = 0;
}

//Scale vectored thrusters according to power setting switch
//First, scale to MID
portaft = map(portaft1, 0, 1023, 44, 133);
stbdaft = map(stbdaft1, 0, 1023, 44, 133);
portfwd = map(portfwd1, 0, 1023, 44, 133);
stbdfwd = map(stbdfwd1, 0, 1023, 44, 133);

//If HIGH switch on, scale to HIGH
if (digitalRead(A9) == LOW) {
  portaft = map(portaft1, 0, 1023, 0, 179);
  stbdaft = map(stbdaft1, 0, 1023, 0, 179);
  portfwd = map(portfwd1, 0, 1023, 0, 179);
  stbdfwd = map(stbdfwd1, 0, 1023, 0, 179);
}

//If LOW switch on, scale to LOW
if (digitalRead(A8) == LOW) {
  portaft = map(portaft1, 0, 1023, 66, 112);
  stbdaft = map(stbdaft1, 0, 1023, 66, 112);
  portfwd = map(portfwd1, 0, 1023, 66, 112);
  stbdfwd = map(stbdfwd1, 0, 1023, 66, 112);
}

//Send vectored values out to EasyTransfer
txdata.portaft = portaft;
txdata.stbdaft = stbdaft;
txdata.portfwd = portfwd;
txdata.stbdfwd = stbdfwd;

//Set pan & tilt values, speed controlled by ptspeed value
if(digitalRead(A4) == LOW) {
  pan = pan - ptspeed;
}

if(pan <=0){
  pan = 0;
}

if(digitalRead(A5) == LOW) {
  pan = pan + ptspeed;
}

if(pan >=179){
  pan = 179;
}

if(digitalRead(A6) == LOW) {
  tilt = tilt - ptspeed;
}

if(tilt <=0){
  tilt = 0;
}

if(digitalRead(A7) == LOW) {
  tilt = tilt + ptspeed;
}

if(tilt >=179){
  tilt = 179;
}

//Centre P&T if trigger pulled
if(digitalRead(A13) == LOW) {
  pan = 89;
  tilt = 89;
}

  txdata.pan = pan;
  txdata.tilt = tilt;

//Send pan & tilt values to EasyTransfer
txdata.pan = pan;
txdata.tilt = tilt;

  //Print sensor values to LCD
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("H:");
  lcd.setCursor(2,0);
  lcd.print(rxdata.heading);
  lcd.setCursor(0,1);
  lcd.print("D:");
  lcd.setCursor(2,1);
  lcd.print(rxdata.depth);
  lcd.setCursor(8,0);
  lcd.print("B:");
  lcd.setCursor(10,0);
  lcd.print(rxdata.battery);

  //Read lights pin and send the value to EasyTransfer
  if(!digitalRead(A10))
    txdata.lights = HIGH;

  else
    txdata.lights = LOW;

  //Read lights pin and set on board LED accordingly
  if(!digitalRead(A10))
    digitalWrite(13, HIGH);
    
  else
    digitalWrite(13, LOW);
    
  //Send all the EasyTransfer data out of the port
  ETout.sendData();
  
 //There's a loop here so that we run the receive function more often then the 
 //transmit function. This is important due to the slight differences in 
 //the clock speed of different Arduinos. If we didn't do this, messages 
 //would build up in the buffer and appear to cause a delay.
  for(int i=0; i<5; i++){
    //remember, you could use an if() here to check for new data, this time it's not needed.
    ETin.receiveData();
    
    //delay
    delay(10);
  }
  
  //delay to slow loop
  //delay(10);

}
User avatar
Bennachie
Posts: 113
Joined: Jul 6th, 2018, 11:38 am

Re: 6 motors controlled via ethernet with game controller

Post by Bennachie »

Finally, the ROV vehicle code.

This is also incomplete but working.

If you use the hardware serial ports, as I am, you'll need to power both Arduinos up BEFORE connecting them together, otherwise the port sees the data from the other Arduino and thinks it's a PC trying to communicate with it and it will hang. That's why one of my fibre modules has a separate power switch to allow it to be turned on after the rest of the vehicle. I believe the other (software) serial ports don't have this problem, but they use processing power. I may try switching to them to see if it makes life simpler.

Enjoy!

Code: Select all

// ///////////////////// //
//                       //
//      ROV Vehicle      //
//  Bennachie  20/08/19  //
//                       //
// ///////////////////// //

// This code was designed for the fmalpardita library (NewLiquidCrystal)

#include <Servo.h>
#include <EasyTransfer.h>
#include <Wire.h>
#define baudRate 115200    // Serial port baud rate
#include <LiquidCrystal_I2C.h>

#define I2C_ADDR 0x27 //Address for LCD display. Usually is 0x27 or 0x32.

LiquidCrystal_I2C lcd(I2C_ADDR,2,1,0,4,5,6,7);


//Create two objects for EasyTransfer.
EasyTransfer ETin, ETout; 

//create servos
Servo pans;
Servo tilts;
Servo stbdfwds;
Servo stbdverts;
Servo stbdafts;
Servo portfwds;
Servo portverts;
Servo portafts;

struct RECEIVE_DATA_STRUCTURE{
  //Put your variable definitions here for the data you want to receive
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  int16_t portvert;
  int16_t stbdvert;
  int16_t portfwd;
  int16_t stbdfwd;
  int16_t portaft;
  int16_t stbdaft;
  int16_t lights;
  int16_t pan;
  int16_t tilt;
};

struct SEND_DATA_STRUCTURE{
  //Put your variable definitions here for the data you want to send
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  int16_t heading;
  int16_t depth;
  int16_t battery;
};


//Give a name to the group of data
RECEIVE_DATA_STRUCTURE rxdata;
SEND_DATA_STRUCTURE txdata;

//Declare global variables
int batt = 0;

void setup(){
  Serial.begin(115200);
  //Start the library, pass in the data details and the name of the serial port. Can be Serial, Serial1, Serial2, etc.
  ETin.begin(details(rxdata), &Serial);
  ETout.begin(details(txdata), &Serial);

  //Set up pins
  pinMode(13, OUTPUT);  
  pinMode(10, OUTPUT);
  pinMode(A15, INPUT);
  //enable pull-up
  //pinMode(12, INPUT_PULLUP);

  //Attach servos & thrusters to pins
  pans.attach(2);
  tilts.attach(3);
  portverts.attach(4);
  stbdverts.attach(5);
  portafts.attach(6);
  stbdafts.attach(7);
  portfwds.attach(8);
  stbdfwds.attach(9);

  //Set servos & thrusters to centre (off) position
  pans.write(89);
  tilts.write(89);
  portverts.write(89);
  stbdverts.write(89);
  portafts.write(89);
  stbdafts.write(89);
  portfwds.write(89);
  stbdfwds.write(89);

  //Initialise the LCD
  lcd.begin (16, 2);
  lcd.setBacklightPin(3,POSITIVE);
  //lcd.setBacklight(HIGH); // NOTE: You can turn the backlight off by setting it to LOW instead of HIGH
  lcd.setBacklight(LOW);
  //Print a splash screen on the LCD
  lcd.clear(); // clean screen and sets cursor to (0,0)
  Serial.begin(baudRate);
  lcd.setBacklight(HIGH); //turn on backlight
  
  lcd.setCursor(0, 0); //First line
  lcd.print("ROV_1");
  lcd.setCursor(0, 1); //Second line
  lcd.print("ROV 19/07/19");
  delay(2000);
  lcd.clear();
  
}

void loop(){

  //Read battery voltage
  batt = analogRead(A15);
  //Map voltage to percentage and send to EasyTransfer
  txdata.battery = map(batt,764, 864, 0, 100);
  //Placeholders for heading & depth values (to be added)
  txdata.heading = 0;
  txdata.depth = 0;

  //Print input values from Console on to LCD to aid debugging
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print(rxdata.portfwd);
  lcd.setCursor(0,1);
  lcd.print(rxdata.portaft);
  lcd.setCursor(4,0);
  lcd.print(rxdata.stbdfwd);
  lcd.setCursor(4,1);
  lcd.print(rxdata.stbdaft);
  lcd.setCursor(8,0);
  lcd.print(rxdata.pan);
  lcd.setCursor(8,1);
  lcd.print(rxdata.tilt);
  lcd.setCursor(12,0);
  lcd.print(rxdata.portvert);
  lcd.setCursor(12,1);
  lcd.print(rxdata.lights);
  
  //Send all the EasyTransfer data out of the port
  ETout.sendData();
  
 //There's a loop here so that we run the receive function more often then the 
 //transmit function. This is important due to the slight differences in 
 //the clock speed of different Arduinos. If we didn't do this, messages 
 //would build up in the buffer and appear to cause a delay.
 
  for(int i=0; i<5; i++){
    //remember, you could use an if() here to check for new data, this time it's not needed.
    ETin.receiveData();

    //Turn lights on or off
    digitalWrite(13, rxdata.lights);
    //This needs work. The lights toggle when given a short input pulse of approx 200ms.
    digitalWrite(10, rxdata.lights);
    
    //Send values to servos & thrusters
    pans.write(rxdata.pan);
    tilts.write(rxdata.tilt);
    portverts.write(rxdata.portvert);
    stbdverts.write(rxdata.stbdvert);
    portafts.write(rxdata.portaft);
    stbdafts.write(rxdata.stbdaft);
    portfwds.write(rxdata.portfwd);
    stbdfwds.write(rxdata.stbdfwd);
    
    //delay
    delay(10);
  }
  
  //delay to slow loop
  //delay(10);

}
Post Reply