Skip to content

Instantly share code, notes, and snippets.

@KainokiKaede
Created July 13, 2025 01:43
Show Gist options
  • Save KainokiKaede/44c864cb6d713535426cd732b0df205d to your computer and use it in GitHub Desktop.
Save KainokiKaede/44c864cb6d713535426cd732b0df205d to your computer and use it in GitHub Desktop.
/**
* Baby-Cam with M5TimerCAM
* – local MJPEG stream for VLC/FFmpeg/Home-Assistant 等
* – no port-forwarding / 外部公開不要
*
* 必要ライブラリ:
* • M5Stack Board package ≥ v2.0.9
* • TimerCam-arduino (M5TimerCAM.h)
*/
#include "M5TimerCAM.h"
#include <WiFi.h>
#include "esp_wifi.h"
// ――― Wi-Fi 設定 ―――
const char* ssid = "WIFISSID";
const char* password = "WIFIPASS";
WiFiServer server(80);
// ――― forward 宣言 ―――
static void streamJPG(WiFiClient& client);
void setup() {
setCpuFrequencyMhz(240);
Serial.begin(115200);
TimerCAM.begin(); // 電源・I²C 周辺初期化
if (!TimerCAM.Camera.begin()) { // OV3660 初期化
Serial.println("Camera init failed!");
while (true) delay(1000);
}
// ─ カメラ画質設定 ─
sensor_t* s = TimerCAM.Camera.sensor;
s->set_pixformat(s, PIXFORMAT_JPEG);
s->set_framesize(s, FRAMESIZE_FHD);
s->set_quality(s, 16); // 0 = 最高画質 (サイズ大)
s->set_vflip(s, 1);
s->set_hmirror(s, 0);
// ─ Wi-Fi STA 接続 ─
esp_wifi_set_ps(WIFI_PS_NONE);
WiFi.mode(WIFI_STA);
WiFi.setSleep(false); // レイテンシ低減
WiFi.begin(ssid, password);
Serial.print("Connecting");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print('.');
}
Serial.println();
Serial.print("Connected, IP: ");
Serial.println(WiFi.localIP());
server.begin();
}
void loop() {
WiFiClient client = server.available();
if (!client) return;
Serial.println("New client");
// 最初の HTTP リクエスト行だけ読む
String req = client.readStringUntil('\r');
client.readStringUntil('\n'); // 改行を捨てる
if (req.startsWith("GET /stream")) {
streamJPG(client);
} else {
// シンプルな HTML ビューア
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println("Connection: close\r\n");
client.println("<!DOCTYPE html><html><body>"
"<h1>M5TimerCAM</h1>"
"<img src=\"/stream\">"
"</body></html>");
}
client.stop();
Serial.println("Client disconnected");
}
// ――― MJPEG ストリーム ―――
#define BOUNDARY "123456789ABCDEF"
static void streamJPG(WiFiClient& client) {
client.println("HTTP/1.1 200 OK");
client.printf("Content-Type: multipart/x-mixed-replace; boundary=%s\r\n", BOUNDARY);
client.println("Access-Control-Allow-Origin: *\r\n");
while (client.connected()) {
if (TimerCAM.Camera.get()) {
TimerCAM.Power.setLed(128); // LED で撮影中を示す
client.printf("--%s\r\n", BOUNDARY);
client.printf("Content-Type: image/jpeg\r\n");
client.printf("Content-Length: %u\r\n\r\n", TimerCAM.Camera.fb->len);
client.write(TimerCAM.Camera.fb->buf, TimerCAM.Camera.fb->len);
client.print("\r\n");
TimerCAM.Camera.free();
TimerCAM.Power.setLed(0);
} else {
Serial.println("Capture failed");
}
if (!client.connected()) break;
delay(10); // 転送中に他処理へ譲る
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment