diff --git a/README.md b/README.md index 9025eb1..7bc3db4 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,6 @@ We've got some interesting branches in this repo inspired by user feedback. Thes - 6 modes (Clock, Digital Clock, SPIRAL animation, TETRIS, SNAKE, PONG) - time update via NTP server - automatic summer/wintertime change -- automatic timezone selection - easy WIFI setup with WifiManager - configurable color - configurable night mode (start and end time) diff --git a/data/index.html b/data/index.html index 4bce60d..46ae4ba 100644 --- a/data/index.html +++ b/data/index.html @@ -354,7 +354,7 @@

WORDCLOCK 2.0

-
+
diff --git a/ledmatrix.cpp b/ledmatrix.cpp index 49f1faa..90e0413 100644 --- a/ledmatrix.cpp +++ b/ledmatrix.cpp @@ -206,7 +206,8 @@ void LEDMatrix::drawOnMatrix(float factor){ // loop over all minute indicator leds for(int i = 0; i < 4; i++){ uint32_t filteredColor = interpolateColor24bit(currentindicators[i], targetindicators[i], factor); - (*neomatrix).drawPixel(WIDTH - (1+i), HEIGHT, color24to16bit(filteredColor)); + (*neomatrix).drawPixel(3-i, HEIGHT, color24to16bit(filteredColor)); + //(*neomatrix).drawPixel(WIDTH - (1+i), HEIGHT, color24to16bit(filteredColor)); currentindicators[i] = filteredColor; totalCurrent += calcEstimatedLEDCurrent(filteredColor); } diff --git a/ledmatrix.h b/ledmatrix.h index e130512..4d3a855 100644 --- a/ledmatrix.h +++ b/ledmatrix.h @@ -9,7 +9,7 @@ // width of the led matrix #define WIDTH 11 // height of the led matrix -#define HEIGHT 11 +#define HEIGHT 10 #define DEFAULT_CURRENT_LIMIT 9999 diff --git a/multicastUDP_receiver.py b/multicastUDP_receiver.py index a5fea57..6c0e572 100644 --- a/multicastUDP_receiver.py +++ b/multicastUDP_receiver.py @@ -5,16 +5,12 @@ import queue # ip address of network interface -MCAST_IF_IP = '192.168.178.38' +MCAST_IF_IP = '192.168.1.55' multicast_group = '230.120.10.2' server_address = ('', 8123) - -def start(filters=None): - if filters is None: - filters = [] - +def start(filter = None): # Create the socket sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) @@ -23,51 +19,55 @@ def start(filters=None): print("Start") - # Tell the operating system to add the socket to the multicast group on the specified interface + # Tell the operating system to add the socket to the multicast group + # on all interfaces. group = socket.inet_aton(multicast_group) mreq = struct.pack('4s4s', group, socket.inet_aton(MCAST_IF_IP)) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) print("Ready") + saveCounter = 0 - # Initialize buffers and save counters for each filter - buffers = {filter_val: queue.Queue(20) for filter_val in filters} - save_counters = {filter_val: 0 for filter_val in filters} + buffer = queue.Queue(20) # Receive/respond loop while True: data, address = sock.recvfrom(1024) data_str = data.decode("utf-8").strip() - timestamped_data = f"[{address[0]} - {datetime.now().strftime('%b-%d-%Y_%H:%M:%S')}] {data_str}" - print(timestamped_data) - - # Check each filter and process data accordingly - for filter_val in filters: - if filter_val in data_str: - buffers[filter_val].put(timestamped_data) - if buffers[filter_val].full(): - buffers[filter_val].get() - - # Save data if specific keywords are found or if save counter is active - if "NTP-Update not successful" in data_str or "Start program" in data_str: - with open(f"log_{filter_val}.txt", 'a') as f: - while not buffers[filter_val].empty(): - f.write(buffers[filter_val].get() + "\n") - save_counters[filter_val] = 20 # Start the save counter - - if save_counters[filter_val] > 0: - with open(f"log_{filter_val}.txt", 'a') as f: - f.write(timestamped_data + "\n") - if save_counters[filter_val] == 1: - f.write("\n") - save_counters[filter_val] -= 1 + if filter is not None and filter not in data_str: + continue + data_str = "[" + str(address[0]) + " - " + datetime.now().strftime('%b-%d-%Y_%H:%M:%S') + "] " + data_str + print(data_str) + buffer.put(data_str) + if buffer.full(): + buffer.get() + + + if "NTP-Update not successful" in data_str or "Start program" in data_str: + f = open("log.txt",'a') + while not buffer.empty(): + f.write(buffer.get()) + f.write("\n") + f.close() + saveCounter = 20 + + if saveCounter > 0: + f = open("log.txt",'a') + f.write(data_str) + f.write("\n") + if saveCounter == 1: + f.write("\n") + f.close() + saveCounter -= 1 # Main if __name__ == '__main__': - # Check if filters are given - # Use filters as arguments: python3 multicastUDP_receiver.py "filter1" "filter2" ... + + # Check if a filter is given + # use filter as argument to filter messages: python3 multicastUDP_receiver.py "filter" + if len(sys.argv) > 1: - start(sys.argv[1:]) + start(sys.argv[1]) else: start() diff --git a/ntp_client_plus.cpp b/ntp_client_plus.cpp index 451bde3..79331f5 100644 --- a/ntp_client_plus.cpp +++ b/ntp_client_plus.cpp @@ -265,10 +265,10 @@ void NTPClientPlus::calcDate() daysInMonth[2] = 28; } - unsigned int dayOfYear = (days1900 - ((this->_dateYear - 1900) * 365) - leapDays); + this->_dayOfYear = (days1900 - ((this->_dateYear - 1900) * 365) - leapDays); // calc current month - this->_dateMonth = this->getMonth(dayOfYear); + this->_dateMonth = this->getMonth(this->_dayOfYear); this->_dateDay = 0; @@ -277,7 +277,7 @@ void NTPClientPlus::calcDate() { this->_dateDay = this->_dateDay + daysInMonth[i]; } - this->_dateDay = dayOfYear - this->_dateDay; + this->_dateDay = this->_dayOfYear - this->_dateDay; // calc day of week: // Monday = 1, Tuesday = 2, Wednesday = 3, Thursday = 4, Friday = 5, Saturday = 6, Sunday = 7 @@ -314,6 +314,16 @@ unsigned int NTPClientPlus::getDayOfWeek() return this->_dayOfWeek; } +/** + * @brief Getter for day of the year + * + * @return unsigned int + */ +unsigned int NTPClientPlus::getDayOfYear() +{ + return this->_dayOfYear; +} + /** * @brief Function to calc current year * diff --git a/ntp_client_plus.h b/ntp_client_plus.h index 847306c..2abe9a7 100644 --- a/ntp_client_plus.h +++ b/ntp_client_plus.h @@ -33,6 +33,7 @@ class NTPClientPlus{ String getFormattedDate(); void calcDate(); unsigned int getDayOfWeek(); + unsigned int getDayOfYear(); unsigned int getYear(); bool isLeapYear(unsigned int year); int getMonth(int dayOfYear); @@ -60,6 +61,7 @@ class NTPClientPlus{ unsigned int _dateMonth = 0; unsigned int _dateDay = 0; unsigned int _dayOfWeek = 0; + unsigned int _dayOfYear = 0; byte _packetBuffer[NTP_PACKET_SIZE]; diff --git a/pong.h b/pong.h index e5c226c..bf44200 100644 --- a/pong.h +++ b/pong.h @@ -21,7 +21,7 @@ #define DEBOUNCE_TIME_PONG 10 // in ms #define X_MAX 11 -#define Y_MAX 11 +#define Y_MAX 10 #define GAME_DELAY_PONG 80 // in ms #define BALL_DELAY_MAX 350 // in ms diff --git a/snake.h b/snake.h index f4619cb..364bc6a 100644 --- a/snake.h +++ b/snake.h @@ -20,7 +20,7 @@ #define DEBOUNCE_TIME_SNAKE 300 // in ms #define X_MAX 11 -#define Y_MAX 11 +#define Y_MAX 10 #define GAME_DELAY_SNAKE 400 // in ms diff --git a/tetris.h b/tetris.h index e5b372f..3a74c17 100644 --- a/tetris.h +++ b/tetris.h @@ -50,7 +50,7 @@ #define LEVELUP 4 // Number of rows before levelup, default 5 #define WIDTH 11 -#define HEIGHT 11 +#define HEIGHT 10 class Tetris{ diff --git a/wordclock_esp8266.ino b/wordclock_esp8266.ino index 12d0dd9..8399b70 100644 --- a/wordclock_esp8266.ino +++ b/wordclock_esp8266.ino @@ -98,7 +98,7 @@ enum direction {right, left, up, down}; // width of the led matrix #define WIDTH 11 // height of the led matrix -#define HEIGHT 11 +#define HEIGHT 10 // own datatype for state machine states #define NUM_STATES 6 @@ -127,6 +127,10 @@ const char WebserverURL[] = "www.wordclock.local"; int utcOffset = 60; // UTC offset in minutes +const unsigned int num_coeff = 5; // number of coeffients + 1 +// Sunsetminutes (without summer- wintertimeshift!) +const double sunsetMinutes[] = { 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1014, 1015, 1016, 1017, 1019, 1020, 1021, 1023, 1024, 1026, 1027, 1028, 1030, 1031, 1033, 1034, 1036, 1037, 1039, 1040, 1042, 1043, 1045, 1047, 1048, 1050, 1051, 1053, 1054, 1056, 1057, 1059, 1061, 1062, 1064, 1065, 1067, 1068, 1070, 1071, 1073, 1075, 1076, 1078, 1079, 1081, 1082, 1084, 1085, 1087, 1088, 1090, 1091, 1093, 1094, 1096, 1097, 1098, 1100, 1101, 1103, 1104, 1106, 1107, 1109, 1110, 1111, 1113, 1114, 1116, 1117, 1119, 1120, 1121, 1123, 1124, 1126, 1127, 1128, 1130, 1131, 1133, 1134, 1135, 1137, 1138, 1140, 1141, 1142, 1144, 1145, 1147, 1148, 1149, 1151, 1152, 1154, 1155, 1156, 1158, 1159, 1161, 1162, 1163, 1165, 1166, 1168, 1169, 1170, 1172, 1173, 1174, 1176, 1177, 1179, 1180, 1181, 1183, 1184, 1185, 1187, 1188, 1189, 1190, 1192, 1193, 1194, 1195, 1197, 1198, 1199, 1200, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210, 1211, 1212, 1213, 1214, 1215, 1216, 1216, 1217, 1218, 1219, 1219, 1220, 1221, 1221, 1222, 1222, 1223, 1223, 1223, 1224, 1224, 1224, 1224, 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1225, 1224, 1224, 1224, 1224, 1223, 1223, 1222, 1222, 1221, 1221, 1220, 1220, 1219, 1218, 1217, 1217, 1216, 1215, 1214, 1213, 1212, 1211, 1210, 1209, 1208, 1207, 1205, 1204, 1203, 1202, 1200, 1199, 1198, 1196, 1195, 1193, 1192, 1190, 1189, 1187, 1186, 1184, 1182, 1181, 1179, 1177, 1176, 1174, 1172, 1170, 1169, 1167, 1165, 1163, 1161, 1159, 1158, 1156, 1154, 1152, 1150, 1148, 1146, 1144, 1142, 1140, 1138, 1136, 1134, 1132, 1130, 1128, 1126, 1124, 1122, 1120, 1118, 1116, 1114, 1112, 1110, 1108, 1106, 1104, 1102, 1100, 1098, 1096, 1094, 1092, 1090, 1088, 1086, 1084, 1082, 1080, 1078, 1076, 1074, 1072, 1070, 1068, 1066, 1064, 1062, 1060, 1058, 1056, 1055, 1053, 1051, 1049, 1047, 1046, 1044, 1042, 1040, 1039, 1037, 1035, 1034, 1032, 1030, 1029, 1027, 1026, 1024, 1023, 1021, 1020, 1019, 1017, 1016, 1015, 1013, 1012, 1011, 1010, 1009, 1008, 1007, 1006, 1005, 1004, 1003, 1002, 1001, 1000, 1000, 999, 998, 998, 997, 997, 996, 996, 995, 995, 995, 994, 994, 994, 994, 994, 994, 994, 994, 994, 994, 995, 995, 995, 996, 996, 997, 997, 998, 998, 999, 1000, 1000, 1001, 1002, 1003, 1004}; + // ---------------------------------------------------------------------------------- // GLOBAL VARIABLES // ---------------------------------------------------------------------------------- @@ -612,23 +616,39 @@ void checkNightmode(){ logger.logString("Check nightmode"); int hours = ntp.getHours24(); int minutes = ntp.getMinutes(); - + int dayofyear = ntp.getDayOfYear(); + bool summertime = ntp.updateSWChange(); + + dayofyear = limit(dayofyear, 1, 365); nightMode = false; // Initial assumption // Convert all times to minutes for easier comparison int currentTimeInMinutes = hours * 60 + minutes; - int startInMinutes = nightModeStartHour * 60 + nightModeStartMin; int endInMinutes = nightModeEndHour * 60 + nightModeEndMin; + //double poly[] = {1.0208E+3, -170.4552E-3, 30.0056E-3, -173.0480E-6, 251.6062E-9}; // a0+a1*x+a2*x^2+a3*x^3+a4*x^4 + //int startInMinutes = poly_eval(poly, dayofyear); + //if (summertime == true) startInMinutes += 60; + //logger.logString("Sunset startInminutes (polynomial): " + String(startInMinutes)); + int startInMinutes = sunsetMinutes[dayofyear - 1]; + if (summertime == true) startInMinutes += 60; + logger.logString("Sunset startInminutes: " + String(startInMinutes)); + + loadBrightnessSettingsFromEEPROM(); + if (startInMinutes < endInMinutes && nightModeActivated) { // Same day scenario if (startInMinutes < currentTimeInMinutes && currentTimeInMinutes < endInMinutes) { - nightMode = true; - logger.logString("Nightmode active"); + brightness = (uint8_t)(brightness * 0.5); + ledmatrix.setBrightness(brightness); + logger.logString("Darkmode Brightness: " + String(brightness)); + logger.logString("Darkmode active"); } } else if (startInMinutes > endInMinutes && nightModeActivated) { // Overnight scenario if (currentTimeInMinutes >= startInMinutes || currentTimeInMinutes < endInMinutes) { - nightMode = true; - logger.logString("Nightmode active"); + brightness = (uint8_t)(brightness * 0.5); + ledmatrix.setBrightness(brightness); + logger.logString("Darkmode Brightness: " + String(brightness)); + logger.logString("Darkmode active"); } } } @@ -1157,3 +1177,35 @@ String leadingZero2Digit(int value){ msg += String(value); return msg; } + +/** + * @brief Polynomial evaluation (num_coeff = grade simplified) + * + * @param coeff + * @param x + * @return double + */ +double poly_eval(double coeff[num_coeff], double x) { + double result = coeff[num_coeff-1]; + for (size_t i = num_coeff-1; i --> 0;) + result = coeff[i] + x * result; + return result; +} + +/** + * @brief Limit value to min/max + * + * @param value + * @param min + * @param max + * @return int + */ +int limit(int value, int min, int max) { + if (value < min) { + return min; + } else if (value > max) { + return max; + } else { + return value; + } +} diff --git a/wordclockfunctions.ino b/wordclockfunctions.ino index ecbdc6e..94afdd2 100644 --- a/wordclockfunctions.ino +++ b/wordclockfunctions.ino @@ -1,5 +1,5 @@ -const String clockStringGerman = "ESPISTAFUNFVIERTELZEHNZWANZIGUVORTECHNICNACHHALBMELFUNFXCONTROLLEREINSEAWZWEIDREITUMVIERSECHSQYACHTSIEBENZWOLFZEHNEUNJUHR"; +const String clockStringGerman = "ESKISCHUFUFVIERTUNFZAAZWANZGSEVORABCHAUBIECMEISZWOISDRUVIERIFUFISTSACHSISIBNIACHTINUNIELZANIECHEUFIZWOUFIENGSI"; /** * @brief control the four minute indicator LEDs @@ -96,53 +96,53 @@ int showStringOnClock(String message, uint32_t color){ String timeToString(uint8_t hours,uint8_t minutes){ //ES IST - String message = "ES IST "; + String message = "ES ISCH "; //show minutes if(minutes >= 5 && minutes < 10) { - message += "FUNF NACH "; + message += "FUF AB "; } else if(minutes >= 10 && minutes < 15) { - message += "ZEHN NACH "; + message += "ZAA AB "; } else if(minutes >= 15 && minutes < 20) { - message += "VIERTEL NACH "; + message += "VIERTU AB "; } else if(minutes >= 20 && minutes < 25) { - message += "ZEHN VOR HALB "; + message += "ZWANZG AB "; } else if(minutes >= 25 && minutes < 30) { - message += "FUNF VOR HALB "; + message += "FUF VOR HAUBI "; } else if(minutes >= 30 && minutes < 35) { - message += "HALB "; + message += "HAUBI "; } else if(minutes >= 35 && minutes < 40) { - message += "FUNF NACH HALB "; + message += "FUF AB HAUBI "; } else if(minutes >= 40 && minutes < 45) { - message += "ZEHN NACH HALB "; + message += "ZWANZG VOR "; } else if(minutes >= 45 && minutes < 50) { - message += "VIERTEL VOR "; + message += "VIERTU VOR "; } else if(minutes >= 50 && minutes < 55) { - message += "ZEHN VOR "; + message += "ZAA VOR "; } else if(minutes >= 55 && minutes < 60) { - message += "FUNF VOR "; + message += "FUF VOR "; } //convert hours to 12h format @@ -150,7 +150,7 @@ String timeToString(uint8_t hours,uint8_t minutes){ { hours -= 12; } - if(minutes >= 20) + if(minutes >= 25) { hours++; } @@ -163,54 +163,49 @@ String timeToString(uint8_t hours,uint8_t minutes){ switch(hours) { case 0: - message += "ZWOLF "; + message += "ZWOUFI "; break; case 1: - message += "EIN"; + message += "EIS "; //EIN(S) - if(minutes > 4){ - message += "S"; - } - message += " "; break; case 2: - message += "ZWEI "; + message += "ZWOI "; break; case 3: - message += "DREI "; + message += "DRU "; break; case 4: - message += "VIER "; + message += "VIERI "; break; case 5: - message += "FUNF "; + message += "FUFI "; break; case 6: - message += "SECHS "; + message += "SACHSI "; break; case 7: - message += "SIEBEN "; + message += "SIBNI "; break; case 8: - message += "ACHT "; + message += "ACHTI "; break; case 9: - message += "NEUN "; + message += "NUNI "; break; case 10: - message += "ZEHN "; + message += "ZANI "; break; case 11: - message += "ELF "; + message += "EUFI "; break; } - if(minutes < 5) + if(minutes % 5 != 0) { - message += "UHR "; + message += "GSI "; } logger.logString("time as String: " + String(message)); return message; } -