Linear clock does not show all LEDs when starting (WS2811)

I see your problem. I was thinking of it like the RTC, which can be pounded without cost. Hitting the time 30x per second might be, um, rude,

There should be a way to avoid the delay and have a glitch-free clock, important if you are a perfectionist (!) but also if you ever want the loop to be running freely to accomplish other tasks.

There is a library that can keep a local RTC synched with network served time, which service would need to be very infrequently (relatively speaking) hit.

But this is outside my own experience. I will say that I am onto this trick because I build all kindsa clocks, and have become very sensitive to missed updates or those that come at anything other than every 1.0000000000000000 seconds. :wink:

a7

1 Like

The ESP32 Arduino Core has a software RTC built in, you don't need to use other libraries, and it's simple to use. Here's some example code, courtesy of forum member @noiasca :

https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm

This software RTC, by default, only fetches time from the NTP servers once per hour to keep itself accurate. You can get the current time as often as you like without impacting your WLAN.

3 Likes

There's another way to control the r, g & b channels of each led independently which may be easier to understand than accessing the bytes with pointers, and easier than using complex logic to decide whether to set the pixel to red, yellow or white:

  // ...or as members of a three-element array:
  leds[i][0] = 50;  // red
  leds[i][1] = 100; // green
  leds[i][2] = 150; // blue

(See this page)

This would let you control, for example, the seconds LEDs like this:

  //Einersekunden
  for (byte s = 0; s < secondsL; s++)
    leds[s/3][s%3] = 255;
1 Like

+1
NTP is built into the new ESP core.
The libraries for the NTP code can be removed and the code reduced to a few lines.
This is basically all you need.

#include <WiFi.h> // ESP32
time_t now; // epoch
tm tm; // time struct

void setup() {
  configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "de.pool.ntp.org"); // https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
}

void loop() {
  time(&now); // epoch
  localtime_r(&now, &tm); // local offset
}

No NTPClient, no timeClient, no WiFiUDP etc.
The structure elements tm.tm_hour, tm.tm_min, tm,tm_sec now contain the time.
Leo..

1 Like

Nice.

So now the idea of watching for the change of seconds works, meaning you retain the ability to exploit a freely running loop should you need.

void loop () {

// control passes here very frequently 

  time(&now); // epoch
  localtime_r(&now, &tm); // local offset 

  static int lastDisplayed = -1;  // impossible initial value

  if (tm.tm_sec == lastDisplayed) return;  // not time yet

  lastDisplayed = tm.tm_sec;

// rest of the loop runs once a second on the second


a7

1 Like

I add a few more lines to my clock.

  1. A callback function with while loop, to wait for a valid time.
  2. An if statement, to only do things when time has changed.

This is currently my basic clock.
Leo..

#include <WiFi.h> // ESP32
unsigned long prevMillis, prevTime;
bool reSync;
time_t now; // epoch
tm tm; // time struct

void cbSyncTime(struct timeval *tv) { // sync callback
  reSync = true;
}

void setup() {
  configTzTime("CET-1CEST,M3.5.0,M10.5.0/3", "de.pool.ntp.org"); // https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv
  while (!reSync) yield(); // wait here for a valid time string
}

void loop() {
  time(&now); // epoch
  localtime_r(&now, &tm); // local offset
  if (now != prevTime) { // if time has changed
    prevTime = now; // remember
    printf("\n%02u:%02u:%02u", tm.tm_hour, tm.tm_min, tm.tm_sec);
  }
}
1 Like

You don't use prevMillis, and technically prevTime should be of type time_t.

I'm old so I have no problem with the code as is, but time_t is not guaranteed to be anything.

a7

Forgot to remove. The sketch is a simplified version of a larger clock sketch.

Not sure what you mean with time_t.
It is only used to see if now has changed, and could be a bool of the LSB of now.
Leo..

Thank you all for so many solutions you have suggested.
This one seems to be a convincing solution, even if I don't understand the formula at all. I'll have to look into it more closely.
I will also try to implement the built-in RTC with the hourly adjustment.

Now that the sketch is running, it was time to adjust the layout of the board. I've added a WS2811 so that none of them have an overarching function (seconds to minutes or minutes to hours). But that also means that one blue channel remains unused in each area.
Next step will be to order the pcb and look how it works.

I changed that from 1 hour (default) to ~10-hourly NTP updates.
My ESP32 stayed within one or two seconds in that time.
If... you want to change it, then study the link in post#22.
Leo..

1 Like

This is my attempt with directly addressing the individual bytes in the leds array.
The only major concern I see is if the red/green/blue order of the array does not match the actual wiring of the LEDs.

//byte location of LEDs within leds array
const byte offset_1s  =  0; // 0- 8
const byte offset_10s =  9; // 9-13
const byte offset_1m  = 15; //15-23
const byte offset_10m = 24; //24-28
const byte offset_1h  = 30; //30-38
const byte offset_10h = 39; //39-40

void loop () {
  timeClient.update();

  int hours = timeClient.getHours();
  int minutes = timeClient.getMinutes();
  int seconds = timeClient.getSeconds();

  Serial.printf(" \tUhrzeit: %02d:%02d:%02d \n", hours, minutes, seconds);

  FastLED.clear();
  setLeds(seconds % 10, offset_1s);
  setLeds(seconds / 10, offset_10s);
  setLeds(minutes % 10, offset_1m);
  setLeds(minutes / 10, offset_10m);
  setLeds(hours % 10, offset_1h);
  setLeds(hours / 10, offset_10h);

  FastLED.show();
  FastLED.delay(1000);
}

void setLeds(byte num, byte offset) {
  //prevent writing past end of array
  if ((offset + num) > sizeof(leds))
    num = sizeof(leds) - offset;
  byte* ptr = (byte*)&leds + offset;
  for (byte i = 0; i < num; i++)
    *ptr++ = 0xFF;
}
2 Likes

I stepped past my knowledge and experience.

I thought that it would be a Good Idea for the type you use for time, here time_t to be the same for all variables used for time.

Since time_t is implementation dependent, you can't rely on it being a 32 bit integer.

In this instance, it is actually a signed integer, and you compare it to an unsigned integer, which elicits a warning.

In other cases it's a 64 bit integer.

Theoretically, it could be implemented in any conceivable way, and assuming it's an integer of any size and signedness is just wrong. You aren't supposed to know, or need to care, what it is or how it works beyond what the API provides.

So for portability.

Now I was shocked (shocked!) to discover that typedef does not invoke strong type checking, a hallmark of C++… if the base type is integer you get no special help from the compikler.

Which led me to find very I-don't-understand-them-at all methods for creating and using new types that would enjoy strong type checking.

By mentioning my age, I mean I come from the wild past where all this strong type checking was unavailable, or underutilized. We didn't need no stinkin' type checking and if something got in our way a cast was always a means to get around things.

Yes, our arms are scarred with the bite marks from those bad habits. I appreciate that with code after a few thousand lines strong type checking is a lifes saver. I assumed that C++ would extend to typedef the ability to work within that philosophy.

So file that under I learnt, or am trying to, something around this; sry for the cage-rattle if that's what it came over like.

a7

Hi David,
thank you for your attempt. I just tried it out: the clock also runs perfectly with your solution. Disconnecting the power and reconnecting it also works without any problems.

@david_2018
@kolaha
@PaulRB

Hello everyone,
I have finally made my way through all of your posts and also worked through the linked websites. I have now summarized the most suitable approaches in a sketch that combines all the good ideas. The time is now retrieved from the NTP server every hour.

I assume that the clock will be way too bright at full brightness.
The HEX value for half brightness is #7F7F7F.

Replacing 0xFF with 0x7F in the last line will halve the brightness.
In principle, any HEX value can be used here. For the sake of simplicity, the ten values ​​0F to 9F and the six values ​​AF to FF are suitable. This corresponds to a gradation of 6.25% (1/16) per brightness level.

By using these two symbols, the brightness of the entire clock can be dimmed in 255 levels (nobody will set level 0). The HEX value can be converted from the RGB values ​​1-255 on the following website: https://www.rgbtohex.net/

Thank you all for the time you sacrificed and the valuable tips

Here is the final sketch:

/*
  Project: Linearuhr NTC Zeit Server mit ESP32
  Author: Michael Todtenbier
  Date: Created 05.07.2024
  Version: V1.1
  IDE: Arduino IDE 1.8.19

  NTC Time:   Thomas Edlinger for www.edistechlab.com
  Website:    https://edistechlab.com/ntp-server
  NTP-Update: Werner Rothschopf
  Webseite:   https://werner.rothschopf.net/microcontroller/202103_arduino_esp32_ntp_en.htm
  Thanks to:  kolaha, david_2018, PaulRB
  Webseite:   https://forum.arduino.cc/t/linear-clock-does-not-show-all-leds-when-starting-ws2811/1277873

  Required libraries (sketch -> include library -> manage libraries)
*/


#ifdef ESP8266
#include <ESP8266WiFi.h>
#else
#include <WiFi.h>
#endif
#include <time.h>
#include "esp_sntp.h"
#include <FastLED.h>

// Wi-Fi Settings
const char *wifi_ssid     = "YourSSID";
const char *wifi_password = "YourPassword";
const char * NTP_SERVER   = "de.pool.ntp.org";
const char * TZ_INFO      = "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00";

time_t now;               // Vergangene Sekunden seit dem 1. Januar 1970 - UTC
tm tm;                    // Sekunden in richtige Uhrzeit wandeln

// Configuration Neopixels
#define DATA_PIN 27       // Pin für LED-Streifen
const byte NUM_LEDS = 14; // Anzahl der WS2811-Chips
CRGB leds[NUM_LEDS];

void setup_wifi() {
  delay(10);
  WiFi.begin(wifi_ssid, wifi_password);
  int counter = 0;
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    if (++counter > 100) ESP.restart();
    Serial.print(".");
  }
  Serial.println("WiFi connected");
}

void setup () {
  Serial.begin(115200);
  setup_wifi();
  configTime(0, 0, NTP_SERVER);
  setenv("TZ", TZ_INFO, 1);
  
  sntp_set_sync_interval(60 * 60 * 1000UL); // Abfrageintervall: 1 Stunde
  sntp_set_time_sync_notification_cb(cbSyncTime);  // set a Callback function for time synchronization notification

  //FastLED aktivieren
  FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);

}

void cbSyncTime(struct timeval *tv)  { // callback function to show when NTP was synchronized
  Serial.println(F("NTP time synched"));
}

  //byte location of LEDs within leds array
  const byte offset_1s  =  0; // 0- 8
  const byte offset_10s =  9; // 9-13
  const byte offset_1m  = 15; //15-23
  const byte offset_10m = 24; //24-28
  const byte offset_1h  = 30; //30-38
  const byte offset_10h = 39; //39-40

  void loop () {
  time(&now);                       // Liest die aktuelle Zeit
  localtime_r(&now, &tm);           // Beschreibt tm mit der aktuelle Zeit  timeClient.update();

  int hours = tm.tm_hour;
  int minutes = tm.tm_min;
  int seconds = tm.tm_sec;

  Serial.printf(" \tUhrzeit: %02d:%02d:%02d \n", hours, minutes, seconds);

  FastLED.clear();
  setLeds(seconds % 10, offset_1s);
  setLeds(seconds / 10, offset_10s);
  setLeds(minutes % 10, offset_1m);
  setLeds(minutes / 10, offset_10m);
  setLeds(hours % 10, offset_1h);
  setLeds(hours / 10, offset_10h);

  FastLED.show();
  FastLED.delay(1000);
}

void setLeds(byte num, byte offset) {
  //prevent writing past end of array
  if ((offset + num) > sizeof(leds))
    num = sizeof(leds) - offset;
  byte* ptr = (byte*)&leds + offset;
  for (byte i = 0; i < num; i++)
        *ptr++ = 0x3F;    // Helligkeit 0F(6%), 1F(13%), 2F(19%), ..., 9F(69%), AF(75%), BF(81%), CF(88%), DF(94%), FF(100%)
}

@kolaha , @david_2018 , @PaulRB

Hello,

as a small thank you for your active support, I would like to present you the final result (without the disguise yet). I would never have managed this without your help!

Michael

2 Likes

nice project.

I haven't followed it so far, but some days ago I have written a sketch for the Lichtzeitpegel on the Rheintal Tower.

it could be used for a Kassel Uhr also if you comment out the delimiters and beacons.

1 Like

Vielen Dank!

Ein Teil des Sketchs beruht ja bereits auf einigen Ihrer Codezeilen, woraus ich im Sketch auch kein Geheimnis gemacht habe :wink:
Vielen Dank also auch für das Bereitstellen der Infos rund um die integrierte Software RTC!

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.