/*
*
* Realisiert von Stefan Hagel, Pansdorf
*
* Private-use only! (you need to ask for a commercial-use)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Private-use only! (you need to ask for a commercial-use)
*
*
* ---------------------------------------------------------------------------------------------------------------------------------
* Remake einer "qlocktwo", realisiert mit einem Arduino Nano, einem "Neopixel RGB-Streifen" und einem "I2C DS1307 Uhrmodul"
*
* Das DS1307-Modul wird an VCC mit 5V versorgt (Unbedingt darauf achten das die Knopfzelle auch ein AKKU und keine Batterie verbaut ist!),
* GND an Masse, SDA an Pin A4, SCL an Pin A5. (Angaben sind für den Arduino Nano: Andere Modelle haben eine andere Pinbelegung. Bitte nach dem passenden Modell und "Pinout"
* suchen um die richtigen Pins zu finden!)
*
* Die Datenleitung des Neopixel-Streifens ist auf Pin D9 definiert (Kann weiter unten auch verändert werden.)
*
* In einem 2-Dimensionalen Array werden die Neopixel definiert die für den Aufruf eines Wortes (oder mehrerer Worte) benötigt werden.
* Es werden im weiteren Verlauf des Programmes Worte definiert (ES_IST) und mit der entsprechenden Zeile aus dem Array verknüpft.
* ES_IST findet man in der ersten Zeile(0). Es wird von 0 an aufwärts gezählt.
*
* Zum einstellen der Uhrzeit werden 3 Taster benötigt. Der "Sicherheitstaster muss gedrückt werden um die Uhrzeit zu verstellen, die zwei anderen Taster
* werden für das Einstellen der Minuten und Stunden benötigt.
* Die Taster müssen gegen Masse schalten. D2 = Sicherheitstaster, D3 = Stundentaster, D4 = Minutentaster.
*
*
* Es fehlt hier noch:
*
* Die möglichkeit der automatischen Sommer/Winterzeitumstellung.
*
* Eine Möglichkeit zur Einstellung per App. (Datum/Zeit einstellen, Farben einstellen, Sommwe/Winterzeit konfigutation und weiteres.
*
* Dimmfunktion (Helle Umgebung = hohe Leuchtkraft...) mit eventuellem Farbwechsel auf rot in der Nacht.
* ---------------------------------------------------------------------------------------------------------------------------------
*/
#include <Adafruit_NeoPixel.h> // Bibliothek für den Neopixel-Streifen einbinden
#include <Wire.h> // Bibliothek für die Schnittstelle zur Uhr einbinden
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
unsigned long color, color_background; // Globale Variablen deklarieren
int hours, minutes, seconds;
#define LED_PIN 9 // An welchem PWM-Ausgang am Arduino sind die NeoPixel angeschlossen?
#define LED_ANZAHL 224 // Wie viele NeoPixel sind angeschlossen?
byte helligkeit=200; // Helligkeit der LEDs (von 0 bis 255)
#define DS1307_ADDRESS 0x68 // I²C Addresse für Uhrmodul
Adafruit_NeoPixel strip(LED_ANZAHL, LED_PIN, NEO_GRB + NEO_KHZ800); // Neopixel-Streifen definieren
/*
* Argument 1 = Anzahl der NeoPixel
* Argument 2 = Arduino PWM-Pin an dem der LED-Streifen angeschlossen ist
* Argument 3 = Welche Art von NeoPixel-Streifen wird verwendet?:
* NEO_KHZ800 800 KHz bitstream (meistverwendet --> NeoPixel w/WS2812 LEDs)
* NEO_KHZ400 400 KHz (classic 'v1' (nicht v2) FLORA pixels, WS2811 treiber)
* NEO_GRB Pixels sind für GRB bitstream vorgesehen (meistverwendet)
* NEO_RGB Pixels sind für RGB bitstream vorgesehen (v1 FLORA pixels, not v2)
* NEO_RGBW Pixels sind für RGBW bitstream vorgesehen (NeoPixel RGBW Produkte)
*/
//Array eröffnen. Die LEDs, die zur Anzeige des benötigten Wortes nötig sind, werden hier deklariert.
//Der Eintrag -1 ist ein Abbruchkriterium und weist auf das Ende des Wortes/der Wörter hin. (Wird in der Schleife verwendendet)
#define NUM_PIXELS 15 // Maximale Anzahl der Pixel für eine Zahl
signed short leds[26][NUM_PIXELS] = { // WORT ANZAHL BENÖTIGTER LEDs
{ 0, 1, 39, 38, 79, 78, 80, 81, 119, 118, -1 }, // ES IST 11
{ 159, 158, 160, 161, 199, 198, 200, 201, -1 }, // FUENF 9
{ 2, 3, 37, 36, 42, 43, 77, 76, -1 }, // ZEHN 9
{ 84, 85, 115, 114, 124, 125, 155, 154, 164, 165, 195, 194, 204, 205, -1 }, // VIERTEL 15
{ 82, 83, 117, 116, 122, 123, 157, 156, 162, 163, 197, 196, 202, 203, -1 }, // ZWANZIG 15
{ 8, 9, 31, 30, 48, 49, 71, 70, -1 }, // HALB 9
{ 126, 127, 153, 152, 166, 167, -1 }, // VOR 7
{ 46, 47, 73, 72, 86, 87, 113, 112, -1 }, // NACH 9
{ 50, 51, 69, 68, 90, 91, 109, 108, -1 }, // EINS 9
{ 10, 11, 29, 28, 50, 51, 69, 68, -1 }, // ZWEI 9
{ 27, 26, 52, 53, 67, 66, 92, 93, -1 }, // DREI 9
{ 145, 144, 174, 175, 185, 184, 214, 215, -1 }, // VIER 9
{ 147, 146, 172, 173, 187, 186, 212, 213, -1 }, // FUENF 9
{ 21, 20, 58, 59, 61, 60, 98, 99, 101, 100, -1 }, // SECHS 11
{ 109, 108, 130, 131, 149, 148, 170, 171, 189, 188, 210, 211, -1 }, // SIEBEN 13
{ 23, 22, 56, 57, 63, 62, 96, 97, -1 }, // ACHT 9
{ 65, 64, 94, 95, 105, 104, 134, 135, -1 }, // NEUN 9
{ 103, 102, 136, 137, 143, 142, 176, 177, -1 }, // ZEHN 9
{ 14, 15, 25, 24, 54, 55, -1 }, // ELF 7
{ 111, 110, 128, 129, 151, 150, 168, 169, 191, 190, -1 }, // ZWOELF 11
{ 178, 179, 181, 180, 218, 219, -1 }, // UHR 7
{ 223, -1 }, // 1 3
{ 222, 223, -1 }, // 2 3
{ 221, 222, 223, -1 }, // 3 3
{ 220, 221, 222, 223, -1 }, // 4 3
{ 50, 51, 69, 68, 90, 91, -1 } // EIN (UHR) - Volle Stunde 7
};
// Hier wird den Worten eine Zeile aus dem Array zugewiesen. (0 ist die erste Zeile!)
#define ES_IST 0
#define M_FUENF 1
#define M_ZEHN 2
#define VIERTEL 3
#define ZWANZIG 4
#define HALB 5
#define VOR 6
#define NACH 7
#define H_EINS 8
#define H_ZWEI 9
#define H_DREI 10
#define H_VIER 11
#define H_FUENF 12
#define H_SECHS 13
#define H_SIEBEN 14
#define H_ACHT 15
#define H_NEUN 16
#define H_ZEHN 17
#define H_ELF 18
#define H_ZWOELF 19
#define UHR 20
#define M_EINS 21
#define M_ZWEI 22
#define M_DREI 23
#define M_VIER 24
#define H_EIN 25
// Setup wird einmal beim Programmstart ausgeführt!
void setup() {
Wire.begin(); // Treiber für den Datenbus initialisieren.
Serial.begin(115200); // Baudrate für die Kommunikation mit dem Uhrmodul
strip.begin(); // Initialisieren des NeoPixel Objectes (Treiber installation und ähnliches...)
strip.show(); // Alle Pixel ausschalten
strip.setBrightness(helligkeit); // Wert der Helligkeit wird weiter oben eingestellt.
color = strip.Color(110,255,30); // RGB-Farbe der Uhrzeit
color_background = strip.Color(0,0,0);
// Pins definieren für das Einstellen der Uhrzeit
pinMode(2, INPUT_PULLUP); // Sicherheitstaster
pinMode(3, INPUT_PULLUP); // Stundentaster
pinMode(4, INPUT_PULLUP); // Minutentaster
//Startsequenz anzeigen
rainbow();
}
// Hier folgen jetzt die Funktionen
// Rainbow cycle along whole strip. Pass delay time (in ms) between frames.
void rainbow() {
// Hue of first pixel runs 5 complete loops through the color wheel.
// Color wheel has a range of 65536 but it's OK if we roll over, so
// just count from 0 to 5*65536. Adding 256 to firstPixelHue each time
// means we'll make 5*65536/256 = 1280 passes through this outer loop:
for(long firstPixelHue = 0; firstPixelHue < 5*65536; firstPixelHue += 256) {
for(int i=0; i<strip.numPixels(); i++) { // For each pixel in strip...
// Offset pixel hue by an amount to make one full revolution of the
// color wheel (range of 65536) along the length of the strip
// (strip.numPixels() steps):
int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
// strip.ColorHSV() can take 1 or 3 arguments: a hue (0 to 65535) or
// optionally add saturation and value (brightness) (each 0 to 255).
// Here we're using just the single-argument hue variant. The result
// is passed through strip.gamma32() to provide 'truer' colors
// before assigning to each pixel:
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
}
strip.show(); // Update strip with new contents
//delay(10); // Pause for a moment
}
}
void setLeds(int i) { //Die Funktion setLeds liest aus der entsprechenden Zeile des Arrays die einzelnen Werte und übergibt sie dem Neopixelstreifen. Pixel für Pixel.
for (int k = 0; k < NUM_PIXELS; k++) {
if (leds[i][k] > -1) {
strip.setPixelColor(leds[i][k], color);
}
else {
break;
}
}
}
void displayTime() { //Die Funktion displayTime errechnet werte aus der ausgelesenen Zeit mit denen dann die passenden Worte der Uhrzeit ausgewählt werden.
int mins = minutes - (minutes % 5); //Erklärung für die Rechnung mit dem Modulo (%): Beispiel mins=28(minutes)%5 = 3 (Ergebnis ist 5 rest 3. Es wird nur der Rest ausgegeben. Somit ist mins=3)
int hrs;
int vor = 0;
strip.clear(); //Strip löschen
for (int a = 0; a < LED_ANZAHL; a++) { //Alle Uhrzeit-Pixel wird ein Farbwert zugewiesen. Alle anderen Pixel in einer anderen Farbe (color_background).
strip.setPixelColor(a, color_background);
}
setLeds(ES_IST);
switch (mins / 5) { //Hier werden die Minutenworte geschaltet
case 0: // Volle Stunde
setLeds(UHR);
break;
case 1:
setLeds(M_FUENF);
setLeds(NACH);
break;
case 2:
setLeds(M_ZEHN);
setLeds(NACH);
break;
case 3:
setLeds(VIERTEL);
setLeds(NACH);
break;
case 4:
setLeds(ZWANZIG);
setLeds(NACH);
break;
case 5:
setLeds(M_FUENF);
setLeds(VOR);
setLeds(HALB);
vor = 1;
break;
case 6:
setLeds(HALB);
vor = 1;
break;
case 7:
setLeds(M_FUENF);
setLeds(NACH);
setLeds(HALB);
vor = 1;
break;
case 8:
setLeds(ZWANZIG);
setLeds(VOR);
vor = 1;
break;
case 9:
setLeds(VIERTEL);
setLeds(VOR);
vor = 1;
break;
case 10:
setLeds(M_ZEHN);
setLeds(VOR);
vor = 1;
break;
case 11:
setLeds(M_FUENF);
setLeds(VOR);
vor = 1;
break;
}
switch (minutes % 5) { // Hier werden die LEDs für die Minutenanzeige (1-4) geschaltet.
case 0:
break;
case 1:
setLeds(M_EINS);
break;
case 2:
setLeds(M_ZWEI);
break;
case 3:
setLeds(M_DREI);
break;
case 4:
setLeds(M_VIER);
break;
}
hrs = hours + vor; // hrs ist die Angezeigte Stunde. Bei zehn vor vier ist es 15:50Uhr. Daher wird eine Stunde hinzugezählt um von 15 auf 16 zu kommen (zehn VOR 4)
if (hrs > 12) { // Stunde 13 ist nicht in den Worten enthalten. Daher werden Stundenzahlen größer als 12 auf 0 zurückgesetzt.
hrs = hrs - 12;
}
if (hrs == 0) { // Da wir keine null Uhr haben wird die Stunde null zur stunde 12 gemacht.
hrs = 12;
}
// STUNDEN
switch (hrs) {
case 1:
if(mins/5==0){
setLeds(H_EIN);
break;
}
else {
setLeds(H_EINS);
break;
}
case 2:
setLeds(H_ZWEI);
break;
case 3:
setLeds(H_DREI);
break;
case 4:
setLeds(H_VIER);
break;
case 5:
setLeds(H_FUENF);
break;
case 6:
setLeds(H_SECHS);
break;
case 7:
setLeds(H_SIEBEN);
break;
case 8:
setLeds(H_ACHT);
break;
case 9:
setLeds(H_NEUN);
break;
case 10:
setLeds(H_ZEHN);
break;
case 11:
setLeds(H_ELF);
break;
case 12:
setLeds(H_ZWOELF);
break;
}
strip.show(); // Zeit anzeigen
}
void TastenLesen() { //Mit dieser Funktion soll die Uhzeit eingestellt werden können. 3 Taster benötigt. (Taster müssen vom PIN auf Masse schalten)
int Sicherheitstaster = digitalRead(2);
int Stundentaster = digitalRead(3);
int Minutentaster = digitalRead(4);
if(Sicherheitstaster == 0){
if(Stundentaster == 0){
//readTime();
hours++;
if(hours>=24) {
hours=0;
}
seconds = 0;
setDate();
delay(250);
}
else{
if(Minutentaster == 0){
minutes++;
if(minutes>=60){
minutes=0;
}
seconds = 0;
setDate();
delay(250);
}
}
}
}
void readTime() {
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(0x00);
Wire.endTransmission();
Wire.requestFrom(DS1307_ADDRESS, 7);
seconds = bcdToDec(Wire.read());
minutes = bcdToDec(Wire.read());
hours = bcdToDec(Wire.read() & 0b111111);
}
void setDate() { // Funktion zum schreiben der Zeit auf das Uhrmodul. Bitte hier die Aktuelle Zeit zum angeben. Diese wird beim ersten Start auf das Uhrmodul geschrieben.
Wire.beginTransmission(DS1307_ADDRESS);
Wire.write(0x00);
Wire.write(decToBcd(seconds)); // sekunden
Wire.write(decToBcd(minutes)); // minuten
Wire.write(decToBcd(hours)); // stunden (24-hour format)
Wire.write(decToBcd(4 )); // wochentag (1=Sonntag)
Wire.write(decToBcd(07)); // tag
Wire.write(decToBcd(04)); // monat
Wire.write(decToBcd(2021)); // jahr
Wire.write(0x00);
Wire.endTransmission();
}
int bcdToDec(int value) { // Binärcode in Dezimalzahl umwandeln
return ((value/16*10) + (value%16));
}
int decToBcd(int value) { // Dezimalzahl in Binärcode umwandeln
return ((value/10*16) + (value%10));
}
void loop() // Diese Funktion wird in einer nie endenden Schleife ausgeführt! Zeit vom Uhrmodul lesen, Zeit anzeigen, 250ms warten und wieder von vorne.
{
readTime();
displayTime();
TastenLesen();
delay(10);
}