feat: Adiciona suporte para dispositivo combo (relé + sensor de temperatura/umidade) e driver para sensor ultrassônico JSN-SR04T
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
/* JSN-SR04T Ultrasonic Distance Sensor Driver Implementation */
|
||||
|
||||
#include "jsn_sr04_driver.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "rom/ets_sys.h"
|
||||
|
||||
static const char *TAG = "JSN_SR04";
|
||||
|
||||
/* Configuração do sensor */
|
||||
static jsn_sr04_config_t sensor_config = {0};
|
||||
static bool initialized = false;
|
||||
|
||||
/* Constantes físicas */
|
||||
#define SPEED_OF_SOUND_CM_US 0.0343 /* Velocidade do som: 343 m/s = 0.0343 cm/µs */
|
||||
#define TRIGGER_PULSE_US 10 /* Duração do pulso trigger em µs */
|
||||
#define MIN_WAIT_BETWEEN_MS 60 /* Tempo mínimo entre leituras em ms */
|
||||
|
||||
/* Timeout padrão baseado na distância máxima */
|
||||
#define DEFAULT_MAX_DISTANCE_CM 400
|
||||
#define CALC_TIMEOUT_US(dist_cm) ((dist_cm * 2 * 100) / 343 + 1000)
|
||||
|
||||
static int64_t last_read_time = -MIN_WAIT_BETWEEN_MS * 1000;
|
||||
|
||||
/**
|
||||
* @brief Aguarda mudança de nível no pino ECHO
|
||||
*
|
||||
* @param level Nível esperado (0 ou 1)
|
||||
* @param timeout_us Timeout em microsegundos
|
||||
* @return Tempo decorrido em µs, ou -1 se timeout
|
||||
*/
|
||||
static int64_t wait_for_echo_level(int level, uint32_t timeout_us)
|
||||
{
|
||||
int64_t start = esp_timer_get_time();
|
||||
|
||||
while (gpio_get_level(sensor_config.echo_gpio) != level) {
|
||||
if ((esp_timer_get_time() - start) > timeout_us) {
|
||||
return -1;
|
||||
}
|
||||
ets_delay_us(1);
|
||||
}
|
||||
|
||||
return esp_timer_get_time() - start;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Envia pulso de trigger
|
||||
*/
|
||||
static void send_trigger_pulse(void)
|
||||
{
|
||||
/* Garante que trigger está LOW */
|
||||
gpio_set_level(sensor_config.trigger_gpio, 0);
|
||||
ets_delay_us(2);
|
||||
|
||||
/* Pulso HIGH de 10µs */
|
||||
gpio_set_level(sensor_config.trigger_gpio, 1);
|
||||
ets_delay_us(TRIGGER_PULSE_US);
|
||||
gpio_set_level(sensor_config.trigger_gpio, 0);
|
||||
}
|
||||
|
||||
esp_err_t jsn_sr04_init(const jsn_sr04_config_t *config)
|
||||
{
|
||||
if (config == NULL) {
|
||||
ESP_LOGE(TAG, "Config is NULL");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (initialized) {
|
||||
ESP_LOGW(TAG, "Already initialized, reinitializing...");
|
||||
jsn_sr04_deinit();
|
||||
}
|
||||
|
||||
/* Salva configuração */
|
||||
sensor_config = *config;
|
||||
|
||||
/* Define timeout padrão se não especificado */
|
||||
if (sensor_config.max_distance_cm == 0) {
|
||||
sensor_config.max_distance_cm = DEFAULT_MAX_DISTANCE_CM;
|
||||
}
|
||||
|
||||
if (sensor_config.timeout_us == 0) {
|
||||
sensor_config.timeout_us = CALC_TIMEOUT_US(sensor_config.max_distance_cm);
|
||||
}
|
||||
|
||||
/* Configura GPIO TRIGGER como saída */
|
||||
gpio_config_t trigger_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = (1ULL << sensor_config.trigger_gpio),
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
};
|
||||
|
||||
esp_err_t ret = gpio_config(&trigger_conf);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to configure TRIGGER GPIO %d", sensor_config.trigger_gpio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Configura GPIO ECHO como entrada */
|
||||
gpio_config_t echo_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pin_bit_mask = (1ULL << sensor_config.echo_gpio),
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
};
|
||||
|
||||
ret = gpio_config(&echo_conf);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to configure ECHO GPIO %d", sensor_config.echo_gpio);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Inicializa trigger em LOW */
|
||||
gpio_set_level(sensor_config.trigger_gpio, 0);
|
||||
|
||||
initialized = true;
|
||||
|
||||
ESP_LOGI(TAG, "JSN-SR04T initialized - TRIGGER: GPIO%d, ECHO: GPIO%d, Max: %d cm",
|
||||
sensor_config.trigger_gpio, sensor_config.echo_gpio,
|
||||
sensor_config.max_distance_cm);
|
||||
|
||||
/* Aguarda sensor estabilizar */
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t jsn_sr04_read(jsn_sr04_reading_t *reading)
|
||||
{
|
||||
if (!initialized) {
|
||||
ESP_LOGE(TAG, "Sensor not initialized");
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (reading == NULL) {
|
||||
ESP_LOGE(TAG, "Reading pointer is NULL");
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
/* Inicializa leitura como inválida */
|
||||
reading->valid = false;
|
||||
reading->distance_cm = 0.0f;
|
||||
reading->echo_time_us = 0;
|
||||
|
||||
/* Verifica intervalo mínimo entre leituras */
|
||||
int64_t current_time = esp_timer_get_time();
|
||||
int64_t time_since_last = current_time - last_read_time;
|
||||
|
||||
if (time_since_last < (MIN_WAIT_BETWEEN_MS * 1000)) {
|
||||
int64_t wait_us = (MIN_WAIT_BETWEEN_MS * 1000) - time_since_last;
|
||||
ESP_LOGD(TAG, "Waiting %lld us before next reading", wait_us);
|
||||
ets_delay_us((uint32_t)wait_us);
|
||||
}
|
||||
|
||||
/* Desabilita interrupções para timing preciso */
|
||||
portDISABLE_INTERRUPTS();
|
||||
|
||||
/* Envia pulso de trigger */
|
||||
send_trigger_pulse();
|
||||
|
||||
/* Aguarda ECHO ir para HIGH (início do pulso) */
|
||||
int64_t wait_high = wait_for_echo_level(1, sensor_config.timeout_us);
|
||||
if (wait_high < 0) {
|
||||
portENABLE_INTERRUPTS();
|
||||
ESP_LOGW(TAG, "Timeout waiting for ECHO HIGH");
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Mede duração do pulso HIGH */
|
||||
int64_t start_time = esp_timer_get_time();
|
||||
int64_t pulse_duration = wait_for_echo_level(0, sensor_config.timeout_us);
|
||||
|
||||
portENABLE_INTERRUPTS();
|
||||
|
||||
if (pulse_duration < 0) {
|
||||
ESP_LOGW(TAG, "Timeout waiting for ECHO LOW");
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
/* Atualiza timestamp da última leitura */
|
||||
last_read_time = esp_timer_get_time();
|
||||
|
||||
/* Calcula distância
|
||||
* Fórmula: distância = (tempo_echo * velocidade_som) / 2
|
||||
* Dividido por 2 porque o som vai e volta
|
||||
*/
|
||||
reading->echo_time_us = (uint32_t)pulse_duration;
|
||||
reading->distance_cm = (pulse_duration * SPEED_OF_SOUND_CM_US) / 2.0f;
|
||||
|
||||
/* Valida leitura */
|
||||
if (reading->distance_cm < 2.0f || reading->distance_cm > sensor_config.max_distance_cm) {
|
||||
ESP_LOGD(TAG, "Distance out of range: %.2f cm", reading->distance_cm);
|
||||
reading->valid = false;
|
||||
return ESP_ERR_INVALID_RESPONSE;
|
||||
}
|
||||
|
||||
reading->valid = true;
|
||||
|
||||
ESP_LOGI(TAG, "Distance: %.2f cm (echo: %u µs)",
|
||||
reading->distance_cm, reading->echo_time_us);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t jsn_sr04_get_distance(float *distance_cm)
|
||||
{
|
||||
if (distance_cm == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
jsn_sr04_reading_t reading;
|
||||
esp_err_t ret = jsn_sr04_read(&reading);
|
||||
|
||||
if (ret == ESP_OK && reading.valid) {
|
||||
*distance_cm = reading.distance_cm;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t jsn_sr04_deinit(void)
|
||||
{
|
||||
if (!initialized) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* Reseta GPIOs */
|
||||
gpio_reset_pin(sensor_config.trigger_gpio);
|
||||
gpio_reset_pin(sensor_config.echo_gpio);
|
||||
|
||||
initialized = false;
|
||||
|
||||
ESP_LOGI(TAG, "JSN-SR04T deinitialized");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user