# LoRaWAN

> LoRaWAN 驱动参考：内置的 Basics Station 网络服务器、网关证书、传感器与集中器两种注册，以及负载寻址。

LoRaWAN 驱动把 Ganter Lab 变成一台小型的 **LoRaWAN 网络服务器**。这层关系的方向和大多数驱动是反的：不是工作站去够传感器，而是**网关连到 Ganter Lab**，传感器则把它们的读数作为上行数据通过那个网关送上来。Ganter Lab 内置了一台说 Semtech **LoRa Basics Station** 协议的网络服务器，所以任何跑着 Basics Station 固件的网关都能把本工作站当作它的 LNS。

内置服务器是在第一台已启用的 LoRaWAN 设备被载入正在跑的连接器时启动的，不是在应用开机时，并在最后一台被移除或禁用时停下：一台传感器和一台集中器都没配置时，端口上根本没有人在听。加一台就把它带回来，不必重启应用。

这个驱动下面的每一台设备都是**两种注册**之一，在它的面板里选：

- 一台**传感器**，由它的 DevEUI 标识，凭它的应用密钥被接纳。它的点位解码上行数据的负载。
- 一台**无线集中器**（也就是网关），由它的 Station EUI 标识，并注册在一个从列表里挑出来的**无线区域**上。它自己不带点位：在设备注册为传感器之前，它的页面上**添加点位**保持灰色，并说明原因。

集中器和传感器都要注册。Basics Station 集中器一连上来就会索要它的无线配置，在得到答复之前一个字都不转发，所以一台从未见过它 Station EUI 的工作站，会让它后面的每一台传感器都没有数值，而连接本身看上去还挺健康。答复是根据你挑的那个区域在这里生成的；没有什么配置文本要你去粘贴。

## 准备网关 {#prepare-the-gateway}

是网关通过 LoRa Basics Station 主动连接本工作站，而它出厂时并不知道工作站在哪里。每台登记为无线集中器的设备的页面上都有一张“**准备网关**”卡片，按本工作站写好要填入网关 Basics Station 设置的内容：

| 要在网关上设置的 | 卡片给出的 |
|---|---|
| LNS 地址（Basics Station 的 `tc.uri`） | `wss://<地址>:5001`，先是以本工作站名称写成的地址（内置 OPC UA 服务器配置中的“网络名称”，为空时是计算机名），然后是本工作站在网络上的每个 IPv4 地址各一条，每条带一个复制按钮。会核对服务器名称的网关（Basics Station 默认就核对，即 `TLS_SNI`）需要按名称写的地址；不核对的网关接受其中任何一个，这时用与它同一网络的那个地址即可。 |
| 基站 EUI | 集中器的基站 EUI，带一个复制按钮：本设备登记网关所用的标识。每个网关各是一台自己的集中器设备。它为空时，卡片会提示在“连接”卡片中填入。 |
| 客户端证书（`tc.crt`，密钥在 `tc.key` 中） | 双向 TLS：网关出示一份客户端证书，其通用名称或 DNS 备用名称中写有它的基站 EUI（见[证书](#certificates)）。 |
| 要信任的服务器证书（`tc.trust`） | 本工作站根证书 `GanterLabConnector-<station>.pem` 的完整路径；“**打开文件夹**”按钮会在 Windows 文件资源管理器中显示这个文件。按钮在工作站自己的窗口里起作用；在其他设备的浏览器里它保持灰色并说明原因。这个文件在网络服务器第一次启动时写出，在那之前卡片会这样提示。 |

工作站打开以来网关还没有连上过时，卡片是展开的；连上之后它会收起，但留在页面上，供下次设置网关时使用。证书没有任何 Windows 中已安装的根证书担保的网关，首次连接时会被拒绝，并在[现场设备访问](#equipment-access)中等待。只会说 Semtech UDP packet forwarder 协议的网关无法连接：它需要 Basics Station 固件。

这个文件对本工作站的所有网关都是同一个，工作站的名称或地址变了，它也不变。使用 Basics Station 默认设置的网关会接受本工作站：在网关的配置里没有什么需要关掉。

**凡是拿到过旧版本文件的网关，都需要把这个文件装成 `tc.trust` 一次。** 这个版本第一次启动时，如果工作站上的网关可能持有旧文件，卡片上会出现一条通知“**本工作站的文件已更换**”，只在那一次运行中显示。通知只是提醒，不是操作说明本身：对原来就有网关的工作站，不论卡片是否显示这条通知，都要在每台网关上把新文件装一次。

## 网络服务器与端口 {#network-server-and-ports}

| 项目 | 取值 |
|---|---|
| 协议 | LoRa Basics Station（WebSocket，JSON） |
| 监听 | 所有接口，TCP 端口 **5001**，只走 TLS |
| 网关地址 | `wss://<station-address>:5001` |
| 发现 endpoint | Basics Station 的 `router-info` 请求由同一个监听器服务 |
| 数据连接 | 以网关的 Station EUI 作为 WebSocket 路径的最后一段来寻址 |

监听永远是 TLS。没有哪个设置能把加密关掉：除了本地回环地址（它从厂区网络够不着，存在只是为了进程内测试）之外，任何地方都拒绝明文 HTTP 的监听。

## 证书 {#certificates}

接纳靠的是双向 TLS：服务器用自己的证书证明它是谁，而每一台网关都必须用一份客户端证书证明它是谁。

**服务器证书。** 网络服务器出示的是由本工作站自己的根证书签发的证书，OCPP 中央系统用的是同一个根证书。根证书每个配置档案只创建一次，在两个服务器中任何一个第一次启动时，并保存在应用数据文件夹里：

- `GanterLabConnector.pfx`：位于 `ua\pki\own\private` 下的私钥，用一个随机口令加密，而那个口令本身按 Windows 用户受保护（DPAPI），所以这一对文件被复制到别处之后，谁也打不开。
- `GanterLabConnector-<station>.pem`：位于 `ua\pki\station` 下的公开根证书。这就是你装到网关上、作为 `tc.trust` 让它信任本工作站的那个文件。

根证书的主题是 `Ganter Lab Connector (<station>)`，其中 `<station>` 是创建它那天的网络名称，而且**永不过期**。它的公开那一半是你亲手在每一台集中器上装一次的，所以一份会到期的根证书，会在它周年那天把整个传感器网络拖垮，在集中器那里被拒绝，而本工作站什么都看不到，还要跑一趟每一台集中器才能把它救回来。这里没有到期提醒，也没有续期命令，因为根本没有什么要续；驱动页面上的“**工作站根证书**”卡片把它带的那个日期作为一个读数显示出来（“**有效期至**”），并给出公开文件的完整路径和“**打开文件夹**”按钮，后者会在 Windows 文件资源管理器中显示这个文件（按钮在工作站自己的窗口里起作用；在其他设备的浏览器里它保持灰色并说明原因）。

网络服务器在这个根证书之下出示的证书，写明工作站的网络名称（内置 OPC UA 服务器配置中的“网络名称”，为空时是计算机名）、`localhost` 和工作站的每个 IPv4 地址，所以会核对服务器名称的网关也接受它。改计算机名、改网络名称或改工作站的地址，都不会更换根证书：只有它之下的那份证书会自动重新签发，集中器上无需做任何事。

只有已保存的根证书再也打不开时（例如是另一个 Windows 账户写入的），根证书才会重新创建。这种情况下，以及这个版本第一次在一个可能已把旧文件给过集中器的工作站上启动时，驱动页面都会在“**集中器需要新的证书文件**”之下说一次：每一台集中器都得拿到新的 `GanterLabConnector-<station>.pem`，作为 `tc.trust`。在它们拿到之前，它们连不上。

**网关客户端证书。** 每一台网关都必须在 TLS 握手期间出示一份客户端证书；没有证书的握手在任何数据流动之前就被丢掉。这份证书必须：

1. 以两种方式之一得到信任：在这台 Windows 机器上构成一条直到其中已安装的根证书的有效证书链；或者你已经在这个驱动页面的**现场设备访问**中批准了这份证书本身（见下文）。无论哪种方式，它都必须在有效期之内。检查不通过就拒绝连接，诊断日志记下是哪一项检查没过。
2. 在它的主体通用名或者一个 DNS 主体备用名里带上这台网关的 **基站 EUI**。这个标识符是按值来读的，所以 `AABBCCDDEEFF0011`、`AA-BB-CC-DD-EE-FF-00-11` 和 `AA:BB:CC:DD:EE:FF:00:11` 点的都是同一台网关。
3. 点名的必须是数据连接自称的**那一台网关**：一份点名了另一台网关、而不是连接地址里那一台的证书会被拒绝。

### 现场设备访问 {#equipment-access}

如果一台网关的证书是自签名的，或者由制造商自己的证书颁发机构签发，因而没有任何 Windows 中已安装的根证书为它担保，也不需要把那个根证书安装到这台电脑上。它的第一次连接会被拒绝，它的证书在 LoRaWAN 驱动页面末尾的**现场设备访问**里等待你的决定，这和 OPC UA 驱动页面上的是同一张卡片。

| 分组 | 列出的内容 | 你能做的事 |
|---|---|---|
| 等待批准 | 每一份被拒绝的证书，列在它所带的基站 EUI 之下：它的 SHA-256 指纹、最近一次尝试的时间和尝试次数，以及它的详细信息（主体、颁发者、域名、有效期）。 | **批准**从网关的下一次连接起接纳这份证书。**阻止**继续拒绝它，且不再把它列到这里。**丢弃所有待处理的证书**清空列表，不批准也不阻止任何东西。 |
| 已批准的现场设备 | 你批准过或导入过的证书，并注明是谁、在什么时候做的决定。 | **撤回批准**关闭网关的连接，并把它的证书放回列表。**导入证书**在网关第一次连接之前就批准它的公开证书。 |
| 被阻止的现场设备 | 你阻止过的证书。被阻止的证书即使 Windows 信任它也会被拒绝。 | **解除阻止**把证书放回列表，但不批准它。 |

批准的是一份一份的证书，从来不是签发它的证书颁发机构，所以同一制造商的第二台网关要等待它自己的批准。批准之前，先把指纹和网关报出的指纹核对一遍。批准不会改变这份证书是哪一台网关：它仍然只以它所带的基站 EUI 被接纳，而且只在有效期之内。Windows 已经信任的证书照旧被接纳，从不出现在这里；没有任何东西会被自动批准。

这些决定保存在工作站数据文件夹的 `ua\pki\lorawan` 下，重启后依然保留。只有可以配置 Connector 的角色才会看到这些命令，而且它们只在工作站自己的窗口里起作用：另一台设备上的浏览器只能读这张卡片，不能修改；也没有任何代理工具能批准证书。列表最多保存 100 份等待批准的证书；列表满了以后，新的证书仍会被拒绝，但不会列出，卡片会说明这一点。

## 连接字段 {#connection-fields}

LoRaWAN 设备没有自己的主机或端口：传感器是通过承载它无线流量的那台集中器够到的，而集中器够到的是本工作站。面板先显示注册类型的选择器，然后才是那种注册需要的字段。

| 字段 | 它是什么 | 格式 | 默认值 |
|---|---|---|---|
| 注册为 | 这台设备是两种注册中的哪一种：**传感器**或**无线集中器**。切换它会清掉下面的字段，因为应用密钥和区域不是一回事。 | 传感器 / 无线集中器 | 传感器 |
| DevEUI（传感器） | 这台传感器的 DevEUI，也就是它在入网请求里出示的身份。 | 16 位十六进制数，可以是纯的（`8899AABBCCDDEEFF`）、带短横线的（`88-99-AA-BB-CC-DD-EE-FF`）或者带冒号的。 | 空 |
| 应用密钥（传感器） | 这台传感器的 OTAA 应用密钥（AppKey）。它为入网做认证，并派生出会话密钥。 | 恰好 32 位十六进制数，不带分隔符。 | 空 |
| Station EUI（集中器） | 这台集中器的 Station EUI，也就是它的客户端证书必须带着、并且它写进连接地址里的那个标识符。 | 16 位十六进制数，上面那几种写法都行。 | 空 |
| 无线区域（集中器） | 这台集中器所在的地方。它决定本工作站在它连上时交给它的信道、数据速率和接收窗口。从列表里挑；没有自由文本。 | 欧洲 868 MHz、美国 915 MHz、澳大利亚 915 MHz、亚洲 923 MHz、中国 470 MHz（修订版 1）、中国 470 MHz（修订版 2） | 欧洲 868 MHz |
| 无传输的最长时间（传感器） | 这台传感器可以安静多少秒，超过之后它存着的那些值就不再算作读数。填 0 关掉过期。见下。 | 0 或更大 | 3600 |

如果设备的 DevEUI（传感器）或基站 EUI（集中器）仍然为空（比如刚添加的设备），它就会等待。工作站不会把它注册到网络服务器，也不会尝试连接；它在树里的那一行和页面顶部的提示会写“尚未填写 DevEUI”或“尚未填写基站 EUI”，而不是显示错误，**重新连接**没有可重新打开的东西，它的健康状态保持“尚未可知”。在“连接”卡片里填写这个标识符，工作站就会注册这台设备。一台保存时标识符为空、但以它的 EUI（十六位十六进制数字，没有分隔符）命名的设备不会等待：它照旧以自己的名称注册。

应用密钥是一份机密，本工作站也这样对待它。面板把它藏在圆点后面，旁边有一个**显示密码**的勾选框，让你在核对刚输入的内容时看到它；配置数据库把它保护起来，绑在输入它的那个 Windows 账户上；它既不出现在诊断日志里，也不出现在驱动读取的地址里，那个地址只带着 DevEUI。换一个 Windows 账户打开同一份配置，这把密钥不会跟着打开：这个字段会空着回来，并写着“在这个 Windows 账户下无法读取：它只能在输入它的那台计算机和那个 Windows 账户上打开。请在这里重新输入。”，在你重新输入密钥之前，传感器不会再入网。用备份带走的配置会保住它，因为备份是用你给它的密码封起来的。

入网**只支持 OTAA**：传感器通过集中器做一次空中入网，网络服务器用配置好的 AppKey 来应答它。来自任何已配置设备都不带的那个 DevEUI 的入网请求会被拒绝并写进诊断日志。不支持个性化激活（ABP，预先下发的会话密钥）。传感器的会话属于本工作站，而不属于转发它入网请求的那台集中器：覆盖范围内有不止一台集中器时，它的上行数据从听到它的那一台送进来，某台集中器掉线或被移除，也不会带走任何传感器。

禁用或删除一台设备会立刻把它从网络服务器上摘下来，不必等重启：它的密钥离开注册表，它在本工作站上持有的会话被释放，它下一次入网会被拒绝。禁用或删除一台集中器还会立刻关闭它的连接，它转发过的那些传感器保留各自的会话。

服务器不接受的注册根本不会做成：已填写但不是 16 位十六进制数的 DevEUI 或 Station EUI，没有填写或者不是 32 位十六进制数的应用密钥，或者本工作站无法配置的区域。这台设备读作没有连上，它在树里的那一行和它页面标题栏下面的红色文字会点名被拒绝的字段以及那里该填什么，诊断日志也留下同一行。不会为它启动网络服务器，本工作站也不会再次尝试同一个注册：改正那个字段才能让设备回来，而**重新连接**会用同样的拒绝来回答。

### 集中器的健康状态 {#the-concentrators-health}

集中器不带点位，所以没有哪个读数能说明它在不在。它的健康状态，也就是它在树里那一行上的圆点和驱动页面上的**健康状态**一列，说的是它的网关有没有连到本工作站：

| 情况 | 健康状态 |
|---|---|
| 基站 EUI 还空着 | **尚未可知**：什么都没有注册 |
| 已注册，网关还没连上（比如它的证书还在等待批准） | **离线** |
| 网关已连上 | **在线** |
| 网关掉线了：停了、断电了或者和网络断开了 | **离线**，直到它重新连上 |
| 已禁用 | **已禁用** |

集中器的改动保存之后，或者 Connector 启动之后，最多十秒内它读作**尚未可知**：重新注册会关闭网关的连接，网关要过一会儿才能重新连上。传感器不管集中器怎样，都按自己的读数保持自己的健康状态：范围内有不止一台集中器时，一台掉线，其余的照样转发。

### 交给集中器的是什么 {#what-the-concentrator-is-sent}

一台已注册的集中器做完它的版本握手之后，本工作站会用它所在区域的无线配置来应答：频率范围、数据速率以及其中哪些只用于下行，还有这个区域默认的信道计划。如果这个 Station EUI 没有在这里注册，那就什么都不发，诊断日志会说是哪一台网关问的、以及它没有注册。集中器连上了、而它后面一台传感器都从来没报过数值时，要找的就是这条记录。

没有轮询间隔字段。LoRaWAN 是推送式的：传感器自己决定什么时候上行，服务器按设备保留最近解码出来的那些负载，而点位的数值以固定的一秒周期从那份缓存里刷新。

因此，一台还没上行过的传感器是**没有读数**，而不是读数失败。点位保持空，设备报告的是“什么都还没观察到”，而不是转为离线，也不会有失败写进诊断日志：等第一次上行，正是协议在正常工作。而在传感器确实发来的负载里解析不到东西的地址是另一回事，那会作为一次读取失败原样报告出来。

### 传感器安静下来的时候 {#when-a-sensor-goes-quiet}

因为没有谁去轮询传感器，所以它最后发来的那份负载会一直待在缓存里，直到它再发一份。放着不管的话，一台电池耗尽的传感器会在工作站运行的整段时间里，一直把同一个数字当作一次好的读数发布出去，喂给图表、历史和报警一个已经不再为真的值。

因此每一台传感器都在自己的面板里带着一个以秒为单位的**无传输的最长时间**。默认是 **3600 秒**，也就是一小时，对常见传感器十到三十分钟的节奏来说绰绰有余；报得更稀的传感器就把自己的这个值抬高。过了那个期限：

- 它的点位不再带数值，改为发布质量不可用，而不是把最后一次上行重复下去。
- 设备面板会说这台传感器最后一次传输是什么时候，好让你看出它安静了多久。

把这个字段设成 **0** 就关掉了那台传感器的过期：它最后一次上行会一直读作良好。别的什么都不变，也不会丢弃任何存下来的负载。

## 点位寻址 {#tag-addressing}

LoRaWAN 的点位寻的是**解码后上行负载里的字节**。没有自由文本地址；“信号源”卡片提供两种模式下的定型字段，由**按 Cayenne LPP 的通道/类型寻址**这个勾选框切换：

| 字段 | 作用 | 取值 | 默认值 |
|---|---|---|---|
| 按 Cayenne LPP 通道/类型寻址 | 关：通道字段就是一个普通的字节偏移。开：在负载里搜索一个 Cayenne LPP 的通道/类型对。 | 开 / 关 | 关 |
| 通道 / 负载偏移 | 偏移模式：这个值起始处那个从 0 起的字节偏移。LPP 模式：要搜索的那个 LPP 通道号。 | 0 或更大 | 0 |
| LPP 类型码 | 只在 LPP 模式下有：跟在通道字节后面的那个 Cayenne LPP 数据类型字节（例如温度是 103）。数值字节是从匹配上的那个通道/类型对紧后面读起的，而有好几份负载都匹配时，最新的那一份胜出。 | 0 或更大 | 0 |
| 读取长度（字节，留空 = 自动） | 这个值由多少个负载字节组成。留空就由数据类型推出来：Boolean 和 Byte 是 1，Int16 是 2，Int32 和 Float 是 4，String 是 16。 | 1 或更大，或留空 | 留空 |
| 位掩码（十六进制，留空 = 无） | 一个十六进制掩码，在别的一切之前先与原始字节做按位与（例如用 `0x0FFF` 把状态位甩掉）。`0x` 前缀可写可不写，长度为奇数的掩码会在左边补零。掩码覆盖的字节数必须和读取长度恰好一样，否则读取报错。 | 十六进制文本或留空 | 留空 |
| 高字节在前（MSB first，大端序） | 声明这份负载里的值是大端序的；读到的字节在转换之前会被翻转。这里没有字序框：一份任意长度的负载里没有 16 位的字可以交换。 | 开 / 关 | 关 |
| 倍数（留空 = 无） | 一个总线侧的系数，由网络服务器在解码之后立刻应用，早于这个值进入点位自己的转换链。只对数值类型有效。填 0 会在打字的地方就被拒绝（“倍数为 0 会把每一次读数都归零。”），因为那会让这个点位每一次读数都以良好质量报成 0。 | 除 0 以外的任何数字，或留空 | 留空 |

一次读取的操作顺序是：取出被寻址的那些字节 → 应用位掩码 → 若“高字节在前”则翻转 → 转换成点位的数据类型 → 应用倍数（数值类型）。

例子，针对一台 11 字节负载在偏移 2 处带着一个大端序 Int16 温度的传感器：

- 偏移模式：通道/偏移填 `2`，读取长度填 `2`，“高字节在前”打开。
- LPP 模式，同一台传感器在通道 1 上以类型 103 说 Cayenne LPP：勾上 LPP 勾选框，通道填 `1`，类型码填 `103`，读取长度填 `2`。

## 支持的数据类型 {#supported-data-types}

| 类型 | 读取字节数（自动） | 这些字节怎么解释 |
|---|---|---|
| Boolean | 1 | 零为假，其余为真 |
| Byte | 1 | 无符号字节 |
| Int16 | 2 | 有符号 16 位整数 |
| Int32 | 4 | 有符号 32 位整数 |
| Float | 4 | IEEE 754 单精度 |
| String | 16 | 读取长度那么长的 UTF-8 文本 |

## 写入与设备发现 {#writes-and-discovery}

LoRaWAN 的点位是**只读**的：数值只来自传感器的上行数据，读写字段不提供可写的选项，运行时也会拒绝一次写入而不是假装成功。下行在协议里作为网络流量存在（入网接受、MAC 命令），从来不作为点位数值的写入。

这个驱动没有设备发现扫描。请手工用 Station EUI 和区域添加集中器，再手工用 DevEUI 和 AppKey 添加每一台传感器；一般的添加设备流程见 [Connector](connector) 页面。
