Add mozc-caps.
@@ -25,6 +25,8 @@ Please refer to the README in each directory for more details.
|
|||||||
* /mozc-yunomi for firmware, schematics, and PCB layout.
|
* /mozc-yunomi for firmware, schematics, and PCB layout.
|
||||||
* [Gboard bar version](https://g.co/____), [blog post](https://japan.googleblog.com/2022/10/gboard-2022.html) (Oct 1, 2022)
|
* [Gboard bar version](https://g.co/____), [blog post](https://japan.googleblog.com/2022/10/gboard-2022.html) (Oct 1, 2022)
|
||||||
* /mozc-bar for firmware, schematics, and PCB layout.
|
* /mozc-bar for firmware, schematics, and PCB layout.
|
||||||
|
* [Gboard CAPS version](https://g.co/CAPS), [blog post](https://japan.googleblog.com/2023/10/caps.html) (Oct 1, 2023)
|
||||||
|
* /mozc-caps for firmware and hardware design.
|
||||||
|
|
||||||
|
|
||||||
# Background stories (in Japanese)
|
# Background stories (in Japanese)
|
||||||
|
|||||||
161
mozc-caps/README.md
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
# Gboard CAPS version
|
||||||
|
|
||||||
|
This directory contains the firmware and hardware design for Gboard CAPS
|
||||||
|
version, which was released on **Oct 1, 2023**.
|
||||||
|
|
||||||
|
This is not an officially supported Google product.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Contents
|
||||||
|
|
||||||
|
The directory structure is as follows:
|
||||||
|
|
||||||
|
- hardware/ : STL files for mechanical parts.
|
||||||
|
- firmware/ : Arduino sketch.
|
||||||
|
|
||||||
|
## Building Gboard CAPS Version
|
||||||
|
|
||||||
|
### Parts
|
||||||
|
|
||||||
|
- M5StickC Plus
|
||||||
|
- SS-10GL13 micro switch
|
||||||
|
- [INC-100 bump cap insert](https://www.midori-helmet.jp/pickup/inc-100/)
|
||||||
|
- [Grove connector cable](https://akizukidenshi.com/catalog/g/gC-16938/)
|
||||||
|
- M3x10mm screw x 8
|
||||||
|
- M2x8mm screw x 2
|
||||||
|
- rubber band x 4
|
||||||
|
- hot glue
|
||||||
|
- heat shrink tubing
|
||||||
|
- 160mm x 160mm x T2.5mm MDF board (or substitute a 3D printed part; see next
|
||||||
|
section)
|
||||||
|
|
||||||
|
### Hardware
|
||||||
|
|
||||||
|
#### Step 1: Prepare required parts
|
||||||
|
|
||||||
|
3D printed parts:
|
||||||
|
|
||||||
|
- [bump cap insert mount](./hardware/bump_cap_insert_mount.stl) x 2
|
||||||
|
\
|
||||||
|

|
||||||
|
- [sliding plate](./hardware/sliding_plate.stl) x 2
|
||||||
|
\
|
||||||
|

|
||||||
|
- [top plate angle bracket mount](./hardware/top_plate_angle_bracket_mount.stl)
|
||||||
|
x 2
|
||||||
|
\
|
||||||
|

|
||||||
|
- [switch mount](./hardware/switch_mount.stl) x 1
|
||||||
|
\
|
||||||
|

|
||||||
|
|
||||||
|
Other parts:
|
||||||
|
|
||||||
|
- top plate: made of 160mm x 160mm x T2.5mm MDF board
|
||||||
|
 \
|
||||||
|
Alternatively, you can 3D print this
|
||||||
|
part ([top_plate.stl](./hardware/top_plate.stl))
|
||||||
|
|
||||||
|
#### Step 2: Wiring
|
||||||
|
|
||||||
|
Connect the NO and the C terminals of the micro switch to the black and yellow
|
||||||
|
wires of the Grove cable, in any order. (These will be connected to port G33 and
|
||||||
|
GND of M5StickC Plus.)
|
||||||
|
\
|
||||||
|

|
||||||
|
|
||||||
|
#### Step 3: Build
|
||||||
|
|
||||||
|
1. Put 2 rubber bands across the two latches on the sliding plates.
|
||||||
|
\
|
||||||
|

|
||||||
|
|
||||||
|
1. Attach one of the top plate angle bracket mounts to one of the sliding plates
|
||||||
|
and fasten using M3 screws. Repeat for the other set.
|
||||||
|
\
|
||||||
|

|
||||||
|
|
||||||
|
1. Fasten the angle bracket mount and sliding plate assemblies to the top plate
|
||||||
|
using M3 screws \
|
||||||
|

|
||||||
|
\
|
||||||
|

|
||||||
|
|
||||||
|
1. Screw the micro switch to the switch mount.
|
||||||
|
\
|
||||||
|

|
||||||
|
|
||||||
|
1. Glue the switch mount to one of the sliding plates. Check that the switch can
|
||||||
|
be pressed by testing using one of the bump cap insert mounts.
|
||||||
|
\
|
||||||
|

|
||||||
|
|
||||||
|
1. Attach the mounts to the left and right side of the cap, and adhere using hot
|
||||||
|
glue. \
|
||||||
|

|
||||||
|
|
||||||
|
1. Combine the two parts by attaching the sliding plates to the rails on the
|
||||||
|
bump cap insert mounts.
|
||||||
|
|
||||||
|
1. Attach M5StickC Plus and connect the other side of the Grove connector that
|
||||||
|
is attached to the micro switch.
|
||||||
|
|
||||||
|
### Firmware
|
||||||
|
|
||||||
|
#### Step 1: Environment setup
|
||||||
|
|
||||||
|
- Arduino IDE
|
||||||
|
- Libraries
|
||||||
|
- [Madgwick library](https://github.com/arduino-libraries/MadgwickAHRS)
|
||||||
|
- You can install from Arduino IDE's Library Manager.
|
||||||
|
- [ESP32 BLE Keyboard library](https://github.com/T-vK/ESP32-BLE-Keyboard)
|
||||||
|
- You can download zip file from github and install from Sketch -> Include
|
||||||
|
Library -> Add .ZIP Library menu in Arduino IDE.
|
||||||
|
- [IMU Library](https://github.com/yamaguchi-am/m5stack-examples)
|
||||||
|
- Download
|
||||||
|
[imu.h](https://github.com/yamaguchi-am/m5stack-examples/blob/main/imu/imu.h)
|
||||||
|
and
|
||||||
|
[imu.cpp](https://github.com/yamaguchi-am/m5stack-examples/blob/main/imu/imu.cpp)
|
||||||
|
from this repository and put them into firmware/ directory.
|
||||||
|
|
||||||
|
#### Step 2: Compile and upload
|
||||||
|
|
||||||
|
- Compile and upload the mozc-caps.ino firmware to the M5StickC Plus using
|
||||||
|
Arduino IDE.
|
||||||
|
|
||||||
|
#### Step 3: Calibration
|
||||||
|
|
||||||
|
- IMU (gyro sensor) calibration
|
||||||
|
- After powering on the M5StickC Plus, the firmware will run calibration for
|
||||||
|
the IMU.
|
||||||
|
- While the display shows "calibrating" message, keep the device still.
|
||||||
|
- You can also trigger the calibration process by long-pressing Button A.
|
||||||
|
- Setting the origin direction
|
||||||
|
- When Button A is pressed, the origin direction is set to the current
|
||||||
|
orientation of the device.
|
||||||
|
|
||||||
|
#### Step 4: Connect
|
||||||
|
|
||||||
|
Connect the keyboard via Bluetooth to a PC or other device that has Google
|
||||||
|
Japanese Input/Gboard installed.
|
||||||
|
|
||||||
|
Note: This keyboard keeps sending keycodes even before you press the key. It is
|
||||||
|
for showing a preview of the selected character to be input on the connected
|
||||||
|
device.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
```
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
not use this file except in compliance with the License. You may obtain
|
||||||
|
a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
```
|
||||||
5
mozc-caps/firmware/mozc-caps/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
imu.cpp
|
||||||
|
imu.h
|
||||||
|
debug.cfg
|
||||||
|
debug_custom.json
|
||||||
|
esp32.svd
|
||||||
172
mozc-caps/firmware/mozc-caps/mozc-caps.ino
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
#include <BleKeyboard.h>
|
||||||
|
#include <M5StickCPlus.h>
|
||||||
|
|
||||||
|
#include "imu.h"
|
||||||
|
|
||||||
|
// Comment out the line below to use English alphabet mode.
|
||||||
|
#define JAPANESE_
|
||||||
|
|
||||||
|
#ifdef JAPANESE_
|
||||||
|
// Japanese (hiragana) mode. Must be used with romaji input mode.
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
const char* characters[] = {
|
||||||
|
"a", "i", "u", "e", "o",
|
||||||
|
"ka", "ki", "ku", "ke", "ko",
|
||||||
|
"ga", "gi", "gu", "ge", "go",
|
||||||
|
"sa", "si", "su", "se", "so",
|
||||||
|
"za", "zi", "zu", "ze", "zo",
|
||||||
|
"ta", "ti", "tu", "te", "to",
|
||||||
|
"da", "di", "du", "de", "do",
|
||||||
|
"na", "ni", "nu", "ne", "no",
|
||||||
|
"ha", "hi", "hu", "he", "ho",
|
||||||
|
"ba", "bi", "bu", "be", "bo",
|
||||||
|
"pa", "pi", "pu", "pe", "po",
|
||||||
|
"ma", "mi", "mu", "me", "mo",
|
||||||
|
"ya", "yu", "yo",
|
||||||
|
"ra", "ri", "ru", "re", "ro",
|
||||||
|
"wa", "wo", "nn",
|
||||||
|
"lya", "lyu", "lyo", "ltu",
|
||||||
|
"-",
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// English alphabet mode.
|
||||||
|
|
||||||
|
const char* characters[] = {"a", "b", "c", "d", "e", "f", "g", "h", "i",
|
||||||
|
"j", "k", "l", "m", "n", "o", "p", "q", "r",
|
||||||
|
"s", "t", "u", "v", "w", "x", "y", "z", " "};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// A class that handles character input, interactively showing
|
||||||
|
// unsettled characater on the target device.
|
||||||
|
class Compositor {
|
||||||
|
public:
|
||||||
|
Compositor() : last_character_unsettled_(false) {}
|
||||||
|
// TODO: add commit() which commits the Japanese composition by Enter key.
|
||||||
|
void enter();
|
||||||
|
void select(const char* str);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool last_character_unsettled_;
|
||||||
|
};
|
||||||
|
|
||||||
|
const float kSamplingFrequency = 100.;
|
||||||
|
const float kIntervalMicros = 1e6 / kSamplingFrequency;
|
||||||
|
const uint8_t PUSH_SW = G33;
|
||||||
|
|
||||||
|
BleKeyboard bleKeyboard("mozc-caps");
|
||||||
|
M5StackIMUManager imu;
|
||||||
|
Compositor compositor;
|
||||||
|
long last_time_micros;
|
||||||
|
float yaw_origin = 180;
|
||||||
|
int current_key = 0;
|
||||||
|
int last_key = 0;
|
||||||
|
|
||||||
|
void Compositor::enter() {
|
||||||
|
if (!last_character_unsettled_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
last_character_unsettled_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Compositor::select(const char* str) {
|
||||||
|
if (last_character_unsettled_) {
|
||||||
|
// It is assumed there is only one unsettled character,
|
||||||
|
// as the IME should be set to the romaji input mode.
|
||||||
|
bleKeyboard.write('\b');
|
||||||
|
}
|
||||||
|
for (const char* p = str; *p; p++) {
|
||||||
|
bleKeyboard.write(*p);
|
||||||
|
}
|
||||||
|
last_character_unsettled_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
M5.begin();
|
||||||
|
M5.Lcd.setRotation(3);
|
||||||
|
M5.Lcd.fillScreen(BLACK);
|
||||||
|
imu.Init();
|
||||||
|
last_time_micros = micros();
|
||||||
|
pinMode(PUSH_SW, INPUT_PULLUP);
|
||||||
|
bleKeyboard.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
float gyro_x, gyro_y, gyro_z, acc_x, acc_y, acc_z;
|
||||||
|
M5.IMU.getGyroData(&gyro_x, &gyro_y, &gyro_z);
|
||||||
|
M5.IMU.getAccelData(&acc_x, &acc_y, &acc_z);
|
||||||
|
// Convert coordinate system to use the rotation around the Y axis as "yaw"
|
||||||
|
// angle.
|
||||||
|
imu.Update(kSamplingFrequency, gyro_z, gyro_y, -gyro_x, acc_z, acc_y, -acc_x);
|
||||||
|
float pitch, roll, yaw;
|
||||||
|
imu.GetAHRSData(&pitch, &roll, &yaw);
|
||||||
|
|
||||||
|
M5.update();
|
||||||
|
if (M5.BtnA.wasPressed()) {
|
||||||
|
yaw_origin = yaw;
|
||||||
|
}
|
||||||
|
if (M5.BtnA.pressedFor(1000)) {
|
||||||
|
imu.Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
float relative_angle = yaw - yaw_origin;
|
||||||
|
if (relative_angle > 180) {
|
||||||
|
relative_angle -= 360;
|
||||||
|
} else if (relative_angle < -180) {
|
||||||
|
relative_angle += 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool key_pressed = false;
|
||||||
|
if (digitalRead(PUSH_SW) == LOW) {
|
||||||
|
key_pressed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The range of rotation angle [deg] from the center, used for selecting
|
||||||
|
// characters.
|
||||||
|
const float kAngleRange = 80;
|
||||||
|
int num_keys = sizeof(characters) / sizeof(characters[0]);
|
||||||
|
float position = (relative_angle + kAngleRange) / kAngleRange / 2 * num_keys;
|
||||||
|
int key_index = static_cast<int>(position + 0.5);
|
||||||
|
if (key_index < 0) key_index = 0;
|
||||||
|
if (key_index > num_keys - 1) key_index = num_keys - 1;
|
||||||
|
float residual = position - key_index;
|
||||||
|
if (residual < -0.5) residual += 1;
|
||||||
|
if (residual > 0.5) residual -= 1;
|
||||||
|
if (residual < 0) {
|
||||||
|
residual = -residual;
|
||||||
|
}
|
||||||
|
if (residual < 0.4 && !key_pressed) {
|
||||||
|
current_key = key_index;
|
||||||
|
if (current_key != last_key) {
|
||||||
|
compositor.select(characters[current_key]);
|
||||||
|
}
|
||||||
|
last_key = current_key;
|
||||||
|
}
|
||||||
|
if (key_pressed) {
|
||||||
|
compositor.enter();
|
||||||
|
}
|
||||||
|
|
||||||
|
M5.Lcd.setTextSize(1);
|
||||||
|
M5.Lcd.setTextColor(OLIVE, 0x000000);
|
||||||
|
M5.Lcd.setCursor(30, 110);
|
||||||
|
if (imu.Ready()) {
|
||||||
|
M5.Lcd.printf(" ");
|
||||||
|
} else {
|
||||||
|
M5.Lcd.printf("calibrating gyro sensors...");
|
||||||
|
}
|
||||||
|
|
||||||
|
M5.Lcd.setTextSize(7);
|
||||||
|
M5.Lcd.setCursor(0, 24);
|
||||||
|
M5.Lcd.setTextColor(key_pressed ? YELLOW : DARKGREEN, 0x000000);
|
||||||
|
M5.Lcd.printf(" %3s ", characters[current_key]);
|
||||||
|
|
||||||
|
long next_time_micros = last_time_micros + kIntervalMicros;
|
||||||
|
while (micros() < next_time_micros) {
|
||||||
|
delay(1);
|
||||||
|
}
|
||||||
|
last_time_micros = next_time_micros;
|
||||||
|
}
|
||||||
BIN
mozc-caps/hardware/bump_cap_insert_mount.stl
Normal file
BIN
mozc-caps/hardware/sliding_plate.stl
Normal file
BIN
mozc-caps/hardware/switch_mount.stl
Normal file
BIN
mozc-caps/hardware/top_plate.stl
Normal file
BIN
mozc-caps/hardware/top_plate_angle_bracket_mount.stl
Normal file
BIN
mozc-caps/images/assembly_1.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
mozc-caps/images/assembly_2.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
mozc-caps/images/assembly_3.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
mozc-caps/images/attach_rubber_band.jpg
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
mozc-caps/images/bump_cap_insert_mount.png
Normal file
|
After Width: | Height: | Size: 6.1 KiB |
BIN
mozc-caps/images/header.jpg
Normal file
|
After Width: | Height: | Size: 465 KiB |
BIN
mozc-caps/images/mount_inner_rail.jpg
Normal file
|
After Width: | Height: | Size: 67 KiB |
BIN
mozc-caps/images/sliding_plate.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
mozc-caps/images/switch_harness.jpg
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
mozc-caps/images/switch_mount.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
mozc-caps/images/switch_pressed.jpg
Normal file
|
After Width: | Height: | Size: 66 KiB |
BIN
mozc-caps/images/switch_with_mount.jpg
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
mozc-caps/images/top_board.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
mozc-caps/images/top_plate_angle_bracket_mount.png
Normal file
|
After Width: | Height: | Size: 6.8 KiB |