帰宅してから急激に体調が悪化し、悪寒がひどくて眠れないぐらいに。体温を計る余裕がなかったけど、38度〜 ぐらいは出ていた気がする。1時間ぐらい悪寒と戦ったあと、どうしよもないので家で余っていたロキソニンを飲んだら急激に楽になって寝れた。朝には熱はひいたが頭痛がひどかったので病院に。
熱の原因はよくわからなかった。ここ数週間地味に頭痛があったり体調が思わしくないけど、とりあえず緊張性頭痛だろうと言われた。熱にも頭痛にも効くからと葛根湯が処方された。
帰宅してから急激に体調が悪化し、悪寒がひどくて眠れないぐらいに。体温を計る余裕がなかったけど、38度〜 ぐらいは出ていた気がする。1時間ぐらい悪寒と戦ったあと、どうしよもないので家で余っていたロキソニンを飲んだら急激に楽になって寝れた。朝には熱はひいたが頭痛がひどかったので病院に。
熱の原因はよくわからなかった。ここ数週間地味に頭痛があったり体調が思わしくないけど、とりあえず緊張性頭痛だろうと言われた。熱にも頭痛にも効くからと葛根湯が処方された。
16:40〜 自作キーボードの話を1時間ぐらいする予定です。なんか1日の最後なのでゆるめでやりたいという気持ちはあります。
おおむね、事前のトーク概要と変わらない内容で話せそうです。
資料つくってます…… 話がまとまらない……
ありがとうございました。
mattn さんが思ったより全くオッサン感なくてビビった。。。挨拶しようと思ったけどできなかったので、いつか(いつ?)挨拶したい。発表もおもしろかった。
IoTツール開発は生産管理ツールについてで、ものすごく貴重な話だと思った。懇親会でももうちょっと聞いたりした。
今回のゲラゲラ枠っぽいが、ものすごい金かかっててやばかった……
Emscripten の話とか。ブラウザって VR もサポートする予定なのか!と思った。あと Emscripten が頑張って OpenCL を WebGL に変換するとか知らなかった。
Flux 触ったことなかったのでわかりやすかった。
FPGA の話とても聞きやすくて解りやすくてよかった。
JS のテスト、闇雲にググっても萎えるだけなので、こういうまとまった資料ってほんとまじで大事
いままでハマったことがなかったのだけど、ついにハマってしまった。
補助グループ権限もつけてくれるsetuidgidのようなもの - (ひ)メモ を読んで、どうするかな〜と思いつつsetusergroupsを試してみたが、Unix::Groups がデフォルトで入っておらずちょっと困ったので python で以下のようにした。
#!/usr/bin/python
import os
import sys
import pwd
if len(sys.argv) < 3:
print >> sys.stderr, 'Usage: setusergroups user program'
sys.exit(1)
pw = pwd.getpwnam(sys.argv[1])
pw_name = pw[0]
pw_uid = pw[2]
pw_gid = pw[3]
pw_home = pw[5]
os.environ['HOME'] = pw_home
os.initgroups(pw_name, pw_gid)
os.setgid(pw_gid)
os.setuid(pw_uid)
os.execvp(sys.argv[2], sys.argv[2:])
エラると例外で落ちてトレースバックがでて十分なので特にエラー処理してな
ハマったのは Raspberry Pi の pi ユーザで i2c を触るようなデーモンを作るケースで、この場合 pi ユーザに i2c グループ権限がついてないとダメなのでこういうのが必要になる。
依存なしでどうにかしたかったのは、cpanm が ansible の Core Modules に入ってなくてめんどかったから
前まで webp のアップロードはできなかった気がするんだけど、最近試したらできるようになっていた。
Google Photos はいつからかブラウザによって webp が落ちてくるように変わったけど、そのタイミングでアップロードも変わったのかな?
ちなみに JPEG 2000 や JPEG XR はあいかわらずアップロード不可能
さくらのVPSにMongo Rescueのリストアができなかった (未解決) | tech - 氾濫原 の続きで、結局クリーンインストールした。さっき DNS を切り替えたので様子見。一応 DNS の TTL 時間はとっくにすぎたので、変な BOT 以外はいまのところ旧サーバにアクセスない。
どうやったかのメモはまた別にかく
一通り移行できたつもりだけど、元々雑多なサーバ内容かつ長期運用していたのでよくわからない。外形監視を自宅 Raspi からやっておくべきだったかなあ。
プランは東京 2GB SSD にした。前がだいぶ古い大阪 1GB HDD プランだったので SSH で繋いで体感できるぐらいパフォーマンスあがった。ウェブ側も早くなってて Google からの評価があがるといいんだけどな〜
カスタムOSインストールガイド - Ubuntu 16.04 – さくらのサポート情報 を見ながらインストールまでやる。
旧サーバと同じホスト名にしてDNSをふりかえたいが、DNS やホスト名まわりは最後にやる。
手元からまず copy-id
$ ssh-copy-id user@ufhostname
リモートの設定をかえる
$ sudo vi /etc/ssh/sshd_config PasswordAuthentication no PermitRootLogin no UsePAM no $ sudo /etc/init.d/ssh restart # 別のターミナルを開いて ssh できることを再度確認
ssh, http, https と mosh 用の udp ポートをあけておく (該当するソフトウェアをインストールすると勝手に空いたりするのだが念のため)
sudo ufw default deny sudo ufw allow 80 sudo ufw allow 443 sudo ufw allow 22 sudo ufw allow 60000:61000/udp sudo ufw enable
sudo apt-get install zsh gcc libncurses5-dev language-pack-ja git daemontools daemontools-run mysql-server ca-certificates cpio curl dnsutils imagemagick imagemagick-common irssi libcairo2-dev libcurl4-openssl-dev mime-support nginx nginx-common nginx-full ntp ntpdate optipng pkg-config rsync shared-mime-info w3m wget xz-utils xml-core zip libxml2-dev libtiff5-dev libssl-dev libsqlite3-dev libreadline-dev libpng12-dev libpango1.0-dev libopenjpeg-dev libmysqlclient-dev libjpeg8-dev libgif-dev libfreetype6-dev libffi-dev cmake screen ruby-dev rrdtool libdb-dev postfix unattended-upgrades
sudo ln -s /etc/service /service
home ディレクトリごとまるごと移す
一時的に sudo で rsync をつかえるようにする
$ sudo vi /etc/sudoers.d/rsync Defaults env_keep += "SSH_AUTH_SOCK" Defaults!/usr/bin/rsync !requiretty cho45 ALL=(ALL) NOPASSWD: /usr/bin/rsync ||< >|| ## dry-run sudo rsync --dry-run -auvz -e 'ssh -i /home/cho45/.ssh/id_ecdsa' --rsync-path='sudo rsync' --progress /home/cho45/ cho45@160.16.210.51:/home/cho45/
/srv /etc/nginx /etc/postfix # /etc/mysql 変更点がなかった。。。あらためて見直そう /etc/letsencrypt # /var/lib/mysql mysqldump でやった # /service 手動でやる
意外とうつすのない?
git の HEAD を入れてるので再度コンパイルして /usr/local にインストール
cd ~/project/h2o git clean -f cmake -DWITH_BUNDLED_SSL=on . make sudo make install
ログファイルを /var/log/h2o に作るようにしてるが前もってディレクトリがないとしぬ
sudo mkdir /var/log/h2o
/var/log/h2o/*.log { daily missingok rotate 90 compress delaycompress notifempty create 0640 www-data adm sharedscripts postrotate svc -h /service/h2o endscript }
5.14.4 が gcc の関係?で入らなかったので、これを期に 5.22.2 に移行
perlbrew install perl-5.22.2 --as perl-5.22
前のマシンで perlbrew list-modules した結果を保存して cpanm にくわせてインストール。入らないやつが無視されるので、完全ではないがだいたい入る。
5.22 で動かなくなったコードを地味に修正したりした。
db ファイルだけ rsync したらいけるだろと思ったけど割といけなかったので、dump して restore する。mysql 自体のバージョンもあがってるのでまぁこのほうがよさそう。
mysqldump -uroot --routines --all-databases --flush-privileges | ssh cho45@160.16.210.51 'mysql -uroot'
たいしてデータ入ってないので一瞬。--routines でユーザテーブルもコピーしている。 --flush-privileges をつけないとコピーしたユーザデータが反映されないのでつけとく。
160.16.210.51 www.lowreal.net 160.16.210.51 lowreal.net 160.16.210.51 cho45.stfuawsc.com 160.16.210.51 coqso.lab.lowreal.net 160.16.210.51 no-real.net 160.16.210.51 block.lab.lowreal.net
みたいなのを /etc/hosts に書いて検証。証明書も転送して設定済みなので DNS だけ書きかえればうごいてくれるという感じのはず。。
DNS の向き先を新サーバに向ける
さくらのVPSはA レコードがひけてないと逆引き設定がつくれないので、Aレコードが適用されたあとにやる。
/etc/hostname も変える。
ここまでやったら一旦リブート。30秒ぐらいで起動するのでたすかる……
旧サーバからぬいてくる
crontab -l > crontab.user sudo crontab -l > crontab.root
新サーバに適用する
crontab crontab.user sudo crontab crontab.root
/etc/sysctl.conf に追記
net.core.somaxconn = 50000 net.ipv4.tcp_max_syn_backlog = 30000 net.core.netdev_max_backlog = 5000 net.ipv4.ip_local_port_range= 1024 65535
sudo sysctl -p で反映
これだけ買った。本当はおむつが欲しいんだけど、欲しいブランドのものがなかった…
定期便でも頼んでるけど、定期便で頼んでいると在庫確認に無頓着になりがちで
の二択になる。適切な配達間隔を設定すること自体が難しい。定期便自体が月イチの決まった日付前後の配達になるので「なくなりそうだから頼もう」と思ったタイミングだと時既に遅しということがある。
結局
のいずれかになる。
おむつの場合ストックがものすごい邪魔なので、多めに頼むってのが難しい。それと成長にともなって商品変更を迫られるので、そもそも大量に買いたくない。おかげで、なくなりそうになったらプライムで追加発送とか、近くのドラッグストアで買うみたいな運用になる。
Dash Button だと「足りない」と気付いたときにすぐ買うという場合は便利そう。そして物理ボタンなので家族で共有して押せるというところが便利。洗剤とかだと気付いた人が詰めかえるという運用なので「なくなったから買って」「忘れてた」「はやく買って」「カートにはいれた」みたいなことが発生してめんどうくさい。
ただし「もうすぐ定期便で届くし、それまでは在庫持つよ」というケースでも発注してしまうことが予想できてちょっとむずい。
キャッシュの持ちかたを変えて、圧縮 (gzip) して持つように変えた。キャッシュを返すときは gzip ずみのをそのまま返すように。
gzip 非対応ブラウザでおかしくなる気がするけど、昨今そんなブラウザは聞いたことがないので現実的には問題なかろうという気がする。
これによりキャッシュ用のDBファイル(SQLite)のサイズが劇的に減ったので、ファイルキャッシュに乗りやすくなったはず……
誤り訂正や、超解像技術みたいなのを前提とした圧縮方法に興味がある。なんとなく面白い感じがする。本来存在していたデータを意図的に削って送って、受取側の「経験」みたいなものを信用する感じが面白い。
画像圧縮も、CPU速度と帯域を比べたときに帯域で律速されるぐらいに高解像度になると、ディープラーニングによる高解像度化や、自動色付けを前提とした画像圧縮アルゴリズムが産まれても不思議ではない。
つまりこれは「おい」と言うとお茶がでてくるみたいなシステム。これがコンピュータで実現すると面白い。送信側の「意図」みたいなものを最大限推論してデータを復元し、転送データを最小限にできる。
最初からアドレスついてたので意外とやることない。
ifconfig すると既に v6 のアドレスがついている。Scope:Global になっているやつがグローバルIPアドレス (正確にはグローバルユニキャストアドレス)
inet6 addr: 2001:e42:102:1807:160:16:210:51/64 Scope:Global
これを該当ドメインの AAAA レコードでひけるようにする。
nginx -V の結果に --with-ipv6 があることを確認する。
server { listen 80; listen [::]:80; ... }
という感じにして ipv6 側もlistenするようにする。
listen: 80 と書いてる場合、デフォルトで v6 も listen するようになっているのでやることはない。
ここでの「DNS サーバの IPv6 対応」はDNS サーバへの通信が IPv6 でできるかという話になるが、value-domain の NS1.VALUE-DOMAIN.COM とか、さくらインターネットの ns1.dns.ne.jp は対応していないっぽい?
IPv4 で解決できるなら別に問題ないんだけど、IPv6 オンリーの環境 (あるの?) からだと名前がひけないことになる。
自分に IPv6 アクセス環境がないので、とりあえず1つのドメインだけ対応させた。現状ではとくに IPv6 の対応したからといってメリットがないのが微妙。。。
人と人とは利害が一致する部分と一致しない部分があり、ある言葉を受けとるときにバイアスを補正して受けとる必要がある。自分にとって無意味なことでいちいち気が乱されるようなことを避けるために、相手のポジションと自分のポジションによって生じるバイアスは補正して理解しないといけない。インターネットは大変いろんな立場の人がいるため、いちいち全部の話を真面目に聞いてるとアホになる。
向いてる方向性が一緒なら議論可能なので、素直に意見をもってよい。
このとき必要なのは議論ではなくて交渉なので、議論をもとめられても適当にやっておけば良く、話真剣に受け止める必要はない。
なお経営者対従業員的な文脈だと、経営系の人はこういうことを言うと渋い顔をするので、あくまで「経営者視点を持ってます(キリッ」ってことにして真面目に聞いているフリをしておくこと。
お互いの利害を直交のベクトルとして合成したときの大きさによって、どれぐらい真剣に聞くかを決めたらいい気がする。二項対立というよりは、どのぐらい話を真剣に聞くべきかというアナログ値を感じたほうがよい。
どのぐらいまで生きていられるのか気になったので試してみる。wrk を使う (ab だとうまく高負荷にできなかった) wrk は brew で入る。
ベンチの際いくつか注意する
$ wrk -c 170 -d 10s -t 16 https://... Running 10s test @ https://... 16 threads and 170 connections Thread Stats Avg Stdev Max +/- Stdev Latency 164.27ms 64.53ms 852.22ms 80.26% Req/Sec 61.56 22.99 170.00 72.60% 9278 requests in 10.10s, 44.31MB read Non-2xx or 3xx responses: 22 Requests/sec: 918.52 Transfer/sec: 4.39MB
日記システムは、これぐらいでCPUは使いきってちょいちょいエラーが発生する感じだった。
ヤフー砲が瞬間最大で100req/sec、継続的に30req/secぐらいらしいので、現実的にはこの日記が負荷で死ぬことはまずなさそう。他のサービスも同居してて相互に影響してる(バックエンドプロセスのワーカーを共有してる)けど。
サーバ移行のタイミングでつかいはじめた。監視をちゃんとするようにしようという感じ。loadavg5 と filesystem でひとまず監視。メトリクスの保存とグラフ化はフリープランだと1日だけなのであまり重視してない。
しかし、さくらのVPSはホスト不具合で落ちるってことは滅多にないので、やりたいことはサーバ自体の死活というよりウェブサーバの監視になる。このへんはプラグインでごにょごにょやってみている。パフォーマンス(応答時間のパーセンタイル値とか)とエラー数あたりでアラートが出せればいいかなという感じ。
高誘電体 MLCC は容量が時間経過で減少していくが、約125度(キュリー温度)まで加熱すると容量が回復する特性がある、らしい。
エージングで容量が減っていくというのはいいとして、一定より温度をあげると回復するというのを知らなかったのでびっくりした。
PS3/PS4 の修理方法でとにかくホットブローするみたいなのがあるけど、はんだクラックだけではなくて容量回復するという意味合いもあるのだろうか。
welq 問題でいろいろ思うところがあるけど、最終的にこれは「国会議員バカばっかり問題」と一緒ということなのだなと思って、解決にはほど遠いので気が重くなった。
「国会議員バカばっかり」って思うんだけど、あれは有権者が求めて選ばれた存在なわけで、こう思うということは有権者バカばっかりと思うに等しい。
検索エンジンの結果って、人々が求めるもののランクを上げた結果なので、その結果がバカなのって、結局人間がバカってことなんだよなあと思う。検索エンジンは人知を超えて賢くあるべきか?そして教育的であるべきか?はたしてそんな人工知能はバカな人間に受け入れられるのか (ビジネスになるのか)。
ネトゲって最初からエンドコンテンツなので、スキル上げとかが途方もない。とはいえゲームなのでやってればいつか終わるぐらいのバランスにはなっている。結果僕らは学ぶことができる、時間をかければあらゆるタスクは必ず終わる。さらにネトゲに飽きて失った時間に気付いたときにもう一つ学ぶことができる。人生は有限であること。
homebrew で入れる。
$ brew install libtiff $ brew install --HEAD webp
~/Library/Application Support/Adobe/Lightroom/Export Actions
にスクリプトを置くと、書き出しダイアログで選べるようになる。書き出したあとスクリプトの引数に現像済みファイル名が渡されるので、これを処理するようにすればよい。
以下のようなファイルを webp.rb という名前で Export Actions フォルダに保存して実行属性をつけておく。
#!/usr/bin/env ruby
require 'logger'
logger = Logger.new('/tmp/webplog')
logger.info ARGV.inspect
begin
ARGV.each do |f|
dir = File.dirname(f)
out = File.join(dir, File.basename(f, ".tif") + '.webp')
logger.info "%s => %s" % [f, out]
IO.popen([
'/usr/local/bin/cwebp',
'-metadata', 'all',
'-preset', 'photo',
'-q', '90',
'-o', out,
'--',
f
], :err=>[:child, :out]) do |io|
while l = io.gets
logger.info l.chomp
end
end
end
rescue Exception => e
logger.fatal e.inspect
end
ファイル設定
として
「後処理」で「webp」を選ぶ
で書きだし。
EXIF が消える。cwebp が TIFF の EXIF デコードに非対応のため
リバースプロキシとして使っている h2o で
proxy.reverse.url: http://localhost:5001/
みたいに書いていたら確率的に connection failure がでて悩んだ。結局
proxy.reverse.url: http://127.0.0.1:5001/
で解決
/etc/hosts に ::1 localhost のエントリ(IPv6)があり、h2o は解決したアドレスのうちからランダムに1つ選択するため。プロキシ先のバックエンドサーバは IPv6 を bind しておらず、::1 が選択された場合に接続に失敗していた。
start_server が listen してないポートは以下のようなエラーになる。
cp socket:(null):80 is not being bound to the server
h2o は SIGHUP で graceful restart が可能だが、listen するポートが変わった場合は実際にlistenしている親プロセス (start_server プロセス) の再起動が必要になる。
これまで HTTPS (443) だけ h2o で処理していたが、nginx に使ってるぶんのメモリ量がMOTTAINAIのでh2oだけでやるようにした。さようなら nginx。
複雑なことをしているホストは全くなかったので普通に書きかえていっただけ
↑ の写真 webp で Google Photos にアップロードしたんだけど、Chrome で見ても jpeg でしか配信されない。うーん…… いまいち webp 配信の条件がわからない。
Google Photos 上のサムネとかは webp になってるけど、大きめの画像は jpeg 固定なんだろうか
internal がデフォルトで、特に問題もないしずっとこれにしていたが、h2o が再起動されるたびにキャッシュが消えるのではないか? どうせ memcached も起動してるのでそっちにストアすべきではないか? と思いはじめたので変えてみた。
まぁ弱小ウェブサイトだとそもそも resumption かかるようなことがぜんぜんないので意味ない。Google Bot も分散アクセスのせいか全く resumption しないし。
最近子どもがとなりのトトロにハマってて毎日見てるんだけど、今朝は「お父さん」と「お母さん」が病院で会話しているシーンを見て、「星野源みたいねぇ」「ガッキーみたいねぇ」とか言いだして笑った。メガネかけてると星野源みたいねぇと言うのはしばしばあるんだけど、さらに「ガッキーみたいねぇ」は初めて聞いた。
最近は直接見てないはずなので (というか親が見ようとすると嫌がる)、結構前のを覚えているようだ。
さくらのVPSのウェブサーバでIPv6の接続をうける | tech - 氾濫原 で、IPv6 アクセス環境がないと書いたが、スマフォで簡単に IPv6 接続できることを知った。
IIJ mio の場合、APN の設定で IPv4/IPv6を選ぶだけで優先的に v6 接続されるようになる。
自力で確認できる環境が身近にあることがわかったので、このサーバでホストしているホストを IPv6 対応してみた (もとも IPv6 インターフェイスも listen してるので単に AAAA レコードをひけるようにしただけ)
見た目が何も変わらないのが正しい状態なのだけど、IPv6 接続されてるのかよくわからないので、仕掛けをしてみた。
判定は単に REMOTE_ADDR を見て、IPv6 アドレスだったら IPv6 接続と判定する。: を含んでいたら IPv6 と判定するようにする。
このサイトの場合、ページ全体をキャッシュしているため、内容をIPv4/IPv6で出しわけると持つべきキャッシュが倍になってしまい現実的ではない。ということで、以下のようにした。
郑州炜盛电子科技 という会社の MH-Z19 という CO2 センサを買ってみました。Aliexpress で $22 ぐらいでした。
5V の電源と 3.3V のインターフェイスが必要ですね。
Ruby 用のライブラリを書いて読みだして、GrowthForecast に投げてみました。Raspberry Pi 上で動かしています。シリアルは USB 経由で、PWM では GPIO 経由で読んでいます。
結構おもしろくて、人がいるかいないかはグラフから一目でわかるレベルです。換気するとそくざに値に反映されるのもおもしろいです。
Ruby で読み出し用のライブラリを書いてみました。
Serial 経由の読みこみとPWM (Linux のみ。sys/class/gpio 経由) どちらとも実装してあります。
Serial は仕様書通りに書いただけですが、PWM はちょっと工夫がいるのと、データシートそのままだとちゃんと値がでず試行錯誤したのでメモしておきます。
PWM はエッジトリガをしかけて時間をそこそこ正確に計測する必要があります。Linux の GPIO でやる方法がわからなかったので、ちょっと調べて以下のようにしました。
select でブロックしてエッジが検出されると制御が戻ります。read したあと seek で戻さないと、次の select でブロックせずビジーループになってしまうようでした。
def self.trigger(pin, edge, timeout=nil, &block)
self.direction(pin, :in)
self.edge(pin, edge) #=> write "/sys/class/gpio/gpio#{pin}/edge"
File.open("/sys/class/gpio/gpio#{pin}/value", "r") do |f|
fds = [f]
buf = " "
while true
rs, ws, es = IO.select(nil, nil, fds, timeout)
if es
f.sysread(1, buf)
block.call(buf.to_i)
f.sysseek(0)
else
break
end
end
end
end
処理速度が不安でしたが、1ms 単位でとれれば十分なので Ruby + /sys/class/gpio でも十分でした。
データシートだと以下の計算式になっています。ここで th は PWM の high の時間、tl は low の時間、ppm は CO2 濃度です。
ppm = 2000 * (th - 2e-3) / ((th + tl) - 4e-3)
が、どうみてもデータシート通りの値がでてきませんでした。どうやら型番が一緒で、検出レンジが 0〜2000ppm のものと、0〜5000ppm のものがあるようで、2000 の部分を 5000 にするとうまく計算できました。
シリアル経由だと、ゼロ補正とスパン補正というのがあります。仕様書にどうやってキャリブレーションするか書いてないのですが、おそらく以下の手順です
↑ の手順おそらく間違いなので以下の正しいと思われる手順を書きます。MH-Z19B のデータシートの手順です。
まぁキャリブレーションは難しいのですが、一定期間内の最低値を外気のCO2濃度 (約400ppm) とみなしてときどき自動で補正していく方法もあるようです。
気象庁の二酸化炭素濃度のグラフを見る限り、近年は 400ppm 程度のようです。季節変動もみられますが、センサーの精度よりも小さいので無視できます。
ビル衛生管理上では 1000ppm を目安に換気せよと書いてあります。これは二酸化炭素の影響だけではなくて、これを目安として他の有害物質の滞留を防ぐという意味合いもあるみたいです。
また CO2 単体でも 2000ppm以上になると自覚できる症状が現れることがあるようです。
花王の漂白剤シリーズに「ハイター」というのがあるが、種類がいっぱいあってよくわからない。というところだけど、実は公式のFAQに一覧があって、どのように成分が違うのかと用途が記載されている。
大きな違いは「界面活性剤」の有無になる。汎用性が高いのは含まれていないほうなので、とりあえず買うなら「ハイターE」「月星ブリーチC」あたり、「ハイターE」は一般向けだと「ハイター 衣料用漂白剤」になる。
なお基本的に「ハイター」には塩素系を期待すると思うが、酸素系の「ハイター」も存在しているので注意が必要。
消毒用途では基本的に10倍希薄して使うことになっている (商品は6%だが、塩素は揮発してしまうため 5% 扱いとする)。0.5%以上 (5000ppm) の濃度で使用しても消毒効果は上がらず、残ってしまう時間が長くなるだけなので、必ず守る。
なお次亜塩素酸ナトリウムが重宝される理由としては、蛋白質と接触すると食塩に変化するため残留リスクが低いことにある。
QAサイトで間違ったことが書いてあると、正解を書きたくなる心理を利用して、どんな回答にもまず間違った答えを機械が書くというのはどうか (あっててもいいけど)
MH-Z19 という格安 CO2 センサを読んでみた | tech - 氾濫原 の続きです。
Open jtalk で喋るようにしてあるので
みたいにした。
alert = false
loop do
begin
ppm = get_latest.last
case
when ppm > 2000
system("jsay.sh", "CO2濃度が2000ppmを超えました。ただちに換気を行ってください")
alert = true
when ppm > 1000
system("jsay.sh", "CO2濃度が1000ppmを超えました。換気を行ってください")
alert = true
when alert && ppm < 600
alert = false
system("jsay.sh", "CO2濃度が600ppmまで下がりました")
end
rescue Exception => e
p e
end
sleep 5 * 60
end
get_latest は GrowthForecast からデータをとってくるやつ。センサーからデータを読むプロセスは別にしてあるので、間接的にgfからとってきてる。
家で余っているスマフォに常時表示するようにした。これも GrowthForecast からデータをとる。具体的には
グラフは vis.js を使ってみた。結構つかいやすい。example のうちの 404 Not Found を参考にしたらうまくできた。
GrowthForecast にデータを投げてるので、gf の API をメインで使ってみたが、gf はそういう用途のためのものじゃないので、かなり無駄感がある。WebSockeet 経由でデータを転送するゲートウェイみたいなのをまず作ったほうがいいかもしれない。
ミラクルニキ-お着替えコーデRPG - Apps on Google Play これ。公式サイトはここ
元々中国のアプリ「奇迹暖暖」で、それをローカライズして販売してるっぽい。暖暖シリーズはいくつかあるっぽく、過去にも日本語版ローカライズ版はでてたりするっぽいがやったことはない。ありがちな日本語に不自然なところはあまりない(が、UIではときどきある)。日本の提供会社の情報があんまりなくて怪しい以外は気になるところはない。
とりあえず最高に楽しい。いろいろゲームやってみた結果、着せ替えができさえすれば楽しいってことには薄々気付きつつあったけど (ポケモン・サン/ムーンやったら着せ替えの衣装が足りなくてものたりなかった)、このゲームは完全に着せ替えオンリーを極めててよい。着せ替え対象のキャラクターは一人だけ(ニキ)だけど、衣装数が極めて多く、髪型も変えれるので気にならない。
キャラクターのデザイン自体も「可愛いが男媚びも女媚びもしてない」ぐらいのバランスだし、衣装も細かく描かれててめっちゃいい。ニキのキャラ設定自体も鼻につかない感じで余計な部分はない。あえていうなら付属してくる変なゆるキャラみたいのがウザいぐらい。
基本的にあんまりハマりどころはないけど、ちょいちょいキツいステージがある。4-12 は衣装がほぼ固定で高得点が出しにくく S がなかなかとれなかった。S限定の衣装をガール級の 5-12 で使うので手に入れたかった (なお 5-12 は 3-1(プリンセス) や 4-5(プリンセス) のチャイナでも代用可能)
結局のところ、衣装でどうにかならないのはスキルでどうにかするしかないことがわかった。このステージだと脱がされないので、厳しい視線を回避してバフかければSとれそう。
自分のときはこれでいけた
h2o の status ディレクティブのJSON出力を眺めていたら、duration-25 とか connect-time-50 とか 不思議なプロパティがいくつか含まれることに気がついた。しかしどうも全て0のようであるので、なんらかのコンパイルオプションなのだろうか?と思ってちょっと調べた。
結局これは global で duration-stats: ON すると有効になるみたいだった。そうすると以下のように数字が埋まりはじめる。
"duration-0": 0, "duration-25": 0, "duration-50": 0, "duration-75": 4731, "duration-99": 26829
おそらくμs単位のパーセンタイル値で、この場合たとえば duration-75 は 75% のリクエストが 4731μs 以内に納まるみたいなやつだと思う。
今のところドキュメントには書いてないっぽい、というかリリースされてないっぽいので HEAD が必要?なのかな。
mackerel-plugin-h2o みたいなのは今のところないっぽい? ので、status/json からとれる内容をポストするプラグインを書いた。
https://github.com/cho45/mackerel-agent-plugins/tree/mackerel-plugin-h2o/mackerel-plugin-h2o
他のプラグインにならって go-mackerel-plugin-helper を使ってみた。FetchMetrics の返り値でちょっと悩んだけど、以下のようにすればよさそう。
GraphDefinition に含まれないメトリクスは、FetchMetrics で map に詰めても送信されない。これが最初理解できてなかったので、FetchMetrics でややこしいことをやってしまったりした。
いままでSANYO時代のエネループ充電器を使っていたが、必要性が増してきたのでこのようなものを買った。
単3と単4どちらも充電できる。一本ずつ充電管理されているのでバランスとかは考えなくて良い。
端子がちょっと固くて、しっかり押しこんでやらないと入らない。一応充電状態にはなってしまうので気をつける。写真のうちいくつかはちゃんと入ってない (撮ったときは気付かなかった) 9 と 10 はちゃんと入っている。1とか4はちゃんと入ってない。
本体側で若干コイル鳴きしててうるさい。といってコイル鳴きなので、かなり静かな環境でなければ聞こえない。
一本も電池を入れてない場合、液晶のバックライトが消えてスタンバイ状態になるようだ。このときの消費電力は 0.3Wぐらい。
全て充電完了になっている場合、液晶のバックライトがついて充電完了を知らせる表示になる。このときの消費電力は0.5Wぐらい。
ACアダプタは12V 1.5A (18W) のものが付属していた。ポートあたり最大 500mA (1.2V) らしいので十分っぽい。
意識高い (意識高い系ではなく、真の) みると、ただただつらい。アドベントカレンダーで毎日意識高いエントリが目につく。精神が攻撃されている。もっと気楽に生きていきたい………
先にまとめておくと、おむつの支出が支配的で、それ以外だと直接子ども用のものをあんまり買ってない。絵本とかをあんまり買ってない気がする。
【おしりふき 詰替用】グーン 肌にやさしいおしりふき タップリッチ 840枚(70枚×12個) [ケース販売] 【Amazon.co.jp限定】 cho45
3ヶ月に1回ぐらい買ってるっぽい。
メリーズ L サイズ → グーン Big サイズ → グーン Big より大きいサイズ と変わった。「Big サイズ」と「Big より大きいサイズ」はまだ併用してて、保育園で昼寝のときに漏れる問題が発生して保育園用のだけワンサイズ大きくしている。
ざっと合計したら60454円だった。だいたい5000円/月ぐらい。だけど保育料の支配率が高いので誤差みたいなもの……
ゆっくといっしょ ぼくひゃっか cho45
くもん出版 くもんのジグソーパズル STEP1 だいすき どうぶつ 知育玩具 おもちゃ 1.5歳以上 KUMON cho45
くもん出版(KUMON PUBLISHING) くもんのジグソーパズル STEP2 はやいぞ しんかんせん 知育玩具 おもちゃ 2歳以上 KUMON cho45
OGK KABUTO(オージーケーカブト) 自転車 ヘルメット 子ども用 FR-KIDS スターホワイト 幼児・児童用(頭囲:50~54cm未満) SG認証 cho45
クリスマスプレゼント多くない? って感じだけど、祖父母分もはいっている (祖父母からは現金が渡ってきて、こちらで適当にあわせて買うという方式)
RiZKiZ 子供用絵本ラック 木目調 キッズブックシェルフ マガヒンラック 収納に便利な仕切り棚付き (3段タイプ/ダークブラウン) cho45
Stokke ストッケ ベビーチェア ハイチェア 本体 トリップトラップ 食卓 赤ちゃん 椅子 ウォールナットブラウン cho45
育児関係で大人用のやつ
ちょっと読んだ感じ小学生以降っぽいのでまだ読んでない。
Raspberry Pi で Wi-Fi が一度切れると二度と繋がらない、という現象に遭遇した。しかたないので変なことをした。
#!/bin/sh
msg() {
echo $1
logger $1
}
while true
do
if ifconfig wlan0 | grep -q "inet addr:"; then
sleep 60
else
msg "Network connection down! Attempting reconnection."
ifdown --force wlan0
sleep 5
ifup wlan0
/etc/init.d/dhcpcd reload
sleep 30
fi
done
ifplugd とかも触ってみたけど、これが結局確実っぽい。なんかどうも dhcpd が ifup 時に反応してくれなくて、手動で reload してる。うーん。もっと一発で解決する方法があればいいんだけど……
systemd になって inittab が消滅したので途方にくれましたが以下のようにするとできました。
UART ピンからのログインの場合
sudo vim /lib/systemd/system/serial-getty@.service
ExecStart=-/sbin/agetty --keep-baud 115200,38400,9600 %I $TERM
となっているので
ExecStart=-/sbin/agetty -a pi --keep-baud 115200,38400,9600 %I $TERM
とする。
なお、この状態でシェルで reset を実行すると halt や reboot がハングするようになる。謎…… reset を殺して対応してるけど理由がわからなくて気持ちわるい。systemd がおかしいのだろうか?
ssh 経由とかの場合。
sudo raspi-config から設定可能です。なので基本的には raspi-config でやったほうがよさそう。
以下は手動でやる方法
sudo vim /lib/systemd/system/getty@.service
ExecStart=-/sbin/agetty --noclear %I $TERM
となっているので、以下のように
ExecStart=-/sbin/getty --noclear -a pi %I $TERM
MH-Z19 という格安 CO2 センサを読んでみた | tech - 氾濫原 を書いたあと、追加で3つ買って、4つ手元にある状態ですべて繋げて値を読んでみると、でてくる値がとんでもないのが2つあった。窓全開で十分に換気されている状態でも 800〜900ppm になってしまう。
センサの種類 (2000ppm or 5000ppm) が違うのか? と思ったけど、UART の値も PWM の計算値も同じ (UART では ppm が直接出力されるので、計算値の答えあわせになる) なので関係なさそう。
その後よくよく比較してみると、どれも全く動きかたがちがう。傾き(感度)も違う。やっぱこれはキャリブレーションをなんとかしてやるしかない気がする。うーん。
ゼロ点とスパン点どっちも難しい。なんか簡単にできる方法がないか考え中
OWON デジタルオシロスコープSDS7102 超薄型 1Gsサンプリング100MHzFFT機能付カラーポータブルフルセット【国内正規品】 cho45
OWON SDS7102 大変つかってるが安いだけあって品質が微妙な点がいくつかある。特にロータリーエンコーダーがひどくて、ちゃんとレンジ切り替えができないことが多くあってイライラする。同様の現象が起きてないか検索したところいくつかヒットしたのでやってみた。
おおむね 500 Can't connect to www.candrian.gr:443 (certificate verify failed) を参考にすればいいのだが、ちゃんと外れないところがあった。
をはずす。裏蓋はハメこみなので、カードとかで隙間をひろげながらはずす。
電源スイッチがひっかかるので、これは押しながら抜く。
開きつつあると、裏蓋に繋がる電源線が3つあるので、基板側のコネクタとビスをはずす。
をはずす。これでモニタも含めてすっぽり外れる。フラットケーブルとかで繋がってるので慎重にやる。
赤丸部分がロータリーエンコーダなので、ここに 1608 サイズの 0.1μF を2つずつはんだづけする。
はずしたコネクタを通すところを間違えるとハメられないので気をつけてハメていく。それ以外はとくに問題ないはず。
全体的に変な動きは減った。しかし中央(2ch) のレンジをかえるロータリーエンコーダはそもそも不良っぽくて完全には治らなかった。
ref.
Aliexpress で歪みゲージ (ロードセル 1kg) と、HX711 のモジュールを買ったので試してみました。
歪ませる必要があるので、このように上下に板をつけて挟みこみます。このとき、多少すきまが必要なのでワッシャーなどをかまします。
今回使った歪みゲージのスペックは以下の通りです。(コピペ) また、M4 と M5 のネジが切ってありました。
Rated Load: 1Kg Rated Output: 1.0mV/V±0.15mV/V Zero Output: ±0.1mV/V Creep: 0.03%F.S./30min Input End: Red+ (power), Black-(power) Output End: Green+(signal), White-(signal) Recommended operating voltage: 3 ~ 12 VDC Maximum operating voltage: 15 VDC Input Impedance: 1115±10%Ω Output Impedance: 1000±10%Ω Protection class: IP65 Total Size: approx. 3.15 x0.50 x 0.50 inch Cable: 0.8 x 20 cm (diameter x length) Material: Aluminum Alloy Weight: 30g
手に入れたのは↑のようなモジュールです。適当に配線して読みます。
以下のライブラリを使ってみました。
README の通りにスケールを設定する必要があります。
#include <Arduino.h>
// https://github.com/bogde/HX711
#include "HX711.h"
const int DT_PIN = 2;
const int SCK_PIN = 3;
HX711 scale;
void setup() {
Serial.begin(9600);
Serial.println("start");
scale.begin(DT_PIN, SCK_PIN);
Serial.print("read:");
Serial.println(scale.read());
scale.set_scale();
scale.tare();
Serial.print("calibrating...");
delay(5000);
Serial.println(scale.get_units(10));
scale.set_scale(-1536.00);
scale.tare();
Serial.print("read (calibrated):");
Serial.println(scale.get_units(10));
}
void loop() {
Serial.println(scale.get_units(10), 1);
scale.power_down();
delay(500);
scale.power_up();
}
このようなコードを書いて、
という方法をとりました。
こんな感じで値が読めました。1円玉を1つずつ増やして5枚まで載せてみました。
同級生との飲み会で若干ハメをはずして飲みすぎて久しぶりに激しい二日酔いになってしまった。二日酔いになるたびに二日酔いをすみやかに解消する方法を調べてるけど、結局ないってことはわかっている。
必要なのは時間と水分(分解に必要)なので、スポーツドリンクをこまめに飲みつつ、梅干し純を舐めて、身体をあたためつつ布団で寝ていた。
アサヒグループ食品 梅ぼし純 24粒×10個 cho45
梅干し純は気持ち悪いのが軽減されるうえに塩分が得られる。スポーツドリンクにも塩は入っているけど、少なめになっているらしいので、ちょっと追加するのがちょうどいいのではないかという狙い。
MH-Z19 の個体差とキャリブレーションの必要性 | tech - 氾濫原 というのを書きましたが、とりあえず 400ppm 環境は比較的簡単に作れるのだから、それでキャリブレーションしてみることにしました。
その過程でこのセンサの「ゼロキャリブレーション」について誤解していたことがわかりました。 MH-Z19 という格安 CO2 センサを読んでみた | tech - 氾濫原 に追記してありますが、ここでいう「ゼロ」は 400ppm 環境のことであるらしく、0ppm の環境は必要ありませんでした。また「スパンキャリブレーション」は最低でも1000ppm以上、2000ppm以上推奨とのことでした。MH-Z19 のドキュメントではなく MH-Z19B のドキュメントのほうが詳しく書いてあり、こちらを参考にしました。
スパンキャリブレーションはそのような特定環境をつくるのが難しいので、今回は「ゼロキャリブレーション」だけを行ってグラフにしばらく投稿してみました。
窓をあけて換気扇をつけ、周辺環境をできるだけ400ppmに近付けます。最低でも30分ぐらいは換気します。室内であっても十分に換気ができていれば400ppm前後にはできるので、気をつけるのは息を吹きかけないようにする (自分が風上にいないこと) ぐらいです。人間の呼気中の CO2 は 4% (40000ppm) ぐらいなので、普通に影響します。
センサはシリアル経由で繋ぎました。起動に数分時間がかかるので放っておきます。
以下のスクリプトを実行します。
https://github.com/cho45/ruby-mh-z19/blob/master/examples/cal.rb
短いのでコピペすると
#!/usr/bin/env ruby
$LOAD_PATH << "#{File.dirname(__FILE__)}/../lib"
require 'mh-z19'
co2 = MH_Z19::Serial.new(ENV['PORT'])
# wait sensor startup
loop do
detail = co2.read_concentration_detail
p detail
case detail[:status]
when 0
p "booting"
when 1
p "startup"
when 64
break
end
sleep 1
end
co2.calibrate_zero_point
sleep 3
p co2.read_concentration_detail
こんな感じになります。この MH-Z19 はドキュメントに書いてないのですが、ステータスビットや認識している温度も返すようで、read_concentration_detail だとそれらもとれるような実装にしてあります。
例によって1日ぐらい放置してみました。スパンキャリブレーションしていないので、やはり最大値に差があります。とはいえ 100〜200ppm 以内には入ってる感じなので、実用的にはとりあえず十分かもしれません。いずれにせよ何もしない場合よりは遥かにマシなので、必ずやったほうがよさそうです。
寝室に置いてみたいので ESP8266 (ESP-WROOM-02) で動かして GrowthForecast にポストするようにしてみた。
MH-Z19 を PWM 経由で読んでいる loop 関数だけ抜きだすと以下のような感じ。とりあえず割込みは使ってない。
void loop() {
ArduinoOTA.handle();
static uint32_t prevTime = 0;
static uint8_t lastState = 0;
static uint32_t th;
static uint32_t tl;
int state = digitalRead(PWM_INPUT);
if (lastState == state) {
// nothing to do
} else {
lastState = state;
uint32_t now = millis();
if (!prevTime) {
prevTime = now;
return;
}
uint32_t interval = 0;
if (prevTime <= now) {
interval = now - prevTime;
} else {
interval = 0xffffffff - prevTime + now + 1;
}
prevTime = now;
if (state == 1) {
tl = interval;
if (tl && th) {
uint32_t cycle = tl + th;
if ((uint16_t)(1004 * 0.95) < cycle && cycle < (uint16_t)(1004 + 1.05)) {
uint16_t ppm = 5000.0 * ((float)(th - 2) / (float)(cycle - 4));
Serial.printf("%d ppm (cycle %d / th: %d, tl: %d)\n", ppm, cycle, th, tl);
gf.post("/home/sensor/co2_1", ppm);
// reset count
th = 0; tl = 0;
prevTime = 0;
} else {
// error
Serial.println("error");
}
}
} else
if (state == 0) {
th = interval;
}
}
}
RabbitMQ + MQTT で Pub/Sub サーバを立てることにしました。
いろいろなセンサーのグラフ化にあたって GrowthForecast へ直接 POST を行っていましたが、やはり一旦メッセージブローカー的なものをいれたほうがよさそうだという感じになってきました。
センサーデータを複数のプログラムから使いたい場合、特にほぼリアルタイムで情報を得たいようなケースだと、直接各アプリケーションに投げるのではなくて、センサーはある一箇所に値を投げることだけを考え、アプリケーションはある一箇所からデータを受けとることだけを考えるように分離したくなります。
例えば今まではセンサーデータをアプリケーションであるグラフサービスに直接投げていましたが、これだとセンサーデータをさらに別のデバイスから読みだして表示するといった場合に、本来の用途ではないグラフサービス側のAPIに問合せたりする必要があって不便です。
MQTT はキューがない (最後の値だけ保存する/Retain) Pub/Sub のメッセージ配信プロトコルで、組込み系だとそこそこメジャーなようです。ググってみるとクライアント実装はそこそこ充実しています。
サーバ実装がいまいちコレというのがない気がするのですが、RabbitMQ のプラグインに MQTT プロトコルサポートがあるので、これを利用するのが比較的よさそうでした。
これ系のプロトコルは MQTT 以外にもいろいろあって、RabbitMQ の本来の用途である AMQP も競合プロトコルになります。AMQP より MQTT が好まれるのはプロトコルのシンプルさのためですが、機能的には AMQP が勝ります。
ひとまず Ubuntu のパッケージをそのままつかうことにしました。
sudo apt-get install rabbitmq-server
Rabbit MQ は Erlang で書かれているので、Erlang 関係のパッケージが大量にはいります。
インストール直後から起動していて、rabbitmqctl status でステータスが見れます (root 権限が必要です)。デフォルトではクラスタリング用のポート25672と、AMQP 用のポート 5672 が listen されていました。
sudo rabbitmqctl status
続いて mqtt プラグインを有効にしておきます。
sudo rabbitmq-plugins enable rabbitmq_mqtt
このコマンドは自動的に設定が反映され、status を見ると mqtt を 1883 で listen していることがわかります。
適当に見てみると /etc/rabbitmq/rabbitmq-env.conf というのが最初からありますが、これは環境変数設定ファイルなのでとりあえずそのままにしておきます。
ログとして /var/log/rabbitmq/$node@$host.log というファイルがあり、これの冒頭に
node : rabbit@stfuawsc home dir : /var/lib/rabbitmq config file(s) : /etc/rabbitmq/rabbitmq.config (not found) cookie hash : log : /var/log/rabbitmq/rabbit@stfuawsc.log sasl log : /var/log/rabbitmq/rabbit@stfuawsc-sasl.log database dir : /var/lib/rabbitmq/mnesia/rabbit@stfuawsc
というログが出ています。この通り設定ファイルは /etc/rabbitmq/rabbitmq.config になりますが、まだないので作る必要があります。
サンプル設定ファイルがあるのでとりあえずこれをコピペしてつくるのがよさそうです。
# とりあえず眺める zless /usr/share/doc/rabbitmq-server/rabbitmq.config.example.gz # コピペからはじめる sudo sh -c 'zcat /usr/share/doc/rabbitmq-server/rabbitmq.config.example.gz > /etc/rabbitmq/rabbitmq.config'
Erlang の設定ファイル形式なのでちょっと読みにくいです。
ここでは方針として以下のようにします。
TLS の設定では取得済みの Let's Encrypt の証明書をそのまま流用します。ただ、Let's Encrypt がつくる /etc/letsencrypt/live 以下が root 以外読めないようになっている一方、RabbitMQ は証明書ファイルを rabbitmq ユーザで読もうとするようで eacces がでます。
しかたないので以下のようにしてアクセスを許可するように変えてしまいました。なにかもっとスマートに解決したほうがいいと思うのですが、思いつきませんでした。
sudo chmod 0755 /etc/letsencrypt/{live,archive}
以下のようにしてみました。
%% -*- mode: erlang -*- %% ---------------------------------------------------------------------------- %% RabbitMQ Sample Configuration File. %% %% See http://www.rabbitmq.com/configure.html for details. %% ---------------------------------------------------------------------------- [ {rabbit, [ {ssl_listeners, [5671]}, {handshake_timeout, 10000}, {log_levels, [{connection, info}, {channel, info}]}, {ssl_options, [{cacertfile, "/etc/letsencrypt/live/cho45.stfuawsc.com/fullchain.pem"}, {certfile, "/etc/letsencrypt/live/cho45.stfuawsc.com/cert.pem"}, {keyfile, "/etc/letsencrypt/live/cho45.stfuawsc.com/privkey.pem"}, {verify, verify_peer}, {fail_if_no_peer_cert, false}]}, {auth_mechanisms, ['PLAIN', 'AMQPLAIN']}, {auth_backends, [rabbit_auth_backend_internal]}, {ssl_handshake_timeout, 5000} ]}, {rabbitmq_mqtt, [ {allow_anonymous, false}, {vhost, <<"/">>}, {exchange, <<"amq.topic">>}, {subscription_ttl, 1800000}, {prefetch, 10}, {tcp_listeners, [1883]}, {ssl_listeners, [8883]} ]} ].
もともとある guest/guest は localhost からだけ接続が許されているのでそのままにして、外部から使う用のユーザを追加します。
# guest しかない $ sudo rabbitmqctl list_users Listing users ... guest [administrator] # ユーザ追加 $ sudo rabbitmqctl add_user tsun pass Creating user "tsun" ... # 確認 $ sudo rabbitmqctl list_users Listing users ... guest [administrator] tsun [] # パーミッションを追加 conf write read の順で指定する。これは正規表現。ここでは全権 $ sudo rabbitmqctl set_permissions tsun ".*" ".*" ".*" Setting permissions for user "tsun" in vhost "/" ...
クライアントに rubygems の mqtt を使ってみます
gem install mqtt
#!/usr/bin/env ruby
require 'mqtt'
require 'thread'
# MQTT::Client seems not thread safe
mutex = Mutex.new
pub_thread = Thread.start do
mutex.lock
MQTT::Client.connect(
host: '127.0.0.1',
port: 8883,
ssl: true,
username: 'guest',
password: 'guest',
) do |client|
mutex.unlock
p [:pub, client]
10.times do
sleep 1
p :publish
client.publish("test", "this is test", false)
end
end
end
sub_thread = Thread.start do
mutex.lock
MQTT::Client.connect(
host: '127.0.0.1',
port: 8883,
ssl: true,
username: 'tsun',
password: 'dere',
) do |client|
mutex.unlock
p [:sub, client]
client.subscribe("test")
client.get do |topic,message|
p [ topic, message ]
end
end
end
pub_thread.join
こんな感じのコードで動いていることを確認できるはずです。ただし guest を指定しているので、同一ホストで動かす必要があります。
基本の動作確認ができたので、パブリックにアクセス可能にするためポートをあけます。ufw で 8883 (MQTT with TLS) だけをあけました。他はいまのところ使用予定がないので閉じたままです。listen 自体しないほうがより安全ですが、サーバ内でごにょごにょすることはありそうなのでそのままにしています。
sudo ufw allow 8883
外部から、さきほど作ったユーザで接続確認を行ってとりあえずセットアップ完了です。
ウェブページから JavaScript で MQTT ブローカーに送られてくるデータを取得したかったので、MQTT over WebSocket を試してみました。
Ubuntu のレポジトリのは 3.5.7 と古いので、Installing on Debian / Ubuntu にしたがって 3.6.6 にします。
既に古いのが入っていても、説明通りに RabbitMQの apt レポジトリを設定して、 apt-get update して install したら自動でアップグレードされます。
rabbitmq_web_mqtt をいれます。これは Community Plugins にありますが、 RabbitMQ のチームが作っているので安心感があります。プラグイン自体が比較的新しくて、RabbitMQ 3.6.1 以降でないと使えません。
$ wget --content-disposition https://bintray.com/rabbitmq/community-plugins/download_file\?file_path\=rabbitmq_web_mqtt-3.6.x-14dae543.ez $ sudo mv rabbitmq_web_mqtt-3.6.x-14dae543.ez /usr/lib/rabbitmq/lib/rabbitmq_server-3.6.6/plugins/ $ sudo rabbitmq-plugins list | grep web_mqtt [ ] rabbitmq_web_mqtt $ sudo rabbitmq-plugins enable rabbitmq_web_mqtt The following plugins have been enabled: cowlib cowboy rabbitmq_web_mqtt Applying plugin configuration to rabbit@stfuawsc... started 3 plugins. $ sudo rabbitmq-plugins list | grep web_mqtt [E*] rabbitmq_web_mqtt
デフォルトで 15675 が listen されています。このままでいいのですが、念のため設定を書いておくことにしました。
設定例がないのでソースを読むしかないようです。rabbitmq_web_mqtt.schema が設定ファイルのスキーマのようなので、これを参考に以下のようにしました。
{rabbitmq_web_mqtt, [ {tcp_config,[{port, 15675}]} ] }
WebSocket 経路の TLS 化はリバースプロキシ (h2o) で行うので、このサーバ自体には TLS の設定を書いていません。
HTTP フロントエンドの h2o からポート 15675 にリバースプロキシするように設定しました。これで外部から接続可能になります。
なおh2oは定期的にWebSocket接続を切るようになっているので、クライアント側で再接続するコードが必須です。
認証はどうなるのか? という疑問があるところですが、MQTT レイヤーでのユーザ認証があります。Upgrade 時に Origin による制限などはかけられないようです。
rabbitmq-web-mqtt-examples というレポジトリがあり、これを参考にすれば簡単に接続できます。
ライブラリとして Eclipse Paho の JavaScript 版を使っています。WebSocket のエンドポイントを指定する以外は普通にMQTTするのと変わりません。
var TOPIC = "/foo/bar/baz";
var USER = "foo";
var PASS = "bar";
function mqttConnect() {
var client = new Paho.MQTT.Client(
location.hostname,
location.port || 80,
"/mqtt", "myclientid_" + Math.random().toString(32)
);
client.onConnectionLost = function (responseObject) {
console.log(responseObject);
// reconnect
setTimeout(mqttConnect, 1000);
};
client.onMessageArrived = function (message) {
var data = Number(message.payloadString);
console.log(data);
};
client.connect({
userName: USER,
password: PASS,
timeout: 3,
onSuccess: function () {
console.log('onSuccess');
client.subscribe(TOPIC, {qos: 1});
},
onFailure: function (message) {
console.log('onFailure', message.errorMessage, message);
}
});
}
ブラウザ上の WebSocket の場合、TLS はブラウザ側でやってくれるので、普通の MQTT with TLS の接続で考えることよりも少なくて楽です。
表題の通りでハマってしまった。
$ sudo rabbitmqctl status ... {rabbit,"RabbitMQ","3.6.6"} ...
$ cat /usr/lib/erlang/releases/18/OTP_VERSION 18.3
{versions, ['tlsv1.2', 'tlsv1.1', tlsv1]}
openssl s_client で接続確認してみる。
$ openssl s_client -connect 127.0.0.1:5671 -tls1_2 < /dev/null
これ問題なかった。
どっちもなぜか同じように以下のようなエラーになる。
$ openssl s_client -connect 127.0.0.1:5671 -tls1_1 < /dev/null CONNECTED(00000003) 139728874526360:error:1409442F:SSL routines:ssl3_read_bytes:tlsv1 alert insufficient security:s3_pkt.c:1472:SSL alert number 71 139728874526360:error:1409E0E5:SSL routines:ssl3_write_bytes:ssl handshake failure:s3_pkt.c:656: --- no peer certificate available --- No client certificate CA names sent --- SSL handshake has read 7 bytes and written 0 bytes --- New, (NONE), Cipher is (NONE) Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE No ALPN negotiated SSL-Session: Protocol : TLSv1.1 Cipher : 0000 Session-ID: Session-ID-ctx: Master-Key: Key-Arg : None PSK identity: None PSK identity hint: None SRP username: None Start Time: 1483074781 Timeout : 7200 (sec) Verify return code: 0 (ok) ---
結局これは Erlang の ssl ライブラリの問題らしく、基本的には Erlang のバージョンをあげる必要があるっぽい。しかしRabbitMQ 3.6.1 / Erlang 18.3 TLS insufficient security failures からリンクがある通り以下のようにするとうまくいった。
{versions, ['tlsv1.2', 'tlsv1.1', tlsv1]}, {ciphers, ["ECDHE-ECDSA-AES256-GCM-SHA384","ECDHE-RSA-AES256-GCM-SHA384", "ECDHE-ECDSA-AES256-SHA384","ECDHE-RSA-AES256-SHA384", "ECDHE-ECDSA-DES-CBC3-SHA", "ECDH-ECDSA-AES256-GCM-SHA384","ECDH-RSA-AES256-GCM-SHA384","ECDH-ECDSA-AES256-SHA384", "ECDH-RSA-AES256-SHA384","DHE-DSS-AES256-GCM-SHA384","DHE-DSS-AES256-SHA256", "AES256-GCM-SHA384","AES256-SHA256","ECDHE-ECDSA-AES128-GCM-SHA256", "ECDHE-RSA-AES128-GCM-SHA256","ECDHE-ECDSA-AES128-SHA256","ECDHE-RSA-AES128-SHA256", "ECDH-ECDSA-AES128-GCM-SHA256","ECDH-RSA-AES128-GCM-SHA256","ECDH-ECDSA-AES128-SHA256", "ECDH-RSA-AES128-SHA256","DHE-DSS-AES128-GCM-SHA256","DHE-DSS-AES128-SHA256", "AES128-GCM-SHA256","AES128-SHA256","ECDHE-ECDSA-AES256-SHA", "ECDHE-RSA-AES256-SHA","DHE-DSS-AES256-SHA","ECDH-ECDSA-AES256-SHA", "ECDH-RSA-AES256-SHA","AES256-SHA","ECDHE-ECDSA-AES128-SHA", "ECDHE-RSA-AES128-SHA","DHE-DSS-AES128-SHA","ECDH-ECDSA-AES128-SHA", "ECDH-RSA-AES128-SHA","AES128-SHA"]}, {honor_cipher_order, true},
日記を読みかえしてみたら今年もいろいろやっていた。年末にせめて自分で自分を承認してあげたい。おおまかには
の順に興味がうつりかわり、同時にウェブ技術とかNC切削とか、普段通りという感じのこともやっていた。振り替えってみると思ったより密度高く生きてるが、主観的にはそうでもないというかやる気がなさすぎて困っているという実感がある。なぜだろう……
h2 対応とか、このウェブサイトの最適化を結構やっていた。ハードウェア触る気力がなくなると、ノートPCだけでぼーっと最適化するみたいな流れ…… ウェブ技術はなんというか、仕事じゃなければあんまり気を張らなくても実装できるので、とりあえずやっておくというか、良い言いかたをすれば癒し的側面がある。
サーバサイドで Mathjax するあたりとかは結構がんばったし効果が高かった。
h2 対応のために h2o を導入したので、それ関係のエントリがかなり多い。
類似エントリみたいな、ちょっとアルゴリズムよりのこともやってみた。ただ実装するというより SQLite にこだわって工夫してやってみたところは良かった。仕事で SQLite 使うことはないので役には立たない。アルゴリズムの実装がものすごく苦手なんだけど (こういうこと言うとバカにされまくる……)、ちょっと頑張った例。TF-IDF なんてクソみたいに簡単なアルゴリズムではあるが……
仕事だと分業のおかげで、こういう上から下まで一貫してチューニングするみたいなのはやる機会がない。
おうちハック的なのもやっている。
デジタルSWR計。アンテナアナライザへの布石。
アンテナアナライザ関係。いろいろ勉強になっておもしろかった。 今年前半では一番大きなプロジェクトだったように思う。数学苦手なのだけれど面白いなと思えたところが地味な収穫かもしれない。
スペアナ最高に楽しい。でも最近高周波回路やってなくてあんまり起動してない……
ヤパチーからの流れで自作して、builderscon で発表させてもらった。今年後半の主なプロジェクトとなった。結果的には、カンファレンスできっかけをもらってカンファレンスのトークに還元した感じになって主観的には良かった。
ESP8266 結構触ってたなという感じ。安くなると夢が広がる。
ebay とかで買ったやつをとにかく動かすシリ〜ズみたいなのをやってた。意外と得るものが多い。そのときそのときで触ってる環境で動かしてるのでプラットフォームに一貫性がない。
ioctl を Ruby から呼ぶのを割とやってて、書いてないけどこれ以外にも SPI を Ruby でやるとかもやってたりした。Ruby は IO 処理書くのがかなり楽なのと全体的に可読性が良く、ビルトインメソッドが充実しているので「パフォーマンスはともかく理解しやすいコードを書く」みたいなときに Ruby で書くことが多い。
gem とか使わないとダメになってくると他の言語に逃げがち。gem、クオリティ低いものが大量にあって、どれを使うべきか?自分で実装すべきか?とか悩んでるうちに1日終わる。テキトーに動かしたいときはほんと時間の無駄。
消費電力可視化もいろいろやってみたが、今のところ実用に至ってない。
急に C++ と親密になって好きになった。組込みなら絶対 C++ で書きたい。
mbed 関係。結構さわったしコードも読んだ。
GPS DO を途中までつくった感じ。完成してない (必要性の無さに気付いて、やる気をうしなってしまった)
いろんなことで、かなり役に立っている。元はとったな〜というぐらいの気分でいる。grbl からmachinekit (beaglebone black) への移行が主なトピック。machinekit を収める筐体作りがまだ途中。
知見はかなり溜ってきた。
Fusion 360 導入で可能性がとてもひろがった。全く新しい分野の技術習得という意味では今年一番価値があったかもしれない。拘束によって作図するのはとても気持ちいい。
3D プリンタがない (置く場所がない) のが悩み。欲しんだけどなあ。
全く分類できないもの
cmake はプログラミング言語ってことはわかったけど結局役に立ってない。相変わらず他人の書いた CMakefile.txt は読む気がしない。