diff --git a/adafruit_esp32spi/adafruit_esp32spi.py b/adafruit_esp32spi/adafruit_esp32spi.py
index e5f109a..8ca576c 100644
--- a/adafruit_esp32spi/adafruit_esp32spi.py
+++ b/adafruit_esp32spi/adafruit_esp32spi.py
@@ -54,6 +54,8 @@
# pylint: disable=bad-whitespace
_SET_NET_CMD = const(0x10)
_SET_PASSPHRASE_CMD = const(0x11)
+_SET_AP_NET_CMD = const(0x18)
+_SET_AP_PASSPHRASE_CMD = const(0x19)
_SET_DEBUG_CMD = const(0x1A)
_GET_CONN_STATUS_CMD = const(0x20)
@@ -64,6 +66,7 @@
_GET_CURR_ENCT_CMD = const(0x26)
_SCAN_NETWORKS = const(0x27)
+_START_SERVER_TCP_CMD = const(0x28)
_GET_SOCKET_CMD = const(0x3F)
_GET_STATE_TCP_CMD = const(0x29)
_DATA_SENT_TCP_CMD = const(0x2A)
@@ -409,6 +412,19 @@ def wifi_set_entenable(self):
if resp[0][0] != 1:
raise RuntimeError("Failed to enable enterprise mode")
+ def wifi_set_ap_network(self, ssid, channel):
+ """TODO Docs"""
+ resp = self._send_command_get_response(_SET_AP_NET_CMD, [ssid, channel])
+ if resp[0][0] != 1:
+ raise RuntimeError("Failed to setup AP network")
+
+ def wifi_set_ap_passphrase(self, ssid, passphrase, channel):
+ """TODO Docs"""
+ """ TODO: Why does this command refuse to work? creating AP w/out password works fine"""
+ resp = self._send_command_get_response(_SET_AP_PASSPHRASE_CMD, [ssid, passphrase, channel])
+ if resp[0][0] != 1:
+ raise RuntimeError("Failed to setup AP password")
+
@property
def ssid(self):
"""The name of the access point we're connected to"""
@@ -443,6 +459,15 @@ def is_connected(self):
self.reset()
return False
+ @property
+ def ap_listening(self):
+ """Whether the ESP32 is in access point mode and is listening for connections"""
+ try:
+ return self.status == WL_AP_LISTENING
+ except RuntimeError:
+ self.reset()
+ return False
+
def connect(self, secrets):
"""Connect to an access point using a secrets dictionary
that contains a 'ssid' and 'password' entry"""
@@ -473,6 +498,25 @@ def connect_AP(self, ssid, password): # pylint: disable=invalid-name
raise RuntimeError("No such ssid", ssid)
raise RuntimeError("Unknown error 0x%02X" % stat)
+ def create_AP(self, ssid, password, channel=b'\x01'):
+ """Create an access point with the given name and password."""
+ if isinstance(ssid, str):
+ ssid = bytes(ssid, 'utf-8')
+ if password:
+ if isinstance(password, str):
+ password = bytes(password, 'utf-8')
+ self.wifi_set_ap_passphrase(ssid, password, channel)
+ else:
+ self.wifi_set_ap_network(ssid, channel)
+ for _ in range(10): # retries
+ stat = self.status
+ if stat == WL_AP_LISTENING:
+ return stat
+ time.sleep(1)
+ if stat == WL_AP_FAILED:
+ raise RuntimeError("Failed to create AP", ssid)
+ raise RuntimeError("Unknown error 0x%02x" % stat)
+
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])
@@ -619,6 +663,30 @@ def socket_close(self, socket_num):
if resp[0][0] != 1:
raise RuntimeError("Failed to close socket")
+ def start_server(self, port, socket_num, conn_mode=TCP_MODE, ip=None):
+ if self._debug:
+ print("*** starting server")
+ self._socknum_ll[0][0] = socket_num
+ port_param = struct.pack('>H', port)
+ if ip: # use the 4 arg version
+ resp = self._send_command_get_response(_START_SERVER_TCP_CMD,
+ (ip,
+ port_param,
+ self._socknum_ll[0],
+ (conn_mode,)))
+ else: # use the 3 arg version
+ resp = self._send_command_get_response(_START_SERVER_TCP_CMD,
+ (port_param,
+ self._socknum_ll[0],
+ (conn_mode,)))
+ if resp[0][0] != 1:
+ raise RuntimeError("Could not start server")
+
+ def get_server_state(self, socket_num):
+ self._socknum_ll[0][0] = socket_num
+ resp = self._send_command_get_response(_GET_STATE_TCP_CMD, self._socknum_ll)
+ return resp[0][0]
+
def set_esp_debug(self, enabled):
"""Enable/disable debug mode on the ESP32. Debug messages will be
written to the ESP32's UART."""
diff --git a/adafruit_esp32spi/adafruit_esp32spi_socket.py b/adafruit_esp32spi/adafruit_esp32spi_socket.py
index ccf5b4f..9aef70b 100644
--- a/adafruit_esp32spi/adafruit_esp32spi_socket.py
+++ b/adafruit_esp32spi/adafruit_esp32spi_socket.py
@@ -66,6 +66,7 @@ def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None):
raise RuntimeError("Only SOCK_STREAM type supported")
self._buffer = b''
self._socknum = _the_interface.get_socket()
+ print("socknum: ", self._socknum)
self.settimeout(0)
def connect(self, address, conntype=None):
@@ -148,6 +149,9 @@ def settimeout(self, value):
"""Set the read timeout for sockets, if value is 0 it will block"""
self._timeout = value
+ def get_sock_num(self):
+ return self._socknum
+
def close(self):
"""Close the socket, after reading whatever remains"""
_the_interface.socket_close(self._socknum)
diff --git a/adafruit_esp32spi/adafruit_esp32spi_wifimanager.py b/adafruit_esp32spi/adafruit_esp32spi_wifimanager.py
index f0acae2..9d7dd3f 100755
--- a/adafruit_esp32spi/adafruit_esp32spi_wifimanager.py
+++ b/adafruit_esp32spi/adafruit_esp32spi_wifimanager.py
@@ -31,14 +31,15 @@
# pylint: disable=no-name-in-module
-from adafruit_esp32spi import adafruit_esp32spi
import adafruit_esp32spi.adafruit_esp32spi_requests as requests
+from adafruit_esp32spi import adafruit_esp32spi
+
class ESPSPI_WiFiManager:
"""
A class to help manage the Wifi connection
"""
- def __init__(self, esp, secrets, status_pixel=None, attempts=2):
+ def __init__(self, esp, secrets, status_pixel=None, attempts=2, debug=False):
"""
:param ESP_SPIcontrol esp: The ESP object we are using
:param dict secrets: The WiFi and Adafruit IO secrets dict (See examples)
@@ -49,9 +50,9 @@ def __init__(self, esp, secrets, status_pixel=None, attempts=2):
"""
# Read the settings
self._esp = esp
- self.debug = False
+ self.debug = debug
self.ssid = secrets['ssid']
- self.password = secrets['password']
+ self.password = secrets.get('password', None)
self.attempts = attempts
requests.set_interface(self._esp)
self.statuspix = status_pixel
@@ -93,6 +94,32 @@ def connect(self):
self.reset()
continue
+ def create_ap(self):
+ """
+ Attempt to initialize in Access Point (AP) mode.
+ Other WiFi devices will be able to connect to the created Access Point
+ """
+ failure_count = 0
+ while not self._esp.ap_listening:
+ try:
+ if self.debug:
+ print("Waiting for AP to be initialized...")
+ self.pixel_status((100,0,0))
+ if(self.password):
+ self._esp.create_AP(bytes(self.ssid, 'utf-8'), bytes(self.password, 'utf-8'))
+ else:
+ self._esp.create_AP(bytes(self.ssid, 'utf-8'), None)
+ failure_count = 0
+ self.pixel_status((0,100,0))
+ except (ValueError, RuntimeError) as error:
+ print("Failed to create access point\n", error)
+ failure_count += 1
+ if failure_count >= self.attempts:
+ failure_count = 0
+ self.reset()
+ continue
+ print("Access Point created! Connect to ssid: {}".format(self.ssid))
+
def get(self, url, **kw):
"""
Pass the Get request to requests and update status LED
diff --git a/examples/esp32spi_server.py b/examples/esp32spi_server.py
new file mode 100644
index 0000000..bc722d3
--- /dev/null
+++ b/examples/esp32spi_server.py
@@ -0,0 +1,86 @@
+import board
+import busio
+import time
+from digitalio import DigitalInOut
+from secrets import secrets
+
+from adafruit_esp32spi import adafruit_esp32spi
+import adafruit_esp32spi.adafruit_esp32spi_requests as requests
+import adafruit_esp32spi.adafruit_esp32spi_wifimanager as wifimanager
+import adafruit_esp32spi.adafruit_esp32spi_socket as socket
+
+print("ESP32 SPI web server test!!!!!!")
+
+esp32_cs = DigitalInOut(board.D10)
+esp32_ready = DigitalInOut(board.D9)
+esp32_reset = DigitalInOut(board.D7)
+esp32_gpio0 = DigitalInOut(board.D12)
+
+
+spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
+esp = adafruit_esp32spi.ESP_SPIcontrol(spi, esp32_cs, esp32_ready, esp32_reset, gpio0_pin=esp32_gpio0, debug=False)
+
+## Create Access Point from SSID and optional password in secrets
+wifi = wifimanager.ESPSPI_WiFiManager(esp, secrets, debug=True)
+wifi.create_ap()
+time.sleep(10)
+
+socket.set_interface(esp)
+sock = socket.socket() # Request a socket for the server
+curr_sock = sock
+sockNum = sock.get_sock_num()
+print("server status: ", esp.get_server_state(sockNum))
+
+# Start the server on port 80 with the socket number we just requested for it.
+esp.start_server(80, sockNum)
+
+print("socket num: ", sockNum)
+print("server status: ", esp.get_server_state(sockNum))
+print("IP addr: ", esp.pretty_ip(esp.ip_address))
+print("info: ", esp.network_data)
+print("done!")
+
+
+status = 0
+last_sock = 255
+def server_avail(): # TODO: make a server helper class
+ global last_sock
+ sock = 255;
+
+ if (curr_sock != 255):
+ # if (last_sock != 255):
+ # TODO: if last sock, check that last_sock is still connected and available
+ # sock = last_sock
+ if (sock == 255):
+ sock = esp.socket_available(sockNum)
+ if (sock != 255):
+ last_sock = sock
+ return sock
+
+ return 255
+
+while True:
+ if status != esp.status: # TODO: Move device connected check to server class ?
+ status = esp.status
+
+ if status == 8:
+ print("Device connected! status=", status)
+ else:
+ print("Device disconnected! status=", status)
+
+
+ avail = server_avail()
+ if (avail != 255):
+ sock.set_sock_num(avail) # TODO: Server class should return a new client socket
+ data = sock.read()
+ if (len(data)):
+ print(data)
+ sock.write(b"HTTP/1.1 200 OK\r\n");
+ sock.write(b"Content-type:text/html\r\n");
+ sock.write(b"\r\n");
+
+ sock.write(b"Click here turn the LED on!!!
\r\n");
+ sock.write(b"Click here turn the LED off!!!!
\r\n");
+
+ sock.write(b"\r\n")
+ sock.close()