Saturday, 11 April 2015

Raspberry Pi Powered Ultra Sonic Sensing Minion Fart Gun Triggering Machine

One of the trials of 21st century life is having to trigger your own Despicable Me Minion fart gun.  Hence I thought it would be a good use of time and resources to build an automatic Minion fart gun triggering machine*.

(* I am being sarcastic by the way!  It was just a bit of Geek fun with a Raspberry Pi).

Here it is in action, (I'll then tell you how I built it):


So the idea is that the ultra sonic sensor determines when you're close to it and starts the motor which runs the machine that pulls the trigger and sets off the minion farts.

Part 1 - The Fart Gun Trigger Pulling Machine
Using my old-skool 1980s Technical Lego I built a machine that can pull the trigger on the Minion Fart gun.  Here's some pictures:


So key design features are:
  • A Lego Power Functions motor.
  • Which runs through a "gear box" to step down the speed.
  • Which powers a large Lego sprocket.
  • Which has an "arm" connected off-centre, thus turn rotary motion into reciprocating motion.
  • Which pushes a beam backwards and forwards.
What this does subsequently is easier to see on the image below:


Connected to the beam is a set of  Lego axles which are pushed backwards and forwards.  The very front axle hooks around the Fart Gun trigger to make it go off.  This is adjustable to make sure the trigger is pulled the correct amount.  A few guides with smooth tile pieces make sure everything is aligned and nothing is caught up.  Overall I had to do a lot of strategic strengthening to make sure that when the trigger is pulled the whole contraption doesn't pull itself apart.

Here's a video of it powered with a Lego battery box.



Part 2 - Ultra Sonic Sensor
I've used an HC-SR04 ultra-sonic sensor for a couple of previous Raspberry Pi Scratch projects like a press up counting machine and a simple game.  Previously I've used Scratch but for this project I decided to use Python to understand a little more about how to control the sensor and interpret the results.

I followed this tutorial, it's utterly brilliant.   I won't replicate it here but the highlights are:

  • Principles of potential dividers (required to drop the sensor's 5V output down to 3.3V for the Raspberry Pi).
  • Wiring up the sensor.
  • Python code for setting up and controlling the GPIO.
  • Python code to trigger a measurement (creating a short pulse).
  • Python code to capture the echo response duration.
  • The physics and maths behind interpreting the length of the echo pulse and turning it into a distance measurement.
It's just fascinating how tangible it is to do this through Python on the Raspberry Pi.  The result is the bulk of the code I've pasted in at the bottom of this posting.  You have:
  • Function GetAMeasurement - Which takes a measurement
  • A main loop that continuously gets a measurement and acts upon the result.

Part 3 - Controlling Motors
For this I re-used techniques I first learnt about for my Lego car project.  Read more about it there but it basically uses a motor controller board to take logic output from Raspberry Pi GPIO pins and switch on the Lego motor.  Never connect the output of the Raspberry Pi to a motor as you'll damage the Pi!

Key snippets of Python code are.
1)Constants for the GPIO pin numbers:
MOT1 = 17
MOT2 = 21

2)Defining the pins as GPIO outputs:
GPIO.setup(MOT1,GPIO.OUT)
GPIO.setup(MOT2,GPIO.OUT)

3)Controlling the pins based upon distance measurements:

  if (TheDistance < 100):
    #Switch motor on
    print "Get farting!"
    GPIO.output(MOT1, True)
    GPIO.output(MOT2, False)
  elif (TheDistance < 10):
    #Switch motor off
    GPIO.output(MOT1, False)
    GPIO.output(MOT2, False)
  else:
    #Switch motor off
    GPIO.output(MOT1, False)
    GPIO.output(MOT2, False)

So if the measurement from the sensor is greater than 100cm or less than 10cm, keep the motor turned off.  Otherwise, turn the motor on by setting one of the GPIO pins as high.  I do the < 10cm check for Lego maintenance purposes, (i.e. if it breaks!).  It means that by covering the sensor I can leave the Python script running and be close to the sensor to tinker with stuff without triggering the farts.

Aside - Wiring it Up
One thing I learnt during this project was the different ways to reference the pins on the Raspberry Pi GPIO.  On previous projects I've specified:

GPIO.setmode(GPIO.BOARD)

...which means reference the pins exactly how they're laid out on the GPIO.  e.g. Pin 11 in the code refers to pin 11 on the GPIO.

The code from the ultra sonic sensor tutorial specified:

GPIO.setmode(GPIO.BCM)

...which means reference the pins how the Broadcom chip references them.  This is explained on this Stack Exchange answer.  What I actually did is best explained with a picture.  Here's how I had it wired up:



Here's a picture of it wired up:


When looking at the sensor from behind:
  • The right hand pin is VCC and is wired to +5V on the Pi.
  • The next to right hand pin is Trig and the green jumper wire goes to GPIO23 as shown on the diagram above.
  • The left hand pin is Ground and is wired to Ground on the Pi.
  • The next to left hand pin is Echo and has a 2k ohm resistor coming from it to a spare track on the bread board.  From the same track, the yellow jumper wire goes to GPIO24 on the Pi and the 1k ohm resistor goes to the Ground rail (which is connected to Ground on the Pi). 


All the Code
As ever on the Geek Dad blog, here's all the code:

#Used https://www.modmypi.com/blog/hc-sr04-ultrasonic-range-sensor-on-the-raspberry-pi as a tutorial

#Import statememts
import RPi.GPIO as GPIO
import time
import datetime

#Set GPIO numbering mode
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

#Constants for the trigger and echo pins
#http://raspberrypi.stackexchange.com/questions/12966/what-is-the-difference-bet
ween-board-and-bcm-for-gpio-pin-numbering
TRIG = 23
ECHO = 24
MOT1 = 17
MOT2 = 21

#Other constants
WaitVar = 1

#Initial message
print 'Distance Measurement In Progress'

#Set up our GPIO inputs and outputs
GPIO.setup(TRIG,GPIO.OUT)
GPIO.setup(ECHO,GPIO.IN)
GPIO.setup(MOT1,GPIO.OUT)
GPIO.setup(MOT2,GPIO.OUT)

#Routine to initialise the sensor
def InitaliseSensor(TrigPin):
  #Initialise the sensor
  GPIO.output(TrigPin, False)
  print "Waiting For Sensor To Settle"
  time.sleep(2)

def GetAMeasurement(TrigPin,EchoPin):
  #Send a pulse that is 10us long
  GPIO.output(TrigPin, True)
  time.sleep(0.00001)
  GPIO.output(TrigPin, False)

  #Keep looping while we wait for a response
  while GPIO.input(EchoPin)==0:
    pulse_start = time.time()  #So when we come out of this loop the start time will be logged

  #Now keep looping while the response comes in (pin is high)
  while GPIO.input(ECHO)==1:
    pulse_end = time.time()    #So when this loop ends the end time will be logged

  #Calculate pulse duration
  pulse_duration = pulse_end - pulse_start

  #Speed = Distance / Time. Hence Distance = Speed * Time.  Speed is speed of sound = 343m/s or 34300cm/2.  Divide resulting answer by 2 as it's a return trip
  distance = pulse_duration * 17150
  distance = round(distance,2)

  #return the distance
  return distance

####Main code body####
#Initialise the sensor
InitaliseSensor(TRIG)

while True:
  #Get the distance
  TheDistance = GetAMeasurement(TRIG,ECHO)

  #Print the result
  print "Distance:",TheDistance,"cm"

  #Wait a bit
  time.sleep(WaitVar)

  #See if someone is close
  if (TheDistance < 100):
    #Switch motor on
    print "Get farting!"
    GPIO.output(MOT1, True)
    GPIO.output(MOT2, False)
  elif (TheDistance < 10):
    #Switch motor off
    GPIO.output(MOT1, False)
    GPIO.output(MOT2, False)
  else:
    #Switch motor off
    GPIO.output(MOT1, False)
    GPIO.output(MOT2, False)

#Clean up the GPIO
GPIO.cleanup()