【Raspberry Pi】LCD1602Aに長文表示機能を実装する

# はじめに
この記事は「ラズパイ5を使ってLCD1602Aを操作していこう！」という趣旨のものになります。
以前の記事で作成したプログラムをベースに改修を行いますので、先にご覧いただけますと幸いです！
- [【Raspberry Pi】I2CアダプターなしでLCD1602Aに文字を表示する](https://qiita.com/ShibesSaico0401/items/2087ea0c3c7891d225c4)

### ■成果物
・ラズパイ5でのLCD操作プログラム（長文表示機能付き）
↓こんな感じで表示ができます↓
![IMG_0812(2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/c627fec2-1742-bafb-b975-20706ee7ad8e.gif)


# 動機
前回のLCD1602Aを操作する記事では以下の課題が残っていました。

1. ASCII以外の文字に対応する
2. write_four_bitsのEピン立ち上げ順序を修正
3. 長文の表示機能を追加
4. 部品モジュールとして使用する際の処理・API追加

このうち、今回は2,3を解決していきたいと思います。
課題を言い換えるとこんな感じでしょうか。

- __【課題3】LCD1602Aで表示できる文字が16x2で32文字しかなくて不便！__
:point_up_tone2:メイン
- 【課題2】GPIOの処理タイミングが微妙におかしくて動作が不安！
:point_up_tone2:サブ

以上の思いを解消していきましょう。
今回の記事は __メインしっかり、サブは修正点のみ__ という塩梅でまとめたいと思います。
それではレッツゴー！

# ライブラリなど
- gpiozero 2.0
- Python 3.11.2

:::note warn
警告
本記事では文量の関係により、ライブラリのインストールなどの手順は記載しませんのでご注意ください。
諸兄が記事にされている内容で特に難しいことはないはずと思います。
:::


# LCD1602A とは
言わずもがなの16文字x2列の液晶ディスプレイです。
外観はこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/e22b4e67-61b4-dc47-60c1-2b87b113d271.png)
詳細はぜひ[前記事](https://qiita.com/ShibesSaico0401/items/2087ea0c3c7891d225c4)をご参照ください！


# メインテーマ（課題3）
### ■前提
まずは今回の課題の前提条件を整理してみましょう。
- __LCD1602Aに表示できる文字は16列x2行__
- __新たにLCDを追加することはできない__（GPIOが枯渇する）

というわけで、今ある16x2文字だけの表示を上手く使う必要があります。

### ■解決案
次に、前提から解決案を2つ考えてみました。
1. 長文を16文字ずつ切り出して、LCDに縦方向に流す形式で表示する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/4f216439-ad67-b764-d707-c1143593ad6b.png)

2. 長文を1文字ずつズラしてLCDに横方向に流す形式で表示する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/b5c64f3c-5016-a782-d868-4a91c0440504.png)

今回、LCDにそこまで長い文は表示させない予定なので、今回は __2の案「文字流し機能」を実装__ していくことにしましょう！

### ■実装
まずはどういった処理を行えば文字流しが行えるか思案します。
今回、文字が横に流れていく動作を想像し、LCDに表示している文字がどう変化していくかに着目してみます。
すると、 __一定時間経過ごとに表示している文字列の先頭の文字が、1文字ずつ削られていく__ という処理が繰り返されていることに気が付きます。
というわけで、「表示したい文字列の先頭の1文字を削って表示」という処理を繰り返し行えば、文字流し機能が実現できそうとわかりました！
では早速、フローチャートに処理の流れを書き起こしてみましょう。
![フローチャート.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/9d496fdc-5c84-214a-c70e-a64e6c086728.png)

初期化だったり代入処理など細かいことは抜けていますが、概ね↑のフローで文字流し機能が実装できそうです。

では続いてプログラミング！
というわけで今回は前回のプログラムに加え、次のメソッドをクラスに追加しました。
（「冗長だ！」「改修性が悪すぎw」などの意見もあるかと思いますが、生暖かく見ていただけますと幸いです:innocent:）

```python:python
    # 1, 2行目に文字を表示する
    def print_row12(self, text_row1, text_row2):
        self.clear()
        self.print(text_row1)
        self.set_cursor(1, 0)
        self.print(text_row2)

    # 文字を流れるように表示する
    def print_Stream(self, text_row1, text_row2, stream2Row=False, streamTime_s=0.3, lockTime_Disp_s = 1):
        print_onDebug("文字流し開始")
        length_text = len(text_row1)

        for _ in range(1): # 2回表示が丁度いい？(デモモード=1回)
            # 表示用変数に代入
            displayMsg_row1 = text_row1
            displayMsg_row2 = text_row2            

            # 文字数分のスライドが終わったら元の順番に戻して表示
            self.print_row12(text_row1, text_row2)
            time.sleep(lockTime_Disp_s)

            # 文字数分だけループ
            for i in range(length_text):
                # 文字列を表示
                self.print_row12(displayMsg_row1, displayMsg_row2)

                # ループ数に対応した数の頭の文字を削る
                displayMsg_row1 = text_row1[i+1:]
                if stream2Row: # 2行目も必要なら
                    displayMsg_row2 = text_row2[i+1:]

                # 更新周期だけ待機(0.3sがちょうど良い？)
                time.sleep(streamTime_s)

            # 文字数分のスライドが終わったら元の順番に戻して表示
            self.print_row12(text_row1, text_row2)

        print_onDebug("文字流し完了")
```

ここで、次のヘルパーメソッドを導入しています。
- print_row12(self, text_row1, text_row2)
LCDの1,2行目に引数の文字列を表示する。


### ■動作確認・テスト
では、上で実装したprint_Streamメソッドの動作確認をしてみましょう。
動作確認用のテストコードをこんな感じで作成してみました。

```python:python
# LCD動作確認（流れ文字表示）
def test_Stream():
        lcd = None
        try:
            # GPIOピン番号は実際の接続に合わせて変更してください
            lcd = LCD1602A(rs=17, e=8, d4=25, d5=24, d6=23, d7=18)

            text1 = "(>_<)*,'*,(o_o)*,'*,ﾁｬﾝﾄｽﾄﾘｰﾑ"
            text2 = "*,'*,(T_T)*,'*,(^_^)ｼﾃﾀ?"
            lcd.print_Stream(text1, text2, True)
            
            print("Press Enter to exit...")
            while True:
                # 標準入力に何か入力があるかチェック
                if select.select([sys.stdin,], [], [], 0.0)[0]:
                    # 入力があればループを抜ける
                    break

                time.sleep(0.1)  # CPU使用率を下げるための短い待機
        
        except Exception as e:
            print(f"エラーが発生しました: {e}")
        finally:
            if lcd:
                lcd.clear()
                lcd.print("Goodbye!")
                time.sleep(2)  # "Goodbye!"を2秒間表示
                lcd.close()
            print("プログラムを終了します。")
```
配線は[前記事](https://qiita.com/ShibesSaico0401/items/2087ea0c3c7891d225c4)のままなので省略！！

プログラムを実行して、LCDに
 __(>_<)*,'*,(o_o)*,'*,ﾁｬﾝﾄｽﾄﾘｰﾑ__
 __*,'*,(T_T)*,'*,(^_^)ｼﾃﾀ?__
 が流れて表示されれば成功です。
 
 さてさて、LCDの表示内容はどうなってるでしょうか…

![IMG_0812(2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/c627fec2-1742-bafb-b975-20706ee7ad8e.gif)


バッチリですね！
文字が右から左へ流れていきます！:v_tone2:

なお、後の並列化処理実装を見越して、print_Streamメソッドは以下の引数を取るようにしています。

```python:
stream2Row      - 2行目の流し表示機能ON/OFF
streamTime_s    - 文字の流れる速度
lockTime_Disp_s - 文字が流れ始める初めの固定時間
```

# まとめ
LCD1602Aに長文を流し表示してくれる機能を実装しました！
単純な仕組みですが、思った通りの動きを実現できると嬉しくなりますね:vulcan::laughing::v:
これで長めのメッセージを表示したい場合に対応することができるようになりました！


# 補足（課題2）
Eピンの立ち上げ/下げのタイミングが少しおかしい部分は以下のように修正したところ、問題なく動作しました。
~~（前から問題なく動作してたんですけどね）~~
```python:
    # 文字データ送信
    def write_four_bits(self, data):
        # E立ち上げ後、データ送信
        self.E.on()
        self.D4.value = bool(data & 0x01)
        self.D5.value = bool(data & 0x02)
        self.D6.value = bool(data & 0x04)
        self.D7.value = bool(data & 0x08)
        
        # Eパルス幅の最小値x1.5倍の時間待機
        time.sleep(2.1e-7)
        self.E.off()
        time.sleep(0.0005)
```
逆になんで動いていたのか不思議ですが、ハードウェア的にはEピンのタイミングは関係ないんですかね…:thinking:
改めてタイミングチャートを見てもよくわからないです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3838080/de0d4df3-0a62-b864-c641-b97653ecac13.png)

アセンブラやスタートアップルーチンなども含め、もっとハード寄りな知識も勉強していかねばとヒシヒシ感じます。


# 今後
1. ASCII以外の文字に対応する
2. ~~write_four_bitsのEピン立ち上げ順序を修正~~
3. ~~長文の表示機能を追加~~
4. 部品モジュールとして使用する際の処理・API追加

今回、課題の2と3が解決されたので、残る課題は1と4のみ！
1と4はそれぞれ小さな記事として近日中に上げる予定です。


# 最後に
本日も乱文でありましたが、ご覧いただきありがとうございました！
これからも組み込みやIoTに関する記事を投稿していく予定ですので、よろしくお願いいたします。:blush:


# やらかし
実際にコードを実行された方はお気づきかと思いますが、現状のプログラムではLCDにASCIIコードに対応したアルファベット（+一部記号）しか表示できないはずなのに、 __流し表示している文字にはカタカナが含まれています。__ 
実は現場ではカタカナ表示機能も続けて実装していた故に、両方の機能を同時に動作確認したプログラムを実行してしまったのでした…ごめんなさい！！:innocent:

というわけで早急に __カタカナ表示機能__ を実装する記事を作成中です！
どうぞお楽しみに！