Theta V の Client Mode設定を間違えてしまってハマった話
モチベーション
Theta VのイメージをRouter経由でPCで接続し、他の機器へ送りたい。
そこで、以下を参考にClient Modeの設定を変更しようとした。
WiFiのsecurityとpasswordを空にしたかったので、
空のstringでHTTP Post送ったらThetaがどこのWiFiにもアクセスできなくてハマった。
サポートデスクに連絡したら以下の通りリセットを推奨されましたが、 上手くいきませんでした。
■TEHTA V本体のリセット THETA V本体が「電源オフ」の状態で、電源ボタンと 無線ボタンを同時に長押し(6秒以上)します。 ※通常、ボタンを同時に長押している間に状態ランプ (シャッターボタンの上のランプ)が点滅し、点滅が 終わってランプが消灯するとリセット完了となりま す。 ■THETA V無線機能のリセット THETA V本体が「電源オン」の状態で、無線ボタンを 長押し(6秒以上)します。カメラの電源が自動的にな ってリセット完了となります。
USBを使ってMTP接続
APIの発見
THETAにはWiFiのほかにもUSBのAPIが用意されています。
USB Reference · v2 · API & SDK | RICOH THETA Developers
USB経由でコマンド送ればいいんだなーーーと思ったんですけど、
どうやるんだと???
ライブラリを作っている人発見
しっかり作ってくれている人がいました。
早速試していきます
1. まずはダウンロード
git clone git@github.com:kon0524/WpdMtp.git
2. Visual Studio 2017で開く
WpdMtpLib.slnを開きます Testプロジェクトを「スタートアッププロジェクトに設定」するのを忘れないでください
Testプロジェクトの中のProgram.cs
を変えていきます。
2.1 デバイスの名前を自分のThetaに変更する
MtpResponse res; MtpCommand command = new MtpCommand(); // 接続されているデバイスIDを取得する string[] deviceIds = command.GetDeviceIds(); if (deviceIds.Length == 0) { return; } // RICOH THETA V デバイスを取得する string targetDeviceId = String.Empty; foreach (string deviceId in deviceIds) { Console.WriteLine(command.GetDeviceFriendlyName(deviceId)); if ("RICOH THETA V".Equals(command.GetDeviceFriendlyName(deviceId))) { targetDeviceId = deviceId; break; }
2.2 デバイス情報(deviceInfo)の上手くいくか試してみる。
メイン文にいろいろ書いてありますが、とりあえずDeviceInfoだけ取得してみる。
DeviceInfo以外のcommand.Executeはすべて消す。
// DeviceInfo res = command.Execute(MtpOperationCode.GetDeviceInfo, null, null); DeviceInfo deviceInfo = new DeviceInfo(res.Data); Console.WriteLine(deviceInfo.Manufacturer);
コンソールが最後閉じてしまうので、キーが押されるまで閉じないようにする
Console.WriteLine(); Console.WriteLine("Press any key to continue."); Console.ReadKey(); // デバイスよさようなら command.Close();
実行するとTheta Vの情報が取れます。
2.3 DeviceInfoに見習ってAcccessPointを取得するExecute Commandを書いていきます。
res = command.Execute(MtpOperationCode.MtpOperationCode, null, null); uint[] accessPointHandles = Utils.GetUIntArray(res.Data);
この時MtpOperationCode
にMtpOperationCode
はないので追加していきます。
他のAPIのついでに追加してしまいましょう。
namespace WpdMtpLib { /// <summary> /// MTPオペレーションコード(Thetaでサポートしている値のみ) /// </summary> public enum MtpOperationCode : ushort { GetDeviceInfo = 0x1001, OpenSession, CloseSession, GetStorageIDs, GetStorageInfo, GetNumObjects, GetObjectHandles, GetObjectInfo, GetObject, GetThumb, DeleteObject, InitiateCapture = 0x100E, GetDevicePropDesc = 0x1014, GetDevicePropValue, SetDevicePropValue, TerminateOpenCapture = 0x1018, GetPartialObject = 0x101B, InitiateOpenCapture, StopSelfTimer = 0x99A2, GetAccessPointHandles = 0x99A3, GetAccessPointInfo = 0x99A4, SetAccessPoint = 0x99A5, DeleteAccessPoint = 0x99A6, SetAccessPointPassword = 0x99AD } }
OperationCodeは分かったのでGetAccessPointInfo
が何をする子なのか教えてあげます。
MtpOperation.cs
ファイルにデータを書き込むのか読み込むのかを記述します。
private static Dictionary<MtpOperationCode, DataPhase> OperationCode2DataPhase = new Dictionary<MtpOperationCode, DataPhase>() { // データフェーズのないオペレーション {MtpOperationCode.OpenSession, DataPhase.NoDataPhase}, {MtpOperationCode.CloseSession, DataPhase.NoDataPhase}, {MtpOperationCode.GetNumObjects, DataPhase.NoDataPhase}, {MtpOperationCode.DeleteObject, DataPhase.NoDataPhase}, {MtpOperationCode.InitiateCapture, DataPhase.NoDataPhase}, {MtpOperationCode.TerminateOpenCapture, DataPhase.NoDataPhase}, {MtpOperationCode.InitiateOpenCapture, DataPhase.NoDataPhase}, {MtpOperationCode.StopSelfTimer, DataPhase.NoDataPhase}, {MtpOperationCode.DeleteAccessPoint, DataPhase.NoDataPhase}, // R->Iのデータフェーズがあるオペレーション {MtpOperationCode.GetDeviceInfo, DataPhase.DataReadPhase}, {MtpOperationCode.GetStorageIDs, DataPhase.DataReadPhase}, {MtpOperationCode.GetStorageInfo, DataPhase.DataReadPhase}, {MtpOperationCode.GetObjectHandles, DataPhase.DataReadPhase}, {MtpOperationCode.GetObjectInfo, DataPhase.DataReadPhase}, {MtpOperationCode.GetObject, DataPhase.DataReadPhase}, {MtpOperationCode.GetThumb, DataPhase.DataReadPhase}, {MtpOperationCode.GetDevicePropDesc, DataPhase.DataReadPhase}, {MtpOperationCode.GetDevicePropValue, DataPhase.DataReadPhase}, {MtpOperationCode.GetPartialObject, DataPhase.DataReadPhase}, {MtpOperationCode.GetAccessPointHandles, DataPhase.DataReadPhase}, {MtpOperationCode.GetAccessPointInfo, DataPhase.DataReadPhase}, // I->Rのデータフェーズがあるオペレーション {MtpOperationCode.SetDevicePropValue, DataPhase.DataWritePhase}, {MtpOperationCode.SetAccessPoint, DataPhase.DataWritePhase}, {MtpOperationCode.SetAccessPointPassword, DataPhase.DataWritePhase} };
2.4 ハンドラーからAccessPointInfoを取得する
最終的にこうなります。
res = command.Execute(MtpOperationCode.GetAccessPointHandles, null, null); uint[] accessPointHandles = Utils.GetUIntArray(res.Data); foreach(uint handle in accessPointHandles) { res = command.Execute(MtpOperationCode.GetAccessPointInfo, new uint[1] { handle }, null); foreach(byte b in res.Data) { Console.Write(b); Console.Write(" "); } AccessPointInfo accessPointInfo = new AccessPointInfo(res.Data); Console.WriteLine(""); Console.WriteLine(accessPointInfo.SSID); ```` 情報を抽出するためには`AccessPointInfo`型を定義しないといけないので、定義します。 新しく`AccessPointInfo.cs`ファイルを作成し ````c#:AccessPointInfo.cs using System; namespace WpdMtpLib { public class AccessPointInfo { public string SSID { get; private set; } public ushort SSIDStealth { get; private set; } public string Security { get; private set; } public ushort ConnectionPriority { get; private set; } public ushort IpAddressAllocation { get; private set; } public uint IPAddress { get; private set; } public uint SubnetMask { get; private set; } public uint DefaultGateway { get; private set; } public AccessPointInfo(byte[] data) { int pos = 0; SSID = Utils.GetString(data, ref pos); SSIDStealth = BitConverter.ToUInt16(data, pos); pos += 2; Security = Utils.GetString(data, ref pos); ConnectionPriority = BitConverter.ToUInt16(data, pos); pos += 2; IpAddressAllocation = BitConverter.ToUInt16(data, pos); pos += 2; IPAddress = BitConverter.ToUInt32(data, pos); pos += 4; SubnetMask = BitConverter.ToUInt32(data, pos); pos += 4; DefaultGateway = BitConverter.ToUInt32(data, pos); } } }
2.5 最後に消したいAccessPointを削除します。
res = command.Execute(MtpOperationCode.DeleteAccessPoint, new uint[1] { handle }, null);
実行
Ctrl + F5で実行して消します。
最終的なProgram.csのMain文コード
static void Main(string[] args) { MtpResponse res; MtpCommand command = new MtpCommand(); // 接続されているデバイスIDを取得する string[] deviceIds = command.GetDeviceIds(); if (deviceIds.Length == 0) { return; } // RICOH THETA V デバイスを取得する string targetDeviceId = String.Empty; foreach (string deviceId in deviceIds) { Console.WriteLine(command.GetDeviceFriendlyName(deviceId)); if ("RICOH THETA V".Equals(command.GetDeviceFriendlyName(deviceId))) { targetDeviceId = deviceId; break; } } if (targetDeviceId.Length == 0) { return; } command.Open(targetDeviceId); // イベントを受け取れるようにする command.MtpEvent += MtpEventListener; // DeviceInfo res = command.Execute(MtpOperationCode.GetDeviceInfo, null, null); DeviceInfo deviceInfo = new DeviceInfo(res.Data); Console.WriteLine(deviceInfo.Manufacturer); res = command.Execute(MtpOperationCode.GetAccessPointHandles, null, null); uint[] accessPointHandles = Utils.GetUIntArray(res.Data); foreach(uint handle in accessPointHandles) { res = command.Execute(MtpOperationCode.GetAccessPointInfo, new uint[1] { handle }, null); foreach(byte b in res.Data) { Console.Write(b); Console.Write(" "); } AccessPointInfo accessPointInfo = new AccessPointInfo(res.Data); Console.WriteLine(""); Console.WriteLine(accessPointInfo.SSID); res = command.Execute(MtpOperationCode.DeleteAccessPoint, new uint[1] { handle }, null); } Console.WriteLine(); Console.WriteLine("Press any key to continue."); Console.ReadKey(); // デバイスよさようなら command.Close(); }