Siddhant Kumar
Project 050 · Security

Perimeter Laser Trip Sensor.

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.

Intermediate 10–16 hours 33 min read LaserAlarmSecurity
Jump to source Bill of materials
Perimeter Laser Trip Sensor — reference build illustration MCU VCC · GND · SIG · NC
Difficulty
Intermediate
Build time
10–16 hours
Indicative cost
₹2,800 – ₹4,500
Platform
ESP32 DevKit V1 (ESP-WROOM-32)
Category
Security
Last updated
28 July 2026
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.

A wall-mounted CCTV surveillance camera
Modulated beams along a boundary detect a crossing at the perimeter, before an intruder reaches the building. Photograph sourced from Wikimedia Commons — CCTV camera.jpg. Reused under the licence stated on that page; please check it before republishing.

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

SettingHow it is used
Property and yard perimetersBeams along a wall-top, gateway or garden boundary that alarm the moment someone crosses, before they reach the building.
Doorways and corridorsA single beam across an entry that detects passage, for shops, storerooms or restricted corridors.
Industrial / site boundariesMulti-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

AttributeValue
Difficulty levelIntermediate
Estimated completion time10–16 hours
Indicative build cost₹2,800 – ₹4,500
Primary disciplineSecurity
Reference platformESP32 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.

ComponentKey specificationQtyApprox. 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 DAC1₹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 nm1₹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 input1₹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 diameter1₹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²C1₹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⁷ operations1₹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 protection1₹350
Laser/IR emitter modules
IR is covert; visible laser is easy to align
Modulated laser or IR LED emitters, one per beam/zone4₹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 runs4₹1,200
Weatherproof beam housingsAligned, hooded housings that shade the receiver and hold aim4₹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

PartSpecificationSupplyInterfaceReference
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 DAC3.3 V logic / 5 V USBUART, SPI, I²C, I²S, CAN, PWMDatasheet
LDR (GL5528 photoresistor) + 10 kΩ divider10–20 kΩ at 10 lx, 1 MΩ dark, peak response 540 nmany (passive)Analogue dividerDatasheet
5 V single-channel opto-isolated relay moduleSPDT contacts rated 10 A @ 250 VAC / 10 A @ 30 VDC, opto-isolated input5 V coilDigital (active-low)Datasheet
Active piezo buzzer 5 V85 dB at 10 cm, 2.3 kHz resonance, 12 mm diameter3–5 VDigital / PWMDatasheet
0.96″ SSD1306 OLED display128 × 64 monochrome, 1.3–3.3 V logic, 100 kHz–400 kHz I²C3.3–5 VI²C (0x3C)Datasheet
Reed switch + magnet pair (door contact)NO contact, 10–15 mm operating gap, 100 V / 0.5 A, > 10⁷ operationspassiveDigital with pull-upDatasheet
5 V 3 A regulated SMPS adapter100–240 VAC in, 5 V ±5 % out, 3 A, short-circuit and over-voltage protection5 VDC barrel / USBDatasheet

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.

LoadSupply railTypical current (mA)Notes
ESP32 DevKit V1 (ESP-WROOM-32)3.3 V logic / 5 V USB160Wi-Fi transmit bursts peak near 500 mA — size the regulator accordingly.
LDR (GL5528 photoresistor) + 10 kΩ dividerany (passive)0.3Cheap and rugged, but non-linear and unit-to-unit variation is large.
5 V single-channel opto-isolated relay module5 V coil70Cut the JD-VCC jumper and feed the coil separately for true isolation.
Active piezo buzzer 5 V3–5 V30Active buzzers make tone on DC; passive ones need a PWM carrier.
0.96″ SSD1306 OLED display3.3–5 V20Static images burn in — invert or scroll the screen periodically.
5 V 3 A regulated SMPS adapter5 V3000Measure 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.json under 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 dialout group: sudo usermod -aG dialout $USER and 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

LibraryWhy it is neededInstall
WiFi (ESP32 core) bundledStation/AP connection management for the ESP32.Bundled with the ESP32 Arduino core
PubSubClient 2.8Lightweight MQTT 3.1.1 client for constrained devices.Library Manager → "PubSubClient" by Nick O'Leary
Adafruit SSD1306 + GFX 2.5.xFramebuffer and text/graphics primitives for the OLED.Library Manager → "Adafruit SSD1306"
ArduinoJson 7.xZero-allocation JSON serialisation and parsing.Library Manager → "ArduinoJson" by Benoit Blanchon
Preferences (NVS) bundledWear-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.

Perimeter Laser Trip Sensor — system block diagramFunctional block diagram of the Perimeter Laser Trip Sensor system. BeamModulated emittercarrier freqTuned receiverfreq-selectiveDecideESP32break/tamper/faultZonesper-beam stateAlarmSiren/relaylocated alarmSupervisionfault not silentControlPanelzone + statusrightrightnone
Perimeter Laser Trip Sensor — system block diagram

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.

Perimeter Laser Trip Sensor — wiring schematicConnection schematic showing which controller pin drives each peripheral. Sensors / InputsControllerActuators / OutputsESP32 DevKit V1(ESP-WROOM-32)3.3 V logic / 5 V USBEmitter driveGPIO 25Modulated carrier toall emittersReceiver z1GPIO 34Zone 1 demodulatedbeamReceiver z2GPIO 35Zone 2 demodulatedbeamReceiver z3/z4GPIO 32/33Zones 3–4Siren relayGPIO 26Alarm outputBuzzerGPIO 27Local sounderOLEDGPIO 21/22Zone statusTamper/supervisionADC/EOLWire/emitter faultdetect
Perimeter Laser Trip Sensor — wiring schematic
PeripheralPeripheral pinController pinSignal
Emitter drivePWMGPIO 25Modulated carrier to all emitters
Receiver z1OUTGPIO 34Zone 1 demodulated beam
Receiver z2OUTGPIO 35Zone 2 demodulated beam
Receiver z3/z4OUTGPIO 32/33Zones 3–4
Siren relayINGPIO 26Alarm output
BuzzerINGPIO 27Local sounder
OLEDSDA/SCLGPIO 21/22Zone status
Tamper/supervisioninADC/EOLWire/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.
An ESP32 development board with the ESP-WROOM-32 module and USB connector
ESP32 module driving the carrier and demodulating each zone to distinguish clear, break and tamper. Photograph sourced from Wikimedia Commons — ESP32 Espressif ESP-WROOM-32 Dev Board.jpg. Reused under the licence stated on that page; please check it before republishing.

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.

Perimeter Laser Trip Sensor — architecture stackLayered architecture from hardware to user interface. Hardware layerESP32 DevKit V1 (ESP-WROOM-32) · sensors and actuatorsDriver layerwifi · pubsub · ssd1306 · arduinojsonApplication logicsampling loop · filtering · thresholds · state machineTransport layerWi-Fi + MQTT → alarm panel / control room · TLS · retry and backoffPresentation layerdashboard · mobile notifications · historical charts
Perimeter Laser Trip Sensor — architecture stack

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)

plainModulated 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

plainThree-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)

plainSupervision (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.

Perimeter Laser Trip Sensor — firmware flowchartControl flow through the main program loop. Emit modulated beamsRead each receiver(demodulated)Beam made at correctfrequency?Zone clearBeam broken → alarm (zone)Zone clearBeam broken → alarm (zone)Unmodulated flood orsupervision fail?Tamper/fault alarmContinueTamper/fault alarmContinue
Perimeter Laser Trip Sensor — firmware flowchart

Assembly Instructions

Build on a breadboard first and only commit to solder once the whole system has run for an hour without a fault.

  1. 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.

  2. 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.

  3. 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.

  1. 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.ino
    enum 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.
  2. 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.

cppperimeter-laser-trip.ino
/* ═══════════════════════════════════════════════════════════════
   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
}
int demodLevel(int pin, bool &modulationPresent)Samples the receiver in phase with the carrier — measuring on-phase minus off-phase — so it extracts only the light that is actually pulsing at the beam's frequency, rejecting steady ambient light.
if (average > A_SAT && !modulationPresent)A high total level with no modulated component is a flood-spoof attempt, flagged so a bright light held on the receiver cannot fake a made beam.
Zone evalZone(int level, bool mod)Classifies each zone into the three meaningful states — clear, break, tamper — so a defeat attempt or a fault is an alarm, never a silent clear.
void raiseAlarm(int z, const char *why)Latches and sounds a located alarm and publishes which zone and why, so response knows exactly where the perimeter was crossed or tampered.
if (latched[z]) anyAlarm = true;Alarms latch until acknowledged, so a momentary beam break by someone stepping through is not missed when you look away.

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.

  1. 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.

  2. 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.

  3. Supervision

    Disconnect/cover an emitter and confirm the zone reads fault/tamper, not clear — proving the perimeter notices its own sabotage.

Network Architecture & Connectivity

Perimeter Laser Trip Sensor — network topologyPath taken by telemetry from field node to end user. Edge nodesGatewayCloudClientsBeam controllerESP32Zonesper-beam receiversWi-Fi 2.4 GHzRouterlocal-first alarmMQTT 1883Panel / control roomzone state + eventsPanelzone mapPhone/SMSalarms
Perimeter Laser Trip Sensor — network topology

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 / endpointDirectionPayload
perimeter/alarmnode → panelzone, event (intrusion/tamper), time
perimeter/zonesnode → panelper-zone clear/break/tamper state
perimeter/ackpanel → nodeacknowledge/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.

TestWhat you should see
Break a beam by walking throughThat zone alarms instantly and is located; alarm latches
Shine a torch at a receiverNo "clear" spoof; steady light rejected, flood flagged as tamper
Cover/disable an emitterZone reads tamper/fault — not a silent clear
Cut a zone wire (bench)Supervision raises a fault
Operate in bright daylightBeams stay reliable thanks to modulation
Acknowledge an alarmLatched 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.

jsonperimeter-alarm.json
{
  "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.

Automated machinery on a factory production line
Multi-zone located alarms feed a control panel so response knows exactly where the perimeter was crossed. Photograph sourced from Wikimedia Commons — Factory automation.jpg. Reused under the licence stated on that page; please check it before republishing.

Troubleshooting: Common Errors & Fixes

False alarms in sunlight

Likely cause. Beam not truly modulated / receiver not frequency-selective

Fix. Ensure emitter modulation and synchronous/band-pass detection; hood the receiver

Beam can be spoofed with a torch

Likely cause. Detecting brightness, not modulation

Fix. Detect only the modulated component; flag unmodulated floods as tamper

Intermittent breaks (flicker)

Likely cause. Misalignment, vibration, or thresholds too tight

Fix. Re-align and steady the mounts; widen make/break hysteresis

Disabled zone reads clear

Likely cause. No supervision

Fix. Add end-of-line/expected-signal supervision so faults alarm

Range too short / weak signal

Likely cause. Insufficient emitter power or dirty optics

Fix. Increase emitter power within eye-safety limits; clean optics; use retroreflectors

The sketch will not upload — "Failed to connect" or "avrdude: stk500_recv()"

Likely cause. The bootloader is not being reached: wrong port, wrong board, a serial monitor holding the port open, or a USB cable that only carries power.

Fix. Close every serial monitor, confirm Tools → Board and Port, and swap to a known data-capable USB cable. On an ESP32 hold BOOT while the IDE prints "Connecting…", then release. If a peripheral is wired to the UART pins (GPIO 1/3 on ESP32, D0/D1 on Uno) unplug it — it fights the programmer.

The board resets in a loop, or the serial monitor prints "Brownout detector was triggered"

Likely cause. The supply cannot deliver peak current. Wi-Fi transmit bursts, relay coils and servos all pull far more than their average draw.

Fix. Power peripherals from a separate regulated supply with a common ground rather than from the board 5 V pin. Add a 470–1000 µF electrolytic capacitor across the supply near the load, and use a real power adapter rather than a laptop USB port.

Serial monitor shows garbage characters

Likely cause. Baud rate mismatch between Serial.begin() and the monitor, or a floating/shared UART line.

Fix. Set the monitor to 115200 to match the sketch. If it still garbles, the crystal or the USB bridge is being confused by noise — shorten the cable and keep motor wiring away from the USB lead.

An I²C device is not detected

Likely cause. Wrong address, missing pull-ups, swapped SDA/SCL, or a bus too long for the pull-up value.

Fix. Run an I²C scanner sketch first — it should print the device address. Most breakout boards include 4.7 kΩ pull-ups, but if you have chained four of them the parallel resistance is too low; remove the pull-ups from all but one board. Keep the bus under 30 cm at 100 kHz.

Wi-Fi connects but MQTT never does (state -2)

Likely cause. Wrong broker address or port, a firewall in the way, or the broker requiring credentials the sketch is not sending.

Fix. Test from a laptop on the same network first: mosquitto_sub -h <broker> -t "#" -v. If that works, the problem is on the device — check the IP literal, port 1883 (or 8883 for TLS), and that client.setServer() runs before connect(). PubSubClient state codes are documented in its header.

Readings arrive for a while and then stop

Likely cause. The Wi-Fi or MQTT session dropped and the sketch never reconnects, or the broker dropped the client on keep-alive timeout.

Fix. Never assume the link stays up. Check WiFi.status() and client.connected() at the top of every loop and reconnect with exponential backoff. Add a watchdog so a wedged network stack reboots the device instead of going silent.

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 a millis() 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_t where 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

Why modulate the beam instead of just shining it?

A steady beam is blinded by daylight and spoofed by a torch. Modulating it and detecting only that frequency makes it work in bright sun and much harder to fool — the receiver ignores any light that is not pulsing at the right frequency.

Can an intruder just cover the sensor and disable it?

That is exactly what the tamper/supervision logic catches. A covered emitter, a spoof flood, or a cut wire reads as a tamper/fault alarm, not a silent clear — a disabled zone is an alarm, not a blind spot.

Does it know who or what crossed the beam?

No — it detects a crossing, not an identity. That is why beams are layered at different heights and combined with other sensors (PIR, cameras) so an intruder cannot simply step over or crawl under one beam unnoticed.

Why multiple zones?

So the alarm is located. Knowing "the east gate beam was broken" rather than just "something happened" lets you respond to the right place — essential on any real perimeter.

Is this good enough for machine safety?

No. For protecting people from machinery you must use properly safety-rated light curtains and guards. This is a security intrusion sensor, not a certified safety device.

References & Learning Resources

These are the primary sources worth reading in full. Manufacturer datasheets always outrank forum posts when the two disagree.

  1. Photoelectric / beam-break sensors — principlesReference
  2. Modulated IR detection and ambient rejection (IR remotes)Reference
  3. Perimeter intrusion detection systems — overviewReference
  4. Retroreflectors (corner cubes)Reference
  5. Laser safety and eye-safe powerReference