ミームの死骸を待ちながら

We are built as gene machines and cultured as meme machines, but we have the power to turn against our creators. We, alone on earth, can rebel against the tyranny of the selfish replicators. - Richard Dawkins "Selfish Gene"

We are built as gene machines and cultured as meme machines, but we have the power to turn against our creators.
We, alone on earth, can rebel against the tyranny of the selfish replicators.
- Richard Dawkins "Selfish Gene"

パーツ買い集めてキーボード (Ergodox) 組み立てるのがめっちゃ楽しい話

先月 Ergodox を組み立てたのだけど,プリント基板を発注したりはんだ付けしたりと,なかなか楽しい体験だったので記録を残そうと思う.

f:id:Hash:20191224180445j:plain
キースイッチはんだ付け作業途中の図

先にでぃすくれいまー書いとくと,自作キーボードのガチ勢な方々は基板設計から始まるっぽい*1から,僕がやった「パーツ買い集めて組み立てる」というムーブはプラモを説明書通りに作っただけで,エンジョイ勢レベルになると思う.技術的にすごいことをやっているわけではない.そこをご理解頂いた上でお読みください.

久々にブログ書くんで戯言欲を持て余し,クソ長く,いらん無駄話も入るけどまぁよかろ.

Ergodox ってなんじゃいな

Ergodox についての詳しい説明は省くが,僕が 2016 年頃から愛用している,オープンソースのセパーレト式メカニカルキーボードである.

f:id:Hash:20200103144457j:plain
こんなの

キーボードがオープンソースとはどういうことかと言うと,キーボードのプリント基板 (PCB) が GitHub に公開されていたり,マニュアル*2をもとに有志が組み立て手順をネットに上げていたり,キーボードファームウェアとしてこれもオープンソースqmk_firmware を採用していたり,みたいな.PCB にキースイッチを初め諸々のパーツをはんだ付けで接続していくことになるのだが,必要なパーツは秋葉原や通販で揃えることができて,あっちこっち探し回るのが RPG みたいでわくわくする.

ファームウェアは C 言語で書かれていて,自分好みにキーレイアウトをカスタマイズすることが出来る*3.僕のキーレイアウトは qmk_firmware を fork して自分のレポジトリに置いているのでよかったら見てみてください.QWERTY ベースなので基本フツーだけど.

f:id:Hash:20200103054255p:plain
僕の Ergodox キーボードレイアウト (layer #1)

見たとおりキーの数が多くて(片手 38 キー x 2 = 76 キー),特に親指周りが充実しているのが嬉しい.基本的に US キーボードレイアウトで使っているが,もちろんあらゆるキーボードと同様に Karabiner-Elements と組み合わせることで Mac 純正日本語キーボードのように左右親指で英数/かな切り替えを実現するなどの拡張も可能.そもそも qmk_firmware 上の定義だけでもわりと色々できてびっくりする.たとえば音量やマウス操作も特定キー組み合わせに割り当てることができるし,僕は Copy/Cut/Paste や Ctrl+F1 (Mac のアプリケーションウィンドウ切り替え) を親指ワンストロークで発行できるようにしている.

さらに Layer を切り替えることでレイアウトをがらっと変えることも出来る.僕は Layer #2 はメールを流し読みして削除フラグ立て等のアクションが片手でできるモード,Layer #3 は数値と記号だけのテンキー状態にしている.

ただ充実しすぎて,正直五段目のキーや親指島,多段レイヤーを 100% 活用しきれてないと思う.なんか面白いアイディアあったら教えてください.詳しい説明は省くとか言いながら割と語ってしまった.

Ergodox 組み立てに至る経緯

さて.Ergodox はオープンソース...だけど,僕は軟弱者なので,Ergodox EZ の組み立て済バージョンを購入して三年半ほど使っていた.三万円くらいしたけど毎日仕事に遊びに活用してきたから完全に減価償却済である.

それが昨年,ついに壊れた.というかキーがいくつかチャタリングを起こすようになってきて,僕が構造をよく理解しないまま,はんだ付けされたキーを外そうと基板を無駄に焼いて動かなくしてしまったので純然たる自爆であるとも言える.

で,軟弱者なので次の Ergodox EZ を購入した.旧 Ergodox EZ は基板だけがダメになってることが予想できたので,購入ついでに Ergodox EZ 販売元である ZSA Technology Labs に「基板だけって売ってないの」と聞いてみた.基板だけ買って復活させれば,自宅と会社,両方に Ergodox のある生活が送れるためだ.すると,

f:id:Hash:20200103063121p:plain
やさしい

返信で CEO Erez さんが直々に「オープンソースだから基板 manufacture すればよくね」とメールくれたので「確かに.そうするわ!」とお返事.Best of luck! :) とのこと.利益にならないのに親身になってくれてやさしい.

なるほど製造業者に基板を発注して新しいやつを入手しつつ,旧 Ergodox EZ を分解してケーブルやケースを流用すればいいだろう.他の人の組み立てブログを読んでケース結構高ぇなと思った記憶がある.他の電子部品はどうだろう? 理想的には基板だけ新しくして既存のパーツは旧 Ergodox EZ から desoldering して回収すれば動かなくもないと思う.でもきれいにはんだ付けを取り外せるスキルは無いし,せっかくだから電子部品は新しく買い集めて作ってみるか.

ようやく始まりである.

基板の発注

まずは何はともあれ PCB の発注.調べたところ,Ergodox の PCB 元データは確かに GitHub 上にあるが,どうやらガーバーファイルという形式で発注しなければならないらしい.そこで,KiCad をダウンロードして Ergodox PCB ファイルを開く.最初混乱したんだけど,面白いことに Ergodox は 1 枚の基板の両面がそれぞれ左手用と右手用になっている.だから,2 枚同じ基板を発注すればいい*4.セパーレト式キーボードだとよくあるのかな.

f:id:Hash:20200111043133p:plain
シンプルで美しい

で,これをガーバーファイル形式で出力する(参考: ガーバーファイルを出力する方法 – Feedback & Ideas for seeed).これでよし.

次に,発注.選んだ製造業者は,Speeed というところが運営する Fusion PCB なる基板製造サービス.個人利用の小規模スケールで基板作ってくれるサービスけっこうあるっぽい.

f:id:Hash:20200103061924p:plain
Fusion PCB 発注画面

これまた「ガーバーファイルたくさんあるんすけど... どれ渡せばいいんですか (´・ω・`)」とメールで担当者に泣き付きながら教えて頂き,無事発注完了.担当者は中国の方だったみたい.世界はやさしい.

以下のファイルを zip にまとめて web から基板製作を依頼.送料込みで $73.2 (=~ ¥7900) くらいかなぁ.

f:id:Hash:20200103154929p:plain
Ergodox 基板製造に必要なガーバーファイル

人生をやっていると,確か一週間くらいで中国から基板が届く.結構速い.

f:id:Hash:20200103064125p:plain
片面が左手用,裏返すと右手用

ちなみに発注後,実はガーバーファイルも GitHub レポジトリに存在することに気が付いたので KiCad のくだりは完全に無駄足であった.でもまぁ基板設計 CAD いじくり回すとかいい経験だろう.KiCad 回路図のネットワーク配線情報が一部 S 式? で書かれているようで脳の配線がバグる.

f:id:Hash:20200103064906p:plain
KiCad 回路図定義における S 式らしきもの

なお本件は総じてこのような無駄足による経験が積めて最高だった.仕事だと,なかなか知識欲ドリブンの無駄な遠回りを楽しめない.

パーツを揃える

Ergodox 基板をゲットしたので,その上にはんだ付けしていくパーツを揃える.公式サイトによると全パーツリストは以下の通り.

f:id:Hash:20200103124726p:plain
知らん単語ばかりでググりまくった

整理する.

個数 値段小計 英語名 解釈 入手場所
1 ¥7900 Pair of pcbs (one for each hand) PCB 基板 1 組(左右の手で計 2 枚).先の段落で発注したやつ Fusion PCB にて最小発注枚数の 5 枚で注文
1 ¥1200 Teensy USB Board, Version 2.0 Teensy なる USB マイコン Teensy 2.0 USB Keyboard Mouse AVR for arduino ISP Board - AliExpress からゲット
24 ¥0 Teensy header pins, male (unless pre-installed) Teensy を基板から浮かせる形で指すトゲトゲしたやつ ↑で Teensy 2.0 買ったら付いてた
1 ¥635 MCP23018-E/SP I/O expander 最大 16 本の I/O を I2C プロトコルを使って 2 本で読み書きできるようにしてくれる IC I/O EXPANDER I2C 16B 28SDIP MCP23018-E/SP - マルツオンライン *5
76 ¥8500 Cherry MX switches 各キーに一個一個つけるキースイッチ*6.好みは無数だけど僕は Cherry 赤軸を選択 10ケセット Cherry MXキースイッチ(赤軸) メカニカル押しボタンスイッチ ジェイダブルシステム を 8 個注文
76 ¥200 1N4148 diodes, SOD-123 package (Surface mount) or DO-35,(0.3” pitch) (through hole) 76 個のダイオードを全キーにちまちまと付ける.ダイオードは電流を一方向にしか流さない.最初にエンドレスはんだ付けで心を折ってくる 秋月電子で 50 個セットのを 2 パック
2 ¥100 2.2k Ω resistors (red, red, red) 抵抗.そうそう抵抗値によって線の色が違うんだった.中学の授業でやったな 秋月電子で最小単位 100 本
3 ¥0 3mm T1 LEDs これ結局買ってない.なんか基板上に付ける場所なくない? USB 挿した時に光らせる部分だと思う --
3 ¥100 220 Ω resistors, or match to LED. (red, red, brown) ↑の 2.2k とは違う 220 抵抗.線の色が赤・赤・茶だ 秋月電子で最小単位 100 本
5 ¥0 Short jumpers (You can also use the clipped off legs from your resistors) これなんだかよくわからずに秋月で「ジャンパワイヤ」みたいなの買っちゃったんだけど,不要だった.抵抗はんだ付けして切り取ったゴミ...じゃなくて足部分を使えば OK --
1 ¥100 0.1 µF ceramic capacitor (marked “104” for 10*104 picofarad) セラミックコンデンサカイガラムシみたいな形してやがる.必須じゃないけどあればベター*7とのこと.電圧を安定させる効果があるらしい? 秋月電子
1 ¥110 USB mini B connector WM17115 USB mini B のメス.お使いの PC から繋ぐ口 千石電商 (秋月電子にはなかった (間違えて表面実装のモデルを買ってしまったため through hole モデルを買い直すという二度手間が発生))
1 ¥256 USB mini B plug with short cable (such as H2955) これ意外と難関.作り進めてようやく何のことか理解したのだが,USB mini B ケーブルをご用意してUSB ケーブルを切ってほぐして繋ぐという外科手術を行う必要がある.分解するのけっこう大変なので,もしかすると USB mini B オス側を買って,自分でケーブル繋いだほうが良いかも知れない エレコム USBケーブル 【miniB】 USB2.0 (USB A オス to miniB オス) ノーマル 0.5m ブラック U2C-M05BK
1 ¥0 USB cable male A to male mini B これは要はあれです.お使いの PC とキーボードを繋ぐやつ 旧 Ergodox EZ についていたやつをそのまま利用
2 ¥700 3.5 mm TRRS sockets, CP-43514*8 左右のキーボードを接続する際の受け口.調べて驚いたんだが,TRRS って イヤホンを差し込むあの丸いジャコっとした手応えのやつの規格らしい.不思議! CONN JACK 4COND 3.5MM R/A SJ-43514 - マルツオンライン
1 ¥0 Cable with two 3.5 mm TRRS plugs 左右のキーボードを繋ぐ線.↑の TRRS sockets 間を繋ぐよ 旧 Ergodox EZ についていたやつをそのまま利用
1 ¥0 Ergodox Keyboard case 見た目を左右するやつですね.キーボードケース.僕は旧 Ergodox EZ のケースが流用できるぜと気楽に考えていたんだが,後述するようにこれが大変だった. 旧 Ergodox EZ についていたやつをそのまま...いや,一部加工して利用
76 ¥0 MX Keycaps キースイッチの上に被せるやつ.僕は Ergodox EZ 無刻印の黒が気に入ってるのでそいつを流用.無刻印であるとはすなわちノーヒントで組み立てる必要があるということ 旧 Ergodox EZ についていたやつをそのまま利用

実物写真などは,はんだ付けしていく段階で改めて乗せる.

値段(通販の場合は個々の送料含む)を合計すると ¥19800 程になる.一方で組み立て済 Ergodox EZ を買うと,付属パーツ等を含めて $325 (=~ ¥35000) くらい*9.自作すると Ergodox EZ で買う時と比べて 60 % 程度の価格で入手できるが,激安!ってほどじゃないし時間もかかるので,組み立てプロセス自体に楽しみを見いだせる人向けの選択肢かなぁやっぱり.細々した電子部品は誤差レベルの安さなので,基板をもっと安価に作る手段があれば,ほぼキースイッチの値段だけで作れそうな気もする.あ,あと僕は特殊だったけど,ふつうはこれに加えてキーボードケースも何らかの方法で入手する必要があるな.

秋月電子千石電商は通販もやってるみたいだけど,僕が買ったのは秋葉原にある実店舗.秋月電子千石電商に足を運ぶついでに,秋葉原から歩いて行ける自作キーボード専門店 遊舎工房 にも寄ってみたり.

f:id:Hash:20191019174651j:plain
遊舎工房にて.どうしてキーボードはこうも俺たちの心を躍らせるのか

楽しい.

はんだ付けの素振り

そもそもはんだ付け自体がたぶん学生時代以来*10だしあのへんの所作 (?) がよくわかっていないので,いきなり貴重なパーツをダメにする悲劇を避けるべく,まずは安い工作キットを買って,なんとなく素振りをしておいた.

HiLetgo® 5個セット NE555 + CD4017 流水ランプ ライトLEDモジュール DIYキット 電子プロダクションスイート [並行輸入品]

HiLetgo® 5個セット NE555 + CD4017 流水ランプ ライトLEDモジュール DIYキット 電子プロダクションスイート [並行輸入品]

できた.

楽しい.

はんだ付け

まずは道具を買いましょう

何はともあれはんだごて.僕は一度よくわからん安いの買っちゃったんだけど,最初から白光 (HAKKO) の温度調節機能付きのやつを買ったほうが間違いないと思う.

白光 ダイヤル式温度制御はんだこて FX600

白光 ダイヤル式温度制御はんだこて FX600

  • 発売日: 2012/01/18
  • メディア: Tools & Hardware

はんだ付けは 270 ℃,はんだを溶かして除去する時は最高の 500 ℃でやっていた.以下,その他に僕の買ったもの.

以上の仲間たちを使ってはんだ付けしていく.

組み立て手順は何度も言及している Ergodox 公式サイト のほか,旧 Massdrop のページ がわかりやすい (archive だけど).まぁでも,実際の作業をイメージするためには動画がベストかも.

www.youtube.com

難しいパーツがある場合は ErgoDox Assembly Part 7: Soldering the MCP23018 I/0 Expander - YouTube この人のシリーズがいいと思う.個々のパーツをはんだ付けする手順を丁寧に解説している.

ダイオード * 76

最初のステップがダイオード付けなんだけど,最初の最初で「ダイオードの陰極・陽極」と「基板の表・裏」をちゃんと理解してスタートするところがちょっとハードル高い(気がする).

そもそもダイオードとは,という説明を Wikipedia のダイオード項目 から引用する.

アノード(陽極)およびカソード(陰極)の二つの端子を持ち(この用語は真空管から来ている)、電流を一方向にしか流さない。すなわち、アノードからカソードへは電流を流すが、カソードからアノードへはほとんど流さない。このような作用を整流作用という。

f:id:Hash:20200106040932p:plain
電圧をかける方向が反対になるとダイオードが電子を流さなくなる

つい先日会社で低レイヤー勉強会があり,ちょうど量子力学のバンド理論半導体,PN 接合ダイオードあたりを勉強した所なのでタイミングがよかった.

さて,まず実際のはんだ付け作業における「ダイオードの陰極・陽極」について.ダイオードを付ける穴は各キーの下部にあるんだけど,よーーーく見ると,はんだ付けの穴の周りの金属が「丸」と「四角」で微妙に差が付けられている.ダイオードのカソード(Cathode/Kathode: 陰極)側には黒線などで印が付けられているものらしい.

f:id:Hash:20191215223531j:plain
方向を間違えないようにメモ

f:id:Hash:20191217020950j:plain
ダイオードの黒い線がある側(カソード)を四角で彩られた穴に突っ込む

ところで,ダイオードには "through hole (足を基板にぶっ刺すやつ)" vs "surface mount (表面実装)" という区別がある.ダイオードにはというか,割とよくある分類っぽい.僕は深く理解せず through hole を選択したから足をぶっ刺して裏からはんだ付けする形で作業を進めたけど,surface mount の方が(ちまちまとして難しくなりそうだけど)すっきりまとまって良かったかも知れない *11

次に「基板の表・裏」について.未来の話をすると,ダイオードを付け終わったら基板をひっくり返すことになる.別の表現をすると,ダイオードより後のはんだ付けは,すべて「ダイオードとは逆側に付けていく」.だから右手用に使う基板であれば "LEFT HAND" と印刷されている側にダイオードを 38 個つけて,左手用に使う基板として "RIGHT HAND" とある面にダイオードを 38 個付けるのである.

f:id:Hash:20191217030904j:plain
これを 76 回繰り返す!

気合.ちなみに全キーにダイオードが必要な理由は Keyboard Matrix Help8. Getting Rid Of Ghosting and Masking に書いてる.

先に「ダイオードを付け終わったら基板をひっくり返すことになる」と書いたとおり,このタイミングで基板を裏返す.次の電子部品「抵抗」以降のはんだ付けは,すべてダイオードとは反対側にやっていく.

抵抗 (2.2k Ω * 2 / 220 Ω * 3)

ダイオード地獄を終えたら抵抗.こいつは楽チン.はんだ付けぐあいもちょうど良い.ダイオードづくしの中ですっごく爽やかな存在だ.

f:id:Hash:20191218223315j:plainf:id:Hash:20191218224520j:plain
これをこう

基板上,左側にはんだ付けされたやつが 赤・赤・赤 すなわち 2.2k Ω 抵抗で,中央と右側にはんだ付けされているのが 赤・赤・茶 こと 220 Ω 抵抗.

ここで一点,気をつけるべきことがある.先の部品リストで Short jumpers * 五本というものがあったのだが,You can also use the clipped off legs from your resistors とあるように,はんだ付けを終えた抵抗たちの足をパチンと切ったら,それを捨てずに取っておく.

そんなもん何に使うのかというと…

f:id:Hash:20191218233712j:plain
お前がジャンパになるんだよ!

見えるかな.切り取った抵抗の足を使って,左右基板 3.5 mm TRRS ソケット取付箇所のまわりにある白い丸で囲われたニ穴を連結させてやる.色んなことするんだな.

3.5 mm TRRS ソケット

じゃあ次は二穴連結によってお膳立ての終わった 3.5 mm TRRS ソケットをはんだ付け.

f:id:Hash:20200103201203p:plain
左右キーボード繋ぎ口なので両方にある

そろそろはんだ付けにも慣れてきた.

Teensy 2.0

Teensy 2.0 は Ergodox の頭脳であり,Ergodox を "programmable" にしてくれている立役者だ*12.Teensy 2.0 は ATmega32U4 プロセッサ が乗っている 8bit マイコンボードで,キー入力をコンピュータに送るシグナルに変換する.ATmega32U4 は Arduino シリーズの一部にも使われているものらしい.

Teensy のバージョン 2.0 は少々時代遅れだったりする?のか,入手経路が乏しくてちょっと困った.スイッチサイエンスは v3.x+ 系からしか売ってないっぽい千石電商にそれらしきパーツも見つけたが,確か Teensy 2.0++ だった.結局 Teensy のために Aliexpress アカウント作ったよ...

f:id:Hash:20200111034032p:plain
トゲトゲしたのは header pins.一緒についてきた

これをこうして

f:id:Hash:20191219200957j:plain
トゲトゲを合体させて自立させた様子

こう(はんだ付け)じゃ

f:id:Hash:20191219202105j:plain
ちょうど裏が I/O Expander 部分なのね

ところで Ergodox はメカニカルキーボードの一種である.メカニカルキーボードのメカニカルたる所以は,各キーが物理的なスイッチとして機能するところ,と理解している.知らんけど.すなわち,メカニカルキーボードのキーが押されて通電して,それが Teensy への入力 (I/O) として現れる.

でも,Ergodox のキーは全部で 76 個,片手でも 38 個.Teensy はそんなに沢山のキー I/O を受け取れるものだろうか?Teensy and Teensy++ Schematic Diagrams から,Teensy 2.0 のスキーマを見てみよう.

f:id:Hash:20200111132640g:plain
Teensy 2.0 のしくみ

右側の F0, F1... B0 と並んでいるところが I/O ピンのはずで,せいぜい 20 個かそこらといったところ.1 I/O を 1 キーに割り当てるためには全然足りない.

ここで Ergodox はどうしているかというと「組み合わせ」を使っている.Ergodox はキーの配置が独特だけど,配線としては片手あたり 6 x 7 のマトリックスで定義されている.6 x 7 の組み合わせでキーを特定するから,受け取る I/O としては片手 6 + 7 = 13 個の I/O を割り当てれば事足りる.先述の回路図で "ROW", "COLUMN" 系のラベルを繋いでやれば,以下のような図が現れる*13

f:id:Hash:20200111133151p:plain
Row x Column でキーを特定する

これで 6 x 7 (以下) のスイッチ ON/OFF を 13 個の I/O で賄うことが出来た.Teensy は受け取る I/O からプログラムにもとづいて,どのキーが押されているのかという情報を逆算することが出来る.

この"プログラム"が qmk_firmware であって,先に僕の GitHub レポジトリを例としてあげたように,勝手にカスタマイズしてビルドすることが可能.ビルドされたファイルは Teensy Loader というアプリケーションを使って Ergodox に送り込むとラク.その後 Teensy 2.0 上の物理的なボタン (リセットボタン) を押すと,送り込んだプログラムが有効になる.

右手はこれでいいが左手はどうするの,という疑問が浮かぶかも知れないがそれは後で.

USB mini B の工作

これが意外な難所.本件のゴールは単純で,はんだ付けの方向の都合上 Teensy に付いてる USB mini B の口がキーボードに対して「ヨコ」を向いちゃうので,ケーブルを継ぎ足して「タテ」というか上っかわに口を開けたい,というだけのもの(と理解している).

f:id:Hash:20191219202414j:plain
mini B がぜんぜん mini に見えないサイズ感

ところが単純に mini B ケーブルを突っ込むと,上の図からわかるように,もう根本の"持ち手"だけでサイズオーバーしてしまう.だから...

f:id:Hash:20191219202518j:plain
ケーブルぶった切って

f:id:Hash:20200111040530p:plain
ほぐして

f:id:Hash:20191223045417j:plain
ニッパーでひん剥いて

f:id:Hash:20200111041341p:plain
はんだ付け!

あとは...

f:id:Hash:20200111040914p:plain
USB mini B の受け口をはんだ付け

最後の USB mini B の口は穴にぶっ刺してはんだ付けする必要があるので「基板実装」型じゃなくて「through hole」型を選ぶこと.以上.電子工作に慣れていないこともあってか,このフェイズが結構面倒だった*14

MCP23018-E/SP I/O Expander

さて次は I/O Expander だが... 僕はこれの存在意義が今ひとつピンとこなくて,ずっともやもやしていた.でも今回組み立てを機に調べてみて,なんとなく自分なりに腑に落ちたので,ちょっと整理してみようと思う.

右手キーボードには Teensy 2.0 がいて,キー入力をシグナルに変換してくれていた.右手側のキー入力をわずか 13 個のピンに繋ぐだけで捌き切ることができた.ありがたい.でも左手はどうするの?という疑問が宙ぶらりんになっていた.左手のキー入力は誰が面倒を見てくれるのか?左手にももう一個 Teensy 2.0 を配置する?それとも,全キーからの出力を何本ものケーブルで右手側に繋ぐ?...どれもうまくない.そこで,ヒーローの如く颯爽と登場するのが MCP23018-E/SP I/O Expander である.

僕は Teensy 2.0 を右手側の基板にくっつけた.一方で MCP23018-E/SP I/O Expander は左手側にある.これは偶然ではない.

MCP23018-E/SP I/O Expander のデータシート から仕組みを読み解いてみよう.まず MCP23018-E/SP の E は temperature range であり,E = -40°C to +125°C を表す./SP は Package を示しており,SP = Skinny Plastic DIP (300 mil Body), 28-Lead だそうだ.SPDIP package とも記載される.アーキテクチャとしては MCP23018 の箇所だけ気にすればいい.以下の通り.

f:id:Hash:20200114224704p:plain
MCP23018 アーキテクチャ

大切なのは,Open-drain と記載されている右側の「GPA0-GPA7, GPB0-GPB7 の計 16 本」と,I2C と書かれているボックスから出ている「SCL (Serial Clock Line), SDA (Serial Data Line)の 2 本」.MCP23018-E/SP I/O Expander を使うと,最大 16 の GPIO (General Purpose I/O) を,わずか 2 本の SCL, SDA ラインで読み書きすることが出来るようになる.16 I/O あれば Ergodox 片手分である 6 + 7 = 13 本の I/O をカバーできる.これで左手 38 キーを賄う I/O 13 本をわずか 2 本に集約して,I2C と呼ばれるシンプルなプロトコルTRRS を介して「右手側 (Teensy が存在する側)」に送ることが出来る.だから,PC から Teensy の存在する右手側のみに USB ケーブルを繋げば,両手分のキー入力がゲットできるという戦略なのでした.

そして各ピンの名前.

f:id:Hash:20200114230838p:plain
MCP23018 各ピンの対応

というわけで,MCP23018-E/SP I/O Expander をはんだ付けしていく.

f:id:Hash:20191218224934j:plain
予備含めて 2 個買ったの忘れてて開封時に「えっ長」ってなった

f:id:Hash:20200103191104p:plain
印字が上下逆さまになる向きが正しい

ところで,MCP23018-E/SP I/O Expander では,上の画像の黄色で囲った 3 穴だけ穴の周辺にはんだ付けする金属部分がない. これどうするの? と迷ったけど ErgoDox Assembly Part 7: Soldering the MCP23018 I/0 Expander - YouTube を見ると飛ばしていいらしい.INTA, INTB, NC を利用していないっぽい.

次に,MCP23018-E/SP I/O Expander の謎の場所にセラミックコンデンサキャパシタ)を追加する.

f:id:Hash:20191218231327j:plainf:id:Hash:20200103194645p:plain
これをこうじゃ

参考に General Purpose I/O: How To Get More | Hackaday らへんを読んでいると,カウンターチップ CD4017 が言及されている.これは,先の "はんだ付け練習台" として作ったおもちゃに使われていた電子部品だ."CD" は CMOS Decade counter IC の冒頭から来ている.閑話休題コンデンサキャパシタ)は電荷を蓄え安定させる電子部品(ref: コンデンサとは? | 村田製作所 技術記事).何故 I/O Expander のこの足にくっつけるの? 先の足の配置と対応させると何か見えるかなと思ったが,これもともと裏側で Teensy のはんだ付けに使われてた穴だし何の意味あるのかよくわからない.電圧を安定させる効果があるらしい,というところまでしか理解できていない...

ケースをめぐる困難・分解編

さて.基板にパーツをはんだ付けして作っていくのはいいが,最終的に剥き出しのまま使うわけにも行かない*15.キーボードケースが必要になる.実は Ergodox GitHub レポジトリにはキーボードケースの設計図も入ってるから,これをどうも 3D プリンタで出力すればケースが出来上がるっぽい.

僕の場合は幸いというか何というか三年半使ってきた旧 Ergodox EZ のケースがあるので,これを流用すりゃ楽勝じゃん,と.

これが地獄の始まりだった.

ネジを外して分解していっても,一向に古い基板がケースから外れてくれない.よくよく構造を観察すると,下から「基板 - ケース - キースイッチ」の順になっていて,キースイッチでケースの枠を挟み込んではんだ付けされている.つまり,全部の取り付け済キーのはんだを一個一個溶かし(desoldering)て基板から取り外し切らないとケースがフリーにならない.まじかよ.

というわけで...

f:id:Hash:20191217235533j:plain
はんだ吸い取り器でキーを外して行く

f:id:Hash:20191218005516j:plain
うおー

最初は goot はんだ吸取り線 CP-30Y を使っていたんだが,吸い取り器の方が効率良かった.はんだごてで溶かしたところをすかさず吸い取る.単にバネじかけで空気を吸っているだけでけっこうギミックがチャチだから,数をこなすと割とすぐヘタる.

無駄に時間をかけて全キーを desoldering,ケースを入手.

f:id:Hash:20191218011312j:plain
分解完了

一番簡単と思っていたケースの分解が一番大変だったという.まるで人生のようだ.

ケースをめぐる困難・合体編

ケースが分解できたらあとは被せるだけ... と思いきや,いくつか問題に遭遇.どうやら Ergodox EZ はオープンソースの PCB ボードをそのまま使っているのではなく,たぶん組み立ての効率のためだと思うが,いくつかカスタマイズしているようだった.

f:id:Hash:20191224161823j:plain
Ergodox EZ,地味にカスタマイズしてる

たとえば↑のような違いがある.ケースを被せると出っ張りがぶつかったりするので

f:id:Hash:20191224155709j:plain
ケース被せるとぶつかる

これを削ったり,

f:id:Hash:20191224155841j:plain
出っ張りを雑に削った図

f:id:Hash:20191224155922j:plain
よしハマった

ケースの穴が Teensy RESET 物理ボタンの場所からずれていて,

f:id:Hash:20191224161759j:plain
Teensy RESET スイッチが押せない

はんだごてで開通 (& カッターで削る) したり.

f:id:Hash:20200115001510p:plain
プラスチックの焼ける匂い

色々苦戦しましたが,なんとか収まる.

キースイッチ

ようやくクライマックス.僕は Cherry MX 赤軸 が好み.

f:id:Hash:20200115002744p:plain
Cherry MX 公式サイトより

こいつを下から「基板 - ケース - キースイッチ」の順になるようにケースを挟み込んでぶっ刺して,はんだ付け.76 キー全部!

f:id:Hash:20191224160706j:plain
台にされるタネンバウム先生の本

f:id:Hash:20191224161027j:plain
よこから

f:id:Hash:20191224160842j:plain
キースイッチと基板でケースをはさむ

f:id:Hash:20191224182416j:plain
キースイッチをはんだ付けした様子

ゴールが近いのと,キーをひとつひとつ付けているという実感から楽しさがマックス.

f:id:Hash:20191224184346j:plain
どんどんいくよ

f:id:Hash:20191224193423j:plain
できたああああ!

いえーい

ジグソーパズルのキーキャップ

キースイッチがついたので,この時点で USB ケーブルを接続して,ちゃんとキー操作が検知されることを確認.おk.

いよいよ最後はキーキャップを被せていくだけ.無刻印を選んだので,どのキーがどのキャップかわからなくてほぼジグソーパズルでしたね.

f:id:Hash:20191224200324j:plain
ここまではわかる

f:id:Hash:20191224201803j:plain
横一列の高さをあわせて...

完成!

f:id:Hash:20191225151315j:plain
やったぜ

いまのところ問題なく動いてます.なんか嬉しい.

いじょ

途中でけっこう語るべきことは語ってしまったのでシメに書くべきことは特段... まぁ,ぼくも社会に出て働き始め 2020 年でまる 10 年になります.早々に世界からフェイドアウトするかと思いきやしぶとく楽しく生きています.やっていきましょう.

*1:自作キーボード設計入門 は僕も買わせていただきました.KiCad の使い方なんかは特に参考になった

*2:先述の公式サイト 2.2 ASSEMBLY GUIDE が一応公式なものになるだろうか

*3:まぁ別に C の知識は必要ないけど

*4:でも最低発注枚数が 5 枚だったので,左手と右手に使ったあと 3 枚余ってるんだよな

*5:秋月電子に MCP23017 はあったが MCP23018-E/SP がない

*6:標準 Ergodox レイアウトだと 76 個だと思うんだけど,公式ページでは 76-80, depending on your layout との記載.80 個使うレイアウトもあるの?

*7:Not strictly necessary but suggested

*8:FC68129 will also work if its extra pins are snipped off

*9:Ergodox EZ は台湾からの発送となり,送料無料.ありがとう台湾

*10:大学院時代に生体分子の質量計測器を自作する研究室にいたのでちょっとだけやった記憶があるが,もうその前といえば中学校とかに遡ってしまう

*11:Ergodox EZ を分解してみたところ surface mount タイプのダイオードでした

*12:たぶん

*13:図は https://kandepet.com/dissecting-the-ergodox-the-ergonomic-programmable-keyboard/ より拝借

*14:ニッパー捌きに自信がないから予備のために無駄に mini B ケーブル買っちゃって余ってる

*15:こんなふう引用元)に裸で使うのも,それはそれでカッコ良さそうだけど

インド出張から得た英会話の学び

三週間ほど海外に出張していた.インドである.趣旨としては,僕が持ついくつかの領域における専門知識と経験をあちらのエンジニアに展開する,先生役として呼ばれた形..になるだろうか.単にセミナーとかを聞くだけの出張なら今夏にも行ったところだが,三週間みっちり先生をやるとなるとプレッシャーが違うし,事前にインドのチームと喋ったところ僕に対する期待値が異様に高く,胃の痛い思いをしながら突入することになった.

午前は講義を行い,午後は行列のできる Hash 相談所,夜はホテルに戻って翌日のトレーニング資料を作る... というすたーたっぷ勤務時代を彷彿とさせる社畜業に勤しみ観光どころではなかった (たまたま向こうで働いていた友達に会ったのと,インドチームとビール飲みに繰り出したりはした) ので,費やした時間分は役に立ったことを願う.黒い髭をたくわえた厳ついエンジニアも鋭い眼光の優秀な若者もみんな良い人で,英語の多少の不備は保管してくれるし理解しようとしてくれる.僕としてはけっこう気に入ったのでまた呼ばれたら行きたいと思う.

f:id:Hash:20171222125716j:plain f:id:Hash:20171231160031p:plain

で,英語読み書きはそれなりに馴染んではいたけど,講義から質疑応答から (日本語でも苦手な) 酒の席の雑談から,全部英語で聞いて喋ってを何週間も続けた経験は人生で初と言ってよく,それなりに学ぶところがあったので箇条書きで投げることにする.思い出したらこっそり追記する.

  • it depends 超便利

    • 何か聞かれたときに (特に技術的な質問とか) その場で Yes/No やら確定した答えを返せないことあると思うんだけど,そういうときにとりあえず「場合によるな」と前置きして,それぞれの "場合" を説明して対話を続ける
    • その場で説明出来なくても "it depends" だけ返せば "depends on what?" みたいに返してきて会話が前に進む
  • I mean... / what I said is... で説明を補足する

    • こちらの喋った内容がどうも相手にピンと来ていないようであり,かつすぐに質問が返ってこないときは,同じ内容を別の言い方で伝え直す
    • この項目に限らずだけど日本語でもスムーズなコミュニケーションのためにやることだなこれ
  • well... / let me xxx... で場を繋ぐ

    • 講義中に質問を受けたり,説明のために準備が必要なときに口ずさむ (?) フレーズとして使っていた
    • トラブル調査するときにログファイル開きながら let me check the log file... とか
    • これはどちらかというと相手に何か伝えるというより,自分のための面が大きい.少なくとも僕は,脳内で常に英語での思考を続けてリズムを保たないとダメになるのでやっていた
  • 進捗どうですか ... How's xxx going? / あるいは単に How is it going?

    • It's going well, 終わった話だったら It went well, 問題があれば we have some problems
    • xxxer than I expected
  • I'd like to を多様

    • ネイティブ的に自然かどうかは知らないけど,よく使ってた.あとなんか聞かれたら I prefer
  • I feel (felt) でほんのり意見

  • Let's say, で例示

    • 一般的な説明をしたあと「例えば」で具体的な話をする流れはよくあると思う.そこで仮定を置くときに lets' say... が便利だった
    • for example, でもいいんだけど,気分的には for example でビシッと個別例を出すというよりはもう少し軽く,仮に xxx だとしよう.そんなときは yyy になるんだよ.という短いフレーズの例示に使いやすいのかなという印象を受けた
  • 学校英語で習ってそのまま忘れてた isn't it? みたいなお尻に付ける質問文, 実戦では便利

    • 疑問文にし忘れて喋り始めてしまったときに最後に付ければ体裁が整う
    • 文法用語的には tag question というらしい
  • seriously? はほぼ「マジで?」みたいに使える

    • seriously↓, seriously↑!? などイントネーションでニュアンスが変わるところも「マジで」っぽい
    • really? も同様に使える模様
  • 聞き取れなかったら sorry? で聞き返す

    • ほんと実感したけどわからないとき聞き返すの超大事.わからないのは普通.
      • お互いに一発でコミュニケーションが通らないことを想定して喋るようにする
    • pardon? は失礼という話をどこかで聞いたし実際向こうでも使ってる人は見なかったので, sorry (あるいは I'm sorry?) 一本で聞き返し続けた
    • 速度が速すぎたら sorry, could you speak slowly? とお願いする
    • 特定の単語だけ聞き取れなかった (たとえば it was caused by xxx, で xxx が聞き取れなかったとする) のなら,sorry, you said... it was caused by... what? とわからないところを what で置き換えて疑問文ぽいイントネーションで聞き返せば伝わった *1
  • 言葉としては聞き取れたけど意味がピンとこなかった時は

    • what do you mean? とか,what do you mean is ... とこちらの言葉で言い換えてみる
    • I couldn't get what you said, のあとにそれってつまりこういうこと? と続けるのでもよい
      • get を「理解する」という文脈で使えるようになってからテクニカルな内容を話すのがすごい楽になった
    • あとは for example? でもう少し細かい話を聞き出す
  • ok, ok... で相槌を打つのらくちん

    • 「それな」でビシっと同意したいときは "exactly"
    • 何か許可とか同意を求められたときは "sure"
    • ところでインド人のジェスチャーで最初混乱するのが 頭をゆらゆらとする,一見 No に見える Yes で,会話の中でもよく頭をゆらゆらしてる.僕も影響されて最後の方はゆらゆらしてた気がする
  • "cool"

    • くぅ, みたいな発音で相槌に使っていた.ok, よりも会話の流れを終わらせる傾向が強い
    • 女の子はあまり使わないみたいだった
  • 講義の中で does it make sense? so far any question? とかで質問を引き出してみる

    • これは先生役で行ったのでよく使ったフレーズ
    • とはいえ基本的にみんな積極的に講義中に割り込んで質問してくるので,日本人相手みたいに質問ターイムを設ける必要はあんまなかった
  • Royal We

    • "Royal We" という概念を英会話教室 (後述) で教えてもらって,個人的に気に入ったので We を意識的によく使ったという話
    • I と You で分けずに同じ問題に取り組む仲間として "We" を使う
    • チームのパフォーマンスを上げるためにこれこれをやると良いと思う,というときに "you" とか "your team" ではなく "we" を使う,的な

インドはヒンディー語公用語として定められているものの,実際は地方ごとにまったく別の言語を喋っていて,オフィスに居るエンジニアたちもそれぞれの home town language が異なるそうだ.そこでコミュニケーションを取るためには bridge language としての英語を使うしかない.そういう意味で彼らにとっても英語は「第二外国語」と言ってよく,ぼくのような外国人が英語に不慣れなところを見せても非常に寛容だったし,お互いのことを理解しようとする姿勢はとても優しかった.インド英語は発音が独特でかつめっちゃ速い,とよく言われるし実際にインド人の間で本気で交わされる英語の会話には付いていくのが難しいが,特に educated な人は基本的に理解されやすい英語を喋るすべを知っているようで*2,僕が「ちょっとわからん」という反応をすると,次からはとても聞き取りやすく喋ってくれる.

一方で Uber のドライバーとかローカルのお店の店員はそもそも英語なのかヒンディー語なのかまた別の言語なのかすらわからず,あるドライバーはマイクつけてて電話の相手と喋りながらおもむろに僕に話しかけて来るので辛かった*3

ところで「日本っていくつ言語があるん?」という質問に最初は戸惑ったが,彼らは同じ国の中でも違う言葉を喋るのが当たり前なのだと気が付く.「みんな日本語使ってるよ.方言 (dialect) でイントネーションとかアクセントは違うけど,書き言葉はどこでも伝わる」と答えると「one country one language, それは最高やね」みたいに感心していた.

雑多な話題

あと日本に関する話題で面白かったのは

  • イギリスには Queen がいるけど,日本にも King がいるよね,そんで今度 Akihito 辞めるんでしょ (よく知ってる)
  • 本の学校ではサムライ・クラスルームがあるのか? ニンジャ・クラスルームは?
    • 剣道なら授業あるけど..
  • 下ネタは世界共通.天丼も笑いの文脈として共通
  • 宗教の話をわりとカジュアルに振られるのがちょっと困った
    • 僕は宗教というミーム自体嫌悪しており... みたいな話はあまり適切とは言えないので,日本では一般的に,という話をしてお茶を濁す
  • 日本では過労死がひどいらしいが,働きすぎた人が道端で死んでるのか
  • useful Japanese として "do-mo" を教え,日本では respect を示すため名字に "-san" を付けるのだと伝えた結果 "do-mo, Hashimoto-san" と呼びかけられる事案が発生

出張以前の英会話のお勉強

基本的にお席に座って地道にやる "お勉強" が大好きな人間なので,この特性を利用して,現場に飛び込む以前になるべくパフォーマンスを高めようと試みた.ひとつは専門知識のぶらっしゅあっぷとトレーニング計画 *4 だが,英語力という面でも何かやっておこう,と考えた.

  • きちんとしたビジネス英会話力を付けるべく Berlitz 英会話 で短期集中受講
    • 英会話教室もいくつか比較した結果,Berlitz に決めた.マンツーマンでじっくり喋ることが出来るのと,"Berlitz Method" と呼ばれる,雰囲気で会話を進めずに文法的に正しい文を作るまでしつこく繰り返させられるというスパルタ方式が気に入った
      • ビジネス英語は必要なくて雰囲気で会話できれば良いのなら,後述の DMM 英会話がちょうどよいと思う
    • Berlitz は老舗だけあり歴史的経緯を拭い去れていないシステムがちょっと非効率だけど,先生やカウンセラがとても協力的で僕の事情やニーズに合わせてスケジュールをカスタマイズしてくれ,心地よくレッスンを受けることが出来た
    • Gaba もマンツーマンでコスト的にはほぼ Berlitz と並んでおり,システム的にはむしろ洗礼されていたのだが,営業の押し付けが肌に合わなかった *5 のと,個室ではなくて適当に区切られたブースでレッスンするので周りが気になってイマイチ
    • シェーン英会話 の雰囲気とコスパはよかったけど,子供塾みたいな感じで,人生に余裕のあるご家庭が教養としてやるっぽさあった.あと短期集中カスタマイズとか無理そうだった
  • 以前から DMM 英会話 もちまちまやってるが,そのペースを上げてインド出身の先生と喋るようにした *6
    • DMM 英会話を始めたのは一年半前くらいになるだろうか.今見たら英会話時間がちょうど 2000 分になってた
    • 教師のプロフェッショナル感としてはやはり Berlitz には劣る印象だけど,いろんなタイプの人と喋る機会を得る,という意味ではかなりコスパがいいと思う.月 5000 円くらいだし
    • ただまぁ出張に行って英語で 1 週間仕事するだけで 8 * 60 * 5 = 2400 分会話が出来ると思うと,日々のコツコツ,もたかが知れているという見方もできる

f:id:Hash:20171231135039p:plain

  • どんどん話すための瞬間英作文トレーニング』で中学生レベルの英語が瞬間的に出るように鍛える
    • 様々な英語学習法を比べてみて,現在自分に最も適当と思われたので購入
    • 日本語の単文から,英文を瞬間的に作る反復トレーニング.読み書きはそれなりに複雑な事ができるのに喋りに自身がない.. という人に良さそうだったので

どんどん話すための瞬間英作文トレーニング (CD BOOK)

どんどん話すための瞬間英作文トレーニング (CD BOOK)

インドで腹を壊さないためのたった N ツの冴えた方法

先にインドを経験した先人がお腹を壊しているのを見て (僕自身も 6 年前にインドを訪れた際はやられたので),彼らの意見を取り入れた結果,最後まで体調を崩さず元気に過ごすことができた.ポイントは以下:

  • 整腸剤 (ヤクルト BL 整腸剤, 下記リンクあり) を毎食後飲む
  • 水はペットボトルの水だけを摂取すること
    • ジュースやカクテルに入ってる氷に気をつける
    • 歯磨きもペットボトルの水でやる…という話も聞いたが僕はそこまではやらなかった.普通に水道水で歯を磨いたあと,気持ちペットボトル水でうがいするくらい
  • 屋台の食事は食わない
    • 6 年前は喜んで屋台のチャパティとかラッシーとか食いまくってた
  • 単なる栄養摂取で事足りる食事は COMP で済ます
    • 同僚とのランチやディナーは普通に食うが,一人の食事では胃をいたわる,という方針
    • インド,基本的にスパイシーな食事しかないので,三食現地食を食ってると日本人の胃は回復時間が足りないんだと思う

ヤクルトBL整腸薬 36包 [指定医薬部外品]

ヤクルトBL整腸薬 36包 [指定医薬部外品]

いじょ

最後に,そのままでは味気ない食事になりがちな COMP を飲む際のコツですが,上記 COMP ちゃんに飲ませてもらってると思うとバブみがあり満足感が上がります.

*1:文法的によろしくない気はする

*2:高等教育の授業は英語で行われるというのもあるらしい

*3:これ日本語でも辛いと思うけど

*4:なおインドに着いてからいろいろと新たな要望が出てきて,僕の計画は大半ちゃぶ台返しとなった.故に毎晩のトレーニング資料作りに追われたわけでもあるが

*5:顧客のためではなく自分たちの利益のために契約させようとする印象を受けたため

*6:あまり居なかったけど

なぜ何もないのではなく何かがあるのか

書きたいことがない.書きたいことはあるが,それをテキスト化のフローに載せていくことが出来ていない.というわけでこれは無理矢理に文章を出力する試みである.軽く 2000 字を目処に.

一つの思想の真の生命は,思想がまさに言葉になろうとする地点に達するまで持続するにすぎない.その地点で思想は石と化し,その後は生命を失う.だが化石化した太古の動植物のようにその思想は荒廃を免れる.我々は思想のつかのまの生命を,まさに結晶せんとする瞬間の結晶体の生命に比することができる.

ショーペンハウアー読書について』 p.38

仕事ではめっきりコードを書く機会が減り,自然言語を書いている.技術職ではあるが,それを道具としながら日本語と英語を読み書きして人間と社会をやっている.英語はもっと喋れるようになりたいな.論理と自然言語を装備して正論と事実で殴ることのできる今の仕事は案外性に合ってるとは思うが,コードを書いてものを作り続けている人はほんとうに尊敬に値する.

仕事が楽しいのでわりと社畜っぽい働き方をしているが,IT 業界の闇みたいな辛い話がない職場で待遇的にもかなり恵まれているとは思う.現世に楽園は存在しないんで嫌なこともあるが総じてな.成果測定の難しいことをやっているので,給料分はチームの役に立っていることを願う.

学生時代から得意とする絨毯爆撃的な学習を日常業務に活用する方法が自分の中でようやく確立できてきた気がする.この方法を人間的やっていきの瞬発的飛び道具と組み合わせ,いくつかそりっどな成果を得ることが出来た.1000 ページあるドキュメントを片っ端から読んでいったりする人間は少なく,知識獲得そのものにモチベーションを感じて継続できるというのは社会全体から見るとレアな方の気質であるらしい (エンジニア職はそういう気質わりと多いような気もするけど).ともあれ,そういう偏執的な姿勢は使いようによっては社会でも役に立つらしい.院を出て就職したばかりの僕に聞かせてやりたい希望のある話である.しかし油断はできない.この世に慈悲はなく僕自体に価値はなく油断をした瞬間に死ぬことは確定的に明らかである.

もちろん偏執的なだけでは社会をやっていくことが出来ないため,人間とのコミュニケーションにおいては基本的な論理を使って丁寧に事実を整理してみる.整理した論理を言葉に落とし込んで理解のポイントをすり合わせようと試みる.そもそも人間の頭脳は基礎的な論理 (とくに因果関係) プラットフォームに乗りやすいようには思う.

つい先日 「異世界からきた」論文を巡って: 望月新一による「ABC予想」の証明と、数学界の戦い « WIRED.jp なんて記事を読んだりもして,"人間の頭脳の程度がだいたい全部同じくらい" とはとても主張できないのだけど.僕もできることなら天才になりたかった.元々ものごとの理解力が格別優れているわけでもなく,むしろとんでもなく基礎的で誰も見向きしないような場所の石ころに躓いていつまでもそこで途方にくれているような頭脳でしかない.

閑話休題

『生命・エネルギー・進化』

突然ですが最近読んでよかった本の紹介です.

ニック・レーンによる『生命・エネルギー・進化』という本で,原題は “THE VITAL QUESTION – Why Is Life the Way It Is?".著者がかつて『ミトコンドリアが進化を決めた』で展開した論をさらに先へ進めたもの,っぽく,より汎用性の高い語り口として "進化には実は,生命のきわめて根本的な特性の一部を第一原理から予測できるようにする強い制約 – エネルギーの制約 – があるということ” を主張する.

とくにこの制約が顕著に感じられるのは我々真核生物という “異端” においてである.細菌も古細菌も,その化学・遺伝的特性の吹っ飛び方からは考えられないほど,見た目にはほとんど同じである. “まるでそこに内在する物理的制約が原核生物を複雑な形態に進化できなくしているかのようで,その制約はなぜか真核生物の進化では解かれたのである” (p.57) … こうした謎を提起しながら論をすすめる本書はある種推理小説的というか,謎解きの楽しさを与えてくれる.まぁ謎解きというか著者に導かれて"謎の解かれていき" を見ることになるわけだが,そこらへんの事情は推理小説でもさほど変わるまい.

生命 (life) とは何か,ではなく,生きている (living) とは何なのか.生化学畑の学生であったこともあり実に楽しい読書体験であったと言える.

ひそむもの

仕事と生活にある程度の安定感が出てくると,この先どうするという思考が仄暗い水の底から浮かび上がってくる.いまググって知ったけど仄暗い水の底からってパチンコになってんのか頭おかしい.人生でやりたいことはいろいろあるが,僕は本当に人生の進捗が遅く遠回りばかりで,ぐるぐると何かの周りを歩き続ける以外にこれといった存在の使い方もしていないように思われる.

そうしてぐるぐると歩いているうちにある程度は身の程を知ってしまい,自身の無能と現実の現実たる現実に打ちひしがれているうちにぐるぐる回っているときは見えていたはずのものがいつのまにかひどくぼんやりとして来て,視界不良と幸福の定義に悩みながら新しい火薬を手札に加えるべく周回ルートを外れてあてどなく生きていく以外に何も

それはぼくの思いすごし 悪いことばかり思いつく いつもの日曜日の午後

スガシカオ「日曜日の午後」

Spark on EMR で 1000 Genomes Project のデータを触る

なんやかんやで年始から記事書いてないですが, 外部要因がないとブログを書かないようになっている. "Distributed computing (Apache Hadoop, Spark, ...) Advent Calendar 2016" の 12/11 担当として何かを書きます.

qiita.com

1000 Genomes Project public data set on S3

AWS S3 には無造作にいろんなびっぐなでーたが置いてあり,

aws.amazon.com

その中に 1000 Genomes Project のデータがあります: 1000 Genomes Project and AWS.

1000 Genomes Project については公式ページ 1000 Genomes | A Deep Catalog of Human Genetic Variation などを参照して頂ければと思いますが, 複数民族をソースとして匿名の 1000 人以上のゲノム配列を決定しよう, というプロジェクトで既に完了しています. なんで 1000 人も要るかって言うとヒトのみならず生物ってのは同じ種でも個体ごとに微妙にゲノム配列に差があってその差がいわゆる "体質" とか "病気のかかりやすさ" 的な差を生み出しているからで, ヒトの標準的なゲノムは何なのか, ということを知る意味でもある程度の N が必要だからです. ともあれ, そのデータは匿名化された上で公開されており, S3 上にも上がっているというわけです.

aws cli が使えるなら以下のコマンドですぐアクセスできます.

$ aws s3 ls s3://1000genomes
                           PRE alignment_indices/
                           PRE changelog_details/
                           PRE complete_genomics_indices/
                           PRE data/
                           PRE hgsv_sv_discovery/
                           PRE phase1/
                           PRE phase3/
                           PRE pilot_data/
                           PRE release/
                           PRE sequence_indices/
                           PRE technical/
2015-09-09 06:16:09       1663 20131219.populations.tsv
2015-09-09 06:17:01         97 20131219.superpopulations.tsv
2015-09-09 00:01:44     257098 CHANGELOG
2014-09-03 00:39:53      15977 README.alignment_data
2014-01-30 20:13:29       5289 README.analysis_history
2014-01-31 12:44:08       5967 README.complete_genomics_data
2014-08-29 09:22:38        563 README.crams
2013-08-07 01:11:58        935 README.ebi_aspera_info
2013-08-07 01:11:58       8408 README.ftp_structure
2014-09-03 06:19:43       2082 README.pilot_data
2014-09-03 21:33:15       1938 README.populations
2013-08-07 01:11:58       7857 README.sequence_data
2015-06-19 03:28:31        672 README_missing_files_20150612
2015-06-04 04:43:32        136 README_phase3_alignments_sequence_20150526
2015-06-19 01:34:45        273 README_phase3_data_move_20150612
2014-09-03 21:34:30    3579471 alignment.index
2014-09-03 21:32:59   54743580 analysis.sequence.index
2014-09-03 21:34:57    3549051 exome.alignment.index
2014-09-03 21:35:15   67069489 sequence.index

Adam

今回は s3://1000genomes/release/ 配下の多型データを使いますが, これは Variant Call Format (VCF) という形式で保存されておりこのままでは Spark で扱うことが出来ません. そこでマエショリが必要になってくるのですが, Spark でゲノムデータ解析をする際は Adam というパッケージがデファクトスタンダードっぽくなっているので, 以下それを使って Parquet ファイル (相当) に変換します.

GitHub - bigdatagenomics/adam: ADAM is a genomics analysis platform with specialized file formats built using Apache Avro, Apache Spark and Parquet. Apache 2 licensed.

EMR クラスタ (Release label: emr-5.1.0 *1 ) を立て, Adam をビルドします. maven, sbt は適当に入れているものとします. EMR 5.1.0 は Spark 2.0.1 がバンドルされており Scala のバージョンは 2.11.8 なので, ./scripts/ 配下の適切なスクリプトを流す.

$ git clone https://github.com/bigdatagenomics/adam.git ~/adam
$ cd !$
$ ./scripts/move_to_scala_2.11.sh
$ ./scripts/move_to_spark_2.sh
$ MAVEN_OPTS="-Xmx512m -XX:MaxPermSize=256m" mvn clean package -DskipTests

モノがビルドされると, spark-shell, spark-submit を拡張した adam-shell, adam-submit なるコマンドが使えるようになります.

[hadoop@ip-172-31-12-100 adam]$ ./bin/adam-shell
Using SPARK_SHELL=/usr/bin/spark-shell
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel).
16/12/11 13:57:32 WARN Client: Neither spark.yarn.jars nor spark.yarn.archive is set, falling back to uploading libraries under SPARK_HOME.
16/12/11 13:57:41 WARN SparkContext: Use an existing SparkContext, some configuration may not take effect.
Spark context Web UI available at http://172.31.12.100:4040
Spark context available as 'sc' (master = yarn, app id = application_1479477268424_0036).
Spark session available as 'spark'.
Welcome to
      ____              __
     / __/__  ___ _____/ /__
    _\ \/ _ \/ _ `/ __/  '_/
   /___/ .__/\_,_/_/ /_/\_\   version 2.0.1
      /_/

Using Scala version 2.11.8 (OpenJDK 64-Bit Server VM, Java 1.8.0_111)
Type in expressions to have them evaluated.
Type :help for more information.

scala>

VCF to "Adam Format" (Parquet に毛の生えたフォーマット)

adam-submit のサブコマンドに vcf2adam というものがあり, これを噛ませることで VCF ファイルを ADAM Format に変換します. 変換元に先述の S3 1000genomes バケットを指定し, 出力先として自分の手持ちのバケットを指定する感じです. ゲノムデータは染色体単位で分かれて記録されているので, とりあえずトリソミーになるとダウン症を発症する 21 番染色体を選んで変換してみようと思いますが, まずは手元に落として, 少し VCF ファイルを覗いてみます.

$ aws s3 cp s3://1000genomes/release/20130502/ALL.chr21.phase3_shapeit2_mvncall_integrated_v5a.20130502.genotypes.vcf.gz
 /home/hadoop/

// 冒頭部分
$ zcat ~/ALL.chr21.phase3_shapeit2_mvncall_integrated_v5a.20130502.genotypes.vcf.gz | head -n 6
##fileformat=VCFv4.1
##FILTER=<ID=PASS,Description="All filters passed">
##fileDate=20150218
##reference=ftp://ftp.1000genomes.ebi.ac.uk//vol1/ftp/technical/reference/phase2_reference_assembly_sequence/hs37d5.fa.gz
##source=1000GenomesPhase3Pipeline
##contig=<ID=1,assembly=b37,length=249250621>

// 行き来しながら探したところ 253 行目がヘッダーの模様.
$ zcat ~/ALL.chr21.phase3_shapeit2_mvncall_integrated_v5a.20130502.genotypes.vcf.gz | head -n 253 | tail -n 1
#CHROM  POS     ID      REF     ALT     QUAL    FILTER  INFO    FORMAT  HG00096 HG00097 HG00099 HG00100 HG00101 HG00102 HG00103 HG00105 HG00106 HG00107      HG00108 HG00109 HG00110 HG00111 HG00112 HG00113 HG00114 HG00115 HG00116 HG00117 HG00118 ... (省略

// データの中身.
$ zcat ~/ALL.chr21.phase3_shapeit2_mvncall_integrated_v5a.20130502.genotypes.vcf.gz | head -n 1000 | tail -n 1 | head -c 200
21      9440889 rs574517239     A       C       100     PASS    AC=1;AF=0.000199681;AN=5008;NS=2504;DP=13457;EAS_AF=0;AMR_AF=0;AFR_AF=0.0008;EUR_AF=0;SAS_AF=0;AA=.|||;VT=SNP        GT      0|0     0|0     0|0     0|0     0|0     0|0     0|0     0|0     0|0     0|0     0|0     0|0     0|0 ... (省略

では, adam-submit 叩きます. adam-submit コマンドは spark 用のオプションと adam オプションの間に -- を入れる必要があります. ちょっとハマりました.

$ ./bin/adam-submit \
--deploy-mode cluster \
--master yarn \
--num-executors 10 \
--executor-memory 20G \
-- \
vcf2adam \
s3://1000genomes/release/20130502/ALL.chr21.phase3_shapeit2_mvncall_integrated_v5a.20130502.genotypes.vcf.gz \
s3://hash-genome-work/chr21.v5/

行けましたね. コアノード 2 台でやっていましたが, この辺で処理が遅いことに気が付き金で殴るべく m3.2xlarge x 10 台に増設しています. そのスペックで 21 番染色体の VCF の Adam フォーマット変換処理に要する時間が 1.5hr 程度でした.

結果.

$ aws s3 ls --recursive s3://hash-genome-work/chr21.v5
2016-12-12 02:39:55          0 chr21.v5/_SUCCESS
2016-12-12 02:39:56      11895 chr21.v5/_common_metadata
2016-12-12 02:39:56       5548 chr21.v5/_header
2016-12-12 02:39:56      29687 chr21.v5/_metadata
2016-12-12 02:39:56      28835 chr21.v5/_samples.avro
2016-12-12 02:39:56       3466 chr21.v5/_seqdict.avro
2016-12-12 02:39:47  119661147 chr21.v5/part-r-00000.gz.parquet
2016-12-12 02:39:50  117920329 chr21.v5/part-r-00001.gz.parquet
2016-12-12 02:39:52  117938957 chr21.v5/part-r-00002.gz.parquet
2016-12-12 02:39:54   24165424 chr21.v5/part-r-00003.gz.parquet
2016-12-12 01:08:37          0 chr21.v5_$folder$

Parquet 化された多型データを adam-shell から参照

adam-shell で色々見てみたいのですが, AdamContext だと S3 からは直接ロードが出来ず HDFS に置く必要があるっぽいので先に HDFS に書き出します.

$ aws s3 cp --recursive s3://hash-genome-work/chr21.v5 /home/hadoop/chr21.parquet
$ hadoop fs -mkdir /user/hadoop/ch21
$ hadoop fs -put /home/hadoop/chr21.parquet/*.parquet /user/hadoop/ch21/

$ hadoop fs -ls /user/hadoop/
Found 2 items
drwxr-xr-x   - hadoop hadoop          0 2016-12-11 19:18 /user/hadoop/.sparkStaging
drwxr-xr-x   - hadoop hadoop          0 2016-12-11 19:35 /user/hadoop/ch21

$ hadoop fs -ls /user/hadoop/ch21/
Found 4 items
-rw-r--r--   3 hadoop hadoop  119661147 2016-12-11 19:35 /user/hadoop/ch21/part-r-00000.gz.parquet
-rw-r--r--   3 hadoop hadoop  117920329 2016-12-11 19:35 /user/hadoop/ch21/part-r-00001.gz.parquet
-rw-r--r--   3 hadoop hadoop  117938957 2016-12-11 19:35 /user/hadoop/ch21/part-r-00002.gz.parquet
-rw-r--r--   3 hadoop hadoop   24165424 2016-12-11 19:35 /user/hadoop/ch21/part-r-00003.gz.parquet

さて, ようやく Adam Shell からデータを読めそうです.

$ ./bin/adam-shell

scala> val data = spark.read.parquet("/user/hadoop/ch21/*.parquet")
data: org.apache.spark.sql.DataFrame = [variant: struct<contigName: string, start: bigint ... 8 more fields>, contigName: string ... 20 more fields]

scala> data.count
res13: Long = 2785018912

約 27.8 億レコードと, 21 番染色体の variant だけでけっこうなデータ量. スキーマ見てみます.

scala> data.first
res14: org.apache.spark.sql.Row = [[null,null,null,WrappedArray(rs559462325),G,A,true,true,WrappedArray(),false],21,9411238,9411239,[true,true,WrappedArray(),null,null,null,null,null,null,null,WrappedArray(),WrappedArray(),null,null,Map()],HG00096,null,null,WrappedArray(REF, REF),null,null,null,null,null,null,WrappedArray(),WrappedArray(),WrappedArray(),false,true,null,null]

scala> data.printSchema
root
 |-- variant: struct (nullable = true)
 |    |-- contigName: string (nullable = true)
 |    |-- start: long (nullable = true)
 |    |-- end: long (nullable = true)
 |    |-- names: array (nullable = true)
 |    |    |-- element: string (containsNull = true)
 |    |-- referenceAllele: string (nullable = true)
 |    |-- alternateAllele: string (nullable = true)
 |    |-- filtersApplied: boolean (nullable = true)
 |    |-- filtersPassed: boolean (nullable = true)
 |    |-- filtersFailed: array (nullable = true)
 |    |    |-- element: string (containsNull = true)
 |    |-- somatic: boolean (nullable = true)
 |-- contigName: string (nullable = true)
 |-- start: long (nullable = true)
 |-- end: long (nullable = true)
 |-- variantCallingAnnotations: struct (nullable = true)
 |    |-- filtersApplied: boolean (nullable = true)
 |    |-- filtersPassed: boolean (nullable = true)
 |    |-- filtersFailed: array (nullable = true)
 |    |    |-- element: string (containsNull = true)
 |    |-- downsampled: boolean (nullable = true)
 |    |-- baseQRankSum: float (nullable = true)
 |    |-- fisherStrandBiasPValue: float (nullable = true)
 |    |-- rmsMapQ: float (nullable = true)
 |    |-- mapq0Reads: integer (nullable = true)
 |    |-- mqRankSum: float (nullable = true)
 |    |-- readPositionRankSum: float (nullable = true)
 |    |-- genotypePriors: array (nullable = true)
 |    |    |-- element: float (containsNull = true)
 |    |-- genotypePosteriors: array (nullable = true)
 |    |    |-- element: float (containsNull = true)
 |    |-- vqslod: float (nullable = true)
 |    |-- culprit: string (nullable = true)
 |    |-- attributes: map (nullable = true)
 |    |    |-- key: string
 |    |    |-- value: string (valueContainsNull = true)
 |-- sampleId: string (nullable = true)
 |-- sampleDescription: string (nullable = true)
 |-- processingDescription: string (nullable = true)
 |-- alleles: array (nullable = true)
 |    |-- element: string (containsNull = true)
 |-- expectedAlleleDosage: float (nullable = true)
 |-- referenceReadDepth: integer (nullable = true)
 |-- alternateReadDepth: integer (nullable = true)
 |-- readDepth: integer (nullable = true)
 |-- minReadDepth: integer (nullable = true)
 |-- genotypeQuality: integer (nullable = true)
 |-- genotypeLikelihoods: array (nullable = true)
 |    |-- element: float (containsNull = true)
 |-- nonReferenceLikelihoods: array (nullable = true)
 |    |-- element: float (containsNull = true)
 |-- strandBiasComponents: array (nullable = true)
 |    |-- element: integer (containsNull = true)
 |-- splitFromMultiAllelic: boolean (nullable = true)
 |-- phased: boolean (nullable = true)
 |-- phaseSetId: integer (nullable = true)
 |-- phaseQuality: integer (nullable = true)

わお. いくつか取ってみます.

scala> data.first.get(1) // contigName. このデータでは染色体番号が入ってるので 21
res15: Any = 21

scala> data.first.get(0)
res17: Any = [null,null,null,WrappedArray(rs559462325),G,A,true,true,WrappedArray(),false]
// variant の情報. referenceAllele = G で alternateAllele = A なので,
// リファレンス配列では G のところが A になってる変異のレコード, ということになる

scala> data.first.get(5)
res21: Any = HG00096
// sampleId.

sampleId HG00096 について少し調べてみると, 年齢不詳イギリス人男性の B 細胞からサンプリングされたものであることがわかる.

なにか面白い解析が出来るとよかったのだけどぱっと思いつかなかったので一旦ここまでで公開. 何か思いついたら追記するかも.

とりあえず一発でアクセス可能な場所 (S3) に世界的な科学研究の成果が転がっていて, データを使ってあれこれ弄れる形まで持って行くのは案外簡単ですよ, というあたりが伝われば幸いです.

*1:最新版は EMR 5.2.0 ですが手元に立ってたのが 5.1.0 だったので...

カレーはハウスの箱裏レシピが至高

カレーを作った.

f:id:Hash:20151213204747j:plain

玉ねぎを炒めます

f:id:Hash:20151213204833j:plain

かぼちゃも切って一緒に

f:id:Hash:20151213204907j:plain

親の仇のように飴色になるまで炒めます.いい具合になったら野菜たちを取り出してざっと鍋を洗ったらおもむろに

f:id:Hash:20151213204918j:plain

肉を

f:id:Hash:20151213204933j:plain

強火で焼き

f:id:Hash:20151213204947j:plain

野菜たちを戻してアスパラとかと一緒に 1L の水で煮込みます.沸騰してから 15 分,

言葉と物―人文科学の考古学

言葉と物―人文科学の考古学

フーコー『言葉と物』を読みながら灰汁を掬い,待ちます.

f:id:Hash:20151213205136j:plain

具材がちょっととろっとして来たらルーを投入.個人的なベストはハウスの「こくまろ 中辛」と「ジャワカレー 辛口」の組み合わせです.

弱火で10分コトコトしたら,炊きたてご飯にかけます

f:id:Hash:20151213205217j:plain

以上,ブログらしいブログを書いてみたかった.オチはない.

取り寄せた本が届いたら DynamoDB Stream を元に Lambda 経由で Twitter に通知させる

人間は図書館を活用することで住民税の元を取ることが出来る.取り寄せ依頼した本が届いたり借りてる本の返却期限が近付いたりすると Twitter bot が通知してくれるようにしている.が,単純に cron で回していたので人間側の対応が遅れると何度も同じことを言われてしまう.

f:id:Hash:20151205132900p:plain

今回「今年もやるよ!AWS Lambda縛り Advent Calendar 2015 - Qiita」の 12/05 分担当として,上記の仕組みをDynamoDB の更新 Stream をイベントソースとして Lambda で一回だけ通知してくれるように改良する話を書く.

前提

図書館の web をスクレイピングして DynamoDB に格納する... ところは Lambda Advent Calendar としては本題ではないので省略*1

f:id:Hash:20151205220309p:plain

うちの地区はこんな感じのページ.とりあえず人間が

  • 本の取り寄せ依頼(予約)をしたとき
  • 取り寄せた本が届いた時
  • 本を借りた時
  • 本を返した時

といった行動をした際に,日付,書籍 ISBN,行動タイプが以下の様な形式で DynamoDB に格納される機能が実装済とお考えください.

{
  "date": "2015-12-02",
  "hashed_title": "7f550dd47d40ea31eb84c092dbcdd2f66ff960db",
  "isbn": "9784000236218",
  "type": "borrow",
  "uuid": "b946c18b-0aa9-45fc-938e-be5b52d1fc33"
}

なお書籍データ本体は別の場所に ISBN をハッシュキーとして格納している. ...で,該当テーブルに対して DynamoDB Stream を有効化する.DynamoDB Stream はリリース当初 Preview 版で利用申請が必要だったが今は誰でも使える.

DynamoDB ストリームは、Amazon DynamoDB テーブル内の項目に加えられた変更に関する情報の順序付けされた情報です。テーブルでストリームを有効にすると、DynamoDB はテーブル内のデータ項目に加えられた各変更に関する情報をキャプチャします。 ...

DynamoDB ストリーム は、ストリームレコードをほぼリアルタイムで書き込むため、これらのストリームを使用し、内容に基づいてアクションを実行するアプリケーションを構築できます。

引用元: DynamoDB ストリーム を使用したテーブルアクティビティのキャプチャ - Amazon DynamoDB

DynamoDB マネジメントコンソールからテーブルを選択してぽちぽちやると有効化される.

f:id:Hash:20151205134428p:plain

すると,こんな感じの Stream ARN が得られる.

arn:aws:dynamodb:ap-northeast-1:<MY_AWS_ID>:table/<MY_TABLE_NAME>/stream/<Stream を有効化した時刻>

ARN = Amazon Resource Names は AWS サービスにおいて何らかのリソース (EC2 インスタンス, IAM ユーザ,DynamoDB のテーブル etc) を URI よろしく一意に識別する ID のようなもので,AWS サービス間で何か連携しようとするとよく出てくる.

Lambda で DynamoDB Stream を受け取る

ようやく Lambda.先の操作で得られた Stream ARN をイベントソースとして受け取り発火させる設定を行う. ゼロから書き始めてもいいんだけど,マネジメントコンソールで "Create a Lambda Function" を選択,"dynamodb-process-stream" という blueprint を元にすると話が早い.

f:id:Hash:20151205220407p:plain

console.log('Loading function');

exports.handler = function(event, context) {
    //console.log('Received event:', JSON.stringify(event, null, 2));
    event.Records.forEach(function(record) {
        console.log(record.eventID);
        console.log(record.eventName);
        console.log('DynamoDB Record: %j', record.dynamodb);
    });
    context.succeed("Successfully processed " + event.Records.length + " records.");
};

event.Records の中に Stream の更新情報がどばっと入ってきている.たとえば以下の様な形.

{
  "Records": [
    {
      "eventID": "1",
      "eventVersion": "1.0",
      "dynamodb": { // 更新レコード情報
        "Keys": { "uuid": { "S": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" } },
        "NewImage": {
          "date": "2015-12-03",
          "hashed_title": "7f550dd47d40ea31eb84c092dbcdd2f66ff960db",
          "isbn": "9784000236218",
          "type": "return",
          "uuid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
        },
        "StreamViewType": "NEW_AND_OLD_IMAGES",
        "SequenceNumber": "111",
        "SizeBytes": 26
      },
      "awsRegion": "ap-northeast-1",
      "eventName": "INSERT", // レコードの新規作成
      "eventSourceARN": "arn:aws:dynamodb:ap-northeast-1:<MY_AWS_ID>:table/<MY_TABLE_NAME>/stream/<Stream を有効化した時刻>",
      "eventSource": "aws:dynamodb"
    }
  ]
}

f:id:Hash:20151205220433p:plain

先の Stream 有効化時に "New and old Images" を選択していると,更新前/更新後のレコードがフルフルで入ってくる.単に「なんか更新合ったよ」という事実を知りたいだけなら "Keys Only" でいいと思う. Stream から取得できる "Records > dynamodb > NewImage ..." あたりの内容を使えば Twitter 通知に必要な情報は取れそう.

Twitter API

今回やりたいことのためには Twitter API を Lambda から叩く必要があるが,その名も twittter という npm package で簡単に実現できる.

var client = new Twitter({
  consumer_key: <consumer_key>,
  consumer_secret: <consumer_secret>,
  access_token_key: <access_token_key>,
  access_token_secret: <access_token_secret>
});

var msg = "予約していた『" + event.Records[0].dynamodb.NewImage.title + "』が図書館で確保済です. (" + event.Records[0].dynamodb.NewImage.date + "迄)";
client.post('/statuses/update.json', {status: msg}, function(err, ...) {
 // ...
});

こんなイメージのコードを書けば,Lambda から bot を喋らせることが出来る.

f:id:Hash:20151205213615p:plain

ところでちょっと余談気味だけど,consumer_key とか access_token_secret とかを zip に含めて Lambda にアップロード,ってのはちょっとせきゅりてぃ的な意味でやりたくない.そこで AWS Key Management Service (KMS) を使うようにした.KMS のコンセプトについてはクラメソさんの 10分でわかる!Key Management Serviceの仕組み #cmdevio | Developers.IO などを参照にするとわかりやすい気がする.

流れとしては,あらかじめ KMS の Master Key を作っておいた後,その key-id を指定して Twitter API の各種 API credentials 情報をカンマで接続したテキストを暗号化.

$ aws kms encrypt --profile my-profile --key-id 9xxxxxx-9999-aaaa-a996-ffffffffffff --plaintext "consumer_key:<my_consumer_key>,consumer_secret:<my_consumer_secret>,access_token:<my_access_token>,access_token_secret:<my_access_token_secret>" --query CiphertextBlob --output text | base64 -D > ./encrypted-credentials

この encrypted-credentials を zip に含め,ソースコードと一緒に Lambda にあげてやる.

Lambda 上では毎回実行時に KMS へのリクエストを行い,credentials を複合する.Lambda Function に紐付けた Role からのみ復号可能となるように IAM 的に制限することができる,という仕組み.Lambda 上で動作する KMS decrypt 部分のコードは以下.

var encrypted = fs.readFileSync('./encrypted-credentials'); // コードと一緒に上げた暗号化済 credentials 情報
kms.decrypt({CiphertextBlob: encrypted}, function(err, data) {
  if (err) {
    console.log(err, err.stack);
    context.fail('failed');
  } else {
    var decrypted = data['Plaintext'].toString();
    // console.log(decrypted);
    obj = {};
    decrypted.split(',').forEach(function(item) {
      var pair = item.split(':');
      obj[pair[0]] = pair[1];
    });

    // Lambda 上で復号した credentials 情報を使って Twitter API を叩く
    var client = new Twitter({
      consumer_key: obj.consumer_key,
      consumer_secret: obj.consumer_secret,
      access_token_key: obj.access_token_key,
      access_token_secret: obj.access_token_secret
    });
    // ...

いじょ

もう少し DynamoDB Stream + Lambda の汎用的な話にするつもりだったんだけど,ざっくり用例を一つ紹介する記事になってしまった.がんばります.

*1:余談だが図書館の web はなかなか酷いものの銀行系のそれに比べればかなり精神負荷低くスクレイピング可能なレベル

人生のコントロールを取り戻す(五七五)

生きれば案外死なないもので,僕も今年で29になる.

労働を始めてからというもの一方的に社会に殴られ続け(体感には個人差があります)特に2013年は公私ともに乱れる酷い年で師走ともなればやれやれ死ぬかという心持ちであったが,憑き物が落ちたかのように2014年から人生のコントロール権をめきめき取り戻している,社会に出て初めて自分の足で立っているような気がしており,三十而立とは孔子もよく言ったもんだと思う.

うまくいってることもいかないこともあるけど, 記憶はただの記録であり語らなければミームは死ぬ.というわけでこのへんで久しぶりに人生の進捗報告風戯言を書いておく."人生コントロール権獲得感覚"は,今回書くようないくつかの行動が精神に反映されたものだと僕は考えている.

こんてんつ

  • 会社を設立する
  • ±100年に想いを馳せる
  • 肉体を管理する
  • 美味いものを食う
  • 知識を積み上げる

会社を設立する

2014年に会社を作った.とはいえ別にこれで食ってるわけではなく,主収入は相も変わらずサラリマンである.

サラリマンってのは税金コストが高すぎて資産形成に向かないという話は聞いていたが,いざ収入が増えてくるとほんとに馬鹿馬鹿しいくらい取られる.20代前半からずっと収入源を複数持ちたいという思いはあったし,そのへんの武器として設立した次第.

働き始めてからはずっと中小・ベンチャー企業を見てきたので会社を作るというとどうしても「事業を興す = 起業」が第一の発想に来てしまうが,僕の目的は橘玲氏の著作なんかで取り上げられる所謂 「マイクロ法人」 である.個人的にはジョジョのスタンド的なイメージで,ひとりでやるつもりだったけど乗ってくれた友達がいたので何人かで楽しくやってる(やってない).

最初の会社で既に「あっ社会ってこんな適当に回るんだ」とびっくりしたものだが*1,会社設立というトモすれば身構えてしまいそうな現実も地続きの日常であるらしい.そうこうあって,"会社"を特別な何かであると見る意識は完全に雲散霧消している.

会社作り自体はCookPadにレシピ乗せれそうなくらい簡単なもの*2で,2005年の会社法改正で資本金の最低額もなくなったので最悪作るだけなら1円と印紙代金あればいいし,司法書士やら行政書士に無駄な金払うような話でもない.RPGのクエスト感覚で面白いので自分でやるのオススメ.

f:id:Hash:20150505035740j:plain

会社を作ることによって,個人とは異なるもう一つの人格(法人格)が手に入る.そうすると,不思議なことが次々と起きるようになる.

貧乏はお金持ち──「雇われない生き方」で格差社会を逆転する橘玲

僕が11年前に株投資を始め,簿記の勉強をして ,金融を扱う会社に飛び込んだ頃から続く一連の経済的自由を求めるプロジェクトであり,法人格を使ってちょんちょん社会を突っついて税務署に怒られながら経験値を溜めている.

彼女は有限責任というダメコン*3を装備して産み出された法人格とはいえ,それなりに語ろうと思えば語れる(いまは語る気がない)熱い心がないでもなく,俺のミームの器となるべく社名に"研究所"を冠する.形式的には「いつかは研究所を作りたい」という僕の目標が叶ったことになるが,今はただのペーパーカンパニーに過ぎない.もっと力と金を貯め,機会を掴んでこの子にちゃんとした魂を込めてやりたい.

自分の仕事をもう少し考慮して選ぶなら、人間はみな本質的には研究者か観察者になるはずだ。というのも、人間の性質や運命は、明らかに誰にとっても同じように興味深いものだからだ。自分自身や子孫のために財産を溜め込もうが、一家や国家を築き上げようが、さらには名声を得ようが、人間は必ず死ぬのだ。しかし真実を相手にするとき、僕たちは不滅であり、変化や不慮の出来事を恐れる必要はなくなる。

孤独の愉しみ方―森の生活者ソローの叡智 (智恵の贈り物)』ヘンリー・ディヴィッド・ソロー

±100年に想いを馳せる

人間は必ず死ぬ.先日祖父母の家に泊まり久しぶり(もしかすると僕が物心ついて初めて)にたっぷり話す機会を作ったのも,仮にも僕の遺伝子プロバイダーなのだし,三十手前にして社会と真正面から殴り合う覚悟が出来てきた今,生きているうちに人間として向き合っておきたかったからかもしれない.まー向き合い直さないといけないものは色々あるんだけど.

祖父にも何か思うところはあったのか,到着すると古いアルバムを見せながら戦時中は戦闘機*4作ってたとか,ご先祖様は,みたいな橋本家系譜の話を聞かせてくれる.祖母からは係る人間の背景心理や数値の話などを.「お前裏の土地に家建てたらええが」と笑って言われたが,確かに土地に根を張り系譜に乗っかる道もあり得たのだろう.

f:id:Hash:20150505035706j:plain

僕が東京に出てきた背景にある考えは6年前(6年前!!)のブログ記事(田舎は好きだが、それでも東京を選択する。 - ミームの死骸を待ちながら )に詳しいが,当時の思考記録にはまだ僕に至るまでの血の歴史という視点が含まれていない.

人間の見ることが出来る時間範囲は極めて狭い.自身の生物としてのルーツに触れることで,僕は狭い視野を超えて思考をすこし遠い時代に飛ばすことができる.

僕は僕という意志主体であると同時にひとつの肉であり,肉の上で生きていくしかない(中でというべきか).肉を支える生命システムはとても興味深く神秘的ですらあるが,運用側としては不便かつ苛立たしい.百年経てば僕もみんなも等しく死骸である.そう考えるととても心が安らかになるが,同時に己の有限性というものを切実な痛みとして感じる*5

人間は肉の上に有限であるが,有限性を乗り越える武器がミームや遺伝子などに代表される自己複製子である.自己複製子は矮小な生命を乗り継ぐ神であると同時に我々の武器でもある.肉上の生を意識すればするほど,遺伝子を残したいという気持ちも強くなってくる.

肉体を管理する

肉といえば,ここ1年で10kgくらい痩せた.とりあえずこの痛快なグラフを見てほしい.

f:id:Hash:20150607041108p:plain

体脂肪率は10-13%くらいか.残念ながらもともと骨格太くて丸顔なのであまり見た目に分からないが,社会に出てから溜め込んだカルマを浄化した感覚が心地よく,体調もすこぶる良い.マウス実験では摂取カロリーを低く保てば寿命が伸びるらしいが,ヒトだとどうなんだろうね.

小細工を弄さずとも結局のところ摂取エネルギーひくことの消費エネルギーで身体の変化は予測可能で,予測できれば計画が立てられ,計画があれば行動の制御も可能である.肉体を意志の管理下におくことで,明確に言語化されるわけではないが何となく「意志で身体を制御できるのだから延長で周りの環境も制御してやろう」という自信が湧いてくる.

あと1,2kg落とせば目標(グラフ中点線)達成なので,そこまでたどり着いたら方法論をブログに書く予定.大したことしてねーけど.まぁ確実に成果が上がる方法ってのはいつもつまらんものなのかもしれない

美味いものを食う

言葉は要らない.

f:id:Hash:20150607051636j:plain f:id:Hash:20150607051642j:plain f:id:Hash:20150607051723j:plain f:id:Hash:20150607051712j:plain f:id:Hash:20150607051717j:plain f:id:Hash:20150607051735j:plain f:id:Hash:20150607051747j:plain

美食とダイエットは完全に両立可能である.

知識を積み上げる

元々学習の快楽で命を繋いでるような人間であったが,労働を始めてからは日々生き延びることに精一杯でそんな余裕もなく.

f:id:Hash:20150607042812p:plain

ようやく「まずは自分を幸福にしなければならない」と気が付き昨年からちょいちょい目標を決めて数ヶ月スパンで勉強することを再開している.試験という明確な合否が出るもので言うと

また,継続的な勉強としては

などがあり,どれも楽しい.あとは読書か.最近の読書メモブログ => ミームの死骸のmemex(拡張記憶)

いじょ

人間は死と税金だけは避ける事が出来ないと言ったのは誰だったか*6,肩の力を抜いてよくよく世界を見てみると,確かにくっきりと浮かび上がってくる高い壁であるのは確かだ.強大ではあるが,尽力すればそれなりに太刀打ち出来ない相手ではないと思っている.殴り合おう.俺はここで足止めする.別に倒してしまっても構わんのだろう?

知識を深め,現実の新たな姿を知るのは実に楽しい.思い通りにならないこともあるが,ものごとが"成る"こと,意志に反応してくれる世界それ自体に愛しさがある.

いっさいの目標がないということ、いっさいの限界がないということは、意志そのものの本質に属している。意志は終わるところを知らぬ努力である。

意志と表象としての世界〈1〉 (中公クラシックス)ショーペンハウアー p.366

抱く理想も遠い歴史も溺死するほど深くない.きっとすべて現実に落とし込んでいくと「ああこんなものか」と感じるのだろう.理想は理想そのままに,真実は真実ありのままに具現化されることは決してない.

そうした現実にまみれたものごとに美しさを感じられるようになったのは,精神が成熟してきたのかそれとも弾力を失っているのか.僕としてはあまりネガティブな変化と思っておらず,自他ともに認める理想主義者でとにかく生きる現実がつらい,な状態よりはマシかなと楽観的に捉えている.

もしくは単純にこれが老いってやつなのかもしれない.いずれにせよ,現実という場に現れた手持ちのカードで最善手を打ち続けていくしか術はない.

老いゆくぼくの次回作にご期待ください.知らんけど.

*1:参考: ちいさな会社の殺し方 : ミームの死骸を越えてゆけ

*2:難しくはないが複雑

*3:日本の合同会社に米国式LLCのパススルー課税が付かなかったのは残念

*4:「これ紫電改じゃん」とわかったのはつまみ食い艦これ知識のおかげ

*5:僕が死ぬまでに意識が電脳世界に飛び立つ技術が確立される可能性もあるが,それはそれ

*6:いまググったらベンジャミン・フランクリンらしい