/* NeoPixel Clock 3
Auf drei Neopixel-Ringen wird die Uhrzeit angezeigt.
Stunde = rot, Minute = grün, Sekunde = blau
Der äußere Ring hat 60 LEDs, der 2. 48, der innere 40.
Wenn weniger LEDs im Ring sind werden, dann wird mit der
map-Funktion auf die nächste LED gerundet.
LED_COUNT ist die Anzahl der verfügbaren LEDs,
Pixel_Nr = map ( Sekunde, 0, 59, 0, LED_COUNT-1)
Stellen der Uhrzeit über die serielle Schnittstelle
Sende ? und der Controller antwortet mit seiner Uhrzeit
Sende !12:05:50 und der Controller übernimmt die Uhrzeit 12:05:50
Sende m0 oder m1 oder m2 um die Anzeige-Betriebsart umzutellen
m0 => nur ein Pixel in den Grundfarben für die Anzeige, die Ausgabe
der Stunde hat Vorrang vor Minute und Sekunde
m1 => wenn das selbe Pixel von mehrern Zeiten genutzt ist, dann wird
eine Mischfarbe ausgegeben
m2 => wie m1, zusätzlich hat die Sekunde einen "Schweif" von 5 Pixeln
Sende n (Normal-Modus) oder d (Demo-Modus, Zeit läuft schneller)
Funktionen und Datenstruktur für NTP-Zugriff siehe
https://github.com/espressif/arduino-esp32/blob/master/cores/esp32/esp32-hal-time.c
Die Struktur tm findet sich in der time.h (Arduino/C Standardbibliothek)
struct tm {
int8_t tm_sec; //< seconds after the minute - [ 0 to 59 ]
int8_t tm_min; //< minutes after the hour - [ 0 to 59 ]
int8_t tm_hour; //< hours since midnight - [ 0 to 23 ]
int8_t tm_mday; //< day of the month - [ 1 to 31 ]
int8_t tm_wday; //< days since Sunday - [ 0 to 6 ]
int8_t tm_mon; //< months since January - [ 0 to 11 ]
int16_t tm_year; //< years since 1900
int16_t tm_yday; //< days since January 1 - [ 0 to 365 ]
int16_t tm_isdst; //< Daylight Saving Time flag
};
Zugriff auf einzelne Komponenten:
sekunde = timeinfo.tm_sec;
minute = timeinfo.tm_min;
stunde = timeinfo.tm_hour;
tag = timeinfo.tm_mday;
monat = timeinfo.tm_mon + 1; // Monat 0 = Januar => wird geändert zu Januar = 1
jahr = timeinfo.tm_year + 1900;
wochentag = timeinfo.tm_wday; // Sonntag = 0, Montag = 1 etc.
*/
// NEOPIXEL BEST PRACTICES for most reliable operation:
// - Add 1000 uF CAPACITOR between NeoPixel strip's + and - connections.
// - MINIMIZE WIRING LENGTH between microcontroller board and first pixel.
// - NeoPixel strip's DATA-IN should pass through a 300-500 OHM RESISTOR.
// - AVOID connecting NeoPixels on a LIVE CIRCUIT. If you must, ALWAYS
// connect GROUND (-) first, then +, then data.
// - When using a 3.3V microcontroller with a 5V-powered NeoPixel strip,
// a LOGIC-LEVEL CONVERTER on the data line is STRONGLY RECOMMENDED.
// (Skipping these may work OK on your workbench but can fail in the field)
#include <WiFi.h>
#include <Adafruit_NeoPixel.h>
// NTP Einstellungen
#define TIMEZONE 1 // Zeitzone: UTC = 0, Winterzeit MEZ: UTC+1, Sommerzeit MESZ: UTC+2
#define DST_OFFSET 3600 // Offset für Winterzeit 0, für Sommerzeit 3600 (DST: daylight saving time)
#define NTP_SERVER "ptbtime1.ptb.de" // Zeitserver der physikalisch-technischen Bundesanstalt in Braunschweig
#define UTC_OFFSET 0
#define UTC_OFFSET_DST 0
//#include <credentials.h>
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const byte channel = 6; // wifi channel only for Wokwi
// Which pin on the Arduino is connected to the NeoPixels?
#define LED_PIN 17
// How many NeoPixels are attached to the Arduino?
#define LED_RING1 60 // für die Sekunden
#define LED_RING2 48 // jede 4. LED für Stunden
#define LED_RING3 40 // jede 10. LED für 3/6/9/12
#define LED_COUNT LED_RING1+LED_RING2+LED_RING3
// Declare our NeoPixel strip object:
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
// Argument 1 = Number of pixels in NeoPixel strip
// Argument 2 = Arduino pin number (most are valid)
// Argument 3 = Pixel type flags, add together as needed:
// NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
// NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
// NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products)
// NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)
// NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products)
// Globale Variablen
byte Stunde, Minute, Sekunde, Mode;
int ColorStunde = 0xFF0000, ColorMinute = 0x00FF00, ColorSekunde = 0x0000FF, msPerSec = 1000;
long NextTime=0, timeRaw;
unsigned long ShowTime, UpdateTime;
// ntp timestamp
struct tm timeinfo;
// setup() function -- runs once at startup --------------------------------
void setup() {
Serial.begin(115200);
strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED)
strip.show(); // Turn OFF all pixels ASAP
strip.setBrightness(200); // Set BRIGHTNESS to about 1/5 (max = 255)
Mode = 0;
// Fill along the length of the strip in various colors...
colorWipe(strip.Color(255, 0, 0), 20); // Red
colorWipe(strip.Color( 0, 255, 0), 15); // Green
colorWipe(strip.Color( 0, 0, 255), 00); // Blue
WiFiStart();
configTime(UTC_OFFSET, UTC_OFFSET_DST, NTP_SERVER);
setTimezone("CET-1CEST,M3.5.0,M10.5.0/3");
//getLocalTime(&timeinfo); // start with correct time from NTP server
//Sekunde = timeinfo.tm_sec;
//Minute = timeinfo.tm_min;
//Stunde = timeinfo.tm_hour;
//AusgabeZeit();
Stunde = 10; // start with a given time
Minute = 0;
Sekunde = 0;
}
// loop() function -- runs repeatedly as long as board is on ---------------
void loop() {
// Uhrzeit bestimmen (grob mit millis(), exakt wird es mit Timer)
if (millis() >= NextTime) {
NextTime = millis() + msPerSec;
AusgabeZeit();
Sekunde ++;
if ( Sekunde == 60 ) {
Sekunde = 0;
Minute ++;
if ( Minute == 60 ) {
Minute = 0;
Stunde ++;
if ( Stunde == 24 ) Stunde = 0;
}
}
}
// Stellen der Uhrzeit über die serielle Schnittstelle
// Sende ? und der Controller antwortet mit seiner Uhrzeit
// Sende !12:05:50 und der Controller übernimmt die Uhrzeit 12:05:50
if (Serial.available() != 0) {
char Zeichen = Serial.read();
if (Zeichen == '?') { // Befehl '?' Uhrzeit ausgeben
Serial.printf("Uhrzeit: %2d:%02d:%02d \n", Stunde, Minute, Sekunde);
}
if (Zeichen == '!') { // Befehl '!' Uhrzeit stellen
Stunde = Serial.parseInt();
if (Stunde > 23) Stunde = 0;
Minute = Serial.parseInt();
if (Minute > 59) Minute = 0;
Sekunde = Serial.parseInt();
if (Sekunde > 59) Sekunde = 0;
Serial.flush(); // restliche Zeichen im Puffer löschen
}
if (Zeichen == 'm') { // Betriebsart einstellen
Mode = Serial.parseInt();
if (Mode > 2) Mode = 0;
}
if (Zeichen == 'h') { // Helligkeit einstellen
int dimm = Serial.parseInt();
if (dimm <= 255) strip.setBrightness(dimm);
}
if (Zeichen == 'd') { // Demo-Modus, Uhr läuft schneller
msPerSec = 10;
}
if (Zeichen == 'n') { // Normal-Modus
msPerSec = 1000;
// jetzt muss die Uhrzeit wieder gestellt werden!
}
if (Zeichen == 'z') { // Zeit vom Zeitserver holen
msPerSec = 1000;
// jetzt wird die Uhrzeit vom NTP-Server gelesen
getLocalTime(&timeinfo);
AusgabeZeit();
}
}
}
void AusgabeZeit ( ) {
int Sekunde_Pixel, Minute_Pixel, Stunde_Pixel, Stunde_Anteil;
Sekunde_Pixel = (map ( Sekunde, 0, 59, 0, LED_RING1 - 1) ) % LED_RING1;
//Sekunde_Pixel = (map ( Sekunde, 0, 59, 0, LED_RING1 - 1) + 30) % LED_RING1; // connector on top
Minute_Pixel = (map ( Minute, 0, 59, 0, LED_RING1 - 1) ) % LED_RING1;
//Minute_Pixel = (map ( Minute, 0, 59, 0, LED_RING1 - 1) + 30) % LED_RING1;
// Umrechnung der 12h Zeit in Minuten. (0:00 = 0 Min, 11:59 = 719 Min)
Stunde_Anteil = (Stunde % 12) * 60 + Minute; // für 12h gibt es 12*60 Minuten
Stunde_Pixel = (map( Stunde_Anteil, 0, 719, 0, LED_RING1 - 1) ) % LED_RING1;
//Stunde_Pixel = (map( Stunde_Anteil, 0, 719, 0, LED_RING1 - 1) + 30) % LED_RING1;
strip.clear();
// Stunden-Ticks zeichnen
for (int k = 0; k < LED_RING1; k += LED_RING1 / 12)
strip.setPixelColor( k, 0xeeeeee); // Stunden-Positionen 1. Ring
for (int k = 0; k < LED_RING2; k += LED_RING2 / 12)
strip.setPixelColor(LED_RING1 + k, 0xaaaaaa); // Stunden-Positionen 2. Ring
// innerer Ring mit 40 LEDs nur für 4 Positionen geeignet
for (int k = 0; k < LED_RING3; k += LED_RING3 / 4)
strip.setPixelColor(LED_RING1 + LED_RING2 + k, 0x999999); // 3h/6h/9h/12h-Position 3. Ring
if (Mode == 0) { // Nur Grundfarben ausgeben
strip.setPixelColor(Sekunde_Pixel, ColorSekunde);
strip.setPixelColor(Minute_Pixel, ColorMinute); // Minute überschreibt Sekundenpixel
strip.setPixelColor(Stunde_Pixel, ColorStunde); // Stunde überschreibt darunter liegendes Pixel
}
else if (Mode == 1) { // Mischfarbe bei gleichem Pixel
int colorSt = ColorStunde, colorMi = ColorMinute, colorSe = ColorSekunde;
if (Sekunde_Pixel == Minute_Pixel) {
colorSe = colorMi = 0x00FFFF;
}
if (Sekunde_Pixel == Stunde_Pixel) {
colorSe = colorSt = 0xFF00FF;
}
if (Stunde_Pixel == Minute_Pixel) {
colorSt = colorMi = 0xFFFF00;
if (Stunde_Pixel == Sekunde_Pixel) { // alle 3 gleich!
colorSt = colorMi = colorSe = 0xFFFFFF;
}
}
strip.setPixelColor(Sekunde_Pixel, colorSe);
strip.setPixelColor(Minute_Pixel, colorMi); // Minute überschreibt Sekundenpixel
strip.setPixelColor(Stunde_Pixel, colorSt); // Stunde überschreibt darunter liegendes Pixel
}
else if (Mode == 2) { // Mischfarbe bei gleichem Pixel, Sekunde hat einen Schweif
int colorSt = ColorStunde, colorMi = ColorMinute, colorSe = ColorSekunde;
if (Sekunde_Pixel == Minute_Pixel) {
colorSe = colorMi = 0x00FFFF;
}
if (Sekunde_Pixel == Stunde_Pixel) {
colorSe = colorSt = 0xFF00FF;
}
if (Stunde_Pixel == Minute_Pixel) {
colorSt = colorMi = 0xFFFF00;
if (Stunde_Pixel == Sekunde_Pixel) { // alle 3 gleich!
colorSt = colorMi = colorSe = 0xFFFFFF;
}
}
strip.setPixelColor((Sekunde_Pixel + LED_RING1 - 5) % LED_RING1, 0x00000F);
strip.setPixelColor((Sekunde_Pixel + LED_RING1 - 4) % LED_RING1, 0x00001F);
strip.setPixelColor((Sekunde_Pixel + LED_RING1 - 3) % LED_RING1, 0x00002F);
strip.setPixelColor((Sekunde_Pixel + LED_RING1 - 2) % LED_RING1, 0x00004F);
strip.setPixelColor((Sekunde_Pixel + LED_RING1 - 1) % LED_RING1, 0x00008F);
strip.setPixelColor(Sekunde_Pixel, colorSe);
strip.setPixelColor(Minute_Pixel, colorMi); // Minute überschreibt Sekundenpixel
strip.setPixelColor(Stunde_Pixel, colorSt); // Stunde überschreibt darunter liegendes Pixel
}
strip.show();
}
///////////////////
// (re-)start WiFi
///////////////////
void WiFiStart()
{
// Connect to WiFi network
Serial.println();
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
//WiFi.begin("Wokwi-GUEST", "", 6);
WiFi.begin(ssid, password, channel);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
// Print the IP address
Serial.println(WiFi.localIP());
}
void setTimezone(String timezone){
Serial.printf(" Setting Timezone to %s\n",timezone.c_str());
setenv("TZ",timezone.c_str(),1); // Now adjust the TZ. Clock settings are adjusted to show the new local time
tzset();
}
void colorWipe(uint32_t color, int wait) {
for (int i = 0; i < strip.numPixels(); i++) { // For each pixel in strip...
strip.setPixelColor(i, color); // Set pixel's color (in RAM)
strip.show(); // Update strip to match
delay(wait); // Pause for a moment
}
}
Taster2
Taster4
LED32
LED33
ESP32 Schulboard mit LEDs und Tastern, I2C-Bus mit Standard-Belegung
SSD1306 Display
WS2812 an Pin26