背景
昨今、IoT機器でメジャーになってきたMQTT通信について理解を深めたいと思います。
やはり、実際に通信の様子を観察するのが一番と考え、MQTTサーバーとクライアントを立ち上げてみたいと思います。
丁度最近、ラズパイ4Bを購入したので、この中で実験してみようと思います。
ラズパイ購入、立ち上げの様子は以下記事に記載していますので、もしご興味ございましたらご覧ください。
MQTT通信とは
MQTT通信とは、IoT製品向けに考案された軽量な通信プロトコルです。
昨今のIoT製品で広く利用されています。
従来のサーバー/クライアント形式の通信ではなく、パブリッシュ/サブスクライブという概念で通信します。
デバイス側(ここではラズパイのこと)がMQTTブローカーという中継装置に対し、「XXに関する配信があったら頂戴ね」というリクエストを送ります。
これをサブスクライブと言います。
そして、情報を配信する側(サーバー側)がMQTTブローカーに対してXXに関する配信(パブリッシュ)をすると、MQTTブローカーがデバイス側へXXに関する情報を送信します。
このXXのことを「トピック」と言います。
MQTT通信準備
サーバー(Broker)のインストール
以下コマンドでMQTTサーバーをインストールすることができます。
$ sudo apt install mosquitto
クライアントのインストール
以下コマンドでMQTTクライアントをインストールすることができます。
$ sudo apt install mosquitto-clients
送信テスト
以下コマンドでMQTTサーバーを起動します。
$ sudo systemctl start mosquitto
次に、別コンソールでMQTTクライアントを起動します。
$ mosquitto_sub -d -t orz
Client (null) sending CONNECT
Client (null) received CONNACK (0)
Client (null) sending SUBSCRIBE (Mid: 1, Topic: orz, QoS: 0, Options: 0x00)
Client (null) received SUBACK
Subscribed (mid: 1): 0
Client (null) sending PINGREQ
Client (null) received PINGRESP
MQTTクライアントが立ち上がったタイミングで、MQTTブローカーと接続(CONNECT)しています。
その後、トピック”orz”をサブスクライブしています(SUBSCRIBE)。
MQTTブローカーからACKを受信した後、MQTTクライアントとしてはサブスクライブ完了と判断したようです。
その後、MQTTクライアントから生存確認(PINGREQ)を定期的に送信しています。
これで、MQTTサーバーからトピック”orz”に対してパブリッシュすればMQTTクライアント側に通知が届くはずです。
早速、MQTTサーバーからトピック”orz”に対して”hello”を送信してみます。
$ mosquitto_pub -d -t orz -m "hello"
Client (null) sending CONNECT
Client (null) received CONNACK (0)
Client (null) sending PUBLISH (d0, q0, r0, m1, 'orz', ... (5 bytes))
Client (null) sending DISCONNECT
接続が切れていたのか今となっては不明ですが、パブリッシュの前に再度接続し直しています。
また、ログとしてはMQTTサーバー側なのにMQTTクライアントとして残るようですね。
確かにMQTTサーバーからパブリッシュするという手順がナンセンスだったのかもしれません。
↓MQTTクライアント側で受信を確認できました。
Client (null) received PUBLISH (d0, q0, r0, m0, 'orz', ... (5 bytes))
hello
“paho-mqtt”をインストール
コマンドラインでもいいのですが、”paho-mqtt”というpythonのライブラリが広く使われているようなので、勉強のためにインストールしてみます。
$ pip install paho-mqtt
error: externally-managed-environment
This environment is externally managed
To install Python packages system-wide, try apt install
python3-xyz, where xyz is the package you are trying to
install.
If you wish to install a non-Debian-packaged Python package,
create a virtual environment using python3 -m venv path/to/venv.
Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
sure you have python3-full installed.
For more information visit http://rptl.io/venv
note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.
システム全体に対してインストールする行為は許しません!というエラーらしいです。
この時の私は仮想環境という概念を理解しておらず、素のままでインストールするお○カさんでした。
大人しく仮想環境を用意し再度インストールに挑戦します。
$ mkdir ~/mqtt_pj
$ cd mqtt_pj
$ python3 -m venv venv
$ source venv/bin/activate
(venv) xxx@yyy:~/mqtt_pj $ pip install paho-mqtt
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Collecting paho-mqtt
Downloading https://www.piwheels.org/simple/paho-mqtt/paho_mqtt-2.1.0-py3-none-any.whl (67 kB)
---------------------------------------- 67.2/67.2 kB 181.2 kB/s eta 0:00:00
Installing collected packages: paho-mqtt
Successfully installed paho-mqtt-2.1.0
無事インストールできました。
仮想環境になると(venv)が前につきます。
pythonでMQTT操作するGUIを実装
トピックのサブスクとパブリッシュをGUIでしてみます。
import tkinter as tk
from tkinter import scrolledtext, StringVar, OptionMenu
import paho.mqtt.client as mqtt
import subprocess
import time
# MQTT settings
BROKER = 'localhost'
PORT = 1883
# Initialize the text area and other UI components first
root = tk.Tk()
root.title("MQTT GUI")
frame = tk.Frame(root)
frame.pack(padx=10, pady=10)
topic_var = StringVar(root)
topic_var.set("ondo/topic") # Set default value
# Dropdown for selecting the topic
topic_label = tk.Label(frame, text="Topic:")
topic_label.grid(row=0, column=0)
topic_dropdown = OptionMenu(frame, topic_var, *["ondo/topic", "shitsudo/topic"])
topic_dropdown.grid(row=0, column=1)
message_label = tk.Label(frame, text="Message:")
message_label.grid(row=1, column=0)
message_entry = tk.Entry(frame, width=40)
message_entry.grid(row=1, column=1)
send_button = tk.Button(frame, text="Send", command=lambda: send_message())
send_button.grid(row=1, column=2)
# Initialize text area
text_area = scrolledtext.ScrolledText(root, width=50, height=15, state=tk.DISABLED)
text_area.pack(padx=10, pady=10)
# Function to display received messages
def on_message(client, userdata, message):
text_area.config(state=tk.NORMAL)
text_area.insert(tk.END, f"Received: {message.topic}: {message.payload.decode()}\n")
text_area.config(state=tk.DISABLED)
text_area.see(tk.END)
# Initialize MQTT client
client = mqtt.Client(protocol=mqtt.MQTTv311) # Use the latest MQTT version
client.on_message = on_message
# Function to start Mosquitto
def start_mosquitto():
# Start Mosquitto in the background
subprocess.Popen(['sudo', 'systemctl', 'start', 'mosquitto'])
# Wait a moment
time.sleep(2)
# Start Mosquitto
start_mosquitto()
# Connect to MQTT broker and subscribe to topics
try:
client.connect(BROKER, PORT, 60)
# Topics to subscribe to
topics = ["ondo/topic", "shitsudo/topic"]
for topic in topics:
client.subscribe(topic)
text_area.config(state=tk.NORMAL)
text_area.insert(tk.END, f"Subscribed to: {topic}\n")
text_area.config(state=tk.DISABLED)
client.loop_start()
except Exception as e:
print(f"MQTT connection error: {e}")
# Function to send messages
def send_message():
topic = topic_var.get()
message = message_entry.get()
print(f"Sending: Topic='{topic}', Message='{message}'") # Debug output
client.publish(topic, message)
message_entry.delete(0, tk.END)
root.protocol("WM_DELETE_WINDOW", lambda: (client.loop_stop(), client.disconnect(), root.destroy()))
root.mainloop()
pythonのGUIでMQTT通信してみた
アプリケーション起動時に2つトピックをサブスクするようにしてみました。
ondo/topicへメッセージ送信します(メッセージ:atsui desune)。
成功です(ondo/topic: atsui desune)。
次はshitsudo/topicへメッセージ送信します(メッセージ:jimejime shitemasune)。
成功しました(shitsudo/topic: jimejime shitemasune)。
クローズドな環境ではありますが、MQTT通信の動きが何となく理解できた気がします。
次はラズパイ4BとPC間でMQTT通信してみたいと思います。
↓↓次の記事はこちらですので、ご興味ございましたらご覧ください。
[…] Raspberry PI 4 Model B(ラズパイ)でMQTT通信してみる(python) Raspberry PI 4 Model B(ラズパイ)で温度を計測してみる(python) […]