// Sand clock
//Yaroslaw Turbin, 12-01-2021
//16x16 FastLed matrix demo
//not for commercial use )
#include <FastLED.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
// RTC module at address 0x68
#define DS1307_ADDRESS 0x68
// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);
uint8_t clear = 0x00;
#define WIDTH 16
#define HEIGHT 16
#define NUM_ROWS HEIGHT
#define NUM_COLS WIDTH
#define NUM_LEDS ((WIDTH) * (HEIGHT))
CRGB leds[NUM_LEDS + 1];
CRGB ledsbuff[NUM_LEDS + 1];
uint16_t XY(uint8_t x, uint8_t y) {
if (x >= WIDTH) return NUM_LEDS;
if (y >= HEIGHT) return NUM_LEDS;
// if (y & 1)
// return (y + 1) * WIDTH - 1 - x;
// else
return y * WIDTH + x;
}
void setup()
{
FastLED.addLeds<NEOPIXEL, 2>(leds, NUM_LEDS);
LEDS.clear();
Wire.begin();
Serial.begin(9600);
lcd.begin (16,2);
lcd.backlight();
// Use a line below to customize a date and time
// sec, min, hour, month, day, day of week, year % 100
// setDateTime(40, 59, 23, 1, 4, 1, 21);
}
void loop()
{
timeLCD();
SandMachine();
PrintTime();
FastLED.show();
}
void SandMachine(){
EVERY_N_MILLISECONDS(30) {updatesand(); randomdot();countSand();} //speed of sand effect
memcpy8(leds, ledsbuff, NUM_LEDS*3);
}
void PrintTime(){
static byte numbers;
static byte colorNumber;
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(clear);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, 0x07);
uint8_t seconds = bcdToDec(Wire.read());
uint8_t minutes = bcdToDec(Wire.read());
uint8_t hours = bcdToDec(Wire.read() & 0xff);
uint8_t wday = bcdToDec(Wire.read());
uint8_t mday = bcdToDec(Wire.read());
uint8_t month = bcdToDec(Wire.read());
uint8_t year = bcdToDec(Wire.read());
//print numbers on led matrix
colorNumber = 170;
prnCHR(0,1,hours/10,colorNumber);
prnCHR(4,1,hours%10,colorNumber);
prnCHR(9,1,minutes/10,colorNumber);
prnCHR(13,1,minutes%10,colorNumber);
}
void prnCHR(byte x, byte y, byte number, byte numberhue){
const static boolean Numbers [150] = {
1,1,1,1,0,1,1,0,1,1,0,1,1,1,1, //0
0,0,1,0,1,1,0,0,1,0,0,1,0,0,1, //1
0,1,1,0,0,1,1,1,1,1,0,0,1,1,1, //2
1,1,1,0,0,1,0,1,1,0,0,1,0,1,1, //3
1,0,0,1,0,1,1,1,1,0,0,1,0,0,1, //4
1,1,0,1,0,0,1,1,1,0,0,1,1,1,1, //5
1,1,0,1,0,0,1,1,1,1,0,1,1,1,1, //6
1,1,1,1,0,1,0,0,1,0,0,1,0,0,1, //7
1,1,1,1,0,1,1,1,1,1,0,1,1,1,1, //8
1,1,1,1,0,1,1,1,1,0,0,1,0,1,1}; //9
byte numbind = number*15;
for (byte j=y; j<(y+5);j++){
for (byte i=x; i<(x+3);i++){
if (Numbers[numbind++]) leds[XY(i,j)]=CHSV(numberhue+j*8+beatsin8(10,0,20),255,255);
}
}
}
void randomdot(){
#define randomdothue 30
byte a= NUM_COLS/2; //random8(NUM_COLS/4)+NUM_COLS*3/8; //
if (!random8(4)) ledsbuff[XY(a,0)].setHue(random8(randomdothue,randomdothue+45));
}
void updatesand (){
int index, indexXadd1Y, indexXsub1Y, indexXYadd1;
for (int y=NUM_ROWS-1; y>0; y--) {
for (int x=1; x<NUM_COLS-1; x++) {
index = XY(x,y); indexXadd1Y = XY(x+1,y); indexXsub1Y = XY(x-1,y); indexXYadd1 = XY(x,y-1);
if (!ledsbuff[index] && !ledsbuff[indexXYadd1]) continue;
if (!ledsbuff[index] && ledsbuff[indexXYadd1]) {ledsbuff[index]=ledsbuff[indexXYadd1]; ledsbuff[indexXYadd1]=0;}
if (ledsbuff[index] && ledsbuff[indexXYadd1] && !ledsbuff[indexXsub1Y] && !ledsbuff[indexXadd1Y])
if (random8(2)) {ledsbuff[indexXsub1Y]=ledsbuff[indexXYadd1]; ledsbuff[indexXYadd1]=0;}
else {ledsbuff[indexXadd1Y]=ledsbuff[indexXYadd1]; ledsbuff[indexXYadd1]=0;}
if (ledsbuff[index] && ledsbuff[indexXYadd1] && !ledsbuff[indexXsub1Y] && ledsbuff[indexXadd1Y]) {ledsbuff[indexXsub1Y]=ledsbuff[indexXYadd1]; ledsbuff[indexXYadd1]=0;}
if (ledsbuff[index] && ledsbuff[indexXYadd1] && ledsbuff[indexXsub1Y] && !ledsbuff[indexXadd1Y]) {ledsbuff[indexXadd1Y]=ledsbuff[indexXYadd1]; ledsbuff[indexXYadd1]=0;}
}
}
}
void randomdel(){
for (int i=0; i< NUM_LEDS; i++) {
if (!random8(3)) ledsbuff[i]=0;
}
}
void falldown(){
int indexXYsub1, index;
for (int y=NUM_ROWS-1; y>0; y--) {
for (int x=0; x<NUM_COLS; x++) {
indexXYsub1 =XY(x,y-1); index= XY(x,y);
if (!ledsbuff[index] && ledsbuff[indexXYsub1]) {ledsbuff[index]=ledsbuff[indexXYsub1]; ledsbuff[indexXYsub1]=0;}
}
}
}
void countSand() {
uint16_t totalSand = 0;
byte rand = random8(30);
for (uint16_t i = 0; i < NUM_LEDS; i++) {
if (ledsbuff[i]) {
totalSand++;
if (totalSand >= NUM_LEDS / 3 + rand ) {randomdel(); falldown(); falldown(); falldown(); break;} //N_LEDS/3 this how many sands on screen? maybe time to delete some?
}
}
}
//____ procedures for work with display and DS1307 by arcostasi _______________
// Set the date and time of the DS1307
void setDateTime(uint8_t seconds, uint8_t minutes, uint8_t hours,
uint8_t wday, uint8_t mday, uint8_t month, uint8_t year)
{
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(clear); // Write clear, so that it can receive data
// The lines below write in the CI the date and time values
// that were placed in the variables above
Wire.write(decToBcd(seconds));
Wire.write(decToBcd(minutes));
Wire.write(decToBcd(hours));
Wire.write(decToBcd(wday));
Wire.write(decToBcd(mday));
Wire.write(decToBcd(month));
Wire.write(decToBcd(year));
Wire.write(clear);
Wire.endTransmission();
}
uint8_t decToBcd(uint8_t value)
{
// Converts the decimal number to BCD
return ((value / 10 * 16) + (value % 10));
}
uint8_t bcdToDec(uint8_t value)
{
// Converts from BCD to decimal
return ((value / 16 * 10) + (value % 16));
}
void timeLCD(){
// Read the values (date and time) of the DS1307 module
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(clear);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, 0x07);
uint8_t seconds = bcdToDec(Wire.read());
uint8_t minutes = bcdToDec(Wire.read());
uint8_t hours = bcdToDec(Wire.read() & 0xff);
uint8_t wday = bcdToDec(Wire.read());
uint8_t mday = bcdToDec(Wire.read());
uint8_t month = bcdToDec(Wire.read());
uint8_t year = bcdToDec(Wire.read());
// Shows the data on the display
lcd.setCursor(0,0);
lcd.print(" ");
// Adds 0 (clear) if the time is less than 10
if (hours < 10)
lcd.print("0");
lcd.print(hours);
if (seconds & 1)
lcd.print(":");
else
lcd.print(" ");
// Adds 0 (clear) if minutes are less than 10
if (minutes < 10)
lcd.print("0");
lcd.print(minutes);
if (seconds & 1)
lcd.print(":");
else
lcd.print(" ");
if (seconds < 10)
lcd.print("0");
lcd.print(seconds);
lcd.setCursor(2,1);
// Show the day of the week
switch(wday-1) // fixed
{
case 0:lcd.print("Sun");
break;
case 1:lcd.print("Mon");
break;
case 2:lcd.print("Tue");
break;
case 3:lcd.print("Wed");
break;
case 4:lcd.print("Thu");
break;
case 5:lcd.print("Fri");
break;
case 6:lcd.print("Sat");
}
lcd.setCursor(6,1);
// Adds 0 (clear) if day of the month is less than 10
if (mday < 10)
lcd.print("0");
lcd.print(mday);
lcd.print("/");
// Add 0 (clear) if month is less than 10
if (month < 10)
lcd.print("0");
lcd.print(month);
lcd.print("/");
lcd.print(year);
}