Simulated Gunfire Control System for 1/96 Scale Operational Battleship

I looked at a mobotools example. It uses a MotoServo declaration for its servo. You left the original Servo type for MyServo.

If you want to use setspeed, you need to use an object that ‘understands’ it and Servo doesn’t.

You can see that I used MotoServo instead, cleaned up some compilation errors of my own making and there you are.

Attached is a working, but still in development, Gunfire Control System that I wrote with all of your help and suggestions. I used Mobatools to control servo rotational speed and ServoInput Library(primarily for servo angle). I did not use Servo library, because I read on here that Mobatools may interfere with Servo library.

In any case, the program controls the servos as intended, but there is a fair bit of servo jitter and periodic twitch, even with just one servo connected. The jitter is not much, but when amplified by a 12" long turret and barrel, it would be very visible.

I do not have that jitter/twitch when the servo is connected directly to the receiver (i.e. no arduino). I have a separate power supply for my servos and receivers, a 100 uF Capacitor across the servo power, ferrite core wrapped around my servos. In addition, I have tried adding an if statement in move servo code that only moves the servos if there is say more than 2 degrees of movement required. That code did not help. I also tried detaching servos after a move command , but I lost my ability then to control speed with Mobatools so I commented it out of the code.

Any suggestions on how to mitigate the servo jitter? Thanks in advance.

/*     1/96 Scale Model Ship
       Gunfire Control System
       January 8, 2024

       This program controls the turret rotation of a 3 gun battleship 
       with 3 RC channels on a DX-9 Spektrum Transmitter/Receiver.
       Note:  Code for Turret #3 (backward facing turret),has not been written yet.

       There are 3 Modes of Operation, controlled on ModeSw pin<3>.
         1.  Centerline Mode:  All turrets at centerline
         2.  Target Mode:  All turrets point toward target.  Dir can
             still move independently.
         3.  Follow Mode:  All turrets move in unison.
         
       DirIn pin<2> is the gun Director input.
       Dir pin<9> is the gun Director output.
       Tur1 pin<10> is Turret 1 and 2 output.

       The Mobatools library is included to servo rotation speed.
       The ServoInput library is used for servo angle commands and servo control.
       The Servo library is not included because I read that it may interfere
       with Mobatools - I'm not sure, I'm just a beginner.

       Some of the code my is prabably inefficient or non-professional, but keep in mind
       that this is my first time programming in C++.   I greatly appreciate all
       the help I have been given and continue to receive.

       */

#include <MobaTools.h>
#include <ServoInput.h>

/* The signal pin for ServoInput MUST be an interrupt-capable pin!
       Uno, Nano, Mini (328P): 2, 3
               Mega, Mega2560: 2, 3, 18, 19, 20, 21
  
*/
ServoInputPin<2> DirIn;         // Director input on pin 2.  Rotatry knob on DX-9.
ServoInputPin<3> ModeSw;        // Mode Switch on pin 3.  3 Position switch on DX-9.
MoToServo Dir;                  // I forget what this does, but it's needed,
MoToServo Tur1;                 // I forget what this does, but it's needed.

float lastangle;                // Last angle of the Director - Floating decimal variable
float Tol;                      // Tolerance (Tol) = lastangle - angle.  Created to minimize servo hunting/jitter.
                                //   It didn't seem to mitigate servo jitter, so I commented it out in the code below.
int position;                   // Position of Mode Switch (= 1,2 or 3)

void setup() {

  Dir.attach(9);                // Attaches servo on pin 9 to servo object "Dir"
  Tur1.attach(10);              // Attaches servo on pin 10 to servo obect "Tur1"
  
  Dir.setSpeed(5);             // Set servo rotation speed of Director (5 is final speed, 50 for testing)
  Tur1.setSpeed(5);            // Set servo rotation speed of Turret 1,2 (5 is final speed, 50 for testing)

  Serial.begin(9600);           // Serial port comu speed 9600 baud
  //      Serial.begin(115200);

  lastangle = 90.0;             // Initialize last angle to centerline = 90 degrees
  Tol = 0.0;                    // Initialize Tol to 0.0
}

void loop()                     // Main loop
{
/*                              Read PWM angle on the 3 pos switch on the DX-9 and to decide what mode you desire:
                                  1.  Modeangle < 30 degrees (position 0 on switch):  Move Turrets to Centerline
                                  2.  Modeangle > 120 degrees (position 2 on switch):  Move Turrets using Follow Mode
                                  3.  All else, Modeangle (position 3 on switch):  Move Turrets using Target Mode

                                  Crude method, but it seems to work.  I was unsuccessful with many attempts to use
                                  case commands and other commands to make the switch reading an integer, etc.  
*/
  float Modeangle = ModeSw.getAngle();      // Determine angle of 3 pos switch on DX-9

   if(Modeangle < 30.0)                     
    {movecntrl();}                         // If < 30 degrees, run movecntrl (move turrets to centerline) function

   else if(Modeangle > 120.0)              // If < 120 degrees, run followmode (all turrets follow Director) function
   {followmode();}

   else                                    // If neither above, run trgtmode (Director moves independently, turret remain at last positon)
   {trgtmode();}

     
  Serial.print("Modeangle: ");             // Print to Serial Monitor for debuggimg
  Serial.println(Modeangle);
  Serial.println();

  float angle = DirIn.getAngle();          // get angle of Director input from DX-9 (0 - 180)
                                           // Processing to modify the input pulse received to the output pulse desired
  Tol = lastangle - angle;

  Serial.print(DirIn.getAngle(), ModeSw.getAngle());
  // print the angle on Serial Monitor
  Serial.println();

}                                          // End of Main Loop

void movecntrl()                           // Function to Move turrets to centeline
 {
//Dir.attach(9);                             // Reattach Dir and Tur1.  Detached them at end of this
//Tur1.attach(10);                           //  of this function to try to mitigate servo jitter.
//Dir.setSpeed(5);                           // Reset servo rotation speed of Director (5 is final speed, 50 for testing)
//Tur1.setSpeed(5);                          // RTest servo rotation speed of Turret 1,2 (5 is final speed, 50 for testing)  
  
  Dir.write(90);
  Tur1.write(90);
  

//delay(20);                                 // Delay 0.5 sec to complete write statements before
//Dir.detach();                              // Detach to try to mitigate servo jitter.
//Tur1.detach();

}

void trgtmode()                            // Function to Move turrets to Director (DirIn) position  
 {
//Dir.attach(9);                             // Reattach Dir and Tur1.  Detached them at end of this
//Tur1.attach(10);                           //  of this function to try to mitigate servo jitter.
//Dir.setSpeed(5);                           // Reset servo rotation speed of Director (5 is final speed, 50 for testing)
//Tur1.setSpeed(5);                          // RTest servo rotation speed of Turret 1,2 (5 is final speed, 50 for testing) 
  
  
  
  float angle = DirIn.getAngle();
  //    if(abs(Tol > 2.0));
  Dir.write(angle);
  lastangle = angle;

//delay(20);                                 // Delay 0.5 sec to complete write statements before
//Dir.detach();                              // Detach to try to mitigate servo jitter.
//Tur1.detach();

}

void followmode()                          // Function to have turrets follow Director
 {
//Dir.attach(9);                             // Reattach Dir and Tur1.  Detached them at end of this
//Tur1.attach(10);                           //  of this function to try to mitigate servo jitter.
//Dir.setSpeed(5);                           // Reset servo rotation speed of Director (5 is final speed, 50 for testing)
//Tur1.setSpeed(5);                          // RTest servo rotation speed of Turret 1,2 (5 is final speed, 50 for testing)

  float angle = DirIn.getAngle();
  //    if(abs(Tol > 2.0));
  Dir.write(angle);
  Tur1.write(angle);
  lastangle = angle;

//delay(20);                                // Delay 0.5 sec to complete write statements before
//Dir.detach();                              // Detach to try to mitigate servo jitter.
//Tur1.detach();

}

I don't see any obvious problem. I would use serial output in your follow mode function to see what's happening. It looks like you were using faster serial earlier, do you have to use 9600 baud for some reason?

Power's often a problem with servos, worth checking. Also note that the Serial output is going to slow things down.

Can you post your serial output with the code you have?

Below is some Serial Output (cut and pasted it - not sure how to post more). The jitter appears to be a couple of degrees. I have a PWM meter on the receiver output and servo input and both jitter. When I drive the servo directly from the receiver there is no jitter on the meter.



19:38:26.093 -> Modeangle: 180.00

19:38:26.093 ->

19:38:26.126 -> Director Input: 108.72

19:38:26.126 -> Modeangle: 180.00

19:38:26.159 ->

19:38:26.159 -> Director Input: 109.44

19:38:26.192 -> Modeangle: 180.00

19:38:26.192 ->

19:38:26.192 -> Director Input: 108.72

19:38:26.224 -> Modeangle: 180.00

19:38:26.257 ->

19:38:26.257 -> Director Input: 111.60

You can cut and paste serial output into a post in code tags.

Time to divide and conquer: if mobatools will show the signal in micro seconds, disconnect the other hardware and just display it.

I’ll look at the library in a few hours when I’m home with access to a bigger display than my phone :grinning:

Also, I can drive my 270 degree servos with a servo driver to the full 270 degrees (476 to 2518 usec). But the getAngle can only drive my servos 214 degrees (900 to 2304 usec) using a 0 degree and 180 degree. I tried writing greater than 180 degrees (e.g. 200 degrees), but it doesn't move the servo any further.

In order to get the full 270 degrees, do I need to not use the getAngle commands, and instead, re-write the entire program using pulse length commands?

I have 270 degree servos and can drive them with a servo driver to the full 270 degrees (476 to 2518 usec). But the getAngle can only drive my servos 214 degrees (900 to 2304 usec) using a 0 degree and 180 degree. I tried writing greater than 180 degrees (e.g. 200 degrees), but it doesn't move the servo any further.

In order to get the full 270 degrees, do I need to not use the getAngle commands, and instead, re-write the entire program using pulse length commands? Which servo library supports servo commands through the entire 476 to 2518 usec range?

ServoInput has methods to set the range of the pulse width, so something like "DirIn.setRange(476, 2518) ;" should give you the extended limits for pulse width. The angle will still be reported in the range 0-180 degrees unless the library source code is patched.

I added

  DirIn.setRange(476, 2518);

to my void setup() and there was no increase in servo response. Then I added:

  Tur1.setRange(476, 2518);
 Tur3.setRange(476, 2518);

And it would not compile. Here's the error method.

C:\Users\npboy\OneDrive\Documents\00 Alabama\SERVO\00_GFCS_r0\00_GFCS_r0.ino: In function 'void setup()':
C:\Users\npboy\OneDrive\Documents\00 Alabama\SERVO\00_GFCS_r0\00_GFCS_r0.ino:61:11: error: 'class MoToServo' has no member named 'setRange'
      Tur1.setRange(476, 2518);
           ^~~~~~~~
C:\Users\npboy\OneDrive\Documents\00 Alabama\SERVO\00_GFCS_r0\00_GFCS_r0.ino:62:11: error: 'class MoToServo' has no member named 'setRange'
      Tur3.setRange(476, 2518);
           ^~~~~~~~

exit status 1

Compilation error: 'class MoToServo' has no member named 'setRange'

Here's my code:

/*     1/96 Scale Model Ship
       Gunfire Control System - USS Alabama
       Neil Boyle
       January 8, 2024

       This program controls the turret rotation of a 3 gun battleship 
       with 3 RC channels on a DX-9 Spektrum Transmitter/Receiver.
       Note:  Code for Turret #3 (backward facing turret),has not been written yet.

       There are 3 Modes of Operation, controlled on ModeSw pin<3>.
         1.  Centerline Mode:  All turrets at centerline
         2.  Target Mode:  All turrets point toward target.  Dir can
             still move independently.
         3.  Follow Mode:  All turrets move in unison.
         
       DirIn pin<2> is the gun Director input.
       Dir pin<9> is the gun Director output.
       Tur1 pin<10> is Turret 1 and 2 output.

       The Mobatools library is included to servo rotation speed.
       The ServoInput library is used for servo angle commands and servo control.
       The Servo library is not included because I read that it may interfere
       with Mobatools - I'm not sure, I'm just a beginner.

       Some of the code my is prabably inefficient or non-professional, but keep in mind
       that this is my first time programming in C++.   I greatly appreciate all
       the help I have been given and continue to receive.

       */

#include <MobaTools.h>
#include <ServoInput.h>

/* The signal pin for ServoInput MUST be an interrupt-capable pin!
       Uno, Nano, Mini (328P): 2, 3
               Mega, Mega2560: 2, 3, 18, 19, 20, 21
  
*/
ServoInputPin<2> DirIn;         // Director input on pin 2.  Rotatry knob on DX-9.
ServoInputPin<3> ModeSw;        // Mode Switch on pin 3.  3 Position switch on DX-9.
MoToServo Dir;                  // I forget what this does, but it's needed,
MoToServo Tur1;                 // I forget what this does, but it's needed.
MoToServo Tur3;                 // I forget what this does, but it's needed.

float lastangle;                // Last angle of the Director - Floating decimal variable
float Tol;                      // Tolerance (Tol) = lastangle - angle.  Created to minimize servo hunting/jitter.
                                //   It didn't seem to mitigate servo jitter, so I commented it out in the code below.
int position;                   // Position of Mode Switch (= 1,2 or 3)

void setup() {

  Dir.attach(9);                // Attaches servo on pin 9 to servo object "Dir"
  Tur1.attach(10);              // Attaches servo on pin 10 to servo obect "Tur1" T(Turrets 1 and 2)
  Tur3.attach(11);              // Attaches servo on pin 11 to servo obect "Tur3"
  
  Dir.setSpeed(50);             // Set servo rotation speed of Director (5 is final speed, 50 for testing)
  Tur1.setSpeed(50);            // Set servo rotation speed of Turret 1,2 (5 is final speed, 50 for testing)
  Tur3.setSpeed(50);            // Set servo rotation speed of Turret 1,2 (5 is final speed, 50 for testing)

     DirIn.setRange(476, 2518);
     Tur1.setRange(476, 2518);
     Tur3.setRange(476, 2518);

  Serial.begin(9600);           // Serial port comu speed 9600 baud
  //      Serial.begin(115200);

  lastangle = 90.0;             // Initialize last angle to centerline = 90 degrees
  Tol = 0.0;                    // Initialize Tol to 0.0
}

void loop()                     // Main loop
{
/*                              Read PWM angle on the 3 pos switch on the DX-9 and to decide what mode you desire:
                                  1.  Modeangle < 30 degrees (position 0 on switch):  Move Turrets to Centerline
                                  2.  Modeangle > 120 degrees (position 2 on switch):  Move Turrets using Follow Mode
                                  3.  All else, Modeangle (position 3 on switch):  Move Turrets using Target Mode

                                  Crude method, but it seems to work.  I was unsuccessful with many attempts to use
                                  case commands and other commands to make the switch reading an integer, etc.  
*/

float angle = DirIn.getAngle();            // get angle of Director input from DX-9 (0 - 180)                                           
Tol = lastangle - angle;                   // Don't do anything unless Tol is > than say 1.5 degrees:  Tol = lastangle - angle

  float Modeangle = ModeSw.getAngle();      // Determine angle of 3 pos switch on DX-9

   if(Modeangle < 30.0)                     
    {movecntrl();}                         // If < 30 degrees, run movecntrl (move turrets to centerline) function

   else if(Modeangle > 120.0)              // If < 120 degrees, run followmode (all turrets follow Director) function
   {followmode();}

   else                                    // If neither above, run trgtmode (Director moves independently, turret remain at last positon)
   {trgtmode();}

     
  Serial.print("Modeangle: ");             // Print to Serial Monitor for debuggimg
  Serial.println(Modeangle);
  Serial.println();
  Serial.print("Director Input:  ");
  Serial.print(DirIn.getAngle());
  Serial.println();
  
}                                          // End of Main Loop

void movecntrl()                           // Function to Move turrets to centeline
 {
//Dir.attach(9);                             // Reattach Dir and Tur1.  Detached them at end of this
//Tur1.attach(10);                           //  of this function to try to mitigate servo jitter.
//Dir.setSpeed(5);                           // Reset servo rotation speed of Director (5 is final speed, 50 for testing)
//Tur1.setSpeed(5);                          // RTest servo rotation speed of Turret 1,2 (5 is final speed, 50 for testing) 

    Dir.write(90);
    Tur1.write(90);
    Tur3.write(90);
  
//delay(20);                                 // Delay 0.5 sec to complete write statements before
//Dir.detach();                              // Detach to try to mitigate servo jitter.
//Tur1.detach();
   
}

void trgtmode()                            // Function to Move turrets to Director (DirIn) position  
 {
//Dir.attach(9);                             // Reattach Dir and Tur1.  Detached them at end of this
//Tur1.attach(10);                           //  of this function to try to mitigate servo jitter.
//Dir.setSpeed(50);                           // Reset servo rotation speed of Director (5 is final speed, 50 for testing)
//Tur1.setSpeed(50);                          // RTest servo rotation speed of Turret 1,2 (5 is final speed, 50 for testing) 
     
  float angle = DirIn.getAngle();            // get angle of Director input from DX-9 (0 - 180)                                           
    Tol = lastangle - angle;                   // Don't do anything unless Tol is > than say 1.5 degrees:  Tol = lastangle - angle
    if(abs(Tol > 10.0));
 
      Dir.write(angle);
      lastangle = angle;

//delay(20);                                 // Delay 0.5 sec to complete write statements before
//Dir.detach();                              // Detach to try to mitigate servo jitter.
//Tur1.detach();
   //}
}

void followmode()                          // Function to have turrets follow Director
 {
//Dir.attach(9);                             // Reattach Dir and Tur1.  Detached them at end of this
//Tur1.attach(10);                           //  of this function to try to mitigate servo jitter.
//Dir.setSpeed(5);                           // Reset servo rotation speed of Director (5 is final speed, 50 for testing)
//Tur1.setSpeed(5);                          // RTest servo rotation speed of Turret 1,2 (5 is final speed, 50 for testing)

float angle = DirIn.getAngle();            // get angle of Director input from DX-9 (0 - 180)                                           
    Tol = lastangle - angle;                   // Don't do anything unless Tol is > than say 1.5 degrees:  Tol = lastangle - angle
    if(abs(Tol > 10.0));

       Dir.write(angle);
       Tur1.write(angle);
       moveTur3();
       lastangle = angle;

//delay(20);                                // Delay 0.5 sec to complete write statements before
//Dir.detach();                              // Detach to try to mitigate servo jitter.
//Tur1.detach();

}

/*  Moving Turret #3

       Backward facing Turret #3 is rotationally automated when the targeted position is in, or near, the port or
       starboard "quadrants".  With the DS3218mg Servos, the "quadrants" currently achieve 34 degrees of actual
       rotation: 17 degrees on either side of the perpendicular, starting on the starboard side at 0 degrees
       t0 34 degrees, then on the port side at 180 degrees to 214 degrees.  Dead ahead, by servo instruction, is 107
       degrees.  So the DS3218 "270 Degree" servos only actually acheive 214 degrees, with the Spektrum DX-9 servo 
       travel set to 150%, and using the Arduino getAngle() function from 0 - 180 degrees.
       Here is a calibration guide: 

           Switch "C"    Command       Actual     Serial Mon      Pulse Width Display    Comment
              0             0           -107          0               702                107 degrees stbd from dead ahead
              1            90              0         90              1503                Dead ahead
              2           180            234        180              2303                214 degrees, 107 port

            Stbd Quad       0-34 Actual Degrees,  
            Port Quad       180-214 Actual Degrees. If Dir is leaving Port Quad leave Tur 3 as is until 120 degrees
             
*/

void moveTur3()                                      // Function to move Turret 3
{
  float angle = DirIn.getAngle();                    // get angle of Director input from DX-9 (0 - 180)                                           

   if((angle >= 0) && (angle < 34))                  // If Dir is in the Starboard quadrant then train Turret 3 to there
     {Tur3.write((angle+146));}

     else if((angle > 146) && (angle <= 180))       // If Dir is in the Port quadrant then train Turret 3 to there
     {Tur3.write((angle-146));}

      else if((angle <= 146) && (angle > 120 ))      // If Dir is leaving the Port Quadrant, leave Turret 3 there until 120 degrees
      {Tur3.write(0);}
  
     else                                            // If not then move Turret 3 back to it's centerline
     {Tur3.write(90);}

       lastangle = angle;                            // reset lastangle to most current angle
}

Because I need to use these 270 degree servos, I tried to modify the servo sweep with microsecs instead of pos (in degrees).

It compiled OK and runs, but it only seems to print the interval entered, the servos do not move: they see 546 microsecs. posmsec prints at either 0 or 10 (see serial monitor output sample after code).

Here's my code:

#include <Servo.h> 



class Sweeper
{
  Servo servo;              // the servo
 /* 
  int pos;              // current servo position 
  int increment;        // increment to move for each interval
  int  updateInterval;      // interval between updates
 */ 
  
  unsigned long lastUpdate; // last update of position
  unsigned long updateInterval;     // Neil's edit
  unsigned long posmsec;            // Neil's edit
  unsigned long incremnt;           // Neil's edit


public: 
  Sweeper(int interval)
  {
    updateInterval = interval;
    incremnt = 10;                    // Neil's edit
  }
  
  void Attach(int pin)
  {
    servo.attach(pin);
  }
  
  void Detach()
  {
    servo.detach();
  }
  
  void Update()
  {
    if((millis() - lastUpdate) > updateInterval)  // time to update
    {
      lastUpdate = millis();
      //  pos += increment;
      //  servo.write(pos);
      posmsec = (posmsec + incremnt);                      // Neil's edit
      servo.writeMicroseconds(posmsec);                    // Neil's edit
      Serial.println(posmsec);                             // Neil's edit
      //  if ((pos >= 180) || (pos <= 0)) // end of sweep
      if ((posmsec >= 2300) || (posmsec <= 900)) // end of sweep    // Neil's edit
      {
        // reverse direction
        //increment = -increment;
        incremnt = -incremnt;                              // Neil's edit
        

      }
    }
  }
};
 



Sweeper sweeper1(15);
Sweeper sweeper2(25);
 
void setup() 
{ 
  Serial.begin(9600);
  sweeper1.Attach(9);
  sweeper2.Attach(10);
} 
 
 
void loop() 
{ 
  sweeper1.Update();
  sweeper2.Update();
 
}

Here's what the serial monitor shows:

15:48:14.576 -> 0
15:48:14.576 -> 10
15:48:14.609 -> 10
15:48:14.609 -> 0
15:48:14.609 -> 0
15:48:14.609 -> 10
15:48:14.641 -> 10
15:48:14.641 -> 0
15:48:14.641 -> 10
15:48:14.674 -> 0
15:48:14.674 -> 0
15:48:14.674 -> 10
15:48:14.707 -> 10
15:48:14.707 -> 0
15:48:14.707 -> 0
15:48:14.707 -> 10
15:48:14.748 -> 0
15:48:14.748 -> 10
15:48:14.748 -> 10
15:48:14.781 -> 0
15:48:14.781 -> 0
15:48:14.781 -> 10

You need to set posmsec to a valid number when you create the sweeper instance. Try 1400 and set it in the Sweeper function (the constructor).

That fixed it - Thanks!

Due to the 270 degree servos that I am using, I rewrote the entire program using pulse length commands. It works good so far (still working on it), however just like my previous code, the servos jitter some. I would like to stop the jitter when the turrets are at rest by moving them to a specific microsecond value, rather than a value that potentially changes slightly during every loop cycle.

So I was thinking of reading the position of switch - when it changes I read the current value of an input, save it, then have the servos move to that specific value (number of msecs). So moving the position of a switch would be like "locking on to a target position", storing that position for a firing solution, then training the turrets to that "fixed position" (fixed, until the switch is toggled again).

How would I do that with code, without slowing/interupting the loop? Some kind of function with a "return"? Interrupt? Perhaps using interrupt when it's time to move a servo would lessen my servo jitter?

Here's my new code so far. I still need to configure the Turret 3 move function and add Modes to switch: 1. Move to Centerline 2. Follow DIrector 3. Leave Turrets in position, move/control Director

#include <ServoInput.h>                  // Library includes getAngle instance-method
#include <Servo.h>                       // Library includes writeMicroseconds instance-method

ServoInputPin<2>DirDesired;                   // Class ServoInput variable DirIn connected to Pin 2
float angle = DirDesired.getAngle();          // floating (decimal) variable "angle"

unsigned long minServoPos;               // Minimum microsecs of servos. Determines 0 degrees and total servo movement
unsigned long maxServoPos;               // Maximum microsecs of servos. Determines 180 degrees and total servo movement
unsigned long DirD_ms;                   // Director Desired Position
unsigned long DirAc_ms;                  // Director Actual Position
unsigned long CurPos;                    // Current Director position incrementor
unsigned long Tur1Ac_ms;                 // Turret 1 Actual Position
unsigned long CurPosT1;                  // Current Director position incrementor
unsigned long Tur3Ac_ms;                 // Turret 1 Actual Position
unsigned long CurPosT3;                  // Current Director position incrementor
unsigned long previousMillis;            // Millisecond incrementor
unsigned long stepIntervalMillis;        // Step Interval in milliseconds
unsigned long PosInterval;               // Position Interval in milliseconds

Servo Dir;                               // Class Servo, Dir variable
Servo Tur1;                              // Class Servo, Tur1 variable
Servo Tur3;                              // Class Servo, Tur3 variable


void setup() 
{ 
  Serial.begin(9600);
  minServoPos =  480;
  maxServoPos = 2500;
  Dir.attach(9);                         // instance-method attach, variable Dir to integer Pin 9
  Tur1.attach(10);                       // instance-method attach, variable Tur1 to integer Pin 10
  Tur3.attach(11);                       // instance-method attach, variable Tur3 to integer Pin 11
  previousMillis = 0;                    // Initialize timer in move to functions
 
  stepIntervalMillis = 5;               // Time interval between steps:  Higher interval number, the slower servo moves
  PosInterval = 4;                       // Position interval between steps;  Lower the Position Interval, the slower the servo moves
} 
void loop()                              // Main Loop
{ 
director();                              // Director Move to Position function
turret1();                               // Turret 1 Move to Position function
turret3();                               // Turret 3 Move to Position function
}


void director()                                               // Director Move to Position function
{
float angle = DirDesired.getAngle();                          // Get Desired Director (Rec Output, Arduino Input) angle in degrees
DirD_ms = map(angle,0,180,minServoPos,maxServoPos);           // Map DirIn angle to DirInms to get full 270 degree servo travel 
DirAc_ms = Dir.readMicroseconds();                            // Read Director Actual Position
CurPos = DirAc_ms;                                            // Current Position = Director Actual Position

if((DirAc_ms) > (DirD_ms))  {                                            // Compare Director Actual to Director Desired Position
         if((millis() - previousMillis) >= stepIntervalMillis) {         //  Increment Director Actual in steps
          CurPos = (CurPos - PosInterval);
          Dir.writeMicroseconds(CurPos);
          previousMillis = millis();
         }
   }

if((DirAc_ms) < (DirD_ms))  {                                            // Compare Director Actual to Director Desired Position
         if((millis() - previousMillis) >= stepIntervalMillis) {         //  Increment Director Actual in steps
          CurPos = (CurPos + PosInterval);
          Dir.writeMicroseconds(CurPos);
          previousMillis = millis();
         }
   }

}

void turret1()                                                // Turret 1 Move to Position function
{
float angle = DirDesired.getAngle();                          // Get Desired Director (Rec Output, Arduino Input) angle in degrees
DirD_ms = map(angle,0,180,minServoPos,maxServoPos);           // Map DirIn angle to DirInms to get full 270 degree servo travel 
Tur1Ac_ms = Tur1.readMicroseconds();                          // Read Turret 1 Actual Position
CurPosT1 = Tur1Ac_ms;                                         // Current Position = Director Actual Position
//millisT1 = millis();

if((Tur1Ac_ms) > (DirD_ms))  {                                          // Compare Turret 1 Actual to Director Desired Position
         if((millis() - previousMillis) >= stepIntervalMillis) {       //  Increment Turret 1 Actual in steps
          CurPosT1 = (CurPosT1 - PosInterval);
          Tur1.writeMicroseconds(CurPosT1);
          previousMillis = millis();
         }
   }

if((Tur1Ac_ms) < (DirD_ms))  {                                         // Compare Turret 1 Actual to Director Desired Position
         if((millis() - previousMillis) >= stepIntervalMillis) {       //  Increment Turret 1 Actual in steps
          CurPosT1 = (CurPosT1 + PosInterval);
          Tur1.writeMicroseconds(CurPosT1);
          previousMillis = millis();
         }
   }
}

void turret3()                                                // Turret 1 Move to Position function
{
float angle = DirDesired.getAngle();                          // Get Desired Director (Rec Output, Arduino Input) angle in degrees
DirD_ms = map(angle,0,180,minServoPos,maxServoPos);           // Map DirIn angle to DirInms to get full 270 degree servo travel 
Tur3Ac_ms = Tur3.readMicroseconds();                          // Read Turret 1 Actual Position
CurPosT3 = Tur3Ac_ms;                                         // Current Position = Director Actual Position
//millisT3 = millis();

if((Tur3Ac_ms) > (DirD_ms))  {                                          // Compare Turret 3 Actual to Director Desired Position
         if((millis() - previousMillis) >= stepIntervalMillis) {       //  Increment Turret 3 Actual in steps
          CurPosT3 = (CurPosT3 - PosInterval);
          Tur3.writeMicroseconds(CurPosT3);
          previousMillis = millis();
         }
   }

if((Tur3Ac_ms) < (DirD_ms))  {                                         // Compare Turret 3 Actual to Director Desired Position
         if((millis() - previousMillis) >= stepIntervalMillis) {       //  Increment Turret 3 Actual in steps
          CurPosT3 = (CurPosT3 + PosInterval);
          Tur3.writeMicroseconds(CurPosT3);
          previousMillis = millis();
         }
   }
}

What happened to ModeSw? That seems like a way to indicate that the turrets should lock in place.

I'd be inclined to chase the jitter problem down by stripping the hardware down to the minimum and adding it back until the problem occurs, but mode will help if you don't want to fix the root cause just yet.

Hi, @ovalracer

What are you using as power supplies?
Do you have a DMM? Digital MultiMeter?

Monitor the power supplies when the jitter is occuring?

Can you please post a copy of your circuit, a picture of a hand drawn circuit in jpg, png?
Hand drawn and photographed is perfectly acceptable.
Please include ALL hardware, power supplies, component names and pin labels.
Not a cut and paste image collection.

Can you post some pictures of your project, in particular the power supply wiring?

Thanks.. Tom.. :grinning: :+1: :coffee: :australia:

I agree with your suggestion. I haven't written the Mode Switch part yet. The code so far is to just make sure the servos move, The next step is to control Turret 3 (backward facing turret) correctly, then write the Mode Switch code.

I guess I am still struggling with how to best move a turret at a controlled, smooth speed and not have it jitter once it gets there.

As I have mentioned, my first program used Mobatools to set servo speed with "setSpeed()" and ServoInput to move using angles with "getAngle()". Because I couldn't get a full 270 degrees of movement, I am now writing a totally new program that controls speed with a Sweeper type of approach, and moves the servos using microsecs. So far so good, but I don't really know how to minimize jitter, without using perhaps an interrupt (which I don't really understand). I am also interested in ServoEasing and Adafruit TiCoServo, but I'm not sure if they work on 270 servos. What do you suggest?

p.s. I am also preparing an answer/picture to TomGeorge's question on my wiring.

I'd write a trivial program that just reads the angle from the director and sets the turret one servo to that value. Do it once, in setup and see whether it jitters. That'll tell you whether the problem can be solved in software.

Hi TomGeorge,

I have a PMW meter that shows the servo jitter (fluctuations of usually 1-10 usecs, sometimes greater spikes) when the Mega is connected to the servos. When my servos are connected to just my receiver, the PWM meter shows no jitter.

Yes, my Digital MultiMeter shows no voltage movement on either the 12 battery (that feeds the 12V to 5V DC DC Converter) or the 5V Converter. The 5V DC DC Converter is a just a generic, cheap converter from Amazon. I paid particular attention to ensure that I connected all negatives together. I even tried wrapping my servo wires with ferrite cores (green donut things)...

Here's pictures of my wiring, test stand, etc.

Good suggestion, I've done that and yes, it jitters. Perhaps the way I wrote the code is causing the servos to jitter? I guess I am still confused on how to write code, that grabs a receiver position then performs a function on that data to move a servo at a slower speed without doing that in a continuous loop.

The first code is with Mobatools (setSpeed, getAngles, but I can't get full 270 degrees). The second code is with my sweeper type structure (microsecs, millis without delay, I can get full 270 degrees). They both jitter, but the jitter gets a little worse the more complex the code gets.

#include <MobaTools.h>
#include <ServoInput.h>
/*
    Example:      Reverse Servo adapted from ServoInput BasicAngle example
    Description:  Reads the PWM signal from a servo motor, converts it to
                  an angle, and prints that angle over Serial.

    Wiring:       DirIn Variable Servo signal input to pin 2
                  Dir Servo signal output to pin 9
*/

/* The signal pin for ServoInput MUST be an interrupt-capable pin!
       Uno, Nano, Mini (328P): 2, 3
               Mega, Mega2560: 2, 3, 18, 19, 20, 21
  
*/
ServoInputPin<2> DirIn;
MoToServo Dir;

void setup()
{
  Dir.attach(9);  // attaches the servo on pin 9 to the servo object "Dir"
  Dir.setSpeed( 100 );    // set speed of servo
  Serial.begin(9600);
}

void loop()
{
  float angle = DirIn.getAngle();  // get angle of servo (0 - 180)
  /* Processing to modify the input pulse received to the output pulse desired */
  Dir.write(angle);
  
  Serial.print(DirIn.getAngle());             // print the angle on Serial Monitor
  Serial.println();

}

Second code:

#include <ServoInput.h>                  // Library includes getAngle instance-method
#include <Servo.h>                       // Library includes writeMicroseconds instance-method

ServoInputPin<2>DirIn;                   // Class ServoInput variable DirIn connected to Pin 2
float angle = DirIn.getAngle();          // floating (decimal) variable "angle"
float lastangle;
float Tol;
float DirInms;
unsigned long DirMillis;

unsigned long currentMills;
unsigned long previousMillis;
unsigned long stepIntervalMillis;

Servo Dir;                               // Class Servo, Dir variable
Servo Tur1;                              // Class Servo, Tur1 variable

void setup() 
{ 
  Serial.begin(9600);
  Dir.attach(9);                         // instance-method attach, variable Dir to integer Pin 9
  Tur1.attach(10);                       // instance-method attach, variable Tur1 to integer Pin 10
  Serial.begin(9600);
} 
void loop() 
{ 
  float angle = DirIn.getAngle();              // Get DirIn angle in degrees
  DirInms = map(angle,0,180,480,2500);         // Map DirIn angle to DirInms to get full 270 degree servo travel

  //if((currentMills - previousMillis) >= stepIntervalMillis) {
  Dir.writeMicroseconds(DirInms);              // Write to Dir using microseconds
  Dir.readMicroseconds();                      // Read Dir microseconds.  readMicroseconds only supported by Servo, not ServoInput
     
     DirMillis = (1000 * Dir.readMicroseconds());

Serial.println(DirMillis);

  //Serial.println(DirInms);                     // Output to monitor DirInms
  Serial.println();
  
  //previousMillis += stepIntervalMillis;
  //}
}

Can you post that code?

If that's giving you jitter, the problem is not likely to be your code.