Im working on a project to Electrify the boat motor controls on my boat its a small trailer sailer. It has an outboard motor on the back . The boat has fairly tall sides and Its really difficult to keep a view on where you're going while your trying to reach outside the cockpit behind you to change the gear position . The throttle control is a tiller type that you twist . It too is hard to keep in your hands while holding and steering mostly with the sailboats rudder . I Have most of the mechanical aspects figured out and I'm not sure if I will need a small magnetic reed switch to serve as an "In Neutral Sensor" so far the Stepper motor controlling the gearbox on the Outboard is only doing moderately well keeping up with the Optical rotary encoder. The program is a little more complex because I Decided I want one lever that you push forward (connected to the Optical rotary Encoder) to proceed forward in gear with the boat and the same lever that you pull all the way back to proceed in reverse . To accomplish this the program has to take that rotary encoder info and real time it to both the stepper and the Servo. Reversing it and scaling it to open the throttle with the servo after the boat has been shifted into forward or reverse by the stepper . I have finished writing the code and am now looking for any clever Ideas to improve it. The biggest disappointment that I'd like to somehow fix is The Issue of lost steps from moving the encoder too fast for the Arduino Uno. My Sketch has 2 Parts: a Main Program and a Diagnostic Mode.. I upload to the Arduino changing Diagnostic mode to a 1 or a Zero to use the controls or see serial data in order to assist in troubleshooting the code .. Diagnostic mode doesn't work very well but It's helpful to a very small degree. I'd love to hear from any one who might have Ideas for improving this sketch in such a way as to true up the step count at each practical spot in the program . I have it set up so that the rotary encoder counter resets to 0 when close to Neutral so that there is a rather large dead band at the Neutral position. This seems to help with the issue but only a little . after 4 or 5 times from forward to reverse and back to Neutral . Neutral always comes up in a different spot, maybe that's as good as it gets but being a Novice I had to ask if there is an Idea I'm overlooking so I don't have to add the complexity of a Neutral indicator to the motor . Anyway here is the code I apologize in advance
#include <AccelStepper.h>
#include <Servo.h>
Servo throttleServo; // create servo object to control a throttleServo
#define motorInterfaceType 1
#define startIndicator 3 //on stepper driver
#define servoPin 4 // on spare stepper driver
#define stepperMove 2//this was 5 and is now 2 for cnc uno
#define outputA 6 // output of the rotry encoder input of the ardrino lower left of stepper driver
#define outputB 7// output of the rotry encoder input of the ardrino lower left of stepper driver
#define outputC 1// this will be a momentary switch when pressed will zero the counter maby
#define stepperEnable 8// labled on shield
#define stepperDirecton 5//; juat changed from 9 for uno cnc shield
int currentStateA;
AccelStepper ForwardReverseStepper(motorInterfaceType, stepperMove, stepperDirecton);
int counter;
int lastStateA;
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int controlMultiplier =1; ;//various spots throuout program. The counter will be amplified
//to result in less or more control travel for same control movment
// so far counter Multipliers are F,R,FN,RN
int F = 4;
int R = 4;
int FN = 3.4;
int RN = 3.4;
//#####################################################################
int diagnosticMode =0;// change to 1 to enter diagnostic test mode
//####################################################################
// the next 4 lines are map constants
int X = 500; // servo throtle in forward "From High" portion of map instruction
int Y = 4900; // servo throtle in forward "to High" portion of map instruction
int Z = 10; // stepper control between reverse and Nutral also forward and nutral "from high"
int D = 400; // stepper control between reverse and Nutral also forward and nutral "to thgh"
// the next 2 lines are the exzact number of steps away from 0 that we want to be in gear
// so figue out the exzact number of steps to forward
//to be perfectly in forward from Nutral vise versa from 0 to Reverse
int expectedCCWCurrentStepperPosision = 1300;
int expectedCWCurrentStepperPosision = -1300;
int A;
int B;
int C ;
int programSpot; //used in diagnostic mode only should help
//identify what part of program Im in
// the next line is the highest the servo will travel set to prevent exceding
//max throttle in miliseconds
int fullThrottleServoPosision = 2500;
// the next line is the lowest the servo will travel
//set to prevent exceding idle throttle in miliseconds
int IdleRPMhServoPosision = 1000;
// The shifter stepper has to be kept below a certin
//point to not shift past forward gear
// this next line is a (counter point) it must be set high
//enough so that the manual control feel is there
// however the stepper stops at this point and then procedes
//to an exzact number of steps that we want for our gear posision in forward
// "expectedForwardCurrentStepperPosision",IS a stepper (number of steps)
//it's that secondary number that actually determins that gear
// posision. If you change this you must first change
//the other to be just above or below this,and you
// must convert forom counter to number of steps to change the other
int highStepperPosision = 90 ;
// Like above only backwards the next line
// is the (counter pointer) that the stepper
//will stop moving under
// control of the lever
// and begin to advance to "'or retreat to'" the reverse posision .Furthermore
// the actual posision is
//expressed in steps and determind by "expectedCCWCurrentStepperPosision"
int LowhstepperPosision = -90;
int ForwardGearStop = 60; // gear shifting the spot at wich you have gone into
//forward gear or out
//of forward gear ...
//
// Just like above the Reverse gear stop is were we wish to stop controling gears and advance the
// trottle reverse or stop controling the throtle and change gears depending on how you look at it
int ReverseGearStop = -60; // gear shifting the spot at wich you have gone into R gear or out
//of reverse
int forwardThrotleStart=49; //This is a counter pointer for the beginning of throtle movment in forward
int reverseThrottleStart=-49 ; //This is a counter pointer for the beginning of throtle movment in forward
int startServoPosision = 1000;//Kind of self explanitory its were we want the throtle
//to be set when we pull on the starter rope.
int currentStepperPosision = 0;// 1500 microseconds is writen as 1500. Every time the program starts
// we assume the stepper is in 0 or Nutral center
int currentServoPosision = IdleRPMhServoPosision;// we will start this at idle
//unless the start swich is closed
int comingOutofNutralTowardsForward = 20;
int comingOutofNutralToReverse = -20;
int forceToNutralLowSpot=-7;
int forceToNutralHighSpot=7;
void(* resetFunc) (void) = 0;
void setup() {
ForwardReverseStepper.setMaxSpeed(900000);
ForwardReverseStepper.setAcceleration(900000);
ForwardReverseStepper.setSpeed(900000);
ForwardReverseStepper.moveTo(0);
pinMode(stepperEnable, OUTPUT);
pinMode(stepperDirecton, OUTPUT);
pinMode(stepperMove, OUTPUT);
pinMode(startIndicator, INPUT_PULLUP);
pinMode(outputA, INPUT);// this is output from the encoder input to the controler
pinMode(outputB, INPUT);// this is output from the encoder input to the controler
Serial.begin(1200);
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// Read the initial state of outputA on the rotory encoder and declare
//it to be identical to to the last state of A
lastStateA = digitalRead(outputA);
}
//**************************************************************************************************************
void loop()
//*********************************************************************************************************************
{
if (diagnosticMode != 1 ) {
theMainprogram();
}
else{
tests();
}
}
void theMainprogram()
{
currentStateA = digitalRead(outputA);// program started
// Read A again did it change?
if (currentStateA != lastStateA && currentStateA == 1) {
if (digitalRead(outputB) != currentStateA) {
counter =(counter+controlMultiplier);// increased from 1 to 2.2 because too many revolutions currentDir = "CW";
} else {
// Encoder is rotating CW so increment
counter =(counter-controlMultiplier);// increased from 1 to 2.2 because too many revolutions
//currentDir = "CW";currentDir = "CCW";
}
}
// **********************************************************************
//**********************************************************************************
// the servo control for throtle in forward ............. ie rotating CW Blue 7 White 6
//***********************************************************************************
// **********************************************************************
if (counter > forwardThrotleStart) {
controlMultiplier =F;
// next 3 lines are to give the stepper a last chance to
//get that stepper to the desired gear if
// it did not make it there due to lost
//steps ie moving lever too fast!!
// right now however were all about
//throtle control for servo in forward
// and these 3 lines are ancalary
ForwardReverseStepper.moveTo(expectedCWCurrentStepperPosision);
while (ForwardReverseStepper.currentPosition() != expectedCWCurrentStepperPosision)
ForwardReverseStepper.run();
// previous 3 lines are reason why its inportant to alow for
//lost steps stepper motor has no closed loop operation
// it will loose steps its inevatable
//during a while comand so proper adjustment of forward gear stop and
//expectedCWCurrentStepperPosision is criticaly dependant on one another
// they need to be close together
digitalWrite(stepperEnable, HIGH);// disable the shifter stepper .High disables. We
//are now about to throtle up in forward
throttleServo.attach(servoPin); // enable throttle servo attaches the servo on pin servoPin
//exmap(counter, fromLow, fromHigh, toLow, toHigh)
A = map(counter, 0 , X, 0, Y);// should amplify the throttle posision a higher
//"to high"moves throtle faster
//might help it stay cought up but the more you
//amplify the more you amplify the error result from
// lost steps
B = A - 1 ; // I thought this was going to be b = a minus the
// reverse gear stop so throttle 0 would be counter zero its not
// wkg why ?so just droped in a 0 to make it work
// leaving in in case will need.
//
// Next linelimits range of values
//to between Idle rpm and full throtle
B = constrain(B, IdleRPMhServoPosision , fullThrottleServoPosision);
currentServoPosision = B; // a declaration
throttleServo.writeMicroseconds(currentServoPosision); // moves the servo position according
// to the
// scaled
}
// **********************************************************************
//**************************************************************************************
// Servo control for throtle in reverse...................... Encoder is CCW Blue 7 White 6
// ****************************************** ******************************************
// **********************************************************************
if (counter <= reverseThrottleStart) {
controlMultiplier =R;
// next 3 lines are a last chance to get that stepper to the desired gear
ForwardReverseStepper.moveTo(expectedCCWCurrentStepperPosision);// this variable
//must be set carfully
// see coments control multiplier
while (ForwardReverseStepper.currentPosition() != expectedCCWCurrentStepperPosision)
ForwardReverseStepper.run();
digitalWrite(stepperEnable, HIGH); //disable stepper, High disables we
//are soon to throtle up in reverse
throttleServo.attach(servoPin); // enable throttle servo
// this attaches the
//servo on pin servoPin
A = map(counter, 0 , X, 0, -Y);
B = A - 0;// I thought this was going to be B equal to "a minus
//reverse gear stop" so throttle 0 would be counter zero seems not matter
// next linelimits range of values to between Idle rpm and full throtle
B = constrain(B, IdleRPMhServoPosision , fullThrottleServoPosision);
currentServoPosision = B;
throttleServo.writeMicroseconds(currentServoPosision);
}
// **********************************************************************
//***********************************************************************
// (Stepper) control between Forward and nutral
// **********************************************************************
// **********************************************************************
if ((counter <= ForwardGearStop ) && (counter > comingOutofNutralTowardsForward)) {
controlMultiplier =FN;
digitalWrite(stepperEnable, LOW);// low enables
throttleServo.writeMicroseconds(currentServoPosision); //one last chance to get that
//throtle servo to the Idle posision
throttleServo.detach();// disable servo
// exmap(counter, fromLow, fromHigh, toLow, toHigh)
C = map(counter, 0 , Z, 0, D); //stepper should respond faster as tolow and to high
// get farther appart
// next linelimits range of values to between reverse and Nutral
C = constrain(C, LowhstepperPosision , highStepperPosision); //
digitalWrite(stepperEnable, LOW);// stepper Low Enables we
//are soon to move gears
currentStepperPosision = C;
ForwardReverseStepper.moveTo(currentStepperPosision);
ForwardReverseStepper.run();
}
// **********************************************************************
//***********************************************************************
// (Stepper) control between reverse and nutral
// **********************************************************************
// **********************************************************************
if ((counter >= ReverseGearStop ) && (counter < comingOutofNutralToReverse)) {
controlMultiplier =RN;
digitalWrite(stepperEnable, LOW);// low enables
throttleServo.writeMicroseconds(currentServoPosision); //one last chance to get that
///hrotle servo to the Idle posision
throttleServo.detach();// disable servo
// exmap(counter, fromLow, fromHigh, toLow, toHigh)
C = map(counter, 0 , -Z, 0, -D); //stepper should respond faster as "fromhigh" and "tohigh"
// get farther appart
// next linelimits range of values to between reverse and Nutral
C = constrain(C, LowhstepperPosision , highStepperPosision); //
currentStepperPosision = C;
ForwardReverseStepper.moveTo(currentStepperPosision);
ForwardReverseStepper.run();
}
//***********************************************************************
//***********************************************************************
// (Stepper) control between comingOutofNutralToReverse and 0
// **********************************************************************
//***********************************************************************
if ((counter >= comingOutofNutralToReverse ) && (counter <= forceToNutralLowSpot))
{
digitalWrite(stepperEnable, LOW);// low enables
while (ForwardReverseStepper.currentPosition() != 0){
ForwardReverseStepper.moveTo(0);
ForwardReverseStepper.run();
digitalWrite(stepperEnable, LOW);// low enables
counter = 0;
}
}
//***********************************************************************
//***********************************************************************
// (Stepper) control between 0 and comingOutofNutralToForward
// **********************************************************************
//***********************************************************************
if ((counter >= forceToNutralHighSpot ) && (counter <= comingOutofNutralTowardsForward))
{
digitalWrite(stepperEnable, LOW);// low enables
while (ForwardReverseStepper.currentPosition() != 0){
ForwardReverseStepper.moveTo(0);
ForwardReverseStepper.run();
counter = 0;
}
}
lastStateA = currentStateA;
startCheck;
}
void startCheck() {
while (digitalRead (startIndicator) == 0){
throttleServo.attach(servoPin); // attaches the servo on pin servoPin
throttleServo.writeMicroseconds(startServoPosision);// in microseconds
throttleServo.detach();// disable servo
}
if ((digitalRead (startIndicator) == 1) ) {
throttleServo.attach(servoPin); // attaches the servo on pin servoPin
throttleServo.writeMicroseconds(currentServoPosision);// in microseconds
throttleServo.detach();// disable servo
}
}
///^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
///^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
///^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
/// The test program
///^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
///^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
///^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
void tests() {
currentStateA = digitalRead(outputA);// program started
// Read A again did it change?
if (currentStateA != lastStateA && currentStateA == 1) {
if (digitalRead(outputB) != currentStateA) {
counter =(counter-controlMultiplier);// increased from 1 to 2.2 because too many revolutions currentDir = "CW";
} else {
// Encoder is rotating CW so increment
counter =(counter+controlMultiplier);// increased from 1 to 2.2 because too many revolutions
//currentDir = "CW";currentDir = "CCW";
}
if ((digitalRead (startIndicator) == 1) ) {
Serial.println();
Serial.println("program spot is ");
Serial.println(programSpot);
Serial.println();
Serial.println(" controlMultiplier");
Serial.println(controlMultiplier);
Serial.println();
Serial.println("The counter is ");
Serial.println(counter);
Serial.println();
Serial.println("Stepper posision is ");
Serial.println(currentStepperPosision);
Serial.println();
Serial.println("servo posision is ");
Serial.println(currentServoPosision);
Serial.println();
}
//**********************************************************************************
// the servo control for throtle in forward =1 ............. ie rotating CW Blue 7 White 6
//***********************************************************************************
if (counter > forwardThrotleStart) {
controlMultiplier =F;
programSpot = 1;
// next 3 lines are to give the stepper a last chance to get that stepper to the desired gear if
// it did not make it there due to lost steps ie moving leSver too fast
// ForwardReverseStepper.moveTo(expectedCWCurrentStepperPosision);
Serial.println(" 1");
//while (ForwardReverseStepper.currentPosition() != expectedCWCurrentStepperPosision)
// ForwardReverseStepper.run();
Serial.println(" 1");
// previous 3 lines are why its inportant to alow for lost steps stepper motor has no closed loop operation
// it will loose steps
//during a while comand . This is why proper adjustment of forward gear stop and
//expectedCWCurrentStepperPosision is criticaly dependant on one another
// digitalWrite(stepperEnable, HIGH);// disable the shifter stepper .High disables. We
Serial.println(" 1"); //are now to throtle up in forward
// throttleServo.attach(servoPin); // enable throttle servo attaches the servo on pin servoPin
//exmap(counter, fromLow, fromHigh, toLow, toHigh)
A = map(counter, 0 , X, 0, Y);// should amplify the throttle posision a higher
//"to high"moves throtle faster
B = A - 1 ; // I thought this was going to b = a minus
//reverse gear stop so throttle 0 would be counter zero
// next linelimits range of values to between Idle rpm and full throtle
B = constrain(B, IdleRPMhServoPosision , fullThrottleServoPosision);
currentServoPosision = B; // a declaration
Serial.println("1");
// throttleServo.writeMicroseconds(currentServoPosision); // moves the servo position according to the scaled
Serial.println(" 1");
}
//**************************************************************************************
// Servo control for throtle in reverse= 2...................... Encoder is CCW Blue 7 White 6
// ****************************************** ******************************************
if (counter < reverseThrottleStart) {
controlMultiplier =R;
programSpot = 2;
// next 3 lines are a last chance to get that stepper to the desired gear
// ForwardReverseStepper.moveTo(expectedCCWCurrentStepperPosision);// this variable must be set carfully
//while (ForwardReverseStepper.currentPosition() != expectedCCWCurrentStepperPosision)
ForwardReverseStepper.run();
Serial.println(" 2");
//digitalWrite(stepperEnable, HIGH); //disable stepper high disables we
//are soon to throtle up in reverse
//throttleServo.attach(servoPin); // enable throttle servo attaches the servo on pin servoPin
A = map(counter, 0 , X, 0, -Y);
B = A - 0;// I thought this was going to b equal to a plus
//reverse gear stop so throttle 0 would be counter zero seems not matter
// next linelimits range of values to between Idle rpm and full throtle
B = constrain(B, IdleRPMhServoPosision , fullThrottleServoPosision);
Serial.println(" 2");
currentServoPosision = B;
//throttleServo.writeMicroseconds(currentServoPosision);
}
//***********************************************************************
// (Stepper) control between Forward and nutral=3
// **********************************************************************
if ((counter <= ForwardGearStop ) && (counter > comingOutofNutralTowardsForward)) {
controlMultiplier =FN;
programSpot = 3;
digitalWrite(stepperEnable, LOW);// low enables
//throttleServo.writeMicroseconds(currentServoPosision); //one last chance to get that
Serial.println(" 3 "); ///hrotle servo to the Idle posision
//throttleServo.detach();// disable servo
Serial.println(" 3");
// exmap(counter, fromLow, fromHigh, toLow, toHigh)
C = map(counter, 0 , -Z, 0, -D); //stepper should respond faster as "tolow" and "tohigh"
// get farther appart
// next linelimits range of values to between reverse and Nutral
C = constrain(C, LowhstepperPosision , highStepperPosision); //
currentStepperPosision = C;
ForwardReverseStepper.moveTo(currentStepperPosision);
// while (ForwardReverseStepper.currentPosition() != currentStepperPosision)
ForwardReverseStepper.run();
Serial.println(" 3");
}
//***********************************************************************
// (Stepper) control between reverse and nutral=4
// **********************************************************************
if ((counter >= ReverseGearStop ) && (counter < comingOutofNutralToReverse)) {
controlMultiplier =RN;
programSpot = 4;
digitalWrite(stepperEnable, LOW);// low enables
throttleServo.writeMicroseconds(currentServoPosision); //one last chance to get that
Serial.println(" 4 "); ///hrotle servo to the Idle posision
//throttleServo.detach();// disable servo
// exmap(counter, fromLow, fromHigh, toLow, toHigh)
C = map(counter, 0 , -Z, 0, -D); //stepper should respond faster as "tolow" and "toHigh"
// get farther appart
// next linelimits range of values to between reverse and Nutral
C = constrain(C, LowhstepperPosision , highStepperPosision); //
currentStepperPosision = C;
// ForwardReverseStepper.moveTo(currentStepperPosision);
// while (ForwardReverseStepper.currentPosition() != currentStepperPosision)
// ForwardReverseStepper.run();
Serial.println("4");
}
//***********************************************************************
// (Stepper) control between comingOutofNutralToReverse and 0
// **********************************************************************
if ((counter >= comingOutofNutralToReverse ) && (counter <= forceToNutralLowSpot))//forceToNutralHighSpot
{
programSpot = 5;
digitalWrite(stepperEnable, LOW);// low enables
currentStepperPosision = 0;
counter = 0;
Serial.println(" 5");
//while (ForwardReverseStepper.currentPosition() != 0){
//ForwardReverseStepper.moveTo(0);
//ForwardReverseStepper.run();
///digitalWrite(stepperEnable, LOW);// low enables
}
}
//***********************************************************************
// (Stepper) control between 0 and comingOutofNutralToForward
// **********************************************************************
//if ((counter >= forceToNutralHighSpot ) && (counter <= comingOutofNutralTowardsForward))
{
programSpot = 6;
digitalWrite(stepperEnable, LOW);// low enables
//currentStepperPosision = 0;
//counter = 0;
Serial.println(" 6");
//while (ForwardReverseStepper.currentPosition() != 0){
//ForwardReverseStepper.moveTo(0);
//ForwardReverseStepper.run();
//digitalWrite(stepperEnable, LOW);// low enables
// }
//}
lastStateA = currentStateA;
startCheck;
}
}