这是使用golang语言实现了一个应用,这个应用可以连接MQTT的服务端,接收JSON数据并解析的功能。

下面是应用,网关和节点的交互流程图
gw102-lorawan-app-txrx.png

 

这里是数据使用了base64编码的方式后打包到JSON数据格式。


package main

import (
    "encoding/base64"
    "encoding/json"
    "errors"
    "fmt"
    "log"
    "strings"
    "time"

    mqtt "github.com/eclipse/paho.mqtt.golang"
)

var (
    serverAddr    = "192.168.30.50"
    serverPort    = 1883
    serverUser    = ""
    serverPass    = ""
    deviceEui     = "eb03290421c69c6a"
    applicationID = "b3525049-5fea-4f22-a60d-6510ce7004e1"
    uplinkTopic   = "application/%s/device/+/event/up"
    downlinkTopic = "application/%s/device/%s/command/down"
)

type DownlinkMessage struct {
    DevEui    string `json:"devEui"`
    Confirmed bool   `json:"confirmed"`
    FPort     int    `json:"fPort"`
    Data      string `json:"data"`
}

// Encode encodes a LoRaWAN payload into a base64 string
func Encode(payload []byte) (string, error) {
    if len(payload) == 0 {
        return "", errors.New("payload is empty")
    }

    return base64.StdEncoding.EncodeToString(payload), nil
}

// Decode decodes a base64 encoded LoRaWAN payload into a byte slice
func Decode(payload string) ([]byte, error) {
    if len(payload) == 0 {
        return nil, errors.New("payload is empty")
    }

    return base64.StdEncoding.DecodeString(payload)
}

func handleMessage(client mqtt.Client, msg mqtt.Message) {

    // receive json message
    // use vipper to decode the json message
    var jsonMessage map[string]interface{}

    err := json.Unmarshal(msg.Payload(), &jsonMessage)
    if err != nil {
        fmt.Println("Error decoding payload:", err)
        return
    }

    fmt.Println("Received message:", jsonMessage)

    if jsonMessage["data"] == nil {
        fmt.Println("No data in message")
        return
    }

    fmt.Println("RECV RawData:", jsonMessage["data"])

    payload, err := Decode(jsonMessage["data"].(string))
    if err != nil {
        fmt.Println("Error decoding payload:", err)
        return
    }

    fmt.Printf("RECV NodeAddr %s\n", jsonMessage["devAddr"])

    // fport is float64, convert to int
    fPort := int(jsonMessage["fPort"].(float64))
    fmt.Printf("fPort %d\n", fPort)
    fmt.Printf("Data 0x%x\n", payload)
}

func publishMessage(client mqtt.Client, topic string, payload []byte) {
    token := client.Publish(topic, 0, false, payload)
    if token.Wait() && token.Error() != nil {
        fmt.Println(token.Error())
    }
}

func main() {

    opts := mqtt.NewClientOptions().AddBroker(fmt.Sprintf("tcp://%s:%d", serverAddr, serverPort))
    opts.SetClientID("go-mqtt-client")
    opts.SetUsername(serverUser)
    opts.SetPassword(serverPass)
    opts.SetDefaultPublishHandler(handleMessage)

    client := mqtt.NewClient(opts)
    if token := client.Connect(); token.Wait() && token.Error() != nil {
        panic(token.Error())
    }

    token := client.Subscribe(fmt.Sprintf(uplinkTopic, applicationID), 0, nil)
    if token.Wait() && token.Error() != nil {
        fmt.Println(token.Error())
    }

    for {
        time.Sleep(time.Second * 1)
        // wait user input "s,data", then publish message
        var input string
        fmt.Scanln(&input)
        parts := strings.Split(input, ",")
        if len(parts) == 2 && parts[0] == "s" {
            downlinkMessage := DownlinkMessage{
                DevEui:    deviceEui,
                Confirmed: true,
                FPort:     5,
            }
            encoded, err := Encode([]byte(parts[1]))
            if err != nil {
                fmt.Println("Error encoding downlink message:", err)
                return
            }
            downlinkMessage.Data = encoded
            jsonMessage, err := json.Marshal(downlinkMessage)
            if err != nil {
                fmt.Println("Error encoding downlink message:", err)
                return
            }
            fmt.Printf("Downlink message: %s\n", string(jsonMessage))
            publishMessage(client, fmt.Sprintf(downlinkTopic, applicationID, deviceEui), jsonMessage)
        }
    }

}

执行程序过程

./lorawan
s,11
Downlink message: {"devEui":"eb03290421c69c6a","confirmed":true,"fPort":5,"data":"MTE="}
Received message: map[adr:true confirmed:false data:EjRW deduplicationId:0e72c729-48bd-4445-abf6-2f047627701c devAddr:0147aae4 deviceInfo:map[applicationId:b3525049-5fea-4f22-a60d-6510ce7004e1 applicationName:i2som-lm401 devEui:eb03290421c69c6a deviceClassEnabled:CLASS_A deviceName:lm401bota deviceProfileId:2e320a4c-f9be-42b6-afcb-89052e8875f4 deviceProfileName:lm401otaa tags:map[] tenantId:52f14cd4-c6f1-4fbd-8f87-4025e1d49242 tenantName:ChirpStack] dr:0 fCnt:11 fPort:2 rxInfo:[map[channel:1 context:CgRe5Q== crcStatus:CRC_OK gatewayId:1172bdd0cd032a42 location:map[] metadata:map[region_common_name:CN470 region_config_id:cn470_10] nsTime:2025-04-28T14:05:24.371805290+00:00 rssi:-29 snr:11.8 uplinkId:33294]] time:2025-04-28T14:05:24.620808640+00:00 txInfo:map[frequency:4.865e+08 modulation:map[lora:map[bandwidth:125000 codeRate:CR_4_5 spreadingFactor:12]]]]
RECV RawData: EjRW
RECV NodeAddr 0147aae4
fPort 2
Data 0x123456

这里s,11是发送执行,s是发送命令,11是数据。11实际发送是0x3232

下面是LM401开发板连接串口后,执行AT+SEND命令时,接收到的数据。

lm401-tx-rx-data.png

EVT:5:02:3232,5是port,02是数据大小,3232是接收到的数据。

作者:SteveChen  创建时间:2025-04-28 21:12
最后编辑:SteveChen  更新时间:2025-05-08 17:19