Dozen0を組み立てた
はじめに
Dozen0を組み立てたので記録を残しておきます。
ミスったところを書いておきます。
Dozen0とは
Dozen0を購入した動機
- 組み立て後もスイッチの交換ができる。
- キースイッチを実際に動作させて試すことができる。
- 自作キーボードキットに使うキースイッチの選定に使えそう。
組み立ててみた後の印象
- キースイッチソケットのはんだ付けは独特だけど、難しくない。
- 組み立て後は隠れちゃうけど、ボトムプレートPCBにも◤◢◤◢◤◢◤◢がワンポイントではいっててカッコいい。
- はんだ付け箇所が少なく、キースイッチの選定に使えるので、これから自作キーボードをはじめようっていう人におすすめ。
あったほうが良い工具
Dozen0の組み立てに必要な工具は公式のビルドガイドの通りですが、 個人的にあったほうが良いと思う工具
- 先の細いこて先
- ソケットのコの字型の部分に先端が入るやつであれば何でもOK
- 白光 こて先 1.6D型 T18-D16
- 逆動作ピンセット
- ソケットがPCBから浮かないように押さえるのに使いました。
- goot TS-16
- LED付きヘッドルーペ
- ソケット部分にちゃんとハンダが流れてるのかとかProMicroのはんだ付けちゃんとできてるかとか、はんだ付け作業をしながら確認できます。
- キースイッチ引き抜き工具
組み立て記録
難しいところはありませんでしたが、 あんまり難しくないだろうとナメてかかってたのでミスりました。
ソケットのはんだ付け
ソケットを逆動作ピンセットで挟んで固定して、はんだ付けしました。
ソケットのコの字型の部分にコテ先を入れて、十分に加熱してからハンダを入れます。 いつもの270度の設定で、2,3秒くらい温めてという感じでやりました。
コの字部分の底にスリットが空いていて、加熱が十分だとここからハンダが流れて PCBのパッドと接合するみたいです。
やらかし1:ロープロファイルのキースイッチと普通のキースイッチが混載できるんだと思ってた。
これは私の完全な思い込みだったんですが、 最初こんな感じでロープロファイルのキースイッチ(右下の3つ, choc系スイッチ)と通常のキースイッチ(Cherry MX系スイッチ)を混載してみたんですが、
こうなりました。 ロープロファイルの方のキースイッチの端子がソケットに届いてません。 Dozen0自体は両方に対応していますが、混載はできません。
気を取り直して全部Cherry MX系のスイッチに切り替えました。 その後、プレートに取り付けたキースイッチをソケットのついたPCBに 全体を均等に少しづつ押し込んでいったんですが、 別の理由でうまくはまらない箇所がありました。
やらかし2:ソケットの方向を間違えてはんだ付けしていた
ソケットのはんだ付け方向を間違えていました。 これによってPCBの真ん中の穴が塞がれ、スイッチがPCBにちゃんとはまりませんでした。
ハンダ吸い取り線ではんだを除去。 ハンダを吸い取っただけだとソケットが取り外せないので、 はんだごてでソケットの端子を加熱しつつ、ピンセットで交互にちょっとずつ浮かしてソケットを取り外しました。
うまく行ってよかった。
ファームウェアを書き込んで動作チェック
最初はQMK Toolbox(mac版)を使おうとしたんですが、うまく扱えなかったので、 QMKのビルド環境を作ってファームウェアを書き込みました。
macでのビルド環境の作り方 https://docs.qmk.fm/#/getting_started_build_tools?id=macos
qmkのファームウェアをクローンしてくる
git clone https://github.com/qmk/qmk_firmware.git
Dozen0のファームウェアのビルドとインストール
Dozen0をPCに接続して、以下のコマンドを実行。
cd qmk_firmware; make dozen0:default:avrdude
Detecting USB port, reset your controller now.......
と表示され始めたらDozen0のリセットスイッチを1回または2回連続で押す。
Dozen0の動作チェック
以下のサイトをつかってキー入力のテストを行いました。
動作チェックしてたんですが、反応しないキーがある。 いったん全部スイッチを外して、ソケットのついたPCBを眺めてみました。 はんだ付けは問題なさそうです。
PCBを明かりに透かしてみると、ソケット部分の端子がやたらと大きく開いてるのがある。 原因はこれっぽい。
やらかし3: ソケット端子がキースイッチの端子に接触してない
たぶん、ソケットのはんだ付け方向を間違えたせいで、ソケットの端子に無理な力がかかったんでしょう。 ソケット内部の端子が広がりすぎてスイッチの端子に接触してない状態になっていました。
問題の起きたソケット内部の端子を壊さないように、 ドライバーセットの千枚通しのようなしっかりした先の細いやつでこじって広がった端子を狭めました。 これもうまく行って良かった。
※精密ピンセットはこういう力のかかる用途に使ってはいけません。 先を曲げてだめにしてしまいます。
キースイッチソケット、意外と繊細ぽいので注意しよう。
完成
もういっかい組み直して無事完成しました。
マスキングテープを使って、安全にProMicroのモゲ対策をする
はじめに
ProMicroのmicroUSBコネクタには穴が空いており、コネクタのモゲ対策で接着剤を盛ろうとすると容易にコネクタ内に接着剤が侵入します。
マスキングテープを使うことで安全に接着剤を盛ることができたので紹介します。
作業に必要なもの
- マスキングテープ
- 先が細い精密ピンセット
microUSBコネクタ内に突っ込めるくらい先が細いピンセットが必要です。
今回使ったのはgootのTS-10です。
マスキングテープでコネクタの穴を塞ぐ
上面に2つ、左右の下側に2つ穴が空いているので、これらをマスキングテープで塞ぎます。
※コネクタ内部のmicroUSBの端子部分を壊さないように注意してください。
- マスキングテープを適当な大きさに切ります。
- 粘着面を外側にして二つ折りにします。
- ピンセットでマスキングテープをつまみコネクタ内に入れます。
- マスキングテープをコネクタの一番奥まで突っ込みます。
- ピンセットの先端でマスキングテープをコネクタの内側から外側に向かって押しつけてマスキングテープを貼り付けます。
- コネクタの穴がちゃんとふさがっているか、マスキングテープとコネクタの間に隙間ができていないか確認します。
反対側の穴も同様にマスキングテープで塞ぎます。
スルーホールの保護
スルーホール内に接着剤が侵入すると困るので、
スルーホールにもマスキングテープを貼って保護します
接着剤をmicroUSBコネクタ周辺に盛る
あとは接着剤をmicroUSBコネクタ周辺に盛るだけです。
スルーホールのマスキングテープの上に接着剤がかかってしまった場合は、
接着剤がゴム状に半分固まったくらいになってから
マスキングテープを剥がして接着剤を整え、スルーホールが塞がらないようにします。
接着剤が固まったらマスキングテープを剥がして完成です。
isaax勉強会#24(ラズパイでオムロン環境センサを使ってみる)に参加してきた
参加しようと思ったきっかけ
自室の温度とか湿度の記録を取りたいなと思っていて、 ウェザーニュースのWxBeacon2(オムロンの環境センサーのOEM)と Raspberry Pi 3B+を買ってたんだけど、 放置したままだったので、これはまさにぴったりの勉強会ではないかと思って参加してみました。
isaaxについての知識はありませんでした。
ハンズオンの中身
ハンズオンで使ったテキストは以下で公開されています。
オムロン環境センサ・Ambient・isaaxハンズオン – Isaax Camp
ハンズオンで利用するプログラム(Pythonスクリプト)はすでにgithubに用意されていて、 それをforkして使いました。
GitHub - isaaxug/envsensor-ambient
全体のシステム構成はこんな感じだと思います。
sshでraspberry piに入って、isaaxdのプロセスとかディレクトリを見てたら、 isaaxdがgithubからraspberry piで動かすPythonスクリプトをダウンロードしてきて実行してました。
事前に用意されていたのは、以下の2つのPythonスクリプトでした。
- オムロンの環境センサーからBLEでデータ読み出しをするもの(sample1.py)
- それに可視化サービスambientへデータを送信する部分が追加されたもの(sample2.py)
コードを書く部分はあまりなくて、 isaax, github, ambientなどの連携や設定がほとんどでした。
githubに上げられない秘匿情報はisaax側に保存しておいて、環境変数として参照できるみたいです。
多少のトラブルがあったものの、 環境センサーから取得したデータをambientに送って可視化することができました。
勉強会をふりかえってみて
isaaxむっちゃ便利!!
家でも使おうと思う。
これまで趣味でraspberry piをちょこちょこいじってたんだけど、 raspberry pi上でプログラムを動かすとなると、
- PC上でraspberry pi上で動かすプログラムのソースコード書く
- raspberry pi上に転送
- raspberry pi上で実行
- 必要ならサービスの作成
とまあ、わりとめんどくさい手順が必要だったんだけど、 isaaxを使えば、
という風にすごく楽にプログラムの修正と反映ができる。
個人が趣味で使っている分には、raspberry piが手元にあるので、 実感できる便利さはこの程度なんだけど、 isaaxの真価はインターネットにつながってさえいれば、 リモートでプログラムの更新ができるところにあるらしい。
swiftでQRコードジェネレータを作ってみた
だいぶ前だけど、swiftでQRコードジェネレータを作ってみた
今回はSwiftとRxSwiftを使ってみようということで、
またしてもMac上で動くQRコードジェネレータを作ってみた。
QRコードのエンジン部分にはQRCoderを使った。
テキストボックスと、スライダーの両方を監視して、
どちらかに変更があったらQRコード画像を生成し直すっていう処理が比較的少ないコード量で書ける。
ただ、RxSwiftの参考書などを見ずにやってたから、このくらいだったら大丈夫だけど、もっと複雑なことをやろうとしたら大変そうだなって感触。
あとMacOS用のRxSwiftはRxCocoaというサブセットで、
サポートされているGUI部品がiOS用のものとだいぶ違うらしい。
たとえば、MacOSだとテーブルはNSTableViewを使うんだけど、
このあたりのサポートがなかったりする。
griffonでQRコードジェネレータを作ってみた
macでQRコードを生成するツールZebraがあって、
便利に使ってたんだけど、URLを変更するときにいちいち別の画面を開かないといけないのが面倒だったんで、QRコードジェネレータを作ってみた。
Zebraでもショートカットキーをつかえば簡単にできるけどね。
バイナリはここから
https://bitbucket.org/cnaos/quickqrgen/downloads/qucickQrGen-0.1-jar.zip
ソースコードはここに。
https://bitbucket.org/cnaos/quickqrgen/src
参考にしたのはこのあたり。
- http://www5.ocn.ne.jp/~coast/programming/groovy/griffon.html
- http://beta.mybetabook.com/showpage/4f27bb060cf26106dca875c4
QRコードジェネレータ部分はgoogleのzxingを使いました。
GitHub - zxing/zxing: ZXing ("Zebra Crossing") barcode scanning library for Java, Android
結構作るのに手間取ったけど、
groovyのswing builderの使い方とか、
Javaのswingのあたりの基礎力が足りないんだろうな。
jerseyのサロゲートペアに関するバグ
現象
POSTするテキストなどにU+10000以上のUTF-8文字(サロゲートペアに変換される文字)が含まれると、OAuthのSHA-1署名が不正なものになる。
まずは、OAuthの署名をやってるメソッドのコード*1です。
78 public class OAuthSignature {
90 public static String generate(OAuthRequest request,
91 OAuthParameters params, OAuthSecrets secrets) throws OAuthSignatureException {
92 return getSignatureMethod(params).sign(elements(request, params), secrets);
93 }
105 public static void sign(OAuthRequest request,
106 OAuthParameters params, OAuthSecrets secrets) throws OAuthSignatureException {
107 params = (OAuthParameters)params.clone(); // don't modify caller's parameters
108 params.setSignature(generate(request, params, secrets));
109 params.writeRequest(request);
110 }
122 public static boolean verify(OAuthRequest request,
123 OAuthParameters params, OAuthSecrets secrets) throws OAuthSignatureException {
124 return getSignatureMethod(params).verify(elements(request, params), secrets, params.getSignature());
125 }
jerseyのOauthモジュールで署名を扱ってるのはこのOAuthSignatureクラスなんですが、sign(), verify()メソッドから呼ばれているelementsメソッドで、URIとかリクエストの中身を正規化しています。
230 private static String elements(OAuthRequest request,
231 OAuthParameters params) throws OAuthSignatureException {
232 // HTTP request method
233 StringBuilder buf = new StringBuilder(request.getRequestMethod().toUpperCase());
235 // request URL, see section 3.4.1.2 http://tools.ietf.org/html/draft-hammer-oauth-10#section-3.4.1.2
236 buf.append('&').append(UriComponent.encode(constructRequestURL(request).toASCIIString(),
237 UriComponent.Type.UNRESERVED));
239 // normalized request parameters, see section 3.4.1.3.2 http://tools.ietf.org/html/draft-hammer-oauth-10#section-3.4.1.3.2
240 buf.append('&').append(UriComponent.encode(normalizeParameters(request, params),
241 UriComponent.Type.UNRESERVED));
243 return buf.toString();
244 }
このあたりには問題がなくて、UriComponent.encode()メソッドが問題。
252 public static String encode(String s, Type t, boolean template) {
253 return _encode(s, t, template, false);
254 }
274 private static String _encode(String s, Type t, boolean template, boolean contextualEncode) {
275 final boolean[] table = ENCODING_TABLES[t.ordinal()];
277 StringBuilder sb = null;
278 for (int i = 0; i < s.length(); i++) {
279 final char c = s.charAt(i);
280 if (c < 0x80 && table[c]) {
281 if (sb != null) sb.append(c);
282 } else {
283 if (template && (c == '{' || c == '}')) {
284 if (sb != null) sb.append(c);
285 continue;
286 } else if (contextualEncode) {
287 if (c == '%' && i + 2 < s.length()) {
288 if (isHexCharacter(s.charAt(i + 1)) &&
289 isHexCharacter(s.charAt(i + 2))) {
290 if (sb != null)
291 sb.append('%').append(s.charAt(i + 1)).append(s.charAt(i + 2));
292 i += 2;
293 continue;
294 }
295 }
296 }
298 if (sb == null) {
299 sb = new StringBuilder();
300 sb.append(s.substring(0, i));
301 }
303 if (c < 0x80) {
304 if (c == ' ' && (t == Type.QUERY_PARAM)) {
305 sb.append('+');
306 } else {
307 appendPercentEncodedOctet(sb, c);
308 }
309 } else {
310 appendUTF8EncodedCharacter(sb, c);
311 }
312 }
313 }
こいつがU+10000のUnicode文字つまり、java内でサロゲートペアに変換される文字を考慮してないんですな。
まあ、中身が正しいかどうかはわからんけど、サロゲートペアが含まれる場合は、以下のようにStringのlength()をそのまま使うと、U+10000以上のコードポイントの1文字が2文字に分割されてるので、バグります。
for (int i = 0; i < s.length(); i++) {
実際にgroovyで確認
ではそれを実地で確認してみましょう。javaだとjersey持ってきたりコンパイルするのがたるいので、goovyのコードです。groovyConsoleから実行するのが楽です。
@Grapes( @Grab(group='com.sun.jersey', module='jersey-core', version='1.14') ) import java.net.URLEncoder; def text="\ud83c\udc00\ud83c\udc01\ud83c\udc02" println "text="+text println "----" def encoded = com.sun.jersey.api.uri.UriComponent.encode(text, com.sun.jersey.api.uri.UriComponent.Type.UNRESERVED) println "UriComponent.encode="+encoded def utf8andurlencoded = URLEncoder.encode(text, "UTF-8"); println "utf8andurlencoded="+utf8andurlencoded;
なにをやってるかというと、unicodeの U+1F000, U+1F001, U+1F002の3文字(unicodeで)をサロゲートペア化したテキストを用意して、JerseyのUriComponent.encodeとjava標準のURLEncodeでそれぞれUTF-8エンコード&URLエンコードした結果を表示しています。
で、結果はこれ。
text=🀀🀁🀂
-
-
- -
-
jerseyの使ってるクラスのほうがエンコード失敗してますね。
というわけで、jerseyのOAuth署名処理にはコードポイントでU+10000以上の文字が含まれる場合に、リクエストパラメタの正規化のためのURLエンコードが正しく行われない問題があり、結果としてOAuth署名が正しく行われないのでした。
jersey2のほうでもまだ修正されてないみたいね。
jersey/UriComponent.java at master · jersey/jersey · GitHub
jersey2で確認するときはGrapeのところ次のように書き換えます。
@Grapes( @Grab(group='org.glassfish.jersey', module='project', version='2.0-m08-1') )
*1:grepcodeの奴が見やすいので、そっちを貼ったけど、状況はjerseyの1.14でも同じ。