5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DRAGINO LoRa/GPS HAT v1.4-JP を The Things Networkにつなぐ

Last updated at Posted at 2021-01-24

やったこと

構成

シンプルにGateway1台×温湿度センサユニットの構成で試してみます。

LoRaWAN Gateway

Gateway

LoRa Node (ES920LRTH2: 温湿度センサ)

Node

ざっくり進め方

1. LoRaWAN ゲートウェイのセットアップ

ネットを探すといろいろ情報が出てきますが、まとまっているページがなかなか見つからなかったので、まずは一次資料である開発元のwikiを参考にしましたが、情報がかなり古く参考となったのはラズパイのSPIを有効にする部分のみでした。

Configuration

  • Connect the Raspberry Pi to the Internet;
  • Use 'raspi-config ' to ensure that SPI can be used on RPi ;
  • Use 'git clone git://git.drogon.net/wiringPi' to install the GPIO access library written in C for the BCM2835 used in the Raspberry Pi;
  • Get the single channel LoRa Gateway source code from here ;
  • Edit the 'main.cpp' to change configuration (look for: "Configure these values!").

引用元)
http://wiki.dragino.com/index.php?title=Use_Lora/GPS_HAT_%2B_RaspberryPi_to_set_up_a_Lora_Node

  • wiringPiは現時点のRaspberryPI OSであればすでにインストール済み
  • LoRa Gatewayサーバのサンプルソースが古かったので、他サイトを参照にDual Channel LoRaWAN Gateway を利用することにしました。

1-1. ラズパイ3のSPIを有効にする

raspi-config を利用して、SPIを有効にするだけ。

sudo raspi-config

3. Interface Options -> P4 SPI から有効にします。

1-2. Gatewayサーバ用のプログラムDual Channel LoRaWAN Gatewayのセットアップ

現時点(2021/01/24)で動かすには日本専用の設定変更に加えていろいろと変更しなければならない点があったのでまるっと修正したソースをgithubに上げました。

参考)ソースの修正箇所詳細について

https://github.com/bokse001/dual_chan_pkt_fwd をcloneして日本仕様に書き換えます1

まずは global_conf.json を書き換えます。

global_conf.json
{
  "SX127x_conf":
  {
    "freq": 923400000,   <--- 日本の周波数帯(デフォルトチャンネル)
    "freq_2": 923200000, <---
    "spread_factor": 10, <--- SF値(拡散率)ここは調整してもよい
    "pin_nss": 6,    <--- DRAGINO LoRa/GPS HATのピンに合わせる
    "pin_dio0": 7,   <---
    "pin_nss_2": 6,  <---
    "pin_dio0_2": 7, <---
    "pin_rst": 3,    <---
    "pin_led1":4,
    "pin_NetworkLED": 22,
    "pin_InternetLED": 23,
    "pin_ActivityLED_0": 21,
    "pin_ActivityLED_1": 29
  },
  "gateway_conf":
  {
    "ref_latitude": 0.0,      <-- ここはGatewayの設置場所にあわせましょう
    "ref_longitude": 0.0,     <--
    "ref_altitude": 10,       <--

    "name": "your name",                     <-- このあたりも適切に
    "email": "a@b.c",                        <--
    "desc": "Single channel pkt forwarder",

    "interface": "eth0",      <-- ラズパイでインターネット接続に利用する

    "servers":
    [
      {
        "address": "router.jp.thethings.network",  <-- 日本向けに変更
        "port": 1700,
        "enabled": true
      },
      {
        "address": "router.as1.thethings.network", <-- TODO: 周波数帯からAS1を選択してみたけど、これでよいのか?
        "port": 1700,
        "enabled": false
      }
    ]
  }
}

serversは ここ を参照に書き換えました。

次に dual_chan_pkt_fwd.cpp を書き換えます。
まずは定数定義。#defineの定義部分(230行目付近)に追加しました。

dual_chan_pkt_fwd.cpp(230行目付近)

// Tx Power Register add 
// Power Setting for Japan ARIB STD-T108
// 20mW=13dBm
// SX1276 RegPaConfig(0x09), Val=0x3f
// bit 7 PaSelect = 0 select RFO
// bit 6-4 MaxPower = 3 Pmax=10.8+0.6*3=12.6 < 13
// bit 3-0 OutputPower = 0x0f Pout=Pmax-(15-0x0f)=12.6
// SX1272 RegPaConfig(0x09), Val=0x0e
// bit 7 PaSelect = 0 select RFO
// bit 6-4 unused = 0
// bit 3-0 OutputPower = 0xe Pout=-1 + OutputPower = -1 + 0x0e = 13dBm
#define PWR_JPN_1276             0x3f
#define PWR_JPN_1272             0x0e

実際の書き込み部分は433行目付近に入れました。

dual_chan_pkt_fwd.cpp(433行目付近)
  // Set Tx Power for Japan
  if (sx1272) {
    WriteRegister(REG_PA_CFG,PWR_JPN_1272, CE);
  } else {
  // sx1276
    WriteRegister(REG_PA_CFG,PWR_JPN_1276, CE);
  }

ここでmakeしたところ下記のwarningが発生しました。

include/rapidjson/document.h:1652:24: warning: ‘void* memcpy(void*, const void*, size_t)’ writing to an object of type ‘rapidjson::GenericValue<rapidjson::UTF8<> >::Member’ {aka ‘struct rapidjson::GenericMember<rapidjson::UTF8<>, rapidjson::MemoryPoolAllocator<> >} with no trivial copy-assignment; use copy-assignment instead [-Wclass-memaccess]
             std::memcpy(data_.o.members, members, count * sizeof(Member));

そこで、エラーが出ていた箇所を書き換えました。

修正前)include/rapidjson/document.h(1630行目付近)
    // Initialize this value as array with initial data, without calling destructor.
    void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) {
        flags_ = kArrayFlag;
        if (count) {
            data_.a.elements = (GenericValue*)allocator.Malloc(count * sizeof(GenericValue));
            std::memcpy(data_.a.elements, values, count * sizeof(GenericValue));
        }
        else
            data_.a.elements = NULL;
        data_.a.size = data_.a.capacity = count;
    }
修正後)
    void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) {
        flags_ = kArrayFlag;
        if (count) {
            // data_.a.elements = (GenericValue*)allocator.Malloc(count * sizeof(GenericValue));
            // std::memcpy(data_.a.elements, values, count * sizeof(GenericValue));
            auto arr = static_cast<GenericValue*>(allocator.Malloc(count * sizeof(GenericValue)));
            for (SizeType idx = 0; idx < count; ++idx)
                new (arr + idx) GenericValue;
            data_.a.elements = arr;
            std::copy_n(values, count, arr);
        }
        else
            data_.a.elements = NULL;
        data_.a.size = data_.a.capacity = count;
    }

SetObjectRaw も同じように警告がでていたので書き換えました。

今度はmakeが成功したので実行してみたところ、以下のエラーでサーバの起動に失敗しました。。。

pi@gateway:~/project/dual_chan_pkt_fwd $ ./dual_chan_pkt_fwd 
dual_chan_pkt_fwd: include/rapidjson/document.h:1398: unsigned int rapidjson::GenericValue<Encoding, Allocator>::GetUint() const [with Encoding = rapidjson::UTF8<>; Allocator = rapidjson::MemoryPoolAllocator<>]: Assertion `flags_ & kUintFlag' failed.
中止

修正方法がちょっとわからなかったので、さくっとASSERTをコメントアウトしてみました。。。誰か正しい修正方法を教えて下さい😢

修正前)include/rapidjson/document.h(1396行目付近)
    int GetInt() const          { RAPIDJSON_ASSERT(flags_ & kIntFlag);   return data_.n.i.i;   }
    unsigned GetUint() const    { RAPIDJSON_ASSERT(flags_ & kUintFlag);  return data_.n.u.u;   }
    int64_t GetInt64() const    { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; }
    uint64_t GetUint64() const  { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; }
    int GetInt() const          { RAPIDJSON_ASSERT(flags_ & kIntFlag);   return data_.n.i.i;   }
    //unsigned GetUint() const    { RAPIDJSON_ASSERT(flags_ & kUintFlag);  return data_.n.u.u;   }
    unsigned GetUint() const    { return data_.n.u.u;   }
    int64_t GetInt64() const    { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; }
    uint64_t GetUint64() const  { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; }

一応これで動いているのでとりあえず良しとします😇

1-2-1. 修正箇所

上記ソースコードを元にした、個別の修正箇所を記載します。

必須

global_conf.json の以下の箇所を修正してください

global_conf.json
...省略...
  "gateway_conf":
  {
    "ref_latitude": 0.0,      <-- ここはGatewayの設置場所にあわせましょう
    "ref_longitude": 0.0,     <--
    "ref_altitude": 10,       <--

    "name": "your name",                     <-- このあたりも適切に
    "email": "a@b.c",                        <--
    "desc": "Single channel pkt forwarder",

    "interface": "eth0",      <-- ラズパイでインターネット接続に利用する

任意

ソースを展開したパスに併せて dual_chan_pkt_fwd.service を修正してください。修正しない場合、 make install で失敗する場合があります。

dual_chan_pkt_fwd.service
WorkingDirectory=/home/pi/dual_chan_pkt_fwd/
ExecStart=/home/pi/dual_chan_pkt_fwd/dual_chan_pkt_fwd 

1-2-2. ソースコードのコンパイル

普通に make & make install する流れでOKです。

$ cd dual_chan_pkt_fwd
$ make
$ make install
sudo cp -f ./dual_chan_pkt_fwd.service /lib/systemd/system/
sudo systemctl enable dual_chan_pkt_fwd.service
sudo systemctl daemon-reload
sudo systemctl start dual_chan_pkt_fwd
sudo systemctl status dual_chan_pkt_fwd -l
● dual_chan_pkt_fwd.service - Lora Packet Forwarder
   Loaded: loaded (/lib/systemd/system/dual_chan_pkt_fwd.service; enabled; vendor preset: enabled)
   Active: active (running) since Sun 2021-01-24 14:52:03 JST; 5s ago
 Main PID: 19602 (dual_chan_pkt_f)
    Tasks: 1 (limit: 2063)
   CGroup: /system.slice/dual_chan_pkt_fwd.service
           └─19602 /home/pi/project/dual_chan_pkt_fwd/dual_chan_pkt_fwd

 1月 24 14:52:03 gateway systemd[1]: Started Lora Packet Forwarder.

上記のようにログが出れば起動OKです!

後々の処理のため、一旦サーバを停止しておきましょう。

$ sudo systemctl stop dual_chan_pkt_fwd 

ここで、TTNへのゲートウェイへの登録に必要なゲートウェイのEUIを確認します。直接動かすとサーバ起動時に表示されますので以下で確認してください

$ ./dual_chan_pkt_fwd
server: .address = router.jp.thethings.network; .port = 1700; .enable = 1
server: .address = router.as1.thethings.network; .port = 1700; .enable = 0
Gateway Configuration
  xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  Single channel pkt forwarder
  Latitude=xxxxxxx
  Longitude=xxxxxxxx
  Altitude=0
  Interface: eth0
Trying to detect module CE0 with NSS=6 DIO0=7 Reset=3 Led1=unused
SX1276 detected on CE0, starting.
Trying to detect module CE1 with NSS=6 DIO0=7 Reset=3 Led1=unused
SX1276 detected on CE1, starting.
Gateway ID: XX:XX:XX:XX:XX:XX:XX:XX      <---- ここがゲートウェイEUIなのでメモっておいてください!
Listening at SF10 on 923.400000 Mhz.
Listening at SF10 on 923.200000 Mhz.
-----------------------------------
stat update: 2021-01-24 09:57:17 GMT no packet received yet

このあとGatewayを登録しますので、起動したままにしておきましょう。

2. LoRaWAN サーバ(TTN)のセットアップ(1)

2-1. Gatewayのセットアップと動作確認

ゲートウェイを登録します。
ゲートウェイEUIには先にメモっておいたものを入力してください。

console_thethingsnetwork_org_gateways_register__1_-5.png

登録後下記のようにステータスが接続済みになれば登録完了です。

image.png

2-2. アプリケーションのセットアップとDeviceの登録

アプリケーションを作成します。

2-2-1

アプリケーション作成後、デバイスを登録します。

2-2-2

※なお、デバイスEUIは自動生成しますがい、あとで変更します。

登録後、デバイスの設定をES920LRTH2向けに変更しておきます。

2-2-3

登録完了後下記画面となりますので、

  • デバイスアドレス
  • ネットワークセッションキー
  • Appセッションキー

をメモしておいてください。このあとES920LRTH2で利用します。

2-2-4

ちなみに独自に生成できない/しないほうがよい項目は下記の2個です。混乱の元となるのでメモしておきます。

DevEUI(TTNコンソール:デバイスEUI)
ES920LRTH2の出荷時にデフォルトセットされている。変更もできるが変えないほうがよい。ES920LRTH2の値を見てTTNコンソール側を書き換える。
DevAddr(TTNコンソール:デバイスアドレス)
TTNコンソールでアプリケーションからデバイス登録する際にTTN側で発行する。ES920LRTH2側で設定する。

3. LoRa ノード(ES920LRTH2: 温湿度センサ)のセットアップ

ES920LRTH2のドキュメントを元にシリアル接続し、下記値をセットしてください。

  • Activate
    • Activation by Personalization をセット
  • DevAddr
  • NwkSKey
  • AppSKey
  • Acknowledge
    • OFF をセット(ちょっと触った限りでは、Acknowledgeが受け取れなかった為)

その他はデフォルトで構いません。また、以降の処理で使いますので、 DevEUI を確認しておいてください。

参考)
LoRaWAN > y
  configuration setting is below
  -------------------------------------
  Class                       : Class A
  ADR                         : ON
  Activate                    : Activation by Personalization
  DevEUI                      : xxxxxxxxxxxxxxxx              <--- ここをメモっておく
  AppEUI                      : 0000000000000000
  AppKey                      : 00000000000000000000000000000000
  DevAddr                     : xxxxxxxx
  NwkSKey                     : xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  AppSKey                     : xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  Acknowledge                 : OFF
  Default Data rate           : DR2
  Output Power                : 13dBm
  Send Interval               : 60

4. LoRaWAN サーバ(TTN)のセットアップ(2)

4-1. DeviceのデバイスEUIを変更

上記処理で取得した DevEUI でデバイスEUIを更新します。

4-1-1

これで終了です。これで以下のようにデバイスの詳細画面でステータスに時間が表示されば登録が完了したことが確認できます。

4-1-2

ES920LRTH2側がきちんと動作していればアプリケーション一覧の「データ」タブでpayloadが飛んでいることが確認できると思います。

4-2. 動作確認

上記手順でバイナリデータを受信できるようになりましたが、目で見えるようにPayloadのdecoder/converterを書き換えましょう。

4-2-1

decoder
function Decoder(bytes, port) {
  // Decode an uplink message from a buffer
  // (array) of bytes to an object of fields.
  //var decoded = {};

  var decoded = {
    "Temperature": (bytes[1] << 8) + bytes[0],
    "Humidity": (bytes[3] << 8) + bytes[2],
    "Voltage": (bytes[5] << 8) + bytes[4]
  };

  // if (port === 1) decoded.led = bytes[0];

  return decoded;
}
converter
function Converter(decoded, port) {
  // Merge, split or otherwise
  // mutate decoded fields.
  var converted = decoded;

  // if (port === 1 && (converted.led === 0 || converted.led === 1)) {
  //   converted.led = Boolean(converted.led);
  // }
  
  roundup = function(value, digit) {
    digits = Math.pow(10, digit);
    return Math.round(value * digits) / digits;
  }
  
  converted.Temperature = -45 + (175 * converted.Temperature / (Math.pow(2, 16) - 1));
  converted.Humidity = 100 * converted.Humidity / (Math.pow(2, 16) - 1)
  converted.Voltage = 3.0 / (Math.pow(2, 12) - 1) * converted.Voltage;

  converted.Temperature = roundup(converted.Temperature, 2)
  converted.Humidity = roundup(converted.Humidity, 2)
  converted.Voltage = roundup(converted.Voltage, 2)

  return converted;
}

こうすることで、下記のようにアプリケーションデータで、センサー値を確認することができます。

4-2-2

(自分に向けて)お疲れさまでした😭

その他メモ

  • Cayenneにつなげてみたかったが、TTNのconverterがバイナリデータを返せないので断念
  • Acknowledgeの受け取り方が知りたい

主な参照元

※この他にもいっぱいあったのですがメモし忘れました…

  1. DRAGINO LoRa/GPS HAT v1.4-JPはシングルチャネルなので、fork元の https://github.com/tftelkamp/single_chan_pkt_fwd を利用すべきだと思いましたがすでにメンテナンスされていないのでこちらを使うのが正解みたいです

5
2
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?