diff --git a/README.rst b/README.rst index df93390..df9b802 100644 --- a/README.rst +++ b/README.rst @@ -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 ============ diff --git a/adafruit_wiznet5k/adafruit_wiznet5k.py b/adafruit_wiznet5k/adafruit_wiznet5k.py index 89b0606..2bfbd5d 100755 --- a/adafruit_wiznet5k/adafruit_wiznet5k.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k.py @@ -41,7 +41,7 @@ * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice """ - +from random import randint import time from micropython import const @@ -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. @@ -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 @@ -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 @@ -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]) @@ -210,9 +213,15 @@ 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)) + self._src_port = 0 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. @@ -224,8 +233,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 @@ -303,15 +315,8 @@ def remote_port(self): @property def ifconfig(self): """Returns the network configuration as a tuple.""" - # set subnet and gateway addresses - self._pbuff = bytearray(8) - for octet in range(0, 4): - self._pbuff += self.read(REG_SUBR+octet, 0x04) - for octet in range(0, 4): - self._pbuff += self.read(REG_GAR+octet, 0x04) - - params = (self.ip_address, self._pbuff[0:3], self._pbuff[3:7], self._dns) - return params + return (self.ip_address, self.read(REG_SUBR, 0x00, 4), + self.read(REG_GAR, 0x00, 4), self._dns) @ifconfig.setter def ifconfig(self, params): @@ -321,14 +326,10 @@ def ifconfig(self, params): """ ip_address, subnet_mask, gateway_address, dns_server = params - # Set IP Address self.write(REG_SIPR, 0x04, ip_address) + self.write(REG_SUBR, 0x04, subnet_mask) + self.write(REG_GAR, 0x04, gateway_address) - # set subnet and gateway addresses - for octet in range(0, 4): - self.write(REG_SUBR+octet, 0x04, subnet_mask[octet]) - self.write(REG_GAR+octet, 0x04, gateway_address[octet]) - # set dns server address self._dns = dns_server def _w5100_init(self): @@ -492,26 +493,36 @@ def socket_connect(self, socket_num, dest, port, conn_mode=SNMR_TCP): """ assert self.link_status, "Ethernet cable disconnected!" if self._debug: - print("*** Connecting: Socket# {}, conn_mode: {}".format(socket_num, conn_mode)) - + print("* w5k socket connect, protocol={}, port={}, ip={}".format(conn_mode, port, + self.pretty_ip(dest))) # initialize a socket and set the mode - res = self.socket_open(socket_num, dest, port, conn_mode=conn_mode) + res = self.socket_open(socket_num, conn_mode=conn_mode) if res == 1: raise RuntimeError('Failed to initalize a connection with the socket.') + # set socket destination IP and port + self._write_sndipr(socket_num, dest) + self._write_sndport(socket_num, port) + self._send_socket_cmd(socket_num, CMD_SOCK_CONNECT) + if conn_mode == SNMR_TCP: - # TCP client - connect socket - self._write_sncr(socket_num, CMD_SOCK_CONNECT) - self._read_sncr(socket_num) # wait for tcp connection establishment while self.socket_status(socket_num)[0] != SNSR_SOCK_ESTABLISHED: + time.sleep(0.001) + if self._debug: + print("SN_SR:", self.socket_status(socket_num)[0]) if self.socket_status(socket_num)[0] == SNSR_SOCK_CLOSED: raise RuntimeError('Failed to establish connection.') - time.sleep(1) elif conn_mode == SNMR_UDP: UDP_SOCK['bytes_remaining'] = 0 return 1 + def _send_socket_cmd(self, socket, cmd): + self._write_sncr(socket, cmd) + while self._read_sncr(socket) != b'\x00': + if self._debug: + print("waiting for sncr to clear...") + def get_socket(self, sockets): """Requests, allocates and returns a socket from the W5k chip. Returned socket number may not exceed max_sockets. @@ -528,15 +539,12 @@ def get_socket(self, sockets): sock = _sock break - if self._src_port == 0: - self._src_port = 1024 - if self._debug: - print("Allocated socket #{}:{}".format(sock, self._src_port)) + print("Allocated socket #{}".format(sock)) return sock - def socket_open(self, socket_num, dest, port, conn_mode=SNMR_TCP): - """Opens a socket to a destination IP address or hostname. By default, we use + 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!" @@ -544,7 +552,7 @@ 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("* Opening W5k Socket, protocol={}".format(conn_mode)) time.sleep(0.00025) self._write_snmr(socket_num, conn_mode) @@ -554,12 +562,7 @@ def socket_open(self, socket_num, dest, port, conn_mode=SNMR_TCP): # write to socket source port self._write_sock_port(socket_num, self._src_port) else: - # if source port is not set, set the local port number - self._write_sock_port(socket_num, LOCAL_PORT) - - # set socket destination IP and port - self._write_sndipr(socket_num, dest) - self._write_sndport(socket_num, port) + self._write_sock_port(socket_num, randint(49152, 65535)) # open socket self._write_sncr(socket_num, CMD_SOCK_OPEN) diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py index a4dafd4..5993c22 100755 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py @@ -29,6 +29,7 @@ * Author(s): Jordan Terrell, Brent Rubell """ +import gc import time from random import randrange from micropython import const @@ -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) @@ -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 @@ -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. @@ -228,48 +242,101 @@ 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: + print("f") + 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. """ @@ -284,18 +351,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 @@ -312,6 +389,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 @@ -324,4 +403,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 diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_dns.py b/adafruit_wiznet5k/adafruit_wiznet5k_dns.py index 347aaca..083e10d 100755 --- a/adafruit_wiznet5k/adafruit_wiznet5k_dns.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_dns.py @@ -61,7 +61,8 @@ class DNS: :param iface: Network interface """ - def __init__(self, iface, dns_address): + def __init__(self, iface, dns_address, debug=False): + self._debug = debug self._iface = iface socket.set_interface(iface) self._sock = socket.socket(type=socket.SOCK_DGRAM) @@ -87,19 +88,23 @@ def gethostbyname(self, hostname): # Send DNS request packet self._sock.connect((self._dns_server, DNS_PORT)) + if self._debug: + print("* DNS: Sending request packet...") self._sock.send(self._pkt_buf) # wait and retry 3 times for a response retries = 0 addr = -1 - while (retries < 3) and (addr == -1): + while (retries < 5) and (addr == -1): addr = self._parse_dns_response() + if addr == -1 and self._debug: + print("* DNS ERROR: Failed to resolve DNS response, retrying...") retries += 1 self._sock.close() return addr - def _parse_dns_response(self): + def _parse_dns_response(self): # pylint: disable=too-many-return-statements, too-many-branches, too-many-statements, too-many-locals """Receives and parses DNS query response. Returns desired hostname address if obtained, -1 otherwise. @@ -110,36 +115,108 @@ def _parse_dns_response(self): while packet_sz <= 0: packet_sz = self._sock.available() if (time.monotonic() - start_time) > 1.0: - # timed out! + if self._debug: + print("* DNS ERROR: Did not receive DNS response!") return -1 time.sleep(0.05) - # store packet in buffer - self._pkt_buf = self._sock.recv(packet_sz)[0] + # recv packet into buf + self._pkt_buf = self._sock.recv() + + if self._debug: + print("DNS Packet Received: ", self._pkt_buf) # Validate request identifier - if not int.from_bytes(self._pkt_buf[0:2], 'l') == self._request_id: + xid = int.from_bytes(self._pkt_buf[0:2], 'l') + if not xid == self._request_id: + if self._debug: + print("* DNS ERROR: Received request identifer {} \ + does not match expected {}".format(xid, self._request_id)) return -1 # Validate flags - if not int.from_bytes(self._pkt_buf[2:4], 'l') == 0x8180: + flags = int.from_bytes(self._pkt_buf[2:4], 'l') + if not flags == 0x8180: + if self._debug: + print("* DNS ERROR: Invalid flags, ", flags) + return -1 + # Number of questions + qr_count = int.from_bytes(self._pkt_buf[4:6], 'l') + if not qr_count >= 1: + if self._debug: + print("* DNS ERROR: Question count >=1, ", qr_count) return -1 - # Validate Answer RRs (>=1) + # Number of answers an_count = int.from_bytes(self._pkt_buf[6:8], 'l') + if self._debug: + print("* DNS Answer Count: ", an_count) if not an_count >= 1: return -1 - # iterate over ANCOUNT since answer may not be type A - while an_count > 0: - ans_type = int.from_bytes(self._pkt_buf[41:43], 'l') - ans_class = int.from_bytes(self._pkt_buf[43:45], 'l') - ans_len = int.from_bytes(self._pkt_buf[49:51], 'l') - if ans_type == TYPE_A and ans_class == CLASS_IN: - if ans_len != 4: - # invalid size ret.'d - return -1 - # return the address - return self._pkt_buf[51:55] - # not the correct answer type or class - an_count += 1 + # Parse query + ptr = 12 + name_len = 1 + while name_len > 0: + # read the length of the name + name_len = self._pkt_buf[ptr] + if name_len == 0x00: + # we reached the end of this name + ptr += 1 # inc. pointer by 0x00 + break + # advance pointer + ptr += name_len + 1 + + # Validate Query is Type A + q_type = int.from_bytes(self._pkt_buf[ptr:ptr+2], 'l') + if not q_type == TYPE_A: + if self._debug: + print("* DNS ERROR: Incorrect Query Type: ", q_type) + return -1 + ptr += 2 + + # Validate Query is Type A + q_class = int.from_bytes(self._pkt_buf[ptr:ptr+2], 'l') + if not q_class == TYPE_A: + if self._debug: + print("* DNS ERROR: Incorrect Query Class: ", q_class) + return -1 + ptr += 2 + + # Let's take the first type-a answer + if self._pkt_buf[ptr] != 0xc0: + return -1 + ptr += 1 + + if self._pkt_buf[ptr] != 0xc: + return -1 + ptr += 1 + + # Validate Answer Type A + ans_type = int.from_bytes(self._pkt_buf[ptr:ptr+2], 'l') + if not ans_type == TYPE_A: + if self._debug: + print("* DNS ERROR: Incorrect Answer Type: ", ans_type) + return -1 + ptr += 2 + + # Validate Answer Class IN + ans_class = int.from_bytes(self._pkt_buf[ptr:ptr+2], 'l') + if not ans_class == TYPE_A: + if self._debug: + print("* DNS ERROR: Incorrect Answer Class: ", ans_class) + return -1 + ptr += 2 + + # skip over TTL + ptr += 4 + + # Validate addr is IPv4 + data_len = int.from_bytes(self._pkt_buf[ptr:ptr+2], 'l') + if not data_len == DATA_LEN: + if self._debug: + print("* DNS ERROR: Unexpected Data Length: ", data_len) + return -1 + ptr += 2 + # Return address + return self._pkt_buf[ptr:ptr+4] def _build_dns_header(self): """Builds DNS header.""" diff --git a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py index 7637c05..ddcc507 100755 --- a/adafruit_wiznet5k/adafruit_wiznet5k_socket.py +++ b/adafruit_wiznet5k/adafruit_wiznet5k_socket.py @@ -68,7 +68,7 @@ def getaddrinfo(host, port, family=0, socktype=0, proto=0, flags=0): """ if not isinstance(port, int): raise RuntimeError("Port must be an integer") - return [AF_INET, socktype, proto, '', (gethostbyname(host), port)] + return [(AF_INET, socktype, proto, '', (gethostbyname(host), port))] def gethostbyname(hostname): """Translate a host name to IPv4 address format. The IPv4 address @@ -141,6 +141,7 @@ def connect(self, address, conntype=None): :param tuple address: Remote socket as a (host, port) tuple. """ + assert conntype != 0x03, "Error: SSL/TLS is not currently supported by CircuitPython." host, port = address if hasattr(host, 'split'): @@ -158,12 +159,12 @@ def send(self, data): _the_interface.socket_write(self.socknum, data) gc.collect() + def recv(self, bufsize=0): #pylint: disable=too-many-branches """Reads some bytes from the connected remote address. :param int bufsize: Maximum number of bytes to receive. - """ - assert _the_interface.link_status, "Ethernet cable disconnected!" + # print("Socket read", bufsize) if bufsize == 0: # read everything on the socket while True: @@ -173,9 +174,9 @@ 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) + 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) + self._buffer += _the_interface.read_udp(self.socknum, avail)[1] else: break gc.collect() @@ -203,7 +204,7 @@ def recv(self, bufsize=0): #pylint: disable=too-many-branches gc.collect() if self._timeout > 0 and time.monotonic() - stamp > self._timeout: break - self._buffer = received + self._buffer += b''.join(received) ret = None if len(self._buffer) == bufsize: @@ -216,24 +217,26 @@ def recv(self, bufsize=0): #pylint: disable=too-many-branches return ret def readline(self): - """Attempt to return as many bytes as we can up to - but not including \n""" + """Attempt to return as many bytes as we can up to \ + but not including '\r\n'. + + """ stamp = time.monotonic() - while b'\n' not in self._buffer: + while b'\r\n' not in self._buffer: if self._sock_type == SOCK_STREAM: avail = self.available() - self._buffer += _the_interface.read(self.socknum, avail)[1] + if avail: + self._buffer += _the_interface.socket_read(self.socknum, avail)[1] elif self._sock_type == SOCK_DGRAM: avail = _the_interface.udp_remaining() - self._buffer += _the_interface.read_udp(self.socknum, avail)[1] + if avail: + self._buffer += _the_interface.read_udp(self.socknum, avail) elif self._timeout > 0 and time.monotonic() - stamp > self._timeout: self.close() raise RuntimeError("Didn't receive response, failing out...") - firstline = self._buffer.split(b'\n', 1) + firstline, self._buffer = self._buffer.split(b'\r\n', 1) gc.collect() - # clear tmp data buffer - self._buffer = b'' - return firstline[0] + return firstline def disconnect(self): """Disconnects a TCP socket.""" diff --git a/examples/wiznet5k_aio_post.py b/examples/wiznet5k_aio_post.py new file mode 100644 index 0000000..eb6f0a1 --- /dev/null +++ b/examples/wiznet5k_aio_post.py @@ -0,0 +1,39 @@ +import time +import board +import busio +from digitalio import DigitalInOut +from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K +import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket +import adafruit_requests as requests + +# Get Adafruit.io details from a secrets.py file +try: + from secrets import secrets +except ImportError: + print("WiFi secrets are kept in secrets.py, please add them there!") + raise + +cs = 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) +requests.set_socket(socket, eth) + +counter = 0 + +while True: + print("Posting data...", end='') + data = counter + feed = 'test' + payload = {'value':data} + response = requests.post( + "http://io.adafruit.com/api/v2/"+secrets['aio_username']+"/feeds/"+feed+"/data", + json=payload, + headers={"X-AIO-KEY":secrets['aio_key']}) + print(response.json()) + response.close() + counter = counter + 1 + print("OK") + response = None + time.sleep(15) diff --git a/examples/wiznet5k_simpletest.py b/examples/wiznet5k_simpletest.py index 9c8bcee..53797de 100644 --- a/examples/wiznet5k_simpletest.py +++ b/examples/wiznet5k_simpletest.py @@ -1,50 +1,44 @@ -import time - import board import busio import digitalio +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) -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]) - -sock.connect(addr_info[4]) +# Initialize a requests object with a socket and ethernet interface +requests.set_socket(socket, eth) -print("Connected to ", sock.getpeername()) +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"))) -# 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") -# Start transmission timer -start = time.monotonic() +#eth._debug = True +print("Fetching text from", TEXT_URL) +r = requests.get(TEXT_URL) +print('-'*40) +print(r.text) +print('-'*40) +r.close() -bytes_avail = 0 -while not bytes_avail: - bytes_avail = sock.available() - if bytes_avail > 0: - data = sock.recv(bytes_avail) - print(data[0]) - break - time.sleep(0.05) +print() +print("Fetching json from", JSON_URL) +r = requests.get(JSON_URL) +print('-'*40) +print(r.json()) +print('-'*40) +r.close() -end = time.monotonic() -print("Received: %d bytes"%bytes_avail) -end = end - start / 1000000.0 -rate = bytes_avail / end / 1000.0 -print("Rate = %0.5f kbytes/second"%rate) +print("Done!") diff --git a/examples/wiznet5k_simpletest_manual_network.py b/examples/wiznet5k_simpletest_manual_network.py new file mode 100644 index 0000000..9b5dfe3 --- /dev/null +++ b/examples/wiznet5k_simpletest_manual_network.py @@ -0,0 +1,43 @@ +import board +import busio +import digitalio +import adafruit_requests as requests +from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K +import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket + +TEXT_URL = "http://wifitest.adafruit.com/testwifi/index.html" + +# Setup your network configuration below +IP_ADDRESS = (192, 168, 10, 1) +SUBNET_MASK = (255, 255, 0, 0) +GATEWAY_ADDRESS = (192, 168, 0, 1) +DNS_SERVER = (8, 8, 8, 8) + +print("Wiznet5k WebClient Test (no DHCP)") + +cs = digitalio.DigitalInOut(board.D10) +spi_bus = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) + +# Initialize ethernet interface without DHCP +eth = WIZNET5K(spi_bus, cs, is_dhcp=False) + +# Set network configuration +eth.ifconfig = (IP_ADDRESS, SUBNET_MASK, GATEWAY_ADDRESS, DNS_SERVER) + +# 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()