1. LoRaWAN 终端节点入网参数规划与频段选择

LoRaWAN 终端节点入网参数规划与频段选择 学习卡片

任务目标

搭建一个基于SX1301/1308芯片的单通道LoRaWAN网关,使其能够接收来自LoRa节点的上行数据包,并通过UDP协议将数据转发至LoRaWAN网络服务器(如ChirpStack)。本案例聚焦于网关核心转发逻辑的最小可复现实现。

环境与依赖

  • 硬件:Raspberry Pi(或任意Linux主机)+ SX1301/1308 LoRa concentrator板卡
  • 系统依赖:libmpsse、spi-dev(内核已启用SPI)
  • 软件依赖:lora_gateway(Semtech HAL库)、packet_forwarder(Semtech UDP转发程序)
  • 网络服务器:ChirpStack(本地或远程,默认端口1700/UDP)

核心实现

以下代码为单通道网关的最小转发实现,基于Semtech HAL库的lgw_receive与lgw_send接口,将接收到的LoRa数据包封装为JSON格式并通过UDP发送至服务器:

// single_chan_gw.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "loragw_hal.h"
#include "loragw_gps.h"

#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 1700
#define RADIO_FREQ 868100000  // 868.1 MHz

static int udp_sock;
static struct sockaddr_in server_addr;

// 构造ChirpStack兼容的JSON上行数据
static void build_pull_resp_json(struct lgw_pkt_rx *pkt, char *buf, size_t size) {
    snprintf(buf, size,
        "{\"rxpk\":[{\"time\":\"%s\",\"tmst\":%u,\"freq\":%lu,"
        "\"chan\":0,\"rfch\":0,\"stat\":1,\"modu\":\"LORA\","
        "\"datr\":\"SF%dBW125\",\"codr\":\"4/5\",\"lsnr\":%.1f,"
        "\"rssi\":%d,\"size\":%u,\"data\":\"",
        pkt->utc_time, pkt->count_us, pkt->freq_hz,
        pkt->datarate, pkt->snr, pkt->rssi, pkt->size);
    
    // Base64编码payload
    static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    for (int i = 0; i < pkt->size; i += 3) {
        uint32_t n = pkt->payload[i] << 16;
        if (i+1 < pkt->size) n |= pkt->payload[i+1] << 8;
        if (i+2 < pkt->size) n |= pkt->payload[i+2];
        char tmp[4] = {b64[(n>>18)&63], b64[(n>>12)&63], 
                       (i+1<pkt->size)?b64[(n>>6)&63]:'=', 
                       (i+2<pkt->size)?b64[n&63]:'='};
        strncat(buf, tmp, size - strlen(buf) - 1);
    }
    strncat(buf, "\"}]}", size - strlen(buf) - 1);
}

int main(void) {
    // 初始化网关硬件
    if (lgw_board_setconf(BOARD_SX1301) != LGW_HAL_SUCCESS) {
        fprintf(stderr, "Board config failed\n");
        return -1;
    }
    lgw_radio_setconf(RADIO_TYPE_SX1257, 0, RADIO_FREQ);
    lgw_radio_setconf(RADIO_TYPE_SX1257, 1, RADIO_FREQ);
    lgw_channel_setconf(0, 0, RADIO_FREQ, 0, 125000);
    lgw_rxrf_setconf(0, 0, RADIO_FREQ, 125000, 0);
    lgw_rxif_setconf(0, 0, 0, 125000, 0, 0);
    if (lgw_start() != LGW_HAL_SUCCESS) {
        fprintf(stderr, "Gateway start failed\n");
        return -1;
    }

    // 创建UDP socket
    udp_sock = socket(AF_INET, SOCK_DGRAM, 0);
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);

    printf("Single-channel gateway running on %lu Hz...\n", RADIO_FREQ);

    // 主接收循环
    struct lgw_pkt_rx pkt;
    char json_buf[512];
    while (1) {
        memset(&pkt, 0, sizeof(pkt));
        if (lgw_receive(1, &pkt) == LGW_HAL_SUCCESS && pkt.size > 0) {
            build_pull_resp_json(&pkt, json_buf, sizeof(json_buf));
            sendto(udp_sock, json_buf, strlen(json_buf), 0,
                   (struct sockaddr*)&server_addr, sizeof(server_addr));
            printf("Forwarded packet: %u bytes\n", pkt.size);
        }
        usleep(1000);  // 1ms轮询间隔
    }

    lgw_stop();
    close(udp_sock);
    return 0;
}

编译与运行

# 编译(需链接Semtech HAL库)
gcc -o single_chan_gw single_chan_gw.c -I/opt/lora_gateway/libloragw \
    -L/opt/lora_gateway/libloragw -lloragw -lpthread -lm

# 运行(需root权限访问SPI)
sudo ./single_chan_gw

验证方法

  1. 硬件检查:运行dmesg | grep spi确认SPI设备已识别;执行cat /proc/spidev查看spidev设备节点。
  2. 服务器监听:在ChirpStack服务器端执行tcpdump -i any udp port 1700,观察是否有来自网关的UDP数据包。
  3. 端到端测试:使用LoRa节点(如SX1276)发送一个上行数据包,网关控制台应打印Forwarded packet: N bytes,同时服务器端tcpdump应捕获到包含rxpk字段的JSON数据。
  4. 通过条件:服务器成功解析出上行数据且RSSI/SNR值在合理范围(RSSI通常在-30~-120 dBm之间),即判定网关搭建成功。

常见问题

  • SPI通信失败:确认/boot/config.txt中已启用dtparam=spi=on,且板卡接线正确。
  • 收不到数据:检查频率设置是否与节点一致(本案例默认868.1MHz),并确认天线已连接。
  • UDP发送失败:确认服务器IP/端口可达,且防火墙未屏蔽1700端口。