i am trying to differentiate between single click and double click on analog pins using sensor input (it gives sharp pulses as output similar to dirac delta functions).
the code i wrote is not able to differentiate between single click and double click and is detecting double click alone, it would be great help if someone can debug my small program. since i am new to programming, your help is much appreciated.
i am considering time-gap of 250 ms as threshold for defining double click (i.e., if sensor is clicked consecutively within 250 ms it should be treated as double click).
also, i have used 30ms as debouching time.
my code so far is as below:
int m = 0;
int chargap = 250;
int debounce = 30;
void setup()
{
Serial.begin(38400);
}
void loop()
{
int c_m = checkinput();
if (c_m ==1) Serial.println("1");
if (c_m ==2) Serial.println("2");
}
int checkinput()
{
int val = analogRead(A1);
int vth = 500;
int previousbuttonstate = 0;
unsigned long pt = 0;
if(val > vth)
{
if(debounce+pt < millis() < chargap+pt && previousbuttonstate == 1 )
{
m = 1;
previousbuttonstate = 0;
}
if(debounce+pt < millis() < chargap+pt && previousbuttonstate ==0)
{
m = 2;
previousbuttonstate = 1;
}
pt = millis();
}
return m;
previousbuttonstate = 0;
}
previousbuttonstate will always be 0 because it is declared as an autovariable in checkinput() which gets initialized to 0 every time checkinput() is called. Precede the declaration by the 'static' keyword if you want to persist previousbuttonstate.
same issue with pt. It will always be 0 entering checkinput(). Precede the declaration by the 'static' keyword.
you have the statement 'previousbuttonstate = 0;' after the 'return m;' statement. It will never get executed, however I suppose you don't want to execute that statement because it would always overwrite the previous button state.
'debounce+pt < millis() < chargap+pt ' is an unconventional expression. I'm not sure it will work but I don't have time to study it right now.
If I understand your input correctly, I would separate the debounce into its own function. Have the debounce always return the current STABLE state of the input. I would use the stable state to control a simple state machine. Your code will be much easier to understand and debug.
When I have some time I will try to provide an example of what I am talking about
Here s the way I would do it. I'm not at a place where I can compile or test.
enum stateEnum
{
WAIT_FOR_PULSE_HIGH,
WAIT_FOR_PULSE_LOW,
GAP_WAIT,
PROCESS_PULSES
};
const byte buttonPin = A0;
const int vth = 500;
const int chargap = 250;
const int debounceHoldMs = 30; // Debounce time for button
stateEnum state;
void setup()
{
Serial.begin(38400);
state = WAIT_FOR_PULSE_HIGH;
}
void loop()
{
static byte currentButtonState;
static unsigned long gapStartMs;
static byte numPulses = 0;
// Get the stable state of the button from the debouncer
currentButtonState = debounceInput(buttonPin);
switch (state)
{
case WAIT_FOR_PULSE_HIGH:
if (currentButtonState == HIGH)
{
numPulses++;
gapStartMs = millis();
state = WAIT_FOR_PULSE_LOW;
}
break;
case WAIT_FOR_PULSE_LOW:
if (currentButtonState == LOW)
{
if (numPulses == 1)
{
// Wait for potential second pulse
gapStartMs = millis();
state = GAP_WAIT;
}
else
{
// This was a second pulse. Report the number of pulses.
state = PROCESS_PULSES;
}
}
else if (millis() - gapStartMs > chargap)
{
// Pulse is longer than gap. Not sure what to do so we'll
// just report the pulses.
// TBD: If it is abnormal for the button to stay high more than
// a gap time you should do some error handling.
state = PROCESS_PULSES;
}
break;
case GAP_WAIT:
if (millis() - gapStartMs > chargap && currentButtonState == LOW)
{
state = PROCESS_PULSES;
}
else if (currentButtonState == HIGH)
{
// Second pulse
state = WAIT_FOR_PULSE_HIGH;
}
break;
case PROCESS_PULSES:
// TBD: Take action here based on the number of pulses!!!
Serial.print("Number of pulses: ");
Serial.println(numPulses);
// Reset number of pulses and state machine
numPulses = 0;
state = WAIT_FOR_PULSE_HIGH;
break;
} // end state switch
} // end loop
byte debounceInput(byte debouncePin)
{
static unsigned long lastTimeMs = 0;
static byte lastReading = LOW;
static byte currentState = LOW;
byte currentReading;
// Read the input pin and convert to HIGH or LOW
int val = analogRead(debouncePin);
if (val > vth)
{
currentState = HIGH;
}
else
{
currentState = LOW;
}
// Debounce
if (currentReading != lastReading)
{
// Pin state just changed. Reset the time and record the last reading value.
lastTimeMs = millis();
lastReading = currentReading;
}
else if (currentState != currentReading && ((millis() - lastTimeMs) >= debounceHoldMs))
{
//
// If we got here then we know:
//
// 1) current reading equals the last reading,
// 2) the current state does not equal the current reading, and
// 3) the curent reading has been stable for at least debounceHoldMs,
//
// Therefore we need to report a new current state
//
currentState = currentReading;
}
else
{
// nothing
}
return currentState;
}
I implemented your inputs on making the variables static and it wasnt enough and the problem still persisted.
the code you gave is not working as its not printing/detecting any peaks, my thoughts is that for debouching you are comparing the "currentreading" variable but, i don't see its related to the "currentstate" variable(which you have related to input). I tried to replacing the "currentstate" variable to currentreading in converting analog to high and low but it still did not produce any outputs.
I did find one error in my debounce so try the code below.
I can't debug without your hardware, but really I did way more than I should have.
enum stateEnum
{
WAIT_FOR_PULSE_HIGH,
WAIT_FOR_PULSE_LOW,
GAP_WAIT,
PROCESS_PULSES
};
const byte buttonPin = A0;
const int vth = 500;
const int chargap = 250;
const int debounceHoldMs = 30; // Debounce time for button
stateEnum state;
void setup()
{
Serial.begin(38400);
state = WAIT_FOR_PULSE_HIGH;
}
void loop()
{
static byte currentButtonState;
static unsigned long gapStartMs;
static byte numPulses = 0;
// Get the stable state of the button from the debouncer
currentButtonState = debounceInput(buttonPin);
switch (state)
{
case WAIT_FOR_PULSE_HIGH:
if (currentButtonState == HIGH)
{
numPulses++;
gapStartMs = millis();
state = WAIT_FOR_PULSE_LOW;
}
break;
case WAIT_FOR_PULSE_LOW:
if (currentButtonState == LOW)
{
if (numPulses == 1)
{
// Wait for potential second pulse
gapStartMs = millis();
state = GAP_WAIT;
}
else
{
// This was a second pulse. Report the number of pulses.
state = PROCESS_PULSES;
}
}
else if (millis() - gapStartMs > chargap)
{
// Pulse is longer than gap. Not sure what to do so we'll
// just report the pulses.
// TBD: If it is abnormal for the button to stay high more than
// a gap time you should do some error handling.
state = PROCESS_PULSES;
}
break;
case GAP_WAIT:
if (millis() - gapStartMs > chargap && currentButtonState == LOW)
{
state = PROCESS_PULSES;
}
else if (currentButtonState == HIGH)
{
// Second pulse
state = WAIT_FOR_PULSE_HIGH;
}
break;
case PROCESS_PULSES:
// TBD: Take action here based on the number of pulses!!!
Serial.print("Number of pulses: ");
Serial.println(numPulses);
// Reset number of pulses and state machine
numPulses = 0;
state = WAIT_FOR_PULSE_HIGH;
break;
} // end state switch
} // end loop
byte debounceInput(byte debouncePin)
{
static unsigned long lastTimeMs = 0;
static byte lastReading = LOW;
static byte currentState = LOW;
byte currentReading;
// Read the input pin and convert to HIGH or LOW
int val = analogRead(debouncePin);
if (val > vth)
{
currentReading = HIGH;
}
else
{
currentReading = LOW;
}
// Debounce
if (currentReading != lastReading)
{
// Pin state just changed. Reset the time and record the last reading value.
lastTimeMs = millis();
lastReading = currentReading;
}
else if (currentState != currentReading && ((millis() - lastTimeMs) >= debounceHoldMs))
{
//
// If we got here then we know:
//
// 1) current reading equals the last reading,
// 2) the current state does not equal the current reading, and
// 3) the curent reading has been stable for at least debounceHoldMs,
//
// Therefore we need to report a new current state
//
currentState = currentReading;
}
else
{
// nothing
}
return currentState;
}