之前写的LoRaWAN GW NS代码中提到了已经基于Twisted实现了GW/NS代码。其中NS代码设计起来比较简单。因为许多Linux系统中并没有Twisted,甚至CPython也是比较低的版本。所以必须基于MicroPython的多线程版本backport到低版本CPython中。
兼容性
MicroPython是基于CPython 3.4的,而旧版本CPython大多都是2.7的,甚至更低。一种做法是直接交叉编译MicroPython到这些SBC中,另外就是乖乖滴重新写代码了。
主要的兼容性问题在于Timer和time两个模块。
Timer
MicroPython将Timer归为machine下的模块,因为它的定时器是物理定时器,采用定时中断驱动。同时也正是这个原因,ISR代码大多采用lambda来写。
CPython中,Timer归为threading的子类。本质上是线程,所以CPython下循环定时器代码反而比MicroPython要繁琐。移植后的CPython版本,成了1+3四个线程的程序:主程序+UDP接收+两个定时器线程。
time
因为MicroPython来自CPython 3.4,所以有些高精度的计时方法,如tick_cpu()/tick_us()/sleep_ms(),其实这些也都是MicroPython特有方法,CPython 3.4里不是这些方法。目前我暂时以time.time()的浮点数来替代,理论上精确到0.1us。其实能够实现1ms就不错了。
LoRaWAN下发窗口其实是有时间精度要求的。在LoRaWANPktFwd协议中有三种方式计时:
- 立即下发;
- 时间戳下发;
- GPS时间戳下发。
如果计时有问题,会返回以下错误报告:
Value | Definition |
---|---|
NONE | Packet has been programmed for downlink |
TOO_LATE | Rejected because it was already too late to program this packet for downlink |
TOO_EARLY | Rejected because downlink packet timestamp is too much in advance |
COLLISION_PACKET | Rejected because there was already a packet programmed in requested timeframe |
COLLISION_BEACON | Rejected because there was already a beacon planned in requested timeframe |
TX_FREQ | Rejected because requested frequency is not supported by TX RF chain |
TX_POWER | Rejected because requested power is not supported by gateway |
GPS_UNLOCKED | Rejected because GPS is unlocked, so GPS timestamp cannot be used |
问题来了,在许多广域应用中,需要借助GPS授时,这当然没有疑问。立即下发也没有问题。反倒是没有严格要求的情况下,一般通过NTP进行授时,能够精确到ms么?好像不能。局域网内1ms,公网一般在100ms左右。如果终端设备没有授时装置,则需要网关不断地Beacon推送时间戳。加上TOF时间,误差真的还蛮大的。
更新(20180930)
基于CPython的多线程设计已经在Ubuntu和OpenWRT中调试完毕,现在只需要对接LoRaWAN USB dongle的HCI接口即可。改HCI接口参考Bluetooth SIG HCI而来,主要做了减法,简化了设计。
祝大家国庆快乐!