/* spindle.ino: Typematic debounce two button switches, labeled UP and DOWN for a spindle motor; the state of the two button switches are HIGH, or, LOW, and the HIGH state is regarded as active, (i.e., the normally off state of the switches is LOW due to a pull down resistor to ground, and pulled up for the HIGH state when the button switch is pressed.) The "standard," PC 101 key keyboard typematic delay, (the time required for a key to be pressed before automatically repeating,) is 250 mS., and, the repeat rate is 10.9 characters per second, or 91.7 mS. per character. (To mimic these typematic timings, define TRANSITION_TIME, below, to 250 mS., and, REPEAT_RATE, below, to 10 mS.) There are two modes to operate the UP and DOWN switches: 1) Single step: the switch must be pressed for less than TRANSITION_TIME mS., and released, causing one action to occur; the switch will not recognize additional pressings until TRANSITION_TIME mS. after the beginning time of the first switch press. Note this functions as debounce-the first button press was probably triggered by switch transition noise. 2) Slew: the switch must be pressed for more than TRANSITION_TIME mS., and one action occurs immediately when the button is pressed, followed by, no actions occurring until TRANSITION_TIME mS. after the beginning time of the first switch press, then actions occur as rapidly as possible until the button is released, stopping actions, immediately. The licensing of this sketch is whatever the licensing of the Arduino library sketches are. John Conover, john@email.johncon.com. */ // Uncomment to print spindle intermediate values to the serial // monitor: // #define SPINDLE_PRINTIT // Keyboard typematic delay, mS: #define TRANSITION_TIME 250 // Keyboard typematic repeat rate, mS: #define REPEAT_RATE 10 typedef struct switch_button /* data structure for a push button switch */ { int Status; // current status of Arduino pin, determined by digitalRead () int LastStatus; // last status of Arduino pin, determined by digitalRead () int ButtonState; // button state, HIGH or LOW int OneShot; // one shot active, 0 = no, 1 = yes int ButtonTransition; // button transition in progress, 0 = no, 1 = yes unsigned long TransitionStartTime; // time of the start of a transition, either LOW to HIGH, or, HIGH to LOW, is accepted } BUTTON; BUTTON SpindleUP, /* data structure for the spindle UP button */ SpindleDOWN; /* data structure for the spindle DOWN button */ const int SpindleUpButtonPin = 8, // digital pin number of the spindle UP button SpindleDownButtonPin = 10; // digital pin number of the spendle DOWN button pin unsigned long TransitionDelay = (unsigned long) TRANSITION_TIME; // delay before a button switch is accepted to accomodate switch noise int PWM_out_pin = 9; // Must be one of 3, 5, 6, 9, 10, or 11 for Arduino Uno byte PWM_out_level = (byte) 0; // PWM output level counter int typematic (switch_button *button); // Handle a button's states and transitions void newDelay (unsigned long mS); // Alternative delay () function void setup () { pinMode (SpindleUpButtonPin, INPUT); pinMode (SpindleDownButtonPin, INPUT); pinMode (PWM_out_pin, OUTPUT); SpindleUP.Status = 0; SpindleUP.LastStatus = 0; SpindleUP.ButtonState = 0; SpindleUP.OneShot = 0; SpindleUP.ButtonTransition = 0; SpindleUP.TransitionStartTime = (unsigned long) 0; SpindleDOWN.Status = 0; SpindleDOWN.LastStatus = 0; SpindleDOWN.ButtonState = 0; SpindleDOWN.OneShot = 0; SpindleDOWN.ButtonTransition = 0; SpindleDOWN.TransitionStartTime = (unsigned long) 0; #ifdef SPINDLE_PRINTIT Serial.begin (9600); delay (2000); while (!Serial); //delay for Leonardo Serial.print ("Ready:\n"); #endif } void loop () { int SpindleUpRpm, SpindleDownRpm; SpindleUP.Status = digitalRead (SpindleUpButtonPin); if (SpindleUpRpm = typematic (&SpindleUP)) { if (PWM_out_level < (byte) 255) { PWM_out_level ++; analogWrite (PWM_out_pin, PWM_out_level); } newDelay ((unsigned long) REPEAT_RATE); } SpindleDOWN.Status = digitalRead (SpindleDownButtonPin); if (SpindleDownRpm = typematic (&SpindleDOWN)) { if (PWM_out_level > (byte) 0) { PWM_out_level --; analogWrite (PWM_out_pin, PWM_out_level); } newDelay ((unsigned long) REPEAT_RATE); } #ifdef SPINDLE_PRINTIT if ((SpindleUpRpm == 1) || (SpindleDownRpm == 1)) { Serial.print ("SpindleUpRpm = "); Serial.print (SpindleUpRpm, DEC); Serial.print (", SpindleDownRpm = "); Serial.print (SpindleDownRpm, DEC); Serial.print (", PWM_out_level = "); Serial.print (PWM_out_level, DEC); Serial.print ("\n"); } #endif } /* Handle a button's states and transitions: int typematic (switch_button *button); typedef struct switch_button // data structure for a push button switch { int Status; // current status of Arduino pin, determined by digitalRead () int LastStatus; // last status of Arduino pin, determined by digitalRead () int ButtonState; // button state, HIGH or LOW int OneShot; // one shot active, 0 = no, 1 = yes int ButtonTransition; // button transition in progress, 0 = no, 1 = yes unsigned long TransitionStartTime; // time of the start of a transition, either LOW to HIGH, or, HIGH to LOW, is accepted } BUTTON; Returns whether to step the the RPM of the spindle motor, i.e., 1, or not, i.e. 0, and sets LastStatus, ButtonState, ButtonTransition, and, TransitionStartTime, in the BUTTON data structure to appropriate values. Status: _____________|-|___________ ButtonTransition: ___|---------|___ ButtonState: ________|-|___________ Single Step Timing Status: _____________|-------------~ ~---|______________ ButtonTransition: ___|---------|___~ ~___|----------|___ ButtonState: ________|-|_______|---~ ~---|______________ Slew Timing Status: _____________|-|___|-|_____~ ~__________________ ButtonTransition: ___|---------|___~ ~__________________ ButtonState: ________|-|___________~ ~__________________ Overlap Timing Status: _____________|-|____|------~ ~---|______________ ButtonTransition: ___|---------|___~ ~___|----------|___ ButtonState: ________|-|_______|---~ ~---|______________ Overlap Timing ButtonTransition is a lockout of TRANSITION_TIME mS., during which no switch operations will be recognized; this time interval was, (probably,) triggered by switch transition noise of a switch state change. The function is debounce of the switch. The millis () rollover issue is avoided via modulo arithmetic, and assumes a 32 bit unsigned long. */ int typematic (switch_button *button) { if (button->ButtonTransition == 1) // button transition in progress, 0 = no, 1 = yes { // button transition in progress if (! (millis () - button->TransitionStartTime < TransitionDelay)) // button transition in progress; time of the end of a transition, either LOW to HIGH, or, HIGH to LOW, completed? { // transition completed button->ButtonTransition = 0; // button transition in progress, 0 = no, 1 = yes button->ButtonState = button->Status; // button state, HIGH or LOW } if (button->OneShot == 1) // one shot active, 0 = no, 1 = yes { button->ButtonState = 0; // last status of Arduino pin, determined by digitalRead () button->OneShot = 0; // one shot active, 0 = no, 1 = yes } } else if (button->LastStatus != button->Status) { // button not in transition, and switch changed status button->ButtonTransition = 1; // button transition in progress, 0 = no, 1 = yes button->TransitionStartTime = millis (); // save the time of the start of a transition, either LOW to HIGH, or, HIGH to LOW, is accepted button->ButtonState = button->Status; // button state, HIGH or LOW button->OneShot = 1; // one shot active, 0 = no, 1 = yes } button->LastStatus = button->Status; // last status of Arduino pin, determined by digitalRead () return (button->ButtonState); } /* Alternative delay () function: void newDelay (unsigned long mS); Alternative delay function, using millis (), instead of delay () which stops important Arduino functions. The millis () rollover issue is avoided via modulo arithmetic, and assumes a 32 bit unsigned long. */ void newDelay (unsigned long mS) { unsigned long start_time = millis (); while (millis () - start_time < mS); }