收集一些在开发过程中参考的文档和文章,并记录下一些开发中的要点。

前言

项目需要的是一个懂硬件的软件开发,对协议有较深的理解,并对低功耗设备,无线通信技术有过研究,此外还需要有处理实时响应和高并发请求的后台开发经验~这些我统统没有,因此麻烦就很大了,好在有协议,也有按照协议实现的开源项目做参考,避免从头设计编写,而硬件上有离标准差一点点的网关和节点。

LoraWAN依赖Server,采用星状网,没有服务端是无法工作的,不过节点和网关的软硬件开发才是最重要,在开发过程中多次遇到问题,原因几乎都出在硬件上。包括节点与网关对协议实现不完善,单弹簧天线频段较窄,同频率上行和下行产生环路,4G网络的通信延迟,都是我无法直接解决的问题。

目前我主要负责服务端的部署和网关的二次开发,Lora技术已经普及了很久,关于LoraWAN的软硬件研发,各家有各家的特色。节点模块是否完善地实现了LoraWAN协议,是否兼顾了低功耗以及具体的使用环境,还有模块的质量与价格,此外还有网关,都是部署前必须考察的。我们的硬件工程师有一句口头禅:互联互通,现实中却很难实现的,许多人依旧在用开放协议做私有网络,一个厂家的网关是否可以正常和另一个厂家的节点通信都是一个问题,LoraWAN协议在设计时也埋了一些坑,没有考虑到一些具体的使用场景。

所以说使用LoraWAN作为一个通信手段,个人认为大概有几个原因:

1.缺乏人手和研发实力,需要现成的软硬件支持;

2.拥有人手和研发实力,但缺少一个噱头;

3.真正要搞事的。

正文

当前采用LoraWAN 1.0.2版本,选择CN470频段,同频上下行。这里针对节点,网关和服务端做一些记录,免得这些想法在一段事件后就遗失在记忆深处。

节点开发

使用模块是最快的方式,模块开发哪家强?每家各有吹牛逼的方式,那到底是不是真的牛逼,最简单就看出货量~不过对小厂使用者来说,能否获得技术支持才是至关重要的。

在最初选择考察供应商时,大厂采取最稳定的方式,同频上下行,提供CLAA或LoraWAN 1.0.1版本的模块或芯片,而我们在第一步就走错路了,选取了新版异频上下行方案,心想日后肯定会过渡到新版本协议,直到遇到了单根弹簧天线,无法完全承载470Mhz~510Mhz频段,因为没有时间再设计双天线方案或者重构模具,只好修改服务端,回归同频,而我们最后合作的模块供应商也在新版本中加入了全新的CN470同频选项,关闭ADR的情况下,服务端也可以不考虑开发MACCommand,因为这时已经没有其他从节点主动发起的MACCommand。之所以关闭ADR,是因为它影响到了节点电流,另外ADR对节点扩频通信因子的调整依赖于服务端的统计,越大的扩频因子有更大的传输距离和更小的传输速率,但如果一个节点在调整扩频因子后,失去了和网关的通信能力,那就完蛋了

我们的硬件工程师负责开发节点,他习惯在完全达成目标才报告,拒绝没有把握的事情,砍掉所有不稳定的因素,设计可靠的硬件。资深工程师的经验能让项目规避风险和冒险,而风险和冒险在99%以上的情况下,都会带来破坏性的结果,只是不冒险,不尝试的话,那实在太无聊了,但以软件开发角度来看待硬件开发也不合理。

网关开发

网关是标准的网关,壳体采用公模,核心软件基于官方packet_forwarder二次开发,搭载debian linux,支持4G网络与有线网络,没有低功耗设计,此外还有一次不足的地方。

1.GPS默认不启用,启用了取不到时间,而网关系统默认的NTP服务器无法访问,导致网关上时间不同步;

2.使用同频传输方案时,有概率出现网关下行信息再次被天线接受的情况,另外也会收到其他网关发送的下行信息,硬件设计上暂时无法解决;

3.工业级网关太灵敏,在节点靠近网关或者节点发射功率较大的情况下,会在接收到上行频率之外的高频和低频各产生一个数据接收,也就是说一次上行将产生三个接收,这里还不考虑确认上行失败重发与接收到其他网关下发数据的情况。

这里吐槽下,测试的节点使用14dbm的功率发射,距离网关10m以上,并且隔了堵墙,就在这种标准发射功率下,也有断断续续出现问题3,后期为了加大传输距离提升节点发射功率时,这种情况变成必现了,不过拉距测试中距离都是100米以上,基本就没有这种情况了。

鉴于这些问题,在网关设置了阿里云的NTP服务器,并追加一个UDP转TCP程序,负责数据预处理和传输,并担当起过滤数据和更新网关程序的角色。

NTP服务

在网关上需要安装ntp程序,修改ntp服务器,启用ntp程序,检查输出

配置文件/etc/ntp.conf

阿里云提供了内网与公共NTP服务器,链接:内网和公共 NTP 服务器

linux使用time1.aliyun.com~time7.aliyun.com,也可以使用ntp开头的子域名,不过会被重定向到time子域名,在配置文件中添加服务器

1
2
3
4
server time1.aliyun.com
server time2.aliyun.com
server time3.aliyun.com
server time4.aliyun.com

启用NTP,并关闭timesyncd

1
2
3
systemctl enable ntpd
systemctl start ntpd
timedatectl set-ntp false

TimeSyncd服务

Debian在Jessie版本时,已经引入了systemd,在修正网关时间同步的过程中,过了一遍systemd的使用指南,下面是阮一峰老师的SystemD入门与实战的教程:

Systemd 入门教程:命令篇

Systemd 入门教程:实战篇

配置文件/etc/systemd/timesyncd.conf

1
2
3
[Time]
Servers=time1.aliyun.com time2.aliyun.com time3.aliyun.com time4.aliyun.com
FallbackNTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org

设定时区与NTP同步

1
2
timedatectl set-timezone Asia/Shanghai
timedatectl set-ntp true

时间同步很重要,虽然在节点中也设置了时间同步,但能够同时收集一个数据的节点时间,到达网关时间与到达服务器的时间,对后续的优化有很大的参考价值。还有,systemd真是个好东西。

UDP转TCP程序

Semtech提供了一个转发程序,我们的供应商在这个基础上做了二次开发,适配了自己的硬件,遵循Semtech给出的网关协议,不足之出在于尚未支持Class B类型,对GPS没有开发完整,原本应该协议定义的数据包中的节点到达网关时间一直为空,此外它以UDP协议明文传输数据,不稳定,而且不安全。因此有必要在网关上添加一个转发程序,处理Packet_Forwarder发送的数据,主要做到了以下几点:

  1. 运行。作为守护进程运行,接收来自packet_forwarder的数据并给予回应;
  2. 数据过滤。根据Payload中的数据类型标志位,过滤硬件上无法避免,被动接收到的下行数据,包括网关自己下发的下行数据,以及从其他网关接收到的下行数据;
  3. 设置时间。上一步中校准了网关时间,这里可以取网关时间,填充进上行数据的时间戳中;
  4. 更新。实现对自身程序,packet_forwarder程序的更新,错误回滚等~这一点还在开发中。
  5. 传输。通过MQTT建立与服务器的连接,使用自签名证书,采取TLS加密数据。

服务端开发

这里偷一张来自LoraAlliance的图片

服务端对外暴露一个MQTT端口,网关访问MQTT时,用的是外网域名,而内网和本地测试访问MQTT时,用的又是不同的主机名或域名,启用TLS就免不了涉及证书,之后会记录下如何自签名证书。

在MQTT服务后是实现LoraWAN协议的服务,与LoraWAN直接相关的主要分三块,NetworkServer,ApplicationServer,CustomServer

NetworkServer

这个服务会是系统中负载最高的服务,需要实现的功能包括:

  1. 数据收集、筛选及校验。收集节点单次上行的全部数据,即一次上行中,从多个网关产生的重复数据,它们具有相同的payload与不同的头部,包括上行频率,RSSI,SNR,网关MAC,数据速率等关键数据,校验过程需要在第一步进行,优先排除无效数据,但JOIN过程不需要校验,因为这时还没有NwkSKey,其他数据上行都需要经过校验后入队列,在一个固定的收集时间后,取出全部数据排序后,选择具有最佳的RSSI与SNR头部的对象回复数据;
  2. 数据上行与下行。将节点数据交付ApplicationServer后休眠,在固定事件后唤醒并尝试从ApplicationServer拉取Payload,选取第一步操作中获取到的具有最佳信号的头部,取出网关MAC及其他头部信息,尝试从ApplicationServer获取payload,最后下发至指定网关。如果没有Payload,则返回协议定义的响应头部,避免消耗过多时间,导致节点重发数据(因为这些操作消耗的时间包含在RXDelay中);
  3. Session。当一个节点被激活后,每次数据上行都需要使用NwkSKey校验数据,AppSKey解密负载,要取出这两个Key,需要根据激活后获取到的DevAddr来获取DevEUI,进一步再获取两个会话密钥,这些都少不了数据库操作,因此有必要在内存中缓存活跃的节点数据,为每一个节点建立一个会话对象,保存相关的密钥,减少数据库查询。

由于NetworkServer直接与网关间接通过数据交互,当节点和网关增加时,会消耗大量的资源,NetworkServer应该设计为可拓展,支持负载均衡,或者通过关联特定的网关,分散压力。

ApplicationServer

这个服务将负责作为NetworkServer的后端,解密负载,与上层应用交互,会有更多的数据库操作。

  1. 数据加解密。解密上行的payload,加密下行payload;
  2. 节点与网关管理。处理API接口增删节点、网关,调整节点网络参数,统计网关与节点的上下行数据等;
  3. 处理节点入网。JOIN过程中根据相关参数生成密钥;
  4. 转发上行数据。将解密后的Payload转发给CustomServer;
  5. 提供下发数据接口。Payload部分属于用户自定义数据,由于无法对Class A节点进行主动下发,因此需要记录用户下发的数据,等待节点上行后捎带。

ApplicationServer的数据库操作较多,负责运维的同事也建议做读写分离,后期需要优化。

CustomServer

从这个服务开始,可以直接处理Payload了,但这时获取到的数据依旧会有重复和部分丢失的问题,需要配合节点上定义的协议开发。

小结

1.对接第三方程序时,要开发中间程序对接,再接入自己的系统,直接接入的话,后续的迭代会产生很多问题,大多都是沟通上的,外部问题应该集中在外部解决;

2.软件研发,或者说它归属的产品的研发,需要和时间赛跑,只能快,慢了不是输,而是产品死亡,无论什么都躲不了时间催命。

最后是开发过程中阅读的相关内容:

  1. LoraWAN 1.0 协议笔记
  2. TTN Wiki
  3. lora-alliance