😺

【M5coreS3】自作DigitalForm

2024/07/28に公開

前回作成したButtonを用います

lib/Button/Button.h
#ifndef BUTTON_H
#define BUTTON_H

#include <Arduino.h>
#include <M5CoreS3.h>
#include <functional>

class Button {
public:
    Button() = default;
    Button(
        const String& label,
        int x, int y, int width, int height,
        std::function<void()> onPressed,
        uint32_t color = WHITE,
        uint32_t textColor = BLACK,
        uint8_t textSize = 1,
        bool autoDraw = true
    );

    void draw() const;
    bool isPressed(int touchX, int touchY);
    void update();

private:
    String label;
    int x = 0, y = 0, width = 0, height = 0;
    uint32_t color = WHITE, textColor = BLACK;
    std::function<void()> onPressed;
    unsigned long pressTime = 0;
    bool isHighlighted = false;
    uint8_t textSize = 1;
    bool autoDrawEnabled = true;
};

#endif // BUTTON_H
lib/Button/Button.cpp
#include "Button.h"

Button::Button(
    const String& label,
    int x, int y, int width, int height,
    std::function<void()> onPressed,
    uint32_t color,
    uint32_t textColor,
    uint8_t textSize,
    bool autoDraw
) : label(label), x(x), y(y), width(width), height(height),
    onPressed(onPressed), color(color), textColor(textColor),
    textSize(textSize), autoDrawEnabled(autoDraw)
{
    if (autoDrawEnabled) {
        draw();
    }
}

void Button::draw() const {

    int32_t colorValue = isHighlighted? color : ((color & 0xFF000000) | (~color & 0x00FFFFFF));
    CoreS3.Display.fillRect(x, y, width, height, colorValue);
    CoreS3.Display.drawRect(x, y, width, height, colorValue);

    int32_t textColorValue = isHighlighted? textColor : ((textColor & 0xFF000000) | (~textColor & 0x00FFFFFF));
    CoreS3.Display.setTextSize(textSize);
    CoreS3.Display.setTextColor(textColorValue);
    CoreS3.Display.setTextDatum(MC_DATUM);
    CoreS3.Display.drawString(label, x + width / 2, y + height / 2);
}

bool Button::isPressed(int touchX, int touchY) {
    bool pressed = (touchX >= x && touchX < x + width &&
                    touchY >= y && touchY < y + height);
    if (pressed && !isHighlighted) {
        isHighlighted = true;
        pressTime = millis();
        draw();
        if (onPressed) {
            onPressed();
        }
    }
    return pressed;
}

void Button::update() {
    if (isHighlighted && millis() - pressTime > 200) {
        isHighlighted = false;
        draw();
    }
}

これをmain.cppで引用します。

main.cpp
#include <M5CoreS3.h>
#include <M5Unified.h>
#include <optional>
#include "Button.h"

const int MAX_DIGITS = 8;
char inputNumber[MAX_DIGITS + 1] = {0};  // +1 for null terminator
int currentDigit = 0;
int cursorBlinkState = 0;
unsigned long lastBlinkTime = 0;
int prevMillis = 0;

const int displayWidth = 320;
const int displayHeight = 240;

//////////////////// digitalDisplay ////////////////////
const int digitalDisplayStartY = 30;
const int digitalDisplayMarginX = 10;
const int digitalDisplayPadding = 5;
const int charWidth = 18; // 固定幅フォントの文字高さ

const int digitalDisplayWidth = displayWidth - digitalDisplayMarginX * 2;
const int digitalDisplayHeight = 28 + digitalDisplayPadding * 2;
const int stringStartX = digitalDisplayMarginX + digitalDisplayPadding;
const int stringStartY = digitalDisplayStartY + digitalDisplayPadding;

void drawDigitalDisplay() {
    CoreS3.Display.fillRect(digitalDisplayMarginX, digitalDisplayStartY, digitalDisplayWidth, digitalDisplayHeight, BLACK);
    CoreS3.Display.drawRect(digitalDisplayMarginX, digitalDisplayStartY, digitalDisplayWidth, digitalDisplayHeight, WHITE);
}

void updateDisplay() {
    drawDigitalDisplay();
    CoreS3.Display.setTextColor(WHITE);
    CoreS3.Display.setTextSize(1);
    CoreS3.Display.setTextDatum(TL_DATUM);        
    CoreS3.Display.drawString(inputNumber, stringStartX, stringStartY);
}

void blinkCursor() {
    if (millis() - lastBlinkTime > 500) {
        cursorBlinkState = !cursorBlinkState;
        lastBlinkTime = millis();
        int cursorX = stringStartX + currentDigit * 20;  // Adjust based on your font size
        CoreS3.Display.fillRect(cursorX, stringStartY, 20, 30, cursorBlinkState ? WHITE : BLACK);
    }
}

//////////////////// テンキーに関するプロパティ、メソッド ////////////////////

const int startY = displayHeight / 2 - 40;
const int spacing = 10;
const int btnWidth = (displayWidth - spacing * 6) / 5;
const int btnHeight = btnWidth-5;

Button buttons[12]; // 0-9, delete, enter

void buttonCallback(int num) {
    if (num >= 0 && num <= 9) {
        if (currentDigit < MAX_DIGITS) {
            inputNumber[currentDigit++] = '0' + num;
            inputNumber[currentDigit] = '\0';
            updateDisplay();
        }
    } else if (num == 10) { // delete
        if (currentDigit > 0) {
            inputNumber[--currentDigit] = '\0';
            updateDisplay();
        }
    } else if (num == 11) { // enter
        // Enter処理をここに追加
        Serial.println("Enter pressed");
    }
}

void initializeButtons() {
    for (int i = 0; i < 10; i++) {
        int x = spacing + (i % 5) * (btnWidth + spacing);
        int y = startY + (i / 5) * (btnHeight + spacing);
        String label = String((i == 9) ? 0 : i + 1);
        buttons[i] = Button(
            label,
            x, y, btnWidth, btnHeight, 
            [i]() { buttonCallback(i == 9 ? 0 : i + 1); },
            WHITE,
            BLACK,
            1
        );
    }

    const int controlKeyWidth = (CoreS3.Display.width() - 3 * spacing) / 2;
    const int controlKeyStartY = startY + 2 * (btnHeight + spacing);
    
    buttons[10] = Button("delete", spacing, controlKeyStartY, controlKeyWidth, btnHeight, 
                         []() { buttonCallback(10); }, WHITE, BLACK);
    buttons[11] = Button("enter", spacing * 2 + controlKeyWidth, controlKeyStartY, controlKeyWidth, btnHeight, 
                         []() { buttonCallback(11); }, WHITE, BLACK);
}

void drawKeypad() {
    for (int i = 0; i < 12; i++) {
        buttons[i].draw();
    }
}

void handleTouch() {
    auto t = CoreS3.Touch.getDetail();

    if (t.isPressed()) {
        for (int i = 0; i < 12; i++) {
            if (buttons[i].isPressed(t.x, t.y)) {
                break; // ボタンが押されたら処理を終了
            }
        }
    }
}

void handleButton(){
    int curMillis = millis();

    Serial.println(curMillis - prevMillis);

    if(curMillis - prevMillis > 200){
        prevMillis = curMillis ;
        return;
    }

    Serial.println("aaaa");
}

void setup(void) {
    Serial.begin(115200);

    auto cfg = M5.config();
    CoreS3.begin(cfg);

    CoreS3.Display.setTextColor(RED);
    CoreS3.Display.setTextDatum(middle_center);
    CoreS3.Display.setFont(&fonts::Orbitron_Light_24);
    CoreS3.Display.setTextSize(1);

    initializeButtons();
    drawDigitalDisplay();
    drawKeypad();
}

void loop(void) {
    CoreS3.update();
    handleTouch();
    blinkCursor();

    M5.update();

    if(M5.BtnB.isPressed()){
        handleButton();
    }

    for (int i = 0; i < 12; i++) {
        buttons[i].update();
    }
}

補足

cores3 SEではcore2 basicにあった物理的なボタンは無く、<< ○ >>のタッチボタンになっていますが、
ライブラリのM5.BtnA、M5.BtnB、M5.BtnCでハンドリングできます。

改造版

#include <M5CoreS3.h>
#include <M5Unified.h>
#include <optional>
#include "Button.h"

const int MAX_DIGITS = 8;
char inputNumber[MAX_DIGITS + 1] = {0};  // +1 for null terminator
int currentDigit = 0;
int cursorBlinkState = 0;
unsigned long lastBlinkTime = 0;
int prevMillis = 0;

const int displayWidth = 320;
const int displayHeight = 240;

//////////////////// digitalDisplay ////////////////////
const int digitalDisplayStartY = 40;
const int digitalDisplayMarginX = 10;
const int digitalDisplayPadding = 5;
const int charWidth = 18; // 固定幅フォントの文字高さ

const int digitalDisplayWidth = displayWidth - digitalDisplayMarginX * 2;
const int digitalDisplayHeight = 28 + digitalDisplayPadding * 2;
const int stringStartX = digitalDisplayMarginX + digitalDisplayPadding;
const int stringStartY = digitalDisplayStartY + digitalDisplayPadding;

void drawDigitalDisplay() {
    CoreS3.Display.fillRect(digitalDisplayMarginX, digitalDisplayStartY, digitalDisplayWidth, digitalDisplayHeight, BLACK);
    CoreS3.Display.drawRect(digitalDisplayMarginX, digitalDisplayStartY, digitalDisplayWidth, digitalDisplayHeight, WHITE);
}

void updateDisplay() {
    drawDigitalDisplay();
    CoreS3.Display.setTextColor(WHITE);
    CoreS3.Display.setTextSize(1);
    CoreS3.Display.setTextDatum(TL_DATUM);        
    CoreS3.Display.drawString(inputNumber, stringStartX, stringStartY);
}

void blinkCursor() {
    if (millis() - lastBlinkTime > 500) {
        cursorBlinkState = !cursorBlinkState;
        lastBlinkTime = millis();
        int cursorX = stringStartX + currentDigit * 20;  // Adjust based on your font size
        CoreS3.Display.fillRect(cursorX, stringStartY, 20, 30, cursorBlinkState ? WHITE : BLACK);
    }
}

//////////////////// テンキーに関するプロパティ、メソッド ////////////////////

const int btnStartY = 100;
const int btnSpacing = 5;
const int btnWidth = 70;
const int btnHeight = 40;

Button buttons[13]; // 0-9, delete, enter

void buttonCallback(int num) {
    if (num >= 0 && num <= 10) {
        if (currentDigit < MAX_DIGITS) {
            inputNumber[currentDigit++] = '0' + num;
            inputNumber[currentDigit] = '\0';
            updateDisplay();
        }
    } else if (num == 11) { // delete
        if (currentDigit > 0) {
            inputNumber[--currentDigit] = '\0';
            updateDisplay();
        }
    } else if (num == 12){ //enter
        // if (currentDigit > 0) {

            updateDisplay();
        // }
    }
}

void initializeButtons() {
    for (int i = 0; i < 9; i++) {
        int x = btnSpacing + (i % 3) * (btnWidth  + btnSpacing);
        int y = btnStartY  + (i / 3) * (btnHeight + btnSpacing);
        String label = String (i + 1);
        buttons[i] = Button(
            label,
            x, y, btnWidth, btnHeight, 
            [i]() { buttonCallback(i + 1); },
            WHITE,
            BLACK,
            1
        );
    }

    int leftBtnStartX = btnWidth * 3 + btnSpacing * 4;

    buttons[10] = Button("0",   leftBtnStartX, btnStartY + (btnHeight + btnSpacing) * 2, btnWidth, btnHeight,
                         []() { buttonCallback(10); }, WHITE, BLACK);    
    
    buttons[11] = Button("del", leftBtnStartX, btnStartY + (btnHeight + btnSpacing) * 0, btnWidth, btnHeight,
                         []() { buttonCallback(11); }, WHITE, BLACK);

    buttons[12] = Button("ent", leftBtnStartX, btnStartY + (btnHeight + btnSpacing) * 1, btnWidth, btnHeight, 
                         []() { buttonCallback(12); }, WHITE, BLACK);

}

void drawKeypad() {
    for (int i = 0; i < 12; i++) {
        buttons[i].draw();
    }
}

void handleTouch() {
    auto t = CoreS3.Touch.getDetail();

    if (t.isPressed()) {
        for (int i = 0; i < 12; i++) {
            if (buttons[i].isPressed(t.x, t.y)) {
                break; // ボタンが押されたら処理を終了
            }
        }
    }
}

void handleButton(){
    int curMillis = millis();

    Serial.println(curMillis - prevMillis);

    if(curMillis - prevMillis > 200){
        prevMillis = curMillis ;
        return;
    }

    Serial.println("aaaa");
}

void setup(void) {
    Serial.begin(115200);

    auto cfg = M5.config();
    CoreS3.begin(cfg);

    CoreS3.Display.setTextColor(RED);
    CoreS3.Display.setTextDatum(middle_center);
    CoreS3.Display.setFont(&fonts::Orbitron_Light_24);
    CoreS3.Display.setTextSize(1);

    initializeButtons();
    drawDigitalDisplay();
    drawKeypad();
}

void loop(void) {
    CoreS3.update(); // タッチスクリーンの状態更新
    handleTouch();
    blinkCursor();

    M5.update(); // 固定ボタンの状態更新
    if(M5.BtnB.isPressed()){
        handleButton();
    }

    for (int i = 0; i < 12; i++) {
        buttons[i].update();
    }
}

Discussion