async def init(self, timeout=5000): # Performs the ECU wakeup and initialize procedure if required (not ready). # ready state is True if it was finished successfully and false it failed (= timeout). # Also setup K-line by pulling it low before (what uart.sendbreak() normally does). tmr = tms() while True: self.connecting = True # K-line pulldown: only has to be done whenever the ECU was turned off tx = Pin(_TX_PIN, Pin.OUT) # => pulls RX LOW (only when ECU connection achieved for first time) tx(0) sleep_ms(70) # no intr here tx(1) del tx await d(130) self._uart.init(10400, timeout=self._uart_to, bits=8, parity=None, stop=1) self._wTmr = tms() try: await self.query((0xFE,), 0xFF) # no resp excepted. alt: use 0x72 instead of 0xFF for response rType=0E await d(200) # default delay might not be enough, just to be safe on startup await self.diag_query(0x00, 0xF0) # return packet (02 04 00 FA) is validated, Exception otherwise self.ready = True # this point is only reached when no error occurs return except ECUError: if tdiff(self._wTmr, tmr) > timeout: return await d(500) # relax finally: self.connecting = False
async def timer(self): io.oled.img("timer", voff=-10) self._area_text("Slow down\nto 0 km/h", voff=38, lspace=1.2) while ecu.speed > 0: await d(500) self._area_text("Buffering...", voff=38) await d(200) # make sure gc collected cbuf = io.oled.prefetch_chrs("0123456789.", 50) # for faster oled update try: io.led_g(1) self._area_text("Let's go!", voff=38) while ecu.speed <= 0: await d(0) finally: # also if cancelled io.led_g(0) tmr = tms() cdiff = -1 io.oled.fill(0) while ecu.speed < 100: diff = round(tdiff(tms(), tmr) / 1000, 1) if diff >= 10: diff = int(diff) if diff != cdiff: cdiff = diff self._area_big(diff, buf=cbuf) await d(0) # todo scheduling required for speed update, but may be too slow for blitting every 0.1 s self.view = -5 await blink(io.led_g, 180, 150, reps=3) await blink(io.buzz, 800) # todo: test if you can hear it @ 100km/h
async def _uread(self, n=None): # Read and return <n> bytes (blocking) from UART. Exception on timeout. # Read all available (meaning non-blocking) bytes if <n> is None. r = b'' if n is None: if self._uart.any(): # todo use StreamReader.read()? r = self._uart.read() # reads all else: # problem: StreamWriter is blocking (does not support timeouts neither in read nor in readexactly) # therefore we are reading byte by byte (uart.any() returns only 1 or 0, not the amount!) while n > 0: # if n % 5 == 0: await d(0) # allow interrupts in large data bundles tmr = tms() while not self._uart.any(): if tdiff(tms(), tmr) > self._uart_to: # todo use fir again? # if self._fir >= 9: # this must be the nth fail -> now reconnect required self.ready = False raise ECUError(0) # UART timeout # else: # self._fir += 1 await d(0) r += self._uart.read(1) n -= 1 # if self._fir > 0: # self._fir -= 1 return r
async def _uwrite(self, msg): # Writes the bytes <msg> to the UART. # Waits for some ms, ensuring a minimum delay between two msgs (prevents UART timeout). diff = tdiff(tms(), self._wTmr) if diff < HondaECU._TX_DELAY: await d(HondaECU._TX_DELAY - diff) self._uart.write(msg) # no StreamWriter, as it would cause too much scheduling self._wTmr = tms()
def main(): if machine.reset_cause() == machine.HARD_RESET: ctrl.off() elif machine.reset_cause() == machine.SOFT_RESET: # this must be a webapp reset, so start it again start_net() else: tmr = tms() while ctrl.switch_pressed(): # wait for switch to be released. NC = remains on until timeout if tdiff(tms(), tmr) > _SW_PWRUP_TIMEOUT: # break light flash switch NC or hold down long for special fun start_net() break else: # not pressed long enough if not ctrl.powered(): # bike not powered on startup; was motion wakeup ctrl.off() # nothing should be on, but ok... deepsleep() while True: if ctrl.powered(): # bike (and maybe network) running loop.create_task(task_ecu()) loop.create_task(task_ctrl()) loop.run_until_complete(await_pwroff()) # wait for poweroff (bike shutdown) # -> bike powered off # only required if net started afterwards, so no wrong data is displayed: reset_loop() # clear ecu and ctrl task as these are not required now ctrl.clear() ecu.reset() if ctrl.switch_pressed(): # switch held down during shutdown start_net(ctrl.mode * 60 if ctrl.mode > 0 else _NET_DEFAULT_ACTIVE_TIME) # will only set tmr if alr active elif ctrl.mode == 10: ctrl.off() return # to console else: if not net.stay_on(): ctrl.off() net.stop() # stop running network (explicitly kick clients), we want to deepsleep deepsleep() else: # stay_on time not over -> reschedule task loop.create_task(task_net()) # -> only network running, should stay on (and check for powerup meanwhile) loop.run_until_complete(await_pwron())
def run(): if machine.reset_cause() == machine.HARD_RESET: io.off() show_logo() elif machine.reset_cause() == machine.SOFT_RESET: # this must be a webapp reset, so start it again start_net() show_logo() else: tmr = tms() while io.switch_pressed(): # wait for switch to be released. NC = remains on until timeout if tdiff(tms(), tmr) > _SW_PWRUP_TIMEOUT: # brakelight flash switch NC or hold down long for special fun start_net() break else: # BLF switch not pressed (or long enough) if not io.powered(): # bike not powered on startup; was motion wakeup io.off() # nothing should be on, but ok... deepsleep() else: show_logo() while True: if io.powered(): # bike (and maybe network) running loop.create_task(task_ctrl.run()) # first start display loop.create_task(task_ecu()) loop.run_until_complete(await_pwroff()) # wait for poweroff (bike shutdown) # -> bike powered off # only required if net started afterwards, so no wrong data is displayed: reset_loop() # clear ecu and ctrl task as these are not required now io.clear() ecu.reset() if io.switch_pressed(): # switch held down during shutdown start_net() elif not net.stay_on(): io.off() net.stop() # stop running network (explicitly kick clients), we want to deepsleep deepsleep() else: # stay_on time not over -> reschedule task loop.create_task(task_net()) # -> only network running, should stay on (and check for powerup meanwhile) loop.run_until_complete(await_pwron())
def __init__(self): super().__init__() # assuming module references don't change over time, so once e.g. ecu is defined, only its contents will # change. data is stored in format {obj1: {...}, obj2: {...}, ...} self.obj = {o: locals()[o] for o in _OBJS} # e.g. obj['ecu'] = <ECU object at 3abf00c1> self.data = {o: {} for o in _OBJS} # initial update cannot be done here (write only allowed after setup()) -> _update self.conn_tmr = tms() # for checking if client is connected
def start(self, stay_on=0): # with stay_on you can optionally specify a time (minutes), how long the network # is expected to stay on after start even if bike is powered off (0 = shutdown network on poweroff) global _stay_on_for, _stay_on_tmr _stay_on_for = stay_on * 60000 # minutes to ms _stay_on_tmr = tms() if not self.active: self.active = True self._set_ap() self._set_sta() super().start(self._port)
def __init__(self, uart, uart_timeout=800): # note that <uart_timeout> is the timeout for reading a single byte from UART, meaning that reading e. g. # 10 bytes could take up to 10*<uart_timeout>. timeout exceed cause an Error and set ECU to not ready state self._uart = uart self._uart_to = uart_timeout # self.sreader = asyncio.StreamReader(uart) self._wTmr = tms() # to make sure there is a delay between two msgs being sent over UART self.ready = False # set to False whenever the ECU has to be woken up using init-method # self._fir = 0 # fails in a row counter. will be increased and set back to zero if read was successful self.connecting = False # set to True while the ECU is trying to connect to K-Line
def routine(self): # main routine, executed all the time the client is active self.obj['io'].oled.println("handle client") if tdiff(tms(), self.conn_tmr) > _WS_INACT_TO*1000: self.close() self.obj['io'].oled.println("closed client") return msg = self.read() if msg: # client is asking for sth (not empty or None) msg = msg.decode('utf-8') try: for m in json.loads('[' + msg.replace("}{", "},{") + ']'): # can be multiple, therefore this shit... self.obj['io'].oled.println(repr(m)) self.execute(m) except ValueError: # invalid JSON pass self._update() # update data locally and submit changes to the client
def task_view_menu(): # todo add async io.oled.fill(0) io.oled.text("MENU", y=14, hspace=3) VSPACING = 13 idx_sel = -1 def blit_opt(txt, **kw): nonlocal idx_sel idx_sel += 1 io.oled.text(txt, y=30 + idx_sel * VSPACING, **kw) blit_opt("Brakeflash") blit_opt("Turn LED " + ("on" if True else "off")) # todo read relay state blit_opt("Turn WiFi " + ("on" if False else "off")) # todo read relay state blit_opt("Timer 0-100") blit_opt("Silent Warn") blit_opt("Horn Warn") blit_opt("Close") IDX_SEL_MAX = idx_sel # idx in range of possible selections (0-max), show = 0 or 1 def sel_draw(idx, show): io.oled.rect(-1, 28 + idx * VSPACING, io.oled.w + 2, 12, show) def sel_move(): nonlocal idx_sel sel_draw(idx_sel, 0) idx_sel += 1 if idx_sel > IDX_SEL_MAX: idx_sel = 0 sel_draw(idx_sel, 1) io.oled.show() sel_draw(IDX_SEL_MAX, 1) # initial selection: back io.oled.show() # todo: loop for checking if button pressed short (move) or long (-> break loop) # todo: meanwhile check: reblit menu if wifi/led state changes (over network) # todo: also break loop if no action for 12 sec for i in range(4): sleep_ms(400) sel_move() io.oled.fill(0) if idx_sel == 0: io.oled.img("brake") io.oled.show() # todo flash until key pressed elif idx_sel in (4, 5): for i in range(20): # todo async endless io.oled.img("warn") io.oled.show() sleep_ms(150) io.oled.fill(0) io.oled.show() sleep_ms(100) elif idx_sel == 3: io.oled.img("timer", voff=-10) if True: # todo: kmh is > 0 io.oled.text("Slow down", y=io.oled.h - 35) io.oled.text("to 0 km/h", y=io.oled.h - 26) io.oled.show() while False: # todo: kmh > 0 and not pressed button pass sleep_ms(1000) # todo io.oled.fill_rect(0, io.oled.h - 35, io.oled.w, 35, 0) io.oled.text("Prefetching...", y=io.oled.h - 31) io.oled.show() cbuf = io.oled.prefetch_chrs("0123456789.", 50) # for faster oled update io.oled.fill_rect(0, io.oled.h - 35, io.oled.w, 35, 0) io.oled.text("Get going!", y=io.oled.h - 31) io.oled.show() while False: # todo: kmh <= 0 and not pressed button pass sleep_ms(1000) # todo tmr = tms() area = (0, 0, io.oled.w, io.oled.h) cdiff = -1 while True: # todo: km/h < 100 # todo exit on click diff = round(tdiff(tms(), tmr) / 1000, 1) if diff >= 10: diff = int(diff) if diff != cdiff: cdiff = diff io.oled.fill_rect(*(area + (0, ))) # clear previous area io.oled.big(diff, buf=cbuf) io.oled.show()
async def task_ctrl(): # mode based on switch (e.g. break light flash) ctrl.sw_pressed = False sw_tmr = tms() # timer to check duration of switch down lap_tmr = tms() # timer for special mode 0-100 km/h measurement (reset at 0 km/h) while True: # check switch (BLF and special modes): if ctrl.sw_pressed != ctrl.switch_pressed(): # just pressed or released (state is updated after methodcal) if ctrl.sw_pressed: # just pressed: sw_tmr = tms() # set timer to check duration later if ctrl.mode != 0: # switch pressed while in special mode ctrl.mode = -ctrl.mode # to reset from any special mode to mode 0 ctrl.led_g(1) else: # just released -> apply mode now if ctrl.mode < 0: if ctrl.mode == -1: # was mode 1 before ctrl.set_rly('BL', False) ctrl.mode = 0 # reset to mode 0 without break light flashing ctrl.led_g(0) elif ctrl.mode == 0: for _ in range(_SW_BL_FLASH_COUNT): ctrl.set_rly('BL', True) sleep_ms(90) ctrl.set_rly('BL', False) sleep_ms(70) elif ctrl.mode == 2: lap_tmr = tms() # this val is only used if this mode is set when driving and then reaching >100 elif ctrl.mode == 7: # activate network if not active (no min active time -> until pwrdown) start_net(0) # already running? just change stayon time to "until poweroff" ctrl.mode = 0 # instant reset elif ctrl.mode == 8: # disable the network iface net.stop() ctrl.mode = 0 # instant reset ctrl.seg_clear() elif ctrl.sw_pressed and ctrl.mode >= 0: # held down -> check duration if tdiff(tms(), sw_tmr) >= _SW_HOLD_THRESH: # special mode ctrl.mode += 1 ctrl.seg_digit(ctrl.mode % 10) ctrl.led_g(1) await d(150) ctrl.led_g(0) sw_tmr = tms() # for next special mode if ctrl.mode != 0: await d(0) # let ECU work continue # skip io.part, cause increasing special mode is non-interruptable on 7-seg # mode-dependent, but ecu independet stuff: if ctrl.mode == 1: # warn mode ctrl.set_rly('BL', not ctrl.rly['BL']) await d(180) # handle cockpit stuff depending on ecu: if ecu.ready: # ECU connected if ctrl.mode == 2: # set timer and green LED based on speed if ecu.speed == 0: lap_tmr = tms() ctrl.led_g(1) elif ecu.speed >= 100: lap_time = tdiff(tms(), lap_tmr) # e. g. 15762 ms (= 15.8s) ctrl.led_g(1) lt_s = lap_time // 1000 # seconds (front part) e.g. 15 lt_h = round((lap_time - lt_s * 1000) / 100) # hundreth e.g. 8 ctrl.seg_clear() await d(1000) ctrl.seg_show_num(lt_s, lt_h) ctrl.led_g(0) ctrl.mode = 0 del lap_tmr # will be reset when mode is set to 1 again elif tdiff(tms(), lap_tmr) > 60000: # reset mode as it does not seem to be used ctrl.mode = 0 await blink(ctrl.led_g) else: # measurement ctrl.led_g(0) if not ecu.engine or ecu.rpm <= 0: # engine not running (probably parking) or engine just starting up ctrl.seg_char('-') elif ecu.idle or ecu.gear is None: if ecu.sidestand or ecu.speed < _REV_CIRCLE_SPEED_THRESH and ecu.tp > 0: # revving ctrl.seg_circle() await d(int(-28*log(ecu.rpm) + 262)) else: await ctrl.seg_flash(250) continue # skip second yield else: # shift light if required: ctrl.seg_digit(ecu.gear) if ecu.rpm > _SHIFT_LIGHT_RPM_THRESH and ecu.gear < 6: await ctrl.seg_flash(60) continue # skip second yield await d(0) # let ECU work
class IOTasks: VIEW_ECU_ATTR = ( # add all ECU attributes (with description + unit) that should be displayed in a view ("ect", "Engine\nCoolant", "° Celsius"), ("iat", "Intake Air", "° Celsius"), ("bat", "Battery", "Volt"), ("map", "Manifold\nPressure", "kPa"), ) def __init__(self): self._area = None # used to hold an OLED area to be cleared later (should be reset on each task change) self.task = None # currently running oled view task gen-object (will be killed on view change) self.view = 1 # current view state (int), that may map to one task function (see __num_to_task()) self.stay_on = False # set to True if the ECU is expected to stay on because of the current view def _area_clear(self): if self._area is not None: io.oled.fill_rect(*(self._area + (0,))) # clear previous area def _area_big(self, t, **kw): # clears old area and displays the txt with big font self._area_clear() self._area = io.oled.big(t, **kw) io.oled.show() def _area_text(self, t, **kw): self._area_clear() self._area = io.oled.text(t, **kw) io.oled.show() async def brakeflash(self): io.oled.img("brake") io.oled.show() while True: io.set_rly('BL', True) sleep_ms(90) # no intr (not to be canceled here) io.set_rly('BL', False) await d(70) async def timer(self): io.oled.img("timer", voff=-10) self._area_text("Slow down\nto 0 km/h", voff=38, lspace=1.2) while ecu.speed > 0: await d(500) self._area_text("Buffering...", voff=38) await d(200) # make sure gc collected cbuf = io.oled.prefetch_chrs("0123456789.", 50) # for faster oled update try: io.led_g(1) self._area_text("Let's go!", voff=38) while ecu.speed <= 0: await d(0) finally: # also if cancelled io.led_g(0) tmr = tms() cdiff = -1 io.oled.fill(0) while ecu.speed < 100: diff = round(tdiff(tms(), tmr) / 1000, 1) if diff >= 10: diff = int(diff) if diff != cdiff: cdiff = diff self._area_big(diff, buf=cbuf) await d(0) # todo scheduling required for speed update, but may be too slow for blitting every 0.1 s self.view = -5 await blink(io.led_g, 180, 150, reps=3) await blink(io.buzz, 800) # todo: test if you can hear it @ 100km/h async def warn(self): self.stay_on = True io.oled.text("Hold switch\nto add horn" if self.view != -4 else "Press to stop", voff=40, lspace=1.2) try: while True: if self.view == -4: io.set_rly('HO', not io.rly['HO']) for _ in range(3): # horn on/off time = x * breaklight on/off time io.set_rly('BL', not io.rly['BL']) if io.rly['BL']: self._area = io.oled.img("warn", voff=-6) else: self._area_clear() io.oled.show() await d(180) finally: # make sure to turn off relay when task gets cancelled io.set_rly('BL', False) if self.view == -4: io.set_rly('HO', False) self.stay_on = False async def view_gear(self): cgear = None # currently displayed on OLED# if not ecu.ready: await d(100) # since view gear is the first task, it will be killed and restarted immediately while True: if ecu.sidestand: # parking ngear = 'P' elif ecu.rpm <= 0 or not ecu.engine: # engine not running, but no sidestand ngear = 'X' elif ecu.speed <= _DRIVING_SPEED_THRESH: # idling while standing ngear = '-' elif ecu.idle or ecu.gear is None: # idling while driving = probably shifting await blink(io.oled.power, 200, 350, 0) continue # skip second yield and gear-change-check else: ngear = ecu.gear if ngear != cgear: self._area_big(ngear) cgear = ngear await d(0) async def view_ecu(self, attr_nr): io.oled.text(IOTasks.VIEW_ECU_ATTR[attr_nr][1], voff=-33, lspace=1.2) io.oled.text(IOTasks.VIEW_ECU_ATTR[attr_nr][2], y=105) cval = None while True: nval = getattr(ecu, IOTasks.VIEW_ECU_ATTR[attr_nr][0]) if cval != nval: cval = nval self._area_big(cval, voff=8) await d(100) async def _kill_task(self): if self.task is not None: await d(0) # the task has to be started, otherwise cancel will block todo: remove if possible cancel(self.task) await d(0) # be sure it gets killed io.oled.fill(0) # prepare oled for next task self._area = None # no area in use async def _setup_task(self): # starts the task that should run given self.view await self._kill_task() self.task = self.__num_to_task(self.view) if self.task is not None: loop.create_task(self.task) def __num_to_task(self, nr): # returns view task: simple views (> 0), menu view (= 0), special modes (< 0) if nr == 1: return self.view_gear() elif 0 <= nr-2 < len(IOTasks.VIEW_ECU_ATTR): return self.view_ecu(nr-2) elif nr == -1: return self.brakeflash() elif nr == -2: return self.timer() elif nr == -3 or nr == -4: # -4 = with horn return self.warn() # -5 = timer finished (time displayed, do not change) => None # 0 = menu -> no view => None # else = invalid => None def __menu_sel_to_view(self, sel_idx): # Maps the index of a menu selection to the corresponding view and returns it. setup_task has to be called. # For some menu entries only one action takes place, but the view will return to gear view. if sel_idx == 0: # brakeflash return -1 elif sel_idx == 1: # timer return -2 elif sel_idx == 2: # led on/off io.set_rly('LED', not io.rly['LED']) elif sel_idx == 3: # wifi on/off if not net.active: start_net(0) # until bike shutdown else: net.stop() elif sel_idx == 4: # warn mode return -3 return 1 # for selection 'Close', no selection (None/-1), and the ones without a specific view (autoreturn) async def run(self): # reacts to switch state changes (eg by mode change), should run all the time menu = MenuView() if ecu.ready: # this should not happen, but just in case the ECU is ready before display task starts, show gear await self._setup_task() # initial task engine_warm = None # ECT heated up? if not, blue LED on while True: if not ecu.ready and io.powered(): # wait for ECU to be connected (only if powered, otherwise don't wait) await self._kill_task() # suspend current view task while not ecu.ready: while ecu.connecting: await blink(io.led_b, 100, 400) else: await d(300) await self._setup_task() # now bring it up again engine_warm = None # resetting blue LED required if (not io.sw_pressed) & io.switch_pressed(): # new BLF switch press; binary and to avoid short-circuiting sw_tmr = tms() while tdiff(tms(), sw_tmr) < _SW_HOLD_THRESH and io.switch_pressed(): # until released or long press await d(10) if io.sw_pressed: # long press if self.view > 0: self.view = 0 # show menu await self._kill_task() menu.show() elif self.view == 0: # menu selection self.view = self.__menu_sel_to_view(menu.selected()) await self._setup_task() elif self.view == -3: # silent warn self.view = -4 # horn warn await self._setup_task() # restart required to load text else: # short press if self.view > 0: self.view += 1 # display next view if self.view > 1+len(IOTasks.VIEW_ECU_ATTR): self.view = 1 await self._setup_task() elif self.view == 0: menu.select_next() elif self.view == -5: # timer finished self.view = -2 # restart timer await self._setup_task() else: # exit special mode self.view = 1 await self._setup_task() if engine_warm != (ecu.ect >= _ENGINE_WARM_THRESH): engine_warm = (ecu.ect >= _ENGINE_WARM_THRESH) io.led_b(int(engine_warm)) # blue LED on if engine not warm yet await d(0) # let ECU work
def stay_on(self): # returns True, if the chip is expected not to shutdown because of stay_on time return tdiff(tms(), _stay_on_tmr) < _stay_on_for
def execute(self, msg): # execute a msg object (must be dict unpacked from json) global _stay_on_for, _stay_on_tmr try: if 'PING' in msg: # send stay-on-time (secs) as ACK self.obj['io'].oled.println("do ACK") self.send(ACK=max((_stay_on_for - tdiff(tms(), _stay_on_tmr)) // 1000, 0)) self.obj['io'].oled.println("acknowledged PING " + str(msg["PING"])) self.conn_tmr = tms() # reset timer elif 'SET' in msg and 'TO' in msg: # client wants to set local variable self._set_var(msg['SET'], msg['TO']) elif 'CMD' in msg: # ESP command without args cmd = msg['CMD'] if cmd == "reboot": reset() # soft reset elif cmd == "deepsleep": deepsleep() elif cmd == "console": raise Exception("net return") elif cmd == "ifconfig": # returns AP and STA IP and Port; 0.0.0.0 if not connected aw = "" ap = network.WLAN(network.AP_IF) if ap.active(): aw += "AP:\n{}\n\n".format(ap.ifconfig()) sta = network.WLAN(network.STA_IF) if sta.active(): aw += "Station:\n{}\n\n".format(sta.ifconfig()) aw += "Port: " + str(read_cfg("port")) self.send(ALERT=aw) elif cmd == "netls": self.send(ALERT='\n\n'.join(["ID: %s\nPW: %s" % (kid, kpw) for (kid, kpw) in read_cfg("knets")])) elif cmd == "netadd": cfg = read_cfg() knets = cfg["knets"] for i in range(len(knets)): if knets[i][0] == msg['ID']: knets[i][1] = msg['PW'] break else: # not found cfg["knets"].append((msg['ID'], msg['PW'])) write_cfg(cfg) elif cmd == "netrm": cfg = read_cfg() knets = cfg["knets"] for i in range(len(knets)): if knets[i][0] == msg['ID']: knets.pop(i) break else: return # not found -> nothing to do write_cfg(cfg) elif cmd == "nettime": _stay_on_for = int(msg['VAL']) * 1000 # ms _stay_on_tmr = tms() elif cmd == "text": self.obj['io'].oled.clear() self.obj['io'].oled.text(msg["MSG"]) self.obj['io'].oled.show() elif cmd == "println": self.obj['io'].oled.println(msg["MSG"]) # elif 'GET' in msg: # client wants to get local variable(s) # if not msg['GET']: # empty string or None -> enquiring all cached data # self.send(UPDATE=self.data) # else: # self._get_var(msg['GET']) except ValueError: # not in JSON format pass
from pwr import deepsleep from machine import reset from uasyncio import get_event_loop import network _CONFIG_FILE = "netconf.json" # this file must contain, hostname, password, known networks, port (...) _OBJS = ('ecu', 'io') # all non-private vars in these objects will be monitored and sent to clients on update _WS_INACT_TO = 14 # client is closed when no message (incl PING!) received for _ s (>= WS_SYN_INTERVAL) _AP_CONF = ('192.168.0.1', '255.255.255.0', '192.168.0.1', '') # ip, subnet, gateway, dns _HTML_INDEX = "/html/index.html" # None = unused _HTML_404 = "/404.html" # None = unused # global because both NetServer and NetClient need access _stay_on_for = 0 # chip is expected to remain on even if bike powered off for this time _stay_on_tmr = tms() # in respect to this timer (set on network start) loop = get_event_loop() # current event loop (same as in main()) def read_cfg(key=None): # key can be specified for a single entry, otherwise returns all data try: with open(_CONFIG_FILE, 'r') as f: dat = json.loads(f.read()) if key is not None: return dat[key] return dat except KeyError: return None