M5Cameraで超安価なタイムラプスカメラを作る
※2019年12月18日に公開された記事の移植です。情報が古い箇所があるかもしれません。
はじめに
みなさん,スマフォでタイムラプスを撮ったことありますか?
撮っているときにポケットに手を突っ込んで「あ,撮ってるんだった...」ってなって寂しくなりますよね
そこで超安価にタイムラプスを撮れるカメラを作ってみました.
使ったもの
M5Camera魚眼レンズ
スイッチサイエンスさんでも売っています.

2,150円!!
(私は増税前だったので2,120円でした)
魚眼レンズじゃなくてもいいならば1,815円!!
以上!!!!
使ったその他のもの
100均のモバイルバッテリー
小さいカメラスタンドとスマフォを挟むやつ 今回使ったものは上記のものとは違うものです(適当に家にあったもの)
M5Cameraで遊ぶ
M5Cameraは電源をつなげると「M5FishEyeCam」というwifiが飛ぶので,それに接続
接続したスマフォとかPCのブラウザで「192.168.4.1」へアクセス
とりあえず映像が写っちゃう
もうこれだけでwebカメラですね
これを利用してもいいけども結局撮影している間インターネットに繋げられない
M5Cameraにプログラムを書き込む
自分のwifiルーターに接続
こちらのサイトを参考にしながらサンプルコードを編集していきます
サンプルが表示されない場合 それでも動かない場合,原因 出荷時に戻す(?)
mDNSの利用
このやり方はシリアルコンソールでipアドレスを見て接続しますが,今回はモバイルバッテリーでカメラを放置しておきたいので,ipアドレスではなくmDNSを利用して名前でアクセスできるようにします
「mDNS追加」と書いてあるあたりが自分が追加したものです
#include "esp_camera.h"
#include <WiFi.h>
#include <ESPmDNS.h> //mDNS追加
//
// WARNING!!! Make sure that you have either selected ESP32 Wrover Module,
// or another board which has PSRAM enabled
//
// Select camera model
//#define CAMERA_MODEL_WROVER_KIT //ここを変更
//#define CAMERA_MODEL_ESP_EYE
#define CAMERA_MODEL_M5STACK_PSRAM //ここを変更
//#define CAMERA_MODEL_M5STACK_WIDE
//#define CAMERA_MODEL_AI_THINKER
#include "camera_pins.h"
const char* ssid = "xxxxxxx"; //ご自分のルーターのSSIDに書き換えてください
const char* password = "xxxxxx"; //ご自分のルーターのパスワードに書き換えてください
const char* mdnsName = "m5camera_f"; //mDNS追加
void startCameraServer();
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
Serial.println();
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;
//init with high specs to pre-allocate larger buffers
if(psramFound()){
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12;
config.fb_count = 1;
}
#if defined(CAMERA_MODEL_ESP_EYE)
pinMode(13, INPUT_PULLUP);
pinMode(14, INPUT_PULLUP);
#endif
// camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
sensor_t * s = esp_camera_sensor_get();
//initial sensors are flipped vertically and colors are a bit saturated
if (s->id.PID == OV3660_PID) {
s->set_vflip(s, 1);//flip it back
s->set_brightness(s, 1);//up the blightness just a bit
s->set_saturation(s, -2);//lower the saturation
}
//drop down frame size for higher initial frame rate
s->set_framesize(s, FRAMESIZE_QVGA);
#if defined(CAMERA_MODEL_M5STACK_WIDE)
s->set_vflip(s, 1);
s->set_hmirror(s, 1);
#endif
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
// ---ここから---
if (!MDNS.begin(mdnsName)) {
Serial.println("Error setting up MDNS responder!");
while (1) {
delay(1000);
Serial.print(".");
}
}
Serial.println("mDNS responder started");
// ---ここまでmDNS追加---
startCameraServer();
MDNS.addService("http", "tcp", 80); //mDNS追加
Serial.print("Camera Ready! Use 'http://");
Serial.print(WiFi.localIP());
Serial.println("' to connect");
}
void loop() {
// put your main code here, to run repeatedly:
delay(10000);
}
これで同じLAN内にあるPCなどからブラウザで「m5camera_f.local」でアクセスできます!
また,こうすることで複数カメラがあっても一つの端末に集約することも可能ですね!
PCに画像保存するぞ
PCにnodeは入ってますか?
入っていないならばこちらから
今回のバージョンはv12.11.0で行いました
ではnpmで必要パッケージをインストールします
npm i axios
そしてソースコードは以下
"use strict";
const axios = require("axios");
const fs = require("fs");
function main() {
console.log("capture start");
axios
.get("http://m5camera_f.local/capture", {
responseType: "arraybuffer",
headers: {
"Content-Type": "image/jpeg"
}
})
.then(res => {
fs.writeFileSync("output.png", res.data, "binary");
console.log("save");
});
}
main(0);
これだけ
実行して画像が保存されるはず
連番で保存して動画化
動画にする際,ffmpegを使います
さらにそれをnodeで使えるようなライブラリもいれます
以下を参考に
Macならば
brew install ffmpeg
んでnodeのライブラリを入れる
npm i fluent-ffmpeg
そして以下を参考にして色々便利要素をつけたソースコードはこちらです
- https://qiita.com/livlea/items/a94df4667c0eb37d859f
- https://github.com/fluent-ffmpeg/node-fluent-ffmpeg
注意点は「.outputOptions("-pix_fmt", "yuv420p")」がないと動画化できなかったでしたね
"use strict";
const axios = require("axios");
const fs = require("fs");
const ffmpeg = require("fluent-ffmpeg");
const mDNS_NAME = "m5camera_f";
const baseURL = "http://" + mDNS_NAME + ".local";
// キャプチャ録画数
// 通信がだいたい5秒なので1枚5秒で
const num = 60;
// 保存先ディレクトリ
const dir = "./img3";
const resolusison = 8; //8:1024*768 10(max):1600*1200 0(min):160*120
const vflip = 1; //上下反転 USBが下の設定
const hmirror = 1; //左右ミラー USBが下の設定
async function main() {
console.log("setup");
// カメラ設定の通信
await axios(baseURL + "/control?var=framesize&val=" + resolusison);
await axios(baseURL + "/control?var=vflip&val=" + vflip);
await axios(baseURL + "/control?var=hmirror&val=" + hmirror);
// 保存先のディレクトリを作成
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
async function loop(_count) {
console.log(_count + " capture start");
let res = await axios.get(baseURL + "/capture", {
responseType: "arraybuffer",
headers: {
"Content-Type": "image/jpeg"
}
});
console.log(_count + " save start");
fs.writeFileSync(
dir + "/" + ("00000" + _count).slice(-6) + ".jpg",
res.data,
"binary"
);
console.log(_count + " finish");
if (_count < num) {
setTimeout(loop, 100, _count + 1);
} else {
console.log("start creating movie");
ffmpeg(dir + "/%06d.jpg")
.inputFPS(30)
.outputOptions("-pix_fmt", "yuv420p")
.outputFPS(60)
.on("end", () => {
console.log("file has been converted succesfully");
})
.on("error", e => {
console.log(e);
})
.save(dir + "/output.mp4");
}
}
loop(1);
}
main();
カメラセッティング
自分はこんな感じでセッティングしました


できた動画はこちらです!
撮影時間は大体17時前後で,約5秒に1回シャッターで30fpsの動画(動画自体は60fpsで2フレーム同じ画像)
240枚撮ったので 240/30=8秒動画
結果
タイムラプス撮影の間にスマフォをいじれるようになった!
あと,安いカメラなので画素数は多いけど感度が弱いのかなんか暗い感じ
でも逆にいい感じかも
注意
スイッチサイエンスのページでは
※カメラモジュールの長時間使用は、オーバーヒートしがちなため推奨しません。短時間での撮影をお勧めします。
だそうです
自分は30分程度でしたが全然熱くなっていませんでした
もしかしたらストリーミングの長時間撮影かもしれません
であれば,5秒に1フレームと固定されてしまいますが逆に需要があるかもですね
Discussion