#define MAX_INTERVAL 150 // max time a LED can be off while blinking, in ms
#define STABILITY_THRESHOLD 50 // time the input state must be stable before movement starts, in ms
#define DO_NOTHING 0x00
#define MOVE_UP 0x01
#define MOVE_DOWN 0x02
#define MOVE_FAST 0x04
#define MOVE_UPFAST (MOVE_UP | MOVE_FAST)
#define MOVE_DOWNFAST (MOVE_DOWN | MOVE_FAST)
// what to do for each combination of inputs; any combination of 2 or more active inputs means
// do nothing, only the combinations with exactly one active input, except the middle (neatral) input
// result in movement. These are positions 0x01 (1) for up fast, 0x02 (2) for up slow, 0x04 (4) for neutral,
// 0x08 (8) for down slow, 0x10 (16) for down fast. Note the array index starts at 0
const unsigned char Actions[ 32 ] =
{ DO_NOTHING, MOVE_UPFAST, MOVE_UP, DO_NOTHING, DO_NOTHING, DO_NOTHING, DO_NOTHING, DO_NOTHING, // 0..7
MOVE_DOWN, DO_NOTHING, DO_NOTHING, DO_NOTHING, DO_NOTHING, DO_NOTHING, DO_NOTHING, DO_NOTHING, // 8..15
MOVE_DOWNFAST, DO_NOTHING, DO_NOTHING, DO_NOTHING, DO_NOTHING, DO_NOTHING, DO_NOTHING, DO_NOTHING, // 16..23
DO_NOTHING, DO_NOTHING, DO_NOTHING, DO_NOTHING, DO_NOTHING, DO_NOTHING, DO_NOTHING, DO_NOTHING }; // 24..31
unsigned short InputCounters[ 5 ] = { 0, 0, 0, 0, 0 };
unsigned char ReadInputs, FilteredInputs, PreviousInputs = 0, AutomaticMode = 0, JoystickInput;
unsigned long StabilityCount = 0, ActivityCounter = 0;
int input_pushbutton = 1; // pushbutton for automatic mode
int input_joy_up = 2; // joystick UP
int input_joy_down = 3; // joystick DOWN
int input_top = 4; // input top LED
int input_up = 5;
int input_mid = 6;
int input_down = 7;
int input_bottom = 8; // input bottom LED
int led_error = 9; // Error output
int led_auto = 10; // LED for automatic mode
int output_up = 11; // move UP output
int output_down = 12; // move DOWN output
int output_slow = 13; // move SLOW output
int led_top = A0; // Indication lED move UP
int led_up = A1;
int led_mid = A2;
int led_down = A3;
int led_bottom = A4; // Indication lED move DOWN
int led_stable = A5; // LED for stable status
void setup()
{
// pin 0 unused; causes problems when loading new software
pinMode( input_pushbutton, INPUT ); // pushbutton for automatic mode
pinMode( input_joy_up, INPUT ); // joystick UP
pinMode( input_joy_down, INPUT ); // joystick DOWN
pinMode( input_top, INPUT ); // input top LED
pinMode( input_up, INPUT );
pinMode( input_mid, INPUT );
pinMode( input_down, INPUT );
pinMode( input_bottom, INPUT ); // input bottom LED
pinMode( led_error, OUTPUT ); // Error output
pinMode( led_auto, OUTPUT ); // LED for automatic mode
pinMode( output_up, OUTPUT ); // move UP output
pinMode( output_down, OUTPUT ); // move DOWN output
pinMode( output_slow, OUTPUT ); // move FAST output (low=fast)
pinMode( led_top, OUTPUT ); // Debugging status LEDs
pinMode( led_up, OUTPUT );
pinMode( led_mid, OUTPUT );
pinMode( led_down, OUTPUT );
pinMode( led_bottom, OUTPUT );
pinMode( led_stable, OUTPUT );
}
void loop()
{
unsigned char Index;
FilteredInputs = 0;
ReadInputs = ( digitalRead( input_top ) ) | ( digitalRead( input_up ) << 1 ) | ( digitalRead( input_mid ) << 2 ) | ( digitalRead( input_down ) << 3 ) | ( digitalRead( input_bottom ) << 4 );
// this loop monitors each input for recent activity; when an input is active, the timer for that inputs
// is set to MAX_INTERVAL, and starts counting down from there; when it reaches 0, the input is considered
// inactive. MAX_INTERVAL should be set slightly longer than the maximum time the LED for an input can be off
// when blinking
for( Index = 0; Index < 5; Index++ )
{
if( ReadInputs & ( 1 << Index ) )
InputCounters[ Index ] = MAX_INTERVAL;
if( InputCounters[ Index ] )
{
InputCounters[ Index ]--;
FilteredInputs |= 1 << Index;
}
// write each input filter status to a debug LED (A0 + 3 is the same as A3)
digitalWrite( led_top + Index, InputCounters[ Index ] );
}
if( !FilteredInputs ) // if all timers have expired, not a single input active
ActivityCounter = 2000; // set timer for 2 seconds; only when signal is lost should all timers expire
if( ActivityCounter )
{
ActivityCounter--;
FilteredInputs = 0; // only after 2 seconds without any expired timers should the outputs become active
}
// now check if the input state has been stable for a number of successive cycles, to prevent movement
// when 2 LEDs are blinking, since one can always be detected slightly earlier or later than the other
// If current and previous input states are different, reset stability counter
if( PreviousInputs != FilteredInputs )
StabilityCount = STABILITY_THRESHOLD;
// now store the current input state for the next cycle
PreviousInputs = FilteredInputs;
// if stability counter has not yet reached the threshold, increment it, and force the input state to 0
// to prevent any movement
if( StabilityCount )
{
StabilityCount--;
FilteredInputs = 0;
}
// write Stability status to debug LED
digitalWrite( led_stable, !StabilityCount );
// write Error LED status; LOW when an error occured (LED with resistor to supply)
digitalWrite( led_error, FilteredInputs );
JoystickInput = digitalRead( input_joy_up ) | ( digitalRead( input_joy_down ) << 1 ); // 0x01 = UP, 0x02 = DOWN
if( JoystickInput )
AutomaticMode = 0;
else
if( digitalRead( input_pushbutton ) ) // pushbutton for automatic mode
AutomaticMode = 1;
if( AutomaticMode )
{
// lookup what to do for this state and write to outputs
digitalWrite( output_up, Actions[ FilteredInputs ] & MOVE_UP );
digitalWrite( output_down, Actions[ FilteredInputs ] & MOVE_DOWN );
digitalWrite( output_slow, ( !( Actions[ FilteredInputs ] & MOVE_FAST ) ) && Actions[ FilteredInputs ] );
}
else
{
digitalWrite( output_up, JoystickInput == 0x01 ); // valve UP when joystick UP is active (and DOWN isn't)
digitalWrite( output_down, JoystickInput == 0x02 ); // valve DOWN when joystick DOWN is active (and UP isn't)
digitalWrite( output_slow, 0 ); // fast mode
}
digitalWrite( led_auto, AutomaticMode );
delay(1); // actual loop interval will be slightly longer, because it also takes some time to execute
}