[Rails] Active Record Transactions の requires_new

transactionをネストしたときに、子のtransaction内でActiveRecord::Rollbackを投げてもロールバックされない。

irb(main):059:0> User.transaction do
irb(main):060:1*   User.create(username: 'Kotori')
irb(main):061:1>   User.transaction do
irb(main):062:2*     User.create(username: 'Nemu')
irb(main):063:2>     raise ActiveRecord::Rollback
irb(main):064:2>   end
irb(main):065:1> end
   (0.1ms)  begin transaction
  User Create (0.7ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Kotori"], ["created_at", "2019-07-18 14:31:50.104487"], ["updated_at", "2019-07-18 14:31:50.104487"]]
  User Create (0.1ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Nemu"], ["created_at", "2019-07-18 14:31:50.106418"], ["updated_at", "2019-07-18 14:31:50.106418"]]
   (1.7ms)  commit transaction
=> nil

irb(main):066:0> User.all.pluck(:username)
   (0.1ms)  SELECT "users"."username" FROM "users"
=> ["Kotori", "Nemu"]

requires_new: trueとすると子のtransaction内だけロールバックする。

irb(main):073:0> User.transaction do
irb(main):074:1*   User.create(username: 'Kotori')
irb(main):075:1>   User.transaction(requires_new: true) do
irb(main):076:2*     User.create(username: 'Nemu')
irb(main):077:2>     raise ActiveRecord::Rollback
irb(main):078:2>   end
irb(main):079:1> end
   (0.1ms)  begin transaction
  User Create (0.3ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Kotori"], ["created_at", "2019-07-18 14:33:44.891135"], ["updated_at", "2019-07-18 14:33:44.891135"]]
   (0.0ms)  SAVEPOINT active_record_1
  User Create (0.1ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Nemu"], ["created_at", "2019-07-18 14:33:44.892205"], ["updated_at", "2019-07-18 14:33:44.892205"]]
   (0.0ms)  ROLLBACK TO SAVEPOINT active_record_1
   (1.5ms)  commit transaction
=> nil

irb(main):080:0> User.all.pluck(:username)
   (0.1ms)  SELECT "users"."username" FROM "users"
=> ["Kotori"]

ちなみに、普通に例外を投げると親のtransactionもロールバックする。普通。

irb(main):087:0> User.transaction do
irb(main):088:1*   User.create(username: 'Kotori')
irb(main):089:1>   User.transaction do
irb(main):090:2*     User.create(username: 'Nemu')
irb(main):091:2>     raise
irb(main):092:2>   end
irb(main):093:1> end
   (0.1ms)  begin transaction
  User Create (0.8ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Kotori"], ["created_at", "2019-07-18 14:36:22.828138"], ["updated_at", "2019-07-18 14:36:22.828138"]]
  User Create (0.2ms)  INSERT INTO "users" ("username", "created_at", "updated_at") VALUES (?, ?, ?)  [["username", "Nemu"], ["created_at", "2019-07-18 14:36:22.830153"], ["updated_at", "2019-07-18 14:36:22.830153"]]
   (1.4ms)  rollback transaction
Traceback (most recent call last):
        3: from (irb):87
        2: from (irb):89:in `block in irb_binding'
        1: from (irb):91:in `block (2 levels) in irb_binding'
RuntimeError ()

irb(main):094:0> User.all.pluck(:username)
   (0.1ms)  SELECT "users"."username" FROM "users"
=> []

参考

[Ruby] 再帰的group_by

やりたいこと

こういうデータ↓を

[
  { prefecture: '埼玉県', city: '深谷市',        town: '北根',   address: '3-5' },
  { prefecture: '埼玉県', city: '深谷市',        town: '北根',   address: '3-7-19' },
  { prefecture: '埼玉県', city: '深谷市',        town: '堀米',   address: '1-11-14' },
  { prefecture: '埼玉県', city: 'さいたま市中央区', town: '新中里', address: '1-2-17' },
  { prefecture: '埼玉県', city: 'さいたま市中央区', town: '新都心', address: '1-6-13' },
  { prefecture: '東京都', city: '品川区',        town: '西中延', address: '3-5' },
]

こう↓したい

{
  "埼玉県"=> {
    "深谷市"=> {
      "北根"=>["3-5", "3-7-19"],
      "堀米"=>["1-11-14"]
    },
    "さいたま市中央区"=> {
      "新中里"=>["1-2-17"],
      "新都心"=>["1-6-13"]
    }
  },
  "東京都"=> {
    "品川区"=> {
      "西中延"=>["3-5"]
    }
  }
}

再帰的group_by

参考:

module Enumerable
  def recursive_group_by(*props, &block)
    groups = group_by{ |e| e[props.first] }
    next_props = props.drop(1)

    if next_props.empty?
      block_given? ? block.call(groups) : groups
    else
      groups.transform_values do |e|
        e.recursive_group_by(*next_props, &block)
      end
    end
  end
end

data = [
  { prefecture: '埼玉県', city: '深谷市',           town: '北根',   address: '3-5' },
  { prefecture: '埼玉県', city: '深谷市',           town: '北根',   address: '3-7-19' },
  { prefecture: '埼玉県', city: '深谷市',           town: '堀米',   address: '1-11-14' },
  { prefecture: '埼玉県', city: 'さいたま市中央区', town: '新中里', address: '1-2-17' },
  { prefecture: '埼玉県', city: 'さいたま市中央区', town: '新都心', address: '1-6-13' },
  { prefecture: '東京都', city: '品川区',           town: '西中延', address: '3-5' },
]

new_data = data.recursive_group_by(:prefecture, :city, :town) do |groups|
  groups.transform_values do |v|
    v.map { |x| x[:address] }
  end
end

pp data
puts '--'
pp new_data

結果

[{:prefecture=>"埼玉県", :city=>"深谷市", :town=>"北根", :address=>"3-5"},
 {:prefecture=>"埼玉県", :city=>"深谷市", :town=>"北根", :address=>"3-7-19"},
 {:prefecture=>"埼玉県", :city=>"深谷市", :town=>"堀米", :address=>"1-11-14"},
 {:prefecture=>"埼玉県", :city=>"さいたま市中央区", :town=>"新中里", :address=>"1-2-17"},
 {:prefecture=>"埼玉県", :city=>"さいたま市中央区", :town=>"新都心", :address=>"1-6-13"},
 {:prefecture=>"東京都", :city=>"品川区", :town=>"西中延", :address=>"3-5"}]
--
{"埼玉県"=>
  {"深谷市"=>{"北根"=>["3-5", "3-7-19"], "堀米"=>["1-11-14"]},
   "さいたま市中央区"=>{"新中里"=>["1-2-17"], "新都心"=>["1-6-13"]}},
 "東京都"=>{"品川区"=>{"西中延"=>["3-5"]}}}

JSONからフットプリントを自動配置するスクリプトを作った

前置き

キーボードを作ろう!と思い立ち、キーマトリクス回路を理解して、KiCadのEeschemaでスイッチとダイオードを結線して、フットプリントを関連付けして、pcbnewを開いて、「うわメンドクサイ」と思った経験はありませんか?僕は毎回思います。

f:id:yskoht:20190401004223p:plain
作業を始める最初の状態。ここからキーボードの形に部品を配置していく

f:id:yskoht:20190401003527p:plain
作業完了した状態

なので、フットプリントを自動配置するスクリプトを作りました。

Keyboard Layouter Plugin

Keyboard Layout Editor (KLE) のJSONからスイッチのフットプリントを自動配置します。

インストール

keyboard_layouter.py をダウンロードし、以下のディレクトリに置くだけです。

  • Windowsの場合
    • %APPDATA%/Roaming/kicad/scripting/plugins
  • macOSの場合
    • /Applications/KiCad/kicad.app/Contents/SharedSupport/scripting/plugins
  • Linuxの場合
    • ~/.kicad/scripting/plugins

Quick Start

簡単に試せるようにJSONと回路のサンプルを用意しました。

これをダウンロードしてKiCadを起動し、pcbnewを開くと以下のようになっていると思います。

f:id:yskoht:20190401010057p:plain

「ツール」→「外部プラグイン」→「Keyboard Layouter」を選択します。

f:id:yskoht:20190401010431p:plain

KLEで作成したJSONを指定します1。ここではErgoDoxのJSONファイルを選びました。

スイッチを移動させたいときは"Switch"のチェックボックスにチェックを入れます。 ダイオードも一緒に移動させたいときは"Diode"にもチェックを入れます。スイッチの原点からどれだけオフセットを持たせて配置させるか、表裏反転させるかを設定することができます。設定が終わったら、最後に"Run"ボタンを押します。

f:id:yskoht:20190401011742p:plain
実行が完了した状態

それっぽく配置されました🎉

制限事項

  • 1uのキー間隔は19.05mmです
  • 対応しているスイッチのフットプリントはkicad-footprints/Button_Switch_Keyboard.prettyのCherry MX Switchのみです。
    • 使えるスイッチのサイズは以下の通りです。これ以外のサイズ、例えば1 x 1.5などは現状では扱えませんが、1.5 x 1を90度回転させることはできます
      • 1.00u (1 x 1)
      • 1.25u (1.25 x 1)
      • 1.50u (1.5 x 1)
      • 1.75u (1.75 x 1)
      • 2.00u (2 x 1, 1 x 2)
      • 2.25u (2.25 x 1)
      • 2.75u (2.75 x 1)
      • 6.25u (6.25 x 1)
      • ISO Enter

最後に

まだまだバグがあるかもしれません。ご使用の際は自己責任で、目視確認を!(Issue, PRいただけると喜びます)

皆様のキーボードづくりの一助になれば幸いです。

参考


  1. 左上の凡例をスイッチのリファレンス番号にする必要があります。

[tmux] Ctrl-dでexitするのをやめる

環境

$ bash --version
bash --version
GNU bash, バージョン 4.4.23(1)-release (x86_64-apple-darwin17.5.0)
Copyright (C) 2016 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL バージョン 3 またはそれ以降 <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

$ tmux -V
tmux -V
tmux 2.7

やったこと

.bashrcでignoreeofを設定

set -o ignoreeof

.tmux.confでデタッチをunbind

unbind -T root C-d

参考

Python EDAで論理式を簡単化する

CPUの創りかた』に載っているデコーダをPython EDAを使って簡単化してみた。

真理値表

OP3, OP2, OP1, OP0, CFlagが入力。B, A, ~LOAD0, ~LOAD1, ~LOAD2, ~LOAD3が出力。

OP3 OP2 OP1 OP0 CFlag B A ~LOAD0 ~LOAD1 ~LOAD2 ~LOAD3
0 0 0 0 X 0 0 0 1 1 1
0 0 0 1 X 0 1 0 1 1 1
0 0 1 0 X 1 0 0 1 1 1
0 0 1 1 X 1 1 0 1 1 1
0 1 0 0 X 0 0 1 0 1 1
0 1 0 1 X 0 1 1 0 1 1
0 1 1 0 X 1 0 1 0 1 1
0 1 1 1 X 1 1 1 0 1 1
1 0 0 1 X 0 1 1 1 0 1
1 0 1 1 X 1 1 1 1 0 1
1 1 1 0 0 1 1 1 1 1 0
1 1 1 0 1 X X 1 1 1 1
1 1 1 1 X 1 1 1 1 1 0

入力側のdon't careを0, 1になおして、入力の欠けている部分も補ったのが↓。

OP3 OP2 OP1 OP0 CFlag B A ~LOAD0 ~LOAD1 ~LOAD2 ~LOAD3 追加
0 0 0 0 0 0 0 0 1 1 1
0 0 0 0 1 0 0 0 1 1 1
0 0 0 1 0 0 1 0 1 1 1
0 0 0 1 1 0 1 0 1 1 1
0 0 1 0 0 1 0 0 1 1 1
0 0 1 0 1 1 0 0 1 1 1
0 0 1 1 0 1 1 0 1 1 1
0 0 1 1 1 1 1 0 1 1 1
0 1 0 0 0 0 0 1 0 1 1
0 1 0 0 1 0 0 1 0 1 1
0 1 0 1 0 0 1 1 0 1 1
0 1 0 1 1 0 1 1 0 1 1
0 1 1 0 0 1 0 1 0 1 1
0 1 1 0 1 1 0 1 0 1 1
0 1 1 1 0 1 1 1 0 1 1
0 1 1 1 1 1 1 1 0 1 1
1 0 0 0 0 X X X X X X
1 0 0 0 1 X X X X X X
1 0 0 1 0 0 1 1 1 0 1
1 0 0 1 1 0 1 1 1 0 1
1 0 1 0 0 X X X X X X
1 0 1 0 1 X X X X X X
1 0 1 1 0 1 1 1 1 0 1
1 0 1 1 1 1 1 1 1 0 1
1 1 0 0 0 X X X X X X
1 1 0 0 1 X X X X X X
1 1 0 1 0 X X X X X X
1 1 0 1 1 X X X X X X
1 1 1 0 0 1 1 1 1 1 0
1 1 1 0 1 X X 1 1 1 1
1 1 1 1 0 1 1 1 1 1 0
1 1 1 1 1 1 1 1 1 1 0

Python EDA

from pyeda.inter import *

X = ttvars('x', 5)

b     = truthtable(X, '00 00 11 11 00 00 11 11 -- 00 -- 11 -- -- 1- 11'.replace(' ', ''))
a     = truthtable(X, '00 11 00 11 00 11 00 11 -- 11 -- 11 -- -- 1- 11'.replace(' ', ''))

load0 = truthtable(X, '00 00 00 00 11 11 11 11 -- 11 -- 11 -- -- 11 11'.replace(' ', ''))
load1 = truthtable(X, '11 11 11 11 00 00 00 00 -- 11 -- 11 -- -- 11 11'.replace(' ', ''))
load2 = truthtable(X, '11 11 11 11 11 11 11 11 -- 00 -- 00 -- -- 11 11'.replace(' ', ''))
load3 = truthtable(X, '11 11 11 11 11 11 11 11 -- 11 -- 11 -- -- 01 00'.replace(' ', ''))

bm, am, load0m, load1m, load2m, load3m = espresso_tts(b, a, load0, load1, load2, load3)

print('b =', bm)
print('a =', am)
print()

print('load0 =', load0m)
print(expr2truthtable(load0m))

print('load1 =', load1m)
print(expr2truthtable(load1m))

print('load2 =', load2m)
print(expr2truthtable(load2m))

print('load3 =', load3m)
print(expr2truthtable(load3m))

出力が↓。OP3, OP2, OP1, OP0, CFlagがそれぞれ、x[4], x[3], x[2], x[1], x[0]に割り当てられる。

b = x[2]  # OP1
a = Or(x[1], x[4])  # OP0 + OP3

load0 = Or(x[4], And(x[3], ~x[4]))  # OP3 + (OP2・~OP3) = OP2 + OP3
x[4] x[3]
   0    0 : 0
   0    1 : 1
   1    0 : 1
   1    1 : 1

load1 = Or(x[4], And(~x[3], ~x[4]))  # OP3 + (~OP2・~OP3) = ~OP2 + OP3
x[4] x[3]
   0    0 : 1
   0    1 : 0
   1    0 : 1
   1    1 : 1

load2 = Or(x[3], And(~x[3], ~x[4]))  # OP2 + (~OP2・~OP3) = OP2 + ~OP3
x[4] x[3]
   0    0 : 1
   0    1 : 1
   1    0 : 0
   1    1 : 1

load3 = Or(~x[3], And(x[3], ~x[4]), And(x[0], ~x[1]))
        # ~OP2 + (OP2・~OP3) + (CFlag・~OP0)
        # = ~OP2 + ~OP3 + (CFlag・~OP0)
x[4] x[3] x[1] x[0]
   0    0    0    0 : 1
   0    0    0    1 : 1
   0    0    1    0 : 1
   0    0    1    1 : 1
   0    1    0    0 : 1
   0    1    0    1 : 1
   0    1    1    0 : 1
   0    1    1    1 : 1
   1    0    0    0 : 1
   1    0    0    1 : 1
   1    0    1    0 : 1
   1    0    1    1 : 1
   1    1    0    0 : 0
   1    1    0    1 : 1
   1    1    1    0 : 0
   1    1    1    1 : 0
  • B, Aは本に載ってるのと同じものが得られた
  • ~LOAD0, ~LOAD1, ~LOAD2, ~LOAD3は分配律(x + yz) = (x + y)(x + z)を使えば同じ形

RaspberryPi Helix

この記事は自作キーボード #2 Advent Calendar 2018 - Adventarの25日目の記事です。 昨日は@zeriyoshiさんの ALPS メカニカルキースイッチとその歴史でした。

TL;DR

f:id:yskoht:20181225032751j:plain

  • HelixのコントローラとしてRaspberry Pi Zero Wを使った
  • Bluetoothキーボードをエミュレートした
  • LEDを制御した

デモ

構成

f:id:yskoht:20181225220443p:plain

リポジトリ


以下、作業メモです。

Raspberry Pi Zero Wのセットアップ

上記の記事がとても丁寧に書かれているので、これに従って設定していきます。以下に要点をメモっておきます。

OSイメージのダウンロード

今回使ったイメージは2018-11-13-raspbian-stretch-lite.img です。

イメージのインストール

$ sudo ./install 2018-11-13-raspbian-stretch-lite.img

設定

$ touch /Volumes/boot/ssh  # ssh有効化
$ vi /Volumes/boot/cmdline.txt  # "modules-load=dwc2,g_ether"を追記
$ echo "dtoverlay=dwc2" >> /Volumes/boot/config.txt

インターネット共有

f:id:yskoht:20181225015209p:plain

$ ssh pi@raspberrypi.local

Wifiの設定

$ sudo sh -c 'wpa_passphrase "wifi_ssid" < wifi_password >> /etc/wpa_supplicant/wpa_supplicant.conf'
$ sudo wpa_cli reconfigure

ここまででラズパイが使えるようになりました。


Bluetoothキーボードのエミュレート

次はこの記事に従ってラズパイをBluethoothキーボードとして使えるようにします。

ツールのインストール

$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install python-gobject bluez bluez-tools bluez-firmware python-bluez python-dev python-pip python-gtk2
$ sudo pip install evdev 

元記事からpython-gtk2を追加しています。

Bluethoothデーモンの設定

$ sudo /etc/init.d/bluetooth stop
$ sudo /usr/sbin/bluetoothd --nodetach --debug -p time

一度Bluethoothデーモンを止めて、立ち上げ直します。

ソースコードのクローン

元記事のソースをクローンしてきます。

$ git clone https://github.com/yaptb/BlogCode.git

DBUSの設定

$ cd BlogCode/btkeyboard/dbus
$ sudo cp org.yaptb.btkkbservice.conf /etc/dbus-1/system.d

Bluetoothデバイスの有効化

$ sudo hciconfig hcio up

sudo hciconfig hcioでBluetoothデバイスアドレスをメモっておき、BlogCode/btkeyboard/server/btk_server.pyMY_ADDRESSを書き換えます。

エミュレーターサーバーの起動

$ cd BlogCode/btkeyboard/server
$ sudo python btkserver.py

ペアリング

$ sudo /usr/bin/bluetoothctl
# agent on
# default-agent
# scan on
# discoverable on

パスキーの確認が出たらyesとタイプしてペアリング完了。

文字を送信してみる

$ cd BlogCode/btkeyboard/keyboard
$ sudo python send_string.py hello_bluetooth!

ここまででラズパイをBluetoothキーボードとして使えるようになりました。


ラズパイとHelixをつなげる

あとはHelixとラズパイを配線し、キーマトリクスの状態をGPIOで読み出してやればオーケーなはずです。

配線図

f:id:yskoht:20181225032203p:plain

Colは左右で共用しています。VCC, GNDは省略しています。

f:id:yskoht:20181225032719j:plain

ファームウェア


LEDの制御

せっかくなのでLED制御もします。Helixに使われているLEDはSK6812MINIですが、 これでググっても情報が出てこないのでws2812 raspberry piなどでググります。1

すると、しっかりとライブラリがあったので、次はこの記事をなぞります。

ツールのインストール

$ sudo apt-get install build-essential scons swig

ライブラリのインストール

$ git clone https://github.com/jgarff/rpi_ws281x.git
$ cd rpi_ws281x && sudo scons && cd python && sudo python setup.py install

元記事はwgetを使っていますが、gitで。

配線

f:id:yskoht:20181225032208p:plain

VCC, GNDは省略しています。

サンプルプログラムの実行

ピンとLED数を修正してrpi_ws281x/python/examples/strandtest.pyを実行します。


まとめ

  • ファームはすごい適当ですが、思ったよりもちゃんと動いていた
  • レイヤー機能がないです。ちゃんと使うなら必要
  • LEDのパターンを増やしていきたい


この記事はpakbdを使って書かれました。


おまけ:うまくいかなかったこと

USB HIDデバイス化

最初は有線でラズパイをHIDデバイスとして使うことを考えていました。試して見ましたがうまくいきませんでした。OSのアップデートが原因っぽい?

スマートキーボード化

Google Assistant SDKを動かして"OK Helix!"とやりたかったんですが、 やってみるとAssistant単体では動くんですが、キーボードのファームに組み込むとLEDが光らなくなりうまくいきませんでした。

ライブラリ内部で使っている何かが競合しているのか、単にタイミングの問題なのか、まだよくわかってないです。



  1. QMKのファームを読んでみたらws2812と書いてあったのを見つけました

pakbdについて。つまり僕の好きなキーボードについて

pakbd rev1

f:id:yskoht:20181103033121j:plain

f:id:yskoht:20181103034232j:plain

f:id:yskoht:20181103033428j:plain

f:id:yskoht:20181103033413j:plain

f:id:yskoht:20181103033459j:plain

f:id:yskoht:20181103033503j:plain

f:id:yskoht:20181103033439j:plain

Row-Staggered

自作キーボードではOrtholinearと呼ばれる格子状の配列や、 Column-Staggerdと呼ばれる縦方向にキーがずれて配置されているレイアウトが多いですが1、 僕は一般的によく目にするタイプのキー配列、Row-Staggeredと呼ばれているレイアウトが好きです。 このレイアウトに慣れているからという単純な理由だけですが。

なので自分で0から自由にキーボードを作れるとしても、よくあるキーボードレイアウトと大きく変わる変更は極力避けるようにしました。 矢印キーやBackspace, Deleteキーも自分としては必須なので省略しませんでした。 pakbdのレイアウトはこんな感じです。

f:id:yskoht:20181103035242p:plain

よくあるレイアウトと違う箇所いくつかありますが、これらの差異はたとえ一般的でなくても自分にとって大きなメリットがあると思います。

  • 左ShiftEnterが1.75u。(よくあるレイアウトでは2.25u)
    • スタビライザを使いたくなかったから。単純にパーツが増えるのが嫌だったのと、スタビライザはカチャカチャ音がうるさいし、タイプの感触が変わってしまうのも嫌でした
  • 左下にCtrlがない。OptionCmdの位置がよくあるレイアウトより右に配置されている
    • 自分はAの隣にCtrlを配置しているから、左下のCtrlは不要でした。OptionCmdの位置はMint60を参考にさせてもらいました。コピー(Cmd+C)やペースト(Cmd+V)のためにCmdキーはもっと右にあるべきだと常々思っていた

pakbd rev1では4行バージョンも作れるようにしました。4行バージョンの想定レイアウトはこんな感じ。

f:id:yskoht:20181103035253p:plain

Raiseキーを押すと一番上の行が数字行に入れ替わります。

6とBの問題

自分は文章中に出てくるBは左手人差し指でタイプするんですが、Ctrl+B(Emacsキーバインドで一文字戻る)のBは右手人指し指でタイプする癖があるので、 左右両方に必要でした。

f:id:yskoht:20181103040525j:plain

キーキャップ

DCS, SA , DSA, etc...いろんなプロファイルがありますが、自分はDCSが好きです。

pakbdではいくつかのキーを追加して、さらにいくつかのキーの長さを変えてしまったのでキーキャップの選択がすごく難しくなってしまいました。 普通のキーキャプセットにBのキーは1つしか含まれていないし、1.75uのキーもcaps lockしか含まれてない。 さらにstep sculpture2なキーキャップは配置する行も決まっているので、 ほんとうに統一感のある見栄えにするには、同じキーキャップセットを2つ以上買わなきゃいけない。つらい。

キーキャップセットを2つ買ったり、grab bagの中からマシなキーキャップを見つけたりしました。

rev1で新しくやったこと

  • スイッチソケット対応した
    • スイッチがホットスワップできるようになった

f:id:yskoht:20181103040617j:plain

  • PCBに切れ目を入れて5行版と4行版に対応した
    • カッターとアクリルカッターでPCBを削ったら、狙い通りに手で折ることができた

f:id:yskoht:20181103040520j:plain

f:id:yskoht:20181103034217j:plain

f:id:yskoht:20181103040909j:plain

  • 自作ケーブルを作成した
    • これはキーボードに直接関係ないですが、pakbdはケーブル端子が側面についているので両L字のケーブルが欲しかったので、秋葉原のオヤイデ電気に行って店員さんにパーツを選んでもらった

f:id:yskoht:20181103034423j:plain

パーツとコスト

あんまりちゃんと勘定したことがなかったので今回はしっかりと単価を計算してみた。

次の表は写真の一番上にある紫色の4行キーボードを作るのに使った金額です。 いくつかgrab bagで手に入れたキーキャップを使っていますがそれは勘定に含んでいないし、送料も含んでいない。

部品 金額 単価 必要数 小計 備考
Kailh Speed Switch Rose Pink ¥400/10 ¥40 55 ¥2200 https://yushakobo.jp/shop/kailh-speed/
GK64 XD64 DZ60 PBT keycaps $30.00 ¥3329 1 ¥3329 https://kbdfans.cn/products/gk64-xd64-dz60-keycaps
スイッチソケット Kailh CPG151101S11 ¥150/10 ¥15 55 ¥825 https://yushakobo.jp/shop/a01ps/
ダイオード 1N4148W ¥400/40 ¥10 55 ¥550 http://akizukidenshi.com/catalog/top.aspx
Pro Micro ATmega32U4 5V/16MHz/MicroUSB2(互換品) ¥550 ¥550 2 ¥1100 https://talpkeyboard.stores.jp/items/5b24504ba6e6ee7ec60063e3
チップ抵抗 4.7kΩ(1206) ¥50/10 ¥5 2 ¥10 https://www.sengoku.co.jp/index.php
TRRSジャック MJ-4PP-9 ¥50 ¥50 2 ¥100 http://akizukidenshi.com/catalog/top.aspx
PCB左側(149.6mm*101.2mm, 1.6, 2 Layers) $37.00/5 ¥863 1 ¥863 https://www.allpcb.com/
PCB右側(182.9mm*101.2mm, 1.6, 2 Layers) $39.00/5 ¥909 1 ¥909 https://www.allpcb.com/
アクリルプレート A4 3mm ¥5600 ¥5600 1 ¥5600 https://yushakobo.jp/lasercut/
ステンレスプレート左側(149.7mm*79.1mm) ¥4770 ¥4770 1 ¥4770 https://www.kiriita.com/
ステンレスプレート右側(178.2mm*79.1mm ) ¥5690 ¥5690 1 ¥5690 https://www.kiriita.com/
スタンドオフM2x5mm ¥1030/100 ¥10.3 12 ¥124 Amazon.co.jp: 100個雌ネジ柱真鍮スタンドオフスペーサーゴールドトーンM2x5mm: DIY・工具・ガーデン
ネジ M2 x 5mm ¥496/60 ¥8.27 12 ¥99 Amazon.co.jp: uxcell ヘッドねじ ラウンド/パンヘッド 304ステンレス鋼 M2 x 5mm: DIY・工具・ガーデン
ネジ M2 x 10mm ¥100/12 ¥8.33 12 ¥100 -
合計 ¥26269

このキーボードはプレートにステンレスを使ってみたので少し高級になった。

rev1の不具合

  • PCBの左右が噛み合わない
    • かみ合わせの凸凹を同じ幅にしてしまったから当然。幸いなことに少しヤスリを掛けるだけでうまく噛み合うようになった。アクリルも同じsvgを使ったんですが、こっちは最初からうまく噛み合った。カット時に溶けた分だけ小さくなったんだと思います
  • ProMicroとソケットが接触する
    • ProMicroの上にもキーを配置したのでPCBとProMicroの間にスイッチソケットが挟まる箇所が左右1箇所ずつあるんですが、ProMicroとソケットが接触してしまい、ProMicroが水平にはんだ付けできなかった
  • PCBの固定方法を間違えた
    • スイッチソケットを使わない場合は、プレートにスイッチをはめてスイッチをPCBにはんだ付けするとPCBは固定されていたんですが、ソケットを使う場合はPCBは別の方法でプレートと固定されなければならないと組み立ててから気づきました

最後に

11/3の天キーにスペースがあれば展示する予定です。見かけたらぜひカチカチしてやってください。


  1. もちろん、Row-Staggeredの自作キーボードもある。Mint60が有名

  2. 行ごとにキーキャップの高さが違うキーキャップのこと。数字行は背が高く、一番手前にある行は背が低くなっている