====== Pentax K-1 WiFi analysis ======
This article documents my findings of analyzing the Pentax K-1 WiFi protocol. The camera has built-in WiFi functionality. An app on a phone (Android and iOS) allows one to remote control the camera and browse images. Here, I want to figure out how it's implemented.
===== Preparation =====
The first step to analyzing the protocol is to be able to intercept the communication between the app and the camera. Therefore, I installed the [[https://play.google.com/store/apps/details?id=com.ricohimaging.imagesync|Image Sync app]] on my phone and extracted the APK from it.
==== Extracting the APK ====
In short, install ADB on a computer, connect the phone with USB debugging enabled and run the following command:
adb shell 'pm list packages -f com.ricohimaging.imagesync'
It should output the path of the apk, as in:
package:/data/app/com.ricohimaging.imagesync-2/base.apk=com.ricohimaging.imagesync
Now, download that file to the current directory:
adb pull /data/app/com.ricohimaging.imagesync-2/base.apk .
See the second half of [[http://android.stackexchange.com/a/75436/34272]] for good instructions.
==== ARC Welder ====
[[https://chrome.google.com/webstore/detail/arc-welder/emfinbmielocnlhgmfkkmkngdoccbadn/|ARC Welder]] is a Chrome app that provides an Android runtime environment inside Chrome. Install that and load the ''base.apk'' downloaded from the phone.
==== Connect computer to WiFi ====
Next, enable WiFi on the camera and connect the computer to that network. Start wireshark to generate a trace of the connection and then start the Ricoh app in Chrome.
===== Protocol =====
The app talks HTTP to the camera. First, it gets general information about the camera's capabilities and then switches to a WebSocket connection to stream requests/responses. Here's an overview of the end points it exports.
==== /v1/props ====
GET /v1/props HTTP/1.1
The first request is sent to ''/v1/props''. It returns a Json file describing the camera:
{"errCode": 200,
"errMsg": "OK",
"resoList": [ "1080x720", "720x480"],
"movieResoList": [ "1280x720", "720x404"],
"WBModeList": [ "auto", "multiAuto", "daylight", "shade", "cloud", "daylightFluorescent", "dayWhiteFluorescent", "coolWhiteFluorescent", "warmWhiteFluorescent", "tungsten", "cte", "manual1", "manual2", "manual3", "colorTemp1", "colorTemp2", "colorTemp3"],
"stillSizeList": [ "L3", "L2", "L1", "M3", "M2", "M1", "S3", "S2", "S1", "XS3", "XS2", "XS1"],
"movieSizeList": [ "FHD30p", "FHD25p", "FHD24p", "HD60p", "HD50p", "FHD60i", "FHD50i"],
"shootModeList": [ "single", "continuousH", "continuousM", "continuousL", "self12s", "self2s", "selfCotinuous", "remocon", "remocon3s", "remoconContinous", "bracket", "bracketSelf", "bracketRemocon", "multiExp", "multiExpContinuous", "multiExpSelf", "multiExpRemocon", "interval", "intervalSelf", "intervalRemocon", "intervalComp", "intervalCompSelf", "intervalCompRemocon", "intervalMovie", "intervalMovieSelf", "intervalMovieRemocon", "starstream", "starstreamSelf", "starstreamRemocon", "mirrorUp", "mirrorUpRemcon"],
"effectList": [ "cim_natural", "cim_bright", "cim_portrait", "cim_landscape", "cim_vibrant", "cim_radiant", "cim_muted", "cim_bleachBypass", "cim_reversal", "cim_monochrome", "cim_crossProcess", "cim_auto", "cim_flat"],
"filterList": ["off", "dfl_extractColor", "dfl_replaceColor", "dfl_toyCamera", "dfl_retro", "dfl_highContrast", "dfl_shading", "dfl_negaPosi", "dfl_solidMonoColor", "dfl_hardMonochrome", "hdr_auto", "hdr_mode1", "hdr_mode2", "hdr_mode3", "hdr_modeA"],
"exposureModeList": [ "P", "SV", "TV", "AV", "TAV", "M", "B", "X", "U1", "U2", "U3", "U4", "U5", "auto", "gps", "movie"],
"avList": [ "4.0", "4.5", "5.0", "5.6", "6.3", "7.1", "8.0", "9.0", "10", "11", "13", "14", "16", "18", "20", "22", "25", "29"],
"tvList": [ "30.1", "25.1", "20.1", "15.1", "13.1", "10.1", "8.1", "6.1", "5.1", "4.1", "3.1", "25.10", "2.1", "16.10", "13.10", "1.1", "8.10", "6.10", "5.10", "4.10", "3.10", "1.4", "1.5", "1.6", "1.8", "1.10", "1.13", "1.15", "1.20", "1.25", "1.30", "1.40", "1.50", "1.60", "1.80", "1.100", "1.125", "1.160", "1.200", "1.250", "1.320", "1.400", "1.500", "1.640", "1.800", "1.1000", "1.1250", "1.1600", "1.2000", "1.2500", "1.3200", "1.4000", "1.5000", "1.6400", "1.8000"],
"svList": ["auto", "100", "200", "400", "800", "1600", "3200", "6400", "12800", "25600", "51200", "102400", "204800"],
"xvList": [ "+5.0", "+4.7", "+4.3", "+4.0", "+3.7", "+3.3", "+3.0", "+2.7", "+2.3", "+2.0", "+1.7", "+1.3", "+1.0", "+0.7", "+0.3", "0.0", "-0.3", "-0.7", "-1.0", "-1.3", "-1.7", "-2.0", "-2.3", "-2.7", "-3.0", "-3.3", "-3.7", "-4.0", "-4.3", "-4.7", "-5.0"],
"exposureModeOption": "",
"state": "idle",
"av": "4.0",
"tv": "4.1",
"sv": "200",
"xv": "0.0",
"WBMode": "auto",
"shootMode": "single",
"exposureMode": "U1",
"stillSize": "L3",
"movieSize": "FHD30p",
"effect": "cim_natural",
"filter": "off",
"timerExposure": false,
"focused": false,
"focusCenters": [],
"focusEffectiveArea": [70,64],
"focusMode": "af",
"model": "PENTAX K-1",
"firmwareVersion": "01.40",
"macAddress": "AC:3F:A4:(hidden)",
"serialNo": "(hidden)",
"channelList": [0,1,2,3,4,5,6,7,8,9,10,11],
"hot": false,
"battery": 100,
"stillFormatList": [ "jpeg", "pef", "rawpef"],
"storages" : [
{"name":"sd1",
"equipped": true,
"writable": true,
"available": true,
"active": true,
"format": "rawpef",
"reservePriority": "high",
"remain": 1177
}
,
{"name":"sd2",
"equipped": false,
"writable": false,
"available": false,
"active": false,
"format": "rawpef",
"reservePriority": "low",
"remain": 0
}
],
"ssid": "PENTAX_(hidden)",
"key": "(hidden)",
"channel": "6",
"slotMode": "reserve",
"liveState": "idle"}
==== /v1/changes ====
GET /v1/changes HTTP/1.1
This command is used to upgrade the connection to websocket.
==== /v1/photos ====
GET /v1/photos?storage=sd1 HTTP/1.1
List the photos on SD1.
{"errCode": 200,
"errMsg": "OK",
"dirs": [
{
"name": "100_1112",
"files": [ "IMGP1774.JPG", "IMGP1774.PEF"]
},
{
"name": "101_1113",
"files": [ "IMGP1775.JPG", "IMGP1775.PEF"]
},
{
"name": "102_1117",
"files": [ "IMGP1856.JPG"]
}
]
}
==== /v1/photos/... ====
GET /v1/photos/102_1117/IMGP1856.JPG?size=thumb&storage=sd1 HTTP/1.1
Reads a specific thumbnail image from the SD card. The camera replies with a HTTP 200 OK and sends a jpeg encoded image.
GET /v1/photos/102_1117/IMGP1857.JPG?size=view&storage=sd1 HTTP/1.1
Reads a larger resolution image from the SD card.
GET /v1/photos/102_1117/IMGP1857.JPG/info?storage=sd1 HTTP/1.1
Get meta information on an image.
{"errCode": 200,
"errMsg": "OK",
"captured": true,
"dir": "102_1117",
"file": "IMGP1857.JPG",
"av": "4.0",
"sv": "200",
"xv": "0",
"tv": "1.8",
"orientation": 1,
"cameraModel": "PENTAX K-1",
"latlng": "47.000000,8.000000",
"datetime": "2016-11-17T23:00:00"}
==== /v1/liveview ====
GET /v1/liveview HTTP/1.1
Seems to initiate live viewing. It didn't really work in the app.
==== /v1//lens/focus ====
POST /v1/lens/focus HTTP/1.1
Parameters are ''pos=52,52''. I think they refer to where the focus point is set. The camera sends the following reply:
{"errCode": 200,
"errMsg": "OK",
"focused": true,
"focusCenters": [],
"focusEffectiveArea": [70,64]}
==== /v1/camera/shoot ====
POST /v1/camera/shoot HTTP/1.1
Parameters are ''af=off''. It shoots a picture.
{"errCode": 200,
"errMsg": "OK",
"focused": true,
"focusCenters": [],
"focusEffectiveArea": [70,64],
"captured": false}
==== /v1/params/camera ====
PUT /v1/params/camera HTTP/1.1
Sets various camera paramters. Here are some examples:
sv=auto
xv=0.0
Without form encoded data, it replies:
{"errCode": 200,
"errMsg": "OK",
"avList": [ "4.0", "4.5", "5.0", "5.6", "6.3", "7.1", "8.0", "9.0", "10", "11", "13", "14", "16", "18", "20", "22", "25", "29"],
"tvList": [ "30.1", "25.1", "20.1", "15.1", "13.1", "10.1", "8.1", "6.1", "5.1", "4.1", "3.1", "25.10", "2.1", "16.10", "13.10", "1.1", "8.10", "6.10", "5.10", "4.10", "3.10", "1.4", "1.5", "1.6", "1.8", "1.10", "1.13", "1.15", "1.20", "1.25", "1.30", "1.40", "1.50", "1.60", "1.80", "1.100", "1.125", "1.160", "1.200", "1.250", "1.320", "1.400", "1.500", "1.640", "1.800", "1.1000", "1.1250", "1.1600", "1.2000", "1.2500", "1.3200", "1.4000", "1.5000", "1.6400", "1.8000"],
"svList": ["auto", "100", "200", "400", "800", "1600", "3200", "6400", "12800", "25600", "51200", "102400", "204800"],
"xvList": [ "+5.0", "+4.7", "+4.3", "+4.0", "+3.7", "+3.3", "+3.0", "+2.7", "+2.3", "+2.0", "+1.7", "+1.3", "+1.0", "+0.7", "+0.3", "0.0", "-0.3", "-0.7", "-1.0", "-1.3", "-1.7", "-2.0", "-2.3", "-2.7", "-3.0", "-3.3", "-3.7", "-4.0", "-4.3", "-4.7", "-5.0"],
"exposureModeOption": "",
"state": "idle",
"av": "4.0",
"tv": "1.40",
"sv": "800",
"xv": "0.0",
"WBMode": "auto",
"shootMode": "single",
"exposureMode": "U1",
"stillSize": "L3",
"movieSize": "FHD30p",
"effect": "cim_natural",
"filter": "off",
"timerExposure": false}
==== /v1/photos/latest/... ====
GET /v1/photos/latest/info?storage=sd1 HTTP/1.1
Gets information about the latest picture. Camera replies
{"errCode": 200,
"errMsg": "OK",
"captured": true,
"dir": "102_1117",
"file": "IMGP1857.JPG",
"av": "4.0",
"sv": "200",
"xv": "0",
"tv": "1.8",
"orientation": 1,
"cameraModel": "PENTAX K-1",
"latlng": "47.000000,8.000000",
"datetime": "2016-11-17T23:20:17"}