打印

很好的LED显示例子

[复制链接]
438|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
keer_zu|  楼主 | 2025-5-21 12:15 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
代码:

// TETRIS
#include <FastLED.h>
#include <LEDMatrix.h>
#include <LEDSprites.h>
#include <LEDText.h>
#include <FontMatrise.h>
#include "BluetoothSerial.h"

#define LED_PIN        15
#define COLOR_ORDER    GRB
#define CHIPSET        WS2812B
#define MATRIX_WIDTH   30
#define MATRIX_HEIGHT  15
#define MATRIX_TYPE    HORIZONTAL_MATRIX

// NOTE the '-' sign before the width, this is due to my leds matrix origin being on the right hand side
cLEDMatrix<MATRIX_WIDTH, -MATRIX_HEIGHT, MATRIX_TYPE> leds;

BluetoothSerial SerialBT;

#define TARGET_FRAME_TIME    15  // Desired update rate, though if too many leds it will just run as fast as it can!
#define INITIAL_DROP_FRAMES  20  // Start of game block drop delay in frames

// Bluetooth input
enum btnInput {NONE, ROTATE, DOWN, LEFT, RIGHT};
btnInput currentInput = NONE;

const uint8_t TetrisIData[] =
{
  // Frame 1
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(11110000),
  // Frame 2
  B8_3BIT(10000000),
  B8_3BIT(10000000),
  B8_3BIT(10000000),
  B8_3BIT(10000000),
  // Frame 3
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(11110000),
  // Frame 4
  B8_3BIT(10000000),
  B8_3BIT(10000000),
  B8_3BIT(10000000),
  B8_3BIT(10000000)
};
const uint8_t TetrisIMask[] =
{
  // Frame 1
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(11110000),
  // Frame 2
  B8_1BIT(10000000),
  B8_1BIT(10000000),
  B8_1BIT(10000000),
  B8_1BIT(10000000),
  // Frame 3
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(11110000),
  // Frame 4
  B8_1BIT(10000000),
  B8_1BIT(10000000),
  B8_1BIT(10000000),
  B8_1BIT(10000000)
};
const uint8_t TetrisJData[] =
{
  // Frame 1
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(22200000),
  B8_3BIT(00200000),
  // Frame 2
  B8_3BIT(00000000),
  B8_3BIT(02000000),
  B8_3BIT(02000000),
  B8_3BIT(22000000),
  // Frame 3
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(20000000),
  B8_3BIT(22200000),
  // Frame 4
  B8_3BIT(00000000),
  B8_3BIT(22000000),
  B8_3BIT(20000000),
  B8_3BIT(20000000)
};
const uint8_t TetrisJMask[] =
{
  // Frame 1
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(11100000),
  B8_1BIT(00100000),
  // Frame 2
  B8_1BIT(00000000),
  B8_1BIT(01000000),
  B8_1BIT(01000000),
  B8_1BIT(11000000),
  // Frame 3
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(10000000),
  B8_1BIT(11100000),
  // Frame 4
  B8_1BIT(00000000),
  B8_1BIT(11000000),
  B8_1BIT(10000000),
  B8_1BIT(10000000)
};
const uint8_t TetrisLData[] =
{
  // Frame 1
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(33300000),
  B8_3BIT(30000000),
  // Frame 2
  B8_3BIT(00000000),
  B8_3BIT(33000000),
  B8_3BIT(03000000),
  B8_3BIT(03000000),
  // Frame 3
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(00300000),
  B8_3BIT(33300000),
  // Frame 4
  B8_3BIT(00000000),
  B8_3BIT(30000000),
  B8_3BIT(30000000),
  B8_3BIT(33000000)
};
const uint8_t TetrisLMask[] =
{
  // Frame 1
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(11100000),
  B8_1BIT(10000000),
  // Frame 2
  B8_1BIT(00000000),
  B8_1BIT(11000000),
  B8_1BIT(01000000),
  B8_1BIT(01000000),
  // Frame 3
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(00100000),
  B8_1BIT(11100000),
  // Frame 4
  B8_1BIT(00000000),
  B8_1BIT(10000000),
  B8_1BIT(10000000),
  B8_1BIT(11000000)
};
const uint8_t TetrisOData[] =
{
  // Frame 1
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(44000000),
  B8_3BIT(44000000),
  // Frame 2
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(44000000),
  B8_3BIT(44000000),
  // Frame 3
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(44000000),
  B8_3BIT(44000000),
  // Frame 4
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(44000000),
  B8_3BIT(44000000)
};
const uint8_t TetrisOMask[] =
{
  // Frame 1
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(11000000),
  B8_1BIT(11000000),
  // Frame 2
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(11000000),
  B8_1BIT(11000000),
  // Frame 3
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(11000000),
  B8_1BIT(11000000),
  // Frame 4
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(11000000),
  B8_1BIT(11000000)
};
const uint8_t TetrisSData[] =
{
  // Frame 1
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(05500000),
  B8_3BIT(55000000),
  // Frame 2
  B8_3BIT(00000000),
  B8_3BIT(50000000),
  B8_3BIT(55000000),
  B8_3BIT(05000000),
  // Frame 3
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(05500000),
  B8_3BIT(55000000),
  // Frame 4
  B8_3BIT(00000000),
  B8_3BIT(50000000),
  B8_3BIT(55000000),
  B8_3BIT(05000000)
};
const uint8_t TetrisSMask[] =
{
  // Frame 1
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(01100000),
  B8_1BIT(11000000),
  // Frame 2
  B8_1BIT(00000000),
  B8_1BIT(10000000),
  B8_1BIT(11000000),
  B8_1BIT(01000000),
  // Frame 3
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(01100000),
  B8_1BIT(11000000),
  // Frame 4
  B8_1BIT(00000000),
  B8_1BIT(10000000),
  B8_1BIT(11000000),
  B8_1BIT(01000000)
};
const uint8_t TetrisTData[] =
{
  // Frame 1
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(66600000),
  B8_3BIT(06000000),
  // Frame 2
  B8_3BIT(00000000),
  B8_3BIT(06000000),
  B8_3BIT(66000000),
  B8_3BIT(06000000),
  // Frame 3
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(06000000),
  B8_3BIT(66600000),
  // Frame 4
  B8_3BIT(00000000),
  B8_3BIT(60000000),
  B8_3BIT(66000000),
  B8_3BIT(60000000)
};
const uint8_t TetrisTMask[] =
{
  // Frame 1
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(11100000),
  B8_1BIT(01000000),
  // Frame 2
  B8_1BIT(00000000),
  B8_1BIT(01000000),
  B8_1BIT(11000000),
  B8_1BIT(01000000),
  // Frame 3
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(01000000),
  B8_1BIT(11100000),
  // Frame 4
  B8_1BIT(00000000),
  B8_1BIT(10000000),
  B8_1BIT(11000000),
  B8_1BIT(10000000)
};
const uint8_t TetrisZData[] =
{
  // Frame 1
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(77000000),
  B8_3BIT(07700000),
  // Frame 2
  B8_3BIT(00000000),
  B8_3BIT(07000000),
  B8_3BIT(77000000),
  B8_3BIT(70000000),
  // Frame 3
  B8_3BIT(00000000),
  B8_3BIT(00000000),
  B8_3BIT(77000000),
  B8_3BIT(07700000),
  // Frame 4
  B8_3BIT(00000000),
  B8_3BIT(07000000),
  B8_3BIT(77000000),
  B8_3BIT(70000000)
};
const uint8_t TetrisZMask[] =
{
  // Frame 1
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(11000000),
  B8_1BIT(01100000),
  // Frame 2
  B8_1BIT(00000000),
  B8_1BIT(01000000),
  B8_1BIT(11000000),
  B8_1BIT(10000000),
  // Frame 3
  B8_1BIT(00000000),
  B8_1BIT(00000000),
  B8_1BIT(11000000),
  B8_1BIT(01100000),
  // Frame 4
  B8_1BIT(00000000),
  B8_1BIT(01000000),
  B8_1BIT(11000000),
  B8_1BIT(10000000)
};

#define TETRIS_SPR_WIDTH  4
#define TETRIS_SPR_HEIGHT 4
const uint8_t *TetrisSprData[] = { TetrisIData, TetrisJData, TetrisLData, TetrisOData, TetrisSData, TetrisTData, TetrisZData };
const uint8_t *TetrisSprMask[] = { TetrisIMask, TetrisJMask, TetrisLMask, TetrisOMask, TetrisSMask, TetrisTMask, TetrisZMask};
const struct CRGB TetrisColours[] = { CRGB(0, 255, 255), CRGB(0, 0, 255), CRGB(255, 165, 0), CRGB(255, 255, 0), CRGB(50, 205, 50), CRGB(255, 0, 255), CRGB(255, 0, 0) };

uint8_t PlayfieldData[MATRIX_HEIGHT * ((MATRIX_WIDTH + 7) / 8) * _3BIT];
uint8_t PlayfieldMask[MATRIX_HEIGHT * ((MATRIX_WIDTH + 7) / 8) * _1BIT];
uint8_t CompletedLinesData[TETRIS_SPR_HEIGHT * ((MATRIX_WIDTH + 7) / 8) * _1BIT];
const struct CRGB CompletedLinesColour[] = { CRGB(255, 255, 255) };
cSprite Playfield, CompletedLines, CurrentBlock;
cLEDSprites Sprites(&leds);

unsigned char AttractMsg[144], GameOverMsg[88];
char BlankMsg[32];
cLEDText TetrisMsg;

uint8_t DropDelay;
boolean AttractMode, NextBlock;
int16_t TotalLines;
unsigned int HighScore = 0, LastScore;

uint16_t PlasmaTime, PlasmaShift;
uint32_t LoopDelayMS, LastLoop;

void setup()
{
  Serial.begin(115200);
  SerialBT.begin("ESP32Tetris");
  
  FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds[0], leds.Size());
  FastLED.setBrightness(200);
  FastLED.clear(true);
  FastLED.show();

  memset(PlayfieldData, 0, sizeof(PlayfieldData));
  memset(PlayfieldMask, 0, sizeof(PlayfieldMask));
  Playfield.Setup(leds.Width(), leds.Height(), PlayfieldData, 1, _3BIT, TetrisColours, PlayfieldMask);
  Playfield.SetPositionFrameMotionOptions(0, 0, 0, 0, 0, 0, 0, 0, 0);
  Sprites.AddSprite(&Playfield);

  memset(CompletedLinesData, 0, sizeof(CompletedLinesData));
  CompletedLines.Setup(leds.Width(), TETRIS_SPR_HEIGHT, CompletedLinesData, 1, _1BIT, CompletedLinesColour, CompletedLinesData);
  CompletedLines.SetPositionFrameMotionOptions(0, 0, 0, 0, 0, 0, 0, 0, 0);

  TetrisMsg.SetFont(MatriseFontData);
  sprintf((char *)BlankMsg, "%.*s", _min(((leds.Height() + TetrisMsg.FontHeight()) / (TetrisMsg.FontHeight() + 1)), (int)sizeof(BlankMsg) - 1), "                              ");
  sprintf((char *)AttractMsg, "%sTETRIS%sSCORE %u%sHIGH %u%sANY BUTTON TO START%s", BlankMsg, BlankMsg, LastScore, BlankMsg, (int)HighScore, BlankMsg, BlankMsg);
  TetrisMsg.Init(&leds, TetrisMsg.FontWidth() + 1, leds.Height(), (leds.Width() - TetrisMsg.FontWidth()) / 2, 0);
  TetrisMsg.SetBackgroundMode(BACKGND_LEAVE);
  TetrisMsg.SetScrollDirection(SCROLL_UP);
  TetrisMsg.SetTextDirection(CHAR_UP);
  TetrisMsg.SetFrameRate(1);
  TetrisMsg.SetOptionsChangeMode(INSTANT_OPTIONS_MODE);
  TetrisMsg.SetText(AttractMsg, strlen((const char *)AttractMsg));
  AttractMode = true;
  LoopDelayMS = TARGET_FRAME_TIME;
  LastLoop = millis() - LoopDelayMS;
  PlasmaShift = (random8(0, 5) * 32) + 64;
  PlasmaTime = 0;
}

void loop()
{
  if (abs(millis() - LastLoop) >= LoopDelayMS)
  {
    LastLoop = millis();
    FastLED.clear();

    // Fill background with dim plasma
    #define PLASMA_X_FACTOR  24
    #define PLASMA_Y_FACTOR  24
    for (int16_t x=0; x<MATRIX_WIDTH; x++)
    {
      for (int16_t y=0; y<MATRIX_HEIGHT; y++)
      {
        int16_t r = sin16(PlasmaTime) / 256;
        int16_t h = sin16(x * r * PLASMA_X_FACTOR + PlasmaTime) + cos16(y * (-r) * PLASMA_Y_FACTOR + PlasmaTime) + sin16(y * x * (cos16(-PlasmaTime) / 256) / 2);
        leds(x, y) = CHSV((uint8_t)((h / 256) + 128), 255, 64);
      }
    }
    uint16_t OldPlasmaTime = PlasmaTime;
    PlasmaTime += PlasmaShift;
    if (OldPlasmaTime > PlasmaTime)
      PlasmaShift = (random8(0, 5) * 32) + 64;

    if (AttractMode)
    {
      if (currentInput != NONE)
      {
        AttractMode = false;
        memset(PlayfieldData, 0, sizeof(PlayfieldData));
        memset(PlayfieldMask, 0, sizeof(PlayfieldMask));
        Sprites.RemoveSprite(&CurrentBlock);
        LastScore = 0;
        TotalLines = 0;
        DropDelay = INITIAL_DROP_FRAMES;
        CurrentBlock.SetXChange(-1);
        NextBlock = true;
        currentInput = NONE;
      }
    }
    else
    {
      if (Sprites.IsSprite(&CompletedLines))  // We have highlighted complete lines, delay for visual effect
      {
        if (CompletedLines.GetXCounter() > 0)
          CompletedLines.SetXCounter(CompletedLines.GetXCounter() - 1);
        else
        {
          Sprites.RemoveSprite(&CompletedLines);
          // Remove completed lines from playfield sprite
          uint8_t *Data = PlayfieldData;
          uint8_t *Mask = PlayfieldMask;
          uint16_t Mbpl = (MATRIX_WIDTH + 7) / 8;
          uint16_t Dbpl = Mbpl * _3BIT;
          int16_t k;
          for (int16_t i=(MATRIX_HEIGHT-1)*Dbpl,j=(MATRIX_HEIGHT-1)*Mbpl; i>=0; i-=Dbpl,j-=Mbpl)
          {
            for (k=0; k<MATRIX_WIDTH; k+=8)
            {
              if ((uint8_t)(0xff00 >> _min(MATRIX_WIDTH - k, 8)) != Mask[j + (k / 8)])
                break;
            }
            if (k >= MATRIX_WIDTH)
            {
              memmove(&Data[Dbpl], &Data[0], i);
              memset(&Data[0], 0, Dbpl);
              memmove(&Mask[Mbpl], &Mask[0], j);
              memset(&Mask[0], 0, Mbpl);
              i+=Dbpl;
              j+=Mbpl;
            }
          }
        }
      }
      else
      {
        if (CurrentBlock.GetXChange() >= 0) // We have a current block
        {
          // Check for user input
          if ( currentInput == ROTATE )
          {
            currentInput = NONE;
            if ((CurrentBlock.GetCurrentFrame() % 2) == 1)
            {
              if (CurrentBlock.GetXChange() == 0)
                CurrentBlock.m_X = _min(CurrentBlock.m_X, MATRIX_WIDTH - TETRIS_SPR_WIDTH);
              else if ((CurrentBlock.GetXChange() != 3) && (CurrentBlock.GetFlags() & SPRITE_EDGE_X_MAX))
                --CurrentBlock.m_X;
            }
            CurrentBlock.IncreaseFrame();
            Sprites.DetectCollisions(&CurrentBlock);
            if (CurrentBlock.GetFlags() & SPRITE_COLLISION)
              CurrentBlock.DecreaseFrame();
          }
         
          if ( currentInput == LEFT && (! (CurrentBlock.GetFlags() & SPRITE_EDGE_X_MIN)) )
          {
            currentInput = NONE;
            CurrentBlock.m_X--;
            Sprites.DetectCollisions(&CurrentBlock);
            if (CurrentBlock.GetFlags() & SPRITE_COLLISION)
              CurrentBlock.m_X++;
          }
         
          else if ( currentInput == RIGHT && (! (CurrentBlock.GetFlags() & SPRITE_EDGE_X_MAX)) )
          {
            currentInput = NONE;
            CurrentBlock.m_X++;
            Sprites.DetectCollisions(&CurrentBlock);
            if (CurrentBlock.GetFlags() & SPRITE_COLLISION)
              CurrentBlock.m_X--;
          }
         
          if ( currentInput == DOWN )
          {
            currentInput = NONE;
            CurrentBlock.SetYCounter(1);
          }
            
          // Do block checks for bottom or collision
          if (CurrentBlock.GetYCounter() <= 1)
          {
            if (CurrentBlock.GetFlags() & SPRITE_EDGE_Y_MIN)
              NextBlock = true;
            else
            {
              --CurrentBlock.m_Y;
              Sprites.DetectCollisions(&CurrentBlock);
              ++CurrentBlock.m_Y;
              if (CurrentBlock.GetFlags() & SPRITE_COLLISION)
              {
                // Block has collided check for game over
                int16_t MaxY = MATRIX_HEIGHT - 2;
                if ((CurrentBlock.GetCurrentFrame() % 2) == 1)
                {
                  if (CurrentBlock.GetXChange() == 0)
                    MaxY -= 2;
                  else if (CurrentBlock.GetXChange() != 3)
                    MaxY -= 1;
                }
                else if (CurrentBlock.GetXChange() == 0)
                    ++MaxY;
                if (CurrentBlock.m_Y < MaxY)
                  NextBlock = true;
                else
                {
                  // Game over
                  CurrentBlock.SetYCounter(2);  // Stop last block moving down!
                  AttractMode = true;
                  if (LastScore > HighScore)
                  {
                    HighScore = LastScore;
                    sprintf((char *)GameOverMsg, "%sGAME OVER%sNEW HIGH SCORE %u%s", BlankMsg, BlankMsg, LastScore, BlankMsg);
                  }
                  else
                    sprintf((char *)GameOverMsg, "%sGAME OVER%sSCORE %u%s", BlankMsg, BlankMsg, LastScore, BlankMsg);
                  sprintf((char *)AttractMsg, "%sTETRIS%sSCORE %u%sHIGH %u%sANY BUTTON TO START%s", BlankMsg, BlankMsg, LastScore, BlankMsg, HighScore, BlankMsg, BlankMsg);
                  TetrisMsg.SetText(GameOverMsg, strlen((char *)GameOverMsg));
                  TetrisMsg.SetBackgroundMode(BACKGND_DIMMING, 0x40);
                }
              }
            }
          }
        }
        if (NextBlock)  // Start new block
        {
          if (CurrentBlock.GetXChange() >= 0) // We have a current block so add to playfield before creating new block
          {
            Playfield.Combine(CurrentBlock.m_X, CurrentBlock.m_Y, &CurrentBlock);
            Sprites.RemoveSprite(&CurrentBlock);
            // Make completed lines highlight sprite & score
            memset(CompletedLinesData, 0, sizeof(CompletedLinesData));
            CompletedLines.m_Y = -1;
            uint8_t *Mask = PlayfieldMask;
            uint16_t Mbpl = (MATRIX_WIDTH + 7) / 8;
            int16_t j, numlines = 0;
            for (int16_t i=(MATRIX_HEIGHT-1)*Mbpl, y=0; i>=0; i-=Mbpl,++y)
            {
              for (j=0; j<MATRIX_WIDTH; j+=8)
              {
                if ((uint8_t)(0xff00 >> _min(MATRIX_WIDTH - j, 8)) != Mask[i + (j / 8)])
                  break;
              }
              if (j >= MATRIX_WIDTH)
              {
                if (CompletedLines.m_Y == -1)
                  CompletedLines.m_Y = y;
                memset(&CompletedLinesData[((TETRIS_SPR_HEIGHT - 1) - (y - CompletedLines.m_Y)) * Mbpl], 0xff, Mbpl);
                numlines++;
              }
            }
            if (numlines > 0)
            {
              CompletedLines.SetXCounter(15);  // Set delay for highlight display to 15 loops
              Sprites.AddSprite(&CompletedLines);
            }
            LastScore += 1;
            if (numlines == 1)
              LastScore += 4;
            else if (numlines == 2)
              LastScore += 12;
            else if (numlines == 3)
              LastScore += 20;
            else if (numlines == 4)
              LastScore += 40;
            TotalLines += numlines;
            DropDelay = _max(1, INITIAL_DROP_FRAMES - (TotalLines / 5));
          }
          // Start new block
          uint8_t j = random8(sizeof(TetrisSprData) / sizeof(TetrisSprData[0]));
          CurrentBlock.Setup(TETRIS_SPR_WIDTH, TETRIS_SPR_WIDTH, TetrisSprData[j], 4, _3BIT, TetrisColours, TetrisSprMask[j]);
          CurrentBlock.SetPositionFrameMotionOptions((MATRIX_WIDTH/2)-1, MATRIX_HEIGHT, 0, 0, 0, 0, -1, DropDelay, SPRITE_DETECT_COLLISION | SPRITE_DETECT_EDGE);
          CurrentBlock.SetXChange(j);
          Sprites.AddSprite(&CurrentBlock);
          NextBlock = false;
        }
        Sprites.UpdateSprites();
      }
    }
    Sprites.RenderSprites();
    if (AttractMode)
    {
      if (TetrisMsg.UpdateText() == -1)
      {
        TetrisMsg.SetText(AttractMsg, strlen((char *)AttractMsg));
        TetrisMsg.SetBackgroundMode(BACKGND_LEAVE);
        Sprites.RemoveSprite(&CurrentBlock);
        memset(PlayfieldData, 0, sizeof(PlayfieldData));
        memset(PlayfieldMask, 0, sizeof(PlayfieldMask));
      }
    }
    FastLED.show();
  }
  if(SerialBT.available()){
    char keyPress = (char)SerialBT.read();
    switch(keyPress) {
      case 'w':
        currentInput = ROTATE;
        break;
      case 'a':
        currentInput = LEFT;
        break;
      case 's':
        currentInput = DOWN;
        break;
      case 'd':
        currentInput = RIGHT;
        break;
    }
    Serial.println(currentInput);
  }
}

使用特权

评论回复

相关帖子

沙发
keer_zu|  楼主 | 2025-5-21 14:35 | 只看该作者
只有背景的显示效果:
// TETRIS
#include <FastLED.h>
#include <LEDMatrix.h>
#include <LEDText.h>
#include <FontMatrise.h>

#define LED_PIN        15
#define COLOR_ORDER    GRB
#define CHIPSET        WS2812B
#define MATRIX_WIDTH   30
#define MATRIX_HEIGHT  15
#define MATRIX_TYPE    HORIZONTAL_MATRIX

// NOTE the '-' sign before the width, this is due to my leds matrix origin being on the right hand side
cLEDMatrix<MATRIX_WIDTH, -MATRIX_HEIGHT, MATRIX_TYPE> leds;


#define TARGET_FRAME_TIME    15  // Desired update rate, though if too many leds it will just run as fast as it can!

uint16_t PlasmaTime, PlasmaShift;

void setup()
{
  FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds[0], leds.Size());
  FastLED.setBrightness(200);
  FastLED.clear(true);
  FastLED.show();

PlasmaShift = (random8(0, 5) * 32) + 64;
  PlasmaTime = 0;
}

void loop()
{
    FastLED.clear();

    // Fill background with dim plasma
    #define PLASMA_X_FACTOR  24
    #define PLASMA_Y_FACTOR  24
    for (int16_t x=0; x<MATRIX_WIDTH; x++)
    {
      for (int16_t y=0; y<MATRIX_HEIGHT; y++)
      {
        int16_t r = sin16(PlasmaTime) / 256;
        int16_t h = sin16(x * r * PLASMA_X_FACTOR + PlasmaTime) + cos16(y * (-r) * PLASMA_Y_FACTOR + PlasmaTime) + sin16(y * x * (cos16(-PlasmaTime) / 256) / 2);
        leds(x, y) = CHSV((uint8_t)((h / 256) + 128), 255, 64);
      }
    }

    uint16_t OldPlasmaTime = PlasmaTime;
    PlasmaTime += PlasmaShift;
    if (OldPlasmaTime > PlasmaTime)
      PlasmaShift = (random8(0, 5) * 32) + 64;

    FastLED.show();
    delay(10);
  
}

使用特权

评论回复
板凳
keer_zu|  楼主 | 2025-5-21 21:19 | 只看该作者
// TETRIS
#include <FastLED.h>
#include <LEDMatrix.h>


#define LED_PIN        15
#define COLOR_ORDER    GRB
#define CHIPSET        WS2812B
#define MATRIX_WIDTH   30
#define MATRIX_HEIGHT  15
#define MATRIX_TYPE    HORIZONTAL_MATRIX

// NOTE the '-' sign before the width, this is due to my leds matrix origin being on the right hand side
cLEDMatrix<MATRIX_WIDTH, -MATRIX_HEIGHT, MATRIX_TYPE> leds;


#define TARGET_FRAME_TIME    15  // Desired update rate, though if too many leds it will just run as fast as it can!

uint16_t PlasmaTime, PlasmaShift;

void setup()
{
  FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds[0], leds.Size());
  FastLED.setBrightness(200);
  FastLED.clear(true);
  FastLED.show();

PlasmaShift = (random8(0, 5) * 32) + 64;
  PlasmaTime = 0;
}

void loop()
{
    // Fill background with dim plasma
    #define PLASMA_X_FACTOR  24
    #define PLASMA_Y_FACTOR  24
    for (int16_t x=0; x<MATRIX_WIDTH; x++)
    {
      for (int16_t y=0; y<MATRIX_HEIGHT; y++)
      {
        int16_t r = sin16(PlasmaTime) / 256;
        int16_t h = sin16(x * r * PLASMA_X_FACTOR + PlasmaTime) + cos16(y * (-r) * PLASMA_Y_FACTOR + PlasmaTime) + sin16(y * x * (cos16(-PlasmaTime) / 256) / 2);
        leds(x, y) = CHSV((uint8_t)((h / 256) + 128), 255, 64);
      }
    }

    uint16_t OldPlasmaTime = PlasmaTime;
    PlasmaTime += PlasmaShift;
    if (OldPlasmaTime > PlasmaTime)
      PlasmaShift = (random8(0, 5) * 32) + 64;

    FastLED.show();
    delay(50);
  
}

使用特权

评论回复
地板
keer_zu|  楼主 | 2025-5-22 08:19 | 只看该作者
限制颜色和平滑的效果(不理想)
// TETRIS
#include <FastLED.h>
#include <LEDMatrix.h>

#define LED_PIN        15
#define COLOR_ORDER    GRB
#define CHIPSET        WS2812B
#define MATRIX_WIDTH   30
#define MATRIX_HEIGHT  15
#define MATRIX_TYPE    HORIZONTAL_MATRIX

cLEDMatrix<MATRIX_WIDTH, -MATRIX_HEIGHT, MATRIX_TYPE> leds;

#define TARGET_FRAME_TIME    15
uint16_t PlasmaTime, PlasmaShift;

void setup() {
  FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds[0], leds.Size());
  FastLED.setBrightness(200);
  FastLED.clear(true);
  PlasmaShift = (random8(0, 5) * 16) + 32;  // 降低变化速度
  PlasmaTime = 0;
}

void loop() {
  static uint8_t lastHue = 128;  // 用于色相平滑过渡
  FastLED.clear();

  // 更平滑的波形参数
  #define PLASMA_X_FACTOR  16  // 降低X/Y方向波形频率
  #define PLASMA_Y_FACTOR  16
  #define HUE_START  96       // 青色
  #define HUE_END    220       // 紫色
  
  for (int16_t x=0; x<MATRIX_WIDTH; x++) {
    for (int16_t y=0; y<MATRIX_HEIGHT; y++) {
      // 使用更平滑的波形组合
      int16_t r = sin16(PlasmaTime * 0.7) / 512;  // 降低振幅变化速度
      int16_t h = sin16(x * PLASMA_X_FACTOR + PlasmaTime * 0.5)
                + cos16(y * PLASMA_Y_FACTOR + PlasmaTime * 0.3)
                + sin16((x + y) * 64 + PlasmaTime * 0.4);  // 简化第三项波形

      // 非线性映射 + 低通滤波平滑色相
      uint8_t targetHue = map(h, -32768, 32767, HUE_START, HUE_END);
      uint8_t hue = (targetHue * 0.3) + (lastHue * 0.7);  // 指数平滑
      lastHue = hue;

      leds(x, y) = CHSV(hue, 200, 100);
    }
  }

  // 更缓慢的时间推进
  PlasmaTime += PlasmaShift;
  if (PlasmaTime % 512 == 0)  // 定期重置避免溢出
    PlasmaShift = random8(0, 5) * 16 + 32;
   
  FastLED.show();
  delay(20);
}

使用特权

评论回复
5
keer_zu|  楼主 | 2025-5-22 10:50 | 只看该作者
随机变化的效果:

// 风场参数
const uint8_t scale = 25;       // 缩小缩放因子扩大波纹范围
const uint8_t speed = 2;        // 降低运动速度
const int8_t windDirection = 1;
const uint8_t blurAmount = 48;  // 新增模糊系数
uint16_t z = 0;
// 优化的噪声生成函数
void generateNoise(uint8_t *noiseData, uint16_t zOffset) {
  for(uint8_t y=0; y<MATRIX_HEIGHT; y++) {
    for(uint8_t x=0; x<MATRIX_WIDTH; x++) {
      uint8_t xOffset = windDirection * beatsin8(
        3,                    // 降低波浪频率
        0, 15,               // 减小振幅范围
        0,
        (x * 2 + y * 3)      // 优化相位关系
      );
      
      noiseData[XY(x,y)] = inoise8(
        (x + xOffset) * scale,
        y * scale * 1.2,     // Y轴增加缩放差异
        zOffset
      );
    }
  }
}


void wind_vision()
{
  static uint32_t lastUpdate = 0;
  static uint8_t blendVal = 0;
  
  if(millis() - lastUpdate > 30) {
    lastUpdate = millis();
   
    // 双缓冲噪声生成
    uint8_t noiseData1[NUM_LEDS];
    uint8_t noiseData2[NUM_LEDS];
   
    // 生成两帧噪声数据
    generateNoise(noiseData1, z);
    generateNoise(noiseData2, z + 50); // 50个时间单位偏移
   
    // 混合两帧数据
    for(int i=0; i<NUM_LEDS; i++) {
      uint8_t finalNoise = blend8(noiseData1[i], noiseData2[i], blendVal);
      
      // 扩展颜色映射范围
      uint8_t hue = map(finalNoise, 40, 210, 180, 255); // 颜色范围180-255
      uint8_t val = map(finalNoise, 60, 190, 50, 220);  // 保持亮度动态
      
      leds[i] = CHSV(hue, 245, val); // 略微降低饱和度
    }
   
    // 应用二维模糊
    blur2d(leds, MATRIX_WIDTH, MATRIX_HEIGHT, blurAmount);
   
    z += speed;
    blendVal += 3; // 控制混合速度
    FastLED.show();
  }
}


使用特权

评论回复
6
keer_zu|  楼主 | 2025-5-23 22:29 | 只看该作者

#include <FastLED.h>

// ========== 硬件配置 ==========
#define LED_PIN       15
#define LED_TYPE      WS2812B
#define COLOR_ORDER   GRB
#define MATRIX_WIDTH  30
#define MATRIX_HEIGHT 15
#define NUM_LEDS      (MATRIX_WIDTH * MATRIX_HEIGHT)
CRGB leds[NUM_LEDS];

// ========== 动画参数 ==========
#define FRAME_RATE    30
#define NOISE_SCALE   0.1f
#define FLOW_SPEED    0.8f
#define HUE_SPEED     0.02f
#define BRIGHTNESS    80

// ========== 全局变量 ==========
float z;
uint8_t hue_base;

// ========== 完整的perm数组 ==========
const uint8_t perm[256] = {
  151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225,
  140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148,
  247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32,
  57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175,
  74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122,
  60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54,
  65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169,
  200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64,
  52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212,
  207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213,
  119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9,
  129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104,
  218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241,
  81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157,
  184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93,
  222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180
};

// ========== 修改后的噪声函数(重命名避免冲突)==========
uint8_t custom_noise(uint16_t x, uint16_t y, uint16_t z) {
  uint8_t a  = perm[(x>>8)&0xFF] + (y>>8);
  uint8_t b  = perm[(y>>8)&0xFF] + (z>>8);
  uint8_t aa = perm[a] + (z>>8);
  uint8_t ba = perm[b] + (x>>8);
  
  uint8_t u = x & 0xFF;
  uint8_t v = y & 0xFF;
  
  return lerp8by8(lerp8by8(perm[aa], perm[ba], u),
                lerp8by8(perm[aa+1], perm[ba+1], u), v);
}

// ========== 坐标映射函数 ==========
uint16_t XY(uint8_t x, uint8_t y) {
  if(y % 2 == 0) {
    return y * MATRIX_WIDTH + x;
  } else {
    return (y + 1) * MATRIX_WIDTH - x - 1;
  }
}

// ========== 主渲染函数(参数类型修正)==========
void renderFrame() {
  static float xOffset = 0;
  static float yOffset = 0;
  
  xOffset += FLOW_SPEED * 0.7;
  yOffset += FLOW_SPEED * 0.5;
  hue_base += HUE_SPEED * 255;
  z += 0.05;
  
  for(uint8_t y = 0; y < MATRIX_HEIGHT; y++) {
    for(uint8_t x = 0; x < MATRIX_WIDTH; x++) {
      // 转换为整数参数
      uint16_t nx = (uint16_t)(x * NOISE_SCALE * 256 + xOffset * 256);
      uint16_t ny = (uint16_t)(y * NOISE_SCALE * 256 + yOffset * 256);
      uint16_t nz = (uint16_t)(z * 256);
      
      uint8_t noise = (
          custom_noise(nx, ny, nz) * 0.6 +
          custom_noise(nx*2, ny*2, nz) * 0.4
      );
      
      CRGB color = CHSV(noise + hue_base, 255, noise);
      leds[XY(x,y)] = color;
    }
  }
}

void setup() {
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();
  randomSeed(analogRead(0));
  z = random(65536);
}

void loop() {
  static uint32_t lastFrame = 0;
  uint32_t frameInterval = 1000 / FRAME_RATE;
  
  if(millis() - lastFrame >= frameInterval) {
    renderFrame();
    FastLED.show();
    lastFrame = millis();
  }
  yield();
}

使用特权

评论回复
7
keer_zu|  楼主 | 2025-5-23 22:35 | 只看该作者

颜色受到限制的版本:
#include <FastLED.h>

// ========== 硬件配置 ==========
#define LED_PIN      15
#define LED_TYPE      WS2812B
#define COLOR_ORDER   GRB
#define MATRIX_WIDTH  30
#define MATRIX_HEIGHT 15
#define NUM_LEDS      (MATRIX_WIDTH * MATRIX_HEIGHT)
CRGB leds[NUM_LEDS];

// ========== 动画参数 ==========
#define FRAME_RATE    30
#define NOISE_SCALE   0.05f    // 更细腻的噪声图案
#define FLOW_SPEED    0.3f     // 慢速流动
#define HUE_SPEED     0.005f   // 缓慢色调变化
#define BRIGHTNESS    80       // 中等亮度

// ========== 全局变量 ==========
float z;
uint8_t hue_base;

// ========== perm数组(保持不变)==========
const uint8_t perm[256] = {
  151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,
  8,99,37,240,21,10,23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,
  35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,74,165,71,
  134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,
  55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,89,
  18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,
  250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,
  189,28,42,223,183,170,213,119,248,152,2,44,154,163,70,221,153,101,155,167,43,
  172,9,129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,218,246,97,
  228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,14,239,107,
  49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,
  236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
};

// ========== 自定义噪声函数 ==========
uint8_t custom_noise(uint16_t x, uint16_t y, uint16_t z) {
  uint8_t a  = perm[(x>>8)&0xFF] + (y>>8);
  uint8_t b  = perm[(y>>8)&0xFF] + (z>>8);
  uint8_t aa = perm[a] + (z>>8);
  uint8_t ba = perm[b] + (x>>8);
  
  uint8_t u = x & 0xFF;
  uint8_t v = y & 0xFF;
  
  return lerp8by8(lerp8by8(perm[aa], perm[ba], u),
                lerp8by8(perm[aa+1], perm[ba+1], u), v);
}

// ========== 蛇形映射函数 ==========
uint16_t XY(uint8_t x, uint8_t y) {
  if(y % 2 == 0) {
    return y * MATRIX_WIDTH + x;
  } else {
    return (y + 1) * MATRIX_WIDTH - x - 1;
  }
}

// ========== 主渲染函数 ==========
void renderFrame() {
  static float xOffset = 0;
  static float yOffset = 0;
  
  // 缓慢更新位置参数
  xOffset += FLOW_SPEED * 0.7;
  yOffset += FLOW_SPEED * 0.5;
  hue_base += HUE_SPEED * 255;
  z += 0.02;
  
  for(uint8_t y = 0; y < MATRIX_HEIGHT; y++) {
    for(uint8_t x = 0; x < MATRIX_WIDTH; x++) {
      // 生成噪声参数
      uint16_t nx = (uint16_t)(x * NOISE_SCALE * 256 + xOffset * 256);
      uint16_t ny = (uint16_t)(y * NOISE_SCALE * 256 + yOffset * 256);
      uint16_t nz = (uint16_t)(z * 256);
      
      // 混合噪声层
      uint8_t noise = (
          custom_noise(nx, ny, nz) * 0.6 +
          custom_noise(nx*2, ny*2, nz) * 0.4
      );
      
      // 颜色处理(限制在100-220 Hue范围)
      uint8_t rawHue = noise + hue_base;
      uint8_t scaledHue = scale8(rawHue, 120) + 100; // 120 = 60*2
      CRGB color = CHSV(scaledHue, 200, noise);      // 适当降低饱和度
      
      leds[XY(x,y)] = color;
    }
  }
}

// ========== 初始化 ==========
void setup() {
  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);
  FastLED.clear();
  randomSeed(analogRead(0));
  z = random(65536);  // 随机初始Z值
}

// ========== 主循环 ==========
void loop() {
  static uint32_t lastFrame = 0;
  uint32_t frameInterval = 1000 / FRAME_RATE;
  
  if(millis() - lastFrame >= frameInterval) {
    renderFrame();
    FastLED.show();
    lastFrame = millis();
  }
  yield();
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1445

主题

12781

帖子

53

粉丝