Pico: strange behaviour with setWatch on BTN1

Posted on
  • Hey Gordon,

    I have a strange phenomenon with setWatch. The Code:

    clearWatch();
    setWatch(demo, BTN1, {repeat:true, edge:'falling', debounce:50 });
    The demo() function just does some LED blinking.

    When pressing the button the watchfunction runs once most times, but sometimes twice and in rare cases it even loops forever without stopping.
    Sometimes i only have to move my finger on the button without pressing it and the demo function runs.
    I tried all kinds of parameter combinations, but did not find one that works once every time the button is pressed.
    Any idea?

    Thanks in advance,
    Marc

  • What board are you using? Looks like it's not an Espruino one given you're using the falling edge?

    Some other boards have really cheap buttons that don't always work, and some may not have pullup/down resistors, which would mean than when not pressed the signal would 'float'

  • Sorry just read the title - this is on a Pico?

    To detect a button press, you probably want edge:'rising'?

    Are you using an up to date firmware? I know some earlier ones had issues with debouncing.

    Also, is that the only code you're running? Could be be that the pin state on BTN (which is actually pin C13) could be being reset to a pure input somehow? By default it is in input_pulldown mode, which uses an internal resistor to stop it floating when the button isn't pressed.

  • Yes, it's a pico.
    The firmware is 1v85.
    The demo code sends analogwrite to the pins B8,A2,A3,B10,B1,A7,A6,A5,A10,B9,A8,B7,B6­,B5,B4,B3. Nothing else in that function.
    I will try again with rising, but I think I also tried that before.

  • If it keeps happening, maybe you could get the code down to something that you're happy sharing, and could post it up so I can see if I can reproduce it?

  • I see similar "sensitive" behavior with the button on my pico, and just assumed it was a defective button. Since i don't really use it, it's not an issue.

    However, same thing: Seems sometimes I just need to get my finger near the button for it to activate. Odd, since the expected tactile "snap" of the button when you actually press it is there. Yet, it seems to take just the lightest of contact -- no actual push -- to activate it.

    This is on the canned demo LED lighting watch that is preloaded with the IDE.

  • Played with it a bit more and, at least in my case, it's definitely some static sensitivity. If I rest my finger on the button, then it takes a positive, tactile-feedback press -- every time -- to activate the button.

    If I simply bring my finger over to to the board at random time to "press" the button, it will activate often at my finger just touches, without any force, the top of the button.

  • Could the pin be floating, instead of being pulled up or down? I forget which way the button is wired (ie if it goes to vcc or gnd when pressed) - set the pin to be pulled up or down as appropriate. If it's just an input, it will float when button isn't pressed, and the voltage measured on the pin will depend on ambient electromagnetic fields. That definitely sounds like a floating pin.

  • Maybe check getPinMode(BTN)? It should report input_pulldown.

    As @DrAzzy says, if it were just floating then it would cause the kinds of issues you're reporting

  • Hello,
    I experience a reliability problem as exposed above. The setWatch function sometimes misses the button event (I press once a second). It's not a floating problem, my voltmeter says 3.21 V. Here is the code :

    var ledjaunestate = false;
    pinMode(B10, 'input_pullup');
    setWatch(function() {
    ledjaunestate = !ledjaunestate;
    digitalWrite(B1, ledjaunestate);
    }, B10, { repeat: true, debounce : 5, edge: "falling" });

    It's on Pico with firmware : espruino_1v85.506_pico_1r3_wiznet.bin
    Any clue ?

    Thanks a lot
    GB

  • @Marc, is your strange phenomenon still well and alive? Could you share your demo() function?

    @GeekBot, what does your volt meter say when the Button is pressed? From your code - pinMode(...,"input_pullup") - I conclude that the button - when pressed - connects pin B10 to GND - correct?

    What kind of button do you have? What's the cleanness of the contacts? No insult here, but buttons come with all kinds of behavior. For the debounce, 5ms is a bit short, most people use 50..100ms.

    Sometimes it is just some electro-mechanical issue... lousy breadboard connectivity...

  • It's exactly the same as dwallersv reported in #7.
    If I press the button with a screwdriver it works as expected, when pressing with my finger the event is called often when just touching the button without pressing it.

    Best wishes,
    Marc

  • @Marc, can you share a pic of the button?

    Does your screw driver have an insulating handle?

    It sound to me as if the button part that you touch has some conductivity and connects 'you' with the pin. Depending on the 'electro smog' you are in, this make the pin to sense edges and triggers by setWatch(). To verify this in an experiment: remove the button, stand free, take a shot piece of blank wire and touch B10...

    Furthermore, I was reading your post #4 where you describe vaguely what else is going on. Do you know the power drawn by these number of outputs? I'm going on a goose chase here, but it could well be that in addition to the button issue, there is a power issue going on. Even though the chip can source/drain 20mA with guaranteed levels per pin, there is a total of power that can be sourced and that is max 120mA - p59 of STM32F401xD datasheet.

  • Thank you for your questions/answers. I did some tests and measurements so, here are my answers :

    • What does your volt meter say when the Button is pressed? : it says 0.00 v. I changed the wire which connects to the pico header. Same effect.
    • When pressed - connects pin B10 to GND - correct? Yes, it is wired to the ground. I also checked with the ohmmeter.
    • What kind of button do you have? It is a http://www.farnell.com/datasheets/182522­1.pdf microswitch. 01L series. I did several objects with these OMRON switches that are quite good and have very low and fast bounce. It is brand new. I will change it and give you the result of the new tests.
    • I tested with various debouncing duration, but it has no effect.
      I come back to you with test results with another switch.
      Best regards
      GB

  • Test with - external -different pullup resistors: 10K, 4.7K 1K 470R... If this does not help, I don't know what to say...

    Or - if possible with the overall setup - make the switches connect to 3.3V (or 5V). Pico inputs are 5V tolerant. If it's not working with built in "input_pulldown", use same external resistors...

  • @Marc @dwallersv does the Pico look ok around where the button is? Maybe try cleaning around the sides of it with some board/contact cleaner and a small brush?

    The internal pullup resistor is around 40k, so I guess it's possible that if the contacts on the side of the button got dirty they'd get conductive and would start to pull the voltage on the input up - and then when you got your finger close, electrical noise might just be enough to set it off.

    @GeekBot The underlying hardware detects changes in the input, but not whether it is rising or falling. If the input changes state really fast it can go down to 0v, triggering the IRQ, but can then rise back up by the time the IRQ has fired so a 1 is read, and can then fall again. Espruino then thinks the button is in the raised state, and doesn't call your callback.

    There's actually very little that can be done about that internally... You could try adding a small capacitor across the button to stop it happening.

    Or one simple way might be to just detect the press, without a debounce?

    var ledjaunestate = false;
    var press = false;
    pinMode(B10, 'input_pullup');
    setWatch(function() {
      if (press) return;
      press=true;
      setTimeout(function(){press=false;},100)­;
      ledjaunestate = !ledjaunestate;
      digitalWrite(B1, ledjaunestate);
    }, B10, { repeat: true, edge: "both" });
    
  • Thank you Gordon
    Testing your code, it does what is expected. However, I have difficulties to link my previous code with your explanation. It probably comes from my ignorance of how Espruino works internally. One additional question: what is the effect of the debounce parameter ? In which use cases shall I use it ?
    Thanks again for your help.
    GB

  • When you specify debounce it doesn't call your function until the button has stayed in the new state for the given number of milliseconds. The idea is to stop the function being called multiple times from one press - however because of the way the underlying hardware works, with very noisy buttons you can sometimes (although it's unlikely) get into the state where the button is pressed but during the last interrupt Espruino received, it was read as having been released.

    ... however, if you only care about whether the button has changed state then you don't have to care about that. You can effectively do your own debouncing by doing something when the button first changes state and then ignoring any extra changes for a fraction of a second.

  • @Gordon's code implements an state machine with two states and dynamic behavior: press false and true and timer driven return to false.

    A very first 'spike' transitions from false to true, executes the application function (invert and apply LED state), AND becomes unresponsive - ignores any pin state changes - for the next 100ms and then flips automatically back to state false. Returning to state false puts it back into responsive state.

    'Unfortunately', it implements only half of 'the world'. With this setup of repeat: true, holding the switch for longer than 100ms AND THEN releasing it, creates another trigger of the application function: invert and apply LED state. This means that a press longer than 100ms switches twice the LED. (Yellow(?) LED goes either on for the pressed time or off.

    To verify this - I assume - unintended behavior with - PICO or Legacy - on-board components BTN1 and LED1, load and run this code... and you notice: it is difficult to toggle LED1 (ledrougestate... ;-) ). You have press the button for less than 100ms, which is more like a 'gentle hitting' / tapping:

    var ledjaunestate = false;
    var press = false;
    // pinMode(B10, 'input_pullup');
    pinMode(BTN1, 'input_pulldown');
    setWatch(function() {
      if (press) return;
      press=true;
      setTimeout(function(){press=false;},100)­;
      ledjaunestate = !ledjaunestate;
    //  digitalWrite(B10, ledjaunestate);
      digitalWrite(LED1, ledjaunestate);
    // }, B10, { repeat: true, edge: "both" });
    }, BTN1, { repeat: true, edge: "both" });
    

    The code has some more (inherent) flaws: press the button for more than 100ms, then release it for less than 100ms with - finally - pressing it for A) more or B) less than 100ms, you get a toggle or a double toggle. Not necessarily the expected UX.

    To (partially) complete the state machine, setting of press to false has to be conditioned similarly to the setting to true. Before 'the LED switch' - accepts a new trigger, the triggering button has to return to its original state for a defined time. To achieve a SINGLE LED switch for ONE PRESS OF ANY DURATION of the button, the state of the button has to be tested (repeatedly) and only if the button has (solidly, after a defined time) switched to off, press is set back to false.

    var ledjaunestate = false;
    var press = false;
    // pinMode(B10, 'input_pullup');
    pinMode(BTN1, 'input_pulldown');
    function resetPressOnBtnOff() {
      // if/then/else specific to BTN1's pulldown
      if (digitalRead(BTN1)) {
        setTimeout(resetPressOnBtnOff, 60);
      } else {
        press = false;
      }
    }
    setWatch(function() {
      if (press) return;
      press=true;
    //  setTimeout(function(){press=false;},100)­;
      setTimeout(resetPressOnBtnOff, 100);
      ledjaunestate = !ledjaunestate;
    //  digitalWrite(B10, ledjaunestate);
      digitalWrite(LED1, ledjaunestate);
    // }, B10, { repeat: true, edge: "both" });
    }, BTN1, { repeat: true, edge: "both" });
    

    A cleaned up version of the code (for BTN1):

    var ledredstate = false;
    var pressed = false;
    pinMode(BTN1, 'input_pulldown');
    function resetPressedOnBtn1Off() {
      if (digitalRead(BTN1)) {
        setTimeout(resetPressedOnBtn1Off, 60);
      } else {
        pressed = false;
      }
    }
    setWatch(function() {
      if (pressed) return;
      pressed = true;
      setTimeout(resetPressedOnBtn1Off, 100);
      ledredstate = !ledredstate;
      digitalWrite(LED1, ledredstate);
    }, BTN1, { repeat: true, edge: "rising" });
    

    Note the edge: "rising" option. No need for both...

    Even though the button test is added, there is still a 'window of opportunity' for a jitter: when the button is released about the time of the pin test, a bouncing may be experienced...

    Furthermore, there are still inherent flaws: between the repeated testing of the button state, the user can have - after some decent press time - released, pressed very briefly, and released the button again, and that action gets lost. Since by experience mechanical button presses are usually longer than 100ms, choosing shorter interval makes it work... something between the (button's typical) bouncing (time) and the 100ms. If it is shorter than the (button's typical) bouncing (time), unpredictable behavior creeps in again.

    Therefore, only making the state machine switching symmetrical makes it more predictable... It boils down to the UX specifications: Presses and releases of a switch have to be pressMs and releaseMS* in order to reliably detect and execute a toggle.

    Debouncing is not a simple task... and buttons with consistent and short bouncing avoid to make it worse... (the buttons picked - as described in post #14 - seem to be decent... except you got a 'lemon' or 'monday' or 'friday late' lot version...)

    PS: Looks like a toggle module could come in handy to take the tedious work off of the hand of the application...

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Pico: strange behaviour with setWatch on BTN1

Posted by Avatar for Marc @Marc

Actions