Beim suchen nach Projektideen mit den LEDs ( WS2812b ) bin ich vor langer Zeit auf eine Wlan Uhr mit WS2812b gestoßen, dieses Projekt wurde in eine herkömmliche Analoge Uhr  untergebracht.

Dies gefiel mir aber vom Aussehen und Umsetzung allerdings gar nicht.
Also setzte ich mich hin und realisierte auf dem Papier eine Standuhr aus Holz, inkl Temperaturfühler und einem Meter LED Strip ( WS2812b ), die Temperatur wird visuell auch nochmal auf einen kleinen 7 Segment Display angezeigt, so brauch ich nicht ständig auf Handy schauen wie warm oder kalt mein Wohnzimmer ist 🙂

Zum Einsatz kommt ein NodeMCU V2 ( V3 geht natürlich auch ) Ein DHT22/11 und natürlich ein Meter mit den LEDs WS2812b ( + 220Ohm Widerstand für die Datenleitung ) und ein 5V Netzteil + 1000er Kondensator + 7 Segment Display.

Teileliste:

NodeMCU V2/3
DHT22/DHT11
LEDs ( 60Stück/Meter ) 1 Meter. ( RGB 3 PIN 5V )
Widerstand 220ohm
Kondensator 1000
5V Netzteil ( min 1,5A )
7 Segment Display ( Direktes Anzeigen der Temperatur ) IC2
3 Holzplatten ( Alte Regalböden etc )
1 Holzstab rund
Bisschen Holzfarbe.

Wie wird es gemacht?

Der „Uhrenring“ wurde aus einem Buchen geleimten Stück geschnitten,
ein zweiter Ring mit größeren Durchmesser wurde als Rückwand angebracht.
Ein dritter Ring ( diesmal geschlossen ) wurde für den Boden ausgeschnitten.

Ich habe es ganz einfach mit einem Zirkel und einer Stichsäge umgesetzt.

Für einen Meter LED gilt folgende Formel:
Formeln:
d = 2 r
u = 2 π r
A = π r²

Kreiszahl pi:
π = 3.141592653589793

Ergebnis:
Radius(r): 15,9155 cm
Durchmesser(d): 31,831 cm

Nun werden die Ringe miteinander verklebt ( alternativ Schrauben ) und oben
( ziemlich mittig ) ein 8er Loch gebohrt. ( Kabeldurchführung )

Nun muss der Holzstab angepasst werden, so das er in dem untersten Ring greift, dazu mit einen Stift und Winkel ( Alternativ Lineal ) den Stab teilen, und aussägen.

Kleben oder Schrauben.

Den Boden habe ich verschraubt ( bitte vorbohren, ist sauberer )
Anschließend lackieren wir das ganze in Wunschfarbe.
Ich habe meine Uhr in Schwarz lackiert.

Den NodeMCu und den Sensor: DHT22 werden auf der Rückseite verklebt oder verschraubt.

Scripte und Anweisungen: 

Haltet eurer Wlan Passwort und euer Wlan -Name bereit, den nun Pflegen wir die Scripte ein.

Es werden folgende Bibliotheken für den Betrieb benötigt:

<Adafruit_Sensor.h>      —– > Im Bibliotheken Manager zu finden
<Adafruit_NeoPixel.h>  —– > Im Bibliotheken Manager zu finden
<ESP8266WiFi.h>          —– > Im Bibliotheken Manager zu finden
„TimeClient.h“                 —– > Ist im Zip Archiv beigefügt.
<DHT.h>                          —– > Im Bibliotheken Manager zu finden
<TM1637Display.h>      —– > Im Bibliotheken Manager zu finden
<PubSubClient.h>         —– > Im Bibliotheken Manager zu finden

Wir benötigen folgende Scripte ( Achtung!! ich übertrage die Werte per MQTT an meinen Server, habt Ihr keinen Dienst im Hintergrund laufen, entfernt bitte die MQTT Einträge )

Wlanuhr

#include <Adafruit_Sensor.h>
#include <Adafruit_NeoPixel.h>
#include <ESP8266WiFi.h>
#include "TimeClient.h"
#include <DHT.h>
#include <TM1637Display.h>
#include <PubSubClient.h>

#define CLK D3 // Pinbelegung für das 7 Segment Display - ggf. anpassen 
#define DIO D4 // Pinbelegung für das 7 Segment Display - ggf. anpassen 
#define PIN D5 // Pinbelegung für den LED Streifen - ggf. anpassen 
#define DHTPIN D2 // Pinbelegung für den DHT22 Sensor -  ggf. anpassen 
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
char tmp[50];
char hum[50];
int tellstate = 0;
long lastUpdate = millis();
long lastSecond = millis();
int ledsInString = 60;
String hours, minutes, seconds;
int currentSecond, currentMinute, currentHour;
int NUMPIXELS = 60; // 1 Meter LED = 60 LED/m
int i = 0;
int hmark = 1;
char ssid[] = "Mein Netzwerkname";  //  SSID Name ( Wlan Funkname )
char pass[] = "Mein Wlan Passwort";       // Wlan Passwort
const char* mqtt_server = "Broker IP Adresse"; //Broker IP Address
const char* mqttUser = "MQTT User"; // MQTT Username
const char* mqttPassword = "MQTT Passwort"; // MQTT Server Passwort
WiFiClient espClient; 
PubSubClient client(espClient); 
TM1637Display display(CLK, DIO);
const float UTC_OFFSET = 1;
TimeClient timeClient(UTC_OFFSET);
Adafruit_NeoPixel strip = Adafruit_NeoPixel(ledsInString, PIN);

void connect_to_MQTT() {
 client.setServer(mqtt_server, 1886);//MQTT Server Details, inkl PORT ( Bitte euren Port dort eintragen )
  client.setCallback(callback);

  if (client.connect("WlanUhr_Temp" , mqttUser, mqttPassword)) {
    Serial.println("Verbinde zum MQTT Server");
    client.subscribe("wlan/sensor");
  } else {
    Serial.println("zu MQTT Server nicht verbunden ");
  }
}

void setup()
{ 
  connect_to_MQTT();
  dht.begin();
  Serial.begin(115200);
  Serial.println();
  Serial.println();
  uint8_t data[] = { 0xff, 0xff, 0xff, 0xff };
  display.setBrightness(0x0f);

  // 7 Segment AN stellen 
  display.setSegments(data);
  strip.begin();
  strip.setBrightness(128);
  strip.show();

  // Netzwerk wird verbunden
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, pass);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");

  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  timeClient.updateTime();
  updateTime() ;
  lastUpdate = millis();
  lastSecond = millis();
}

void loop()
{ 
  float Luftfeuchtigkeit = dht.readHumidity(); //unter „Luftfeutchtigkeit“ speichern
  float Temperatur = dht.readTemperature();//unter „Temperatur“ speichern
  //Serial.print("Temperatur: ");
//Serial.print(Temperatur);
//Serial.println(" Grad Celsius");
  display.showNumberDec(Temperatur,false,4,0);

  //display.showNumberDec(0x00,false,4,1);
  //uint8_t segto;
  //int value = 1244;
  //segto = 0x80 | display.encodeDigit((value / 100)%10);
  //display.setSegments(&segto, 1, 1);
  //display.showNumberDec(grad,true,2,2);
  updateTime();
    if(currentHour > 11) currentHour -= 12; // 12 Stunden, wenn Stunde 13-23, ändere auf 0-11
    currentHour = (currentHour*60 + currentMinute) / 12;   
    zeiger();

    
    strip.setPixelColor(currentHour, 0xff0000);strip.setPixelColor(currentHour-1, 0xff0000);strip.setPixelColor(currentHour+1, 0xff0000);
//    strip.setPixelColor(hourval-2, 0x001010);strip.setPixelColor(hourval+2, 0x001010);
    strip.setPixelColor(currentMinute, 0x00ff00);
//    strip.setPixelColor(minuteval-1, 0x200020);strip.setPixelColor(minuteval+1, 0x200020);
    strip.setPixelColor(currentSecond, 0x0000ff);//strip.setPixelColor(secondval-1, 0x002F00);strip.setPixelColor(secondval+1, 0x002F00);
    
    strip.show();
    
    strip.setPixelColor(currentHour, 0x000000);strip.setPixelColor(currentHour-1, 0x000000);strip.setPixelColor(currentHour+1, 0x000000);
    strip.setPixelColor(currentHour-2, 0x000000);strip.setPixelColor(currentHour+2, 0x000000); 
    strip.setPixelColor(currentMinute, 0x000000);strip.setPixelColor(currentMinute-1, 0x000000);strip.setPixelColor(currentMinute+1, 0x000000);
    strip.setPixelColor(currentSecond, 0x000000);//strip.setPixelColor(secondval-1, 0x000000);strip.setPixelColor(secondval+1, 0x000000);
  
  delay(25);
  client.loop();
  if (!client.connected()) {
    Serial.println("Keine Verbindung zum MQTT Server....");
    connect_to_MQTT();
  }
  if ( (millis() - tellstate) > 60000 ) {
    getTemperature();
    tellstate = millis();
  }
  }


void updateTime() 
{
  hours = timeClient.getHours();
  minutes = timeClient.getMinutes();
  seconds = timeClient.getSeconds();
  currentHour = hours.toInt();
  if (currentHour >= 12) currentHour = currentHour - 12;
  currentMinute = minutes.toInt();
  currentSecond = seconds.toInt();
  lastUpdate = millis();
}

void zeiger() {
if(hmark > 0) {
    for(int i = 0; i<12; i++) {
      strip.setPixelColor((5*i), strip.Color(5,5,5));
    }
  }
  
}

void callback(char* topic, byte* payload, unsigned intlength) // Reservere
{
  Serial.print("Messageved in topic: ");
  Serial.println(topic);
}
  

void getTemperature() {
  float h = dht.readHumidity();
  // Lese Temperatur in Grad (default)
  float t = dht.readTemperature();

  // Wenn keine Daten vorhanden sind, versuch es nochmal.
  if (isnan(h) || isnan(t)) {
    Serial.println("Keine Daten vom DHT Sensor!");
    return;
  }

  //Temp as string
  itoa(t,tmp,10);
  client.publish("wlan/sensor/temperature",tmp);
  Serial.println(tmp);

  //Humidity as string
  itoa(h,hum,10);
  client.publish("wlan/sensor/humidity",hum);
  Serial.println(hum);
}

Jetzt benötigen wir noch die Zeit Scripte ( TimeClient.h ):

/**The MIT License (MIT)
Copyright (c) 2018 by Daniel Eichhorn, ThingPulse
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
See more at https://thingpulse.com
*/
#pragma once

#include <ESP8266WiFi.h>

#define NTP_PACKET_SIZE 48

class TimeClient {

  private:
    float myUtcOffset = 0;
    long localEpoc = 0;
    unsigned long localMillisAtUpdate;

    const char* ntpServerName = "time.nist.gov";
    unsigned int localPort = 2390;

    byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets


  public:
    TimeClient(float utcOffset);
    void updateTime();
    void setUtcOffset(float utcOffset);
    String getHours();
    String getMinutes();
    String getSeconds();
    String getFormattedTime();
    long getCurrentEpoch();
    long getCurrentEpochWithUtcOffset();

};

Die TimeClient.h benötigt noch die TimeClient.cpp :

/**The MIT License (MIT)
Copyright (c) 2018 by Daniel Eichhorn, ThingPulse
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
See more at https://thingpulse.com
*/

#include "TimeClient.h"

TimeClient::TimeClient(float utcOffset) {
  myUtcOffset = utcOffset;
}

void TimeClient::setUtcOffset(float utcOffset) {
  myUtcOffset = utcOffset;
}

void TimeClient::updateTime() {
  WiFiClient client;
  const int httpPort = 80;
  if (!client.connect("google.com", httpPort)) {
    Serial.println("connection failed");
    return;
  }

  // This will send the request to the server
  client.print(String("GET / HTTP/1.1\r\n") +
               String("Host: google.com\r\n") +
               String("Connection: close\r\n\r\n"));
  int repeatCounter = 0;
  while(!client.available() && repeatCounter < 10) {
    delay(1000);
    Serial.println(".");
    repeatCounter++;
  }

  String line;

  int size = 0;
  client.setNoDelay(false);
  while(client.available() || client.connected()) {
    while((size = client.available()) > 0) {
      line = client.readStringUntil('\n');
      line.toUpperCase();
      // example:
      // date: Thu, 19 Nov 2015 20:25:40 GMT
      if (line.startsWith("DATE: ")) {
        Serial.println(line.substring(23, 25) + ":" + line.substring(26, 28) + ":" +line.substring(29, 31));
        int parsedHours = line.substring(23, 25).toInt();
        int parsedMinutes = line.substring(26, 28).toInt();
        int parsedSeconds = line.substring(29, 31).toInt();
        Serial.println(String(parsedHours) + ":" + String(parsedMinutes) + ":" + String(parsedSeconds));

        localEpoc = (parsedHours * 60 * 60 + parsedMinutes * 60 + parsedSeconds);
        Serial.println(localEpoc);
        localMillisAtUpdate = millis();
      }
    }
  }

}

String TimeClient::getHours() {
    if (localEpoc == 0) {
      return "--";
    }
    int hours = ((getCurrentEpochWithUtcOffset()  % 86400L) / 3600) % 24;
    if (hours < 10) {
      return "0" + String(hours);
    }
    return String(hours); // print the hour (86400 equals secs per day)

}
String TimeClient::getMinutes() {
    if (localEpoc == 0) {
      return "--";
    }
    int minutes = ((getCurrentEpochWithUtcOffset() % 3600) / 60);
    if (minutes < 10 ) {
      // In the first 10 minutes of each hour, we'll want a leading '0'
      return "0" + String(minutes);
    }
    return String(minutes);
}
String TimeClient::getSeconds() {
    if (localEpoc == 0) {
      return "--";
    }
    int seconds = getCurrentEpochWithUtcOffset() % 60;
    if ( seconds < 10 ) {
      // In the first 10 seconds of each minute, we'll want a leading '0'
      return "0" + String(seconds);
    }
    return String(seconds);
}

String TimeClient::getFormattedTime() {
  return getHours() + ":" + getMinutes() + ":" + getSeconds();
}

long TimeClient::getCurrentEpoch() {
  return localEpoc + ((millis() - localMillisAtUpdate) / 1000);
}

long TimeClient::getCurrentEpochWithUtcOffset() {
  return round(getCurrentEpoch() + 3600 * myUtcOffset + 86400L) % 86400L;
}

 

UPDATE zum SCRIPT 

in der TimeClient.cpp muss zwingend folgender Teil geändert werden wenn man den Core 2.5 oder neuer verwendet:

long TimeClient::getCurrentEpochWithUtcOffset() {
  return round(getCurrentEpoch() + 3600 * myUtcOffset + 86400L) % 86400L;
}

in

long TimeClient::getCurrentEpochWithUtcOffset() {
  return fmod(round(getCurrentEpoch() + 3600 * myUtcOffset + 86400L), 86400L);
}

Alles unter 2,5 ( Core 2.4.2 zum Beispiel ) kann die Zeile so belassen werden.

Ansonsten werdet Ihr den Fehler:
invalid operands of types ‚double‘ and ‚long int‘ to binary ‚operator%‘
erhalten. 

 

Flasht alles auf den NodeMCU und testest es „on the fly“, geht alles reibungslos, kann alles verklebt/verschraubt werden.

Anbei paar Bilder:

Hier mal ein kleines Video vom fast fertigen Projekt:

Anbei noch die ZP Datei:

wlanUhr_seite

Das Projekt benötigt ein wenig mehr Zeit, alleine die Holzarbeiten benötigen viel Aufmerksamkeit, man möchte ja eine runde Uhr und kein Ei 😉
Wer natürlich auf Professionelle Schneidwerkzeuge zurückgreifen kann, sollte dies auch tun, das Ergebnis dankt es einem 🙂

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.