Contents — 26 sections
Project Overview
Magnetic contacts on doors and windows that instantly sound a networked, zoned alarm when one is opened — with supervision so a defeated or dead sensor is never a silent blind spot.
The most reliable intrusion detection is also the simplest: a magnetic contact that knows whether a door or window is open or shut. A magnet on the moving part and a reed switch on the frame keep a circuit made while the two are together; open the door and the circuit breaks. It is fast, unambiguous, cheap and — done right — very hard to fool, which is why magnetic contacts are the backbone of virtually every real alarm system. This project builds a proper multi-zone version: contacts on every protected opening, an ESP32 that watches them all, and a networked alarm that sounds instantly and tells you which door or window was breached.
The step from a toy to a real alarm is in the details that a bare reed switch skips. Zones: each contact is its own zone so the alarm is located — "kitchen window", not just "alarm" — which matters for response and for finding the breach. Arm/disarm: the system has states (armed-away, armed-home, disarmed) with an entry/exit delay so you can leave and enter through a designated door without tripping it, and it distinguishes perimeter openings from interior movement. And critically, supervision: the wiring is monitored (classically with an end-of-line resistor) so that cutting or shorting a sensor loop — the obvious way to defeat a contact — is detected as a fault and alarmed, rather than silently disabling that zone.
Because it is networked, a breach does more than sound a local siren: it pushes an instant located alert to your phone or a control panel, logs the event, and can trigger other actions (lights, cameras). It supports battery backup so a power cut does not disarm it, and it supervises the sensors themselves so a flat wireless contact or a broken wire raises a maintenance warning before an intruder finds it. It is honest that magnetic contacts detect opening, not glass being smashed or a wall being breached (which is why they are layered with glass-break and motion sensors in a complete system), and that a DIY alarm is not a professionally-monitored installation. But as a fast, located, supervised, networked perimeter alarm, it is exactly the dependable first line that catches the overwhelmingly common intrusion: someone opening a door or window that should be shut.
What this project does
- Detects a door/window opening instantly with magnetic contacts
- Treats each contact as a located zone (which opening was breached)
- Supports armed-away/armed-home/disarmed with entry/exit delays
- Supervises the sensor loops so a cut/shorted wire is a fault, not a silent gap
- Sounds a local siren and pushes an instant located alert / logs it
- Runs on mains with battery backup so a power cut does not disarm it
- Distinguishes perimeter openings from interior zones
Real-World Applications
| Setting | How it is used |
|---|---|
| Home intrusion alarm | Contacts on all external doors and windows, zoned and armable, with phone alerts and a siren. |
| Small shop / office | Perimeter protection with located alarms and an audit of open/close events. |
| Storeroom / cabinet monitoring | Alerting whenever a specific door or cabinet is opened, armed or always. |
| Vacation / second-home watch | Remote monitoring of a property's openings with battery backup and instant alerts. |
Deployment contexts where a build of this kind earns its keep.
Features & Capabilities
- Reliable, hard-to-fool magnetic-contact detection
- Zoned, located alarms for fast response
- Arm/disarm states with entry/exit delays
- End-of-line supervision against tamper (cut/short)
- Networked instant alerts + local siren
- Battery backup against power-cut attacks
- Honest layering with glass-break/motion for full coverage
Difficulty, Time & Required Skills
| Attribute | Value |
|---|---|
| Difficulty level | Beginner |
| Estimated completion time | 8–14 hours |
| Indicative build cost | ₹2,200 – ₹3,800 |
| Primary discipline | Security |
| Reference platform | ESP32 DevKit V1 (ESP-WROOM-32) |
Skills you should have (or will pick up)
- Wiring magnetic (reed) contacts and multi-zone loops
- End-of-line supervision to detect cut/short tamper
- Arm/disarm state machines with entry/exit delays
- Networked alerting and event logging
- Battery-backup and power-loss handling
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 |
| 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 | 6 | ₹330 |
| 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 |
| 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 |
| 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 |
| 4×4 matrix membrane keypad Debounce in software — 20 ms is enough for membrane keys. | 16 keys, 8-wire matrix, 35 × 76 mm adhesive-backed | 1 | ₹70 |
| 18650 Li-ion cell 3400 mAh + holder Never charge below 0 °C; always use a protected cell or a BMS. | 3.7 V nominal, 4.2 V full, 3400 mAh, ~12.6 Wh, 2 C discharge | 1 | ₹450 |
| Magnetic door/window contacts Surface or recessed; recessed is harder to defeat | Reed + magnet contact sets, one per opening (zone) | 8 | ₹3,840 |
| End-of-line resistors | For supervised loops (detect cut/short) | 8 | ₹480 |
| Siren + strobe | Loud internal/external sounder | 1 | ₹500 |
| Backup battery + charger | So a mains cut does not disarm the panel | 1 | ₹300 |
Estimated total: ₹6,785, 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 |
| 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 |
| Active piezo buzzer 5 V | 85 dB at 10 cm, 2.3 kHz resonance, 12 mm diameter | 3–5 V | Digital / PWM | 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 |
| 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 |
| 4×4 matrix membrane keypad | 16 keys, 8-wire matrix, 35 × 76 mm adhesive-backed | logic level | 8 GPIO matrix scan | Datasheet |
| 18650 Li-ion cell 3400 mAh + holder | 3.7 V nominal, 4.2 V full, 3400 mAh, ~12.6 Wh, 2 C discharge | 3.0–4.2 V | Holder / spot-welded tabs | 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. |
| Active piezo buzzer 5 V | 3–5 V | 30 | Active buzzers make tone on DC; passive ones need a PWM carrier. |
| 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. |
| 0.96″ SSD1306 OLED display | 3.3–5 V | 20 | Static images burn in — invert or scroll the screen periodically. |
| 4×4 matrix membrane keypad | logic level | 1 | Debounce in software — 20 ms is enough for membrane keys. |
Summed typical draw is 281 mA. With a 1.5× design margin the supply should deliver at least 500 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" |
| NTPClient / configTime bundled | Wall-clock time from an NTP server for timestamping. | Bundled (`configTime()` on ESP32) |
| 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 |
|---|---|---|---|
| Zone loops (supervised) | EOL | GPIO 34/35/32/33 | Per-zone contact + supervision (ADC) |
| Keypad | matrix | GPIO | Arm/disarm code |
| Tamper | NC | GPIO 14 | Panel tamper |
| Siren relay | IN | GPIO 26 | Sounder |
| OLED | SDA/SCL | GPIO 21/22 | Status/zone display |
| Wi-Fi | on-chip | — | Alerts + logging |
| Backup battery | sense | ADC | Power supervision |
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
- Wire each zone as a supervised loop with an end-of-line resistor read on an ADC, so a normal closed contact, an open contact, a cut wire and a short each give a distinct level.
- Use recessed contacts where possible (harder to defeat than surface-mounted) and one contact per opening for located zones.
- Power the panel from mains with automatic battery backup and sense the supply so power loss is detected and alerted.
- Add a panel tamper switch, and drive the siren via a relay sized for it.
- Keep interior zones separate from perimeter zones so armed-home can watch the perimeter while allowing interior movement.
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 magnetic contact is the archetypal intrusion sensor because it answers a binary, physical question with almost no ambiguity: is this opening open or shut? A magnet holds a reed switch closed while the door is shut; opening the door separates them and the switch opens. There is little to false-trigger on — no light, temperature or vibration to confuse it — which is why contacts are trusted as the primary perimeter sensor. The engineering that makes a system out of them is about three things the bare switch does not provide: knowing where, controlling when it is active, and detecting attempts to defeat it.
Zoning gives the "where". Each contact is wired and tracked as its own zone, so an alarm is not just "something happened" but "the kitchen window opened", which is what lets a responder go straight to the breach and lets the owner tell a real event from a mistake. Zones are also typed — perimeter (doors/windows) versus interior — so the system can behave differently in different armed states.
Arm/disarm with entry/exit delays gives the "when". A real alarm is not always on; it has states. Disarmed, openings are merely logged. Armed-away, every perimeter and interior zone is active. Armed-home, the perimeter is watched but interior zones are ignored so occupants can move around. And because you must be able to leave and enter through a door without tripping the alarm, a designated entry/exit zone gets a delay: an exit delay after arming to get out, and an entry delay on opening that door to reach the keypad and disarm — while any other zone opening alarms instantly, since an intruder coming through a window has no legitimate delay. This state machine is what makes an alarm livable rather than a constant nuisance.
The "defeat" problem is where amateur systems fail and supervision earns its place. The obvious way to beat a contact is to stop the loop from ever seeing the door open — bypass the switch with a wire (a short), or cut the wire so the panel simply loses the sensor. A naive two-state input (open/closed) cannot tell a healthy closed contact from a shorted-out one, or a cut wire from a benign disconnect, so it can be silently disabled. The classic fix is an end-of-line resistor: a resistor at the far end of the loop so that a healthy closed contact reads one specific resistance, an open contact another, a short reads zero and a cut reads infinite — four distinguishable states from one wire. Now cutting or shorting a zone is a detected tamper/fault, alarmed like any breach, so a defeated sensor is never a silent blind spot. Coupled with battery backup (so cutting mains does not disarm the panel, and power loss is itself alerted) and supervision of wireless contacts' batteries, this is what makes the perimeter trustworthy. The system is candid that contacts detect opening and not glass being smashed or a wall breached — which is why complete installations layer glass-break and motion sensors on top — and that a homebrew panel is not a professionally-monitored alarm. But as a fast, located, supervised, networked perimeter alarm, it reliably catches the most common intrusion of all: a door or window opened that should have stayed shut.
The maths behind it
End-of-line supervised zone
One EOL resistor per loop gives four distinguishable states
from the measured resistance/voltage:
R ≈ R_eol → closed & healthy (normal)
R ≈ R_eol+R_x → contact OPEN (breach) [with series/parallel scheme]
R ≈ 0 → SHORT (tamper/bypass)
R ≈ ∞ → OPEN CIRCUIT (cut wire/tamper)
Cut and short are alarmed — a defeated zone is never silent.
Arm-state + entry/exit logic
On a monitored zone opening while ARMED:
if zone == ENTRY_DELAY_ZONE: start entry timer T_e
alarm if not disarmed within T_e
else: alarm immediately (located)
ARMED_HOME ignores INTERIOR zones; ARMED_AWAY includes them.
Exit timer T_x after arming lets you leave without a trip.
Power-loss supervision
Battery backup keeps the panel armed; watch the mains:
if mains_lost: log + alert "power loss" (stay armed)
if battery_low: maintenance alert before it fails
Cutting power must not disarm — and should itself warn.
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.
Fit supervised contacts on every opening
Mount a magnetic contact on each protected door and window (recessed where possible), wiring each as a supervised loop with an end-of-line resistor so cut/short is detectable.
Assign each contact a zone name and type (perimeter/interior, and which is the entry/exit door).
Wire the panel with backup power
Connect the zone loops, keypad, siren relay and OLED to the ESP32 panel, powered from mains with automatic battery backup and mains-sensing so power loss is detected.
Set up alerts and states
Configure Wi-Fi alerts/logging, and the armed-away/armed-home/disarmed states with the entry/exit door and delays.
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.
Read supervised zones into four states
Measure each loop and classify it as normal, open (breach), short (tamper) or cut (tamper), so both breaches and defeat attempts are detected.
cppsupervised-zone.inoenum ZoneState { NORMAL, OPEN, SHORT, CUT }; // One EOL-resistor loop read on an ADC → four distinguishable states. ZoneState readZone(int pin) { int v = analogRead(pin); // 0..4095 if (v < 100) return SHORT; // near 0 -> shorted (tamper) if (v > 3900) return CUT; // near full -> open circuit (cut) if (v > 1600 && v < 2400) return NORMAL; // EOL band -> closed & healthy return OPEN; // out of the EOL band -> contact open } // Classify all zones; return true if any needs to alarm given the state. bool scanZones(ZoneState st[], int n, ArmState arm) { bool alarm = false; for (int z = 0; z < n; z++) { st[z] = readZone(zonePin[z]); if (st[z] == SHORT || st[z] == CUT) { raise(z, "tamper"); alarm = true; } else if (st[z] == OPEN && zoneActive(z, arm)) { raise(z, "breach"); alarm = true; // located, arm-aware } logState(z, st[z]); } return alarm; }if (v < 100) return SHORT;A near-zero reading means the loop has been shorted — a bypass attempt — which is treated as tamper, not a benign closed contact.if (v > 3900) return CUT;A near-full reading means the wire is cut/open — the sensor has been removed — again tamper, not silently ignored.if (v > 1600 && v < 2400) return NORMAL;Only a reading in the end-of-line resistor's band counts as a healthy closed contact; anything else is a meaningful, distinguishable state.if (st[z] == SHORT || st[z] == CUT) { raise(z, "tamper")Cut and short are alarmed like a breach, so a defeated zone can never become a silent blind spot.else if (st[z] == OPEN && zoneActive(z, arm))A contact opening alarms only if that zone is active in the current armed state, giving located, arm-aware breach detection.Run the arm-state machine and alert
Apply the armed-away/armed-home rules and entry/exit delays, sound the siren and push a located alert on an alarm, and log every open/close and state change.
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.
/* ═══════════════════════════════════════════════════════════════
Door/Window Breach Alarm — ESP32, supervised multi-zone
Magnetic contacts on doors/windows, each a supervised (EOL) zone,
with armed-away/home/disarmed states and entry/exit delays. Cut/short
tamper is alarmed. Located alerts + local siren; battery backup.
══════════════════════════════════════════════════════════════════ */
#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <Preferences.h>
#define PIN_SIREN 26
#define PIN_MAINS 15
#define NZONES 6
#define ENTRY_ZONE 0 // the entry/exit door
#define ENTRY_MS 30000UL
#define EXIT_MS 30000UL
const int zonePin[NZONES] = {34,35,32,33,25,27};
const bool interior[NZONES] = {false,false,false,false,true,true};
const char *zoneName[NZONES] =
{"Front door","Kitchen win","Bedroom win","Back door","Hall PIR","Landing PIR"};
enum ArmState { DISARMED, ARMED_HOME, ARMED_AWAY };
enum ZoneState { NORMAL, OPEN, SHORT, CUT };
ArmState arm = DISARMED;
Adafruit_SSD1306 oled(128,64,&Wire);
Preferences prefs;
WiFiClient net; PubSubClient mqtt(net);
uint32_t entryStart = 0; bool entryRunning = false, alarming = false;
ZoneState readZone(int pin) {
int v = analogRead(pin);
if (v < 100) return SHORT;
if (v > 3900) return CUT;
if (v > 1600 && v < 2400) return NORMAL;
return OPEN;
}
bool zoneActive(int z) {
if (arm == DISARMED) return false;
if (arm == ARMED_HOME && interior[z]) return false; // allow interior at home
return true;
}
void alertLocated(int z, const char *why) {
char m[120];
snprintf(m,sizeof m,"{\"zone\":%d,\"name\":\"%s\",\"event\":\"%s\"}",
z+1, zoneName[z], why);
mqtt.publish("alarm/event", m);
}
void triggerAlarm(int z, const char *why) {
alarming = true;
digitalWrite(PIN_SIREN, HIGH);
alertLocated(z, why);
}
void setup() {
Serial.begin(115200);
pinMode(PIN_SIREN, OUTPUT);
pinMode(PIN_MAINS, INPUT);
for (int z=0; z<NZONES; z++) analogSetPinAttenuation(zonePin[z], ADC_11db);
Wire.begin(21,22); oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);
WiFi.begin(WIFI_SSID, WIFI_PASS);
mqtt.setServer(MQTT_HOST, 1883);
prefs.begin("alarm", true);
arm = (ArmState)prefs.getInt("arm", DISARMED);
prefs.end();
}
void loop() {
if (!mqtt.connected() && WiFi.status()==WL_CONNECTED) mqtt.connect("alarm-1");
mqtt.loop();
handleKeypad(&arm); // arm/disarm; sets exit delay etc.
if (digitalRead(PIN_MAINS) == LOW) // power-loss supervision
mqtt.publish("alarm/status", "power loss (armed on battery)");
oled.clearDisplay(); oled.setCursor(0,0);
for (int z = 0; z < NZONES; z++) {
ZoneState s = readZone(zonePin[z]);
if (s == SHORT || s == CUT) triggerAlarm(z, "tamper");
else if (s == OPEN && zoneActive(z)) {
if (z == ENTRY_ZONE && !entryRunning) { // entry delay on the door
entryRunning = true; entryStart = millis();
mqtt.publish("alarm/status", "entry delay");
} else if (z != ENTRY_ZONE) {
triggerAlarm(z, "breach"); // other zones: instant
}
}
oled.printf("%s\n", zoneName[z]);
}
oled.display();
if (entryRunning && millis() - entryStart > ENTRY_MS && arm != DISARMED)
triggerAlarm(ENTRY_ZONE, "breach (entry timeout)");
if (arm == DISARMED) { entryRunning = false; alarming = false;
digitalWrite(PIN_SIREN, LOW); }
delay(50);
}
Configuration & Calibration
Configuration steps
- Define each zone's name, type (perimeter/interior) and the entry/exit door and delays.
- Set the end-of-line thresholds for your resistor values so normal/open/short/cut are distinct.
- Configure the armed states, keypad codes, siren and Wi-Fi alerts/logging.
- Ensure mains-sensing and battery backup with a low-battery maintenance alert.
Calibration procedure
An uncalibrated sensor produces confident, precise, wrong numbers. Do this once per physical unit and record the constants.
EOL thresholds
Measure each zone's ADC value for closed, open, shorted and cut conditions and set the bands so all four are cleanly distinguished.
Delays
Set exit/entry delays long enough to leave/reach the keypad but no longer, and confirm non-entry zones alarm instantly.
Power backup
Verify the panel stays armed and reports power loss on a simulated mains cut, and set the low-battery threshold.
Network Architecture & Connectivity
Communication protocol
Zone states, arm changes and alarms publish immediately; the local siren sounds independent of the network so connectivity loss cannot silence a breach.
| Topic / endpoint | Direction | Payload |
|---|---|---|
alarm/event | panel → app | located breach / tamper (zone, name, event) |
alarm/status | panel → app | armed state, entry delay, power loss |
alarm/cmd | app → panel | arm/disarm, acknowledge |
Message contract between the device and the broker.
Cloud platform configuration
An app/panel shows zone states and armed mode, logs open/close and alarm history, and pushes instant located alerts; multiple panels cover larger premises.
Dashboard setup
A floor plan of zones coloured by state, armed-mode control, and an event log of breaches, tampers and arm/disarm actions.
Mobile app integration
Instant located breach/tamper alerts and power-loss/low-battery maintenance alerts, with remote arm/disarm.
Security considerations
- Authenticate arm/disarm commands; keep the local siren and supervision independent of the network.
- Supervise loops (EOL) and power so tamper and power-cut are alarmed, not silent.
- Alert on panel tamper and connectivity loss.
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 |
|---|---|
| Open a perimeter window while armed | Instant located alarm for that zone |
| Open the entry door while armed | Entry delay starts; alarm only if not disarmed in time |
| Short or cut a zone loop | Tamper alarm for that zone (not silently disabled) |
| Armed-home with interior movement | Interior zones ignored; perimeter still armed |
| Cut mains power | Stays armed on battery; power-loss alert |
| Disarm at the keypad | Siren clears; system returns to disarmed/logging |
Bench-test checklist. If a row fails, stop and fix it before moving on.
Expected output
The app/panel shows each zone's state and the armed mode; a breach or tamper produces an instant located alert and log entry.
{
"zone": 2,
"name": "Kitchen win",
"event": "breach",
"time": "2026-07-27T02:03:18"
}
A located breach on the kitchen window fires the siren and pushes an alert naming the zone; a cut or shorted loop would instead raise a tamper alarm for that zone, so a defeat attempt is never silent.
Troubleshooting: Common Errors & Fixes
Performance Optimisation
- Poll zones fast enough to catch an opening instantly while keeping ADC work light.
- Debounce contacts to avoid chatter without adding perceptible delay.
- Keep the local siren and supervision independent of the network; alert immediately.
- Log open/close and state changes for a useful audit without flooding.
- 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
- Contacts detect opening, not glass-break or wall breach — layer with glass-break/motion for full coverage.
- Supervise loops so a defeated sensor is an alarm, and provide battery backup against power-cut attacks.
- A DIY alarm is not a professionally-monitored system; use one where required, and never rely on it alone for life-safety.
- Ensure sirens and alerts comply with local noise and alarm regulations.
- 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.
- Lithium cells vent and burn when abused. Only use protected cells or a proper BMS, never charge below 0 °C, and never leave a charging pack unattended on a wooden desk.
- 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
- Test each zone (open, and cut/short) periodically to confirm detection and supervision.
- Check magnet alignment and replace weak wireless-contact batteries (supervised).
- Verify battery backup run-time and the low-battery/power-loss alerts.
- Keep zone names/floor plan accurate as the premises change.
- Re-check every screw terminal and header after the first week — thermal cycling loosens connections that felt tight on day one.
- Log pack voltage. When resting voltage after a full charge drops below about 4.0 V, the cell is near end of life — replace it.
- 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 glass-break and motion (PIR) sensors as additional zone types for full coverage.
- Add wireless supervised contacts to simplify retrofits.
- Integrate cameras so a breach pulls up the relevant view.
- Add smart-home actions (lights on, doors lock) on alarm.
- 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.