masato-ka's diary

日々思ったこととか、やったことの備忘録。

RS304をWio LTEで動かすライブラリを書いてみた。

この記事について

この記事ではFUTABA社製のホビー用サーボモータRS304をWio LTE(Arduino)で動かすためのライブラリを紹介します。 現在のところWio LTE, Arduino Leonardoでの動作を確認しています。まだまだ作成中ですのですが、最低限の機能が実装できたので公開します。

ライブラリのインストール

   ライブラリは以下のGitHubリポジトリで開発しています。ダウンロードしたzipをArduino IDEに読み込ませるか、ライブラリフォルダにおくことで使えます。 利用方法はreadmeをサンプルを確認してください。

github.com

現在対応しているサーボの動作は以下の通りです。

  1. サーボの角度指定
  2. トルクのON, OFF, BREAK
  3. 角度の取得

サーボとArduinoの接続回路

以下、先人の回路を参考にして下さい。74HC125で半二重全二重の変換回路を組みます。

制限事項

 現在のところサーボのコンプライアンス制御や細かい設定は実装していません。徐々に実装していく予定です。また、最低限の動作しか確認していませんので、動作は保証できません。さらにハードウェアシリアルの利用が必要です。ソフトウェアシリアルは現在は対応していませんが、今後対応してく予定です。

まとめ

 使ってみて不具合や対応してほしい、提案などあればコメント欄やGitHubのIssueにあげていただけるとありがたいです。未実装機能の中で希望があれば、優先して実装していきたいと思います。また、プルリクエストや提案があれば対応いたします。現在のところ@lutecla16vさんがLeonardoでの動作報告とESP32へのインポートを検討されています。

WioLTEをVSCode(PlatformIO)で開発する

この記事について

この記事ではSeeed社から発売されているWio LTEをVisualStudio CodeにインストールしたPlatformIO環境で開発する方法を紹介します。PlatformIOの対応プラットフォームにWioLTEがなかったためビルドスクリプトと設定情報を用意しました。

Wio LTEについて

Wio LTEはSeeed社から発売されているLTE SIMが刺さるCortex-M4(STM32F407)の開発ボードです。I2CやUART, デジタル、アナログI/OをGrove端子で提供しています。また、Arduino互換ボードを謳い開発にはArduino IDEを使います。他のArduinoとほぼ違いなく開発ができます。その他にはEspruinoというJavaScriptで開発を行うフレームワークを使う方法もあります。GitHubリポジトリにはmbed対応のリポジトリもあるようです。   Arduinoのライブラリなどをそのまま流用したいという理由から、Arduino IDEでの開発を選択するとArduino IDEを使うことになります。しかし、Arduino IDEはビルドとバイナリの転送ができるメモ帳と言っても過言ではない使い勝手の悪さです。「()」やダブルコーテーションの入力補助やコードのサジェッションを受けることができません。また、Gitなどの構成管理ツールと言った他の開発ツールとのインテグレーションが気軽にできません。高機能なエディタやターミナルのツールを駆使することになります。  そこで今回はPlatformIOを使いVSCodeでWIo LTEの開発を行っていきます。

PlatformIO

 PlatformIOはPythonで書かれた組み込み向けの開発ツールです。ArduinoだけでなくMbedやネイティブな環境での開発をサポートしています。開発ボード(CPU)-フレームワークの関係を管理し、そのためのツールとインストールとビルド転送をラップしているイメージです。

platformio.org

 PlatformIOは前述のようにPythonで書かれており、コマンドラインで実行します。しかし、VSCodeAtomエディタにPlatformIO IDEというプラグンが用意されており各エディタにインテグレーションすることができます。  PlatformIOは前述のように開発ボードの種類(CPU)-フレームワークの関係を管理しています。逆に言うと開発ボードに対応した定義がなければPlatformIO上で開発することができません。現在対応しているプラットフォームはPlatformIOのコマンドから調べることができます。現在のところWio LTEはPlatformIOに登録されていません。またSTM32F4とArduinoフレームワークで開発を行うプラットフォームを試してみましたが、ビルドがうまくいきませんでした。そこで、Wio LTEのプラットフォーム定義を作成する必要があります。

プラットフォームを定義する

PlatformIOで独自にプラットフォームの定義を行う場合、大きく、以下の3つが必要になります。

  1. platoform.json
  2. board.json
  3. ビルドスクリプト

詳細は以下のドキュメントを参考にしていください。  Custom Development Platform — PlatformIO 3.5.2a1 documentation

platform.jsonはプラットフォームの基本的な定義を記載します。一番重要なのはどの開発ツールを利用するのか、どこからダウンロードするかです。ここでいう開発ツールは開発のためのフレームワークコンパイルや実行ファイルを作るためのビルドツール、開発ボードへ実行ファイルを転送するアップロードです。

それぞれ以下のように設定しています。

 名前 説明
oolchain-gccarmnoneeabi ARMのコンパイラリンカなどのビルドツール
framework-Seeed_STM32F4 Wio LTE用のArduinoフレームワーク Seeed社のリポジトリに公開されているものにPlatformIO用のJSONファイルを追加
tool-stm32duino DFUのアップロードツール

board.jsonは対象のボードのコンパイルオプションやCPUの設定を記載します。ビルドスクリプトから参照してい利用します。ビルドスクリプトはSConsというPython製のビルドツールを使って記載します。Makefileの置き換えとして開発されたようです。ビルドスクリプトにはコンパイル、リンクのためパスのしていや各種フラグの設定を行います。また、アップロード方法についても定義しておきます。

今回作成したプラットフォームの定義は以下のリポジトリに置いています。

github.com

利用方法

  1. PlatformIOのインストール

以下のページを参考にVSCodeへPlatformIO IDE Pluginをインストールしてください。

PlatformIO IDE for VSCode — PlatformIO 3.5.2a1 documentation

  1. Wio LTEプラットフォームの設定

以下のようにプラットホーム定義をcloneします。clone先のリポジトリはPlatformIOのホームディレクトリ配下にあるplatformsディレクトリです。macOSの場合はホームディレクトリ配下の.platformsの中になります。Windowsの場合はおそらくユーザディレクトリ配下の.platforms以下となります。

> cd $USER_HOME/.platformio/platforms
> git clone https://github.com/masato-ka/platform-wiolte.git

また、WioLTEforArduinoライブラリをlibフォルダにクローンします。

>cd $USER_HOME/.platformio/lib
>git clone https://github.com/SeeedJP/WioLTEforArduino

上記実行後VSCodeを再起動します。

  1. プロジェクトの作成

VSCodeを立ち上げるとPlatformIOのホーム画面が立ち上がるようになります。ホーム画面にある 「New Project」を選択後、Wio LTEArduinoを選択しFinishを押下します。

f:id:masato-ka:20180122221455p:plain

その後、プロジェクトが作成され、後はArduino IDEと同様にコードを書いてビルド後アップロードします。VSCodeの下側にビルドボタンとアップロードボタン、シリアルモニタのボタンが表示されるはずです。またアップロード時にはWio LTEをDFUモードにしてください。コードを書くとこのようにサジェッションが出てきます。

f:id:masato-ka:20180122221901p:plain

まとめ

簡単に書いてますが、ビルドスクリプトを作成するのに手間取りました。もしよければ使っていただけると嬉しいです。OSX環境しか試してませんが、他の環境でももれなく動くはずです。不具合があれば報告していただければ可能な範囲で対応します。公式で対応されるのが待たれます。PlatformIO本体への登録ってどうやるんだろうな。。。 また今回の内容をLTにまとめて2/6のSORACOM UG LT大会で話せればと思っています。(抽選に当たってと仕事が片付けば。。。)

AMG8833のデータを簡易サーモグラフィぽくブラウザで可視化(後編)

この記事について

この記事はAMG8833のデータを簡易サーモグラフィぽくブラウザで可視化(前編)の続きです。AMG8833のデータを使い、WEBブラウザ上でヒートマップ表示にチャレンジしています。

masato-ka.hatenablog.com

前回まではESP8266を使い、AMG8833からデータを取得するところまで紹介しました。今回はESP8266から受け取ったセンサデータをサーバ側の可視化について説明します。

サーバ側の構成について

 サーバ側の基本的な動作はESP8266からセンサデータをHTTPで受け取ります。受け取ったデータはそのままキューに送りつけます。また可視化用のブラウザからのリクエスト受け付けるとデータの可視化の非同期処理を起動します。この非同期処理は先ほどのセンサデータを送っているキューからデータを取り出し、ブラウザにSSE(Server sent event)で非同期にデータを送り続けます。前編で説明した通り、センサデータを逐次送りつけ、ブラウザの表示をリアルタイム表示させたいと思い、バックエンド(センサ受信処理)とフロントエンド(ブラウザの表示)を非同期通信で実装しました。サーバ側の受信スレッドを枯渇させないように、別スレッドとして、SSEの送信処理を実装しています。また、センサデータの受信スレッドとフロントエンドの送信処理をメッセージキューで同期させています。メッセージキューは別にサービスを建てたくなかったので、組みこみActiveMQを利用しています。  ヒートマップなどの表示処理はブラウザ側で実行します。サーバ側のバックエンドの処理はSpringBootで実装します。ブラウザ表示のフロントエンドはHTMLとAngularJSで実装しています。ブラウザは初回表示時にフロントエンドを取得し、その後、サーバからのSSEを受信する都度画面の描画を変更します。

f:id:masato-ka:20180106112641p:plain

今回実装したサーバ側コードは以下のリポジトリにアップしています。

github.com

バックエンド処理の実装

バックエンドはSpringBootで実装しています。

センサデータの受信

センサデータは[30.0,30.0,30.0,・・・・・・]JSON形式でPOSTリクエストで送信されます。これを以下のRestControllerで取得します。

@RestController
@RequestMapping("/api/v1/thermography")
public class Controller {

    private final JmsTemplate jmsTemplate;

    private final Queue queue;

    public Controller(JmsTemplate jmsTemplate, Queue queue) {
        this.jmsTemplate = jmsTemplate;
        this.queue = queue;
    }

    @PostMapping
    public void postThermoGraphyTelemetory(@RequestBody List<Double> payload) {
        jmsTemplate.convertAndSend(queue, payload); //(1)
    }

}
  • (1) 受け取ったデータはinmemory.queueと言う名前のキューに入れます。バックエンド側ではDoubleオブジェクトのリストとして表現されています。

ServerSentEvent

ServerSentEventの受信処理はRestContorollerに以下のエンドポイントを追加します。非同期処理のタイムアウト値は1分に設定します。(SSEが1分送信されなければタイムアウトします。)

    private final AsyncEmitData asyncEmitData;
    private final JmsTemplate jmsTemplate;

    private final Queue queue;

    public Controller(AsyncEmitData asyncEmitData, JmsTemplate jmsTemplate, Queue queue) {
        this.asyncEmitData = asyncEmitData;
        this.jmsTemplate = jmsTemplate;
        this.queue = queue;
    }

   @GetMapping("/sse")
    public SseEmitter getThermoGraphySSE() throws IOException, InterruptedException {
        SseEmitter emitter = new SseEmitter(60000L);
        log.info("do Async");
        asyncEmitData.streming(emitter);
        log.info("close Async");
        return emitter;
    }

実際に非同期処理を実施しているのはAsyncEmitData classで実装しています。

@Component
public class AsyncEmitData {

    private boolean isLock = true;

    private final JmsTemplate jmsTemplate;

    public AsyncEmitData(JmsTemplate jmsTemplate) {
        this.jmsTemplate = jmsTemplate;
    }


    @Async
    public void streming(SseEmitter emitter) throws IOException, InterruptedException {

        jmsTemplate.setReceiveTimeout(0);// (1)
        while (true) {
            List<Double> result = (List<Double>) jmsTemplate.receiveAndConvert("inmemory.queue"); //(2)
            emitter.send(result); //(3)
            log.info("send event1");
            if (result == null) {
                break;
            }
        }
        log.info("complete sse session.");
        emitter.complete();
    }

}

別途@EnableAsyncアノテーションを指定します。

  • (1) JmsTemplateの受信処理はデフォルトで非同期処理のため、タイムアウトを0に指定し同期的に受信待ちするようにします。
  • (2) JmsTemplate.receiveAndConvertでinmemoru.queueのデータを待ち受けします。
  • (3) キューから取得したデータをそのままSSEとして送信します。またデータがnullであればサーバから切断します。

Embedded ActiveMQの利用

組みこみActiveMQの設定です。

@Configuration
@EnableJms
public class ActiveMqConfig {

    @Bean
    public Queue queue() {
        return new ActiveMQQueue("inmemory.queue");
    }

}
  • application.yml
spring:
  activemq:
    in-memory: true
    pool:
      enabled: false
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>

ブラウザ側処理

ヒートマップの表示処理

ヒートマップの表示はTableを温度の値ごとにAngularJSで表示させます。

  • index.html
<!DOCTYPE html>
<html lang="en" ng-app="App">
<head>
    <meta charset="UTF-8">
    <title>Viaualization AMG88xx</title>
    <style>
        table {
            border: none;
        }
        td {
            width: 50px;
            height: 50px;
            border-style: none;
            border: none;
        }

        tr {
            border: none;
        }
    </style>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.min.js"></script>
    <script type=text/javascript src="./js/controller.js"></script>
</head>
<body ng-controller="ApplicationController">
<center>
<h1>Visualization.</h1>
<table bgcolor="white" >
    <tr ng-repeat="line in image">
        <td ng-repeat="pixel in line" style="background-color: rgb({{pixel[0]}},{{pixel[1]}},{{pixel[2]}})">1</td>
    </tr>
</table>
</center>
</body>
</html>
  • controller.js
ar app = angular.module('App',[]);
var es = new EventSource('/api/v1/thermography/sse');

app.controller('ApplicationController', ['$scope',function($scope){

    es.addEventListener('message', function (event) {

        var acceptData = JSON.parse(event.data);

        var temp = []

        for (var i = 0; i <= 56; i += 8) {
            temp.push(acceptData.slice(i, i + 8));
        }
        $scope.image = [];
        $scope.image = temp.map(function (line) {
            return line.map(function (temp) {
                return translateTempToRGB(temp);
            })
        });
        $scope.$apply();
    });

    data = function () {
        $scope.image = [];
        var temp = [[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
            [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]];

        $scope.image = temp.map(function (line) {
            return line.map(function (temp) {
                return translateTempToRGB(temp);
            })
        });
    }
    data();
}]);

SSEでデータを取得するたびにAngularJSで表示を切り替えています。温度の値をRGBに変換する処理translateTempTpRGBファンクションは以下のような実装です。

var translateTempToRGB = function(temp){
    var scale = 1 / 50.0;
    temp * scale;
    return colorRGBBar(temp * scale);
}

var sigmoid = function(x, gain, offset){
    var value = (x+offset)*gain;
    return ((Math.tanh(value*0.5)+1)*0.5);
}

var colorRGBBar = function(x){
    var gain = 10
    var offset_x = 0.2
    x = (x * 2) - 1
    red = sigmoid(x, gain, -1*offset_x)
    blue = 1-sigmoid(x, gain, offset_x)
    green = sigmoid(x, gain, 0.6) + (1-sigmoid(x,gain,-1*0.6))
    green = green - 1.0
    return [Math.floor(red*255),Math.floor(green*255),Math.floor(blue*255)];
}

動作

実際に動作させた動画は以下の通りです。わかりやすく氷をセンサの前にくぐらせています。また、チョキやパーなどもわかる程度にデータが取れています。


blog20180106

まとめ

今回はAMG8833のデータをESP8266で取得し、WEBブラウザで可視化するところまでやりました.AMG8833自体は指定温度での割り込み機能などがあり、センサとしては高度な処理ができそうです。また、補間処理をすることで8x8から画素を広げて表示することにもチャレンジしたいです。 サーバ側はフルスクラッチで実装していますが、クラウドサービスなどを使ってデータの取得部分を置き換えるようにしたいです。

AMG8833のデータを簡易サーモグラフィぽくブラウザで可視化(前編)

この記事について

この記事ではサーモパイルセンサ(温度センサアレイ)を利用し、サーモグラフィを作成した内容を記載しています。今回は赤外線温度センサアレイとしてAMG8833を利用しています。スイッチサイエンスさんから購入することができます。なお、この記事はデバイス側の実装までを紹介した前編です。サーバ側の実装は後編で紹介しています。(2017/01/06追記)

masato-ka.hatenablog.com

AMG8833について

AMG8833は赤外線温度センサが8x8の2次元アレイになっているセンサです。Panasonicから発売されています。エアコンなどに搭載し、室内の人の位置などを検知することを目的に作られているようです。計測温度は0度〜80度です。電源とI2Cの端子のみでデータを取得することができます。ブレークアウト基板に実装されたものは1万円程度するものがほとんどです。そんな中スイッチサイエンスさんから4000円代でContaと呼ばれるスイッチサイエンス社規格の拡張ボード向け部品として発売されています。

www.switch-science.com

もちろん、端子を使えばブレットボードに直差しで動かすことが可能です。今回はこのモジュールから取得したデータをサーモグラフィ画像ぽくヒートマップを表示します。

ヒートマップ表示の検討

初期検討

GUIのプログラミングをするのが面倒だったので、WEBブラウザで簡易的なヒートマップを作成することにしました。そのため、AMG8833からI2Cで取得したデータを何らかの方法でWEBブラウザに送る必要があります。基本方針としてはマイコンで取得したデータをWEBサービスに送り、そこからWEBブラウザに表示するようにしました。デバイスからサーバ間はHTTPでデータを送信します。デバイスからのセンサ値をサーバ側で受診後、すぐにWEBブラウザを更新したいため、サーバとWEBブラウザの通信はリアルタイムな通信を利用したいと考え、今回はSSE(Server sent event)を採用しました。まとめると今回の構成は以下の図のようになっています。

f:id:masato-ka:20180105164439p:plain

バイスの実装

AMG8833からのデータ取得

データ取得にはESP8266(WROOM-02)を利用しています。I2Cで取得したデータを外部のサーバに送りたかったのとある程度のCPUパワーが欲し句、手持ちで使えそうなものを選択しました。今回はこれ以外にもデバック用にOLEDディスプレイをI2C経由で接続しています。上記で紹介したAMG8833はI2C用のプルアップ抵抗が付属していないのでI2CのSDAとSLCラインそれぞれにプルアップ抵抗(10kΩ)を入れています。OLEDディスプレイ側についてるのでなくても問題ないかもしれませんが、念のため入れておきます。

ライブラリの利用

AMG8833はAdafruitからライブラリが公開されています。今回はこちらのライブラリを利用しました。Arduino IDEを利用している場合はIDEのライブラリの管理からインストールすることができます。

github.com

AMG8833はI2Cのアドレスを入力ピンへの入力で0x68と0x69のどちらかに指定することができます。Adafruitのライブラリはデフォルトで0x69が指定されていますが、スイッチサイエンスのボードは0x68が設定されています。(基板上に実装されている端子をショートすることで切り替えできます。)そのため、ライブラリを利用する場合はアドレスを明示的に指定する必要があります。データを読み出し、OLEDにヒートマップを読み出すコードは以下のようになります。

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_AMG88xx.h>
#include "SSD1306.h"

const int pixel_array_size = 8*8;
const float thd = 29.0;
float pixels[pixel_array_size];

Adafruit_AMG88xx amg;
SSD1306  display(0x3c, 4, 5);

void setup() {
    Serial.begin(115200);
    bool status = amg.begin(0x68);//(1)
    Serial.println("-- Thermal Camera Test --");
    if(!status){
        Serial.println("AMG88xx is not available. Please connect the sensor.");
        while(1);
    }
    //OLED Initialize.
    display.init();
    display.flipScreenVertically();

}

//Draw rectangle to OLED.
void black_rect(int x, int y, int size){
    display.drawRect(x,y,size,size);
}

//Draw rectangle to OLED.
void circle(int x, int y, int radius){
    int r = radius/2;
    display.drawCircle(x+r, y+r, r);
}

//Draw X to OLED.
void cross(int x, int y, int size){
    
    for(int i=0; i < size; ++i){
        display.setPixel(x+i, y+i);
        display.setPixel(x+size-i, y+i);
    }
}

//Draw fill rectangle to OLED.
void white_rect(int x,int y, int size){
    display.setColor(WHITE);
    display.fillRect(x,y,size,size);
}

//Draw heatmap image.
void draw_display(float pixels[], float thd){
    int x=0;
    int y=0;
    int size = 7;
    for(int i =0; i < pixel_array_size; ++i){
        if(i%8==0){y+=size;x=0;}
        x = size * (i%8);
        if(pixels[i] > 24 && pixels[i] <= 29){
            cross(x,y,size);
        }else if(pixels[i] > 29 && pixels[i] <= 34){
            circle(x,y,size);
        }else if(pixels[i] > 34){
            white_rect(x,y,size);
        }else{
            black_rect(x,y,size);  
        }
    }
    Serial.println("");
}

void loop() {
    amg.readPixels(pixels);//(2)
    Serial.println(" ");
    delay(1000);
    display.clear();
    draw_display(pixels, thd);
    display.display();
}
  • (1) AMG8833のイニシャライズ時にI2Cのアドレスを指定します。
  • (2) readPixelsメソッドにサイズが64のfloat型配列を渡し、計測値を取得します。

実行すると以下のようにOLEDに8x8のデータが表示されます。温度に応じて表示される図形が変わります。

f:id:masato-ka:20180104162232j:plain

まとめ

最終的に実装したデバイス側のソースコードGitHubに公開しています。

github.com

64要素のfloat型配列をそのままJSONとしてサーバ側に送信し続けるコードになっています。次回はこのデータを受けて、ブラウザにヒートマップを表示する実装を紹介します。

Wio LTEとRN4020を使ってBLEゲートウェイ化からのSORACOM Harvestで可視化する。

この記事について

先日SORACOMさんとSeeedさん主催の「Wio LTE ユーザイベント」と言うイベントに参加しました。その際に、会場で販売されていたWio LTEを購入したので、利用のレポートとしてこの記事を紹介します。今回はWio LTEとRN4020を接続してBLEセンサビーコンの温度情報をSORACOMのデータ可視化サービスであるSORACOM Harvestで可視化してみました。てんこ盛りの内容のようですがとても手軽に実現できます。

soracom.connpass.com

Wio LTEについて

Wio LTEArduino互換(Arduino IDEでソフトウェアが書ける)でSORACOMのSIM が刺さり単体でLTE通信ができます。また、Groveコネクタで外部のセンサや機器と接続できます。主な端子として Digital Analogの入出力端子、I2C UARTを搭載しています。Seeedさんが製造しており、日本ではSORACOMさんから購入することができます。単体の価格は9800円(税別)です。もちろん技適も通っており、LTE通信を使いセンサデータを送りたい場合には最も簡単なボードになると思います。

soracom.jp

Wio LTEの一般的な使い方はGroveコネクタにセンサを接続して、LTE経由でデータを送受信する使い方になります。今回はWio LTEに直接センサをつながずにBLEのゲートウェイとして利用します。

全体の構成

全体の構成は以下の写真のようになっています。Wio LTEのUART端子に無理やりRN4020を接続しています。また、白いものが[BLEのセンサビーコン、[Wx2Beacon]e(http://masato-ka.hatenablog.com/entry/2017/09/12/223129)です。RN4020とWx2Beaconの詳細についてはそれぞれ過去記事参照ください。 Wio LTEはシリアル経由でRN4020を操作します。RN4020はWio LTEからの指示を受けて、一定時間ごとにセントラルとしてWx2Beaconに接続し、最新のセンサ値を取得します。その後、Wio LTE側でセンサ値をデコードし、SORACOM Harvestに送っています。

f:id:masato-ka:20171212083212j:plain

  • SORACOM Harvestで可視化した温度データ。

f:id:masato-ka:20171212082230p:plain

ソースコードは以下のリポジトリに上がっています。 github.com

以下、ソースコードをかいつまんで紹介します。

RN4020との接続

過去記事で紹介したシリアルからRN4020をセントラルで使う方法をWio LTEのGrove側のシリアルから実行しただけです。Wio LTEとRN4020の結線ではRXとTXはクロスに接続してください。

masato-ka.hatenablog.com

GroveのシリアルはSerialとなります。SerialUSBはPCと接続しているUSBケーブルに割り当てられており、コンソール出力として使っています。

double get_temprature(){
  SerialUSB.println("### Connect to periferal");
  Serial.println("E,1,E6E4B1DA83BC");
  delay(2000);
  trashSerialBuffer();
  SerialUSB.println("#### Read data");
  Serial.println("CHR,0019");
  delay(500);
  copySerialBuffer(buffer);
  String raw_data = String(buffer);
  #ifdef PRINT_DEBUG
    SerialUSB.print("Dump raw_data: ");
    SerialUSB.println(raw_data.substring(2,40));
  #endif
  String data = raw_data.substring(2,40);
  double temp = templature(data.substring(2,6));
  #ifdef PRINT_DEBUG
    SerialUSB.println(temp);
  #endif
  SerialUSB.println("#### Disconnect");
  Serial.println("K");
  delay(500);
  trashSerialBuffer();
  return temp;
}

取得したセンサデータの変換

RN4020から取得されたデータは以下のような形式になっています。先頭のR,と終わりの.はバリです。それ以外は16進数表記の文字列です。つまり2文字で1byteの扱いとなります。またリトルエンディアン表記となります。

R,038C081D14070002007127420D7D1A5907650B.

Wx2Beaconの温度データは2バイト目と3バイト目に格納されています。つまりこの場合"8C08"が温度データに該当します。さらにリトルエンディアンですので"0x088C"となります。これを10進数に変換して0.01をかけると温度の値(セルシウス度)となります。以下が、変換の一連のコードです。上記のコードのようにtemplatureに文字列を渡すと変換できます。

int hex_format_char_to_decimal(const char* hex_char){
  #ifdef PRINT_DEBUG
    SerialUSB.println(hex_char);
    SerialUSB.println(sizeof(hex_char));
  #endif
  if(sizeof(hex_char) != sizeof(char)*4){return -1;}
  int result = 0;
  int base = 0;
  for(int i=0; i < 2; ++i){
    if(65 <= hex_char[i] && hex_char[i] <=70){
     result += hex_char[i]-55;
    }
    if(48 <= hex_char[i] && hex_char[i] <= 57){
     result += hex_char[i]-48;
    }
    base = (16*(1-i)==0)? 1 : 16*(1-i);
    result *= base;
  }
  #ifdef PRINT_DEBUG
    SerialUSB.println(result);
  #endif
  return result;
}


int hex_format_str_to_decimal(String str){
  #ifdef PRINT_DEBUG
    SerialUSB.println("raw:" + str);
    SerialUSB.println(str.length());
  #endif
  if(str.length() != 4){
    SerialUSB.println("break");
    return -1;
  }

  int lsb = hex_format_char_to_decimal(str.substring(0,2).c_str());
  int msb = hex_format_char_to_decimal(str.substring(2,4).c_str());
  if(msb == -1 || lsb == -1){return -1;}
  return (msb<<8) + lsb;
}


double templature(String raw){
  return hex_format_str_to_decimal(raw.c_str()) * 0.01;
}

SORACOM Harvestへの接続

SORACOM Harvestへの接続はWioLTEのサンプルコード通りです。そちらを参考にしていただいた方が良いと思います。 UDPのソケットを開き8541に送りつけます。ユーザやSIMの設定はSORACOMの管理画面から設定しているため不要のようです。これだけで マネージドな可視化サービスに遅れるのは非常にすぐれものです。

void send_to_harvest(char *data){
  int connectId;
  SerialUSB.println("### Open.");
  connectId = Wio.SocketOpen("harvest.soracom.io", 8514, WIOLTE_UDP);
  if (connectId < 0) {
    SerialUSB.println("### ERROR! ###");
    return;  
  }

  SerialUSB.println("### Send.");
  SerialUSB.print("Send:");
  SerialUSB.print(data);
  SerialUSB.println("");
  if (!Wio.SocketSend(connectId, data)) {
    SerialUSB.println("### ERROR! ###");
    return;
  }

  SerialUSB.println("### Receive.");
  int length;
  do {
    length = Wio.SocketReceive(connectId, data, sizeof (data));
    if (length < 0) {
      SerialUSB.println("### ERROR! ###");
      return;
    }
  } while (length == 0);
  SerialUSB.print("Receive:");
  SerialUSB.print(data);
  SerialUSB.println("");

  SerialUSB.println("### Close.");
  if (!Wio.SocketClose(connectId)) {
    SerialUSB.println("### ERROR! ###");
    return;
  }
}

動作の全体像。

これまで紹介したメソッドはloopメソッドで以下のように使われています。 

void loop() {
  char data[100];
  
  double temp = get_temprature();
  sprintf(data,"{\"temp\":%.1f}", temp);
  SerialUSB.println(data);

  send_to_harvest(data);
  
  delay(600000L);
}

まとめ

Wio LTEは簡単にネットワークにつなげ、SORACOMのサービスを使うライブラリも充実しているので、LTE環境で何かしようとする際にはとても強力なツールになりそうです。また、RN4020とつなぐことでIoTゲートウェイっぽい使い方ができるので、利用シーンも広がりそうです。今年の冬休みの工作にいかがでしょうか。またWx2Beaconの温度データだけ使いましたが他にもセンサがついてるので多くの情報の可視化にチャレンジしたいです。

RN4020をBLEセントラルとして利用する。

この記事について

この記事ではRN4020をBLEセントラルデバイスとして利用し、BLEのペリフェラルバイスとやりとりするための方法を紹介します。これまではRN4020をペリフェラルバイスとして利用してきましたが、RN4020自信はセントラルとしても動作することが可能です。

masato-ka.hatenablog.com

RN4020のセントラルとしての利用方法

セントラルとして活用するには以下の方法で行います。

1. セントラル動作モードの設定

RN4020をセントラルとして活用するためにはSRコマンドで動作モードを変更するだけで利用できます。

SR,80000000
R,1

以上でRN4020がセントラルデバイスとして動作始めます。

2. デバイスの検索

続いて、ペリフェラルバイスの検索を行い、接続したいデバイスを発見します。今回は接続対象のデバイスとしてWx2Beaconを利用します。Wx2Beaconの詳細については以下の記事を確認してください。

masato-ka.hatenablog.com

ペリフェラルバイスの検索を行うためにはFコマンドを実行します。以下のように結果が表示されます。

F
62CEEEEE4D99,1,,,-4E
579E9EEEE4DB,0,,11111101C75C5666666A678911111111,-52
E6E4B1DA83BC,1,,,-14
FEA458B80011,1,,,-43

データは「,」区切りのデータとして出力れます。先頭がデバイスのUUIDとなります。また、2つ目はアドレスの種類を表します。接続のために必要になるためこの2つを控えておきます。一番最後がRSSI(電波強度)となります。目的のデバイスをRN4020に最も近づけておき、一番値が大きい(ここでは-14)デバイスを探すとわかりやすいです。今回は「E6E4B1DA83BC,1」を値として記録しておきます。

目的のデバイスが見つかったらXコマンドを使いスキャンを停止します。

3. デバイスへの接続 

目的のデバイスが見つかったらEコマンドを使い接続します。デバイスの検索で記録した「E6E4B1DA83BC,1」を使い接続します。

E,1,E6E4B1DA83BC
AOK
Connected
ConnParam:0028,0004,0190

バイス検索時とは逆に入力します。接続を切断する場合はKを実行します。

K
Connection End

4. キャラクタリスティックの操作

サービス又はキャラクタリスティックの検索はペリフェラルとして利用するのと同様LS又はLCを利用します。ペリフェラルとして利用するときは自身に設定されたサービスとキャラクタリスティックを表示していましたが、セントラルとして動作する場合は接続先デバイスのサービスとキャラクタリスティックを表示します。  今回の場合は以下のように表示されます。

LC
180A
  2A29,000E,02
  2A24,0010,02
  2A25,0012,02
  2A27,0014,02
  2A26,0016,02
0C4C3000770046F4AA96D5E974E32A54
  0C4C3001770046F4AA96D5E974E32A54,0019,02
  0C4C3001770046F4AA96D5E974E32A54,001A,10
  0C4C3002770046F4AA96D5E974E32A54,001C,02
  0C4C3003770046F4AA96D5E974E32A54,001E,0A
  0C4C3004770046F4AA96D5E974E32A54,0020,02
  0C4C3005770046F4AA96D5E974E32A54,0022,02
  0C4C3006770046F4AA96D5E974E32A54,0024,02
  0C4C3006770046F4AA96D5E974E32A54,0025,10
END

WxBeacon2の場合は 0C4C3001770046F4AA96D5E974E32A54,0019,02センサデータを読み出すことができます。先頭からUUID,HandleID,パーミッション(0x02はRead)となっています。このキャラクタリスティックに対してCHRコマンドでデータを読み出します。UUIDではなく、HandleIDを使うことに注意してください。

CHR, 0019
R,038C081D14070002007127420D7D1A5907650B.

対象のデータが取れました。これをデコードすることでWxBeacon2のセンサ情報を取得できます。Arduinoなどのマイコンを使うことで簡単にBLEセントラルデバイスを構築できそうです。

Web Bluetooth API とRN4020のワークショップを開催しました。

この記事について

 この記事では2017/10/20に開催したWeb Bluetooth APIとRN4020のワークショップの模様を記録しています。このワークショップでは以下過去記事の内容をベースに実際に皆さんにWeb Bluetooth APIを使ったアプリケーションとRN4020を使ったBLEデバイスの開発を体験していただきました。

masato-ka.hatenablog.com

募集時のconpassページは以下です。

connpass.com

開催の経緯と目的

このハンズオンはBLE周りのライブラリ情報を集める中で、Web Bluetooth APIと言うウェブブラウザからBLEを制御できるAPIを知ったことがきっかけです。Web Bluetooth APIで簡単にアプリが作れるなら、それに対向してデバイス側も 簡単に作れるものがないかなと探し当てたのがRN4020でした。  この2つで何かやってみたいなと思ったのと、前々からイベントを企画してみたいと考えていたので今回のワークショップを企画しました。  このワークショップを通して、BLE開発の敷居がぐっと下がってきたことを参加者の方に体験いただければよかったと思います。また、自分自身の知識の整理もやっていく予定でした。

開催模様

ワークショップ会場について

今回は友人の@atottoさんに場所を手配いただき、GROOVE X様のセミナールームをお借りすることができました。GROOVE X様はあのPepperの生みの親である林要さんが創業された会社でロボット界隈注目のスタートアップ企業です。社屋や会議室もとてもお洒落な場所でした。写真は入り口でこの裏手にセミナー会場が隠れています。お洒落。

f:id:masato-ka:20171020214426j:plain

ワークショップ内容につてい

今回のワークショップではBLEの基本やWeb Bluetooth API, RN4020の基礎知識を知ってもらう知識説明を30分実施した後、RN4020を利用したデバイス開発とWeb Bluetooth APIを利用したサンプルアプリケーションの開発を行いました。資料はGitHubWikiページで作成し、以下の通りとなっています。

ハンズオン資料へのリンク

Home · masato-ka/bel-key-security-app Wiki · GitHub

全体でハンズオン実施者は6名でしたがそのうち最後まで完了した方が3名、2章のデバイス開発まで完了した方が3名でした。完了しなかった方の大半はWindows PCを利用されていたため、Web Bluetooth APIの機能を確認する手間がかかっていたのが原因のようです。演習時間はおおよそ90分程度でした。

f:id:masato-ka:20171020203545j:plain

実際に回路を組み立てている様子です。資料の実体配線図に沿ってブレットボード上で組み立てを行います。

f:id:masato-ka:20171020203550j:plain

みなさん、デバイスが動くところまで到達したため、BLE開発の敷居が下がったことを理解してもらう点では概ね目的を達成できたかと思います。

反省点

実際に開催してみてよかった点や悪かった面です。

良かった点

主だってよかった点は以下のところです。

  • 動画を使い完成した時の映像を見せられた。
  • GitHubWikiでハンズオン資料を作成した。
  • 良い会場を借りられたこ
  • ハンズオンの資材セットがほぼはけた
  • 時間配分

 今回は資料をGitHubWiki ページで作成しました。間違いが発覚してもその場でコミットして共有するということができました。演習中誰かが見つけた間違いを共有できますし、ハンズオン後も共有できるので非常に便利です。またハンズオンの部品は自腹購入していたのですが、みなさん気に入っていただけた様子で引き取っていただくことができました。この辺は参加者の方に興味を持ってもらうことに成功した証ではないかなと思っています。  今回演習時間は90分でしたが達成率は50%でした。また未達成の方の離脱理由を考えると演習時間としてはちょうど良いか少し短めだと思います。平日夜開催という制約を考えるとちょうどいい時間ではないかと思いました。

悪かった点

逆に悪かった点

  • 演習中に記載漏れに気づく
  • 事前知識が必要な資料構成
  • そもそもWeb Bluetooth APIの実行環境がない人が数名いた。

演習中に資料の記載漏れに気づく点が幾つかありました。良い点であげた通りその場で修正できたのでしが、そこで少し時間を取られていたのでその点はチェックの甘を反省しています。また、回路の組み立てなど部品の詳細な説明がなかった点が気になります。今回の参加者はたまたま回路について知識のある方が多かったため問題ありませんでしたが、そうでない方には少し難しい内容だったかと思います。Web Bluetooth APIの方ではそもそもコピペで動くようにしていたので問題ないと思いますが、Angular.jsやJavaScriptの説明は必要だったかもしれません。また手持ちの環境でWeb Bluetooth APIの環境をお持ちでない方が数名いらっしゃいました。まだまだ対応環境が限られるためこの辺りも課題があると思います。

今後に向けて

 今回は参加者のバックグラウンドに助けられた面もありますが、もう少し内容を整理しないと継続は難しいかなという印象です。Web Bluetooth APIの対応環境を持ってな方でも試せるような事前の環境作りをしたり、参加者のスキル要件をもっと明確化するなど対応が必要かもしれません。  次回開催は未定ですが、別の内容をするにせよまた何かしら実施したいと思っています。その際は今回の反省を踏まえて開催したいです。