Movatterモバイル変換


[0]ホーム

URL:


Future Tech Blog
フューチャー技術ブログ
IoTカテゴリ

雀を見守るカメラを作ってみた

電子工作/IoT連載 の2本目です。

背景、はじめに。

みなさんこんにちは。フューチャーの矢野です。

もう春ですね。この季節になると実家の壁に雀が巣を作ることがあります。

今年も雀が巣を作ったなら、それを見守れたら楽しいなと思います。

そこで、雀を見守るためのカメラを試作してみました。

概要

作ったのがこれです。(デモ)
デモ

今回実現したのは下記です。

  1. LAN内ならカメラの映像が確認できる
  2. 雀がいない間はカメラの電源を落とすことで節電
  3. カメラが起動したら通知が届く
  4. かわいい雀の写真がLINEに通知される
  5. カメラの設定はスマホからできる

フロー図

フロー図

人感センサーに反応があると、カメラに電源が入ります。
カメラは起動するとまずWi-Fiに接続を試みて、失敗したらAPモードになり、APに接続することでWi-Fi設定などができるようになります。設定が終了したら、再起動して、またWi-Fiの接続を試みます。Wi-Fiの接続に成功したらば、まずLINEに通知を飛ばします。飛ばせない場合は、黙ってカメラのサーバーを立ち上げます。30秒以上センサーに反応がない場合は、カメラの電源が落ちます。ここのロジックはATTiny202のマイコン側でやっていますが、ESP32-CAMだけで実現できると思います。

使用の想定

使用の想定

カメラがアクセスするWi-Fiなどの設定はスマホからできるようにしました。カメラへのアクセスはLAN内に限定しました。インターネット越しに雀を見守ることはできません。

今回の想定は、ユーザー(私)がLINEの通知をみたあと、同じWi-Fi内にいるにスマホでカメラに接続する想定です。

回路図

回路図

常時人感センサーだけ給電されていて、人感センサーに反応があると、ほかのモジュールにも電源がいくようになっています。NchMOSFETとPchMOSFETを組み合わせて電源を入れる処理は実現しました。電源が入ったATTiny202がQ2のゲートを開けます。

これによって電源が切れないようなります。電源を切るときはQ2のゲートを閉じます。

ブレッドボードだとこうなります。
ブレッドボード
一枚のブレッドボードでサクッと作れるのは気持ちがいいですね。

パーツリスト

今回の工作で使ったパーツと、購入できるページを載せておきます。
ブレッドボードやジャンパーピンは省略します。

名称購入先例
U1焦電型赤外線センサーモジュール(焦電人感センサ)秋月電子
U2AVRマイコン ATTINY202-SSNR秋月電子
U3ESPカメラモジュール [ESP32-CAM-K]www.aitendo.com
Q1,Q2NchMOSFET 30V5A IRLML6344TRPBFTR秋月電子
Q3PchMOSFET IRLML2246TRPBF秋月電子
R1,R4,R5100kΩ抵抗秋月電子等
R239kΩ抵抗秋月電子等
R3100Ω抵抗秋月電子等
LED1何かしらのLED秋月電子等

MOSFETやATTiny202などの表面実装パッケージをブレッドボードに差し込むために下記の基板を使いました。

名称購入先例
ATTiny202用SOT-23-3 DIP化基板 (5枚入)秋月電子
MOSFET用SOP8(1.27mm)DIP変換基板 金フラッシュ (9枚入)秋月電子

ソースコード

ソースコードは下記です。
ESP32のスケッチ例、CameraWebServerに下記のような変更を加えたものです。

  1. Wi-Fi接続時にWiFiManagerを用いることで、Wi-Fi設定をスマホからできるようにした。
  2. HTTPClientを用いて、LINE NotifyのAPIを叩くようにした。

画像をHTTPClientを用いてLINEにPOSTするのですが、この記事が大変参考になりました。

camera_server.ino
#include"esp_camera.h"
#include<WiFi.h>
#include<DNSServer.h>
#include<WebServer.h>
#include<WiFiManager.h>
#include<HTTPClient.h>
#define CAMERA_MODEL_AI_THINKER
#include"camera_pins.h"

voidstartCameraServer();

#define LINE_ACCESS_TOKEN"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
#define BOUNDARY"CHUNCHUNNOTICE20220327"
HTTPClient http;
int32_tlinePost(camera_fb_t *fb ) {
String RequestURL="https://notify-api.line.me/api/notify";
http.begin(RequestURL);
if (fb)
{
String stConType ="multipart/form-data; boundary=";
stConType+=BOUNDARY;
http.addHeader("Content-Type", stConType);
String authorization ="Bearer ";
authorization += LINE_ACCESS_TOKEN;
http.addHeader("Authorization", authorization);

String stMHead="--";
stMHead += BOUNDARY;
stMHead +="\r\n";

stMHead +="Content-Disposition: form-data; name=\"message\";\r\n\r\n";
stMHead +="Camera: http://";
stMHead += WiFi.localIP().toString();
stMHead +="\r\n";

stMHead +="--";
stMHead += BOUNDARY;
stMHead +="\r\n";
stMHead +="Content-Disposition: form-data; name=\"imageFile\"; filename=\"./a.jpg\" \r\n";
stMHead +="Content-Type: image/jpeg \r\n";

stMHead +="\r\n";
uint32_t iNumMHead = stMHead.length();

String stMTail="\r\n--";
stMTail += BOUNDARY;
stMTail +="--\r\n\r\n";
uint32_t iNumMTail = stMTail.length();

uint32_t iNumTotalLen = iNumMHead + iNumMTail + fb->len;

uint8_t *uiB = (uint8_t *)malloc(sizeof(uint8_t)*iNumTotalLen);

for(int uilp=0;uilp<iNumMHead;uilp++)
uiB[0+uilp]=stMHead[uilp];
for(int uilp=0;uilp<fb->len;uilp++)
uiB[iNumMHead+uilp]=(fb->buf)[uilp];
for(int uilp=0;uilp<iNumMTail;uilp++)
uiB[iNumMHead+fb->len+uilp]=stMTail[uilp];

int32_t httpResponseCode = (int32_t)http.POST(uiB,iNumTotalLen);
http.end();
free(uiB);
Serial.print("Response Code:");
Serial.println(httpResponseCode);
Serial.print("Response Body:");
Serial.println(http.getString());
return (httpResponseCode);
}
}

voidtakePhoto() {
camera_fb_t * fb =NULL;
fb = esp_camera_fb_get();
if(!fb) Serial.println("Camera capture failed");
linePost(fb);
esp_camera_fb_return(fb);
}


voidsetup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
Serial.println();

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.pixel_format = PIXFORMAT_JPEG;

// if PSRAM IC present, init with UXGA resolution and higher JPEG quality
// for larger pre-allocated frame buffer.
if(psramFound()){
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality =10;
config.fb_count =2;
}else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality =12;
config.fb_count =1;
}

#if defined(CAMERA_MODEL_ESP_EYE)
pinMode(13, INPUT_PULLUP);
pinMode(14, INPUT_PULLUP);
#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;
}

sensor_t * s = esp_camera_sensor_get();
// initial sensors are flipped vertically and colors are a bit saturated
if (s->id.PID == OV3660_PID) {
s->set_vflip(s,1);// flip it back
s->set_brightness(s,1);// up the brightness just a bit
s->set_saturation(s,-2);// lower the saturation
}
// drop down frame size for higher initial frame rate
s->set_framesize(s, FRAMESIZE_QVGA);

#if defined(CAMERA_MODEL_M5STACK_WIDE) || defined(CAMERA_MODEL_M5STACK_ESP32CAM)
s->set_vflip(s,1);
s->set_hmirror(s,1);
#endif
WiFiManager wifiManager;
wifiManager.setConfigPortalTimeout(30);
if (!wifiManager.autoConnect("BirdWatcher","hogehoge")) {
Serial.println("failed to connect and hit timeout");
}
takePhoto();
startCameraServer();

Serial.print("Camera Ready! Use 'http://");
Serial.print(WiFi.localIP());
Serial.println("' to connect");
}

voidloop() {
// put your main code here, to run repeatedly:
delay(10000);
}

ATTINY202側のコードです。人感センサーが反応してから30秒間電源が落ちないように0pin(NchMOSFETに繋がっている)をオンにします。こちらもArduinoのブートローダーを書き込んでArduinoIDEで開発してます。

attiny202.ino
/*
PowerController for tiny 202
*/

boolkeepPassive(int digiin,int interval){
for(int i=0; i<interval; i++)
{
if (digitalRead(digiin) == HIGH)returnfalse;
delay(1000);
}
returntrue;
}

voidsetup() {
pinMode(0, OUTPUT);// POWER FLAG
pinMode(1, INPUT);// POWER BUTTON
digitalWrite(0, HIGH);
}

voidloop() {
if ( digitalRead(1)==LOW && keepPassive(1,30) ) digitalWrite(0, LOW);
}

デモ

demo.gif消費電力はテスターで測れる範囲で測った結果が下記です。電源の電圧は5Vです。
状態電流
人感センサのみ0.0mA以下(※0.0mA以下をテスターで計測することができませんでした)
人感センサ反応後、Wi-Fi起動時190mA前後
サーバー起動、待機中120mA前後
カメラ起動、配信開始200mA以上(※テスターでは200mA以上の測定ができませんでした)

上記の通り、カメラを常時起動させているよりは電気を節約できていそうですね。

まとめ

これでかわいい雀が巣を作っても見守れますね。今回はカメラ部分だけ作りました。

実際に雀を見守るとなると、巣箱と、カメラを動かす電源が必要になります。

電源にはソーラーパネルと鉛蓄電池を使おうかなと思っています。

ありがとうございました。

利用ツール・参考

  1. diagrams.net(フロー図、利用想定図作成)
  2. KiCad(回路図作成)
  3. fritzing(ブレッドボードの図作成)
  4. ffmpeg(デモ動画変換)
  5. QuickTime Player + iPhone(デモ動画撮影)
  6. Arduino HTTPClientでファイルのバイナリ送信
  7. LINE Notify API Document([POST] /api/notify)

目次

  1. 背景、はじめに。
  2. 概要
    1. フロー図
    2. 使用の想定
    3. 回路図
    4. パーツリスト
    5. ソースコード
  3. デモ
  4. まとめ
  5. 利用ツール・参考

カテゴリー


[8]ページ先頭

©2009-2025 Movatter.jp