tech explorers, welcome!

Etiqueta: electronics

Carcasa + pantalla OLED con panel de estado para Raspberry Pi

Estaba tentado de intercambiar la carcasa de mi Raspberry Pi 4 a la Pi 5, pero mírala…

  • 16×2 pantalla LCD de un Arduino Starter Kit
  • Programa en C++ con la librería WiringPi
  • Carcasa casera

Estaba realmente orgulloso de ella en su momento y fue un buen aprendizaje allá por 2020, pero seguro que lo podía hacer mejor. Seguro que me podía currar un pedazo panel de estado!!

Componentes

  • Raspberry Pi (probablemente sirva en cualquiera de ellas)
  • Pantalla OLED (RGB de 1.5 pulgadas es ideal)
  • PCB placa de prototipos
  • Cables
  • Material de soldadura
(cortesía del modelo epiCRealism en ComfyUI)
  • Carcasa (impresa en 3D o elaborada a mano)

Configuración de software

Configuraré la Raspberry para interactuar con la pantalla usando python, mediante la librería de Adafruit Circuitpython para el controlador SSD1351 de la pantalla:
https://learn.adafruit.com/adafruit-1-5-color-oled-breakout-board/python-wiring-and-setup#setup-3042417

Es tan simple como instalar Circuitpython con el siguiente comando:

sudo pip3 install --upgrade click setuptools adafruit-python-shell build adafruit-circuitpython-rgb-display

Si tienes problemas con tu versión de python para encontrar Circuitpython, incluye --break-system-packages al final. (No romperá nada hoy, pero no te acostumbres a ello...)

sudo pip3 install --upgrade click setuptools adafruit-python-shell build adafruit-circuitpython-rgb-display --break-system-packages

Cableado

Ahora conectaremos la pantalla según las indicaciones del fabricante. La mía es esta de BuyDisplay:

https://www.buydisplay.com/full-color-1-5-inch-arduino-raspberry-pi-oled-display-module-128x128

OLED DisplayRaspberry Pi (pin #)
GNDGND (20)
VCC3V3 (17)
SCLSPI0 SCLK (23)
SDASPI0 MOSI (19)
RESGPIO25 (22)
DCGPIO 24 (18)
CSSPI0 CE0 (24)

Usa un sitio como pinout.xyz para encontrar una configuración adecuada para tu cableado.

Estás listo para hacer algunos tests antes de mover tu diseño a la PCB.

Configuración de script

Puedes probar el script demo de Adafruit. Solo asegúrate de escoger la pantalla adecuada y actualiza los cambios en la asignación de pines (usa los números/nombres IO en lugar de los números físicos del pin):

# Configuration for CS and DC pins (adjust to your wiring):
cs_pin = digitalio.DigitalInOut(board.CE0)
dc_pin = digitalio.DigitalInOut(board.D24)
reset_pin = digitalio.DigitalInOut(board.D25)

# Setup SPI bus using hardware SPI:
spi = board.SPI()

disp = ssd1351.SSD1351(spi, rotation=270,                         # 1.5" SSD1351
    cs=cs_pin,
    dc=dc_pin,
    rst=reset_pin,
    baudrate=BAUDRATE
)

Ensamblaje

Aquí están los archivos STL en 3D para este diseño de carcasa:

Ahora pongámoslo todo junto:

  1. Atornilla el marco a la pantalla
  2. Suelda los 7 pines de la pantalla a los 7 cables a través de la PCB
  3. Cablea todas las conexiones a la Raspberry Pi
  4. Atornilla las partes superiores e inferiores de la carcasa entre sí
  5. Coloca la pantalla sobre el soporte

Resultado final

He compartido el script que se ve en las imágenes a través de github:

https://github.com/TheRoam/RaspberryPi-SSD1351-OLED

Actualmente muestra:

  • Fecha y hora
  • Estadísticas del sistema (SO, uso del disco y temperatura de CPU)
  • Tiempo local de la World Meteorological Organization (actualizado cada hora)

Y así es como acaba quedando. Mucho mejor verdad?

Como siempre, disfruta cacharreando, y hazme llegar tus comentarios o dudas a través de Twitter!

🐦 @RoamingWorkshop

UNIHIKER-PAL: asistente del hogar simplificado, de código abierto en python

PAL es una versión simplificada de mi asistente del hogar en python, que se ejecuta en una placa UNIHIKER de DFRobot, y que lanzo en código abierto gratuito.

Esta es solo una pequeña demostración de la simplicidad de la ejecución de comandos con reconocimiento de voz en python, y con suerte servirá de guía para tu propio asistente.

Version actual: v0.2.0 (actualizado septiembre 2024)

Características

La versión actual incluye lo siguiente:

  • Reconocimiento de voz: usando la librería de código abierto SpeechRecognition, se obtiene una lista con las cadenas de texto reconocidas.
  • Pronóstico del tiempo: usando la API de datos de World Meteorological Organization, devuelve el tiempo de hoy y el pronóstico de los próximos 3 días. Incluye los iconos de la WMO.
  • Temperatura local: reads local BMP-280 temperature sensor to provide a room temperature indicator.
  • Comandos HTTP para IoT: flujo de trabajo básico para controlar dispositivos inteligentes de IoT mediante comandos HTTP. Actualmente ENCIENDE y APAGA un interruptor inteligente Shelly2.5.
  • Modo ahorro de energía: controla el brillo para reducir el consumo de energía.
  • Gestor de conexión: periódicamente comprueba el wifi y llama a internet para restaurar la conexión cuando se pierda.
  • Muestra de voces de PAL: voz clonada de PAL de "Los Mitchells contra las Máquinas" usando el modelo de IA de voz CoquiAI-TTS v2.
  • Botones UNIHIKER: botón A despliega un simple menú (está pensado para desarrollar un menú más complejo en el futuro).
  • Controles táctiles: restaura el brillo (centro), cambia de programa (izquierda) y cierra el programa (derecha) pulsando en distintas zonas de la pantalla.

Instalación

  1. Instala dependencias:
    pip install SpeechRecognition pyyaml
  2. Descarga el repositorio de github:
    https://github.com/TheRoam/UNIHIKER-PAL
  3. Sube los archivos y carpetas a la UNIHIKER en /root/upload/PAL/
  4. Configura en PAL_config.yaml las credenciales WIFI, dispositivos IoT, etc.
  5. Lanza el script de python python /root/upload/PAL/PAL_020.py desde la terminal de Mind+ o desde la interfaz táctil de UNIHIKER.

Si habilitas Auto boot en el menú de Service Toggle, el script se ejecutará automáticamente cada vez que se reinicie la UNIHIKER

https://www.unihiker.com/wiki/faq#Error:%20python3:%20can't%20open%20file…

Configuración

Version 0.2.0 includes configuration using a yaml file that is read when the program starts.

CREDENTIALS:
ssid: "WIFI_SSID"
pwd: "WIFI_PASSWORD"

DEVICES:
light1:
brand: "Shelly25"
ip: "192.168.1.44"
channel: 0

light2:
brand: "Shelly25"
ip: "192.168.1.44"
channel: 1

light3:
brand: "Shelly1"
ip: "192.168.1.42"
channel: 0

PAL:
power_save_mode: 0
temperature_sensor: 0
wmo_city_id: "195"

Localización

La variable "CityID" se usa para proporcionar un pronóstico del tiempo de WMO más cercano a tu localización.

Elige una de las localizaciones disponibles en su listado oficial:

https://worldweather.wmo.int/en/json/full_city_list.txt

Dispositivos IoT

En este momento, PAL v0.2.0 solo incluye compatibilidad con Shelly2.5 para fines demostrativos.

Usa las variables lampBrand, lampChannel y lampIP para ajustar tu configuración de Shelly2.5.

Esto es solo un ejemplo para mostrar cómo se pueden configurar distintos dispositivos. Estas variables se deberían usar para cambiar las peculiaridades de los comandos HTTP que se envían a cada dispositivo IoT.

Se añadirán más dispositivos en futuras actualizaciones, como Shelly1, ShellyDimmer, Sonoff D1, etc.

Modo ahorro de energía

El ahorro de energía disminuye el brillo del dispositivo para reducir el consumo de energía de la UNIHIKER. Esto se hace usando el comando del sistema "brightness".

Cambia la variable "ps_mode" para habilitar ("1") o deshabilitar ("0") el modo ahorro de energía.

Temperatura de habitación

Cambia la variable "room_temp" para habilitar ("1") o deshabilitar ("0") el módulo de lectura de sensor de temperatura. Esto requiere un sensor BMP-280 instalado mediante el conector I2C.

Comprueba este otro post para los detalles de la instalación del sensor:

https://theroamingworkshop.cloud/b/en/2490/

Otras configuraciones desde el código fuente:

Tema

Se habilita la personalización de la imagen de fondo que representa los ojos de PAL.

Usa las variables "eyesA" y "eyesB" para especificar uno de los siguientes valores y cambia la expresión del PAL:

  • "happy"
  • "angry"
  • "surprised"
  • "sad"

"eyesA" se usa como fondo por defecto y "eyesB" se usa como transición cuando el reconocimiento de voz se activa y PAL habla.

El valor por defecto de "eyesA" es "surprised" y cambiará a "happy" cuando se reconoce un comando.

Comandos personalizados

Añadir tus propios comandos a PAL es simple usando la función "comandos".

Cada audio reconocido por SpeechRecognition se envía como una cadena a la función "comandos", que filtra y lanza uno u otro comando coincidente con la orden.

Solo define todas las posibles cadenas que pueden ser reconozidas para lanzar tu comando (a veces SpeechRecognition devuelve transcripciones erróneas o imprecisas).

Finalmente, define el comando que se lanza cuando la cadena coincide.

def comandos(msg):
    # LAMP ON
    if any(keyword in msg for keyword in ["turn on the lamp", "turn the lights on","turn the light on", "turn on the light", "turn on the lights"]):
        turnLAMP("on")
        os.system("aplay '/root/upload/PAL/mp3/Turn_ON_lights.wav'")

Palabra clave para la activación

Puedes personalizar las palabras clave o cadenas que activan las funciones de comandos. Si cualquiera de las palabras clave en la lista es reconocida, toda la frase se envía a la función "comandos" para buscar un comando coincidente y ejecutarlo.

En caso de PAL v0.2, estas son las palabras clave que lo activan (el 90% de las veces es Paypal...):

activate=[
    "hey pal",
    "hey PAL",
    "pal",
    "pall",
    "Pall",
    "hey Pall",
    "Paul",
    "hey Paul",
    "pol",
    "Pol",
    "hey Pol",
    "poll",
    "pause",
    "paypal",
    "PayPal",
    "hey paypal",
    "hey PayPal"
]

Puedes cambiar esto a cualquier otra frase o nombre para activar a PAL cuando la llames de esa manera.

Voz de PAL

Usa la muestra de audio "PAL_full" abajo (también en el repositorio de github en /mp3) como audio de referencia para la clonación de voz de CoquiAI-TTS v2 y genera tus propias voces:

https://huggingface.co/spaces/coqui/xtts

TIP!
Comprueba este otro post sobre clonación de voz con CoquiAI-XTTS:
https://theroamingworkshop.cloud/b/2429

Demo

A continuación hay algunos ejemplos de consultas y respuestas de PAL:

"Hey PAL, turn on the lights!"
"Hey PAL, turn the lights off"
"Hey PAL, what's the weather like?"

Futuros lanzamientos (To-Do list)

Iré desarrollando estas características en mi asistente personal, e iré actualizando la versión de código abierto de vez en cuando. Ponte en contacto via github si tienes especial interés en alguna de ellas:

  • Menú avanzado: añadir configuración y comandos manuales.
  • Dispositivos IoT: incluir todos los comandos HTTP de Shelly y Sonoff.
  • Consultar la hora: requiere clonar la voz de todas las combinaciones de números...
  • Consulta de Wikipedia/internet: requiere generar voz en tiempo real...
  • Mejoradas animaciones / temas.

Comentarios, problemas o mejoras, me encantará recibirlos en github o Twitter!

🐦 @RoamingWorkshop

🌡UNIHIKER sensor de temperatura en tiempo real funcionando en 2 minutos

Sigo experimentando con la placa UNIHIKER de DFRobot y es increíblemente rápido hacer que las cosas funcionen en ella. Hoy traigo cómo configurar en 2 minutos la visualización en tiempo real de un sensor de temperatura, usando un módulo BMP-280 y absolutamente nada de programación.

Prerrequisitos

Aquí está el truco, y es que esperaba que tuvieras un par de cosas configuradas antes de activar el cronómetro:

  • Descarga e instala Mind+, el IDE para UNIHIKER de DFRobot.
    En Linux se trata de fichero .deb que se toma su tiempo para instalar:
    https://mindplus.cc/download-en.html
  • Suelda un modulo de presión y temperatura BMP-280 y conéctalo a un cable I2C que vienen con la placa. Tendrás que doblar ligeramente algunos pines para que encajen en el conector que parece un nano JST de 1mm.

Ya estás listo!

Configuración

  1. En Mind+, ve al Blocks editor y abre el menú Extensions.
  2. En la pestaña pinpong selecciona el propio módulo pinpong (que habilita la interacción con los pines de la UNIHIKER) y el módulo de extensión BMP-280, que añade la interacción con estos módulos.
  1. Vuelve al Blocks editor y comienza a construir tu bloque de código. Simplemente navega por el menú lateral izquierdo y arrastra todo lo que necesitas debajo del bloque Python program start:
    • pinpong - initialize board.
    • bmp280 - initialize I2C address 0x76 para inicializar el módulo.
    • control - bloque forever (introduce un bucle while True).
    • unihiker - añade objetos a la pantalla. Primero añado un objeto filled rectangle para limpiar texto anterior, luego añado el objeto text. Especifica coordenadas X,Y donde se mostrará cada objeto, al igual que su color.
    • bmp280 - objeto read temperature. Arrástralo dentro del campo de texto del objeto text.
    • python - (opcional) añade un objeto print para mostrar los datos en terminal. Yo he incluido todas las otras propiedades que devuelve el sensor.
    • control - añade un objeto wait para detener el programa durante 1 segundo antes del siguiente bucle.

      Debería quedar todo así (haz click para ampliar)

Ejecución

Y ya está todo el programa montado, sin programar! Pulsa RUN en la parte superior de Mind+ y observa cómo se carga y se muestra por la pantalla de la UNIHIKER. Toca el sensor ligeramente con el dedo para ver cómo varían los valores con el incremento de temperatura.

Han sido sólo 2 minutos no?

🐦 @RoamingWorkshop

Shelly 1: interruptor/programador wifi, configuración de API HTTP local y ensamblaje en caja de enchufe.

Hoy os traigo mi segunda oportunidad que le voy a dar a Shelly. Y es que mi primer Shelly Dimmer petó por exceso de temperatura, al tenerlo dentro de una caja de conexiones, pero el Shelly 2.5 que controla dos luces está aguantando también insertado en la pared.

Quizás la diferencia son los 5ºC más que soportan, así que hoy voy a montar un enchufe de pared con interruptor/programador wifi añadiéndole un diminuto Shelly 1, y esperemos que aguante.

Además de su pequeño tamaño, los Shelly son muy fáciles de configurar, así que también veremos cómo controlarlos localmente mediante su API HTTP.

Requisitos

Mi objetivo es habilitar un enchufe de pared que pueda controlar y programar mediante WIFI, en mi caso, para controlar el calentador de agua eléctrico. Veamos qué voy a usar:

  • Shelly 1.
  • Cable eléctrico de dos hilos (fase y neutro).
  • Clavija de enchufe macho.
  • Clavija de enchufe hembra.
  • Material para ensamblar una carcasa (impresora 3D, madera, etc).

Conexión eléctrica

Veamos el manual de Shelly a ver cómo nos indica que realicemos las conexiones:

https://www.shelly.com/documents/user_guide/shelly_1_multi_language.pdf

La idea de este esquema estándar es que conectes el Shelly 1 a una luz y a su interruptor, significando cada símbolo lo siguiente:

  • L: línea o fase
  • N: neutro
  • SW: switch (interruptor)
  • I: input o entrada
  • O: output o salida

Como quiero habilitar una clavija de enchufe, el esquema variará ligeramente, ya que no usaré el interruptor y puedo conectar la línea directamente a la entrada. Por otro lado, y por motivos que desconozco, no tengo cableado en esta caja de conexiones, así que traigo la línea eléctrica mediante un cable que va a otro enchufe... En fin, queda todo así:

TIP! Juraría que me he rayado confundiendo los colores, pero en este caso el circuito propio es cerrado así que no importa demasiado si no eres maniático, ya que el circuito está conectado correctamente.

Ensamblaje

Como verás, me he hecho un pequeño soporte en 3D para guiar las conexiones y también una tapa para cubrir el hueco del enchufe. Además, simular las piezas en 3D, con medidas reales , te ayudará a distribuir bien el espacio y asegurarte de que tu solución se acopla correctamente:

Te dejo los modelos 3D en .stl para mandar a imprimir en software slicer.

Shelly-Plug_support_v1.stl

https://theroamingworkshop.cloud/demos/Shelly-Plug_support_v1.stl

Shelly-Plug_tapa_v1.stl

https://theroamingworkshop.cloud/demos/Shelly-Plug_tapa_v1.stl

Al final, queda todo así montado. No es el ajuste perfecto, pero me vale para cumplir su función.

Conexión a internet

Ahora veamos como traer a la vida al Shelly 1 y manejarlo localmente.

Al contrario que los Sonoff, Shelly nos lo pone mucho más fácil y solo hay que seguir su manual.

  1. Enchufa el Shelly 1 a la corriente mediante el cable con clavija macho.
  2. Se activará un AP (Access Point) o red Wi-Fi con una SSID del estilo "shelly1-01A3B4". Conéctate a esta red Wi-Fi mediante un smartphone o PC.
  3. Cuando te conectes, usa un navegador web para acceder a la IP 192.168.33.1 y accederás a la interfaz web de configuración del dispositivo.
  1. Una vez dentro, deberás configurar el dispositivo (en el menú Internet & Security) para que se conecte automáticamente a tu red Wi-Fi, así como también es recomendable que bloquees el acceso al dispositivo con usuario y contraseña, por lo que pudiera ocurrir...

Con esto ya podemos comunicarnos localmente con nuestro Shelly 1.

Uso de Shelly HTTP API

Para usar los comandos de la API HTTP deberás conocer la IP del dispositivo en la red local.

Averiguar IP mediante router

Puedes acceder al mapa de red de tu router, normalmente mediante la dirección http://192.168.1.1

Tanto la dirección como la contraseña de acceso deberían aparecer en una pegatina en tu router. Luego verás tu dispositivo con algún nombre tipo shelly1-XXXXXXXXXXXX:

Averiguar IP mediante nmap

Mediante un terminal, puedes usar el programa nmap para escanear tu red local.

  • Descárgalo si no lo tienes:
    sudo apt-get update
    sudo apt-get install nmap
  • Escanea tu red (con sudo aportará también la dirección MAC, útil por que la IP podría llegar a cambiar al reiniciar tu router)
    sudo nmap -v -sn 192.168.1.0/24

Enviar solicitudes HTTP al dispositivo

La API HTTP de Shelly viene documentada en su web dedicada:

https://shelly-api-docs.shelly.cloud/gen1/#common-http-api

Para poder comunicarte con el dispositivo necesitas enviar solicitudes HTTP mediante algún programa tipo Postman o mediante curl o wget en un terminal.

La solicitud se hará a la IP del dispositivo con:

$ curl -X GET http://192.168.1.XX/command

Si has definido usuario y contraseña, deberás ingresarlo también en la URL de esta manera o devolverá una respuesta "401 unauthorized":

$ curl -X GET http://user:[email protected]/command

Ahora veamos algunos casos concretos:

Información del dispositivo

http://[user]:[pass]@[ip]/status

  • curl

curl -X GET 'http://user:[email protected]/status

  • Respuesta
{"wifi_sta":{"connected":true,"ssid":"MYWIFINETWORK","ip":"192.168.1.XX","rssi":-70},"cloud":{"enabled":false,"connected":false},"mqtt":{"connected":false},"time":"19:30","unixtime":1699295403,"serial":1,"has_update":false,"mac":"A4CF12F407B1","cfg_changed_cnt":0,"actions_stats":{"skipped":0},"relays":[{"ison":false,"has_timer":false,"timer_started":0,"timer_duration":0,"timer_remaining":0,"source":"input"}],"meters":[{"power":0.00,"is_valid":true}],"inputs":[{"input":0,"event":"","event_cnt":0}],"ext_sensors":{},"ext_temperature":{},"ext_humidity":{},"update":{"status":"idle","has_update":false,"new_version":"20230913-112003/v1.14.0-gcb84623","old_version":"20230913-112003/v1.14.0-gcb84623"},"ram_total":51688,"ram_free":39164,"fs_size":233681,"fs_free":146333,"uptime":2679}

Encender/apagar (on/off)

http://[usr]:[pass]@[ip]/relay/0?turn=[on/off]

  • curl

curl -X GET http://user:[email protected]/relay/0?turn=on

  • Respuesta
{"ison":true,"has_timer":false,"timer_started":0,"timer_duration":0,"timer_remaining":0,"source":"http"}

El valor de 0 en la URL corresponde con el número del relay o interruptor interno del Shelly. En este caso solo hay uno, pero en caso del Shelly 2.5 tienes dos relays, por lo que puedes manejarlos individualmente cambiando este número.

Programador

http://[usr]:[pass]@[ip]/settings/relay/0?schedule_rules=[HHMM]-[0123456]-[on/off]

  • curl

curl -X GET http://user:[email protected]/settings/relay/0?schedule_rules=1945-0123456-on

  • Respuesta
{"name":"CALENTADOR","appliance_type":"General","ison":false,"has_timer":false,"default_state":"off","btn_type":"toggle","btn_reverse":0,"auto_on":0.00,"auto_off":0.00,"power":0.00,"schedule":true,"schedule_rules":["1945-0123456-on"]}

En este caso, en la URL se definen los siguientes parámetros de las reglas:

  • HHMM: hora y minuto que activan la regla
  • 0123456: los días de la semana en los que se produce la regla
  • on/off: estado que se produce al activarse la regla

De esta forma, para programar el encendido y apagado del dispositivo (excepto fines de semana), tendrías una solicitud de este estilo:

curl -X GET http://192.168.1.XX/settings/relay/0?schedule_rules=2300-01234-on,0700-01234-off

Obviamente, también podrás configurar las reglas de programación desde la interfaz web, o comprobar que los comandos han funcionado:

Y con eso estaría todo. Lánzate a llenar tu casa de discretos Shellys totalmente personalizables. Cualquier duda o comentario, al Twitter 🐦, gracias! (aunque visto lo de X, no sé yo lo que me durará...)

🐦 @RoamingWorkshop

Sonoff D1 Dimmer: configuración de API HTTP local (modo DIY) y ensamblaje con alargador para conexión exterior.

Tenía un Shelly Dimmer incrustado en la caja del enchufe en la pared, pero un buen día dejó de funcionar (probablemente por las altas temperaturas, soporta hasta 35ºC). Buscando un repuesto, vi que Sonoff había sacado su equivalente y por 1/3 del precio del Shelly.

Al final, lo barato sale caro, y es que resulta bastante más complicado de configurar que el Shelly Dimmer y tiene un mayor tamaño.

Después de muchas pruebas, y debido a la mala documentación, aquí explico cómo configurar el Sonoff D1 Dimmer para usar su API local y no depender de la aplicación e-weLink.

Además, por su tamaño, no encontrarás mucho sitio en tus cajas de conexiones para incrustar el Sonoff Dimmer, así que te doy la idea de montar un alargador para conectarlo de manera externa.

Requisitos

  • Sonoff D1 Dimmer.
  • Cable eléctrico de dos hilos (fase y neutro).
  • Clavija de enchufe macho.
  • Clavija de enchufe hembra.
  • Mando Sonoff RM-433 (muy recomendado).
  • Material para ensamblar una carcasa (impresora 3D, madera, etc).

Conexión eléctrica

Lo primero que necesitarás es conectar el D1 a la red de 220V de tu casa, según el esquema de montaje que nos facilitan en su manual:

https://sonoff.tech/wp-content/uploads/2021/03/%E8%AF%B4%E6%98%8E%E4%B9%A6-D1-V-1.1-20210305.pdf

El esquema de conexión anterior sigue más o menos la norma Europea:

  • Fase (positivo): negro, marrón o gris (en este caso rojo...)
  • Neutro (negativo): azul.

También te digo que el Shelly Dimmer es mucho más compacto y cabe sin problemas en las cajas que normalmente se usan para alojar los enchufes de pared. En este caso no, así que yo lo voy a conectar de forma externa mediante un cable alargador, y luego detallaré una sencilla carcasa para su ensamblaje.

TIP! Si no tienes mucha experiencia con la electricidad, más vale que investigues un poco y tomes todas las precauciones. No es agradable pegarse un calambrazo con un cable de la red doméstica. Si realizas la conexión de manera externa, como en este caso, no corres tanto peligro.

Por ahora con esto ya podemos hacerlo funcionar.

Conexión a internet

Ahora viene lo complicado, y es que en tanta carcasa se ve que no cabía un simple botón para las habituales tareas de encender/apagar/restaurar.

Si tienes suerte, tu Sonoff no vendrá preconfigurado y podrás conectarte a él de primeras. Si viene preconfigurado, posiblemente porque se haya comprobado su funcionamiento en otra red, el dispositivo ya no es detectable ni si quiera mediante la app e-weLink, a menos que estes en la red donde se configuró.

Para reconocerlo, se deben restaurar los valores de fábrica y para ello tenemos dos opciones:

  • Restaurarlo mediante app e-weLink desde la red donde se configuró (muy poco probable que tengas acceso a ella).
  • Restaurarlo mediante el mando Sonoff RM-433 (al final acabarás teniendo que comprarte este accesorio extra).

Emparejar mando Sonoff RM-433

Al final, el humilde precio del D1 se ha duplicado por la necesidad de comprar el mando RM-433, pero bueno, su precio tampoco es descabellado. Aquí su manual:

https://sonoff.tech/wp-content/uploads/2021/03/%E8%AF%B4%E6%98%8E%E4%B9%A6-RM433-V1.1-20210305.pdf

Lo primero que debemos hacer es emparejar el mando con el D1:

  1. Conectamos el D1 a un enchufe.
  2. Mantenemos el botón 7 pulsado durante unos 5 segundos, hasta que se oiga un pitido (esto elimina la asignación de radiofrecuencia anterior).
  3. Desenchufamos y enchufamos el D1 para reiniciarlo.
  4. Pulsamos cualquier botón del mando para que se asigne al D1.
  5. Se oirá un pitido y el mando queda emparejado, pudiendo manejarse el D1 con él.

Restaurar red WIFI

Ahora necesitamos restaurar también la red asignada al D1.

Para ello, mantenemos pulsado el botón 8 durante unos 5 segundos, o más bien, hasta que veas que empieza a parpadear de la siguiente manera:

Modo "respiración" o breath. Dos pulsos rápidos, uno lento.

Has eliminado la red anterior. Ahora hay que ponerlo en modo emparejamiento.

Volvemos a pulsar el botón 8 durante unos 5 segundos, o más bien, hasta que veas que empieza a parpadear constantemente:

Modo emparejamiento. Pulsos constantes.

De esta forma el dispositivo inicia un Punto de Acceso WIFI (WIFI AP) que tendrá un nombre en la forma ITEAD-XXXXXXXXXX.

Emparejar con e-weLink

Ahora, si quieres el camino fácil, deberás descargarte la aplicación e-weLink y pulsando el botón emparejamiento rápido tendrás tu D1 disponible para usar mediante esta app.

Emparejar en modo DIY

Pero ya sabrás que a mí me gusta complicarme y quiero habilitar el modo DIY (Do It Yourself) para poder acceder a la red del dispositivo y manejarlo mediante los comando de la API HTTP desde mi propia web app.

Para esto, buscamos la red ITEAD-XXXXXXXXXX creada por el dispositivo y nos conectamos introduciendo la contraseña 12345678.

Ahora abrimos un navegador web y accedemos a la dirección http://10.10.7.1 donde veremos las siguientes pantallas.

Aquí introduciremos el nombre (SSID) y la contraseña de nuestra red WIFI, y el dispositivo quedará ya vinculado a ella.

Ensamblaje

Antes de entrar en el detalle del manejo de la API HTTP, te dejo un diseño de carcasa impresa en 3D para evitar que se queden los cables y las conexiones al aire.

Cuenta con dos piezas de PLA (base y tapa) para poder atornillarlas entre sí y las puedes descargar desde mi servidor:

https://theroamingworkshop.cloud/demos/D1case_v1_base.stl

https://theroamingworkshop.cloud/demos/D1case_v1_top.stl

Y aquí puedes previsualizarla:

Uso de D1 HTTP API

Para usar los comandos de la API HTTP deberás conocer la IP del dispositivo en la red local.

Averiguar IP mediante router

Puedes acceder al mapa de red de tu router, normalmente mediante la dirección http://192.168.1.1

Tanto la dirección como la contraseña de acceso deberían aparecer en una pegatina en tu router. Luego verás tu dispositivo con algún nombre tipo ESP-XXXX derivado del dispositivo WIFI que porta (aquí yo ya lo he renombrado):

Averiguar IP mediante nmap

Mediante un terminal, puedes usar el programa nmap para escanear tu red local.

  • Descárgalo si no lo tienes:
    sudo apt-get update
    sudo apt-get install nmap
  • Escanea tu red (con sudo aportará también la dirección MAC, útil por que la IP podría llegar a cambiar al reiniciar tu router)
    sudo nmap -v -sn 192.168.1.0/24

Enviar solicitudes HTTP al dispositivo

La API HTTP del Sonoff D1 viene documentada en su web:

https://sonoff.tech/sonoff-diy-developer-documentation-d1-http-api/

Para poder comunicarte con el dispositivo necesitas enviar solicitudes HTTP mediante algún programa tipo Postman o mediante curl o wget en un terminal.

La solicitud se hará a la IP del dispositivo, en el puerto por defecto 8081, y además deberemos incluir la id del dispositivo en el cuerpo o body de la solicitud (la id coincide con el código XXXXXXXXXX que tenía la red WIFI ITEAD-XXXXXXXXXX).

Veamos algunos casos usando curl y Postman.

Información del dispositivo

http://[ip]:[port]/zeroconf/info

  • curl

curl -X POST 'http://192.168.1.34:8081/zeroconf/info' --data-raw '{"deviceid": "XXXXXXXXXX","data": {}}'

  • Postman
  • Respuesta
{
    "seq": 6,
    "error": 0,
    "data": {
        "deviceid": "XXXXXXXXXX",
        "switch": "off",
        "startup": "off",
        "brightness": 60,
        "brightMin": 0,
        "brightMax": 100,
        "mode": 0,
        "otaUnlock": false,
        "fwVersion": "3.5.0",
        "ssid": "TU_RED_WIFI",
        "bssid": "XX:XX:XX:XX:XX:XX",
        "signalStrength": -58
    }
}

Encender/apagar (on/off)

http://[ip]:[port]/zeroconf/switch

  • curl

curl -X POST 'http://192.168.1.34:8081/zeroconf/switch' --data-raw '{"deviceid": "XXXXXXXXXX","data": {"switch":"on"}}'

  • Postman
  • Respuesta
{
    "seq": 9,
    "error": 0
}

Ajuste del brillo

http://[ip]:[port]/zeroconf/dimmable

  • curl

curl -X POST 'http://192.168.1.34:8081/zeroconf/dimmable' --data-raw '{"deviceid": "XXXXXXXXXX","data": {"switch":"on","brightness":50,"mode":0,"brightmin":0,"brightmax":100}}'

  • Postman
  • Respuesta
{
    "seq": 14,
    "error": 0
}

Y con esto ya puedes programar tu propia aplicación y controlar tu D1 a tu gusto de forma totalmente privada. Espero que te resulte útil, pero si te surgen dudas o comentarios, no dudes dejarlos en Twitter 🐦!

🐦 @RoamingWorkshop

Temps-i 7: reloj de sobremesa casero con WIFI, sensor de temperatura-presión y autonomía +48h.

Después de muchas idas y venidas, pruebas, rediseños, quemaduras, cortes y alguna que otra pequeña explosión, por fin traigo un del reloj de mesa inteligente en el que llevo dos años trabajando. Hacer un tutorial completo y detallado sería todavía más largo y tedioso, pero espero que estas pinceladas sean de ayuda para hacer el tuyo propio. Aviso de que se requiere mucho tiempo y práctica y no se puede hacer a la ligera…

Qué es Temps-i 7?

Desgranemos su nombre:

  • Temps es tiempo en valenciano,
  • i de internet, que es de donde obtiene la hora,
  • 7 de la pantalla que utiliza, de 7 segmentos para cada dígito.

Estos tres conceptos definen a este compacto reloj de sobremesa, con conectividad WIFI, sensor de temperatura y buena autonomía.

Vamos a ver cómo se hace!

Componentes

Primero, veamos los ingredientes de la receta y qué hace cada uno:

  • Placa ESP32-Thing de SparkFun
    >>Microcontrolador de altas prestaciones con conectividad WIFI y Bluetooth gracias al chip ESP32 integrado, ideal para proyectos de IoT.
  • Pantalla de 4 dígitos de 7 segmentos de color rojo.
    >>Mostrará la hora y la temperatura.
  • Módulo BMP-280
    >>Sensor digital de temperatura y presión barométrica muy compacto.
  • Resistencias de 100 Ohm.
    >>Necesarias para reducir la corriente en la pantalla, sin disminuir excesivamente su brillo (admite hasta 1k Ohm, pero así sería difícilmente visible con la luz del día).
  • Placa PCB con circuito impreso personalizado.
    >>Simplifica la conexión de la pantalla y las resistencias al microcontrolador.
  • Batería LiPo 3.7v 1000mAh
    >>Asegura una autonomía de más de 48 horas sin alimentación externa.
  • Cables puente.
    >>Para realizar conexiones adicionales con los módulos externos.
  • Pulsador
    >>Usado para alternar el programa mostrado en pantalla.

Diseño electrónico

Es muy importante estudiar los componentes electrónicos que vamos a utilizar, leer todas las especificaciones y prototipar con todas las precauciones antes de ponernos a soldar como locos.

Elección de los componentes

Los componentes arriba listados no son mera casualidad ni copia de ninguna otra parte. Simplemente son la prueba más exitosa de muchas otras y que cumplen todas las necesidades del proyecto:

  • La pantalla de 4 dígitos muestra exclusivamente lo que busco ver: la hora. Si además puedo aprovechar para mostrar la temperatura, pues mejor. Pero una pantalla de más calidad, de tipo LCD, generaría un mayor consumo realmente innecesario ya que otra de las características fundamentales es la autonomía.
  • El microcontrolador cuenta con conexión a internet, así como suficiente capacidad de cómputo. Además, tiene suficientes pines entrada/salida para manejar la pantalla sin necesidad de un expansor de pines. Otras opciones que he probado:
    • Microcontroladores más compactos: Teensy 4, Digispark, SparkFun Pro Micro. Necesitan expansor de entrada/salida (PCF8574) y/o módulo WIFI (ESP-01). Además, requiere demasiadas conexiones.
    • Microcontralor con conexión WIFI integrada y suficientes E/S, como la NodeMCU con Esp8266. Faltaba potencia en el procesador y se retrasaba casi 4 segundos en cada minuto.

Prototipado del circuito eléctrico

Una vez estudiados y obtenidos los componentes, los conectamos en una placa de prototipos (o protoboard) para probar su funcionamiento.

En mi caso, tras varias combinaciones de pines, la forma más organizada me ha resultado ser la siguiente:

ESP32-ThingComponente
VBATLiPo +
3V3BMP-280 3V3
GNDLiPo -
BMP GND
BMP SD0
Pulsador -
GPIO21BMP SDA
GPIO04BMP SCL
GPIO32Pulsador +
GPIO17Pantalla Dígito 1
GPIO23Pantalla Dígito 2
GPIO19Pantalla Dígito 3
GPIO25Pantalla Dígito 4
GPIO15Pantalla Segmento A
GPIO22Pantalla Segmento B
GPIO27Pantalla Segmento C
GPIO12Pantalla Segmento D
GPIO13Pantalla Segmento E
GPIO18Pantalla Segmento F
GPIO26Pantalla Segmento G
GPIO14Pantalla Segmento P (punto)

Esquema de patillaje

Una vez tengamos el prototipo montado, es bueno pasarlo a un esquema con un programa de EDA (automatización de diseño electrónico) como Kicad, donde también podremos generar un diseño de PCB para mandar a fabricar. Si no, siempre puedes hacer el esquema en papel, ya que dentro de dos meses no vas a recordar dónde iba cada cable...

Kicad tiene su truquillo y es bueno practicar antes con proyectos más sencillos. Aun así, es asequible para usuarios medios, ya que básicamente consiste en buscar y elegir símbolos de nuestros componentes y conectar sus pines correctamente según sus especificaciones.

Yo, para no liar la cosa de cables en el papel, he usado nombres en cada conexión, que también es válido en Kicad. Además, verás que la ESP32-Thing está compuesta por dos tiras de 20x pines, ya que no encontré ningún símbolo que me funcionara y no tenía tiempo de diseñarlo como toca. Lo importante es que nuestro diseño funcione y sea coherente con la realidad.

Esquema de Kicad

Lo siguiente es asignar huellas realistas (o footprints) a cada símbolo de componente, y entonces podremos diseñar una placa de circuito impreso (o PCB) que podemos enviar a fabricar (normalmente a China) por unos 15€ / 5 placas.

No hay que volverse muy locos con esto, especialmente si no somos expertos. Yo solo lo uso para facilitar las soldaduras cuando hay muchas conexiones, como es este caso donde se realizan unas 90 soldaduras pero se mantiene un formato bastante compacto.

Programación del reloj

La mayoría de microcontroladores, como el ESP32-Thing, son compatibles con el IDE de Arduino, lo cual facilitará la conexión con la placa para enviarle un programa de reloj.

Antes de empezar, es importante hacer un pequeño listado de las tareas y funciones que queremos incluir en el programa e irlo modificando mientras las programamos. De esta forma podremos probar las distintas funciones por separado, y debuguear los distintos pasos para corregir errores más rápidamente. En mi caso, de nuevo tras muchas pruebas, quiero que el programa conste de lo siguiente:

  1. Definición de librerías y variables.
  2. Configuración de pines.
  3. Conexión WiFi.
  4. Obtener fecha por SNTP.
  5. Desconexión y apagado de WiFi (ahorra batería).
  6. Conversión de fecha a dígitos.
  7. Muestra de dígitos en pantalla.
  8. Iniciar temporizador.
  9. Iniciar lectura de pin de cambio de programa.
  10. Cambio de programa al activar el pulsador.
    1. Lectura de sensores.
    2. Muestra de temperatura en pantalla.
  11. Actualizar hora al finalizar el temporizador (cada minuto).
  12. Reiniciar temporizador.

No me quiero entretener mucho con el código, y encima no es el más ordenado que tengo, pero lo dejo aquí incrustado para quien lo quiera copiar, o también en github:

https://github.com/TheRoam/Tempsi-7

//----------------------------------------------------------------//
//    Temps-i 7 WiFi clock and temperature in 4 digit display     //
//  v 1.0.1                                                       //
//                                                                //
//  Interfaces:                                                   //
//  - Sparkfun ESP32-Thing micocontroller                         //
//  - BMP-280 temperature and pressure digital sensor             //
//  - 4 digit 7 segment display                                   //
//  - Programm push button                                        //
//  - LiPo Battery                                                //
//                                                                //
//  Detailed documentation:                                       //
//  https://theroamingworkshop.cloud                              //
//                                                                //
//                © THE ROAMING WORKSHOP 2022                     //
//----------------------------------------------------------------//
#include < esp_wifi.h >
#include < WiFi.h >
#include < WiFiMulti.h >
#include "time.h"
#include "sntp.h"
#include < Wire.h >
#include < Adafruit_BMP280.h >
// Spaces inside <> only shown for correct html display. Remove them in Arduino IDE
//BMP280 sensor using I2C interface
Adafruit_BMP280 bmp;
#define BMP_SCK  (4)
#define BMP_MISO (21)
#define BMP_MOSI (4)
#define BMP_CS   (21)
//Sensor variables
float TEMP=0;     //temperature variable
float ALT=0;      //altitude variable
float PRES=0;     //pressure variable
float hREF=1020.0;//sea level reference pressure in hPa
//define time variables
RTC_DATA_ATTR long long TIME=0; //concatenated time
RTC_DATA_ATTR long long d=0;  //day
RTC_DATA_ATTR long long m=0;  //month
RTC_DATA_ATTR long long Y=0;  //year
RTC_DATA_ATTR long long H=0;  //hour
RTC_DATA_ATTR long long M=0;  //minute
RTC_DATA_ATTR long long S=0;  //second
RTC_DATA_ATTR uint32_t dS=0;  //seconds counter for dot
RTC_DATA_ATTR struct tm timeinfo; //saves full date variable
long long inicio=0; //saves start time
long long ahora=0;  //saves current time
//Define digit pins in an array, in display order, for looping
//Numbers match ESP32-Thing GPIO number
int DigPins[4]{
  17,// first digit (GPIO 17)
  23,//second digit (GPIO 23)
  19,//third digit  (GPIO 19)
  25//fourth digit  (GPIO  25)
};
//Define segment pins
//Numbers match ESP32-Thing GPIO number
int SegPins[8]{
  14,   //P
  26,   //g
  18,   //f
  13,   //e
  12,   //d
  27,   //c
  22,   //b
  15    //a
};
//Auxiliary variables
//Numbers match ESP32-Thing GPIO number
int ProgPin=32;   //Pin no used for program
int ButtonStatus=1;
int ledPin=5;     //Used to blink the ESP32-Thing blue led
int ProgNum=-1;   //Define a variable to keep track of current program number
// WIFI
// Define your wifi network and credentials
char ssid1[] = "YOUR_WIFI_SSID1";
char pass1[] = "YOUR_WIFI_PASS1";
char ssid1[] = "YOUR_WIFI_SSID2";
char pass1[] = "YOUR_WIFI_PASS2";
WiFiMulti wifiMulti;
// NTP variables
// Update time zone if needed
const char* ntpServer1 = "pool.ntp.org";
const char* ntpServer2 = "time.nist.gov";
const long  gmtOffset_sec = 3600;
const int   daylightOffset_sec = 0; //this will be corrected later with software
const char* time_zone = "CET-1CEST,M3.5.0,M10.5.0/3";  // TimeZone rule for Europe/Rome including daylight adjustment rules (optional)
// Displayed characters in every digit are a byte array indicating ON (1) and OFF (0) segments
// Use a wiring pattern that matches an understandable byte chain so you can make them up easily
// Segment/byte pattern: Babcdefgp --> where 1 is HIGH (ON) and 0 is LOW (OFF)
// Refer to a character by calling this array, i.e.:
//  - call number 3 by calling ns[3]
//  - call letter A by calling ns[20]
//  - call underscore symbol (_) by calling ns[49]
byte ns[50]{ // Array Position - Byte character
  B11111100,// 0-0
  B01100000,// 1-1
  B11011010,// 2-2
  B11110010,// 3-3
  B01100110,// 4-4
  B10110110,// 5-5
  B10111110,// 6-6
  B11100000,// 7-7
  B11111110,// 8-8
  B11110110,// 9-9
  B11111101,// 10-0.
  B01100001,// 11-1.
  B11011011,// 12-2.
  B11110011,// 13-3.
  B01100111,// 14-4.
  B10110111,// 15-5.
  B10111111,// 16-6.
  B11100001,// 17-7.
  B11111111,// 18-8.
  B11110111,// 19-9.
  B11101110,// 20-A
  B00111110,// 21-b
  B10011100,// 22-C
  B01111010,// 23-d
  B10011110,// 24-e
  B10001110,// 25-f
  B10111100,// 26-G
  B00101110,// 27-h
  B00001100,// 28-I
  B11111000,// 29-J
  B01101110,// 30-K(H)
  B00011100,// 31-L
  B00101010,// 32-m(n)
  B00101010,// 33-n
  B00111010,// 34-o
  B11001110,// 35-P
  B11100110,// 36-q
  B00001010,// 37-r
  B10110110,// 38-S
  B00011110,// 39-t
  B01111100,// 40-U
  B00111000,// 41-v
  B00111000,// 42-w(v)
  B01101110,// 43-X(H)
  B01110110,// 44-y
  B11011010,// 45-Z
  B00000001,// 46-. (dot)
  B11000110,// 47-* (astherisc)
  B00000010,// 48-- (hyphon)
  B00010000,// 49-_ (underscore)
};
//array to store displayed digits
int digits[4];
//digit calculation variables
int first_digit = 0;
int second_digit = 0;
int third_digit = 0;
int fourth_digit = 0;
//counters for looping digits and current number
int dig=0;
int n=0;
int dot=1;
void setup()
{
  // Start disabling bluetooth and WIFI to save energy
  // Just power WIFI later, when needed.
  esp_err_t esp_bluedroid_disable(void);
  esp_err_t esp_bt_controller_disable(void);
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
  
  // Start serial communication for any debug messages
  // Commented for production; uncomment for debugging
  //Serial.begin(115200);
  //Define I2C pins, as we are not using standard ones
  Wire.begin(21,4);
  // Activate digit pins looping the pin array.
  for (dig=0; dig<4; dig++){
    pinMode(DigPins[dig], OUTPUT);
  }
  for (dig=0; dig<4; dig++){  //set them LOW (turn them OFF)
    digitalWrite(DigPins[dig], LOW);
  }
  // Activate segment pins
  for (int i=0; i<8; i++){
    pinMode(SegPins[i],OUTPUT);
  }
  // Activate LED pin
  pinMode(ledPin, OUTPUT);
  // Activate PROG pin
  pinMode(ProgPin, INPUT_PULLUP);
  // Turn ON Wifi
  wifiON();
  
  // Setup NTP parameters
  sntp_set_time_sync_notification_cb( timeavailable );
  sntp_servermode_dhcp(1);
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer1, ntpServer2);
  //wait for date
  do{
    delay(100);
  }while(TIME==0);
  delay(500);
  //WIFI can be turned off now
  wifiOFF();  
  // Setup BMP280
  unsigned status;
  //BMP-280 I2C Address:
  // 0x76 if SD0 is grounded
  // 0x77 if SD0 is high
  status = bmp.begin(0x76);
  if (!status) {
    Serial.println(F("Could not find a valid BMP280 sensor, check wiring or "
                      "try a different address!"));
    Serial.print("SensorID was: 0x"); Serial.println(bmp.sensorID(),16);
    Serial.print("        ID of 0xFF probably means a bad address, a BMP 180 or BMP 085\n");
    Serial.print("   ID of 0x56-0x58 represents a BMP 280,\n");
    Serial.print("        ID of 0x60 represents a BME 280.\n");
    Serial.print("        ID of 0x61 represents a BME 680.\n");
    ESP.restart();
    while (1) delay(10);
  }
  //Default settings from datasheet.
  bmp.setSampling(Adafruit_BMP280::MODE_NORMAL,     // Operating Mode.
                  Adafruit_BMP280::SAMPLING_X2,     // Temp. oversampling
                  Adafruit_BMP280::SAMPLING_X16,    // Pressure oversampling
                  Adafruit_BMP280::FILTER_X16,      // Filtering.
                  Adafruit_BMP280::STANDBY_MS_500); // Standby time.
  //get initial readings
  readSensors();
  
  // Get correct time
  sync_clock();
  num_shift();
  // Set start time
  inicio = millis();
  // Set dot time
  dS=millis();
}
void loop(){
  // After setup, this function will loop until shutdown.
  
  // Start time counter using chip's milisecond counter
  uint32_t ahora=millis();
  
  // Check if the counter has reached 60000 miliseconds.
  // Calibrate if you realize that time desyncs
  // This depends on processor calculation, which can vary with input voltage (if on batteries) and temperature.
  while ( (ahora-inicio+(S*1000)) < 59500){
    // Run dot blinker when not showing temperature
    //commented as it sometimes desyncs the hour digit (needs fixing)
    /*if(ProgNum==-1){
      dot_blinker();
    }*/
    
    // Reading push button:
    // when pressed change status
    if(digitalRead(ProgPin)==1){
      ButtonStatus=1;
    }
    // when released after press, set "program change" status
    // this avoids constant change when holding
    if(digitalRead(ProgPin)==0 && ButtonStatus==1){
      ButtonStatus=2;
    }
    // Status 2 -> Programm change
    if(ButtonStatus==2){
      //reset status
      ButtonStatus=0;
      //update programm number
      ProgNum=-ProgNum;
      Serial.println(ProgNum);
      //change to temp
      if(ProgNum==1){
       //light LED
       digitalWrite(ledPin, HIGH); 
       //save TIME
       TIME=digits[0]*1000+(digits[1]-10)*100+digits[2]*10+digits[3];
       //show temp
       room_temp();
      }else if(ProgNum==-1){  //change to time
        //turn off LED
        digitalWrite(ledPin, LOW);
        //restore time
        split_time(TIME);
      }
    }
    
    //update time counter
    ahora=millis();
    num_shift();
    delay(6);
  }
  // End of while() after 60 seconds
  // Reset initial time
  S=0;
  inicio=millis();
  //make sure there's dot at the end
  if(digits[1]<=10){
    digits[1]+10;
    dot=0;
  }
  //update time
  //if program is in temperature, change to time
  if(ProgNum==1){
    split_time(TIME);
    ProgNum=-1;
  }
  updateMinutes();
}
//-WIFI function
//--Setup and turn Wifi ON
void wifiON(){
  // Set up WIFI (ESP32 Thing only working with wifiMulti library)
  Serial.println("Conectando");
  wifiMulti.addAP(ssid1,pass1);
  //wifiMulti.addAP(ssid2,pass2);
  int led=1;
  int boot=1;
  wifiMulti.run();
  //WiFi.disconnect(true);
  
  while((WiFi.status() != WL_CONNECTED)){
    digitalWrite(ledPin, HIGH);
    delay(500);
    digitalWrite(ledPin,LOW);
    delay(500);
    wifiMulti.run();
  }
  // Make sure LED is off when finished
  digitalWrite(ledPin,LOW);
  // When CONNECTED, while loop ends.
  Serial.print("Conectado a ");
  Serial.println(WiFi.SSID());
  Serial.println(WiFi.localIP());
}
//-Disable WIFI
void wifiOFF(){
  //turn WIFI off as it's not needed any more
  WiFi.disconnect(true);
  WiFi.mode(WIFI_OFF);
  //disable wifi (deinit clears all flash data)
  esp_wifi_deinit();
}
//----BMP280 functions
//-Read sensor data
void readSensors(){
  TEMP=bmp.readTemperature();
  Serial.println("T: "+(String)TEMP+"ºC");
  PRES=bmp.readPressure();
  Serial.println("P: "+(String)PRES+"hPa");
  ALT=bmp.readAltitude(hREF);
  Serial.println("h: "+(String)ALT+"msnm");
}
//----NTP functions
//-Callback function (get's called when time adjusts via NTP)
void timeavailable(struct timeval *t)
{
  Serial.println("Got time adjustment from NTP!");
  simpleTime();
}
void simpleTime()
{ 
  if(!getLocalTime(&timeinfo)){
    Serial.println("No time available (yet)");
    return;
  }
  Serial.print("Synced time: ");
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
  //save time
  sync_clock();
}
//-
//PROGRAM #0: HELLO at startup
//function to say "HOLA" at start up while connecting to WiFi
void ini_HOLA(){
  int h=30;
  int o=34;
  int l=31;
  int a=20;
  digits[0]=46;
  digits[1]=46;
  digits[2]=46;
  digits[3]=46;
}
//PROGRAM #1: WiFi synced time
//function that gets current time via WiFi
void sync_clock(){
  Y=timeinfo.tm_year;
    TIME=TIME+Y*10000000000;
  m=timeinfo.tm_mon;
  m=m+1;
    TIME=TIME+m*100000000;
  d=timeinfo.tm_mday;
    TIME=d*1000000;
  H=timeinfo.tm_hour;
  //check daylight saving time and correct hour
  //"27 Mar (03 27) +1 hour; 30 Oct (10 30) back -1 hour"
  if( ( m*100+d >= 327 ) && ( m*100+d < 1030 ) ){
    H=H+1;
  }else{
    H=H;
  }
  TIME=TIME+H*10000;
  M=timeinfo.tm_min;
    TIME=TIME+M*100;
  S=timeinfo.tm_sec;
    TIME=TIME+S;
    
  //split current 4 digit time into sigle digits
  split_time((TIME/100)-((TIME/100)/10000)*10000);
}
//number splitting function to separate time string into digits
void split_time(long long num) {
  first_digit = num / 1000;
  digits[0] = first_digit;
  int first_left = num - (first_digit * 1000);
  second_digit = first_left / 100;
  digits[1] = second_digit;
  //añadimos el segundero fijo (sumamos 10)
  digits[1] = digits[1]+10;
  int second_left = first_left - (second_digit * 100);
  third_digit = second_left / 10;
  digits[2] = third_digit;
  fourth_digit = second_left - (third_digit * 10);
  digits[3] = fourth_digit;
}
// number shifting function
void num_shift(){
  for (dig=0; dig<4; dig++){// turn digits off
    digitalWrite(DigPins[dig], HIGH);
  }
  
  //turn them ON (LOW) one by one
    digitalWrite(DigPins[n], LOW);
    for(int seg=7; seg>=0; seg--){
      //read byte array for digit
      int x = bitRead(ns[digits[n]],seg);
      //turn the segments ON or OFF
      digitalWrite(SegPins[seg],x);
    }
    n++;// move to next no.
    if (n==4){// if no. is 4, restart
      n=0;
    }
}
//Getting room temperature from LM35 sensor via NodeMCU analog input pin (ADC)
void room_temp(){
  readSensors();
  //This will return temperature in XY.Z format
  //We don't want the integer value, so we can use the four digits to display temperature units as well "XYºC"
  //Getting first digit in byte format ns[X] where X=int(XY.Z/10)=int(X.YZ)=X
  digits[0]=int(TEMP/10)-int(TEMP/100)*10;
  //Getting second digit in byte format ns[Y] where Y=int(XY.Z)-X*10=int(XY.Z)-int(XY.Z/10)*10
  digits[1]=int(TEMP/1)-int(TEMP/10)*10;
  //Setting temperature units as degree celsius (ºC) in byte format
  digits[2]=47; //astherisc *
  digits[3]=22; //character C
}
void updateMinutes(){
  //add 1 to the last digit
  digits[3]=digits[3]+1;
  //if greater than 9, reset to 0
  if (digits[3]>9){
    digits[3]=0;
    //then add 1 to the third digit
    updateM0();
  }
  //send last digit to display
  digitalWrite(DigPins[3], LOW);
}
void updateM0(){
  //add 1 to third digit
  digits[2]=digits[2]+1;
  //if greater than 5, reset to 0
  if (digits[2]>5){
    digits[2]=0;
    //then add 1 to current hours
    updateH1();
  }
  //send digit to display
  digitalWrite(DigPins[2], LOW);
}
void updateH1(){
  //add 1 hour
  digits[1]=digits[1]+1;
  // if greater than 19, reset to 10
  // (instead of number 0-9, we use numbers 10-19 in order to add the "dot" to the display)
  if (digits[1]>19){
    digits[1]=10;
    //then add 1 to the first digit
    updateH0();
  }// reset when it's 24h (go back to 00)
  if(digits[1]>13 && digits[0]==2){
    digits[0]=0;
    digits[1]=10;
  }
  //display digits
  digitalWrite(DigPins[1], LOW);
  digitalWrite(DigPins[0], LOW);
}
void updateH0(){
  //add 1 to first digit
  digits[0]=digits[0]+1;
  //if greater than 2, reset to 0 (it shouldn't happen as we reset earlier, but just in case..)
  if (digits[0]>2){
    digits[0]=0;
  }
  //display digit
  digitalWrite(DigPins[0], LOW);
}
void dot_blinker(){
  //every second, blink the dot
    if( millis()-dS > 1000 && dot == 1){
      digits[1]=digits[1]-10;
      dS=millis();
      dot=0;
    }
    if( millis()-dS > 250 && dot == 0){
      digits[1]=digits[1]+10;
      dot=1;
    }
}

Ten en cuenta que se hace uso de las siguientes librerías adicionales que deberás instalar usando el IDE de Arduino:

  • Gestor de placas esp32 de Espressif
  • Librería WiFiMulti
  • Librería Adafruit_BMP280
    • (el resto de librerías se derivan de las anteriores)

Deberemos probar constantemente el funcionamiento del código durante la fase de prototipado para poder cambiar la asignación de pines en caso de algún mal funcionamiento. Si ya está todo soldado y nos falla algo, será bastante más complicado encontrar y resolver el error, de ser en alguna conexión.

Ensamblaje

Soldaduras

Una vez comprobado el código y diseñada la PCB, ya podemos empezar a soldar, teniendo claro donde va cada cosa y tomando mucha precaución, porque una mala soldadura puede arruinarte cualquier módulo o generar errores en el programa.

Diseño de carcasa

Con todo soldado y montado ya tenemos una mejor idea del volumen real del dispositivo. Yo lo voy a medir todo milimétricamente con un pie de rey para hacer un modelo 3D de la placa ya montada.

Modelo 3D en Blender de los componentes conectados en su posición final

Así puedes diseñar una carcasa alrededor del modelo y fabricarla con una impresora 3D, pero puedes buscar otros tipos de ensamblajes con madera o metal laminados.

Yo suelo usar dos piezas (una de base y otra de tapa) para poder atornillar la una a la otra. También voy a incluir distintos huecos para permitir la conexión del cable USB, para añadir el pulsador de cambio de programa y para añadir un soporte rotatorio que encajará el reloj en una ranura del televisor.

Además usaré PLA blanco en una pieza y gris en otra para darle algo más de vida al diseño, quedando todo así:

Y eso es todo. Espero que te haya gustado y te sea útil. ¡Lánzate a montar tu propio reloj WIFI! Cualquier duda sobre este reloj, al Twitter! 🐦

🐦 @RoamingWorkshop