エンジニアパパの2歳までのクリエイティブなDIY育児の記録

このブログでもちょこちょこクリエイティブな育児情報を発信してきましたが、それらは割と大掛かりなものでした。

 

そこで今回は、記事にするほどでもない日々の小粒なDIY事例を紹介しようと思います。

 

同じようなコンセプトで生後2か月の時に書いた記事はこちら。

inajob.hatenablog.jp

うちの娘ももうすぐ2歳となりますが、ここまでに行ってきた小粒なDIY事例を紹介していきます。

 

3Dプリント失敗作を入れたマラカス

あまり遊んでくれず

f:id:inajob:20220327224149p:plain

おむつのパッケージの赤ちゃんを切り取っておもちゃ箱に貼り付ける

じっと見ていた

f:id:inajob:20220327224233p:plain

なぜか某アニメ風の顔をいろいろな育児グッズに描く(妻)

特に娘は反応せず

f:id:inajob:20220327224307p:plainf:id:inajob:20220327224524p:plain

うんこ棒の反対側を保護するキャップを3Dプリンーーで作る

f:id:inajob:20220328083525p:plain

良かった写真を額縁に入れて飾る(妻)

家にいてふと目に留まるとなごむ

f:id:inajob:20220327224340p:plain

足置きとしての技術書

足が地に着くほうが落ち着くらしいです。

f:id:inajob:20220327224413p:plain

トンネルを自作

結構遊んでくれました。

f:id:inajob:20220327224616p:plain

牛乳パックで積み木

これも結構遊んでくれました

f:id:inajob:20220328083613p:plain

コンセントカバーの隙間を3Dプリンターを使って埋める

別にこの隙間があっても困ることはないですが、不格好だったので・・

f:id:inajob:20220327225604p:plainf:id:inajob:20220327225623p:plain

ニューブロックで親が遊ぶ

親の方が夢中になるパターン

f:id:inajob:20220327224715p:plainf:id:inajob:20220327224740p:plain

気付くと真面目なメモを書き始める落書き大会

娘のために描いてたはずが、突然真面目なアイデアメモを書き始めたりもします。

f:id:inajob:20220327224805p:plain

水で絵が描けるシートが便利

f:id:inajob:20220327224827p:plain

これです。壁とかに落書きされないので安心です。

ドアノブをやたらと開け始めたので対策

ドアノブを外すと、90度回転させて取り付けることが出来ます。こうなると通常のノブより開けるのが難しくなります。(今の所これを開けることはないです

f:id:inajob:20220327224850p:plain

言葉遊びかるたを自作(妻)

f:id:inajob:20220327224908p:plain

緩衝材に顔を描いておもちゃにする

あまり遊んでくれず

f:id:inajob:20220327224939p:plain

マスキングテープを駆使して娘に年賀状を書いてもらう(妻)

悩みの種の年賀状のデザインを娘の落書きで済ませるという作戦。

マスキングテープで白を残した部分のおかげでデザインっぽくなった。

f:id:inajob:20220327225038p:plain

引き出しにイラストを描く(妻)

保育園とかでも良く見かけるアレ

f:id:inajob:20220327225055p:plain

ブロックで親が遊ぶ

親の方が熱中するシリーズ

f:id:inajob:20220327225119p:plainf:id:inajob:20220327225138p:plain

f:id:inajob:20220327225153p:plain

一口ゼリーの容器でテントウムシを作る(妻)

これはよく遊んでいた。コスパの良いおもちゃです。

f:id:inajob:20220327225216p:plain

縄跳びをぶつ切りにしてポットん落としを作る(妻)

ベビーシッターさんに教えてもらったおもちゃ

よく遊んでいる

f:id:inajob:20220327225250p:plain

これを並べて絵のようなものを作る

親の方が遊ぶパターン

f:id:inajob:20220327225316p:plainf:id:inajob:20220327225334p:plain

f:id:inajob:20220327225353p:plainf:id:inajob:20220327225410p:plain

洗面所下の収納をむやみに開かないようにする部品を3Dプリンターで作る

今の所これで開けられなくなりました。

f:id:inajob:20220327225457p:plain

まとめ

生後2か月ごろから 2歳くらいの間に作ったDIY系の事例をざざっと紹介しました。

ここまでの所、このように親も楽しみながら育児出来ています。

 

子供への影響とかそういうのもありますが、まずは親が楽しんで育児をするのが大切だと思っています。

 

引き続きクリエイティブに育児をしていきたいと思います。

キッチン水切り棚の導入と3Dプリンターによる不具合修正

キッチン水切り棚

皆さんの家は洗った食器を置く棚、どのようなものを使っていますか?

我が家はもともと、シンプルな水受けの付いた棚だったのですが、子供が生まれたこともあり、皿の数が増え、少し手狭になってきました。

そこで、いわゆる「水切り棚」を購入することにしました。

ここで言う「水切り棚」というのは、キッチンシンクの上に固定できる棚で、棚から落ちた水がそのままシンクに落下するため、水受けが不要となり、かつスペースが節約できるという便利収納家具のことです。

初回購入のミス

比較的大物家具という事で、長さを計ったりして、良さそうなものを注文したのですが、棚の脚間の内径と外形を取り違えたせいで、絶妙に我が家のシンク上に設置できない棚を購入してしましました・・

幅も足らなければ、高さも大きすぎて、シンク上収納と干渉してしまうという残念な結果です。

返品なども考えたのですが、ちょうど工作室の棚に良いという事に気付いたので、返品せずに活用することにしました。

f:id:inajob:20220320121334p:plain

これ、意外と良いですよ。

2度目の正直

初回購入でミスしたので、今度はしっかりと寸法を測って、適合する商品を探しました。

要件は・・

  • シンク幅制限
  • シンク高さ制限
  • できれば2段

f:id:inajob:20220320121419p:plain

どうやら我が家の要件だと、海外製の安い水切り棚では対応するサイズが無く、少し高めの日本製のものがよさそうだという事がわかりました。

という事で、2度目はこちらを購入。

この商品は横幅、高さともに可変という事で、我が家の要件にマッチしました。

棚部分は、フラットなものとかご状のものをそれぞれ選択できるのですが、フラットなものは端の囲いがなく、食器が落下しそうだったので、かご4つタイプを選択しました。

棚のサイズは良し!しかし・・・

設置してみたところ棚のサイズは問題なく、無事にキッチンに設置することが出来ました。

しかし、新たな問題として、かごと皿の相性問題が発覚しました。

この棚に付属しているかごの幅が、絶妙に我が家の皿とマッチしません。

小さな皿はすり抜けて落ちてしまうし、大きな皿はかごの幅が足りず入りません。

f:id:inajob:20220320121452p:plain f:id:inajob:20220320121514p:plain f:id:inajob:20220320121538p:plain

またしても失敗か・・・

と思ったのですが、代替となりそうな棚も見つからないし、何とか工夫して乗り切ることにしました。

Try1 100均の皿置きを重ねる

収納で利用していた皿置きを棚の上に載せてみました。これで一応皿を収納することが出来ました。

f:id:inajob:20220320121605p:plain

しばらくこれで運用していたのですが、皿が高い位置に収納されてしまうため、上の収納と干渉してしまい、出し入れするのが難しいことがわかりました。

Try2 3Dプリンタでシンデレラフィットの皿置きを作る

Try1 は結構いい感じだったのですが、皿置きの幅と棚の幅が合わないために、無駄にスペースを取ってしまっていたのが問題だと考えました。

そこで我が家の3Dプリンタを活用して、棚のサイズにぴったりの皿置きを作る事にしました。

この棚の問題は大きく2つあります

  • 棚の幅が狭いのに、深すぎて、大きな皿が奥まで入らない
  • 棚の網の間隔が広すぎて、皿が倒れたり、小さな皿が落下する

これを解決するために、以下の対策を考えました

  • 皿置きに高さを持たせることで負傘を底上げする
  • 網の間隔を狭くする、横網により落下を防ぐ

ということで、OpenSCADでささっと立体を設計し、、3Dプリンタで印刷! f:id:inajob:20220320121636p:plain

意外と大きくなってしまったので、形状は単純なのですが印刷に5時間ほどかかる大作になってしまいました・・

f:id:inajob:20220320121655p:plain

何とか使えるものになった

ということで、使い勝手が微妙だった水切り棚に、3Dプリンタで作った皿置きを組み合わせる事で、何とか使い物になるソリューションが完成しました。

f:id:inajob:20220320121724p:plain

まとめ

色々失敗も多かった水切り棚の購入ですが、いろいろと試行錯誤して、それなりに使いやすいものが出来ました。

3Dプリンタはこういうちょっとした生活の工夫が出来て便利です。

TVチューナー(EX-BCTX2)が時々HDDを見失う対策と録画一覧をDLNAで引っこ抜く話

これは何?

我が家ではモニタ一体型TVではなく、TVチューナーをPCモニタにつなげています。

inajob.hatenablog.jp

製品としてはEX-BCTX2です。

まぁ、いわゆるnasne的なTVチューナーです。

この製品はHDDを内蔵しておらずUSB接続HDDを外付けします。

しかし、どうもこれが曲者で、我が家では、1か月に1度くらいの頻度で接続しているHDDを見失うことがありました。

この現象が起きると、録画済みの番組が見れないだけでなく、録画そのものが行われなくなるため、せっかく見たくて録画予約していた番組の録画が失敗しとても悲しい気持ちになります。

前からこの現象には悩まされてきたのですが、頻度が低かったので特に対策していませんでした。

しかし、先日「ダイの大冒険」を録画し損ねて、とても悔しかったので、その気持ちが熱いうちに対策を考えることにしました。

ステータスページなどは無いのか?

ひとまずHDDを見失うのはあきらめて、「HDDを見失ったことに気付く」というのが出来るのかというアプローチで調査を始めました。

このTVチューナーはIPアドレスを持つので、そこにブラウザでアクセスするなどして、HDDの様子がわかるのであれば、スクレイピングなどで、監視が出来そう・・と思ったのですが、、

表示されるのはファームウェアのバージョンやIPアドレスの情報だけで、HDDの接続状態は含まれていませんでした。

f:id:inajob:20220213203427p:plain

メディアプレイヤーから見ると・・?

このチューナーは専用のソフト(Windowsだと「テレキングリモート」「テレキングプレイ」)を使って視聴するのですが、なぜか録画した番組一覧をWindowsのメディアプレイヤーから取得することが出来ることに気付きました。 (※当たり前ですが、著作権保護機能により映像を見ることはできません)

f:id:inajob:20220213204003p:plain

これは、何らかの標準的なプロトコルで、録画した番組情報を取得できるのだろう、とあたりをつけました。

この方法がわかれば、録画した番組情報が取得でき無くなる==HDDの認識が出来なくなっている、と外部のプログラムから気付くことが出来ます。

UPnPDLNA

ちょっと調べるとすぐにわかりました、まずこのTVチューナーはUPnPでサービスを広報する仕組みがありました。 そして、そのアドレスに対してDLNAと呼ばれる仕様でアクセスすることで、メディアサーバーとしてある程度の操作が出来そうという事がわかりました。

UPnP

まずはUPnPを使ってみます。

丁度我が家のLANにはRaspberryPiが常時起動しているので、ここからコマンドを実行してTVチューナーのサービスを調べてみることにします。

下記のようにgupnp-toolsパッケージに含まれるgssdp-discoverというコマンドでこれを調べることが出来ました。 (USNに含まれるuuidを公開するのがまずいのかどうかよくわかっていないのでマスクしています、多分大丈夫だと思うけど・・)

$ sudo apt-get install gupnp-tools
...
$ gssdp-discover -i eth0 --timeout=3
...
Using network interface eth0
Scanning for all resources
Showing "available" messages
resource available
  USN:      uuid:XXXXXXXXXX::upnp:rootdevice
  Location: http://192.168.1.15:55958/drgd/
  USN:      uuid:XXXXXXXXXX
  Location: http://192.168.1.15:55958/drgd/
resource available
  USN:      uuid:XXXXXXXXXX::urn:schemas-digion-com:device:DragD:1
  Location: http://192.168.1.15:55958/drgd/
resource available
  USN:      uuid:XXXXXXXXXX::urn:schemas-digion-com:service:DragPlusLRManager:1
  Location: http://192.168.1.15:55958/drgd/
resource available
  USN:      uuid:XXXXXXXXXX::upnp:rootdevice
  Location: http://192.168.1.15:55959/public/
resource available
  USN:      uuid:XXXXXXXXXX
  Location: http://192.168.1.15:55959/public/
resource available
  USN:      uuid:XXXXXXXXXX::urn:schemas-iodata-jp:device:NetworkTunerCommand:1
  Location: http://192.168.1.15:55959/public/
resource available
  USN:      uuid:XXXXXXXXXX
  Location: http://192.168.1.15:55247/dms/
resource available
  USN:      uuid:XXXXXXXXXX::urn:schemas-upnp-org:device:MediaServer:1
  Location: http://192.168.1.15:55247/dms/
resource available
  USN:      uuid:XXXXXXXXXX::urn:schemas-upnp-org:service:ContentDirectory:1
  Location: http://192.168.1.15:55247/dms/
resource available
  USN:      uuid:XXXXXXXXXX::urn:schemas-upnp-org:service:ConnectionManager:1
  Location: http://192.168.1.15:55247/dms/
resource available
  USN:      uuid:XXXXXXXXXX::urn:schemas-digion-com:service:X_AccessControl:1
  Location: http://192.168.1.15:55247/dms/
resource available
  USN:      uuid:XXXXXXXXXX::urn:schemas-digion-com:service:X_DeviceConfiguration:1
  Location: http://192.168.1.15:55247/dms/
resource available
  USN:      uuid:XXXXXXXXXX::urn:schemas-dlpa-jp:service:X_DtcpPlus:1
  Location: http://192.168.1.15:55247/dms/
...

これで出てきたHTTPのアドレスにはブラウザでアクセスすることが出来ました。

今回目をつけたのはContentDirectoryというものです。どうも、ここにうまいことリクエストすることで、録画した番組一覧などを取得できるようです。

DLNA

ここからはDLNAという仕様に則って通信を行えば良さそうです。 少し調べるといくつか参考になりそうな情報がありました。 DLNAと言っても、その実態はHTTPのGET/POSTのリクエストのようで、Curlなどでも実行できるものでした。

まずはPOSTのリクエストボディを作ります。

読む限りSOAPのようです(よく知らんけど・・)。

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <SOAP-ENV:Body>
    <m:Browse xmlns:m="urn:schemas-upnp-org:service:ContentDirectory:1">
      <ObjectID>0</ObjectID>
      <BrowseFlag>BrowseDirectChildren</BrowseFlag>
      <Filter>*</Filter>
      <StartingIndex>0</StartingIndex>
      <RequestedCount>200</RequestedCount>
      <SortCriteria></SortCriteria>
</m:Browse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

なんとなく意味するところとしては、

  • ContentDirectoryというサービスにBrowseというActionを要求している。
  • ObjectIDというのが操作対象で、0だとルートを指す。
  • BrowseFlagをBrowseDirectChildrenとするとその操作対象の直下にあるリソース一覧を要求することとなる。
  • Filterは*以外の例を見なかった
  • StartIndexは大量のデータの途中から読む際につかうIndex
  • RequestedCountは取得したい数、しかしこの数取れるわけではないようだ
  • SortCriteria は空文字以外の例を見なかった

という感じ。

さて、これを踏まえてCurlのコマンドを呼び出します。

$ curl -v -H "Content-Type: text/xml; charset=\"utf-8\""  -H "SOAPAction: \"urn:schemas-upnp-org:service:ContentDirectory:1#Browse\"" --data-binary @request.xml http://192.168.1.15:55247/dms/control/ContentDirectory

ヘッダにContent-TypeSOAPActionというものを付与しないとエラーが返るようでした。

HTTPのアドレスは UPnPで見つけた http://192.168.1.15:55247/dms/ にアクセスするとそれっぽいURLが書いてありました。

さて、これを実行するとルートのリソース直下にある子供のリソースが取得できるはずです・・

*   Trying 192.168.1.15:55247...
* Connected to 192.168.1.15 (192.168.1.15) port 55247 (#0)
> POST /dms/control/ContentDirectory HTTP/1.1
> Host: 192.168.1.15:55247
> User-Agent: curl/7.73.0
> Accept: */*
> Content-Type: text/xml; charset="utf-8"
> SOAPAction: "urn:schemas-upnp-org:service:ContentDirectory:1#Browse"
> Content-Length: 899
>
* upload completely sent off: 899 out of 899 bytes
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: Linux/3.0.8 UPnP/1.0 DiXiM/4.0
< Date: Sun, 13 Feb 2022 09:03:53 GMT
< Connection: close
< EXT:
< Content-Type: text/xml; charset="utf-8"
< Transfer-Encoding: chunked
<
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:BrowseResponse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">
<Result>&lt;DIDL-Lite xmlns=&quot;urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/&quot; xmlns:upnp=&quot;urn:schemas-upnp-org:metadata-1-0/upnp/&quot;
xmlns:dc=&quot;http://purl.org/dc/elements/1.1/&quot; xmlns:dlna=&quot;urn:schemas-dlna-org:metadata-1-0/&quot; xmlns:dixim=&quot;urn:schemas-digion
-com:metadata-1-0/dixim/DIDL-Lite/&quot; xmlns:microsoft=&quot;urn:schemas-microsoft-com:WMPNSS-1-0/&quot; xmlns:lamprey=&quot;http://www.lampreynet
works.com/schema/lamprey_1.0/&quot; xmlns:arib=&quot;urn:schemas-arib-or-jp:elements-1-0/&quot; xmlns:dtcp=&quot;urn:schemas-dtcp-com:metadata-1-0/&
quot; xmlns:dlpa=&quot;urn:schemas-dlpa-jp:metadata-1-0/&quot; xmlns:xsrs=&quot;urn:schemas-xsrs-org:metadata-1-0/x_srs/&quot;&gt;
&lt;container id=&quot;root/XXXXXXXXXX/&quot; parentID=&quot;0&quot; restricted=&quot;0&quot; childCount=&quot;11&quot;&gt;
&lt;dc:title&gt;USB2:大容量&lt;/dc:title&gt;
&lt;upnp:containerUpdateID&gt;3802&lt;/upnp:containerUpdateID&gt;
&lt;upnp:class&gt;object.container&lt;/upnp:class&gt;
&lt;/container&gt;&lt;container id=&quot;root/LIVE_TUNER/&quot; parentID=&quot;0&quot; restricted=&quot;1&quot; childCount=&quot;3&quot;&gt;
&lt;dc:title&gt;ライブチューナー&lt;/dc:title&gt;
&lt;upnp:class&gt;object.container&lt;/upnp:class&gt;
&lt;arib:objectType&gt;&lt;/arib:objectType&gt;
&lt;upnp:containerUpdateID&gt;350&lt;/upnp:containerUpdateID&gt;
&lt;/container&gt;&lt;/DIDL-Lite&gt;
</Result>
<NumberReturned>2</NumberReturned>
<TotalMatches>2</TotalMatches>
<UpdateID>3810</UpdateID>
</u:BrowseResponse>
</s:Body>
</s:Envelope>
* Closing connection 0

うげ、なんだこれ・・

と思いつつそれっぽい情報も含まれているので注意深く読んでみます。

どうやらこれは、XMLの中にさらに文字列としてXMLが入ってしまっているようです。

しかしよく見ると「USB2:大容量」「ライブチューナー」という文字が見えます。

この「USB2:大容量」というのは自分が外付けHDDにつけた名前なので、どうやらルートにあるリソースが出ているようです。

さらに良く見るとこの外付けHDDのIDがroot/XXXXXXXXXX/である事もわかります(よくわからないので一応マスクしています。)

次はObijectIDをこの外付けHDDのIDにして、再度同じコマンドを実行すると、今度は外付けHDD内のディレクトリ一覧を取得できました。

ディレクトリ一覧ですが、その中に「すべて」という名前のディレクトリがあり、そのIDで同じコマンドを実行するとどうも、すべての録画データを取得できるようでした。(確かに純正アプリの「テレキングリモート」もそういう挙動をします)

f:id:inajob:20220213212516p:plain

という事で、無事DLNAを使ってTVチューナで録画した番組の一覧を取得することが出来ました。

HDDの死活監視を設定する

という事で、ちょっとやりすぎた感もありますがDLNAを使って外付けHDD内の録画データの一覧を取得できました。

HDDが認識できていないときは、このデータが取得できないはずなので、これを利用して外付けHDDが認識できないときにアラートを上げることが出来るようになりました。

雑に書いたスクリプトはこんな感じ・・

まずはおおもとのシェルスクリプト。これをcronで実行します。

#!/bin/bash

cd `dirname $0`

MAX_RETRY=5
n=0
until [ $n -ge $MAX_RETRY ]
do
# ====================
n=$[$n+1]
curl -v -H "Content-Type: text/xml; charset=\"utf-8\""  -H "SOAPAction: \"urn:schemas-upnp-org:service:ContentDirectory:1#Browse\"" --data-binary @request.xml http://192.168.1.15:55247/dms/control/ContentDirectory > get.txt

python check.py && break

echo "ERROR"
sleep 10

# ====================
done

if [ $n -ge $MAX_RETRY ]; then
  echo "failed: ${@}" >&2
  # SLACKなどにHDDが認識できない旨を通知
  exit 1
else
  echo "OK"
fi

リクエストに使うXML文書(ObjectIDはマスクしています)

<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"><SOAP-ENV:Body><m:Browse xmlns:m="urn:schemas-upnp-org:service:ContentDirectory:1"><ObjectID xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="string">「外付けHDD内のすべてのリソースを表すID」</ObjectID><BrowseFlag xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="string">BrowseDirectChildren</BrowseFlag><Filter xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="string">*</Filter><StartingIndex xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="ui4">0</StartingIndex><RequestedCount xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="ui4">200</RequestedCount><SortCriteria xmlns:dt="urn:schemas-microsoft-com:datatypes" dt:dt="string"></SortCriteria></m:Browse></SOAP-ENV:Body></SOAP-ENV:Envelope>

curlで得たXMLを解析しちゃんと番組データが入っているかを確認するpythonスクリプト

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import html
import xml.etree.ElementTree as ET

count = 0

with open("get.txt", encoding="utf8") as f:
    s = f.read()
    x = html.unescape(s)
    #print(x)
    root = ET.fromstring(x)
    print(root) # Envelope
    print(root[0]) # Body
    print(root[0][0]) # BrowseResponse
    print(root[0][0][0]) # Result
    print(root[0][0][0][0]) # DITL-Lite
    for item in root[0][0][0][0]:
        print(count, item[0].text) # title
        count = count + 1
    print("count", count)

さて、これで外付けHDDの死活監視を実現できました。

シェルスクリプトで、謎にループしているのは、初回のリクエストは失敗し、その後HDDがスピンアップして安定するとリクエストが成功するためです。

このスクリプトを仕込んでから、まだ1度もHDDの認識を失敗したことがないのでわからないですが、この定期的な外付けHDDのスピンアップのおかげで、認識しなくなる問題も発生頻度が下がっているかもしれません。

蛇足: 録画しているすべての番組を一覧する

ここまでで、表題の問題は解決しましたが、せっかく録画番組一覧が取得できるなら、何かに応用したいな、、と思って取得したデータを見ていました。

しかし、「すべて」のデータを取得しようとしても1度のリクエストでは数十件分のデータしか返却されていないことに気付きました。

どうやら、StartingIndexを取得したデータの個数ずつずらして何度もリクエストすることが必要のようです。

いわゆるWebアプリのAPIの「ページング」的なやつですね。

ここまで来たら、やってしまおうという事で、このページングをすべてたどって、録画データのタイトル一覧を出力するプログラムを書いてみました。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import html
import xml.etree.ElementTree as ET
import urllib.request
import sys

url = 'http://192.168.1.15:55247/dms/control/ContentDirectory'
headers = {
        'Content-Type': 'text/xml; charset="utf-8"',
        'SOAPAction': '"urn:schemas-upnp-org:service:ContentDirectory:1#Browse"'
        }

def createRequestBody(objectID, startIndex):
  return '''<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <SOAP-ENV:Body>
    <m:Browse xmlns:m="urn:schemas-upnp-org:service:ContentDirectory:1">
      <ObjectID>%s</ObjectID>
      <BrowseFlag>BrowseDirectChildren</BrowseFlag>
      <Filter>*</Filter>
      <StartingIndex>%s</StartingIndex>
      <RequestedCount>0</RequestedCount>
      <SortCriteria></SortCriteria>
</m:Browse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>''' % (objectID, startIndex)

def browse(objectID, startIndex):
  reqbody = createRequestBody(objectID, startIndex)
  req = urllib.request.Request(
          url,
          reqbody.encode("utf8"),
          method="POST",
          headers = headers
          )
  count = 0
  with urllib.request.urlopen(req) as res:
      s = res.read().decode("utf8")
      x = html.unescape(s)
      root = ET.fromstring(x)
      return root

def getItems(root):
    body = root.findall('{http://schemas.xmlsoap.org/soap/envelope/}Body')[0]
    browseResponse = body.findall('{urn:schemas-upnp-org:service:ContentDirectory:1}BrowseResponse')[0]
    return browseResponse.findall('Result')[0].findall('{urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/}DIDL-Lite')[0]

root = browse(0, 0)
# find USB disk
usbID = -1
for item in getItems(root):
    if item[0].text.find('USB') != -1:
        usbID = item.attrib['id']

root = browse(usbID, 0)
# find ALL directory
allID = -1
for item in getItems(root):
    if item[0].text.find('すべて') != -1:
        allID = item.attrib['id']

# list all contents
index = 0
while True:
    root = browse(allID, index)
    body = root.findall('{http://schemas.xmlsoap.org/soap/envelope/}Body')[0]
    browseResponse = body.findall('{urn:schemas-upnp-org:service:ContentDirectory:1}BrowseResponse')[0]
    numberReturned = int(browseResponse.findall('NumberReturned')[0].text)
    totalMatches = int(browseResponse.findall('TotalMatches')[0].text)
    # print(index, numberReturned, totalMatches)

    items = getItems(root)
    for i, x in enumerate(items):
        title = x.findall('{http://purl.org/dc/elements/1.1/}title')[0]
        start = x.findall('{http://purl.org/dc/elements/1.1/}date')[0]
        print(index + i, start.text, title.text)
    index = index + numberReturned
    if index + numberReturned == totalMatches:
        break

実行すると、何度もリクエストを行い、すべての録画番組のタイトルを取得します。

f:id:inajob:20220213212235p:plain
出力したデータをgrepして特定の番組を取り出している

まだ残る謎

さて、ここまでTVチューナのDLNA機能を使って、録画番組一覧を取得してきましたが、やっていく中で以下のような疑問が出てきましたが、まだ未解決のままです。

何か情報をご存知の方は、ぜひ教えてください。

  • 「テレキングリモート」から録画番組の削除を行っても、DLNA経由での呼び出しではデータが残り続ける。一度「テレキングリモート」で録画番組の一覧を取得すると(このときものすごく遅い)、以降DLNA経由での呼び出しでもデータが正しく消えるようになる。
  • 「テレキングリモート」のUIでは視聴済みの番組にはマークがつくが、DLNAのレスポンスを見る限りそのようなデータが入っていない

DLNAでできるのかは不明ですが、以下のようなこともやってみたいです

  • 放送が終了しているのに残り続けている繰り返し予約の検知
  • 録画予約の取得
  • 録画予約の実施
  • 録画された番組と繰り返し予約エントリの紐づけの取得

まとめ

ということで、TVチューナーで外付けHDDが突然認識されなくなる問題を、外形監視により検知できるようになりました。 まだ一度も発動していないのでスクリプトにはバグがあるかもしれません。

また、副産物として、TVチューナで録画した番組のタイトル一覧を取得できるようになったので、これも何かに使ってみたいです。

例えば、「テレキングリモート」より一覧性の高い録画番組ビューアとか?(ただし再生はできない・・)

参考

DLNA周りの参考となる記事が少なかったのですが、結局以下のページを丸々真似する感じになりました。

記事を書いてくださった方に感謝します。

3Dプリンタと無料のツールで「うんこクッキー」を作った

f:id:inajob:20220122080901p:plain

事の発端

1月某日、我が家の電子レンジが壊れました。これを機に新しいものを買おう・・という事になりいろいろと吟味した結果・・

オーブンレンジを買いました!

オーブン機能をつけたのは、「お菓子」を作りたかったから!

ということで早速クッキーを作る事にしました。

3Dプリンタで型を作る

クッキーの型は市販のものも購入していたのですが、せっかく家に3Dプリンタがあるので、オリジナルの型も作ってみることにしました。

今回作るのは、みんな大好き「うんこ」です!

unko.svg

まずはSVG画像でうんこのシルエットを作ります。作図にはInkscapeを利用しました。 ベジェ曲線を使った作図は少しコツが必要ですが、慣れればこのくらいは簡単に作れます。

f:id:inajob:20220121215523p:plain

unko.scad、unko.stl

SVG画像をもとに3Dデータを作成します。ツールはOpenSCADを利用しました。 offsetで少し輪郭を大きくして、元のデータとdifferenceすることで、クッキー型の3Dデータを作成します。

ソースコードはこんな感じです。

module unko()
  import("unko.svg", center = false, dpi = 96); // unko.svgの読み込み

linear_extrude(height=20) // 型の厚さは20cm
difference(){ // 膨らませたものから元のデータの差をとる
  offset(3)  // unko.svgのデータを3mm膨らませる
    unko();
  unko();
}

完成したらSTLデータとしてエクスポートします。

f:id:inajob:20220121215543p:plain

unko.gcode

Silc3rでSTLファイルをG-Codeファイルに変換します。

f:id:inajob:20220121215602p:plain

3Dプリンタに転送して印刷

3Dプリンタにデータを送って印刷を開始し、後は待つだけです。 今回は手元にあったPLAのフィラメントを使いましたが、おそらく厳密には健康に良くない物質が混ざる危険があるような気がします。 真似して実施する場合は自己責任でお願いします。

f:id:inajob:20220121215632p:plain

クッキーの製作

今回は市販のクッキーの素を使いました。

クッキー型で生地を抜くとこんな感じです。

f:id:inajob:20220121215759p:plain

で、電子レンジのオーブンで加熱。大体30分ほどで完成です。

f:id:inajob:20220121215834p:plain

おまけ

Twitterのアイコンもクッキーにしてみたらいい感じに出来ました!

f:id:inajob:20220121215907p:plain

感想

オリジナルの形のクッキーを作るというのは思った以上にテンションが上がりました。 データを用意する方法も、はじめのSVGを作るところ以外は機械的な作業なので、思った以上に簡単でした。

まだうちの子供は1歳なので、あまり楽しめていない感じでしたが、もう少し大きくなったら、子供の好きなものの形のクッキーを作ってあげようと思っています。

子供がを持つことで、3Dプリンタも今までと違った使い方をするようになってきました。折角子供を授かったので、この環境の変化をポジティブにとらえ、このような楽しい活動をどんどんやっていこうと思っています。

2021まとめ

f:id:inajob:20211231203539p:plain

ピックアップニュース

娘(1歳)がコロナに罹患

なんといっても今年は、これが一番のニュースでした。24日間の隔離はとても大変でしたが、家族全員大事が無くて良かったです。

inajob.hatenablog.jp

育児と仕事の両立

1月から長い9か月の育休を終え、仕事に復帰しました。妻は4月から復帰で、2オペで育児しつつ仕事と両立させています。 時短勤務などを駆使して、なるべく無理しないように努めています。

インターネットを活用したパパ友との交流も活発に行うことが出来ました。

パパ育コミュ | 子育てパパが集う場所 に参加し、たくさんの子育てを楽しむパパと交流したり、育児Scrapboxを公開し育児ノウハウの共有を行うこともできました(自由参加)。

inajob.hatenablog.jp

toICNの開発

クラウドファンディングで購入したインスタコード専用の楽譜変換ツール「toICN」を作り、これが、そこそこ利用され、OSSとしての開発も、小規模ながら体験することが出来ています。

インスタコードはまだまだアップグレードが続いているので、今後も開発をやっていきます!

inajob.hatenablog.jp

ステータス

  • 引っ越してから5年目
  • 会社に入ってから12年目(来年で13年目)
  • 結婚して5年ちょっと
  • 娘が生まれて1年目

今年学んだスキル

育児全般(引き続き)

育児は学びの連続です。娘もいつの間にか歩けるようになり、大人のいう事も少しずつ分かるようになってきて、また少し言葉もしゃべるようになってきました。来年もどんどん成長するんだろうなぁ・・

オクラの育て方

今年はミニトマトに加えて、オクラとスイートバジルも栽培しました。 inajob.hatenablog.jp

自作キーボードづくりの一通りの流れ

ALLPCB39というキーボードを設計しました。

inajob.hatenablog.jp

怒涛の基板設計

ALLPCBさんで、無料のクーポンが毎月利用できるという事で、基板設計を毎月行い 3枚の基板を試作できました。

  • CheapSeqDuo

  • ALLPCB39
    • 前述のキーボード
  • PiPoPa
    • 携帯電話型のArduino互換ボード。まだ記事を書いていない・・
    • ALLPCBのUselessコンテストの「BestIdea」入賞した

勢いで設計した割には3枚ともそこそこ面白いものが出来て満足しています。また記事を書かねば・・

CH451制御

グリッド状のスイッチとLEDを制御できる便利ICであるCH451の試作基板を作り、制御の方法を学びました。(同時押しが検出できないこともわかり、お蔵入りですが・・

今年買ったもの

  • ガス給湯器
  • 電動コーヒーミル

(追記予定)

感想

育児と仕事の両立というのは思った以上にハードで、自分の趣味の活動や、勉強に使える時間が限られてきたと感じた1年でしたが、そんな中でも、このブログの毎月更新や、もう一つのinajobがいろいろ紹介するサイトです – inajobのいろいろレビュー の平日ほぼ毎日更新が出来たのは、育休中に育児に向けた生活のリズムや、夫婦の役割分担がうまくできたからだと思っています。

こうやって記事にして見直してみると、いろいろ挑戦したが、アウトプットできていない作品が結構あるなと感じました。子育てなどで可処分時間が減っている中で、インプット・アウトプットのバランスをもう少し考えていく必要があるなと思っています。

育児はまだまだ長期戦ですが、息切れしないように気を配りつつ、自分のやりたいこともうまく両立していきたいと考えています。

月別生データ

1月

  • 育休から復職
  • Arduboy FX Modをもらう
  • 保育園決定

  • M5Paper使ってみる

inajob.github.io

inajob.github.io

  • clubhouse初体験
  • 夜間断乳頑張る

2月

  • 娘用電子工作

inajob.hatenablog.jp

  • duktape, emscriptenなどで遊んでいた
  • 1週間のレシピを考案するソフトを試作してた
  • HACHIBARのLiPo化
  • Graphviz Live Previewの開発

inajob.github.io

3月

  • igeta試作
  • ひな祭り
  • パパ育の電子書籍でアイコンを書いてもらったり
  • クラシルでレシピを決め始める

inajob.hatenablog.jp

  • Meowbit

inajob.github.io

  • コンセントカバー

  • M5StackとUSB-Host-Shield
  • ノートパソコンスタンド

inajob.github.io

  • HACHIBARを増やす
  • おうちサイネージにランダム画像を出すようにする

4月

  • 慣らし保育
  • RakuChord,HACHIBARの新バージョン発注(まだ倉庫に眠っている・・)
  • TTGO T-Watch Keyboard

inajob.github.io

  • LibreCADを試すが、合わない
  • 手挽のコーヒーミル購入
  • ぼにぎり
  • 毎月風邪をひく生活に・・
  • Aliから謎の荷物が届く事件

5月

  • テープカッターチャレンジ

inajob.hatenablog.jp

  • 冷蔵庫調味料棚をDIY

  • パンダのうんこ 購入
  • HACHIBAR少し販売再開
  • igeta組み立て
  • mmf21
  • ヨトウムシにやられる
  • ironingを試す
  • カニカルキーボード

inajob.github.io

6月

  • PVBフィラメントを試す
  • カニカルキーボードデビュー

inajob.hatenablog.jp

  • 役員トレカを作ってみる
  • CheapSeqのケースを作る
  • Arduboy FX Mod組み立て

inajob.hatenablog.jp

  • 3Dプリンタでキーキャップを作ってみる
  • FitBitに腕をやられる
  • おもしろいのを3Dプリントしたらちょっとバズる
  • コードリーディングメモの記事が盛り上がる

qiita.com

7月

inajob.github.io

  • オクラ収穫開始

inajob.hatenablog.jp

  • ニューブロックで遊ぶ
  • CheapSeqDuoの設計
  • Type-C to VGA
  • MCE3開発

github.com

  • SONICWARE LIVEN 8bit warps LVN-010 -- ほとんど遊べていない・・もったいない

8月

inajob.hatenablog.jp

  • 新しいロボット掃除機
  • toICN実装開始
  • 高解像度ディスプレイモジュール

inajob.github.io

9月

  • World Makerのクローズドベータ当選
  • インスタコードが届く

inajob.hatenablog.jp

  • 5000フォロワー達成
  • PiPoPaの設計・実装
  • 大量のPLAフィラメントを購入

  • 水で落書きできるシート
  • ABAさんのゲームをRaspberryPiで動かそうとしてみる
  • XDAキーキャップ

inajob.github.io

  • ALLPCB39とM5Stackの組みあわせ
  • RaspberryPi4スターターキット

inajob.github.io

10月

  • toICNひと段落

inajob.hatenablog.jp

  • 給湯器の予防交換
  • 電子計り

inajob.github.io

  • 貧者のUSBチェッカー

  • インスタコードのボリュームの固定具を3Dプリント
  • TodayTwitの開発

11月

  • ALLPCB Useless Contest 受賞(何賞だったかな?)
  • ALLPCB39の記事

inajob.hatenablog.jp

12月

  • 3DPCB

inajob.hatenablog.jp

  • おうちサイネージのオープン化

  • クリスマスオーナメントを作る

  • 怒涛の帰省

参考

3Dプリンタで基板のようなもの(3DPCB)を作ってみた

基板のようなもの?

下記の記事で紹介していた方法が、前から気になっており、今回はこの方法でオリジナルの基板のようなものを作ってみました。

inajob.github.io

この手法は Developing 3DPCB – 3D Printed Circuit Board with lots of potential. – Johan von Konow で紹介されており、「3DPCB」と呼ばれています。

基板設計

基板の設計は通常のプリント基板と同様にKiCADで行います。

こんな風に論理回路図を書いて・・

f:id:inajob:20211202213528p:plain

基板を書きます。

f:id:inajob:20211202213553p:plain

この時KiCADを「レガシーツールセット」に切り替えて、「配線時の角度を45度単位に制限」のチェックを外して、自由な角度に配線できるようにしておくのがポイントです。

45度に制限しても良いのですが、実際に配線をするときに導線を曲げる必要があり面倒です。なるべく直線で配線します。

f:id:inajob:20211202213743p:plain

3Dプリンタの精度にもよりますが、私は2mmの配線幅としました。

データ作成

基板の片面にのみ配線したので、その面のデータをdxfで出力します。

f:id:inajob:20211202214044p:plain

ドリルデータはいつも通りに出力します。

ドリルデータはそのままだとCADソフトに読み込ませることが出来ないので、下記スクリプトでDXFに変換します。 (元となるソースコードDRL NC file to DXF · GitHub を参考にしています。)

import ezdxf
from sys import argv
import re

if len(argv) < 2:
    print("Need filename")

in_filename = argv[1]
out_filename = in_filename[:-4] + ".dxf"

doc = ezdxf.new(dxfversion="R2010")

doc.layers.add('holes', color=2)
msp = doc.modelspace()

tools = {}
current_tool = None
for line in open(in_filename).readlines():
    line = line.strip()

    match = re.match("^T(\d+)C(.+)$", line)
    if match:
        tool_id = int(match.group(1))
        size = float(match.group(2))
        tools[tool_id] = size
        continue
    match = re.match("^T(\d+)$", line)
    if match:
        current_tool = int(match.group(1))
        continue
    match = re.match("^X(.+)Y(.+)$", line)
    if match:
        x = float(match.group(1))
        y = float(match.group(2))
        size = tools[current_tool]

        print(x/100,y/100,size/2*50*4)
        msp.add_circle((x/100,y/100), radius=size/2*50*4)

print(out_filename)
doc.saveas(out_filename)

3DPCBの作成

OpenSCADを使います。

difference(){
  translate([0,0,-1])
  // 雑に外形線を描く
  linear_extrude(height=3){
    polygon([[0,50],[40,20],[25,20],[50,-20],[30,-20],[15,-20],[10,-40],[0,-40]]);
    mirror([-1,0,0])
      polygon([[0,50],[40,20],[25,20],[50,-20],[30,-20],[15,-20],[10,-40],[0,-40]]);
  }
  // 配線をくりぬく
  linear_extrude(height=3)
    translate([0,40,0]){
      import("out/christmas-B_Cu.dxf");
    }
  
  // ドリル穴をくりぬく
  translate([0,0,-2])
  linear_extrude(height=2-0.3)
  translate([0,40,0]){
    scale([1/4, 1/4, 1/4])
    import("out/christmas.dxf");
  }
  
  // ドリル穴の上部にはんだ付けしやすいように大き目の穴をくりぬく
  translate([0,0,0])
  linear_extrude(height=6)
  translate([0,40,0]){
    scale([1/4, 1/4, 1/4])
    offset(5)
    import("out/christmas.dxf");
  }
}

これでこんな立体が生成できます。

こちらははんだ付け面。

f:id:inajob:20211202214615p:plain

部品配置面はこんな感じ。

f:id:inajob:20211202214647p:plain

ドリル穴部分は裏面には小さな穴をあけつつ、表面にははんだ付けしやすいように、大きな穴をあけています。

dxfデータはドリル穴しかないですがOpenSCADのoffsetを使うことでこの穴を大きくして大きな穴のデータを用意しています。

今回は配線面を下にして、部品実装面を上に3Dプリントしようとしていたので、オーバーハング部分の造形をうまい事やるために、1層だけあえて穴をふさぐようにデータを作っています。

f:id:inajob:20211202215846p:plain

(このテクニックは FDM(FFF)3Dプリンタで印刷する機械部品をモデリングするときの、たくさんの小さな工夫|はるかぜポポポ|note この神記事を参考にしています)

3Dプリント

あとは3Dプリンタが出力するのを待つだけです。

厚さは3mmでそこまで大きくないので、40分ほどで完成しました。

部品取り付け穴は1層分だけふさがっているので、キリで突いて穴を貫通させます。

部品の取り付け

今回の部品はLEDと抵抗です。部品取り付け穴に部品を差し込んで、溝に沿って余った足を折り曲げて配線します。

配線すべき個所には溝があるため、迷うことなく配線できました。これがこの3DPCBを使う利点ですね。

PLAは熱に弱いので、はんだ付けは素早く行います。と言っても普通にはんだ付けしてる分にはそこまで気を遣う必要もなさそうでした。 (PLAが溶けたとき独特の甘い匂いが少し出ます)

そんなこんなでできた基板がこちら!

f:id:inajob:20211203072456p:plain

まとめ

挑戦してみたかった3DPCBを試してみました。

PCBほどではありませんが、ユニバーサル基板に実装するよりは簡単でミスが少ない基板が出来たように思います。

今回は試していないですが、配線面を下にして3Dプリントすることが出来ることもわかったので、部品実装面にも造形できそうです。

参考とした記事には具体的なツールの紹介などは無かったのですが、使い慣れたKiCAD, OpenSCADを組み合わせる事で比較的「職人芸」なしに3DPCBが作れることが分かったのも大きな収穫でした。

リアランスなどはPCBには遠く及びませんが、家にある3Dプリンタでこういうものが作れるのは意外と使えるテクニックのように思いました。

ALLPCBの無料キャンペーンで初めての自作キーボードを作ったら本当に無料で最高だった件

無料プリント基板が作れる!?

f:id:inajob:20211110123159p:plain

Twitter「無料でプリント基板を作ってくれる中国の会社がある」と聞いて、調べると、どうもALLPCBという会社がキャンペーンをやっているようでした。

f:id:inajob:20211110102332p:plain

キャンペーンの詳細を見ると・・

  • 毎月送料も含め無料で 5枚の基板を1回注文できる
  • 基板には大きさの制限がある(100mm*150mm = 15000mm2以内)
  • 基板の色は緑と白のみ
  • クレジットカード情報不要

というようなものでした。

f:id:inajob:20211110102353p:plain

プリント基板の製作会社は中国にたくさんあり、最近は少量を安くで作ってくれる業者も増えてきましたが、「送料を含めて無料」というキャンペーンは初めて見ました。

※このキャンペーンは2021年6月から9月ごろまで行われていたようですが、今現在は「1つ有料で注文すると、以降6か月は毎月無料で1度5枚基板を注文できる」というキャンペーンに変更されているようです。これでも十分お得です。この記事を見て興味がわいた方は → ALLPCB から試してみてください。

月に1回無料で基板を注文する日々

無料と聞いたら試してみたくなるのが人情です。

自分はプリント基板を設計し、中国の業者に発注したことは何度かあったので、これはうれしい!と思いこのキャンペーンに乗じて毎月基板を発注しました。

今回はそんな基板の中でも、比較的うまく作ることが出来た、「自作キーボード」について紹介しようと思います。

自作キーボードとは

このブログを見ているような人は、ご存知だとは思いますが「自作キーボード」という電子工作のジャンルがあります。

キーボードというのは、いわゆるパソコンに接続するキーボードです。

このキーボード、かなり奥が深く、配列やキーボードスイッチの押し具合の違い、テンキーの有無や、有線・無線など、こだわりポイントがたくさんあります。

市販の製品も大量に販売されていますが、キーボード自体を「自作」することもでき、近年この「自作キーボード」というのがエンジニアを中心に話題となっています。

「自作キーボードを作る」と言っても、様々なレベルがあり、「組み立て済みのキーボードを購入する」、「キットを買ってきてはんだ付けする」、など、様々な取り組み方がありますが、今回自分が挑戦したのは「基板の設計からおこなう」という比較的ガチ目な方法です。

キー配列を設計する

前述したALLPCBのキャンペーンを見ていて、気になったのが「基板の大きさの制限」です。

基板には大きさの制限がある(100mm*150mm = 15000mm2以内)

と書かれていますが、この100mmと150mmというのはあくまで例のようで、面積さえ条件を満たしていれば、とても細長い基板などもキャンペーンの対象であることがわかりました。

※残念ながら、現在のキャンペーンではこの制限は少しきつくなり長辺が150mm以内となっているようです。キーボードを作るのであれば左右分割式や、キーボードスイッチの小さなものが選択肢となりそうです。

f:id:inajob:20211110102353p:plain

この制限は中国の基板製造会社の中では異例です。

通常よくあるのは、100mmx100mmサイズがとても安く設定されており、それよりはみ出すと途端に料金が高くなるという価格設定です。 そのため、キーボードなどの細長い基板を作る際は、思った以上に基板の製造費用が高くついてしまっていました。

しかし、ALLPCBではこれが毎月5枚まで無料!!

ということで、自分はこの制限の中で自作キーボードを作る事を計画しました。

15000mm2以内のサイズでキーボードを作ろうと考えると、まずは標準となるキーボードスイッチの大きさが19.05mm程度なので、、

13×3のキーボードスイッチを並べるのがよさそうです。

一般的なキーボードはファンクションキー、数字、英字3列、機能キーと6列キーが並ぶものですが、今回作るキーボードはたった3列だけです。

自作キーボードの世界では、キーの数の少ないキーボードも数多く設計されており、このような3列のキーボードは30%キーボードなどと呼ばれるジャンルが確立されています。

f:id:inajob:20211110102555p:plain

少なくなったキーはLayerという仕組みを用いて、Layerキーと同時押しすることで利用することが出来るようにします。

キーボードのスイッチには様々なサイズがあります。例えばShiftキーは一般的に細長く、Enterキー縦長で、少し変わった形をしているものが多いです。

しかし今回は、設計を簡単にしようという事で、すべてのキーを文字キーと同じ正方形のキーとしました。

また、キーボードの各列は、通常のキーボードだと列ごとに少しずつずれているものですが、今回設計するキーボードはずらさず格子状にキーボードスイッチを並べました。

このような格子状のキーボード配置をOrtholinearなどと呼んだりするようです。

この段階で各キーの役割はふわっと決めていましたが、まぁ後でいくらでも変更することが出来るので、そこまでしっかり考えていません。

キーボードの配列の設計には下記サイトが非常に役立ちました。

www.keyboard-layout-editor.com

f:id:inajob:20211110102944p:plain

実はこのサイトでキーボードの配列を作ると、この後の基板の設計に必要な下準備も出来てしまうという優れものです。

基板を設計する

前項でキーボードの配列が決定したので、基板を設計していきます。

設計にはKiCADというオープンソースの基板設計ソフトウェアを利用しました。

keyboard-layout-editorでキーボードを設計し、生成されるjsonファイルを使うと基板設計のひな型になるデータを作ることが出来ます。

GitHub - yskoht/keyboard-layouter-playground: A sample circuit and netlist to quickly try the Keyboard Layouter plugin.に元となる基板データがあります。 さらに、このプラグインGitHub - yskoht/keyboard-layouter: Footprint auto placement plugin for keyboard layoutを使うことで、keyboard-layout-editorで設計したキーボードに必要な部品だけが残されます。

ここまでで、基板に載せるキーボードスイッチとダイオードのデータが用意できました。

これを基に以下の作業を行うことで基板データを完成させます。

  • マイコンの配置
  • 部品間の論理配線
  • 基板上の部品配置
  • 基板上の物理配線

マイコンの配置

今回はPro micro(Arduino)互換のマイコンボードを使うことにします。

f:id:inajob:20211110111152p:plain

加えてマイコンをリセットするためのスイッチや、デバッグに利用できるようにシリアルポートや、電源ポート、I2Cポートなどを接続するためのピンヘッダも配置しておきます。

f:id:inajob:20211110103326p:plain

部品間の論理配線

マイコン、キーボードスイッチ、ダイオードを論理配線します。

マイコン、キーボードスイッチ間はマトリクス状に配線を行います、何行、何列に配線するかはお好みです。 今回はなるべくArduinoのピン数を節約するために、6x7のマトリクスで配線を行いました。

f:id:inajob:20211110103350p:plain

基板上の部品配置

論理配線が終わったら次は部品配置です。 キーボードスイッチは、ほぼ自動生成された形のまま利用します。 ダイオードを対応するキーの近くに配置し、マイコンモジュールをなるべく邪魔にならないと頃に配置します。

基板の外形線もここで設計します。ALLPCBの無料の範囲をはみ出さないように気を付けて設計します。

基板とケースを固定するための穴もここで指定します。

f:id:inajob:20211110103431p:plain

基板上の物理配線

最後に物理配線です。各部品の足を、論理配線に従って配線します。 配線にこだわりがある場合は、手で配線を1つずつ指定することが出来ますが、今回はFreeroutingという自動配線ツールを使います。

KiCADから部品情報をExportしてFreeroutingに読み込ませ、自動配線を指示して、数時間放置するだけで、勝手に配線を行ってくれます。

ここまで作ると、出来上がりのイメージを3Dで見ることもできます。

折角なので「I Love ALLPCB」みたいな文字を入れてみることにします。

f:id:inajob:20211110103539p:plain

基板を発注する

基板データが出来たらいよいよ発注です。

KiCADから基板発注に必要なデータを出力しZipファイルに固めてALLPCBの注文フォームからアップロードします。

上手くキャンペーンが適応されると、合計金額が$0と表示され、本当に0円で注文ができます。

f:id:inajob:20211110103756p:plain

製造の状況は適宜アップデートされ、今回の場合は、8/15に家に届いたので、注文から1週間かからずに基板を手に取ることが出来ました。

Webページやメールはすべて英語で書かれており、中国語が必要となる事はありませんでした。

部品を注文する

自作キーボードづくりに必要な部品は、様々なパーツショップで購入することができます。

今回必要となった部品は以下です。

  • キーボードスイッチx39
  • キーボードキャップx39
  • ダイオード(表面実装版)x39
  • ネジとナット
  • リセット用タクトスイッチ(表面実装版)
  • Pro micro(Arduino)互換モジュール

自分はもっぱらAliExpressを使います。届くに1か月以上かかることもあり、また記載されている部品と違うものが届いたりすることもたまにあるので、あらかじめ買っておくのが良いと思います。

(すぐに部品が欲しい場合や、信頼のおける部品が欲しい場合は遊舎工房など、国内のパーツショップを利用するのが良いと思います。)

実は自分はすべての必要部品をすでに購入済みだったので、このステップは不要でした。 f:id:inajob:20211110104031p:plain

ファームウェアを用意する

自作キーボードのファームウェアは、QMKというものが非常に有名です。

QMKを自作のキーボードで使うためには、配線とキーの関係を設定ファイルとして用意して、QMKをビルドしなおす必要があります。

これにはプログラミングの知識が必要ですが、下記サイトを利用すると、ポチポチと配線をGUIで設定するだけで、QMKをビルドして、ファームウェアをダウンロードすることが出来ます。

どのキーに何の役割を持たせるかといった試行錯誤もこのツールがあれば非常に簡単でした。

Keyboard Firmware Builder

f:id:inajob:20211110104211p:plain

キーボードを組み立てる

さて、基板が届いたら、いよいよ組み立てです。

Pro micro(Arduino)互換モジュールに前述のファームウェアを書き込みます。ファームウェアはhex形式なので、avrdudeなどを使えば書き込みができますが、下記サイトを使うとWebページから簡単に書き込みが出来ました。

Pro Micro Web Updater

Pro micro(Arduino)互換モジュールと、キーを1つ、対応するダイオードを1つはんだ付けをして、パソコンにつないでみて、キーボードとして認識されるか?、1つのキーを押すと期待する文字が入力されるかを確かめるのが良いです。

f:id:inajob:20211110104236p:plain
写真は2つのキーで動作確認をしている様子です。

動作が確認出来たら後はひたすらはんだ付けです。

ケースを作る

ここまででキーボードの内臓は完成しましたが、このキーボード、このままではまっすぐ机の上に置くことが出来ず、使うことが出来ません。

ということで3Dプリンタを使ってケースを作る事にしました。

我が家の3Dプリンタでは、横幅が足りず、このキーボードのケースを一気に作ることが出来ず、左右2つの部品に分割してケースを出力しました。

f:id:inajob:20211110110615p:plain f:id:inajob:20211110110652p:plain

ケースの設計にはFreeCADを用いました。

f:id:inajob:20211110104506p:plain

応用する

前項までで、キーボードとしては完成ですが、この自作キーボード、コンパクトでかつ、ケースなどを取り付けるためのネジ穴もあるので、このキーボードと電子工作を組み合わせる事で、キーボード付きのガジェットを自作できそうだなと考えています。

もちろん市販のキーボードでも似たようなことはできるのですが、キーボード自体が大きかったり、ほかのパーツを取り付ける余地が無かったりと、不便が多いです。

こんな感じでM5StackとUSBHostシールドを組み合わせて、モバイル入力システムを作ってみたりしています。

f:id:inajob:20211110114206p:plain

今回の反省点

初めての自作キーボードという事もあり反省点は山積みです。同じようなことに挑戦する人のために、少しメモを残しておきます。

ネジがキーボードスイッチと干渉しそう

ネジ穴をキーボードスイッチの隙間に開けたのですが、いざ組み立てた後にねじを取り付けようとしたところ、ねじの頭が引っかかってしまい、ねじを取り付けるのが難しいという問題が発生しました。

f:id:inajob:20211110111738p:plain

ナットを上にすることでこの問題は一応回避できました。

f:id:inajob:20211110111816p:plain

Pro micro(Arduino)互換モジュールがかさばる

基板の面積を小さく保つために、キーボードスイッチの裏側に重なるようにPro micro(Arduino)互換モジュールを取り付けるように設計しました。 そのため、キーボード自体の厚みが増してしまいました。

また、キーボードスイッチとPro micro(Arduino)互換モジュールの両方をはんだ付けしてしまうと、部品をつけなおすことなどが非常に難しいという点も気になります。(普通に組み立てる場合は部品のつけなおしは不要なので、そこまで問題になりません。)

ハートマークが消えた

これは自分のデータの作り方がまずかったのですが、 この基板のキモの1つであるI Love(ハートマーク) ALLPCBの ハートマークが消えてしまっていました。

発注前にガーバーデータを確認していればこの間違いに気付けたのですが、、残念です。

仕方が無いのであとから修正液でハートマークを書きました。これはこれで好きな記号が書けて面白いという事で結果オーライです。

f:id:inajob:20211110112212p:plain

キーボードスイッチが基板取り付け用ではなかった

今回家にあったキーボードスイッチは、「パネル取付用」でした、このほかに「基板取り付け用」というものがあり、今回のようにに基板だけでキーボードスイッチを「基板取り付け用」を使うべきでした。

「基板取り付け用」のキーボードスイッチには基板に固定するための足が付いており、これによりまっすぐにキーボードスイッチを固定することが出来ます。

「パネル取付用」を使うと、この足が無いのでキーボードスイッチが微妙に傾いて、ガタガタになってしまいました。

複数のキーボードスイッチに対応したフットプリントを使いたい

キーボードスイッチのフットプリントはCherry MX専用のものを使いましたが、ホットスワップソケットや、ロープロファイルなど、ほかのキーボードスイッチにも対応したフットプリントもあるようで、これら複数のキーボードスイッチに対応したフットプリントを使うと、拡張性がさらに広がると思います。

GitHub - foostan/kbd: for building keyboard libraries に良いフットプリントがあるようです。

キー配列は12x3でもよかったかも

今回なるべくたくさんキーボードスイッチを並べようと13x3の配列にしましたが、 実際に利用してみるとどうも1列多く感じました。

30%キーボードの良いところの一つに、指を横にずらす必要が無い、というがあるのですが、13列だとどうも1列指が左右に動く箇所が出てきてしまい、もてあます感じがありました。

実際キーに文字を割り当てていても1列だけどうにも割り当てが難しい箇所がありました。

少しだけ隙間を開けてマクロキーとして使えるようにすると良いかもしれません。

参考

この記事が非常によくまとまっており、ほとんどこれに従ってキーボードを作ることが出来ました。

zenn.dev

まとめ

自作キーボード、気になってはいたのですが、基板を作るとなるとお値段が・・と躊躇していましたが、ALLPCBの素晴らしいキャンペーンのおかげで、ついにその一歩を踏み出すことが出来ました。

この記事を見て興味がわいた方は → ALLPCB から試してみてください。

流行っているだけあって、周辺ツールが充実しており、プログラミングすることなく、オリジナルの自作キーボードを作ることが出来ました。

皆さんもこの機会に自作キーボードに入門してみませんか?