Mostrando entradas con la etiqueta iot. Mostrar todas las entradas
Mostrando entradas con la etiqueta iot. Mostrar todas las entradas

2022/03/21

ESP32 con BMP280

En el marco del mínimo sistema IoT que he preparado para la futura materia Implementación de IoT, mientras se confeccionaba la lista de materiales para la carrera completa, hubo discusión acerca de si usar DHT11 o BMP280, ambos sensores de temperatura.


  • El DHT11 es más barato, usa un solo pin, pero es menos preciso.
  • El BMP280 usa I2C, dos pines y se deduce de la frase anterior que es más caro y preciso.

 

Lo que no se deduce es que el primero mide temperatura y humedad y el segundo además presión, salvo el modelo que he conseguido que no mide humedad.

Al DHT11, dependiendo de la presentación hace falta agregarle un resistencia de entre 5 a 10 K, le he puesto unas SMD rescatadas de unas viejas placas de una central telefónica hallada en un volquete en el centro cerca del trabajo.

 

DHT11 con resistencia SMD en los pines
DHT11 con resistencia SMD en los pines

 

Había implementado todo para DHT11 pero entre que las otras materias no lo iban a usar y que para ESP32c3 no funcionó, incorporé el BMP280 al ESP32c3 y luego al ESP32, llegando al punto de poder retirar al DHT11 de la lista de materiales, discusión resuelta. De todos modos quedó en el repo disponible.


Para la adaptación no hay ningún sufrimiento, hay que tomar la implementación con DHT11, borrar lo específico y reemplazarlo con el código de ejemplo de UncleRus, que es básicamente un include, elegir los pines, armar una estructura, inicializar y leer.

 

Respecto a los pines, a diferencia de DHT11 que usa un protocolo serial OneWire por software y se puede implementar en cualquier pin GPIO, BMP280 usa I2C por hardware, supuse que debía ser un subconjunto bastante menor.


Busqué en esp32-wroom-32_datasheet_en.pdf y nada. Por suerte luego hallé un tutorial https://randomnerdtutorials.com/esp32-i2c-communication-arduino-ide/ que decía que:

 

the SDA line may also be labeled as SDI and the SCL line as SCK.

 

y luego que se podían mapear a casi cualquier pin, supuse mal.

 

Alguna líneas interesantes del código, en estas se determina que chip es: 


bool bme280p = dev.id == BME280_CHIP_ID;
ESP_LOGI(TAG, \
    "BMP280: found %s\n", bme280p ? "BME280" : "BMP280");

 

Luego se manifiesta en los valores que se pueden completar:


if (bmp280_read_float( \
          &dev, \
          &temperature, \
          &pressure, \
          &humidity) != ESP_OK) {
  ESP_LOGI(TAG, "Temp./press. reading failed\n");
} else {
  ESP_LOGI(TAG, \
    "Press: %.2f Pa, Temp: %.2f C", pressure, temperature);
 

//if (bme280p) {
    sprintf(send_buf, REQUEST_POST, temperature , humidity );
//} else {
//    sprintf(send_buf, REQUEST_POST, temperature , 0);
//}

 

Lo dejé comantado pues la verdad es que me dá lo mismo mandar un cero hardcodeado que uno proveniente de la lectura, pero es incorrecto, debería no enviar el valor y/o agregar un campo con el tipo de sensor, para diferenciar el cero de cero humedad del cero de cero lectura.

Este sistema está todo hecho así, una reducción muy básica con múltiples oportunidades de completar y mejorar. Si te viene interesando, podés implementarlo siguiendo las instrucciones y luego:

  • Agregar campos para reportar errores.
  • Agregar timestamp, pensá donde...
  • Enviar JSON en lugar de x-www-form-urlencoded.
  • Implementar algún DNS para usar nombres en lugar de IPs. 
  • Usar https en lugar de http.
  • Envíar vía MQTT en lugar de HTTP-POST.

Truquito, en el router fijá la MAC address del servidor a una IP y te va a facilitar la vida.

Se supone que quizás viste todo esto antes de llegar hasta acá:


https://seguridad-agile.blogspot.com/2022/03/ejemplo-de-esp8266-con-lectura-de-dht11planB.html


https://seguridad-agile.blogspot.com/2022/02/ejemplo-de-esp32-con-lectura-de-dht11.html


https://seguridad-agile.blogspot.com/2022/02/primer-contacto-con-esp32.html




 



2022/03/19

Ejemplo de ESP8266 con lectura de DHT11: Plan B

Venimos de un momentáneo fracaso.

 

Plan B

 

La gente que viene de sistemas embebidos siente un cierto desprecio por ArduinoIDE debido al alto nivel de abstracción, te pasaste años estudiando como funciona una CPU, un microcontrolador, viendo como se configura bit a bit cada dispositivo y ArduinoIDE te lo reduce a un .setup() y obviamente en situaciones anormales no tenés recursos y probablemente hayas perdido el RealTime.

Se parece al desprecio que sentimos los que venimos del otro lado al ver como con cualquier gestor de Cloud cualquier iletrado puede armar cosas bastante complejas, completamente inconcientes de cómo instalar y configurar un sistema operativo, base de datos, servidor de lo que sea, optimización y hardening. No importa, no quiero ninguna flame war.


Te bajás ArduinoIDE de 

https://www.arduino.cc/en/software
 
Descomprimís:
 
tar -xf ../Downloads/arduino-1.8.19-linux64.tar.xz 

Primero no hice el install.sh pues mirándolo por encima no parecía aportar nada, luego al ver que no aparecía el programmer en tools, ejecuté arduino-linux-setup.sh, pero no era por eso, el flash se hace directamente.

Para ejecutar:

./arduino

Hay que agregar el board con 

  • File
    • Preferences
      • Additional Boars Manager URLs: 

ahí ponés

http://arduino.esp8266.com/stable/package_esp8266com_index.json

 

Luego vás a 

  • Tools 
    • Board
      • Board Manager
        • search: esp8266
          • esp8266 by ESP8266 Community
            • Install
    • Board
      • ESP8266 Boards
        • ESP Generic  Module
    • Port 
      • /dev/ttyUSB0
    • Manage Libraries
      • filtrar por dht
        • DHT Sensor Library for ESPx 
          • install


Para el DHT quise apoyarme en lo aprendido antes, pero no me sirvió, lo que hice fue incorporar DHT Sensor Library for ESPx
 
 
 
Y funcionó ok con el siguiente programa, que es una mezcla entre los ejemplos:
  • File
    • Examples
      • Examples for Generic ESP8266 Module
        • ESP8266HTTPClient
          • PostHTTPClient
      • Examples for Custom Libraries
        • DHT_sensor_library_for_ESPxDHT_ESP8266


#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

#include "DHTesp.h"
 
#define DEVICE_ID "61"
#define SERVER_IP "192.168.0.100:8080"

#define STASSID "" <- recordá no versionar
#define STAPSK  "" <- recordá no versionar
 
DHTesp dht;

float humidity = 0.0;
float temperature = 0.0;

void setup() {
  delay(10000);

  Serial.begin(115200);

  Serial.println("ESP8266 DHT HTTP POST");
 
  dht.setup(2, DHTesp::DHT11);
  delay(dht.getMinimumSamplingPeriod());

  Serial.println(dht.getStatusString());
  humidity    = dht.getHumidity();
  temperature = dht.getTemperature();
  Serial.print("Temperature : ");
  Serial.println(temperature);
  Serial.print("Humidity : ");
  Serial.println(humidity);

  WiFi.begin(STASSID, STAPSK);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.print("Connected! IP address: ");
  Serial.println(WiFi.localIP());
}

void loop() {
  // wait for WiFi connection
  if ((WiFi.status() == WL_CONNECTED)) {
    WiFiClient client;
    HTTPClient http;

    Serial.print("[HTTP] begin...\n");
    // configure traged server and url
    http.begin(client,
"http://" SERVER_IP "/measurement"); //HTTP http.addHeader("Content-Type", "application/x-www-form-urlencoded"
    );

    Serial.print("[HTTP] POST...\n");
    // start connection and send HTTP header and body
    String post("id=");
    post += DEVICE_ID;
    post += "&t=";
    post += temperature;
    post += "&h=";
    post += humidity;
    int httpCode = http.POST(post);

    // httpCode will be negative on error
    if (httpCode > 0) {
      Serial.printf("[HTTP] POST... code: %d\n", \
           httpCode);

      // file found at server
      if (httpCode == HTTP_CODE_OK) {
        const String& payload = http.getString();
        Serial.println("received payload:\n<<");
        Serial.println(payload);
        Serial.println(">>");
      }
    } else {
      Serial.printf( \
           "[HTTP] POST... failed, error: %s\n", \
           http.errorToString(httpCode).c_str());
    }

    http.end();
  }

  delay(10000);
}

 
El código bastante parecido está en github en la carpeta esp8266-arduino. En esp8266 dejé la fallada con esp-idf.
 
Nos quedó asi:
 
~/esp/
     /esp-idf
     /esp-idf-lib
     /ESP8266_RTOS_SDK
     /arduino-1.8.19
     /ceiot/
           /esp32
           /esp32c3
           /esp32s2
           /esp8266
           /esp8266-arduino 




Ejemplo de ESP8266 con lectura de DHT11

Hace ya un tiempo exploré como lidiar con el entorno de desarrollo de ESP8266. Mientras incorporaba esa arquitectura a un proyecto educativo que consiste en leer un sensor de temperatura DHT11 y transmitirlo por HTTP-POST, me encontré con que las instrucciones que tenía quizás ya no son vigentes.

El proceso es similar a cómo se hace con ESP32 y acá lo mostraré adaptado. 

 

No tomes este camino

Si no tenés curiosidad por mis desgracias, andá directo al Plan B más abajo.

Si lo tomás, tené en cuenta que te rompe la instalación de esp-idf.

 

Plan A

La estructura de archivos que es esta:

~/esp/
     /esp-idf
     /esp-idf-lib
     /ESP8266_RTOS_SDK
     /ceiot/
           /esp32
           /esp32c3
           /esp32s2
           /esp8266

Resaltado lo que vamos a agregar.

esp-idf es la SDK para ESP32, esp-idf-lib son componentes de un tal RusLand.

Vamos a suponer que al haber instalado previamente todo lo necesario para esp-idf, si siguieras los siguientes pasos en una máquina limpia quizás no alcanzaría.


$ git clone https://github.com/espressif/ESP8266_RTOS_SDK.git
$ cd ESP8266_RTOS_SDK.git
$ git checkout origin/release/v3.4
$ sudo apt install python3-pip python-setuptools libncurses5-dev
$ /usr/bin/python3 -m pip install --user -r requirements.txt
$ ./install.sh
$ . export.sh



Mirando por encima requirements.txt, que es lo que consume install.sh apreciarás que hay poca superposición.

 
export.sh te genera un entorno para que funcionen los make posteriores y se debe ejecutar en cada terminal que abrás. Si te falla por que no encuentra python, la primera tentación es ponerse a modificar las referencias a python por python2 o python3 en mi caso, primero en export.sh y luego en otras partes. Mala idea, lo mejor, recordando que tengo armada una virtual sólo para esto, es:
 

sudo ln -s /usr/bin/python3 /usr/bin/python

El problema de hacer esto es que te rompe luego lo que tengas para ESP32.

Así que tenés que estar alternando con:

sudo rm /usr/bin/python

Como no pienso hacer mucho es ESP8266, alternaré. Si luego se me ocurre algo mejor o alguien lo tiene, me lo pasa y lo pongo.
 
Si prestaste atención a cómo se hace en ESP32, habrás notado que acá no existe el paso para determinar que chip usas:
 
idf.py set-target esp8266

Esto es porque hay múltiples ESP32, como ESP32, ESP32c3, ESPs2 para mencionar algunos y sólo hay un ESP8266.

Listo para el build. Pero antes, incorporemos el proyecto, lo que hay uno tiene la tentación de hacer es (no hacer):
 
 
mkdir esp8266
cd esp32
cp CMakeLists.txt -r main/ Makefile ../esp8266
cd ../esp8266
 
 
Pero no, equivocado, lo que hay que hacer es:
 
cp -r ../ESP8266_RTOS_SDK/examples/wifi/getting_started/station .
mv station esp8266
# modificar el programa tal como con ESP32
 
 
Para la configuración de los componente, en este caso credenciales de WiFi:
 
make menuconfig

 
No te olvides de borrarlas al versionar .

Finalmente, para que funcione la parte del DHT11, hay que tocar el Makefile:

EXTRA_COMPONENT_DIRS += $(IDF_PATH)/../esp-idf-lib/components


Y ahora si

make 
make flash
make monitor


No tan fácil...

Habían varios errores simultáneos que me confundieron. Algunos relacionados con la inclusión de archivos, otros quizás relativos a los branches de cada repositorio, en una conversación en embebidos quedó registrado parte de esto, no aporta que lo repita acá.
 
Lo más significativo de esa conversación es que si hubiera leido el manual quizás hubiera detectado que hay componentes no soportados de esp-idf-lib en ESP8266 y que hay que excluirlos del proceso de compilación.

La receta resultante es, por ahora:
 
Usar los tags v3.4 y v0.8.2 (esp-idf y esp-idf-lib respectivamente)
 
En el Makefile, para que incluya esp-idf-lib:
 
EXTRA_COMPONENT_DIRS += $(IDF_PATH)/../esp-idf-lib/components
 
para que excluya los componentes no soportados:

EXCLUDE_COMPONENTS := max7219 mcp23x17 led_strip max31865 ls7366r hts221
 
 
Aún así, aunque compila no funciona:
 
 
...a ver...


Reconectando...
Reconectando...


 
Mmmh, fijate que intenta conectar a como dé lugar, habría que agregar un mensajito de log tal como he resaltado:

static void on_wifi_disconnect(void *arg, 
                        esp_event_base_t event_base,
                        int32_t event_id,
                        void *event_data){
  system_event_sta_disconnected_t *event =
     (system_event_sta_disconnected_t *)event_data;
 
  ESP_LOGI(TAG, 
            "Wi-Fi disconnected reason %d\n\r",
            event->reason);   

  ESP_LOGI(TAG,"Wi-Fi disconnected, trying to reconnect...");
  if (event->reason == WIFI_REASON_BASIC_RATE_NOT_SUPPORT){
      /*Switch to 802.11 bgn mode */

      esp_wifi_set_protocol(
         ESP_IF_WIFI_STA,
         WIFI_PROTOCOL_11B | 
         WIFI_PROTOCOL_11G |
         WIFI_PROTOCOL_11N);
  }
  ESP_ERROR_CHECK(esp_wifi_connect());
}


Si querés ver que hace esp_wifi_connect, no existe, me parece que está implementado en ROM o similar en el chip.

Ahora dice:

phy_version: 1163.0, 665d56c, Jun 24 2020, 10:00:08, RTOS new
I (415) phy_init: phy ver: 1163_0
I (426) example_connect: Connecting to thebox...
I (3217) wifi:state: 0 -> 2 (b0)
I (4217) wifi:state: 2 -> 0 (200)
I (4218) example_connect: Wi-Fi disconnected reason 2

I (4220) example_connect: Wi-Fi disconnected, trying to reconnect...
I (6286) example_connect: Wi-Fi disconnected reason 205




Las event-reasons están en components/esp8266/include/esp_wifi_types.h, son un montón y se llaman wifi_err_reason_t.

WIFI_REASON_AUTH_EXPIRE       = 2
WIFI_REASON_CONNECTION_FAIL   = 205 

 
¿Qué si probé con reiniciar el router? ¡Claro! antes de usar linux usaba windows...

Probé con
 
make menuconfig -> 
   Component config  --->
    Wi-Fi  --->
[*] Enable WiFi debug log (ESP8266_WIFI_DEBUG_LOG_ENABLE)
 
 
Pero no hallé como acceder o interactuar con ese log.
 

 
Me pasé al ejemplo más básico, station, para reducir variables, probé todo lo que sigue:
 
  • ESP8266_PHY_MAX_WIFI_TX_POWER, ya está en 20, pasé a 21Cambié de canal en el AP, del 11 al 1 y al 6 
  • Puse modo B only
  • Puse modo G only
  • Puse modo N only
  • AES en lugar de TKIP + AES
  • a 30 centímetros del AP
  • Branches v3.3 y v3.3-rc1
  • Cambié de router
  • Probé con 20 y 40 MHz de ancho

 
No encuentro más recursos, sólo esta pista:

WiFi.hostname("ESP-host"); before WiFi.begin(ssid, password);

Pero eso es con ArduinoIDE.

Me quedaría armar un Access Point para diagnosticar desde ese lado o buscar como hacer sniffing, quedará para otra vez.



2022/03/03

Por qué IoT y sistemas embebidos si trabajás en un banco

...o cualquier trabajo que aparentemente no tenga sistemas embebidos ni nada que ver con IoT.

 

Buena pregunta, un banco es el terreno del mainframe rodeado de bases de datos y servidores diversos que atienden vía web en el home banking, en las aplicaciones mobile, por terminales diversas a empleados, en ATM, en encoladores, dispensadores de tarjetas y ahí ya te estoy contestando un poco, pues todos estos últimos vienen a ser el terreno de los SSEE.

Que un sistema esté implementado con "commodity hardware", off-the-shelf cómo le dicen, es una cuestión completamente táctica, no afecta su esencia.

Tenemos a grandes rasgos que si una computadora lo que más hace es operaciones específicas atendiendo quizás multitud de orígenes, le decimos servidor. Puede tener pantalla y teclado, es irrelevante. Puede ser una raspberry-pi, su función es servidor. Si tenemos un montón dependiendo de como estén organizadas, son cloud, farms, supercomputadoras.

Tenemos que si tiene KVM (Keyboard Video Mouse - Teclado Video Mouse) y es de uso general, ya sea en formato laptop o desktop, le decimos "Estación de Trabajo", la compu, una PC.

Si en lugar de KVM tiene una pantalla táctil y un par de botones, funciona con batería, entra en el bolsillo y tiene wifi y pegada una línea de teléfono somos lo suficientemente smart para denominarlo "mobile".

Una y otra vez, no dejan de ser computadoras.

Cuando tenemos una computadora que tiene un teclado loco o ni tiene, que anda en batería encima de un poste o bajo tierra, que maneja a una máquina, que es tan chica que apenas le entra un hello world, que la mayor parte del tiempo se la pasa durmiendo, se despierta unos milisegundos, mide un sensor, lo transmite y vuelve a dormir, que desde que se prende ejecuta el mismo conjunto de programas en lugar de ir ejecutar programas arbitrarios, que en realidad son varias distintas haciendo lo mismo y comparando que estén haciendo realmente lo mismo, que es tán... lo que sea, estamos en el terreno de los SSEE.

Si no tiene nada salvo un montón de conexiones de red, como un switch, un router, un firewall, balanceador de carga, es equipamiento de comunicaciones, SE.

Entonces, una PC común dentro de un ATM, un sistema de dispensador de turnos o de reconocimiento facial, no es una PC, es un SE. Para contrastar, una raspberry-pi a la cual le conectás KVM y no usas los GPIO (los cablecitos para leer botones y prender leds) para mi deja de ser un SE y pasa a ser una PC.

No es lo mismo sin embargo un SE que nace como tal y se usa como SE que usar una PC con el mismo propósito. Esto es en parte por el hardware, que en la PC es de uso general, debe funcionar lo mejor posible en situaciones muy distintas. Contrastando, los SSEE acostumbran usar hardware especializado para lo que van a hacer, pueden estar optimizados para bajo consumo, tiempo real, mínima latencia, infernal paralelismo, ínfimo precio, obviamente no todos a la vez, es como la regla del PM: rápido, barato, bueno, elegí sólo dos.

En el motor de un auto debe ir inevitablemente un SE, pues una PC no soporta las vibraciones, temperaturas y ruido eléctrico ni los requerimientos de tiempo real de que la chispa ocurra en ese preciso instante.

En un equipo médico de soporte vital debe ir un SE, pues no puede colgarse ni ponerse a hacer otra cosa que la que está haciendo.

En un ATM, será más lenta la PC o consumirá más que el SE, pero no hay actividades críticas a nivel de hardware. En realidad si hay pero a nivel de cada dispositivo, el lector de la autenticación, el lector de cheques, el expendedor, que  tienen su propio SE, orquestados por la PC. Es por eso que en un ATM se puede usar una PC, por que las actividades especiales no las lleva adelante la PC, si no estos SSEE específicos.

No es nada novedoso, incluso dentro de la PC hay SSEE, como la controladora de disco rígido, ya he lidiado con eso al intentar rescatar un microcontrolador 8051 de un disco. El teclado tiene un microcontrolador, es un SE.

Siguiendo por el resto del banco, de modo directo tenemos esos SSEE y de modo indirecto, de soporte tenemos cámaras IP, sistemas de alarmas, control de acceso, la controladora de los ascensores si los hay, aire acondicionado.

Si por algún motivo se pudiera saltar de la red de las cámaras IP o cualquier otro sistema de soporte a las otras redes, entonces la seguridad de esas redes nos interesa tanto como la propia del "negocio".

Este salto puede ser por compartir la red aunque esté segregada de modo lógico (buscá VLAN), puede haber un componente con una patita en cada red, pueden tener un almacenamiento o servidor común en Cloud.

 

Falta IoT

 

IoT parece ser un montón de dispositivos (evidentemente SSEE) comunicándose entre ellos o con servidores, recolectando información y actuando. Por ejemplo ese coso que se llama Alexa que le decís "quiero escuchar música de ascensor" y te pasa esa música por el SmartTV, o que controles algún automatismo de tu casa desde el SmartPhone o que si bajó el nivel de humedad de las macetas y no hay pronóstico de lluvia cercano, se abra una válvula y haya riego.

La clave que diferencia o mejor dicho lo que extiende SSEE a IoT es la comunicación. Un lavarropas por mucho microcontrolador que tenga, si no tiene conexión a algo, no es IoT. Y claramente la comunicación no es un mero capricho de la gente de marketing, es para poder controlar el lavaropas remotamente y/o poder obtener telemetría.

Un componente con un protagonismo no del todo evidente en todos esos ejemplos es que buena parte de la inteligencia no está en los dispositivos, está en algún servidor o conjunto, ya sea en Cloud o en un servidor local. Y lo interesante es que la complejidad y extensión de estos servidores es probablemente siempre superior a la de los dispositivos. Y quizás fundamentalmente, los datos y el control están en esos servidores.

En el caso de Alexa, el dispositivo probablemente sea un microcontrolador con un parlante, un micrófono, una conexión wifi y otra bluetooth. El código seguramente sea un loop que va comprimiendo y transfiriendo el audio a Cloud, donde es reconocido y se generan los comandos que luego se envían al SmartTV, sistema un poco más complicado pero no por el aspecto IoT, la parte IoT es sólo que ejecute una acción que normalmente se hace manualmente. De hecho, si se usara siempre con Alexa, se podría simplificar, ser menos Smart.

 

Visión IoT vs SSEE
Visión IoT vs SSEE

 

Este gráfico es una simplificación extrema, las formas tan pronunciadas son para no entrar en discusiones de detalles de cuál sería la verdadera forma, si más redonditas. Pensá que SSEE en principio no tiene nada que ver con los servidores de la izquierda, pero para comprender bien el funcionamiento de estos tenés que saber de arquitectura de computadoras y eso claramente no es terreno de IoT, si de SSEE, pues una vez que comprendiste un ARM o un RISC-V estás a centímetros de comprender un x86, si es que los servidores no son ARM.

Entonces, el eje del conocimiento de IoT termina siendo en realidad el conocimiento de la infraestructura de soporte, condimentado con los casos de Edge Processing, donde la inteligencia vuelve al dispostivo, esto es por ejemplo cuando se transmite un video sólo cuando se detecta un evento complejo como el reconocimiento de que hay una cara. Se envía al servidor la foto y este reconoce la identidad de la cara. Reconocer que hay una cara no es tarea menor y ahorra un montón de ancho de banda.

O un vehículo autónomo, a dónde va a ir se lo dice el sistema central, quizás más o menos por dónde, pero el cómo, el evitar chocar lo va a resolver por si mismo.

Otra característica que mueve el eje es cuando hay condiciones "especiales", para llamarlas de alguna manera, esto es, por ejemplo cuando hay distancias apreciables y la fuente de alimentación es restringida, lo cual te obliga a usar protocolos de bajo consumo, ya no es entonces uso WiFi cero cerebro, hay que estudiar un poquito mejor el asunto.

Y a lo que se está yendo, pero me parece que aún no es tan importante, es la coordinación autónoma entre los dispositivos. Por ahora es más sencillo que el sensor de humedad le diga al servidor que la maceta está seca y éste le diga a la válvula de riego que se abra. Es que ni el sensor ni la válvula saben cual es el pronóstico para las próximas horas. Y un auto claramente puede negociar mucho mejor el cruce en una calle sin semáforo comunicándose con los autos cercanos, pero no puede anticipar una congestión un kilómetro más adelante así.

Volviendo a un banco, tenemos que aunque un poco distintos, con respecto a las estaciones de trabajo, notebooks y SmartPhones al menos, se tiene un problema similar al de IoT respecto al inventario, tracking, actualización de imagen en el caso de IoT más básico, del sistema operativo y aplicaciones en no IoT, autenticación de los dispositivos.

Respecto a los ATM el aspecto de desatendido y que se protejan de ataques físicos, no sólo físicos físicos, para eso está el blindaje, sino para que no se les haga escupir billetes conectándole un cable en el expendedor.

Asi que aunque en mi caso lo que prima es mi interés natural por SSEE e IoT, a cualquier persona técnica lo que aprenda de arquitecturas y seguridad le servirá de modo directo pues no dejemos de ver que los ATM son los que entregan la plata y aunque parezcan PCs adornadas, son SSEE. El resto forma parte de la superficie de ataque y en todo caso servirá de modo indirecto pues se aprende mucho al cruzar campos de conocimiento.

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.