#include <ArduinoJson.h>
#include <SD.h>
#include <SPI.h>
#include <StreamUtils.h>
#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
#include "gifs.h"
#include "images.h"
extern const uint8_t gamma8[];
#define MAX_ANIMATION_FRAMES_ACCEPTED (5)
#define LED_NUM (1024)
#define RGB_NUM (3)
#define LED_DATA_SIZE (3)
#define DATA_PIN (26)
#define matrixWidth (32)
#define matrixHeight (32)
#define tileWidth (16)
#define tileHeight (16)
#define tilesNum (2)
#define Brightness (3)
#define BLACK (0)
#define GIF_Repetitions (8)
#define flashMemGifs (6)
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(tileWidth, tileHeight,tilesNum,tilesNum, DATA_PIN,
NEO_MATRIX_BOTTOM + NEO_MATRIX_RIGHT +
NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG
+ NEO_TILE_TOP + NEO_TILE_RIGHT + NEO_TILE_PROGRESSIVE,
NEO_GRB + NEO_KHZ800);
//this struct contains all the data we want for the gif that is currently showing.
struct Config {
uint8_t width;
uint8_t height;
uint8_t frames;
uint16_t colors;
uint8_t colors_palette[LED_NUM][RGB_NUM];
uint16_t frame_sizes[MAX_ANIMATION_FRAMES_ACCEPTED];
uint16_t animation[MAX_ANIMATION_FRAMES_ACCEPTED][LED_NUM][LED_DATA_SIZE];
};
char filePath[256] = { 0 };
char fileName[256] = { 0 };
File root,gifFile;
Config config;
// load gifs and images from the flash memory
void loadConfigurationFromFlashMem(uint8_t index) {
switch (index) {
case 0:
showJump(&matrix);
break;
case 1:
showPikachu(&matrix);
break;
case 2:
showMario(&matrix);
break;
case 3:
showPokeBall(&matrix);
break;
case 4:
showFlower(&matrix);
break;
case 5:
showFish(&matrix);
break;
}
}
void loadConfiguration(const char *filename, Config &config) {
File file = SD.open(filename);
//simpler because we dont need to specify doc size but still not the most stable, but fastest.
JsonDocument doc;
ReadBufferingStream bufferedFile(file, 64);
DeserializationError error = deserializeJson(doc, bufferedFile);
// this is slower but more stable with no flares mostly.
//DynamicJsonDocument doc(262144);
//DeserializationError error = deserializeJson(doc, file);
// DynamicJsonDocument doc(262144);
// ReadBufferingStream bufferedFile(file, 256);
// DeserializationError error = deserializeJson(doc, bufferedFile);
if (error)
Serial.println(F("Failed to read file, using default configuration"));
config.width = doc["width"];
config.height = doc["height"];
config.frames = doc["frames"];
config.colors = doc["colors"];
for(uint16_t j = 0; j < config.colors; j++){
JsonArray curr_color = doc["colors_palette"][j];
config.colors_palette[j][0] = curr_color[0];
config.colors_palette[j][1] = curr_color[1];
config.colors_palette[j][2] = curr_color[2];
}
uint8_t height = (matrixHeight-config.height)/2;
uint8_t width = (matrixWidth-config.width)/2;
for(uint8_t j = 0; j < config.frames; j++){
config.frame_sizes[j] = doc["frame_sizes"][j];
JsonArray curr_frame = doc["animation"][j];
for(uint8_t y = height; y < height+config.height; y++){
for(uint8_t x = width; x < width+config.width; x++){
uint16_t i = y*matrixWidth+x;
JsonArray curr_led = curr_frame[i];
config.animation[j][i][0] = curr_led[0];
config.animation[j][i][1] = curr_led[1];
config.animation[j][i][2] = curr_led[2];
}
}
}
file.close();
}
void setup() {
Serial.begin(115200);
while (!Serial) continue;
char text[64];
int pixelPerChar = 4;
uint8_t index = 0;
matrix.begin();
matrix.setBrightness(Brightness);
matrix.setTextWrap(false);
matrix.setTextColor(matrix.Color(255, 255, 255)); // White color
matrix.setTextSize(4);
matrix.fillScreen(BLACK);
strcpy(text, "SD ERROR");
while (!SD.begin()) {
Serial.println(F("Failed to initialize SD library"));
if(index = 0){
// Calculate the position to center the text
int16_t x = (matrix.width() - (strlen(text) * pixelPerChar)) / 2; // Assuming each character is 6 pixels wide
int16_t y = (matrix.height() - pixelPerChar) / 2; // Assuming each character is 8 pixels tall
// Draw the text
matrix.setCursor(x, y);
matrix.print(text);
// Display the text
delay(5);
matrix.show();
}
if(index == 6) index = 0;
loadConfigurationFromFlashMem(index);
index++;
matrix.fillScreen(BLACK);
}
index = 0;
strcpy(text, "SD EMPTY");
if (SD.exists("/")) {
Serial.println("SD card is not empty.");
} else {
Serial.println("SD card is empty.");
// Calculate the position to center the text
int16_t x = (matrix.width() - (strlen(text) * pixelPerChar)) / 2; // Assuming each character is 6 pixels wide
int16_t y = (matrix.height() - pixelPerChar) / 2; // Assuming each character is 8 pixels tall
// Draw the text
matrix.setCursor(x, y);
matrix.print(text);
// Display the text
delay(5);
matrix.show();
while(1){
if(index == 6) index = 0;
loadConfigurationFromFlashMem(index);
index++;
matrix.fillScreen(BLACK);
}
}
}
void loop() {
root = SD.open("/");
if(root){
gifFile = root.openNextFile();
while(gifFile)
{
if (!gifFile.isDirectory()) // play it
{
memset(fileName, 0x0, sizeof(fileName));
strcpy(fileName, gifFile.name());
snprintf(filePath, sizeof(filePath)+1, "/%s", fileName);
loadConfiguration(filePath, config);
matrix.fillScreen(BLACK);
uint8_t height = (matrixHeight-config.height)/2;
uint8_t width = (matrixWidth-config.width)/2;
for(uint8_t k = 0; k < GIF_Repetitions; k++){
for(uint8_t j = 0; j < config.frames; j++){
for(uint8_t y = height; y < height+config.height; y++){
for(uint8_t x = width; x < width+config.width; x++){
uint16_t i = y*matrixWidth+x;
uint8_t red = pgm_read_dword(&(config.animation[j][i][0]));
uint8_t green = pgm_read_dword(&(config.animation[j][i][1]));
uint8_t blue = pgm_read_dword(&(config.animation[j][i][2]));
matrix.drawPixel( x, y, matrix.Color(red,green,blue));
}
}
delay(5);
matrix.show();
delay(75);
}
}
}
gifFile.close();
gifFile = root.openNextFile();
}
root.close();
}
}