「#ラズパイ」タグアーカイブ

【ラズパイ】【いろいろ計測モニター】HTTPサーバを実装する

測定した温度、湿度をWindows側で表示させたいと思います。

前回HATでやったときはラズパイ側をクライアント、Windows側とサーバとして動作させましたが、

今回は逆で、ラズパイ側をサーバ、Windows側をクライアントとして実行させます。

常に稼働している方をサーバにする方が何かと都合が良い。

こちらの記事を参考にしました。

https://qiita.com/linxuesong/items/8ac98102c24b8f587a16

ただし、このままサーバとして稼働させると、他の処理が出来なくなってしまいます。

なので、HTTPサーバの処理を、既存のループ処理とは別スレッドで実行する必要があります。

なので、スレッドの扱いについて、こちらの記事を参照。

https://qiita.com/tchnkmr/items/b05f321fa315bbce4f77

最終的に組み上がったコードはこちら。

import os
import sys
import urllib.parse
import json
import RPi.GPIO as GPIO
import time
import datetime
import calendar
import GLCD
import AM2320
import Weather
import threading

from http.server import BaseHTTPRequestHandler
from http.server import HTTPServer
from http import HTTPStatus

PORT = 8000
sw = False

def __main__():
    global sw
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(22,GPIO.IN) 
    GPIO.add_event_detect(22, GPIO.FALLING, callback=callback, bouncetime=300)
    GLCD.PinsInit(20, 7, 8, 9, 18, 19, 10, 11, 12, 13, 14, 15, 16, 17)
    GLCD.GLCDInit()
    GLCD.GLCDDisplayClear()

    roop = 10 * 60 * 60
    mode = 1
    
    thread = threading.Thread(target=httpServe)
    thread.start()
    
    try:
        while True:

            #既存のループ処理

            time.sleep(1)
    except KeyboardInterrupt:
        GLCD.GLCDDisplayClear()
        GPIO.cleanup()

def callback(channel):
    global sw
    sw = True

def httpServe():
    handler = StubHttpRequestHandler
    httpd = HTTPServer(('',PORT),handler)
    httpd.serve_forever()

class StubHttpRequestHandler(BaseHTTPRequestHandler):
    server_version = "HTTP Stub/0.1"

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def do_GET(self):
        enc = sys.getfilesystemencoding()

        data = {
            'datetime' : datetime.datetime.now().strftime('%Y:%m:%d %H:%M:%S'),
            'temperature': AM2320.GetTemp(),
            'humidity': AM2320.GetHum(),
        }

        encoded = json.dumps(data).encode()

        self.send_response(HTTPStatus.OK)
        self.send_header("Content-type", "text/html; charset=%s" % enc)
        self.send_header("Content-Length", str(len(encoded)))
        self.end_headers()

        self.wfile.write(encoded)     

__main__()

Windows側からHTTPのGETリクエストを受信すると、時刻、温度、湿度をJson形式で応答を返します。

今日の北海道は暑いぜ。

【ラズパイ】【いろいろ計測モニター】時計表示を大きくする。

フォントをもっとシンプルなものに変更しました。

複雑なフォントにすると、データ化がめんどくさい。

こういうシンプルなデザインにした方が、数字表示用8LEDディスプレイみたいで(厳密には違うが)データ化が1時間程度で完了しました。

さらに、横幅も少し小さくして、時間と分のセパレータ「:」も入れることが出来ました。

このサイズだったらかなり見やすいでしょ。

    [ #1
        [
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        ],
        [
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
        ],
        [
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
        ],
        [
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
        ],
        [
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00,
        ],
        [
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
        ]
    ],

データもこんな感じなので(というか、縦に線を引いているだけなので)サクッと作れました。

具体的にはgitHubのソースを見て欲しい。

https://github.com/takishita2nd/RaspiDisplayMonitor

表示する時刻データを、

GLCD.drowLargeClock(datetime.datetime.now().strftime('%H:%M'))

こんな感じで文字列化して、

def drowLargeClock(time):
    position = 0
    val = 0
    for s in time:
        if s == ':':
            val = 10
        else:
            val = int(s)

        for page in range(6):
            for addr in range(24):
                if position + addr < 64:
                    SelectIC(1)
                    SetPage(page)
                    SetAddress(position + addr)
                else:
                    SelectIC(2)
                    SetPage(page)
                    SetAddress(position + addr - 64)
                WriteData(LFont.Array[val][page][addr])
        position += addr

このように処理させることで、大きいフォントで時刻表示できます。

これでさらに使い物になりましたな。

【ラズパイ】【いろいろ計測モニター】時計表示を大きくしたい。

サービスでプログラムを稼働させてからは安定して動作しております。

ラズパイZeroで稼働させていますが、本体を手で触っても全然熱を感じないので、省エネだし、熱で壊れることもありません。

いいね、これ。

ただ、文字が小さいので、時刻だけ大きく表示させてもいいんじゃないかなって思っています。

ということで、フォントデータを作成中です。

Array = [
    [ #0
        [
            0x00, 0x00, 0x00, 0x80, 0xc0, 0xf0, 0xf8, 0xf8,
            0xfc, 0xfe, 0x7e, 0x3e, 0x3e, 0x1f, 0x1f, 0x1f,
            0x1f, 0x1f, 0x1f, 0x1f, 0x3f, 0x3e, 0x7e, 0xfe,
            0xfc, 0xf8, 0xf8, 0xf0, 0xc0, 0x80, 0x00, 0x00
        ],
        [
            0x00, 0xe0, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x0f,
            0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x03, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xe0
        ],
        [
            0xf8, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff, 0xff
        ],
        [
            0x3f, 0xff, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff
        ],
        [
            0x00, 0x1f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xe0,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x80, 0xe0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x0f
        ],
        [
            0x00, 0x00, 0x00, 0x03, 0x07, 0x1f, 0x3f, 0x3f,
            0x7f, 0xfe, 0xfc, 0xf8, 0xf8, 0xf0, 0xf0, 0xf0,
            0xf0, 0xf0, 0xf0, 0xf0, 0xf8, 0xf8, 0xfc, 0xfe,
            0x7f, 0x3f, 0x3f, 0x1f, 0x07, 0x03, 0x00, 0x00
        ]
    ],
:
:

ちょっと大きくしすぎた感がありますが、最低限数字4文字入ればなんとかなるので、このままフォントデータのビットパターンを作成していきます。(残り6文字)

【ラズパイ】スイッチ操作を割り込みで行う。

最新ソースはこちら。

https://github.com/takishita2nd/RaspiDisplayMonitor

以前使用していた12個のスイッチは3つの信号と4つの制御線の組み合わせで検出していましたが、

いまはスイッチは1個なので、エッジの割り込みで操作できるはずです。

スレッドが一つ無くなるので、ラズパイZeroでも動作が軽くなると思います。

import RPi.GPIO as GPIO
import time
import datetime
import calendar
import GLCD
import AM2320
import Weather

sw = False

def __main__():
    global sw
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(22,GPIO.IN) 
    GPIO.add_event_detect(22, GPIO.FALLING, callback=callback, bouncetime=300)
    GLCD.PinsInit(20, 7, 8, 9, 18, 19, 10, 11, 12, 13, 14, 15, 16, 17)
    GLCD.GLCDInit()
    GLCD.GLCDDisplayClear()

    roop = 10 * 60 * 60
    mode = 1
    try:
        while True:
            if sw == True:
                GLCD.GLCDDisplayClear()
                mode += 1
                if mode > 3:
                    mode = 1
                sw = False

            if mode == 1:
                if roop >= 10 * 60 * 60:
                    GLCD.GLCDDisplayClear()
                    Weather.RequestAPI()
                    weather = Weather.GetWeather()
                    temp = Weather.GetTemp()
                    roop = 0

                GLCD.GLCDPuts(1, 0, "Date :")
                GLCD.GLCDPuts(5, 8, datetime.datetime.now().strftime('%Y:%m:%d %A '))
                GLCD.GLCDPuts(1, 16, "Weather :")
                GLCD.GLCDPuts(10,24, weather)
                GLCD.GLCDPuts(10,32, "Temp : " + format(temp) + 'C')
                GLCD.GLCDPuts(1, 40, "Time : " + datetime.datetime.now().strftime('%H:%M'))
                GLCD.GLCDPuts(1, 48, "Humidity    : " + AM2320.GetHum() + '%')
                GLCD.GLCDPuts(1, 56, "Temperature : " + AM2320.GetTemp() + 'C')

                roop += 1
            elif mode == 2:
                cal = calendar.month(datetime.datetime.now().year, datetime.datetime.now().month)
                cals = cal.split("\n")
                y = 0
                for c in cals:
                    GLCD.GLCDPuts(1, y, c)
                    y += 8
            
            elif mode == 3:
                GLCD.drowMashiro()

            time.sleep(1)
    except KeyboardInterrupt:
        GLCD.GLCDDisplayClear()
        GPIO.cleanup()

def callback(channel):
    global sw
    sw = True

__main__()

GPIO.add_event_detect()で割り込み処理の登録を行います。

監視するGPIOの番号、エッジ(HIGH/LOW)、コールバック関数、検出間隔指定します。

上の処理では、GPIO22が下りエッジを検出したらcallback()が実行されます。

スイッチ検出がかなりスムーズになりました。

【ラズパイ】pythonプログラムをサービスで実行する

ラズパイZeroで作成したpythonプログラムをサービスに登録して、起動と同時に実行するように設定します。

以下のファイルを作成

pimonitor.service

[Unit]
Description=PIMonitor
After=syslog.target

[Service]
Type=simple
WorkingDirectory=/opt/pimonitor
ExecStart=/usr/bin/python3 main.py
TimeoutStopSec=5
StandardOutput=null

[Install]
WantedBy = multi-user.target

このファイルを/etc/systemd/system/配下にコピー

$ cp pimonitor.service /etc/systemd/system

※ /opt/pimonitor配下にソースファイル全て揃っている前提。

パーミッションの設定でrootでも実行できようにしておく

$ chmod 777 *

サービス開始

$ sudo systemctl start pimonitor.service

サービス登録

$ sudo systemctl enable pimonitor.service

【ラズパイ】【いろいろ計測モニター】実際に組んでみる。

今日は9時間頑張りました。

実際にこの回路を組んでみました。

材料はこちらの記事にあります。

結果はこうなりました。

挫けた。

そもそも、

このラズパイZero用の基板に全部盛り込もうとしたのが間違い。

かなり難易度の高いはんだ付け作業になりました。

また、穴と穴が繋がっていない(ユニバーサル基板とはそういうもの)ので、その線をつなぐのも苦労しました。

さらに、


GLCD側のピッチ幅がラズパイZero側の基板より小さい事が発覚。

これがさらにはんだ付けの難易度を上げました。

はんだの熱で基板と部品が若干溶けてました。

このままじゃ悔しいので、ブレッドボードに回路を作り直して、ラズパイZeroで動かしました。

今回はこれで勘弁してください。

9時間頑張ったんだよ・・・

でもラズパイZeroで動かしているので、長時間稼働していても全然暑くないです。

でも性能は明らかに劣っているからモッサリしているけどね。

損傷して使い物にならなくなってしまいました。

残念ながらゴミ箱行きです。

【ラズパイ】【いろいろ計測モニター】

どうせならきちんとして使える物を作りたい。

前回まではラズパイ4(2GB)を使用しましたが、これって、ファンを使わないと発熱がやばいらしいですね。

なので、消費電力が少なくて、発熱も少ないラズパイZERO HWを買いました。

使用する部品も買いました。

MicroUSBのACアダプター

MicroSDカード(16GB)

8GBでも十分だったんだけどね。

これらは今まで使用していた部品と同じです。

ラズパイZero用の基板です。

これにピンヘッダを取り付けて、基板と本体を取り外しできるようにします。

スイッチに使用する抵抗とダイオード。

(たぶん)同じ物を購入しました。

それらをつなぐジャンパ線です。

こんな感じで回路を組もうと思ってます。

結構ごちゃごちゃしてる。

まぁ、ピンの位置とか実際の位置に合わせて書いているので仕方が無い。

問題はスイッチが正しく動くかどうか。

前回は12個のスイッチでしたが、今回は1個のスイッチを使います。それに合わせて同じような回路を組んでいます。

ちゃんと機能するか、ブレッドボードに簡単に組んでみました。

上手くいけば、スイッチ押下時にインプット信号がHIGH→LOWになるはずです。

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setup(21, GPIO.IN)
swState = 0

try:
    while True:
        if GPIO.input(21) == 0 and swState == 0:
            time.sleep(0.05)
            if GPIO.input(21) == 0:
                print("ON")
                swState = 1
        elif GPIO.input(21) == 1 and swState == 1:
            print("OFF")
            swState = 0
        time.sleep(0.005)

except KeyboardInterrupt:
    GPIO.cleanup()
$ python3 test.py 
ON
OFF
ON
OFF
ON
OFF
ON
OFF
ON
OFF

うん、うまく動いているようです。

じゃあ、制作開始といきますか。

【ラズパイ】これまでやってきたことを組み合わせる。

ここまでやってきたことを全て組み合わせます。

GLCDに表示するのは、

  • 時刻、天気、温度、湿度
  • カレンダー
  • 倉田ましろの絵

この3つの表示を、スイッチの1番を押すことで切り替えます。

import RPi.GPIO as GPIO
import time
import datetime
import calendar
import GLCD
import AM2320
import Weather
import SW


def __main__():
    GPIO.setmode(GPIO.BCM)
    GLCD.PinsInit(20, 7, 8, 9, 18, 19, 10, 11, 12, 13, 14, 15, 16, 17)
    GLCD.GLCDInit()
    GLCD.GLCDDisplayClear()

    SW.PinsInit(21, 22, 23, 24, 25, 26, 27)
    SW.Start()

    roop = 10 * 60 * 60
    mode = 1
    try:
        while True:
            key = SW.GetKey()
            if key == "1":
                GLCD.GLCDDisplayClear()
                mode += 1
                if mode > 3:
                    mode = 1

            if mode == 1:
                if roop >= 10 * 60 * 60:
                    Weather.RequestAPI()
                    weather = Weather.GetWeather()
                    temp = Weather.GetTemp()
                    roop = 0

                GLCD.GLCDPuts(1, 0, "Date :")
                GLCD.GLCDPuts(5, 8, datetime.datetime.now().strftime('%Y:%m:%d %A'))
                GLCD.GLCDPuts(1, 16, "Weather :")
                GLCD.GLCDPuts(10,24, weather)
                GLCD.GLCDPuts(10,32, "Temp : " + format(temp) + 'C')
                GLCD.GLCDPuts(1, 40, "Time : " + datetime.datetime.now().strftime('%H:%M:%S'))
                GLCD.GLCDPuts(1, 48, "Humidity    : " + AM2320.GetHum() + '%')
                GLCD.GLCDPuts(1, 56, "Temperature : " + AM2320.GetTemp() + 'C')

                roop += 1
            elif mode == 2:
                cal = calendar.month(datetime.datetime.now().year, datetime.datetime.now().month)
                cals = cal.split("\n")
                y = 0
                for c in cals:
                    GLCD.GLCDPuts(1, y, c)
                    y += 8
            
            elif mode == 3:
                GLCD.drowMashiro()

            time.sleep(0.1)
    except KeyboardInterrupt:
        SW.Stop()
        GLCD.GLCDDisplayClear()
        GPIO.cleanup()

__main__()

スイッチ押下でmodeの値が変化し、表示処理の内容を変えています。

なかなか上手くいったので、これで完成品を作ってみようか。

ラズパイ遊びの集大成です。

【ラズパイ】【GLCD】カレンダーを表示する

GLCDにカレンダーを表示させたいと思います。

import RPi.GPIO as GPIO
import time
import datetime
import calendar
import GLCD
import AM2320
import Weather
import SW


def __main__():
    GPIO.setmode(GPIO.BCM)
    GLCD.PinsInit(20, 7, 8, 9, 18, 19, 10, 11, 12, 13, 14, 15, 16, 17)
    GLCD.GLCDInit()
    GLCD.GLCDDisplayClear()

    try:
        while True:
            cal = calendar.month(datetime.datetime.now().year, datetime.datetime.now().month)
            cals = cal.split("\n")
            y = 0
            for c in cals:
                GLCD.GLCDPuts(1, y, c)
                y += 8

            time.sleep(0.1)
    except KeyboardInterrupt:
        GLCD.GLCDDisplayClear()
        GPIO.cleanup()

__main__()

calendar.month(year, month)で現在月のカレンダーを文字列で出力されます。

これは改行文字を使って整形されているので、改行文字で分割し一行ずつ表示位置を変えながら表示させています。

出来れば今日の日付の部分が分かるようにしたかったんだけど、無理っぽい。

よし、これでやりたいことは一通り出来た。

【ラズパイ】スイッチをモジュール化する

前回までの状況はこちら。

前回作成したサンプルソースコードを元に、スイッチ処理をモジュール化します。

やり方としては、スイッチの押下検出をスレッドで動かしてメインスレッドで取り出す、という方式が良いと思います。

GPIOのエッジ検出で割り込み処理する、という方法もありますが、このスイッチモジュールの仕様上、それは難しそうです。

import RPi.GPIO as GPIO
import time
import threading

X_p = 0
Y_p = 0
Z_p = 0
A_p = 0
B_p = 0
C_p = 0
D_p = 0

Non = ""
Key = Non

thread = None
Loop = True

def PinsInit(x, y, z, a, b, c, d):
 省略

def Start():
    thread = threading.Thread(target=threadProcess)
    thread.start()

def Stop():
    global Loop
    Loop = False

def GetKey():
    global Key
    ret = Key
    Key = Non
    return ret

def threadProcess():
    global Key

    col = 1
    swState = [0] * 12
    while Loop:
        if col == 1:
            GPIO.output(X_p, GPIO.LOW)
            GPIO.output(Y_p, GPIO.HIGH)
            GPIO.output(Z_p, GPIO.HIGH)
        elif col == 2:
            GPIO.output(X_p, GPIO.HIGH)
            GPIO.output(Y_p, GPIO.LOW)
            GPIO.output(Z_p, GPIO.HIGH)
        else:
            GPIO.output(X_p, GPIO.HIGH)
            GPIO.output(Y_p, GPIO.HIGH)
            GPIO.output(Z_p, GPIO.LOW)

        # 1押下
        if swState[1] == 0 and col == 1 and GPIO.input(D_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(D_p) == 0:
                Key = "1"
                swState[1] = 1
        # 1押下戻し
        elif swState[1] == 1 and col == 1 and GPIO.input(D_p) == 1:
            Key = Non
            swState[1] = 0
        # 2押下
        if swState[2] == 0 and col == 2 and GPIO.input(D_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(D_p) == 0:
                Key = "2"
                swState[2] = 1
        # 2押下戻し
        elif swState[2] == 1 and col == 2 and GPIO.input(D_p) == 1:
            Key = Non
            swState[2] = 0
        # 3押下
        if swState[3] == 0 and col == 3 and GPIO.input(D_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(D_p) == 0:
                Key = "3"
                swState[3] = 1
        # 3押下戻し
        elif swState[3] == 1 and col == 3 and GPIO.input(D_p) == 1:
            Key = Non
            swState[3] = 0
        # 4押下
        if swState[4] == 0 and col == 1 and GPIO.input(C_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(C_p) == 0:
                Key = "4"
                swState[4] = 1
        # 4押下戻し
        elif swState[4] == 1 and col == 1 and GPIO.input(C_p) == 1:
            Key = Non
            swState[4] = 0
        # 5押下
        if swState[5] == 0 and col == 2 and GPIO.input(C_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(C_p) == 0:
                Key = "5"
                swState[5] = 1
        # 5押下戻し
        elif swState[5] == 1 and col == 2 and GPIO.input(C_p) == 1:
            Key = Non
            swState[5] = 0
        # 6押下
        if swState[6] == 0 and col == 3 and GPIO.input(C_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(C_p) == 0:
                Key = "6"
                swState[6] = 1
        # 6押下戻し
        elif swState[6] == 1 and col == 3 and GPIO.input(C_p) == 1:
            Key = Non
            swState[6] = 0
        # 7押下
        if swState[7] == 0 and col == 1 and GPIO.input(B_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(B_p) == 0:
                Key = "7"
                swState[7] = 1
        # 7押下戻し
        elif swState[7] == 1 and col == 1 and GPIO.input(B_p) == 1:
            Key = Non
            swState[7] = 0
        # 8押下
        if swState[8] == 0 and col == 2 and GPIO.input(B_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(B_p) == 0:
                Key = "8"
                swState[8] = 1
        # 8押下戻し
        elif swState[8] == 1 and col == 2 and GPIO.input(B_p) == 1:
            Key = Non
            swState[8] = 0
        # 9押下
        if swState[9] == 0 and col == 3 and GPIO.input(B_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(B_p) == 0:
                Key = "9"
                swState[9] = 1
        # 9押下戻し
        elif swState[9] == 1 and col == 3 and GPIO.input(B_p) == 1:
            Key = Non
            swState[9] = 0
        # *押下
        if swState[10] == 0 and col == 1 and GPIO.input(A_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(A_p) == 0:
                Key = "*"
                swState[10] = 1
        # *押下戻し
        elif swState[10] == 1 and col == 1 and GPIO.input(A_p) == 1:
            Key = Non
            swState[10] = 0
        # 0押下
        if swState[0] == 0 and col == 2 and GPIO.input(A_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(A_p) == 0:
                Key = "0"
                swState[0] = 1
        # 0押下戻し
        elif swState[0] == 1 and col == 2 and GPIO.input(A_p) == 1:
            Key = Non
            swState[0] = 0
        # #押下
        if swState[11] == 0 and col == 3 and GPIO.input(A_p) == 0:
            # チャタリング回避
            time.sleep(0.05)
            if GPIO.input(A_p) == 0:
                Key = "#"
                swState[11] = 1
        # #押下戻し
        elif swState[11] == 1 and col == 3 and GPIO.input(A_p) == 1:
            Key = Non
            swState[11] = 0

        col += 1
        if col > 3:
            col = 1
        
        time.sleep(0.005)

Start()でthreadProcess()をバックグラウンドで実行、Stop()でLoop=Falseになるまで実行します。

スイッチを押すと、その値がKeyに入るので、それをGetKey()で取り出します。

変数でワンクッション置いているのは、ボタン押しっぱなしで複数回キー入力を検出するのを防ぐためです。

def __main__():
    GPIO.setmode(GPIO.BCM)

    SW.PinsInit(21, 22, 23, 24, 25, 26, 27)
    SW.Start()

    try:
        while True:
            key = SW.GetKey()
            if key != "":
                print(key)

            time.sleep(0.1)
    except KeyboardInterrupt:
        SW.Stop()
        GPIO.cleanup()

__main__()

ここまで出来れば、他モジュールと組み合わせることも出来そうです。