0
votes

In my project I need to measure distances up to 3-4m, so I am using a HC-SR04P sensor hooked up to an ESP32 dev board.

The code is written without any third-party library (was inspired by a very simple HC-SR04 arduino library, though), in plain C, within a project created from the ESP32 eclipse IDF plugin; no extra libraries or arduino code; just the RTOS.

Everything works fine when the device boots and measurements are pretty accurate, but after a while (can't say exactly what triggers this), the sensor/devboard circuit (can't say which) starts behaving strangely : after the TRIG pulse, the ECHO pin does not go HIGH within a reasonable 1s timeout, and no measurement is performed.

Once this happens, no new measurement is performed again unless reboot/power on; it looks like something happens and somehow there is a faulty state either for the sensor or within the communication code.

A couple of observations :

  • sensor is the right version to be powered at 3.3V.
  • HC-SR04P uses GPIO2 and GPIO4 for TRIG and ECHO.
  • measurements are not required to be frequent, hence the 30s timer for the measurement task.
  • at power on, everything works fine.
  • after reset by dev board micro-switch, everything works correctly again.
  • when timeout occurs, re-init the sensor (settings up GPIOs, etc.), but nothing happens; still timeouts.

For reference, the timing function is below (the HCSR04_Info struct holds only pin and measurement data); it is called from a timed task every 30s.

uint32_t hcsr04_timing(HCSR04_Info* pDevice)
{
    // TRIG pulse for 10ms
    gpio_set_level(pDevice->trig, 1);
    ets_delay_us(10);
    gpio_set_level(pDevice->trig, 0);

    pDevice->startMicros = esp_timer_get_time();

    // wait for the echo pin HIGH or timeout
    while ((!gpio_get_level(pDevice->echo)) && (esp_timer_get_time() - pDevice->startMicros) <= pDevice->timeout);

    if (!gpio_get_level(pDevice->echo)) {
        pDevice->status = STATUS_OFFLINE;
        ESP_LOGE(TAG, "hcsr04_timing timeout (1)");
        return 0;
    }

    pDevice->startMicros = esp_timer_get_time();

    // wait for the echo pin LOW or timeout
    while ((gpio_get_level(pDevice->echo)) && (esp_timer_get_time() - pDevice->startMicros) <= pDevice->timeout);

    if (gpio_get_level(pDevice->echo)) {
        pDevice->status = STATUS_OFFLINE;
        ESP_LOGE(TAG, "hcsr04_timing timeout (2)");
        return 0;
    }

    pDevice->status = STATUS_ONLINE;
    pDevice->endMicros = esp_timer_get_time();

    return pDevice->endMicros - pDevice->startMicros;
}

Any help is appreciated. Thank you.

2
I smell that it may be some variable overflow. Have you checked that? If you are using signed variables for something related to the ultrasonic device, make sure they are not overflowing. It is my guess.campescassiano
Also, you may add one extra GPIO in this code, so you can toggle a LED to visually see if this part of the code is being reached after the problem happens.campescassiano
Thanks for the idea; I think it is unlikely, since there is no code that could overflow; or at least none that I see; I just the get the timeout message in the output; a repeated overflow might cause a reboot or other undesired behavior. In my case, the module keeped printing timeout messages over the entire weekend ...stefanu

2 Answers

0
votes

This does not generate a pulse of 10 ms; it's 10 us. Probably takes your device into an undetermined state eventually.

    // TRIG pulse for 10ms
    gpio_set_level(pDevice->trig, 1);
    ets_delay_us(10);
    gpio_set_level(pDevice->trig, 0);

The comment in the header file where ets_delay_us() is defined says: In FreeRTOS task, please call FreeRTOS apis.

Anyway, use delay(10) if in Arduino-land; or vTaskDelay(pdMS_TO_TICKS(10)) if in FreeRTOS-land.

0
votes

Following up on campescassiano suggestions on overflow, the solution finally presented itself. Not really an overflow in the exact sense of the problem, but closely related.

It's finally a stupid bug in the code, so please close or delete the question if appropriate.

The problem was that pDevice->startMicros was defined as an uint32_t (probably because of a copy/paste or bad habit error), while esp_timer_get_time() returns microseconds as an uint64_t.

So it 'overflows' at about 1h 11m 34s (which is about 232 microseconds) after boot, and timeout calculations become off since (esp_timer_get_time() - pDevice->startMicros) will obviously be an uint64_t.

Because of that (esp_timer_get_time() - pDevice->startMicros) <= pDevice->timeout will always be false after 1h 11m 34s, so the loop breaks before getting an ECHO input.