Skip to content

wrong connection error messages from connect_AP method #189

Open
@mMerlin

Description

@mMerlin

ESP_SPIcontrol.connect_AP is not reporting connection failures correctly for the actual failure causes. Testing is pointing to a possible failure or limitation in the NINA fw. The problem could also be the way, and which, commands are being sent to the fw.

Observed:
When specifying an SSID that exists, but using the wrong password, connect_AP reports 'No such ssid'.
When specifying an SSID that does not exist, connect_AP reports 'Failed to connect to ssid'

Expected:
The reverse.

To demonstrate, I took a copy of the simple_test.py example, cut it down, modified it, and added to it. As part of that, I created a subclass of ESP_SPIcontrol to add a testing version of connect_AP. That version monitors and reports the status changes more closely, but raises the same exceptions. The result is below, along with the output I get. In that, I noticed that the status never goes back to idle.

# SPDX-FileCopyrightText: 2024 µMerlin
# SPDX-License-Identifier: MIT

"""testing connection failure statuses"""

import sys
import os
import time
# pylint:disable=import-error
import board
import busio
from digitalio import DigitalInOut
# pylint:enable=import-error
from adafruit_esp32spi import adafruit_esp32spi as wl_const
from adafruit_esp32spi.adafruit_esp32spi import ESP_SPIcontrol

class ExtendedSPIControl(ESP_SPIcontrol):
    """Override individual method for testing"""

    def testing_connect(self, ssid:str, password:str, timeout_s:int=10) -> int:
        """emulate connect_AP() to add debug tracing"""
        print(f'{type(self).__name__ =} in testing version of connect_ap()')
        stat_changes = []
        time_pt0 = time.monotonic()
        stat_changes.append((self.status, time_pt0))
        if isinstance(ssid, str):
            ssid = bytes(ssid, "utf-8")
        if password:
            if isinstance(password, str):
                password = bytes(password, "utf-8")
            self.wifi_set_passphrase(ssid, password)
            time_pt1 = time.monotonic()
        else:
            self.wifi_set_network(ssid)
            time_pt1 = time.monotonic()
        stat_changes.append((self.status, time_pt1))
        while (time.monotonic() - time_pt1) < timeout_s:  # wait until connected or timeout
            stat = self.status
            if stat != stat_changes[-1][0]:
                stat_changes.append((stat, time.monotonic()))
            if stat == wl_const.WL_CONNECTED:
                break
            time.sleep(0.05)
        stat_changes.append((stat, time.monotonic()))
        print(f'set_passphrase elapsed: {time_pt1 - time_pt0}')
        prev_tm = time_pt0
        for idx, stamp in enumerate(stat_changes):
            print(f'{idx:3}: {stamp[0]} at {stamp[1] - prev_tm}')
            prev_tm = stamp[1]
        if stat in (wl_const.WL_CONNECT_FAILED, wl_const.WL_CONNECTION_LOST,
                    wl_const.WL_DISCONNECTED):
            raise ConnectionError("Failed to connect to ssid", ssid)
        if stat == wl_const.WL_NO_SSID_AVAIL:
            raise ConnectionError("No such ssid", ssid)
        if stat != wl_const.WL_CONNECTED:
            raise OSError(f"Unknown error {stat:02X}")
        return stat


secrets = {
    "ssid": os.getenv("CIRCUITPY_WIFI_SSID"),
    "password": os.getenv("CIRCUITPY_WIFI_PASSWORD"),
}
if secrets == {"ssid": None, "password": None}:
    raise OSError('no credentials found in settings.toml')

print("ESP32 SPI AP connection test")
print(f"  Board ID: {getattr(board, 'board_id', 'Unknown')}")
print(f'  Implementation: {sys.implementation}')
print(f'  Platform: {sys.platform}')

# If you are using a board with pre-defined ESP32 Pins:
esp32_cs = DigitalInOut(board.ESP_CS)
esp32_ready = DigitalInOut(board.ESP_BUSY)
esp32_reset = DigitalInOut(board.ESP_RESET)

# Secondary (SCK1) SPI used to connect to WiFi board on Arduino Nano Connect RP2040
if "SCK1" in dir(board):
    spi = busio.SPI(board.SCK1, board.MOSI1, board.MISO1)
else:
    spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = ExtendedSPIControl(spi, esp32_cs, esp32_ready, esp32_reset)

if esp.status == wl_const.WL_IDLE_STATUS:
    print("ESP32 found and in idle mode")
print(f"  Firmware vers. {esp.firmware_version.decode('utf-8'):11}")
MAC = ':'.join(f'{byte:02X}' for byte in esp.MAC_address)
print(f"  MAC addr: {MAC}")

print("Connecting to AP...")
esp.connect_AP(secrets["ssid"], secrets["password"])
print(f'{esp.status =}')
print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi)
print("My IP address is", esp.pretty_ip(esp.ip_address))

esp.disconnect()
time.sleep(1.0)

try:
    esp.connect_AP(secrets["ssid"], "BAD PASSWORD")
except ConnectionError as exc:
    print(f'Error when SSID exists, but using wrong password: {exc}')
print(f'{esp.status =}')
time.sleep(1.0)

try:
    esp.connect_AP("NO SUCH SSID HERE", secrets["password"])
except ConnectionError as exc:
    print(f'Error when SSID does not exist: {exc}')
print(f'{esp.status =}')
time.sleep(1.0)

esp.connect_AP(secrets["ssid"], secrets["password"])
print(f'{esp.status =}')
print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi)
print("My IP address is", esp.pretty_ip(esp.ip_address))
esp.disconnect()
time.sleep(1.0)

print("\nrepeat with test version of connect_AP\n")

print("Connecting to AP...")
esp.testing_connect(secrets["ssid"], secrets["password"])
print(f'{esp.status =}')
print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi)
print("My IP address is", esp.pretty_ip(esp.ip_address))

esp.disconnect()
time.sleep(1.0)

try:
    esp.testing_connect(secrets["ssid"], "BAD PASSWORD")
except ConnectionError as exc:
    print(f'Error when SSID exists, but using wrong password: {exc}')
print(f'{esp.status =}')
time.sleep(1.0)

try:
    esp.testing_connect("NO SUCH SSID HERE", secrets["password"])
except ConnectionError as exc:
    print(f'Error when SSID does not exist: {exc}')
print(f'{esp.status =}')
time.sleep(1.0)

esp.testing_connect(secrets["ssid"], secrets["password"])
print(f'{esp.status =}')
print("Connected to", str(esp.ssid, "utf-8"), "\tRSSI:", esp.rssi)
print("My IP address is", esp.pretty_ip(esp.ip_address))

print("Done!")
main.py output:
ESP32 SPI AP connection test
  Board ID: pyportal
  Implementation: (name='circuitpython', version=(8, 2, 10), mpy=517)
  Platform: MicroChip SAMD51
ESP32 found and in idle mode
  Firmware vers. 1.7.7     
  MAC addr: 14:48:57:12:CF:A4
Connecting to AP...
esp.status =3
Connected to Dungeon 	RSSI: -44
My IP address is 192.168.2.25
Error when SSID exists, but using wrong password: ('No such ssid', b'Dungeon')
esp.status =1
Error when SSID does not exist: ('Failed to connect to ssid', b'NO SUCH SSID HERE')
esp.status =4
esp.status =3
Connected to Dungeon 	RSSI: -45
My IP address is 192.168.2.25

repeat with test version of connect_AP

Connecting to AP...
type(self).__name__ =ExtendedSPIControl in testing version of connect_ap()
set_passphrase elapsed: 0.193848
  0: 6 at 0.0
  1: 1 at 0.193848
  2: 3 at 1.98193
  3: 3 at 0.0
esp.status =3
Connected to Dungeon 	RSSI: -45
My IP address is 192.168.2.25
type(self).__name__ =ExtendedSPIControl in testing version of connect_ap()
set_passphrase elapsed: 0.217285
  0: 6 at 0.0
  1: 1 at 0.217285
  2: 1 at 10.0059
Error when SSID exists, but using wrong password: ('No such ssid', b'Dungeon')
esp.status =1
type(self).__name__ =ExtendedSPIControl in testing version of connect_ap()
set_passphrase elapsed: 0.20166
  0: 1 at 0.0
  1: 6 at 0.20166
  2: 4 at 1.93311
  3: 4 at 8.07324
Error when SSID does not exist: ('Failed to connect to ssid', b'NO SUCH SSID HERE')
esp.status =4
type(self).__name__ =ExtendedSPIControl in testing version of connect_ap()
set_passphrase elapsed: 0.222168
  0: 4 at 0.0
  1: 1 at 0.222168
  2: 3 at 2.13477
  3: 3 at 0.0
esp.status =3
Connected to Dungeon 	RSSI: -45
My IP address is 192.168.2.25
Done!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions