def __set_wifi_dev_static_ip(sta_if): console_write("[NW: STA] Set device static IP.") stored_ip = cfgget('devip') if 'n/a' not in stored_ip.lower() and '.' in stored_ip: conn_ips = list(sta_if.ifconfig()) # Check ip type before change, conn_ip structure: 10.0.1.X if conn_ips[0] != stored_ip and conn_ips[-1].split( '.')[0:3] == stored_ip.split('.')[0:3]: console_write( "\t| [NW: STA] micrOS dev. StaticIP request: {}".format( stored_ip)) conn_ips[0] = stored_ip try: # IP address, subnet mask, gateway and DNS server sta_if.ifconfig(tuple(conn_ips)) return True # was reconfigured except Exception as e: console_write( "\t\t| [NW: STA] StaticIP conf. failed: {}".format(e)) errlog_add("__set_wifi_dev_static_ip error: {}".format(e)) else: console_write("[NW: STA][SKIP] StaticIP conf.: {} ? {}".format( stored_ip, conn_ips[0])) else: console_write("[NW: STA] IP was not stored: {}".format(stored_ip)) return False # was not reconfigured
def __inject_default_conf(): # Load config and template liveconf = Data.read_cfg_file(nosafe=True) # Remove obsolete keys from conf try: remove( 'cleanup.pds' ) # Try to remove cleanup.pds (cleanup indicator by micrOSloader) console_write("[CONFIGHANDLER] Purge obsolete keys") for key in (key for key in liveconf.keys() if key not in Data.CONFIG_CACHE.keys()): liveconf.pop(key, None) except Exception: console_write( "[CONFIGHANDLER] SKIP obsolete keys check (no cleanup.pds)") # Merge template to live conf Data.CONFIG_CACHE.update(liveconf) # Run conf injection and store console_write( "[CONFIGHANDLER] Inject user config ...") # Data.CONFIG_CACHE try: # [LOOP] Only returns True Data.write_cfg_file() console_write("[CONFIGHANDLER] Save conf struct successful") except Exception as e: console_write( "[CONFIGHANDLER] Save conf struct failed: {}".format(e)) errlog_add('__inject_default_conf error: {}'.format(e)) finally: del liveconf
def scheduler(scheduler_input, irqperiod): """ irqperiod - in sec RAW INPUT SYNTAX: '{cron time}!COMD;{cron time2}!COMD2;...' ! - execute ; - cron task separator """ state = False time_now = localtime()[0:8] # time_now = GEN.__next__() # TODO: remove after test # Actual time - WD, H, M, S cron_time_now = (time_now[-2], time_now[-5], time_now[-4], time_now[-3]) # Cron overall time now in sec - hour in sec, minute in sec, sec now_sec_tuple = (cron_time_now[1] * 3600, cron_time_now[2] * 60, cron_time_now[3]) try: for cron in deserialize_raw_input(scheduler_input): state |= __scheduler_trigger(cron_time_now, now_sec_tuple, cron, deltasec=irqperiod) return state except Exception as e: console_write("scheduler callback error: {}".format(e)) errlog_add('scheduler error: {}'.format(e)) return False
def emergency_mbuff(): emergency_buff_kb = 1000 if cfgget('extirq') or cfgget("timirq"): from micropython import alloc_emergency_exception_buf console_write("[IRQ] Interrupts was enabled, alloc_emergency_exception_buf={}".format(emergency_buff_kb)) alloc_emergency_exception_buf(emergency_buff_kb) else: console_write("[IRQ] Interrupts disabled, skip alloc_emergency_exception_buf configuration.")
def profiling_info(label=""): """ Runtime memory measurements """ if cfgget('dbg'): console_write("{} [PROFILING INFO] - {} {}".format( '~' * 5, label, '~' * 5)) mem_info() console_write("~" * 30)
def deserialize_raw_input(raw_cron_input): try: return tuple( tuple(cron.split('!')) for cron in raw_cron_input.split(';')) except Exception as e: console_write( "deserialize_raw_input: input syntax error: {}".format(e)) errlog_add('deserialize_raw_input input syntax error: {}'.format(e)) return tuple()
def set_ntp_rtc(): err = '' for _ in range(4 if cfgget('cron') else 2): try: ntptime(utc_shift=int(cfgget('gmttime'))) return True except Exception as e: console_write("set_ntp_rtc errer.:{}".format(e)) err = e sleep_ms(100) errlog_add("set_ntp_rtc error: {}".format(err)) return False
def cfgget(key=None): if key is None: return Data.CONFIG_CACHE try: val = Data.CONFIG_CACHE.get(key, None) if val == '...': # Handle special "offloaded" keys return Data.disk_keys(key) return val except Exception as e: console_write("[CONFIGHANDLER] Get config value error: {}".format(e)) errlog_add('cfgget {} error: {}'.format(key, e)) return None
def write_cfg_file(): while True: try: # WRITE JSON CONFIG with open(Data.CONFIG_PATH, 'w') as f: dump(Data.CONFIG_CACHE, f) break except Exception as e: console_write( "[CONFIGHANDLER] __write_cfg_file error {} (json): {}". format(Data.CONFIG_PATH, e)) errlog_add('write_cfg_file error: {}'.format(e)) sleep(0.2) return True
def init(): # Inject user config into template Data.__inject_default_conf() # [!!!] Init selected pinmap - default pinmap calculated by platform pinmap = set_pinmap(Data.CONFIG_CACHE['cstmpmap']) console_write("[PIN MAP] {}".format(pinmap)) # SET dbg based on config settings (inject user conf) DebugCfg.DEBUG = cfgget('dbg') if Data.CONFIG_CACHE['dbg']: # if debug ON, set progress led DebugCfg.init_pled() else: # Show info message - dbg OFF console_write("[micrOS] debug print was turned off")
def enableInterrupt(): """ Set task pool executor in interrupt timer0 Input: timirq(bool), timirqseq(ms), timirqcbf(str) """ console_write("[IRQ] TIMIRQ SETUP: {} SEQ: {}".format(cfgget("timirq"), cfgget("timirqseq"))) console_write("|- [IRQ] TIMIRQ CBF:{}".format(cfgget('timirqcbf'))) if cfgget("timirq"): from machine import Timer # INIT TIMER IRQ with callback function wrapper lm_str = cfgget('timirqcbf') timer = Timer(0) timer.init(period=int(cfgget("timirqseq")), mode=Timer.PERIODIC, callback=lambda timer: exec_lm_pipe_schedule(lm_str))
def enableCron(): """ Set time stump based scheduler aka cron in timer1 Input: cron(bool), cronseq(ms), crontasks(str) """ console_write("[IRQ] CRON IRQ SETUP: {} SEQ: {}".format(cfgget('cron'), cfgget("cronseq"))) console_write("|- [IRQ] CRON CBF:{}".format(cfgget('crontasks'))) if cfgget("cron") and cfgget('crontasks').lower() != 'n/a': from machine import Timer # INIT TIMER 1 IRQ with callback function wrapper lm_str = cfgget('crontasks') sample = int(cfgget("cronseq")/1000) timer = Timer(1) timer.init(period=int(cfgget("cronseq")), mode=Timer.PERIODIC, callback=lambda timer: scheduler(lm_str, sample))
def __select_available_wifi_nw(sta_if, raw_essid, raw_pwd): """ raw_essid: essid parameter, in case of multiple values separator is ; raw_pwd: essid pwd parameter, in case of multiple values separator is ; return detected essid with corresponding password """ for idx, essid in enumerate(raw_essid.split(';')): essid = essid.strip() # Scan wifi network - retry workaround for _ in range(0, 2): if essid in (wifispot[0].decode('utf-8') for wifispot in sta_if.scan()): console_write( '\t| - [NW: STA] ESSID WAS FOUND: {}'.format(essid)) return essid, str(raw_pwd.split(';')[idx]).strip() sleep_ms(1000) return None, ''
def exec_lm_pipe(taskstr): """ Input: taskstr contains LM calls separated by ; Used for execute config callback parameters (IRQs and BootHook) """ try: # Handle config default empty value (do nothing) if taskstr.startswith('n/a'): return True # Execute individual commands - msgobj->"/dev/null" for cmd in (cmd.strip().split() for cmd in taskstr.split(';')): if not exec_lm_core_schedule(cmd): console_write("|-[LM-PIPE] task error: {}".format(cmd)) except Exception as e: console_write("[IRQ-PIPE] error: {}\n{}".format(taskstr, e)) errlog_add('exec_lm_pipe error: {}'.format(e)) return False return True
def read_cfg_file(nosafe=False): conf = {} while True: try: with open(Data.CONFIG_PATH, 'r') as f: conf = load(f) break except Exception as e: console_write( "[CONFIGHANDLER] read_cfg_file error {} (json): {}".format( conf, e)) # Write out initial config, if no config exists. if nosafe: break sleep(0.2) errlog_add('read_cfg_file error: {}'.format(e)) # Return config cache return conf
def __scheduler_trigger(cron_time_now, now_sec_tuple, crontask, deltasec=2): """ SchedulerCore logic cron time now format: WD, H, M, S """ check_time = tuple( int(t.strip()) if t.strip() != '*' else t.strip() for t in crontask[0].split(':')) # Cron actual time (now) parts summary in sec check_time_now_sec = now_sec_tuple[0] + now_sec_tuple[1] + now_sec_tuple[2] # Cron overall requested time in sec - hour in sec, minute in sec, sec check_time_scheduler_sec = int(now_sec_tuple[0] if check_time[1] == '*' else check_time[1] * 3600) \ + int(now_sec_tuple[1] if check_time[2] == '*' else check_time[2] * 60) \ + int(now_sec_tuple[2] if check_time[3] == '*' else check_time[3]) # Time frame +/- corrections tolerance_min_sec = 0 if check_time_now_sec - deltasec < 0 else check_time_now_sec - deltasec tolerance_max_sec = check_time_now_sec + deltasec task_id = "{}:{}|{}".format(check_time[0], check_time_scheduler_sec, crontask[1].replace(' ', '')) # Check WD - WEEK DAY if check_time[0] == '*' or check_time[0] == cron_time_now[0]: # Check H, M, S in sec format between tolerance range if tolerance_min_sec <= check_time_scheduler_sec <= tolerance_max_sec: __cron_task_cache_manager(check_time_now_sec, deltasec) if check_time[3] == '*' or task_id not in LAST_CRON_TASKS: lm_state = exec_lm_core_schedule(crontask[1].split()) if not lm_state: console_write( "[CRON ERROR]NOW[{}] {} <-> {} CONF[{}] EXECUTE[{}] LM: {}" .format(cron_time_now, __convert_sec_to_time(tolerance_min_sec), __convert_sec_to_time(tolerance_max_sec), crontask[0], lm_state, crontask[1])) # SAVE TASK TO CACHE if check_time[3] != '*': # SAVE WHEN SEC not * LAST_CRON_TASKS.append(task_id) return True return False
def run(cls): """ Main method, runs socket server with interpreter shell """ cls.server_console( "[ socket server ] SERVER ADDR: telnet {} {}".format( cfgget("devip"), cls.__port)) try: cfgput('version', cls.__socket_interpreter_version) except Exception as e: console_write( "Export system version to config failed: {}".format(e)) errlog_add('socket run system version export error: {}'.format(e)) cls.__init_socket() while True and cls.__isconn: try: # Evaluate incoming msg via InterpreterShell -> InterpreterCore "Console prompt" is_healthy = shell(cls.__wait_for_msg(), sso=cls) if not is_healthy: console_write( "[EXEC-WARNING] InterpreterShell internal error.") cls.__recovery(is_critic=False) except OSError: # Broken pipe error handling cls.__reconnect() except Exception as e: console_write( "[EXEC-ERROR] InterpreterShell error: {}".format(e)) errlog_add("Socket-InterpreterShell error: {}".format(e)) cls.__recovery(is_critic=True) # Memory dimensioning dump cls.server_console( '[X] AFTER INTERPRETER EXECUTION FREE MEM [byte]: {}'.format( mem_free()))
def type_handler(key, value): value_in_cfg = Data.CONFIG_CACHE[key] try: if isinstance(value_in_cfg, bool): del value_in_cfg if str(value).lower() == 'true': return True if str(value).lower() == 'false': return False raise Exception("type_handler type handling error") if isinstance(value_in_cfg, str): del value_in_cfg return str(value) if isinstance(value_in_cfg, int): del value_in_cfg return int(value) if isinstance(value_in_cfg, float): del value_in_cfg return float(value) except Exception as e: console_write("Input value type error! {}".format(e)) return None
def initEventIRQs(): """ EVENT INTERRUPT CONFIGURATION - multiple """ irqdata = ((cfgget("irq1"), cfgget("irq1_trig"), cfgget("irq1_cbf")), (cfgget("irq2"), cfgget("irq2_trig"), cfgget("irq2_cbf")), (cfgget("irq3"), cfgget("irq3_trig"), cfgget("irq3_cbf")), (cfgget("irq4"), cfgget("irq4_trig"), cfgget("irq4_cbf"))) # [*] hardcopy parameters to be able to resolve cbf-s cbf_resolver = {} for i, data in enumerate(irqdata): irq, trig, cbf = data console_write("[IRQ] EXTIRQ SETUP - EXT IRQ{}: {} TRIG: {}".format(i+1, irq, trig)) console_write("|- [IRQ] EXTIRQ CBF: {}".format(cbf)) pin = physical_pin('irq{}'.format(i+1)) # irq1, irq2, etc. if irq and pin: # [*] update cbf dict by pin number (available in irq callback) cbf_resolver['Pin({})'.format(pin)] = cbf trig = trig.strip().lower() # Init event irq with callback function wrapper # pin_obj = Pin(pin, Pin.IN, Pin.PULL_UP) # TODO: expose parameter pin_obj = Pin(pin, Pin.IN, Pin.PULL_DOWN) # [IRQ] - event type setup if trig == 'down': # pin_obj.irq(trigger=Pin.IRQ_FALLING, handler=lambda pin: print("[down] {}:{}".format(pin, cbf_resolver[str(pin)]))) pin_obj.irq(trigger=Pin.IRQ_FALLING, handler=lambda pin: exec_lm_pipe_schedule(cbf_resolver[str(pin)])) continue if trig == 'both': # pin_obj.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=lambda pin: print("[both] {}:{}".format(pin, cbf_resolver[str(pin)]))) pin_obj.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=lambda pin: exec_lm_pipe_schedule(cbf_resolver[str(pin)])) continue # Default # pin_obj.irq(trigger=Pin.IRQ_RISING, handler=lambda pin: print("[up] {}:{}".format(pin, cbf_resolver[str(pin)]))) pin_obj.irq(trigger=Pin.IRQ_RISING, handler=lambda pin: exec_lm_pipe_schedule(cbf_resolver[str(pin)]))
def set_access_point(_essid, _pwd, _authmode=3): console_write("[NW: AP] SET AP MODE: {} - {} - auth mode: {}".format( _essid, _pwd, _authmode)) sta_if = WLAN(STA_IF) if sta_if.isconnected(): sta_if.active(False) ap_if = WLAN(AP_IF) ap_if.active(True) # Set WiFi access point name (formally known as ESSID) and WiFi authmode (3): WPA2-PSK try: console_write("[NW: AP] Configure") ap_if.config(essid=_essid, password=_pwd, authmode=_authmode) except Exception as e: console_write("[NW: AP] Config Error: {}".format(e)) errlog_add("set_access_point error: {}".format(e)) if ap_if.active() and str(ap_if.config('essid')) == str( _essid) and ap_if.config('authmode') == _authmode: cfgput("devip", ap_if.ifconfig()[0]) console_write("\t|\t| [NW: AP] network config: " + str(ap_if.ifconfig())) set_dev_uid() return ap_if.active()
def __del__(cls): console_write("[ socket server ] <<destructor>>") cls.__close() cls.__s.close()
def server_console(cls, msg): console_write("|" + "-" * cls.__server_console_indent + msg) if cls.__server_console_indent < 50: # if less then max indent cls.__server_console_indent += 1
Designed by Marcell Ban aka BxNxM """ ######################################################### # IMPORTS # ######################################################### from socket import socket, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR from utime import sleep from ConfigHandler import cfgget, cfgput from Debug import console_write, errlog_add from InterpreterShell import shell try: from gc import collect, mem_free except: console_write("[SIMULATOR MODE GC IMPORT]") from simgc import collect, mem_free ######################################################### # SOCKET SERVER CLASS # ######################################################### class SocketServer: """ Socket message data packet layer - send and receive Embedded command interpretation: - hello - version - exit - reboot
def heartbeat(): console_write("<3 heartbeat <3") return "<3 heartbeat <3"
def bootup_hook(): """ Executes when system boots up. """ # Execute LMs from boothook config parameter console_write("[BOOTHOOK] EXECUTION ...") bootasks = cfgget('boothook') if bootasks is not None and bootasks.lower() != 'n/a': console_write("|-[BOOTHOOK] TASKS: {}".format(bootasks)) if exec_lm_pipe(bootasks): console_write("|-[BOOTHOOK] DONE") else: console_write("|-[BOOTHOOK] ERROR") # Set boostmd (boost mode) if cfgget('boostmd') is True: console_write( "[BOOT HOOKS] Set up CPU 16MHz/24MHz - boostmd: {}".format( cfgget('boostmd'))) if platform == 'esp8266': freq(160000000) if platform == 'esp32': freq(240000000) else: console_write("[BOOT HOOKS] Set up CPU 8MHz - boostmd: {}".format( cfgget('boostmd'))) freq(80000000)
def set_wifi(essid, pwd, timeout=60): console_write('[NW: STA] SET WIFI STA NW {}'.format(essid)) # Disable AP mode ap_if = WLAN(AP_IF) if ap_if.active(): ap_if.active(False) del ap_if # Set STA and Connect sta_if = WLAN(STA_IF) sta_if.active(True) # Set custom DHCP hostname sta_if.config(dhcp_hostname=cfgget('devfid')) # Check are we already connected if not sta_if.isconnected(): # Multiple essid and pwd handling with retry mechanism essid, pwd = __select_available_wifi_nw(sta_if, essid, pwd) # Connect to the located wifi network if essid is not None: console_write('\t| [NW: STA] CONNECT TO NETWORK {}'.format(essid)) # connect to network sta_if.connect(essid, pwd) # wait for connection, with timeout set while not sta_if.isconnected() and timeout > 0: console_write( "\t| [NW: STA] Waiting for connection... {} sec".format( timeout)) timeout -= 1 sleep_ms(500) # Set static IP - here because some data comes from connection. (subnet, etc.) if sta_if.isconnected() and __set_wifi_dev_static_ip(sta_if): sta_if.disconnect() del sta_if return set_wifi(essid, pwd) else: console_write( "\t| [NW: STA] Wifi network was NOT found: {}".format(essid)) return False console_write("\t|\t| [NW: STA] network config: " + str(sta_if.ifconfig())) console_write("\t|\t| [NW: STA] CONNECTED: " + str(sta_if.isconnected())) else: console_write("\t| [NW: STA] ALREADY CONNECTED TO {}".format(essid)) cfgput("devip", str(sta_if.ifconfig()[0])) set_dev_uid() return sta_if.isconnected()