Introduction: Distance Hat, Arduino With NeoPixels and Sound

About: eh? PhDork • #tinker #tailor #maker #breaker • I teach computer science, previously: design innovation and Interactivity • assistive technologies • forgetfulness • wearables • physical computing • popcorn •

In this tutorial, for a distance sensor hat, I’ll be using an ultrasonic sensor to measure the distance of others approaching. This will trigger a neopixel ring with colours, a buzzer for sound, and a vibration sensor to get some haptic feedback.

Supplies

  • Sharp ultrasonic distance sensor. This item comes with a lot of kits and is pretty common so you will be able to find it easily.
  • Lilypad USB board (choose a board to suit your project)
  • NeoPixel ring, or string of NeoPixels or another visual way to communicate
  • Vibration Motor
  • Sound module / piezo
  • LiPoly battery, small so you can keep it in the hat,
  • Croc clips are great for the prototyping phase
  • Cable to upload your code

Step 1: Hooking Up!

For this circuit, I’m using the following pins, but I recommend setting each item up one by one, we’ll start with the distance sensor:

  • Ultrasonic distance sensor: ECHO – A2
  • Ultrasonic distance sensor: TRIG – A3
  • NeoPixels data wire – pin 3
  • Vibration – pin 9
  • Buzzer – pin 10

All Ground to Ground and Power to Power

Step 2: Starting With Ultrasonic Distance Sensor

Hooking up the basics to check what works, step by step. First, add the ultrasonic distance sensor.

on the sensor you’ll see 4 pins out, hook them up:

VCC – to your ‘+

TRIG – choose a pin and change it in the code, I’m using A3

ECHO – again, choose a pin, I’m using A2

GND – connect to your GND pin ‘-‘


Do you have the Library for HCSR04 installed?

Not too sure how to install a library?

You’ll need to have the HCSR04 installed to get this examples folder with sketches. For a quick tutorial on how to add an item to the library, here is a quick guide to installing Libraries with Arduino to get you up and running. [Beta Arduino 2.0.0 guide]

If you know how to then just go ahead and add the library (library from Martin Sosic).

I found that this library worked the first time without errors, and the code was just clean and simple to check your sensor works.

#include <HCSR04.h>

UltraSonicDistanceSensor distanceSensor(13, 12);  // Initialize sensor that uses digital pins 13 and 12.

void setup () {
    Serial.begin(9600);  // We initialize serial connection so that we could print values from sensor.
}

void loop () {
    // Every 500 miliseconds, do a measurement using the sensor and print the distance in centimeters.
    Serial.println(distanceSensor.measureDistanceCm());
    delay(500);
}

Change the code to reflect the pins you are using. I’m using A3 and A2 so I’ve modified the code. After you have uploaded the code, open the Serial monitor to check it’s working.

You should have something similar to the output in my screenshot

Step 3: Testing the NeoPixels for Your Distance Sensor Hat

My next step is to connect and test the NeoPixels. Again, make sure you have installed the NeoPixel library through the Library manager.

I’m connecting the ‘data‘ wire to pin 3 on my board and I’ll change this in the code. You’ll also change the number of Neos you’re using. Connect your Ground and Power wires too.

Once you have installed the library and connected up to your circuit, Go to the NeoPixel examples folder. Then in the Examples folder, you’ll want to load the simple example (or just try any example you want).


Step 4: Add a Buzzer and Vibration Sensor If You Are Using Them…

I am going to add a vibration sensor and a buzzer for sound. these values will change as the distance changes. I’m hooking up the buzzer to pin 10, and then the vibration to pin 9.

We will define these at the start of our code file, with the ultrasonic sensor:

// Define the hardware connections:
const int pin_buzzer = 10;     // pin where the buzzer is connceted to
const int pin_vibration = 9;     // pin where the buzzer is connceted to
const int pin_Echo = A2;       // digital pin for HC-SR04 echo connector
const int pin_Trigger = A3;    // digital pin for HC-SR04 Trigger connector

We will also add the pinMode in our setup section of the code:

  pinMode(pin_vibration, OUTPUT);


Step 5: Adding the Distance Sensor and NeoPixels to One Circuit

You’ll want to start combining all the components into your circuit. Using croc clips to get a circuit to work together is a great way to prototype. I connected all the elements up with my croc clips. Then upload the code to see if all of your circuit elements work together!

/**************************************************
 * Ultrasonic Distance Sensor Hat with NeoPixels, vibration motor and buzzer
 * based on HC-SR04 sonar sensor, NeoPixel libraries
 * Author:  Christine Farion
 * Site: www.christinefarion.com
 * Twitter: @cmoz
 * License: CC BY-NC-SAi
 * 
 * using Adafruit NeoPixel Library 
 * 
 * Pinout for a lilypad USB board that I used... 
 * PINOUTS  : NEOPIXELS PIN £
 *          : BUZZER PIN 10
 *          : VIBRATION PIN 9
 *          : ECHO A2
 *          : TRIGGER A3
 */

/**************************************************
 * Ultrasonic Distance Warner
 * based on HC-SR04 sonar sensor
 * Author:  Marcel Andr
 * Date:    6.7.2020
 * License: CC BY-NC-SA
 * using sonar library from Martin Sosic (https://github.com/Martinsos/arduino-lib-hc-sr04)
 */

#include <HCSR04.h>       // include Library - use the extension manager to add this library to your environment
#include <Adafruit_NeoPixel.h>

// Which pin on the Arduino is connected to the NeoPixels?
#define PIN        3

// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS 24 // Popular NeoPixel ring size

Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);
//Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);

#define DELAYVAL 50 // Time (in milliseconds) to pause between pixels

// Define the hardware connections:
const int pin_buzzer = 10;     // pin where the buzzer is connceted to
const int pin_vibration = 9;     // pin where the buzzer is connceted to
const int pin_Echo = A2;       // digital pin for HC-SR04 echo connector
const int pin_Trigger = A3;    // digital pin for HC-SR04 Trigger connector

// Define the signalling
const int tone_repeat = 1;    // How often a tone / smile will be repeated

const int tone_low = 800;     // Frequency for tone if emergency level is low
const int time_low = 0;     // Tone duration if emergency level is low
const int dist_low = 150;     // distance level for low emergency level

const int tone_medium = 1600; // Frequency for tone if emergency level is medium
const int time_medium = 350;  // Tone duration if emergency level is medium
const int dist_medium = 100;   // distance level for medium emergency level

const int tone_high = 2400;   // Frequency for tone if emergency level is high
const int time_high = 200;    // Tone duration if emergency level is high
const int dist_high = 50;     // distance level for high emergency level

// to store the current distance:
float distance;               // distance read from ultrasonic sensor

//create an instance to control the sensor:
UltraSonicDistanceSensor distanceSensor(pin_Trigger, pin_Echo);  // Initialize sensor that uses digital pins 13 and 12.

// ********************************************* SETUP ***************************************************//
void setup () {
  Serial.begin(9600);     // Enabled for debug
  pinMode(pin_vibration, OUTPUT);
  Start(200);                // Call start sequence
  
  pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
  pixels.show();
  pixels.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255)
}

/*
 * The main programm loop:
 * 
 * - read current distance
 * - compare with distance intervals defined above
 * - call signalling (emergency) routine to set smiling and beeping
 */

// ************************************************ LOOP ***************************************************//
void loop () {

    pixels.clear(); // Set all pixel colors to 'off'
    distance = distanceSensor.measureDistanceCm();  // read current distance
    Serial.println(distance);                       // output for debug

    // check interval:
    if (distance < dist_high)
    {
      emergency(3);
    }
    else if (distance <= dist_medium)
    {
      emergency(2);
    }
    else if (distance <= dist_low)
    {
      emergency(1);
    }
    else if (distance > dist_low)
    {
      emergency(0);
    }
    delay(100);               
}

 // ******************************************* FUNCTIONS ********************************************** //

/*
 * Start Sequence
 * ==============
 * Params:
 * -------
 *  startpeed: time in milliseconds between each LED step
 */
void Start(int startspeed) {
  delay(500);
}


void greenSmile() {
  //pixelColor(0,255,0);
  rainbow(10);             // Flowing rainbow cycle along the whole strip
}

void redSmile() {
  pixelColor(255,0,0);
}

void blueSmile() {
  pixelColor(0,0,255);
}

void purpleSmile() {
  pixelColor(180, 0, 255);
}

void offSmile() {
    pixels.clear(); // Set all pixel colors to 'off'
}

/*
 * Signal to short distance
 * ========================
 *
 * Params:
 * -------
 *  level: "loudness" of signaling -> 0 (off), 1 (low),2 (medium) or 3 (high)
 * 
 */

void emergency(int level) {
  switch (level) {
    case 0:             // off
      greenSmile();
      break;
    case 1:             // low
      for (int i = 1; i <= tone_repeat; i++) {
        //tone (pin_buzzer, tone_low, time_low);
        blueSmile();
        //digitalWrite (pin_vibration, HIGH);
        delay (time_low);
        offSmile();
        delay (time_low);
        digitalWrite (pin_vibration, LOW);
      }
      break;
    case 2:             // medium
      for (int i = 1; i <= tone_repeat; i++) {
        tone (pin_buzzer, tone_medium, time_medium);
        purpleSmile();
        digitalWrite (pin_vibration, HIGH);
        delay (time_medium);
        offSmile();
        delay (time_medium);
        digitalWrite (pin_vibration, LOW);
      }
      break;
    case 3:             // high
      for (int i = 1; i <= tone_repeat; i++) {
        tone (pin_buzzer, tone_high, time_high);
        redSmile();
        digitalWrite (pin_vibration, HIGH);
        delay (time_high);
        offSmile();
        delay (time_high);
        digitalWrite (pin_vibration, LOW);
      }   
      break;
    default:            // default = off
      greenSmile();
      delay (time_low);
      break;
  }
}


void pixelColor(int color1, int color2, int color3) {
  // The first NeoPixel in a strand is #0, second is 1, all the way up
  // to the count of pixels minus one.
  for(int i=0; i<NUMPIXELS; i++) { // For each pixel...

    // pixels.Color() takes RGB values, from 0,0,0 up to 255,255,255
    // Here we're using a moderately bright green color:
    pixels.setPixelColor(i, pixels.Color(color1, color2, color3));

    pixels.show();   // Send the updated pixel colors to the hardware.

    delay(DELAYVAL); // Pause before next pass through loop
  }
}


// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow(int wait) {
  // Hue of first pixel runs 5 complete loops through the color wheel. EDITED To run once
  // Color wheel has a range of 65536 but it's OK if we roll over, so
  // just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
  // means we'll make 5*65536/256 = 1280 passes through this outer loop:
  for(long firstPixelHue = 0; firstPixelHue < 1*65536; firstPixelHue += 256) {
    for(int i=0; i<pixels.numPixels(); i++) { // For each pixel in strip...
      // Offset pixel hue by an amount to make one full revolution of the
      // color wheel (range of 65536) along the length of the strip
      // (strip.numPixels() steps):
      int pixelHue = firstPixelHue + (i * 65536L / pixels.numPixels());
      // strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or
      // optionally add saturation and value (brightness) (each 0 to 255).
      // Here we're using just the single-argument hue variant. The result
      // is passed through strip.gamma32() to provide 'truer' colors
      // before assigning to each pixel:
      pixels.setPixelColor(i, pixels.gamma32(pixels.ColorHSV(pixelHue)));
    }
    pixels.show(); // Update strip with new contents
    delay(wait);  // Pause for a moment
  }
}




Step 6: Soldering & Sewing… Let’s Put It All Together

If your circuit is working, then it’s time to put it all together for your project.

I started by soldering the buzzer and vibration motors. I then soldered the connections to the neoPixel ring. All the components were sewed and/or glued to the hat. 

Solder the DATA INPUT connection to the pin that you are using. I’m using PIN 3. Then solder the Power and Ground.

Step 7: Enjoy!

Also – now that we don’t need to do physical distancing anymore – you can do the reverse .. Have the lights red if your friend is too far away 😀

To see more of my projects and wearables, visit http://christinefarion.com, follow me on Instagram, and Twitter is great if you have any questions or comments!

Wearable Electronics items purchased from Tinker Tailor.