LinuxのHDDに不良セクタが発生したので修復してみた

NAS(Linux機)からファイルを読み出していたら、突然ハングアップしてしまい‥…dmesgを調べてみるとHDDに不良セクターと思しきI/O Errorが発生していました。

sda: Current: sense key=0x3
    ASC=0x0 ASCQ=0x0
Info fld=0x43c024d6
end_request: I/O error, dev sda, sector 1136665808
Buffer I/O error on device sda, logical block 142083226

確かに、このセクターをddで読み出してみると、延々カシャカシャ音を立てるばかり。しばらくするとI/O Errorで失敗します。

dd if=/dev/sda of=/dev/null skip=$((1136665808/8)) bs=4k count=1
(しばらく固まる)
dd: /dev/sda: Input/output error


こういう時は、このセクターに書き込みを行うと、HDDのファームウェア代替(だいたい)セクターに置き換えてくれるはず。
このセクターはファイルデータの一部であることが分かっているので、とりあえず0で埋めます。(ファイルシステムメタデータの一部だと厄介。と言っても0で埋めてあとでfsckをかけてみるくらいしか無いのですが)

注意点として、セクターは512Byte単位ですが、書き込みはLinuxのページサイズである4KB単位で行わないと、データをいったん読み出してから書き込もうとするので、またI/O Errorになってしまいます。
したがって、以下のようにbs=4kを指定して、セクタ番号を8で除算した値を指定します。間違うと別のデータを破壊してしまうので注意。

sudo dd if=/dev/zero of=/dev/sda seek=$((1136665808/8)) bs=4k count=1
1+0 records in
1+0 records out

書き込みに成功しました。

もう一度読んでみると

dd if=/dev/sda of=/dev/null skip=$((1136665808/8)) bs=4k count=1
1+0 records in
1+0 records out

正常に読み出せるようになっていました(というか、そう見えるように代替セクターに置き換えられていると思われます)。


前後のセクターを読んでみても正常そうなので、しばらくこれで様子を見てみます。

ESP32をBluetoothキーボードやマウスにする

Arduino Leonardo等は、USBキーボードやマウスとして振舞わせ、PC等を操作するのに使うことができます。
同じくArduino IDEをプログラミング環境として使えるESP32ではUSBキーボード機能は付いていません。しかし、代わりにBluetooth LEを利用してキーボードとして振舞わせることができるはず。ということで、やってみました。

セットアップ

今回はM5StickCを使いましたが、ESP32ならどのボードでも同様です。

M5StickC ESP32 PICOミニIoT開発ボードフィンガーコンピューターカラーLCD付き

Arduino IDEでESP32を使用するためのセットアップ手順は、下記サイトなどを参考にしてください。
esp32_setup – スイッチサイエンス

BLE HID (Human Interface Device)のためのクラスはESP32のツールキットにすでに含まれていますが、キーボードやマウスとして振る舞うのに必要なセットアップをクラスにまとめたライブラリを作ってくれている方がいるので、これを利用すると簡単です。

  • キーボード

github.com

  • マウス

github.com

このライブラリをダウンロードして、スケッチを保存している Arduino フォルダの下の libraries 内に入れて IDE を起動すれば利用できるようになります。


ライブラリの使い方は、ファイル>スケッチ例>(ライブラリ名) 以下のサンプルを見れば一目瞭然なほど簡単です。
例えば以下のスケッチは、5秒ごとに Hello world とタイプし、エンターキーを押すものです。

#include <BleKeyboard.h>

BleKeyboard bleKeyboard;

void setup() {
  Serial.begin(115200);
  bleKeyboard.begin();
}

void loop() {
  if (bleKeyboard.isConnected()) {
    bleKeyboard.print("Hello world");

    bleKeyboard.write(KEY_RETURN);
  }
  delay(5000);
}

これをESPに書き込んで起動して、BLEに対応したPCやスマートフォン等からBluetoothバイス一覧を開くと、 「ESP32 BLE Keyboard」というデバイスが見えます。これに接続してテキストエディタ等に切り替えると、5秒ごとに入力されるのが確認できます。
PCだけでなく、iPhoneでも動作を確認できました。

マウスでも同様です。ただし、こちらはiPhoneでは動作が確認できませんでした(Assosiative Touchを有効にしてみたりしたのですが、ボタンもカーソル移動も反応せず…)。

追記

コメントで教えていただきました。多少の修正で接続できるようになるとのことです。
参考:
IoS 13 compatibility? · Issue #3 · T-vK/ESP32-BLE-Mouse · GitHub
ESP32でiOSにも繋がるBLE Mouse - Qiita


このままだと勝手に動くだけですが、せっかくM5StickCを使っているので、カウントダウンを表示したりボタン操作で一時停止などができるようにしてみました。
下記サンプルではCtrlキーを5秒ごとに押しています。(これだけだとあまり意味はないですが、ゲーム等と組み合わせたり、使い方は工夫次第ということで。)

続きを読む

太陽電池で動くワイヤレス環境センサー

前回の記事のドアセンサーで使ったTWELITEを使って、太陽電池で動くワイヤレス環境センサーを作りました。
BME280で温度・湿度・気圧を計測し、定期的に無線で送信することができます。電気二重層コンデンサを搭載しており、夜間もデータ送信が可能です。
主にエナジーハーベストの実験目的です。


使ったもの

受信用はMONOSTICKをRaspberry PiにUSB接続しています。

太陽電池はTWE-EH SOLARに付属していますが、試しに室内用の別のパネルに変えてみました。日の入らない室内など、暗めの環境では有効ですが、屋外など十分昼に明るくなる環境であれば付属のものでも十分動作しそうです。

※ BME280のブレークアウトボードは、商品名にSPIとか書いてありますがI2C接続です。

作り方

配線方法は、TWE-EH SOLARの製品ページにあるPDFの通りです。
https://mono-wireless.com/jp/products/TWE-EH-SOLAR/index.html

裏面はこんな感じに。だいぶ空間が空いていてスカスカ。ケースは手持ちの適当なやつですが、ちゃんと選べばもっと小さくもできそう。

TWELITEには上記ページにある「無線タグアプリ」の子機用のファームウェアを書き込み、10分に一度,BME280の情報を送信するように設定します。注意書きにある通り、起動時のOTAは無効にするようオプションビットを設定しておきます。

またMONOSTICKにも「無線タグアプリ」の親機用(MONOSTICK BLUE用)を書き込みます。

すると、親機の方のシリアルにデータが流れてくるので、あとはこれを記録してあげればOKです。

キャパシタ電圧の変化

A1の値として、キャパシタ(電気二重層コンデンサ)に蓄電されている電圧値(の1/2の値)を取得することができます。これをプロットしてみると、夜間に暗くなった時にキャパシタの電力を使って動作している様子を見ることができます。以下は約4日分のデータ。夜間に電圧が落ちて,日が出てくると一気に充電されています。

f:id:NeoCat:20190804112422p:plain

今回設置したのは屋外の直射日光の当たらない場所ですが、ちゃんと終夜で動いています。ただし、夜間も暗いながらも夜間灯の光が入っており、多少は消費を緩和している可能性はあります。

ボタン電池で年単位で使えるワイヤレスドアセンサーを作った

ボタン電池で数年ほど動作し続ける、ワイヤレスなドアセンサーを作りました。よくあるIoTネタですが、オフィスのトイレ個室が混雑しがちなので、自席で空きがわかると良いなというやつです。

外観と、ドアの上部に載せたところ。
f:id:NeoCat:20190507122108j:plain

仕様

センサーは、1分間ごとにドアの開閉状態を無線送信します。(リアルタイム送信もできるのですが、あえて1分間ごとのみにしています。用途的に、混雑しているかが荒く分かればよく、逆にあんまりリアルタイムに監視したくないからという理由です。)
複数のセンサー情報を集約して混雑しているか空いているかをWebページで見ることができます。

使用したもの

無線送信にはTWE-LITEを使用しました。簡単に省電力な無線システムを作ることができます。いくつかタイプがありますが、ボタン電池ホルダーが付いているPALを使いました。

無線マイコンモジュール TWELITE BLUE PAL-トワイライト ブルー パル MW-B-PAL-P 【アンテナ内蔵タイプ 標準出力(1mW級)】

実は 開閉センサーPAL|MONO-WIRELESS.COM というのが販売されているので、これを使えばあとは磁石さえ用意すればOKです。
が、そこそこ値段もするので、今回はセンサーにはリードスイッチをつけて使います。

ガラス管の金属リードがわずかに離れて2本入っており、両側のそれぞれに磁石のS極とN極を近づけると、磁力でくっついて導通するという仕組みです。

Hommy 磁気センサー 10xMKA-10110 10-15AT磁気センサー N/O SPSTコンタクトリードスイッチ

子機のケースにはタカチのSW-90Bを使用。ちょっと長さも高さも余ってしまいますが、幅の小さいケースがなく……。
SW型プラスチックケース 黒 SW-90Bの通販ならマルツオンライン

マグネットはこれ。めちゃくちゃ小さいので対向位置にくっついていても邪魔になりません。ただし位置は結構ぴったりと合わせる必要があります。

OMO Magnetics 【世界最強マグネット】 強力マグネット 強力磁石 N50 ネオジム磁石 ネオジウム磁石 円柱型 丸型 2x10mm 直径2mm 厚み10mm ニッケルメッキ 専用ケース付 20個セット

親機としてはDIPタイプのTWE-LITEを使いました。

超簡単! 無線マイコンTWELITE DIP-トワイライトディップ 2次元マッチ棒アンテナタイプ ピンヘッダ端子実装

親機からWi-Fi経由でデータを流すのにはM5Stick-Cを使いました。
M5Stick-C - スイッチサイエンス

配線

こんな感じにリードスイッチをつけます。
f:id:NeoCat:20190605101954j:plain

A1 --+-- 1〜5MΩ  -- Vcc
     |
  リードスイッチ
     |
GND -+

リードスイッチがONになると、A1がプルダウンされます。

親機側は、M5Stick-Cにくっつけました。ちょっと強引
f:id:NeoCat:20190430204116j:plain

ファームウェアの書き込みと設定

ファームウェアとして、以下のページからダウンロードできる「無線タグアプリ」を使用しました。
無線タグアプリ (App_Tag) - MONO-WIRELESS.COM

子機には子機用のバイナリ(現時点で最新のv2.1.5)を書き込みます。適当な3.3VのFTDI USBシリアルでGND/TXD<->RXD/3.3Vにつなぎ、PRGをLOWに落として電源を入れ、PRGをHIGHに戻すとプログラムモードに入ります。
接続方法は以下。 UART-USBケーブルでの書き込み - MONO-WIRELESS.COM
Mac/Linuxから書き込む際は、SDK(ソフトウエア開発環境 TWELITE SDK - MONO-WIRELESS.COM)の Tools/tweprog_py に含まれるPythonのツールを使います。

sudo pip3 install pyftdi
sudo ./tweterm.py -p ftdi:///1 -F ./App_Tag-EndDevice-BLUE.bin  # ファームウェアを指定して書き込み

M2ピンをLOWにして起動すると設定画面に入り、以下のように表示されます。

--- CONFIG/App_Tag V2-01-5/SID=0x810e____/LID=0x01/RC=9065/ST=0 ---
 a: set Application ID (0x810e____)
 i: set Device ID (1=0x01) 
 c: set Channels (20) 
 x: set Tx Power (13) 
 b: set UART baud (38400) 
 B: set UART option (8N1) 
 k: set Enc Key (0x5A5A5A5A) 
 o: set Option Bits (0x00000441)*
 d: set Sleep Dur (60000)*
 w: set Sensor Wait Dur (30) 
 m: set Sensor Mode (0x10) 
 p: set Sensor Parameter (0)*
 P: set Sensor Parameter2 (  ) 
---
 S: save Configuration
 R: reset to Defaults

ここで、App IDやチャンネルなどを適当に設定していきます。子機が複数あるので、IDとして1, 2, 3などと違う番号をつけておきます。 オプション(o)として 0x441 を指定して、OTAは無効にしておきます。センサータイプは10(アナログセンサー)にしておきます。

親機には親機用のバイナリを書き込みます。こちらは書き込み後に + + + と入力すると設定用のインタラクティブモードに入るので、子機と同じApp ID、チャンネルを指定します。
無線受信したデータがシリアル出力されるので、これをESP32側のSerial1で受け取り、適当にパースしてWi-Fi経由でHTTP送信するようにしました。

結果

こんな感じで混雑状況がわかります。
あえて個別の開閉状態は表示しないようになっています。

空いた時にお知らせしてほしいとはみんな思うようで、早速 1liner でこのページを定期取得して空きができたらポップアップ通知するスクリプトを書いた人などが現れていますw


ちなみに親機と子機は壁2枚隔てた距離にありますが、かなり感度は良好で滅多にデータ欠損などはないようです。

EXAPUNKSのソリティアを自動操作で解く

前回の記事ソリティアの解法を求めるコードを紹介しましたが、これに画像認識と自動マウス操作機能を加えて、全自動で解くようにしました。

youtu.be

コードはGitHubにあります。 GitHub - NeoCat/exapunks_solitaire_solver: Automate EXAPUNKS ПАСЬЯНС (Solitaire)

画像認識は、screencaptureコマンドで撮ったスクリーンショットOpenCVで画像認識しています。画像認識といっても、ゲーム画面の位置を探し出して切り抜き、カードの左上の部分のテンプレート画像を用意しておいて match_template でマッチングしているだけの簡単なものです。

これで認識したカードの並びを前回のソルバーで解き、その手順に従ったカード移動操作を cliclick で自動化しています。

EXAPUNKSのソリティアをRubyで解くついでにJITを試してみる

プログラミングっぽい独特なパズルゲームを開発しているZachtronicsが出したEXAPUNKSというゲームで遊んでいます。
簡単なアセンブリのような言語でEXAというウイルスのようなエージェントをプログラミングし、ストーリーを追いながらハッキングでデータを改ざんしたりハードウェアを操作したり、時には別のハッカー(プレイヤー)のプログラムと対戦したりするというパズルゲームです。

store.steampowered.com
www.youtube.com

さて、本編もなかなかやりごたえのある内容(かつ適度な楽しめる難易度)なのですが、少しストーリーを進めると、おまけで息抜きにミニゲーム「ПАСЬЯНС」(ソリティア)で遊べるようになります。

数字カードは赤黒赤黒…と互い違いの色で降順に重ねることができ、絵のカードは同じスートのカードは重ねられるという条件を満たしつつ、一枚、またはこの条件で重ねたカードをまとめて移動して積み重ねていき、最終的に 10-9-8-7-6 という降順のセット4つと各スートの絵文字4枚を重ねたセット、計8セットを作れば勝利、というルールです。
なお、途中で一枚だけ、右上の空きスペースに避けておくことが可能です。
f:id:NeoCat:20190509074138p:plain

息抜き用とはいえ、適当にやっていると結構、途中で詰まってしまうくらいの難易度です(UNDOもできないし)。
せっかくのプログラミングゲームなので、このПАСЬЯНСをプログラムで解かせてみました。とはいえこのゲームのEXA用言語ではなく、普通のRubyです。

Solve ПАСЬЯНС puzzle in EXAPUNKS · GitHub

※ 以下のデータ入力と操作の自動化については次の記事で:
EXAPUNKSのソリティアを自動操作で解く - Okiraku Programming


入力として data.txt というファイルに、カードの並びを左上から右に記入したものを与えます。絵のカードは各スートを s h c d の文字で、数字のカードは赤6なら r6 黒10なら b10 のように記述します。上記のスクリーンショットであれば、以下のようになります。

r10 r8 b8 r6 h c c b7 r7
d b8 s d b6 b9 r9 b10 b10
c b9 r10 r7 c r8 h r6 d
s r9 d h s h b7 b6 s

これに対して solver.rb を走らせると、以下のように解法の手順が表示されます。

movable: 0 -> 4  ::  s -> s
movable: 0 -> 9  ::  c -> []
movable: 0 -> 2  ::  d -> d
movable: 3 -> 5  ::  h -> h
movable: 4 -> 8  ::  s-s -> s
movable: 7 -> 3  ::  b6 -> r7
...

:: の左が移動するカードの位置(0が左で8が右の山、9は右上の空きスペース)、:: の右はカードの数字や絵カードのスートを示します。
まず、0(一番左)のスペードの絵カードを4(真ん中)のスペードに重ねる。
次に、0(一番左)のクラブの絵カードを右上の空きスペースに退避する。

という感じです。

このままでも、通常は一瞬で、遅くとも数秒で解けますが、稀に延々計算していて解が表示されないことがあります。このプログラムでは素朴に手順を深さ優先で探索しているために、解が見つからない場合には最後の方の似たような手順を何度も調べ続けてしまうためです。
ランダムな配置でどれくらい解ける*1のか調べてみたところ、だいたい96%くらいは解けるようでした。

ちょっと素朴すぎるので、次のように適当に深読みしすぎるのを防止してやると、ほぼ確実に解けるようになります。

+$depth_limit = []
+
 def scan(deck, steps)
+  (0...(steps.length-1)).each do |depth|
+    return if ($depth_limit[depth] += 1) > 30 * 9 ** (steps.length - depth + 1)
+  end
+
   rem = 0
   (0..9).each do |i|
     unless deck[i].length == 0 || completed(deck[i])
+      $depth_limit[steps.length] = 0
       rem += 1
       scan_i(deck, i, steps)
     end


時間がかかる例として、以下の入力があります。

b10 r8 h b6 c b7 r10 r7 r7
h s b10 b6 d b8 r10 r9 s
c c d r6 s s d h r9
h b9 b8 b7 b9 d c r6 r8

depth_limitをつけた上で、手元のMacBook Proでだいたい10秒くらいかかります。せっかくCPUインテンシブな負荷なので、たまたま手元にあるRubyのバージョンごとに速度を測ってみました。

Rubyバージョン 所要実時間(3回平均) CPU時間(user+sys, 3回平均)
ruby 2.3.7p456 10.63s 10.59s
ruby 2.5.3p105 10.42s 10.38s
ruby 2.6.3p62 8.79s 8.77s
ruby 2.6.3p62 --enable-jit 8.39s 16.76s
ruby-trunk(2.7.0-dev 025206d0dd) 9.17s 9.13s
ruby-trunk(2.7.0-dev 025206d0dd) --enable-jit 8.13s 10.56s


JITで多少ですが実時間が早くなっていますね。
また、ruby-trunkではJITなしの時はv2.6.3よりも若干遅くなっていますが、JITありではさらに高速になっており、かつJIT時のCPU時間の上昇も抑えられているのが分かります。

*1:5秒以内に終了しない場合は解けないとみなしました

Macのテキストエディットやメモで入力される制御コード

macOS (Mojaveで確認) のテキストエディットやメモ、メールといった、標準テキスト入力を使ったアプリケーションでは、option や ctrl キーの組み合わせによって特殊な制御文字コードが入力される場合があります。これらは目に見えなかったり単なる改行に見えますが、処理するプログラムによっては思わぬところで文字化けや表示が崩れる原因になることがあるので、注意が必要です。

なお、通常の改行は LF (U+000A) です。

ctrl + enter (メモ帳などでは shift + enterも同様)

Unicode で定義された Line Separatpr (U+2028) が入力されます。
これは箇条書きなどの中で改行するために使われているようです。

ctrl + option + enter

CR (U+000D) が入力されます。単独でも改行されますが、直後に enter を押すと CR+LF となってこれも一つの改行としてレンダリングされます。

ctrl + option + アルファベット

C0 Control Codeに定義された制御コードが入力されます(記号は英語キーボードの位置に準拠するようです)。以下に一覧があります。

C0 and C1 control codes - Wikipedia

参考: Unicodeにおける空白文字

空白文字の一覧は以下にあります。

Whitespace character - Wikipedia

Line Separatpr (U+2028) はHTMLだと < BR > に相当するものとのこと。
Paragraph Separatpr (U+2029) はHTMLだと < P > に相当するそうですが、入力する方法は見つかりませんでした。
(といっても、例えばWebブラウザからこれらのタグのレンダリング結果をコピーしても単なる LF になります。)

プログラミング言語によって、これらが改行扱いされるかは異なるようです。場合によっては、これらが改行として扱われることを想定していないコードにおいて脆弱性を引き起こす原因になることもあるとか。