エンジニアリング

へたれエンジニアが水耕栽培の成長記録を日々収めたいので1日に1回撮影してFTPでサーバーに保存するようにした(ESP32)

どーも まる@へたれ です。

先日に水耕栽培用の水やりマンを作ったので、今度はリモートから育てている野菜の状態を覗けると良いなぁと思って作ってみることにしました。

水やりマンはこちら

こんな感じのものです。

システムとしてはESP32-CAMというカメラ合体型のモジュールがあったので、それをパシャっと撮ってFTPに上げて成長を見守る感じにします。

FTPに上げた先はWebサーバー化してある前提で外から(or 家庭内ローカル)から見れるようにするイメージです。
※今回サーバー設定は記事のスコープ外です

最終的にはこんな感じで見れるようにしてます。

作ってみましょう

今回も出来るだけ省電力で電池駆動にしたいためESP32を使います。
いい感じにカメラも合体しているESP32-CAMという製品があったのでありがたくそれを使います。

ESP32-CAMは直接接続が出来ないのでシリアル変換モジュールで接続してコードを書き込みます。

配線をしてみる

接続は以下の形になります。

ESP32-CAMFTDI
GNDGND
5VVCC (5V)
U0RTX
U0TRX

上記と併せてコード転送時はIO0とGNDを接続しておきます。そうじゃないとエラーで書き込めないので要注意。

動かす時はIO0とGNDの接続外す必要があります。

さらに、コード転送時にエラーが発生することが多いです。その際は物理のresetボタンをいー感じに押すと何故かうまくいきます(ざ・曖昧・・・・・w)

ボードの設定はこちらです。

コードはこんな感じです

#include "esp_camera.h"
#include "soc/soc.h"          
#include "soc/rtc_cntl_reg.h"  
#include "driver/rtc_io.h"
#include <WiFi.h>
#include <WiFiClient.h>   
#include "ESP32_FTPClient.h"
#include <WiFiUdp.h>
#include "time.h"

char* ftp_server = "FTPサーバーのURL";
char* ftp_user = "ログインユーザー名";
char* ftp_pass = "パスワード";
char* ftp_path = "/var/www/html/images"; //アップロードディレクトリ

const char* WIFI_SSID = "wifi SSIDです";
const char* WIFI_PASS = "wifi SSID のパスです";


ESP32_FTPClient ftp (ftp_server,ftp_user,ftp_pass, 5000, 2);

// 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

camera_config_t config;

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); 
 
  Serial.begin(115200);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
 
  Serial.println("Connecting Wifi...");
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.println("Connecting to WiFi..");   
  }
  Serial.println("IP address: ");   
  Serial.println(WiFi.localIP());
  initCamera();
  timeClient.begin();
  timeClient.update();
  ftp.OpenConnection();
  takePhoto();

  Serial.println("deepsleep start");  
  float set_sleep_sec;
  set_sleep_sec = 24 * 60 * 60;   // sec
  esp_sleep_enable_timer_wakeup(set_sleep_sec * 1000000LL * 1.006);
  esp_deep_sleep_start();
  delay(10000);
}

void loop() {

  delay(60000);
}

void initCamera() {
  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; 
  
  if(psramFound()){
    config.frame_size = FRAMESIZE_UXGA;//FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }  
  // Init Camera
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return;
  }  
}

void takePhoto() {
   
  camera_fb_t * fb = NULL;
    fb = esp_camera_fb_get();  
  if(!fb) {
    Serial.println("Camera capture failed");
    return;
  }

  /*
   * Upload to ftp server
   */
  ftp.ChangeWorkDir(ftp_path);
  ftp.InitFile("Type I");
  String archive =  "tekephoto.jpg";
  Serial.println("file"+archive);
  int str_len = archive.length() + 1; 
 
  char char_array[str_len];
  archive.toCharArray(char_array, str_len);
  
  ftp.NewFile(char_array);
  ftp.WriteData( fb->buf, fb->len );
  ftp.CloseFile();
  
  /*
   * Free buffer
   */
  esp_camera_fb_return(fb); 
}

WIFI接続→カメラ撮影→FTPに撮影したファイルをアップロード→24hのdeepsleepという動きになります。

deepsleepを使うのでloop()には処理を書かず全てsetup()で動かしてます。

最後に

今回は保存ファイルを固定にしているので毎回上書きになってますが、日付とかにしてサーバーに保存しておけばタイムラプス的な楽しみが後で出来ると思うので、頃合いをみてそちらも試してみようと思います〜

しかし、最近こんなことばっかしているな・・・・・w