Introduction: Electronic Safe


This is a simple electronic safe. It will consist of a 3d printed safe coupled with an Arduino at the center of the electronic access system. This project is being completed as part of the End of 2017 Project Contest on the Hackerboxes Sub Reddit. As such the major electronic components would be ones from previous Hackerboxes. Box numbers have been included with the components accordingly.

For those unaware, Hackerboxes is a monthly subscription box service focusing on DIY electronics and computer technology. Find out more at and visit their Instructables page at:

At the core of the electronics is an Arduino Uno that acts as the central control unit. It takes all the user inputs from the keypad and communicates with the other components. The main functionality is accomplished with the inclusion of a keypad for user's to enter password, a LCD screen to provide feedback and a servo to open/close the lock. An SD card module allows a record to be maintained and a Real Time Clock to enable both the recording of time date in the log and allowing two-factor authentication be used. A simple switch and some standard LED's with resistors allow for some internal lighting.

A single Arduino Uno will be a few pins short for everything. The main culprit is the LCD display as it requires seven pins (at minimum). To get around this there are a few options:

  1. Switch to a few LED's to indicate state or use an addressable LED such as a NeoPixel.
  2. Switch to a different board with more I/O (such as an Arduino Mega).
  3. Use I/O expander or Shift Register
  4. Use an LCD with an I2C Backpack - This will reduce the number of pins needed for the display.
  5. Use a second Arduino to act as a an I2C Backpack. This is the approach taken in these instructions.

However, if data collection is being omitted, then enough pins will be available to handle all the remaining components.

Disclaimer: This is not designed as a professional security product and should not be used in such an application. Use at your own risk.

Step 1: Bill of Materials and Wiring Diagram

Billing of Materials:

These are the components used for this build. The major components are all from previous Hackerboxes. Adafruit links are also included for your reference, though many other retailers, including Amazon, will have these components for sale.


A wiring diagram and a schematic are included for reference. In addition, the sketches and the following steps break down the pinouts accordingly.

Step 2: Display

Begin with setting up and testing the LCD display. Due the lack of enough pins on the Arduino Uno, the LCD is going to be driven by a second Arduino which is going to get data from the first which is controlling the other components of the system.

The LCD is going to need seven wires between it and the microcontroller driving it. In addition there is a 5V connection and three Ground connections needed. Finally the contrast is driven by a 10K potentiometer with connections to both 5V and Ground. A mini Breadboard may be beneficial in making all these connections.

While the pin order seams common among these kind of display you should still confirm with your particular display while making these connections. All connections to an Arduino go to the one driving the LCD. The LCD can be fed data in a 4-bit or 8-bit mode. Each bit has it's own data pin (D0-D7). Because the only real benefit to 8-bit is increase of speed that is not needed here, this project is going to stick to a 4-bit setup. As such pins D0-D3 are not being used and can be left disconnected (i.e. floating) without issue. This is the pin setup as written in the included code. They, however can be changed to whatever you want by changing the pin output in the constants defined at the start of the code as long as these do not use the I2C/TWI bus pins (A4 & A5 on the Arduino Uno.

  1. VSS (Display Power) to Ground
  2. VCC (Display Power) to 5V
  3. VO (Contrast) to 10 K Pot Wiper
  4. RS (Registry Select) to Arduino Pin 12
  5. RW (Read/Write) to Ground
  6. EN (Enable) to Arduino Pin 11
  7. D0 (Data) - No Connection
  8. D1 (Data) - No Connection
  9. D2 (Data) - No Connection
  10. D3 (Data) - No Connection
  11. D4 (Data) to Arduino pIn 5
  12. D5 (Data) to Arduino Pin 4
  13. D6 (Data) to Arduino Pin 3
  14. D7 (Data) to Arduino Pin 2
  15. A (5V Backlight) to Arduino Pin 6
  16. K (Backlight Ground) to Ground

Additionally, the other two connections of the pot will need to be connected to 5V and ground. The Microcontroller driving the LCD is connected to the main board over the I2C/TWI bus. On the Arduino Uno these are pins A4 (SDA) and A5 (SCL). The two boards will also need to share the same ground.

After making all the connections, it is time to upload new code to the Arduino. The LCD-Controller sketch includes all the code needed for the LCD Controller board. This sketch relies on both the Wire and LiquidCrystal libraries. Both are likely included with the Arduino IDE, if not these will need to be installed before compiling the sketch. Instructions on installing a library are available on the Arduino's official website (

Currently this sketch is set up to receive data over the i2C bus from the other board and then write it on the screen. It also keeps track of how long the backlight has been on and turns it off after a fixed amount of time. Currently this is written as 10 seconds but it can be changed by altering the timeoutled constant in the beginning of the sketch. Note that this is based on milliseconds or 1/1000 of a second. (1 Second = 1,000 milliseconds).

In order to confirm these are working you will need to send some data over the I2C bus. To test your setup two sketches are included for running off the main control board. This will let you test both controllers and their ability to communicate with each other. Test_LCD_Data will send new data every 1/2 second. Test_LCD_timeout sends data at increasing intervals to test that the backlight turns on and off accordingly. Once the LCD and two Arduino boards are working in unison, you can begin wiring and testing the keypad.

Step 3: Keypad

With the LCD now working, it is time to add the Keypad. The Keypad will connect directly to the main Arduino which will handle the receiving of key inputs. A matrix keypad works sending power to each column individually and looking at the rows to see if any go high. After checking to see if any rows have gone high, the controller moves on to the next column. Repeat this for each column quickly over and over and the micro controller can detect the button presses quicker than you can press.

The keypad uses a pin per column and row. Since we need a classic number pad setup, it will use 4 row of three buttons. In total it will use seven pins. If your keypad also includes a fourth column (such as Letters (A-D) or function keys (F1 - F4)), you can leave that column disconnected without any impact. These pins can be assigned as desired. There are, however, several limitations to consider. These include: 1) the I2C bus needs to be free for the LCD, 2) the SPI bus needs to be free for the SD card module & 3) A single PWM pin, except pin 9, will be needed for the Servo. They keypad does not need any separate Ground or 5V connections.

The provided code here has the pinout as follow:

  • Row 1 (Keys: 1, 2, 3, A) > Arduino Pin A3
  • Row 2 (Keys: 4, 5, 6, B) > Arduino Pin A21
  • Row 3 (Keys: 7, 8, 9, C) > Arduino Pin A1
  • Row 4 (Keys: *, 0, #, D) > Arduino Pin A0
  • Column 1 (Keys: 1, 4, 7, #) > Arduino Pin 7
  • Column 2 (Keys: 2, 5, 8, 0) > Arduino Pin 8
  • Column 3 (Keys: 3, 6, 9, #) > Arduino Pin 9
  • Column 4 (Keys: A, B, C, D) > Not Connected

This sketch (Test_Matrix_Keypad) will rely on the Keypad library by Mark Stanley, Alexander Brevig. The library can be installed directly from the Arduino IDE. Just search for Keypad in the Manage Libraries window. The library handles most of the hard work. Users define the size of the keypad, map out the keys (i.e. which key is which character), and identify the pinout of the rows and columns. The library will work out most of the rest, including establishing pins as input or output and switching inputs to High or Low. This sketch uses an event handler to pick up when a button is pressed. When the event is triggered, it transmits the keypress and some text to the LCD. With the LCD displaying your key press, it is now time to incorporate the real time clock.

Step 4: Real Time Clock

The Real Time Clock is used to keep track of the time for the system. These are low power modules that can be powered off a button cell for extended periods of time (years). This allows the system to keep the time even in the event of a power failure. Since accurate time will be needed for the One Time Password components, the Real Time Clock becomes necessary. Like the keypad, the Real Time clock interfaces with our main Arduino.

In this project, the DS1302 Real Time Clock will be used. However there are different versions, including the DS1307, which will also work with some adjustments to the code and/or pinout tweaks. The Real time clock is only going to need three pins, plus power. These three pins can be defined as any pins you desire. There are, however, several limitations to consider. These include: 1) the I2C bus needs to be free for the LCD, 2) the SPI bus needs to be free for the SD card module & 3) A single PWM pin will be needed for the Servo.

The Pinout for the sketch included is as follows:

  • RTC Clock > Arduino Pin 4
  • RTC Data > Arduino Pin 5
  • RTC Chip Enable/Reset > Arduino Pin 6

After making all the connections, it is time to upload new code to the Arduino. This step's test code adds the Time library ( and the DS1302RTC library ( The time library handles many of the time based functions with the DS1302RTC Library handling many of the interaction aspects between the RTC and the Arduino. If you are using a different RTC, use the corresponding library for your Real Time Clock.

The Real Time Clock can be set to the correct time using the SetSerial example that came with the DS1302RTC library. Once set the chip should keep the correct time as long as it is powered either by the pins or the button cell battery. Note: Many of the examples from both the Time Library and the DS1302RTC library include the #include <time.h> but lack the #include<TimeLib.h> that is added if using the drop down menu to include the library. If you run into outside of scope issues related to things like Sync Provider, check to see if the missing include statement cures the issue.

Start by creating an Instance of the RTC. In the setup, Sync Provider is started. This allows the syncing of the time with the Real Time Clock on a regular basis. and the starting Sync Provider in the setup. Most of the code additions come from the inclusion of sending the date and time to the LCD when there has not been any button presses for a set period (5 Seconds). This is accomplished with an If-Then statement comparing the last button press (stored based on the millis() function) and the current millis() value. Because this is set up to update the LCD every second, a tweak was made to the LCD controller to ensure the backlight was not staying on all the time. Instead of turning on when data is received, the LCD controller is looking for a specific character (2) and turns on the backlight if received. The timeout remains untouched on the LCD controller. Likewise the main controller has an additional Wire.write() statement to send the character when applicable. Note that 2 and "2" are different. Sending Wire.write("2"); will print a 2 on the LCD screen, whereas sending Wire.write(2); sends a start of text character which the controller is programed to look for to turn on the backlight.

Step 5: SD Card/Log File

The SD card module will be used to keep a log of the actions that occur along with time stamps. Data is written to a log.txt file located on the SD card. When a transaction occurs that requires writing data to the file. The file is opened the text is added and then the file is closed again to prevent corruption.

The SD card module connects via the SPI Bus. The MISO, MOSI, & SCK (Clock) pins on the SD card module connect to their corresponding pins on the main Arduino. For the Arduino Uno the pinout is as follows:

  • MOSI > Arduino Pin 11
  • MISO > Arduino Pin 12
  • SCK (Clock) > Arduino Pin 13

The Chip Select pin can be assigned to any pin, keeping in mind the limitations previously mention. For the included sketch, Arduino Pin 10 is used.

The SD card should be formatted in the FAT file format. If the card you are using is over 2GB you may run into formatting issues due to the limitation of the FAT file format. An exFAT file format may be useable in this case with the inclusion of the appropate library. However, this is beyond the scope of this instrutable.

It is advisable to create a blank log.txt file on the SD card you are going to use before working with sketch to avoid missing this step. On your computer use Notepad on Windows or Text/Edit on Mac and saving a blank document as txt will suffice. Alternatively, a log.txt file is included for ease and convince. This file should be saved to the root (ie. not in any folders) of the SD card. Once save the SD card can be removed and then inserted into SD module.

The SD card module relies on both the SPI library and the SD library. Both libraries should come packaged with the Arduino IDE. The SD library handles interacting with the SD card while the SPI library allows communications between the two.

An instance of the SD file is created in the header and the SD card is started (SD.begin(pin)); in the setup. Writing to the file only requires opening the file, by assigning it to the instance and then writing the data accordingly. Once completed, the file is closed to prevent the risk of data corruption/power failure when the file is not being used.

The remaining additions mirror the code written to display the time on the LCD just replacing the Wire with the file on the SD card. This one is set up to write the date and time along with the keypress any time a key is pressed. The file will have this chronological order (Oldest press to newest).

Step 6: Two Factor Authentication

With the exception of the servo, the hardware is all interconnected. For added security, this project will include Time-based One Time Password system to add a second layer of security. This is a form of multi-factor authentication. Multi-factor authentication relies on two, or more, different authentication means. In order for access to be granted, all the authentication means must be completed successfully. With the Time-based One Time Password, a six digit code is generated every thirty seconds. In order to gain access the user will need the current six digit code. The user has a device that is generating codes, such as a phone or small electronic token device. This device shares the same key as the safe. When both are set to the same time they will generate the same six digit code. This increases the level of security by requiring an unauthorized person to obtained your password and the device that is generating your six digit codes. Find out more about Multi-factor authentication ( and Time-based One Time Password ( on wikipedia.

To achieve this on the Arduino, this project will rely on the TOTP Library by Luca Dentella. This can be installed directly from the Arduino IDE by searching for TOTP. Alternatively you can find more about this library, including the download link from Github, by visiting:

Both the Android and a external device need to be able to generate the same codes in order for someone to unlock the safe. Starting with the external device, the easiest way to set this up is to use a phone or tablet. Multiple apps exist to generate these codes, including the Google Authenticator app by Google. After installing the app, you will need to input the key to generate the correct codes. The Arduino also needs a key to be able to calculate the current code. Both can be conveniently generated at: In addition, a test setup has been provided in the image above, which includes the QR code to ease setup on the phone.

With your phone generating codes successfully, it is time to set up the Arduino. The new additions to the test code begin with the inclusion of the TOPT library. With the library added, you now need to supply the sketch the key and create an instance of the TOTP. With that established, a couple lines of code is all that it takes to generate the current six digit code. This includes parsing the current time to the getCode() function and storing the return code into a variable.

For this test sketch, the code is generated when the * key is pressed. It will then display the code on the LCD screen. However, the TOPT relies on GMT, which does not rely on Daylight Savings Time and does not take into account any time offset. As such the time would need to be adjusted for timezone and daylight savings time. The easiest solution would be to adjust the time given to the code generator to compensate for the difference (in seconds) between local time and GMT. However, this would need to be adjusted twice a year to compensate for Daylight Savings Time.

A better solution relies on the Real Time Clock being set to GMT and using a library to adjust for local time. The library, put out by Github user JChristensen, can be found at: Setup begins by defining the rules for your particular timezone and a few lines to set things up. In the main loop, there is a line to get the current time (UTC/GMT) and a second line to convert to local time. The only other change is to send the local time to the functions parsing current date and time (ie. year(), month(), day(), hour(), minute(), second()). This is easily achieved by including the variable holding the local time in the ().

With the Real Time Clock set to UTC/GMT and timezone library converting to local time. You should have both the current time displayed on the LCD display and a matching six digit code on your phone and when you press * on the keypad. Once both of those working, it is time to start putting these pieces into one coherent piece that work in unison to lock and unlock the safe.

Step 7: Servo & Polishing the Code

With most of the components working, now is a good time to start putting these pieces together in a way to effectively control the safe. The global and setup() functions do not vary much from the previous test codes. The rest of the code is broken up into multiple functions. A brief overview of these functions are below with some more details in the sketch comments.

The loop() gets any keypress and has a few if-else statements to handle what is going on and what state things are at. The first if statement is triggered on a key press. It adds the keypress to the variable storing the user provided code. Nested inside this if statement is a set of If, Else-if statements that trigger the functions for verifying the password and one time password based on the current state of the safe. A second set of If-Else statements is used to determine when things time out. (i.e. there is two long between keypresses)

The passwordcheck() function is triggered by the main loop. This function has a single set of If-else statements. It simply checks the password of the safe against the user inputed code. If successful it changes a boolean variable so the main loop knows the password was entered successfully, displays that the One Time Password needs to be entered, and writes to the SD card that the password was entered successfully.

The authenticate() function is triggered by the main loop. This function operates similarly the passwordcheck() function. The only main difference is the inclusion of the time for the one time password, and the triggering of the unlock() function if the code entered matches.

The lock() function is triggered by the keypadEvent() function when the * key is held down. This resets the variables determining the state of the safe back to lock. It displays that the safe is locked and writes that to the SD card. It also triggers the servo to move to the locked position.

The unlock() function is triggered by the authenticate() function when the one time password matches the user input. It smily moves the servo to the unlocked position, and displays the safe is unlock with instruction on how to relook the safe.

The keypadEvent() function is used to pass key presses to the main loop as well as trigger the lock() function when the * key has been held down.

The showtime() function is triggered by the main loop when no other action is occurring (i.e. idle). It contains several statements to format and display the local time on the LCD.

The writetime() function mirrors the showtime() function but for the SD card. It is triggered after any writing to the SD card occurs and writes the date and time to the SD card.

Adding the servo to the mix is only a matter of adding a few lines of code. Globally, it requires calling the library (#include <Servo.h>) and creating an instance of the library (Servo lockservo;). Since the library can handle up to 12 servos on most Arduino boards, the particular servo being used needs to be attached with the lockservo.attach(pin number) line of code. While this can be added to the setup() and be left attached, it may make the final product more noisy. Alternatively the servo can be attached before each change and detached (lockservo.detach()) right afterwords. This second approach is used in the sketch included with this step. To give the servo enough time to move position, a 1 second delay (delay(1000)) is used before the servo detach. To change the servo's position a simple write statement (lockservo.write(position)) is used with the desired position, in degrees, passed along. A simple arm attachment that comes with most servo's will suffice. This arm will extend out the door and will hit the door frame it someone attempts to open the door.

With all these pieces together, it is work running a few test with things set up on the desk to ensure the sketches and components work smoothly in the final configuration. It is easier now to work out bugs than later when things are all set up in the final enclosure.

Step 8: Safe Construction

The safe consists of six components. These include: 1) Safe Body, 2) Door, 3) Door Frame, 4) Hinges, 5) Handle, & 6) Door Back Cover. This multipart construction allows ease of assembly and minimizes the needs for supports.

This is a first pass at the design of the safe. Tweaks are going to be needed if you decided to print this. For example the door was designed to have a small gap all around the frame. When printed and assembled the door hit the frame on the side opposite of the hinges. A couple of tweaks, such as the door size, have been made in the included STL files.

Printing Considerations

1) This safe was designed with the build envelope of the Prusa i3 MK2 in mind (250 x 210 x 200).

2) The original Fusion 360 file is included to allow modifications to the design/sizes to compensate for different size components and print beds.

3) The original Fusion 360 file is set to inches, however the STL files come over as if in millimeters. As such the STL files may need to be scaled up by 2540% to be sized correctly.


Safe Body: Prints without supports and flat side on the bed. Some cleanup of the bolt holes on the bottom may be needed.

Door Frame:Prints without supports and flat side on the bed. Some cleanup will be needed for the nut slots, however they can print successfully with a good printer due to their small size.

Door:Prints without supports and flat side on the bed. Some cleanup will be needed for the nut slots, however they can print successfully with a good printer due to their small size.

Hinges: These print on their side with supports. The supports help with both the bridging and the rounded edges. Supports removed with relative ease with needle nose pliers.

Handle: Prints without supports and flat side on the bed. The hole should be resized to be slightly smaller than the hardware being used to attach it to the door. The Machine Screw/Bolt used will be cutting the threads into the part

Door Back Cover:Prints without supports and flat side on the bed.

Step 9: Assembly

With everything working and all the components printed out, it is time to assemble the safe.

1) Installing the electronic components onto the back side of the door first. Waiting means dealing with both the handle and hinges preventing the door from laying flat. The safe door is designed for a press fit of electronics. Each pice fits tightly between two or more post. If a fit is slightly too tight, filing the post down can help. If a fit is slightly too loose, some tape or other thin material can be used between the post and component to create a secure fit. The original Fusion 360 Archive file is included in the previous step if a new version of the door is needed for your components.

2) The hinges are installed on the the door and door frame. The sides with holes go on the door and the ones with posts go on the door frame. The top hinge needs the part with a cut to the underside. The middle hinge is inverted to the other two. The hinges are a tight fit in the groves. A bar clamp may be helpful to get the hinges installed flushed. To complete the installation, install the hinges to the door or door frame (user's choice). Then mate the post with their corresponding holes. While the two hinge halves are connected, install the remaining connection (door or door frame, whichever one remains undone). Because the middle hinge is inverted to prevent the door from just being lifted off the hinges, the two hinge halves cannot be installed on the door and door frame and then mated as the middle hinge will cause interference.

3) The electric connections can be made (diagram and pin layouts are available in the previous steps). Jumper wires can be used if desired the back cover will cover most of these connections well enough.

4) Then handle can be connected to the door. The handle connects with two machine screws sized just larger than the holes in the handle. The machine screw will be digging into the hole walls (A/K/A cutting it's own threads) so going too big will make installing the handle tough. The keypad is adhesives backed to the front of the safe and the connection is fed through the hole where it is wired up on the back side.

5) Power is supplied to all the electronics through a shared power bus (i.e. Breadboard). Two wires (positive and negative) feed out the door along the hinged side. These wires will connect to the power connection in the back of the safe.

6) With all the electrical connections done. Confirm the Arduino controllers are programmed correctly and that the correct one is connected to the correct components. It is also a good time to confirm that the Real Time Clock is set to the correct time (UTC). A through testing is in order here as the next step will be adding the back cover which will limit access. This is also a good time to set the servo position that works best for the safe and adjust the sketch accordingly.

7) Once everything is wired correctly and working, it is time to install the door back cover which will protect these connections and make things look neater. Nuts are slotted into the slots on the post found on the door. The door fits over the components and wires. Feed the two wires for power through the gap between the door and the door back cover by the hinges. Machine screws are fed though the four holes and tighten down. They should engage with the nuts that were placed in the slot.

8) The switch for the interior lights screw through the two slots on the door frame. Adjust the height so the switch so the circuit closes when the door closes and opens when the door opens. It will be easier to connect wires to the switch before installing the switch and, especially, before installing the safe body.

9) The body of the safe is mounted with the same machine screws and nuts in slot configuration as the door and back door cover. The safe body features four mounting holes/feet. The safe can be assembled with these feet down for floor/desktop setup or up for an under-desk/under-shelf setup.

10) Power (5V) is fed through the hole in the back of the safe body. A power jack can be used if desired. This connects to the wires suppling power to the electronics in the safe door. Leave enough slack for the safe door to open and close without the wires coming out of the door restricting the movement.

11) The LED lights for the interior can be mounted in multiple positions. These include along the top edge of the door frame. Wire your LEDS up including any necessary resistor(s) and mount them to the safe. Power for the LED's come from the same 5V supply feeding the safe with the switch wired to disconnect power while the door is closed.

12) Finish with tucking wires into edges and adding hot glue or caulking to keep them in place and for a nicer look. Preform one final test to confirm operation and enjoy.

Step 10: Future Improvements

This final section goes over some possible avenues of improvements and upgrades for the safe.

SD Card Access

The current design does not allow easy access to the SD card. If someone needed access to the cared (say to review the log) it would require unscrewing the entire back door cover to gain any meaningful access. Creating a spot for access in the back door cover will allow easier access while the safe is open. This, however, may be undesired in some applications as it could also allow ease to tampering or destroying the log/card data while the safe is open.

Better Locking Mechanism.

The current design uses only one servo to lock the safe in the middle of the door. This was done to make sure the servo would clear the frame of the safe. However this leaves some flex in the door, more than may be desired. Reconfiguring the door and door back cover to allow either the servo to sit closer to the handle and/or for a second servo on the other edge will provide better locking results

Single Arduino

A few simple tweaks should allow the safe to drop to one Arduino controller. Adding an I2C backpack to the LCD may be enough (absent memory issues arising from the additional library). Furthermore, if the SD card/log is not being used or is needed, it's exclusion will free enough pins on the main Arduino to drive the LCD. It will also remove the need for both the SPI and SD card libraries freeing up room. It will also allow good amount of code to be discarded as well.

Safe Design Improvements

The 3D design for this safe is best described as an first iteration. There are spots for improvements. These includes adding thickness to some spots for additional stiffness and ensuring things are sized to the hardware. Depending on switch design, some better clearances may be needed for the mounting point for the light switch.

Better Time Keeping

The DS1302 is adequate but can gain or loose time more so than some other Real Time Clocks. A function to adjust the time could be used or an upgrade to a better Real Time Clock may be in order. The function can simply be written to utilize a button combination (such as # & something) or the fourth column of buttons found on some keypads, usually A-D or F1-F4. This function will increase or decrease the time by a number of seconds and update the Real Time Clock accordingly. The display will then update with the new time. A user repeats this process until the time on the display reads the current local time (and therefore the Real Time Clock is reading the current UTC time).

Arduino Contest 2017

Participated in the
Arduino Contest 2017