IoT|通訊協議|MQTT| Mosquitto|Mac OS 及 樹莓派安裝及使用 Mosquitto

簡介

瞭解使用 MQ 遙測傳輸 (MQTT) 在行動式應用程式之間傳送訊息。通訊協定預期用於無線及低頻寬網路。使用 MQTT 的行動式應用程式透過呼叫 MQTT 程式庫傳送及接收訊息。透過 MQTT 傳訊伺服器交換訊息。MQTT 用戶端及伺服器可針對行動式應用程式可靠地處理遞送訊息的複雜性,並保持低成本地進行網路管理

MQTT 用戶端程式庫比較小。程式庫如同郵箱,使用連接至 MQTT 伺服器的其他 MQTT 應用程式傳送及接收訊息。透過傳送訊息(而不是保持連接至等待回應的伺服器),MQTT 應用程式可節約電池壽命。程式庫透過執行 MQTT 3.1 版通訊協定的 MQTT 伺服器將訊息傳送至其他裝置。您可以將訊息傳送至特定的用戶端,或使用發佈/訂閱傳訊來連接許多裝置

MQTT 是一種 machine-to-machine(M2M) 的輕量級通訊協定,可以讓各種設備互相溝通,而其所需要的運算與傳輸頻寬很低,非常適合用於物聯網中的各種應用。
在 MQTT 的通訊架構之下,會有一台設備專門負責所有訊息的派送工作,這個角色就稱為 broker,所有的訊息在傳遞時都會經過 broker,由 broker 來負責處理每一則訊息該如何遞送。


Mosquitto

MQTT 是 IBM開發的一個即時通訊協議。MQTT 是面向M2M和物聯網的連接協議,採用輕量級發佈和訂閱消息傳輸機制。
Mosquitto 是一款實現了 MQTT v3.1 協議的開源消息代理軟體,提供輕量級的,支持發佈/訂閱的的消息推送模式,使設備對設備之間的短消息通信簡單易用。安裝於樹莓派中就可以把所有的感測器、運算與控制設備連結起來,打造一個整合性的物聯網架構。

在物聯網的應用中,有許多的感測器會產生各種的資料,這些資料可能會傳送至資料庫中儲存、交給運算伺服器分析、或是直接傳遞至使用者端即時顯示,而物聯網中的各種設備也需要接收來自於使用者或自動控制程式程式的指令,進行各種智慧化的動作,要讓整個物聯網環境具備互相溝通的能力,就需要有一個資訊傳遞的機制。

MQTT broker 的實作有非常多種,例如 ActiveMQ Apollo、 HiveMQ、 Mosca、 Mosquitto、 RabbitMQ 等,基本上來說不管使用那一個實作版本都可以,基本上功能上不會差太多。

Mosquitto 安裝及設定 in Mac OS


Mac 版 Mosquitto 伺服器透過 Homebrew 套件管理員安裝。Homebrew 相當於 Debian Linux 系統上的APT套件管理員,如果之前沒有安裝過,可自行參考這篇筆記 IT|作業系統|Mac|Homebrew 安裝套件管理工具

[Install and start a MQTT Broker]

Install My Mac:
brew install mosquitto

修改組態設定
nano /usr/local/etc/mosquitto/mosquitto.conf

預設 Port
---------
# Port to use for the default listener.
#port 1883

網友提供 conf 範例中文說明
# =================================================================
# General configuration
# =================================================================

# 客户端心跳的间隔时间
#retry_interval 20

# 系统状态的刷新时间
#sys_interval 10

# 系统资源的回收时间,0表示尽快处理
#store_clean_interval 10

# 服务进程的PID
#pid_file /var/run/mosquitto.pid

# 服务进程的系统用户
#user mosquitto

# 客户端心跳消息的最大并发数
#max_inflight_messages 10

# 客户端心跳消息缓存队列
#max_queued_messages 100

# 用于设置客户端长连接的过期时间,默认永不过期
#persistent_client_expiration

# =================================================================
# Default listener
# =================================================================

# 服务绑定的IP地址
#bind_address

# 服务绑定的端口号
#port 1883

# 允许的最大连接数,-1表示没有限制
#max_connections -1

# cafile:CA证书文件
# capath:CA证书目录
# certfile:PEM证书文件
# keyfile:PEM密钥文件
#cafile
#capath
#certfile
#keyfile

# 必须提供证书以保证数据安全性
#require_certificate false

# 若require_certificate值为true,use_identity_as_username也必须为true
#use_identity_as_username false

# 启用PSK(Pre-shared-key)支持
#psk_hint

# SSL/TSL加密算法,可以使用"openssl ciphers"命令获取
# as the output of that command.
#ciphers

# =================================================================
# Persistence
# =================================================================

# 消息自动保存的间隔时间
#autosave_interval 1800

# 消息自动保存功能的开关
#autosave_on_changes false

# 持久化功能的开关
persistence true

# 持久化DB文件
#persistence_file mosquitto.db

# 持久化DB文件目录
#persistence_location /var/lib/mosquitto/

# =================================================================
# Logging
# =================================================================

# 4种日志模式:stdout、stderr、syslog、topic
# none 则表示不记日志,此配置可以提升些许性能
log_dest none

# 选择日志的级别(可设置多项)
#log_type error
#log_type warning
#log_type notice
#log_type information

# 是否记录客户端连接信息
#connection_messages true

# 是否记录日志时间
#log_timestamp true

# =================================================================
# Security
# =================================================================

# 客户端ID的前缀限制,可用于保证安全性
#clientid_prefixes

# 允许匿名用户
#allow_anonymous true

# 用户/密码文件,默认格式:username:password
#password_file

# PSK格式密码文件,默认格式:identity:key
#psk_file

# pattern write sensor/%u/data
# ACL权限配置,常用语法如下:
# 用户限制:user <username>
# 话题限制:topic [read|write] <topic>
# 正则限制:pattern write sensor/%u/data
#acl_file

# =================================================================
# Bridges
# =================================================================

# 允许服务之间使用"桥接"模式(可用于分布式部署)
#connection <name>
#address <host>[:<port>]
#topic <topic> [[[out | in | both] qos-level] local-prefix remote-prefix]

# 设置桥接的客户端ID
#clientid

# 桥接断开时,是否清除远程服务器中的消息
#cleansession false

# 是否发布桥接的状态信息
#notifications true

# 设置桥接模式下,消息将会发布到的话题地址
# $SYS/broker/connection/<clientid>/state
#notification_topic

# 设置桥接的keepalive数值
#keepalive_interval 60

# 桥接模式,目前有三种:automatic、lazy、once
#start_type automatic

# 桥接模式automatic的超时时间
#restart_timeout 30

# 桥接模式lazy的超时时间
#idle_timeout 60

# 桥接客户端的用户名
#username

# 桥接客户端的密码
#password

# bridge_cafile:桥接客户端的CA证书文件
# bridge_capath:桥接客户端的CA证书目录
# bridge_certfile:桥接客户端的PEM证书文件
# bridge_keyfile:桥接客户端的PEM密钥文件
#bridge_cafile
#bridge_capath
#bridge_certfile
#bridge_keyfile

啟動 Mosquitto 伺服器
brew services start mosquitto

啟動成功資訊
----------
remote: Counting objects: 14, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 14 (delta 0), reused 7 (delta 0), pack-reused 0
Unpacking objects: 100% (14/14), done.
Tapped 0 formulae (43 files, 55.2KB)
==> Successfully started `mosquitto` (label: homebrew.mxcl.mosquitto)

如不要用背景啟動,可用下列指令執行服務器
----------------------------------------------------
mosquitto -c /usr/local/etc/mosquitto/mosquitto.conf

停止服務 Mosquitto
brew services stop mosquitto

Mosquitto 安裝及設定 in 樹莓派

安裝 Mosquitto Broker 時可一併安裝 mosquitto-clients  MQTT clients 套件,方便後續開發測試
sudo apt-get install mosquitto mosquitto-clients

安裝過程若一切正常,正常情況安裝完成後 mosquitto 服務會自動啟動,可以使用 service 指令檢查一下 mosquitto 服務的狀態
service mosquitto status

啟動狀態
=======
● mosquitto.service - LSB: mosquitto MQTT v3.1 message broker
   Loaded: loaded (/etc/init.d/mosquitto)
   Active: active (running) since 二 2018-05-08 09:44:23 CST; 28s ago
  Process: 17308 ExecStart=/etc/init.d/mosquitto start (code=exited, status=0/SUCCESS)
Main PID: 434 (code=exited, status=0/SUCCESS)
   CGroup: /system.slice/mosquitto.service
           └─17313 /usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf

mosquitto.conf 組態檔路徑,後需若有需要可逕行調整
/etc/mosquitto/mosquitto.conf


訂閱(Subscriber)、發佈(Publisher)、轉送(Broker)訊息服務

在 MQTT 的架構中,設備可分為三種:
1.Publisher:發送訊息者。
2.Subscriber:接收訊息者。
3.Broker:轉送訊息者。


[主題命名策略]
MQTT 中的主題名稱類似一般的檔案系統,是採用階層的方式命名,可以自己決定命名的規則,要使用幾層都可以,善用有條理的命名方式可以讓訊息更好管理,例如 sensors/COMPUTER_NAME/My_Location/Place/temperature 這樣就是一個不錯的命名策略。

訂閱(Subscriber),使用指令:mosquitto_sub 來訂閱指定的主題,也就是成為一個訊息的接收者,MQTT 的主題在使用時不需要事先設定,直接在發布或訂閱時指定自己要的主題名稱即可
mosquitto_sub -h 127.0.0.1 -t dht-22/mypi1/myhome/bedroom/temperature

參數說明
----------
-h 127.0.0.1 主機名稱
-t 參數就是指定要訂閱的主題
sensors/mypi1/temperature/dht-22 主題名稱

發佈(Publish ),開啟另外一個虛擬終端機視窗,作為訊息的發送者, 將訊息發送至主題。執行這行指令之後,就會將訊息傳送至 broker,在由 broker 將訊息送給主題的訂閱者,也就是另外一個虛擬終端機視窗,所以這時候就可以在另外一個視窗中看到這行訊息
mosquitto_pub -h 127.0.0.1 -t dht-22/mypi1/myhome/bedroom/temperature -m "Hello word"
參數說明
------
-m "Hello word" 發送訊息內容

訂閱多個主題,MQTT 的主題設計成階層式的架構,主要是為了可以支援比較複雜的訂閱規則,最簡單的主題訂閱方式就是直接指定完整的主題名稱
如訂閱主題為 dht-22/mypi1/myhome/bedroom/temperature
可使用 dht-22/mypi1/+/bedroom/+

更多範例
-------
假設有一個主題名稱為 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 設定帳號與密碼的步驟。

建立 Mosquitto 用的帳號密碼檔案,並新增 kevin 這個使用者
mosquitto_passwd -c /etc/mosquitto/passwd kevin

設定 kevin 密碼,編輯設定檔
# 設定帳號密碼檔案
password_file /etc/mosquitto/passwd

# 禁止匿名登入
allow_anonymous false

重啟服務
service mosquitto restart

以設定好的帳號密碼登入,訂閱主題
mosquitto_sub -t dht-22/mypi1/myhome/bedroom/temperature -u kevin -P 999999

以設定好的帳號密碼登入,發送訊息至 gtwang/test
mosquitto_pub -t dht-22/mypi1/myhome/bedroom/temperature -u kevin -P 999999 -m "Hello, world!"

[安全加密 TLS 連線]

在安全性的考量上,僅僅只有加上帳號密碼是不夠的,至少還要有安全加密的保護,才能達到基本的防禦效果。


在安全性的考量上,僅僅只有加上帳號密碼是不夠的,至少還要有安全加密的保護,才能達到基本的防禦效果,否則讓帳號與密碼以明碼的方式在網路上傳遞,跟沒有設定帳號密碼是差不多的。
以下是 Mosquitto 設定安全加密 TLS 連線的步驟。

STEP 1

建立一個只有自己能讀取的目錄,隨後我們要在這個目錄中產生伺服器用的金鑰檔案。
cd ~
mkdir myCA
chmod 700 myCA
cd myCA

產生金鑰的過程可以參考 mosquitto-tls 的 man page,而更快速的方式是使用 generate-CA.sh 這一個已經寫好的指令稿
bash generate-CA.sh

執行 generate-CA.sh 之後,會自動產生伺服器用的金鑰。

將金鑰複製到 Mosquitto 的目錄之中
sudo cp ca.crt /etc/mosquitto/ca_certificates/
sudo cp raspberrypi.crt raspberrypi.key /etc/mosquitto/certs/

重新啟動 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

檢查伺服器的金鑰是否正常運作
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

使用密碼配合 TLS 安全加密,訂閱主題
mosquitto_sub -t dht-22/mypi1/myhome/bedroom/temperature -u gtwang -P 999999 \
  --cafile ca.crt

使用密碼配合 TLS 安全加密,發送訊息至主題
mosquitto_pub -t dht-22/mypi1/myhome/bedroom/temperature -u gtwang -P 999999 \
  -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 檔。

從 bouncycastle.org 下載 bcprov-ext-jdk15on-156.jar

將 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 999999

將 轉換好的 BKS 檔 android.bks 複製到 Android 手機上,然後開啟 IoT MQTT Dashboard 進行設定,填入樹莓派的基本資訊
Server 欄位就填入樹莓派的 IP 位址,連接埠(Port)預設是 1883,Username 與 Password 就是剛剛上面新增的Mosquitto 帳號與密碼,將 SSL 打勾之後,並填入剛剛產生的 BKS 檔,還有 BKS 檔的密碼。

設定好 MQTT 伺服器之後,新增一個 Subscribe 設定,也就是訂閱一個主題,這裡以 dht-22/mypi1/myhome/bedroom/temperature 這個主題做示範

在樹莓派中執行以下指令,送出兩則測試訊息
mosquitto_pub -t dht-22/mypi1/myhome/bedroom/temperature -u kevin -P 999999 \
  -m "Hello, world!" --cafile ca.crt
mosquitto_pub -t dht-22/mypi1/myhome/bedroom/temperature -u kevin -P 999999 \
  -m "This is a test." --cafile ca.crt

查看 Android 手機上的 MQTT Dashboard 訊息,正常的話這樣就可以收到從樹莓派發送出來的兩則訊息了


[Write first MQTT Application]

Add Eclipse Paho dependency to my Maven Project
<dependency> <groupId>org.eclipse.paho</groupId> <artifactId>org.eclipse.paho.client.mqttv3</artifactId> <version>1.1.0</version> </dependency>

[Build and Run the Application]


[Run the Subscriber]

編譯及執行
mvn clean package
./target/mqtt-sample subscriber

[Run the Publisher]

編譯及執行
./target/mqtt-sample publisher "My first MQTT message..."




[參考]

留言

這個網誌中的熱門文章

IoT|硬體|樹莓派|外接麥克風及喇叭設置

成長|語文|學習-英文 持續更新!

IoT|硬體|通訊|Arduino 使用 SoftwareSerial Library 與電腦通訊