Advertisement
okidokiss

ClockSketch_v7-7SCv3-TFO-3

Dec 8th, 2023
502
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 87.46 KB | Source Code | 0 0
  1. /* -[ClockSketch v7.3]----------------------------------------------------------------------------------------
  2.    https://www.instructables.com/ClockSketch-V7-Part-I/
  3.    
  4.    pre-configured for:
  5.    Retro 7 Segment Clock v3 - The Final One(s) (3 LEDs/Segment)
  6.    https://www.instructables.com/Retro-7-Segment-Clock-the-Final-Ones/
  7.    https://www.thingiverse.com/thing:5001559
  8.    
  9.    Arduino UNO/Nano/Pro Mini (AtMega328, 5V, 16 MHz), DS3231 RTC
  10.  
  11.    February 2023 - Daniel Cikic
  12.  
  13.    Serial Baud Rates:
  14.    Arduino: 57600
  15.    nodeMCU: 74880
  16.  
  17.   "C:\Users\XpUser\AppData\Local\Arduino15\packages\esp8266\tools\python3\3.7.2-post1/python3" -I "C:\Users\XpUser\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.1.2/tools/espota.py" -i 192.168.121.111   -p 8266  --auth="Clock7.3" -f "C:\Users\XpUser\AppData\Local\Temp\arduino\sketches\FFDFABAD2704F29E70802C3319CD8AAC/ClockSketch_v7-7SCv3-TFO-3.ino.bin"
  18.   "C:\Users\XpUser\AppData\Local\Arduino15\packages\esp8266\tools\python3\3.7.2-post1/python3" -I "C:\Users\XpUser\AppData\Local\Arduino15\packages\esp8266\hardware\esp8266\3.1.2/tools/espota.py" -i 192.168.121.111   -p 8266  --auth="Clock7.3" -f "C:\Users\XpUser\AppData\Local\Temp\arduino\sketches\FFDFABAD2704F29E70802C3319CD8AAC/ClockSketch_v7-7SCv3-TFO-3.ino.bin"
  19. -------------------------------------------------------------------------------------------------------------- */
  20.  
  21.  
  22. // comment below to disable serial in-/output and free some RAM
  23. //#define DEBUG
  24.  
  25. // nodeMCU - uncomment to compile this sketch for nodeMCU 1.0 / ESP8266, make sure to select the proper board
  26. // type inside the IDE! This mode is NOT supported and only experimental!
  27. #define NODEMCU
  28.  
  29. // useWiFi - enable WiFi support, WPS setup only! If no WPS support is available on a router check settings
  30. // further down, set useWPS to false and enter ssid/password there
  31. #define USEWIFI
  32.  
  33. // useNTP - enable NTPClient, requires NODEMCU and USEWIFI. This will also enforce AUTODST.
  34. // Configure a ntp server further down below!
  35. #define USENTP
  36.  
  37. // RTC selection - uncomment the one you're using, comment all others and make sure pin assignemts for
  38. // DS1302 are correct in the parameters section further down!
  39. // #define RTC_DS1302
  40. // #define RTC_DS1307
  41. #define RTC_DS3231
  42.  
  43. // autoDST - uncomment to enable automatic DST switching, check Time Change Rules below!
  44. #define AUTODST
  45.  
  46. // FADING - uncomment to enable fading effects for dots/digits, other parameters further down below
  47. #define FADING
  48.  
  49. // autoBrightness - uncomment to enable automatic brightness adjustments by using a photoresistor/LDR
  50. //
  51. #define AUTOBRIGHTNESS
  52.  
  53. // customDisplay - uncomment this to enable displayMyStuff(). It's an example of how to display values
  54. // at specified times, like temperature readouts
  55. //#define CUSTOMDISPLAY
  56.  
  57. // FastForward will speed up things and advance time, this is only for testing purposes!
  58. // Disables AUTODST, USENTP and USERTC.
  59. // #define FASTFORWARD
  60.  
  61. // customHelper will start some kind of assistant when adapting this sketch to other led layouts, this
  62. // tests all the steps neccessary to run it on almost any led strip configuration.
  63. //#define CUSTOMHELPER
  64.  
  65. /* ----------------------------------------------------------------------------------------------------- */
  66.  
  67.  
  68. #include <TimeLib.h>  // "Time" by Michael Margolis, used in all configs
  69. #include <EEPROM.h>   // required for reading/saving settings to eeprom
  70.  
  71.  
  72. /* Start RTC config/parameters--------------------------------------------------------------------------
  73.    Check pin assignments for DS1302 (SPI), others are I2C (A4/A5 on Arduino by default)                  
  74.    Currently all types are using the "Rtc by Makuna" library                                             */
  75. #ifdef RTC_DS1302
  76. #include <ThreeWire.h>
  77. #include <RtcDS1302.h>
  78. ThreeWire myWire(7, 6, 8);  // IO/DAT, SCLK, CE/RST
  79. RtcDS1302<ThreeWire> Rtc(myWire);
  80. #define RTCTYPE "DS1302"
  81. #define USERTC
  82. #endif
  83.  
  84. #ifdef RTC_DS1307
  85. #include <Wire.h>
  86. #include <RtcDS1307.h>
  87. RtcDS1307<TwoWire> Rtc(Wire);
  88. #define RTCTYPE "DS1307"
  89. #define USERTC
  90. #endif
  91.  
  92. #ifdef RTC_DS3231
  93. #include <Wire.h>
  94. #include <RtcDS3231.h>
  95. RtcDS3231<TwoWire> Rtc(Wire);
  96. #define RTCTYPE "DS3231"
  97. #define USERTC
  98. #endif
  99.  
  100. #if !defined(USERTC)
  101. #pragma message "No RTC selected, check definitions on top of the sketch!"
  102. #endif
  103. /* End RTC config/parameters---------------------------------------------------------------------------- */
  104.  
  105.  
  106. /* Start WiFi config/parameters------------------------------------------------------------------------- */
  107. #ifdef USEWIFI
  108. const bool useWPS = false;  // set to false to disable WPS and use credentials below
  109. const char* wifiSSID = "---- erased --- ";
  110. const char* wifiPWD = "---- erased --- ";
  111. #endif
  112. /* End WiFi config/parameters--------------------------------------------------------------------------- */
  113.  
  114.  
  115. /* Start NTP config/parameters--------------------------------------------------------------------------
  116.    Using NTP will enforce autoDST, so check autoDST/time zone settings below!                            */
  117. #ifdef USENTP
  118. /* I recommend using a local ntp service (many routers offer them), don't spam public ones with dozens
  119.      of requests a day, get a rtc! ^^                                                                    */
  120. #define NTPHOST "europe.pool.ntp.org"
  121. //#define NTPHOST "192.168.2.1"
  122. #ifndef AUTODST
  123. #define AUTODST
  124. #endif
  125. #endif
  126. /* End NTP config/parameters---------------------------------------------------------------------------- */
  127.  
  128.  
  129. /* Start autoDST config/parameters ----------------------------------------------------------------------
  130.    Comment/uncomment/add TimeChangeRules as needed, only use 2 (tcr1, tcr2), comment out unused ones!    
  131.    Enabling/disabling autoDST will require to set time again, clock will be running in UTC time if autoDST
  132.    is enabled, only display times are adjusted (check serial monitor with DEBUG defined!)                
  133.    This will also add options for setting the date (Year/Month/Day) when setting time on the clock!      */
  134. #ifdef AUTODST
  135. #include <Timezone.h>  // "Timezone" by Jack Christensen
  136. TimeChangeRule* tcr;
  137. //-----------------------------------------------
  138. /* US */
  139. // TimeChangeRule tcr1 = {"tcr1", First, Sun, Nov, 2, -360};   // utc -6h, valid from first sunday of november at 2am
  140. // TimeChangeRule tcr2 = {"tcr2", Second, Sun, Mar, 2, -300};  // utc -5h, valid from second sunday of march at 2am
  141. //-----------------------------------------------
  142. /* Europe */
  143. TimeChangeRule tcr1 = { "tcr1", Last, Sun, Oct, 3, 0 };   // standard/winter time, valid from last sunday of october at 3am, UTC + 1 hour (+60 minutes) (negative value like -300 for utc -5h)
  144. TimeChangeRule tcr2 = { "tcr2", Last, Sun, Mar, 2, 60 };  // daylight/summer time, valid from last sunday of march at 2am, UTC + 2 hours (+120 minutes)
  145.                                                           //-----------------------------------------------
  146. Timezone myTimeZone(tcr1, tcr2);
  147. #endif
  148. /* End autoDST config/parameters ----------------------------------------------------------------------- */
  149.  
  150.  
  151. /* Start autoBrightness config/parameters -------------------------------------------------------------- */
  152. uint8_t upperLimitLDR = 180;              // everything above this value will cause max brightness (according to current level) to be used (if it's higher than this)
  153. uint8_t lowerLimitLDR = 50;               // everything below this value will cause minBrightness to be used
  154. uint8_t minBrightness = 30;               // anything below this avgLDR value will be ignored
  155. const bool nightMode = true;              // nightmode true -> if minBrightness is used, colorizeOutput() will use a single color for everything, using HSV
  156. const uint8_t nightColor[2] = { 0, 70 };  // hue 0 = red, fixed brightness of 70, https://github.com/FastLED/FastLED/wiki/FastLED-HSV-Colors
  157. float factorLDR = 1.0;                    // try 0.5 - 2.0, compensation value for avgLDR. Set dbgLDR true & define DEBUG and watch the serial monitor. Looking...
  158. const bool dbgLDR = false;                // ...for values roughly in the range of 120-160 (medium room light), 40-80 (low light) and 0 - 20 in the dark
  159. #ifdef NODEMCU
  160. uint8_t pinLDR = 0;  // LDR connected to A0 (nodeMCU only offers this one)
  161. #else
  162. uint8_t pinLDR = 1;         // LDR connected to A1 (in case somebody flashes this sketch on arduino and already has an ldr connected to A1)
  163. #endif
  164. uint8_t intervalLDR = 75;  // read value from LDR every 75ms (most LDRs have a minimum of about 30ms - 50ms)
  165. uint16_t avgLDR = 0;       // we will average this value somehow somewhere in readLDR();
  166. uint16_t lastAvgLDR = 0;   // last average LDR value we got
  167. /* End autoBrightness config/parameters ---------------------------------------------------------------- */
  168.  
  169.  
  170. #define SKETCHNAME "ClockSketch v7.3"
  171. #define CLOCKNAME "Retro 7 Segment Clock v3 - The Final One(s), 3 LEDs/segment"
  172.  
  173.  
  174. /* Start button config/pins----------------------------------------------------------------------------- */
  175. #ifdef NODEMCU
  176. const uint8_t buttonA = 13;  // momentary push button, 1 pin to gnd, 1 pin to d7 / GPIO_13
  177. const uint8_t buttonB = 14;  // momentary push button, 1 pin to gnd, 1 pin to d5 / GPIO_14
  178. #else
  179. const uint8_t buttonA = 3;  // momentary push button, 1 pin to gnd, 1 pin to d3
  180. const uint8_t buttonB = 4;  // momentary push button, 1 pin to gnd, 1 pin to d4
  181. #endif
  182. /* End button config/pins------------------------------------------------------------------------------- */
  183.  
  184.  
  185. /* Start basic appearance config------------------------------------------------------------------------ */
  186. const bool dotsBlinking = false;                     // true = only light up dots on even seconds, false = always on
  187. const bool leadingZero = false;                     // true = enable a leading zero, 9:00 -> 09:00, 1:30 -> 01:30...
  188. uint8_t displayMode = 0;                            // 0 = 24h mode, 1 = 12h mode ("1" will also override setting that might be written to EEPROM!)
  189. uint8_t colorMode = 0;                              // different color modes, setting this to anything else than zero will overwrite values written to eeprom, as above
  190. uint16_t colorSpeed = 750;                          // controls how fast colors change, smaller = faster (interval in ms at which color moves inside colorizeOutput();)
  191. const bool colorPreview = true;                     // true = preview selected palette/colorMode using "8" on all positions for 3 seconds
  192. const uint8_t colorPreviewDuration = 3;             // duration in seconds for previewing palettes/colorModes if colorPreview is enabled/true
  193. const bool reverseColorCycling = false;             // true = reverse color movements
  194. const uint8_t brightnessLevels[3]{ 80, 130, 220 };  // 0 - 255, brightness Levels (min, med, max) - index (0-2) will be saved to eeprom
  195. uint8_t brightness = brightnessLevels[0];           // default brightness if none saved to eeprom yet / first run
  196. #ifdef FADING
  197. uint8_t fadeDigits = 2;  // fade digit segments, 0 = disabled, 1 = only fade out segments turned off, 2 = fade old out and fade new in
  198. uint8_t fadeDots = 0;    // fade dots, 0 = disabled, 1 = turn dots off without fading in/out after specidfied time, 2 = fade in and out
  199. uint8_t fadeDelay = 15;  // milliseconds between each fading step, 5-25 should work okay-ish
  200. #endif
  201. /* End basic appearance config-------------------------------------------------------------------------- */
  202.  
  203.  
  204. /* End of basic config/parameters section */
  205.  
  206.  
  207. /* End of feature/parameter section, unless changing advanced things/modifying the sketch there's absolutely nothing to do further down! */
  208.  
  209.  
  210. /* library, wifi and ntp stuff depending on above config/parameters */
  211. #ifdef NODEMCU
  212. #if defined(USENTP) && !defined(USEWIFI)  // enforce USEWIFI when USENTP is defined
  213. #define USEWIFI
  214. #pragma warning "USENTP without USEWIFI, enabling WiFi"
  215. #endif
  216. #ifdef USEWIFI
  217. #include <ESP8266WiFi.h>
  218. #include <WiFiUdp.h>
  219. #include <ArduinoOTA.h>
  220. #define OTA_Host_Name "Clock2"
  221. #define OTA_Password "Clock7.3"
  222. #endif
  223. #endif
  224.  
  225. #ifdef USENTP
  226. #include <NTPClient.h>
  227. WiFiUDP ntpUDP;
  228. NTPClient timeClient(ntpUDP, NTPHOST, 0, 60000);
  229. #endif
  230. /* end library stuff */
  231.  
  232.  
  233. /* setting feature combinations/options */
  234. #if defined(FASTFORWARD) || defined(CUSTOMHELPER)
  235. bool firstLoop = true;
  236. #ifdef USERTC
  237. #undef USERTC
  238. #endif
  239. #ifdef USEWIFI
  240. #undef USEWIFI
  241. #endif
  242. #ifdef USENTP
  243. #undef USENTP
  244. #endif
  245. #ifdef AUTODST
  246. #undef AUTODST
  247. #endif
  248. #endif
  249. /* setting feature combinations/options */
  250.  
  251.  
  252. /* Start of FastLED/clock stuff */
  253. #define LEDSTUFF
  254. #ifdef LEDSTUFF
  255. #ifdef NODEMCU
  256. #define FASTLED_ESP8266_RAW_PIN_ORDER  // this means we'll be using the raw esp8266 pin order -> GPIO_12, which is d6 on nodeMCU
  257. #define LED_PIN 12                     // led data in connected to GPIO_12 (d6/nodeMCU)
  258. #else
  259. #define FASTLED_ALLOW_INTERRUPTS 0  // AVR + WS2812 + IRQ = https://github.com/FastLED/FastLED/wiki/Interrupt-problems
  260. #define LED_PIN 6                   // led data in connected to d6 (arduino)
  261. #endif
  262.  
  263. #define LED_PWR_LIMIT 1300  // 500mA - Power limit in mA (voltage is set in setup() to 5v)
  264. #define LED_DIGITS 6        // 4 or 6 digits, HH:MM or HH:MM:SS
  265. #define LED_COUNT 103       // Total number of leds, 103 on Retro 7 Segment Clock v3 - The Final One(s) - 3 LEDs/segment
  266. #if (LED_DIGITS == 6)
  267. #define LED_COUNT 157  // leds on the 6 digit version
  268. #endif
  269.  
  270. #include <FastLED.h>
  271.  
  272. uint8_t markerHSV[3] = { 0, 127, 20 };  // this color will be used to "flag" leds for coloring later on while updating the leds
  273. CRGB leds[LED_COUNT];
  274. CRGBPalette16 currentPalette;
  275. #endif
  276.  
  277.  
  278. // start clock specific config/parameters
  279. /* Segment order, seen from the front:
  280.  
  281.    <  A  >
  282.  /\       /\
  283.  F        B
  284.  \/       \/
  285.    <  G  >
  286.  /\       /\
  287.  E        C
  288.  \/       \/
  289.    <  D  >
  290.  
  291. digit positions, seen from the front:
  292.  _   _   _   _   _   _
  293. |_| |_| |_| |_| |_| |_|
  294. |_| |_| |_| |_| |_| |_|
  295.  
  296.  0   1   2   3   4   5
  297.  
  298. Note: Digit positions for showSegments() depends on the order in which the segments
  299. are defined in segGroups[] below. Most of my things/clocks published so far start
  300. from the right side when seen from the front, TFO from the left. Others may have different
  301. orders, like Lazy 7 - QBE, which is using a single strip and has an order of
  302. 3, 0, 2, 1 for top left, top right, bottom left, bottom right.
  303.  
  304. "The Final One(s)" is starting from the left when looking at the front, so it's
  305. exactly the reverse order of the old/other ones. This doesn't really matter, that's
  306. what "digitPositions" a few lines below is for...
  307.  
  308. */
  309.  
  310.  
  311. /* Below is the configuration for led <> segment assignments.
  312.    LED_ACCESS_MODE 0 will use the two values inside each segment (led a, led b)
  313.    as they are - 2 leds per segment.
  314.    LED_ACCESS_MODE 1 will use the two values inside each segment (led a, led b)
  315.    as start and end value to get 2+ leds/segment.
  316.  
  317.    Example:
  318.    leds 0, 3 -> MODE 0 -> led 0 and 3 inside the segment -> 2 leds
  319.    leds 0, 3 -> MODE 1 -> led 0 - 3 inside the segment -> 4 leds
  320.  
  321.    Simply add all the leds into their corresponding segments inside the array.
  322.    The order of digits/strip routing doesn't really matter there, positions of
  323.    HH:MM:SS are assigned using digitPositions.
  324.  
  325. digitsLAM -> LED_ACCESS_MODE per digit
  326.  
  327. */
  328.  
  329. // defining access modes for each digit individually
  330. uint8_t digitsLAM[6] = { 1, 1, 1, 1, 1, 1 };
  331.  
  332. #if (LED_DIGITS == 4)
  333. const uint8_t digitPositions[4] = { 0, 1, 2, 3 };  // positions of HH:MM (3, 0, 2, 1 on L7-QBE)
  334. const uint16_t segGroups[28][2] PROGMEM = {
  335. #endif
  336.  
  337. #if (LED_DIGITS == 6)
  338.   const uint8_t digitPositions[6] = { 0, 1, 2, 3, 4, 5 };  // positions of HH:MM:SS
  339. const uint16_t segGroups[42][2] PROGMEM = {
  340. #endif
  341.  
  342.   /* segments 0-27, 4 digits x 7 segments */
  343.   /* digit position 0 */
  344.   { 6, 8 },    // top, a
  345.   { 3, 5 },    // top right, b
  346.   { 20, 22 },  // bottom right, c
  347.   { 17, 19 },  // bottom, d
  348.   { 14, 16 },  // bottom left, e
  349.   { 9, 11 },   // top left, f
  350.   { 0, 2 },    // center, g
  351.   /* digit position 1 */
  352.   { 40, 42 },  // top, a
  353.   { 37, 39 },  // top right, b
  354.   { 32, 34 },  // bottom right, c
  355.   { 29, 31 },  // bottom, d
  356.   { 26, 28 },  // bottom left, e
  357.   { 43, 45 },  // top left, f
  358.   { 46, 48 },  // center, g
  359.   /* digit position 2 */
  360.   { 60, 62 },  // top, a
  361.   { 57, 59 },  // top right, b
  362.   { 74, 76 },  // bottom right, c
  363.   { 71, 73 },  // bottom, d
  364.   { 68, 70 },  // bottom left, e
  365.   { 63, 65 },  // top left, f
  366.   { 54, 56 },  // center, g
  367.   /* digit position 3 */
  368.   { 94, 96 },          // top, a
  369.   { 91, 93 },          // top right, b
  370.   { 86, 88 },          // bottom right, c
  371.   { 83, 85 },          // bottom, d
  372.   { 80, 82 },          // bottom left, e
  373.   { 97, 99 },          // top left, f
  374.   { 100, 102 }         // center, g
  375. #if (LED_DIGITS == 6)  // add two digits, 14 segments, only used if LED_DIGITS is 6...
  376.                        /* segments 28-41, 6 digits x 7 segments */
  377.                        /* (bogus on some models which don't support 6 digits) */
  378.                        /* digit position 4 */
  379.   ,
  380.   { 114, 116 },  // top, a      !! do not remove the "," at the start of this line !!
  381.   { 111, 113 },  // top right, b
  382.   { 128, 130 },  // bottom right, c
  383.   { 125, 127 },  // bottom, d
  384.   { 122, 124 },  // bottom left, e
  385.   { 117, 119 },  // top left, f
  386.   { 108, 110 },  // center, g
  387.   /* digit position 5 */
  388.   { 148, 150 },  // top, a
  389.   { 145, 147 },  // top right, b
  390.   { 140, 142 },  // bottom right, c
  391.   { 137, 139 },  // bottom, d
  392.   { 134, 136 },  // bottom left, e
  393.   { 151, 153 },  // top left, f
  394.   { 154, 156 }   // center, g
  395. #endif           // ...end of digits 5+6
  396. };
  397.  
  398. #if (LED_DIGITS == 4)
  399. const uint16_t upperDots[2] PROGMEM = { 49, 50 };  // leds inside the upper dots (right on L7-QBE)
  400. const uint16_t lowerDots[2] PROGMEM = { 52, 53 };  // leds inside the lower dots (left on L7-QBE)
  401. #endif
  402.  
  403. #if (LED_DIGITS == 6)
  404. const uint16_t upperDots[4] PROGMEM = { 49, 50, 103, 104 };  // all the leds inside the upper dots (bogus values on some models which don't support 6 digits)
  405. const uint16_t lowerDots[4] PROGMEM = { 52, 53, 106, 107 };  // all the leds inside the lower dots (bogus values on some models which don't support 6 digits)
  406. #endif
  407.  
  408. // Using above arrays it's very easy to "talk" to the segments. Simply use 0-6 for the first 7 segments, add 7 (7-13) for the second one, 14-20 for third....
  409. const uint8_t digits[21][7] PROGMEM = {
  410.   /* Lets define 10 numbers (0-9) with 7 segments each, also adding some letters
  411.   1 = segment is on, 0 = segment is off   */
  412.   { 1, 1, 1, 1, 1, 1, 0 },  // 0 -> Show segments a - f, don't show g (center one)
  413.   { 0, 1, 1, 0, 0, 0, 0 },  // 1 -> Show segments b + c (top right and bottom right), nothing else
  414.   { 1, 1, 0, 1, 1, 0, 1 },  // 2 -> and so on...
  415.   { 1, 1, 1, 1, 0, 0, 1 },  // 3
  416.   { 0, 1, 1, 0, 0, 1, 1 },  // 4
  417.   { 1, 0, 1, 1, 0, 1, 1 },  // 5
  418.   { 1, 0, 1, 1, 1, 1, 1 },  // 6
  419.   { 1, 1, 1, 0, 0, 0, 0 },  // 7
  420.   { 1, 1, 1, 1, 1, 1, 1 },  // 8
  421.   { 1, 1, 1, 1, 0, 1, 1 },  // 9
  422.   { 0, 0, 0, 1, 1, 1, 1 },  // t -> some letters/symbols from here on (index 10-20, so this won't...
  423.   { 0, 0, 0, 0, 1, 0, 1 },  // r -> ...interfere with using digits 0-9 by using index 0-9
  424.   { 0, 1, 1, 1, 0, 1, 1 },  // y
  425.   { 0, 1, 1, 1, 1, 0, 1 },  // d
  426.   { 1, 0, 0, 1, 1, 1, 0 },  // C
  427.   { 1, 0, 0, 0, 1, 1, 1 },  // F
  428.   { 1, 1, 0, 0, 1, 1, 0 },  // some kind of "half letter M" (left half), displayed using two digits
  429.   { 1, 1, 1, 0, 0, 1, 0 },  // some kind of "half letter M" (right half), displayed using two digits
  430.   { 1, 1, 0, 0, 0, 1, 1 },  // °
  431.   { 0, 1, 1, 0, 1, 1, 1 },  // H
  432.   { 0, 0, 0, 0, 0, 0, 0 }   // "blank"
  433. };
  434.  
  435. uint8_t clockStatus = 1;  // Used for various things, don't mess around with it! 1 = startup
  436.                           // 0 = regular mode, 1 = startup, 9x = setup modes (90, 91, 92, 93...)
  437.  
  438. /* these values will be saved to EEPROM:
  439.   0 = index for selected palette
  440.   1 = index for selected brightness level
  441.   2 = displayMode, 12h/24h mode
  442.   3 = colorMode */
  443.  
  444. /* End of FastLED/clock stuff */
  445. // End clock specific configs/parameters
  446.  
  447. /* other variables */
  448. uint8_t btnRepeatCounter = 0;  // keeps track of how often a button press has been repeated
  449. /* */
  450.  
  451.  
  452. /* -- this is where the fun parts start -------------------------------------------------------------------------------------------------------- */
  453.  
  454.  
  455. void setup() {
  456.   ArduinoOTA.setHostname(OTA_Host_Name);
  457.   ArduinoOTA.setPassword(OTA_Password);
  458.   ArduinoOTA.begin();
  459. #ifdef DEBUG
  460.   while (millis() < 300) {  // safety delay for serial output
  461. #ifdef NODEMCU
  462.     yield();
  463. #endif
  464.   }
  465. #ifdef NODEMCU
  466.   Serial.begin(115200);
  467.   Serial.println(F("  "));
  468. #else
  469.   Serial.begin(57600);
  470.   Serial.println(F("  "));
  471. #endif
  472. #ifdef SKETCHNAME
  473.   Serial.print(SKETCHNAME);
  474.   Serial.println(F(" starting up..."));
  475. #endif
  476. #ifdef CLOCKNAME
  477.   Serial.print("Clock Type: ");
  478.   Serial.println(CLOCKNAME);
  479. #endif
  480. #ifdef RTCTYPE
  481.   Serial.print(F("Configured RTC: "));
  482.   Serial.println(RTCTYPE);
  483. #endif
  484. #ifdef LEDSTUFF
  485.   Serial.print(F("LED power limit: "));
  486.   Serial.print(LED_PWR_LIMIT);
  487.   Serial.println(F(" mA"));
  488.   Serial.print(F("Total LED count: "));
  489.   Serial.println(LED_COUNT);
  490.   Serial.print(F("LED digits: "));
  491.   Serial.println(LED_DIGITS);
  492. #endif
  493. #ifdef AUTODST
  494.   Serial.println(F("autoDST enabled"));
  495. #endif
  496. #ifdef NODEMCU
  497.   Serial.println(F("Configured for nodeMCU"));
  498. #ifdef USEWIFI
  499.   Serial.println(F("WiFi enabled"));
  500. #endif
  501. #ifdef USENTP
  502.   Serial.print(F("NTP enabled, NTPHOST: "));
  503.   Serial.println(NTPHOST);
  504. #endif
  505. #else
  506.   Serial.println(F("Configured for Arduino"));
  507. #endif
  508. #ifdef FASTFORWARD
  509.   Serial.println(F("!! FASTFORWARD defined !!"));
  510. #endif
  511.   while (millis() < 600) {  // safety delay for serial output
  512. #ifdef NODEMCU
  513.     yield();
  514. #endif
  515.   }
  516. #endif
  517.  
  518. #ifdef AUTOBRIGHTNESS
  519. #ifdef DEBUG
  520.   Serial.print(F("autoBrightness enabled, LDR using pin: "));
  521.   Serial.println(pinLDR);
  522. #endif
  523.   pinMode(pinLDR, INPUT);
  524. #endif
  525.  
  526.   pinMode(buttonA, INPUT_PULLUP);
  527.   pinMode(buttonB, INPUT_PULLUP);
  528.  
  529. #ifdef DEBUG
  530.   if (digitalRead(buttonA) == LOW || digitalRead(buttonB) == LOW) {
  531.     if (digitalRead(buttonA) == LOW) {
  532.       Serial.println(F("buttonA is LOW / pressed - check wiring!"));
  533.     }
  534.     if (digitalRead(buttonB) == LOW) {
  535.       Serial.println(F("buttonB is LOW / pressed - check wiring!"));
  536.     }
  537.   }
  538. #endif
  539.  
  540. #ifdef LEDSTUFF
  541.   FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, LED_COUNT).setCorrection(TypicalSMD5050).setTemperature(DirectSunlight).setDither(1);
  542.   FastLED.setMaxPowerInVoltsAndMilliamps(5, LED_PWR_LIMIT);
  543.   FastLED.clear();
  544.   FastLED.show();
  545. #ifdef CUSTOMHELPER  // customHelper() will run in a loop if defined!
  546.   while (1 > 0) {
  547.     customHelper();
  548.   }
  549. #endif
  550. #ifdef DEBUG
  551.   Serial.println(F("setup(): Lighting up some leds..."));
  552. #endif
  553.   for (uint8_t i = 0; i < LED_DIGITS; i++) {
  554.     showSegment(6, i);
  555.   }
  556.   FastLED.show();
  557. #endif
  558.  
  559. #ifdef NODEMCU  // if building for nodeMCU...
  560. #ifdef USEWIFI  // ...and if using WiFi.....
  561. #ifdef DEBUG
  562.   Serial.println(F("Starting up WiFi..."));
  563. #endif
  564.   WiFi.mode(WIFI_STA);  // set WiFi mode to STA...
  565.   if (useWPS) {
  566.     WiFi.begin(WiFi.SSID().c_str(), WiFi.psk().c_str());  // ...and start connecting using saved credentials...
  567. #ifdef DEBUG
  568.     Serial.println(F("Using WPS setup / saved credentials"));
  569. #endif
  570.   } else {
  571.     WiFi.begin(wifiSSID, wifiPWD);  // ...or credentials defined in the USEWIFI config section
  572. #ifdef DEBUG
  573.     Serial.println(F("Using credentials from sketch"));
  574. #endif
  575.   }
  576.   unsigned long startTimer = millis();
  577.   uint8_t wlStatus = 0;
  578.   uint8_t counter = 6;
  579. #ifdef DEBUG
  580.   Serial.print(F("Waiting for WiFi connection... "));
  581. #endif
  582.   while (wlStatus == 0) {
  583.     if (WiFi.status() != WL_CONNECTED) wlStatus = 0;
  584.     else wlStatus = 1;
  585. #ifdef LEDSTUFF
  586.     if (millis() - startTimer >= 1000) {
  587.       FastLED.clear();
  588.       showDigit(counter, digitPositions[3]);
  589.       FastLED.show();
  590.       if (counter > 0) counter--;
  591.       else wlStatus = 2;
  592.       startTimer = millis();
  593. #ifdef DEBUG
  594.       Serial.print(F("."));
  595. #endif
  596.     }
  597. #endif
  598. #ifdef NODEMCU
  599.     yield();
  600. #endif
  601.   }
  602.   if (WiFi.status() == WL_CONNECTED) {  // if status is connected...
  603. #ifdef USENTP                           // ...and USENTP defined...
  604.     timeClient.begin();                 // ...start timeClient
  605. #endif
  606.   }
  607. #ifdef DEBUG
  608.   Serial.println();
  609.   if (WiFi.status() != 0) {
  610.     Serial.print(F("setup(): Connected to SSID: "));
  611.     Serial.println(WiFi.SSID());
  612.     Serial.println(WiFi.localIP());
  613.     Serial.println(WiFi.getHostname());
  614.   } else Serial.println(F("setup(): WiFi connection failed."));
  615. #endif
  616. #endif
  617.   EEPROM.begin(512);
  618. #endif
  619.  
  620. #ifdef USERTC
  621.   Rtc.Begin();
  622.   if (Rtc.GetIsRunning() == false) {
  623. #ifdef DEBUG
  624.     Serial.println(F("setup(): RTC not running, trying to start..."));
  625. #endif
  626.     Rtc.SetIsRunning(true);
  627.   }
  628. #ifdef DEBUG
  629.   Serial.println(F("setup(): RTC.begin(), 2 second safety delay before"));
  630.   Serial.println(F("         doing any read/write actions!"));
  631. #endif
  632.   unsigned long tmp_time = millis();
  633.   while (millis() - tmp_time < 2000) {
  634. #ifdef NODEMCU
  635.     yield();
  636. #endif
  637.   }
  638. #ifdef DEBUG
  639.   Serial.println(F("setup(): RTC initialized"));
  640. #endif
  641. #else
  642. #ifdef DEBUG
  643.   Serial.println(F("setup(): No RTC defined!"));
  644.   setTime(12, 0, 0, 1, 1, 2000);
  645. #endif
  646. #endif
  647.  
  648. #ifdef LEDSTUFF
  649.   FastLED.clear();
  650.   FastLED.show();
  651. /* eeprom settings */
  652. #ifdef nodeMCU
  653.   EEPROM.begin(512);
  654. #endif
  655.   paletteSwitcher();
  656.   brightnessSwitcher();
  657.   colorModeSwitcher();
  658.   displayModeSwitcher();
  659. #endif
  660.  
  661. #ifdef FASTFORWARD
  662.   setTime(21, 59, 50, 31, 1, 2023);  // h, m, s, d, m, y to set the clock to when using FASTFORWARD
  663. #endif
  664.  
  665. #ifdef USENTP
  666.   syncHelper();
  667. #endif
  668.  
  669.   clockStatus = 0;  // change from 1 (startup) to 0 (running mode)
  670.  
  671. #ifdef DEBUG
  672.   printTime();
  673.   Serial.println(F("setup() done"));
  674.   Serial.println(F("------------------------------------------------------"));
  675. #endif
  676. }
  677.  
  678.  
  679. /* MAIN LOOP */
  680.  
  681.  
  682. void loop() {
  683.   ArduinoOTA.handle();
  684.   static uint8_t lastInput = 0;                  // != 0 if any button press has been detected
  685.   static uint8_t lastSecondDisplayed = 0;        // This keeps track of the last second when the display was updated (HH:MM and HH:MM:SS)
  686.   static unsigned long lastCheckRTC = millis();  // This will be used to read system time in case no RTC is defined (not supported!)
  687.   static bool doUpdate = false;                  // Update led content whenever something sets this to true. Coloring will always happen at fixed intervals!
  688. #ifdef USERTC
  689.   static RtcDateTime rtcTime = Rtc.GetDateTime();  // Get time from rtc
  690. #else
  691.   static time_t sysTime = now();  // if no rtc is defined, get local system time
  692. #endif
  693. #ifdef LEDSTUFF
  694.   static uint8_t refreshDelay = 5;     // refresh leds every 5ms
  695.   static long lastRefresh = millis();  // Keeps track of the last led update/FastLED.show() inside the loop
  696. #ifdef AUTOBRIGHTNESS
  697.   static long lastReadLDR = millis();
  698. #endif
  699. #endif
  700. #ifdef FASTFORWARD
  701.   static unsigned long lastFFStep = millis();  // Keeps track of last time increment if FASTFORWARD is defined
  702. #endif
  703.  
  704.   if (lastInput != 0) {          // If any button press is detected...
  705.     if (btnRepeatCounter < 1) {  // execute short/single press function(s)
  706. #ifdef DEBUG
  707.       Serial.print(F("loop(): "));
  708.       Serial.print(lastInput);
  709.       Serial.println(F(" (short press)"));
  710. #endif
  711.       if (lastInput == 1) {  // short press button A
  712. #ifdef LEDSTUFF
  713.         brightnessSwitcher();
  714. #endif
  715.       }
  716.       if (lastInput == 2) {  // short press button B
  717. #ifdef LEDSTUFF
  718.         paletteSwitcher();
  719. #endif
  720.       }
  721.       if (lastInput == 3) {  // short press button A + button B
  722.       }
  723.     } else if (btnRepeatCounter > 8) {  // execute long press function(s)...
  724.       btnRepeatCounter = 1;             // ..reset btnRepeatCounter to stop this from repeating
  725. #ifdef DEBUG
  726.       Serial.print(F("loop(): "));
  727.       Serial.print(lastInput);
  728.       Serial.println(F(" (long press)"));
  729. #endif
  730.       if (lastInput == 1) {  // long press button A
  731. #ifdef LEDSTUFF
  732.         colorModeSwitcher();
  733. #endif
  734.       }
  735.       if (lastInput == 2) {  // long press button B
  736. #ifdef LEDSTUFF
  737.         displayModeSwitcher();
  738. #endif
  739.       }
  740.       if (lastInput == 3) {  // long press button A + button B
  741. #ifdef USEWIFI               // if USEWIFI is defined and...
  742.         if (useWPS) {        // ...if useWPS is true...
  743.           connectWPS();      // connect WiFi using WPS
  744.         }
  745. #else  // if USEWIFI is not defined...
  746. #ifdef LEDSTUFF
  747.         FastLED.clear();
  748.         FastLED.show();
  749.         setupClock();  // start date/time setup
  750. #endif
  751. #endif
  752.       }
  753.       while (digitalRead(buttonA) == LOW || digitalRead(buttonB) == LOW) {  // wait until buttons are released again
  754. #ifdef LEDSTUFF
  755.         if (millis() % 50 == 0) {  // Refresh leds every 50ms to give optical feedback
  756.           colorizeOutput(colorMode);
  757.           FastLED.show();
  758.         }
  759. #endif
  760. #ifdef NODEMCU
  761.         yield();
  762. #endif
  763.       }
  764.     }
  765.   }
  766.  
  767. #ifdef FASTFORWARD                     // if FASTFORWARD is defined...
  768.   if (millis() - lastFFStep >= 250) {  // ...and 250ms have passed...
  769.     adjustTime(5);                     // ...add 5 seconds to current time
  770.     lastFFStep = millis();
  771.   }
  772. #endif
  773.  
  774.   if (millis() - lastCheckRTC >= 50) {  // check rtc/system time every 50ms
  775. #ifdef USERTC
  776.     rtcTime = Rtc.GetDateTime();
  777.     if (lastSecondDisplayed != rtcTime.Second()) doUpdate = true;
  778. #else
  779.     sysTime = now();
  780.     if (lastSecondDisplayed != second(sysTime)) doUpdate = true;
  781. #endif
  782.     lastCheckRTC = millis();
  783.   }
  784.  
  785.   if (doUpdate) {  // this will update the led array if doUpdate is true because of a new second from the rtc
  786. #ifdef USERTC
  787.     setTime(rtcTime.Hour(), rtcTime.Minute(), rtcTime.Second(),
  788.             rtcTime.Day(), rtcTime.Month(), rtcTime.Year());  // sync system time to rtc every second
  789. #ifdef LEDSTUFF
  790.     FastLED.clear();     // 1A - clear all leds...
  791.     displayTime(now());  // 2A - output rtcTime to the led array..
  792. #endif
  793.     lastSecondDisplayed = rtcTime.Second();
  794. #else
  795. #ifdef LEDSTUFF
  796.     FastLED.clear();     // 1B - clear all leds...
  797.     displayTime(now());  // 2B - output sysTime to the led array...
  798. #endif
  799.     lastSecondDisplayed = second(sysTime);
  800. #endif
  801. #ifdef CUSTOMDISPLAY
  802.     displayMyStuff();  // 3AB - if customDisplay is defined this will clear the led array again to display custom values...
  803. #endif
  804.     doUpdate = false;
  805. #ifdef DEBUG
  806.     if (second() % 20 == 0) {
  807.       printTime();
  808.     }
  809. #endif
  810. #ifdef USENTP                              // if NTP is enabled, resync to ntp server at 3:01:00 am
  811.     time_t t = myTimeZone.toLocal(now());  // convert current system time to local time zone according to rules on top of the sketch
  812.     if (hour(t) == 3 && minute(t) == 1 and second(t) == 0) {
  813.       syncHelper();
  814.     }
  815. #endif
  816.   }
  817.  
  818. #ifdef LEDSTUFF
  819.   colorizeOutput(colorMode);  // 1C, 2C, 3C...colorize the data inside the led array right now...
  820. #ifdef AUTOBRIGHTNESS
  821.   if (millis() - lastReadLDR >= intervalLDR) {  // if LDR is enabled and sample interval has been reached...
  822.     readLDR();                                  // ...call readLDR();
  823.     if (abs(avgLDR - lastAvgLDR) >= 5) {        // if avgLDR has changed for more than +/- 5 update lastAvgLDR
  824.       lastAvgLDR = avgLDR;
  825.       FastLED.setBrightness(avgLDR);
  826.     }
  827.     lastReadLDR = millis();
  828.   }
  829. #endif
  830. #ifdef FADING
  831.   digitsFader();
  832.   dotsFader();
  833. #endif
  834.   if (millis() - lastRefresh >= refreshDelay) {
  835.     FastLED.show();
  836.     lastRefresh = millis();
  837.   }
  838. #endif
  839.  
  840.   lastInput = inputButtons();
  841. }
  842.  
  843.  
  844. /* */
  845.  
  846.  
  847. #ifdef LEDSTUFF
  848.  
  849. #ifdef CUSTOMDISPLAY
  850. void displayMyStuff() {
  851.   /* One way to display custom sensor data/other things. displayMyStuff() is then called inside the doUpdate if statement inside
  852.      void loop() - after updating the leds but before calling colorizeOutput() and FastLED.show()                              */
  853.   if (second() >= 30 && second() < 40) {                 // only do something if current second is 30-39
  854. #ifdef RTC_DS3231                                        // if DS3231 is used we can read the temperature from that for demo purposes here
  855.     float rtcTemp = Rtc.GetTemperature().AsFloatDegC();  // get temperature in °C as float (25.75°C)....
  856.     uint8_t tmp = round(rtcTemp);                        // ...and round (26°C)
  857. #else
  858.     uint8_t tmp = 99;  // get whatever value from whatever sensor into tmp
  859. #endif
  860.     FastLED.clear();
  861.     if (LED_DIGITS == 4) {                     // if 4 digits, display following content:
  862.       showDigit(tmp / 10, digitPositions[0]);  // tmp (26°C) / 10 = 2 on position 1 of HH
  863.       showDigit(tmp % 10, digitPositions[1]);  // tmp (26°C) % 10 = 6 on position 2 of HH
  864.       showDigit(18, digitPositions[2]);        // ° symbol from array digits[][] on position 1 of MM
  865.       showDigit(14, digitPositions[3]);        // C from array digits[][] on position 2 of MM
  866.     }
  867.     if (LED_DIGITS == 6) {                     // if 6 digits....
  868.       showDigit(tmp / 10, digitPositions[2]);  // ...do the above using MM:SS positions instead of HH:MM
  869.       showDigit(tmp % 10, digitPositions[3]);
  870.       showDigit(18, digitPositions[4]);
  871.       showDigit(14, digitPositions[5]);
  872.     }
  873.   }
  874. }
  875. #endif
  876.  
  877.  
  878. #ifdef FADING
  879. void fadeSegment(uint8_t pos, uint8_t segment, uint8_t amount, uint8_t fadeType) {
  880.   /* this will check if the first led of a given segment is lit and if it is, will fade by  
  881.      amount using fadeType. fadeType is important because when fading things in that where
  882.      off previously we must avoid setting them black at first - hence fadeLightBy instead
  883.      of fadeToBlack.  */
  884.   uint8_t ledAM = digitsLAM[pos];  // led access mode according to the position
  885.   if (leds[pgm_read_word_near(&segGroups[segment + pos * 7][0])]) {
  886.     if (ledAM == 0) {
  887.       for (uint8_t i = 0; i < 2; i++) {
  888.         if (fadeType == 0) {
  889.           leds[pgm_read_word_near(&segGroups[segment + pos * 7][i])].fadeToBlackBy(amount);
  890.         } else {
  891.           leds[pgm_read_word_near(&segGroups[segment + pos * 7][i])].fadeLightBy(amount);
  892.         }
  893.       }
  894.     }
  895.     if (ledAM == 1) {
  896.       uint16_t startLed = pgm_read_word_near(&segGroups[segment + pos * 7][0]);
  897.       uint16_t endLed = pgm_read_word_near(&segGroups[segment + pos * 7][1]);
  898.       for (uint16_t i = startLed; i <= endLed; i++) {
  899.         if (fadeType == 0) {
  900.           leds[i].fadeToBlackBy(amount);
  901.         } else {
  902.           leds[i].fadeLightBy(amount);
  903.         }
  904.       }
  905.     }
  906.   }
  907. }
  908.  
  909.  
  910. void digitsFader() {
  911.   if (fadeDigits == 0) return;
  912.   static unsigned long firstRun = 0;                       // time when a change has been detected and fading starts
  913.   static unsigned long lastRun = 0;                        // used to store time when this function was executed the last time
  914.   static boolean active = false;                           // will be used as a flag when to do something / fade segments
  915.   static uint8_t previousSegments[LED_DIGITS][7] = { 0 };  // all the segments lit after the last run
  916.   static uint8_t currentSegments[LED_DIGITS][7] = { 0 };   // all the segments lit right now
  917.   static uint8_t changedSegments[LED_DIGITS][7] = { 0 };   // used to store the differences -> 1 = led has been turned off, fade out, 2 = was off, fade in
  918.   static uint8_t fadeSteps = 15;                           // steps used to fade dots in or out
  919.   lastRun = millis();
  920.   if (!active) {  // this will check if....
  921.     firstRun = millis();
  922.     for (uint8_t digitPos = 0; digitPos < LED_DIGITS; digitPos++) {  // ...any of the segments are on....
  923.       for (uint8_t segmentPos = 0; segmentPos < 7; segmentPos++) {
  924.         if (leds[pgm_read_word_near(&segGroups[segmentPos + digitPos * 7][0])]) {
  925.           currentSegments[digitPos][segmentPos] = 1;
  926.         } else {
  927.           currentSegments[digitPos][segmentPos] = 0;
  928.         }
  929.         if (currentSegments[digitPos][segmentPos] != previousSegments[digitPos][segmentPos]) {  // ...and compare them to the previous displayed segments.
  930.           active = true;                                                                        // if a change has been detected, set active = true so fading gets executed
  931. #ifdef DEBUG
  932.           Serial.print(F("digitPos: "));
  933.           Serial.print(digitPos);
  934.           Serial.print(F(" - segmentPos: "));
  935.           Serial.print(segmentPos);
  936.           Serial.print(F(" was "));
  937. #endif
  938.           if (currentSegments[digitPos][segmentPos] == 0) {
  939.             changedSegments[digitPos][segmentPos] = 1;
  940. #ifdef DEBUG
  941.             Serial.println(F("ON, is now OFF"));
  942. #endif
  943.           } else {
  944.             changedSegments[digitPos][segmentPos] = 2;
  945. #ifdef DEBUG
  946.             Serial.println(F("OFF, is now ON"));
  947. #endif
  948.           }
  949.         }
  950.       }
  951.     }
  952.   }
  953.   if (active) {  // this part is executed once a change has been detected....
  954.     static uint8_t counter = 1;
  955.     static unsigned long lastFadeStep = millis();
  956.     for (uint8_t digitPos = 0; digitPos < LED_DIGITS; digitPos++) {  // redraw segments that have turned off, so we can fade them out...
  957.       for (uint8_t segmentPos = 0; segmentPos < 7; segmentPos++) {
  958.         if (changedSegments[digitPos][segmentPos] == 1) {
  959.           showSegment(segmentPos, digitPos);
  960.         }
  961.       }
  962.     }
  963.     colorizeOutput(colorMode);  // colorize again after redraw, so colors keep consistent
  964.     for (uint8_t digitPos = 0; digitPos < LED_DIGITS; digitPos++) {
  965.       for (uint8_t segmentPos = 0; segmentPos < 7; segmentPos++) {
  966.         if (changedSegments[digitPos][segmentPos] == 1) {                       // 1 - segment has turned on, this one has to be faded in
  967.           fadeSegment(digitPos, segmentPos, counter * (255.0 / fadeSteps), 0);  // fadeToBlackBy, segments supposed to be off/fading out
  968.         }
  969.         if (changedSegments[digitPos][segmentPos] == 2) {  // 2 - segment has turned off, this one has to be faded out
  970.           if (fadeDigits == 2) {
  971.             fadeSegment(digitPos, segmentPos, 255 - counter * (255.0 / fadeSteps), 1);  // fadeLightBy, segments supposed to be on/fading in
  972.           }
  973.         }
  974.       }
  975.     }
  976.     if (millis() - lastFadeStep >= fadeDelay) {
  977.       counter++;
  978.       lastFadeStep = millis();
  979.     }
  980.     if (counter > fadeSteps) {  // done with fading, reset variables...
  981.       counter = 1;
  982.       active = false;
  983.       for (uint8_t digitPos = 0; digitPos < LED_DIGITS; digitPos++) {  // and save current segments to previousSegments
  984.         for (uint8_t segmentPos = 0; segmentPos < 7; segmentPos++) {
  985.           if (leds[pgm_read_word_near(&segGroups[segmentPos + digitPos * 7][0])]) {
  986.             previousSegments[digitPos][segmentPos] = 1;
  987.           } else {
  988.             previousSegments[digitPos][segmentPos] = 0;
  989.           }
  990.           changedSegments[digitPos][segmentPos] = 0;
  991.         }
  992.       }
  993. #ifdef DEBUG
  994.       Serial.print(F("digit fading sequence took "));  // for debugging/checking duration - fading should never take longer than 1000ms!
  995.       Serial.print(millis() - firstRun);
  996.       Serial.println(F(" ms"));
  997. #endif
  998.     }
  999.   }
  1000. }
  1001.  
  1002.  
  1003. void dotsFader() {
  1004.   if (fadeDots == 0) return;
  1005.   static unsigned long firstRun = 0;
  1006.   static unsigned long lastRun = 0;
  1007.   static boolean active = false;
  1008.   static uint8_t fadeSteps = 15;
  1009.   lastRun = millis();
  1010.   if (!active) {
  1011.     if (leds[pgm_read_word_near(&upperDots[0])]) {
  1012.       active = true;
  1013.       firstRun = millis();
  1014.     }
  1015.   }
  1016.   if (fadeDots == 1 && active) {  // action = 1, simply turn off specidifc leds after 500ms
  1017.     if (lastRun - firstRun >= 500) {
  1018.       for (uint8_t i = 0; i < (sizeof(upperDots) / sizeof(upperDots[0])); i++) {
  1019.         leds[pgm_read_word_near(&upperDots[i])].setHSV(0, 0, 0);
  1020.       }
  1021.       for (uint8_t i = 0; i < (sizeof(lowerDots) / sizeof(lowerDots[0])); i++) {
  1022.         leds[pgm_read_word_near(&lowerDots[i])].setHSV(0, 0, 0);
  1023.       }
  1024.       active = false;
  1025.     }
  1026.   }
  1027.   if (fadeDots == 2 && active) {  // fade in/out dots
  1028.     static uint8_t counter = 1;
  1029.     static unsigned long lastFadeStep = millis();
  1030.     static boolean fadeInDone = true;
  1031.     if (!fadeInDone) {
  1032.       for (uint8_t i = 0; i < (sizeof(upperDots) / sizeof(upperDots[0])); i++) {
  1033.         leds[pgm_read_word_near(&upperDots[i])].fadeToBlackBy(255 - counter * (255.0 / fadeSteps));
  1034.       }
  1035.       for (uint8_t i = 0; i < (sizeof(lowerDots) / sizeof(lowerDots[0])); i++) {
  1036.         leds[pgm_read_word_near(&lowerDots[i])].fadeToBlackBy(255 - counter * (255.0 / fadeSteps));
  1037.       }
  1038.       if (millis() - lastFadeStep >= fadeDelay) {
  1039.         counter++;
  1040.         lastFadeStep = millis();
  1041.       }
  1042.       if (counter > fadeSteps) {
  1043.         counter = 1;
  1044.         fadeInDone = true;
  1045. #ifdef DEBUG
  1046.         Serial.print(F("dot fade-in sequence took "));  // for debugging/checking
  1047.         Serial.print(millis() - firstRun);
  1048.         Serial.println(F(" ms"));
  1049. #endif
  1050.       }
  1051.     }
  1052.     if (lastRun - firstRun >= 950 - fadeDelay * fadeSteps) {
  1053.       for (uint8_t i = 0; i < (sizeof(upperDots) / sizeof(upperDots[0])); i++) {
  1054.         leds[pgm_read_word_near(&upperDots[i])].fadeToBlackBy(counter * (255.0 / fadeSteps));
  1055.       }
  1056.       for (uint8_t i = 0; i < (sizeof(lowerDots) / sizeof(lowerDots[0])); i++) {
  1057.         leds[pgm_read_word_near(&lowerDots[i])].fadeToBlackBy(counter * (255.0 / fadeSteps));
  1058.       }
  1059.       if (millis() - lastFadeStep >= fadeDelay) {
  1060.         counter++;
  1061.         lastFadeStep = millis();
  1062.       }
  1063.       if (counter > fadeSteps) {
  1064.         counter = 1;
  1065.         active = false;
  1066.         fadeInDone = false;
  1067. #ifdef DEBUG
  1068.         Serial.print(F("dot fading sequence took "));  // for debugging/checking
  1069.         Serial.print(millis() - firstRun);
  1070.         Serial.println(F(" ms"));
  1071. #endif
  1072.       }
  1073.     }
  1074.   }
  1075. }
  1076. #endif
  1077.  
  1078.  
  1079. #ifdef AUTOBRIGHTNESS
  1080. void readLDR() {  // read LDR value 5 times and write average to avgLDR
  1081.   static uint8_t runCounter = 1;
  1082.   static uint16_t tmp = 0;
  1083.   uint8_t readOut = map(analogRead(pinLDR), 0, 1023, 0, 250);
  1084.   tmp += readOut;
  1085.   if (runCounter == 5) {
  1086.     avgLDR = (tmp / 5) * factorLDR;
  1087.     tmp = 0;
  1088.     runCounter = 0;
  1089. #ifdef DEBUG
  1090.     if (dbgLDR) {
  1091.       Serial.print(F("readLDR(): avgLDR value: "));
  1092.       Serial.print(avgLDR);
  1093.     }
  1094. #endif
  1095.     if (avgLDR < minBrightness) avgLDR = minBrightness;
  1096.     if (avgLDR > brightness) avgLDR = brightness;
  1097.     if (avgLDR >= upperLimitLDR && avgLDR < brightness) avgLDR = brightness;  // if avgLDR is above upperLimitLDR switch to max current brightness
  1098.     if (avgLDR <= lowerLimitLDR) avgLDR = minBrightness;                      // if avgLDR is below lowerLimitLDR switch to minBrightness
  1099. #ifdef DEBUG
  1100.     if (dbgLDR) {
  1101.       Serial.print(F(" - adjusted to: "));
  1102.       Serial.println(avgLDR);
  1103.     }
  1104. #endif
  1105.   }
  1106.   runCounter++;
  1107. }
  1108. #endif
  1109.  
  1110. void setupClock() {
  1111.   /* This sets time and date (if AUTODST is defined) on the clock/rtc */
  1112.   clockStatus = 90;                                                     // clockStatus 9x = setup, relevant for other functions/coloring
  1113.   while (digitalRead(buttonA) == LOW || digitalRead(buttonB) == LOW) {  // do nothing until both buttons are released to avoid accidental inputs right away
  1114. #ifdef NODEMCU
  1115.     yield();
  1116. #endif
  1117.   }
  1118.   tmElements_t setupTime;  // Create a time element which will be used. Using the current time would
  1119.   setupTime.Hour = 12;     // give some problems (like time still running while setting hours/minutes)
  1120.   setupTime.Minute = 0;    // Setup starts at 12 (12 pm) (utc 12 if AUTODST is defined)
  1121.   setupTime.Second = 0;    //
  1122.   setupTime.Day = 1;       // date settings only used when AUTODST is defined, but will set them anyways
  1123.   setupTime.Month = 2;     // see above
  1124.   setupTime.Year = 23;     // current year - 2000 (2023 - 2000 = 23)
  1125. #ifdef USERTC
  1126.   RtcDateTime writeTime;
  1127. #endif
  1128. #ifdef AUTODST
  1129.   clockStatus = 91;  // 91 = y/m/d setup
  1130.   uint8_t y, m, d;
  1131.   y = getUserInput(12, 20, 23, 99);  // show Y + blank, get value from 21 - 99 into y
  1132.   setupTime.Year = y;                // 2 digit year + 30 (epoch), so we get offset from 1970
  1133.   m = getUserInput(16, 17, 1, 12);   // show M, get value from 1 - 12 into m
  1134.   setupTime.Month = m;
  1135.   if (m == 2) {
  1136.     if (leapYear(y + 2000)) {  // check for leap year...
  1137. #ifdef DEBUG                   // ...and get according day input ranges for each month
  1138.       Serial.println(F("setupClock(): Leap year detected"));
  1139. #endif
  1140.       d = getUserInput(13, 20, 1, 29);
  1141.     } else {
  1142.       d = getUserInput(13, 20, 1, 28);
  1143.     }
  1144.   }
  1145.   if (m == 1 || m == 3 || m == 5 || m == 7 || m == 8 || m == 10 || m == 12) {
  1146.     d = getUserInput(13, 20, 1, 31);
  1147.   }
  1148.   if (m == 4 || m == 6 || m == 9 || m == 11) {
  1149.     d = getUserInput(13, 20, 1, 30);
  1150.   }
  1151.   setupTime.Day = d;
  1152. #ifdef USERTC
  1153.   writeTime = { 2000 + y, setupTime.Month, setupTime.Day,
  1154.                 setupTime.Hour, setupTime.Minute, setupTime.Second };
  1155.   Rtc.SetDateTime(writeTime);
  1156.   setTime(writeTime.Hour(), writeTime.Minute(), writeTime.Second(), writeTime.Day(), writeTime.Month(), writeTime.Year());
  1157. #else
  1158.   setTime(makeTime(setupTime));
  1159. #endif
  1160. #ifdef DEBUG
  1161.   Serial.print(F("setupClock(): now is "));
  1162.   Serial.println(now());
  1163. #endif
  1164. #endif
  1165.   uint8_t lastInput = 0;
  1166.   // hours
  1167.   while (lastInput != 2) {
  1168.     clockStatus = 92;  // 92 = HH setup
  1169.     if (lastInput == 1) {
  1170.       if (setupTime.Hour < 23) {
  1171.         setupTime.Hour++;
  1172.       } else {
  1173.         setupTime.Hour = 0;
  1174.       }
  1175.     }
  1176.     displayTime(makeTime(setupTime));
  1177.     lastInput = inputButtons();
  1178.   }
  1179.   lastInput = 0;
  1180.   // minutes
  1181.   while (lastInput != 2) {
  1182.     clockStatus = 93;  // 93 = MM setup
  1183.     if (lastInput == 1) {
  1184.       if (setupTime.Minute < 59) {
  1185.         setupTime.Minute++;
  1186.       } else {
  1187.         setupTime.Minute = 0;
  1188.       }
  1189.     }
  1190.     displayTime(makeTime(setupTime));
  1191.     lastInput = inputButtons();
  1192.   }
  1193.   lastInput = 0;
  1194.   // seconds
  1195.   if (LED_DIGITS == 6) {
  1196.     while (lastInput != 2) {
  1197.       clockStatus = 94;  // 94 = SS setup
  1198.       if (lastInput == 1) {
  1199.         if (setupTime.Second < 59) {
  1200.           setupTime.Second++;
  1201.         } else {
  1202.           setupTime.Second = 0;
  1203.         }
  1204.       }
  1205.       displayTime(makeTime(setupTime));
  1206.       lastInput = inputButtons();
  1207.     }
  1208.     lastInput = 0;
  1209.   }
  1210. #ifdef DEBUG
  1211. #ifdef AUTODST
  1212.   Serial.print(F("setupClock(): "));
  1213.   Serial.print(F("Y/M/D -> "));
  1214.   Serial.print(2000 + setupTime.Year);
  1215.   Serial.print(F("/"));
  1216.   Serial.print(setupTime.Month);
  1217.   Serial.print(F("/"));
  1218.   Serial.println(setupTime.Day);
  1219. #endif
  1220.   Serial.print(F("setupClock(): "));
  1221.   Serial.print(F("HH:MM:SS -> "));
  1222. #ifdef AUTODST
  1223.   Serial.print(F("AUTODST enabled, setting LOCAL time -> "));
  1224. #endif
  1225.   if (setupTime.Hour < 10) Serial.print(F("0"));
  1226.   Serial.print(setupTime.Hour);
  1227.   Serial.print(F(":"));
  1228.   if (setupTime.Minute < 10) Serial.print(F("0"));
  1229.   Serial.print(setupTime.Minute);
  1230.   Serial.print(F(":"));
  1231.   if (setupTime.Second < 10) Serial.print(F("0"));
  1232.   Serial.println(setupTime.Second);
  1233. #endif
  1234. #ifdef USERTC
  1235.   writeTime = { 2000 + setupTime.Year, setupTime.Month, setupTime.Day,
  1236.                 setupTime.Hour, setupTime.Minute, setupTime.Second };
  1237. #ifdef AUTODST
  1238.   setupTime.Year += 30;
  1239.   time_t t = myTimeZone.toUTC(makeTime(setupTime));  // get UTC time from entered time
  1240.   writeTime = { year(t), month(t), day(t), hour(t), minute(t), second(t) };
  1241. #endif
  1242.   Rtc.SetDateTime(writeTime);
  1243.   setTime(writeTime.Hour(), writeTime.Minute(), writeTime.Second(), writeTime.Day(), writeTime.Month(), writeTime.Year());
  1244. #ifdef DEBUG
  1245.   Serial.println(F("setupClock(): RTC time set"));
  1246.   printTime();
  1247. #endif
  1248. #else
  1249. #ifdef AUTODST
  1250.   setupTime.Year += 30;
  1251.   time_t t = myTimeZone.toUTC(makeTime(setupTime));  // get UTC time from entered time
  1252.   setTime(t);
  1253. #else
  1254.   setupTime.Year += 30;
  1255.   setTime(makeTime(setupTime));
  1256. #endif
  1257. #endif
  1258.   clockStatus = 0;
  1259. #ifdef DEBUG
  1260.   Serial.println(F("setupClock() done"));
  1261. #endif
  1262. }
  1263.  
  1264.  
  1265. uint16_t getUserInput(uint8_t sym1, uint8_t sym2, uint8_t startVal, uint8_t endVal) {
  1266.   /* This will show two symbols on HH and allow to enter a 2 digit value using the buttons
  1267.    and display the value on MM.                                                                                        */
  1268.   static uint8_t lastInput = 0;
  1269.   static uint8_t currentVal = startVal;
  1270.   static bool newInput = true;
  1271.   if (newInput) {
  1272.     currentVal = startVal;
  1273.     newInput = false;
  1274.   }
  1275.   while (lastInput != 2) {
  1276.     if (lastInput == 1) {
  1277.       if (currentVal < endVal) {
  1278.         currentVal++;
  1279.       } else {
  1280.         currentVal = startVal;
  1281.       }
  1282.     }
  1283.     FastLED.clear();
  1284.     showDigit(sym1, digitPositions[0]);
  1285.     showDigit(sym2, digitPositions[1]);
  1286.     showDigit(currentVal / 10, digitPositions[2]);
  1287.     showDigit(currentVal % 10, digitPositions[3]);
  1288.     if (millis() % 30 == 0) {
  1289.       colorizeOutput(colorMode);
  1290.       FastLED.show();
  1291.     }
  1292.     lastInput = inputButtons();
  1293.   }
  1294. #ifdef DEBUG
  1295.   Serial.print(F("getUserInput(): returned "));
  1296.   Serial.println(currentVal);
  1297. #endif
  1298.   lastInput = 0;
  1299.   newInput = true;
  1300.   return currentVal;
  1301. #ifdef DEBUG
  1302.   Serial.print(F("getUserInput(): returned "));
  1303.   Serial.println(currentVal);
  1304. #endif
  1305. }
  1306.  
  1307.  
  1308. void colorizeOutput(uint8_t mode) {
  1309.   /* So far showDigit()/showSegment() only set some leds inside the array to values from "markerHSV" but we haven't updated
  1310.    the leds yet using FastLED.show(). This function does the coloring of the right now single colored but "insivible"
  1311.    output. This way color updates/cycles aren't tied to updating display contents                                      */
  1312.   static unsigned long lastColorChange = 0;
  1313.   static uint8_t startColor = 0;
  1314.   static uint8_t colorOffset = 0;  // different offsets result in quite different results, depending on the amount of leds inside each segment...
  1315.                                    // ...so it's set inside each color mode if required
  1316.   /* mode 0 = check every segment if it's lit and assign a color based on position -> different color per digit
  1317.      Checking the leds like this will not include the dots - they'll be colored later on                               */
  1318.   if (mode == 0) {
  1319.     colorOffset = 256 / LED_DIGITS;
  1320.     for (uint8_t pos = 0; pos < LED_DIGITS; pos++) {
  1321.       for (uint8_t segment = 0; segment < 7; segment++) {
  1322.         colorizeSegment(segment, pos, startColor + colorOffset * pos);
  1323.       }
  1324.     }
  1325.   }
  1326.   /* mode 1 = simply assign different colors with an offset of "colorOffset" to each led that's not black/off -
  1327.      This will include the dots if they're supposed to be on - but they will be overwritten later for all modes        */
  1328.   if (mode == 1) {
  1329.     colorOffset = 32;
  1330.     for (uint16_t i = 0; i < LED_COUNT; i++) {
  1331.       if (leds[i]) {
  1332.         leds[i] = ColorFromPalette(currentPalette, startColor + i * colorOffset, brightness, LINEARBLEND);
  1333.       }
  1334.     }
  1335.   }
  1336.   /* mode 2 = check every segment if it's lit and assign a color based on segment -> different color per segment,
  1337.      same across digits. Checking the leds like this will not include the dots - they'll be colored later on           */
  1338.   if (mode == 2) {
  1339.     colorOffset = 24;
  1340.     for (uint8_t pos = 0; pos < LED_DIGITS; pos++) {
  1341.       for (uint8_t segment = 0; segment < 7; segment++) {
  1342.         colorizeSegment(segment, digitPositions[pos], startColor + 1 * colorOffset * segment);
  1343.       }
  1344.     }
  1345.   }
  1346.   /* mode 3 = same as above - but will assign colorOffsets depending on segment in a specific order (top/down effect)  */
  1347.   if (mode == 3) {
  1348.     uint8_t colorOffsets[7] = { 0, 24, 72, 96, 72, 24, 48 };  // colorOffsets for segments a-g
  1349.     for (uint8_t pos = 0; pos < LED_DIGITS; pos++) {
  1350.       for (uint8_t segment = 0; segment < 7; segment++) {
  1351.         colorizeSegment(segment, digitPositions[pos], startColor + 1 * colorOffsets[segment]);
  1352.       }
  1353.     }
  1354.   }
  1355.   /* clockStatus >= 90 is used for coloring output while in setup mode */
  1356.   if (clockStatus >= 90) {
  1357.     static boolean blinkFlag = true;
  1358.     static unsigned long lastBlink = millis();
  1359.     static uint8_t b = brightnessLevels[0];
  1360.     if (millis() - lastBlink > 333) {  // blink switch frequency, 3 times a second
  1361.       if (blinkFlag) {
  1362.         blinkFlag = false;
  1363.         b = brightnessLevels[1];
  1364.       } else {
  1365.         blinkFlag = true;
  1366.         b = brightnessLevels[0];
  1367.       }
  1368.       lastBlink = millis();
  1369.     }  // unset values = red, set value = green, current value = yellow and blinkinkg
  1370.     for (uint8_t pos = 0; pos < LED_DIGITS; pos++) {
  1371.       if (clockStatus == 91) {  // Y/M/D setup
  1372.         colorHelper(digitPositions[0], 0, 255, brightness);
  1373.         colorHelper(digitPositions[1], 0, 255, brightness);
  1374.         colorHelper(digitPositions[2], 64, 255, b);
  1375.         colorHelper(digitPositions[3], 64, 255, b);
  1376.       }
  1377.       if (clockStatus == 92) {  // hours
  1378.         colorHelper(digitPositions[0], 64, 255, b);
  1379.         colorHelper(digitPositions[1], 64, 255, b);
  1380.         colorHelper(digitPositions[2], 0, 255, brightness);
  1381.         colorHelper(digitPositions[3], 0, 255, brightness);
  1382.         if (LED_DIGITS == 6) {
  1383.           colorHelper(digitPositions[4], 0, 255, brightness);
  1384.           colorHelper(digitPositions[5], 0, 255, brightness);
  1385.         }
  1386.       }
  1387.       if (clockStatus == 93) {  // minutes
  1388.         colorHelper(digitPositions[0], 96, 255, brightness);
  1389.         colorHelper(digitPositions[1], 96, 255, brightness);
  1390.         colorHelper(digitPositions[2], 64, 255, b);
  1391.         colorHelper(digitPositions[3], 64, 255, b);
  1392.         if (LED_DIGITS == 6) {
  1393.           colorHelper(digitPositions[4], 0, 255, brightness);
  1394.           colorHelper(digitPositions[5], 0, 255, brightness);
  1395.         }
  1396.       }
  1397.       if (clockStatus == 94) {  // seconds
  1398.         colorHelper(digitPositions[0], 96, 255, brightness);
  1399.         colorHelper(digitPositions[1], 96, 255, brightness);
  1400.         colorHelper(digitPositions[2], 96, 255, brightness);
  1401.         colorHelper(digitPositions[3], 96, 255, brightness);
  1402.         if (LED_DIGITS == 6) {
  1403.           colorHelper(digitPositions[4], 64, 255, b);
  1404.           colorHelper(digitPositions[5], 64, 255, b);
  1405.         }
  1406.       }
  1407.     }
  1408.   }
  1409.   /* The dots will always be colored in the same way, just using colors from the current palette. Depending on setup/parameters
  1410.      this can otherwise lead to the dots looking quite different from the digits, so as before they're cycling through the
  1411.      color palette once per minute */
  1412.   if (leds[pgm_read_word_near(&upperDots[0])]) {                                // if the first led inside the array upperDot is lit...
  1413.     for (uint8_t i = 0; i < (sizeof(upperDots) / sizeof(upperDots[0])); i++) {  // ...start applying colors to all leds inside the array
  1414.       if (clockStatus == 0) {
  1415.         leds[pgm_read_word_near(&upperDots[i])] = ColorFromPalette(currentPalette, second() * 4.25, brightness, LINEARBLEND);
  1416.       } else {
  1417.         leds[pgm_read_word_near(&upperDots[i])].setHSV(64, 255, brightness);
  1418.       }
  1419.     }
  1420.   }
  1421.   if (leds[pgm_read_word_near(&lowerDots[0])]) {  // same as before for the lower dots...
  1422.     for (uint8_t i = (sizeof(lowerDots) / sizeof(lowerDots[0])); i > 0; i--) {
  1423.       if (clockStatus == 0) {
  1424.         leds[pgm_read_word_near(&lowerDots[i - 1])] = ColorFromPalette(currentPalette, second() * 4.25, brightness, LINEARBLEND);
  1425.       } else {
  1426.         leds[pgm_read_word_near(&lowerDots[i - 1])].setHSV(64, 255, brightness);
  1427.       }
  1428.     }
  1429.   }
  1430. #ifdef FASTFORWARD
  1431.   if (millis() - lastColorChange > 15) {
  1432. #else
  1433.   if (millis() - lastColorChange > colorSpeed) {
  1434. #endif
  1435.     if (reverseColorCycling) {
  1436.       startColor--;
  1437.     } else {
  1438.       startColor++;
  1439.     }
  1440.     lastColorChange = millis();
  1441.   }
  1442. #ifdef AUTOBRIGHTNESS
  1443.   if (nightMode && clockStatus == 0) {  // nightmode will overwrite everything that has happened so far...
  1444.     for (uint16_t i = 0; i < LED_COUNT; i++) {
  1445.       if (leds[i]) {
  1446.         if (avgLDR == minBrightness) {
  1447.           leds[i].setHSV(nightColor[0], 255, nightColor[1]);  // and assign nightColor to all lit leds. Default is a very dark red.
  1448.           FastLED.setDither(0);
  1449.         } else {
  1450.           FastLED.setDither(1);
  1451.         }
  1452.       }
  1453.     }
  1454.   }
  1455. #endif
  1456.  
  1457.   /*  // example for time based coloring
  1458.   // for coloring based on current times the following will get local display time into
  1459.   // checkTime if autoDST is defined as the clock is running in utc time then
  1460.   #ifdef AUTODST
  1461.     time_t checkTime = myTimeZone.toLocal(now());
  1462.   #else
  1463.     time_t checkTime = now();
  1464.   #endif
  1465.  
  1466.   // below if-loop simply checks for a given time and colors everything in green/blue accordingly
  1467.   if ( hour(checkTime) > 6 && hour(checkTime) <= 22 ) {           // if hour > 6 AND hour <= 22 ---> 07:00 - 22:59
  1468.     for ( uint16_t i = 0; i < LED_COUNT; i++ ) {                  // for each position...
  1469.       if ( leds[i] ) {                                            // ...check led and if it's lit...
  1470.         leds[i].setHSV(96, 255, brightness);                      // ...redraw with HSV color 96 -> green
  1471.       }
  1472.     }
  1473.   } else {                                                        // ---> 23:00 - 06:59
  1474.     for ( uint16_t i = 0; i < LED_COUNT; i++ ) {                  // for each position...
  1475.       if ( leds[i] ) {                                            // ...check led and if it's lit...
  1476.         leds[i].setHSV(160, 255, brightness);                     // ...redraw with HSV color 160 -> blue
  1477.       }
  1478.     }
  1479.   }
  1480. */
  1481. }
  1482.  
  1483.  
  1484. void colorizeSegment(uint8_t segment, uint8_t pos, uint8_t color) {
  1485.   /* Checks if segment at position is on - and if it is, assigns color from current palette  */
  1486.   uint8_t ledAM = digitsLAM[pos];  // led access mode according to the position
  1487.   if (leds[pgm_read_word_near(&segGroups[segment + digitPositions[pos] * 7][0])]) {
  1488.     if (ledAM == 0) {
  1489.       for (uint8_t i = 0; i < 2; i++) {
  1490.         leds[pgm_read_word_near(&segGroups[segment + digitPositions[pos] * 7][i])] = ColorFromPalette(currentPalette, color, brightness, LINEARBLEND);
  1491.       }
  1492.     }
  1493.     if (ledAM == 1) {
  1494.       uint16_t startLed = pgm_read_word_near(&segGroups[segment + digitPositions[pos] * 7][0]);
  1495.       uint16_t endLed = pgm_read_word_near(&segGroups[segment + digitPositions[pos] * 7][1]);
  1496.       for (uint16_t i = startLed; i <= endLed; i++) {
  1497.         leds[i] = ColorFromPalette(currentPalette, color, brightness, LINEARBLEND);
  1498.       }
  1499.     }
  1500.   }
  1501. }
  1502.  
  1503.  
  1504. void colorHelper(uint8_t pos, uint8_t hue, uint8_t sat, uint8_t bri) {
  1505.   /* Used for coloring digits inside setup routines/steps
  1506.    It will simply set the digit at the given position to the given hsv values */
  1507.   uint8_t ledAM = digitsLAM[pos];  // led access mode according to the position
  1508.   for (uint8_t segment = 0; segment < 7; segment++) {
  1509.     if (leds[pgm_read_word_near(&segGroups[segment + pos * 7][0])]) {  // if first led inside segment is lit...
  1510.       if (ledAM == 0) {
  1511.         for (uint8_t i = 0; i < 2; i++) {  // assign hue to led 0 + 1 inside segment
  1512.           leds[pgm_read_word_near(&segGroups[segment + pos * 7][i])].setHSV(hue, sat, bri);
  1513.         }
  1514.       }
  1515.       if (ledAM == 1) {
  1516.         uint16_t startLed = pgm_read_word_near(&segGroups[segment + pos * 7][0]);
  1517.         uint16_t endLed = pgm_read_word_near(&segGroups[segment + pos * 7][1]);
  1518.         for (uint16_t i = startLed; i <= endLed; i++) {  // assign hue to led 0 - 1 inside segment
  1519.           leds[i].setHSV(hue, sat, bri);
  1520.         }
  1521.       }
  1522.     }
  1523.   }
  1524. }
  1525.  
  1526.  
  1527. void displayTime(time_t t) {
  1528. #ifdef AUTODST
  1529.   if (clockStatus < 90) {       // display adjusted times only while NOT in setup
  1530.     t = myTimeZone.toLocal(t);  // convert display time to local time zone according to rules on top of the sketch
  1531.   }
  1532. #endif
  1533.   if (clockStatus >= 90) {
  1534.     FastLED.clear();
  1535.   }
  1536.   /* hours */
  1537.   if (displayMode == 0) {
  1538.     if (hour(t) < 10) {
  1539.       if (leadingZero) {
  1540.         showDigit(0, digitPositions[0]);
  1541.       }
  1542.     } else {
  1543.       showDigit(hour(t) / 10, digitPositions[0]);
  1544.     }
  1545.     showDigit(hour(t) % 10, digitPositions[1]);
  1546.   } else if (displayMode == 1) {
  1547.     if (hourFormat12(t) < 10) {
  1548.       if (leadingZero) {
  1549.         showDigit(0, digitPositions[0]);
  1550.       }
  1551.     } else {
  1552.       showDigit(hourFormat12(t) / 10, digitPositions[0]);
  1553.     }
  1554.     showDigit(hourFormat12(t) % 10, digitPositions[1]);
  1555.   }
  1556.   /* minutes */
  1557.   showDigit(minute(t) / 10, digitPositions[2]);
  1558.   showDigit(minute(t) % 10, digitPositions[3]);
  1559.   if (LED_DIGITS == 6) {
  1560.     /* seconds */
  1561.     showDigit(second(t) / 10, digitPositions[4]);
  1562.     showDigit(second(t) % 10, digitPositions[5]);
  1563.   }
  1564.   if (clockStatus >= 90) {  // in setup modes displayTime will also use colorizeOutput/FastLED.show!
  1565.     static unsigned long lastRefresh = millis();
  1566.     if (isAM(t) && displayMode == 1) {  // in 12h mode and if it's AM only light up the upper dots (while setting time)
  1567.       showDots(1);
  1568.     } else {
  1569.       showDots(2);
  1570.     }
  1571.     if (millis() - lastRefresh >= 25) {
  1572.       colorizeOutput(colorMode);
  1573.       FastLED.show();
  1574.       lastRefresh = millis();
  1575.     }
  1576.     return;
  1577.   }
  1578.   /* dots */
  1579.   if (dotsBlinking) {
  1580.     if (second(t) % 2 == 0) {
  1581.       showDots(2);
  1582.     }
  1583.   } else {
  1584.     showDots(2);
  1585.   }
  1586. }
  1587.  
  1588.  
  1589. void showSegment(uint8_t segment, uint8_t segDisplay) {
  1590.   // This shows the segments from top of the sketch on a given position (segDisplay). Order of positions/segDisplay is the order
  1591.   // of definitions on the top, first one defined is segDisplay 0, second one is segDisplay 1 and so on...
  1592.   // "firstLoop" is used to display all information only once per test if customHelper is defined
  1593.   uint8_t ledAM = digitsLAM[segDisplay];  // led access mode according to the position
  1594. #ifdef DEBUG
  1595. #ifdef CUSTOMHELPER
  1596.   if (firstLoop) {
  1597.     Serial.print(F("LED_ACCESS_MODE for segment "));
  1598.     Serial.print(segment);
  1599.     Serial.print(F(" at position "));
  1600.     Serial.print(segDisplay);
  1601.     Serial.print(F(" is "));
  1602.     Serial.print(ledAM);
  1603.   }
  1604. #endif
  1605. #endif
  1606.   if (ledAM == 0) {  // using both values inside the array to light up two leds
  1607. #ifdef DEBUG
  1608. #ifdef CUSTOMHELPER
  1609.     if (firstLoop) {
  1610.       Serial.print(F(". Leds "));
  1611.     }
  1612. #endif
  1613. #endif
  1614.     segment += segDisplay * 7;
  1615.     for (uint8_t i = 0; i < 2; i++) {
  1616.       leds[pgm_read_word_near(&segGroups[segment][i])].setHSV(markerHSV[0], markerHSV[1], markerHSV[2]);
  1617. #ifdef DEBUG
  1618. #ifdef CUSTOMHELPER
  1619.       if (firstLoop) {
  1620.         if (i == 0) {
  1621.           Serial.print(pgm_read_word_near(&segGroups[segment][i]));
  1622.           Serial.print(F(" and "));
  1623.         }
  1624.         if (i == 1) {
  1625.           Serial.println(pgm_read_word_near(&segGroups[segment][i]));
  1626.         }
  1627.       }
  1628. #endif
  1629. #endif
  1630.     }
  1631.   }
  1632.   if (ledAM == 1) {  // using both values inside the array as start and end to light up multiple leds
  1633.     segment += segDisplay * 7;
  1634.     uint16_t startLed = pgm_read_word_near(&segGroups[segment][0]);
  1635.     uint16_t endLed = pgm_read_word_near(&segGroups[segment][1]);
  1636. #ifdef DEBUG
  1637. #ifdef CUSTOMHELPER
  1638.     if (firstLoop) {
  1639.       Serial.print(F(". Leds "));
  1640.       Serial.print(startLed);
  1641.       Serial.print(F(" - "));
  1642.       Serial.println(endLed);
  1643.     }
  1644. #endif
  1645. #endif
  1646.     for (uint16_t i = startLed; i <= endLed; i++) {
  1647.       leds[i].setHSV(markerHSV[0], markerHSV[1], markerHSV[2]);
  1648.     }
  1649.   }
  1650. }
  1651.  
  1652.  
  1653. void showDots(uint8_t dots) {
  1654.   // dots 0 = upper dots, dots 1 = lower dots, dots 2 = all dots (right/left/both on Lazy 7 - Quick Build Edition)
  1655.   if (dots == 1 || dots == 2) {
  1656.     for (uint8_t i = 0; i < (sizeof(upperDots) / sizeof(upperDots[0])); i++) {
  1657.       leds[pgm_read_word_near(&upperDots[i])].setHSV(markerHSV[0], markerHSV[1], markerHSV[2]);
  1658.     }
  1659.   }
  1660.   if (dots == 0 || dots == 2) {
  1661.     for (uint8_t i = 0; i < (sizeof(lowerDots) / sizeof(lowerDots[0])); i++) {
  1662.       leds[pgm_read_word_near(&lowerDots[i])].setHSV(markerHSV[0], markerHSV[1], markerHSV[2]);
  1663.     }
  1664.   }
  1665. }
  1666.  
  1667.  
  1668. void showDigit(uint8_t digit, uint8_t pos) {
  1669.   // This draws numbers using the according segments as defined on top of the sketch (0 - 9) or symbols/characters (index 10+)
  1670.   for (uint8_t i = 0; i < 7; i++) {
  1671.     if (pgm_read_byte_near(&digits[digit][i]) != 0) showSegment(i, pos);
  1672.   }
  1673. }
  1674.  
  1675.  
  1676. void paletteSwitcher() {
  1677.   /* As the name suggests this takes care of switching palettes. When adding palettes, make sure paletteCount increases
  1678.   accordingly.  A few examples of gradients/solid colors by using RGB values or HTML Color Codes  below               */
  1679.   static uint8_t paletteCount = 6;
  1680.   static uint8_t currentIndex = 0;
  1681.   if (clockStatus == 1) {  // Clock is starting up, so load selected palette from eeprom...
  1682.     uint8_t tmp = EEPROM.read(0);
  1683.     if (tmp >= 0 && tmp < paletteCount) {
  1684.       currentIndex = tmp;  // 255 from eeprom would mean there's nothing been written yet, so checking range...
  1685.     } else {
  1686.       currentIndex = 0;  // ...and default to 0 if returned value from eeprom is not 0 - 6
  1687.     }
  1688. #ifdef DEBUG
  1689.     Serial.print(F("paletteSwitcher(): loaded EEPROM value "));
  1690.     Serial.println(tmp);
  1691. #endif
  1692.   }
  1693.   switch (currentIndex) {
  1694.     case 0:
  1695.       currentPalette = CRGBPalette16(CRGB(224, 0, 32),
  1696.                                      CRGB(0, 0, 244),
  1697.                                      CRGB(128, 0, 128),
  1698.                                      CRGB(224, 0, 64));
  1699.       break;
  1700.     case 1:
  1701.       currentPalette = CRGBPalette16(CRGB(224, 16, 0),
  1702.                                      CRGB(192, 64, 0),
  1703.                                      CRGB(192, 128, 0),
  1704.                                      CRGB(240, 40, 0));
  1705.       break;
  1706.     case 2:
  1707.       currentPalette = CRGBPalette16(CRGB::Aquamarine,
  1708.                                      CRGB::Turquoise,
  1709.                                      CRGB::Blue,
  1710.                                      CRGB::DeepSkyBlue);
  1711.       break;
  1712.     case 3: currentPalette = RainbowColors_p; break;
  1713.     case 4: currentPalette = PartyColors_p; break;
  1714.     case 5: currentPalette = CRGBPalette16(CRGB::LawnGreen); break;
  1715.   }
  1716. #ifdef DEBUG
  1717.   Serial.print(F("paletteSwitcher(): selected palette "));
  1718.   Serial.println(currentIndex);
  1719. #endif
  1720.   if (clockStatus == 0) {  // only save selected palette to eeprom if clock is in normal running mode, not while in startup/setup/whatever
  1721.     EEPROM.put(0, currentIndex);
  1722. #ifdef NODEMCU
  1723.     EEPROM.commit();
  1724. #endif
  1725. #ifdef DEBUG
  1726.     Serial.print(F("paletteSwitcher(): saved index "));
  1727.     Serial.print(currentIndex);
  1728.     Serial.println(F(" to eeprom"));
  1729. #endif
  1730.   }
  1731.   if (currentIndex < paletteCount - 1) {
  1732.     currentIndex++;
  1733.   } else {
  1734.     currentIndex = 0;
  1735.   }
  1736.   if (colorPreview) {
  1737.     previewMode();
  1738.   }
  1739. #ifdef DEBUG
  1740.   Serial.println(F("paletteSwitcher() done"));
  1741. #endif
  1742. }
  1743.  
  1744.  
  1745. void brightnessSwitcher() {
  1746.   static uint8_t currentIndex = 0;
  1747.   if (clockStatus == 1) {  // Clock is starting up, so load selected palette from eeprom...
  1748.     uint8_t tmp = EEPROM.read(1);
  1749.     if (tmp >= 0 && tmp < 3) {
  1750.       currentIndex = tmp;  // 255 from eeprom would mean there's nothing been written yet, so checking range...
  1751.     } else {
  1752.       currentIndex = 0;  // ...and default to 0 if returned value from eeprom is not 0 - 2
  1753.     }
  1754. #ifdef DEBUG
  1755.     Serial.print(F("brightnessSwitcher(): loaded EEPROM value "));
  1756.     Serial.println(tmp);
  1757. #endif
  1758.   }
  1759.   switch (currentIndex) {
  1760.     case 0: brightness = brightnessLevels[currentIndex]; break;
  1761.     case 1: brightness = brightnessLevels[currentIndex]; break;
  1762.     case 2: brightness = brightnessLevels[currentIndex]; break;
  1763.   }
  1764. #ifdef DEBUG
  1765.   Serial.print(F("brightnessSwitcher(): selected brightness index "));
  1766.   Serial.println(currentIndex);
  1767. #endif
  1768.   if (clockStatus == 0) {  // only save selected brightness to eeprom if clock is in normal running mode, not while in startup/setup/whatever
  1769.     EEPROM.put(1, currentIndex);
  1770. #ifdef NODEMCU
  1771.     EEPROM.commit();
  1772. #endif
  1773. #ifdef DEBUG
  1774.     Serial.print(F("brightnessSwitcher(): saved index "));
  1775.     Serial.print(currentIndex);
  1776.     Serial.println(F(" to eeprom"));
  1777. #endif
  1778.   }
  1779.   if (currentIndex < 2) {
  1780.     currentIndex++;
  1781.   } else {
  1782.     currentIndex = 0;
  1783.   }
  1784. #ifdef DEBUG
  1785.   Serial.println(F("brightnessSwitcher() done"));
  1786. #endif
  1787. }
  1788.  
  1789.  
  1790. void colorModeSwitcher() {
  1791.   static uint8_t currentIndex = 0;
  1792.   if (clockStatus == 1) {        // Clock is starting up, so load selected palette from eeprom...
  1793.     if (colorMode != 0) return;  // 0 is default, if it's different on startup the config is set differently, so exit here
  1794.     uint8_t tmp = EEPROM.read(3);
  1795.     if (tmp >= 0 && tmp < 4) {  // make sure tmp < 3 is increased if color modes are added in colorizeOutput()!
  1796.       currentIndex = tmp;       // 255 from eeprom would mean there's nothing been written yet, so checking range...
  1797.     } else {
  1798.       currentIndex = 0;  // ...and default to 0 if returned value from eeprom is not 0 - 2
  1799.     }
  1800. #ifdef DEBUG
  1801.     Serial.print(F("colorModeSwitcher(): loaded EEPROM value "));
  1802.     Serial.println(tmp);
  1803. #endif
  1804.   }
  1805.   colorMode = currentIndex;
  1806. #ifdef DEBUG
  1807.   Serial.print(F("colorModeSwitcher(): selected colorMode "));
  1808.   Serial.println(currentIndex);
  1809. #endif
  1810.   if (clockStatus == 0) {  // only save selected colorMode to eeprom if clock is in normal running mode, not while in startup/setup/whatever
  1811.     EEPROM.put(3, currentIndex);
  1812. #ifdef NODEMCU
  1813.     EEPROM.commit();
  1814. #endif
  1815. #ifdef DEBUG
  1816.     Serial.print(F("colorModeSwitcher(): saved index "));
  1817.     Serial.print(currentIndex);
  1818.     Serial.println(F(" to eeprom"));
  1819. #endif
  1820.   }
  1821.   if (currentIndex < 3) {
  1822.     currentIndex++;
  1823.   } else {
  1824.     currentIndex = 0;
  1825.   }
  1826.   if (colorPreview) {
  1827.     previewMode();
  1828.   }
  1829. #ifdef DEBUG
  1830.   Serial.println(F("colorModeSwitcher() done"));
  1831. #endif
  1832. }
  1833.  
  1834.  
  1835. void displayModeSwitcher() {
  1836.   static uint8_t currentIndex = 0;
  1837.   if (clockStatus == 1) {          // Clock is starting up, so load selected palette from eeprom...
  1838.     if (displayMode != 0) return;  // 0 is default, if it's different on startup the config is set differently, so exit here
  1839.     uint8_t tmp = EEPROM.read(2);
  1840.     if (tmp >= 0 && tmp < 2) {  // make sure tmp < 2 is increased if display modes are added
  1841.       currentIndex = tmp;       // 255 from eeprom would mean there's nothing been written yet, so checking range...
  1842.     } else {
  1843.       currentIndex = 0;  // ...and default to 0 if returned value from eeprom is not 0 - 1 (24h/12h mode)
  1844.     }
  1845. #ifdef DEBUG
  1846.     Serial.print(F("displayModeSwitcher(): loaded EEPROM value "));
  1847.     Serial.println(tmp);
  1848. #endif
  1849.   }
  1850.   displayMode = currentIndex;
  1851. #ifdef DEBUG
  1852.   Serial.print(F("displayModeSwitcher(): selected displayMode "));
  1853.   Serial.println(currentIndex);
  1854. #endif
  1855.   if (clockStatus == 0) {  // only save selected colorMode to eeprom if clock is in normal running mode, not while in startup/setup/whatever
  1856.     EEPROM.put(2, currentIndex);
  1857. #ifdef NODEMCU
  1858.     EEPROM.commit();
  1859. #endif
  1860. #ifdef DEBUG
  1861.     Serial.print(F("displayModeSwitcher(): saved index "));
  1862.     Serial.print(currentIndex);
  1863.     Serial.println(F(" to eeprom"));
  1864. #endif
  1865.   }
  1866.   if (clockStatus == 0) {  // show 12h/24h for 2 seconds after selected in normal run mode, don't show this on startup (status 1)
  1867.     FastLED.clear();
  1868.     unsigned long timer = millis();
  1869.     while (millis() - timer <= 2000) {
  1870.       if (currentIndex == 0) {
  1871.         showDigit(2, digitPositions[0]);
  1872.         showDigit(4, digitPositions[1]);
  1873.         showDigit(19, digitPositions[3]);
  1874.       }
  1875.       if (currentIndex == 1) {
  1876.         showDigit(1, digitPositions[0]);
  1877.         showDigit(2, digitPositions[1]);
  1878.         showDigit(19, digitPositions[3]);
  1879.       }
  1880.       colorizeOutput(colorMode);
  1881.       if (millis() % 50 == 0) {
  1882.         FastLED.show();
  1883.       }
  1884. #ifdef NODEMCU
  1885.       yield();
  1886. #endif
  1887.     }
  1888.   }
  1889.   if (currentIndex < 1) {
  1890.     currentIndex++;
  1891.   } else {
  1892.     currentIndex = 0;
  1893.   }
  1894. #ifdef DEBUG
  1895.   Serial.println(F("displayModeSwitcher() done"));
  1896. #endif
  1897. }
  1898.  
  1899.  
  1900. void previewMode() {
  1901.   /*  This will simply display "8" on all positions, speed up the color cyling and preview the
  1902.     selected palette or colorMode                                                            */
  1903.   if (clockStatus == 1) return;  // don't preview when starting up
  1904.   unsigned long previewStart = millis();
  1905.   uint16_t colorSpeedBak = colorSpeed;
  1906.   colorSpeed = 5;
  1907.   while (millis() - previewStart <= uint16_t(colorPreviewDuration * 1000L)) {
  1908.     for (uint8_t i = 0; i < LED_DIGITS; i++) {
  1909.       showDigit(8, i);
  1910.     }
  1911.     colorizeOutput(colorMode);
  1912.     FastLED.show();
  1913. #ifdef NODEMCU
  1914.     yield();
  1915. #endif
  1916.   }
  1917.   colorSpeed = colorSpeedBak;
  1918.   FastLED.clear();
  1919. }
  1920. #endif /* LEDSTUFF */
  1921.  
  1922.  
  1923. bool leapYear(uint16_t y) {
  1924.   boolean isLeapYear = false;
  1925.   if (y % 4 == 0) isLeapYear = true;
  1926.   if (y % 100 == 0 && y % 400 != 0) isLeapYear = false;
  1927.   if (y % 400 == 0) isLeapYear = true;
  1928.   if (isLeapYear) return true;
  1929.   else return false;
  1930. }
  1931.  
  1932.  
  1933. uint8_t inputButtons() {
  1934.   /* This scans for button presses and keeps track of delay/repeat for user inputs
  1935.      Short keypresses will only be returned when buttons are released before repeatDelay
  1936.      is reached. This is to avoid constantly sending 1 or 2 when executing a long button
  1937.      press and/or multiple buttons.
  1938.      Note: Buttons are using pinMode INPUT_PULLUP, so HIGH = not pressed, LOW = pressed! */
  1939.   static uint8_t scanInterval = 30;                     // only check buttons every 30ms
  1940.   static uint16_t repeatDelay = 1000;                   // delay in milliseconds before repeating detected keypresses
  1941.   static uint8_t repeatRate = 1000 / 10;                // 10 chars per 1000 milliseconds
  1942.   static uint8_t minTime = scanInterval * 2;            // minimum time to register a button as pressed
  1943.   static unsigned long lastReadout = millis();          // keeps track of when the last readout happened
  1944.   static unsigned long lastReturn = millis();           // keeps track of when the last readout value was returned
  1945.   static uint8_t lastState = 0;                         // button state from previous scan
  1946.   uint8_t currentState = 0;                             // button state from current scan
  1947.   uint8_t retVal = 0;                                   // return value, will be 0 if no button is pressed
  1948.   static unsigned long eventStart = millis();           // keep track of when button states are changing
  1949.   if (millis() - lastReadout < scanInterval) return 0;  // only scan for button presses every <scanInterval> ms
  1950.   if (digitalRead(buttonA) == LOW) currentState += 1;
  1951.   if (digitalRead(buttonB) == LOW) currentState += 2;
  1952.   if (currentState == 0 && currentState == lastState) {
  1953.     btnRepeatCounter = 0;
  1954.   }
  1955.   if (currentState != 0 && currentState != lastState) {  // if any button is pressed and different from the previous scan...
  1956.     eventStart = millis();                               // ...reset eventStart to current time
  1957.     btnRepeatCounter = 0;                                // ...and reset global variable btnRepeatCounter
  1958.   }
  1959.   if (currentState != 0 && currentState == lastState) {  // if same input has been detected at least twice (2x scanInterval)...
  1960.     if (millis() - eventStart >= repeatDelay) {          // ...and longer than repeatDelay...
  1961.       if (millis() - lastReturn >= repeatRate) {         // ...check for repeatRate...
  1962.         retVal = currentState;                           // ...and set retVal to currentState
  1963.         btnRepeatCounter++;
  1964.         lastReturn = millis();
  1965.       } else retVal = 0;  // return 0 if repeatDelay hasn't been reached yet
  1966.     }
  1967.   }
  1968.   if (currentState == 0 && currentState != lastState && millis() - eventStart >= minTime && btnRepeatCounter == 0) {
  1969.     retVal = lastState;  // return lastState if all buttons are released after having been pressed for <minTime> ms
  1970.     btnRepeatCounter = 0;
  1971.   }
  1972.   lastState = currentState;
  1973.   lastReadout = millis();
  1974. #ifdef DEBUG  // output some information and read serial input, if available
  1975.   uint8_t serialInput = dbgInput();
  1976.   if (serialInput != 0) {
  1977.     Serial.print(F("inputButtons(): Serial input detected: "));
  1978.     Serial.println(serialInput);
  1979.     retVal = serialInput;
  1980.   }
  1981.   if (retVal != 0) {
  1982.     Serial.print(F("inputButtons(): Return value is: "));
  1983.     Serial.print(retVal);
  1984.     Serial.print(F(" - btnRepeatCounter is: "));
  1985.     Serial.println(btnRepeatCounter);
  1986.   }
  1987. #endif
  1988.   return retVal;
  1989. }
  1990.  
  1991.  
  1992. // following will only be included if USENTP is defined
  1993. #ifdef USENTP
  1994. /* This syncs system time to the RTC at startup and will periodically do other sync related
  1995.    things, like syncing rtc to ntp time */
  1996. void syncHelper() {
  1997.   static unsigned long lastSync = millis();                     // keeps track of the last time a sync attempt has been made
  1998.   if (millis() - lastSync < 60000 && clockStatus != 1) return;  // only allow one ntp request per minute
  1999.   if (WiFi.status() != WL_CONNECTED) {
  2000. #ifdef DEBUG
  2001.     Serial.println(F("syncHelper(): No WiFi connection"));
  2002.     return;
  2003. #endif
  2004.   }
  2005. #ifndef USERTC
  2006. #ifndef USENTP
  2007. #ifdef DEBUG
  2008.   Serial.println(F("syncHelper(): No RTC and no NTP configured, nothing to do..."));
  2009.   return;
  2010. #endif
  2011. #endif
  2012. #endif
  2013.   time_t ntpTime = 0;
  2014. #ifdef USERTC
  2015.   RtcDateTime ntpTimeConverted;
  2016. #endif
  2017.   if (clockStatus == 1) {  // looks like the sketch has just started running...
  2018. #ifdef DEBUG
  2019.     Serial.println(F("syncHelper(): Initial sync on power up..."));
  2020. #endif
  2021.     ntpTime = getTimeNTP();
  2022. #ifdef DEBUG
  2023.     Serial.print(F("syncHelper(): NTP result is "));
  2024.     Serial.println(ntpTime);
  2025. #endif
  2026.     lastSync = millis();
  2027.   } else {
  2028. #ifdef DEBUG
  2029.     Serial.println(F("syncHelper(): Resyncing to NTP..."));
  2030. #endif
  2031.     ntpTime = getTimeNTP();
  2032. #ifdef DEBUG
  2033.     Serial.print(F("syncHelper(): NTP result is "));
  2034.     Serial.println(ntpTime);
  2035. #endif
  2036.     lastSync = millis();
  2037.   }
  2038. #ifdef USERTC
  2039.   ntpTimeConverted = { year(ntpTime), month(ntpTime), day(ntpTime),
  2040.                        hour(ntpTime), minute(ntpTime), second(ntpTime) };
  2041.   RtcDateTime rtcTime = Rtc.GetDateTime();  // get current time from the rtc....
  2042.   if (ntpTime > 100) {
  2043.     Rtc.SetDateTime(ntpTimeConverted);
  2044.   }
  2045. #else
  2046.   time_t sysTime = now();  // ...or from system
  2047. #ifdef DEBUG
  2048.   Serial.println(F("syncHelper(): No RTC configured, using system time"));
  2049.   Serial.print(F("syncHelper(): sysTime was "));
  2050.   Serial.println(now());
  2051. #endif
  2052.   if (ntpTime > 100) {
  2053.     setTime(ntpTime);
  2054.   }
  2055. #endif
  2056. #ifdef DEBUG
  2057.   Serial.println(F("syncHelper() done"));
  2058. #endif
  2059. }
  2060.  
  2061.  
  2062. time_t getTimeNTP() {
  2063.   unsigned long startTime = millis();
  2064.   time_t timeNTP;
  2065.   if (WiFi.status() != WL_CONNECTED) {
  2066. #ifdef DEBUG
  2067.     Serial.print(F("getTimeNTP(): Not connected, WiFi.status is "));
  2068.     Serial.println(WiFi.status());
  2069. #endif
  2070.   }                                      // Sometimes the connection doesn't work right away although status is WL_CONNECTED...
  2071.   while (millis() - startTime < 2000) {  // ...so we'll wait a moment before causing network traffic
  2072. #ifdef NODEMCU
  2073.     yield();
  2074. #endif
  2075.   }
  2076.   timeClient.update();
  2077.   timeNTP = timeClient.getEpochTime();
  2078.   if (timeNTP < 100) {
  2079. #ifdef DEBUG
  2080.     Serial.print(F("getTimeNTP(): NTP returned "));
  2081.     Serial.println(timeNTP);
  2082.     Serial.print(F(" - trying again..."));
  2083. #endif
  2084.   }
  2085.   timeClient.update();
  2086.   timeNTP = timeClient.getEpochTime();
  2087.   if (timeNTP < 100) {
  2088. #ifdef DEBUG
  2089.     Serial.print(F("getTimeNTP(): NTP returned "));
  2090.     Serial.println(timeNTP);
  2091.     Serial.print(F(" - giving up"));
  2092. #endif
  2093.   }
  2094. #ifdef DEBUG
  2095.   Serial.println(F("getTimeNTP() done"));
  2096. #endif
  2097.   return timeNTP;
  2098. }
  2099. #endif
  2100. // ---
  2101.  
  2102.  
  2103. // functions below will only be included if DEBUG is defined on top of the sketch
  2104. #ifdef DEBUG
  2105. void printTime() {
  2106.   /* outputs current system and RTC time to the serial monitor, adds autoDST if defined */
  2107.   time_t tmp = now();
  2108. #ifdef USERTC
  2109.   RtcDateTime tmp2 = Rtc.GetDateTime();
  2110.   setTime(tmp2.Hour(), tmp2.Minute(), tmp2.Second(),
  2111.           tmp2.Day(), tmp2.Month(), tmp2.Year());
  2112.   tmp = now();
  2113. #endif
  2114.   Serial.println(F("-----------------------------------"));
  2115.   Serial.print(F("System time is : "));
  2116.   if (hour(tmp) < 10) Serial.print(F("0"));
  2117.   Serial.print(hour(tmp));
  2118.   Serial.print(F(":"));
  2119.   if (minute(tmp) < 10) Serial.print(F("0"));
  2120.   Serial.print(minute(tmp));
  2121.   Serial.print(F(":"));
  2122.   if (second(tmp) < 10) Serial.print(F("0"));
  2123.   Serial.println(second(tmp));
  2124.   Serial.print(F("System date is : "));
  2125.   Serial.print(year(tmp));
  2126.   Serial.print("-");
  2127.   Serial.print(month(tmp));
  2128.   Serial.print("-");
  2129.   Serial.print(day(tmp));
  2130.   Serial.println(F(" (Y/M/D)"));
  2131. #ifdef USERTC
  2132.   Serial.print(F("RTC time is    : "));
  2133.   if (tmp2.Hour() < 10) Serial.print(F("0"));
  2134.   Serial.print(tmp2.Hour());
  2135.   Serial.print(F(":"));
  2136.   if (tmp2.Minute() < 10) Serial.print(F("0"));
  2137.   Serial.print(tmp2.Minute());
  2138.   Serial.print(F(":"));
  2139.   if (tmp2.Second() < 10) Serial.print(F("0"));
  2140.   Serial.println(tmp2.Second());
  2141.   Serial.print(F("RTC date is    : "));
  2142.   Serial.print(tmp2.Year());
  2143.   Serial.print("-");
  2144.   Serial.print(tmp2.Month());
  2145.   Serial.print("-");
  2146.   Serial.print(tmp2.Day());
  2147.   Serial.println(F(" (Y/M/D)"));
  2148. #endif
  2149. #ifdef AUTODST
  2150.   tmp = myTimeZone.toLocal(tmp);
  2151.   Serial.print(F("autoDST time is: "));
  2152.   if (hour(tmp) < 10) Serial.print(F("0"));
  2153.   Serial.print(hour(tmp));
  2154.   Serial.print(F(":"));
  2155.   if (minute(tmp) < 10) Serial.print(F("0"));
  2156.   Serial.print(minute(tmp));
  2157.   Serial.print(F(":"));
  2158.   if (second(tmp) < 10) Serial.print(F("0"));
  2159.   Serial.println(second(tmp));
  2160.   Serial.print(F("autoDST date is: "));
  2161.   Serial.print(year(tmp));
  2162.   Serial.print("-");
  2163.   Serial.print(month(tmp));
  2164.   Serial.print("-");
  2165.   Serial.print(day(tmp));
  2166.   Serial.println(F(" (Y/M/D)"));
  2167. #endif
  2168.   Serial.println(F("-----------------------------------"));
  2169. }
  2170.  
  2171.  
  2172. uint8_t dbgInput() {
  2173.   /* this catches input from the serial console and hands it over to inputButtons() if DEBUG is defined
  2174.        Serial input "7" matches buttonA, "8" matches buttonB, "9" matches buttonA + buttonB */
  2175.   if (Serial.available() > 0) {
  2176.     uint8_t incomingByte = 0;
  2177.     incomingByte = Serial.read();
  2178.     if (incomingByte == 52) {  // 4 - long press buttonA
  2179.       btnRepeatCounter = 10;
  2180.       return 1;
  2181.     }
  2182.     if (incomingByte == 53) {  // 5 - long press buttonB
  2183.       btnRepeatCounter = 10;
  2184.       return 2;
  2185.     }
  2186.     if (incomingByte == 54) {  // 6 - long press buttonA + buttonB
  2187.       btnRepeatCounter = 10;
  2188.       return 3;
  2189.     }
  2190.     if (incomingByte == 55) return 1;  // 7 - buttonA
  2191.     if (incomingByte == 56) return 2;  // 8 - buttonB
  2192.     if (incomingByte == 57) return 3;  // 9 - buttonA + buttonB
  2193.   }
  2194.   return 0;
  2195. }
  2196. #endif
  2197. // ---
  2198.  
  2199.  
  2200. #ifdef USEWIFI
  2201. void connectWPS() {  // join network using wps. Will try for 3 times before exiting...
  2202. #ifdef DEBUG
  2203.   Serial.println(F("connectWPS(): Initializing WPS setup..."));
  2204. #endif
  2205.   uint8_t counter = 1;
  2206.   static unsigned long startTimer = millis();
  2207. #ifdef LEDSTUFF
  2208.   FastLED.clear();
  2209.   showDigit(10, digitPositions[0]);
  2210.   showDigit(11, digitPositions[1]);
  2211.   showDigit(12, digitPositions[2]);
  2212.   showDigit(counter, digitPositions[3]);
  2213.   colorizeOutput(colorMode);
  2214.   FastLED.show();
  2215. #endif
  2216.   while (counter < 4) {
  2217. #ifdef LEDSTUFF
  2218.     if (millis() % 50 == 0) {
  2219.       FastLED.clear();
  2220.       showDigit(10, digitPositions[0]);
  2221.       showDigit(11, digitPositions[1]);
  2222.       showDigit(12, digitPositions[2]);
  2223.       showDigit(counter, digitPositions[3]);
  2224.       colorizeOutput(colorMode);
  2225.       FastLED.show();
  2226.     }
  2227. #endif
  2228.     if (millis() - startTimer > 300) {
  2229. #ifdef DEBUG
  2230.       Serial.print(F("connectWPS(): Waiting for WiFi/WPS, try "));
  2231.       Serial.println(counter);
  2232. #endif
  2233.       WiFi.beginWPSConfig();
  2234.       if (WiFi.SSID().length() <= 0) counter++;
  2235.       else counter = 4;
  2236.       startTimer = millis();
  2237.     }
  2238. #ifdef NODEMCU
  2239.     yield();
  2240. #endif
  2241.   }
  2242.   FastLED.clear();
  2243.   startTimer = millis();
  2244.   if (WiFi.SSID().length() > 0) {
  2245. #ifdef LEDSTUFF
  2246.     FastLED.clear();
  2247.     showDigit(5, digitPositions[0]);
  2248.     showDigit(5, digitPositions[1]);
  2249.     showDigit(1, digitPositions[2]);
  2250.     showDigit(13, digitPositions[3]);
  2251.     colorizeOutput(colorMode);
  2252.     FastLED.show();
  2253. #endif
  2254. #ifdef DEBUG
  2255.     Serial.print(F("connectWPS(): Connected to SSID: "));
  2256.     Serial.println(WiFi.SSID());
  2257. #endif
  2258.     while (millis() - startTimer < 2000) {
  2259. #ifdef NODEMCU
  2260.       yield();
  2261. #endif
  2262.     }
  2263. #ifdef USENTP
  2264.     clockStatus = 1;
  2265.     syncHelper();
  2266.     clockStatus = 0;
  2267. #endif USENTP
  2268.   } else {
  2269. #ifdef DEBUG
  2270.     Serial.println(F("connectWPS(): Failed, no WPS connection established"));
  2271. #endif
  2272.   }
  2273. #ifdef DEBUG
  2274.   Serial.println(F("connectWPS() done"));
  2275. #endif
  2276. }
  2277. #endif
  2278.  
  2279.  
  2280. #ifdef CUSTOMHELPER
  2281. /* This assists in troubleshooting and basic configuration. Testing all neccessary steps to get
  2282.   showSegment(), showDigit(), showDots() to work. Ugly and using delay() but does the job ^^         */
  2283. void customHelper() {
  2284.   markerHSV[0] = 96;
  2285.   markerHSV[1] = 255;
  2286.   markerHSV[2] = 60;
  2287.   colorModeSwitcher();
  2288.   paletteSwitcher();
  2289.   brightness = 50;
  2290.   currentPalette = RainbowColors_p;
  2291.   uint8_t test = 1;
  2292. #ifdef DEBUG
  2293.   Serial.println(F("\n\n\nSome kind of troubleshooting/custom assistant... ^^"));
  2294.   Serial.println(F("\nTests will finish before proceeding to the next one.\n"));
  2295.   Serial.print(F("The first step is to check all leds, so this test will\nsimply light up all the leds from 0 to "));
  2296.   Serial.println(LED_COUNT - 1);
  2297.   Serial.println(F("Press button A (or send 7 using serial input) to advance to the next step...\n"));
  2298. #endif
  2299.   while (test == 1) {
  2300.     for (uint16_t i = 0; i < LED_COUNT; i++) {
  2301.       leds[i].setHSV(markerHSV[0], markerHSV[1], markerHSV[2]);
  2302.       FastLED.show();
  2303.       delay(75);
  2304.       if (inputButtons() != 0) test++;
  2305.     }
  2306.     FastLED.clear();
  2307.     delay(300);
  2308.   }
  2309. #ifdef DEBUG
  2310.   Serial.println(F("\n\n\nNext we will light up segments 0-6 (a-g) at position 0, this"));
  2311.   Serial.println(F("will show if all of the leds inside segArray[][] for position 0 are correct."));
  2312.   Serial.println(F("Press button A (or send 7 using serial input) to advance to the next step...\n"));
  2313. #endif
  2314.   FastLED.clear();
  2315.   while (test == 2) {
  2316.     for (uint8_t i = 0; i < 7; i++) {
  2317.       showSegment(i, 0);
  2318.       FastLED.show();
  2319.       delay(750);
  2320.       if (inputButtons() != 0) test++;
  2321.     }
  2322.     FastLED.clear();
  2323.     FastLED.show();
  2324.     delay(300);
  2325.     firstLoop = false;
  2326.   }
  2327. #ifdef DEBUG
  2328.   Serial.println(F("\n\nNow let's check this for all the positions as defined (LED_DIGITS 4 or 6), starting from 0..."));
  2329.   Serial.println(F("Press button A (or send 7 using serial input) to advance to the next step...\n"));
  2330. #endif
  2331.   firstLoop = true;
  2332.   while (test == 3) {
  2333.     for (uint8_t pos = 0; pos < LED_DIGITS; pos++) {
  2334.       for (uint8_t i = 0; i < 7; i++) {
  2335.         showSegment(i, pos);
  2336.         FastLED.show();
  2337.         delay(400);
  2338.         if (inputButtons() != 0) test++;
  2339.       }
  2340.       if (firstLoop) Serial.println();
  2341.     }
  2342.     FastLED.clear();
  2343.     FastLED.show();
  2344.     delay(300);
  2345.     firstLoop = false;
  2346.   }
  2347. #ifdef DEBUG
  2348.   Serial.println(F("\n\nTesting showDigit() on position 0, displaying 0-9"));
  2349.   Serial.println(F("Press button A (or send 7 using serial input) to advance to the next step...\n"));
  2350. #endif
  2351.   while (test == 4) {
  2352.     for (uint8_t i = 0; i < 10; i++) {
  2353.       FastLED.clear();
  2354.       showDigit(i, 0);
  2355.       if (inputButtons() != 0) test++;
  2356.       FastLED.show();
  2357.       delay(500);
  2358.     }
  2359.     FastLED.clear();
  2360.     FastLED.show();
  2361.     delay(300);
  2362.   }
  2363. #ifdef DEBUG
  2364.   Serial.println(F("\n\nTesting showDots() lighting up the upper/lower dots in a repeating pattern..."));
  2365.   Serial.println(F("Press button A (or send 7 using serial input) to advance to the next step...\n"));
  2366. #endif
  2367.   while (test == 5) {
  2368.     if (second() % 2 == 1) {
  2369.       showDots(0);
  2370.     } else {
  2371.       showDots(1);
  2372.     }
  2373.     if (inputButtons() != 0) test++;
  2374.     FastLED.show();
  2375.     delay(20);
  2376.     FastLED.clear();
  2377.   }
  2378.   FastLED.clear();
  2379.   FastLED.show();
  2380.   delay(300);
  2381. #ifdef DEBUG
  2382.   Serial.println(F("\n\nFinal test, displaying 0-9 on all positions, using colorizeOutput();"));
  2383.   Serial.println(F("Press button A (or send 7 using serial input) to start over...\n"));
  2384. #endif
  2385.   while (test == 6) {
  2386.     for (uint8_t i = 0; i < 10; i++) {
  2387.       for (uint8_t pos = 0; pos < LED_DIGITS; pos++) {
  2388.         showDigit(i, pos);
  2389.       }
  2390.       if (inputButtons() != 0) test++;
  2391.       colorizeOutput(1);
  2392.       FastLED.show();
  2393.       delay(500);
  2394.       FastLED.clear();
  2395.     }
  2396.   }
  2397.   FastLED.clear();
  2398.   FastLED.show();
  2399.   delay(500);
  2400. }
  2401. #endif
  2402.  
  2403. /* Wooohaa... this one took a bit longer than expected... ^^ /daniel cikic - 07/2021 */
  2404.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement