#include <FastLED.h>
#include <avr/pgmspace.h>
// ================= MATRIX CONFIG =================
#define LED_PIN 12
#define CHIPSET WS2812
#define COLOR_ORDER GRB
#define BRIGHTNESS 200
const uint8_t matrixWidth = 32;
const uint8_t matrixHeight = 16;
#define NUM_LEDS (matrixWidth * matrixHeight)
CRGB leds[NUM_LEDS];
#define SERPENTINE true
// ================= CURRENT COLOR =================
CRGB currentColor = CRGB::Red;
bool rainbowMode = false;
// ================= SERIAL VARIABLES =================
String serialBuffer = "";
int countdownValue = 0;
bool countdownActive = false;
bool countdownPaused = false;
unsigned long lastUpdate = 0;
// ================= 5x7 FONT =================
const uint8_t font5x7[][5] PROGMEM = {
{62,81,73,69,62}, //0
{0,66,127,64,0}, //1
{98,81,73,73,70}, //2
{34,65,73,73,54}, //3
{24,20,18,127,16},//4
{39,69,69,69,57}, //5
{60,74,73,73,48}, //6
{1,113,9,5,3}, //7
{54,73,73,73,54}, //8
{6,73,73,41,30} //9
};
// ================= XY FUNCTION =================
uint16_t XY(uint8_t x, uint8_t y)
{
if (SERPENTINE && (y & 1))
return (y * matrixWidth) + (matrixWidth - 1 - x);
else
return (y * matrixWidth) + x;
}
// ================= DRAW PIXEL =================
void drawPixel(int x,int y,CRGB color)
{
if(x<0||y<0||x>=matrixWidth||y>=matrixHeight) return;
leds[XY(x,y)] = color;
}
// ================= DRAW CHAR =================
void drawChar(int x,char c)
{
if(c<'0'||c>'9') return;
for(int col=0; col<5; col++)
{
uint8_t line = pgm_read_byte(&(font5x7[c-'0'][col]));
for(int row=0; row<7; row++)
{
if(line & (1<<row))
drawPixel(x+col,row+4,currentColor);
}
}
}
// ================= DISPLAY NUMBER =================
void displayNumber(int num)
{
FastLED.clear();
String s = String(num);
int startX = (matrixWidth - s.length()*6)/2;
for(int i=0;i<s.length();i++)
drawChar(startX + i*6 , s[i]);
FastLED.show();
}
// ================= DISPLAY TEXT =================
void displayText(String txt)
{
FastLED.clear();
int startX = (matrixWidth - txt.length()*6)/2;
for(int i=0;i<txt.length();i++)
drawChar(startX + i*6 , txt[i]);
FastLED.show();
}
// ================= SCROLL TEXT =================
void scrollText(String txt)
{
int width = txt.length()*6;
for(int offset=matrixWidth; offset>-width; offset--)
{
FastLED.clear();
for(int i=0;i<txt.length();i++)
drawChar(offset + i*6 , txt[i]);
FastLED.show();
delay(80);
}
}
// ================= RAINBOW EFFECT =================
void rainbowEffect()
{
static uint8_t hue=0;
for(int i=0;i<NUM_LEDS;i++)
leds[i]=CHSV(hue+i*2,255,255);
FastLED.show();
hue++;
}
// ================= PROCESS COMMAND =================
void processCommand(String cmd)
{
cmd.trim();
cmd.toUpperCase();
if(cmd=="HELP")
{
Serial.println("Commands:");
Serial.println("MSG TEXT");
Serial.println("SCROLL TEXT");
Serial.println("NUMBER (example 60)");
Serial.println("PAUSE");
Serial.println("RESUME");
Serial.println("STOP");
Serial.println("COLOR R,G,B");
Serial.println("RED GREEN BLUE YELLOW CYAN WHITE");
Serial.println("RAINBOW");
}
else if(cmd.startsWith("MSG "))
{
String msg = cmd.substring(4);
displayText(msg);
}
else if(cmd.startsWith("SCROLL "))
{
String msg = cmd.substring(7);
scrollText(msg);
}
else if(cmd.startsWith("COLOR "))
{
int r,g,b;
sscanf(cmd.c_str(),"COLOR %d,%d,%d",&r,&g,&b);
currentColor = CRGB(r,g,b);
rainbowMode=false;
Serial.println("Color Updated");
}
else if(cmd=="RED"){currentColor=CRGB::Red;rainbowMode=false;}
else if(cmd=="GREEN"){currentColor=CRGB::Green;rainbowMode=false;}
else if(cmd=="BLUE"){currentColor=CRGB::Blue;rainbowMode=false;}
else if(cmd=="YELLOW"){currentColor=CRGB::Yellow;rainbowMode=false;}
else if(cmd=="CYAN"){currentColor=CRGB::Cyan;rainbowMode=false;}
else if(cmd=="WHITE"){currentColor=CRGB::White;rainbowMode=false;}
else if(cmd=="RAINBOW")
{
rainbowMode=true;
}
else if(cmd=="PAUSE")
{
countdownPaused=true;
}
else if(cmd=="RESUME")
{
countdownPaused=false;
}
else if(cmd=="STOP")
{
countdownActive=false;
FastLED.clear();
FastLED.show();
}
else
{
bool isNumber=true;
for(int i=0;i<cmd.length();i++)
if(!isDigit(cmd[i])) isNumber=false;
if(isNumber)
{
countdownValue = cmd.toInt();
countdownActive=true;
countdownPaused=false;
lastUpdate=millis();
displayNumber(countdownValue);
}
}
}
// ================= SETUP =================
void setup()
{
Serial.begin(9600);
FastLED.addLeds<CHIPSET,LED_PIN,COLOR_ORDER>(leds,NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
FastLED.clear();
FastLED.show();
Serial.println("16x32 MATRIX READY");
Serial.println("TYPE HELP");
}
// ================= LOOP =================
void loop()
{
while(Serial.available())
{
char c = Serial.read();
if(c=='\n'||c=='\r')
{
if(serialBuffer.length())
{
processCommand(serialBuffer);
serialBuffer="";
}
}
else
serialBuffer+=c;
}
if(rainbowMode)
rainbowEffect();
if(countdownActive && !countdownPaused)
{
if(millis()-lastUpdate>=1000)
{
lastUpdate=millis();
countdownValue--;
if(countdownValue<=0)
{
countdownActive=false;
displayText("DONE");
}
else
displayNumber(countdownValue);
}
}
}