Construye tu Propio Monstruo en una Caja: Proyecto de Robótica con Arduino
Componentes y Suministros
| Caja de Madera / Cajón | 1 |
| Arduino UNO | 1 |
| Adafruit Wave Shield | 1 |
| Módulo Relé de 4 Canales SainSmart | 1 |
| Motor de Limpiaparabrisas | 1 |
| Luces LED Navideñas Rojas (100) | 1 |
| Máquina de Humo | 1 |
| Amplificador/Altavoz Alimentado | 1 |
Sobre el Proyecto
*** Actualizado a la Versión 4 ***
Recientemente reescribí el código de Arduino para este proyecto. Aquí un resumen de los cambios:
- El bucle principal ahora se implementa como una máquina de estados
- Todo el tiempo se basa en evaluaciones de tiempo de evento vs. tiempo actual
- El tiempo ya no depende de frames
- Se eliminó
delay()en el bucle principal - Se solucionó el problema de "puerto ocupado" al conectar con el IDE de Arduino
- Los relés ahora usan conexiones Normally Open (NO) en lugar de Normally Closed (NC) *** ¡Actualiza tus conexiones de relé! ***
- Se añadió LED de Movimiento Detectado
- Se añadió LED de Listo para Despertar
- Se añadió LED de Pausa
- Se eliminó el Interruptor de Disparo de Acción
- Se añadió la capacidad de agregar tiempo extra a Luces Rojas y Humo
- Se añadieron muchos comentarios
Encontrarás todo el código más reciente en GitHub.
¡Monstruo en una Caja!
Me encantan los accesorios de Halloween y he visto y leído sobre algunas construcciones geniales de Monstruo en una Caja el año pasado, así que decidí construir el mío. La idea es simple: crear la ilusión de que algún tipo de contenedor retiene a un monstruo peligroso a punto de escapar. La ilusión se puede crear con cualquier combinación de movimiento, sonido, luz, humo y, por supuesto, sorpresa.
¿Qué Hay en la Caja?!?
Querrás una caja lo suficientemente grande para parecer capaz de contener algo grande y aterrador. También ayuda a la ilusión si la caja parece vieja, gastada y apenas lo bastante fuerte para mantener al monstruo a raya. Al principio busqué en tiendas de segunda mano y en línea, pero no encontré nada que me gustara, así que la construí yo mismo. No fue muy difícil.
Usé 15 tablas de pino de 6 pies (listones de valla) de Home Depot para hacer una caja de aproximadamente 90 x 75 x 75 cm. Corté todas las tablas a medida con una sierra de mesa y ensamblé la caja usando una clavadora neumática de 18 gauge. Esto pareció funcionar bien, pero luego me di cuenta de que los clavos no eran lo suficientemente fuertes para mantener la caja unida debido a todos los golpes y rebotes que sufriría. Para reforzarla, la atornillé después con tornillos de 1 pulgada para paneles de yeso (desde el interior hacia afuera para que no se vieran).
Una vez construida, envejecí la madera con un proceso que usaba té negro, vinagre y lana de acero. Luego la decoré con plantillas y etiquetas que encontré en línea y/o creé e imprimí.

¡Cerebros!!!
El accesorio tiene 2 estados operativos: "Durmiendo" y "Despierto". Mientras duerme, la caja está relativamente tranquila y solo emite ronquidos y gruñidos leves. El monstruo se despierta cuando un niño que pide dulces activa un sensor de movimiento. Cuando el monstruo despierta, ruge, exhala humo, hace rebotar la tapa y el interior de la caja brilla en rojo.
Utilicé un Arduino Uno junto con un Wave Shield y un Módulo Relé para orquestar el comportamiento. El Wave Shield contiene una pequeña colección de sonidos de sueño y rugidos. Un sensor PIR proporciona la entrada al Arduino para cambiar entre los modos "durmiendo" y "despierto". Durante el modo despierto, el Arduino activa los canales del relé para accionar el motor de la tapa, las luces rojas, el generador de humo y reproducir los sonidos en el Wave Shield. Otra característica que añadí fue un Botón de Pausa, que me permite silenciar temporalmente al monstruo cuando pasan niños más pequeños.

¡Está Vivo!
Un elevador de tapa es un dispositivo que sube y baja la tapa rápidamente para que golpee y rebote como si el monstruo intentara escapar. Hay varias formas de hacerlo; las más populares son actuadores neumáticos y levas rotativas. Aunque creo que un actuador neumático controlado electrónicamente habría permitido movimientos de tapa más aleatorios, opté por una leva rotativa por ser la opción más simple y económica.
Compré un motor de limpiaparabrisas automotriz usado en un desguace local por $15. Fue gracioso: entré y pedí un motor. El chico detrás del mostrador preguntó "¿para qué marca y modelo de coche?" y cuando dije "no me importa", inclinó la cabeza y me miró de reojo. Después de explicarle el proyecto, ¡él también quiso construir uno!
Estos motores, independientemente de la marca/modelo, funcionan a 12 voltios, giran a una velocidad razonable y tienen un gran par: ¡perfecto! Nota: Asegúrate de que venga con algo del arnés de cables original, te facilitará mucho empalmar tus cables. También asegúrate de que tenga su brazo de manivela y poste excéntrico. Si el poste principal del motor es la única conexión física con la que trabajas, tu leva probablemente resbalará.
Monta el motor en algún lugar dentro de la caja. Lo puse en una de las paredes laterales. Recuerda que habrá una leva giratoria grande conectada a él y necesita poder girar libre y sin obstáculos de cualquier objeto, especialmente cables, que vayas a poner en la caja. Hay varios buenos tutoriales en YouTube sobre cómo trabajar con estos motores.

Corté una leva de 30 cm de diámetro de una pieza de contrachapado de 1/2 pulgada. Primero corté un círculo perfecto. La monté en el motor y la usé para determinar la altura requerida del motor en la caja. Solo quería que la tapa se levantara unos 5 cm. Después de este ajuste, usé una sierra de calar para cortar "dientes" aleatorios en la leva, intentando mantenerlos en anchos y profundidades aleatorias.

Por último, monté un rodillo en la parte inferior de la tapa usando 2 soportes metálicos en L, un separador de nailon de 1 pulgada y un tornillo de carro. El rodillo se posiciona justo encima de la leva para que ruede a lo largo de los dientes.

Usé el rail de 12 voltios de una fuente de alimentación ATX para alimentar el motor a través del relé controlado por el Arduino. Sí, alinear todo requiere un poco de prueba y error. Después de reposicionar el rodillo una o dos veces... bueno, tres veces... todo se alineó y la tapa comenzó a rebotar como se planeaba.
Aliento de Fuego: Humo y Luces
Usé un generador de humo barato de 400 vatios para los efectos de humo. De fábrica era controlado por radio. Para controlarlo con el Arduino, abrí el receptor y soldé dos cables a los contactos del botón de disparo. Esto me dio líneas que conecté a un canal de relé controlado por Arduino. Fue un hack simple que aprendí viendo unos videos de YouTube. También añadí una manguera de aspiradora vieja para dirigir el humo como un chorro desde justo debajo de la tapa. Me gustó el efecto visual y ayuda a reducir la condensación de vapor en la electrónica dentro de la caja.
Para las luces, simplemente corté un cable de extensión ligero y lo cableé a través de un canal de relé. La cadena de 100 luces LED rojas conectada a esto podía entonces encenderse y apagarse por el Arduino.



Usa tu Voz de Fuera
El Wave Shield tiene una salida de jack de 1/8 pulgada, así como 2 pines I/O para conexión directa. Usé un cable patch de 1/8 pulgada para conectar a un amplificador de bajo de 30 vatios que pedí prestado de mi sala de ensayo. Cualquier número de altavoces amplificados funcionarían, incluyendo altavoces de PC. Pero usar algo con buenos graves es definitivamente una ventaja.

¡Encadena Todo!
Compré cadenas de plástico en una tienda de Halloween e imprimí en 3D un candado. Envolver estos sobre la caja añadió profundidad y realismo a la ilusión.

¡Halloween!
Saqué la caja al jardín y escondí el sensor de movimiento en un arbusto cercano. Usé un largo cable de altavoz para conectar el sensor y poder experimentar con el posicionamiento de modo que el monstruo se despertara cuando alguien se acercara (unos 1.5 metros) a la caja.
Al caer la noche, me di cuenta de que todo el detalle del exterior de la caja se volvía difícil de ver. Finalmente la iluminé con un foco estroboscópico y quedé muy contento con el efecto.
Mientras los niños recorrían la entrada, notaban la caja gruñendo: algunos con curiosidad, otros realmente asustados. Al acercarse y cobrar vida el monstruo, saltaban, chillaban e incluso aplaudían. Fue un gran éxito.
Mejoras
Me cansé de buscar formas de esconder mi sensor de movimiento en los arbustos, así que pegué uno con silicona caliente en el vientre de una rata de plástico que puedo posicionar en la entrada o bajo el arbusto. También instalé un conector RJ11 en la parte trasera de la caja para conectar el sensor de la rata usando un cable telefónico de 15 metros, mucho mejor que los dos hilos de cable de altavoz que usaba antes.


También añadí un interruptor y algunos LEDs indicadores a esa nueva placa. El interruptor PAUSA todas las funciones de la caja sin apagarla. Mi interruptor principal de apagado es la regleta de enchufes interior y es incómodo de acceder, además con este solo se usa voltaje de señal (5V) en el interruptor externo. Este interruptor ha sido genial para detener el monstruo para niños más pequeños y otros que piden echar un vistazo dentro en la noche de Halloween.
¡Ah! Drapo cadenas de plástico sobre la caja para efecto y a veces son difíciles de manejar, especialmente si necesito entrar en la caja durante la noche. Para facilitar esto, até con bridas mosquetones de resorte negros a los extremos de las cadenas. Esto me permite desenganchar/enganchar rápidamente los extremos a argollas que atornillé en las esquinas inferiores de la caja. No es muy alta tecnología pero es de gran ayuda.
El Futuro
Hay algunas cosas que quiero añadir en el futuro. Quizás control R/C para poder pausar o disparar el monstruo a distancia. También añadiré algún tipo de retroalimentación al elevador de tapa para que el Arduino pueda "saber" cuándo la tapa está abierta o cerrada. Varias veces el elevador se detuvo en un punto alto del ciclo, lo que hacía visible las "tripas" de la caja hasta el siguiente ciclo de despertar. Por último, puedo conectar la iluminación/externa/estrobos al Arduino para permitir que sean controlados por el programa y/o el mando R/C.
Repositorio en GitHub
Encontrarás el código más reciente y diagramas de cableado aquí en GitHub.
Código
Monster In A Box Sketch (C/C++)
/* Wave Shield Pins in Use: 2, 3, 4, 5, 10, 11, 12 & 13
Pins 13, 12, 11 are always used by the SD card (they are the only pins that have a high speed SPI interface).
Then there are 5 other pins used to talk to the DAC and SD card, but they can be set to connect to any arduino pin.
However, by default, the library is configured to use pins 10 (for SD card) and pins 2, 3, 4 and 5 for the DAC.
To change these pins requires modifying the library - the pins are referenced by their 'hardware' pin names (ie PORTD, etc) not by arduino pins.
That means pins 6, 7, 8, 9 and the 6 analog in pins (also known as digital i/o pins 14-20) are available.
https://learn.adafruit.com/adafruit-wave-shield-audio-shield-for-arduino/faq */
#include "MonsterSounds.h"
#define RESERVED_00 0 // Reserved for Serial RX
#define RESERVED_01 1 // Reserved for Serial TX
#define RESERVED_02 2 // Reserved for Wave Shield
#define RESERVED_03 3 // Reserved for Wave Shield
#define RESERVED_04 4 // Reserved for Wave Shield
#define RESERVED_05 5 // Reserved for Wave Shield
#define FOG_MACHINE 6 // Connect Digital Pin on Arduino to Relay Module
#define RED_LEDS 7 // Connect Digital Pin on Arduino to Relay Module
#define LID_BOUNCER 8 // Connect Digital Pin on Arduino to Relay Module
#define RESERVED_09 9 // Connect Digital Pin on Arduino to Relay Module
#define RESERVED_10 10 // Reserved for Wave Shield
#define RESERVED_11 11 // Reserved for Wave Shield
#define RESERVED_12 12 // Reserved for Wave Shield
#define RESERVED_13 13 // Reserved for Wave Shield
#define PIR_SENSOR A0 // PIR Input
#define MOTION_LED A1 // LED: lights when motion is detected (regardless of pause/sleep/wake state)
#define PAUSED_LED A2 // LED: lights when system is paused
#define READY_LED A3 // LED: lights when monster is in READY_TO_WAKE state
#define PAUSE_BUTTON A4 // Pause Switch Input
#define DEBUG_BUTTON A5 // Debug Switch Input
// Effects Timer Settings - in Seconds (EDIT THESE)
#define WAKE_DELAY 30 // Minimum amount of time between 'awake' occurrences in Seconds
#define WAKE_DELAY_DEBUG 10 // WAKE_DELAY override when DEBUG switch is engaged
#define SLEEP_SOUND_DELAY 1 // Number of seconds to wait between attempting to fire the next 'sleep' sound
#define WAKE_MIN 3 // Minimum amount of 'awake' time in Seconds
#define WAKE_MAX 5 // Maximum amount of 'awake' time in Seconds
#define RED_LIGHT_EXTRA_TIME 1 // Allows the red lights to run a bit longer than the lid bouncer, if desired
#define SMOKE_EXTRA_TIME 2 // Allows the smoke to run a bit longer than the lid bouncer, if desired
// Effects Timers Settings - in Milliseconds (DO NOT EDIT THESE)
#define WAKE_DELAY_MILLIS WAKE_DELAY * 1000
#define WAKE_DELAY_DEBUG_MILLIS WAKE_DELAY_DEBUG * 1000
#define SLEEP_SOUND_DELAY_MILLIS SLEEP_SOUND_DELAY * 1000
#define WAKE_MIN_MILLIS WAKE_MIN * 1000
#define WAKE_MAX_MILLIS WAKE_MAX * 1000
#define RED_LIGHT_EXTRA_TIME_MILLIS RED_LIGHT_EXTRA_TIME * 1000
#define SMOKE_EXTRA_TIME_MILLIS SMOKE_EXTRA_TIME * 1000
MonsterSounds sounds;
static unsigned long timeSinceLastSnore= 0;
static unsigned long wakeAllowedTimer = 0;
static unsigned long lidBounceTimer = 0;
static unsigned long lidBounceDuration = 0;
static unsigned long smokeTimer = 0;
static unsigned long smokeDuration = 0;
static unsigned long redLightTimer = 0;
static unsigned long redLightDuration = 0;
enum States {
STATE_INITIALIZE, // Only while running setup() and first time into loop()
STATE_PAUSED, // Turn off all sounds and effects
STATE_SLEEPING, // No effects, sleeping sounds, does not allow awake to be triggered
STATE_READY_TO_WAKE, // No effects, sleeping sounds, allows awake to be triggered
STATE_AWAKE // Fires effects and monster awake sounds
};
States state = STATE_INITIALIZE;
void setup() {
Serial.begin(9600);
pinMode(LID_BOUNCER, OUTPUT);
pinMode(RED_LEDS, OUTPUT);
pinMode(FOG_MACHINE, OUTPUT);
pinMode(PAUSED_LED, OUTPUT);
pinMode(MOTION_LED, OUTPUT);
pinMode(READY_LED, OUTPUT);
stopAllEffects();
pinMode(PIR_SENSOR, INPUT);
pinMode(PAUSE_BUTTON, INPUT_PULLUP);
pinMode(DEBUG_BUTTON, INPUT_PULLUP);
sounds.initialize();
sounds.playSystemReady();
delay(1000);
Serial.println();
Serial.print("*** System Ready ***");
Serial.println();
}
/*
* NOTE: All buttons are using pullups, thus LOW means the button is PRESSED
* Keep in mind the pull-up means that switch logic is inverted.
* It goes HIGH when it's open, and LOW when it's pressed.
*
* The PIR motion sensor does NOT behave this way.
*/
/*
* Main processing loop
* - Manages the Monster's State Machine
*/
void loop() {
boolean pauseSwitchClosed = digitalRead(PAUSE_BUTTON) == LOW;
boolean motionDetected = digitalRead(PIR_SENSOR) == HIGH;
digitalWrite(MOTION_LED, digitalRead(PIR_SENSOR));
switch (state) {
case STATE_INITIALIZE:
if (pauseSwitchClosed) { goToPause(); }
else { goToSleep(); }
break;
case STATE_PAUSED:
if (!pauseSwitchClosed) { goToSleep(); digitalWrite(PAUSED_LED, LOW); }
else { digitalWrite(PAUSED_LED, HIGH); }
break;
case STATE_SLEEPING:
if (pauseSwitchClosed) { goToPause(); }
else if ( isAllowedToWake() ) { goToReadyToWake(); }
else { processSleeping(); }
break;
case STATE_READY_TO_WAKE:
if (pauseSwitchClosed) { goToPause(); digitalWrite(READY_LED, LOW); }
else if (motionDetected) { goToAwake(); digitalWrite(READY_LED, LOW); }
else { processSleeping(); }
break;
case STATE_AWAKE:
if (pauseSwitchClosed){ goToPause(); }
else if ( processAwakeAnimation() ) { goToSleep(); }
break;
default:
Serial.println("UNKNOWN STATE");
break;
}
}
/* Transition to the Pause State */
inline void goToPause() {
Serial.println("PAUSED");
state = STATE_PAUSED;
stopAllEffects();
sounds.stopAll();
}
/* Transition to the Sleep State */
inline void goToSleep() {
Serial.println("GOING TO SLEEP");
state = STATE_SLEEPING;
wakeAllowedTimer = millis();
}
/* Transition to the Ready To Awake State */
inline void goToReadyToWake() {
Serial.println("READY TO WAKE");
state = STATE_READY_TO_WAKE;
}
/* Transition to the Awake State */
inline void goToAwake() {
Serial.println("AWAKE");
state = STATE_AWAKE;
wakeMonster();
}
/* process a cycle of the SLEEP activity */
inline void processSleeping() {
if ((millis() - timeSinceLastSnore) > SLEEP_SOUND_DELAY_MILLIS) {
sounds.playSnore();
timeSinceLastSnore = millis();
}
}
/* Determines if monster is allowed to wake up at this time */
inline boolean isAllowedToWake() {
boolean isDebug = digitalRead(DEBUG_BUTTON) == LOW;
unsigned long requiredDelay = WAKE_DELAY_MILLIS;
if ( isDebug ) { requiredDelay = WAKE_DELAY_DEBUG_MILLIS; }
boolean isAllowed = (millis() - wakeAllowedTimer ) > requiredDelay;
if (isAllowed ) { digitalWrite(READY_LED, HIGH); }
return isAllowed;
}
/* Wake Monster - Starts the Awake Animations & Plays the Awake Sounds */
void wakeMonster() {
int activityDuration = random(WAKE_MIN_MILLIS, WAKE_MAX_MILLIS);
Serial.print(" wake duration: ");
Serial.print(activityDuration);
Serial.println(" ms ");
bounceLid(activityDuration);
flashRedLight(activityDuration + RED_LIGHT_EXTRA_TIME_MILLIS);
activateSmoke(activityDuration + SMOKE_EXTRA_TIME_MILLIS);
sounds.playRoar();
}
/* Manages the progress of the AWAKE animations */
boolean processAwakeAnimation() {
printTimersToLog();
boolean done1 = false;
boolean done2 = false;
boolean done3 = false;
if (millis() - lidBounceTimer > lidBounceDuration) { bounceLid(0); done1 = true; }
if (millis() - redLightTimer > redLightDuration ) { flashRedLight(0); done2 = true; }
if (millis() - smokeTimer > smokeDuration ) { activateSmoke(0); done3 = true; }
return done1 && done2 && done3;
}
/* Manage Effect: Bounce the box lid */
inline void bounceLid(unsigned long duration) {
if (duration <= 0) { energizeRelay(LID_BOUNCER); lidBounceDuration = 0; }
else { de_energizeRelay(LID_BOUNCER); lidBounceTimer = millis(); lidBounceDuration = duration; }
}
/* Manage Effect: Flash Red Lights */
inline void flashRedLight(unsigned long duration) {
if (duration <= 0) { energizeRelay(RED_LEDS); redLightDuration = 0; }
else { de_energizeRelay(RED_LEDS); redLightTimer = millis(); redLightDuration = duration; }
}
/* Start/Stop Effect: Activate Smoke */
inline void activateSmoke(unsigned long duration) {
if (duration <= 0) { energizeRelay(FOG_MACHINE); smokeDuration = 0; }
else { de_energizeRelay(FOG_MACHINE); smokeTimer = millis(); smokeDuration = duration; }
}
/* Stop all of the Effects */
inline void stopAllEffects() {
bounceLid(0); flashRedLight(0); activateSmoke(0);
}
/* Prints the Awake Animation timers to the log once per second */
inline void printTimersToLog() {
static unsigned long timeofLastTimerLog = 0;
if (millis() - timeofLastTimerLog >= 1000) {
Serial.print(" lid: ");
Serial.print( (millis()-lidBounceTimer) > lidBounceDuration ? 0 : (lidBounceDuration -(millis()-lidBounceTimer) ) );
Serial.print(" lights: ");
Serial.print( (millis()-redLightTimer) > redLightDuration ? 0 : (redLightDuration -(millis()-redLightTimer) ) );
Serial.print(" smoke: ");
Serial.println( (millis()-smokeTimer) > smokeDuration ? 0 : (smokeDuration -(millis()-smokeTimer) ) );
timeofLastTimerLog = millis();
}
}
/* Energize Relay - Sets the Normally Open (NO) terminal to OPEN */
inline void energizeRelay(int channel) { digitalWrite(channel, HIGH); }
/* De-Energize Relay - Sets the Normally Open (NO) terminal to CLOSED */
inline void de_energizeRelay(int channel) { digitalWrite(channel, LOW); }
MonsterSounds.h (C/C++)
/* NOTE ABOUT ARDUINO PINS
* Pins 13, 12, 11 are always used by the SD card (they are the only pins that have a high speed SPI interface).
* Then there are 5 other pins used to talk to the DAC and SD card, but they can be set to connect to any arduino pin.
* However, by default, the library is configured to use pins 10 (for SD card) and pins 2, 3, 4 and 5 for the DAC.
* To change these pins requires modifying the library - the pins are referenced by their 'hardware' pin names (ie PORTD, etc) not by arduino pins.
* That means pins 6, 7, 8, 9 and the 6 analog in pins (also known as digital i/o pins 14-20) are available.
* */
#include
#include
#include
#include
static const char roar0[] = "ROAR0000.wav";
static const char roar1[] = "ROAR0001.wav";
static const char roar2[] = "ROAR0002.wav";
static const char * const roarSounds[] = {roar0, roar1, roar2};
static const char sleep0[] = "SNORE000.wav";
static const char sleep1[] = "SNORE001.wav";
static const char sleep2[] = "SNORE002.wav";
static const char * const sleepSounds[] = {sleep0, sleep1, sleep2};
int previousRoarSound = -1;
class MonsterSounds {
private:
SdReader card;
FatVolume vol;
FatReader root;
FatReader file;
WaveHC wave;
void playfile(char *name);
public:
void initialize();
void playSystemReady();
void playRoar();
void playSnore();
void stopAll();
};
MonsterSounds.ino (C/C++)
/* NOTE ABOUT ARDUINO PINS
* Pins 13, 12, 11 are always used by the SD card (they are the only pins that have a high speed SPI interface).
* Then there are 5 other pins used to talk to the DAC and SD card, but they can be set to connect to any arduino pin.
* However, by default, the library is configured to use pins 10 (for SD card) and pins 2, 3, 4 and 5 for the DAC.
* To change these pins requires modifying the library - the pins are referenced by their 'hardware' pin names (ie PORTD, etc) not by arduino pins.
* That means pins 6, 7, 8, 9 and the 6 analog in pins (also known as digital i/o pins 14-20) are available.
* */
void MonsterSounds::initialize() {
Serial.println("Initializing Sounds...");
if(!card.init()) Serial.println(F("Card init. failed!"));
if(!vol.init(card)) Serial.println(F("No partition!"));
if(!root.openRoot(vol)) Serial.println(F("Couldn't open dir"));
Serial.println(F("Files found:"));
root.ls();
randomSeed(analogRead(0));
}
void MonsterSounds::playSystemReady() { this->playfile("WELCOME0.WAV"); }
void MonsterSounds::playRoar() {
int index = random(3); // 0, 1, 2
while (index == previousRoarSound) { index = random(3); }
previousRoarSound = index;
this->playfile(roarSounds[index]);
}
void MonsterSounds::playSnore() {
if (!wave.isplaying) {
int index = random(3); // 0, 1, 2
this->playfile(sleepSounds[index]);
}
}
void MonsterSounds::stopAll() { wave.stop(); }
// playfile() - Open and start playing a WAV file
void MonsterSounds::playfile(char *name) {
PgmPrint("Playing sound: ");
Serial.println(name);
if (wave.isplaying) { wave.stop(); }
if (!file.open(root, name)) { PgmPrintln("File not found "); return; }
if (!wave.create(file)) { PgmPrintln("Not a valid WAV"); return; }
wave.play();
}
Partes Personalizadas y Recintos
monstersounds_7vLZD3NU4t.zip
Esquemáticos
Proceso de manufactura
- Equipos de EEG: Diseño, fabricación y aplicaciones emergentes
- La Revolución del Brick de Jugo: Historia, Diseño e Impacto Ambiental
- Máquinas expendedoras: Una industria de 36.600 millones de dólares en EE. UU.
- Guía Experta de Máquinas Cambiadoras Modernas: Diseño, Componentes y Tendencias Futuras
- La evolución y fabricación de máquinas de coser: de la innovación del siglo XIX a la automatización moderna
- Pantis: Guía Definitiva de la Media Elegante para Mujer
- Construye tu propia máquina de escribir deberes casera (DIY Homework Writing Machine)
- Giftduino: El Set de Regalo Arduino Definitivo para Aficionados y Educadores
- Construye un Dispositivo de Levitación Ultrasónica con Arduino – Guía Paso a Paso
- Estación meteorológica compacta: Arduino Nano y ESP8266 con sensor BMP280 y pantalla OLED/Nokia LCD
- Construye un Reloj Digital en Arduino usando una Máquina de Estados Finitos