/*
round clock:
hours are blue
minutes is green
seconds is red
_________________________________________
3D Drucker
https://github.com/cyborg5/NeoPixel-Clock
https://wokwi.com/projects/344893935754150484
https://elektro.turanis.de/html/prj047/index.html
https://forum.arduino.cc/t/photoresistor-and-fastled-play-function-if-value-xx/1017364
https://wayoda.github.io/LedControl/pages/software
https://github.com/wilmouths/RGBLed
https://www.rc-modellbau-portal.de/index.php?threads/arduino-mit-dcf77-modul-und-rtc-echtzeituhr-ds3231.8278/
https://github.com/FastLED/FastLED/blob/master/examples/AnalogOutput/AnalogOutput.ino
https://elektro.turanis.de/html/prj061/index.html
https://wokwi.com/projects/371331542047593473
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <MD_Parola.h>
#include <MD_MAX72xx.h>
#include <DHT.h>
#include <SPI.h>
#include <Wire.h>
#include <FastLED.h>
#include "RGBLed.h"
#include "Max44009.h"
#include "Font7Seg.h"
#include "BH1750FVI.h"
#include "DCF77.h"
#include "TimeLib.h"
#include "DS3231.h"
#include "SevenSegment.h"
// This is not time, it is divided millis()
#define seconds() ((unsigned long)millis()/1000)
#define minutes() ((unsigned int)seconds()/60)
#define hours() ((unsigned int)minutes()/60)
SevenSegment* run;
// MAX44009 I2C address is 0x4A(74)
#define Addr 0x4A
Max44009 myLux(0x4A);
uint32_t lastDisplay = 0;
#define DATA_PIN 8
#define NUM_LEDS 61
#define LED_OFFSET 30 // 12 hour led. It can be useful when wires go from bottom.
#define BRIGHTNESS 255
#define LED_TYPE WS2812
#define COLOR_ORDER GRB
// Define the number of devices we have in the chain and the hardware interface
#define HARDWARE_TYPE MD_MAX72XX::PAROLA_HW
#define MAX_DEVICES 4 // Define the number of displays connected
#define CLK_PIN 52 // CLK or SCK
#define DIN_PIN 51 // DATA or MOSI
#define CS_PIN 53 // CS or SS
#define BMU_PIN 10 // CLK left uhr
#define BMD_PIN 11 // DT rechtz Uhr
#define BMS_PIN 12 // SW save
#define SPEED_TIME 75 // Speed of the transition
#define PAUSE_TIME 0
#define MAX_MESG 20
// 7segment 3
#define DS1_pin 49
#define STCP1_pin 47
#define SHCP1_pin 45
// 7segment 6
#define DS2_pin 39
#define STCP2_pin 41
#define SHCP2_pin 43
// 7segment 9
#define DS3_pin 33
#define STCP3_pin 35
#define SHCP3_pin 37
// 7segment 12
#define DS4_pin 27
#define STCP4_pin 29
#define SHCP4_pin 31
#define DS5_pin 30
#define STCP5_pin 28
#define SHCP5_pin 26
// Connection pin to DCF 77 device
#define DCF_PIN 2
#define DCF_INTERRUPT 0
// These are for the temperature
#define DHTPIN 6
#define DHTTYPE DHT22
#define TIMEDHT 1000
// The it Status RGB led
#define red_pin 3
#define green_pin 2
#define blue_pin 5
char szTime[9]; // mm:ss\0
char szMesg[MAX_MESG + 1] = "";
float humidity, celsius;
uint8_t degC[] = { 6, 3, 3, 56, 68, 68, 68 }; // Deg C
uint8_t degF[] = { 6, 3, 3, 124, 20, 20, 4 }; // Deg F
int dec_digits[10] {1, 79, 18, 6, 76, 36, 32, 15, 0, 4};
uint32_t timerDHT = TIMEDHT;
CRGB leds[NUM_LEDS];
#define UPDATES_PER_SECOND 20
DHT dht(DHTPIN, DHTTYPE);
MD_Parola P = MD_Parola(HARDWARE_TYPE, CS_PIN, MAX_DEVICES);
CRGBPalette16 myPal = CloudColors_p;
time_t t;
bool g_bDCFTimeFound = false;
byte ErstStart = 1;
byte DCFaktiv = 0;
byte DCFZeitOK = 0;
static byte stdMerker = 255;
long Intervall = 120000;
long UpdateTimer = 0;
DCF77 DCF = DCF77(DCF_PIN, DCF_INTERRUPT, false);
RGBLed stLed(red_pin, green_pin, blue_pin, RGBLed::COMMON_CATHODE);
//RGBLed led(11, 10, 9, RGBLed::COMMON_CATHODE);
int i, lux;
// Code for reading clock time
void getTime(char *psz, bool f = true){
sprintf(psz, "%02d%c%02d", hour(t), (f ? ':' : ' '), minute(t));
}
// Code for reading clock date
void getDate(char *psz){
char szBuf[10];
sprintf(psz, "%d %s %04d", day(t), mon2str(month(t), szBuf, sizeof(szBuf) - 1), year(t), DEC);
}
// Code for reading Erro date
void getErr(char *psz, uint8_t err){
char szBuf[13];
//sprintf(psz, "ERROR: 0x871 ");
sprintf(psz, "ERR: 0x%d ", err, err2str(err, szBuf, sizeof(szBuf) - 1), err);
}
/*
char *err2str(uint8_t code, char *psz, uint8_t len) {
static const char *str[] = {
"871", "427",
"874", "057"
};
if (code < 1 || code > sizeof(str) / sizeof(str[0])) {
strncpy(psz, "UNKNOWN", len);
} else {
strncpy(psz, str[code - 1], len);
}
psz[len] = '\0';
return psz;
}
*/
char *err2str(uint8_t code, char *psz, uint8_t len){
static const __FlashStringHelper* str[] = {
F("871"), F("427"),
F("874"), F("057")
};
strncpy_P(psz, (const char PROGMEM *)str[code - 1], len);
psz[len] = '\0';
return (psz);
}
// Get a label from PROGMEM into a char array
char *mon2str(uint8_t mon, char *psz, uint8_t len){
static const __FlashStringHelper* str[] = {
F("Jan"), F("Feb"), F("Mar"), F("Apr"),
F("May"), F("Jun"), F("Jul"), F("Aug"),
F("Sep"), F("Oct"), F("Nov"), F("Dec")
};
strncpy_P(psz, (const char PROGMEM *)str[mon - 1], len);
psz[len] = '\0';
return (psz);
}
char *dow2str(uint8_t code, char *psz, uint8_t len){
static const __FlashStringHelper* str[] = {
F("Sunday"), F("Monday"), F("Tuesday"),
F("Wednesday"), F("Thursday"), F("Friday"),
F("Saturday")
};
strncpy_P(psz, (const char PROGMEM *)str[code - 1], len);
psz[len] = '\0';
return (psz);
};
void setup(){
//run = new segment();
//run->begin();
adjustTime();
Serial.begin(9600);
Wire.begin();
dht.begin();
pinMode(DS1_pin, OUTPUT);
pinMode(STCP1_pin, OUTPUT);
pinMode(SHCP1_pin, OUTPUT);
pinMode(DS2_pin, OUTPUT);
pinMode(STCP2_pin, OUTPUT);
pinMode(SHCP2_pin, OUTPUT);
pinMode(DS3_pin, OUTPUT);
pinMode(STCP3_pin, OUTPUT);
pinMode(SHCP3_pin, OUTPUT);
pinMode(DS4_pin, OUTPUT);
pinMode(STCP4_pin, OUTPUT);
pinMode(SHCP4_pin, OUTPUT);
pinMode(DS5_pin, OUTPUT);
pinMode(STCP5_pin, OUTPUT);
pinMode(SHCP5_pin, OUTPUT);
pinMode(BMU_PIN, INPUT_PULLUP);
pinMode(BMD_PIN, INPUT_PULLUP);
pinMode(BMS_PIN, INPUT_PULLUP);
t = now();
DCF.Start();
Serial.println(F("FastLED 60 led ring clock."));
Serial.print(F("Compile Date/Time: "));
Serial.print(F(__DATE__));
Serial.print(F(" "));
Serial.println(F(__TIME__));
Serial.println(F("Blue actual time finished hours 0-12. Blinking green minute hand. Blinking red second hand."));
Serial.print(F("Current Date/Time: "));
Serial.print(day(t), DEC); Serial.print(".");
Serial.print(month(t), DEC); Serial.print(".");
Serial.print(year(t), DEC); Serial.print(" ");
Serial.print(hour(t), DEC); Serial.print(":");
if (minute(t) < 10){
Serial.print("0");
}
Serial.print(minute(t), DEC); Serial.println(".");
//Setup MAX44009 Light sensor
myLux.setAutomaticMode();
FastLED.addLeds<NEOPIXEL, DATA_PIN>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
for (int i = 0; i < NUM_LEDS ; i++ ){
leds[i] = ColorFromPalette (myPal, i * 255 / NUM_LEDS, 255);
FastLED.delay(10);
}
FastLED.delay(1000);
P.begin(2);
P.setInvert(false);
//P.setIntensity(0);
P.setIntensity(lux);
P.setZone(0, MAX_DEVICES - 4, MAX_DEVICES - 1);
P.setZone(1, MAX_DEVICES - 4, MAX_DEVICES - 1);
P.displayZoneText(1, szTime, PA_CENTER, SPEED_TIME, PAUSE_TIME, PA_PRINT, PA_NO_EFFECT);
P.displayZoneText(0, szMesg, PA_CENTER, SPEED_TIME, 0, PA_PRINT, PA_NO_EFFECT);
P.addChar('$', degC);
P.addChar('&', degF);
digitalWrite(STCP1_pin, LOW);
shiftOut(DS1_pin, SHCP1_pin, LSBFIRST, dec_digits[3]);
digitalWrite(STCP1_pin, HIGH);
digitalWrite(STCP2_pin, LOW);
shiftOut(DS2_pin, SHCP2_pin, LSBFIRST, dec_digits[6]);
digitalWrite(STCP2_pin, HIGH);
digitalWrite(STCP3_pin, LOW);
shiftOut(DS3_pin, SHCP3_pin, LSBFIRST, dec_digits[9]);
digitalWrite(STCP3_pin, HIGH);
digitalWrite(STCP4_pin, LOW);
shiftOut(DS4_pin, SHCP4_pin, LSBFIRST, dec_digits[1]);
digitalWrite(STCP4_pin, HIGH);
digitalWrite(STCP5_pin, LOW);
shiftOut(DS5_pin, SHCP5_pin, LSBFIRST, dec_digits[2]);
digitalWrite(STCP5_pin, HIGH);
}
void loop(){
//run->writeDigit(0, 1,true); // write 0 on first digit
uint32_t interval = 100;
if (millis() - lastDisplay >= interval){
lastDisplay += interval;
float lux = myLux.getLux();
int err = myLux.getError();
int config = myLux.getConfiguration();
int CDR = (config & 0x80) >> 3;
int TIM = (config & 0x07);
int integrationTime = myLux.getIntegrationTime();
if (err != 0 ){
stLed.setColor(RGBLed::RED);
P.displayAnimate();
if (P.getZoneStatus(0)){
P.setPause(0, 1000);
P.setTextEffect(0, PA_SCROLL_LEFT, PA_SCROLL_UP);
getErr(szMesg, err -1);
P.displayReset(0);
}
}
else{
time_t DCFtime = DCF.getTime();
if (DCFtime != 0){
stLed.setColor(RGBLed::GREEN);
Serial.println("Time is uipdated");
setTime(DCFtime);
g_bDCFTimeFound = true;
DCFZeitOK = 1;
}
if ((DCFZeitOK = 1) && (year() > 2000) && (hour() != stdMerker) ){
ErstStart = 0;
MAX72xx();
pride();
stLed.off();
//Serial.println("Current time has been received!");
} else{
stLed.setColor(RGBLed::BLUE);
getErr(szMesg, err);
//Serial.print("Conditions for synchronizing time are NOT met");
//Serial.println();
}
//Serial.print("lux:\t");
//Serial.print(lux);
//Serial.print("\tCDR:\t");
//Serial.print(CDR);
//Serial.print("\tTIM:\t");
//Serial.print(TIM);
//Serial.print("\t");
//Serial.print(integrationTime);
//Serial.print(" ms");
//Serial.println();
}
}
}
void pride(){
int a = millis();
static long last_a;
static uint8_t color_index;
static uint8_t h, m, s;
static int ldrValue = 0;
static int lastBrightness = 0;
static bool isLedActive = false;
EVERY_N_MILLIS(5){
color_index = abs( (millis() % 7200) * (double)255 / 3600 - 255 );
t = now();
h = hour(t);
h = h > 12 ? h - 12 : h;
m = minute(t);
s = second(t);
fill_solid(leds, NUM_LEDS, CRGB::Black); // hours to go with this color
myRainbow( leds, NUM_LEDS, 0, 255 / NUM_LEDS );
fadeToBlackBy( leds, NUM_LEDS, 200);
/*
for ( i=0 ; i<NUM_LEDS ; i++ ){
leds[i] = ColorFromPalette(myPal,m,255);
}
*/
// hour time arc, fill leds
//fill_solid(leds,h*5,CRGB::Blue);
myRainbow( leds, h * 5, 0, 255 / NUM_LEDS );
// secondary points 1,2,4,5..
for ( i = 0 ; i < NUM_LEDS ; i += 5 ){
//leds[i] = CRGB::Black;
}
// main points 0,3,6,9
for ( i = 0 ; i < 46 ; i += 15 ){
leds[i] = CRGB::Black;
}
// minute time flashing led
leds[m] = (s % 2 == 0 ? CRGB::Green : CRGB::Black);
// second hour
leds[s] = CRGB::Red;
}
EVERY_N_SECONDS(1){
if ( s == 0 ){
if ( m == 0 ){
myRainbow( leds, NUM_LEDS, 0, 255 / NUM_LEDS );
FastLED.delay(5000);
} else if ( m == 30 ){
myRainbow( leds, NUM_LEDS, 0, 255 / NUM_LEDS * 2 );
FastLED.delay(5000);
} else if ( m % 5 == 0 ){
myRainbow( leds, NUM_LEDS, 0, 255 / NUM_LEDS );
FastLED.delay(5000);
}
Serial.print(F("Time: "));
Serial.print(hour(t), DEC); Serial.print(":");
if (m < 10){
Serial.print("0");
}
Serial.print(minute(t), DEC); Serial.println(".");
}
}
EVERY_N_SECONDS(3600){
adjustTime();
}
blur1d (leds, NUM_LEDS, 70);
leds[NUM_LEDS - 1] = CRGB(158, 0, 142);
FastLED.delay(1000 / UPDATES_PER_SECOND);
}
void getTemperature(){
if ((millis() - timerDHT) > TIMEDHT){
timerDHT = millis();
humidity = dht.readHumidity();
celsius = dht.readTemperature();
if (isnan(humidity) || isnan(celsius)){
Serial.println(F("Failed to read from DHT sensor!"));
return;
}
}
}
void myRainbow(struct CRGB * pFirstLED, int numToFill, uint8_t initialhue, uint8_t deltahue ){
CHSV hsv;
hsv.hue = initialhue;
hsv.val = 255;
hsv.sat = 240;
for ( int i = 0; i < numToFill; i++){
pFirstLED[i] = hsv;
hsv.hue += deltahue - 1;
}
}
void adjustTime(){
byte t_sec, t_min, t_hr, t_wd, t_dy, t_mon, t_yr;
Wire.begin();
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);
Wire.end();
}
void MAX72xx(){
static uint32_t lastTime = 0;
static uint8_t display = 0;
static bool flasher = false;
getTemperature();
P.displayAnimate();
if (P.getZoneStatus(0)){
switch (display){
case 0:
P.setPause(0, 1000);
P.setTextEffect(0, PA_SCROLL_LEFT, PA_SCROLL_UP);
display++;
dtostrf(celsius, 3, 1, szMesg);
strcat(szMesg, "$");
break;
case 1:
P.setTextEffect(0, PA_SCROLL_DOWN, PA_SCROLL_LEFT);
display++;
dtostrf(humidity, 3, 0, szMesg);
strcat(szMesg, "%rH");
break;
case 2:
P.setFont(0, numeric7Seg);
P.setTextEffect(0, PA_PRINT, PA_NO_EFFECT);
P.setPause(0, 0);
if ((millis() - lastTime) >= 1000){
lastTime = millis();
getTime(szMesg, flasher);
flasher = !flasher;
}
if ((second(t) == 00) && (second(t) <= 30)){
display++;
P.setTextEffect(0, PA_PRINT, PA_WIPE_CURSOR);
}
break;
case 3:
P.setFont(0, nullptr);
P.setTextEffect(0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display++;
dow2str(dayOfWeek(t), szMesg, MAX_MESG);
break;
default:
P.setTextEffect(0, PA_SCROLL_LEFT, PA_SCROLL_LEFT);
display = 0;
getDate(szMesg);
break;
}
P.displayReset(0);
}
}