技術 約15分で読めます

Nikon COOLPIX A1000をPCから操作できるか調べた

いけさん目次

COOLPIX A1000を持っているが、PCから画像を取り込んだりリモート撮影したりできるのか気になった。 Nikonの公式ソフトウェアはDSLR/Zシリーズ向けばかりで、コンパクトカメラは基本スマホのSnapBridgeに誘導される。

じゃあ自分で繋げばいいじゃん、ということでプロトコルレベルから調べてみた。

COOLPIX A1000の接続スペック

まず公式リファレンスマニュアル(p.226-228)から引っ張ってきた仕様。

インターフェース仕様
USBMicro-USB(UC-E21)、Hi-Speed USB 2.0
USBプロトコルMTP / PTP / PictBridge
WiFiIEEE 802.11b/g(2.4GHz、ch1-11)
WiFi認証Open / WPA2-PSK
WiFiモードAPモード(カメラがAP) / STAモード(既存ネットワーク参加)
BluetoothBluetooth 4.1 + BLE

注意点として、802.11nは非対応。サードパーティのスペックサイトでは対応と書かれていることがあるが、公式マニュアルにはb/gのみと明記されている。Bluetoothも4.1で、同時期の超望遠モデル(Pシリーズ)と比べると据え置き。A1000以降、Aシリーズの後継は出ていない。

Nikon公式ソフトウェアの対応状況

ソフトウェアA1000対応備考
NX Studioo画像閲覧・編集・管理。ViewNX-iの後継
ViewNX-ioレガシー。Windows 11非対応
Camera Control Pro 2xDSLR/Zシリーズ専用
Nikon Webcam UtilityxDSLR/Zシリーズ専用
SnapBridge(モバイル)oiOS/Android。PC版は存在しない

Camera Control Pro 2もWebcam UtilityもCOOLPIXは対象外。公式にPCからリモート操作する手段はない。 NX Studioは画像管理ソフトであって、カメラ制御はできない。

USB接続(MTP/PTP)

A1000をUSBで繋ぐと、OSからはMTP/PTPデバイスとして認識される。 macOSの ioreg -p IOUSB で確認した実機の情報はこうなった。

項目
デバイス名NIKON DSC COOLPIX A1000
Vendor ID0x04B0(Nikon)
Product ID0x036B
USBHi-Speed USB 2.0(480Mbps)
シリアル000021000717
消費電力500mA

なお、SwiftMTPのデバイスデータベースにはA1000のProduct IDが 0x4038 と登録されているが、実機は 0x036B だった。ファームウェアバージョンや地域で異なる可能性もあるが、少なくとも手元の個体はこの値。

gphoto2での認識

オープンソースのカメラ制御ライブラリlibgphoto2のソースコード(camlibs/ptp2/library.c)を調べた。

A1000は未登録。登録されているCOOLPIXモデルと比較するとこうなった。

モデルProduct IDキャプチャ対応フラグ
COOLPIX A9000x019e制限ありPTP_NIKON_BROKEN_CAP
COOLPIX B7000x0231あり(プレビュー付き)-
COOLPIX P10000x0232あり(プレビュー付き)-
COOLPIX P11000x0234あり(プレビュー付き)-
COOLPIX B5000x0362制限ありPTP_NIKON_BROKEN_CAP
COOLPIX A10000x036B(実測)未登録-

A900にはPTP_NIKON_BROKEN_CAPフラグがついていて、リモートキャプチャに問題があることを示している。 A1000はA900の後継なので、仮にlibgphoto2に追加しても同様の制限がある可能性が高い。

ただし、libgphoto2にはPTPクラスの汎用検出フォールバックがあるので、gphoto2 --auto-detectで繋いでみれば基本的なファイル転送は動くかもしれない。

macOSの罠

macOSはPTPデバイスを自動的にグラブする(PTPCameraプロセス)。gphoto2を使う場合は先にこれを殺す必要がある:

killall PTPCamera
gphoto2 --auto-detect

WiFi接続(PTP/IP)

ここからが本題。A1000のWiFi通信はPTP/IP(Picture Transfer Protocol over IP)を使っている。

PTP/IPの基本

PTP/IP(CIPA DC-X005-2005)はもともとNikonとFotoNationが共同開発した規格で、PTPコマンドをTCP/IPで運ぶ。

項目
ポートTCP 15740
接続数2本(Command/Data + Event)
カメラIPAPモードでは通常 192.168.1.1

パケット構造

PTP/IPのパケットはすべて同じヘッダ構造を持つ:

[4バイト: パケット長][4バイト: タイプコード][ペイロード]

リトルエンディアン。主要なタイプコードをまとめた。

コード方向名前内容
0x01Client→CameraInit_Command_Request16バイトGUID + UTF-16クライアント名
0x02Camera→ClientInit_Command_AckセッションID + 16バイトGUID + UTF-16カメラ名
0x03Client→CameraInit_Event_RequestセッションID(type 0x02から取得)
0x04Camera→ClientInit_Event_Ack-
0x06Client→CameraCmd_RequestPTPコマンドコード + Transaction ID + 引数
0x07Camera→ClientCmd_ResponsePTPレスポンスコード + Transaction ID + 引数
0x09EitherStart_Data_Packetデータ転送開始
0x0AEitherData_Packetデータ本体
0x0CEitherEnd_Data_Packetデータ転送終了

接続シーケンス

sequenceDiagram
    participant PC as PC
    participant Cam as COOLPIX A1000<br/>(192.168.1.1:15740)

    Note over PC,Cam: Command/Dataチャネル(TCP接続1本目)
    PC->>Cam: Init_Command_Request<br/>(GUID + クライアント名)
    Cam->>PC: Init_Command_Ack<br/>(セッションID + GUID + カメラ名)

    Note over PC,Cam: Eventチャネル(TCP接続2本目)
    PC->>Cam: Init_Event_Request<br/>(セッションID)
    Cam->>PC: Init_Event_Ack

    Note over PC,Cam: PTPセッション開始
    PC->>Cam: OpenSession (0x1002)
    Cam->>PC: OK (0x2001)
    PC->>Cam: GetDeviceInfo (0x1001)
    Cam->>PC: DeviceInfo(対応コマンド一覧等)

Nikon固有のPTPオペコード

PTPの標準コマンド(0x10010x101B)に加えて、Nikonは0x9000番台の独自コマンドを実装している。 libgphoto2のptp.hから主要なものを抜き出した。

オペコード名前機能
0x90C0InitiateCaptureRecInSdramSDRAMにキャプチャ
0x90C1AFDriveAF駆動
0x90C2ChangeCameraModeカメラモード切替
0x90C7CheckEventsイベント取得
0x90C8DeviceReadyデバイス準備状態確認
0x9201StartLiveViewライブビュー開始
0x9202EndLiveViewライブビュー終了
0x9203GetLiveViewImgライブビュー画像取得
0x9207InitiateCaptureRecInMediaメディアにキャプチャ
0x920AStartMovieRecInCard動画記録開始
0x920BEndMovieRec動画記録終了

A1000がこれらのコマンドにどこまで応答するかは実機テストで確認する。COOLPIX系は一部コマンドが未実装だったり、エラーを返すケースがある。

BLE通信の実機テスト

BLEスキャン

PythonのbleakライブラリでBLEスキャンをかけた。カメラのメニューから「スマホと接続」画面を開いた状態でアドバタイズが飛ぶ。

A1000_21000717  addr=36C20FB9-...  mfr={}
  Service UUIDs: ['0000de00-3dd4-4255-8d62-6dc7b9bd5561']

SnapBridgeのサービスUUID 0000de00-3dd4-4255-8d62-6dc7b9bd5561 を確認。ただしManufacturer Dataは空で、Nikon Company ID(0x0399)はアドバタイズに含まれていなかった。Company IDやデバイス名でフィルタするとヒットしない場合がある。

WiFi SSID(A1000_21000717)がそのままBLEのデバイス名になっている。

アドバタイズの挙動

スキャン方法によって検出結果がかなりブレた。

  • BleakScanner.discover()(スナップショット型)だと検出できたりできなかったり不安定
  • BleakScanner(detection_callback=...)(コールバック型)だと30秒間で50回以上検出

find_device_by_name() で名前指定検索すると20秒待ってもタイムアウトすることがあった。コールバックモードで拾ってから接続する方が確実。

GATT接続(認証前)

コールバックで見つけた瞬間に BleakClient で接続。MTU 185で繋がった。

Device Information Service (0x180A):

Characteristic
Manufacturer Name (0x2A29)Nikon
Model Number (0x2A24)A1000
Firmware Revision (0x2A26)1.2
Hardware Revision (0x2A28)1.2

認証前はSnapBridge Service内の全Characteristicがゼロ返しだった。

Blowfish認証の実装と実行

OSSプロジェクトやネットワーク解析ツールで集めた情報をもとに、SnapBridgeのBLE認証をPythonで実装して実機に送った。

認証のプロトコルはBlowfish暗号ベースのチャレンジ・レスポンス方式で、5ステージで構成される。

sequenceDiagram
    participant PC
    participant A1000

    PC->>A1000: Stage 1(ノンス + デバイスID)
    A1000->>PC: Stage 2(カメラのノンス + Blowfishハッシュ)
    Note over PC: ハッシュを検証してソルトインデックスを特定
    PC->>A1000: Stage 3(自分のノンスで計算したハッシュ)
    A1000->>PC: Stage 4(シリアル番号)
    PC->>A1000: Stage 5(完了通知)

Blowfishの鍵とソルトテーブルはfurbleプロジェクトが公開している。

実行結果:

S1-2 OK salt=4
S3-4 OK serial='21000717'
S5 sent
No NOT1
POWER=03 VALID_WAKE

認証は通った。ソルトインデックスの特定、カメラのハッシュ検証、シリアル番号の取得まで成功。ただしステージ5の後に期待される成功通知(NOT1 = 0x2008[0x01, 0x00])が来なかった。これはBluetooth Classicのセキュアボンディングを待っている状態。

認証後のGATT Characteristicマップ

認証が通ると、それまでゼロだったCharacteristicにデータが入る。NikonのBLE仕様書は非公開だが、通信の観察とOSSの情報を突き合わせて各Characteristicの役割が判明した。

UUID名称認証後の値備考
0x2000AUTHENTICATIONシリアル番号入り認証ステージの書き込み先
0x2001POWER_CONTROL033 = VALID_WAKE(カメラ起動中)
0x2002CLIENT_DEVICE_NAME(write-only)接続元デバイス名を書き込む
0x2003SERVER_DEVICE_NAMEA1000_21000717カメラのSSID名
0x2004CONNECTION_CONFIGURATION0000WiFi設定(暗号化)。未接続時は空
0x2005CONNECTION_ESTABLISHMENT0000WiFi/BTC接続トリガー
0x2006CURRENT_TIMEea070411123a032026-04-17 18:58:03(JST)
0x2007LOCATION_INFORMATION全ゼロ(41バイト)GPS座標の書き込み先
0x2008LSS_CONTROL_POINT0000制御フラグ(notify)
0x2009LSS_FEATUREfd030000対応機能ビットフラグ
0x200ALSS_CABLE_ATTACHMENT(notify-only)ケーブル接続通知
0x200BLSS_SERIAL_NUMBER21000717カメラのシリアル
0x2A19BATTERY_LEVEL64バッテリー残量100%

0x2009のLSS_FEATUREは0x03FDで、WiFi・BLE・時刻同期・位置情報など対応機能をビットフラグで示している。

WiFi AP起動の試み

認証後、WiFi APの起動を試みた。0x2005(CONNECTION_ESTABLISHMENT)にWiFi要求ビット0x01を書き込む。

>>> 0x2005 <- 0x01 (WiFi) <<<
Waiting 5s for AP...
CONN_CFG=0000 (2B)
CONN_EST=0000

書き込みはエラーなしで受け付けられたが、0x2004(WiFi設定)は空のまま、0x2005も即座に0000にリセットされた。WiFi APは起動しなかった。

0x03(WiFi + Bluetooth Classic両方のビット)も試したが結果は同じ。

なぜWiFi APが起動しないか

Bluetooth Classicのセキュアボンディングが完了していないため。SnapBridgeの接続フローを解析すると、以下の順序になっている。

graph TD
    A[BLE接続] --> B[Blowfish認証<br/>ステージ1-5]
    B --> C{NOT1成功通知?}
    C -->|来ない| D[Bluetooth Classic<br/>セキュアボンディング待ち]
    D --> E[ボンディング完了]
    E --> F[NOT1 = 0x01,0x00]
    F --> G[0x2005に0x01書き込み]
    G --> H[WiFi AP起動]
    H --> I[0x2004にSSID/パスワード<br/>Blowfishで暗号化]
    
    C -->|0x01,0x00| G

    style D fill:#f66,color:#fff
    style E fill:#f66,color:#fff

Pythonのbleakライブラリ(BLE専用)ではBluetooth Classicのボンディングができない。furbleプロジェクトも同じ理由で「smart device pairing not fully functional」と断念している。

リモコンモードでの接続

カメラの「リモコンと接続」メニューからリモートモードに入ると、スマートデバイスモードでは見えなかったCharacteristic(0x20800x2087)が出現した。

UUIDProperties役割
0x2080readリモート状態1
0x2082read, writeリモート設定
0x2083writeシャッター制御
0x2084indicate, readカメラフィードバック
0x2086readリモート状態2
0x2087indicate, read, writeリモートペアリング

リモートモードのペアリングはBlowfish不要のシンプルなハンドシェイクで、ステージ1-5の全レスポンスがゼロ固定。ペアリング自体は成功し、0x2082にモード設定データ(0202030320200000...)が現れた。

しかしシャッターコマンド(0x2083[0x02, 0x02]書き込み)はエラーなしで受け付けられるものの、カメラは反応しなかった。A1000のリモートモードはfurbleが検証したB600とプロトコルが微妙に異なる可能性がある。

SnapBridgeの通信アーキテクチャ

SnapBridgeの通信をネットワーク解析ツールで観察すると、3層のプロトコルスタックが見えてくる。

graph TD
    A[SnapBridge App] --> B[BLE<br/>常時接続・ペアリング・制御]
    A --> C[Bluetooth Classic<br/>一部モデルでデータ転送]
    A --> D[WiFi<br/>フルサイズ画像転送]

    B --> E[PTP/MTP<br/>コマンドレイヤー<br/>ISO 15740 + Nikon拡張]
    C --> E
    D --> E

    B --> F[GATT Service<br/>UUID: 0000de00-...<br/>Blowfish認証]
    D --> G[PTP/IP<br/>TCP:15740<br/>GUIDハンドシェイク]

通常の利用フロー:

  1. BLE常時接続でバッテリー消費を抑えつつカメラとの接続を維持
  2. 撮影するとBLE経由で2MP縮小画像を自動転送
  3. フルサイズ画像が必要になるとBLEからWiFiにハンドオフ
  4. PTP/IPでフルサイズJPEGを転送
  5. 転送完了後、WiFiを切ってBLE常時接続に戻る

BLEからWiFiへのハンドオフについては、Foolographyの開発者が「BLE ClassicとBLEとWiFiの奇妙な組み合わせ」と表現している。今回の実機テストで、このフローの全体像がかなり見えてきた。

WiFi APが起動しない

カメラの「スマホと接続」画面ではWiFi SSID(A1000_21000717)とパスワード(NikonCoolpix — 機種共通の初期値)が表示される。しかしMac/Windows両方のWiFiスキャンにSSIDが出てこなかった。BLEアドバタイズは飛んでいるのにWiFi APは動いていない。

この画面でSSIDとパスワードが表示されるのは、ペアリング完了後にWiFi APが起動した際の接続情報を事前に見せているだけ。BLE認証後に0x2005(CONNECTION_ESTABLISHMENT)にWiFi要求を書き込んでも、Bluetooth Classicのセキュアボンディングが完了していないとカメラはリクエストを無視する。

使えそうなOSSツール

調査で見つかったツールのうち、A1000で試す価値がありそうなもの。

USB経由

ツール言語概要
gphoto2 / libgphoto2C定番のカメラ制御ライブラリ。A1000未登録だが汎用PTP検出あり
python-gphoto2Pythonlibgphoto2のPythonバインディング
SwiftMTPSwiftmacOS向けMTP実装。A1000のデバイスプロファイルが登録済み
miniPtpPython教育用の最小PTP実装。PyUSBベース

WiFi経由(PTP/IP)

ツール言語概要
ptpip-d5300PythonNikon D5300向けPTP/IPクライアント。A1000で最も有望
libpictC汎用PTP実装。USB/IPデュアル対応。Nikon拡張は未実装
ptp-ip (Go)GoPTP/IP実装。Fujifilm向けだが標準部分は共通

BLE経由

ツール言語概要
furbleC++ (ESP32)BLEリモートシャッター。COOLPIX B600で動作確認済み

ptpip-d5300がWiFi経由での第一候補。D5300向けだがPTP/IPは標準規格なので、基本的なファイル転送やデバイス情報取得は動く可能性がある。

pip install ptpip
from ptpip import PtpIpConnection

# A1000のWiFi APに接続した状態で
conn = PtpIpConnection("192.168.1.1", 15740)
conn.open_session()
device_info = conn.get_device_info()
print(device_info)

SnapBridgeアプリの権限

SnapBridge(com.nikon.snapbridge.cmru v2.13.3)がAndroidで要求するパーミッションも確認した。 Exodus Privacyのレポートから、注目すべき権限を抜き出した。

パーミッション用途
CHANGE_WIFI_MULTICAST_STATEマルチキャストでのデバイス探索
NFCNFC対応カメラ向け(A1000はNFC非搭載)
RECEIVE_BOOT_COMPLETED起動時のBLE常時接続復帰
REQUEST_IGNORE_BATTERY_OPTIMIZATIONSBLE常時接続の維持
FOREGROUND_SERVICE_CONNECTED_DEVICEBLEデバイス接続のフォアグラウンドサービス

トラッカーはAdobe Experience Cloudが1件。通信の中身としてはPTP/MTPコマンドのラッパーなので、データ漏洩リスクは低い。

Bluetooth Classicペアリング

BLE認証だけではWiFi APが起動しない。Bluetooth Classicのセキュアボンディングが必要。

Classic BTの発見

macOSのBluetooth Classicスキャン(blueutil --inquiry)では、BLE接続中にカメラのClassicデバイスは見えない。BLE認証後にBluetoothを再起動(off→on)すると、カメラが別のMACアドレスでClassicデバイスとして出現する。

接続方式MACアドレス
BLE毎回変わる(macOSがランダム化)
Bluetooth Classic3C:E1:A1:16:13:79(固定)

ペアリングの成功

macOSのシステム設定(Bluetooth)から手動でNumeric Comparisonペアリングを実施。両側で同じ6桁の数字を確認してOKを押す方式。

ポイントはMac側を先にOK→すぐにカメラ側のOKの順序。逆だとタイムアウトする。blueutilexpectでの自動化ではタイミングが合わず、macOS標準UIからの手動操作で初めて成功した。

カメラのClassic BTサービス

ペアリング後にSDP(Service Discovery Protocol)クエリで取得したカメラのサービス一覧。

サービス名RFCOMM ChannelUUID
SPP ServiceChannel 10x1101(Serial Port Profile)
iAP ServiceChannel 2独自UUID
GATT over L2CAPPSM 310x1800 / 0x1801

SPP Serviceが存在し、macOS上に/dev/tty.A1000_21000717としてシリアルポートが出現した。

フリーズ問題

Classicペアリング成功後、カメラが「接続処理中」でフリーズする問題が発生した。Mac側にSnapBridgeのSPPサーバーがないため、カメラがRFCOMM接続を試みて永遠に待つ状態になる。電源ボタンも効かず、バッテリー抜きでしか復帰しない。

Mac側の確認タイミングとカメラ側のOK操作の順序を調整することで、フリーズせずにペアリングを完了できるケースがあった。撮影モードに戻り、BT接続を維持した状態でカメラが動作する。位置情報の設定ダイアログまで出てきたので、カメラはMacをSnapBridgeデバイスとして認識している。

写真撮影後のフラグ変化

Classicペアリング済み・BT接続維持中に写真を撮影すると、BLEの0x2008(LSS_CONTROL_POINT)が0x0000から0x0001に変化した。カメラが画像転送を要求している状態だと思われる。

しかしBLE認証(Blowfishハンドシェイク)がClassicと別セッションのため、WiFiトリガー(0x2005への書き込み)は受け付けられない。

残っている壁

SnapBridgeはBLE認証→Classicボンディング→WiFi AP起動を同一セッション内で行う。今回の実験ではBLE認証後にBluetooth再起動が必要で、セッションが分断される。

macOSではBLE接続中にClassicペアリングを同時実行する方法が見つかっていない。Androidでは同一BTスタック上で両方を処理できるが、macOSのCoreBluetooth(BLE)とIOBluetooth(Classic)は別レイヤーで動作する。

現状と次のステップ

できたこと

  • USB: macOSでMTP/PTPデバイスとして認識、Product ID 0x036B を実測
  • BLE: Blowfish認証を完全実装・実行、ソルトインデックス特定、シリアル番号取得
  • BLE: 全GATT Characteristicの役割を解明(13種)
  • BLE: リモートモードのCharacteristic(0x20800x2087)発見とペアリング
  • Classic BT: macOSシステム設定からのNumeric Comparisonペアリング成功
  • Classic BT: カメラのSPP/iAPサービスのSDP情報取得
  • Classic BT: /dev/tty.A1000_21000717 シリアルポート出現確認
  • プロトコル解析: WiFi AP起動フロー、BLE→Classic→WiFiの3段階ゲートの全容解明

突破できなかった壁

  • BLE認証とClassicボンディングの同一セッション化(macOSの制約)
  • WiFi APの起動(Classicボンディング後のBLE再認証でNOT1が来ない)
  • RFCOMMシリアルポート経由のPTP通信(カメラが応答しない)

次に試すこと

  • BLE認証時のdevice IDを固定値にして、Classicペアリング後のBLE再認証で同一デバイスとして認識させる
  • Classicペアリングの手順をスクリプト化(Mac先にOK→カメラOK、タイミング調整済み)
  • macOS IOBluetooth APIでBLE接続中のClassicペアリングをプログラマティックに実行
  • SPPサーバーをClassicペアリング前に起動してカメラのRFCOMM接続を受け入れる

SnapBridgeはBLE→Bluetooth Classic→WiFiの3段階ゲートキーパーとして機能している。BLE認証もClassicペアリングも個別には突破できた。カメラはMacをSnapBridgeデバイスとして認識し、写真撮影後に転送フラグも立てている。残る課題は「同一セッション内での両方の完了」のみ。