The basic 2 gpio circuit is actually quite simple, but includes additional code for convenience and completeness which can be removed if not needed.
Software Notes
Sensor status is shown in a simple Output web page, and also optionally displayed on neo-pixel (if available) by setting a 'user flag'.
A dbug flag is offered for sensor debugging, 0=off, 1=show all sensor activity in the wlog window, 2=show only when both sensors are triggered.
An optional timeout countdown is offered to prevent retrigger annoyance: 0=disabled, non-zero sets the required number of seconds to not re-trigger.
An 'esp' user var allows the program to run on various different esp devices, the 3 included here as demonstration are esp8266, esp32-S2, S3 UNO clone.
Others can be added by assigning some of their gpio's to the arduino 'D'(igital) number sequence... my usual program header lets me choose from more than a dozen of my various esp types.
Many devices use a wemos or arduino UNO standard footprint whose D number pinouts remain consistent even if mapped to different gpio pins.
This allows many different devices to take advantage of readily available wemos or uno shields (UNO esp clones include esp8266, esp32, and S3-N16R8).
The S3 UNO clone has an onboard neo-pixel led (gpio 48), but neo's can easily be added to other esp32's by specifying the appropriate gpio pin number.
Neo's can also be added to esp8266's, but must use gpio2, therefore their Neo.setup syntax is different because it is not looking for a gpio pin number.
A tip used here for the script to automatically work with either esp8266 or esp32 is to test if Bas.ver$ contains "Annex32" then choose the correct syntax.
Another useful tip is that a subroutine can have more than one branch label entry name (and more than one Return exit), and because every PIR or Radar trigger needs to check both sensors, having both their interrupts branch to the same interrupt handler subroutine avoids unnecessary code duplication.
Sensor Logic
The interrupt handler is basically a 2 sensor truth table with 4 possible event states.
The most significant event is when both PIR and Radar are triggered (which is not necessarily both sensors Hi if either uses active-Lo trigger).
That event is ok for giving an audible announcements, but all other events are handled because they all tell a story... the PIR is only forward-sensing, whereas the Radar can sense behind - so the PIR can only show activity outside a perimeter, whereas the Radar can show activity inside the perimeter.
To differentiate between all conditions on a neo, Both is red, PIR-only is orange, Radar-only is yellow, neither is green.
Those 4 obvious events can be tailored to specific needs with different event actions, and according to whether using active-Hi and/or active-Lo sensors.
(all my sensor activities are transmitted by EspNow to remote receivers, some make TTS arrival announcements, others give visual display of all activity)
Hardware Notes
This was developed using 5v active-Hi PIRs, then subsequently implemented using external 12v active-Lo PIRs with appropriate voltage matching.
A possible voltage matching solution could use a small 12v to 5v DC to DC module to convert 12v PIR Out to 5v esp In (the esp is 5v tolerant).
It could be located at the esp, or mounted under the PIR screw terminal cover, sending the 5v PIR triggers down the original PIR signal cable.
The program runs quite happily on esp8266, but I use the S3 UNO clone because it has onboard neo-pixel, and more importantly has external antenna connector and 12v DC jack input, so can supply Vin 12v, 5v, 3v to an UNO proto-shield, which is useful for mounting components like screw terminals to connect the ext 12v PIR.
I used the cheap RCWL-0516 doppler radar modules, whose default range is about 7m, but which can be reduced by soldering a resistor across the R-GN solder pads (shown by the yellow arrow), 1M gives about 5m, 500K about 3m.
Only the modules inner three connections GND, OUT, VIN are used (3V3 is a fixed regulated output).
VIN can be from 5v to 12v, but the signal output is 3v, so it may be possible to mount the module under the PIR terminal cover and power it from 12v, and send its 3v OUT down the original wire straight into the esp. For me, the Radar module was sensitive enough to be fitted directly on the proto-shield.
Incidentally, there was plenty of room to also add an LDR and resistor on the shield for monitoring light level through the clear lid of the waterproof case.
Script
Code: [Local Link Removed for Guests]
'Combined PIR and Radar motion sensors - by Electroguard
'Intended to prevent false triggers, and run on a wide range of devices.
'Sensor indicators are shown in Output web page
'Status is also optionally displayed on neo-pixel
'dbug flag optionally shows events in log window
'----User flags
neos=1 'set neos= to number of required pixels, or neos=0 to disable neo-pixels
esp$="8266" 'choose device type from below, or add your own
timeout=0 'number of secs to timeout after trigger without retriggering, 0 to disable
dbug=1 '2=only both, 1=show all activity in log windows, 0=dbug off
'--------------
countdown=0
select case esp$
case "8266" : D0=16: D1=5: D2=4: D3=0: D4=2: D5=14: D6=12: D7=13: D8=15: neopin=d4
case "s2" : D0=5: D1=35:D2=33:D3=18:D4=16:D5=7:D6=9:D7=11:D8=12: neopin=d4 ': ledpin=15
case "unos3": A0=2:A1=1:A2=7:A3=6:A4=4:A5=4: D0=5:D1=1:D2=18:D3=17:D4=19:D5=20:D6=3:D7=14:D8=21:D9=46:D10=10:D11=11:D12=13:D13=12:D14=8:D15=9: neopin=48
end select
pirled=1: pirpin=d6: pin.mode pirpin, input, pullup
interrupt pirpin, pirtrigger
radarled=1: radarpin=d7: pin.mode radarpin, input, pullup
interrupt radarpin, radartrigger
if neos>0 then 'automatically use correct syntax for Annex or Annex32
if instr(bas.ver$,"Annex32")=0 then neo.setup neos else neo.setup neopin, neos
neo.strip 0, neos-1, 0, 0, 0 'reset R, G, B, leds to off
endif
onhtmlreload web
gosub web
timer0 1000, tick 'only needed for no-retrigger timeout countdown
wait
web:
a$="": cls
autorefresh 1000
a$=a$ + "<div style='display:table;margin-right:auto;margin-left:auto;text-align:center;'>"
a$=a$ + "<table text-align:left>"
a$=a$ + "<tr><td>" + led$(pirled) + " PIR" + "</tr></td>"
a$=a$ + "<tr><td>" + led$(radarled) + " RADAR" + "</tr></td>"
a$=a$ + "</table></div><br>"
html a$
return
pirtrigger:
radartrigger:
if (pin(pirpin)=1) and (pin(radarpin)=1) then 'PIR on, Radar on
if (countdown=0) then
if dbug>=1 then wlog "Both"
countdown=timeout 'start no-retrigger countdown
pirled=0: radarled=0 '0=red status led$
if neos>0 then neo.strip 0, neos-1, 100, 0 ,0 'increase to make brighter
else
if dbug>=1 then wlog "no-retrigger timeout"
endif
endif
if (pin(pirpin)=1) and (pin(radarpin)=0) then 'PIR on, Radar off
if dbug=1 then wlog "PIR-only"
pirled=0: radarled=1
if neos>0 then neo.strip 0, neos-1, 50, 100, 0
endif
if (pin(pirpin)=0) and (pin(radarpin)=1) then 'PIR off, Radar on
if dbug=1 then wlog "Radar-only"
pirled=1: radarled=0
if neos>0 then neo.strip 0, neos-1, 50, 50, 0
endif
if (pin(pirpin)=0) and (pin(radarpin)=0) then 'PIR off, Radar off
if dbug=1 then wlog "both off"
pirled=1: radarled=1 '0=green status led$
if neos>0 then neo.strip 0, neos-1, 0, 100, 0
endif
return
tick:
if countdown>0 then countdown=countdown-1 'no-retrigger timeout countdown
return
'------------ End ------------