Using the THETA API Over USB

camera usb ready
Figure 1. Ready for USB Control - Power on and unmounted

Developers can access the RICOH THETA API over USB.

Advantages of API Over USB

  • The THETA can be left powered on indefinitely for applications such as surveillance, map creation or time-lapse photography

  • The files download much faster from the camera to the computer

  • There are no issues with WiFi signal strength or WiFi setup

Disadvantages of API Over USB

  • The PTP API (the USB one) is not as complete as the THETA API v2 (the WiFi one)

  • There are less people using the USB API

  • It’s harder to work with as you need to send it hex codes, not human-readable commands

  • We may find issues with the libraries for PTP access

If you choose to develop your application with the v2 USB API, you are a pioneer. Please contribute your experiences with the community.

Controlling the THETA from Raspberry Pi or Linux With USB

The community initially started connecting to the THETA from Windows with MTP Extensions and Raspberry Pi devices using libptp. As I am more familiar with Linux, I ran my tests on Ubuntu 16.04. I also know that people are using the same programs on the Raspberry Pi with Raspian OS (similar to Ubuntu or Debian).

As far as I know, the API works fine on Windows and the examples on the Microsoft site work.

Note
In the above example for Windows, change the Op Code to 0x100e and the StorageID

This tutorial will focus on libptp, which I compiled from source. I’ll document the installation of libptp in a future update. libptp also installs ptpcam.

Preparing the Camera for USB Control

  • Camera is on, there is a blue light

  • Camera is connected with USB to Linux computer (camera is USB 2.0, but it’ll work on a USB 3.0 port)

  • Camera is not mounted as filesystem on your computer

Tip
With $3 for common nuts and a bolt, you can attach the THETA to any tripod or attach to another machine for industrial use. Use 1/4 inch, 20 TPI parts
usb tripod
Figure 2. 1/4 inch, 20 TPI bolt adapter

Here’s more information on building a camera attachment adapter that works with the USB cable plugged in.

Verify Computer finds Camera as USB Device

$ lsusb
Bus 003 Device 009: ID 05ca:0366 Ricoh Co., Ltd

Verify ptpcam Can Connect to Camera

First verify that ptpcam can connect to the camera.

$ ptpcam -i

Camera information
==================
Model: RICOH THETA S
  manufacturer: Ricoh Company, Ltd.
  serial number: '00010093'
  device version: 01.42
  extension ID: 0x00000006
  extension description: (null)
  extension version: 0x006e

Next, we’ll list all the operations that libptp can access on the camera over usb.

$ ptpcam -o
Listing supported operations...
Camera: RICOH THETA S
  0x1001: GetDeviceInfo
  0x1002: OpenSession
  0x1003: CloseSession
  0x1004: GetStorageIDs
  0x1005: GetStorageInfo
  0x1006: GetNumObjects
  0x1007: GetObjectHandles
  0x1008: GetObjectInfo
  0x1009: GetObject
  0x100a: GetThumb
  0x100b: DeleteObject
  0x1014: GetDevicePropDesc
  0x1015: GetDevicePropValue
  0x101b: GetPartialObject
  0x9001: UNKNOWN
  0x9991: UNKNOWN
  0x9999: UNKNOWN
  0x999a: UNKNOWN
  0x999b: UNKNOWN
  0x999c: UNKNOWN
  0x999d: UNKNOWN
  0x100e: InitiateCapture
  0x1016: SetDevicePropValue
  0x101c: InitiateOpenCapture
  0x1018: TerminateOpenCapture
  0x99a2: UNKNOWN

It’s looking very promising. Next, we’ll list the properties that can accessed.

Access Camera Properties Over USB

$ ptpcam -p
Listing properties...
Camera: RICOH THETA S
  0x5001: Battery Level
  0x5002: Functional Mode
  0x5003: Image Size
  0x5011: Date Time
  0x5012: Pre-Capture Delay
  0xd407: UNKNOWN
  0x5005: White Balance
  0x500e: Exposure Program Mode
  0x500f: Exposure Index (film speed ISO)
  0x5010: Exposure Bias Compensation
  0x5013: Still Capture Mode
  0x501a: Timelapse Number
  0x501b: Timelapse Interval
  0x502c: UNKNOWN
  ....

Let’s check if I have a good battery charge.

$ ptpcam --show-property=0x5001
Camera: RICOH THETA S
'Battery Level' is set to: 100

It’s plugged into USB, so it makes sense that my battery is at 100%.

Next, I’ll check the image size:

$ ptpcam --show-property=0x5003
Camera: RICOH THETA S
'Image Size' is set to: "5376x2688"

I’ll check the time:

$ ptpcam --show-property=0x5011
Camera: RICOH THETA S
'Date Time' is set to: "20160701T142845-0700"

Check Capture Mode

$ ptpcam --show-property=0x5013
Camera: RICOH THETA S
'Still Capture Mode' is set to: [Normal]

Set Capture Mode to Video

Using the RICOH v2 USB documentation, you can find that video shooting corresponds to 0x8002.

$ ptpcam --set-property=0x5013 --val=0x8002
Camera: RICOH THETA S
'Still Capture Mode' is set to: [Normal]
Changing property value to 0x8002 [(null)] succeeded.

Verify that the change was saved:

$ ptpcam --show-property=0x5013
Camera: RICOH THETA S
'Still Capture Mode' is set to: 0x8002 (-32766)

The blue light on the THETA now shows a video icon.

video light
Figure 3. Using USB to set mode

At this stage, I boiled a pot of tea and left the camera on. When I came back, it had turned off.

I verified that the battery was still at 100%.

$ ptpcam --show-property=0x5013
Camera: RICOH THETA S
'Still Capture Mode' is set to: 0x8002 (-32766)
craig@linux-silver:~$ ptpcam --show-property=0x5001
Camera: RICOH THETA S
'Battery Level' is set to: 100

I then looked at the sleepDelay

$ ptpcam --show-property=0xd803
Camera: RICOH THETA S
'UNKNOWN' is set to: 600

The sleepDelay is set to 600 seconds, or 10 minutes. I’m going to disable it.

$ ptpcam --set-property=0xd803 --val=0
Camera: RICOH THETA S
'UNKNOWN' is set to: 600
Changing property value to 0 [(null)] succeeded.

Now, hopefully, the THETA will stay on indefinitely, powered by the USB and I’ll be able to use it in an industrial application such as security surveillance.

List the files on your THETA.

$ ptpcam -L
Listing files...
Camera: RICOH THETA S
Handler:           Size:     Captured:          name:
0x0064026e:      4000851    2016-06-03 21:11    R0010622.JPG
0x0064026f:      3953884    2016-06-09 22:52    R0010623.JPG
0x00640270:      3923907    2016-06-10 00:22    R0010624.JPG

Download the first file from the camera to your local computer.

$ ptpcam --get-file=0x0064026e
Camera: RICOH THETA S
Saving file: "R0010622.JPG" is done.

Boom! It’s super fast.

You can look at the image in your Linux file browser just to verify that the image was downloaded. In this picture, I have the THETA lying on it’s side on my desk.

image sample
Figure 4. Sample Image Downloaded with USB API

It’s not a very nice picture, so I’ll delete it.

$ ptpcam --delete-object=0x0064026e
Object 0x0064026e (R0010622.JPG) deleted.

I’m going to take another shot with my camera in a tripod. First, I’ll set the mode to still image as I set it to video earlier.

$ ptpcam --set-property=0x5013 --val=0x0001
Camera: RICOH THETA S
'Still Capture Mode' is set to: 0x8002 (-32766)
Changing property value to 0x0001 [(null)] succeeded.

Then, I take the image.

$ ptpcam -c
Initiating captue...

List the files on the camera:

$ ptpcam -L
Listing files...
Camera: RICOH THETA S
Handler:           Size:     Captured:          name:
....
0x0064027e:      3930938    2016-07-01 23:22    R0010638.JPG

Download the file to my local computer

$ ptpcam --get-file=0x0064027e
Camera: RICOH THETA S
Saving file: "R0010638.JPG" is done.

Verify that the download worked.

sample
Figure 5. Sample Image of My Rig Downloaded with USB API

You can now see my boss Antec case for my overclocked system and the top of my overclocked and watercooled Linux test rig decorated with rainbow tape by my daughter.

I then checked the battery to verify that the THETA can last indefinitely connected with USB:

$ ptpcam --show-property=0x5001
Camera: RICOH THETA S
'Battery Level' is set to: 100

Wait! The camera just turned off. I’m going to connect with the WiFi USB to set the offDelay.

Tip
I have two network interfaces in my computer, I can access the THETA over WiFi while I am connected to the Internet.
Tip
I use DHC to easily set the options. DHC is a tool to send, save, and organize HTTP requests. As I test the THETA frequently.
dhc
Figure 6. Use the free DHC Chrome App to save HTTP tests

If the image is difficult to see, the relevant POST body is

{"name": "camera.setOptions",
    "parameters":
	{
    "sessionId": "SID_0001",
    "options": {
		"offDelay": 65535
    	}
	}
}

You can check the offDelay parameter with this:

{"name": "camera.getOptions",
    "parameters":
	{
    	"sessionId": "SID_0001",
    	"optionNames": [
            "offDelay"
    	]
	}
}

Again, I have this template saved in DHC, so it’s easy for me to just change the optionNames.

I’m hopeful that the THETA will now stay on forever. At this point, it’s been on for several hours and I can send it API commands. So, it’s in a good state for surveillance or mapping. I think I can leave it on for days. I just checked the battery again after leaving the camera on for hours.

$ ptpcam --show-property=5001
Camera: RICOH THETA S
'Battery Level' is set to: 100

Bring on the industrial applications!

Using Raw PTP Commands

Your program may have to use raw PTP commands. It’s almost the same as the examples above. I’ll include some examples using raw PTP.

Grabbing Info

Pass the raw PTP command of GetDeviceInfo as a hex string, 0x1001

My gosh, this looks horrible. What does it mean? Oh wait, there’s some human-readable text in the right-hand column next to the hex dump.

craig@linux-silver:$ ptpcam -R 0x1001
Camera: RICOH THETA S
Sending generic request: reqCode=0x1001, params=[0x00000000,0x00000000,0x00000000,0x00000000,0x00000000]
64 00 06 00 00 00 6e 00 00 01 80 1a 00 00 00 01 - d.....n.........
10 02 10 03 10 04 10 05 10 06 10 07 10 08 10 09 - ................
10 0a 10 0b 10 14 10 15 10 1b 10 01 90 91 99 99 - ................
99 9a 99 9b 99 9c 99 9d 99 0e 10 16 10 1c 10 18 - ................
10 a2 99 06 00 00 00 02 40 06 40 08 40 0a 40 0c - ........@.@.@.@.
40 0d 40 1d 00 00 00 01 50 02 50 03 50 11 50 12 - @.@.....P.P.P.P.
50 07 d4 05 50 0e 50 0f 50 10 50 13 50 1a 50 1b - P...P.P.P.P.P.P.
50 2c 50 06 d0 0f d0 01 d8 02 d8 03 d8 05 d8 06 - P,P.............
d8 07 d8 08 d8 09 d8 0a d8 0b d8 0c d8 0d d8 0e - ................
d8 00 00 00 00 04 00 00 00 01 30 01 38 02 b8 82 - ..........0.8...
b9 14 52 00 69 00 63 00 6f 00 68 00 20 00 43 00 - ..R.i.c.o.h. .C.
6f 00 6d 00 70 00 61 00 6e 00 79 00 2c 00 20 00 - o.m.p.a.n.y.,. .
4c 00 74 00 64 00 2e 00 00 00 0e 52 00 49 00 43 - L.t.d......R.I.C
00 4f 00 48 00 20 00 54 00 48 00 45 00 54 00 41 - .O.H. .T.H.E.T.A
00 20 00 53 00 00 00 06 30 00 31 00 2e 00 34 00 - . .S....0.1...4.
32 00 00 00 09 30 00 30 00 30 00 31 00 30 00 30 - 2....0.0.0.1.0.0
00 39 00 33 00 00 00 00                         - .9.3....
PTP: response OK

Well, at least I know it works. Though, it’s a little daunting that the responses are in hex. I’m going to power through!

Checking Battery Status

0x1015 is GetDevicePropValue. The property battery is 0x5001. The result is a number between 0 and 100. Hmm, 64. That doesn’t correspond to 100%. Ahh, it’s in hex. :-(

64 in hex corresponds to a decimal value of 100. My battery charge is 100%. It’s connected with USB, it’s always 100% :-)

craig@linux-silver:$ ptpcam -R 0x1015,0x5001
Camera: RICOH THETA S
Sending generic request: reqCode=0x1015, params=[0x00005001,0x00000000,0x00000000,0x00000000,0x00000000]
64 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - d...............
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
00 00 00 00 00 00 00 00                         - ........
PTP: response OK

Wow, I can only imagine dealing with the response codes in C. If the connection wasn’t so fast and so stable, it would be too much to put up with.

However, the more I look at this, the hex dump is starting to grow on me.

Getting Size of Still Image

In this example, I look for the property ImageSize which is 0x5003. Just to refresh from the previous example, 0x1015 is GetDevicePropValue. In the right column, you can see that the image size is 5376x2688

craig@linux-silver:$ ptpcam -R 0x1015,0x5003
Camera: RICOH THETA S
Sending generic request: reqCode=0x1015, params=[0x00005003,0x00000000,0x00000000,0x00000000,0x00000000]
0a 35 00 33 00 37 00 36 00 78 00 32 00 36 00 38 - .5.3.7.6.x.2.6.8
00 38 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - .8..............
00 00 00 00 00 00 00 00                         - ........
PTP: response OK

Taking a Picture

I can take a picture, but get a PTP: I/O error. This doesn’t appear to negatively impact taking a picture. I’ll take a look at this in the future. I think it may be caused by the delay that the THETA causes when it processes the image into equirectangular format.

$ ptpcam -R 0x100e
Camera: RICOH THETA S
Sending generic request: reqCode=0x100e, params=[0x00000000,0x00000000,0x00000000,0x00000000,0x00000000]
PTP: I/O error

Get Storage IDs

$ ptpcam -R 0x1004
Camera: RICOH THETA S
Sending generic request: reqCode=0x1004, params=[0x00000000,0x00000000,0x00000000,0x00000000,0x00000000]
01 00 00 00 01 00 01 00 00 00 00 00 00 00 00 00 - ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
00 00 00 00 00 00 00 00                         - ........
PTP: response OK

Wow, this was not pleasant to look at for the first few minutes. Eventually, I realized that the storage ID was reversed.

Focus on the numbers in places 5-8 and reverse the order.

getstorageid
Figure 7. getting the storage IDs in the THETA

Get Object Handles

$ ptpcam -R 0x1007,0x00010001
Camera: RICOH THETA S
Sending generic request: reqCode=0x1007, params=[0x00010001,0x00000000,0x00000000,0x00000000,0x00000000]
15 00 00 00 00 00 00 80 00 00 64 00 6f 02 64 00 - ..........d.o.d.
70 02 64 00 71 02 64 00 72 02 64 00 73 02 64 00 - p.d.q.d.r.d.s.d.
74 02 64 00 75 02 64 00 76 02 64 00 77 02 64 00 - t.d.u.d.v.d.w.d.
79 02 64 00 7a 02 64 00 7b 02 64 00 7c 02 64 00 - y.d.z.d.{.d.|.d.
7d 02 64 00 7e 02 64 00 7f 02 64 00 80 02 64 00 - }.d.~.d...d...d.
81 02 64 00 82 02 64 00                         - ..d...d.
PTP: response OK
$

You’ll need to reverse the order of each file handle to get information on it or download.

Get File Information From Camera

$ ptpcam -R 0x1008,0x00640282
Camera: RICOH THETA S
Sending generic request: reqCode=0x1008, params=[0x00640282,0x00000000,0x00000000,0x00000000,0x00000000]
01 00 01 00 01 38 00 00 76 1f 3c 00 08 38 72 0d - .....8..v.<..8r.
00 00 a0 00 00 00 78 00 00 00 00 15 00 00 80 0a - ......x.........
00 00 08 00 00 00 00 00 64 00 00 00 00 00 00 00 - ........d.......
00 00 00 00 0d 52 00 30 00 30 00 31 00 30 00 36 - .....R.0.0.1.0.6
00 34 00 32 00 2e 00 4a 00 50 00 47 00 00 00 10 - .4.2...J.P.G....
32 00 30 00 31 00 36 00 30 00 37 00 30 00 32 00 - 2.0.1.6.0.7.0.2.
54 00 30 00 37 00 30 00 38 00 33 00 33 00 00 00 - T.0.7.0.8.3.3...
10 32 00 30 00 31 00 36 00 30 00 37 00 30 00 32 - .2.0.1.6.0.7.0.2
00 54 00 30 00 37 00 30 00 38 00 33 00 33 00 00 - .T.0.7.0.8.3.3..
00 01 00 00 00 00 00 00                         - ........
PTP: response OK

Download File as Hexdump For Testing

File contents below have been edited so you can see the beginning and end of the file.

$ ptpcam -R 0x1009,0x00640282 > hexdump
$ less hexdump
Camera: RICOH THETA S
Sending generic request: reqCode=0x1009, params=[0x00640282,0x00000000,0x00000000,0x00000000,0x00000000]
ff d8 ff e1 f1 fe 45 78 69 66 00 00 4d 4d 00 2a - ......Exif..MM.*
00 00 00 08 00 0d 01 0e 00 02 00 00 00 40 00 00 - .............@..
00 aa 01 0f 00 02 00 00 00 16 00 00 00 ea 01 10 - ................
....
....
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - ................
PTP: response OK

It’s possible to write a bit of code to parse the file and convert the hex into JPG format. I decided to leave this one for someone else. I suspect that another library may be a bit more friendly. The good news is that it’s possible to download the image. The source code for ptpcam has an example of how to download the binary file. Or, you can use ptpcam --get-file=FILE_HANDLE within a script to download the image as a JPEG file.

Windows MTP Extensions

To get the API to work over USB on Windows, you just have to issue “raw” MTP/PTP commands to get it to work. For people using Windows Portable Devices API to control the THETA S, keep this in mind:

It looks like you can’t use the WPD command WPD_COMMAND_STILL_IMAGE_CAPTURE_INITIATE to initiate capture. If you use this command the driver returns an error to you.

The correct way to do this is to use WPD_COMMAND_MTP_EXT_EXECUTE_COMMAND_WITHOUT_DATA_PHASE and set up all the parameters and Op Code for image capture and then send that command. Take a look at this example from Microsoft for setting things up. Basically you just need to change the Op Code to 0x100E (for initiate capture) and change the StorageID to 0x0 (as documented in the Theta v2 USB API) and you are ready to go.

This information was originally contributed by Matt Wymore in the RICOH developer forum.

Tip
Refer to Windows MTP Extensions documentation on the Microsoft site

gphoto2 on Linux

gphoto2 partially works without modification. It may be useful for some applications or you can potentially get it to work fully with a bit of effort. As it has some use and can be installed with a simple apt install gphoto2, I will include some information on it. It could also help with troubleshooting.

gphoto2 automatically detected the THETA and correctly identified the BUS and PORT.

$ gphoto2 --auto-detect
Model                          Port
----------------------------------------------------------
USB PTP Class Camera           usb:003,009

It can list files on the THETA.

# gphoto2 -L -f /store_00010001/DCIM/100RICOH
There are 8 files in folder '/store_00010001/DCIM/100RICOH'.
#1     R0010622.JPG               rd  3908 KB 5376x2688 image/jpeg
#2     R0010623.JPG               rd  3862 KB 5376x2688 image/jpeg
#3     R0010624.JPG               rd  3832 KB 5376x2688 image/jpeg

Capture Image and Download also works

$ gphoto2 --capture-image-and-download
New file is in location /store_00010001/DCIM/100RICOH/R0010632.JPG on the camera
Saving file as R0010632.JPG
Deleting file /store_00010001/DCIM/100RICOH/R0010632.JPG on the camera
craig@linux-silver:~/Documents/tmp/img$

In addition to using gphoto2, I also tried the Python bindings for libgphoto2. I only got it to work partially. If you can get it to work fully, let me know.

At this stage, I have libptp working almost completely with only a few glitches with stopping video. If you get it to work fully, send me a note. I’ll take another look at it in the future.

Troubleshooting the USB connection

Could not open session

unmount the camera.

craig@linux-silver:~$ ptpcam -i
Camera information
==================
ERROR: Could not open session!
camera mounted
Figure 8. You may have problems with ptpcam if your camera is mounted

After you unmount the camera, you will see this:

craig@linux-silver:~$ ptpcam -i
Camera information
==================
Model: RICOH THETA S
  manufacturer: Ricoh Company, Ltd.
  serial number: '00010093'
  device version: 01.42
  extension ID: 0x00000006
  extension description: (null)
  extension version: 0x006e

Simple USB Control of Camera with Python

Using the Python subprocess module, you can simply run ptpcam inside of your Python program and control the control without having to deal with the raw PTP codes.

The example below does the following:

  1. takes a picture

  2. gets camera info and manipulates it inside the Python program

  3. gets list of files on camera and snips off the handle for the last image taken

  4. downloads the last image to local disk

    import subprocess
    ## example of taking a picture
    def takePicture():
        subprocess.call("ptpcam -c", shell=True)
    takePicture()
    # example of grabbing device info and using it in your python program.
    ptpinfo = subprocess.Popen(["ptpcam", "--info"], stdout=subprocess.PIPE)
    # although this simply prints to stdout, you can parse
    # the response for your program
    for line in ptpinfo.stdout.readlines():
        print(line.rstrip())
    # find the last picture taken. Modify to parse for date or other
    files = []
    listFiles = subprocess.Popen(["ptpcam", "-L"], stdout=subprocess.PIPE)
    for line in listFiles.stdout.readlines():
        files.append(line.rstrip())
    lastLine = files[len(files) - 2].split(" ")
    lastPicture = lastLine[0][:-1]
    print("The handle for the last picture taken is " + lastPicture)
    # download the picture
    ptpcommand = "ptpcam --get-file=" + lastPicture
    subprocess.call(ptpcommand, shell=True)

I’ve saved the file as pyptp.py

$ python pyptp.py
Camera information
==================
Model: RICOH THETA S
  manufacturer: Ricoh Company, Ltd.
  serial number: '00010093'
  device version: 01.42
  extension ID: 0x00000006
  extension description: (null)
  extension version: 0x006e
The handle for the last picture taken is 0x00640284
Camera: RICOH THETA S
Saving file: "R0010644.JPG" is done.