LoRaWANセンサーのSenseCAPを設置してみました

昨年にSeeedのLoRaWAN対応デバイス「SenseCAP S210Xセンサー」の技適認証を取得しました。現在は国内の販売店さんの方で取り扱いしてくださっています。

今回、検証用のSenseCAP S2100 LoRaWAN Data LoggerとSenseCAP S2110 Sensor Builderを入手したので組み合わせて動作検証してみようと思います。

SenseCAP S210Xセンサーシリーズの特徴
  • LoRaWAN対応、長距離伝送が可能
  • スマホで簡単設定 (Bluetoothで接続する専用のアプリを使用)
  • 屋外でも利用できるIP66準拠の筐体を採用
  • 大容量バッテリーを内蔵

さて、この2つのデバイスがどのようなものなのか大まかに説明すると、

SenseCAP S2100 LoRaWAN Data Logger

Modbus RTU(RS485)、アナログ入力、デジタル入力インターフェースに対応したLoRaWANデータロガーです。センシング方法や測定間隔などは専用のスマートフォンアプリで設定することができ、取得した計測データは任意のLoRaWAN Network Server(LNS)を経由してSenseCAP Portalなどのアプリケーションサーバーに送信することができます。

SenseCAP S2110 Sensor Builder

Groveモジュールを利用して独自のModbus RTU(RS485)センサーを構築できるキットです。Groveモジュールを組み込み、セットアップしたセンサーはModbus RTUスレーブとして動作し、マスタ(SenseCAP S2100など)からのクエリによってGroveモジュールで計測したデータを返すことができます。

Sensor Builderのセットアップ

せっかく冬ということで屋外に設置して氷点下環境でも動作することができるか試してみたいと思います。

まずはSensor Builderをセットアップしていきます。 温度を測定したいので標準でサポートしているGroveモジュールのBMP280やBME680などがあれば面倒はなかったのですが、、それらのモジュールは所有していないので、手元にあるGroveモジュールBME280をサンプルコードに加えていきます。

尚、Sensor Builderが標準でサポートしているGroveモジュールは下記となります。

Groveモジュール タイプ I/F
Grove - Temperature and Barometer Sensor (BMP280) 気温、気圧 I2C
Grove - Oxygen Sensor (MIX8410) 酸素濃度 アナログ
Grove - CO2 & Temperature & Humidity Sensor - SCD41 二酸化炭素濃度、温湿度 I2C
Grove - Sunlight sensor - SI1151 照度 I2C
Grove - Light Sensor v1.2 - LS06-S phototransistor 照度 アナログ
Grove - Flame Sensor 炎(近赤外線) デジタル
Grove - Gas Sensor (BME680) 温湿度、気圧、ガス I2C
Grove - Multichannel Gas Sensor v2 ガス I2C
Grove - TDS Sensor/Meter For Water Quality (Total Dissolved Solids) 水質(TDS) アナログ
Grove – UV Sensor UV アナログ
Grove - Ultrasonic Ranger 距離 デジタル
Grove - Turbidity Sensor 水質(濁度) アナログ

S2110 Sensor BuilderのサンプルコードはGithubで公開されているので、任意のセンサーを対応させることができます。

github.com

今回追加するのはGrove BME280なので、Arduino IDEにGrove_BME280ライブラリを追加して、追加したライブラリ用のコード「sensorBME280.hpp」を追加しました。 sensorBME688.hppに倣って作業してみました。

  • src/sensorBME280.hpp
#ifndef _SENSOR_BME280_H
#define _SENSOR_BME280_H

#include "sensorClass.hpp"
#include <Seeed_BME280.h>
#include <Wire.h>

#define SENSOR_BME280_I2C_ADDR 0x76

class sensorBME280 : public sensorClass
{
public:
    sensorBME280() : sensorClass("BME280"){};
    ~sensorBME280(){};

    uint16_t init(uint16_t reg, bool i2c_available);
    bool connected();
    bool sample();

    enum
    {
        TEMPERATURE = 0x00, // C
        PRESSURE = 0x01,    // KPa
        ALTITUDE = 0x02,    // M
        HUMIDITY = 0x03,    // %
        MAX
    };

private:
    BME280 _bme280;
};

uint16_t sensorBME280::init(uint16_t reg, bool i2c_available)
{
    uint16_t t_reg = reg;

    for (uint16_t i = 0; i < sensorBME280::MAX; i++)
    {
        sensorClass::reg_t value;
        value.addr = t_reg;
        value.type = sensorClass::regType_t::REG_TYPE_S32_ABCD;
        value.value.s32 = 0;
        m_valueVector.emplace_back(value);
        t_reg += sensorClass::valueLength(value.type);
    }

    if (!i2c_available)
    {
        _connected = false;
        return t_reg - reg;
    }
    GROVE_SWITCH_IIC;
    Wire.begin();
    Wire.beginTransmission(SENSOR_BME280_I2C_ADDR);
    if (Wire.endTransmission() != 0)
    {
        _connected = false;
        return t_reg - reg;
    }

    Serial.println("bme280 init");
    GROVE_SWITCH_IIC;
    if (!_bme280.init())
    {
        Serial.println("bme280 init failed");
        _connected = false;
        return t_reg - reg;
    }

    _connected = true;
    return t_reg - reg;
}

bool sensorBME280::sample()
{
    GROVE_SWITCH_IIC;

    float temperature = _bme280.getTemperature();
    float pressure = _bme280.getPressure();
    float altitude = _bme280.calcAltitude(pressure);
    float humidity = _bme280.getHumidity();

    m_valueVector[TEMPERATURE].value.s32 = temperature * SCALE;
    m_valueVector[PRESSURE].value.s32 = pressure * SCALE;
    m_valueVector[ALTITUDE].value.s32 = altitude * SCALE;
    m_valueVector[HUMIDITY].value.s32 = humidity * SCALE;

    return true;
}

bool sensorBME280::connected()
{
    return _connected;
}
#endif

作成したヘッダファイル「sensorBME280.hpp」は、既存センサーのインクルード位置の後ろに追加。 ・src/sensorBuilder.hpp

#include "sensor/sensorTurbidity.hpp"
#include "sensor/sensorTDS.hpp"
#include "sensor/sensorUltrasonic.hpp"
#include "sensor/sensorBME280.hpp"  // ← ココに追加

#define SENSOR_BUILDER_DEF_BAUD 9600
#define SENSOR_BUILDER_DEF_SLAVE 1

この場所に追加した理由としては、既存のModbus Register Tableの後ろに今回追加する温湿度、気圧のRegisterを追加で配置するためです。レジスタアドレスは、この後に控えているS2100 Data Loggerのコンフィグレーション作業時に必要となります。

Grove Sensor NameRegister NameRegister Address (Hex)
Grove - Barometer Sensor(BME280)Temperature
52 (0x34)
Pressure
54 (0x36)
Altitude
56 (0x38)
Humidity
58 (0x3a)

コード追加後は、リポジトリに記載のビルド手順に沿ってビルドし、Sensor BuilderのXIAO RP2040に書き込みました。

ファームウェア書き込み後は、S2100と接続して組み上げ。接続方法はコチラを参照しています。

専用アプリ「SenseCAP Mate」でData Loggerをセットアップ

(事前にLoRaWANゲートウェイはSenseCAP用にセットアップ済みのため、この記事ではその部分は割愛します)

SenseCAP S210XセンサーはSenseCAP Mateアプリを使って設定する必要がありますので、スマホにインストールしてGlobalサーバーにログイン。

(以下はiPhoneでの手順や画面キャプチャーとなります)

Mateアプリにログインするにはアカウントが必要となりますので、もしなければRegisterして作成する必要があります。アカウントがあるかないかわからない人はとりあえずRegisterしてみるで良いと思います。メールアドレスは重複して登録できないようになっているようです。

ログイン後、まずはアカウントにデバイスを追加します。 HOMEの右上にある+アイコンをタップするとQRコードスキャナーが立ち上がるので、 S2100に貼付られているラベルのQRコードを読み込ませ、Add to accountをタップ。

続いてコンフィグレーション。 S2100のボタンをLEDが点滅するまで長押しし、Mateアプリとペアリングさせると、 S2100のS/Nが表示されるのでタップしData Logger Modeを選択。

Settingsタブに移動して、PlatformにSenseCAP for The Things Network、Frequency PlanにはAS923-LBT-Japanを設定。 Uplink Intervalはとりあえず10分間隔に設定しました。

Temperature、Pressure、Humidityの3つを測定するようにするため、Measurement Numberは3を設定し、Measurement SettingsをタップしてそれぞれのRegister Addressを設定。設定する値は、ソースコード作成時にあらかじめ控えておいた値です。

Measurement Settingsができたらデバイスに送信。これでコンフィグレーションも完了です。HOMEに戻るとS2100が追加されていることを確認できました。

SenseCAP Portal

S2100がSensor Builderから収集した計測データはSenseCAP Portalにアップロードされます。 SenseCAP Portalでは、計測したデータを表形式やグラフなどで閲覧することができます。

SenseCAP APIを利用すれば、SenseCAP Portalに溜まったデータを他のアプリケーションなどで参照することができるようです。 APIのドキュメントはSenseCAP Document Centerにあり、Quickstartに倣ってAPI IDとAccess Keyを発行すればすぐに利用することができました。

参考までに、cURLで最新のデータを参照する場合は次のような感じとなります。

$ export API_ID="[YOUR API ID]"
$ export API_KEY="[YOUR API KEY]"
$ export DEVICE_EUI="[DEVICE EUI]"
$ curl --user "${API_ID}:${API_KEY}" \
       --url 'https://sensecap.seeed.cc/openapi/view_latest_telemetry_data?device_eui='${DEVICE_EUI}'&channel_index=1'
{
  "code": "0",
  "data": [
    {
      "channel_index": 1,
      "points": [
        {
          "measurement_value": -3.77,
          "measurement_id": "4165",
          "time": "2024-01-17T03:03:08.373Z"
        },
        {
          "measurement_value": 101922.69,
          "measurement_id": "4166",
          "time": "2024-01-17T03:03:08.373Z"
        },
        {
          "measurement_value": 50.69,
          "measurement_id": "4167",
          "time": "2024-01-17T03:03:08.373Z"
        }
      ]
    }
  ]
}

屋外に設置

デバイスのセットアップができたので、なるべく直射日光があたらない日陰に設置してみます。今回は自宅のベランダ塀の陰に。

雪が降り積もり、最高気温が氷点下(0℃以下)の真冬日も含めて1週間程度放置してみた結果がコチラです。晴れた日中は反射光が当たってしまったようで測定失敗。屋外の気温を測るにはやはり百葉箱や強制通風筒(参考リンクを参照)などのように、センサーに日射が当たらず、風通しの良い環境で測定する必要があります。 また、全体的に2~3℃の誤差(オフセット?)がありそうです。Sensor Builderの内部温度上昇の影響があるのかもしれません。

参考までに測定日の天気と最高/最低気温。

【失敗】後日確認したところ、がっつり反射光が当たってしまっていました🤣

まとめ

今回はS2110 Sensor Builderに標準対応しているGroveモジュールを所有していなかったのでコーディングとビルドなどが発生してしまいましたが、対応するGroveモジュールを所有していたり、完成品のSenseCAP S210Xシリーズ(S2101など)であればスマホ一つでセットアップでき、容易に周囲環境の計測ができる便利なセンサーでした。

日本ではまだあまり普及していないLoRaWANですが活躍できるシーンは多そうなので、地道に普及活動をしていきたいと思います。

参考リンク

変更履歴

日付 変更者 変更内容
2024/1/18 mnakai 作成