Pythonを用いたPC(Mac) - LEGO Mindstorms EV3間のMQTT通信について

# 参考

https://qiita.com/yohei1126/items/de355f540a83743213fb

https://qiita.com/ekzemplaro/items/95c31c684a2faef656d1

https://www.mochinoki-labo.com/python-mqtt-intro/


# 概要　
PC(Mac)でBroker(mosquitto)を立ち上げ、LEGO Mindstorms EV3とのMQTT通信を行った際に
少しつまずいたのでメモ

# 環境
**PC**
- macOS Catalina バージョン 10.15.7
- Homebrew 3.3.10
- Python 3.8.5(paho-mqtt使用)
- mosquitto version 2.0.14

**EV3**
- OS : ev3dev
- Python 3.5.3

# 内容
PCでBrokerを立ち上げPCからメッセージをEV3へ送信する
PC(Broker, publisher) → EV3(subscriber)

**PC**
1. mosqittoをインストール
2. paho-mqttインストール 
3. publish用のコード作成

**EV3**
1. subscribe用のコード作成 

**実行手順**
1. PC: Broker(mosquito)起動
2. EV3: subscribe用のコード実行 <font color="OrangeRed">←つまずいた箇所</font>
3. PC: publish用のコード実行
4. EV3: 確認




## PC
1. **mosqittoをインストール**

```
% brew install mosquitto
```

初期状態だとパス が通っていないため以下でBroker起動できる

``` 
$ /usr/local/opt/mosquitto/sbin/mosquitto
```

mosquittoのパスを通すため .zshrc に以下の記述を追加

```shell:.zshrc
export PATH=/usr/local/opt/mosquitto/sbin:$PATH
export PATH=/usr/local/opt/mosquitto/bin:$PATH
```

```
% source .zshrc
```

パスが通っていれば以下のコマンドでBrokerを起動できる

```
% mosquitto

1642506173: mosquitto version 2.0.14 starting
1642506173: Using default config.
1642506173: Starting in local only mode. Connections will only be possible from clients running on this machine.
1642506173: Create a configuration file which defines a listener to allow remote access.
1642506173: For more details see https://mosquitto.org/documentation/authentication-methods/
1642506173: Opening ipv4 listen socket on port 1883.
1642506173: Opening ipv6 listen socket on port 1883.
1642506173: mosquitto version 2.0.14 running

```

2.　**paho-mqttインストール** 
問題なくインストールできればok

```
pip install paho-mqtt
``` 

3.　**publish用のコード作成**
コードは以下の記事のコードを参考にしています

https://www.mochinoki-labo.com/python-mqtt-intro/

コードの内容としてはBrokerに対してトピックを指定した上で
"Hello_EV3!!"という文字列を5回送信

```python:mqtt_pub.py
#!/usr/bin/env python3
# === プログラミング概要 ===#
# MQTTテスト用のプログラム
# Publisher
# =======================#

import paho.mqtt.client as mqtt
from time import sleep

MQTT_PORT = 1883
KEEP_ALIVE = 60
TOPIC = "topic/01/"

#Brokerに接続できたとき
def on_connect(client, userdata, flag, rc):
  print("Connect Broker:" + str(rc))

#Brokerと切断したとき
def on_disconnect(client, userdata, rc):
  if rc != 0:
     print("disconnect broker")

#publishが完了したとき
def on_publish(client, userdata, mid):
  print("publish Done")

#ログ
def on_log(client, userdata, level, buf):
    print("log: ", buf)

client = mqtt.Client()
#コールバックを登録 
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.on_publish = on_publish
client.on_log = on_log


client.connect("localhost", MQTT_PORT, KEEP_ALIVE)  

for i in range(5):
    client.publish(TOPIC,f"{i}Hello_EV3!!")
    sleep(3)

client.disconnect()
```

## EV3 

1. **publish用のコード作成**

コードの内容としてはBrokerに対して指定したトピックの内容を受信

```python:mqtt_sub.py
#!/usr/bin/env python3
# === プログラミング概要 ===#
# MQTTテスト用のプログラム
# Subscriber
# =======================#

import paho.mqtt.client as mqtt 

MQTT_PORT = 1883
KEEP_ALIVE = 60
TOPIC = "topic/01/"

#Brokerに接続できたとき
def on_connect(client, userdata, flag, rc):
  print("connect broker:" + str(rc))
  client.subscribe(TOPIC) 

#Brokerと切断したとき
def on_disconnect(client, userdata, rc):
  if  rc != 0:
    print("disconnect broker")

#メッセージ受信
def on_message(client, userdata, msg):
  print("Received message '" + str(msg.payload) + "' on topic '" + msg.topic + "' with QoS " + str(msg.qos))

#ログ
def on_log(client, userdata, level, buf):
    print("log: ", buf)

client = mqtt.Client()  
#コールバックを登録     
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.on_message = on_message
client.on_log = on_log

# IPアドレスは環境ごとに切り替える
client.connect("192.168.11.2", MQTT_PORT, KEEP_ALIVE)

#待ち受け
client.loop_forever()

```

##実行手順
1. **PC: Broker(mosquito)起動**

```
% mosquitto

1642506173: mosquitto version 2.0.14 starting
1642506173: Using default config.
1642506173: Starting in local only mode. Connections will only be possible from clients running on this machine.
1642506173: Create a configuration file which defines a listener to allow remote access.
1642506173: For more details see https://mosquitto.org/documentation/authentication-methods/
1642506173: Opening ipv4 listen socket on port 1883.
1642506173: Opening ipv6 listen socket on port 1883.
1642506173: mosquitto version 2.0.14 running

```


2.　**EV3: subscribe用のコード実行**

```
robot@ev3dev:~$ python3 mqtt_sub.py 

Traceback (most recent call last):
  File "mqtt_sub.py", line 36, in <module>
    client.connect("192.168.11.2", MQTT_PORT, KEEP_ALIVE)
  File "/usr/local/lib/python3.5/dist-packages/paho/mqtt/client.py", line 914, in connect
    return self.reconnect()
  File "/usr/local/lib/python3.5/dist-packages/paho/mqtt/client.py", line 1044, in reconnect
    sock = self._create_socket_connection()
  File "/usr/local/lib/python3.5/dist-packages/paho/mqtt/client.py", line 3685, in _create_socket_connection
    return socket.create_connection(addr, timeout=self._connect_timeout, source_address=source)
  File "/usr/lib/python3.5/socket.py", line 712, in create_connection
    raise err
  File "/usr/lib/python3.5/socket.py", line 703, in create_connection
    sock.connect(sa)
ConnectionRefusedError: [Errno 111] Connection refused
```

Brokerから接続拒否されている
先ほどのBroker(mosquitto)起動時の表示を確認すると
ローカルのみ接続可能となっているためためmosquittoの設定の変更が必要

```
1642506173: Starting in local only mode. Connections will only be possible from clients running on this machine.
```


以下の設定ファイルに外部からのアクセスを許可する内容を追記

```:/usr/local/opt/mosquitto/etc/mosquitto/mosquitto.conf
(省略)
listener 1883
allow_anonymous true
```

再度Broker(mosquitto)を起動

```
% mosquitto

1642506173: mosquitto version 2.0.14 starting
1642506173: Using default config.
1642506173: Starting in local only mode. Connections will only be possible from clients running on this machine.
1642506173: Create a configuration file which defines a listener to allow remote access.
1642506173: For more details see https://mosquitto.org/documentation/authentication-methods/
1642506173: Opening ipv4 listen socket on port 1883.
1642506173: Opening ipv6 listen socket on port 1883.
1642506173: mosquitto version 2.0.14 running

```

治ってない？？？

どうやらBroker起動時にオプション -c で設定ファイルを指定する必要がある

```
% mosquitto -c /usr/local/opt/mosquitto/etc/mosquitto/mosquitto.conf

1642509183: mosquitto version 2.0.14 starting
1642509183: Config loaded from /usr/local/opt/mosquitto/etc/mosquitto/mosquitto.conf.
1642509183: Opening ipv6 listen socket on port 1883.
1642509183: Opening ipv4 listen socket on port 1883.
1642509183: mosquitto version 2.0.14 running
```

再度EV3よりsubscribe用のコードを実行

```
robot@ev3dev:~$ python3 mqtt_sub.py 

connect broker:0
```

3.　**PC: publish用のコード実行**

```
% python3 mqtt_pub.py

publish Done
publish Done
publish Done
publish Done
publish Done
```

4.　**EV3: 確認**

```
robot@ev3dev:~$ python3 mqtt_sub.py 

connect broker:0
Received message 'b'0Hello!_EV3!!'' on topic 'topic/01/' with QoS 0
Received message 'b'1Hello!_EV3!!'' on topic 'topic/01/' with QoS 0
Received message 'b'2Hello!_EV3!!'' on topic 'topic/01/' with QoS 0
Received message 'b'3Hello!_EV3!!'' on topic 'topic/01/' with QoS 0
Received message 'b'4Hello!_EV3!!'' on topic 'topic/01/' with QoS 0

```

PC(publisher)より送信したデータをBrokerを介してEV3(subscriber)で受信することを確認した。


以上