Does the space need a clock?


(ian) #41

ok, thanks,


(Steve Kennedy) #42

@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.


(ian) #43

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);
	}
}

(Eyal Lebedinsky) #44

@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?


(ian) #45

I think I should be able to make next Wednesday,


(ian) #46

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


(Eyal Lebedinsky) #47

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.