/*
* Sourcecode für Wörteruhr (Züridütsch)
*/
#include "RTClib.h" //https://github.com/adafruit/RTClib
#include <TimeLib.h>
#include <Adafruit_GFX.h> //https://github.com/adafruit/Adafruit-GFX-Library
#include <Adafruit_NeoMatrix.h> //https://github.com/adafruit/Adafruit_NeoMatrix
#include <Adafruit_NeoPixel.h> //https://github.com/adafruit/Adafruit_NeoPixel
#include <EEPROM.h>
// Definition der Pins
#define NEOPIXEL_PIN 6 // Verbindungspin zum Neopixel-LED-Streifen
int sensorPin = A6; // Analogeingangspin für Lichtsensor
const byte tasterColor = 7; // Taster für Farbwechsel
boolean aktKlappe, altKlappe;
const uint32_t prellIntervall = 30;
uint32_t aktMillis, prellMillis;
// char array to save time an date as string
char time_s[9];
char date_s[11];
// define parameters for the project
const int width = 11; // width of LED matirx
const int height = 10 + 1; // height of LED matrix + additional row for minute leds
const int eeAddressTime = 10; // eeprom address for time value (persist values during power off)
const int eeAddressColor = 20; // eeprom address for color value (persist values during power off)
const int upperLight = 800; // upper threshold for lightsensor (above this value brightness is always 20%)
const int lowerLight = 700; // lower threshold for lightsensor (below this value brightness is always 100%)
const int centerrow = height / 2; // id of center row
const int centercol = width / 2; // id of center column
// create RTC object
RTC_DS3231 rtc;
// define mapping array for nicer printouts
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
// create Adafruit_NeoMatrix object
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(width, height, NEOPIXEL_PIN,
NEO_MATRIX_TOP + NEO_MATRIX_LEFT +
NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG,
NEO_GRB + NEO_KHZ800);
// define color modes
const uint16_t colors[] = {
matrix.Color(255, 255, 255), // white (in this mode together with colorwheel)
matrix.Color(255, 0, 0), // red
matrix.Color(255, 255, 0), // yellow
matrix.Color(255, 0, 200), // magenta
matrix.Color(255, 255, 255), // white (darker)
matrix.Color(0, 255, 0), // green
matrix.Color(0, 0, 255) // blue
};
// definition of global variables
int valLight = 100; // currentvalue of light sensor
int brightness = 20; // current brughtness for leds
int activeColorID = 0; // current active color mode
int offset = 0; // offset for colorwheel
long updateIntervall = 120000; // Updateintervall 2 Minuten
long updateTimer = 0; // Zwischenspeicher für die Wartezeit
// representation of matrix as 2D array
int grid[height][width] = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};
// function prototypes
void timeToArray(uint8_t hours, uint8_t minutes);
void printDateTime(DateTime datetime);
void checkDCFSignal();
int DCF77signalQuality(int pulses);
void checkForNewDCFTime(DateTime now);
void gridAddPixel(uint8_t x, uint8_t y);
void gridFlush(void);
void drawOnMatrix(void);
void setup() {
// enable serial output
Serial.begin(9600);
// Measure DCF signal quality
//checkDCFSignal();
// Init RTC
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
Serial.flush();
abort();
}
pinMode(tasterColor, INPUT_PULLUP);
// get active color from last power cycle
EEPROM.get(eeAddressColor, activeColorID);
// check if in the last 3 seconds was a power cycle
// if yes, so change to next color mode
long laststartseconds = 0;
EEPROM.get(eeAddressTime, laststartseconds);
long currentseconds = rtc.now().secondstime();
Serial.print("Startseconds: ");
Serial.println(laststartseconds);
Serial.print("Currentseconds: ");
Serial.println(currentseconds);
EEPROM.put(eeAddressTime, currentseconds);
Serial.print("active color: ");
Serial.println(activeColorID);
Serial.println("Buildtime: ");
Serial.println(F(__DATE__));
Serial.println(F(__TIME__));
// check if RTC battery was changed
if (rtc.lostPower() || DateTime(F(__DATE__), F(__TIME__)) > rtc.now()) {
Serial.println("RTC lost power or RTC time is behind build time, let's set the time!");
// When time needs to be set on a new device, or after a power loss, the
// following line sets the RTC to the date & time this sketch was compiled
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
// Init LED matrix
matrix.begin();
matrix.setBrightness(100);
}
void loop() {
aktMillis = millis();
altKlappe = aktKlappe;
if (aktMillis - prellMillis >= prellIntervall)
{
prellMillis = aktMillis;
aktKlappe = digitalRead(tasterColor);
}
if (altKlappe != aktKlappe) {
if (aktKlappe) {
}
else
{
activeColorID = activeColorID +1;
if (activeColorID >= 7) {
activeColorID = 0;
}
}
}
// get light value from light sensor
valLight = analogRead(sensorPin);
// calc led brightness from light value
if (valLight < lowerLight) {
brightness = 100;
}
else if (valLight > upperLight) {
brightness = 20;
}
else {
brightness = 80 - ((valLight - lowerLight) * 1.0 / (upperLight - lowerLight)) * 80 + 20;
}
Serial.print("Status Taster: ");
Serial.println(aktKlappe);
Serial.print("activeColorID: ");
Serial.println(activeColorID);
Serial.print("Light: ");
Serial.println(valLight);
Serial.print("Brightness: ");
Serial.println(brightness);
matrix.setBrightness(brightness);
// Print current date and time
DateTime rtctime = rtc.now();
Serial.print("RTC: ");
printDateTime(rtctime);
Serial.print("Temperature: ");
Serial.print(rtc.getTemperature());
Serial.println(" C");
// convert time to a grid representation
timeToArray(rtctime.hour(), rtctime.minute());
// clear matrix
matrix.fillScreen(0);
// add condition if nightmode (LEDs = OFF) should be activated
if (rtctime.hour() > 22 && rtctime.hour() < 6) { // turn in off LEDs between 22:00 and 6:00
// do nothing, matrix already cleared (see previous statment)
} else {
// do the normal job and display the time on the wordclock
// if color mode is set to 0, a color wheel will be shown in background
if (activeColorID == 0) {
// draw colorwheel on matrix
drawCircleOnMatrix(offset);
offset = (offset + 1) % 256;
}
// draw grid reprentation of time to matrix
drawOnMatrix();
}
// send the commands to the LEDs
matrix.show();
// change depending on color mode the refreshing time of clock
if (activeColorID == 0) {
delay(500);
} else {
delay(500);
}
}
// Draws a 360 degree colorwheel on the matrix rotated by offset
void drawCircleOnMatrix(int offset) {
for (int r = 0; r < height; r++) {
for (int c = 0; c < width; c ++) {
int angle = ((int)((atan2(r - centerrow, c - centercol) * (180 / M_PI)) + 180) % 360);
int hue = (int)(angle * 255.0 / 360 + offset) % 256;
matrix.drawPixel(c, r, Wheel(hue));
}
}
}
// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
WheelPos = 255 - WheelPos;
if (WheelPos < 85) {
return matrix.Color(255 - WheelPos * 3, 0, WheelPos * 3);
}
if (WheelPos < 170) {
WheelPos -= 85;
return matrix.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
WheelPos -= 170;
return matrix.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
// Prints given date and time to serial
void printDateTime(DateTime datetime) {
Serial.print(datetime.year(), DEC);
Serial.print('/');
Serial.print(datetime.month(), DEC);
Serial.print('/');
Serial.print(datetime.day(), DEC);
Serial.print(" (");
Serial.print(daysOfTheWeek[datetime.dayOfTheWeek()]);
Serial.print(") ");
Serial.print(datetime.hour(), DEC);
Serial.print(':');
Serial.print(datetime.minute(), DEC);
Serial.print(':');
Serial.print(datetime.second(), DEC);
Serial.println();
}
// "activates" a pixel in grid
void gridAddPixel(uint8_t x, uint8_t y) {
grid[y][x] = 1;
}
// "deactivates" all pixels in grid
void gridFlush(void) {
//Setzt an jeder Position eine 0
for (uint8_t i = 0; i < height; i++) {
for (uint8_t j = 0; j < width; j++) {
grid[i][j] = 0;
}
}
}
// draws the grid to the ledmatrix with the current active color
void drawOnMatrix() {
for (int s = 0; s < width; s++) {
for (int z = 0; z < height; z++) {
if (grid[z][s] != 0) {
Serial.print("1 ");
matrix.drawPixel(s, z, colors[activeColorID]);
}
else {
Serial.print("0 ");
}
}
Serial.println();
}
}
// Wandelt die angegebene Zeit in eine Gitterdarstellung um
void timeToArray(uint8_t hours, uint8_t minutes) {
Serial.println(hours);
Serial.println(minutes);
//clear grid
gridFlush();
//start filling grid with pixels
//ES ISCH
Serial.print("ES ISCH ");
gridAddPixel(0, 0);
gridAddPixel(1, 0);
gridAddPixel(3, 0);
gridAddPixel(4, 0);
gridAddPixel(5, 0);
gridAddPixel(6, 0);
//show minutes
if (minutes >= 5 && minutes < 10)
{
//FOIF
Serial.print("FOIF ");
gridAddPixel(7, 1);
gridAddPixel(8, 1);
gridAddPixel(9, 1);
gridAddPixel(10, 1);
//AB
Serial.print("AB ");
gridAddPixel(2, 3);
gridAddPixel(3, 3);
}
else if (minutes >= 10 && minutes < 15)
{
//ZÄÄ
Serial.print("ZÄÄ ");
gridAddPixel(8, 0);
gridAddPixel(9, 0);
gridAddPixel(10, 0);
//AB
Serial.print("AB ");
gridAddPixel(2, 3);
gridAddPixel(3, 3);
}
else if (minutes >= 15 && minutes < 20)
{
//VIERTEL
Serial.print("VIERTEL ");
gridAddPixel(0, 1);
gridAddPixel(1, 1);
gridAddPixel(2, 1);
gridAddPixel(3, 1);
gridAddPixel(4, 1);
gridAddPixel(5, 1);
gridAddPixel(6, 1);
//AB
Serial.print("AB ");
gridAddPixel(2, 3);
gridAddPixel(3, 3);
}
else if (minutes >= 20 && minutes < 25)
{
//ZWÄNZG
Serial.print("ZWÄNZG ");
gridAddPixel(1, 2);
gridAddPixel(2, 2);
gridAddPixel(3, 2);
gridAddPixel(4, 2);
gridAddPixel(5, 2);
gridAddPixel(6, 2);
//AB
Serial.print("AB ");
gridAddPixel(2, 3);
gridAddPixel(3, 3);
}
else if (minutes >= 25 && minutes < 30)
{
//FOIF
Serial.print("FOIF ");
gridAddPixel(7, 1);
gridAddPixel(8, 1);
gridAddPixel(9, 1);
gridAddPixel(10, 1);
//VOR
Serial.print("VOR ");
gridAddPixel(8, 2);
gridAddPixel(9, 2);
gridAddPixel(10, 2);
//HALBI
Serial.print("HALBI ");
gridAddPixel(5, 3);
gridAddPixel(6, 3);
gridAddPixel(7, 3);
gridAddPixel(8, 3);
gridAddPixel(9, 3);
}
else if (minutes >= 30 && minutes < 35)
{
//HALBI
Serial.print("HALBI ");
gridAddPixel(5, 3);
gridAddPixel(6, 3);
gridAddPixel(7, 3);
gridAddPixel(8, 3);
gridAddPixel(9, 3);
}
else if (minutes >= 35 && minutes < 40)
{
//FOIF
Serial.print("FOIF ");
gridAddPixel(7, 1);
gridAddPixel(8, 1);
gridAddPixel(9, 1);
gridAddPixel(10, 1);
//AB
Serial.print("AB ");
gridAddPixel(2, 3);
gridAddPixel(3, 3);
//HALBI
Serial.print("HALBI ");
gridAddPixel(5, 3);
gridAddPixel(6, 3);
gridAddPixel(7, 3);
gridAddPixel(8, 3);
gridAddPixel(9, 3);
}
else if (minutes >= 40 && minutes < 45)
{
//ZWÄNZG
Serial.print("ZWÄNZG ");
gridAddPixel(1, 2);
gridAddPixel(2, 2);
gridAddPixel(3, 2);
gridAddPixel(4, 2);
gridAddPixel(5, 2);
gridAddPixel(6, 2);
//VOR
Serial.print("VOR ");
gridAddPixel(8, 2);
gridAddPixel(9, 2);
gridAddPixel(10, 2);
}
else if (minutes >= 45 && minutes < 50)
{
//VIERTEL
Serial.print("VIERTEL ");
gridAddPixel(0, 1);
gridAddPixel(1, 1);
gridAddPixel(2, 1);
gridAddPixel(3, 1);
gridAddPixel(4, 1);
gridAddPixel(5, 1);
gridAddPixel(6, 1);
//VOR
Serial.print("VOR ");
gridAddPixel(8, 2);
gridAddPixel(9, 2);
gridAddPixel(10, 2);
}
else if (minutes >= 50 && minutes < 55)
{
//ZÄÄ
Serial.print("ZÄÄ ");
gridAddPixel(8, 0);
gridAddPixel(9, 0);
gridAddPixel(10, 0);
//VOR
Serial.print("VOR ");
gridAddPixel(8, 2);
gridAddPixel(9, 2);
gridAddPixel(10, 2);
}
else if (minutes >= 55 && minutes < 60)
{
//FOIF
Serial.print("FOIF ");
gridAddPixel(7, 1);
gridAddPixel(8, 1);
gridAddPixel(9, 1);
gridAddPixel(10, 1);
//VOR
Serial.print("VOR ");
gridAddPixel(8, 2);
gridAddPixel(9, 2);
gridAddPixel(10, 2);
}
//separate LEDs for minutes in an additional row
{
switch (minutes % 5)
{
case 0:
break;
case 1:
gridAddPixel(0, 10);
break;
case 2:
gridAddPixel(0, 10);
gridAddPixel(1, 10);
break;
case 3:
gridAddPixel(0, 10);
gridAddPixel(1, 10);
gridAddPixel(2, 10);
break;
case 4:
gridAddPixel(0, 10);
gridAddPixel(1, 10);
gridAddPixel(2, 10);
gridAddPixel(3, 10);
break;
}
}
//convert hours to 12h format
if (hours >= 12)
{
hours -= 12;
}
if (minutes >= 25)
{
hours++;
}
if (hours == 12)
{
hours = 0;
}
// show hours
switch (hours)
{
case 0:
//ZWÖLFI
Serial.print("ZWÖLFI ");
gridAddPixel(0, 9);
gridAddPixel(1, 9);
gridAddPixel(2, 9);
gridAddPixel(3, 9);
gridAddPixel(4, 9);
gridAddPixel(5, 9);
break;
case 1:
//EINS
Serial.print("EIS");
gridAddPixel(0, 4);
gridAddPixel(1, 4);
gridAddPixel(2, 4);
break;
case 2:
//ZWEI
Serial.print("ZWEI ");
gridAddPixel(3, 4);
gridAddPixel(4, 4);
gridAddPixel(5, 4);
gridAddPixel(6, 4);
break;
case 3:
//DRÜ
Serial.print("DRÜ ");
gridAddPixel(8, 4);
gridAddPixel(9, 4);
gridAddPixel(10, 4);
break;
case 4:
//VIERI
Serial.print("VIERI ");
gridAddPixel(0, 5);
gridAddPixel(1, 5);
gridAddPixel(2, 5);
gridAddPixel(3, 5);
gridAddPixel(4, 5);
break;
case 5:
//FOIFI
Serial.print("FOIFI ");
gridAddPixel(6, 5);
gridAddPixel(7, 5);
gridAddPixel(8, 5);
gridAddPixel(9, 5);
gridAddPixel(10, 5);
break;
case 6:
//SÄCHSI
Serial.print("SÄCHSI ");
gridAddPixel(0, 6);
gridAddPixel(1, 6);
gridAddPixel(2, 6);
gridAddPixel(3, 6);
gridAddPixel(4, 6);
gridAddPixel(5, 6);
break;
case 7:
//SIBNI
Serial.print("SIBNI ");
gridAddPixel(6, 6);
gridAddPixel(7, 6);
gridAddPixel(8, 6);
gridAddPixel(9, 6);
gridAddPixel(10, 6);
break;
case 8:
//ACHTI
Serial.print("ACHTI ");
gridAddPixel(1, 7);
gridAddPixel(2, 7);
gridAddPixel(3, 7);
gridAddPixel(4, 7);
gridAddPixel(5, 7);
break;
case 9:
//NÜNI
Serial.print("NÜNI ");
gridAddPixel(7, 7);
gridAddPixel(8, 7);
gridAddPixel(9, 7);
gridAddPixel(10, 7);
break;
case 10:
//ZÄNI
Serial.print("ZÄNI ");
gridAddPixel(1, 8);
gridAddPixel(2, 8);
gridAddPixel(3, 8);
gridAddPixel(4, 8);
break;
case 11:
//ELFI
Serial.print("ELFI ");
gridAddPixel(6, 8);
gridAddPixel(7, 8);
gridAddPixel(8, 8);
gridAddPixel(9, 8);
break;
}
if (minutes > 75)
{
//UHR
Serial.print("UHR ");
gridAddPixel(8, 9);
gridAddPixel(9, 9);
gridAddPixel(10, 9);
}
Serial.println();
}