Combination Lock

Arduino-based 4-digit Combination Relay

Johann Wyss

Issue 39, October 2020

This article includes additional downloadable resources.
Please log in to access.

Log in

With this ATtiny-based project, only users who know the four-digit combination can be granted access.


There may be times when you want to prevent unwanted visitors from entering your room or accessing your personal belongings. With this project, only users who know the correct four-digit combination will be given access, without the need for keys and padlocks.


This project uses the tiny, yet mighty ATtiny85 microcontroller to trigger a relay when the correct four-digit combination is entered into the four push pushbuttons.

The relay can be set up to activate all types of devices, including solenoids, servo motors, door strikes, lights, etc.

We prototype the project on a breadboard using an Arduino Nano, and then use a perfboard for our main build, housed within a 3D printed enclosure.



To keep the costs down, we used the very smallest, most inexpensive microcontroller that you can find at most electronics stores, the ATtiny85.

The ATtiny85 is an 8-pin dual inline package (DIP) microcontroller with 5 GPIO pins available. If you are familiar with the ATtiny85, you may be thinking that it has 6 GPIO. This is absolutely correct, the reset pin on the ATtiny85 can be used as a GPIO, but in doing so, you lose the ability to program the microcontroller using In-Circuit Serial Programming (ICSP) method. This means you would need to program the microcontroller using a High Voltage Parallel Programmer (HVPP).

Whilst not difficult, and you can easily make an Arduino based HVPP, this isn’t something the average hobbyist / maker has in their programmer collection. In our case, let’s just say there are 5 pins available. Perhaps, in a future issue, we can build a HVPP and add it to our growing list of programmers.

On the subject of programming, we will be using the Arduino Nano for the prototyping phase of the project due to its ability to be programmed in circuit. The ATtiny85 in this circuit, however, can’t as the GPIO pins we need are shared among the same pins used for programming.

ATtiny85 Pinout from ATMEL datasheet.

In-circuit serial programming requires the following pins:

  • MISO (Master In Slave Out)
  • MOSI (Master Out Slave In)
  • SCK (Serial Clock)

These are all required to program the ATtiny85 microcontroller. In our circuit, some of these pins will be tied to ground potential via a 10KΩ resistor. This would make it quite an involved process to constantly remove the microcontroller for programming and replacing it for testing. Thus, we opted to write the program using an Arduino Nano development board for testing first. Once testing had confirmed the desired functionality, we will modify the program to suit the ATtiny.

This way, we get the benefit of rapid prototyping from the Arduino Nano and the reduced cost of using the ATtiny in the final project. If you’re not wanting to modify the code, you can skip ahead and simply use the circuit from the final build. If you’re wanting to have a tinker, we thoroughly recommend that you use the prototyping circuit featuring the Arduino Nano development board.

Inputs and outputs

Since our intention is to use the ATtiny85, our design needs to fit the small number of inputs and outputs available on that microcontroller. Our four pushbutton switches will require a pin each, and we need an output to actuate a relay. This means the ATtiny85 has just enough GPIO pins for our application (ignoring the reset pushbutton for the reasons mentioned previously).

The input side of things is incredibly simple. All we need is a circuit that changes the GPIO’s state whenever a pushbutton is actuated. For this, we use the following circuit on each pushbutton input.

When the pushbutton isn’t pressed, the GPIO pin is pulled low via the 10KΩ resistor. When the pushbutton is pressed, 5V is applied, and thus, dropped across the resistor to provide the GPIO with 5V potential. Therefore, in our project, we can simply poll the pushbuttons, looking for a high at the GPIO pins.


The output for this project really can be anything you want providing that it falls within the capabilities of the ATtiny85.

As you can see in the ATtiny85 datasheet, the maximum DC current per I/O pin is 40mA. Exceeding this will likely damage the microcontroller.

This means the device you intend to control shouldn’t have a DC current that will exceed 40mA or need a voltage higher than 5V. If the device you want to control draws more current than this, you will need to use some additional circuitry such as transistors.

For our purposes, a relay module is perfect. These modules include all of the required circuitry to safely trigger the relay without damaging your microcontroller. The relay itself is capable of safely switching loads with potentials as high as 240V mains with currents up to 10A, or 30VDC with up to 10A current. Of course, working with mains power can be lethal. In Australia, only qualified electricians can work with voltages above 50VAC. With this in mind, we’ll stick to low power 12VDC applications.

To control the relay, we just send a high to the signal pin which causes the contacts on the relay to move from the normally closed (N/C) position on pin 1 to the normally open (N/O) position on pin 3. Pin 2 is the common pin.

For our example, we will use a 12V incandescent lightbulb. This will demonstrate how you can connect the relay to a circuit. Obviously a pin code operated lightbulb is a terrible idea but it's just a way to explain the circuit. In reality, you can connect the output to a solenoid or other such actuator which, when voltage is applied or removed, changes to the state of the device you’re controlling.

Let’s say you want a device that actuates when the correct sequence is entered on the pushbuttons. In this orientation, you would want your load connected to the normally open contacts on your relay. This way, when the correct sequence is entered, the relay is triggered and the normally closed contact switches to the normally open contact, and your device is powered.

Whereas, if you want a device that is always powered until you insert the correct sequence you can use the normally closed contacts as follows.

This isn’t the only option you have for an output. You could, for example, use a small DC micro servo like the SG90 on the output. For this, you would need to use the software servo library, as the standard servo library runs on the ATmega328’s 16-bit timer. The ATtiny85 only has an 8-bit timer available.

Build 1:

The Prototype


Parts Required:JaycarAltronicsCore Electronics
1 x Arduino Nano or CompatibleXC4414Z6372A000005
4 x Tactile Switches*SP0600S1120FIT0179
1 x 5mm LED*ZD0150Z0800COM-12903
4 X 10KΩ Resistors*RR0596R7058CE05092
1 x 180Ω Resistor*RR0554R7037CE05092

Parts Required:

* Quantity required, may only be sold in packs. A breadboard and prototyping accessories are also required.

Follow our Fritzing diagram to wire up your prototype. An important thing to keep in mind is that the pushbutton switches are programmed to represent numbers, and thus, they need to be positioned in a specific order. In the prototype, these were 1, 2, 3, and 4, from left to right.

The pushbuttons are connected so that one pin is connected to 5V potential from the Arduino Nano’s 5V pin. The adjacent pin of the pushbutton connects to a 10KΩ resistor.

The other side of this resistor is connected to GND on the Arduino Nano.

Use this guide to wire the pushbuttons to the Arduino Nano.

The LED’s cathode connects to GND, and its anode connects to a 180Ω resistor. The opposite end of the 180Ω resistor connects to digital pin 6 on the Arduino Nano.

The code

The code for this project can be downloaded from our website and is fairly self-explanatory. We use an array called ‘sequence’ to store a 4-digit code. In our example, 1,2,3,4.

int sequence[] = {1,2,3,4};

We use the polling technique to check the state of the four pushbuttons on each loop of the program by calling the readButtons() function. If a pushbutton has been pulled high, the program assigns the value for that pushbutton to a second array called ‘entered’. We use the variable ‘pos’ (for position) to dictate which position in the array that pushbutton press belongs to. This variable starts at zero and is incremented each time a pushbutton press is detected.

Therefore, the first time a pushbutton is pressed, the value of the pushbutton pressed is assigned to the entered array in position 0. The second into position 1, etc.

Once four elements have been added to the entered array, the following code block runs.

if (pos == 4) {
  //check each element.
  Serial.println("pos = 4");
  for (int i = 0; i < 4; i ++) {
    Serial.print("sequence = ");
    Serial.print("entered = ");
    if (sequence[i] == entered[i]) {
      correct ++;

We can ignore the Serial.print functions. They are simply there for debug purposes, allowing us to watch what is happening during testing on the Serial Monitor. The only code we need to be worried about here is as follows.

if (pos == 4) {
  //check each element.
  for (int i = 0; i < 4; i ++) {
    if (sequence[i] == entered[i]) {
      correct ++;

if (pos == 4) is the conditional statement for this block of code. For this to be true, four pushbutton presses must have been detected. We then use a for loop to loop 4 times, comparing the two arrays sequence [ ] and entered [ ]. If the same digit is in the same location on both arrays, variable correct is incremented as shown here.

for (int i = 0; i < 4; i ++) {
  if (sequence[i] == entered[i]) {
    correct ++;

If, after this comparison, the value in correct equals 4 (indicating that all 4 inputs were current), the output is pulled high and a delay is triggered. If any other number is stored the variable called correct, the program variables correct, and position, are returned to zero. The program then calls the zeroArray( ) function.

  if (correct == 4) {
    digitalWrite(out, HIGH);
    correct = 0;
  else {
    correct = 0;
    pos = 0;

The zeroArray() function simply writes zeros to each of the elements in the entered [ ] array, and is done via the for loop.

void zeroArray() {
  for (int i = 0; i < 4; i ++) {
    entered[i] = 0;


Using Arduino IDE, upload the 4_button_lock_uno.ino code to your Arduino Nano.

Press the pushbuttons to match what you have set in the int sequence[] and the LED should illuminate. If you have left the code as default, the combination will be buttons, 1, 2, 3, and 4.

Any other pushbutton combination should not trigger the LED.

Build 2:

The Main Build


ADDITIONAL Parts Required:JaycarAltronicsCore Electronics
1 x ATtiny85ZZ8721Z5105COM-09378
1 x 8-Pin IC SocketPI6500P0550PRT-07937
1 x Red Pushbutton Tactile SwitchSP0720S1095CE07303
1 x Black Pushbutton Tactile SwitchSP0721S1096CE07304
1 x White Pushbutton Tactile SwitchSP0723S1099CE07305
1 x Yellow Pushbutton Tactile SwitchSP0722(S1094 Grey)CE07306
1 x Green Pushbutton Tactile SwitchS0724S1098CE07307
1 x 4xAAA Battery HolderPH9268S5053POLOLU-1145
4 x Pack of AAA Batteries*SB2413S4949BCE05336
1 x 5x7cm Perfboard^HP9550-ADA2670
1 x 1N4001/1N4004 Diode*ZR1004Z0109CE05272
4 x #4 6mm screwsHP0550H1145CE07308

ADDITIONAL Parts Required:

OPTIONAL: JaycarAltronicsCore Electronics
1 x Relay ModuleXC4419Z6325CE05137
1 × 10uF Electrolytic Capacitor (for Arduino Uno programming method)RE6066R5065CE05274


* Quantity required, may only be sold in packs. Some hook-up wire is also required. ^ Perfboard may need to be trimmed down to suit 3D printed case.

To make this project suitable for a permanent application. we will build it on a perfboard with 2.54mm pin spacing. All of the connections are identical to the Fritzing diagram shown here. We simply used the solder bridge method to connect the solder pads together until a trace had been made. After each trace, we used a multimeter in continuity mode to verify the connection was made.

Note: We recommend using a thicker solder of about 1mm, which will make it easier to get sufficient solder to create the bridge. A thinner solder means you need to add heat for longer and can result in lifting the solder pads.

The project is powered using four 1.5V AAA batteries, which gives a maximum voltage of 6V. 6V is the maximum operating voltage of the ATtiny85, so for peace of mind, we have added a 1N4004 diode in series. This diode protects the ATtiny85 if the user inserts the batteries in reverse polarity but will also drop about 0.7V across it, which means the voltage provided by the four AAA batteries will be about 5.3V.

Note: Do not use 1.5V lithium battery types because they can have a voltage potential as high as 1.7V. This would mean the voltage of four lithium batteries in series would be closer to 7V, which would exceed the absolute maximum of the ATtiny85.

To conserve battery power, we designed the circuit so that you have to hold down a pushbutton to distribute power to the circuit. This means that there is no power power loss while the project is not being used, and thus, your batteries will last quite some time.

If you plan to use the 3D printed enclosure we designed for the project, you will need to trim the perfboard to about 70mm x 50mm to fit.

Install the pushbuttons so that they are all inline and with three hole spaces between the pins, as shown here.

The power pushbutton sits 10 holes up from the lower four pushbuttons and one hole to the left of the furthest left pushbutton. The LED is mounted one hole to the right of the rightmost pushbutton’s rightmost pins and 7-hole spacings up. If you follow this layout the pushbuttons and LED on your PCB should align with the 3D printed enclosure.

We used the following coloured pushbuttons:

You could label these if you desire, however, that is entirely optional. Likewise, you can change the pushbutton names / value by changing the pin assignments in the code.

In the code for the ATtiny85 program, we define the pins in this line.

int out = 0, pos = 0, b1 = 1, b2 = 2, b3 = 3, b4 = 4;

b1 - b4 define the pushbutton and assign the pin number. If for example, if you wanted pushbutton 1 to be on the left-hand side and increment left to right, the code would be:

int out = 0, pos = 0, b1 = 4, b2 = 3, b3 = 2, b4 = 1;

The relay is connected to the circuit via a 3-pin female header. This header is attached to three wires and then to the PCB. This allows you to store or mount the relay elsewhere or omit it completely if you choose to use a different device.

We solder the wires directly to the back of the PCB onto the traces we made, as indicated in this image.

The battery pack is also soldered to the Vcc and GND locations with red being Vcc and black being GND.

  1. goes to Vcc on relay module.
  2. goes to GND on the relay module and GND on the battery.
  3. goes to signal on the relay module.
  4. goes to the positive of the battery.

Programming the ATtiny85

To program the ATtiny microcontroller, we used the DIYODE ATDEV kit which you can purchase from Altronics (K9815). You can find information on this project in Issue 14 or via the following link:

This kit makes programming the ATtiny nice and simple by using an Arduino Uno as an in-circuit serial programmer. It isn’t completely necessary though.

If you don’t have that kit you can still program the ATtiny using an Arduino by connecting the Arduino to the ATtiny85 as follows.

Note: You may need to include a 10µF electrolytic capacitor to ensure success.

Your first step is to upload the ArduinoISP sketch to your Arduino Uno. This is a standard example sketch in the Arduino IDE.

In this sketch, you need to uncomment line 81 so it reads:


This allows you to use the wiring configuration shown above and not the ICSP header. You want to upload this to your Arduino Uno just as you would upload any sketch to it.

Note: If you have issues uploading the ArduinoISP sketch to the Arduino Uno while the ATtiny85 is connected, try disconnecting the reset wire on the Arduino.

With the ArduinoISP sketch on the Arduino Uno, you need to change the board to the ATtiny85. If you don’t have this board installed use the Boards Manager to install it.

You need to select the 1MHz internal oscillator for clock speed, and change the programmer to Arduino as ISP.

With the settings correct, you can open the 4_button_lock.ino sketch, which you can download from our website and upload it to the ATtiny85. However, you don’t click the usual upload button as that will just upload it to the Arduino Uno. We need to use the upload via programmer option, which you can find in the sketch dropdown or just press Control + Shift + U if you are using a PC.

Note: Before you upload, you should enter your own 4-digit pushbutton combination code into the int sequence[] = {1,1,1,1}; array.

This will use the Arduino as an in-circuit serial programmer and upload your sketch to the ATtiny85.

You can then remove the ATtiny and place it into your circuit.

3D Printed Case

The case is a simple press fit clamshell design. We added four mounting posts that you can screw your PCB down too. This will firmly hold the PCB and prevent uncontrolled movement. We left the mounting posts hole free, allowing you to use any perfboard you like. You simply need to screen into the post itself.

Both sides of the clamshell were printed on our Flashforge Creator Pro using white Flashforge branded PLA. They were both printed at 200-microns layer height and flat on the build surface without using supports.

In this orientation, and at this layer height, the front cover took about 4.5 hours to print.

The much smaller rear cover took about 1.5 hours.


Assembly is straightforward, provided you have positioned your pushbuttons and LED in the same position as ours. We have left a 1mm clearance around the area for the pushbuttons and LED to poke through the case to cater for slight discrepancies. With that said, the case is entirely optional.

Secure the PCB to the mounting posts in the enclosure. We simply dropped the PCB into the case and aligned it so the pushbuttons and LED were protruding through and the PCB was flush against the mounting posts. With the PCB correctly aligned, and the pushbuttons not fouling on the enclosure, we inserted four #4 6mm screws through the PCB and into the plastic mounting post.

Note: You may need to use a drill to create a pilot hole in the plastic mounting post and to enlarge the holes on your perfboard to match the diameter of the screw. This will prevent the PCB from splitting. Use a larger drill bit for the PCB and a smaller drill bit for the plastic to ensure that the screw has enough plastic to purchase on.

With the PCB mounted, you can route the female header extension cable through the hole in the side of the enclosure and connect it to the relay module.

Insert the battery pack into the enclosure so the batteries are facing away from the PCB. If you face them toward the PCB, the traces on the PCB could short against the metal shell of some batteries. You can then close the case by adding the rear cover and carefully pressing closed.


The program works the same way as our prototype, however, the default combination in our code has been set to {1,1,1,1}. Push and hold down the power pushbutton, and then press the first button (green) four times to trigger the relay.

Our finished build with an incandescent globe connected to the relay to demonstrate that it is working.

Where to from here?

There are many ways to expand this project. One would be a way to modify the 4-digit password without needing to reprogram the microcontroller. We would tackle this by creating a while loop after the correct sequence was entered. You could use a millis function to set a timer for x number of seconds, which if elapsed, would return the code to the beginning, resetting the entered array and locking the device again.

However, you could also have it look for a held pushbutton in this loop. If it detects a held pushbutton you could pulse the output pin several times, illuminating the LED to indicate that the project is ready to receive a new combination. At which point, the pushbuttons you enter next replace the numbers stored in the sequence array.

You could have some fun with this project by making a ‘crack the code’ game out of it. The four pushbuttons would provide up to 256 combinations (4x4x4x4 or 4^4 == 256)

You would need a random number generator to assign a number between 1 and 4 to each of the array locations. You may want this to be stored in the EEPROM so it could be retrieved after power down.

After you enter in a combination, the LED could flash with how many correct presses in the correct sequence were detected.

Your job is to crack the code as quickly as possible.

Johann Wyss

Johann Wyss

DIYODE Staff Technical Writer