編集履歴一覧に戻る
youのアイコン画像

you が 2022年09月26日07時19分25秒 に編集

初版

タイトルの変更

+

【お手軽 IoTカメラ】Spresense をインターネット越しに操作(obniz を活用)

タグの変更

+

SPRESENSE

+

spresense

+

JavaScript

+

IoT

+

obniz

+

obnizBoard1Y

+

Arduino_IDE

メイン画像の変更

メイン画像が設定されました

記事種類の変更

+

製作品

本文の変更

+

この記事では、[Spresense](https://developer.sony.com/ja/develop/spresense/) を使って、以下に書いたものを作成する手順などを書いています。 - [2022年 SPRESENSE 活用コンテスト](https://elchika.com/promotion/spresense2022/)の[チュートリアル](https://elchika.com/promotion/spresense2022/library/#nav)の 1つである[「カメラ撮影をするやり方」](https://www.youtube.com/watch?v=ZHuGDJXXk_s)を参考にして、その中で説明されているカメラ撮影の仕組みを作成 - 上記のカメラ撮影の操作を、インターネット越しの操作などで実行できる仕組みを追加(IoT に便利なデバイス「[obniz](https://obniz.com/ja/)」を介した仕組みで実現) この「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](https://obniz.com/ja/products/obnizboard/) でも問題ないです。 # 構成図 上記の部品・デバイスの構成は以下の通りです。 ![構成図](https://camo.elchika.com/2e1ba9a6c0cdafded1d18fc7582cf38fdc28c1bf/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f39643166303338632d613130612d343237612d393435312d3862313266633865313763382f64646664376338392d313831652d346535302d626661302d353930626165376463303962/) ## Spresense と obniz の接続 上記の構成図の、Spresense と obniz の接続部分について、少し補足します。 今回の構成では、Spresense と obniz のそれぞれが USB から電源をとっています。そのため、2台のデバイス間のシリアル通信を行う部分で、接続する必要があるのは「Tx、Rx、GND」のみです。 これについて、ジャンパワイヤーでデバイス間を接続した部分を、以下に示します。 ![ジャンパワイヤーでの接続部分](https://camo.elchika.com/4df056ffe204171c5967d8c7655df3150c137bb4/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f39643166303338632d613130612d343237612d393435312d3862313266633865313763382f39376534396461372d646531652d346635352d396334662d613664323730623434613933/) # ソースコード ## 実装した内容 今回用いるソースコードは、Spresense に書きこむものと、Webブラウザ上で動作させるもの(HTML+JavaScript)の 2種類があります。 まずは、それぞれのソースコードを記載してから、補足などを書いていきます。 ```cpp: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"); } } } } ``` ```html: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側の実装について、今回は簡単に動作確認などが行えるよう、「[公式の開発者コンソール](https://obniz.com/ja/console)」で動かせるものを使いました。 そして、obniz側で実装した処理は、公式ドキュメントなどに書かれている処理のいくつかを組み合わせて用いています。用いた処理は、具体的には以下の通りです。 - obniz の接続処理 - シリアル通信用の処理 - [メッセージング](https://obniz.com/ja/doc/reference/common/messaging)の受信用の処理 - [スイッチ](https://obniz.com/ja/doc/reference/common/switch)の処理(動作確認用)  なお、上記のソースコードで「【ご自身の obniz_id】」と書かれた部分は、ご自身が利用される obniz の ID を記載してください。 # インターネット越しでのカメラ撮影操作を行う手順 それでは、インターネット越しでのカメラ撮影操作を行う手順をおおまかに説明します。 ## デバイス周りの準備 まずは、デバイス周りの準備です。以下の手順で進めてください。 1. 上の「部品・デバイス」に書いていた部品・デバイスを、「構成図」の部分に記載していたとおりにセット(※ Spresense も obniz も電源が入った状態) 1. Arduino IDE を使い、上の「ソースコード」の部分に記載していた Spresense用のソースコードを、Spresense に書きこむ 1. obniz公式の開発者コンソールを開き、「開発」>「HTMLプログラム」で、上の「ソースコード」の部分に記載していた obniz用のソースコードを実行する 1. カメラの撮影操作を実行する(※ 以下に補足を記載) ## カメラの撮影操作に関する補足 ここで、上記の手順の「カメラの撮影操作」について補足します。 ### 動作テスト用の操作 今回の仕組みは、「カメラ撮影をインターネット越しに実行する」という内容にしていますが、動作テスト用に obniz に対する操作でもカメラ撮影を行えるようにしています。操作は簡単で、obniz本体のスイッチを押下するだけです。 次のインターネット経由での操作を行う前に、obniz を使ったカメラ撮影の仕組みが動くことを、この操作で確認してみましょう。 ### インターネット経由での撮影1: ブラウザを使った方法 今回、obniz を使ったインターネット経由でのカメラ撮影操作には、obniz の「メッセージング」の仕組みを使っています。このメッセージングの仕組みでは、以下の URL への GETリクエストでメッセージを送信できます(※ 送信内容で「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 のメッセージング](https://obniz.com/ja/doc/reference/common/messaging)の仕組みでは、GET/POSTリクエストを利用できれば OK です。それらを利用することができる便利な仕組みの 1つに [IFTTT](https://ifttt.com/) があります。 ここでは、IFTTT 自体の使い方の詳細説明は省きますが、IFTTT の「Then」の部分に「Webhook」を使ったアプレットを作ることで、今回の仕組みを IFTTT経由で利用することが可能になります。 以下は、Button Widget と Webhook とを組み合わせたアプレットの例です。IFTTT の Webhook の設定は「URL」に「 `https://obniz.com/obniz/【ご自身の obniz_id】/message?data=cam` 」を指定し、Method の部分で「GET」を指定するだけで良いです。 ![IFTTTの利用](https://camo.elchika.com/8f656379d34dd830461f31e22e15144528e8e5ec/687474703a2f2f73746f726167652e676f6f676c65617069732e636f6d2f656c6368696b612f76312f757365722f39643166303338632d613130612d343237612d393435312d3862313266633865313763382f65653033636464642d373033382d343239662d396138352d633135356337613030353561/) ### インターネット経由での撮影4: その他の方法(※ 概要のみ記載) その他、GET/POSTリクエストを利用できるやり方であれば、上記の 1〜3 の方法に限らず用いることができます。 例えば、各プログラミング言語で HTTPリクエストを扱える処理を書けば、この仕組みを利用できます。上でも用いている JavaScript を例にとると、[Fetch API](https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch) を用いたり、[Axios](https://axios-http.com/) などのライブラリを用いる方法があります。 # 今回の仕組みのメリット ## obniz関連 今回の構成では、Spresense をインターネット経由で操作できるようにするための仕組みに、obniz を活用しました。 Spresense に加えて、obniz も準備する必要がある構成ですが、この構成を用いることで以下のメリットが得られると考えています。 ### インターネット側からデバイスへの通信部分を簡単化できる 今回、インターネット側からの何らかの通信を受けて、Spresense のカメラ撮影を行う仕組みになっています。 この時、インターネット側から通信を受けとる部分は、何らかサーバーなどの仲介役が必要になります。この部分について、obniz を用いることで、サーバー側の準備をあまり意識することなく用意することができます。 具体的には、以下のメッセージングの仕組みを用いることで、HTTP により obniz でメッセージを受けとるエンドポイントを、自分で特別な用意をすることなく利用できます。 ●メッセージング - obniz Docs  https://obniz.com/ja/doc/reference/common/messaging ●ブラウザアプリ: クラウド実行 - 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/ # その他 ## obniz を Node.js で動かす 上記のobniz側の実装は、公式の開発者コンソールで動かせるものを使いました。しかし、これについて Node.js で動かしたいという場合もあるかと思います。 そこで、obniz 用のソースコードの Node.js版の実装も以下に記載しておきます。 ```javascript: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