adafruit / Adafruit_CircuitPython_NTP

Network Time Protocol (NTP) Helper for CircuitPython
MIT License
9 stars 18 forks source link

Incorrect time retrieved + unable to set timezone #12

Closed gromgit closed 3 years ago

gromgit commented 3 years ago

I just installed CircuitPython v5.3.1 on my PyPortal Pynt, and updated the lib directory on the Pynt with the corresponding files/dirs from adafruit-circuitpython-bundle-5.x-mpy-20201010.zip, so everything's as up-to-date as reasonable.

I then installed the following secrets.py:

secrets = {
    'ssid' : 'MySSID',
    'password' : 'MyPassword',
    'timezone' : 'Asia/Singapore',
    }

and the following simple clock code.py, based on https://github.com/imekon/weather-clock :

import board
import busio
import time
import json

from digitalio import DigitalInOut
import adafruit_esp32spi.adafruit_esp32spi_socket as socket
from adafruit_esp32spi import adafruit_esp32spi
import adafruit_requests as requests

from rtc import RTC
from adafruit_ntp import NTP

import displayio
from adafruit_bitmap_font import bitmap_font
from adafruit_display_text import label

# Get wifi details and more from a secrets.py file
try:
    from secrets import secrets
except ImportError:
    print("WiFi secrets are kept in secrets.py, please add them there!")
    raise

# If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)

spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset)

requests.set_socket(socket, esp)

if esp.status == adafruit_esp32spi.WL_IDLE_STATUS:
    print("ESP32 found and in idle mode")

print("Firmware vers.", esp.firmware_version)
print("MAC addr:", [hex(i) for i in esp.MAC_address])

print("Connecting to AP...")
# This copes with two AP's, trying each in turn
choice = 0
while not esp.is_connected:
    try:
        if choice == 0:
            esp.connect_AP(secrets["ssid"], secrets["password"])
        elif choice == 1:
            esp.connect_AP(secrets["ssid2"], secrets["password2"])
    except RuntimeError as e:
        print("could not connect to AP, retrying: ", e)
        choice += 1
        if choice >= 2:
            choice = 0
        continue

print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi)

# Get NTP time, may make several attempts
ntp = NTP(esp)
ntp.set_time()
while not ntp.valid_time:
    print("time not valid...")
    time.sleep(5)
    ntp.set_time(60 * 60)

# Get the RTC time, not NTP updates RTC silently
rtc = RTC()
print("{:02}/{:02}/{:04} {:02}:{:02}".format(
    rtc.datetime.tm_mday, rtc.datetime.tm_mon, rtc.datetime.tm_year,
    rtc.datetime.tm_hour, rtc.datetime.tm_min
))

# Fonts should already be on your PyPortal for the original software installed
font = bitmap_font.load_font("fonts/Arial-16.bdf")
big_font = bitmap_font.load_font("fonts/Arial-Bold-24.bdf")

vert_spacing = 30

background_bitmap = "/pyportal_startup.bmp"

# gore card.bmp should be replaced with a suitable 320x240 bitmap
with open(background_bitmap, "rb") as bitmap_file:
    bitmap = displayio.OnDiskBitmap(bitmap_file)
    tile_grid = displayio.TileGrid(bitmap, pixel_shader=displayio.ColorConverter())

    time_area = label.Label(big_font, text = "{:02}:{:02}:{:02}".format(
        rtc.datetime.tm_hour, rtc.datetime.tm_min, rtc.datetime.tm_sec), color=0xff00ff, max_glyphs = 30)
    time_area.x = 10
    time_area.y = 15

    date_area = label.Label(font, text = "{:02}/{:02}/{:04}".format(
        rtc.datetime.tm_mday, rtc.datetime.tm_mon, rtc.datetime.tm_year), max_glyphs = 30)
    date_area.x = 10
    date_area.y = 50

    epoch_area = label.Label(font, text = "epoch = {:10}".format(0), max_glyphs = 30)
    epoch_area.x = 10
    epoch_area.y = 50 + vert_spacing

    text_group = displayio.Group(max_size = 7)
    text_group.append(tile_grid)
    text_group.append(time_area)
    text_group.append(date_area)
    text_group.append(epoch_area)

    board.DISPLAY.show(text_group)

    while True:
        epoch = time.time()
        ltime = time.localtime(epoch)
        time_area.text = "{:02}:{:02}:{:02}".format(
            ltime.tm_hour, ltime.tm_min, ltime.tm_sec)
        epoch_area.text = "epoch = {:10}".format(epoch)
        time.sleep(1)

The code runs correctly, but the time displayed is exactly 7 hours behind my local time, despite my double-checking the timezone setting in secrets.py. Also, I confirmed that the epoch time displayed on the Pynt is exactly 1 hour (3,600 seconds) ahead of the corresponding epoch on my Linux box.

Therefore, two things seem to be going wrong:

  1. the timezone setting in secrets.py appears to have no effect, so local time = UTC
  2. the "UTC" retrieved via NTP is somehow 1 hour ahead of actual time

Am I doing something wrong in the code, or are these bugs in CircuitPython and/or its libraries?

gromgit commented 3 years ago

Forgot to mention: I also ran the sample code in this project's README (https://github.com/adafruit/Adafruit_CircuitPython_NTP/blob/master/README.rst), and it also returns epoch times that are 1 hour ahead of real time.

gromgit commented 3 years ago

Apologies, it turned out to be a false alarm. I just noticed the following in the code I posted:

    ntp.set_time(60 * 60)

:man_facepalming:

brentru commented 3 years ago

the timezone setting in secrets.py appears to have no effect, so local time = UTC

The timezone in secrets.py is used for the Adafruit IO time service in the PyPortal library (https://github.com/adafruit/Adafruit_CircuitPython_PyPortal), not this library.

the "UTC" retrieved via NTP is somehow 1 hour ahead of actual time

I'm not exactly sure what's causing the error, but I'd like to find out.

It likely wouldn't be a bug in CircuitPython - this library sends a command to the ESP32, running nina-fw, and is handled by the getTime method in nina-fw (https://github.com/adafruit/nina-fw/blob/master/main/CommandHandler.cpp#L809). I believe this uses the ESP32 SNTP which queries pool.ntp.org.

gromgit commented 3 years ago

Yeah, the bug turned out to be in the code I copied. I'm not sure why running the NTP example code after I discovered the issue still returned the incorrect epoch, but since an overnight power-cycle seems to have made that problem go away, I think I'm good.