ATOMS3R M12カメラキット(OV3660)を使ってみた。
小さい。単四電池と比べてみたらどのくらい小さいかわかる(24×24×23.8m)。

USB接続
オフィシャルサイトには、「工場出荷時のファームウェアには、UVC 機能と Wi-Fi 送信機能の両方が含まれています」との記載。
USB接続の画像が下の画像(右)。比較のための左の画像は、DELL Inspiron 15 3535のカメラ(0.92 メガピクセル)で撮影したもの。

Wi-Fi 接続
Wi-Fi 送信機能で接続してみる。PCのSSID「AtomS3R-M12-WiFi」に切り替え、192.168.4.1に接続した画面が下の画像。「Screen」ボタンを押すと、カメラ画像が表示される。なぜかWindows11からは接続できず、Ubuntuからは接続できた。どうしてWindows11から接続できないかは分かっていない。


上記は「工場出荷時の状態」を使った内容である。次のステップは自宅のルーターに接続することである。
参照サイト
- オフィシャルサイト
- M5AtomS3Rカメラキットを使ってみた
- 【M5Stack】カメラに映った動画をリアルタイム表示(ATOMS3R + Camera Kit x ESPNowCam)
- ATOMS3R M12を工場出荷時のファームウェアに戻すメモ
- M5Stack ATOMS3を購入
ステーションモードでWi-Fiアクセスポイントに接続する
「工場出荷時の状態」では、ルーターに接続していないので実用的でない。この製品の唯一のオフィシャル例題を使ってルーターに接続する。
環境設定
例題プログラムを利用するためのArduino IDE環境設定は、オフィシャルページの通りに実施すればよい。ここではGOODMINI2のArduino IDEバージョン2.3.4を使用した。
Arduinoボード管理
「ファイル」→「基本設定」→「追加のボードマネージャのURL」に下記を入力する。
https://static-cdn.m5stack.com/resource/arduino/package_m5stack_index.json
サイドバーで「Board Manager」を選択し、検索欄に「M5Stack」を入力する。
表示された「M5Stack by M5Stack」のインストール・ボタンをクリックする。
メニューバーから以下の順に選択し開発ボードを設定する。
「ツール」→「ボード」→「M5Stack」→「M5AtomS3」
Arduino ライブラリ管理
サイドバーから「Library Manager」を選択する。
検索フィルターに「M5AtomS3」を入力する。
表示された「M5AtomS3 by M5Stack」のインストール・ボタンをクリックする。
依存関係として他のライブラリをインストールするように求められた場合は、Install Allボタンをクリックする。
AtomS3 プログラムのコンパイルとアップロードはサイトの説明の通り。
本体横のボタンを押してダウンロードモードするところがポイント
カメラの例題プログラムに修正を加える。
例題プログラムに2点の修正を加える。具体的な修正箇所はソースコードを参照。
※画像のサイズは、QVGAである。試しにVGAに設定したら表示できなかった。
.frame_size = FRAMESIZE_QVGA,
動作確認
ブラウザで192.168.0.105にアクセスする。
Arduino IDEのコード — camera_IP_FIXED
/* camera_IP_FIXED
* サンプルプログラムcamera_copyに固定IPのWiFi設定を行ったもの。 2025.03.25
* SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
/**
* @file camera.ino
* @brief M5AtomS3R Cam Web Server
* @version 1.0
* @date 2024-09-27
*
*
* @Hardwares: M5AtomS3R Cam
* @Platform Version: Arduino M5Stack Board Manager v2.1.2
* @Notes: Remember to turn on PSRAM, otherwise the camera can't be initialized normally.
*/
#include "camera_pins.h"
#include <WiFi.h>
#include "esp_camera.h"
#define STA_MODE
// #define AP_MODE
const char* ssid = "aterm-2b4139-a"; // WiFi設定
const char* password = "3e00cfa4ba409"; // WiFi設定
IPAddress local_IP(192, 168, 0, 105); // *** 25.3.25 ***
IPAddress gateway(192, 168, 0, 1); // *** 25.3.25 ***
IPAddress subnet(255, 255, 255, 0); // *** 25.3.25 ***
IPAddress primaryDNS(192, 168, 0, 1); // *** 25.3.25 ***
WiFiServer server(80);
camera_fb_t* fb = NULL;
uint8_t* out_jpg = NULL;
size_t out_jpg_len = 0;
static void jpegStream(WiFiClient* client);
static camera_config_t camera_config = {
.pin_pwdn = PWDN_GPIO_NUM,
.pin_reset = RESET_GPIO_NUM,
.pin_xclk = XCLK_GPIO_NUM,
.pin_sscb_sda = SIOD_GPIO_NUM,
.pin_sscb_scl = SIOC_GPIO_NUM,
.pin_d7 = Y9_GPIO_NUM,
.pin_d6 = Y8_GPIO_NUM,
.pin_d5 = Y7_GPIO_NUM,
.pin_d4 = Y6_GPIO_NUM,
.pin_d3 = Y5_GPIO_NUM,
.pin_d2 = Y4_GPIO_NUM,
.pin_d1 = Y3_GPIO_NUM,
.pin_d0 = Y2_GPIO_NUM,
.pin_vsync = VSYNC_GPIO_NUM,
.pin_href = HREF_GPIO_NUM,
.pin_pclk = PCLK_GPIO_NUM,
.xclk_freq_hz = 20000000,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_RGB565,
.frame_size = FRAMESIZE_QVGA,
.jpeg_quality = 0,
.fb_count = 2,
.fb_location = CAMERA_FB_IN_PSRAM,
.grab_mode = CAMERA_GRAB_LATEST,
.sccb_i2c_port = 0,
};
void setup() {
Serial.begin(115200);
pinMode(POWER_GPIO_NUM, OUTPUT);
digitalWrite(POWER_GPIO_NUM, LOW);
delay(500);
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK) {
Serial.println("Camera Init Fail");
delay(1000);
esp_restart();
} else {
Serial.println("Camera Init Success");
}
delay(100);
#ifdef STA_MODE
WiFi.mode(WIFI_STA);
// *** 25.3.25 *** for fixeed IP Address ここから
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS)) {
Serial.println("STA Failed to configure");
}
// *** 25.3.25 *** for fixeed IP Address ここかまで
WiFi.begin(ssid, password);
WiFi.setSleep(false);
Serial.println("");
Serial.print("Connecting to ");
Serial.println(ssid);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.print("Connected to ");
Serial.println(ssid);
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
#endif
#ifdef AP_MODE
if (!WiFi.softAP(ssid, password)) {
log_e("Soft AP creation failed.");
while (1)
;
}
Serial.println("AP SSID:");
Serial.println(ssid);
Serial.println("AP PASSWORD:");
Serial.println(password);
IPAddress IP = WiFi.softAPIP();
Serial.print("AP IP address: ");
Serial.println(IP);
#endif
server.begin();
}
void loop() {
WiFiClient client = server.available(); // listen for incoming clients
if (client) { // if you get a client,
while (client.connected()) { // loop while the client's connected
if (client.available()) { // if there's bytes to read from the
jpegStream(&client);
}
}
// close the connection:
client.stop();
Serial.println("Client Disconnected.");
}
}
// used to image stream
#define PART_BOUNDARY "123456789000000000000987654321"
static const char* _STREAM_CONTENT_TYPE = "multipart/x-mixed-replace;boundary=" PART_BOUNDARY;
static const char* _STREAM_BOUNDARY = "\r\n--" PART_BOUNDARY "\r\n";
static const char* _STREAM_PART = "Content-Type: image/jpeg\r\nContent-Length: %u\r\n\r\n";
static void jpegStream(WiFiClient* client) {
Serial.println("Image stream satrt");
client->println("HTTP/1.1 200 OK");
client->printf("Content-Type: %s\r\n", _STREAM_CONTENT_TYPE);
client->println("Content-Disposition: inline; filename=capture.jpg");
client->println("Access-Control-Allow-Origin: *");
client->println();
static int64_t last_frame = 0;
if (!last_frame) {
last_frame = esp_timer_get_time();
}
for (;;) {
fb = esp_camera_fb_get();
if (fb) {
frame2jpg(fb, 255, &out_jpg, &out_jpg_len);
Serial.printf("pic size: %d\n", out_jpg_len);
client->print(_STREAM_BOUNDARY);
client->printf(_STREAM_PART, out_jpg_len);
int32_t to_sends = out_jpg_len;
int32_t now_sends = 0;
uint8_t* out_buf = out_jpg;
uint32_t packet_len = 8 * 1024;
while (to_sends > 0) {
now_sends = to_sends > packet_len ? packet_len : to_sends;
if (client->write(out_buf, now_sends) == 0) {
goto client_exit;
}
out_buf += now_sends;
to_sends -= packet_len;
}
int64_t fr_end = esp_timer_get_time();
int64_t frame_time = fr_end - last_frame;
last_frame = fr_end;
frame_time /= 1000;
Serial.printf("MJPG: %luKB %lums (%.1ffps)\r\n", (long unsigned int)(out_jpg_len / 1024),
(long unsigned int)frame_time, 1000.0 / (long unsigned int)frame_time);
if (fb) {
esp_camera_fb_return(fb);
fb = NULL;
}
if (out_jpg) {
free(out_jpg);
out_jpg = NULL;
out_jpg_len = 0;
}
} else {
Serial.println("Camera capture failed");
}
}
client_exit:
if (fb) {
esp_camera_fb_return(fb);
fb = NULL;
}
client->stop();
Serial.printf("Image stream end\r\n");
}
工場出荷の状態に戻す
オフィシャルサイトの項目「イージーローダー」にあるダウンロードをクリックすると「AtomS3R-M12-Demo-V0.1.exe」がダウンロードされる。これを起動し、現れた画面のCOMを指定してから「Burn」をクリックすと初期状態に戻る。