Skip to content

Commit ccb0c36

Browse files
authored
Merge pull request #156 from kevin-tritz/main
replace time.monotonic with adafruit_ticks to preserve timing precision
2 parents a09a95a + 4ab7141 commit ccb0c36

File tree

5 files changed

+55
-34
lines changed

5 files changed

+55
-34
lines changed

adafruit_wiznet5k/adafruit_wiznet5k.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import time
5252
import gc
5353
from micropython import const
54+
from adafruit_ticks import ticks_ms, ticks_diff
5455

5556
from adafruit_bus_device.spi_device import SPIDevice
5657
import adafruit_wiznet5k.adafruit_wiznet5k_dhcp as dhcp
@@ -249,8 +250,9 @@ def __init__(
249250
self.udp_from_port = [0] * self.max_sockets
250251

251252
# Wait to give the Ethernet link to initialise.
252-
stop_time = time.monotonic() + 5
253-
while time.monotonic() < stop_time:
253+
start_time = ticks_ms()
254+
timeout = 5000
255+
while ticks_diff(ticks_ms(), start_time) < timeout:
254256
if self.link_status:
255257
break
256258
debug_msg("Ethernet link is down…", self._debug)
@@ -764,9 +766,10 @@ def socket_close(self, socket_num: int) -> None:
764766
self._sock_num_in_range(socket_num)
765767
self._write_sncr(socket_num, _CMD_SOCK_CLOSE)
766768
debug_msg(" Waiting for socket to close…", self._debug)
767-
timeout = time.monotonic() + 5.0
769+
start = ticks_ms()
770+
timeout = 5000
768771
while self._read_snsr(socket_num) != SNSR_SOCK_CLOSED:
769-
if time.monotonic() > timeout:
772+
if ticks_diff(ticks_ms(), start) > timeout:
770773
raise RuntimeError(
771774
"Wiznet5k failed to close socket, status = {}.".format(
772775
self._read_snsr(socket_num)
@@ -889,15 +892,15 @@ def socket_write(
889892
bytes_to_write = _SOCK_SIZE
890893
else:
891894
bytes_to_write = len(buffer)
892-
stop_time = time.monotonic() + timeout
895+
start_time = ticks_ms()
893896

894897
# If buffer is available, start the transfer
895898
free_size = self._get_tx_free_size(socket_num)
896899
while free_size < bytes_to_write:
897900
free_size = self._get_tx_free_size(socket_num)
898901
status = self.socket_status(socket_num)
899902
if status not in (SNSR_SOCK_ESTABLISHED, SNSR_SOCK_CLOSE_WAIT) or (
900-
timeout and time.monotonic() > stop_time
903+
timeout and ticks_diff(ticks_ms(), start_time) / 1000 > timeout
901904
):
902905
raise RuntimeError("Unable to write data to the socket.")
903906

@@ -920,7 +923,7 @@ def socket_write(
920923
_SNSR_SOCK_CLOSING,
921924
):
922925
raise RuntimeError("No data was sent, socket was closed.")
923-
if timeout and time.monotonic() > stop_time:
926+
if timeout and ticks_diff(ticks_ms(), start_time) / 1000 > timeout:
924927
raise RuntimeError("Operation timed out. No data sent.")
925928
if self.read_snir(socket_num) & SNIR_TIMEOUT:
926929
self.write_snir(socket_num, SNIR_TIMEOUT)

adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import time
3030
from random import randint
3131
from micropython import const
32+
from adafruit_ticks import ticks_ms, ticks_diff, ticks_add
3233
from adafruit_wiznet5k.adafruit_wiznet5k_debug import ( # pylint: disable=ungrouped-imports
3334
debug_msg,
3435
)
@@ -152,6 +153,7 @@ def __init__(
152153
self._dhcp_state = _STATE_INIT
153154
self._transaction_id = randint(1, 0x7FFFFFFF)
154155
self._start_time = 0.0
156+
self._start_ticks = 0
155157
self._blocking = False
156158
self._renew = None
157159

@@ -209,13 +211,14 @@ def _dsm_reset(self) -> None:
209211
self._renew = None
210212
self._increment_transaction_id()
211213
self._start_time = time.monotonic()
214+
self._start_ticks = ticks_ms()
212215

213216
def _increment_transaction_id(self) -> None:
214217
"""Increment the transaction ID and roll over from 0x7fffffff to 0."""
215218
debug_msg("Incrementing transaction ID", self._debug)
216219
self._transaction_id = (self._transaction_id + 1) & 0x7FFFFFFF
217220

218-
def _next_retry_time(self, *, attempt: int, interval: int = 4) -> float:
221+
def _next_retry_time(self, *, attempt: int, interval: int = 4) -> int:
219222
"""Calculate a retry stop time.
220223
221224
The interval is calculated as an exponential fallback with a random variation to
@@ -227,18 +230,18 @@ def _next_retry_time(self, *, attempt: int, interval: int = 4) -> float:
227230
:param int interval: The base retry interval in seconds. Defaults to 4 as per the
228231
DHCP standard for Ethernet connections. Minimum value 2, defaults to 4.
229232
230-
:returns float: The timeout in time.monotonic() seconds.
233+
:returns int: The timeout in ticks_ms milliseconds.
231234
232235
:raises ValueError: If the interval is not > 1 second as this could return a zero or
233236
negative delay.
234237
"""
235238
debug_msg("Calculating next retry time and incrementing retries.", self._debug)
236239
if interval <= 1:
237240
raise ValueError("Retry interval must be > 1 second.")
238-
delay = 2**attempt * interval + randint(-1, 1) + time.monotonic()
241+
delay = (2**attempt * interval + randint(-1, 1)) * 1000
239242
return delay
240243

241-
def _receive_dhcp_response(self, socket_num: int, timeout: float) -> int:
244+
def _receive_dhcp_response(self, socket_num: int, timeout: int) -> int:
242245
"""
243246
Receive data from the socket in response to a DHCP query.
244247
@@ -249,12 +252,13 @@ def _receive_dhcp_response(self, socket_num: int, timeout: float) -> int:
249252
maximum packet size is limited by the size of the global buffer.
250253
251254
:param int socket_num: Socket to read from.
252-
:param float timeout: time.monotonic at which attempt should time out.
255+
:param int timeout: ticks_ms interval at which attempt should time out.
253256
254257
:returns int: The number of bytes stored in the global buffer.
255258
"""
256259
debug_msg("Receiving a DHCP response.", self._debug)
257-
while time.monotonic() < timeout:
260+
start_time = ticks_ms()
261+
while ticks_diff(ticks_ms(), start_time) < timeout:
258262
# DHCP returns the query plus additional data. The query length is 236 bytes.
259263
if self._eth.socket_available(socket_num, _SNMR_UDP) > 236:
260264
bytes_count, bytes_read = self._eth.read_udp(socket_num, _BUFF_LENGTH)
@@ -285,9 +289,11 @@ def _process_messaging_states(self, *, message_type: int):
285289
self._dhcp_state = _STATE_INIT
286290
elif message_type == _DHCP_ACK:
287291
debug_msg("Message is ACK, setting FSM state to BOUND.", self._debug)
288-
self._t1 = self._start_time + self._lease // 2
289-
self._t2 = self._start_time + self._lease - self._lease // 8
290-
self._lease += self._start_time
292+
self._t1 = ticks_add(self._start_ticks, self._lease // 2)
293+
self._t2 = ticks_diff(
294+
ticks_add(self._start_ticks, self._lease), self._lease // 8
295+
)
296+
self._lease = ticks_add(self._lease, self._start_ticks)
291297
self._increment_transaction_id()
292298
if not self._renew:
293299
self._eth.ifconfig = (
@@ -330,13 +336,14 @@ def _handle_dhcp_message(self) -> int:
330336
else:
331337
dhcp_server = _BROADCAST_SERVER_ADDR
332338
sock_num = None
333-
deadline = time.monotonic() + 5.0
339+
deadline = 5000
340+
start_time = ticks_ms()
334341
try:
335342
while sock_num is None:
336343
sock_num = self._eth.get_socket()
337344
if sock_num == 0xFF:
338345
sock_num = None
339-
if time.monotonic() > deadline:
346+
if ticks_diff(ticks_ms(), start_time) > deadline:
340347
raise RuntimeError("Unable to initialize UDP socket.")
341348

342349
self._eth.src_port = 68
@@ -349,7 +356,8 @@ def _handle_dhcp_message(self) -> int:
349356
for attempt in range(4): # Initial attempt plus 3 retries.
350357
self._eth.socket_write(sock_num, _BUFF[:message_length])
351358
next_resend = self._next_retry_time(attempt=attempt)
352-
while time.monotonic() < next_resend:
359+
start_time = ticks_ms()
360+
while ticks_diff(ticks_ms(), start_time) < next_resend:
353361
if self._receive_dhcp_response(sock_num, next_resend):
354362
try:
355363
msg_type_in = self._parse_dhcp_response()
@@ -382,17 +390,17 @@ def _dhcp_state_machine(self, *, blocking: bool = False) -> None:
382390
self._blocking = blocking
383391
while True:
384392
if self._dhcp_state == _STATE_BOUND:
385-
now = time.monotonic()
386-
if now < self._t1:
393+
now = ticks_ms()
394+
if ticks_diff(now, self._t1) < 0:
387395
debug_msg("No timers have expired. Exiting FSM.", self._debug)
388396
return
389-
if now > self._lease:
397+
if ticks_diff(now, self._lease) > 0:
390398
debug_msg(
391399
"Lease has expired, switching state to INIT.", self._debug
392400
)
393401
self._blocking = True
394402
self._dhcp_state = _STATE_INIT
395-
elif now > self._t2:
403+
elif ticks_diff(now, self._t2) > 0:
396404
debug_msg(
397405
"T2 has expired, switching state to REBINDING.", self._debug
398406
)
@@ -407,13 +415,15 @@ def _dhcp_state_machine(self, *, blocking: bool = False) -> None:
407415
debug_msg("FSM state is RENEWING.", self._debug)
408416
self._renew = "renew"
409417
self._start_time = time.monotonic()
418+
self._start_ticks = ticks_ms()
410419
self._dhcp_state = _STATE_REQUESTING
411420

412421
if self._dhcp_state == _STATE_REBINDING:
413422
debug_msg("FSM state is REBINDING.", self._debug)
414423
self._renew = "rebind"
415424
self.dhcp_server_ip = _BROADCAST_SERVER_ADDR
416425
self._start_time = time.monotonic()
426+
self._start_ticks = ticks_ms()
417427
self._dhcp_state = _STATE_REQUESTING
418428

419429
if self._dhcp_state == _STATE_INIT:
@@ -486,7 +496,9 @@ def option_writer(
486496
# Transaction ID (xid)
487497
_BUFF[4:8] = self._transaction_id.to_bytes(4, "big")
488498
# Seconds elapsed
489-
_BUFF[8:10] = int(time.monotonic() - self._start_time).to_bytes(2, "big")
499+
_BUFF[8:10] = int(ticks_diff(ticks_ms(), self._start_ticks) / 1000).to_bytes(
500+
2, "big"
501+
)
490502
# Flags (only bit 0 is used, all other bits must be 0)
491503
if broadcast:
492504
_BUFF[10] = 0b10000000

adafruit_wiznet5k/adafruit_wiznet5k_dns.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import time
2727
from random import getrandbits
2828
from micropython import const
29+
from adafruit_ticks import ticks_ms, ticks_diff
2930

3031
_QUERY_FLAG = const(0x00)
3132
_OPCODE_STANDARD_QUERY = const(0x00)
@@ -261,9 +262,10 @@ def gethostbyname(self, hostname: bytes) -> Union[int, bytes]:
261262
ipaddress = -1
262263
for _ in range(5):
263264
# wait for a response
264-
socket_timeout = time.monotonic() + 5.0
265+
socket_timeout = 5000
266+
start_time = ticks_ms()
265267
while not self._iface.socket_available(dns_socket, 0x02):
266-
if time.monotonic() > socket_timeout:
268+
if ticks_diff(ticks_ms(), start_time) > socket_timeout:
267269
_debug_print(
268270
debug=self._debug,
269271
message="* DNS ERROR: Did not receive DNS response (socket timeout).",

adafruit_wiznet5k/adafruit_wiznet5k_socket.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
pass
2626

2727
import gc
28-
import time
2928
from sys import byteorder
3029

3130
from micropython import const
31+
from adafruit_ticks import ticks_ms, ticks_diff
3232

3333
import adafruit_wiznet5k as wiznet5k
3434

@@ -396,12 +396,15 @@ def accept(
396396
the connection, and address is the address bound to the socket on the other
397397
end of the connection.
398398
"""
399-
stamp = time.monotonic()
399+
stamp = ticks_ms()
400400
while self._status not in (
401401
wiznet5k.adafruit_wiznet5k.SNSR_SOCK_SYNRECV,
402402
wiznet5k.adafruit_wiznet5k.SNSR_SOCK_ESTABLISHED,
403403
):
404-
if self._timeout and 0 < self._timeout < time.monotonic() - stamp:
404+
if (
405+
self._timeout
406+
and 0 < self._timeout < ticks_diff(ticks_ms(), stamp) / 1000
407+
):
405408
raise TimeoutError("Failed to accept connection.")
406409
if self._status == wiznet5k.adafruit_wiznet5k.SNSR_SOCK_CLOSED:
407410
self.close()
@@ -564,7 +567,7 @@ def recv_into(self, buffer: bytearray, nbytes: int = 0, flags: int = 0) -> int:
564567
if not 0 <= nbytes <= len(buffer):
565568
raise ValueError("nbytes must be 0 to len(buffer)")
566569

567-
last_read_time = time.monotonic()
570+
last_read_time = ticks_ms()
568571
num_to_read = len(buffer) if nbytes == 0 else nbytes
569572
num_read = 0
570573
while num_to_read > 0:
@@ -583,7 +586,7 @@ def recv_into(self, buffer: bytearray, nbytes: int = 0, flags: int = 0) -> int:
583586

584587
num_avail = self._available()
585588
if num_avail > 0:
586-
last_read_time = time.monotonic()
589+
last_read_time = ticks_ms()
587590
bytes_to_read = min(num_to_read, num_avail)
588591
if self._sock_type == SOCK_STREAM:
589592
bytes_read = _the_interface.socket_read(
@@ -606,7 +609,7 @@ def recv_into(self, buffer: bytearray, nbytes: int = 0, flags: int = 0) -> int:
606609
if self._timeout == 0:
607610
# non-blocking mode
608611
break
609-
if time.monotonic() - last_read_time > self._timeout:
612+
if ticks_diff(ticks_ms(), last_read_time) / 1000 > self._timeout:
610613
raise timeout("timed out")
611614
return num_read
612615

@@ -644,7 +647,7 @@ def _readline(self) -> bytes:
644647
645648
:return bytes: The data read from the socket.
646649
"""
647-
stamp = time.monotonic()
650+
stamp = ticks_ms()
648651
while b"\r\n" not in self._buffer:
649652
avail = self._available()
650653
if avail:
@@ -655,7 +658,7 @@ def _readline(self) -> bytes:
655658
if (
656659
self._timeout
657660
and not avail
658-
and 0 < self._timeout < time.monotonic() - stamp
661+
and 0 < self._timeout < ticks_diff(ticks_ms(), stamp) / 1000
659662
):
660663
self.close()
661664
raise RuntimeError("Didn't receive response, failing out...")

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44

55
Adafruit-Blinka
66
adafruit-circuitpython-busdevice
7+
adafruit-circuitpython-ticks

0 commit comments

Comments
 (0)