ENPLDEPT
Flash
Toda a documentação
Gateway LoRaWAN

Seu gateway já ouve tudo. Deixe-o trabalhar para a rede.

Duas formas. Direta — o forwarder também envia para sensmos.com:1700 e você pareia o gateway com a carteira no app; ele rende e transmite beacons. Passiva — um script no gateway informa o que ouve; nada muda no gateway.

radio · 868 MHz · emergency frame
◈
Heard by a neighbour · 2.1 kmgarage · temp 3.4°C · door OPEN · 21:44

Forma 1 — direta: pareie com a carteira

Funciona com qualquer gateway cujo packet forwarder consiga enviar para mais de um servidor (Crankk, RAK, Dragino, MikroTik, a maioria dos hotspots da época do Helium).

  1. No packet forwarder do gateway, adicione um servidor: sensmos.com, porta 1700 (UDP, uplink e downlink). Mantenha os outros servidores — o forwarder envia cada frame para cada um deles.
  2. Espere um minuto, até o gateway nos enviar algo.
  3. No app Sensmos: Painel → + → Adicionar gateway LoRaWAN. Digite o EUI do gateway (16 caracteres hexadecimais, do painel dele), opcionalmente um nome e uma posição — ou use a posição do celular estando ao lado dele. Toque em Parear com a carteira.

O app recusa se o gateway estiver em silêncio há mais de 10 minutos (verifique a entrada do servidor) ou se já estiver pareado com outra carteira.

O que você recebe: um card no Painel — quantos nodes o gateway ouve e quantos o ouvem (24 h), frames Sensmos ouvidos em 24 h, o último beacon, os ganhos. No mapa ao vivo o gateway tem cor e painel próprios. Nome e posição edita; Desparear gateway no card o libera.

Antes de pareá-lo: um gateway que há 10 minutos encaminha para sensmos.com:1700 começa a trabalhar para a rede sob a carteira do projeto — seus frames Sensmos são entregues, ele transmite o beacon de cobertura e conta na época. O pareamento move o card e os ganhos para você; um gateway que ninguém pareia continua ganhando para o projeto.

O que a rede faz com ele: cada frame Sensmos ouvido — transmissão de emergência, frame de sensor — é entregue ao dono exatamente como um frame ouvido por um node. A cada 5 minutos a rede transmite pelo seu gateway um beacon curto de cobertura; os nodes que o ouvem confirmam que o gateway está onde diz estar.

Forma 2 — passiva: um script, nada muda no gateway

Para um gateway que você não quer reconfigurar. Um script Python no gateway lê o tráfego do forwarder no loopback e informa metadados de rádio. Precisa de um node Sensmos na mesma rede para responder por ele. Detalhes a seguir.

Para quem é

Qualquer máquina Linux a correr um packet forwarder Semtech simples (UDP, ao estilo da porta 1700) apontado para um servidor remoto: hotspots Helium e as suas stacks de multi-mining, RAK, Dragino, Mikrotik, um Raspberry Pi com um HAT concentrador. Se o seu gateway corre o ChirpStack Gateway Bridge, existe um broker MQTT local e aplica-se um agente diferente.

Também precisa de um node Sensmos na mesma LAN que o gateway (qualquer ESP32 com o firmware, sem rádio necessário) e do seu PIN. O node responde pelo gateway; sem ele o backend não emite um token.

Porquê passivo, e não um relay

A forma óbvia de ler um forwarder é pôr algo no meio — apontar o forwarder para nós e reencaminhar para a frente. Deliberadamente não o fazemos. Em hardware que ganha dinheiro, a nossa falha tornar-se-ia a sua falha. O forwarder já envia cada frame para cada servidor configurado separadamente pelo loopback; o script limita-se a ler em lo. Não é o destino de ninguém, não retransmite nada e não atrasa nada.

Início rápido

Corra tudo isto no gateway, não no seu laptop. Apenas Python 3 — sem pacotes pip, nada para compilar. É preciso root porque ler pacotes em bruto exige CAP_NET_RAW.

# 1 — install the script
sudo curl -fsSL -o /usr/local/bin/sensmos-gw-udp.py https://raw.githubusercontent.com/Galusz/sensmos-gateway/main/sensmos-gw-udp.py
sudo chmod +x /usr/local/bin/sensmos-gw-udp.py

# 2 — see what would leave your box (sends nothing, needs no pairing)
sudo sensmos-gw-udp.py --dry-run

# 3 — find a Sensmos node on this network (prints the exact pair command)
sudo sensmos-gw-udp.py --find

# 4 — pair with it (address from step 3, PIN = the node's PIN)
sudo sensmos-gw-udp.py --pair NODE_IP --pin YOUR_PIN --name "rooftop RAK"

# 5 — install & start it as a boot-persistent service
sudo sensmos-gw-udp.py --install-service

# 6 — confirm
systemctl status sensmos-gw-udp --no-pager
journalctl -u sensmos-gw-udp -f

O passo 6 deve mostrar active (running), e o log imprime connection works — first batch accepted assim que os frames fluem, e depois uma linha de estado a cada minuto. Na app, o gateway aparece em Attachments do node; no mapa ao vivo o node ganha a camada RF.

Já emparelhado, mas sem serviço? O token sobrevive; repita os passos 1 e 5. Só se --install-service disser pair first repita o passo 4.

Veja antes de confiar em nós

--dry-run imprime, em JSON, exatamente os frames que reencaminharia e fica por aí. Adicione --log-tx para ver também quando o seu gateway transmite. O script é um único ficheiro legível; o código está no GitHub.

Emparelhamento — como funciona o token

O script não tem credenciais próprias. Pede ao node Sensmos na sua LAN (com o PIN do node) que responda por ele; o node retransmite o pedido ao backend pela sua própria ligação, e o backend emite um token limitado ao scope radio.frames — reportar frames recebidos, nada mais. O token fica guardado em /etc/sensmos-gw-udp.json (modo 0600). A partir daí o script fala diretamente com o backend; o node fica fora do circuito.

  • Um node pode responder por até quatro attachments.
  • O dono pode revogar qualquer um deles a partir da app (node → Attachments); o script fica inerte de imediato.
  • Um token vive 30 dias a partir do primeiro uso e o script renova-o sozinho.

O contrato completo (endpoints, scopes, formatos) está em Attachments e a API EXT.

O que ele lê

Semtech GWMP por UDP, reconhecido pelo cabeçalho do pacote, não pela porta, por isso funciona sejam quais forem as portas que a sua stack usa:

byte
0versão do protocolo (1 ou 2)
1–2token
3tipo — 0 PUSH_DATA (frames recebidos), 3 PULL_RESP (gateway instruído a transmitir)
4–11EUI do gateway
restoJSON

De PUSH_DATA extrai as entradas rxpk: frequência, spreading factor, RSSI, SNR, tamanho, resultado do CRC e o payload PHY em bruto. Os payloads de aplicação estão encriptados com chaves que não temos; o que a rede usa são os metadados de rádio e o reconhecimento de frames Sensmos no meio do tráfego.

PULL_RESP — uma transmissão que o seu gateway está prestes a fazer — é reportado apenas quando é audível por outros (ipol = false, p. ex. beacons de cobertura que algumas redes transmitem). Um downlink LoRaWAN normal usa IQ invertido que mais ninguém consegue ouvir, não prova nada e é ignorado. Os reportados são prova de que o gateway é real: o seu payload é imprevisível, por isso um node Sensmos que ouça os mesmos bytes de forma independente confirma que o gateway realmente transmitiu.

Um forwarder multi-servidor envia o frame idêntico para cada servidor configurado, pelo que a mesma transmissão aparece várias vezes no loopback (RF packets received: 1 / PUSH_DATA datagrams sent: 2 no log do seu forwarder). O script desduplica; uma transmissão é reportada uma vez. Os frames são agrupados (uma vez por segundo ou a cada 50 frames) e enviados para https://sensmos.com/v1/ext/radio/frames.

Flags

flagsignificado
--ifaceinterface a observar, por padrão lo
--dry-runimprimir o que seria enviado, não enviar nada
--log-txregistar também o que o seu gateway transmite
--findprocurar nodes Sensmos na LAN e imprimir o comando de emparelhamento
--pair IP --pin PIN --name "…"emparelhamento único através de um node
--install-servicecopiar para /usr/local/bin, escrever e ativar a unit systemd (mostrada antes)
--be URLbackend alternativo, para testes

A unit que ele escreve, caso prefira fazê-lo à mão:

# /etc/systemd/system/sensmos-gw-udp.service
[Unit]
Description=Sensmos passive gateway listener
After=network-online.target
Wants=network-online.target

[Service]
ExecStart=/usr/bin/python3 /usr/local/bin/sensmos-gw-udp.py
Restart=always
RestartSec=10
User=root

[Install]
WantedBy=multi-user.target

Docker e containers

O script escuta AF_PACKET no loopback do host. O loopback é privado por namespace de rede, por isso um container isolado vê um lo vazio e não ouve nada. Corrê-lo num container exige --network host e --cap-add NET_RAW. A forma suportada é o serviço systemd no host; uma build em container é sua para manter a funcionar.

Atualizações

O script nunca se atualiza sozinho — por design. Corre como root no seu gateway, na sua LAN; puxar código automaticamente de um servidor faria desse servidor uma porta de entrada para a sua rede. Para atualizar, repita o passo 1 e reinicie o serviço:

sudo curl -fsSL -o /usr/local/bin/sensmos-gw-udp.py https://raw.githubusercontent.com/Galusz/sensmos-gateway/main/sensmos-gw-udp.py
sudo systemctl restart sensmos-gw-udp

Resolução de problemas

  • root required: run with sudo — a captura em bruto precisa de CAP_NET_RAW. Use sudo (ou setcap cap_net_raw+ep no binário do Python, o que o concede a todos os programas Python — sudo é mais limpo).
  • Ligado mas sem frames — confirme que o tráfego está mesmo no loopback: sudo tcpdump -i lo -n -c 10 udp. Se o seu forwarder fala com um servidor remoto pela rede em vez de com uma bridge local, aponte o script para essa interface: --iface eth0.
  • AF_PACKET is Linux only — é mesmo. Isto corre no gateway, não num Mac.
  • --find não encontra nada — o node tem de estar no mesmo segmento da LAN e online (o seu /info tem de responder com ws_connected: true). Em alternativa, dê-lhe o IP do node a partir da app.
  • pair first em --install-service — o emparelhamento não foi concluído; verifique o PIN e se o node está online.

Desinstalar

sudo systemctl disable --now sensmos-gw-udp
sudo rm /etc/systemd/system/sensmos-gw-udp.service /usr/local/bin/sensmos-gw-udp.py /etc/sensmos-gw-udp.json

Revogue também o attachment na app. Nada mais foi alguma vez alterado, por isso não há mais nada para desfazer.

Na rede hoje

O primeiro gateway de terceiros juntou-se em agosto de 2026 (Alemanha), com um node sem rádio próprio a responder por ele. Os frames que ouve — transmissões de emergência e frames de sensores dos nodes ao alcance — são entregues como os frames ouvidos por qualquer node. Detalhes do que o LoRa faz pelo dono de um node: LoRa.

Perguntas, respostas diretas

Qual forma escolher?

A direta, se você puder adicionar um servidor ao packet forwarder — leva dois minutos, o gateway ganha um card próprio e rende. A passiva, se não quiser mexer na configuração do forwarder ou se o gateway pertencer a uma rede que proíbe isso.

Um gateway pareado rende GALU?

Sim, como um node com peso 0,3 — participa do pool de recompensas, mas não aumenta a emissão. Independentemente disso, quando um recebimento pago de frame de sensor é creditado ao primeiro ouvinte externo do dia e esse ouvinte é o seu gateway, 70% da taxa vai para você.

A forma direta faz o meu gateway transmitir?

Sim — um beacon curto de cobertura Sensmos a cada 5 minutos, enviado pelo forwarder como qualquer downlink, dentro do duty cycle da região. Os nodes que o ouvem confirmam que o gateway é real. O script passivo nunca transmite.

A posição é obrigatória?

Não. Sem ela, usa-se uma posição aproximada da conexão do gateway. Uma posição digitada precisa estar no mesmo país dessa conexão e a até 500 km dela.

Preciso de uma conta?

Não. A forma direta pareia o gateway com a sua carteira no app. Na forma passiva, um node Sensmos na mesma rede responde pelo gateway, que recebe um token restrito só para informar frames; o dono do node pode revogá-lo no app.

O que a rede recebe?

Cada frame LoRa que o gateway ouve, como metadados de rádio mais os bytes brutos. Os frames Sensmos (transmissões de emergência, frames de sensores) são reconhecidos e entregues aos donos; o tráfego LoRaWAN de terceiros conta só como estatística de espectro — o conteúdo é criptografado e não temos as chaves.

Meu gateway usa ChirpStack Gateway Bridge, não um forwarder puro

Para a forma direta não importa — adicione o servidor no bridge. Para a passiva existe outro agente (sensmos-gw-mqtt.py, no mesmo repositório) — não testado: não temos um gateway assim para verificar.

Atualizado: 2026-09-30