The Arduino sketch in support of Roy Cheers ‘An Introduction to Arduino’ article as published in the May 2020 Edition of Model Boats Magazine. (See Page 59)
// Demonstration of propulsion & thruster control
// SECTION 1 ...............................................................................
#include (NB this line should have Servo.h7 added between less than and greater than sign characters which do not reproduce on the text editor used on this document.)
// Assign names and pin numbers for input from radio receiver
#define Speed_In_Pin 4 // Channel 3 throttle
#define Rudder_In_Pin 2 // Channel 1, rudder and stern thruster
#define Ch2_In_Pin 3 // Channel 2, bow thruster
Servo port_ESC; //create a servo object to control the port ESC
Servo stbd_ESC; //create a servo ojbect to contorl the stbd ESC
Servo Steer; //create a servo object to move rudder
// Define pulse durations corresponding to neutral positions
const int SpeedNeutral = 1500; // Value in mu of Neutral/Zero speed signal from receiver
const int RudderNeutral = 1500;
const int Ch2Neutral = 1500;
const int BowThrustDirPin = 11; //Define pins for outputs to devices
const int SternThrustDirPin = 12;
const int ThrusterPwrPin = 13;
const int ThrustDisable = 200; // Plus/minus change from throttle neutral over which thrusters
// are enabled.e.g if = 200, throttle signal must be between
// 1300 and 1700 for thrusters to be enabled, if neutral = 1500.
// Confirm and adjust as necessary.
const int RudderLimit = 300; //Change of rudder input from neutral below which thrusters are off
//i.e. only turned on when a tight turn is signalled
//change value to suit
boolean ThrPwrEnabled; //Is TRUE if thruster operation allowed.
boolean ThrusterPwrOn; //Is TRUE if thruster power is actually on
boolean DirnChg; //TRUE if a change in direction of the thrusters is to be made
String BowPush, SternPush; //Is "Right" if model to be pushed to the right, othewise "left"
int Mvrg_Mode; //Manoeuvring mode: 0 normal, 1 side thrust, 2 rotate
int SpeedInput, RudderInput, Ch2Input; // Values read from rc receiver
int SpdMove; //Amount speed signal has changed from neutral
int R, S; //temporary variables
// SECTION 2 ........................................................................
void setup()
{
Serial.begin(9600); //This is required for any test values you wish to display.
pinMode(BowThrustDirPin, OUTPUT); //Define pins being used for output.
pinMode(SternThrustDirPin, OUTPUT);
pinMode(ThrusterPwrPin, OUTPUT);
digitalWrite(BowThrustDirPin, LOW); //Set relays unpowered
digitalWrite(SternThrustDirPin, LOW);
digitalWrite(ThrusterPwrPin, LOW); // Set thrusters off
ThrPwrEnabled = false; //Set conditions off for thruster power
// attach servo objects to their signal pins, these will generate the correct
// pulses for driving Electronic speed controllers, & servos
// designed to interface directly with RC Receivers
port_ESC.attach(9, 1000, 2000); //attach these pins for output to ESC's
stbd_ESC.attach(10, 1000, 2000);
Steer.attach(6, 1000, 2200); //attach this pin for output to rudder servo
} // End of 'setup'.
//SECTION 3..................................................................................
void loop()
{
// Read signals from RC receiver; start timing pulse when voltage goes high
SpeedInput = pulseIn(Speed_In_Pin, HIGH);
RudderInput = pulseIn(Rudder_In_Pin, HIGH);
Ch2Input = pulseIn(Ch2_In_Pin, HIGH);
//If you want to verify the pulse durations that your receiver is sending to Arduino,
//remove the comment slashes from the code below and, with your Arduino connected to your
//computer, and your Arduino sketch open, click on 'Tools -> Serial Monitor'.
//Do not touch the control sticks on your radio and observe the readings. Ajust your trim tabs
//if you want to change the values you see.
//Then move the sticks to confirm the direction that the pulses increase.
//Remove the comment slashes from the following seven lines.
//Serial.println("Throttle Channel 2 Rudder");
//Serial.print(SpeedInput);
//Serial.print(" ");
//Serial.print(Ch2Input);
//Serial.print(" ");
//Serial.println(RudderInput);
//delay(500); //slow down execution to give time to read
//If any channel signals increase in the wrong direction, use whichever of the following statements
// apply to reverse them, by removing the '//'
// SpeedInput = 2 * SpeedNeutral - SpeedInput;
// RudderInput = 2 * RudderNeutral - SpeedInput;
// Ch2Input = 2 * Ch2Neutral - Ch2Input;
//If there is no throttle signal, or an out-of-range signal, set zero speed
if ((SpeedInput < 900) or (SpeedInput > 2100))
{
SpeedInput = SpeedNeutral;
}
// Determine manoeuvring mode; assume normal control then check to see if stick positions
// match other modes. Also assume that received signal may vary from neutral by +/-50 mu,
// with no stick movement, due to radio interference, etc.
Mvrg_Mode = 0;
if (abs(SpeedInput - SpeedNeutral) < 50) // Throttle stick in neutral
{
if (abs(RudderInput - RudderNeutral) > 50) // Rudder stick not neutral
{
Mvrg_Mode = 1; //Side thrust
}
else if (abs(Ch2Input - Ch2Neutral) > 50) // Ch 2 stick not in neutral
{
Mvrg_Mode = 2; //Rotate
}
}
// SECTION 4...........................................................................
//Assume thruster power can be switched on; but is set off if in normal control
//and either of the following apply
// Forward/astern speed is high enough that thrusters ineffective
// Turn is not tight enough to require thruster assistance
ThrPwrEnabled = true;
SpdMove = abs(SpeedInput - SpeedNeutral);
if ( Mvrg_Mode == 0 )
{
if (SpdMove > ThrustDisable || abs(RudderInput - RudderNeutral) < RudderLimit)
{
ThrPwrEnabled = false;
}
}
if (ThrPwrEnabled)
{
if (Mvrg_Mode == 1) // Sideways movement
{
if (RudderInput > RudderNeutral) //Rudder stick to right; side thrust right
{
BowPush = "Right";
SternPush = "Right";
}
else // Rudder stick to left; side thrust left
{
BowPush = "Left";
SternPush = "Left";
}
}
else if (Mvrg_Mode == 2 ) // Rotate
{
if (Ch2Input > Ch2Neutral) //Rudder stick pushed forwards; rotate CCW
{
BowPush = "Left";
SternPush = "Right";
}
else //Rudder stick pulled back; rotate CW
{
BowPush = "Right";
SternPush = "Left";
}
}
else // Must be Mvrg_Mode 0
{
if (SpeedInput - SpeedNeutral > 0) {S = +1 ;} else { S = -1;}
if (RudderInput - RudderNeutral > 0) {R = +1;} else { R = -1;}
if (S * R > 0 ) // Ahead - Turning right; Astern - turning left
{
BowPush = "Right";
SternPush = "Left";
}
else // Ahead - turning left; astern - turning right
{
BowPush = "Left";
SternPush = "Right";
}
}
// SECTION 5 ................................................................................
DirnChg = false;
if (digitalRead(SternThrustDirPin) == LOW && SternPush == "Left") //These represent opposite directions
{
DirnChg = true;
}
else if (digitalRead(SternThrustDirPin) == HIGH && SternPush == "Right") //also opposite
{
DirnChg = true;
}
if (digitalRead(BowThrustDirPin) == LOW && BowPush == "Left") //Also opposite directions
{
DirnChg = true;
}
else if (digitalRead(BowThrustDirPin) == HIGH && BowPush == "Right") //also opposite
{
DirnChg = true;
}
if (DirnChg == true || ThrusterPwrOn == false) //If direction change reqd OR thruster power off
{
if (ThrusterPwrOn == true)
{
digitalWrite(ThrusterPwrPin, LOW); //Switch off power to thrusters
delay(10); //Pause 10ms for kickback voltage to dissipate
}
if (BowPush == "Right")
{
digitalWrite(BowThrustDirPin, LOW); //Bow Thruster pushes model right
}
else
{
digitalWrite(BowThrustDirPin, HIGH); //Bow Thruster pushes model left
}
if (SternPush == "Right")
{
digitalWrite(SternThrustDirPin, LOW); //Stern Thruster pushes model right
}
else
{
digitalWrite(SternThrustDirPin, HIGH); //Stern Thruster pushes model left
}
digitalWrite(ThrusterPwrPin, HIGH); //Switch on power to thrusters
ThrusterPwrOn = true;
}
}
else
{
digitalWrite(ThrusterPwrPin, LOW); // Thrusters off
ThrusterPwrOn = false;
}
//Removing the comment slashes from the following lines will display the information.
//You may find this useful for testing, or just to check what's going on.
//Serial.println("Man'vrg Mode Thr Pwr? Bow Stern Rudder Inp");
//Serial.print(Mvrg_Mode);
//Serial.print(" ");
//if (ThrPwrEnabled) {Serial.print("ON ");} else {Serial.print("off ");}
//Serial.print(BowPush);
//Serial.print(" ");
//Serial.print(SternPush);
//Serial.print(" ");
//Serial.println(RudderInput);
//Serial.println();
//delay(400);
if (Mvrg_Mode == 0)
{
port_ESC.writeMicroseconds(SpeedInput); //send signal to ESC's
stbd_ESC.writeMicroseconds(SpeedInput);
Steer.writeMicroseconds(RudderInput); // send signal to rudder servo
}
} //End of 'loop'