Simulated Gunfire Control System for 1/96 Scale Operational Battleship

I'm trying to automate the guns on a 1/96 scale, fully operational, battleship USS Alabama.

I have automated guns on other 1/96 model ships with servos, but would like to develop a more sophisticated Gunfire Control System using stepper motors controlled by an arduino. I have researched (and purchased) ardino's, stepper motors, drivers, comu with rc receiver, etc. and have reached the conclusion that it would take me a long time to figure this out, even though I know the basics of using an arduino.

Specifically, I would like to control the 3 16" guns similar to how it was actually done on this ww2 ship: the guns follow wherever the director is pointed. It may sound easy, but the devil is in the details..

Could I hire someone to write the code for me? What's the best way to go about find someone that could successfully write the code? Thanks in advance for your suggestions/help.

Long before you need code written, do you have all the physical installation done and tested? You make no mention of the power for your project. All that needs to be done and ready for testing. Testing of your program must be done a step at a time.
Do you envision someone being able to test and develop a program for your project sending a test program to you so you can test it and report back each success and failure, so the next step can be programmed?

This is a good place to look... also Model Railroad web sites and fora who also use discrete electronics and microcontrollers. I can imagine a small DC motor rotating the RADAR, lights in the port holes, green and red on starboard and port, an orange flashes in a few barrels, vibration motors while the turrets turn on servo motors... sounds like a lot of fun! Write it down. Get it in motion.

Yes I have all the physical equipment, power supply, etc., I just need help in writing C++ code for an Arduino to control 3 turret (270 degree) servos via a RC Transmitter (Spektrum dx9).

I have scoured this site and the internet for example code to do that and can find plenty that can do some things, but not all 3: arduino, RC transmitter and servos. Also, I have basic code written for Basic Stamp (written by a friend along time ago) that details all the functions.

To get a better idea of the specifics of what I need, here is Basic Stamp code for a 5 turret destroyer. My friends and I couldn't get it to work properly...

' {$STAMP BS2sx}
' {$PBASIC 2.5}

' The two lines above identify the Basic Stamp type and PBASIC version,
' used in this program.

'==========================================
'=        Gun Fire Control System         =
'==========================================
'
' Author:     Nico Ottevaere
' Filename:   DD_GFCS_21_with_comments.BSX
' Revision:   2.1
' Date:       10 Nov 04
'
' This program allows control of the main gun battery of a 5-gun destroyer
' to be controlled by 2 RC channels.
'     One channel controls the gun director bearing
'     Another channel controls the mode of operation
'         OFF         :  Director and guns align with ships centerline
'         STANDBY     :  De guns remain in their last controlled position,
'                        the director is controlled by the RC bearing channel
'         ASSIGNED    :  The guns follow the director, when within rotation angle.
'                        Otherwise they train to the closest possible position.
'
' Program Revision History:
'   1.0 Proportional position control of target, 6 channels
'   1.1 Introduced 4 control groups instead of 6 channels to speed up program
'   2.0 Proportional speed control of target bearing
'   2.1 Incorporated separate maximum rotating speed for director or guns

' PDL
' Initialize system
' Main Loop:
'     Adjust servo position but limit training rate
'     Send servo pulses
'     Change the status LED indication
'     Measure bearing information pulse from receiver
'     Adjust targets position for measured pulse (speed-control)
'     Measure mode information pulse from receiver
'     Decode mode
'     Mode = OFF
'         Make all Desired Positions for all CGs centerline
'         Continue at Main Loop
'     Mode = STANDBY
'         Make Desired Position for CG1 (gun director) equal to Target
'         Continue at Main Loop
'     Mode = ASSIGNED
'         Make Desired Position for CG1 (gun director) equal to Target
'         Make Desired Position for CG2 (Turrets #1 & #2) equal to Target
'         IF Target within range of turret #3
'            Make Desired Position for CG3 (Turret #3) equal to Target
'         ELSE
'            Set Desired Position for CG3 (Turret #3) as close as possible
'                     to Target
'         IF Target within range of turrets #4 & #5
'            Calculate a Desired Position for CG4 (Turret #4 & #5) so
'                that the guns align with Target
'         ELSE
'            Set Desired Position for CG4 (Turret #4 & #5) as close
'                as possible to Target
'         Continue at Main Loop

'==========================================
'=             DEFINITIONS                =
'==========================================
'
'------------------------------------------
'-               Constants                -
'------------------------------------------
'
MPX_Mid          CON     1875       ' Duration of RC pulse when at center position
                                    ' BS-SX has pulse measuring resolution of 0.8us
                                    ' 1875 * 0.8 us totals 1500us or 1.5ms
Min_Pulse        CON     680        ' Minimum pulse length that can be applied
                                    ' to Parallax 180DEG servo.
                                    ' 680 * 0.8us =  480us or 0.48ms
Max_Pulse        CON     2810       ' Maximum pulse length that can be applied
                                    ' to Parallax 180DEG servo.
                                    ' 2810 * 0.8us =  2248us or 2.25ms
                                    ' Pulse value has a range of 2810 - 680 =
                                    ' 2130.  This corresponds to 270DEG of
                                    ' rotation of the turrets so each degree of
                                    ' rotation corresponds to app. 8 units
Mid_Pulse        CON     1745       ' Mid pulse to put Parallax 180DEG servos
                                    ' centerline
                                    ' this is not the mathematic center of the pulse
                                    ' range because of the non-linear operation due
                                    ' to extreme pulses
                                    ' Response is most linear around mid pulse
P3_Limit         CON     1600       ' Port training limit of #3 gun
SB3_Limit        CON     1890       ' Starboard training limit of #3 gun
P4_Limit         CON     1550       ' Port training limit of #4 and #5 gun
SB4_Limit        CON     2030       ' Starboard training limit of #4 and #5 gun
                                    ' Following are 180DEG correction values
                                    ' for guns #4 and #5
Offset_P         CON     1400       ' Offset positive
Offset_M         CON     1400       ' Offset minus
                                    ' I have two values to be able to make final
                                    ' adjustments when installed and operational
Turret_Rate      CON     15         ' Maximum rate at which turret can train
                                    ' 15 units is about 2DEG, calculations are done
                                    ' 50 times a second so the maximum training rate
                                    ' is 100DEG / second
Director_Rate    CON     25         ' Maximum rate at which director can train
                                    ' 25 units is about 3DEG, calculations are done
                                    ' 50 times a second so the maximum training rate
                                    ' is 150DEG / second

'------------------------------------------
'-      Input Output configuration        -
'------------------------------------------
'
Alive            PIN      7         ' Assign name "Alive" to IO-pin 7
Receiver_Bearing PIN     15         ' Assign name "Receiver_Bearing" to IO-pin 15
Receiver_Mode    PIN     14         ' Assign name "Receiver_Mode" to IO-pin 14
                                    ' The servo pulses are generated by the command
                                    ' PULSOUT. This command sets the IO configuration
                                    ' of concerned PIN each time it executes

'------------------------------------------
'-               Variables                -
'------------------------------------------
'
Mode             VAR     Nib        ' Nibble sized variable (4bits) that holds
                                    ' the current operating mode
                                    ' 0 = OFF
                                    ' 1 = STANDBY
                                    ' 2 = ASSIGNED
Index            VAR     Nib        ' Nibble sized variable used as a counter (0 - 15)
Pulse_In         VAR     Word       ' Word sized variable (16bits) to hold measured
                                    ' pulse length values
Target           VAR     Word       ' Word sized variable that holds the bearing
                                    ' value of the target in "pulse length format" so
                                    ' 1745 means dead ahead
Current_Pos      VAR     Word(4)    ' Array of 4 words, one for each Control Group,
                                    ' holding the current pulse length value
                                    ' Current_Pos(0) for the Gun Director
                                    ' Current_Pos(1) for the turrets #1 and #2
                                    ' Current_Pos(2) for turret #3
                                    ' Current_Pos(3) for the turrets #4 and #5
Desired_Pos      VAR     Word(4)    ' Array of 4 words, one for each Control Group,
                                    ' holding the desired pulse length value
                                    ' Similar to Current_Pos(0-3) here above
Error            VAR     Word       ' Word sized variable used in calculations

'==========================================
'=               PROGRAM                  =
'==========================================

'-----------------------------------------
'--           Initialization            --
'-----------------------------------------

' Set all turrets and gun director in centerline position.
' Set mode to OFF and Target to dead ahead

Initialize:                         ' Label to make code more readable
  FOR Index = 0 TO 3                ' This FOR NEXT loop starts with Index = 0
                                    ' so the first pass, Current_Pos(0) and
                                    ' Desired_Pos(0)are set to centerline
    Current_Pos(Index) = Mid_Pulse
    Desired_Pos(Index) = Mid_Pulse
  NEXT                              ' Here Index is incremented with 1 and if the
                                    ' condition is still true, the loop is performed
                                    ' again.  So Index now gets the value 1, then 2,
                                    ' then 3.
                                    ' After the fourth pass Index would be 4 but this
                                    ' doesn't comply with the condition so no more
                                    ' loops are executed

  Mode = 0                          ' Mode is set to "OFF"
  Target = Mid_Pulse                ' Target is set to be dead ahead

'-----------------------------------------
'--              Main Loop              --
'-----------------------------------------

Main_Loop:                          ' Label to make code more readable
                                    ' A label is also used to direct the program to
                                    ' a certain entry point
  GOSUB Slew_to_Bearing             ' Hold execution of this program and go execute a
                                    ' subroutine starting at the label
                                    ' "Slew_to_Bearing:"
                                    ' Upon completion of this subroutine, continue
                                    ' with the next line

  GOSUB Send_Pulses                 ' Again go execute a subroutine this time
                                    ' labeled "Send_Pulses:", then continue next line

  TOGGLE Alive                      ' Make IO Pin "Alive" (defined to be PIN 7) an
                                    ' OUTPUT and change its state. If 0 make 1,
                                    ' if 1 make 0.
                                    ' As the status LED is connected to this PIN,
                                    ' it blinks ON and OFF for each program pass

  Measure_Receiver_Bearing_Pulse:   ' Label to make code more readable
                                    ' Next instruction will configure PIN
                                    ' "Receiver_Bearing" as an input and wait for a
                                    ' positive pulse.  If no pulse is detected before
                                    ' the command times out, it returns a 0
                                    ' Otherwise, the duration is counted in 0.8us
                                    ' for the SX version of the Basic Stamp
    PULSIN Receiver_Bearing, 1, Pulse_In
                                    ' If the PULSIN instruction returned a 0, then
                                    ' no pulse was measured yet, so go to label
                                    ' "Measure_Receiver_Bearing_Pulse:" and measure
                                    ' again.  In fact the program waits here until it
                                    ' gets the pulse and thus synchronizes with the
                                    ' RC receiver pulse sequence
    IF ( Pulse_In = 0 ) THEN  Measure_Receiver_Mode_Pulse

  Adjust_Target_Bearing:            ' Label to make code more readable
                                    ' Now we have to convert the measured pulse value
                                    ' into a usable value with witch we can adjust
                                    ' the targets bearing.
                                    ' Dividing the measured pulse value by 40 gives
                                    ' a value ranging from 32 to 66.
                                    ' By subtracting 49, the resulting range becomes
                                    ' -17 to +17 (speed value representing the
                                    ' sticks direction and displacement)
    IF Mode > 0  THEN               ' On the condition that mode is not OFF
      Pulse_In = (Pulse_In / 40) - 49           ' Convert measured pulse
      Target = Target - Pulse_In MIN Min_Pulse  ' Adjust targets position by
                                    ' subtracting the calculated speed value BUT
                                    ' let Target not become smaller than Min_Pulse.
      Target = Target MAX Max_Pulse ' If speed is negative, subtracted from target
                                    ' makes Target becomes bigger, so also make sure
                                    ' that Targets doesn't become bigger than
                                    ' Maximum pulse.
    ENDIF                           ' End of this conditional section of code

  Measure_Receiver_Mode_Pulse:      ' Label to make code more readable
                                    ' Same variable is used again, but now to measure
                                    ' a positive pulse on PIN "Receiver_Mode"
    PULSIN Receiver_Mode, 1, Pulse_In
    IF ( Pulse_In = 0 ) THEN  Measure_Receiver_Mode_Pulse

  Determine_Mode:                   ' Label to make code more readable
                                    ' Now we have to decode the commanded mode.
                                    ' As this is done with a 3-position switch,
                                    ' the values can either be MIN, Mid or Max
                                    ' RC pulse value.
                                    ' As there is no trim on this RC function,
                                    ' I included a dead-zone around center position.
                                    ' Only the extremes will extend beyond the
                                    ' dead-zone

    Mode = 1                        ' Make mode STANDBY (as if a Mid pulse was received
    IF Pulse_In > MPX_Mid + 300 THEN' If the measured value is larger
      Mode = 2                      ' then make mode = ASSIGNED
    ELSEIF Pulse_In < MPX_Mid - 300 THEN ' else if measured value is smaller
      Mode = 0                      ' then make mode = OFF
    ENDIF                           ' End of conditional section.
                                    ' If non of the conditions was met, then the mode
                                    ' remains STANDBY as set before the IF statement

  BRANCH Mode, [Off, Standby, Assigned] ' This instruction will, depending on the
                                    ' value of Mode continue program execution as
                                    ' follows:
                                    '    Mode = 0 go to label "Off:"
                                    '    Mode = 1 go to label "Standby:"
                                    '    Mode = 2 go to label "Assigned:"

  ' +++++++++++++++++++++++
  ' ++        OFF        ++
  ' +++++++++++++++++++++++
    Off:                            ' Label to make code more readable
                                    ' Now come the specific things to do in OFF
      FOR Index = 0 TO 3            ' Loop for all Control Groups to set the desired
                                    ' position to centerline.
        Desired_Pos(Index) = Mid_Pulse
      NEXT
      GOTO Main_Loop                ' Continue program execution at label "Main_Loop:"

  ' +++++++++++++++++++++++
  ' ++     STANDBY       ++
  ' +++++++++++++++++++++++
    Standby:                        ' Label to make code more readable
                                    ' In this mode, only the director is controlled
                                    ' Its desired position becomes the target, and
                                    ' as the target is controlled by the RC
                                    ' transmitter, one controls in fact the director
      Desired_Pos(0) = Target
    GOTO Main_Loop                  ' Nothing is changed on the turrets desired
                                    ' positions, so these do not move.
                                    ' Continue program execution at label "Main_Loop:"

  ' +++++++++++++++++++++++
  ' ++     Assigned      ++
  ' +++++++++++++++++++++++

    Assigned:                       ' Label to make code more readable
      CG_Director:
      Desired_Pos(0) = Target       ' Align director with target
      Guns_1_and_2:
      Desired_Pos(1) = Target       ' Align guns #1 and #2 with target
                                    ' In my system these have the same mechanical
                                    ' range and same direction for centerline so
                                    ' they rotate from -135DEG (Port)
                                    ' over 0DEG centerline forward TO +135DEG (STBD)
                                    ' I can always simply slave these guns to the
                                    ' director

     CG_Gun_3:                      ' Now come the calculations for turret #3. It is
                                    ' also pointing forward, but has a sector around
                                    ' centerline in which it can not be used
                                    ' (do NOT fire into own ship)
                                    ' So only if the target is outside this sector
      IF (Target < P3_Limit) OR (Target > SB3_Limit) THEN
        Desired_Pos(2) = Target     ' Align turret #3 with target
      ELSE                          ' otherwise
        IF Target > Mid_Pulse THEN  ' If the Target is to starboard,
          Desired_Pos(2) = SB3_Limit' then set the gun to the starboard sector limit
        ELSE
          Desired_Pos(2) = P3_Limit ' else set the gun to the port sector limit
        ENDIF
      ENDIF
      CG_Guns_4_and_5:              ' Calculations for guns 4 and 5 have also to
                                    ' consider that these guns have their center
                                    ' position facing aft These guns rotate from
                                    ' -45DEG (PORT) over 180DEG centerline AFT
                                    ' to +45DEG (STBD)
                                    ' In my setup the servo rotation is therefore
                                    ' reversed.  These guns can not train in the
                                    ' sector -45 - 0 - +45.  So first verify that the
                                    ' target is outside the no train zone
      IF (Target < P4_Limit) OR (Target > SB4_Limit) THEN
        IF Target > Mid_Pulse THEN  ' Now if the target is on the starboard side
                                    ' make the desired position target - 180DEG, but
                                    ' let it not be less than Min pulse
           Desired_Pos(3) = Target - Offset_M  MIN Min_Pulse
        ELSE                        ' else target on port side
                                    ' make the desired position target + 180DEG, but
                                    ' let it not be more than Max pulse
           Desired_Pos(3) = Target + Offset_P  MAX Max_Pulse
        ENDIF
      ELSE                          ' So the target is in a no train zone
        IF Target => Mid_Pulse THEN ' If target is dead ahead or to starboard
                                    ' train the turrets max forward to port
          Desired_Pos(3) = Min_Pulse
        ELSE                        ' so target is to port
                                    ' train the turrets max forward to starboard
          Desired_Pos(3) = Max_Pulse
        ENDIF
      ENDIF

      GOTO Main_Loop                ' Continue at label "Main_Loop:"

'==========================================
'=             SUBROUTINES                =
'==========================================

'------------------------------------------
'--            Send_Pulses               --
'------------------------------------------
'
' Version 2.0
' 10 Nov 04
' Subroutine that generates the servo pulses to IO pins 0 - 3
' 0 : Gun Director
' 1 : Turrets 1 and 2
' 2 : Turret 3
' 3 : Turrets 4 and 5

' The value for the pulses is stored in the array Current_Pos

' INPUT :     Current_Pos()   Array of words holding pulse values
' OUTPUT :    Servo Pulses to pins 0 through 3
' VARIABLES : Index

' Written:          28 Oct 04
' Modifications
' Version:          2  Reduced number of pulses from 6 to 4 to reduce
'                      duration of routine
' PDL
' Send servo pulses
'    For each control group
'       Send servo pulse of "current position" length
'    End of subroutine

Send_pulses:
  FOR Index = 0 TO 3                ' Loop for all Control Groups
    PULSOUT Index, ( Current_Pos(Index) )' This command defines PIN(Index) as an
                                    ' OUTPUT and generates a pulse with a duration of
                                    ' Current_Position(Index) * 0.8 us (BS-SX)
  NEXT                              ' Next Index
  RETURN                            ' Return from this subroutine.  The program now
                                    ' continues at the line after the call to this
                                    ' subroutine

'------------------------------------------
'--           Slew_to_Bearing            --
'------------------------------------------
'
' Version 2.0
' 29 Nov 04
' Slew selected turret to target pulse and limit speed to Slew_Rate

' INPUT :     Desired_Pos(Index)
'             Index   (Selected servo channel 0 = gun director
'                                             1 = turrets 1 and 2
'                                             2 = turret 3
'                                             3 = turrets 4 and 5
'             Current_Pos(Index)
' OUTPUT :    Current_Pos(Index) (Corrected position of selected servo channel)
' VARIABLES : Error, Current_Pos(Index)

' Written:          28 Oct 04
' Modifications
' Version:          2  added separate calculations for director to give it
'                      bigger speed than turrets
' PDL
' Director Calculation
'   If current position >= desired position then go to Decrement_Director
'      Error = current position - desired position, but limit to Director_Rate
'      Add Error to current position
'      Continue with gun calculations
'   Decrement_Director
'      Error = desired position - current position, but limit to Director_Rate
'      Subtract Error from current position
' Gun Calculations
'   For each gun control group
'     If current position >= desired position then go to Decrement_Gun
'       Error = current position - desired position, but limit to Turret_Rate
'       Add Error to current position
'       Continue with gun calculations
'     Decrement_Gun
'       Error = desired position - current position, but limit to Director_Rate
'       Subtract Error from current position
'   End of subroutine

Slew_to_Bearing:
  Director:
    IF (Current_Pos(0) >= Desired_Pos(0)) THEN Decrement_D
      Error = Desired_Pos(0) - Current_Pos(0) MAX Director_Rate
      Current_Pos(0) = Current_Pos(0) + Error
      GOTO Guns
    Decrement_D:
      Error = Current_Pos(0) - Desired_Pos(0) MAX Director_Rate
      Current_Pos(0) = Current_Pos(0) - Error
  Guns:
    FOR Index = 1 TO 3
      IF (Current_Pos(Index) >= Desired_Pos(Index)) THEN Decrement_G
        Error = Desired_Pos(Index) - Current_Pos(Index) MAX Turret_Rate
        Current_Pos(Index) = Current_Pos(Index) + Error
        GOTO Done_Slewing
      Decrement_G:
        Error = Current_Pos(Index) - Desired_Pos(Index) MAX Turret_Rate
        Current_Pos(Index) = Current_Pos(Index) - Error
      Done_slewing:
    NEXT
  RETURN

In layman's language, a real wwII Gunfire Control System (GFCS) work basically as follows.

A gun director is used to track the target that is to be engaged. Once a target is tracked, the measured data is fed into a calculator (an analog computer) along with own ship data and ballistic properties of the controlled guns. The computer then calculates a predicted hitting point in which the fired projectile and the target will "meet". Control signals then pointed the turrets so that they fired at that calculated point.

For a scale GFCS, I would like the same overall functionality. First, have free control over the gun director from the transmitter. And be able to assign (or slave) the guns to the director.

Can I ask why you decided to move to stepper motors?

I would have thought that the beauty of using a servo is that it's a self contained positioning system. Just "tell it" the angle you want and it goes there. With a stepper you would need to include a method of determining its rotational position, some gearing and a motor driver - all of which you get as part of a servo.

I think an UNO can control up to 12 servos.

AdaFruit sell a 16 channel servo controller board that is controlled via I2C so you can offload the control to that board whilst your Arduino handles the computations needed:

Oh, and thumbs up from me for a great looking ship! :+1:

One of the details is each individual gun has independent elevation movement. Do you also scale that function?

I agree servos would seem to be a better approach. Thanks for the suggestion.

Yes. I would like to scale the individual gun elevations too if it is easy to do so. Mechanically it is easy for me to install individual micro servos on each barrel.

I have a Maestro 12 channel servo controller / driver board, arduinos unos and mega.

Does anyone know where I can find code to control servos using a RC receiver and transmitter and an arduino? Specifically, I want to be able to move a stick/switch on my Spektrum transmitter, that moves a corresponding servo via arduino commands (it would be a great if the code includes the ability to control speed, accelration and position, but I don't want to ask for too much). You would think that this would be easy to find on this website or the internet, but I cannot find it.

Still waiting for pictures showing the stepper motors mounted in your model.

I would like to use servos instead and will make necessary mounting hardware changes.

But here's some pictures of the existing motors under the superstruture - again, I'm thinking that servos are the way to go, so I don't plan on using either these motors or steppers.


Hello ovalracer

Welcome to the world's best Arduino forum ever.

Take a search engine of your choice and ask the WWW for 'Spektrum transmitter +arduino' to collect data to be sorted out to get the needed information.

Have a nice day and enjoy coding in C++.

Thanks for the suggestion! I am studying the results of the search to see if any of these articles will work. I don't see any obvious answers. It is very confusing to me.

For example: "Using the Spektrum Satellite Protocol in Arduino". ...satellite protocall?... Does my spectrum dx9 use satellite protocal?..

You mean you couldn't get that code to work properly on a BASIC STAMP?

Or you tried to convert the code to Arduino/C++ and couldn't get that to work?

On Basic Stamp, the code ran, but it did not move the servos as intended.

We didn't try to convert it to C++ because I don't know enough about C++ to know if it would work.

Ah, but do you mean didn't work as you wanted it to, or didn't work as the original author intended it to?

What did it do, if anything?

What did you want it to do?

It didn't work as everyone intended. It moved the servos, but not to the correct position(s).