Tommy Bianco
Published © CC BY-NC-ND

Halloween Shield

The night before a Halloween party I thought - wouldn't it be cool to add a sound-responsive element to my costume?

BeginnerFull instructions provided2 hours369
Halloween Shield

Things used in this project

Hardware components

Arduino UNO
Arduino UNO
×1
ElectroPeak MAX7219 8x8 Dot Matrix Display Module
×1
ElectroPeak KY-037 Sound Detection Sensor Module
×1
5 mm LED: Green
5 mm LED: Green
×2
ElectroPeak Switch Push Button with Cap
×1
Samsung 10,000mAh Ultra Battery pack
×1
Elegoo A soldering PCB (5X7cm)
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Schematics

Halloween Shield Layout

A breadboard layout demonstrating the wiring of a custom Arduino Uno shield

Code

Halloween Shield Code

Arduino
Halloween-themed sound-responsive animations for a custom Arduino Uno shield
/*  |||||||||||||||||||||||||||||||||||||||||||||||||||||
 *  ||||||       Defining the shield pinout        ||||||
 *  ||||||||||||||||||||||||||||||||||||||||||||||||||||| */

// LED matrix module
#include "LedControl.h"   
int DIN = 5;
int CS = 6; 
int CLK = 7; 
LedControl display=LedControl(DIN,CLK,CS,1);

// Sound sensor module
int sensorAnalogPin = A0;    // Select the pin for the Sound Sensor's analog output 
int analogValue = 0;
int baseline = 540;         // Pick a baseline with decent signal-to-noise ratio 
int signal_interval = 10;   // Minimal deviation from the baseline to be considered signal (not background noise) 
int sensorDigitalPin = A1;  // Select the pin for the Sound Sensor's digital output
// Using an analog pin due to the custom shield limitations >_<

// Other elements
int LED_1 = 2;
int LED_2 = 3;
int button = 4;

// Other variables
int operationMode = 0;      // Current animation's ID
const int modeMax = 3;      // Total number of animations - 1
bool buttonState = HIGH;
bool soundSignal = HIGH;    // Holding the sound signal until an appropriate moment in the program cycle

/*  |||||||||||||||||||||||||||||||||||||||||||||||||||||
 *  ||||||          Analog signal filter           ||||||
 *  ||||||||||||||||||||||||||||||||||||||||||||||||||||| */

// A simple filter returning an average of several analogRead() values
int filteredAnalog (int counter = 10, int filterDelay = 0.1) {
  int analogPot = 0;
  for (int i = 0; i<(counter-1); i++){
    analogPot = analogPot + analogRead(sensorAnalogPin);
    delay(filterDelay);
  }
  return(analogPot/counter);
}

/*  |||||||||||||||||||||||||||||||||||||||||||||||||||||
 *  ||||||            Debug functions              ||||||
 *  ||||||||||||||||||||||||||||||||||||||||||||||||||||| */
 
void serialDebug(){
  Serial.print(baseline);
  Serial.print(" ");
  Serial.print(baseline + signal_interval);
  Serial.print(" ");
  Serial.print(baseline - signal_interval);
  Serial.print(" ");
  Serial.println(filteredAnalog());
}

/*  |||||||||||||||||||||||||||||||||||||||||||||||||||||
 *  ||||||          Button control cycle           ||||||
 *  ||||||||||||||||||||||||||||||||||||||||||||||||||||| */

// For cycling through the animations
void buttonCheck (){
  bool buttonStateNew = digitalRead (button);
  if (buttonStateNew != buttonState){
    buttonState = buttonStateNew;
    if (buttonState == LOW){ 
      if (operationMode < modeMax){operationMode++;}
      else {operationMode = 0;}
    }}
    }

/*  |||||||||||||||||||||||||||||||||||||||||||||||||||||
 *  ||||||         Sound input mini-cycles        ||||||
 *  ||||||||||||||||||||||||||||||||||||||||||||||||||||| */
 
void soundCheck (){
  // Check buttons & send signal every time you check sound
  buttonCheck ();
  //serialDebug ();
  
  // Read the value of the analog interface A0 assigned to analogValue 
  analogValue = filteredAnalog();

  // At what volume do the animation and LEDs react?
  if (analogValue >= (baseline + signal_interval) /*|| analogValue <= (baseline - signal_interval)*/){
    // A double threshold (above & below the baseline) may improve responsiveness to certain noises
    soundSignal = LOW;
  }
  if (analogValue > (baseline + signal_interval)){digitalWrite(LED_1, LOW);}
  else {digitalWrite(LED_1, HIGH);}
  if (analogValue < (baseline - signal_interval)){digitalWrite(LED_2, LOW);}
  else {digitalWrite(LED_2, HIGH);}}

/*  |||||||||||||||||||||||||||||||||||||||||||||||||||||
 *  ||||||       Matrix display functions          ||||||
 *  ||||||||||||||||||||||||||||||||||||||||||||||||||||| */

void displayImage(uint64_t image) {
  for (int i = 0; i < 8; i++) {
    byte row = (image >> i * 8) & 0xFF;
    for (int j = 0; j < 8; j++) {
      display.setLed(0, i, j, bitRead(row, j));
    }}}

void displayAnimation (uint64_t animation [], uint8_t start_i = 0, uint8_t end_i = 3, uint8_t animation_delay = 0) {
    for (int i = start_i; i <= end_i; i++){
      displayImage (animation [i]);
      int timer = 0;
      while (timer != animation_delay){
        delay (1);
        timer ++;
        soundCheck ();
      }
    }}

/*  |||||||||||||||||||||||||||||||||||||||||||||||||||||
 *  ||||||          Angry Face animation           ||||||
 *  ||||||||||||||||||||||||||||||||||||||||||||||||||||| */

// Has both sounds-responsive and passive animation

const uint64_t angryFace[] = {
  0x00dd7700e7e7c381,
  0x00eebb00e7e7c381,
  0x0077dd00e7e7c381,
  0x00bbee00e7e7c381};

const uint64_t angryBlink_3[] = {
  0x00bbee00e7e70000,
  0x00bbee00e7000000,
  0x00bbee0000000000,
  0x00bbee00e7e70000};
  
const uint64_t angryBlink_2[] = {
  0x0077dd00e7e70000,
  0x0077dd00e7000000,
  0x0077dd0000000000,
  0x0077dd00e7e70000};
  
const uint64_t angryBlink_1[] = {
  0x00eebb00e7e70000,
  0x00eebb00e7000000,
  0x00eebb0000000000,
  0x00eebb00e7e70000};
  
const uint64_t angryBlink_0[] = {
  0x00dd7700e7e70000,
  0x00dd7700e7000000,
  0x00dd770000000000,
  0x00dd7700e7e70000};

// Allows "interruption" of the passive animation whenever soundSignal == HIGH with an appropriate 
// soound-responsive animation cycle
void responsive_angryFace (uint8_t start_i = 0, uint8_t end_i = 3, uint8_t animation_delay = 300) {
  for (int i = start_i; i <= end_i; i++){
    soundCheck ();
    if (soundSignal == LOW){ 
      switch (i) {
        case 0:
          displayAnimation(angryBlink_0, 0, 3, animation_delay/4);
          break;
        case 1:
          displayAnimation(angryBlink_1, 0, 3, animation_delay/4);
          break;
        case 2:
          displayAnimation(angryBlink_2, 0, 3, animation_delay/4);
          break;
        case 3:
          displayAnimation(angryBlink_3, 0, 3, animation_delay/4);
          break;
      }
      soundSignal = HIGH;;  
    }
    else {
      int timer = 0;
      while (timer != animation_delay){
        delay (1);
        timer ++;
        soundCheck ();
      }}
    displayImage(angryFace [i]);
  }}

/*  |||||||||||||||||||||||||||||||||||||||||||||||||||||
 *  ||||||            Ghost animation              ||||||
 *  ||||||||||||||||||||||||||||||||||||||||||||||||||||| */

// Has both sounds-responsive and passive animation

const uint64_t ghost[] = {
  0xa5ffffff99997e3c,
  0xa5ffffff99ff7e3c,
  0xa5ffff99ffff7e3c,
  0xa5ffffff99ff7e3c};

const uint64_t ghost_0[] = {
  0xa5fff1ff99997e3c,
  0xa5ffe3ff99997e3c,
  0xa5ffc7ff99997e3c,
  0xa5ff8fff99997e3c};


const uint64_t ghost_1[] = {
  0xa5fff1ffff997e3c,
  0xa5ffe3ffff997e3c,
  0xa5ffc7ffff997e3c,
  0xa5ff8fffff997e3c};


const uint64_t ghost_2[] = {
  0xa5fff1ffffff7e3c,
  0xa5ffe3ffffff7e3c,
  0xa5ffc7ffffff7e3c,
  0xa5ff8fffffff7e3c};

// Allows "interruption" of the passive animation whenever soundSignal == HIGH with an appropriate 
// soound-responsive animation cycle
void responsive_ghost (uint8_t start_i = 0, uint8_t end_i = 3, uint8_t animation_delay = 500) {
  for (int i = start_i; i <= end_i; i++){
    soundCheck ();
    if (soundSignal == LOW){ 
      switch (i) {
        case 0:
          displayAnimation(ghost_0, 0, 3, animation_delay/5);
          break;
        case 1:
          displayAnimation(ghost_1, 0, 3, animation_delay/5);
          break;
        case 2:
          // No animation in this case
          break;
        case 3:
          displayAnimation(ghost_1, 0, 3, animation_delay/5);
          break;
      }
      soundSignal = HIGH;;  
    }
    else {
      int timer = 0;
      while (timer != animation_delay){
        delay (1);
        timer ++;
        soundCheck ();
      }}
    displayImage(ghost [i]);
  }}

/*  |||||||||||||||||||||||||||||||||||||||||||||||||||||
 *  ||||||            PacMan animation             ||||||
 *  ||||||||||||||||||||||||||||||||||||||||||||||||||||| */

// A sound-responsive animation only

const uint64_t pacman [] = {
  0x3c7eff0ffffb7e3c,
  0x3c7e3f1f1f3b7e3c,
  0x3c7eff0ffffb7e3c,
  0x3c7eff0ff3f37e3c};

void responsive_pacman (uint8_t animation_delay = 250) {
  soundCheck ();
  if (soundSignal == LOW){ 
    displayAnimation(pacman, 0, 3, animation_delay);
    soundSignal = HIGH;;  
    }
  else {
    displayImage(pacman [0]);
    }}

/*  |||||||||||||||||||||||||||||||||||||||||||||||||||||
 *  ||||||          Heartbeat animation            ||||||
 *  ||||||||||||||||||||||||||||||||||||||||||||||||||||| */

// A sound-responsive animation only

const uint64_t heart_empty [] = {
  0x183c7effffff6600,
  0x183c66e7ffff6600,
  0x183c66c3dbff6600,
  0x1824428181996600};

const uint64_t heart_fill [] = {
  0x1824428181996600,
  0x183c66c3dbff6600,
  0x183c66e7ffff6600,
  0x183c7effffff6600};

void responsive_heart (uint8_t animation_delay = 50) {
  soundCheck ();
  if (soundSignal == LOW){ 
    displayAnimation(heart_empty, 0, 3, animation_delay);
    displayAnimation(heart_fill, 0, 3, animation_delay);
    soundSignal = HIGH;;  
    }
  else {
    displayImage(heart_empty [0]);
    }}

/*  |||||||||||||||||||||||||||||||||||||||||||||||||||||
 *  ||||||         The main program body           ||||||
 *  ||||||||||||||||||||||||||||||||||||||||||||||||||||| */

void setup(){
  // Wake up, initialise and clear the LED matrix on startup
  display.shutdown(0,false);
  display.setIntensity(0,0.5);
  display.clearDisplay(0);
  pinMode (button, INPUT_PULLUP);
  digitalWrite(LED_1, LOW);
  digitalWrite(LED_2, LOW);
  // Not used in this version of the script:
  // pinMode (sensorDigitalPin, INPUT);
  // Serial.begin(9600);
}

void loop(){
  // serialDebug ();
  switch(operationMode){
    case 0:
      responsive_angryFace ();
      break;
    case 1:
      responsive_ghost ();
      break;
    case 2:
      responsive_pacman ();
      break;
    case 3:
      responsive_heart ();
      break;
  }}

Credits

Tommy Bianco

Tommy Bianco

5 projects • 1 follower
A biologist procrastinating

Comments