Pular para o conteúdo principal

Integração do Gateway ChirpStack R1X com SenseCAP S2101

Introdução​

pir

Este guia orienta você na configuração de uma solução completa de gateway LoRaWAN usando ChirpStack no controlador de borda Seeed reComputer R11, com tecnologia Raspberry Pi. Com o módulo concentrador LoRa WM1302, o dispositivo R1X funciona como um gateway poderoso, capaz de comunicação sem fio de longa distância confiável. Ao configurar o Semtech Packet Forwarder, os dados LoRa podem ser transmitidos de forma contínua para o ChirpStack, que gerencia as camadas de rede e de aplicação. Usaremos Docker para simplificar a instalação e implantação dos serviços ChirpStack, garantindo uma configuração modular e escalável. Por fim, o sistema é integrado ao MQTT, permitindo transmissão de dados de IoT segura e em tempo real de dispositivos LoRa, como o sensor SenseCAP S2101, para aplicações acessíveis em qualquer lugar do mundo.

Hardware Necessário​

reComputer R1XMódulo Gateway LoRaWAN WM1302 SenseCAP S2101

Guia de Instalação do Docker​

1. Atualizar Pacotes do Sistema

sudo apt update
sudo apt upgrade

2. Instalar Docker

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

3. Adicionar Usuário ao Grupo Docker

sudo usermod -aG docker ${USER}

4. Reiniciar o Sistema

sudo reboot

5. Verificar Instalação

docker run hello-world

6. Instalar Docker Compose

sudo apt install docker-compose

Executar o Packet Forwarder​

O concentrador LoRa WM1302 requer o Semtech Packet Forwarder para retransmitir dados entre o módulo LoRa e o ChirpStack. O reComputer R11 fornece um guia de configuração pré-compilado para módulos LoRa.

Consulte o Wiki oficial da Seeed para as etapas de instalação: Seeed reComputer R11 LoRa Module Guide

Depois de instalado, siga os passos abaixo para configurar e executar o Packet Forwarder.

1. Modificar Configuração

Abra o arquivo de configuração correspondente à sua região LoRa. Por exemplo, para US915:

nano global_conf.json.sx1250.US915

Atualize a seção gateway_conf para apontar para o seu servidor ChirpStack:

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

Substitua AA555A0000000000 pelo seu Gateway ID real. Vamos manter como está Use o arquivo JSON correto para sua região LoRaWAN, dependendo do módulo que você comprou.

Salve o arquivo e saia:

  • Pressione CTRL + X,
  • Depois Y,
  • E, por fim, Enter.

2. Iniciar o Packet Forwarder

Execute o Packet Forwarder usando a configuração atualizada:

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

Iniciar o Gateway​

Para baixar o arquivo Docker Compose você precisa visitar esta página no reComputer e baixá-lo. Link

Em seguida, modifique a banda de frequência de acordo com suas configurações no arquivo yaml

 chirpstack-gateway-bridge:
image: chirpstack/chirpstack-gateway-bridge:4
restart: unless-stopped
ports:
- "1700:1700/udp"
volumes:
- ./configuration/chirpstack-gateway-bridge:/etc/chirpstack-gateway-bridge
environment:
- INTEGRATION__MQTT__EVENT_TOPIC_TEMPLATE=us915_0/gateway/{{ .GatewayID }}/event/{{ .EventType }}
- INTEGRATION__MQTT__STATE_TOPIC_TEMPLATE=us915_0/gateway/{{ .GatewayID }}/state/{{ .StateType }}
- INTEGRATION__MQTT__COMMAND_TOPIC_TEMPLATE=us915_0/gateway/{{ .GatewayID }}/command/#
depends_on:
- mosquitto

Após instalar o ChirpStack, você pode registrar seu gateway LoRa R11 e começar a processar dados.

Iniciar Serviços do ChirpStack

Se ainda não estiverem em execução, inicie todos os serviços do ChirpStack:

sudo docker-compose up -d

Verifique se os containers estão em execução:

sudo docker ps

Acessar a Web UI do ChirpStack

  1. Abra um navegador web e acesse:
http://localhost:8080/
  1. Faça login com as credenciais padrão:
Username: admin
Password: admin

Adicionar seu Gateway​

  1. Na interface do ChirpStack, vá para Gateways → Create Gateway

pir

  1. Insira os seguintes detalhes:

    • Gateway ID: AA555A0000000000 (substitua pelo seu Gateway ID real)
    • Name: Dê um nome descritivo para o seu gateway

pir

  1. Clique em Create Gateway para registrá-lo.

  2. Depois disso, você poderá visualizar o gateway na interface do ChirpStack

pir

Adicionar Perfil de Dispositivo​

Para conectar um dispositivo LoRaWAN (por exemplo, SenseCAP S2101) ao ChirpStack, primeiro você precisa criar um Device Profile.

  1. Navegue até Device Profiles → Create Device Profile

  2. Insira os seguintes detalhes:

    • Name: Dê um nome descritivo para o seu perfil de dispositivo
    • Region: Selecione a região/sub-banda que corresponde ao seu dispositivo e gateway (por exemplo, US915)

pir

  1. Navegue até a aba Codec:

    • Selecione JavaScript Functions
    • Cole o codec do seu dispositivo

⚠️ O codec é específico para o seu dispositivo LoRa. Por exemplo, se você estiver usando Seeed S201x, pode usar o código abaixo. Se você estiver usando um dispositivo diferente, consulte o fabricante para obter o codec correto.

  1. Copie e cole o codec na seção Uplink/Downlink Codec e salve o perfil.

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('');
}

Adicionar Dispositivo​

Depois que o Perfil de Dispositivo for criado, você poderá registrar seu dispositivo LoRaWAN no ChirpStack.

  1. Navegue até Tenant → Application e clique em Add Application

pir

  1. Insira um Nome para sua aplicação e salve
  2. Abra sua aplicação recém-criada e clique em Add Device

pir

  1. Insira os seguintes detalhes:

    • Device EUI: Cole o EUI do seu dispositivo LoRa (encontrado no datasheet do dispositivo ou no software de configuração, por exemplo, aplicativo SenseCAP)
    • Device Profile: Selecione o perfil de dispositivo que você criou anteriormente

pir

  1. Insira a Application Key e clique em Submit

pir

Verificar Status do Dispositivo​

Depois de adicionar seu dispositivo LoRaWAN, você pode verificar se ele está devidamente conectado e transmitindo dados.

  1. Navegue até sua aplicação e selecione o dispositivo que você adicionou

  2. Vá até a aba Events

    • Você deverá ver um join packet quando o dispositivo ingressar na rede com sucesso

pir

  1. Clique nos pacotes para visualizar informações detalhadas

pir

  • Por exemplo, você pode ver os dados de temperatura e umidade reportados por dispositivos como o SenseCAP S2101

Integração MQTT​

O ChirpStack usa MQTT para transmitir dados de dispositivos LoRaWAN para aplicações ou dashboards. Você pode monitorar essas mensagens em tempo real.

pir

  1. Conecte seu PC à mesma rede que o gateway reComputer R11

  2. Use um cliente MQTT como o MQTT Explorer para assinar tópicos

  3. Configure o cliente MQTT:

    • Host: Endereço IP do seu reComputer R11
    • Port: 1883
  4. Uma vez conectado, você verá uma árvore de tópicos representando seus dispositivos, por exemplo:

application/c853ffcd-53f0-4de3-83b9-5467ff895f76/device/2cf7f1c043500402/event/up
  1. Ao expandir o tópico, serão exibidas mensagens uplink contendo dados dos sensores, como temperatura e umidade de dispositivos como o SenseCAP S2101

pir

Integração com Node-RED​

Você pode visualizar os dados dos dispositivos LoRaWAN no Node-RED usando nós MQTT e funções personalizadas.

  1. Abra o Node-RED e arraste um nó MQTT IN para o fluxo

  2. Configure o nó MQTT:

    • Server: IP do seu reComputer R11 (por exemplo, 10.0.0.208)
    • Port: 1883
    • Topic: application/+/device/+/event/up

pir

pir

  1. Adicione um Function node para decodificar o payload da mensagem MQTT

    • Por exemplo, extraia temperatura e umidade do objeto 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. Conecte dois nós de saída a partir do Function node, um para temperatura e outro para umidade

  2. Conecte cada saída a um nó Gauge ou qualquer outro nó de visualização no Node-RED para exibir as leituras dos sensores

pir

pir

Suporte Técnico & Discussão de Produtos​

Obrigado por escolher nossos produtos! Estamos aqui para oferecer diferentes tipos de suporte para garantir que sua experiência com nossos produtos seja a mais tranquila possível. Oferecemos diversos canais de comunicação para atender a diferentes preferências e necessidades.

Loading Comments...