目前仅支持在 linux 上运行。
已支持以下功能:
- 数据上传:从文件读取或者从串口接收 JSON 格式的数据,通过 MQTT 发送给服务器
- 远程控制:可以借助 MQTT 的订阅机制从服务器接收数据,通过串口发送出去
- 网关离线时可以将数据暂时缓存到数据库内,网络连接恢复后再从数据库里面取出来上传
- 支持数据模板功能,即可以根据数据模板重新格式化来自终端的数据、添加自定义属性、预定义属性(例如添加时间戳)等,从而生成新的JSON数据
- 支持日志功能
- 支持 MQTTS(TLS v1.2,v1.3)
在本地启动 MQTT broker,例如使用 mosquitto:
# 默认监听 1883 端口
mosquitto -v
或者修改配置文件 gw.toml
,指定可用的 MQTT broker:
[server]
address = "127.0.0.1:1883"
修改配置文件(默认是 gw.toml),指定文件,并且将数据接口类型设置为 text_file
(默认为此配置):
[data_if]
if_name = "./data_if.txt"
if_type = "text_file"
运行网关程序:
cargo run -- -c gw.toml
在文件中写入数据(需要有换行 '\n'
):
echo "{\"id\":1,\"name\":\"SN-001\",\"temperature\": 27.45,\"humidity\": 25.36,\"voltage\": 3.88,\"status\": 0}" > data_if.txt
说明:网关程序每隔 1 秒读清一次文件,读清后需要手动写入数据。
顺利的话,可以在 mosquitto 的窗口内看到网关发送过去的消息。
修改配置文件(默认是 gw.toml),指定串口,并且将数据接口类型设置为 serial_port
:
[data_if]
if_name = "/dev/ttyS14"
if_type = "serial_port"
网关程序:
cargo run -- -c gw.toml
使用外部设备按 MIN 协议向串口发送数据( arduino/min 目录内有 Arduino UNO 的示例,烧录 min 中的程序):
{"id":1,"name":"SN-001","temperature": 27.45,"humidity": 25.36,"voltage": 3.88,"status": 0}
如果是 Arduino UNO,只有一个串口,只能用于和网关通信,网关的配置文件中配置接该串口即可。
顺利的话,网关会收到 Arduino 发送的消息,并且会发送给 mosquitto(可以在 mosquitto 的窗口内看到)。
将 SX1276 Lora 模块连接到 SPI 口上,修改配置文件(默认是 gw.toml),指定 spi 设备,并且将数据接口类型设置为 spi_sx1276
:
[data_if]
if_name = "/dev/spidev0.0"
if_type = "spi_sx1276"
网关程序:
cargo run -- -c gw.toml
需要使用另一个射频参数和 SpiIf::setup_lora
中定义的射频参数匹配的 Lora 设备发送数据(4字节头 + 前面提到的 json 字符串)。
网关已支持远程控制功能。该远程控制不是指可以远程控制网关,而是网关会将服务器发过来的控制命令发送给 MCU,MCU 去响应命令,例如点灯等。
arduino 目录下有 Arduino UNO 和 Arduino DUE 的代码示例(min 子目录),可以通过服务器控制 Arduino 点亮或熄灭 LED。
这里还是以使用 mosquitto 作为 broker 为例:
# 默认监听 1883 端口
mosquitto -v
按照 (2) 中的指引操作。
在另一个终端内使用 mosquitto_pub 按照 gw.toml 里面配置的 sub_topic
(默认为“ctrl/#”) 发送数据:
# 点亮 LED
mosquitto_pub -d -h "localhost" -p 1883 -t "ctrl/1" -m "turn_on"
# 熄灭 LED
mosquitto_pub -d -h "localhost" -p 1883 -t "ctrl/1" -m "turn_off"
从发布数据到 LED 点亮或熄灭大概会有 3s 左右延时。
在本地启动 MQTT broker,例如使用 mosquitto:
mosquitto -c mosquitto.conf
配置文件内容(按实际情况修改 ca 文件路径):
# mosquitto.conf
log_type error
log_type warning
log_type notice
log_type information
log_type debug
allow_anonymous true
# non-SSL listeners
listener 1883
# server authentication - no client authentication
listener 18885
# 指定 ca 文件路径
cafile ca/ca.crt
certfile ca/server.crt
keyfile ca/server.key
require_certificate false
tls_version tlsv1.2
修改 Cargo.toml 文件,开启 ssl
特性:
[features]
default = ["build_bindgen", "ssl"]
build_bindgen = ["paho-mqtt-sys/build_bindgen"]
ssl = []
修改配置文件(默认是 gw.toml),使用 ssl 协议,并指定 ca 文件:
[server]
#address = "127.0.0.1:1883"
address = "ssl://127.0.0.1:18885"
[tls]
cafile = "ca/ca.crt"
# pem 文件生成方式:cat client.crt client.key ca.crt > client.pem
key_store = "ca/client.pem"
编译运行网关程序:
cargo run -- -c gw.toml
[待整理]
数据模板引擎需要在配置文件中配置,减 gw.toml 内 [msg]
部分。
模板支持的功能:
- 可以使用原消息中字符串类型的属性值作为属性名
- 可以使用原消息中的属性值
- 可以新增自定义的属性名/属性值
- 可以使用有特定内容的模板,比如时间戳
对原始数据的要求:
-
格式为 JSON
-
不能有同名的属性
-
打算用做模板属性名的字符串属性值需要符合 JSON 属性名命名规范
模板示例(以 gw.toml
中的为例):
# 原始数据示例
"{\"l\":\"SN-004\",\"t\": 27.45,\"h\": 25.36,\"v\": 3.88,\"e\": 0}"
# 模板
template = "{<{l}>: [{\"ts\": <#TS#>,\"values\": {\"temperature\": <{t}>, \"humidity\": <{h}>,\"voltage\": <{v}>,\"status\": <{e}>}}]}"
以上设置的转换效果为:
# 原始数据
{"l":"SN-001","t": 27.45,"h": 25.36,"v": 3.88,"e": 0}
# 输出数据
{"SN-001": [{"ts": 1596965153255,"values": {"temperature": 27.45, "humidity": 25.36,"voltage": 3.88,"status": 0}}]}
模板注解:
<{l}>
:取原消息属性l
对应的属性值。例如,需要使用消息"t": 27.45
中t
的属性值27.45
作为输出数据中的属性值,需要在模板中填写<{t}>
<#NAME#>
:使用模板引擎可以提供的值。例如<#TS#>表示自 EPOCH 以来的秒数;- 符合 JSON 属性名命名规范的字符串类型的属性值可以作为模板中的属性名。需要将模板填成 "<{属性名}>" 的形式. 例如, 需要使用消息
{"l": "SN-001"}
中l
的属性值SN-001
作为输出数据中的属性名, 需要在模板中填写<{l}>
。
-
x86_64-unknown-linux-gnu
-
mips-unknown-linux-uclibc
- 需要为该目标平台编译 rust:Cross Compile Rust For OpenWRT
-
arm-unknown-linux-gnueabihf(树莓派)
-
mipsel-unknown-linux-musl
-
riscv64gc-unknown-linux-gnu
目录 tools 内有部分平台的编译脚本。
/mnt/f/wsl/OpenWRT/OpenWrt-SDK-ar71xx-for-linux-x86_64-gcc-4.8-linaro_uClibc-0.9.33.2/staging_dir/toolchain-mips_34kc_gcc-4.8-linaro_uClibc-0.9.33.2/bin/../lib/gcc/mips-openwrt-linux-uclibc/4.8.3/../../../../mips-openwrt-linux-uclibc/bin/ld: cannot find -lanl
按照 src/CMakeLists.txt: fix build on uclibc or musl 修改 .cargo/registry/src/crates.rustcc.com-a21e0f92747beca3/paho-mqtt-sys-0.3.0/paho.mqtt.c/src/CMakeLists.txt
修改后仍然可能因找到了主机的 libanl 报错,如果还报错,按如下方式修改:
#SET(LIBS_SYSTEM c dl pthread anl rt)
SET(LIBS_SYSTEM c dl pthread rt)
thread 'main' panicked at 'Unable to find libclang: "couldn't find any valid shared libraries matching: ['libclang.so', 'libclang-.so', 'libclang.so.']
sudo apt-get install clang libclang-dev
thread 'main' panicked at 'No generated bindings exist for the version/target: bindings/bindings_paho_mqtt_c_1.3.2-mips-unknown-linux-uclibc.rs', paho-mqtt-sys/build.rs:102:13
cargo install bindgen
sudo apt install libc6-dev-i386
cd ~/.cargo/registry/src/crates.rustcc.com-a21e0f92747beca3/paho-mqtt-sys-0.5.0
TARGET=mips-unknown-linux-uclibc bindgen wrapper.h -o bindings/bindings_paho_mqtt_c_1.3.2-mips-unknown-linux-uclibc.rs -- -Ipaho.mqtt.c/src --verbose
debug:clang version: clang version 10.0.0-4ubuntu1 debug:bindgen include path: -I/mnt/f/wsl/project/iot_gw/target/mipsel-unknown-linux-musl/release/build/paho-mqtt-sys-0e7cd946c58a7093/out/include
--- stderr fatal: not a git repository (or any of the parent directories): .git /usr/include/stdio.h:33:10: fatal error: 'stddef.h' file not found /usr/include/stdio.h:33:10: fatal error: 'stddef.h' file not found, err: true thread 'main' panicked at 'Unable to generate bindings: ()', /home/dell/.cargo/registry/src/crates.rustcc.com-a21e0f92747beca3/paho-mqtt-sys-0.3.0/build.rs:139:14 note: run with
RUST_BACKTRACE=1
environment variable to display a backtrace
安装 clang
sudo apt-get install clang
指定交叉编译工具链,例如:
export CC_mipsel_unknown_linux_musl=mipsel-openwrt-linux-gcc
export CXX_mipsel_unknown_linux_musl=mipsel-openwrt-linux-g++
找不到 -lz
:
将对应平台上的 libz.so 复制到所对应的工具链的库目录下。例如:
/mnt/f/wsl/tool/raspberrypi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/arm-linux-gnueabihf/lib$ ls -l libz*
lrwxrwxrwx 1 dell dell 14 Oct 29 18:16 libz.so -> libz.so.1.2.11
-rwxrwxrwx 1 dell dell 95880 Oct 29 18:15 libz.so.1.2.11
找不到 libz.h
:
根据运行环境上的 libz.so
的版本,下载对应的 zlib
源码,然后在编译时指定头文件路径,例如:
CFLAGS="-I/home/dell/wsl/source/libz-1.2.1100+2/libz
- error: unknown target triple 'riscv64gc-unknown-linux-gnu', please use -triple or -arch thread 'main' panicked at 'libclang error; possible causes include:
- Invalid flag syntax
- Unrecognized flags
- Invalid flag arguments
- File I/O errors If you encounter an error missing from this list, please file an issue or a PR!', /home/dell/.cargo/registry/src/mirrors.tuna.tsinghua.edu.cn-df7c3c540f42cdbd/bindgen-0.52.0/src/ir/context.rs:574:15
修改 bindgen-0.52.0
的源码,在打印上述信息前的位置将传给 clang
的标志打印出来:
println!("{:?}", clang_args);
clang::TranslationUnit::parse(
&index,
"",
&clang_args,
&options.input_unsaved_files,
parse_options,
).expect("libclang error; possible causes include:
- Invalid flag syntax
- Unrecognized flags
- Invalid flag arguments
- File I/O errors
If you encounter an error missing from this list, please file an issue or a PR!")
得到如下信息:
debug:Using bindgen for Paho C debug:clang version: clang version 10.0.0-4ubuntu1 debug:bindgen include path: -I/mnt/f/wsl/project/iot_gw/target/riscv64gc-unknown-linux-gnu/release/build/paho-mqtt-sys-b3925b784bd5c394/out/include ["--target=riscv64gc-unknown-linux-gnu", "-I/mnt/f/wsl/project/iot_gw/target/riscv64gc-unknown-linux-gnu/release/build/paho-mqtt-sys-b3925b784bd5c394/out/include", "-isystem", "/usr/local/include", "-isystem", "/usr/lib/llvm-10/lib/clang/10.0.0/include", "-isystem", "/usr/include/x86_64-linux-gnu", "-isystem", "/usr/include", "wrapper.h"]
可以看到,三元组 riscv64gc-unknown-linux-gnu
被传给了 clang
,同时,从 paho-mqtt-sys-0.5.0/build.rs
打印的信息可以看到 clang
的版本比较老,可能还不支持 riscv64gc-unknown-linux-gnu
三元组。最新的 16.0.0 版本是支持的()。
试一下 clang-13
:
sudo apt-get install clang-13
sudo apt-get install libclang-13-dev
ls -l /usr/bin/clang*
lrwxrwxrwx 1 root root 24 Mar 21 2020 /usr/bin/clang -> ../lib/llvm-10/bin/clang
lrwxrwxrwx 1 root root 26 Mar 21 2020 /usr/bin/clang++ -> ../lib/llvm-10/bin/clang++
lrwxrwxrwx 1 root root 26 Apr 20 2020 /usr/bin/clang++-10 -> ../lib/llvm-10/bin/clang++
lrwxrwxrwx 1 root root 26 Jul 6 20:01 /usr/bin/clang++-13 -> ../lib/llvm-13/bin/clang++
lrwxrwxrwx 1 root root 24 Apr 20 2020 /usr/bin/clang-10 -> ../lib/llvm-10/bin/clang
lrwxrwxrwx 1 root root 24 Jul 6 20:01 /usr/bin/clang-13 -> ../lib/llvm-13/bin/clang
lrwxrwxrwx 1 root root 28 Apr 20 2020 /usr/bin/clang-cpp-10 -> ../lib/llvm-10/bin/clang-cpp
lrwxrwxrwx 1 root root 28 Jul 6 20:01 /usr/bin/clang-cpp-13 -> ../lib/llvm-13/bin/clang-cpp
将 clang
软链接指向 clang-13
:
sudo rm /usr/bin/clang
sudo ln -s /lib/llvm-13/bin/clang /usr/bin/clang
仍然有问题:
debug:clang version: Ubuntu clang version 13.0.1-2ubuntu2~20.04.1 debug:bindgen include path: -I/mnt/f/wsl/project/iot_gw/target/riscv64gc-unknown-linux-gnu/release/build/paho-mqtt-sys-b3925b784bd5c394/out/include ["--target=riscv64gc-unknown-linux-gnu", "-I/mnt/f/wsl/project/iot_gw/target/riscv64gc-unknown-linux-gnu/release/build/paho-mqtt-sys-b3925b784bd5c394/out/include", "-isystem", "/usr/lib/llvm-13/lib/clang/13.0.1/include", "-isystem", "/usr/local/include", "-isystem", "/usr/include/x86_64-linux-gnu", "-isystem", "/usr/include", "wrapper.h"]
从 llvm 源码可以看到,支持 riscv64(https://github.com/llvm/llvm-project/blob/release/13.x/llvm/include/llvm/ADT/Triple.h):
riscv32, // RISC-V (32-bit): riscv32
riscv64, // RISC-V (64-bit): riscv64
但是好像没有 riscv64gc-unknown-linux-gnu
的组合。
继续搜索,有人提到,RISC-V 在 clang
和 rustc
上的三元组不同:https://github.com/rust-lang/rust-bindgen/issues/2136,并且该问题已经得到了解决,但是查看代码时,发现相关提交是 rust-bindgen
0.52.0 之后合入的。
修改 paho-mqtt-sys-0.5.0/Cargo.toml
,将 bindgen
的版本修改为 0.60,但还是不行,不知道什么原因,从报错信息中看,调用的仍然是 0.52.0 版。
阅读 paho-mqtt-sys-0.5.0/build.rs
源码可知,可以禁用 build_bindgen
属性,不要在编译过程中调用 bindgen
生成绑定。而是使用 bindgen
0.61.0 版手动生成绑定:
# 删除老版本的 bindgen
cargo uninstall bindgen
# 0.61.0 版本,bindgen 的 crate 改名了
cargo install bindgen-cli
cd ~/.cargo/registry/src/mirrors.tuna.tsinghua.edu.cn-df7c3c540f42cdbd/paho-mqtt-sys-0.5.0
# 但是运行的时候仍然是 bindgen
RUST_BACKTRACE=full TARGET=riscv64gc-unknown-linux-gnu bindgen wrapper.h -o bindings/bindings_paho_mqtt_c_1.3.8-riscv64gc-unknown-linux-gnu.rs -- -Ipaho.mqtt.c/src --verbose
有如下报错:
End of search list. thread 'main' panicked at 'assertion failed:
(left == right)
left:4
, right:8
: Target platform requires--no-size_t-is-usize
. The size ofssize_t
(4) does not match the target pointer size (8)', /home/dell/.cargo/registry/src/mirrors.tuna.tsinghua.edu.cn-df7c3c540f42cdbd/bindgen-0.61.0/codegen/mod.rs:851:25
带上 --no-size_t-is-usize
选项:
RUST_BACKTRACE=full TARGET=riscv64gc-unknown-linux-gnu bindgen --no-size_t-is-usize wrapper.h -o bindings/bindings_paho_mqtt_c_1.3.8-riscv64gc-unknown-linux-gnu.rs -- -Ipaho.mqtt.c/src --verbose
可以生成绑定 paho-mqtt-sys-0.5.0/bindings/bindings_paho_mqtt_c_1.3.8-riscv64gc-unknown-linux-gnu.rs
:
然后在 iot_gw/Cargo.toml
中,禁用 paho-mqtt-sys/build_bindgen
特性:
[features]
default = []
build_bindgen = ["paho-mqtt-sys/build_bindgen"]
ssl = []
进入 tools
目录,运行 build_f133.sh
即可。
thread 'main' panicked at 'Init data interface failed: DataIfUnknownType'
Cargo.toml 中有关数据接口的特性和网关配置文件内的不一致。
Error connecting to the broker: NULL Parameter: NULL Parameter
使用 ssl 连接 broker,但是没有在 Cargo.toml
中启用 ssl
特性。