TCP/IP server for libgphoto2 — DIY DSLR Remote controller
From my previous blogs, we saw how PTP can work with cameras, and mobile devices can control the entire camera easily over the WIFI.
With a proxy server like ddServer, there is very less control we get over features customizations. The main reason is, ddServer is just a proxy and it passes command as it is from the APP to camera in PTP packets format.
What we need here is the actual code that will help build our PTP packets for different commands.
To make our life simpler and to let us focus on the business logic, there are some nice people who worked extensively on such libraries and shared them as open source.
Here I am talking about one of the famous library gphoto2.
This library is so popular nowadays that most of the RPi operating system images come with gphoto2 pre-installed.
In this article, we will write a wrapper over this gphoto2 library to remotely interact with your camera over TCP/IP
The main problem with libgphoto2 (Internal library of the gphoto2 wrapper) was to access it remotely and develop a remote control APP for any DSLR camera. It works well with gphoto2 (user interface for the libgphoto2) but the problem to solve was that accessing those commands over the wire remotely.
With TCP server implementation, it can be done easily from any client implementation like Java, Android App, IOS App, or even from other CPP client code.
I am assuming that you have prior knowledge of Cpp implementation of TCP/IP Client-server programming basics and understands some other language like Java/Python.
Tools and libraries you need:
- gphoto2pp : https://github.com/maldworth/gphoto2pp
- libgphoto2 : https://github.com/gphoto/libgphoto2
- IOTShifu’s TCP Server: https://github.com/IOTShifu/TCP-Server-for-libgphoto2
Quick Introduction:
libgphoto2 is a library that can be used by applications to access various digital cameras.
gphoto2pp is a C++ wrapper for libgphoto2.
This TCP server can become a part of gphot2pp or can be used independently with gphot2pp libraries dynamic linking (For that, you need to modify this code a bit)
Setup:
- Select a device or a machine where you want to run libgphoto2 and planning to connect your camera over USB port e.g. Arduino, Rpi, Windows, or Unix laptop.
- Build and Install libgphoto2 from https://github.com/gphoto/libgphoto2
- Get the code to your local machine from (DO NOT BUILD AND INSTALL YET) https://github.com/maldworth/gphoto2pp
- Get the code from https://github.com/IOTShifu/TCP-Server-for-libgphoto2 and add this to the gphoto2pp’s code you just downloaded in the above step
- Now build “gphoto2pp”
- Run the binary
At this stage, your TCP server is up and running. You can check your system logs for the messages like “ShifuServer starting…”, “Starting TCP socket server on port 5555” etc.
Let’s go deep:
If you see this code, there are 2 major code files,
ShifuServer.cpp
The main entry for this server taking care of all 3 servers over different sockets (Port numbers are 5555, 4444, 6666)
- Port 5555 — Command server (communicator) This server receives all the commands listed in commands.hpp
- Port 4444 — Live streaming server This server creates a channel for the live streaming over TCP with the client, this server will be started only when the Command server receives START_LIVE_VIEW command from the client. On STOP_LIVE_VIEW command from the client, this server will stop and exit.
- Port 6666 — Captured Image downloader This server will be up only on the DOWNLOAD_IMAGE command received by the command server from the client. Typically after CAPTURE_IMAGE command and some delay as per the camera body and settings etc.
ShifuCommunicator.cpp :
This class isolates the TCP communication from the business logic, so the user who is not much comfortable with the TCP and network programming can focus on the business logic of handling DSLR using this class.
The important thing to deal with is ptpPacket struct :
struct PtpPacket{
uint32_t packet_len;
uint16_t packet_type;
uint16_t packet_command;
uint32_t session_ID;
};
All the commands received by the command server will be in this format. The client has to build this struct (Don't worry, languages like Java, C# has the option to form a Byte stream)
e.g. JAVA Code to set this packet on the client-side:
private static byte[] setCommunicationPacket(short packet_command,
short widget_type,
String widget_id,
String widget_value)
{
int size = 2; //Size of Packet Command
//Calcuate Size requried for the packet to send, Min 2 is required to send command
if(widget_type != Constants.EMPTY) size+=2;
if(widget_id != null ){
size+=50;
if(widget_value != null) { //if no widget id provided then there will be no widget value for sure, but only widgegt id can be provided and hence this conditon is inside first one
size+=50;
}
}
ByteBuffer c = ByteBuffer.allocate(size); //We got our size
c.putShort(0,(short)packet_command); //Sure that this value will always be there
if(widget_type != Constants.EMPTY)
c.putShort(2,(short)widget_type);
if(widget_id != null ) {
c.position(4); //Fixed size 50 ( 4 to 53)
byte[] byteArrW_id = new byte[50];
byteArrW_id = widget_id.getBytes();
c.put(byteArrW_id);
if(widget_value != null) {
c.position(54); //Fixed size 50 (54 to 104)
byte[] byteArrW_val = new byte[50];
byteArrW_val = widget_value.getBytes();
c.put(byteArrW_val);
}
}
byte[] returnVal = c.array();
return returnVal;
}
One more example of how client code in JAVA can be written to fire a basic command i.e. Get Connected Camera List:
public static final short GET_CAMERAS_LIST = 0x0902;
public String GetCameraList()
{
String strCamName="No Camera Detected!"; //In case of failure
byte[] cameraListRequestBytes = setCommunicationPacket(Constants.GET_CAMERAS_LIST,
Constants.EMPTY,
null,
null);
try {
//You need to implement this function to send byte stream to the server writeToServerSocket(cameraListRequestBytes);
}catch(Exception e){
return strCamName;
}
///Read response from the server on above command
byte[] response;
try {
response = readBytes(200); //Cam name max len is 50 X we would get max 4 cameras connected: hence 200
}catch(Exception e){
return strCamName;
}
strCamName = new String(response);
return strCamName.trim();
}
Or the most exciting one, i.e. Capture Image:
public static final short CAPTURE_IMAGE = 0x0002;
public void CaptureImage() {
byte[] captureRequestBytes = setCommunicationPacket(Constants.CAPTURE_IMAGE,
Constants.EMPTY,
null,
null);
try {
writeToServerSocket(captureRequestBytes);
} catch (Exception e) {
}
}
Hope this will help you understanding TCP/IP client-server code utilization to communicate to the camera.
It can be used to build a complete remote controller APP, It is just a matter of understanding commands of libgphoto2 and implementing those on the user interface.
Share your RnD with all of us!
Thanks & Regards
Akshay
akshay.r.deshmukh@gmail.com