Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
a63118b
Fix double subtraction of bytes_read from num_to_read in recv_into
us3r64 Jan 15, 2024
c69b6f2
Merge pull request #183 from us3r64/fix/socket-recv_into
dhalbert Jan 15, 2024
e4583c6
Fix potential socket data read already into self._buffer
us3r64 Jan 15, 2024
e119719
Fix formatting
us3r64 Jan 15, 2024
0adb75b
Merge pull request #185 from us3r64/fix/recv_into-self-_buffer
dhalbert Jan 15, 2024
a9129c9
Remove legacy requests.set_socket
justmobilize Feb 23, 2024
3042d19
Merge pull request #188 from justmobilize/remove-set-socket
tannewt Feb 23, 2024
5b06d23
Update README.rst
tannewt Feb 27, 2024
ad7816b
Merge pull request #191 from adafruit/tannewt-patch-1
FoamyGuy Feb 28, 2024
d310104
tweak output formatting
mMerlin Feb 29, 2024
817d50e
Fix README requirements
justmobilize Feb 29, 2024
f1f9251
Update README.rst
dhalbert Feb 29, 2024
1788f98
Merge pull request #193 from justmobilize/fix-readme-requirements
dhalbert Feb 29, 2024
4b33ddd
Merge pull request #192 from mMerlin/new-sample
dhalbert Mar 15, 2024
57ade90
add __enter__ & __exit__
anecdata Mar 16, 2024
956d6a0
Merge pull request #196 from anecdata/exit
dhalbert Mar 19, 2024
35fd193
Convert to SocketPool
justmobilize Apr 24, 2024
06281a5
Use standard super() for __new__
justmobilize Apr 26, 2024
a48d516
Merge pull request #198 from justmobilize/convert-to-socketpool
dhalbert Apr 30, 2024
8349d77
Fully deprecate `secrets` and improve native wifi API compatibility.
anecdata Apr 30, 2024
a589e81
ad `mac_address` and fix `connect` per discussion. `connect` with `se…
anecdata May 2, 2024
cfb61f5
Update adafruit_esp32spi/adafruit_esp32spi.py
anecdata May 2, 2024
e69c950
Update adafruit_esp32spi/adafruit_esp32spi.py
anecdata May 2, 2024
5a64044
add documentation for `.connect(secrets_dict)`.
dhalbert May 3, 2024
42dc7f5
remove extraneous whitespace
dhalbert May 3, 2024
69b6e97
Merge pull request #199 from anecdata/connect
dhalbert May 3, 2024
40b51da
socket.connect(): Auto-select UDP_MODE
jepler May 6, 2024
86ffa6b
socketpool: don't use "is" to compare numbers
jepler May 6, 2024
56864ac
Merge pull request #201 from adafruit/connect_dgram_mode
FoamyGuy May 6, 2024
380a242
Add sendto
justmobilize May 15, 2024
69fac99
Merge pull request #202 from justmobilize/add-sendto
dhalbert May 15, 2024
3575a0e
Remove SocketPoolContants
justmobilize May 20, 2024
098c0b3
Merge pull request #205 from justmobilize/remove-socket-pool-contants
dhalbert May 20, 2024
b160267
Remove timeout Exception
justmobilize May 21, 2024
deb2a5f
Merge pull request #206 from justmobilize/remove-timeout-exception
dhalbert May 21, 2024
30d73e0
Clear error for only one SSL socket open at a time
justmobilize May 23, 2024
7bf8763
Merge pull request #207 from justmobilize/only-one-ssl-error
dhalbert May 24, 2024
04b7950
Add ap_info
justmobilize Jun 12, 2024
c4dfdf5
Update adafruit_esp32spi/adafruit_esp32spi.py
justmobilize Jun 23, 2024
2da9843
Rename from ESP_Network to Network
justmobilize Jun 23, 2024
e7e129a
Merge pull request #209 from justmobilize/add-ap-info
dhalbert Jun 24, 2024
5f388a6
really remove examples/server/esp32spi_wsgiserver.py
dhalbert Sep 29, 2024
d82a300
really, really remove wsgi server example; update pylint exceptions
dhalbert Sep 29, 2024
152f3c0
create non-empty esp32spi_wsgiserver.py so we can delete it!
dhalbert Sep 29, 2024
b82b7cc
Merge pull request #212 from dhalbert/wsgi-server-example
dhalbert Sep 29, 2024
91f0dfe
try to delete zombie file
dhalbert Sep 29, 2024
a2a2aee
Merge pull request #213 from dhalbert/zap-file
dhalbert Sep 29, 2024
99fd76d
remove deprecated get_html_theme_path() call
FoamyGuy Oct 7, 2024
71a07cc
add sphinx configuration to rtd.yaml
FoamyGuy Jan 14, 2025
5dfafb6
WiFiManager updates
justmobilize Feb 19, 2025
3584737
Apply suggestions from code review
justmobilize Feb 21, 2025
78c5cfa
Fix typo
justmobilize Feb 21, 2025
508935e
Fix lint error
justmobilize Feb 21, 2025
45705df
status_light -> status_pixel everywhere, to match now required keywor…
dhalbert Feb 22, 2025
4d0a107
Merge pull request #216 from justmobilize/wifi-manager-update
dhalbert Feb 22, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/pycqa/pylint
rev: v2.17.4
rev: v3.3.1
hooks:
- id: pylint
name: pylint (library code)
types: [python]
args:
- --disable=consider-using-f-string
- --disable=consider-using-f-string,too-many-arguments,too-many-positional-arguments
exclude: "^(docs/|examples/|tests/|setup.py$)"
- id: pylint
name: pylint (example code)
Expand Down
3 changes: 3 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
# Required
version: 2

sphinx:
configuration: docs/conf.py

build:
os: ubuntu-20.04
tools:
Expand Down
4 changes: 3 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ Dependencies
This driver depends on:

* `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_
* `Bus Device <https://github.com/adafruit/Adafruit_CircuitPython_BusDevice>`_
* `Adafruit Bus Device <https://github.com/adafruit/Adafruit_CircuitPython_BusDevice>`_
* `Adafruit CircuitPython ConnectionManager <https://github.com/adafruit/Adafruit_CircuitPython_ConnectionManager/>`_
* `Adafruit CircuitPython Requests <https://github.com/adafruit/Adafruit_CircuitPython_Requests/>`_

Please ensure all dependencies are available on the CircuitPython filesystem.
This is easily achieved by downloading
Expand Down
187 changes: 145 additions & 42 deletions adafruit_esp32spi/adafruit_esp32spi.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

import struct
import time
import warnings
from micropython import const
from adafruit_bus_device.spi_device import SPIDevice
from digitalio import Direction
Expand Down Expand Up @@ -131,6 +132,95 @@
# pylint: disable=too-many-lines


class Network:
"""A wifi network provided by a nearby access point."""

def __init__( # pylint: disable=too-many-arguments
self,
esp_spi_control=None,
raw_ssid=None,
raw_bssid=None,
raw_rssi=None,
raw_channel=None,
raw_country=None,
raw_authmode=None,
):
self._esp_spi_control = esp_spi_control
self._raw_ssid = raw_ssid
self._raw_bssid = raw_bssid
self._raw_rssi = raw_rssi
self._raw_channel = raw_channel
self._raw_country = raw_country
self._raw_authmode = raw_authmode

def _get_response(self, cmd):
respose = self._esp_spi_control._send_command_get_response( # pylint: disable=protected-access
cmd, [b"\xFF"]
)
return respose[0]

@property
def ssid(self):
"""String id of the network"""
if self._raw_ssid:
response = self._raw_ssid
else:
response = self._get_response(_GET_CURR_SSID_CMD)
return response.decode("utf-8")

@property
def bssid(self):
"""BSSID of the network (usually the AP’s MAC address)"""
if self._raw_bssid:
response = self._raw_bssid
else:
response = self._get_response(_GET_CURR_BSSID_CMD)
return bytes(response)

@property
def rssi(self):
"""Signal strength of the network"""
if self._raw_bssid:
response = self._raw_rssi
else:
response = self._get_response(_GET_CURR_RSSI_CMD)
return struct.unpack("<i", response)[0]

@property
def channel(self):
"""Channel number the network is operating on"""
if self._raw_channel:
return self._raw_channel[0]
return None

@property
def country(self):
"""String id of the country code"""
return self._raw_country

@property
def authmode(self):
"""String id of the authmode

derived from Nina code:
https://github.com/adafruit/nina-fw/blob/master/arduino/libraries/WiFi/src/WiFi.cpp#L385
"""
if self._raw_authmode:
response = self._raw_authmode[0]
else:
response = self._get_response(_GET_CURR_ENCT_CMD)[0]

if response == 7:
return "OPEN"
if response == 5:
return "WEP"
if response == 2:
return "PSK"
if response == 4:
return "WPA2"
return "UNKNOWN"


class ESP_SPIcontrol: # pylint: disable=too-many-public-methods, too-many-instance-attributes
"""A class that will talk to an ESP32 module programmed with special firmware
that lets it act as a fast an efficient WiFi co-processor"""
Expand Down Expand Up @@ -327,7 +417,7 @@ def _wait_response_cmd(self, cmd, num_responses=None, *, param_len_16=False):
print("Read %d: " % len(responses[0]), responses)
return responses

def _send_command_get_response(
def _send_command_get_response( # pylint: disable=too-many-arguments
self,
cmd,
params=None,
Expand Down Expand Up @@ -359,7 +449,7 @@ def firmware_version(self):
if self._debug:
print("Firmware version")
resp = self._send_command_get_response(_GET_FW_VERSION_CMD)
return resp[0]
return resp[0].decode("utf-8").replace("\x00", "")

@property
def MAC_address(self): # pylint: disable=invalid-name
Expand All @@ -372,12 +462,12 @@ def MAC_address(self): # pylint: disable=invalid-name
@property
def MAC_address_actual(self): # pylint: disable=invalid-name
"""A bytearray containing the actual MAC address of the ESP32"""
if self._debug:
print("MAC address")
resp = self._send_command_get_response(_GET_MACADDR_CMD, [b"\xFF"])
new_resp = bytearray(resp[0])
new_resp = reversed(new_resp)
return new_resp
return bytearray(reversed(self.MAC_address))

@property
def mac_address(self):
"""A bytes containing the actual MAC address of the ESP32"""
return bytes(self.MAC_address_actual)

def start_scan_networks(self):
"""Begin a scan of visible access points. Follow up with a call
Expand All @@ -397,16 +487,19 @@ def get_scan_networks(self):
# print("SSID names:", names)
APs = [] # pylint: disable=invalid-name
for i, name in enumerate(names):
a_p = {"ssid": name}
rssi = self._send_command_get_response(_GET_IDX_RSSI_CMD, ((i,),))[0]
a_p["rssi"] = struct.unpack("<i", rssi)[0]
encr = self._send_command_get_response(_GET_IDX_ENCT_CMD, ((i,),))[0]
a_p["encryption"] = encr[0]
bssid = self._send_command_get_response(_GET_IDX_BSSID_CMD, ((i,),))[0]
a_p["bssid"] = bssid
chan = self._send_command_get_response(_GET_IDX_CHAN_CMD, ((i,),))[0]
a_p["channel"] = chan[0]
APs.append(a_p)
rssi = self._send_command_get_response(_GET_IDX_RSSI_CMD, ((i,),))[0]
channel = self._send_command_get_response(_GET_IDX_CHAN_CMD, ((i,),))[0]
authmode = self._send_command_get_response(_GET_IDX_ENCT_CMD, ((i,),))[0]
APs.append(
Network(
raw_ssid=name,
raw_bssid=bssid,
raw_rssi=rssi,
raw_channel=channel,
raw_authmode=authmode,
)
)
return APs

def scan_networks(self):
Expand Down Expand Up @@ -512,23 +605,12 @@ def _wifi_set_ap_passphrase(self, ssid, passphrase, channel):
raise OSError("Failed to setup AP password")

@property
def ssid(self):
"""The name of the access point we're connected to"""
resp = self._send_command_get_response(_GET_CURR_SSID_CMD, [b"\xFF"])
return resp[0]

@property
def bssid(self):
"""The MAC-formatted service set ID of the access point we're connected to"""
resp = self._send_command_get_response(_GET_CURR_BSSID_CMD, [b"\xFF"])
return resp[0]

@property
def rssi(self):
"""The receiving signal strength indicator for the access point we're
connected to"""
resp = self._send_command_get_response(_GET_CURR_RSSI_CMD, [b"\xFF"])
return struct.unpack("<i", resp[0])[0]
def ap_info(self):
"""Network object containing BSSID, SSID, authmode, channel, country and RSSI when
connected to an access point. None otherwise."""
if self.is_connected:
return Network(esp_spi_control=self)
return None

@property
def network_data(self):
Expand All @@ -545,14 +627,19 @@ def ip_address(self):
return self.network_data["ip_addr"]

@property
def is_connected(self):
def connected(self):
"""Whether the ESP32 is connected to an access point"""
try:
return self.status == WL_CONNECTED
except OSError:
self.reset()
return False

@property
def is_connected(self):
"""Whether the ESP32 is connected to an access point"""
return self.connected

@property
def ap_listening(self):
"""Returns if the ESP32 is in access point mode and is listening for connections"""
Expand All @@ -568,10 +655,21 @@ def disconnect(self):
if resp[0][0] != 1:
raise OSError("Failed to disconnect")

def connect(self, secrets):
"""Connect to an access point using a secrets dictionary
that contains a 'ssid' and 'password' entry"""
self.connect_AP(secrets["ssid"], secrets["password"])
def connect(self, ssid, password=None, timeout=10):
"""Connect to an access point with given name and password.

**Deprecated functionality:** If the first argument (``ssid``) is a ``dict``,
assume it is a dictionary with entries for keys ``"ssid"`` and, optionally, ``"password"``.
This mimics the previous signature for ``connect()``.
This upward compatibility will be removed in a future release.
"""
if isinstance(ssid, dict): # secrets
warnings.warn(
"The passing in of `secrets`, is deprecated. Use connect() with `ssid` and "
"`password` instead and fetch values from settings.toml with `os.getenv()`."
)
ssid, password = ssid["ssid"], ssid.get("password")
self.connect_AP(ssid, password, timeout_s=timeout)

def connect_AP(self, ssid, password, timeout_s=10): # pylint: disable=invalid-name
"""Connect to an access point with given name and password.
Expand Down Expand Up @@ -647,6 +745,11 @@ def create_AP(
raise ConnectionError("Failed to create AP", ssid)
raise OSError("Unknown error 0x%02x" % stat)

@property
def ipv4_address(self):
"""IP address of the station when connected to an access point."""
return self.pretty_ip(self.ip_address)

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])
Expand Down Expand Up @@ -701,7 +804,7 @@ def socket_open(self, socket_num, dest, port, conn_mode=TCP_MODE):
if self._debug:
print("*** Open socket to", dest, port, conn_mode)
if conn_mode == ESP_SPIcontrol.TLS_MODE and self._tls_socket is not None:
raise OSError(23) # ENFILE - File table overflow
raise OSError(23, "Only one open SSL connection allowed")
port_param = struct.pack(">H", port)
if isinstance(dest, str): # use the 5 arg version
dest = bytes(dest, "utf-8")
Expand Down Expand Up @@ -925,7 +1028,7 @@ def set_digital_read(self, pin):
:param int pin: ESP32 GPIO pin to read from.
"""
# Verify nina-fw => 1.5.0
fw_semver_maj = bytes(self.firmware_version).decode("utf-8")[2]
fw_semver_maj = self.firmware_version[2]
assert int(fw_semver_maj) >= 5, "Please update nina-fw to 1.5.0 or above."

resp = self._send_command_get_response(_SET_DIGITAL_READ_CMD, ((pin,),))[0]
Expand All @@ -944,7 +1047,7 @@ def set_analog_read(self, pin, atten=ADC_ATTEN_DB_11):
:param int atten: attenuation constant
"""
# Verify nina-fw => 1.5.0
fw_semver_maj = bytes(self.firmware_version).decode("utf-8")[2]
fw_semver_maj = self.firmware_version[2]
assert int(fw_semver_maj) >= 5, "Please update nina-fw to 1.5.0 or above."

resp = self._send_command_get_response(_SET_ANALOG_READ_CMD, ((pin,), (atten,)))
Expand Down
Loading