From f6322ce0508eef81ea293deb71ddaca892461cce Mon Sep 17 00:00:00 2001 From: telegnom Date: Sat, 23 Feb 2019 16:44:01 +0100 Subject: [PATCH] initial version --- LICENSE | 21 ++ README.md | 22 ++ wordclock/wordclock.ino | 501 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 544 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 wordclock/wordclock.ino diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fc004ff --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 telegnom + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..bcd7f90 --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Wordclock + +Firmware für die WordClock vom c3e. + +## Anleitung + +1. Strom anschließen, 5V, USB-Netzeil +2. Ins WLAN `WordClock` verbinden +3. Im Browser die IP `192.168.4.1` aufrufen +4. WLAN auswählen und ggf. Passwort eingeben +5. Nach dem Speichern der Einstellungen verschwindet das WLAN +6. Uhr zeigt die Uhrzeit an + +## Design + +Die Idee für das Design wurden vom FabLab Nürnberg übernommen. Die Firmware ist eine Eigenentwicklung. + +http://wiki.fablab-nuernberg.de/w/Ding:Wortuhr_von_Udo + +## Lizenz + +MIT License diff --git a/wordclock/wordclock.ino b/wordclock/wordclock.ino new file mode 100644 index 0000000..d267932 --- /dev/null +++ b/wordclock/wordclock.ino @@ -0,0 +1,501 @@ +/** + * @file wordclock.ino + * @author telegnom (ccc@telegnom.org) + * @date 2019-02-23 + * @version 1.1 + * + * @brief firmware for the c3e wordclock + * + * requires the time.h and timezone.h libraries + * https://github.com/PaulStoffregen/Time/ + * https://github.com/JChristensen/Timezone/ + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define NUM_LEDS 42 +#define DATA_PIN 5 + +CRGB leds[NUM_LEDS]; + +// NTP Servers: +static const char ntpServerName[] = "pool.ntp.org"; + +// defining words + +#define M_ES 0 +#define M_IST 1 +#define M_FUENF 2 +#define M_ZEHN 3 +#define M_VIERTEL 4 +#define M_VOR 5 +#define M_NACH 6 +#define M_HALB 7 +#define M_UHR 8 +#define H_EINS 9 +#define H_EIN 9 +#define H_ZWEI 10 +#define H_DREI 11 +#define H_VIER 12 +#define H_FUENF 13 +#define H_SECHS 14 +#define H_SIEBEN 15 +#define H_ACHT 16 +#define H_NEUN 17 +#define H_ZEHN 18 +#define H_ELF 19 +#define H_ZWOELF 20 + +TimeChangeRule myDST = {"CEST", Last, Sun, Mar, 2, 120}; //Daylight time = UTC - 4 hours +TimeChangeRule mySTD = {"CET", Last, Sun, Oct, 2, 60}; //Standard time = UTC - 5 hours +Timezone myTZ(myDST, mySTD); + +int clockWords[21][10] = { + {3,-1,-1,-1,-1,-1,-1,-1,-1,-1}, // es 0 + {2,-1,-1,-1,-1,-1,-1,-1,-1,-1}, // ist 1 + {0,1,-1,-1,-1,-1,-1,-1,-1,-1}, // fuenf 2 + {4,5,-1,-1,-1,-1,-1,-1,-1,-1}, // zehn 3 + {8,9,10,-1,-1,-1,-1,-1,-1,-1}, // viertel 4 + {11,12,-1,-1,-1,-1,-1,-1,-1,-1}, // vor 5 + {6,7,-1,-1,-1,-1,-1,-1,-1,-1}, // nach 6 + {13,14,-1,-1,-1,-1,-1,-1,-1,-1}, // halb 7 + {40,41,-1,-1,-1,-1,-1,-1,-1,-1}, // uhr 8 + {19,20,-1,-1,-1,-1,-1,-1,-1,-1}, // ein 9 + {21,22,-1,-1,-1,-1,-1,-1,-1,-1}, // zwei 10 + {25,26,-1,-1,-1,-1,-1,-1,-1,-1}, // drei 11 + {23,24,-1,-1,-1,-1,-1,-1,-1,-1}, // vier 12 + {15,16,-1,-1,-1,-1,-1,-1,-1,-1}, // fuenf 13 + {27,28,-1,-1,-1,-1,-1,-1,-1,-1}, // sechs 14 + {33,34,35,-1,-1,-1,-1,-1,-1,-1}, // sieben 15 + {29,30,-1,-1,-1,-1,-1,-1,-1,-1}, // acht 16 + {17,18,-1,-1,-1,-1,-1,-1,-1,-1}, // neun 17 + {36,37,-1,-1,-1,-1,-1,-1,-1,-1}, // zehn 18 + {38,39,-1,-1,-1,-1,-1,-1,-1,-1}, // elf 19 + {31,32,-1,-1,-1,-1,-1,-1,-1,-1}, // zwölf 20 +}; + +WiFiUDP Udp; +unsigned int localPort = 8888; // local port to listen for UDP packets +time_t getNtpTime(); +time_t loctime; +uint8_t color = 0; +int lastmin = 100; +void printDigits(int digits); +void sendNTPpacket(IPAddress &address); + +void clear(bool show=true) +{ + for(int i = 0; i(leds, NUM_LEDS); + clear(); + Serial.begin(115200); + WiFiManager wifiManager; + while (!Serial) ; // Needed for Leonardo only + delay(250); + Serial.println("WordClock by telegnom"); + Serial.print("Setup Wifi ... "); + wifiManager.autoConnect("WordClock"); + Serial.println('[ ok ]'); + while (WiFi.status() != WL_CONNECTED) { + noWifi(); + delay(500); + } + + Serial.print("IP number assigned by DHCP is "); + Serial.println(WiFi.localIP()); + Serial.println("Starting UDP"); + Udp.begin(localPort); + Serial.print("Local port: "); + Serial.println(Udp.localPort()); + Serial.println("waiting for sync"); + setSyncProvider(getNtpTime); + setSyncInterval(600); +} + +int prevDisplay = 100; // when the digital clock was displayed + +void loop() +{ + // time_t utc = now(); + loctime = myTZ.toLocal(now()); + if (timeStatus() != timeNotSet) { + // if (prevDisplay != second(localtime)) + if (prevDisplay != second(loctime)) { //update the display only if time has changed + prevDisplay = second(loctime); + if (second()%10 == 0) + { + digitalClockDisplay(); + if (lastmin != minute(loctime)) + { + setClock(); + } + prevDisplay = second(loctime); + } + // } + } + } +} + +void set_word(int word, time_t current_time = loctime) { + int wordlen = sizeof(clockWords[word])/sizeof(clockWords[word][0]); + if (lastmin != minute(loctime)) + { + color += 40; + } + for (int i=0; i= 0) + { + leds[clockWords[word][i]] = CHSV(color, 255, 128); + Serial.print(clockWords[word][i]); + Serial.print(" "); + } + } + Serial.println(); +} + +void setClock() { + Serial.println("Updating display..."); + time_t current_time = loctime; + clear(false); + set_word(M_ES); // switch es on + set_word(M_IST); // switch ist on + getMinuteWord(); //get word for "minute" + getHourWord(); // get word for hour + FastLED.show(); //writing to led stipe + lastmin = minute(loctime); + Serial.println("... display updated."); +} + +int getHour() +{ + int h = int(hour(loctime)); + int m = int(minute(loctime)); + /* + get current hour if minute < 20 + Es ist viertel nach zehn + get next hour if minute >=20 + Es ist zehn vor halb elf + adjust hour to correct word around midnight: + 23:25 --> Es ist fuenf vor halb zwoelf + */ + Serial.println(m); + if (m >= 20) + { + if (h < 23) + { + return h+1; + } else + { + return 0; + } + } else + { + return h; + } +} + +void getHourWord() +{ + switch(getHour()) + { + case 12: + case 0: + set_word(H_ZWOELF); + Serial.println("zwoelf"); + break; + case 1: + case 13: + // special case to get sigular @ "ein uhr" + if (int(minute(loctime)) < 5) + { + set_word(H_EIN); + Serial.println("ein"); + } else + { + set_word(H_EINS); + Serial.println("eins"); + } + break; + case 2: + case 14: + set_word(H_ZWEI); + Serial.println("zwei"); + break; + case 3: + case 15: + set_word(H_DREI); + Serial.println("drei"); + break; + case 4: + case 16: + set_word(H_VIER); + Serial.println("vier"); + break; + case 5: + case 17: + set_word(H_FUENF); + Serial.println("fuenf"); + break; + case 6: + case 18: + set_word(H_SECHS); + Serial.println("sechs"); + break; + case 7: + case 19: + set_word(H_SIEBEN); + Serial.println("sieben"); + break; + case 8: + case 20: + set_word(H_ACHT); + Serial.println("acht"); + break; + case 9: + case 21: + set_word(H_NEUN); + Serial.println("neun"); + break; + case 10: + case 22: + set_word(H_ZEHN); + Serial.println("zehn"); + break; + case 11: + case 23: + set_word(H_ELF); + Serial.println("elf"); + break; + } +} + +void getMinuteWord() +{ + /* + get word for current "minute" + */ + switch(int(minute(loctime))) + { + case 0: + case 1: + case 2: + case 3: + case 4: + set_word(M_UHR); + Serial.println("uhr"); + break; + case 5: + case 6: + case 7: + case 8: + case 9: + set_word(M_FUENF); + set_word(M_NACH); + Serial.println("fuenf nach"); + break; + case 10: + case 11: + case 12: + case 13: + case 14: + set_word(M_ZEHN); + set_word(M_NACH); + Serial.println("zehn nach"); + break; + case 15: + case 16: + case 17: + case 18: + case 19: + set_word(M_VIERTEL); + set_word(M_NACH); + Serial.println("viertel nach"); + break; + case 20: + case 21: + case 22: + case 23: + case 24: + set_word(M_ZEHN); + set_word(M_VOR); + set_word(M_HALB); + Serial.println("zehn vor halb"); + break; + case 25: + case 26: + case 27: + case 28: + case 29: + set_word(M_FUENF); + set_word(M_VOR); + set_word(M_HALB); + Serial.println("fuenf vor halb"); + break; + case 30: + case 31: + case 32: + case 33: + case 34: + set_word(M_HALB); + Serial.println("halb"); + break; + case 35: + case 36: + case 37: + case 38: + case 39: + set_word(M_FUENF); + set_word(M_NACH); + set_word(M_HALB); + Serial.println("fuenf nach halb"); + break; + case 40: + case 41: + case 42: + case 43: + case 44: + set_word(M_ZEHN); + set_word(M_NACH); + set_word(M_HALB); + Serial.println("zehn nach halb"); + break; + case 45: + case 46: + case 47: + case 48: + case 49: + set_word(M_VIERTEL); + set_word(M_VOR); + Serial.println("viertel vor"); + break; + case 50: + case 51: + case 52: + case 53: + case 54: + set_word(M_ZEHN); + set_word(M_VOR); + Serial.println("zehn vor"); + break; + case 55: + case 56: + case 57: + case 58: + case 59: + case 60: + set_word(M_FUENF); + set_word(M_VOR); + Serial.println("fuenf vor"); + break; + } +} + +/*-------- NTP code ----------*/ + +const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message +byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets + +time_t getNtpTime() +{ + IPAddress ntpServerIP; // NTP server's ip address + + while (Udp.parsePacket() > 0) ; // discard any previously received packets + Serial.println("Transmit NTP Request"); + // get a random server from the pool + WiFi.hostByName(ntpServerName, ntpServerIP); + Serial.print(ntpServerName); + Serial.print(": "); + Serial.println(ntpServerIP); + sendNTPpacket(ntpServerIP); + uint32_t beginWait = millis(); + while (millis() - beginWait < 1500) { + int size = Udp.parsePacket(); + if (size >= NTP_PACKET_SIZE) { + Serial.println("Receive NTP Response"); + Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer + unsigned long secsSince1900; + // convert four bytes starting at location 40 to a long integer + secsSince1900 = (unsigned long)packetBuffer[40] << 24; + secsSince1900 |= (unsigned long)packetBuffer[41] << 16; + secsSince1900 |= (unsigned long)packetBuffer[42] << 8; + secsSince1900 |= (unsigned long)packetBuffer[43]; + return secsSince1900 - 2208988800UL; + } + } + Serial.println("No NTP Response :-("); + return 0; // return 0 if unable to get the time +} + +// send an NTP request to the time server at the given address +void sendNTPpacket(IPAddress &address) +{ + // set all bytes in the buffer to 0 + memset(packetBuffer, 0, NTP_PACKET_SIZE); + packetBuffer[0] = 0b11100011; // LI, Version, Mode + packetBuffer[1] = 0; // Stratum, or type of clock + packetBuffer[2] = 6; // Polling Interval + packetBuffer[3] = 0xEC; // Peer Clock Precision + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; + Udp.beginPacket(address, 123); //NTP requests are to port 123 + Udp.write(packetBuffer, NTP_PACKET_SIZE); + Udp.endPacket(); +}