Hi. I'm struggling to understand why a Feather M0 controlled Halloween prop I've been making occasionally stops working at the point in a switch() structure where a pneumatic valve should be activated.
When a trigger pulse is detected, the Feather turns on a light in the coffin and triggers an MP3 board to start playing spooky sounds. The Feather then switches a linear actuator (via an electromechanical power and a direction relay) to open the coffin lid. After a limit switch detects that the coffin lid is fully open, the Feather turns off the two linear actuator relays and applies power to the pneumatic valve to make the zombie pop out (... and say some spooky stuff, pop back into his coffin, and the coffin lid closes).
This all typically works fine about 80% of the time, but the rest of the time the program hangs up at (or shortly before) the same point where the pneumatic valve should be activated.
I've tested my limit switches. I'm beginning to wonder if a disruptive inductive fly-back voltage spike is being generated when the linear actuator, or its power and direction relays, are turned off. As a precaution I staggered the relay switching times by 50 ms to avoid concurrent changes. In addition to the routine diode across the relay coils, I also put a simple snubber circuit (a 43 ohm resistor in series with a 0.01 uF capacitor) across the linear actuator motor. As far as I can tell, these precautions have had no effect.
While I continue to suspect a hardware-based problem, I'd appreciate a sanity check as to whether there's something I'm not understanding about my code which might interfere with the sketch getting to the "sc_Extend" case where it normally would turn on the pneumatic valve.
Any advice about my code or suggestions for diagnosing and fixing the root cause would be greatly appreciated!
#define OpeningLightSoundPeriod 5000 // Pre-opening sound period in ms
#define ExtraLidMovementTime 3000 // Extra time (ms) to make sure lid fully opens/closes
#define PostTalkingPauseTime 4000 // Time (ms) for zombie to chill after saying his piece
#define PostLidClosurePauseTime 2000 // Time (ms) from lid closure to light & sound off
#define PostLightOffPauseTime 5000 // Time (ms) from light off to sound off after lid closure
#define RUNstop 0
#define OPENclose 1
#define WaveLight 5
#define EXTENDretract 6
#define LidOpen 9
#define LidClosed 10
#define Trigger 11
#define Extended 12
#define Retracted 17 // Formerly 13
#define Mp3Trigger 14 // FN-BC10 Level Hold Loop Playback (Mode 1). Hold trigger pin low for playback duration
#define HdMp3Busy 15
#define CofMp3Busy 16
const byte sc_AwaitingTrigger = 1; // If triggered, turn on WaveLight & lower CofMp3 trigger (sound begins)
const byte sc_StartOpeningLid = 2; // Wait 5 sec before opening coffin lid
const byte sc_FinishOpeningLid = 3; // If OPEN limit switch = HIGH, start 3 sec extra movement timer
const byte sc_Extend = 4; // Disable lid actuator. Turn on PValve (zombie begins to rise)
const byte sc_HdMp3Busy = 5; // Wait until HdMp3 is LOW (busy, zombie starts talking)
const byte sc_HdMp3NotBusy = 6; // Wait until HdMp3 is HIGH (not busy, zombie stops talking)
const byte sc_StartRetracting = 7; // Wait 4 sec then turn off PValve (begin to retract)
const byte sc_StartClosingLid = 8; // If RETRACTED limit switch is HIGH, start closing lid
const byte sc_LidClosed = 9; // If CLOSED limit switch is HIGH, start 2 sec post closure timer
const byte sc_LightOff = 10; // Turn off WaveLight then start 5 sec post light timer
const byte sc_SoundOff = 11; // Raise CofMp3 trigger (coffin sound off). Loop back to await next trigger
byte CoffinStepNo = sc_AwaitingTrigger;
unsigned long MillisMoment;
void setup() {
pinMode(RUNstop, OUTPUT);
pinMode(OPENclose, OUTPUT);
pinMode(WaveLight, OUTPUT);
pinMode(EXTENDretract, OUTPUT);
pinMode(LidOpen, INPUT_PULLUP);
pinMode(LidClosed, INPUT_PULLUP);
pinMode(Trigger, INPUT_PULLUP);
pinMode(Extended,INPUT_PULLUP);
pinMode(Retracted, INPUT_PULLUP);
pinMode(Mp3Trigger, OUTPUT);
pinMode(HdMp3Busy, INPUT); // Active low
pinMode(CofMp3Busy, INPUT); // Active low
digitalWrite(RUNstop, LOW); // Initialize output levels
digitalWrite(OPENclose, LOW);
digitalWrite(WaveLight, LOW);
digitalWrite(EXTENDretract, LOW);
digitalWrite(Mp3Trigger, HIGH);
// put your setup code here, to run once:
}
void loop() {
switch (CoffinStepNo)
{
case sc_AwaitingTrigger:
if(digitalRead(Trigger) == HIGH)
{
digitalWrite(WaveLight, HIGH); // Turn on Wave Light
digitalWrite(Mp3Trigger, LOW); // Initiate falling edge of MP3 board trigger
MillisMoment = millis(); // Capture time to measure pre-opening light/sound period
CoffinStepNo = sc_StartOpeningLid; // Point to next switch state
}
break; // Jump to end of switch
case sc_StartOpeningLid:
if(millis() - MillisMoment >= OpeningLightSoundPeriod)
{
digitalWrite(OPENclose, HIGH); // Switch linear actuator power route to open lid
delay(50);
digitalWrite(RUNstop, HIGH); // Apply power to run the linear actuator
delay(50);
CoffinStepNo = sc_FinishOpeningLid; // Point to next switch state
}
break; // Jump to end of switch
case sc_FinishOpeningLid:
if(digitalRead(LidOpen) == HIGH) // If coffin lid limit switch has opened
{
MillisMoment = millis(); // Capture time to allow time to fully open
CoffinStepNo = sc_Extend; // Point to next switch state
}
break;
case sc_Extend: // Jump to end of switch
if(millis() - MillisMoment >= ExtraLidMovementTime) // If extra lid movement time has passed
{
digitalWrite(RUNstop, LOW); // Switch off power to linear actuator
delay(50);
digitalWrite(OPENclose, LOW); // Power off linear actuator direction relay
delay(50);
digitalWrite(EXTENDretract, HIGH); // Set pneumatic valve to extend the scissor mechanism
CoffinStepNo = sc_HdMp3Busy; // Point to next switch state
}
break; // Jump to end of switch
case sc_HdMp3Busy:
if(digitalRead(HdMp3Busy) == LOW) // Wait for head Mp3 player to get active (zombie's talking)
{
CoffinStepNo = sc_HdMp3NotBusy; // Point to next switch state
}
break; // Jump to end of switch
case sc_HdMp3NotBusy:
if(digitalRead(HdMp3Busy) == HIGH) // If zombie has finished talking
{
MillisMoment = millis(); // Capture time to allow time for zombie to chill
CoffinStepNo = sc_StartRetracting; // Point to next switch state
}
break; // Jump to end of switch
case sc_StartRetracting:
if(millis() - MillisMoment >= PostTalkingPauseTime) // If it'a timw for the zombie to go back home
{
digitalWrite(EXTENDretract, LOW); // Set pneumatic valve to retract the scissor mechanism
CoffinStepNo = sc_StartClosingLid; // Point to next switch state
}
break; // Jump to end of switch
case sc_StartClosingLid:
if(digitalRead(Retracted) == HIGH) // If limit switch indicates scissor mechanism has retracted
{
digitalWrite(OPENclose, LOW); // Set linear actuator direction relay to close the lid
delay(50);
digitalWrite(RUNstop, HIGH); // Switch on power to the linear actuator
delay(50);
CoffinStepNo = sc_LidClosed; // Point to next switch state
}
break; // Jump to end of switch
case sc_LidClosed:
if(digitalRead(LidClosed) == HIGH) // If limit switch indicates lid has closed
{
MillisMoment = millis(); // Capture time to measure post-closure delay
CoffinStepNo = sc_LightOff; // Point to next switch state
}
break; // Jump to end of switch
case sc_LightOff:
if(millis() - MillisMoment >= PostLidClosurePauseTime) // If desired time has passed since lid closed
{
digitalWrite(RUNstop, LOW); // Turn off RUNstop relay
delay(50); // Debounce 50 ms
digitalWrite(WaveLight, LOW); // Turn off Wave Light
MillisMoment = millis(); // Capture time to measure post-light delay
CoffinStepNo = sc_SoundOff; // Point to next switch state
}
break; // Jump to end of switch
case sc_SoundOff:
if(millis() - MillisMoment >= PostLightOffPauseTime)
{
digitalWrite(Mp3Trigger, HIGH); // Disable Coffin MP3 player
CoffinStepNo = sc_AwaitingTrigger; // Loop back to await next trigger
}
break; // Jump to end of switch
}
}