# MQTT
MQTT訊息佇列遙測傳輸 (Message Queuing Telemetry Transport) 是一種建構在TCP/IP協議上,且基於發布/訂閱 (Publish/Subscribe) 模式的輕量級通訊協議,基於硬體效能低下的遠端裝置以及網路狀況糟糕的情況下而設計的發佈/訂閱型訊息協定。
只要有一台運行中的MQTT Server,任何連線到此伺服器的節點,都可發佈(Publish)或訂閱(Subscribe)某一主題(Topic),不同節點可在不知對方節點情況下,送出資料或是取得指定的資料。

免費的MQTT伺服器最受歡迎的是Mosquitto:
https://mosquitto.org/
* Topic 命名
* 主題名稱使用英文
* 區分英文大小寫
* 主題名稱勿使用【 $ 】、【 # 】、【 + 】、【 - 】、【 * 】、【 空格 】等字元
* 階層數沒有固定
* 主題層級以斜線符號(/)區隔
* 萬用字元:
* (+)匹配單一階層的主題名稱,例a/+ 只收到a下一個層級的主題。
* (#)匹配多層主題名稱,這個字元放在最後面,例a/# 收到a層級以下所有主題。
* QoS(品質)設定
QoS代表發布者與代理人,或者代理人與訂閱者之間的傳輸品質。MQTT定義了0, 1和2三個層級的品質設定(實際支援情況依伺服器軟體而定,Mosquitto伺服器全都支援):
* 0:最多傳送一次。
像寄平信,不保證訊息會送達。
優點:佔用頻寬與傳送時間較少。
缺點:會有遺漏資料的可能性,如果遺漏資料不會對結果造成太大影響的情況下亦可使用。
* 1:最少傳送一次。
像寄送掛號信;Broker 收到 Publisher 訊息後,回應 PUBACK 給 Publisher 以確認收到要發布的訊息。
當 Publisher 沒收到 PUBACK 訊息,則 Publisher 會再重送此訊息。
優點: 佔用頻寬與傳送時間比 Qos 2 少。
缺點: 可能重複收到訊息,Subscriber 要自行篩選接收到的重複訊息。
* 2:確實傳送一次。
Broker 收到 Publisher 訊息後,回應 PUBREC 給 Publisher 以確認收到要發布的訊息。
Publisher 收到 PUBREC 訊息時,回應 PUBREL 給 Broker,告訴 Broker 可以傳送訊息給 Subscriber。
Broker 傳送訊息給 Subscriber 後,回應 PUBCOMP 訊息給 Publisher 告知訊息發送完畢。
優點: 不會重覆傳送相同訊息。
缺點: 佔用頻寬與傳送時間較多。
* Retain 設定
將Retain設定為true,則MQTT Broker必須保留該訊息,並且替換此主題的任何已存在的訊息,也就是會保留『最新的一筆』。未來有訂閱該主題的訂閱者將會取得該筆訊息。
如果發佈訊息的Payload為空且Retain設定為true,則Broker會丟棄該主題的保留訊息,之後有訂閱該主題的訂閱者將不會收到保留訊息。
如果發佈訊息時將Retain設定為false,MQTT Broker不會將此訊息儲存成保留訊息,也不會丟棄、替換已存在的保留訊息。
pip install paho-mqtt
Python Subscribe 程式碼範例:
```python=
import paho.mqtt.client as mqtt
# 當地端程式連線伺服器得到回應時,要做的動作
def on_connect(client, userdata, flags, rc):
print("Connected with result code "+str(rc)) # rc: return code判斷客戶端是否連接成功
# 將訂閱主題寫在on_connet中
# 如果我們失去連線或重新連線時
# 地端程式將會重新訂閱
client.subscribe("Test1/MQTT")
# 當接收到從伺服器發送的訊息時要進行的動作
def on_message(client, userdata, msg):
print(msg.topic+" "+ msg.payload.decode('utf-8')) # 轉換中文編碼utf-8
# 連線設定
client = mqtt.Client() # 初始化地端程式
client.on_connect = on_connect # 設定連線的動作
client.on_message = on_message # 設定接收訊息的動作
client.username_pw_set("account","pwd") # 設定登入帳號密碼
client.connect("192.xxx.xxx.xxx", 1883, 60) # 設定連線資訊(IP, Port, 連線時間)
# 開始連線,執行設定的動作和處理重新連線問題
# 也可使用其他loop函式來進行連接
client.loop_forever()
```
Python Publish 程式碼範例:
(每隔5秒發布溫度及時間的資訊)
```python=
import paho.mqtt.client as mqtt
import random
import json
import datetime
import time
ISOTIMEFORMAT = '%m/%d %H:%M:%S' # 設置日期時間的格式
# 連線設定
client = mqtt.Client() # 初始化地端程式
client.username_pw_set("account","pwd") # 設定登入帳號密碼
client.connect("192.xxx.xxx.xxx", 1883, 60) # 設定連線資訊(IP, Port, 連線時間)
while True:
t0 = random.randint(20,30)
t1 = datetime.datetime.now().strftime(ISOTIMEFORMAT)
payload = {'Temperature' : t0,'Time' : t1}
print (json.dumps(payload))
time.sleep(2)
#要發布的主題和內容
client.publish("TEST1/MQTT", json.dumps(payload), 2, False) # topic, payload, qos, retain
```
```python=
# 準備要傳送的訊息
messages = [
{'topic':"hello/world", 'payload':"test message"},
{'topic':"hello/world", 'payload':"test message", 'qos':0, 'retain':False},
("hello/world", "test message 2", 0, False) # topic, payload, qos, retain
]
# 一次發布多則 MQTT 訊息
publish.multiple(
messages,
hostname="mqtt.example.com",
port=1883,
auth={'username':'myuser','password':'mypassword'})
```
測試
Publish、Subscribe測試
開啟兩個終端機
一台為 Subscribe
一台為 Publish
也可以開啟MQTTlens 接收資料,或者和 python 互傳資料。
終端機-1 (Subscribe)
接收從 python Publish 出來的溫度資料(亂數出來的~)
還有從 MQTTlens Publish 出來的文字資訊
終端機-2 (Publish)
從 python Publish 溫度資料資料出去

**Reference:**
https://ithelp.ithome.com.tw/articles/10227131
https://ithelp.ithome.com.tw/articles/10214825
https://www.uj5u.com/qita/265995.html
https://ithelp.ithome.com.tw/articles/10216196
https://swf.com.tw/?p=1002
待看:
https://www.796t.com/article.php?id=80052
https://104.es/2021/05/24/mqtt-python-nodemcu/
https://www.rocksaying.tw/archives/2016/MQTT-3-Python-clients.html
https://tw511.com/a/01/29440.html
https://oranwind.org/-data-visualization-python-chuan-jie-mosquitto-mqtt-broker-2/
https://ithelp.ithome.com.tw/articles/10216196
https://www.uj5u.com/qita/265995.html
https://swf.com.tw/?p=1002