ENPLDEPT
Flash
Toda a documentação
Gateway LoRaWAN

Adicione o seu gateway LoRaWAN ao Sensmos — ouvinte passivo para packet forwarders Semtech

Um gateway Helium, TTN ou privado vê cada frame LoRa à sua volta. Um script passivo reporta os metadados de rádio ao Sensmos — as transmissões de emergência e os frames de sensores dos nodes próximos são entregues, e o seu gateway nunca muda o que faz para a sua própria rede.

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

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

Isto altera a configuração do meu gateway?

Não. Nada é editado, nenhum serviço é reiniciado, nenhum servidor é adicionado ao forwarder. O script apenas lê pacotes que já circulam no loopback. Mate-o e o gateway nem repara.

Pode fazer o meu gateway transmitir?

Não — não há nenhum caminho no código para isso. Reporta transmissões que o gateway faz por conta própria (beacons de cobertura audíveis), mas nunca provoca uma.

Preciso de uma conta?

Não. Um node Sensmos na mesma LAN responde pelo gateway e o backend emite-lhe um token restrito, limitado a reportar frames. O dono do node pode revogá-lo a partir da app a qualquer momento.

O que ganha a rede?

Cada frame LoRa que o gateway ouve, como metadados de rádio mais os bytes PHY em bruto. Os frames Sensmos (transmissões de emergência, frames de sensores) são reconhecidos e entregues aos seus donos; o tráfego LoRaWAN de terceiros conta apenas como estatística de espectro — os payloads estão encriptados e não temos chaves.

Um gateway ganha GALU?

O gateway não tem carteira própria. O node que responde por ele ganha a categoria de dados RF, o que aumenta o peso de recompensa desse node (+10% por categoria de dados). Além disso, quando uma receção paga de frame de sensor é creditada ao primeiro ouvinte estrangeiro do dia e esse ouvinte é o seu gateway, 70% da taxa vai para o dono do node que responde por ele.

O meu gateway corre o ChirpStack Gateway Bridge, não um forwarder simples

Então existe um broker MQTT local e um agente diferente (sensmos-gw-mqtt.py, no mesmo repositório) encaixa melhor — mas está por testar: não temos nenhum gateway desses para o verificar. Arranque-o com --dry-run e diga-nos o que vê. Esta página cobre o caso do packet forwarder simples, que é o que a maioria dos gateways de classe Helium corre.

Atualizado: 2026-09-03