Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

It takes ~ 35 ms to for isTouch() resistive to return the correct value #458

Open
microbit-carlos opened this issue Dec 6, 2024 · 3 comments

Comments

@microbit-carlos
Copy link
Collaborator

microbit-carlos commented Dec 6, 2024

Connecting a crocodile clip between P0 and ground, and then calling uBit.io.P0.isTouched(TouchMode::Resistive) returns false for around 35 milliseconds.

As P0 is grounded, the correct behaviour should be to return 1 (touched) from the first time it's called.

#include "MicroBit.h"

MicroBit uBit;

int main() {
    uBit.init();

    // From the first time isTouched() is called, it takes 35ish ms to correctly return 1
    uint32_t stop_time = uBit.systemTime() + 50;
    while (uBit.systemTime() < stop_time) {
        uBit.serial.printf("%d", uBit.io.P0.isTouched(TouchMode::Resistive));
        uBit.sleep(4);
    }

}

Output:

0000000001111

Which returns the wrong value 9 times, with a 4ms delay = 36 ms.

A mode detailed example with precise timings can be expanded here

Hex: example2.hex.zip

#include "MicroBit.h"

MicroBit uBit;

int main() {
    uBit.init();

    uBit.display.print("S");

    // This delay is just added to prove it's not a startup time issue
    uBit.sleep(1000);

    // Uncommenting these lines fixes the issue
    // uBit.io.P0.isTouched(TouchMode::Resistive);
    // uBit.sleep(50);

    // From the first time isTouched() is called, it takes 35ish ms to correctly return 1
    uint32_t t = uBit.systemTime();
    while (uBit.io.P0.isTouched(TouchMode::Resistive) == 0) {
        uBit.serial.send(".");
    }
    int elapsed = uBit.systemTime() - t;
    uBit.serial.send("\n" + ManagedString(elapsed) + "ms\n");
    uBit.display.print(ManagedString(elapsed));
    uBit.sleep(1000);

    // This should be an infinite loop
    while (uBit.io.P0.isTouched(TouchMode::Resistive) == 1) {
        uBit.display.print("T");
        uBit.sleep(100);
    }

    // And this should never be reached
    uBit.display.print("X");
    while (true) {
        uBit.sleep(1000);
    }
}
@microbit-carlos
Copy link
Collaborator Author

This is by design, as in codal-core, the Button::periodicCallback() waits for 8 ticks (DEVICE_BUTTON_SIGMA_THRESH_HI), before it detects the button as "pressed", so that will be at least 32ms until it will return "true" in the example above.

While this is expected, for a user shorting the pins (and this came from a science experiment where this block was used as a water level detector, where water connects P0 to GND) and having an "if" statement or a "while" loop, it means that the first reading can be "incorrect" if the default pin state is "pressed".

What would be the best way to ensure the first isTouched() call returns a "correct" reading?
It's not ideal, but something like this would work, and not delay isTouch excesively:

int Button::isPressed()
{
    if (_pin.obj != this)
    {
        if (_pin.obj != NULL)
            _pin.obj->releasePin(_pin);

        _pin.obj = this;
        _pin.setPolarity( polarity == ACTIVE_HIGH ? 1 : 0);
        _pin.setPull(pullMode);
        this->status |= DEVICE_COMPONENT_STATUS_SYSTEM_TICK;

        // If the button is "pressed" it will only register as such after
        // DEVICE_BUTTON_SIGMA_THRESH_HI ticks in the periodicCallback().
        // To prevent a false negative when calling this method, start sigma
        // at a couple of ticks away and wait.
        if (buttonActive()) {
            sigma = DEVICE_BUTTON_SIGMA_THRESH_HI - 1;
            fiber_sleep(2 * SCHEDULER_TICK_PERIOD_US / 1000);
        }
    }

    return status & DEVICE_BUTTON_STATE ? 1 : 0;
}

@martinwork
Copy link
Collaborator

I think that would fire the DOWN event as if it had changed to pressed. How about...

        if (buttonActive()) {
            status |= DEVICE_BUTTON_STATE;
        }

@finneyj
Copy link
Contributor

finneyj commented Dec 17, 2024

I think I added the longer delay here due to the large amount of debouncing needed when using capacitate inputs... So we probably wat to be careful not to break that as we address this issue.

Suggest we perhaps:

  • Add a fastpath into Button.cpp for inputs with a stable behaviour, via a status field option.
  • Enabled disable that behaviour for resistive button inputs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants