#tinker #tailor

Create your own Social Distancing Hat, with extras…

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.

You can adapt this tutorial to suit your needs or to improve on usage. I’m looking at adding additional sensors for 2 other angles to cover more of the distance from other directions.

Ingredients for the distance sensor hat

  • Sharp ultrasonic distance sensor. This item comes with a lot of kits and is pretty commons 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 other 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

Steps to create the circuit.

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.


Hook up: let’s connect our circuit

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 ‘-‘

Distance Sensor code

Code for the distance sensor, I used the ‘sample’ code from the sensor library. Make sure to get the library for the HCSR04 Ultrasonic distance sensor.

Wait – 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 following output:

The output from the Serial monitor.

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.

Again, we need to install a library. Look for the Adafruit NeoPixel library… and install it as we did with the previous library for the ultrasonic distance sensor.

Then in the Examples folder, you’ll want to load the simple example (or just try the example you want).

Open the simple sketch and upload it to your board.
// NeoPixel Ring simple sketch (c) 2013 Shae Erisson
// Released under the GPLv3 license to match the rest of the
// Adafruit NeoPixel library

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
 #include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif

// Which pin on the Arduino is connected to the NeoPixels?
#define PIN        3 // On Trinket or Gemma, suggest changing this to 1

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

// When setting up the NeoPixel library, we tell it how many pixels,
// and which pin to use to send signals. Note that for older NeoPixel
// strips you might need to change the third parameter -- see the
// strandtest example for more information on possible values.
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

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

void setup() {
  // These lines are specifically to support the Adafruit Trinket 5V 16 MHz.
  // Any other board, you can remove this part (but no harm leaving it):
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
  clock_prescale_set(clock_div_1);
#endif
  // END of Trinket-specific code.

  pixels.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
}

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

  // 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(0, 150, 0));

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

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

Check this all works and then add more sensors if you’re using them.


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);

Adding the Distance Sensor and NeoPixels to one circuit

You’ll want to start combining all the components into your circuit.

Using crocclips to get a circuit to work together is a great way to prototype.

I connected all the elements up with my crocclips. 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
 * Date:    October 2021
 * 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

// When setting up the NeoPixel library, we tell it how many pixels,
// and which pin to use to send signals. Note that for older NeoPixel
// strips you might need to change the third parameter -- see the
// strandtest example for more information on possible values.
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 steps
 */

 // *************************************************** 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);                                       // wait a bit :-)
}



 // *************************************************** 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
  }
}
  

Soldering & Sewing… let’s put it all together

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

I started by soldering the buzzer and vibration motors. I then soldered the connections to the neoPixel ring.

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

NeoPixel ring
The NeoPixel ring soldered to my connections.
neopixel ring, buzzer soldered
The Buzzer, vibration, and NeoPixel with soldered connections.
the Lilypad USB
The corresponding pins on the USB Lilypad.

Putting the circuit into your hat… or wherever you decide.

hat and components
The hat that I found at a Charity shop, which had a cute fabric flower on the side, so I’ll use that as the place for the NeoPixel ring.
lilypad and battery
Once it is ready to go into a wearable item, I tend to glue down my wires in some way first. This helps to keep it around for a little longer. I’ve had circuits with wires ripped out after a little wear and tear.
distance sensors in the hat
completed hat
All the components were sewed and/or glued to the hat.

The complete distance sensor hat

The completed circuit. Also shown in the opening video in this post.

distance sensor hat

Also – once 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 😀

I hope you enjoyed this project and please share with me what you’ve been working on! I’d love to see.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.