Skip to content

Commit 06d09c6

Browse files
authored
Merge pull request #38 from jef41/feature/wifi_reconnect
v1.2.0 release, wifi reconnect rewritten & status now shown in card stack prior 1.2.0 was a bit too soon
2 parents a9d1601 + c359361 commit 06d09c6

File tree

10 files changed

+239
-262
lines changed

10 files changed

+239
-262
lines changed

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ Custom configurations can be used by creating a file `config.json` in the root d
7777
|`ssid` (str) | SSID for your wifi newtork | None | [Example config](examples/wifi.md) |
7878
|`password` (str) | password for your wifi newtork | None | [Example config](examples/wifi.md) |
7979
|`country_code` (str) | ISO 3166-1 alpha-2 character country code for wifi | `None` | [Example config](examples/wifi.md) |
80-
|`wifi_check_interval` (int) | Check there is a working internet conenction every n seconds | `3600` | [Example config](examples/wifi.md) |
8180
|`debug_log` (list) | How many kb in each and how many debug backup files to keep | `[20, 1]` | |
8281
|`display_type` (str) | currently only option is "DISPLAY_PICO_DISPLAY" | None | [Example config](examples/display.md) |
8382
|`display_update_secs` (float) | how frequently to cycle content of display screen | `5` | [Example config](examples/display.md) |
@@ -197,19 +196,21 @@ SIMULATE_BEACONS = False
197196

198197
# Status LED
199198

200-
The Pico board has an onbaord LED. This is used to give a basic visual indication of the condittion of the code. The table below should help to interpret the LED status;
199+
The Pico board has an onboard LED. This is used to give a basic visual indication of the condition of the code. The table below should help to interpret the LED status;
201200

202201

203202
| Condition | Appearance | Timing (on/off) milliseconds | Indication |
204203
| ---------------------------- | ---------------------------- | --------------------- | --------------------- |
205204
|STARTUP | solid ON | None | The Pico is in its initial startup state, loading variables etc. It should progress within 1 second to initiate a wifi connection |
206-
|CONNECTING | fast blink (on-off ~ twice per second) | 10, 400 | Initial configuration loaded, connecting to wifi |
207-
|CONNECTED | 1Hz blink brief | 200, 800 | The Pico has connected to wifi. It willl progress from this state once a stable wifi connection has been established |
208-
|NOT CONNECTED | 1Hz blink slow | 800, 200 | A wifi connection has not been established. If not using wifi (i.e. logging locally to file) this will not be a problem |
209-
|RUNNING | blink once per 3 secs | 10, 3,000 | The application is running and listenting for data from Tilt devices |
205+
|CONNECTING | fast blink (on-off ~ twice per second) | 10, 400 | Initial configuration loaded, in process of connecting to wifi |
206+
|CONNECTED | 1Hz blink brief | 200, 800 | The Pico has connected to wifi. After 5 seconds it will progress from this state once a stable wifi connection has been established, or try to reconnect |
207+
|NOT CONNECTED | 1Hz blink slow | 800, 200 | A wifi connection has not been established. If not requiring wifi (i.e. logging locally to file) this will not be a problem |
208+
|RUNNING | blink once per 3 secs | 10, 3,000 | Once the startup routine has finished the LED should change to this status to indicate that the application is running and listenting for data from Tilt devices |
210209

211210
If the LED remains solidly lit this indicates that the Pico has encountered an error. It is most likely that either the config.json file is not present, or this file is invalid. In this situation, use Thonny to connect to the device, inspect the debug.log file and correct the issue.
212211

212+
During startup, if a LCD display is present, some information on progress (and errors) will be shown. Once starup has completed and the device is running, the display will normally report Tilt beacon data. If the wifi connection becomes disconnected the LED will revert to its `NOT CONNECTED` state and (if an LCD is present) the wifi connection status will be added to the display pages. The device will try to reconnect every 2 minutes, unless the password is detected as being incorrect, in which case the device will stop and cease to record Tilt data.
213+
213214
# Integrations
214215

215216
* [ ] [Prometheus](#Prometheus-Metrics)

bridge/lib/bridge_main.py

Lines changed: 8 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
"""central handler holds most coros & passes data between
2-
1.1.1 use enabled_tilts instead of colours_enabled - needs test
3-
TODO track how to store HD or SD
1+
""" central handler holds most coros & passes data between
2+
1.1.1 use enabled_tilts instead of colours_enabled
43
"""
54
import logging
65
import gc
@@ -25,7 +24,7 @@
2524
class BridgeMain:
2625
logger = logging.getLogger("bridge")
2726

28-
def __init__(self):
27+
def __init__(self, onboard_led=None):
2928
self.config = None
3029
self.providers = None
3130
self.data_archive = bytearray() # Queue for holding incoming data from scans
@@ -36,7 +35,7 @@ def __init__(self):
3635
self.enabled_tilts = list()
3736
self.rtc = None
3837
self.wdt = None
39-
self.onboard_led = None
38+
self.onboard_led = onboard_led
4039
# Load config from file, with defaults, and args
4140
result = True
4241
gc.collect()
@@ -62,15 +61,10 @@ def __init__(self):
6261
def initialised(self):
6362
return self.rtc
6463

65-
async def bridge_main(self, onboard_led, simulate_beacons: bool = False):
64+
async def bridge_main(self, simulate_beacons: bool = False):
6665
gc.collect()
67-
self.onboard_led = onboard_led
68-
# if providers is None:
69-
# self.providers = self.set_providers()
70-
# else:
71-
# self.providers = providers
7266
# add any webhooks defined in config
73-
# todo !! not currently implemented/tested
67+
# TODO not currently implemented/tested
7468
self.webhook_providers = self._get_webhook_providers()
7569

7670
if self.webhook_providers:
@@ -137,10 +131,7 @@ async def bridge_main(self, onboard_led, simulate_beacons: bool = False):
137131
self.logger.info("starting beacon scanner...")
138132
self.scanner = asyncio.create_task(self._scan_for_ibeacons())
139133
# pass
140-
# either way create a task to update the display
141-
# TODO self.display_enabled - def to test for attached display
142-
#lcd_colours = {"simulated"} #, "red"}
143-
#time.sleep(5)
134+
# in either case create a task to update the display
144135
if self.display:
145136
self.display_updater = asyncio.create_task(self.display.card_stack(self.data_archive, self.enabled_tilts))
146137
try:
@@ -190,16 +181,13 @@ async def _scan_for_ibeacons(self, simulate=False):
190181
adv_data = b"".join([pre, col, post, major, minor, tx_pwr])
191182
#print(adv_data)
192183
iBeacon_data = iBeaconStatus(adv_data, 0, "00:00:00:00:00:00")
193-
# await _beacon_callback(uuid, major, minor, 0, 0, simulate)#, bridge_q)
194-
# print(f'{iBeacon_data.colour} {col} {iBeacon_data.major} {iBeacon_data.minor}')
195184
try:
196185
task = asyncio.create_task(
197186
self._beacon_callback(iBeacon_data, simulate)
198187
)
199188
# task running
200189
await asyncio.sleep_ms(randrange(80, 120)) # pause here & give way
201190
await task # then wait for task to complete
202-
# res = await asyncio.gather(t1,t2, return_exceptions=True)
203191
except (
204192
asyncio.TimeoutError
205193
): # These only happen if return_exceptions is False
@@ -208,8 +196,6 @@ async def _scan_for_ibeacons(self, simulate=False):
208196
) # With the default times, cancellation occurs first
209197
except asyncio.CancelledError:
210198
logger.warning("scanner Cancelled")
211-
# asyncio.sleep_ms(randrange(100, 750))
212-
# pckt_complete = True
213199
else:
214200
async with aioble_central.scan(
215201
duration_ms=5000,
@@ -437,17 +423,6 @@ def set_providers(self, network=False):
437423
)
438424
self.providers = normal_providers
439425

440-
def get_time(self):
441-
result = False
442-
try:
443-
ntptime.settime()
444-
self.logger.info("time set to UTC:{}".format(self.rtc.datetime()))
445-
result = True
446-
except:
447-
# todo catch more specific exception
448-
self.logger.info("npttime.settime() failed.")
449-
return result
450-
451426

452427
def max_av_period(en_providers, tilt_devices):
453428
# return the maximum averaging value (seconds) for enabled providers
@@ -487,4 +462,4 @@ async def debug_memory(logger):
487462
logger.debug(f"gc: {gc.mem_free()}")
488463

489464

490-
__version__ = "1.0.2"
465+
__version__ = "1.1.2"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from .bridge_config import BridgeConfig
22
#from .pitch_config import PitchConfig
33

4-
__version__ = '1.0.0'
4+
__version__ = '1.1.0'

bridge/lib/configuration/bridge_config.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ def __init__(self, data: dict):
1111
self.ssid = None
1212
self.password = None
1313
self.country_code = None
14-
self.wifi_check_interval = 3600
1514
# Debug log
1615
self.debug_log = [20, 1]
1716
# Defaults
@@ -86,9 +85,6 @@ def get_original_gravity(self, colour: str):
8685
return self.__dict__.get(colour + '_original_gravity')
8786
#return getattr(self,colour + '_original_gravity', None)
8887

89-
#def get_temp_offset(self, colour: str):
90-
# return self.__dict__.get(colour + '_temp_offset', 0)
91-
9288
def get_brew_name(self, colour: str):
9389
return self.__dict__.get(colour + '_name', colour)
9490
#return getattr(self, colour + '_name', colour)

bridge/lib/display_driver.py

Lines changed: 46 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
"""Classes to mediate between bridge_main and LCD LED devices
2-
16,17,18,19 SPI0_RX, SPI0_CSN, SPI0_SCK, SPI0_TX GPIO numbering
1+
""" Classes to mediate between bridge_main and LCD LED devices
2+
16,17,18,19 SPI0_RX, SPI0_CSN, SPI0_SCK, SPI0_TX GPIO numbering
33
"""
44

55
import gc
@@ -57,14 +57,10 @@ def __new__(cls, config: BridgeConfig):
5757
def __init__(self, config: BridgeConfig):
5858
self.config = config
5959
self.lcd = None # getattr gpio pins
60-
self.update_intvl = None
60+
self.update_intvl = getattr(self.config, "display_update_secs", 3)
6161
self.brightness = None # getattr
6262
self.indicator = True
6363
self.startup_msg = []
64-
# lcd = PicoGraphics(display=DISPLAY_PICO_DISPLAY, pen_type=PEN_P4,rotate=0)
65-
# lcd.set_backlight(1.0)
66-
# update_frequency = 3 # seconds to cycle through each screen
67-
# self._check_for_display(pins) if (pins := getattr(config, 'lcd_spi_gpio', None)) else None
6864
(
6965
self._check_for_display(display_type)
7066
if (display_type := getattr(self.config, "display_type", None))
@@ -80,7 +76,6 @@ def _check_for_display(self, display_type):
8076
pen_type=picographics.PEN_P4,
8177
rotate=0,
8278
)
83-
# self.lcd = PicoGraphics(display=DISPLAY_PICO_DISPLAY, pen_type=PEN_P4,rotate=0)
8479
# Create palette mapping dynamically
8580
self.colour_to_palette = {
8681
colour: self.lcd.create_pen(*get_color_values(colour, 1))
@@ -109,20 +104,23 @@ async def card_stack(
109104
): #: TiltDevice
110105
# display the most recent data as basic & extended info for each tilt
111106
# some sort of loading screen
112-
#blinky = asyncio.create_task(self.display_heartbeat())
113-
del self.startup_msg
114-
self.lcd.set_font("serif")
107+
self.startup_msg = [] # clear startup messages
115108
while True:
116109
#index = 0
110+
self.lcd.set_font("serif")
117111
for tilt in cards:
118112
#if index == 0:
119113
# # print("show clock")
120114
# await self.display_clock()
121115
# show standard info for each configured tilt colour
116+
# & test for extended info
122117
extended_info = await self.display_sg_t(tilt, tilt_data_store)
123118
# return a tilt_status object or None
124119
if extended_info:
125120
await self.display_extended(tilt, extended_info)
121+
if len(self.startup_msg):
122+
# there's probably a wifi disconnection
123+
await self.show_msg()
126124
#index = (index + 1) % len(cards)
127125

128126
def read_latest_vals(self, tilt_data_store, tilt_colour):
@@ -131,7 +129,7 @@ def read_latest_vals(self, tilt_data_store, tilt_colour):
131129
uncal_tempF, uncal_SG = tilt_data_store.get_data(
132130
tilt_colour,
133131
av_period=0,
134-
log_period=(3 * getattr(self.config, "display_update_secs", 3)),
132+
log_period=(3 * self.update_intvl),
135133
)
136134
if uncal_tempF and uncal_SG:
137135
tilt_status = TiltStatus(
@@ -152,11 +150,12 @@ def read_latest_vals(self, tilt_data_store, tilt_colour):
152150
uncal_temp, uncal_gravity, tilt_status = None, None, None
153151
return (uncal_temp, uncal_gravity, tilt_status)
154152

153+
'''
155154
async def display_clock(self):
156155
# show a clock or a MOTD or something
157156
width, height = self.lcd.get_bounds()
158157
offset = None
159-
for _ in range(getattr(self.config, "display_update_secs", 3)):
158+
for _ in range(self.update_intvl):
160159
self.lcd.set_pen(self.colour_to_palette["BG"])
161160
self.lcd.clear()
162161
self.lcd.set_thickness(2)
@@ -178,6 +177,7 @@ async def display_clock(self):
178177
self.lcd.update()
179178
await asyncio.sleep(1)
180179
# TODO subtract processing time from 1 second ticks_diff
180+
'''
181181

182182
async def display_sg_t(self, tilt, tilt_data_store):
183183
#
@@ -191,6 +191,7 @@ async def display_sg_t(self, tilt, tilt_data_store):
191191
tilt_data_store, tilt.colour
192192
)
193193
n = 4 if tilt.hd else 3
194+
self.lcd.set_font("serif")
194195
self.lcd.set_pen(self.colour_to_palette["BG"])
195196
'''self.lcd.polygon([
196197
(0, 0),
@@ -275,11 +276,12 @@ async def display_sg_t(self, tilt, tilt_data_store):
275276
self.lcd.update()
276277
# t2 = time.ticks_ms()
277278
# print(f"standard drawing took:{time.ticks_diff(t1, t_start)}, update took:{time.ticks_diff(t2, t1)}")
278-
await asyncio.sleep(getattr(self.config, "display_update_secs", 3))
279+
await asyncio.sleep(self.update_intvl)
279280
return tilt_values if getattr(tilt_values, "original_gravity", None) else None
280281

281282
async def display_extended(self, tilt, tilt_values):
282283
#
284+
self.lcd.set_font("serif")
283285
self.lcd.set_pen(self.colour_to_palette["BG"])
284286
'''self.lcd.polygon([
285287
(0, 0),
@@ -327,7 +329,7 @@ async def display_extended(self, tilt, tilt_values):
327329
self.lcd.update()
328330
# t2 = time.ticks_ms()
329331
# print(f"extended drawing took:{time.ticks_diff(t1, t_start)}, update took:{time.ticks_diff(t2, t1)}")
330-
await asyncio.sleep(getattr(self.config, "display_update_secs", 3))
332+
await asyncio.sleep(self.update_intvl)
331333

332334
def update_indicator(self):
333335
# alternate a little indicator so we know values are being received even if they are not changing
@@ -338,43 +340,45 @@ def update_indicator(self):
338340
self.indicator = not self.indicator
339341
self.lcd.circle(10, 60, 5) # x, y, r
340342

341-
def show_msg(self, msg, append=True):
342-
#self.lcd.set_font("serif")
343-
if append:
344-
#self.startup_msg = self.startup_msg + "\n" + msg
345-
try:
346-
self.startup_msg.append(msg)
347-
except NameError:
348-
self.startup_msg = [msg]
343+
async def show_msg(self, msg=None, append=True):
344+
''' if msg then add to list
345+
display text
346+
'''
347+
intvl = 1 #
348+
if msg:
349+
if append:
350+
# not currently used, could remove append() property
351+
try:
352+
self.startup_msg.append(msg)
353+
except NameError:
354+
self.startup_msg = [msg]
355+
else:
356+
# overwrite last line
357+
try:
358+
self.startup_msg[-1] = msg
359+
except NameError:
360+
self.startup_msg = [msg]
349361
else:
350-
# overwrite last line
351-
#self.overwrite_msg(msg)
352-
try:
353-
self.startup_msg[-1] = msg
354-
except NameError:
355-
self.startup_msg = [msg]
362+
# not adding a message
363+
intvl = self.update_intvl
356364
self.lcd.set_font("bitmap8")
357365
self.lcd.set_pen(self.colour_to_palette["BG"])
358366
self.lcd.clear()
359367
self.lcd.set_thickness(1)
360368
self.lcd.set_pen(self.colour_to_palette["WHITE"])
361369
#self.lcd.text(msg, 0, 65, scale=1)
362370
#self.lcd.text(self.startup_msg, 0, 0)
371+
while len(self.startup_msg) > 7:
372+
# remove oldest messages
373+
del self.startup_msg[0]
363374
self.lcd.text('\n'.join([item for item in self.startup_msg]), 0, 0)
364375
self.lcd.update()
365-
time.sleep(1)
376+
#time.sleep(1)
377+
await asyncio.sleep(intvl)
366378

367-
'''def overwrite_msg(self, new_msg):
368-
# overwrite the last line of a messgae
369-
import re
370-
regex = re.compile("[\n]")
371-
msgs = regex.split(self.startup_msg)
372-
if len(msgs)>0:
373-
msgs[len(msgs)-1] = new_msg
374-
else:
375-
msgs[0] = new_msg
376-
self.startup_msg = '\n'.join([item for item in msgs])
377-
'''
379+
def blocking_show_msg(self, msg, append=True):
380+
''' allow a synchronous display method - used from main.py '''
381+
asyncio.run(self.show_msg(msg, append))
378382

379383

380384
class RGB_Driver:
@@ -401,10 +405,6 @@ def off(self):
401405
# turn off
402406
self._led.set_rgb(0, 0, 0)
403407

404-
# def _init_rgb_task(self):
405-
# # ensure off at start
406-
# self.rgb_led.set_rgb(0,0,0)
407-
408408
async def _flash_rgb_task(self, rgb_colours):
409409
#
410410
self._led.set_rgb(*rgb_colours)
@@ -435,4 +435,4 @@ def r_align(lcd_obj, txt, sz, width):
435435
return int(width - lcd_obj.measure_text(txt, sz))
436436

437437

438-
__version__ = "1.1.2"
438+
__version__ = "1.2.0"

0 commit comments

Comments
 (0)