====== 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"}