Skip to content

Add Adafruit_CircuitPython_Requests Compatibility #7

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 41 commits into from
Mar 6, 2020
Merged
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
eae6025
add some updates for Adafruit_Requests
Mar 3, 2020
af4a485
return buffer and firstline properly
Mar 3, 2020
3b0a686
only add to buffer if bytes are avail on the socket
Mar 3, 2020
2da4616
fix incorrect read within dns, add correct join in socket
Mar 3, 2020
b0a1cdc
add query count
Mar 3, 2020
28d1c7b
parse based on answer instead of fixed buff size lazy parsing
Mar 3, 2020
d17a9ff
lintin
Mar 4, 2020
be5eac4
rebuild docx!
Mar 4, 2020
29eb7b5
ESP32SPI_Simpletest-style-test
Mar 4, 2020
bc82d38
correct parse, TODO: guards
Mar 4, 2020
32f18e0
lintin
Mar 4, 2020
4ca248d
add checks for types
Mar 4, 2020
ea7f98d
modify readme for new example, spnx test buildin docs
Mar 4, 2020
39385ad
throw assertionerror if dns was failed to resolve
Mar 4, 2020
5d96a2d
add resolved ip for debugging
Mar 4, 2020
42a2faf
debugging for DHCP server
Mar 4, 2020
6dc07ae
add debug iface to DNS
Mar 4, 2020
4580e6d
add way more verbose debugging within dns
Mar 4, 2020
d65fc58
trying a different approach for parsing out the query name and valida…
Mar 4, 2020
2043d7d
Better parsing for answer
Mar 4, 2020
08bb7c9
remove gc
Mar 4, 2020
cb352dd
add ip to socket begin
Mar 4, 2020
9ec9ed0
add debug to dhcp
Mar 4, 2020
ec49329
add old params
Mar 4, 2020
e0b3d23
remove cruft, new impl
Mar 4, 2020
7d7ef0e
run buf til pointer is at end
Mar 5, 2020
7db5a2c
add more verbose output for parsing DHCP requests if debug
Mar 5, 2020
a028c34
if initialized, dhcp failing to configure should Raise. Handle invali…
Mar 5, 2020
30c9f0a
break on padding opt
Mar 5, 2020
088421e
add TLS_mode conntype, assertion for connecting to HTTPS
Mar 5, 2020
406c7fb
use dynamic port numbers 41952-65535, randomize to prevent reuse!
Mar 5, 2020
4467ffe
switch to wait for sncr to clear?
Mar 5, 2020
1eb1a51
wait less time between checking sn_sr
Mar 5, 2020
ebf2d0e
read and write to/from subnet registers better in ifconfig setter/getter
Mar 5, 2020
7df14f1
testing port configuration tweaks
Mar 5, 2020
74e6d7b
prune extra from get_socket, just allocate a socket from the chipset
Mar 5, 2020
4030144
continue separating functionality: socket_open should only issue OPEN…
Mar 5, 2020
dab3551
fix indentation for lintin
Mar 5, 2020
55be280
add Adafruit IO example!
Mar 6, 2020
30569bd
add example for manually setting the network configuration
Mar 6, 2020
f510eb2
fix newline
Mar 6, 2020
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: 35 additions & 30 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -57,45 +57,50 @@ wifitest.adafruit.com.

.. code-block:: python

import time

import board
import busio
import digitalio
from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET
import adafruit_requests as requests
from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K
import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket

print("Wiznet5k WebClient Test")

TEXT_URL = "http://wifitest.adafruit.com/testwifi/index.html"
JSON_URL = "http://api.coindesk.com/v1/bpi/currentprice/USD.json"

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

# Initialize ethernet interface with DHCP
eth = WIZNET5K(spi_bus, cs, debug=True)

print("DHCP Assigned IP: ", eth.pretty_ip(eth.ip_address))

socket.set_interface(eth)

host = 'wifitest.adafruit.com'
port = 80

addr_info = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)
sock = socket.socket(addr_info[0], addr_info[1], addr_info[2])

print("Connected to ", sock.getpeername())

# Make a HTTP Request
sock.send(b"GET /testwifi/index.html HTTP/1.1\n")
sock.send(b"Host: 104.236.193.178\n")
sock.send(b"Connection: close\n\n")

bytes_avail = 0
while not bytes_avail:
bytes_avail = sock.available()
if bytes_avail > 0:
data = sock.recv(bytes_avail)
print(data)
break
time.sleep(0.05)
eth = WIZNET5K(spi_bus, cs)

# Initialize a requests object with a socket and ethernet interface
requests.set_socket(socket, eth)

print("Chip Version:", eth.chip)
print("MAC Address:", [hex(i) for i in eth.mac_address])
print("My IP address is:", eth.pretty_ip(eth.ip_address))
print("IP lookup adafruit.com: %s" %eth.pretty_ip(eth.get_host_by_name("adafruit.com")))


#eth._debug = True
print("Fetching text from", TEXT_URL)
r = requests.get(TEXT_URL)
print('-'*40)
print(r.text)
print('-'*40)
r.close()

print()
print("Fetching json from", JSON_URL)
r = requests.get(JSON_URL)
print('-'*40)
print(r.json())
print('-'*40)
r.close()

print("Done!")

Contributing
============
Expand Down
32 changes: 22 additions & 10 deletions adafruit_wiznet5k/adafruit_wiznet5k.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@
'remote_ip': 0,
'remote_port': 0}

class WIZNET5K:
class WIZNET5K: # pylint: disable=too-many-public-methods
"""Interface for WIZNET5K module.
:param ~busio.SPI spi_bus: The SPI bus the Wiznet module is connected to.
:param ~digitalio.DigitalInOut cs: Chip select pin.
Expand All @@ -149,7 +149,11 @@ class WIZNET5K:

"""

# pylint: disable=too-many-arguments, too-many-public-methods
TCP_MODE = const(0x21)
UDP_MODE = const(0x02)
TLS_MODE = const(0x03) # This is NOT currently implemented

# pylint: disable=too-many-arguments
def __init__(self, spi_bus, cs, reset=None,
is_dhcp=True, mac=DEFAULT_MAC, debug=False):
self._debug = debug
Expand Down Expand Up @@ -180,7 +184,8 @@ def __init__(self, spi_bus, cs, reset=None,
self._dns = 0
# Set DHCP
if is_dhcp:
self.set_dhcp()
ret = self.set_dhcp()
assert ret == 0, "Failed to configure DHCP Server!"

def set_dhcp(self, response_timeout=1):
"""Initializes the DHCP client and attempts to retrieve
Expand All @@ -193,11 +198,9 @@ def set_dhcp(self, response_timeout=1):
print("* Initializing DHCP")
self._src_port = 68
# Return IP assigned by DHCP
_dhcp_client = dhcp.DHCP(self, self.mac_address, response_timeout)
_dhcp_client = dhcp.DHCP(self, self.mac_address, response_timeout, debug=self._debug)
ret = _dhcp_client.request_dhcp_lease()
if ret == 1:
if self._debug:
print("* Found DHCP server - setting configuration...")
_ip = (_dhcp_client.local_ip[0], _dhcp_client.local_ip[1],
_dhcp_client.local_ip[2], _dhcp_client.local_ip[3])

Expand All @@ -210,9 +213,14 @@ def set_dhcp(self, response_timeout=1):
self._dns = (_dhcp_client.dns_server_ip[0], _dhcp_client.dns_server_ip[1],
_dhcp_client.dns_server_ip[2], _dhcp_client.dns_server_ip[3])
self.ifconfig = ((_ip, _subnet_mask, _gw_addr, self._dns))
if self._debug:
print("* Found DHCP Server:")
print("IP: {}\nSubnet Mask: {}\nGW Addr: {}\nDNS Server: {}".format(_ip,
_subnet_mask,
_gw_addr,
self._dns))
return 0
self._src_port = 0
return 1
return -1

def get_host_by_name(self, hostname):
"""Convert a hostname to a packed 4-byte IP Address.
Expand All @@ -224,8 +232,11 @@ def get_host_by_name(self, hostname):
hostname = bytes(hostname, 'utf-8')
self._src_port = int(time.monotonic())
# Return IP assigned by DHCP
_dns_client = dns.DNS(self, self._dns)
_dns_client = dns.DNS(self, self._dns, debug=self._debug)
ret = _dns_client.gethostbyname(hostname)
if self._debug:
print("* Resolved IP: ", ret)
assert ret != -1, "Failed to resolve hostname!"
self._src_port = 0
return ret

Expand Down Expand Up @@ -544,7 +555,8 @@ def socket_open(self, socket_num, dest, port, conn_mode=SNMR_TCP):
print("*** Opening socket %d"%socket_num)
if self._read_snsr(socket_num)[0] == SNSR_SOCK_CLOSED:
if self._debug:
print("w5k socket begin, protocol={}, port={}".format(conn_mode, port))
print("w5k socket begin, protocol={}, port={}, ip={}".format(conn_mode, port,
self.pretty_ip(dest)))
time.sleep(0.00025)

self._write_snmr(socket_num, conn_mode)
Expand Down
151 changes: 115 additions & 36 deletions adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
* Author(s): Jordan Terrell, Brent Rubell

"""
import gc
import time
from random import randrange
from micropython import const
Expand Down Expand Up @@ -74,6 +75,18 @@
DEFAULT_LEASE_TIME = const(900)
BROADCAST_SERVER_ADDR = '255.255.255.255'

# DHCP Response Options
MSG_TYPE = 53
SUBNET_MASK = 1
ROUTERS_ON_SUBNET = 3
DNS_SERVERS = 6
DHCP_SERVER_ID = 54
T1_VAL = 58
T2_VAL = 59
LEASE_TIME = 51
OPT_END = 255


# pylint: enable=bad-whitespace
_BUFF = bytearray(317)

Expand All @@ -88,7 +101,8 @@ class DHCP:
"""

# pylint: disable=too-many-arguments, too-many-instance-attributes, invalid-name
def __init__(self, eth, mac_address, timeout=1, timeout_response=1):
def __init__(self, eth, mac_address, timeout=1, timeout_response=1, debug=False):
self._debug = debug
self._timeout = timeout
self._response_timeout = timeout_response
self._mac_address = mac_address
Expand Down Expand Up @@ -214,7 +228,7 @@ def send_dhcp_message(self, state, time_elapsed):
# Send DHCP packet
self._sock.send(_BUFF)

def parse_dhcp_response(self, response_timeout):
def parse_dhcp_response(self, response_timeout): # pylint: disable=too-many-branches, too-many-statements
"""Parse DHCP response from DHCP server.
Returns DHCP packet type.

Expand All @@ -228,48 +242,100 @@ def parse_dhcp_response(self, response_timeout):
return (255, 0)
time.sleep(0.05)
# store packet in buffer
_BUFF = self._sock.recv(packet_sz)[0]
_BUFF = self._sock.recv()
if self._debug:
print("DHCP Response: ", _BUFF)

# Check OP, if valid, let's parse the packet out!
# -- Parse Packet, FIXED -- #
# Validate OP
assert _BUFF[0] == DHCP_BOOT_REPLY, "Malformed Packet - \
DHCP message OP is not expected BOOT Reply."

# Client Hardware Address (CHADDR)
chaddr = bytearray(6)
for mac, _ in enumerate(chaddr):
chaddr[mac] = _BUFF[28+mac]

if chaddr != 0:
xid = _BUFF[4:8]
if bytes(xid) < self._initial_xid:
return 0, 0
xid = _BUFF[4:8]
if bytes(xid) < self._initial_xid:
return 0, 0

# Your IP Address (YIADDR)
self.local_ip = _BUFF[16:20]

# Gateway IP Address (GIADDR)
self.gateway_ip = _BUFF[20:24]

# NOTE: Next 192 octets are 0's for BOOTP legacy

# DHCP Message Type
msg_type = _BUFF[242]
# DHCP Server ID
self.dhcp_server_ip = _BUFF[245:249]
# Lease Time, in seconds
self._lease_time = int.from_bytes(_BUFF[251:255], 'l')
# T1 value
self._t1 = int.from_bytes(_BUFF[257:261], 'l')
# T2 value
self._t2 = int.from_bytes(_BUFF[263:267], 'l')
# Subnet Mask
self.subnet_mask = _BUFF[269:273]
# DNS Server
self.dns_server_ip = _BUFF[281:285]

if _BUFF[28:34] == 0:
return 0, 0

if int.from_bytes(_BUFF[235:240], 'l') != MAGIC_COOKIE:
return 0, 0

# -- Parse Packet, VARIABLE -- #
ptr = 240
while _BUFF[ptr] != OPT_END:
if _BUFF[ptr] == MSG_TYPE:
ptr += 1
opt_len = _BUFF[ptr]
ptr += opt_len
msg_type = _BUFF[ptr]
ptr += 1
elif _BUFF[ptr] == SUBNET_MASK:
ptr += 1
opt_len = _BUFF[ptr]
ptr += 1
self.subnet_mask = _BUFF[ptr:ptr+opt_len]
ptr += opt_len
elif _BUFF[ptr] == DHCP_SERVER_ID:
ptr += 1
opt_len = _BUFF[ptr]
ptr += 1
self.dhcp_server_ip = _BUFF[ptr:ptr+opt_len]
ptr += opt_len
elif _BUFF[ptr] == LEASE_TIME:
ptr += 1
opt_len = _BUFF[ptr]
ptr += 1
self._lease_time = int.from_bytes(_BUFF[ptr:ptr+opt_len], 'l')
ptr += opt_len
elif _BUFF[ptr] == ROUTERS_ON_SUBNET:
ptr += 1
opt_len = _BUFF[ptr]
ptr += 1
self.gateway_ip = _BUFF[ptr:ptr+opt_len]
ptr += opt_len
elif _BUFF[ptr] == DNS_SERVERS:
ptr += 1
opt_len = _BUFF[ptr]
ptr += 1
self.dns_server_ip = _BUFF[ptr:ptr+4]
ptr += opt_len # still increment even though we only read 1 addr.
elif _BUFF[ptr] == T1_VAL:
ptr += 1
opt_len = _BUFF[ptr]
ptr += 1
self._t1 = int.from_bytes(_BUFF[ptr:ptr+opt_len], 'l')
ptr += opt_len
elif _BUFF[ptr] == T2_VAL:
ptr += 1
opt_len = _BUFF[ptr]
ptr += 1
self._t2 = int.from_bytes(_BUFF[ptr:ptr+opt_len], 'l')
ptr += opt_len
elif _BUFF[ptr] == 0:
break
else:
# We're not interested in this option
ptr += 1
opt_len = _BUFF[ptr]
ptr += 1
# no-op
ptr += opt_len

if self._debug:
print("Msg Type: {}\nSubnet Mask: {}\nDHCP Server ID:{}\nDNS Server IP:{}\
\nGateway IP:{}\nT1:{}\nT2:{}\nLease Time:{}".format(msg_type, self.subnet_mask,
self.dhcp_server_ip,
self.dns_server_ip,
self.gateway_ip,
self._t1, self._t2,
self._lease_time))

gc.collect()
return msg_type, xid

def request_dhcp_lease(self):
def request_dhcp_lease(self): # pylint: disable=too-many-branches, too-many-statements
"""Request to renew or acquire a DHCP lease.

"""
Expand All @@ -284,18 +350,28 @@ def request_dhcp_lease(self):
if self._dhcp_state == STATE_DHCP_START:
self._transaction_id += 1
self._sock.connect(((BROADCAST_SERVER_ADDR), DHCP_SERVER_PORT))
if self._debug:
print("* DHCP: Discover")
self.send_dhcp_message(STATE_DHCP_DISCOVER,
((time.monotonic() - start_time) / 1000))
self._dhcp_state = STATE_DHCP_DISCOVER
elif self._dhcp_state == STATE_DHCP_DISCOVER:
if self._debug:
print("* DHCP: Parsing OFFER")
msg_type, xid = self.parse_dhcp_response(self._timeout)
if msg_type == DHCP_OFFER:
# use the _transaction_id the offer returned,
# rather than the current one
self._transaction_id = self._transaction_id.from_bytes(xid, 'l')
if self._debug:
print("* DHCP: Request")
self.send_dhcp_message(DHCP_REQUEST, ((time.monotonic() - start_time) / 1000))
self._dhcp_state = STATE_DHCP_REQUEST
else:
print("* Received DHCP Message is not OFFER")
elif STATE_DHCP_REQUEST:
if self._debug:
print("* DHCP: Parsing ACK")
msg_type, xid = self.parse_dhcp_response(self._timeout)
if msg_type == DHCP_ACK:
self._dhcp_state = STATE_DHCP_LEASED
Expand All @@ -312,6 +388,8 @@ def request_dhcp_lease(self):
self._rebind_in_sec = self._t2
elif msg_type == DHCP_NAK:
self._dhcp_state = STATE_DHCP_START
else:
print("* Received DHCP Message is not OFFER")

if msg_type == 255:
msg_type = 0
Expand All @@ -324,4 +402,5 @@ def request_dhcp_lease(self):
self._last_check_lease_ms = time.monotonic()
# close the socket, we're done with it
self._sock.close()
gc.collect()
return result
Loading