The WrongCo Garden Guardian (a Rube Frankenstein)
Off the Shelf Intro: Several years ago, there was an E14 discussion about gardens & pest control. I’d started a project to discourage deer from pillaging my yard & garden. The deer were winning, I shelved the project and bought ultrasonic deterrents.
The ultrasonic deterrents alone were not very effective. The units were left in the garden for 5 years to remind me to do better. Scent based deterrents do OK until it rains. There has got to be a better way. For the sake of my precious hot pepper plants, now is the time for action. The deer herd just gets bigger each year.
Specification: My solution would be NOVEL:
- I would not use piezo speaker IC driver chips for the ultrasonic speaker. The solution needs to provide audible tones for human operation confirmation, and to really annoy garden beasties, have the ultrasonic frequencies change while operating. The speakers can run for awhile since people can’t hear them. To achieve ultrasonic PWM, I selected a Raspberry Pi Pico as the controller.
- My coup de grace – the garden guardian would spray some water, laced with strong/ nasty ,but eco-friendly, scent so the deer stay away.
- When the deer enter the PIR range, sound an extremely loud audible siren to hopefully startle them away.
- Despite marketing claims, deer don’t seem to be bothered at all by lights (deer in the headlights), so those won’t be used.
- The project starts with an autopsy of the old ultrasonic units, hopefully salvage parts and learn what not to do.
For those who like to jump straight to the final solution, here is an action video and photos:
***** May 31 2026 ****** update:
Camouflage the Garden Guardian structure, spray paint base & tower green. Apply plastic leaves and vines.
The stealthy update is not so much for the deer, but to avoid distracting passing neighborhood foot and vehicle traffic. The Garden Guardian gets moved around the yard depending on deer herd movements , garden plant development, and activation activity. It was a bit conspicuous with the white parts, drawing passerby attention.
cammo added front view and side view

Build history, Schematics & materials, then code are shown at the end of this post.
side view
front view
salvaged green enclosure for PIR and ultrasonic speaker.
close up of sprayer nozzle assembly
outdoor electrical box opened to see controls -
Green LED on Pico is on during an operation cycle .
Red LED on PUMP relay on, running when I took the photo

the pump & open frame motor aren't for outdoor use, so they have a cover.
This project starts with an autopsy of the failed purchased units. The enclosures realistically are the only item able to salvage due to from insect and moisture ingress. They have to be modified to accept new piezo-speakers and 3V PIR.
Challenges:
Pump & spray:
- After some research on pressure, mass flow, and nozzle size, the calcs were done, I'm ready. On receipt of the nozzles, the holes and gaskets and adjustments are not straighforward.
- The first trial started with a target of 8 spray nozzles for the Garden Guardian. These are the same nozzles used in the grocery store produce dept. There wasn’t enough pressure with 8 nozzles to cover the desired spray area. 4 nozzles does great for now. Later maybe can upsize to a bigger pump and tank.
- The pump requires a stiff 12V and draws up to 600mA which prompted me to switch from a 3S 2200mAhr Lipo battery to a 12V 7Ahr lead acid battery. The bigger 12V battery doesn’t fit inside the electrical enclosure, but its not problem to find cheap plastic enclosure as these batteries are common to lawn and garden equipment.
Ultrasonic speaker:
- I couldn't find any info published for driving an ultrasonic speaker without a driver IC. I also wanted flexibility to do crazy things with ultrasonic audio to annoy the deer. It took awhile and many experiments to get the ultrasonic driver circuit working.
- My original driver board design had undersized resistor power ratings since the ultrasonic doesn’t run for long. They did get toasty during test. I also forgot to put the pulldown resistor on the FET gate, which worked fine through all stages of test. When testing in the field, I fortunately had the electrical box open to watch LEDs, the FET triggered when it was supposed to be off and those poor resistors smoked like a dry cigar. I built a new driver board that is much more robust , included the gate pull down, and added margin for a higher battery voltage.
The teardown of the old ultrasonic deterrent:
The enclosures are the only salvageable part of the old deterrents. The solar panels worked, but they are non compatible 5V low power panels.
The new ultrasonic speaker is larger than the old, and the new PIR is smaller. This doctor had to operate :-)

With the completed sensor head, the controls can be tested and scope the panel layout on cardboard.
Once working, and on receipt of the electrical box, the components were fit to the subpanel. re assembled, and re tested.

prototype assembly - electrical box & sensor head mounted to the mast,
subpanel installed in electrical box, sensor head mounted and wired

The mast was taken outdoors to install the pump, hoses, and spray head nozzles.
Tabled for the next Spring Clean revision of this project:
- Replace all relays with MOSFETs
- Evaluate/eliminate less effective sirens
- Build an ultrasonic 3 speaker array for added deer annoyance & discord
- Add a light dependent resistor circuit (or solar cell) to disable audible sirens at night
- Add solar recharging to reduce battery size and lower maintenance
- Add external LED to indicate the guardian cycle is executing
- waterproof on/off switch
- waterproof quick disconnect for battery
Wiring and components:

# May 21 2026
# DeerDeflector Rev 2 works !
# saved to Pico as main.py
from machine import Pin, PWM
import utime
import random
# ----------- IO definitions -----------------------
ultrasonicHorn = PWM(Pin(27)) # Use GPIO 27 cal pin 32
ultrasonicHorn.freq(24500) # Set frequency to 24.5 kHz
ultrasonicHorn.duty_u16(0) # Set duty cycle to 50% (65535/2)
AliveLED = Pin(25,machine.Pin.OUT)
AliveLED.value(0)
PIR = Pin(14, machine.Pin.IN)#GPI14 is physical pin 19
buzzer400Hz = Pin(5,machine.Pin.OUT)#GPIO5 is physical pin 7
alarmSiren = Pin(9,machine.Pin.OUT)#GPIO9 is physical pin 12
runSprayPumpRelay = Pin(11,machine.Pin.OUT)#GPIO11 is physical pin 15
def ultrasonicAnnoyCycle():
# warning beep that I can hear
#alarmTime = random.randint(3,6)
ultrasonicHorn.duty_u16(32768) # Set duty cycle to 50% (65535/2)
ultrasonicHorn.freq(3000) # Set frequency to 24.5 kHz
utime.sleep(2)
for freqRun in range (6000,25000,100):
ultrasonicHorn.freq(freqRun) # Set frequency to 24.5 kHz
utime.sleep(.05)
alarmTime = random.randint(2,6)
ultrasonicHorn.duty_u16(32768) # Set duty cycle to 50% (65535/2)
ultrasonicHorn.freq(20000) # Set frequency to 24.5 kHz
utime.sleep(alarmTime)
alarmTime = random.randint(4,9)
ultrasonicHorn.freq(24500) # Set frequency to 24.5 kHz
utime.sleep(alarmTime)
alarmTime = random.randint(2,5)
ultrasonicHorn.freq(30000) # Set frequency to 24.5 kHz
utime.sleep(alarmTime)
alarmTime = random.randint(2,15)
ultrasonicHorn.freq(24500) # Set frequency to 24.5 kHz
utime.sleep(alarmTime)
ultrasonicHorn.duty_u16(0) # Set duty cycle to 50% (65535/2)
def runSprayPump():
runSprayPumpRelay.value(1)
utime.sleep(15)
runSprayPumpRelay.value(0)
def cycleBuzzer():
alarmTime = random.randint(1,4)
buzzer400Hz.value(1)
utime.sleep(alarmTime)
buzzer400Hz.value(0)
def soundSiren():
#alarmTime = random.randint(1,4)
alarmSiren.value(1)
utime.sleep(2)
alarmSiren.value(0)
utime.sleep(5) #initialization delay on boot up
while True:
if PIR.value() == 1:
AliveLED.value(1)
selectAction=random.randint(0,4)
if selectAction == 0:
runSprayPump()
cycleBuzzer()
soundSiren()
ultrasonicAnnoyCycle()
elif selectAction == 1:
cycleBuzzer()
soundSiren()
ultrasonicAnnoyCycle()
runSprayPump()
elif selectAction == 2 :
soundSiren()
ultrasonicAnnoyCycle()
runSprayPump()
cycleBuzzer()
elif selectAction == 3 :
ultrasonicAnnoyCycle()
runSprayPump()
cycleBuzzer()
soundSiren()
elif selectAction == 4 :
ultrasonicAnnoyCycle()
runSprayPump()
else:
AliveLED.value(0)
utime.sleep(3)