Community project

Connected Multi-Lock Security System

ESP32
Photo of Connected Multi-Lock Security System
Generated with AI

Anubhav Gupta

Last updated October 17, 2026

This connected multi-lock security system uses an ESP32 to control two 12 V solenoid bolts via logic-level MOSFETs, adding smart access control to gates or doors. The system authenticates users through RFID cards and fingerprint scanning, with optional visitor detection via ultrasonic sensor and manual override capabilities. A reed switch monitors bolt position while status LEDs provide visual feedback.

The guide provides a complete wiring diagram, detailed parts list, and step-by-step assembly instructions covering power supply configuration, solenoid and diode installation, sensor integration, and mechanical testing. Firmware is included with support for multiple security modes (Home, Away, Night, Alarm, Smoke) and configurable authentication windows, allowing builders to deploy a flexible access control system tailored to residential or commercial gate security.

Wiring diagram

Wiring diagram for Connected Multi-Lock Security System

Gather all the parts

QtyComponent
1

LM2596 12 V to 5V-A step-down module

Adjustable converter that supplies 5 V to the C6 controller and ESP32-CAM.

1

LM2596 12 V to 5V-B step-down module

Adjustable converter that supplies 5 V to the ultrasonic sensor.

1

D4184/AOD4184A logic-level MOSFET module, top bolt

Low-side driver that switches the top 12 V bolt solenoid from a 3.3 V-compatible control input.

1

D4184/AOD4184A logic-level MOSFET module, bottom bolt

Low-side driver that switches the bottom 12 V bolt solenoid from a 3.3 V-compatible control input.

1

12 V push-pull return-spring solenoid, top bolt

Pulls the top spring bolt into the gate so the gate can open.

1

12 V push-pull return-spring solenoid, bottom bolt

Pulls the bottom spring bolt into the gate so the gate can open.

1

1N5819 flyback diode, top solenoid

Safely absorbs the top solenoid's voltage spike when it is switched off.

1

1N5819 flyback diode, bottom solenoid

Safely absorbs the bottom solenoid's voltage spike when it is switched off.

1

MFRC522 RFID Module

13.56 MHz RFID reader/writer module based on the NXP MFRC522 IC. Communicates over SPI and is commonly sold as an RC522 breakout with an onboard antenna.

1

Uart Fingerprint Sensor

AS608 optical fingerprint sensor module with onboard image processing and template matching. Communicates over TTL UART; common Arduino/ESP32 modules support enrollment, 1:1 verification, and 1:N search with the Adafruit Fingerprint-style serial protocol.

1

HC-SR04

Ultrasonic distance measurement sensor

1

Reed Switch

Glass-encapsulated magnetic reed switch -- contacts close in the presence of a magnetic field. Standard door/window sensor for alarms. Wire as a digital input with internal pull-up; debounce in firmware.

1

Series bolt-out microswitch pair

Two bolt position switches wired in series so the signal closes only when both bolts are fully out.

1

Inside lever microswitch

Detects when the inside lever manually draws both bolts in.

1

5 mm green status LED

Shows the gate is unlocked or the system is in Home mode.

1

5 mm red status LED

Shows the gate bolts are locked or the system is in Alarm mode.

1

330 ohm green LED series resistor

Limits current through the green status LED.

1

330 ohm red LED series resistor

Limits current through the red status LED.

1

12 V 5 A DC power adapter

Single plug-in supply for the gate demonstration.

1

5 A inline blade fuse holder

Protects the main 12 V branch if a wiring fault occurs.

1

1000 uF 25 V electrolytic capacitor

Reduces voltage dips on the fused 12 V rail when the solenoids operate.

1

1 kohm resistor, HC-SR04 echo divider

Upper resistor that reduces the HC-SR04 five-volt echo signal to a safe C6 input voltage.

1

2 kohm resistor, HC-SR04 echo divider

Lower resistor that completes the HC-SR04 echo voltage divider to ground.

1

AI-Thinker ESP32-CAM with OV2640 wide-angle camera

Separate Wi-Fi camera board that takes a gate photograph when its GPIO13 trigger input rises.

Assemble it in 9 steps

1. Build the fused 12 V supply

Connect the adapter positive lead to the 5 A fuse input, then take the fuse output as the 12 V rail. Put the 1000 uF capacitor across that rail: capacitor + to fused 12 V and capacitor - to the shared ground. (power protection)

  • The electrolytic capacitor has polarity: reversing it can make it overheat or burst.
  • Do not connect USB and either 5 V buck output to the C6 at the same time. Unplug both buck outputs before flashing by USB.

2. Set the two 5 V supplies

Connect both LM2596 inputs to fused 12 V and shared ground. Before connecting any electronics, use a multimeter to adjust each output to exactly 5.0 V. Use 5V-A for the C6 5V pin and ESP32-CAM 5V pin; use 5V-B only for the HC-SR04. Join every ground. (power)

  • Do not power the RC522 or AS608 from 5 V; both use the C6 3V3 rail.

3. Fit the two spring bolts and manual release

Mount one bolt at the top and one at the bottom of the gate. Each bolt must slide freely toward its keeper under its own compression spring. Join both bolt pull links to one inside lever and the cam-lock key bar, so either manual control pulls both bolts in without electrical power. (mechanical emergency release)

  • The inside lever and key must open the gate even when every wire is disconnected; the firmware is not part of the exit path.

4. Wire each solenoid and its diode

For the top bolt, connect fused 12 V to solenoid + and the top driver LOAD- terminal to solenoid -. Put the 1N5819 directly across that solenoid: striped cathode to solenoid + and unstriped anode to solenoid -. Repeat the same arrangement for the bottom bolt and bottom driver. Connect both driver ground terminals to the shared ground. (solenoid power and spike protection)

  • A diode with its stripe toward the solenoid negative creates a short circuit; its stripe must face the fused +12 V side.
  • Use D4184/AOD4184A drivers with a 3.3 V-compatible SIG input. Never substitute an IRF520 board or put 5 V/12 V onto a C6 GPIO.

5. Wire the Gate C6 signals

On the WeAct ESP32-C6-MiNi, wire top driver SIG to GPIO19 (top bolt control) and bottom driver SIG to GPIO23 (bottom bolt control). Wire the gate reed from GPIO0 to GND, the series bolt-out switch pair from GPIO1 to GND, and the inside-lever switch from GPIO3 to GND. The code uses the board's internal pull-up resistors. (signals)

  • The two bolt position switches are wired in series: the GPIO1 circuit closes only when both bolts are fully out.

6. Wire the RFID and fingerprint readers

Wire the RC522 VCC and RST to C6 3V3, GND to shared ground, SCK to GPIO6, MOSI to GPIO7, MISO to GPIO2, and SDA/CS to GPIO18. Wire AS608 VCC to 3V3, GND to ground, AS608 TX to GPIO10 and AS608 RX to GPIO11. (identity signals)

  • The RC522 is a 3.3 V-only module; connecting it to 5 V can damage it.
  • GPIO2, GPIO6 and GPIO7 are intentionally routed SPI lines. Check these exact header labels rather than relying on a DevKitC diagram.

7. Wire the visitor detector and camera trigger

Power the HC-SR04 from 5V-B and ground. Connect TRIG to GPIO20. Make the echo divider in this order: HC-SR04 ECHO to the 1 kohm resistor, resistor far end to GPIO21, then the 2 kohm resistor from that same GPIO21 junction to GND. Connect C6 GPIO22 to ESP32-CAM GPIO13, and share ground. Power the camera from 5V-A. (visitor distance and photo trigger)

  • HC-SR04 ECHO is 5 V. The 1 kohm / 2 kohm divider is required before GPIO21; bypassing it can damage the C6.

8. Fit the status LEDs

Connect GPIO4 through the green 330 ohm resistor to the green LED long leg, then connect its short leg to GND. Connect GPIO5 through the red 330 ohm resistor to the red LED long leg, then its short leg to GND. (locked and unlocked status)

  • Do not omit the 330 ohm resistors; without them the LEDs or C6 pins can be damaged.

9. Test the mechanics before locking the gate

With the gate open and the keeper plates clear, test that each bolt springs out when solenoid power is removed, that the inside lever retracts both bolts, and that the key works with all power removed. Then align the two keepers so the slanted bolt tips latch as the gate swings shut. (safe mechanical check)

  • Do not first test this system with anyone trapped behind a locked gate. Confirm manual inside release at every stage.

Review all connections

1. Connections between "gate_power_adapter" and "ESP32"

Functiongate_power_adapterESP32
power+12V → 5 A inline blade fuse holder INEXT
groundGNDGND

2. Connections between "gate_fuse" and "ESP32"

Functiongate_fuseESP32
powerOUTVIN

3. Connections between "gate_bulk_cap" and "ESP32"

Functiongate_bulk_capESP32
power+VIN
ground-GND

4. Connections between "gate_5v_a_buck" and "ESP32"

Functiongate_5v_a_buckESP32
powerIN+VIN
groundIN-GND
powerOUT+5V
groundOUT-GND

5. Connections between "gate_5v_b_buck" and "ESP32"

Functiongate_5v_b_buckESP32
powerIN+VIN
groundIN-GND
powerOUT+5V
groundOUT-GND

6. Connections between "gate_top_driver" and "ESP32"

Functiongate_top_driverESP32
pwmSIGGPIO 19
groundGNDGND
groundSUPPLY-GND
powerLOAD- → 12 V push-pull return-spring solenoid, top bolt -EXT

7. Connections between "gate_bottom_driver" and "ESP32"

Functiongate_bottom_driverESP32
pwmSIGGPIO 23
groundGNDGND
groundSUPPLY-GND
powerLOAD- → 12 V push-pull return-spring solenoid, bottom bolt -EXT

8. Connections between "gate_top_solenoid" and "ESP32"

Functiongate_top_solenoidESP32
power+VIN

9. Connections between "gate_bottom_solenoid" and "ESP32"

Functiongate_bottom_solenoidESP32
power+VIN

10. Connections between "gate_top_diode" and "ESP32"

Functiongate_top_diodeESP32
powerCathode → 12 V push-pull return-spring solenoid, top bolt +EXT
powerAnode → 12 V push-pull return-spring solenoid, top bolt -EXT

11. Connections between "gate_bottom_diode" and "ESP32"

Functiongate_bottom_diodeESP32
powerCathode → 12 V push-pull return-spring solenoid, bottom bolt +EXT
powerAnode → 12 V push-pull return-spring solenoid, bottom bolt -EXT

12. Connections between "gate_rfid" and "ESP32"

Functiongate_rfidESP32
powerVCC3V3
groundGNDGND
spiSCKGPIO 6
spiMOSIGPIO 7
spiMISOGPIO 2
spiSDAGPIO 18
powerRST3V3

13. Connections between "gate_fingerprint" and "ESP32"

Functiongate_fingerprintESP32
powerVCC3V3
groundGNDGND
dataTXGPIO 10
dataRXGPIO 11

14. Connections between "gate_ultrasonic" and "ESP32"

Functiongate_ultrasonicESP32
powerVCC5V
groundGNDGND
digitalTRIGGPIO 20
digitalECHO → 1 kohm resistor, HC-SR04 echo divider INEXT

15. Connections between "gate_echo_r1" and "ESP32"

Functiongate_echo_r1ESP32
digitalOUTGPIO 21

16. Connections between "gate_echo_r2" and "ESP32"

Functiongate_echo_r2ESP32
digitalIN → 1 kohm resistor, HC-SR04 echo divider OUTEXT
groundOUTGND

17. Connections between "gate_reed" and "ESP32"

Functiongate_reedESP32
dataPIN1GPIO 0
groundPIN2GND

18. Connections between "gate_bolts_switch" and "ESP32"

Functiongate_bolts_switchESP32
digitalCOMGPIO 1
groundNOGND

19. Connections between "gate_lever_switch" and "ESP32"

Functiongate_lever_switchESP32
digitalCOMGPIO 3
groundNOGND

20. Connections between "gate_green_resistor" and "ESP32"

Functiongate_green_resistorESP32
digitalINGPIO 4
digitalOUT → 5 mm green status LED AnodeEXT

21. Connections between "gate_green_led" and "ESP32"

Functiongate_green_ledESP32
groundCathodeGND

22. Connections between "gate_red_resistor" and "ESP32"

Functiongate_red_resistorESP32
digitalINGPIO 5
digitalOUT → 5 mm red status LED AnodeEXT

23. Connections between "gate_red_led" and "ESP32"

Functiongate_red_ledESP32
groundCathodeGND

24. Connections between "gate_camera" and "ESP32"

Functiongate_cameraESP32
power5V5V
groundGNDGND
digitalGPIO13GPIO 22

Deploy the firmware

#include <Arduino.h>
#include <SPI.h>
#include <MFRC522.h>
#include <Adafruit_Fingerprint.h>
#include "OThread.h"
#include "OThreadCLI.h"

#define NIVASA_DATASET ""
constexpr uint8_t RFID_SCK=6, RFID_MOSI=7, RFID_MISO=2, RFID_CS=18, FINGER_RX=10, FINGER_TX=11, TOP_SOL=19, BOTTOM_SOL=23, ULTRA_TRIG=20, ULTRA_ECHO=21, REED=0, BOLTS_OUT=1, LEVER=3, CAMERA_TRIGGER=22, GREEN_LED=4, RED_LED=5;
constexpr uint32_t PWM_HZ=20000,PULLIN_MS=200,OPEN_TIMEOUT_MS=5000,GATE_OPEN_SETTLE_MS=2000,HOLD_LIMIT_MS=15000,JAM_WAIT_MS=2000,STATE_MS=30000,PRESENCE_MS=1000,PHOTO_COOLDOWN_MS=30000,AUTH_WINDOW_MS=10000,FORCE_GRACE_MS=10000,WRONG_WINDOW_MS=60000,FINGER_SCAN_MS=250,CARD_COOLDOWN_MS=1200;
constexpr uint8_t PWM_FULL=255,PWM_HOLD=77;
const byte AUTHORISED_UID[]={0xDE,0xAD,0xBE,0xEF};
enum Mode{HOME,AWAY,NIGHT,ALARM,SMOKE}; enum BoltDrive{BOLT_RELEASED,BOLT_PULLIN,BOLT_HOLD}; Mode mode=HOME; BoltDrive boltDrive=BOLT_RELEASED;
MFRC522 rfid(RFID_CS,255); HardwareSerial fingerUart(1); Adafruit_Fingerprint finger(&fingerUart); String cliLine,lastState;
uint32_t driveStartedAt=0,reedOpenedAt=0,releasedAt=0,lastStateAt=0,authorisedAt=0,lastPhotoAt=0,presenceStartedAt=0,wrongWindowAt=0,lastFingerScanAt=0,lastCardAt=0; uint8_t wrongCount=0; bool reedLast,boltsLast,leverLast,presenceReported=false,pendingSecondFactor=false;
void logLine(const String&t){Serial.printf("%lu %s\n",millis(),t.c_str());} void ot(const String&c){OThreadCLI.println(c);logLine("OT> "+c);} void sendNv(const String&t){ot("udp send ff03::1 1234 "+t);logLine("TX.THREAD "+t);} void eventNv(const char*e){sendNv(String("NV.GATE.EVT.")+e);} bool gateShut(){return digitalRead(REED)==LOW;} bool boltsOut(){return digitalRead(BOLTS_OUT)==LOW;} bool leverPressed(){return digitalRead(LEVER)==LOW;}
void updateLeds(){bool locked=boltsOut();digitalWrite(GREEN_LED,(mode==HOME||!locked)?HIGH:LOW);digitalWrite(RED_LED,(mode==ALARM||locked)?HIGH:LOW);} void sendState(bool force=false){String now=String(boltsOut()?"LOCKED":"UNLOCKED")+"."+(gateShut()?"SHUT":"OPEN");if(force||now!=lastState||millis()-lastStateAt>=STATE_MS){lastState=now;lastStateAt=millis();sendNv("NV.GATE.STATE."+now);}updateLeds();}
void setSolenoidDuty(uint8_t d){ledcWrite(TOP_SOL,d);ledcWrite(BOTTOM_SOL,d);} void releaseBolts(const char*r){if(boltDrive==BOLT_RELEASED)return;setSolenoidDuty(0);boltDrive=BOLT_RELEASED;releasedAt=millis();logLine(String("ACT.SOLENOID.RELEASE.")+r);sendState(true);} void startOpening(const char*r){if(mode==ALARM&&strcmp(r,"MODE_SMOKE")!=0){logLine(String("OPEN.BLOCKED.ALARM.")+r);return;}if(boltDrive==BOLT_RELEASED){setSolenoidDuty(PWM_FULL);boltDrive=BOLT_PULLIN;driveStartedAt=millis();reedOpenedAt=0;logLine(String("ACT.SOLENOID.PULLIN.200MS.")+r);}authorisedAt=millis();}
void triggerCamera(const char*r){if(millis()-lastPhotoAt<PHOTO_COOLDOWN_MS)return;digitalWrite(CAMERA_TRIGGER,HIGH);delay(200);digitalWrite(CAMERA_TRIGGER,LOW);lastPhotoAt=millis();logLine(String("ACT.CAMERA.TRIGGER.200MS.")+r);} void registerWrong(const char*r){logLine(String("AUTH.FAIL.")+r);eventNv("AUTH_FAIL");if(!wrongWindowAt||millis()-wrongWindowAt>WRONG_WINDOW_MS){wrongWindowAt=millis();wrongCount=0;}if(++wrongCount>=3){wrongCount=0;wrongWindowAt=millis();logLine("AUTH.TAMPER.THREE_WRONG_IN_1MIN");eventNv("TAMPER");triggerCamera("TAMPER");}}
void authorised(const char*m){logLine(String("AUTH.OK.")+m);eventNv("AUTH_OK");startOpening(m);} bool uidMatches(const MFRC522::Uid&u){if(u.size!=sizeof(AUTHORISED_UID))return false;for(byte i=0;i<u.size;i++)if(u.uidByte[i]!=AUTHORISED_UID[i])return false;return true;}
void processCard(){if(!rfid.PICC_IsNewCardPresent()||!rfid.PICC_ReadCardSerial())return;if(millis()-lastCardAt<CARD_COOLDOWN_MS){rfid.PICC_HaltA();rfid.PCD_StopCrypto1();return;}lastCardAt=millis();bool known=uidMatches(rfid.uid);rfid.PICC_HaltA();rfid.PCD_StopCrypto1();logLine(known?"SENSOR.RFID.KNOWN_CARD":"SENSOR.RFID.UNKNOWN_CARD");if(mode==ALARM){logLine("AUTH.IGNORED.ALARM.CARD");return;}if(!known){registerWrong("CARD");return;}if(mode==HOME){authorised("RFID");return;}if(mode==AWAY||mode==NIGHT){if(pendingSecondFactor&&millis()-authorisedAt<=AUTH_WINDOW_MS){pendingSecondFactor=false;authorised("RFID_AFTER_FINGER");}else{pendingSecondFactor=true;authorisedAt=millis();logLine("AUTH.CARD.OK.WAITING_FINGER_10S");}}}
void processFinger(){if(millis()-lastFingerScanAt<FINGER_SCAN_MS)return;lastFingerScanAt=millis();uint8_t p=finger.getImage();if(p==FINGERPRINT_NOFINGER)return;if(p!=FINGERPRINT_OK){logLine("SENSOR.FINGER.READ_ERROR");return;}p=finger.image2Tz();if(p!=FINGERPRINT_OK){registerWrong("FINGER_TEMPLATE");return;}p=finger.fingerFastSearch();bool known=p==FINGERPRINT_OK;logLine(known?"SENSOR.FINGER.MATCH":"SENSOR.FINGER.NO_MATCH");if(mode==ALARM){logLine("AUTH.IGNORED.ALARM.FINGER");return;}if(!known){registerWrong("FINGER");return;}if(mode==HOME){authorised("FINGER");return;}if(mode==AWAY||mode==NIGHT){if(pendingSecondFactor&&millis()-authorisedAt<=AUTH_WINDOW_MS){pendingSecondFactor=false;authorised("FINGER_AFTER_RFID");}else{pendingSecondFactor=true;authorisedAt=millis();logLine("AUTH.FINGER.OK.WAITING_CARD_10S");}}}
void setMode(const String&v){if(v=="HOME")mode=HOME;else if(v=="AWAY")mode=AWAY;else if(v=="NIGHT")mode=NIGHT;else if(v=="ALARM")mode=ALARM;else if(v=="SMOKE")mode=SMOKE;else return;pendingSecondFactor=false;logLine("MODE."+v);if(mode==SMOKE)startOpening("MODE_SMOKE");sendState(true);} void parseMessage(const String&m){if(!m.startsWith("NV."))return;logLine("RX.THREAD "+m);int a=m.indexOf('.',3),b=a<0?-1:m.indexOf('.',a+1);if(a<0||b<0)return;String from=m.substring(3,a),kind=m.substring(a+1,b),rest=m.substring(b+1);if(from!="CORE")return;if(kind=="MODE"){setMode(rest);return;}if(kind!="CMD")return;int d=rest.indexOf('.');if(d<0)return;String target=rest.substring(0,d),action=rest.substring(d+1);if(target!="GATE"&&target!="ALL")return;logLine("CMD."+action);if(action=="RELEASE"||action=="UNLOCK")startOpening("REMOTE");}
void pollThread(){while(OThreadCLI.available()){char c=OThreadCLI.read();if(c=='\n'){int at=cliLine.indexOf("NV.");if(at>=0){String m=cliLine.substring(at);int sp=m.indexOf(' ');if(sp>0)m=m.substring(0,sp);m.trim();parseMessage(m);}cliLine="";}else if(c!='\r'&&cliLine.length()<300)cliLine+=c;}}
long distanceCm(){digitalWrite(ULTRA_TRIG,LOW);delayMicroseconds(2);digitalWrite(ULTRA_TRIG,HIGH);delayMicroseconds(10);digitalWrite(ULTRA_TRIG,LOW);unsigned long us=pulseIn(ULTRA_ECHO,HIGH,25000);return us?(long)(us/58):-1;} void serviceSensors(){bool shut=gateShut(),out=boltsOut(),lever=leverPressed();if(shut!=reedLast){logLine(String("SENSOR.REED.")+(shut?"SHUT":"OPEN"));if(!shut){if(boltsOut()&&millis()-authorisedAt>FORCE_GRACE_MS&&!lever){eventNv("FORCED");triggerCamera("FORCED");}reedOpenedAt=millis();eventNv("OPENED");}else eventNv("CLOSED");reedLast=shut;sendState(true);}if(out!=boltsLast){logLine(String("SENSOR.BOLTS.")+(out?"BOTH_OUT":"NOT_BOTH_OUT"));boltsLast=out;sendState(true);}if(lever&&!leverLast){logLine("SENSOR.INSIDE_LEVER");authorisedAt=millis();eventNv("LEVER");}leverLast=lever;}
void serviceBolts(){if(boltDrive==BOLT_PULLIN&&millis()-driveStartedAt>=PULLIN_MS){setSolenoidDuty(PWM_HOLD);boltDrive=BOLT_HOLD;logLine("ACT.SOLENOID.HOLD.30PCT");}if(boltDrive!=BOLT_RELEASED){if(mode==SMOKE&&millis()-driveStartedAt>=HOLD_LIMIT_MS){releaseBolts("SMOKE_HOLD_LIMIT");eventNv("THERMAL");}else if(mode!=SMOKE&&((reedOpenedAt&&millis()-reedOpenedAt>=GATE_OPEN_SETTLE_MS)||millis()-driveStartedAt>=OPEN_TIMEOUT_MS))releaseBolts(reedOpenedAt?"GATE_OPEN_PLUS_2S":"NO_GATE_OPEN_5S");}if(releasedAt&&!boltsOut()&&millis()-releasedAt>=JAM_WAIT_MS){releasedAt=0;logLine("ACT.SOLENOID.JAM.BOLTS_NOT_OUT");eventNv("JAM");}}
void servicePresence(){static uint32_t lastSample=0;if(millis()-lastSample<120)return;lastSample=millis();long cm=distanceCm();bool close=cm>0&&cm<80;if(close){if(!presenceStartedAt){presenceStartedAt=millis();presenceReported=false;logLine("SENSOR.ULTRASONIC.UNDER_80CM");}if(!presenceReported&&millis()-presenceStartedAt>=PRESENCE_MS){presenceReported=true;logLine("SENSOR.PRESENCE.OVER_1S");eventNv("AT_GATE");triggerCamera("AT_GATE");}}else{presenceStartedAt=0;presenceReported=false;}}
void setup(){Serial.begin(115200);delay(250);pinMode(REED,INPUT_PULLUP);pinMode(BOLTS_OUT,INPUT_PULLUP);pinMode(LEVER,INPUT_PULLUP);pinMode(ULTRA_TRIG,OUTPUT);pinMode(ULTRA_ECHO,INPUT);pinMode(CAMERA_TRIGGER,OUTPUT);pinMode(GREEN_LED,OUTPUT);pinMode(RED_LED,OUTPUT);digitalWrite(CAMERA_TRIGGER,LOW);ledcAttach(TOP_SOL,PWM_HZ,8);ledcAttach(BOTTOM_SOL,PWM_HZ,8);setSolenoidDuty(0);SPI.begin(RFID_SCK,RFID_MISO,RFID_MOSI,RFID_CS);rfid.PCD_Init();fingerUart.begin(57600,SERIAL_8N1,FINGER_RX,FINGER_TX);finger.begin(57600);logLine(finger.verifyPassword()?"FINGERPRINT.READY":"FINGERPRINT.NOT_FOUND");reedLast=gateShut();boltsLast=boltsOut();leverLast=leverPressed();logLine("NIVASA.GATE.BOOT.MODE.HOME");OThread.begin(false);OThreadCLI.begin();OThreadCLI.setTimeout(100);ot("thread stop");ot("ifconfig down");if(strlen(NIVASA_DATASET)){ot("dataset set active " NIVASA_DATASET);logLine("THREAD.DATASET.CONFIGURED");}else logLine("THREAD.DATASET.MISSING");ot("ifconfig up");ot("thread start");ot("udp close");ot("udp open");ot("udp bind :: 1234");sendState(true);}
void loop(){pollThread();serviceSensors();processCard();processFinger();if(pendingSecondFactor&&millis()-authorisedAt>AUTH_WINDOW_MS){pendingSecondFactor=false;registerWrong("SECOND_FACTOR_TIMEOUT");}serviceBolts();servicePresence();sendState();delay(5);}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik