December 1, 2019

Ultrasonic Echo Gun

The HC-SR04 ultrasonic distance sensor is amazingly cheap and fun. It’s the sort of thing that can really bring electronics alive and would have featured in a MacGyver episode back in the ’80s.

Here I have rigged up an arduino to a speaker and the ultrasonic senor to measure the distance 5 times per second and show it as a dot the screen. The dots scroll from the right to the left over time. The speaker emits a tone relative to the distance e.g. up close is a high tone and further away is a lower tone. When the trigger is pulled, then the distance in centimeters is displayed on the matrix.

The wiring diagram

The wiring diagram

Here is a sample of the code. The full code is available on  Github.

/*
  HC-SR04 Ultrasonic echo gun
*/
#include <MaxMatrix.h>
#include <avr/pgmspace.h>
#include "CharacterDefinitions.h"

const int trigPin = 13;
const int echoPin = 12;
const int piezoPin = 3;
const int buttonPin = 4;

// led matrix
const int DIN = 7;   // DIN pin of MAX7219 module
const int CLK = 6;   // CLK pin of MAX7219 module
const int CS = 5;    // CS pin of MAX7219 module
const int maxInUse = 1;

const int maxDistance = 200;
bool hasGraphData = false;
MaxMatrix m(DIN, CS, CLK, maxInUse);

byte buffer[10];

void setup() {
  Serial.begin (9600);

  // init echo
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);

  // init matrix
  m.init(); // module initialize
  m.setIntensity(15); // dot matix intensity 0-15

  pinMode(buttonPin, INPUT);
}

void loop() {

  int distance = readDistance();
  playTone(distance); 
  
  bool isValidMeasurement = distance < maxDistance && distance > 0;
  
  if (!isValidMeasurement) {
    Serial.println("Out of range ");
  }
  else {
    String text = String(distance) + " cm";
    Serial.println(text);
  }
  
  int buttonState = digitalRead(buttonPin);
  bool showGraph = buttonState == LOW;
  Serial.println("Button " + String(buttonState == HIGH));
  
  if (showGraph) {
    // Show graph
    hasGraphData = true;
    m.shiftLeft();
    int graphMax = 100;
    int height = distance * 8 / graphMax;
    height = abs(min(7, height) - 7);  
    m.setDot(7, height, 255);
  }
  else {
    if (hasGraphData) {
      m.clear();
      hasGraphData = false;
    }
    String text;
    if (isValidMeasurement) {
      text = String(distance) + ":";
    }
    else {
      text = String("!");
    }
    
    Serial.println(text);
    char textBuffer[text.length()+1];
    text.toCharArray(textBuffer, text.length() + 1);
    printStringWithShift(textBuffer, 75);    
  }
  
  delay(200);
}

int readDistance(){
  
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);
  long duration = pulseIn(echoPin, HIGH);
  int distance = (duration / 2) / 29.1;
  return distance;
}

void playTone(int distance)
{
  int toneFreq = distance * 50;
  toneFreq = max(200, min(8000, toneFreq));
  tone(piezoPin, toneFreq, 10);
}

void printCharWithShift(char c, int shift_speed) {
  // Display=the extracted characters with scrolling
  if (c < 32) return;
  c -= 32;
  int offset = 8 * maxInUse;
  memcpy_P(buffer, CH + 7 * c, 7);
  m.writeSprite(offset, 0, buffer);
  m.setColumn(offset + buffer[0], 0);

  for (int i = 0; i < buffer[0] + 1; i++)
  {
    delay(shift_speed);
    m.shiftLeft(false, false);
  }
}

// Extract the characters from the text string
void printStringWithShift(char* s, int shift_speed) {
  while (*s != 0) {
    printCharWithShift(*s, shift_speed);
    s++;
  }
}