Pages

Monday, 30 December 2019

树莓派安装Mosquitto,轻量级MQTT Broker ,连接各种物联网设备

Mosquitto 是一個開放原始碼 MQTT broker,安裝於樹莓派中就可以把所有的感測器、運算與控制設備連結起來,打造一個整合性的物聯網架構。
在物聯網的應用中,有許多的感測器會產生各種的資料,這些資料可能會傳送至資料庫中儲存、交給運算伺服器分析、或是直接傳遞至使用者端即時顯示,而物聯網中的各種設備也需要接收來自於使用者或自動控制程式程式的指令,進行各種智慧化的動作,要讓整個物聯網環境具備互相溝通的能力,就需要有一個資訊傳遞的機制。
MQTT 是一種 machine-to-machine(M2M) 的輕量級通訊協定,可以讓各種設備互相溝通,而其所需要的運算與傳輸頻寬很低,非常適合用於物聯網中的各種應用。
在 MQTT 的通訊架構之下,會有一台設備專門負責所有訊息的派送工作,這個角色就稱為 broker,所有的訊息在傳遞時都會經過 broker,由 broker 來負責處理每一則訊息該如何遞送。

MQTT 通讯协定与物联网IOT 应用整合概念
MQTT broker的实作有非常多种,例如ActiveMQ ApolloHiveMQMoscaMosquittoRabbitMQ等,基本上来说不管使用那一个实作版本都可以,功能上不会差太多。
这里我们以树莓派的Linux环境为例,介绍Mosquitto这一个MQTT实作版本的安装与使用方式,也就是把树莓派打造成一个MQTT broker的角色,负责所有物联网设备之间的相互沟通。
名称:Mosquitto
描述:开放原始码MQTT Broker
网址:Mosquitto官方网站

安装Mosquitto

在树莓派的Raspbian Linux中我们可以使用apt直接安装mosquitto套件,同时也顺便安装mosquitto-clients这个MQTT clients的套件,方便测试:
apt-get install mosquitto mosquitto-clients
正常来说,安装完成后mosquitto服务会自动启动,我们可以使用service指令检查一下mosquitto服务的状态:
service mosquitto status
服务的状态呈现绿色的active就表示Mosquitto有在正常运作,接着就可以开始使用MQTT Broker的功能了。

使用Mosquitto MQTT Broker

在MQTT 的架构中,设备可分为三种:
  • Publisher:发送讯息者。
  • Subscriber:接收讯息者。
  • Broker:转送讯息者。
而不同的讯息可能会需要传递给不同的接收者,所以讯息在发送的时候,发送者(publisher)必须标示这则讯息的主题(topic),而转送讯息者(broker)则会依照这则讯息的主题,将讯息传递给有订阅该主题的接收者(subscriber),这就是MQTT 基本的讯息传递架构。
在物联网的应用中,一个设备可以是讯息的接收者,接收从其他设备发送过来的讯息,同时也可以讯息的发布者,发送讯息给其他设备。
了解这个MQTT的基本架构之后,我们可以开启一个虚拟终端机视窗,使用mosquitto_sub指令来订阅指定的主题,也就是成为一个讯息的接收者:
mosquitto_sub -t gtwang/test
这里的-t参数就是指定要订阅的主题(topic),而后面的gtwang/test就是主题的名称。
MQTT 的主题在使用时不需要事先设定,直接在发布或订阅时指定自己要的主题名称即可。
MQTT中的主题名称类似一般的档案系统,是采用阶层的方式命名,我们可以自己决定命名的规则,要使用几层都可以,善用有条理的命名方式可以让讯息更好管理,例如sensors/COMPUTER_NAME/temperature/HARDDRIVE_NAME这样就是一个不错的命名方式。
接着再开启另外一个虚拟终端机视窗,作为讯息的发送者,使用mosquitto_pub将讯息发送至gtwang/test这个主题:
mosquitto_pub -t gtwang/test -m  " Hello, world! "
这里的-m参数就是指定要送出的讯息内容,执行这行指令之后,就会将讯息传送至broker,在由broker将讯息送给gtwang/test主题的订阅者,也就是另外一个虚拟终端机视窗,所以这时候就可以在另外一个视窗中看到这行讯息。
以上就是基本MQTT讯息传递的传送方式。

订阅多个主题

MQTT 的主题设计成阶层式的架构,主要是为了可以支援比较复杂的订阅规则,最简单的主题订阅方式就是直接指定完整的主题名称,例如:
sensors/my_computer/temperature/my_hd
除了这种方式之外,我们可以利用加号+这个万用字元来匹配任意的名称,例如:
sensors/+/temperature/+
这样的话就会接收到所有第一层为sensors而第三层为temperature的讯息。
假设有一个主题名称为a/b/c/d,则以下这几种主题指定方式都可以匹配该主题:
  • a/b/c/d
  • +/b/c/d
  • a/+/c/d
  • a/+/+/d
  • +/+/+/+
但是以下这几种就不会与a/b/c/d批配:
  • a/b/c
  • b/+/c/d
  • +/+/+
另外一种万用字元是井字号#,这个万用字元专门用于结尾处,可以匹配之后所有的阶层,以下几种写法都可以匹配a/b/c/d
  • #
  • a/#
  • a/b/#
  • a/b/c/#
  • +/b/c/#

使用者认证

预设的Mosquitto 服务是允许匿名登入使用的,也就是说任何人都可以透过MQTT 的协定传送与接收资料,若是在封闭的网路环境之下是没有问题,但若是开放性的网路,最好就要上一些安全机制。
最基本的安全机制就是设定登入的帐号密码,只有经过认证的使用者可以透过Mosquitto 服务传送或是接收资料,以下是Mosquitto 设定帐号与密码的步骤。
STEP 1
建立Mosquitto用的帐号密码档案,并新增gtwang这个使用者:
mosquitto_passwd -c /etc/mosquitto/passwd gtwang
执行这行指令后,接着要设定gtwang的密码。
STEP 2
编辑/etc/mosquitto/mosquitto.conf设定档,加入以下设定:
# 设定帐号密码档案
password_file /etc/mosquitto/passwd

#禁止匿名登入 
allow_anonymous false
STEP 3
重新启动mosquitto服务:
service mosquitto restart
STEP 4
以设定好的帐号密码登入,订阅gtwang/test主题:
mosquitto_sub -t gtwang/test -u gtwang -P secret123
以设定好的帐号密码登入,发送讯息至gtwang/test主题:
mosquitto_pub -t gtwang/test -u gtwang -P secret123 -m  " Hello, world! "
设定好使用者认证机制之后,接下来还要设定安全加密连线.

安全加密 TLS 連線

在安全性的考量上,僅僅只有加上帳號密碼是不夠的,至少還要有安全加密的保護,才能達到基本的防禦效果,否則讓帳號與密碼以明碼的方式在網路上傳遞,跟沒有設定帳號密碼是差不多的。
以下是 Mosquitto 設定安全加密 TLS 連線的步驟。
STEP 1
建立一個只有自己能讀取的目錄,隨後我們要在這個目錄中產生伺服器用的金鑰檔案。
mkdir myCA
chmod 700 myCA
cd myCA
STEP 2
產生金鑰的過程可以參考 mosquitto-tls 的 man page,而更快速的方式是使用 generate-CA.sh 這一個已經寫好的指令稿:
wget https://github.com/owntracks/tools/raw/master/TLS/generate-CA.sh
bash generate-CA.sh
執行 generate-CA.sh 之後,會自動產生伺服器用的金鑰。
STEP 3
將金鑰複製到 Mosquitto 的目錄之中:
sudo cp ca.crt /etc/mosquitto/ca_certificates/
sudo cp raspberrypi.crt raspberrypi.key /etc/mosquitto/certs/
STEP 4
重新啟動 Mosquitto 服務:
service mosquitto restart
如果重新啟動 Mosquitto 服務時,/var/log/mosquitto/mosquitto.log 出現這樣的錯誤:
Error: Unable to load server key file "/etc/mosquitto/certs/raspberrypi.key". Check keyfile.
通常是因為檔案權限不足的問題,只要修改一下檔案的擁有者即可:
sudo chown mosquitto /etc/mosquitto/certs/raspberrypi.key
STEP 5
檢查伺服器的金鑰是否正常運作:
mosquitto_sub -t '$SYS/broker/bytes/#' \
  -u gtwang -P secret123 \
  --cafile ca.crt -v
$SYS/broker/bytes/# 這個主題每隔 10 秒會輸出統計資訊,類似這樣:
$SYS/broker/bytes/received 1008
$SYS/broker/bytes/sent 253
$SYS/broker/bytes/received 1092
$SYS/broker/bytes/sent 325
有收到訊息就表示正常。
STEP 6
使用密碼配合 TLS 安全加密,訂閱 gtwang/test 主題:
mosquitto_sub -t gtwang/test -u gtwang -P secret123 \
  --cafile ca.crt
使用密碼配合 TLS 安全加密,發送訊息至 gtwang/test 主題:
mosquitto_pub -t gtwang/test -u gtwang -P secret123 \
  -m "Hello, world!" --cafile ca.crt

連接手機與電腦

在 Mosquitto 的 MQTT broker 伺服器架設好之後,任何支援 MQTT 協定的設備都可以直接連線進來傳送或是接收訊息,這裡我們以一台 Android 手機作為示範,將樹莓派上的訊息傳送至手機,並同時也可以將手機上的訊息傳回樹莓派。
在 Andorid 手機上有非常多 MQTT client 的 app,這次我選擇 IoT MQTT Dashboard 這一款支援加密的 MQTT client app 來做示範,而這個 app 在建立加密連線時需要讀取 BKS 格式的憑證檔案,所以我們要先把自己的 ca.crt 轉為 BKS 檔。
STEP 1
從 bouncycastle.org 下載 bcprov-ext-jdk15on-156.jar
wget http://bouncycastle.org/download/bcprov-ext-jdk15on-156.jar
STEP 2
將 ca.crt 轉換為 BKS 檔:
keytool -importcert -v -trustcacerts -file ca.crt \
  -alias IntermediateCA -keystore android.bks \
  -provider org.bouncycastle.jce.provider.BouncyCastleProvider \
  -providerpath bcprov-ext-jdk15on-156.jar \
  -storetype BKS -storepass mysecret
其中 mysecret 可以換成自己的密碼。
STEP 3
將 轉換好的 BKS 檔 android.bks 複製到 Android 手機上,然後開啟 IoT MQTT Dashboard 進行設定,填入樹莓派的基本資訊(左圖)。
Server 欄位就填入樹莓派的 IP 位址(可用 ifconfig 指令查詢),連接埠(Port)預設是 1883,Username 與 Password 就是剛剛上面新增的Mosquitto 帳號與密碼,將 SSL 打勾之後,並填入剛剛產生的 BKS 檔,還有 BKS 檔的密碼.
Android 手機 MQTT Dashboard
設定好 MQTT 伺服器之後,新增一個 Subscribe 設定,也就是訂閱一個主題,這裡我以 gtwang/test 這個主題做示範。
STEP 4
在樹莓派中執行以下指令,送出兩則測試訊息。
mosquitto_pub -t gtwang/test -u gtwang -P secret123 \
  -m "Hello, world!" --cafile ca.crt
mosquitto_pub -t gtwang/test -u gtwang -P secret123 \
  -m "This is a test." --cafile ca.crt
STEP 5
查看 Android 手機上的 MQTT Dashboard 訊息,正常的話這樣就可以收到從樹莓派發送出來的兩則訊息了。
STEP 6
讓 Android 手機發布訊息至樹莓派中,在 MQTT Dashboard 中新增一個 Publish 設定,發送訊息至 gtwang/test 主題。
這時候在樹莓派中就可以使用 Mosquitto 的 client 接收這個來自於 Android 手機的訊息:
Mosquitto client
這裡我們是將 Android 手機發送的訊息也送進 gtwang/test 主題中,而 Android 手機本身也是這個主題的訊息訂閱者,所以自己也會收到這則訊息,如果不想讓自己也收到訊息的話,可以更換訊息的主題(例如 gtwang/test2),要如何設計訊息的主題就要看實際的需求而定。
基本上不管訊息要從哪個設備發送,以及從哪個設備接收,只要依循 MQTT 的協定、設定好訊息主題,任何設備都可以非常容易地互相溝通,因此 MQTT 在物聯網的應用上可以說是相當有發展性的技術。