Description
I am writing an application on a PyPortal that needs to talk to two access points. After connecting to the first and communicating via socket with a host there, I close the socket and do an esp.disconnect() to disconnect from the access point. This seems to work. The esp's status goes to WL_DISCONNECTED.
I then connect to the second access point with the esp's status going back to WL_CONNECTED. But, at that point the ESP seems to be in a bad state. Any function requiring communication with the AP fails, in one way or another. I wrote a simple test case that demonstrates the problem:
import board
from digitalio import DigitalInOut
import busio
import sys
from adafruit_esp32spi import adafruit_esp32spi
from adafruit_esp32spi import adafruit_esp32spi_socket
esp32_cs_pin = DigitalInOut(board.ESP_CS)
esp32_ready_pin = DigitalInOut(board.ESP_BUSY)
esp32_reset_pin = DigitalInOut(board.ESP_RESET)
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs_pin, esp32_ready_pin, esp32_reset_pin)
ssids = (('linkme', '***********'), ('corrigan', '**********'))
fwstr = str(esp.firmware_version, 'utf-8')
major, minor, sub = sys.implementation.version
print("Circuitpython {}.{}.{}, ESP32 firmware {}".format(major, minor, sub, fwstr))
test_ip = esp.unpretty_ip('192.168.43.148')
port = 65432
for ssid, password in ssids:
print("Testing ssid = {}".format(ssid))
# connect to an AP
esp.connect_AP(ssid, password, timeout_s = 30)
print("Create socket")
# create a socket
adafruit_esp32spi_socket.set_interface(esp)
my_sock = adafruit_esp32spi_socket.socket()
print("Connect socket")
# connect to socket server on other system
my_sock.connect((test_ip, port))
print("close socket")
# close socket
my_sock.close()
print("disconnect from AP")
# disconnect from AP
esp.disconnect()
The output is:
code.py output:
Circuitpython 6.1.0, ESP32 firmware 1.7.3
Testing ssid = linkme
Create socket
Connect socket
close socket
disconnect from AP
Testing ssid = corrigan
Create socket
Connect socket
Traceback (most recent call last):
File "code.py", line 35, in <module>
File "adafruit_esp32spi/adafruit_esp32spi_socket.py", line 75, in connect
File "/lib/adafruit_esp32spi/adafruit_esp32spi.py", line 783, in socket_connect
File "/lib/adafruit_esp32spi/adafruit_esp32spi.py", line 685, in socket_open
File "/lib/adafruit_esp32spi/adafruit_esp32spi.py", line 345, in _send_command_get_response
File "/lib/adafruit_esp32spi/adafruit_esp32spi.py", line 296, in _wait_response_cmd
File "/lib/adafruit_esp32spi/adafruit_esp32spi.py", line 199, in _wait_for_ready
RuntimeError: ESP32 not responding
Code done running.
Everything works for the first access point, and seems to work for the second until the attempt to connect the socket. It also fails in the same way if I re-connect to the first access point instead. Other functions also fail. For example, esp.get_host_by_name(host) fails with RuntimeError: Failed to request hostname.
A more elaborate test shows that after connecting to the second access point, the ESP32's internal state is inconsistent. Asking the ESP32 for the bssid and ssid that it is connected to show the second access point, but asking the ESP32 for its 'network data', gives back the ip address, and gateway of the first access point.
If I change the timeout in _wait_for_ready, I find that the socket.connect request actual finishes after 18 seconds (waiting for the response, the normal timeout is 10). But, when the socket.connect finishes, it is with a RuntimeError: Expected 01 but got 00, which is what you get when the connection fails.
If you do an esp.reset(), everything works again.
Activity
mikejc58 commentedon Apr 9, 2021
The more elaborate test and its output:
elaborate.txt
anecdata commentedon Sep 15, 2021
ESP32SPI
.disconnect
simply callsdisconnect
in the NINA firmware, which uses the Arduino functionWiFi.disconnect()
. Similarly, ESP32SPI.connect
calls into NINA, which does aWiFi.begin(ssid, pass)
. So I think we'd have to dig into the ESP32 implementation of the Arduino functions.I suspect WiFiNINA would behave similarly, it would be interesting if it was different.
If you can incur the <1 second penalty of
esp.reset()
, that's probably the best workaround.