Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
65 changes: 64 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,69 @@ wifitest.adafruit.com.

print("Done!")

This example demonstrates a simple web server that allows setting the Neopixel color.

.. code-block:: python

import board
import busio
import digitalio
import neopixel

from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K
import adafruit_wiznet5k.adafruit_wiznet5k_wsgiserver as server
from adafruit_wsgi.wsgi_app import WSGIApp

print("Wiznet5k Web Server Test")

# Status LED
led = neopixel.NeoPixel(board.NEOPIXEL, 1)
led.brightness = 0.3
led[0] = (0, 0, 255)

# W5500 connections
cs = digitalio.DigitalInOut(board.D10)
spi_bus = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)

# Initialize ethernet interface with DHCP and the MAC we have from the 24AA02E48
eth = WIZNET5K(spi_bus, cs)

# Here we create our application, registering the
# following functions to be called on specific HTTP GET requests routes

web_app = WSGIApp()


@web_app.route("/led/<r>/<g>/<b>")
def led_on(request, r, g, b):
print("led handler")
led.fill((int(r), int(g), int(b)))
return ("200 OK", [], ["led set!"])

@web_app.route("/")
def root(request):
print("root handler")
return ("200 OK", [], ["root document"])

@web_app.route("/large")
def large(request):
print("large handler")
return ("200 OK", [], ["*-.-" * 2000])


# Here we setup our server, passing in our web_app as the application
server.set_interface(eth)
wsgiServer = server.WSGIServer(80, application=web_app)

print("Open this IP in your browser: ", eth.pretty_ip(eth.ip_address))

# Start the server
wsgiServer.start()
while True:
# Our main loop where we have the server poll for incoming requests
wsgiServer.update_poll()
# Could do any other background tasks here, like reading sensors

Contributing
============

Expand All @@ -122,4 +185,4 @@ with `CircuitPython <https://circuitpython.org/>`_ and made changes so it works
5500 Ethernet interface <https://circuitpython.readthedocs.io/en/latest/shared-bindings/wiznet/wiznet5k.html>`_ and CPython's `Socket low-level
networking interface module <https://docs.python.org/3.8/library/socket.html>`_.

This open source code is licensed under the LGPL license (see LICENSE for details).
This open source code is licensed under the LGPL license (see LICENSE for details).
47 changes: 37 additions & 10 deletions adafruit_wiznet5k/adafruit_wiznet5k.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
# Copyright (c) 2008 Bjoern Hartmann
# Copyright 2018 Paul Stoffregen
# Modified by Brent Rubell for Adafruit Industries, 2020
# Copyright (c) 2021 Patrick Van Oosterwijck
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
Expand All @@ -29,7 +30,8 @@

Pure-Python interface for WIZNET 5k ethernet modules.

* Author(s): WIZnet, Arduino LLC, Bjoern Hartmann, Paul Stoffregen, Brent Rubell
* Author(s): WIZnet, Arduino LLC, Bjoern Hartmann, Paul Stoffregen, Brent Rubell,
Patrick Van Oosterwijck

Implementation Notes
--------------------
Expand Down Expand Up @@ -257,7 +259,7 @@ def get_host_by_name(self, hostname):
print("* Get host by name")
if isinstance(hostname, str):
hostname = bytes(hostname, "utf-8")
self._src_port = int(time.monotonic())
self._src_port = int(time.monotonic()) & 0xFFFF
# Return IP assigned by DHCP
_dns_client = dns.DNS(self, self._dns, debug=self._debug)
ret = _dns_client.gethostbyname(hostname)
Expand Down Expand Up @@ -563,16 +565,15 @@ def _send_socket_cmd(self, socket, cmd):
if self._debug:
print("waiting for sncr to clear...")

def get_socket(self, sockets):
def get_socket(self):
"""Requests, allocates and returns a socket from the W5k
chip. Returned socket number may not exceed max_sockets.
:parm int socket_num: Desired socket number
"""
if self._debug:
print("*** Get socket")

sock = 0
for _sock in range(len(sockets), self.max_sockets):
for _sock in range(self.max_sockets):
status = self.socket_status(_sock)
if (
status[0] == SNSR_SOCK_CLOSED
Expand All @@ -586,14 +587,42 @@ def get_socket(self, sockets):
print("Allocated socket #{}".format(sock))
return sock

def socket_listen(self, socket_num, port):
"""Start listening on a socket (TCP mode only).
:parm int socket_num: socket number
:parm int port: port to listen on
"""
assert self.link_status, "Ethernet cable disconnected!"
if self._debug:
print(
"* Listening on port={}, ip={}".format(
port, self.pretty_ip(self.ip_address)
)
)
# Initialize a socket and set the mode
self._src_port = port
res = self.socket_open(socket_num, conn_mode=SNMR_TCP)
if res == 1:
raise RuntimeError("Failed to initalize the socket.")
# Send listen command
self._send_socket_cmd(socket_num, CMD_SOCK_LISTEN)
# Wait until ready
status = [SNSR_SOCK_CLOSED]
while status[0] != SNSR_SOCK_LISTEN:
status = self._read_snsr(socket_num)
if status[0] == SNSR_SOCK_CLOSED:
raise RuntimeError("Listening socket closed.")


def socket_open(self, socket_num, conn_mode=SNMR_TCP):
"""Opens a TCP or UDP socket. By default, we use
'conn_mode'=SNMR_TCP but we may also use SNMR_UDP.
"""
assert self.link_status, "Ethernet cable disconnected!"
if self._debug:
print("*** Opening socket %d" % socket_num)
if self._read_snsr(socket_num)[0] == SNSR_SOCK_CLOSED:
status = self._read_snsr(socket_num)[0]
if status in (SNSR_SOCK_CLOSED, SNSR_SOCK_FIN_WAIT, SNSR_SOCK_CLOSE_WAIT):
if self._debug:
print("* Opening W5k Socket, protocol={}".format(conn_mode))
time.sleep(0.00025)
Expand Down Expand Up @@ -719,7 +748,7 @@ def socket_write(self, socket_num, buffer):
dst_addr = offset + (socket_num * 2048 + 0x8000)

# update sn_tx_wr to the value + data size
ptr += len(buffer)
ptr = (ptr + len(buffer)) & 0xFFFF
self._write_sntx_wr(socket_num, ptr)

cntl_byte = 0x14 + (socket_num << 5)
Expand Down Expand Up @@ -826,12 +855,10 @@ def _read_sncr(self, sock):
def _read_snmr(self, sock):
return self._read_socket(sock, REG_SNMR)

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

def _read_socket(self, sock, address):
Expand Down
2 changes: 1 addition & 1 deletion adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ class DHCP:

# pylint: disable=too-many-arguments, too-many-instance-attributes, invalid-name
def __init__(
self, eth, mac_address, hostname=None, response_timeout=3, debug=False
self, eth, mac_address, hostname=None, response_timeout=5, debug=False
):
self._debug = debug
self._response_timeout = response_timeout
Expand Down
Empty file modified adafruit_wiznet5k/adafruit_wiznet5k_dns.py
100755 → 100644
Empty file.
61 changes: 32 additions & 29 deletions adafruit_wiznet5k/adafruit_wiznet5k_socket.py
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#
# Copyright (c) 2019 ladyada for Adafruit Industries
# Modified by Brent Rubell for Adafruit Industries, 2020
# Copyright (c) 2020 Patrick Van Oosterwijck
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
Expand All @@ -26,7 +27,7 @@

A socket compatible interface with the Wiznet5k module.

* Author(s): ladyada, Brent Rubell
* Author(s): ladyada, Brent Rubell, Patrick Van Oosterwijck

"""
import gc
Expand Down Expand Up @@ -64,8 +65,6 @@ def htons(x):
AF_INET = const(3)
NO_SOCKET_AVAIL = const(255)

# keep track of sockets we allocate
SOCKETS = []

# pylint: disable=too-many-arguments, unused-argument
def getaddrinfo(host, port, family=0, socktype=0, proto=0, flags=0):
Expand Down Expand Up @@ -122,9 +121,7 @@ def __init__(
self._buffer = b""
self._timeout = 0

self._socknum = _the_interface.get_socket(SOCKETS)
SOCKETS.append(self._socknum)
self.settimeout(self._timeout)
self._socknum = _the_interface.get_socket()

@property
def socknum(self):
Expand All @@ -135,22 +132,20 @@ def socknum(self):
def connected(self):
"""Returns whether or not we are connected to the socket."""
if self.socknum >= _the_interface.max_sockets:
return 0
return False
status = _the_interface.socket_status(self.socknum)[0]
if (
status == adafruit_wiznet5k.SNSR_SOCK_CLOSE_WAIT
and self.available()[0] == 0
):
if (status == adafruit_wiznet5k.SNSR_SOCK_CLOSE_WAIT
and self.available() == 0):
result = False
result = status not in (
adafruit_wiznet5k.SNSR_SOCK_CLOSED,
adafruit_wiznet5k.SNSR_SOCK_LISTEN,
adafruit_wiznet5k.SNSR_SOCK_CLOSE_WAIT,
adafruit_wiznet5k.SNSR_SOCK_FIN_WAIT,
)
if not result:
else:
result = status not in (
adafruit_wiznet5k.SNSR_SOCK_CLOSED,
adafruit_wiznet5k.SNSR_SOCK_LISTEN,
adafruit_wiznet5k.SNSR_SOCK_TIME_WAIT,
adafruit_wiznet5k.SNSR_SOCK_FIN_WAIT,
)
if not result and status != adafruit_wiznet5k.SNSR_SOCK_LISTEN:
self.close()
return result
return result

def getpeername(self):
Expand All @@ -167,6 +162,13 @@ def inet_aton(self, ip_string):
self._buffer = bytearray(self._buffer)
return self._buffer

def listen(self, port):
"""Listen on the specified port.
:param int port: The port to listen on.
"""
_the_interface.socket_listen(self.socknum, port)
self._buffer = b""

def connect(self, address, conntype=None):
"""Connect to a remote socket at address. (The format of address depends
on the address family — see above.)
Expand Down Expand Up @@ -209,11 +211,11 @@ def recv(self, bufsize=0): # pylint: disable=too-many-branches
avail = _the_interface.udp_remaining()
if avail:
if self._sock_type == SOCK_STREAM:
self._buffer += _the_interface.socket_read(self.socknum, avail)[
1
]
self._buffer += _the_interface.socket_read(
self.socknum, avail)[1]
elif self._sock_type == SOCK_DGRAM:
self._buffer += _the_interface.read_udp(self.socknum, avail)[1]
self._buffer += _the_interface.read_udp(
self.socknum, avail)[1]
else:
break
gc.collect()
Expand All @@ -235,10 +237,10 @@ def recv(self, bufsize=0): # pylint: disable=too-many-branches
stamp = time.monotonic()
if self._sock_type == SOCK_STREAM:
recv = _the_interface.socket_read(
self.socknum, min(to_read, avail)
)[1]
self.socknum, min(to_read, avail))[1]
elif self._sock_type == SOCK_DGRAM:
recv = _the_interface.read_udp(self.socknum, min(to_read, avail))[1]
recv = _the_interface.read_udp(
self.socknum, min(to_read, avail))[1]
recv = bytes(recv)
received.append(recv)
to_read -= len(recv)
Expand Down Expand Up @@ -267,12 +269,14 @@ def readline(self):
if self._sock_type == SOCK_STREAM:
avail = self.available()
if avail:
self._buffer += _the_interface.socket_read(self.socknum, avail)[1]
self._buffer += _the_interface.socket_read(
self.socknum, avail)[1]
elif self._sock_type == SOCK_DGRAM:
avail = _the_interface.udp_remaining()
if avail:
self._buffer += _the_interface.read_udp(self.socknum, avail)
elif self._timeout > 0 and time.monotonic() - stamp > self._timeout:
if not avail and self._timeout > 0 and \
time.monotonic() - stamp > self._timeout:
self.close()
raise RuntimeError("Didn't receive response, failing out...")
firstline, self._buffer = self._buffer.split(b"\r\n", 1)
Expand All @@ -287,7 +291,6 @@ def disconnect(self):
def close(self):
"""Closes the socket."""
_the_interface.socket_close(self.socknum)
SOCKETS.remove(self.socknum)

def available(self):
"""Returns how many bytes of data are available to be read from the socket."""
Expand Down
Loading