[Spresense] HDR カメラで写真を撮り、YYYYMMDD-HHMMSS を含めたファイル名で microSD に保存するスケッチ
前書き
Spresense でトレイルカメラを作る!ためのスケッチです。
今回は、HDR カメラで写真を 1 枚撮り、その写真のファイル名に YYYYMMDD-HHMMSS を含めて、microSD の images ディレクトリに保存する、という内容だ。
ファイル名に YYYYMMDD-HHMMSS を含めるためには、リアルタイムクロック = RTC (Real-Time Clock) が設定されている必要があるようで、RTC に時刻を設定するために衛星から情報取得するところもコードに含めた。
RTC とは、システムの時刻を正確に記録し、電源がオフの状態でもバッテリーによって動作を継続するハードウェアで、Spresense メインボードに実装されています。
Spresense のハードウェア構成としては、Spresense メインボード + LTE 拡張ボード (LM1) + HDR カメラボードである。microSD は LTE 拡張ボードに挿入している。microSD に保存される画像のサイズは 1280 x 960 である。
LTE 拡張ボードが LM2 であっても、HDR カメラボードが普通のカメラボードでも動作するのではないかと思う。
コード
写真のファイル名が関連するところは赤字で記載した。
#include <Camera.h> #include <GNSS.h> #include <RTC.h> #include <SDHCI.h> #define BAUDRATE (115200) #define CAMERA_ID "cam01" SDClass theSD; SpGnss gnss; bool rtcInitialized = false; bool syncRtcWithGnss() { if (gnss.waitUpdate(1000)) { SpNavData navData; gnss.getNavData(&navData); if (navData.time.year >= 2000) { RtcTime rtc_time(navData.time.year, navData.time.month, navData.time.day, navData.time.hour, navData.time.minute, navData.time.sec); RTC.setTime(rtc_time); return true; } } return false; } void printError(enum CamErr err) { Serial.print("Error: "); switch(err) { case CAM_ERR_NO_DEVICE: Serial.println("No Device"); break; case CAM_ERR_ILLEGAL_DEVERR: Serial.println("Illegal device error"); break; case CAM_ERR_ALREADY_INITIALIZED: Serial.println("Already initialized"); break; case CAM_ERR_NOT_INITIALIZED: Serial.println("Not initialized"); break; case CAM_ERR_NOT_STILL_INITIALIZED: Serial.println("Still picture not initialized"); break; case CAM_ERR_CANT_CREATE_THREAD: Serial.println("Failed to create thread"); break; case CAM_ERR_INVALID_PARAM: Serial.println("Invalid parameter"); break; case CAM_ERR_NO_MEMORY: Serial.println("No memory"); break; case CAM_ERR_USR_INUSED: Serial.println("Buffer already in use"); break; case CAM_ERR_NOT_PERMITTED: Serial.println("Operation not permitted"); break; default: Serial.println("Unknown error"); break; } } void takePictureAndSave() { Serial.println("写真を撮影します。"); RtcTime now = RTC.getTime(); CamImage img = theCamera.takePicture(); if(img.isAvailable()) { char filename[64] = {0}; snprintf(filename, sizeof(filename), "/images/%s-%04d%02d%02d-%02d%02d%02d.jpg", CAMERA_ID, now.year(), now.month(), now.day(), now.hour(), now.minute(), now.second()); Serial.print("撮影した写真を保存しています。"); theSD.remove(filename); File myFile = theSD.open(filename, FILE_WRITE); myFile.write(img.getImgBuff(), img.getImgSize()); myFile.close(); Serial.println(String("写真が保存されました。") + String(filename)); } else { Serial.println("写真の撮影に失敗しました。"); } } void setup() { CamErr err; Serial.begin(BAUDRATE); while (!Serial); Serial.println("シリアル接続が確立されました。"); RTC.begin(); if (RTC.getTime().year() < 2000) { Serial.println("GNSS 衛星からの信号受信を開始します。 (数分かかる場合があります)"); if (gnss.begin() == 0 && gnss.start(COLD_START) == 0) { while (!rtcInitialized) { if (syncRtcWithGnss()) { rtcInitialized = true; // GNSS モジュールの動作停止・リソース開放 gnss.stop(); gnss.end(); Serial.println("\nGNSS から時刻情報を取得し RTC を更新しました。"); } else { delay(1000); Serial.print("."); } } } else { Serial.println("GNSS 衛星からの情報取得に失敗しました。"); } } while(!theSD.begin()) { Serial.println("microSD カードを挿入してください。"); delay(1000); } Serial.println("microSD カードの認識と準備が完了しました。"); if (!theSD.exists("/images")) { if (theSD.mkdir("/images")) { Serial.println("images ディレクトリを作成しました。"); } else { Serial.println("images ディレクトリの作成に失敗しました。"); } } Serial.println("カメラの初期化を開始します。"); err = theCamera.begin(0); if(err != CAM_ERR_SUCCESS) { printError(err); return; } Serial.println("カメラが初期化されました。"); Serial.println("画像フォーマットを設定します。"); err = theCamera.setStillPictureImageFormat( CAM_IMGSIZE_QUADVGA_H, CAM_IMGSIZE_QUADVGA_V, CAM_IMAGE_PIX_FMT_JPG); if(err != CAM_ERR_SUCCESS) { printError(err); return; } Serial.println("画像フォーマットを設定しました。"); // 写真を 1 枚撮影して保存 takePictureAndSave(); // カメラを終了 theCamera.end(); Serial.println("プログラムを終了します。"); } void loop() { // ループ処理は行わない }
トレイルカメラ用プログラムが完成したら複数の Spresense にインストールして使うことを想定している。だから、CAMERA_ID というグローバル変数にカメラ番号的な情報を設定することを意図している。上記では cam01 というのがそれだ。
この cam01 のあとにハイフンが 1 つ入り、YYYYMMDD-HHMMSS が続き、拡張子 .jpg が付く。それが microSD カードの images ディレクトリに保存される。
YYYYMMDD-HHMMSS の時刻は、実際に撮影をするコード theCamera.takePicture() の直前で、RTC.getTime() により RTC から取得するようにしている。
コードを動かした時のコンソール表示
Arduino IDE でプログラムを動かしているところ。
microSD カードに保存されているところ
E ドライブとして認識されているのが microSD カードで、images ディレクトリに保存されていることがわかるだろう。