5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

XIAO ESP32S3を監視カメラにしてみる

5
Last updated at Posted at 2026-03-01

「Seeed Studio XIAO ESP32S3 Series」を買ってきたので、とりあえずカメラで撮った画像をサーバに転送するプログラムを作っていきます。
今回もプログラマはGeminiです。

image.png


XIAO ESP32S3 Series のスペックまとめ

XIAO ESP32S3シリーズには、基本モデルの「XIAO ESP32S3」と、カメラとマイクがセットになった「XIAO ESP32S3 Sense」の2種類があります。今回は、カメラ付きの「Sense」モデルの機能使っていきます。

項目 スペック詳細 補足
MCU (マイコン) ESP32-S3R8 (Xtensa 32-bit LX7 デュアルコア, 最大240MHz) 高速な処理とAI演算(ベクトル命令)に対応
ワイヤレス通信 2.4GHz Wi-Fi (802.11 b/g/n)
Bluetooth 5.0 (BLE)/ Bluetooth Mesh
ネットワーク接続やスマホ連携が容易
メモリ 8MB PSRAM / 8MB Flashメモリ 画像処理やAIモデルの保存に十分な容量
** SD カードスロット** 32GB FAT 対応
インターフェース 1x UART
1x IIC
1x IIS
1x SPI
11x GPIOs (PWM)
9x ADC
1x User LED
1x Charge LED
1x B2B Connector (with 2 additional GPIOs)
1x Reset button
1x Boot button
各種センサーやディスプレイの接続が可能
USBポート Type-C 電源供給とプログラム書き込み用
内蔵センサ 1x OV3660 カメラセンサ
1x デジタルマイク
サイズ 21 x 17.5 mm 非常に小さく、組み込み用途に最適

開発環境の構築手順 (Arduino IDE)

Arduino IDE を使ってプログラミングする準備をします。

1. Arduino IDE のインストール

  • PC(Windows/Mac/Linux)に、Arduino公式サイトから最新の「Arduino IDE (v2.x系)」をダウンロードしてインストールします。

2. ESP32ボードパッケージの追加

  • Arduino IDEを開き、メニューの ファイル > 基本設定 (Macの場合は Arduino IDE > Settings) を開きます。
    image.png
    image.png

  • 「追加のボードマネージャーのURL」の欄に以下のURLをコピー&ペーストし、「OK」を押します。
    https://espressif.github.io/arduino-esp32/package_esp32_index.json
    image.png

3. ボードマネージャーからESP32をインストール

  • 左側のメニューアイコンから「ボードマネージャー」(基板のアイコン)をクリックします。
  • 検索窓に esp32 と入力し、「Espressif Systems」が提供しているパッケージをインストールします。
    image.png

4. ボードの選択

  • XIAO ESP32S3をUSB Type-CケーブルでPCに接続します。

  • 画面上部のボード選択プルダウンメニューから「他のボードとポートを選択」をクリックします。
    image.png

  • ボード検索窓で XIAO_ESP32S3 と入力し、リストから「XIAO_ESP32S3」を選択します。(Senseモデルの場合もこれを選びます)。

  • ポートは接続したUSBポート(COMx や /dev/cu.usbserial-xxx など)を選択します。
    image.png
    image.png

設定タブのツールメニューから「PSRAM: "OPI PSRAM"」が有効になっていることを確認してください(カメラ機能を使うために必須です)。
image.png
image.png
image.png


画像データを送信するサンプルコード

ここでは、XIAO ESP32S3 Senseのカメラで撮影した画像を、Wi-Fi経由で指定したサーバーにHTTP POSTで送信するサンプルコードを紹介します。

#include <WiFi.h>
#include <HTTPClient.h>
#include "esp_camera.h"

// ==========================================
// ご自身の環境に合わせて変更してください
// ==========================================
const char* ssid = "YOUR_WIFI_SSID";       // Wi-FiのSSID
const char* password = "YOUR_WIFI_PASSWORD"; // Wi-Fiのパスワード
const char* serverUrl = "http://your-server-address.com/upload"; // 画像の送信先URL

// XIAO ESP32S3 Sense のカメラピン定義
#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

void setup() {
  Serial.begin(115200);
  delay(1000);

  // 1. Wi-Fiに接続
  Serial.print("Connecting to Wi-Fi...");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nConnected to Wi-Fi!");

  // 2. カメラの初期設定
  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_VGA;     // 画像サイズ (VGA: 640x480)
  config.pixel_format = PIXFORMAT_JPEG;  // フォーマット (JPEG)
  config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
  config.fb_location = CAMERA_FB_IN_PSRAM; // PSRAMを使用
  config.jpeg_quality = 12;              // 画質 (数値が小さいほど高画質 0-63)
  config.fb_count = 1;

  // カメラの初期化を実行
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x\n", err);
    return;
  }
  Serial.println("Camera initialized successfully!");
}

void loop() {
  // 1. 画像をキャプチャ
  camera_fb_t * fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    delay(1000);
    return;
  }
  Serial.printf("Picture taken! Size: %d bytes\n", fb->len);

  // 2. サーバーへ画像を送信 (Wi-Fi接続時のみ)
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    http.begin(serverUrl);
    // 送信するデータがJPEG画像であることをサーバーに伝える
    http.addHeader("Content-Type", "image/jpeg"); 

    Serial.println("Sending image to server...");
    // HTTP POSTリクエストを実行し、画像を送信
    int httpResponseCode = http.POST(fb->buf, fb->len);

    if (httpResponseCode > 0) {
      Serial.printf("HTTP Response code: %d\n", httpResponseCode);
    } else {
      Serial.printf("Error code: %d\n", httpResponseCode);
    }
    http.end(); // リソースを解放
  } else {
    Serial.println("Wi-Fi Disconnected");
  }

  // 3. カメラのメモリを解放 (重要)
  esp_camera_fb_return(fb);

  // 10秒待機してから再度撮影&送信
  delay(10000); 
}

サンプルコードの動かし方

  1. コード内の YOUR_WIFI_SSIDYOUR_WIFI_PASSWORDhttp://your-server-address.com/upload の部分を実際の環境に合わせて書き換えてください。

  2. 左上の「書き込み」ボタン(右向き矢印 )をクリックして、ボードにプログラムを転送します。
    image.png

  3. 書き込みが完了したら、右上の「シリアルモニタ」(虫眼鏡のアイコン)を開き、通信速度を 115200 baud に設定すると、Wi-Fiへの接続状況や送信ステータスが確認できます。
    image.png


Server側の構築

1. 準備:PythonとFlaskのインストール

Pythonのインストール手順については省略します。
Pythonをインストールしたら、ターミナルを開き以下のコマンドを入力してFlaskをインストールします。

pip install Flask

2. Pythonサーバーのコード (server.py)

お好きな場所にフォルダを作り、その中に server.py という名前で以下のコードを保存してください。

server.py
from flask import Flask, request
import os
import datetime

app = Flask(__name__)

# 画像の保存先フォルダ名
UPLOAD_FOLDER = 'uploads'

# フォルダが存在しない場合は自動で作成
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

@app.route('/upload', methods=['POST'])
def upload_image():
    # ESP32から送られてきた生のバイナリデータ(画像)を取得
    img_data = request.data
    
    if not img_data:
        return "No data received", 400

    # ファイル名を現在時刻から生成(例: 20260228_143000.jpg)
    filename = datetime.datetime.now().strftime("%Y%m%d_%H%M%S") + ".jpg"
    filepath = os.path.join(UPLOAD_FOLDER, filename)

    # データをファイルとして保存
    with open(filepath, 'wb') as f:
        f.write(img_data)
    
    print(f"画像を保存しました: {filepath} (サイズ: {len(img_data)} bytes)")
    return "Image uploaded successfully", 200

if __name__ == '__main__':
    # host='0.0.0.0' にすることで、同一Wi-Fiネットワーク内のESP32からアクセス可能になります
    # port=3000 は待ち受けるポート番号です
    app.run(host='0.0.0.0', port=3000)

サーバーを起動

  1. コマンドプロンプト(またはターミナル)を開き、server.py を保存したフォルダに移動(cd コマンドを使用)します。
  2. 以下のコマンドを実行してサーバーを起動します。
python server.py

起動すると、Running on http://192.168.X.X:3000 のようなメッセージが表示されます。これでサーバー側は画像を受け入れる準備が完了です。

 * Serving Flask app 'server'
 * Debug mode: off
Address already in use
Port 5000 is in use by another program. Either identify and stop that program, or start the server with a different port.
On macOS, try searching for and disabling 'AirPlay Receiver' in System Settings.
nomura@M4MBA upload-web % !vi
vi server.py
nomura@M4MBA upload-web % vi server.py
nomura@M4MBA upload-web % python server.py
 * Serving Flask app 'server'
 * Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on all addresses (0.0.0.0)
 * Running on http://127.0.0.1:3000
 * Running on http://192.168.1.18:3000
Press CTRL+C to quit

ESP32側のコード(Arduino IDE)の書き換え

最後に、前回作成したArduinoコードの送信先URL(serverUrl)を、サーバーを動かしているPCのIPアドレスに書き換えます。
PCとESP32は同じWi-Fiルーターに接続されている必要があります。

PCのローカルIPアドレス(192.168.X.X など)を確認し、Arduinoコードの以下の部分を修正してください。

変更前
// 変更前
const char* serverUrl = "http://your-server-address.com/upload";
変更後
// 変更後(例:PCのIPアドレスが 192.168.1.15 の場合)
const char* serverUrl = "http://192.168.1.15:3000/upload";

書き換え後、ESP32にプログラムを書き込みます。
シリアルモニタで HTTP Response code: 200 と表示され、PCの uploads フォルダ内に写真がどんどん保存されていけば大成功です!

image.png

サーバ側のターミナルには以下のような画像を保存した旨のメッセージが繰り返し表示されます。

画像を保存しました: uploads/20260301_132226.jpg (サイズ: 13957 bytes)
192.168.1.4 - - [01/Mar/2026 13:22:26] "POST /upload HTTP/1.1" 200 -
画像を保存しました: uploads/20260301_132236.jpg (サイズ: 13983 bytes)
192.168.1.4 - - [01/Mar/2026 13:22:36] "POST /upload HTTP/1.1" 200 -
画像を保存しました: uploads/20260301_132246.jpg (サイズ: 13146 bytes)
192.168.1.4 - - [01/Mar/2026 13:22:46] "POST /upload HTTP/1.1" 200 -
画像を保存しました: uploads/20260301_132257.jpg (サイズ: 14068 bytes)
192.168.1.4 - - [01/Mar/2026 13:22:57] "POST /upload HTTP/1.1" 200 -
5
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?