Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Unexpected Maker
Published © GPL3+

UltrasonicEyes

UltrasonicEyes is a fun and quirky project you can place somewhere and watch as it looks at things moving around in front of it. Freaky!

BeginnerFull instructions provided2 hours41,570

Things used in this project

Hardware components

Ultrasonic Sensor - HC-SR04 (Generic)
Ultrasonic Sensor - HC-SR04 (Generic)
×2
Arduino Nano R3
Arduino Nano R3
×1
Photo resistor
Photo resistor
×1
Maxim Integrated 8x8 LED Matrix MAX7219
×2
Solderless Breadboard Half Size
Solderless Breadboard Half Size
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Resistor 330 ohm
Resistor 330 ohm
×1

Software apps and online services

Arduino IDE
Arduino IDE

Story

Read more

Custom parts and enclosures

3D printed case - Back

Print with any 3D printer, no specific settings required.

3D printed case - Front

Print with any 3D printer, no specific settings required.

Schematics

Circuit Layout

Fritzing Circuit

Code

Ultrasonic Eyes

C/C++
#include <MD_MAX72xx.h> // We use this to control the 8x8 LED matrix Displays - You'll need to install this library from the library manager if you donb't already have it.
#include <NewPing.h> // We use NewPing to control the Ultrasonic Sensors - You'll need to install this library from the library manager if you donb't already have it.

// define pins attached to LED matrix display 1
//#define  CLK_PIN   13  // We are using hardware SPI - make sure you have connected the CLK pin to the hardware CLK pin on your device
//#define DATA_PIN  11  // We are using hardware SPI - make sure you have connected the MOSI pin to the hardware MOSI pin on your device
#define CS_PIN    10  // Chip Select pin
#define  MAX_DEVICES 2 // number of displays - we need 2, one for each eye

#define LIGHT A5 // We use Analogue pin 5 to read the light value from the LDR

// We are using hardware SPI which automatically 
MD_MAX72XX mx = MD_MAX72XX(CS_PIN, MAX_DEVICES); // Initialise the 2 Matrix displays

#define  t1  2  // Trigger pin on Ultrasonic Sensor 1
#define  e1  3  // Echo pin on Ultrasonic Sensor 1
#define  t2  4  // Trigger pin on Ultrasonic Sensor 2
#define  e2  5  // Echo pin on Ultrasonic Sensor 2
#define maxDist 400 // the max distance for the ultrasonic pulse

NewPing eyeR(t2, e2, maxDist ); // Initialise Ultrasonic sensor 2
NewPing eyeL(t1, e1, maxDist ); // Initialise Ultrasonic sensor 1

// We track the current state of the system with this integer variable, this way after a blink, we can out the eyes back to looking at the last direction they were in
// Possible states are:
// 0: Looking forward
// 1: Looking Right
// 2: Looking Left
int currentState = -1; 

// We store the time and distance for the pings for each Ultrasonic sensor in these variables
long duration1, duration2;
int distance1, distance2;

// We want the blink to be randomly inserted into the cycle
float nextBlink = millis() + 1000;

// We store the current light intensity in this variable
float lightAmount = 0;

uint8_t eye_forward[COL_SIZE] =
{
  0b00111100,
  0b01000010,
  0b01011010,
  0b10101101,
  0b10111101,
  0b10011001,
  0b01000010,
  0b00111100
};

uint8_t eye_right[COL_SIZE] =
{
  0b00111100,
  0b01000010,
  0b01110010,
  0b11011001,
  0b11111001,
  0b10110001,
  0b01000010,
  0b00111100
};

uint8_t eye_left[COL_SIZE] =
{
  0b00111100,
  0b01000010,
  0b01001110,
  0b10010111,
  0b10011111,
  0b10001101,
  0b01000010,
  0b00111100
};

uint8_t eye_blink[COL_SIZE] =
{
  0b00000000,
  0b00111100,
  0b01111110,
  0b11111111,
  0b10111101,
  0b11000011,
  0b01111110,
  0b00111100
};

void setup()
{
  // Initialise the Matrix Display library
  mx.begin();

  // Set the pin modes for Ultrasonic sensor 1
  pinMode( t1, OUTPUT );
  pinMode( e1, INPUT );

  // Set the pin modes for Ultrasonic sensor 2
  pinMode( t2, OUTPUT );
  pinMode( e2, INPUT );

  // Set each trigger pin on the Ultrasonic sensors to start at LOW 
  digitalWrite( t1, LOW );
  digitalWrite( t2, LOW );

// Set the pin mode for the LDR to be an INPUT
  pinMode( LIGHT, INPUT );

  // Start with the eyes looking forward
  ShowEye_Forward();
  currentState = 0;
}

void loop()
{
  // read in the current light level
  lightAmount = analogRead( LIGHT );
  // make sure the light value is within the range of the Max Intensity of the displays
  lightAmount = ( lightAmount / 255 ) * MAX_INTENSITY;
  // set the intensity
  mx.control(MD_MAX72XX::INTENSITY, lightAmount );

  // Ping the Left eye with a recursion of 5
  distance1 = eyeL.ping_median( 5 );
  // Delay 500ms before we ping the right eye, so we don't get conflicting results
  delay(500);
  // Ping the Right eye with recursion of 5 
  distance2 = eyeR.ping_median( 5 );

  // Check to see if it's time to try to insert a blink
  if ( nextBlink < millis() )
  {
    // Set the next blink time to be a random time between now + 2 seconds and 10 seconds
    nextBlink = millis() + random(2000, 10000);

    // Now we need to decide if this blink is a single blink, or a double blink
    // Pick a random number between 1 and 13 and if it is less than or equal to 6, it's going to be a double blink
    if ( random(1,13) <= 6 )
      ShowEye_Blink_Dbl();
    else
      ShowEye_Blink();

    // Pause for 250ms to ensure the blink can happen before we go back to looking around
    delay(250);

    // Exit this itterartion of loop90 early.
    return;
  }
  
  // We need to work out if the difference between the distances of each ultrasonic sensors detected item is less than 15
  // or if both distances are 0, meaning nothing was detected
  // If this condition is met, then make the eyes look forward 
  float difference = ( distance2 - distance1 );
  if ( abs( difference ) < 250 || (distance1 == 0 && distance2 == 0 ) )
  {
    ShowEye_Forward();
    currentState = 0;
  }
  // Now if distance1 is greater than distance2 and distance1 is also greater than 0, then we want to look right  
  else if ( distance2 < distance1 && distance1 > 0)
  {
    ShowEye_Right();
    currentState = 1;
  }
  // Now if distance2 is greater than distance1 and distance2 is also greater than 0, then we want to look left  
  else if ( distance1 < distance2 && distance2 > 0 )
  {
    ShowEye_Left();
    currentState = 2;
  }

  // delay the loop for 250ms to ensure the eyes have time to display correctly
  delay(250);
}



/***************************************/
/************ MAX7219 Stuff ************/
/***************************************/

void ShowEye_Right()
{
  // Clear the displays
  mx.clear();
  // Set the current display brightness
  mx.control(MD_MAX72XX::INTENSITY, lightAmount );

  // Loop through each row of the displays
  for (uint8_t row=0; row<ROW_SIZE; row++)
  {
    // Set the colums of the display 1
    mx.setColumn(row, eye_right[row]);
    // Set the colums of the display 2
    mx.setColumn(row+8, eye_right[row]);
  }
}

void ShowEye_Left()
{
// Clear the displays
  mx.clear();
  // Set the current display brightness
  mx.control(MD_MAX72XX::INTENSITY, lightAmount );

  // Loop through each row of the displays
  for (uint8_t row=0; row<ROW_SIZE; row++)
  {
     // Set the colums of the display 1
    mx.setColumn(row, eye_left[row]);
     // Set the colums of the display 2
    mx.setColumn(row+8, eye_left[row]);
  }
}


void ShowEye_Forward()
{
// Clear the displays
  mx.clear();
  // Set the current display brightness
  mx.control(MD_MAX72XX::INTENSITY, lightAmount );

  // Loop through each row of the displays
  for (uint8_t row=0; row<ROW_SIZE; row++)
  {
     // Set the colums of the display 1
    mx.setColumn(row, eye_forward[row]);
     // Set the colums of the display 2
    mx.setColumn(row+8, eye_forward[row]);
  }
}

void ShowEye_Blink()
{
// Clear the displays
  mx.clear();
  // Set the current display brightness
  mx.control(MD_MAX72XX::INTENSITY, lightAmount );

  // Loop through each row of the displays
  for (uint8_t row=0; row<ROW_SIZE; row++)
  {
     // Set the colums of the display 1
    mx.setColumn(row, eye_blink[row]);
     // Set the colums of the display 2
    mx.setColumn(row+8, eye_blink[row]);
  }

  // Hold the blink for 150ms
  delay(150);

  // Put the state of the eyes back to what they were
  if ( currentState == 0 )
    ShowEye_Forward();
  else if ( currentState == 1 )
    ShowEye_Right();
  else if ( currentState == 2 )
    ShowEye_Left();

}

void ShowEye_Blink_Dbl()
{
// Clear the displays
  mx.clear();
  // Set the current display brightness
  mx.control(MD_MAX72XX::INTENSITY, lightAmount );

  // Loop through each row of the displays
  for (uint8_t row=0; row<ROW_SIZE; row++)
  {
     // Set the colums of the display 1
    mx.setColumn(row, eye_blink[row]);
     // Set the colums of the display 2
    mx.setColumn(row+8, eye_blink[row]);
  }

  // Wait 75ms
  delay(75);

  // Put the state of the eyes back to what they were - briefly
  if ( currentState == 0 )
    ShowEye_Forward();
  else if ( currentState == 1 )
    ShowEye_Right();
  else if ( currentState == 2 )
    ShowEye_Left();

  // Wait 75ms
  delay(75);

  // Blink the eyes
  ShowEye_Blink();
}

Credits

Unexpected Maker

Unexpected Maker

9 projects • 128 followers
Join me on my journey as I make & possibly break all sorts of projects I am interested in, all from the comfort of your couch!

Comments