Last active
March 31, 2024 11:06
-
-
Save PaulskPt/9570a8ee3ea50243bbf6c34ac18da45e to your computer and use it in GitHub Desktop.
Lolin S3 PRO w CircuitPython V9.0.0-rc.0 test onboard SDCard, external Lolin e-Paper Display and external Adafruit Gamepad QT
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# SPDX-FileCopyrightText: 2024 Paulus Schulinck | |
# | |
# SPDX-License-Identifier: MIT | |
############################### | |
""" | |
Platform: Lolin S3 PRO. | |
Attached hardware: | |
a) on-board: 32GB microSDCard (SPI); | |
b) external: Lolin 2.13inch Lolin e-Paper 3-color display, 250x122px (SPI); | |
c) external: Adafruit Gamepad QT (seesaw system) (I2C). | |
Update 2024-03-02. | |
Successfully entered a 32GB microSDCard in the SDCard slot on the board. I made some changes to the script below. | |
The script then successfully mounted the SDCard and showed the files on the SDCard; | |
2) Connected a Lolin 2.13inch e-Paper 3-color display, 250x122px. Needs SSD1680 driver module. | |
Downloaded and added the Adafruit_ssd1680 module. | |
I downloaded and installed adafruit_display_text module. | |
At start this script establishes WiFi connection, then tries to retrieve a datetime stamp from a NTP time server. | |
Then the internal realtime clock (RTC) will be set with the NTP datetime. | |
This script contains a State class which keeps varios data. This eliminates the use of a lot of global variables | |
(still exist twelve of them). | |
Update 2024-03-04: | |
I managed to have the microSDCard, the e-Paper Display and the Gamepad QT all three working. | |
Added functionality to refresh the e-Paper display every 4 minutes. | |
Update 2024-03-13: | |
Added a Date class, used to calculate a number of days difference between two dates. It is used to calculate | |
the number of days to the start and end of the next dst period. | |
Until now there does not exist a build of Circuitpython for the Lolin S3 PRO board. | |
Initially I used the build for the Lolin S3, however I managed to create a build of Circuitpython V9.0.0-rc.0 for | |
the Lolin S3 PRO. | |
Today a Pull Request (PR) I created has been accepted. This PR has been merged into the upstream Repo of Adafruit/Circuitpython. | |
I expect that the build for the Lolin S3 PRO will soon be available in https://circuitpython.org. | |
""" | |
import board | |
import time | |
import sys, os | |
import gc | |
import busio | |
import storage | |
import sdcardio | |
import adafruit_ssd1680 | |
import displayio | |
import fourwire | |
import terminalio | |
import microcontroller | |
import rtc | |
import wifi | |
import socketpool | |
import adafruit_ntp | |
import random | |
import neopixel as neopixel | |
from micropython import const | |
from adafruit_display_text import label | |
from adafruit_seesaw.seesaw import Seesaw | |
from dst_PRT import dst | |
time.sleep(5) # Give IDE (e.g. mu V2.1.0) time to run and open REPL. | |
mRTC = rtc.RTC() # create internal RTC object | |
epd_bus = None | |
epd = None | |
spi = None | |
sd = None | |
vfs = None | |
displayio.release_displays() | |
spi = board.SPI() | |
state = None | |
class State: | |
def __init__(self, saved_state_json=None): | |
self.my_debug = False | |
self.use_TAG = True | |
self.board_id = None | |
self.pool = None | |
# self.tft_led = board.IO14 | |
self.tft_busy = board.IO14 | |
self.tft_reset = board.IO21 | |
self.ts_cs = board.IO45 # touchscreen cs | |
self.sd_cs = board.IO46 | |
self.tft_dc = board.IO47 | |
self.tft_cs = board.IO48 | |
self.flip_show_t = False | |
self.dtg_start = None | |
self.SDCard_mounted = False | |
self.text_group = None | |
self.text_group_x = 0 | |
self.text_group_y = 0 | |
self.text_area = None | |
self.text_area_text = "" | |
self.flip_show_t = False | |
self.qt_buttons_start = True | |
self.dtg = None | |
self.dt_text_area = None | |
self.dt_text_area_text = "" | |
self.dst_offset = 0 # PT wintertime | |
self.last_epd_refresh = 0.00 | |
self.elapsed_msg_shown = False | |
self.refresh_in_t = 240 # 4 minutes until next display refresh (3 minutes or 180 seconds still gave an error) | |
self.pth = "" | |
self.tag_le_max = 24 # see tag_adj() | |
self.set_SYS_RTC = True | |
self.NTP_dt_is_set = False | |
self.SYS_RTC_is_set = False | |
self.SYS_dt = None # time.localtime() | |
self.ssid = None | |
self.ip = None | |
self.s__ip = None | |
self.mac = None | |
self.NTP_dt = None | |
self.SYS_dt = None | |
self.dst = None | |
self.last_x = 0 | |
self.last_y = 0 | |
self.msg1_shown = False | |
self.use_neopixel = True | |
self.do_blink = False | |
self.led_state = 0 | |
self.pixel = None | |
self.neopixel_brightness = 0.03 | |
self.delay = 0.5 | |
# e-paper display color defines: | |
self.disp_fg_color = 0xFF0000 | |
self.disp_bg_color = 0xFFFFFF | |
self.red_ = 0x00ff00 | |
self.grn_ = 0xff0000 | |
self.blu_ = 0x0000ff | |
self.cyan = 0x00ffff | |
self.purple = 0x00b4ff | |
self.yellow = 0xffff00 | |
self.white = 0xffffff | |
self.blk_ = 0x000000 | |
self.curr_color_set = self.blk_ | |
self.TFT_show_duration = 5 | |
# See: https://docs.python.org/3/library/time.html#time.struct_time | |
self.tm_year = 0 | |
self.tm_mon = 1 | |
self.tm_mday = 2 | |
self.tm_hour = 3 | |
self.tm_min = 4 | |
self.tm_sec = 5 | |
self.tm_wday = 6 | |
self.tm_yday = 7 | |
self.tm_isdst = 8 | |
self.TIMEZONE = "" | |
self.TZ_OFFSET = None | |
self.COUNTRY = None | |
self.STATE = None | |
self.mRTC_DOW = DOW = { | |
0: "Monday", | |
1: "Tuesday", | |
2: "Wednesday", | |
3: "Thursday", | |
4: "Friday", | |
5: "Saturday", | |
6: "Sunday", | |
} | |
self.pin_dict = { | |
"IO11": ["mosi", board.IO11], | |
"IO12": ["sck", board.IO12], | |
"IO13": ["miso", board.IO13], | |
"IO14": ["tft_busy", board.IO14], | |
"IO21": ["tft_reset", board.IO21], | |
"IO45": ["ts_cs", board.IO45], | |
"IO46": ["tf_cs", board.IO46], | |
"IO47": ["tft_dc", board.IO47], | |
"IO48": ["tft_cs", board.IO48], | |
} | |
self.qt_btns_present = False | |
self.BUTTON_SELECT = const(0) | |
self.BUTTON_B = const(1) | |
self.BUTTON_Y = const(2) | |
self.BUTTON_A = const(5) | |
self.BUTTON_X = const(6) | |
self.BUTTON_START = const(16) | |
self.button_mask = const( | |
(1 << self.BUTTON_X) # = 1 << 6 = 64 = 0x00040 | |
| (1 << self.BUTTON_Y) # = 1 << 2 = 4 = 0x00004 | |
| (1 << self.BUTTON_A) # = 1 << 5 = 32 = 0x00020 | |
| (1 << self.BUTTON_B) # = 1 << 1 = 2 = 0x00002 | |
| (1 << self.BUTTON_SELECT) # = 1 << 0 = 1 = 0x00001 | |
| (1 << self.BUTTON_START) # = 1 << 16 = 65536 = 0x10000 | |
) # = 65639 dec or 0x10067 | |
try: | |
self.TZ_OFFSET = int(os.getenv("TZ_OFFSET")) | |
self.TIMEZONE = os.getenv("TIMEZONE") | |
self.COUNTRY = os.getenv("COUNTRY") | |
self.STATE = os.getenv("STATE") | |
self.id = board.board_id | |
except OSError as e: | |
print("State.__init__(): Error: {e}") | |
pass | |
except AttributeError as e: | |
print("State.__init__(): Error: {e}") | |
pass | |
# Create an instance of the State class | |
state = State() | |
print(f"\nboard ID: {state.id}\n") | |
try: | |
sd = sdcardio.SDCard(spi, state.sd_cs) | |
except OSError as e: | |
sd = None | |
print(f"Error: {e}") | |
pass | |
i2c = busio.I2C(board.IO10, board.IO9) # was busio.I2C(board.SCL, board.SDA) | |
device_address = None | |
while not i2c.try_lock(): | |
pass | |
try: | |
dev_list = [] | |
for device_address in i2c.scan(): | |
dev_list.append(hex(device_address)) | |
if len(dev_list) > 0: | |
print("I2C devices found:") | |
for _ in range(len(dev_list)): | |
print(f"\t#{_+1}: {dev_list[_]}") | |
else: | |
print("No I2C devices found") | |
finally: # unlock the i2c bus when ctrl-c'ing out of the loop | |
i2c.unlock() | |
# Cleanup | |
device_address = None | |
dev_list = None | |
if state.my_debug: | |
print(f"state.TZ_OFFSET= {state.TZ_OFFSET}") | |
state.pool = socketpool.SocketPool(wifi.radio) | |
ntp = adafruit_ntp.NTP(state.pool, tz_offset=state.TZ_OFFSET) | |
# Added pullup resistors to pin 41 (SCL) and pin 42 (SDA) | |
try: | |
seesaw = Seesaw(i2c, addr=0x50) | |
seesaw.pin_mode_bulk(state.button_mask, seesaw.INPUT_PULLUP) | |
state.qt_btns_present = True | |
print(f"global: seesaw.chip_id= {hex(seesaw.chip_id)}") | |
except Exception as e: | |
print(f"global(): Error while creating an instance seesaw class: {e}") | |
state.qt_btns_present = False | |
raise | |
""" | |
* @brief This function adds spacing | |
* to the given string parameter t | |
* If parameter t is a string | |
* this function will try to add | |
* the needed spacing to the end | |
* of the parameter t | |
* | |
* @param state, t | |
* | |
* @return Boolean | |
""" | |
def tag_adj(state, t): | |
global tag_le_max | |
if state.use_TAG: | |
le = 0 | |
spc = 0 | |
ret = t | |
if isinstance(t, str): | |
le = len(t) | |
if le > 0: | |
spc = state.tag_le_max - le | |
# print(f"spc= {spc}") | |
ret = "" + t + "{0:>{1:d}s}".format("", spc) | |
# print(f"s=\'{s}\'") | |
return ret | |
return "" | |
""" | |
* @brief This function prints to REPL | |
* a list of all the board Pins mapped | |
* for the board in use | |
* | |
* @param None | |
* | |
* @return None | |
""" | |
def show_pin_to_board_mappings(): | |
for pin in dir(microcontroller.pin): | |
if isinstance(getattr(microcontroller.pin, pin), microcontroller.Pin): | |
print("".join(("microcontroller.pin.", pin, "\t")), end=" ") | |
for alias in dir(board): | |
if getattr(board, alias) is getattr(microcontroller.pin, pin): | |
print("".join(("", "board.", alias)), end=" ") | |
print() | |
""" | |
* @brief This function creates: | |
* a display object named 'epd', a | |
* main displayio group, a | |
* displayio group 'text_group' and a | |
* displayio group 'dt_group' | |
* The text_group will contain a random selected element | |
* from a list of World and Continent names. | |
* | |
* @param state | |
* | |
* @return None | |
""" | |
def create_display(state): | |
global spi, epd_bus, epd, i2c | |
# Create the displayio connection to the display pins | |
TAG = tag_adj(state, "create_display(): ") | |
msg_shown = False | |
try_cnt = 0 | |
s_lst = [] | |
if state.my_debug: | |
print(TAG + f"type(spi)= {type(spi)}") | |
print(TAG + f"type(epd_bus)= {type(epd_bus)}") | |
# Used to ensure the display is free in CircuitPython | |
displayio.release_displays() | |
time.sleep(1) | |
if epd_bus is None: | |
while True: | |
try: | |
if spi is not None: | |
epd_bus = fourwire.FourWire( | |
spi, command=state.tft_dc, chip_select=state.tft_cs, reset=state.tft_reset | |
) # , | |
# baudrate=1_000_000) | |
time.sleep(1) | |
if state.my_debug: | |
print(TAG + f"type(epd_bus)= {type(epd_bus)}") | |
break | |
except ValueError as e: | |
try_cnt += 1 | |
if try_cnt >= 10: | |
print( | |
TAG | |
+ f"Tried {try_cnt} times. Failed to resolve error. Exiting...." | |
) | |
raise | |
s = e.args[0] | |
if not msg_shown or (s[:4] not in s_lst): | |
s_lst.append(s[:4]) | |
print(TAG + f"s_lst: {s_lst}") | |
msg_shown = True | |
time.sleep(1) # Wait a bit | |
DISPLAY_WIDTH = 250 # was: 212 | |
DISPLAY_HEIGHT = 122 # was: 104 | |
# Create the display object - the third color is red (0xff0000) | |
# For issues with display not updating top/bottom rows correctly set colstart to 8 | |
epd = adafruit_ssd1680.SSD1680( | |
epd_bus, | |
width=DISPLAY_WIDTH, | |
height=DISPLAY_HEIGHT, # , | |
busy_pin=state.tft_busy, | |
highlight_color=0xFF0000, | |
rotation=270, | |
colstart=0, | |
) | |
if state.my_debug: | |
print(TAG + f"type(epd)= {type(epd)}") | |
# Create a display group for our screen objects | |
g = displayio.Group() | |
# print(f"type(display)= {type(display)}") | |
# Set a background | |
background_bitmap = displayio.Bitmap(DISPLAY_WIDTH, DISPLAY_HEIGHT, 1) | |
# Map colors in a palette | |
palette = displayio.Palette(1) | |
palette[0] = state.disp_bg_color # BACKGROUND_COLOR | |
# Create a Tilegrid with the background and put in the displayio group | |
t = displayio.TileGrid(background_bitmap, pixel_shader=palette) | |
g.append(t) | |
# Draw simple text using the built-in font into a displayio group | |
state.text_group_x = 10 | |
state.text_group_y = 61 | |
text_group = displayio.Group(scale=2, x=state.text_group_x, y=state.text_group_y) | |
text_lst = [ | |
"World", | |
"Europe", | |
"Asia", | |
"North America", | |
"South America", | |
"Africa", | |
"Oceania", | |
"Antarctica", | |
] | |
idx = random.randint(0, len(text_lst) - 1) | |
text = "Hello " + text_lst[idx] + "!" | |
text_area = label.Label(terminalio.FONT, text=text, color=state.disp_fg_color) | |
state.text_area = text_area | |
state.text_area_text = text | |
text_group.append(text_area) # Add this text to the text group | |
g.append(text_group) | |
# Create datetime group | |
dtg = displayio.Group() | |
t2 = displayio.TileGrid(background_bitmap, pixel_shader=palette) | |
dtg.append(t2) | |
dt_group = displayio.Group(scale=2, x=10, y=10) | |
dt_text = "" | |
dt_text_area = label.Label(terminalio.FONT, text=dt_text, color=state.disp_fg_color) | |
state.dt_text_area = dt_text_area | |
state.dt_text_area_text = dt_text | |
dt_group.append(dt_text_area) | |
dtg.append(dt_group) | |
state.dtg = dtg | |
epd.auto_refresh = False | |
# Place the display group on the screen | |
epd.root_group = g | |
# time.sleep(180) | |
# See: https://learn.adafruit.com/todbot-circuitpython-tricks?view=all#displays-lcd-oled-e-ink-and-displayio | |
# Paragraph: "Dealing with E-ink "Refresh Too Soon" Error: | |
if state.my_debug: | |
print(TAG + f"epd.time_to_refresh= {epd.time_to_refresh}") | |
time.sleep(epd.time_to_refresh) | |
# Refresh the display to have everything show on the display | |
# NOTE: Do not refresh eInk displays more often than 180 seconds! | |
if state.my_debug: | |
print(TAG + f"epd.busy= {epd.busy}") | |
while epd.busy: | |
if state.my_debug: | |
print(TAG + f"epd.busy= {epd.busy}") | |
pass | |
state.last_epd_refresh = float(time.monotonic()) | |
if state.my_debug: | |
print(TAG + f"last display refresh= {state.last_epd_refresh}") | |
epd.refresh() | |
print(TAG + "display refreshed") | |
""" | |
* @brief This function creates a text | |
* equivalent for the received float | |
* parameter | |
* | |
* @param m | |
* | |
* @return string | |
""" | |
def mins_to_txt(m): | |
if m == 4.0: | |
ret = "4" | |
elif m == 3.5: | |
ret = "3 1/2" | |
elif m == 3.0: | |
ret = "3" | |
elif m == 2.5: | |
ret = "2 1/2" | |
elif m == 2.0: | |
ret = "2" | |
elif m == 1.5: | |
ret = "1 1/2" | |
elif m == 1.0: | |
ret = "1" | |
elif m == 0.5: | |
ret = "1/2" | |
else: | |
ret = str(m) | |
return ret | |
# ldr = last display refresh | |
""" | |
* @brief This function checks if the | |
* time lapse between the last epd refresh | |
* and the current time is less than | |
* the value in state.refresh_in_t, | |
* alias 'max_seconds'. | |
* This function will print a text, | |
* given by variable txt, or a default text | |
* togethere with the value of a string | |
* equivalent to the value of variable togo_t- | |
* This function returns a True if the | |
* elapsed time is less or equal than the max_seconds | |
* This function returns a False if the | |
* elapsed time is greater than max_seconds. | |
* | |
* @param state, txt, repl_pr | |
* | |
* @return Boolean | |
""" | |
def wait_for_refresh(state, txt=None, repl_pr=False): | |
TAG = tag_adj(state, "wait_for_refresh(): ") | |
ret = True | |
if txt is None: | |
txt = "Display refresh in:" | |
state.flip_show_t = not state.flip_show_t # Negate | |
start_t = state.last_epd_refresh # first time set in func create_display() | |
curr_t = float(time.monotonic()) | |
max_seconds = state.refresh_in_t | |
elapsed_t = curr_t - start_t | |
# print(TAG+f"repl_pr= {repl_pr}, state.flip_show_t= {state.flip_show_t}, elapsed_t = {elapsed_t}, elapsed_t > 0 and elapsed_t % 30 <= 0.5 : {elapsed_t > 0 and elapsed_t % 30 <= 0.5}") | |
if elapsed_t >= 0.00 and elapsed_t <= 1 and not state.elapsed_msg_shown: | |
state.elapsed_msg_shown = True | |
togo_t = round((max_seconds - elapsed_t) / 60, 1) | |
m = "minutes" | |
print(TAG + f"{txt} {mins_to_txt(togo_t)} {m}") | |
elif repl_pr and state.flip_show_t and elapsed_t > 0 and elapsed_t % 30 <= 0.5: | |
# print(TAG+f"start_t= {start_t}, curr_t= {curr_t}, elapsed_t= {elapsed_t}") | |
# print(TAG+ f"Elaped time since last display refresh {elapsed_t} seconds") | |
togo_t = round((max_seconds - elapsed_t) / 60, 1) | |
#print(TAG+f"togo_t= {togo_t}") | |
m = "minutes" if togo_t > 1.0 else "minute" | |
if togo_t >= 0.30: | |
if repl_pr: | |
print(TAG + f"{txt} {mins_to_txt(togo_t)} {m}") | |
time.sleep(1) | |
if elapsed_t < max_seconds: | |
if elapsed_t > 0 and elapsed_t % 5 <= 0.5: | |
state.do_blink = not state.do_blink | |
if state.do_blink: # blink half of the time | |
blink_led(state, state.red_, 1) | |
# print(TAG+f"elapsed_t= {elapsed_t}") | |
ret = False | |
return ret | |
class Date: | |
def __init__(self): | |
self.d1 = 0 # d | |
self.m1 = 0 # m | |
self.y1 = 0 # y | |
self.d2 = 0 # d | |
self.m2 = 0 # m | |
self.y2 = 0 # y | |
self.monthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] | |
# To store number of days in all months from | |
# January to Dec. | |
# This function counts number of leap years | |
# before the given date | |
# d = (dd, mo, yy) | |
def countLeapYears(self, d): | |
years = d[2] | |
# Check if the current year needs to be considered | |
# for the count of leap years or not | |
if (d[1] <= 2): | |
years -= 1 | |
# An year is a leap year if it is a multiple of 4, | |
# multiple of 400 and not a multiple of 100. | |
return int(years / 4) - int(years / 100) + int(years / 400) | |
# This function returns number of days between two | |
# given dates | |
def getDifference(self, dt1, dt2): | |
TAG = "Date.getDifference(): " | |
# COUNT TOTAL NUMBER OF DAYS BEFORE FIRST DATE 'dt1' | |
for _ in range(2): | |
if _ == 0: | |
p = dt1 | |
elif _ == 1: | |
p = dt2 | |
if not isinstance(p, tuple): | |
print(TAG+f"Parameter {"dt1" if _ == 0 else "dt2" if _ == 1 else "?"} has to be of type tuple, received type: {type(p)}") | |
self.d1 = dt1[0] | |
self.m1 = dt1[1] | |
self.y1 = dt1[2] | |
self.d2 = dt2[0] | |
self.m2 = dt2[1] | |
self.y2 = dt2[2] | |
# initialize count using years and day | |
n1 = self.y1 * 365 + self.d1 | |
# Add days for months in given date | |
for i in range(0, self.m1 - 1): | |
n1 += self.monthDays[i] | |
# Since every leap year is of 366 days, | |
# Add a day for every leap year | |
n1 += self.countLeapYears(dt1) | |
# SIMILARLY, COUNT TOTAL NUMBER OF DAYS BEFORE 'dt2' | |
n2 = self.y2 * 365 + self.d2 | |
for i in range(0, self.m2 - 1): | |
n2 += self.monthDays[i] | |
n2 += self.countLeapYears(dt2) | |
# return difference between two counts | |
return (n2 - n1) | |
""" | |
* @brief This function checks if the passed | |
* param tm falls between the limits of | |
* 'daylight-saving-time' (dst) of the | |
* actual country. | |
* This function: | |
* - uses the dt dictionary, | |
* which has been imported from the file | |
* dst_prt.py | |
* - If global variable state.my_debug is True, prints to REPL: | |
* - the dst datetime limits to the REPL | |
* - nr of days to the start of dst period. | |
* - nr of days to the end of dst period. | |
* - returns True if the given | |
* date is within the dst limits. | |
* - returns False if the given | |
* date is not within the dst limits. | |
* | |
* @param state, tm (a time structure) | |
* | |
* @return Boolean | |
""" | |
def is_dst(state, tm): | |
global ntp | |
TAG = tag_adj(state, "is_dst(): ") | |
dst_org = state.dst_offset # get original value | |
if not tm[state.tm_year] in dst.keys(): | |
print( | |
TAG | |
+ f"year: {tm[state.tm_year]} not in dst dictionary {dst.keys()}.\nUpdate the dictionary! Exiting..." | |
) | |
raise SystemExit | |
else: | |
dst_start_end = dst[tm[state.tm_year]] | |
if state.my_debug: | |
print(TAG + f"dst_start_end: {dst_start_end}") | |
cur_dt = time.localtime() | |
dst_start1 = dst_start_end[0] | |
dst_end1 = dst_start_end[1] | |
dst_start2 = time.localtime(dst_start1) | |
dst_end2 = time.localtime(dst_end1) | |
if state.my_debug: | |
print( | |
TAG | |
+ "dst_start1: {:4d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format( | |
dst_start2[state.tm_year], | |
dst_start2[state.tm_mon], | |
dst_start2[state.tm_mday], | |
dst_start2[state.tm_hour], | |
dst_start2[state.tm_min], | |
dst_start2[state.tm_sec], | |
) | |
) | |
print( | |
TAG | |
+ "current date: {:4d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format( | |
cur_dt[state.tm_year], | |
cur_dt[state.tm_mon], | |
cur_dt[state.tm_mday], | |
cur_dt[state.tm_hour], | |
cur_dt[state.tm_min], | |
cur_dt[state.tm_sec], | |
) | |
) | |
print( | |
TAG | |
+ "dst_end1: {:4d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format( | |
dst_end2[state.tm_year], | |
dst_end2[state.tm_mon], | |
dst_end2[state.tm_mday], | |
dst_end2[state.tm_hour], | |
dst_end2[state.tm_min], | |
dst_end2[state.tm_sec], | |
) | |
) | |
if state.my_debug: | |
print( | |
TAG | |
+ f"year: {tm[state.tm_year]}, dst start: {dst_start2}, dst end: {dst_end2}" | |
) | |
current_time = time.time() | |
if current_time > dst_start1 and current_time < dst_end1: | |
dst_new = 1 | |
else: | |
dst_new = 0 | |
if dst_new != dst_org: | |
state.dst_offset = dst_new | |
ntp = adafruit_ntp.NTP(state.pool, tz_offset=state.TZ_OFFSET if state.dst_offset else 0) | |
ret = True if state.dst_offset == 1 else False | |
if state.my_debug: | |
# print(TAG+f"state.dst_offset: {state.dst_offset}") | |
s = "Yes" if state.dst_offset == 1 else "No" | |
print( | |
TAG | |
+ f"Are we in daylight saving time for country: '{state.COUNTRY}', state: '{state.STATE}' ? {s}" | |
) | |
myDate = Date() | |
curr_yr = time.localtime(current_time)[0] | |
curr_mon = time.localtime(current_time)[1] | |
curr_mday = time.localtime(current_time)[2] | |
curr_dt = (curr_mday, curr_mon, curr_yr) | |
start_yr = time.localtime(dst_start1)[0] | |
start_mon = time.localtime(dst_start1)[1] | |
start_mday = time.localtime(dst_start1)[2] | |
start_dt = (start_mday, start_mon, start_yr) | |
end_yr = time.localtime(dst_end1)[0] | |
end_mon = time.localtime(dst_end1)[1] | |
end_mday = time.localtime(dst_end1)[2] | |
end_dt = (end_mday, end_mon, end_yr) | |
if state.dst_offset: | |
days_to_dst_start = myDate.getDifference(curr_dt, start_dt) | |
days_to_dst_end = myDate.getDifference(curr_dt, end_dt) | |
else: | |
days_to_dst_start = myDate.getDifference(curr_dt, start_dt) | |
days_to_dst_end = myDate.getDifference(curr_dt, end_dt) | |
print(TAG+"Days to start of daylight saving time period: {:3d}".format(days_to_dst_start)) | |
print(TAG+"Days to end of daylight saving time period: {:3d}".format(days_to_dst_end)) | |
return ret | |
""" | |
* @brief This function gets the datetime for the built-in | |
* realtime clock. It converts the datetime data to string- | |
* data in 2 ways: 1 string to add to the displayio dt_group | |
* object. The other string to print to REPL. | |
* | |
* @param state | |
* | |
* @return None | |
""" | |
def disp_dt(state): | |
TAG = tag_adj(state, "disp_dt(): ") | |
""" | |
Get the datetime from the built-in RTC | |
After being updated (synchronized) from the AIO time server; | |
Note: the built-in RTC datetime gives always -1 for tm_isdst | |
If the datetime stamp is received from the AIO time server, | |
we can determine is_dst from resp_lst[5] extracted from the AIO time server response text, | |
however we now call function is_dst() to determine if the datetime is within the dst | |
datetime limits or not. | |
""" | |
ct = mRTC.datetime # read datetime from built_in RTC | |
tm = time.localtime(time.time()) # was (time.time() + state.UTC_OFFSET) | |
tm_dst = is_dst(state, tm) # was: -1 | |
tm_dst_s = "Yes" if tm_dst == 1 else "No " | |
spc = " " * state.tag_le_max | |
# print(TAG+"datetime from built-in rtc= {}".format(ct), file=sys.stderr) | |
# weekday (ct[6]) Correct because built-in RTC weekday index is different from the AIO weekday | |
# | |
sDt = "{} YrDay: {}\n{:4d}-{:02d}-{:02d} {:02d}:{:02d}\nutc offset: {} Hr\ndst: {}".format( | |
# wDay yDay yy mo dd hh mm UTC-offset is_dst | |
state.mRTC_DOW[ct[state.tm_wday]], | |
ct[state.tm_yday], | |
ct[state.tm_year], | |
ct[state.tm_mon], | |
ct[state.tm_mday], | |
ct[state.tm_hour], | |
ct[state.tm_min], | |
state.TZ_OFFSET, | |
tm_dst_s, | |
) # was: ct[8]) | |
# yy mo dd | |
sDt2 = "{} YrDay: {}\n{:s}{:4d}-{:02d}-{:02d} {:02d}:{:02d}\n{:s}timezone: {}\n{:s}utc offset: {} Hr\n{:s}dst: {}".format( | |
# wDay yDay yy mo dd hh mm UTC-offset is_dst | |
state.mRTC_DOW[ct[state.tm_wday]], | |
ct[state.tm_yday], | |
spc, | |
ct[state.tm_year], | |
ct[state.tm_mon], | |
ct[state.tm_mday], | |
ct[state.tm_hour], | |
ct[state.tm_min ], | |
spc, | |
state.TIMEZONE, | |
spc, | |
state.TZ_OFFSET, | |
spc, | |
tm_dst_s, | |
) # was: ct[8]) | |
dtg = state.dtg | |
dt_text_area = state.dt_text_area | |
text = f"{sDt}" # f"{dt}\n{tm}" | |
dt_text_area.text = text | |
state.dt_text_area_text = text | |
epd.root_group = dtg # Select the dt_group | |
if not state.my_debug: | |
print(TAG + f"{sDt2}") | |
# time.sleep(state.TFT_show_duration) # in seconds | |
""" | |
* @brief This function prints to REPL | |
* a message and prints the same text to | |
* the attached e-Paper display | |
* This function is called by main(). | |
* | |
* @param state | |
* | |
* @return None | |
""" | |
def disp_done(state): | |
global epd | |
TAG = tag_adj(state, "disp_done(): ") | |
text_area = state.text_area | |
text = "That's all folks!" | |
text_area.text = text | |
state.text_area_text = text | |
print(TAG + f"Displaying text: {text}") | |
wait_for_refresh(state, "Final display refresh in: ", True) | |
if epd: | |
epd.refresh() | |
else: | |
print(TAG + f"type(epd)= {type(epd)}") | |
""" | |
* @brief This function mounts the on-board SDCard | |
* It then prints to the REPL a sorted list from | |
* the contents of the SDCard | |
* Finnally the SDCard will be unmounted | |
* If a SDCard mount attempt was successful, | |
* this function returns a True | |
* If not successful this function returns a False.add()* | |
* @param state | |
* | |
* @return Boolean | |
""" | |
def mount_sd(state): | |
global spi, sd, vfs | |
TAG = tag_adj(state, "mount_sd(): ") | |
# Setup for SDCard: | |
msg_shown = False | |
state.pth = "/sd" | |
spc = " " * state.tag_le_max | |
mount_res = None | |
ret = False | |
try_cnt = 0 | |
if True: | |
if state.my_debug: | |
print(TAG + f"spi= {spi}") | |
print(TAG + f"sd= {sd}, type(sd)= {type(sd)}") | |
if not sd and spi is not None: | |
while True: | |
try: | |
try_cnt += 1 | |
sd = sdcardio.SDCard(spi, state.sd_cs) | |
break | |
except OSError as e: | |
print(TAG + f"Error: {e}\n") | |
break | |
except ValueError as e: | |
if try_cnt >= 10: | |
print(TAG+ f"Tried {try_cnt} times. Failed to resolve error. Exiting....") | |
raise | |
if not msg_shown: | |
msg_shown = True | |
# print(TAG+f"e.args[0]= {e.args[0]}") | |
if mount_res: | |
storage.umount(state.pth) | |
print(TAG + "SD Card unmounted") | |
state.SDCard_mounted = False | |
gc.collect() | |
if state.my_debug: | |
print(TAG + f"sd= {sd}") | |
if sd: | |
vfs = storage.VfsFat(sd) | |
if state.my_debug: | |
print(TAG + f"vfs= {vfs}, state.pth= \'{state.pth}\'") | |
storage.mount(vfs, state.pth) | |
mount_res = storage.getmount(state.pth) | |
# print(TAG+f"mount_res= {mount_res}") | |
if mount_res: | |
state.SDCard_mounted = True | |
ret = True | |
print(TAG + "SD Card mounted") | |
print("\nContents of SD Card:", end='') | |
lst = os.listdir(state.pth) | |
lst.sort() | |
for _ in range(len(lst)): | |
if _ == 0: | |
print(" \'"+lst[_]+"\'") | |
else: | |
print(spc+"\'"+lst[_]+"\'") | |
print() | |
storage.umount(state.pth) | |
print(TAG + "SD Card unmounted") | |
state.SDCard_mounted = False | |
return ret | |
""" | |
* @brief This test creates a neopixel object instance | |
* It switches the neopixel led in three colors | |
* Finally it switches the neopixel led off | |
* | |
* @param None | |
* | |
* @return Boolean | |
""" | |
if state.use_neopixel: | |
# Note built-in NEOPIXEL Pin = (GPIO38) | |
# Create a NeoPixel instance | |
# Brightness of 0.3 is ample for the 1515 sized LED | |
state.pixel = neopixel.NeoPixel(board.NEOPIXEL, 1) | |
state.pixel.brightness = state.neopixel_brightness | |
print("\nTesting Neopixel rgb led:") | |
state.pixel.fill(state.red_) | |
print("Neopixel RED: 0x{:06x}".format(state.red_)) | |
time.sleep(state.delay) | |
state.pixel.fill(state.grn_) | |
print("Neopixel GREEN: 0x{:06x}".format(state.grn_)) | |
time.sleep(state.delay) | |
state.pixel.fill(state.blu_) | |
print("Neopixel BLUE: 0x{:06x}".format(state.blu_)) | |
time.sleep(state.delay) | |
""" | |
pixel.fill(state.cyan) | |
print("Neopixel CYAN: 0x{:06x}".format(state.cyan)) | |
time.sleep(state.delay) | |
pixel.fill(state.purple) | |
print("Neopixel PURPLE: 0x{:06x}".format(state.purple)) | |
time.sleep(state.delay) | |
""" | |
state.pixel.fill(state.blk_) | |
print("Neopixel BLACK: 0x{:06x}".format(state.blk_)) | |
time.sleep(state.delay) | |
print("End of neopixel rgb led test\n") | |
""" | |
* @brief This function blinks the builtin | |
* led, in a color | |
* | |
* @params: state | |
* nr_times: integer that determines the number of times the led will blink | |
* bi_led. Boolean that controls if the builtin led has to blink on not | |
* blink_slow: Boolean that controls the speed of the blinkin | |
* | |
* @return None | |
""" | |
def blink_led(state, colr=None, nr_times=None, bi_led=True, blink_slow=False): | |
if colr is None: | |
clr = state.purple # grn_ | |
else: | |
clr = colr | |
curr_state = state.led_state | |
# print(f"blink_led(): nr_times: {nr_times}, bi_led= {bi_led}, blink_slow= {blink_slow}") | |
if nr_times is None: | |
nr_times = 1 | |
if blink_slow: | |
delay = 0.5 | |
else: | |
delay = 0.1 | |
if curr_state == 1: | |
if bi_led: | |
state.pixel.fill(state.blk_) | |
# led.value = False # first switch the led off | |
time.sleep(delay) | |
for _ in range(nr_times): | |
if bi_led: | |
state.pixel.fill(clr) | |
# led.value = True | |
time.sleep(delay) | |
if bi_led: | |
state.pixel.fill(state.blk_) | |
# led.value = False | |
time.sleep(delay) | |
if curr_state == 1: # if the led originally was on, switch it back on | |
if bi_led: | |
state.pixel.fill(clr) | |
# led.value = True | |
""" | |
* @brief This function prints a message | |
* containing instructions for use of the | |
* Adafruit Gamepad QT | |
* | |
* @param state | |
* | |
* @return None | |
""" | |
def test_msg(state): | |
TAG = tag_adj(state, "gamepad_test(): ") | |
if not state.msg1_shown: | |
state.msg1_shown = True | |
spc = " " * state.tag_le_max | |
print(TAG + f"We're going to test the Gamepad QT and the {id}.") | |
print( | |
spc | |
+ "or press any of the buttons (X, Y, A, B, Select or Start) on the Gamepad QT.\n" | |
+ spc | |
+ f"To soft reset {id} press Gamepad QT button Start.\n" | |
) | |
""" | |
* @brief This function asks the user for input | |
* It checks if the users inputs one of the | |
* following letters "Y", "y", "N" or "n". | |
* | |
* @param None | |
* | |
* @return Boolean | |
""" | |
def ck_usr_answr(): | |
spc = " " * state.tag_le_max | |
ret = False | |
while True: | |
answer = input(spc + "Are you sure? (Y/n)+<Enter>: ") | |
print(spc + f"You answered: '{answer}'") | |
if answer in ["Y", "y"]: | |
ret = True | |
break | |
elif answer in ["N", "n"]: | |
break | |
return ret | |
""" | |
* @brief This function performs a software reset | |
* of this board | |
* | |
* @param None | |
* | |
* @return None | |
""" | |
def reboot(): | |
print("\nRebooting...") | |
time.sleep(3) | |
# os.system('sudo shutdown -r now') # for Raspberry Pi boards | |
microcontroller.reset() # for CircuitPython boards | |
""" | |
Adafruit Gamepad QT button values list: | |
+--------+--------+----------------------------+ | |
| BUTTON | VALUE | Value in CPY when pressed | | |
+--------+--------+----------------------------+ | |
| SELECT | 1 | 65638 | | |
+--------+--------+----------------------------+ | |
| B | 2 | 65637 | | |
+--------+--------+----------------------------+ | |
| Y | 4 | 65635 | | |
+--------+--------+----------------------------+ | |
| ? | 8 | ? | | |
+--------+--------+----------------------------+ | |
| ? | 16 | ? | | |
+--------+--------+----------------------------+ | |
| A | 32 | 65607 | | |
+--------+--------+----------------------------+ | |
| X | 64 | 65637 ! | |
+--------+--------+----------------------------+ | |
| START | 65636 | 103 | | |
+--------+--------+----------------------------+ | |
""" | |
""" | |
* @brief This function prints the name of the button | |
* pressed. | |
* If a button in the list btns is pressed, | |
* this function calls blink_led() | |
* | |
* @param state, res | |
* | |
* @return None | |
""" | |
def pr_btn_name(state, res): | |
btns = ["X", "Y", "A", "B", "Select", "Start"] | |
if res >= 0 and res < len(btns): | |
blink_led(state) | |
print((" " * state.tag_le_max) + "Button " + btns[res] + " pressed") | |
""" | |
* @brief This function checks and handles | |
+ button presses or joystick movement | |
* of the Adafruit Gamepad QT. | |
* At start or when Start button press is answered with 'n' | |
* this function will call the function test_msg() | |
* On button press or joystick movemnet this function | |
* will call pr_btn_name(). | |
* | |
* @param state | |
* | |
* @return None | |
""" | |
# Check for button presses on the Gamepad QT | |
def ck_qt_btns(state): | |
if not state.qt_btns_present: | |
return | |
TAG = tag_adj(state, "ck_qt_btns(): ") | |
nr_btns = 6 | |
res_x = res_y = res_a = res_b = res_sel = res_sta = -1 | |
elapsed_t = None | |
interval_t = 36 | |
interval_cnt = 0 | |
gc.collect() | |
time.sleep(0.2) | |
spc = " " * state.tag_le_max | |
test_msg(state) | |
stop = False # Set by Y-button | |
while True: | |
try: | |
x = 1023 - seesaw.analog_read(14) | |
y = 1023 - seesaw.analog_read(15) | |
if state.qt_buttons_start: # Prevent an inital REPL print | |
state.qt_buttons_start = False | |
state.last_x = x | |
state.last_y = y | |
except Exception as e: | |
if e.errno == 121: # Remote I/O Error | |
print(TAG + f"Error: {e}") | |
pass | |
if x >= state.last_x: | |
diff_x = abs(x - state.last_x) | |
else: | |
diff_x = abs(state.last_x - x) | |
if y >= state.last_y: | |
diff_y = abs(y - state.last_y) | |
else: | |
diff_y = abs(state.last_y - y) | |
if (diff_x > 3) or (diff_y > 3): | |
blink_led(state) | |
print(TAG + f"joystick: (x, y)= ({x}, {y})") | |
# print(TAG+f"diff_x= {diff_x}, diff_y= {diff_y}") | |
state.last_x = x | |
state.last_y = y | |
# Get the button presses, if any... | |
buttons = seesaw.digital_read_bulk(state.button_mask) | |
if state.my_debug: | |
print("\n" + TAG + f"buttons = {buttons}") | |
if buttons == 65639: | |
if state.my_debug: | |
print(TAG + f"Gamepad QT: no button pressed") | |
stop = True | |
break | |
# time.sleep(0.5) | |
start_t = time.monotonic() | |
if buttons: | |
res = -1 | |
for _ in range(nr_btns): | |
if _ == 0: | |
bz = 1 << state.BUTTON_X | |
if not buttons & (bz): | |
res = _ | |
if res_x != res: | |
pr_btn_name(state, res) | |
res_x = res | |
break | |
if _ == 1: | |
bz = 1 << state.BUTTON_Y | |
if not buttons & (bz): | |
res = _ | |
if res_y != res: | |
pr_btn_name(state, res) | |
res_y = res | |
break | |
if _ == 2: | |
bz = 1 << state.BUTTON_A | |
if not buttons & (bz): | |
res = _ | |
if res_a != res: | |
pr_btn_name(state, res) | |
res_a = res | |
break | |
if _ == 3: | |
bz = 1 << state.BUTTON_B | |
if not buttons & (bz): | |
res = _ | |
if res_b != res: | |
pr_btn_name(state, res) | |
res_b = res | |
break | |
if _ == 4: | |
bz = 1 << state.BUTTON_SELECT | |
if not buttons & (bz): | |
res = _ | |
if res_sel != res: | |
pr_btn_name(state, res) | |
res_sel = res | |
break | |
if _ == 5: | |
bz = 1 << state.BUTTON_START | |
if not buttons & (bz): | |
res = _ | |
if res_sta != res: | |
pr_btn_name(state, res) | |
res_sta = res | |
print(spc + f"About to soft reset the {id}") | |
if ck_usr_answr(): | |
reboot() # Reboot the board | |
else: | |
state.msg1_shown = False | |
res_sta = -2 | |
test_msg(state) | |
break | |
curr_t = time.monotonic() | |
elapsed_t = (curr_t - start_t) * 1000 | |
if elapsed_t >= interval_t: | |
interval_cnt += 1 | |
if interval_cnt >= 100: | |
interval_cnt = 0 | |
res_x = res_y = res_a = res_b = res_sel = res_sta = -2 | |
start_t = curr_t | |
time.sleep(0.01) | |
state.pixel.fill(state.blk_) | |
# led.value = False | |
state.led_state = 0 | |
""" | |
* @brief This function checks if exists an ntp object | |
* If so, it retrieves a datetime stamp from an NTP server | |
* and sets state.NTP_dt to the retrieved datetime stamp | |
* It then also sets the state.NTP_dt_is_set flag | |
* | |
* @param state | |
* | |
* @return boolean | |
""" | |
def is_NTP(state): | |
TAG = tag_adj(state, "is_NTP(): ") | |
ret = False | |
dt = None | |
try: | |
if ntp is not None: | |
if not state.NTP_dt_is_set: | |
dt = ntp.datetime | |
state.NTP_dt = dt | |
if state.my_debug: | |
print(TAG + f"state.NTP_dt: {state.NTP_dt}") | |
state.NTP_dt_is_set = True | |
ret = True if dt is not None else False | |
except OSError as e: | |
print(f"is_NTP() error: {e}") | |
return ret | |
""" | |
* @brief This function sets the internal | |
* realtime clock | |
* It retrieves the datetime stamp from an NTP server | |
* on internet | |
* | |
* @param state | |
* | |
* @return None | |
""" | |
def set_INT_RTC(state): | |
global mRTC | |
if not state.set_SYS_RTC: | |
return | |
TAG = tag_adj(state, "set_INT_RTC(): ") | |
s1 = "Internal (SYS) RTC is set from " | |
s2 = "datetime stamp: " | |
dt = None | |
internal_RTC = True if is_INT_RTC() else False | |
if internal_RTC: | |
try: | |
dt = ntp.datetime | |
mRTC.datetime = dt | |
except OSError as e: | |
print( | |
TAG + f"Error while trying to set internal RTC from NTP datetime: {e}" | |
) | |
raise | |
except Exception as e: | |
raise | |
state.SYS_dt = mRTC.datetime | |
if state.my_debug: | |
print(TAG + f"mRTC.datetime: {mRTC.datetime}") | |
# print(TAG+f"state.SYS_dt: {state.SYS_dt}") | |
state.SYS_RTC_is_set = True | |
if state.SYS_dt.tm_year >= 2000: | |
print(TAG + s1 + "NTP service " + s2) | |
dt = state.SYS_dt | |
if not state.my_debug: | |
print(TAG + "{:d}/{:02d}/{:02d}".format(dt.tm_mon, dt.tm_mday, dt.tm_year)) | |
print( | |
TAG | |
+ "{:02d}:{:02d}:{:02d} weekday: {:s}".format( | |
dt.tm_hour, dt.tm_min, dt.tm_sec, state.mRTC_DOW[dt.tm_wday] | |
) | |
) | |
if internal_RTC: | |
print(TAG + "Note that NTP weekday starts with 0") | |
""" | |
* @brief In this version of CircuitPython one can only check if there is a WiFi connection | |
* by checking if an IP address exists. | |
* In the function do_connect() the global variable s_ip is set. | |
* | |
* @param state | |
* | |
* @return boolean. True if exists an ip address. False if not. | |
""" | |
def wifi_is_connected(state): | |
TAG = tag_adj(state, "wifi_is_connected(): ") | |
if state.ip is not None: | |
my_ip = state.ip | |
else: | |
my_ip = wifi.radio.ipv4_address | |
if my_ip is None: | |
return False | |
else: | |
my_s__ip = str(my_ip) | |
ret = ( | |
True | |
if my_s__ip is not None and len(my_s__ip) > 0 and my_s__ip != "0.0.0.0" | |
else False | |
) | |
if ret: | |
state.ssid = os.getenv("CIRCUITPY_WIFI_SSID") | |
print(TAG + f"Connected to: {state.ssid}") | |
state.s__ip = my_s__ip | |
print(TAG + f"IP: {state.s__ip}") | |
return ret | |
""" | |
* @brief function that establish WiFi connection | |
* Function tries to establish a WiFi connection with the given Access Point | |
* If a WiFi connection has been established, function will: | |
* sets the global variables: 'ip' and 's_ip' ( the latter used by function wifi_is_connected() ) | |
* | |
* @param state | |
* | |
* @return None | |
""" | |
def do_connect(state): | |
TAG = tag_adj(state, "do_connect(): ") | |
# Get env variables from file settings.toml | |
ssid = os.getenv("CIRCUITPY_WIFI_SSID") | |
pw = os.getenv("CIRCUITPY_WIFI_PASSWORD") | |
try: | |
wifi.radio.connect(ssid=ssid, password=pw) | |
except ConnectionError as e: | |
print(TAG + f"WiFi connection Error: '{e}'") | |
except Exception as e: | |
print(TAG + f"Error: {dir(e)}") | |
state.ip = wifi.radio.ipv4_address | |
if state.ip: | |
state.s__ip = str(state.ip) | |
""" | |
* @brief function returns True | |
* if RTC object instance mRTC exists | |
* | |
* @param state | |
* | |
* @return None | |
""" | |
def is_INT_RTC(): | |
if mRTC is not None: | |
return True | |
return False | |
""" | |
* @brief function print hostname to REPL | |
* | |
* @param state | |
* | |
* @return None | |
""" | |
def hostname(state): | |
TAG = tag_adj(state, "hostname(): ") | |
print(TAG + f"wifi.radio.hostname= '{wifi.radio.hostname}'") | |
""" | |
* @brief function prints mac address to REPL | |
* | |
* @param state | |
* | |
* @return None | |
""" | |
def mac(state): | |
TAG = tag_adj(state, "mac(): ") | |
mac = wifi.radio.mac_address | |
le = len(mac) | |
if le > 0: | |
state.mac = mac | |
print(TAG + "wifi.radio.mac_address= ", end="") | |
for _ in range(le): | |
if _ < le - 1: | |
print("{:x}:".format(mac[_]), end="") | |
else: | |
print("{:x}".format(mac[_]), end="") | |
print("", end="\n") | |
""" | |
* @brief this function sets the WiFi.AuthMode. | |
+ if there is no WiFi connection the function calls | |
+ the function do_connect() to establish a WiFi connection. | |
* | |
* @param state | |
* | |
* @return None | |
""" | |
def setup(state): | |
global pixels, my_brightness, mRTC, SYS_dt, tz_o, ntp | |
TAG = tag_adj(state, "setup(): ") | |
wifi.AuthMode.WPA2 # set only once | |
if not wifi_is_connected(state): | |
do_connect(state) | |
hostname(state) | |
if is_NTP(state): | |
if not state.my_debug: | |
print(TAG + "We have NTP") | |
if is_INT_RTC(): | |
state.dtg_start = True # Set flag to call disp_dt() | |
# dummy = is_dst(state, time.localtime(time.time()) ) | |
if not state.my_debug: | |
print(TAG + "We have an internal RTC") | |
print(TAG + "Going to set internal RTC") | |
set_INT_RTC(state) | |
if state.SYS_RTC_is_set: | |
if state.my_debug: | |
print(TAG + "and the internal RTC is set from an NTP server") | |
gc.collect() | |
if not state.my_debug: | |
print() | |
""" | |
* @brief this is the main function. | |
* | |
* @param None | |
* | |
* @return None | |
""" | |
def main(): | |
global spi, state | |
TAG = tag_adj(state, "main(): ") | |
loopnr = 0 | |
setup(state) | |
if state.my_debug: | |
show_pin_to_board_mappings() | |
print(TAG + "Going to mount SD Card") | |
res = mount_sd(state) | |
if state.my_debug: | |
print(TAG + f"SD Card mounted= {state.SDCard_mounted}") | |
print(TAG + "Done with SD Card") | |
print(TAG + "Going to create and write text to display") | |
# Used to ensure the display is free in CircuitPython | |
displayio.release_displays() | |
create_display(state) | |
while True: | |
try: | |
loopnr += 1 | |
# print(f"\nLoopnr: {loopnr}") | |
if loopnr >= 1000: | |
loopnr = 0 | |
ck_qt_btns(state) | |
# state.msg1_shown = False | |
# time.sleep(0.5) | |
if state.dtg_start: | |
state.dtg_start = False | |
gc.collect() | |
if state.my_debug: | |
print(TAG+f"Memory free: {gc.mem_free()}") | |
disp_dt(state) | |
blink_led(state, state.grn_, 1) | |
if epd: | |
epd.refresh() | |
state.elapsed_msg_shown = False | |
state.last_epd_refresh = int(float(time.monotonic())) | |
elif wait_for_refresh(state, "Displaying datetime in:", True): | |
gc.collect() | |
if state.my_debug: | |
print(TAG+f"Memory free: {gc.mem_free()}") | |
disp_dt(state) | |
blink_led(state, state.grn_, 1) | |
if epd: | |
epd.refresh() | |
state.elapsed_msg_shown = False | |
state.last_epd_refresh = int(float(time.monotonic())) | |
except RuntimeError as e: | |
print(TAG + f"Error: {e.args[0]}") | |
pass | |
except KeyboardInterrupt: | |
print(TAG + "KeyboardInterrrupt. Exiting...") | |
break | |
# if loopnr >= 1000: | |
# break | |
state.pixel.fill(state.blk_) | |
print(TAG + "That's all folks!") | |
# disp_done(state) | |
time.sleep(5) | |
sys.exit() | |
if __name__ == "__main__": | |
main() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# To auto-connect to Wi-Fi | |
CIRCUITPY_WIFI_SSID="" | |
CIRCUITPY_WIFI_PASSWORD="" | |
TIMEZONE="Europe/Lisbon" # http=//worldtimeapi.org/timezones | |
TZ_OFFSET="1" # hours difference from utc | |
# TIMEZONE="America/New_York" | |
# TZ_OFFSET="-4" | |
COUNTRY="PRT" | |
STATE="" | |
DEBUG_FLAG="0" # No debug | |
LOCAL_TIME_FLAG="1" | |
NTP_LOCAL_FLAG= "1" | |
NTP_LOCAL_URL="pt.pool.ntp.org" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Wednesday 2024-03-13 17h38 utc | |
Board: Lolin S3 PRO with 32GB SDCard and a Lolin 2.13 INCH e-Paper 250x122 SPI Display | |
IDE: mu V2.1.0 | |
REPL Output | |
soft reboot | |
Auto-reload is on. Simply save files over USB to run them or enter REPL to disable. | |
code.py output: | |
board ID: lolin_s3_pro | |
I2C devices found: | |
#1: 0x50 | |
global: seesaw.chip_id= 0x87 | |
Testing Neopixel rgb led: | |
Neopixel RED: 0x00ff00 | |
Neopixel GREEN: 0xff0000 | |
Neopixel BLUE: 0x0000ff | |
Neopixel BLACK: 0x000000 | |
End of neopixel rgb led test | |
wifi_is_connected(): Connected to: __________ | |
wifi_is_connected(): IP: 192.168.x.xxx | |
hostname(): wifi.radio.hostname= 'LOLIN-S3-PRO' | |
setup(): We have NTP | |
setup(): We have an internal RTC | |
setup(): Going to set internal RTC | |
set_INT_RTC(): Internal (SYS) RTC is set from NTP service datetime stamp: | |
set_INT_RTC(): 3/13/2024 | |
set_INT_RTC(): 17:38:16 weekday: Wednesday | |
set_INT_RTC(): Note that NTP weekday starts with 0 | |
main(): Going to mount SD Card | |
mount_sd(): SD Card mounted | |
Contents of SD Card: 'BigBuckBunny2.pcm' | |
'BigBuckBunny2.rgb' | |
'Ham_DV.wav' | |
'System Volume Information' | |
'example_app_bad_apple.jpg' | |
'example_app_moon.jpg' | |
'morse.wav' | |
'table_720p30.png' | |
'table_vga60.png' | |
'two_connectors.jpg' | |
mount_sd(): SD Card unmounted | |
create_display(): display refreshed | |
gamepad_test(): We're going to test the Gamepad QT and the <function>. | |
or press any of the buttons (X, Y, A, B, Select or Start) on the Gamepad QT. | |
To soft reset <function> press Gamepad QT button Start. | |
Button X pressed | |
Button Y pressed | |
Button A pressed | |
Button B pressed | |
Button Select pressed | |
Are you sure? (Y/n)+<Enter>: n | |
You answered: 'n' | |
gamepad_test(): We're going to test the Gamepad QT and the <function>. | |
or press any of the buttons (X, Y, A, B, Select or Start) on the Gamepad QT. | |
To soft reset <function> press Gamepad QT button Start. | |
wait_for_refresh(): Displaying datetime in: 4 minutes | |
wait_for_refresh(): Displaying datetime in: 3 1/2 minutes | |
wait_for_refresh(): Displaying datetime in: 3 minutes | |
wait_for_refresh(): Displaying datetime in: 2 1/2 minutes | |
wait_for_refresh(): Displaying datetime in: 2 minutes | |
wait_for_refresh(): Displaying datetime in: 1 1/2 minutes | |
wait_for_refresh(): Displaying datetime in: 1 minute | |
wait_for_refresh(): Displaying datetime in: 1/2 minute | |
is_dst(): Are we in daylight saving time for country: 'PRT', state: '' ? No | |
is_dst(): Days to start of daylight saving time period: 18 | |
is_dst(): Days to end of daylight saving time period: 228 | |
disp_dt(): Wednesday YrDay: 73 | |
2024-03-13 17:42 | |
timezone: Europe/Lisbon | |
utc offset: 0 Hr | |
dst: No | |
wait_for_refresh(): Displaying datetime in: 4 minutes | |
wait_for_refresh(): Displaying datetime in: 3 1/2 minutes |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment