Skip to content
/ miniPtp Public

a portable and easy to use Python library to interact with your camera via the Picture Transfer Protocol

License

Notifications You must be signed in to change notification settings

lclevy/miniPtp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

15 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

miniPtp.py

version 16jan2023

A minimal Python PTP implementation to talk to your Camera.

Take it as an educational example for PyUsb and PTP, feel free to discover proprietary functions, such as 0x911F / EOS_UpdateFirmware.

Installation

Tested with:

  • Python 3.10 and Python 3.8
  • Windows 10 + libusb-win ("libusb-win32 filter" installed with Zadig)
  • Ubuntu 20.10 and MacOs 11
  • Canon R6 and Ixus 180 (aka Elph 190)

Requirements

  • PyUSB 1.2.1, pyyaml

Features

  • list supported operations, properties and properties descriptions.
  • list storages and files/handlers
  • allow to get a file (object) using his handler
  • allow to upload a file at root dir (on Ixus 180). Error 0x200f / access denied on R6.
  • Only USB transport yet, but designed with IP as possible extension
  • PTP operations implemented : GetDeviceInfo, OpenSession, CloseSession, GetStorageIDs, GetStorageInfo, GetObjectHandles, GetObjectInfo, GetObject, GetDevicePropDesc, SendObjectInfo, SendObject and Canon GetMacAddress. But you can use ptp.transaction directly.

Syntax

a la ptpcam:

usage: miniPtp.py [-h] [-o] [-p] [-d] [-i] [-L] [-g HANDLER] [-u UPLOAD]

options:
  -h, --help            show this help message and exit
  -o, --list-operations
                        list supported operations
  -p, --list-properties
                        list supported properties
  -d, --list-properties-description
                        list supported properties
  -i, --info            show device info
  -L, --list-files      list all files
  -g HANDLER, --get-file HANDLER
                        get file by given handler
  -u UPLOAD, --upload UPLOAD
                        upload file. storage,filename

Upload example

>python miniPtp.py -u 0,miniPtp.py
+ Model= Canon IXUS 180
+ Device_version= 1-15.0.1.0
Connected
+ Model_id: 0x4030000
Try to upload miniPtp.py
PTP_SendObjectInfo returned: storage = 0x10001 and handle = 0x40029
Done

>python miniPtp.py -L
+ Model= Canon IXUS 180
+ Device_version= 1-15.0.1.0
Connected
+ Model_id: 0x4030000
+ Storage_IDs
3
{'storage_type': 4, 'filesystem_type': 3, 'access_capability': 0, 'max_capacity': 7736426496, 'free_space_bytes': 7726202880, 'free_space_objects': 4294967295, 'storage_description': 'SD', 'volume_identifier': ''}
  00080000 00010001 00000000 3001        0 DCIM
    01a00000 00010001 00080000 3001        0 104___07
      01a0aee1 00010001 01a00000 3801  4952866 IMG_2798.JPG
      01a0aef1 00010001 01a00000 3801  4637973 IMG_2799.JPG
  00040029 00010001 00000000 bf01    18517 miniPtp.py

miniPtp.py example

>python miniPtp.py -Lodip
+ operations_supported
0x1001/PTP_GetDeviceInfo 0x1002/PTP_OpenSession 0x1003/PTP_CloseSession 0x1004/PTP_GetStorageIDs 0x1005/PTP_GetStorageInfo 0x1006/PTP_GetNumObjects 0x1007/PTP_GetObjectHandles 0x1008/PTP_GetObjectInfo 0x1009/PTP_GetObject 0x100a/PTP_GetThumb 0x100b/PTP_DeleteObject 0x100c/PTP_SendObjectInfo 0x100d/PTP_SendObject 0x100f/PTP_FormatStore 0x1014/PTP_GetDevicePropDesc 0x1016/PTP_SetDevicePropValue 0x101b/PTP_GetPartialObject 0x902f 0x9033/Canon_GetMacAddress 0x9050/Canon_InitiateEventProc_50 0x9051/Canon_TerminateEventProc_51 0x905c/Canon_InitiateEventProc_5c 0x905d/Canon_TerminateEventProc_5d 0x9060/Canon_IsNeoKabotanProcMode 0x9068/Canon_GetWebServiceSpec 0x9069/Canon_GetWebServiceData 0x906a/Canon_SetWebServiceData 0x906b/Canon_DeleteWebServiceData 0x906c/Canon_GetRootCertificateSpec 0x906d/Canon_GetRootCertificateData 0x906e/Canon_SetRootCertificateData 0x906f/Canon_DeleteRootCertificateData 0x9077/Canon_GetTranscodeApproxSize 0x9078/Canon_RequestTranscodeStart 0x9079/Canon_RequestTranscodeCancel  0x9101/EOS_GetStorageIDs 0x9102/EOS_GetStorageInfo 0x9103/EOS_GetObjectInfo 0x9104/EOS_GetObject 0x9105/EOS_DeleteObject 0x9106/EOS_FormatStore 0x9107/EOS_GetPartialObject 0x9108/EOS_GetDeviceInfoEx 0x9109/EOS_GetObjectInfoEx 0x910a/EOS_GetThumbEx 0x910c/EOS_SetObjectAttributes 0x910f/EOS_Remote_Release 0x9110/EOS_SetDevicePropValueEx 0x9114/EOS_SetRemoteMode 0x9115/EOS_SetEventMode 0x9116/EOS_GetEvent 0x9117/EOS_TransferComplete 0x9118/EOS_CancelTransfer 0x911a/EOS_PCHDDCapacity 0x911b/EOS_SetUILock 0x911c/EOS_ResetUILock 0x911d/EOS_KeepDeviceOn 0x911e 0x911f/EOS_UpdateFirmware 0x9122 0x9123 0x9124 0x9127 0x9128/EOS_RemoteReleaseOn 0x9129/EOS_RemoteReleaseOff 0x912b 0x912c/EOS_GetPartialObjectEx 0x912d 0x912e 0x912f 0x9130 0x9131 0x9132/EOS_EndGetPartialObjectEx 0x9133 0x9134 0x9135/EOS_GetCTGInfo 0x9136/EOS_GetLensAdjust 0x9137 0x9138 0x9139 0x913a 0x913b 0x913c 0x913d/EOS_SetRequestOLCInfoGroup 0x913e 0x913f/EOS_GetCameraSupport 0x9140 0x9141/EOS_RequestInnerDevelopStart 0x9143/EOS_RequestInnerDevelopEnd 0x9144/EOS_GetGpsLoggingData 0x9145/EOS_GetGpsLogCurrentHandle 0x9146/EOS_SetImageRecoveryData 0x9148/EOS_FormatRecoveryData 0x9149/EOS_GetPresetLensAdjustParam 0x914a/EOS_GetRawDispImage 0x914b/EOS_SaveImageRecoveryData 0x914d/EOS_DrivePowerZoom 0x914f/EOS_GetIptcData 0x9150/EOS_SetIptcData 0x9153/EOS_GetViewFinderData 0x9154/EOS_DoAutoFocus 0x9155/EOS_DriveLens 0x9157/Canon_ClickWB 0x9158/Canon_Zoom 0x9159/Canon_ZoomPosition 0x915a/Canon_SetLiveAFFrame 0x915b/Canon_TouchAfPosition 0x915c/Canon_SetLvPcFlavoreditMode 0x915d/Canon_SetLvPcFlavoreditParam 0x9160/Canon_AFCancel 0x9166 0x916b/Canon_SetImageRecoveryDataEx 0x916c/Canon_GetImageRecoveryListEx 0x916d/Canon_CompleteAutoSendImages 0x916e/Canon_NotifyAutoTransferStatus 0x916f/Canon_GetReducedObject 0x9170/Canon_GetObjectInfo64 0x9171/Canon_GetObject64 0x9172/Canon_GetPartialObject64 0x9173/Canon_GetObjectInfoEx64 0x9174/Canon_GetPartialObjectEx64 0x9177/Canon_NotifySaveComplete 0x9178/Canon_GetTranscodedBlock 0x9179/Canon_TransferCompleteTranscodedBlock 0x9180 0x9181 0x9182/Canon_NotifyEstimateNumberofImport 0x9183/Canon_NotifyNumberofImported 0x9184/Canon_NotifySizeOfPartialDataTransfer 0x9185/Canon_NotifyFinish 0x9186/EOS_GetWftData 0x9187/EOS_SetWftData 0x9188/EOS_ChangeWftSettingNum 0x9189/EOS_GetPictureStylePCFlavorParam 0x918a 0x91ae 0x91af 0x91b9/Canon_SetFELock 0x91ba/EOS_DeleteWftSettingNum 0x91d3 0x91d4/Canon_SendCertData 0x91d5 0x91d7/Canon_DistinctionRTC 0x91d8/Canon_NotifyGpsTimeSyncStatus 0x91d9 0x91da 0x91db 0x91dc 0x91dd 0x91de 0x91e1 0x91e3 0x91e6/Canon_NotifyAdapterStatus 0x91e7 0x91e8/Canon_ceresNotifyNetworkError 0x91e9/Canon_AdapterTransferProgress 0x91ec/Canon_ceresSEndWpsPinCode 0x91f0/Canon_TransferComplete2 0x91f1/Canon_CancelTransfer2
+ device_prop_supported
0x5001/BatteryLevel 0xd303/UsedDeviceState 0xd402/DeviceFriendlyName 0xd406/SessionInitiatorVersionInfo 0xd407/PerceivedDeviceType
+ Model= Canon EOS R6
+ Device_version= 3-1.5.0
+ Opening session
Connected
+ Model_id: 0x80000453
LensName b'RF24-105mm F4 L IS USM'
hostname b'EOSR6_xxxxxx'
+ Mac_addr b'74bfc0xxxxxx'
+ properties supported
property_code: 0x5001/BatteryLevel, datatype: 0x2/uint8, current: 16
property_code: 0xd303/UsedDeviceState, datatype: 0x2/uint8, current: 1
property_code: 0xd402/DeviceFriendlyName, datatype: 0xffff/str, current: Canon EOS R6
property_code: 0xd406/SessionInitiatorVersionInfo, datatype: 0xffff/str, current: Windows/10.0.19045 MTPClassDriver/10.0.19041.0
property_code: 0xd407/PerceivedDeviceType, datatype: 0x6/uint32, current: 1
+ Storage_IDs
0
{'storage_type': 4, 'filesystem_type': 3, 'access_capability': 0, 'max_capacity': 0, 'free_space_bytes': 0, 'free_space_objects': 4294967295, 'storage_description': 'SD1', 'volume_identifier': ''}
2
{'storage_type': 4, 'filesystem_type': 3, 'access_capability': 0, 'max_capacity': 15923150848, 'free_space_bytes': 15420555264, 'free_space_objects': 4294967295, 'storage_description': 'SD2', 'volume_identifier': ''}
  90000000 00020001 00000000 3001 1        0 DCIM
    91900000 00020001 90000000 3001 1        0 100CANON
      9190d321 00020001 91900000 b108 0 21662294 2U4A3378.CR3
      9190d381 00020001 91900000 b108 0 18202710 2U4A3384.CR3
...
      9190d4f1 00020001 91900000 b108 0 18219606 2U4A3407.CR3
    91940000 00020001 90000000 3001 1        0 101CNOOO
      9194d501 00020001 91940000 b108 0 24550486 2U4A3408.CR3
  a0080000 00020001 00000000 3001 1        0 MISC

to get file 2U4A3384.CR3:

>python miniPtp.py -g 5190d381
...
transfering 2U4A3384.CR3 (18202710 bytes)...
done

Transaction API

The transaction function is designed to allow one liners and test easily new code and parameters:

  def transaction(self, code, req_params, data_phase=True, senddata=None):
    ...
  return { 'ResponseCode': ptp_header.code, 'Data':bytes(data), 'Parameter':respParams }
  

With the following line, you send a request with code 0x9033 and no parameter (the 2nd arg, the empty list). You got back data in the with 'Data' key.

mac_address = self.transaction( 0x9033, [] )['Data'][ptp.S_HEADER.size:]

In the following example, we open the session with this request which has one parameter, the session id. False means there is no data phase:

self.transaction( 0x1002, [ self.session_id ], False )

In the following, we have got 3 parameters during request, and a list of 1 parameter (type int) inside response (type 3):

  def get_num_objects( self, storage_id, object_format=0, association=0xffffffff ):
    data = self.transaction( 0x1006, [ storage_id, object_format, association ], False )['Parameter'] # all_formats, all_handles
    return data[0]

Of course, you can check if 'ResponseCode' is 0x2001 which means transaction worked.

You can also send data during data phase:

  def set_prop_device_value( self, prop, value ): 
    res = self.transaction( 0x1016, [ prop ], senddata=value )
    #print(res['ResponseCode'])

miniPtp as library

You can use use miniPtp as a library by importing ptp. In the example below (see update_canon.py), you can send a firmware update file to you EOS camera using PTP/USB:

# update_canon.py
# python update.py filepath/EOSR6150.FIR

import sys
import os
from binascii import hexlify

from miniPtp import ptp

try:
  ptp_obj = ptp()
except ValueError as e:
  print(e)
  sys.exit()  

dev_info = ptp_obj.get_device_info() 

if 0x911f in dev_info['operations_supported']: #0x911f is EOS_UpdateFirmware
  print('Update operation is supported')
  
  ptp_obj.open_session()

  PTP_DATA_LEN = 0x200000 
  FILENAME_LEN = 32
  FIRM_CHUNK_LEN = PTP_DATA_LEN - FILENAME_LEN
    
  with open(sys.argv[1], 'rb') as firm_f:
    firm_data = firm_f.read()
    base_filename = os.path.basename(sys.argv[1])
    filename = base_filename.encode() + b'\x00'*(FILENAME_LEN - len(base_filename)) #for each transaction, FIR data is prefixed by filename within a 32 bytes field

    sent = 0
    error = False
    while sent < len(firm_data) and not error:
      print('Transaction %d : filename %s, fir length %d, offset %d' % (ptp_obj._transaction, base_filename, len(firm_data), sent) )
      r = ptp_obj.transaction( 0x911f, [ len(firm_data), sent ], senddata = filename + firm_data[sent:sent+FIRM_CHUNK_LEN] )
      error = r['ResponseCode'] != 0x2001
      sent += min(FIRM_CHUNK_LEN, len(firm_data)-sent)
    print('Done')  
else:
  print('Update firmware operation not supported') 

References and inspirations

Official ways to drive your Camera from Canon:

About

a portable and easy to use Python library to interact with your camera via the Picture Transfer Protocol

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages