【RaspberryPi】OLED (SH1106HAT)を使う

it

単色OLED SH1106HATを使ってみました。
Waveshare製のRaspberry Pi HATで、RaspberryPiのピンヘッダと合体して使うことができます。
RaspberryPi上で動作するプログラムをAI生成して表示制御した内容を記事にしました。

OLED SH1106HAT

簡単紹介

Raspberry Pi 4B, 5B, ZERO シリーズのピンヘッダに直接合体接続できる。
Arduino環境にもモジュールライブラリにより簡単に使うことができる。

メーカー紹介 : 1.3inch OLED display HAT for Raspberry Pi, 128×64 pixels, SPI/I2C interface

◆関連記事

ピン配置

外観

使ってみた

◆まとめ

Raspberry Pi と配線不要で使えるので手軽。
ただしスペーサの使えるRaspberry Pi ZERO系のほうが固定しやすい。
表示ライブラリが少ないので、使い初めに苦労する。

◆開発環境

Raspberry Pi 4B Version12 bookworm 64bit
開発環境は Geany Programmer’s Editor
ライブラリにWiringPi Version3.16

◆使ってみた

AIに簡単なライブラリを作ってもらいました。
表示したした様子はこちらです。
RaspberryPi のピンヘッダからブレークアウト基板を使ってブレッドボード上に配線しています。

Arduino環境のライブラリのような手軽さはありませんが、現在ではAIを使ったコード生成により簡単なプログラムがデータシートを見なくても作れてしまいます。

なお、Arduino環境で使った様子はこちらです。
今回は使えることが分かっただけでこちらは掘り下げませんが、Arduino環境でライブラリを使って開発するのはとてもスピーディです。

◆合体

RaspberryPi ZeroとSH1106HATを合体させました。
表示しているところ撮りたかったのですが、用意に手間がかかるので省略します。

樹脂のスペーサは必須です。
ピンヘッダだけでも保持はしますが軽い衝撃でグラつきます。
ボタンのクリック感は固めなのでピンヘッダ保持では押せません。

こちらはRaspberry Pi 4Bと合体する様子です。
普段は冷却ファン付き筐体で使っていますが、ファンがSH1106HATと干渉するのでファンを外さないと使えません。
筐体を使うならアルミ筐体で熱を逃がすタイプにするなどの工夫が必要です。

Raspberry Pi 4Bと合体した図です。
スペーサで固定することができないためピンヘッダだけで保持しています。
ボタンが固いので手で支えながらボタンを押しています。                                             

◆制御信号

SPIとI2Cを使えると記載があります。
デフォルトはSPIです。
I2Cにするには、背面チップ抵抗を指定状態に移し替える作業をします。
チップ抵抗をはがして付け直す作業なので高い半田技術が必要です。

I2Cにして試してみましたが表示されず。
後から思えばRST信号を制御することで使えたかもしれませんが、気が向いたらリトライしてみます。

ジャンパワイヤなどで配置をするならI2Cのほうが配線数が少なく済みますが、HATなのでピンヘッダに直接合体となると作業性の違いはありません。

◆十字キー、ボタン

クリック感固めです。
SH1106HATがしっかり固定されていないと押せません。

十字キーには上下左右の他プッシュボタンにもなります。

準備

以下記事によりRaspberry Pi 、WiringPiのセットアップを行います。

使い方

SH1106HATに図形、文字、画像を表示

説明

RaspberryPi からSH1106HATの表示制御します。

画面淵を矩形で囲み、■TAMANEGI BLOG 2026と当サイトアイコンを表示します。

機能は
・SH1106の初期化
・表示方向の設定(デフォルト 0° / 180°回転)
・バッファクリア(画面クリア)
・ドット
・線
・矩形
・塗りつぶし矩形
・円
・ビットマップ
・文字列表示

スケッチ

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <wiringPi.h>
#include <wiringPiSPI.h>
#include <stdarg.h>

#include "font5x7.h"

#define M_WIDTH     (132)
#define M_HEIGHT    (64)

#define V_WIDTH     (128)
#define V_HEIGHT    (64)

#define SPI_DC      (24)
#define SPI_RST     (25)

#define SPI_CHANNEL (0)
#define SPI_SPEED   (8000000)

typedef enum {
    ROTATE_0,      // default
    ROTATE_180,    // bottom up
} DisplayRotation;

unsigned char buffer[M_WIDTH * M_HEIGHT / 8];
static const unsigned char  logo_bmp[] =
{
0x00, 0x00, 0x00, 0x00,    //0
0x00, 0x00, 0x00, 0x00,    //1
0x00, 0x00, 0x00, 0x00,    //2
0x00, 0x00, 0x00, 0x00,    //3
0x00, 0x00, 0x06, 0x00,    //4
0x00, 0x00, 0x1B, 0xA0,    //5
0x00, 0x00, 0x30, 0x70,    //6
0x00, 0x00, 0xE0, 0x30,    //7
0x00, 0x7F, 0x80, 0x30,    //8
0x01, 0xC0, 0x00, 0x10,    //9
0x03, 0x70, 0x07, 0x98,    //10
0x0F, 0xFC, 0x1C, 0x8C,    //11
0x19, 0x3C, 0x30, 0x44,    //12
0x12, 0x7E, 0x78, 0x46,    //13
0x32, 0xF2, 0xF8, 0x42,    //14
0x23, 0xA2, 0xCC, 0x83,    //15
0x21, 0xF6, 0xDD, 0x81,    //16
0xE0, 0x7E, 0xFF, 0x01,    //17
0x70, 0x38, 0xFC, 0x71,    //18
0x4F, 0x00, 0x07, 0xF1,    //19
0x61, 0xFF, 0xFC, 0x21,    //20
0x20, 0x00, 0x00, 0x61,    //21
0x30, 0xE0, 0x01, 0x83,    //22
0x18, 0x3F, 0xFE, 0x06,    //23
0x0C, 0x00, 0x00, 0x0C,    //24
0x03, 0xF8, 0x00, 0xF0,    //25
0x00, 0x0F, 0x7F, 0xC0,    //26
0x00, 0x00, 0x00, 0x00,    //27
0x00, 0x00, 0x00, 0x00,    //28
0x00, 0x00, 0x00, 0x00,    //29
0x00, 0x00, 0x00, 0x00,    //30
0x00, 0x00, 0x00, 0x00,    //31
};

void spi_send(uint8_t *data, int len);
void sh1106_command(uint8_t cmd);
void sh1106_data(uint8_t *data, int len);
void sh1106_init();
void sh1106_update();
void sh1106_clear();
void drawPixel(int x, int y, int color);
void drawBitmap(int x, int y, const unsigned char *bitmap, int w, int h, int color);

//--------------------------------------
// SPI 送信
//--------------------------------------
void spi_send(uint8_t *data, int len) {
    wiringPiSPIDataRW(SPI_CHANNEL, data, len);
}

//--------------------------------------
// コマンド送信
//--------------------------------------
void sh1106_command(uint8_t cmd) {
    digitalWrite(SPI_DC, LOW);
    spi_send(&cmd, 1);
}

//--------------------------------------
// データ送信
//--------------------------------------
void sh1106_data(uint8_t *data, int len) {
    digitalWrite(SPI_DC, HIGH);
    spi_send(data, len);
}

void sh1106_setRotation(DisplayRotation rot) {

    switch (rot) {

        case ROTATE_0:
            sh1106_command(0xA1); // SEG normal
            sh1106_command(0xC8); // COM normal
            break;

        case ROTATE_180:
            sh1106_command(0xA0); // SEG reverse
            sh1106_command(0xC0); // COM reverse
            break;

    }
}

//--------------------------------------
// sh1106 初期化
//--------------------------------------
void sh1106_init() {

    // Reset
    digitalWrite(SPI_RST, LOW);
    usleep(50000);
    digitalWrite(SPI_RST, HIGH);
    usleep(50000);

    sh1106_command(0xAE);
    sh1106_command(0xD5); sh1106_command(0x80);
    sh1106_command(0xA8); sh1106_command(0x3F);
    sh1106_command(0xD3); sh1106_command(0x00);
    sh1106_command(0x40);
    sh1106_command(0x8D); sh1106_command(0x14);
    sh1106_command(0x20); sh1106_command(0x00);
    sh1106_command(0xA1);
    sh1106_command(0xC8);
    sh1106_command(0xDA); sh1106_command(0x12);
    sh1106_command(0x81); sh1106_command(0x7F);
    sh1106_command(0xD9); sh1106_command(0xF1);
    sh1106_command(0xDB); sh1106_command(0x40);
    sh1106_command(0xA4);
    sh1106_command(0xA6);
    sh1106_command(0xAF);

    sh1106_setRotation(ROTATE_0);

}

//--------------------------------------
// バッファ送信
//--------------------------------------
void sh1106_update() {
    for (int page = 0; page < 8; page++) {
        sh1106_command(0xB0 + page);
        sh1106_command(0x00);
        sh1106_command(0x10);

        uint8_t line[132] = {0};
        memcpy(&line[2], &buffer[page * 128], 128);
        
        sh1106_data(&buffer[page * M_WIDTH], M_WIDTH);
    }
}

//--------------------------------------
// バッファクリア
//--------------------------------------
void sh1106_clear() {
    memset(buffer, 0, sizeof(buffer));
}

//--------------------------------------
// Pixel
//--------------------------------------
void drawPixel(int x, int y, int color) {
    x += 2;
    if (x < 0 || x >= M_WIDTH || y < 0 || y >= M_HEIGHT) return;

    int index = x + (y / 8) * M_WIDTH;

    if (color)
        buffer[index] |= (1 << (y % 8));
    else
        buffer[index] &= ~(1 << (y % 8));
}


//--------------------------------------
// 線(Bresenham)
//--------------------------------------
void drawLine(int x0, int y0, int x1, int y1, int color) {
    int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
    int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
    int err = dx + dy, e2;

    while (1) {
        drawPixel(x0, y0, color);
        if (x0 == x1 && y0 == y1) break;
        e2 = 2 * err;
        if (e2 >= dy) { err += dy; x0 += sx; }
        if (e2 <= dx) { err += dx; y0 += sy; }
    }
}

//--------------------------------------
// 四角(枠)
//--------------------------------------
void drawRect(int x, int y, int w, int h, int color) {
    drawLine(x, y, x + w - 1, y, color);
    drawLine(x, y, x, y + h - 1, color);
    drawLine(x + w - 1, y, x + w - 1, y + h - 1, color);
    drawLine(x, y + h - 1, x + w - 1, y + h - 1, color);
}

//--------------------------------------
// 四角(塗りつぶし)
//--------------------------------------
void fillRect(int x, int y, int w, int h, int color) {
    for (int i = x; i < x + w; i++)
        for (int j = y; j < y + h; j++)
            drawPixel(i, j, color);
}

//--------------------------------------
// 円(枠)
//--------------------------------------
void drawCircle(int x0, int y0, int r, int color) {
    int x = -r, y = 0, err = 2 - 2 * r;

    do {
        drawPixel(x0 - x, y0 + y, color);
        drawPixel(x0 - y, y0 - x, color);
        drawPixel(x0 + x, y0 - y, color);
        drawPixel(x0 + y, y0 + x, color);
        r = err;
        if (r <= y) err += ++y * 2 + 1;
        if (r > x || err > y) err += ++x * 2 + 1;
    } while (x < 0);
}

//--------------------------------------
// Bitmap
//--------------------------------------
void drawBitmap(int x, int y, const unsigned char *bitmap, int w, int h, int color)
{
    int byteWidth = (w + 7) / 8;
    
    for (int j = 0; j < h; j++) {
        for (int i = 0; i < w; i++) {

            int byteIndex = j * byteWidth + (i / 8);
            int bitMask   = 0x80 >> (i & 7);

            if (bitmap[byteIndex] & bitMask) {
                drawPixel(x + i, y + j, color);
            }
        }
    }
}

void drawChar(int x, int y, char c, int color) {
    if (c < 32 || c > 126) return;
    
    const uint8_t *glyph = font5x7[c - 32];

    for (int col = 0; col < 5; col++) {
        uint8_t line = glyph[col];
        for (int row = 0; row < 7; row++) {
            if (line & (1 << row)) {
                drawPixel(x + col, y + row, color);
            }
        }
    }

    // 1列スペース
    for (int row = 0; row < 7; row++) {
        drawPixel(x + 5, y + row, 0);
    }
}

void drawString(int x, int y, const char *str, int color) {
    while (*str) {
        drawChar(x, y, *str, color);
        x += 6;  // 5px + 1px space
        str++;
    }
}


void drawStringF(int x, int y, int color, const char *fmt, ...){
    
    char buf[128];
    va_list args;
    
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);
    
    drawString(x, y, buf, color);
}


//--------------------------------------
// main
//--------------------------------------
int main() {

    wiringPiSetupGpio();

    pinMode(SPI_DC, OUTPUT);
    pinMode(SPI_RST, OUTPUT);

    wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED);

    sh1106_init();
    sh1106_setRotation(ROTATE_0);
    sh1106_clear();

    drawRect(0, 0, V_WIDTH , V_HEIGHT, 1);
    fillRect(2, 2, 5, 5, 1);
    drawStringF(10, 2, 1, "TMANEGI BLOG %d", 2026);
    drawBitmap(10, 20, logo_bmp, 32, 32, 1);

    sh1106_update();

    return 0;
}

結果

単色青色発光品です。
鮮やかな発色できれいな表示です。

十字キー、3ボタンを使う

説明

十字キーの入力、3つのタクトボタンの入力を読み取ります。

十字キーの操作方向に「たまねぎアイコン」が動きます。
ボタンを押している間ボタン横の□が塗りつぶされます。

十字キーにはプッシュボタン機能もありますが、今回含み忘れました。

スケッチ

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <wiringPi.h>
#include <wiringPiSPI.h>
#include <stdarg.h>

#include "font5x7.h"

#define M_WIDTH     (132)
#define M_HEIGHT    (64)

#define V_WIDTH     (128)
#define V_HEIGHT    (64)

// ==== SH1106 HAT (BCM ピン) ====
#define SPI_DC      (24)      // DC
#define SPI_RST     (25)      // RST

#define SPI_CHANNEL (0)
#define SPI_SPEED   (8000000)

// キー(BCM)
#define KEY_UP_PIN      6
#define KEY_DOWN_PIN    19
#define KEY_LEFT_PIN    5
#define KEY_RIGHT_PIN   26
#define KEY_PRESS_PIN   13

#define KEY1_PIN        21
#define KEY2_PIN        20
#define KEY3_PIN        16

typedef enum {
    ROTATE_0,      // default
    ROTATE_180,    // bottom up
} DisplayRotation;

unsigned char buffer[M_WIDTH * M_HEIGHT / 8];

static const unsigned char  logo_bmp[] =
{
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x06, 0x00,
0x00, 0x00, 0x1B, 0xA0,
0x00, 0x00, 0x30, 0x70,
0x00, 0x00, 0xE0, 0x30,
0x00, 0x7F, 0x80, 0x30,
0x01, 0xC0, 0x00, 0x10,
0x03, 0x70, 0x07, 0x98,
0x0F, 0xFC, 0x1C, 0x8C,
0x19, 0x3C, 0x30, 0x44,
0x12, 0x7E, 0x78, 0x46,
0x32, 0xF2, 0xF8, 0x42,
0x23, 0xA2, 0xCC, 0x83,
0x21, 0xF6, 0xDD, 0x81,
0xE0, 0x7E, 0xFF, 0x01,
0x70, 0x38, 0xFC, 0x71,
0x4F, 0x00, 0x07, 0xF1,
0x61, 0xFF, 0xFC, 0x21,
0x20, 0x00, 0x00, 0x61,
0x30, 0xE0, 0x01, 0x83,
0x18, 0x3F, 0xFE, 0x06,
0x0C, 0x00, 0x00, 0x0C,
0x03, 0xF8, 0x00, 0xF0,
0x00, 0x0F, 0x7F, 0xC0,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};

void spi_send(uint8_t *data, int len);
void sh1106_command(uint8_t cmd);
void sh1106_data(uint8_t *data, int len);
void sh1106_init();
void sh1106_update();
void sh1106_clear();
void drawPixel(int x, int y, int color);
void drawBitmap(int x, int y, const unsigned char *bitmap, int w, int h, int color);
void drawLine(int x0, int y0, int x1, int y1, int color);
void drawRect(int x, int y, int w, int h, int color);
void fillRect(int x, int y, int w, int h, int color);
void drawCircle(int x0, int y0, int r, int color);
void drawChar(int x, int y, char c, int color);
void drawString(int x, int y, const char *str, int color);
void drawStringF(int x, int y, int color, const char *fmt, ...);

//--------------------------------------
// SPI 送信
//--------------------------------------
void spi_send(uint8_t *data, int len) {
    wiringPiSPIDataRW(SPI_CHANNEL, data, len);
}

//--------------------------------------
// コマンド送信
//--------------------------------------
void sh1106_command(uint8_t cmd) {
    digitalWrite(SPI_DC, LOW);
    spi_send(&cmd, 1);
}

//--------------------------------------
// データ送信
//--------------------------------------
void sh1106_data(uint8_t *data, int len) {
    digitalWrite(SPI_DC, HIGH);
    spi_send(data, len);
}

void sh1106_setRotation(DisplayRotation rot) {

    switch (rot) {

        case ROTATE_0:
            sh1106_command(0xA1); // SEG normal
            sh1106_command(0xC8); // COM normal
            break;

        case ROTATE_180:
            sh1106_command(0xA0); // SEG reverse
            sh1106_command(0xC0); // COM reverse
            break;

    }
}

//--------------------------------------
// sh1106 初期化
//--------------------------------------
void sh1106_init() {

    // Reset
    digitalWrite(SPI_RST, LOW);
    usleep(50000);
    digitalWrite(SPI_RST, HIGH);
    usleep(50000);

    sh1106_command(0xAE);
    sh1106_command(0xD5); sh1106_command(0x80);
    sh1106_command(0xA8); sh1106_command(0x3F);
    sh1106_command(0xD3); sh1106_command(0x00);
    sh1106_command(0x40);
    sh1106_command(0x8D); sh1106_command(0x14);
    sh1106_command(0x20); sh1106_command(0x00);
    sh1106_command(0xA1);
    sh1106_command(0xC8);
    sh1106_command(0xDA); sh1106_command(0x12);
    sh1106_command(0x81); sh1106_command(0x7F);
    sh1106_command(0xD9); sh1106_command(0xF1);
    sh1106_command(0xDB); sh1106_command(0x40);
    sh1106_command(0xA4);
    sh1106_command(0xA6);
    sh1106_command(0xAF);

    sh1106_setRotation(ROTATE_0);
}

//--------------------------------------
// バッファ送信
//--------------------------------------
void sh1106_update() {
    for (int page = 0; page < 8; page++) {
        sh1106_command(0xB0 + page);
        sh1106_command(0x00);
        sh1106_command(0x10);

        sh1106_data(&buffer[page * M_WIDTH], M_WIDTH);
    }
}

//--------------------------------------
// バッファクリア
//--------------------------------------
void sh1106_clear() {
    memset(buffer, 0, sizeof(buffer));
}

//--------------------------------------
// Pixel
//--------------------------------------
void drawPixel(int x, int y, int color) {
    x += 2;
    if (x < 0 || x >= M_WIDTH || y < 0 || y >= M_HEIGHT) return;

    int index = x + (y / 8) * M_WIDTH;

    if (color)
        buffer[index] |= (1 << (y % 8));
    else
        buffer[index] &= ~(1 << (y % 8));
}

//--------------------------------------
// 線(Bresenham)
//--------------------------------------
void drawLine(int x0, int y0, int x1, int y1, int color) {
    int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
    int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
    int err = dx + dy, e2;

    while (1) {
        drawPixel(x0, y0, color);
        if (x0 == x1 && y0 == y1) break;
        e2 = 2 * err;
        if (e2 >= dy) { err += dy; x0 += sx; }
        if (e2 <= dx) { err += dx; y0 += sy; }
    }
}

//--------------------------------------
// 四角(枠)
//--------------------------------------
void drawRect(int x, int y, int w, int h, int color) {
    drawLine(x, y, x + w - 1, y, color);
    drawLine(x, y, x, y + h - 1, color);
    drawLine(x + w - 1, y, x + w - 1, y + h - 1, color);
    drawLine(x, y + h - 1, x + w - 1, y + h - 1, color);
}

//--------------------------------------
// 四角(塗りつぶし)
//--------------------------------------
void fillRect(int x, int y, int w, int h, int color) {
    for (int i = x; i < x + w; i++)
        for (int j = y; j < y + h; j++)
            drawPixel(i, j, color);
}

//--------------------------------------
// 円(枠)
//--------------------------------------
void drawCircle(int x0, int y0, int r, int color) {
    int x = -r, y = 0, err = 2 - 2 * r;

    do {
        drawPixel(x0 - x, y0 + y, color);
        drawPixel(x0 - y, y0 - x, color);
        drawPixel(x0 + x, y0 - y, color);
        drawPixel(x0 + y, y0 + x, color);
        r = err;
        if (r <= y) err += ++y * 2 + 1;
        if (r > x || err > y) err += ++x * 2 + 1;
    } while (x < 0);
}

//--------------------------------------
// Bitmap
//--------------------------------------
void drawBitmap(int x, int y, const unsigned char *bitmap, int w, int h, int color)
{
    int byteWidth = (w + 7) / 8;
    
    for (int j = 0; j < h; j++) {
        for (int i = 0; i < w; i++) {

            int byteIndex = j * byteWidth + (i / 8);
            int bitMask   = 0x80 >> (i & 7);

            if (bitmap[byteIndex] & bitMask) {
                drawPixel(x + i, y + j, color);
            }
        }
    }
}

void drawChar(int x, int y, char c, int color) {
    if (c < 32 || c > 126) return;
    
    const uint8_t *glyph = font5x7[c - 32];

    for (int col = 0; col < 5; col++) {
        uint8_t line = glyph[col];
        for (int row = 0; row < 7; row++) {
            if (line & (1 << row)) {
                drawPixel(x + col, y + row, color);
            }
        }
    }

    for (int row = 0; row < 7; row++) {
        drawPixel(x + 5, y + row, 0);
    }
}

void drawString(int x, int y, const char *str, int color) {
    while (*str) {
        drawChar(x, y, *str, color);
        x += 6;
        str++;
    }
}

void drawStringF(int x, int y, int color, const char *fmt, ...){
    
    char buf[128];
    va_list args;
    
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);
    
    drawString(x, y, buf, color);
}

//--------------------------------------
// main
//--------------------------------------
int main() {

    // ★ BCM 番号で使う
    wiringPiSetupGpio();

    pinMode(SPI_DC, OUTPUT);
    pinMode(SPI_RST, OUTPUT);

    wiringPiSPISetup(SPI_CHANNEL, SPI_SPEED);

    // キー入力ピン(プルアップ)
    pinMode(KEY_UP_PIN,     INPUT);
    pinMode(KEY_DOWN_PIN,   INPUT);
    pinMode(KEY_LEFT_PIN,   INPUT);
    pinMode(KEY_RIGHT_PIN,  INPUT);
    pinMode(KEY_PRESS_PIN,  INPUT);
    pinMode(KEY1_PIN,       INPUT);
    pinMode(KEY2_PIN,       INPUT);
    pinMode(KEY3_PIN,       INPUT);

    pullUpDnControl(KEY_UP_PIN,     PUD_UP);
    pullUpDnControl(KEY_DOWN_PIN,   PUD_UP);
    pullUpDnControl(KEY_LEFT_PIN,   PUD_UP);
    pullUpDnControl(KEY_RIGHT_PIN,  PUD_UP);
    pullUpDnControl(KEY_PRESS_PIN,  PUD_UP);
    pullUpDnControl(KEY1_PIN,       PUD_UP);
    pullUpDnControl(KEY2_PIN,       PUD_UP);
    pullUpDnControl(KEY3_PIN,       PUD_UP);

    sh1106_init();
    sh1106_setRotation(ROTATE_180);
    sh1106_clear();

    int logo_x = 10;
    int logo_y = 20;

    while (1) {

        // 十字キーで BMP を 1ドット移動(押すと LOW)
        if (digitalRead(KEY_UP_PIN) == LOW) {
            if (logo_y > 0) logo_y -= 1;
        }
        if (digitalRead(KEY_DOWN_PIN) == LOW) {
            if (logo_y < V_HEIGHT - 32) logo_y += 1;
        }
        if (digitalRead(KEY_LEFT_PIN) == LOW) {
            if (logo_x > 0) logo_x -= 1;
        }
        if (digitalRead(KEY_RIGHT_PIN) == LOW) {
            if (logo_x < V_WIDTH - 32) logo_x += 1;
        }

        // 画面クリア
        sh1106_clear();

        // 外枠
        drawRect(0, 0, V_WIDTH , V_HEIGHT, 1);

        // タイトル
        fillRect(2, 2, 5, 5, 1);
        drawStringF(10, 2, 1, "TMANEGI BLOG %d", 2026);

        // BMP(動く)
        drawBitmap(logo_x, logo_y, logo_bmp, 32, 32, 1);

        // 右端に KEY1〜3 の □
        int box_w = 10;
        int box_h = 10;
        int box_x = V_WIDTH - box_w - 2;

        int box1_y = 10;
        int box2_y = 26;
        int box3_y = 42;

        // KEY1
        drawRect(box_x, box1_y, box_w, box_h, 1);
        if (digitalRead(KEY1_PIN) == LOW) {
            fillRect(box_x+1, box1_y+1, box_w-2, box_h-2, 1);
        }

        // KEY2
        drawRect(box_x, box2_y, box_w, box_h, 1);
        if (digitalRead(KEY2_PIN) == LOW) {
            fillRect(box_x+1, box2_y+1, box_w-2, box_h-2, 1);
        }

        // KEY3
        drawRect(box_x, box3_y, box_w, box_h, 1);
        if (digitalRead(KEY3_PIN) == LOW) {
            fillRect(box_x+1, box3_y+1, box_w-2, box_h-2, 1);
        }

        sh1106_update();

        usleep(20000); // 20ms
    }

    return 0;
}

結果

Raspberry PiとSH1106HATを合体させて使っています。
動きを適当に遅くするウエイトを入れていますがスムーズに動いてくれます。

ボタンの押し感は固めでしっかりとしたクリック感があります。
画像ではピンヘッダのみで保持していますが、スペーサなどでしっかり保持しないとボタンを押せません。

コメント

タイトルとURLをコピーしました