LoginSignup
4
4

More than 1 year has passed since last update.

enebularから、遠隔のM5Cameraのシャッターを押して、LINEに写真を送る。

Last updated at Posted at 2022-12-18

遠くからenebularでシャッターを押して、LINEに写真を送りたい。

遠くからの見守りを考えています。以下の方法で、見守りたいと考えました。

image.png

beebotteの準備

以下の記事を参考にBeebotte の MQTT ブローカーを立ち上げる
Beebotte の MQTT ブローカーと Node-RED の MQTT ノードでやり取りをするメモ
https://www.1ft-seabass.jp/memo/2022/02/28/connecting-beebotte-using-nodered/

https://beebotte.com/ でアカウント登録後、チャンネルを作成する。「Create New」ボタンを押す。
image.png

Create a new channelの画面内でChannel Name:camera, Resource name:shutter と入力する。後のスケッチと一致させるために、この名前を入力してください。Create channelを押す。
image.png

作ったcameraのチャネルを押す。
image.png

token_xxxxxxxxxxxxxxxxの部分をコピーして保管する。後のスケッチに書き込みます。
image.png

enebularの準備

flowに、injectノードとmqtt outノードを置いて、線でつなぐ。

image.png

injectノードをダブルクリックして編集する。
msg.payload = の右欄について、まず数値を選択する。次にその右に数値の1を入力する。後のスケッチ内で数値の1を使うので、数値の1を入れてください。

image.png

MQTT nodeをダブルクリック
新規にmqtt-brokerを追加のまま 右の鉛筆マークをクリック
image.png

プロパティ内の名前を、Beebotte
接続タブ内で
サーバー:mqtt.beebotte.com
ポート:1883
自動接続:チェック
セッションの初期化:チェック
プロトコル:MQTT V3.1.1
セッションの初期化:チェック

image.png
セキュリティタブな内で
ユーザー名:token_xxxxxxxxxxxxxxxxxxx (Beebotteのチャンネルからコピペ保管した)
パスワード:token_xxxxxxxxxxxxxxxxxxx (ユーザー名と同じものを入力)

メッセージタブは、デフォルトのまま。

右上の「追加」ボタンを押して、新規にmqtt-broker設定ノードを追加する

image.png

トピック欄に、camera/shutter (beebotteで設定したChannel Name/Resource name)を入力して、完了ボタンを押す。
image.png

flowの画面の右上の「保存」ボタンを押す。
image.png

flowの画面の正面上に、「デプロイが成功しました」のメッセージが出るはず。
image.png

これでenebularの設定終了。

LINE Notifyを設定する

この記事を参考にしてトークンを発行して、発行したトークンをコピペで保存しておく。
https://zenn.dev/protoout/articles/18-line-notify-setup

TIMER CAMERA Fに書き込む

Arduino IDEの準備

新規にmqtt-brokerを追加のまま 右の鉛筆マークをクリック
[ツール]メニューから[ボード]→[ESP32 Arduino]→[M5Stack-Timer-CAM]を選ぶ
image.png
[スケッチ]メニューから[ライブラリをインクルード]→[ライブラリを管理]を選んでライブラリマネージャーを表示する
検索をフィルタのボックスに timer camを入力して、Timer-CAM by M5Stackをインストールする。今回インストールしたバージョンは0.0.3でした。
image.png

Arduino IDEから書き込む。

以下のスケッチ内に、以下項目を入力してから書き込む。

  1. WIFI_SSID
  2. WIFI_PASS
  3. LINE Notifyのアクセストークン
  4. Beebotteで取得したチャネルトークン
  5. Beebotteで設定したChannel Name/Resource nameをトピックとして。今回の場合はcamera/shutter
#include <WiFiClientSecure.h>
#include <esp_camera.h>
#include <battery.h>
#include <camera_pins.h>

#include <PubSubClient.h> 


// MQTTの接続先(Beebotte)のURLを指定
const char *endpoint = "mqtt.beebotte.com";
// MQTTのポート番号を指定
const int port = 1883;
// Beebotteのチャネルトークンを指定
const char* channelToken = "token_xxxxxxxxxxxxxxxxx"; //←変更箇所
//メッセージを知らせるトピックを指定
char *pubTopic = "camera/shutter";  //←変更箇所 beebotteで設定したChannel Name/Resource name
//メッセージを待つトピックを指定
char *subTopic = "camera/shutter";  //←変更箇所 beebotteで設定したChannel Name/Resource name

//MQTT
WiFiClient espClient;  
PubSubClient client(espClient);

long lastReconnectAttempt = 0;
const char chars[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890";
char id[17];

// MQTTのクライアントID用としてランダムIDを生成
const char *generateID() {
  randomSeed(analogRead(0));
  int i = 0;
  for(i = 0; i < sizeof(id) - 1; i++) {
    id[i] = chars[random(sizeof(chars))];
  }
  id[sizeof(id) - 1] = '\0';
  Serial.print("clientid: ");
  Serial.println(String(id));

  return id;
}

#define WIFI_SSID       "xxxxxxxxx"   //←変更箇所
#define WIFI_PASS       "xxxxxxxxx"    //←変更箇所
#define ACCESS_TOKEN    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"    //←変更箇所 Line Notifyで取得したアクセストークンを入力する。
#define MESSAGE         "enebularでシャッターを押したよ。" //←変更箇所 写真と共に送られるメッセージ
#define CAMERA_VFLIP    1       // カメラの設置向きで変更する(1:通常 0:逆さ)
#define CAMERA_DELAY    1000   // 写真を撮るまでの待ち時間(ミリ秒)

// 通知メッセージ作成用の部品
#define BOUNDARY        "------------------------133747188241686651551404"
#define PARTS1          "\r\nContent-Disposition: form-data;"
#define PARTS2          " name=\"message\"\r\n\r\n"
#define BODY_MESSAGE    "\r\n--" BOUNDARY PARTS1 PARTS2 MESSAGE
#define PARTS3          "\r\nContent-Disposition: form-data;"
#define PARTS4          " name=\"imageFile\"; filename=\"picture.jpg\""
#define BODY_IMAGEFILE  "\r\n--" BOUNDARY PARTS3 PARTS4 "\r\n\r\n"
#define BODY_END        "\r\n--" BOUNDARY "--\r\n"



//コールバック用の関数
void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived ["); //トピック名を出力
  Serial.print(topic);
  Serial.print("] ");

  payload[length] = '\0'; //受信メッセージをString型に変換
  String msg = String((char*) payload);
  Serial.println(msg);

  //M5.Lcd.setCursor(0, 0); //カーソル位置をx=0 y=0で初期化
  //受信したメッセージの内容で分岐
  if (msg == "1") {
    Serial.println("Just send photo"); // ログに表示
    delay(CAMERA_DELAY);      // 少し待つ
    sendPhoto();              // 写真を撮ってLINEに送る
  } 
}

//切断時に再接続する
boolean reconnect() {
  //再接続を試行
  if (client.connect(generateID(), channelToken, "")) {
    //接続が完了したらpublish
    client.publish(pubTopic, "hello from m5stack");
    //サブスクライブする
    client.subscribe(subTopic);

    Serial.println("Connected to Beebotte MQTT");
    //M5.Lcd.println("Connected to Beebotte MQTT");
  }
  return client.connected();
}




boolean triggerswitch = false;      // ボタン状態フラグ

// カメラの初期化(参考:web_camのサンプルコード)
void initCamera() {
  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;
  config.frame_size = FRAMESIZE_UXGA;
  config.jpeg_quality = 10;
  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", err);
    return;
  }
  sensor_t *s = esp_camera_sensor_get();
  s->set_vflip(s, CAMERA_VFLIP);
  s->set_hmirror(s, !CAMERA_VFLIP);
}

// Wi-Fiの初期化
void initWiFi() {
  WiFi.begin(WIFI_SSID, WIFI_PASS); // Wi-Fiに接続
  Serial.print("Connecting");   // 接続中のログ表示
  while (WiFi.status() != WL_CONNECTED) { // 繋がるまでループ
    Serial.print(".");          // ログに「.」を表示
    delay(1000);                // 1秒待つ
  }
  // 接続したSSIDをログに表示
  Serial.println(String("\nConnected to ") + WiFi.SSID());
}

// 写真を撮ってLINEに送る
void sendPhoto() {
  // 写真を撮影
  camera_fb_t *fb = esp_camera_fb_get();
 
  // ボディのサイズを計算
  int content_len = strlen(BODY_MESSAGE) + strlen(BODY_IMAGEFILE)
                    + fb->len + strlen(BODY_END);

  // LINE Notify APIサーバーに接続し、POSTリクエストを送信
  WiFiClientSecure client;
  client.setInsecure();         // 証明書の検証をスキップする
  client.connect("notify-api.line.me", 443);
  client.println("POST /api/notify HTTP/1.0");
  // ヘッダーを送信
  client.println("Authorization: Bearer " ACCESS_TOKEN); 
  client.println("Content-Type: multipart/form-data; boundary=" BOUNDARY);
  client.println(String("Content-Length: ") + content_len);
  client.println();
  // ボディを送信
  client.write((uint8_t *)BODY_MESSAGE, strlen(BODY_MESSAGE));
  client.write((uint8_t *)BODY_IMAGEFILE, strlen(BODY_IMAGEFILE));
  uint8_t *send_pointer = fb->buf;
  for (int remaining = fb->len; remaining > 0; remaining -= 10240) {
    client.write(send_pointer, remaining > 10240 ? 10240 : remaining);
    send_pointer += 10240;
  }
  // ボディの終了を送信
  client.write((uint8_t *)BODY_END, strlen(BODY_END));
  // サーバーからレスポンスが返ってきたら、ログに表示し接続を終了する
  while (!client.available()) {
    delay(10);
  }
  Serial.println(String("Sent request to LINE.\nResponse : ")
                 + client.readStringUntil('\n'));
  client.stop();
}

void setup() {
  Serial.begin(115200);         // シリアルモニタの通信速度
  bat_init();                   // バッテリーの初期化
  bat_disable_output();         // USBが繋がっていない時は電源を切る
  pinMode(2, OUTPUT);           // LEDのGPIOを出力モードに設定
  digitalWrite(2, HIGH);        // LEDを点ける
  pinMode(13, INPUT);           // ドアスイッチのGPIOを入力モードに設定
  initCamera();                 // カメラの初期化
  initWiFi();                   // Wi-Fiの初期化
  client.setServer(endpoint, port); //MQTT
  client.setCallback(callback); //MQTT
}

void loop() {
  if (digitalRead(13) == HIGH) { // BUTTONを押したとき
    if (!triggerswitch) {           // BUTTON状態確認
      triggerswitch = true;         // BUTTON_ON
      Serial.println("Just send photo"); // ログに表示
      delay(CAMERA_DELAY);      // 少し待つ
      sendPhoto();              // 写真を撮ってLINEに送る
    }
  } else {
    triggerswitch = false;          // BUTTON_OFF
  }

  //接続が切れてしまった場合に再接続を試行
  if (!client.connected()) {
    long now = millis();
    if (now - lastReconnectAttempt > 5000) {
      lastReconnectAttempt = now;
      //再接続を試行
      if (reconnect()) {
        lastReconnectAttempt = 0;
      }
    }
  } else {
    //接続できている場合
    client.loop();
  }
  
  delay(100);                   // 100ミリ秒待つ
}

動作確認

物理シャッターもあると便利。M5Stack用ボタンユニットをgroveケーブルで接続します。使わないときは外しておいてもいい。
image.png

enebularからシャッターボタンを押す。
image.png

LINE Notifyで写真が来た!
image.png

(参考にした技術書籍)
1.アイデアをカタチにする!M5Stack入門&実践ガイド(技術評論社) 4.7 M5Stack同士をMQTTで連携する
2.メイカーズ工作工房 M5StackではじめるIoT電子工作(伊藤浩之著)(Gihyo Digital Publishing) Chapter3 LINEに通知できる防犯カメラを作る

(使ったIoT・パーツ)
ESP32 PSRAM Timer Camera F (OV3660)
https://www.switch-science.com/products/6786?_pos=1&_sid=cdd68811e&_ss=r
商品コード: M5STACK-U082-F ¥3,267
なお、ESP32 PSRAM Timer Camera X (OV3660)でも同じスケッチで動作します。

M5Stack用ボタンユニット [U027]
https://www.switch-science.com/products/4047?_pos=5&_sid=059291001&_ss=r
商品コード: M5STACK-BUTTON-UNIT ¥385

最後に

これで、遠隔においたカメラのシャッターをenebularで押せて、LINEで写真が入手できてガッツポーズ!!

さらに

injectノード内の繰り返しを設定すれば、指定した時間間隔、日時で自動撮影して
LINEで写真が自動入手できて、さらにガッツポーズ!!

image.png

4
4
0

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
4
4