Arduino, Run Time Code Error in Debouncing Voltage Time Delay Code - c++

I am Ansh Goel, I was learning Arduino from Udemy. I am beginner to this field. I was creating a code for Debouncing the button to solve the issues of bouncing voltage. But there is error in the code. There is no Compile time error but it is run time error.
I also tried to check the code using the Serial.print() to find the where the error is, then I found that the error is in the second nested if condition. I have also mention where there is the error in the code for ease. There I am not able to get the Serial.print("A") function too to the Serial Monitor.
My main motive is to run the code so that I am able to stop bouncing voltages when a button is pressed using some delay.
It is from line 41
This is the code I used to debounce the button
const int btn_pin = 2;
const int debounce_delay = 50; //ms
// We need to remember the previous button state between loops
int btn_prev = HIGH;
int btn_state = HIGH;
unsigned long last_debounce_time = 0;
// Counter
int counter = 0;
void setup() {
Serial.begin(9600);
// Set up pins
pinMode(btn_pin, INPUT_PULLUP);
pinMode(13, OUTPUT);
}
void loop() {
int btn_read;
// Read current button state
btn_read = digitalRead(btn_pin);
//Remember when the button change state
// If the button was previously HIGH and now LOW, it's been pressed
if ( (btn_prev == HIGH) && (btn_read == LOW )) {
//Store the time it took to take the action for button press
last_debounce_time = millis();
}
//Wait before changing the state of the button
// IN THIS CONDITION THERE IS ERROR SOMEWHERE I AM NOT GETTING IT
if(millis() > (last_debounce_time + debounce_delay)){
if(btn_read != btn_state) {
Serial.println("A");
// Then store the button change value to the global variable
btn_state = btn_read;
if(btn_state == LOW) {
// Increment and print counter
counter++;
Serial.println(counter);
digitalWrite(13,HIGH);
delay(500);
digitalWrite(13,LOW);
delay(500);
}
}
}
// Remember the previous button state for the next loop iteration
btn_prev = btn_state;
}
For testing purposes, this is the circuit design on TinkerCad, that you can check online.
TinkerCad Circuit Design
Please Help me solve the issue, it will be a great help from your side for me.

There are several locations where your code could malfunction:
you're not looping properly for loop
your logic doesn't work
digitalRead doesn't work
print doesn't work
first, remove the debounce check and see if this works:
//if(millis() > (last_debounce_time + debounce_delay)){
to check all other issues, add the following right before the remaining if:
delay so you don't get endless data
print millis, last_debounce_time, debounce_delay and btn_read
end line
then run and press the button. The output will let you know what's the issue

Related

SAMD21 doesn't wake up after deep sleep

I'am working on a personnal board build with an atsamd21e18a. I'am actually working on sleep mode. I make a function to put samd21 in sleep mode like that. I use RTCZero library.
So in my setup function I've something like this
RTCZero rtc;
void setup(){
// Set the XOSC32K to run in standby
SYSCTRL->XOSC32K.bit.RUNSTDBY = 1;
rtc.begin();
.... other line code ....
attachInterrupt(digitalPinToInterrupt(PIN_USER_BUTTON), wakeUpButtonUser, CHANGE);
...other line....
}
So in my setup I intialise rtc and I attach an interrupt in my user button. This button is used to power on or power off my board.
My function goTosleep() :
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__DSB();
__WFI();
In my function wakeUpButtonUser I have a simple if/else statement. If the user pressed the button more than 3 seconds samd21 go to sleep with goToSleep() function, else if it's less than 3 seconds I want wake up the board and light up my led but it's doesn't work.
Here my wakeUpButtonUser function, user_button is object create from C++ for my button.
void wakeUpButtonUser(){
if (userButton.isPressed())
{
userButton.setTimeStartPressed(millis());
PRINT_DEBUG_LN("Pressed");
}
else
{
userButton.setTimeEndPressed(millis());
PRINT_DEBUG_LN("Released");
uint32_t time_pressed = userButton.getTimePressed();
if (time_pressed >= temps_on && time_pressed < temps_off)
{
PRINT_DEBUG_LN("ON");
//here I have code to light up a led
}
else
{
PRINT_DEBUG_LN("STOP");
goSleepMode();
}
}
}
This fuction works because if I comment the line goToSleep(), I can read on my serial ON or OFF depending the time I pressed my button and the led works also because I can light up led before going to sleep. But when my board go to sleep, she's never wake up I don't understand why, I missed something?

ESP32 Simple button hold for 3 seconds example. Output doesn't seem to make sense?

Hoping someone can see what I'm missing as it's gotta be right there staring at me in the face..
I've got this code (below) set up on an ESP32 to spawn a thread that simply monitors the state of a pin connected to a switch. Essentially this code is supposed to wait for the button to be held for 3 seconds and then do something important. The actual input seems to read fine, but for some reason once I've pressed the button, the button state is stuck for like 15 seconds after un-pressing the switch.
For example,
Press the switch, the actualBtnState reads 1, buttonState reads 1 (after 50us),and btnPressTime increments as expected.
Release switch, actualBtnState reads 0, btnState reads 1, and btnPressTime stops incrementing.
After 50us, expecting to see btnState read 0 and then trigger the else or elseif blocks (depending on how long the button was held). Actual results continue to read btnState = 1 and btnPressTime = [whatever the last held time was] for a solid 15 seconds or more. actuyalBtnState reads correctly at 0 this entire time and for some reason lastDebounceTime keeps incrementing?
I should note that this is part of a much larger project, hence the threading. I also can't seem to print anything within the resetBtnCB function as I immediately get a "guru mediation error kernel panic whatever-the-error-is" error and the esp reboots.
Code:
#include <Arduino.h>
#define BUTTON_PIN 27
// Variables will change:
int buttonState; // the current reading from the input pin
int lastButtonState = LOW; // the previous reading from the input pin
unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
unsigned long debounceDelay = 50; // the debounce time; increase if the output flickers
unsigned long buttonPressTime = 0; //Amount of time the button has been held down
unsigned long actualBtnState = 0; //The actual reading from the pin without noise filtering
void resetBtnCB(void *pvParameters)
{
pinMode(BUTTON_PIN, INPUT);
while (true)
{
// read the state of the switch into a local variable:
int reading = digitalRead(BUTTON_PIN);
actualBtnState = reading;
// If the switch changed, due to noise or pressing:
if (reading != lastButtonState)
{
// reset the debouncing timer
lastDebounceTime = millis();
}
unsigned long timeSinceDebounce = millis() - lastDebounceTime;
if (timeSinceDebounce > debounceDelay)
{
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
buttonState = reading;
if (buttonState == HIGH)
{
buttonPressTime += timeSinceDebounce;
}
else if (buttonPressTime > 300)
{
buttonPressTime = 0;
// SUCCESS! Do something important here as we've held the button for x seconds
}
else
{
buttonPressTime = 0;
}
}
// save the reading. Next time through the loop, it'll be the lastButtonState:
lastButtonState = reading;
vTaskDelay(10);
}
}
void setup()
{
Serial.begin(115200);
xTaskCreate(resetBtnCB, "reset_button", 1024, NULL, 10, NULL);
}
void loop()
{
char debug[512];
sprintf(debug, "button state %u, lastD %u, buttonPressTime %u, actualBtnState %u, lastBtnState %u", buttonState, lastDebounceTime, buttonPressTime, actualBtnState, lastButtonState);
Serial.println(debug);
delay(50);
yield();
}

Button Push turning on an OLED ESP32

I am working on getting a DHT22 sensor which displays a reading onto an OLED when a button is pushed. I am using Arduino IDE. Right now I have the sensor working and displaying on the screen, but I am having difficulty getting it to only turn on when the button is pushed. GPIO 13 is currently getting the signal from the sensor, and GPIO 26 is connected to the physical button along with power and ground. All of the code with "//added" is the new code I added in order to get the button to work. Any help is greatly appreciated!
At the top I added:
const int ButtonPin = 26; //added
const int PushButton; //added
I am getting the error:
exit status 1
uninitialized const 'PushButton' [-fpermissive]
In setup function I added:
pinMode(ButtonPin, OUTPUT); //added
pinMode(PushButton, INPUT)://added
And in loop function I added:
int Push_button_state = digitalRead(PushButton); //added
if (Push_button_state == HIGH) //added
float h = dht.readHumidity();
// Read temperature as Celsius
float t = dht.readTemperature();
u8g2.firstPage();
do {
draw();
} while ( u8g2.nextPage() );
delay(1000);
}
else { //added
I think you are not clear about what you want to achieve. First of all, you only need ButtonPin variable to work with the button. As your button is connected to GPIO 26, you must declare this pin as an INPUT.
Later, you shoould use the digitalWrite function to get the state of the pin. Depending on how you connect your button, you will receive a '0' or a '1' when it is pressed. Anyways, I have been using a really comfortable library to work with buttons on Arduino, check it here. I hope this could help

Microcontroller - Button 'holding down' listener

I am working with a Texas Instruments LauncherPad MSP432 P401R and Energia.
I am trying to write a small program that counts the times I press button2 while I keep button1 pressed down.
int push1_listener = -1;
int push2_listener = -1;
int digit = 0;
void setup() {
Serial.begin(9600);
pinMode(PUSH1, INPUT_PULLUP);
pinMode(PUSH2, INPUT_PULLUP);
}
void loop() {
push1_listener = digitalRead(PUSH1);
push2_listener = digitalRead(PUSH2);
while(push1_listener == 0) {
if(push2_listener == 0) {
digit++;
delay(200);
Serial.print("btn2");
}
Serial.print("btn1");
}
}
The program goes into the while loop, I can see the "btn1" in the Serial console. However the program does not go into the if case.
Can somebody help me?
Your code reads the status of both push buttons once before entering the while loop. Then it never reads the push button status within the while loop. So the value of the push button variables will never change within the while loop. You need to re-read the push button status repeatedly within the while loop.
Maybe something like this:
while ((push1_listener = digitalRead(PUSH1)) == 0) {
push2_listener = digitalRead(PUSH2);
if (push2_listener == 0) {
...
}
...
}

Interrupt arduino routine to run a slow delay based process

I have an arduino that does mostly data collection and sends it to an ESP8266 over serial. Serial communication to the ESP is not quick as you may know and it depends on a lot of waiting. I have a button and I want to immediately stop any data collection or sending and have it open a door. The door opening takes about 30 seconds. What's the best way to do this?
Not the full code, but it goes something like the below.
Of course this doesn't work because you can't use WHILE or DELAY in an ISR, but I don't know how to restructure it.
attachInterrupt(4 , openadoor, FALLING);
void loop(){
gathersomedata();
senddatatoESP();
if(wait_for_esp_response(2000,"OK")) lightGreenLED();
else lightRedLED();
}
byte wait_for_esp_response(int timeout, const char* term) {
unsigned long t = millis();
bool found = false;
int i = 0;
int len = strlen(term);
while (millis() < t + timeout) {
if (Serial2.available()) {
buffer[i++] = Serial2.read();
if (i >= len) {
if (strncmp(buffer + i - len, term, len) == 0) {
found = true;
break;
}
}
}
}
buffer[i] = 0;
}
void openadoor(){
while (doortimer + dooropentime >= millis() && digitalRead(openbutton) == HIGH && digitalRead(closebutton) == HIGH) {
digitalWrite(DoorOpenRelay, LOW);
}
digitalWrite(DoorOpenRelay, HIGH);
}
TL;DR - see Nick's Answer. :-)
Without the complete code, I can only guess at a few things:
1) You shouldn't wait in an ISR. Even calling millis() is discouraged, as it depends on the Timer0 ISR getting called, which will be prevented as long as you're in your openadoor ISR.
2) In general, the ISR should only do things that are very quick... think microseconds. That's tens to hundreds of instructions, which can be just a few lines of code. Even digitalWrite is almost too slow. If there's more to do, you should just set a volatile flag that is watched in loop. Then loop can do the time-consuming work.
3) Calculating elapsed time must be in this form:
if (millis() - startTime >= DESIRED_TIME)
where startTime is the same type as millis(), a uint32_t:
uint32_t startTime;
You set startTime whereever it's appropriate:
startTime = millis();
This avoids the rollover problem, when millis() rolls over from 232-1 to 0.
4) It looks like you know how to "block" until a certain amount of time has elapsed: the while loop will keep your sketch at that point. If you just change it to an if statement, the Arduino can continue on its way to handle other things.
Because loop happens so quickly, the if statement will check the time very frequently... unless you delay or block somewhere else, like wait_for_esp_response. :-( That while loop should change to an if statement as well. The routine is more like check_for_esp_response.
5) You have to track the state of the door opening and closing process. This is a Finite-State machine problem. Nick has a good description here, too. You can use the enum type to define the states that the door can be in: CLOSED, OPENING, OPENED and CLOSING.
When the OPEN button is pressed, you can look at the state and see if you should start opening it. Then start a timer, turn on the relay and, most importantly, set the state to OPENING. Next time through loop, you can test the state (a switch statement), and for the OPENING case, look at the time to see if it has been long enough. If it has set the state to OPENED. And so on.
If I incorporate all these things into your sketch, it should start to look like this:
volatile bool doorOpenPressed = false;
volatile bool doorClosePressed = false;
static const uint32_t DOOR_OPEN_TIME = 30000UL; // ms
static const uint32_t DOOR_CLOSE_TIME = 30000UL; // ms
static const uint32_t DATA_SAMPLE_TIME = 60000UL; // ms
static uint32_t lastDataTime, sentTime, relayChanged;
static bool waitingForResponse = false;
static uint8_t responseLen = 0;
enum doorState_t { DOOR_CLOSED, DOOR_OPENING, DOOR_OPENED, DOOR_CLOSING };
doorState_t doorState = DOOR_CLOSED;
void setup()
{
attachInterrupt(4 , openadoor, FALLING);
}
void loop()
{
// Is it time to take another sample?
if (millis() - lastDataTime > DATA_SAMPLE_TIME) {
lastDataTime = millis();
gathersomedata();
// You may want to read all Serial2 input first, to make
// sure old data doesn't get mixed in with the new response.
senddatatoESP();
sentTime = millis();
waitingForResponse = true;
responseLen = 0; // ready for new response
}
// If we're expecting a response, did we get it?
if (waitingForResponse) {
if (check_for_esp_response("OK")) {
// Got it!
lightGreenLED();
waitingForResponse = false;
} else if (millis() - sentTime > 2000UL) {
// Too long!
lightRedLED();
waitingForResponse = false;
} // else, still waiting
}
// Check and handle the door OPEN and CLOSE buttons,
// based on the current door state and time
switch (doorState) {
case DOOR_CLOSED:
if (doorOpenPressed) {
digitalWrite(DoorOpenRelay, LOW);
relayChanged = millis();
doorState = DOOR_OPENING;
}
break;
case DOOR_OPENING:
// Has the door been opening long enough?
if (millis() - relayChanged > DOOR_OPEN_TIME) {
digitalWrite(DoorOpenRelay, HIGH);
doorState = DOOR_OPENED;
} else if (!doorOpenPressed && doorClosePressed) {
// Oops, changed their mind and pressed the CLOSE button.
// You may want to calculate a relayChanged time that
// is set back from millis() based on how long the
// door has been opening. If it just started opening,
// you probably don't want to drive the relay for the
// full 30 seconds.
...
}
break;
case DOOR_OPENED:
if (doorClosePressed) {
...
}
break;
case DOOR_CLOSING:
if (millis() - relayChanged > DOOR_CLOSE_TIME) {
...
}
break;
}
}
void openadoor()
{
doorOpenPressed = true;
}
bool check_for_esp_response(const char* term)
{
bool found = false;
if (Serial2.available()) {
// You should make sure you're not running off the end
// of "buffer" here!
buffer[responseLen++] = Serial2.read();
int len = strlen(term);
if (responseLen >= len) {
if (strncmp(buffer + responseLen - len, term, len) == 0) {
found = true;
}
}
}
return found;
}
The key is that you don't block or delay anywhere. loop gets called over and over, and it just checks a few variables. Most of the time, there's nothing to do. But sometimes, based on the state or the current time, it gathers some data, sends it, reads the response, and opens or closes the door. These actions do not interfere with each other, because there are no blocking while loops, only quick checks with if statements.
Open the door in the ISR and set a flag. Also store the time when you opened it. Both of those variables should be declared volatile.
Then in your main loop see if:
The flag is set; and
Time is up
If so, close the door (and clear the flag).
May I assume that setting the variables as "volatile" will prevent the compiler optimizing them? If so, then would you mind explaining why you thought this necessary.
Variables modified inside an ISR may change when the compiler does not expect them to. Using volatile tells the compiler to reload such variables from RAM (and not cache them into a register) so it always gets the most up-to-date copy.
Just as an example, say you had a flag set inside an ISR. And in your main (non-ISR) code you had this:
flag = false;
while (!flag)
{ } // wait for flag to be set
The compiler looks at that and thinks "well, flag will never change" and optimizes away the test for it changing. With volatile though, the compiler keeps the test, because it has to keep reloading flag from RAM.