feat: Adiciona suporte para dispositivo combo (relé + sensor de temperatura/umidade) e driver para sensor ultrassônico JSN-SR04T

This commit is contained in:
2026-01-17 00:34:24 +00:00
parent fcecd9d273
commit 5c063ff367
32 changed files with 467 additions and 9 deletions
+241
View File
@@ -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;
}