Skip to main content

ChirpStack R1XゲートウェイとSenseCAP S2101の統合

はじめに

このガイドでは、Raspberry Piを搭載したSeeed reComputer R11エッジコントローラーでChirpStackを使用して、完全なLoRaWANゲートウェイソリューションを設定する手順を説明します。WM1302 LoRaコンセントレーターモジュールにより、R1Xデバイスは信頼性の高い長距離無線通信が可能な強力なゲートウェイとして機能します。Semtech Packet Forwarderを設定することで、LoRaデータをChirpStackにシームレスに送信でき、ChirpStackがネットワーク層とアプリケーション層を管理します。Dockerを使用してChirpStackサービスのインストールと展開を簡素化し、モジュラーでスケーラブルなセットアップを確保します。最後に、システムはMQTTと統合され、SenseCAP S2101センサーなどのLoRaデバイスから世界中のどこからでもアクセス可能なアプリケーションへの安全でリアルタイムなIoTデータストリーミングを可能にします。

必要なハードウェア

reComputer R1XWM1302 LoRaWANゲートウェイモジュールSenseCAP S2101

Dockerインストールガイド

1. システムパッケージの更新

sudo apt update
sudo apt upgrade

2. Dockerのインストール

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

3. ユーザーをDockerグループに追加

sudo usermod -aG docker ${USER}

4. システムの再起動

sudo reboot

5. インストールの確認

docker run hello-world

6. Docker Composeのインストール

sudo apt install docker-compose

完璧です。あなたのPacket Forwarderセットアップを、使用している同じ構造化されたwikiスタイルに再フォーマットします:


Packet Forwarderの実行

WM1302 LoRaコンセントレーターは、LoRaモジュールとChirpStack間でデータを中継するためにSemtech Packet Forwarderが必要です。reComputer R11は、LoRaモジュール用の事前構築されたセットアップガイドを提供しています。

インストール手順については、公式のSeeed Wikiを参照してください: Seeed reComputer R11 LoRaモジュールガイド

インストール後、以下の手順に従ってPacket Forwarderを設定し実行してください。

1. 設定の変更

あなたのLoRa地域に対応する設定ファイルを開きます。例えば、US915の場合:

nano global_conf.json.sx1250.US915

gateway_confセクションを更新して、ChirpStackサーバーを指すようにします:

"gateway_conf": {
"gateway_ID": "AA555A0000000000",
/* change with default server address/ports */
"server_address": "localhost",
"serv_port_up": 1700,
"serv_port_down": 1700
}

AA555A0000000000を実際のゲートウェイIDに置き換えてください。そのままにしておきます 購入したモジュールに応じて、LoRaWAN地域に適したJSONファイルを使用してください。

ファイルを保存して終了します:

  • CTRL + Xを押し、
  • 次にYを押し、
  • 最後にEnterを押します。

2. Packet Forwarderの開始

更新された設定を使用してPacket Forwarderを実行します:

./lora_pkt_fwd -c global_conf.json.sx1250.US915

こちらが同じwikiスタイルでの**「ゲートウェイの作成」セクション**です:


ゲートウェイの開始

ChirpStackをインストールした後、R11 LoRaゲートウェイを登録してデータ処理を開始できます。

ChirpStackサービスの開始

まだ実行されていない場合は、すべてのChirpStackサービスを起動します:

sudo docker-compose up -d

コンテナが実行されていることを確認します:

sudo docker ps

ChirpStack Web UIへのアクセス

  1. Webブラウザを開いて以下にアクセスします:
http://localhost:8080/
  1. デフォルトの認証情報でログインします:
Username: admin
Password: admin

ゲートウェイの追加

  1. ChirpStack UIで、Gateways → Create Gatewayに移動します

pir

  1. 以下の詳細を入力します:

    • Gateway ID: AA555A0000000000(実際のゲートウェイIDに置き換えてください)
    • Name: ゲートウェイの説明的な名前を付けます

pir

  1. Create Gatewayをクリックして登録します。

  2. この後、ChirpStack UIでゲートウェイを表示できるようになります

pir

デバイスプロファイルの追加

LoRaWANデバイス(例:SenseCAP S2101)をChirpStackに接続するには、まずデバイスプロファイルを作成する必要があります。

  1. Device Profiles → Create Device Profileに移動します

  2. 以下の詳細を入力します:

    • Name: デバイスプロファイルの説明的な名前を付けます
    • Region: デバイスとゲートウェイに一致する地域/サブバンドを選択します(例:US915

pir

  1. Codecタブに移動します:

    • JavaScript Functionsを選択します
    • デバイス用のコーデックを貼り付けます

⚠️ コーデックはLoRaデバイス固有です。例えば、Seeed S201xを使用している場合は、以下のコードを使用できます。 異なるデバイスを使用している場合は、正しいコーデックについてメーカーに相談してください。

  1. Uplink/Downlink Codecセクションにコーデックをコピー&ペーストし、プロファイルを保存します。

pir

.js

function decodeUplink(input) {
return Decode(input.fPort, input.bytes, input.variables);
}

function Decode(fPort, bytes, variables) {
var bytesString = bytes2HexString(bytes).toLocaleUpperCase();
var fport = parseInt(fPort);
var decoded = {
valid: true,
err: 0,
payload: bytesString,
messages: []
};

// CRC check
if (!crc16Check(bytesString)) {
decoded['valid'] = false;
decoded['err'] = -1; // "crc check fail."
return { data: decoded };
}

// Length Check
if ((bytesString.length / 2 - 2) % 7 !== 0) {
decoded['valid'] = false;
decoded['err'] = -2; // "length check fail."
return { data: decoded };
}

// Cache sensor id
var sensorEuiLowBytes;
var sensorEuiHighBytes;

// Handle each frame
var frameArray = divideBy7Bytes(bytesString);
for (var forFrame = 0; forFrame < frameArray.length; forFrame++) {
var frame = frameArray[forFrame];
var channel = strTo10SysNub(frame.substring(0, 2));
var dataID = strTo10SysNub(frame.substring(2, 6));
var dataValue = frame.substring(6, 14);
var realDataValue = isSpecialDataId(dataID) ? ttnDataSpecialFormat(dataID, dataValue) : ttnDataFormat(dataValue);

if (checkDataIdIsMeasureUpload(dataID)) {
decoded.messages.push({
type: 'report_telemetry',
measurementId: dataID,
measurementValue: realDataValue
});
} else if (isSpecialDataId(dataID) || dataID === 5 || dataID === 6) {
switch (dataID) {
case 0x00: // node version
var versionData = sensorAttrForVersion(realDataValue);
decoded.messages.push({
type: 'upload_version',
hardwareVersion: versionData.ver_hardware,
softwareVersion: versionData.ver_software
});
break;
case 1: // sensor version
break;
case 2: // sensor eui low
sensorEuiLowBytes = realDataValue;
break;
case 3: // sensor eui high
sensorEuiHighBytes = realDataValue;
break;
case 7: // battery + interval
decoded.messages.push({
type: 'upload_battery',
battery: realDataValue.power
}, {
type: 'upload_interval',
interval: parseInt(realDataValue.interval) * 60
});
break;
case 9:
decoded.messages.push({
type: 'model_info',
detectionType: realDataValue.detectionType,
modelId: realDataValue.modelId,
modelVer: realDataValue.modelVer
});
break;
case 0x120: // remove sensor
decoded.messages.push({
type: 'report_remove_sensor',
channel: 1
});
break;
default:
break;
}
} else {
decoded.messages.push({
type: 'unknown_message',
dataID: dataID,
dataValue: dataValue
});
}
}

if (sensorEuiHighBytes && sensorEuiLowBytes) {
decoded.messages.unshift({
type: 'upload_sensor_id',
channel: 1,
sensorId: (sensorEuiHighBytes + sensorEuiLowBytes).toUpperCase()
});
}

return { data: decoded };
}

// ---------- Utils ----------
function crc16Check(data) {
return true;
}

function bytes2HexString(arrBytes) {
var str = '';
for (var i = 0; i < arrBytes.length; i++) {
var num = arrBytes[i];
var tmp = (num < 0 ? (255 + num + 1) : num).toString(16);
if (tmp.length === 1) tmp = '0' + tmp;
str += tmp;
}
return str;
}

function divideBy7Bytes(str) {
var frameArray = [];
for (var i = 0; i < str.length - 4; i += 14) {
frameArray.push(str.substring(i, i + 14));
}
return frameArray;
}

function littleEndianTransform(data) {
var arr = [];
for (var i = 0; i < data.length; i += 2) {
arr.push(data.substring(i, i + 2));
}
return arr.reverse();
}

function strTo10SysNub(str) {
var arr = littleEndianTransform(str);
return parseInt(arr.join(''), 16);
}

function checkDataIdIsMeasureUpload(dataId) {
return parseInt(dataId) > 4096;
}

function isSpecialDataId(dataID) {
switch (dataID) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 7:
case 9:
case 0x120:
return true;
default:
return false;
}
}

function ttnDataSpecialFormat(dataId, str) {
var strReverse = littleEndianTransform(str);
if (dataId === 2 || dataId === 3) {
return strReverse.join('');
}

var str2 = toBinary(strReverse);
var arr = [];
switch (dataId) {
case 0: case 1: // versions
for (var k = 0; k < str2.length; k += 16) {
var tmp = str2.substring(k, k + 16);
tmp = (parseInt(tmp.substring(0, 8), 2) || 0) + '.' + (parseInt(tmp.substring(8, 16), 2) || 0);
arr.push(tmp);
}
return arr.join(',');
case 4:
for (var i = 0; i < str2.length; i += 8) {
var item = parseInt(str2.substring(i, i + 8), 2);
arr.push(item < 10 ? '0' + item : item.toString());
}
return arr.join('');
case 7:
return {
interval: parseInt(str2.substr(0, 16), 2),
power: parseInt(str2.substr(-16, 16), 2)
};
case 9:
return {
detectionType: parseInt(str2.substring(0, 8), 2),
modelId: parseInt(str2.substring(8, 16), 2),
modelVer: parseInt(str2.substring(16, 24), 2)
};
}
}

function ttnDataFormat(str) {
var strReverse = littleEndianTransform(str);
var str2 = toBinary(strReverse);
if (str2[0] === '1') {
var arr = str2.split('').map(b => b === '1' ? 0 : 1);
var val = parseInt(arr.join(''), 2) + 1;
return parseFloat('-' + val / 1000);
}
return parseInt(str2, 2) / 1000;
}

function sensorAttrForVersion(dataValue) {
var arr = dataValue.split(',');
return { ver_hardware: arr[0], ver_software: arr[1] };
}

function toBinary(arr) {
return arr.map(item => {
var bin = parseInt(item, 16).toString(2).padStart(8, '0');
return bin;
}).join('');
}

デバイスの追加

デバイスプロファイルが作成されたら、LoRaWANデバイスをChirpStackに登録できます。

  1. テナント → アプリケーションに移動し、アプリケーションを追加をクリックします

pir

  1. アプリケーションの名前を入力して保存します
  2. 新しく作成したアプリケーションを開き、デバイスを追加をクリックします

pir

  1. 以下の詳細を入力します:

    • Device EUI: LoRaデバイスのEUIを貼り付けます(デバイスのデータシートまたは設定ソフトウェア(例:SenseCAP アプリケーション)で確認できます)
    • Device Profile: 先ほど作成したデバイスプロファイルを選択します

pir

  1. Application Keyを入力し、送信をクリックします

pir

⚠️ Device EUIとApplication Keyは、LoRaデバイスのデータシートまたは設定ソフトウェアから取得できます。SenseCAP デバイスの場合、SenseCAP アプリケーションを使用してこれらの設定を表示または再設定できます。

以下は、wikiスタイルに合わせて洗練された**「デバイスステータスの確認」**セクションで、前のセクションとの一貫性を保っています:


デバイスステータスの確認

LoRaWANデバイスを追加した後、デバイスが適切に接続され、データを送信していることを確認できます。

  1. アプリケーションに移動し、追加したデバイスを選択します

  2. イベントタブに移動します

    • デバイスがネットワークに正常に参加すると、joinパケットが表示されます

pir

  1. パケットをクリックして詳細情報を表示します

pir

  • 例えば、SenseCAP S2101などのデバイスから報告される温度と湿度データを確認できます

MQTT統合

ChirpStackはMQTTを使用してLoRaWANデバイスからアプリケーションやダッシュボードにデータをストリーミングします。これらのメッセージをリアルタイムで監視できます。

pir

  1. PCをreComputer R11ゲートウェイと同じネットワークに接続します

  2. MQTT ExplorerなどのMQTTクライアントを使用してトピックを購読します

  3. MQTTクライアントを設定します:

    • ホスト: reComputer R11のIPアドレス
    • ポート: 1883
  4. 接続すると、デバイスを表すトピックのツリーが表示されます。例:

application/c853ffcd-53f0-4de3-83b9-5467ff895f76/device/2cf7f1c043500402/event/up
  1. トピックを展開すると、SenseCAP S2101などのデバイスの温度や湿度などのセンサーデータを含むアップリンクメッセージが表示されます

pir

Node-RED統合

MQTTノードとカスタム関数を使用して、Node-REDでLoRaWANデバイスデータを可視化できます。

  1. Node-REDを開き、MQTT INノードをフローにドラッグします

  2. MQTTノードを設定します:

    • サーバー: reComputer R11のIP(例:10.0.0.208
    • ポート: 1883
    • トピック: application/+/device/+/event/up

pir

pir

  1. MQTTメッセージペイロードをデコードするFunctionノードを追加します

    • 例えば、JSONオブジェクトから温度湿度を抽出します
   // Get the JSON payload
let data = msg.payload;

if (typeof data === "string") {
try {
data = JSON.parse(data);
} catch (e) {
node.error("Invalid JSON", msg);
return [null, null];
}
}

// Check if "object" and "messages" exist
if (!data.object || !Array.isArray(data.object.messages)) {
node.warn("No messages found in payload");
return [null, null];
}

// Find the two measurements
let tempMsg = null;
let humMsg = null;

data.object.messages.forEach(m => {
if (m.type === "report_telemetry") {
if (m.measurementId === 4097) {
tempMsg = { topic: "temperature", payload: m.measurementValue };
} else if (m.measurementId === 4098) {
humMsg = { topic: "humidity", payload: m.measurementValue };
}
}
});

// Return 2 outputs: [temperature, humidity]
return [tempMsg, humMsg];
  1. Functionノードから2つの出力ノードを接続します。1つは温度用、もう1つは湿度用です

  2. 各出力をNode-REDのGaugeノードまたはその他の可視化ノードに接続して、センサー読み取り値を表示します

pir

pir

技術サポート & 製品ディスカッション

弊社製品をお選びいただき、ありがとうございます!弊社製品での体験が可能な限りスムーズになるよう、さまざまなサポートを提供いたします。さまざまな好みやニーズに対応するため、複数のコミュニケーションチャンネルを提供しています。

Loading Comments...