17 июл


Обзавёлся экшн камерой Soocoo C30, но так получилось что производитель не сделал для неё пульта удалённого управления, вернее сделал, но приложение для андроид телефона, что не всегда тоже удобно. Управление камерой с приложения происходит по WiFi - это значит что можно подавать на камеру сигналы с WiFi модуля ESP8266. На нём и был построен прототип пульта удалённого управления камерой.

Для прошивки модуля потребуется Arduino версии 1.6.9 и Arduino core for ESP8266 WiFi chip версии 2.3.0 - только тут есть небольшой нюанс, у меня возник на ubuntu x64, прошивки прошиваются, всё работает кроме подключения и поиска WiFi на модуле, как оказалось проблема набюдалась начиная с 2.1.0 версии Arduino-ESP8266 и заключалась в кривом esptool, его пришлось пересобрать (esptool-ck и закинуть по пути: ~/.arduino15/packages/esp8266/tools/esptool/0.4.9/ ) и всё начало работать как положено.

Код программы для пульта:
/* WiFi Remote для Sookoo C30 на ESP8266
 * Автор: MSW 
 * Сайт:  https://0-web.ru/
 */
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>

ESP8266WebServer server(80);
WiFiClient client;

const char* ssid = "C30-Remote";
const char* pass = "";
int chanel = 6;

int BTN_PHOTO = 4; //*** ESP-12: GPIO4 / ESP-01: GPIO0
int BTN_VIDEO = 5; //*** ESP-12: GPIO5 / ESP-01: GPIO2
int LED = 1;       //*** ESP-12: GPIO2 / ESP-01: GPIO1

int ledState = LOW;
unsigned long previousMillis = 0;
const long intervalOn = 100;
const long intervalOff = 900;

String content;
String html;

bool makePhoto = false;
bool statVideo = false;

void setup() {
  pinMode( BTN_PHOTO, INPUT );
  pinMode( BTN_VIDEO, INPUT );
  pinMode( LED, OUTPUT );
  digitalWrite( LED, HIGH ); //*** выключить светодиод

  EEPROM.begin( 26 ); //*** инициализация EEPROM для хранения SSID и PASS

  if ( digitalRead( BTN_PHOTO ) == 0 ) {   //*** если при включении нажата кнопка Фото
    blinkLed( 850, 150, 3 );               //*** пауза (850+150)*3 = 3000мс
    if ( digitalRead( BTN_PHOTO ) == 0 ) { //*** если всё ещё нажата кнопка Фото
      configMode();                        //*** переходим в режим настройки
    }
  }

  //*** Чтение SSID из EEPROM
  String eSid;
  for ( int i = 0; i < 18; ++i ) { eSid += char( EEPROM.read(i) ); }   //*** SSID = 18 символов

  //*** Чтение PASS из EEPROM
  String ePass = "";
  for ( int i = 18; i < 26; ++i ) { ePass += char( EEPROM.read(i) ); } //*** PASS = 8 символов

  WiFi.mode( WIFI_STA );
  WiFi.begin( eSid.c_str(), ePass.c_str() );
  while ( WiFi.status() != WL_CONNECTED ) {
    blinkLed( 150, 150, 2 ); //*** пауза (150+150)*2 = 600мс
  }
  digitalWrite( LED, HIGH );
}


void loop() {
  if ( digitalRead( BTN_PHOTO ) == 0 ) { //*** если нажата кнопка Фото
    delay( 100 ); //*** дребезг контактов
    if( digitalRead( BTN_PHOTO ) == 0 ) {
      if( makePhoto == false ) {         //*** если режим фото не включен
        makePhoto = true;
        makeQuery( "3001&par=0" );       //*** переключаемся в режим фото
      }
      makeQuery( "1001" );               //*** сделать фото
    }
  }
  if ( digitalRead( BTN_VIDEO ) == 0 ) { //*** если нажата кнопка Видео
    delay( 100 ); //*** дребезг контактов
    if ( digitalRead( BTN_VIDEO ) == 0 ) {
      if ( makePhoto ) {                 //*** если включен режим фото
        makePhoto = false;
        makeQuery( "3001&par=1" );       //*** переключаемся в режим видео
      }
      if ( statVideo == true ) {         //*** если видео запущено
        statVideo = false;
        makeQuery( "2001&par=0" );       //*** остановить запись
      } else {                           //*** если видео остановлено
        statVideo = true;
        makeQuery( "2001&par=1" );       //*** начать запись
      }
    }
  }
}

void configMode() {
  WiFi.mode( WIFI_STA );
  WiFi.disconnect();
  delay( 100 );
  WiFi.softAP( ssid, pass, chanel );

  html = "<!DOCTYPE html>";
  html += "<html>";
  html += "<head>";
  html += "<title>C30 Remote Seting</title>";
  html += "<meta http-equiv=\"Content-type\" content=\"text/html; charset=utf-8\">";
  html += "<meta name=\"viewport\" content=\"width=device-width, minimum-scale=1, initial-scale=1, user-scalable=no\">";
  html += "<style>";
  html += ".element { width:100%; border:1px solid #00F; font-size:18pt; color:#333; background:#F2F2F2; }";
  html += ".error { font-size:18pt; color:#F66; }";
  html += ".success { font-size:18pt; color:#66F; }";
  html += "</style>";
  html += "</head>";
  html += "<body>";

  server.on("/", []() { //*** главная страница настроек
    int n = WiFi.scanNetworks(); //*** сканирование доступных сетей
    if ( n == 0 ) {
      server.send( 200, "text/html", html + "<div class=\"error\">WIFI сеть не обнуружена</div>" );
    } else {
      content = "<form action=\"/save\" method=\"get\">";
      content += "<select name=\"ssid\" class=\"element\">";
      for ( int i = 0; i < n; ++i ) {
        content += "<option value=\"" + WiFi.SSID(i) + "\">" + WiFi.SSID(i) + "</option>";
      }
      content += "</select>";
      content += "<p><input type=\"password\" name=\"pass\" class=\"element\" placeholder=\"Пароль\"></p>";
      content += "<p><input type=\"submit\" value=\"Сохранить настройки\" class=\"element\"></p>";
      content += "</form>";
      server.send( 200, "text/html", html + content );
    }
  });
  server.on("/save", []() { //*** сохранение настроек
    String qsid = server.arg( "ssid" );
    String qpass = server.arg( "pass" );
    if ( qsid.length() > 0 && qpass.length() > 0 ) {
      for ( int i = 0; i < 26; ++i ) { EEPROM.write( i, 0 ); }                        //*** очищаем EEPROM
      for ( int i = 0; i < qsid.length(); ++i ) { EEPROM.write( i, qsid[i] ); }       //*** записываем SSID
      for ( int i = 0; i < qpass.length(); ++i ) { EEPROM.write( 18+i, qpass[i] ); }  //*** записываем PASS
      EEPROM.commit();
      content = "<div class=\"success\">Настройки успешно сохранены</div>";
    } else {
      content = "<div class=\"error\">Не указан SSID или PASS</div>";
    }
    content += "<form action=\"/\"><input type=\"submit\" value=\"Назад\" class=\"element\"></form>";

    server.send( 200, "text/html", html + content );
  });
  server.begin();

  serverLoop:                           //*** зацикливаемся в настройках
  server.handleClient();

  //*** Мигание светодиодом в режиме сервера
  unsigned long currentMillis = millis();
  if ( ledState == LOW && currentMillis - previousMillis >= intervalOn ) {
    previousMillis = currentMillis;
    ledState = HIGH;
    digitalWrite( LED, ledState );
  }
  if ( ledState == HIGH && currentMillis - previousMillis >= intervalOff ) {
    previousMillis = currentMillis;
    ledState = LOW;
    digitalWrite( LED, ledState );
  }
  goto serverLoop;
}

void makeQuery(String cmd) {
  if ( !client.connect("192.168.1.254", 80) ) {
    return;
  }
  client.print("GET /?custom=1&cmd=" + cmd + " HTTP/1.1\r\nUser-Agent: Dalvik/2.1.0\r\nHost: 192.168.1.254\r\nConnection: close\r\nAccept-Encoding: gzip\r\n\r\n");
}

void blinkLed(int ledOn, int ledOff, int cnt) {
  for ( int i = 0; i < cnt; ++i ) {
    digitalWrite( LED, LOW );
    delay( ledOn );
    digitalWrite( LED, HIGH );
    delay( ledOff );
  }
}


Логика работы простая, при включении пульт читает настройки из EEPROM и подключается к точке доступа на камере. Если на выключеном пульте зажать кнопку фото и включить питание, то пульт создаёт свою точку доступа C30-Remote, запускает веб-сервер, сканирует все доступные WiFi сети и выводит их на свою страничку настроек. После чего любым смартфоном/планшетом/ноутбуком подключаемся к точке доступа пульта без пароля, выбираем сеть камеры и вводим пароль от неё. После сохранения настроек надо перезагрузить пульт и он подключится к камере.

Наглядно этот процесс можно увидеть на видео: Пульт управления для Soocoo C30 на ESP8266

Скетч в архиве:
7
2,56 Kb
C30_Remote.zip
Загружен: 17-07-2016, 08:51 / MD5: b244feea87cad703edb12f8f40a7650d

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.