Introduction: Osu! Keyboard
I recently started playing a rhythm game called osu! and after seeing a video of a commercial mini keyboard I thought it would be a fun project to design one myself. Not long after that I decided it would be a good idea to put it on instructables as my first project.
If you want to replicate this project exactly to the last instruction then be my guest, but some of the desicions I made are not based on lowest price or best quality. Some components are chosen almost purely because i had them lying around. If you can handle it I would encourege you to customize your project.
Note 1: SMD components (small electronics) are used so if you replicate this project soldering skills are required. maybe an easy to solder version will be added but these leds don't come in trough hole package
Note 2: I have updated the code multiple times and im up to version 3ish now. I'll leave all code online but I recommend you use the last version. It currently doesn't have led functionality but it should be the best performing one.
Step 1: Materials and Explanations
Depending on how you make your project you may need different components, but these components are te ones I used. If you have the time and want to save mony, order form aliexpress and don't order the PCB.
1 Arduino pro micro + USB cable
3 Kailh BOX red switches
3 10k resistor (0805 SMD)
3 100nF capacitor (0805 SMD)
4 APA102 rgb LED (5050 SMD)
1 Printed circuit board (PCB) provided in this project
1 3D printed case provided in this project
Why do I use an Arduino pro micro?
Most arduino boards like the Uno (Atmega328) don't have native support for USB communication. Yes you can program them over USB very easily and I think there are workarounds, but I like to keep it simple when it comes to USB communication and I don't know if the workarounds are as responsive. Those boards use an external chip to make USB communication possible whereas the Arduino pro micro (Atmega32U4) has it built in.
There are many mechanical switches you can use. Linear, tactile or clicky from Kailh or Cherry MX. Choose whichever you like. I used the Kailh switches because they were cheap on Ailexpress. If you choose to use the PCB you'll need Kailh BOX switches. The colour determines the feeling.
The electronic components
Not much to explain about them in this chapter, but if you dont use the PCB I would recomend just normal trough hole components for soldering ease. Unfortunately the leds used are not available in trough hole packages. I would also not recommend to use wires on SMD packages unless you are Very confident in your soldering skills. Even for SMD on a PCB "advanced" soldering skills are equired.
I do provide a housing in this project, but it is at this moment flawed. Modifications are needed to fit bolts, the openings for the leds are not optimal, the arduino is exposed and a part needs to be cut out for the USB to fit. In the future a new housing may be added. If you have a 3D printer go ahead and print it, but please don't go out of your way to print this flawed case if you dont and just use some kind of project box.
Step 2: The Schematic
The schematic for this project is rather simple, but I want to explain the components for the people who are interested and don't know this implementation.
Switch connections to the Arduino
The switches are connected to Arduino pins 0, 2 and 3 because those pins can be used as external interrupts. This is further explained in the code section.
The debounce circuit
On the left side of the schematic is a circuit that is copied 3 times. This circuit is used to debounce the switch. To know what debouncing is you need to understand switch bouncing and it's not hard to understand.
First look at this simulation to paint a first picture (click the switch fast and look at the signal below) http://tinyurl.com/yajdtndw
When you press or release a switch it bounces and your signal alternates between high and low a couple of times for a few milliseconds. An Arduino is really fast and reads every high and low in this short time. The program will send a key press or release every time a high or low is read so with every press your computer will receive multiple key presses. Not ideal for a rhythm game.
This debounce circuit will slow down the falling edge of the signal. The signal to the Arduino won't be able to change as fast as the bouncing occurs so it will be read as one press. Don't worry about it falling to slow for the next real press because it will.
The Atmaga32U4 reads a digital low at 0.2Vcc - 0.1V = 0.9 volt. The voltage of the capacitor at any time in its discharge is Vcc * e^(-t/RC). If you measure a different debounce time on your switch you can calculate your resistor and capacitor values.
formula form https://www.electronics-tutorials.ws/rc/rc_2.html
The rgb LEDs are APA102 leds which are individually addressable using a clock and data line. No external components are needed to make them work. For many LEDs you should use a capacitor parallel to 5 volt and ground but with just 4 LEDs you don't need it.
Step 3: The Board Design
The PCB was designed in JLCPCB. I'm not sponsored by them but for cheap prototypes they make excelent PCB's. For 2 dollar you get 10 of the same board, but shipping was about 11 dollar for me. If you don't nessecarily want rgb lighting and plan on making just one, you should consider making your keyboard without PCB.
The design of the board was pretty straight forward. I only needed to add a component for the switches, but after watching some video's I got the hang of it. The only thing flaw I realised is the placement of the holes is a bit too close to the switches.
To order the PCB go to https://jlcpcb.com/ and choose the 2 layered option. It will ask you for a Gerber file. download the ".zip" file and drag it to the window. You don't need to unzip it. The settings should be fine and you can go ahead and complete the order.
Step 4: Case Design and Assembly Tips
As mensioned before, my design is flawed but you can still print it if you want. the design was made in Fusion 360. It's a free 3D modeling software and with my experience from inventor and solidworks it was pretty easy to work with. The circles on the corners of the case are to prevent peeling from the printbed.
If you make your own case only one thing is really important. Your switches need to be firmly placed and unable to move. I've provided pictures of the square cutouts with dimensions so you can use it for your own design assuming you use Kailh BOX switches.
Now you have all components needed to assemble. There is an order to assembling this first version because the switches are soldered.
1. Solder the SMD components. these are the resistors, capacitors and LEDs.
2. Solder the Arduino pro micro.
3. Place the 3 switches in the 3D printed coverplate before soldering. The coverplate can't be removed after soldering the switches. Desoldering the switches is not advised and may destroy them.
4. Now solder the switches in place. Do this as quickly as possible because the plastic switches can melt and ruin them or drastically decrease their number of clicks.
5. Place the assembled coverplate in the 3D printed case and secure with tape or use bolts if they dont interfere with the keycaps.
6. Place the keyCaps on the switches and you're done.
Desolder or mask the LEDs on the arduino after uploading your code. The leds are nice to have if your code doesn't upload but are not nice to look at as a finished product. Skill and pointed tweezers are required.
Also some grip feet on the bottom are nice for anti slip and let the rgb light shine through.
Step 5: The Code V1 (hardware Debounce)
The code for this project is not beginner friendly so if you are just beginning to program in arduino then this code will possibly scare you a bit. However i'll try to explain what's going on as best as i can. Some things are explained later on in this tex so if you have questions please first read the whole thing.
Uploading the code
First download all 3 ".ino" files and put them in one folder. If you don't have the Arduino IDE just download it for free on the official arduino site. https://www.arduino.cc/en/Main/Software
Connect your Arduino to your PC and open "OSU_Keyboard_code_V1.ino". In Tools --> Board select "Arduino/Genuino Micro". Also in Tools select the right COM port. This can sometimes change.To upload the code to your Arduino just click on the arrow on the top left of the screen and wait until it tells you it completed in the bottom left.
Including and defining
First you need to include the Keyboard library. This makes it possible to use the Arduino as a keyboard.
Next I define some values. Define is just like a variable but they can't change while the program is running.
The first 9 are for the keyboard character, arduino pin number and port bits.
Then the port bits of the LED data and clock.
Also the number of leds is defined and a variable for the angle of the color wheel.
This part of the code will only be executed once when the arduino is plugged in.
First the clock and data pins of the LEDs are set as outputs and the switch pins as inputs. This is the the advanced version of pinMode(). If you are interested search for "direct port manipulation". https://www.arduino.cc/en/Reference/PortManipulati...
Keyboard.begin() simply starts the usb connection as keyboard.
Next 3 interrupts are are linked to the switch pins. Every time a change is detected on the switch pin a tiny program will be executed. This little program will be made further on.
This part will continuously repeat while the arduino is powered.
I only use it to change and update the color of the LEDs.
Here the little programs, which will only be executed when a change is detected on the switch pins, are made. They are identical except for which pin they react to.
First it checks if the button is pressed or released and sends the correct keyboard command.
LED (explained in a different order)
If you are curious how the LEDs are controlled you should look at the APA102 datasheet. https://cdn-shop.adafruit.com/datasheets/APA102.pd...
This is again the direct port manipulation version of digital write.
First it checks if it should send a 0 or 1 and respectively pulls the data pin low or high. Then it writes the clock pin high very short and writes it low again.
This repeats oneBit 8 times with a "for" loop. It reads the first bit in a byte and passes its value to the oneBit function and does the same for the next 7 bits.
This repeats oneByte 4 times to provide the data needed for one led. The first byte starts with 111xxxxx and a 5 bit brightness value on the place of the xxxxx. The brightness can be set from 0 to 31 (2^5 = 32 levels) .
The next 3 bytes are for the blue, green and red values. One byte for each color.
This function calls ledData gives it the rgb colors depending on an angle in the color wheel.
The 16 bit value is dividend in 6 equally spaced sections of 60 degrees. Looking at the images might help you understand better.
(an 8 bit version is also provided but commented out because it's too flickery)
The start frame needs to be used every time you want to send new colors to the leds and want to update the actual color of the leds
I only use the start frame because the endframe is not needed. The start frame is 4 bytes of 0. The end frame is 4 bytes of 255 (11111111).
Step 6: The Code V2 (software Debounce With Timers)
After a while of playing I noticed some double tapping problems with the hardware debounce. This could be fixed with some other value resistors or capacitors, but as the buttons and lid are not removable I thought software debouncing would be a nice solution. The software debounce should work wether hardware debounce is implemented or not. In my current setup I coundn't remove the lid so i just left the resistors and capacitors in place.
I won't explain the code as extensively as the previous version because it is a little harder to explain.
Basically most of the code works the same and the led code is left untouched. what changed is the external interrupts don't use the arduino functions anymore. Now it works in pure C code. And now what's added is the software interrupt. For this i used the AVR timers to wait a certain amount of time until the bouncing has stopped. Because the timers are interrupt based the the decounce time is not influenced by anything happening in the loop.
The only downside I can come up with is that the arduino delay functions can not be used anymore. Because delay functions use Timer 0 and this program uses Timer 0 to debounce.
In the image you can see how the code roughly works. The mem bit indicates if a timer is running. What is not depicted is the case that at the end of the button press the input is low. In this case only a keypress would be send while the button is already released. Which means the key will be held down as far as the computer is concerned. For this rare exeption a check will be preformed when a timer expires. If at the end of a timer the button is not pressed, a keyrelease command will be send.
Step 7: The Code V3 (software Debounce With Vertical Counter) (recommended)(no LED)
This code ALSO has a version where you don't need pull down resistors. Make sure you connect each button to the input and GROUND! The build-in pull-up is used.
I also experienced some unregistered presses in the code V2. I think the code just became too complex with its timer- and external interrupt and I may have missed some exceptions. For this reason I sterted from scratch with searching on the internet for software debounce methods.
(honestly, at least half of this project has become button debouncing at this point)
After some searching I came across this post:
To be honest, it took me quite some time to fully understand how it exactly works. It involves some rather complex bit manipulations, but I'll try to make it as easy as possible. However my explanations will only be an addition to the post so you should at least read the "vertical counters", "an annotated implementation" and "reducing latency".
The timing diagram (made in WaveDrom) I added should make this hard to understand bit math at least a little more understandable. Note the image has 2 counter bits, but my code has 3. This means a longer debounce time.
One bit per value
With the vertical counter implementation it is possible to debounce multiple buttons at the same time, in parallel. All values are of type Byte( uint8_t) and consists of 8 bits. we are not concerned what value any of these byte contain, but rather we are interested in the bits on their own. Every buton to be debounced only uses one bit of each byte. The first button uses only the first bit of each byte, the second button uses the second bit etc.
All at the same time
By using bit math it is possible to execute these pin debounces in parallel. And, although bit math is pretty complicated, it is very efficient for the processor.
With an 8 bit data type it is thus possible to do this for 8 buttons. Using larger datatypes allows for more debounces at once.
The debounce routine is executed every 1 millisecond with a timer interrupt.
when the button is pressed the buttonState, which is the debounced state, will emedeately go low, indicating a button press. To detect a release the button has to be high for long enough, indicating it hasn't bounced for a certain time. Toggle is used to indicate a button change. The counter bits are used for .... counting how long there hasn't been a bounce.
Delta indicates a difference between the input and the debounced state. Only when there is a difference the counter will count. the counter will be reset when a bounce is detected (delta is 0).
Step 8: The Result
If everything went well you should now have a working keyboard to play Osu! on. I personally havn't noticed any latency at all. If you do please let me know. Also if there are any questions feel free to ask anything.
The previous mentions about a V2 are not meant as a promise so don't postpone this project because you want to wait for V2.
I hope you enjoy your keyboard!
Osu! name: Thomazzz3
If you think you are having troubles with your keyboard, first open a text editor and press each key once for a short time.
- Does one or multiple keys not work?
It is possible you destroyed a switch internally while soldering. If you have a multimeter put it on continuity/beeping, put it parallel to the switch while the Arduino is not connected and press the key. It should beep.
- Do the characters you just typed match the keys you configured in Osu! ?
Change the characters in the arduino code in the first 3 #Defines ( ' ' is nessecary!).
Or change your Osu! settings to use the configured keys.
- Are one or multiple keys repeated a few times?
The debounce circuit propably doesn't work for your switches or is not correctly soldered. Check your solder connections. If it still occurs try a capacitor value of 1uF. This will be very hard for the PCB users.
If you are having troubles with your LEDs
- Do the LEDs flicker?
A solder connection may be loose. If you use the PCB confirm the soldering tin realy flowed on the pad on the print.
- Do none of the leds work or from a certain number of LEDs stops working?
Check for shorts between the conections of the first LED (follow tracks) and check for well connected tin on the outputs of the Arduino and again the first LED. If confirmed correct and still defect you may need to replace the first LED.
If this fixes it repeat for the next LEDs if needed.
Participated in the
Game Life Contest