boarchuz / HULP

ESP32 ULP Coprocessor Helper
MIT License
185 stars 20 forks source link

How to count capacitive touches #36

Open trullock opened 1 month ago

trullock commented 1 month ago

Thanks for this awesome library

I've got stuck with one problem, I'm hoping you can help me

I want to use capacitive touch to wake up from sleep, which I've implemented below. This all works great.

However, the nature of my project is that the physical touch event might be accidental, so I want to make it so the wake-up-touch-logic is smarter and looks for a more definitely deliberate touch interaction.

For example, I want to make it so the user has to touch the pin 5 times where each touch is within a second of the previous touch.

I've been looking at the button debouncing example as this code is probably very similar to what I need but I'm just not used to working at such a low level that I can't figure out the exact code :(

here's where I've got to that works and makes sense:

    enum {
        LBL_TOUCH_INTERVAL,
        LBL_HALT
    };

    const ulp_insn_t program[] = {

        M_UPDATE_TICKS(),

        M_IF_MS_ELAPSED(LBL_TOUCH_INTERVAL, ULP_INTERVAL_MS, LBL_HALT),
            M_TOUCH_BEGIN(),
            M_TOUCH_WAIT_DONE(),

            I_TOUCH_GET_GPIO_VALUE(GPIO_NUM_2),
            M_BGE(LBL_HALT, 3000),
                I_WAKE(),

        M_LABEL(LBL_HALT),
            I_HALT(),
        };

    const hulp_touch_controller_config_t controller_config = HULP_TOUCH_CONTROLLER_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(hulp_configure_touch_controller(&controller_config));

    const hulp_touch_pin_config_t pin_config = HULP_TOUCH_PIN_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(hulp_configure_touch_pin(GPIO_NUM_2, &pin_config));

    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);

    ESP_ERROR_CHECK(hulp_ulp_load(program, sizeof(program), 1ULL * 10 * 1000, 0));
    ESP_ERROR_CHECK(hulp_ulp_run(0));

Thanks very much

boarchuz commented 1 month ago

Hi @trullock,

This is how I might approach that:

    #define TOUCH_DETECT_HOLD_TIMEOUT_MS    5000    /* If the touchpad is held for this long, assume a false reading and exit. */
    #define TOUCH_DETECT_IDLE_TIMEOUT_MS    1000    /* Timeout between consecutive touches. If the next touch doesn't arrive in this time after the last release, then exit. */
    #define TOUCH_DETECT_DEBOUNCE_MS        50      /* Touch readings may be noisy as the user's finger settles on the pad. Delay for this duration when a touch or release event occurs. */
    #define TOUCH_DETECT_THRESHOLD          3000    /* Value from the touch sensor to interpret as a touch (<) vs release (>=) */
    #define TOUCH_DETECT_COUNT              5       /* Number of touches required to wake SoC. */

    enum {
        LBL_NEW_TOUCH_DETECTED,
        LBL_WAIT_RELEASE,
        LBL_RELEASED,
        LBL_WAIT_NEXT_TOUCH,
        LBL_WAKE,
        LBL_HALT,
    };

    const ulp_insn_t program[] = {
        // The stage register will be used to keep count of the number of touches. Reset counter to 0.
        I_STAGE_RST(),

        // Check if touched. Go straight back to sleep if not.
        M_TOUCH_BEGIN(),
        M_TOUCH_WAIT_DONE(),
        I_TOUCH_GET_GPIO_VALUE(GPIO_NUM_2),
        M_BGE(LBL_HALT, TOUCH_DETECT_THRESHOLD),

    M_LABEL(LBL_NEW_TOUCH_DETECTED),
        /* TOUCHED */
        // Increment the counter. If it has reached TOUCH_DETECT_COUNT then wake the SoC.
        I_STAGE_INC(1),
        M_BSGE(LBL_WAKE, TOUCH_DETECT_COUNT),

        M_DELAY_MS_20_60000(TOUCH_DETECT_DEBOUNCE_MS), // Careful to avoid macros that might clobber the stage register. This one uses R0 so it's ok to use.

        // This block waits for a release event or hold timeout.
        M_UPDATE_TICKS(),
        I_RD_TICKS(TOUCH_DETECT_HOLD_TIMEOUT_MS),
        I_MOVR(R1, R0),
        M_LABEL(LBL_WAIT_RELEASE),
            M_TOUCH_BEGIN(),
            M_TOUCH_WAIT_DONE(),
            I_TOUCH_GET_GPIO_VALUE(GPIO_NUM_2),
            M_BGE(LBL_RELEASED, TOUCH_DETECT_THRESHOLD),
            M_UPDATE_TICKS(),
            I_RD_TICKS(TOUCH_DETECT_HOLD_TIMEOUT_MS),
            I_SUBR(R0, R0, R1),
            M_BGE(LBL_HALT, hulp_ms_to_ulp_ticks((TOUCH_DETECT_HOLD_TIMEOUT_MS))),
            M_BX(LBL_WAIT_RELEASE),
        M_LABEL(LBL_RELEASED),

        /* RELEASED */
        M_DELAY_MS_20_60000(TOUCH_DETECT_DEBOUNCE_MS),

        // And this block waits for the next touch event or idle timeout.
        M_UPDATE_TICKS(),
        I_RD_TICKS(TOUCH_DETECT_IDLE_TIMEOUT_MS),
        I_MOVR(R1, R0),
        M_LABEL(LBL_WAIT_NEXT_TOUCH),
            M_TOUCH_BEGIN(),
            M_TOUCH_WAIT_DONE(),
            I_TOUCH_GET_GPIO_VALUE(GPIO_NUM_2),
            M_BL(LBL_NEW_TOUCH_DETECTED, TOUCH_DETECT_THRESHOLD),
            M_UPDATE_TICKS(),
            I_RD_TICKS(TOUCH_DETECT_IDLE_TIMEOUT_MS),
            I_SUBR(R0, R0, R1),
            M_BGE(LBL_HALT, hulp_ms_to_ulp_ticks((TOUCH_DETECT_IDLE_TIMEOUT_MS))),
            M_BX(LBL_WAIT_NEXT_TOUCH),

        M_LABEL(LBL_WAKE),
            I_WAKE(),
        M_LABEL(LBL_HALT),
            I_HALT(),
    };

It's not tested, optimised, etc but should get you pretty close to what you want.

trullock commented 1 month ago

This is absolutely spot on, thank you so much for your generous help.