最近尝试学习协议适配框架,其中EdgeX作为一款开源的边缘计算平台受到大家的青睐,于是决定一试。目标是实现基于EdgeX的基础的南北MQTT通信功能,整体实现参考了一位大神的博客:
(13条消息) EdgeX 树莓派实践部署_无止无休-CSDN博客_edgex部署https://blog.csdn.net/bxjie/article/details/113860800由于大神写的比较高阶,作为小白的我踩了不少坑,下面分享给大家,仅供参考。
Raspberry Pi 4 Model B
不一定非要最新的,不同的硬件版本配套不同的操作系统及其他软件安装版本
microSD卡 128GB
没必要买很大,主要看需求。最好不要低于8G
micro转HDMI转接头
由于本人比较懒不想安远程控制软件,于是必须有个连接显示屏的转接头,具体规格因显示屏而异
键盘、读卡器、鼠标、电源线(TypeC)、显示屏
先用SD格式化软件格式化存储卡,如果后面安装出现问题(比如:安错系统,别问我怎么知道的...)想清空存储卡可以先SD格式化再用diskgenius软件恢复存储卡系统分区。这部分网上很多参考,也很简单。
Index of /raspios_arm64/images/raspios_arm64-2021-05-28
下载.zip文件即可。
烧录软件也有很多选择,我用的是官网的:
Raspberry Pi OS – Raspberry Pi
至此烧录完成。
第一次使用的话会有一些设置选择,可参考:
树莓派4B开门教程HDMI连接显示器 - 知乎
如果想安装一个中文输入法,可参考:
树莓派入门(八)—— 汉化Raspbian操作系统_bigmarshal的博客-CSDN博客_树莓派汉化
输入法需要reboot之后才生效。
按照大神的步骤可以开始搭建EdgeX了,为了整体流畅性,下面有一些步骤直接拷贝大神的
$curl -fsSL https://mirrors.ustc.edu.cn/docker-ce/linux/raspbian/gpg | sudo apt-key add -
获取docker-ce安装包,下载下面三个文件并离线安装:
https://download.docker.com/linux/debian/dists/buster/pool/edge/arm64/
$sudo dpkg -i containerd.io_1.3.7-1_arm64.deb
$sudo dpkg -i docker-ce-cli_19.03.13_3-0_debian-buster_arm64.deb
$sudo dpkg -i docker-ce_19.03.13_3-0_debian-buster_arm64.deb
设置docker镜像:
$sudo cat /etc/docker/daemon.json
{
"registry-mirrors": ["http://hub-mirror.c.163.com"]
}
$sudo systemctl daemon-reload
$sudo systemctl restart docker
$sudo apt-get install libffi-dev
$sudo apt-get install openssl
$pip3 install --default-timeout=100000 docker-compose
根据官网的命令可以quick-start:
Quick Start - EdgeX Foundry Documentation
$curl https://raw.githubusercontent.com/edgexfoundry/developer-scripts/master/releases/geneva/compose-files/docker-compose-geneva-redis-no-secty-arm64.yml -o docker-compose.yml
此时报错:
直接搜索报错内容说docker-compose没有安装成功,于是尝试大神的解决方法:
$pip3 install --upgrade --default-timeout=100000 docker-compose -i http://pypi.douban.com/simple
出现新的错误:
这样看是python版本不够,不匹配pysistent。先尝试升级python,遇到如下问题:
"sudo: /etc/sudoers is world writable\r\nsudo: no vaild sudoers sources found,quitting"
检索错误后发现原来是因为在安装docker镜像时为了写入json文件和解决DNS污染问题而重写入hosts中的raw.githubusercontent.com的IP地址(前面遇到的一个问题),而使得牵一发而动全身。解决方法:
$pkexec chmod0440 /etc/sudoers
当我以为可以简简单单升级python时,天真了...
升级也费劲,同时发现pyrsistent竟然没有,于是反向思维指定pyrsistent版本,并查看docker是否安装,发现新的错误:
docker.errors.DockerException:Error while fetching server API version:('Connection aborted'......
原因是docker_runner 没有docker的运行权限,解决方案:
vi /etc/group
...
docker:x:123:gitlab-runner
gitlab-runner:x:122:
...
至此,刚把获取docker-compose.yml文件的命令的坑填好...
下面进行EdgeX UI可视化,同时添加device-mqtt微服务.
在docker_compose文件中有注释mqtt,同时没有ui命令, 需要取消相应注释,同时补充ui。参考大神的图片:
本机输入localhost:4000/可以看到前端展示了,登录用本机账户和密码即可。
这一部分分为三个步骤:
1)北向基于 app-service-mqtt 实现 EdgeXCore 的 device-random 服务
2)南向基于 DeviceDemo 模拟设备发送数据报文
3)南北打通
流程图照搬大神的:
5.4.1 北向打通
这一步算是基础,因为后面的很多实现步骤都类似,而且也是理解整体业务流程的第一步,所以值得好好琢磨一下。
首先安装mosquitto:
$sudo apt-get install mosquitto
$sudo apt-get install mosquitto-clients
配置微服务,可以新建一个docker-compose-less.yml,也可以在原来的yml文件里面粘贴。
app-service-mqtt:
image: edgexfoundry/docker-app-service-configurable-arm64:1.2.0
ports:
- "0.0.0.0:48101:48101"
container_name: edgex-app-service-configurable-mqtt
hostname: edgex-app-service-configurable-mqtt
networks:
- edgex-network
environment:
<<: *common-variables
edgex_profile: mqtt-export
Service_Host: edgex-app-service-configurable-mqtt
Service_Port: 48101
MessageBus_SubscribeHost_Host: edgex-core-data
Binding_PublishTopic: events
Writable_Pipeline_Functions_MQTTSend_Addressable_Address: 172.17.0.1
Writable_Pipeline_Functions_MQTTSend_Addressable_Port: 1883
Writable_Pipeline_Functions_MQTTSend_Addressable_Protocol: tcp
Writable_Pipeline_Functions_MQTTSend_Addressable_Publisher: edgex
Writable_Pipeline_Functions_MQTTSend_Addressable_Topic: EdgeXEvents
depends_on:
- consul
- data
启动mqtt broker(默认port:1883):
$sudo mosquitto -v
启动mqtt微服务:
$sudo docker-compose -f docker-compose-less.yml up app-service-mqtt
启动MQTT订阅者:
$sudo mosquitto_sub -h 127.0.0.1 -p 1883 -t EdgeXEvents
5.4.2 南向打通
安装golang环境,网上资料很多。
下载device demo程序:
$git clone https://github.com/edgexfoundry/device-mqtt-go
device demo需要修改的地方如下:
const (
brokerUrl = "127.0.0.1"
brokerPort = 1883
username = "admin"
password = "public"
)
func runCommandHandler() {
var mqttClientId = "DeviceCommandSubscriber"
var qos = 0
var topic = "CommandTopic"
func runDataSender() {
var mqttClientId = "DeviceIncomingDataPublisher"
var qos = byte(0)
var topic = "DataTopic"uri := &url.URL{
Scheme: "tcp",
Host: fmt.Sprintf("%s:%d", brokerUrl, brokerPort),
User: url.UserPassword(username, password),
}client, err := createMqttClient(mqttClientId, uri)
defer client.Disconnect(5000)
if err != nil {
fmt.Println(err)
}var data = make(map[string]interface{})
data["name"] = "device_name_1"
data["cmd"] = "randfloat32"
data["method"] = "get"for {
data["randfloat32"] = rand.Float64()
jsonData, err := json.Marshal(data)
if err != nil {
fmt.Println(err)
}
client.Publish(topic, qos, false, jsonData)fmt.Println(fmt.Sprintf("Send response: %v", string(jsonData)))
time.Sleep(time.Second * time.Duration(15))
}}
编译device.go
go build
./mock
下载配置文档 mqtt.test.device.profile.yml(博客最后给出文件)
在UI和consul上设置参数(见大神博客)
5.4.3 南北打通
启动两个mqtt broker,以防之前的端口被占用,先查看一下:
ps -ef | grep mosquitto
如果有MQTT被占用,则kill it IPD
分别启动1883和1884的mqtt broker:
$sudo mosquitto -v
$sudo mosquitto -v -p 1884
启动docker-compose,并关闭device-random微服务,并检查容器此时开启的服务
$sudo docker-compose up -d
$sudo docker stop edgx-device-random
$sudo docker-compose ps
启动北向app-service-mqtt以及mqtt订阅者,注意此时的docer-compose里面的app-service-mqtt的port需要改为:1884
$sudo docker-compose -f docker-compose-less.yml up app-service-mqtt
$sudo mosquitto_sub -h 127.0.0.1 -p 1884 -t EdgeXEvents
启动南向:
go build
./mock
如果想查看docker中mqtt微服务的运行情况 ,可执行:
$sudo docker-compose up device-mqtt
在UI上的配置操作和前面一样,同样参考大神博客即可,显摆一张成功操作界面:)
命令行窗口的南北成功连接状态也可参考大神博客,下面是我的一些随便截图:
至此,基于EdgeX的简单南北MQTT搭建成功~~欢迎一起探讨~~
##mqtt.test.device.profile
name: "Test.Device.MQTT.Profile"
manufacturer: "Dell"
model: "MQTT-2"
labels:
- "test"
description: "Test device profile"
deviceResources:
- name: randfloat32
description: "device random number with Base64 encoding"
properties:
value:
{ type: "Float32", size: "4", readWrite: "R", defaultValue: "0.00", minimum: "100.00", maximum: "0.00", floatEncoding: "Base64" }
units:
{ type: "String", readWrite: "R", defaultValue: "" }
- name: randfloat64
description: "device random number with e notion"
properties:
value:
{ type: "Float64", size: "4", readWrite: "R", defaultValue: "0.00", minimum: "100.00", maximum: "0.00", floatEncoding: "eNotation" }
units:
{ type: "String", readWrite: "R", defaultValue: "" }
-
name: ping
description: "device awake"
properties:
value:
{ type: "String", size: "0", readWrite: "R", defaultValue: "oops" }
units:
{ type: "String", readWrite: "R", defaultValue: "" }
-
name: message
description: "device notification message"
properties:
value:
{ type: "String", size: "0", readWrite: "W" ,scale: "", offset: "", base: "" }
units:
{ type: "String", readWrite: "R", defaultValue: "" }
deviceCommands:
- name: testrandfloat32
get:
- { index: "1", operation: "get", deviceResource: "randfloat32"}
- name: testrandfloat64
get:
- { index: "1", operation: "get", deviceResource: "randfloat64"}
-
name: testping
get:
- { index: "1", operation: "get", deviceResource: "ping"}
-
name: testmessage
get:
- { index: "1", operation: "get", deviceResource: "message"}
set:
- { index: "1", operation: "set", deviceResource: "message"}
coreCommands:
- name: testrandfloat32
get:
path: "/api/v1/device/{deviceId}/testrandfloat32"
responses:
-
code: "200"
description: "get the random float32 value"
expectedValues: ["randfloat32"]
- code: "500"
description: "internal server error"
expectedValues: []
- name: testrandfloat64
get:
path: "/api/v1/device/{deviceId}/testrandfloat64"
responses:
- code: "200"
description: "get the random float64 value"
expectedValues: ["randfloat64"]
- code: "500"
description: "internal server error"
expectedValues: []
-
name: testping
get:
path: "/api/v1/device/{deviceId}/testping"
responses:
-
code: "200"
description: "ping the device"
expectedValues: ["ping"]
- code: "500"
description: "internal server error"
expectedValues: []
-
name: testmessage
get:
path: "/api/v1/device/{deviceId}/testmessage"
responses:
-
code: "200"
description: "get the message"
expectedValues: ["message"]
- code: "500"
description: "internal server error"
expectedValues: []
put:
path: "/api/v1/device/{deviceId}/testmessage"
parameterNames: ["message"]
responses:
-
code: "204"
description: "set the message."
expectedValues: []
- code: "500"
description: "internal server error"
expectedValues: []