Michael Darby - 314Reactor
Published © GPL3+

Nerf Gun Ammo Counter / Range Finder

Why? Because ammo counters are cool and range finders are also pretty cool and Nerf is cool – mix them together and it's ice cold.

IntermediateFull instructions provided4 hours1,746
Nerf Gun Ammo Counter / Range Finder

Things used in this project

Hardware components

Raspberry Pi Zero
Raspberry Pi Zero
×1
sd card
×1
Resistor 1k ohm
Resistor 1k ohm
×1
resistor 2k
×1
proximity sensor
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Male Header 40 Position 1 Row (0.1")
Male Header 40 Position 1 Row (0.1")
×1
Pi header
×1
Ultrasonic Sensor - HC-SR04 (Generic)
Ultrasonic Sensor - HC-SR04 (Generic)
×1
Nerf gun
×1
wire
×1
veroboard
×1
blu-tak
×1
sugru
×1
Cable Ties (10 Pack)
OpenBuilds Cable Ties (10 Pack)
×1
powerboost
×1
battery
×1
pi zero case
×1
USB-A to Micro-USB Cable
USB-A to Micro-USB Cable
×1
usb hub
×1
Rainbow HAT
Pimoroni Rainbow HAT
×1

Software apps and online services

pibakery

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
wire cutter
veroboard cutter

Story

Read more

Code

Ammo Counter Code

Python
This code is used for the ammo setting/counting - thanks to the tutorials listed in the story for the codebase
#!/usr/bin/env python
#imports
import sys,os
import colorsys
import time

import rainbowhat

import time

import Adafruit_VCNL40xx

vcnl = Adafruit_VCNL40xx.VCNL4010()

#the class used for counting the shots up/down and resetting
class adjustShots(object):
	
	def __init__(self, shotsN):
		self.shotsN = shotsN
	
	def changeAmmo(self, shotsN):
		self.shotsN = shotsN
		return self.shotsN
		
	def showAmmo(self):
		return self.shotsN
	
	def plusShots(self):
		if self.shotsN < 20:
			self.shotsN = self.shotsN + 1
		else:
			self.shotsN = (-0)
		return self.shotsN
		
	def minusShots(self):
		if not self.shotsN < 0:
			self.shotsN = self.shotsN - 1
		return self.shotsN

#here we set the number of shots that are currently in the magazine (0 is -1 here as the counter works by 0 indexing)		
shotsMag = adjustShots(-1)
#here we set the default number of shots per magazine that will be used (as above 11 will count as 12 due to 0 indexing)
shotsPerMag = adjustShots(11)

#display function for the leds
def display_message(message):
    rainbowhat.display.print_str(message)
    rainbowhat.display.show()

#this is the code that handles showing the number of leds at the top, first 7 being red, next 7 orange and final 7 green for a total of-
#21 possible shots, the code here also sets the colour 'below' it for empty shots, so you will see orange shots remaining below green shots etc.
#(check the youtube video linked in the guide on my site for how this works)    
def display_shots(shot_count):
	if shot_count <= 6:
		for x in range(7):
			if x <= shot_count:
				rainbowhat.rainbow.set_pixel(x, 50, 0, 0, brightness=0.1)
				rainbowhat.rainbow.show()
			else:
				rainbowhat.rainbow.set_pixel(x, 0, 0, 0, brightness=0.1)
				rainbowhat.rainbow.show()

	if shot_count > 6 and shot_count < 14:
		for x in range(7):
			shot_count_2 = shot_count - 7
			if x <= shot_count_2:
				rainbowhat.rainbow.set_pixel(x, 200, 50, 0, brightness=0.1)
				rainbowhat.rainbow.show()
			else:
				rainbowhat.rainbow.set_pixel(x, 50, 0, 0, brightness=0.1)
				rainbowhat.rainbow.show()					

	if shot_count > 13:
		for x in range(7):
			shot_count_3 = shot_count - 14
			if x <= shot_count_3:
				rainbowhat.rainbow.set_pixel(x, 0, 50, 0, brightness=0.1)
				rainbowhat.rainbow.show()
			else:
				rainbowhat.rainbow.set_pixel(x, 200, 50, 0, brightness=0.1)
				rainbowhat.rainbow.show()
			
display_shots(shotsMag.showAmmo())

#the 'A' button will cleanly shut the device down
@rainbowhat.touch.A.press()
def press_a(channel):
	os.system("sudo shutdown now")
	pass

#the 'B' button will count the number of shots per mag up +1, looping back around to 1 once it reaches 21
@rainbowhat.touch.B.press()
def press_b(channel):
	shotsPerMag.plusShots()
	display_shots(shotsPerMag.showAmmo())
	time.sleep(1)
	display_shots(shotsMag.showAmmo())
	pass

#the 'C' button initiates a 'reload', so when you put a mag in or reload it will reset the leds to show remaining ammo in the current mag
@rainbowhat.touch.C.press()
def press_c(channel):
	shotsMag.changeAmmo(shotsPerMag.showAmmo())
	print (shotsMag.showAmmo())
	display_shots(shotsMag.showAmmo())
	pass

#loop for detecting proximity - proximity decreasing when a shot passes the sensor indicating a shot and calling the class-
#for the current magazine to minus one shot
try:
	while True:

		proximity = vcnl.read_proximity()

		if proximity > 2540:
			display_shots(shotsMag.minusShots())
			print (shotsMag.showAmmo())
			time.sleep(1)
		
except KeyboardInterrupt:
    pass

Range Finder Code

Python
This is the code that measures the distance from the ultrasonic sensor and relays it to the Rainbow Hat - again thanks to the tutorials listed in the story for the code base.
#!/usr/bin/env python

#general imports
import time
import colorsys

import rainbowhat

import RPi.GPIO as GPIO
import time

#setup for the GPIO
GPIO.setmode(GPIO.BCM)

TRIG = 23 
ECHO = 24

GPIO.setup(TRIG,GPIO.OUT)
GPIO.setup(ECHO,GPIO.IN)

#loop for measuring distance, this fires out a pulse and measures time to return, it then converts to an int to remove decimals and-
#finally to a string to be sent to the led readout on the lower part of the rainbow hat
try:
    while True:
		
		#print "Distance Measurement In Progress"



		GPIO.output(TRIG, False)
		#print "Waiting For Sensor To Settle"
		time.sleep(2)

		GPIO.output(TRIG, True)
		time.sleep(0.00001)
		GPIO.output(TRIG, False)

		while GPIO.input(ECHO)==0:
		  pulse_start = time.time()

		while GPIO.input(ECHO)==1:
		  pulse_end = time.time()

		pulse_duration = pulse_end - pulse_start

		distance = pulse_duration * 17150

		distance = int(distance)
		
		distance = str(distance)

		print "Distance:",distance,"cm"

		rainbowhat.display.clear()
		rainbowhat.display.print_str(distance)
		rainbowhat.display.show()

		#time.sleep(1)

except KeyboardInterrupt:
    pass

#cleanup code for exiting
rainbowhat.display.clear()
rainbowhat.display.show()

GPIO.cleanup()

rc.local

Python
This is the setup for launching the python scripts upon boot.
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi

sudo python /home/pi/nerfpi.py &
sudo python /home/pi/nerfpi_range.py &

exit 0

Github

https://github.com/adafruit/Adafruit_Python_VCNL40xx

Credits

Michael Darby - 314Reactor

Michael Darby - 314Reactor

16 projects • 37 followers
I like to keep fit, explore and of course make projects. https://314reactor.com/

Comments