ESP32 Bluetooth Mechanical Keyboard - the Sanctuary

50K31942

Intro: ESP32 Bluetooth Mechanical Keyboard - the Sanctuary

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.

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.

31 Comments

Olá estou querendo fazer algo parecido com seu teclado. no entanto restringindo a apenas 22 teclas para o teclado numero. gostaria de saber se poderia me auxiliar quanto ao codigo, pois nao estou usando somente a placa do ESP32, tenho aqui o esp32 devkit v1 38 pin. Como faço para compilar todos os codigos de uma vez via usb.
Pois ao tentar subir o cod aparece este erro

Hello I'm wanting to do something similar with your keyboard. however restricting it to just 22 keys for the number pad. I would like to know if you could help me with the code, because I am not using only the ESP32 board, I have here the esp32 devkit v1 38 pin. How do I compile all codes at once via usb.

Because when trying to upload the cod this error appears

error: 'Rows' não foi declarado neste escopo pinMode(
Rows[i],OUTPUT);
alternativa sugerida: 'Row2' pinMode(
Rows[i],OUTPUT);
^~~~
Row2
erro: 'Cols' não foi declarado neste escopo pinMode(Cols[i],
INPUT_PULLDOWN );
^~~~
obs: alternativa sugerida: 'Col3'
pinMode(Cols[i],INPUT_PULLDOWN);
^~~~
Col3
erro: 'Rows' não foi declarado neste escopo
digitalWrite(Rows[RowCnt],HIGH);
^~~~
note: alternativa sugerida: 'Row2' digitalWrite(
Rows[RowCnt],HIGH);
^~~~
error: 'Cols'
if(digitalRead( Cols[ColCnt] ) == HIGH && PressedCheck[LayerCnt][RowCnt][ColCnt] == OFF) ^
nota: alternativa sugerida: 'Col3'
if(digitalRead( Cols[ColCnt] ) == HIGH && PressedCheck[LayerCnt][RowCnt][ColCnt] == OFF) ^
~~~
erro: 'PressedCheck' não foi declarado neste escopo if(digitalRead( Cols[ColCnt] ) == HIGH
&& PressedCheck[LayerCnt ][RowCnt][ColCnt] == OFF)
^~~~~~~~~~~~
erro: 'Layer1' não foi declarado neste switch de escopo(Layer1[LayerCnt][RowCnt][ColCnt]
)
^~~~~~
obs: alternativa sugerida: 'LayerCnt' switch(Layer1
[LayerCnt][RowCnt][ ColCnt])
^~~~~~
error: 'Layer1' não foi declarado neste escopo
switch(Layer1 [LayerCnt][RowCnt][ColCnt])
^~~~~~
note: alternativa sugerida: 'LayerCnt' switch(Layer1[LayerCnt][RowCnt][
ColCnt])
^~~ ~~~
LayerCnt
exit status 1
Erro de compilação: 'Rows' não foi declarado neste escopo
Looking into the errors, it seems like it can't find variables that are in the other files, likely having trouble linking the files properly. It seems like it can't find variables that are defined in external files, make sure that all the files are present in the same folder when compiling. Alternatively, you could try to copy all the code into one file (just append it), if I remember correctly, the code would work in one file, but was split for ease of use
Certo, consegui juntar todos os codigos em um arquivo so e estou modificando para que possar ser utilizado em apenas 22 teclas, pois somente isso é o suficiente.
gostaria de saber se poderia disponibilizar o esquema elétrico do projeto para facilitar nas minhas modificações? Meu interesse será em fazer algo parecido com essas imagens.


Right, I managed to gather all the codes in a single file and I'm modifying it so that it can be used in just 22 keys, because that alone is enough.
I would like to know if you could provide the electrical schematic of the project to facilitate my modifications? My interest will be in doing something similar with these images.


Sorry for the late response, I've had a busy week, but here's a link to a photo of the schematics
https://drive.google.com/file/d/1dA8WFwAGj0sQj3juA...
but this here should be the schematics I have, it's not terribly organized, but it should hopefully help
Just a heads up, the pinheaders on the left are for the power daughterboards, they are pinned out to a separate battery and regulator circuits, the right pinheaders are for the daughterboard in the PCB
Good luck with your project!
Oi Foster, sem problemas, sua resposta veio no momento certo.
Seu esquema me ajudou bastante a desenvolver o meu aqui, e me deu ideias para modificações dele.
No entanto gostaria de tirar apenas mais uma duvida se possivel.
Como seria o codigo para mudança de padrões das led's, como a mudança das camadas do macro, pois queria q cada camada tivesse seu padrao?


Hi Foster, no problem, your reply came at just the right time.
Your schema helped me a lot to develop mine here, and gave me ideas for modifications to it.
However, I would like to take just one more doubt if possible.
How would the code be for changing the patterns of the led's, such as changing the layers of the macro, as I wanted each layer to have its pattern?

Hi!

First of all, I must say this is a very impessive project, from the hardware to the design itself. Lately, I've been thinking about building a custom wireless mechanical keyboard as well, but I've been wondering about how long the battery lasts. What I've read so far is that the ESP32 draws quite a hefty amount of power during BLE transmission. Could you tell me more about the battery life of your keyboard, and the capacity of the batteries?

Thanks in advance
Totally, the short story is it only lasts about a day of use, but there's quite a few reasons for that. The ESP32 power consumption is definitely part of that, but I also did quite a few things "wrong", for lack of a better term
One issue is the LEDs, addressable LEDs aren't power efficient, and having them on all the time just drains the battery. The code has no sleep function either, so it is basically "always" transmitting, when it really doesn't need to. The power path isn't efficient either, using linear (so only about 70% efficient), the batteries were actually salvaged from a battery bank, and claim to be "9 watt-hours" (they're just lithium 3.7v nominal batteries)
If you'd like to build a wireless keyboard, I'd recommend taking a look into the ZMK firmware, and microcontrollers like the Nice!Nano, the codebase of ZMK is much more efficient code, allowing for the keyboard to sleep, and the Nice!Nano both uses a more efficient nRF52840 wireless chip and more efficient power path to save battery life. Supposedly some users have been able to achieve multi-month battery lives, however it should be noted those keyboards don't have LEDs
This is so cool,If you use esp32 s2 or s3 and modify the program, can you support usb hard connections?

Hey sorry to ask a ton of questions but, would the serial connector used to program the board potentially work as a hardwired connection if I wrote the appropriate firmware?
I found with 'hot air' I just blew the components right off the board, but I was actually using a temperature controlled heat gun more designed for paint stripping :-(

I have success with soldering ESP and similar modules using just a soldering iron.

Push some solder paste into the center holes. Scrape off excess, so the holes are just filled.
Position the module with the aid of pins through holes added to the earth pads
see this project for an example
https://www.forward.com.au/pfod/BLE/LowPower/NanoR...

Solder one pin and check the alignment.
Solder another pin on the opposite side. Check for alignment.
Solder the rest of the pins. Use solder wick to wick out shorts between pins
Turn the board over and apply the soldering iron and extra solder to the thermal vias in the center of the module. Be careful with this and only apply enough heat to make the solder paste wick and slightly bubble as the flux comes out. Do one via at a time.

P.S. I find soldering small components is easier by putting a bit of solder paste on the pad at one end and then hold the component with tweezers and touch the soldering iron to the solder paste end to anchor the component. Then solder the other end with normal solder and then finish off the solder paste end with normal solder.
Yeah that's another option too! The annoying thing is you have to use a hot air rework station, not a heat gun, and you have to tune the settings pretty well for the hot air rework. It's gotta have the air fast enough so you won't burn anything, but slow enough that it won't blow everything off.

The soldering is a good option too! I've never tried to solder the heat pad that way, but if it works it works. I just know I'll be doing more hot air rework as I go on, so I figured this would be a perfect project to do that.
The hot air gun I was using was a temperature controlled BOSCH PHG 630 DCE, with temperatures adjustable from 50C to 630C.
I usually set it at about 320C The box actually has photo of desoldering a component from a PCB.

I have used it for desoldering SMD components by heating the underside of the PCB until the solder on the top side pads melts.
It has two air speeds, but even the slowest is a bit strong for 0805 size components.
Thank you for your help with a split-ergonomic keyboard. Take care, God bless, and be safe!
Wow! What a tutorial! I learned a lot but am not computer literate when dealing with building or combining parts, but it was totally interesting anyway. I do have one question---Is this possible with an ergonomic keyboard. I would like to find a tutorial for wireless ergonomic keyboard that can be split in half and connected to chair arms. I am disabled using my hands and wrists. If you can come up with an idea, I would be most appreciative. Take care, God bless, and be safe!
It's possible, but I don't think I'd recommend an ESP32 for it
There's a more robust wireless keyboard firmware that supports split keyboards, it's called ZMK and I believe it supports the Corne PCBs, which are a popular split keyboard PCB. (However ESP32s aren't supported)
An example of someone's wireless Corne with ZMK can be found here: https://github.com/jhelvy/wireless-corne
Hi I learned a lot about how keyboards work from skimming through the details. I suspect custom projects are expensive as they lack the volume required to distribute component & intellectual costs. Thank you for posting
Yup, they unfortunately are. There's things you can do to keep the cost down, like using premade regulators and chargers, and printing the case with 3d printers, but unfortunately when making a custom PCB, there's only so much you can do. They're not always cheap, but you learn a lot from making them!
Is there a way to get around the custom PCB? I'm thinking of making a couple of these since the processor comes in a 5 pack. Also am I going to have to change any hardware to get the wired connection working? It looks like the connection used to program the processor would work fine but, I don't know much hardware stuff (literally had to google PCB when I found this post)
The PCB isn't technically required, however it would be difficult to do with the ESP32 Wroom module. I'd likely suggest to use a dev board so that it's easier to solder, and has stuff like the reset pins built in (ex Adafruit Huzzah32 is a dev board)
Keyboards are often hand wired, so you can always hand wire it yourself, but you'd have to redesign the case, or add something like (nonconductive) foam, since I used the PCB for rigidity, as well as likely change the hardware to make it easier to solder as mentioned above.
I used a PCB for rigidity and for more pins on the ESP32 (dev boards don't always have all the pins accessible) but you can use something like an Adafruit Huzzah32 which combines stuff like the battery charging into one convenient board - but the issue is it just doesn't have the same number of pins (so you'd have to reduce the size of the keyboard, and maybe remove stuff like the rotary encoder)
Currently the keyboard won't connect to a computer via USB either. The USB Serial convertor is only used to program the keyboard, and it has to connect via bluetooth to type, so any device you want to connect to must have bluetooth. It would require additional hardware and code to get the wired connection working.
More Comments