USBでニコンのカメラをコントロールするリモートコントローラの続きです。ソフトウェアはオープンソースのPTPライブラリを使って実装しましたが、これは単にリモコンからカメラをコントロールするだけで、逆にカメラ側の設定をリモコンに反映させたり、撮影が終わったことをカメラから教えてもらったりすることができませんでした。今回はそこを実装します。

ニコンのカメラはカメラに記録されているイベントを取得するのに

GetEvent 0x9C07

というベンダコードが用意されています。これを呼び出すと、その前に起こった、例えばシャッタスピードが変化した、とか、撮影が完了した、とか、ファイルが追加された、というイベントが一覧として取得できます。このコマンドを定期的にポーリングしてやると、カメラ内部で起こっている変化を常に取得することができます。

が、このコマンドを呼び出してみると、
17:19:11.730 -> Guru Meditation Error: Core  1 panic'ed (Double exception)
17:19:11.730 -> Core 1 register dump:
17:19:11.730 -> PC      : 0x400803c0Guru Meditation Error: Core  1 panic'ed (LoadStoreError). Exception was unhandled.
17:19:11.730 -> Core 1 register dump:
17:19:11.730 -> PC      : 0x4008bfc5Guru Meditation Error: Core  1 panic'ed (LoadStoreError). Exception was unhandled.
と落ちてしまいます。
直すところは
・nkeventparser.cpp
の43行目
	case 3:
		theBuffer.valueSize = sizeof(NKEvent);
		valueParser.Initialize(&theBuffer);
		nStage = 4;

	case 3:
		theBuffer.valueSize = 6;
		valueParser.Initialize(&theBuffer);
		nStage = 4;
に変更します。オリジナルのソースはこちらから見られます。

ニコンから取り寄せられるPTPコマンド仕様書によると、GetEventコマンドで取得できるデータは
OffsetValueName行Discription
0NEventCountイベント数
2EventCodeEventCode[0]最も古いイベント
4 EventParameter[0]最も古いイベントに付随するパラメータ
8EventCodeEventCode[1]二番目に古いイベント
10 EventParameter[1]二番目に古いイベントに付随するパラメータ

というふうに、最初の2バイトでイベント数、その後2バイトでイベントコード、4バイトでイベントのパラメータが繰り返す、という作りになっています。上記で変更したNKEventは、そのイベントデータを受け取るための構造体で、nkeventparser.hで次のように定義されています。
	
struct NKEvent
{
	uint16_t	eventCode;

	union
	{
		struct
		{
			uint16_t	wParam1;
			uint16_t	wParam2;
		};
		uint32_t		dwParam;
	};
};

ちょうど2バイトのイベントコードと4バイトのパラメータの組み合わせとなっていて、パラメータは2バイトx2あるいは4バイトのどちらかを共用体で選べるようになっています。そして

theBuffer.valueSize = sizeof(NKEvent)

で、GetEventで受信したイベントデータをパースするために、イベントデータの繰り返しサイズを指定しています。

ですが、このPTPライブラリはオリジナルのArduinoで使われている8bitのAVRマイコン向けに作られたもの。8bit環境だとsizeof()で構造体の実データサイズ6を返してくれますが、ESP32は32bitマイコンなので、パディングが入って4の倍数の8を返してしまいます。そのためPTPで取得したデータサイズ以上のアドレスにアクセスして、落ちていた、ということでした。
実際には数字直打ちでなくて、defineするとか、マナーは守りましょう。

ついでに前回とりあえずビルドを通すためにいい加減に直した
		{
			uint32_t tmp = (uint32_t)varBuffer;
			numEvents = (uint16_t)tmp;
		}

		numEvents = varBuffer[0];
に修正 。ここがGetEventコマンドで取得できる最初のデータ コマンド数 になります。

無事イベントが取れるようになりました。
	
17:27:42.846 -> OnEvent Object Added handle = 0x1F322919
17:27:42.846 -> OnEvent CaptureComplete Transaction ID = 0x1F320000
17:27:42.846 -> OnEvent DevicePropChanged Changed prop code = 0xD0A4
17:27:42.879 -> OnEvent DevicePropChanged Changed prop code = 0xD1F1

これはシャッタを切ったときに発生するイベントで、古い順に

撮影した画像が追加された。
撮影完了 
動画記録禁止条件変更
カードに記録可能な枚数が変更

になります。RAWとJPEGを両方記録するような設定になっているときはObject Addedイベントが2回飛んできます。