ok, thanks,
@ian Is there plans to put an led panel in the center of the clock, if there is i have either a dmd red led panel (32x64) or a 32x32 rgb panel that would run on a pi zero.
I can donate one to the cause.
Hi @crashman39,
All contributions welcome,
If you would like to add a date or something in the middle feel free to do so.
@eyal, I have updated the program, but note I havenât tested it yet. Code below.
#define FastLED
#if NeoPixel
# include <Adafruit_NeoPixel.h>
#elif FastLED
# include <FastLED.h>
# include "RTClib.h"
#endif
const int LedDataPin = 5;
const int LedCount = 180;
#if NeoPixel
Adafruit_NeoPixel leds = Adafruit_NeoPixel(180, LedDataPin, NEO_GRB + NEO_KHZ800);
#elif FastLED
static CRGB[] leds = new CRGB[LedCount];
#endif
static RTC_DS3231 rtc;
const float pulsePeriod = 6.0f;
const float MilliSecondDisplayWidth = 7.5f / LedCount;
const int MilliSecondRed = 16;
const int MilliSecondGreen = 16;
const int MilliSecondBlue = 16;
const float SecondDisplayWidth = 15.0f / LedCount;
const int SecondRed = 131;
const int SecondGreen = 215;
const int SecondBlue = 32;
const float SecondPulseOffset = pulsePeriod / 4.0f;
const float MinuteDisplayWidth = 30.0f / LedCount;
const int MinuteRed = 222;
const int MinuteGreen = 24;
const int MinuteBlue = 121;
const float MinutePulseOffset = pulsePeriod / 2.0f;
const float HourDisplayWidth = 45.0f / LedCount;
const int HourRed = 12;
const int HourGreen = 68;
const int HourBlue = 245;
const float HourPulseOffset = 3.0f * pulsePeriod / 4.0f;
int hour;
int minute;
int second;
int milliSecond;
long lastTime;
int red;
int green;
int blue;
int ledIndex;
void setup()
{
#if NeoPixel
leds.begin();
#elif FastLED
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, LedCount).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(255);
#endif
minute = 0;
second = 0;
}
void loop()
{
if (minute == 0 && second == 0)
{
ReadExternalTime();
}
ProgressInternalTime();
TrailingFadeHandDisplay();
#if NeoPixel
leds.show();
#elif FastLED
FastLED.show();
#endif
}
void ReadExternalTime()
{
DateTime previousTime = rtc.now();
uint8_t previousSecond = previousTime.second();
DateTime nextTime;
while (true)
{
nextTime = rtc.now();
uint8_t nextSecond = nextTime.second();
if (nextSecond != previousSecond)
{
break;
}
}
hour = nextTime.hour() % 12;
minute = nextTime.minute();
second = nextTime.second();
milliSecond = 0;
lastTime = millis();
}
void ProgressInternalTime()
{
long currentTime = millis();
int timeDifference = (int)(currentTime - lastTime);
lastTime = currentTime;
if (timeDifference > 100)
{
return;
}
milliSecond += timeDifference;
while (milliSecond >= 1000)
{
milliSecond -= 1000;
second++;
if (second >= 60)
{
second -= 60;
minute++;
if (minute >= 60)
{
minute -= 60;
hour++;
if (hour >= 12)
{
hour -= 12;
}
}
}
}
}
void TrailingFadeHandDisplay()
{
float milliSecondEnd = milliSecond / 1000.0f;
float secondEnd = (second + milliSecond / 1000.0f) / 60.0f;
float minuteEnd = (minute + secondEnd) / 60.0f;
float hourEnd = (hour + minuteEnd) / 12.0f;
for (ledIndex = 0; ledIndex < LedCount; ledIndex++)
{
ClearColour();
//TrailingFadeHandAddColour(milliSecondEnd, MilliSecondDisplayWidth, MilliSecondRed, MilliSecondGreen, MilliSecondBlue, 0);
TrailingFadeHandAddColour(secondEnd, SecondDisplayWidth, SecondRed, SecondGreen, SecondBlue, SecondPulseOffset);
TrailingFadeHandAddColour(minuteEnd, MinuteDisplayWidth, MinuteRed, MinuteGreen, MinuteBlue, MinutePulseOffset);
TrailingFadeHandAddColour(hourEnd, HourDisplayWidth, HourRed, HourGreen, HourBlue, HourPulseOffset);
PulsingFiveMinuteMarker();
ApplyColourToLed();
}
}
void ClearColour()
{
red = 0;
green = 0;
blue = 0;
}
void AddColour(int sectionRed, int sectionGreen, int sectionBlue, float percent)
{
percent = percent * percent;
red = min((int)255, red + (int)(percent * sectionRed));
green = min((int)255, green + (int)(percent * sectionGreen));
blue = min((int)255, blue + (int)(percent * sectionBlue));
}
void ApplyColourToLed()
{
int ledPosition = IndexToLed(ledIndex);
#if NeoPixel
leds.setPixelColor(ledPosition, 0
| (long)red << 16
| (long)green << 8
| (long)blue
);
#elif FastLED
leds[ledPosition].red = red;
leds[ledPosition].green = green;
leds[ledPosition].blue = blue;
#endif
}
int IndexToLed(int ledPosition)
{
return (2 * LedCount - 1 - ledPosition) % LedCount;
}
void TrailingFadeHandAddColour(float sectionEnd, float sectionWidth, int sectionRed, int sectionGreen, int sectionBlue, float pulseOffset)
{
const float LedWidth = 1.0f / LedCount;
float sectionStart = sectionEnd - sectionWidth;
float ledMin = (float)ledIndex / LedCount;
ledMin = RoundToComparable(sectionStart, ledMin);
float ledMax = ledMin + LedWidth;
if (ledMin <= sectionEnd && sectionEnd < ledMax)
{
float percent = (sectionEnd - ledMin) / LedWidth + 0.25f * PulsePercent(sectionEnd, ledMin, ledMax, pulseOffset);
AddColour(sectionRed, sectionGreen, sectionBlue, percent);
} else if (sectionStart <= ledMin && ledMax < sectionEnd)
{
float percent = 1.0f - (sectionEnd - ledMax) / sectionWidth + 0.25f * PulsePercent(sectionEnd, ledMin, ledMax, pulseOffset);
AddColour(sectionRed, sectionGreen, sectionBlue, percent);
} else if (ledMin < sectionStart && sectionStart < ledMax)
{
float percent = 1.0f - (sectionEnd - ledMax) / sectionWidth + 0.25f * PulsePercent(sectionEnd, ledMin, ledMax, pulseOffset);
AddColour(sectionRed, sectionGreen, sectionBlue, percent);
}
}
float PulsePercent(float sectionEnd, float ledMin, float ledMax, float pulseOffset)
{
float pulseSeconds = second + milliSecond / 1000.0f + pulseOffset;
float pulsePosition = sectionEnd + SquareWave(pulseSeconds, pulsePeriod);
pulsePosition = RoundToComparable(ledMax, pulsePosition);
float pulsePercent;
if (pulsePosition > ledMax)
{
pulsePercent = 1.0f - 2.0f * (pulsePosition - ledMax);
} else if (pulsePosition < ledMin)
{
pulsePercent = 1.0f - 2.0f * (ledMin - pulsePosition);
} else
{
pulsePercent = 1.0f;
}
pulsePercent = pow(pulsePercent, 16.0f);
return pulsePercent;
}
float SquareWave(float seconds, float period)
{
return (seconds - period * (int)(seconds / period)) / period;
}
float RoundToComparable(float baseValue, float value)
{
while (value - baseValue >= 0.5f)
{
value -= 1;
}
while (baseValue - value >= 0.5f)
{
value += 1;
}
return value;
}
void PulsingFiveMinuteMarker()
{
const float pulseMin = 0.5f;
const float pulseMax = 1.0f;
const float pulseRange = pulseMax - pulseMin;
if ((ledIndex % (LedCount / 12)) == 0)
{
float pulseSeconds = second + milliSecond / 1000.0f;
float percent = 2 * SquareWave(pulseSeconds, pulsePeriod) - 1;
if (percent < 0)
{
percent = -percent;
}
percent = pulseMin + percent * pulseRange;
percent = pow(percent, 7);
AddColour(0xff, 0xff, 0xff, percent);
}
}
@ian, Looks like a major surgery to me. Would it be better to just have two programs, one for each library? Should be much easier to read/maintain.
I only read the first few lines. Two comments:
1)
Use #if defined(NeoPixel)
etc.
2)
Replace the #endif
with
#else
#error No library selected
#endif
Maybe check that only one library is selected?
You can see why mixing the two is difficult and error prone.
See you next Wed?
I think I should be able to make next Wednesday,
Thanks @eyal for fixing the code with me last night.
Iâm happy it eventually worked.
I know the code can be optimised, so I will have a go at some stage.
but I also found this which may be worth trying
http://www.instructables.com/id/Arduino-IDE-16x-compiler-optimisations-faster-code/
I now checked in the version from last night.
As for the speedup: the improvement depends very much of what the code does, and may not be enough to remove the slight flicker.
Since the LED configuration changes only once every 1/3 second, I wonder if reducing the update rate to only repaint when the LED position changes will hide the flicker.
Before I forget, latest code.
Changes around optimising performance
-
fixed point math
-
equation approximations,
-
use the existing millis() time rather than managing it our self
-
reduced number of leds used and processed
#define fastLED 1
//#define NeoPixel 0#if NeoPixel
#include <Adafruit_NeoPixel.h>
#elif fastLED
#include <FastLED.h>
#else
#error Must select a LED library
#endif#include âRTClib.hâ
#define LedDataPin 5
#define LedCount 180const int SecondRed = 131;
const int SecondGreen = 215;
const int SecondBlue = 32;const int MinuteRed = 222;
const int MinuteGreen = 24;
const int MinuteBlue = 121;const int HourRed = 12;
const int HourGreen = 68;
const int HourBlue = 245;const unsigned long MinutesPerHour = 60;
const unsigned long SecondsPerMinute = 60;
const unsigned long MilliSecondsPerSecond = 1000;const unsigned long SyncTimeStart = (((1 * MinutesPerHour) + 23 * SecondsPerMinute + 45) * MilliSecondsPerSecond + 678);
const unsigned long SyncTimeEnd = (SyncTimeStart + 321);
const unsigned long MaxTime = (((12 * MinutesPerHour + 0) * SecondsPerMinute + 0) * MilliSecondsPerSecond + 0);#define IntScale 1000
#if NeoPixel
Adafruit_NeoPixel leds = Adafruit_NeoPixel(LedCount, LedDataPin, NEO_GRB + NEO_KHZ800);
#elif fastLEDstatic CRGB leds[LedCount];
//static CRGB[] leds = new CRGB[LedCount];
#endifstatic RTC_DS3231 rtc;
void setup()
{
Serial.begin(115200);
Serial.println(âsetupâ);if (!rtc.begin())
{
Serial.println(âCouldnât find RTCâ);
while (true)
;
}#if NeoPixel
leds.begin();
#elif fastLED
FastLED.addLeds<WS2812B, LedDataPin, GRB>(leds, LedCount).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(255);
#endifSetMillis(SyncTimeStart);
}void loop()
{
long milliSeconds = millis();if (SyncTimeStart <= milliSeconds && milliSeconds <= SyncTimeEnd)
{
ReadExternalTime();
}if (milliSeconds > MaxTime)
{
SetMillis(0);
milliSeconds = 0;
}ClearLeds();
long highlightIndex = LedCount - 1 - milliSeconds % (4000) * LedCount / 4000;
long secondPosition = 100 * (milliSeconds / 1000 % 60 * IntScale + (milliSeconds % 1000)) / 60;
WriteHand(secondPosition, 5, SecondRed, SecondGreen, SecondBlue, highlightIndex);long minutePosition = 100 * (milliSeconds / 60000 % 60 * IntScale + (milliSeconds / 1000 % 60 * IntScale / 60)) / 60;
WriteHand(minutePosition, 10, MinuteRed, MinuteGreen, MinuteBlue, (highlightIndex + 45) % LedCount);long hourPosition = 100 * (milliSeconds / 3600000 * IntScale + (milliSeconds / 60000 % 60 * IntScale / 60)) / 12;
WriteHand(hourPosition, 15, HourRed, HourGreen, HourBlue, (highlightIndex + 90) % LedCount);WriteHourMarkers((highlightIndex + 135) % LedCount);
#if NeoPixel
leds.show();
#elif fastLED
FastLED.show();
#endif
}void ReadExternalTime()
{
Serial.println(âReadExternalTimeâ);DateTime previousTime = rtc.now();
uint8_t previousSecond = previousTime.second();DateTime nextTime;
while (true)
{
nextTime = rtc.now();uint8_t nextSecond = nextTime.second();
if (nextSecond != previousSecond)
{
break;
}
}long hour = nextTime.hour() % 12;
long minute = nextTime.minute();
long second = nextTime.second();
long milliSecond = 0;long time = ((hour * MinutesPerHour + minute) * SecondsPerMinute + second) * MilliSecondsPerSecond + milliSecond;
SetMillis(time);Serial.print(âReadExternalTime finishedâ);
}void ClearLeds()
{
for (long ledIndex = 0; ledIndex < LedCount; ledIndex++)
{
#if NeoPixel
leds.setPixelColor(ledIndex, 0 );
#elif fastLED
leds[ledIndex].red = 0;
leds[ledIndex].green = 0;
leds[ledIndex].blue = 0;
#endif
}
}void WriteHand(long handPosition, long width, long red, long green, long blue, long highlightIndex)
{
long ledEndIndex = LedCount * handPosition / 100;
long ledIndexDecimals = ledEndIndex % IntScale;
ledEndIndex /= IntScale;long ledIndex = (ledEndIndex + LedCount - width + 1) % LedCount;
for (long index = 0; index < width; index++)
{
long ledValue = ((index + 1) * IntScale - ledIndexDecimals) / width;
ledValue = IntCurve20(ledValue);AddColour(ledIndex, red * ledValue / IntScale, green * ledValue / IntScale, blue * ledValue / IntScale);
ledIndex = (ledIndex + 1) % LedCount;
}AddColour(ledIndex, red * ledIndexDecimals / IntScale, green * ledIndexDecimals / IntScale, blue * ledIndexDecimals / IntScale);
if (highlightIndex < width)
{
ledIndex = (ledEndIndex + LedCount + LedCount - highlightIndex - 1) % LedCount;
AddColour(ledIndex, red / 8, green / 8, blue / 4);
ledIndex = (ledEndIndex + LedCount - highlightIndex) % LedCount;
AddColour(ledIndex, red / 4, green / 4, blue / 4);
ledIndex = (ledEndIndex + LedCount - highlightIndex + 1) % LedCount;
AddColour(ledIndex, red / 8, green / 8, blue / 4);
}
}void WriteHourMarkers(long highlightIndex)
{
if (highlightIndex > 60)
{
return;
}uint8_t value = 255 * (IntScale * (30 - abs(highlightIndex - 30)) / 30) / IntScale;
for (long ledIndex = 0; ledIndex < LedCount; ledIndex += 15)
{
AddColour(ledIndex, value, value, value);
}
}long IndexToLed(long ledIndex)
{
return (2 * LedCount - 1 - ledIndex) % LedCount;
//return ledIndex;
}void AddColour(long ledIndex, long red, long green, long blue)
{
long ledPosition = IndexToLed(ledIndex);
#if NeoPixel
#error not implementedleds.setPixelColor(ledIndex, 0
| (long)red << 16
| (long)green << 8
| (long)blue
);#elif fastLED
leds[ledPosition].red = min(max(0, leds[ledPosition].red + red), 255);
leds[ledPosition].green = min(max(0, leds[ledPosition].green + green), 255);
leds[ledPosition].blue = min(max(0, leds[ledPosition].blue + blue), 255);
#endif
}long IntCurve20(long x)
{
if (x <= 0)
{
return 0;
}if (x >= IntScale)
{
return IntScale;
}if (x < 740)
{
if (x < 0466)
{
return x * 206 / IntScale;
} else
{
return (x - 466) * 719 / IntScale + 96;
}
} else
{
if (x < 939)
{
return (x - 740) * 1747 / IntScale + 293;
} else
{
return (x - 939) * 5902 / IntScale + 640;
}
}
}long IntCurve05(long x)
{
if (x <= 0)
{
return 0;
}if (x >= 1000)
{
return 1000;
}if (x < 894)
{
if (x < 721)
{
if (x < 451)
{
return x * 34 / IntScale;
} else
{
return (x - 451) * 66 / IntScale + 16;
}
} else
{
if (x < 831)
{
return (x - 721) * 236 / IntScale + 33;
} else
{
return (x - 831) * 887 / IntScale + 59;
}
}
} else
{
if (x < 975)
{
if (x < 940)
{
return (x - 894) * 2344 / IntScale + 116;
} else
{
return (x - 940) * 4936 / IntScale + 222;
}
} else
{
if (x < 998)
{
return (x - 975) * 9602 / IntScale + 395;
} else
{
return (x - 998) * 175249 / IntScale + 617;
}
}
}
}extern volatile unsigned long timer0_millis;
void SetMillis(unsigned long time)
{
Serial.println(âSetMillisâ);
uint8_t oldSREG = SREG;// disable longerrupts while we read timer0_millis or we might get an
// inconsistent value (e.g. in the middle of a write to timer0_millis)
cli();
timer0_millis = time;
SREG = oldSREG;
}
Thanks @ian.
Enclosing the code inside a code block (delimited by three back-quotes) will keep the formatting and allow people to copy it properly.
Even better if you can place it in a github repository. Do you have permission for MHV github? Ask the committee if not.