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'