def disconnectWLAN(self): try: from network import WLAN wlan=None if self.isWipy1(): wlan=WLAN() else: wlan=WLAN(mode=WLAN.STA) wlan.disconnect() except: pass
def wifi_connect(): global client, wlan wlan = WLAN(mode=WLAN.STA) wlan.disconnect() networks = wlan.scan() for net in networks: if net.ssid == WIFI_AP['name']: print('Wifi connecting...') wlan.connect(ssid=net.ssid, auth=(net.sec, WIFI_AP['pass']), timeout=40) while not wlan.isconnected(): idle() sleep(1) break else: return False try: print('MQTT connecting...') client = MQTTClient(client_id="5bc8d724c03f971859b7747b", server="things.ubidots.com", user="******", password="******", port=1883) #client.set_callback(sub_cb) if client.connect() == -1: return False else: return True #client.subscribe(topic="youraccount/.../...") except Exception as e: return False
def mqtt_log(payload): #_AP = {'name': 'INFOTECH', 'pass': '******'} _AP = {'name': 'RPiAP-DP', 'pass': '******'} wlan = WLAN(mode=WLAN.STA) wlan.disconnect() nets = wlan.scan() for net in nets: if net.ssid == _AP['name']: print('Wifi connecting...') wlan.connect(ssid=net.ssid, auth=(net.sec, _AP['pass']), timeout=40) while not wlan.isconnected(): idle() sleep(1) break else: return False try: print('MQTT connecting...') client = MQTTClient("Sipy", server="192.168.56.1", port=1883) #client.set_callback(sub_cb) if client.connect() == -1: return False print('MQTT publish') #client.subscribe(topic="youraccount/.../...") client.publish(topic="sipy/log", msg=payload) except MQTTException as e: return False
def find_wifi(testCase=None): wlan = WLAN(mode=WLAN.STA) try: if isinstance(testCase, Exception): raise testCase wlan.disconnect() nets = wlan.scan() for net in nets: if net.ssid == WIFI_AP['name']: #print(net, net[4]) if not testCase == 'Not found': rssi = net[4] break else: rssi = -10000 wlan.deinit() except Exception as e: return -10000 if testCase is not None and not testCase == 'Not found': rssi = testCase if rssi >= 0: return -10000 return rssi
def wifi_connect(): while True: try: sta_if = WLAN(STA_IF) sta_if.active(True) sta_if.disconnect() sta_if.connect(*WIFI_SSID_PASSWORD) break time.sleep(0.5) except Exception as e: print("Error in Wlan connect: [Exception] %s: %s" % (type(e).__name__, e))
def disconnectWLAN(self): try: from network import WLAN wlan = None if self.isWipy1(): wlan = WLAN() else: wlan = WLAN(mode=WLAN.STA) wlan.disconnect() except: pass
def set_wifi(essid, pwd, timeout=60): console_write('[NW: STA] SET WIFI: {}'.format(essid)) essid_found = False # 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) if not sta_if.isconnected(): console_write('\t| [NW: STA] CONNECT TO NETWORK {}'.format(essid)) # Scan wifi network - retry workaround for _ in range(0, 2): if essid in (wifispot[0].decode('utf-8') for wifispot in sta_if.scan()): essid_found = True console_write( '\t| - [NW: STA] ESSID WAS FOUND {}'.format(essid_found)) break sleep(1) # Connect to the located wifi network if essid_found: # 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... " + str(timeout) + "/60") timeout -= 1 sleep(0.5) # Set static IP - here because some data comes from connection. 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_uid_macaddr_hex(sta_if) return sta_if.isconnected()
def wifi2connect(ssid=None, password=None): from network import WLAN, STA_IF STA = WLAN(STA_IF) STA.active(True) STA.disconnect() wfScan=STA.scan() nets=[] [nets.append(nw[0].decode("utf-8")) for nw in wfScan] if ssid in nets: STA.connect(ssid if ssid else APSSID, password if password else APSSIDPW) else: print("\n".join(nets), end="\n"+"="*80+"\n") STA.active(False)
def station_connected(station: WLAN, wifiLogger: Logger): #TODO: remove print print("Connected...Testing Access...") wifiLogger.info("Connected...Testing Access...") resolved = getaddrinfo("pmtlogger.000webhostapp.com", 80) if resolved == []: #TODO: remove print print("No Internet Access") wifiLogger.debug("No Internet Access") station.disconnect() else: #TODO: remove print print("Internet Accessible") wifiLogger.debug("Internet Accessible")
class ConnectWIFI: def __init__(self): self.wlan = WLAN(mode=WLAN.STA) def connectwifi(self): if self.isConnected(): print("Wifi Connected") else: self.SSID = config.wifissid self.PASSWORD = config.wifipassword timeout = time.time() + 10 self.wlan.connect(self.SSID, auth=(WLAN.WPA, self.PASSWORD), channel=9) while not self.wlan.isconnected(): if time.time() > timeout: pycom.rgbled(config.LED_ERROR) time.sleep(0.5) pycom.rgbled(config.LED_ERROR) machine.reset() print(".", end="") pycom.rgbled(config.LED_blink_WIFI) time.sleep(0.5) pycom.rgbled(0x000000) time.sleep(0.5) machine.idle() # save power while waiting print('.........WiFi connected........') print("Wifi Config --> ", self.wlan.ifconfig()) def isConnected(self): if self.wlan.isconnected(): return True else: return False def deinit(self): print('Deinitializing wifi radio') self.wlan.deinit() def disconnect(self): print("disconnecting wifi") self.wlan.disconnect() def reconnect(self): if self.isConnected(): print("wifi connected") else: print("Trying to reconnect") self.__init__() self.connectwifi()
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()
def disconnectWLAN(self): # disconnect wlan because it spams debug messages that disturb the monitor protocol try: from network import WLAN wlan = None if self.isWipy1(): wlan = WLAN() else: wlan = WLAN(mode=WLAN.STA) wlan.disconnect() except: # if wifi disconnect fails for whatever reason, let it continue to sync # often this is the 'OSError: the requested oparation is not possible' thrown by the wlan.disconnect line pass
def connect_wifi_sdk(ssid, pw): from network import WLAN from network import STA_IF import machine wlan = WLAN(STA_IF) nets = wlan.scan() if (wlan.isconnected()): wlan.disconnect() wlan.connect(ssid, pw) while not wlan.isconnected(): machine.idle() # save power while waiting print('WLAN connection succeeded!') break print("connected:", wlan.ifconfig())
def wlan_scan(): while (True): global oled wlan = WLAN(STA_IF) wlan.active(True) if wlan.isconnected(): wlan.disconnect() list = wlan.scan() oled.fill(0) if not all(list): oled.text('Searching networks.', 0, 0) oled.show() else: oled.text('Networks found: ', 0, 0) oled.show() for i in range(len(list)): oled.text(list[i][0].decode(), 0, (i * 8) + 8) oled.show() sleep(5)
class WIFI(Connection): def __init__(self, networks: dict): from network import WLAN self.wlan = WLAN(mode=WLAN.STA) self.networks = networks def connect(self): if self.wlan.isconnected(): return for _ in range(4): nets = self.wlan.scan() print("\tsearching for wifi networks...") for net in nets: if net.ssid in self.networks: ssid = net.ssid password = self.networks[ssid] print('\twifi network ' + ssid + ' found, connecting ...') self.wlan.connect(ssid, auth=(net.sec, password), timeout=5000) while not self.wlan.isconnected(): machine.idle() # save power while waiting print('\twifi network connected') print('\tIP address: {}'.format(self.wlan.ifconfig())) print('\tMAC address: {}\n'.format( hexlify(machine.unique_id(), ':').decode().upper())) return print("!! no usable networks found, trying again in 30s") print("!! available networks:") print("!! " + repr([net.ssid for net in nets])) machine.idle() time.sleep(30) raise OSError("!! unable to connect to WIFI network.") def isconnected(self) -> bool: return self.wlan.isconnected() def disconnect(self): if self.wlan.isconnected(): self.wlan.disconnect()
def ConnectWLAN(onOff): wlan = WLAN() # Connect to wlan if onOff: wlan.antenna(wlanAntType) if machine.reset_cause() != machine.SOFT_RESET: wlan.init(mode=WLAN.STA) wlan.ifconfig(config=(ip, subnet, router, dns)) if not wlan.isconnected(): wlan.connect(wlanName, auth=(wlanType, wlanPass), timeout=5000) while not wlan.isconnected(): machine.idle() # Disconnect from wlan else: wlan.disconnect() while wlan.isconnected(): machine.idle()
class NanoGateway: """ Nano gateway class, set up by default for use with TTN, but can be configured for any other network supporting the Semtech Packet Forwarder. Only required configuration is wifi_ssid and wifi_password which are used for connecting to the Internet. """ PROTOCOL_VERSION = const(2) PUSH_DATA = const(0) PUSH_ACK = const(1) PULL_DATA = const(2) PULL_ACK = const(4) PULL_RESP = const(3) TX_ERR_NONE = 'NONE' TX_ERR_TOO_LATE = 'TOO_LATE' TX_ERR_TOO_EARLY = 'TOO_EARLY' TX_ERR_COLLISION_PACKET = 'COLLISION_PACKET' TX_ERR_COLLISION_BEACON = 'COLLISION_BEACON' TX_ERR_TX_FREQ = 'TX_FREQ' TX_ERR_TX_POWER = 'TX_POWER' TX_ERR_GPS_UNLOCKED = 'GPS_UNLOCKED' UDP_THREAD_CYCLE_MS = const(10) STAT_PK = { 'stat': { 'time': '', 'lati': 0, 'long': 0, 'alti': 0, 'rxnb': 0, 'rxok': 0, 'rxfw': 0, 'ackr': 100.0, 'dwnb': 0, 'txnb': 0 } } RX_PK = { 'rxpk': [{ 'time': '', 'tmst': 0, 'chan': 0, 'rfch': 0, 'freq': 0, 'stat': 1, 'modu': 'LORA', 'datr': '', 'codr': '4/5', 'rssi': 0, 'lsnr': 0, 'size': 0, 'data': '' }] } TX_ACK_PK = {'txpk_ack': {'error': ''}} def __init__(self, wifi_ssid, wifi_password, gateway_id=None, server='router.eu.thethings.network', port=1700, frequency=868100000, datarate='SF7BW125', ntp_server='pool.ntp.org', ntp_period=3600): # If unset, set the Gateway ID to be the first 3 bytes # of MAC address + 'FFFE' + last 3 bytes of MAC address if gateway_id is None: gateway_id = ubinascii.hexlify(machine.unique_id()).upper() gateway_id = gateway_id[:6] + 'FFFE' + gateway_id[6:12] self.gateway_id = gateway_id self.server = server self.port = port self.frequency = frequency self.datarate = datarate self.wifi_ssid = wifi_ssid self.wifi_password = wifi_password self.ntp_server = ntp_server self.ntp_period = ntp_period self.server_ip = None self.rxnb = 0 self.rxok = 0 self.rxfw = 0 self.dwnb = 0 self.txnb = 0 self.sf = self._dr_to_sf(self.datarate) self.bw = self._dr_to_bw(self.datarate) self.stat_alarm = None self.pull_alarm = None self.uplink_alarm = None self.wlan = None self.sock = None self.udp_stop = False self.udp_lock = _thread.allocate_lock() self.lora = None self.lora_sock = None self.rtc = machine.RTC() def start(self): """ Starts the nano gateway. """ self.log('Starting nano gateway with id {}', self.gateway_id) # Change WiFi to STA mode and connect self.wlan = WLAN(mode=WLAN.STA) self._connect_to_wifi() # Get a time sync self.log('Syncing time with {} ...', self.ntp_server) self.rtc.ntp_sync(self.ntp_server, update_period=self.ntp_period) while not self.rtc.synced(): utime.sleep_ms(50) self.log('RTC NTP sync complete') # Get the server IP and create an UDP socket self.server_ip = usocket.getaddrinfo(self.server, self.port)[0][-1] self.log('Opening UDP socket to {} ({}) port {}...', self.server, self.server_ip[0], self.server_ip[1]) self.sock = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM, usocket.IPPROTO_UDP) self.sock.setsockopt(usocket.SOL_SOCKET, usocket.SO_REUSEADDR, 1) self.sock.setblocking(False) # Push the first time stat immediately self._push_data(self._make_stat_packet()) # Create the alarms self.stat_alarm = machine.Timer.Alarm( handler=lambda t: self._push_data(self._make_stat_packet()), s=60, periodic=True) self.pull_alarm = machine.Timer.Alarm( handler=lambda u: self._pull_data(), s=25, periodic=True) # Start the UDP receive thread self.udp_stop = False _thread.start_new_thread(self._udp_thread, ()) # Initialize the LoRa radio in LORA mode self.log('Setting up LoRa socket on {:.1f} Mhz using {}', self._freq_to_float(self.frequency), self.datarate) self.lora = LoRa(mode=LoRa.LORA, frequency=self.frequency, bandwidth=self.bw, sf=self.sf, preamble=8, coding_rate=LoRa.CODING_4_5, tx_iq=True) # Create a raw LoRa socket self.lora_sock = usocket.socket(usocket.AF_LORA, usocket.SOCK_RAW) self.lora_sock.setblocking(False) self.lora.callback(trigger=(LoRa.RX_PACKET_EVENT | LoRa.TX_PACKET_EVENT), handler=self._lora_cb) self.log('Nano gateway online') def stop(self): """ Stops the nano gateway. """ self.log('Stopping...') # Send the LoRa radio to sleep self.lora.callback(trigger=None, handler=None) self.lora.power_mode(LoRa.SLEEP) # Stop the NTP sync self.rtc.ntp_sync(None) # Cancel all the alarms self.stat_alarm.cancel() self.pull_alarm.cancel() # Signal the UDP thread to stop self.udp_stop = True while self.udp_stop: utime.sleep_ms(50) # Disable WLAN self.wlan.disconnect() self.wlan.deinit() def _connect_to_wifi(self): self.wlan.connect(self.wifi_ssid, auth=(None, self.wifi_password)) while not self.wlan.isconnected(): utime.sleep_ms(50) self.log('WiFi connected: {}', self.wifi_ssid) def _dr_to_sf(self, dr): sf = dr[2:4] if sf[1] not in '0123456789': sf = sf[:1] return int(sf) def _dr_to_bw(self, dr): bw = dr[-5:] if bw == 'BW125': return LoRa.BW_125KHZ elif bw == 'BW250': return LoRa.BW_250KHZ else: return LoRa.BW_500KHZ def _sf_bw_to_dr(self, sf, bw): dr = 'SF' + str(sf) if bw == LoRa.BW_125KHZ: return dr + 'BW125' elif bw == LoRa.BW_250KHZ: return dr + 'BW250' else: return dr + 'BW500' def _lora_cb(self, lora): """ Event listener for LoRa radio events. """ events = lora.events() if events & LoRa.RX_PACKET_EVENT: self.rxnb += 1 self.rxok += 1 rx_data = self.lora_sock.recv(256) stats = lora.stats() packet = self._make_node_packet(rx_data, self.rtc.now(), stats.rx_timestamp, stats.sfrx, self.bw, stats.rssi, stats.snr) self.log('Received packet: {}', packet) self._push_data(packet) self.rxfw += 1 if events & LoRa.TX_PACKET_EVENT: self.log('Re-initing LoRa radio after transmission') self.txnb += 1 lora.init(mode=LoRa.LORA, frequency=self.frequency, bandwidth=self.bw, sf=self.sf, preamble=8, coding_rate=LoRa.CODING_4_5, tx_iq=True) def _freq_to_float(self, frequency): """ MicroPython has some inprecision when doing large float division. To counter this, this method first does integer division until we reach the decimal breaking point. This doesn't completely elimate the issue in all cases, but it does help for a number of commonly used frequencies. """ divider = 6 while divider > 0 and frequency % 10 == 0: frequency = frequency // 10 divider -= 1 if divider > 0: frequency = frequency / (10**divider) return frequency def _make_stat_packet(self): now = self.rtc.now() self.STAT_PK['stat']['time'] = '%d-%02d-%02d %02d:%02d:%02d GMT' % ( now[0], now[1], now[2], now[3], now[4], now[5]) self.STAT_PK['stat']['rxnb'] = self.rxnb self.STAT_PK['stat']['rxok'] = self.rxok self.STAT_PK['stat']['rxfw'] = self.rxfw self.STAT_PK['stat']['dwnb'] = self.dwnb self.STAT_PK['stat']['txnb'] = self.txnb return ujson.dumps(self.STAT_PK) def _make_node_packet(self, rx_data, rx_time, tmst, sf, bw, rssi, snr): self.RX_PK['rxpk'][0]['time'] = '%d-%02d-%02dT%02d:%02d:%02d.%dZ' % ( rx_time[0], rx_time[1], rx_time[2], rx_time[3], rx_time[4], rx_time[5], rx_time[6]) self.RX_PK['rxpk'][0]['tmst'] = tmst self.RX_PK['rxpk'][0]['freq'] = self._freq_to_float(self.frequency) self.RX_PK['rxpk'][0]['datr'] = self._sf_bw_to_dr(sf, bw) self.RX_PK['rxpk'][0]['rssi'] = rssi self.RX_PK['rxpk'][0]['lsnr'] = float(snr) self.RX_PK['rxpk'][0]['data'] = ubinascii.b2a_base64(rx_data)[:-1] self.RX_PK['rxpk'][0]['size'] = len(rx_data) return ujson.dumps(self.RX_PK) def _push_data(self, data): token = uos.urandom(2) packet = bytes([self.PROTOCOL_VERSION]) + token + bytes( [self.PUSH_DATA]) + ubinascii.unhexlify(self.gateway_id) + data with self.udp_lock: try: self.sock.sendto(packet, self.server_ip) except BaseException as ex: self.log('Failed to push uplink packet to server: {}', ex) def _pull_data(self): token = uos.urandom(2) packet = bytes([self.PROTOCOL_VERSION]) + token + bytes( [self.PULL_DATA]) + ubinascii.unhexlify(self.gateway_id) with self.udp_lock: try: self.sock.sendto(packet, self.server_ip) except BaseException as ex: self.log('Failed to pull downlink packets from server: {}', ex) def _ack_pull_rsp(self, token, error): self.TX_ACK_PK['txpk_ack']['error'] = error resp = ujson.dumps(self.TX_ACK_PK) packet = bytes([self.PROTOCOL_VERSION]) + token + bytes( [self.PULL_ACK]) + ubinascii.unhexlify(self.gateway_id) + resp with self.udp_lock: try: self.sock.sendto(packet, self.server_ip) except BaseException as ex: self.log('PULL RSP ACK exception: {}', ex) def _send_down_link(self, data, tmst, datarate, frequency): """ Transmits a downlink message over LoRa. """ self.lora.init(mode=LoRa.LORA, frequency=frequency, bandwidth=self._dr_to_bw(datarate), sf=self._dr_to_sf(datarate), preamble=8, coding_rate=LoRa.CODING_4_5, tx_iq=True) while utime.ticks_us() < tmst: pass self.lora_sock.send(data) self.log( 'Sent downlink packet scheduled for {:.3f}, at {:.1f} Mhz using {}: {}', tmst / 1000000, self._freq_to_float(frequency), datarate, data) def _udp_thread(self): """ UDP thread, reads data from the server and handles it. """ while not self.udp_stop: try: data, src = self.sock.recvfrom(1024) _token = data[1:3] _type = data[3] if _type == self.PUSH_ACK: self.log('Push ack') elif _type == self.PULL_ACK: self.log('Pull ack') elif _type == self.PULL_RESP: self.dwnb += 1 ack_error = self.TX_ERR_NONE tx_pk = ujson.loads(data[4:]) tmst = tx_pk['txpk']['tmst'] t_us = tmst - utime.ticks_us() - 12500 if t_us < 0: t_us += 0xFFFFFFFF if t_us < 20000000: self.uplink_alarm = machine.Timer.Alarm( handler=lambda x: self._send_down_link( ubinascii.a2b_base64(tx_pk['txpk']['data']), tx_pk['txpk']['tmst'] - 50, tx_pk['txpk'][ 'datr'], int(tx_pk['txpk']['freq'] * 1000000)), us=t_us) else: ack_error = self.TX_ERR_TOO_LATE self.log('Downlink timestamp error!, t_us: {}', t_us) self._ack_pull_rsp(_token, ack_error) self.log('Pull rsp') else: self.log('Unknown message type from server: {}', _type) except usocket.timeout: pass except OSError as ex: if ex.errno != errno.EAGAIN: self.log('UDP recv OSError Exception: {}', ex) except BaseException as ex: self.log('UDP recv Exception: {}', ex) # Wait before trying to receive again utime.sleep_ms(self.UDP_THREAD_CYCLE_MS) self.sock.close() self.udp_stop = False self.log('UDP thread stopped') def log(self, message, *args): """ Prints a log message to the stdout. """ print('[{:>10.3f}] {}'.format(utime.ticks_ms() / 1000, str(message).format(*args)))
class WifiManager: def __init__(self, jsonfile): # Load configuration from config JSON file. # wificonfig.json contans the network settings # STATIC_IP is 'None' or empty string -> use dynamic IP self._config = self.readjson(jsonfile) # create network in STAtion mode # pycom: device always starts up in AP-mode self._wlan = WLAN(mode=WLAN.STA) if USE_DEBUG: print('WifiManager::WLAN mode:', self._wlan.mode()) # pycom: 1=STA, 2=AP) def readjson(self, jsonfile): """readjson(file) - returns the contents of file in JSON-format""" with open(jsonfile, 'r') as infile: config = json.load(infile) if USE_DEBUG: print('WifiManager::JSON settings: {}'.format(config)) return config # pycom connect def connect(self): """connect() - connects device according to network parameters in JSON-file.""" self._wlan = WLAN() # get current object, without changing the mode # skip connecting, when a soft-reset is performed if machine.reset_cause() != machine.SOFT_RESET: self._wlan.init(mode=WLAN.STA) # configuration below MUST match your home router settings!! # IP, Subnet, Gateway, DNS if self._config['STATIC_IP'] is None: if USE_DEBUG: print('WifiManager::Static IP configuration for SSID: ', self._config['SSID']) self._wlan.ifconfig(config=(self._config['STATIC_IP'], self._config['MASKER'], self._config['GATEWAY_IP'], self._config['DNS'])) else: if USE_DEBUG: print('WifiManager::Dynamic IP configuration for SSID: ', self._config['SSID']) pass # connect to Wifi if USE_DEBUG: print('WifiManager::isconnected:', self._wlan.isconnected()) if not self._wlan.isconnected(): if USE_DEBUG: print( "WifiManager::start '{0}' to connect to '{1}' with IP '{2}'" .format(self._config['IDENTITY'], self._config['SSID'], self._config['STATIC_IP'])) # change the line below to match your network ssid, security and password self._wlan.connect(self._config['SSID'], auth=(WLAN.WPA2, self._config['PASSWRD']), timeout=5000) while not self._wlan.isconnected(): machine.idle() # save power while waiting # connected, return network config return self._wlan.ifconfig() # wrapper for disconnecting network def disconnect(self): """disconnect() - de-activate network interface, but leaves Wifi radio on""" self._wlan.disconnect( ) # pycom - disconnect from Wifi, but leave Wif radio on. if USE_DEBUG: print('WifiManager::Wifi disconnected') # wrapper for disabling Wifi radio def deinit(self): """deinit() - disable Wifi radio""" self._wlan.deint() # pycom if USE_DEBUG: print('WifiManager::Wifi radio off') # wrapper for network scan def scan(self): """scan() - Performs a network scan and returns a list of named tuples with (ssid, bssid, sec, channel, rssi) """ return self._wlan.scan() # wrapper for wlan.isconnected() @property def isconnected(self): """isconnected() - returns if connected to Wifi (True) or not (False)""" return self._wlan.isconnected() def print_config(self): """print_config() - print config data on screen.""" for key in self._config.keys(): print('[{0}] = {1}'.format(key, self._config[key])) def change_access(self, user=None, passwrd=None): """change_access - change password for telnet and ftp access""" if (user is None) or (passwrd is None): print('WifiManager:: username and password must be specified') return server = Server() # from network # disable the server server.deinit() # enable the server again with new credentials # for example: remote access, ftp and telnet, not USB server.init(login=(user, passwrd), timeout=600) if USE_DEBUG: print('WifiManager::password {} is changed...'.format(user)) @property def __config(self): """returns config tuple""" return self._config @property def mac(self): """returns MAC-address of device""" mac = hexlify(self._wlan.mac(), ':').decode() # pycom # return (mac) # lower case return mac.upper()
print(err) LOGFILE = ("{}-{:04d}{:02d}{:02d}.{}".format(LOGNAME, t[0], t[1], t[2], LOGEXT)) f = open(LOGFILE, 'a') f.write('%s\n' %(err)) f.close() #print(".", end = "") sleep_ms(500) if retry==MQTTRETRY: LED2.value(0) err = ("{:04d}{:02d}{:02d}-{:02d}{:02d}{:02d} failed MQTT connect, will reboot".format(t[0], t[1], t[2], t[4], t[5], t[6])) print(err) f = open(LOGFILE, 'a') f.write('%s\n' %(err)) f.close() print('disconnection station...', end='') station.disconnect() sleep_ms(10) print('station connected:', station.isconnected()) print('going to deepsleep') deepsleep(100) LED2.value(0) ### status MQTT msg ### ''' LED2.value(1) t=rtc.datetime() sta = ("{:04d}{:02d}{:02d}-{:02d}{:02d}{:02d} connected to MQTT socket".format(t[0], t[1], t[2], t[4], t[5], t[6])) print(sta) client.publish(TOPICSTA, sta, qos=QOSSTA) LED2.value(0) '''
class WiFi: RECONNECT_TIMEOUT = 10 def __init__(self, config, verbose=0): self.verbose = verbose self.config = config self.__connecting = False self.connect_start = None self.station = WLAN(STA_IF) def __repr__(self): return '<WiFi: {}, {} at {:x}>'.format(self.station.config('essid'), self.station.ifconfig(), id(self)) def device_id(self): import ubinascii return ubinascii.hexlify(self.station.config('mac'), ':').decode().upper() def connecting(self): return self.__connecting def connected(self): return self.station.isconnected() def connect(self): if self.__connecting: if self.station.isconnected(): self.__connecting = False self.connect_start = None elif (time() - self.connect_start) > self.RECONNECT_TIMEOUT: # self.station.active(False) self.__connecting = False self.connect_start = time() if self.verbose: print('-> ' 'Connect ' 'timeout') elif self.station.isconnected() is False: if self.connect_start is None or ( time() - self.connect_start) > self.RECONNECT_TIMEOUT: if self.verbose: if self.connect_start is None: print('-> ' 'Reconnect ' 'timer started') else: print('-> ' 'Reconnect ' 'timeout') self.connect_start = time() else: return False self.station.active(True) ssid, password = self.scan() if self.verbose: print('[{}] [{}]'.format(ssid, password)) if ssid: self.station.connect(ssid, password) sleep(1) self.__connecting = True return True return False def disconnect(self): self.station.disconnect() def scan(self): ssid, password = None, None ap_scan = self.station.scan() ap_list = [] for ap in ap_scan: for ssid_mask, password in self.config['wifi']: mask = '^{}$'.format(ssid_mask) if search(mask, ap[0]): ap_list.append((ap[3], ap[0], password)) ap_list.sort(reverse=True) if self.verbose: for ap in ap_list: print(ap) if len(ap_list): ssid, password = ap_list[0][1], ap_list[0][2] return ssid, password
wifi.antenna(WLAN.INT_ANT) wifi.mode(WLAN.STA) print(wifi.mode() == WLAN.STA) wifi.connect(testconfig.wlan_ssid, auth=testconfig.wlan_auth, timeout=10000) wait_for_connection(wifi) wifi.ifconfig(config='dhcp') wait_for_connection(wifi) print('0.0.0.0' not in wifi.ifconfig()) wifi.ifconfig(0, ('192.168.178.109', '255.255.255.0', '192.168.178.1', '8.8.8.8')) wait_for_connection(wifi) print(wifi.ifconfig(0) == ('192.168.178.109', '255.255.255.0', '192.168.178.1', '8.8.8.8')) wait_for_connection(wifi) print(wifi.isconnected() == True) wifi.disconnect() print(wifi.isconnected() == False) t0 = time.ticks_ms() wifi.connect(testconfig.wlan_ssid, auth=testconfig.wlan_auth, timeout=0) print(time.ticks_ms() - t0 < 500) wifi.disconnect() print(wifi.isconnected() == False) # test init again wifi.init(WLAN.AP, ssid='www.wipy.io', auth=None, channel=5, antenna=WLAN.INT_ANT) print(len(wifi.mac()) == 6) # next ones MUST raise
class Wifi: """ A module for controlling the Wifi radio of the device. The methods in this module are permissive. For instance, if the Wifi radio is not yet activated, it will automatically be turned on when connecting to an access point. In order to save battery, the Wifi radio should be turned off through a call to `deactivate()` whenever possible. Since this module is shared by multiple threads, there is a potential risk in one thread turning off the Wifi radio when it is used by another. Thus, whenever a thread uses the Wifi radio, it should inform the wifi module first. It is also vital to inform the Wifi module when done using the Wifi radio - otherwise it may never be possile to deactivate it again. Other threads are not able to disable Wifi when it is in use. However, threads are free to enable Wifi at any time. The Wifi locking mechanism used in this module is a simple readers-writer lock using the Raynal method. Thus, the technique prefers users of the Wifi radio. """ def __init__(self, ssid: str, pw: str): self._station = WLAN(STA_IF) self._ssid = ssid self._pw = pw # Readers-writer lock for activation/deactivation self.__readers_lock = _thread.allocate_lock() self.__global_lock = _thread.allocate_lock() self.__clients = [] def activate(self): """Turn on the Wifi radio if it is off.""" if not self._station.active(): self._station.active(True) def deactivate(self, blocking=True): """ Turn off the Wifi radio. This will also disconnect from the access point, if connected. If the Wifi radio is used by other threads, this method will block until Wifi can be deactivated. Alternatively, the the client can pass `False` to the `blocking` parameter to return immediately if the radio is used by others. This is useful if deactivation of the radio is not a hard requirement, but simply a hint. """ if self.__global_lock.acquire(1 if blocking else 0): try: if self._station.isconnected(): self.__disconnect() if self._station.active(): self._station.active(False) finally: self.__global_lock.release() def connect(self) -> bool: """ Connect to the access point if not already connected. This method will also ativate the Wifi radio if it is not currently on. However, if the method fails, it will not turn off the radio again. """ self.activate() if self._station.isconnected(): return True self._station.connect(self._ssid, self._pw) utime.sleep(2) # Allow the radio some time to connect if self._station.isconnected(): return True self._station.disconnect() return False def is_connected(self): """Check if currently connected to the access point.""" return self._station.isconnected() def disconnect(self, blocking=True): """ Disconnect from the access point if currently connected. If the Wifi radio is currently used by other threads, this method will block until it is possible to disconnect. Alternatively, the the client can pass `False` to the `blocking` parameter to return immediately if the radio is used by others. This is useful if deactivation of the radio is not a hard requirement, but simply a hint. """ if self.__global_lock.acquire(1 if blocking else 0): try: self.__disconnect() finally: self.__global_lock.release() def __disconnect(self): if self.is_connected(): self._station.disconnect() def scan(self): """ Scan for nearby access points. If the Wifi radio is not on, it will automatically be enabled. """ self.activate() for _ in range(3): try: return self._station.scan() except RuntimeError: utime.sleep(1) # Give radio time to turn on def acquire(self): """ Inform the Wifi module that a thread wishes to use the Wifi radio. It is vital to match each call to this method with a similar call to `release()`. Each thread will be registered only once, regardless of how many times it calls this method. """ self.__readers_lock.acquire() try: tid = _thread.get_ident() if not tid in self.__clients: self.__clients.append(tid) self.__global_lock.acquire(0) finally: self.__readers_lock.release() def release(self): """ Inform the Wifi module that a client is done using the Wifi radio. If the calling thread has not been registered by a previous call to `acquire()`, then calling this method has no effect. Furthermore, it has no effect if a thread calls this method multiple times in succession. """ self.__readers_lock.acquire() try: tid = _thread.get_ident() if tid in self.__clients: self.__clients.remove(tid) if len(self.__clients) == 0 and self.__global_lock.locked(): self.__global_lock.release() finally: self.__readers_lock.release()
class WlanThreaded: TIMEOUT = 5000 def __init__(self, ssid:str='', key:str=''): self._ssid = ssid self._key = key self._wlan = WLAN(mode=WLAN.STA) self._running = False _thread.start_new_thread(self._connect, ()) def is_connected(self): return self._wlan.isconnected() def print_wlan(self): print(self._wlan.ifconfig()) def stop(self): self._running = False def _connect(self): global color self._running = True try: while self._running: color = 0x300000 print('Connecting to <%s>' % (self._ssid)) self._wlan.scan() timer = Timer.Chrono() timer.start() self._wlan.connect(ssid=self._ssid, auth=(WLAN.WPA2, self._key), timeout=WlanThreaded.TIMEOUT) while not self._wlan.isconnected(): if not self._running: break duration = timer.read_ms() if duration > WlanThreaded.TIMEOUT: break sleep(0.1) if self._running and self._wlan.isconnected(): self.print_wlan() self._start_server() except KeyboardInterrupt: pass self._running = False color = 0xff0000 print('Disconnecting from <%s>' % (self._ssid)) while True: try: self._wlan.disconnect() break except KeyboardInterrupt: pass color = 0x000000 print('Shutting down') def _start_server(self): global color color = 0x300030 print('Starting server') pyServer = PyServer(10000) pyServer.listen(self) color = 0x300030 print('Stopped server')
class Wifi(ConfigOp): def __init__(self, hostname, pin=2): self.__wlan = None self.__timeout = DEFAULT_TMOUT self.__led = Relay(pin) self.is_ok = False self.gw = None self.ip = None self.dns = None ConfigOp.__init__(self, 'wifi', CONFIG_NAME) self.add_command(self.__get_info, GET) self.add_command(self.__reconnect, SET, 'reconnect') self.__hostname = hostname def is_connected(self): return self.__wlan != None and self.__wlan.isconnected() async def __get_info(self, _): v = self.get_info() await sleep(0) return result(200, None, v) async def __reconnect(self, _): delayed_task(5000, self.async_connect, (True), True) return result(200, None, RECONNECT_WIFI) async def __reload_config(self): # NOSONAR return await self.__reconnect(None) def get_info(self): return { "mac": MAC, "connected": self.is_connected(), "connection tested": self.is_ok, "hostname": self.__hostname, "ip": self.ip, "gw": self.gw, "dns": self.dns } def check_wifi_config(self): self.load() return not (self.__config is None or is_str_empty(self.__config[SSID]) or is_str_empty(self.__config[PASSWORD])) def disconnect(self): if self.__wlan is not None and self.__wlan.isconnected(): self.__wlan.disconnect() self.__wlan.active(False) async def async_connect(self, force_rec=False): if self.__wlan is not None and self.__wlan.isconnected(): if force_rec: self.disconnect() return await self.__async_connect() return True return await self.__async_connect() async def __async_connect(self): self.__connect_init() return await self.__async_connect_finish() async def __async_connect_finish(self): start_time = time() # Check time while not self.__wlan.isconnected(): await sleep_ms(DEFAULT_300) self.__led.on() await sleep_ms(DEFAULT_300) self.__led.off() if time() - start_time > self.__timeout: log.error("Wifi connection timeout: %d", self.__timeout) break return self.__set_properties() def __connect_init(self): self.check_wifi_config() if self.__config is None: log.error("Wifi config is None") return log.info("Connect to wifi: %s", self.__config[SSID]) self.__wlan = WLAN(STA_IF) # 创建 station 接口 if self.__wlan.isconnected(): self.__wlan.disconnect() self.__wlan.active(True) # Activate the interface self.__wlan.scan() # Scan self.__led.off() self.__wlan.config(dhcp_hostname=self.__hostname) self.__wlan.connect(self.__config[SSID], self.__config[PASSWORD]) # 连接到指定ESSID网络 if TIMEOUT in self.__config: self.__timeout = self.__config[TIMEOUT] def __set_properties(self): if self.__wlan.isconnected(): log.info('network information: %r', self.__wlan.ifconfig()) (self.ip, _, self.gw, self.dns) = self.__wlan.ifconfig() self.is_ok = True self.__led.on() else: self.ip = None self.gw = None self.dns = None self.is_ok = False self.__wlan = None self.__led.off() return self.is_ok def check_connection(self): if hw.WIFI_CHECK_TYPE == 0: return True if not self.is_connected(): return False if hw.WIFI_CHECK_TYPE == 1: return True dest = self.gw if hw.WIFI_CHECK_TYPE == 3: if WIFI_CHECK_HOST in self.__config != None: dest = self.__config[WIFI_CHECK_HOST] else: dest = hw.WIFI_CHECK_HOST return ping_check(dest) async def monitor(self): log.debug("Setup wifi monitor") while hw.WIFI: try: await sleep(hw.WIFI_CHECK_INTVAL) if not self.check_connection(): log.info("Wifi is not ready, reconnecting...") await self.async_connect(True) except: #NOSONAR # pylint: disable=W0702 pass
def check_open_network(ssid, wlan: WLAN) -> bool: result = connect_to_ap(wlan, ssid) and ping(SERVICE_ADDRESS) if wlan.isconnected(): wlan.disconnect() return result
class MicroWifi: # ============================================================================ # ===( Constants )============================================================ # ============================================================================ _ETH_AP = 1 _ETH_STA = 0 _IP_NONE = '0.0.0.0' _DEFAULT_AUTH_TYPE = WLAN.WPA2 _AP_MASK = '255.255.255.0' _DEFAULT_TIMEOUT_SEC = 10 # ============================================================================ # ===( Utils )=============================================================== # ============================================================================ @staticmethod def _mac2Str(binMac): return hexlify(binMac, ':').decode().upper() # ---------------------------------------------------------------------------- def _setAPInfos(self, ssid=None, key=None, ip=None, mask=None, gateway=None, dns=None): self._apInfos = { 'ssid': ssid, 'key': key, 'ip': ip, 'mask': mask, 'gateway': gateway, 'dns': dns } # ---------------------------------------------------------------------------- def _setConnectionInfos(self, bssid=None, ssid=None, key=None, ip=None, mask=None, gateway=None, dns=None): self._connInfos = { 'macBssid': bssid, 'ssid': ssid, 'key': key, 'ip': ip, 'mask': mask, 'gateway': gateway, 'dns': dns } # ---------------------------------------------------------------------------- def _openConf(self): try: with open(self._filePath, 'r') as jsonFile: self._confObj = load(jsonFile) except: self._confObj = {} if self._confObj.get('STA', None) is None: self._confObj['STA'] = {} # ---------------------------------------------------------------------------- def _writeConf(self): try: jsonStr = dumps(self._confObj) try: mkdir(self._confPath) except: pass jsonFile = open(self._filePath, 'wb') jsonFile.write(jsonStr) jsonFile.close() return True except: return False # ============================================================================ # ===( Constructor )========================================================== # ============================================================================ def __init__(self, confName="wifi", confPath="/flash/conf", useExtAntenna=False): self._confPath = confPath self._filePath = '%s/%s.json' % (confPath, confName) self._wlan = WLAN() self._antenna = WLAN.EXT_ANT if useExtAntenna else WLAN.INT_ANT self._openConf() self._setAPInfos() self._setConnectionInfos() self._wlan.init(antenna=self._antenna) self.DisableRadio() # ============================================================================ # ===( Functions )============================================================ # ============================================================================ def DisableRadio(self): self.CloseAccessPoint() self.CloseConnectionToAP() self._wlan.deinit() # ---------------------------------------------------------------------------- def GetMACAddr(self): return self._mac2Str(self._wlan.mac()) # ---------------------------------------------------------------------------- def GetAPInfos(self): if not self.IsAccessPointOpened(): self._setAPInfos() return self._apInfos # ---------------------------------------------------------------------------- def GetConnectionInfos(self): if not self.IsConnectedToAP(): self._setConnectionInfos() return self._connInfos # ---------------------------------------------------------------------------- def ScanAP(self): try: if self._wlan.mode() == WLAN.STA: self._wlan.init(antenna=self._antenna) return self._wlan.scan() except: return () # ---------------------------------------------------------------------------- def OpenAccessPoint(self, ssid, key=None, ip='192.168.0.254', autoSave=True): if ssid and ip: try: self._wlan.ifconfig(id=self._ETH_AP, config=(ip, self._AP_MASK, ip, ip)) auth = (self._DEFAULT_AUTH_TYPE, key) if key else None self._wlan.init(mode=WLAN.STA_AP, ssid=ssid, auth=auth, antenna=self._antenna) print("WIFI ACCESS POINT OPENED :") print(" - MAC address : %s" % self.GetMACAddr()) print(" - Network SSID : %s" % ssid) print(" - IP address : %s" % ip) print(" - Mask : %s" % self._AP_MASK) print(" - Gateway IP : %s" % ip) print(" - DNS server : %s" % ip) if autoSave: self._confObj['AP'] = {'ssid': ssid, 'key': key, 'ip': ip} self._writeConf() self._setAPInfos(ssid, key, ip, self._AP_MASK, ip, ip) return True except: self.CloseAccessPoint() return False # ---------------------------------------------------------------------------- def OpenAccessPointFromConf(self): try: ssid = self._confObj['AP']['ssid'] key = self._confObj['AP']['key'] ip = self._confObj['AP']['ip'] return self.OpenAccessPoint(ssid, key, ip, False) except: return False # ---------------------------------------------------------------------------- def RemoveAccessPointFromConf(self): try: del self._confObj['AP'] return self._writeConf() except: return False # ---------------------------------------------------------------------------- def CloseAccessPoint(self): try: ip = self._IP_NONE self._wlan.mode(WLAN.STA) self._wlan.ifconfig(id=self._ETH_AP, config=(ip, ip, ip, ip)) return True except: return False # ---------------------------------------------------------------------------- def IsAccessPointOpened(self): return self._wlan.ifconfig(self._ETH_AP)[0] != self._IP_NONE # ---------------------------------------------------------------------------- def ConnectToAP(self, ssid, key=None, macBssid=None, timeoutSec=None, autoSave=True): if ssid: if not key: key = '' if not timeoutSec: timeoutSec = self._DEFAULT_TIMEOUT_SEC timeout = timeoutSec * 1000 if self._wlan.mode() == WLAN.STA: self._wlan.init(antenna=self._antenna) print("TRYING TO CONNECT WIFI TO AP %s..." % ssid) for ap in self.ScanAP(): if ap.ssid == ssid and \ ( not macBssid or self._mac2Str(ap.bssid) == macBssid ) : self._wlan.connect(ssid=ap.ssid, bssid=ap.bssid, auth=(self._DEFAULT_AUTH_TYPE, key), timeout=timeout) t = ticks_ms() while ticks_diff(t, ticks_ms()) < timeout: sleep(0.100) if self.IsConnectedToAP(): bssid = self._mac2Str(ap.bssid) staCfg = self._wlan.ifconfig(id=self._ETH_STA) ip = staCfg[0] mask = staCfg[1] gateway = staCfg[2] dns = staCfg[3] print("WIFI CONNECTED TO AP :") print(" - MAC address : %s" % self.GetMACAddr()) print(" - Network BSSID : %s" % bssid) print(" - Network SSID : %s" % ssid) print(" - IP address : %s" % ip) print(" - Mask : %s" % mask) print(" - Gateway IP : %s" % gateway) print(" - DNS server : %s" % dns) if autoSave: sta = { 'ssid': ssid, 'key': key, } self._confObj['STA'][bssid] = sta self._writeConf() self._setConnectionInfos(bssid, ssid, key, ip, mask, gateway, dns) return True self.CloseConnectionToAP() break print("FAILED TO CONNECT WIFI TO AP %s" % ssid) return False # ---------------------------------------------------------------------------- def ConnectToAPFromConf(self, bssidMustBeSame=False, timeoutSec=None): if self._wlan.mode() == WLAN.STA: self._wlan.init(antenna=self._antenna) for ap in self.ScanAP(): for bssid in self._confObj['STA']: macBssid = self._mac2Str(ap.bssid) if bssidMustBeSame else None if self._confObj['STA'][bssid]['ssid'] == ap.ssid and \ ( not macBssid or bssid == macBssid ) : if self.ConnectToAP(ap.ssid, self._confObj['STA'][bssid]['key'], macBssid, timeoutSec, False): return True break return False # ---------------------------------------------------------------------------- def RemoveConnectionToAPFromConf(self, ssid, macBssid=None): try: changed = False for bssid in list(self._confObj['STA']): if self._confObj['STA'][bssid]['ssid'] == ssid and \ ( not macBssid or bssid == macBssid ) : del self._confObj['STA'][bssid] changed = True if changed: return self._writeConf() except: pass return False # ---------------------------------------------------------------------------- def CloseConnectionToAP(self): try: self._wlan.disconnect() self._wlan.ifconfig(id=self._ETH_STA, config='dhcp') return True except: return False # ---------------------------------------------------------------------------- def IsConnectedToAP(self): return self._wlan.ifconfig(self._ETH_STA)[0] != self._IP_NONE # ---------------------------------------------------------------------------- def ResolveIPFromHostname(self, hostname): originalMode = self._wlan.mode() if originalMode == WLAN.STA_AP: self._wlan.mode(WLAN.STA) try: ipResolved = getaddrinfo(hostname, 0)[0][-1][0] except: ipResolved = None if originalMode == WLAN.STA_AP: self._wlan.mode(WLAN.STA_AP) return ipResolved if ipResolved != self._IP_NONE else None # ---------------------------------------------------------------------------- def InternetAccessIsPresent(self): return (self.ResolveIPFromHostname('iana.org') is not None) # ---------------------------------------------------------------------------- def WaitForInternetAccess(self, timeoutSec=None): if not timeoutSec: timeoutSec = self._DEFAULT_TIMEOUT_SEC timeout = timeoutSec * 1000 t = ticks_ms() while ticks_diff(t, ticks_ms()) < timeout: sleep(0.100) if self.InternetAccessIsPresent(): return True return False
def disconnectWLAN(self): # disconnedt wlan because it spams debug messages that disturb the monitor protocol from network import WLAN wlan = WLAN(mode=WLAN.STA) wlan.disconnect()
class WifiManager: def __init__(self, known_nets): self._known_nets = known_nets # create network in STAtion mode # pycom: device always starts up in AP-mode #self._wl = WLAN() #self._wl.mode(WLAN.STA) '''def print_debug(self, message): """print_debug() - for debugging """ if USE_DEBUG: print(msg) ''' # 2019-1203 new, due to Exception error in legacy WifiManager # pre-condition: self._known_nets is not None # post-condition: self._wl is created # returns: IP # URL: https://docs.pycom.io/tutorials/all/wlan/ def connect(self): """connect() - connects device according to network parameters in JSON-file.""" if machine.reset_cause() != machine.SOFT_RESET: # from network import WLAN self._wl = WLAN() self._wl.mode(WLAN.STA) original_ssid = self._wl.ssid() original_auth = self._wl.auth() print_debug("Wifimanager - scanning for known wifi nets...") available_nets = self._wl.scan() nets = frozenset([e.ssid for e in available_nets]) known_nets_names = frozenset([key for key in self._known_nets]) net_to_use = list(nets & known_nets_names) print_debug("Wifimanager - SSID to use...{}".format(net_to_use)) try: net_to_use = net_to_use[0] print_debug("Wifimanager - net to use...{}".format(net_to_use)) net_properties = self._known_nets[net_to_use] pwd = net_properties['pwd'] sec = [e.sec for e in available_nets if e.ssid == net_to_use][0] print_debug( "Wifimanager - net_properties...{}".format(net_properties)) if 'wlan_config' in net_properties: print_debug("Wifimanager - wlan_config...{}".format( net_properties['wlan_config'])) self._wl.ifconfig(config=net_properties['wlan_config']) self._wl.connect(net_to_use, (sec, pwd), timeout=10000) ip = self.wait_for_networking(1) self._ssid = net_to_use print_debug("Connected to " + net_to_use + " with IP address:" + ip) except Exception as e: print( "Failed to connect to any known network, going into AP mode" ) self._wl.init(mode=WLAN.AP, ssid=original_ssid, auth=original_auth, channel=6, antenna=WLAN.INT_ANT) self._ssid = None else: print_debug("Already connected to " + net_to_use + " with IP address:" + self._wl.ifconfig()[0]) return self._wl.ifconfig()[0] def wait_for_networking(self, dt=1): """ wait unitil network is connected and returns IP""" station = self._wl # network.WLAN(mode=network.WLAN.STA) while not station.isconnected(): time.sleep(dt) machine.idle() ip = station.ifconfig()[0] return ip # wrapper for disconnecting network def disconnect(self): """disconnect() - de-activate network interface, but leaves Wifi radio on""" self._wl.disconnect( ) # pycom - disconnect from Wifi, but leave Wif radio on. print_debug('WifiManager::Wifi disconnected') # wrapper for disabling Wifi radio def deinit(self): """deinit() - disable Wifi radio""" self._wl.deint() # pycom print_debug('WifiManager::Wifi radio off') # wrapper for network scan def scan(self): """scan() - Performs a network scan and returns a list of named tuples with (ssid, bssid, sec, channel, rssi) """ return self._wl.scan() def change_access(self, user=None, passwrd=None): """change_access - change password for telnet and ftp access""" if (user is None) or (passwrd is None): print('WifiManager:: username and password must be specified') return server = Server() # from network # disable the server server.deinit() # enable the server again with new credentials # for ftp and telnet, not USB server.init(login=(user, passwrd), timeout=600) print_debug('WifiManager::password {} is changed...'.format(user)) # wrappers for wlan settings. @property def ssid(self): """ ssid() - returns SSID of connected Wifi network""" return self._ssid @property def isconnected(self): """isconnected() - returns if connected to Wifi (True) or not (False)""" return self._wl.isconnected() @property def ip(self): """ip() - returns IP of device on connected Wifi network""" return self._wl.ifconfig()[0] @property def mac(self): """returns MAC-address of device""" mac = hexlify(self._wl.mac(), ':').decode() # pycom # return (mac) # lower case return mac.upper() # ================================================ # legacy methods # ================================================ import json def __readjson(self, jsonfile): """readjson(file) - returns the contents of file in JSON-format""" with open(jsonfile, 'r') as infile: config = json.load(infile) if USE_DEBUG: print('WifiManager::JSON settings: {}'.format(config)) return config
wifi.connect(testconfig.wlan_ssid, auth=testconfig.wlan_auth, timeout=10000) wait_for_connection(wifi) wifi.ifconfig(config='dhcp') wait_for_connection(wifi) print('0.0.0.0' not in wifi.ifconfig()) wifi.ifconfig(0, ('192.168.178.109', '255.255.255.0', '192.168.178.1', '8.8.8.8')) wait_for_connection(wifi) print( wifi.ifconfig(0) == ('192.168.178.109', '255.255.255.0', '192.168.178.1', '8.8.8.8')) wait_for_connection(wifi) print(wifi.isconnected() == True) wifi.disconnect() print(wifi.isconnected() == False) t0 = time.ticks_ms() wifi.connect(testconfig.wlan_ssid, auth=testconfig.wlan_auth, timeout=0) print(time.ticks_ms() - t0 < 500) wifi.disconnect() print(wifi.isconnected() == False) # test init again wifi.init(WLAN.AP, ssid='www.wipy.io', auth=None, channel=5, antenna=WLAN.INT_ANT)
print("------ Sending an ACK") lora_sock.send(ack_pkg) #msg="{\"value\":"+str(some_number)+"}") pycom.rgbled(colorgreen) # green time.sleep(1) # Do any extra processing required for the package. Keep in mind it should be as fast as posible # to make sure that the other clients are not waiting too long for their messages to be ac except ValueError: print(ValueError) pycom.rgbled(0x0F0000) print( "--------------------- An exception occurred(1) ! ---------------------" ) machine.reset() except Exception as e: pycom.rgbled(0x0F0000) print(e) print( "--------------------- An exception occurred(2) ! ---------------------" ) # machine.reset() finally: # If an exception is thrown ... client.disconnect() # ... disconnect the client and clean up. client = None wlan.disconnect() wlan = None pycom.rgbled(0x000022) # Status blue: stopped print("Disconnected from Bluemix")
class WifiManager: def __init__(self, jsonfile): # Load Wifi configuration from JSON file. self._config = self.readjson(jsonfile) # create network in STAtion mode self._wlan = WLAN(STA_IF) def connect(self): """connect() - connects device according to network parameters in config-file.""" # check if network is connected. If yes: return, finished # 2019-0801 changed: if self._wlan.isconnected(): if self.isconnected: if USE_DEBUG: print('WLAN already connected') return self._wlan.ifconfig() # activate Wifi interface if self._wlan.active() is False: self._wlan.active(True) # scan available networks for the required one nets = self._wlan.scan() for net in nets: ssid = net[0] if ssid == bytearray(self._config['SSID']): # must use bytearray! if USE_DEBUG: print("Startup WiFi ..." + self._config['SSID']) # specify if static or dynamic IP is requested # STATIC IP: an IP is given # DYNAMIC IP: None if self._config['STATIC_IP'] is not '': if USE_DEBUG: print('WifiManager::Static IP configuration') # configure network for static IP self._wlan.ifconfig( (self._config['STATIC_IP'], self._config['MASKER'], self._config['GATEWAY_IP'], self._config['DNS'])) # connect to SSID... either for STATIC or DYNAMIC IP self._wlan.connect(self._config['SSID'], self._config['PASSWRD']) while not self.isconnected: idle() # save power while waiting time.sleep_ms(100) # give it some time if USE_DEBUG: print("Network '{}' connection succeeded!".format(ssid)) break # check connection, if not succesfull: raise exception if not self._wlan.active(): raise exception('Network {0} not found.'.format(ssid)) # returns network configuration... # although 'myPy.local' should work on MacOS X (Bonjour) return self._wlan.ifconfig() def readjson(self, jsonfile): """readjson(file) - returns the contents of a JSON file""" with open(jsonfile, 'r') as infile: config = json.load(infile) return config def print_config(self): """print_config() - print config data on screen.""" for key in self._config.keys(): print('[{0}] = {1}'.format(key, self._config[key])) # wrapper for network scan def scan(self): """scan() - Performs a network scan and returns a list of named tuples with (ssid, bssid, sec, channel, rssi) """ if self.isconnected is False: return False nets = self._wlan.scan() nets_list = {} for net in nets: nets_list['SSID'] = str(net[0], 'utf8') # nets_list['bssid'] = str(net[1]) nets_list['CHANNEL'] = str(net[2]) nets_list['RSSI'] = str(net[3]) + ' dBm' nets_list['SECURITY'] = self._get_secure(net[4]) print(nets_list) def _get_secure(self, num): security = [ 'Open Wifi', # 0 'WEP', # 1 'WPA-PSK', # 2 'WPA2-PSK', # 3 'WPA/WP2-PSK' # 4 ] if num < len(security): return security[num] else: return str(num) def disconnect(self): """disconnect() - disconnect the Wifi connection""" self._wlan.disconnect() time.sleep_ms(50) # give it time, trial-and-error value return self.isconnected @property def isconnected(self): """isconnected() - returns True when device is connected to wifi, else False """ return self._wlan.isconnected() @property def mac(self): """returns MAC-address of device""" mac = hexlify(WLAN().config('mac'), ':').decode() return mac.upper() # MAC-address in upper case
class NanoGateway: """ Nano gateway class, set up by default for use with TTN, but can be configured for any other network supporting the Semtech Packet Forwarder. Only required configuration is wifi_ssid and wifi_password which are used for connecting to the Internet. """ def __init__(self, id, frequency, datarate, ssid, password, server, port, ntp_server='pool.ntp.org', ntp_period=3600): self.id = id self.server = server self.port = port self.frequency = frequency self.datarate = datarate self.ssid = ssid self.password = password self.ntp_server = ntp_server self.ntp_period = ntp_period self.server_ip = None self.rxnb = 0 self.rxok = 0 self.rxfw = 0 self.dwnb = 0 self.txnb = 0 self.sf = self._dr_to_sf(self.datarate) self.bw = self._dr_to_bw(self.datarate) self.stat_alarm = None self.pull_alarm = None self.uplink_alarm = None self.wlan = None self.sock = None self.udp_stop = False self.udp_lock = _thread.allocate_lock() self.lora = None self.lora_sock = None self.rtc = machine.RTC() def start(self): """ Starts the LoRaWAN nano gateway. """ self._log('Starting LoRaWAN nano gateway with id: {}', self.id) # setup WiFi as a station and connect self.wlan = WLAN(mode=WLAN.STA) self._connect_to_wifi() # get a time sync self._log('Syncing time with {} ...', self.ntp_server) self.rtc.ntp_sync(self.ntp_server, update_period=self.ntp_period) while not self.rtc.synced(): utime.sleep_ms(50) self._log("RTC NTP sync complete") # get the server IP and create an UDP socket self.server_ip = usocket.getaddrinfo(self.server, self.port)[0][-1] self._log('Opening UDP socket to {} ({}) port {}...', self.server, self.server_ip[0], self.server_ip[1]) self.sock = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM, usocket.IPPROTO_UDP) self.sock.setsockopt(usocket.SOL_SOCKET, usocket.SO_REUSEADDR, 1) self.sock.setblocking(False) # push the first time immediatelly self._push_data(self._make_stat_packet()) # create the alarms self.stat_alarm = Timer.Alarm( handler=lambda t: self._push_data(self._make_stat_packet()), s=60, periodic=True) self.pull_alarm = Timer.Alarm(handler=lambda u: self._pull_data(), s=25, periodic=True) # start the UDP receive thread self.udp_stop = False _thread.start_new_thread(self._udp_thread, ()) # initialize the LoRa radio in LORA mode self._log('Setting up the LoRa radio at {} Mhz using {}', self._freq_to_float(self.frequency), self.datarate) self.lora = LoRa(mode=LoRa.LORA, frequency=self.frequency, bandwidth=self.bw, sf=self.sf, preamble=8, coding_rate=LoRa.CODING_4_5, tx_iq=True) # create a raw LoRa socket self.lora_sock = usocket.socket(usocket.AF_LORA, usocket.SOCK_RAW) self.lora_sock.setblocking(False) self.lora_tx_done = False self.lora.callback(trigger=(LoRa.RX_PACKET_EVENT | LoRa.TX_PACKET_EVENT), handler=self._lora_cb) self._log('LoRaWAN nano gateway online') def stop(self): """ Stops the LoRaWAN nano gateway. """ self._log('Stopping...') # send the LoRa radio to sleep self.lora.callback(trigger=None, handler=None) self.lora.power_mode(LoRa.SLEEP) # stop the NTP sync self.rtc.ntp_sync(None) # cancel all the alarms self.stat_alarm.cancel() self.pull_alarm.cancel() # signal the UDP thread to stop self.udp_stop = True while self.udp_stop: utime.sleep_ms(50) # disable WLAN self.wlan.disconnect() self.wlan.deinit() def _connect_to_wifi(self): self.wlan.connect(self.ssid, auth=(None, self.password)) while not self.wlan.isconnected(): utime.sleep_ms(50) self._log('WiFi connected to: {}', self.ssid) def _dr_to_sf(self, dr): sf = dr[2:4] if sf[1] not in '0123456789': sf = sf[:1] return int(sf) def _dr_to_bw(self, dr): bw = dr[-5:] if bw == 'BW125': return LoRa.BW_125KHZ elif bw == 'BW250': return LoRa.BW_250KHZ else: return LoRa.BW_500KHZ def _sf_bw_to_dr(self, sf, bw): dr = 'SF' + str(sf) if bw == LoRa.BW_125KHZ: return dr + 'BW125' elif bw == LoRa.BW_250KHZ: return dr + 'BW250' else: return dr + 'BW500' def _lora_cb(self, lora): """ LoRa radio events callback handler. """ events = lora.events() if events & LoRa.RX_PACKET_EVENT: self.rxnb += 1 self.rxok += 1 rx_data = self.lora_sock.recv(256) stats = lora.stats() packet = self._make_node_packet(rx_data, self.rtc.now(), stats.rx_timestamp, stats.sfrx, self.bw, stats.rssi, stats.snr) self._push_data(packet) self._log('Received packet: {}', packet) self.rxfw += 1 if events & LoRa.TX_PACKET_EVENT: self.txnb += 1 lora.init(mode=LoRa.LORA, frequency=self.frequency, bandwidth=self.bw, sf=self.sf, preamble=8, coding_rate=LoRa.CODING_4_5, tx_iq=True) def _freq_to_float(self, frequency): """ MicroPython has some inprecision when doing large float division. To counter this, this method first does integer division until we reach the decimal breaking point. This doesn't completely elimate the issue in all cases, but it does help for a number of commonly used frequencies. """ divider = 6 while divider > 0 and frequency % 10 == 0: frequency = frequency // 10 divider -= 1 if divider > 0: frequency = frequency / (10**divider) return frequency def _make_stat_packet(self): now = self.rtc.now() STAT_PK["stat"]["time"] = "%d-%02d-%02d %02d:%02d:%02d GMT" % ( now[0], now[1], now[2], now[3], now[4], now[5]) STAT_PK["stat"]["rxnb"] = self.rxnb STAT_PK["stat"]["rxok"] = self.rxok STAT_PK["stat"]["rxfw"] = self.rxfw STAT_PK["stat"]["dwnb"] = self.dwnb STAT_PK["stat"]["txnb"] = self.txnb return ujson.dumps(STAT_PK) def _make_node_packet(self, rx_data, rx_time, tmst, sf, bw, rssi, snr): RX_PK["rxpk"][0]["time"] = "%d-%02d-%02dT%02d:%02d:%02d.%dZ" % ( rx_time[0], rx_time[1], rx_time[2], rx_time[3], rx_time[4], rx_time[5], rx_time[6]) RX_PK["rxpk"][0]["tmst"] = tmst RX_PK["rxpk"][0]["freq"] = self._freq_to_float(self.frequency) RX_PK["rxpk"][0]["datr"] = self._sf_bw_to_dr(sf, bw) RX_PK["rxpk"][0]["rssi"] = rssi RX_PK["rxpk"][0]["lsnr"] = snr RX_PK["rxpk"][0]["data"] = ubinascii.b2a_base64(rx_data)[:-1] RX_PK["rxpk"][0]["size"] = len(rx_data) return ujson.dumps(RX_PK) def _push_data(self, data): token = uos.urandom(2) packet = bytes([PROTOCOL_VERSION]) + token + bytes( [PUSH_DATA]) + ubinascii.unhexlify(self.id) + data with self.udp_lock: try: self.sock.sendto(packet, self.server_ip) except Exception as ex: self._log('Failed to push uplink packet to server: {}', ex) def _pull_data(self): token = uos.urandom(2) packet = bytes([PROTOCOL_VERSION]) + token + bytes( [PULL_DATA]) + ubinascii.unhexlify(self.id) with self.udp_lock: try: self.sock.sendto(packet, self.server_ip) except Exception as ex: self._log('Failed to pull downlink packets from server: {}', ex) def _ack_pull_rsp(self, token, error): TX_ACK_PK["txpk_ack"]["error"] = error resp = ujson.dumps(TX_ACK_PK) packet = bytes([PROTOCOL_VERSION]) + token + bytes( [PULL_ACK]) + ubinascii.unhexlify(self.id) + resp with self.udp_lock: try: self.sock.sendto(packet, self.server_ip) except Exception as ex: self._log('PULL RSP ACK exception: {}', ex) def _send_down_link(self, data, tmst, datarate, frequency): """ Transmits a downlink message over LoRa. """ self.lora.init(mode=LoRa.LORA, frequency=frequency, bandwidth=self._dr_to_bw(datarate), sf=self._dr_to_sf(datarate), preamble=8, coding_rate=LoRa.CODING_4_5, tx_iq=True) while utime.ticks_cpu() < tmst: pass self.lora_sock.send(data) self._log( 'Sent downlink packet scheduled on {:.3f}, at {:.3f} Mhz using {}: {}', tmst / 1000000, self._freq_to_float(frequency), datarate, data) def _udp_thread(self): """ UDP thread, reads data from the server and handles it. """ while not self.udp_stop: try: data, src = self.sock.recvfrom(1024) _token = data[1:3] _type = data[3] if _type == PUSH_ACK: self._log("Push ack") elif _type == PULL_ACK: self._log("Pull ack") elif _type == PULL_RESP: self.dwnb += 1 ack_error = TX_ERR_NONE tx_pk = ujson.loads(data[4:]) tmst = tx_pk["txpk"]["tmst"] t_us = tmst - utime.ticks_cpu() - 15000 if t_us < 0: t_us += 0xFFFFFFFF if t_us < 20000000: self.uplink_alarm = Timer.Alarm( handler=lambda x: self._send_down_link( ubinascii.a2b_base64(tx_pk["txpk"]["data"]), tx_pk["txpk"]["tmst"] - 50, tx_pk["txpk"][ "datr"], int(tx_pk["txpk"]["freq"] * 1000) * 1000), us=t_us) else: ack_error = TX_ERR_TOO_LATE self._log('Downlink timestamp error!, t_us: {}', t_us) self._ack_pull_rsp(_token, ack_error) self._log("Pull rsp") except usocket.timeout: pass except OSError as ex: if ex.errno != errno.EAGAIN: self._log('UDP recv OSError Exception: {}', ex) except Exception as ex: self._log('UDP recv Exception: {}', ex) # wait before trying to receive again utime.sleep_ms(UDP_THREAD_CYCLE_MS) # we are to close the socket self.sock.close() self.udp_stop = False self._log('UDP thread stopped') def _log(self, message, *args): """ Outputs a log message to stdout. """ print('[{:>10.3f}] {}'.format(utime.ticks_ms() / 1000, str(message).format(*args)))
class NanoGateway: """ Nano gateway class, set up by default for use with TTN, but can be configured for any other network supporting the Semtech Packet Forwarder. Only required configuration is wifi_ssid and wifi_password which are used for connecting to the Internet. """ def __init__(self, id, frequency, datarate, ssid, password, server, port, ntp_server='pool.ntp.org', ntp_period=3600): self.id = id self.server = server self.port = port self.frequency = frequency self.datarate = datarate self.ssid = ssid self.password = password self.ntp_server = ntp_server self.ntp_period = ntp_period self.server_ip = None self.rxnb = 0 self.rxok = 0 self.rxfw = 0 self.dwnb = 0 self.txnb = 0 self.sf = self._dr_to_sf(self.datarate) self.bw = self._dr_to_bw(self.datarate) self.stat_alarm = None self.pull_alarm = None self.uplink_alarm = None self.wlan = None self.sock = None self.udp_stop = False self.udp_lock = _thread.allocate_lock() self.lora = None self.lora_sock = None self.rtc = machine.RTC() def start(self): """ Starts the LoRaWAN nano gateway. """ self._log('Starting LoRaWAN nano gateway with id: {}', self.id) # setup WiFi as a station and connect self.wlan = WLAN(mode=WLAN.STA) self._connect_to_wifi() # get a time sync self._log('Syncing time with {} ...', self.ntp_server) self.rtc.ntp_sync(self.ntp_server, update_period=self.ntp_period) while not self.rtc.synced(): utime.sleep_ms(50) self._log("RTC NTP sync complete") # get the server IP and create an UDP socket self.server_ip = usocket.getaddrinfo(self.server, self.port)[0][-1] self._log('Opening UDP socket to {} ({}) port {}...', self.server, self.server_ip[0], self.server_ip[1]) self.sock = usocket.socket(usocket.AF_INET, usocket.SOCK_DGRAM, usocket.IPPROTO_UDP) self.sock.setsockopt(usocket.SOL_SOCKET, usocket.SO_REUSEADDR, 1) self.sock.setblocking(False) # push the first time immediatelly self._push_data(self._make_stat_packet()) # create the alarms self.stat_alarm = Timer.Alarm(handler=lambda t: self._push_data(self._make_stat_packet()), s=60, periodic=True) self.pull_alarm = Timer.Alarm(handler=lambda u: self._pull_data(), s=25, periodic=True) # start the UDP receive thread self.udp_stop = False _thread.start_new_thread(self._udp_thread, ()) # initialize the LoRa radio in LORA mode self._log('Setting up the LoRa radio at {:.1f} Mhz using {}', self._freq_to_float(self.frequency), self.datarate) self.lora = LoRa( mode=LoRa.LORA, frequency=self.frequency, bandwidth=self.bw, sf=self.sf, preamble=8, coding_rate=LoRa.CODING_4_5, tx_iq=True ) # create a raw LoRa socket self.lora_sock = usocket.socket(usocket.AF_LORA, usocket.SOCK_RAW) self.lora_sock.setblocking(False) self.lora_tx_done = False self.lora.callback(trigger=(LoRa.RX_PACKET_EVENT | LoRa.TX_PACKET_EVENT), handler=self._lora_cb) self._log('LoRaWAN nano gateway online') def stop(self): """ Stops the LoRaWAN nano gateway. """ self._log('Stopping...') # send the LoRa radio to sleep self.lora.callback(trigger=None, handler=None) self.lora.power_mode(LoRa.SLEEP) # stop the NTP sync self.rtc.ntp_sync(None) # cancel all the alarms self.stat_alarm.cancel() self.pull_alarm.cancel() # signal the UDP thread to stop self.udp_stop = True while self.udp_stop: utime.sleep_ms(50) # disable WLAN self.wlan.disconnect() self.wlan.deinit() def _connect_to_wifi(self): self.wlan.connect(self.ssid, auth=(None, self.password)) while not self.wlan.isconnected(): utime.sleep_ms(50) self._log('WiFi connected to: {}', self.ssid) def _dr_to_sf(self, dr): sf = dr[2:4] if sf[1] not in '0123456789': sf = sf[:1] return int(sf) def _dr_to_bw(self, dr): bw = dr[-5:] if bw == 'BW125': return LoRa.BW_125KHZ elif bw == 'BW250': return LoRa.BW_250KHZ else: return LoRa.BW_500KHZ def _sf_bw_to_dr(self, sf, bw): dr = 'SF' + str(sf) if bw == LoRa.BW_125KHZ: return dr + 'BW125' elif bw == LoRa.BW_250KHZ: return dr + 'BW250' else: return dr + 'BW500' def _lora_cb(self, lora): """ LoRa radio events callback handler. """ events = lora.events() if events & LoRa.RX_PACKET_EVENT: self.rxnb += 1 self.rxok += 1 rx_data = self.lora_sock.recv(256) stats = lora.stats() packet = self._make_node_packet(rx_data, self.rtc.now(), stats.rx_timestamp, stats.sfrx, self.bw, stats.rssi, stats.snr) self._push_data(packet) self._log('Received packet: {}', packet) self.rxfw += 1 if events & LoRa.TX_PACKET_EVENT: self.txnb += 1 lora.init( mode=LoRa.LORA, frequency=self.frequency, bandwidth=self.bw, sf=self.sf, preamble=8, coding_rate=LoRa.CODING_4_5, tx_iq=True ) def _freq_to_float(self, frequency): """ MicroPython has some inprecision when doing large float division. To counter this, this method first does integer division until we reach the decimal breaking point. This doesn't completely elimate the issue in all cases, but it does help for a number of commonly used frequencies. """ divider = 6 while divider > 0 and frequency % 10 == 0: frequency = frequency // 10 divider -= 1 if divider > 0: frequency = frequency / (10 ** divider) return frequency def _make_stat_packet(self): now = self.rtc.now() STAT_PK["stat"]["time"] = "%d-%02d-%02d %02d:%02d:%02d GMT" % (now[0], now[1], now[2], now[3], now[4], now[5]) STAT_PK["stat"]["rxnb"] = self.rxnb STAT_PK["stat"]["rxok"] = self.rxok STAT_PK["stat"]["rxfw"] = self.rxfw STAT_PK["stat"]["dwnb"] = self.dwnb STAT_PK["stat"]["txnb"] = self.txnb return ujson.dumps(STAT_PK) def _make_node_packet(self, rx_data, rx_time, tmst, sf, bw, rssi, snr): RX_PK["rxpk"][0]["time"] = "%d-%02d-%02dT%02d:%02d:%02d.%dZ" % (rx_time[0], rx_time[1], rx_time[2], rx_time[3], rx_time[4], rx_time[5], rx_time[6]) RX_PK["rxpk"][0]["tmst"] = tmst RX_PK["rxpk"][0]["freq"] = self._freq_to_float(self.frequency) RX_PK["rxpk"][0]["datr"] = self._sf_bw_to_dr(sf, bw) RX_PK["rxpk"][0]["rssi"] = rssi RX_PK["rxpk"][0]["lsnr"] = snr RX_PK["rxpk"][0]["data"] = ubinascii.b2a_base64(rx_data)[:-1] RX_PK["rxpk"][0]["size"] = len(rx_data) return ujson.dumps(RX_PK) def _push_data(self, data): token = uos.urandom(2) packet = bytes([PROTOCOL_VERSION]) + token + bytes([PUSH_DATA]) + ubinascii.unhexlify(self.id) + data with self.udp_lock: try: self.sock.sendto(packet, self.server_ip) except Exception as ex: self._log('Failed to push uplink packet to server: {}', ex) def _pull_data(self): token = uos.urandom(2) packet = bytes([PROTOCOL_VERSION]) + token + bytes([PULL_DATA]) + ubinascii.unhexlify(self.id) with self.udp_lock: try: self.sock.sendto(packet, self.server_ip) except Exception as ex: self._log('Failed to pull downlink packets from server: {}', ex) def _ack_pull_rsp(self, token, error): TX_ACK_PK["txpk_ack"]["error"] = error resp = ujson.dumps(TX_ACK_PK) packet = bytes([PROTOCOL_VERSION]) + token + bytes([PULL_ACK]) + ubinascii.unhexlify(self.id) + resp with self.udp_lock: try: self.sock.sendto(packet, self.server_ip) except Exception as ex: self._log('PULL RSP ACK exception: {}', ex) def _send_down_link(self, data, tmst, datarate, frequency): """ Transmits a downlink message over LoRa. """ self.lora.init( mode=LoRa.LORA, frequency=frequency, bandwidth=self._dr_to_bw(datarate), sf=self._dr_to_sf(datarate), preamble=8, coding_rate=LoRa.CODING_4_5, tx_iq=True ) while utime.ticks_us() < tmst: pass self.lora_sock.send(data) self._log( 'Sent downlink packet scheduled on {:.3f}, at {:.1f} Mhz using {}: {}', tmst / 1000000, self._freq_to_float(frequency), datarate, data ) def _udp_thread(self): """ UDP thread, reads data from the server and handles it. """ while not self.udp_stop: try: data, src = self.sock.recvfrom(1024) _token = data[1:3] _type = data[3] if _type == PUSH_ACK: self._log("Push ack") elif _type == PULL_ACK: self._log("Pull ack") elif _type == PULL_RESP: self.dwnb += 1 ack_error = TX_ERR_NONE tx_pk = ujson.loads(data[4:]) tmst = tx_pk["txpk"]["tmst"] t_us = tmst - utime.ticks_us() - 12500 if t_us < 0: t_us += 0xFFFFFFFF if t_us < 20000000: self.uplink_alarm = Timer.Alarm( handler=lambda x: self._send_down_link( ubinascii.a2b_base64(tx_pk["txpk"]["data"]), tx_pk["txpk"]["tmst"] - 50, tx_pk["txpk"]["datr"], int(tx_pk["txpk"]["freq"] * 1000000) ), us=t_us ) else: ack_error = TX_ERR_TOO_LATE self._log('Downlink timestamp error!, t_us: {}', t_us) self._ack_pull_rsp(_token, ack_error) self._log("Pull rsp") except usocket.timeout: pass except OSError as ex: if ex.errno != errno.EAGAIN: self._log('UDP recv OSError Exception: {}', ex) except Exception as ex: self._log('UDP recv Exception: {}', ex) # wait before trying to receive again utime.sleep_ms(UDP_THREAD_CYCLE_MS) # we are to close the socket self.sock.close() self.udp_stop = False self._log('UDP thread stopped') def _log(self, message, *args): """ Outputs a log message to stdout. """ print('[{:>10.3f}] {}'.format( utime.ticks_ms() / 1000, str(message).format(*args) ))