空の写真リベンジ

投稿日:

 先週試したAdafruit製の「小型TTLシリアルjpegカメラ」での間欠撮影先週はどうしたわけか同じ画像ばかり撮れてしまい、失敗である。

 (チナミに、このカメラのメーカーの「Adafruit」という会社、有名なマッシモ・バンジのTEDの中で紹介されていたことに気付いた。)

https://youtube.com/watch?v=UoBUXOOdLXY%3Ft%3D5m50s

 気を取り直して、スケッチを直す。撮影時のカメラのステータスを確認し、撮れていなければ何回でも撮り続ける。

「while(撮れてない)撮る;」

……というわけだ。まあ、万が一ハードウェアエラーなどがあるとループが回り続けるので良くないが、ループが回り続けようが結局のところはモノとしては電源を入れ直す他にどうしようもないので、こんなものだろう。

 それと、撮影が終わったらカメラをそのつどリセットすることにした。また、既に存在するファイル名は避けるようにした。こういう時、Arduinoには書式文字列付きの「sprintf」がないので、少し不便だなと思う。

 夜明けから日没までの一日の空の雲を撮りたいので、天気予報を見て雨が降らぬ確信を持ってから、昨夜寝る前にベランダにカメラをセットしておいた。朝早く起きるより楽だからだ。夜のうちに1000枚くらい写真が撮れてしまうが、SDカードには余裕があるので大丈夫である。

 昼間は用事があるのでカメラの面倒は見れないが、放置しておけば淡々と写真は撮れていく。

IMG_3143

 で、撮れた写真をWindows Movie Makerに流し込むと、微速撮影動画の一丁上がりだ。

 スケッチは次のとおりである。

//
//  camera2Web.ino
//    27.07.20(月) 0850~
//    佐藤俊夫
//    Adafruit製小型TTLシリアルJPEGカメラ+ETHERNET SHIELD 2で
//    間欠撮影をし、Webでダウンロードできるようにする。
//
#include <Adafruit_VC0706.h>
#include <SPI.h>
#include <SD.h>
#include <SoftwareSerial.h>
#include <Ethernet2.h>

#define CHIPSELECT 4

SoftwareSerial CAMCONNECTION(2, 3);
Adafruit_VC0706 CAM = Adafruit_VC0706(&CAMCONNECTION);
const unsigned long int INTERVAL = 30L * 1000L;
byte MAC[] = {  0x90, 0xA2, 0xDA, 0x0F, 0xF6, 0x74 };
IPAddress IP(192, 168, 1, 129);
EthernetServer SERVER(80);
EthernetClient CLIENT;

void setup() {
  pinMode(10, OUTPUT);
  if(!SD.begin(CHIPSELECT)) return;
  if(!CAM.begin()) return;
  CAM.setImageSize(VC0706_320x240);
  Ethernet.begin(MAC, IP);
  SERVER.begin();
  delay(1000);
}

void loop() {
  static unsigned long int prevtime = 0;
  char c;
  String rstr = "";
  //  INTERVALおきに写真を撮る
  if(millis() >= prevtime + INTERVAL){
    prevtime = millis();
    takePicture();
  }
  //  Webサーバ
  CLIENT = SERVER.available();
  if(CLIENT) {
    while(CLIENT.connected()) {
      if(CLIENT.available()) {
        c = CLIENT.read();
        rstr += c;
        if(rstr.endsWith("\r\n")){
          break;
        }
      }
    }
    if(rstr.indexOf("IMG") >= 0){
      String filename = "DCIM/";
      char cfilename[17];
      filename.concat(rstr.substring(rstr.indexOf("IMG"), rstr.indexOf("JPG") + 3));
      filename.toCharArray(cfilename, 17);
      CLIENT.println("HTTP/1.1 200 OK");
      CLIENT.println("Content-Type: image/jpg");
      CLIENT.println("Connection: close");
      CLIENT.println();
      File img = SD.open(cfilename);
      while(img.available()){
        CLIENT.write(img.read());
      }
      img.close();
    }else{
      sendform();
    }
    rstr = "";
    delay(1);
    // close the connection:
    CLIENT.stop();
  }
  delay(20);
}

void sendform(){
  //  フォームを送る。
  CLIENT.println("HTTP/1.1 200 OK");
  CLIENT.println("Content-Type: text/html");
  CLIENT.println("Connection: close");
  CLIENT.println();
  CLIENT.println("<!DOCTYPE HTML>");
  CLIENT.println("<html><head></head><body><center>");
  File dcim = SD.open("/DCIM");
  while(true) {
    File imgfile =  dcim.openNextFile();
    if(!imgfile){
      dcim.rewindDirectory();
      break;
    }
    CLIENT.write("<a href=\"");
    CLIENT.write(imgfile.name());
    CLIENT.write("\">");    
    CLIENT.write(imgfile.name());
    CLIENT.println("</a><br>");
    imgfile.close();    
  }
  dcim.close();
  CLIENT.println("</center></body></html>");
}

void takePicture(){
  static unsigned int pnum = 0;
  char filename[] = "DCIM/img0000.jpg";
  while(!CAM.takePicture());
  do{
    filename[8]  = '0' + pnum / 1000;
    filename[9]  = '0' + (pnum / 100) % 10;
    filename[10] = '0' + (pnum /  10) % 10;
    filename[11] = '0' + pnum % 10;
    pnum ++;
  }while(SD.exists(filename));
  if(pnum > 9999) pnum = 0;
  File imgFile = SD.open(filename, FILE_WRITE);
  uint16_t jpglen = CAM.frameLength();
  pinMode(8, OUTPUT);
  while (jpglen > 0) {
    uint8_t *buffer;
    uint8_t bytesToRead = min(32, jpglen);
    buffer = CAM.readPicture(bytesToRead);
    imgFile.write(buffer, bytesToRead);
    jpglen -= bytesToRead;
  }
  while(!CAM.reset());
  imgFile.close();
}




投稿者: 佐藤俊夫

 50代後半の爺。技術者。元陸上自衛官。2等陸佐で定年退官。ITストラテジストテクニカルエンジニア(システム管理)基本情報技術者

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください