(MA-X320/LAD)DIを検知してプログラムを起動する

# ■概略
[センチュリー・システムズのMAシリース](https://www.centurysys.co.jp/products/linuxserver/max300s.html)はLinuxアプライアンス
ラズベリーパイと同等のことができるが、屋外でも安定して動作するのでIoTソリューションに利用している
＊動作保証が-20度～60度(Raspberry Pi 3 model Bは0～70度)
　屋外だと氷点下以下になることがある！
＊SLC(書込み回数が多い)SDカード(8GB)もオプション提供あり
＊機種によりLTE標準対応で、海外LTE対応機種もある

# ■GPIOとDIN、DOUT
MAシリーズは標準で2以上のアナログ入力(DIN)インターフェースがある
DINを使えば物理的なスイッチを押せば特定のプログラムを起動させることが可能になる
物理スイッチが押された時、python3の常駐プログラム(gpio)で検知し特定のプログラムを起動する方法をまとめた

MA-X320/LADはDINが標準で2個、拡張で24個ある
メインボード側(裏/ACアダプタ側)のDIの名称  DIN_S0とDIN_S1
拡張ボード側(表)のDIの名称  EXT_DI0 - EXT_DI23
[DI/DOの利用…DINの名前他](https://ma-tech.centurysys.jp/doku.php?id=use_di_do:start)

# ■GPIO検知常駐プログラム
### ◯DIN名に対応するgpiochip名とoffset番号
DIN名に対応するgpiochip名とoffset番号をMA-X320/LADにsshログインして調べることができる
```
# gpiofind {DIN名}
```
例)
```
# gpiofind DIN_S0
gpiochip2 60
```

gpiochip毎に常駐プログラムが必要で、筆者の手元のMA-X320/LADではgpiochipが3つあった
この場合3プログラムを常駐させる

＊DIN名に対応するgpiochipとoffset番号一覧(筆者の手元で調査。各人調査すること)
<table border="1" style="border-collapse:collapse;width:500px">
<tr><th>DIN名</th><th>gipochip名</th><th>offset番号</th></tr>
<tr><td>DIN_S0</td><td>gpiochip2</td><td>60</td></tr>
<tr><td>DIN_S1</td><td>gpiochip2</td><td>61</td></tr>
<tr><td>EXT_E0</td><td>gpiochip4</td><td>0</td></tr>
<tr><td>EXT_E1</td><td>gpiochip4</td><td>1</td></tr>
<tr><td>EXT_E2</td><td>gpiochip4</td><td>2</td></tr>
<tr><td>EXT_E3</td><td>gpiochip4</td><td>3</td></tr>
<tr><td>EXT_E4</td><td>gpiochip4</td><td>4</td></tr>
<tr><td>EXT_E5</td><td>gpiochip4</td><td>5</td></tr>
<tr><td>EXT_E6</td><td>gpiochip4</td><td>6</td></tr>
<tr><td>EXT_E7</td><td>gpiochip4</td><td>7</td></tr>
<tr><td>EXT_E8</td><td>gpiochip4</td><td>8</td></tr>
<tr><td>EXT_E9</td><td>gpiochip4</td><td>9</td></tr>
<tr><td>EXT_E10</td><td>gpiochip4</td><td>10</td></tr>
<tr><td>EXT_E11</td><td>gpiochip4</td><td>11</td></tr>
<tr><td>EXT_E12</td><td>gpiochip3</td><td>0</td></tr>
<tr><td>EXT_E13</td><td>gpiochip3</td><td>1</td></tr>
<tr><td>EXT_E14</td><td>gpiochip3</td><td>2</td></tr>
<tr><td>EXT_E15</td><td>gpiochip3</td><td>3</td></tr>
<tr><td>EXT_E16</td><td>gpiochip3</td><td>4</td></tr>
<tr><td>EXT_E17</td><td>gpiochip3</td><td>5</td></tr>
<tr><td>EXT_E18</td><td>gpiochip3</td><td>6</td></tr>
<tr><td>EXT_E19</td><td>gpiochip3</td><td>7</td></tr>
<tr><td>EXT_E20</td><td>gpiochip3</td><td>8</td></tr>
<tr><td>EXT_E21</td><td>gpiochip3</td><td>9</td></tr>
<tr><td>EXT_E22</td><td>gpiochip3</td><td>10</td></tr>
<tr><td>EXT_E23</td><td>gpiochip3</td><td>11</td></tr>
</table>

### ◯GPIO常駐プログラム
[gpiofind pythonスクリプト](https://github.com/aswild/python-gpiod/tree/master/examples)を常駐に改造した

gpiomon.py
```
#!/usr/bin/env python3
# SPDX-License-Identifier: LGPL-2.1-or-later
# https://github.com/aswild/python-gpiod/tree/master/examples
#
import os, io, sys, re
import gpiod
# gpiomon.pyと同じディレクトリにconfig.py
sys.path.append(os.path.dirname(__file__))
import config

if __name__ == '__main__':
    def print_event(event):
    	if event.type == gpiod.LineEvent.RISING_EDGE:
    	    evstr = ' RISING EDGE'
    	elif event.type == gpiod.LineEvent.FALLING_EDGE:
    	    evstr = 'FALLING EDGE'
    	else:
    	    raise TypeError('Invalid event type')
    
    	print('event: {} source: {} timestamp: [{}.{}]'.format(evstr,
    							       event.source,
    							       event.sec, event.nsec))

    with gpiod.Chip(sys.argv[1]) as chip:
	offsets = []
	for off in config.gpio_offsets[sys.argv[1]]:
	    offsets.append(int(off))

	lines = chip.get_lines(offsets)
	lines.request(consumer=sys.argv[0], type=gpiod.LINE_REQ_EV_BOTH_EDGES)

	try:
	    while True:
		ev_lines = lines.event_wait(sec=1)
		if ev_lines:
		    for line in ev_lines:
			event = line.event_read()
			print_event(event)
			if event.type == gpiod.LineEvent.FALLING_EDGE:
                            if 'DIN_S0' in event.source:
            			        # DIN_S0時のプログラムを起動する
                            elif 'DIN_S1' in event.source:
            			        # DIN_S1時のプログラムを起動する
                            elif 'EXT_E0' in event.source:
            			        # EXT_E0時のプログラムを起動する
                            …
                            elif 'EXT_E23' in event.source:
            			        # EXT_E23時のプログラムを起動する
	except KeyboardInterrupt:
	    sys.exit(130)
```

config.py
```
## GPIO
gpiodir = "system_gpiomon_*.pyのあるディレクトリ"
# https://ma-tech.centurysys.jp/doku.php?id=use_di_do:start#:~:text=また、外部電源の接続
# メインボード側(裏/ACアダプタ側) DIN_S0とDIN_S1
# 拡張ボード側(表) EXT_DI0 - EXT_DI23
gpio_chip_S00_S01 = 'gpiochip2'   # gpiofind DIN_S0|awk '{print $1}'
gpio_chip_E00_E11 = 'gpiochip4'   # gpiofind EXT_DI0|awk '{print $1}'
gpio_chip_E12_E23 = 'gpiochip3'   # gpiofind EXT_DI12|awk '{print $1}'
gpio_offsets = {}
gpio_offsets[gpio_chip_S00_S01] = [60,61] # gpiofind DIN_S0,S1|awk '{print $2}'
gpio_offsets[gpio_chip_E00_E11] = [0,1,2,3,4,5,6,7,8,9,10,11]  # gpiofind EXT_DI0-11|awk '{print $2}'
gpio_offsets[gpio_chip_E12_E23] = [0,1,2,3,4,5,6,7,8,9,10,11]  # gpiofind EXT_DI12-23|awk '{print $2}'
```

### ◯GPIO起動スクリプト
DIN_S0-S1用起動スクリプト
systemd_gpiomon_s0.py
```
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
#
# systemd_gpio_*.py
#
# 標準
import os, io, sys, re
import subprocess
# systemd_gpio_*.pyと同じディレクトリにconfig.py
sys.path.append(os.path.dirname(__file__))
import config

cmd_template = "{0}/gpiomon.py {1}"
cmd = cmd_template.format(config.gpiodir, config.gpio_chip_S00_S01)
ret = subprocess.run(cmd, shell=True)
```

EXT_E0-E11用起動スクリプト
systemd_gpiomon_e0.py
```
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
#
# systemd_gpio_*.py
#
# 標準
import os, io, sys, re
import subprocess
# systemd_gpio_*.pyと同じディレクトリにconfig.py
sys.path.append(os.path.dirname(__file__))
import config

cmd_template = "{0}/gpiomon.py {1}"
cmd = cmd_template.format(config.gpiodir, config.gpio_chip_E00_E11)
ret = subprocess.run(cmd, shell=True)
```

EXT_E12-E23用起動スクリプト
systemd_gpiomon_e12.py
```
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
#
# systemd_gpio_*.py
#
# 標準
import os, io, sys, re
import subprocess
# systemd_gpio_*.pyと同じディレクトリにconfig.py
sys.path.append(os.path.dirname(__file__))
import config

cmd_template = "{0}/gpiomon.py {1}"
cmd = cmd_template.format(config.gpiodir, config.gpio_chip_E12_E23)
ret = subprocess.run(cmd, shell=True)
```

### ◯GPIO常駐設定
DIN_S0-S1用起動設定
/etc/systemd/system/gpiomon_s0.service
```
[Unit]
Description=GPIO Monitor S00-S01
After=network.target

[Service]
Type=simple
PIDFile=/var/run/systemd_gpiomon_s0.pid
RemainAfterExit=no
Restart=on-failure
RestartSec=10
ExecStart=/var/www/diomail/batch/systemd_gpiomon_s0.py
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target
```

EXT_E0-E11用起動設定
/etc/systemd/system/gpiomon_e0.service
```
[Unit]
Description=GPIO Monitor E00-11
After=network.target

[Service]
Type=simple
PIDFile=/var/run/systemd_gpiomon_e0.pid
RemainAfterExit=no
Restart=on-failure
RestartSec=10
ExecStart=/var/www/diomail/batch/systemd_gpiomon_e0.py
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target
```

EXT_E12-E23用起動設定
/etc/systemd/system/gpiomon_e11.service
```
[Unit]
Description=GPIO Monitor E12-23
After=network.target

[Service]
Type=simple
PIDFile=/var/run/systemd_gpiomon_e12.pid
RemainAfterExit=no
Restart=on-failure
RestartSec=10
ExecStart=/var/www/diomail/batch/systemd_gpiomon_e12.py
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target
```

```
# systemctl enable gpiomon_e0
# systemctl enable gpiomon_e12
# systemctl enable gpiomon_s0
# systemctl start gpiomon_e0
# systemctl start gpiomon_e12
# systemctl start gpiomon_s0
```

# ■ログ出力
※DIN_S0をon/offすると以下の出力がでる

/var/log/syslog
```
2024-09-30T18:05:24.400672+09:00 max300lad systemd_gpiomon_s0.py[4053]: event: FALLING EDGE source: 'gpiochip2:60 /DIN_S0/' timestamp: [26166.410662111]
※print("FALLING_EDGE")が実行された
2024-09-30T18:05:24.401485+09:00 max300lad systemd_gpiomon_s0.py[4053]: FALLING_EDGE
2024-09-30T18:05:37.098265+09:00 max300lad systemd_gpiomon_s0.py[4053]: event:  RISING EDGE source: 'gpiochip2:60 /DIN_S0/' timestamp: [26179.108469019]
```
# ■USBステップアップコンバータ
＊写真の組み合わせを作り出力をMA-X320/LADの裏側のDIN_0とDIN_Cに挿した
＊青い基盤は以下の[リンク](https://www.amazon.co.jp/dp/B01D0WSBGY?ref=cm_sw_r_apin_dp_REXJ54M9SGBPXQEGEFVH&ref_=cm_sw_r_apin_dp_REXJ54M9SGBPXQEGEFVH&social_share=cm_sw_r_apin_dp_REXJ54M9SGBPXQEGEFVH&starsLeft=1&skipTwisterOG=1)を購入した
＊黒いスイッチのON/OFFで上記ログが出力される
![USBStepUp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3875673/2e6ba610-8300-7380-84f5-5604713a7617.png)