element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet & Tria Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • About Us
    About the element14 Community
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Japan
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      •  Vietnam
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Project14
  • Challenges & Projects
  • More
Project14
Show and Tell! Secure Digital Combination Lock
  • News
  • Member Updates
  • Competitions
  • Forum
  • Documents
  • Theme Suggestions
  • Polls
  • Members
  • More
  • Cancel
  • New
Join Project14 to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: kp35
  • Date Created: 29 Aug 2026 10:57 PM Date Created
  • Views 88 views
  • Likes 1 like
  • Comments 0 comments
Related
Recommended

Secure Digital Combination Lock

kp35
kp35
29 Aug 2026
Secure Digital Combination Lock

This project details the hardware architecture, firmware mechanisms, and interface dynamics of an embedded electronic access control system (EACS) centered on the STM32-based Nucleo-L476RG platform. Featuring a dynamic matrix keypad scanner, a TM1637-driven 7-segment LED display module, and a Pulse-Width Modulation (PWM) controlled servomechanism, the system manages secure state transitions between locked and unlocked modes. 

Embedded access control systems require a rigorous balance between input validation latency, user feedback precision, and physical actuator safety. Utilizing traditional microcontroller unit (MCU) peripherals without non-blocking structures often leads to UI freeze or missed keystrokes during prolonged execution loops (e.g., waiting for motor displacement).

This work addresses the realization of a deterministic, state-machine-driven door lock interface implemented on the STM32L476RG microcontroller (ARM® Cortex-4 core operating up to 80 MHz). The interface combines:

  • A 4×4 membrane keypad for credential entry and administrative triggers.

  • A TM1637 two-wire display controller for Human-Machine Interface (HMI) feedback.

  • A high-torque positional servo motor for locking bolt engagement.

You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

Hardware Architecture & Pin Topology

The system topology maps digital, analog, and power routing to isolate high-current inductive spikes generated by the motor from sensitive logic lines.

image

System Interconnect Topology

  • Core Microcontroller: STMicroelectronics Nucleo-L476RG (ARM Cortex-M4)

  • Display Interface (TM1637 4-Digit 7-Segment Display Module): Bit-banged or hardware-timed synchronous two-wire serial interface (CLK_PIN on PA8, DIO_PIN on PB10).

  • Actuator Interface: SG90 Micro Servo Motor - PWM channel driving a 50 Hz control frame (SERVO_PIN on PB4).

  • Input Device: 4x4 Membrane Matrix Keypad Interface - 8 GPIO lines partitioned into 4 drive outputs (PC0–PC3) and 4 sense inputs with internal pull-up resistors (PB0, PA4, PA1, PA0).

  • Misc: Breadboard, jumper wires, external capacitor (recommended for servo decoupling)

Hardware Pinout Configuration

Component Signal / Function Microcontroller Pin (Nucleo-L476RG)
Keypad Rows Row 0–3 (Drive) PC0, PC1, PC2, PC3
Keypad Columns Column 0–3 (Sense) PB0, PA4, PA1, PA0
TM1637 Display CLK (Clock) PA8 (Arduino D7)
TM1637 Display DIO (Data) PB10 (Arduino D6)
SG90 Servo PWM Control PB4 (Arduino D5)

image

Key Technical Challenges Solved

  1. Eliminating External Dependencies: By writing a native scanning algorithm using INPUT_PULLUP and dynamic row output toggling, the build environment remains lean and immune to version conflicts.

  2. Matrix Transposition & Calibration: Physical membrane keypads often feature internal trace crossings that differ from standard layout assumptions. Using systematic mapping adjustments resolved coordinate inversions cleanly in software.

  3. Non-Blocking UI State Machine: The system safely tracks entered PIN sequences against a hardcoded string ("1234"), providing visual feedback via custom 7-segment glyphs (OPEn, CLO, Err) while supporting manual lockdown via key 'A'.

image

image

Bill of Materials

  • STMicroelectronics Nucleo-L476RG (ARM Cortex-M4)

image

  • 4x4 Membrane Matrix Keypad

image

  • TM1637 4-Digit 7-Segment Display Module

image

  • SG90 Micro Servo Motor

image

image

Complete Source Code

#include <Arduino.h>
#include <TM1637Display.h>
#include <Servo.h>

// --- DISPLAY AND SERVO PINS ---
#define CLK_PIN   PA8   // Pin D7 (Nucleo-L476RG)
#define DIO_PIN   PB10  // Pin D6 (Nucleo-L476RG)
#define SERVO_PIN PB4   // Pin D5 (Nucleo-L476RG)

// --- KEYPAD PIN MAPPING ---
const uint8_t rowPins[4] = {PC0, PC1, PC2, PC3};
const uint8_t colPins[4] = {PB0, PA4, PA1, PA0};

// Calibrated 4x4 key map matrix
const char keyMap[4][4] = {
  {'D', 'C', 'B', 'A'},
  {'#', '9', '6', '3'},
  {'0', '8', '5', '2'},
  {'*', '7', '4', '1'}
};

TM1637Display display(CLK_PIN, DIO_PIN);
Servo lockServo;

const String CORRECT_PIN = "1234";
String inputPin = "";

// 7-Segment Custom Glyphs
const uint8_t SEG_OPEN[] = {
  SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F, // O
  SEG_A | SEG_B | SEG_E | SEG_F | SEG_G,         // P
  SEG_A | SEG_D | SEG_E | SEG_F | SEG_G,         // E
  SEG_A | SEG_B | SEG_C | SEG_E | SEG_F          // N
};

const uint8_t SEG_CLOSE[] = {
  0x00,                                          // Blank
  SEG_A | SEG_D | SEG_E | SEG_F,                 // C
  SEG_E | SEG_F,                                 // L
  SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F  // O
};

const uint8_t SEG_ERR[] = {
  0x00,                                          // Blank
  SEG_A | SEG_D | SEG_E | SEG_F | SEG_G,         // E
  SEG_E | SEG_G,                                 // r
  SEG_E | SEG_G                                  // r
};

char readKeypad() {
  for (uint8_t r = 0; r < 4; r++) {
    pinMode(rowPins[r], OUTPUT);
    digitalWrite(rowPins[r], LOW);

    for (uint8_t c = 0; c < 4; c++) {
      if (digitalRead(colPins[c]) == LOW) {
        delay(40); // Soft debouncing
        while (digitalRead(colPins[c]) == LOW); 
        
        pinMode(rowPins[r], INPUT_PULLUP);
        return keyMap[r][c];
      }
    }

    pinMode(rowPins[r], INPUT_PULLUP);
  }
  return 0;
}

void setup() {
  Serial.begin(115200);

  for (uint8_t i = 0; i < 4; i++) {
    pinMode(rowPins[i], INPUT_PULLUP);
    pinMode(colPins[i], INPUT_PULLUP);
  }

  display.setBrightness(0x0f, true);
  display.clear();
  display.showNumberDec(0, true);

  lockServo.attach(SERVO_PIN);
  lockServo.write(0); // Initial state: locked

  Serial.println("System ready. Use 'A' to lock manually.");
}

void loop() {
  char key = readKeypad();

  if (key != 0) {
    Serial.print("Pressed key: ");
    Serial.println(key);

    if (key == '#') { // PIN validation trigger
      if (inputPin == CORRECT_PIN) {
        Serial.println("Access granted. Lock opened.");
        display.setSegments(SEG_OPEN);
        lockServo.write(90); // Unlock position
      } else {
        Serial.println("Wrong code.");
        display.setSegments(SEG_ERR);
        delay(1500);
        display.clear();
        display.showNumberDec(0, true);
      }
      inputPin = "";
    } 
    else if (key == 'A') { // Manual lock trigger
      Serial.println("Lock closed.");
      lockServo.write(0); // Lock position
      display.setSegments(SEG_CLOSE);
      delay(1500);
      inputPin = "";
      display.clear();
      display.showNumberDec(0, true);
    }
    else if (key == '*') { // Clear input buffer
      inputPin = "";
      display.clear();
      display.showNumberDec(0, true);
    } 
    else if (key >= '0' && key <= '9') { // Digits entry
      if (inputPin.length() < 4) {
        inputPin += key;
        display.showNumberDec(inputPin.toInt(), false);
      }
    }
  }
}

Initialization and Pin Configuration

In this section, the hardware mapping is defined. The pins were chosen to avoid hardware conflicts (e.g., the external crystal oscillator on pins PH0/PH1). Keypad rows are controlled via port PC, and columns via PB and PA.

// --- DISPLAY AND SERVO PINS ---
#define CLK_PIN   PA8   // Pin D7 (Nucleo-L476RG)
#define DIO_PIN   PB10  // Pin D6 (Nucleo-L476RG)
#define SERVO_PIN PB4   // Pin D5 (Nucleo-L476RG)

// --- KEYPAD PINS ---
const uint8_t rowPins[4] = {PC0, PC1, PC2, PC3};
const uint8_t colPins[4] = {PB0, PA4, PA1, PA0};

// Calibrated key map matched to the physical ribbon cable layout
const char keyMap[4][4] = {
  {'D', 'C', 'B', 'A'},
  {'#', '9', '6', '3'},
  {'0', '8', '5', '2'},
  {'*', '7', '4', '1'}
};

Matrix Scanning Algorithm (Without External Libraries)

Instead of relying on unstable external PlatformIO libraries, the readKeypad() function implements fast, software-based row-scanning. Rows are cyclically pulled low (LOW), and columns are read using internal pull-up resistors (INPUT_PULLUP).

char readKeypad() {
  for (uint8_t r = 0; r < 4; r++) {
    // Activate the row by pulling it LOW
    pinMode(rowPins[r], OUTPUT);
    digitalWrite(rowPins[r], LOW);

    // Check columns for a short to ground (pressed key)
    for (uint8_t c = 0; c < 4; c++) {
      if (digitalRead(colPins[c]) == LOW) {
        delay(40); // Software debounce
        while (digitalRead(colPins[c]) == LOW); // Wait for key release
        
        pinMode(rowPins[r], INPUT_PULLUP); // Restore high-impedance state
        return keyMap[r][c]; // Return the corresponding character from the map
      }
    }

    pinMode(rowPins[r], INPUT_PULLUP);
  }
  return 0; // No active key
}

Seven-Segment Vector Graphics (Glyphs)

The TM1637 display does not have built-in support for letters, so we create custom bit arrays controlling individual segments (SEG_A through SEG_G) for messages such as OPEn, CLO (Close), and Err.

// Definition of the word "OPEn"
const uint8_t SEG_OPEN[] = {
  SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F, // O
  SEG_A | SEG_B | SEG_E | SEG_F | SEG_G,         // P
  SEG_A | SEG_D | SEG_E | SEG_F | SEG_G,         // E
  SEG_A | SEG_B | SEG_C | SEG_E | SEG_F          // N
};

// Definition of the word "CLO" (Closed)
const uint8_t SEG_CLOSE[] = {
  0x00,                                          // Space (segment turned off)
  SEG_A | SEG_D | SEG_E | SEG_F,                 // C
  SEG_E | SEG_F,                                 // L
  SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F  // O
};

image

Main Control Loop and State Machine

In the main loop, the program waits for a keypad event. Depending on the pressed key, the appropriate logic branch is executed:

  • Digit entry (0-9): Appended to the inputPin buffer (up to 4 characters) and rendered on the display.

  • Confirmation (#): Compares the entered string with CORRECT_PIN. On success, moves the servo to 90° (unlock) and displays OPEn. Otherwise, reports an Err.

  • Manual closing (A): Allows immediate locking (0°) and displays the CLO message.

  • Reset (*): Clears the input buffer.

image

void loop() {
  char key = readKeypad();

  if (key != 0) {
    if (key == '#') { // PIN verification
      if (inputPin == CORRECT_PIN) {
        display.setSegments(SEG_OPEN);
        lockServo.write(90); // Unlock mechanism
      } else {
        display.setSegments(SEG_ERR);
        delay(1500);
        display.clear();
        display.showNumberDec(0, true);
      }
      inputPin = "";
    } 
    else if (key == 'A') { // Manual locking
      lockServo.write(0); // Lock mechanism
      display.setSegments(SEG_CLOSE);
      delay(1500);
      inputPin = "";
      display.clear();
      display.showNumberDec(0, true);
    }
    else if (key == '*') { // Clear inputs
      inputPin = "";
      display.clear();
      display.showNumberDec(0, true);
    } 
    else if (key >= '0' && key <= '9') { // Accumulate PIN digits
      if (inputPin.length() < 4) {
        inputPin += key;
        display.showNumberDec(inputPin.toInt(), false);
      }
    }
  }
}


You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image

Developing secure, lightweight embedded applications often reveals that third-party library dependencies can introduce build system instabilities or bloated code. This project demonstrates how to build a fully functional electronic combination lock using an STM32 Nucleo-L476RG, an SG90 servo motor, a TM1637 4-digit display, and a standard 4x4 matrix keypad, relying entirely on native, non-blocking GPIO manipulation without external keypad libraries.

  • Sign in to reply
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2026 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube