Introduction: ESP32 Bluetooth Mechanical Keyboard - the Sanctuary

About: Good projects, badly executed

Have you ever wanted the convenience of a wireless keyboard, with the comfort of a mechanical keyboard?

I have, however there's few designs available for a wireless mechanical keyboard, primarily due to the reason that Bluetooth micro-controllers are hard to come by, and usually are expensive. The other issue being software, while keyboards themselves are simple, the firmware behind them may not be.

With this in mind, I set myself 3 goals for this project:

  1. Must be wireless, with full keyboard functionality
  2. Must currently be built with common parts during the parts shortage
  3. Must have understandable code (at least to myself)

So, with these goals in mind, I designed the Sanctuary, and in this instructable I plan to provide files, and explanations of how to build it.

My goal with this guide is to walk through the entire keyboard design process, while mechanical keyboards are so popular, I surprisingly don't see them often on instructables. So, I want to try to make a definitive keyboard build guide. I truly believe that keyboards are some of the best introductions to electronics. It is a great way to learn some basics of electronics, in a longer form project. While this guide is a bit more involved, it is designed to walk you through the process, and as long as you practice soldering skills, then you can assemble this board. I will provide the files to build it yourself, so you can skip over the design steps, however, I think it is important to walk though some of the process, as the fun of building keyboards is customization. While this is my perfect layout, it may not work for everyone.

Note: This guide uses the first revision of the keyboard for photos, however the files provided are a revision 2, a improved version, fixing minor mistakes, and making small improvements.

Supplies

This section will be separated into required materials, and some extra materials for the extra functions I added.


Required:

  1. ESP32 WROOM Module
  2. USB TTL/Serial Programmer
  3. Battery Charger / Boost
  4. 3.3V Regulator
  5. 2x 18650 Battery Holder
  6. 2x 18650 Battery
  7. These are difficult to acquire, namely due to the numerous knockoffs on mainstream storefronts (amazon, ebay etc). I've heard recommendations for 18650batterystore.com, however I haven't personally used them (just make sure they are the 18650 size, otherwise they won't fit)
  8. I personally salvaged the 18650 batteries from a portable battery pack, and ensured that they were healthy and usable
  9. 83 x 1N4148 Diodes (83 minimum, however more are required for different layouts, and are recommended to be bought in bulk (ex 100))
  10. 4x 4.7k Resistors
  11. 2x 5.1k Resistors (Included in the kit with the 4.7k Resistors)
  12. USB C (Mouser)
  13. The design is specific to this brand of connector, no alternatives can be used without redesigning the PCB
  14. 12x M3 Heated Insert
  15. 14x M3 Screw
  16. 2x M3 Nut
  17. Exact listing no longer exists, I believe these are an alternative
  18. JST Connector (2 needed)
  19. 6x6x8mm Pushbutton
  20. DPDT Switch
  21. I believe these are the same switches, however I bought mine so long ago that I don't have an exact listing
  22. 2.54mm Pin Headers

Customizable but required:

  1. Keyswitch
  2. Linked is the Clicky Pale Blues I use
  3. Any Cherry compatible Keyswitch can be used - 83 switches are required at the bare minimum, however they usually come in packs of 90
  4. Keycaps (Specific ones I used, good quality)
  5. Cheaper Alternative 1
  6. Cheaper Alternative 2
  7. Any other Cherry stem keycaps can be used, these are examples of alternative mid-quality and easily available keycaps
  8. Stabilizers
  9. Screw in stabilizers are preferred, however any can be chosen
  10. PLA / PETG Filament
  11. Black PETG Used
  12. Transparent PETG Used
  13. Any filament can be used, however PETG is a bit more durable (albeit harder to print)

Optional Functions:

  1. SK6812 Mini-E LEDs
  2. Rotary Encoders
  3. Vertical USB Port
  4. 3x8mm Magnets

Tools:

  1. Soldering Iron
  2. Hot Air Rework Station
  3. Hot Glue Gun
  4. Long and thin Phillips/Flathead screwdriver (see second photo)

Misc Supplies:

  1. Solder
  2. Flux
  3. Solder Paste

Note: Amazon links are all an affiliate link, so I receive a small kickback from sales. Amazon links are Canadian (as that's where I bought from), however you should be able to change the .ca to a .com, or accompanying url.

Step 1: Concept and Design

Concept:

For this keyboard, I wanted to do a custom design, with unique functionality. Primarily, I wanted a wireless keyboard, since these are uncommon in the mechanical keyboard community. While most builds are done with a Pro Micro, this has limited pins, and is only wired. This would make it impossible to add the wireless features I wanted, so after I found Bluetooth Keyboard libraries for an ESP32, I decided to go with them.

The Third Photo is actually the initial sketched design, however as you can see, much has changed from the final project. As the project commenced I reduced the scope, and decided on features that were most important to me, while I could add the others later.

Layout Design:

This is one of the most important steps for designing a mechanical keyboard, designing your own layout! This is the primary reason I design my own keyboards, as I prefer to use my own layout for them. I like to have certain keys accessible to me, however it is convenient if it is in a more compact layout.

Keyboard-Layout-Editor is a great tool that can be used to design your own layout. Starting with a regular "Default 60%" preset, we can build the keyboard from there. Since I like my navigation cluster, I added extra columns to the right, so that I can have my arrow keys and the home/end keys, the extra keys in between will be used for other functions later. I also added half of the function keys, as I use some of the functions, specifically F1 to F6. The layout I designed is shown in the first image, it looks funky, but it works for me!

CAD File:

Next, we need to design the plate. While many in the community use Swill, a tool to build plates, I like to use the ai03 plate generator, as the plate generated becomes a lot easier to use for 3d printing.

On our layout, we click "</> Raw Data", and copy that data. Using ai03, paste that raw data, and leave the default values for the switch plate. Once the CAD had been generated, download the .DXF file. The preview from the ai03 website is shown in the second image.

Step 2: Learning About Matrices, and the ESP

Next is to prepare to create the schematics. Before we can design the schematics, we need to learn about the two primary components in a keyboard. This is the microcontroller, in this case an ESP32, as well as the matrix used in the design.

ESP32 Pinout

The ESP32 is a bit different than Arduinos, since I used the Wroom module, it is much smaller, but the pins are a lot more specific to their functions. Many pins have limitations on their use, as an example, certain pins can only be used as input. Luckily, a great guide can be found from "randomnerdtutorials" for the pinout.

Pin 0 is attached to a button to GND, this is not used for anything else, as it changes how the ESP32 boots. This will become the 'reset' or 'boot' button in the final design, which is needed to program the ESP32.

Pins numbered "5, 16, 17, 18, 19, 34, 35, 36, 39" are all used as input, however it is important to note, that the pins 34, 35, 36 and 39 need external pulldown pins, as they are not included in the ESP32 module. In the initial design, I missed that, and needed to add them later. Ensure that if you're using these pins, that you include these pulldown pins, otherwise you'll have random keyboard inputs.

Pins "2, 4, 12, 13, 14, 23, 25, 26, 27, 32, 33" are used as output, these simply output the values for the matrix to read.

The inputs and outputs were chosen carefully, to make routing in in the board easier, while maintaining compatibility with the requirements of the pinout

How A Matrix Works:

We can see an issue with this design though, if the ESP32 only has around 24 pins to use, how do we fit a full keyboard with around 90 keys? Well, we end up using a matrix. In the simplest manor, a matrix assigns multiple keys to a single input. This can be seen in the first 4 images provided, where we have 4 inputs, and 4 outputs. Regularily, we would only be able to have 8 keys inputted, however when arranged like this, we can have 16 keys. This scales further, as you make the matrix larger. Note that if you want the most efficient pin-to-key matrix, a square will always be the most efficient.

However, we now have to add a small algorithm to read the matrix, this is pretty simple, as we only want to read one column at a time, so we set one column to a HIGH logic level, and read all the row pins. Then, when we're done, we increment that HIGH logic level to the next column, and remove the previous column state, and repeat this to scan all columns. This way, we read the columns one by one. You may think this is slower to do, and that we may miss an input, however microcontrollers are very fast, and it will be impossible to press a key fast enough that the microcontroller will miss the input.

So now that we know how the scanning works, we have another issue, which is the current arrangement of the matrix, it allows for backflow. Backflow can create many issues, as it can introduce "ghost inputs", referred to as "ghosting", where the microcontroller gets confused by what key is pressed. This is best seen visually, as shown in images 2 and 3. We can see that we are scanning the last column (shown in blue), where at that time, only two switches should be read (last column in black), however due to the other switches that are pressed in columns we are currently not scanning, it can redirect the signal through the switch to the second column. The red lighting bolt would be an input read by the ESP, however since we only scan one columns at a time, it thinks the key pressed is in that column, meaning that the microcontroller thinks that the last three keys in the last column were pressed, when in reality only the two were. The backflow is visualized in the green arrow, as it would be one direction the electronic logic takes, causing the ghost input. These ghost inputs will appear as a seemingly random keypress, which can be very annoying, especially at high typing speeds.

So, we need a way of directing the electricity in one direction, which is where the Diode is used! In the simplest form, a diode only allows electricity to flow in one direction, so by adding these to each of the switches, we prevent backflow. The application can be seen in the fourth photo.


Now, we can apply this theory to our keyboard, if your layout is small enough, you can do a matrix that matches the amount of rows and columns on your keyboard (ex a 5x14 matrix for a 60% keyboard). However, if we recall, the square is the most efficient matrix, and since I want to have extra features, I need to make my matrix as close to a square as possible.

A great visualization of the matrix can be found at kbfirmware (note that we won't use the firmware, only the visualization).

For this case, I split my keyboard matrix in half, so that the matrix could be a more efficient 11x9 matrix, that can be seen in the last two photos above.


Note that the input and output of a matrix depends on the diode direction. In the example provided above, the inputs are the columns, and the outputs are the rows. However, in the final Sanctuary design, the rows are inputs, and the columns are outputs.

Step 3: PCB Design: Schematics

This is the most difficult part of the project, designing a PCB. While PCB software is convoluted for the first time, it is a good software to know, and a keyboard is a great project to start! For this guide however, I am assuming some proficiency, as this guide would only apply to one piece of software, so feel free to watch an introduction video guide for your software chosen.

While I will go in detail as much as I can, another great series about keyboard design can be found here. I highly recommend checking it out, as a way to get more familiar with the PCB design process.

For this reason, I am providing a revision 2 of the Sanctuary Keyboard if you would prefer to skip this step. These are in a gerber format, so you can order directly from JLCPCB, or another PCB manufacturer. Note that these files are provided as is, and notably I haven't tested the revision 2, although they are heavily based on revision one, which currently works. The files are available in the next step.

Software Needed:

Next we need software to design our PCB. Two common hobbiest level PCB software is KiCAD and Eagle. This keyboard was designed in Eagle, however I am in the process of switching to KiCAD. Although I used Eagle, I'd recommend starting with KiCAD, as the open source nature of the software has many benefits.

KiCAD should have a keyboard library built in, however if using Eagle, a keyboard library will need to be included in the design. Many different libraries exist, however these don't support the SK6812 Mini-E LEDs. For this, I've provided a library that can be used. It is the "Cherry-SK6812.lbr" file. Note that you'll have to measure out the stabilizers yourself, or use a seperate library such as the Clueboard library.


The Schematics:

Before we can design the PCB, we need to design our schematics in the software. This is easier said than done, and organized schematics are important to the design of the PCB.

For this, I recommend organizing the keyboard matrix in a logical manor. Make sure to name each key used, so it will be easier to organize when you start to route the PCB.

I recommend using the matrix we made in step 2 as a reference, as it becomes much easier to design the matrix around it. Ensure that you have diodes, and that they point to the output lines.

Now most of the connections are pretty simple, we connect the input of the USB C to the battery charger, and the output of the battery charger to the 3.3V voltage regulator, which in turn is connected to the ESP32

Some important notes about the schematics though:

  • the 5.1k Ohm resistors are pulldowns on the CC lines (one for each line) - they configure the keyboard to use up to 3A, and to be a device if you plan to use the passthrough USB port
  • The Rotary encoder I used has a button - this should be included in the matrix to save pins (other pins A and B of the rotary encoder are connected to their own GPIO pins to detect rotation)
  • The ESP32 has an Enable pin that must be pulled HIGH to boot - luckily they're right next to each other if you miss that (as I did the first time), however it should be done in the PCB
  • Pin 0 should be a reset button, if it is LOW on boot, it will put the ESP32 into a programming mode

Step 4: PCB Design: Board

Now that we have all of the schematics, we can create the board, however, we need to do a few things first to prepare for the PCB board.

Note: Final Board Files are Provided Here. They are a second revision of the board, removing unnecessary/unfinished features, and fixing errors in design. This should be much easier to assemble.

Modifying the CAD files:

Before we can design the PCB, we need to design the outline of the PCB. This will come into use when we start to design the PCB itself

Using the plate we got in step 1, copy the file, and edit the copied file. For this file, remove the square switches, and name it as an outline file. If you want rounded corners on your PCB, add them to this file.

Make another new file. Name this one as the Switch Reference, and put a 12.5mm circle in the middle of each square. Delete the outline, and switch squares. Alternatively, you could just use the square switches as an reference. Whatever you choose to do, this file should be named as a reference file.

Now we need to adjust the layout to be on the keyboard grid. For this, we should be able to move the whole board around the origin to align the middle of the keyswitches to a 2.38125mm grid. This may seem like an odd number, but 19.05mm is the standard spacing between keyboard switches, and each switch typically only moves by 1/8th of a unit, which becomes 2.38125mm.

The simplest way to align this, would be to move the whole keyboard to allow for the bottom left key to be on the origin. This way, the middle of the key is on the origin, so it should be aligned to the grid now. Now, you can move it around by the standard 19.05mm units, so that it's no longer across the origin (since that's typically bad practice)

Creating the PCB:

Now we can open the board file. Before we do anything, we need to import the CAD files we created. Import the outline file we created as the board outline layer. Then, import the reference board as the into the reference layer.

Setup the design rules for the board while you're at it. Use the PCB manufacturer's capabilities, from the manufacturer you expect to order from (JLC PCB Capabilities example), and use their capabilities for your design rules. While you can ignore this, it is recommended to stop you from making any un-manufacturable errors.

Now, we need to design the PCB. Since this is a keyboard, the keyswitches need to be spaced specifically. Before you import any changes from the schematics, change the grid size to 19.05mm, and the alternate grid to 2.38125mm. While these seem odd, it's the standard spacing for keyboards. (Note, that Eagle automatically imports changes to the schematics, so you must cut and paste the schematics again to align the components to the grid)

Now, you need to arrange the keyswitches into their places. Unfortunately, they can only go in one place, and it can be quite tedious, but it needs to be done, otherwise it will be impossible to route, and would not work properly. This is why naming each switch is important in the previous step.

Once every switch and stabilizer is in place, you can change the grid size back to a normal amount. Usually for myself I do a 1mm grid, with a 0.1mm alt grid. Then, you can start routing.

Some tips for routing:

  • Try to keep one layer for horizontal routing, and one for vertical routing
  • Ensure that power traces are thick, and have ample room (polygon pours can be a good idea) - more information can be found here

Don't forget to do a DRC, or design rule check for the PCB before you export it, to make sure that it is a complete design, and is able to be manufactured. Once that's done, you can finally export your design into a gerber file.

Downloading The Sanctuary Manufacturing Files:

Unfortunately, instructables doesn't allow for .zip files to be uploaded. The manufacturing files for PCBs, known as gerbers, are in a zip format. So you'll have to head over to the Sanctuary Hardware Github repo, and navigate to the Gerber folder, and download "TheSanctuaryGerber2022.zip".

Ordering the PCB:

Now you can order the PCB. I personally order from JLC PCB, as they are one of the most affordable PCB manufacturers, however places like OSHPARK and PCBWay are other common manufacturing services.

For JLC PCB, upload the gerber as a zip file. Ensure that the online configurator hasn't thrown any errors, and that the PCB is rendered properly within the online tool. (Examples in last two Photos)

JLC PCB should automatically detect the size of the PCB, and most of the default settings should be fine. The only one you can change is if you want to have a different coloured board, in this case I went with black. Add to your cart, and complete the order.

Step 5: Hot Air Rework

This is the assembly that needs to be completed first. If you haven't done hot air rework before, don't worry! It's simpler than you think, and it is a valuable skill to use. Hot air can be tricky, however If you ordered your own PCB from a manufacturer like JLC PCB, then you have 5 boards to practice! If you buy enough supplies (which is cheap with the ESP32's), you should be able to learn the process, and complete one board!

For this purpose, we only need to SMD solder two things:

  1. The ESP32
  2. The USB C

Soldering the ESP32:

The ESP32 is easiest, so we'll start with that. The steps to solder it are as follows:

  1. Put solder paste the heatpad (large middle pad where the ESP32 will sit)
  2. Set the ESP32 in place
  3. Heat up the ESP32, ensure the heatpad has attached to the ESP32
  4. Ensure that the ESP32 is straight on the PCB (need to prevent shorts by accidental overlapping pins)
  5. If not, heat up, and desolder and resolder until the ESP32 is straight
  6. Solder paste the edge pins
  7. Heat up the pins, and solder pins together
  8. Inspect pins, ensure no shorts between the pins

And you're done! It's as simple as that. It might take a few tries to get it right, but it's definitely possible. You can even see on mine, it's not particularily clean, as I used a lot of flux on it (that needs to be cleaned, but I unfortunately didn't do it). I've gotten a lot better, but I had to do some repairs that made it so messy. Don't be afraid of the mess, as long as it works, it's okay!

Soldering the USB C:

The USB C is similar, but is a bit more annoying due to the finer pins. The steps I suggest are below:

  1. Put a small amount of solder paste on the pins
  2. Place the USB C in place (do NOT solder anything else)
  3. Apply heat to the USB C, wait for the solder to melt
  4. When done, carefully plug in a USB C cable to a pinout connector (if possible), and test for shorts. A USB Pinout connector with a multimeter will be better, to prevent damage to any USB port on your PC. Primarily, ensure that power and ground are NOT shorting. Inspect visually for shorts, or other issues
  5. If shorting, heat up the connector, and remove the port, then repeat steps 1 through 4
  6. Once the SMD pins are soldered, and not shorting, solder the USB C mounting tabs to the PCB for rigidity.
  7. Plug into power, (preferably not a computer initially, for safety) and test pins for power.

Step 6: Through Hole Soldering

Now that the SMD soldering is done, we can start to do the through hole soldering. This is significantly easier, however, there is an order needed to solder these.

These all must be installed/soldered before the switches:

  • Diodes
  • ensure direction - black stripe will be on the side of the arrow tip, see photos
  • Battery Charge Board Enable Pin jumper
  • See photos - on the pad labeled "key" beside the button
  • This can be done with the cutoff leg of a diode, or a jumper wire, and needs to be connected to the accompanying pin labeled "Key" on the Sanctuary PCB
  • This must be done before adding to the board
  • Resistors
  • 4x 4.7k Ohm in the area labeled as Pulldown Resistors
  • 2x 5.1k Ohm, in the daughter board above the rotary encoder (labeled)
  • Stabilizers
  • Screw in the stabilizers now, as you will be unable to add them later
  • Pin Headers (and battery board)
  • These are the 3 pairs for the daughter board (Labeled as 'Daughterboard Mount Point 1-3)
  • and the battery charger pin headers
  • Rotary Encoder
  • USB Port
  • Reset/Boot Pushbutton
  • Capacitor
  • Not required, however smoothes out when disconnecting from charge (rather than restarting every time a cable is disconnected) - I used a value of 1000uF, however sometimes it still restarts briefly when disconnecting the cable
  • JST Connectors on the PCB
  • Labeled as 'Bat D/C' and 'PushBTN' on the PCB

Next you can install the switches

  • Switches (ensure they are attached to the switch plate)
  • Ensure stabilizers are installed - after soldering switches you will be unable to install stabilizers
  • Attach and solder the switches corners first, to hold the switch plate in place, then you can add the rest of the switches, and start soldering them in place

Misc Soldering:

Next, we need to simply make a Battery disconnect switch, a pushbutton, and a jumper to the battery board.

To do this, on the JST cables, wire one side to a DPDT switch, as if it was a SPST switch. The switch is used as a battery disconnect, to turn off the keyboard.

Next, wire the JST cables to a pushbutton. This is the cable to enable the keyboard, to turn it on. Simply wire the cables on both sides of the pushbutton.

Lastly, we need to solder the 'key' pin on the battery charger board. This was mentioned above, however it is important because otherwise, the keyboard will not turn on without it. For this, you simply need to jump one wire from the 'key' pad on the board, to the 'bat key' hole on the PCB. This needs to be done before, or while soldering the battery daughterboard, otherwise it will be inaccessible to solder. See the photos if confused, as it will make much more sense.

Daughter-Boards:

Then, you can solder the daughter boards to the main board, these are:

  • The battery charger board
  • The 3.3V regulator board
  • The USB C and rotary encoder board

Battery Holders:

Now that everything is in place, we can solder the battery holders. Before we solder them, we should attach them to the board. That's what the battery holder mounts are for. Use a M3 countersunk screw, and carefully place a M3 nut inbetween the plate and the PCB. Then, screw through the battery holder, and PCB. Now that they're attached, arrange them so that they are in the same direction.

They are wired in parallel, so both the black cables can be routed to the pin labeled 'Bat GND' (technically any GND)

The red are the positive, and both need to be connected to the 'Bat V+'

Step 7: Designing the Case

All files are provided in the next step, so you can simply print them and skip this step. However, I will briefly go over how I designed the case. This heavily depends on your design, and since I built the hardware first, the case unfortunately adopted some obscure measurements.

Software Needed:

Next is the case for the keyboard. The case can be designed from scratch, using software such as Fusion 360, Autodesk Inventor, Solidworks, TinkerCAD, or even blender. It all depends on your personal preferences, however Fusion 360 is a great start for hobbiests.


Alternatively, you could design the keyboard for an existing case, the PCB would need to be designed specifically for a keyboard case. Note that this case needs to be a plastic, while many keyboard cases are metals such as aluminum they cannot be used as this will interfere with the ESP32 antenna.


Case Design:

Personally, this case was designed in Solidworks. I recommend designing the switch plate first, because you need to design the case around the PCB and plate. For this, I imported my plate we made in step 1, and added a 3mm border around each side, to create tabs. This will allow the case to hold down on these tabs in a sandwich style. I split this plate into two, right along the edge of the enter key, and navigation cluster. One the plates, I added a chamfer to make the plate print easier, and allow for the switches to pop in easier as well.

Next, I designed the top cases. For an example, I have provided images of the steps I used to create the Left Top Case. I started by using the 3mm boarder created, and extruded by 3.6mm that as the initial lip - the height of the plate to the keycaps. Then, I added a wall so that there would be an actual lip. This became a 8x12mm wall, alongside the initial lip. This way, it allowed for room for the threaded inserts.

Then, it was as simple as adding and extruding holes for the threaded inserts and magnets. I added some holes as well to fit a small piece of filament to line up the top cases together, however I found in the final design they weren't really needed.

Then, just clean up the design with some fillets and chamfers, and it becomes a completed piece!


The bottom case needed a different approach. For this, the main design needed for this, was the angle of the keyboard, and the cutouts needed for the daughter-boards and the batteries. Using the minimum angle needed to fit the batteries, a side profile was extruded. From this, the cutouts were made for the batteries, and daughter-boards according to the measurements. The size of the parts were designed in the opposite way of the upper case, so that the bottom left, and top right cases would attach to provide structure.

The right bottom side was designed similarly, with the same profile made, with cutouts made for cable management, and the daughter board. On the backside of the case, cutouts were made for the USB C, Battery Disconnect Switch, and ON Button.

Step 8: Printing the Case

The case files are provided below, and I have even provided variable files for optional features. You can find the STL files below in a zip file, you can pick from there. For the purposes of this guide, there are groups of files you need:

  • Keyswitch Plate (1, or 2 parts, depending on choice)
  • Left Bottom Case
  • Right Bottom Case
  • Left Top Case
  • Right Top Case
  • Colour Plate (If the right top case has arrows)

Note that these are primarily designed for a 300x300mm printer, so pieces like the Left Keyswitch Plate may not fit on smaller printers. The files also have been tuned for my printer, so you may need to tweak parameters to make parts fit better.

Step 9: Sub Assembly

This step is a relatively simple step, primarily the case needs to be assembled for the keyboard.

Heat-set Threaded Inserts

Now that all the case parts have been assembled, they need to be put together. I decided to go for threaded inserts for this build, as I like to have the ability to open up my devices and repair them. So, all you have to do to set these in place, is get your soldering iron, and lower it's temperature to about 250-300C. Then, place the threaded insert on the hole, and place your soldering iron on the insert, with a small amount of pressure. Wait a few seconds, and let it head up. Once it's heated up, that small amount of pressure should slowly push down on the insert, and just push until it is flush with the case. Repeat that for all 12 Threaded inserts.

Threaded insert locations:

  • 4 on the Top Left Case
  • 6 on the Top Right Case
  • 2 on the Bottom Left Case

Once the threaded inserts are added to the Bottom Left Case, take the two bottom halves, and screw together with the M3 countersunk screws, as seen in photo 4.

Magnets

If you have magnets, add them now too. There is one in each corner of the keyboard, as well as two over the arrow colour plate. The corners usually hold onto the magnets better (at least with my printer tolerances), however the two by the arrow plate may need to be glued in (see photo 5 and 6). Check polarity before placing, depending on how you want to use the magnets.

Assembling the Bottom Case:

This is a pretty simple step. With the pair of threaded inserts inserted to the bottom case, you can screw the case together. There are cutouts made in the right case to fit the screws and screwdriver, and can be seen in photo #4.

Now you can glue in the Battery disconnect switch, and the Pushbutton into the bottom case.

Plugging in Batteries:

Lastly you can add the Batteries to the Sanctuary. Be very careful of the orientation. Lithium batteries are very dangerous if handled improperly. So ensure that the wiring is correct, with the battery holders wired in parallel, and connected to the proper pads. Ensure that battery voltages are close to one another (within a few decimals of volts), lastly, ensure that the batteries will be inserted in the correct orientation (positive to positive, negative to GND) and add them to the battery holders.

Step 10: Programming

All the Code needed will be available here: https://github.com/LegoRocket/Sanctuary-Keyboard-Firmware

This is done so that I can improve upon the firmware without needing to update this instructable, or allow people to fork the repository to suit their own needs.

Setting up the Programming Environment:

With this project, the Arduino IDE was used. Install the Arduino IDE here. Note: use the original IDE, not version 2.

Next, we need to setup the ESPs in the Arduino IDE, since they are not supported initially. This can be done by adding this link: https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json to the additional boards manager in the Arduino IDE. Go into the Boards Manager (from Tools>Boards) and add the ESP boards. For the most up to date information on installation, visit here.

Now, we need to install the Bluetooth Keyboard Library. To do this, download the zip from the releases tab, and store it in your library location (ie Documents->Arduino->libraries). Then, in the Arduino IDE, go to sketch->Include Library->Add .ZIP Library, and navigate to the library we just downloaded. Open it, and it should be added to your sketch.

We also need to install the Rotary Encoder Library. This is called SimpleRotary by MPrograms, and is included in the Arduino Library manager, you just have to install it.

Another library needed, is FastLED, made by Daniel Garcia, this is another included in the Arduino Library manager, so you will need to install that as well.

Lastly, is the EEPROM library, this should have been installed when you setup the ESP programming environment.

Now that we have all the libraries needed, we just need to select the device. Select "FireBeetle-ESP32" from the Arduino Tools tab.

Uploading Code to ESP32:

Now that the controller board is done, we can start to program the board. To do this, we need to setup the Arduino IDE, and install software for the ESP32. Download and open the code from the linked github above. Configure or modify if needed.

When you're ready to upload to the Keyboard, use the USB-to-Serial adapter, and connect the RX, TX, and GND pins. Note that the RX connects to the TX, and TX connects to the RX (they are a crossover configuration). DO NOT CONNECT POWER. The batteries will provide power to the ESP32, we do not want to provide 2 power sources. These pins are all labeled on the PCB

Hold down the boot button, and toggle the switch to turn on to engage the batteries, and press the outside pushbutton to turn on the keyboard. This will put the ESP32 in the programming mode, and you can upload the firmware. Wait for it to complete, and release the boot button. Now, if you have LEDs, you should see them light up! If not, then check your Bluetooth settings, as you should have a functioning keyboard! Connect to it from your favourite device, and start typing!

If you've made your own code, be prepared to switch off the batteries often, as the keyboard may press keys on your device, and may not be able to release it.


Explaining the Code:

While the code is commented well, I figured I'd touch on the code here as well, and do a brief overview of the thought process behind the code.

Unfortunately, since this is a uncommon microcontroller in the keyboard world, the code had to be written from scratch. I don't mind this personally, as I find the QMK documentation to be confusing. So, while I try to explain it the best that I can, feel free to reach out and ask questions. I can attempt to clarify how the code works.

To keep the code organized, I've separated it out into 4 files. These files are "SanctuaryHardware.h", "Matrix.ino", "KeyboardFirmwareRev1.ino" and "_Main.ino"

"SanctuaryHardware.h"

Starting with the simplest, is the SanctuaryHardware.h header file. This is an easy way to change the keyboard parameters through definitions. This defines all the pins used, with a useful name, and sets up the rows and columns for the matrix. It also defines information for functions like the LEDs. (Note: The SK6812 LEDs operate with the same data structure as a WS2812 LED, therefore we specify that they are a WS2812)


"Matrix.ino"

This is another simple one to explain, this is simply the keyboard layout that is used for the keyboard. This allows you to define what each key does, and how the layers operate. Most of the time, this takes the form of the characters that the keyboard is going to press, however since we added more functions, sometimes this is hard coded.

If surrounded by ' ', then it is a character being sent. Modifiers are typically all capitals, and are a recognized definition of the keyboard library. However, some other numbers and all capital functions were added. These are checked before being sent, and if they're there, they perform special functions, such as volume control, or layer swapping.


"KeyboardFirmwareRev1.ino"

This file is to setup the functions and libraries used. It starts by including any library or header file needed for the firmware. Next, it sets up the libraries used by calling their initialization function, both for the keyboard and rotary encoder

Then, code used for the MAC address is there, some MAC addresses are added, and then a function to change the MAC address is implemented. This function primarily changes a number in the EEPROM, a number that is read on boot, to change the MAC address of the ESP32.

Lastly is a small function to change the current LED function. It simply uses a switch case to change the current implementation.


"_Main.ino"

This is primarily where most of the logic is programmed.

  • Setup()

This setup is a pretty common setup code, we initialize the row pins with a for loop as an output, and set a default state of LOW (or off).

Then, we initialize the columns with a for loop, and initialize them to be read. We want a pulldown configuration to set a default state of off to those inputs. Therefore, we set them to a INPUT_PULLDOWN. (Note: some pins do not have a pulldown option - we covered these in hardware back in step 2 - so they automatically configure as a regular input, although we said a pulldown)

After that, we have the FastLED library initialization. With the data defined in "SanctuaryHardware.h", we setup the LEDs to function.

Lastly, we setup the EEPROM to store, and pick a MAC Address from the storage on the ESP. This allows us to connect the keyboard to multiple devices, and "save" those devices. Then, we can start the keyboard.

  • Loop()

Now, this code primarily loops through the matrix, to find which key has been pressed. We check to make sure the keyboard is connected (save resources when not), and write to a row to start scanning. Since we initialized all rows to 0, we only have one row active at once.

Next, through a while loop, we check each column (input) pin, if it is pressed, we switch based on the layer information in "Matrix.ino". For most of these, it will default to pressing that key, however certain functions need to be treated differently, such as a media key, or extra functionality. Lastly, we record if the key has been pressed, so that the key is not pressed every loop.

If the key was not pressed, we check if it was held the last loop. If it was held last loop, and we notice a change in state (key released), then we want to release the key pressed, and signify that the key is not held anymore (PressedCheck). Then, we increment to check the next column, and check if it is pressed or not.

Then, we're done scanning that row, so we set the row to low (off), and increment the row counter, to check the next row the next loop.

After scanning the matrix, we scan the rotary encoder using the rotary library. For this, we change the behaviour based on the direction of the encoder, if it's rotated clockwise, we press volume up, and release. We want to release this time, because the code will not recognize when it has stopped rotating, only that it has rotated.

Lastly, using an ESP function, we refresh the LEDs every 25ms.

Step 11: Final Assembly

Now is the simplest step! Your board should be programmed and able to be used!

Now, you can assemble the final case. This is pretty easy, as there should be only 4 pieces now, the board assembly, the bottom case assembly, and the two top case pieces. Put the board assembly in the bottom case, and place the top cases on top. Now simply screw in the 12 bottom screws, to secure the board in place. The design should automatically be centered, so there it nothing else to lose

Now attach your keycaps, and you're done! You have your own Sanctuary Keyboard!

Step 12: Foot Notes and Q&A

Hopefully by now, you have your own version of the Sanctuary built! If you enjoyed this instructable, check out my YouTube Channel, or you can click here to find my other social media!


If you have any trouble, reach out to me! I can try to help you work through it, and if you bring something to my attention, I may update this instructable


Accompanying Firmware Github:

Accompanying Hardware Github:

Important Notes about the Project:

This section will talk about some downfalls of this project, and stuff that I'd love to see improved.

  • The reception of your keyboard heavily depends on your Bluetooth chipset

So what does this mean? Basically the reliability of your keyboard interacts with your device heavily depends on computer's bluetooth interface. I have tested on most of my devices, from Laptops to Phones. My laptop gets the best reception - it uses a built in AX1200 wireless chipset (Bluetooth 5.0) - and connects the best to the Sanctuary keyboard, it almost never needs to be adjusted.

My desktop however uses a USB Bluetooth adapter (Bluetooth 4.0), a cheap one from amazon, and needs to be reconnected on a weekly or so basis. It also has much less range, because the antenna of the USB Bluetooth is much smaller - I put up with it personally, however it is an important note to your device.

  • I haven't tested with Apple Devices

While I don't see why this won't work, I unfortunately don't have a Mac, iPhone, or iPad. The Bluetooth Keyboard library used notes that while these devices are supported, they may be unstable. While I don't see any issue why this keyboard wouldn't work, I personally can't vouch for it.

  • The Keyboard is Bluetooth Only

While I would love to have both, currently the Sanctuary Keyboard is a Bluetooth only device. This means that your device needs to have Bluetooth to receive keyboard inputs. Plugging in your keyboard to the PC will only charge the keyboard.

  • Lag Exists, but is Personally Not Noticeable

This heavily depends on the person, but personally, with a good Bluetooth chipset, I don't really notice any lag. Even with my USB Bluetooth adapter for my desktop, I am still able to play games on it, and use it while streaming fast paced games. This may vary depending on the person, depending on how well you notice lag, just be wary that this may be an issue.


Optional Reading: What was the Inspiration for the Keyboard?

This section is optional reading, however I figured I'd go in depth a bit more about the keyboard, primarily with my thinking behind the features and naming, as it might help reveal my thought process.

Primarily, the features included in this keyboard were a bit different from regular keyboards. These features were:

  1. Rotary Encoder
  2. Magnets
  3. Wireless
  4. Passthrough USB Port

Primarily, I designed this because I wanted a wireless mechanical keyboard. Most of the wireless mechanical keyboards are rare, and personally I like a different layout, as most mechanical keyboards are only a 60%. I also liked to have the rotary encoder, for media controls, as it makes it much easier to change the volume on my desktop.

The magnets were an inclusion to add extra functionality to the keyboard, as it's wireless, it opens itself to more portability. With portability, you want to prevent damage, or unintentional power-ons during transport, so magnets were added so that I could design a cover for it later. Unfortunately I haven't had time to design the cover yet, however if I ever got around to that, I could do it. These are what the magnets in the corner are for, however the top magnets by the arrows are for extra functions. One function I designed was a screen that will attach to the keyboard magnetically. This allows me to have a keyboard terminal, and use it as a general device for my server, and raspberry pi's.

This is where the name comes from as well, although the cover has not been made yet (not sure if I'll really get around to it, I don't have the space for it), I've already named it the Fortress. This way, when combined with the Sanctuary keyboard, it becomes the Sanctuary Fortress - a reference to one of my favourite games, Metroid Prime 2! Generally this area in the game was a large inspiration in general, guiding the arrow designs in the top, as well as the colour scheme of black, burgendy/red, and cyan.

Electronics Contest

Second Prize in the
Electronics Contest