← Back to selected work
CASE / 01closed source

EKNOT Access

Production physical access control deployed across 15 residential entrances. Integrates custom ESP32-C6 firmware in C++ with AES-128-CBC challenge-response authentication, optocoupled relay commutation, and an Android administration app in Kotlin and Jetpack Compose.

  • C++
  • ESP32-C6
  • Kotlin
  • BLE
  • Jetpack Compose
  • OTA
SYSTEM MAP / 01

Signal path

The working path from input to physical or measurable outcome.

REQUESTResident / service app
CONTROLLERESP32-C6
PHYSICALDoor relay
↳ DEVICE LIFECYCLEOTA / logs / recovery

Release · diagnostics · deferred lossless synchronisation

RoleFirmware & Android engineering
Year2026
Deployment15 access points
SecurityAES-128-CBC challenge-response
ScopeFirmware + Android service app
01

Challenge & Requirements

Engineering constraints and goals.

The physical access system required sub-second door unlocks over BLE while maintaining AES-128 cryptographic authentication, reliable power-cycling recovery, and lossless offline logging across 15 access controllers.

02

Implementation

Architecture and engineering approach.

  1. 01

    Wrote the ESP32-C6 firmware in C++ for authenticated BLE commands, relay control, diagnostics, logs and OTA updates.

  2. 02

    Built the Android service application in Kotlin and Jetpack Compose for discovery, access operations and device administration.

  3. 03

    Implemented an AES-128-CBC challenge-response protocol with role-aware commands and backend-assisted key lookup.

  4. 04

    Prioritised the door-open path while deferring lossless log synchronisation so background work never blocks a person at the entrance.

HW

Hardware Protection & Pinout

Galvanic isolation, flyback suppression & live boot telemetry.

HW SPEC // 01

Hardware Pinout & Galvanic Protection Matrix

SCHEMATIC AUDITED · 15 SITES
Production access controllers interface with dirty inductive loads and unshielded cable runs up to 50 meters. The ESP32-C6 core is completely isolated from high-voltage flyback EMF, static human discharge, and switch chatter.
Pin / LineNet NameDirLogic LevelProtection MechanismComponentEngineering Rationale
GPIO18RELAY_ACTUATEOUT3.3V LVTTL -> 12VOptocoupler IsolationEverlight EL817Galvanically isolates 3.3V ESP32-C6 GPIO from 12V lock strike surges; 5,000 Vrms isolation rating.
COIL_CLAMPSTRIKE_SNUBBERPWR12V DC RailFlyback Snubber Diode1N4007 (1kV / 1A)Shunts inductive kickback (>70V EMF spike) generated when electromagnetic door strike de-energizes.
GPIO19EXIT_BTN_TRIGIN3.3V (10k Pullup)RC Low-Pass Debounce10kΩ + 100nF C0GHardware low-pass filter (τ ≈ 1.0 ms) rejects mechanical contact bounce and EMI from long cable runs.
GPIO20 / 21USB_PROV_DP_DMBUS3.3V Full-Speed USBTVS ESD Array ClampUSBLC6-2SC6 (SOT-23-6)Ultra-low capacitance (<3.5pF) TVS diode array clamping ±15kV air / ±8kV contact ESD on provisioning port.
GPIO22STATUS_BEACONOUT3.3V Active-HighCurrent Limiting Resistor330Ω 1% + Green 0805 LEDLimits MCU sink/source current to <7.5mA, preventing GPIO driver thermal stress during long activations.
VIN / GND_ISOPWR_5V_REGPWR12V -> 5V / 3.3V DCSchottky Reverse PolaritySS34 (40V / 3A)Blocks accidental reverse polarity insertion from building power supply; star-point digital/actuator GND split.
Galvanic Isolation: Separate ground planes between 3.3V MCU digital logic and 12V strike coil commutators.
ESD Rating: Tested to IEC 61000-4-2 Level 4 (±15kV air, ±8kV contact discharge).
SERIAL LOG // UART0 · 115200 8N1 · ESP32-C6 (RV32IMC @ 160MHz)
ESP-ROM:esp32c6-20220919
Build:Sep 19 2022
rst:0x1 (POWERON),boot:0x2c (SPI_FAST_FLASH_BOOT)
I (28) boot: ESP-IDF v5.2.1-dirty 2nd stage bootloader
I (28) boot: compile time May 14 2026 10:14:22
I (29) boot: chip revision: v0.0
I (33) boot.esp32c6: SPI Speed      : 80MHz
I (38) boot.esp32c6: SPI Mode       : DIO
I (42) boot.esp32c6: SPI Flash Size : 4MB
I (47) boot: Loaded app partition offset 0x00010000
I (72) cpu_start: Pro cpu up. Frequency: 160MHz
I (88) cpu_start: Starting scheduler on CPU0 (RISC-V RV32IMC)
I (95) main_task: Calling app_main()
I (102) eknot_sys: Initializing EKNOT Access firmware v2.1.4
I (110) heap_init: Initializing. RAM available for dynamic allocation: 498 KB (internal SRAM)
I (125) gpio: GPIO[18]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 1
I (138) eknot_driver: EL817 optocoupler galvanic stage armed on GPIO18
I (145) eknot_filter: Hardware RC debounce (10kΩ/100nF) validated on GPIO19
I (154) esp_ble: BLE 5.3 GATT Server initialized. Service UUID: 0xFFE0, TX Power: +6dBm
I (168) eknot_auth: AES-128-CBC hardware accelerator linked
I (175) eknot_log: Non-volatile ring buffer storage mounted on flash partition
I (190) eknot_main: Hardware initialization completed in 190ms. Listening for BLE clients...
I (2450) eknot_ble: Incoming BLE connection from 64:A2:F9:31:0B:C4 [RSSI: -52 dBm]
I (2472) eknot_auth: Generated 128-bit cryptographic nonce; awaiting client response...
I (2514) eknot_auth: Mutual challenge-response verified OK (HMAC valid, latency: 18ms)
I (2518) eknot_relay: ACTUATE PULSE -> GPIO18 HIGH (800ms) -- [DOOR STRIKE UNLOCKED]
I (3318) eknot_relay: ACTUATE PULSE -> GPIO18 LOW -- [DOOR STRIKE LOCKED]
I (3325) eknot_log: Event #0412 persisted to flash: UNLOCK_SUCCESS [UID: #78291, status: 0x00]
04

Outcome & Deployment

The complete firmware-to-mobile path was deployed across 15 access points, with diagnostics and recovery designed for field operation.