Makato-kan が 2025年07月16日10時54分32秒 に編集
記述追加
本文の変更
>#### 目的 家庭菜園用に成長を記録するカメラをXIAO ESP32-S3 Senseで作りました。 >#### 概要 ESP32-S3で撮影したデータをftpを使ってラズベリーパイに送信します。 ラズベリーパイはシェルスクリプトを使い、データ受信後ファイル名を日付時刻付きに変更します。 カメラ画像はPSRAMに記録後,ftpでラズベリーパイに送信しているのでSDカードは使っていません。 >#### ご注意 この製作例はセキュリティがほぼ無い状態です。 フォルダ名やディレクトリ名の変更はラズパイ側とESP32側いずれも変更必要です。 >#### 部品表 | 番号 | 品名 | | |:---:|:---|:---| | 1 | ESP32-S3 Sense | カメラ付きのESP32です | | 2 | ラズベリーパイ | ネットワーク接続できればなんでも大丈夫 | | 3 | モバイルバッテリー | 低消費電流停止しなければより良いです | | 4 | Wi-Fiルーター | 2.4GのWiFiが使えれば良いです |  >#### プログラムの導入と設定(ラズベリーパイ側) ネットワーク接続できているラズベリーパイにvsftpdとinotifywait をインストールします。 ```arduino:インストールコマンド sudo apt install vsfptd sudo apt install inotify-tools ``` /etc/vsftpd.confのバックアップをとりvsftpd.confに以下の記述を追加します。 ```arduino:vsftpd.conf-orgとvsftpd.confのdiff /etc $ diff vsftpd.conf-org vsftpd.conf 25a26,28 > > allow_writeable_chroot=YES > 31c34 < #write_enable=YES --- > write_enable=YES 35c38 < #local_umask=022 --- > local_umask=022 114c117 < #chroot_local_user=YES --- > chroot_local_user=YES ```
以下のシェルスクリプトを/home/test-cam直下に作成します。
test-camというユーザ名を作成します。 以下のシェルスクリプトを/home/test-cam直下に生成します。
実行できるよう chmod 755 ./check.shとします ```arduino:check.sh #!/bin/bash WATCH_DIR="/home/test-cam" #ディレクトリの変更はESP32側のプログラムも変更必要です FILENAME="directfb.jpg" inotifywait -m -e close_write --format "%f" "$WATCH_DIR" |while read FILE do if [ "$FILE" = "$FILENAME" ]; then TIMESTAMP=$(date +"%Y-%m-%d-%H-%M") sleep 3 NEW_NAME="cam$TIMESTAMP.jpg" mv "$WATCH_DIR/$FILENAME" "$WATCH_DIR/$NEW_NAME" echo "Renamed $FILENAME to $NEW_NAME" #echoの行は自動起動する時はコメントにします fi done ``` >#### プログラムの導入と設定(ESP32-S3側) ボードマネージャーでesp32 by Espressifをインストールします ライブラリマネージャーでesp32_ftpclientをインストールします ボードのタイプは"XIAO_ESP32S3"を指定します 指定が終わったら"ツール"タブのメニューからPSRAMを選択して"OPI PSRAM"に変更します。 書き込むプログラムは2つあります、同じフォルダ内に入れて使います。 ```arduino:camera_pins.h #if defined(CAMERA_MODEL_WROVER_KIT) #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 21 #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 19 #define Y4_GPIO_NUM 18 #define Y3_GPIO_NUM 5 #define Y2_GPIO_NUM 4 #define VSYNC_GPIO_NUM 25 #define HREF_GPIO_NUM 23 #define PCLK_GPIO_NUM 22 #elif defined(CAMERA_MODEL_ESP_EYE) #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 4 #define SIOD_GPIO_NUM 18 #define SIOC_GPIO_NUM 23 #define Y9_GPIO_NUM 36 #define Y8_GPIO_NUM 37 #define Y7_GPIO_NUM 38 #define Y6_GPIO_NUM 39 #define Y5_GPIO_NUM 35 #define Y4_GPIO_NUM 14 #define Y3_GPIO_NUM 13 #define Y2_GPIO_NUM 34 #define VSYNC_GPIO_NUM 5 #define HREF_GPIO_NUM 27 #define PCLK_GPIO_NUM 25 #define LED_GPIO_NUM 22 #elif defined(CAMERA_MODEL_M5STACK_PSRAM) #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM 15 #define XCLK_GPIO_NUM 27 #define SIOD_GPIO_NUM 25 #define SIOC_GPIO_NUM 23 #define Y9_GPIO_NUM 19 #define Y8_GPIO_NUM 36 #define Y7_GPIO_NUM 18 #define Y6_GPIO_NUM 39 #define Y5_GPIO_NUM 5 #define Y4_GPIO_NUM 34 #define Y3_GPIO_NUM 35 #define Y2_GPIO_NUM 32 #define VSYNC_GPIO_NUM 22 #define HREF_GPIO_NUM 26 #define PCLK_GPIO_NUM 21 #elif defined(CAMERA_MODEL_M5STACK_V2_PSRAM) #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM 15 #define XCLK_GPIO_NUM 27 #define SIOD_GPIO_NUM 22 #define SIOC_GPIO_NUM 23 #define Y9_GPIO_NUM 19 #define Y8_GPIO_NUM 36 #define Y7_GPIO_NUM 18 #define Y6_GPIO_NUM 39 #define Y5_GPIO_NUM 5 #define Y4_GPIO_NUM 34 #define Y3_GPIO_NUM 35 #define Y2_GPIO_NUM 32 #define VSYNC_GPIO_NUM 25 #define HREF_GPIO_NUM 26 #define PCLK_GPIO_NUM 21 #elif defined(CAMERA_MODEL_M5STACK_WIDE) #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM 15 #define XCLK_GPIO_NUM 27 #define SIOD_GPIO_NUM 22 #define SIOC_GPIO_NUM 23 #define Y9_GPIO_NUM 19 #define Y8_GPIO_NUM 36 #define Y7_GPIO_NUM 18 #define Y6_GPIO_NUM 39 #define Y5_GPIO_NUM 5 #define Y4_GPIO_NUM 34 #define Y3_GPIO_NUM 35 #define Y2_GPIO_NUM 32 #define VSYNC_GPIO_NUM 25 #define HREF_GPIO_NUM 26 #define PCLK_GPIO_NUM 21 #define LED_GPIO_NUM 2 #elif defined(CAMERA_MODEL_M5STACK_ESP32CAM) #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM 15 #define XCLK_GPIO_NUM 27 #define SIOD_GPIO_NUM 25 #define SIOC_GPIO_NUM 23 #define Y9_GPIO_NUM 19 #define Y8_GPIO_NUM 36 #define Y7_GPIO_NUM 18 #define Y6_GPIO_NUM 39 #define Y5_GPIO_NUM 5 #define Y4_GPIO_NUM 34 #define Y3_GPIO_NUM 35 #define Y2_GPIO_NUM 17 #define VSYNC_GPIO_NUM 22 #define HREF_GPIO_NUM 26 #define PCLK_GPIO_NUM 21 #elif defined(CAMERA_MODEL_M5STACK_UNITCAM) #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM 15 #define XCLK_GPIO_NUM 27 #define SIOD_GPIO_NUM 25 #define SIOC_GPIO_NUM 23 #define Y9_GPIO_NUM 19 #define Y8_GPIO_NUM 36 #define Y7_GPIO_NUM 18 #define Y6_GPIO_NUM 39 #define Y5_GPIO_NUM 5 #define Y4_GPIO_NUM 34 #define Y3_GPIO_NUM 35 #define Y2_GPIO_NUM 32 #define VSYNC_GPIO_NUM 22 #define HREF_GPIO_NUM 26 #define PCLK_GPIO_NUM 21 #elif defined(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 LED_GPIO_NUM 33 #elif defined(CAMERA_MODEL_TTGO_T_JOURNAL) #define PWDN_GPIO_NUM 0 #define RESET_GPIO_NUM 15 #define XCLK_GPIO_NUM 27 #define SIOD_GPIO_NUM 25 #define SIOC_GPIO_NUM 23 #define Y9_GPIO_NUM 19 #define Y8_GPIO_NUM 36 #define Y7_GPIO_NUM 18 #define Y6_GPIO_NUM 39 #define Y5_GPIO_NUM 5 #define Y4_GPIO_NUM 34 #define Y3_GPIO_NUM 35 #define Y2_GPIO_NUM 17 #define VSYNC_GPIO_NUM 22 #define HREF_GPIO_NUM 26 #define PCLK_GPIO_NUM 21 #elif defined(CAMERA_MODEL_XIAO_ESP32S3) #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 10 #define SIOD_GPIO_NUM 40 #define SIOC_GPIO_NUM 39 #define Y9_GPIO_NUM 48 #define Y8_GPIO_NUM 11 #define Y7_GPIO_NUM 12 #define Y6_GPIO_NUM 14 #define Y5_GPIO_NUM 16 #define Y4_GPIO_NUM 18 #define Y3_GPIO_NUM 17 #define Y2_GPIO_NUM 15 #define VSYNC_GPIO_NUM 38 #define HREF_GPIO_NUM 47 #define PCLK_GPIO_NUM 13 #define LED_GPIO_NUM 21 #elif defined(CAMERA_MODEL_ESP32_CAM_BOARD) // The 18 pin header on the board has Y5 and Y3 swapped #define USE_BOARD_HEADER 0 #define PWDN_GPIO_NUM 32 #define RESET_GPIO_NUM 33 #define XCLK_GPIO_NUM 4 #define SIOD_GPIO_NUM 18 #define SIOC_GPIO_NUM 23 #define Y9_GPIO_NUM 36 #define Y8_GPIO_NUM 19 #define Y7_GPIO_NUM 21 #define Y6_GPIO_NUM 39 #if USE_BOARD_HEADER #define Y5_GPIO_NUM 13 #else #define Y5_GPIO_NUM 35 #endif #define Y4_GPIO_NUM 14 #if USE_BOARD_HEADER #define Y3_GPIO_NUM 35 #else #define Y3_GPIO_NUM 13 #endif #define Y2_GPIO_NUM 34 #define VSYNC_GPIO_NUM 5 #define HREF_GPIO_NUM 27 #define PCLK_GPIO_NUM 25 #elif defined(CAMERA_MODEL_ESP32S3_CAM_LCD) #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 40 #define SIOD_GPIO_NUM 17 #define SIOC_GPIO_NUM 18 #define Y9_GPIO_NUM 39 #define Y8_GPIO_NUM 41 #define Y7_GPIO_NUM 42 #define Y6_GPIO_NUM 12 #define Y5_GPIO_NUM 3 #define Y4_GPIO_NUM 14 #define Y3_GPIO_NUM 47 #define Y2_GPIO_NUM 13 #define VSYNC_GPIO_NUM 21 #define HREF_GPIO_NUM 38 #define PCLK_GPIO_NUM 11 #elif defined(CAMERA_MODEL_ESP32S2_CAM_BOARD) // The 18 pin header on the board has Y5 and Y3 swapped #define USE_BOARD_HEADER 0 #define PWDN_GPIO_NUM 1 #define RESET_GPIO_NUM 2 #define XCLK_GPIO_NUM 42 #define SIOD_GPIO_NUM 41 #define SIOC_GPIO_NUM 18 #define Y9_GPIO_NUM 16 #define Y8_GPIO_NUM 39 #define Y7_GPIO_NUM 40 #define Y6_GPIO_NUM 15 #if USE_BOARD_HEADER #define Y5_GPIO_NUM 12 #else #define Y5_GPIO_NUM 13 #endif #define Y4_GPIO_NUM 5 #if USE_BOARD_HEADER #define Y3_GPIO_NUM 13 #else #define Y3_GPIO_NUM 12 #endif #define Y2_GPIO_NUM 14 #define VSYNC_GPIO_NUM 38 #define HREF_GPIO_NUM 4 #define PCLK_GPIO_NUM 3 #elif defined(CAMERA_MODEL_ESP32S3_EYE) #define PWDN_GPIO_NUM -1 #define RESET_GPIO_NUM -1 #define XCLK_GPIO_NUM 15 #define SIOD_GPIO_NUM 4 #define SIOC_GPIO_NUM 5 #define Y2_GPIO_NUM 11 #define Y3_GPIO_NUM 9 #define Y4_GPIO_NUM 8 #define Y5_GPIO_NUM 10 #define Y6_GPIO_NUM 12 #define Y7_GPIO_NUM 18 #define Y8_GPIO_NUM 17 #define Y9_GPIO_NUM 16 #define VSYNC_GPIO_NUM 6 #define HREF_GPIO_NUM 7 #define PCLK_GPIO_NUM 13 #else #error "Camera model not selected" #endif ``` ```arduino:ESP32S3_take_camera.ino #include "esp_camera.h" #include "SPI.h" #define CAMERA_MODEL_XIAO_ESP32S3 // Has PSRAM #include "camera_pins.h" //ftp-client #include "Arduino.h" #include <WiFi.h> #include <ESP32_FTPClient.h> //ftp-client #define WIFI_SSID "" #define WIFI_PASS "" char ftp_server[] = ""; //サーバー名を記入します char ftp_user[] = "test-cam";//このユーザのホームディレクトリに画像ファイルを作ります char ftp_pass[] = ""; ESP32_FTPClient ftp (ftp_server,ftp_user,ftp_pass, 5000, 2); //ftp-client bool camera_sign = false; // Check camera status void setup() { // Serial.begin(9600); //モバイルバッテリー使用時はコメントにしないと起動しないです // while(!Serial); // When the serial monitor is turned on, the program starts to execute // Serial.println("Photos will begin in 30sec, please be ready0."); delay(10); 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.frame_size = FRAMESIZE_UXGA; config.pixel_format = PIXFORMAT_JPEG; // for streaming config.grab_mode = CAMERA_GRAB_WHEN_EMPTY; config.fb_location = CAMERA_FB_IN_PSRAM; config.jpeg_quality = 12; config.fb_count = 1; // if PSRAM IC present, init with UXGA resolution and higher JPEG quality // for larger pre-allocated frame buffer. if(config.pixel_format == PIXFORMAT_JPEG){ if(psramFound()){ config.jpeg_quality = 10; config.fb_count = 2; config.grab_mode = CAMERA_GRAB_LATEST; } else { // Limit the frame size when PSRAM is not available config.frame_size = FRAMESIZE_SVGA; config.fb_location = CAMERA_FB_IN_DRAM; } } else { // Best option for face detection/recognition config.frame_size = FRAMESIZE_240X240; #if CONFIG_IDF_TARGET_ESP32S3 config.fb_count = 2; #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; } camera_sign = true; // Camera initialization check passes //Serial.println("Photos will begin in 30sec, please be ready1."); //画像劣化対策 camera_fb_t *fb = esp_camera_fb_get(); delay(10); esp_camera_fb_return(fb); //画像劣化対策 } void loop() { if(camera_sign){ // Get the current time // unsigned long now = millis(); //Serial.println("Photos will begin in 30sec, please be ready2."); camera_fb_t *fb = esp_camera_fb_get(); delay(10); // camera_fb_t *fb = esp_camera_fb_get(); if (!fb) { //Serial.println("Failed to get camera frame buffer"); return; } //ftp-client start delay(100); WiFi.begin( WIFI_SSID, WIFI_PASS ); WiFi.setTxPower(WIFI_POWER_15dBm); //送信出力を変更しています //Serial.println("Connecting Wifi..."); while (WiFi.status() != WL_CONNECTED) { delay(500); // Serial.print("."); } //Serial.println(""); //Serial.print("IP address: "); //Serial.println(WiFi.localIP()); ftp.OpenConnection(); ftp.InitFile("Type I"); ftp.NewFile("directfb.jpg"); ftp.WriteData( fb->buf,fb->len); ftp.CloseFile(); esp_camera_fb_return(fb); //ftp-client end delay(100000);//タイムラプスの間隔指定です } } ``` >#### 雑記 ESP32-S3は電波の強度をデフォルトのまま使うと接続エラー出ます 電波の強さを変更しておく必要あります。 モバイルバッテリーで起動するにはSerial.begin(9600);を停止しておきます。 モバイルバッテリーが低消費電流で停止するタイプだったのでdelay()で間隔指定しています。 カメラは初回の画像が緑かぶりしていたのでsetup()の中で読み込んだのちloop()で使う画像を読み込んでいます。 これらの対策情報はネットから拾っておりますが、具体的な参照先はよく覚えていないので、参照先記載しておりません。 ArduinoIDEのシリアルモニタをつかって動作確認する時はSerial.bigin()を使えるようにしてください。 シェルスクリプトは/etc/rc.localに起動時動作するよう記述しておけば手間要らずです。 SDカードを使っている時と比べて発熱があまりありませんでした。 inotifywaitはファイルの生成や変更などを監視するコマンドでchatgptに教えてもらいました。 >#### 使い方 ラズベリーパイのコマンドラインでcheck.shを起動します。(シェルスクリプトの終了はCtrl-cで停止します)  ESP32-S3にモバイルバッテリーを接続します。 ラズベリーパイの画像を保存するフォルダをlsコマンドで見るとcam2025-07-14-10-33.jpgといったファイルができていれば成功です。 >#### 追記(2025−7−15) これはFTPでデータを送信しています。MQTTでも同様の事できそうなのでチャレンジする予定です ESP-NOWでは250バイトの制限あるので無理でした