
I^2R (Customer) asked a question.
Hi,
I need to debounce some Discrete inputs. I came up with the following code, based on a modified Adafruit routine.
I'm an old PLC guy, but pretty new to C++. The code appears to work, but I wanted to know if anyone sees something I'm doing that's silly or could be optimized.
I'm hoping to use this as a generic debounce function for future projects. Feel free to use and modify.
I'd like to expand this to accommodate more than one discrete input module. What would be the best approach? I'm thinking 2D arrays?
Thanks.
- #include <P1AM.h>
-
- // This debouncing routine is a modified version of code posted by Adafruit:
- // https://blog.adafruit.com/2009/10/20/example-code-for-multi-button-checker-with-debouncing/
-
- #define DEBOUNCE 10 // input debouncer, how many ms to debounce, 5+ ms is usually plenty
-
- // Here is where we define the inputs that we'll debounce.
- // This is for a digital input module in A SINGLE SLOT ONLY as set by SLOT below. For multiple modules the code will require modification (2D array perhaps?).
- // QUANTITY sets the number of consecutive inputs to debounce, beginning at input #1.
- #define SLOT 2 //in this example we have a digital input module in slot #2
- #define QUANTITY 6 //in this example, we are debouncing inputs 1-6
-
- // Define an array to represent the inputs. As array is zero indexed, but inputs start at #1, we create the array one element larger than the number of inputs.
- // We will ignore the array element at index zero.
-
- #define NUMINPUTS QUANTITY + 1 // Gets the size of the array
- //byte inputs[NUMINPUTS];
-
-
- // we will track if a input is on, just had a rising or falling edge.
- byte inputIsOn[NUMINPUTS], inputRisingEdge[NUMINPUTS], inputFallingEdge[NUMINPUTS];
-
- // Array to track output states. Not part of debouncing logic. Used for example code in main loop
- byte outstate[NUMINPUTS];
-
- void setup() {
-
- // set up serial port
- //Serial.begin(9600);
- Serial.begin(115200);
-
-
- while (!P1.init()) {
- ; //Wait for Modules to Sign on
- }
- }
-
- void loop() {
-
- /*
- Upon calling the check_inputs function, it will debounce the inputs and update the three arrays where n = the input number.
- the inputs start at 1, but the arrays are 0 indexed: The 0 index element of the arrays are ignored.
-
- inputIsOn[n]
- shows the current debounced state of each input.
-
- inputRisingEdge[n] and inputFallingEdge[n]
- Will be true after an input state change for ONE pass through the main loop.
- The next call to check_inputs will clear the array.
-
- */
- check_inputs();
-
-
-
- /*
- The following example code is not part of the debounce logic. This demonstrates a P1-16CDR module in slot #2, this is a module with 8 inputs and 8 outputs.
- The code toggles the outputs (and the LED for each) corresponding to each input. The output will toggle state on each rising edge.
- The state of each input and the rising and falling edges are printed to the terminal.
- */
-
- for (byte i = 1; i < NUMINPUTS; i++) {
- if (inputRisingEdge[i]) { //If we see a rising edge
- Serial.print(i, DEC);
- Serial.println(" Rising edge");
- if (!outstate[i]) { //If corresponding output is off...
- P1.writeDiscrete(HIGH, SLOT, i); //Turn corresponding output on
- outstate[i] = 1; //update array to track output state
- }
- else { //Else corresponding output is on...
- P1.writeDiscrete(LOW, SLOT, i); //Turn corresponding output off
- outstate[i] = 0; //update array to track output state
- }
- }
- if (inputFallingEdge[i]) {
- Serial.print(i, DEC);
- Serial.println(" Falling edge");
- }
- if (inputIsOn[i]) {
- Serial.print("Input #");
- Serial.print(i, DEC);
- Serial.println(" is on");
- // is the input on at this moment?
- }
- if (!inputIsOn[i]) {
- Serial.print("Input #");
- Serial.print(i, DEC);
- Serial.println(" is off");
- // is the input off at this moment?
- }
- }
-
- // Delay not required, just here to slow the serial display down.
- // CAUTION: Beware of long delays in the main loop. A legitimate input pulse longer than the debouce time, but shorter than the loop cylic time could be missed.
- delay(300);
- }
-
-
- void check_inputs() {
-
- // Create arrays to hold comparisons. These are static so the persist between function calls...
- static byte previousstate[NUMINPUTS]; //
- static byte currentstate[NUMINPUTS];
- static long lasttime;
-
-
- // Clear the rising / falling arrays...
- memset(inputRisingEdge, 0, NUMINPUTS);
- memset(inputFallingEdge, 0, NUMINPUTS);
-
- if (millis() < lasttime) {
- // we wrapped around, lets just try again
- lasttime = millis();
- }
-
- if ((lasttime + DEBOUNCE) > millis()) {
- // not enough time has passed to debounce
- return;
- }
- // ok, we have waited DEBOUNCE milliseconds, lets reset the timer
- lasttime = millis();
-
-
- for (byte index = 1; index < NUMINPUTS; index++) {
-
- currentstate[index] = P1.readDiscrete(SLOT, index); // read the input
-
- if (currentstate[index] == previousstate[index]) { //Same reading two times in a row, must be the real deal
- if ((inputIsOn[index] == 0) && (currentstate[index] == 1)) { // rising edge
- inputRisingEdge[index] = 1; //Update the array
- } else if ((inputIsOn[index] == 1) && (currentstate[index] == 0)) { // falling edge
- inputFallingEdge[index] = 1; //Update the array
- }
- inputIsOn[index] = currentstate[index]; //Update the array
- }
- previousstate[index] = currentstate[index]; // Save state to compare next time through
- }
- }
Hey @I^2R (Customer) , If you're looking to use multiple modules and optimize things a bit, I would recommend using the readBlockData function. It'll let you query all of your discrete input modules in a single call. With your current approach there is some overhead due to readDiscrete being called for each channel individually.