# MQTT & Eclipse Mosquitto™
## MQTT 介紹
今天介紹的是因應 IoT 而生的 MQTT(Message Queuing Telemetry Transport)。
## 起源
MQTT 消息隊列遙測傳輸(Message Queuing Telemetry Transport)是 ISO 標準(ISO/IEC PRF 20922)下基於發布/訂閱範式的消息協議。它工作在 TCP/IP 協議族上,是為硬體性能低下的遠程設備以及網絡狀況糟糕的情況下而設計的發布/訂閱型消息協議,為此,它需要一個消息中間件 。
為何說是硬體性能低下,因為應用場景在 IoT 的應用需要考慮在低耗電長時間待命的狀態下在短時間內完成資料傳送過程。
以及網路狀況糟糕,因為地處偏遠訊號複雜狀態下會做訊息傳送與接收的確認。
## 特點介紹
- 連接: 等待與伺服器建立連接然後創建節點之間的連接.
- 斷開連接: 待 MQTT 客戶端完成所必須完成的工作,然後等待 TCP/IP 會話關閉連接。
- 發布: 將請求傳遞給 MQTT 客戶端後立即返回到應用程式執行緒。
- 服務品質(QOS)
- 服務品質: 服務品質指的是交通優先級和資源預留控制機制,而不是接收的服務品質。 服務品質是為不同應用程式,用戶或數據流提供的不同優先級的能力,或者也可以說是為數據流保證一定的性能水平的能力。
- 以下是每一個服務品質級別的具體描述
- 0: 最多一次傳送 (只負責傳送,發送過後就不管數據的傳送情況)
- 1: 至少一次傳送 (確認數據交付)
- 2: 正好一次傳送 (保證數據交付成功)
## MQTT的Publisher, Broker和Subscriber
根據[MQTT 3.1.1版本規格書](http://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html)的描述,MQTT是一種基於「發布∕訂閱」機制的訊息傳輸協定(MQTT is a Client Server publish/subscribe messaging transport protocol),我們可以把它想成雜誌發行和訂閱的機制。MQTT訊息發送端,相當於雜誌出版社,雜誌出版之後並不直接寄給消費者,而是交給經銷商或者書店一般的代理人(broker),來統籌管理發行和訂閱事宜。每一個訊息來源(刊物)都有個唯一的主題名稱(刊物名稱)。
代理人是個伺服器軟體,向伺服器發送主題的一方是發布者(publisher),從伺服器獲取主題的一方則是訂閱者(subscriber)。以下圖為例,傳送感測器資料的一邊是發布者,接收感測器資料的一邊則是訂閱者。每個感測器∕微控器的訊息都需要有個主題名稱以利識別,像下圖的主題A、B和C。

## 架設 MQTT Broker
### Windows
安裝 Mosquitto
[mosquitto-2.0.14-install-windows-x64.exe](https://mosquitto.org/download/)
安裝完成後,打開工作管理員確認服務是否開啟。

> #### Windows 要記得檢查防火牆設定,不然可能會連不到。
---
### Linux
```shell
> sudo apt-get update
> sudo apt-get -y upgrade
> sudo apt install -y mosquitto
```
檢查 Mosquitto 是否有執行
```shell
> sudo systemctl status mosquitto
```
```shell
● mosquitto.service - Mosquitto MQTT v3.1/v3.1.1 Broker
Loaded: loaded (/lib/systemd/system/mosquitto.service; enabled; vendor preset: enabled)
Active: active (running) since Thu 2022-03-17 05:37:30 UTC; 9min ago
Docs: man:mosquitto.conf(5)
man:mosquitto(8)
Main PID: 12499 (mosquitto)
Tasks: 3 (limit: 4677)
Memory: 1.2M
CGroup: /system.slice/mosquitto.service
└─12499 /usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf
Mar 17 05:37:30 mqtt systemd[1]: Starting Mosquitto MQTT v3.1/v3.1.1 Broker...
Mar 17 05:37:30 mqtt mosquitto[12499]: [ 220.488739]~DLT~12499~INFO ~FIFO /tmp/dlt cannot be opened. Retrying late>
Mar 17 05:37:30 mqtt systemd[1]: Started Mosquitto MQTT v3.1/v3.1.1 Broker.
```
## 設定 MQTT
MQTT 的設定檔為 **mosquitto.conf**,預設會放在以下路徑:
#### Windows
> 你的mosquitto安裝路徑/mosquitto/mosquitto.conf
#### Linux
> /etc/mosquitto/mosquitto.conf
加上以下基本設定
```
# MQTT 是否允許匿名連入
allow_anonymous true
# MQTT Port & Protocol 設定
listener 1883
protocol mqtt
listener 1884
protocol websockets
```
## 連線到 Broker & Subscribe topics
### Windows
Windows 可以使用 [MQTTX](https://mqttx.app/)。
新增一個連線


連線成功

Subscribe topics **"demo"**

### python
```python=1
import paho.mqtt.client as mqtt
# mqtt subscribe
def mqtt_sub(client, userdata, flags, rc):
print("Connected")
client.subscribe("demo")
# mqtt message
def mptt_message(client, userdata, msg):
print(msg.payload)
# mptt init
client = mqtt.Client()
client.on_connect = mqtt_sub
client.on_message = mptt_message
# 身分驗證用,後面會講
# client.username_pw_set("admin","admin")
# client.connect(HOST, PORT, KEEPALIVE)
client.connect("localhost", 1883, 60)
client.loop_forever()
```
### node.js
```javascript=
const mqtt = require("mqtt");
const mqttConnection = {
host: "localhost",
port: 1883,
endpoint: "/",
clean: true,
clientId: "node_32f83427",
username: 'admin', //身分驗證用,後面會講
password: 'admin', //身分驗證用,後面會講
};
const {host, port, endpoint, ...options} = mqttConnection;
const connectUrl = `mqtt://${host}:${port}${endpoint}`;
console.log("=== create connection mqtt ===", connectUrl);
const client = mqtt.connect(connectUrl, options);
client.on("error", (error) => {
console.log("[MQTT] Connection failed", error);
});
const subTopic = {"demo": {qos: 1}};
client.subscribe(subTopic, (error) => {
if (!error) {
console.log("[MQTT] Subscribe to topics", subTopic);
return;
}
console.log("[MQTT] Subscribe to topics error", error);
});
client.on("message", (topic, payload) => {
const data = JSON.parse(payload);
console.log(`[MQTT] topic ${topic}`);
console.log("[MQTT] ", data);
});
```
### 發送測試




## 新增 MQTT 身份驗證
### 建立密碼檔
### Windows
```shell
> 你的mosquitto安裝路徑\mosquitto\mosquitto_passwd.exe -c "密碼檔存放路徑\密碼檔名稱" username
Password:
Reenter Password:
```
### Linux
```shell
> sudo mosquitto_passwd -b 密碼檔存放路徑/密碼檔名稱 username password
```
### 修改 MQTT 設定檔
```
# MQTT 是否允許匿名連入
allow_anonymous false
password_file **密碼檔路徑**
```
要記得重啟 MQTT

```shell
> sudo systemctl restart mosquitto
```
設定好後,之後連入都要帶帳號密碼才能連線。