--> -->
前回の記事で味を占め、ESP32-CAMを使って定期的に写真を撮ってサーバーに転送してみました。
小さいのに写真が撮れて、フラッシュも点いて、Wifi通信もできる優れものです。
例によってAliExpressで送料込みで900円ほどです。
https://ja.aliexpress.com/item/1005001970568846.html
このESP32-CAM(単体)は以下の点が要注意です。
上記の商品はESP32-CAM-MBという開発用ボードがセットになっており、ここにUSB接続することでプログラムを書き込めます。
HW的には特にすることは無いのでソースです。
#include "esp_camera.h"
#include <WiFiMulti.h>
#include <HTTPClient.h>
#include <SPIFFS.h>
#include <FS.h>
#define WIFI_SSID "[SSID]"
#define WIFI_PASSWORD "[PASSWORD]"
#define UPLOAD_INTERVAL 15000 // WEB upload interval(ms)
#define STRING_BOUNDARY "ABCDEFG0123456789"
#define STRING_MULTIHEAD1 "Content-Disposition: form-data; name=\"uploadFile\"; filename=\"./camera.jpg\""
#define STRING_MULTIHEAD2 "Content-Type: image/jpeg"
#define STATUS_SUCCESS 0;
#define STATUS_CAMERA_FAILED 1;
#define STATUS_SAVE_FAILED 2;
WiFiMulti wifiMulti;
//TEST
const int ledPin = 4;
constexpr int kCameraPin_PWDN = 32;
constexpr int kCameraPin_RESET = -1; // NC
constexpr int kCameraPin_XCLK = 0;
constexpr int kCameraPin_SIOD = 26;
constexpr int kCameraPin_SIOC = 27;
constexpr int kCameraPin_Y9 = 35;
constexpr int kCameraPin_Y8 = 34;
constexpr int kCameraPin_Y7 = 39;
constexpr int kCameraPin_Y6 = 36;
constexpr int kCameraPin_Y5 = 21;
constexpr int kCameraPin_Y4 = 19;
constexpr int kCameraPin_Y3 = 18;
constexpr int kCameraPin_Y2 = 5;
constexpr int kCameraPin_VSYNC = 25;
constexpr int kCameraPin_HREF = 23;
constexpr int kCameraPin_PCLK = 22;
void setupCamera() {
const camera_config_t config = {
.pin_pwdn = kCameraPin_PWDN,
.pin_reset = kCameraPin_RESET,
.pin_xclk = kCameraPin_XCLK,
.pin_sscb_sda = kCameraPin_SIOD,
.pin_sscb_scl = kCameraPin_SIOC,
.pin_d7 = kCameraPin_Y9,
.pin_d6 = kCameraPin_Y8,
.pin_d5 = kCameraPin_Y7,
.pin_d4 = kCameraPin_Y6,
.pin_d3 = kCameraPin_Y5,
.pin_d2 = kCameraPin_Y4,
.pin_d1 = kCameraPin_Y3,
.pin_d0 = kCameraPin_Y2,
.pin_vsync = kCameraPin_VSYNC,
.pin_href = kCameraPin_HREF,
.pin_pclk = kCameraPin_PCLK,
.xclk_freq_hz = 20000000,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_JPEG,
// .frame_size = FRAMESIZE_96X96,
// .frame_size = FRAMESIZE_QQVGA,
// .frame_size = FRAMESIZE_QCIF,
// .frame_size = FRAMESIZE_QCIF,
// .frame_size = FRAMESIZE_HQVGA,
// .frame_size = FRAMESIZE_240X240,
// .frame_size = FRAMESIZE_QVGA,
// .frame_size = FRAMESIZE_CIF,
// .frame_size = FRAMESIZE_HVGA,
.frame_size = FRAMESIZE_VGA,
// .frame_size = FRAMESIZE_SVGA,
// .frame_size = FRAMESIZE_XGA,
// .frame_size = FRAMESIZE_HD,
// .frame_size = FRAMESIZE_SXGA,
// .frame_size = FRAMESIZE_UXGA,
.jpeg_quality = 12,
.fb_count = 1,
};
if(kCameraPin_PWDN != -1){
pinMode(kCameraPin_PWDN, OUTPUT);
digitalWrite(kCameraPin_PWDN, LOW);
}
esp_err_t err = esp_camera_init(&config);
Serial.printf("esp_camera_init: 0x%x\n", err);
// init flash pin
pinMode(ledPin, OUTPUT);
}
void flash(bool flag) {
if(flag) {
digitalWrite(ledPin, HIGH);
} else {
digitalWrite(ledPin, LOW);
}
}
int post(camera_fb_t *fb, uint8_t msg) {
String str = "";
// Wifi
if ((wifiMulti.run() == WL_CONNECTED)) {
String post = "status=" + String(msg);
HTTPClient http;
uint8_t httpCode;
// POST
http.begin("http://[SERVER_NAME]/post.php");
if(!fb) {
char buf[512];
String post = "status=false";
post.toCharArray(buf, post.length());
http.addHeader("Content-Type", "application/x-www-form-urlencoded", false, true);
httpCode = http.POST((uint8_t*)buf, strlen(buf));
} else {
str = "multipart/form-data; boundary=";
str += STRING_BOUNDARY;
http.addHeader("Content-Type", str);
// 構造定義
String head1 = "--";
head1 +=STRING_BOUNDARY;
head1 += "\r\n";
head1 += STRING_MULTIHEAD1;
head1 += "\r\n";
head1 += STRING_MULTIHEAD2;
head1 += "\r\n\r\n";
uint32_t clen1 = head1.length();
String head2 = "\r\n--";
head2 += STRING_BOUNDARY;
head2 += "--\r\n\r\n";
uint32_t clen2 = head2.length();
uint32_t clen = clen1 + clen2 + fb->len;
uint8_t *uiB = (uint8_t *)malloc(sizeof(uint8_t)*clen);
Serial.printf("length: head1=%d, head2=%d, fb=%d, total=%d",clen1,clen2,fb->len,clen);
// POST
for(int pos=0;pos<clen1;pos++){
uiB[0+pos]=head1[pos];
}
for(int pos=0;pos<(fb->len);pos++){
uiB[clen1+pos]=(fb->buf)[pos];
}
for(int pos=0;pos<clen2;pos++){
uiB[clen1+(fb->len)+pos]=head2[pos];
}
httpCode = http.POST(uiB,clen);
free(uiB);
}
// 結果確認
if(httpCode > 0) {
Serial.printf("[HTTP] POST... code: %d\n", httpCode);
// HTTP OK
if(httpCode == HTTP_CODE_OK) {
String payload = http.getString();
Serial.println(payload);
}
} else {
Serial.printf("[HTTP] POST... failed, error: %s\n", http.errorToString(httpCode).c_str());
}
http.end();
} else {
Serial.printf("WIFI NOT CONNECTED.\n");
}
}
void setup() {
Serial.begin(115200, SERIAL_8N1);
// init camera
setupCamera();
// Wifi
wifiMulti.addAP(WIFI_SSID, WIFI_PASSWORD);
}
void loop() {
uint16_t msg = 0;
Serial.println("-----------------");
// take a picture
Serial.println("Taking a photo...");
flash(true);
camera_fb_t *fb = esp_camera_fb_get();
flash(false);
if (!fb) {
Serial.println("Camera capture failed");
msg = STATUS_CAMERA_FAILED;
post(fb, msg);
} else {
msg = STATUS_SUCCESS;
// post to wifi
Serial.printf("Picture taken.Its size was: %zu bytes.\n",fb->len);
post(fb, msg);
esp_camera_fb_return(fb);
}
delay(UPLOAD_INTERVAL);
}
カメラ部分はこのあたり*2や、GitHubのサンプル*3を参考にすれば難しくありません。
POST部分は、ファイル転送は自前で作らないといけません。こちらのサイトを参考にしました。
■Arduino HTTPClientでファイルのバイナリ送信
https://qiita.com/dzonesasaki/items/1417b917751443994ffc
こんな感じで撮れます。
17:21:33.824 -> Taking a photo... 17:21:37.806 -> [E][camera.c:1483] esp_camera_fb_get(): Failed to get the frame on time!
当初、原因不明のエラーが出て、パラメータを試行錯誤でいじったりしましたが、原因はカメラの接続不良でした。カメラ部は未接続で送られてくるので、黒いパーツを垂直に起こしてカメラをはめ込み、再び戻すのですが、正しく装着できていないと上記エラーが出ます。
指で押さえてエラーが解消する場合は、それが原因です。
Tweet日本で買いたい方はこういうのもあります。3,000円は安心料。 |
■約800円のESP32-CAMで乾電池駆動のWebカメラサーバーを立てる
https://qiita.com/Nabeshin/items/b195cad1afe99ce29f1e
■ESP32-CAMで撮影してみた
https://qiita.com/yuyakato/items/dff67d3e97277666778d
https://github.com/espressif/esp32-camera/blob/master/examples/take_picture.c