前回までで、ESP32CAMで写真が撮れる様になったので、今回はその応用として、”タイムラプス”をやって見ます。方針は、
- タイマー機能を使用して、一定時間で割り込みを発生
- 割り込み処理で写真の撮影とデータの保存
- これを規定回数繰り返す
先ずは、必要な関数を調べる。
タイマー機能
- hw_timer_t * timerBegin(uint8_t num, uint16_t divider, bool countUp);
- タイマーの初期設定
- —- 引数 ——-
- uint8_t num:タイマー番号。0~3。
- uint16_t divider:分周比。クロック周波数は80MHz。divider=80 -> 80/80M -> 1E-6 sec
- bool countUp: true -> up / false -> down
- — 戻り値 —
- タイマーのハンドラ
- void timerAlarmWrite(hw_timer_t *timer, uint64_t alarm_value, bool autoreload);
- タイマーの設定値(割り込みのタイミング)を設定
- —- 引数 ——-
- hw_timer_t *timer:タイマーのハンドラ
- uint64_t alarm_value:タイミング設定値
- bool autoreload:自動再起動 -> true
- void timerAttachInterrupt(hw_timer_t *timer, void (*fn)(void), bool edge);
- タイマー割り込み実行関数登録
- —- 引数 ——-
- hw_timer_t *timer:タイマーのハンドラ
- void (*fn)(void):割り込み処理関数ポインタ(引数無し)
- bool edge:割り込みのタイプ。ture -> edge / false -> level
- void timerAlarmEnable(hw_timer_t *timer);
- タイマ(割り込み)を開始
- —- 引数 ——-
- hw_timer_t *timer:タイマーのハンドラ
- void timerWrite(hw_timer_t *timer, uint64_t val);
- タイマーの値(現在値)を設定する。
- —- 引数 ——-
- hw_timer_t *timer:タイマーのハンドラ
- uint64_t val:スタート時間現設定値。
- void timerEnd(hw_timer_t *timer);
- タイマーを止める。
- —- 引数 ——-
- hw_timer_t *timer:タイマーのハンドラ
割り込み処理関係
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR onTimer()
{
portENTER_CRITICAL_ISR(&timeMux);
interruptCounter++;
portEXIT_CRITICAL_ISR(&timeMux);
}
- void IRAM_ATTR onTimer()
- ISR関数
- 引数を伴わないvoid型
- IRAM_ATTR属性が必要
- 関数内での処理
- portENTER_CRITICAL_ISR(&timeMux); と portEXIT_CRITICAL_ISR(&timeMux);で挟まれた内部に記述
- 例では、interruptCounter++;
- 処理は最小限に抑え、処理時間を極力短くする。
- ISR関数
スケッチ作成
これらの関数を使ってスケッチを作成しました。
#include "esp_camera.h"
#include "Arduino.h"
#include "FS.h" // SD Card ESP32
#include "SD.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
volatile int i_num=0;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
hw_timer_t * timer = NULL;
void IRAM_ATTR onTimer()
{
portENTER_CRITICAL_ISR(&timerMux);
i_num ++;
portEXIT_CRITICAL_ISR(&timerMux);
}
void setup()
{
Serial.begin(115200);
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_UXGA;
config.jpeg_quality = 10;
config.fb_count = 2;
// Init Camera
esp_camera_init(&config);
timer = timerBegin(0, 8000, true);
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, 50000, true);
timerAlarmEnable(timer);
pinMode(4, OUTPUT);
digitalWrite(4, LOW);
Serial.printf("Start\n");
}
void loop() {
int t_num=1;
String path;
File file;
camera_fb_t * fb = NULL;
while(t_num != 5)
{
if(t_num != i_num)
{
// Take Picture with Camera
fb = esp_camera_fb_get();
// Path where new picture will be saved in SD Card
path = "/picture" + String(t_num) +".jpg";
SPI.begin(sd_sck, sd_miso, sd_mosi, sd_ss);
SD.begin(sd_ss);
// save (image)
file = SD.open(path.c_str(), FILE_WRITE);
file.write(fb->buf, fb->len);
file.close();
esp_camera_fb_return(fb);
t_num=i_num;
digitalWrite(4, HIGH);
delay(10);
digitalWrite(4, LOW);
}
}
Serial.printf("End\n");
while(1) ;
- EEPROM関係を削除しました。
- 30行:volatile int i_num=0;
- 割り込み回数を、i_numでカウントする。
- volatile属性は必須。これが無いと誤動作する。
- 31行:portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
- 割り込み関数の中で使用する変数の宣言
- ほぼお約束?
- 32行:hw_timer_t * timer = NULL;
- タイマーハンドラの宣言
- 34から39行:割り込み処理関数宣言
- 処理の内容は、i_numの値を1つ増やす。
- i_numの値が割り込み回数。
- 73行から76行:タイマーの設定と割り込み関連付け
- timerBegin()の2つ目に引数は、uint16_t 。基本周波数が80MHzなので80000を指定すればタイマーの周期が1msecとなるが、80000はuint16_t型ではオーバーフロー。2^16 = 65536 以上の値はセット出来ない。
- 74行で割り込み関数を onTimerに指定
- 73と75行で割り込み時間を5秒にセット
- 76行で割り込み開始
- 84行:int t_num=1;
- メインルーティンでは、これとi_numの値を比較して割り込みの発生を判断
- 87行:camera_fb_t * fb = NULL;
- カメラ用ハンドラの宣言
- 89行:while(t_num != 5)
- 撮影の繰り返し回数。
- 91行:if(t_num != i_num)
- 割り込みを判断し、写真を撮影、SDに保存
スケッチでは、撮影終了を知らせるために、Flashを1回点灯させています。このスケッチを実行して下さい。SDに、”pictureX.jpg”、(Xが0から5)の6つのファイルが保存されているはずです。後はそれらを再生すれば、タイムラプスになるはずです。