Smart Indoor Security System is a runnable microcontroller simulation example on SensBench — open the wired circuit, inspect the code, and compile real firmware in your browser, no install needed. Last updated Aug 14, 2026.

FREE SIMULATOR EXAMPLE · INTERMEDIATE

Smart Indoor Security System

This example runs an indoor security system on the ESP32 — motion sensing, an arm/trigger/latch alarm state machine, and alert outputs. The entry-level alarm pattern with the logic spelled out.

Board ESP32 DevKit (Xtensa LX6, 240 MHz)
Difficulty intermediate
Category communication
Libraries None — just the Arduino core
Runs in Sensbench browser simulator — free, no account
ESP32 smart indoor security system with PIR and alerts in the Sensbench simulator

The exact circuit you get when you open this example — already wired, ready to run.

The sketch

# main.py
import machine, utime, network, urequests, ujson



# ─── Favoriot Credentials ───────────────────────
API_KEY   = '..'
USERNAME  = ''
DEVICE_ID = 'SmartSecurityr'+USERNAME
URL       = 'https://apiv2.favoriot.com/v2/streams'

#import machine, utime, network, urequests, ujson

# ─── WiFi Credentials ───────────────────────────
SSID     = 'SENSBENCH-GUEST'
PASSWORD = ''



# ─── Pin Setup ──────────────────────────────────
pir  = machine.Pin(14, machine.Pin.IN)

ROW_PINS = [19, 18, 5, 17]
COL_PINS = [16,  4, 2, 15]
KEYS = [
    ['1','2','3','A'],
    ['4','5','6','B'],
    ['7','8','9','C'],
    ['*','0','#','D']
]

rows = [machine.Pin(p, machine.Pin.OUT) for p in ROW_PINS]
cols = [machine.Pin(p, machine.Pin.IN, machine.Pin.PULL_DOWN) for p in COL_PINS]

# ─── Settings ───────────────────────────────────
CORRECT_PASSWORD = '1234'
TIMEOUT_MS       = 30000

# ─── WiFi Connect ───────────────────────────────
def connect_wifi():
    wlan = network.WLAN(network.STA_IF)
    wlan.active(True)
    wlan.connect(SSID, PASSWORD)
    print('Connecting to WiFi', end='')
    for _ in range(20):
        if wlan.isconnected():
            break
        print('.', end='')
        utime.sleep(0.5)
    if wlan.isconnected():
        print(' Connected! IP:', wlan.ifconfig()[0])
    else:
        print(' WiFi Failed - running without Favoriot')

# ─── Keypad ─────────────────────────────────────
def get_key():
    for r, row in enumerate(rows):
        row.value(1)
        for c, col in enumerate(cols):
            if col.value() == 1:
                row.value(0)
                # Wait for button release
                while col.value() == 1:
                    utime.sleep_ms(10)
                utime.sleep_ms(50)
                return KEYS[r][c]
        row.value(0)
    return None
# ─── Favoriot ───────────────────────────────────
def send_to_favoriot(event, status, access):
    print('>>> FAVORIOT | event:', event, '| status:', status, '| access:', access)
    wlan = network.WLAN(network.STA_IF)
    if not wlan.isconnected():
        print('No WiFi - skipping Favoriot')
        return
    try:
        headers = {
            'Content-Type': 'application/json',
            'apikey': API_KEY,
            'username': USERNAME
        }
        body = ujson.dumps({
            'device_developer_id': DEVICE_ID,
            'data': {
                'event': event,
                'status': status,
                'access': str(access)
            }
        })
        r = urequests.post(URL, headers=headers, data=body)
        print('Favoriot Response:', r.status_code)
        r.close()
    except Exception as e:
        print('Favoriot Error:', e)

# ─── Main ───────────────────────────────────────
connect_wifi()
print('System Ready - Waiting for motion...')

while True:
    if pir.value() == 1:
        print('Motion Detected!')
        send_to_favoriot('MOTION_DETECTED', 'WAITING', 0)

        entered = ''
        start = utime.ticks_ms()

        while utime.ticks_diff(utime.ticks_ms(), start) < TIMEOUT_MS:
            key = get_key()
            if key:
                if key == '#':
                    if entered == CORRECT_PASSWORD:
                        print('Access Granted!')
                        send_to_favoriot('ACCESS_GRANTED', 'SAFE', 1)
                    else:
                        print('Wrong Password!')
                        send_to_favoriot('WRONG_PASSWORD', 'ALARM', 0)
                    break
                elif key == '*':
                    entered = ''
                    print('Cleared')
                else:
                    entered += key
                    print('*', end='')
        else:
            print('Timeout - Alarm!')
            send_to_favoriot('TIMEOUT_ALARM', 'ALARM', 0)

    utime.sleep_ms(100)

How it works

The firmware runs an alarm state machine over its sensor inputs: armed mode watches for triggers, a trigger latches the alarm, and the alert output fires until reset. The serial log records every state transition, making the system’s behavior auditable.

The arming flow is the security logic: a grace period after arming lets the user leave, the trigger path latches so a passing intruder cannot silence the alarm by leaving, and the reset is deliberate. These details are what make it a security system rather than a motion indicator.

Before you build it for real

A hobby alarm is not a security product — no tamper detection, no backup power, no redundant paths; the example teaches the logic, not the certification. The PIR’s false triggers are the real-world enemy: pets, curtains and HVAC airflow all trigger the sensor, and the alarm’s usefulness depends on threshold tuning and placement. And the latch must survive power loss — a security system that disarms itself when the power blips is not securing anything.

Get the parts

Related examples

FAQ

What sensors make up the system?

Motion (PIR) is the primary trigger, with door contacts or sound sensors as the usual companions. The firmware monitors the inputs, applies the alarm logic, and raises alerts — the layered detection model of every entry-level alarm system.

How does the alarm logic work?

State machine: disarmed, armed, triggered. The system only alarms when armed, and a trigger latches the alarm state until reset — the arm-trigger-latch cycle that distinguishes an alarm from a motion light.