Contents — 26 sections
Project Overview
Invisible modulated light beams strung along a boundary that sound the alarm the instant someone crosses them — and that cannot be quietly fooled by ambient light or a covered emitter.
A perimeter trip sensor is one of the oldest ideas in security — a beam of light across a boundary that, when broken, means something crossed it — and it is still one of the most useful, because it detects an intrusion at the fence line rather than after the intruder is already inside. This project builds a robust, multi-zone version: emitters send beams across the gaps you want to protect (a gateway, a wall-top, a garden boundary) to receivers on the far side, and the instant a beam is interrupted the system raises a located alarm. The difference between a toy and a real security device is entirely in how it handles the ways such a beam can be defeated or falsely triggered, and that is what this design focuses on.
The core robustness trick is modulation. A naive beam — a laser or LED shining steadily at a receiver — is trivially fooled: bright sunlight or a torch can swamp the receiver so it never notices the real beam breaking, and it false-triggers on every passing shadow or cloud. So the emitter is switched on and off at a specific frequency, and the receiver looks only for light pulsing at that exact frequency, ignoring steady ambient light entirely. This makes the beam robust in daylight, and — crucially — makes it far harder to defeat: an attacker cannot simply shine their own light to hold the receiver "made", because it must be the right frequency. The system also detects the two tell-tales of tampering: a beam that goes and stays broken (something is blocking it, or the emitter is covered/failed) and a receiver that suddenly sees an unmodulated flood (someone trying to spoof it).
Around that core sits practical security engineering: multiple beams as separate zones so the alarm tells you where the perimeter was crossed; retroreflective operation (emitter and receiver on the same side, beam bounced off a reflector) to simplify wiring on long runs; supervision so a cut wire or dead emitter raises a fault rather than a silent blind spot; and a networked alarm output to a control panel or siren. It is honest about a beam sensor's nature — it detects a crossing, not who or what crossed, and a determined intruder aware of the beam can try to step over or under it, which is why beams are layered and combined with other sensors — but as a fast, all-weather, hard-to-fool line-of-sight detector, a well-built modulated beam perimeter is a genuinely strong first line of defence.
What this project does
- Projects modulated light beams across the boundaries you want to protect
- Detects a beam break instantly and raises a located, per-zone alarm
- Rejects ambient light (and daylight) by detecting only the modulation frequency
- Resists spoofing (wrong-frequency floods) and detects blocked/covered beams
- Supervises for cut wires / dead emitters (fault, not silent blind spot)
- Supports multiple zones and retroreflective long runs
- Drives a networked alarm / control-panel output
Real-World Applications
| Setting | How it is used |
|---|---|
| Property and yard perimeters | Beams along a wall-top, gateway or garden boundary that alarm the moment someone crosses, before they reach the building. |
| Doorways and corridors | A single beam across an entry that detects passage, for shops, storerooms or restricted corridors. |
| Industrial / site boundaries | Multi-zone perimeter protection around yards, compounds and equipment, with located alarms. |
| Machine-guarding-style safety (adapted) | Beam-break to stop or warn when someone enters a hazardous zone (with proper safety-rated hardware for true safety use). |
Deployment contexts where a build of this kind earns its keep.
Features & Capabilities
- Modulated beam — robust in daylight and hard to spoof
- Per-zone located alarms (which beam was crossed)
- Tamper detection: blocked beam and spoof-flood recognition
- Supervision so faults are never silent blind spots
- Retroreflective option for simple long-run wiring
- Networked alarm output to panel/siren
- Honest layering with other sensors for real security
Difficulty, Time & Required Skills
| Attribute | Value |
|---|---|
| Difficulty level | Intermediate |
| Estimated completion time | 10–16 hours |
| Indicative build cost | ₹2,800 – ₹4,500 |
| Primary discipline | Security |
| Reference platform | ESP32 DevKit V1 (ESP-WROOM-32) |
Skills you should have (or will pick up)
- Building a modulated optical emitter and a frequency-selective receiver
- Rejecting ambient light and detecting spoofing/tamper
- Multi-zone alarm logic and supervision
- Retroreflective beam alignment
- Driving alarm outputs and networking events
Bill of Materials
Every part below is commonly available from Indian and international hobby-electronics suppliers. Prices are indicative 2026 retail figures in Indian rupees and will drift — treat them as a budgeting guide, not a quotation.
| Component | Key specification | Qty | Approx. cost |
|---|---|---|---|
| ESP32 DevKit V1 (ESP-WROOM-32) Wi-Fi transmit bursts peak near 500 mA — size the regulator accordingly. | Dual-core Xtensa LX6 @ 240 MHz, 520 KB SRAM, 4 MB flash, Wi-Fi 802.11 b/g/n + BLE 4.2, 34 GPIO, 18× 12-bit ADC, 2× 8-bit DAC | 1 | ₹450 |
| LDR (GL5528 photoresistor) + 10 kΩ divider Cheap and rugged, but non-linear and unit-to-unit variation is large. | 10–20 kΩ at 10 lx, 1 MΩ dark, peak response 540 nm | 1 | ₹15 |
| 5 V single-channel opto-isolated relay module Cut the JD-VCC jumper and feed the coil separately for true isolation. | SPDT contacts rated 10 A @ 250 VAC / 10 A @ 30 VDC, opto-isolated input | 1 | ₹90 |
| Active piezo buzzer 5 V Active buzzers make tone on DC; passive ones need a PWM carrier. | 85 dB at 10 cm, 2.3 kHz resonance, 12 mm diameter | 1 | ₹25 |
| 0.96″ SSD1306 OLED display Static images burn in — invert or scroll the screen periodically. | 128 × 64 monochrome, 1.3–3.3 V logic, 100 kHz–400 kHz I²C | 1 | ₹250 |
| Reed switch + magnet pair (door contact) Wire it normally-closed so a cut cable reads the same as an open door — fail-safe by construction. | NO contact, 10–15 mm operating gap, 100 V / 0.5 A, > 10⁷ operations | 1 | ₹55 |
| 5 V 3 A regulated SMPS adapter Measure the real output — many "3 A" adapters sag below 4.7 V at 2 A. | 100–240 VAC in, 5 V ±5 % out, 3 A, short-circuit and over-voltage protection | 1 | ₹350 |
| Laser/IR emitter modules IR is covert; visible laser is easy to align | Modulated laser or IR LED emitters, one per beam/zone | 4 | ₹1,600 |
| Modulation-tuned receivers An IR-remote-style demodulator gives strong ambient rejection | Photodiode/phototransistor front-ends (or IR receiver ICs tuned to the carrier) | 4 | ₹1,600 |
| Retroreflectors (optional) | Corner-cube reflectors for same-side emitter/receiver runs | 4 | ₹1,200 |
| Weatherproof beam housings | Aligned, hooded housings that shade the receiver and hold aim | 4 | ₹2,400 |
Estimated total: ₹8,035, excluding tools, shipping and consumables.
Tools and consumables
- Soldering iron (temperature controlled, 350 °C) with 0.8 mm 60/40 or lead-free solder
- Digital multimeter — continuity, DC volts and current ranges
- Wire strippers, flush cutters and a small set of precision screwdrivers
- Heat-shrink tubing and a heat gun (or a lighter, carefully)
- A laptop with a USB port and the toolchain listed above
Hardware Specifications
| Part | Specification | Supply | Interface | Reference |
|---|---|---|---|---|
| ESP32 DevKit V1 (ESP-WROOM-32) | Dual-core Xtensa LX6 @ 240 MHz, 520 KB SRAM, 4 MB flash, Wi-Fi 802.11 b/g/n + BLE 4.2, 34 GPIO, 18× 12-bit ADC, 2× 8-bit DAC | 3.3 V logic / 5 V USB | UART, SPI, I²C, I²S, CAN, PWM | Datasheet |
| LDR (GL5528 photoresistor) + 10 kΩ divider | 10–20 kΩ at 10 lx, 1 MΩ dark, peak response 540 nm | any (passive) | Analogue divider | Datasheet |
| 5 V single-channel opto-isolated relay module | SPDT contacts rated 10 A @ 250 VAC / 10 A @ 30 VDC, opto-isolated input | 5 V coil | Digital (active-low) | Datasheet |
| Active piezo buzzer 5 V | 85 dB at 10 cm, 2.3 kHz resonance, 12 mm diameter | 3–5 V | Digital / PWM | Datasheet |
| 0.96″ SSD1306 OLED display | 128 × 64 monochrome, 1.3–3.3 V logic, 100 kHz–400 kHz I²C | 3.3–5 V | I²C (0x3C) | Datasheet |
| Reed switch + magnet pair (door contact) | NO contact, 10–15 mm operating gap, 100 V / 0.5 A, > 10⁷ operations | passive | Digital with pull-up | Datasheet |
| 5 V 3 A regulated SMPS adapter | 100–240 VAC in, 5 V ±5 % out, 3 A, short-circuit and over-voltage protection | 5 V | DC barrel / USB | Datasheet |
Consolidated electrical and interface specifications for every active part in the build.
Power Budget & Supply Sizing
Add up the typical active current of every part, then size the supply with at least 50 % headroom so transmit bursts and motor inrush never brown out the controller.
| Load | Supply rail | Typical current (mA) | Notes |
|---|---|---|---|
| ESP32 DevKit V1 (ESP-WROOM-32) | 3.3 V logic / 5 V USB | 160 | Wi-Fi transmit bursts peak near 500 mA — size the regulator accordingly. |
| LDR (GL5528 photoresistor) + 10 kΩ divider | any (passive) | 0.3 | Cheap and rugged, but non-linear and unit-to-unit variation is large. |
| 5 V single-channel opto-isolated relay module | 5 V coil | 70 | Cut the JD-VCC jumper and feed the coil separately for true isolation. |
| Active piezo buzzer 5 V | 3–5 V | 30 | Active buzzers make tone on DC; passive ones need a PWM carrier. |
| 0.96″ SSD1306 OLED display | 3.3–5 V | 20 | Static images burn in — invert or scroll the screen periodically. |
| 5 V 3 A regulated SMPS adapter | 5 V | 3000 | Measure the real output — many "3 A" adapters sag below 4.7 V at 2 A. |
Summed typical draw is 3280.3 mA. With a 1.5× design margin the supply should deliver at least 5000 mA continuously at the stated rail voltage.
Software Requirements & Development Environment
Reference toolchain: Arduino IDE 2.3.x with the ESP32 board package 3.x (or PlatformIO on VS Code). Anything newer normally works; anything older may lack the board definitions used here.
- Install the Arduino IDE 2.3.x (or PlatformIO if you prefer a real editor and dependency locking).
- Add
https://espressif.github.io/arduino-esp32/package_esp32_index.jsonunder File → Preferences → Additional Board Manager URLs, then install esp32 from the Boards Manager. - Set the correct port under Tools → Port. On Linux add yourself to the
dialoutgroup:sudo usermod -aG dialout $USERand log out and back in. - Open the Serial Monitor at 115200 baud — every sketch here logs its state there.
- Keep File → Preferences → Show verbose output during: compilation switched on while you are debugging build errors.
Required libraries
| Library | Why it is needed | Install |
|---|---|---|
| WiFi (ESP32 core) bundled | Station/AP connection management for the ESP32. | Bundled with the ESP32 Arduino core |
| PubSubClient 2.8 | Lightweight MQTT 3.1.1 client for constrained devices. | Library Manager → "PubSubClient" by Nick O'Leary |
| Adafruit SSD1306 + GFX 2.5.x | Framebuffer and text/graphics primitives for the OLED. | Library Manager → "Adafruit SSD1306" |
| ArduinoJson 7.x | Zero-allocation JSON serialisation and parsing. | Library Manager → "ArduinoJson" by Benoit Blanchon |
| Preferences (NVS) bundled | Wear-levelled key/value storage in ESP32 flash for settings. | Bundled with the ESP32 core |
Block Diagram
The block diagram shows the functional decomposition of the system — what senses, what decides, what acts, and where the data ends up.
Circuit Diagram & Wiring
Every signal line in the build is shown below, followed by a pin-by-pin connection table you can work through with a multimeter in hand.
| Peripheral | Peripheral pin | Controller pin | Signal |
|---|---|---|---|
| Emitter drive | PWM | GPIO 25 | Modulated carrier to all emitters |
| Receiver z1 | OUT | GPIO 34 | Zone 1 demodulated beam |
| Receiver z2 | OUT | GPIO 35 | Zone 2 demodulated beam |
| Receiver z3/z4 | OUT | GPIO 32/33 | Zones 3–4 |
| Siren relay | IN | GPIO 26 | Alarm output |
| Buzzer | IN | GPIO 27 | Local sounder |
| OLED | SDA/SCL | GPIO 21/22 | Zone status |
| Tamper/supervision | in | ADC/EOL | Wire/emitter fault detect |
Wire one row at a time and tick it off — most "it does not work" reports trace back to a single swapped pair.
Wiring explanation
- Drive all emitters from one modulated carrier (a PWM at a few kHz) so every receiver can lock to the same frequency and reject ambient light.
- Use frequency-selective receivers — an IR-remote-style demodulator IC, or a photodiode front-end with a band-pass — so only the modulated beam registers as "made".
- Hood and align each receiver to shade it from direct sun and hold aim; a receiver staring at bright sky is easily blinded.
- Supervise each zone (e.g. end-of-line resistor / expected signal level) so a cut wire or a dead emitter reads as a fault distinct from a clean beam.
- For long runs, use retroreflective beams (emitter+receiver together, reflector opposite) to keep wiring on one side.
System Architecture
Read the stack from the bottom up: physical hardware, the firmware that drives it, the transport that moves data off the device, and the software a human actually looks at.
Working Principle
A beam sensor is a line-of-sight detector: light travels from an emitter to a receiver, and an object crossing the line interrupts it. The value is where it detects — at the boundary, the moment of crossing — and its speed and all-weather reliability. But a beam is only as good as its resistance to the two failure modes that plague naive designs: being blinded/false-triggered by ambient light, and being defeated by an aware adversary. The engineering that separates a real perimeter sensor from a photocell toy is almost entirely about those two problems, and modulation is the key to both.
Modulation means switching the emitter on and off at a chosen carrier frequency (a few kilohertz is typical, exactly as an infrared TV remote does) and building the receiver to respond only to light flickering at that frequency. Steady light — sunlight, room lighting, a torch — is at "DC" from the receiver's point of view and is filtered out entirely, so the beam works in bright daylight where a steady beam would be swamped. This is not just about daylight: it is a security property. An attacker who wants to hold the receiver "made" while they cut the real beam cannot just shine any bright light at it; they would have to reproduce the exact modulation, which is far harder and can be made harder still (rolling codes, unusual frequencies). Modulation turns the beam from something a shadow can trip and a torch can spoof into something robust and defensible.
Good perimeter design then treats the receiver's state as three-valued, not two. A properly-made, correctly-modulated beam is clear. A beam that is interrupted is a break — the intrusion alarm. But a receiver seeing a strong unmodulated flood, or a beam that has gone dark and stayed dark, is a tamper/fault, not a normal clear or break — someone may be trying to spoof it, or the emitter is covered, or a wire is cut. Recognising this third state is what stops the classic defeat where an intruder blinds or disables a sensor and the system reports "all clear". Coupled with supervision — continuously checking that each zone's wiring and signal are healthy (an end-of-line reference, an expected modulated level) — a fault becomes an alarm-worthy event rather than a silent blind spot. A perimeter you cannot trust to notice its own sabotage is not a perimeter.
The rest is practical layering. Multiple beams become independent zones so an alarm is located ("east gate beam broken"), which matters for response. Retroreflective operation — emitter and receiver together, the beam bounced back off a corner-cube reflector — halves the wiring on long runs. And the whole thing feeds a networked alarm output to a panel or siren. The design is candid about what a beam is and is not: it detects a crossing, not an identity, so it cannot tell you whether the thing that broke the beam was an intruder, a large animal or a wind-blown branch (which is why beam height, hooding, and combination with other sensors matter); and a determined, aware intruder may try to step over or crawl under a single beam (which is why beams are layered at multiple heights and paired with volumetric sensors). Used with those limits in mind, though, a modulated, supervised, multi-zone beam perimeter is a fast, robust, and genuinely hard-to-fool first line of detection.
The maths behind it
Modulated detection (ambient rejection)
Emitter driven at carrier f_c (e.g. 4 kHz).
Receiver band-passes / synchronously detects at f_c:
beam_made when demod amplitude @ f_c > A_hi
beam_broken when it falls below A_lo (hysteresis)
Steady ambient light is at ~0 Hz → rejected. Only light
pulsing at f_c counts, giving daylight robustness and
spoof resistance.
Three-state zone logic
For each zone, from the demodulated level and supervision:
CLEAR : modulated beam present at expected level
BREAK : modulated beam interrupted → intrusion alarm
TAMPER : unmodulated flood high, OR supervision fails
(beam dark+static, wire fault, emitter covered)
TAMPER is alarm-worthy — it is how a defeat attempt shows.
Supervision (end-of-line)
Expect a known healthy signature per zone:
healthy if A_lo < demod_level < A_sat AND wiring intact
fault if level ~0 static (cut/dead) OR saturated (flood)
Continuous supervision means a disabled zone raises a fault
instead of silently ceasing to protect.
Program Flowchart
The firmware is a single cooperative loop. Nothing blocks for long, so networking, sensing and the user interface all stay responsive.
Assembly Instructions
Build on a breadboard first and only commit to solder once the whole system has run for an hour without a fault.
Build the modulated emitter/receiver pairs
Drive each emitter (laser or IR LED) from a common carrier frequency, and pair it with a frequency-selective receiver (an IR-demodulator IC or a photodiode front-end with a band-pass) that responds only to that frequency.
Hood and align each receiver to shade it from direct sun and hold its aim on the beam.
Lay out zones and supervision
Run separate beams for each area you want located independently, and wire each zone with supervision (end-of-line reference/expected level) so a cut wire or dead emitter is detectable.
For long runs, use retroreflective beams to keep emitter and receiver on the same side.
Wire alarm output and status
Connect a siren/relay and local buzzer for alarms, an OLED for zone status, and Wi-Fi to a panel. Ensure the alarm sounds locally regardless of the network.
Step-by-Step Implementation Guide
Work through these in order. Each step ends in something you can observe, so a failure is always localised to the step you just finished.
Drive the carrier and demodulate
Emit a steady carrier on all beams and evaluate each receiver's demodulated level, classifying each zone as clear, broken, or tampered, with hysteresis to avoid flicker.
cppbeam-zones.inoenum Zone { CLEAR, BREAK, TAMPER }; #define A_LO 400 // ADC counts: below = beam broken #define A_HI 700 // above = beam made #define A_SAT 3800 // near-saturation with no modulation = flood // Evaluate one zone from its demodulated receiver level + modulation check. Zone evalZone(int demodLevel, bool modulationPresent) { if (demodLevel > A_SAT && !modulationPresent) return TAMPER; // spoof flood if (demodLevel < A_LO) return BREAK; // interrupted if (demodLevel > A_HI && modulationPresent) return CLEAR; // healthy beam return TAMPER; // ambiguous/fault } void scanZones(int levels[], bool mod[], int n) { for (int z = 0; z < n; z++) { Zone s = evalZone(levels[z], mod[z]); if (s == BREAK) raiseAlarm(z, "intrusion"); if (s == TAMPER) raiseAlarm(z, "tamper/fault"); setZoneStatus(z, s); } }if (demodLevel > A_SAT && !modulationPresent) return TAMPERA strong but unmodulated flood is the signature of a spoofing attempt — bright light held on the receiver at the wrong frequency — and is treated as tamper, not clear.if (demodLevel < A_LO) return BREAKA collapse of the demodulated level means the modulated beam is interrupted — the intrusion alarm.if (demodLevel > A_HI && modulationPresent) return CLEAROnly a strong signal that is genuinely modulated at the carrier counts as a healthy, clear beam — steady ambient light cannot fake it.return TAMPER; // ambiguous/faultAnything that is neither a clean make nor a clean break — a marginal or static signal — is treated as a fault rather than silently ignored, so a disabled zone cannot masquerade as clear.Supervise, alarm and network
Continuously supervise each zone's wiring/signal, sound the local alarm on break or tamper, latch the located event, and report zone states to the panel.
Complete Source Code
The listing below is complete and compiles as written — there are no elided sections. Read the annotations under each block before you upload it.
/* ═══════════════════════════════════════════════════════════════
Perimeter Laser Trip Sensor — ESP32, modulated multi-zone beams
Emits modulated beams, detects breaks per zone, rejects ambient
light and spoof floods, supervises for wire/emitter faults, and
raises located alarms to a local siren and a networked panel.
══════════════════════════════════════════════════════════════════ */
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#define PIN_CARRIER 25 // modulated emitter drive (all beams)
#define PIN_SIREN 26
#define PIN_BUZZER 27
#define NZONES 4
#define CARRIER_HZ 4000
#define A_LO 400
#define A_HI 700
#define A_SAT 3800
const int RX[NZONES] = {34, 35, 32, 33};
WiFiClient net; PubSubClient mqtt(net);
Adafruit_SSD1306 oled(128,64,&Wire);
enum Zone { CLEAR, BREAK, TAMPER };
const char *ZS[] = {"clear","BREAK","TAMPER"};
Zone state[NZONES];
bool latched[NZONES] = {false};
// Synchronous detection: sample the receiver in phase with the carrier
// to measure how much of the signal is at the carrier frequency.
int demodLevel(int pin, bool &modulationPresent) {
long onSum = 0, offSum = 0; const int N = 64;
for (int i = 0; i < N; i++) {
digitalWrite(PIN_CARRIER, HIGH); delayMicroseconds(125); // half period @4kHz
onSum += analogRead(pin);
digitalWrite(PIN_CARRIER, LOW); delayMicroseconds(125);
offSum += analogRead(pin);
}
int amplitude = (onSum - offSum) / N; // modulated component
int average = (onSum + offSum) / (2 * N); // total incl. ambient
modulationPresent = amplitude > 60; // real carrier present?
// Report the modulated amplitude as the "beam level"; average detects flood.
if (average > A_SAT && !modulationPresent) return A_SAT + 1; // signal flood
return constrain(amplitude, 0, 4095);
}
Zone evalZone(int level, bool mod) {
if (level > A_SAT && !mod) return TAMPER;
if (level < A_LO) return BREAK;
if (level > A_HI && mod) return CLEAR;
return TAMPER;
}
void raiseAlarm(int z, const char *why) {
latched[z] = true;
digitalWrite(PIN_SIREN, HIGH);
digitalWrite(PIN_BUZZER, HIGH);
char m[100];
snprintf(m, sizeof m, "{\"zone\":%d,\"event\":\"%s\"}", z+1, why);
mqtt.publish("perimeter/alarm", m);
}
void setup() {
Serial.begin(115200);
pinMode(PIN_CARRIER, OUTPUT);
pinMode(PIN_SIREN, OUTPUT);
pinMode(PIN_BUZZER, OUTPUT);
for (int z = 0; z < NZONES; z++) analogSetPinAttenuation(RX[z], ADC_11db);
Wire.begin(21,22); oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
WiFi.begin(WIFI_SSID, WIFI_PASS);
mqtt.setServer(MQTT_HOST, 1883);
}
void loop() {
if (!mqtt.connected() && WiFi.status()==WL_CONNECTED) mqtt.connect("perim-1");
mqtt.loop();
bool anyAlarm = false;
oled.clearDisplay(); oled.setCursor(0,0);
for (int z = 0; z < NZONES; z++) {
bool mod;
int level = demodLevel(RX[z], mod);
Zone s = evalZone(level, mod);
state[z] = s;
if (s == BREAK) { raiseAlarm(z, "intrusion"); anyAlarm = true; }
if (s == TAMPER) { raiseAlarm(z, "tamper/fault"); anyAlarm = true; }
if (latched[z]) anyAlarm = true;
oled.printf("Z%d: %s\n", z+1, ZS[s]);
}
oled.display();
// siren stays on while any zone is latched (until acknowledged)
if (!anyAlarm) { digitalWrite(PIN_SIREN, LOW); digitalWrite(PIN_BUZZER, LOW); }
delay(50); // ~20 Hz scan — fast enough to catch a crossing
}
Configuration & Calibration
Configuration steps
- Set the carrier frequency and the A_LO/A_HI/A_SAT thresholds to your emitters/receivers and range.
- Define the zones (which receiver is which boundary) and their supervision expectations.
- Configure alarm latching/acknowledgement and the siren/panel outputs.
- Choose visible-laser (easy alignment) vs IR (covert) emitters and set beam heights/layering.
Calibration procedure
An uncalibrated sensor produces confident, precise, wrong numbers. Do this once per physical unit and record the constants.
Beam levels
With each beam aligned and clear, record the demodulated level; set A_HI below it and A_LO above the broken level, with margin, for reliable make/break.
Ambient/spoof rejection
Shine steady light (torch/sun) at a receiver and confirm it does not read "made"; confirm an unmodulated flood registers as tamper.
Supervision
Disconnect/cover an emitter and confirm the zone reads fault/tamper, not clear — proving the perimeter notices its own sabotage.
Network Architecture & Connectivity
Communication protocol
Zone states and located alarm/tamper events publish immediately; the local siren fires independent of the network so a crossing is signalled even if the link is down.
| Topic / endpoint | Direction | Payload |
|---|---|---|
perimeter/alarm | node → panel | zone, event (intrusion/tamper), time |
perimeter/zones | node → panel | per-zone clear/break/tamper state |
perimeter/ack | panel → node | acknowledge/clear a latched alarm |
Message contract between the device and the broker.
Cloud platform configuration
A panel shows the perimeter as a zone map, logs every located alarm and tamper, and lets an operator acknowledge alarms; multiple controllers cover a large site.
Dashboard setup
A boundary/zone map coloured by state, an event log of located intrusions and tampers, and supervision/health indicators per zone.
Mobile app integration
Immediate located alerts on intrusion or tamper, with acknowledge.
Security considerations
- Keep the local siren and alarm logic independent of the network so a lost link cannot disable protection.
- Authenticate acknowledgements so only authorised operators can clear alarms.
- Treat tamper/supervision events as first-class alarms — they are how a defeat attempt appears.
Testing Procedure & Expected Output
Test from the bottom up. Confirm power, then each sensor in isolation, then the integrated loop — the first failing step tells you exactly where to look.
| Test | What you should see |
|---|---|
| Break a beam by walking through | That zone alarms instantly and is located; alarm latches |
| Shine a torch at a receiver | No "clear" spoof; steady light rejected, flood flagged as tamper |
| Cover/disable an emitter | Zone reads tamper/fault — not a silent clear |
| Cut a zone wire (bench) | Supervision raises a fault |
| Operate in bright daylight | Beams stay reliable thanks to modulation |
| Acknowledge an alarm | Latched siren clears; zones resume normal monitoring |
Bench-test checklist. If a row fails, stop and fix it before moving on.
Expected output
The panel/OLED shows each zone's state (clear/break/tamper); alarms are located by zone and latched until acknowledged.
{
"zone": 2,
"event": "intrusion",
"time": "2026-07-27T02:14:31"
}
A located intrusion on zone 2 (say the east gate beam) — the operator knows exactly where the perimeter was crossed, and a tamper event would be flagged the same way if someone tried to blind or cut a beam.
Troubleshooting: Common Errors & Fixes
Performance Optimisation
- Scan fast enough (tens of Hz) to catch a quick crossing, but no faster than needed.
- Use synchronous detection with enough samples to reject ambient reliably without heavy CPU.
- Drive all emitters from one carrier to keep timing simple and receivers comparable.
- Latch alarms so brief breaks are never missed between scans.
- Replace every
delay()with amillis()comparison — blocking delays are the single most common cause of dropped readings. - Sample sensors on a fixed cadence and publish on a slower one; you almost never need to transmit at the sampling rate.
- Move networking into its own FreeRTOS task so a slow DNS lookup cannot stall the control loop.
- Use
uint8_t/uint16_twhere the range allows; on an 8-bit AVR a 32-bit add costs four times as much. - Batch several samples into one MQTT publish. Radio time, not CPU time, dominates the energy budget.
- Set the MQTT keep-alive to a value that matches your reporting interval so the broker does not churn reconnections.
- For battery builds use deep sleep between samples: an ESP32 drops from ~160 mA awake to about 10 µA asleep, which is the difference between days and months of runtime.
Safety Precautions
- A beam sensor detects a crossing, not an identity or intent; layer beams and combine with other sensors for real security.
- Use eye-safe emitter power, especially with lasers; never aim a laser where it could reach eyes at close range.
- For genuine machine-safety, use only properly safety-rated light curtains/guards — this is a security sensor, not a certified safety device.
- Keep the local alarm functional independent of the network so protection does not depend on connectivity.
- Mains voltage kills. Anything on the load side of the relay is at 230 V. Do not work on a powered circuit, and never leave exposed mains wiring on a bench where someone could touch it.
- Keep at least 6 mm of creepage between the mains and low-voltage sides of any board you make, and never route mains tracks under the microcontroller.
- Have a qualified electrician do the final installation into a consumer unit or wall fitting. In most jurisdictions this is a legal requirement, not a suggestion.
- Fit an RCD/RCBO upstream and fuse the load appropriately for its rating.
- Wear eye protection when soldering or cutting, and solder in a ventilated space — rosin flux fumes are a respiratory irritant.
- Power the circuit through a bench supply with a current limit while you are testing. A 300 mA limit turns a wiring mistake into a beep instead of a dead board.
- Disconnect power before changing any wiring. Hot-plugging a sensor onto a live bus is the fastest way to lose a controller.
Maintenance
- Clean emitter/receiver optics and reflectors; dirt reduces range and causes false breaks.
- Re-verify alignment after weather/vibration; check beam levels against the calibration.
- Test each zone's tamper/supervision periodically so faults still alarm.
- Confirm the siren, latching and acknowledge paths work.
- Re-check every screw terminal and header after the first week — thermal cycling loosens connections that felt tight on day one.
- Keep the broker and dashboard containers patched, and rotate device credentials at least once a year.
- Recalibrate at the interval given in the calibration section, and keep the constants in a text file next to the firmware — not only in flash.
- Keep a short logbook of firmware versions and what changed. Six months later you will not remember why that constant is 1.083.
Future Improvements & Upgrades
A working v1 is a platform, not a finish line. These are the upgrades that add the most capability for the least rework.
- Add rolling-code modulation for even stronger anti-spoof security.
- Add beam-height layering and pairing with PIR/camera for classification of what crossed.
- Add battery backup so the perimeter survives mains loss (a classic attack).
- Auto-alignment feedback to speed installation and flag drift.
- Design a proper PCB. Once the breadboard version has run for a month, moving to a two-layer board removes the intermittent-contact failures that dominate prototype faults.
- Add over-the-air firmware updates so you never have to physically reach a deployed node again.
- Add persistent local storage (microSD or the on-chip flash) so a network outage does not create a hole in your data.
- Move configuration out of the source: a captive-portal setup page or a JSON config file makes the build reusable without a recompile.
- Add a battery and solar option so the unit survives a power cut and can be sited away from a socket.
- Write a small test harness that feeds synthetic sensor values through the decision logic, so you can validate thresholds without physically triggering the event.
Frequently Asked Questions
References & Learning Resources
These are the primary sources worth reading in full. Manufacturer datasheets always outrank forum posts when the two disagree.