タイムラプスやるなら外で(その3)

今回は機器の使い方とソフト関係の説明です。。先ずは使い方

  1. 機器の電源をいれて機器を起動させます。
    • 今回はスイッチを容器の中に入れています。
    • 下図の左上のネジとESP32CAMの間にある黒い細い棒がそれです。
    • スイッチを入れてからふたをするのでちょっと面倒です。
    • その他
      • 真ん中がカメラのレンズ
      • 右側の赤いプッシュスイッチ ー> リセット
      • 右側の緑のプッシュスイッチ ー> GIPO0(Boot)
  1. 機器のReady用LEDが無いので機器の状態が分からないのですが、普通なら10秒位で立ち上がります。
  2. 次にスマホとWiFiで繋ぎます。
    • A: WiFi接続の画面に行き、”ESP32CAM”を選択
    • B: パスワード入力の画面で、”12345678”を入力
    • C: WiFiには接続出来るのですが、インターネットは利用出来ないと表示されました。
    • D: 相手が目の前にあるので非常に強い電波です。
  1. 次はスマホのブラウザをあげてURL入力欄に、”192.168.4.1”と入力して下さい。
  2. 最初に下図左側の画面が表示されると思います。このHPで、”Take”ボタンを押すと、撮影した写真が表示されます。
  1. 簡単な説明
    • バッテリー電圧
      • バッテリー電圧を表示します。
      • 乾電池4本(1.5×4)の電圧ですので、約6V近辺の値。
    • 撮影経過時間
      • スキップ時間を除いた実際に撮影している時間
    • 写真表示部
      • 撮影ボタンを押した時の写真をここに表示します。
      • 被写体の位置合わせに使います。
    • 撮影ボタン
      • このボタンを押すと写真が撮れます。
    • 撮影間隔時間
      • 撮影間隔時間を入力します。左が時、右が分。
    • 総撮影時間
      • スキップ時間を除いた総撮影時間。
    • スキップ
      • このカメラで夜の撮影は出来ません。
      • そこで撮影しない時間をセットするようにしました。
      • ここをチェックしてからスキップ時間の設定を行います。
    • 現在の時間
      • スキップ時間の計算に使用
    • スキップ開始時間
    • スキップ終了時間
    • スタートロック
      • 間違って、”Start”ボタンを押さないように付けました。
      • ここをチェックしないと、”Start”ボタンを押せない
    • 撮影開始ボタン
      • ボタンを押すと撮影が開始されます。
  1. その他の注意
    • Total時間以外は24時間制で指定して下さい。午前午後の区別は有りません
    • 録画をスキップする場合、日にちをまたぐとStart時間よりStop時間が早くなる場合が有ります。
    • 例えば、現在が午前10時で、午後8時から次の日の朝3時までスキップする場合、下記の様に入力します。
      • Now時間: 10:00
      • Start時間: 20:00
      • Stop時間: 03:00
    • 現在の時間がスキップ時間内でも動作します。例えば、現在10:00。スキップ開始9:00。スキップ終了12:00の場合
      • Now: 10:00
      • Start: 09:00
      • Stop: 12:00

今回のスケッチ


#include "esp_camera.h"
#include "Arduino.h"
#include "SD.h"                          
#include <WiFi.h>
#include <WebServer.h>
#include "esp_deep_sleep.h"

// Pin definition for CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27

#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22

#define sd_sck  14
#define sd_mosi 15
#define sd_ss   13
#define sd_miso  2

WebServer server(80);
const char ssid[] = "ESP32CAM";  // SSID
const char pass[] = "12345678";   // password

// Take Picture with Camera
//camera_fb_t * fb = NULL;

RTC_DATA_ATTR int t_time[12]={0};
#define lap_h       0
#define lap_m       1
#define total_h     2
#define total_m     3
#define now_h       4
#define now_m       5
#define start_h     6
#define start_m     7
#define stop_h      8
#define stop_m      9
#define elapse_h    10
#define elapse_m    11

RTC_DATA_ATTR int bootCount = 0;
RTC_DATA_ATTR int skip_flg = 0;
uint64_t lap_Time = 0;

String index_Dt =
                  "<!DOCTYPE html>\n<html>\n<head>\n"
                  "<title>ESP32 Timelapse</title>\n"
                  "<style type='text/css'>\n"
                  "button.bt1 {background:#90ee90;font-size:50px;padding:10px;margin:20px;border-radius:30px;box-shadow:4px 4px #555;}\n"
                  "input.ip1 {font-size:40px;width:100px;}\n"
                  "input.ip2 {font-size:40px;width:70px;}\n"
                  "</style>\n</head>\n"
                  "<body>\n<center>\n"
                  "<div style='width:700px; background-color:#d8a373; border-style:solid; border-radius:30px; border-color:#6b3f31'>\n"
                  "<br><span style='font-size:70px'><b><i><u>Timelapse</u></i></b></span><br>\n"
                  "<span style='font-size:40px' id='p_vol'>50</span><span style='font-size:40px' id='p_no'> Done: xxx</span><br>\n"
                  "<img src='/data.jpg'>\n"
                  "<form method='get' style='font-size:40px'>\n"
                  "<button type='submit' name='1' class='bt1'>Take</button><br></form>\n"
                  "<form method='get' style='font-size:40px'>\n"
                  "<span>Lap : <input type='number' max='100' min='0' name='20' class='ip1'/>h\n"
                  "<input type='number' max='59' min='0' name='21' class='ip2'/>m<br>\n"
                  "Total:<input type='number' max='10000' min='0' name='22' class='ip1'/>h\n"
                  "<input type='number' max='59' min='0' name='23' class='ip2'/>m<br><br>\n"
                  "<input type='checkbox' style='scale:2.5;vertical-align:middle;' onclick='onBtnSkip()' name='30' />  Skip Time<br>\n"
                  "Now : <input type='number' max='100' min='0' name='24' class='ip1'/>h\n"
                  "<input type='number' max='59' min='0' name='25' class='ip2'/>m<br>"
                  "Start : <input type='number' max='100' min='0' name='26' class='ip1'/>h\n"
                  "<input type='number' max='59' min='0' name='27' class='ip2'/>m<br>\n"
                  "Stop : <input type='number' max='100' min='0' name='28' class='ip1'/>h\n"
                  "<input type='number' max='59' min='0' name='29' class='ip2'/>m</span><br>\n"
                  "<input type='checkbox' style='scale:2.5;vertical-align:middle;' onclick='onBtnStart()' name='31' />\n"
                  "<button type='submit' class='bt1' id='b_st'>Start</button>\n"
                  "</form>\n</div>\n</center>\n"
                  "<script>\n"
                  "function onBtnStart() {\n"
                  "if(document.getElementsByName('31')[0].checked)\n document.getElementById('b_st').disabled = '';\n"
                  "else document.getElementById('b_st').disabled = 'disabled';\n}\n\n"
                  "function onBtnSkip() {\n"
                  "if(!document.getElementsByName('30')[0].checked){\n";
                  
                  
void setup() {

  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG; 
  config.frame_size = FRAMESIZE_VGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
  config.jpeg_quality = 10;
  config.fb_count = 2;

  Serial.begin(115200);

  // Init Camera
  esp_camera_init(&config);
  delay(3000);

  SPI.begin(sd_sck, sd_miso, sd_mosi, sd_ss);
  SD.begin(sd_ss);

  esp_deep_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_AUTO);
  gpio_pullup_en(GPIO_NUM_0);    // use pullup on GPIO
  gpio_pulldown_dis(GPIO_NUM_0); // not use pulldown on GPIO

  if(esp_sleep_get_wakeup_cause() != 4)
  { 
    Serial.println("Connecting to WiFi");
    WiFi.disconnect();
    WiFi.softAPdisconnect(true);
    delay(500);
    
    WiFi.softAP(ssid, pass); 
    delay(1000);

    Serial.print("AP IP address: ");
    Serial.println(WiFi.softAPIP());

    server.on("/", handleRoot);
    server.onNotFound(handleWebRequests); 
    server.begin();
    Serial.println("HTTP server started");
  }
  else
  {
    take_Poto();
    switch(check_Next())
    {
      case 0: 
      case 1: esp_sleep_enable_timer_wakeup(lap_Time);
              break;
              
      case 2: esp_sleep_enable_ext0_wakeup(GPIO_NUM_0,0);
    }

    esp_deep_sleep_start(); 
    delay(500);
  }
}

int check_Next()
{
  int a,b,flg;
  int d_data[12];

    flg=0;
    for(a=0; a<12; a++) d_data[a] = t_time[a];
    
    if(check_Two(&d_data[total_h], &d_data[elapse_h])) flg=2;  
    //  esp_sleep_enable_ext0_wakeup(GPIO_NUM_16,0);
    else
    {
      if(skip_flg)                                            // Skip = On
      {
        if(check_Two(&d_data[start_h], &d_data[stop_h]))      // start < stop
        {
          if(check_Two(&d_data[stop_h], &d_data[now_h])) ;    // now >= stop 
          else                                                // now  < stop
          {
            if(check_Two(&d_data[start_h], &d_data[now_h]))   // start =< now 
            {
                calc_Wait(0); flg=1;          
            }
          }
        }
        else                                                  // start > stop
        {
          if(check_Two(&d_data[start_h], &d_data[now_h]))     // now > start     
          {
             calc_Wait(1); flg=1;          
          }
        }
      }
    }
    
    if(flg == 0)
    {
      lap_Time = t_time[lap_h] * 60 + t_time[lap_m];
      t_time[now_m] += t_time[lap_m]; 
      if(t_time[now_m] > 59) 
      {
        t_time[now_m] -= 60;
        t_time[now_h] ++;
      }
      t_time[now_h] += t_time[lap_h]; 
      if(t_time[now_h] > 23) t_time[now_h] -= 24;

      t_time[elapse_m] += t_time[lap_m]; 
      if(t_time[elapse_m] > 59) 
      {
        t_time[elapse_m] -= 60;
        t_time[elapse_h] ++;
      }
      t_time[elapse_h] += t_time[lap_h]; 
    }
    //Set timer 
    lap_Time *= 60000000;
     
    return(flg); 
}

int check_Two(int *st, int *sp)
{
//  st - sp then, st > sp :0   st =< sp :1
  
  int a,b,c;
  
    a = st[0];
    b = st[1] - sp[1];
    if(b < 0) a --;
    a -= sp[0];

    c=0;
    if(a < 0) c=1;
    else 
      if(a == 0 && b < 1) c=1; 
    
    return (c); 
}

void calc_Wait(int flg)
{
  int a,b;

    b = t_time[stop_h];
    if(flg) b += 24;

    a = t_time[stop_m] - t_time[now_m];
    if( a < 0)
    {
      a = t_time[stop_m] + 60 - t_time[now_m];
      b --;
    }
    
    b -= t_time[now_h];
    lap_Time = b * 60 + a;
    t_time[now_h] = t_time[stop_h];
    t_time[now_m] = t_time[stop_m];  
}

void loop() {
  
  server.handleClient();
}

void handleRoot() {
  String cmd,buf;
  int a,b,flg;
  File dataFile;
  camera_fb_t * fb = NULL;
  float d_vol;
  char temp[16];

    flg=0;
    cmd=server.argName(0);
    switch(cmd.toInt())
    {
      case 1:   // Start Log
                fb = esp_camera_fb_get();
                dataFile = SD.open("/data.jpg", FILE_WRITE);
                dataFile.write(fb->buf, fb->len); // payload (image), payload length
                dataFile.close();
                esp_camera_fb_return(fb); 
                Serial.printf("Take a photo.\n");
                break;
                
      case 20:   // Start Log
                skip_flg=1; b=30;
                if(server.arg("30") == "") { skip_flg=0; b=24; }

                for(a=20; a<b; a++)
                {
                  cmd=server.arg(String(a));
                  t_time[a-20]=cmd.toInt(); 
                }
                
                t_time[elapse_h] = t_time[elapse_m] = 0;
                bootCount = 0;
                switch(check_Next())
                {
                  case 0: take_Poto();
                  case 1: esp_sleep_enable_timer_wakeup(lap_Time);
                          break;
              
                  case 2: esp_sleep_enable_ext0_wakeup(GPIO_NUM_0,0);
                        break;
                }
                flg=1;

  for(a=0; a<12; a += 2) 
    Serial.println(String(a) + "=" + String(t_time[a]) + ":" + String(t_time[a+1]));              
                break;
    }

    buf=index_Dt;
    for(a=24; a<29; a++)
      buf += ("document.getElementsByName('" + String(a) + "')[0].disabled =\n");
    buf += "document.getElementsByName('29')[0].disabled =true;}\nelse{\n";
    for(a=24; a<29; a++)
      buf += ("document.getElementsByName('" + String(a) + "')[0].disabled =\n");
    buf += "document.getElementsByName('29')[0].disabled =false;}}\n\n";
    
    d_vol = analogRead(33);
    d_vol *= 3.3; d_vol /= 2048;
    dtostrf(d_vol, 5, 2,temp);
    cmd=temp;
    buf += ("document.getElementById('p_vol').innerHTML=\"Battery: " + cmd +  " V \";\n");
    buf += ("document.getElementById('p_no').innerHTML=\"Elapsed: " + String(t_time[elapse_h]) + ":" + String(t_time[elapse_m]) + "\";\n\n");

    for(a=20; a<30; a++)
      buf += ("document.getElementsByName('" + String(a) + "')[0].value=" + String(t_time[a-20]) +";\n");
    
    if(skip_flg)
      buf += "document.getElementById('lap').checked=true;\n";
      
    buf += "onBtnStart();\nonBtnSkip();\nhistory.pushState(null,null,'/');\n</script>\n</body>\n</html>";

    server.send(200, "text/html", buf);

    if(flg)
    {
      esp_deep_sleep_start();
      delay(500);
    }
}

void handleWebRequests()
{
  String dataType = "text/plain";
  String path;
  File dataFile;
   
    path = server.uri();
    if(path.endsWith(".txt")) dataType = "text/plain";
    else if(path.endsWith(".css")) dataType = "text/css";
    else if(path.endsWith(".js")) dataType = "application/javascript";
    else if(path.endsWith(".png")) dataType = "image/png";
    else if(path.endsWith(".gif")) dataType = "image/gif";
    else if(path.endsWith(".jpg")) dataType = "image/jpeg";
    delay(5);
    
    dataFile = SD.open(path.c_str(), "r");
    server.streamFile(dataFile, dataType);
    dataFile.close();
    delay(5);
}

void take_Poto()
{
  File dataFile;
  camera_fb_t * fb = NULL;
  String f_name;
  char s_name[15];
  
    fb = esp_camera_fb_get();
    sprintf(s_name,"%08d",bootCount);
    f_name = s_name; 
    f_name = "/" + f_name + ".jpg" ;
    dataFile = SD.open(f_name, FILE_WRITE);
    dataFile.write(fb->buf, fb->len); // payload (image), payload length
    dataFile.close();
    esp_camera_fb_return(fb); 
    bootCount ++;
    Serial.println("No." + String(bootCount));
}

先ずはこれで撮影して見ます。