2022/02/27

Ejemplo de ESP32 con lectura de DHT11

Extendiendo los pasos de primer contacto con ESP32, va tipo receta con anotaciones sin mayores explicaciones, la idea es terminar con una carpeta con el proyecto armado.

 

Instalación de dependencias

 

sudo apt install git wget flex bison gperf python3 python3-pip python3-setuptools cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0


Mi entorno suele ser una virtual, por comodidad también hago:

sudo apt install vim kdiff3 dirdiff

 

Estructura de archivos

 

mkdir esp http_request

 

~/esp/esp-idf
      esp-idf-lib
      http_request

 

Aunque he puesto el ejemplo http_request a la par de los repositorios, puede estar en cualquier lado.

 

Repositorios y setup herramientas

 

El primer repo te provee un montón de dispositivos, nos interesa el componente dht11.

git clone https://github.com/UncleRus/esp-idf-lib.git

git clone -b v4.4 --recursive https://github.com/espressif/esp-idf.git

 

El segundo es la adaptación de FreeRTOS a extensa de expresif.

 

cd esp-idf

./install.sh esp32 

 

Eso te instaló lo que haga falta para ESP32. Tanto acá como en pasos posteriores, donde dice esp32 puede ir esp32c3 o esp32s2, pero por ahora sólo probé con esp32, no debería en general pero pueden haber diferencias sutiles con los pines en estos ejemplos básicos.

 

El código


cd ../http_request

. ../esp-idf/export.sh

Esto es para que funcione el entorno de build.

 

El código es copia del ejemplo http_request:

cp -r ../esp-idf/examples/protocols/http_request/ .

Ajuste del proyecto a esp32:

idf.py set-target esp32

Sería un buen momento para hacer funcionar el ejemplo así como está, pero vamos directo al ejemplo completo.

 

Dependencias de código

 

En CMakeFiles.txt, agregá la dependencia a esp-idf-lib:

set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common $ENV{IDF_PATH}/../esp-idf-lib/components)

Menuconfig

idf.py menuconfig

Si está versionando, no te olvides de NO VERSIONAR sdkconfig una vez que hayas puesto los valores de conexión de la WiFi. Lo que hago yo es:

idf.py menuconfig -> Save

git add sdkconfig

git commit -m "xxx"

idf.py menuconfig

Example Connection Configuration --->
  [*] connect using WiFi interface
  (xxxxxxx) WiFi SSID
  (xxxxxxx) WiFi Password

Pero si tenés otros valores secretos se empieza a complicar. De un modo u otro, repito, NO VERSIONES SECRETOS.

 

Ajustes del código

 

Tomamos como base esp-idf-lib/examples/dht/main/main.c

includes

#include <stdio.h>
#include "dht.h"

constants

static const dht_sensor_type_t sensor_type = DHT_TYPE_DHT11;
static const gpio_num_t dht_gpio = 17;

La URL

#define WEB_SERVER "example.com" -> la IP que uses
#define WEB_PORT "80" -> el puerto que uses
#define WEB_PATH "/" -> según mi ejemplo "/collect.php"

static char *REQUEST_GET =
        "GET " WEB_PATH "/?t=%d&h=%d HTTP/1.0\r\n"...

La idea es reemplazar esos dos %d con sprintf(), para eso incluí <stdio.h>, con los valores leídos.

Las variables

char send_buf[256];

int16_t temperature = 0;
int16_t humidity = 0;

La lectura del sensor

while(1) {
  if (dht_read_data(sensor_type, dht_gpio, &humidity, &temperature) == ESP_OK) {
    ESP_LOGI(TAG,"Humidity: %d%% Temp: %dC\n", humidity, temperature);
    sprintf(send_buf, REQUEST_GET, temperature, humidity);
    ESP_LOGI(TAG,"sending: \n%s\n",send_buf);
 } else {
    ESP_LOGE(TAG,"Could not read data from sensor\n");
 }

y la escritura, al buffer lo preparamos con el sprintf() anterior.

if (write(s, send_buf, strlen(send_buf)) < 0) {...



Acá el código entero resultante con propuestas de ejercicios intercalados, no deberías necesitarlo, pero no molesta. El proyecto completo en github

 

#include <string.h>
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "protocol_examples_common.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
#include "dht.h"


/* Constants that aren't configurable in menuconfig */
#define WEB_SERVER "192.168.1.102"
#define WEB_PORT "8080"
#define WEB_PATH "/collect.php"

static const dht_sensor_type_t sensor_type = DHT_TYPE_DHT11;
static const gpio_num_t dht_gpio = 17;


static const char *TAG = "temp_collector";

static char *REQUEST_GET = "GET " WEB_PATH "/?t=%d&h=%d HTTP/1.0\r\n"
    "Host: "WEB_SERVER":"WEB_PORT"\r\n"
    "User-Agent: esp-idf/1.0 esp32\r\n"
    "\r\n";

// Ejercicio: enviar POST    
// Ejercicio: enviar json

static void http_get_task(void *pvParameters)
{
    const struct addrinfo hints = {
        .ai_family = AF_INET,
        .ai_socktype = SOCK_STREAM,
    };
    struct addrinfo *res;
    struct in_addr *addr;
    int s, r;
    char recv_buf[64];

    char send_buf[256];

    int16_t temperature = 0;
    int16_t humidity = 0;
 
    while(1) {
        if (dht_read_data(sensor_type, dht_gpio,
                &humidity, &temperature) == ESP_OK) {
            ESP_LOGI(TAG,
    "Humidity: %d%% Temp: %dC\n", humidity / 10, temperature / 10);
            sprintf(send_buf, REQUEST_GET, temperature / 10, humidity / 10);
        ESP_LOGI(TAG,"sending: \n%s\n",send_buf);
        } else {
            ESP_LOGE(TAG,"Could not read data from sensor\n");
        // Ejercicio: enviar mensaje
        }

        int err = getaddrinfo(WEB_SERVER, WEB_PORT, &hints, &res);

        if(err != 0 || res == NULL) {
            ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p",
                      err, res);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            continue;
        }

        addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
        ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s",
                        inet_ntoa(*addr));

        s = socket(res->ai_family, res->ai_socktype, 0);
        if(s < 0) {
            ESP_LOGE(TAG, "... Failed to allocate socket.");
            freeaddrinfo(res);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
            continue;
        }
        ESP_LOGI(TAG, "... allocated socket");

        if(connect(s, res->ai_addr, res->ai_addrlen) != 0) {
            ESP_LOGE(TAG,
                 "... socket connect failed errno=%d", errno);
            close(s);
            freeaddrinfo(res);
            vTaskDelay(4000 / portTICK_PERIOD_MS);
            continue;
        }

        ESP_LOGI(TAG, "... connected");
        freeaddrinfo(res);

        if (write(s, send_buf, strlen(send_buf)) < 0) {
            ESP_LOGE(TAG, "... socket send failed");
            close(s);
            vTaskDelay(4000 / portTICK_PERIOD_MS);
            continue;
        }
        ESP_LOGI(TAG, "... socket send success");

        struct timeval receiving_timeout;
        receiving_timeout.tv_sec = 5;
        receiving_timeout.tv_usec = 0;
        if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO,
           &receiving_timeout, sizeof(receiving_timeout)) < 0) {
            ESP_LOGE(TAG,
              "... failed to set socket receiving timeout");
            close(s);
            vTaskDelay(4000 / portTICK_PERIOD_MS);
            continue;
        }
        ESP_LOGI(TAG, "... set socket receiving timeout success");

        /* Read HTTP response */
        do {
            bzero(recv_buf, sizeof(recv_buf));
            r = read(s, recv_buf, sizeof(recv_buf)-1);
            for(int i = 0; i < r; i++) {
                putchar(recv_buf[i]);
            }
        } while(r > 0);

        ESP_LOGI(TAG, "... done reading from socket. Last read return=%d errno=%d.", r, errno);
        close(s);

        for(int countdown = 10; countdown >= 0; countdown--) {
            ESP_LOGI(TAG, "%d... ", countdown);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
        ESP_LOGI(TAG, "Starting again!");
    }
}

void app_main(void)
{
    ESP_ERROR_CHECK( nvs_flash_init() );
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    ESP_ERROR_CHECK(example_connect());

    xTaskCreate(&http_get_task, "http_get_task", 4096, NULL, 5, NULL);
}


Si hubieras usado el ejemplo http_request sin tocar y vez esa basura no te preocupes, es que el servidor te ha dado la respuesta comprimida, observá el Content-Encoding: gzip, no tenés error.


example.org
example.org


Errata


Me faltó mencionar que hay que conectar el sensor a algún pin, en este caso usé el 17

static const gpio_num_t dht_gpio = 17;

y no olvides poner la resistencia entre el positivo (3.3v) y el cable de datos.

No hay comentarios:

Publicar un comentario