From b34de50918bbd5caecf6241692d8fb5d1cc5cf8c Mon Sep 17 00:00:00 2001 From: brentru Date: Mon, 4 May 2020 12:07:10 -0400 Subject: [PATCH 01/18] add unpretty_ip, iccid --- adafruit_fona/adafruit_fona.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/adafruit_fona/adafruit_fona.py b/adafruit_fona/adafruit_fona.py index e75acd2..9c68ef5 100644 --- a/adafruit_fona/adafruit_fona.py +++ b/adafruit_fona/adafruit_fona.py @@ -132,10 +132,6 @@ def iemi(self): iemi = self._buf[0:15] return iemi.decode("utf-8") - def pretty_ip(self, ip): # pylint: disable=no-self-use, invalid-name - """Converts a bytearray IP address to a dotted-quad string for printing""" - return "%d.%d.%d.%d" % (ip[0], ip[1], ip[2], ip[3]) - @property def local_ip(self): """Returns the local IP Address.""" @@ -146,6 +142,26 @@ def local_ip(self): self._read_line() return self.pretty_ip(self._buf) + @property + def iccid(self): + """Returns SIM Card's ICCID number as a string.""" + if self._debug: + print("\t---> AT+CCID: ") + self._uart.write(b"AT+CCID\r\n") + self._read_line(timeout=2000) #6.2.23, 2sec max. response time + iccid = self._buf.decode() + self._read_line() # eat 'OK' + return iccid + + def pretty_ip(self, ip): # pylint: disable=no-self-use, invalid-name + """Converts a bytearray IP address to a dotted-quad string for printing""" + return "%d.%d.%d.%d" % (ip[0], ip[1], ip[2], ip[3]) + + def unpretty_ip(self, ip): # pylint: disable=no-self-use, invalid-name + """Converts a dotted-quad string to a bytearray IP address""" + octets = [int(x) for x in ip.split(".")] + return bytes(octets) + # pylint: disable=too-many-branches, too-many-statements def _init_fona(self): """Initializes FONA module.""" @@ -221,6 +237,7 @@ def _init_fona(self): self._fona_type = FONA_800_H return True + @property def gprs(self): """Returns module's GPRS state.""" From 4aa116aa670eed30327715cbb9a33382d4be5d6b Mon Sep 17 00:00:00 2001 From: brentru Date: Mon, 4 May 2020 13:07:05 -0400 Subject: [PATCH 02/18] add factory reset --- adafruit_fona/adafruit_fona.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/adafruit_fona/adafruit_fona.py b/adafruit_fona/adafruit_fona.py index 9c68ef5..9f8ce40 100644 --- a/adafruit_fona/adafruit_fona.py +++ b/adafruit_fona/adafruit_fona.py @@ -71,7 +71,7 @@ # pylint: enable=bad-whitespace -# pylint: disable=too-many-instance-attributes +# pylint: disable=too-many-instance-attributes, too-many-public-methods class FONA: """CircuitPython FONA module interface. :param ~busio.uart UART: FONA UART connection. @@ -148,11 +148,21 @@ def iccid(self): if self._debug: print("\t---> AT+CCID: ") self._uart.write(b"AT+CCID\r\n") - self._read_line(timeout=2000) #6.2.23, 2sec max. response time + self._read_line(timeout=2000) # 6.2.23, 2sec max. response time iccid = self._buf.decode() - self._read_line() # eat 'OK' + self._read_line() # eat 'OK' return iccid + def factory_reset(self): + """Resets modem to factory configuration.""" + if self._debug: + print("\t---> ATZ") + self._uart.write(b"ATZ\r\n") + + if not self._expect_reply(REPLY_OK): + return False + return True + def pretty_ip(self, ip): # pylint: disable=no-self-use, invalid-name """Converts a bytearray IP address to a dotted-quad string for printing""" return "%d.%d.%d.%d" % (ip[0], ip[1], ip[2], ip[3]) @@ -237,7 +247,6 @@ def _init_fona(self): self._fona_type = FONA_800_H return True - @property def gprs(self): """Returns module's GPRS state.""" From 3f7a1faddcf696cb1b4af95a0716920670b13430 Mon Sep 17 00:00:00 2001 From: brentru Date: Mon, 4 May 2020 13:12:09 -0400 Subject: [PATCH 03/18] add reset outside of init. routine --- adafruit_fona/adafruit_fona.py | 163 +++++++++++++++++---------------- 1 file changed, 86 insertions(+), 77 deletions(-) diff --git a/adafruit_fona/adafruit_fona.py b/adafruit_fona/adafruit_fona.py index 9f8ce40..7a55d3a 100644 --- a/adafruit_fona/adafruit_fona.py +++ b/adafruit_fona/adafruit_fona.py @@ -101,87 +101,11 @@ def __init__(self, uart, rst, debug=False): self._apn_username = None self._apn_password = None - @property - # pylint: disable=too-many-return-statements - def version(self): - """Returns FONA Version, as a string.""" - if self._fona_type == FONA_800_L: - return "FONA 800L" - if self._fona_type == FONA_800_H: - return "FONA 800H" - if self._fona_type == FONA_808_V1: - return "FONA 808 (v1)" - if self._fona_type == FONA_808_V2: - return "FONA 808 (v2)" - if self._fona_type == FONA_3G_A: - return "FONA 3G (US)" - if self._fona_type == FONA_3G_E: - return "FONA 3G (EU)" - return -1 - - @property - def iemi(self): - """Returns FONA module's IEMI number.""" - self._buf = b"" - self._uart.reset_input_buffer() - - if self._debug: - print("\t---> ", "AT+GSN") - self._uart.write(b"AT+GSN\r\n") - self._read_line(multiline=True) - iemi = self._buf[0:15] - return iemi.decode("utf-8") - - @property - def local_ip(self): - """Returns the local IP Address.""" - if self._debug: - print("\t---> AT+CIFSR") - - self._uart.write(b"AT+CIFSR\r\n") - self._read_line() - return self.pretty_ip(self._buf) - - @property - def iccid(self): - """Returns SIM Card's ICCID number as a string.""" - if self._debug: - print("\t---> AT+CCID: ") - self._uart.write(b"AT+CCID\r\n") - self._read_line(timeout=2000) # 6.2.23, 2sec max. response time - iccid = self._buf.decode() - self._read_line() # eat 'OK' - return iccid - - def factory_reset(self): - """Resets modem to factory configuration.""" - if self._debug: - print("\t---> ATZ") - self._uart.write(b"ATZ\r\n") - - if not self._expect_reply(REPLY_OK): - return False - return True - - def pretty_ip(self, ip): # pylint: disable=no-self-use, invalid-name - """Converts a bytearray IP address to a dotted-quad string for printing""" - return "%d.%d.%d.%d" % (ip[0], ip[1], ip[2], ip[3]) - - def unpretty_ip(self, ip): # pylint: disable=no-self-use, invalid-name - """Converts a dotted-quad string to a bytearray IP address""" - octets = [int(x) for x in ip.split(".")] - return bytes(octets) - # pylint: disable=too-many-branches, too-many-statements def _init_fona(self): """Initializes FONA module.""" - # Reset the module - self._rst.value = True - time.sleep(0.01) - self._rst.value = False - time.sleep(0.1) - self._rst.value = True + self.reset() if self._debug: print("Attempting to open comm with ATs") @@ -247,6 +171,82 @@ def _init_fona(self): self._fona_type = FONA_800_H return True + def factory_reset(self): + """Resets modem to factory configuration.""" + if self._debug: + print("\t---> ATZ") + self._uart.write(b"ATZ\r\n") + + if not self._expect_reply(REPLY_OK): + return False + return True + + def reset(self): + """Performs a hardware reset on the modem. + NOTE: This may take a few seconds to complete. + + """ + if self._debug: + print("* Resetting modem.") + # Reset the module + self._rst.value = True + time.sleep(0.01) + self._rst.value = False + time.sleep(0.1) + self._rst.value = True + + @property + # pylint: disable=too-many-return-statements + def version(self): + """Returns FONA Version, as a string.""" + if self._fona_type == FONA_800_L: + return "FONA 800L" + if self._fona_type == FONA_800_H: + return "FONA 800H" + if self._fona_type == FONA_808_V1: + return "FONA 808 (v1)" + if self._fona_type == FONA_808_V2: + return "FONA 808 (v2)" + if self._fona_type == FONA_3G_A: + return "FONA 3G (US)" + if self._fona_type == FONA_3G_E: + return "FONA 3G (EU)" + return -1 + + @property + def iemi(self): + """Returns FONA module's IEMI number.""" + self._buf = b"" + self._uart.reset_input_buffer() + + if self._debug: + print("\t---> ", "AT+GSN") + self._uart.write(b"AT+GSN\r\n") + self._read_line(multiline=True) + iemi = self._buf[0:15] + return iemi.decode("utf-8") + + @property + def local_ip(self): + """Returns the local IP Address.""" + if self._debug: + print("\t---> AT+CIFSR") + + self._uart.write(b"AT+CIFSR\r\n") + self._read_line() + return self.pretty_ip(self._buf) + + @property + def iccid(self): + """Returns SIM Card's ICCID number as a string.""" + if self._debug: + print("\t---> AT+CCID: ") + self._uart.write(b"AT+CCID\r\n") + self._read_line(timeout=2000) # 6.2.23, 2sec max. response time + iccid = self._buf.decode() + self._read_line() # eat 'OK' + return iccid + @property def gprs(self): """Returns module's GPRS state.""" @@ -558,6 +558,15 @@ def get_host_by_name(self, hostname): print("\t<--- ", self._buf) return self._buf + def pretty_ip(self, ip): # pylint: disable=no-self-use, invalid-name + """Converts a bytearray IP address to a dotted-quad string for printing""" + return "%d.%d.%d.%d" % (ip[0], ip[1], ip[2], ip[3]) + + def unpretty_ip(self, ip): # pylint: disable=no-self-use, invalid-name + """Converts a dotted-quad string to a bytearray IP address""" + octets = [int(x) for x in ip.split(".")] + return bytes(octets) + ### Socket API (TCP, UDP) ### def get_socket(self): From b2c9f4699147aca1ed43aa54a71c9e649886a241 Mon Sep 17 00:00:00 2001 From: brentru Date: Mon, 4 May 2020 15:32:55 -0400 Subject: [PATCH 04/18] starting refactoring out to GSM class --- adafruit_fona/adafruit_fona.py | 96 +++++++++--------------------- adafruit_fona/adafruit_fona_gsm.py | 66 ++++++++++++++++++++ examples/fona_simpletest.py | 32 ++-------- 3 files changed, 100 insertions(+), 94 deletions(-) create mode 100755 adafruit_fona/adafruit_fona_gsm.py diff --git a/adafruit_fona/adafruit_fona.py b/adafruit_fona/adafruit_fona.py index 7a55d3a..e97338b 100644 --- a/adafruit_fona/adafruit_fona.py +++ b/adafruit_fona/adafruit_fona.py @@ -101,6 +101,7 @@ def __init__(self, uart, rst, debug=False): self._apn_username = None self._apn_password = None + # pylint: disable=too-many-branches, too-many-statements def _init_fona(self): """Initializes FONA module.""" @@ -242,9 +243,9 @@ def iccid(self): if self._debug: print("\t---> AT+CCID: ") self._uart.write(b"AT+CCID\r\n") - self._read_line(timeout=2000) # 6.2.23, 2sec max. response time + self._read_line(timeout=2000) #6.2.23, 2sec max. response time iccid = self._buf.decode() - self._read_line() # eat 'OK' + self._read_line() # eat 'OK' return iccid @property @@ -256,46 +257,31 @@ def gprs(self): return False return self._buf - def configure_gprs(self, config): - """If config provided, sets GPRS configuration to provided tuple in format: - (apn_network, apn_username, apn_password) - - """ - if self._debug: - print("* Setting GPRS Config to: ", config) - apn, username, password = config - self._apn = apn.encode() - self._apn_username = username.encode() - self._apn_password = password.encode() - return self._apn, self._apn_username, self._apn_password - @gprs.setter - def gprs(self, gprs_on=True): - """Sets GPRS configuration. - :param bool gprs_on: Turns on GPRS, enabled by default. + def gprs(self, apn=None, enable=True): + """Sets up GPRS. + :param tuple apn: Tuple containing APN network name, username, and password. + :param bool enable: Enables or disables GPRS. """ - attempts = 5 - while not self._set_gprs(gprs_on): - if attempts == 0: - raise RuntimeError("Unable to establish PDP context.") - if self._debug: - print("* Unable to bringup network, retrying, ", attempts) - self._set_gprs(False) - attempts -= 1 - time.sleep(5) - return True + return self._set_gprs(apn, enable) # pylint: disable=too-many-return-statements - def _set_gprs(self, gprs_on=True): - """Enables or disables GPRS configuration. - :param bool gprs_on: Turns on GPRS, enabled by default. + def _set_gprs(self, apn=None, enable=True): + """Sets and configures GPRS. + :param bool enable: Enables or disables GPRS. """ - if gprs_on: + if enable: if self._debug: print("* Enabling GPRS..") + apn_name, apn_user, apn_pass = apn + + apn_name = apn_name.encode() + apn_user = apn_user.encode() + apn_pass = apn_pass.encode() + # ensure FONA is registered with cell network attempts = 10 while self.network_status != 1: @@ -333,7 +319,7 @@ def _set_gprs(self, gprs_on=True): # Send command AT+SAPBR=3,1,"APN","" # where is the configured APN value. self._send_check_reply_quoted( - b'AT+SAPBR=3,1,"APN",', self._apn, REPLY_OK, 10000 + b'AT+SAPBR=3,1,"APN",', apn_name, REPLY_OK, 10000 ) # send AT+CSTT,"apn","user","pass" @@ -341,13 +327,13 @@ def _set_gprs(self, gprs_on=True): print("setting APN...") self._uart.reset_input_buffer() - self._uart.write(b'AT+CSTT="' + self._apn) + self._uart.write(b'AT+CSTT="' + apn_name) - if self._apn_username is not None: - self._uart.write(b'","' + self._apn_username) + if apn_user is not None: + self._uart.write(b'","' + apn_user) - if self._apn_password is not None: - self._uart.write(b'","' + self._apn_password) + if apn_pass is not None: + self._uart.write(b'","' + apn_pass) self._uart.write(b'"\r\n') if not self._get_reply(REPLY_OK): @@ -355,13 +341,13 @@ def _set_gprs(self, gprs_on=True): # Set username if not self._send_check_reply_quoted( - b'AT+SAPBR=3,1,"USER",', self._apn_username, REPLY_OK, 10000 + b'AT+SAPBR=3,1,"USER",', apn_user, REPLY_OK, 10000 ): return False # Set password if not self._send_check_reply_quoted( - b'AT+SAPBR=3,1,"PWD",', self._apn_password, REPLY_OK, 100000 + b'AT+SAPBR=3,1,"PWD",', apn_pass, REPLY_OK, 100000 ): return False @@ -439,7 +425,7 @@ def rssi(self): def gps(self): """Returns the GPS status.""" if self._debug: - print("GPS STATUS") + print("* GPS Status") if self._fona_type == FONA_808_V2: # 808 V2 uses GNS commands and doesn't have an explicit 2D/3D fix status. # Instead just look for a fix and if found assume it's a 3D fix. @@ -462,37 +448,13 @@ def gps(self): ) return status - @gps.setter - def gps(self, enable_gps=False): - """Attempts to enable or disable the GPS module. - NOTE: This is only for FONA 3G or FONA808 modules. - :param bool enable_gps: Enables the GPS module, disabled by default. - - """ - # failed attempts before returning -1 - attempts = 10 - failure_count = 0 - # Set the GPS module - self._set_gps(enable_gps) - - # Wait for a GPS fix - while self.gps != 3: - if self._debug: - print("\t* GPS fix not found, retrying, ", failure_count) - failure_count += 1 - if failure_count >= attempts: - return False - time.sleep(1) - return True - - def _set_gps(self, gps_on=False): + @gps.setter + def gps(self, gps_on=False): """Sets GPS module power, parses returned buffer. :param bool gps_on: Enables the GPS module, disabled by default. """ - if self._debug: - print("* Setting GPS") if not ( self._fona_type == FONA_3G_A or self._fona_type == FONA_3G_E diff --git a/adafruit_fona/adafruit_fona_gsm.py b/adafruit_fona/adafruit_fona_gsm.py new file mode 100755 index 0000000..f942c00 --- /dev/null +++ b/adafruit_fona/adafruit_fona_gsm.py @@ -0,0 +1,66 @@ +# The MIT License (MIT) +# +# Copyright (c) 2020 Brent Rubell for Adafruit Industries +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +""" +`adafruit_fona_gsm` +================================================================================= + +Interface for 2G GSM cellular modems such as the Adafruit FONA808. + +* Author(s): Brent Rubell + +""" +import time + +class GSM: + def __init__(self, fona, apn): + """Initializes interface with 2G GSM modem. + :param adafruit_fona fona: Adafruit FONA module. + :param tuple apn: Tuple containing APN name, (optional) APN username, + and APN password. + + """ + self._iface = fona + # Attempt to enable GPS module and obtain GPS fix + self._iface.gps = True + + attempts = 10 + while not self._iface.gps == 3: + if attempts < 0: + raise RuntimeError("Unable to establish GPS fix.") + attempts -= 1 + + # Set GPRS + self._iface.gprs(True, apn) + attempts = 5 + + while not self.gprs(apn, True): + if attempts == 0: + raise RuntimeError("Unable to establish PDP context.") + if self._debug: + print("* Unable to bringup network, retrying, ", attempts) + self._iface.gprs(apn, False) + attempts -= 1 + time.sleep(5) + + def network_attached(self): + """Returns if modem is attached to cellular network.""" + diff --git a/examples/fona_simpletest.py b/examples/fona_simpletest.py index 7998d0f..75649bb 100644 --- a/examples/fona_simpletest.py +++ b/examples/fona_simpletest.py @@ -2,6 +2,7 @@ import busio import digitalio from adafruit_fona.adafruit_fona import FONA +from adafruit_fona.adafruit_fona_gsm import GSM import adafruit_fona.adafruit_fona_socket as cellular_socket import adafruit_requests as requests @@ -26,35 +27,12 @@ # Initialize FONA module (this may take a few seconds) fona = FONA(uart, rst) -# Enable GPS -fona.gps = True +# Enable FONA debugging +fona._debug = True -# Bring up cellular connection -fona.configure_gprs((secrets["apn"], secrets["apn_username"], secrets["apn_password"])) +# Enable GSM +gsm = GSM(fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"])) -# Bring up GPRS -fona.gprs = True - -# Initialize a requests object with a socket and cellular interface -requests.set_socket(cellular_socket, fona) print("My IP address is:", fona.local_ip) print("IP lookup adafruit.com: %s" % fona.get_host_by_name("adafruit.com")) - -# fona._debug = True -print("Fetching text from", TEXT_URL) -r = requests.get(TEXT_URL) -print("-" * 40) -print(r.text) -print("-" * 40) -r.close() - -print() -print("Fetching json from", JSON_URL) -r = requests.get(JSON_URL) -print("-" * 40) -print(r.json()) -print("-" * 40) -r.close() - -print("Done!") From b2fa999a4765f88941f617b8fe03d0ef849a8466 Mon Sep 17 00:00:00 2001 From: brentru Date: Mon, 4 May 2020 16:55:19 -0400 Subject: [PATCH 05/18] move network check outside fona --- adafruit_fona/adafruit_fona.py | 36 +++++------------------------- adafruit_fona/adafruit_fona_gsm.py | 31 +++++++++++++++++++------ examples/fona_simpletest.py | 4 ++-- 3 files changed, 31 insertions(+), 40 deletions(-) diff --git a/adafruit_fona/adafruit_fona.py b/adafruit_fona/adafruit_fona.py index e97338b..3580613 100644 --- a/adafruit_fona/adafruit_fona.py +++ b/adafruit_fona/adafruit_fona.py @@ -96,12 +96,6 @@ def __init__(self, uart, rst, debug=False): if not self._init_fona(): raise RuntimeError("Unable to find FONA. Please check connections.") - # GPRS - self._apn = None - self._apn_username = None - self._apn_password = None - - # pylint: disable=too-many-branches, too-many-statements def _init_fona(self): """Initializes FONA module.""" @@ -234,7 +228,7 @@ def local_ip(self): print("\t---> AT+CIFSR") self._uart.write(b"AT+CIFSR\r\n") - self._read_line() + print(self._buf) return self.pretty_ip(self._buf) @property @@ -257,24 +251,15 @@ def gprs(self): return False return self._buf - @gprs.setter - def gprs(self, apn=None, enable=True): - """Sets up GPRS. - :param tuple apn: Tuple containing APN network name, username, and password. - :param bool enable: Enables or disables GPRS. - - """ - return self._set_gprs(apn, enable) - # pylint: disable=too-many-return-statements - def _set_gprs(self, apn=None, enable=True): + def set_gprs(self, apn=None, enable=True): """Sets and configures GPRS. :param bool enable: Enables or disables GPRS. """ if enable: if self._debug: - print("* Enabling GPRS..") + print("* Enabling GPRS...") apn_name, apn_user, apn_pass = apn @@ -282,16 +267,6 @@ def _set_gprs(self, apn=None, enable=True): apn_user = apn_user.encode() apn_pass = apn_pass.encode() - # ensure FONA is registered with cell network - attempts = 10 - while self.network_status != 1: - if attempts == 0: - return False - if self._debug: - print("* Not registered with network, retrying, ", attempts) - attempts -= 1 - time.sleep(5) - # enable multi connection mode (3,1) if not self._send_check_reply(b"AT+CIPMUX=1", reply=REPLY_OK): return False @@ -361,10 +336,9 @@ def _set_gprs(self, apn=None, enable=True): if not self._send_check_reply(b"AT+CIICR", reply=REPLY_OK, timeout=10000): return False - # Query local IP - if not self.local_ip: - return False else: + if self._debug: + print("* Disabling GPRS...") # reset PDP state if not self._send_check_reply( b"AT+CIPSHUT", reply=b"SHUT OK", timeout=20000 diff --git a/adafruit_fona/adafruit_fona_gsm.py b/adafruit_fona/adafruit_fona_gsm.py index f942c00..482c6d5 100755 --- a/adafruit_fona/adafruit_fona_gsm.py +++ b/adafruit_fona/adafruit_fona_gsm.py @@ -31,36 +31,53 @@ import time class GSM: - def __init__(self, fona, apn): + def __init__(self, fona, apn, debug=True): """Initializes interface with 2G GSM modem. :param adafruit_fona fona: Adafruit FONA module. :param tuple apn: Tuple containing APN name, (optional) APN username, and APN password. + :param bool debug: Enable verbose debug output. """ self._iface = fona - # Attempt to enable GPS module and obtain GPS fix - self._iface.gps = True + self._debug = debug + # Attempt to enable GPS module and obtain GPS fix attempts = 10 + self._iface.gps = True while not self._iface.gps == 3: if attempts < 0: raise RuntimeError("Unable to establish GPS fix.") attempts -= 1 + time.sleep(0.5) + + # Check if FONA is registered to GSM network + attempts = 30 + while self._iface.network_status != 1: + if attempts == 0: + raise TimeoutError("Not registered with GSM network.") + if self._debug: + print("* Not registered with network, retrying, ", attempts) + attempts -= 1 + time.sleep(1) # Set GPRS - self._iface.gprs(True, apn) attempts = 5 - - while not self.gprs(apn, True): + while not self._iface.set_gprs(apn, True): if attempts == 0: raise RuntimeError("Unable to establish PDP context.") if self._debug: print("* Unable to bringup network, retrying, ", attempts) - self._iface.gprs(apn, False) + self._iface.set_gprs(apn, False) attempts -= 1 time.sleep(5) + # Query local IP + # TODO + print("IP: ", self._iface.local_ip) + def network_attached(self): """Returns if modem is attached to cellular network.""" + # TODO! + pass diff --git a/examples/fona_simpletest.py b/examples/fona_simpletest.py index 75649bb..39ab542 100644 --- a/examples/fona_simpletest.py +++ b/examples/fona_simpletest.py @@ -34,5 +34,5 @@ gsm = GSM(fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"])) -print("My IP address is:", fona.local_ip) -print("IP lookup adafruit.com: %s" % fona.get_host_by_name("adafruit.com")) +# print("My IP address is:", fona.local_ip) +# print("IP lookup adafruit.com: %s" % fona.get_host_by_name("adafruit.com")) From 2e555831019e984c27ea691042ea5ecf2e9f7015 Mon Sep 17 00:00:00 2001 From: brentru Date: Mon, 4 May 2020 17:01:48 -0400 Subject: [PATCH 06/18] working init. sequence of gsm --- adafruit_fona/adafruit_fona.py | 2 +- adafruit_fona/adafruit_fona_gsm.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/adafruit_fona/adafruit_fona.py b/adafruit_fona/adafruit_fona.py index 3580613..1362ef8 100644 --- a/adafruit_fona/adafruit_fona.py +++ b/adafruit_fona/adafruit_fona.py @@ -228,7 +228,7 @@ def local_ip(self): print("\t---> AT+CIFSR") self._uart.write(b"AT+CIFSR\r\n") - print(self._buf) + self._read_line() return self.pretty_ip(self._buf) @property diff --git a/adafruit_fona/adafruit_fona_gsm.py b/adafruit_fona/adafruit_fona_gsm.py index 482c6d5..3327244 100755 --- a/adafruit_fona/adafruit_fona_gsm.py +++ b/adafruit_fona/adafruit_fona_gsm.py @@ -73,8 +73,8 @@ def __init__(self, fona, apn, debug=True): time.sleep(5) # Query local IP - # TODO - print("IP: ", self._iface.local_ip) + if not self._iface.local_ip: + raise RuntimeError("Unable to obtain module IP address.") def network_attached(self): """Returns if modem is attached to cellular network.""" From 6c2412fbc68c27c5f72492d235188a9fb7502b70 Mon Sep 17 00:00:00 2001 From: brentru Date: Mon, 4 May 2020 17:09:22 -0400 Subject: [PATCH 07/18] bringin connect, remove attached, dont need to do that because we attach during initialization instead --- adafruit_fona/adafruit_fona_gsm.py | 15 ++++++++++++--- examples/fona_simpletest.py | 4 +--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/adafruit_fona/adafruit_fona_gsm.py b/adafruit_fona/adafruit_fona_gsm.py index 3327244..77e39ae 100755 --- a/adafruit_fona/adafruit_fona_gsm.py +++ b/adafruit_fona/adafruit_fona_gsm.py @@ -76,8 +76,17 @@ def __init__(self, fona, apn, debug=True): if not self._iface.local_ip: raise RuntimeError("Unable to obtain module IP address.") - def network_attached(self): - """Returns if modem is attached to cellular network.""" - # TODO! + @property + def is_connected(self): + """Returns if attached to GPRS and an IP Addresss was obtained. + """ + if not self._iface.gprs and self._iface.local_ip: + return False + return True + + def connect(self): + """Connect to GPRS network + """ + # TODO pass diff --git a/examples/fona_simpletest.py b/examples/fona_simpletest.py index 39ab542..579c393 100644 --- a/examples/fona_simpletest.py +++ b/examples/fona_simpletest.py @@ -33,6 +33,4 @@ # Enable GSM gsm = GSM(fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"])) - -# print("My IP address is:", fona.local_ip) -# print("IP lookup adafruit.com: %s" % fona.get_host_by_name("adafruit.com")) +print(gsm.is_connected) \ No newline at end of file From 2b6bccf9fbe1df4cfb164bb9d271f9920733f1e4 Mon Sep 17 00:00:00 2001 From: brentru Date: Tue, 5 May 2020 11:54:29 -0400 Subject: [PATCH 08/18] sep. connect and is_connected --- adafruit_fona/adafruit_fona.py | 6 +++++- adafruit_fona/adafruit_fona_gsm.py | 31 +++++++++++++----------------- examples/fona_simpletest.py | 1 + 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/adafruit_fona/adafruit_fona.py b/adafruit_fona/adafruit_fona.py index 1362ef8..7235860 100644 --- a/adafruit_fona/adafruit_fona.py +++ b/adafruit_fona/adafruit_fona.py @@ -229,7 +229,11 @@ def local_ip(self): self._uart.write(b"AT+CIFSR\r\n") self._read_line() - return self.pretty_ip(self._buf) + try: + ip = self.pretty_ip(self._buf) + except: + return False + return ip @property def iccid(self): diff --git a/adafruit_fona/adafruit_fona_gsm.py b/adafruit_fona/adafruit_fona_gsm.py index 77e39ae..ad78f09 100755 --- a/adafruit_fona/adafruit_fona_gsm.py +++ b/adafruit_fona/adafruit_fona_gsm.py @@ -41,6 +41,7 @@ def __init__(self, fona, apn, debug=True): """ self._iface = fona self._debug = debug + self._apn = apn # Attempt to enable GPS module and obtain GPS fix attempts = 10 @@ -61,32 +62,26 @@ def __init__(self, fona, apn, debug=True): attempts -= 1 time.sleep(1) - # Set GPRS - attempts = 5 - while not self._iface.set_gprs(apn, True): - if attempts == 0: - raise RuntimeError("Unable to establish PDP context.") - if self._debug: - print("* Unable to bringup network, retrying, ", attempts) - self._iface.set_gprs(apn, False) - attempts -= 1 - time.sleep(5) - - # Query local IP - if not self._iface.local_ip: - raise RuntimeError("Unable to obtain module IP address.") @property def is_connected(self): """Returns if attached to GPRS and an IP Addresss was obtained. """ + self._iface.local_ip if not self._iface.gprs and self._iface.local_ip: return False return True - def connect(self): + def connect(self, attempts=5): """Connect to GPRS network - """ - # TODO - pass + :param int attempts: Amount of attempts to connect to GSM network. + """ + while not self._iface.set_gprs(self._apn, True): + if attempts == 0: + raise RuntimeError("Unable to establish PDP context.") + if self._debug: + print("* Unable to bringup network, retrying, ", attempts) + self._iface.set_gprs(self._apn, False) + attempts -= 1 + time.sleep(5) diff --git a/examples/fona_simpletest.py b/examples/fona_simpletest.py index 579c393..7efd48a 100644 --- a/examples/fona_simpletest.py +++ b/examples/fona_simpletest.py @@ -32,5 +32,6 @@ # Enable GSM gsm = GSM(fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"])) +gsm.connect() print(gsm.is_connected) \ No newline at end of file From 2c38ffabfa59fd038c7224050ede6c432d196298 Mon Sep 17 00:00:00 2001 From: brentru Date: Tue, 5 May 2020 12:35:43 -0400 Subject: [PATCH 09/18] query is_connected, handle within user-code --- adafruit_fona/adafruit_fona.py | 6 +++++- adafruit_fona/adafruit_fona_gsm.py | 23 ++++++++--------------- examples/fona_simpletest.py | 11 ++++++++--- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/adafruit_fona/adafruit_fona.py b/adafruit_fona/adafruit_fona.py index 7235860..2c7aa7f 100644 --- a/adafruit_fona/adafruit_fona.py +++ b/adafruit_fona/adafruit_fona.py @@ -249,11 +249,15 @@ def iccid(self): @property def gprs(self): """Returns module's GPRS state.""" + self._read_line() if self._debug: print("* Check GPRS State") + if not self._send_parse_reply(b"AT+CGATT?", b"+CGATT: ", ":"): return False - return self._buf + if not self._buf: + return False + return True # pylint: disable=too-many-return-statements def set_gprs(self, apn=None, enable=True): diff --git a/adafruit_fona/adafruit_fona_gsm.py b/adafruit_fona/adafruit_fona_gsm.py index ad78f09..1f420e6 100755 --- a/adafruit_fona/adafruit_fona_gsm.py +++ b/adafruit_fona/adafruit_fona_gsm.py @@ -42,6 +42,7 @@ def __init__(self, fona, apn, debug=True): self._iface = fona self._debug = debug self._apn = apn + self._gsm_connected = False # Attempt to enable GPS module and obtain GPS fix attempts = 10 @@ -65,23 +66,15 @@ def __init__(self, fona, apn, debug=True): @property def is_connected(self): - """Returns if attached to GPRS and an IP Addresss was obtained. + """Returns if attached to GSM and an IP Addresss was obtained. """ - self._iface.local_ip - if not self._iface.gprs and self._iface.local_ip: - return False - return True + return self._gsm_connected - def connect(self, attempts=5): - """Connect to GPRS network - :param int attempts: Amount of attempts to connect to GSM network. + def connect(self): + """Connect to GSM network """ - while not self._iface.set_gprs(self._apn, True): - if attempts == 0: - raise RuntimeError("Unable to establish PDP context.") - if self._debug: - print("* Unable to bringup network, retrying, ", attempts) + if not self._iface.set_gprs(self._apn, True): + # reset context for next connection attempt self._iface.set_gprs(self._apn, False) - attempts -= 1 - time.sleep(5) + self._gsm_connected = True diff --git a/examples/fona_simpletest.py b/examples/fona_simpletest.py index 7efd48a..6caeb7b 100644 --- a/examples/fona_simpletest.py +++ b/examples/fona_simpletest.py @@ -1,3 +1,4 @@ +import time import board import busio import digitalio @@ -30,8 +31,12 @@ # Enable FONA debugging fona._debug = True -# Enable GSM +# initialize gsm gsm = GSM(fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"])) -gsm.connect() -print(gsm.is_connected) \ No newline at end of file +while not gsm.is_connected: + print("Connecting to network...") + gsm.connect() + time.sleep(5) + +print("connected!") \ No newline at end of file From 29544f94b8459ef705518d3dcac706e737ab3a09 Mon Sep 17 00:00:00 2001 From: brentru Date: Tue, 5 May 2020 12:43:50 -0400 Subject: [PATCH 10/18] move local ip query to gprs method --- adafruit_fona/adafruit_fona.py | 4 ++++ adafruit_fona/adafruit_fona_gsm.py | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/adafruit_fona/adafruit_fona.py b/adafruit_fona/adafruit_fona.py index 2c7aa7f..af5fb41 100644 --- a/adafruit_fona/adafruit_fona.py +++ b/adafruit_fona/adafruit_fona.py @@ -344,6 +344,10 @@ def set_gprs(self, apn=None, enable=True): if not self._send_check_reply(b"AT+CIICR", reply=REPLY_OK, timeout=10000): return False + # Query local IP + if not self.local_ip: + return False + else: if self._debug: print("* Disabling GPRS...") diff --git a/adafruit_fona/adafruit_fona_gsm.py b/adafruit_fona/adafruit_fona_gsm.py index 1f420e6..62350b7 100755 --- a/adafruit_fona/adafruit_fona_gsm.py +++ b/adafruit_fona/adafruit_fona_gsm.py @@ -68,13 +68,16 @@ def __init__(self, fona, apn, debug=True): def is_connected(self): """Returns if attached to GSM and an IP Addresss was obtained. """ - return self._gsm_connected + if not self._gsm_connected: + return False + return True def connect(self): """Connect to GSM network """ - if not self._iface.set_gprs(self._apn, True): + if self._iface.set_gprs(self._apn, True): + self._gsm_connected = True + else: # reset context for next connection attempt self._iface.set_gprs(self._apn, False) - self._gsm_connected = True From 203717751f2a938407a3a83f856e20d0d2d81c47 Mon Sep 17 00:00:00 2001 From: brentru Date: Tue, 5 May 2020 12:47:47 -0400 Subject: [PATCH 11/18] update to gsm module --- adafruit_fona/adafruit_fona.py | 10 +++++----- adafruit_fona/adafruit_fona_gsm.py | 8 ++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/adafruit_fona/adafruit_fona.py b/adafruit_fona/adafruit_fona.py index af5fb41..6ef98d9 100644 --- a/adafruit_fona/adafruit_fona.py +++ b/adafruit_fona/adafruit_fona.py @@ -223,17 +223,17 @@ def iemi(self): @property def local_ip(self): - """Returns the local IP Address.""" + """Returns the local IP Address, False if not set.""" if self._debug: print("\t---> AT+CIFSR") self._uart.write(b"AT+CIFSR\r\n") self._read_line() try: - ip = self.pretty_ip(self._buf) - except: + ip_addr = self.pretty_ip(self._buf) + except ValueError: return False - return ip + return ip_addr @property def iccid(self): @@ -261,7 +261,7 @@ def gprs(self): # pylint: disable=too-many-return-statements def set_gprs(self, apn=None, enable=True): - """Sets and configures GPRS. + """Configures and brings up GPRS. :param bool enable: Enables or disables GPRS. """ diff --git a/adafruit_fona/adafruit_fona_gsm.py b/adafruit_fona/adafruit_fona_gsm.py index 62350b7..29dea8e 100755 --- a/adafruit_fona/adafruit_fona_gsm.py +++ b/adafruit_fona/adafruit_fona_gsm.py @@ -31,9 +31,11 @@ import time class GSM: + """Interface for interacting with FONA 2G GSM modems. + """ def __init__(self, fona, apn, debug=True): """Initializes interface with 2G GSM modem. - :param adafruit_fona fona: Adafruit FONA module. + :param adafruit_fona fona: Adafruit FONA module. :param tuple apn: Tuple containing APN name, (optional) APN username, and APN password. :param bool debug: Enable verbose debug output. @@ -66,7 +68,9 @@ def __init__(self, fona, apn, debug=True): @property def is_connected(self): - """Returns if attached to GSM and an IP Addresss was obtained. + """Returns if attached to GSM + and an IP Addresss was obtained. + """ if not self._gsm_connected: return False From 52c01a58fa0f1fae4ad9e50bafdeef101dd5191e Mon Sep 17 00:00:00 2001 From: brentru Date: Tue, 5 May 2020 13:56:55 -0400 Subject: [PATCH 12/18] add destructor, properties, disconnect, refactor examples --- adafruit_fona/adafruit_fona_gsm.py | 24 +++++++++++++++++++++--- examples/fona_aio_post.py | 17 +++++++++-------- examples/fona_simpletest.py | 24 +++++++++++++++++++++++- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/adafruit_fona/adafruit_fona_gsm.py b/adafruit_fona/adafruit_fona_gsm.py index 29dea8e..04d7de0 100755 --- a/adafruit_fona/adafruit_fona_gsm.py +++ b/adafruit_fona/adafruit_fona_gsm.py @@ -65,6 +65,21 @@ def __init__(self, fona, apn, debug=True): attempts -= 1 time.sleep(1) + def __enter__(self): + return self + + def __exit__(self): + self.disconnect() + + @property + def imei(self): + """Returns the GSM modem's IEMI number, as a string.""" + return self._iface.iemi + + @property + def iccid(self): + """Returns the SIM card's ICCID, as a string.""" + return self._iface.iccid @property def is_connected(self): @@ -77,11 +92,14 @@ def is_connected(self): return True def connect(self): - """Connect to GSM network - - """ + """Connect to GSM network.""" if self._iface.set_gprs(self._apn, True): self._gsm_connected = True else: # reset context for next connection attempt self._iface.set_gprs(self._apn, False) + + def disconnect(self): + """Disconnect from GSM network.""" + self._iface.set_gprs(self._apn, False) + self._gsm_connected = False diff --git a/examples/fona_aio_post.py b/examples/fona_aio_post.py index 2119c37..2d4df41 100755 --- a/examples/fona_aio_post.py +++ b/examples/fona_aio_post.py @@ -3,6 +3,7 @@ import busio import digitalio from adafruit_fona.adafruit_fona import FONA +from adafruit_fona.adafruit_fona_gsm import GSM import adafruit_fona.adafruit_fona_socket as cellular_socket import adafruit_requests as requests @@ -20,18 +21,18 @@ rst = digitalio.DigitalInOut(board.D4) # Initialize FONA module (this may take a few seconds) -print("Initializing FONA") fona = FONA(uart, rst) -# Enable GPS -fona.gps = True +# Enable FONA debugging +fona._debug = True -# Bring up cellular connection -fona.configure_gprs((secrets["apn"], secrets["apn_username"], secrets["apn_password"])) +# initialize gsm +gsm = GSM(fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"])) -# Bring up GPRS -fona.gprs = True -print("FONA initialized") +while not gsm.is_connected: + print("Connecting to network...") + gsm.connect() + time.sleep(5) # Initialize a requests object with a socket and cellular interface requests.set_socket(cellular_socket, fona) diff --git a/examples/fona_simpletest.py b/examples/fona_simpletest.py index 6caeb7b..39ca29d 100644 --- a/examples/fona_simpletest.py +++ b/examples/fona_simpletest.py @@ -39,4 +39,26 @@ gsm.connect() time.sleep(5) -print("connected!") \ No newline at end of file +print("My IP address is:", fona.local_ip) +print("IP lookup adafruit.com: %s" % fona.get_host_by_name("adafruit.com")) + +# Initialize a requests object with a socket and cellular interface +requests.set_socket(cellular_socket, fona) + +# fona._debug = True +print("Fetching text from", TEXT_URL) +r = requests.get(TEXT_URL) +print("-" * 40) +print(r.text) +print("-" * 40) +r.close() + +print() +print("Fetching json from", JSON_URL) +r = requests.get(JSON_URL) +print("-" * 40) +print(r.json()) +print("-" * 40) +r.close() + +print("Done!") From 57b808e7efd0f81804a3f68cc6b3d9fb2eb1eb00 Mon Sep 17 00:00:00 2001 From: brentru Date: Tue, 5 May 2020 14:17:06 -0400 Subject: [PATCH 13/18] update to poll within user code instead of gsm module --- adafruit_fona/adafruit_fona_gsm.py | 28 +++++++++++----------------- examples/fona_cheerlights.py | 18 ++++++++++-------- examples/fona_simpletest.py | 4 ++++ 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/adafruit_fona/adafruit_fona_gsm.py b/adafruit_fona/adafruit_fona_gsm.py index 04d7de0..3bad7ac 100755 --- a/adafruit_fona/adafruit_fona_gsm.py +++ b/adafruit_fona/adafruit_fona_gsm.py @@ -46,24 +46,8 @@ def __init__(self, fona, apn, debug=True): self._apn = apn self._gsm_connected = False - # Attempt to enable GPS module and obtain GPS fix - attempts = 10 + # Enable GPS module self._iface.gps = True - while not self._iface.gps == 3: - if attempts < 0: - raise RuntimeError("Unable to establish GPS fix.") - attempts -= 1 - time.sleep(0.5) - - # Check if FONA is registered to GSM network - attempts = 30 - while self._iface.network_status != 1: - if attempts == 0: - raise TimeoutError("Not registered with GSM network.") - if self._debug: - print("* Not registered with network, retrying, ", attempts) - attempts -= 1 - time.sleep(1) def __enter__(self): return self @@ -81,6 +65,16 @@ def iccid(self): """Returns the SIM card's ICCID, as a string.""" return self._iface.iccid + @property + def is_attached(self): + """Returns if the modem is attached to the network + and the GPS has a fix.""" + if self._iface.gps == 3: + if self._iface.network_status == 1: + return True + return False + + @property def is_connected(self): """Returns if attached to GSM diff --git a/examples/fona_cheerlights.py b/examples/fona_cheerlights.py index 879c612..6fc17dd 100755 --- a/examples/fona_cheerlights.py +++ b/examples/fona_cheerlights.py @@ -3,12 +3,14 @@ import busio import digitalio from adafruit_fona.adafruit_fona import FONA +from adafruit_fona.adafruit_fona_gsm import GSM import adafruit_fona.adafruit_fona_socket as cellular_socket import adafruit_requests as requests import neopixel import adafruit_fancyled.adafruit_fancyled as fancy + # Get GPRS details and more from a secrets.py file try: from secrets import secrets @@ -23,18 +25,18 @@ rst = digitalio.DigitalInOut(board.D4) # Initialize FONA module (this may take a few seconds) -print("Initializing FONA") fona = FONA(uart, rst) -# Enable GPS -fona.gps = True +# Enable FONA debugging +fona._debug = True -# Bring up cellular connection -fona.configure_gprs((secrets["apn"], secrets["apn_username"], secrets["apn_password"])) +# initialize gsm +gsm = GSM(fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"])) -# Bring up GPRS -fona.gprs = True -print("FONA initialized") +while not gsm.is_connected: + print("Connecting to network...") + gsm.connect() + time.sleep(5) # Initialize a requests object with a socket and cellular interface requests.set_socket(cellular_socket, fona) diff --git a/examples/fona_simpletest.py b/examples/fona_simpletest.py index 39ca29d..ace6b02 100644 --- a/examples/fona_simpletest.py +++ b/examples/fona_simpletest.py @@ -34,6 +34,10 @@ # initialize gsm gsm = GSM(fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"])) +while not gsm.is_attached: + print("Attaching to network...") + time.sleep(0.5) + while not gsm.is_connected: print("Connecting to network...") gsm.connect() From 030c45c12677540d8d561dd80b16640efdc68f68 Mon Sep 17 00:00:00 2001 From: brentru Date: Tue, 5 May 2020 14:21:46 -0400 Subject: [PATCH 14/18] remove debug (redundant) --- adafruit_fona/adafruit_fona_gsm.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/adafruit_fona/adafruit_fona_gsm.py b/adafruit_fona/adafruit_fona_gsm.py index 3bad7ac..bd3ebb9 100755 --- a/adafruit_fona/adafruit_fona_gsm.py +++ b/adafruit_fona/adafruit_fona_gsm.py @@ -33,16 +33,14 @@ class GSM: """Interface for interacting with FONA 2G GSM modems. """ - def __init__(self, fona, apn, debug=True): + def __init__(self, fona, apn): """Initializes interface with 2G GSM modem. - :param adafruit_fona fona: Adafruit FONA module. + :param adafruit_fona fona: The Adafruit FONA module we are using. :param tuple apn: Tuple containing APN name, (optional) APN username, and APN password. - :param bool debug: Enable verbose debug output. """ self._iface = fona - self._debug = debug self._apn = apn self._gsm_connected = False @@ -69,9 +67,8 @@ def iccid(self): def is_attached(self): """Returns if the modem is attached to the network and the GPS has a fix.""" - if self._iface.gps == 3: - if self._iface.network_status == 1: - return True + if self._iface.gps == 3 and self._iface.network_status == 1: + return True return False From 8b0875461561b6a3a277fd8fdcea1213a67835d9 Mon Sep 17 00:00:00 2001 From: brentru Date: Tue, 5 May 2020 14:27:17 -0400 Subject: [PATCH 15/18] add examples --- examples/fona_aio_post.py | 4 ++++ examples/fona_cheerlights.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/examples/fona_aio_post.py b/examples/fona_aio_post.py index 2d4df41..10bf2f7 100755 --- a/examples/fona_aio_post.py +++ b/examples/fona_aio_post.py @@ -29,6 +29,10 @@ # initialize gsm gsm = GSM(fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"])) +while not gsm.is_attached: + print("Attaching to network...") + time.sleep(0.5) + while not gsm.is_connected: print("Connecting to network...") gsm.connect() diff --git a/examples/fona_cheerlights.py b/examples/fona_cheerlights.py index 6fc17dd..a717e70 100755 --- a/examples/fona_cheerlights.py +++ b/examples/fona_cheerlights.py @@ -33,6 +33,10 @@ # initialize gsm gsm = GSM(fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"])) +while not gsm.is_attached: + print("Attaching to network...") + time.sleep(0.5) + while not gsm.is_connected: print("Connecting to network...") gsm.connect() From c3eee884f7f8fea5e8bae3082e55395be49b2582 Mon Sep 17 00:00:00 2001 From: brentru Date: Tue, 5 May 2020 14:29:00 -0400 Subject: [PATCH 16/18] black, lint --- adafruit_fona/adafruit_fona.py | 5 ++--- adafruit_fona/adafruit_fona_gsm.py | 6 +++--- examples/fona_aio_post.py | 3 --- examples/fona_cheerlights.py | 3 --- examples/fona_simpletest.py | 3 --- 5 files changed, 5 insertions(+), 15 deletions(-) diff --git a/adafruit_fona/adafruit_fona.py b/adafruit_fona/adafruit_fona.py index 6ef98d9..fef508e 100644 --- a/adafruit_fona/adafruit_fona.py +++ b/adafruit_fona/adafruit_fona.py @@ -241,9 +241,9 @@ def iccid(self): if self._debug: print("\t---> AT+CCID: ") self._uart.write(b"AT+CCID\r\n") - self._read_line(timeout=2000) #6.2.23, 2sec max. response time + self._read_line(timeout=2000) # 6.2.23, 2sec max. response time iccid = self._buf.decode() - self._read_line() # eat 'OK' + self._read_line() # eat 'OK' return iccid @property @@ -434,7 +434,6 @@ def gps(self): ) return status - @gps.setter def gps(self, gps_on=False): """Sets GPS module power, parses returned buffer. diff --git a/adafruit_fona/adafruit_fona_gsm.py b/adafruit_fona/adafruit_fona_gsm.py index bd3ebb9..2c84cde 100755 --- a/adafruit_fona/adafruit_fona_gsm.py +++ b/adafruit_fona/adafruit_fona_gsm.py @@ -28,11 +28,12 @@ * Author(s): Brent Rubell """ -import time + class GSM: """Interface for interacting with FONA 2G GSM modems. """ + def __init__(self, fona, apn): """Initializes interface with 2G GSM modem. :param adafruit_fona fona: The Adafruit FONA module we are using. @@ -50,7 +51,7 @@ def __init__(self, fona, apn): def __enter__(self): return self - def __exit__(self): + def __exit__(self, exception_type, exception_value, traceback): self.disconnect() @property @@ -71,7 +72,6 @@ def is_attached(self): return True return False - @property def is_connected(self): """Returns if attached to GSM diff --git a/examples/fona_aio_post.py b/examples/fona_aio_post.py index 10bf2f7..1aa23cc 100755 --- a/examples/fona_aio_post.py +++ b/examples/fona_aio_post.py @@ -23,9 +23,6 @@ # Initialize FONA module (this may take a few seconds) fona = FONA(uart, rst) -# Enable FONA debugging -fona._debug = True - # initialize gsm gsm = GSM(fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"])) diff --git a/examples/fona_cheerlights.py b/examples/fona_cheerlights.py index a717e70..8ed164d 100755 --- a/examples/fona_cheerlights.py +++ b/examples/fona_cheerlights.py @@ -27,9 +27,6 @@ # Initialize FONA module (this may take a few seconds) fona = FONA(uart, rst) -# Enable FONA debugging -fona._debug = True - # initialize gsm gsm = GSM(fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"])) diff --git a/examples/fona_simpletest.py b/examples/fona_simpletest.py index ace6b02..8eb5e1d 100644 --- a/examples/fona_simpletest.py +++ b/examples/fona_simpletest.py @@ -28,9 +28,6 @@ # Initialize FONA module (this may take a few seconds) fona = FONA(uart, rst) -# Enable FONA debugging -fona._debug = True - # initialize gsm gsm = GSM(fona, (secrets["apn"], secrets["apn_username"], secrets["apn_password"])) From 5d6ccf11b0532e6189fadbaaf3f08f7e75f00e53 Mon Sep 17 00:00:00 2001 From: brentru Date: Wed, 6 May 2020 10:49:10 -0400 Subject: [PATCH 17/18] add uart_write, remove extra uart debugs, simplify # of debug msgs to core modules --- adafruit_fona/adafruit_fona.py | 165 +++++++++++++++------------------ 1 file changed, 75 insertions(+), 90 deletions(-) diff --git a/adafruit_fona/adafruit_fona.py b/adafruit_fona/adafruit_fona.py index fef508e..9128475 100644 --- a/adafruit_fona/adafruit_fona.py +++ b/adafruit_fona/adafruit_fona.py @@ -99,11 +99,8 @@ def __init__(self, uart, rst, debug=False): # pylint: disable=too-many-branches, too-many-statements def _init_fona(self): """Initializes FONA module.""" - self.reset() - if self._debug: - print("Attempting to open comm with ATs") timeout = 7000 while timeout > 0: while self._uart.in_waiting: @@ -115,8 +112,6 @@ def _init_fona(self): timeout -= 500 if timeout <= 0: - if self._debug: - print(" * Timeout: No response to AT. Last ditch attempt.") self._send_check_reply(CMD_AT, reply=REPLY_OK) time.sleep(0.1) self._send_check_reply(CMD_AT, reply=REPLY_OK) @@ -139,9 +134,7 @@ def _init_fona(self): self._buf = b"" self._uart.reset_input_buffer() - if self._debug: - print("\t---> ", "ATI") - self._uart.write(b"ATI\r\n") + self.uart_write(b"ATI\r\n") self._read_line(multiline=True) if self._buf.find(b"SIM808 R14") != -1: @@ -154,10 +147,8 @@ def _init_fona(self): self._fona_type = FONA_3G_E if self._fona_type == FONA_800_L: - # determine if _H - if self._debug: - print("\t ---> AT+GMM") - self._uart.write(b"AT+GMM\r\n") + # determine if SIM800H + self.uart_write(b"AT+GMM\r\n") self._read_line(multiline=True) if self._debug: print("\t <---", self._buf) @@ -168,9 +159,7 @@ def _init_fona(self): def factory_reset(self): """Resets modem to factory configuration.""" - if self._debug: - print("\t---> ATZ") - self._uart.write(b"ATZ\r\n") + self.uart_write(b"ATZ\r\n") if not self._expect_reply(REPLY_OK): return False @@ -182,7 +171,7 @@ def reset(self): """ if self._debug: - print("* Resetting modem.") + print("* Reset FONA") # Reset the module self._rst.value = True time.sleep(0.01) @@ -211,12 +200,12 @@ def version(self): @property def iemi(self): """Returns FONA module's IEMI number.""" + if self._debug: + print("FONA IEMI") self._buf = b"" self._uart.reset_input_buffer() - if self._debug: - print("\t---> ", "AT+GSN") - self._uart.write(b"AT+GSN\r\n") + self.uart_write(b"AT+GSN\r\n") self._read_line(multiline=True) iemi = self._buf[0:15] return iemi.decode("utf-8") @@ -224,10 +213,7 @@ def iemi(self): @property def local_ip(self): """Returns the local IP Address, False if not set.""" - if self._debug: - print("\t---> AT+CIFSR") - - self._uart.write(b"AT+CIFSR\r\n") + self.uart_write(b"AT+CIFSR\r\n") self._read_line() try: ip_addr = self.pretty_ip(self._buf) @@ -239,8 +225,8 @@ def local_ip(self): def iccid(self): """Returns SIM Card's ICCID number as a string.""" if self._debug: - print("\t---> AT+CCID: ") - self._uart.write(b"AT+CCID\r\n") + print("ICCID") + self.uart_write(b"AT+CCID\r\n") self._read_line(timeout=2000) # 6.2.23, 2sec max. response time iccid = self._buf.decode() self._read_line() # eat 'OK' @@ -250,8 +236,6 @@ def iccid(self): def gprs(self): """Returns module's GPRS state.""" self._read_line() - if self._debug: - print("* Check GPRS State") if not self._send_parse_reply(b"AT+CGATT?", b"+CGATT: ", ":"): return False @@ -266,9 +250,6 @@ def set_gprs(self, apn=None, enable=True): """ if enable: - if self._debug: - print("* Enabling GPRS...") - apn_name, apn_user, apn_pass = apn apn_name = apn_name.encode() @@ -310,14 +291,14 @@ def set_gprs(self, apn=None, enable=True): print("setting APN...") self._uart.reset_input_buffer() - self._uart.write(b'AT+CSTT="' + apn_name) + self.uart_write(b'AT+CSTT="' + apn_name) if apn_user is not None: - self._uart.write(b'","' + apn_user) + self.uart_write(b'","' + apn_user) if apn_pass is not None: - self._uart.write(b'","' + apn_pass) - self._uart.write(b'"\r\n') + self.uart_write(b'","' + apn_pass) + self.uart_write(b'"\r\n') if not self._get_reply(REPLY_OK): return False @@ -347,10 +328,7 @@ def set_gprs(self, apn=None, enable=True): # Query local IP if not self.local_ip: return False - else: - if self._debug: - print("* Disabling GPRS...") # reset PDP state if not self._send_check_reply( b"AT+CIPSHUT", reply=b"SHUT OK", timeout=20000 @@ -362,6 +340,8 @@ def set_gprs(self, apn=None, enable=True): @property def network_status(self): """Returns cellular/network status""" + if self._debug: + print("Network status") if not self._send_parse_reply(b"AT+CREG?", b"+CREG: ", idx=1): return False if self._buf == 0: @@ -388,6 +368,8 @@ def network_status(self): @property def rssi(self): """Returns cellular network's Received Signal Strength Indicator (RSSI).""" + if self._debug: + print("RSSI") if not self._send_parse_reply(b"AT+CSQ", b"+CSQ: "): return False @@ -409,9 +391,9 @@ def rssi(self): @property def gps(self): - """Returns the GPS status.""" + """Returns the GPS fix.""" if self._debug: - print("* GPS Status") + print("GPS Fix") if self._fona_type == FONA_808_V2: # 808 V2 uses GNS commands and doesn't have an explicit 2D/3D fix status. # Instead just look for a fix and if found assume it's a 3D fix. @@ -483,14 +465,12 @@ def get_host_by_name(self, hostname): :param str hostname: Destination server. """ - self._read_line() if self._debug: - print("*** get_host_by_name: ", hostname) + print("*** Get host by name") + self._read_line() if isinstance(hostname, str): hostname = bytes(hostname, "utf-8") - if self._debug: - print("\t---> AT+CDNSGIP=", hostname) if not self._send_check_reply( b'AT+CDNSGIP="' + hostname + b'"\r\n', reply=REPLY_OK ): @@ -521,9 +501,9 @@ def get_socket(self): """ if self._debug: - print("*** Allocating Socket") + print("*** Get socket") - self._uart.write(b"AT+CIPSTATUS\r\n") + self.uart_write(b"AT+CIPSTATUS\r\n") self._read_line(100) # OK self._read_line(100) # table header @@ -538,6 +518,8 @@ def get_socket(self): # read out the rest of the responses for _ in range(allocated_socket, FONA_MAX_SOCKETS): self._read_line(100) + if self._debug: + print("Allocated socket #%d"%allocated_socket) return allocated_socket def remote_ip(self, sock_num): @@ -549,7 +531,7 @@ def remote_ip(self, sock_num): sock_num < FONA_MAX_SOCKETS ), "Provided socket exceeds the maximum number of \ sockets for the FONA module." - self._uart.write(b"AT+CIPSTATUS=" + str(sock_num).encode() + b"\r\n") + self.uart_write(b"AT+CIPSTATUS=" + str(sock_num).encode() + b"\r\n") self._read_line(100) self._parse_reply(b"+CIPSTATUS:", idx=3) @@ -624,6 +606,13 @@ def socket_connect(self, sock_num, dest, port, conn_mode=TCP_MODE): :param int conn_mode: Connection mode (TCP/UDP) """ + if self._debug: + print( + "*** Socket connect, protocol={}, port={}, ip={}".format( + conn_mode, port, dest + ) + ) + self._uart.reset_input_buffer() assert ( sock_num < FONA_MAX_SOCKETS @@ -638,25 +627,19 @@ def socket_connect(self, sock_num, dest, port, conn_mode=TCP_MODE): ) # Query local IP Address - if self._debug: - print("\t---> AT+CIFSR") - self._uart.write(b"AT+CIFSR\r\n") + self.uart_write(b"AT+CIFSR\r\n") self._read_line() # Start connection - self._uart.write(b"AT+CIPSTART=") - self._uart.write(str(sock_num).encode()) + self.uart_write(b"AT+CIPSTART=") + self.uart_write(str(sock_num).encode()) if conn_mode == 0: - if self._debug: - print('\t--->AT+CIPSTART="TCP","{}",{}'.format(dest, port)) - self._uart.write(b',"TCP","') + self.uart_write(b',"TCP","') else: - if self._debug: - print('\t--->AT+CIPSTART="UDP","{}",{}'.format(dest, port)) - self._uart.write(b',"UDP","') - self._uart.write(dest.encode() + b'","') - self._uart.write(str(port).encode() + b'"') - self._uart.write(b"\r\n") + self.uart_write(b',"UDP","') + self.uart_write(dest.encode() + b'","') + self.uart_write(str(port).encode() + b'"') + self.uart_write(b"\r\n") if not self._expect_reply(REPLY_OK): return False @@ -672,14 +655,16 @@ def socket_close(self, sock_num, quick_close=1): :param int quick_close: Quickly or slowly close the socket. Enabled by default """ + if self._debug: + print("*** Closing socket #%d" % sock_num) assert ( sock_num < FONA_MAX_SOCKETS ), "Provided socket exceeds the maximum number of \ sockets for the FONA module." self._read_line() - self._uart.write(b"AT+CIPCLOSE=" + str(sock_num).encode() + b",") - self._uart.write(str(quick_close).encode() + b"\r\n") + self.uart_write(b"AT+CIPCLOSE=" + str(sock_num).encode() + b",") + self.uart_write(str(quick_close).encode() + b"\r\n") self._read_line() if not self._parse_reply(b"CLOSE OK", idx=0): return False @@ -699,11 +684,11 @@ def socket_read(self, sock_num, length): self._read_line() if self._debug: print("* socket read") - print("\t ---> AT+CIPRXGET=2,{},{}".format(sock_num, length)) - self._uart.write(b"AT+CIPRXGET=2,") - self._uart.write(str(sock_num).encode()) - self._uart.write(b",") - self._uart.write(str(length).encode() + b"\r\n") + + self.uart_write(b"AT+CIPRXGET=2,") + self.uart_write(str(sock_num).encode()) + self.uart_write(b",") + self.uart_write(str(length).encode() + b"\r\n") self._read_line() @@ -727,13 +712,10 @@ def socket_write(self, sock_num, buffer): ), "Provided socket exceeds the maximum number of \ sockets for the FONA module." - if self._debug: - print("\t--->AT+CIPSEND={},{}".format(sock_num, len(buffer))) - self._uart.reset_input_buffer() - self._uart.write(b"AT+CIPSEND=" + str(sock_num).encode()) - self._uart.write(b"," + str(len(buffer)).encode()) - self._uart.write(b"\r\n") + self.uart_write(b"AT+CIPSEND=" + str(sock_num).encode()) + self.uart_write(b"," + str(len(buffer)).encode()) + self.uart_write(b"\r\n") self._read_line() if self._debug: @@ -743,7 +725,7 @@ def socket_write(self, sock_num, buffer): # promoting mark ('>') not found return False - self._uart.write(buffer + b"\r\n") + self.uart_write(buffer + b"\r\n") self._read_line(3000) if self._debug: @@ -756,6 +738,21 @@ def socket_write(self, sock_num, buffer): ### UART Reply/Response Helpers ### + def uart_write(self, buffer): + """UART ``write`` with optional debug that prints + the buffer before sending. + :param bytes buffer: Buffer of bytes to send to the bus. + + """ + #out_buffer_str = ", ".join([hex(i) for i in buffer]) + if self._debug: + print("\tUARTWRITE ::", buffer.decode()) + self._uart.write(buffer) + + def uart_read(self): + # TODO! + pass + def _send_parse_reply(self, send_data, reply_data, divider=",", idx=0): """Sends data to FONA module, parses reply data returned. :param bytes send_data: Data to send to the module. @@ -780,13 +777,9 @@ def _get_reply( self._uart.reset_input_buffer() if data is not None: - if self._debug: - print("\t---> ", data) - self._uart.write(data + "\r\n") + self.uart_write(data + b"\r\n") else: - if self._debug: - print("\t---> {}{}".format(prefix, suffix)) - self._uart.write(prefix + suffix + b"\r\n") + self.uart_write(prefix + suffix + b"\r\n") line = self._read_line(timeout) @@ -912,18 +905,10 @@ def _get_reply_quoted(self, prefix, suffix, timeout): """ self._uart.reset_input_buffer() - if self._debug: - print("\t---> ", end="") - print(prefix, end="") - print('""', end="") - print(suffix, end="") - print('""') - - self._uart.write(prefix + b'"') - self._uart.write(suffix + b'"\r\n') + self.uart_write(prefix + b'"') + self.uart_write(suffix + b'"\r\n') line = self._read_line(timeout) - if self._debug: print("\t<--- ", self._buf) From 0a194ec56d54c1890781704ff560b1c884c75748 Mon Sep 17 00:00:00 2001 From: brentru Date: Wed, 6 May 2020 11:27:26 -0400 Subject: [PATCH 18/18] cleanup uart read into readline --- adafruit_fona/adafruit_fona.py | 37 ++++++---------------------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/adafruit_fona/adafruit_fona.py b/adafruit_fona/adafruit_fona.py index 9128475..3c6b498 100644 --- a/adafruit_fona/adafruit_fona.py +++ b/adafruit_fona/adafruit_fona.py @@ -150,8 +150,6 @@ def _init_fona(self): # determine if SIM800H self.uart_write(b"AT+GMM\r\n") self._read_line(multiline=True) - if self._debug: - print("\t <---", self._buf) if self._buf.find(b"SIM800H") != -1: self._fona_type = FONA_800_H @@ -287,8 +285,6 @@ def set_gprs(self, apn=None, enable=True): ) # send AT+CSTT,"apn","user","pass" - if self._debug: - print("setting APN...") self._uart.reset_input_buffer() self.uart_write(b'AT+CSTT="' + apn_name) @@ -481,8 +477,6 @@ def get_host_by_name(self, hostname): while not self._parse_reply(b"+CDNSGIP:", idx=2): self._read_line() - if self._debug: - print("\t<--- ", self._buf) return self._buf def pretty_ip(self, ip): # pylint: disable=no-self-use, invalid-name @@ -519,7 +513,7 @@ def get_socket(self): for _ in range(allocated_socket, FONA_MAX_SOCKETS): self._read_line(100) if self._debug: - print("Allocated socket #%d"%allocated_socket) + print("Allocated socket #%d" % allocated_socket) return allocated_socket def remote_ip(self, sock_num): @@ -535,8 +529,6 @@ def remote_ip(self, sock_num): self._read_line(100) self._parse_reply(b"+CIPSTATUS:", idx=3) - if self._debug: - print("\t<--- ", self._buf) return self._buf def socket_status(self, sock_num): @@ -553,8 +545,6 @@ def socket_status(self, sock_num): # eat the 'STATE: ' message self._read_line() - if self._debug: - print("\t<--- ", self._buf) # read "C: " for each active connection for state in range(0, sock_num + 1): @@ -718,9 +708,6 @@ def socket_write(self, sock_num, buffer): self.uart_write(b"\r\n") self._read_line() - if self._debug: - print("\t<--- ", self._buf) - if self._buf[0] != 62: # promoting mark ('>') not found return False @@ -728,9 +715,6 @@ def socket_write(self, sock_num, buffer): self.uart_write(buffer + b"\r\n") self._read_line(3000) - if self._debug: - print("\t<--- ", self._buf) - if "SEND OK" not in self._buf.decode(): return False @@ -744,15 +728,10 @@ def uart_write(self, buffer): :param bytes buffer: Buffer of bytes to send to the bus. """ - #out_buffer_str = ", ".join([hex(i) for i in buffer]) if self._debug: print("\tUARTWRITE ::", buffer.decode()) self._uart.write(buffer) - def uart_read(self): - # TODO! - pass - def _send_parse_reply(self, send_data, reply_data, divider=",", idx=0): """Sends data to FONA module, parses reply data returned. :param bytes send_data: Data to send to the module. @@ -782,9 +761,6 @@ def _get_reply( self.uart_write(prefix + suffix + b"\r\n") line = self._read_line(timeout) - - if self._debug: - print("\t<--- ", self._buf) return line def _parse_reply(self, reply, divider=",", idx=0): @@ -813,7 +789,8 @@ def _parse_reply(self, reply, divider=",", idx=0): return True def _read_line(self, timeout=FONA_DEFAULT_TIMEOUT_MS, multiline=False): - """Reads one or multiple lines into the buffer. + """Reads one or multiple lines into the buffer. Optionally prints the buffer + after reading. :param int timeout: Time to wait for UART serial to reply, in seconds. :param bool multiline: Read multiple lines. @@ -848,6 +825,9 @@ def _read_line(self, timeout=FONA_DEFAULT_TIMEOUT_MS, multiline=False): timeout -= 1 time.sleep(0.001) + if self._debug: + print("\tUARTREAD ::", self._buf.decode()) + return reply_idx def _send_check_reply( @@ -909,9 +889,6 @@ def _get_reply_quoted(self, prefix, suffix, timeout): self.uart_write(suffix + b'"\r\n') line = self._read_line(timeout) - if self._debug: - print("\t<--- ", self._buf) - return line def _expect_reply(self, reply, timeout=10000): @@ -920,8 +897,6 @@ def _expect_reply(self, reply, timeout=10000): """ self._read_line(timeout) - if self._debug: - print("\t<--- ", self._buf) if reply not in self._buf: return False return True