OLED displej 128x32 0,91" SSD1306 I²C a Arduino

Tvoříte zajímavý projekt? Pochlubte se s ním.
Pravidla fóra
Vkládejte prosím jen HOTOVÉ projekty, které chcete představit ostatním.
Odpovědět
Uživatelský avatar
Diego
Příspěvky: 267
Registrován: 23 črc 2017, 09:43
Kontaktovat uživatele:

OLED displej 128x32 0,91" SSD1306 I²C a Arduino

Příspěvek od Diego » 23 zář 2026, 04:42

Dnes jsem se podíval na malý OLED displej 128x32 0,91" s řadičem SSD1306 a I²C sběrnicí a jak ho připojit k Arduinu. Mám pár základních prográmků a pár fotek a videí, prostě jako vždy ;)
Odkaz je tady: https://www.michaluvbrloh.cz/2026/09/23 ... a-arduino/
OLED_091_05.jpg
https://www.michaluvbrloh.cz - Můj web
https://hackaday.io/projects/hacker/1326779 - Moje některé probíhající i dokončené projekty

Uživatelský avatar
kiRRow
Příspěvky: 1377
Registrován: 07 kvě 2019, 07:03
Bydliště: Opava

Re: OLED displej 128x32 0,91" SSD1306 I²C a Arduino

Příspěvek od kiRRow » 23 zář 2026, 15:29

Tu máš tetris, ať to máš ještě zajímavější ten článek. :D

Kód: Vybrat vše

#include <U8glib.h>

U8GLIB_SSD1306_128X32 u8g(U8G_I2C_OPT_NONE);

/*********************************************************/
#define bW 4
#define bH 4
#define ROW 23
#define COL 10
#define bOffX 1
#define bOffY 33
#define nextX 4
#define nextY 4
#define bP 3

#define DOWN_SCORE 1+1*lvl

int board[ROW][COL];


boolean isStarted = 0;
unsigned long int gameTime = 0;

unsigned long int score = 0;
unsigned int lines = 0;
int lvl = 0;

int gameSpeed = 1000;
boolean gameOver = 1;

unsigned int IBlock[4] = {0x4444,0x0F00,0x4444,0x0F00};
unsigned int JBlock[4] = {0x2260,0x4700,0x3220,0x0710};
unsigned int LBlock[4] = {0x4460,0x0E80,0xC440,0x2E00};
unsigned int OBlock[4] = {0x6600,0x6600,0x6600,0x6600};
unsigned int SBlock[4] = {0x6C00,0x4620,0x06C0,0x8C40};
unsigned int TBlock[4] = {0x0270,0x4640,0x7200,0x1310};
unsigned int ZBlock[4] = {0xC600,0x2640,0x0C60,0x4C80};

unsigned int nextBlock;
unsigned int activeBlock[4];
int nbR;
int nbID;

int abID;
int abX;
int abY;
int abR;

byte mID;
bool mActive;
/*********************************************************/

/*-------------------------------------------------------*/
class Button {
public:
    enum State {
        OFF,
        ON,
        HELD
    };

    Button(uint8_t pin, unsigned long timeToHeld, unsigned long debounceTime = 20)
        : _pin(pin), _timeToHeld(timeToHeld), _debounceTime(debounceTime) {
        pinMode(_pin, INPUT_PULLUP);   // předpokládám tlačítko proti GND
        _lastStableState = HIGH;
        _lastReading = HIGH;
        _lastDebounceTime = 0;
        _state = OFF;
        _pressedTime = 0;
    }

    void update() {
        int reading = digitalRead(_pin);

        // debounce
        if (reading != _lastReading) {
            _lastDebounceTime = millis();
        }
        _lastReading = reading;

        if ((millis() - _lastDebounceTime) > _debounceTime) {
            if (reading != _lastStableState) {
                _lastStableState = reading;

                if (_lastStableState == LOW) {
                    // tlačítko zmáčknuto
                    _pressedTime = millis();
                    _state = ON;
                } else {
                    // tlačítko puštěno
                    _state = OFF;
                }
            }

            // check held
            if (_lastStableState == LOW && (millis() - _pressedTime) >= _timeToHeld) {
                _state = HELD;
            }
        }
    }

    State getState() const {
        return _state;
    }

private:
    uint8_t _pin;
    unsigned long _timeToHeld;
    unsigned long _debounceTime;

    int _lastStableState;
    int _lastReading;
    unsigned long _lastDebounceTime;
    unsigned long _pressedTime;

    State _state;
};
/*-------------------------------------------------------*/

Button upBtn(2, 100);  // pin 2, HELD po 100 ms
Button leftBtn(3, 100);  // pin 3, HELD po 100 ms
Button downBtn(4, 100);  // pin 4, HELD po 100 ms
Button rightBtn(5, 100);  // pin 5, HELD po 100 ms
Button rotBtn(6, 100);  // pin 6, HELD po 100 ms

void setup() {
  Serial.begin(115200);
  u8g.setRot90();
  
  abX = 0;
  abY = 0;
  abR = 0;

  isStarted = 0;
  gameOver = 1;
  gameSpeed = 1200;

  mID = 0;
  mActive = 0;

  randomSeed(analogRead(A0));

  delay(500);
}

byte up_btnState, up_btnLastState, left_btnState, left_btnLastState, down_btnState, down_btnLastState, right_btnState, right_btnLastState, rot_btnState, rot_btnLastState;

void loop() {  
  upBtn.update();
  leftBtn.update();
  downBtn.update();
  rightBtn.update();
  rotBtn.update();
  up_btnState = upBtn.getState();
  left_btnState = leftBtn.getState();
  down_btnState = downBtn.getState();
  right_btnState = rightBtn.getState();
  rot_btnState = rotBtn.getState();

  //UP
  if(up_btnState != up_btnLastState || up_btnState == Button::HELD){
    switch (up_btnState) {
        case Button::OFF:
            break;

        case Button::ON:
            if(gameOver == 0){
              //moveLeft();
            } else {
              if(!mActive){
                if(mID == 0){mID = 2;}else{mID = (mID - 1) % 3;}
              } else {
                if(lvl > 0 && mID == 1){lvl--;}
              }
            }
            break;

        case Button::HELD:
            break;
    }
  }
  //LEFT
  if(left_btnState != left_btnLastState || left_btnState == Button::HELD){
    switch (left_btnState) {
        case Button::OFF:
            break;

        case Button::ON:

            if(gameOver == 0){
              moveLeft();
            } else {
              if(!mActive){
                if(mID == 0){mID = 2;}else{mID = (mID - 1) % 3;}
              } else {
                if(lvl > 0 && mID == 1){lvl--;}
              }
            }
            break;

        case Button::HELD:
            if(gameOver == 0){
              moveLeft();
              moveLeft();
            }
            break;
    }
  }
  //DOWN
  if(down_btnState != down_btnLastState || down_btnState == Button::HELD){
    switch (down_btnState) {
        case Button::OFF:
            break;

        case Button::ON:
            if(gameOver == 0){
              //moveRight();
            } else {
              if(!mActive){
                mID = (mID + 1) % 3;
              } else {
                if(lvl < 9 && mID == 1){lvl++;}
              }
            }
            break;

        case Button::HELD:
            if(gameOver == 0){
              moveDown();
              score += DOWN_SCORE;
              moveDown();
              score += DOWN_SCORE;
            }
            break;
    }
  }
  //RIGHT
  if(right_btnState != right_btnLastState || right_btnState == Button::HELD){
    switch (right_btnState) {
        case Button::OFF:
            break;

        case Button::ON:
            if(gameOver == 0){
              moveRight();
            } else {
              if(!mActive){
                mID = (mID + 1) % 3;
              } else {
                if(lvl < 9 && mID == 1){lvl++;}
              }
            }
            break;

        case Button::HELD:
            if(gameOver == 0){
              moveRight();
              moveRight();
            }
            break;
    }
  }
  //ROT
  if(rot_btnState != rot_btnLastState || rot_btnState == Button::HELD){
    switch (rot_btnState) {
        case Button::OFF:
            break;

        case Button::ON:
            if(gameOver == 0){
              moveRotate();
            } else {
              menuOpen();
              //isStarted = 1;
              //gameOver = 0;
            }
            break;

        case Button::HELD:
            break;
    }
  }

  up_btnLastState = up_btnState;
  left_btnLastState = left_btnState;
  down_btnLastState = down_btnState;
  right_btnLastState = right_btnState;
  rot_btnLastState = rot_btnState;
//--------------------------------------
  if(!gameOver){
    if (millis()-gameTime > (gameSpeed-lvl*100)) {
      moveDown();
      gameTime = millis();
    }
  }

  if(isStarted && gameOver == 0){
    //gameOver = 0;
    score = 0;
    lines = 0;
    isStarted = 0;
    prepareNext();
    createBlock();
  }
//--------------------------------------
  u8g.firstPage();
  do {
    draw();
  } while( u8g.nextPage() );
}//end loop
//--------------------------------------
void menuOpen(){
  if(mID == 0){
    isStarted = 1;
    gameOver = 0;
  }else{
    mActive = !mActive;
  }
}

void drawMenu(){
  if(!mActive || mID == 1){
    u8g.setFont(u8g_font_5x7);
    u8g.setPrintPos(4, 42);
    u8g.print(F("Start"));
    u8g.setPrintPos(4, 52);
    u8g.print(F("Level"));
    u8g.setPrintPos(4, 62);
    u8g.print(F("Score"));
  
    u8g.drawLine(2,42+mID*10,28,42+mID*10);
    if(mActive){
      u8g.drawLine(2,42-8+mID*10,28,42-8+mID*10);
      u8g.drawLine(2,42-7+mID*10,2,42+mID*10);
      u8g.drawLine(28,42-7+mID*10,28,42+mID*10);
    }
  }
}

void drawGUI(void){
  u8g.drawFrame(2, 2, 16, 16);
  u8g.drawFrame(0, 32, COL*bP+2, ROW*bP+2);
  u8g.setFont(u8g_font_7x14);//lvl
  u8g.setPrintPos(22, 15);
  u8g.print(lvl);
  drawScore();
}

void drawScore(){
  u8g.setFont(u8g_font_5x7);//score
  u8g.setPrintPos(1, 28);
  u8g.print(score);  
}

void drawBlock(int x, int y){
  u8g.drawFrame(x ,y , bP, bP);  
}

void drawNextBlock(void){
  int b = 15;
  for(int s=0;s < bW; s++){
    for(int r=0;r < bH; r++){
      if(!bitRead(nextBlock, b)){ b--;continue;}        
        drawBlock(r*bP+nextX, s*bP+nextY);
        b--;
    }
  }
}

void drawBoard(){
  for(int y=0;y < ROW; y++){
    for(int x=0;x < COL; x++){
      if(!board[y][x]){continue;}      
      drawBlock(x*bP+bOffX, y*bP+bOffY);
    }
  }
}

void drawActiveBlock(){
int b = 15;
  for(int s=0;s < bW; s++){
    for(int r=0;r < bH; r++){
      if(!bitRead(activeBlock[abR], b)){ b--;continue;}        
        drawBlock(r*bP+bOffX+abX*bP, s*bP+bOffY+abY*bP);
        b--;
    }
  }  
}

void prepareNext(){
  nbID = random(6);
  nbR = random(3);

  switch(nbID){
    case 0:
      nextBlock = IBlock[nbR];
      break;
    case 1:
      nextBlock = JBlock[nbR];
      break;
    case 2:
      nextBlock = LBlock[nbR];
      break;
    case 3:
      nextBlock = OBlock[nbR];
      break;
    case 4:
      nextBlock = SBlock[nbR];
      break;
    case 5:
      nextBlock = TBlock[nbR];
      break;
    case 6:
      nextBlock = ZBlock[nbR];
      break;
  } 
}

void createBlock(){
abX = 3;
abY = 0;
abR = nbR;
abID = nbID;
  switch(abID){
    case 0:
      for(int i=0; i < 4;i++){activeBlock[i] = IBlock[i];}
      break;
    case 1:
      for(int i=0; i < 4;i++){activeBlock[i] = JBlock[i];}
      break;
    case 2:
      for(int i=0; i < 4;i++){activeBlock[i] = LBlock[i];}
      break;
    case 3:
      for(int i=0; i < 4;i++){activeBlock[i] = OBlock[i];}
      break;
    case 4:
      for(int i=0; i < 4;i++){activeBlock[i] = SBlock[i];}
      break;
    case 5:
      for(int i=0; i < 4;i++){activeBlock[i] = TBlock[i];}
      break;
    case 6:
      for(int i=0; i < 4;i++){activeBlock[i] = ZBlock[i];}
      break;
  }
  
prepareNext();
}

void moveRight(){
  if(notColision(abX+1,abY,abR)){
    abX++;
  }
}

void moveLeft(){
  if(notColision(abX-1,abY,abR)){
  abX--;
  }
}

void moveDown(){
  if(notColision(abX,abY+1,abR)){
  abY++;
  }else{
  lockBlock();
  }
}

void moveRotate(){
  byte kOff = 1;
  if(notColision(abX,abY,(abR + 1) % 4)){
  abR = (abR + 1) % 4;
  }else{
    if(abX <= 0){
      if(notColision(abX+1,abY,(abR + 1) % 4)){
        abX++;
        abR = (abR + 1) % 4;      
      }
    }
    if(abX >= 7){
      if(abID == 0 && abX == 8){kOff = 2;}
      if(notColision(abX-kOff,abY,(abR + 1) % 4)){
        abX -= kOff;
        abR = (abR + 1) % 4;
      }
    }
  }
}

void lockBlock(){
  int b = 15;
  int fullRowCounter = 0;
  for(int s=0;s < bW; s++){
    for(int r=0;r < bH; r++){
      if(!bitRead(activeBlock[abR], b)){ b--;continue;}
      
      board[abY+s][abX+r] = 1;

      b--;
      if(abY == 0){
        gameOver = 1;
      }
    }
  }
  if(!gameOver){
    for(int y=0;y < ROW; y++){
      boolean isRowFull = true;
      
      for(int x=0;x < COL; x++){
        isRowFull = isRowFull && board[y][x];
      }

      if(isRowFull){
        for(int r = y;r > 1; r--){
          for(int s = 0; s < COL; s++){
            board[r][s] = board[r-1][s];
          }
        }
      score += 10 + (10*fullRowCounter);      
      fullRowCounter++;
      }
    }
    
    createBlock();
  } else {
    eraseGame();
  }
}

boolean notColision(int newX, int newY, int newR){
boolean nocolide = true;  
int b = 15;
  for(int s=0;s < bW; s++){
    for(int r=0;r < bH; r++){
      if(!bitRead(activeBlock[newR], b)){b--; continue;}
      
      if((newX+r >= COL)||(newX+r < 0)){nocolide=false;}
      if(newY+s >= ROW){nocolide=false;}
      if(board[newY+s][newX+r]){nocolide=false;}

      b--;
    }
  }

return nocolide;  
}

void eraseGame(){
  for(int y=0;y < ROW; y++){
  for(int x=0;x < COL; x++){
    board[y][x] = 0;
  }
  }
  gameOver = 1;
  isStarted = 0;
}

void draw(void) {
  drawGUI();
  drawScore();
  
  if(gameOver) {
    drawMenu();    
  }else{
    drawActiveBlock();
    drawNextBlock();
    drawBoard();
  }
}
IMG-20250926-WA0005.jpg

Odpovědět

Kdo je online

Uživatelé prohlížející si toto fórum: Žádní registrovaní uživatelé a 2 hosti