モチベーション
Theta VのイメージをRouter経由でPCで接続し、他の機器へ送りたい。
そこで、以下を参考にClient Modeの設定を変更しようとした。
WiFiのsecurityとpasswordを空にしたかったので、
空のstringでHTTP Post送ったらThetaがどこのWiFiにもアクセスできなくてハマった。
https://www.blacklab-blog.net/entry/theta_v/2
サポートデスクに連絡したら以下の通りリセットを推奨されましたが、
上手くいきませんでした。
■TEHTA V本体のリセット
THETA V本体が「電源オフ」の状態で、電源ボタンと
無線ボタンを同時に長押し(6秒以上)します。
※通常、ボタンを同時に長押している間に状態ランプ
(シャッターボタンの上のランプ)が点滅し、点滅が
終わってランプが消灯するとリセット完了となりま
す。
■THETA V無線機能のリセット
THETA V本体が「電源オン」の状態で、無線ボタンを
長押し(6秒以上)します。カメラの電源が自動的にな
ってリセット完了となります。
USBを使ってMTP接続
APIの発見
THETAにはWiFiのほかにもUSBのAPIが用意されています。
https://developers.theta360.com/ja/docs/v2/usb_reference/
USB経由でコマンド送ればいいんだなーーーと思ったんですけど、
どうやるんだと???
ライブラリを作っている人発見
しっかり作ってくれている人がいました。
https://qiita.com/soundkonchan/items/d392794d57dc849720f3
https://github.com/kon0524/WpdMtp
早速試していきます
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.GetAccessPointHandles, null, null);
uint[] accessPointHandles = Utils.GetUIntArray(res.Data);
この時MtpOperationCode
にGetAccessPointHandles
はないので追加していきます。
他の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
ファイルを作成し
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を削除します。
```c#
res = command.Execute(MtpOperationCode.DeleteAccessPoint, new uint[1] { handle }, null);
```
### 実行
Ctrl + F5で実行して消します。
# 最終的なProgram.csのMain文コード
```c#:Program.cs
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();
}
```