/*
*IDN?
*RST
SYST:SCPI?
SYST:LED:TIME:ON
SYST:LED:TIME:ON?
SYST:LED:TIME:OFF
SYST:LED:TIME:OFF?
SYST:MEAS:VBAT?
SYST:MEM?
SYST:MEAS:INT
SYST:MEAS:INT?
TODO:
SYST:TIME:SEC
SYST:TIME:MIN
SYST:TIME:HOUR
SYST:TIME:SEC?
SYST:TIME:MIN?
SYST:TIME:HOUR?
SYST:DATE:DAY
SYST:DATE:MON
SYST:DATE:YEAR
SYST:DATE:DAY?
SYST:DATE:MON?
SYST:DATE:YEAR?
SYST:SLE:TIME
SYST:SLE:TIME?
SYST:LDR:THR
SYST:LDR:THR?
*/
#include "RTClib.h"
#include "Vrekrer_scpi_parser.h" //include the SCPI library
#include <EEPROM.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
#include <avr/pgmspace.h>
RTC_DS1307 rtc;
SCPI_Parser Vierendehl;
#define SLEEPTIME 1 //Sleeptime * 8.4s
#define LED_PIN 5
#define OFF_TIME 3500
#define ON_TIME 500
#define BAT_VOL_PIN A3
#define NUM_OF_MEAS 1023
#define MEAS_INTERVAl 1 //in min
#define idn_string "IDN: Lighthouse Vierendehlgrund U-F. - C2572 - Fl. W. 4 s" //define an IDN
byte pos = 0;
bool ledState = LOW;
unsigned long previousMillis = 0;
unsigned long myTime;
int minuteOfDayold = 0;
int onTime = ON_TIME;
int offTime = OFF_TIME;
byte measInterval = MEAS_INTERVAl;
volatile bool toggle = true;
volatile uint8_t wakeUpCounter = 0; // Zähler für 1 Minute
static const int month[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
static const int sunRise[] PROGMEM =
{494, 494, 494, 493, 493, 493, 492, 492, 491, 491, 490, 490, 489, 488, 487, 486, 486, 485, 484, 483, 482, 480, 479, 478, 477, 476, 474, 473, 471, 470, 469, //jan
467, 466, 464, 462, 461, 459, 458, 456, 454, 452, 451, 449, 447, 445, 443, 441, 439, 437, 436, 434, 432, 430, 428, 425, 423, 421, 419, 417, //feb
415, 413, 411, 409, 406, 404, 402, 400, 398, 396, 393, 391, 389, 387, 384, 382, 380, 378, 376, 373, 371, 369, 367, 364, 362, 360, 358, 355, 353, 411, 409, //mar
406, 404, 402, 400, 398, 395, 393, 391, 389, 387, 384, 382, 380, 378, 376, 374, 372, 370, 367, 365, 363, 361, 359, 357, 355, 353, 352, 350, 348, 346, //apr
344, 342, 340, 339, 337, 335, 333, 332, 330, 328, 327, 325, 324, 322, 321, 319, 318, 317, 315, 314, 313, 312, 310, 309, 308, 307, 306, 305, 304, 304, 303, //may
302, 301, 301, 300, 299, 299, 298, 298, 297, 297, 297, 297, 296, 296, 296, 296, 296, 296, 296, 296, 297, 297, 297, 298, 298, 298, 299, 299, 300, 301, //jun
301, 302, 303, 304, 304, 305, 306, 307, 308, 309, 310, 311, 312, 314, 315, 316, 317, 318, 320, 321, 322, 324, 325, 326, 328, 329, 330, 332, 333, 335, 336, //jul
338, 339, 341, 342, 344, 345, 347, 348, 350, 351, 353, 354, 356, 357, 359, 361, 362, 364, 365, 367, 368, 370, 371, 373, 375, 376, 378, 379, 381, 382, 384, //aug
386, 387, 389, 390, 392, 393, 395, 396, 398, 399, 401, 403, 404, 406, 407, 409, 410, 412, 413, 415, 417, 418, 420, 421, 423, 424, 426, 428, 429, 431, //sep
432, 434, 436, 437, 439, 440, 442, 444, 445, 447, 449, 450, 452, 453, 455, 457, 458, 460, 462, 464, 465, 467, 469, 470, 472, 414, 415, 417, 419, 421, 422, //oct
424, 426, 427, 429, 431, 433, 434, 436, 438, 439, 441, 443, 444, 446, 448, 449, 451, 453, 454, 456, 458, 459, 461, 462, 464, 465, 467, 468, 470, 471, //nov
472, 474, 475, 476, 477, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 488, 489, 490, 490, 491, 492, 492, 492, 493, 493, 493, 494, 494, 494, 494, 494 //dec
};
static const int sunSet[] PROGMEM =
{980, 981, 982, 983, 984, 985, 987, 988, 989, 991, 992, 993, 995, 996, 998, 999, 1001, 1003, 1004, 1006, 1007, 1009, 1011, 1012, 1014, 1016, 1018, 1019, 1021, 1023, 1024, //jan
1026, 1028, 1030, 1032, 1033, 1035, 1037, 1039, 1040, 1042, 1044, 1046, 1048, 1049, 1051, 1053, 1055, 1056, 1058, 1060, 1062, 1064, 1065, 1067, 1069, 1070, 1072, 1074 , //feb
1076, 1077, 1079, 1081, 1083, 1084, 1086, 1088, 1089, 1091, 1093, 1094, 1096, 1098, 1099, 1101, 1103, 1104, 1106, 1108, 1109, 1111, 1113, 1114, 1116, 1118, 1119, 1121, 1122, 1184, 1186, //mar
1187, 1189, 1191, 1192, 1194, 1196, 1197, 1199, 1201, 1202, 1204, 1205, 1207, 1209, 1210, 1212, 1214, 1215, 1217, 1218, 1220, 1222, 1223, 1225, 1227, 1228, 1230, 1232, 1233, 1235, //apr
1236, 1238, 1240, 1241, 1243, 1244, 1246, 1247, 1249, 1251, 1252, 1254, 1255, 1257, 1258, 1260, 1261, 1262, 1264, 1265, 1267, 1268, 1269, 1271, 1272, 1273, 1274, 1276, 1277, 1278, 1279, //may
1280, 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1287, 1288, 1289, 1290, 1290, 1291, 1291, 1292, 1292, 1292, 1293, 1293, 1293, 1293, 1294, 1294, 1294, 1294, 1294, 1293, 1293, 1293, //jun
1293, 1292, 1292, 1292, 1291, 1291, 1290, 1289, 1289, 1288, 1287, 1286, 1285, 1285, 1284, 1283, 1282, 1280, 1279, 1278, 1277, 1276, 1274, 1273, 1272, 1270, 1269, 1267, 1266, 1264, 1263, //jul
1261, 1260, 1258, 1256, 1255, 1253, 1251, 1249, 1247, 1246, 1244, 1242, 1240, 1238, 1236, 1234, 1232, 1230, 1228, 1226, 1224, 1222, 1220, 1218, 1216, 1214, 1211, 1209, 1207, 1205, 1203, //aug
1201, 1198, 1196, 1194, 1192, 1189, 1187, 1185, 1183, 1181, 1178, 1176, 1174, 1171, 1169, 1167, 1165, 1162, 1160, 1158, 1156, 1153, 1151, 1149, 1146, 1144, 1142, 1140, 1137, 1135, //sep
1133, 1131, 1128, 1126, 1124, 1122, 1120, 1117, 1115, 1113, 1111, 1109, 1107, 1105, 1102, 1100, 1098, 1096, 1094, 1092, 1090, 1088, 1086, 1084, 1082, 1020, 1019, 1017, 1015, 1013, 1011, //oct
1009, 1008, 1006, 1004, 1003, 1001, 999, 998, 996, 995, 993, 992, 990, 989, 988, 986, 985, 984, 983, 982, 981, 979, 978, 978, 977, 976, 975, 974, 973, 973, //nov
972, 972, 971, 971, 970, 970, 970, 969, 969, 969, 969, 969, 969, 969, 969, 969, 969, 970, 970, 971, 971, 972, 972, 973, 973, 974, 975, 976, 977, 977, 978 //dec
};
extern int __heap_start, *__brkval;
int freeMemory() {
int v;
return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int)__brkval);
}
extern int __data_start;
void setup() {
// Takt prescalen (1:2 = 8 MHz)
//cli(); // Interrupts deaktivieren
//CLKPR = 0x80; // Prescaler-Änderung erlauben
//CLKPR = 0x01; // Prescaler 2 (16 MHz / 2 = 8 MHz)
//sei(); // Interrupts wieder aktivieren
pinMode(BAT_VOL_PIN, INPUT);
pinMode(LED_PIN, OUTPUT);
pinMode(2, INPUT_PULLUP);
Serial.begin(9600);
// setup of the WDT
MCUSR &= ~(1 << WDRF); // remove reset flag
WDTCSR |= (1 << WDCE) | (1 << WDE); // set WDCE, access prescaler
WDTCSR = 1 << WDP0 | 0 << WDP1 | 0 << WDP2 | 1 << WDP3; // set prescaler bits to to 8s
WDTCSR |= 1 << WDIE; // access WDT interrupt
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
abort();
}
attachInterrupt(digitalPinToInterrupt(2), pinISR, RISING);
//rtc.adjust(DateTime(2025, 2, 3, 8, 43, 30)); //(year, month, day, hour, min, sec)
Vierendehl.RegisterCommand(F("*IDN?"), &Identify);
Vierendehl.RegisterCommand(F("*RST"), &SoftReset);
Vierendehl.SetCommandTreeBase(F("SYST")); //define root
Vierendehl.RegisterCommand(F(":SCPI?"), &GetSCPICommands);
Vierendehl.RegisterCommand(F(":LED:TIME:ON"), &SetOnTime);
Vierendehl.RegisterCommand(F(":LED:TIME:ON?"), &GetOnTime);
Vierendehl.RegisterCommand(F(":LED:TIME:OFF"), &SetOffTime);
Vierendehl.RegisterCommand(F(":LED:TIME:OFF?"), &GetOffTime);
Vierendehl.RegisterCommand(F(":MEM?"), &GetMemSpace);
Vierendehl.RegisterCommand(F(":MEAS:VBAT?"), &GetVBAT);
Vierendehl.RegisterCommand(F(":MEAS:INT"), &SetMeasInt);
Vierendehl.RegisterCommand(F(":MEAS:INT?"), GetMeasInt);
}
void loop() {
if(toggle){
Vierendehl.ProcessInput(Serial, "\n");
DateTime now = rtc.now();
int dayOfYear = int(month[now.month() - 1] + now.day());
int minuteOfDay = int(60*now.hour() + now.minute());
int sunRiseToday = pgm_read_word_near(sunRise + (dayOfYear-1));
int sunSetToday = pgm_read_word_near(sunSet + (dayOfYear-1));
if(minuteOfDay > minuteOfDayold + measInterval - 1){
if(pos >= NUM_OF_MEAS){
pos = 0;
}
EEPROM.update(pos, map(analogRead(BAT_VOL_PIN), 0, 1023, 0, 255));
Serial.print("VBAT = [");
for(int i = constrain((pos-10), 0, 1013); i <= pos; i++){
Serial.print((EEPROM.read(i) / 255.0) * 5.0);
Serial.print(", ");
}
Serial.println("]");
pos++;
minuteOfDayold = minuteOfDay;
}
if(analogRead(A7)>500 || digitalRead(2) == HIGH || (sunRiseToday + 60) > minuteOfDay || sunSetToday < (minuteOfDay + 60)){
unsigned long currentMillis = millis();
if ((currentMillis - previousMillis >= offTime) && (ledState == LOW)) {
previousMillis = currentMillis;
ledState = HIGH;
fadeIn(100);
}
if((currentMillis - previousMillis >= onTime) && (ledState == HIGH)){
previousMillis = currentMillis;
fadeOut(1500);
ledState = LOW;
Serial.print(now.hour());
Serial.print(":");
Serial.print(now.minute());
Serial.print(":");
Serial.print(now.second());
Serial.print(" dayOfYear=");
Serial.print(dayOfYear);
Serial.print(" minuteOfDay=");
Serial.print(minuteOfDay);
Serial.print(" sunRiseToday=");
Serial.print(sunRiseToday);
Serial.print(" sunSetToday=");
Serial.print(sunSetToday);
Serial.println(" -- Light is ON");
}
}else{
Serial.print(now.hour());
Serial.print(":");
Serial.print(now.minute());
Serial.print(":");
Serial.print(now.second());
Serial.print(" dayOfYear=");
Serial.print(dayOfYear);
Serial.print(" minuteOfDay=");
Serial.print(minuteOfDay);
Serial.print(" sunRiseToday=");
Serial.print(sunRiseToday);
Serial.print(" sunSetToday=");
Serial.print(sunSetToday);
Serial.print(" -- Light is OFF");
toggle = false;
enterSleepMode();
}
}
}
void fadeOut(int t){
for(int i = 255; i>0; i--){
analogWrite(LED_PIN,i);
delayMicroseconds(t);
}
}
void fadeIn(int t){
for(int i = 0; i<255; i++){
analogWrite(LED_PIN,i);
delayMicroseconds(t);
}
}
void enterSleepMode(void){
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
power_adc_disable();
power_spi_disable();
power_timer0_disable();
power_timer2_disable();
power_twi_disable();
Serial.println(" -- enter Sleep");
Serial.flush();
sleep_mode();
//Serial.println("wake up");
//sleep_disable();
//power_all_enable();
}
ISR(WDT_vect){ // Timer1 Overflow-Interrupt (4.19s)
wakeUpCounter++;
if (wakeUpCounter >= (SLEEPTIME)) {
wakeUpCounter = 0; // reset counter
toggle = true; // run loop()
}
}
void pinISR(){
toggle = true; // run loop()
}
void(* resetFunc) (void) = 0;//declare reset function at address 0
void Identify(SCPI_C commands, SCPI_P parameters, Stream& interface){ //function to print the IDN
interface.println(F(idn_string));
}
void SoftReset(SCPI_C commands, SCPI_P parameters, Stream& interface){
interface.print(F("SOFT RESET..."));
delay(500);
for(int i = 0; i < NUM_OF_MEAS; i++){
EEPROM.update(i, 0);
interface.print(F("."));
}
delay(500);
interface.println(F("DONE"));
delay(500);
resetFunc(); //call reset
}
void GetSCPICommands(SCPI_C commands, SCPI_P parameters, Stream& interface){
interface.println(F("*IDN?"));
interface.println(F("*RST"));
interface.println(F("SYST:SCPI?"));
interface.println(F("SYST:LED:TIME:ON"));
interface.println(F("SYST:LED:TIME:ON?"));
interface.println(F("SYST:LED:TIME:OFF"));
interface.println(F("SYST:LED:TIME:OFF?"));
interface.println(F("SYST:MEAS:VBAT?"));
interface.println(F("SYST:MEM?"));
}
void SetOnTime(SCPI_C commands, SCPI_P parameters, Stream& interface){
if (parameters.Size() > 0) { //if there is any value transmitted
onTime = String(parameters[0]).toInt();
interface.print(F("ON TIME SET TO: "));
interface.print(onTime);
interface.println(F(" ms"));
}else{
interface.println(F("ERROR"));
}
}
void GetOnTime(SCPI_C commands, SCPI_P parameters, Stream& interface){
interface.print(F("ON TIME SET TO: "));
interface.print(onTime);
interface.println(F(" ms"));
}
void SetOffTime(SCPI_C commands, SCPI_P parameters, Stream& interface){
if (parameters.Size() > 0) { //if there is any value transmitted
offTime = String(parameters[0]).toInt();
interface.print(F("OFF TIME SET TO: "));
interface.print(offTime);
interface.println(F(" ms"));
}else{
interface.println(F("ERROR"));
}
}
void GetOffTime(SCPI_C commands, SCPI_P parameters, Stream& interface){
interface.print(F("OFF TIME SET TO: "));
interface.print(offTime);
interface.println(F(" ms"));
}
void GetMemSpace(SCPI_C commands, SCPI_P parameters, Stream& interface){
interface.print("FREE SRAM: ");
interface.print(freeMemory());
interface.println(" Bytes");
}
void GetVBAT(SCPI_C commands, SCPI_P parameters, Stream& interface){
for(int i = 0; i < NUM_OF_MEAS; i++){
Serial.print((EEPROM.read(i) / 255.0) * 5.0);
Serial.print(", ");
}
}
void SetMeasInt(SCPI_C commands, SCPI_P parameters, Stream& interface){
if (parameters.Size() > 0) { //if there is any value transmitted
measInterval = String(parameters[0]).toInt();
interface.print(F("MEASUREMENT INTERVAL IS SET TO: "));
interface.print(measInterval);
interface.println(F(" min"));
}else{
interface.println(F("ERROR"));
}
}
void GetMeasInt(SCPI_C commands, SCPI_P parameters, Stream& interface){
interface.print("MEASUREMENT INTERVAL: ");
interface.print(measInterval);
interface.println(F(" min"));
}