les vencimos

Arduino · Proyectos

Fénix

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.PiezaFichaOrigenPrecio relativoEn este montaje
1ESP32 DevKit (clone)placa-esp32genérico / clonepoco (≈1–3 €)DevKit clónico. En el IDE elige el perfil de ESP32 Dev Module, no inventes una placa oficial.
1LED 5 mmmodulo-ledgenérico / cloneirrisorio (<1 €)LED externo. No uses solo el LED de la placa, para ver el cable.
1Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<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

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

  1. Elige en el IDE la tarjeta ESP32 Dev Module para este DevKit clónico. No busques un identificador de placa oficial: no hace falta.
  2. Conecta GPIO4 al LED con 330 ohmios hacia masa. La lógica del ESP32 es de 3,3 V.
  3. Sube el sketch por USB. En el monitor a 115200 baudios aparece la dirección del punto de acceso.
  4. En el teléfono, conéctate a TallerLocal con la clave del sketch. Olvida el Wi-Fi de casa durante la prueba.
  5. Escribe en el navegador la dirección impresa, normalmente 192.168.4.1, sin ponerle ningún prefijo delante.
  6. Pulsa encender y apagar. El LED debe seguir al enlace y la página debe decir el estado nuevo.
  7. Apaga el punto de acceso cortando el USB y comprueba que la página deja de existir. No había nada guardado fuera.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1ESP32 DevKit (clone)placa-esp32genérico / clonepoco (≈1–3 €)DevKit clónico, perfil ESP32 Dev Module.
1DHT11sensor-dht11genérico / cloneirrisorio (<1 €)Datos en GPIO16. Si tu módulo no trae resistencia de pull-up, añade 10 kiloohmios a 3,3 V.
1TMP36sensor-tmp36ambos (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.
1Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<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

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

  1. Alimenta DHT11 y TMP36 desde el pin de 3,3 V del DevKit. No les des 5 V.
  2. Conecta el dato del DHT11 a GPIO16 y la salida del TMP36 a GPIO34.
  3. Instala la librería DHT desde el gestor de librerías del IDE, en tu ordenador.
  4. Sube el sketch y únete al punto de acceso TallerLocal. No hace falta el Wi-Fi de casa.
  5. Abre la dirección que imprime el monitor, normalmente 192.168.4.1, escrita sola.
  6. Compara las dos temperaturas. Pueden diferir un grado: el DHT11 es burdo y el TMP36 es analógico.
  7. 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

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1ESP32 DevKit (clone)placa-esp32genérico / clonepoco (≈1–3 €)DevKit clónico en la LAN de casa, no en un servicio externo.
1Pulsadormodulo-pulsadorgenérico / cloneirrisorio (<1 €)Sube el umbral guardado en una unidad y lo reescribe en el archivo.
1LED 5 mmmodulo-ledgenérico / cloneirrisorio (<1 €)Se enciende si el valor de prueba supera el umbral guardado.
1Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<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

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

  1. 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.
  2. Conecta el LED a GPIO4 y el pulsador a GPIO17 contra masa.
  3. Sube el sketch. Espera a que el monitor imprima una dirección de tu rango, parecida a 192.168.1.algo.
  4. Desde un ordenador unido a la misma red, abre esa dirección sola, sin prefijo. Tienes que ver el umbral.
  5. Pulsa el botón. El número sube y, si reinicias la placa, sigue igual porque está en el archivo umbral.txt.
  6. 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.
  7. No entres al router a reenviar puertos. Si lo hicieras, romperías la condición de esta ficha.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1ESP32 DevKit (clone)placa-esp32genérico / clonepoco (≈1–3 €)Cliente MQTT. El broker no es esta placa: es una Raspberry en la misma red.
1LED 5 mmmodulo-ledgenérico / cloneirrisorio (<1 €)Obedece al tema taller/led.
1Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<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

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

  1. En la Raspberry de tu LAN, arranca un broker MQTT escuchando solo en esa red, puerto 1883. No lo publiques en internet.
  2. Pon en el sketch la dirección real de esa Raspberry. Instala PubSubClient desde el gestor de librerías.
  3. Sustituye el nombre y la clave por los de tu Wi-Fi de casa y conecta el LED a GPIO4.
  4. Sube el sketch. En el monitor tiene que aparecer la dirección local del ESP32 y, al conectar, no un error repetido.
  5. Desde la Raspberry, publica en el tema taller/led el carácter 1 y luego el 0. El LED debe obedecer.
  6. Apaga la Raspberry. El cliente debe quedarse reintentando y el LED no tiene que cambiar por un mensaje que ya no puede llegar.
  7. No crees usuario en un proveedor externo. Si el ensayo solo funciona con uno de esos, no es esta ficha.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1ESP32 DevKit (clone)placa-esp32genérico / clonepoco (≈1–3 €)DevKit clónico. La actualización sale del IDE de tu ordenador en la misma LAN.
1LED 5 mmmodulo-ledgenérico / cloneirrisorio (<1 €)Late a un ritmo que cambiarás a propósito entre versiones.
1Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<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

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

  1. Rellena el nombre y la clave de tu Wi-Fi de casa. La clave de ArduinoOTA es otra, local, y solo protege la subida.
  2. Conecta el LED a GPIO4, sube este sketch una primera vez por USB y abre el monitor a 115200 baudios.
  3. Comprueba que imprime una dirección de tu LAN y que el LED late cada 400 ms.
  4. Cambia RITMO_MS a 100, guarda, y en el IDE elige el puerto de red de taller-local, no el USB.
  5. 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.
  6. Prueba a subir con el ordenador unido a otra red. No debe aparecer el puerto. Así sabes que no está expuesto.
  7. No reenvíes puertos en el router. Esta vía es solo para quien ya está en casa.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1Arduino Uno R3placa-uno-r3original / oficialconsultar (>10 €)Usa esta o el clon.
1Uno R3 clone CH340placa-uno-ch340genérico / clonepoco (≈1–3 €)Mismos tres pines de control.
174HC165modulo-74hc165genérico / cloneirrisorio (<1 €)Registro de entrada paralelo a serie. Ocho canales.
8Pulsadormodulo-pulsadorgenérico / cloneirrisorio (<1 €)Uno por entrada. El otro extremo de cada uno a masa.
8Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<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

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

  1. Coloca el 74HC165 y une D8 a la carga, D9 al reloj y D10 a Q7.
  2. 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.
  3. Alimenta el chip a 5 V y comparte la masa con la placa y con los pulsadores.
  4. Sube el sketch y abre el monitor a 9600 baudios. Sin pulsar nada tienes que ver unos estables.
  5. 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.
  6. Aprieta dos a la vez y comprueba que se ven los dos ceros en el mismo byte.
  7. Si un canal no cambia nunca, ese pull-up o ese pulsador está suelto. No es el reloj, que es común.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1Arduino Uno R3placa-uno-r3original / oficialconsultar (>10 €)Usa esta o el clon.
1Uno R3 clone CH340placa-uno-ch340genérico / clonepoco (≈1–3 €)Mismos pines.
274HC595modulo-74hc595genérico / cloneirrisorio (<1 €)El QH prima del primero entra al SER del segundo.
16LED 5 mmmodulo-ledgenérico / cloneirrisorio (<1 €)Una luz por salida Q0 a Q7 de cada chip.
16Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<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

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

  1. Encadena los dos 74HC595: QH prima del primero al SER del segundo. Reloj y cerrojo van en paralelo a los dos.
  2. Conecta D11 al SER del primero, D12 a los dos relojes y D8 a los dos cerrojos.
  3. Pon en cada salida Q un LED y una resistencia de 220 ohmios hacia masa. No juntes los LED sin resistencia.
  4. Sube el sketch. Una sola luz debe recorrer las dieciséis posiciones.
  5. 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.
  6. Para en un valor fijo, por ejemplo 0x00FF, y cuenta si se encienden ocho luces seguidas.
  7. Toca un chip tras un minuto con varios LED encendidos. Si quema, estás pidiendo demasiada corriente por salida.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1Arduino Uno R3placa-uno-r3original / oficialconsultar (>10 €)ATmega328P a 16 MHz. El cálculo es para ese reloj.
1Uno R3 clone CH340placa-uno-ch340genérico / clonepoco (≈1–3 €)Mismo micro y mismo D9 como OC1A.
1LED 5 mmmodulo-ledgenérico / cloneirrisorio (<1 €)Para ver el hercio. Un ciclo completo es un segundo.
1Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<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

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

  1. Conecta el LED a D9 con su resistencia. Tiene que ser D9, la salida OC1A del timer 1.
  2. Sube el sketch a un Uno a 16 MHz. Si la placa fuera de otro reloj, el hercio no saldría.
  3. Mira el LED: un segundo encendido y un segundo apagado, de forma estable.
  4. Abre el monitor a 9600 baudios. Verás el contador del timer moverse aunque el LED lo lleve el hardware.
  5. Añade un delay largo dentro del loop, de dos segundos. El LED no debe cambiar de ritmo: no lo estás conmutando tú.
  6. No uses analogWrite en D9 en este sketch. Ese uso reprograma el mismo timer.
  7. Si más adelante quieres 10 Hz, recalcula OCR1A y no toques el LED a mano.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1Arduino Uno R3placa-uno-r3original / oficialconsultar (>10 €)Este esclavo es un Uno. Usa esta o el clon, no un ESP32.
1Uno R3 clone CH340placa-uno-ch340genérico / clonepoco (≈1–3 €)También vale como esclavo. Misma dirección 0x12.
1Level shiftermodulo-level-shiftergenérico / cloneirrisorio (<1 €)Imprescindible: la Raspberry no admite 5 V en SDA ni en SCL.
1LED 5 mmmodulo-ledgenérico / cloneirrisorio (<1 €)Obedece al byte recibido.
1Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<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

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

  1. 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.
  2. 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.
  3. Conecta el LED a D8. Sube este sketch al Uno. La dirección queda en 0x12.
  4. 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.
  5. Manda un 48, el carácter 0, y el LED se apaga. El monitor del Uno debe enseñar el hexadecimal.
  6. Desconecta SDA. La Pi tiene que fallar la escritura y el LED no debe cambiar: la orden no llegó.
  7. No pongas el esclavo en un ESP32 en esta ficha. El que espera en onReceive es el Uno.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1Arduino Uno R3placa-uno-r3original / oficialconsultar (>10 €)Usa esta o el clon. El enlace serie del USB sirve para hablar con la Pi o con el monitor.
1Uno R3 clone CH340placa-uno-ch340genérico / clonepoco (≈1–3 €)Misma UART en D0 y D1 a través del puente USB.
1LED 5 mmmodulo-ledgenérico / cloneirrisorio (<1 €)Se enciende si el primer dato de una trama válida es 1.
1Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<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

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

  1. 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.
  2. 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.
  3. 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.
  4. El monitor debe responder trama ok y el LED encenderse.
  5. Cambia el último byte a propósito. Tiene que decir suma mala y apagar el LED.
  6. Manda basura antes de AA. El autómata se queda en espera y no se descuadra el LED.
  7. 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

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1Arduino Uno R3placa-uno-r3original / oficialconsultar (>10 €)Usa esta o el clon.
1Uno R3 clone CH340placa-uno-ch340genérico / clonepoco (≈1–3 €)Misma entrada analógica.
1TMP36sensor-tmp36ambos (oficial y genérico)poco (≈1–3 €)Salida a A0. Alimentación a 5 V en el Uno.
1Pulsadormodulo-pulsadorgenérico / cloneirrisorio (<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

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

  1. Alimenta el TMP36 a 5 V y lleva su salida a A0. El pulsador de D2 va a masa.
  2. Sube el sketch y abre el monitor a 9600 baudios. Espera a llenar nueve muestras.
  3. Anota media y mediana con el sensor quieto. Deben parecerse.
  4. Mantén el pulsador un instante. Esa muestra entra como 900 cuentas, un pico imposible para una habitación.
  5. La media de cuentas se desplaza. La mediana en grados debe aguantar mucho mejor, porque el pico no ocupa el centro.
  6. Suelta el botón y espera nueve muestras nuevas. Las dos cifras vuelven a juntarse.
  7. Mueve el cable del sensor para oír ruido de verdad y compara otra vez, ya sin el pico falso.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1Arduino Uno R3placa-uno-r3original / oficialconsultar (>10 €)Usa esta o el clon.
1Uno R3 clone CH340placa-uno-ch340genérico / clonepoco (≈1–3 €)Mismos pines.
1Reedsensor-reedgenérico / cloneirrisorio (<1 €)En el marco de una puerta de maqueta. Con el imán cerca, la puerta cuenta como cerrada.
1Pulsadormodulo-pulsadorgenérico / cloneirrisorio (<1 €)Arma y desarma.
1LED 5 mmmodulo-ledgenérico / cloneirrisorio (<1 €)Testigo de alarma disparada.
1Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<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

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

  1. 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.
  2. Conecta D3 al reed, D2 al pulsador y D8 al LED, todos contra masa.
  3. Sube el sketch. El estado 0 es desarmada. Cierra la puerta antes de armar.
  4. Pulsa armar. Durante cinco segundos estás en salida: puedes abrir y no dispara.
  5. Pasado ese tiempo, abre la puerta. Entra el retardo de entrada, unos cuatro segundos, y el LED sigue apagado.
  6. Si no pulsas armar otra vez dentro de ese retardo, el estado pasa a disparada y el LED se queda encendido.
  7. Pulsa armar para volver a desarmada. El LED se apaga. No hay aviso fuera de la mesa.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1Arduino Uno R3placa-uno-r3original / oficialconsultar (>10 €)La EEPROM es la del ATmega328P. Usa esta o el clon.
1Uno R3 clone CH340placa-uno-ch340genérico / clonepoco (≈1–3 €)Misma memoria interna.
1Pulsadormodulo-pulsadorgenérico / cloneirrisorio (<1 €)Cada pulsación anota un valor nuevo.
1LED 5 mmmodulo-ledgenérico / cloneirrisorio (<1 €)Parpadea cuando la escritura se ha hecho.
1Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<1 €)220 ohmios.
1TMP36sensor-tmp36ambos (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

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

  1. Conecta el TMP36 a 5 V, masa y A0. El pulsador va a D2 y el LED a D8.
  2. Sube el sketch. El primer volcado puede ser basura de una EEPROM virgen: es normal.
  3. Pulsa varias veces. Cada pulsación guarda un grado entero y enciende un instante el LED.
  4. Quita el USB, vuelve a conectar y mira el volcado. Los valores siguen ahí, en la placa.
  5. Sigue pulsando más de 32 veces. Los primeros valores desaparecen porque el anillo los pisa.
  6. La cabeza está en la dirección 0. Las muestras ocupan de la 1 a la 32. No uses esas casillas para otra cosa.
  7. update solo escribe si el byte cambia, para no gastar la celda en balde.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1Pro Mini 328P 5 V / 16 MHzplaca-pro-minigenérico / cloneirrisorio (<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.
1Pulsadormodulo-pulsadorgenérico / cloneirrisorio (<1 €)Despierta por nivel bajo en D2, que es la interrupción externa 0.
1LED 5 mmmodulo-ledgenérico / cloneirrisorio (<1 €)Encendido solo durante la vigilia, para ver que no se quedó colgado despierto.
1Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<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

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

  1. Usa el Pro Mini de 5 V. Programa el sketch con tu adaptador USB-serie y el perfil de placa correspondiente a 16 MHz.
  2. Conecta el pulsador a D2 contra masa y el LED a D4. No dejes D2 al aire.
  3. Abre el monitor a 9600 baudios. Verás el aviso y, enseguida, el mensaje de que se duerme. El LED se apaga.
  4. Pulsa D2. La interrupción por nivel bajo despierta la CPU. Tiene que imprimir despierto y encender el LED un instante.
  5. Suelta el botón. Tras el parpadeo se vuelve a dormir. Si lo mantienes, el while impide dormir en bucle.
  6. 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.
  7. Para medir corriente de verdad, alimenta por VCC y no dejes el adaptador USB enchufado, porque el puente serie también consume.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1Arduino Uno R3placa-uno-r3original / oficialconsultar (>10 €)Usa esta o el clon. Referencia de 5 V.
1Uno R3 clone CH340placa-uno-ch340genérico / clonepoco (≈1–3 €)Mismas entradas A0, A1 y A2.
1TMP36sensor-tmp36ambos (oficial y genérico)poco (≈1–3 €)Canal A0. Dos puntos: por ejemplo agua con hielo y agua tibia medida con otro termómetro.
1LDRsensor-ldrgenérico / cloneirrisorio (<1 €)Canal A1, en divisor con 10 kiloohmios.
1Hallsensor-hallgenérico / cloneirrisorio (<1 €)Canal A2, salida analógica. Dos distancias o dos imanes de referencia.
1Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<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

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

  1. Monta el TMP36 en A0, el divisor de la LDR en A1 y el Hall analógico en A2. Alimenta todo a 5 V.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. Regraba y comprueba que en los dos puntos de cada canal la cifra calibrada se acerca al valor que mediste fuera.
  7. Si cambias de sensor, repites los dos puntos. No hay un perfil guardado fuera de esta placa.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1Arduino Uno R3placa-uno-r3original / oficialconsultar (>10 €)Usa esta o el clon.
1Uno R3 clone CH340placa-uno-ch340genérico / clonepoco (≈1–3 €)Mismos pines de fase.
1Stepper 28BYJ-48modulo-28byj-48genérico / clonepoco (≈1–3 €)Motor de 5 V. La rampa no lo convierte en un motor de 12 V.
1ULN2003modulo-uln2003genérico / cloneirrisorio (<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

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

  1. Cablea D8 a D11 hacia IN1 a IN4 y alimenta el ULN2003 a 5 V.
  2. Sube el sketch y escucha el motor: el tictac empieza espaciado, se aprieta y vuelve a espaciarse.
  3. No cambies PERIODO_RAPIDO de 2000 a 200 de un golpe. Eso sería el salto que esta ficha quiere evitar.
  4. Si en la zona rápida el eje se para y vibra, pierde pasos. Sube el periodo rápido a 2500 y repite.
  5. Marca el eje y repite el arranque. La posición final debe parecerse, porque el número de pasos no cambia.
  6. Al terminar, las cuatro fases quedan en bajo para no dejar el motor frenado en caliente.
  7. Si más adelante inviertes el sentido, la rampa se aplica igual: primero lento, luego rápido, luego lento.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1Arduino Uno R3placa-uno-r3original / oficialconsultar (>10 €)Usa esta o el clon. D9 es el PWM del mando.
1Uno R3 clone CH340placa-uno-ch340genérico / clonepoco (≈1–3 €)Mismo D9.
1TMP36sensor-tmp36ambos (oficial y genérico)poco (≈1–3 €)Mide la carga, pegado a ella sin hacer un corto.
1BJT / MOSFETmodulo-transistorgenérico / cloneirrisorio (<1 €)MOSFET o BJT de lógica, para el PWM. El pin no alimenta la carga.
1LED 5 mmmodulo-ledgenérico / cloneirrisorio (<1 €)Indicador del esfuerzo, en paralelo lógico con el mando, no como carga térmica.
2Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<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

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

  1. 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.
  2. Une todas las masas. Sin eso el PWM y la temperatura no comparten referencia.
  3. Pon la consigna unos grados por encima de la sala, por ejemplo 30, y sube el sketch.
  4. Con la carga fría el mando debe subir, y la integral que imprime no puede pasar de 40 ni bajar de -40.
  5. Cuando la temperatura cruce la consigna, el mando tiene que aflojar pronto. Si la integral no estuviera limitada, seguiría alta un buen rato.
  6. Tapa la carga para que se pase de temperatura. La integral baja hasta su tope negativo y el PWM se queda en cero.
  7. 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

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1Arduino Uno R3placa-uno-r3original / oficialconsultar (>10 €)Usa esta o el clon, como maestro.
1Uno R3 clone CH340placa-uno-ch340genérico / clonepoco (≈1–3 €)I2C en A4 y A5.
1BMP280sensor-bmp280genérico / clonepoco (≈1–3 €)Solo 3,3 V. El registro de identificador es 0xD0 y suele devolver 0x58.
1Level shiftermodulo-level-shiftergenérico / cloneirrisorio (<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

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

  1. 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.
  2. Sube el sketch y abre el monitor a 9600 baudios.
  3. En el listado de direcciones tiene que aparecer 76, o 77 si tu módulo viene en la otra dirección.
  4. La lectura del registro D0 debe enseñar 58 en hexadecimal. Ese byte es la identidad del BMP280.
  5. Si sale 77 y no 76, cambia las dos apariciones de 0x76 por 0x77 y vuelve a mirar el byte.
  6. Desconecta SDA y repite. El ACK desaparece y la lectura no puede traer el 58. Vuelves a ver el bus de verdad.
  7. No des por bueno un sensor porque la librería lo diga: aquí el byte tiene que verse.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1Arduino Uno R3placa-uno-r3original / oficialconsultar (>10 €)Usa esta o el clon. El test no cubre D0 ni D1, que están en el USB.
1Uno R3 clone CH340placa-uno-ch340genérico / clonepoco (≈1–3 €)Mismo mapa de pines.
1LED 5 mmmodulo-ledgenérico / cloneirrisorio (<1 €)En D13, el de la propia placa también vale, como resumen: fijo si todo fue bien.
1Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<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

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

  1. Quita shields y cables de D2 a D12. Deja el USB puesto para ver el monitor.
  2. Sube el sketch y ábrelo a 9600 baudios. Cada digital debe decir alto.
  3. D13 se queda encendido si el recuento de fallos es cero.
  4. 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.
  5. Quita el cable y resetea otra vez. El fallo desaparece: el pin no se ha quedado marcado.
  6. 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.
  7. No uses este test con un pin ya unido a un relé o a un motor. Primero desmonta, luego diagnostica.

Variantes

Depuración

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.PiezaFichaOrigenPrecio relativoEn este montaje
1Arduino Uno R3placa-uno-r3original / oficialconsultar (>10 €)Placa sin radio. Usa esta o el clon. No pongas aquí un ESP32: esta ficha es offline.
1Uno R3 clone CH340placa-uno-ch340genérico / clonepoco (≈1–3 €)Misma maqueta.
1LED 5 mmmodulo-ledgenérico / cloneirrisorio (<1 €)La luz de la habitación.
1Resistenciasmodulo-resistenciagenérico / cloneirrisorio (<1 €)220 ohmios con ese LED.
1Reedsensor-reedgenérico / cloneirrisorio (<1 €)Puerta. Con el imán cerca, el contacto va a masa y la puerta está cerrada.
1TMP36sensor-tmp36ambos (oficial y genérico)poco (≈1–3 €)Temperatura de la maqueta.
1Relé 5 Vmodulo-relegenérico / clonepoco (≈1–3 €)Activo a nivel bajo. Carga de 5 a 12 V, nunca la red.
1Pulsadormodulo-pulsadorgenérico / cloneirrisorio (<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

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

  1. Monta el LED en D5, el pulsador en D2, el reed en D3, el TMP36 en A0 y el relé en D7.
  2. 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.
  3. 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.
  4. Pulsa la luz. El LED cambia y el relé no tiene por qué seguirlo: son cosas distintas.
  5. Abre la puerta. El estado pasa a puerta y el relé cierra la carga de baja tensión.
  6. 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.
  7. 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

Depuración

Software: maquina-estados analogread digitalread digitalwrite serial