// Segment clock made of WS2812 led strip
// fire width 19 height 7
// testing for segment clock
// Stolen from fire_201 byte by ldir
// Perlin fire
// https://pastebin.com/jSSVSRi6
//Perlin noise fire procedure
//16x16 rgb led matrix demo
//Yaroslaw Turbin, 22.06.2020
//https://vk.com/ldirko
//https://www.reddit.com/user/ldirko/
//https://www.reddit.com/r/FastLED/comments/hgu16i/my_fire_effect_implementation_based_on_perlin/
//idea in make perlin noise with time offset X and Z coord
//this automatic scroll fire pattern
//and distort fire noise.
//then substract Y based coodrd value to shift
//fire color (not brightness) in palette.
//this fadeout color from bottom matrix to up.
//this need some palette tweak for good looking fire color
#include "FastLED.h"
#include <Wire.h>
#include <Time.h>
#include "DS3231.h"
#define seconds() ((unsigned long)millis()/1000)
#define minutes() ((unsigned int)seconds()/60)
// LEDs pin
#define DATA_PIN 3
// LED brightness
#define BRIGHTNESS 255
#define OFF_LED CRGB(192,192,192)
// Matrix size
#define NUM_ROWS 7
#define NUM_COLS 19
#define NUM_LEDS NUM_ROWS * NUM_COLS
#define DISP_PIN 8
// Display size
#define SEG_LEDS 68 // fire: 3*24 = 72, seg disp: 4 * 14 + 3 * 4 = 68
#define SEG_DOTS 33 // 33 and 34
// SEGMENT clock
#define SEG_SEGMENTS 4 // 0,1,2,3
#define SEG_SEG_SIZE 18 // leds from one seg char to another
boolean SEG_FONT[][14] = {
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, // 0
{0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, // 1
{1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1}, // 2
{1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1}, // 3
{0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1}, // 4
{1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1}, // 5
{1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // 6
{1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, // 7
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // 8
{1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1}, // 9
{1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0}, // C
{1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1}, // % left half
{1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1} // % right half
};
// Define the arrays of leds
CRGB leds[NUM_LEDS];
CRGB disp[SEG_LEDS];
boolean seg[SEG_LEDS];
// These are physical leds positions on disp
// 1 2 4 6
// 19 22 24
// 38 41 42 43
// 58 59 63
// 76 79 80 81
// 95 98 100
// 115 116 118 120 121 122 123
uint8_t disp_positions[] = {1,2,22,41,79,98,116,115,95,76,38,19,58,59,118,80,42,4,6,7,27,46,84,103,121,120,100,81,43,24,63,64,123,85,47,9,11,12,32,51,89,108,126,125,105,86,48,29,68,69,128,90,52,14,16,17,37,56,94,113,131,130,110,91,53,34,73,74};
DEFINE_GRADIENT_PALETTE( firepal ) { // define fire palette
0, 90, 60, 60, //black
64, 255, 0, 0, // red
140, 255, 255, 0, // yellow
180, 255, 255, 255, // white
190, 90, 0, 0, // brown
255, 60, 60, 60 // black
};
CRGBPalette16 myPal = firepal;
time_t t;
int i;
void setup() {
byte t_sec, t_min, t_hr, t_wd, t_dy, t_mon, t_yr;
// delay(2000); // POWER UP
// Serial.begin(9600);
Wire.begin(); // Serial
// DS3231
readDS3231time(&t_sec, &t_min, &t_hr, &t_wd, &t_dy, &t_mon, &t_yr);
setTime(t_hr, t_min, t_sec, t_dy, t_mon, 2000 + t_yr);
// set the initial time here:
// if (yr < 1) {
// // DS3231 seconds, minutes, hours, wday, date, month, year
// setDS3231time( 0, 27, 23, 7, 12, 6, 16);
// }
t = now();
// DEBUG
// Serial.print("Date/Time: ");
// Serial.print(day(t), DEC);
// Serial.print(".");
// Serial.print(month(t), DEC);
// Serial.print(".");
// Serial.print(year(t) - 2000, DEC);
// Serial.print(" ");
// Serial.print(hour(t), DEC);
// Serial.print(":");
// Serial.print(minute(t), DEC);
// Serial.println(".");
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
FastLED.addLeds<NEOPIXEL, DISP_PIN>(disp, SEG_LEDS);
FastLED.setBrightness(BRIGHTNESS);
// JUST TO SEE POSITIONS:
// for (int i=0; i<min(NUM_LEDS,SEG_LEDS) ; i++ ) {
// leds[i] = CRGB::Red;
// disp[i] = CRGB::Red;
// FastLED.delay(100);
// }
}
void loop() {
int a = millis();
static int last_seconds; boolean secondely = false;
static int last_minutes; boolean minutely = false;
secondely = (last_seconds < seconds() ? true : false);
minutely = (last_minutes < minutes() ? true : false);
// Update once per second
if (secondely) {
setSegs();
seg[SEG_DOTS] = seg[SEG_DOTS+1] = (last_seconds % 2 == 0 ? true : false);
}
// DEBUG
// if (minutely) {
// Serial.print("Time: ");
// Serial.print(hour(t), DEC);
// Serial.print(":");
// Serial.print(minute(t), DEC);
// Serial.print(":");
// Serial.print(second(t), DEC);
// Serial.println(".");
// }
for (int i = 0; i < NUM_COLS; i++) {
for (int j = 0; j < NUM_ROWS; j++) {
leds[XY(i,j)] = ColorFromPalette (myPal, // palette
qsub8 ( inoise8 (i*60 , j*60+a , a/3), abs8(j - (NUM_ROWS-1)) * 255 / (NUM_ROWS+4)), // color index
BRIGHTNESS); // brightness
}
}
// copy leds values into disp by their positions
for (int i = 0 ; i < SEG_LEDS ; i++) {
if (seg[i]) {
disp[i] = leds[ disp_positions[i] ];
} else {
disp[i] = OFF_LED;
}
}
if ( secondely ) {
last_seconds = seconds();
last_minutes = minutes();
// last_hours = hours();
}
// last_millis = millis();
FastLED.delay(10);
}
uint8_t XY (uint8_t x, uint8_t y) { return (y * NUM_COLS + x);} //simple function to find led number in led matrix,
//change this to your routine
//or generate XY function for your matrix there:
//https://macetech.github.io/FastLED-XY-Map-Generator/
// We need to calculate which leds are off and which are on.
// on leds may be affected with fire.
// It's like mask.
void setSegs() {
t = now(); // maybe localtime() should be used
int h, m;
h = minute(t);
m = second(t);
// Swith off all segs:
for (int i=0; i<=SEG_LEDS; i++ ) {
seg[i] = false;
}
// leading zero at 08:25
if (h>9) {
segMarkSegments( 0, (int) h / 10 );
}
segMarkSegments( 1, (int) h % 10 );
segMarkSegments( 2, (int) m / 10 );
segMarkSegments( 3, (int) m % 10 );
}
// Write character at position
// use "seg" array to note, which segments can be fired
// position is display position: 0-3
void segMarkSegments(byte position, byte font_char) {
byte j, disp_position;
// strip pos = position * 18
// for each of 7 segment display (14)
for ( j = 0 ; j < 14 ; j++ ) {
disp_position = position * SEG_SEG_SIZE + j;
if ( disp_position < NUM_LEDS ) {
if ( SEG_FONT[font_char][j] == 1 ) {
seg[disp_position] = true;
} else {
seg[disp_position] = false;
}
}
}
}
// // Write character at position
// // use "leds" array
// void seg_write(byte position, byte font_char, CRGB color) {
// byte j, strip_position;
// // strip pos = position * 18
// // for each of 7 segment display (14)
// for ( j = 0 ; j < 14 ; j++ ) {
// strip_position = position * SEG_SEG_SIZE + j;
// if ( strip_position < NUM_LEDS ) {
// if ( SEG_FONT[font_char][j] == 1 ) {
// if ( color.r == 0 && color.g == 0 && color.b == 0 ) {
// leds[strip_position] = ColorFromPalette( gPal, COLOR_INDEX, 255, LINEARBLEND);
// } else {
// leds[strip_position] = color;
// }
// } else {
// leds[strip_position] = BACKGROUND_COLOR;
// }
// }
// }
// }