Saltar al contenido

Control Remoto WiFi para Cámara Sony

  • PROYECTO

Desde hace unos años tengo una cámara compacta Sony WX350, en general la uso durante los viajes y cuando la cámara del teléfono no es suficiente. Una de las características que más me gustan de la cámara es que al tener WiFi integrado, puedo utilizar la aplicación Imaging Edge Mobile y controlar los disparos de la cámara desde el teléfono; ideal para las fotos en grupo o con trípode. Sin embargo, entre las funciones de la cámara no están los timelapse, buscando agregar esta función a la cámara encontré la aplicación Timelapse – Sony Camera que usa la Sony Remote API para enviar los comandos de disparo; y aunque esto requiere dejar de usar el teléfono durante la captura del timelapse, es una buena solución. Pero para no tener que usar el teléfono implementé la API en un microcontrolador ESP8266, esto debería ser suficiente para tomar buenos timelapses.

De acuerdo con la documentación de la API, se deben enviar los comandos en formato JSON a la URL de la cámara usando peticiones HTTP. La API tiene dos funciones principales, una es la captura de imágenes o vídeo y la otra es la transferencia de imágenes, la función para captura pertenece al servicio camera y la función de transferencia al servicio avContent; estos servicios son mutuamente excluyentes, en este caso nos interesa la función de captura.

La estructura de las funciones de la API contiene dos partes principales, la forma en la que se envía la petición a la cámara y la forma en la que se recibe la respuesta de la cámara. Para enviar la petición se usa un método, este tiene un nombre definido que se puede hallar en la documentación de la API, a este método le sigue un parámetro cuyo valor depende directamente del método; finalmente se envía un id y un número de versión. El siguiente ejemplo es el comando para cambiar el modo de captura a película o grabación vídeo.

{
  "method": "setShootMode",
  "params": ["movie"],
  "id": 1,
  "version": "1.0"
}

La estructura de los mensajes de respuesta de la cámara sólo tiene dos campos, el resultado y el id. El campo más importante es el resultado, si la petición es exitosa este campo tendrá un valor de 0 o el correspondiente dato solicitado, en caso contrario aparece el campo error junto con un código para identificar la falla. El siguiente mensaje es la respuesta a la petición de cambio de modo de captura, y muestra que la cámara ha realizado el cambio correctamente.

{
  "result": [0],
  "id": 1
}

Luego de tener idea sobre cómo trabaja la API, el siguiente paso era implementar algunas de las funciones de esta en el ESP8266 para controlar la cámara. Usando la información de la API y algunos ejemplos en Arduino fue posible escribir un programa que se conecta a la cámara y envía los comandos para hacer el timelapse.

El programa crea un Access Point y presenta una página web desde la que se puede configurar el SSID y la contraseña del Access Point creado por la cámara; también se puede configurar la duración del timelapse y el tiempo entre disparos. Una vez que los parámetros se almacenan, el microcontrolador trata de conectarse al Access Point creado por la cámara, luego de esto espera por un pulso de confirmación (pulso bajo en el GPIO0) para iniciar la captura del timelapse.

El sketch usa las librerías ESP8266WiFi, ESP8266WebServer y ESP8266HTTPClient para crear el servidor de configuración, habilitar y manejar la conexión a la cámara, y publicar las peticiones HTTP respectivamente.

const char* ssid_ap = "SonyRemoteAP";
const char* password_ap = "sony1234";
ESP8266WebServer server(80); // Create WebServer on port 80
WiFiClient  client; // Create a client to access the camera
String ssid = {};
String password = {};

Luego se definen el nombre de la red y la contraseña para el Access Point creado por el ESP8266, además se crean el servidor web y el cliente WiFi, las variables ssid y password se declaran, pero quedan vacías, pues sus valores se escribirán desde la página web.

const int start_button = 0;         // GPIO0 to start capture
const String host = "10.0.0.1";     // Camera IP, defined on API documentation
const String http_port = "10000";   // Camera port, defined on API documentation
const String url = "/sony/camera";  // URL to send htto request, defined on API documentation
int timelapse_span;                 // Timelapse span
int shot_period;                    // Time between shots (in seconds)
int shot_period_ms;                 // Time between shots (in milliseconds)
int picture_number = 0;             // Max number of pictures on the timelapse
int op_mode = 0;                    // Operation mode

Las siguientes variables que se definen son el botón para iniciar la captura, el host, puerto y URL para publicar las peticiones HTTP, estos valores pueden cambiar dependiendo del modelo de la cámara Sony y se pueden verificar en la documentación de la API. Las demás variables definen la duración del timelapse y la cantidad de fotos para capturar.

char setShootMode[] = "{\"method\":\"setShootMode\",\"params\":[\"still\"],\"id\":1,\"version\":\"1.0\"}"; // Set shot mode to "still"
char actTakePicture[] = "{\"method\":\"actTakePicture\",\"params\":[],\"id\":1,\"version\":\"1.0\"}"; // Take a picture
char setFocusMode[] = "{\"method\":\"setFocusMode\",\"params\":[\"AF-S\"],\"id\":1,\"version\":\"1.0\"}"; // Set focus mode to Single AF
char setAutoPowerOff[] = "{\"method\":\"setAutoPowerOff\",\"params\":[{\"autoPowerOff\":60}],\"id\":1,\"version\":\"1.0\"}"; // Set automatic power off after 60 seconds

Estas cuatro cadenas de texto son las funciones de la API usadas en el proyecto y tienen el formato JSON definido en la documentación. Arduino tiene una librería para codificar y descodificar JSON, pero en este caso no es necesario usarla ya que los parámetros de las funciones no se modifican.

const char index_html[] PROGMEM = R"rawliteral(<!DOCTYPE html>...</html>)rawliteral";
const char save_html[] PROGMEM = R"rawliteral(<!DOCTYPE html>...</html>)rawliteral";

En estas dos constantes se almacena el código HTML de las dos páginas del servidor web, el código es bastante extenso, pero está completo en GitHub. En la primera página se configura el SSID y la contraseña del Access Point de la cámara, la duración del timelapse y la cantidad de fotos; la segunda página es la confirmación de que los parámetros han sido almacenados.

void setup() {
  WiFi.mode(WIFI_AP_STA);
  WiFi.softAP(ssid_ap);
  server.on("/", HTTP_GET, handleRoot);
  server.on("/save", HTTP_POST, handleSave);
  server.begin();
  pinMode(start_button, INPUT_PULLUP);
}

En la función de configuración se inician el Access Point y el servidor web, además se configura el botón de inicio de captura como entrada. La dirección por defecto del Access Point es 192.168.4.1.

void loop()
{
  switch (op_mode)
  {
    case 0:
      while (picture_number == 0) {
        server.handleClient();
      }
      op_mode = 1;
      Serial.println();
      break;
    case 1:
      WiFi.begin(ssid, password);
      while (WiFi.status() != WL_CONNECTED) {
        delay(500);
      }
      httpPost(setShootMode);
      op_mode = 2;
      break;
    case 2:
      while (digitalRead(start_button) != 0) {
        delay(100);
      }
      op_mode = 3;
    case 3:
      httpPost(setFocusMode);
      for (int i = 0; i < picture_number; i++) {
        httpPost(actTakePicture);
        delay(shoot_period_ms);
      }
      op_mode = 4;
      httpPost(setAutoPowerOff);
      break;
    case 4:
      delay(100);
      break;
  }
}

En el loop se configura un switch case con la variable op_mode, para alternar el modo de operación entre el servidor web, la conexión al Access Point de la cámara, la espera por la pulsación del botón para iniciar la captura y la captura del timelapse.

La función server.handleClient() permite la conexión de un computador o teléfono al Access Point y muestra la página web, cuando se almacenan los parámetros para el timelapse, esta función se detiene.

void httpPost(char* j_request);
void handleRoot();
void handleSave();

La función httpPost(char* j_request) se encarga publicar las peticiones HTTP, el argumento de esta función son las funciones de la API en formato JSON. La función handleRoot() es la que publica la página web inicial, y la función handleSave() es la que captura los parámetros escritos en los campos de texto y muestra la segunda página donde confirma que se almacenaron los datos.

Para esta aplicación usé el módulo ESP-01 con la ESP8266 Breakout Board para facilitar la programación y alimentación del ESP8266. El código completo y con comentarios está disponible en GitHub.

GitHub: SonyRemoteTimelapse

Más información:
Sony Camera Remote API
WX350 Compact Camera
Arduino core for ESP8266
A Beginner’s Guide to the ESP8266
Update ESP-01 Firmware

Si tienes alguna inquietud no dudes en comentar.

Deja un comentario