Explorer Pro HAT primitive intruder detector code

Hi @gadgetoid, @Gisky, @Jon and anyone else who has been following the evolving code.

I cannot thank you all enough for your help.

As mentioned previously, I am running the code through LXTerminal using sudo idle from XWindow.

So close… so very close.

I needed to apt-get install espeak for the voice to work.
I used the info supplied in the following link to do this.
http://elinux.org/RPi_Text_to_Speech_(Speech_Synthesis)

I have one final issue… the following lines near the bottom of the code need to request another DISARM input until it matches PIN (if an incorrect DISARM has been already entered) - this is currently not working. In BASIC, I would gosub to the start of the routine that checks for a DISARM entry. I have not been able to figure this out in Python.

The erroneous lines are:

#Keep system active if DISARM is not the same as PIN
if DISARM != PIN:
    pass

The code for the whole program is as follows and, apart from the little glitch, works as intended. I have added comments to help others understand the processes. As usual, I feel this may be very clunky and could be streamlined by those with better coding skills than I currently possess. I welcome any feedback.

import explorerhat, time, os

#Set the PIN number
PIN = []
#Set the DISARM number
DISARM = []

#Check for keypad presses, print on screen and log the response into the PIN number
def onePressed(channel, event):
    print("I got a touch on button: {}".format(channel))
    PIN.append("1")
explorerhat.touch.one.pressed(onePressed)

def twoPressed(channel, event):
    print("I got a touch on button: {}".format(channel))
    PIN.append("2")
explorerhat.touch.two.pressed(twoPressed)

def threePressed(channel, event):
    print("I got a touch on button: {}".format(channel))
    PIN.append("3")
explorerhat.touch.three.pressed(threePressed)

def fourPressed(channel, event):
    print("I got a touch on button: {}".format(channel))
    PIN.append("4")
explorerhat.touch.four.pressed(fourPressed)

print ("Please input PIN number:")

#Check the PIN number has four digits or wait until it has
while len(PIN) < 4:
    pass

#When PIN has four digits, display the PIN and continue
explorerhat.touch.pressed(None)
PIN = PIN[0:4]
print(PIN)

#Set up the speech voice parameters
def speak(text):
    os.system("espeak -vm7 -p0,180 -s170 ' " + text + " ' ")

#Display a steady green LED indicating the system is armed
explorerhat.light.green.on()
print "*** SYSTEM ARMED ***"
speak("SSISSTEM ARMED")

#Detect an alert and respond with red, pulsing LED, screen prompt and voice alert
def alert(pin):
    print "*** WARNING! INTRUDER ALERT! ***"
    print("Zone : " + pin.name)
    explorerhat.light.red.pulse()
    explorerhat.light.green.off()
    speak("IWARNING. INTRUDER ALERT.")
    return

explorerhat.input.one.pressed(alert)

#Alarm resets awaiting further alert
def zeroalert(pin):
    print "*** SYSTEM ARMED ***"
    print "*** ENTER PIN TO DISARM SYSTEM ***"
    explorerhat.light.red.off()
    explorerhat.light.green.on()

explorerhat.input.one.released(zeroalert)

#Check for keypad presses to disarm, print on screen and log the response into the DISARM number
def onePressed(channel, event):
    print("I got a touch on button: {}".format(channel))
    DISARM.append("1")
explorerhat.touch.one.pressed(onePressed)

def twoPressed(channel, event):
    print("I got a touch on button: {}".format(channel))
    DISARM.append("2")
explorerhat.touch.two.pressed(twoPressed)

def threePressed(channel, event):
    print("I got a touch on button: {}".format(channel))
    DISARM.append("3")
explorerhat.touch.three.pressed(threePressed)

def fourPressed(channel, event):
    print("I got a touch on button: {}".format(channel))
    DISARM.append("4")
explorerhat.touch.four.pressed(fourPressed)

#Check the DISARM number has four digits or wait until it has
while len(DISARM) < 4:
    pass

explorerhat.touch.pressed(None)
DISARM = DISARM[0:4]

#Keep system active if DISARM is not the same as PIN
if DISARM != PIN:
    pass

#If DISARM code is same as PIN then shutdown system
if DISARM == PIN:
    print "*** SYSTEM SHUTTING DOWN ***"
    speak("SYSTEM SHUTTING DOWN")
    explorerhat.light.off()
    explorerhat.output.off()
    explorerhat.input.clear_events()

Kind regards,
TankMan

I’ve done a little refactoring, which I’ll explain if it works… here we go:

import explorerhat, time, os

#Set the PIN number
INPUT_PIN = []
#Set the DISARM number
DISARM = []

def handle_press(channel, event):
    '''
    Add the number that's been pressed to
    INPUT_PIN every time a butotn is pressed
    '''
    print('Got {event} on {channel}'.format(event=event, channel=channel))
    if channel <= 4:
        INPUT_PIN.append(channel)

#Set up the speech voice parameters
def speak(text):
    os.system("espeak -vm7 -p0,180 -s170 ' " + text + " ' ")

#Detect an alert and respond with red, pulsing LED, screen prompt and voice alert
def alert(pin):
    print "*** WARNING! INTRUDER ALERT! ***"
    print("Zone : " + pin.name)
    explorerhat.light.red.pulse()
    explorerhat.light.green.off()
    speak("IWARNING. INTRUDER ALERT.")
    return

#Alarm resets awaiting further alert
def zeroalert(pin):
    print "*** SYSTEM ARMED ***"
    print "*** ENTER PIN TO DISARM SYSTEM ***"
    explorerhat.light.red.off()
    explorerhat.light.green.on()

explorerhat.touch.pressed(handle_press)

'''
Clear the INPUT_PIN list and wait for
the user to type in a PIN to arm the system
'''
INPUT_PIN = []

print ("Please input PIN number:")

while len(INPUT_PIN) < 4:
    pass

PIN = INPUT_PIN[0:4]

'''
Display a steady green LED indicating
the system is armed
'''
explorerhat.light.green.on()
explorerhat.input.one.pressed(alert)
explorerhat.input.one.released(zeroalert)
print "*** SYSTEM ARMED ***"
speak("SSISSTEM ARMED")

'''
Constantly poll for the correct key
code and DISARM the system if found
'''
while True:

    INPUT_PIN = []

    while len(INPUT_PIN) < 4:
        pass

    DISARM = INPUT_PIN[0:4]

    if DISARM == PIN:
        print "*** SYSTEM SHUTTING DOWN ***"
        speak("SYSTEM SHUTTING DOWN")
        explorerhat.light.off()
        explorerhat.output.off()
        explorerhat.input.clear_events()
        break # Exit the "while True" loop

A little explanation:

explorerhat.touch.pressed is a nifty function which assigns an “event handler” and always runs it when it a button is pressed. You can tell it what to do right at the beginning of your code, and then forget about it. There’s no need to rebind those functions.

Also, channel is already a variable containing the number which I think corresponds to the numbers you want to enter into your system- 1,2,3,4. By using channel as the number we append onto the PIN_INPUT list, we don’t have to use a separate event handler function for each button- we can recycle the same one!

def handle_press(channel, event):
    '''
    Add the number that's been pressed to
    INPUT_PIN every time a butotn is pressed
    '''
    print('Got {event} on {channel}'.format(event=event, channel=channel))
    if channel <= 4:
        INPUT_PIN.append(channel)

Once set up, you can use the same handler to capture the PIN and the DISARM value just by watching a new value, INPUT_PIN and waiting for it to fill up.

Hi gadgetoid and @Gisky ,
Sorry I’ve been out of touch for a while.

I have started a new job, which is going very well, but I have needed to focus on those challenges for a few weeks.

Back to the intruder detector… both your code and mine are giving the same result, as follows:-

Explorer HAT Pro detected…
Please input PIN number:
I got a touch on button: 4
I got a touch on button: 4
I got a touch on button: 4
I got a touch on button: 4
[‘4’, ‘4’, ‘4’, ‘4’]
*** SYSTEM ARMED ***
*** WARNING! INTRUDER ALERT! ***
Zone : one
*** WARNING! INTRUDER ALERT! ***
Zone : one
*** SYSTEM ARMED ***
*** ENTER PIN TO DISARM SYSTEM ***
I got a touch on button: 3
I got a touch on button: 3
I got a touch on button: 3
I got a touch on button: 3

*** WARNING! INTRUDER ALERT! ***
Zone : one
*** SYSTEM ARMED ***
*** ENTER PIN TO DISARM SYSTEM ***
*** WARNING! INTRUDER ALERT! ***
Zone : one
*** SYSTEM ARMED ***
*** ENTER PIN TO DISARM SYSTEM ***

The problem I am having is that if an incorrect PIN is entered to disable the system, then, although the screen is still asking for the PIN to be entered, it does not register any further presses on the keypad and consequently does not disarm. However, it does continue to activate if the PIR detects motion.

If the correct disarm PIN is entered first time then the system disarms cleanly.

I hope this makes sense. I would like for it to print a message to screen “Incorrect PIN entered” and then reset DISARM and await for the correct DISARM PIN to be entered. Then to repeat until the correct PIN is entered.

Kindest regards,
TankMan

This slightly refined version handles “Incorrect Pin”, I also moved the logic for getting input pins to “get_pin” so it’s tidier and re-usable.

import explorerhat, time, os

#Set the PIN number
INPUT_PIN = []
PIN = []

#Add button presses 1-4 to our input buffer
def handle_press(channel, event):
    global INPUT_PIN
    print('Got {event} on {channel}'.format(event=event, channel=channel))
    if channel <= 4:
        INPUT_PIN.append(channel)

#Set up the speech voice parameters
def speak(text):
    os.system("espeak -vm7 -p0,180 -s170 ' " + text + " ' ")

#Detect an alert and respond with red, pulsing LED, screen prompt and voice alert
def alert(pin):
    print "*** WARNING! INTRUDER ALERT! ***"
    print("Zone : " + pin.name)
    explorerhat.light.red.pulse()
    explorerhat.light.green.off()
    speak("IWARNING. INTRUDER ALERT.")
    return

#Alarm resets awaiting further alert
def zeroalert(pin):
    print "*** SYSTEM ARMED ***"
    print "*** ENTER PIN TO DISARM SYSTEM ***"
    explorerhat.light.red.off()
    explorerhat.light.green.on()

# Clear the input buffer and wait for 4 digits
def get_pin():
    global INPUT_PIN
    INPUT_PIN = []
    while len(INPUT_PIN) < 4:
        pass
    return INPUT_PIN[0:4]

explorerhat.touch.pressed(handle_press)

'''
Wait for the user to type in 
a PIN to arm the system
'''

print ("Please input PIN number:")

PIN = get_pin()

'''
Display a steady green LED indicating
the system is armed
'''
explorerhat.light.green.on()
explorerhat.input.one.pressed(alert)
explorerhat.input.one.released(zeroalert)
print "*** SYSTEM ARMED ***"
speak("SSISSTEM ARMED")

'''
Constantly poll for the correct key
code and DISARM the system if found
'''
while True:

    if get_pin() == PIN:
        print "*** SYSTEM SHUTTING DOWN ***"
        speak("SYSTEM SHUTTING DOWN")
        explorerhat.light.off()
        explorerhat.output.off()
        explorerhat.input.clear_events()
        break # Exit the "while True" loop
    else:
        print "*** INCORRECT PIN! ***"
        speak("INCORRECT PIN")
1 Like

Good evening gadgetoid,

Yeah! It works! I’m so happy!

Thank you so very much!

The “if, while, else” had turned into a minefield of “why?!”

The code is complete!

The next step is to activate the tank.

This has a been a wonderful lesson and Pimoroni and its community deserve a big gold star.

Best wishes and kindest regards,

TankMan

2 Likes

Really glad it’s up and running! Although I’m slightly afraid of what you might create now :D

2 Likes

Hi gadgetoid and @gisky,

I have made a start on the next part of the project, which is to take the intruder detector further into the physical world and also make it a little more stand-alone.

It has been a very productive day on the Explorer Pro Hat, starting with activating a 5V piezo buzzer, which will remove the need to send an audio signal through the HDMI (I have not explored composite video/audio, yet).

The second step was to use the Explorer Pro Hat to drive the motor on the tank’s BB gun. This takes my experience from detecting motion to creating motion.

My final, but not quite as successful mission was to link up my HC-SR04, After testing I discovered @ColinD 's post: Unreliable HC-SR04 with Explorer HAT Pro. Thank you Colin for posting your experience with this device.

My code was slightly different, but I also received inaccurate measurements, which I guess is down to the timing issue described in gadgetoid’s response to the post.

I will try running the HC-SR04 directly from GPIO and see if there is a difference in accuracy.

I’d like to thank everyone in the Pimoroni community for sharing their knowledge and experiences. I would probably have given up if it wasn’t for all your help.

The beauty of the Explorer Pro HAT is that I have each of the devices mentioned above, including the PIR for the intruder detector, all wired up at the same time using the HAT’s mini breadboard. I will need to do some code hacking to get the bits working together, but as all the snippets work individually, I am hoping this will be easy. I’ll let you know.

Best wishes,
TankMan

2 Likes

The Explorer Pro HAT truly is a great bit of kit. One of my favourite plug-in add-on boards for the Raspberry Pi.

@TankMan if you have luck with the HC-SR04 using direct GPIO would you mind sharing your code? Thanks.

1 Like

Hi ColinD,

Thank you for replying.

I will have a go at the GPIO setup this week and will gladly share the code.

Unfortunately there are never enough hours in a day, but I will get to it as soon as I am able.

Kind regards,
TankMan

Cheers @TankMan. I’m also going to purchase an analog distance sensor and wire that up to see if joy is to be had as an alternative to the HC-SR04. If it works I’ll let you know.

Hi ColinD,

I did see your links to the analog sensors and would be very interested in hearing your results.

I look forward to comparing notes.

Kind regards,
TankMan

My faith is placed with the analog distance sensor, although I’ve got it on my mental todo list that I should try writing a very basic C driver for ultrasonic distance sensing on the Pi. The measurements certainly wont be perfect, but they will be better than the results you get in Python which just adds far too much into the mix.

Hi gadgetoid,

I have just hooked up the HC-SR04 directly to the Raspberry Pi B+ GPIO, not going through the Explorer Pro HAT and am getting accurate readings using Python code.

Does this mean there is something in the Explorer’s timings that is causing inaccurate readings when the sensor is run through it? If so, can this be remedied?

Kind regards,
TankMan

There’s almost certainly something in Explorer HAT which causes inaccurate timings, but I’m not sure it can be remedied. There’s always going to be an overhead to running the Explorer HAT library, but maybe with a bit of tweaking we can get it down to a bare minimum.

Try importing RPi.GPIO in to your code, setting it up and doing a normal read of the HC-SR04 without using Explorer HAT’s “input.one.read()” wrappers.

In fact, you might even be able to read it like this:

explorerhat.GPIO.input(23)

Or:

explorerhat.GPIO.wait_for_edge(23,explorerhat.GPIO.RISING)

I’m pulling this code out of thin air, so I have no idea if it’ll actually work!

yes, works here… not saying timing is better since I have no relevant sensor to hook up to the input but your code samples are certainly not erroring…

1 Like

Wow… I’ve transcended mortality and become able to remember things for more than 3 seconds. I’m not sure I believe you…

me neither… it’s been more than 3s so I forgot what I was posting about :D

Nice, thanks for the code.

I’m teaching a class of students how to use sensors with the explorerhat and this is perfect starter code. I’ll give them this then get them to be creative and set off an alarm, or take a photo of the intruder.

I’ve been creating a lot more starter projects with explorerhat and sensors. I’ll be adding more as and when I create them PiTime Projects

2 Likes

Hi @puskas97

This was a great project for me as I was very new to all things Pi.

It opened my eyes to practical Pi applications and also introduced me to this great community, who helped make the project successful.

I wish yourself and your students happy Pi adventures.

Kind regards,

TankMan