masato-ka's diary

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

WioLTEをVSCode(PlatformIO)で開発する

この記事について

  • 追記 20180317 Wio LTEArduino coreライブラリを現時点最新の1.1.3に変更しました。

この記事では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.3a3 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.3a3 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の対応環境を持ってな方でも試せるような事前の環境作りをしたり、参加者のスキル要件をもっと明確化するなど対応が必要かもしれません。  次回開催は未定ですが、別の内容をするにせよまた何かしら実施したいと思っています。その際は今回の反省を踏まえて開催したいです。

RN4020でI2Cの値を読み取る

この記事について

この記事ではRN4020のI2Cを利用する方法について紹介します。RN4020にはI2Cポートが付いています。EEPROMなどを接続しデータを一時的にためておくために利用できます。もちろん他のマイコンと同じようにI2Cポートをもったセンサを接続することができます。そこで、RN4020のI2Cポートに温湿度センサを取り付けて動作させてみました。

RN4020のI2C仕様について

 RN4020は21Pinと22PinがI2Cのポートとなっています。データシート上では21PinがSDA, 22PinがSCAになっていますが、実際には22PinがSDA, 21PinがSCAと逆になっています。(2017/10/16現在)秋月電子のボードも他のブレークアウトボードもデータシートにならって軒並み逆に記載されています。そのため基板の印字と回路図を比較する必要があります。おそらく逆につければ大丈夫です。I2Cバスのプルアップ抵抗はRN4020についていないません。そのため、センサモジュールについていなければ別途つける必要があります。ロジックレベルは3.3Vです。  また、RN4020のI2Cはファームウェアのバージョンが1.20以降で利用できます。現在販売されているものは1.23が多いようです。(最新はバージョン1.3)念のため、シリアルからVコマンドを使ってバージョンを確認しておきましょう。  RN4020はI2Cのマスタとして動作します。スレーブ動作は設定できません。また、EEPROM の読み書きを実行するコマンドがありますが、それ以外は指定データをシリアルバスにそのまま流す低レベルなAPIしかありません。

I2Cのコマンド

RN4020でI2Cを利用するためには以下の5つのコマンドを覚えれば大丈夫です。以下の5つのコマンドの組み合わせでI2Cのスレーブにアクセスします。

コマンド 用途
]A I2Cポートの初期化
]C I2Cのイベント制御
]Z I2Cポートの開放
]W データの書き込み
]R データの取得

I2Cコマンド利用方法

 今回はHDC1000と呼ばれるI2Cインタフェースの温湿度センサから温度の値を読み取る方法を例にとり、コマンドの使い方を説明します。 このセンサは前まで秋月電子通商で手に入りましたが最近は廃盤になったようです。センサのアドレスは7bitアドレスで1000000を持っています。上記のコマンドを使ってHDC1000から温度データを取得するには以下のように実行します。

]A,1,2 //(1)
]C,0 //(2)
]W,80020000 // (3)
]W,8000 //(4)
]C,1 //(5)
]W,81//(6)
]R,02 //(7)
6541 //16進数表記
  • (1) I2Cの初期化

最初の引数1はI2Cのクロックスピードです。100kHzを指定します。4を指定すると400kHzになります。また2番目の引数はGPIOを1,2,3,4,7で指定します。指定したGPIOは出力モードとなり、I2Cのスタート時にHighになります。スレーブの電源として利用することで、普段は電源を切っておくことができます。今回はGPIOを使わずに実験のため3.3Vの電源に直接接続しています。

  • (2) I2Cのスタート

]Cの引数に0コマンドをつけ、I2Cのスタートビットをバスに流します。この時、I2Cの初期化で指定したGPIOがHighになります。

  • (3) HDC1000の初期化

HDC1000はデータの取得方法など個別に細かく設定することができます。起動時に0x02のレジスタに値を書き込みます。今回は0x0000を書き込みます。詳細は以下のデータシートを参考にしてください。

]Wコマンドは指定されたバイト列をMSB側からバスに出力するコマンドです。データにバスを流すためのコマンドですので必ずしもスレーブへのWrite要求を出すコマンドではありません。そのため、I2Cの先頭バイトには7bitのスレーズ指定のアドレスと1byteのRead(1)/Wrie(0)を指定する必要があります。今回、HDC1000は2進数7bit表記で1000000がアドレスになります。最後にWriteを表す0をつけることで書き込みを表します。その後、書き込み先レジスタアドレス0x02と書き込みデータ0x0000を付与します。  実際にこの時の出力をロジックアナライザで確認すると以下の図のようになっています。

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

もしデバイスアドレスが8bit表記であれば左へ1bitシフトしたものにRead/Writeのbitを付け足します。(MSB側は必ず0になっているはず。。。)

引き続き]Wコマンドでレジスタ0x00へアクセスします。HDC1000ではこのジレスタにアクセスすることで、センサないのA/Dが温度の値を変換することになっています。Write要求ですが特にデータは書き込まず問題ありません。同じようにいかに信号の状態を出力しておきます。

  • (5) I2Cリセット

温度を読み取るまでのRN4020側の書き込みは完了したため、一度I2Cのバスを解放します。

(4)から(5)までのロジックアナライザ上での動作になります。

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

  • (6) リード要求

RN4020からリード要求を出します。この場合もバスに信号を流すため]Wコマンドを利用します。0x81はアドレス1000000のLSB側にRead要求の1を足したものになります。 I2CバスにREAD要求のコマンドが出力されます。

  • (7) データの読み取り

I2Cのデータの読み取りはリードコマンド]Rを使います。引数は受信するバイト数でここでは2バイトを表します。HDC1000の場合結果は4桁の16進数で帰ってきます。これを指定された式で計算して温度値を読み取ります。

(5)から(7)までのロジックアナライザ上での動きは以下の通りです。前述の説明と照らし合わせてみてください。

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

以上でRN4020のI2Cの値の読み取りになります。]Wと]Rの使い方に気をつければ他のデバイスやアクセス方法でも問題なく利用できると思います。ロジックアナライザの出力とコマンドを照らし合わせると出力がどうなるか理解できるかと思います。

I2Cスクリプトサンプル

I2Cはもちろんスクリプトモードからも利用できます。上記の例を参考に温度データを一定間隔でNotifyするスクリプトです。キャラクタリスティックには2byteでRead Notifyが指定されています。スクリプトとプライベートサービスの詳細は過去記事を御覧ください。

masato-ka.hatenablog.com

@PW_ON
A

@CONN
]A,1,2
]C,0
]W,80020000
]W,8000
]C,1 
SM,1,00002710

@TMR1
]W,81
$VAR1=]R,02
SHW,000B,$VAR1
SM,2,00002710

@TMR2
]W,8000
]C,1
SM,1,00002710

@DISCON
]Z
A

上記例では温度の変換命令''']W,8000```と読み取り命令を別のタイマーイベントとし、相互に繰り返すようになっています。(お互いのタイマーイベントを指定している。)これはHDC1000の温度変換の結果がくるのを少し待たないといけないためです。(ハードウェア割り込みもありますが今回は不使用です。)

まとめ

今回はRN4020でI2Cを利用してみました。I2Cもスタンドアローンで利用できるので、I2Cのセンサをつないでお手軽に利用することができます。簡単なセンサーデバイスはすぐに作れそうです。注意点としてはデータシート状のI2CのピンSDAとSCLが逆になっている点、]W,]Rのそれぞれの使い方です。ここさえ押さえればすぐに利用できます。データシートは何年もほったらかしのようですが、いつか修正されるのでしょうか。。。