ブログのアクセスログを見ていたら、Aidafruiteの小型TTLシリアルJPEGカメラの基本的な扱いについて書いた私の記事を参考にされた方があって、その方がブログに取り上げてくださっていることが分かった。
- カメラで遊んでみる(私の記事)
- VC0706 で撮影し SDカード に記録する Arduino camera(My Spice Cabinet 04/09/平成29年(2017))
取り上げていただいて、誠に恐縮なことである。
オッサンは生きている。
ブログのアクセスログを見ていたら、Aidafruiteの小型TTLシリアルJPEGカメラの基本的な扱いについて書いた私の記事を参考にされた方があって、その方がブログに取り上げてくださっていることが分かった。
取り上げていただいて、誠に恐縮なことである。
先週試したAdafruit製の「小型TTLシリアルjpegカメラ」での間欠撮影。先週はどうしたわけか同じ画像ばかり撮れてしまい、失敗である。
(チナミに、このカメラのメーカーの「Adafruit」という会社、有名なマッシモ・バンジのTEDの中で紹介されていたことに気付いた。)
https://youtube.com/watch?v=UoBUXOOdLXY%3Ft%3D5m50s
気を取り直して、スケッチを直す。撮影時のカメラのステータスを確認し、撮れていなければ何回でも撮り続ける。
……というわけだ。まあ、万が一ハードウェアエラーなどがあるとループが回り続けるので良くないが、ループが回り続けようが結局のところはモノとしては電源を入れ直す他にどうしようもないので、こんなものだろう。
それと、撮影が終わったらカメラをそのつどリセットすることにした。また、既に存在するファイル名は避けるようにした。こういう時、Arduinoには書式文字列付きの「sprintf」がないので、少し不便だなと思う。
夜明けから日没までの一日の空の雲を撮りたいので、天気予報を見て雨が降らぬ確信を持ってから、昨夜寝る前にベランダにカメラをセットしておいた。朝早く起きるより楽だからだ。夜のうちに1000枚くらい写真が撮れてしまうが、SDカードには余裕があるので大丈夫である。
昼間は用事があるのでカメラの面倒は見れないが、放置しておけば淡々と写真は撮れていく。
で、撮れた写真を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(); }
秋月電子のサイトを見ていると、小さなカメラがあり、Arduinoに付きそうな感じだ。
3850円。Arduino自体が2800円かそこらなので、それに比べるとちょっと高いが、早速行って購入。
だが、あまり情報は多くない。まず、メーカーのサイトを見ていくと、チュートリアルがあり、「とりあえずテストするには、電源をくれてやって、一番端のピンをテレビにつなぎゃあ絵が出る」みたいなザックリ感満載の解説が。それで、テレビにつなぐためのRCAジャックなども買う。
チュートリアルはこれを読んでおけばだいたいいいようだ。
ほどいてみるとこんな感じで、かなり小さい。
ピンのピッチが2mmで、ブレッドボードで扱いにくい。それで、普通の2.54mmのピンヘッダを出して、その根元をこんなふうにムリヤリ(笑)2mmピッチにせばめる。
こいつをカメラの基盤にえいやっ、とねじ込み、半田付けする。
なかなか小さいので、ルーペと老眼鏡を併用しつつ、ICなんか壊しちゃってもナンだから、20Wのぬるくて細い半田鏝でさっさとつける。
我ながらなかなかスピーディな仕事だなあ(笑)。
で、メーカーのサイトには「5V」と書いてあるが、これは互換品の別の製品のためのチュートリアルのようで、買ってきたものの基盤をよく見ると「3.3V」と印刷されている。壊してはもったいないから、3.3Vで試す。3.3Vの電源代わりにArduinoの3.3Vピンを使う。
基盤の印刷通り、3.3V、GND、それから右端のピンをRCAジャックのセンターに、RCAジャックのアースを同じくGNDに入れて、テレビの前に持っていく。
テレビにつなぐと、おお、確かに、値段なりのフザけた画質(笑)で、自分の顔が映る。
上下が逆だが、まあ、いいや。
で、今度はArduinoで画像を撮影してみよう。
チュートリアルにしたがってArduino用のライブラリをダウンロードし、これをArduinoのインストールフォルダの「libraries」に配置する。
そうしておいてArduinoのIDEを起動すると、「ファイル」→「スケッチの例」の中に「Adafruit VC0706 Serial Camera Library」が現れるから、この中から「Snapshot」を選ぶ。これは静止画をjpegで撮影するスケッチのサンプルだ。
// This is a basic snapshot sketch using the VC0706 library. // On start, the Arduino will find the camera and SD card and // then snap a photo, saving it to the SD card. // Public domain. // If using an Arduino Mega (1280, 2560 or ADK) in conjunction // with an SD card shield designed for conventional Arduinos // (Uno, etc.), it's necessary to edit the library file: // libraries/SD/utility/Sd2Card.h // Look for this line: // #define MEGA_SOFT_SPI 0 // change to: // #define MEGA_SOFT_SPI 1 // This is NOT required if using an SD card breakout interfaced // directly to the SPI bus of the Mega (pins 50-53), or if using // a non-Mega, Uno-style board. #include <Adafruit_VC0706.h> #include <SPI.h> #include <SD.h> // comment out this line if using Arduino V23 or earlier #include <SoftwareSerial.h> // uncomment this line if using Arduino V23 or earlier // #include <NewSoftSerial.h> // SD card chip select line varies among boards/shields: // Adafruit SD shields and modules: pin 10 // Arduino Ethernet shield: pin 4 // Sparkfun SD shield: pin 8 // Arduino Mega w/hardware SPI: pin 53 // Teensy 2.0: pin 0 // Teensy++ 2.0: pin 20 #define chipSelect 10 // Pins for camera connection are configurable. // With the Arduino Uno, etc., most pins can be used, except for // those already in use for the SD card (10 through 13 plus // chipSelect, if other than pin 10). // With the Arduino Mega, the choices are a bit more involved: // 1) You can still use SoftwareSerial and connect the camera to // a variety of pins...BUT the selection is limited. The TX // pin from the camera (RX on the Arduino, and the first // argument to SoftwareSerial()) MUST be one of: 62, 63, 64, // 65, 66, 67, 68, or 69. If MEGA_SOFT_SPI is set (and using // a conventional Arduino SD shield), pins 50, 51, 52 and 53 // are also available. The RX pin from the camera (TX on // Arduino, second argument to SoftwareSerial()) can be any // pin, again excepting those used by the SD card. // 2) You can use any of the additional three hardware UARTs on // the Mega board (labeled as RX1/TX1, RX2/TX2, RX3,TX3), // but must specifically use the two pins defined by that // UART; they are not configurable. In this case, pass the // desired Serial object (rather than a SoftwareSerial // object) to the VC0706 constructor. // Using SoftwareSerial (Arduino 1.0+) or NewSoftSerial (Arduino 0023 & prior): #if ARDUINO >= 100 // On Uno: camera TX connected to pin 2, camera RX to pin 3: SoftwareSerial cameraconnection = SoftwareSerial(2, 3); // On Mega: camera TX connected to pin 69 (A15), camera RX to pin 3: //SoftwareSerial cameraconnection = SoftwareSerial(69, 3); #else NewSoftSerial cameraconnection = NewSoftSerial(2, 3); #endif Adafruit_VC0706 cam = Adafruit_VC0706(&cameraconnection); // Using hardware serial on Mega: camera TX conn. to RX1, // camera RX to TX1, no SoftwareSerial object is required: //Adafruit_VC0706 cam = Adafruit_VC0706(&Serial1); void setup() { // When using hardware SPI, the SS pin MUST be set to an // output (even if not connected or used). If left as a // floating input w/SPI on, this can cause lockuppage. #if !defined(SOFTWARE_SPI) #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) if(chipSelect != 53) pinMode(53, OUTPUT); // SS on Mega #else if(chipSelect != 10) pinMode(10, OUTPUT); // SS on Uno, etc. #endif #endif Serial.begin(9600); Serial.println("VC0706 Camera snapshot test"); // see if the card is present and can be initialized: if (!SD.begin(chipSelect)) { Serial.println("Card failed, or not present"); // don't do anything more: return; } // Try to locate the camera if (cam.begin()) { Serial.println("Camera Found:"); } else { Serial.println("No camera found?"); return; } // Print out the camera version information (optional) char *reply = cam.getVersion(); if (reply == 0) { Serial.print("Failed to get version"); } else { Serial.println("-----------------"); Serial.print(reply); Serial.println("-----------------"); } // Set the picture size - you can choose one of 640x480, 320x240 or 160x120 // Remember that bigger pictures take longer to transmit! cam.setImageSize(VC0706_640x480); // biggest //cam.setImageSize(VC0706_320x240); // medium //cam.setImageSize(VC0706_160x120); // small // You can read the size back from the camera (optional, but maybe useful?) uint8_t imgsize = cam.getImageSize(); Serial.print("Image size: "); if (imgsize == VC0706_640x480) Serial.println("640x480"); if (imgsize == VC0706_320x240) Serial.println("320x240"); if (imgsize == VC0706_160x120) Serial.println("160x120"); Serial.println("Snap in 3 secs..."); delay(3000); if (! cam.takePicture()) Serial.println("Failed to snap!"); else Serial.println("Picture taken!"); // Create an image with the name IMAGExx.JPG char filename[13]; strcpy(filename, "IMAGE00.JPG"); for (int i = 0; i < 100; i++) { filename[5] = '0' + i/10; filename[6] = '0' + i%10; // create if does not exist, do not open existing, write, sync after write if (! SD.exists(filename)) { break; } } // Open the file for writing File imgFile = SD.open(filename, FILE_WRITE); // Get the size of the image (frame) taken uint16_t jpglen = cam.frameLength(); Serial.print("Storing "); Serial.print(jpglen, DEC); Serial.print(" byte image."); int32_t time = millis(); pinMode(8, OUTPUT); // Read all the data up to # bytes! byte wCount = 0; // For counting # of writes while (jpglen > 0) { // read 32 bytes at a time; uint8_t *buffer; uint8_t bytesToRead = min(32, jpglen); // change 32 to 64 for a speedup but may not work with all setups! buffer = cam.readPicture(bytesToRead); imgFile.write(buffer, bytesToRead); if(++wCount >= 64) { // Every 2K, give a little feedback so it doesn't appear locked up Serial.print('.'); wCount = 0; } //Serial.print("Read "); Serial.print(bytesToRead, DEC); Serial.println(" bytes"); jpglen -= bytesToRead; } imgFile.close(); time = millis() - time; Serial.println("done!"); Serial.print(time); Serial.println(" ms elapsed"); } void loop() { }
で、これはSDカードに書き込むようになっている。
私の手持ちの、ArduinoにSDカードをつなぐ手段は、先日から愛用中の「ETHERNET SHIELD 2」に搭載されているSDカードスロットだけだから、とりあえずこれを使う。
手持ちのSDカードをETHERNET SHIELD 2に挿し、Adafruitのサイトのチュートリアルを参考に回路をブレッドボードに組む。
注意する点は2つだ。
そうやってArduinoをスタートさせると、写真が1枚だけ撮れる。
下は、そうやって撮った私の顔である。
……むっちゃむさくるしいなあw。
秋月電子に行ったら、「焦電センサ」が売られていた。付加回路のついたモジュールで、800円。秋月電子のサイトを見ると、他にも安いものはあるが、店頭にあるものではこれが一番安かった。
「焦電センサ」という言い方は随分通ぶった言い方で、一般的には「赤外線人感センサ」と言った方が通りがよかろう。
買ったものは5V・TTLの負ロジックで、35度くらいのものがセンサ前面の角度120°の範囲2メートル以内で動くと、出力が「負」になる。ロジック端子にプルアップ抵抗をつけて、5Vくらいをくれておいてやるとよいわけだ。
そこで、早速こんなふうにする。ブレッドボードシールド上にはプルアップ抵抗と、リード線を伸ばして3色LEDを取り付けるだけでいい。
スケッチをこんなふうに書けば、即席人感ランプの出来上がりである。
// // Pir2Led.ino // 焦電センサで遊ぶ。 // 秋月電子焦電センサモジュール「SE-10」使用 // https://satotoshio.net/blog/?p=1471 // 27.7.11 1100~ // 佐藤俊夫 // const int PIR = 8, LEDR = 9, LEDG = 10, LEDB = 11, LIGHTTIME = 5000, DULL = 100; // DULL 過敏さを抑えるため、100回くらい人感しないと点灯させない。 // LEDはアノードコモン、焦電センサは負論理なので、どっちも「負 LOW」で点灯 void setup() { pinMode(PIR, INPUT); pinMode(LEDR, OUTPUT); pinMode(LEDG, OUTPUT); pinMode(LEDB, OUTPUT); digitalWrite(LEDR, HIGH); digitalWrite(LEDG, HIGH); digitalWrite(LEDB, HIGH); } void loop() { static int pcount = 0; boolean p = HIGH; delay(20); p = digitalRead(PIR); if(p == LOW){ pcount++; } if(pcount >= DULL){ digitalWrite(LEDR, LOW); digitalWrite(LEDG, LOW); digitalWrite(LEDB, LOW); pcount = 0; delay(LIGHTTIME); }else{ digitalWrite(LEDR, HIGH); digitalWrite(LEDG, HIGH); digitalWrite(LEDB, HIGH); } }