Skip to content

Commit 8d2c1e8

Browse files
committed
add socket
1 parent 66ad6f9 commit 8d2c1e8

File tree

2 files changed

+86
-8
lines changed

2 files changed

+86
-8
lines changed

adafruit_esp32spi.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -340,9 +340,11 @@ def get_socket(self):
340340
resp = resp[0][0]
341341
if resp == 255:
342342
raise RuntimeError("No sockets available")
343+
if self._debug:
344+
print("Allocated socket #%d" % resp)
343345
return resp
344346

345-
def socket_open(self, dest, ip, port, socket_num, conn_mode=TCP_MODE):
347+
def socket_open(self, socket_num, ip, port, conn_mode=TCP_MODE):
346348
if self._debug:
347349
print("*** Open socket")
348350
port_param = struct.pack('>H', port)
@@ -378,20 +380,17 @@ def socket_read(self, socket_num, size):
378380
sent_param_len_16=True, recv_param_len_16=True)
379381
return resp[0]
380382

381-
def socket_connect(self, dest, port):
383+
def socket_connect(self, socket_num, dest, port):
382384
if self._debug:
383385
print("*** Socket connect")
384386
if isinstance(dest, str): # convert to IP address
385387
dest = self.get_host_by_name(dest)
386-
self.sock_num = self.get_socket()
387-
if self._debug:
388-
print("Allocated socket #%d" % self.sock_num)
389388

390-
self.socket_open(dest, dest, port, self.sock_num)
389+
self.socket_open(socket_num, dest, port)
391390
times = time.monotonic()
392391
while (time.monotonic() - times) < 3: # wait 3 seconds
393-
if self.socket_connected(self.sock_num):
394-
return
392+
if self.socket_connected(socket_num):
393+
return True
395394
time.sleep(0.01)
396395
raise RuntimeError("Failed to establish connection")
397396

adafruit_esp32spi_socket.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
"""A 'socket' compatible interface thru the ESP SPI command set"""
2+
from micropython import const
3+
4+
_the_interface = None # pylint: disable=invalid-name
5+
def set_interface(iface):
6+
"""Helper to set the global internet interface"""
7+
global _the_interface # pylint: disable=global-statement, invalid-name
8+
_the_interface = iface
9+
10+
SOCK_STREAM = const(1)
11+
AF_INET = const(2)
12+
13+
# pylint: disable=too-many-arguments, unused-argument
14+
def getaddrinfo(host, port, family=0, socktype=0, proto=0, flags=0):
15+
"""Given a hostname and a port name, return a 'socket.getaddrinfo'
16+
compatible list of tuples. Honestly, we ignore anything but host & port"""
17+
if not isinstance(port, int):
18+
raise RuntimeError("Port must be an integer")
19+
ipaddr = _the_interface.get_host_by_name(host)
20+
return [(AF_INET, socktype, proto, '', (ipaddr, port))]
21+
# pylint: enable=too-many-arguments, unused-argument
22+
23+
# pylint: disable=unused-argument, redefined-builtin, invalid-name
24+
class socket:
25+
"""A simplified implementation of the Python 'socket' class, for connecting
26+
through an interface to a remote device"""
27+
def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None):
28+
if family != AF_INET:
29+
raise RuntimeError("Only AF_INET family supported")
30+
if type != SOCK_STREAM:
31+
raise RuntimeError("Only SOCK_STREAM type supported")
32+
self._buffer = b''
33+
self._socknum = _the_interface.get_socket()
34+
35+
def connect(self, address, conntype=None):
36+
"""Connect the socket to the 'address' (which can be 32bit packed IP or
37+
a hostname string). 'conntype' is an extra that may indicate SSL or not,
38+
depending on the underlying interface"""
39+
host, port = address
40+
if not _the_interface.socket_connect(self._socknum, host, port):
41+
raise RuntimeError("Failed to connect to host", host)
42+
self._buffer = b''
43+
44+
def write(self, data): # pylint: disable=no-self-use
45+
"""Send some data to the socket"""
46+
_the_interface.socket_write(self._socknum, data)
47+
48+
def readline(self):
49+
"""Attempt to return as many bytes as we can up to but not including '\r\n'"""
50+
while b'\r\n' not in self._buffer:
51+
# there's no line already in there, read some more
52+
avail = _the_interface.socket_available(self._socknum)
53+
if avail:
54+
self._buffer += _the_interface.socket_read(self._socknum, avail)
55+
firstline, self._buffer = self._buffer.split(b'\r\n', 1)
56+
return firstline
57+
58+
def read(self, size=0):
59+
"""Read up to 'size' bytes from the socket, this may be buffered internally!
60+
If 'size' isnt specified, return everything in the buffer."""
61+
avail = _the_interface.socket_available(self._socknum)
62+
if avail:
63+
self._buffer += _the_interface.socket_read(self._socknum, avail)
64+
if size == 0: # read as much as we can
65+
ret = self._buffer
66+
self._buffer = b''
67+
return ret
68+
while len(self._buffer) < size:
69+
avail = _the_interface.socket_available(self._socknum)
70+
if avail:
71+
self._buffer += _the_interface.socket_read(self._socknum, avail)
72+
ret = self._buffer[:size]
73+
self._buffer = self._buffer[size:]
74+
return ret
75+
76+
def close(self):
77+
"""Close the socket, after reading whatever remains"""
78+
_the_interface.socket_close(self._socknum)
79+
# pylint: enable=unused-argument, redefined-builtin, invalid-name

0 commit comments

Comments
 (0)