def reset_modem(lte: LTE, debug_print=False): function_level = "1" cereg_level = "0" if debug_print: print("\twaiting for reset to finish") lte.reset() lte.init() if debug_print: print("\tsetting function level") for tries in range(5): _send_at_cmd(lte, "AT+CFUN=" + function_level) result = _send_at_cmd(lte, "AT+CFUN?") if result[0] == '+CFUN: ' + function_level: break else: raise Exception("could not set modem function level") if debug_print: print("\twaiting for SIM to be responsive") for tries in range(10): result = _send_at_cmd(lte, "AT+CIMI") if result[-1] == 'OK': break else: raise Exception("SIM does not seem to respond after reset") if debug_print: print("\tdisabling CEREG messages") # we disable unsolicited CEREG messages, as they interfere with AT communication with the SIM via CSIM commands # this also requires to use an attach method that does not require cereg messages, for pycom that is legacyattach=false for tries in range(5): _send_at_cmd(lte, "AT+CEREG=" + cereg_level) result = _send_at_cmd(lte, "AT+CEREG?") if result[0][0:9] == '+CEREG: ' + cereg_level: break else: raise Exception("could not set CEREG level")
def check_OTA_update(): # Configuration (if you are looking for the server pubkey: it's in the OTA class) #TODO: Change server URL and Pubkey to non-testing versions SERVER_URL = "ota.iot.wheelmap.pro" SERVER_PORT = 80 NBIOT_APN = "iot.1nce.net" NBIOT_BAND = None #None = autoscan NBIOT_ATTACH_TIMEOUT = 15 * 60 #seconds NBIOT_CONNECT_TIMEOUT = 15 * 60 #seconds WATCHDOG_TIMEOUT = 15 * 60 * 1000 #milliseconds #setup watchdog wdt = machine.WDT(timeout=WATCHDOG_TIMEOUT) wdt.feed() #initialize ota object variable for proper exception handling ota = None try: # # Setup Wifi OTA # from ota_wifi_secrets import WIFI_SSID, WIFI_PW # ota = WiFiOTA(WIFI_SSID, # WIFI_PW) # Setup NB-IoT OTA print("Initializing LTE") lte = LTE() lte.reset() lte.init() ota = NBIoTOTA(lte, NBIOT_APN, NBIOT_BAND, NBIOT_ATTACH_TIMEOUT, NBIOT_CONNECT_TIMEOUT) #start the update itself print("Current version: ", ota.get_current_version()) ota.connect(url=SERVER_URL, port=SERVER_PORT) ota.update() # the update did not fail, proceed to check file integrity n = ota.check_stored_files() if n > 0: print("%d corrupted files detected and reloaded! Rebooting ..." % n) time.sleep(0.5) machine.reset() except Exception as e: raise (e) #let top level loop handle exception finally: if ota is not None: ota.clean_up() # before leaving, set watchdog to large value, so we don't interfere # with code in main.py (wdt can never be disabled after use) wdt = machine.WDT(timeout=10 * 24 * 60 * 60 * 1000) wdt.feed()
class StartIoT: def __init__(self, network=LTE_M): self._network = network self.lte = LTE() try: self.lte.deinit() self.lte.reset() except: pass sleep(5) self.lte.init() sleep(5) self._assure_modem_fw() def _assure_modem_fw(self): response = self.lte.send_at_cmd('ATI1') if response != None: lines = response.split('\r\n') fw_id = lines[1][0:3] is_nb = fw_id == 'UE6' if is_nb: print('Modem is using NB-IoT firmware (%s/%s).' % (lines[1], lines[2])) else: print('Modem in using LTE-M firmware (%s/%s).' % (lines[1], lines[2])) if not is_nb and self._network == NB_IOT: print('You cannot connect using NB-IoT with wrong modem firmware! Please re-flash the modem with the correct firmware.') raise WrongNetwork if is_nb and self._network == LTE_M: print('You cannot connect using LTE-M with wrong modem firmware! Please re-flash the modem with the correct firmware.') raise WrongNetwork else: print('Failed to determine modem firmware. Rebooting device...') reset() # Reboot the device def send_at_cmd_pretty(self, cmd): print('>', cmd) response = self.lte.send_at_cmd(cmd) if response != None: lines = response.split('\r\n') for line in lines: if len(line.strip()) != 0: print('>>', line) else: print('>> No response.') return response def connect(self): # NB-IoT if (self._network == NB_IOT): self.send_at_cmd_pretty('AT+CFUN=0') self.send_at_cmd_pretty('AT+CEMODE=0') self.send_at_cmd_pretty('AT+CEMODE?') self.send_at_cmd_pretty('AT!="clearscanconfig"') self.send_at_cmd_pretty('AT!="addscanfreq band=%s dl-earfcn=%s"' % (BAND, EARFCN)) self.send_at_cmd_pretty('AT+CGDCONT=1,"IP","%s"' % APN) self.send_at_cmd_pretty('AT+COPS=1,2,"%s"' % COPS) self.send_at_cmd_pretty('AT+CFUN=1') # LTE-M (Cat M1) else: self.send_at_cmd_pretty('AT+CFUN=0') self.send_at_cmd_pretty('AT!="clearscanconfig"') self.send_at_cmd_pretty('AT!="addscanfreq band=%s dl-earfcn=%s"' % (BAND, EARFCN)) self.send_at_cmd_pretty('AT+CGDCONT=1,"IP","%s"' % APN) self.send_at_cmd_pretty('AT+CFUN=1') self.send_at_cmd_pretty('AT+CSQ') # For a range scan: # AT!="addscanfreqrange band=20 dl-earfcn-min=3450 dl-earfcn-max=6352" print('Attaching...') seconds = 0 while not self.lte.isattached() and seconds < attach_timeout: sleep(0.25) seconds += 0.25 if self.lte.isattached(): print('Attached!') else: print('Failed to attach to LTE (timeout)!') raise AttachTimeout self.lte.connect() print('Connecting...') seconds = 0 while not self.lte.isconnected() and seconds < connect_timeout: sleep(0.25) seconds += 0.25 if self.lte.isconnected(): print('Connected!') else: print('Failed to connect to LTE (timeout)!') raise ConnectTimeout def disconnect(self): if self.lte.isconnected(): self.lte.disconnect() def dettach(self): if self.lte.isattached(): self.lte.dettach()
class LteComms: def __init__(self): self.message_storage = 'AT+CPMS="SM", "SM", "SM"' gc.collect() try: self.lte = LTE() time.sleep(4) except: print("initialize LTE object?") self.lte.reset() time.sleep(4) print("delay 4 secs") def at(self, cmd): print("modem command: {}".format(cmd)) r = self.lte.send_at_cmd(cmd).split('\r\n') r = list(filter(None, r)) print("response={}".format(r)) return r def attach_LTE(self): gc.collect() time.sleep(10.0) if self.lte.isattached(): try: print("LTE was already attached, disconnecting...") if self.lte.isconnected(): print("disconnect") self.lte.disconnect() except: print("Exception during disconnect") try: if self.lte.isattached(): print("detach") self.lte.dettach() except: print("Exception during dettach") try: print("resetting modem...") self.lte.reset() except: print("Exception during reset") print("delay 5 secs") time.sleep(5.0) # enable network registration and location information, unsolicited result code self.at('AT+CEREG=2') # print("full functionality level") self.at('AT+CFUN=1') time.sleep(1.0) # using Hologram SIM self.at('AT+CGDCONT=1,"IP","hologram"') print("attempt to attach cell modem to base station...") # lte.attach() # do not use attach with custom init for Hologram SIM self.at("ATI") time.sleep(2.0) i = 0 while self.lte.isattached() == False: # get EPS Network Registration Status: # +CEREG: <stat>[,[<tac>],[<ci>],[<AcT>]] # <tac> values: # 0 - not registered # 1 - registered, home network # 2 - not registered, but searching... # 3 - registration denied # 4 - unknown (out of E-UTRAN coverage) # 5 - registered, roaming r = self.at('AT+CEREG?') try: r0 = r[0] # +CREG: 2,<tac> r0x = r0.split(',') # ['+CREG: 2',<tac>] tac = int(r0x[1]) # 0..5 print("tac={}".format(tac)) except IndexError: tac = 0 print("Index Error!!!") # get signal strength # +CSQ: <rssi>,<ber> # <rssi>: 0..31, 99-unknown r = self.at('AT+CSQ') # extended error report # r =self.at('AT+CEER') # if lte.isattached(): # print("Modem attached (isattached() function worked)!!!") # break # if (tac==1) or (tac==5): # print("Modem attached!!!") # break i = i + 5 print("not attached: {} secs".format(i)) # while self.lte.isattached(): # # self.receive_and_forward_to_chat() # continue # print("Modem not attached") print("set to check messages on sim") self.at(self.message_storage) def connect_lte_data(self): self.at('AT+CEREG?') print("Attempt to connect") if self.lte.isattached() == False: print("Not attached, try again, will fail") else: print("Attached and continue") self.lte.connect() i = 0 while not self.lte.isconnected(): i = i + 1 print("not connected: {}".format(i)) time.sleep(1.0) print("LTE connected for data!!!") # also send this to chat # so just pymesh this to all nodes in leader_mesh_list while self.lte.isconnected(): continue def scrape_webpage(self, url): s = socket.socket() s = ssl.wrap_socket(s) s.connect(socket.getaddrinfo(url, 443)[0][-1]) s.send(b"GET / HTTP/1.0\r\n\r\n") print(s.recv(4096)) s.close() def send_sms(self, number, msg): # this will somehow have to be connected to the chat with a JM msg1 print("set mode to text") self.at('AT+CMGF=1') time.sleep(.5) # msg = ('AT+CMGS="%s"\r%s\0x1a' % (number, msg)) # print(('ATTB+SQNSMSSEND="%s", "%s"' % (number, msg))) print('sendin an sms', end=' ') ans = self.lte.send_at_cmd( ('AT+SQNSMSSEND="%s", "%s"' % (number, msg))).split('\r\n') print(ans) # self.at(msg) time.sleep(4) print("sent!") def receive_and_forward_to_chat(self): # this will somehow have to be connected to the chat with a JM msg1 print("set mode to text") self.at('AT+CMGF=1') msg_list = [] msg_list = self.at('AT+CMGL="ALL"') number_of_messages = 0 if len(msg_list) > 1: print("This'll print if there a msg") if len(msg_list) > 20: print("More then 10 messages, loop") i = 1 while len(msg_list) > 20: print("This is the inner loop running %s times" % i) msg_list = self.at('AT+CMGL="ALL"') number_of_messages += len(msg_list) self.write_msg_to_file_and_delete(msg_list) time.sleep(15) i += 1 print("This is to get the last group of messages") # you don't scan for messages while it sleep, almost Need # to run this in a thread in the background. time.sleep(10) msg_list = self.at('AT+CMGL="ALL"') number_of_messages += len(msg_list) self.write_msg_to_file_and_delete(msg_list) else: print("The list is less than 10, so straight to file") number_of_messages += len(msg_list) self.write_msg_to_file_and_delete(msg_list) else: print("This prints when no messages") self.at('AT+CMGD=1,4') # Cuz apparently you need to clean out the sim card, it only holds 10 msgs # at('AT+CMGD=1,4') time.sleep(5) actual_messages = (number_of_messages / 2) - 1 print(actual_messages) def msg_parse(self, msg_list): parsed_msg_list = [] msg_list_string = "".join(msg_list) split_msg_list = msg_list_string.split('+CMGL:') for i in range(len(split_msg_list)): temp_string = str(split_msg_list[i]) if temp_string[-2:] == 'OK': parsed_msg_list.append(temp_string[:-2]) else: parsed_msg_list.append(temp_string) return parsed_msg_list def disconnect_LTE(self): self.lte.disconnect() print("LTE Data disconnected") # send to chat def unattach_lte(self): self.lte.detach(reset=True) print("LTE modem deattached") def signal_strength(self): self.at('AT+CSQ') def check_read_sms(self): self.at('AT+CMGF=1') msg_list = self.at('AT+CMGL="ALL"') print(msg_list) def write_msg_to_file_and_delete(self, msg_list): parsed_msg_list = self.msg_parse(msg_list) print("Writing to SMS log") f = open('/sd/www/sms.txt', 'a+') for i in range(len(parsed_msg_list)): if parsed_msg_list[i] != '\r\n': f.write(str(parsed_msg_list[i])) f.write('\r\n') f.close() self.at('AT+CMGD=1,4')
# r = input("Enter anything to abort...") # allow opportunity to stop program # if r != "": # sys.exit(0) print("disable MicroPython control of LED") pycom.heartbeat(False) pycom.rgbled(WHITE) print("instantiate LTE object") lte = LTE(carrier="verizon") print("delay 4 secs") time.sleep(4.0) print("reset modem") try: lte.reset() except: print("Exception during reset") print("delay 5 secs") time.sleep(5.0) if lte.isattached(): try: print("LTE was already attached, disconnecting...") if lte.isconnected(): print("disconnect") lte.disconnect() except: print("Exception during disconnect")
class StartIoT: def __init__(self, network=LTE_M): self._network = network self.lte = LTE() try: self.lte.deinit() self.lte.reset() except: pass sleep(5) self.lte.init() sleep(5) self._assure_modem_fw() def _assure_modem_fw(self): response = self.lte.send_at_cmd('ATI1') if response != None: lines = response.split('\r\n') fw_id = lines[1][0:3] is_nb = fw_id == 'UE6' if is_nb: print('Modem is using NB-IoT firmware (%s/%s).' % (lines[1], lines[2])) else: print('Modem in using LTE-M firmware (%s/%s).' % (lines[1], lines[2])) if not is_nb and self._network == NB_IOT: print( 'You cannot connect using NB-IoT with wrong modem firmware! Please re-flash the modem with the correct firmware.' ) raise WrongNetwork if is_nb and self._network == LTE_M: print( 'You cannot connect using LTE-M with wrong modem firmware! Please re-flash the modem with the correct firmware.' ) raise WrongNetwork else: print('Failed to determine modem firmware. Rebooting device...') reset() # Reboot the device def _get_assigned_ip(self): ip_address = None try: self.lte.pppsuspend() response = self.send_at_cmd_pretty('AT+CGPADDR=1') self.lte.pppresume() lines = response.split('\r\n') sections = lines[1].split('"') ip_address = sections[1] except: print('Failed to retrieve assigned IP from LTE network.') return ip_address def send_at_cmd_pretty(self, cmd): print('>', cmd) response = self.lte.send_at_cmd(cmd) if response != None: lines = response.split('\r\n') for line in lines: if len(line.strip()) != 0: print('>>', line) else: print('>> No response.') return response def connect(self): # NB-IoT if (self._network == NB_IOT): self.send_at_cmd_pretty('AT+CFUN=0') self.send_at_cmd_pretty('AT+CEMODE=0') self.send_at_cmd_pretty('AT+CEMODE?') self.send_at_cmd_pretty('AT!="clearscanconfig"') self.send_at_cmd_pretty('AT!="addscanfreq band=%s dl-earfcn=%s"' % (BAND, EARFCN)) self.send_at_cmd_pretty('AT+CGDCONT=1,"IP","%s"' % APN) self.send_at_cmd_pretty('AT+COPS=1,2,"%s"' % COPS) self.send_at_cmd_pretty('AT+CFUN=1') # LTE-M (Cat M1) else: self.send_at_cmd_pretty('AT+CFUN=0') self.send_at_cmd_pretty('AT!="clearscanconfig"') self.send_at_cmd_pretty('AT!="addscanfreq band=%s dl-earfcn=%s"' % (BAND, EARFCN)) self.send_at_cmd_pretty('AT+CGDCONT=1,"IP","%s"' % APN) self.send_at_cmd_pretty('AT+CFUN=1') self.send_at_cmd_pretty('AT+CSQ') # For a range scan: # AT!="addscanfreqrange band=20 dl-earfcn-min=3450 dl-earfcn-max=6352" print('Attaching...') seconds = 0 while not self.lte.isattached() and seconds < attach_timeout: sleep(0.25) seconds += 0.25 if self.lte.isattached(): print('Attached!') else: print('Failed to attach to LTE (timeout)!') raise AttachTimeout self.lte.connect() print('Connecting...') seconds = 0 while not self.lte.isconnected() and seconds < connect_timeout: sleep(0.25) seconds += 0.25 if self.lte.isconnected(): print('Connected!') else: print('Failed to connect to LTE (timeout)!') raise ConnectTimeout print('Retrieving assigned IP...') ip_address = self._get_assigned_ip() print("Device IP: {}".format(ip_address)) print(ip_address) # Initialise the CoAP module Coap.init(ip_address) # Register the response handler for the requests that the module initiates as a CoAP Client Coap.register_response_handler(self.response_callback) # A CoAP server is needed if CoAP push is used (messages are pushed down from Managed IoT Cloud) # self.setup_coap_server() def setup_coap_server(self): # Add a resource with a default value and a plain text content format r = Coap.add_resource('', media_type=Coap.MEDIATYPE_APP_JSON, value='default_value') # Configure the possible operations on the resource r.callback( Coap.REQUEST_GET | Coap.REQUEST_POST | Coap.REQUEST_PUT | Coap.REQUEST_DELETE, True) # Get the UDP socket created for the CoAP module coap_server_socket = Coap.socket() # Create a new poll object p = uselect.poll() # Register the CoAP module's socket to the poll p.register(coap_server_socket, uselect.POLLIN | uselect.POLLHUP | uselect.POLLERR) # Start a new thread which will handle the sockets of "p" poll _thread.start_new_thread(socket_thread, (p, coap_server_socket)) print('CoAP server running!') # The callback that handles the responses generated from the requests sent to a CoAP Server def response_callback(self, code, id_param, type_param, token, payload): # The ID can be used to pair the requests with the responses print('ID: {}'.format(id_param)) print('Code: {}'.format(code)) print('Type: {}'.format(type_param)) print('Token: {}'.format(token)) print('Payload: {}'.format(payload)) def disconnect(self): if self.lte.isconnected(): self.lte.disconnect() def dettach(self): if self.lte.isattached(): self.lte.dettach() def send(self, data): if not self.lte.isconnected(): raise Exception('Not connected! Unable to send.') id = Coap.send_request(IOTGW_IP, Coap.REQUEST_POST, uri_port=IOTGW_PORT, uri_path=IOTGW_ENDPOINT, payload=data, include_options=True) print('CoAP POST message ID: {}'.format(id)) def pull(self, uri_path='/'): if not self.lte.isconnected(): raise Exception('Not connected! Unable to pull.') id = Coap.send_request(IOTGW_IP, Coap.REQUEST_GET, uri_port=IOTGW_PORT, uri_path=uri_path, include_options=True) Coap.read() print('CoAP GET message ID: {}'.format(id))
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, 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.lte = 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. """ pycom.heartbeat(False) 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() # setup LTE CATM1 connection self.lte = LTE(carrier="verizon") self._connect_to_LTE() # 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 LTE self.lte.disconnect() self.lte.dettach() 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 _connect_to_LTE(self): print("reset modem") try: self.lte.reset() except: print("Exception during reset") print("delay 5 secs") utime.sleep(5.0) if self.lte.isattached(): try: print("LTE was already attached, disconnecting...") if self.lte.isconnected(): print("disconnect") self.lte.disconnect() except: print("Exception during disconnect") try: if self.lte.isattached(): print("detach") self.lte.dettach() except: print("Exception during dettach") try: print("resetting modem...") self.lte.reset() except: print("Exception during reset") print("delay 5 secs") utime.sleep(5.0) # enable network registration and location information, unsolicited result code self.at('AT+CEREG=2') # print("full functionality level") self.at('AT+CFUN=1') utime.sleep(1.0) # using Hologram SIM self.at('AT+CGDCONT=1,"IP","hologram"') print("attempt to attach cell modem to base station...") # lte.attach() # do not use attach with custom init for Hologram SIM self.at("ATI") utime.sleep(2.0) i = 0 while self.lte.isattached() == False: # get EPS Network Registration Status: # +CEREG: <stat>[,[<tac>],[<ci>],[<AcT>]] # <tac> values: # 0 - not registered # 1 - registered, home network # 2 - not registered, but searching... # 3 - registration denied # 4 - unknown (out of E-UTRAN coverage) # 5 - registered, roaming r = self.at('AT+CEREG?') try: r0 = r[0] # +CREG: 2,<tac> r0x = r0.split(',') # ['+CREG: 2',<tac>] tac = int(r0x[1]) # 0..5 print("tac={}".format(tac)) except IndexError: tac = 0 print("Index Error!!!") # get signal strength # +CSQ: <rssi>,<ber> # <rssi>: 0..31, 99-unknown r = self.at('AT+CSQ') # extended error report # r = at('AT+CEER') if self.lte.isattached(): print("Modem attached (isattached() function worked)!!!") break if (tac == 1) or (tac == 5): print("Modem attached!!!") break i = i + 5 print("not attached: {} secs".format(i)) if (tac != 0): self.blink(BLUE, tac) else: self.blink(RED, 5) utime.sleep(2) self.at('AT+CEREG?') print("connect: start a data session and obtain an IP address") self.lte.connect(cid=3) i = 0 while not self.lte.isconnected(): i = i + 1 print("not connected: {}".format(i)) self.blink(YELLOW, 1) utime.sleep(1.0) print("connected!!!") pycom.rgbled(GREEN) 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))) def at(self, cmd): print("modem command: {}".format(cmd)) r = self.lte.send_at_cmd(cmd).split('\r\n') r = list(filter(None, r)) print("response={}".format(r)) return r def blink(self, rgb, n): for i in range(n): pycom.rgbled(rgb) utime.sleep(0.25) pycom.rgbled(BLACK) utime.sleep(0.1)