macOS 13 Ventura で全ての通知を閉じるスクリプト

macOSの通知で、個々の通知をクリックするまで残しておきたいケースでは、通知のスタイルを「通知パネル」にし、かつ「通知をグループ化」をオフにして利用します。
(私の場合は Slack の自分宛メッセージの通知などでこうしています。)

しかし、時には通知が(場合によっては自動的に)大量に発行され、画面に大量に溜まってしまう、なんていうこともあります。
こんな時に一気に閉じたくなりますがそういったボタンはなく、連打してもゆっくり消えるアニメーションが入るためになかなか閉じられずにストレスになります。。

そんな時のために一気に閉じる AppleScript を用意しておくと良いでしょう。
似たようなスクリプトはあちこちで見つかりますが、内部的な通知センターのUIの作りに依存しているため、OSのバージョンが異なったりすると動作しません。
以下は Ventura で動作を確認しているものです。

なお、各通知に対して下から順番に閉じるアクションを発行してもなぜか一部の通知が閉じずに残ったりする時があるので、何度かループしてやり直すという苦肉の策をとっています。

スクリプトエディタに下記を入力して、アプリケーションとして保存してダブルクリックで利用できます。なお初回実行時に許可を求められるので、保存したアプリケーションを「設定 > プライバシーとセキュリティ」の「オートメーション」と「アクセシビリティ」でアクセスを許可しておく必要があります。


activate application "NotificationCenter"
tell application "System Events"
	tell process "NotificationCenter"
		tell UI element 1 of scroll area 1 of group 1 of window "Notification Center"
			set loop to true
			
			repeat while loop
				set theWindows to every UI element as list
				set n to (count theWindows)
				set closedCount to 0
				repeat with i from n to 1 by -1
					try
						set theWindow to item i of theWindows
						set theActions to actions of theWindow
						repeat with theAction in theActions
							if description of theAction is "Close" then
								tell theWindow
									set closedCount to closedCount + 1
									perform theAction
									exit repeat
								end tell
							end if
						end repeat
					end try
				end repeat
				if closedCount = 0 then
					set loop to false
				end if
			end repeat
		end tell
	end tell
end tell

安価なToFセンサ MaixSense-A010

MaixSense-A010 (または MetaSense A010 ) という3Dデプスセンサ(ToFセンサ)がSipeedから発売されていたので使用してみました。
100x100の解像度で、2.5mまでの距離を8bit精度で、最大19psで取得できるという性能を持っています。
接続はUSB Type-Cとシリアルを利用できます (後述しますが、シリアル接続だと100x100 最大fpsでの利用は厳しめです)。
下記写真のようにLCDを搭載したモデルもあり、これだと他にマイコン等を使うことなく手軽に動作や視野の確認ができて便利です。

下記はAmazon.co.jpのリンクですが、慣れている人はSeeed studioやAliExpress等で購入した方が安く(おそらく5000円前後くらいで)購入できます。

Sipeed MaixSense A010

使い方

PC用のツールで画像取得でき、こちらのWikiに方法がまとまっています。
が、今回はこちらは使わず、プログラムからデータを取得してみました。

USB接続すると、2つのシリアルポート(FTDI)として認識されます。そのうち若い番号のほうでコマンド送信やデータの受信をします。なおこのUSBシリアルでは通信速度設定などは無視されます。

$ lsusb
Bus 001 Device 004: ID 0403:6001 Future Technology Devices International, Ltd FT232 Serial (UART) IC
Bus 001 Device 011: ID 0403:6010 Future Technology Devices International, Ltd FT2232C/D/H Dual UART/FIFO IC

ATコマンドを送って、転送開始や解像度・fps設定などを指示をします。
最も単純には、データ送信先としてUSBを指定し、ISP(画像信号処理)を開始させる2つのコマンドを送ればOKです。

MaixSense-A010 Development - Sipeed Wiki

この時注意点として、データ送信先としてシリアルを指定すると、1fps程度でしかデータが取得できなくなります。これはシリアルの通信速度がデフォルトだと115200bpsであり、これでは100x100x8bitのデータはせいぜい1秒に1枚程度を送るのが限界だからで、USBやLCDもそれに引きづられるためです。シリアルの速度を AT+BAUD=コマンドで上げればfpsも上がりますが、ESP32マイコンのHardwareSerialで受信してみた限りでは230400bpsが限界で、それ以上にしてしまうと同期が取れずに正常にデータが受信できませんでした。このためシリアルしか接続手段のないマイコン等では解像度またはfpsを下げないと厳しいと思われます。

USBであれば通信速度の問題はありませんので、マイコンを使うならRaspberryPi等USBが使えるものが良いでしょう。ただしちょくちょくdmesgを見ているとパケットロストのようなエラーが出たりハングしたりするので、下記プログラムではエラーチェックやリトライは厳しめにしています。

データフォーマットも上記にWikiにある通りですが、ざっくり下記のようなフォーマットで流れてきます。

<ff> <00> <size (little endian 2byte)> <header (16B)> <data (10000B)> <checksum (1B)> <0xdd>

これをパースして、WebSocketに流すプログラムをRubyで書いてみました。

続きを読む

冷凍庫の温度変化を無線センサーで測ってみた

冷凍庫にちょっとまだ暖かいものを入れた時、周囲のものにどれくらい影響があるのか気になりませんか? ということで、小型の無線センサーで温度変化を計測してみました。

使用機器

TWELITE ARIAは小型の無線センサータグです。-30℃〜85℃の温度範囲を0.01℃の分解能で計測できるため、冷凍庫でも動作するはず。
温湿度や扉の開閉等を磁気で計測して無線送信でき、2.5cm × 2.5cm × 1.0cm と小型です。コイン型電池(CR2032)1つで動作し、1分ごとの計測だと4年以上も動作するという省電力も良いですね。

BLUE(送信出力1mW)とRED(10mW)があり、冷凍庫の壁で隔てられた内側から送信することもあってRED版を使います。というか出力以外はほぼ同スペックのようなので、ほとんどのケースでREDを選ぶのが良さそうです。

モノワイヤレス 磁気・湿度・温度センサー無線タグ TWELITE ARIA RED


受信機として、USB接続できるMONOSTICKがあると簡単です。

モノワイヤレス USBスティック MONOSTICK レッド

セットアップ

初期設定として、送信チャンネルなどを設定します。設定方法はMONOSTICK側から設定を送信するOTAか、シリアルで直接接続して行います。
今回はすでに他の用途でMONOSTICKを使用していたので、それに合わせるようにシリアルで設定を行いました。

設定用のUSBアダプタ も販売されていますが、適当な3.3V FTDI - USBボードをすでに持っていればそれでも問題ありません。適当なターミナルソフトウェアで115200bpsでUSB FTDIにシリアル接続しておき、バッテリーを抜いてシリアルTX/RXと電源をARIAに接続します。SET端子をGNDに落とした状態でRSTをLO→HIにしてリセットをかけると,シリアルに以下のようなメニューが出てきて設定を行えます。

 a: set Application ID (0xabcd1234) 
 i: set Device ID (42=0x2a)
 c: set Channels (12)
 x: set Tx Power (13) 
 b: set UART baud (38400) 
 B: set UART option (8N1) 
 k: set Enc Key (0xa5a5a5a5)
 o: set Option Bits (0x00000000) 
 t: set Transmission Interval (15) 
 p: set Senser Parameter (0x00000000) 
 d: set Temperature Coefficient (0) 
 D: set Temperature Offset (0) 
 f: set Humidity Coefficient (0) 
 F: set Humidity Offset (0) 

MONOSTICK側では、標準で入っている App_Wings というソフトウェアを利用します。ファームウェアを書き換えている場合は Wings への書き換えが必要です*1。PC等に接続するとUSBシリアルとして認識されるので、そこに115200bpsで接続して + キーを1-2秒ほどかけて3回押すと、インタラクティブモードに入って同様に設定を行えます。

ここで計測値の送信間隔のほか、ARIAとMONOSTICK側に同じApplication IDと通信チャンネルを設定します。また、もし暗号化等を行う場合は暗号キーやその有効化フラグ(Option Bits)を設定します。他に、複数台のARIAを使用する場合は識別用にDevice IDを設定しておきます。ちなみにUARTの速度もメニューに出て来ますが、特定のOption Bitsを有効化しないとこの設定は使用されません。

詳細は商品紹介ページやドキュメントを確認してください。

どちらも設定がうまくいったら S を入力して設定値を保存すると、リセットがかかってその設定で動作し始めますので、通信が確認できたらARIAはバッテリー動作に切り替えます。

受信してみる

無事データを受信できると、MONOSTICKのシリアルにHEXで以下のようなデータが流れてきます。

:80000000D201858201C1BB2A800607003400038135001205350401000000113008020DAC113001020550000000018005010002063F010200021204FCB2

形式は以下ページに記載されています。
アリアアプリ - WINGS

がこのままでは読みづらいので、プログラムで温度に変換してやります。
MacLinux (RaspberryPi) で動作確認しています。使用する場合はシリアルのデバイス名を環境に合わせて書き換えてください。

なおこのプログラムはARIAの他に無線タグアプリの温湿度データも読めるようになっています。

#!/usr/bin/env python3
import datetime
import serial

ser = serial.Serial('/dev/ttyUSB0', 115200)  # Linux
# ser = serial.Serial('/dev/cu.usbserial-MW3NRDHG', 115200)  # macOS

def to_int(data, start, len, signed=False):
    ret = 0
    for x in data[start:start+len]:
        ret = ret * 256 + x
    if signed:
        ret = ret - 65536 if ret > 32768 else ret
    return ret

def parse(line):
    line = line[1:].rstrip()
    data = [x for x in bytes.fromhex(line)]
    pos = 0
    result = []
    if len(data) == 23:
        # 8000000030F523810C5978003A6F03B1022D039914445A
        # ^^^^^^^1^2^^^3^^^^^^^4^5^6^7^^^8^^^9^^^a^^^b^c
        for l in [4, 1, 2, 4, 1, 1, 1, 2, 2, 2, 2]:
            result.append(to_int(data, pos, l))
            pos += l
        result[6] = 1950+result[6]*5 if result[6] < 170 else 2800+(result[6]-170)*10
        result[9] = result[9] - 65536 if result[9] > 32768 else result[9]
        result[10] = result[10] - 65536 if result[10] > 32768 else result[10]
        return "relay=%X LQI=%d seq=%X id=%X sid=%X type=%X mV=%d adc1=%d adc2=%d temp=%d hum=%d" % tuple(result)
    else:
        for l in [4, 1, 2, 4, 1, 1]:
            result.append(to_int(data, pos, l))
            pos += l
        result.append(to_int(data, 34, 2))
        result.append(to_int(data, 51, 2, True))
        result.append(to_int(data, 57, 2, True))
        result.append(to_int(data, 46, 1))
        return "relay=%X LQI=%d seq=%X id=%X sid=%X type=%X mV=%d temp=%d hum=%d mag=%X" % tuple(result)

while True:
    line = ser.readline().strip()
    if not line.startswith(b':'):
        continue
    data = '{0:%F %T}\t{1}'.format(datetime.datetime.now(), parse(line.decode()))
    print(data, flush=True)

実行するとこんな感じの出力が出ます。tempの値が温度(℃)の100倍、humは湿度(%)の100バイです。他に磁気センサ(mag、HEX)や電池電圧(mV)、電波強度(LQI)等が出ます。
この例では -19.53℃ です。冷凍庫に入っているので冷えてますねw

2023-02-04 05:39:17	relay=80000000 LQI=30 seq=68D5 id=8201C1BB sid=2A type=80 mV=2530 temp=-1953 hum=4773 mag=80

出力をリダイレクトしてファイルに記録しておきます。

冷凍庫の温度変化をグラフ化してみる

さて、ARIAを冷凍庫に入れてみたところ、庫外の5-6mほど離れたMONOSTICKで無事庫内からのデータを受信できました。
十分ARIAが冷えたところで,炊き立てご飯を粗熱をとって冷凍庫のARIAの少し離れたところに入れてみた結果をプロットしたのが以下です。

30分周期くらいで山ができていて、冷凍庫の作動状況が見て取れますね。21:00くらいに3-4℃ほど上がったところで庫内に食品を入れています。ちょっと山が潰れているところがありますが、この期間はバースト的に通信に失敗してデータが欠落していたようです。2.4GHzで通信しているので、他の同周波数帯の通信の影響を受けてしまったのかもしれません。

特に冷凍庫は動きっぱなしになるでもなく、徐々に数時間かけて冷えていっているのが分かります。庫内には他に保冷剤なども入っていたので、それらと暖かい食品が冷えていく過程と冷却動作がそれぞれ影響しあっているようなイメージでしょうか。


というわけで、冷凍庫のような冷えた環境でもちゃんとTWELITE ARIAが動作することが確認できました。

*1:最初、無線タグアプリの親機モードで受信したところ、途中でデータ出力が止まってしまいました。ARIAはデータが大きくWingsでないとダメなようです。

片手用Bluetoothキーボードを買ったのでカスタマイズツールを作ってみた

Amazonで、2つのノブの付いた片手用Bluetoothキーボードを買ってみました。Bluetooth付きのもの・USB専用のものや、6/9/12キー他、3ノブのものなど、色々種類があるようです。
いろんなブランド名で出品されており怪しげなのですが、念のためAmazon出荷のものを選んでみました。

片手キーボード (Bluetooth接続 3層レイヤー機能付き ノブ2個付き 9キー) 黒
片手キーボード (Bluetooth接続 3層レイヤー機能付き ノブ2個付き 9キー) 白


接続はUSB Type-Cです。Bluetooth対応版だとリチウムイオン電池を内蔵していて、USB接続中に充電されます(USB接続中はUSBキーボードとして振る舞います)。ノブ付きの片手キーボードデバイスは意外と高価なものが多いので、多少安価な部類に思います(それでもBluetooth版だと価格は上がりますが)。質感もアクリルパネルで悪くはないのですが、裏面がネジ露出でそのままだと置いた場所が傷だらけになるので、ゴム足なりビニールテープなりを貼る等の対策が必須です。
Bluetooth版だと3つのレイヤーに違うキーを割り当ててボタンで切り替えて使うこともできます。

しかし最大の欠点は設定ツールで、商品説明のGoogleDriveリンクで配布されている未署名のWindows用アプリしかなく、ちょっと怪しげな感じで作りも最低限のものです。商品名にはLinux/Macなどと書いてあるのですが、設定するにはWindowsが必須になってしまいます*1


そこでLinux/Mac、あるいはWinでもこのアプリを使わずに設定できるよう、どうやって設定されているのか調べてみました。
やり方は簡単(?)で、Windows(念のため環境が壊れてもいいように仮想環境でやりました)上でこのアプリを動かし、WiresharkでUSBキャプチャして設定時の通信内容からプロトコルを調べます。

*1:一旦設定さえしてしまえばツールがなくてもキー割当はデバイスが記憶してくれるのですが

続きを読む

ArduinoをPS/2キーボードとして振る舞わせる

Arduino (Pro Mini) からPS/2キーボードの信号を送り、PCにキー入力ができないかやってみました。

PS/2端子

キーボードやマウス用の端子であるPS/2端子。今時は搭載していないPCも多いですが、例えばASRockのマザーボードには今でも搭載されているようです。ゲーム用に多数のキーを同時押しできるロールオーバー対応するためでしょうか。ただし、キーボード・マウスにそれぞれ独立の端子ではなく一体となっているタイプで、2つ繋ぐならY字型のスプリッタケーブルで分離して繋ぐようになっています。


ピン配置はこんな感じ。元々のPS/2では2, 6ピンは未接続のピンでしたが、一体型ではここにClockとDataをもう一系統割り当てています。



1 Keyboard Data
2 Mouse Data
3 GND
4 +5V
5 Keyboard Clock
6 Mouse Clock

Arduino Pro Miniとの接続

今回はキーボードとして接続するので、PS/2の1, 3, 4, 5ピンと接続します。(写真だと電源はシリアルアダプタから取っているため4ピンは未接続)
Arduino側は GPIO 2ピンをData, GPIO 3ピンをClockに接続。


プログラム

ArduinoPS/2エミュレーションをするライブラリとしてps2devというものがあります。
ライブラリマネージャで見つかるものはキーボードのサンプルもなく、色々組んでみてもタイミング等の相性が厳しくうまく動作しませんでした。
これを拡張してキーボード用の各種機能を実装したライブラリとして、下記が公開されています。今回はこれを使用しました。
github.com

ライブラリをダウンロードして~/Documents/Arduino/libraries/ に導入すると、Arduino IDE内にサンプルスケッチとして ps2dev > ps2_keyboard が現れます。
これをArduino Pro Miniに書き込み、PCに接続した状態で再起動すると、 1秒ごとに「1」が入力されるようになりました。
キー入力自体は

  keyboard.keyboard_mkbrk(PS2dev::ONE);  // 数字の1

と書けば、指定キーを押下後、直ちに離したことになります。簡単ですね。
対応しているキーの一覧はヘッダファイルにあります。
あとは、プログラム次第でシリアルやボタン、タイマー等でタイミングを決めて好きなキーを入力できるようにすればOKです。


(実は今回の目論見としては、今のPCではUltra Fast Bootモードにしていると電源投入時にUSBキーボード等のデバイスが初期化されずにOS起動に入ってしまうのでキー押下でUEFIメニューに入ることができないところ、PS/2接続なら入れるのではと思ったのがきっかけでした。が、PS/2接続でもどんなに素早くキー押下してもOS起動前には反応してくれず、結局諦めることとなりました。残念。)

IR202赤外線カメラのデータを取得

IR202 という携帯電話に接続する赤外線カメラを買ってみました。
解像度80x60とちゃんと画像として見られるレベルながら比較的安価です(最近円安のせいか値上がりしていますが…)。
PerfectPrime IR202 赤外線サーマルイメージャーカメラ (Android TypeC)

通常は対応する携帯のアプリを入れて使用します。
IR202 Thermal Camera Viewer - Google Play のアプリ
https://play-lh.googleusercontent.com/ovxdbSB7FARB0o621YUwia3-WEvh9elqrjbAuOWlIsV7XNUXl-96Ml2gA4FUCF5oXWe5=w5120-h2880

実は普段私はiPhoneを使っていますが、敢えてAndroid版を購入。USB Type-C接続なので、PCに繋いでデータが取れるんじゃないかと思ったからです。

さっそくPC (Linuxで試しましたが、MacWindowsでも同様に認識されます) に繋いで見ると、Virtual COMというデバイスとして見え、USBモデム(シリアルポート)として認識されました。

$ lsusb | grep COM
Bus 003 Device 021: ID 0416:b002 Winbond Electronics Corp. USB Virtual COM
$ dmesg | tail
[12151.951595] usb 3-1.4: new full-speed USB device number 21 using xhci_hcd
[12152.069983] usb 3-1.4: New USB device found, idVendor=0416, idProduct=b002, bcdDevice= 3.00
[12152.069989] usb 3-1.4: New USB device strings: Mfr=1, Product=2, SerialNumber=0
[12152.069991] usb 3-1.4: Product: USB Virtual COM
[12152.069993] usb 3-1.4: Manufacturer: Nuvoton
[12152.109110] cdc_acm 3-1.4:1.0: ttyACM0: USB ACM device

データシートなどは見つけられなかったので、アプリの動作を調べてみました。起動時に初期化/データ送信開始コマンド、終了時にデータ送信停止コマンドを送っているようでした。
シリアルポートに 115200bps で接続し、下記コマンドを送ってやると、想定どおりデータを取得できました。
#[size (hex 4B)][command (4B)][data][CRC (hex 4B)] という構造をしているようです。デバイスからのデータも同様の形式でした。

内容 コマンド
初期化 #000CWREGB40402F3
データ送信開始 #000CWREGB10302DE
データ送信停止 #000CWREGB10002DB

バイスからのデータを眺めると、GFRA というcommandで、ヘッダらしきもの(詳細不明)の後に80×62のピクセル値(little endian 16bit)が続くという形式でした。仕様よりも実際は2行多いようです。
これを温度に変換したいのですが、変換規則を調べるためにカップウォーマーとか保冷剤とかいろんな温度の物を撮ってアプリの結果と比べるという方法で対応を調べました。1次関数で大丈夫そうです。ただ、他の放射温度系と較べてみると、アプリの出力は高温は高く、低温は低く表示されがちでした。対象物との距離にも多少影響されます。あくまで周囲との高低を見る程度にして温度はあまり厳密でないと思ったほうが良さそうですね。

IR202 Graph

あとはこれを画像化してやればOK。Rubyでデータを取得してWebSocketで送りJavaScriptCanvasに描画してみました。
生データだと結構なノイズが乗るので、適当にフィルタを噛ましています(何か色々やってますがなるべく応答速度を犠牲にしすぎずにノイズを減らそうとした結果です)。
コードはこちら。

クリックした位置の温度、最高/最低温度およびその場所が表示されます。
下のスクリーンショットで青くなっているのはエアコンの吹き出し口です。

PCでデータ処理できるようになったので、温度監視とか動きの認識とかに応用できそうですね。

Arduino MKR WAN 1310に搭載されたECC508を利用して乱数を生成してみる

Arduino MKR WAN 1310にはECC508というセキュアエレメントが搭載されており、これを用いて公開鍵認証などを安全に実現することができます。
ハードウェア乱数生成器を搭載しているため、暗号論的にセキュアな乱数を生成することもできます。

以前の記事で紹介したLoRa GPSノードでは公開鍵認証は利用しませんでしたが、共通鍵暗号のIVの初期化にこのチップを利用しているため、利用方法を書いておきます。

まず、ArduinoECCX08 ライブラリをライブラリマネージャで導入します。
f:id:NeoCat:20220412035243p:plain:w600

するとスケッチ例 > ArduinoECCX08 以下に幾つかのサンプルが追加されます。

このうちECCX08RandomNumberがまさに乱数生成をするサンプルなのですが、これを利用する前に lock という作業が必要です。これは設定をチップに書き込んで二度と改変できないようにする操作です。以下ではデフォルト設定 ECCX08_DEFAULT_TLS_CONFIG を利用するので、特定の用途で使う予定がある場合はそれを使う必要があるかもしれません。

スケッチ例の Tools > ECCX08CSR を書き込み、シリアルコンソールをつなぎます。

すると購入したばかりのMKRであれば、

The ECCX08 on your board is not locked, would you like to PERMANENTLY configure and lock it now? (y/N)

と聞かれますので、 y を入力すると、チップに初期設定が書き込まれます。

それ以降の操作はここでは不要です。

改めて ECCX08RandomNumber を書き込んでやると、乱数値がシリアル出力されるはずです。

最小では

// setup
ECCX08.begin();
if (!ECCX08.locked()) { error(...) }

long random = ECCX08.random(65535);

などと指定すれば乱数値が得られます。
なおlockせずに利用しようとするとRNGがテスト用に常に特定パターンを返すため、lockされていない場合はエラーにしてこの値を使用してはいけません。

他に以下のような乱数を取得するための関数が用意されています。

  long random(long max);
  long random(long min, long max);
  int random(byte data[], size_t length);