Al terminar, el LCD 16x2 enseña la temperatura en grados Celsius y la humedad relativa que mide el DHT11, renovadas cada dos segundos con millis para no congelar el programa. La misma lectura sale por el monitor serie, así que compruebas el sensor aunque el contraste de la pantalla todavía esté mal ajustado. Queda un medidor ambiente estable, listo para leer sin pulsar nada.
Al terminar, el LCD muestra la distancia en centimetros que mide el HC-SR04 y el monitor serie repite la misma cifra. Si el eco no vuelve a tiempo, la pantalla dice que estas fuera de rango en lugar de inventar un numero. El disparo del trigger y la lectura se repiten varias veces por segundo, asi que al mover la mano ves el valor cambiar sin pulsar nada.
Al terminar, la cerradura de juguete se abre solo si tecleas el PIN de cuatro cifras que vive en el propio sketch. No hay servidor, ni clave en la red, ni nada fuera de la placa: el teclado se lee por filas y columnas, el rele se activa unos segundos y luego vuelve a cerrar. La carga del rele es de 5 a 12 V en corriente continua. No se conmuta la red.
Al terminar, la alarma no suena en cuanto alimentas la placa: primero cuenta un retardo de armado para que cierres la puerta, y despues, si el reed detecta que se abre, espera un segundo retardo corto antes de activar el zumbador. Si vuelves a cerrar a tiempo, el aviso se cancela. El conteo entero sale de millis, con un filtrado sencillo del contacto para que un rebote no dispare la sirena.
Al terminar, la placa lee la humedad del suelo por A0 y, si la tierra esta seca por encima del umbral que calibres, activa el rele el tiempo justo para regar y luego lo corta aunque el sensor tarde en reaccionar. La bomba o la electrovalvula es una carga de 5 a 12 V en corriente continua, con su propia fuente. No se conmuta la red. Entre dos riegos hay una pausa para no encharcar si el sensor falla.
Al terminar, una barrera de juguete movida por el servo se levanta cuando el HC-SR04 ve un objeto a menos de veinte centimetros y vuelve a la posicion cerrada cuando el hueco queda libre. El servo solo recibe orden al cambiar de estado, para que no vibre, y el monitor serie te dice si la barrera esta abierta o cerrada junto con la distancia. Queda un paso automatico que puedes probar con la mano, sin empujar el brazo.
Al terminar, tres leds hacen de semaforo de juguete y cambian de fase con millis, sin quedarse bloqueados en una espera. El verde y el ambar duran un tiempo fijo; el rojo puede acortarse si el HC-SR04 detecta presencia cerca, siempre que el rojo lleve ya un minimo encendido. Si no pasa nadie, el rojo cumple su tiempo maximo y el ciclo sigue solo. El serie te dice la fase activa para que no dependas solo de los colores.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Código
// AP-FAL-07 Semaforo con presencia
// Pines: rojo D2, ambar D3, verde D4, TRIG D7, ECHO D8
const int PIN_ROJO = 2;
const int PIN_AMBAR = 3;
const int PIN_VERDE = 4;
const int PIN_TRIG = 7;
const int PIN_ECHO = 8;
enum Fase { VERDE, AMBAR, ROJO };
Fase fase = VERDE;
unsigned long inicioFase = 0;
const unsigned long T_VERDE = 5000;
const unsigned long T_AMBAR = 2000;
const unsigned long T_ROJO_MIN = 2000;
const unsigned long T_ROJO_MAX = 8000;
const float CERCA_CM = 40.0;
float medirCm() {
digitalWrite(PIN_TRIG, LOW);
delayMicroseconds(2);
digitalWrite(PIN_TRIG, HIGH);
delayMicroseconds(10);
digitalWrite(PIN_TRIG, LOW);
unsigned long us = pulseIn(PIN_ECHO, HIGH, 30000UL);
if (us == 0) {
return -1.0;
}
return us / 58.0;
}
void pintar() {
digitalWrite(PIN_VERDE, fase == VERDE ? HIGH : LOW);
digitalWrite(PIN_AMBAR, fase == AMBAR ? HIGH : LOW);
digitalWrite(PIN_ROJO, fase == ROJO ? HIGH : LOW);
}
void cambiar(Fase nueva, unsigned long ahora) {
fase = nueva;
inicioFase = ahora;
pintar();
if (fase == VERDE) Serial.println("Fase verde");
if (fase == AMBAR) Serial.println("Fase ambar");
if (fase == ROJO) Serial.println("Fase rojo");
}
void setup() {
Serial.begin(9600);
pinMode(PIN_ROJO, OUTPUT);
pinMode(PIN_AMBAR, OUTPUT);
pinMode(PIN_VERDE, OUTPUT);
pinMode(PIN_TRIG, OUTPUT);
pinMode(PIN_ECHO, INPUT);
cambiar(VERDE, millis());
}
void loop() {
unsigned long ahora = millis();
unsigned long lleva = ahora - inicioFase;
if (fase == VERDE && lleva >= T_VERDE) {
cambiar(AMBAR, ahora);
return;
}
if (fase == AMBAR && lleva >= T_AMBAR) {
cambiar(ROJO, ahora);
return;
}
if (fase == ROJO) {
float cm = medirCm();
bool presencia = (cm > 2.0 && cm < CERCA_CM);
if (lleva >= T_ROJO_MAX || (presencia && lleva >= T_ROJO_MIN)) {
cambiar(VERDE, ahora);
}
}
}
Pasos
Monta una sola placa y selecciona Arduino Uno. Conecta el led rojo a D2, el ambar a D3 y el verde a D4, con sus masas a GND.
Conecta TRIG del HC-SR04 a D7, ECHO a D8, VCC a 5 V y GND a masa, apuntando hacia donde vas a poner la mano.
Sube el sketch y abre el monitor serie a 9600. Deben sucederse verde, ambar y rojo sin que el programa se quede mudo.
Durante el rojo, no pongas nada delante: la fase debe agotar unos ocho segundos antes de volver al verde.
Reinicia, espera al rojo y acerca la mano a menos de 40 cm cuando ya lleve un par de segundos. El verde tiene que adelantarse.
Comprueba que en el primer instante del rojo la presencia no lo salta: el minimo de dos segundos se respeta.
Si un color no enciende, cambia solo ese modulo antes de tocar los tiempos.
Variantes
Alarga T_VERDE para un cruce de juguete mas tranquilo, sin tocar el sensor.
Baja CERCA_CM si el semaforo se pone verde al detectar la mesa de enfrente.
Anade un segundo grupo de leds en pines libres para el otro sentido, apagado cuando este este en verde.
Depuración
Si nunca adelanta el verde, ECHO y TRIG estan cruzados o la mano esta mas lejos de 40 cm. Mira el serie y acerca mas el sensor.
Si un led queda fijo, su pin no coincide con D2, D3 o D4 o el modulo es activo a nivel bajo.
Si las fases van a trompicones, pulseIn esta esperando el tope de 30 ms porque no hay eco: eso es normal al aire libre, no es un bloqueo de varios segundos.
Si los tres leds lucen a la vez, comparten un cable de senal o la masa esta suelta y se encienden por fugas.
Al terminar, el rele se comporta como un termostato de verdad: enciende la carga cuando la temperatura baja del umbral bajo y no la apaga hasta llegar al umbral alto. Esa histéresis evita que el rele tabletee cuando el sensor ronda un solo numero. Entre medias no hace nada aunque la lectura se mueva un poco. La carga es de 5 a 12 V en corriente continua. No se conmuta la red. Si el DHT11 falla una lectura, el rele se queda como estaba.
Conmuta un calentador o un ventilador de juguete. Carga de 5 a 12 V. No se conmuta la red.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Código
// AP-FAL-08 Termostato on/off con histeresis
// Pines: DHT D2, rele D8
// Umbral alto y umbral bajo: no es un unico punto de conmutacion.
// Carga de 5 a 12 V. No se conmuta la red.
#include <DHT.h>
const int PIN_DHT = 2;
const int PIN_RELE = 8;
const byte RELE_ON = LOW;
const byte RELE_OFF = HIGH;
const float UMBRAL_ALTO = 25.0;
const float UMBRAL_BAJO = 22.0;
const unsigned long PERIODO_MS = 2000;
DHT dht(PIN_DHT, DHT11);
bool calentando = false;
void aplicarRele() {
digitalWrite(PIN_RELE, calentando ? RELE_ON : RELE_OFF);
}
void setup() {
Serial.begin(9600);
pinMode(PIN_RELE, OUTPUT);
dht.begin();
calentando = false;
aplicarRele();
Serial.println("Termostato listo");
}
void loop() {
static unsigned long anterior = 0;
unsigned long ahora = millis();
if (ahora - anterior < PERIODO_MS) {
return;
}
anterior = ahora;
float t = dht.readTemperature();
if (isnan(t)) {
Serial.println("Lectura no valida, el rele no cambia");
return;
}
if (calentando) {
if (t >= UMBRAL_ALTO) {
calentando = false;
}
} else {
if (t <= UMBRAL_BAJO) {
calentando = true;
}
}
aplicarRele();
Serial.print("T ");
Serial.print(t, 1);
Serial.print(" C ");
Serial.print("alto ");
Serial.print(UMBRAL_ALTO, 1);
Serial.print(" bajo ");
Serial.print(UMBRAL_BAJO, 1);
Serial.println(calentando ? " CALOR" : " REPOSO");
}
Pasos
Monta una sola placa y selecciona Arduino Uno. Conecta el DHT11: datos a D2, VCC a 5 V y GND a masa.
Instala la libreria DHT sensor library desde el gestor de librerias del IDE.
Conecta el IN del rele a D8 y su alimentacion de bobina a 5 V y GND. Deja la carga sin conectar en el primer ensayo.
Sube el sketch y abre el serie a 9600. Anota la temperatura de la sala.
Pon UMBRAL_BAJO un grado por encima de la sala y UMBRAL_ALTO dos grados mas alto, vuelve a subir y comprueba que entra en CALOR.
Calienta el sensor con los dedos, sin mojarlo, hasta pasar el umbral alto y confirma que pasa a REPOSO y no vuelve a encender al bajar solo unas decimas.
Conecta entonces una carga de 5 a 12 V en COM y NO. No se conmuta la red.
Variantes
Invierte la logica si lo que quieres enfriar: enciende el rele por encima del umbral alto y apagalo por debajo del umbral bajo.
Separa mas los dos umbrales si el rele todavia cicla porque el sensor esta pegado a la propia carga.
Muestra la humedad en el serie sin usarla para decidir, y deja la histéresis solo con la temperatura.
Depuración
Si el rele oscila cada dos segundos, has dejado los dos umbrales iguales o casi: tienen que ser un umbral alto y un umbral bajo distintos.
Si nunca calienta, la sala esta por encima del umbral bajo. Baja ese numero o enfria el sensor, no puentes el rele.
Si el serie dice lectura no valida, DATA no esta en D2 o el sensor esta al reves.
Si la carga es un aparato de la red electrica, no lo conectes: este rele solo debe ver de 5 a 12 V.
Al terminar, cada cinco segundos la placa lee el potenciometro y anade una linea al archivo REGISTRO.CSV de la tarjeta SD, con el tiempo en millis y el valor analogico. Puedes apagar, sacar la tarjeta y abrir el archivo en el ordenador para ver que las medidas siguen ahi. El chip select queda fijo en D10 y el resto del bus es el SPI del nucleo: D11, D12 y D13. Si la tarjeta no arranca, el serie lo dice y no finge que ha guardado nada.
Genera el dato que se registra, del extremo a A0. Sirve para probar el archivo sin otro sensor.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Código
// AP-FAL-09 Registro en tarjeta SD
// Pines: CS D10, MOSI D11, MISO D12, SCK D13, potenciometro A0
// Libreria SD del nucleo. SPI del nucleo.
#include <SPI.h>
#include <SD.h>
const int PIN_CS = 10;
const int PIN_POT = A0;
const unsigned long PERIODO_MS = 5000;
bool sdOk = false;
void setup() {
Serial.begin(9600);
sdOk = SD.begin(PIN_CS);
if (!sdOk) {
Serial.println("Fallo SD. Revisa CS en D10, SPI y la tarjeta.");
return;
}
Serial.println("SD lista en D10");
}
void loop() {
if (!sdOk) {
return;
}
static unsigned long anterior = 0;
unsigned long ahora = millis();
if (ahora - anterior < PERIODO_MS) {
return;
}
anterior = ahora;
int valor = analogRead(PIN_POT);
File f = SD.open("REGISTRO.CSV", FILE_WRITE);
if (!f) {
Serial.println("No se abre REGISTRO.CSV");
return;
}
f.print(ahora);
f.print(",");
f.println(valor);
f.close();
Serial.print("Guardado ");
Serial.print(ahora);
Serial.print(",");
Serial.println(valor);
}
Pasos
Formatea la tarjeta en FAT32 con un lector del ordenador y dejala vacia, con nombre de volumen corto.
Monta una sola placa. Conecta CS a D10, MOSI a D11, MISO a D12, SCK a D13, VCC y GND del modulo SD.
Conecta el cursor del potenciometro a A0 y los extremos a 5 V y GND.
Inserta la tarjeta, sube el sketch y abre el serie a 9600. Tiene que decir que la SD esta lista.
Gira el potenciometro y espera dos o tres lineas de Guardado. Apaga la placa antes de sacar la tarjeta.
Lee REGISTRO.CSV en el ordenador y comprueba que cada linea trae millis y el valor, y que el valor cambio al girar.
Si begin falla, no sigas escribiendo a ciegas: revisa el pin D10 y que la tarjeta entre hasta el fondo.
Variantes
Sustituye el potenciometro por un sensor analogico en el mismo A0 cuando el archivo ya se abre bien.
Baja el periodo a un segundo para una prueba corta y subelo despues para no llenar la tarjeta.
Anade una cabecera solo si el archivo no existe, abriendolo antes en lectura, sin cambiar los pines SPI.
Depuración
Si el serie repite Fallo SD, CS no esta en D10, la tarjeta no es FAT o MOSI y MISO estan cruzados.
Si begin va bien pero no se abre el archivo, el nombre no cabe en 8.3 o la tarjeta esta protegida contra escritura.
Si el ordenador no ve datos nuevos, sacaste la tarjeta sin cerrar el archivo o sin apagar: este sketch cierra tras cada linea, asi que espera al mensaje Guardado.
Si D13 parpadea pero no hay archivo, estas mirando otra tarjeta u otra ranura.
Al terminar, el LCD muestra la fecha y la hora que mantiene el DS3231 por I2C, y siguen avanzando aunque reinicies la placa, porque el reloj no depende del millis de la Uno. La primera vez que el modulo ha perdido la pila, el sketch carga la hora de compilacion; despues no la pisa en cada arranque. El monitor serie repite la hora una vez por segundo para que compruebes el bus aunque el contraste del LCD este cerrado.
Al terminar, el LCD muestra un menu de tres ajustes y puedes recorrerlo con dos pulsadores y cambiar el valor con el tercero, sin que un rebote te salte varias lineas. El programa distingue navegar de editar con una maquina de estados corta, asi que el boton de aceptar no se mezcla con el de avanzar. Los valores se ven tambien por el monitor serie. No se guarda nada todavia: al reiniciar vuelven los valores iniciales, y eso es justo lo que este montaje deja funcionando.
Al terminar, el monitor serie muestra la tension en el pin A0 y la tension exterior estimada con un divisor de dos resistencias iguales de 10 kΩ. Como las dos valen lo mismo, fuera hay el doble que en el pin. La entrada de la placa, que es el nudo del divisor, no debe pasar de 5 V: con este par, la tension que mides por fuera no debe pasar de 10 V. No hay pantalla; lees el resultado en el serie y compruebas con una pila o una fuente baja antes de fiarte del numero.
10 kΩ. Rama baja del divisor, entre A0 y GND. La entrada del pin no debe pasar de 5 V.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Código
// AP-FAL-12 Voltimetro con divisor resistivo
// Pines: A0 es el nudo entre dos resistencias de 10 kOhm.
// La entrada del pin no debe pasar de 5 V.
const int PIN_MEDIDA = A0;
const float VREF = 5.0;
const float R_ALTA = 10000.0; // 10 kOhm
const float R_BAJA = 10000.0; // 10 kOhm
const unsigned long PERIODO_MS = 400;
void setup() {
Serial.begin(9600);
Serial.println("Voltimetro listo. Entrada A0 maximo 5 V.");
}
void loop() {
static unsigned long anterior = 0;
unsigned long ahora = millis();
if (ahora - anterior < PERIODO_MS) {
return;
}
anterior = ahora;
int adc = analogRead(PIN_MEDIDA);
float vPin = adc * (VREF / 1023.0);
float vExt = vPin * (R_ALTA + R_BAJA) / R_BAJA;
Serial.print("ADC ");
Serial.print(adc);
Serial.print(" Vpin ");
Serial.print(vPin, 2);
Serial.print(" V Vext ");
Serial.print(vExt, 2);
Serial.println(" V");
if (vPin > 4.8) {
Serial.println("Casi al tope: no subas mas la tension exterior.");
}
}
Pasos
Monta una sola placa y selecciona Arduino Uno. No conectes todavia la tension que quieres medir.
Pon la resistencia de 10 kΩ entre el punto exterior y A0, y la otra de 10 kΩ entre A0 y GND.
Une la masa de la fuente que vas a medir con el GND de la placa. Sin esa masa comun la lectura no vale.
Sube el sketch y abre el serie a 9600. Con la entrada a masa, Vpin tiene que quedar cerca de cero.
Prueba primero con una pila de 1,5 V o de 9 V en el extremo exterior, nunca con la red, y compara Vext con un polimetro si lo tienes.
Si Vpin se acerca a 5 V, para: la entrada no debe pasar de 5 V. Con 10 kΩ y 10 kΩ el exterior no debe pasar de 10 V.
Gira o cambia la fuente solo dentro de ese limite y comprueba que Vext vale aproximadamente el doble de Vpin.
Variantes
Cambia la rama alta a otro modulo de resistencia, por ejemplo 20 kΩ o el valor que tengas, y actualiza R_ALTA sin mover el pin A0. Recalcula el maximo para que el nudo siga bajo 5 V.
Promedia ocho lecturas seguidas antes de imprimir si el ultimo digito baila.
Mide solo Vpin, con la rama alta en cero ohmios logicos, cuando la tension ya es menor de 5 V y no necesitas divisor. En este montaje las dos resistencias siguen siendo de 10 kΩ.
Depuración
Si el numero flota con nada conectado, falta la resistencia de 10 kΩ hacia GND o A0 no es el nudo.
Si Vext sale el doble de lo que marca el polimetro, has contado dos veces el factor o las dos resistencias no son iguales.
Si al conectar la masa de la fuente la placa se reinicia, esa fuente no era aislada o has cruzado polos. Quita el cable.
Si alguien acerca tension de la red al divisor, corta el experimento: este circuito solo sirve para tensiones bajas y el pin no puede pasar de 5 V.
Al terminar, el led cambia de brillo con PWM segun lo que ve el LDR: a mas luz en el divisor, mas cuentas en A0 y mas pulso alto en D9. No es un encendido seco, es un analogWrite proporcional, y el serie te ensena el valor leido y el duty para que ajustes la resistencia si el led se queda siempre apagado o siempre a tope. El montaje queda reaccionando solo, sin pulsador.
Al terminar, la cafetera de juguete recorre reposo, calentando, lista y sirviendo con un enum y un switch. Cada fase enciende su led y pasa a la siguiente cuando vence su tiempo o cuando pulsas, contando con millis. No hay esperas bloqueantes largas: mientras calienta, el programa sigue dando vueltas y puede atender el pulsador al llegar a lista. Un pitido corto avisa al pasar a lista, tambien cortado por tiempo. Queda un ciclo que puedes repetir sin reiniciar la placa.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Código
// AP-FAL-14 Cafetera de juguete
// Pines: boton D2, calor D3, lista D4, servir D5, zumbador D6
// Maquina de estados con enum y switch. Sin esperas bloqueantes.
const int PIN_BOTON = 2;
const int PIN_CALOR = 3;
const int PIN_LISTA = 4;
const int PIN_SERVIR = 5;
const int PIN_ZUM = 6;
enum Estado { REPOSO, CALENTANDO, LISTA, SIRVIENDO };
Estado estado = REPOSO;
const unsigned long T_CALENTAR = 8000;
const unsigned long T_SERVIR = 4000;
const unsigned long T_PITIDO = 150;
unsigned long tFase = 0;
unsigned long finPitido = 0;
bool pitido = false;
bool botonAntes = HIGH;
void leds(bool calor, bool lista, bool servir) {
digitalWrite(PIN_CALOR, calor ? HIGH : LOW);
digitalWrite(PIN_LISTA, lista ? HIGH : LOW);
digitalWrite(PIN_SERVIR, servir ? HIGH : LOW);
}
void entrar(Estado nuevo, unsigned long ahora) {
estado = nuevo;
tFase = ahora;
if (nuevo == LISTA) {
pitido = true;
finPitido = ahora + T_PITIDO;
}
Serial.println(
nuevo == REPOSO ? "REPOSO" :
nuevo == CALENTANDO ? "CALENTANDO" :
nuevo == LISTA ? "LISTA" : "SIRVIENDO");
}
bool pulsado() {
bool ahoraBtn = digitalRead(PIN_BOTON);
bool flanco = (botonAntes == HIGH && ahoraBtn == LOW);
botonAntes = ahoraBtn;
return flanco;
}
void setup() {
Serial.begin(9600);
pinMode(PIN_BOTON, INPUT_PULLUP);
pinMode(PIN_CALOR, OUTPUT);
pinMode(PIN_LISTA, OUTPUT);
pinMode(PIN_SERVIR, OUTPUT);
pinMode(PIN_ZUM, OUTPUT);
digitalWrite(PIN_ZUM, LOW);
leds(false, false, false);
entrar(REPOSO, millis());
}
void loop() {
unsigned long ahora = millis();
if (pitido) {
digitalWrite(PIN_ZUM, HIGH);
if (ahora >= finPitido) {
pitido = false;
digitalWrite(PIN_ZUM, LOW);
}
}
bool clic = pulsado();
switch (estado) {
case REPOSO:
leds(false, false, false);
if (clic) {
entrar(CALENTANDO, ahora);
}
break;
case CALENTANDO:
leds(true, false, false);
if (ahora - tFase >= T_CALENTAR) {
entrar(LISTA, ahora);
}
break;
case LISTA:
leds(false, true, false);
if (clic) {
entrar(SIRVIENDO, ahora);
}
break;
case SIRVIENDO:
leds(false, false, true);
if (ahora - tFase >= T_SERVIR) {
entrar(REPOSO, ahora);
}
break;
default:
entrar(REPOSO, ahora);
break;
}
}
Pasos
Monta una sola placa y selecciona Arduino Uno. Conecta el pulsador entre D2 y GND.
Conecta el led de calor a D3, el de lista a D4, el de servicio a D5 y el zumbador a D6, con las masas a GND.
Sube el sketch y abre el serie a 9600. Debe quedar en REPOSO con los tres leds apagados.
Pulsa una vez. Tiene que pasar a CALENTANDO y encender solo el led de D3 durante unos ocho segundos, mientras el serie sigue vivo.
Al cumplir el tiempo suena un pitido corto y queda LISTA. Pulsa otra vez para SIRVIENDO y espera a que vuelva solo a REPOSO.
Durante CALENTANDO no hace falta pulsar: la transicion la hace el tiempo. Comprueba que la placa no se queda congelada.
Recorre el ciclo dos veces seguidas para ver que el enum vuelve a REPOSO sin reiniciar.
Variantes
Acorta T_CALENTAR y T_SERVIR para ensayar el ciclo en la mesa, y alarga los tiempos cuando el juego este claro.
Anade un estado de error si mantienes el pulsador al alimentar, usando el mismo switch.
Cambia el led de servir por un servo que incline un vaso de juguete solo mientras el estado es SIRVIENDO.
Depuración
Si al pulsar no sale de REPOSO, el pulsador no va a GND o el pin no es D2.
Si un led no coincide con la fase, has cruzado D3, D4 y D5.
Si el pitido no corta, finPitido no se compara con millis o el zumbador es activo a nivel bajo.
Si el ciclo tarda pero el serie deja de imprimir hasta el final, has metido una espera bloqueante dentro de un case. Este sketch no debe parar el loop.
Al terminar, dos placas se hablan por SoftwareSerial sin tocar los pines 0 y 1, que siguen libres para el monitor serie de cada una. La placa A envia una frase cada dos segundos por su TX en D11 y la placa B la recibe en su RX en D10, la copia al serie y conmuta un led. El cable va cruzado, TX con RX, y las masas van unidas. En el codigo los dos sketches van separados por un comentario claro: grabas cada bloque en su placa, no los dos a la vez en la misma.
Va en la placa B, patilla D4. Parpadea al recibir bytes.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Código
// AP-FAL-15 Dos placas por SoftwareSerial
// Cada bloque se graba en una placa distinta.
// RX en D10, TX en D11. No usa los pines 0 ni 1.
/* ============================================================
SKETCH PLACA A (emisora)
Graba este programa solo en la primera placa.
============================================================ */
#include <SoftwareSerial.h>
SoftwareSerial enlace(10, 11); // RX, TX
void setup() {
Serial.begin(9600);
enlace.begin(9600);
Serial.println("Placa A lista");
}
void loop() {
static unsigned long anterior = 0;
unsigned long ahora = millis();
if (ahora - anterior >= 2000) {
anterior = ahora;
enlace.println("hola desde A");
Serial.println("Enviado");
}
while (enlace.available()) {
Serial.write(enlace.read());
}
}
/* ============================================================
SKETCH PLACA B (receptora)
Graba este programa solo en la segunda placa.
============================================================ */
#include <SoftwareSerial.h>
SoftwareSerial enlace(10, 11); // RX, TX
const int PIN_LED = 4;
void setup() {
Serial.begin(9600);
enlace.begin(9600);
pinMode(PIN_LED, OUTPUT);
Serial.println("Placa B lista");
}
void loop() {
while (enlace.available()) {
char c = enlace.read();
Serial.write(c);
digitalWrite(PIN_LED, !digitalRead(PIN_LED));
}
}
Pasos
Prepara dos placas del mismo tipo: dos Uno R3 o, en su lugar, dos Uno CH340. No mezcles el recuento.
Une GND con GND. Cruza A D11 con B D10 y B D11 con A D10. No uses D0 ni D1 para ese enlace.
Pon el led en D4 de la placa B, con su masa al GND de esa misma placa.
Copia solo el bloque de la placa A, compilalo y subelo a la primera placa con el IDE en Arduino Uno.
Copia solo el bloque de la placa B en un sketch nuevo, subelo a la segunda placa y abre su monitor serie a 9600.
Alimenta las dos a la vez. En A debes ver Enviado cada dos segundos y en B la frase hola desde A, con el led cambiando.
Si B no recibe, invierte el cruce una sola vez y vuelve a probar antes de tocar el codigo.
Variantes
Haz que B conteste con un numero de linea usando su TX, para ver el texto de vuelta en el serie de A.
Baja la velocidad de ambos begin al mismo valor si el cable es largo; las dos placas tienen que ir iguales.
Sustituye el led de B por un rele de juguete en D4 cuando el enlace ya sea fiable, sin mover D10 ni D11.
Depuración
Si no llega nada, falta el GND comun o TX no cae en el RX de la otra placa.
Si el monitor serie de una placa se llena de basura, has pinchado el enlace en D0 o D1. Vuelve a D10 y D11.
Si solo una direccion falla, ese cable de cruce esta suelto; el otro sentido puede seguir bien.
Si las dos placas se programan igual por error, las dos hablaran a la vez por el mismo pin y no veras la frase.
Al terminar, la placa recorre las direcciones I2C de la 1 a la 126 y escribe en el monitor serie las que responden. Con el DS3231 conectado tienes que ver 0x68, que es su direccion fija, y asi compruebas SDA, SCL y la masa antes de intentar leer la hora. El barrido se repite cada pocos segundos usando millis, de modo que puedes enchufar o quitar el modulo y ver como aparece o desaparece sin volver a subir el sketch.
Dispositivo I2C de prueba. Debe aparecer en la direccion 0x68.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Código
// AP-FAL-16 Escaner I2C
// Pines: SDA A4, SCL A5. El DS3231 responde en 0x68.
#include <Wire.h>
const unsigned long PERIODO_MS = 3000;
void setup() {
Wire.begin();
Serial.begin(9600);
Serial.println("Escaner I2C en A4 y A5");
}
void loop() {
static unsigned long anterior = 0;
unsigned long ahora = millis();
if (anterior != 0 && ahora - anterior < PERIODO_MS) {
return;
}
anterior = ahora;
byte encontrados = 0;
Serial.println("Buscando...");
for (byte addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
byte err = Wire.endTransmission();
if (err == 0) {
Serial.print("Encontrado en 0x");
if (addr < 16) {
Serial.print('0');
}
Serial.println(addr, HEX);
encontrados++;
}
}
if (encontrados == 0) {
Serial.println("Ningun dispositivo. Revisa A4, A5, 5 V y GND.");
} else {
Serial.print("Total ");
Serial.println(encontrados);
}
Serial.println("---");
}
Pasos
Monta una sola placa y selecciona Arduino Uno. Conecta SDA del DS3231 a A4, SCL a A5, VCC a 5 V y GND a masa.
Sube el sketch. Wire forma parte del nucleo, no instales otra libreria para este escaner.
Abre el monitor serie a 9600 y espera el primer barrido.
Confirma que aparece 0x68. Esa es la direccion del DS3231.
Desconecta solo SDA, espera el siguiente barrido y comprueba que 0x68 desaparece. Vuelve a conectarlo.
Si tienes otro modulo I2C a 5 V, ponlo en paralelo en A4, A5, 5 V y GND y mira si sale una segunda direccion.
Anota las direcciones que veas: te serviran antes de escribir un programa que hable con ese chip.
Variantes
Reduce el periodo si quieres ver el efecto de un cable suelto al momento, sin cambiar los pines.
Cuando 0x68 sea estable, pasa al reloj con RTClib sabiendo que el bus ya responde.
Ignora en un if la direccion 0x68 si quieres listar solo otros dispositivos que anadas al mismo bus.
Depuración
Si no aparece nadie, SDA y SCL estan cruzados o no hay masa comun. Prueba a intercambiar A4 y A5 una sola vez.
Si salen casi todas las direcciones, SDA esta fija a masa o las pull-up no existen y el bus lee fantasmas.
Si el serie no abre, el baudaje no es 9600 o el cable USB es solo de carga.
Si aparece otra direccion distinta de 0x68 con solo el DS3231, hay un segundo chip en ese modulo, por ejemplo una EEPROM de respaldo: no es un fallo del escaner.
Al terminar, el brillo que dejas en el potenciometro se puede guardar en la EEPROM de la propia placa al pulsar el boton, y al reiniciar o al quitar el USB el led vuelve a ese ultimo ajuste sin que tengas que girar nada. El sketch usa EEPROM.h del nucleo y EEPROM.update para no gastar la celda si el valor no ha cambiado. El serie confirma lo recuperado al arrancar y lo guardado en cada pulsacion. El potencio sigue pudiendo previsualizar otro brillo antes de decidirte a grabarlo.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Código
// AP-FAL-17 Guardar el ultimo ajuste en EEPROM
// Pines: potenciometro A0, pulsador D2, LED PWM D9
// Libreria EEPROM.h del nucleo. Direccion 0.
#include <EEPROM.h>
const int PIN_POT = A0;
const int PIN_BOTON = 2;
const int PIN_LED = 9;
const int DIR = 0;
const unsigned long VENTANA_MS = 40;
byte brilloGuardado = 0;
bool seguirPot = false;
int potAlArrancar = 0;
bool estable = HIGH;
bool previo = HIGH;
unsigned long cambioBtn = 0;
void aplicar(byte brillo) {
analogWrite(PIN_LED, brillo);
}
bool flancoGuardar() {
bool lectura = digitalRead(PIN_BOTON);
unsigned long ahora = millis();
if (lectura != estable) {
if (ahora - cambioBtn >= VENTANA_MS) {
estable = lectura;
}
} else {
cambioBtn = ahora;
}
bool pulso = (previo == HIGH && estable == LOW);
previo = estable;
return pulso;
}
void setup() {
Serial.begin(9600);
pinMode(PIN_BOTON, INPUT_PULLUP);
pinMode(PIN_LED, OUTPUT);
brilloGuardado = EEPROM.read(DIR);
aplicar(brilloGuardado);
potAlArrancar = analogRead(PIN_POT);
Serial.print("Recuperado de EEPROM ");
Serial.println(brilloGuardado);
}
void loop() {
int lectura = analogRead(PIN_POT);
byte propuesto = lectura / 4;
if (abs(lectura - potAlArrancar) > 12) {
seguirPot = true;
}
if (seguirPot) {
aplicar(propuesto);
} else {
aplicar(brilloGuardado);
}
if (flancoGuardar()) {
EEPROM.update(DIR, propuesto);
brilloGuardado = propuesto;
seguirPot = false;
potAlArrancar = lectura;
aplicar(brilloGuardado);
Serial.print("Guardado en EEPROM ");
Serial.println(propuesto);
}
}
Pasos
Monta una sola placa y selecciona Arduino Uno. Conecta el cursor del potenciometro a A0 y los extremos a 5 V y GND.
Conecta el pulsador entre D2 y GND, y el led de brillo a D9 con su masa a GND.
Sube el sketch. EEPROM.h viene en el nucleo. Abre el serie a 9600 y anota el byte recuperado, que la primera vez sera basura o 255.
Gira el potenciometro hasta un brillo medio y pulsa una sola vez. El serie debe decir que lo ha guardado.
Cambia el potenciometro de sitio, desconecta el USB, vuelve a alimentar y comprueba que el led arranca en el brillo guardado antes de que muevas el mando.
Gira de nuevo sin pulsar y confirma que la vista previa cambia, pero un reset sin pulsar recupera otra vez el ultimo ajuste.
Pulsa varias veces sin mover el mando: update no debe reescribir si el byte ya es el mismo, aunque el serie confirme la accion.
Variantes
Guarda un segundo byte en la direccion 1, por ejemplo un tope, sin cambiar el pin del pulsador.
Usa el valor guardado como brillo minimo y deja que el potenciometro solo suba a partir de ahi.
Tras comprobar la EEPROM, enlaza este pulsador con el menu de tres ajustes para conservar el ultimo numero elegido.
Depuración
Si al reiniciar el led no vuelve al mismo brillo, no llegaste a pulsar, el pulsador no esta en D2 o estas mirando otro pin PWM.
Si el serie no dice Recuperado, el monitor no esta a 9600 o el sketch no arranco.
Si cada pulsacion guarda un numero distinto sin girar el mando, el cursor de A0 flota porque falta un extremo del potenciometro.
Si esperabas un chip EEPROM externo y no lo hay, es normal: esta practica usa solo la memoria interna a traves de EEPROM.h.
Al terminar, cada flanco de bajada en el pin 2 suma un pulso dentro de una interrupcion, aunque el loop este ocupado imprimiendo por el serie. El contador es volatile y solo se copia con las interrupciones paradas un instante, para no leer un numero a medias. El monitor serie enseña el total varias veces por segundo. Puedes pulsar a mano y ver que la cuenta sube sin preguntar en cada vuelta del loop si el pin ha cambiado. El pin de la interrupcion es el 2, que en la Uno esta preparado para ello.
Genera el pulso. Entre el pin 2 y GND. El rebote mecanico puede contar varios pulsos por pulsacion.
La franja sale del catálogo de Hardware. No es un precio de tienda.
Cableado
Código
// AP-FAL-18 Contar pulsos con interrupcion
// Pin 2 = INT0. Flanco de bajada. El contador es volatile.
const byte PIN_PULSO = 2;
volatile unsigned long pulsos = 0;
void contarPulso() {
pulsos++;
}
void setup() {
Serial.begin(9600);
pinMode(PIN_PULSO, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(PIN_PULSO), contarPulso, FALLING);
Serial.println("Contador listo en el pin 2");
}
void loop() {
static unsigned long anterior = 0;
unsigned long ahora = millis();
if (ahora - anterior < 300) {
return;
}
anterior = ahora;
noInterrupts();
unsigned long copia = pulsos;
interrupts();
Serial.print("Pulsos ");
Serial.println(copia);
}
Pasos
Monta una sola placa y selecciona Arduino Uno. Conecta el pulsador entre el pin 2 y GND. No uses otro pin.
Sube el sketch y abre el serie a 9600. Tiene que imprimir Pulsos 0 de forma periodica.
Pulsa varias veces. La cuenta debe subir. Un pulsador mecanico a menudo suma mas de uno por golpe: es el rebote, no un fallo del pin.
Mantente sin pulsar y comprueba que el numero se queda quieto entre lineas del serie.
Reinicia la placa y confirma que la cuenta vuelve a cero: esta variable no vive en EEPROM.
No llames a Serial dentro de contarPulso. La interrupcion solo incrementa.
Variantes
Pasa el mismo pin 2 a un sensor de efecto hall o a un reed si quieres pulsos mas limpios que los de un pulsador.
Cambia FALLING por RISING solo si tu sensor da el flanco al reves y el comun sigue en GND.
Cuando la cuenta sea fiable, copia el total a la EEPROM de vez en cuando desde el loop, nunca desde la propia interrupcion.
Depuración
Si la cuenta no se mueve, el pulsador no esta en el pin 2 o no cierra a GND.
Si sube sin que pulses, el cable del pin 2 hace de antena: acortalo y asegurate de que el pull-up interno esta activo.
Si un solo golpe suma diez o veinte, es rebote mecanico. Para contarlo como un solo evento pasa al montaje del antirrebote, o filtra por tiempo fuera de la interrupcion.
Si el serie se cuelga al pulsar, has metido una impresion o una espera dentro de contarPulso. Dejala vacia salvo el incremento.
Al terminar, el pulsador pasa por una funcion reutilizable que solo devuelve un flanco cuando el nivel lleva quieto el tiempo de filtro. El led de D8 conmuta una vez por pulsacion valida, y el serie cuenta esos flancos, no los rebotes. La funcion no guarda un unico boton escondido: recibe una estructura con el pin y su estado, asi que puedes declararla otra vez para un segundo pulsador sin copiar el filtro. El programa sigue libre gracias a millis.
Al terminar, cuatro reles se gobiernan con cuatro pulsadores y el LCD va mostrando el nombre de cada uno y si esta en ON o en OFF. Los nombres son luz, bomba, ventilador y cerradura, las cuatro de juguete. Cada rele solo puede conmutar una carga de 5 a 12 V en corriente continua, con su propia fuente. No se conmuta la red. El filtrado evita que un rebote cambie dos veces el mismo canal, y el serie repite el nombre y el estado cada vez que conmutas.