def wifi(): print('wifi init') pycom.rgbled(0x009999) # blue wlan = WLAN(mode=WLAN.AP, ssid='waterLevel', auth=(WLAN.WPA2, 'ucuenca1234'), channel=7, antenna=WLAN.INT_ANT) serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: serversocket.bind(socket.getaddrinfo( host, port)[0][-1]) #ipServer 192.168.4.1 except Exception as e: print('bind failed, error code: ', str(e[0])) sys.exit() serversocket.listen(1) print('socket is now listening over port: ', port) wifiSocket = True while (wifiSocket): print('socket init') sc, addr = serversocket.accept() print('sc: ', sc, ' addr: ', addr) recibido = sc.recv(16) print('valor recibido :', recibido) print('dato[0]: ', recibido[0]) wifiSocket, msg = calibrationType(recibido) sc.send(msg) print('closing wifi and socket') sc.close() serversocket.close() wlan.deinit()
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 get_WLAN(): global wlan if wlan == None: wlan = WLAN(mode=WLAN.STA) if wlan.isconnected(): return True nets = wlan.scan() for net in nets: if net.ssid == config.WLAN_SSID: debugprint('Network found!') wlan.connect(net.ssid, auth=(net.sec, config.WLAN_WPA), timeout=5000) attempts = 0 while (not wlan.isconnected()) and (attempts < 10): debugprint('Connecting...') attempts = attempts + 1 time.sleep(1) if wlan.isconnected(): debugprint('WLAN connection succeeded') return True else: debugprint('WLAN connection failed') end_WLAN() return False debugprint('No known WLAN SSID found') wlan.deinit() wlan = None return False
def setup_new_config(logger): """ Start a WiFi AP that provides a configuration page. The device automatically reboots and applies modifications upon successful configuration. :param logger: status logger :type logger: LoggerFactory """ # Only one of this thread is allowed to run at a time if not wifi_lock.locked(): with wifi_lock: logger.info("New configuration setup started") # Config uses LED colours to indicate the state of the connection - lock is necessary to disable error pings led_lock.acquire(1) unique_id = ubinascii.hexlify(machine.unique_id()).decode() # set pycom up as access point wlan = WLAN(mode=WLAN.AP, ssid=config.get_config("device_name") + unique_id) # Connect to PmSensor using password set by the user wlan.init( mode=WLAN.AP, ssid=config.get_config("device_name") + unique_id, auth=(WLAN.WPA2, config.get_config("password")), channel=1, antenna=WLAN.INT_ANT, ) # Load HTML via entering 192,168.4.10 to browser wlan.ifconfig( id=1, config=("192.168.4.10", "255.255.255.0", "192.168.4.1", "192.168.4.1"), ) logger.info("Access point turned on as {}".format( config.get_config("device_name") + unique_id)) logger.info( "Configuration website can be accessed at 192.168.4.10") address = socket.getaddrinfo( "0.0.0.0", 80)[0][-1] # Accept stations from all addresses sct = socket.socket() # Create socket for communication sct.settimeout(int( float(config.get_config("config_timeout")) * 60)) # session times out after x seconds gc.collect( ) # frees up unused memory if there was a previous connection sct.bind(address) # Bind address to socket sct.listen(1) # Allow one station to connect to socket pycom.rgbled(0x000055) # Blue LED - waiting for connection get_new_config(sct, logger) wlan.deinit() # turn off wifi gc.collect() logger.info("rebooting...") machine.reset()
def preSetup(self): wlan = WLAN() wlan.deinit() #Wlan uitschakelen # Uitschakelen van LTE en Bluetooth zorgt voor een grote vertraging. Normaal zijn deze niet actief dus moeten ze niet uitgeschakeld worden. # lte = LTE() # lte.deinit(reset=True) #lte (4G) uitschakelen # ble = Bluetooth() # ble.deinit() #Disable Bluetooth print("preSetup completed")
def bringup_wlan(ext_ant=False): wlan = WLAN() wlan.deinit() init_kwargs = { "mode": WLAN.STA, "antenna": WLAN.INT_ANT if not ext_ant else WLAN.EXT_ANT } wlan.init(**init_kwargs) return wlan
def __cli_activation_over_wifi(self, activation_info): print('Please wait while we try to connect to {}'.format( activation_info.get('s'))) from network import WLAN wlan = WLAN(mode=WLAN.STA) attempt = 0 known_nets = [((activation_info['s'], activation_info['p']))] # noqa print_debug(3, 'WLAN connected? {}'.format(wlan.isconnected())) while not wlan.isconnected() and attempt < 10: attempt += 1 print_debug(3, "Wifi connection attempt: {}".format(attempt)) print_debug(3, 'WLAN connected? {}'.format(wlan.isconnected())) available_nets = None while available_nets is None: try: available_nets = wlan.scan() for x in available_nets: print_debug(5, x) time.sleep(3) except: pass nets = frozenset([e.ssid for e in available_nets]) known_nets_names = frozenset([e[0] for e in known_nets]) net_to_use = list(nets & known_nets_names) try: net_to_use = net_to_use[0] pwd = dict(known_nets)[net_to_use] sec = [e.sec for e in available_nets if e.ssid == net_to_use][0] # noqa print_debug( 99, "Connecting with {} and {}".format(net_to_use, pwd)) if sec == 0: wlan.connect(net_to_use, timeout=10000) else: wlan.connect(net_to_use, (sec, pwd), timeout=10000) start_time = time.time() while not wlan.isconnected(): if time.time() - start_time > timeout: raise TimeoutError( 'Timeout trying to connect via WiFi') time.sleep(0.1) except Exception as e: if str(e) == "list index out of range" and attempt == 3: print("Please review Wifi SSID and password!") wlan.deinit() return None elif attempt == 3: print("Error connecting using WIFI: %s" % e) return None
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_wlan_to_access_point(ssid="wipy_https_server", password="******", host_ip="192.168.4.1", log=lambda msg: None): log("Creating Access Point {} with password {}".format(ssid, password)) wlan = WLAN() wlan.deinit() wlan.ifconfig(config=(host_ip, '255.255.255.0', '0.0.0.0', '8.8.8.8')) wlan.init(mode=WLAN.AP, ssid=ssid, auth=(WLAN.WPA2, password), channel=5, antenna=WLAN.INT_ANT) return wlan
def software_update(logger): with wifi_lock: try: logger.info("Over the Air update started") led_lock.acquire(1) # disable all other indicator LEDs pycom.rgbled(0x555500) # Yellow LED # Get credentials from configuration ssid = config.get_config("SSID") password = config.get_config("wifi_password") server_ip = config.get_config("server") port = int(config.get_config("port")) print(ssid, password, server_ip, port) # Perform OTA update ota = WiFiOTA(ssid, password, server_ip, port) # Turn off WiFi to save power w = WLAN() w.deinit() ota.connect() ota.update() time.sleep(5) except Exception as e: logger.exception("Failed to update the device") pycom.rgbled(0x550000) time.sleep(3) finally: # Turn off update mode config.save_configuration({"update": False}) pycom.rgbled(0x000000) led_lock.release() # Reboot the device to apply patches machine.reset()
def connect_wifi(network, password, oled): wlan = WLAN() wlan.deinit() wlan.init(mode=WLAN.STA) wlan.ifconfig(config=('dhcp')) wlan.scan() # scan for available networks wlan.connect(ssid=network, auth=(WLAN.WPA2, password)) for _ in range(15): if wlan.isconnected(): print("Joining Success") # Boot Text with version oled.fill(0) oled.text("Connected to:", 0, 0) oled.text(network, 0, 15) oled.text("IP Address:", 0, 40) oled.text(wlan.ifconfig()[0], 0, 55) oled.show() utime.sleep_ms(1000) return else: # Boot Text with version oled.fill(0) oled.text("Connecting to:", 0, 0) oled.text(network, 0, 15) oled.show() utime.sleep_ms(1000) # WiFi Creds probably broken print("Can't Join Clearing Credentials") kv.set("AP_PWD", False) kv.set("AP_SSID", False) oled.fill(0) oled.text("Failed to", 0, 0) oled.text("connect to:", 0, 15) oled.text(network, 0, 30) oled.text("Rebooting", 0, 55) oled.show() utime.sleep_ms(3000) machine.reset()
def wifiTask(): global wifi_wdt_lock, wifi_stop_flag logger = Logger(name='WIFI ' + __version__, level=logging.INFO, filename=None) logger.info('** WIFI Task started **') wlan = WLAN() wlan.deinit() uid = str(binascii.hexlify(machine.unique_id())[6:]) Ssid = 'ENS-PP-' + uid[2:8] logger.info('Using Hidden AP SSID:' + Ssid) wlan.init(mode=WLAN.AP, ssid=Ssid, auth=(WLAN.WPA2, 'Li56#fd0gertQQ'), channel=8, antenna=WLAN.INT_ANT, hidden=True) server = network.Server() server.deinit() # disable the server # enable the server again with new settings server.init(login=('ensuser', 'n0ty0urbu5ine55'), timeout=120) wifi_ap_init_done = True while not wifi_stop_flag.locked(): if wifi_wdt_lock.locked(): wifi_wdt_lock.release() time.sleep(1) if fota_wifi_release_sem.locked() and wifi_ap_init_done == True: wlan.deinit() wifi_ap_init_done = False wlan = None logger.info('Deinit AP mode for FOTA') try: wlan.deinit() except: pass logger.error('** WIFI Task ended **')
def software_update(logger): """ Connects to the wlan and fetches updates from a server. After having applied the patches successfully, it reboots the device. :param logger: status logger :type logger: LoggerFactory object """ with wifi_lock: try: logger.info("Over the Air update started") led_lock.acquire(1) # disable all other indicator LEDs pycom.rgbled(0x555500) # Yellow LED # Get credentials from configuration ssid = config.get_config("SSID") password = config.get_config("wifi_password") server_ip = config.get_config("server") port = int(config.get_config("port")) logger.info("SSID: " + str(ssid)) logger.info("server_ip: " + str(server_ip)) logger.info("port: " + str(port)) version = config.get_config("code_version") # Perform OTA update ota = WiFiOTA(logger, ssid, password, server_ip, port, version) # Turn off WiFi to save power w = WLAN() w.deinit() logger.info("connecting...") ota.connect() if ota.update(): new_version = str( ota.get_current_version()) # get updated version config.save_config({"code_version": str(new_version) }) # save new version to config on SD logger.info( "Successfully updated the device from version {} to version {}" .format(version, new_version)) except Exception as e: logger.exception("Failed to update the device") pycom.rgbled(0x550000) # turn LED to RED time.sleep(3) finally: # Turn off update mode config.save_config({"update": False}) # Turn off indicator LED pycom.rgbled(0x000000) led_lock.release() # Reboot the device to apply patches logger.info("rebooting...") machine.reset()
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)))
def http_daemon(ssid="OpenScale", password="******", path_to_handler={}): host_ip = "192.168.4.1" print("Creating Access Point {} with password {}".format(ssid, password)) print("User will need to connect to the webpage at {}".format(host_ip)) wlan = WLAN() wlan.deinit() wlan.ifconfig(config=(host_ip, '255.255.255.0', '0.0.0.0', '8.8.8.8')) wlan.init(mode=WLAN.AP, ssid=ssid, auth=(WLAN.WPA2, password), channel=5, antenna=WLAN.INT_ANT) # The address of the board at port 80 address = socket.getaddrinfo('0.0.0.0', 80)[0][-1] s = socket.socket() s.bind(address) s.listen(5) print('listening on ', address) while True: connection_socket, address = s.accept() connection_socket.setblocking(False) print('New connection from', address) msg = connection_socket.recv(MAX_HTTP_MESSAGE_LENGTH).decode() msg = msg.replace("\r\n", "\n") blank_line_split = msg.split('\n\n') if len(blank_line_split) != 2: raise Exception("Malformated HTTP request.") preamble = blank_line_split[0].split("\n") request = preamble[0] request_keys = ["method", "path", "version"] request_key_value = zip(request_keys, request.split(" ")) request = {key: value for key, value in request_key_value} headers = preamble[1:] headers = { line.split(":")[0].strip(): line.split(":")[1].strip() for line in headers } for key, value in headers.items(): request[key] = value print("Received Request:\n{}".format(request)) request['body'] = blank_line_split[1] if 'Content-Length' in request: content_length = int(request['Content-Length']) if len(request['body']) < content_length: print( "Attempting to retrieve {} ({} remaining) bytes of content" .format(content_length, content_length - len(request['body']))) while len(request['body']) != content_length: new_segment = connection_socket.recv( MAX_HTTP_MESSAGE_LENGTH).decode() request['body'] += new_segment if request['path'] not in path_to_handler: print("{} not found in path_to_handler".format(request['path'])) response = not_configured_response else: endpoint_handler = path_to_handler[request['path']] print("Path found. Passing to {}".format(endpoint_handler)) response = endpoint_handler(**request) print("Sending response") connection_socket.send(response) connection_socket.close()
class Scanner: def __init__(self): # Initialize wlan self.wlan = WLAN(mode=WLAN.STA) # Internal (WLAN.INT_ANT) or external (WLAN.EXT_ANT) antenna self.wlan.antenna(WLAN.INT_ANT) self.nets = [] # Define the named tuple that contains the WiFi access point information self.net = namedtuple('net', ('ssid', 'bssid', 'sec', 'channel', 'rssi')) def get(self, testNetworksValid=False, testNetworks=[]): if testNetworksValid: self.nets = testNetworks else: # Scan available networks self.nets = self.wlan.scan(type=WLAN.SCAN_PASSIVE, scantime=120) # Switch WiFi off self.wlan.deinit() # Nets is defined None if no networks are found if self.nets is None: self.nets = [] # If more than one network, order the list by RSSI (strongest first) if len(self.nets) > 1: sorted(self.nets, key=self.__byRssi_key) # Flash file storage not used because it is slow #self.storeNets(self.convertNetList(self.nets)) #print(self.readNets()) return self.nets # Convert from list of named tuples to list of dictionaries # ssid', 'bssid', 'sec', 'channel', 'rssi' def convertNetList(self, tupleNetList): dictNetList = [] for tupleNet in tupleNetList: dictNet = { "ssid": tupleNet.ssid, "bssid": tupleNet.bssid, "sec": tupleNet.sec, "channel": tupleNet.channel, "rssi": tupleNet.rssi } dictNetList.append(dictNet) return dictNetList def writeLongList(self, name, longVars, maxLen): i = 0 while i < len(longVars) and i < maxLen: pycom.nvs_set(name + str(i) + 'l', struct.unpack(">I", longVars[i][-4:])[0]) pycom.nvs_set(name + str(i) + 'm', struct.unpack(">H", longVars[i][0:2])[0]) i = i + 1 try: pycom.nvs_erase(name + str(i) + 'l') pycom.nvs_erase(name + str(i) + 'm') except: return else: return def readLongList(self, name): longVars = [] i = 0 while True: try: lsb = pycom.nvs_get(name + str(i) + 'l') msb = pycom.nvs_get(name + str(i) + 'm') except: break else: longVars.append(struct.pack(">HI", msb, lsb)) i = i + 1 return longVars def writeStore(self): macList = [] i = 0 while i < len(self.nets): macList.append(self.nets[i].bssid) i = i + 1 self.writeLongList('net', macList, 10) return def readStore(self): macList = self.readLongList('net') self.storedNets = [] for mac in macList: self.storedNets.append( self.net(ssid='', bssid=mac, sec=0, channel=1, rssi=-100)) return self.storedNets def storeNets(self, nets): f = open('data.txt', 'w+') for net in nets: f.write(repr(net) + '\n') f.close() def readNets(self): dictNetList = [] f = open('data.txt') nets = f.readlines() for net in nets: dictNetList.append(eval(net)) f.close() return dictNetList def createMessage(self): message = bytearray() i = 0 while i < len(self.nets) and i < 3: # Add BSSID to the LoRa message message = message + self.nets[i].bssid # Add RSSI (without minus) to the LoRa message message.append(self.nets[i].rssi * -1) i = i + 1 return message def netDiff(self, nets1=[], nets2=[]): matchCnt = 0 # Define the named tuple that contains the result of the matching diffRes = namedtuple('diffRes', ('net1', 'net2', 'match', 'noMatch')) for net1 in nets1: for net2 in nets2: if net1.bssid == net2.bssid: matchCnt = matchCnt + 1 noMatchCnt = len(nets1) + len(nets2) - (2 * matchCnt) return diffRes(net1=len(nets1), net2=len(nets2), match=matchCnt, noMatch=noMatchCnt) def __byRssi_key(self, net): return net.rssi
class PybytesConnection: def __init__(self, config, message_callback): if config is not None: self.__conf = config try: self.__host = pycom.nvs_get('pybytes_server') except: self.__host = config.get('server') self.__ssl = config.get('ssl', False) self.__ssl_params = config.get('ssl_params', {}) self.__user_name = config.get('username') self.__device_id = config.get('device_id') self.__mqtt_download_topic = "d" + self.__device_id self.__mqtt_upload_topic = "u" + self.__device_id self.__pybytes_protocol = PybytesProtocol( config, message_callback, pybytes_connection=self ) self.__connection = None self.__connection_status = constants.__CONNECTION_STATUS_DISCONNECTED self.__lora_socket = None self.lora = None self.lora_lock = _thread.allocate_lock() self.__sigfox_socket = None self.lte = None self.wlan = None self.__network_type = None self.__wifi_lte_watchdog = None def lte_ping_routine(self, delay): while True: self.send_ping_message() time.sleep(delay) def print_pretty_response(self, rsp): lines = rsp.split('\r\n') for line in lines: if line: if line not in ['OK']: print(line) def __initialise_watchdog(self): if self.__conf.get('connection_watchdog', True): self.__wifi_lte_watchdog = WDT( timeout=constants.__WDT_TIMEOUT_MILLISECONDS ) print('Initialized watchdog for WiFi and LTE connection with timeout {} ms'.format(constants.__WDT_TIMEOUT_MILLISECONDS)) # noqa else: print('Watchdog for WiFi and LTE was disabled, enable with "connection_watchdog": true in pybytes_config.json') # noqa # Establish a connection through WIFI before connecting to mqtt server def connect_wifi(self, reconnect=True, check_interval=0.5, timeout=120): self.__initialise_watchdog() if self.__connection_status != constants.__CONNECTION_STATUS_DISCONNECTED: # noqa print("Error connect_wifi: Connection already exists. Disconnect First") # noqa return False try: from network import WLAN antenna = self.__conf.get('wlan_antenna', WLAN.INT_ANT) known_nets = [((self.__conf['wifi']['ssid'], self.__conf['wifi']['password']))] # noqa if antenna == WLAN.EXT_ANT: print("WARNING! Using external WiFi antenna.") '''to connect it to an existing network, the WiFi class must be configured as a station''' self.wlan = WLAN(mode=WLAN.STA, antenna=antenna) attempt = 0 print_debug(3, 'WLAN connected? {}'.format(self.wlan.isconnected())) while not self.wlan.isconnected() and attempt < 3: attempt += 1 print_debug(3, "Wifi connection attempt: {}".format(attempt)) print_debug(3, 'WLAN connected? {}'.format(self.wlan.isconnected())) available_nets = None while available_nets is None: try: available_nets = self.wlan.scan() for x in available_nets: print_debug(5, x) time.sleep(1) except: pass nets = frozenset([e.ssid for e in available_nets]) known_nets_names = frozenset([e[0]for e in known_nets]) net_to_use = list(nets & known_nets_names) try: net_to_use = net_to_use[0] pwd = dict(known_nets)[net_to_use] sec = [e.sec for e in available_nets if e.ssid == net_to_use][0] # noqa print_debug(99, "Connecting with {} and {}".format(net_to_use, pwd)) if sec == 0: self.wlan.connect(net_to_use, timeout=10000) else: self.wlan.connect(net_to_use, (sec, pwd), timeout=10000) start_time = time.time() while not self.wlan.isconnected(): if time.time() - start_time > timeout: raise TimeoutError('Timeout trying to connect via WiFi') time.sleep(0.1) except Exception as e: if str(e) == "list index out of range" and attempt == 3: print("Please review Wifi SSID and password inside config") self.wlan.deinit() return False elif attempt == 3: print("Error connecting using WIFI: %s" % e) return False self.__network_type = constants.__NETWORK_TYPE_WIFI print("WiFi connection established") try: self.__connection = MQTTClient( self.__device_id, self.__host, self.__mqtt_download_topic, self.__pybytes_protocol, user=self.__user_name, password=self.__device_id ) self.__connection.connect() self.__connection_status = constants.__CONNECTION_STATUS_CONNECTED_MQTT_WIFI # noqa self.__pybytes_protocol.start_MQTT( self, constants.__NETWORK_TYPE_WIFI ) return True except Exception as ex: if '{}'.format(ex) == '4': print('MQTT ERROR! Bad credentials when connecting to server: "{}"'.format(self.__host)) # noqa else: print("MQTT ERROR! {}".format(ex)) return False except Exception as ex: print("Exception connect_wifi: {}".format(ex)) return False # Establish a connection through LTE before connecting to mqtt server def connect_lte(self, activation_info=False, start_mqtt=True): if activation_info: lte_cfg = activation_info else: lte_cfg = self.__conf.get('lte') self.__initialise_watchdog() if lte_cfg is not None: if (os.uname()[0] not in ['FiPy', 'GPy']): print("You need a device with FiPy or GPy firmware to connect via LTE") # noqa return False try: from network import LTE time.sleep(3) if lte_cfg.get('carrier', 'standard') == 'standard': carrier = None else: carrier = lte_cfg.get('carrier') print_debug(1, 'LTE init(carrier={}, cid={})'.format(carrier, lte_cfg.get('cid', 1))) # noqa # instantiate the LTE object self.lte = LTE(carrier=carrier, cid=lte_cfg.get('cid', 1)) try: lte_type = lte_cfg.get('type') if len(lte_cfg.get('type')) > 0 else None except: lte_type = None try: lte_apn = lte_cfg.get('apn') if len(lte_cfg.get('type')) > 0 else None except: lte_apn = None try: lte_band = int(lte_cfg.get('band')) except: lte_band = None print_debug( 1, 'LTE attach(band={}, apn={}, type={})'.format( lte_band, lte_apn, lte_type ) ) self.lte.attach(band=lte_band, apn=lte_apn, type=lte_type) # noqa # attach the cellular modem to a base station while not self.lte.isattached(): time.sleep(0.25) time.sleep(1) print_debug(1, 'LTE connect()') # start a data session and obtain an IP address self.lte.connect() print_debug(1, 'LTE is_connected()') while not self.lte.isconnected(): time.sleep(0.25) print("LTE connection established") self.__network_type = constants.__NETWORK_TYPE_LTE if start_mqtt: try: self.__connection = MQTTClient( self.__device_id, self.__host, self.__mqtt_download_topic, self.__pybytes_protocol, user=self.__user_name, password=self.__device_id ) self.__connection.connect() self.__connection_status = constants.__CONNECTION_STATUS_CONNECTED_MQTT_LTE # noqa self.__pybytes_protocol.start_MQTT( self, constants.__NETWORK_TYPE_LTE ) print("Connected to MQTT {}".format(self.__host)) return True except Exception as ex: if '{}'.format(ex) == '4': print('MQTT ERROR! Bad credentials when connecting to server: "{}"'.format(self.__host)) # noqa else: print("MQTT ERROR! {}".format(ex)) return False except Exception as ex: print("Exception connect_lte: {}".format(ex)) sys.print_exception(ex) return False else: print("Error... missing configuration!") return False # LORA def connect_lora_abp(self, lora_timeout, nanogateway): print_debug(1,'Attempting to connect via LoRa') if (self.__connection_status != constants.__CONNECTION_STATUS_DISCONNECTED): # noqa print("Error connect_lora_abp: Connection already exists. Disconnect First") # noqa return False try: from network import LoRa except Exception as ex: print("This device does not support LoRa connections: %s" % ex) return False lora_class = self.__conf.get('lora', {}).get('class', 0) if self.__conf.get('lora', {}).get('region') is not None: self.lora = LoRa(mode=LoRa.LORAWAN, region=self.__conf.get('lora').get('region'), device_class=lora_class) else: self.lora = LoRa(mode=LoRa.LORAWAN, device_class=lora_class) self.lora.nvram_restore() try: dev_addr = self.__conf['lora']['abp']['dev_addr'] nwk_swkey = self.__conf['lora']['abp']['nwk_skey'] app_swkey = self.__conf['lora']['abp']['app_skey'] except Exception as ex: print("Invalid LoRaWAN ABP configuration!") print_debug(1, ex) return False timeout_ms = self.__conf.get('lora_timeout', lora_timeout) * 1000 dev_addr = struct.unpack(">l", binascii.unhexlify(dev_addr.replace(' ', '')))[0] # noqa nwk_swkey = binascii.unhexlify(nwk_swkey.replace(' ', '')) app_swkey = binascii.unhexlify(app_swkey.replace(' ', '')) try: print("Trying to join LoRa.ABP for %d seconds..." % self.__conf.get('lora_timeout', lora_timeout)) self.lora.join( activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey), timeout=timeout_ms ) # if you want, uncomment this code, but timeout must be 0 # while not self.lora.has_joined(): # print("Joining...") # time.sleep(5) self.__open_lora_socket(nanogateway) # print_debug(5, 'Stack size: {}'.format(self.__thread_stack_size)) # _thread.stack_size(self.__thread_stack_size) # _thread.start_new_thread(self.__check_lora_messages, ()) return True except Exception as e: message = str(e) if message == 'timed out': print("LoRa connection timeout: %d seconds" % self.__conf.get('lora_timeout', lora_timeout)) else: print_debug(3, 'Exception in LoRa connect: {}'.format(e)) return False def connect_lora_otaa(self, lora_timeout, nanogateway): print_debug(1,'Attempting to connect via LoRa') if (self.__connection_status != constants.__CONNECTION_STATUS_DISCONNECTED): # noqa print("Error connect_lora_otaa: Connection already exists. Disconnect First") # noqa return False try: from network import LoRa except Exception as ex: print("This device does not support LoRa connections: %s" % ex) return False try: dev_eui = self.__conf['lora']['otaa']['app_device_eui'] app_eui = self.__conf['lora']['otaa']['app_eui'] app_key = self.__conf['lora']['otaa']['app_key'] except Exception as ex: print("Invalid LoRaWAN OTAA configuration!") print_debug(1, ex) return False timeout_ms = self.__conf.get('lora_timeout', lora_timeout) * 1000 lora_class = self.__conf.get('lora', {}).get('class', 0) if self.__conf.get('lora', {}).get('region') is not None: self.lora = LoRa(mode=LoRa.LORAWAN, region=self.__conf.get('lora', {}).get('region'), device_class=lora_class) else: self.lora = LoRa(mode=LoRa.LORAWAN, device_class=lora_class) self.lora.nvram_restore() dev_eui = binascii.unhexlify(dev_eui.replace(' ', '')) app_eui = binascii.unhexlify(app_eui.replace(' ', '')) app_key = binascii.unhexlify(app_key.replace(' ', '')) try: if not self.lora.has_joined(): print("Trying to join LoRa.OTAA for %d seconds..." % self.__conf.get('lora_timeout', lora_timeout)) self.lora.join( activation=LoRa.OTAA, auth=(dev_eui, app_eui, app_key), timeout=timeout_ms ) # if you want, uncomment this code, but timeout must be 0 # while not self.lora.has_joined(): # print("Joining...") # time.sleep(5) self.__open_lora_socket(nanogateway) # print_debug(5, 'Stack size: {}'.format(self.__thread_stack_size)) # _thread.stack_size(self.__thread_stack_size) # _thread.start_new_thread(self.__check_lora_messages, ()) return True except Exception as e: message = str(e) if message == 'timed out': print("LoRa connection timeout: %d seconds" % self.__conf.get('lora_timeout', lora_timeout)) else: print_debug(3, 'Exception in LoRa connect: {}'.format(e)) return False def __open_lora_socket(self, nanogateway): if (nanogateway): for i in range(3, 16): self.lora.remove_channel(i) self.lora.add_channel(0, frequency=868100000, dr_min=0, dr_max=5) self.lora.add_channel(1, frequency=868100000, dr_min=0, dr_max=5) self.lora.add_channel(2, frequency=868100000, dr_min=0, dr_max=5) print("Setting up LoRa socket...") self.__lora_socket = socket.socket(socket.AF_LORA, socket.SOCK_RAW) self.__lora_socket.setsockopt(socket.SOL_LORA, socket.SO_DR, 5) self.__connection_status = constants.__CONNECTION_STATUS_CONNECTED_LORA self.__pybytes_protocol.start_Lora(self) print("Connected using LoRa") # SIGFOX def connect_sigfox(self): if (self.__connection_status != constants.__CONNECTION_STATUS_DISCONNECTED): # noqa print("Error: Connection already exists. Disconnect First") pass try: from network import Sigfox except Exception: print("This device does not support Sigfox connections") return sigfox_config = self.__conf.get('sigfox', {}) if sigfox_config is None or sigfox_config.get('RCZ') is None: print(constants.__SIGFOX_WARNING) try: sf_rcz = int(sigfox_config.get('RCZ', 1)) - 1 if sf_rcz >= 0 and sf_rcz <= 3: Sigfox(mode=Sigfox.SIGFOX, rcz=sf_rcz) self.__sigfox_socket = socket.socket(socket.AF_SIGFOX, socket.SOCK_RAW) # noqa self.__sigfox_socket.setblocking(True) self.__sigfox_socket.setsockopt(socket.SOL_SIGFOX, socket.SO_RX, False) # noqa self.__network_type = constants.__NETWORK_TYPE_SIGFOX self.__connection_status = constants.__CONNECTION_STATUS_CONNECTED_SIGFOX # noqa self.__pybytes_protocol.start_Sigfox(self) print( "Connected using Sigfox. Only upload stream is supported" ) return True else: print('Invalid Sigfox RCZ specified in config!') return False except Exception as e: print('Exception in connect_sigfox: {}'.format(e)) return False # COMMON def disconnect(self, keep_wifi=False, force=False): if self.__wifi_lte_watchdog is not None: self.__wifi_lte_watchdog = WDT(timeout=constants.__WDT_MAX_TIMEOUT_MILLISECONDS) print('Watchdog timeout has been increased to {} ms'.format(constants.__WDT_MAX_TIMEOUT_MILLISECONDS)) # noqa print_debug( 1, 'self.__connection_status={} | self.__network_type={}'.format( self.__connection_status, self.__network_type ) ) if (self.__connection_status == constants.__CONNECTION_STATUS_DISCONNECTED): # noqa print_debug(3, "Already disconnected") if (constants.__CONNECTION_STATUS_CONNECTED_MQTT_WIFI <= self.__connection_status <= constants.__CONNECTION_STATUS_CONNECTED_MQTT_LTE): # noqa print_debug(1, 'MQTT over WIFI||LTE... disconnecting MQTT') try: self.__connection.disconnect(force=force) self.__connection_status = constants.__CONNECTION_STATUS_DISCONNECTED # noqa except Exception as e: print("Error disconnecting: {}".format(e)) if (self.__connection_status == constants.__CONNECTION_STATUS_CONNECTED_LORA): # noqa print_debug(1, 'Connected over LORA... closing socket and saving nvram') # noqa try: self.__lora_socket.close() self.lora.nvram_save() except Exception as e: print("Error disconnecting: {}".format(e)) if (self.__connection_status == constants.__CONNECTION_STATUS_CONNECTED_SIGFOX): # noqa print_debug(1, 'Connected over SIGFOX... closing socket') try: self.__sigfox_socket.close() except Exception as e: print("Error disconnecting: {}".format(e)) if (self.__network_type == constants.__NETWORK_TYPE_WIFI and not keep_wifi): print_debug(1, 'Connected over WIFI... disconnecting') try: self.wlan.deinit() except Exception as e: print("Error disconnecting: {}".format(e)) if (self.__network_type == constants.__NETWORK_TYPE_LTE): print_debug(1, 'Connected over LTE... disconnecting') try: lte_cfg = self.__conf.get('lte') print_debug(1, 'lte.deinit(reset={})'.format(lte_cfg.get('reset', False))) # noqa self.lte.deinit(reset=lte_cfg.get('reset', False)) except Exception as e: print("Error disconnecting: {}".format(e)) self.__network_type = None self.__connection_status = constants.__CONNECTION_STATUS_DISCONNECTED def is_connected(self): return not (self.__connection_status == constants.__CONNECTION_STATUS_DISCONNECTED) # noqa # Added for convention with other connectivity classes def isconnected(self): return not (self.__connection_status == constants.__CONNECTION_STATUS_DISCONNECTED) # noqa
def disable(): wlan = WLAN() logger.info("disabling wlan.") wlan.deinit()
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.region = LoRa.AU915 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, region=self.region, frequency=self.frequency, bandwidth=self.bw, sf=self.sf, preamble=8, coding_rate=LoRa.CODING_4_5, power_mode=LoRa.ALWAYS_ON, #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) if uos.uname()[0] == "LoPy": self.window_compensation = -1000 else: self.window_compensation = -6000 self.downlink_count = 0 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() if DEBUG: self._log("stats "+ujson.dumps(stats)) self._log('rx_timestamp diff: {}', utime.ticks_diff(stats.rx_timestamp,utime.ticks_cpu())) packet = self._make_node_packet(rx_data, self.rtc.now(), stats.rx_timestamp, stats.sfrx, self.bw, stats.rssi, stats.snr) packet = self.frequency_rounding_fix(packet, self.frequency) self._log('Received and uploading packet: {}', packet) self._push_data(packet) self._log('after _push_data') self.rxfw += 1 if events & LoRa.TX_PACKET_EVENT: self.txnb += 1 lora.init( mode=LoRa.LORA, region=self.region, frequency=self.frequency, bandwidth=self.bw, sf=self.sf, preamble=8, coding_rate=LoRa.CODING_4_5, power_mode=LoRa.ALWAYS_ON, #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 frequency_rounding_fix(self, packet, frequency): freq = str(frequency)[0:3] + '.' + str(frequency)[3] start = packet.find("freq\":") end = packet.find(",", start) packet = packet[:start + 7] + freq + packet[end:] return packet 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, region=self.region, frequency=frequency, bandwidth=self._dr_to_bw(datarate), # LoRa.BW_125KHZ sf=self._dr_to_sf(datarate), preamble=8, coding_rate=LoRa.CODING_4_5, power_mode=LoRa.ALWAYS_ON, #tx_iq=True ) if WINDOW_COMPENSATION=='cycle': self.window_compensation = -((self.downlink_count % 25) * 1000) else: self.window_compensation = WINDOW_COMPENSATION t_adj = utime.ticks_add(tmst, self.window_compensation) self.lora_sock.settimeout(1) t_cpu = utime.ticks_cpu() self._log("BEFORE spin wait at {} late {}",t_cpu,t_cpu-tmst) while utime.ticks_diff(t_adj, utime.ticks_cpu()) > 0: pass t_cpu = utime.ticks_cpu() self._log("BEFORE lora_sock.send at {} late {} window_compensation {}",t_cpu,t_cpu-tmst,self.window_compensation) self.lora_sock.send(data) self._log("AFTER lora_sock.send late {}",utime.ticks_cpu()-tmst) self.lora_sock.setblocking(False) self._log( 'Sent downlink packet scheduled on {}, at {:,d} Hz using {}: {}', tmst, frequency, datarate, data ) self.downlink_count += 1 def _send_down_link_class_c(self, data, datarate, frequency): 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, region=self.region, power_mode=LoRa.ALWAYS_ON, #tx_iq=True, device_class=LoRa.CLASS_C ) self.lora_sock.send(data) self._log( 'Sent downlink packet scheduled on {}, at {:.3f} Mhz using {}: {}', utime.ticks_cpu(), self._freq_to_float(frequency), datarate, data ) def _udp_thread(self): """ UDP thread, reads data from the server and handles it. """ loops = 0 while not self.udp_stop: if loops % 20 == 19: b4 = utime.ticks_cpu() gc.collect() self._log("gc.collect for {} us",utime.ticks_diff(utime.ticks_cpu(),b4)) b4 = utime.ticks_cpu() utime.sleep_ms(UDP_THREAD_CYCLE_MS) t_diff = utime.ticks_diff(utime.ticks_cpu(),b4) if t_diff > (UDP_THREAD_CYCLE_MS*1000*1.5): self._log("overslept! for {} us",t_diff) try: b4 = utime.ticks_cpu() data, src = self.sock.recvfrom(1024) self._log("sock.recvfrom for {} us",utime.ticks_diff(utime.ticks_cpu(),b4)) _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._log("Pull resp") self.dwnb += 1 ack_error = TX_ERR_NONE tx_pk = ujson.loads(data[4:]) if DEBUG: self._log("tx data "+ujson.dumps(tx_pk)) payload = ubinascii.a2b_base64(tx_pk["txpk"]["data"]) # depending on the board, pull the downlink message 1 or 6 ms upfronnt # tmst = utime.ticks_add(tx_pk["txpk"]["tmst"], self.window_compensation) # t_us = utime.ticks_diff(utime.ticks_cpu(), utime.ticks_add(tmst, -15000)) tmst = tx_pk["txpk"]["tmst"] t_req = utime.ticks_add(tmst, -RX_DELAY_TIMER_EARLY) t_cpu = utime.ticks_cpu() self._log("t_cpu {}",t_cpu) t_us = utime.ticks_diff(t_req, t_cpu) self._log("t_us {}",t_us) if 1000 < t_us < 10000000: self._log("Delaying for {} at {}, so should fire at t_req {}, compensated early_by {}",t_us,t_cpu,t_req,RX_DELAY_TIMER_EARLY) def handler(x): t_cpu = utime.ticks_cpu() self._log("_send_down_link alarm fired at {} late {}us",t_cpu,t_cpu-t_req) self._send_down_link( payload, tmst, tx_pk["txpk"]["datr"], int(tx_pk["txpk"]["freq"] * 1000 + 0.0005) * 1000 ) self.uplink_alarm = Timer.Alarm(handler=handler, 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.args[0] != errno.EAGAIN: self._log('UDP recv OSError Exception: {}', ex) except Exception as ex: self._log('UDP recv Exception: {}', ex) # 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. """ if len(args)==0: print('[{}] '.format(utime.ticks_cpu()) + str(message)) else: print('[{}] {}'.format( utime.ticks_cpu(), str(message).format(*args) ))
class NanoGateway: def __init__(self, id, frequency, datarate, ssid, password, server, port, ntp='pool.ntp.org', ntp_period=3600): self.id = id self.frequency = frequency self.datarate = datarate self.sf = self._dr_to_sf(datarate) self.ssid = ssid self.password = password self.server = server self.port = port self.ntp = ntp self.ntp_period = ntp_period self.rtc = machine.RTC() self.rxnb = 0 self.rxok = 0 self.rxfw = 0 self.dwnb = 0 self.txnb = 0 self.stat_alarm = None self.pull_alarm = None self.uplink_alarm = None self.udp_stop = False self.udp_lock = _thread.allocate_lock() self.lora = None self.lora_sock = None def start(self): # setup WiFi as a station and connect self.wlan = WLAN(mode=WLAN.STA) self._connect_to_wifi() # get a time sync self.rtc.ntp_sync(self.ntp, update_period=self.ntp_period) while not self.rtc.synced(): time.sleep_ms(50) print("RTC NTP sync complete") # get the server IP and create an UDP socket self.server_ip = socket.getaddrinfo(self.server, self.port)[0][-1] self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) self.sock.setsockopt(socket.SOL_SOCKET, socket.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.lora = LoRa(mode=LoRa.LORA, frequency=self.frequency, bandwidth=LoRa.BW_125KHZ, sf=self.sf, preamble=8, coding_rate=LoRa.CODING_4_5, tx_iq=True) # create a raw LoRa socket self.lora_sock = socket.socket(socket.AF_LORA, socket.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) def stop(self): # send the LoRa radio to sleep, stop the NTP sync, cancel the alarms and kill the UDP thread self.lora.callback(trigger=None, handler=None) self.lora.power_mode(LoRa.SLEEP) self.rtc.ntp_sync(None) self.stat_alarm.cancel() self.pull_alarm.cancel() self.udp_stop = True while self.udp_stop: time.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(): time.sleep_ms(50) print("WiFi connected") def _dr_to_sf(self, dr): sf = dr[2:4] if sf[1] not in '0123456789': sf = sf[:1] return int(sf) def _sf_to_dr(self, sf): return self.datarate 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 json.dumps(STAT_PK) def _make_node_packet(self, rx_data, rx_time, tmst, sf, 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]["datr"] = self._sf_to_dr(sf) RX_PK["rxpk"][0]["rssi"] = rssi RX_PK["rxpk"][0]["lsnr"] = float(snr) RX_PK["rxpk"][0]["data"] = binascii.b2a_base64(rx_data)[:-1] RX_PK["rxpk"][0]["size"] = len(rx_data) return json.dumps(RX_PK) def _push_data(self, data): token = os.urandom(2) packet = bytes([PROTOCOL_VERSION]) + token + bytes( [PUSH_DATA]) + binascii.unhexlify(self.id) + data with self.udp_lock: try: self.sock.sendto(packet, self.server_ip) except Exception: print("PUSH exception") def _pull_data(self): token = os.urandom(2) packet = bytes([PROTOCOL_VERSION]) + token + bytes( [PULL_DATA]) + binascii.unhexlify(self.id) with self.udp_lock: try: self.sock.sendto(packet, self.server_ip) except Exception: print("PULL exception") def _ack_pull_rsp(self, token, error): TX_ACK_PK["txpk_ack"]["error"] = error resp = json.dumps(TX_ACK_PK) packet = bytes([PROTOCOL_VERSION]) + token + bytes( [PULL_ACK]) + binascii.unhexlify(self.id) + resp with self.udp_lock: try: self.sock.sendto(packet, self.server_ip) except Exception: print("PULL RSP ACK exception") def _lora_cb(self, lora): 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() self._push_data( self._make_node_packet(rx_data, self.rtc.now(), stats.rx_timestamp, stats.sfrx, stats.rssi, stats.snr)) self.rxfw += 1 if events & LoRa.TX_PACKET_EVENT: self.txnb += 1 lora.init(mode=LoRa.LORA, frequency=self.frequency, bandwidth=LoRa.BW_125KHZ, sf=self.sf, preamble=8, coding_rate=LoRa.CODING_4_5, tx_iq=True) def _send_down_link(self, data, tmst, datarate, frequency): self.lora.init(mode=LoRa.LORA, frequency=frequency, bandwidth=LoRa.BW_125KHZ, sf=self._dr_to_sf(datarate), preamble=8, coding_rate=LoRa.CODING_4_5, tx_iq=True) while time.ticks_us() < tmst: pass self.lora_sock.send(data) def _udp_thread(self): while not self.udp_stop: try: data, src = self.sock.recvfrom(1024) _token = data[1:3] _type = data[3] if _type == PUSH_ACK: print("Push ack") elif _type == PULL_ACK: print("Pull ack") elif _type == PULL_RESP: self.dwnb += 1 ack_error = TX_ERR_NONE tx_pk = json.loads(data[4:]) tmst = tx_pk["txpk"]["tmst"] t_us = tmst - time.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( binascii.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 print("Downlink timestamp error!, t_us:", t_us) self._ack_pull_rsp(_token, ack_error) print("Pull rsp") except socket.timeout: pass except OSError as e: if e.errno == errno.EAGAIN: pass else: print("UDP recv OSError Exception") except Exception: print("UDP recv Exception") # wait before trying to receive again time.sleep(0.025) # we are to close the socket self.sock.close() self.udp_stop = False
# if mac > 10: # pymesh.end_device(True) # if mac == 20: # pymesh.leader_priority(255) # elif mac == 15: # pymesh.leader_priority(250) while not pymesh.is_connected(): print(pymesh.status_str()) time.sleep(3) print("Current available memory after pymesh load: %d" % gc.mem_free()) gc.collect() wlan = WLAN() wlan.deinit() wlan = WLAN(mode=WLAN.AP, ssid=node.node_ssid, auth=(WLAN.WPA2, node.node_pass), channel=11, antenna=WLAN.INT_ANT) wlan.ifconfig(id=1, config=('192.168.1.1', '255.255.255.0', '192.168.1.1', '8.8.8.8')) print("====================================================================") print("AP setting up") node.first_time_set()
from network import WLAN w = WLAN() w.deinit() ##Switch Off WLAN
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) ))
import pycom import machine from machine import Pin pycom.heartbeat(False) from network import WLAN from network import Bluetooth import os pycom.wifi_on_boot(False) wlan = WLAN() # Instantiates WLAN wlan.deinit() # Turns off wifi bluetooth = Bluetooth() # Instantiates Bluetooth bluetooth.deinit() # Turns off Bluetooth counter = pycom.nvs_get('count') # Get ID value from memory if type(counter) != int: # If the value is not in memory pycom.nvs_set('count', 0) # ID = 0
class WiFiManager: def __init__(self, manager, settings): self.manager = manager self.settings = settings # WIFI settings. self.stations = self.settings.get('networking.wifi.stations') self.station = None def start(self): """ https://docs.pycom.io/tutorials/all/wlan.html https://github.com/pycom/pydocs/blob/master/firmwareapi/pycom/network/wlan.md """ # Todo: Propagate more parameters here, e.g. for using an external antenna. self.station = WLAN() #if machine.reset_cause() == machine.SOFT_RESET: # print("WiFi STA: Network connection after SOFT_RESET.") # self.print_short_status() # # Inform about networking status. # self.print_address_status() # return True # Save the default ssid and auth for restoring AP mode later original_ssid = self.station.ssid() original_auth = self.station.auth() # Inform about networking status. self.print_address_status() # Setup network interface. self.station.init() # Check WiFi connectivity. if self.station.isconnected(): log.info( "WiFi STA: Network connection already established, will skip scanning and resume connectivity." ) self.print_short_status() # Give system some breath. time.sleep(0.25) # Inform about networking status. self.print_address_status() return True # Prepare information about known WiFi networks. networks_known = frozenset( [station['ssid'] for station in self.stations]) log.info("WiFi STA: Starting interface") self.station.mode(WLAN.STA) # Attempt to connect to known/configured networks. log.info("WiFi STA: Directly connecting to configured networks: %s", list(networks_known)) try: self.connect_stations(networks_known) except: log.warning('WiFi: Switching to AP mode not implemented yet') # Todo: Reenable WiFi AP mode in the context of an "initial configuration" mode. """ log.info('WiFi: Switching to AP mode') # WLAN.AP, original_ssid, original_auth, WLAN.INT_ANT # TOOD: Make default channel configurable self.station.init(mode=WLAN.AP, ssid=original_ssid, auth=original_auth, channel=6, antenna=WLAN.INT_ANT) """ def power_off(self): """ Power off all radio peripherals. - https://forum.pycom.io/topic/563/disabling-wifi-on-lopy - https://github.com/Hiverize/FiPy/commit/b6b15677 """ # WiFi if self.station: try: log.info('Turning off WiFi') self.station.deinit() except: log.exception('Turning off WiFi failed') def connect_stations(self, network_names): # Prepare information about known WiFi networks. network_map = {station['ssid']: station for station in self.stations} for network_name in network_names: try: # All the configuration details for this network. # { # 'ssid': 'FooBar', # 'password': '******', # 'ifconfig': ('192.168.42.42', '255.255.255.0', '192.168.42.1', '192.168.42.1'), # } network_selected = network_map[network_name] if self.connect_station(network_selected): break except Exception: log.exception( 'WiFi STA: Connecting to "{}" failed'.format(network_name)) if not self.station.isconnected(): self.forget_network(network_name) message = 'WiFi STA: Connecting to any network candidate failed' description = 'Please check your WiFi configuration for one of the ' \ 'station candidates {}.'.format(len(network_names)) log.error('{}. {}'.format(message, description)) log.warning( 'Todo: We might want to switch to AP mode here or alternatively ' 'buffer telemetry data to flash to be scheduled for transmission later.' ) raise WiFiException(message) def connect_station(self, network): network_name = network['ssid'] log.info('WiFi STA: Prepare connecting to network "{}"'.format( network_name)) auth_mode = self.get_auth_mode(network_name) log.info( 'WiFi STA: Attempt connecting to network "{}" with auth mode "{}"'. format(network_name, auth_mode)) password = network['password'] # TODO: Optionally, configure hostname. # https://docs.micropython.org/en/latest/library/network.WLAN.html # https://github.com/pycom/pycom-micropython-sigfox/pull/165 # https://forum.pycom.io/topic/3326/new-firmware-release-v1-18-0 if 'dhcp_hostname' in network: if hasattr(self.station, 'config'): log.ingo('WiFi STA: Using dhcp_hostname "{}"'.format( network['dhcp_hostname'])) self.station.config(dhcp_hostname=network['dhcp_hostname']) else: log.error('Could not set hostname on older MicroPython') # Optionally, configure static IP address. if 'ifconfig' in network: log.info( 'WiFi STA: Using static network configuration "{}"'.format( network_name)) self.station.ifconfig(config=network['ifconfig']) # Obtain timeout value. network_timeout = network.get('timeout', 15.0) # Set interval how often to poll for WiFi connectivity. network_poll_interval = 800 # Connect to WiFi station. log.info( 'WiFi STA: Starting connection to "{}" with timeout of {} seconds'. format(network_name, network_timeout)) self.station.connect(network_name, (auth_mode, password), timeout=int(network_timeout * 1000)) # Wait for station network to arrive. # ``isconnected()`` returns True when connected to a WiFi access point *and* having a valid IP address. retries = int(network_timeout * network_poll_interval) while not self.station.isconnected() and retries > 0: log.info( 'WiFi STA: Waiting for network "{}".'.format(network_name)) retries -= 1 # Save power while waiting. machine.idle() # Feed watchdog. self.manager.device.feed_watchdog() # Don't busy-wait. time.sleep_ms(network_poll_interval) if not self.station.isconnected(): raise WiFiException( 'WiFi STA: Unable to connect to "{}"'.format(network_name)) # Inform about networking status. self.print_short_status() self.print_address_status() return True def scan_stations(self): self.manager.device.feed_watchdog() # Inquire visible networks. log.info("WiFi STA: Scanning for networks") stations_available = self.station.scan() networks_found = frozenset([e.ssid for e in stations_available]) # Print names/SSIDs of networks found. log.info("WiFi STA: Networks available: %s", list(networks_found)) return stations_available # Compute set of effective networks by intersecting known with found ones. #network_candidates = list(networks_found & networks_known) #log.info("WiFi STA: Network candidates: %s", network_candidates) def get_ssid(self): return self.station.ssid() def get_ip_address(self): try: return self.station.ifconfig()[0] except: pass def get_auth_mode(self, network_name): # NVRAM key for storing auth mode per network. Maximum of 15 characters. auth_mode_nvs_key = self.auth_mode_nvs_key(network_name) # Get WiFi STA auth mode from NVRAM. try: import pycom auth_mode = pycom.nvs_get(auth_mode_nvs_key) log.info('WiFi STA: Auth mode from NVRAM with key=%s, value=%s', auth_mode_nvs_key, auth_mode) except: auth_mode = None # Fall back to find out WiFi STA auth mode by network scan. if auth_mode is None: log.info( 'WiFi STA: Unknown auth mode for network "%s", invoking WiFi scan', network_name) wifi_neighbourhood = self.scan_stations() #log.info('WiFi STA: Neighbourhood is %s', wifi_neighbourhood) for e in wifi_neighbourhood: if e.ssid == network_name: auth_mode = e.sec break if not auth_mode: message = 'WiFi STA: Unable to inquire auth mode for network "{}"'.format( network_name) log.warning(message) raise WiFiException(message) log.info( 'WiFi STA: Storing auth mode into NVRAM with key=%s, value=%s', auth_mode_nvs_key, auth_mode) try: import pycom pycom.nvs_set(auth_mode_nvs_key, auth_mode) except: log.exception('WiFi STA: Storing auth mode into NVRAM failed') return auth_mode def auth_mode_nvs_key(self, ssid): """ Hack to get a short representation of a WiFi SSID in order to squeeze it into a NVRAM key with a maximum length of 15 characters. Fixme: Review this. """ import hashlib import ubinascii digest = ubinascii.hexlify(hashlib.sha512(ssid).digest()).decode() identifier = 'wa.{}'.format(digest[15:27]) return identifier def forget_network(self, network_name): log.info('WiFi STA: Forgetting NVRAM data for network "{}"'.format( network_name)) auth_mode_nvs_key = self.auth_mode_nvs_key(network_name) try: import pycom pycom.nvs_erase(auth_mode_nvs_key) except: pass def print_short_status(self): log.info('WiFi STA: Connected to "{}" with IP address "{}"'.format( self.get_ssid(), self.get_ip_address())) def print_address_status(self): mac_address = self.humanize_mac_addresses(self.station.mac()) ifconfig = self.station.ifconfig() log.info('WiFi STA: Networking address (MAC): %s', mac_address) log.info('WiFi STA: Networking address (IP): %s', ifconfig) def humanize_mac_addresses(self, mac): info = {} if hasattr(mac, 'sta_mac'): info['sta_mac'] = format_mac_address( binascii.hexlify(mac.sta_mac).decode()) if hasattr(mac, 'ap_mac'): info['ap_mac'] = format_mac_address( binascii.hexlify(mac.ap_mac).decode()) return info def print_metrics(self): metrics = SystemWiFiMetrics(self.station).read() log.info('WiFi STA: Metrics: %s', metrics)
time.sleep_ms(5000) tft.fill(TFT.BLACK) print("Sleep: ") v = 0 fft_print = "Enter Sleep" tft.text((0, v), fft_print, TFT.WHITE, sysfont, 1) time.sleep_ms(1000) #====================================== # Garbage Collector #====================================== gc.collect() gc.mem_free() #sigfox = Sigfox(mode=Sigfox.SIGFOX, rcz=Sigfox.RCZ1) #sigfox.deinit() #bt = Bluetooth() #bt.deinit() w = WLAN() w.deinit() lte = LTE() lte.deinit() time_ms = 60000 machine.deepsleep(time_ms)
lora.power_mode(LoRa.SLEEP) random_sleep(10) pycom.rgbled(off) ### MAIN ### MY_ID = 0x00 # to be filled in get_id pycom.heartbeat(False) get_id() bt = Bluetooth() bt.deinit() server = Server() server.deinit() wlan = WLAN(mode=WLAN.STA) wlan.deinit() # remove these two lines if you activate OTA pybytes.smart_config(False) # disable provisioning # py = Pytrack() # ANSELC_ADDR = const(0x18E) # py.poke_memory(ANSELC_ADDR, ~(1 << 7)) _LORA_PKG_FORMAT = "!BB%ds" _LORA_RCV_PKG_FORMAT = "!BB%ds" (my_sf, my_bw_index, my_bw_plain, guard, my_slot, packet_size) = (7, 0, 0, 15000, -1, 16) # default values index = 0 S = 1000 active_rx = 0.0 active_tx = 0.0 proc_gw = 4000 # gw default (minimum) processing time (us) (sack_rcv, sack_bytes) = (0, 0) # will be filled later msg = crypto.getrandbits(packet_size*8) # just a random packet
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
try: sd = SD() os.mount(sd, SD_MOUNT_DIR) sd_en = True rgb.green(0x88) rgb.red(0x88) time.sleep(1) rgb.green_off() rgb.red_off() except OSError: sd_en = False #Make sure SD card access is disabled print("SD Card enabled: " + str(sd_en)) wlan = WLAN() wlan.deinit() #Disable the WiFi access point #Enable GPS here red on rgb.red_on() py = Pytrack() gps = L76GNSS(py, timeout=config.GPS_TIMEOUT) fix = False #Initially we don't have a fix #Join Lora blue on lora = LoRa(mode=LoRa.LORAWAN) dev_addr = struct.unpack(">l", binascii.unhexlify(config.DEV_ADDR))[0] nwk_swkey = binascii.unhexlify(config.NWS_KEY) app_swkey = binascii.unhexlify(config.APPS_KEY) lora.join(activation=LoRa.ABP, auth=(dev_addr, nwk_swkey, app_swkey)) prev_lat = 0 prev_lon = 0
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 WLANAgent: def __init__(self, ap_config=None, sta_config=None): self._wlan = None self.networks = {} self._sta_config = sta_config self._ap_config = ap_config def isActive(self): return self._wlan != None def isconnected(self): return self._wlan.isconnected() def activate_ap_mode(self, ssid, password): if self._ap_config is None: log('No config to init AP Mode.') return try: ssid = self._ap_config['ssid'] password = self._ap_config['password'] except KeyError: log('Need password or ssid to init Access Point.') return if self._wlan: self._wlan.deinit() self._wlan = None self._wlan = WLAN( mode=WLAN.AP, ssid=ssid, auth=(WLAN.WPA2, password), ) def stop(self): if self._wlan: self._wlan.deinit() self._wlan = None def activate_sta_mode(self): if self._sta_config is None: log('No config to init STA Mode.') return try: self.networks = self._sta_config['wifi_networks'] except KeyError: log('Need networks to init Wifi Station.') return if self._wlan: self._wlan.deinit() self._wlan = None self._wlan = WLAN(mode=WLAN.STA, ) self._wlan.ifconfig(config="dhcp") self.connect_to_ap() def connect_to_ap(self): while not self._wlan.isconnected(): try: if len(list(self.networks.items())) < 1: raise Exception("Need networks to connect to Wifi AP.") if self._wlan.mode() != WLAN.STA: raise Exception( "Must in Station Mode to connect to Wifi AP.") existing_networks = self._wlan.scan() log('Found {}. existing wlan networks.'.format( len(existing_networks))) for network in existing_networks: (ssid, bssid, sec, channel, rssi) = network log('Try to connect to network {}'.format(ssid)) if ssid in self.networks: password = self.networks[ssid] self._wlan.connect(ssid, auth=(WLAN.WPA2, password)) break now = time() while not self._wlan.isconnected(): if (time() - now > 10): raise Exception( 'Failed to connect to wlan {}. Timeout.'.format( self._wlan.ssid())) sleep(0.3) (ip, subnet_mask, gateway, DNS_server) = self._wlan.ifconfig() log('Connect to wifi {}'.format(self._wlan.ssid())) log('IP: {}'.format(ip)) except Exception as err: log(err) sleep(2)