#include <WiFi.h>
#include "time.h"
#include "RTClib.h"
#include "structs.h"
#include <Wire.h>
#include <math.h>
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
RTC_DS3231 rtc;
#define NIGHT 0 //FLAG Nacht
#define SUNRISE 1 //FLAG Sonnenaufgang
#define DAY 2 //FLAG Tag
#define SUNSET 3 //FLAG Sonnuntergang
#define CLOUD_ON 4 //FLAG Wolke kommt
#define CLOUD_DELAY 5 // FLAG Wolke verweilt
#define CLOUD_OFF 6 //FLAG Wolke geht
#define OFF 7
#define DAYLIGHT 8 //FLAG Tageslicht
#define REDLIGHT 9 //FLAG Rotlicht
#define BLUELIGHT 10 //FLAG Blaulicht
#define LIGHTCHANALS 16 //Anzahl der Lichtkanäle
#define MAXBRIGHTNESS_DAYLIGHT 10 //Maximale Helligkeit in Prozent
#define MAXBRIGHTNESS_REDLIGHT 10 //Maximale Helligkeit in Prozent
#define MAXBRIGHTNESS_BLUELIGHT 10 //Maximale Helligkeit in Prozent
#define CLOUD_SPEED 1 // max 0-100 sek Zeit für Dimmvorgang in Sek.2
#define CLOUD_AMOUNT 5000 // max 0-65535 Bereich für Zufallszahl je kleiner um so häufiger 5000
#define CLOUD_MAXBRIGHTNESS 5 // max 0-100 % Maximale Helligkeit bei Wolke in Prozent
#define CLOUD_MINBRIGHTNESS 1 // max 0-100 % Minimale Helligkeit bei Wolke in Prozent
#define CLOUD_DLYMIN 5 // max 0-500 msek Minimale Länge der Wolke ohne dimmen 500
#define CLOUD_DLY 5000 // max 0-65535 msek Bereich für Zufallszahl je größer um so länger 5000
#define SERIAL_OUT 2 // 1 Debug 2 Log 3 PWM Out CH 1
const char* ssid = "*";
const char* password = "*";
unsigned long last_calc = 0;
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 3600;
const int daylightOffset_sec = 3600;
int wifi_set = 1;
CHANAL chanals[] = { {0, DAYLIGHT, get_ts(0, 1,0), get_ts(0, 6, 0), 2, 2, OFF, 0, 0, 0, 0, 0,0},
{1, DAYLIGHT, get_ts(0, 1,0), get_ts(0, 6, 0), 2, 2, OFF, 0, 0, 0, 0, 0,0},
{4, DAYLIGHT, get_ts(0, 1,0), get_ts(0, 6, 0), 2, 2, OFF, 0, 0, 0, 0, 0,0},
{5, DAYLIGHT, get_ts(0, 1,0), get_ts(0, 6, 0), 2, 2, OFF, 0, 0, 0, 0, 0,0},
{8, DAYLIGHT, get_ts(0, 1,0), get_ts(0, 6, 0), 2, 2, OFF, 0, 0, 0, 0, 0,0},
{9, DAYLIGHT, get_ts(0, 1,0), get_ts(0, 6, 0), 2, 2, OFF, 0, 0, 0, 0, 0,0},
{12, DAYLIGHT, get_ts(0, 1,0), get_ts(0, 6, 0), 2, 2, OFF, 0, 0, 0, 0, 0,0},
{13, DAYLIGHT, get_ts(0, 1,0), get_ts(0, 6, 0), 2, 2, OFF, 0, 0, 0, 0, 0,0},
{3, BLUELIGHT,get_ts(0, 1, 10), get_ts(0, 6,0), 1, 3, OFF, 0, 0, 0, 0, 0,0},
{7, BLUELIGHT,get_ts(0, 1, 10), get_ts(0, 6,0), 1, 3, OFF, 0, 0, 0, 0, 0,0},
{11,BLUELIGHT,get_ts(0, 1, 10), get_ts(0, 6,0), 1, 3, OFF, 0, 0, 0, 0, 0,0},
{15,BLUELIGHT,get_ts(0, 1, 10), get_ts(0, 6,0), 1, 3, OFF, 0, 0, 0, 0, 0,0},
{2,REDLIGHT, get_ts(0, 0, 10), get_ts(0, 6,0), 3, 1, OFF, 0, 0, 0, 0, 0,0},
{6,REDLIGHT, get_ts(0, 0, 10), get_ts(0, 6,0), 3, 1, OFF, 0, 0, 0, 0, 0,0},
{10,REDLIGHT, get_ts(0, 0, 10), get_ts(0, 6,0), 3, 1, OFF, 0, 0, 0, 0, 0,0},
{14,REDLIGHT, get_ts(0, 0, 10), get_ts(0, 6,0), 3, 1, OFF, 0, 0, 0, 0, 0,0}};
DateTime now;
void setup()
{
Serial.begin(115200);
randomSeed(analogRead(0));
if (! rtc.begin())
{
Serial.println("Couldn't find RTC");
Serial.flush();
while (1) delay(10);
}
setupClock(23,59,10,1,2,20); // HH,MM,SS,DD,MM;YY setupClock(10,10,10,3,2,20)
Serial.print(printTime());
pwm.begin();
pwm.setPWMFreq(400); // This is the maximum PWM frequency
delay(3000);
for (unsigned char i = 0; i < 16; i++)
{
pwm.setPWM(i, 0, 0);
}
for (unsigned char i = 0; i < LIGHTCHANALS; i++)
{
Serial.print(printTime());
Serial.print(" PIN: ");
Serial.print(chanals[i].pin);
Serial.print(" Status:");
Serial.println(chanals[i].status);
}
}
void loop()
{
if (millis() > last_calc+50)
{
now = rtc.now();
// RTC.getTime();
// t.update();
last_calc = millis();
for (int i = 0; i < LIGHTCHANALS; i++)
{
if (SERIAL_OUT==2)
{
Serial.print(int(exp(sqrt(chanals[i].pos) * 0.144295) * 0.4)); //Log
Serial.print(";"); //Log
}
if (TimeStamp() >= chanals[i].starttime && TimeStamp() <= (chanals[i].endtime + get_ts(0, chanals[i].sunsetlength, 0)))
{
switch (chanals[i].status)
{
case NIGHT:
set_sunrise(i);
break;
case SUNRISE:
sunrise(i);
break;
case DAY:
daylight(i);
break;
case CLOUD_ON:
cloudy(i);
break;
case CLOUD_DELAY:
cloudydelay(i);
break;
case CLOUD_OFF:
sunny(i);
break;
case SUNSET:
sunset(i);
break;
}
if (TimeStamp() >= chanals[i].endtime && chanals[i].status != SUNSET && chanals[i].status != OFF)
{
chanals[i].status = SUNSET;
chanals[i].startmillis = last_calc;
chanals[i].freq = float(chanals[i].pos / 60000.0 / chanals[i].sunsetlength);
chanals[i].endpos = chanals[i].pos;
if (SERIAL_OUT==1)
{
Serial.print(printTime());
Serial.print(" PIN: ");
Serial.print(chanals[i].pin);
Serial.print(" Sonnenuntergang über ");
Serial.print(chanals[i].sunsetlength);
Serial.println(" Minute(n)");
}
}
if (chanals[i].pos != chanals[i].lastpos)
{
setpwm(chanals[i].pin, int(chanals[i].pos));
}
}
chanals[i].lastpos = chanals[i].pos;
}
if (SERIAL_OUT==2)
{
Serial.println(); //Log
}
if (TimeStamp() >= get_ts(0,0,0) && TimeStamp()<=get_ts(0,0,10))
{
for (int i = 0; i < LIGHTCHANALS; i++)
{
if (chanals[i].status == OFF)
{
chanals[i].status = NIGHT;
if (SERIAL_OUT==1)
{
Serial.print(printTime());
Serial.print(" PIN: ");
Serial.print(chanals[i].pin);
Serial.println(" Set NIGHT");
}
}
}
}
if (TimeStamp() >= get_ts(0,10,0) && TimeStamp()<=get_ts(0,10,2))
{
Serial.print(printTime());
Serial.print(" Reset Time ");
setupClock(23,59,40,1,2,20);
delay(1000);
Serial.print(printTime());
}
}
}
void setpwm(int CH, int pos)
{
if (CH >= 0 && CH <= 15)
{
pwm.setPWM(CH, 0, int(exp(sqrt(pos) * 0.144295) * 0.4));
if (SERIAL_OUT==3 && CH ==1)
{
Serial.println(int(exp(sqrt(pos) * 0.144295) * 0.4)); //Log CH1
}
}
}
void set_sunrise(int y)
{
switch (chanals[y].wrb)
{
case DAYLIGHT:
chanals[y].freq = float(sq(log((4095.0/100.0*MAXBRIGHTNESS_DAYLIGHT)/0.4)/0.144295)/ 60000.0 / chanals[y].sunriselength);
break;
case REDLIGHT:
chanals[y].freq = float(sq(log((4095.0/100.0*MAXBRIGHTNESS_REDLIGHT)/0.4)/0.144295)/ 60000.0 / chanals[y].sunriselength);
break;
case BLUELIGHT:
chanals[y].freq = float(sq(log((4095.0/100.0*MAXBRIGHTNESS_BLUELIGHT)/0.4)/0.144295)/ 60000.0 / chanals[y].sunriselength);
break;
}
chanals[y].status = SUNRISE;
chanals[y].startmillis = last_calc;
if (SERIAL_OUT==1)
{
Serial.print(printTime());
Serial.print(" PIN: ");
Serial.print(chanals[y].pin);
Serial.print(" Sonnenaufgang über ");
Serial.print(chanals[y].sunriselength);
Serial.println(" Minute(n)");
}
}
void sunrise(int y)
{
chanals[y].pos = chanals[y].freq * (last_calc - chanals[y].startmillis);
switch (chanals[y].wrb)
{
case DAYLIGHT:
if (chanals[y].pos >= float(sq(log((4095.0/100.0*MAXBRIGHTNESS_DAYLIGHT)/0.4)/0.144295)))
{
chanals[y].pos = float(sq(log((4095.0/100.0*MAXBRIGHTNESS_DAYLIGHT)/0.4)/0.144295));
chanals[y].status = DAY;
}
break;
case REDLIGHT:
if (chanals[y].pos >= float(sq(log((4095.0/100.0*MAXBRIGHTNESS_REDLIGHT)/0.4)/0.144295)))
{
chanals[y].pos = float(sq(log((4095.0/100.0*MAXBRIGHTNESS_REDLIGHT)/0.4)/0.144295));
chanals[y].status = DAY;
}
break;
case BLUELIGHT:
if (chanals[y].pos >= float(sq(log((4095.0/100.0*MAXBRIGHTNESS_BLUELIGHT)/0.4)/0.144295)))
{
chanals[y].pos = float(sq(log((4095.0/100.0*MAXBRIGHTNESS_BLUELIGHT)/0.4)/0.144295));
chanals[y].status = DAY;
}
break;
}
}
void daylight(int y)
{
word cl;
float tmp;
cl = random(0, CLOUD_AMOUNT);
if (cl < LIGHTCHANALS && chanals[y].wrb==DAYLIGHT) //Nur Kanäle mit Tageslicht
{
if (TimeStamp() <= (chanals[y].endtime + get_ts(0,0,((2*CLOUD_SPEED)+10))))
{
chanals[y].startmillis = last_calc;
chanals[y].status = CLOUD_ON; //Status Wolke an
chanals[y].endpos = chanals[y].pos; //Aktuelle Position zwischenspeichern
tmp=random(0, CLOUD_MAXBRIGHTNESS-CLOUD_MINBRIGHTNESS);
chanals[y].cloudset = (sq(log((4095.0/100.0*(CLOUD_MINBRIGHTNESS + tmp))/0.4)/0.144295)/4095)*100;
chanals[y].freq = float((chanals[y].pos - (4095.0 / 100 * chanals[y].cloudset)) / 1000.0 / CLOUD_SPEED); //Frequenz berechnen für Dimmvorgang
if (SERIAL_OUT==1)
{
Serial.print(printTime());
Serial.print(" PIN: ");
Serial.print(chanals[y].pin);
Serial.print(" Wolke kommt mit ");
Serial.print(CLOUD_MINBRIGHTNESS+tmp);
Serial.print("% Helligkeit PWM:");
Serial.println(int(exp(sqrt(4095.0/100.0*chanals[y].cloudset) * 0.144295) * 0.4));
}
}
}
}
void cloudy(int y)
{
if (chanals[y].wrb==DAYLIGHT) //Nur Kanäle mit Tageslicht
{
chanals[y].pos = chanals[y].endpos - (chanals[y].freq * (last_calc - chanals[y].startmillis));
if (chanals[y].pos <= float(4095.0 / 100.0 * chanals[y].cloudset))
{
chanals[y].pos = float(4095.0 / 100.0 * chanals[y].cloudset);
chanals[y].status = CLOUD_DELAY;
chanals[y].startmillis = last_calc;
chanals[y].freq = float(((sq(log((4095.0/100.0*MAXBRIGHTNESS_DAYLIGHT)/0.4)/0.144295)) - chanals[y].pos) / 1000.0 / CLOUD_SPEED); //Frequenz berechnen für Dimmvorgang
chanals[y].endpos = chanals[y].pos;
chanals[y].dly = random(0, CLOUD_DLY);
if (SERIAL_OUT==1)
{
Serial.print(printTime());
Serial.print(" PIN: ");
Serial.print(chanals[y].pin);
Serial.print(" Wolke bleibt für ");
Serial.print(float((chanals[y].dly+CLOUD_DLYMIN)/1000.000),3);
Serial.println(" Sekunden");
}
}
}
}
void cloudydelay(int y)
{
if (chanals[y].wrb==DAYLIGHT) //Nur Kanäle mit Tageslicht
{
if (last_calc > chanals[y].startmillis + CLOUD_DLYMIN + chanals[y].dly)
{
chanals[y].status = CLOUD_OFF;
chanals[y].startmillis = last_calc;
if (SERIAL_OUT==1)
{
Serial.print(printTime());
Serial.print(" PIN: ");
Serial.print(chanals[y].pin);
Serial.println(" Wolke geht");
}
}
}
}
void sunny(int y)
{
if (chanals[y].wrb==DAYLIGHT) //Nur Kanäle mit Tageslicht
{
chanals[y].pos = chanals[y].endpos + (chanals[y].freq * (last_calc - chanals[y].startmillis));
if (chanals[y].pos >= float(sq(log(4095.0/100.0*MAXBRIGHTNESS_DAYLIGHT/0.4)/0.144295)))
{
chanals[y].pos = float(sq(log(4095.0/100.0*MAXBRIGHTNESS_DAYLIGHT/0.4)/0.144295));
chanals[y].status = DAY;
}
}
}
void sunset(int y)
{
chanals[y].pos = chanals[y].endpos - (chanals[y].freq * (last_calc - chanals[y].startmillis));
if (chanals[y].pos <= 0.0)
{
chanals[y].pos = 0;
chanals[y].status = OFF;
if (SERIAL_OUT==1)
{
Serial.print(printTime());
Serial.print(" PIN: ");
Serial.print(chanals[y].pin);
Serial.println(" Aus");
}
}
}