sábado, 12 de septiembre de 2026

El Juego de la Vida de Conway en Python: una guía paso a paso (Writeups + CLI)

¿Cuántas veces viste una animación del Juego de la Vida de Conway o te lo encontraste como ejercicio de programación en algún curso y pensaste: "esto lo tengo que intentar" A mí me pasó un montón de veces.

"El Juego de la vida es un autómata celular diseñado por el matemático británico John Horton Conway en 1970. Es un juego de cero jugadores, en el que su evolución es determinada por un estado inicial, sin requerir intervención adicional. Se considera un sistema Turing completo que puede simular cualquier otra Máquina de Turing." Fuente: Wikipedia

Partiendo de reglas simples sobre una cuadrícula, surgen comportamientos y patrones caóticos que parecen tener vida propia. Sin embargo, cada vez que me sentaba a buscar cómo encararlo desde cero, me chocaba contra la misma pared: tutoriales incompletos, explicaciones que daban por obvios pasos fundamentales, o implementaciones que usaban librerías como NumPy con convoluciones matemáticas avanzadas, sin explicarte qué es realmente una célula, cómo se indexa la matriz o cómo se calculan los vecinos en una simple cuadrícula de dos dimensiones.

"Se trata de un juego de cero jugadores, lo que quiere decir que su evolución está determinada por el estado inicial y no necesita ninguna entrada de datos posterior. El "tablero de juego" es una malla plana formada por cuadrados (las "células") que se extiende por el infinito en todas las direcciones. Por tanto, cada célula tiene 8 células "vecinas", que son las que están próximas a ella, incluidas las diagonales. Las células tienen dos estados: están "vivas" o "muertas" (o "encendidas" y "apagadas"). El estado de las células evoluciona a lo largo de unidades de tiempo discretas..." Fuente: Wikipedia

El Juego de la Vida es un desafío de programación que siempre me costó llevar a cabo de forma limpia. Por eso decidí encararlo de otra manera, armando un camino pedagógico paso a paso, guiado, donde cada concepto se construyera sobre el anterior usando Python puro, usando solo librerías estándar.

Hoy les comparto el resultado de ese proceso: conway_writeups_cli_py, un proyecto que reúne una serie de cuadernos interactivos (Writeups en Jupyter) y una versión modular para jugar directamente desde la terminal (CLI).

Objetivos del proyecto

  1. Aprender sin saltear pasos: Comprender la lógica del autómata usando estructuras de datos nativas de Python (listas de listas) antes de depender de paquetes externos.
  2. Documentar el recorrido en fases: Escribir cuadernos interactivos de Jupyter que sirvan como guía de estudio paso a paso.
  3. Construir una CLI modular y limpia: Separar responsabilidades en módulos independientes (lógica, visualización y bucle de control).
  4. Entorno reproducible con uv: Facilitar que cualquiera pueda clonar el proyecto y probarlo en segundos sin lidiar con dependencias rotas.

Los Writeups en Jupyter

Para no saltar de golpe a un archivo de 150 líneas de código, dividí este desafío en tres cuadernos dentro de la carpeta writeups/:

Fase 1: Fundamentos y representación de matrices

Matrices bidimensionales. Acá arrancamos desde lo más básico:

  • Representar una cuadrícula con listas anidadas (list[list[int]]).
  • Comprender la relación entre coordenadas, filas y columnas.
  • Generar matrices con estados aleatorios iniciales (células vivas con 1 y muertas con 0).

Fase 2: Las reglas clásicas y la vecindad

El corazón de Conway reside en cómo interactúa una célula con su entorno:

  • Vecindad de Moore: Los 8 vecinos que rodean a cada posición.
  • Coordenadas relativas: Definir una constante con los desplazamientos (-1, -1), (0, 1), etc.
  • Control de bordes: Cómo evitar errores de índice (IndexError) cuando evaluamos células en los márgenes de la cuadrícula.
  • Reglas con match / case: Implementar de forma limpia las transiciones: una célula muerta nace con 3 vecinos vivos; una célula viva sobrevive con 2 o 3, y muere en cualquier otro caso por aislamiento o sobrepoblación.

Fase 3: La simulación en tiempo real

Con la lógica ya resuelta, tocaba darle movimiento:

  • Copiar matrices de forma segura para no mutar el estado durante el cálculo generacional.
  • Limpiar la consola entre fotogramas para generar la ilusión de animación.
  • Detectar cuándo la simulación se estanca (juego terminado).

De los cuadernos a la arquitectura modular (CLI)

Una vez que los cuadernos cumplieron su rol, el siguiente paso fue organizar el código como un proyecto formal. Dividiendo la versión CLI en tres archivos:

  • cli/logica.py: Contiene la lógica pura del juego. Agnóstico a la consola; no tiene ningún print ni dependencias del sistema operativo. Maneja la generación del tablero, el conteo de vecinos vivos y la evolución generacional.
  • cli/display.py: Se encarga de la vista. Transforma los números en caracteres visuales (. para células muertas y # para vivas), imprime el tablero y limpia la pantalla.
  • cli/main.py: El orquestador. Conecta la lógica con la vista, maneja la velocidad de la animación y permite salir limpiamente con Ctrl + C sin tirar errores en la terminal.

Estructura de archivos del repositorio

.
├── cli/               # Versión ejecutable por terminal
│   ├── display.py     # Manejo de pantalla y formato visual
│   ├── logica.py      # Lógica pura del juego y reglas
│   └── main.py        # Orquestador del bucle de simulación
├── writeups/          # Cuadernos de estudio paso a paso (Jupyter)
│   ├── fase1_fundamentos_matrices.ipynb
│   ├── fase2_reglas_juego.ipynb
│   └── fase3_simulacion_cli.ipynb
├── pyproject.toml     # Definición del entorno y dependencias
├── uv.lock            # Lockfile para reproducibilidad absoluta
└── LICENSE            # Licencia GNU GPL v3

¿Cómo probarlo en tu pc?

Últimamente estoy usando uv (gran herramienta para gestionar entornos en Python)

# 1. Clonás el repositorio
git clone https://github.com/mcattani/conway_writeups_cli_py.git
cd conway_writeups_cli_py

# 2. Sincronizás el entorno
uv sync

1. Para correr la simulación en la consola:

uv run python cli/main.py

(Podés detenerla apretando Ctrl + C).

2. Para explorar los cuadernos interactivos:

uv run jupyter lab
# o si preferís:
uv run jupyter notebook

¿Qué se viene? Próximo paso: versión gráfica con Pygame

Esta versión en consola y los writeups son el primer escalón. Me gustaría escribir una versión simple utilizando pygame (que nunca usé). Esa versión será el foco del próximo post.


Como siempre, les dejo todo el código (la versión cli y los writeups) en mi repo:

Repositorio: https://github.com/mcattani/conway_writeups_cli_py

Todos los comentarios son siempre bienvenidos. Saludos!

Y si este paso a paso te sirvió de ayuda o te pareció interesante, acordate que podés invitarme un cafecito para apoyar el blog:

Invitame un café en cafecito.app

viernes, 10 de julio de 2026

PytDown: Script en python para descargar videos de Youtube, IG y FB.


¿Cuántas veces intentaste descargar un video YouTube, Facebook o Instagram y terminaste lidiando con páginas llenas de spam, captchas infinitos y botones falsos de "Descargar" que te querían hacer descargar software dudosísimo?

Para los que preferimos la terminal y las herramientas limpias, yt-dlp es la opción preferida. Pero a veces escribir comandos gigantes en la consola para elegir el formato ideal, configurar el audio o filtrar idiomas puede ser bastante molesto, sobre todo si no es una herramienta que quizás usamos tan seguido.

El programa que les comparto hoy: PytDown, un script interactivo en Python para la terminal que automatiza todo este proceso con una interfaz un tanto más amigable.

Originalmente el script se centraba exclusivamente en la descarga de videos de YT, luego me di cuenta que con pocas modificaciones podía adaptarse para funcionar bien con FB e IG (al momento de escribir esto la compatibilidad con IG requiere una actualización de yt-dlp ¯(ツ)/¯ Editado: 14/07/2026 recién se actualizó la librería yt-dlp, las descargas funcionan en todos los sitios!)


Objetivos de este proyecto:

  1. Crear una interfaz interactiva en la terminal: Con tablas limpias y barras de progreso fluidas usando la librería Rich.
  2. Filtrar formatos duplicados y doblados: Seleccionar automáticamente el video en su idioma original, evitando descargar por error una pista doblada a otro idioma (detesto este tema de los doblajes automáticos que a veces mete YT).
  3. Integración con Deno: Utilizar este runtime de JavaScript para procesar las firmas y descifrados de YouTube, necesario para que las descargas no fallen.
  4. Convertir el script en un comando global: Usar uv o pip para instalarlo en el sistema y ejecutarlo escribiendo simplemente pytdown desde cualquier carpeta.

¿Cómo está estructurado?

Como vimos en nuestro post sobre modularización), el proyecto está dividido en trozos lógicos para que sea fácil de comprender y mantener:

  • pyproject.toml: Donde definimos la configuración del paquete, los requerimientos (Python >= 3.13) y cómo se registra el comando global (pytdown).
  • src/pytdown/main.py: El punto de entrada y la interfaz de la consola. Acá es donde se arma la tabla con los formatos disponibles usando Rich y se le pide al usuario que elija qué descargar.
  • src/pytdown/get_video_info.py: Se conecta a la URL mediante yt-dlp sin descargar nada, extrae los formatos válidos, filtra y se queda solo con lo que sirve.
  • src/pytdown/download_video.py: Se encarga de llamar a la descarga real, de buscar la carpeta destino (con un selector de archivos visual o por consola, según este instalado tkinter) y de actualizar la barra de progreso.
  • src/pytdown/utils.py: Tiene funciones para formatear el tamaño de los bytes (a KB, MB, GB), sanitizar nombres de archivos y chequear si FFmpeg está instalado.

¿Por qué necesitamos Deno?

Si leíste el código de extracción de datos, seguro notaste esta línea:

ydl_opts: dict[str, Any] = {    
    ...    
    "js_runtimes": {"deno": {"path": deno.find_deno_bin()}}    
}

¿Por qué usamos Deno? YouTube cambia constantemente la forma en que entrega los videos y usa scripts en JavaScript en su frontend para descifrar las URLs de streaming (las famosas firmas o signature ciphers). Para resolver esto sin problemas, yt-dlp necesita un intérprete de JavaScript.

En lugar de depender de engines más pesados o complejos, el paquete de Python deno nos permite instalar y acceder al binario de Deno, asegurando que yt-dlp pueda descifrar el código dinámico de YouTube sin problema.


El idioma original y la fusión con FFmpeg

Al diseñar este script nos topamos con dos problemas típicos que suelen ser bastante molestos:

1. El audio correcto

Últimamente, canales populares de YouTube suben videos con múltiples pistas de audio para distintos doblajes. Si hacés una descarga genérica, es muy probable que termines con el audio en un idioma que no querés. PytDown detecta el language original del video en los metadatos y, cuando inicia la descarga, fuerza a yt-dlp a buscar e integrar prioritariamente esa pista de audio usando el formato:format_id+bestaudio[language=original_lang]/bestaudio/best.

2. El misterio de los formatos HD (FFmpeg)

En YouTube y otras redes sociales, los videos de alta calidad (1080p, 4K, etc.) se transmiten por separado: el flujo de video va por un lado y el de audio por otro. Para poder descargar el video completo, ambos flujos deben ser descargados e integrados. La herramienta encargada de hacer esto es FFmpeg.

Si no tenés FFmpeg instalado, PytDown hace dos cosas:

  • En la tabla de calidades marca el formato con un asterisco (*) y los deshabilita visualmente.
  • Si intentás seleccionar uno de esos formatos de todas formas, el script termina su ejecución avisándote que necesitás instalar FFmpeg.

¿Cómo se usa?

La instalación es sencilla. Si sos del equipo de uv (herramienta moderna para gestionar proyectos de Python que recomendamos), podés clonar el repositorio e instalarlo como una herramienta global de tu sistema con un solo comando:

# Clonás el repositorio    
git clone https://github.com/mcattani/pytdown.git    
cd youtube_downloader    

# Lo instalás globalmente en tu sistema    
uv tool install .

Si preferís el método clásico con pip, también podés hacerlo:

pip install .

Una vez instalado solo tenés que abrir tu terminal y ejecutar:

pytdown

El script te va a pedir la URL, va a procesar la info y te va a mostrar una tabla. Según esté instalado o no ffmpeg:



Elegís el número de la opción, se abrirá un selector de carpetas gráfico (usando Tkinter) para que elijas dónde guardarlo (si tkinter no esta instalado te va a permitir escribir la ruta a mano), ¡y listo! Vas a ver una barra de progreso indicando que la descarga está en curso.

Como siempre, podés ver y descargar todo el código fuente del proyecto desde el repo: https://github.com/mcattani/pytdown

¿Qué te pareció el proyecto? ¿Qué otra función le agregarías? ¡Todos los comentarios son siempre bienvenidos!

Y si te sirvió de ayuda, acordate que podés invitarme un cafecito para apoyar el blog.

Invitame un café en cafecito.app


sábado, 30 de mayo de 2026

ESP32: Reloj con RTC interno, sincronización NTP y Deep Sleep


La idea de este post es aprender sobre tres temas interesantes y útiles del ESP32 que servirán mucho para proyectos actuales y futuros: el RTC, el modo Deep Sleep y la sincronización NTP.

Ya en un post anterior (servidor web con sensor DHT11) tocamos el tema de la sincronización con un servidor NTP, pero hoy vamos a utilizarlo de otra manera.

Qué nos proponemos:

  1. Entender el RTC interno: Olvidarnos de módulos externos y aprender a usar el reloj que ya viene dentro del ESP32.
  2. Sincronización NTP: Aprender a "preguntar" la hora a Internet para que nuestro reloj sea atómico.
  3. Eficiencia con Deep Sleep: Entender cómo poner a dormir el chip para que la batería no se agote en un suspiro.
  4. Modularización: Seguir practicando cómo separar el código en archivos para que sea limpio y profesional.
  5. Solucionar problemas reales que nos encontramos durante la marcha y nos hacen desesperar y querer tirar todo a la basura: Aprender qué es el Time Drift y cómo ganarle la batalla.

Para este proyecto vamos a usar componentes que probablemente ya tengas en tu cajón de proyectos:
  • ESP32: En mi caso uso un WEMOS D1 R32, pero cualquier ESP32 te sirve.
  • Pantalla OLED SSD1306: La clásica (primera vez que la uso, pero uno la ve en todos lados ahora) de 128x64 píxeles que se conecta por I2C.
  • Pulsador (Push Button): El que va a "despertar" a nuestro microcontrolador.
  • Resistencia de 10kΩ: Fundamental para que el botón funcione correctamente (luego te explico por qué).
  • Protoboard y cables.

Tabla de Conexiones

Si estás usando el WEMOS D1 R32, aquí tienes el mapa para no perderte. Si tenés otro modelo de ESP32, chequea bien los pines correspondientes. Es importante ubicar los pines SDA/SCL y algún pin marcado como RTC para el botón (push).

Componente Pin del ESP32 Notas
OLED VCC 3.3V o 5V Según tu modelo de pantalla
OLED GND GND Tierra
OLED SCL GPIO 22 Reloj I2C
OLED SDA GPIO 21 Datos I2C
Botón (Terminal 1) 3.3V Entrada de voltaje
Botón (Terminal 2) GPIO 27 Señal de entrada
Resistencia 10k GPIO 27 a GND Configuración Pull-down

Ojo con el botón: Es MUY importante conectar la resistencia entre el GPIO 27 y GND. Esto mantiene el pin en "bajo" (0V) mientras no tocamos el botón, evitando que el ruido eléctrico despierte al ESP32 por error.

1. ¿Qué es el RTC del ESP32?

El RTC (Real-Time Clock) es un módulo interno del chip capaz de mantener la fecha y la hora "con precisión" (más o menos..., veremos más adelante).

A diferencia de otros microcontroladores que nos obligan a comprar módulos externos (como el famoso DS3231), el ESP32 ya lo trae de serie. Como bien explican en Vasanza - RTC Interno, es vital para proyectos de data logging o riego automático. Eso sí, hay que tener en cuenta que si le quitamos la alimentación por completo, la hora se borra, por eso necesitamos "ayuda" externa para ponerlo en hora al principio.

2. Sincronización vía NTP

Para que nuestro reloj sea exacto, usamos el Network Time Protocol (NTP). Básicamente, es el lenguaje que usa el ESP32 para preguntarle a un servidor en Internet: "¿qué hora es exactamente?".

El ESP32 se conecta a servidores como pool.ntp.org mediante el puerto UDP 123. Es un proceso rápido, pero requiere que configuremos el GMT_OFFSET (para que sepa en qué país estamos) y el DAYLIGHT_OFFSET para el horario de verano. Si quieres profundizar en cómo sincronizarlo, te recomiendo este artículo sobre Sincronizar RTC.

3. Modos Sleep: ¿Cómo ahorrar batería?

Aquí es donde ocurre la magia. Si dejamos el ESP32 encendido a tope, consume unos 160-260 mA (variando un según modelos... en este caso estoy usando un WEMOS D1 R32 que no es el más ahorrador...), por eso usamos los modos de ahorro.

Según fuentes como Luis Llamas o Last Minute Engineers, el ESP32 tiene varios estados. Nosotros usaremos el Deep Sleep. En este modo, el CPU y el Wi-Fi se apagan por completo, pero —y aquí está el secreto— el módulo RTC se queda encendido. El consumo baja a unos increíbles 10 ~ 150 µA.

Para este proyecto, el ESP32 se despertará solo cuando pulsemos un botón (despertador externo GPIO).


El reto: El "Time Drift" (o por qué mi reloj se atrasa o se adelanta volviéndome loco)

Durante mis pruebas armando este proyecto me golpee la cabeza contra la pared durante un rato largo (por decir poco...). El oscilador interno del ESP32 no es perfecto. Sin un cristal de cuarzo externo de 32kHz, el reloj tiende a desviarse varios minutos en unas pocas horas.

¿La solución? En cada despertar, el código hace una sincronización NTP rapidísima. Así, aunque el reloj interno haya "derrapado" un poco mientras dormía, al mostrarte la hora siempre será la correcta. Admito que no era la idea original mientras armaba este proyecto, la idea era sincronizar una sola vez el RTC del ESP32 y luego (mientras se mantuviera conectado) leer siempre la información del mismo.

Estructura Modular

Como ya vimos en nuestro post sobre modularización, hemos separado el código en trocitos lógicos:

  • wifi_connect: Para entrar y salir de Internet sin dramas.
  • time_manager: El encargado de hablar con el NTP y ajustar el RTC.
  • display_utils: El artista que dibuja en la pantalla OLED.

Ventajas de la encapsulamiento! Si mañana queŕes usar una pantalla distinta, solo cambias el archivo del display y el resto del proyecto sigue funcionando perfectamente.

Consejos finales para tu montaje

  1. ¡Apaga el display! Muchos se olvidan de usar displayPowerOff() antes de dormir. Si el display se queda encendido, de nada sirve que el ESP32 esté en Deep Sleep; la batería adiós gracias.

  2. Hardware: Usa una resistencia de 10kΩ en modo Pull-down para el botón del GPIO 27. Si no, "despertares fantasma" que te van a enloquecer.

  3. Alimentación: Alimenta por el pin 5V/VIN para que el regulador de la placa haga su trabajo y proteja tu ESP32.

Como siempre, todo el código estará disponible en github: https://github.com/mcattani/reloj_esp32_rtc_ntp_dsleep

Espero que este post les haya interesado. Todos los comentarios son bienvenidos. Y si quieren, pueden invitarme un cafecito!

Invitame un café en cafecito.app

jueves, 29 de enero de 2026

Separando archivos en Arduino y ESP: .ino, .h y .cpp

Cuando uno empieza a programar en Arduino o ESP, lo más común es trabajar con un único archivo .ino.

Eso funciona bien para ejemplos chicos y pruebas rápidas. El problema aparece cuando el proyecto empieza a crecer. “Modularizar” el código no solo es una buena práctica, sino que además nos da la gran ventaja de poder reutilizarlo de manera muy sencilla.

Para introducir este tema me pareció un buen ejemplo la conexión a WiFi, dado que es algo que utilizaremos bastante, sobre todo con los ESP.

1. El problema del .ino único

Un sketch típico suele empezar así:

#include <WiFi.h>

const char* ssid = "MiWiFi";
const char* password = "MiPassword";

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("Conectado!");
}

void loop() {
  // código principal
}

Al principio es claro y fácil de entender. El problema es que, con el tiempo, empiezan a aparecer:

  • más configuraciones
  • más funciones
  • más lógica

Y el archivo .ino termina teniendo:

  • código de WiFi
  • código de red
  • lógica de la aplicación
  • configuraciones globales

Y todo esto puede dificultar la lectura del sketch, no escalar bien a medida que el proyecto crece e incluso hacer que reutilizar código termine siendo más costoso que volver a escribirlo.

2. El rol de cada archivo: .ino, .h y .cpp


Archivo .ino

  • Punto de entrada
  • Contiene setup() y loop()
  • Orquesta el flujo general

Archivo .h

  • Declara qué ofrece un módulo
  • Define funciones, constantes y estructuras públicas
  • Actúa como contrato

Archivo .cpp

  • Implementa la lógica real
  • Contiene los detalles internos

Captura1

Resumen

El programa principal (.ino) tiene el programa, pero no las funciones que usa. Esas se definen en otro lado. El archivo .h trae las definiciones de las funciones y los parámetros que recibe y que devuelve, (pero no el código propiamente dicho) y nos permite ver que funciones tenemos disponibles para trabajar: Nos da la información precisa para usar las funciones del [archivo] .cpp - El fichero .cpp, tiene el código final de las funciones y aquí es donde podemos añadir o modificar funciones, siempre y cuando las definamos en el fichero .h

Fuente: https://www.prometec.net/organizando-tus-programas-arduino/

Archivo Responsabilidad
.ino Orquestación
.h Interfaz
.cpp Implementación

3. WiFi como módulo reutilizable

Para este ejemplo estaré usando un ESP32 (Wemos D1 R32)

La estructura de archivos será la siguiente:

/
├── main.ino
├── config.h
├── wifi_connect.h
└── wifi_connect.cpp

config.h

#pragma once

// WiFi
const char* WIFI_SSID = "TU_WIFI_AQUI";
const char* WIFI_PASSWORD = "TU_PASSWORD_AQUI";

// mDNS
const char* MDNS_NAME = "NOMBRE_DE_RED_AQUI";

En los lenguajes de programación C y C++, #pragma once es una directiva del preprocesador no estándar pero con un extenso soporte. Está diseñado para asegurar que el código fuente que lo invoca sea incluido una única vez. [...] Usando #pragma once en lugar de la protección de macros (también visto como protección de inclusiones (include) ) generalmente aumentará la velocidad de compilación puesto que es un mecanismo de nivel más alto.

Fuente: https://es.wikipedia.org/wiki/Pragma_once

wifi_connect.h

#pragma once
void conectarWiFi();

wifi_connect.cpp

#include <WiFi.h>
#include <ESPmDNS.h>
#include "config.h"
#include "wifi_connect.h"

void conectarWiFi() {
  Serial.println();
  Serial.print("Conectando a: ");
  Serial.println(WIFI_SSID);

  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);

  // Bucle mientras se conecta
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.print(".");
  }

  // Conexión exitosa -> mostramos los datos de conexión
  Serial.println("");
  Serial.println("WiFi Conectado!");
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());
  Serial.print("IP asignada: ");
  Serial.println(WiFi.localIP());

  // mDNS
  if (!MDNS.begin(MDNS_NAME)) {  // Seteamos el hostname 
    Serial.println("Error iniciando mDNS");
    return;
  }

  // Mostramos los datos de mDNS
  Serial.print("mDNS iniciado: http://");
  Serial.print(MDNS_NAME);
  Serial.println(".local");
}

main.ino

#include "wifi_connect.h"

void setup() {
  Serial.begin(115200);
  // Llamamos a la función para conectar a WiFi
  conectarWiFi();
}

void loop() {}

4. Credenciales, .gitignore y repositorios públicos

Como vimos en el ejemplo de arriba, el archivo config.h contendrá las credenciales para la conexión (entre otras cosas) y debemos tener cuidado de no subir este archivo a nuestro repositorio.

captura2

Debemos siempre agregarlo al archivo .gitignore al crear nuestro repo. En su lugar podemos crear un archivo config_example.h por ejemplo y aclarar que el mismo debe renombrarse antes de subirse al microcontrolador.

// config_example.h
#pragma once

/* Renombrar este archivo a config.h
y completar con tus credenciales*/

// WiFi
const char* WIFI_SSID = "TU_WIFI_AQUI";
const char* WIFI_PASSWORD = "TU_PASSWORD_AQUI";

// mDNS
const char * MDNS_NAME = "NOMBRE_DE_RED_AQUI";

En conclusión, separar el código desde el inicio tiene sus ventajas:

  • mejora la organización
  • facilita el mantenimiento
  • permite reutilizar módulos

Es una buena práctica a adquirir (ya sé que nunca lo hice hasta ahora...) e intentaré mantenerla para proyectos futuros.

Espero que este post les haya interesado. Como siempre, todos los comentarios son bienvenidos. Y si quieren, pueden invitarme un cafecito!

Invitame un café en cafecito.app

sábado, 27 de diciembre de 2025

Temporizador Simple con Gambas3


A veces, las herramientas más sencillas son las más difíciles de encontrar. En mi día a día a menudo necesito un temporizador rápido para no pasarme con el café o simplemente para recordar sacar algo del horno. Buscaba algo que fuera rápido, que no consumiera recursos y que no tuviera mil opciones que nunca uso.

Para este pequeño proyecto, volví a uno de mis lenguajes favoritos para desarrollo rápido de aplicaciones de escritorio: Gambas3.

Recordemos esta entrada para evitar cualquier problema con las versiones del intérprete.

Al momomento de escribir esta entrada la versión actual (stable) es la 3.21.1.

captura 2

Diseño

  • Interfaz de Usuario: La ventana principal tiene dos pestañas: 'Timer' y 'Configuración'.
    • La pestaña 'Timer' permite al usuario establecer las horas, minutos y segundos. Muestra el tiempo restante en una etiqueta estilo LCD.
    • La pestaña 'Configuración' permite seleccionar entre 5 sonidos de alarma diferentes (Alarma, Buzzer, Casio, Teléfono, Radio) y ajustar el volumen.
  • Lógica del Temporizador:
    • Al pulsar 'Iniciar', la aplicación calcula el total de segundos y comienza una cuenta regresiva.
    • Un temporizador interno se actualiza cada segundo, mostrando el tiempo restante en formato HH:MM:SS.
    • Cuando el tiempo llega a cero, se detiene y reproduce el sonido de alarma seleccionado.
  • Configuración: La aplicación guarda la última configuración utilizada (tiempo, sonido de alarma y volumen) y la carga al iniciarse. Los cambios se guardan al cerrar la aplicación.

captura 2

En resumen, es una aplicación de temporizador funcional con opciones de personalización de alarma y persistencia de configuración. El código está bien estructurado en eventos que corresponden a las acciones del usuario (clics en botones, selección en listas, etc.).

Como siempre, dejo el link el repo en github (el código es bastante claro y está bastante comentado)

https://github.com/mcattani/gambas_timer

Cada pequeña ayuda o gesto de apoyo significa un montón para mí. Si quieres ayudar puedes invitándome un cafecito:

Invitame un café en cafecito.app

Saludos!