From 5fd12457dbda5c717149d5dbb45e154a7467733d Mon Sep 17 00:00:00 2001 From: BiffoBear Date: Wed, 21 Jun 2023 08:26:04 +0700 Subject: [PATCH 01/18] Move message generation outside loop. --- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index 8d42ca7..ccc6f76 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -366,9 +366,8 @@ def _handle_dhcp_message(self) -> int: raise ValueError( "FSM can only send messages while in SELECTING or REQUESTING states." ) + message_length = self._generate_dhcp_message(message_type=msg_type_out) for attempt in range(4): # Initial attempt plus 3 retries. - message_length = self._generate_dhcp_message(message_type=msg_type_out) - if self._renew: dhcp_server_address = self.dhcp_server_ip else: From ba001b791a887b745c955dc254650d81a7e87210 Mon Sep 17 00:00:00 2001 From: BiffoBear Date: Wed, 21 Jun 2023 08:29:51 +0700 Subject: [PATCH 02/18] Move move get UDP port to _handle_dhcp_message. --- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index ccc6f76..51292b3 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -197,7 +197,6 @@ def _dsm_reset(self) -> None: state machine INIT state.""" debug_msg("Resetting DHCP state machine.", self._debug) self._socket_release() - self._dhcp_connection_setup() self.dhcp_server_ip = _BROADCAST_SERVER_ADDR self._eth.ifconfig = ( _UNASSIGNED_IP_ADDR, @@ -366,6 +365,7 @@ def _handle_dhcp_message(self) -> int: raise ValueError( "FSM can only send messages while in SELECTING or REQUESTING states." ) + self._dhcp_connection_setup() message_length = self._generate_dhcp_message(message_type=msg_type_out) for attempt in range(4): # Initial attempt plus 3 retries. if self._renew: @@ -431,7 +431,6 @@ def _dhcp_state_machine(self, *, blocking: bool = False) -> None: if self._dhcp_state == _STATE_RENEWING: debug_msg("FSM state is RENEWING.", self._debug) self._renew = "renew" - self._dhcp_connection_setup() self._start_time = time.monotonic() self._dhcp_state = _STATE_REQUESTING @@ -439,7 +438,6 @@ def _dhcp_state_machine(self, *, blocking: bool = False) -> None: debug_msg("FSM state is REBINDING.", self._debug) self._renew = "rebind" self.dhcp_server_ip = _BROADCAST_SERVER_ADDR - self._dhcp_connection_setup() self._start_time = time.monotonic() self._dhcp_state = _STATE_REQUESTING From a5a6d60ece3099f9c7437c47023fd4198e24fa25 Mon Sep 17 00:00:00 2001 From: BiffoBear Date: Wed, 21 Jun 2023 08:45:37 +0700 Subject: [PATCH 03/18] Move close UDP port to _handle_dhcp_message. --- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index 51292b3..dd03cd9 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -196,7 +196,6 @@ def _dsm_reset(self) -> None: """Close the socket and set attributes to default values used by the state machine INIT state.""" debug_msg("Resetting DHCP state machine.", self._debug) - self._socket_release() self.dhcp_server_ip = _BROADCAST_SERVER_ADDR self._eth.ifconfig = ( _UNASSIGNED_IP_ADDR, @@ -215,7 +214,7 @@ def _dsm_reset(self) -> None: def _socket_release(self) -> None: """Close the socket if it exists.""" debug_msg("Releasing socket.", self._debug) - if self._wiz_sock: + if self._wiz_sock is not None: self._eth.socket_close(self._wiz_sock) self._wiz_sock = None debug_msg(" Socket released.", self._debug) @@ -386,12 +385,16 @@ def _handle_dhcp_message(self) -> int: return msg_type_in except ValueError as error: debug_msg(error, self._debug) + finally: + self._socket_release() if not self._blocking or self._renew: debug_msg( "No message, FSM is nonblocking or renewing, exiting loop.", self._debug, ) + self._socket_release() return 0 # Did not receive a response in a single attempt. + self._socket_release() raise TimeoutError( "No response from DHCP server after {} retries.".format(attempt) ) @@ -409,7 +412,6 @@ def _dhcp_state_machine(self, *, blocking: bool = False) -> None: now = time.monotonic() if now < self._t1: debug_msg("No timers have expired. Exiting FSM.", self._debug) - self._socket_release() return if now > self._lease: debug_msg( From 7805e1a3958de591ffb61ceb52084ade6eae274e Mon Sep 17 00:00:00 2001 From: BiffoBear Date: Wed, 21 Jun 2023 14:20:08 +0700 Subject: [PATCH 04/18] Move all socket calls to _receive_dhcp_response and _handle_dhcp_message. --- adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py | 131 ++++++++------------ 1 file changed, 52 insertions(+), 79 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index dd03cd9..b4ff581 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -147,7 +147,6 @@ def __init__( # Set socket interface self._eth = eth - self._wiz_sock = None # DHCP state machine self._dhcp_state = _STATE_INIT @@ -211,45 +210,6 @@ def _dsm_reset(self) -> None: self._increment_transaction_id() self._start_time = time.monotonic() - def _socket_release(self) -> None: - """Close the socket if it exists.""" - debug_msg("Releasing socket.", self._debug) - if self._wiz_sock is not None: - self._eth.socket_close(self._wiz_sock) - self._wiz_sock = None - debug_msg(" Socket released.", self._debug) - - def _dhcp_connection_setup(self, timeout: float = 5.0) -> None: - """Initialise a UDP socket. - - Attempt to initialise a UDP socket. If the finite state machine (FSM) is in - blocking mode, repeat failed attempts until a socket is initialised or - the operation times out, then raise an exception. If the FSM is in non-blocking - mode, ignore the error and return. - - :param int timeout: Time to keep retrying if the FSM is in blocking mode. - Defaults to 5. - - :raises TimeoutError: If the FSM is in blocking mode and a socket cannot be - initialised. - """ - stop_time = time.monotonic() + timeout - debug_msg("Setting up connection for DHCP.", self._debug) - while self._wiz_sock is None and time.monotonic() < stop_time: - self._wiz_sock = self._eth.get_socket() - if self._wiz_sock == 0xFF: - self._wiz_sock = None - while time.monotonic() < stop_time: - self._eth.write_snmr(self._wiz_sock, 0x02) # Set UDP connection - self._eth.write_sock_port(self._wiz_sock, 68) # Set DHCP client port. - self._eth.write_sncr(self._wiz_sock, 0x01) # Open the socket. - if self._eth.read_snsr(self._wiz_sock) == 0x22: - self._eth.write_sndport(2, _DHCP_SERVER_PORT) - debug_msg("+ Connection OK, port set.", self._debug) - return - self._wiz_sock = None - raise RuntimeError("Unable to initialize UDP socket.") - def _increment_transaction_id(self) -> None: """Increment the transaction ID and roll over from 0x7fffffff to 0.""" debug_msg("Incrementing transaction ID", self._debug) @@ -278,7 +238,7 @@ def _next_retry_time(self, *, attempt: int, interval: int = 4) -> float: delay = 2**attempt * interval + randint(-1, 1) + time.monotonic() return delay - def _receive_dhcp_response(self, timeout: float) -> int: + def _receive_dhcp_response(self, socket_num: int, timeout: float) -> int: """ Receive data from the socket in response to a DHCP query. @@ -288,23 +248,22 @@ def _receive_dhcp_response(self, timeout: float) -> int: If the packet is too short, it is discarded and zero is returned. The maximum packet size is limited by the size of the global buffer. - :param float timeout: time.monotonic at which attempt should timeout. + :param int socket_num: Socket to read from. + :param float timeout: time.monotonic at which attempt should time out. :returns int: The number of bytes stored in the global buffer. """ debug_msg("Receiving a DHCP response.", self._debug) while time.monotonic() < timeout: # DHCP returns the query plus additional data. The query length is 236 bytes. - if self._eth.socket_available(self._wiz_sock, _SNMR_UDP) > 236: - bytes_count, bytes_read = self._eth.read_udp( - self._wiz_sock, _BUFF_LENGTH - ) + if self._eth.socket_available(socket_num, _SNMR_UDP) > 236: + bytes_count, bytes_read = self._eth.read_udp(socket_num, _BUFF_LENGTH) _BUFF[:bytes_count] = bytes_read debug_msg("Received {} bytes".format(bytes_count), self._debug) del bytes_read gc.collect() return bytes_count - raise TimeoutError("No DHCP response received.") + return 0 # No bytes received. def _process_messaging_states(self, *, message_type: int): """ @@ -355,6 +314,7 @@ def _handle_dhcp_message(self) -> int: :raises TimeoutError: If the FSM is in blocking mode and no valid response has been received before the timeout expires. """ + # pylint: disable=too-many-branches debug_msg("Processing SELECTING or REQUESTING state.", self._debug) if self._dhcp_state == _STATE_SELECTING: msg_type_out = _DHCP_DISCOVER @@ -364,40 +324,53 @@ def _handle_dhcp_message(self) -> int: raise ValueError( "FSM can only send messages while in SELECTING or REQUESTING states." ) - self._dhcp_connection_setup() - message_length = self._generate_dhcp_message(message_type=msg_type_out) - for attempt in range(4): # Initial attempt plus 3 retries. - if self._renew: - dhcp_server_address = self.dhcp_server_ip - else: - dhcp_server_address = _BROADCAST_SERVER_ADDR - self._eth.write_sndipr(self._wiz_sock, dhcp_server_address) - self._eth.write_sndport(self._wiz_sock, _DHCP_SERVER_PORT) - self._eth.socket_write(self._wiz_sock, _BUFF[:message_length]) - next_resend = self._next_retry_time(attempt=attempt) - while time.monotonic() < next_resend: - if self._receive_dhcp_response(next_resend): - try: - msg_type_in = self._parse_dhcp_response() + debug_msg("Setting up connection for DHCP.", self._debug) + if self._renew: + dhcp_server = self.dhcp_server_ip + else: + dhcp_server = _BROADCAST_SERVER_ADDR + sock_num = None + deadline = time.monotonic() + 5.0 + try: + while sock_num is None: + sock_num = self._eth.get_socket() + if sock_num == 0xFF: + sock_num = None + if time.monotonic() > deadline: + raise RuntimeError("Unable to initialize UDP socket.") + + self._eth.src_port = 68 + self._eth.socket_connect( + sock_num, dhcp_server, _DHCP_SERVER_PORT, conn_mode=0x02 + ) + self._eth.src_port = 0 + + message_length = self._generate_dhcp_message(message_type=msg_type_out) + for attempt in range(4): # Initial attempt plus 3 retries. + self._eth.socket_write(sock_num, _BUFF[:message_length]) + next_resend = self._next_retry_time(attempt=attempt) + while time.monotonic() < next_resend: + if self._receive_dhcp_response(sock_num, next_resend): + try: + msg_type_in = self._parse_dhcp_response() + debug_msg( + "Received message type {}".format(msg_type_in), + self._debug, + ) + return msg_type_in + except ValueError as error: + debug_msg(error, self._debug) + if not self._blocking or self._renew: debug_msg( - "Received message type {}".format(msg_type_in), self._debug + "No message, FSM is nonblocking or renewing, exiting loop.", + self._debug, ) - return msg_type_in - except ValueError as error: - debug_msg(error, self._debug) - finally: - self._socket_release() - if not self._blocking or self._renew: - debug_msg( - "No message, FSM is nonblocking or renewing, exiting loop.", - self._debug, - ) - self._socket_release() - return 0 # Did not receive a response in a single attempt. - self._socket_release() - raise TimeoutError( - "No response from DHCP server after {} retries.".format(attempt) - ) + return 0 # Did not receive a response in a single attempt. + raise TimeoutError( + "No response from DHCP server after {} retries.".format(attempt) + ) + finally: + self._eth.socket_close(sock_num) # Close the socket whatever happens. def _dhcp_state_machine(self, *, blocking: bool = False) -> None: """ From 79668f482757248d7c5f52d8171de934657d59f3 Mon Sep 17 00:00:00 2001 From: BiffoBear Date: Wed, 21 Jun 2023 15:14:25 +0700 Subject: [PATCH 05/18] Refactor, rename methods to reflect their private status. --- adafruit_wiznet5k/adafruit_wiznet5k.py | 62 +++++++++++++------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k.py b/adafruit_wiznet5k/adafruit_wiznet5k.py index 278b886..5452f82 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k.py @@ -156,7 +156,7 @@ _MR_RST = const(0x80) # Mode Register RST # Socket mode register _SNMR_CLOSE = const(0x00) -SNMR_TCP = const(0x21) +_SNMR_TCP = const(0x21) SNMR_UDP = const(0x02) _SNMR_IPRAW = const(0x03) _SNMR_MACRAW = const(0x04) @@ -492,7 +492,7 @@ def ifconfig( # *** Public Socket Methods *** - def socket_available(self, socket_num: int, sock_type: int = SNMR_TCP) -> int: + def socket_available(self, socket_num: int, sock_type: int = _SNMR_TCP) -> int: """ Number of bytes available to be read from the socket. @@ -514,7 +514,7 @@ def socket_available(self, socket_num: int, sock_type: int = SNMR_TCP) -> int: self._sock_num_in_range(socket_num) number_of_bytes = self._get_rx_rcv_size(socket_num) - if self.read_snsr(socket_num) == SNMR_UDP: + if self._read_snsr(socket_num) == SNMR_UDP: number_of_bytes -= 8 # Subtract UDP header from packet size. if number_of_bytes < 0: raise ValueError("Negative number of bytes found on socket.") @@ -533,14 +533,14 @@ def socket_status(self, socket_num: int) -> int: :return int: The connection status. """ - return self.read_snsr(socket_num) + return self._read_snsr(socket_num) def socket_connect( self, socket_num: int, dest: IpAddress4Raw, port: int, - conn_mode: int = SNMR_TCP, + conn_mode: int = _SNMR_TCP, ) -> int: """ Open and verify a connection from a socket to a destination IPv4 address @@ -567,11 +567,11 @@ def socket_connect( # initialize a socket and set the mode self.socket_open(socket_num, conn_mode=conn_mode) # set socket destination IP and port - self.write_sndipr(socket_num, dest) - self.write_sndport(socket_num, port) - self.write_sncr(socket_num, _CMD_SOCK_CONNECT) + self._write_sndipr(socket_num, dest) + self._write_sndport(socket_num, port) + self._write_sncr(socket_num, _CMD_SOCK_CONNECT) - if conn_mode == SNMR_TCP: + if conn_mode == _SNMR_TCP: # wait for tcp connection establishment while self.socket_status(socket_num) != SNSR_SOCK_ESTABLISHED: time.sleep(0.001) @@ -638,7 +638,7 @@ def release_socket(self, socket_number): WIZNET5K._sockets_reserved[socket_number - 1] = False def socket_listen( - self, socket_num: int, port: int, conn_mode: int = SNMR_TCP + self, socket_num: int, port: int, conn_mode: int = _SNMR_TCP ) -> None: """ Listen on a socket's port. @@ -665,7 +665,7 @@ def socket_listen( self.socket_open(socket_num, conn_mode=conn_mode) self.src_port = 0 # Send listen command - self.write_sncr(socket_num, _CMD_SOCK_LISTEN) + self._write_sncr(socket_num, _CMD_SOCK_LISTEN) # Wait until ready status = SNSR_SOCK_CLOSED while status not in ( @@ -673,7 +673,7 @@ def socket_listen( SNSR_SOCK_ESTABLISHED, _SNSR_SOCK_UDP, ): - status = self.read_snsr(socket_num) + status = self._read_snsr(socket_num) if status == SNSR_SOCK_CLOSED: raise RuntimeError("Listening socket closed.") @@ -703,7 +703,7 @@ def socket_accept(self, socket_num: int) -> Tuple[int, Tuple[str, int]]: ) return next_socknum, (dest_ip, dest_port) - def socket_open(self, socket_num: int, conn_mode: int = SNMR_TCP) -> None: + def socket_open(self, socket_num: int, conn_mode: int = _SNMR_TCP) -> None: """ Open an IP socket. @@ -720,7 +720,7 @@ def socket_open(self, socket_num: int, conn_mode: int = SNMR_TCP) -> None: self._sock_num_in_range(socket_num) self._check_link_status() debug_msg("*** Opening socket {}".format(socket_num), self._debug) - if self.read_snsr(socket_num) not in ( + if self._read_snsr(socket_num) not in ( SNSR_SOCK_CLOSED, SNSR_SOCK_TIME_WAIT, SNSR_SOCK_FIN_WAIT, @@ -732,22 +732,22 @@ def socket_open(self, socket_num: int, conn_mode: int = SNMR_TCP) -> None: debug_msg("* Opening W5k Socket, protocol={}".format(conn_mode), self._debug) time.sleep(0.00025) - self.write_snmr(socket_num, conn_mode) + self._write_snmr(socket_num, conn_mode) self.write_snir(socket_num, 0xFF) if self.src_port > 0: # write to socket source port - self.write_sock_port(socket_num, self.src_port) + self._write_sock_port(socket_num, self.src_port) else: s_port = randint(49152, 65535) while s_port in self._src_ports_in_use: s_port = randint(49152, 65535) - self.write_sock_port(socket_num, s_port) + self._write_sock_port(socket_num, s_port) self._src_ports_in_use[socket_num] = s_port # open socket - self.write_sncr(socket_num, _CMD_SOCK_OPEN) - if self.read_snsr(socket_num) not in [_SNSR_SOCK_INIT, _SNSR_SOCK_UDP]: + self._write_sncr(socket_num, _CMD_SOCK_OPEN) + if self._read_snsr(socket_num) not in [_SNSR_SOCK_INIT, _SNSR_SOCK_UDP]: raise RuntimeError("Could not open socket in TCP or UDP mode.") def socket_close(self, socket_num: int) -> None: @@ -760,14 +760,14 @@ def socket_close(self, socket_num: int) -> None: """ debug_msg("*** Closing socket {}".format(socket_num), self._debug) self._sock_num_in_range(socket_num) - self.write_sncr(socket_num, _CMD_SOCK_CLOSE) + self._write_sncr(socket_num, _CMD_SOCK_CLOSE) debug_msg(" Waiting for socket to close…", self._debug) timeout = time.monotonic() + 5.0 - while self.read_snsr(socket_num) != SNSR_SOCK_CLOSED: + while self._read_snsr(socket_num) != SNSR_SOCK_CLOSED: if time.monotonic() > timeout: raise RuntimeError( "Wiznet5k failed to close socket, status = {}.".format( - self.read_snsr(socket_num) + self._read_snsr(socket_num) ) ) time.sleep(0.0001) @@ -783,7 +783,7 @@ def socket_disconnect(self, socket_num: int) -> None: """ debug_msg("*** Disconnecting socket {}".format(socket_num), self._debug) self._sock_num_in_range(socket_num) - self.write_sncr(socket_num, _CMD_SOCK_DISCON) + self._write_sncr(socket_num, _CMD_SOCK_DISCON) def socket_read(self, socket_num: int, length: int) -> Tuple[int, bytes]: """ @@ -819,7 +819,7 @@ def socket_read(self, socket_num: int, length: int) -> Tuple[int, bytes]: # After reading the received data, update Sn_RX_RD register. pointer = (pointer + bytes_on_socket) & 0xFFFF self._write_snrx_rd(socket_num, pointer) - self.write_sncr(socket_num, _CMD_SOCK_RECV) + self._write_sncr(socket_num, _CMD_SOCK_RECV) else: # no data on socket if self._read_snmr(socket_num) in ( @@ -906,7 +906,7 @@ def socket_write( # update sn_tx_wr to the value + data size pointer = (pointer + bytes_to_write) & 0xFFFF self._write_sntx_wr(socket_num, pointer) - self.write_sncr(socket_num, _CMD_SOCK_SEND) + self._write_sncr(socket_num, _CMD_SOCK_SEND) # check data was transferred correctly while not self.read_snir(socket_num) & _SNIR_SEND_OK: @@ -1175,18 +1175,18 @@ def _read_sndipr(self, sock) -> bytes: ) return bytes(data) - def write_sndipr(self, sock: int, ip_addr: bytes) -> None: + def _write_sndipr(self, sock: int, ip_addr: bytes) -> None: """Write to socket destination IP Address.""" for offset, value in enumerate(ip_addr): self._write_socket_register( sock, _REG_SNDIPR[self._chip_type] + offset, value ) - def write_sndport(self, sock: int, port: int) -> None: + def _write_sndport(self, sock: int, port: int) -> None: """Write to socket destination port.""" self._write_two_byte_sock_reg(sock, _REG_SNDPORT[self._chip_type], port) - def read_snsr(self, sock: int) -> int: + def _read_snsr(self, sock: int) -> int: """Read Socket n Status Register.""" return self._read_socket_register(sock, _REG_SNSR[self._chip_type]) @@ -1202,15 +1202,15 @@ def _read_snmr(self, sock: int) -> int: """Read the socket MR register.""" return self._read_socket_register(sock, _REG_SNMR) - def write_snmr(self, sock: int, protocol: int) -> None: + def _write_snmr(self, sock: int, protocol: int) -> None: """Write to Socket n Mode Register.""" self._write_socket_register(sock, _REG_SNMR, protocol) - def write_sock_port(self, sock: int, port: int) -> None: + def _write_sock_port(self, sock: int, port: int) -> None: """Write to the socket port number.""" self._write_two_byte_sock_reg(sock, _REG_SNPORT[self._chip_type], port) - def write_sncr(self, sock: int, data: int) -> None: + def _write_sncr(self, sock: int, data: int) -> None: """Write to socket command register.""" self._write_socket_register(sock, _REG_SNCR[self._chip_type], data) # Wait for command to complete before continuing. From 91d1f31054c656308e689bb3eee0bf9c823256f6 Mon Sep 17 00:00:00 2001 From: BiffoBear Date: Wed, 21 Jun 2023 16:13:58 +0700 Subject: [PATCH 06/18] Add _read_socket_reservations and _read_sndport methods to aid trouble shooting. --- adafruit_wiznet5k/adafruit_wiznet5k.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k.py b/adafruit_wiznet5k/adafruit_wiznet5k.py index 5452f82..692bc57 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k.py @@ -1057,6 +1057,11 @@ def _check_link_status(self): if not self.link_status: raise ConnectionError("The Ethernet connection is down.") + @staticmethod + def _read_socket_reservations() -> list[int]: + """Return the list of reserved sockets.""" + return WIZNET5K._sockets_reserved + def _read_mr(self) -> int: """Read from the Mode Register (MR).""" return int.from_bytes(self._read(_REG_MR[self._chip_type], 0x00), "big") @@ -1182,6 +1187,10 @@ def _write_sndipr(self, sock: int, ip_addr: bytes) -> None: sock, _REG_SNDIPR[self._chip_type] + offset, value ) + def _read_sndport(self, sock: int) -> int: + """Read socket destination port.""" + return self._read_two_byte_sock_reg(sock, _REG_SNDPORT[self._chip_type]) + def _write_sndport(self, sock: int, port: int) -> None: """Write to socket destination port.""" self._write_two_byte_sock_reg(sock, _REG_SNDPORT[self._chip_type], port) From 23abb0b6d1fa424548e0988cacad2fea7911b8fe Mon Sep 17 00:00:00 2001 From: BiffoBear Date: Thu, 22 Jun 2023 03:37:52 +0700 Subject: [PATCH 07/18] Removed NTP client module. --- adafruit_wiznet5k/adafruit_wiznet5k_ntp.py | 87 ---------------------- 1 file changed, 87 deletions(-) delete mode 100644 adafruit_wiznet5k/adafruit_wiznet5k_ntp.py diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_ntp.py b/adafruit_wiznet5k/adafruit_wiznet5k_ntp.py deleted file mode 100644 index f15d8d4..0000000 --- a/adafruit_wiznet5k/adafruit_wiznet5k_ntp.py +++ /dev/null @@ -1,87 +0,0 @@ -# SPDX-FileCopyrightText: 2019 Brent Rubell for Adafruit Industries -# -# SPDX-License-Identifier: MIT - -""" -`adafruit_wiznet5k_ntp` -================================================================================ - -Network Time Protocol (NTP) helper for CircuitPython - - * Author(s): Brent Rubell, irinakim - -Implementation Notes --------------------- -**Hardware:** -**Software and Dependencies:** - - -""" -from __future__ import annotations - -try: - from typing import TYPE_CHECKING - - if TYPE_CHECKING: - from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K -except ImportError: - pass -import time -import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket - -# __version__ = "0.0.0+auto.0" -# __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_NTP.git" - - -class NTP: - """Wiznet5k NTP Client.""" - - def __init__( - self, - iface: WIZNET5K, - ntp_address: str, - utc: float, - debug: bool = False, - ) -> None: - """ - :param adafruit_wiznet5k.WIZNET5K iface: Wiznet 5k object. - :param str ntp_address: The hostname of the NTP server. - :param float utc: Numbers of hours to offset time from UTC. - :param bool debug: Enable debugging output, defaults to False. - """ - self._debug = debug - self._iface = iface - socket.set_interface(self._iface) - self._sock = socket.socket(type=socket.SOCK_DGRAM) - self._sock.settimeout(1) - self._utc = utc - - self._ntp_server = ntp_address - self._host = 0 - self._request_id = 0 # request identifier - - self._pkt_buf_ = bytearray([0x23] + [0x00] * 55) - - def get_time(self) -> time.struct_time: - """ - Get the time from the NTP server. - - :return time.struct_time: The local time. - """ - self._sock.bind((None, 50001)) - max_retries = 4 - for retry in range(max_retries): - self._sock.sendto(self._pkt_buf_, (self._ntp_server, 123)) - end_time = time.monotonic() + 0.2 * 2**retry - while time.monotonic() < end_time: - data = self._sock.recv(64) # NTP returns a 48 byte message. - if data: - sec = data[40:44] - int_cal = int.from_bytes(sec, "big") - # UTC offset may be a float as some offsets are half hours so force int. - cal = int(int_cal - 2208988800 + self._utc * 3600) - cal = time.localtime(cal) - return cal - raise TimeoutError( - "No reply from NTP server after {} attempts.".format(max_retries) - ) From 993238832089d27380ee5a274326cee82b1ae2d0 Mon Sep 17 00:00:00 2001 From: BiffoBear Date: Thu, 22 Jun 2023 04:34:34 +0700 Subject: [PATCH 08/18] Removed reference to NTP client module in api.rst. Pruned optional_requirements.txt. --- docs/api.rst | 3 --- optional_requirements.txt | 4 ---- 2 files changed, 7 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index d0d6694..560c4b0 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -13,9 +13,6 @@ .. automodule:: adafruit_wiznet5k.adafruit_wiznet5k_dhcp :members: -.. automodule:: adafruit_wiznet5k.adafruit_wiznet5k_ntp - :members: - .. automodule:: adafruit_wiznet5k.adafruit_wiznet5k_dns :members: diff --git a/optional_requirements.txt b/optional_requirements.txt index 7877be0..d4e27c4 100644 --- a/optional_requirements.txt +++ b/optional_requirements.txt @@ -1,7 +1,3 @@ # SPDX-FileCopyrightText: 2022 Alec Delaney, for Adafruit Industries # # SPDX-License-Identifier: Unlicense - -pytest -pytest-mock -freezegun From ae1bdacac822527795f7342baafa95f9fbf2ac19 Mon Sep 17 00:00:00 2001 From: Edward Wright Date: Thu, 29 Jun 2023 17:27:52 -0400 Subject: [PATCH 09/18] Fix socket swap in socket.accept() --- adafruit_wiznet5k/adafruit_wiznet5k_socket.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py index 650fd1f..7425f2e 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py @@ -406,12 +406,12 @@ def accept( self.close() self.listen() - new_listen_socknum, addr = _the_interface.socket_accept(self._socknum) + _, addr = _the_interface.socket_accept(self._socknum) current_socknum = self._socknum # Create a new socket object and swap socket nums, so we can continue listening client_sock = socket() + self._socknum = client_sock._socknum client_sock._socknum = current_socknum # pylint: disable=protected-access - self._socknum = new_listen_socknum self._bind((None, self._listen_port)) self.listen() while self._status != wiznet5k.adafruit_wiznet5k.SNSR_SOCK_LISTEN: From 0b21be27af9c768abfdaca2b9c24fec85746335e Mon Sep 17 00:00:00 2001 From: Edward Wright Date: Thu, 29 Jun 2023 18:07:09 -0400 Subject: [PATCH 10/18] Disable pylint protected-access error --- adafruit_wiznet5k/adafruit_wiznet5k_socket.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py index 7425f2e..2278aeb 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py @@ -410,7 +410,7 @@ def accept( current_socknum = self._socknum # Create a new socket object and swap socket nums, so we can continue listening client_sock = socket() - self._socknum = client_sock._socknum + self._socknum = client_sock._socknum # pylint: disable=protected-access client_sock._socknum = current_socknum # pylint: disable=protected-access self._bind((None, self._listen_port)) self.listen() From d5215683497f07d0064d107f01e7d52cc22ddcee Mon Sep 17 00:00:00 2001 From: Edward Wright Date: Thu, 29 Jun 2023 19:01:42 -0400 Subject: [PATCH 11/18] Improve reset during initialization --- adafruit_wiznet5k/adafruit_wiznet5k.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k.py b/adafruit_wiznet5k/adafruit_wiznet5k.py index 278b886..667acb2 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k.py @@ -219,10 +219,12 @@ def __init__( # Reset wiznet module prior to initialization. if reset: + debug_msg("* Resetting WIZnet chip", self._debug) + reset.switch_to_output() reset.value = False time.sleep(0.1) reset.value = True - time.sleep(0.1) + time.sleep(5) # Setup chip_select pin. time.sleep(1) From 6d3e9090057dbb1e07606e067e74bc0349f4b29f Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 18 Sep 2023 16:24:23 -0500 Subject: [PATCH 12/18] "fix rtd theme " --- docs/conf.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 641ec97..8714ac3 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -105,19 +105,10 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -on_rtd = os.environ.get("READTHEDOCS", None) == "True" - -if not on_rtd: # only import and set the theme if we're building docs locally - try: - import sphinx_rtd_theme - - html_theme = "sphinx_rtd_theme" - html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."] - except: - html_theme = "default" - html_theme_path = ["."] -else: - html_theme_path = ["."] +import sphinx_rtd_theme + +html_theme = "sphinx_rtd_theme" +html_theme_path = [sphinx_rtd_theme.get_html_theme_path(), "."] # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, From f3070a3ecf91ffeee56bc4e23fbcec15ea071839 Mon Sep 17 00:00:00 2001 From: foamyguy Date: Mon, 16 Oct 2023 14:30:31 -0500 Subject: [PATCH 13/18] unpin sphinx and add sphinx-rtd-theme to docs reqs Signed-off-by: foamyguy --- docs/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 797aa04..979f568 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -2,5 +2,6 @@ # # SPDX-License-Identifier: Unlicense -sphinx>=4.0.0 +sphinx sphinxcontrib-jquery +sphinx-rtd-theme From fbfa6008c9f449946c7d9a11bae83949dbab4afe Mon Sep 17 00:00:00 2001 From: adam_cummick Date: Tue, 19 Dec 2023 08:38:47 -0500 Subject: [PATCH 14/18] Correct error raised bytes MAC address --- adafruit_wiznet5k/adafruit_wiznet5k.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k.py b/adafruit_wiznet5k/adafruit_wiznet5k.py index b2771ef..ddf798a 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k.py @@ -386,7 +386,7 @@ def mac_address(self, address: Union[MacAddressRaw, str]) -> None: """ try: address = [int(x, 16) for x in address.split(":")] - except AttributeError: + except TypeError: pass try: if len(address) != 6: From 429ef02255c03355beabec9ad5017c23541de5c0 Mon Sep 17 00:00:00 2001 From: Dorin Date: Mon, 15 Jan 2024 14:05:05 +0200 Subject: [PATCH 15/18] Fix body recv when no content-length --- adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py b/adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py index 6309b8f..d4625e3 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py @@ -229,7 +229,7 @@ def _get_environ(self, client: socket.socket) -> Dict: body = client.recv(int(env["CONTENT_LENGTH"])) env["wsgi.input"] = io.StringIO(body) else: - body = client.recv(1024) + body = client.recv(0) env["wsgi.input"] = io.StringIO(body) for name, value in headers.items(): key = "HTTP_" + name.replace("-", "_").upper() From c0a563788d48a42a5507cfb1168bb52a5974eb69 Mon Sep 17 00:00:00 2001 From: Dorin Date: Mon, 15 Jan 2024 18:46:37 +0200 Subject: [PATCH 16/18] Fix recv, recv_into logic and have same behavior as ESP32SPI --- adafruit_wiznet5k/adafruit_wiznet5k_socket.py | 68 +++++++++++++------ 1 file changed, 47 insertions(+), 21 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py index 2278aeb..195ecad 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py @@ -495,21 +495,9 @@ def recv( :return bytes: Data from the socket. """ - stamp = time.monotonic() - while not self._available(): - if self._timeout and 0 < self._timeout < time.monotonic() - stamp: - break - time.sleep(0.05) - bytes_on_socket = self._available() - if not bytes_on_socket: - return b"" - bytes_to_read = min(bytes_on_socket, bufsize) - if self._sock_type == SOCK_STREAM: - bytes_read = _the_interface.socket_read(self._socknum, bytes_to_read)[1] - else: - bytes_read = _the_interface.read_udp(self._socknum, bytes_to_read)[1] - gc.collect() - return bytes(bytes_read) + buf = bytearray(bufsize) + self.recv_into(buf, bufsize) + return bytes(buf) def _embed_recv( self, bufsize: int = 0, flags: int = 0 @@ -568,12 +556,42 @@ def recv_into(self, buffer: bytearray, nbytes: int = 0, flags: int = 0) -> int: :return int: the number of bytes received """ - if nbytes == 0: - nbytes = len(buffer) - bytes_received = self.recv(nbytes) - nbytes = len(bytes_received) - buffer[:nbytes] = bytes_received - return nbytes + if not 0 <= nbytes <= len(buffer): + raise ValueError("nbytes must be 0 to len(buffer)") + + last_read_time = time.monotonic() + num_to_read = len(buffer) if nbytes == 0 else nbytes + num_read = 0 + while num_to_read > 0: + # we might have read socket data into the self._buffer with: + # _readline + if len(self._buffer) > 0: + bytes_to_read = min(num_to_read, len(self._buffer)) + buffer[num_read : num_read + bytes_to_read] = self._buffer[:bytes_to_read] + num_read += bytes_to_read + num_to_read -= bytes_to_read + self._buffer = self._buffer[bytes_to_read:] + # explicitly recheck num_to_read to avoid extra checks + continue + + num_avail = self._available() + if num_avail > 0: + last_read_time = time.monotonic() + bytes_to_read = min(num_to_read, num_avail) + if self._sock_type == SOCK_STREAM: + bytes_read = _the_interface.socket_read(self._socknum, bytes_to_read)[1] + else: + bytes_read = _the_interface.read_udp(self._socknum, bytes_to_read)[1] + buffer[num_read : num_read + len(bytes_read)] = bytes_read + num_read += len(bytes_read) + num_to_read -= len(bytes_read) + elif num_read > 0: + # We got a message, but there are no more bytes to read, so we can stop. + break + # No bytes yet, or more bytes requested. + if self._timeout > 0 and time.monotonic() - last_read_time > self._timeout: + raise timeout("timed out") + return num_read @_check_socket_closed def recvfrom_into( @@ -730,3 +748,11 @@ def type(self): def proto(self): """Socket protocol (always 0x00 in this implementation).""" return 0 + + +class timeout(TimeoutError): + """TimeoutError class. An instance of this error will be raised by recv_into() if + the timeout has elapsed and we haven't received any data yet.""" + + def __init__(self, msg): + super().__init__(msg) From 22bf906f2f2c1e4855d617dadd56f25a7cc34905 Mon Sep 17 00:00:00 2001 From: Dorin Date: Mon, 15 Jan 2024 19:01:27 +0200 Subject: [PATCH 17/18] Fix formatting --- adafruit_wiznet5k/adafruit_wiznet5k_socket.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py index 195ecad..6c26351 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py @@ -567,7 +567,9 @@ def recv_into(self, buffer: bytearray, nbytes: int = 0, flags: int = 0) -> int: # _readline if len(self._buffer) > 0: bytes_to_read = min(num_to_read, len(self._buffer)) - buffer[num_read : num_read + bytes_to_read] = self._buffer[:bytes_to_read] + buffer[num_read : num_read + bytes_to_read] = self._buffer[ + :bytes_to_read + ] num_read += bytes_to_read num_to_read -= bytes_to_read self._buffer = self._buffer[bytes_to_read:] @@ -579,9 +581,13 @@ def recv_into(self, buffer: bytearray, nbytes: int = 0, flags: int = 0) -> int: last_read_time = time.monotonic() bytes_to_read = min(num_to_read, num_avail) if self._sock_type == SOCK_STREAM: - bytes_read = _the_interface.socket_read(self._socknum, bytes_to_read)[1] + bytes_read = _the_interface.socket_read( + self._socknum, bytes_to_read + )[1] else: - bytes_read = _the_interface.read_udp(self._socknum, bytes_to_read)[1] + bytes_read = _the_interface.read_udp(self._socknum, bytes_to_read)[ + 1 + ] buffer[num_read : num_read + len(bytes_read)] = bytes_read num_read += len(bytes_read) num_to_read -= len(bytes_read) From e200b6c47cd0daf6da1d4c781227553fa9d1c875 Mon Sep 17 00:00:00 2001 From: Dorin Date: Mon, 15 Jan 2024 19:16:13 +0200 Subject: [PATCH 18/18] Fix linting --- adafruit_wiznet5k/adafruit_wiznet5k_socket.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py index 6c26351..7fd3673 100644 --- a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py @@ -70,16 +70,16 @@ def getdefaulttimeout() -> Optional[float]: return _default_socket_timeout -def setdefaulttimeout(timeout: Optional[float]) -> None: +def setdefaulttimeout(_timeout: Optional[float]) -> None: """ Set the default timeout in seconds (float) for new socket objects. When the socket module is first imported, the default is None. See settimeout() for possible values and their respective meanings. - :param Optional[float] timeout: The default timeout in seconds or None. + :param Optional[float] _timeout: The default timeout in seconds or None. """ global _default_socket_timeout # pylint: disable=global-statement - if timeout is None or (isinstance(timeout, (int, float)) and timeout >= 0): + if _timeout is None or (isinstance(timeout, (int, float)) and timeout >= 0): _default_socket_timeout = timeout else: raise ValueError("Timeout must be None, 0.0 or a positive numeric value.") @@ -450,8 +450,8 @@ def send(self, data: Union[bytes, bytearray]) -> int: :return int: Number of bytes sent. """ - timeout = 0 if self._timeout is None else self._timeout - bytes_sent = _the_interface.socket_write(self._socknum, data, timeout) + _timeout = 0 if self._timeout is None else self._timeout + bytes_sent = _the_interface.socket_write(self._socknum, data, _timeout) gc.collect() return bytes_sent @@ -762,3 +762,6 @@ class timeout(TimeoutError): def __init__(self, msg): super().__init__(msg) + + +# pylint: enable=unused-argument, redefined-builtin, invalid-name