用 Arduino 自制 GoPro 无线控制器


MAKER:randofo/译:趣无尽 Cherry(转载请注明出处)
在本项目中,我将向你展示如何使用 Arduino 板控制GoPro Hero 4和GoPro Hero Session 5。掌握这些技能后,就可以轻松用于任何型号的GoPro,这将非常实用。你可以通过Arduino直接使用一些基本的网络命令与其进行通信,而不是侵入现有的控制器。你不仅可以控制GoPro上的功能,还可以接收有关相机状态的更新。这将成为不同类型项目的通用解决方案,包括自定义机器人和无人机。

材料清单

1、GoPro Hero 4和GoPro Hero Session 5(也适用于最新型号GoPro Hero 6。Hero Session模型与其它型号的控制略有不同,但我们将在稍后介绍。)
2、Arduino MKR1000或Arduino MKR ZeroX1 (你也可以使用另一种兼容Arduino的无线微控制器板,例如:Feather,但这款我没有进行个人测试。)
3、与Arduino相连的USB连接线X1
4、一台运行Arduino开发软件的计算机X1
5、安装GoPro应用程序的智能手机X1

如果你没有这些东西,或者之前从未使用过Arduino,建议你在开始这个项目之前,先阅读Arduino的基础教程

设置相机的Wifi



打开相机并配置好相机的wifi连接。

型号GoPro Hero4,最简单的wifi连接方法是按照应用程序中的说明将相机连接到手机。手机与相机配对后,使用应用程序打开相机设置,并为wifi网络设置新名称和密码。同样方法适用于GoPro Hero Session 5。

要在相机上查看wifi网络名称,查看“连接设置”菜单下的“相机信息”。

找到你的GoPro的MAC地址

启用wifi后,你需要确定相机的MAC(media access control,媒体访问地址)。在处理传统的Hero系列GoPros时,这个地址是提供信息一个很好的途径。如果你使用的是GoPro Hero Session,这个地址是必备的。

首先使用上一步中的网络名称和密码将计算机连接到相机的wifi网络。

其次,登录相机的wifi网络后,打开计算机上的任何浏览器,然后访问以下网址:
http://10.5.5.9/gp/gpControl/info

此网址应在你的Web浏览器中显示一串信息,如下所示:

{"model_number"21,"model_name":"HERO5Session","firmware_version":"HD5.03.02.51.00","serial_number":"C3212365684485","board_type":"0x05","ap_mac":"0641631510c4","ap_ssid":"GP54688615","ap_has_default_credentials":"0","capabilities":"16"}

如果没有,请确保你的相机打开,并检查wifi连接。

字符中我们感兴趣的是“ap_mac:”后面的数字字符。这个12位数字串是MAC地址,即:0641631510c4。

确定地址后,每隔两个字符将其分开,并按如下格式进行格式化:
0x06, 0x41, 0x63, 0x15, 0x10, 0xC4

将Arduino连接到GoPro Hero


为了让Arduino与GoPro Hero系列相机相连,需要使用识别码进行配对。要获取识别码,请在手机菜单中导航将相机与应用程序配对。所有后期型号相机上将生成4位数的识别码(Hero 3及更早版本是6位数的识别码)。

识别码只持续3分钟,所以时间至关重要。

你需要在下面的代码中手动输入识别码两次:

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//ENTER YOUR PIN HERE WHERE IT SAYS XXXX
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

输入识别码后,你只需不到三分钟的时间将其上传到Arduino并与相机建立连接。

注意,不要忘记输入GoPro的wifi凭证,如下:

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//ENTER YOUR WIFI NAME AND PASSWORD HERE
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

所有的操作都是以成功上传代码为目的,一旦成功,不会发生任何事情。即便如此,你可以进行以下操作。如果你从GoPro的设置菜单导航回视频屏幕,你现在应该能够发送命令(我们将在稍后介绍)。

如果在接下来的步骤中你无法向相机发送命令,请返回并重复此步骤。
使用识别码码将GoPro与Arduino配对的代码:

#include <SPI.h>
#include <WiFi101.h>
#include <WiFiUdp.h>



int status = WL_IDLE_STATUS;

  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  //ENTER YOUR WIFI NAME AND PASSWORD HERE
  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 

char ssid[] = "XXXXXXXXXXXX"; //  your network SSID (name)
char pass[] = "XXXXXXXXXXXX";    // your network password (use for WPA, or use as key for WEP)

WiFiClient client;

const char* host = "10.5.5.9";
const int httpPort = 80;


void setup(){  

  //Initialize serial and wait for port to open:
  Serial.begin(115200);


  // check for the presence of the wifi module:
  if (WiFi.status() == WL_NO_SHIELD) {
     Serial.println("WiFi not present");
     // don't continue:
     while (true);
  }

  // attempt to connect to Wifi network:
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
    status = WiFi.begin(ssid, pass);

    // wait 8 seconds for connection:
    delay(8000);
  }

  Serial.println("Connected to wifi");
  printWifiStatus();


  // START PIN
  StartPin();

  delay(10000);

  // FINISH PIN
  FinishPin();


}


void loop(){

  //Nothing to do here!
  delay(1000);

}


void StartPin(){
  Serial.print("connecting to ");
  Serial.println(host);

  if (!client.connect("10.5.5.9", httpPort)) {
    Serial.println("connection failed");
    return;
  }

  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  //ENTER YOUR PIN HERE WHERE IT SAYS XXXX
  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  
  
  String StartUrl = "/gpPair?c=start&pin;=XXXX&mode;=0";
  Serial.print("Requesting URL: ");
  Serial.println(StartUrl);
  client.print(String("GET ") + StartUrl + " HTTP/1.1\r\n" +
  "Host: " + host + "\r\n" +
  "Connection: close\r\n\r\n");
  Serial.println("Started");
}



void FinishPin(){
  Serial.print("connecting to ");
  Serial.println(host);
  
  if (!client.connect("10.5.5.9", httpPort)) {
    Serial.println("connection failed");
    return;
  }

  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  //ENTER YOUR PIN HERE WHERE IT SAYS XXXX
  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  
  String StopUrl = "10.5.5.9/gpPair?c=finish&pin;=XXXX&mode;=0";
  Serial.print("Requesting URL: ");
  Serial.println(StopUrl);
  client.print(String("GET ") + StopUrl + " HTTP/1.1\r\n" +
  "Host: " + host + "\r\n" +
  "Connection: close\r\n\r\n");
  Serial.println("Finished");
}



void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

将Arduino连接到GoPro Hero Session


GoPro Session没有可供使用的识别码。它的连接方式有些麻烦。正如你注意到的那样,只要你完成录制,或者只要你完成对相机的大部分操作,它就会关闭并进入睡眠模式。

在你发送任何命令到GoPro Hero之前,你需要将其唤醒。最简单的方法是手动按下摄像机背面的菜单按钮,并在它醒来的几秒窗口内发送命令。然而,令人厌烦的是,这个方法并不是特别实用。

唤醒GoPro的更好方法是使用WOL数据包或“魔术数据包”。这个首字母缩写词代表Wake-on-Lan,是一种远程唤醒计算机从睡眠模式的协议。它需要使用UDP协议,从Arduino发送字节到GoPro以唤醒它。有点让人抓狂的是,它与你发送所有其他控制命令的协议不同。如果你不熟悉编程,代码也不那么棒,处理起来有点复杂。

然而,当它起作用时,它真的像魔术一样神奇。通过向Arduino发送命令,看着我的相机醒来,这种感叹从未停止过!

WOL命令(代码中的相机启动()功能)需要在大多数其他命令之前发送,并且通常会延迟1-2秒。基本上,在你需要唤醒相机时(大多数时间)发送它。发送命令后,暂停片刻才能让相机实际唤醒。

请看下面示例,WOL功能设置中调试使用,因此它只能在你第一次运行时唤醒相机。

不要忘记在提示输入代码的地方输入GoPro的wifi凭证和MAC地址!

以下是唤醒相机的代码:

#include <SPI.h>
#include <WiFi101.h>
#include <WiFiUdp.h>



int status = WL_IDLE_STATUS;

  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  //ENTER YOUR WIFI NAME AND PASSWORD HERE
  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 

char ssid[] = "XXXXXXXXXXXX"; //  your network SSID (name)
char pass[] = "XXXXXXXXXXXX";    // your network password (use for WPA, or use as key for WEP)

int localPort = 7;
byte broadCastIp[] = { 10,5,5,9 };

  //!!!!!!!!!!!!!!!!!!!!!!!!!!!
  //ENTER YOUR MAC ADDRESS HERE
  //!!!!!!!!!!!!!!!!!!!!!!!!!!! 

byte remote_MAC_ADD[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
int wolPort = 9;


WiFiUDP Udp;

WiFiClient client;

const char* host = "10.5.5.9";
const int httpPort = 80;


void setup(){  

  //Initialize serial and wait for port to open:
  Serial.begin(115200);

  // check for the presence of the wifi module:
  if (WiFi.status() == WL_NO_SHIELD) {
     Serial.println("WiFi not present");
     // don't continue:
     while (true);
  }

  // attempt to connect to Wifi network:
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
     status = WiFi.begin(ssid, pass);

    // wait 8 seconds for connection:
    delay(8000);
  }

  Serial.println("Connected to wifi");
  printWifiStatus();

  delay(1000);

  // WAKE UP YOUR SLEEPY CAMERA!
  CameraInitiate(){

  // NOTE: If this does not seem to be working, 
  // turn your camera on and off and try again.

}


void loop(){

  //Nothing to do here!
  delay(1000);

}



// FUNCTION TO WAKE UP THE CAMERA

void CameraInitiate(){

  //Begin UDP communication
  Udp.begin(localPort);

  //Send the magic packet to wake up the GoPro out of sleep
  delay(2000);
  SendMagicPacket();
  delay(5000);  

  // Absolutely necessary to flush port of UDP junk for Wifi client communication
  Udp.flush();
  delay(1000);

  //Stop UDP communication
  Udp.stop();
  delay(1000);

}


// Function to create and send magic packet
// Taken and translated from here:
// https://www.logicaprogrammabile.it/wol-accendere-computer-arduino-wake-on-lan/

void SendMagicPacket(){

  //Create a 102 byte array
  byte magicPacket[102];

  // Variables for cycling through the array
  int Cycle = 0, CycleMacAdd = 0, IndexArray = 0;

  // This for loop cycles through the array
  for( Cycle = 0; Cycle < 6; Cycle++){

    // The first 6 bytes of the array are set to the value 0xFF
    magicPacket[IndexArray] = 0xFF;

    // Increment the array index
    IndexArray++;
  }

  // Now we cycle through the array to add the GoPro address
  for( Cycle = 0; Cycle < 16; Cycle++ ){
    //eseguo un Cycle per memorizzare i 6 byte del
    //mac address
    for( CycleMacAdd = 0; CycleMacAdd < 6; CycleMacAdd++){
      
      magicPacket[IndexArray] = remote_MAC_ADD[CycleMacAdd];
      
      // Increment the array index
      IndexArray++;
    }
  }

  //The magic packet is now broadcast to the GoPro IP address and port
  Udp.beginPacket(broadCastIp, wolPort);
  Udp.write(magicPacket, sizeof magicPacket);
  Udp.endPacket();

}


void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

发送GoPro命令


在这个例子中,我将展示如何发送命令来启动和停止视频录制。

此处将使用相同方法来演示,你能够将相机中的每个功能发送命令。 GoPro Hero 4命令似乎是所有后续相机型号的标准设置。然而,一些较新的相机具有值得研究的新功能和特定于摄像头的命令。你可以通过单击特定相机型号的链接,在Unofficial GoPro WiFi API github页面上找到特定于摄像头的命令。

请看以下示例,录制持续5秒,停止录制,等待5秒钟后,然后重新开始。如果你使用的是Hero Session,请务必取消唤醒GoPro的代码行。

再次注意,不要忘记输入你的GoPro的WiFi凭证和MAC地址!

用于发送开始和停止录制命令的代码:

#include <SPI.h>
#include <WiFi101.h>
#include <WiFiUdp.h>



int status = WL_IDLE_STATUS;

  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  //ENTER YOUR WIFI NAME AND PASSWORD HERE
  //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 

char ssid[] = "XXXXXXXXXXXX"; //  your network SSID (name)
char pass[] = "XXXXXXXXXXXX";    // your network password (use for WPA, or use as key for WEP)

int localPort = 7;
byte broadCastIp[] = { 10,5,5,9 };

  //!!!!!!!!!!!!!!!!!!!!!!!!!!!
  //ENTER YOUR MAC ADDRESS HERE
  //!!!!!!!!!!!!!!!!!!!!!!!!!!! 

byte remote_MAC_ADD[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
int wolPort = 9;


WiFiUDP Udp;

WiFiClient client;

const char* host = "10.5.5.9";

const int httpPort = 80;


void setup(){  

  //Initialize serial and wait for port to open:
  Serial.begin(115200);


  // check for the presence of the wifi module:
  if (WiFi.status() == WL_NO_SHIELD) {
     Serial.println("WiFi not present");
     // don't continue:
     while (true);
  }


// ADD THIS TO LOOP??????????????
// MAKE ITS OWN FUNCTION???????

  // attempt to connect to Wifi network:
  while ( status != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    // Connect to WPA/WPA2 network. Change this line if using open or WEP network:
     status = WiFi.begin(ssid, pass);

    // wait 8 seconds for connection:
    delay(8000);
  }

  Serial.println("Connected to wifi");
  printWifiStatus();


}


void loop(){

// Add code to check to see if wifi is still on. If not, stop and keep trying to connect

// WAKE UP CAMERA FROM SLEEP AND CONNECT
CameraInitiate();

// Add code to make sure that camera recognized (status 31)


// START RECORDING
StartRecording();

delay(5000);

// STOP RECORDING
StopRecording();

delay(5000);

}


void StartRecording(){
  Serial.print("connecting to ");
  Serial.println(host);

  if (!client.connect("10.5.5.9", httpPort)) {
    Serial.println("connection failed");
    return;
  }

  //Command for starting recording
  String StartUrl = "/gp/gpControl/command/shutter?p=1";
  Serial.print("Requesting URL: ");
  Serial.println(StartUrl);
  client.print(String("GET ") + StartUrl + " HTTP/1.1\r\n" +
  "Host: " + host + "\r\n" +
  "Connection: close\r\n\r\n");
  Serial.println("Recording");
}



void StopRecording(){
  Serial.print("connecting to ");
  Serial.println(host);
  
  if (!client.connect("10.5.5.9", httpPort)) {
    Serial.println("connection failed");
    return;
  }

  //Command for stopping recording
  String StopUrl = "/gp/gpControl/command/shutter?p=0";
  Serial.print("Requesting URL: ");
  Serial.println(StopUrl);
  client.print(String("GET ") + StopUrl + " HTTP/1.1\r\n" +
  "Host: " + host + "\r\n" +
  "Connection: close\r\n\r\n");
  Serial.println("Stopped");
}



// FUNCTION TO WAKE UP THE CAMERA

void CameraInitiate(){

  //Begin UDP communication
  Udp.begin(localPort);

  //Send the magic packet to wake up the GoPro out of sleep
  delay(2000);
  SendMagicPacket();
  delay(5000);  

  // Absolutely necessary to flush port of UDP junk for Wifi client communication
  Udp.flush();
  delay(1000);

  //Stop UDP communication
  Udp.stop();
  delay(1000);

}


// Function to create and send magic packet
// Taken and translated from here:
// https://www.logicaprogrammabile.it/wol-accendere-computer-arduino-wake-on-lan/

void SendMagicPacket(){

  //Create a 102 byte array
  byte magicPacket[102];

  // Variables for cycling through the array
  int Cycle = 0, CycleMacAdd = 0, IndexArray = 0;

  // This for loop cycles through the array
  for( Cycle = 0; Cycle < 6; Cycle++){

    // The first 6 bytes of the array are set to the value 0xFF
    magicPacket[IndexArray] = 0xFF;

    // Increment the array index
    IndexArray++;
  }

  // Now we cycle through the array to add the GoPro address
  for( Cycle = 0; Cycle < 16; Cycle++ ){
    //eseguo un Cycle per memorizzare i 6 byte del
    //mac address
    for( CycleMacAdd = 0; CycleMacAdd < 6; CycleMacAdd++){
      
      magicPacket[IndexArray] = remote_MAC_ADD[CycleMacAdd];
      
      // Increment the array index
      IndexArray++;
    }
  }

  //The magic packet is now broadcast to the GoPro IP address and port
  Udp.beginPacket(broadCastIp, wolPort);
  Udp.write(magicPacket, sizeof magicPacket);
  Udp.endPacket();

}



void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

接收GoPro状态更新

可以使用以下链接从GoPro接收状态更新:
http://10.5.5.9/gp/gpControl/status

使用此链接时,它会提供一个JSON对象,可以对摄像头状态指南对其进行分析。

最简单的方法是使用计算机连接到GoPro的wifi网络,并使用网络浏览器加载上述链接。

它的反馈信息是:

{"status"{"1":1,"2":2,"3":0,"4":0,"6":0,"8":0,"9":0,"10":0,"13":0,"14":0,"15":0,"16":0,"17":1,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"26":0,"27":0,"28":82,"29":"","30":"","31":0,"32":0,"33":0,"34":2796,"35":1917,"36":0,"37":1,"38":0,"39":1,"40":"%12%05%13%0C%04%0E","41":0,"42":0,"43":0,"44":0,"45":0,"46":1,"47":1,"48":1,"49":0,"54":15476384,"55":1,"56":0,"57":3927,"58":0,"59":0,"60":500,"61":2,"62":0,"63":0,"64":0,"69":1,"71":12,"72":19,"73":20,"74":0}, "settings":{"1":0,"2":1,"3":8,"4":0,"5":0,"6":1,"7":1,"8":1,"9":0,"10":0,"11":0,"12":0,"13":1,"14":0,"15":4,"16":0,"17":4,"18":1,"19":0,"20":0,"21":1,"22":0,"23":0,"24":0,"25":0,"26":4,"27":0,"28":4,"29":5,"30":0,"31":0,"32":3601,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":4,"40":0,"41":13,"42":8,"43":0,"44":8,"45":8,"46":0,"47":0,"48":0,"52":1,"54":1,"57":0,"58":1,"59":6,"60":8,"61":1,"62":2500000,"63":7,"64":4,"65":0,"66":0,"67":0,"68":0,"69":1,"70":0,"71":0,"73":0,"74":0,"75":3,"76":3,"78":1,"84":0,"85":0,"86":1,"87":40,"89":12,"91":0,"92":12,"95":1,"96":0}}

恕我直言,每次我使用Arduino时,它都会以某种方式让我的GoPro系统崩溃。还好我不是真正需要这个功能所以就说这么多了。

MAKER 趣无尽项目页面:http://maker.quwj.com/project/65
via

坐沙发

发表评论