1. Introduction
Similar to EOS cameras, Canon PowerShots support small set of standard PTP commands and device properties for identification and image transfer, while main camera control functionality is implemented as a set of vendor extension commands and device properties. All camera functions, including lens zoom, focus, and shooting mode are controlled by firmware. It is possible for the Arduino equipped with USB Host Shield to take control over the camera making it cheap yet powerful tool for automated shooting tasks such as motion-activated, aerial, and time lapse photography. Unlike Canon EOS PTP extension, PowerShot PTP implementation is pretty straightforward. PowerShots operate device properties in a standard PTP way, what means that base class methods GetDevicePropValue
/SetDevicePropValue
/GetDevicePropDesc
can be used with no “hacking”. The code is part of Arduino Camera Control GitHub repo; it works on top of USB Host library written to run on Arduino board equipped with USB Host Shield.
2. Preparations
Canon makes many different cameras under PowerShot brand, some very simple, another quite sophisticated. Most entry-level full-auto-only models won’t support remote control via PTP; suitable cameras are A640, S3, Gxx or similar. To see if a certain PowerShot camera has remote control capability, check Gphoto project. A camera supported in GPhoto will most likely work with Arduino PTP library; however, there is no guarantee. Canon PowerShot SDK page is also worth checking. Finally, there are two sketches developed to help exploring camera capabilities. Before start coding for your camera try running them to see if your camera is capable of performing remote operations you need for your application.
Start with PTPDevInfo sketch to find out if your camera PTP protocol supports remote shooting. Take a look at sketch output for various cameras. Check to see if your camera supports PTP InitiateCapture
command (0x100e), PowerShot-specific PS_StartShootingMode
command (0x9008), or PS_InitiateCaptureInMemory
(0x901a). Then run PSDevProp sketch to find out which device properties are supported and available for setting/getting and their types as well. I specifically point out to PSDevProp
sketch because many properties are not shown until camera is properly initialized and PTPDevInfo
is meant to be generic and does not perform PowerShot-specific initialization. Here is the example of the PSDevProp output for the ISO speed property:
Device Property: D01C (Vendor defined) Data Type: UINT16 Get/Set: Get/Set Default Value: 0000 Current Value: 0000 Enumeration: {0000, 0045, 0048, 0050, 0058, 0060} |
When all of the above is done and available camera functions are known you may start coding. One great thing about using standard PTP GetDevicePropDesc
function is that it returns device property description in a format defined in PTP specification – it returns enumerator or range of acceptable values which makes it possible to use different camera models without being afraid of setting wrong device property value not supported by the camera. In the example above, which was taken from PowerShot A640, enumeration shows values for ‘Auto’, ’80’, ‘100’, ‘200’, ‘400’, and ‘800’, in this order. On some other camera, where, for example, ISO 80 and 800 are not available, the enumeration for this property would look like {0000, 0048, 0050, 0058}
.
3. Using CanonPS class
CanonPS
class is derived from PTP
class and implements PowerShot specific initialization and a few methods. canonps.h
file contains operation codes and device property definitions with descriptive names. All constants have names similar to Canon PowerShot SDK constants, so if you have any trouble using constants not used in sample sketches you can look through the SDK documentation for more detailed explanation.
There are several simple steps you have to follow when coding for your PowerShot:
- Derive your own class from
PSStateHandlers
- Implement at least one virtual method
OnDeviceInitializedState
- Implement your functionality call within
OnDeviceInitializedState
method - Create an instance of
CanonPS
class - Implement a call to
Init
method from thesetup
function - Implement a call to
Task
method from theloop
function - From
loop
or timer interrupt handler function periodically callEventCheck
. I don’t recommend callingEventCheck
more frequently than once per 300 ms because of the camera hardware latency. If you are developing for a specific camera model and don’t worry about compatibility, time interval can be made shorter, as long as camera agrees
Here is a fragment of PSCapture
sample sketch code illustrating the above steps (includes, defines and parser code are omitted for brevity):
class CamStateHandlers : public PSStateHandlers { bool stateConnected; public: CamStateHandlers() : stateConnected(false) {}; virtual void OnDeviceDisconnectedState(PTP *ptp); virtual void OnDeviceInitializedState(PTP *ptp); } CamStates; CanonPS Ps(DEV_ADDR, DATA_IN_EP, DATA_OUT_EP, INTERRUPT_EP, CONFIG_NUM, &CamStates); SimpleTimer eventTimer, captureTimer; void CamStateHandlers::OnDeviceDisconnectedState(PTP *ptp) { if (stateConnected) { eventTimer.Disable(); captureTimer.Disable(); stateConnected = false; Notify(PSTR("Camera disconnected\r\n")); } } void CamStateHandlers::OnDeviceInitializedState(PTP *ptp) { if (!stateConnected) { Notify(PSTR("stateConnected\r\n")); stateConnected = true; eventTimer.Enable(); captureTimer.Enable(); } } void setup() { Serial.begin( 115200 ); Serial.println("Start"); Ps.Setup(); eventTimer.Set(&OnEventTimer, 200); captureTimer.Set(&OnCaptureTimer, 5000); delay( 200 ); } void loop() { eventTimer.Run(); captureTimer.Run(); Ps.Task(); } void OnCaptureTimer() { Ps.SetDevicePropValue(PS_DPC_CaptureTransferMode, (uint16_t)0x0D); uint16_t rc = Ps.Capture(); if (rc != PTP_RC_OK) Message(PSTR("Error: "), rc); } void OnEventTimer() { PSEventParser prs; Ps.EventCheck(&prs); if (uint32_t handle = prs.GetObjHandle()) { PTPObjInfoParser inf; Ps.GetObjectInfo(handle, &inf); } } |
4. Parsers
Because of the shortage of RAM, parsers really become vital tools in PTP programming for Arduino. With the updated version of PTP library you do not have to write your own parsers for device property info – I wrote a general purpose set of parser templates for device property info extraction and modification in a sequential up/down manner. To be able to use it simply include ptpdpparser.h
. By the time you start using parsers I hope you did not forget to find out the type of the device property you want to change. If you simply need to set the desired property one step up or down or want to output value to the console you can use these three simple templates:
template <class VALUE_TYPE> uint16_t StepUp(PTP *ptp, uint16_t prop) |
Sets new value of the device property, specified by prop, to a new value which is one step greater than the current value.
template <class VALUE_TYPE> uint16_t StepDown(PTP *ptp, uint16_t prop) |
Sets new value of the device property, specified by prop, to a new value which is one step smaller than the current value.
template <class VALUE_TYPE, class LIST_VALUE_TYPE, const uint8_t TABLE_SIZE, const uint8_t TEXT_SIZE> uint16_t PrintValueTitle(PTP *ptp, uint16_t prop, const ValueTitle<LIST_VALUE_TYPE, TEXT_SIZE> *p ) |
Outputs title of the current value of the device property, specified by prop, out of the title list specified by ValueTitle parameter.
As the name suggests, VALUE_TYPE
stands for the type of the device property value which you should know before you start coding. Each of two template functions StepUp
and StepDown
simply creates a parser, calls GetDevicePropertyDesc
to get current value of the device property along with the enumerator or range values and sets the neighbor value if available. All you have to care of is to specify the right VALUE_TYPE
. This is how it is done in PSRemote sketch:
StepUp<uint16_t>((PTP*)&Ps, PS_DPC_Aperture); |
and
StepDown<uint16_t>((PTP*)&Ps, PS_DPC_Aperture); |
PrintValueTitle
is a bit more complicated at first sight. VALUE_TYPE
has the same meaning. LIST_VALUE_TYPE
stands for the type of the numerical value specified in ValueTitle
definitions. TABLE_SIZE
is the constant which specifies the number of elements in ValueTitle
array. TEXT_SIZE
is the length of the title field. Title arrays for some device property values are defined in psvaluetitles.h
header file. Here is how it is implemented for the aperture in PSRemote
sample sketch:
PrintValueTitle<uint16_t, uint8_t, VT_APT_COUNT, 4>((PTP*)&Ps, PS_DPC_Aperture, ApertureTitles); |
There is one important thing about VALUE_TYPE
and LIST_VALUE_TYPE
I have to focus on. Because cameras usually use more bytes for the value than it is necessary, I sometimes reduce the insignificant bytes in order to save memory. That is why VALUE_TYPE
and LIST_VALUE_TYPE
may have different value types. VALUE_TYPE
is the type the device property value represented in camera. LIST_VALUE_TYPE
is the type the values are represented in ValueList
.
5. Summary
PowerShot part of PTP library lets you use many interesting features of your camera. When coding your real life application reuse the already written code for parsers in order to save your time and effort. Use sample sketches to see how the library code can be reused.