CircuitPython Setup
For this project you need to ensure your version of CircuitPython is at least 3.0. Adafruit recommends the latest version available by clicking the green button below. Download the file to your Downloads folder, Desktop, etc. (a place you'll remember for the next step).
To load the downloaded file, plug in the HalloWing over USB to your computer and then double-click the reset button on the HalloWing. This will put it into bootloader mode, allowing you to change the firmware. You'll see a USB drive appear on your computer called HALLOWBOOT.
Drag the .uf2 file you downloaded onto the HALLOWBOOT drive. Once it copies over, it will automatically restart, show up as a drive named CIRCUITPY and you will be ready to load a CircuitPython program.
Adafruit really likes using the Mu editor to edit the CircuitPython code. See this guide on loading and using Mu.
Libraries
You'll also need to add a code library for this project. Follow this guide on adding libraries. The only one you'll need is the adafruit_motor folder from the Circuit Python bundle in the lib folder, so just drag it from your downloaded, unzipped lib folder onto the HalloWing's lib folder.
Code Outline
Here's the basic flow of our trap:
- Startup
- Rotate the servo arm to the set position
- Watch for the PIR sensor to be triggered
- Play hissing sound over the speaker
- Rotate the servo to drop the creepy bug
- Wait for the screaming to subside
- Rotate the servo back to the set position
- Wait for the user to press the reset cap touch tooth button
- Begin waiting for the PIR sensor to be tripped again
User Feedback
In order to help you (or your lab assistant Igor) in setting the trap, we'll use the LCD TFT screen on the HalloWing to provide status messages and prompts.
Here are the screens we'll use:
The simplest way to us the HalloWing screen currently is as an image player to show pre-generated bitmap graphics. Download this .zip file and then uncompress it. Plug in you HalloWing and then drag the four .bmp files onto the root level of your HalloWing's CIRCUITPY USB drive.
Audio Playback
We'll use two methods of audio playback with the audio.play()
-- for beeping we'll use the tone playback of a sine wave sample, and for hissing we'll use the audiocore.WaveFile()
playback method.
Download this wave file and drag it onto your HalloWing's CIRCUITPY drive at the root level.
# SPDX-FileCopyrightText: 2018 John Edgar Park for Adafruit Industries # # SPDX-License-Identifier: MIT # HalloWing Jump Scare Trap # use PIR sensor, speaker, and servo import time import array import math import board import displayio import pwmio from adafruit_motor import servo import digitalio import touchio import audioio import audiocore # Setup LED and PIR pins LED_PIN = board.LED # Pin number for the board's built in LED. PIR_PIN = board.SENSE # Pin port connected to PIR sensor output wire. # Setup digital input for PIR sensor: pir = digitalio.DigitalInOut(PIR_PIN) pir.direction = digitalio.Direction.INPUT # Setup digital output for LED: led = digitalio.DigitalInOut(LED_PIN) led.direction = digitalio.Direction.OUTPUT # Setup servo # servo = pwmio.PWMOut(board.D4, frequency=50) pwm = pwmio.PWMOut(board.D4) servo = servo.Servo(pwm) # Setup cap touch button ready_button = touchio.TouchIn(board.TOUCH1) def servo_ready(): servo.angle = 0 def servo_release(): servo.angle = 90 # Set servo to ready position servo_ready() # Function for playing wav file, releasing servo def play_wave(): wave_file = open("hiss01.wav", "rb") # open a wav file wave = audiocore.WaveFile(wave_file) audio.play(wave) # play the wave file led.value = True servo_release() print('Motion detected!') while audio.playing: # turn on LED, turn servo pass wave_file.close() # close the wav file # Setup audio out pin audio = audioio.AudioOut(board.A0) # tone player setup for status beeps tone_volume = 0.1 # Increase this to increase the volume of the tone. frequency_hz = 880 # Set this to the Hz of the tone you want to generate. length = 8000 // frequency_hz sine_wave = array.array("H", [0] * length) for i in range(length): sine_wave[i] = int((1 + math.sin(math.pi * 2 * i / length)) * tone_volume * (2 ** 15 - 1)) sine_wave_sample = audiocore.RawSample(sine_wave) # Function for beeping, usage: 'beep(3)' will beep 3x def beep(count): for _ in range(count): audio.play(sine_wave_sample, loop=True) time.sleep(0.1) audio.stop() time.sleep(0.05) # Function for counting down, usage: 'countdown(5)' def countdown(count): for k in range(count): print(count - k) led.value = True time.sleep(0.1) led.value = False time.sleep(1) # function for blinking, usage: 'blink(5, 0.2)' def blink(count, speed): for _ in range(count): led.value = True time.sleep(speed) led.value = False time.sleep(speed) # display setup backlight = pwmio.PWMOut(board.TFT_BACKLIGHT) splash = displayio.Group() board.DISPLAY.root_group = splash max_brightness = 2 ** 15 backlight.duty_cycle = 0 # Image list images = ["trap_sprung.bmp", "reset_trap.bmp", "please_standby.bmp", "trap_set.bmp"] # Function for displaying images on HalloWing TFT screen def show_image(filename): # CircuitPython 6 & 7 compatible image_file = open(filename, "rb") odb = displayio.OnDiskBitmap(image_file) face = displayio.TileGrid( odb, pixel_shader=getattr(odb, 'pixel_shader', displayio.ColorConverter()) ) # # CircuitPython 7+ compatible # odb = displayio.OnDiskBitmap(filename) # face = displayio.TileGrid(odb, pixel_shader=odb.pixel_shader) backlight.duty_cycle = 0 splash.append(face) board.DISPLAY.refresh(target_frames_per_second=60) backlight.duty_cycle = max_brightness beep(1) # startup beep show_image(images[2]) # waiting display print('Stabilizing') countdown(3) print('Ready') blink(3, 0.2) beep(3) # ready beeps triggered = False ready = True show_image(images[3]) # ready display while True: # Check PIR sensor pir_value = pir.value # Check touch button ready = ready_button.value if pir_value and triggered is not True: # PIR is detecting movement! play_wave() splash.pop() show_image(images[0]) print('Triggered') countdown(8) blink(3, 0.2) beep(1) print('Please reset') led.value = False triggered = True servo_ready() splash.pop() show_image(images[1]) if ready: # touch sensor has been pressed beep(1) splash.pop() show_image(images[2]) countdown(8) print('Ready.') blink(3, 0.2) beep(3) splash.pop() show_image(images[3]) triggered = False
Next we'll test out the code and deploy the trap!
Page last edited January 21, 2025
Text editor powered by tinymce.