Project Predator, part three – Code

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()

 

Leave a Reply

Your email address will not be published.

I accept that my given data and my IP address is sent to a server in the USA only for the purpose of spam prevention through the Akismet program.More information on Akismet and GDPR.