ラズパイ親機から子機経由でNゲージを遠隔制御する（２）shellscriptでラズパイ子機を構築


## この記事で説明すること 

[ラズパイ親機から子機経由でNゲージを遠隔制御する（１）shellscriptでラズパイAP親機を構築](https://qiita.com/nsaito9628/items/e5ba356c97d545a2601d)からの続きです。

本稿では、想定する下図のシステム中で太い破線で囲まれたラズパイ子機の構築について説明します。

mosquittoまたはPythonでpaho-mqttのいずれかを使いpubsubできるBroker機能を持たせます。

**【2023.03.13追記】** 子機構築後、親機と相互にPubSub確認をします。

また、[前回](https://qiita.com/nsaito9628/items/e5ba356c97d545a2601d)構築したAP親機を介したAWS IoTへの疎通を確認するために、子機で測定した温湿度をPublishしてAWS側のMQTT テストクライアントでsubscribeさせます。
<img width= 700 src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565396/c865d132-0648-39db-3c5f-564e1a38c74a.png>

## 環境
 
**Raspberry Pi：** Raspberry Pi 4 Model B Rev 1.2
**OS：** Raspberry Pi OS Lite 32-bit (Raspbian GNU/Linux 11 (bullseye))


## 事前準備
#### 起動用microSDカード
[こちらの記事](https://qiita.com/nsaito9628/items/528ab7ccd0a62515463b#%E8%B5%B7%E5%8B%95%E7%94%A8microsd%E3%82%AB%E3%83%BC%E3%83%89)等を参考に起動用環境を作成します
Lite版OSが推奨です。

構築する子機のhostnameは[前回](https://qiita.com/nsaito9628/items/e5ba356c97d545a2601d#%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E4%BD%9C%E6%88%90)親機のhostsに登録した中で一番目のsubpi1にします。


## 構築内容

図中の色付き部分を構築します。

<img width= 700 src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565396/30d8e344-fd9c-c4ca-05f1-41d7c8e04e37.png>

**paho：** pythonでmqtt通信したいときに使うパッケージ 
**mosquitto：** 汎用のMQTT Broker　**【2023.03.13修正】**~~(今回はインストールのみで設定はしない)~~
**iptables-persistent：** Linux FW(nftables)設定に利用するパッケージ
**postfix：** cronの実行に使うメーラーパッケージ
**Adafruit_Python_DHT：** DHT11等から測定値を読み取るためのドライバー

## 導入手順

### 基本的にshellscriptの実行により構築を進めます。


#### パラメーターファイルの作成
パラメーターを~/nw_proc.confと~/iot_prov.confの2つのconfファイルに予め設定しておきます。confファイルのコメント文は削除して使ったほうが無難です。
```
/home
┣ /user
┃ ┣ nw_prov.conf
┃ ┣ iot_prov.conf
┃ ┣     ・ 
┃ ┣     ・ 
```
[前回設定したhosts](https://qiita.com/nsaito9628/items/e5ba356c97d545a2601d#%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%83%BC%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E4%BD%9C%E6%88%90)のsubpi1に紐づいた値を使います。
```sh:/home/user/nw_prov.conf
#AP親機のstatic ip
APIP_WLAN1=10.1.1.1
#自分のstatic ip
MYIP_WLAN0=10.1.1.11
#AP親機のSSID
WLAN0_SSID=MySSID1
#AP親機のpassphrase
PSK=MyPassWD
#AP親機に/24で許可されるアドレス範囲
CIDR_RANGE=10.1.1.0
```

```sh:/home/user/iot_prov.conf
#AP親機のhostname、AWS IoTに登録するモノ(ラズパイ親機)の名前と同じ
BROKER_NAME=test1
#親機のmosquittoに設定されてるusername
MOS_USR=MyMosquittoUsername
#親機のmosquitto userに設定されてるpassphrase
MOS_PW=MymosquittoPW
```


#### パッケージ導入
aptとpipでインストールします

```sh:environment.sh
#!/bin/bash

sudo apt update  
sudo apt -y upgrade  

sudo apt install -y python3-dev python3-pip git expect
sudo apt install -y build-essential libssl-dev libffi-dev python3-dev
python3 -m pip install --upgrade pip

echo iptables-persistent iptables-persistent/autosave_v4 boolean true | sudo debconf-set-selections
echo iptables-persistent iptables-persistent/autosave_v6 boolean false | sudo debconf-set-selections
sudo apt install -y iptables iptables-persistent

sudo apt install -y mosquitto mosquitto-clients
sudo pip3 install paho-mqtt --upgrade

pi_install_postfix()
{
  cat <<EOF | sudo debconf-set-selections
postfix postfix/main_mailer_type select No configuration
EOF
  sudo apt install -y postfix

  cat <<EOF | sudo tee /etc/postfix/main.cf
myhostname = ${1}
mydomain = ${2}
myorigin = \$myhostname.\$mydomain
mydestination = localhost, localhost.\$mydomain, \$myhostname, \$mydomain, \$myorigin
compatibility_level = 2
command_directory = /usr/sbin
daemon_directory = /usr/lib/postfix/sbin
data_directory = /var/lib/postfix
mail_owner = postfix
inet_interfaces = all
local_recipient_maps = unix:passwd.byname \$alias_maps
unknown_local_recipient_reject_code = 550
mynetworks_style = subnet
mynetworks = 127.0.0.0/8
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
smtpd_banner = \$myhostname ESMTP \$mail_name (Debian/GNU)
debugger_command =
         PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
         ddd \$daemon_directory/\$process_name \$process_id & sleep 5
sendmail_path = /usr/sbin/sendmail
newaliases_path = /usr/bin/newaliases
mailq_path = /usr/bin/mailq
setgid_group = postdrop
inet_protocols = ipv4
EOF
  sudo newaliases
  sudo systemctl restart postfix
  sudo apt install -y mutt
}

main()
{
  pi_install_postfix $(uname -n) localdomain
}

main

sudo reboot
```

#### dhcpcd.confの設定

```sh:set_dhcp.conf
#!/bin/bash
sudo cp /etc/dhcpcd.conf /etc/dhcpcd.conf.bak 

echo interface wlan0 | sudo tee -a /etc/dhcpcd.conf
echo static ip_address=$(cat ./nw_prov.conf | grep MYIP_WLAN0 | awk -F'=' '{print echo $2}' | sed -e "s/[\r\n]\+//g")/24 | sudo tee -a /etc/dhcpcd.conf
echo static routers=$(cat ./nw_prov.conf | grep APIP_WLAN1 | awk -F'=' '{print echo $2}' | sed -e "s/[\r\n]\+//g") | sudo tee -a /etc/dhcpcd.conf
echo "static domain_name_servers=$(cat ./nw_prov.conf | grep APIP_WLAN1 | awk -F'=' '{print echo $2}' | sed -e "s/[\r\n]\+//g") 8.8.8.8" | sudo tee -a /etc/dhcpcd.conf
echo noipv6 | sudo tee -a /etc/dhcpcd.conf
```

#### wpa_supplicant.confの設定
```sh:set_wpa0.conf
#!/bin/bash

sudo cp /etc/wpa_supplicant/wpa_supplicant.conf /etc/wpa_supplicant/wpa_supplicant.conf.bak
sudo rm /etc/wpa_supplicant/wpa_supplicant.conf

SSID=$(cat ./nw_prov.conf | grep WLAN0_SSID | awk -F'=' '{print echo $2}' | sed -e "s/[\r\n]\+//g")
PSK=$(cat ./nw_prov.conf | grep PSK | awk -F'=' '{print echo $2}' | sed -e "s/[\r\n]\+//g")

echo 'ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev' | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf
echo 'update_config=1' | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf
echo 'country=JP' | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf
echo "" | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf
echo 'network={' | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf
echo "        ssid=\"${SSID}\"" | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf
echo "        psk=\"${PSK}\"" | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf
echo '}' | sudo tee -a /etc/wpa_supplicant/wpa_supplicant.conf
```

#### timesyncd.conf の設定 

```sh:set_timesync.sh
#!/bin/bash
sudo cp  /etc/systemd/timesyncd.conf   /etc/systemd/timesyncd.conf.bak

echo "NTP=$(cat ./nw_prov.conf | grep APIP_WLAN1 | awk -F'=' '{print echo $2}' | sed -e "s/[\r\n]\+//g")" | sudo tee -a /etc/systemd/timesyncd.conf 

sudo systemctl restart systemd-timesyncd
```


#### **【2023.03.13追記】** mosquitto.conf の設定 

親機と同様にmosquittoにusernameとpassphraseを設定しておきます。

ここでは親機と同じusernameとpassphraseを使用して設定します。実運用ではポリシーに従って変更します。

```sh:mos_prov.sh
#!/bin/bash
sudo rm /etc/mosquitto/mosquitto.conf
echo \# Place your local configuration in /etc/mosquitto/conf.d/ | sudo tee -a /etc/mosquitto/mosquitto.conf
echo \# | sudo tee -a /etc/mosquitto/mosquitto.conf
echo \# A full description of the configuration file is at | sudo tee -a /etc/mosquitto/mosquitto.conf
echo \# /usr/share/doc/mosquitto/examples/mosquitto.conf.example | sudo tee -a /etc/mosquitto/mosquitto.conf
echo "" | sudo tee -a /etc/mosquitto/mosquitto.conf
echo per_listener_settings true | sudo tee -a /etc/mosquitto/mosquitto.conf
echo pid_file /run/mosquitto/mosquitto.pid | sudo tee -a /etc/mosquitto/mosquitto.conf
echo "" | sudo tee -a /etc/mosquitto/mosquitto.conf | sudo tee -a /etc/mosquitto/mosquitto.conf
echo persistence true | sudo tee -a /etc/mosquitto/mosquitto.conf
echo persistence_location /var/lib/mosquitto/ | sudo tee -a /etc/mosquitto/mosquitto.conf
echo "" | sudo tee -a /etc/mosquitto/mosquitto.conf | sudo tee -a /etc/mosquitto/mosquitto.conf
echo log_dest file /var/log/mosquitto/mosquitto.log | sudo tee -a /etc/mosquitto/mosquitto.conf
echo "" | sudo tee -a /etc/mosquitto/mosquitto.conf | sudo tee -a /etc/mosquitto/mosquitto.conf
echo include_dir /etc/mosquitto/conf.d | sudo tee -a /etc/mosquitto/mosquitto.conf
echo "" | sudo tee -a /etc/mosquitto/mosquitto.conf | sudo tee -a /etc/mosquitto/mosquitto.conf
echo listener 1883 | sudo tee -a /etc/mosquitto/mosquitto.conf
echo allow_anonymous false | sudo tee -a /etc/mosquitto/mosquitto.conf
echo password_file /etc/mosquitto/passwd | sudo tee -a /etc/mosquitto/mosquitto.conf

sudo systemctl enable mosquitto
sudo systemctl start mosquitto
```

```sh:passwd.sh
#!/bin/bash
timeout=10
password=$(cat ~/iot_prov.conf | grep MOS_PW | awk -F'=' '{print $2}' | sed -e "s/[\r\n]\+//g")
username=$(cat ~/iot_prov.conf | grep MOS_USR | awk -F'=' '{print $2}' | sed -e "s/[\r\n]\+//g")
command="sudo mosquitto_passwd -c /etc/mosquitto/passwd $username"
expect -c "
    set timeout ${timeout}
    spawn ${command}
    expect \"Password:\"
    send \"${password}\n\"
    expect \"Password:\"
    send \"${password}\n\"
    expect \"$\"
    exit 0
"
```

#### 再起動
ここで一度再起動します。
```sh
sudo reboot
```

#### Adafruit_Python_DHTのインストール
DHT11のドライバーをインストールします。
```sh
#!/bin/bash

sudo git clone https://github.com/adafruit/Adafruit_Python_DHT.git
cd Adafruit_Python_DHT
sudo python setup.py install
cd
```

#### 温湿度測定DEMOプロジェクトの配置
4つのPythonファイルをuserフォルダーに配置します
```
/home
┣ /user
┃ ┣ main.py
┃ ┣ parameters.py
┃ ┣ sensing.py
┃ ┣ awsMQTTconnect.py
┃ ┣     ・ 
┃ ┣     ・ 
```

```Python:main.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import time
import sys
from awsMQTTconnect import Com, Pub
from sensing import Sensor
import parameters as para

sensor = Sensor()
com = Com()
pub = Pub()

def loop():
    sub_t_count = 0

    while True:
        temp, humi = sensor.temp_humi()
        if para.NW_TYPE == "public":
            sub_t_count = pub.publish_pub(sub_t_count, temp, humi)
        elif para.NW_TYPE == "private":
            sub_t_count = pub.publish_pri(sub_t_count, temp, humi)

        time.sleep(0.1)


if __name__ == '__main__':
    try:
        time.sleep(60)

        #wifi connection confirmation and MQTT connection
        com.get_ssid()
        if para.NW_TYPE == "public":
            com.aws_connect()
        elif para.NW_TYPE == "private":
            com.mosquitto_connect()

        #Main loop execution
        loop()

    except KeyboardInterrupt:
        sys.exit()
```

```Python:parameters.py
import os
import datetime
import paho.mqtt.client as mqtt

try:
    ENDPOINT = os.environ['HOST_ENDPOINT']  # AWS IoT Endpoint
    CACERT = os.environ['CACERT']  # root ca
    CLIENTCERT = os.environ['CLIENTCERT']  # certificate
    CLIENTKEY = os.environ['CLIENTKEY']  # private key
    HOSTNAME = os.environ['HOSTNAME']  # topic
    TOPIC = HOSTNAME + "/TempHumi"
    ID = HOSTNAME
    CLIENT = mqtt.Client(client_id=ID, protocol=mqtt.MQTTv311)
    PORT = 8883  # mqtts port
    NW_TYPE = "public"
except KeyError:
    BROKER_HOST = os.environ['BROKER_HOST']
    BROKER_IP = os.environ['BROKER_IP']
    HOSTNAME = os.uname()[1]
    TOPIC = BROKER_HOST + "_mos/" + HOSTNAME + "/TempHumi"
    USERNAME = os.environ['BROKER_USR']
    PASSWD = os.environ['BROKER_PW']
    CLIENT = mqtt.Client(protocol=mqtt.MQTTv311)
    PORT = 1883
    NW_TYPE = "private"

SENSOR_TYPE = 11 #11:DHT11 22:DHT22
DHT_PIN = 21
```

```Python:sensing.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import Adafruit_DHT
import parameters as para

class Sensor:
    def __init__(self):
        self.sensor_type = para.SENSOR_TYPE #Motion sensor signal port : GPIO 21
        self.dht_pin = para.DHT_PIN
       

    #Output sensor HI / LO at 1/0
    def temp_humi(self): 
        humidity, temperature = Adafruit_DHT.read_retry(self.sensor_type, self.dht_pin)
        temp = temperature #f"{temperature:.1f}" #気温（小数点第一位まで）
        humi = humidity #f"{humidity:.1f}" #湿度（小数点第一位まで）

        return temp, humi
```

```Python:awsMQTTconnect.py
#!/usr/bin/python
# -*- coding: utf-8 -*-
import json
import ssl
import time
from datetime import datetime
import subprocess
import parameters as para

class Com:
    def __init__(self):
        self.nw_type = para.NW_TYPE
        if self.nw_type == "public":
            self.cacert = para.CACERT
            self.clientCert = para.CLIENTCERT
            self.clientKey = para.CLIENTKEY
            self.host = para.ENDPOINT
            self.client = para.CLIENT
            self.port = para.PORT
        elif self.nw_type == "private":
            self.broker_host = para.BROKER_HOST
            self.broker_ip = para.BROKER_IP
            self.topic = para.TOPIC
            self.username = para.USERNAME
            self.passwd = para.PASSWD
            self.client = para.CLIENT
            self.port = para.PORT


    #Callback function when mqtt connection is successful
    def on_connect(self, client, userdata, flags, respons_code):
        #If the connection cannot be established, reboot after 90 seconds of waiting time for terminal access
        if respons_code != 0:
            print("respons_code:", respons_code, " flags:", flags)
            time.sleep(90)
            subprocess.call(["sudo","reboot"])
        print('Connected')


    #Function to determine the establishment of wifi connection
    def get_ssid(self):
        cmd = 'iwconfig wlan0|grep ESSID'
        r = subprocess.run(cmd, shell = True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)\
            .stdout.decode().rstrip()
        idx = r.find('ESSID:')
        #If the connection cannot be established, reboot after 90 seconds of waiting time for terminal access
        if r[idx + 7:-1] == "ff/an":
            print("ESSID:", r[idx + 7:-1])
            time.sleep(90)
            subprocess.call(["sudo","reboot"])


    #Function that launches an MQTT client and creates an object instance
    def aws_connect(self):
        try:
            # certifications
            self.client.tls_set(
                self.cacert,
                certfile=self.clientCert,
                keyfile=self.clientKey,
                tls_version=ssl.PROTOCOL_TLSv1_2)
            self.client.tls_insecure_set(True)

            # callback
            self.client.on_connect = self.on_connect

            # port, keepalive
            self.client.connect(self.host, self.port, keepalive=60)

            self.client.loop_start()

        except KeyboardInterrupt:
            time.sleep(90)
            subprocess.call(["sudo","reboot"])


    def mosquitto_connect(self):
        try:
            # certifications
            self.client.username_pw_set(self.username, self.passwd)

        except KeyboardInterrupt:
            time.sleep(90)
            subprocess.call(["sudo","reboot"])


class Pub:
    def __init__(self):
        self.nw_type = para.NW_TYPE
        if self.nw_type == "public":
            self.client = para.CLIENT
            self.topic = para.TOPIC
        elif self.nw_type == "private":
            self.broker_ip = para.BROKER_IP
            self.topic = para.TOPIC
            self.client = para.CLIENT
            self.port = para.PORT

    #Callback function when mqtt connection is successful
    def on_connect(self, client, userdata, flags, respons_code):
        #If the connection cannot be established, reboot after 90 seconds of waiting time for terminal access
        if respons_code != 0:
            print("respons_code:", respons_code, " flags:", flags)
            time.sleep(90)
            subprocess.call(["sudo","reboot"])
        print('Connected')

    #Function that dispenses motion sensor data to the cloud at 0 seconds per minute
    def publish_pub(self, sub_t_count, temp, humi):

        data = {} #KeyValue to publish
        t = datetime.now()
        sub_t = str(t.minute/2)

        if sub_t[-1] != "0":
            sub_t_count = 0

        if (sub_t[-1] == "0") and sub_t_count == 0:

            # IoTcoreへpublish
            data['Timestamp'] = int(time.time())
            data['temp'] = temp
            data['humi'] = humi
            self.client.publish(self.topic, json.dumps(data, default=self.json_serial), qos=1)
            print(data)

            sub_t_count = 1
            return sub_t_count

        if (t.minute == 0 or sub_t[-1] == 0) and sub_t_count == 1:
            pass

        return sub_t_count


    def publish_pri(self, sub_t_count, temp, humi):

        data = {} #KeyValue to publish
        t = datetime.now()
        sub_t = str(t.minute/2)

        if sub_t[-1] != "0":
            sub_t_count = 0

        if (sub_t[-1] == "0") and sub_t_count == 0:
            self.client.on_connect = self.on_connect
            print((self.broker_ip, self.port))
            self.client.connect(self.broker_ip, port=self.port, keepalive=60)
            # IoTcoreへpublish
            data['Timestamp'] = int(time.time())
            data['temp'] = temp
            data['humi'] = humi
            self.client.publish(self.topic, json.dumps(data, default=self.json_serial), qos=1)
            print(data)
            self.client.disconnect()

            sub_t_count = 1
            return sub_t_count

        if (t.minute == 0 or sub_t[-1] == 0) and sub_t_count == 1:
            pass

        return sub_t_count


    def json_serial(self, para):
        return para.isoformat()
```

#### 温湿度測定DEMOプロジェクトに必要な環境変数の起動設定

```sh:iot_prov.conf
#!/bin/bash

sudo rm ./cron_mod.conf

# creating cron_mod.conf
echo BROKER_HOST=$(cat ~/iot_prov.conf | grep BROKER_NAME | awk -F'=' '{print $2}') | sudo tee -a  cron_mod.conf
echo BROKER_IP=$(cat ~/nw_prov.conf | grep APIP_WLAN1 | awk -F'=' '{print $2}') | sudo tee -a  cron_mod.conf
echo BROKER_USR=$(cat ~/iot_prov.conf | grep MOS_USR | awk -F'=' '{print $2}') | sudo tee -a  cron_mod.conf
echo BROKER_PW=$(cat ~/iot_prov.conf | grep MOS_PW | awk -F'=' '{print $2}') | sudo tee -a  cron_mod.conf
echo | sudo tee -a  cron_mod.conf

echo | sudo tee -a  cron_mod.conf
echo @reboot sh ~/.profile | sudo tee -a  cron_mod.conf
echo @reboot python ~/main.py | sudo tee -a  cron_mod.conf

# adding .profile
echo | sudo tee -a  ~/.profile
echo "export BROKER_HOST="$(cat ./iot_prov.conf | grep BROKER_NAME | awk -F'=' '{print $2}') | sudo tee -a  ~/.profile
echo "export BROKER_IP=$(cat ~/nw_prov.conf | grep APIP_WLAN1 | awk -F'=' '{print $2}')" | sudo tee -a  ~/.profile
echo "export BROKER_USR=$(cat ./iot_prov.conf | grep MOS_USR | awk -F'=' '{print $2}')" | sudo tee -a  ~/.profile
echo "export BROKER_PW=$(cat ./iot_prov.conf | grep MOS_PW | awk -F'=' '{print $2}')" | sudo tee -a  ~/.profile

crontab -r
crontab ./cron_mod.conf
```


#### filterの設定
iptables.ipv4.filterを設定します。

```sh:set_nft.sh
#!/bin/bash

sudo cp /etc/iptables/rules.v4 /etc/iptables/rules.v4.bak
sudo rm /etc/iptables/rules.v4

cidr=$(cat ./nw_prov.conf | grep WLAN1_CIDR | awk -F'=' '{print $2}' | sed -e "s/[\r\n]\+//g")
router_ip=$(cat ./nw_prov.conf | grep APIP_WLAN1 | awk -F'=' '{print $2}' | sed -e "s/[\r\n]\+//g")

sudo rm /etc/iptables/rules.v4
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -d 127.0.0.0/8 ! -i lo -j REJECT --reject-with icmp-port-unreachable
sudo iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A INPUT -s 127.0.0.0/8 -p tcp -m tcp --dport 1883 -j ACCEPT
sudo iptables -A INPUT -s ${cidr}/24 -i wlan1 -p tcp -m tcp --dport 1883 -j ACCEPT
sudo iptables -A INPUT -s ${router_ip}/32 -p udp -m udp --dport 123 -j ACCEPT
sudo iptables -A INPUT -s ${cidr}/24 -i wlan1 -p udp -m udp --dport 53 -j ACCEPT
sudo iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 --tcp-flags FIN,SYN,RST,ACK SYN -m hashlimit --hashlimit-name t_sshd --hashlimit-upto 1/min --hashlimit-burst 10 --hashlimit-mode srcip --hashlimit-htable-expire 120000 -j ACCEPT
sudo iptables -A INPUT -s 192.168.0.0/16 -p icmp -m icmp --icmp-type 8 -j ACCEPT
sudo iptables -A INPUT -s 169.254.0.0/16 -p icmp -m icmp --icmp-type 8 -j ACCEPT
sudo iptables -A INPUT -s ${cidr}/24 -p icmp -m icmp --icmp-type 8 -j ACCEPT
sudo iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
sudo iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable

sudo service netfilter-persistent reload
sudo service netfilter-persistent save
sudo sh -c "iptables-save >/etc/iptables.ipv4.filter"
sudo iptables -L
```

#### シャットダウン
動作確認前にDHT11を接続するので一度電源を落とします。
```sh
sudo shutdown now
```

## 動作確認

#### DHT11の接続後にラズパイ起動
GPIO21に信号線をつなぎVCC(5V)とGNDに結線してラズパイを起動します。

#### AP親機から子機への疎通確認
```sh
user@test1:~ $ ping 10.1.1.11
PING 10.1.1.11 (10.1.1.11) 56(84) bytes of data.
64 bytes from 10.1.1.11: icmp_seq=1 ttl=64 time=54.0 ms
64 bytes from 10.1.1.11: icmp_seq=2 ttl=64 time=1.40 ms
64 bytes from 10.1.1.11: icmp_seq=3 ttl=64 time=1.37 ms
64 bytes from 10.1.1.11: icmp_seq=4 ttl=64 time=4.26 ms
64 bytes from 10.1.1.11: icmp_seq=5 ttl=64 time=1.50 ms
64 bytes from 10.1.1.11: icmp_seq=6 ttl=64 time=1.57 ms
```


#### AP親機からSSH接続
pingが通るので設定をミスしていなければSSH接続できます。config設定してあるとおりにsub1を呼び出します。

```sh
user@test1:~ $ ssh sub1
The authenticity of host '10.1.1.11 (10.1.1.11)' can't be established.'
ECDSA key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.1.1.11' (ECDSA) to the list of known hosts.
Linux subpi1 5.15.84-v7+ #1613 SMP Thu Jan 5 11:59:48 GMT 2023 armv7l

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
You have new mail.
Last login: Fri Mar 10 19:10:54 2023 from 10.1.1.1
user@subpi1:~ $ 
```

#### 温湿度測定DEMOプロジェクトの起動確認
設定どおり自動起動してプロセスが動いていることを確認します。
```sh
user@subpi1:~ $ ps -ax | grep python
  370 ?        Ss     0:00 /bin/sh -c python /home/user/main.py
  376 ?        S      4:39 python /home/user/main.py
 1424 pts/0    S+     0:00 grep --color=auto python
user@subpi1:~ $ 
```

#### **【2023.03.13追記】** 親機<->子機間の相互PubSub確認
親機から子機へPubSubできることを確認します。
```sh:親機から子機へpublish
user@test1:~ $ mosquitto_pub -h "10.1.1.11" -u MyMosquittoUsername -P MymosquittoPW -t "subpi1_mos" -m "{\"body\" : \"test\"}"
```
```sh:子機でsubscribe
user@subpi1:~ $ mosquitto_sub -h "10.1.1.11" -u MyMosquittoUsername -P MymosquittoPW -t "subpi1_mos/#" -v
subpi1_mos {"body" : "test"}
```
子機から親機へPubSubできることも確認します。子機で起動時実行した温湿度測定DEMO codeがPublishした温湿度も受信していることを確認できました。雨の日に湿度27%なのは恐らくセンサーがお疲れですのでご容赦を…(筆者と一緒です)
```sh:子機から親機へpublish
user@subpi1:~ $ mosquitto_pub -h "10.1.1.1"  -u MyMosquittoUsername -P MymosquittoPW -t "test1_mos" -m "{\"body\" : \"test\"}"
```
```sh:親機でsubscribe
user@test1:~ $ mosquitto_sub -h "10.1.1.1" -u MyMosquittoUsername -P MymosquittoPW -t "test1_mos/#" -v
test1_mos/subpi1/TempHumi {"Timestamp": 1678672200, "temp": 24.0, "humi": 27.0}
test1_mos {"body" : "test"}
```

#### MQTT テストクライアントのsubscriberで受信確認
ラズパイ子機がPublishしたデータがAP親機でBridgeされてAWS IoTでsubscribeされていることが確認できました。

<img width= 800 src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/565396/e8c0d66d-fd0e-d86a-0852-a3bf17e3674a.png>


## 次回

[ラズパイ親機から子機経由でNゲージを遠隔制御する（３）モータードライバーでNゲージを操作]()を予定します。


## 追補

**2023.03.10** 暫定投稿としてmqtt brokerの設定を説明していませんが後日追記する予定です(remind)

**2023.03.13** mosquittoの認証設定と親機子機間で相互PubSub確認を追加しました。