Sony camera controller using ESP8266

This post is just documenting the work I’ve done with the Sony AS20 and ESP8266 to assist others doing something similar.

The same SSDP discovery + JSON-RPC over HTTP POST is used by all sorts of things for WIFI control now, so this could well be applicable to all sorts of control (and hence is part of the universal remote project.)

The Sony camera control API is available here:
https://developer.sony.com/2013/11/29/how-to-develop-an-app-using-the-camera-remote-api-2/

However it’s aimed at phone app developers, giving Android and iOS examples which aren’t really applicable to ESP. Too much memory required and over complicated.

The key to controlling these things is finding the endpoint that you need to send the HTTP requests to. For a PC or phone app this is done dynamically using SSDP, however the ESP doesn’t have enough memory to process the xml strings for this so it needs to be done separately.

Using an example from here

I connected the camera wifi to my PC and traded M-SEARCH/NOTIFY packets to get the description http address. The camera’s notify response was:

UDP-Socket setup done...

M-Search sent...

HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1800
EXT:
LOCATION: http://192.168.122.1:64321/DmsRmtDesc.xml
SERVER: UPnP/1.0 SonyImagingDevice/1.0
ST: upnp:rootdevice
USN: uuid:00000000-0005-0010-8000-fcdbb316da7e::upnp:rootdevice
X-AV-Physical-Unit-Info: pa=""; pl=;
X-AV-Server-Info: av=5.0; hn=""; cn="Sony Corporation"; mn="SonyImagingDevice"; mv="1.0";


HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1800
EXT:
LOCATION: http://192.168.122.1:64321/DmsRmtDesc.xml
SERVER: UPnP/1.0 SonyImagingDevice/1.0
ST: uuid:00000000-0005-0010-8000-fcdbb316da7e
USN: uuid:00000000-0005-0010-8000-fcdbb316da7e
X-AV-Physical-Unit-Info: pa=""; pl=;
X-AV-Server-Info: av=5.0; hn=""; cn="Sony Corporation"; mn="SonyImagingDevice"; mv="1.0";


HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1800
EXT:
LOCATION: http://192.168.122.1:64321/DmsRmtDesc.xml
SERVER: UPnP/1.0 SonyImagingDevice/1.0
ST: urn:schemas-upnp-org:device:MediaServer:1
USN: uuid:00000000-0005-0010-8000-fcdbb316da7e::urn:schemas-upnp-org:device:MediaServer:1
X-AV-Physical-Unit-Info: pa=""; pl=;
X-AV-Server-Info: av=5.0; hn=""; cn="Sony Corporation"; mn="SonyImagingDevice"; mv="1.0";


HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1800
EXT:
LOCATION: http://192.168.122.1:64321/DmsRmtDesc.xml
SERVER: UPnP/1.0 SonyImagingDevice/1.0
ST: urn:schemas-upnp-org:service:ContentDirectory:1
USN: uuid:00000000-0005-0010-8000-fcdbb316da7e::urn:schemas-upnp-org:service:ContentDirectory:1
X-AV-Physical-Unit-Info: pa=""; pl=;
X-AV-Server-Info: av=5.0; hn=""; cn="Sony Corporation"; mn="SonyImagingDevice"; mv="1.0";


HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1800
EXT:
LOCATION: http://192.168.122.1:64321/DmsRmtDesc.xml
SERVER: UPnP/1.0 SonyImagingDevice/1.0
ST: urn:schemas-upnp-org:service:ConnectionManager:1
USN: uuid:00000000-0005-0010-8000-fcdbb316da7e::urn:schemas-upnp-org:service:ConnectionManager:1
X-AV-Physical-Unit-Info: pa=""; pl=;
X-AV-Server-Info: av=5.0; hn=""; cn="Sony Corporation"; mn="SonyImagingDevice"; mv="1.0";


HTTP/1.1 200 OK
CACHE-CONTROL: max-age=1800
EXT:
LOCATION: http://192.168.122.1:64321/DmsRmtDesc.xml
SERVER: UPnP/1.0 SonyImagingDevice/1.0
ST: urn:schemas-sony-com:service:ScalarWebAPI:1
USN: uuid:00000000-0005-0010-8000-fcdbb316da7e::urn:schemas-sony-com:service:ScalarWebAPI:1
X-AV-Physical-Unit-Info: pa=""; pl=;
X-AV-Server-Info: av=5.0; hn=""; cn="Sony Corporation"; mn="SonyImagingDevice"; mv="1.0";

The important piece out of this is the location line

LOCATION: http://192.168.122.1:64321/DmsRmtDesc.xml

which with the camera connected you can just put into the web browser and retrieve the xml description.

Again this is a rather long drawn out file designed for PC processing, but there’s one important line

<av:X_ScalarWebAPI_ActionList_URL>http://192.168.122.1:10000/sony</av:X_ScalarWebAPI_ActionList_URL>

This address is the EndPoint we can use to send control commands to. So once we have this, dynamic resolution by SSDP is no longer necessary for these practically static devices.

Now to formualing HTTP POST requests. The API doco gives the commands in the JSON-RPC format:

{
“method”: “actTakePicture”,
“params”: ,
“id”: 1,
“version”: “1.0”
}

So all that’s left to do is wrap that in a HTTP POST header, and send to the endpoint found above using TCP.

The using the command above, the complete HTTP POST packet becomes:

POST /sony/camera HTTP/1.1
Content-Type: text/json
Host: 192.168.122.1:10000
Content-Length: 68
Connection: Keep-Alive

{“method”: “actTakePicture”, “params”: ,“id”: 1,“version”: “1.0” }

Noting you can actually try this out by connecting the camera wifi to a PC, connecting to the endpoint using putty in RAW mode, then copy and pasting the above in.

Back to the ESP, an example function to take a still picture becomes:

function takePicture ()

    POST_CMD = 'POST /sony/camera HTTP/1.1\r\nContent-Type: text/json\r\nHost: 192.168.122.1:10000\r\nContent-Length: 68\r\nConnection: Keep-Alive\r\n\r\n{"method": "actTakePicture", "params": [],"id": 1,"version": "1.0" }\r\n'
        
        conn=net.createConnection(net.TCP, 0)
        conn:on("receive", function(conn, payload) 
                print(payload) 
            end )
                
        conn:connect(10000,"192.168.122.1")
        conn:send(POST_CMD)
    end

Now a question for the ESP gurus @clixx_io @eyal

Each of the camera’s hosts it’s own wifi AP with unique SSID and password. In order to start/stop/pause a bunch of these camera’s (could be mounted all over a motorcycle for example.) The ESP needs to sequence through the networks in a fashion:

  • connect to network,
  • issue the command,
  • wait for a response,
  • disconnect from network
  • repeat for remaining networks.

However using the following code I only ever get a status of 4, connection fail. No amount of waiting or calling the connect function has got it to reconnect. If I issue a reset it connects immediately and I get a status code of 5.

Do you have any sample code for disconnecting and reconnecting to a different network?

wifi.sta.disconnect()
wifi.setmode(wifi.STATION)
wifi.sta.setip({ip="192.168.122.100",netmask="255.255.0.0",gateway="192.168.122.1"})
wifi.sta.config("SSID","PWD")
wifi.sta.connect()

Try using a static array of ssid’s, and a tmr.alarm(…)

btw, it connects asynchronously. The connection is not immediate.

thanks for the documenting, have a sony AS100 so this could be useful for me in the future.

I have a solution working now albeit the codes a mess.

Turns out it was executing the wifi.sta.config(xx,xx) function too quickly after wifi.sta.disconnect. Doing this causes the wifi module to hang requiring a reset.

So it all starts to be a bit of a mess with the call back system of nodemcu. You have to call disconnect, then poll the wifi.sta.status for it to return to 0, then call config, poll for the status to change to 5 and you’re good to go.

I need to write a bit of a statemachine framework for this. Even in this very small program there’s 6 places it’s polling for some sort of wifi response before it moves on to the next stage.

No worries, AS100 is exactly the same API.

True, the lua non-preemptive style is verbose. Some people use what is called “waterfall” where each task triggers the next. I just accept that the situation is painful and live with it.

The new nodemcu (with SDK 1.5.0 in dev) has an event system that could make some of this cleaner, but I did not yet use it so cannot make an informed comment.

BTW, waiting for a status forever is unsafe, you will need to set limits on the tries and deal with intermittent failed connections anyway. Even more spaghetti code.

I bet you are now thinking “hardware is so much easier”, right?

cheers

If you ever think that about hardware, it’s because you haven’t done anything new lately, there’s always mind boggling challenges (and the same could be said for software too.) :smile:

I will happily say I just don’t have a pattern in my head for how to structure an application for nodemcu. As you said, it’s not preemptive, but it’s also nothing like the standard cooperative state machine patterns typically used in embedded systems because fundamentally you don’t have access to the main task loop in nodemcu. There’s some fairly horrible hacks to make it fit this sort of pattern, but it’s nothing like clean coding.

I kind of feel like the current nodemcu API (and task scheduler) is missing some critical parts to making clean applications possible. Hopefully the new one is better?

Regardless I’m going to have a crack at using the Espressif SDK in C and see if I can put a toolchain together.

There was a Hackaday article recently on controlling a Canon E3 camera but it was for a Canon not a Sony. I wonder what would need to be changed.