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!

viernes, 5 de diciembre de 2025

Calculadora React + Vite

Ya en el post anterior les compartí mi primer proyecto con javascript. Continuamos la ruta de aprendizaje con dos tecnologías con las que me estoy familiarizando: React y Vite Y qué mejor manera de empezar que con un clásico: una calculadora.

Debo admitir que me costó un poco más de lo que me esperaba. No soy gran fanático del diseño en general por lo que adelanto que seguramente utilice mucho bootstrap de aquí en adelante y algo de ayuda del buen chatbot (en lo que respecta exclusivamente al diseño). Resulta que las calculadoras son algo más que un if anidado, sobre todo en lo que respecta al comportamiento estable y las situaciones edge:

  • Evitar los doble "00", el doble punto: "1.14.5"
  • Manejo del doble operador
  • Manejo del doble ==
  • Cambio de operadores
  • Permitir el ingreso de datos a través del teclado
  • ...

En fin, situaciones que surgen del uso.

Un diseño muy a lo android que busca ser simple, moderno y funcional, con todo lo necesario para las operaciones del día a día.

Vista previa de la calculadora

Arquitectura de componentes

La calculadora está construida siguiendo una arquitectura de componentes reutilizables, una de las principales ventajas de React.

  • Calculadora.jsx: Es el componente principal y el cerebro de la aplicación. Orquesta a todos los demás componentes y centraliza la lógica y el estado. Aquí se manejan los clics de los botones, se realizan las operaciones matemáticas y se actualiza el valor que se muestra en pantalla.

  • Display.jsx: Un componente simple y reutilizable cuya única responsabilidad es mostrar los valores que recibe a través de sus props. No contiene lógica, simplemente refleja el estado actual de la calculadora que le es proporcionado por el componente anterior

  • Teclado.jsx: Actúa como un contenedor para todos los botones de la calculadora. Su función es organizar y renderizar los componentes Boton.jsx en un diseño coherente de cuadrícula.

  • Boton.jsx: Representa un botón individual en la calculadora. Es un componente altamente reutilizable que recibe propiedades (props) para definir su texto (ej. "7", "+", "="), su color y la función que debe ejecutar al ser presionado.

Flujo de Datos y Lógica

El funcionamiento sigue el patrón de diseño común en React de "levantamiento del estado" (lifting state up):

  1. Estado Centralizado: Toda la lógica y los datos importantes (como el número actual, la operación pendiente y el resultado) residen en el estado del componente Calculadora.jsx.
  2. Paso de Datos (Props): Calculadora.jsx pasa los datos necesarios a los componentes hijos. Por ejemplo, pasa el valor a mostrar al componente Display.jsx.
  3. Actualización del Estado: Esta función, al ser llamada, actualiza el estado dentro de Calculadora.jsx con el nuevo valor o realiza la operación correspondiente.
  4. Re-renderizado: Al cambiar el estado de Calculadora.jsx, React vuelve a renderizar este componente y sus hijos. Como resultado, Display.jsx recibe el nuevo valor y lo muestra en pantalla, completando el ciclo.

Tecnologías y Conceptos Utilizados

Me apoyé en un conjunto de herramientas y librerías muy populares en el mundo del front-end:

  • React: El corazón de la aplicación. Toda la interfaz está construida con componentes reutilizables, como los botones (<Boton />) o la pantalla (<Display />). Fue mi primera inmersión real en el manejo del estado (useState) para la lógica de los cálculos.
  • Vite: Como entorno de desarrollo, es increíblemente rápido. Permite tener un servidor de desarrollo casi instantáneo y una experiencia de "hot-reloading" que agiliza muchísimo el trabajo.
  • Styled Components: Para el diseño, opté por esta librería de CSS-in-JS. Me encantó la idea de tener los estilos encapsulados dentro de cada componente, lo que hace que el código sea más ordenado y mantenible.
  • Bootstrap y React-Bootstrap: Utilicé Bootstrap para la estructura general y algunos componentes base, aprovechando su sistema de grids.
  • React Toastify: Para las notificaciones. Si intentas hacer una operación inválida (como dividir por cero), una pequeña notificación aparecerá para avisarte.
  • React Helmet: Una herramienta genial para gestionar el <head> de la página y los aspectos relacionados al SEO del sitio.

Y por supuesto, en modo oscuro. Detesto los sitios y/o apps quema retinas.

Calculadora en modo oscuro

Como siempre, el código está muy comentado. Les comparto los links al deploy y al repo en github.

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

domingo, 14 de septiembre de 2025

JsTimer - Mi Primer Proyecto con JavaScript: Creando un temporizador

Recientemente empecé a interiorizarme un poco con el mundo del front-end. Les comparto mi primer proyecto: un temporizador completamente funcional creado desde cero.

Al mismo tiempo hice un temporizador en Gambas3 que compartiré en algún momento, pues aún no está terminado. No sé que tengo con los temporizadores...


¿Qué hace el proyecto?

Es simple, pero efectivo. Podemos:

  • Establecer un tiempo: Puedes definir horas, minutos y segundos.
  • Iniciar la cuenta regresiva: Con solo un clic, el tiempo empieza a correr.
  • Ver el tiempo restante: Una pantalla digital muestra la cuenta regresiva.
  • Sonar una alarma: Cuando el tiempo llega a 00:00:00, una alarma suena para avisarte.
  • Detener la alarma: Un botón aparece para que puedas silenciar el sonido una vez que has escuchado el aviso.

No usé frameworks ni librerías complicadas, solo los tres pilares de la web:

  1. HTML: Fue mi punto de partida. Lo usé para dar estructura a la página: un título, los campos para introducir el tiempo (<input type="number">), los botones (<button>) y, por supuesto, el <h2> donde se muestra el tiempo. También incluí una etiqueta <audio> que, aunque invisible, es la responsable de reproducir el sonido de la alarma.

  2. CSS: Usé un fondo oscuro y semitransparente para el contenedor principal para que resaltara sobre una imagen de fondo. Con flexbox, centré todo perfectamente en la pantalla.

  3. JavaScript (Vanilla): El cerebro de toda la operación y la parte que más me interesa realmente. El diseño no es lo mío por lo que probablemente en futuros proyectos use bootstrap o algunos templates que encuentre en la web.

    • Manipulación del DOM: Usé document.getElementById() para "conectar" mi código JavaScript con los elementos de mi HTML (botones, inputs, etc.).
    • Eventos: Con addEventListener("click", ...), puse a los botones a "escuchar" los clics del usuario para que supieran cuándo ejecutar una función.
    • setInterval: Esta fue la clave de todo. setInterval(funcion, 1000) es una función de JavaScript que me permitió ejecutar el código para actualizar el temporizador cada segundo (1000 milisegundos).
    • Lógica de tiempo: Tuve que desempolvar las matemáticas para convertir el total de segundos a un formato de horas:minutos:segundos. El operador módulo (%) -> esencial.
    • Funciones Flecha: Adopté la sintaxis moderna de JavaScript usando funciones flecha () => {}.

Pueden ver el timer en funcionamiento en este link: https://jstimer-tna.netlify.app/

Crear este proyecto desde cero fue una buena experiencia para arrancar. Me enfrenté a pequeños desafíos, como asegurarme de que el formato del tiempo siempre tuviera dos dígitos (ej. 09 en vez de 9) o detener correctamente el intervalo del temporizador.

Si estás aprendiendo, te animo a que intentes construir algo similar. No tienes que hacerlo perfecto, solo tienes que empezar.

Como siempre, les dejo el link al repo de GitHub: https://github.com/mcattani/jsTimer.git

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

Invitame un café en cafecito.app.

Saludos!

sábado, 5 de abril de 2025

Info Clima - Script de Python (v2)

Versión actualizada del script publicado en esta entrada: Info Clima - Script de Python para ver la información del Servicio Meteorológico Nacional

Esta versión ya no usa la información del SMN dado que el sitio me estaba dando problemas, específicamente con la descarga del archivo zip.

Esta versión se utiliza la API de WeatherApi

captura script

Para el uso del script es necesario crear una cuenta gratuita en el sitio y obtener la API KEY para insertar luego en el código.

captura api key

Dado que sigo continuando mi aprendizaje sobre python y estaba leyendo sobre clases decidí reescribir el código utilizando esté método. Esto responde a una decisión completamente arbitraria aunque sí creo que el resultado es un código más ordenado o "limpio" si se quiere.

Las clases proveen una forma de empaquetar datos y funcionalidad juntos. Al crear una nueva clase, se crea un nuevo tipo de objeto, permitiendo crear nuevas instancias de ese tipo. Cada instancia de clase puede tener atributos adjuntos para mantener su estado. Las instancias de clase también pueden tener métodos (definidos por su clase) para modificar su estado. Fuente

Por el momento el script anterior será reemplazado por este, si en algún momento logro hacer funcionar el script anterior subiré ambas versiones al repo.

Nota: me olvidé de aclarar que la funcionalidad del script es exactamente igual, por un tema de comodidad si no se ingresa nada en el prompt obtiene los datos para 'Buenos Aires'. En este caso no hay que descargar un archivo ni descomprimirlo, simplemente solicitar los datos mediante la API. Lo cual introduce el uso de la librería json para convertir los datos recibidos en un diccionario.

Link al repo de GitHub: https://github.com/mcattani/script_clima

Espero les resulte de interés. Si es así los invito a dejar un comentario y si tienen ganas pueden invitarme un cafecito!:

Invitame un café en cafecito.app

Weather data by WeatherAPI.com