So let me just say right up front that I basically don’t code and I have never in my life looked at a Python script before this project. If anyone happens to know of better approaches for anything I’ve done here, I’d sure like to know.
I wanted a few changes in the functionality of the Rainbow Hat. Since reloading was intended to be automatic (driven by a state change from magazine not inserted to magazine inserted), I didn’t need one of the Hat’s three buttons for that purpose. I also didn’t want a power-down button since I intended to just pull the battery and 1500 mAh of juice will run a Pi for a long time. I decided to set the possible options of magazine size in code, and use the A button to switch to the next smaller size, use the B button to show the current magazine size, and use C to switch to the next larger size. But I wanted feedback on magazine size changes in real time, and since the number of darts available doesn’t change until a magazine is ejected and replaced, I had the idea to use the alphanumeric display on the Hat.
BUT, given that the Hat is generally displaying distance from the range sensor, I had to determine a way to free up the display at the time of magazine size change and then hand it back to the rangefinder. I’m sure there’s a far better way but since I’m new to coding, I decided to have the ammunition script call the rangefinder script as a subprocess, and kill that process whenever a button is pressed which requires the display. The main ammo loop queries as to whether the range subprocess is running, and if not, starts it again. Because of that, the ammo script is the only script that autoruns at startup – follow the instructions here to get autorun working.
I also wanted a nice, easy to recognize alert when the mag is either empty or removed. Luckily the Hat has a piezo buzzer, so it will alert for either shots = 0 or magazine not inserted with a series of 0.3-second beeps.
Here is the code for the primary script
import RPi.GPIO as GPIO import time import rainbowhat as rh import colorsys import subprocess GPIO.setmode(GPIO.BCM) GPIO.setup (27, GPIO.IN, pull_up_down=GPIO.PUD_UP) GPIO.setup (22, GPIO.IN, pull_up_down=GPIO.PUD_UP) #initialize input variables prev_mag_state = 0 prev_trig_state = 0 ammo_left = 7 mag_options = [4, 6, 10, 12, 18] mag_pointer = 0 mag_size = mag_options[mag_pointer] #launch rangefinder script as subprocess rangescript = subprocess.Popen(['python', '/home/nerfpi-range.py']) try: while True: #test for rangefinder script poll = rangescript.poll() if not poll == None: rangescript = subprocess.Popen(['python','/home/nerfpi-range.py']) #magazine detection mag_state = GPIO.input(27) if mag_state == True: ammo_left = 0 if (prev_mag_state and not(mag_state)): ammo_left = mag_size prev_mag_state = mag_state #trigger pull detection trig_state = GPIO.input(22) if (trig_state and not(prev_trig_state)): ammo_left = ammo_left - 1 prev_trig_state = trig_state #warning buzzer if ammo_left <= 0: rh.buzzer.note(261,0.3) time.sleep(0.6) #hat LEDs #shots under 7 if ammo_left <= 7: for x in range(7): if x <= (ammo_left - 1): rh.rainbow.set_pixel(x, 50, 0, 0, brightness = 0.1) rh.rainbow.show() else: rh.rainbow.set_pixel(x,0,0,0,brightness=0.1) rh.rainbow.show() #shots 8-14 if ammo_left > 7 and ammo_left <=14: for x in range(7): ammo_left_2 = ammo_left - 7 if x <= (ammo_left_2 - 1): rh.rainbow.set_pixel(x,200,50,0,brightness = 0.1) rh.rainbow.show() else: rh.rainbow.set_pixel(x,50,0,0,brightness=0.1) rh.rainbow.show() #shots 15+ if ammo_left > 14: for x in range(7): ammo_left_3 = ammo_left - 14 if x <= (ammo_left_3 - 1): rh.rainbow.set_pixel(x,0,50,0,brightness=0.1) rh.rainbow.show() else: rh.rainbow.set_pixel(x,200,50,0,brightness=0.1) rh.rainbow.show() #show mag size function, called from magsize change commands #begins by killing rangefinder to prevent conflict, relaunch #rangefinder after display is clear def show_mag_size(mag_size, script_name): script_name.kill() rh.display.clear() rh.display.print_str('MAG') rh.display.show() time.sleep(0.3) rh.display.clear() rh.display.print_str('SIZE') rh.display.show() time.sleep(0.3) rh.display.clear() rh.display.print_str(str(mag_size)) rh.display.show() time.sleep(0.6) rh.display.clear() rh.display.show() #mag size display @rh.touch.B.press() def touch_b(channel): show_mag_size(mag_size, rangescript) @rh.touch.C.press() def touch_c(channel): global mag_size, mag_pointer, rangescript mag_pointer = mag_pointer + 1 if mag_pointer > 4: mag_pointer = 4 mag_size = mag_options[mag_pointer] show_mag_size(mag_size, rangescript) @rh.touch.A.press() def touch_a(channel): global mag_size, mag_pointer mag_pointer = mag_pointer - 1 if mag_pointer <0: mag_pointer = 0 mag_size = mag_options[mag_pointer] show_mag_size(mag_size, rangescript) time.sleep(0.1) #smooth exit except KeyboardInterrupt: rh.display.clear() rh.display.show() for x in range(7): rh.rainbow.set_pixel(x,0,0,0,brightness=0.1) rh.rainbow.show()
And here’s the code for the rangefinder subroutine:
import RPi.GPIO as GPIO import time import rainbowhat import colorsys GPIO.setmode(GPIO.BCM) TRIG = 23 ECHO = 24 GPIO.setup(TRIG,GPIO.OUT) GPIO.setup(ECHO,GPIO.IN) GPIO.output(TRIG, False) try: while True: 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) rainbowhat.display.clear() rainbowhat.display.print_str(distance) rainbowhat.display.show() time.sleep(0.5) except KeyboardInterrupt: pass rainbowhat.display.clear() rainbowhat.display.show() GPIO.cleanup()