2 Analog Potentiometer Joystick + 1 Arduino + 4 T100 Thrusters - Programming

Hi Blue Robotics Engineers,

i have a team of students that have ZERO programming experience and we desperately need help programming our ROV. We are using an Arduino Uno, 4 T100 Thrusters, and 2 analog joystick Potentiometer. our design is to have one joy stick be responsible for horizontal motion and have the other joystick be responsible for vertical motion. the motor placement is just like the QGC frametype with side-by-side vertical thrusters. we checked out the example code on one of the other posts that gave the code for 3 motors but we had 0 clue on how to reprogram that to 4 motors so we are hoping if you guys can help us out.

sincere regards,
Mike

PS im using one of my student’s account

2 Likes

Hi Mike,

I assume you are referring to this example code.

First, what kind of control are you looking for in your ROV? The simple 3-thruster ROVs targeted by that code are capable of forward/reverse, up/down, and yaw control. 4-thruster ROVs with two side-by-side vertical thrusters as shown in QGC are capable of all three as well as roll control, although in our experience roll is not nearly as important as the other three controls. Are you looking to directly adapt the code to behave the same on your 4-thruster ROV as it would on a 3-thruster ROV (i.e. forward/reverse, up/down, and yaw), or do you wish to also add roll control?

If you just want to add the fourth thruster without adding roll control, modifying the example code is pretty straightforward. You just need the second vertical thruster to act exactly the same as the first one. Add a new servo object for your fourth thruster (for example thrusterVertical2), define which pin it should be on (say, pin 12), attach the servo to the pin, and then duplicate every line concerning thrusterVertical for thrusterVertical2 (i.e. thrusterVertical2.writeMicroseconds(...);), and the two thrusters should do the same thing.

As a side note, you don’t actually need to modify the code to do this: the same PWM output can be used for both vertical thrusters. Just split the signal coming off of pin 11 and send it to both of your vertical thrusters’ ESCs.

If you want to add roll control, you will first need to duplicate all the lines concerning the joystick inputs (lines 42~50, 59~61) for your fourth joystick axis, then duplicate the lines that convert the raw joystick values to input commands (92~106) to calculate the rollCommand from your fourth joystick input. Like the above case, you will need to create a new servo object as well (I recommend differentiating them as thrusterVerticalLeft and thrusterVerticalRight). Much like the left/right thruster controls, you will need to combine the vertical and roll control inputs:

thrusterVerticalLeft.writeMicroseconds(CENTER_THROTTLE+verticalCommand+rollCommand);
thrusterVerticalRight.writeMicroseconds(CENTER_THROTTLE+verticalCommand-rollCommand);

The signs of the rollCommand terms don’t really matter, so long as one is positive and one is negative. If the ROV rolls the wrong way, just swap the signs.

1 Like

I am looking to directly adapt the code to behave the same on my 4-thruster ROV as it would on a 3-thruster ROV (i.e. forward/reverse , up/down , and yaw

how would you change

  int verticalCommand   = map(analogRead(JS_ADC_2), // Read raw joystick value
                              JS_CENTER_2-JS_DIR_2*JS_RANGE_2, // Joystick low value
                              JS_CENTER_2+JS_DIR_2*JS_RANGE_2, // Joystick high value
                              -MAX_VERTICAL_THROTTLE, // Command low value
                              MAX_VERTICAL_THROTTLE); // Command high value

so that it would also include the fourth motor. which one do i change

@dheideman could you maybe check the code once im done. i’m really new to this

@dheideman should i change anything inside the parentheses or should i leave that alone?

btw here is the code

 #include <Servo.h>

// Joystick Settings
static const int JS_CENTER_0 = 511; // Analog reading at center, 0-1023
static const int JS_CENTER_1 = 511;
static const int JS_CENTER_2 = 511;
static const int JS_RANGE_0 = 128; // Analog range, 0-1023
static const int JS_RANGE_1 = 128; // Set to 128 for Parallax joystick
static const int JS_RANGE_2 = 128;
static const int JS_DIR_0 = 1; // +1 or -1
static const int JS_DIR_1 = 1;
static const int JS_DIR_2 = 1;

// ESC/Thruster Settings
static const int MAX_FWD_REV_THROTTLE = 400; // Value between 0-400
static const int MAX_TURN_THROTTLE = 400; // Value between 0-400
static const int MAX_VERTICAL_THROTTLE = 400; // Value between 0-400
static const int CENTER_THROTTLE = 1500;

// Arduino Pins
static const byte JS_ADC_0 = A0;
static const byte JS_ADC_1 = A1;
static const byte JS_ADC_2 = A2;
static const byte THRUSTER_LEFT = 9;
static const byte THRUSTER_RIGHT = 10;
static const byte THRUSTER_VERTICAL = 11;
static const byte THRUSTER_VERTICAL2 = 12      // THIS IS ADDED 

// Servos
Servo thrusterLeft;
Servo thrusterRight;
Servo thrusterVertical;
Servo thrusterVertical2;  // THIS IS ADDED 

void setup() {
  // Set up serial port to print inputs and outputs
  Serial.begin(38400);

  // Set up Arduino pins to send servo signals to ESCs
  thrusterLeft.attach(THRUSTER_LEFT);
  thrusterRight.attach(THRUSTER_RIGHT);
  thrusterVertical.attach(THRUSTER_VERTICAL);
  thrusterVertical2.attach(THRUSTER_VERTICAL2);   // THIS IS ADDED 

  // Set output signal to 1500 microsecond pulse (stopped command)
  thrusterLeft.writeMicroseconds(CENTER_THROTTLE);
  thrusterRight.writeMicroseconds(CENTER_THROTTLE);
  thrusterVertical.writeMicroseconds(CENTER_THROTTLE);
  thrusterVertical2.writeMicroseconds(CENTER_THROTTLE);
  // Delay to allow time for ESCs to initialize
  delay(7000); 
}

void loop() {
  // Read the joysticks and use the Arduino "map" function to map the raw values
  // to the desired output commands.
  int forwardCommand    = map(analogRead(JS_ADC_0), // Read raw joystick value
                              JS_CENTER_0-JS_DIR_0*JS_RANGE_0, // Joystick low value
                              JS_CENTER_0+JS_DIR_0*JS_RANGE_0, // Joystick high value
                              -MAX_FWD_REV_THROTTLE, // Command low value
                              MAX_FWD_REV_THROTTLE); // Command high value
  int turnCommand       = map(analogRead(JS_ADC_1), // Read raw joystick value
                              JS_CENTER_1-JS_DIR_1*JS_RANGE_1, // Joystick low value
                              JS_CENTER_1+JS_DIR_1*JS_RANGE_1, // Joystick high value
                              -MAX_TURN_THROTTLE, // Command low value
                              MAX_TURN_THROTTLE); // Command high value
  int verticalCommand   = map(analogRead(JS_ADC_2), // Read raw joystick value
                              JS_CENTER_2-JS_DIR_2*JS_RANGE_2, // Joystick low value
                              JS_CENTER_2+JS_DIR_2*JS_RANGE_2, // Joystick high value
                              -MAX_VERTICAL_THROTTLE, // Command low value
                              MAX_VERTICAL_THROTTLE); // Command high value

  // Combine the "stopped" command with forward, turn, and vertical and send 
  // to the ESCs.
  thrusterLeft.writeMicroseconds(CENTER_THROTTLE+forwardCommand+turnCommand);
  thrusterRight.writeMicroseconds(CENTER_THROTTLE+forwardCommand-turnCommand);
  thrusterVertical.writeMicroseconds(CENTER_THROTTLE+verticalCommand);
 thrusterVertical2.writeMicroseconds(CENTER_THROTTLE+verticalCommand); // THIS IS ADDED 

  // Output via serial
  Serial.print("Fwd: "); Serial.print(forwardCommand);
  Serial.print("Turn: "); Serial.print(turnCommand);
  Serial.print("Vert: "); Serial.print(verticalCommand);
  Serial.println("");

  // Delay 1/10th of a second. No need to update at super fast rates.
  delay(100);
}

verticalCommand doesn’t refer to the vertical motor(s), but rather is the control input converted to a more-easily understood value inside the code.

analogRead(JS_ADC_2) reads the voltage on pin JS_ADC_2 (pin A2), which corresponds to the position of joystick #2. Then map(...) converts this voltage value, stored as a number between 0 and 1023, to a throttle value that can be used later in the code (namely for thrusterVertical.writeMicroseconds(CENTER_THROTTLE+verticalCommand);).

The “Servos” (i.e. Servo thrusterVertical;) refer to the ESCs used to control the thrusters. The control signals sent out by the Arduino to servos and ESCs are identical, so we use the Servo code already in the Arduino libraries to control the ESCs/thrusters.

Thus each ESC/thruster requires its own “Servo” in the code. This is why you needed to add a fourth “Servo”, i.e. Servo thrusterVertical2, to the code just below the line Servo thrusterVertical.

@jjabraham Have you tested the code? It looks like it should work. As mentioned in my previous post, you shouldn’t need to touch anything concerning verticalCommand, so it looks like everything is fine.

However you are missing a semicolon on the line static const byte THRUSTER_VERTICAL2 = 12.

i can’t test it on the ROV Because i left my school and i dont have access to it anymore but im sure i can access it later in the week. just as a quick clarification, should i not change anything in the parentheses of anything in the code

For driving the four thrusters the way you described, you should not change anything in the parentheses of anything currently in the code.

static const byte THRUSTER_VERTICAL = 11;
static const byte THRUSTER_VERTICAL2 = 12; //ADDED CODE

thrusterVertical.attach(THRUSTER_VERTICAL);
thrusterVertical2.attach(THRUSTER_VERTICAL2); //ADDED CODE

thrusterVertical.writeMicroseconds(CENTER_THROTTLE);
thrusterVertical2.writeMicroseconds(CENTER_THROTTLE); //ADDED CODE

thrusterVertical.writeMicroseconds(CENTER_THROTTLE+verticalCommand);
thrusterVertical2.writeMicroseconds(CENTER_THROTTLE+verticalCommand); //ADDED CODE

@dheideman those were all the changes i made. i made these changes on line 64, 78, 83, and 112

does it look right to you?

It looks right to me.

THANK YOU SO MUCH!!! for all your help and patience. once i get back to school, i’ll test it out and get back to you :grinning::grinning::grinning::grinning::grinning:

Hello, I have been working with @jjabraham on this for a while, but there are some issues when we go to run the code. The wiring is done the same way, and everything is in the same ports indicated. However, when we turn power on, the motors beep three times to say they turned on, then they beep twice and do nothing. Then, when moving the joysticks around, the vertical motors start spinning, and one of the joysticks makes the vertical go faster. However, the vertical motors do not even turn off, and the horizontal motors do not turn on at all. Instead, they just continuously beep twice. I’m not sure what I’m doing wrong, but I would appreciate all the help I can get!
Thank you so much!

1 Like

Hello,

What ESCs are you using? You should be using ours or other bl-heli ESC set up for bi-directional movement.

we’re using the Blue Robotics ESCs

Can you share your whole code with pastebin.com?

1 Like
#include <Servo.h>

// Joystick Settings
static const int JS_CENTER_0 = 511; // Analog reading at center, 0-1023
static const int JS_CENTER_1 = 511;
static const int JS_CENTER_2 = 511;
static const int JS_RANGE_0 = 128; // Analog range, 0-1023
static const int JS_RANGE_1 = 128; // Set to 128 for Parallax joystick
static const int JS_RANGE_2 = 128;
static const int JS_DIR_0 = 1; // +1 or -1
static const int JS_DIR_1 = 1;
static const int JS_DIR_2 = 1;

// ESC/Thruster Settings
static const int MAX_FWD_REV_THROTTLE = 400; // Value between 0-400
static const int MAX_TURN_THROTTLE = 400; // Value between 0-400
static const int MAX_VERTICAL_THROTTLE = 400; // Value between 0-400
static const int CENTER_THROTTLE = 1500;

// Arduino Pins
static const byte JS_ADC_0 = A0;
static const byte JS_ADC_1 = A1;
static const byte JS_ADC_2 = A2;
static const byte THRUSTER_LEFT = 9;
static const byte THRUSTER_RIGHT = 10;
static const byte THRUSTER_VERTICAL = 11;
static const byte THRUSTER_VERTICAL2 = 12;                                                                          //ADDED CODE
// Servos
Servo thrusterLeft;
Servo thrusterRight;
Servo thrusterVertical;

void setup() {
  // Set up serial port to print inputs and outputs
  Serial.begin(38400);

  // Set up Arduino pins to send servo signals to ESCs
  thrusterLeft.attach(THRUSTER_LEFT);
  thrusterRight.attach(THRUSTER_RIGHT);
  thrusterVertical.attach(THRUSTER_VERTICAL);
  thrusterVertical2.attach(THRUSTER_VERTICAL2);                                            //ADDED CODE
  // Set output signal to 1500 microsecond pulse (stopped command)
  thrusterLeft.writeMicroseconds(CENTER_THROTTLE);
  thrusterRight.writeMicroseconds(CENTER_THROTTLE);
  thrusterVertical.writeMicroseconds(CENTER_THROTTLE);
  thrusterVertical2.writeMicroseconds(CENTER_THROTTLE);                                                 //ADDED CODE
  // Delay to allow time for ESCs to initialize
  delay(7000); 
}

void loop() {
  // Read the joysticks and use the Arduino "map" function to map the raw values
  // to the desired output commands.
  int forwardCommand    = map(analogRead(JS_ADC_0), // Read raw joystick value
                              JS_CENTER_0-JS_DIR_0*JS_RANGE_0, // Joystick low value
                              JS_CENTER_0+JS_DIR_0*JS_RANGE_0, // Joystick high value
                              -MAX_FWD_REV_THROTTLE, // Command low value
                              MAX_FWD_REV_THROTTLE); // Command high value
  int turnCommand       = map(analogRead(JS_ADC_1), // Read raw joystick value
                              JS_CENTER_1-JS_DIR_1*JS_RANGE_1, // Joystick low value
                              JS_CENTER_1+JS_DIR_1*JS_RANGE_1, // Joystick high value
                              -MAX_TURN_THROTTLE, // Command low value
                              MAX_TURN_THROTTLE); // Command high value
  int verticalCommand   = map(analogRead(JS_ADC_2), // Read raw joystick value
                              JS_CENTER_2-JS_DIR_2*JS_RANGE_2, // Joystick low value
                              JS_CENTER_2+JS_DIR_2*JS_RANGE_2, // Joystick high value
                              -MAX_VERTICAL_THROTTLE, // Command low value
                              MAX_VERTICAL_THROTTLE); // Command high value

  // Combine the "stopped" command with forward, turn, and vertical and send 
  // to the ESCs.
  thrusterLeft.writeMicroseconds(CENTER_THROTTLE+forwardCommand+turnCommand);
  thrusterRight.writeMicroseconds(CENTER_THROTTLE+forwardCommand-turnCommand);
  thrusterVertical.writeMicroseconds(CENTER_THROTTLE+verticalCommand);
  thrusterVertical2.writeMicroseconds(CENTER_THROTTLE+verticalCommand);                                                                 //ADDED CODE
  // Output via serial
  Serial.print("Fwd: "); Serial.print(forwardCommand);
  Serial.print("Turn: "); Serial.print(turnCommand);
  Serial.print("Vert: "); Serial.print(verticalCommand);
  Serial.println("");

  // Delay 1/10th of a second. No need to update at super fast rates.
  delay(100);
}

we also used the 3motor code on Github and we experienced similar problems