Python
RaspberryPi
SORACOM
awsIoT
ISDN
SORACOMDay 22

機器の遠隔制御する通信をISDN回線からSORACOMに変える

概要

SORACOM Advent Calendar 2017 ネタも兼ねて、今やっている
機器の遠隔制御する通信を ISDN回線とISDNルータから Raspberry Pi と SORACOM を使った仕組みに変える
ための試作システムづくりを、自分自身の備忘録も兼ねてまとめてみました。

ISDN回線のサービスが終了

私の住む高知県内では未だにISDN回線が現役で活用されていますが、
2024年初頭にはISDN回線のサービスが終了しそうな雲行きです。

ISDN終了に潜む落とし穴、すべての企業は再点検を
http://itpro.nikkeibp.co.jp/atcl/column/14/346926/041100930/

とりあえずISDNルータでIP通信する環境は2024年以降も残る可能性もありますが、
そもそも田舎の電話線は発注してから工事が遅い、芯線の品質が悪いなどトラブルも多く、
この際 SORACOM の SIM を使った通信に変更するための検証システムを試作中しています。

試作ソフトの設計

現状の構成図

ISDN回線を利用して、シンプルなIP網を構築しています。
ISDN network

想定される要件

先ずは要件の確認から。

  • 現在は ISDN ルータで IP ネットワーク網を構築している
  • 操作PC は 1台のISDNルータを経由して、複数の 制御機器(ISDNルータ)を制御している
  • 操作PC から 制御機器 を SORACOM SIM 経由の通信で制御できるようにしたい
  • 移行期間は ISDN ルータと SORACOM SIM 経由の通信を併用したい
  • 操作PC と 制御機器 は固定IP
  • 操作PC から 制御機器 は TCP 上位レイヤーで独自プロトコルでピアツーピア通信を行う
  • 操作PC から 制御機器 は制御要求が送信され、制御機器 から 操作PC へは制御結果が返される
  • インターネットなどのパブリックなIP通信網は避ける、もしくは暗号化通信対応を実施する
  • 操作PC と 制御機器 のソフトは変更しない(操作PC、制御機器とも、設定変更でIPアドレスとポート番号は変更できる)

制約が多いですよね。(汗)

SORACOM の活用できるサービスを検討

上記の要件を満たすため SORACOM のデザインパターンを考えてみました。

1.SORACOM Air SIM と Gate で Public Gate を使う

https://dev.soracom.io/jp/docs/public_gate/
固定IPでデバイス間の通信ができるようで Raspberry Pi をルータにして操作PCと制御機器間を接続できそうです。
しかしながら、共有のIP通信回線で独自制御用プロトコルを平文で通信するのは要件から外れています。
SSLで暗号化して中継するトンネル通信システムの開発はコストが合いそうになく今回は断念。

2.SORACOM Air SIM と Gate で VPG Type-D で Door を使う

https://soracom.jp/services/door/
https://dev.soracom.io/jp/docs/gate_access_between_devices/
Public Gate が仮想専用線に変わるイメージですので、これは使えそうです。
ですが、VPG Type-D 利用料金が月約21万5千円~で、台数が増えるまでコスト的に対応できず今回は断念。

3.SORACOM Air SIM と Beam、AWS IoT の MQTTS を使う

https://soracom.jp/services/beam/
http://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/what-is-aws-iot.html
MQTTS の publish/subscribe の Topic を宛先に活用することで、独自制御用プロトコル通信のトンネルを実装できそうです。
Beam を使うと MQTTS の X.509 証明書のクライアント認証をよろしくやってくれるので、プログラム開発で楽できそうです。
また、機器が盗まれても証明書を持ってないので、SIMを無効化するだけで対応できるのが良いですね。

4.SORACOM Air SIM とAWS IoT の MQTTS を使う

MQTTS の publish/subscribe の Topic を宛先に活用することで、独自制御用プロトコル通信のトンネルを実装できそうです。
ですが、MQTTSの X.509 証明書のクライアント認証の対応せねばならず、プログラム開発が少し面倒かな。
また、機器が盗まれたら全ての機器の証明書を変える必要があり、想像するだけで大変。

検討の結果

以上を検討した結果、今回は
「3.SORACOM Air SIM と Beam、AWS IoT の MQTTS を使う」
「4.SORACOM Air SIM とAWS IoT の MQTTS を使う」
の2つを試験実装まで行って、比較することにします。

システムの試作で使う機器

今回はシステム試作に、以下の機器を用意しました。

操作PC側試作機器

  • 機器本体 Raspberry Pi 3(OS Rasbian)
  • Witty Pi 2
  • 3G USBドングル AK-020
  • OS Rasbian
  • SORACOM Sim

安価な USBドングル AK-020 と、Raspberry Pi の運用管理ができる Witty Pi 2 で試験します。

制御機器側試作機器

  • 機器本体 Raspberry Pi 3(OS Rasbian)
  • ラズベリーパイ用3G通信モジュール「3GPi」
  • SORACOM Sim

制御機器は無人運用のためちょっとコストをかけて 連続運用でも3G通信の安定稼働が期待できる 3GPi で試験します。

AWS IoT の MQTTS で機器制御通信を中継する設計

想定している MQTTS で機器制御通信を中継する設計イメージはこんな感じでしょうか。
MQTTS network

試作では操作PCと、制御機器の通信はプログラムで代替えします。
実機はメーカさんの倉庫中で寒そうなので、次のフェーズの試作で試験するつもりです。(汗)

プログラムは以下です。

  • 操作PC (RequestSend.py)
  • 操作PC側 Raspberry Pi (RequestProxyServer.py)
  • 制御機器側 Raspberry Pi (ResponseProxyServer.py)
  • 制御機器 (ResponseSend.py)

システムの試作

AWS IoT の設定

AWS IoT 設定の説明は省略します。
詳細を知りたい方は以下(ナレコムAWSレシピ)などを参照ください。

AWS IoTを始めよう -MQTTの設定(AWS IoT編)-(ナレコムAWSレシピ)
https://recipe.kc-cloud.jp/archives/9612

AWS IoT の MQTTS の接続先を確認します。
AWS IoT setting 1

MQTTS のデータ送信を他でも確認するため、ルールを追加してS3にデータを保管すると良いです。
AWS IoT setting 2

その他、秘密鍵、証明書、CA証明書 をダウンロードしておきます。

今回使う Raspberry Pi の開発環境を準備

Raspberry Pi の OS Rasbian に Python 2.7 をインストールします。
この辺りの情報はネットに沢山ありますので、説明は割愛します。

開発環境としては、Python 2.7 の他に、以下を用意します。

Mosquitto をインストールします
sudo apt-get install mosquitto-clients

paho-mqtt をインストールします。
sudo pip install paho-mqtt

参考情報は以下です。

20.17. SocketServer — ネットワークサーバ構築のためのフレームワーク
https://docs.python.jp/2.7/library/socketserver.html

Mosquitto と paho-mqtt をつかってPythonで MQTTをさわる
https://librabuch.jp/blog/2015/09/mosquiito_paho_python_mqtt/

paho-mqtt 1.3.1
https://pypi.python.org/pypi/paho-mqtt

MQTT with AWS IoT Platform using Python and Paho
https://iotbytes.wordpress.com/mqtt-with-aws-iot-using-python-and-paho/
https://github.com/pradeesi/AWS-IoT-with-Python-Paho

操作PC、制御機器の通信代替えプログラム

操作PCの代替えプログラム

以下のような、操作PC の通信を代替えするプログラム準備しました。
別途PCを準備するのも手間ですので、今回は 操作PC側の Raspberry Pi で試験します。
送信データ、受信データ、処理時間をコンソールで表示します。

RequestSend.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import socket
import time

host = "localhost"
port = 20000

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect((host, port))
senddata = "TEST_1234567890"

print "SendData:" + senddata
start = time.time()
client.send(senddata)

response = client.recv(1024)
elapsedTime = time.time() - start
client.close()

print "RecvData:" + response
print "Time:"+ str(elapsedTime) + " sec"

制御機器の代替えプログラム

以下のような、制御機器の通信を代替えするプログラムを用意しました。
制御機器はメーカさんの倉庫ですので、今回は 制御機器側の Raspberry Pi で試験します。
受信したデータの末尾に"_Response"を付けて、返信するだけのプログラムです。

ResponseSend.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import SocketServer

class Handler(SocketServer.StreamRequestHandler):

    def handle(self):
        while True:
            data = self.request.recv(1024).strip()
            if(len(data) > 4):
                print data
                # 受信したデータに"_Response"を付けて返信する
                self.request.send(data + "_Response")
        self.request.close()

# 20001番ポートで通信を待つ
server = SocketServer.ThreadingTCPServer(('', 20001), Handler)
print 'listening', server.socket.getsockname()
server.serve_forever()

「4.SORACOM Air SIM とAWS IoT の MQTTS を使う」の試験

順番は前後しますが、先ずは面倒そうな AWS IoT の MQTTS だけを利用する実装を試します。

操作PC側 Raspberry Pi の MQTTS 通信プログラム

以下のような MQTTS 通信プログラムを作成しました。

  1. SocketServer に受信があったら ProxyHandler() クラスの handle() メソッドを実行
  2. mqtt.client の初期設定(証明書の設定)
  3. MQTT subscribe のレスポンスを受信する準備(レスポンス用のTopicsは soracom/beam/response)
  4. MQTT publish でリクエストを送信(リクエスト用のTopicsは soracom/beam)
  5. MQTT subscribe でレスポンスを受信したら、操作PCにそのレスポンスを送信
  6. 1秒間隔で10回確認して、subscribe でレスポンスを受信しない場合は"No Response Data."を操作PCに送信
RequestProxyServer.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import paho.mqtt.client as mqtt
import SocketServer
import ssl
import time

Response = ""

# Custom MQTT connect callback
def customConnect(client, userdata, flags, responsCode):
    client.subscribe("soracom/beam/response")

# Custom MQTT message callback
def customCallback(client, userdata, message):
    global Response
    Response = str(message.payload)
    client.disconnect()

class ProxyHandler(SocketServer.BaseRequestHandler):

    def handle(self):

        recvData = self.request.recv(1024)

        # AWS IoT 接続設定
        mqttClient = mqtt.Client(protocol=mqtt.MQTTv311)
        mqttClient.on_connect = customConnect
        mqttClient.on_message = customCallback

        # 
        host            = "<AWS IoT で確認した接続先>.amazonaws.com"
        dir             = "/home/pi/NoBeam/key/"
        rootCAPath      = dir + "rootCA.pem" # CA証明書
        certificatePath = dir + "<AWS IoT でダウンロードしたファイル名>.pem.crt" # 証明書
        privateKeyPath  = dir + "<AWS IoT でダウンロードしたファイル名>.pem.key" # 秘密鍵
        mqttClient.tls_set(rootCAPath, certfile=certificatePath, keyfile=privateKeyPath, cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLSv1_2, ciphers=None)

        mqttClient.connect(host, port=8883, keepalive=10)
        mqttClient.loop_start()

        # MQTT publish
        mqttClient.publish("soracom/beam", recvData)

        # MQTT subscriber respons wait
        global Response
        tryCount = 0
        while Response == "":
            tryCount += 1
            time.sleep(1)
            if(Response == "" and tryCount > 10):
                Response = "No Response Data."
                break

        # KL respons send
        self.request.send(Response)
        self.request.close()

# 20000番ポートで通信を待つ
server = SocketServer.TCPServer(('', 20000), ProxyHandler)
print 'listening', server.socket.getsockname()
server.serve_forever()

制御機器側 Raspberry Pi の MQTTS 通信プログラム

以下のような MQTTS 通信プログラムを用意しました。

  1. mqtt.client の初期設定(証明書の設定)
  2. MQTT subscribe で操作PCのリクエストを受信する準備(リクエスト用のTopics soracom/beam)
  3. MQTT subscribe でリクエストを受信したら、制御機器に受信したリクエストを送信
  4. 制御機器からレスポンスを受信したら、MQTT publish でレスポンスを送信(レスポンス用のTopics soracom/beam/response)
ResponseProxyServer.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import paho.mqtt.client as mqtt
import socket
import ssl
import time

# Custom MQTT connect callback
def customConnect(client, userdata, flags, responsCode):
    client.subscribe("soracom/beam")

# Custom MQTT message callback
def customCallback(client, userdata, message):
    recvData = str(message.payload)

    # Machine にデータを送信し、応答を受信する
    host = "localhost"
    port = 20001
    socketClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    socketClient.connect((host, port)) 
    socketClient.send(recvData)

    response = socketClient.recv(1024)
    socketClient.close()

    # レスポンス用のデータを publish
    client.publish("soracom/beam/response", response)
    print "soracom/beam/response:"+response

# AWS IoT 接続設定
mqttClient = mqtt.Client(protocol=mqtt.MQTTv311)
mqttClient.on_connect = customConnect
mqttClient.on_message = customCallback

host            = "<AWS IoT で確認した接続先>.amazonaws.com"
dir             = "/home/pi/NoBeam/key/"
rootCAPath      = dir + "rootCA.pem" # CA証明書
certificatePath = dir + "<AWS IoT でダウンロードしたファイル名>.pem.crt" # 証明書
privateKeyPath  = dir + "<AWS IoT でダウンロードしたファイル名>.pem.key" # 秘密鍵
mqttClient.tls_set(rootCAPath, certfile=certificatePath, keyfile=privateKeyPath, cert_reqs=ssl.CERT_REQUIRED, tls_version=ssl.PROTOCOL_TLSv1_2, ciphers=None)

# Connect and subscribe to AWS IoT
mqttClient.connect(host, port=8883, keepalive=60)

# subscribe を待つ
mqttClient.loop_forever()

試験結果

試験結果は以下です。

操作PC側
No Beam Test 1

制御機器側
No Beam Test 2

期待どおり、RequestSend.py にレスポンスが返っています。
SIM の速度は一番遅い minimum の設定ですが、データ量が少ないのでレスポンス時間も問題ないです。

AWS の S3 でも通信を確認できます。
No Beam Test 2

先ずは、この方法で問題ないことが確認できました。

「3.SORACOM Beam と AWS IoT の MQTTS を使う」の試験

続いて、SORACOM Beam を使って AWS IoT MQTTS を利用する実装を試します。

SORACOM Beam について

以下を参照ください。
https://soracom.jp/services/beam/

SORACOM Beam の設定

今回使う SORACOM Sim を同じグループ(今回の例では beam )に所属させます。
グループの設定を開いて、Beam の設定を追加します。

「MQTTエントリポイント」を追加します。
beam setting 1

AWS IoT の MQTTS の接続先を設定します。
beam setting 2

認証情報を追加します。
beam setting 3

以下のように、秘密鍵、証明書、CA証明書 を設定します。(ファイルの中身をコピペ)
beam setting 4

mosquitto-clients を使って、コマンドラインで確認してみましょう。
mosquitto-clients をインストールします。

sudo apt-get install mosquitto-clients

以下のように、SORACOM beam の設定で AWS IoT MQTT へ publish できることが確認できました。
mosquitto-clients

mosquitto-clients などの詳細は以下を参照ください。

SORACOM Beam を使用して AWS IoT と接続する(コンソール版)
https://dev.soracom.io/jp/docs/aws_iot_guide_console/

操作PC側 Raspberry Pi の MQTTS 通信プログラム

今回は、SORACOM beam を使う MQTT 通信プログラムを用意しました。
SORACOM beam が AWS IoT の MQTTS X.509 証明書のクライアント認証をやってくれるので、
MQTT の実装で済んでしまうのがいいですね。

  1. SocketServer に受信があったら ProxyHandler() クラスの handle() メソッドを実行
  2. mqtt.client の初期設定
  3. MQTT subscribe のレスポンスを受信する準備(レスポンス用のTopicsは soracom/beam/response)
  4. MQTT publish でリクエストを送信(リクエスト用のTopicsは soracom/beam)
  5. MQTT subscribe でレスポンスを受信したら、操作PCにそのレスポンスを送信
  6. 1秒間隔で10回確認して、subscribe でレスポンスを受信しない場合は"No Response Data."を操作PCに送信

今回は MQTTS の X.509 証明書のクライアント認証の必要もないので MQTT の処理に paho.mqtt.client を使います。
処理自体は beam を使わない場合とほぼ同じです。

RequestProxyServer.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import paho.mqtt.client as mqtt
import SocketServer
import time

Response = ""

# Custom MQTT connect callback
def customConnect(client, userdata, flags, responsCode):
    client.subscribe("soracom/beam/response")

# Custom MQTT message callback
def customCallback(client, userdata, message):
    global Response
    Response = str(message.payload)
    client.disconnect()

class ProxyHandler(SocketServer.BaseRequestHandler):

    def handle(self):

        recvData = self.request.recv(1024)

        # AWS IoT 接続設定
        host = "beam.soracom.io"
        mqttClient = mqtt.Client(protocol=mqtt.MQTTv311)
        mqttClient.on_connect = customConnect
        mqttClient.on_message = customCallback
        mqttClient.connect(host, port=1883, keepalive=10)
        mqttClient.loop_start()

        # MQTT publish
        mqttClient.publish("soracom/beam", recvData)

        # MQTT subscriber respons wait
        global Response
        tryCount = 0
        while Response == "":
            tryCount += 1
            time.sleep(1)
            if(Response == "" and tryCount > 10):
                Response = "No Response Data."
                break

        # KL respons send
        self.request.send(Response)
        self.request.close()

# 20000番ポートで通信を待つ
server = SocketServer.TCPServer(('', 20000), ProxyHandler)
print 'listening', server.socket.getsockname()
server.serve_forever()

制御機器側 Raspberry Pi の MQTTS 通信プログラム

こちらも 操作PC側 同様、SORACOM beam を使う MQTT 通信プログラムを用意しました。
MQTT の実装で済んでしまうのがいいですね。

  1. mqtt.client の初期設定
  2. MQTT subscribe で操作PCのリクエストを受信する準備(リクエスト用のTopics soracom/beam)
  3. MQTT subscribe でリクエストを受信したら、制御機器に受信したリクエストを送信
  4. 制御機器からレスポンスを受信したら、MQTT publish でレスポンスを送信(レスポンス用のTopics soracom/beam/response)
ResponseProxyServer.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import paho.mqtt.client as mqtt
import socket
import time

# Custom MQTT connect callback
def customConnect(client, userdata, flags, responsCode):
    client.subscribe("soracom/beam")

# Custom MQTT message callback
def customCallback(client, userdata, message):
    recvData = str(message.payload)

    # Machine にデータを送信し、応答を受信する
    host = "localhost"
    port = 20001
    socketClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    socketClient.connect((host, port)) 
    socketClient.send(recvData)

    response = socketClient.recv(1024)
    socketClient.close()

    # レスポンス用のデータを publish
    client.publish("soracom/beam/response", recvData)
    print "soracom/beam/response:"+recvData

# AWS IoT 接続設定
host = "beam.soracom.io"
mqttClient = mqtt.Client(protocol=mqtt.MQTTv311)
mqttClient.on_connect = customConnect
mqttClient.on_message = customCallback

# Connect and subscribe to AWS IoT
mqttClient.connect(host, port=1883, keepalive=60)

# subscribe を待つ
mqttClient.loop_forever()

試験結果

試験した結果は以下です。

操作PC側
Beam Test 1

制御機器側
Beam Test 2

こちらの方法でも、期待どおり RequestSend.py にレスポンスが返っています。
SIM の速度も一番遅い minimum のままですが、レスポンス時間も問題ないです。

AWS S3 でも通信結果を確認できました。
Beam Test 3

SORACOM Beam を使うと Raspberry Pi 内に証明書を保管する必要がなくなるのは良いですね。
多少ですが、プログラムコード量も少なくなりスッキリ!

まとめ

機器の遠隔制御通信を ISDN回線とISDNルータから Raspberry Pi と SORACOM を使ったシステムに変えるため、
SORACOM のデザインパターンを検討し、以下の2点に絞りました。

  • SORACOM Air SIM と Beam、AWS IoT の MQTTS を使う
  • SORACOM Air SIM とAWS IoT の MQTTS を使う

試験実装まで行った結果
「SORACOM Air SIM と Beam、AWS IoT の MQTTS を使う」
が良いと判断しました。
予測はできたあたりまえの結果ではありますが。(汗)

理由は以下です。

  • 証明書 を遠隔に設置する 制御機器 に置く必要がない
  • 多少ではあるがプログラムのコード量を少なくできる
  • 利用コストを安く抑えることができる

今回は簡単な試験まででしたが、これからは実機の試験用の実装に移ります。
また SORACOM の API を活用して、運用状況の通知、リカバリー用の開発も行う予定です。

余談(AWS IoT SDK を使った開発)

今回は beam の有無をわかりやすく比較するため python の paho-mqtt を使いました。
ですが、AWS も Python 用 SDK も無論使えます。
(個人的には paho-mqtt がお勧めですが。)

AWS IoT Device SDK for Python
https://github.com/aws/aws-iot-device-sdk-python

AWS IoT Device SDK for Python をインストール。

sudo pip install AWSIoTPythonSDK

操作PC側 Raspberry Pi の MQTTS 通信プログラム例

RequestProxyServer.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
import logging
import SocketServer
import sys
import time

Response = ""

# Custom MQTT message callback
def customCallback(client, userdata, message):
    global Response
    Response = str(message.payload)

class ProxyHandler(SocketServer.BaseRequestHandler):

    def handle(self):

        # AWS IoT 接続設定
        useWebsocket    = False
        host            = "<AWS IoT で確認した接続先>.iot.ap-northeast-1.amazonaws.com"
        dir             = "/home/pi/NoBeam/key/"
        rootCAPath      = dir + "rootCA.pem" # CA証明書
        certificatePath = dir + "<AWS IoT でダウンロードしたファイル名>.pem.crt" # 証明書
        privateKeyPath  = dir + "<AWS IoT でダウンロードしたファイル名>.pem.key" # 秘密鍵
        topic           = "soracom/beam"

        # Configure logging
        logger = logging.getLogger("AWSIoTPythonSDK.core")  # Python 2
        streamHandler = logging.StreamHandler()
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        streamHandler.setFormatter(formatter)
        logger.addHandler(streamHandler)

        # Init AWSIoTMQTTClient
        myMQTTClient = AWSIoTMQTTClient("soracom_beam_Bord")
        myMQTTClient.configureEndpoint(host, 8883)
        myMQTTClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath)

        # AWSIoTMQTTClient connection configuration
        myMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20)
        myMQTTClient.configureOfflinePublishQueueing(-1)    # Infinite offline Publish queueing
        myMQTTClient.configureConnectDisconnectTimeout(10)  # 10 sec
        myMQTTClient.configureMQTTOperationTimeout(5)       # 5 sec

        # Connect and subscribe to AWS IoT
        myMQTTClient.connect()
        time.sleep(2)

        while True:
            recvData = self.request.recv(1024)

            # MQTT subscribe
            responseTopic = topic + "/response" # レスポンス用のトピック
            myMQTTClient.subscribe(responseTopic, 1, customCallback)
            time.sleep(1)

            # MQTT publish
            myMQTTClient.publish(topic, recvData, 1)

            # MQTT subscriber respons wait (Max 10 seconds)
            global Response
            Response = ""
            tryCount = 0
            while Response == "":
                tryCount += 1
                time.sleep(1)
                if(Response == "" and tryCount > 10):
                    Response = "No Response Data."
                    break

            # KL respons send
            myMQTTClient.unsubscribe(responseTopic)
            self.request.send(Response)
            break

        myMQTTClient.disconnect()
        self.request.close()

# 20000番ポートで通信を待つ
server = SocketServer.TCPServer(('', 20000), ProxyHandler)
print 'listening', server.socket.getsockname()
server.serve_forever()

制御機器側 Raspberry Pi の MQTTS 通信プログラム例

MQTT publish で問題なく送信できるのに、何故かエラーが発生します。
とりあえずエラーをキャッチして、コンソールに表示しいます。

2017-12-18 03:27:43,163 - AWSIoTPythonSDK.core.protocol.mqtt_core - ERROR - Publish timed out

publish ができているのに timed out エラーになるのが不思議。

ResponseProxyServer.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
import logging
import socket
import sys
import time

# Custom MQTT message callback
def customCallback(client, userdata, message):
    recvData = str(message.payload)

    # Machine にデータを送信し、応答を受信する
    host = "localhost"
    port = 20001
    socketClient = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    socketClient.connect((host, port)) 
    socketClient.send(recvData)

    response = socketClient.recv(1024)
    socketClient.close()

    # レスポンス用のデータを publish
    print "soracom/beam/response:"+response
    try:
        myMQTTClient.publish("soracom/beam/response", response, 1)
    except Exception, e:
        print e

# AWS IoT 接続設定
useWebsocket    = False
host            = "<AWS IoT で確認した接続先>.amazonaws.com"
dir             = "/home/pi/NoBeam/key/"
rootCAPath      = dir + "rootCA.pem" # CA証明書
certificatePath = dir + "<AWS IoT でダウンロードしたファイル名>.pem.crt" # 証明書
privateKeyPath  = dir + "<AWS IoT でダウンロードしたファイル名>.pem.key" # 秘密鍵

# Configure logging
logger = logging.getLogger("AWSIoTPythonSDK.core")  # Python 2
streamHandler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler)

# Init AWSIoTMQTTClient
myMQTTClient = AWSIoTMQTTClient("soracom_beam_Machine")
myMQTTClient.configureEndpoint(host, 8883)
myMQTTClient.configureCredentials(rootCAPath, privateKeyPath, certificatePath)

# AWSIoTMQTTClient connection configuration
myMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20)
myMQTTClient.configureOfflinePublishQueueing(-1)    # Infinite offline Publish queueing
myMQTTClient.configureConnectDisconnectTimeout(10)  # 10 sec
myMQTTClient.configureMQTTOperationTimeout(5)       # 5 sec

# Connect and subscribe to AWS IoT
myMQTTClient.connect()
myMQTTClient.subscribe("soracom/beam", 1, customCallback)
time.sleep(2)

# subscribe を待つ
while True:
    time.sleep(0)