24 KiB
weight | title | date | draft | summary | categories | tags | ||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
4 | Apuntes de Domótica | 2020-11-03T18:01:27+0100 | false | Notas sobre Domótica |
|
|
Apuntes no terminados sobre Domótica Libre
{{< admonition type=warning title="Work in progress" open=true >}} Estos apuntes no están completos, (ni de lejos) {{< /admonition >}}
Grupo de investigación
- Fundado el 2020-11-02, tenemos grupo de telegram y BBB.
- Cryptpad del grupo de domótica
Hardware para empezar
- Wemos: este es el micro procesador que vamos a usar para el sensor. Se puede programar con el IDE Arduino. Este es el ESP8285, equivale a un ESP8266 con 1Mb de flash.
- Sensor de temperatura/humedad
- RTC y tarjeta SD Como este
- Pantalla OLED Como esta compatible con la mecánica Wemos
- Módulo de gestión de batería Como este.Para alimentar el sensor con una batería de 3,3 volt
Ver la tabla de componentes aquí
MQTT
Message Queuing Telemetry Transport, es un protocolo ligero (bajo consumo de memoria y cpu) diseñado especificamente para que dispositivos de bajo consumo transmitan datos de banda estrecha (con velocidades muy bajas). El protocolo funciona sobre TCP/IP (otro protocolo, "el de internet"). Es un protocolo viejecito (1999), lo que pasa es que se ha puesto de moda por que encaja muy bien con todo el tema de IoT (Internet of Things)
Conceptos
- Broker
-
Es un middleware (un intermediario) entre los publishers (agentes que envían datos) y receivers (agentes que reciben los datos)
- Publisher
-
Un agente que envía datos al Broker, puede ser un dispositivo sensor, un demonio monitorizando un servidor, una raspi, etc. etc.
- Subscriber
-
Es el receptor de los datos enviados por el Publisher
- Topic
-
Los Publisher y Subscriber no se relacionan directamente. Cuando un publisher envía datos al broker los asocia a un topic. El topic es una cadena en forma de jerarquia (p.ej. /sensor/cocina/temperatura), el carácter '/' actúa como separador de nivel, cada campo separado por '/' representa un nivel. Los puedes organizar como quieras. Cuando un subscriber se conecta al broker especifica los topic en los que está interesado. El broker le hará llegar todos los mensajes de esos topic.
Tenemos dos operadores o máscaras para especificar las subscripciones:
#
y+
.Supongamos que tenemos los siguientes topic:
/sensor/cocina/temperatura
/sensor/cocina/humedad
/sensor/cocina/humo
/sensor/salon/temperatura
/sensor/salon/humedad
/sensor/salon/humo
Con la especificación
/sensor/+/temperatura
me suscribo a todos los sensores de temperatura.+
encaja con un nivel simple.Con la especificación
/sensor/#
me suscribo a todos los sensores.#
encaja con múltiples niveles.Si organizamos los niveles de los topic con lógica, podremos gestionar fácilmente las suscripciones.
- message
-
Los datos que son enviados y recibidos
Versiones del protocolo
- V3.1.1
- V5.0
Brokers MQTT
Hay muchos brokers, esta lista es muy parcial pero todos los que están tienen licencia libre.
- Mosquitto: Mosquitto es un broker sencillo y que aparece en un montón de tutoriales disponibles en internet. Está incluido en los repos de Debian y Ubuntu (seguramente no muy actualizado) y hay ppa disponible. La principal pega de Mosquitto es que no soporta "clustering" (varios brokers funcionando en paralelo) que es la forma natural de escalar la carga en el servido y dar redundancia. Es práctico para desarrollar aplicaciones pero puede dar problemas si lo quieres usar en un entorno de producción.
- RabittMQ: Es un broker multiprotocolo así que no solo soporta MQTT. El soporte para distintos protocolos de colas de mensajes se implementa mediante plugins. Soporta clustering así que es escalable para aguantar cargas grandes de peticiones y se puede implementar redundancia. El soporte de MQTT no es completo, no tiene soporte para MQTT V5.0 ni para todas las carácteristicas de MQTT V3.1.1 (por ejemplo no soporta QoS2)
- VerneMQ: Es un broker MQTT puro (no es multiprotocolo) Soporta clustering y todas las versiones de MQTT, aparentemente con todas sus funcionalidades.
Pruebas con Mosquitto
Vamos a hacer pruebas con el broker más sencillo: Mosquitto
En Ubuntu y derivadas es muy fácil de instalar, añadimos el origen de
paquetes oficiales para estar a la última, instalamos el paquete
mosquitto
y el paquete mosquitto-clients
, los clientes nos vendrán
bien para hacer pruebas. En otras distribuciones tendrás que mirar
como se instala, pero no puede ser muy complicado.
sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppa
sudo apt-get update
sudo apt install mosquitto mosquitto-clients
En Ubuntu y derivadas queda lanzado el servicio y podemos hacer pruebas con los clientes instalados. Si después queremos instalar el Mosquitto de otra manera (con docker por ejemplo) hay que acordarse de parar el servicio.
Probando
En un terminal con el comando mosquitto_sub -h localhost -t test
lanzamos un subscriber que atiende al topic test.
Desde otro terminal con el comando mosquitto_pub -h localhost -t test -m "Hola Mundo"
ejecutamos un publisher que manda el mensaje "Hola
Mundo" al topic test.
En la primera ventana podemos ver:
$ mosquitto_sub -h localhost -t test
Hola mundo
Se ha recibido el mensaje en el topic suscrito.
Probando con python
Vamos a ver como usar el mqtt desde Python. Así queda mucho más claro como funciona el broker
Creamos un entorno virtual y lo activamos (yo lo he llamado paho
),
en ese entorno instalamos la librería: pip install paho-mqtt
Creamos un programa subscriber:
import paho.mqtt.client as mqtt
import time
# la rutina a ejecutar cuando recibimos un mensaje
def on_message(client, userdata, message):
print("received message: ", str(message.payload.decode("utf-8")))
# la rutina a ejecutar al recibir CONNACK
def on_connect(client, b, c, rt):
print("Connected with result " + str(rt))
# hacemos la suscripcion dentro de la rutina on_connect
# de esta forma si tenemos reconexiones ya nos aseguramos
# la suscripcion
client.subscribe("ALEATORIOS")
# dirección del broker
mqttBroker = "localhost"
# creamos un "cliente"
client = mqtt.Client("subs-1")
client.on_connect = on_connect
client.on_message = on_message
# conexión al broker
client.connect(mqttBroker)
# lanzamos un loop no bloqueante
client.loop_start()
# Escuchamos durante 30 segundos y paramos el programa.
time.sleep(30)
client.loop_stop()
Y este sería el programa publisher
import paho.mqtt.client as mqtt
from random import randrange, uniform
import time
# definimos la rutina a ejecutar cuando recibimos CONNACK
def on_connect(client, userdata, flags, rc):
print("Connected with result " + str(rc))
# definimos la dirección del broker
mqttBroker = "localhost"
# creamos un "cliente"
client = mqtt.Client("publisher-1")
client.on_connect = on_connect
# conexión al broker
client.connect(mqttBroker)
# Bucle infinito, publicamos un número aleatorio y esperamos 1 sg
while True:
randNumber = uniform(0, 10)
client.publish("ALEATORIOS", randNumber)
print("Publicado " + str(randNumber) + " en el topic ALEATORIOS")
time.sleep(1)
Podemos ver los programas en acción ejecutando cada uno en su terminal (terminal=ventana de comandos) ¡Recuerda activar el entorno virtual en todos los terminales que abras!
¿Qué está pasando aquí?
Cada vez que desde un cliente le pedimos algo al broker este nos responde con una señal, pero ¡ojo! el protocolo es asíncrono, así que mas que una conversación entre cliente y servidor tenemos un intercambio de correos.
Por ejemplo, cuando nosotros le mandamos la petición de conexión al broker debería llegarnos un CONNACK desde el mismo. En el CONNACK el broker nos adjunta tres campos: user data, flags y result code. El campo más interesante probablemente sea el result code, estos son los valores definidos en el protocolo:
rc | Significado |
---|---|
0 | Connection successful |
1 | Connection refused – incorrect protocol version |
2 | Connection refused – invalid client identifier |
3 | Connection refused – server unavailable |
4 | Connection refused – bad username or password |
5 | Connection refused – not authorised |
6-255 | Currently unused. |
La biblioteca paho-mqtt nos permite definir rutinas _callback que la librería invocará cuando se produzcan ciertos eventos.
Un par de ejemplos: un evento de conexión reconocida (se ha recibido el CONNACK) hará que se ejecute el callback on_connect, el evento "recibir un mensaje" hará que paho intente ejecutar el callback on_message. Esta tabla cubre los principales eventos:
Evento | Callback |
---|---|
Conexión reconocida | on_connect |
Desconexión reconocida | on_disconnect |
Suscripción reconocida | on_subscribe |
De-suscripción reconocida | on_unsubscribe |
Publicación reconocida | on_publish |
Mensaje recibido | on_message |
Info de log disponible | on_log |
Los ejemplos que puse son muy sencillos, si queremos escribir un código más robusto hay que tener en cuenta todo lo que el protocolo ofrece y que es un protocolo asíncrono. Un par de ejemplos de mejoras
- La función
on_connect
debería comprobar el result code y no dejar continuar el programa sin tener una conexión verificada. - La función
on_message
seguramente tendrá que procesar el topic del mensaje y puede que también la qos
influxdb
- https://discourse.nodered.org/t/mqtt-node-red-influxdb/34706
- https://diyi0t.com/visualize-mqtt-data-with-influxdb-and-grafana/
- https://dev.to/project42/install-grafana-influxdb-telegraf-using-docker-compose-56e9
- https://lazyadmin.nl/it/installing-grafana-influxdb-and-telegraf-using-docker/
Influxdb es una base de datos especializada en series temporales de datos. Eso hace que sea mucho más eficiente que una base de datos relacional tratando este tipo de datos. Aunque internamente cambia bastante el funcionamiento el lenguaje de consultas no es muy diferente de SQL. Otra ventaja relacionada con nuestro escenario es que se integra muy bien con Grafana.
Hasta ahora influxdb viene con varias aplicaciones asociadas:
- Telegraf
-
un agente de recolección de datos, para capturar datos de stacks y otros sistemas
- Cronograf
-
Un visualizador de datos al estilo de Graphana
- Kapacitor
-
un motor de procesado de datos en tiempo real especializado en series temporales
Pero está disponible la versión InfluxDB OSS 2.0 que integra todas las herramientas en un único programa. Por lo que veo la instalación es por binarios únicamente y además no es compatible con Raspberry. Parece interesante pero por ahora lo dejamos.
Si instalamos el Influxdb clásico nos quedamos con la versión 1.8.3 (en este momento). Una vez instalada y arrancado el servicio lo podemos probar preguntando por las bases de datos disponibles (solo está la internal)
$ influx
Connected to http://localhost:8086 version 1.8.3
InfluxDB shell version: 1.8.3
> show databases
name: databases
name
----
_internal
>
Que no os despiste el interfaz http, no es human friendly ni funciona con navegadores (en esta versión de influxdb). Si que podemos interrogarlo con curl por ejemplo y nos responderá en perfecto json:
$ curl -G http://localhost:8086/query --data-urlencode "q=SHOW DATABASES"
{"results":[{"statement_id":0,"series":[{"name":"databases","columns":["name"],"values":[["_internal"]]}]}]}
Por lo demás trabajar con InfluxDB no es tan diferente de trabajar con una base de datos relacional de toda la vida.
- InfluxDB está optimizada para trabajar con series de datos temporales, es más rápida (con este tipo de datos) que las bases de datos relacionales clásicas
- Cambia un poco la terminología:
- La base de datos clásica era una colección de tablas, la de Influx puede verse como una colección de "medidas" (measurements)
- Las "medidas" se parecen a las tablas de las bases de datos
relacionales, pero:
- Siempre tienen que tener una referencia temporal (timestamp, basicamente nanosegundos desde el Linux Epoch)
- Se distingue entre campos (fields) y etiquetas (tags), equivalen a los campos (columnas) de las tablas de las bases de datos relacionales, pero: las etiquetas están indexadas y los campos no.
- los registros, las filas de las tablas, se llaman puntos
- Se pueden ajustar las políticas de retención de datos, es decir, cuanto tiempo se mantienen en la base de datos. Depende de para que quieras usar los datos, a lo mejor quieres un histórico de una hora o de cinco años. Ajustar bien las políticas de retención influye en el rendimiento de la base de datos.
Instalación de InfluxDB
Dependerá del sistema operativo, por ejemplo para Linux Mint:
wget -qO- https://repos.influxdata.com/influxdb.key | sudo apt-key add -
#source /etc/lsb-release
DISTRIB_ID=ubuntu
DISTRIB_CODENAME=focal
echo "deb https://repos.influxdata.com/${DISTRIB_ID} ${DISTRIB_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/influxdb.list
apt update
apt install influxdb
apt install telegraf
Una vez instalado verifica que el servicio está arrancado.
El cliente de la base de datos se llama influx si ejecutamos el
cliente podemos preguntar por las bases de datos presentes con: show databases
(solo muestra la base de datos interna)
Una sesión completa
- Creamos la base de datos
- La ponemos como base de datos activa
- Insertamos datos
influx -precision rfc3339
Connected to http://localhost:8086 version 1.8.3
InfluxDB shell version: 1.8.3
> show databases
name: databases
name
----
_internal
>create database mydb
> show databases
name: databases
name
----
_internal
mydb
> use mydb
Using database mydb
> insert temperature,location=campo external=13,internal=23
> insert temperature,location=ciudad external=19, internal=22
Las lineas de los insert siguen el esquema:
<measurement>[,<tag-key>=<tag-value>...] <field-key>=<field-value>[,<field2-key>=<field2-value>...] [unix-nano-timestamp]
El primer grupo son los tags que están indexados. En nuestro
ejemplo el tag sería location
. El segundo grupo son los fields
(no indexados), en nuestro ejemplo tendríamos dos external
e
internal
. Opcionalmente podemos escribir el timestamp o dejar que
influx lo ponga automáticamente (pondrá el instante en que se
realiza la inserción)
Un ejemplo con python
Lo primero es instalar la biblioteca de python así que en nuestro
entorno virtual ejecutamos pip install influxdb
Un programa python chorras para ejecutar las inserciones del ejemplo anterior:
from influxdb import InfluxDBClient
client = InfluxDBClient(host=localhost, port=8086)
client.switch_database('mydb')
client.write_points("temperature,location='countryside' external=13,internal=23", protocol='line')
client.write_points("temperature,location='city' external=19,internal=22", protocol='line')
Si después de ejecutar el programa vamos al cliente influx:
> use mydb
Using database mydb
> show measurements
name
----
temperature
> select * from temperature
name: temperature
time external internal location
---- -------- -------- --------
2020-11-27T15:23:20.523830387Z 13 23 countryside
2020-11-27T15:23:33.459690565Z 19 22 city
Análisis y Visualización de datos
- Metabase: Visualización de gráficos sencilla y potente.
- Grafana: Plataforma de visualización y análisis de datos muy completa.
node-red
Instalación en Linux
Para instalar node-red en linux necesitamos instalar primero
node.js
. Hay varias formas de instalar node.js
, yo voy a optar por
instalar nvm
que es el node version manager.
Para ello ejecutamos el siguiente comando (la versión actual de nvm
es la 0.37.0)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.37.0/install.sh | bash
El script de instalación añade las siguientes lineas al fichero
~/.bashrc
, nosotros las eliminamos de ~/.bashrc
y las movemos al
fichero ~/.profile
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
Para comprobar la instalación usamos command -v nvm
que nos
devolverá nvm
. which
no funciona en este caso por que es un script
pensado para invocarlo con source
Instalación de node.js
Ahora que tenemos nvm
instalado, ya podemos instalar fácilmente la versión o versiones que queramos de node.js
nvm ls-remote # para listar las versiones disponibles
nvm install node # instala la última versión disponible
nvm install --lts # instala la versión LTS
Instalación de node-red
Una vez instalado el node.js
instalar el node-red
es muy fácil:
activar el node.js
que hemos instalado y hacer la instalación de
node-red
nvm use node
npm install -g --unsafe-perm node-red
Instalación del IDE Arduino en Linux para trabajar con Wemos D1
Descargamos el IDE de la pagina web. Para proceder a la instalación se puede consultar la guía de postinstalación de Linux Mint que detalla todos los pasos.
Solo necesitamos habilitar el soporte para ESP8266, añadiendo la linea siguiente a nuestras preferencias de Arduino:
http://arduino.esp8266.com/stable/package_esp8266com_index.json
Configuración de mini-PC como servidor
Describimos la instalación en un mini-pc Beelink T34 (con 8 gb de RAM y 256Gb de disco duro SSD). No es obligatorio hacerlo como describimos, esto es solo para referencia.
Nos hemos decidido por instalar Debian en el mini-pc. Hay varias
formas de instalar Debian, pero como nosotros queremos un Debian sin
interfaz gráfico y con lo mínimo, creemos que lo más fácil es usar el
netinst
.
Descargamos la imagen iso de netinst
desde la página de
Debian (la
hemos descargado via torrent)
Desde linux hemos usado Etcher (es multiplataforma) para grabar la image iso descargada en una memoria USB.
Conectamos nuestro mini-PC a un teclado usb y a un monitor (hemos usado un coversor HDMI-VGA) y conectamos la memoria USB en algúno de los puertos libres, también le hemos dado internet con un cable ethernet a nuestro router.
Al arrancar el mini-PC veremos un aviso: pulsando F7 nos deja elegir el dispositivo de arranque. Elegimos arrancar desde la memoria USB e iniciamos la instalación de Debian, con particionado manual y sin LVM.
Hemos creado tres particiones:
Particion | Tamaño | Tipo | Punto de montaje | Comentarios |
---|---|---|---|---|
/dev/sda1 | 511Mb | EFI | /boot/efi | La crea el programa de instalación |
/dev/sda2 | 55Gb | ext4 | / | root |
/dev/sda4 | 171Gb | ext4 | /store | Dejamos casi todo el espacio disponible en esta partición |
El programa de instalación nos ha pedido drivers adicionales pero lo hemos ignorado.
Hemos escogido idioma inglés con teclado español. También hemos
escogido locales es_ES-UTF-8
Solo hemos instalado el openssh server y las herramientas básicas. Tendremos que instalar muchas más cosas pero eso nos deja un sistema lo más limpio posible.
Tras completar la instalación el sistema no arranca normalmente se
queda pillado en el prompt de grub. Tras teclear exit
en el
prompt arranca normalmente.
Se soluciona ejecutando como root grub-install
y ya no vuelve a fallar.
Una vez arrancado el sistema podemos conectarnos via ssh.
Referencias
Wiki de Bricolabs
Referencias MQTT
- Buenos apuntes para aprender de MQTT
- Para empezar con python y MQTT (muy completo)
- Enviar datos desde ESP via mqtt
- Visualizar datos (cont. del ant.)
Referencias Influxdb
- Getting started
- Getting started with Influxdb OSS 2.0
- Guia de Influxdb
- Instrucciones de instalación para la versión clásica y la nueva 2.0 en ubuntu y debian
- otro getting started
- Python e Influxdb OSS 2.0
- Docker compose: Grafana/Influx/Telegraf
- idem
- idem, tiene dos partes
- Acumulados y caducidad: Continous query and retention policy
Home assistant seguros
- https://makezine.com/2020/03/17/private-by-design-free-and-private-voice-assistants/
- https://makezine.com/2020/08/03/set-up-your-own-private-smart-home/
- https://makezine.com/projects/mycroft-the-open-source-private-voice-assistant-on-raspberry-pi/>
- Magpi vol 99, pag 54 http://magpi.cc
Ejemplos del escenario básico que queremos implementar
- Visualizar datos con mqtt-influxdb-grafana (bridge-python)
- Una configuración con Telegraf
- nilhcem (web)