2022-08-25 05:23:48 +02:00
/* -[ClockSketch v7.2]----------------------------------------------------------------------------------------
https : //www.instructables.com/ClockSketch-V7-Part-I/
2022-08-25 05:30:54 +02:00
2022-08-25 05:23:48 +02:00
pre - configured for :
Retro 7 Segment Clock v3 - The Final One ( s ) ( 3 LEDs / Segment )
https : //www.instructables.com/Retro-7-Segment-Clock-the-Final-Ones/
https : //www.thingiverse.com/thing:5001559
2022-08-25 05:30:54 +02:00
2022-08-25 05:23:48 +02:00
Arduino UNO / Nano / Pro Mini ( AtMega328 , 5 V , 16 MHz ) , DS3231 RTC
May 2022 - Daniel Cikic
Serial Baud Rates :
Arduino : 57600
nodeMCU : 74880
2022-08-25 05:30:54 +02:00
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2022-08-25 05:23:48 +02:00
// comment below to disable serial in-/output and free some RAM
# define DEBUG
2022-08-25 05:30:54 +02:00
// nodeMCU - uncomment to compile this sketch for nodeMCU 1.0 / ESP8266, make sure to select the proper board
// type inside the IDE! This mode is NOT supported and only experimental!
// #define NODEMCU
// useWiFi - enable WiFi support, WPS setup only! If no WPS support is available on a router check settings
// further down, set useWPS to false and enter ssid/password there
// #define USEWIFI
// useNTP - enable NTPClient, requires NODEMCU and USEWIFI. This will also enforce AUTODST.
// Configure a ntp server further down below!
// #define USENTP
// RTC selection - uncomment the one you're using, comment all others and make sure pin assignemts for
// DS1302 are correct in the parameters section further down!
// #define RTC_DS1302
// #define RTC_DS1307
# define RTC_DS3231
// autoDST - uncomment to enable automatic DST switching, check Time Change Rules below!
// #define AUTODST
// FADING - uncomment to enable fading effects for dots/digits, other parameters further down below
// #define FADING
// autoBrightness - uncomment to enable automatic brightness adjustments by using a photoresistor/LDR
// #define AUTOBRIGHTNESS
// customDisplay - uncomment this to enable displayMyStuff(). It's an example of how to display values
// at specified times, like temperature readouts
// #define CUSTOMDISPLAY
// FastForward will speed up things and advance time, this is only for testing purposes!
// Disables AUTODST, USENTP and USERTC.
// #define FASTFORWARD
// customHelper will start some kind of assistant when adapting this sketch to other led layouts, this
// tests all the steps neccessary to run it on almost any led strip configuration.
// #define CUSTOMHELPER
2022-08-25 05:23:48 +02:00
/* ----------------------------------------------------------------------------------------------------- */
2022-08-25 05:30:54 +02:00
2022-08-25 05:23:48 +02:00
# include <TimeLib.h> // "Time" by Michael Margolis, used in all configs
# include <EEPROM.h> // required for reading/saving settings to eeprom
2022-08-25 05:30:54 +02:00
/* Start RTC config/parameters--------------------------------------------------------------------------
Check pin assignments for DS1302 ( SPI ) , others are I2C ( A4 / A5 on Arduino by default )
Currently all types are using the " Rtc by Makuna " library */
# ifdef RTC_DS1302
# include <ThreeWire.h>
# include <RtcDS1302.h>
ThreeWire myWire ( 7 , 6 , 8 ) ; // IO/DAT, SCLK, CE/RST
RtcDS1302 < ThreeWire > Rtc ( myWire ) ;
# define RTCTYPE "DS1302"
# define USERTC
# endif
# ifdef RTC_DS1307
# include <Wire.h>
# include <RtcDS1307.h>
RtcDS1307 < TwoWire > Rtc ( Wire ) ;
# define RTCTYPE "DS1307"
# define USERTC
# endif
# ifdef RTC_DS3231
# include <Wire.h>
# include <RtcDS3231.h>
RtcDS3231 < TwoWire > Rtc ( Wire ) ;
# define RTCTYPE "DS3231"
# define USERTC
# endif
# if !defined ( USERTC )
# pragma message "No RTC selected, check definitions on top of the sketch!"
# endif
/* End RTC config/parameters---------------------------------------------------------------------------- */
/* Start WiFi config/parameters------------------------------------------------------------------------- */
# ifdef USEWIFI
const bool useWPS = true ; // set to false to disable WPS and use credentials below
const char * wifiSSID = " maWhyFhy " ;
const char * wifiPWD = " 5up3r1337r0xX0r! " ;
# endif
2022-08-25 05:23:48 +02:00
/* End WiFi config/parameters--------------------------------------------------------------------------- */
2022-08-25 05:30:54 +02:00
/* Start NTP config/parameters--------------------------------------------------------------------------
2022-08-25 05:23:48 +02:00
Using NTP will enforce autoDST , so check autoDST / time zone settings below ! */
2022-08-25 05:30:54 +02:00
# ifdef USENTP
/* I recommend using a local ntp service (many routers offer them), don't spam public ones with dozens
of requests a day , get a rtc ! ^ ^ */
//#define NTPHOST "europe.pool.ntp.org"
# define NTPHOST "192.168.2.1"
# ifndef AUTODST
# define AUTODST
# endif
# endif
2022-08-25 05:23:48 +02:00
/* End NTP config/parameters---------------------------------------------------------------------------- */
2022-08-25 05:30:54 +02:00
/* Start autoDST config/parameters ----------------------------------------------------------------------
Comment / uncomment / add TimeChangeRules as needed , only use 2 ( tcr1 , tcr2 ) , comment out unused ones !
2022-08-25 05:23:48 +02:00
Enabling / disabling autoDST will require to set time again , clock will be running in UTC time if autoDST
2022-08-25 05:30:54 +02:00
is enabled , only display times are adjusted ( check serial monitor with DEBUG defined ! )
2022-08-25 05:23:48 +02:00
This will also add options for setting the date ( Year / Month / Day ) when setting time on the clock ! */
2022-08-25 05:30:54 +02:00
# ifdef AUTODST
# include <Timezone.h> // "Timezone" by Jack Christensen
TimeChangeRule * tcr ;
//-----------------------------------------------
/* US */
// TimeChangeRule tcr1 = {"tcr1", First, Sun, Nov, 2, -360}; // utc -6h, valid from first sunday of november at 2am
// TimeChangeRule tcr2 = {"tcr2", Second, Sun, Mar, 2, -300}; // utc -5h, valid from second sunday of march at 2am
//-----------------------------------------------
/* Europe */
TimeChangeRule tcr1 = { " tcr1 " , Last , Sun , Oct , 3 , 60 } ; // standard/winter time, valid from last sunday of october at 3am, UTC + 1 hour (+60 minutes) (negative value like -300 for utc -5h)
TimeChangeRule tcr2 = { " tcr2 " , Last , Sun , Mar , 2 , 120 } ; // daylight/summer time, valid from last sunday of march at 2am, UTC + 2 hours (+120 minutes)
//-----------------------------------------------
Timezone myTimeZone ( tcr1 , tcr2 ) ;
# endif
2022-08-25 05:23:48 +02:00
/* End autoDST config/parameters ----------------------------------------------------------------------- */
2022-08-25 05:30:54 +02:00
/* Start autoBrightness config/parameters -------------------------------------------------------------- */
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)
uint8_t lowerLimitLDR = 50 ; // everything below this value will cause minBrightness to be used
uint8_t minBrightness = 30 ; // anything below this avgLDR value will be ignored
const bool nightMode = false ; // nightmode true -> if minBrightness is used, colorizeOutput() will use a single color for everything, using HSV
const uint8_t nightColor [ 2 ] = { 0 , 70 } ; // hue 0 = red, fixed brightness of 70, https://github.com/FastLED/FastLED/wiki/FastLED-HSV-Colors
float factorLDR = 1.0 ; // try 0.5 - 2.0, compensation value for avgLDR. Set dbgLDR true & define DEBUG and watch the serial monitor. Looking...
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
# ifdef NODEMCU
uint8_t pinLDR = 0 ; // LDR connected to A0 (nodeMCU only offers this one)
# else
uint8_t pinLDR = 1 ; // LDR connected to A1 (in case somebody flashes this sketch on arduino and already has an ldr connected to A1)
# endif
uint8_t intervalLDR = 75 ; // read value from LDR every 75ms (most LDRs have a minimum of about 30ms - 50ms)
uint16_t avgLDR = 0 ; // we will average this value somehow somewhere in readLDR();
uint16_t lastAvgLDR = 0 ; // last average LDR value we got
/* End autoBrightness config/parameters ---------------------------------------------------------------- */
2022-08-25 05:23:48 +02:00
# define SKETCHNAME "ClockSketch v7.2"
# define CLOCKNAME "Retro 7 Segment Clock v3 - The Final One(s), 3 LEDs / segment"
2022-08-25 05:30:54 +02:00
2022-08-25 05:23:48 +02:00
/* Start button config/pins----------------------------------------------------------------------------- */
2022-08-25 05:30:54 +02:00
# ifdef NODEMCU
const uint8_t buttonA = 13 ; // momentary push button, 1 pin to gnd, 1 pin to d7 / GPIO_13
const uint8_t buttonB = 14 ; // momentary push button, 1 pin to gnd, 1 pin to d5 / GPIO_14
# else
const uint8_t buttonA = 3 ; // momentary push button, 1 pin to gnd, 1 pin to d3
const uint8_t buttonB = 4 ; // momentary push button, 1 pin to gnd, 1 pin to d4
# endif
2022-08-25 05:23:48 +02:00
/* End button config/pins------------------------------------------------------------------------------- */
2022-08-25 05:30:54 +02:00
2022-08-25 05:23:48 +02:00
/* Start basic appearance config------------------------------------------------------------------------ */
const bool dotsBlinking = true ; // true = only light up dots on even seconds, false = always on
const bool leadingZero = false ; // true = enable a leading zero, 9:00 -> 09:00, 1:30 -> 01:30...
2022-08-25 05:30:54 +02:00
uint8_t displayMode = 0 ; // 0 = 24h mode, 1 = 12h mode ("1" will also override setting that might be written to EEPROM!)
2022-08-25 05:23:48 +02:00
uint8_t colorMode = 0 ; // different color modes, setting this to anything else than zero will overwrite values written to eeprom, as above
uint16_t colorSpeed = 750 ; // controls how fast colors change, smaller = faster (interval in ms at which color moves inside colorizeOutput();)
const bool colorPreview = true ; // true = preview selected palette/colorMode using "8" on all positions for 3 seconds
const uint8_t colorPreviewDuration = 3 ; // duration in seconds for previewing palettes/colorModes if colorPreview is enabled/true
const bool reverseColorCycling = false ; // true = reverse color movements
const uint8_t brightnessLevels [ 3 ] { 80 , 130 , 220 } ; // 0 - 255, brightness Levels (min, med, max) - index (0-2) will be saved to eeprom
uint8_t brightness = brightnessLevels [ 0 ] ; // default brightness if none saved to eeprom yet / first run
2022-08-25 05:30:54 +02:00
# ifdef FADING
uint8_t fadeDigits = 2 ; // fade digit segments, 0 = disabled, 1 = only fade out segments turned off, 2 = fade old out and fade new in
uint8_t fadeDots = 2 ; // fade dots, 0 = disabled, 1 = turn dots off without fading in/out after specidfied time, 2 = fade in and out
uint8_t fadeDelay = 15 ; // milliseconds between each fading step, 5-25 should work okay-ish
# endif
2022-08-25 05:23:48 +02:00
/* End basic appearance config-------------------------------------------------------------------------- */
2022-08-25 05:30:54 +02:00
2022-08-25 05:23:48 +02:00
/* End of basic config/parameters section */
2022-08-25 05:30:54 +02:00
2022-08-25 05:23:48 +02:00
/* End of feature/parameter section, unless changing advanced things/modifying the sketch there's absolutely nothing to do further down! */
2022-08-25 05:30:54 +02:00
2022-08-25 05:23:48 +02:00
/* library, wifi and ntp stuff depending on above config/parameters */
2022-08-25 05:30:54 +02:00
# ifdef NODEMCU
# if defined ( USENTP ) && !defined ( USEWIFI ) // enforce USEWIFI when USENTP is defined
# define USEWIFI
# pragma warning "USENTP without USEWIFI, enabling WiFi"
# endif
# ifdef USEWIFI
# include <ESP8266WiFi.h>
# include <WiFiUdp.h>
# endif
# endif
# ifdef USENTP
# include <NTPClient.h>
WiFiUDP ntpUDP ;
NTPClient timeClient ( ntpUDP , NTPHOST , 0 , 60000 ) ;
# endif
2022-08-25 05:23:48 +02:00
/* end library stuff */
2022-08-25 05:30:54 +02:00
/* setting feature combinations/options */
# if defined ( FASTFORWARD ) || defined ( CUSTOMHELPER )
bool firstLoop = true ;
# ifdef USERTC
# undef USERTC
# endif
# ifdef USEWIFI
# undef USEWIFI
# endif
# ifdef USENTP
# undef USENTP
# endif
# ifdef AUTODST
# undef AUTODST
# endif
# endif
/* setting feature combinations/options */
2022-08-25 05:23:48 +02:00
2022-08-25 05:30:54 +02:00
/* Start of FastLED/clock stuff */
# define LEDSTUFF
# ifdef LEDSTUFF
# ifdef NODEMCU
# define FASTLED_ESP8266_RAW_PIN_ORDER // this means we'll be using the raw esp8266 pin order -> GPIO_12, which is d6 on nodeMCU
# define LED_PIN 12 // led data in connected to GPIO_12 (d6/nodeMCU)
# else
# define FASTLED_ALLOW_INTERRUPTS 0 // AVR + WS2812 + IRQ = https://github.com/FastLED/FastLED/wiki/Interrupt-problems
# define LED_PIN 6 // led data in connected to d6 (arduino)
# endif
# define LED_PWR_LIMIT 500 // 500mA - Power limit in mA (voltage is set in setup() to 5v)
# define LED_DIGITS 4 // 4 or 6 digits, HH:MM or HH:MM:SS
# define LED_COUNT 103 // Total number of leds, 103 on Retro 7 Segment Clock v3 - The Final One(s) - 3 LEDs/segment
# if ( LED_DIGITS == 6 )
# define LED_COUNT 157 // leds on the 6 digit version
# endif
# include <FastLED.h>
uint8_t markerHSV [ 3 ] = { 0 , 127 , 20 } ; // this color will be used to "flag" leds for coloring later on while updating the leds
CRGB leds [ LED_COUNT ] ;
CRGBPalette16 currentPalette ;
# endif
2022-08-25 05:23:48 +02:00
// start clock specific config/parameters
/* Segment order, seen from the front:
< A >
2022-08-25 05:30:54 +02:00
/ \ / \
F B
\ / \ /
2022-08-25 05:23:48 +02:00
< G >
2022-08-25 05:30:54 +02:00
/ \ / \
E C
\ / \ /
2022-08-25 05:23:48 +02:00
< D >
2022-08-25 05:30:54 +02:00
digit positions , seen from the front :
_ _ _ _ _ _
| _ | | _ | | _ | | _ | | _ | | _ |
| _ | | _ | | _ | | _ | | _ | | _ |
2022-08-25 05:23:48 +02:00
2022-08-25 05:30:54 +02:00
0 1 2 3 4 5
2022-08-25 05:23:48 +02:00
2022-08-25 05:30:54 +02:00
Note : Digit positions for showSegments ( ) depends on the order in which the segments
are defined in segGroups [ ] below . Most of my things / clocks published so far start
from the right side when seen from the front , TFO from the left . Others may have different
orders , like Lazy 7 - QBE , which is using a single strip and has an order of
3 , 0 , 2 , 1 for top left , top right , bottom left , bottom right .
2022-08-25 05:23:48 +02:00
2022-08-25 05:30:54 +02:00
" The Final One(s) " is starting from the left when looking at the front , so it ' s
exactly the reverse order of the old / other ones . This doesn ' t really matter , that ' s
what " digitPositions " a few lines below is for . . .
2022-08-25 05:23:48 +02:00
*/
/* Below is the configuration for led <> segment assignments.
LED_ACCESS_MODE 0 will use the two values inside each segment ( led a , led b )
as they are - 2 leds per segment .
LED_ACCESS_MODE 1 will use the two values inside each segment ( led a , led b )
as start and end value to get 2 + leds / segment .
Example :
leds 0 , 3 - > MODE 0 - > led 0 and 3 inside the segment - > 2 leds
leds 0 , 3 - > MODE 1 - > led 0 - 3 inside the segment - > 4 leds
Simply add all the leds into their corresponding segments inside the array .
The order of digits / strip routing doesn ' t really matter there , positions of
HH : MM : SS are assigned using digitPositions .
2022-08-25 05:30:54 +02:00
digitsLAM - > LED_ACCESS_MODE per digit
2022-08-25 05:23:48 +02:00
*/
// defining access modes for each digit individually
uint8_t digitsLAM [ 6 ] = { 1 , 1 , 1 , 1 , 1 , 1 } ;
# if ( LED_DIGITS == 4 )
2022-08-25 05:30:54 +02:00
const uint8_t digitPositions [ 4 ] = { 0 , 1 , 2 , 3 } ; // positions of HH:MM (3, 0, 2, 1 on L7-QBE)
const uint16_t segGroups [ 28 ] [ 2 ] PROGMEM = {
# endif
# if ( LED_DIGITS == 6 )
const uint8_t digitPositions [ 6 ] = { 0 , 1 , 2 , 3 , 4 , 5 } ; // positions of HH:MM:SS
const uint16_t segGroups [ 42 ] [ 2 ] PROGMEM = {
2022-08-25 05:23:48 +02:00
# endif
/* segments 0-27, 4 digits x 7 segments */
/* digit position 0 */
2022-08-25 05:30:54 +02:00
{ 6 , 8 } , // top, a
{ 3 , 5 } , // top right, b
{ 20 , 22 } , // bottom right, c
{ 17 , 19 } , // bottom, d
{ 14 , 16 } , // bottom left, e
{ 9 , 11 } , // top left, f
{ 0 , 2 } , // center, g
2022-08-25 05:23:48 +02:00
/* digit position 1 */
2022-08-25 05:30:54 +02:00
{ 40 , 42 } , // top, a
{ 37 , 39 } , // top right, b
{ 32 , 34 } , // bottom right, c
{ 29 , 31 } , // bottom, d
{ 26 , 28 } , // bottom left, e
{ 43 , 45 } , // top left, f
{ 46 , 48 } , // center, g
2022-08-25 05:23:48 +02:00
/* digit position 2 */
2022-08-25 05:30:54 +02:00
{ 60 , 62 } , // top, a
{ 57 , 59 } , // top right, b
{ 74 , 76 } , // bottom right, c
{ 71 , 73 } , // bottom, d
{ 68 , 70 } , // bottom left, e
{ 63 , 65 } , // top left, f
{ 54 , 56 } , // center, g
2022-08-25 05:23:48 +02:00
/* digit position 3 */
2022-08-25 05:30:54 +02:00
{ 94 , 96 } , // top, a
{ 91 , 93 } , // top right, b
{ 86 , 88 } , // bottom right, c
{ 83 , 85 } , // bottom, d
{ 80 , 82 } , // bottom left, e
{ 97 , 99 } , // top left, f
{ 100 , 102 } // center, g
# if ( LED_DIGITS == 6 ) // add two digits, 14 segments, only used if LED_DIGITS is 6...
/* segments 28-41, 6 digits x 7 segments */
/* (bogus on some models which don't support 6 digits) */
/* digit position 4 */
, { 114 , 116 } , // top, a !! do not remove the "," at the start of this line !!
{ 111 , 113 } , // top right, b
{ 128 , 130 } , // bottom right, c
{ 125 , 127 } , // bottom, d
{ 122 , 124 } , // bottom left, e
{ 117 , 119 } , // top left, f
{ 108 , 110 } , // center, g
/* digit position 5 */
{ 148 , 150 } , // top, a
{ 145 , 147 } , // top right, b
{ 140 , 142 } , // bottom right, c
{ 137 , 139 } , // bottom, d
{ 134 , 136 } , // bottom left, e
{ 151 , 153 } , // top left, f
{ 154 , 156 } // center, g
# endif // ...end of digits 5+6
2022-08-25 05:23:48 +02:00
} ;
# if ( LED_DIGITS == 4 )
2022-08-25 05:30:54 +02:00
const uint16_t upperDots [ 2 ] PROGMEM = { 49 , 50 } ; // leds inside the upper dots (right on L7-QBE)
const uint16_t lowerDots [ 2 ] PROGMEM = { 52 , 53 } ; // leds inside the lower dots (left on L7-QBE)
# endif
# if ( LED_DIGITS == 6 )
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)
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)
2022-08-25 05:23:48 +02:00
# endif
// 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....
const uint8_t digits [ 21 ] [ 7 ] PROGMEM = {
2022-08-25 05:30:54 +02:00
/* Lets define 10 numbers (0-9) with 7 segments each, also adding some letters
1 = segment is on , 0 = segment is off */
2022-08-25 05:23:48 +02:00
{ 1 , 1 , 1 , 1 , 1 , 1 , 0 } , // 0 -> Show segments a - f, don't show g (center one)
{ 0 , 1 , 1 , 0 , 0 , 0 , 0 } , // 1 -> Show segments b + c (top right and bottom right), nothing else
{ 1 , 1 , 0 , 1 , 1 , 0 , 1 } , // 2 -> and so on...
{ 1 , 1 , 1 , 1 , 0 , 0 , 1 } , // 3
{ 0 , 1 , 1 , 0 , 0 , 1 , 1 } , // 4
{ 1 , 0 , 1 , 1 , 0 , 1 , 1 } , // 5
{ 1 , 0 , 1 , 1 , 1 , 1 , 1 } , // 6
{ 1 , 1 , 1 , 0 , 0 , 0 , 0 } , // 7
{ 1 , 1 , 1 , 1 , 1 , 1 , 1 } , // 8
{ 1 , 1 , 1 , 1 , 0 , 1 , 1 } , // 9
{ 0 , 0 , 0 , 1 , 1 , 1 , 1 } , // t -> some letters/symbols from here on (index 10-20, so this won't...
{ 0 , 0 , 0 , 0 , 1 , 0 , 1 } , // r -> ...interfere with using digits 0-9 by using index 0-9
{ 0 , 1 , 1 , 1 , 0 , 1 , 1 } , // y
{ 0 , 1 , 1 , 1 , 1 , 0 , 1 } , // d
{ 1 , 0 , 0 , 1 , 1 , 1 , 0 } , // C
{ 1 , 0 , 0 , 0 , 1 , 1 , 1 } , // F
{ 1 , 1 , 0 , 0 , 1 , 1 , 0 } , // some kind of "half letter M" (left half), displayed using two digits
{ 1 , 1 , 1 , 0 , 0 , 1 , 0 } , // some kind of "half letter M" (right half), displayed using two digits
{ 1 , 1 , 0 , 0 , 0 , 1 , 1 } , // °
{ 0 , 1 , 1 , 0 , 1 , 1 , 1 } , // H
{ 0 , 0 , 0 , 0 , 0 , 0 , 0 } // "blank"
} ;
uint8_t clockStatus = 1 ; // Used for various things, don't mess around with it! 1 = startup
2022-08-25 05:30:54 +02:00
// 0 = regular mode, 1 = startup, 9x = setup modes (90, 91, 92, 93...)
2022-08-25 05:23:48 +02:00
/* these values will be saved to EEPROM:
0 = index for selected palette
1 = index for selected brightness level
2 = displayMode , 12 h / 24 h mode
3 = colorMode */
/* End of FastLED/clock stuff */
// End clock specific configs/parameters
/* other variables */
uint8_t btnRepeatCounter = 0 ; // keeps track of how often a button press has been repeated
/* */
/* -- this is where the fun parts start -------------------------------------------------------------------------------------------------------- */
void setup ( ) {
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
while ( millis ( ) < 300 ) { // safety delay for serial output
# ifdef NODEMCU
yield ( ) ;
# endif
}
# ifdef NODEMCU
Serial . begin ( 74880 ) ; Serial . println ( F ( " " ) ) ;
# else
Serial . begin ( 57600 ) ; Serial . println ( F ( " " ) ) ;
# endif
# ifdef SKETCHNAME
Serial . print ( SKETCHNAME ) ; Serial . println ( F ( " starting up... " ) ) ;
# endif
# ifdef CLOCKNAME
Serial . print ( " Clock Type: " ) ; Serial . println ( CLOCKNAME ) ;
# endif
# ifdef RTCTYPE
Serial . print ( F ( " Configured RTC: " ) ) ; Serial . println ( RTCTYPE ) ;
# endif
# ifdef LEDSTUFF
Serial . print ( F ( " LED power limit: " ) ) ; Serial . print ( LED_PWR_LIMIT ) ; Serial . println ( F ( " mA " ) ) ;
Serial . print ( F ( " Total LED count: " ) ) ; Serial . println ( LED_COUNT ) ;
Serial . print ( F ( " LED digits: " ) ) ; Serial . println ( LED_DIGITS ) ;
# endif
# ifdef AUTODST
Serial . println ( F ( " autoDST enabled " ) ) ;
# endif
# ifdef NODEMCU
Serial . println ( F ( " Configured for nodeMCU " ) ) ;
# ifdef USEWIFI
Serial . println ( F ( " WiFi enabled " ) ) ;
# endif
# ifdef USENTP
Serial . print ( F ( " NTP enabled, NTPHOST: " ) ) ; Serial . println ( NTPHOST ) ;
# endif
# else
Serial . println ( F ( " Configured for Arduino " ) ) ;
# endif
# ifdef FASTFORWARD
Serial . println ( F ( " !! FASTFORWARD defined !! " ) ) ;
# endif
while ( millis ( ) < 600 ) { // safety delay for serial output
# ifdef NODEMCU
yield ( ) ;
# endif
}
# endif
# ifdef AUTOBRIGHTNESS
# ifdef DEBUG
Serial . print ( F ( " autoBrightness enabled, LDR using pin: " ) ) ; Serial . println ( pinLDR ) ;
# endif
pinMode ( pinLDR , INPUT ) ;
# endif
2022-08-25 05:23:48 +02:00
pinMode ( buttonA , INPUT_PULLUP ) ;
pinMode ( buttonB , INPUT_PULLUP ) ;
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
if ( digitalRead ( buttonA ) = = LOW | | digitalRead ( buttonB ) = = LOW ) {
if ( digitalRead ( buttonA ) = = LOW ) {
Serial . println ( F ( " buttonA is LOW / pressed - check wiring! " ) ) ;
}
if ( digitalRead ( buttonB ) = = LOW ) {
Serial . println ( F ( " buttonB is LOW / pressed - check wiring! " ) ) ;
}
2022-08-25 05:23:48 +02:00
}
2022-08-25 05:30:54 +02:00
# endif
2022-08-25 05:23:48 +02:00
2022-08-25 05:30:54 +02:00
# ifdef LEDSTUFF
FastLED . addLeds < WS2812B , LED_PIN , GRB > ( leds , LED_COUNT ) . setCorrection ( TypicalSMD5050 ) . setTemperature ( DirectSunlight ) . setDither ( 1 ) ;
FastLED . setMaxPowerInVoltsAndMilliamps ( 5 , LED_PWR_LIMIT ) ;
2022-08-25 05:23:48 +02:00
FastLED . clear ( ) ;
FastLED . show ( ) ;
2022-08-25 05:30:54 +02:00
# ifdef CUSTOMHELPER // customHelper() will run in a loop if defined!
while ( 1 > 0 ) {
customHelper ( ) ;
}
# endif
# ifdef DEBUG
Serial . println ( F ( " setup(): Lighting up some leds... " ) ) ;
# endif
for ( uint8_t i = 0 ; i < LED_DIGITS ; i + + ) {
showSegment ( 6 , i ) ;
}
FastLED . show ( ) ;
# endif
# ifdef NODEMCU // if building for nodeMCU...
# ifdef USEWIFI // ...and if using WiFi.....
# ifdef DEBUG
Serial . println ( F ( " Starting up WiFi... " ) ) ;
# endif
WiFi . mode ( WIFI_STA ) ; // set WiFi mode to STA...
if ( useWPS ) {
WiFi . begin ( WiFi . SSID ( ) . c_str ( ) , WiFi . psk ( ) . c_str ( ) ) ; // ...and start connecting using saved credentials...
# ifdef DEBUG
Serial . println ( F ( " Using WPS setup / saved credentials " ) ) ;
# endif
} else {
WiFi . begin ( wifiSSID , wifiPWD ) ; // ...or credentials defined in the USEWIFI config section
# ifdef DEBUG
Serial . println ( F ( " Using credentials from sketch " ) ) ;
# endif
}
unsigned long startTimer = millis ( ) ;
uint8_t wlStatus = 0 ;
uint8_t counter = 6 ;
# ifdef DEBUG
Serial . print ( F ( " Waiting for WiFi connection... " ) ) ;
# endif
while ( wlStatus = = 0 ) {
if ( WiFi . status ( ) ! = WL_CONNECTED ) wlStatus = 0 ; else wlStatus = 1 ;
# ifdef LEDSTUFF
if ( millis ( ) - startTimer > = 1000 ) {
FastLED . clear ( ) ;
showDigit ( counter , digitPositions [ 3 ] ) ;
FastLED . show ( ) ;
if ( counter > 0 ) counter - - ; else wlStatus = 2 ;
startTimer = millis ( ) ;
# ifdef DEBUG
Serial . print ( F ( " . " ) ) ;
# endif
}
# endif
# ifdef NODEMCU
yield ( ) ;
# endif
}
if ( WiFi . status ( ) = = WL_CONNECTED ) { // if status is connected...
# ifdef USENTP // ...and USENTP defined...
timeClient . begin ( ) ; // ...start timeClient
# endif
}
# ifdef DEBUG
Serial . println ( ) ;
if ( WiFi . status ( ) ! = 0 ) {
Serial . print ( F ( " setup(): Connected to SSID: " ) ) ; Serial . println ( WiFi . SSID ( ) ) ;
} else Serial . println ( F ( " setup(): WiFi connection failed. " ) ) ;
# endif
# endif
EEPROM . begin ( 512 ) ;
# endif
# ifdef USERTC
Rtc . Begin ( ) ;
# ifdef DEBUG
Serial . println ( F ( " setup(): RTC.begin(), 2 second safety delay before " ) ) ;
Serial . println ( F ( " doing any read/write actions! " ) ) ;
# endif
unsigned long tmp_time = millis ( ) ;
while ( millis ( ) - tmp_time < 2000 ) {
# ifdef NODEMCU
yield ( ) ;
# endif
}
# ifdef DEBUG
Serial . println ( F ( " setup(): RTC initialized " ) ) ;
# endif
# else
# ifdef DEBUG
Serial . println ( F ( " setup(): No RTC defined! " ) ) ;
# endif
# endif
2022-08-25 05:23:48 +02:00
2022-08-25 05:30:54 +02:00
# ifdef LEDSTUFF
FastLED . clear ( ) ;
FastLED . show ( ) ;
/* eeprom settings */
# ifdef nodeMCU
EEPROM . begin ( 512 ) ;
# endif
paletteSwitcher ( ) ;
brightnessSwitcher ( ) ;
colorModeSwitcher ( ) ;
displayModeSwitcher ( ) ;
# endif
# ifdef FASTFORWARD
setTime ( 21 , 59 , 50 , 30 , 6 , 2021 ) ; // h, m, s, d, m, y to set the clock to when using FASTFORWARD
# endif
# ifdef USENTP
syncHelper ( ) ;
# endif
clockStatus = 0 ; // change from 1 (startup) to 0 (running mode)
# ifdef DEBUG
printTime ( ) ;
Serial . println ( F ( " setup() done " ) ) ;
Serial . println ( F ( " ------------------------------------------------------ " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
/* MAIN LOOP */
void loop ( ) {
static uint8_t lastInput = 0 ; // != 0 if any button press has been detected
static uint8_t lastSecondDisplayed = 0 ; // This keeps track of the last second when the display was updated (HH:MM and HH:MM:SS)
static unsigned long lastCheckRTC = millis ( ) ; // This will be used to read system time in case no RTC is defined (not supported!)
static bool doUpdate = false ; // Update led content whenever something sets this to true. Coloring will always happen at fixed intervals!
2022-08-25 05:30:54 +02:00
# ifdef USERTC
static RtcDateTime rtcTime = Rtc . GetDateTime ( ) . Epoch32Time ( ) ; // Get time from rtc (epoch)
# else
static time_t sysTime = now ( ) ; // if no rtc is defined, get local system time
# endif
# ifdef LEDSTUFF
static uint8_t refreshDelay = 5 ; // refresh leds every 5ms
static long lastRefresh = millis ( ) ; // Keeps track of the last led update/FastLED.show() inside the loop
# ifdef AUTOBRIGHTNESS
static long lastReadLDR = millis ( ) ;
# endif
# endif
# ifdef FASTFORWARD
static unsigned long lastFFStep = millis ( ) ; // Keeps track of last time increment if FASTFORWARD is defined
# endif
2022-08-25 05:23:48 +02:00
if ( lastInput ! = 0 ) { // If any button press is detected...
if ( btnRepeatCounter < 1 ) { // execute short/single press function(s)
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " loop(): " ) ) ; Serial . print ( lastInput ) ; Serial . println ( F ( " (short press) " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
if ( lastInput = = 1 ) { // short press button A
2022-08-25 05:30:54 +02:00
# ifdef LEDSTUFF
brightnessSwitcher ( ) ;
# endif
2022-08-25 05:23:48 +02:00
}
if ( lastInput = = 2 ) { // short press button B
2022-08-25 05:30:54 +02:00
# ifdef LEDSTUFF
paletteSwitcher ( ) ;
# endif
2022-08-25 05:23:48 +02:00
}
if ( lastInput = = 3 ) { // short press button A + button B
}
} else if ( btnRepeatCounter > 8 ) { // execute long press function(s)...
btnRepeatCounter = 1 ; // ..reset btnRepeatCounter to stop this from repeating
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " loop(): " ) ) ; Serial . print ( lastInput ) ; Serial . println ( F ( " (long press) " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
if ( lastInput = = 1 ) { // long press button A
2022-08-25 05:30:54 +02:00
# ifdef LEDSTUFF
colorModeSwitcher ( ) ;
# endif
2022-08-25 05:23:48 +02:00
}
if ( lastInput = = 2 ) { // long press button B
2022-08-25 05:30:54 +02:00
# ifdef LEDSTUFF
displayModeSwitcher ( ) ;
# endif
2022-08-25 05:23:48 +02:00
}
if ( lastInput = = 3 ) { // long press button A + button B
2022-08-25 05:30:54 +02:00
# ifdef USEWIFI // if USEWIFI is defined and...
if ( useWPS ) { // ...if useWPS is true...
connectWPS ( ) ; // connect WiFi using WPS
}
# else // if USEWIFI is not defined...
# ifdef LEDSTUFF
FastLED . clear ( ) ;
FastLED . show ( ) ;
setupClock ( ) ; // start date/time setup
# endif
# endif
2022-08-25 05:23:48 +02:00
}
while ( digitalRead ( buttonA ) = = LOW | | digitalRead ( buttonB ) = = LOW ) { // wait until buttons are released again
2022-08-25 05:30:54 +02:00
# ifdef LEDSTUFF
if ( millis ( ) % 50 = = 0 ) { // Refresh leds every 50ms to give optical feedback
colorizeOutput ( colorMode ) ;
FastLED . show ( ) ;
}
# endif
# ifdef NODEMCU
yield ( ) ;
# endif
2022-08-25 05:23:48 +02:00
}
}
}
2022-08-25 05:30:54 +02:00
# ifdef FASTFORWARD // if FASTFORWARD is defined...
if ( millis ( ) - lastFFStep > = 250 ) { // ...and 250ms have passed...
adjustTime ( 5 ) ; // ...add 5 seconds to current time
lastFFStep = millis ( ) ;
}
# endif
2022-08-25 05:23:48 +02:00
if ( millis ( ) - lastCheckRTC > = 50 ) { // check rtc/system time every 50ms
2022-08-25 05:30:54 +02:00
# ifdef USERTC
rtcTime = Rtc . GetDateTime ( ) . Epoch32Time ( ) ;
if ( lastSecondDisplayed ! = second ( rtcTime ) ) doUpdate = true ;
# else
sysTime = now ( ) ;
if ( lastSecondDisplayed ! = second ( sysTime ) ) doUpdate = true ;
# endif
2022-08-25 05:23:48 +02:00
lastCheckRTC = millis ( ) ;
}
if ( doUpdate ) { // this will update the led array if doUpdate is true because of a new second from the rtc
2022-08-25 05:30:54 +02:00
# ifdef USERTC
setTime ( rtcTime ) ; // sync system time to rtc every second
# ifdef LEDSTUFF
FastLED . clear ( ) ; // 1A - clear all leds...
displayTime ( rtcTime ) ; // 2A - output rtcTime to the led array..
# endif
lastSecondDisplayed = second ( rtcTime ) ;
# else
# ifdef LEDSTUFF
FastLED . clear ( ) ; // 1B - clear all leds...
displayTime ( sysTime ) ; // 2B - output sysTime to the led array...
# endif
lastSecondDisplayed = second ( sysTime ) ;
# endif
# ifdef CUSTOMDISPLAY
displayMyStuff ( ) ; // 3AB - if customDisplay is defined this will clear the led array again to display custom values...
# endif
2022-08-25 05:23:48 +02:00
doUpdate = false ;
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
if ( second ( ) % 20 = = 0 ) {
printTime ( ) ;
}
# endif
# ifdef USENTP // if NTP is enabled, resync to ntp server at 0:00:00 utc
if ( hour ( ) = = 0 & & minute ( ) = = 0 and second ( ) = = 0 ) {
syncHelper ( ) ;
}
# endif
2022-08-25 05:23:48 +02:00
}
2022-08-25 05:30:54 +02:00
# ifdef LEDSTUFF
colorizeOutput ( colorMode ) ; // 1C, 2C, 3C...colorize the data inside the led array right now...
# ifdef AUTOBRIGHTNESS
if ( millis ( ) - lastReadLDR > = intervalLDR ) { // if LDR is enabled and sample interval has been reached...
readLDR ( ) ; // ...call readLDR();
if ( abs ( avgLDR - lastAvgLDR ) > = 5 ) { // if avgLDR has changed for more than +/- 5 update lastAvgLDR
lastAvgLDR = avgLDR ;
FastLED . setBrightness ( avgLDR ) ;
}
lastReadLDR = millis ( ) ;
}
# endif
# ifdef FADING
digitsFader ( ) ;
dotsFader ( ) ;
# endif
if ( millis ( ) - lastRefresh > = refreshDelay ) {
FastLED . show ( ) ;
lastRefresh = millis ( ) ;
2022-08-25 05:23:48 +02:00
}
2022-08-25 05:30:54 +02:00
# endif
2022-08-25 05:23:48 +02:00
lastInput = inputButtons ( ) ;
}
/* */
# ifdef LEDSTUFF
# ifdef CUSTOMDISPLAY
2022-08-25 05:30:54 +02:00
void displayMyStuff ( ) {
2022-08-25 05:23:48 +02:00
/* One way to display custom sensor data/other things. displayMyStuff() is then called inside the doUpdate if statement inside
void loop ( ) - after updating the leds but before calling colorizeOutput ( ) and FastLED . show ( ) */
2022-08-25 05:30:54 +02:00
if ( second ( ) > = 30 & & second ( ) < 40 ) { // only do something if current second is 30-39
# ifdef RTC_DS3231 // if DS3231 is used we can read the temperature from that for demo purposes here
float rtcTemp = Rtc . GetTemperature ( ) . AsFloatDegC ( ) ; // get temperature in °C as float (25.75°C)....
uint8_t tmp = round ( rtcTemp ) ; // ...and round (26°C)
# else
uint8_t tmp = 99 ; // get whatever value from whatever sensor into tmp
# endif
FastLED . clear ( ) ;
if ( LED_DIGITS = = 4 ) { // if 4 digits, display following content:
showDigit ( tmp / 10 , digitPositions [ 0 ] ) ; // tmp (26°C) / 10 = 2 on position 1 of HH
showDigit ( tmp % 10 , digitPositions [ 1 ] ) ; // tmp (26°C) % 10 = 6 on position 2 of HH
showDigit ( 18 , digitPositions [ 2 ] ) ; // ° symbol from array digits[][] on position 1 of MM
showDigit ( 14 , digitPositions [ 3 ] ) ; // C from array digits[][] on position 2 of MM
}
if ( LED_DIGITS = = 6 ) { // if 6 digits....
showDigit ( tmp / 10 , digitPositions [ 2 ] ) ; // ...do the above using MM:SS positions instead of HH:MM
showDigit ( tmp % 10 , digitPositions [ 3 ] ) ;
showDigit ( 18 , digitPositions [ 4 ] ) ;
showDigit ( 14 , digitPositions [ 5 ] ) ;
}
2022-08-25 05:23:48 +02:00
}
}
# endif
# ifdef FADING
void fadeSegment ( uint8_t pos , uint8_t segment , uint8_t amount , uint8_t fadeType ) {
2022-08-25 05:30:54 +02:00
/* this will check if the first led of a given segment is lit and if it is, will fade by
2022-08-25 05:23:48 +02:00
amount using fadeType . fadeType is important because when fading things in that where
off previously we must avoid setting them black at first - hence fadeLightBy instead
of fadeToBlack . */
uint8_t ledAM = digitsLAM [ pos ] ; // led access mode according to the position
if ( leds [ pgm_read_word_near ( & segGroups [ segment + pos * 7 ] [ 0 ] ) ] ) {
if ( ledAM = = 0 ) {
for ( uint8_t i = 0 ; i < 2 ; i + + ) {
if ( fadeType = = 0 ) {
leds [ pgm_read_word_near ( & segGroups [ segment + pos * 7 ] [ i ] ) ] . fadeToBlackBy ( amount ) ;
} else {
leds [ pgm_read_word_near ( & segGroups [ segment + pos * 7 ] [ i ] ) ] . fadeLightBy ( amount ) ;
}
}
}
if ( ledAM = = 1 ) {
uint16_t startLed = pgm_read_word_near ( & segGroups [ segment + pos * 7 ] [ 0 ] ) ;
uint16_t endLed = pgm_read_word_near ( & segGroups [ segment + pos * 7 ] [ 1 ] ) ;
for ( uint16_t i = startLed ; i < = endLed ; i + + ) {
if ( fadeType = = 0 ) {
leds [ i ] . fadeToBlackBy ( amount ) ;
} else {
leds [ i ] . fadeLightBy ( amount ) ;
}
}
}
}
}
void digitsFader ( ) {
if ( fadeDigits = = 0 ) return ;
static unsigned long firstRun = 0 ; // time when a change has been detected and fading starts
static unsigned long lastRun = 0 ; // used to store time when this function was executed the last time
static boolean active = false ; // will be used as a flag when to do something / fade segments
static uint8_t previousSegments [ LED_DIGITS ] [ 7 ] = { 0 } ; // all the segments lit after the last run
static uint8_t currentSegments [ LED_DIGITS ] [ 7 ] = { 0 } ; // all the segments lit right now
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
static uint8_t fadeSteps = 15 ; // steps used to fade dots in or out
lastRun = millis ( ) ;
if ( ! active ) { // this will check if....
firstRun = millis ( ) ;
for ( uint8_t digitPos = 0 ; digitPos < LED_DIGITS ; digitPos + + ) { // ...any of the segments are on....
for ( uint8_t segmentPos = 0 ; segmentPos < 7 ; segmentPos + + ) {
if ( leds [ pgm_read_word_near ( & segGroups [ segmentPos + digitPos * 7 ] [ 0 ] ) ] ) {
currentSegments [ digitPos ] [ segmentPos ] = 1 ;
} else {
currentSegments [ digitPos ] [ segmentPos ] = 0 ;
}
if ( currentSegments [ digitPos ] [ segmentPos ] ! = previousSegments [ digitPos ] [ segmentPos ] ) { // ...and compare them to the previous displayed segments.
active = true ; // if a change has been detected, set active = true so fading gets executed
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " digitPos: " ) ) ; Serial . print ( digitPos ) ;
Serial . print ( F ( " - segmentPos: " ) ) ; Serial . print ( segmentPos ) ;
Serial . print ( F ( " was " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
if ( currentSegments [ digitPos ] [ segmentPos ] = = 0 ) {
changedSegments [ digitPos ] [ segmentPos ] = 1 ;
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . println ( F ( " ON, is now OFF " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
} else {
changedSegments [ digitPos ] [ segmentPos ] = 2 ;
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . println ( F ( " OFF, is now ON " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
}
}
}
}
if ( active ) { // this part is executed once a change has been detected....
static uint8_t counter = 1 ;
static unsigned long lastFadeStep = millis ( ) ;
for ( uint8_t digitPos = 0 ; digitPos < LED_DIGITS ; digitPos + + ) { // redraw segments that have turned off, so we can fade them out...
for ( uint8_t segmentPos = 0 ; segmentPos < 7 ; segmentPos + + ) {
if ( changedSegments [ digitPos ] [ segmentPos ] = = 1 ) {
showSegment ( segmentPos , digitPos ) ;
}
}
}
colorizeOutput ( colorMode ) ; // colorize again after redraw, so colors keep consistent
for ( uint8_t digitPos = 0 ; digitPos < LED_DIGITS ; digitPos + + ) {
for ( uint8_t segmentPos = 0 ; segmentPos < 7 ; segmentPos + + ) {
if ( changedSegments [ digitPos ] [ segmentPos ] = = 1 ) { // 1 - segment has turned on, this one has to be faded in
fadeSegment ( digitPos , segmentPos , counter * ( 255.0 / fadeSteps ) , 0 ) ; // fadeToBlackBy, segments supposed to be off/fading out
}
if ( changedSegments [ digitPos ] [ segmentPos ] = = 2 ) { // 2 - segment has turned off, this one has to be faded out
if ( fadeDigits = = 2 ) {
fadeSegment ( digitPos , segmentPos , 255 - counter * ( 255.0 / fadeSteps ) , 1 ) ; // fadeLightBy, segments supposed to be on/fading in
}
}
}
}
if ( millis ( ) - lastFadeStep > = fadeDelay ) {
counter + + ;
lastFadeStep = millis ( ) ;
}
if ( counter > fadeSteps ) { // done with fading, reset variables...
counter = 1 ;
active = false ;
for ( uint8_t digitPos = 0 ; digitPos < LED_DIGITS ; digitPos + + ) { // and save current segments to previousSegments
for ( uint8_t segmentPos = 0 ; segmentPos < 7 ; segmentPos + + ) {
if ( leds [ pgm_read_word_near ( & segGroups [ segmentPos + digitPos * 7 ] [ 0 ] ) ] ) {
previousSegments [ digitPos ] [ segmentPos ] = 1 ;
} else {
previousSegments [ digitPos ] [ segmentPos ] = 0 ;
}
changedSegments [ digitPos ] [ segmentPos ] = 0 ;
}
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " digit fading sequence took " ) ) ; // for debugging/checking duration - fading should never take longer than 1000ms!
Serial . print ( millis ( ) - firstRun ) ;
Serial . println ( F ( " ms " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
}
}
void dotsFader ( ) {
if ( fadeDots = = 0 ) return ;
static unsigned long firstRun = 0 ;
static unsigned long lastRun = 0 ;
static boolean active = false ;
static uint8_t fadeSteps = 15 ;
lastRun = millis ( ) ;
if ( ! active ) {
if ( leds [ pgm_read_word_near ( & upperDots [ 0 ] ) ] ) {
active = true ;
firstRun = millis ( ) ;
}
}
if ( fadeDots = = 1 & & active ) { // action = 1, simply turn off specidifc leds after 500ms
if ( lastRun - firstRun > = 500 ) {
for ( uint8_t i = 0 ; i < ( sizeof ( upperDots ) / sizeof ( upperDots [ 0 ] ) ) ; i + + ) {
leds [ pgm_read_word_near ( & upperDots [ i ] ) ] . setHSV ( 0 , 0 , 0 ) ;
}
for ( uint8_t i = 0 ; i < ( sizeof ( lowerDots ) / sizeof ( lowerDots [ 0 ] ) ) ; i + + ) {
leds [ pgm_read_word_near ( & lowerDots [ i ] ) ] . setHSV ( 0 , 0 , 0 ) ;
}
active = false ;
}
}
if ( fadeDots = = 2 & & active ) { // fade in/out dots
static uint8_t counter = 1 ;
static unsigned long lastFadeStep = millis ( ) ;
static boolean fadeInDone = true ;
if ( ! fadeInDone ) {
for ( uint8_t i = 0 ; i < ( sizeof ( upperDots ) / sizeof ( upperDots [ 0 ] ) ) ; i + + ) {
leds [ pgm_read_word_near ( & upperDots [ i ] ) ] . fadeToBlackBy ( 255 - counter * ( 255.0 / fadeSteps ) ) ;
}
for ( uint8_t i = 0 ; i < ( sizeof ( lowerDots ) / sizeof ( lowerDots [ 0 ] ) ) ; i + + ) {
leds [ pgm_read_word_near ( & lowerDots [ i ] ) ] . fadeToBlackBy ( 255 - counter * ( 255.0 / fadeSteps ) ) ;
}
if ( millis ( ) - lastFadeStep > = fadeDelay ) {
counter + + ;
lastFadeStep = millis ( ) ;
}
if ( counter > fadeSteps ) {
counter = 1 ;
fadeInDone = true ;
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " dot fade-in sequence took " ) ) ; // for debugging/checking
Serial . print ( millis ( ) - firstRun ) ;
Serial . println ( F ( " ms " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
}
if ( lastRun - firstRun > = 950 - fadeDelay * fadeSteps ) {
for ( uint8_t i = 0 ; i < ( sizeof ( upperDots ) / sizeof ( upperDots [ 0 ] ) ) ; i + + ) {
leds [ pgm_read_word_near ( & upperDots [ i ] ) ] . fadeToBlackBy ( counter * ( 255.0 / fadeSteps ) ) ;
}
for ( uint8_t i = 0 ; i < ( sizeof ( lowerDots ) / sizeof ( lowerDots [ 0 ] ) ) ; i + + ) {
leds [ pgm_read_word_near ( & lowerDots [ i ] ) ] . fadeToBlackBy ( counter * ( 255.0 / fadeSteps ) ) ;
}
if ( millis ( ) - lastFadeStep > = fadeDelay ) {
counter + + ;
lastFadeStep = millis ( ) ;
}
if ( counter > fadeSteps ) {
counter = 1 ;
active = false ;
fadeInDone = false ;
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " dot fading sequence took " ) ) ; // for debugging/checking
Serial . print ( millis ( ) - firstRun ) ;
Serial . println ( F ( " ms " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
}
}
}
# endif
# ifdef AUTOBRIGHTNESS
void readLDR ( ) { // read LDR value 5 times and write average to avgLDR
static uint8_t runCounter = 1 ;
static uint16_t tmp = 0 ;
uint8_t readOut = map ( analogRead ( pinLDR ) , 0 , 1023 , 0 , 250 ) ;
tmp + = readOut ;
if ( runCounter = = 5 ) {
avgLDR = ( tmp / 5 ) * factorLDR ;
tmp = 0 ; runCounter = 0 ;
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
if ( dbgLDR ) {
Serial . print ( F ( " readLDR(): avgLDR value: " ) ) ;
Serial . print ( avgLDR ) ;
}
# endif
2022-08-25 05:23:48 +02:00
if ( avgLDR < minBrightness ) avgLDR = minBrightness ;
if ( avgLDR > brightness ) avgLDR = brightness ;
if ( avgLDR > = upperLimitLDR & & avgLDR < brightness ) avgLDR = brightness ; // if avgLDR is above upperLimitLDR switch to max current brightness
if ( avgLDR < = lowerLimitLDR ) avgLDR = minBrightness ; // if avgLDR is below lowerLimitLDR switch to minBrightness
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
if ( dbgLDR ) {
Serial . print ( F ( " - adjusted to: " ) ) ;
Serial . println ( avgLDR ) ;
}
# endif
2022-08-25 05:23:48 +02:00
}
runCounter + + ;
}
# endif
void setupClock ( ) {
2022-08-25 05:30:54 +02:00
/* This sets time and date (if AUTODST is defined) on the clock/rtc */
2022-08-25 05:23:48 +02:00
clockStatus = 90 ; // clockStatus 9x = setup, relevant for other functions/coloring
while ( digitalRead ( buttonA ) = = LOW | | digitalRead ( buttonB ) = = LOW ) { // do nothing until both buttons are released to avoid accidental inputs right away
2022-08-25 05:30:54 +02:00
# ifdef NODEMCU
yield ( ) ;
# endif
2022-08-25 05:23:48 +02:00
}
tmElements_t setupTime ; // Create a time element which will be used. Using the current time would
setupTime . Hour = 12 ; // give some problems (like time still running while setting hours/minutes)
setupTime . Minute = 0 ; // Setup starts at 12 (12 pm) (utc 12 if AUTODST is defined)
2022-08-25 05:30:54 +02:00
setupTime . Second = 0 ; //
2022-08-25 05:23:48 +02:00
setupTime . Day = 8 ; // date settings only used when AUTODST is defined, but will set them anyways
setupTime . Month = 7 ; // see above
setupTime . Year = 21 ; // current year - 2000 (2021 - 2000 = 21)
2022-08-25 05:30:54 +02:00
# ifdef USERTC
RtcDateTime writeTime ;
# endif
# ifdef AUTODST
clockStatus = 91 ; // 91 = y/m/d setup
uint8_t y , m , d ;
y = getUserInput ( 12 , 20 , 21 , 99 ) ; // show Y + blank, get value from 21 - 99 into y
setupTime . Year = y + 30 ; // 2 digit year + 30 (epoch), so we get offset from 1970
m = getUserInput ( 16 , 17 , 1 , 12 ) ; // show M, get value from 1 - 12 into m
setupTime . Month = m ;
if ( m = = 2 ) {
if ( leapYear ( y + 2000 ) ) { // check for leap year...
# ifdef DEBUG // ...and get according day input ranges for each month
Serial . println ( F ( " setupClock(): Leap year detected " ) ) ;
# endif
d = getUserInput ( 13 , 20 , 1 , 29 ) ;
} else {
d = getUserInput ( 13 , 20 , 1 , 28 ) ;
}
}
if ( m = = 1 | | m = = 3 | | m = = 5 | | m = = 7 | | m = = 8 | | m = = 10 | | m = = 12 ) {
d = getUserInput ( 13 , 20 , 1 , 31 ) ;
}
if ( m = = 4 | | m = = 6 | | m = = 9 | | m = = 11 ) {
d = getUserInput ( 13 , 20 , 1 , 30 ) ;
}
setupTime . Day = d ;
# ifdef USERTC
writeTime = { 2000 + y , setupTime . Month , setupTime . Day ,
setupTime . Hour , setupTime . Minute , setupTime . Second } ;
Rtc . SetDateTime ( writeTime ) ;
setTime ( makeTime ( setupTime ) ) ;
# ifdef DEBUG
Serial . println ( now ( ) ) ;
Serial . print ( F ( " setupClock(): RTC time/date set to: " ) ) ; Serial . println ( writeTime ) ;
# endif
# else
setTime ( makeTime ( setupTime ) ) ;
# endif
# else
setupTime . Year = 51 ;
# endif
2022-08-25 05:23:48 +02:00
uint8_t lastInput = 0 ;
// hours
while ( lastInput ! = 2 ) {
clockStatus = 92 ; // 92 = HH setup
if ( lastInput = = 1 ) {
if ( setupTime . Hour < 23 ) {
setupTime . Hour + + ;
} else {
setupTime . Hour = 0 ;
}
}
displayTime ( makeTime ( setupTime ) ) ;
lastInput = inputButtons ( ) ;
}
lastInput = 0 ;
// minutes
while ( lastInput ! = 2 ) {
clockStatus = 93 ; // 93 = MM setup
if ( lastInput = = 1 ) {
if ( setupTime . Minute < 59 ) {
setupTime . Minute + + ;
} else {
setupTime . Minute = 0 ;
}
}
displayTime ( makeTime ( setupTime ) ) ;
lastInput = inputButtons ( ) ;
}
lastInput = 0 ;
// seconds
if ( LED_DIGITS = = 6 ) {
while ( lastInput ! = 2 ) {
clockStatus = 94 ; // 94 = SS setup
if ( lastInput = = 1 ) {
if ( setupTime . Second < 59 ) {
setupTime . Second + + ;
} else {
setupTime . Second = 0 ;
}
}
displayTime ( makeTime ( setupTime ) ) ;
lastInput = inputButtons ( ) ;
}
lastInput = 0 ;
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
# ifdef AUTODST
Serial . print ( F ( " setupClock(): " ) ) ;
Serial . print ( F ( " Y/M/D -> " ) ) ;
Serial . print ( 1970 + setupTime . Year ) ; Serial . print ( F ( " / " ) ) ;
Serial . print ( setupTime . Month ) ; Serial . print ( F ( " / " ) ) ;
Serial . println ( setupTime . Day ) ;
# endif
Serial . print ( F ( " setupClock(): " ) ) ;
Serial . print ( F ( " HH:MM:SS -> " ) ) ;
# ifdef AUTODST
Serial . print ( F ( " AUTODST enabled, setting LOCAL time -> " ) ) ;
# endif
if ( setupTime . Hour < 10 ) Serial . print ( F ( " 0 " ) ) ;
Serial . print ( setupTime . Hour ) ; Serial . print ( F ( " : " ) ) ;
if ( setupTime . Minute < 10 ) Serial . print ( F ( " 0 " ) ) ;
Serial . print ( setupTime . Minute ) ; Serial . print ( F ( " : " ) ) ;
if ( setupTime . Second < 10 ) Serial . print ( F ( " 0 " ) ) ;
Serial . println ( setupTime . Second ) ;
# endif
# ifdef USERTC
writeTime = { 1970 + setupTime . Year , setupTime . Month , setupTime . Day ,
setupTime . Hour , setupTime . Minute , setupTime . Second } ;
# ifdef AUTODST
time_t t = myTimeZone . toUTC ( makeTime ( setupTime ) ) ; // get UTC time from entered time
writeTime = { 1970 + setupTime . Year , month ( t ) , day ( t ) ,
hour ( t ) , minute ( t ) , second ( t ) } ;
# endif
Rtc . SetDateTime ( writeTime ) ;
setTime ( makeTime ( setupTime ) ) ;
# ifdef DEBUG
Serial . println ( F ( " setupClock(): RTC time set " ) ) ;
Serial . println ( makeTime ( setupTime ) ) ;
printTime ( ) ;
# endif
# else
# ifdef AUTODST
time_t t = myTimeZone . toUTC ( makeTime ( setupTime ) ) ; // get UTC time from entered time
setTime ( t ) ;
# else
setTime ( makeTime ( setupTime ) ) ;
# endif
# endif
2022-08-25 05:23:48 +02:00
clockStatus = 0 ;
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . println ( F ( " setupClock() done " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
uint16_t getUserInput ( uint8_t sym1 , uint8_t sym2 , uint8_t startVal , uint8_t endVal ) {
2022-08-25 05:30:54 +02:00
/* This will show two symbols on HH and allow to enter a 2 digit value using the buttons
and display the value on MM . */
2022-08-25 05:23:48 +02:00
static uint8_t lastInput = 0 ;
static uint8_t currentVal = startVal ;
static bool newInput = true ;
if ( newInput ) {
currentVal = startVal ;
newInput = false ;
}
while ( lastInput ! = 2 ) {
if ( lastInput = = 1 ) {
if ( currentVal < endVal ) {
currentVal + + ;
} else {
currentVal = startVal ;
}
}
FastLED . clear ( ) ;
showDigit ( sym1 , digitPositions [ 0 ] ) ;
showDigit ( sym2 , digitPositions [ 1 ] ) ;
showDigit ( currentVal / 10 , digitPositions [ 2 ] ) ;
showDigit ( currentVal % 10 , digitPositions [ 3 ] ) ;
if ( millis ( ) % 30 = = 0 ) {
colorizeOutput ( colorMode ) ;
FastLED . show ( ) ;
2022-08-25 05:30:54 +02:00
}
2022-08-25 05:23:48 +02:00
lastInput = inputButtons ( ) ;
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " getUserInput(): returned " ) ) ; Serial . println ( currentVal ) ;
# endif
2022-08-25 05:23:48 +02:00
lastInput = 0 ;
newInput = true ;
return currentVal ;
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " getUserInput(): returned " ) ) ; Serial . println ( currentVal ) ;
# endif
2022-08-25 05:23:48 +02:00
}
void colorizeOutput ( uint8_t mode ) {
2022-08-25 05:30:54 +02:00
/* So far showDigit()/showSegment() only set some leds inside the array to values from "markerHSV" but we haven't updated
the leds yet using FastLED . show ( ) . This function does the coloring of the right now single colored but " insivible "
output . This way color updates / cycles aren ' t tied to updating display contents */
2022-08-25 05:23:48 +02:00
static unsigned long lastRun = 0 ;
static unsigned long lastColorChange = 0 ;
static uint8_t startColor = 0 ;
static uint8_t colorOffset = 0 ; // different offsets result in quite different results, depending on the amount of leds inside each segment...
2022-08-25 05:30:54 +02:00
// ...so it's set inside each color mode if required
2022-08-25 05:23:48 +02:00
/* mode 0 = check every segment if it's lit and assign a color based on position -> different color per digit
Checking the leds like this will not include the dots - they ' ll be colored later on */
if ( mode = = 0 ) {
colorOffset = 256 / LED_DIGITS ;
for ( uint8_t pos = 0 ; pos < LED_DIGITS ; pos + + ) {
for ( uint8_t segment = 0 ; segment < 7 ; segment + + ) {
colorizeSegment ( segment , pos , startColor + colorOffset * pos ) ;
}
}
}
/* mode 1 = simply assign different colors with an offset of "colorOffset" to each led that's not black/off -
This will include the dots if they ' re supposed to be on - but they will be overwritten later for all modes */
if ( mode = = 1 ) {
colorOffset = 32 ;
for ( uint16_t i = 0 ; i < LED_COUNT ; i + + ) {
if ( leds [ i ] ) {
leds [ i ] = ColorFromPalette ( currentPalette , startColor + i * colorOffset , brightness , LINEARBLEND ) ;
}
}
}
/* mode 2 = check every segment if it's lit and assign a color based on segment -> different color per segment,
same across digits . Checking the leds like this will not include the dots - they ' ll be colored later on */
if ( mode = = 2 ) {
colorOffset = 24 ;
for ( uint8_t pos = 0 ; pos < LED_DIGITS ; pos + + ) {
for ( uint8_t segment = 0 ; segment < 7 ; segment + + ) {
colorizeSegment ( segment , digitPositions [ pos ] , startColor + 1 * colorOffset * segment ) ;
}
}
}
/* mode 3 = same as above - but will assign colorOffsets depending on segment in a specific order (top/down effect) */
if ( mode = = 3 ) {
uint8_t colorOffsets [ 7 ] = { 0 , 24 , 72 , 96 , 72 , 24 , 48 } ; // colorOffsets for segments a-g
for ( uint8_t pos = 0 ; pos < LED_DIGITS ; pos + + ) {
for ( uint8_t segment = 0 ; segment < 7 ; segment + + ) {
colorizeSegment ( segment , digitPositions [ pos ] , startColor + 1 * colorOffsets [ segment ] ) ;
}
}
}
/* clockStatus >= 90 is used for coloring output while in setup mode */
if ( clockStatus > = 90 ) {
static boolean blinkFlag = true ;
static unsigned long lastBlink = millis ( ) ;
static uint8_t b = brightnessLevels [ 0 ] ;
if ( millis ( ) - lastBlink > 333 ) { // blink switch frequency, 3 times a second
if ( blinkFlag ) {
blinkFlag = false ;
b = brightnessLevels [ 1 ] ;
} else {
blinkFlag = true ;
b = brightnessLevels [ 0 ] ;
}
lastBlink = millis ( ) ;
2022-08-25 05:30:54 +02:00
} // unset values = red, set value = green, current value = yellow and blinkinkg
2022-08-25 05:23:48 +02:00
for ( uint8_t pos = 0 ; pos < LED_DIGITS ; pos + + ) {
if ( clockStatus = = 91 ) { // Y/M/D setup
colorHelper ( digitPositions [ 0 ] , 0 , 255 , brightness ) ;
colorHelper ( digitPositions [ 1 ] , 0 , 255 , brightness ) ;
colorHelper ( digitPositions [ 2 ] , 64 , 255 , b ) ;
colorHelper ( digitPositions [ 3 ] , 64 , 255 , b ) ;
}
if ( clockStatus = = 92 ) { // hours
colorHelper ( digitPositions [ 0 ] , 64 , 255 , b ) ;
colorHelper ( digitPositions [ 1 ] , 64 , 255 , b ) ;
colorHelper ( digitPositions [ 2 ] , 0 , 255 , brightness ) ;
colorHelper ( digitPositions [ 3 ] , 0 , 255 , brightness ) ;
if ( LED_DIGITS = = 6 ) {
colorHelper ( digitPositions [ 4 ] , 0 , 255 , brightness ) ;
colorHelper ( digitPositions [ 5 ] , 0 , 255 , brightness ) ;
}
}
if ( clockStatus = = 93 ) { // minutes
colorHelper ( digitPositions [ 0 ] , 96 , 255 , brightness ) ;
colorHelper ( digitPositions [ 1 ] , 96 , 255 , brightness ) ;
colorHelper ( digitPositions [ 2 ] , 64 , 255 , b ) ;
colorHelper ( digitPositions [ 3 ] , 64 , 255 , b ) ;
if ( LED_DIGITS = = 6 ) {
colorHelper ( digitPositions [ 4 ] , 0 , 255 , brightness ) ;
colorHelper ( digitPositions [ 5 ] , 0 , 255 , brightness ) ;
}
}
if ( clockStatus = = 94 ) { // seconds
colorHelper ( digitPositions [ 0 ] , 96 , 255 , brightness ) ;
colorHelper ( digitPositions [ 1 ] , 96 , 255 , brightness ) ;
colorHelper ( digitPositions [ 2 ] , 96 , 255 , brightness ) ;
colorHelper ( digitPositions [ 3 ] , 96 , 255 , brightness ) ;
if ( LED_DIGITS = = 6 ) {
colorHelper ( digitPositions [ 4 ] , 64 , 255 , b ) ;
colorHelper ( digitPositions [ 5 ] , 64 , 255 , b ) ;
}
}
}
}
/* The dots will always be colored in the same way, just using colors from the current palette. Depending on setup/parameters
this can otherwise lead to the dots looking quite different from the digits , so as before they ' re cycling through the
color palette once per minute */
if ( leds [ pgm_read_word_near ( & upperDots [ 0 ] ) ] ) { // if the first led inside the array upperDot is lit...
for ( uint8_t i = 0 ; i < ( sizeof ( upperDots ) / sizeof ( upperDots [ 0 ] ) ) ; i + + ) { // ...start applying colors to all leds inside the array
if ( clockStatus = = 0 ) {
leds [ pgm_read_word_near ( & upperDots [ i ] ) ] = ColorFromPalette ( currentPalette , second ( ) * 4.25 , brightness , LINEARBLEND ) ;
} else {
leds [ pgm_read_word_near ( & upperDots [ i ] ) ] . setHSV ( 64 , 255 , brightness ) ;
}
}
}
if ( leds [ pgm_read_word_near ( & lowerDots [ 0 ] ) ] ) { // same as before for the lower dots...
for ( uint8_t i = ( sizeof ( lowerDots ) / sizeof ( lowerDots [ 0 ] ) ) ; i > 0 ; i - - ) {
if ( clockStatus = = 0 ) {
leds [ pgm_read_word_near ( & lowerDots [ i - 1 ] ) ] = ColorFromPalette ( currentPalette , second ( ) * 4.25 , brightness , LINEARBLEND ) ;
} else {
leds [ pgm_read_word_near ( & lowerDots [ i - 1 ] ) ] . setHSV ( 64 , 255 , brightness ) ;
}
}
}
2022-08-25 05:30:54 +02:00
# ifdef FASTFORWARD
if ( millis ( ) - lastColorChange > 15 ) {
# else
if ( millis ( ) - lastColorChange > colorSpeed ) {
# endif
2022-08-25 05:23:48 +02:00
if ( reverseColorCycling ) {
startColor - - ;
} else {
startColor + + ;
}
lastColorChange = millis ( ) ;
}
2022-08-25 05:30:54 +02:00
# ifdef AUTOBRIGHTNESS
2022-08-25 05:23:48 +02:00
if ( nightMode & & clockStatus = = 0 ) { // nightmode will overwrite everything that has happened so far...
for ( uint16_t i = 0 ; i < LED_COUNT ; i + + ) {
if ( leds [ i ] ) {
if ( avgLDR = = minBrightness ) {
leds [ i ] . setHSV ( nightColor [ 0 ] , 255 , nightColor [ 1 ] ) ; // and assign nightColor to all lit leds. Default is a very dark red.
FastLED . setDither ( 0 ) ;
} else {
FastLED . setDither ( 1 ) ;
}
}
}
}
2022-08-25 05:30:54 +02:00
# endif
2022-08-25 05:23:48 +02:00
2022-08-25 05:30:54 +02:00
/* // example for time based coloring
// for coloring based on current times the following will get local display time into
// checkTime if autoDST is defined as the clock is running in utc time then
# ifdef AUTODST
time_t checkTime = myTimeZone . toLocal ( now ( ) ) ;
# else
time_t checkTime = now ( ) ;
# endif
2022-08-25 05:23:48 +02:00
2022-08-25 05:30:54 +02:00
// below if-loop simply checks for a given time and colors everything in green/blue accordingly
if ( hour ( checkTime ) > 6 & & hour ( checkTime ) < = 22 ) { // if hour > 6 AND hour <= 22 ---> 07:00 - 22:59
for ( uint16_t i = 0 ; i < LED_COUNT ; i + + ) { // for each position...
if ( leds [ i ] ) { // ...check led and if it's lit...
leds [ i ] . setHSV ( 96 , 255 , brightness ) ; // ...redraw with HSV color 96 -> green
2022-08-25 05:23:48 +02:00
}
2022-08-25 05:30:54 +02:00
}
} else { // ---> 23:00 - 06:59
for ( uint16_t i = 0 ; i < LED_COUNT ; i + + ) { // for each position...
if ( leds [ i ] ) { // ...check led and if it's lit...
leds [ i ] . setHSV ( 160 , 255 , brightness ) ; // ...redraw with HSV color 160 -> blue
2022-08-25 05:23:48 +02:00
}
}
2022-08-25 05:30:54 +02:00
}
*/
2022-08-25 05:23:48 +02:00
lastRun = millis ( ) ;
}
void colorizeSegment ( uint8_t segment , uint8_t pos , uint8_t color ) {
2022-08-25 05:30:54 +02:00
/* Checks if segment at position is on - and if it is, assigns color from current palette */
2022-08-25 05:23:48 +02:00
uint8_t ledAM = digitsLAM [ pos ] ; // led access mode according to the position
if ( leds [ pgm_read_word_near ( & segGroups [ segment + digitPositions [ pos ] * 7 ] [ 0 ] ) ] ) {
if ( ledAM = = 0 ) {
for ( uint8_t i = 0 ; i < 2 ; i + + ) {
leds [ pgm_read_word_near ( & segGroups [ segment + digitPositions [ pos ] * 7 ] [ i ] ) ] = ColorFromPalette ( currentPalette , color , brightness , LINEARBLEND ) ;
}
}
if ( ledAM = = 1 ) {
uint16_t startLed = pgm_read_word_near ( & segGroups [ segment + digitPositions [ pos ] * 7 ] [ 0 ] ) ;
uint16_t endLed = pgm_read_word_near ( & segGroups [ segment + digitPositions [ pos ] * 7 ] [ 1 ] ) ;
for ( uint16_t i = startLed ; i < = endLed ; i + + ) {
leds [ i ] = ColorFromPalette ( currentPalette , color , brightness , LINEARBLEND ) ;
}
}
}
}
void colorHelper ( uint8_t pos , uint8_t hue , uint8_t sat , uint8_t bri ) {
2022-08-25 05:30:54 +02:00
/* Used for coloring digits inside setup routines/steps
It will simply set the digit at the given position to the given hsv values */
2022-08-25 05:23:48 +02:00
uint8_t ledAM = digitsLAM [ pos ] ; // led access mode according to the position
for ( uint8_t segment = 0 ; segment < 7 ; segment + + ) {
if ( leds [ pgm_read_word_near ( & segGroups [ segment + pos * 7 ] [ 0 ] ) ] ) { // if first led inside segment is lit...
if ( ledAM = = 0 ) {
for ( uint8_t i = 0 ; i < 2 ; i + + ) { // assign hue to led 0 + 1 inside segment
leds [ pgm_read_word_near ( & segGroups [ segment + pos * 7 ] [ i ] ) ] . setHSV ( hue , sat , bri ) ;
}
}
if ( ledAM = = 1 ) {
uint16_t startLed = pgm_read_word_near ( & segGroups [ segment + pos * 7 ] [ 0 ] ) ;
uint16_t endLed = pgm_read_word_near ( & segGroups [ segment + pos * 7 ] [ 1 ] ) ;
for ( uint16_t i = startLed ; i < = endLed ; i + + ) { // assign hue to led 0 - 1 inside segment
leds [ i ] . setHSV ( hue , sat , bri ) ;
}
}
}
}
}
void displayTime ( time_t t ) {
2022-08-25 05:30:54 +02:00
# ifdef AUTODST
if ( clockStatus < 90 ) { // display adjusted times only while NOT in setup
t = myTimeZone . toLocal ( t ) ; // convert display time to local time zone according to rules on top of the sketch
}
# endif
2022-08-25 05:23:48 +02:00
if ( clockStatus > = 90 ) {
FastLED . clear ( ) ;
}
/* hours */
if ( displayMode = = 0 ) {
if ( hour ( t ) < 10 ) {
if ( leadingZero ) {
showDigit ( 0 , digitPositions [ 0 ] ) ;
}
} else {
showDigit ( hour ( t ) / 10 , digitPositions [ 0 ] ) ;
}
showDigit ( hour ( t ) % 10 , digitPositions [ 1 ] ) ;
} else if ( displayMode = = 1 ) {
if ( hourFormat12 ( t ) < 10 ) {
if ( leadingZero ) {
showDigit ( 0 , digitPositions [ 0 ] ) ;
}
} else {
showDigit ( hourFormat12 ( t ) / 10 , digitPositions [ 0 ] ) ;
}
showDigit ( hourFormat12 ( t ) % 10 , digitPositions [ 1 ] ) ;
}
/* minutes */
showDigit ( minute ( t ) / 10 , digitPositions [ 2 ] ) ;
showDigit ( minute ( t ) % 10 , digitPositions [ 3 ] ) ;
if ( LED_DIGITS = = 6 ) {
/* seconds */
showDigit ( second ( t ) / 10 , digitPositions [ 4 ] ) ;
showDigit ( second ( t ) % 10 , digitPositions [ 5 ] ) ;
}
if ( clockStatus > = 90 ) { // in setup modes displayTime will also use colorizeOutput/FastLED.show!
static unsigned long lastRefresh = millis ( ) ;
if ( isAM ( t ) & & displayMode = = 1 ) { // in 12h mode and if it's AM only light up the upper dots (while setting time)
showDots ( 1 ) ;
} else {
showDots ( 2 ) ;
}
if ( millis ( ) - lastRefresh > = 25 ) {
colorizeOutput ( colorMode ) ;
FastLED . show ( ) ;
lastRefresh = millis ( ) ;
}
return ;
}
/* dots */
if ( dotsBlinking ) {
if ( second ( t ) % 2 = = 0 ) {
showDots ( 2 ) ;
}
} else {
showDots ( 2 ) ;
}
}
void showSegment ( uint8_t segment , uint8_t segDisplay ) {
// This shows the segments from top of the sketch on a given position (segDisplay). Order of positions/segDisplay is the order
// of definitions on the top, first one defined is segDisplay 0, second one is segDisplay 1 and so on...
// "firstLoop" is used to display all information only once per test if customHelper is defined
uint8_t ledAM = digitsLAM [ segDisplay ] ; // led access mode according to the position
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
# ifdef CUSTOMHELPER
if ( firstLoop ) {
Serial . print ( F ( " LED_ACCESS_MODE for segment " ) ) ; Serial . print ( segment ) ;
Serial . print ( F ( " at position " ) ) ; Serial . print ( segDisplay ) ;
Serial . print ( F ( " is " ) ) ; Serial . print ( ledAM ) ;
}
# endif
# endif
2022-08-25 05:23:48 +02:00
if ( ledAM = = 0 ) { // using both values inside the array to light up two leds
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
# ifdef CUSTOMHELPER
if ( firstLoop ) {
Serial . print ( F ( " . Leds " ) ) ;
}
# endif
# endif
2022-08-25 05:23:48 +02:00
segment + = segDisplay * 7 ;
for ( uint8_t i = 0 ; i < 2 ; i + + ) {
leds [ pgm_read_word_near ( & segGroups [ segment ] [ i ] ) ] . setHSV ( markerHSV [ 0 ] , markerHSV [ 1 ] , markerHSV [ 2 ] ) ;
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
# ifdef CUSTOMHELPER
if ( firstLoop ) {
if ( i = = 0 ) {
Serial . print ( pgm_read_word_near ( & segGroups [ segment ] [ i ] ) ) ; Serial . print ( F ( " and " ) ) ;
}
if ( i = = 1 ) {
Serial . println ( pgm_read_word_near ( & segGroups [ segment ] [ i ] ) ) ;
}
}
# endif
# endif
2022-08-25 05:23:48 +02:00
}
}
if ( ledAM = = 1 ) { // using both values inside the array as start and end to light up multiple leds
segment + = segDisplay * 7 ;
uint16_t startLed = pgm_read_word_near ( & segGroups [ segment ] [ 0 ] ) ;
uint16_t endLed = pgm_read_word_near ( & segGroups [ segment ] [ 1 ] ) ;
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
# ifdef CUSTOMHELPER
if ( firstLoop ) {
Serial . print ( F ( " . Leds " ) ) ; Serial . print ( startLed ) ;
Serial . print ( F ( " - " ) ) ; Serial . println ( endLed ) ;
}
# endif
# endif
2022-08-25 05:23:48 +02:00
for ( uint16_t i = startLed ; i < = endLed ; i + + ) {
leds [ i ] . setHSV ( markerHSV [ 0 ] , markerHSV [ 1 ] , markerHSV [ 2 ] ) ;
}
}
}
void showDots ( uint8_t dots ) {
// dots 0 = upper dots, dots 1 = lower dots, dots 2 = all dots (right/left/both on Lazy 7 - Quick Build Edition)
if ( dots = = 1 | | dots = = 2 ) {
for ( uint8_t i = 0 ; i < ( sizeof ( upperDots ) / sizeof ( upperDots [ 0 ] ) ) ; i + + ) {
leds [ pgm_read_word_near ( & upperDots [ i ] ) ] . setHSV ( markerHSV [ 0 ] , markerHSV [ 1 ] , markerHSV [ 2 ] ) ;
}
}
if ( dots = = 0 | | dots = = 2 ) {
for ( uint8_t i = 0 ; i < ( sizeof ( lowerDots ) / sizeof ( lowerDots [ 0 ] ) ) ; i + + ) {
leds [ pgm_read_word_near ( & lowerDots [ i ] ) ] . setHSV ( markerHSV [ 0 ] , markerHSV [ 1 ] , markerHSV [ 2 ] ) ;
}
}
}
void showDigit ( uint8_t digit , uint8_t pos ) {
// This draws numbers using the according segments as defined on top of the sketch (0 - 9) or symbols/characters (index 10+)
for ( uint8_t i = 0 ; i < 7 ; i + + ) {
if ( pgm_read_byte_near ( & digits [ digit ] [ i ] ) ! = 0 ) showSegment ( i , pos ) ;
}
}
2022-08-25 05:30:54 +02:00
void paletteSwitcher ( ) {
/* As the name suggests this takes care of switching palettes. When adding palettes, make sure paletteCount increases
accordingly . A few examples of gradients / solid colors by using RGB values or HTML Color Codes below */
static uint8_t paletteCount = 6 ;
2022-08-25 05:23:48 +02:00
static uint8_t currentIndex = 0 ;
if ( clockStatus = = 1 ) { // Clock is starting up, so load selected palette from eeprom...
uint8_t tmp = EEPROM . read ( 0 ) ;
if ( tmp > = 0 & & tmp < paletteCount ) {
currentIndex = tmp ; // 255 from eeprom would mean there's nothing been written yet, so checking range...
} else {
currentIndex = 0 ; // ...and default to 0 if returned value from eeprom is not 0 - 6
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " paletteSwitcher(): loaded EEPROM value " ) ) ;
Serial . println ( tmp ) ;
# endif
2022-08-25 05:23:48 +02:00
}
switch ( currentIndex ) {
case 0 : currentPalette = CRGBPalette16 ( CRGB ( 224 , 0 , 32 ) ,
2022-08-25 05:30:54 +02:00
CRGB ( 0 , 0 , 244 ) ,
CRGB ( 128 , 0 , 128 ) ,
CRGB ( 224 , 0 , 64 ) ) ; break ;
2022-08-25 05:23:48 +02:00
case 1 : currentPalette = CRGBPalette16 ( CRGB ( 224 , 16 , 0 ) ,
2022-08-25 05:30:54 +02:00
CRGB ( 192 , 64 , 0 ) ,
CRGB ( 192 , 128 , 0 ) ,
CRGB ( 240 , 40 , 0 ) ) ; break ;
2022-08-25 05:23:48 +02:00
case 2 : currentPalette = CRGBPalette16 ( CRGB : : Aquamarine ,
2022-08-25 05:30:54 +02:00
CRGB : : Turquoise ,
CRGB : : Blue ,
CRGB : : DeepSkyBlue ) ; break ;
2022-08-25 05:23:48 +02:00
case 3 : currentPalette = RainbowColors_p ; break ;
case 4 : currentPalette = PartyColors_p ; break ;
case 5 : currentPalette = CRGBPalette16 ( CRGB : : LawnGreen ) ; break ;
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " paletteSwitcher(): selected palette " ) ) ;
Serial . println ( currentIndex ) ;
# endif
2022-08-25 05:23:48 +02:00
if ( clockStatus = = 0 ) { // only save selected palette to eeprom if clock is in normal running mode, not while in startup/setup/whatever
EEPROM . put ( 0 , currentIndex ) ;
2022-08-25 05:30:54 +02:00
# ifdef NODEMCU
EEPROM . commit ( ) ;
# endif
# ifdef DEBUG
Serial . print ( F ( " paletteSwitcher(): saved index " ) ) ;
Serial . print ( currentIndex ) ;
Serial . println ( F ( " to eeprom " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
if ( currentIndex < paletteCount - 1 ) {
2022-08-25 05:30:54 +02:00
currentIndex + + ;
2022-08-25 05:23:48 +02:00
} else {
currentIndex = 0 ;
}
if ( colorPreview ) {
previewMode ( ) ;
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . println ( F ( " paletteSwitcher() done " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
void brightnessSwitcher ( ) {
static uint8_t currentIndex = 0 ;
if ( clockStatus = = 1 ) { // Clock is starting up, so load selected palette from eeprom...
uint8_t tmp = EEPROM . read ( 1 ) ;
if ( tmp > = 0 & & tmp < 3 ) {
currentIndex = tmp ; // 255 from eeprom would mean there's nothing been written yet, so checking range...
} else {
currentIndex = 0 ; // ...and default to 0 if returned value from eeprom is not 0 - 2
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " brightnessSwitcher(): loaded EEPROM value " ) ) ;
Serial . println ( tmp ) ;
# endif
2022-08-25 05:23:48 +02:00
}
switch ( currentIndex ) {
case 0 : brightness = brightnessLevels [ currentIndex ] ; break ;
case 1 : brightness = brightnessLevels [ currentIndex ] ; break ;
case 2 : brightness = brightnessLevels [ currentIndex ] ; break ;
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " brightnessSwitcher(): selected brightness index " ) ) ;
Serial . println ( currentIndex ) ;
# endif
2022-08-25 05:23:48 +02:00
if ( clockStatus = = 0 ) { // only save selected brightness to eeprom if clock is in normal running mode, not while in startup/setup/whatever
EEPROM . put ( 1 , currentIndex ) ;
2022-08-25 05:30:54 +02:00
# ifdef NODEMCU
EEPROM . commit ( ) ;
# endif
# ifdef DEBUG
Serial . print ( F ( " brightnessSwitcher(): saved index " ) ) ;
Serial . print ( currentIndex ) ;
Serial . println ( F ( " to eeprom " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
if ( currentIndex < 2 ) {
2022-08-25 05:30:54 +02:00
currentIndex + + ;
2022-08-25 05:23:48 +02:00
} else {
currentIndex = 0 ;
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG {
Serial . println ( F ( " brightnessSwitcher() done " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
void colorModeSwitcher ( ) {
static uint8_t currentIndex = 0 ;
if ( clockStatus = = 1 ) { // Clock is starting up, so load selected palette from eeprom...
if ( colorMode ! = 0 ) return ; // 0 is default, if it's different on startup the config is set differently, so exit here
uint8_t tmp = EEPROM . read ( 3 ) ;
if ( tmp > = 0 & & tmp < 4 ) { // make sure tmp < 3 is increased if color modes are added in colorizeOutput()!
currentIndex = tmp ; // 255 from eeprom would mean there's nothing been written yet, so checking range...
} else {
currentIndex = 0 ; // ...and default to 0 if returned value from eeprom is not 0 - 2
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " colorModeSwitcher(): loaded EEPROM value " ) ) ;
Serial . println ( tmp ) ;
# endif
2022-08-25 05:23:48 +02:00
}
colorMode = currentIndex ;
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " colorModeSwitcher(): selected colorMode " ) ) ;
Serial . println ( currentIndex ) ;
# endif
2022-08-25 05:23:48 +02:00
if ( clockStatus = = 0 ) { // only save selected colorMode to eeprom if clock is in normal running mode, not while in startup/setup/whatever
EEPROM . put ( 3 , currentIndex ) ;
2022-08-25 05:30:54 +02:00
# ifdef NODEMCU
EEPROM . commit ( ) ;
# endif
# ifdef DEBUG
Serial . print ( F ( " colorModeSwitcher(): saved index " ) ) ;
Serial . print ( currentIndex ) ;
Serial . println ( F ( " to eeprom " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
if ( currentIndex < 3 ) {
2022-08-25 05:30:54 +02:00
currentIndex + + ;
2022-08-25 05:23:48 +02:00
} else {
currentIndex = 0 ;
}
if ( colorPreview ) {
previewMode ( ) ;
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG {
Serial . println ( F ( " colorModeSwitcher() done " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
void displayModeSwitcher ( ) {
static uint8_t currentIndex = 0 ;
if ( clockStatus = = 1 ) { // Clock is starting up, so load selected palette from eeprom...
if ( displayMode ! = 0 ) return ; // 0 is default, if it's different on startup the config is set differently, so exit here
uint8_t tmp = EEPROM . read ( 2 ) ;
if ( tmp > = 0 & & tmp < 2 ) { // make sure tmp < 2 is increased if display modes are added
currentIndex = tmp ; // 255 from eeprom would mean there's nothing been written yet, so checking range...
} else {
currentIndex = 0 ; // ...and default to 0 if returned value from eeprom is not 0 - 1 (24h/12h mode)
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " displayModeSwitcher(): loaded EEPROM value " ) ) ;
Serial . println ( tmp ) ;
# endif
2022-08-25 05:23:48 +02:00
}
displayMode = currentIndex ;
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . print ( F ( " displayModeSwitcher(): selected displayMode " ) ) ;
Serial . println ( currentIndex ) ;
# endif
2022-08-25 05:23:48 +02:00
if ( clockStatus = = 0 ) { // only save selected colorMode to eeprom if clock is in normal running mode, not while in startup/setup/whatever
EEPROM . put ( 2 , currentIndex ) ;
2022-08-25 05:30:54 +02:00
# ifdef NODEMCU
EEPROM . commit ( ) ;
# endif
# ifdef DEBUG
Serial . print ( F ( " displayModeSwitcher(): saved index " ) ) ;
Serial . print ( currentIndex ) ;
Serial . println ( F ( " to eeprom " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
if ( clockStatus = = 0 ) { // show 12h/24h for 2 seconds after selected in normal run mode, don't show this on startup (status 1)
FastLED . clear ( ) ;
unsigned long timer = millis ( ) ;
while ( millis ( ) - timer < = 2000 ) {
if ( currentIndex = = 0 ) {
showDigit ( 2 , digitPositions [ 0 ] ) ;
showDigit ( 4 , digitPositions [ 1 ] ) ;
showDigit ( 19 , digitPositions [ 3 ] ) ;
}
if ( currentIndex = = 1 ) {
showDigit ( 1 , digitPositions [ 0 ] ) ;
showDigit ( 2 , digitPositions [ 1 ] ) ;
showDigit ( 19 , digitPositions [ 3 ] ) ;
}
colorizeOutput ( colorMode ) ;
if ( millis ( ) % 50 = = 0 ) {
FastLED . show ( ) ;
}
2022-08-25 05:30:54 +02:00
# ifdef NODEMCU
yield ( ) ;
# endif
2022-08-25 05:23:48 +02:00
}
}
if ( currentIndex < 1 ) {
2022-08-25 05:30:54 +02:00
currentIndex + + ;
2022-08-25 05:23:48 +02:00
} else {
currentIndex = 0 ;
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG {
Serial . println ( F ( " displayModeSwitcher() done " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
void previewMode ( ) {
2022-08-25 05:30:54 +02:00
/* This will simply display "8" on all positions, speed up the color cyling and preview the
selected palette or colorMode */
2022-08-25 05:23:48 +02:00
if ( clockStatus = = 1 ) return ; // don't preview when starting up
unsigned long previewStart = millis ( ) ;
uint16_t colorSpeedBak = colorSpeed ;
colorSpeed = 5 ;
while ( millis ( ) - previewStart < = uint16_t ( colorPreviewDuration * 1000L ) ) {
2022-08-25 05:30:54 +02:00
for ( uint8_t i = 0 ; i < LED_DIGITS ; i + + ) {
2022-08-25 05:23:48 +02:00
showDigit ( 8 , i ) ;
}
colorizeOutput ( colorMode ) ;
FastLED . show ( ) ;
2022-08-25 05:30:54 +02:00
# ifdef NODEMCU
yield ( ) ;
# endif
2022-08-25 05:23:48 +02:00
}
colorSpeed = colorSpeedBak ;
FastLED . clear ( ) ;
}
# endif /* LEDSTUFF */
bool leapYear ( uint16_t y ) {
boolean isLeapYear = false ;
if ( y % 4 = = 0 ) isLeapYear = true ;
if ( y % 100 = = 0 & & y % 400 ! = 0 ) isLeapYear = false ;
if ( y % 400 = = 0 ) isLeapYear = true ;
if ( isLeapYear ) return true ; else return false ;
}
uint8_t inputButtons ( ) {
/* This scans for button presses and keeps track of delay/repeat for user inputs
Short keypresses will only be returned when buttons are released before repeatDelay
is reached . This is to avoid constantly sending 1 or 2 when executing a long button
press and / or multiple buttons .
Note : Buttons are using pinMode INPUT_PULLUP , so HIGH = not pressed , LOW = pressed ! */
static uint8_t scanInterval = 30 ; // only check buttons every 30ms
static uint16_t repeatDelay = 1000 ; // delay in milliseconds before repeating detected keypresses
static uint8_t repeatRate = 1000 / 10 ; // 10 chars per 1000 milliseconds
static uint8_t minTime = scanInterval * 2 ; // minimum time to register a button as pressed
static unsigned long lastReadout = millis ( ) ; // keeps track of when the last readout happened
static unsigned long lastReturn = millis ( ) ; // keeps track of when the last readout value was returned
static uint8_t lastState = 0 ; // button state from previous scan
uint8_t currentState = 0 ; // button state from current scan
uint8_t retVal = 0 ; // return value, will be 0 if no button is pressed
static unsigned long eventStart = millis ( ) ; // keep track of when button states are changing
if ( millis ( ) - lastReadout < scanInterval ) return 0 ; // only scan for button presses every <scanInterval> ms
if ( digitalRead ( buttonA ) = = LOW ) currentState + = 1 ;
if ( digitalRead ( buttonB ) = = LOW ) currentState + = 2 ;
if ( currentState = = 0 & & currentState = = lastState ) {
btnRepeatCounter = 0 ;
}
if ( currentState ! = 0 & & currentState ! = lastState ) { // if any button is pressed and different from the previous scan...
eventStart = millis ( ) ; // ...reset eventStart to current time
btnRepeatCounter = 0 ; // ...and reset global variable btnRepeatCounter
}
if ( currentState ! = 0 & & currentState = = lastState ) { // if same input has been detected at least twice (2x scanInterval)...
if ( millis ( ) - eventStart > = repeatDelay ) { // ...and longer than repeatDelay...
if ( millis ( ) - lastReturn > = repeatRate ) { // ...check for repeatRate...
retVal = currentState ; // ...and set retVal to currentState
btnRepeatCounter + + ;
lastReturn = millis ( ) ;
} else retVal = 0 ; // return 0 if repeatDelay hasn't been reached yet
}
}
if ( currentState = = 0 & & currentState ! = lastState & & millis ( ) - eventStart > = minTime & & btnRepeatCounter = = 0 ) {
retVal = lastState ; // return lastState if all buttons are released after having been pressed for <minTime> ms
btnRepeatCounter = 0 ;
}
lastState = currentState ;
lastReadout = millis ( ) ;
2022-08-25 05:30:54 +02:00
# ifdef DEBUG // output some information and read serial input, if available
uint8_t serialInput = dbgInput ( ) ;
if ( serialInput ! = 0 ) {
Serial . print ( F ( " inputButtons(): Serial input detected: " ) ) ; Serial . println ( serialInput ) ;
retVal = serialInput ;
}
if ( retVal ! = 0 ) {
Serial . print ( F ( " inputButtons(): Return value is: " ) ) ; Serial . print ( retVal ) ; Serial . print ( F ( " - btnRepeatCounter is: " ) ) ; Serial . println ( btnRepeatCounter ) ;
}
# endif
2022-08-25 05:23:48 +02:00
return retVal ;
}
// following will only be included if USENTP is defined
# ifdef USENTP
/* This syncs system time to the RTC at startup and will periodically do other sync related
things , like syncing rtc to ntp time */
2022-08-25 05:30:54 +02:00
void syncHelper ( ) {
static unsigned long lastSync = millis ( ) ; // keeps track of the last time a sync attempt has been made
if ( millis ( ) - lastSync < 60000 & & clockStatus ! = 1 ) return ; // only allow one ntp request per minute
if ( WiFi . status ( ) ! = WL_CONNECTED ) {
# ifdef DEBUG
Serial . println ( F ( " syncHelper(): No WiFi connection " ) ) ;
return ;
# endif
}
# ifndef USERTC
# ifndef USENTP
# ifdef DEBUG
Serial . println ( F ( " syncHelper(): No RTC and no NTP configured, nothing to do... " ) ) ;
return ;
# endif
# endif
# endif
time_t ntpTime = 0 ;
# ifdef USERTC
RtcDateTime ntpTimeConverted = ntpTime ;
# endif
if ( clockStatus = = 1 ) { // looks like the sketch has just started running...
# ifdef DEBUG
Serial . println ( F ( " syncHelper(): Initial sync on power up... " ) ) ;
# endif
ntpTime = getTimeNTP ( ) ;
# ifdef DEBUG
Serial . print ( F ( " syncHelper(): NTP result is " ) ) ;
Serial . println ( ntpTime ) ;
# endif
lastSync = millis ( ) ;
} else {
# ifdef DEBUG
Serial . println ( F ( " syncHelper(): Resyncing to NTP... " ) ) ;
# endif
ntpTime = getTimeNTP ( ) ;
# ifdef DEBUG
Serial . print ( F ( " syncHelper(): NTP result is " ) ) ;
Serial . println ( ntpTime ) ;
# endif
lastSync = millis ( ) ;
}
# ifdef USERTC
ntpTimeConverted = { year ( ntpTime ) , month ( ntpTime ) , day ( ntpTime ) ,
hour ( ntpTime ) , minute ( ntpTime ) , second ( ntpTime ) } ;
RtcDateTime rtcTime = Rtc . GetDateTime ( ) ; // get current time from the rtc....
# ifdef DEBUG
if ( ntpTime > 100 ) {
Rtc . SetDateTime ( ntpTimeConverted ) ;
}
# endif
# else
time_t sysTime = now ( ) ; // ...or from system
# ifdef DEBUG
Serial . println ( F ( " syncHelper(): No RTC configured, using system time " ) ) ;
Serial . print ( F ( " syncHelper(): sysTime was " ) ) ;
Serial . println ( now ( ) ) ;
# endif
if ( ntpTime > 100 ) {
setTime ( ntpTime ) ;
}
# endif
# ifdef DEBUG
Serial . println ( F ( " syncHelper() done " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
2022-08-25 05:30:54 +02:00
time_t getTimeNTP ( ) {
unsigned long startTime = millis ( ) ;
time_t timeNTP ;
if ( WiFi . status ( ) ! = WL_CONNECTED ) {
# ifdef DEBUG
Serial . print ( F ( " getTimeNTP(): Not connected, WiFi.status is " ) ) ;
Serial . println ( WiFi . status ( ) ) ;
# endif
} // Sometimes the connection doesn't work right away although status is WL_CONNECTED...
while ( millis ( ) - startTime < 2000 ) { // ...so we'll wait a moment before causing network traffic
# ifdef NODEMCU
yield ( ) ;
# endif
}
timeClient . update ( ) ;
timeNTP = timeClient . getEpochTime ( ) ;
if ( timeNTP < 100 ) {
# ifdef DEBUG
Serial . print ( F ( " getTimeNTP(): NTP returned " ) ) ; Serial . println ( timeNTP ) ;
Serial . print ( F ( " - trying again... " ) ) ;
# endif
}
timeClient . update ( ) ;
timeNTP = timeClient . getEpochTime ( ) ;
if ( timeNTP < 100 ) {
# ifdef DEBUG
Serial . print ( F ( " getTimeNTP(): NTP returned " ) ) ; Serial . println ( timeNTP ) ;
Serial . print ( F ( " - giving up " ) ) ;
# endif
}
# ifdef DEBUG
Serial . println ( F ( " getTimeNTP() done " ) ) ;
# endif
return timeNTP ;
2022-08-25 05:23:48 +02:00
}
# endif
// ---
// functions below will only be included if DEBUG is defined on top of the sketch
# ifdef DEBUG
2022-08-25 05:30:54 +02:00
void printTime ( ) {
/* outputs current system and RTC time to the serial monitor, adds autoDST if defined */
time_t tmp = now ( ) ;
# ifdef USERTC
RtcDateTime tmp2 = Rtc . GetDateTime ( ) . Epoch32Time ( ) ;
setTime ( tmp2 ) ;
tmp = now ( ) ;
# endif
Serial . println ( F ( " ----------------------------------- " ) ) ;
Serial . print ( F ( " System time is : " ) ) ;
if ( hour ( tmp ) < 10 ) Serial . print ( F ( " 0 " ) ) ;
Serial . print ( hour ( tmp ) ) ; Serial . print ( F ( " : " ) ) ;
if ( minute ( tmp ) < 10 ) Serial . print ( F ( " 0 " ) ) ;
Serial . print ( minute ( tmp ) ) ; Serial . print ( F ( " : " ) ) ;
if ( second ( tmp ) < 10 ) Serial . print ( F ( " 0 " ) ) ;
Serial . println ( second ( tmp ) ) ;
Serial . print ( F ( " System date is : " ) ) ;
Serial . print ( year ( tmp ) ) ; Serial . print ( " - " ) ;
Serial . print ( month ( tmp ) ) ; Serial . print ( " - " ) ;
Serial . print ( day ( tmp ) ) ; Serial . println ( F ( " (Y/M/D) " ) ) ;
# ifdef USERTC
Serial . print ( F ( " RTC time is : " ) ) ;
if ( hour ( tmp2 ) < 10 ) Serial . print ( F ( " 0 " ) ) ;
Serial . print ( hour ( tmp2 ) ) ; Serial . print ( F ( " : " ) ) ;
if ( minute ( tmp2 ) < 10 ) Serial . print ( F ( " 0 " ) ) ;
Serial . print ( minute ( tmp2 ) ) ; Serial . print ( F ( " : " ) ) ;
if ( second ( tmp2 ) < 10 ) Serial . print ( F ( " 0 " ) ) ;
Serial . println ( second ( tmp2 ) ) ;
Serial . print ( F ( " RTC date is : " ) ) ;
Serial . print ( year ( tmp2 ) ) ; Serial . print ( " - " ) ;
Serial . print ( month ( tmp2 ) ) ; Serial . print ( " - " ) ;
Serial . print ( day ( tmp2 ) ) ; Serial . println ( F ( " (Y/M/D) " ) ) ;
# endif
# ifdef AUTODST
tmp = myTimeZone . toLocal ( tmp ) ;
Serial . print ( F ( " autoDST time is: " ) ) ;
if ( hour ( tmp ) < 10 ) Serial . print ( F ( " 0 " ) ) ;
Serial . print ( hour ( tmp ) ) ; Serial . print ( F ( " : " ) ) ;
if ( minute ( tmp ) < 10 ) Serial . print ( F ( " 0 " ) ) ;
Serial . print ( minute ( tmp ) ) ; Serial . print ( F ( " : " ) ) ;
if ( second ( tmp ) < 10 ) Serial . print ( F ( " 0 " ) ) ;
Serial . println ( second ( tmp ) ) ;
Serial . print ( F ( " autoDST date is: " ) ) ;
Serial . print ( year ( tmp ) ) ; Serial . print ( " - " ) ;
Serial . print ( month ( tmp ) ) ; Serial . print ( " - " ) ;
Serial . print ( day ( tmp ) ) ; Serial . println ( F ( " (Y/M/D) " ) ) ;
# endif
Serial . println ( F ( " ----------------------------------- " ) ) ;
}
uint8_t dbgInput ( ) {
/* this catches input from the serial console and hands it over to inputButtons() if DEBUG is defined
Serial input " 7 " matches buttonA , " 8 " matches buttonB , " 9 " matches buttonA + buttonB */
if ( Serial . available ( ) > 0 ) {
uint8_t incomingByte = 0 ;
incomingByte = Serial . read ( ) ;
if ( incomingByte = = 52 ) { // 4 - long press buttonA
btnRepeatCounter = 10 ;
return 1 ;
}
if ( incomingByte = = 53 ) { // 5 - long press buttonB
btnRepeatCounter = 10 ;
return 2 ;
}
if ( incomingByte = = 54 ) { // 6 - long press buttonA + buttonB
btnRepeatCounter = 10 ;
return 3 ;
}
if ( incomingByte = = 55 ) return 1 ; // 7 - buttonA
if ( incomingByte = = 56 ) return 2 ; // 8 - buttonB
if ( incomingByte = = 57 ) return 3 ; // 9 - buttonA + buttonB
2022-08-25 05:23:48 +02:00
}
2022-08-25 05:30:54 +02:00
return 0 ;
2022-08-25 05:23:48 +02:00
}
2022-08-25 05:30:54 +02:00
# endif
// ---
2022-08-25 05:23:48 +02:00
# ifdef USEWIFI
2022-08-25 05:30:54 +02:00
void connectWPS ( ) { // join network using wps. Will try for 3 times before exiting...
# ifdef DEBUG
Serial . println ( F ( " connectWPS(): Initializing WPS setup... " ) ) ;
# endif
uint8_t counter = 1 ;
static unsigned long startTimer = millis ( ) ;
# ifdef LEDSTUFF
2022-08-25 05:23:48 +02:00
FastLED . clear ( ) ;
showDigit ( 10 , digitPositions [ 0 ] ) ;
showDigit ( 11 , digitPositions [ 1 ] ) ;
showDigit ( 12 , digitPositions [ 2 ] ) ;
showDigit ( counter , digitPositions [ 3 ] ) ;
colorizeOutput ( colorMode ) ;
FastLED . show ( ) ;
2022-08-25 05:30:54 +02:00
# endif
while ( counter < 4 ) {
# ifdef LEDSTUFF
if ( millis ( ) % 50 = = 0 ) {
FastLED . clear ( ) ;
showDigit ( 10 , digitPositions [ 0 ] ) ;
showDigit ( 11 , digitPositions [ 1 ] ) ;
showDigit ( 12 , digitPositions [ 2 ] ) ;
showDigit ( counter , digitPositions [ 3 ] ) ;
colorizeOutput ( colorMode ) ;
FastLED . show ( ) ;
}
# endif
if ( millis ( ) - startTimer > 300 ) {
# ifdef DEBUG
Serial . print ( F ( " connectWPS(): Waiting for WiFi/WPS, try " ) ) ;
Serial . println ( counter ) ;
# endif
WiFi . beginWPSConfig ( ) ;
if ( WiFi . SSID ( ) . length ( ) < = 0 ) counter + + ; else counter = 4 ;
startTimer = millis ( ) ;
}
# ifdef NODEMCU
yield ( ) ;
# endif
2022-08-25 05:23:48 +02:00
}
FastLED . clear ( ) ;
2022-08-25 05:30:54 +02:00
startTimer = millis ( ) ;
if ( WiFi . SSID ( ) . length ( ) > 0 ) {
# ifdef LEDSTUFF
FastLED . clear ( ) ;
showDigit ( 5 , digitPositions [ 0 ] ) ;
showDigit ( 5 , digitPositions [ 1 ] ) ;
showDigit ( 1 , digitPositions [ 2 ] ) ;
showDigit ( 13 , digitPositions [ 3 ] ) ;
colorizeOutput ( colorMode ) ;
FastLED . show ( ) ;
# endif
# ifdef DEBUG
Serial . print ( F ( " connectWPS(): Connected to SSID: " ) ) ; Serial . println ( WiFi . SSID ( ) ) ;
# endif
while ( millis ( ) - startTimer < 2000 ) {
# ifdef NODEMCU
yield ( ) ;
# endif
}
# ifdef USENTP
clockStatus = 1 ;
syncHelper ( ) ;
clockStatus = 0 ;
# endif USENTP
} else {
# ifdef DEBUG
Serial . println ( F ( " connectWPS(): Failed, no WPS connection established " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . println ( F ( " connectWPS() done " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
}
# endif
# ifdef CUSTOMHELPER
/* This assists in troubleshooting and basic configuration. Testing all neccessary steps to get
showSegment ( ) , showDigit ( ) , showDots ( ) to work . Ugly and using delay ( ) but does the job ^ ^ */
2022-08-25 05:30:54 +02:00
void customHelper ( ) {
markerHSV [ 0 ] = 96 ;
markerHSV [ 1 ] = 255 ;
markerHSV [ 2 ] = 60 ;
colorModeSwitcher ( ) ;
paletteSwitcher ( ) ;
brightness = 50 ;
currentPalette = RainbowColors_p ;
uint8_t test = 1 ;
# ifdef DEBUG
Serial . println ( F ( " \n \n \n Some kind of troubleshooting/custom assistant... ^^ " ) ) ;
Serial . println ( F ( " \n Tests will finish before proceeding to the next one. \n " ) ) ;
Serial . print ( F ( " The first step is to check all leds, so this test will \n simply light up all the leds from 0 to " ) ) ; Serial . println ( LED_COUNT - 1 ) ;
Serial . println ( F ( " Press button A (or send 7 using serial input) to advance to the next step... \n " ) ) ;
# endif
while ( test = = 1 ) {
for ( uint16_t i = 0 ; i < LED_COUNT ; i + + ) {
leds [ i ] . setHSV ( markerHSV [ 0 ] , markerHSV [ 1 ] , markerHSV [ 2 ] ) ;
FastLED . show ( ) ;
delay ( 75 ) ;
if ( inputButtons ( ) ! = 0 ) test + + ;
}
FastLED . clear ( ) ;
delay ( 300 ) ;
2022-08-25 05:23:48 +02:00
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . println ( F ( " \n \n \n Next we will light up segments 0-6 (a-g) at position 0, this " ) ) ;
Serial . println ( F ( " will show if all of the leds inside segArray[][] for position 0 are correct. " ) ) ;
Serial . println ( F ( " Press button A (or send 7 using serial input) to advance to the next step... \n " ) ) ;
# endif
2022-08-25 05:23:48 +02:00
FastLED . clear ( ) ;
2022-08-25 05:30:54 +02:00
while ( test = = 2 ) {
2022-08-25 05:23:48 +02:00
for ( uint8_t i = 0 ; i < 7 ; i + + ) {
2022-08-25 05:30:54 +02:00
showSegment ( i , 0 ) ;
2022-08-25 05:23:48 +02:00
FastLED . show ( ) ;
2022-08-25 05:30:54 +02:00
delay ( 750 ) ;
2022-08-25 05:23:48 +02:00
if ( inputButtons ( ) ! = 0 ) test + + ;
}
2022-08-25 05:30:54 +02:00
FastLED . clear ( ) ;
FastLED . show ( ) ;
delay ( 300 ) ;
firstLoop = false ;
2022-08-25 05:23:48 +02:00
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . println ( F ( " \n \n Now let's check this for all the positions as defined (LED_DIGITS 4 or 6), starting from 0... " ) ) ;
Serial . println ( F ( " Press button A (or send 7 using serial input) to advance to the next step... \n " ) ) ;
# endif
firstLoop = true ;
while ( test = = 3 ) {
for ( uint8_t pos = 0 ; pos < LED_DIGITS ; pos + + ) {
for ( uint8_t i = 0 ; i < 7 ; i + + ) {
showSegment ( i , pos ) ;
FastLED . show ( ) ;
delay ( 400 ) ;
if ( inputButtons ( ) ! = 0 ) test + + ;
}
if ( firstLoop ) Serial . println ( ) ;
}
2022-08-25 05:23:48 +02:00
FastLED . clear ( ) ;
FastLED . show ( ) ;
2022-08-25 05:30:54 +02:00
delay ( 300 ) ;
firstLoop = false ;
2022-08-25 05:23:48 +02:00
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . println ( F ( " \n \n Testing showDigit() on position 0, displaying 0-9 " ) ) ;
Serial . println ( F ( " Press button A (or send 7 using serial input) to advance to the next step... \n " ) ) ;
# endif
while ( test = = 4 ) {
for ( uint8_t i = 0 ; i < 10 ; i + + ) {
FastLED . clear ( ) ;
showDigit ( i , 0 ) ;
if ( inputButtons ( ) ! = 0 ) test + + ;
FastLED . show ( ) ;
delay ( 500 ) ;
}
2022-08-25 05:23:48 +02:00
FastLED . clear ( ) ;
FastLED . show ( ) ;
delay ( 300 ) ;
}
2022-08-25 05:30:54 +02:00
# ifdef DEBUG
Serial . println ( F ( " \n \n Testing showDots() lighting up the upper/lower dots in a repeating pattern... " ) ) ;
Serial . println ( F ( " Press button A (or send 7 using serial input) to advance to the next step... \n " ) ) ;
# endif
while ( test = = 5 ) {
if ( second ( ) % 2 = = 1 ) {
showDots ( 0 ) ;
} else {
showDots ( 1 ) ;
2022-08-25 05:23:48 +02:00
}
if ( inputButtons ( ) ! = 0 ) test + + ;
FastLED . show ( ) ;
2022-08-25 05:30:54 +02:00
delay ( 20 ) ;
2022-08-25 05:23:48 +02:00
FastLED . clear ( ) ;
}
2022-08-25 05:30:54 +02:00
FastLED . clear ( ) ;
FastLED . show ( ) ;
delay ( 300 ) ;
# ifdef DEBUG
Serial . println ( F ( " \n \n Final test, displaying 0-9 on all positions, using colorizeOutput(); " ) ) ;
Serial . println ( F ( " Press button A (or send 7 using serial input) to start over... \n " ) ) ;
# endif
while ( test = = 6 ) {
for ( uint8_t i = 0 ; i < 10 ; i + + ) {
for ( uint8_t pos = 0 ; pos < LED_DIGITS ; pos + + ) {
showDigit ( i , pos ) ;
}
if ( inputButtons ( ) ! = 0 ) test + + ;
colorizeOutput ( 1 ) ;
FastLED . show ( ) ;
delay ( 500 ) ;
FastLED . clear ( ) ;
}
}
FastLED . clear ( ) ;
FastLED . show ( ) ;
delay ( 500 ) ;
2022-08-25 05:23:48 +02:00
}
# endif
/* Wooohaa... this one took a bit longer than expected... ^^ /daniel cikic - 07/2021 */