logo elektroda
logo elektroda
X
logo elektroda
Dostępna jest polska wersja

Czy wolisz polską wersję strony elektroda?

Nie, dziękuję Przekieruj mnie tam

How to Implement a Clock in OpenBeken Devices Without Using NTP Protocol

max4elektroda 6639 97
Best answers

How can I add a clock to OpenBeken devices that keeps time locally without relying on NTP, while staying compatible across platforms?

Implement it as a separate optional clock subsystem, not as NTP itself: split the generic time helpers out of the NTP driver, rename APIs to `Clock_*`, and gate the feature with a disabled-by-default `ENABLE_LOCAL_CLOCK` in `obk_config.h` so existing builds and devices are unaffected [#21031682][#21032115] Use a companion `ENABLE_LOCAL_CLOCK_ADVANCED` for DST/timezone handling if needed [#21032907] For timekeeping, base the clock on a more accurate uptime source from RTOS ticks rather than a simple `g_secondsElapsed++`, because the counter can drift and it should not be moved backward at runtime or driver logic may break [#21639787][#21639876] If you need an initial time source without NTP, the clock can be set from the browser or by an HTTP request from another device, then continue running locally [#21038303]
Generated by the language model.
ADVERTISEMENT
  • #91 21640158
    divadiow
    Level 38  
    ECR6600 1.18.156
    OpenECR6600 interface screen showing ON state and energy measurement data

    ECR6600 1768_merge_cf30e28012eb ✅
    OpenECR6600 control interface with voltage, current, and energy data
  • ADVERTISEMENT
  • #92 21640213
    p.kaczmarek2
    Moderator Smart Home
    Regarding counter role - calling Channel_Add from ISR is not probably a good idea. I will need to fix it tomorrow. Probably store deltas and collect them in quick tick...
    Helpful post? Buy me a coffee.
  • ADVERTISEMENT
  • #93 21640278
    max4elektroda
    Level 24  
    p.kaczmarek2 wrote:
    Probably store deltas and collect them in quick tick...

    Btw, anyone knows in which periods quickticks are called in real life?
    If I understood it right, it will get the real time in Beken and ESP and else assume 25ms?
    We might even here go for the "right time" with xticks and possibly even set g_secondsElapsed while at it?
    Just thinking out loud...

    Added after 1 [hours] 58 [minutes]:

    max4elektroda wrote:
    Using this for now - working on W800, will test with others soon ...

    Code: text Expand Select all Copy to clipboarddiff --git a/src/user_main.c b/src/user_main.c
    index a0ea6322..dbee5483 100644
    --- a/src/user_main.c
    +++ b/src/user_main.c
    @@ -583,6 +583,11 @@ float g_wifi_temperature = 0;
     static byte g_secondsSpentInLowMemoryWarning = 0;
     void Main_OnEverySecond()
     {
    +#if PLATFORM_W600 || PLATFORM_W800
    +#define TimeOut_t xTimeOutType 
    +#endif
    +       TimeOut_t myTimeout;    // to get uptime from xTicks 
    +
            int newMQTTState;
            const char* safe;
            int i;
    @@ -754,8 +759,9 @@ void Main_OnEverySecond()
                            }
                    }
            }
    -
    -       g_secondsElapsed++;
    +//     g_secondsElapsed++;
    +       vTaskSetTimeOutState( &myTimeout );
    +       g_secondsElapsed = (int)((((uint64_t) myTimeout.xOverflowCount << (sizeof(portTickType)*8) | myTimeout.xTimeOnEntering)*portTICK_RATE_MS ) / 1000 );
            if (bSafeMode) {
                    safe = "[SAFE] ";
            }


    Tested over night with BL602, BK7238, BK7231N, ESP32, W800, LN882H:

    Screenshots of OpenBK UIs on various IoT devices and a PTB time sync clock.


    For LN882H its 3 seconds off after 15 hours, rest ~ 1 second (plus there's a small gap in how exactly time was set).
    Only "big" difference on ESP32, which is 12 minutes(!) off.
    Maybe @insmod xTicks are "second choice" and I should try to use esp_timer_get_time() , like for quickticks?
  • #94 21640422
    p.kaczmarek2
    Moderator Smart Home
    My only worry is that now Main_OnEverySecond may either:
    - be called twice with the same g_secondsElapsed value
    - skip g_secondsElapsed values
    still, if it's not breaking anything, then acceptable?

    Related: Counter_f works on BK7238 https://www.elektroda.com/rtvforum/topic4092465-210.html#21640407
    Probably need to check on other platforms. Seems to crash on ESP32 for me?
    Helpful post? Buy me a coffee.
  • ADVERTISEMENT
  • #95 21640471
    max4elektroda
    Level 24  
    p.kaczmarek2 wrote:
    - be called twice with the same g_secondsElapsed value
    - skip g_secondsElapsed values

    both is possible, I think, but at least we will never "go back in time".

    Did a quick check, I can't see a "big" breaking point:
    
    .../OpenBeken/main/OpenBK7231T_App$ find src/ -name "*.[ch]" | xargs grep g_secondsElapsed
    src/httpserver/new_http.c:   hprintf255(request, "<br>Online for <span id=\"onlineFor\" data-initial=\"%i\">-</span>", g_secondsElapsed);
    src/httpserver/json_interface.c:   format_time(g_secondsElapsed, buff, sizeof(buff));
    src/httpserver/json_interface.c:   JSON_PrintKeyValue_Int(request, printer, "UptimeSec", g_secondsElapsed, true);
    src/httpserver/json_interface.c:   JSON_PrintKeyValue_Int(request, printer, "Uptime", g_secondsElapsed, true);
    src/httpserver/json_interface.c:   if (NTP_GetCurrentTimeWithoutOffset() > g_secondsElapsed) {   // would be negative else, leading to unwanted results when converted to (unsigned) time_t 
    src/httpserver/json_interface.c:      ntpTime = (time_t)NTP_GetCurrentTimeWithoutOffset() - (time_t)g_secondsElapsed;
    src/httpserver/rest_interface.c:   hprintf255(request, "{\"uptime_s\":%d,", g_secondsElapsed);
    src/driver/drv_ds1820_full.c:   // if (dsread == 1 && g_secondsElapsed % 5 == 2) {
    src/driver/drv_ds1820_full.c:            float t_float = 20.0 + i/10.0 + (float)(g_secondsElapsed%100)/100.0;
    src/driver/drv_ds1820_full.c:               lastconv = g_secondsElapsed;
    src/driver/drv_ds1820_full.c:               lastconv = g_secondsElapsed;
    src/driver/drv_ds1820_full.c:         if(dsread == 0 && (g_secondsElapsed % ds18_conversionPeriod == 0 || lastconv == 0))
    src/driver/drv_dht_internal.c:   uint32_t currenttime = g_secondsElapsed;
    src/driver/drv_tm_gn_display_shared.c:      segments[i] = g_digits[(g_secondsElapsed + i) % 10];
    src/driver/drv_tm_gn_display_shared.c:      segments[i] = g_digits[(g_secondsElapsed + i) % 10];
    src/driver/drv_bl_shared.c:          cJSON_AddNumberToObject(root, "uptime", g_secondsElapsed);
    src/driver/drv_ntp.c:      if (g_secondsElapsed < 60) {
    src/driver/drv_ds1820_simple.c:   hprintf255(request, "<h5>DS1820 Temperature: %.2f C (read %i secs ago)</h5>", (float)t / 100, g_secondsElapsed - lastconv);
    src/driver/drv_ds1820_simple.c:   // if (dsread == 1 && g_secondsElapsed % 5 == 2) {
    src/driver/drv_ds1820_simple.c:      lastconv = g_secondsElapsed;
    src/driver/drv_ds1820_simple.c:   if(g_secondsElapsed % ds18_conversionPeriod == 0 || lastconv == 0) //dsread == 0
    src/mqtt/new_mqtt.c:      sprintf(dataStr, "%d", g_secondsElapsed);
    src/new_common.h:extern int g_secondsElapsed;
    src/cmnds/cmd_if.c:   return g_secondsElapsed;
    src/cmnds/cmd_test.c:      cJSON_AddNumberToObject(root, "uptime", g_secondsElapsed);
    src/user_main.c:int g_secondsElapsed = 0;
    src/user_main.c:      if(g_secondsElapsed < 30)
    src/user_main.c:      if (g_secondsElapsed < 30) {
    src/user_main.c:   if (g_timeSinceLastPingReply != -1 && g_secondsElapsed > 60)
    src/user_main.c:   g_secondsElapsed++;
    src/user_main.c:         safe, g_secondsElapsed, idleCount, xPortGetFreeHeapSize(), bMQTTconnected,
    src/user_main.c:         safe, g_secondsElapsed, idleCount, xPortGetFreeHeapSize(),g_bHasWiFiConnected, g_timeSinceLastPingReply, LWIP_GetActiveSockets(), LWIP_GetMaxSockets(),
    src/user_main.c:   if (!(g_secondsElapsed % 10))
    src/user_main.c:      if (g_secondsElapsed > bootCompleteSeconds)
    src/user_main.c:   if (g_secondsElapsed < 5)
    src/hal/w800/hal_main_w800.c:   //actually starts shifting g_secondsElapsed. To compensate, we are checking how much
    


    new_http.c only printing value
    json_interface.c only printing value

    drv_ds1820_full.c used for "fake windows temperatures (no problem)
    --> we might skip one read, if we skip a second dividable by our period

    drv_dht_internal.c used as currenttime in if (!force && ((currenttime - dht->_lastreadtime) < 3)) { return dht->_lastresult; ...
    --> we might be one second late for reading

    drv_tm_gn_display_shared.c display value only
    -> display might "jump" one second if we skipped one value

    drv_ds1820_simple.c
    --> we might skip one read, if we skip a second dividable by our period

    new_mqtt.c only printing value
    cmd_if.c only printing value
    cmd_test.c only printing value
    main.c only checking against limits
    hal_main_w800.c only in comment

    Added after 27 [minutes]:

    p.kaczmarek2 wrote:
    Seems to crash on ESP32 for me?

    Is this with "Add_Channel"? I crashed ESP32 in ISR, too by using addLogAdv in it (which worked fine on W800 with much more logging during debugging)
  • #96 21640503
    p.kaczmarek2
    Moderator Smart Home
    Well if there are no drawbacks then we can merge your changes really soon. I also saw @insmod has nothing against them... so I guess we'll have them in a release :D

    I am still thinking what else can I cover with self tests for clock stuff, but not sure for now.

    Hm, addLogAdv crashes ESP? This may be it! That is with Add_Channel channel in ISR routine. I am not sure if we can just "silently" add to channel, since we also want to do MQTT publish. Maybe I will just create some kind of int addToChannels[MAX_CHANNELS] table and add it in quick ticks...
    Helpful post? Buy me a coffee.
  • ADVERTISEMENT
  • #97 21864940
    omniron
    Level 11  
    Very amazed about you guys' process and diversions, baffled this all fits in memory still.
    Is there a version I could try out?
    Just wanted to see if an update is available for my switches.
    Thanks!!
  • #98 21865147
    max4elektroda
    Level 24  
    @omniron If you are talking about the RTC driver: The driver for DS3231 is available now since some time. But it's not enabled per default on any MCU, so you will need to compile one yourself with
    "#define ENABLE_DRIVER_DS3231 1"
    set for your platform.

Topic summary

✨ The discussion revolves around implementing a local clock in OpenBeken devices without relying on the NTP protocol. The initial approach involved using a variable to track the time since startup, allowing for time calculations based on elapsed seconds. While this method lacks the accuracy provided by NTP, it offers advantages such as independence from network connectivity and reduced resource requirements. Participants provided feedback on code structure, suggested improvements for compatibility, and discussed the potential for using real-time clocks (RTC) for enhanced accuracy. Testing across various platforms, including LN882H, BL602, and W600, was conducted to evaluate the clock's performance and reliability. The conversation also touched on daylight saving time adjustments and the possibility of remote clock synchronization.
Generated by the language model.

FAQ

TL;DR: A tuned ‘local clock’ for OpenBeken now drifts only 1–2 s per day—down from 120 s—after switching to RTOS-tick math; “good enough for daily energy stats” [Elektroda, max4elektroda, post #21031093] Why it matters: You can timestamp logs even when Wi-Fi or Internet is absent.

Quick Facts

• Typical drift after optimisation: 1–2 s / day ≈ 0.002 % [Elektroda, max4elektroda, post #21031093] • Tick counter overflow period: ~50 days with 32-bit FreeRTOS ticks [Elektroda, max4elektroda, post #21063405] • Code gated by #defines ENABLE_LOCAL_CLOCK & ENABLE_LOCAL_CLOCK_ADVANCED (default OFF) [Elektroda, max4elektroda, post #21032907] • Extra flash footprint: “only some bytes” when feature enabled [Elektroda, max4elektroda, post #21035091] • Tested platforms: BK7231T/N, LN882H, BL602, W600; BL602 showed watchdog resets edge-case [Elektroda, divadiow, post #21055037]

How does the new OpenBeken clock work without NTP?

At boot the firmware stores the startup Unix time in a variable and adds the ever-growing g_secondsElapsed value, now calculated from precise RTOS ticks. This produces current time even in AP-only mode, with no network traffic [Elektroda, max4elektroda, post #21031093]

What accuracy can I expect?

Field tests show 1–2 seconds drift per 24 hours across LN882H and BK7231 chips, versus the original 2-minute drift [Elektroda, divadiow, #21054381; Elektroda, max4elektroda, #21031093].

Which microcontrollers are supported?

Source builds succeed for BK7231T/N, LN882H, BL602 and W600 once the extra driver file drv_deviceclock.c is added to each platform’s Makefile or CMakeLists.txt [Elektroda, max4elektroda, post #21031093]

How do I enable the feature?

  1. Open obk_config.h.
  2. Set ENABLE_LOCAL_CLOCK 1 (and ENABLE_LOCAL_CLOCK_ADVANCED 1 for DST logic).
  3. Re-compile and flash. The GUI now shows “Offline Time System” with a ‘Set to browser time’ button [Elektroda, max4elektroda, post #21032907]

Can I still use NTP afterwards?

Yes. NTP can initialise the clock once; if the service stops, the local clock keeps running using the stored sync point plus ticks, avoiding freeze issues seen when only NTP_OnEverySecond() drove time [Elektroda, max4elektroda, post #21031093]

How is daylight-saving time handled?

The ADVANCED flag loads a DST table modelled on Tasmota. Future commits allow runtime configuration, not just compile-time tables, saving the rule set in a new uint64 config field [Elektroda, max4elektroda, post #21063405]

Will tick counter overflow break timekeeping?

No. The algorithm uses unsigned subtraction, so when the 32-bit tick counter wraps (~50 days) the math still yields correct deltas [Elektroda, max4elektroda, post #21063405]

How can I set the clock remotely?

Send HTTP GET /cm?cmnd=clock_set%201713880000 (example) from any LAN host; a router cron job can push time after reboot, avoiding WAN access [Elektroda, max4elektroda, post #21038303]

Can one OpenBeken act as NTP server for others?

Not yet. Current firmware lacks NTP-server code, but any device can HTTP-push its local time to peers. An internal NTP server is on the roadmap [Elektroda, omniron, post #21038282]

Does the new code bloat firmware size?

Link-time optimisation drops unused functions; only entry points inside #ifdef blocks compile. Developers report negligible flash increase—“only some bytes” [Elektroda, p.kaczmarek2, #21032115; Elektroda, max4elektroda, #21035091].

What happens after a power loss?

Because no RTC is present, the clock resets. Script another node to POST the correct time at boot, or add an external RTC like DS3231 for ±1 min/year accuracy [Elektroda, omniron, post #21034483]

Is the feature ready for production?

Maintainer feedback calls the pull request “very professional… hope we can merge it soon” [Elektroda, p.kaczmarek2, post #21053508] Until merged, you must build from the PR branch.
Generated by the language model.
ADVERTISEMENT