前回に続き、今回はロータリーエンコーダーを2個にしてみました。

複数のロータリーエンコーダーの同時読み取りは、switch-case分で対応出来ました。

その対応と言っても、人の指で2個のロータリーエンコーダーを普通にクルクル回す速さでの事です。モーターなどで機械的に高速で回した場合には対応出来ないかも。

「尚、私はロータリーエンコーダーを読み取るソースコードを書くのは始めたばかりだし、C言語も素人で、しかも初心者なので凄いソースコードは書けません。悪しからず」

この記事のソースコードや考えに誤り、非効率、プロならこんなソースコードは書かないとか、あるでしょうから先に言って置きます。

 

↓ 白色(PETG)の造形物は、私がOnshapeで設計、Ender-3 V3 SEで造形しました。

今は電子工作に3Dプリンターは、半田ごてくらいに必需品だと思う。無いなら自分で作ればいい。電子工作もその考えだし。3DCAD設計も楽しいしね。PC画面の3Dが現物になるんだから凄い。

3D model-viewer

 

 

↑↓ central_line の上下は0、x_speed(グラフ描画間隔)は1のロータリーエンコーダーで変化させる。

x_speedの値は待機時間。つまり、グラフ描画(左端から右端までの描画)後に、待機時間を設けて次のグラフ描画となる。オシロスコープで言う時間軸の変化みたいなもの。

 

 

↓ この関数で複数(2個)のロータリーエンコーダーの読み取りを行います。

void lxs_gpio_callback(uint gpio, uint32_t events)

↓ 次のソースコードは一部分ですが、core1で行うロータリーエンコーダーの読み取りに関係します。

// コールバック関数
// 1つ又は複数のロータリーエンコーダーを同時に回した時
// (割り込みが発生した時)に呼ばれる関数
// gpio の値でどのロータリーエンコーダーなのか特定出来る
void lxs_gpio_callback(uint gpio, uint32_t events) {

    int lxs_clk;

    // この関数実行中のみ点灯させて
    // ロータリーエンコーダーの
    // 立ち上がりや立ち下がりに
    // 発生する割り込みを確認するLED
    gpio_put(5, 1); // GP5のLED点灯

    // 割り込みの禁止
    gpio_set_irq_enabled(gpio, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, false);

    sleep_ms(3); // 0.003秒 念の為のチャタリング対策

    switch (gpio) {

        // DT0 の割り込み ///////////////////////
        case PIN_DT0: // 0のロータリーエンコーダー

            // CLK0の状態を取得
            lxs_clk = gpio_get(PIN_CLK0);

            switch (events) {

                // DT0 の立ち上がり割り込み
                case GPIO_IRQ_EDGE_RISE:        // 0x8u
                    if (lxs_clk == 1) {         // #A 右回り
                        central_line--;
                    } else if (lxs_clk == 0) {  // #C 左回り
                        central_line++;
                    }
                    break;

                // DT0 の立ち下がり割り込み
                case GPIO_IRQ_EDGE_FALL:        // 0x4u
                    if (lxs_clk == 0) {         // #B 右回り
                        central_line--;
                    } else if (lxs_clk == 1) {  // #D 左回り
                        central_line++;
                    }
                    break;

                // evetes値が12(0x0Cかな)とか意味不明の場合
                default:
                    central_line = central_line;
                    break;
            }
            break;

        // DT1 の割り込み ///////////////////////
        case PIN_DT1: // 1のロータリーエンコーダー
        
            // CLK1の状態を取得
            lxs_clk = gpio_get(PIN_CLK1);

            switch (events) {

                // DT1 の立ち上がり割り込み
                case GPIO_IRQ_EDGE_RISE:        // 0x8u
                    if (lxs_clk == 1) {         // #A 右回り
                        x_speed = x_speed + 10; // +10μ秒(+0.00001秒)
                    } else if (lxs_clk == 0) {  // #C 左回り
                        x_speed = x_speed - 10;
                    }
                    break;

                // DT1 の立ち下がり割り込み
                case GPIO_IRQ_EDGE_FALL:        // 0x4u
                    if (lxs_clk == 0) {         // #B 右回り
                        x_speed = x_speed + 10;
                    } else if (lxs_clk == 1) {  // #D 左回り
                        x_speed = x_speed - 10;
                    }
                    break;

                // events値が12(0x0Cかな)とか意味不明の場合
                default:
                    x_speed = x_speed;
                    break;
            }
            break;

        default: // gpio の値が想定外の時
            central_line = 45; // 初期値
            x_speed = 920; // 初期値
            break;
    }

    // 割り込み禁止の解除
    gpio_set_irq_enabled(gpio, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true);

    gpio_put(5, 0); // GP5のLED消灯
}

void core1_entry() {

    const uint LED_PIN = 25;
    gpio_init(LED_PIN);
    gpio_set_dir(LED_PIN, GPIO_OUT);

    // GPIOの初期化(ロータリーエンコーダー用)
    gpio_init(PIN_CLK0); // GP4
    gpio_init(PIN_DT0); // GP3
    gpio_init(PIN_SW0); // GP2
    gpio_init(PIN_CLK1); // GP12
    gpio_init(PIN_DT1); // GP11
    gpio_init(PIN_SW1); // GP10

    // 立ち上がり、立ち下がりの割り込み確認用LED
    gpio_init(5); // GP5

    // 入出力設定
    gpio_set_dir(PIN_CLK0, GPIO_IN);
    gpio_set_dir(PIN_DT0, GPIO_IN);
    gpio_set_dir(PIN_SW0, GPIO_IN);
    gpio_set_dir(PIN_CLK1, GPIO_IN);
    gpio_set_dir(PIN_DT1, GPIO_IN);
    gpio_set_dir(PIN_SW1, GPIO_IN);
    gpio_set_dir(5, GPIO_OUT);

    // 内部プルダウン設定
    gpio_pull_down(PIN_CLK0);
    gpio_pull_down(PIN_DT0);
    gpio_pull_down(PIN_SW0);
    gpio_pull_down(PIN_CLK1);
    gpio_pull_down(PIN_DT1);
    gpio_pull_down(PIN_SW1);

    // ヒステレリス設定(シュミットトリガ利用)念の為のチャタリング対策
    gpio_set_input_hysteresis_enabled(PIN_CLK0, true);
    gpio_set_input_hysteresis_enabled(PIN_DT0, true);
    gpio_set_input_hysteresis_enabled(PIN_SW0, true);
    gpio_set_input_hysteresis_enabled(PIN_CLK1, true);
    gpio_set_input_hysteresis_enabled(PIN_DT1, true);
    gpio_set_input_hysteresis_enabled(PIN_SW1, true);

    // 割り込み用コールバック関数の登録
    gpio_set_irq_enabled_with_callback(PIN_DT0, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true, &lxs_gpio_callback);
    gpio_set_irq_enabled_with_callback(PIN_DT1, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true, &lxs_gpio_callback);

    // 割り込み禁止の解除
    gpio_set_irq_enabled(PIN_DT0, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true);
    gpio_set_irq_enabled(PIN_DT1, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true);

    // 基板実装LED(GP25)の点灯用ループ
    while (true) {
        if (gpio_get(PIN_CLK0) == 1) {
            gpio_put(LED_PIN, 1);
            //sleep_ms(2);
        } else {
            gpio_put(LED_PIN, 0);
            //sleep_ms(2);
        }
    }
}

 

まぁ〜、このソースコード、C言語初心者の私にとっては趣味に使える。

でも、以下の事が、今後解決したらいいと思う。

 

【理解不能】

↓ 上記ソースコードの一部で、自分で書いて置きながら、私には理解出来ない事がある。割り込み用コールバック関数の登録で、ロータリーエンコーダーが2個だから2つ書いているけど、1つだけでも同様に動作する。なんで?

// 割り込み用コールバック関数の登録
gpio_set_irq_enabled_with_callback(PIN_DT0, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true, &lxs_gpio_callback);
gpio_set_irq_enabled_with_callback(PIN_DT1, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true, &lxs_gpio_callback);

 

【意味不明】

上記の理解不能な件に加えて、コールバック関数内で使用するeventsの値が意味不明なのがある。
それは、ロータリーエンコーダーを回している時(1クリックごとに)、私が想定するeventsの値は0x4uと0x8uなんだけど、12(多分0x0Cuかな)が頻繁に発生する。
この12とは? なに? エラーとかファールのコード?
ググり方が不味いのか、ググっても解決していない。

これは仕方ないので、その部分のソースコードで想定外の値の時は、ロータリーエンコーダーを回しても(1クリック分だけ)変化しない事にした。↓

// evetes値が12(0x0Cかな)とか意味不明の場合
default:
    central_line = central_line;
    break;
// events値が12(0x0Cかな)とか意味不明の場合
default:
    x_speed = x_speed;
    break;

AmazonでポチったロータリーエンコーダーHW-040を、Raspberry Pi Picoの割り込みとC言語で読み取る記事。ただ私は素人なので誤りが有るかも知れません。

開発環境

VSCode C/C++ Windows 11
RP2040 Raspberry Pi Pico

 

 

↓ 目標はロータリーエンコーダーでcentral_lineを上下に動かす事。

 

↓ DTの立ち上がり割り込み、又は立ち下がり割り込みの時、CLKの値を読み取り右回り、又は左回りを判断する。尚、画像中の#A、#B、#C、#Dは、ソースコードにあるそれと一致します。

 

↓ ロータリーエンコーダーの読み取りはcore1で行い、ADCとOLEDの表示はcore0で行う。次のコードはcore1で処理する部分で、ロータリーエンコーダーの読み取りをGPIOの割り込みで処理します。尚、ソースコード中の#A、#B、#C、#Dは上記画像のそれと一致します。

// コールバック関数
// ロータリーエンコーダーを回した時(割り込み発生時)に呼ばれる関数
void lxs_gpio_callback(uint gpio, uint32_t events) {

    int lxs_clk;

    // この関数実行中のみ点灯させて
    // ロータリーエンコーダーの
    // 立ち上がり
    // 立ち下がり を確認する為のLED
    gpio_put(5, 1); // GP5のLED点灯

    // 割り込みの禁止
    gpio_set_irq_enabled(gpio, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, false);

    sleep_ms(3); // 0.003秒 念の為のチャタリング対策

    // CLKの状態を取得
    lxs_clk = gpio_get(PIN_CLK);

    // DTの立ち上がり割り込み
    if (gpio == PIN_DT && events == GPIO_IRQ_EDGE_RISE) {
        if (lxs_clk == 1) {         // #A
            central_line--;
        } else if (lxs_clk == 0) {  // #C
            central_line++;
        }

    // DTの立ち下がり割り込み
    } else if (gpio == PIN_DT && events == GPIO_IRQ_EDGE_FALL) {
        if (lxs_clk == 0) {         // #B
            central_line--;
        } else if (lxs_clk == 1) {  // #D
            central_line++;
        }
    }

    // 割り込み禁止の解除
    gpio_set_irq_enabled(gpio, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true);

    gpio_put(5, 0); // GP5のLED消灯
}

void core1_entry() {

    const uint LED_PIN = 25;
    gpio_init(LED_PIN);
    gpio_set_dir(LED_PIN, GPIO_OUT);

    // GPIOの初期化(ロータリーエンコーダー用)
    gpio_init(PIN_CLK); // GP4
    gpio_init(PIN_DT); // GP3
    gpio_init(PIN_SW); // GP2

    // 立ち上がり、立ち下がりの割り込み確認用LED
    gpio_init(5); // GP5

    // 入出力設定
    gpio_set_dir(PIN_CLK, GPIO_IN);
    gpio_set_dir(PIN_DT, GPIO_IN);
    gpio_set_dir(PIN_SW, GPIO_IN);
    gpio_set_dir(5, GPIO_OUT);

    // 内部プルダウン設定
    gpio_pull_down(PIN_CLK);
    gpio_pull_down(PIN_DT);
    gpio_pull_down(PIN_SW);

    // ヒステレリス設定(シュミットトリガ利用)念の為のチャタリング対策
    gpio_set_input_hysteresis_enabled(PIN_CLK, true);
    gpio_set_input_hysteresis_enabled(PIN_DT, true);
    gpio_set_input_hysteresis_enabled(PIN_SW, true);

    // 割り込み用コールバック関数の登録
    gpio_set_irq_enabled_with_callback(PIN_DT, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true, &lxs_gpio_callback);

    // 割り込み禁止の解除
    gpio_set_irq_enabled(PIN_DT, GPIO_IRQ_EDGE_FALL | GPIO_IRQ_EDGE_RISE, true);

    // 基板実装LED(GP25)の点灯用ループ
    while (true) {
        if (gpio_get(PIN_CLK) == 1) {
            gpio_put(LED_PIN, 1);
        } else {
            gpio_put(LED_PIN, 0);
        }
    }
}

 

↓ GP5に繋げた緑色のLEDは、ロータリーエンコーダーを回した時に呼ばれる関数 lxs_gpio_callback() の実行中だけ点灯。立ち上がり、立ち下がりの割り込みをLEDの点灯で分かる仕組み。

2021年2月15日投稿 Pi PicoのADCで電圧表示

 

↑ 以前作ったラズパイPicoのプログラムを、Windows11にインストールしたPico用VSCode C/C++開発環境にコピペする。

以前のCMakeLists.txtでは駄目で、エラーやファールばかりを繰り返して大変でした。そもそも私はズブの素人、CMakeLists.txtの役目を知らずにやっているのですから、本当に大変でした。
でも、最終的にエラーやファール、警告も無く正常にuf2ファイルが出来たので、忘れない内にその方法を残します。

どの様なプログラムかと言うと、次の画像がそのプログラムの実行中の様子で、ADC0の電圧をADCで測定しSSD1331に表示する。と言う事をCore0で無限ループする。それに加え並列してCore1でLEDの点滅をさせています。

 

環境作りは以下のリンクから pico-setup-windows-0.3.5-x64.exe をダウンロードして行う。

pico-setup-windows

VSCodeの立ち上げは、必ず Visual Studio Code for Pico のショートカットを使用する。このショートカットはドキュメントフォルダに作られたPicoフォルダの中にある。

↓ New C/C++ Projectをクリック。

 

↓ New Pico Project
Name SSD1331_test
Board type Pico
Location c:\Users\JM1LXS\Documents\LXS_Pico
Select Pico SDK version v2.1.0
Features spi
Stdio support Console over USB
Code generation options Use project name as entry point file name
Generate C++ code
Debugger DebugProbe

 

↓ CMakeLists.txtに追記

hardware_adc
pico_multicore
pico_bootsel_via_double_reset

↑ pico_bootsel_via_double_reset は、リセットボタンをダブルクリックするとBOOTSELモードになる。なのでUSBケーブルの抜き差しは不要。

 

次は、Cソースファイルの内容を丸ごと入れ替える。

以前も今回もソースファイルは1つだけ。

以前のソースファイル pico_ssd1331_oled.c
今回のソースファイル SSD1331_test.cpp

SSD1331_test.cppの中身は削除して空にする。以前のpico_ssd1331_oled.cの中身をコピーしてSSD1331_test.cppにペーストする。

font.hはコピーしてSSD1331_testフォルダにペーストする。

 

↓ ビルドが成功して最終的なファイルはこんな感じ。

 

 

この記事で扱ったソースファイルをダウンロード出来ます。

LXS-TEST.zip

尚、SSD1331を初期化するコード lxs_oled_init()内のコマンド 0xAE 0x25から始まるそれは、ネット上で見つけた物で、私には何故そのコマンドなのかなど説明出来ません。
また、font.h内のフォントデータもネットから拝借した物です。

私は素人なので質問されても良い回答が出来ないので悪しからず。

2024年9月6日ごろ、ラズパイ5を sudo apt upgrade したら?
gpiochip4が無い。

ソースコードのchipname部分をgpiochip4からgpiochip0に変更しました。

const char *chipname = “gpiochip4”;

const char *chipname = “gpiochip0”;

 

gpioinfo で確認すると ↓

アップグレード前のgpiochip0は32linesだけど、アップグレード後は54linesになっている。これはgpiochip4と同じ数。

 

2024年9月7日上記追記。以下、元記事のまま変更していません。ソースコードは上記を参考に変更して下さい。

 

外国のYouTuberの動画と、そこからのリンク先のページが参考になります。

外国のYouTuberの動画
Use Raspberry Pi 5 GPIO – Push button input – LED output – ChatGTP for code – GPIOD [PART 2 OF 2]

リンク先のページ
GPIO Programming: Exploring the libgpiod Library

 

以下、素人の私が書く事ですので、何処かに誤りがあるかも知れません。C言語のソースコードなのに、拡張子がCPPである事を指摘しないで下さい。それに、私本人は以下のプログラムが動くので、取り敢えずこれで良しと思っています。それを承知の上で参考にして下さい。

 

ラズパイ5のGPIOはgpiodで使うみたい。ラズパイ4まではpigpioでLチカ出来ていた。pigpioを使用したソースコードでもラズパイ5でコンパイルは成功する。だけど、そのソフトを走らせると不動作で「ラズパイでは無いようです」みたいに言われてしまう。

↓ この画像は、ラズパイ5にgpiod等をインストールして、/dev/gpiochip4を利用してLチカしている所です。

 

↓ C言語でGPIOを使用したい時に必要。

$ sudo apt install gpiod libgpiod2 libgpiod-dev

 

↓ chronyc sources

↑ PPSはこのラズパイ5。pi4やzero2にpi2は同じLAN内のラズパイで、どれもNTPサーバーです。PPSのラズパイ5、pi4、zero2の3つのラズパイは各々に繋がれたGPSの1PPSに同期中。pi2はLAN内のNTPサーバーを参照しています。

↓ 3つのLEDをchronyc sourcesの結果に応じて点灯させるプログラムのソースコード。

// Raspberry Pi 5
// pi@pi5:~ $ g++ -Wall -o chronyCheck chronyCheck.cpp -l gpiod

#include <err.h>
#include <cstring>
#include <iostream>
#include <gpiod.h>
#include <unistd.h>
#include <stdio.h>

#define BUF 256
#define H 1
#define L 0
#define PPS 10
#define NTP 9
#define NG 11

using namespace std;

enum kekka{
    pps_hantei = 10,
    pps_ntp_hantei,
    ntp_hantei,
    ng_hantei
};

int mainLoop(void);

int main(void)
{
    if(daemon(0, 0) == 0) { // デーモン関数
        mainLoop();
    } else {
        cout << "error" << endl;
    }
    return 0;
}

int mainLoop(void)
{
    FILE *fp;
    char *ret;
    char str[512], *ptr;
    const char* cmdline = "chronyc sources";
    
    enum kekka hantei;
    
    gpiod_chip *chip;
    gpiod_line *line_PPS, *line_NTP, *line_NG;
    //For Raspberry Pi 5 use gpiochip4 (For Raspberry Pi 4 use gpiochip0)
    const char *chipname = "gpiochip4";
        
    // Open GPIO chip
    chip = gpiod_chip_open_by_name(chipname);

    // Open GPIO lines
    line_PPS = gpiod_chip_get_line(chip, PPS);
    line_NTP = gpiod_chip_get_line(chip, NTP);
    line_NG = gpiod_chip_get_line(chip, NG);

    // Open LED lines for output
    gpiod_line_request_output(line_PPS, "chronyCheck", 0);
    gpiod_line_request_output(line_NTP, "chronyCheck", 0);
    gpiod_line_request_output(line_NG, "chronyCheck", 0);
    
   
    // 無限ループ
    while (1) {
        
        // コマンドの chronyc sources 実行
        if ((fp=popen(cmdline,"r")) == NULL) {
            err(EXIT_FAILURE, "%s", cmdline);
        }
        
        // chronyc sources 実行結果を1行づつチェックする
        // それを必要なら最後の行まで繰り返す。
        do {
            ret = fgets(str, BUF, fp); // 1行読み取り
            ptr = strstr(str, " 377 "); //  377 を検索(377前後のスペースも含む)
            if (ptr != NULL) {
                ptr = strstr(str, "#*"); // #* を検索
                if (ptr != NULL) {       // 検索行に #* と 377 があった時だけ1PPS同期判定とする
                    hantei = pps_hantei;
                    // cout << "Synchronizing to 1PPS & 377" << endl;
                    ret = NULL; // ここに来たらdo-whileから抜けて1行読み取りを終了する
                } else {
                    ptr = strstr(str, "^*");
                    if (ptr != NULL) {
                        hantei = ntp_hantei;
                        // cout << "Synchronizing to NTP" << endl;
                        ret = NULL; // ここに来たらdo-whileから抜けて1行読み取りを終了する
                    } else {
                        ptr = strstr(str, "^+");
                        if (ptr != NULL) {
                            hantei = ntp_hantei;
                            // cout << "Synchronizing to NTP" << endl;
                            ret = NULL; // ここに来たらdo-whileから抜けて1行読み取りを終了する
                        } else {
                            ptr = strstr(str, "^-");
                            if (ptr != NULL) {
                                hantei = ntp_hantei;
                                // cout << "Synchronizing to NTP" << endl;
                                ret = NULL; // ここに来たらdo-whileから抜けて1行読み取りを終了する
                            } else {
                                hantei = ng_hantei; // ここに来た場合は、1PPSにもNTPにも同期せず
                                // cout << "NG" << endl;
                            }
                        }
                    }
                }
            } else {
                ptr = strstr(str, "#*");
                if (ptr != NULL) {
                    hantei = pps_ntp_hantei; // 377は無いが #* の場合
                    ret = NULL;
                } else {
                    ptr = strstr(str, "^*");
                    if (ptr != NULL) {
                        hantei = ntp_hantei;
                        ret = NULL;
                    } else {
                        hantei = ng_hantei;
                    }
                }
            }
        } while (ret != NULL);
    
        // 全LEDを消灯
        gpiod_line_set_value(line_PPS, L);
        gpiod_line_set_value(line_NTP, L);
        gpiod_line_set_value(line_NG, L);
    
        // hanteiに応じてLEDを点灯
        switch (hantei) {
            case pps_hantei:
                gpiod_line_set_value(line_PPS, H);
                sleep(70); // PPS同期判定時は70秒待機
                break;
            case pps_ntp_hantei: // PPS? NTP? どっちの時は、PPS用LED、NTP用LED両方とも点灯
                gpiod_line_set_value(line_PPS, H);
                gpiod_line_set_value(line_NTP, H);
                sleep(16); // PPS? NTP? 同期判定時は16秒待機
                break;
            case ntp_hantei:
                gpiod_line_set_value(line_NTP, H);
                sleep(16); // NTP同期判定時は16秒待機
                break;
            case ng_hantei:
                gpiod_line_set_value(line_NG, H);
                sleep(4); // NG判定時は4秒待機
                break;
            default: // ここに来るはず無いので意味合いとしては異常表示
                gpiod_line_set_value(line_PPS, H);
                gpiod_line_set_value(line_NTP, H);
                gpiod_line_set_value(line_NG, H);   
        }

        pclose(fp);
        
    }

    // 以下は無限ループ外なので不要?
    gpiod_line_release(line_PPS);
    gpiod_line_release(line_NTP);
    gpiod_line_release(line_NG);
    gpiod_chip_close(chip);
    
    return 0;
}

↑ デーモン関数はOS起動と共に起動して常に作動する為の物。それにはユニットファイル作成等も必要だけど。ここには記載していません。

コンパイル

$ g++ -Wall -o chronyCheck chronyCheck.cpp -l gpiod

OLEDのクリア方法を代えてパラパラを無くしました。

環境はこちらの別サイトに習い整えました。
Raspberry Pi: OLED-Display 128×64 mit Python ansteuern (I2C)

 

ここでは、上記環境下での画面クリアの違いを動画で見比べます。

動画前半(改善前)→ oled.cls()
動画後半(改善後)→ draw.rectangle((0, 0, 127, 63), outline=0, fill=0)

 

↓ 動画前半がパラパラした(瞬きする様な)表示になってしまっています。後半からはパラパラせず普通に見やすい表示器です。

 

↓ 動画後半のPythonコードで、無限ループする描画に関した部分です。

#oled.cls()
draw.rectangle((0, 0, 127, 63), outline=0, fill=0)
# ↑ oled.cls()の代わりに画面全体を黒色の四角で塗りつぶす。

draw.text((0, 0), data1, fill=1)
draw.text((0, 17), f'{data2:>6.1f}', font=DejaVuSansMono14, fill=1)
draw.text((0, 34), f'{data3:>6.1f}', font=DejaVuSansMono14, fill=1)
draw.text((0, 51), f'{press:>6.1f}', font=DejaVuSansMono14, fill=1)
    
draw.text((55, 17), "C", font=FreeSerif14, fill=1)
draw.text((55, 34), "%", font=FreeSerif14, fill=1)
draw.text((55, 51), "hPa", font=FreeSerif14, fill=1)

oled.display()

time.sleep(1)

oled.cls()の代わりにdraw.rectangleで画面全体を黒色の四角で塗りつぶし、それから必要な描画設定をして最後にoled.display()で表示させます。