OverView
This project was kindly sponsored by PCBWay.(https://www.pcbway.com/)
Originally, the plan was to create a left-hand device for FreeCAD. The design was to include three mechanical key swichies and one joystic. However, I made a mistake in the PCB design for the joystick, and in the end, device only include the mechanical key switches. As a result, I decied to abandon the idea of using it as a left-hand device for FreeCAD.
In any case, I would like to explain the functional concept behind the device I developed for this project.
I decided to incude the following features:
- USB Type-C connection
- A built-in piezoelectric speaker
- Three mechanical keys
- A grip-style form factor (desinged to be held in the hand)
Since the shape would become somewhat complex, I decided to divide the design into three separate PCBs: a joystick, and a key switch board.And, I decided to use the RP2040 as the microcontroller. Although it will be a wired device, the main reasons are that it is easy to implement HID functionality and it is inexpensive. The assembly difficulty is relatively.
Circuit Design
The schematic is shown below.
The circuit for the RP2040 section is below. The flash memory used is the W25Q16JVSSIQ.Special care must be taken with the trace length between the RP2040 and the flash memory. I made a mistake with this in the past.
Since this device operates as a USB Type-C sink, the CC pins are pulled down with 5.1 kΩ resistors.
The circuit below is the control sectiom for the piezoelectric speaker. It is connected to the RP2040's GPIO through an NMOS.
The circuit below is for the key swiches. It is connected to the main contorl section via a flat cable.
The circuit below is for the joystick.
PCB Design
To make it small enough to fit comforably in the hand, I needed to reduce the width of the PCB. Routing the traces turned out to be much more challenging than I had expected.
This board is for the joystick. (In fact, the joystick pad size was too small, and I wasn't able to solder it properly. It looks like I will need to redesign this part.)
This board is for the key switches.
In the end, since I also planned to design the enclosure, I created a detailed 3D model in KiCad.
PCB fabrication
The PCBs were manufactured with sponsorship from PCBWay. Thank you very much.
I use KiCad for my designs. I'm not familiar with other software, but there is a PCBWay plugin available for KiCad. By installing it, you can easily generate the Gerber files for PCBWay.
Launch KiCad and click "Plugin and Content Manager".
Type "PCBWay" into the search bar and instal the plagin.
The plugin appear in the toolbar of the PCB Editor.
When you click it, the PCBway website automatically open and the Gerber files be uploaded.
hand-solder components
The printed circuit boards have arricved.
Placing the RP2040 is extremely difficult in terms of alignment. Therfore, I designed an alignment jig and printed it using 3D printer.
After checking the alignment with a loupe and confirming that the position is correct, I solder it using a soldering iron.
After positioning it correctly, I proceed with soldering. The key at this stage is to apply plenty of flux.
The RP2040 has a GND pad on the underside, so it cannot normally be soldered with a starndard soldering iron. Therefore, as indicated by the red arrow, I added a hole in the center of the PCB and applied solder from the back side connect it.
Since components are mounted on both sides of the board, I made a jig with 3D printer to create a flat surface. This made the soldering process much easier.
I have finished mounting the minimum required components to run the RP2040, so I now perform a functionality test. Actually, threre was an issue with GND trace layout, and some parts were not properly connected, so I added jumper wires to fix it.
Now, I connect it to the PC. It was successfully recognized, and the folder opened as expected. If it is not recognized at this stage, I need to redo the soldering of the RP2040 or USB Type-C receptacle.
I proceed to mount all the remaining componets as well.
For the key switch section, I used Kailh swich sockets. These are sockets that allow the mechanical key switches to be replaceable.
I used red switches for the key switches.
I connect the key switch board to the main unit using a flat cable and performed a functionality test. It appears to be functioning correctly.
The program below was used for testing.
#include <Arduino.h>
#include <Mouse.h>
#include <Keyboard.h>
// put function declarations here:
int myFunction(int, int);
int key_swich[3] = {0,1,2};
int skr[5] = {4,5,6,7,9};
int buzzer = 3;
int tmp_val[8];
int input_val[8];
int count = 0;
void setup() {
pinMode(key_swich[0],INPUT);
pinMode(key_swich[1],INPUT);
pinMode(key_swich[2],INPUT);
pinMode(skr[0],INPUT);
pinMode(skr[1],INPUT);
pinMode(skr[2],INPUT);
pinMode(skr[3],INPUT);
pinMode(skr[4],INPUT);
pinMode(buzzer,OUTPUT);
Keyboard.begin();
Serial.begin(9600);
void loop() {
tmp_val[0] = digitalRead(key_swich[0]);
tmp_val[1] = digitalRead(key_swich[1]);
tmp_val[2] = digitalRead(key_swich[2]);
if(tmp_val[2] == LOW) {
int tmp=0;
//Serial.print(count);
if(tmp_val[1] == LOW)
{
delay(20);
while(digitalRead(key_swich[0]) == HIGH)
{
}
input_val[count] = 1;
count++;
Serial.println(1);
analogWrite(buzzer,100);
}
else if(tmp_val[0] == LOW)
{
while(tmp_val[0] == LOW)
{
}
input_val[count] = 0;
count++;
Serial.println(0);
analogWrite(buzzer,100);
delay(50);
}
analogWrite(buzzer,0);
if(count > 8)
{
uint8_t result = 0;
for (int i = 0; i < 8; i++) {
result = (result << 1) | input_val[7 - i];
}
analogWrite(buzzer,100);
delay(1000);
analogWrite(buzzer,0);
delay(1000);
analogWrite(buzzer,100);
delay(100);
analogWrite(buzzer,0);
Serial.println("count 8");
Serial.println(result);
Keyboard.press(result);
delay(100);
Keyboard.release(result);
count = 0;
}
}
else
{
count = 0;
for(int i=0; i<8; i++) {
input_val[i] = 0;
}
}
delay(1);
}Unfortunately, the joystick boad did not respond. The reason was that I made the pads for mounting the joystick too small, which made it difficult to solder properly.
3D-printed enclosure design
The enclosure is desinged in FreeCAD and printed with a 3D printer. First, I printed the internal frame to hold the PCB in place. The 3D printer used is the Bambu Lab X1C.
The outer case was printed in TPU to imporve the tactile feel. It was printed using a K1 that I modified specifically for TPU printing.
While the "Function Key" is held down, pressing the "1" and "0" buttons records an 8-bit binary value. Once the Function Key is released, the coressponding ASCII character is transmitted to the PC.
For example. suppose you want to type "HELLO WORLD" using this device. The ASCII codes in binary are as follows.
H:01001000
E: 01000101
L:01001100
L:01001100
"SPACE":00100000
W:01010111
O:01001111
R:01010010
L:01001100
D:01000100
The program code is shown below.
#include <Arduino.h>
#include <Mouse.h>
#include <Keyboard.h>
// put function declarations here:
int myFunction(int, int);
int key_swich[3] = {0,1,2};
int skr[5] = {4,5,6,7,9};
int buzzer = 3;
int tmp_val[8];
int input_val[8];
int count = 0;
void setup() {
pinMode(key_swich[0],INPUT);
pinMode(key_swich[1],INPUT);
pinMode(key_swich[2],INPUT);
pinMode(skr[0],INPUT);
pinMode(skr[1],INPUT);
pinMode(skr[2],INPUT);
pinMode(skr[3],INPUT);
pinMode(skr[4],INPUT);
pinMode(buzzer,OUTPUT);
Keyboard.begin();
Serial.begin(9600);
}
void loop() {
// ★ ここで毎回ちゃんと読んでいるか確認
tmp_val[0] = digitalRead(key_swich[0]);
tmp_val[1] = digitalRead(key_swich[1]);
tmp_val[2] = digitalRead(key_swich[2]);
//Serial.printf("1:%d 2:%d 3:%d \n",tmp_val[0],tmp_val[1],tmp_val[2]);
// 毎回状態を表示
if(tmp_val[2] == LOW){
if (tmp_val[1] == LOW) {
delay(20); // 簡易チャタリング対策
tmp_val[1] = digitalRead(key_swich[1]); // ← これ絶対必要!
if (tmp_val[1] == LOW) { // まだ押されてるか再確認
while (tmp_val[1] == LOW) {
// ここで止まっているはず
tmp_val[1] = digitalRead(key_swich[1]); // ← これ超重要!
//Serial.print("@"); // 動いている証拠(ドットがたくさん出る)
delay(50); // シリアルを詰まらせない程度に
}
input_val[count] = 1;
count++;
Serial.println(1);
analogWrite(buzzer, 100);
delay(30);
analogWrite(buzzer, 0);
}
}
else if(tmp_val[0] == LOW)
{
delay(20); // 簡易チャタリング対策
tmp_val[0] = digitalRead(key_swich[0]); // ← これ絶対必要!
if (tmp_val[2] == LOW) { // まだ押されてるか再確認
while (tmp_val[0] == LOW) {
// ここで止まっているはず
tmp_val[0] = digitalRead(key_swich[0]); // ← これ超重要!
//Serial.print("."); // 動いている証拠(ドットがたくさん出る)
delay(50); // シリアルを詰まらせない程度に
}
input_val[count] = 0;
count++;
Serial.println(0);
analogWrite(buzzer, 100);
delay(30);
analogWrite(buzzer, 0);
}
}
if(count > 7)
{
uint8_t result = 0;
for (int i = 0; i < 8; i++) {
result = (result << 1) | input_val[i];
}
analogWrite(buzzer,100);
delay(100);
analogWrite(buzzer,0);
delay(100);
analogWrite(buzzer,100);
delay(100);
analogWrite(buzzer,0);
Serial.println("count 8");
Serial.println(result);
Keyboard.press(result);
delay(100);
Keyboard.release(result);
count = 0;
}
}
else
{
count = 0;
for(int i=0; i<8; i++) {
input_val[i] = 0;
}
}
delay(10); // loop全体の速度調整(なくてもOK)
}






Comments