Skip to content

PyPortal class requires the 'esp' parameter, which defaults to 'None' #110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
uxp opened this issue Apr 11, 2021 · 3 comments
Closed

PyPortal class requires the 'esp' parameter, which defaults to 'None' #110

uxp opened this issue Apr 11, 2021 · 3 comments
Assignees

Comments

@uxp
Copy link

uxp commented Apr 11, 2021

I'm opening this as an issue before attempting a fix.

tl;dr: the object PyPortal doesnt properly initialize the network before attempting to use it. Should this be fixed?

I've been going through some of the examples on the Adafruit Learn site with the PyPortal and I keep on running into an issue resulting in a stacktrace when instantiating a PyPortal object like this:

Traceback (most recent call last):
  File "code.py", line 12, in <module>
  File "adafruit_pyportal/__init__.py", line 152, in __init__
  File "adafruit_pyportal/network.py", line 90, in __init__
  File "adafruit_pyportal/wifi.py", line 77, in __init__
AttributeError: 'NoneType' object has no attribute 'is_connected'

Taken from https://github.com/adafruit/Adafruit_Learning_System_Guides/blob/master/PyPortal_Quotes/quote.py

This can be attributed to the line if self.esp.is_connected: in the constructor of WiFi:

    def __init__(self, *, status_neopixel=None, esp=None, external_spi=None):
        [...omitted...]

        if external_spi:  # If SPI Object Passed
            spi = external_spi
        else:  # Else: Make ESP32 connection
            spi = busio.SPI(board.SCK, board.MOSI, board.MISO)

        if esp:  # If there was a passed ESP Object
            self.esp = esp
        else:
            esp32_ready = DigitalInOut(board.ESP_BUSY)
            esp32_gpio0 = DigitalInOut(board.ESP_GPIO0)
            esp32_reset = DigitalInOut(board.ESP_RESET)
            esp32_cs = DigitalInOut(board.ESP_CS)

            self.esp = adafruit_esp32spi.ESP_SPIcontrol(
                spi, esp32_cs, esp32_ready, esp32_reset, esp32_gpio0
            )

        requests.set_socket(socket, self.esp)
        if self.esp.is_connected:
            self.requests = requests

which is called from Network's constructor:

    def __init__(
        self,
        *,
        status_neopixel=None,
        esp=None,
        external_spi=None,
        [...omitted...]
    ):
        wifi = WiFi(status_neopixel=status_neopixel, esp=esp, external_spi=external_spi)

which is called from the PyPortal constructor:


    # pylint: disable=too-many-instance-attributes, too-many-locals, too-many-branches, too-many-statements
    def __init__(
        self,
        [...omitted...]
        esp=None,
        external_spi=None,
        [...omitted...]
    ):

        [...omitted...]

        if external_spi:  # If SPI Object Passed
            spi = external_spi
        else:  # Else: Make ESP32 connection
            spi = board.SPI()

        [...omitted...]

        network = Network(
            status_neopixel=status_neopixel,
            esp=esp,
            external_spi=spi,

So, the ultimate reason for the error is because the default constructor for PyPortal propagates a None value for the esp parameter into Network, which propagates it to WiFi, which seemingly instantiates it correctly, but doesnt give time for the esp object to properly connect to the network.

An additional Learn article, https://learn.adafruit.com/adafruit-pyportal/internet-connect, lists a more complete example of connecting to a wireless network for the PyPortal product, which is copied here:

requests.set_socket(socket, esp)

if esp.status == adafruit_esp32spi.WL_IDLE_STATUS:
    print("ESP32 found and in idle mode")
print("Firmware vers.", esp.firmware_version)
print("MAC addr:", [hex(i) for i in esp.MAC_address])
 
for ap in esp.scan_networks():
    print("\t%s\t\tRSSI: %d" % (str(ap["ssid"], "utf-8"), ap["rssi"]))
 
print("Connecting to AP...")
while not esp.is_connected:
    try:
        esp.connect_AP(secrets["ssid"], secrets["password"])
    except RuntimeError as e:
        print("could not connect to AP, retrying: ", e)
        continue
print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi)
print("My IP address is", esp.pretty_ip(esp.ip_address))

which simply checks for the ESP's status and will attempt to connect to the SSID with the given secrets as per the standard secrets.py file containing the secrets dictionary.

There seems to be two possible solutions here:

  1. Update the WiFi class' constructor to wait for the network before continuing as per the PyPortal Learn guide on connecting to a network
  2. Update the PyPortal constructor arguments to require a manually configured esp object that has already been configured and connected to the network.

The first seems more user friendly and would allow people to continue to use the vast amounts of example code that already construct a PyPortal object without setting up a network connection, though the second option appears like a less invasive approach, though would continue to make all the example code on the Learn site and github broken by default.

@uxp
Copy link
Author

uxp commented Apr 11, 2021

An interesting observation I just made: Swapping the library for the source .py version on my PyPortal device does not exhibit this issue. Keeping the "compiled" .mpy version of the library does show this issue.

I've also tried various firmware versions, such as adafruit-circuitpython-pyportal-en_US-7.0.0-alpha.1 with the library as taken from adafruit-circuitpython-bundle-7.x-mpy-20210410.zip, as well as adafruit-circuitpython-pyportal-en_US-6.2.0 with the libraries taken from adafruit-circuitpython-bundle-6.x-mpy-20210409.zip, which all exhibit this issue.

Maybe this isn't as clear as waiting for the network to initialize, as I'm a bit more confused why the source version behaves differently now.

@Craigxyzzy
Copy link

Issue resolved:
forums.adafruit.com/viewtopic.php?t=177959
forums.adafruit.com/viewtopic.php?t=177980

@makermelissa
Copy link
Collaborator

Fixed via #109. Thank you @Neradoc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants