Fénix
AP-FEN-01 AP-FEN-02 AP-FEN-03 AP-FEN-04 AP-FEN-05 AP-FEN-06 AP-FEN-07 AP-FEN-08 AP-FEN-09 AP-FEN-10 AP-FEN-11 AP-FEN-12 AP-FEN-13 AP-FEN-14 AP-FEN-15 AP-FEN-16 AP-FEN-17 AP-FEN-18 AP-FEN-19 AP-FEN-20
Fénix · AP-FEN-01
ESP32: punto de acceso local y un LED
Objetivo
Vas a crear un punto de acceso con el ESP32 DevKit clónico, el que en el catálogo es placa-esp32, y a encender un LED desde el navegador de un teléfono unido solo a ese punto de acceso. La placa no se conecta a tu router, no pide ninguna cuenta y no tiene camino hacia fuera: quien no esté unido a TallerLocal no ve la página. La clave del acceso es una clave local del propio aparato, no un usuario de ningún servicio.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 ESP32 DevKit (clone) placa-esp32 genérico / clone poco (≈1–3 €) DevKit clónico. En el IDE elige el perfil de ESP32 Dev Module, no inventes una placa oficial. 1 LED 5 mm modulo-led genérico / clone irrisorio (<1 €) LED externo. No uses solo el LED de la placa, para ver el cable. 1 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 330 ohmios, porque el pin del ESP32 sale a 3,3 V.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
ESP32 GPIO4
USB del ordenador
ESP32 GND
Teléfono
Resistencia y LED hacia GND
Conector del DevKit
Cátodo del LED
Punto de acceso TallerLocal
ESP32 GPIO4 → Resistencia y LED hacia GNDEl pin fuente el LED. No pongas el LED directo a 3,3 V.
USB del ordenador → Conector del DevKitAlimentas la placa por USB. No inyectes 5 V en un GPIO.
ESP32 GND → Cátodo del LEDCierra el circuito del indicador.
Teléfono → Punto de acceso TallerLocalÚnete solo a ese nombre. No actives datos móviles para esta prueba: la página vive en la placa. La dirección habitual es 192.168.4.1, escríbela tal cual, sin prefijo.
Código
// AP-FEN-01 ESP32 punto de acceso local y un LED
// Pin: GPIO4 LED. Solo softAP, sin router y sin cuenta.
#include <WiFi.h>
#include <WebServer.h>
const int PIN_LED = 4;
const char* NOMBRE_AP = "TallerLocal";
const char* CLAVE_AP = "taller1234";
WebServer servicio(80);
void paginaRaiz() {
String cuerpo = "<html><body><h1>Taller local</h1><p>LED ";
cuerpo += digitalRead(PIN_LED) ? "encendido" : "apagado";
cuerpo += "</p><p><a href=\"/encender\">Encender</a></p>";
cuerpo += "<p><a href=\"/apagar\">Apagar</a></p></body></html>";
servicio.send(200, "text/html", cuerpo);
}
void setup() {
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, LOW);
Serial.begin(115200);
WiFi.mode(WIFI_AP);
WiFi.softAP(NOMBRE_AP, CLAVE_AP);
servicio.on("/", paginaRaiz);
servicio.on("/encender", []() {
digitalWrite(PIN_LED, HIGH);
paginaRaiz();
});
servicio.on("/apagar", []() {
digitalWrite(PIN_LED, LOW);
paginaRaiz();
});
servicio.begin();
Serial.println(F("AP local levantado. Sin ruta hacia fuera."));
Serial.println(WiFi.softAPIP());
}
void loop() {
servicio.handleClient();
}
Pasos
Elige en el IDE la tarjeta ESP32 Dev Module para este DevKit clónico. No busques un identificador de placa oficial: no hace falta. Conecta GPIO4 al LED con 330 ohmios hacia masa. La lógica del ESP32 es de 3,3 V. Sube el sketch por USB. En el monitor a 115200 baudios aparece la dirección del punto de acceso. En el teléfono, conéctate a TallerLocal con la clave del sketch. Olvida el Wi-Fi de casa durante la prueba. Escribe en el navegador la dirección impresa, normalmente 192.168.4.1, sin ponerle ningún prefijo delante. Pulsa encender y apagar. El LED debe seguir al enlace y la página debe decir el estado nuevo. Apaga el punto de acceso cortando el USB y comprueba que la página deja de existir. No había nada guardado fuera.
Variantes
Cambia el nombre del punto de acceso y la clave local, y vuelve a unir el teléfono. Añade una ruta /estado que solo devuelva encendido o apagado, sin la página completa. Haz que al arrancar el LED quede apagado aunque la última vez lo dejaras encendido: no hay memoria en esta ficha.
Depuración
Si el teléfono no ve TallerLocal, el sketch se subió a otra tarjeta o WiFi.mode no es el de punto de acceso. Si te unes y la página no carga, estás escribiendo la dirección del router de casa. Tiene que ser la que imprime softAPIP. Si la página carga y el LED no, GPIO4 no es el pin cableado o falta la resistencia a masa. Si pide una cuenta de algún servicio, no estás en este sketch: aquí solo existe la clave local del punto de acceso.
Software: wifi-ap digitalwrite serial
Fénix · AP-FEN-02
ESP32: página local de sensores, sin nube
Objetivo
Vas a publicar en el propio ESP32 una página con la temperatura de un DHT11 y la de un TMP36. El teléfono se une al punto de acceso de la placa y lee esa página. No hay nube, no hay cuenta y el punto de acceso no enruta hacia internet: si no estás unido a él, no hay medida que consultar. Los dos sensores se alimentan a 3,3 V, que es lo que admite el DevKit en sus entradas.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 ESP32 DevKit (clone) placa-esp32 genérico / clone poco (≈1–3 €) DevKit clónico, perfil ESP32 Dev Module. 1 DHT11 sensor-dht11 genérico / clone irrisorio (<1 €) Datos en GPIO16. Si tu módulo no trae resistencia de pull-up, añade 10 kiloohmios a 3,3 V. 1 TMP36 sensor-tmp36 ambos (oficial y genérico) poco (≈1–3 €) Salida a GPIO34, que es solo entrada y admite el analógico. Alimentación a 3,3 V, nunca a 5 V. 1 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 10 kiloohmios de pull-up en el DHT11 si el módulo no la incluye.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
ESP32 GPIO16
ESP32 GPIO34
ESP32 pin 3,3 V
ESP32 GND
Punto de acceso de la placa
Datos del DHT11
Salida del TMP36
VCC del DHT11 y del TMP36
GND de los dos sensores
Navegador del teléfono
ESP32 GPIO16 → Datos del DHT11Una sola línea. No la pongas en un pin de solo entrada.
ESP32 GPIO34 → Salida del TMP36GPIO34 es ADC y no sirve como salida. Perfecto para este sensor.
ESP32 pin 3,3 V → VCC del DHT11 y del TMP36No uses 5 V: el ADC del ESP32 no lo admite.
ESP32 GND → GND de los dos sensoresMasa común.
Punto de acceso de la placa → Navegador del teléfonoSin nube. La página solo existe dentro de TallerLocal. Dirección habitual 192.168.4.1, sin prefijo.
Código
// AP-FEN-02 ESP32 pagina local de sensores, sin nube
// Pines: GPIO16 DHT11, GPIO34 TMP36. Punto de acceso, sin router.
#include <WiFi.h>
#include <WebServer.h>
#include <DHT.h>
const int PIN_DHT = 16;
const int PIN_TMP = 34;
DHT dht(PIN_DHT, DHT11);
WebServer servicio(80);
float leerTmp36() {
int crudo = analogRead(PIN_TMP);
float voltios = crudo * (3.3f / 4095.0f);
return (voltios - 0.5f) * 100.0f;
}
void pagina() {
float td = dht.readTemperature();
float ta = leerTmp36();
String cuerpo = "<html><body><h1>Sensores locales</h1><p>DHT11: ";
cuerpo += String(td, 1);
cuerpo += " C</p><p>TMP36: ";
cuerpo += String(ta, 1);
cuerpo += " C</p><p>No sale de esta placa.</p></body></html>";
servicio.send(200, "text/html", cuerpo);
}
void setup() {
Serial.begin(115200);
analogReadResolution(12);
dht.begin();
WiFi.mode(WIFI_AP);
WiFi.softAP("TallerLocal", "taller1234");
servicio.on("/", pagina);
servicio.begin();
Serial.println(WiFi.softAPIP());
Serial.println(F("Sin nube y sin cuenta."));
}
void loop() {
servicio.handleClient();
}
Pasos
Alimenta DHT11 y TMP36 desde el pin de 3,3 V del DevKit. No les des 5 V. Conecta el dato del DHT11 a GPIO16 y la salida del TMP36 a GPIO34. Instala la librería DHT desde el gestor de librerías del IDE, en tu ordenador. Sube el sketch y únete al punto de acceso TallerLocal. No hace falta el Wi-Fi de casa. Abre la dirección que imprime el monitor, normalmente 192.168.4.1, escrita sola. Compara las dos temperaturas. Pueden diferir un grado: el DHT11 es burdo y el TMP36 es analógico. Sal del punto de acceso y comprueba que la página ya no abre. Las medidas no se fueron a ningún otro sitio.
Variantes
Muestra también la humedad del DHT11 en la misma página. Refresca sola la página con una espera en el navegador, sin salir de la red local. Si GPIO34 te da cero, mueve el TMP36 a GPIO35, que también es solo entrada, y cambia el número en el sketch.
Depuración
Si el DHT11 imprime nan, falta el pull-up o GPIO16 está suelto. Espera dos segundos entre lecturas, la librería ya lo espacia si no la llamas en bucle cerrado. Si el TMP36 da cifras imposibles, lo alimentaste a 5 V o usaste un pin que no es ADC. Si la página no carga estando unido al Wi-Fi de casa, es lo esperado: este servicio solo está en el punto de acceso. Si el ADC se queda fijo con el Wi-Fi encendido, no uses un pin del ADC2. GPIO34 pertenece al ADC1 y vale.
Software: wifi-ap analogread serial
Fénix · AP-FEN-03
ESP32: ajustes guardados y servicio solo en la LAN
Objetivo
Vas a guardar un umbral en la memoria de archivos del ESP32 y a servir una página solo dentro de la red de casa. La placa se une a tu router como un aparato más, escucha en la dirección local que le den y no abre ninguna conexión hacia fuera. No hay cuenta que crear. Si apagas la placa, el umbral sigue en el archivo. Quien esté fuera de tu LAN no tiene por dónde entrar, y no vas a abrir puertos en el router.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 ESP32 DevKit (clone) placa-esp32 genérico / clone poco (≈1–3 €) DevKit clónico en la LAN de casa, no en un servicio externo. 1 Pulsador modulo-pulsador genérico / clone irrisorio (<1 €) Sube el umbral guardado en una unidad y lo reescribe en el archivo. 1 LED 5 mm modulo-led genérico / clone irrisorio (<1 €) Se enciende si el valor de prueba supera el umbral guardado. 1 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 330 ohmios con el LED.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
ESP32 GPIO4
ESP32 GPIO17
USB
ESP32 GND
Router de casa
LED y resistencia a GND
Pulsador hacia GND
DevKit
LED y pulsador
ESP32 unido por la clave de tu Wi-Fi
ESP32 GPIO4 → LED y resistencia a GNDIndicador del umbral.
ESP32 GPIO17 → Pulsador hacia GNDCada pulsación suma uno al umbral y lo guarda.
USB → DevKitAlimentación de la placa mientras pruebas. Luego puede ser un cargador local, no un servicio.
ESP32 GND → LED y pulsadorMasa común.
Router de casa → ESP32 unido por la clave de tu Wi-FiEsa clave es la de tu LAN, no una cuenta. No abras puertos. La página solo responde en la dirección local que imprime el monitor.
Código
// AP-FEN-03 Ajustes guardados y servicio solo en la LAN
// Pines: GPIO4 LED, GPIO17 pulsador. Archivo local, sin salir de casa.
#include <WiFi.h>
#include <WebServer.h>
#include <LittleFS.h>
const int PIN_LED = 4;
const int PIN_BOTON = 17;
const char* NOMBRE_LAN = "MiCasa";
const char* CLAVE_LAN = "clave-de-casa";
int umbral = 20;
WebServer servicio(80);
void guardar() {
File f = LittleFS.open("/umbral.txt", "w");
if (!f) return;
f.println(umbral);
f.close();
}
void cargar() {
File f = LittleFS.open("/umbral.txt", "r");
if (!f) return;
umbral = f.parseInt();
f.close();
if (umbral < 0 || umbral > 100) umbral = 20;
}
void pagina() {
String cuerpo = "<html><body><h1>Solo LAN</h1><p>Umbral ";
cuerpo += umbral;
cuerpo += "</p><p>No hay conexion hacia fuera.</p></body></html>";
servicio.send(200, "text/html", cuerpo);
}
void setup() {
pinMode(PIN_LED, OUTPUT);
pinMode(PIN_BOTON, INPUT_PULLUP);
Serial.begin(115200);
if (!LittleFS.begin(true)) {
Serial.println(F("No se pudo montar el archivo."));
}
cargar();
WiFi.mode(WIFI_STA);
WiFi.begin(NOMBRE_LAN, CLAVE_LAN);
while (WiFi.status() != WL_CONNECTED) delay(300);
servicio.on("/", pagina);
servicio.begin();
Serial.println(WiFi.localIP());
Serial.println(F("Escuchando solo en la LAN. No abras el router."));
}
void loop() {
servicio.handleClient();
if (digitalRead(PIN_BOTON) == LOW) {
delay(30);
if (digitalRead(PIN_BOTON) == LOW) {
umbral += 1;
if (umbral > 100) umbral = 0;
guardar();
while (digitalRead(PIN_BOTON) == LOW) delay(10);
}
}
int prueba = 25;
digitalWrite(PIN_LED, prueba >= umbral ? HIGH : LOW);
}
Pasos
Sustituye NOMBRE_LAN y CLAVE_LAN por los de tu Wi-Fi de casa. Esa clave se queda en el sketch, no abre ninguna cuenta. Conecta el LED a GPIO4 y el pulsador a GPIO17 contra masa. Sube el sketch. Espera a que el monitor imprima una dirección de tu rango, parecida a 192.168.1.algo. Desde un ordenador unido a la misma red, abre esa dirección sola, sin prefijo. Tienes que ver el umbral. Pulsa el botón. El número sube y, si reinicias la placa, sigue igual porque está en el archivo umbral.txt. Prueba desde el móvil con los datos móviles apagados y el Wi-Fi de casa encendido. Tiene que ir. Con el Wi-Fi apagado, no. No entres al router a reenviar puertos. Si lo hicieras, romperías la condición de esta ficha.
Variantes
Guarda también un nombre corto de la sala en el mismo archivo, en una segunda línea. Haz que el botón baje el umbral si lo mantienes más de un segundo. Si LittleFS.begin falla, formatea solo una vez con el parámetro true, que este sketch ya usa, y vuelve a cargar.
Depuración
Si no imprime dirección, el nombre o la clave de la LAN están mal, o el DevKit está lejos del router. Si la página no abre desde el móvil con datos activos, el teléfono se está yendo por otra red. Apaga los datos. Si el umbral vuelve a 20 tras cada reset, el archivo no se escribió. Mira el valor de retorno de open. Si alguien de fuera de casa puede entrar, has abierto un puerto. Ciérralo: este servicio no está pensado para eso.
Software: fs digitalread digitalwrite serial
Fénix · AP-FEN-04
MQTT contra un broker de la red local
Objetivo
Vas a publicar y a escuchar un tema MQTT usando la librería PubSubClient, contra un broker que corre en una Raspberry de tu propia LAN. El ESP32 se une al Wi-Fi de casa y habla con esa Raspberry por el puerto 1883. No uses un broker público ni una cuenta de un proveedor: si la Raspberry está apagada, el ensayo se para, y eso es justamente la prueba de que no hay un respaldo fuera. El mensaje enciende o apaga un LED.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 ESP32 DevKit (clone) placa-esp32 genérico / clone poco (≈1–3 €) Cliente MQTT. El broker no es esta placa: es una Raspberry en la misma red. 1 LED 5 mm modulo-led genérico / clone irrisorio (<1 €) Obedece al tema taller/led. 1 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 330 ohmios.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
ESP32 GPIO4
USB
ESP32 GND
Raspberry de la LAN en el puerto 1883
LED y resistencia a GND
DevKit
Cátodo del LED
ESP32 como cliente
ESP32 GPIO4 → LED y resistencia a GNDSe enciende con el mensaje 1 y se apaga con el 0.
USB → DevKitAlimentación local.
ESP32 GND → Cátodo del LEDMasa del indicador.
Raspberry de la LAN en el puerto 1883 → ESP32 como clienteBroker en tu red, por ejemplo 192.168.1.50. No pongas un nombre de un servicio público. Sin cuenta.
Código
// AP-FEN-04 MQTT contra un broker de la red local
// Pin: GPIO4 LED. Libreria PubSubClient. Broker en la LAN, puerto 1883.
#include <WiFi.h>
#include <PubSubClient.h>
const int PIN_LED = 4;
const char* NOMBRE_LAN = "MiCasa";
const char* CLAVE_LAN = "clave-de-casa";
IPAddress broker(192, 168, 1, 50);
WiFiClient enlace;
PubSubClient cliente(enlace);
void alLlegar(char* tema, byte* carga, unsigned int largo) {
if (largo < 1) return;
if (carga[0] == '1') digitalWrite(PIN_LED, HIGH);
if (carga[0] == '0') digitalWrite(PIN_LED, LOW);
Serial.print(F("tema "));
Serial.println(tema);
}
void setup() {
pinMode(PIN_LED, OUTPUT);
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(NOMBRE_LAN, CLAVE_LAN);
while (WiFi.status() != WL_CONNECTED) delay(300);
cliente.setServer(broker, 1883);
cliente.setCallback(alLlegar);
Serial.println(WiFi.localIP());
}
void reconectar() {
while (!cliente.connected()) {
if (cliente.connect("esp32-taller")) {
cliente.subscribe("taller/led");
cliente.publish("taller/estado", "vivo");
} else {
delay(1000);
}
}
}
void loop() {
if (!cliente.connected()) reconectar();
cliente.loop();
}
Pasos
En la Raspberry de tu LAN, arranca un broker MQTT escuchando solo en esa red, puerto 1883. No lo publiques en internet. Pon en el sketch la dirección real de esa Raspberry. Instala PubSubClient desde el gestor de librerías. Sustituye el nombre y la clave por los de tu Wi-Fi de casa y conecta el LED a GPIO4. Sube el sketch. En el monitor tiene que aparecer la dirección local del ESP32 y, al conectar, no un error repetido. Desde la Raspberry, publica en el tema taller/led el carácter 1 y luego el 0. El LED debe obedecer. Apaga la Raspberry. El cliente debe quedarse reintentando y el LED no tiene que cambiar por un mensaje que ya no puede llegar. No crees usuario en un proveedor externo. Si el ensayo solo funciona con uno de esos, no es esta ficha.
Variantes
Publica la palabra vivo cada diez segundos en taller/estado para ver el enlace sin tocar el LED. Suscríbete a un segundo tema que ajuste un número, y rechaza cualquier carga que no sea un dígito. Cambia el identificador de cliente si tienes dos placas, para que el broker no las confunda.
Depuración
Si connect falla siempre, la dirección del broker no es la de tu LAN o el puerto 1883 está cerrado en la Raspberry. Si conecta y el LED no cambia, el tema no coincide: tiene que ser exactamente taller/led. Si la Raspberry no está en casa y aún así conecta, estás apuntando a un broker de fuera. Cámbialo. Si el Wi-Fi no une, la clave de casa está mal. Eso no es un fallo del MQTT.
Software: pubsubclient digitalwrite serial
Fénix · AP-FEN-05
Actualizar el programa desde la LAN
Objetivo
Vas a actualizar el programa del ESP32 desde el IDE, por la red de casa, con ArduinoOTA. La placa se une a tu LAN y escucha ahí la subida. No hay un servidor fuera ni una cuenta: si tu ordenador no está en la misma red, la actualización no tiene que funcionar. Tampoco vas a abrir el router. El LED cambia de ritmo entre una versión y la siguiente para que veas que entró el programa nuevo y no el de siempre.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 ESP32 DevKit (clone) placa-esp32 genérico / clone poco (≈1–3 €) DevKit clónico. La actualización sale del IDE de tu ordenador en la misma LAN. 1 LED 5 mm modulo-led genérico / clone irrisorio (<1 €) Late a un ritmo que cambiarás a propósito entre versiones. 1 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 330 ohmios.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
ESP32 GPIO4
USB
ESP32 GND
Ordenador en la misma LAN
LED y resistencia a GND
DevKit
Cátodo del LED
Puerto de red que ofrece la placa
ESP32 GPIO4 → LED y resistencia a GNDTestigo de la versión que está corriendo.
USB → DevKitHace falta la primera vez, para meter este sketch. Las siguientes pueden ir por la LAN.
ESP32 GND → Cátodo del LEDMasa del LED.
Ordenador en la misma LAN → Puerto de red que ofrece la placaArduinoOTA solo desde tu red. Si no ves el puerto, no estás en la misma LAN. No abras puertos en el router.
Código
// AP-FEN-05 Actualizar el programa desde la LAN
// Pin: GPIO4 LED. ArduinoOTA, solo red local. Version de parpadeo 1.
#include <WiFi.h>
#include <ArduinoOTA.h>
const int PIN_LED = 4;
const char* NOMBRE_LAN = "MiCasa";
const char* CLAVE_LAN = "clave-de-casa";
const unsigned long RITMO_MS = 400;
unsigned long anterior = 0;
bool alto = false;
void setup() {
pinMode(PIN_LED, OUTPUT);
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(NOMBRE_LAN, CLAVE_LAN);
while (WiFi.status() != WL_CONNECTED) delay(300);
ArduinoOTA.setHostname("taller-local");
ArduinoOTA.setPassword("clave-local");
ArduinoOTA.onStart([]() { digitalWrite(PIN_LED, LOW); });
ArduinoOTA.onError([](ota_error_t e) {
Serial.print(F("Fallo OTA "));
Serial.println((int)e);
});
ArduinoOTA.begin();
Serial.println(WiFi.localIP());
Serial.println(F("Listo para recibir el programa solo desde la LAN."));
}
void loop() {
ArduinoOTA.handle();
if (millis() - anterior >= RITMO_MS) {
anterior = millis();
alto = !alto;
digitalWrite(PIN_LED, alto ? HIGH : LOW);
}
}
Pasos
Rellena el nombre y la clave de tu Wi-Fi de casa. La clave de ArduinoOTA es otra, local, y solo protege la subida. Conecta el LED a GPIO4, sube este sketch una primera vez por USB y abre el monitor a 115200 baudios. Comprueba que imprime una dirección de tu LAN y que el LED late cada 400 ms. Cambia RITMO_MS a 100, guarda, y en el IDE elige el puerto de red de taller-local, no el USB. Sube otra vez. Te pedirá la clave local del sketch. Al terminar, el LED tiene que latir más rápido, ya sin el cable de datos. Prueba a subir con el ordenador unido a otra red. No debe aparecer el puerto. Así sabes que no está expuesto. No reenvíes puertos en el router. Esta vía es solo para quien ya está en casa.
Variantes
Cambia el nombre de host si tienes más de un DevKit, para no subir al aparato equivocado. En onStart apaga también cualquier salida de potencia que más adelante añadas, antes de que empiece la escritura. Quita la clave local solo en un ensayo de mesa y vuelve a ponerla: sin ella cualquiera de tu LAN podría reprogramar la placa.
Depuración
Si no aparece el puerto de red, la placa y el ordenador no comparten LAN, o el sketch no llegó a ArduinoOTA.begin. Si la subida falla a mitad, no cortes la alimentación. Espera el reinicio y, si hace falta, recupera por USB. Si el ritmo no cambia, subiste al puerto USB el programa viejo o no guardaste RITMO_MS. Si un ordenador fuera de casa ve el puerto, has abierto el router. Ciérralo.
Software: ota-local millis digitalwrite serial
Fénix · AP-FEN-06
Muchos pulsadores con 74HC165
Objetivo
Vas a leer ocho pulsadores con un solo 74HC165, que convierte esas entradas en un chorro de bits. La placa solo gasta tres pines: carga, reloj y dato. Así aprendes a no consumir un pin por tecla cuando el panel crece. Cada pulsador va a masa y el chip te devuelve un uno o un cero según esté suelto o apretado. El monitor enseña el byte completo para que veas la posición de cada tecla.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 Arduino Uno R3 placa-uno-r3 original / oficial consultar (>10 €) Usa esta o el clon. 1 Uno R3 clone CH340 placa-uno-ch340 genérico / clone poco (≈1–3 €) Mismos tres pines de control. 1 74HC165 modulo-74hc165 genérico / clone irrisorio (<1 €) Registro de entrada paralelo a serie. Ocho canales. 8 Pulsador modulo-pulsador genérico / clone irrisorio (<1 €) Uno por entrada. El otro extremo de cada uno a masa. 8 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 10 kiloohmios de pull-up en cada entrada si el módulo no los trae.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
Uno D8
Uno D9
Uno D10
Uno 5 V
Uno GND
Patilla de carga PL del 74HC165
Reloj CP del 74HC165
Salida serie Q7 del 74HC165
VCC del 74HC165
GND del chip y de los ocho pulsadores
Uno D8 → Patilla de carga PL del 74HC165Un pulso bajo copia los pulsadores al registro.
Uno D9 → Reloj CP del 74HC165Cada flanco saca el siguiente bit.
Uno D10 → Salida serie Q7 del 74HC165Aquí lees el bit, no escribas en esta patilla.
Uno 5 V → VCC del 74HC165Misma lógica que el Uno.
Uno GND → GND del chip y de los ocho pulsadoresMasa común. Sin ella los bits flotan.
Código
// AP-FEN-06 Muchos pulsadores con 74HC165
// Pines: D8 carga, D9 reloj, D10 dato Q7
const int PIN_CARGA = 8;
const int PIN_RELOJ = 9;
const int PIN_DATO = 10;
uint8_t leerTeclas() {
digitalWrite(PIN_CARGA, LOW);
delayMicroseconds(5);
digitalWrite(PIN_CARGA, HIGH);
uint8_t bits = 0;
for (int i = 0; i < 8; i++) {
bits <<= 1;
if (digitalRead(PIN_DATO)) bits |= 1;
digitalWrite(PIN_RELOJ, HIGH);
delayMicroseconds(2);
digitalWrite(PIN_RELOJ, LOW);
}
return bits;
}
void setup() {
pinMode(PIN_CARGA, OUTPUT);
pinMode(PIN_RELOJ, OUTPUT);
pinMode(PIN_DATO, INPUT);
digitalWrite(PIN_CARGA, HIGH);
digitalWrite(PIN_RELOJ, LOW);
Serial.begin(9600);
Serial.println(F("Ocho teclas en un byte."));
}
void loop() {
uint8_t teclas = leerTeclas();
Serial.println(teclas, BIN);
delay(150);
}
Pasos
Coloca el 74HC165 y une D8 a la carga, D9 al reloj y D10 a Q7. Pon un pulsador en cada entrada paralela, hacia masa, y un pull-up de 10 kiloohmios a 5 V en cada una si no viene en la placa. Alimenta el chip a 5 V y comparte la masa con la placa y con los pulsadores. Sube el sketch y abre el monitor a 9600 baudios. Sin pulsar nada tienes que ver unos estables. Aprieta una tecla. Un bit pasa a cero. Anota cuál, porque el orden físico depende de cómo hayas cableado A a H. Aprieta dos a la vez y comprueba que se ven los dos ceros en el mismo byte. Si un canal no cambia nunca, ese pull-up o ese pulsador está suelto. No es el reloj, que es común.
Variantes
Encadena un segundo 74HC165 y lee 16 bits con los mismos tres pines. Traduce el byte a un número de tecla, de 0 a 7, cuando solo hay una pulsada. Ignora una tecla hasta que lleve 20 ms en el mismo nivel, para no contar rebotes.
Depuración
Si el byte es siempre cero, la carga no vuelve a alto o Q7 está a masa. Si los bits bailan solos, falta el pull-up de esa entrada. Si el orden sale invertido, recorre el bucle al revés o acepta el orden y documéntalo. Si al pulsar cambian varios bits, dos entradas están en corto.
Software: digitalread digitalwrite serial
Fénix · AP-FEN-07
Dieciséis LEDs con dos 74HC595
Objetivo
Vas a encender dieciséis LED con dos 74HC595 encadenados, usando solo tres pines del Uno: dato, reloj y cerrojo. El primer byte que sale acaba en el segundo chip, y el segundo byte se queda en el primero. Verás un bit que recorre las dieciséis luces sin dedicar un pin a cada una. Cada LED lleva su resistencia. No alimentes los LED desde las patillas del chip sin límite de corriente.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 Arduino Uno R3 placa-uno-r3 original / oficial consultar (>10 €) Usa esta o el clon. 1 Uno R3 clone CH340 placa-uno-ch340 genérico / clone poco (≈1–3 €) Mismos pines. 2 74HC595 modulo-74hc595 genérico / clone irrisorio (<1 €) El QH prima del primero entra al SER del segundo. 16 LED 5 mm modulo-led genérico / clone irrisorio (<1 €) Una luz por salida Q0 a Q7 de cada chip. 16 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 220 ohmios, una en serie con cada LED.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
Uno D11
Uno D12
Uno D8
Uno 5 V
Uno GND
SER del primer 74HC595
SRCLK de los dos 74HC595
RCLK de los dos 74HC595
VCC de los dos chips
GND de los chips y cátodos de los LED
Uno D11 → SER del primer 74HC595Dato serie. El segundo chip no recibe este cable: recibe QH prima del primero.
Uno D12 → SRCLK de los dos 74HC595Reloj compartido.
Uno D8 → RCLK de los dos 74HC595Cerrojo. Hasta que no sube, los LED no cambian.
Uno 5 V → VCC de los dos chipsLógica a 5 V. Los LED salen de las patillas Q, con resistencia.
Uno GND → GND de los chips y cátodos de los LEDMasa común.
Código
// AP-FEN-07 Dieciseis LEDs con dos 74HC595
// Pines: D11 SER, D12 SRCLK, D8 RCLK
const int PIN_SER = 11;
const int PIN_CLK = 12;
const int PIN_LATCH = 8;
void escribir16(uint16_t valor) {
digitalWrite(PIN_LATCH, LOW);
shiftOut(PIN_SER, PIN_CLK, MSBFIRST, (valor >> 8) & 0xFF);
shiftOut(PIN_SER, PIN_CLK, MSBFIRST, valor & 0xFF);
digitalWrite(PIN_LATCH, HIGH);
}
void setup() {
pinMode(PIN_SER, OUTPUT);
pinMode(PIN_CLK, OUTPUT);
pinMode(PIN_LATCH, OUTPUT);
escribir16(0);
Serial.begin(9600);
Serial.println(F("Recorrido de un bit por 16 LED."));
}
void loop() {
for (int i = 0; i < 16; i++) {
escribir16((uint16_t)1 << i);
Serial.println(i);
delay(120);
}
}
Pasos
Encadena los dos 74HC595: QH prima del primero al SER del segundo. Reloj y cerrojo van en paralelo a los dos. Conecta D11 al SER del primero, D12 a los dos relojes y D8 a los dos cerrojos. Pon en cada salida Q un LED y una resistencia de 220 ohmios hacia masa. No juntes los LED sin resistencia. Sube el sketch. Una sola luz debe recorrer las dieciséis posiciones. Si el recorrido salta del primer chip al segundo a destiempo, el orden de los dos shiftOut está bien y el cable QH prima es el que hay que revisar. Para en un valor fijo, por ejemplo 0x00FF, y cuenta si se encienden ocho luces seguidas. Toca un chip tras un minuto con varios LED encendidos. Si quema, estás pidiendo demasiada corriente por salida.
Variantes
Enciende solo los pares, con la máscara 0xAAAA, para ver el byte de un vistazo. Invierte el sentido del recorrido restando el índice a 15. Añade un tercer chip más adelante: el mismo esquema vale, con otro byte.
Depuración
Si no luce nada, el cerrojo D8 no llega o los cátodos no están a masa. Si lucen todas flojas, falta la resistencia y el chip está limitando mal. Páralo y pon las 220 ohmios. Si solo funciona un chip, QH prima no entra en el SER del segundo. Si el orden está volteado, cambias MSBFIRST por LSBFIRST y lo dejas escrito en el comentario del sketch.
Software: digitalwrite serial
Fénix · AP-FEN-08
Señal periódica precisa con timer
Objetivo
Vas a generar una onda cuadrada de 1 Hz en el pin D9 sin usar millis ni delay para conmutarla. Lo hace el timer 1 del Uno, en modo CTC, alternando la salida OC1A cada vez que el contador llega al tope. Con reloj de 16 MHz y preescaler 256, el comparador se carga a 31249 y la pata cambia sola. El bucle puede hacer otra cosa: la frecuencia no depende de lo que tardes en el loop. Eso es un timer de verdad, no una espera disfrazada.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 Arduino Uno R3 placa-uno-r3 original / oficial consultar (>10 €) ATmega328P a 16 MHz. El cálculo es para ese reloj. 1 Uno R3 clone CH340 placa-uno-ch340 genérico / clone poco (≈1–3 €) Mismo micro y mismo D9 como OC1A. 1 LED 5 mm modulo-led genérico / clone irrisorio (<1 €) Para ver el hercio. Un ciclo completo es un segundo. 1 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 220 ohmios.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
Uno D9
Uno 5 V
Uno GND
Timer 1
LED y resistencia a GND
Placa, por USB
Cátodo del LED
Pin OC1A
Uno D9 → LED y resistencia a GNDD9 es OC1A. No elijas otro pin si quieres que el timer lo mueva solo.
Uno 5 V → Placa, por USBEl LED lo alimenta el pin, no este 5 V directo.
Uno GND → Cátodo del LEDMasa del indicador.
Timer 1 → Pin OC1ANo llames a analogWrite en D9 mientras este timer esté configurado: se pisan.
Código
// AP-FEN-08 Senal periodica precisa con timer
// Pin: D9 es OC1A. Onda de 1 Hz por hardware.
// 16 MHz / 256 = 62500 cuentas por segundo.
// Conmutar la pata exige dos coincidencias por ciclo: 62500 / 2 - 1 = 31249.
const int PIN_OC1A = 9;
void setup() {
pinMode(PIN_OC1A, OUTPUT);
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 31249;
TCCR1A |= (1 << COM1A0);
TCCR1B |= (1 << WGM12);
TCCR1B |= (1 << CS12);
Serial.begin(9600);
Serial.println(F("1 Hz en D9. El loop no conmuta el pin."));
}
void loop() {
Serial.println(TCNT1);
delay(200);
}
Pasos
Conecta el LED a D9 con su resistencia. Tiene que ser D9, la salida OC1A del timer 1. Sube el sketch a un Uno a 16 MHz. Si la placa fuera de otro reloj, el hercio no saldría. Mira el LED: un segundo encendido y un segundo apagado, de forma estable. Abre el monitor a 9600 baudios. Verás el contador del timer moverse aunque el LED lo lleve el hardware. Añade un delay largo dentro del loop, de dos segundos. El LED no debe cambiar de ritmo: no lo estás conmutando tú. No uses analogWrite en D9 en este sketch. Ese uso reprograma el mismo timer. Si más adelante quieres 10 Hz, recalcula OCR1A y no toques el LED a mano.
Variantes
Calcula OCR1A para 10 Hz y comprueba con el móvil, grabando un vídeo corto, que el parpadeo se acelera. Pasa el preescaler a 1024 y ajusta el comparador para seguir en 1 Hz. Deja el timer y usa el loop solo para contar vueltas de otra tarea, demostrando que no se estorban.
Depuración
Si el LED no late, D9 no es el pin cableado o COM1A0 no quedó puesto. Si late el doble de rápido, olvidaste que la pata conmuta dos veces por ciclo y el OCR está a la mitad. Si el ritmo cambia al abrir el monitor, estás en una placa que no va a 16 MHz. Si analogWrite de otra prueba te dejó el pin mudo, vuelve a cargar este setup completo.
Software: timers serial
Fénix · AP-FEN-09
Arduino esclavo I2C de una Raspberry
Objetivo
Vas a dejar el Uno como esclavo I2C en la dirección 0x12 para que una Raspberry de tu mesa le mande bytes. La placa registra Wire.onReceive y, cada vez que llega un byte, lo guarda y enciende o apaga un LED. El Uno no inicia la conversación: espera. Como la Raspberry habla a 3,3 V y el Uno a 5 V, las dos líneas pasan por un level shifter. No hay red de por medio, solo el bus de la mesa.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 Arduino Uno R3 placa-uno-r3 original / oficial consultar (>10 €) Este esclavo es un Uno. Usa esta o el clon, no un ESP32. 1 Uno R3 clone CH340 placa-uno-ch340 genérico / clone poco (≈1–3 €) También vale como esclavo. Misma dirección 0x12. 1 Level shifter modulo-level-shifter genérico / clone irrisorio (<1 €) Imprescindible: la Raspberry no admite 5 V en SDA ni en SCL. 1 LED 5 mm modulo-led genérico / clone irrisorio (<1 €) Obedece al byte recibido. 1 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 220 ohmios.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
Uno A4
Uno A5
Uno D8
Uno 5 V
Uno GND
Raspberry
Lado de 5 V del shifter, canal SDA
Lado de 5 V del shifter, canal SCL
LED y resistencia a GND
Alimentación alta del shifter
GND del shifter y de la Raspberry
Maestra del bus
Uno A4 → Lado de 5 V del shifter, canal SDAEl lado de 3,3 V de ese canal va al SDA de la Raspberry.
Uno A5 → Lado de 5 V del shifter, canal SCLEl lado de 3,3 V va al SCL de la Raspberry.
Uno D8 → LED y resistencia a GNDSe enciende si el byte recibido es 1.
Uno 5 V → Alimentación alta del shifterReferencia de 5 V. La Raspberry pone la referencia de 3,3 V en el otro lado.
Uno GND → GND del shifter y de la RaspberrySin masa común el bus no existe.
Raspberry → Maestra del busLa Pi no es una placa del catálogo de este montaje. Tú la programas aparte para escribir en la dirección 0x12. No pongas 5 V en sus pines.
Código
// AP-FEN-09 Arduino esclavo I2C de una Raspberry
// Pines: A4 SDA, A5 SCL, D8 LED. Direccion 0x12.
// La recepcion entra por Wire.onReceive. El Uno no llama al maestro.
#include <Wire.h>
const int PIN_SDA = A4;
const int PIN_SCL = A5;
const int PIN_LED = 8;
const byte DIRECCION = 0x12;
volatile byte ultimo = 0;
volatile bool nuevo = false;
void alRecibir(int cuantos) {
while (Wire.available()) {
ultimo = Wire.read();
nuevo = true;
}
(void)cuantos;
}
void setup() {
pinMode(PIN_LED, OUTPUT);
digitalWrite(PIN_LED, LOW);
Wire.begin(DIRECCION);
Wire.onReceive(alRecibir);
Serial.begin(9600);
Serial.println(F("Esclavo 0x12 esperando."));
}
void loop() {
if (!nuevo) return;
noInterrupts();
byte valor = ultimo;
nuevo = false;
interrupts();
digitalWrite(PIN_LED, valor == '1' ? HIGH : LOW);
Serial.print(F("byte "));
Serial.println(valor, HEX);
}
Pasos
Monta el level shifter entre el Uno y la Raspberry. Lado alto a A4 y A5, lado bajo a SDA y SCL de la Pi. Une las masas. Alimenta el lado alto con 5 V del Uno y el lado bajo con 3,3 V de la Pi. No cruces esas alimentaciones. Conecta el LED a D8. Sube este sketch al Uno. La dirección queda en 0x12. En la Raspberry, con el bus ya habilitado en su sistema, escribe un byte a esa dirección. Un 49, que es el carácter 1, enciende el LED. Manda un 48, el carácter 0, y el LED se apaga. El monitor del Uno debe enseñar el hexadecimal. Desconecta SDA. La Pi tiene que fallar la escritura y el LED no debe cambiar: la orden no llegó. No pongas el esclavo en un ESP32 en esta ficha. El que espera en onReceive es el Uno.
Variantes
Acepta un segundo byte como brillo y limítalo, aunque el LED de esta ficha sea solo de encendido. Responde con Wire.onRequest el último byte, para que la Pi pueda leer lo que el Uno recuerda. Cambia la dirección a 0x22 si 0x12 ya está ocupada en tu mesa, y cámbiala también en la Pi.
Depuración
Si la Pi no ve el 0x12, el shifter está al revés o no hay masa común. Si el LED no cambia y el serie sí imprime, D8 está suelto. Si el Uno se reinicia al conectar el bus, metiste 5 V en la Pi o alimentaste el shifter al revés. Para y revisa. Si onReceive no entra, el sketch se subió sin Wire.begin con dirección, y el Uno quedó como maestro.
Software: i2c digitalwrite serial
Fénix · AP-FEN-10
Protocolo propio por serie con una Pi
Objetivo
Vas a definir una trama corta entre el Uno y una Raspberry, o el monitor serie si todavía no tienes la Pi cableada. La trama empieza por el byte 0xAA, sigue con la longitud, los datos y un byte de comprobación que es la suma exclusiva de los datos. Un autómata de estados descarta basura hasta ver la cabecera, y solo entonces acepta el mensaje. Así no te crees un dato a medias cuando el cable se mueve.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 Arduino Uno R3 placa-uno-r3 original / oficial consultar (>10 €) Usa esta o el clon. El enlace serie del USB sirve para hablar con la Pi o con el monitor. 1 Uno R3 clone CH340 placa-uno-ch340 genérico / clone poco (≈1–3 €) Misma UART en D0 y D1 a través del puente USB. 1 LED 5 mm modulo-led genérico / clone irrisorio (<1 €) Se enciende si el primer dato de una trama válida es 1. 1 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 220 ohmios.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
Uno D0
Uno D1
Uno D8
USB
Uno GND
Raspberry o monitor
Recepción de la trama
Envío de la respuesta
LED y resistencia a GND
Uno
GND del LED y, si vas por pines, de la Pi
Trama que empieza por 0xAA
Uno D0 → Recepción de la tramaRX. Por el USB lo usa el puente serie. No lo ocupes con otro cable mientras programas.
Uno D1 → Envío de la respuestaTX hacia la Pi o hacia el monitor.
Uno D8 → LED y resistencia a GNDIndicador de trama aceptada.
USB → UnoAlimentación y, a la vez, camino serie con el ordenador o la Pi.
Uno GND → GND del LED y, si vas por pines, de la PiSi el enlace es el propio USB, la masa ya viaja por el cable.
Raspberry o monitor → Trama que empieza por 0xAANo envíes texto suelto si quieres ver el LED. Hace falta la cabecera, la longitud y la suma.
Código
// AP-FEN-10 Protocolo propio por serie con una Pi
// Pines: D0 RX, D1 TX, D8 LED
// Trama: 0xAA, longitud, datos, XOR de los datos.
const int PIN_RX = 0;
const int PIN_TX = 1;
const int PIN_LED = 8;
const byte CABECERA = 0xAA;
enum Fase { ESPERA, LONGITUD, DATOS, SUMA };
Fase fase = ESPERA;
byte esperado = 0;
byte leidos = 0;
byte acumulado = 0;
byte datos[8];
void setup() {
pinMode(PIN_LED, OUTPUT);
Serial.begin(9600);
Serial.println(F("Esperando trama AA."));
}
void loop() {
if (!Serial.available()) return;
byte b = Serial.read();
if (fase == ESPERA) {
if (b == CABECERA) fase = LONGITUD;
return;
}
if (fase == LONGITUD) {
if (b == 0 || b > 8) { fase = ESPERA; return; }
esperado = b;
leidos = 0;
acumulado = 0;
fase = DATOS;
return;
}
if (fase == DATOS) {
datos[leidos++] = b;
acumulado ^= b;
if (leidos >= esperado) fase = SUMA;
return;
}
if (b == acumulado) {
digitalWrite(PIN_LED, datos[0] == '1' ? HIGH : LOW);
Serial.println(F("trama ok"));
} else {
Serial.println(F("suma mala"));
digitalWrite(PIN_LED, LOW);
}
fase = ESPERA;
}
Pasos
Deja D0 y D1 al USB. Conecta solo el LED a D8. Programar y hablar por el mismo puerto obliga a no tener otro cable en RX. Sube el sketch y abre el monitor a 9600 baudios, o abre la UART en la Raspberry si el Uno cuelga de ella por USB. Para encender el LED manda, en hexadecimal, AA 01 31 31. El 31 es el carácter 1, y la suma exclusiva de un solo byte es el propio byte. El monitor debe responder trama ok y el LED encenderse. Cambia el último byte a propósito. Tiene que decir suma mala y apagar el LED. Manda basura antes de AA. El autómata se queda en espera y no se descuadra el LED. Cuando pases a pines sueltos hacia la Pi, cruza TX con RX y une masas, y no alimentes la Pi desde el pin de 5 V del Uno.
Variantes
Acepta dos bytes de datos y usa el segundo como un contador que imprimes. Responde con otra trama AA para que la Pi compruebe el camino de vuelta. Tras tres sumas malas seguidas, apaga el LED y exige una cabecera nueva.
Depuración
Si nunca dice trama ok, el monitor está enviando el texto AA como letras, no como bytes. Usa un terminal que pueda mandar hexadecimal. Si se descuadra para siempre, la longitud se te fue de rango y no volviste a ESPERA. Este sketch sí vuelve. Si el LED no cambia con trama ok, D8 no es el pin del LED. Si al programar falla la subida, tienes un cable ocupando D0. Quítalo antes de grabar.
Software: serial maquina-estados digitalwrite
Fénix · AP-FEN-11
Media móvil y mediana en un sensor ruidoso
Objetivo
Vas a leer un TMP36 ruidoso y a mostrar a la vez la media de las últimas nueve muestras y su mediana. La media se mueve cuando entra un pico, porque todos los valores suman. La mediana, al quedar en el centro de la lista ordenada, aguanta mejor un valor loco. Un pulsador mete a propósito un pico falso para que veas la diferencia en el monitor, sin tocar el sensor. Las dos cifras salen de la misma ventana de muestras.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 Arduino Uno R3 placa-uno-r3 original / oficial consultar (>10 €) Usa esta o el clon. 1 Uno R3 clone CH340 placa-uno-ch340 genérico / clone poco (≈1–3 €) Misma entrada analógica. 1 TMP36 sensor-tmp36 ambos (oficial y genérico) poco (≈1–3 €) Salida a A0. Alimentación a 5 V en el Uno. 1 Pulsador modulo-pulsador genérico / clone irrisorio (<1 €) Inyecta un pico de prueba en la ventana, no en el sensor.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
Salida del TMP36
Uno 5 V
Uno GND
Uno D2
Uno A0
VCC del TMP36
GND del TMP36 y del pulsador
Pulsador hacia GND
Salida del TMP36 → Uno A0No uses un pin digital para esta lectura.
Uno 5 V → VCC del TMP36En el Uno la referencia del ADC es 5 V, así que el sensor también va a 5 V.
Uno GND → GND del TMP36 y del pulsadorMasa común.
Uno D2 → Pulsador hacia GNDAl pulsarlo, la próxima muestra se sustituye por un pico.
Código
// AP-FEN-11 Media movil y mediana en un sensor ruidoso
// Pines: A0 TMP36, D2 pico de prueba
const int PIN_SENSOR = A0;
const int PIN_PICO = 2;
const int N = 9;
int ventana[N];
int indice = 0;
bool llena = false;
float aGrados(int crudo) {
float v = crudo * (5.0f / 1023.0f);
return (v - 0.5f) * 100.0f;
}
float media() {
long s = 0;
int n = llena ? N : indice;
if (n == 0) return 0;
for (int i = 0; i < n; i++) s += ventana[i];
return s / (float)n;
}
float mediana() {
int n = llena ? N : indice;
int copia[N];
for (int i = 0; i < n; i++) copia[i] = ventana[i];
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (copia[j] < copia[i]) {
int t = copia[i];
copia[i] = copia[j];
copia[j] = t;
}
}
}
return aGrados(copia[n / 2]);
}
void setup() {
pinMode(PIN_PICO, INPUT_PULLUP);
Serial.begin(9600);
Serial.println(F("Media y mediana del TMP36."));
}
void loop() {
int crudo = analogRead(PIN_SENSOR);
if (digitalRead(PIN_PICO) == LOW) crudo = 900;
ventana[indice] = crudo;
indice++;
if (indice >= N) { indice = 0; llena = true; }
Serial.print(F("media "));
Serial.print(aGrados((int)media()) >= 0 ? media() : 0);
Serial.print(F(" cuentas_media "));
Serial.print(media(), 1);
Serial.print(F(" mediana_C "));
Serial.println(mediana(), 1);
delay(200);
}
Pasos
Alimenta el TMP36 a 5 V y lleva su salida a A0. El pulsador de D2 va a masa. Sube el sketch y abre el monitor a 9600 baudios. Espera a llenar nueve muestras. Anota media y mediana con el sensor quieto. Deben parecerse. Mantén el pulsador un instante. Esa muestra entra como 900 cuentas, un pico imposible para una habitación. La media de cuentas se desplaza. La mediana en grados debe aguantar mucho mejor, porque el pico no ocupa el centro. Suelta el botón y espera nueve muestras nuevas. Las dos cifras vuelven a juntarse. Mueve el cable del sensor para oír ruido de verdad y compara otra vez, ya sin el pico falso.
Variantes
Sube la ventana a 15 y observa que el pico tarda más en salir de la media. Imprime también el máximo de la ventana para ver el pico aunque la mediana lo ignore. Convierte la media de cuentas a grados solo al final, para no promediar ya en grados y en cuentas a la vez.
Depuración
Si media y mediana saltan igual, el pico no entra: D2 no lee bajo o el pull-up no está activo. Si el TMP36 da números bajo cero en una sala normal, está sin 5 V o la salida no es A0. Si la mediana también se dispara, la ventana es tan corta que el pico llega al centro. Mantén nueve o más. Si el monitor no se mueve, el retardo está bien y el puerto serie no es el de la placa.
Software: analogread digitalread serial
Fénix · AP-FEN-12
Alarma de casa por estados
Objetivo
Vas a montar una alarma de prácticas con un sensor reed en una puerta de maqueta y un pulsador para armar. El programa no es una cadena de if sueltos: recorre estados con nombre. Desarmada, espera a que armes. Al armar, te deja unos segundos para salir. Ya armada, si la puerta se abre empieza un retardo de entrada y, si no desarma a tiempo, pasa a disparada y enciende el LED. No hay sirena de red ni avisador externo. Es una máquina de estados en la mesa.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 Arduino Uno R3 placa-uno-r3 original / oficial consultar (>10 €) Usa esta o el clon. 1 Uno R3 clone CH340 placa-uno-ch340 genérico / clone poco (≈1–3 €) Mismos pines. 1 Reed sensor-reed genérico / clone irrisorio (<1 €) En el marco de una puerta de maqueta. Con el imán cerca, la puerta cuenta como cerrada. 1 Pulsador modulo-pulsador genérico / clone irrisorio (<1 €) Arma y desarma. 1 LED 5 mm modulo-led genérico / clone irrisorio (<1 €) Testigo de alarma disparada. 1 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 220 ohmios con el LED.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
Uno D2
Uno D3
Uno D8
Uno 5 V
Uno GND
Pulsador de armar hacia GND
Reed hacia GND
LED y resistencia a GND
Placa por USB
Pulsador, reed y LED
Uno D2 → Pulsador de armar hacia GNDActivo a bajo, con antirrebote.
Uno D3 → Reed hacia GNDCerrado a masa cuando el imán está junto al reed, es decir, puerta cerrada.
Uno D8 → LED y resistencia a GNDSolo luce en el estado disparada.
Uno 5 V → Placa por USBNo hay otra alimentación en esta maqueta.
Uno GND → Pulsador, reed y LEDMasa común.
Código
// AP-FEN-12 Alarma de casa por estados
// Pines: D2 armar, D3 reed, D8 LED
enum Estado { DESARMADA, SALIDA, ARMADA, ENTRADA, DISPARADA };
const int PIN_ARMAR = 2;
const int PIN_REED = 3;
const int PIN_LED = 8;
Estado estado = DESARMADA;
unsigned long marca = 0;
bool estable = HIGH;
unsigned long cambio = 0;
bool flancoArmar() {
bool crudo = digitalRead(PIN_ARMAR);
if (crudo != estable) {
if (millis() - cambio > 30) {
estable = crudo;
cambio = millis();
return crudo == LOW;
}
} else {
cambio = millis();
}
return false;
}
bool puertaAbierta() { return digitalRead(PIN_REED) == HIGH; }
void setup() {
pinMode(PIN_ARMAR, INPUT_PULLUP);
pinMode(PIN_REED, INPUT_PULLUP);
pinMode(PIN_LED, OUTPUT);
Serial.begin(9600);
}
void loop() {
bool armar = flancoArmar();
if (estado == DESARMADA && armar) { estado = SALIDA; marca = millis(); }
else if (estado == SALIDA && millis() - marca > 5000) estado = ARMADA;
else if (estado == ARMADA && puertaAbierta()) { estado = ENTRADA; marca = millis(); }
else if (estado == ENTRADA && !puertaAbierta()) estado = ARMADA;
else if (estado == ENTRADA && millis() - marca > 4000) estado = DISPARADA;
else if ((estado == ARMADA || estado == ENTRADA || estado == DISPARADA || estado == SALIDA) && armar && estado != SALIDA) {
estado = DESARMADA;
}
if (armar && estado == SALIDA && millis() - marca > 300) estado = DESARMADA;
digitalWrite(PIN_LED, estado == DISPARADA ? HIGH : LOW);
Serial.println((int)estado);
delay(20);
}
Pasos
Pon el reed en el marco y el imán en la puerta de la maqueta, de forma que al cerrar el contacto vaya a masa. Conecta D3 al reed, D2 al pulsador y D8 al LED, todos contra masa. Sube el sketch. El estado 0 es desarmada. Cierra la puerta antes de armar. Pulsa armar. Durante cinco segundos estás en salida: puedes abrir y no dispara. Pasado ese tiempo, abre la puerta. Entra el retardo de entrada, unos cuatro segundos, y el LED sigue apagado. Si no pulsas armar otra vez dentro de ese retardo, el estado pasa a disparada y el LED se queda encendido. Pulsa armar para volver a desarmada. El LED se apaga. No hay aviso fuera de la mesa.
Variantes
Alarga la salida a diez segundos si tu maqueta está lejos del pulsador. Haz parpadear el LED en el retardo de entrada y déjalo fijo al disparar, para distinguir los dos estados. Niegua la lectura del reed si tu contacto cierra al abrir la puerta, y escríbelo en el comentario.
Depuración
Si arma solo, el pulsador está a masa o el antirrebote no corre. Mira D2 en reposo: tiene que estar alto. Si dispara con la puerta cerrada, el imán no llega al reed o la lógica de puertaAbierta está al revés. Si el LED no enciende en disparada, D8 no es el pin del LED. El número de estado en el monitor manda. Si no sale del retardo, millis no avanza porque estás parado en un while. Este loop no se bloquea.
Software: maquina-estados debounce digitalread digitalwrite millis serial
Fénix · AP-FEN-13
Registro circular en EEPROM
Objetivo
Vas a guardar lecturas en la EEPROM del Uno en forma de anillo. Una casilla recuerda por dónde va la cabeza, y las siguientes guardan los valores. Cuando llegas al final, la cabeza vuelve al principio y pisas lo más viejo. Así el registro no crece sin límite y no desgasta siempre la misma celda. Al arrancar puedes volcar el anillo al monitor. Lo que se escribe se queda en la placa aunque quites el USB, y no sale a ningún otro sitio.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 Arduino Uno R3 placa-uno-r3 original / oficial consultar (>10 €) La EEPROM es la del ATmega328P. Usa esta o el clon. 1 Uno R3 clone CH340 placa-uno-ch340 genérico / clone poco (≈1–3 €) Misma memoria interna. 1 Pulsador modulo-pulsador genérico / clone irrisorio (<1 €) Cada pulsación anota un valor nuevo. 1 LED 5 mm modulo-led genérico / clone irrisorio (<1 €) Parpadea cuando la escritura se ha hecho. 1 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 220 ohmios. 1 TMP36 sensor-tmp36 ambos (oficial y genérico) poco (≈1–3 €) La muestra que se guarda es su lectura, ya en grados enteros.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
Uno D2
Uno D8
Salida del TMP36
Uno 5 V
Uno GND
Pulsador de anotar hacia GND
LED y resistencia a GND
Uno A0
VCC del TMP36
GND del sensor, del pulsador y del LED
Uno D2 → Pulsador de anotar hacia GNDUn flanco, una escritura.
Uno D8 → LED y resistencia a GNDConfirma la escritura.
Salida del TMP36 → Uno A0Origen de la muestra.
Uno 5 V → VCC del TMP36Referencia de 5 V, igual que el ADC.
Uno GND → GND del sensor, del pulsador y del LEDMasa común.
Código
// AP-FEN-13 Registro circular en EEPROM
// Pines: D2 anotar, D8 LED, A0 TMP36
#include <EEPROM.h>
const int PIN_BOTON = 2;
const int PIN_LED = 8;
const int PIN_SENSOR = A0;
const int POS_CABEZA = 0;
const int BASE = 1;
const int CAPACIDAD = 32;
int cabeza() {
int h = EEPROM.read(POS_CABEZA);
if (h < 0 || h >= CAPACIDAD) h = 0;
return h;
}
void anotar(byte valor) {
int h = cabeza();
EEPROM.update(BASE + h, valor);
h = (h + 1) % CAPACIDAD;
EEPROM.update(POS_CABEZA, h);
}
void volcar() {
int h = cabeza();
Serial.println(F("anillo, de mas viejo a mas nuevo"));
for (int i = 0; i < CAPACIDAD; i++) {
int idx = (h + i) % CAPACIDAD;
Serial.println(EEPROM.read(BASE + idx));
}
}
void setup() {
pinMode(PIN_BOTON, INPUT_PULLUP);
pinMode(PIN_LED, OUTPUT);
Serial.begin(9600);
volcar();
}
void loop() {
if (digitalRead(PIN_BOTON) != LOW) return;
delay(30);
if (digitalRead(PIN_BOTON) != LOW) return;
float v = analogRead(PIN_SENSOR) * (5.0f / 1023.0f);
int grados = (int)((v - 0.5f) * 100.0f);
if (grados < 0) grados = 0;
if (grados > 255) grados = 255;
anotar((byte)grados);
digitalWrite(PIN_LED, HIGH);
Serial.print(F("guardado "));
Serial.println(grados);
delay(200);
digitalWrite(PIN_LED, LOW);
while (digitalRead(PIN_BOTON) == LOW) delay(10);
}
Pasos
Conecta el TMP36 a 5 V, masa y A0. El pulsador va a D2 y el LED a D8. Sube el sketch. El primer volcado puede ser basura de una EEPROM virgen: es normal. Pulsa varias veces. Cada pulsación guarda un grado entero y enciende un instante el LED. Quita el USB, vuelve a conectar y mira el volcado. Los valores siguen ahí, en la placa. Sigue pulsando más de 32 veces. Los primeros valores desaparecen porque el anillo los pisa. La cabeza está en la dirección 0. Las muestras ocupan de la 1 a la 32. No uses esas casillas para otra cosa. update solo escribe si el byte cambia, para no gastar la celda en balde.
Variantes
Guarda dos bytes por muestra si más adelante quieres décimas, y dobla el avance de la cabeza. Añade un segundo botón que solo vuelque, sin anotar. Limpia el anillo escribiendo ceros una vez, con una función que quitas después de usarla.
Depuración
Si al reiniciar todo es 255, todavía no has anotado nada válido o la cabeza apunta mal. Si no guarda, D2 no baja. Mide el pulsador. Si el LED queda fijo, el while de espera no ve el flanco de suelta. Si los grados son absurdos, el TMP36 no está a 5 V y la fórmula no cuadra.
Software: eeprom analogread digitalread digitalwrite serial
Fénix · AP-FEN-14
Sueño y despertar por interrupción
Objetivo
Vas a dormir un Pro Mini de 5 V y 16 MHz hasta que una interrupción en D2 lo despierte. El sueño es el de power-down, por registro: se arma el modo, se habilita sleep y la CPU se para. La librería LowPower hace el mismo apagado si prefieres llamarla, pero este sketch usa avr/sleep.h para que veas los pasos. Mientras duerme, el LED queda apagado y el consumo baja. Al soltar y volver a pulsar, despierta, late un momento y se duerme otra vez. No es un ESP32 ni un Uno con USB permanente: el Pro Mini es la placa de este ensayo.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 Pro Mini 328P 5 V / 16 MHz placa-pro-mini genérico / clone irrisorio (<1 €) Versión de 5 V y 16 MHz. Se programa con el adaptador USB-serie que ya uses, no hace falta un identificador nuevo. 1 Pulsador modulo-pulsador genérico / clone irrisorio (<1 €) Despierta por nivel bajo en D2, que es la interrupción externa 0. 1 LED 5 mm modulo-led genérico / clone irrisorio (<1 €) Encendido solo durante la vigilia, para ver que no se quedó colgado despierto. 1 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 220 ohmios.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
Pro Mini D2
Pro Mini D4
Pro Mini VCC
Pro Mini GND
Pulsador hacia GND
LED y resistencia a GND
Alimentación de 5 V del adaptador o de una pila regulada
GND del pulsador, del LED y del adaptador
Pro Mini D2 → Pulsador hacia GNDLa interrupción es por nivel bajo. Mantenerlo pulsado lo deja despierto.
Pro Mini D4 → LED y resistencia a GNDTestigo de vigilia.
Pro Mini VCC → Alimentación de 5 V del adaptador o de una pila reguladaNo alimentes este modelo de 5 V con 3,3 V ni al revés.
Pro Mini GND → GND del pulsador, del LED y del adaptadorMasa común. Sin ella ni programas ni despierta.
Código
// AP-FEN-14 Sueno y despertar por interrupcion
// Pines: D2 despertar, D4 LED. Placa Pro Mini.
// Registro: SLEEP_MODE_PWR_DOWN. LowPower.powerDown hace este mismo corte.
#include <avr/sleep.h>
#include <avr/interrupt.h>
const int PIN_DESPERTAR = 2;
const int PIN_LED = 4;
void despertar() {}
void dormir() {
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
attachInterrupt(digitalPinToInterrupt(PIN_DESPERTAR), despertar, LOW);
interrupts();
sleep_cpu();
sleep_disable();
detachInterrupt(digitalPinToInterrupt(PIN_DESPERTAR));
}
void setup() {
pinMode(PIN_DESPERTAR, INPUT_PULLUP);
pinMode(PIN_LED, OUTPUT);
Serial.begin(9600);
Serial.println(F("Me dormire. Pulsa D2 para despertar."));
}
void loop() {
digitalWrite(PIN_LED, HIGH);
delay(300);
digitalWrite(PIN_LED, LOW);
Serial.println(F("a dormir"));
Serial.flush();
while (digitalRead(PIN_DESPERTAR) == LOW) delay(10);
dormir();
Serial.println(F("despierto"));
}
Pasos
Usa el Pro Mini de 5 V. Programa el sketch con tu adaptador USB-serie y el perfil de placa correspondiente a 16 MHz. Conecta el pulsador a D2 contra masa y el LED a D4. No dejes D2 al aire. Abre el monitor a 9600 baudios. Verás el aviso y, enseguida, el mensaje de que se duerme. El LED se apaga. Pulsa D2. La interrupción por nivel bajo despierta la CPU. Tiene que imprimir despierto y encender el LED un instante. Suelta el botón. Tras el parpadeo se vuelve a dormir. Si lo mantienes, el while impide dormir en bucle. Si instalas la librería LowPower, la llamada equivalente es powerDown con sueño indefinido y la misma patilla. Este sketch te enseña el registro para no esconderlo. Para medir corriente de verdad, alimenta por VCC y no dejes el adaptador USB enchufado, porque el puente serie también consume.
Variantes
Cambia el tiempo despierto de 300 ms a dos segundos para ver mejor el despertar. Cuenta en una variable cuántas veces despertó y muéstrala al volver. Usa el flanco FALLING si quieres que un pulso corto baste y el nivel bajo continuo no reteenga la CPU.
Depuración
Si no se duerme, D2 está a masa. El while o la interrupción LOW lo impiden a propósito. Si no despierta, el pulsador no llega a D2 o no es la interrupción 0. En el 328P, D2 es INT0. Si el monitor se queda a medias, falta Serial.flush antes de dormir: el último texto se corta al parar el reloj. Si programas y no responde, el adaptador está cruzado en TX y RX o elegiste 8 MHz en el IDE.
Software: sleep interrupciones digitalread digitalwrite serial
Fénix · AP-FEN-15
Calibración en dos puntos de varias entradas
Objetivo
Vas a calibrar tres entradas analógicas con dos puntos cada una, escritos en el sketch. Para cada canal mides un valor bajo y un valor alto que ya conoces, anotas las cuentas que dio el ADC y guardas esas cuatro cifras en el programa. La recta entre los dos puntos convierte cuentas nuevas en unidades reales. No es una cuenta en ningún servicio: si cambias de sensor, repites los dos puntos y regrabas. Verás que la misma función sirve para el TMP36, la LDR y el Hall, cambiando solo las constantes.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 Arduino Uno R3 placa-uno-r3 original / oficial consultar (>10 €) Usa esta o el clon. Referencia de 5 V. 1 Uno R3 clone CH340 placa-uno-ch340 genérico / clone poco (≈1–3 €) Mismas entradas A0, A1 y A2. 1 TMP36 sensor-tmp36 ambos (oficial y genérico) poco (≈1–3 €) Canal A0. Dos puntos: por ejemplo agua con hielo y agua tibia medida con otro termómetro. 1 LDR sensor-ldr genérico / clone irrisorio (<1 €) Canal A1, en divisor con 10 kiloohmios. 1 Hall sensor-hall genérico / clone irrisorio (<1 €) Canal A2, salida analógica. Dos distancias o dos imanes de referencia. 1 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 10 kiloohmios para el divisor de la LDR.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
TMP36 salida
Divisor de la LDR
Hall analógico
Uno 5 V
Uno GND
Uno A0
Uno A1
Uno A2
Alimentación de los tres sensores
Masa de los tres sensores
TMP36 salida → Uno A0Canal de temperatura.
Divisor de la LDR → Uno A1LDR a 5 V, resistencia a masa, nudo central a A1.
Hall analógico → Uno A2No uses la patilla digital del módulo para esta recta.
Uno 5 V → Alimentación de los tres sensoresMisma referencia que el ADC, si no la recta no vale.
Uno GND → Masa de los tres sensoresMasa común.
Código
// AP-FEN-15 Calibracion en dos puntos de varias entradas
// Pines: A0 TMP36, A1 LDR, A2 Hall
// Cada canal: cuentas bajas, valor bajo, cuentas altas, valor alto.
const int PIN_TMP = A0;
const int PIN_LDR = A1;
const int PIN_HALL = A2;
const int TMP_C0 = 140;
const float TMP_V0 = 0.0f;
const int TMP_C1 = 200;
const float TMP_V1 = 25.0f;
const int LDR_C0 = 200;
const float LDR_V0 = 10.0f;
const int LDR_C1 = 800;
const float LDR_V1 = 80.0f;
const int HALL_C0 = 500;
const float HALL_V0 = 0.0f;
const int HALL_C1 = 700;
const float HALL_V1 = 1.0f;
float recta(int cuentas, int c0, float v0, int c1, float v1) {
if (c1 == c0) return v0;
return v0 + (v1 - v0) * (cuentas - c0) / (float)(c1 - c0);
}
void setup() {
Serial.begin(9600);
Serial.println(F("Dos puntos por canal, constantes en el sketch."));
}
void loop() {
int t = analogRead(PIN_TMP);
int l = analogRead(PIN_LDR);
int h = analogRead(PIN_HALL);
Serial.print(F("T "));
Serial.print(recta(t, TMP_C0, TMP_V0, TMP_C1, TMP_V1), 1);
Serial.print(F(" luz "));
Serial.print(recta(l, LDR_C0, LDR_V0, LDR_C1, LDR_V1), 1);
Serial.print(F(" campo "));
Serial.println(recta(h, HALL_C0, HALL_V0, HALL_C1, HALL_V1), 2);
delay(400);
}
Pasos
Monta el TMP36 en A0, el divisor de la LDR en A1 y el Hall analógico en A2. Alimenta todo a 5 V. Sube el sketch una primera vez solo para leer cuentas. Anota las de A0 con el sensor en el punto frío y en el punto tibio. Escribe esas cuentas en TMP_C0 y TMP_C1, y los grados reales en TMP_V0 y TMP_V1. Esos números se quedan en el sketch. Haz lo mismo con la LDR: un rincón oscuro y una lámpara cercana, con la unidad que tú definas, por ejemplo de 10 a 80. Para el Hall, anota las cuentas sin imán y con el imán a una distancia fija. El valor alto puede ser 1, en unidades tuyas. Regraba y comprueba que en los dos puntos de cada canal la cifra calibrada se acerca al valor que mediste fuera. Si cambias de sensor, repites los dos puntos. No hay un perfil guardado fuera de esta placa.
Variantes
Rechaza una lectura si cae muy lejos de los dos puntos, para no extrapolar sin avisar. Añade un cuarto canal repitiendo la misma función, sin copiar la fórmula a mano en el loop. Imprime también las cuentas crudas al lado del valor, para no perder el origen de la recta.
Depuración
Si el resultado no se mueve, c0 y c1 quedaron iguales y la función devuelve el valor bajo a propósito. Si la temperatura baja cuando calientas, tienes cruzados el punto frío y el tibio. Si la LDR no cambia, el divisor está al revés o A1 no es el nudo central. Si el Hall se queda fijo, estás en la patilla digital del módulo.
Software: analogread serial
Fénix · AP-FEN-16
Paso a paso con rampa de aceleración
Objetivo
Vas a mover el 28BYJ-48 acelerando y frenando, no con un salto de velocidad. El intervalo entre pasos empieza largo, se acorta un poco en cada paso hasta un mínimo y luego vuelve a alargarse. El eje no pasa de lento a rápido de golpe: la rampa es la lista de intervalos. El motor sigue a 5 V con el ULN2003. Si oyes que pierde pasos, la rampa es demasiado agresiva y hay que suavizarla, no subir la tensión.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 Arduino Uno R3 placa-uno-r3 original / oficial consultar (>10 €) Usa esta o el clon. 1 Uno R3 clone CH340 placa-uno-ch340 genérico / clone poco (≈1–3 €) Mismos pines de fase. 1 Stepper 28BYJ-48 modulo-28byj-48 genérico / clone poco (≈1–3 €) Motor de 5 V. La rampa no lo convierte en un motor de 12 V. 1 ULN2003 modulo-uln2003 genérico / clone irrisorio (<1 €) Driver a 5 V.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
Uno D8
Uno D9
Uno D10
Uno D11
Uno 5 V
Uno GND
ULN2003 IN1
ULN2003 IN2
ULN2003 IN3
ULN2003 IN4
ULN2003 positivo
ULN2003 GND
Uno D8 → ULN2003 IN1Fase 1.
Uno D9 → ULN2003 IN2Fase 2.
Uno D10 → ULN2003 IN3Fase 3.
Uno D11 → ULN2003 IN4Fase 4.
Uno 5 V → ULN2003 positivo5 V. No lo alimentes a 12 V para ir más rápido.
Uno GND → ULN2003 GNDMasa común.
Código
// AP-FEN-16 Paso a paso con rampa de aceleracion
// Pines: D8 IN1, D9 IN2, D10 IN3, D11 IN4
// El periodo baja de 6 ms a 2 ms de uno en uno, y luego sube. No hay salto.
const int PINES[4] = {8, 9, 10, 11};
const int SECUENCIA[4][4] = {
{1, 0, 0, 0},
{0, 1, 0, 0},
{0, 0, 1, 0},
{0, 0, 0, 1}
};
const int PASOS = 800;
const int PERIODO_LENTO = 6000;
const int PERIODO_RAPIDO = 2000;
void aplicar(int indice) {
int k = (indice % 4 + 4) % 4;
for (int b = 0; b < 4; b++) digitalWrite(PINES[b], SECUENCIA[k][b]);
}
void setup() {
for (int b = 0; b < 4; b++) pinMode(PINES[b], OUTPUT);
Serial.begin(9600);
int periodo = PERIODO_LENTO;
int dirPeriodo = -20;
for (int n = 0; n < PASOS; n++) {
aplicar(n);
delayMicroseconds(periodo);
periodo += dirPeriodo;
if (periodo <= PERIODO_RAPIDO) { periodo = PERIODO_RAPIDO; dirPeriodo = 20; }
if (periodo >= PERIODO_LENTO) { periodo = PERIODO_LENTO; dirPeriodo = -20; }
}
for (int b = 0; b < 4; b++) digitalWrite(PINES[b], LOW);
Serial.println(F("Rampa hecha. Bobinas sueltas."));
}
void loop() {}
Pasos
Cablea D8 a D11 hacia IN1 a IN4 y alimenta el ULN2003 a 5 V. Sube el sketch y escucha el motor: el tictac empieza espaciado, se aprieta y vuelve a espaciarse. No cambies PERIODO_RAPIDO de 2000 a 200 de un golpe. Eso sería el salto que esta ficha quiere evitar. Si en la zona rápida el eje se para y vibra, pierde pasos. Sube el periodo rápido a 2500 y repite. Marca el eje y repite el arranque. La posición final debe parecerse, porque el número de pasos no cambia. Al terminar, las cuatro fases quedan en bajo para no dejar el motor frenado en caliente. Si más adelante inviertes el sentido, la rampa se aplica igual: primero lento, luego rápido, luego lento.
Variantes
Haz solo la subida, sin frenar, y compara el golpe final con esta versión. Reduce dirPeriodo a 5 para una rampa más larga y suave. Parte la rampa en tres tramos escritos a mano si quieres ver la tabla de tiempos.
Depuración
Si arranca ya rápido, periodo no sale de PERIODO_LENTO. Mira el valor inicial. Si vibra en el sitio, el orden de IN1 a IN4 está cruzado. La rampa no arregla una fase mal cableada. Si el USB se cae, el motor pide más de lo que da el puerto. Fuente de 5 V aparte y masa común. Si el movimiento es un salto, dejaste un delay fijo y no estás sumando dirPeriodo.
Software: digitalwrite serial
Fénix · AP-FEN-17
PID de temperatura con anti-windup
Objetivo
Vas a regular la temperatura de una carga pequeña con un PID de verdad, leído en un TMP36 y actuado por PWM sobre un transistor que alimenta una resistencia de 5 a 12 V. El anti-windup no consiste solo en recortar la salida: la integral tiene límites propios, I_MAX e I_MIN, y se recorta en cada paso antes de sumar al mando. Si solo recortas la salida y dejas crecer la integral, al cruzar la consigna el término sigue empujando un buen rato. Aquí ese término no puede acumularse fuera de su caja. No conmutas la red: la carga es de baja tensión.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 Arduino Uno R3 placa-uno-r3 original / oficial consultar (>10 €) Usa esta o el clon. D9 es el PWM del mando. 1 Uno R3 clone CH340 placa-uno-ch340 genérico / clone poco (≈1–3 €) Mismo D9. 1 TMP36 sensor-tmp36 ambos (oficial y genérico) poco (≈1–3 €) Mide la carga, pegado a ella sin hacer un corto. 1 BJT / MOSFET modulo-transistor genérico / clone irrisorio (<1 €) MOSFET o BJT de lógica, para el PWM. El pin no alimenta la carga. 1 LED 5 mm modulo-led genérico / clone irrisorio (<1 €) Indicador del esfuerzo, en paralelo lógico con el mando, no como carga térmica. 2 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 220 ohmios en el LED y la que pida la puerta o la base del transistor.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
Salida del TMP36
Uno D9
Uno D6
Uno 5 V
Uno GND
Fuente de 5 a 12 V
Uno A0
Entrada del transistor
LED y resistencia a GND
VCC del TMP36
Masa del sensor, del transistor y de la fuente de carga
Carga térmica a través del transistor
Salida del TMP36 → Uno A0Temperatura de proceso.
Uno D9 → Entrada del transistorPWM. La carga cuelga del transistor, no del pin.
Uno D6 → LED y resistencia a GNDSe enciende si el mando pasa de un umbral, para ver el esfuerzo sin medir la carga.
Uno 5 V → VCC del TMP36Referencia del sensor.
Uno GND → Masa del sensor, del transistor y de la fuente de cargaMasa común obligatoria.
Fuente de 5 a 12 V → Carga térmica a través del transistorNo conmutas la red. Una resistencia de potencia pequeña o una lámpara de baja tensión. Nada de enchufe de casa.
Código
// AP-FEN-17 PID de temperatura con anti-windup
// Pines: A0 TMP36, D9 PWM, D6 LED
// La integral se recorta a I_MAX e I_MIN. No basta con recortar la salida.
const int PIN_SENSOR = A0;
const int PIN_PWM = 9;
const int PIN_LED = 6;
const float CONSIGNA = 30.0f;
const float KP = 8.0f;
const float KI = 0.5f;
const float KD = 1.0f;
const float I_MAX = 40.0f;
const float I_MIN = -40.0f;
float integral = 0;
float errorPrevio = 0;
unsigned long tPrevio = 0;
float leerGrados() {
float v = analogRead(PIN_SENSOR) * (5.0f / 1023.0f);
return (v - 0.5f) * 100.0f;
}
int pasoPid(float medida) {
unsigned long ahora = millis();
float dt = (ahora - tPrevio) / 1000.0f;
tPrevio = ahora;
if (dt <= 0 || dt > 2) return 0;
float error = CONSIGNA - medida;
integral += error * dt;
if (integral > I_MAX) integral = I_MAX;
if (integral < I_MIN) integral = I_MIN;
float derivada = (error - errorPrevio) / dt;
errorPrevio = error;
float mando = KP * error + KI * integral + KD * derivada;
if (mando > 255) mando = 255;
if (mando < 0) mando = 0;
return (int)mando;
}
void setup() {
pinMode(PIN_PWM, OUTPUT);
pinMode(PIN_LED, OUTPUT);
analogWrite(PIN_PWM, 0);
tPrevio = millis();
Serial.begin(9600);
Serial.println(F("PID con integral limitada. Carga de 5 a 12 V."));
}
void loop() {
float t = leerGrados();
int mando = pasoPid(t);
analogWrite(PIN_PWM, mando);
digitalWrite(PIN_LED, mando > 40 ? HIGH : LOW);
Serial.print(F("C "));
Serial.print(t, 1);
Serial.print(F(" mando "));
Serial.print(mando);
Serial.print(F(" I "));
Serial.println(integral, 2);
delay(200);
}
Pasos
Monta el TMP36 en A0 y el transistor gobernado por D9. La carga de 5 a 12 V pasa por el transistor, nunca por el pin y nunca por la red. Une todas las masas. Sin eso el PWM y la temperatura no comparten referencia. Pon la consigna unos grados por encima de la sala, por ejemplo 30, y sube el sketch. Con la carga fría el mando debe subir, y la integral que imprime no puede pasar de 40 ni bajar de -40. Cuando la temperatura cruce la consigna, el mando tiene que aflojar pronto. Si la integral no estuviera limitada, seguiría alta un buen rato. Tapa la carga para que se pase de temperatura. La integral baja hasta su tope negativo y el PWM se queda en cero. Si la carga no es capaz de llegar a la consigna, no subas I_MAX para forzarlo: cambia la carga o la consigna. El tope existe para no embalar el término.
Variantes
Baja I_MAX a 10 y observa que el mando se queda más corto al arrancar. Pon KI a cero y compara el error que queda, para ver qué aportaba la integral. Mueve el sensor lejos de la carga y comprueba que el lazo se vuelve inútil aunque las cuentas cuadren.
Depuración
Si la integral impresa pasa de 40, el recorte no está en la variable integral sino solo en el mando. Vuelve a poner los dos if. Si el PWM no cambia, D9 no es el pin o analogWrite está pisado por otra configuración del timer. Si la temperatura no sube, el transistor no conduce o la fuente de 5 a 12 V está apagada. El LED de esfuerzo te dice si el mando sí salió. Si oscila sin parar, baja KP antes de tocar el sensor.
Software: pid analogread analogwrite pwm millis digitalwrite serial
Fénix · AP-FEN-18
Ver los bytes que pasan por I2C
Objetivo
Vas a mirar, byte a byte, lo que el Uno escribe y lee en el bus I2C cuando habla con un BMP280. Sin librería de sensor: tú mandas la dirección, el registro y lees la respuesta, y cada byte sale en hexadecimal por el monitor. Así dejas de imaginar el bus y ves la trama. El BMP280 va a 3,3 V a través de un level shifter. También recorres las direcciones para ver cuáles contestan. Nada de eso sale de la mesa.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 Arduino Uno R3 placa-uno-r3 original / oficial consultar (>10 €) Usa esta o el clon, como maestro. 1 Uno R3 clone CH340 placa-uno-ch340 genérico / clone poco (≈1–3 €) I2C en A4 y A5. 1 BMP280 sensor-bmp280 genérico / clone poco (≈1–3 €) Solo 3,3 V. El registro de identificador es 0xD0 y suele devolver 0x58. 1 Level shifter modulo-level-shifter genérico / clone irrisorio (<1 €) Entre el Uno a 5 V y el sensor a 3,3 V.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
Uno A4
Uno A5
Uno pin de 3,3 V
Uno 5 V
Uno GND
Lado alto del shifter, canal SDA
Lado alto del shifter, canal SCL
VCC del BMP280 y referencia baja del shifter
Referencia alta del shifter
GND del sensor y del shifter
Uno A4 → Lado alto del shifter, canal SDAEl lado bajo va al SDA del BMP280.
Uno A5 → Lado alto del shifter, canal SCLEl lado bajo va al SCL del BMP280.
Uno pin de 3,3 V → VCC del BMP280 y referencia baja del shifterNo alimentes el sensor a 5 V.
Uno 5 V → Referencia alta del shifterSolo el shifter, no el sensor.
Uno GND → GND del sensor y del shifterMasa común.
Código
// AP-FEN-18 Ver los bytes que pasan por I2C
// Pines: A4 SDA, A5 SCL. BMP280 a 3,3 V.
#include <Wire.h>
const int PIN_SDA = A4;
const int PIN_SCL = A5;
void imprimirByte(byte b) {
if (b < 16) Serial.print('0');
Serial.print(b, HEX);
Serial.print(' ');
}
void explorar() {
Serial.println(F("direcciones que contestan"));
for (byte dir = 1; dir < 127; dir++) {
Wire.beginTransmission(dir);
if (Wire.endTransmission() == 0) {
Serial.print(F("ACK "));
imprimirByte(dir);
Serial.println();
}
}
}
void leerIdentidad() {
Serial.println(F("escritura direccion 76, registro D0"));
Wire.beginTransmission(0x76);
Wire.write(0xD0);
byte err = Wire.endTransmission();
Serial.print(F("fin escritura "));
Serial.println(err);
Wire.requestFrom(0x76, 1);
Serial.print(F("lectura "));
while (Wire.available()) imprimirByte(Wire.read());
Serial.println();
}
void setup() {
Wire.begin();
Serial.begin(9600);
delay(200);
explorar();
leerIdentidad();
}
void loop() {
delay(2000);
leerIdentidad();
}
Pasos
Alimenta el BMP280 a 3,3 V y pasa SDA y SCL por el level shifter. A4 y A5 se quedan en el lado de 5 V. Sube el sketch y abre el monitor a 9600 baudios. En el listado de direcciones tiene que aparecer 76, o 77 si tu módulo viene en la otra dirección. La lectura del registro D0 debe enseñar 58 en hexadecimal. Ese byte es la identidad del BMP280. Si sale 77 y no 76, cambia las dos apariciones de 0x76 por 0x77 y vuelve a mirar el byte. Desconecta SDA y repite. El ACK desaparece y la lectura no puede traer el 58. Vuelves a ver el bus de verdad. No des por bueno un sensor porque la librería lo diga: aquí el byte tiene que verse.
Variantes
Lee seis bytes seguidos desde el registro de datos y numéralos en el monitor, sin convertirlos todavía a grados. Imprime también el byte que escribes, no solo el que lees, en la misma línea. Repite el ensayo con un OLED en 0x3C para ver otra dirección en el mismo barrido.
Depuración
Si no hay ningún ACK, el shifter no tiene las dos referencias o A4 y A5 están cruzados. Si hay ACK y el byte no es 58, no estás en el registro D0 o el chip no es un BMP280. Si el Uno se cuelga en explorar, un dispositivo mantiene el bus bajo. Quita módulos hasta que SCL vuelva a alto. Si alimentaste el sensor a 5 V, el byte puede no llegar nunca. Pasa a 3,3 V y comprueba si el módulo sigue vivo.
Software: i2c serial
Fénix · AP-FEN-19
Autotest de pines al arrancar
Objetivo
Vas a comprobar, nada más arrancar, que un grupo de pines digitales no está pegado a masa y que las entradas analógicas responden dentro de un margen. El test pone cada digital en entrada con pull-up y espera un nivel alto, que es lo normal si no hay nada conectado. Si alguno lee bajo, lo nombra y no lo da por bueno. Las analógicas se imprimen para que veas si una está clavada en cero o en el fondo de escala. No se pone ningún pin en salida fuerte: así un corto no recibe corriente del test.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 Arduino Uno R3 placa-uno-r3 original / oficial consultar (>10 €) Usa esta o el clon. El test no cubre D0 ni D1, que están en el USB. 1 Uno R3 clone CH340 placa-uno-ch340 genérico / clone poco (≈1–3 €) Mismo mapa de pines. 1 LED 5 mm modulo-led genérico / clone irrisorio (<1 €) En D13, el de la propia placa también vale, como resumen: fijo si todo fue bien. 1 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) Solo si usas un LED externo en D13, 220 ohmios.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
Uno D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12
Uno A0 A1 A2 A3 A4 A5
Uno D13
USB
Uno GND
Nada, durante el test
Nada, o el sensor que quieras observar
LED de resumen
Uno
Referencia del ensayo
Uno D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 → Nada, durante el testDéjalos libres. Un cable a masa hará fallar ese pin a propósito.
Uno A0 A1 A2 A3 A4 A5 → Nada, o el sensor que quieras observarEl test no juzga un valor único: imprime las cuentas.
Uno D13 → LED de resumenAlto continuo si ningún digital leyó bajo.
USB → UnoAlimentación mientras miras el monitor.
Uno GND → Referencia del ensayoPuedes tocar un pin digital contra masa para ver cómo el test lo señala.
Código
// AP-FEN-19 Autotest de pines al arrancar
// Pines: D2 D3 D4 D5 D6 D7 D8 D9 D10 D11 D12 A0 A1 A2 A3 A4 A5 D13
const int DIGITALES[] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
const int N_DIG = 11;
const int ANALOGICAS[] = {A0, A1, A2, A3, A4, A5};
const int PIN_RESUMEN = 13;
int fallos = 0;
void setup() {
Serial.begin(9600);
pinMode(PIN_RESUMEN, OUTPUT);
Serial.println(F("Autotest. Los digitales tienen que leer alto."));
for (int i = 0; i < N_DIG; i++) {
pinMode(DIGITALES[i], INPUT_PULLUP);
delay(2);
int v = digitalRead(DIGITALES[i]);
Serial.print(F("D"));
Serial.print(DIGITALES[i]);
if (v == HIGH) Serial.println(F(" alto"));
else {
Serial.println(F(" pegado a masa"));
fallos++;
}
}
for (int i = 0; i < 6; i++) {
int v = analogRead(ANALOGICAS[i]);
Serial.print(F("A"));
Serial.print(i);
Serial.print(' ');
Serial.println(v);
if (v == 0 || v >= 1020) {
Serial.println(F("analógica clavada, revisa el cable"));
}
}
digitalWrite(PIN_RESUMEN, fallos == 0 ? HIGH : LOW);
Serial.print(F("fallos "));
Serial.println(fallos);
}
void loop() {}
Pasos
Quita shields y cables de D2 a D12. Deja el USB puesto para ver el monitor. Sube el sketch y ábrelo a 9600 baudios. Cada digital debe decir alto. D13 se queda encendido si el recuento de fallos es cero. Toca D4 contra GND con un cable y pulsa reset. Esa línea tiene que decir pegado a masa y el LED de resumen se apaga. Quita el cable y resetea otra vez. El fallo desaparece: el pin no se ha quedado marcado. Lee las seis analógicas. Un pin al aire puede dar un número cualquiera. Uno cableado a masa se queda en cero y el texto te avisa. No uses este test con un pin ya unido a un relé o a un motor. Primero desmonta, luego diagnostica.
Variantes
Añade D13 al barrido solo si no lo usas como resumen. Considera fallo analógico únicamente por debajo de 2 o por encima de 1022, para no asustarte con un sensor a media escala. Guarda el número de fallos y compáralo con el del arranque anterior si más adelante lo anotas en EEPROM.
Depuración
Si un digital falla siempre, hay una soldadura a masa o un shield pinzado. No es el pull-up del sketch. Si fallan D0 o D1, no están en esta lista a propósito: el USB los usa. Si todas las analógicas salen 0, olvidaste que A4 y A5 pueden estar ocupadas por un I2C que dejaste puesto. Si el LED de resumen no enciende con cero fallos, D13 está cargado por otro montaje.
Software: digitalread digitalwrite analogread serial
Fénix · AP-FEN-20
Mini hogar offline: luz, puerta, temperatura y relé
Objetivo
Vas a juntar en un Uno, sin radio y sin ninguna red, cuatro piezas de una casa de maqueta: la luz con un LED, la puerta con un reed, la temperatura con un TMP36 y un relé que solo puede mover una carga de 5 a 12 V. El programa cambia de estado según la puerta y el calor, y el relé no se entera de ningún servicio externo porque la placa no tiene de dónde hablar. No conmutas la red. Si la puerta se abre, el estado lo dice y el relé puede encender una lámpara de baja tensión. Si hace demasiado calor, también. La luz de la habitación la mandas tú con un pulsador.
Materiales
Cant. Pieza Ficha Origen Precio relativo En este montaje 1 Arduino Uno R3 placa-uno-r3 original / oficial consultar (>10 €) Placa sin radio. Usa esta o el clon. No pongas aquí un ESP32: esta ficha es offline. 1 Uno R3 clone CH340 placa-uno-ch340 genérico / clone poco (≈1–3 €) Misma maqueta. 1 LED 5 mm modulo-led genérico / clone irrisorio (<1 €) La luz de la habitación. 1 Resistencias modulo-resistencia genérico / clone irrisorio (<1 €) 220 ohmios con ese LED. 1 Reed sensor-reed genérico / clone irrisorio (<1 €) Puerta. Con el imán cerca, el contacto va a masa y la puerta está cerrada. 1 TMP36 sensor-tmp36 ambos (oficial y genérico) poco (≈1–3 €) Temperatura de la maqueta. 1 Relé 5 V modulo-rele genérico / clone poco (≈1–3 €) Activo a nivel bajo. Carga de 5 a 12 V, nunca la red. 1 Pulsador modulo-pulsador genérico / clone irrisorio (<1 €) Enciende y apaga la luz.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Placa
Pieza
Uno D5
Uno D2
Uno D3
Salida del TMP36
Uno D7
Uno 5 V
Uno GND
Contacto del relé
LED de la luz y resistencia a GND
Pulsador de la luz hacia GND
Reed de la puerta hacia GND
Uno A0
IN del relé
TMP36 y VCC del módulo de relé
Sensores, pulsador, LED y relé
Lámpara o timbre de 5 a 12 V
Uno D5 → LED de la luz y resistencia a GNDLa luz no pasa por el relé.
Uno D2 → Pulsador de la luz hacia GNDCada flanco cambia la luz.
Uno D3 → Reed de la puerta hacia GNDBajo con la puerta cerrada.
Salida del TMP36 → Uno A0Temperatura.
Uno D7 → IN del reléSolo para la carga de baja tensión.
Uno 5 V → TMP36 y VCC del módulo de reléLa bobina del módulo sale de aquí. La carga, no.
Uno GND → Sensores, pulsador, LED y reléMasa común con la fuente de la carga.
Contacto del relé → Lámpara o timbre de 5 a 12 VNo conmutas la red. Esta casa de maqueta no tiene enchufe de pared.
Código
// AP-FEN-20 Mini hogar offline: luz, puerta, temperatura y rele
// Pines: D5 luz, D2 pulsador, D3 reed, A0 TMP36, D7 rele
// Sin radio. Rele de 5 a 12 V. No se conmuta la red.
const int PIN_LUZ = 5;
const int PIN_BOTON = 2;
const int PIN_REED = 3;
const int PIN_TEMP = A0;
const int PIN_RELE = 7;
const int RELE_ON = LOW;
const int RELE_OFF = HIGH;
const float UMBRAL_C = 29.0f;
bool luz = false;
bool botonAntes = HIGH;
enum Estado { REPOSO, PUERTA, CALOR, AMBOS };
Estado estado = REPOSO;
float leerGrados() {
float v = analogRead(PIN_TEMP) * (5.0f / 1023.0f);
return (v - 0.5f) * 100.0f;
}
void setup() {
pinMode(PIN_LUZ, OUTPUT);
pinMode(PIN_BOTON, INPUT_PULLUP);
pinMode(PIN_REED, INPUT_PULLUP);
pinMode(PIN_RELE, OUTPUT);
digitalWrite(PIN_RELE, RELE_OFF);
digitalWrite(PIN_LUZ, LOW);
Serial.begin(9600);
Serial.println(F("Hogar de mesa, sin enlace."));
}
void loop() {
bool boton = digitalRead(PIN_BOTON);
if (botonAntes == HIGH && boton == LOW) luz = !luz;
botonAntes = boton;
digitalWrite(PIN_LUZ, luz ? HIGH : LOW);
bool abierta = digitalRead(PIN_REED) == HIGH;
bool calor = leerGrados() >= UMBRAL_C;
if (abierta && calor) estado = AMBOS;
else if (abierta) estado = PUERTA;
else if (calor) estado = CALOR;
else estado = REPOSO;
digitalWrite(PIN_RELE, (estado == PUERTA || estado == CALOR || estado == AMBOS) ? RELE_ON : RELE_OFF);
Serial.print(F("estado "));
Serial.print((int)estado);
Serial.print(F(" C "));
Serial.println(leerGrados(), 1);
delay(200);
}
Pasos
Monta el LED en D5, el pulsador en D2, el reed en D3, el TMP36 en A0 y el relé en D7. Deja el contacto del relé para una carga de 5 a 12 V. No lo lleves a la red. Esta placa no tiene radio y no va a tenerla. Cierra la puerta de la maqueta, con el imán junto al reed, y sube el sketch. El estado tiene que ser reposo si la sala está por debajo de 29 grados. Pulsa la luz. El LED cambia y el relé no tiene por qué seguirlo: son cosas distintas. Abre la puerta. El estado pasa a puerta y el relé cierra la carga de baja tensión. Calienta el TMP36 con el dedo, con la puerta cerrada. Cuando pase el umbral, el estado es calor y el relé también actúa. Si coinciden puerta abierta y calor, el estado es ambos. Al volver a la normalidad el relé abre. No queda ningún aviso fuera de la mesa.
Variantes
Separa el relé de la puerta y déjalo solo para el calor, si quieres una maqueta más clara. Sube el umbral a 32 grados si tu dedo no llega a dispararlo. Añade un retardo de dos segundos antes de aceptar la puerta, reutilizando la idea de la alarma por estados.
Depuración
Si el relé clica al revés, tu módulo es activo a nivel alto. Invierte RELE_ON y RELE_OFF, no la red. Si la puerta siempre sale abierta, el imán no cierra el reed o D3 no tiene pull-up. Si la luz rebota, el flanco ve ruido. Espera 20 ms antes de aceptar el cambio. Si aparecen grados imposibles, el TMP36 no está a 5 V o A0 está suelto.
Software: maquina-estados analogread digitalread digitalwrite serial