Here is the new version
//move_signals_17
//https://forum.arduino.cc/t/programming-for-realistic-semaphore-control-using-pca-9685/1283543
//NOTE : dangerPWM must always be less than clearPWM
//https://forum.arduino.cc/t/programming-for-realistic-semaphore-control-using-pca-9685/1283543/54
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver signalBoard01 = Adafruit_PWMServoDriver(0x40);
enum states
{
AT_DANGER, //each signal can be in any one of the following states
AT_CLEAR,
MOVING_TO_DANGER,
MOVING_TO_CLEAR,
BOUNCING_AT_DANGER,
BOUNCING_AT_CLEAR
};
struct signalData //struct for signal variables
{
int currentPWM; //current PWM value
unsigned long lastMoveTime; //time of last move
states currentState; //current state of signal
boolean moveDone; //true if the move to a new state is finished
boolean bounceDone; //flag to prevent bouncing being repeated
int bounceCount; //counter for bounces
boolean bounceAway; //true if bouncing away from end position
int bounceDistance; //distance to bounce
};
struct signalDefs //signal definitions
{
const byte swPinNum; //switch pin number
const int clearPWM; //PWM value for clear signal
const int dangerPWM; //PWM value for danger signal
const byte pwmChannel; //servo channel number
const byte dangerInterval; //interval between steps to danger. Lower values are faster
const byte clearInterval; //unterval between steps to clear
const unsigned int dangerBounceInterval; //interval between danger bounces
const byte dangerNumBounces; //number of bounces to perform at danger
const byte dangerBouncePercent; //percentage of total movement to bounce when reaching danger
const unsigned int clearBounceInterval; //interval between danger bounces
const byte clearNumBounces; //number of bounces to perform at danger
const byte clearBouncePercent; //percentage of total movement to bounce when reaching danger
};
signalDefs signals[] = {
{A3, 150, 115, 0, 15, 10, 100, 5, 40, 200, 0, 50 }, //bounce at clear only
{ A2, 210, 110, 1, 15, 45, 200, 5, 30, 200, 8, 30 }, //bounce at both
{ A1, 800, 110, 15, 15, 45, 200, 8, 10, 200, 0, 40 }, //bounce at danger only
};
const byte signalCount = sizeof(signals) / sizeof(signals[0]);
signalData sigVars[signalCount];
unsigned long currentTime;
void setup()
{
Serial.begin(115200);
Serial.println("GingerAngles Signal Control!");
signalBoard01.begin();
signalBoard01.setPWMFreq(50); // Analog servos run at ~60 Hz updates
initialse();
}
void loop()
{
for (int s = 0; s < signalCount; s++)
{
currentTime = millis();
byte currentSwitchState = digitalRead(signals[s].swPinNum);
switch (sigVars[s].currentState)
{
case AT_DANGER:
if (signals[s].dangerNumBounces > 0 && sigVars[s].bounceDone == false)
{
sigVars[s].currentState = BOUNCING_AT_DANGER;
Serial.print(signals[s].pwmChannel);
Serial.println(" bouncing at danger");
// sigVars[s].bounceDone = false;
sigVars[s].lastMoveTime = currentTime + signals[s].dangerBounceInterval; //force immediate bounce BBB
sigVars[s].currentPWM = signals[s].dangerPWM;
sigVars[s].bounceCount = 0;
sigVars[s].bounceAway = true;
sigVars[s].bounceDistance = (signals[s].clearPWM - signals[s].dangerPWM) / (100 / signals[s].dangerBouncePercent); //initial bounce back distance
}
else if (currentSwitchState == LOW) //the switch is closed
{
sigVars[s].currentState = MOVING_TO_CLEAR;
Serial.print(signals[s].pwmChannel);
Serial.println(" moving to clear");
sigVars[s].moveDone = false;
sigVars[s].lastMoveTime = currentTime;
}
break;
case MOVING_TO_CLEAR:
moveTowardsClear(s);
if (sigVars[s].moveDone == true)
{
sigVars[s].currentState = AT_CLEAR;
Serial.print(signals[s].pwmChannel);
Serial.println(" arrived at clear");
sigVars[s].moveDone = false;
sigVars[s].bounceDone = false;
}
break;
case AT_CLEAR:
if (signals[s].clearNumBounces > 0 && sigVars[s].bounceDone == false)
{
sigVars[s].currentState = BOUNCING_AT_CLEAR;
Serial.print(signals[s].pwmChannel);
Serial.println(" bouncing at clear");
sigVars[s].lastMoveTime = currentTime +signals[s].clearBounceInterval; //force immediate bounce
sigVars[s].currentPWM = signals[s].clearPWM;
sigVars[s].bounceCount = 0;
sigVars[s].bounceAway = true;
sigVars[s].bounceDistance = (signals[s].clearPWM - signals[s].dangerPWM) / (100 / signals[s].dangerBouncePercent); //initial bounce back distance
}
else if (currentSwitchState == HIGH) //the switch is open
{
sigVars[s].currentState = MOVING_TO_DANGER;
Serial.print(signals[s].pwmChannel);
Serial.println(" moving to danger");
sigVars[s].moveDone = false;
sigVars[s].lastMoveTime = currentTime;
}
break;
case MOVING_TO_DANGER:
moveTowardsDanger(s);
if (sigVars[s].moveDone == true)
{
sigVars[s].currentState = AT_DANGER;
Serial.print(signals[s].pwmChannel);
Serial.println(" arrived at danger");
sigVars[s].moveDone = false;
sigVars[s].bounceDone = false;
}
break;
case BOUNCING_AT_CLEAR:
if (currentTime - sigVars[s].lastMoveTime >= signals[s].clearBounceInterval) //time to do another bounce
{
if (sigVars[s].bounceAway == true) //bounce away from clear - PWM is smaller when bounced away from clear
{
// Serial.print("bounceDistance ");
// Serial.println(sigVars[s].bounceDistance);
// Serial.print("clear PWM value ");
// Serial.println(signals[s].clearPWM - sigVars[s].bounceDistance);
// Serial.print("bounce # ");
// Serial.println(sigVars[s].bounceCount);
signalBoard01.setPWM(signals[s].pwmChannel, 0, signals[s].clearPWM - sigVars[s].bounceDistance);
sigVars[s].bounceAway = false; //next movement is back to clear
sigVars[s].lastMoveTime = currentTime;
sigVars[s].bounceCount++;
//sigVars[s].bounceDistance -= sigVars[s].bounceDistance / signals[s].clearNumBounces; //adjust bounce distance
sigVars[s].bounceDistance -= adjustBounceDistance(sigVars[s].bounceDistance, signals[s].clearNumBounces);
if (sigVars[s].bounceCount >= signals[s].clearNumBounces) //done required bounces ?
{
sigVars[s].currentState = AT_CLEAR;
sigVars[s].bounceDone = true;
signalBoard01.setPWM(signals[s].pwmChannel, 0, signals[s].clearPWM); //bounce back to clearPWM
Serial.print(signals[s].pwmChannel);
Serial.println(" stoppped bouncing at clear");
}
}
else //back to danger
{
signalBoard01.setPWM(signals[s].pwmChannel, 0, signals[s].clearPWM); //bounce back to clear
sigVars[s].lastMoveTime = currentTime;
sigVars[s].bounceAway = true;
}
} //end time to bounce
break;
case BOUNCING_AT_DANGER:
if (currentTime - sigVars[s].lastMoveTime >= signals[s].dangerBounceInterval) //time to do another bounce
{
if (sigVars[s].bounceAway == true) //bounce away from danger - PWM is smaller when bounced away from danger
{
// Serial.print("bounceDistance ");
// Serial.println(sigVars[s].bounceDistance);
// Serial.print("danger PWM value ");
// Serial.println(signals[s].dangerPWM + sigVars[s].bounceDistance);
// Serial.print("bounce # ");
// Serial.println(sigVars[s].bounceCount);
signalBoard01.setPWM(signals[s].pwmChannel, 0, signals[s].dangerPWM + sigVars[s].bounceDistance);
sigVars[s].bounceAway = false; //next movement is back to danger
sigVars[s].lastMoveTime = currentTime;
sigVars[s].bounceCount++;
//sigVars[s].bounceDistance -= sigVars[s].bounceDistance / signals[s].dangerNumBounces; //adjust bounce distance
sigVars[s].bounceDistance -= adjustBounceDistance(sigVars[s].bounceDistance, signals[s].dangerNumBounces);
if (sigVars[s].bounceCount >= signals[s].dangerNumBounces) //done required bounces ?
{
sigVars[s].currentState = AT_DANGER;
sigVars[s].bounceDone = true;
signalBoard01.setPWM(signals[s].pwmChannel, 0, signals[s].dangerPWM); //bounce back to dangerPWM
Serial.print(signals[s].pwmChannel);
Serial.println(" stoppped bouncing at danger");
}
}
else //back to danger
{
signalBoard01.setPWM(signals[s].pwmChannel, 0, signals[s].dangerPWM); //bounce back to danger
sigVars[s].lastMoveTime = currentTime;
sigVars[s].bounceAway = true;
}
} //end time to bounce
break;
} //end switch case
} //end for loop
}
byte adjustBounceDistance(byte distance, byte numBounces)
{
return distance / numBounces;
}
void moveTowardsClear(byte target) //consider adding code to move in the reverse direction based on an entry in the struct
{
if (currentTime - sigVars[target].lastMoveTime > signals[target].clearInterval) //time to move ?
{
signalBoard01.setPWM(signals[target].pwmChannel, 0, sigVars[target].currentPWM);
sigVars[target].lastMoveTime = currentTime; //save time move was made
sigVars[target].currentPWM++;
if (sigVars[target].currentPWM >= signals[target].clearPWM)
{
sigVars[target].moveDone = true;
}
}
}
void moveTowardsDanger(byte target) //consider adding code to move in the reverse direction based on an entry in the struct
{
if (currentTime - sigVars[target].lastMoveTime > signals[target].dangerInterval) //time to move ?
{
signalBoard01.setPWM(signals[target].pwmChannel, 0, sigVars[target].currentPWM);
sigVars[target].lastMoveTime = currentTime; //save time move was made
sigVars[target].currentPWM--;
if (sigVars[target].currentPWM <= signals[target].dangerPWM)
{
sigVars[target].moveDone = true;
}
}
}
void initialse()
{
boolean error = false;
for (int s = 0; s < signalCount; s++)
{
if (signals[s].dangerPWM > signals[s].clearPWM)
{
Serial.print("check clearPWM and dangerPWM values for signal ");
Serial.println(signals[s].pwmChannel);
error = true;
}
if (!error)
{
pinMode(signals[s].swPinNum, INPUT_PULLUP);
sigVars[s].currentState = AT_DANGER; //set all signals to danger
sigVars[s].currentPWM = signals[s].dangerPWM;
signalBoard01.setPWM(signals[s].pwmChannel, 0, sigVars[s].currentPWM);
sigVars[s].moveDone = true;
sigVars[s].bounceDone = true;
}
}
while (error)
{
}
}
There is no change to the data layout so your previous definitions can be copied into it
The main change is that bouncing should now start immediately when the signal reaches its target angle rather than pausing, and I have also fixed the problem caused by the data types for the PWM values not being able to cope with large values
Enjoy your break