youのアイコン画像
you 2022年09月26日作成 (2022年09月26日更新)
製作品 製作品 閲覧数 1686
you 2022年09月26日作成 (2022年09月26日更新) 製作品 製作品 閲覧数 1686

【Spresense でお手軽 IoTカメラ】obniz を活用してインターネット越しの操作を実現

【Spresense でお手軽 IoTカメラ】obniz を活用してインターネット越しの操作を実現

この記事では、Spresense を使って、以下に書いたものを作成する手順などを書いています。

この「Spresense を使ったカメラ撮影をインターネット越しに行う方法」は、自分の手持ちのデバイスを使って、Spresense のチュートリアルにあった仕組みの拡張を手軽に行えないか(※ この記事を見た方が、簡単に真似して取り組めるようにできないか)と検討して作ったものです。

部品・デバイス

今回用いた部品・デバイスは、以下のとおりです。

  • SPRESENSEメインボード x1
  • SPRESENSE拡張ボード x1
  • SPRESENSEカメラボード x1
  • SDカード x1
  • USBケーブル(Micro USB Type-B) x1
  • ジャンパワイヤー x3
  • obniz Board 1Y x1
  • USBケーブル(USB Type-C) x1

上記の obniz Board 1Y は、1Y ではない obniz Board でも問題ないです。

構成図

上記の部品・デバイスの構成は以下の通りです。

構成図

Spresense と obniz の接続

上記の構成図の、Spresense と obniz の接続部分について、少し補足します。

今回の構成では、Spresense と obniz のそれぞれが USB から電源をとっています。そのため、2台のデバイス間のシリアル通信を行う部分で、接続する必要があるのは「Tx、Rx、GND」のみです。
これについて、ジャンパワイヤーでデバイス間を接続した部分を、以下に示します。

ジャンパワイヤーでの接続部分

また今回の構成に関して、自分が何かデバイス間の接続周りでミスした時に、デバイスが故障するリスクを少し下げるという観点で、「インターフェース電圧を 3.3V」にして進めています。それに関して、Spresense側では拡張ボードでの切り替え(※ 拡張ボードのピンソケットの動作電圧設定をジャンパで行うもの)を行っており、obniz側ではこの後に出てくるソースコードの中で電圧を指定する部分で「drive: "3v"」という指定をしている部分が出てきます。

ソースコード

実装した内容

今回用いるソースコードは、Spresense に書きこむものと、Webブラウザ上で動作させるもの(HTML+JavaScript)の 2種類があります。
まずは、それぞれのソースコードを記載してから、補足などを書いていきます。

Spresense用のソースコード

#include <SDHCI.h> #include <stdio.h> #include <Camera.h> #define BAUDRATE (115200) SDClass theSD; int take_picture_count = 0; void printError(enum CamErr err) { Serial.print("Error: "); switch (err) { case CAM_ERR_NO_DEVICE: Serial.println("No Device"); break; case CAM_ERR_ILLEGAL_DEVERR: Serial.println("Illegal device error"); break; case CAM_ERR_ALREADY_INITIALIZED: Serial.println("Already initialized"); break; case CAM_ERR_NOT_INITIALIZED: Serial.println("Not initialized"); break; case CAM_ERR_NOT_STILL_INITIALIZED: Serial.println("Still picture not initialized"); break; case CAM_ERR_CANT_CREATE_THREAD: Serial.println("Failed to create thread"); break; case CAM_ERR_INVALID_PARAM: Serial.println("Invalid parameter"); break; case CAM_ERR_NO_MEMORY: Serial.println("No memory"); break; case CAM_ERR_USR_INUSED: Serial.println("Buffer already in use"); break; case CAM_ERR_NOT_PERMITTED: Serial.println("Operation not permitted"); break; default: break; } } void CamCB(CamImage img) { if (img.isAvailable()) { img.convertPixFormat(CAM_IMAGE_PIX_FMT_RGB565); Serial.print("Image data size = "); Serial.print(img.getImgSize(), DEC); Serial.print(" , "); Serial.print("buff addr = "); Serial.print((unsigned long)img.getImgBuff(), HEX); Serial.println(""); } else { Serial.println("Failed to get video stream image"); } } void setup() { pinMode(LED0, OUTPUT); pinMode(LED1, OUTPUT); CamErr err; Serial.begin(BAUDRATE); Serial2.begin(BAUDRATE); while (!Serial) { ; } while (!Serial2) { ; } Serial.println("Start!"); while (!theSD.begin()) { Serial.println("Insert SD card."); } Serial.println("Prepare camera"); err = theCamera.begin(); if (err != CAM_ERR_SUCCESS) { printError(err); } Serial.println("Start streaming"); err = theCamera.startStreaming(true, CamCB); if (err != CAM_ERR_SUCCESS) { printError(err); } Serial.println("Set Auto white balance parameter"); err = theCamera.setAutoWhiteBalanceMode(CAM_WHITE_BALANCE_DAYLIGHT); if (err != CAM_ERR_SUCCESS) { printError(err); } Serial.println("Set still picture format"); err = theCamera.setStillPictureImageFormat( CAM_IMGSIZE_QUADVGA_H, CAM_IMGSIZE_QUADVGA_V, CAM_IMAGE_PIX_FMT_JPG); if (err != CAM_ERR_SUCCESS) { printError(err); } } void loop() { String inputStr; sleep(1); if (Serial2.available()) { inputStr = Serial2.readString(); inputStr.trim(); Serial.println(inputStr); Serial2.println(inputStr); if (inputStr == "cam") { Serial.println("call takePicture()"); CamImage img = theCamera.takePicture(); if (img.isAvailable()) { char filename[16] = {0}; sprintf(filename, "PICT%03d.JPG", take_picture_count); Serial.print("Save taken picture as "); Serial.print(filename); Serial.println(""); theSD.remove(filename); File myFile = theSD.open(filename, FILE_WRITE); myFile.write(img.getImgBuff(), img.getImgSize()); myFile.close(); digitalWrite(LED0, HIGH); delay(100); digitalWrite(LED1, HIGH); delay(200); digitalWrite(LED0, LOW); delay(100); digitalWrite(LED1, LOW); delay(200); take_picture_count++; } else { Serial.println("Failed to take picture"); } } } }

obniz用のソースコード

<html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" /> <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script> <script src="https://unpkg.com/obniz@3.23.0/obniz.js" crossorigin="anonymous" ></script> </head> <body> <div id="obniz-debug"></div> <h3>Connect From Your Browser</h3> <script> const obniz = new Obniz("【ご自身の obniz_id】"); obniz.onconnect = async function () { const uart = obniz.getFreeUart(); uart.start({ tx: 0, rx: 1, gnd: 11, baud: 115200, drive: "3v", pull: null, }); obniz.switch.onchange = function (state) { if (state === "push") { console.log("pressed"); uart.send("cam"); } }; obniz.onmessage = function (message, from) { if (message === "cam") { console.log("message cam"); uart.send("cam"); } }; uart.onreceive = function (data, text) { console.log(data); console.log(text); }; }; </script> </body> </html>

ソースコードの補足

Spresense関連

Spresense側の実装のベースは、2022年 SPRESENSE 活用コンテストのチュートリアル「カメラ撮影をするやり方」の内容です(※ 実装には、Arduino IDE を用います)。ベースにしたサンプルに関して、主に以下の変更を加えています。

  • 「画像を一定枚数撮影して処理を終了する」という部分の、元の流れを削除
  • シリアル通信の 2つ目(Serial2)を追加
  • 「Serial2」から特定の文字列(「cam」という文字列)が送られてきたことを、写真撮影のトリガーにする
  • カメラ撮影が行われたことを、見た目的に分かりやすくなるよう、LED0 と LED1 を光らせる処理を追加

obniz関連

obniz側の実装について、今回は簡単に動作確認などが行えるよう、「公式の開発者コンソール」で動かせるものを使いました。

そして、obniz側で実装した処理は、公式ドキュメントなどに書かれている処理のいくつかを組み合わせて用いています。用いた処理は、具体的には以下の通りです。

なお、上記のソースコードで「【ご自身の obniz_id】」と書かれた部分は、ご自身が利用される obniz の ID を記載してください。

インターネット越しでのカメラ撮影操作を行う手順

それでは、インターネット越しでのカメラ撮影操作を行う手順をおおまかに説明します。

デバイス周りの準備

まずは、デバイス周りの準備です。以下の手順で進めてください。

  1. 上の「部品・デバイス」に書いていた部品・デバイスを、「構成図」の部分に記載していたとおりにセット(※ Spresense も obniz も電源が入った状態)
  2. Arduino IDE を使い、上の「ソースコード」の部分に記載していた Spresense用のソースコードを、Spresense に書きこむ
  3. obniz公式の開発者コンソールを開き、「開発」>「HTMLプログラム」で、上の「ソースコード」の部分に記載していた obniz用のソースコードを実行する
  4. カメラの撮影操作を実行する(※ 以下に補足を記載)

カメラの撮影操作に関する補足

ここで、上記の手順の「カメラの撮影操作」について補足します。

動作テスト用の操作

今回の仕組みは、「カメラ撮影をインターネット越しに実行する」という内容にしていますが、動作テスト用に obniz に対する操作でもカメラ撮影を行えるようにしています。操作は簡単で、obniz本体のスイッチを押下するだけです。

インターネット経由での操作を行う前に、obniz を使ったカメラ撮影の仕組みが動くことを、この操作で確認してみましょう。

インターネット経由での撮影1: ブラウザを使った方法

今回、obniz を使ったインターネット経由でのカメラ撮影操作には、obniz の「メッセージング」の仕組みを使っています。このメッセージングの仕組みでは、以下の URL への GETリクエスト(または、POSTリクエスト)でメッセージを送信できます。
※ 以下の例は、送信内容で「cam」という文字列を送る形になっています

https://obniz.com/obniz/【ご自身の obniz_id】/message?data=cam

※ 上記の URL の「【ご自身の obniz_id】」と書かれた部分は、ご自身が利用される obniz の ID を記載してください
※ これ以降の説明でも、【ご自身の obniz_id】」と書かれた部分が出てきているものは、同様にご自身が利用される obniz の ID に置きかえてください

そのため、ご自身の PC でブラウザを開いていただき、上記の URL を開くだけで、インターネット経由での撮影を行うことが可能です。

インターネット経由での撮影2: curl を使った方法

上記とは別の方法として、curl を使った方法もあります。以下のコマンドを実行することで、インターネット経由での撮影を行うことが可能です。

curl "https://obniz.com/obniz/【ご自身の obniz_id】/message?data=cam"

インターネット経由での撮影3: IFTTT を使った方法

今回利用している obniz のメッセージングの仕組みでは、GET/POSTリクエストを利用できれば OK です。それらを利用することができる便利な仕組みの 1つに IFTTT があります。IFTTT を活用することで、インターネット経由でのカメラ撮影の操作を、別の様々なサービスに結びつけることができます。

ここでは、IFTTT 自体の使い方の詳細説明は省きますが、IFTTT の「Then」の部分に「Webhook」を使ったアプレットを作ることで、今回の仕組みを IFTTT経由で利用することが可能になります。

以下は、Button Widget と Webhook とを組み合わせたアプレットの例です。IFTTT の Webhook の設定は「URL」に「 https://obniz.com/obniz/【ご自身の obniz_id】/message?data=cam 」を指定し、Method の部分で「GET」を指定するだけで良いです。
IFTTTの利用

インターネット経由での撮影4: その他の方法(※ 概要のみ記載)

その他、GET/POSTリクエストを利用できるやり方であれば、上記の 1〜3 の方法に限らず用いることができます。

例えば、各プログラミング言語で HTTPリクエストを扱える処理を書けば、この仕組みを利用できます。上でも用いている JavaScript を例にとると、Fetch API を用いたり、Axios などのライブラリを用いることで、GET/POSTリクエストを送る方法があります。

今回の仕組みのメリット

obniz関連

今回の構成では、Spresense をインターネット経由で操作できるようにするための仕組みに、obniz を活用しました。Spresense に加えて、obniz も準備する必要がある構成ですが、この構成を用いることで以下のメリットが得られると考えています。

インターネット側からデバイスへの通信部分を簡単化できる

今回、インターネット側からの何らかの通信を受けて、Spresense のカメラ撮影を行う仕組みになっています。

この時、インターネット側から通信を受けとる部分は、何らかサーバーなどの仲介役が必要になります。この部分について、obniz を用いることで、サーバー側の準備をあまり意識することなく用意することができます。
具体的には、以下のメッセージングの仕組みを用いることで、HTTP により obniz でメッセージを受けとるエンドポイントを、自分で特別な用意をすることなく利用できます。

●メッセージング - obniz Docs
 https://obniz.com/ja/doc/reference/common/messaging

その他、obniz のクラウドで提供される以下の仕組みを活用する構成も作ることができると思います。

●ブラウザアプリ: クラウド実行 - obniz Docs
 https://obniz.com/ja/doc/reference/cloud/app/browserapp-cloudexec

●Message - obniz Docs
 https://obniz.com/ja/doc/reference/websocket/message

●サーバーレス イベント - obniz Docs
 https://obniz.com/ja/doc/reference/cloud/serverless-event/

おわりに

今回、Spresense を使ったカメラ撮影を obniz を使ってインターネット越しに実行できるようにしました。
今回の記事では、そのベースになる部分をシンプルに作成しただけの内容になっています。そのため、この仕組みを応用した仕組みをさらに作っていければと思っています。

その他

obniz を Node.js で動かす

上記のobniz側の実装は、公式の開発者コンソールで動かせるものを使いました。しかし、これについて Node.js で動かしたいという場合もあるかと思います。
そこで、obniz 用のソースコードの Node.js版の実装も以下に記載しておきます。

obniz用のソースコード

const Obniz = require("obniz"); const obniz = new Obniz("【ご自身の obniz_id】"); obniz.onconnect = async function () { const uart = obniz.getFreeUart(); uart.start({ tx: 0, rx: 1, gnd: 11, baud: 115200, drive: "3v", pull: null, }); obniz.switch.onchange = function (state) { if (state === "push") { console.log("pressed"); uart.send("cam"); } }; obniz.onmessage = function (message, from) { if (message === "cam") { console.log("message cam"); uart.send("cam"); } }; uart.onreceive = function (data, text) { console.log(data); console.log(text); }; };

obniz にセンサー等を接続する

今回、obniz はインターネット経由での操作を行う際の仲介役として用いた形でした。しかし、それ以外にセンサー等を接続して処理する役割としても用いることができます(今回の記事の趣旨からはずれますが...)。

それを利用することで、Spresense でのセンサー利用などの実装例だけでなく、obniz公式の電子工作周りから実装周りまでの豊富な事例・サンプル(※ 以下に書かれたものなど)も合わせて活用できます。

●obnizスタートガイド - obniz Docs
 https://obniz.com/ja/doc/guides/

●obniz.js サンプルコード - obniz Docs
 https://obniz.com/ja/doc/sample-codes/

●ドキュメント - obniz Docs
 https://obniz.com/ja/doc/reference/

●Parts Library | obniz
 https://obniz.com/ja/sdk/parts

ログインしてコメントを投稿する