Skip to content

Add W5100S chip and NTP #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
162 changes: 128 additions & 34 deletions adafruit_wiznet5k/adafruit_wiznet5k.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@
REG_GAR = const(0x0001) # Gateway IP Address
REG_SUBR = const(0x0005) # Subnet Mask Address
REG_VERSIONR_W5500 = const(0x0039) # W5500 Silicon Version
REG_VERSIONR_W5100S = const(0x0080) # W5100S Silicon Version
REG_SHAR = const(0x0009) # Source Hardware Address
REG_SIPR = const(0x000F) # Source IP Address
REG_PHYCFGR = const(0x002E) # W5500 PHY Configuration
REG_PHYCFGR_W5100S = const(0x003C) # W5100S PHY Configuration

# Wiznet5k Socket Registers
REG_SNMR = const(0x0000) # Socket n Mode
Expand Down Expand Up @@ -99,6 +101,7 @@

CH_SIZE = const(0x100)
SOCK_SIZE = const(0x800) # MAX W5k socket size
SOCK_MASK = const(0x7FF)
# Register commands
MR_RST = const(0x80) # Mode Register RST
# Socket mode register
Expand All @@ -116,6 +119,7 @@

# Maximum number of sockets to support, differs between chip versions.
W5200_W5500_MAX_SOCK_NUM = const(0x08)
W5100_MAX_SOCK_NUM = const(0x04)
SOCKET_INVALID = const(255)

# UDP socket struct.
Expand Down Expand Up @@ -179,8 +183,19 @@ def __init__(
self.src_port = 0
self._dns = 0

# Set DHCP
# First, wait link status is on
# to avoid the code during DHCP, socket listen, connect ...
# assert self.link_status, "Ethernet cable disconnected!"
start_time = time.monotonic()
while True:
if self.link_status or ((time.monotonic() - start_time) > 5):
break
time.sleep(1)
if self._debug:
print("My Link is:", self.link_status)
self._dhcp_client = None

# Set DHCP
if is_dhcp:
ret = self.set_dhcp(hostname, dhcp_timeout)
if ret != 0:
Expand All @@ -198,16 +213,6 @@ def set_dhcp(self, hostname=None, response_timeout=30):
if self._debug:
print("* Initializing DHCP")

# First, wait link status is on
# to avoid the code during DHCP - assert self.link_status, "Ethernet cable disconnected!"
start_time = time.monotonic()
while True:
if self.link_status or ((time.monotonic() - start_time) > 5):
break
time.sleep(1)
if self._debug:
print("My Link is:", self.link_status)

# Return IP assigned by DHCP
self._dhcp_client = dhcp.DHCP(
self, self.mac_address, hostname, response_timeout, debug=self._debug
Expand Down Expand Up @@ -251,6 +256,8 @@ def max_sockets(self):
"""Returns max number of sockets supported by chip."""
if self._chip_type == "w5500":
return W5200_W5500_MAX_SOCK_NUM
if self._chip_type == "w5100s":
return W5100_MAX_SOCK_NUM
return -1

@property
Expand Down Expand Up @@ -319,6 +326,9 @@ def link_status(self):
if self._chip_type == "w5500":
data = self.read(REG_PHYCFGR, 0x00)
return data[0] & 0x01
if self._chip_type == "w5100s":
data = self.read(REG_PHYCFGR_W5100S, 0x00)
return data[0] & 0x01
return 0

def remote_port(self, socket_num):
Expand Down Expand Up @@ -367,25 +377,47 @@ def _w5100_init(self):
self.write(0x1E, ctrl_byte, 2)
self.write(0x1F, ctrl_byte, 2)
else:
return 0
# Detect if chip is Wiznet W5100S
if self.detect_w5100s() == 1:
pass
else:
return 0
return 1

def detect_w5500(self):
"""Detects W5500 chip."""
self._chip_type = "w5500"
assert self.sw_reset() == 0, "Chip not reset properly!"
self._write_mr(0x08)
assert self._read_mr()[0] == 0x08, "Expected 0x08."
# assert self._read_mr()[0] == 0x08, "Expected 0x08."
if self._read_mr()[0] != 0x08:
return -1

self._write_mr(0x10)
assert self._read_mr()[0] == 0x10, "Expected 0x10."
# assert self._read_mr()[0] == 0x10, "Expected 0x10."
if self._read_mr()[0] != 0x10:
return -1

self._write_mr(0x00)
assert self._read_mr()[0] == 0x00, "Expected 0x00."
# assert self._read_mr()[0] == 0x00, "Expected 0x00."
if self._read_mr()[0] != 0x00:
return -1

if self.read(REG_VERSIONR_W5500, 0x00)[0] != 0x04:
return -1
self._chip_type = "w5500"
self._ch_base_msb = 0x10
# self._chip_type = "w5500"
# self._ch_base_msb = 0x10
return 1

def detect_w5100s(self):
"""Detects W5100S chip."""
self._chip_type = "w5100s"
# sw reset
assert self.sw_reset() == 0, "Chip not reset properly!"
if self.read(REG_VERSIONR_W5100S, 0x00)[0] != 0x51:
return -1

self._ch_base_msb = 0x0400
return 1

def sw_reset(self):
Expand All @@ -396,7 +428,9 @@ def sw_reset(self):
mode_reg = self._read_mr()
self._write_mr(0x80)
mode_reg = self._read_mr()
if mode_reg[0] != 0x00:

# W5100S case => 0x03
if (mode_reg[0] != 0x00) and (mode_reg[0] != 0x03):
return -1
return 0

Expand All @@ -418,9 +452,16 @@ def read(self, addr, callback, length=1, buffer=None):

"""
with self._device as bus_device:
bus_device.write(bytes([addr >> 8])) # pylint: disable=no-member
bus_device.write(bytes([addr & 0xFF])) # pylint: disable=no-member
bus_device.write(bytes([callback])) # pylint: disable=no-member
if self._chip_type == "w5500":
bus_device.write(bytes([addr >> 8])) # pylint: disable=no-member
bus_device.write(bytes([addr & 0xFF])) # pylint: disable=no-member
bus_device.write(bytes([callback])) # pylint: disable=no-member
else:
# if self._chip_type == "w5100s":
bus_device.write(bytes([0x0F])) # pylint: disable=no-member
bus_device.write(bytes([addr >> 8])) # pylint: disable=no-member
bus_device.write(bytes([addr & 0xFF])) # pylint: disable=no-member

if buffer is None:
self._rxbuf = bytearray(length)
bus_device.readinto(self._rxbuf) # pylint: disable=no-member
Expand All @@ -437,9 +478,15 @@ def write(self, addr, callback, data):

"""
with self._device as bus_device:
bus_device.write(bytes([addr >> 8])) # pylint: disable=no-member
bus_device.write(bytes([addr & 0xFF])) # pylint: disable=no-member
bus_device.write(bytes([callback])) # pylint: disable=no-member
if self._chip_type == "w5500":
bus_device.write(bytes([addr >> 8])) # pylint: disable=no-member
bus_device.write(bytes([addr & 0xFF])) # pylint: disable=no-member
bus_device.write(bytes([callback])) # pylint: disable=no-member
else:
# if self._chip_type == "w5100s":
bus_device.write(bytes([0xF0])) # pylint: disable=no-member
bus_device.write(bytes([addr >> 8])) # pylint: disable=no-member
bus_device.write(bytes([addr & 0xFF])) # pylint: disable=no-member

if hasattr(data, "from_bytes"):
bus_device.write(bytes([data])) # pylint: disable=no-member
Expand Down Expand Up @@ -687,14 +734,28 @@ def socket_read(self, socket_num, length):
# Read the starting save address of the received data
ptr = self._read_snrx_rd(socket_num)

# Read data from the starting address of snrx_rd
ctrl_byte = 0x18 + (socket_num << 5)
if self._chip_type == "w5500":
# Read data from the starting address of snrx_rd
ctrl_byte = 0x18 + (socket_num << 5)

resp = self.read(ptr, ctrl_byte, ret)
resp = self.read(ptr, ctrl_byte, ret)
else:
# if self._chip_type == "w5100s":
offset = ptr & SOCK_MASK
src_addr = offset + (socket_num * SOCK_SIZE + 0x6000)
if offset + ret > SOCK_SIZE:
size = SOCK_SIZE - offset
resp1 = self.read(src_addr, 0x00, size)
size = ret - size
src_addr = socket_num * SOCK_SIZE + 0x6000
resp2 = self.read(src_addr, 0x00, size)
resp = resp1 + resp2
else:
resp = self.read(src_addr, 0x00, ret)

# After reading the received data, update Sn_RX_RD to the increased
# value as many as the reading size.
ptr += ret
ptr = (ptr + ret) & 0xFFFF
self._write_snrx_rd(socket_num, ptr)

# Notify the W5k of the updated Sn_Rx_RD
Expand Down Expand Up @@ -740,8 +801,29 @@ def socket_write(self, socket_num, buffer, timeout=0):

# Read the starting address for saving the transmitting data.
ptr = self._read_sntx_wr(socket_num)
offset = ptr & 0x07FF
dst_addr = offset + (socket_num * 2048 + 0x8000)
offset = ptr & SOCK_MASK
if self._chip_type == "w5500":
dst_addr = offset + (socket_num * SOCK_SIZE + 0x8000)

txbuf = buffer[:ret]
cntl_byte = 0x14 + (socket_num << 5)
self.write(dst_addr, cntl_byte, txbuf)

else:
# if self._chip_type == "w5100s":
dst_addr = offset + (socket_num * SOCK_SIZE + 0x4000)

if offset + ret > SOCK_SIZE:
size = SOCK_SIZE - offset
txbuf = buffer[0:size]
self.write(dst_addr, 0x00, txbuf)
txbuf = buffer[size:ret]
size = ret - size
dst_addr = socket_num * SOCK_SIZE + 0x4000
self.write(dst_addr, 0x00, txbuf)
else:
txbuf = buffer[:ret]
self.write(dst_addr, 0x00, buffer[:ret])

# update sn_tx_wr to the value + data size
ptr = (ptr + ret) & 0xFFFF
Expand Down Expand Up @@ -864,10 +946,22 @@ def _read_snmr(self, sock):

def _write_socket(self, sock, address, data):
"""Write to a W5k socket register."""
cntl_byte = (sock << 5) + 0x0C
return self.write(address, cntl_byte, data)
if self._chip_type == "w5500":
cntl_byte = (sock << 5) + 0x0C
return self.write(address, cntl_byte, data)
if self._chip_type == "w5100s":
cntl_byte = 0
return self.write(
self._ch_base_msb + sock * CH_SIZE + address, cntl_byte, data
)
return None

def _read_socket(self, sock, address):
"""Read a W5k socket register."""
cntl_byte = (sock << 5) + 0x08
return self.read(address, cntl_byte)
if self._chip_type == "w5500":
cntl_byte = (sock << 5) + 0x08
return self.read(address, cntl_byte)
if self._chip_type == "w5100s":
cntl_byte = 0
return self.read(self._ch_base_msb + sock * CH_SIZE + address, cntl_byte)
return None
64 changes: 64 additions & 0 deletions adafruit_wiznet5k/adafruit_wiznet5k_ntp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# SPDX-FileCopyrightText: 2019 Brent Rubell for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
`wiznet5k_ntp`
================================================================================

Network Time Protocol (NTP) helper for CircuitPython

* Author(s): Brent Rubell, irinakim

Implementation Notes
--------------------
**Hardware:**
**Software and Dependencies:**


"""
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
:param iface: Wiznet 5k object
:param str ntp_address: The hostname of the NTP server
:param int utc: Numbers of hours to offset time from UTC
:param bool debug: Enable debugging output.
"""

def __init__(self, iface, ntp_address, utc, debug=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):
"""
Get the time from the NTP server
:return: time in seconds since the epoch
"""
self._sock.bind((None, 50001))
self._sock.sendto(self._pkt_buf_, (self._ntp_server, 123))
while True:
data = self._sock.recv()
if data:
sec = data[40:44]
int_cal = int.from_bytes(sec, "big")
cal = int_cal - 2208988800 + self._utc * 3600
cal = time.localtime(cal)
return cal
13 changes: 8 additions & 5 deletions adafruit_wiznet5k/adafruit_wiznet5k_wsgiserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,6 @@ def set_interface(iface):
socket.set_interface(iface)


# Maximum number of sockets for the web server (number of connections we can hold)
MAX_SOCK_NUM = const(6)

# pylint: disable=invalid-name
class WSGIServer:
"""
Expand All @@ -62,14 +59,20 @@ def __init__(self, port=80, debug=False, application=None):

self._response_status = None
self._response_headers = []
if _the_interface.chip == "w5100s":
self.MAX_SOCK_NUM = const(2)
else:
self.MAX_SOCK_NUM = const(6)
if self._debug:
print("Max sockets: ", self.MAX_SOCK_NUM)

def start(self):
"""
Starts the server and begins listening for incoming connections.
Call update_poll in the main loop for the application callable to be
invoked on receiving an incoming request.
"""
for _ in range(MAX_SOCK_NUM):
for _ in range(self.MAX_SOCK_NUM):
new_sock = socket.socket()
new_sock.settimeout(self._timeout)
new_sock.bind((None, self.port))
Expand All @@ -95,7 +98,7 @@ def update_poll(self):
for sock in self._client_sock:
if sock.status == wiznet5k.adafruit_wiznet5k.SNSR_SOCK_CLOSED:
self._client_sock.remove(sock)
for _ in range(len(self._client_sock), MAX_SOCK_NUM):
for _ in range(len(self._client_sock), self.MAX_SOCK_NUM):
try:
new_sock = socket.socket()
new_sock.settimeout(self._timeout)
Expand Down