物联网开发117 - Micropython ESP32 C3连接W5500有线以太网UDP&TCP协议通讯模块_esp32 w5500

120 阅读14分钟

img img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

def chip(self):
    """Returns the chip type."""
    return self._chip_type


@property
def ip_address(self):
    """Returns the configured IP address."""
    return self.read(REG_SIPR, 0x00, 4)


def pretty_ip(self, ip):  # pylint: disable=no-self-use, invalid-name
    """Converts a bytearray IP address to a
    dotted-quad string for printing
    """
    return "%d.%d.%d.%d" % (ip[0], ip[1], ip[2], ip[3])


def unpretty_ip(self, ip):  # pylint: disable=no-self-use, invalid-name
    """Converts a dotted-quad string to a bytearray IP address"""
    octets = [int(x) for x in ip.split(".")]
    return bytes(octets)


@property
def mac_address(self):
    """Returns the hardware's MAC address."""
    return self.read(REG_SHAR, 0x00, 6)


@mac_address.setter
def mac_address(self, address):
    """Sets the hardware MAC address.
    :param tuple address: Hardware MAC address.
    """
    self.write(REG_SHAR, 0x04, address)


def pretty_mac(self, mac):  # pylint: disable=no-self-use, invalid-name
    """Converts a bytearray MAC address to a
    dotted-quad string for printing
    """
    return "%s:%s:%s:%s:%s:%s" % (
        hex(mac[0]),
        hex(mac[1]),
        hex(mac[2]),
        hex(mac[3]),
        hex(mac[4]),
        hex(mac[5]),
    )


def remote_ip(self, socket_num):
    """Returns the IP address of the host who sent the current incoming packet.
    :param int socket num: Desired socket.
    """
    if socket_num >= self.max_sockets:
        return self._pbuff
    for octet in range(0, 4):
        self._pbuff[octet] = self._read_socket(socket_num, REG_SNDIPR + octet)[0]
    return self.pretty_ip(self._pbuff)


@property
def link_status(self):
    """ "Returns if the PHY is connected."""
    if self._chip_type == "w5500":
        data = self.read(REG_PHYCFGR, 0x00)
        return data[0] & 0x01
    return 0


@property
def link_speed(self):
    """ "Returns link speed."""
    if self._chip_type == "w5500":
        data = self.read(REG_PHYCFGR, 0x00)
        if data[0] & 0x01:  # link up
            if data[0] & 0x02:
                return 100
            else:
                return 10
    return None


@property
def link_full_duplex(self):
    """ "Returns if link is full duplex."""
    if self._chip_type == "w5500":
        data = self.read(REG_PHYCFGR, 0x00)
        if data[0] & 0x01:  # link up
            if data[0] & 0x04:
                return True
            else:
                return False
    return None


def remote_port(self, socket_num):
    """Returns the port of the host who sent the current incoming packet."""
    if socket_num >= self.max_sockets:
        return self._pbuff
    for octet in range(0, 2):
        self._pbuff[octet] = self._read_socket(socket_num, REG_SNDPORT + octet)[0]
    return int((self._pbuff[0] << 8) | self._pbuff[0])

@property
def ifconfig(self):
    """Returns the network configuration as a tuple."""
    print('IFCONFIG')    
    return (self.ip_address, self.read(REG_SUBR, 0x00, 4), self.read(REG_GAR, 0x00, 4), self._dns)
    

@ifconfig.setter
def ifconfig(self, params):
    """Sets network configuration to provided tuple in format:
    (ip_address, subnet_mask, gateway_address, dns_server).
    """
    ip_address, subnet_mask, gateway_address, dns_server = params
    self.write(REG_SIPR, 0x04, ip_address)
    self.write(REG_SUBR, 0x04, subnet_mask)
    self.write(REG_GAR, 0x04, gateway_address)
    self._dns = dns_server


def _w5100_init(self):
    """Initializes and detects a wiznet5k module."""
    time.sleep(1)
    self._cs.value(1)
    # Detect if chip is Wiznet W5500
    if self.detect_w5500() == 1:
        # perform w5500 initialization
        for i in range(0, W5200_W5500_MAX_SOCK_NUM):
            ctrl_byte = 0x0C + (i << 5)
            self.write(0x1E, ctrl_byte, 2)
            self.write(0x1F, ctrl_byte, 2)
    else:
        return 0
    return 1


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


def sw_reset(self):
    """Performs a soft-reset on a Wiznet chip
    by writing to its MR register reset bit.
    """
    mode_reg = self._read_mr()
    self._write_mr(0x80)
    mode_reg = self._read_mr()
    if mode_reg[0] != 0x00:
        return -1
    return 0


def _read_mr(self):
    """Reads from the Mode Register (MR)."""
    res = self.read(REG_MR, 0x00)
    return res


def _write_mr(self, data):
    """Writes to the mode register (MR).
    :param int data: Data to write to the mode register.
    """
    self.write(REG_MR, 0x04, data)


def read(self, addr, callback, length=1, buffer=None):
    """Reads data from a register address.
    :param int addr: Register address.
    """
    if self._debug:
        print(f'READ from {addr}')
    self._cs.value(0)
    self._device.write(bytes([addr >> 8]))  # pylint: disable=no-member
    self._device.write(bytes([addr & 0xFF]))  # pylint: disable=no-member
    self._device.write(bytes([callback]))  # pylint: disable=no-member
    if buffer is None:
        self._rxbuf = bytearray(length)
        self._device.readinto(self._rxbuf)  # pylint: disable=no-member
        self._cs.value(1)
        return self._rxbuf
    self._device.readinto(buffer, end=length)  # pylint: disable=no-member
    self._cs.value(1)
    return buffer


def write(self, addr, callback, data):
    """Write data to a register address.
    :param int addr: Destination address.
    :param int callback: Callback reference.
    :param int data: Data to write, as an integer.
    :param bytearray data: Data to write, as a bytearray.
    """
    if self._debug:
        print(f'WRITE on {addr}')
    self._cs.value(0)
    self._device.write(bytes([addr >> 8]))  # pylint: disable=no-member
    self._device.write(bytes([addr & 0xFF]))  # pylint: disable=no-member
    self._device.write(bytes([callback]))  # pylint: disable=no-member

    if hasattr(data, "from_bytes"):
        self._device.write(bytes([data]))  # pylint: disable=no-member
    else:
        for i, _ in enumerate(data):
            self._device.write(bytes([data[i]]))  # pylint: disable=no-member
    self._cs.value(1)


# Socket-Register API
def udp_remaining(self):
    """Returns amount of bytes remaining in a udp socket."""
    if self._debug:
        print("* UDP Bytes Remaining: ", UDP_SOCK["bytes_remaining"])
    return UDP_SOCK["bytes_remaining"]


def socket_available(self, socket_num, sock_type=SNMR_TCP):
    """Returns the amount of bytes to be read from the socket.
    :param int socket_num: Desired socket to return bytes from.
    :param int sock_type: Socket type, defaults to TCP.
    """
    if self._debug:
        print("* socket_available called on socket {}, protocol {}".format(socket_num, sock_type))
    assert socket_num <= self.max_sockets, "Provided socket exceeds max_sockets."
    res = self._get_rx_rcv_size(socket_num)
    if sock_type == SNMR_TCP:
        return res
    if res > 0:
        if UDP_SOCK["bytes_remaining"]:
            return UDP_SOCK["bytes_remaining"]
        # parse the udp rx packet
        # read the first 8 header bytes
        ret, self._pbuff = self.socket_read(socket_num, 8)
        if ret > 0:
            UDP_SOCK["remote_ip"] = self._pbuff[:4]
            UDP_SOCK["remote_port"] = (self._pbuff[4] << 8) + self._pbuff[5]
            UDP_SOCK["bytes_remaining"] = (self._pbuff[6] << 8) + self._pbuff[7]
            ret = UDP_SOCK["bytes_remaining"]
            return ret
    return 0


def socket_status(self, socket_num):
    """Returns the socket connection status. Can be: SNSR_SOCK_CLOSED,
    SNSR_SOCK_INIT, SNSR_SOCK_LISTEN, SNSR_SOCK_SYNSENT, SNSR_SOCK_SYNRECV,
    SNSR_SYN_SOCK_ESTABLISHED, SNSR_SOCK_FIN_WAIT, SNSR_SOCK_CLOSING,
    SNSR_SOCK_TIME_WAIT, SNSR_SOCK_CLOSE_WAIT, SNSR_LAST_ACK,
    SNSR_SOCK_UDP, SNSR_SOCK_IPRAW, SNSR_SOCK_MACRAW, SNSR_SOCK_PPOE.
    """
    return self._read_snsr(socket_num)


def socket_connect(self, socket_num, dest, port, conn_mode=SNMR_TCP):
    """Open and verify we've connected a socket to a dest IP address
    or hostname. 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("* 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, 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:
        # 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.")
    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):
    """Requests, allocates and returns a socket from the W5k
    chip. Returned socket number may not exceed max_sockets.
    """
    if self._debug:
        print("*** Get socket")
    sock = SOCKET_INVALID
    for _sock in range(self.max_sockets):
        status = self.socket_status(_sock)[0]
        if status == SNSR_SOCK_CLOSED:
            sock = _sock
            break
    if self._debug:
        print("Allocated socket #{}".format(sock))
    return sock


def socket_listen(self, socket_num, port, conn_mode=SNMR_TCP):
    """Start listening on a socket (default TCP mode).
    :parm int socket_num: socket number
    :parm int port: port to listen on
    :parm int conn_mode: connection mode SNMR_TCP (default) or SNMR_UDP
    """
    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=conn_mode)
    self.src_port = 0
    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] not in (SNSR_SOCK_LISTEN, SNSR_SOCK_ESTABLISHED, SNSR_SOCK_UDP):
        status = self._read_snsr(socket_num)
        if status[0] == SNSR_SOCK_CLOSED:
            raise RuntimeError("Listening socket closed.")


def socket_accept(self, socket_num):
    """Gets the dest IP and port from an incoming connection.
    Returns the next socket number so listening can continue
    :parm int socket_num: socket number
    """
    dest_ip = self.remote_ip(socket_num)
    dest_port = self.remote_port(socket_num)
    next_socknum = self.get_socket()
    if self._debug:
        print("* Dest is ({}, {}), Next listen socknum is #{}".format(dest_ip, dest_port, next_socknum))
    return next_socknum, (dest_ip, dest_port)


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)
    status = self._read_snsr(socket_num)[0]
    if status in (
        SNSR_SOCK_CLOSED,
        SNSR_SOCK_TIME_WAIT,
        SNSR_SOCK_FIN_WAIT,
        SNSR_SOCK_CLOSE_WAIT,
        SNSR_SOCK_CLOSING,
        SNSR_SOCK_UDP,
        SNSR_SOCK_LISTEN,
    ):
        if self._debug:
            print("* Opening W5k Socket, protocol={}".format(conn_mode))
        time.sleep(0.00025)
        self._write_snmr(socket_num, conn_mode)
        self._write_snir(socket_num, 0xFF)
        if self.src_port > 0:
            # write to socket source port
            self._write_sock_port(socket_num, self.src_port)
        else:
            s_port = randint(49152, 65535)
            while s_port in SRC_PORTS:
                s_port = randint(49152, 65535)
            self._write_sock_port(socket_num, s_port)
            SRC_PORTS[socket_num] = s_port
        # open socket
        self._write_sncr(socket_num, CMD_SOCK_OPEN)
        self._read_sncr(socket_num)
        assert (self._read_snsr((socket_num))[0] == 0x13
            or self._read_snsr((socket_num))[0] == 0x22 ), "Could not open socket in TCP or UDP mode."
        return 0
    return 1


def socket_close(self, socket_num):
    """Closes a socket."""
    if self._debug:
        print("*** Closing socket #%d" % socket_num)
    self._write_sncr(socket_num, CMD_SOCK_CLOSE)
    self._read_sncr(socket_num)


def socket_disconnect(self, socket_num):
    """Disconnect a TCP connection."""
    if self._debug:
        print("*** Disconnecting socket #%d" % socket_num)
    self._write_sncr(socket_num, CMD_SOCK_DISCON)
    self._read_sncr(socket_num)


def socket_read(self, socket_num, length):
    """Reads data from a socket into a buffer.
    Returns buffer.
    """
    assert self.link_status, "Ethernet cable disconnected!"
    assert socket_num <= self.max_sockets, "Provided socket exceeds max_sockets."
    # Check if there is data available on the socket
    ret = self._get_rx_rcv_size(socket_num)
    if self._debug:
        print("Bytes avail. on sock: ", ret)
    if ret == 0:
        # no data on socket?
        status = self._read_snmr(socket_num)
        if status in (SNSR_SOCK_LISTEN, SNSR_SOCK_CLOSED, SNSR_SOCK_CLOSE_WAIT):
            # remote end closed its side of the connection, EOF state
            ret = 0
            resp = 0
        else:
            # connection is alive, no data waiting to be read
            ret = -1
            resp = -1
    elif ret > length:
        # set ret to the length of buffer
        ret = length
    if ret > 0:
        if self._debug:
            print("\t * Processing {} bytes of data".format(ret))
        # 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)
        resp = self.read(ptr, ctrl_byte, ret)
        #  After reading the received data, update Sn_RX_RD to the increased
        # value as many as the reading size.
        ptr += ret
        self._write_snrx_rd(socket_num, ptr)
        # Notify the W5k of the updated Sn_Rx_RD
        self._write_sncr(socket_num, CMD_SOCK_RECV)
        self._read_sncr(socket_num)
    return ret, resp


def read_udp(self, socket_num, length):
    """Read UDP socket's remaining bytes."""
    if UDP_SOCK["bytes_remaining"] > 0:
        if UDP_SOCK["bytes_remaining"] <= length:
            ret, resp = self.socket_read(socket_num, UDP_SOCK["bytes_remaining"])
        else:
            ret, resp = self.socket_read(socket_num, length)
        if ret > 0:
            UDP_SOCK["bytes_remaining"] -= ret
        return ret, resp
    return -1


def socket_write(self, socket_num, buffer, timeout=0):
    """Writes a bytearray to a provided socket."""
    assert self.link_status, "Ethernet cable disconnected!"
    assert socket_num <= self.max_sockets, "Provided socket exceeds max_sockets."
    status = 0
    ret = 0
    free_size = 0
    if len(buffer) > SOCK_SIZE:
        ret = SOCK_SIZE
    else:
        ret = len(buffer)
    stamp = time.time()
    # if buffer is available, start the transfer
    free_size = self._get_tx_free_size(socket_num)
    while free_size < ret:
        free_size = self._get_tx_free_size(socket_num)
        status = self.socket_status(socket_num)[0]
        if status not in (SNSR_SOCK_ESTABLISHED, SNSR_SOCK_CLOSE_WAIT) or (timeout and time.time() - stamp > timeout):
            ret = 0
            break
    # 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)
    # update sn_tx_wr to the value + data size
    ptr = (ptr + len(buffer)) & 0xFFFF
    self._write_sntx_wr(socket_num, ptr)
    cntl_byte = 0x14 + (socket_num << 5)
    self.write(dst_addr, cntl_byte, buffer)
    self._write_sncr(socket_num, CMD_SOCK_SEND)
    self._read_sncr(socket_num)
    # check data was  transferred correctly
    while (self._read_socket(socket_num, REG_SNIR)[0] & SNIR_SEND_OK) != SNIR_SEND_OK:
        if (self.socket_status(socket_num)[0] in (
                SNSR_SOCK_CLOSED,
                SNSR_SOCK_TIME_WAIT,
                SNSR_SOCK_FIN_WAIT,
                SNSR_SOCK_CLOSE_WAIT,
                SNSR_SOCK_CLOSING,
            )
            or (timeout and time.time() - stamp > timeout)
        ):
            # self.socket_close(socket_num)
            return 0
        time.sleep(0.01)
    self._write_snir(socket_num, SNIR_SEND_OK)
    return ret


# Socket-Register Methods

def _get_rx_rcv_size(self, sock):
    """Get size of recieved and saved in socket buffer."""
    val = 0
    val_1 = self._read_snrx_rsr(sock)
    while val != val_1:
        val_1 = self._read_snrx_rsr(sock)
        if val_1 != 0:
            val = self._read_snrx_rsr(sock)
    return int.from_bytes(val, "b")


def _get_tx_free_size(self, sock):
    """Get free size of sock's tx buffer block."""
    val = 0
    val_1 = self._read_sntx_fsr(sock)
    while val != val_1:
        val_1 = self._read_sntx_fsr(sock)
        if val_1 != 0:
            val = self._read_sntx_fsr(sock)
    return int.from_bytes(val, "b")


def _read_snrx_rd(self, sock):
    self._pbuff[0] = self._read_socket(sock, REG_SNRX_RD)[0]
    self._pbuff[1] = self._read_socket(sock, REG_SNRX_RD + 1)[0]
    return self._pbuff[0] << 8 | self._pbuff[1]


def _write_snrx_rd(self, sock, data):
    self._write_socket(sock, REG_SNRX_RD, data >> 8 & 0xFF)
    self._write_socket(sock, REG_SNRX_RD + 1, data & 0xFF)


def _write_sntx_wr(self, sock, data):
    self._write_socket(sock, REG_SNTX_WR, data >> 8 & 0xFF)
    self._write_socket(sock, REG_SNTX_WR + 1, data & 0xFF)


def _read_sntx_wr(self, sock):
    self._pbuff[0] = self._read_socket(sock, 0x0024)[0]
    self._pbuff[1] = self._read_socket(sock, 0x0024 + 1)[0]
    return self._pbuff[0] << 8 | self._pbuff[1]


def _read_sntx_fsr(self, sock):
    data = self._read_socket(sock, REG_SNTX_FSR)
    data += self._read_socket(sock, REG_SNTX_FSR + 1)
    return data


def _read_snrx_rsr(self, sock):
    data = self._read_socket(sock, REG_SNRX_RSR)
    data += self._read_socket(sock, REG_SNRX_RSR + 1)
    return data


def _write_sndipr(self, sock, ip_addr):
    """Writes to socket destination IP Address."""
    for octet in range(0, 4):
        self._write_socket(sock, REG_SNDIPR + octet, ip_addr[octet])


def _write_sndport(self, sock, port):
    """Writes to socket destination port."""
    self._write_socket(sock, REG_SNDPORT, port >> 8)
    self._write_socket(sock, REG_SNDPORT + 1, port & 0xFF)


def _read_snsr(self, sock):
    """Reads Socket n Status Register."""
    return self._read_socket(sock, REG_SNSR)


def _write_snmr(self, sock, protocol):
    """Write to Socket n Mode Register."""
    self._write_socket(sock, REG_SNMR, protocol)


def _write_snir(self, sock, data):
    """Write to Socket n Interrupt Register."""
    self._write_socket(sock, REG_SNIR, data)


def _write_sock_port(self, sock, port):
    """Write to the socket port number."""
    self._write_socket(sock, REG_SNPORT, port >> 8)
    self._write_socket(sock, REG_SNPORT + 1, port & 0xFF)


def _write_sncr(self, sock, data):
    self._write_socket(sock, REG_SNCR, data)


def _read_sncr(self, sock):
    return self._read_socket(sock, REG_SNCR)


def _read_snmr(self, sock):
    return self._read_socket(sock, REG_SNMR)


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


def _read_socket(self, sock, address):
    """Read a W5k socket register."""
    cntl_byte = (sock << 5) + 0x08
    return self.read(address, cntl_byte)

**四、驱动2**


wiznet5k\_dhcp.py



SPDX-FileCopyrightText: 2009 Jordan Terell (blog.jordanterrell.com)

SPDX-FileCopyrightText: 2020 Brent Rubell for Adafruit Industries

SPDX-FileCopyrightText: 2021 Patrick Van Oosterwijck @ Silicognition LLC

SPDX-FileCopyrightText: 2021 Vincenzo D'Angelo

SPDX-License-Identifier: MIT

""" wiznet5k_dhcp

Pure-Python implementation of Jordan Terrell's DHCP library v0.3

  • Author(s): Jordan Terrell, Brent Rubell, Vincenzo D'Angelo

""" import gc import time import wiznet5k_socket as socket

from random import randint from micropython import const from wiznet5k_socket import htonl, htons

DHCP State Machine

STATE_DHCP_START = const(0x00) STATE_DHCP_DISCOVER = const(0x01) STATE_DHCP_REQUEST = const(0x02) STATE_DHCP_LEASED = const(0x03) STATE_DHCP_REREQUEST = const(0x04) STATE_DHCP_RELEASE = const(0x05) STATE_DHCP_WAIT = const(0x06) STATE_DHCP_DISCONN = const(0x07)

DHCP wait time between attempts

DHCP_WAIT_TIME = const(60)

DHCP Message Types

DHCP_DISCOVER = const(1) DHCP_OFFER = const(2) DHCP_REQUEST = const(3) DHCP_DECLINE = const(4) DHCP_ACK = const(5) DHCP_NAK = const(6) DHCP_RELEASE = const(7) DHCP_INFORM = const(8)

DHCP Message OP Codes

DHCP_BOOT_REQUEST = const(0x01) DHCP_BOOT_REPLY = const(0x02)

DHCP_HTYPE10MB = const(0x01) DHCP_HTYPE100MB = const(0x02)

DHCP_HLENETHERNET = const(0x06) DHCP_HOPS = const(0x00)

MAGIC_COOKIE = const(0x63825363) MAX_DHCP_OPT = const(0x10)

Default DHCP Server port

DHCP_SERVER_PORT = const(67)

DHCP Lease Time, in seconds

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

Packet buffer

_BUFF = bytearray(318)

class DHCP: """W5k DHCP Client implementation. :param eth: Wiznet 5k object :param list mac_address: Hardware MAC. :param str hostname: The desired hostname, with optional {} to fill in MAC. :param int response_timeout: DHCP Response timeout. :param bool debug: Enable debugging output.

"""

# pylint: disable=too-many-arguments, too-many-instance-attributes, invalid-name
def __init__(
    self, eth, mac_address, hostname=None, response_timeout=30, debug=False
):
    self._debug = debug
    self._response_timeout = response_timeout
    self._mac_address = mac_address

    # Set socket interface
    socket.set_interface(eth)
    self._eth = eth
    self._sock = None

    # DHCP state machine
    self._dhcp_state = STATE_DHCP_START
    self._initial_xid = 0
    self._transaction_id = 0
    self._start_time = 0

    # DHCP server configuration
    self.dhcp_server_ip = BROADCAST_SERVER_ADDR
    self.local_ip = 0
    self.gateway_ip = 0
    self.subnet_mask = 0
    self.dns_server_ip = 0

    # Lease configuration
    self._lease_time = 0
    self._last_lease_time = 0
    self._renew_in_sec = 0
    self._rebind_in_sec = 0
    self._t1 = 0
    self._t2 = 0

    # Select an initial transaction id
    self._transaction_id = randint(1, 0x7FFFFFFF)

    # Host name
    mac_string = "".join("{:02X}".format(o) for o in mac_address)
    self._hostname = bytes(
        (hostname or "WIZnet{}").split(".")[0].format(mac_string)[:42], "utf-8"
    )

# pylint: disable=too-many-statements
def send_dhcp_message(self, state, time_elapsed, renew=False):
    """Assemble and send a DHCP message packet to a socket.
    :param int state: DHCP Message state.
    :param float time_elapsed: Number of seconds elapsed since DHCP process started
    :param bool renew: Set True for renew and rebind
    """
    _BUFF[:] = b"\x00" * len(_BUFF)
    # OP
    _BUFF[0] = DHCP_BOOT_REQUEST
    # HTYPE
    _BUFF[1] = DHCP_HTYPE10MB
    # HLEN
    _BUFF[2] = DHCP_HLENETHERNET
    # HOPS
    _BUFF[3] = DHCP_HOPS

    # Transaction ID (xid)
    self._initial_xid = htonl(self._transaction_id)
    self._initial_xid = self._initial_xid.to_bytes(4, "l")
    _BUFF[4:7] = self._initial_xid

    # seconds elapsed
    _BUFF[8] = (int(time_elapsed) & 0xFF00) >> 8
    _BUFF[9] = int(time_elapsed) & 0x00FF

    # flags
    flags = htons(0x8000)
    flags = flags.to_bytes(2, "b")
    _BUFF[10] = flags[1]
    _BUFF[11] = flags[0]

    # NOTE: Skipping ciaddr/yiaddr/siaddr/giaddr
    # as they're already set to 0.0.0.0
    # Except when renewing, then fill in ciaddr
    if renew:
        _BUFF[12:15] = bytes(self.local_ip)

    # chaddr
    _BUFF[28:34] = self._mac_address

    # NOTE:  192 octets of 0's, BOOTP legacy

    # Magic Cookie
    _BUFF[236] = (MAGIC_COOKIE >> 24) & 0xFF
    _BUFF[237] = (MAGIC_COOKIE >> 16) & 0xFF
    _BUFF[238] = (MAGIC_COOKIE >> 8) & 0xFF
    _BUFF[239] = MAGIC_COOKIE & 0xFF

    # Option - DHCP Message Type
    _BUFF[240] = 53
    _BUFF[241] = 0x01
    _BUFF[242] = state

    # Option - Client Identifier
    _BUFF[243] = 61
    # Length
    _BUFF[244] = 0x07
    # HW Type - ETH
    _BUFF[245] = 0x01
    # Client MAC Address
    for mac in range(0, len(self._mac_address)):
        _BUFF[246 + mac] = self._mac_address[mac]

    # Option - Host Name
    _BUFF[252] = 12
    hostname_len = len(self._hostname)
    after_hostname = 254 + hostname_len
    _BUFF[253] = hostname_len
    _BUFF[254:after_hostname] = self._hostname

    if state == DHCP_REQUEST and not renew:
        # Set the parsed local IP addr
        _BUFF[after_hostname] = 50
        _BUFF[after_hostname + 1] = 0x04
        _BUFF[after_hostname + 2 : after_hostname + 6] = bytes(self.local_ip)
        # Set the parsed dhcp server ip addr
        _BUFF[after_hostname + 6] = 54
        _BUFF[after_hostname + 7] = 0x04
        _BUFF[after_hostname + 8 : after_hostname + 12] = bytes(self.dhcp_server_ip)

    _BUFF[after_hostname + 12] = 55
    _BUFF[after_hostname + 13] = 0x06
    # subnet mask
    _BUFF[after_hostname + 14] = 1
    # routers on subnet
    _BUFF[after_hostname + 15] = 3
    # DNS
    _BUFF[after_hostname + 16] = 6
    # domain name
    _BUFF[after_hostname + 17] = 15
    # renewal (T1) value
    _BUFF[after_hostname + 18] = 58
    # rebinding (T2) value
    _BUFF[after_hostname + 19] = 59
    _BUFF[after_hostname + 20] = 255

    # Send DHCP packet
    self._sock.send(_BUFF)

# pylint: disable=too-many-branches, too-many-statements
def parse_dhcp_response(self):
    """Parse DHCP response from DHCP server.
    Returns DHCP packet type.
    """
    # store packet in buffer
    _BUFF = self._sock.recv()
    if self._debug:
        print("DHCP Response: ", _BUFF)

    # -- Parse Packet, FIXED -- #
    # Validate OP
    assert (
        _BUFF[0] == DHCP_BOOT_REPLY
    ), "Malformed Packet - \
        DHCP message OP is not expected BOOT Reply."

    xid = _BUFF[4:8]
    if bytes(xid) < self._initial_xid:
        print("f")
        return 0, 0

    self.local_ip = tuple(_BUFF[16:20])
    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 = tuple(_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 = tuple(_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 = tuple(_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 = tuple(_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 IP: {}\nDNS Server IP: {}\
              \nGateway IP: {}\nLocal IP: {}\nT1: {}\nT2: {}\nLease Time: {}".format(
                msg_type,
                self.subnet_mask,
                self.dhcp_server_ip,
                self.dns_server_ip,
                self.gateway_ip,
                self.local_ip,
                self._t1,
                self._t2,
                self._lease_time,
            )
        )

    gc.collect()
    return msg_type, xid

# pylint: disable=too-many-branches, too-many-statements
def _dhcp_state_machine(self):
    """DHCP state machine without wait loops to enable cooperative multi tasking
    This state machine is used both by the initial blocking lease request and
    the non-blocking DHCP maintenance function"""
    if self._eth.link_status:
        if self._dhcp_state == STATE_DHCP_DISCONN:
            self._dhcp_state = STATE_DHCP_START
    else:
        if self._dhcp_state != STATE_DHCP_DISCONN:
            self._dhcp_state = STATE_DHCP_DISCONN
            self.dhcp_server_ip = BROADCAST_SERVER_ADDR
            self._last_lease_time = 0
            reset_ip = (0, 0, 0, 0)
            self._eth.ifconfig = (reset_ip, reset_ip, reset_ip, reset_ip)
            if self._sock is not None:
                self._sock.close()
                self._sock = None

    if self._dhcp_state == STATE_DHCP_START:
        self._start_time = time.time()
        self._transaction_id = (self._transaction_id + 1) & 0x7FFFFFFF
        try:
            self._sock = socket.socket(type=socket.SOCK_DGRAM)
        except RuntimeError:
            if self._debug:
                print("* DHCP: Failed to allocate socket")
            self._dhcp_state = STATE_DHCP_WAIT
        else:
            self._sock.settimeout(self._response_timeout)
            self._sock.bind((None, 68))
            self._sock.connect((self.dhcp_server_ip, DHCP_SERVER_PORT))
            if self._last_lease_time == 0 or time.time() > (
                self._last_lease_time + self._lease_time
            ):
                if self._debug:
                    print("* DHCP: Send discover to {}".format(self.dhcp_server_ip))
                self.send_dhcp_message(
                    STATE_DHCP_DISCOVER, (time.time() - self._start_time)
                )
                self._dhcp_state = STATE_DHCP_DISCOVER
            else:
                if self._debug:
                    print("* DHCP: Send request to {}".format(self.dhcp_server_ip))
                self.send_dhcp_message(
                    DHCP_REQUEST, (time.time() - self._start_time), True
                )
                self._dhcp_state = STATE_DHCP_REQUEST

    elif self._dhcp_state == STATE_DHCP_DISCOVER:
        if self._sock.available():
            if self._debug:
                print("* DHCP: Parsing OFFER")
            msg_type, xid = self.parse_dhcp_response()
            if msg_type == DHCP_OFFER:
                # Check if transaction ID matches, otherwise it may be an offer
                # for another device
                if htonl(self._transaction_id) == int.from_bytes(xid, "l"):
                    if self._debug:
                        print(
                            "* DHCP: Send request to {}".format(self.dhcp_server_ip)
                        )
                    self._transaction_id = (self._transaction_id + 1) & 0x7FFFFFFF
                    self.send_dhcp_message(
                        DHCP_REQUEST, (time.time() - self._start_time)
                    )
                    self._dhcp_state = STATE_DHCP_REQUEST
                else:
                    if self._debug:
                        print("* DHCP: Received OFFER with non-matching xid")
            else:
                if self._debug:
                    print("* DHCP: Received DHCP Message is not OFFER")

    elif self._dhcp_state == STATE_DHCP_REQUEST:
        if self._sock.available():
            if self._debug:
                print("* DHCP: Parsing ACK")
            msg_type, xid = self.parse_dhcp_response()
            # Check if transaction ID matches, otherwise it may be
            # for another device
            if htonl(self._transaction_id) == int.from_bytes(xid, "l"):
                if msg_type == DHCP_ACK:
                    if self._debug:
                        print("* DHCP: Successful lease")
                    self._sock.close()
                    self._sock = None
                    self._dhcp_state = STATE_DHCP_LEASED
                    self._last_lease_time = self._start_time
                    if self._lease_time == 0:
                        self._lease_time = DEFAULT_LEASE_TIME
                    if self._t1 == 0:
                        # T1 is 50% of _lease_time
                        self._t1 = self._lease_time >> 1
                    if self._t2 == 0:
                        # T2 is 87.5% of _lease_time
                        self._t2 = self._lease_time - (self._lease_time >> 3)
                    self._renew_in_sec = self._t1
                    self._rebind_in_sec = self._t2
                    self._eth.ifconfig = (
                        self.local_ip,
                        self.subnet_mask,
                        self.gateway_ip,
                        self.dns_server_ip,
                    )
                    gc.collect()
                else:
                    if self._debug:
                        print("* DHCP: Received DHCP Message is not ACK")
            else:
                if self._debug:
                    print("* DHCP: Received non-matching xid")

    elif self._dhcp_state == STATE_DHCP_WAIT:
        if time.time() > (self._start_time + DHCP_WAIT_TIME):
            if self._debug:
                print("* DHCP: Begin retry")
            self._dhcp_state = STATE_DHCP_START
            if time.time() > (self._last_lease_time + self._rebind_in_sec):
                self.dhcp_server_ip = BROADCAST_SERVER_ADDR
            if time.time() > (self._last_lease_time + self._lease_time):
                reset_ip = (0, 0, 0, 0)
                self._eth.ifconfig = (reset_ip, reset_ip, reset_ip, reset_ip)

    elif self._dhcp_state == STATE_DHCP_LEASED:
        if time.time() > (self._last_lease_time + self._renew_in_sec):
            self._dhcp_state = STATE_DHCP_START
            if self._debug:
                print("* DHCP: Time to renew lease")

    if (
        self._dhcp_state == STATE_DHCP_DISCOVER
        or self._dhcp_state == STATE_DHCP_REQUEST
    ) and time.time() > (self._start_time + self._response_timeout):
        self._dhcp_state = STATE_DHCP_WAIT
        if self._sock is not None:
            self._sock.close()
            self._sock = None

def request_dhcp_lease(self):
    """Request to renew or acquire a DHCP lease."""
    if self._dhcp_state == STATE_DHCP_LEASED or self._dhcp_state == STATE_DHCP_WAIT:
        self._dhcp_state = STATE_DHCP_START

    while (
        self._dhcp_state != STATE_DHCP_LEASED
        and self._dhcp_state != STATE_DHCP_WAIT
    ):
        self._dhcp_state_machine()

    return self._dhcp_state == STATE_DHCP_LEASED

def maintain_dhcp_lease(self):
    """Maintain DHCP lease"""
    self._dhcp_state_machine()

**五、驱动3**


wiznet5k\_dns.py



SPDX-FileCopyrightText: 2009-2010 MCQN Ltd

SPDX-FileCopyrightText: Brent Rubell for Adafruit Industries

SPDX-FileCopyrightText: 2021 Vincenzo D'Angelo

SPDX-License-Identifier: MIT

""" wiznet5k_dns

Pure-Python implementation of the Micropython on ESP32 DNS client for WIZnet 5k-based ethernet modules.

  • Author(s): MCQN Ltd, Brent Rubell, Vincenzo D'Angelo

""" import time import wiznet5k_socket as socket

from wiznet5k_socket import htons from random import getrandbits from micropython import const

QUERY_FLAG = const(0x00) OPCODE_STANDARD_QUERY = const(0x00) RECURSION_DESIRED_FLAG = 1 << 8

TYPE_A = const(0x0001) CLASS_IN = const(0x0001) DATA_LEN = const(0x0004)

Return codes for gethostbyname

SUCCESS = const(1) TIMED_OUT = const(-1) INVALID_SERVER = const(-2) TRUNCATED = const(-3) INVALID_RESPONSE = const(-4)

DNS_PORT = const(0x35) # port used for DNS request

class DNS: """W5K DNS implementation. :param iface: Network interface """

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)
    self._sock.settimeout(1)

    self._dns_server = dns_address
    self._host = 0
    self._request_id = 0  # request identifier
    self._pkt_buf = bytearray()


def gethostbyname(self, hostname):
    """Translate a host name to IPv4 address format.
    :param str hostname: Desired host name to connect to.

    Returns the IPv4 address as a bytearray if successful, -1 otherwise.
    """
    if self._dns_server is None:
        return INVALID_SERVER
    self._host = hostname
    # build DNS request packet
    self._build_dns_header()
    self._build_dns_question()

    # Send DNS request packet
    self._sock.bind((None, DNS_PORT))
    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 < 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


# pylint: disable=too-many-return-statements, too-many-branches, too-many-statements, too-many-locals
def _parse_dns_response(self):
    """Receives and parses DNS query response.
    Returns desired hostname address if obtained, -1 otherwise.
    """
    # wait for a response
    start_time = time.time()
    packet_sz = self._sock.available()
    while packet_sz <= 0:
        packet_sz = self._sock.available()
        if (time.time() - start_time) > 1.0:
            if self._debug:
                print("* DNS ERROR: Did not receive DNS response!")
            return -1
        time.sleep(0.05)
    # recv packet into buf
    self._pkt_buf = self._sock.recv()

    if self._debug:
        print("DNS Packet Received: ", self._pkt_buf)

    # Validate request identifier
    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
    flags = int.from_bytes(self._pkt_buf[2:4], "l")
    if not flags in (0x8180, 0x8580):
        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
    # 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

    # 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."""
    # generate a random, 16-bit, request identifier
    self._request_id = getrandbits(16)

    # ID, 16-bit identifier
    self._pkt_buf.append(self._request_id >> 8)
    self._pkt_buf.append(self._request_id & 0xFF)

    # Flags (0x0100)
    self._pkt_buf.append(0x01)
    self._pkt_buf.append(0x00)

    # QDCOUNT
    self._pkt_buf.append(0x00)
    self._pkt_buf.append(0x01)
    # ANCOUNT
    self._pkt_buf.append(0x00)
    self._pkt_buf.append(0x00)
    # NSCOUNT
    self._pkt_buf.append(0x00)
    self._pkt_buf.append(0x00)
    # ARCOUNT
    self._pkt_buf.append(0x00)
    self._pkt_buf.append(0x00)


def _build_dns_question(self):
    """Build DNS question"""
    host = self._host.decode("utf-8")
    host = host.split(".")
    # write out each section of host
    for i, _ in enumerate(host):
        # append the sz of the section
        self._pkt_buf.append(len(host[i]))
        # append the section data
        self._pkt_buf += host[i]
    # end of the name
    self._pkt_buf.append(0x00)
    # Type A record
    self._pkt_buf.append(htons(TYPE_A) & 0xFF)
    self._pkt_buf.append(htons(TYPE_A) >> 8)
    # Class IN
    self._pkt_buf.append(htons(CLASS_IN) & 0xFF)
    self._pkt_buf.append(htons(CLASS_IN) >> 8)

**六、驱动4**


wiznet5k\_socket.py



SPDX-FileCopyrightText: 2019 ladyada for Adafruit Industries

SPDX-FileCopyrightText: 2020 Brent Rubell for Adafruit Industries

SPDX-FileCopyrightText: 2021 Vincenzo D'Angelo

SPDX-License-Identifier: MIT

""" wiznet5k_socket

A socket compatible interface with the Wiznet5k module.

  • Author(s): ladyada, Brent Rubell, Patrick Van Oosterwijck, Adam Cummick, Vincenzo D'Angelo

"""

import gc import time import wiznet5k as wiznet5k

from micropython import const

_the_interface = None # pylint: disable=invalid-name

def set_interface(iface): """Helper to set the global internet interface.""" global _the_interface # pylint: disable=global-statement, invalid-name _the_interface = iface

def htonl(x): """Convert 32-bit positive integers from host to network byte order.""" return ( ((x) << 24 & 0xFF000000) | ((x) << 8 & 0x00FF0000) | ((x) >> 8 & 0x0000FF00) | ((x) >> 24 & 0x000000FF) )

def htons(x): """Convert 16-bit positive integers from host to network byte order.""" return (((x) << 8) & 0xFF00) | (((x) >> 8) & 0xFF)

SOCK_STREAM = const(0x21) # TCP TCP_MODE = 80 SOCK_DGRAM = const(0x02) # UDP AF_INET = const(3) SOCKET_INVALID = const(255)

pylint: disable=too-many-arguments, unused-argument

def getaddrinfo(host, port, family=0, socktype=0, proto=0, flags=0): """Translate the host/port argument into a sequence of 5-tuples that contain all the necessary arguments for creating a socket connected to that service.

"""
if not isinstance(port, int):
    raise RuntimeError("Port must be an integer")
if is_ipv4(host):
    return [(AF_INET, socktype, proto, "", (host, port))]
return [(AF_INET, socktype, proto, "", (gethostbyname(host), port))]

def gethostbyname(hostname): """Translate a host name to IPv4 address format. The IPv4 address is returned as a string. :param str hostname: Desired hostname. """ addr = _the_interface.get_host_by_name(hostname) addr = "{}.{}.{}.{}".format(addr[0], addr[1], addr[2], addr[3]) return addr

def is_ipv4(host): """Checks if a host string is an IPv4 address. :param str host: host's name or ip """ octets = host.split(".", 3) if len(octets) != 4 or not "".join(octets).isdigit(): return False for octet in octets: if int(octet) > 255: return False return True

pylint: disable=invalid-name, too-many-public-methods

class socket: """A simplified implementation of the Python 'socket' class for connecting to a Wiznet5k module. :param int family: Socket address (and protocol) family. :param int type: Socket type.

"""

# pylint: disable=redefined-builtin,unused-argument
def __init__(self, family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None, socknum=None):
    if family != AF_INET:
        raise RuntimeError("Only AF_INET family supported by W5K modules.")
    self._sock_type = type
    self._buffer = b""
    self._timeout = 0
    self._listen_port = None

    self._socknum = _the_interface.get_socket()
    if self._socknum == SOCKET_INVALID:
        raise RuntimeError("Failed to allocate socket.")

def __enter__(self):
    return self

def __exit__(self, exc_type, exc_val, exc_tb):
    if self._sock_type == SOCK_STREAM:
        self.disconnect()
        stamp = time.time()
        while self.status == wiznet5k.SNSR_SOCK_FIN_WAIT:
            if time.time() - stamp > 1000:
                raise RuntimeError("Failed to disconnect socket")
    self.close()
    stamp = time.time()
    while self.status != wiznet5k.SNSR_SOCK_CLOSED:
        if time.time() - stamp > 1000:
            raise RuntimeError("Failed to close socket")

@property
def socknum(self):
    """Returns the socket object's socket number."""
    return self._socknum

@property
def status(self):
    """Returns the status of the socket"""
    return _the_interface.socket_status(self.socknum)[0]

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

def getpeername(self):
    """Return the remote address to which the socket is connected."""
    return _the_interface.remote_ip(self.socknum)

def inet_aton(self, ip_string):
    """Convert an IPv4 address from dotted-quad string format.
    :param str ip_string: IP Address, as a dotted-quad string.

    """
    self._buffer = b""
    self._buffer = [int(item) for item in ip_string.split(".")]
    self._buffer = bytearray(self._buffer)
    return self._buffer

def bind(self, address):
    """Bind the socket to the listen port, if host is specified the interface
    will be reconfigured to that IP.
    :param tuple address: local socket as a (host, port) tuple.
    """
    if address[0] is not None:
        ip_address = _the_interface.unpretty_ip(address[0])
        current_ip, subnet_mask, gw_addr, dns = _the_interface.ifconfig
        if ip_address != current_ip:
            _the_interface.ifconfig = (ip_address, subnet_mask, gw_addr, dns)
    self._listen_port = address[1]
    # For UDP servers we need to open the socket here because we won't call
    # listen
    if self._sock_type == SOCK_DGRAM:
        _the_interface.socket_listen(
            self.socknum, self._listen_port, wiznet5k.SNMR_UDP
        )
        self._buffer = b""

def listen(self, backlog=None):
    """Listen on the port specified by bind.
    :param backlog: For compatibility but ignored.
    """
    assert self._listen_port is not None, "Use bind to set the port before listen!"
    _the_interface.socket_listen(self.socknum, self._listen_port)
    self._buffer = b""

def accept(self):
    """Accept a connection. The socket must be bound to an address and listening for
    connections. The return value is a pair (conn, address) where conn is a new
    socket object usable to send and receive data on the connection, and address is
    the address bound to the socket on the other end of the connection.
    """
    stamp = time.time()
    while self.status not in (
        wiznet5k.SNSR_SOCK_SYNRECV,
        wiznet5k.SNSR_SOCK_ESTABLISHED,
    ):
        if self._timeout > 0 and time.time() - stamp > self._timeout:
            return None
        if self.status == wiznet5k.SNSR_SOCK_CLOSED:
            self.close()
            self.listen()

    new_listen_socknum, addr = _the_interface.socket_accept(self.socknum)
    current_socknum = self.socknum
    # Create a new socket object and swap socket nums so we can continue listening
    client_sock = socket()
    client_sock._socknum = current_socknum  # pylint: disable=protected-access
    self._socknum = new_listen_socknum  # pylint: disable=protected-access
    self.bind((None, self._listen_port))
    self.listen()
    while self.status != wiznet5k.SNSR_SOCK_LISTEN:
        raise RuntimeError("Failed to open new listening socket")
    return client_sock, addr

def connect(self, address, conntype=None):
    """Connect to a remote socket at address.
    :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"):
        try:
            host = tuple(map(int, host.split(".")))
        except ValueError:
            host = _the_interface.get_host_by_name(host)
    if self._listen_port is not None:
        _the_interface.src_port = self._listen_port
    result = _the_interface.socket_connect(
        self.socknum, host, port, conn_mode=self._sock_type
    )
    _the_interface.src_port = 0
    if not result:
        raise RuntimeError("Failed to connect to host", host)
    self._buffer = b""

def send(self, data):
    """Send data to the socket. The socket must be connected to
    a remote socket.
    :param bytearray data: Desired data to send to the socket.
    """
    _the_interface.socket_write(self.socknum, data, self._timeout)
    gc.collect()

def sendto(self, data, address):
    """Send data to the socket. The socket must be connected to
    a remote socket.
    :param bytearray data: Desired data to send to the socket.
    :param tuple address: Remote socket as a (host, port) tuple.
    """
    self.connect(address)
    return self.send(data)

def recv(self, bufsize=0, flags=0):  # pylint: disable=too-many-branches
    """Reads some bytes from the connected remote address.
    :param int bufsize: Maximum number of bytes to receive.
    :param int flags: ignored, present for compatibility.
    """
    if self.status == wiznet5k.SNSR_SOCK_CLOSED:
        return b""

    if bufsize == 0:
        # read everything on the socket
        while True:
            avail = self.available()
            if avail:
                if self._sock_type == SOCK_STREAM:
                    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]
            else:
                break
        gc.collect()
        ret = self._buffer
        self._buffer = b""
        gc.collect()
        return ret
    stamp = time.time()

    to_read = bufsize - len(self._buffer)
    received = []
    while to_read > 0:
        avail = self.available()
        if avail:
            stamp = time.time()
            if self._sock_type == SOCK_STREAM:
                recv = _the_interface.socket_read(
                    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 = bytes(recv)
            received.append(recv)
            to_read -= len(recv)
            gc.collect()
        if self._timeout > 0 and time.time() - stamp > self._timeout:
            break
    self._buffer += b"".join(received)

    ret = None
    if len(self._buffer) == bufsize:
        ret = self._buffer
        self._buffer = b""
    else:
        ret = self._buffer[:bufsize]
        self._buffer = self._buffer[bufsize:]
    gc.collect()
    return ret

def recvfrom(self, bufsize=0, flags=0):
    """Reads some bytes from the connected remote address.
    :param int bufsize: Maximum number of bytes to receive.
    :param int flags: ignored, present for compatibility.
    :returns: a tuple (bytes, address) where address is a tuple (ip, port)
    """
    return (
        self.recv(bufsize),
        (
            _the_interface.remote_ip(self.socknum),
            _the_interface.remote_port(self.socknum),
        ),
    )

def recv_into(self, buf, nbytes=0, flags=0):
    """Reads some bytes from the connected remote address info the provided buffer.
    :param bytearray buf: Data buffer
    :param nbytes: Maximum number of bytes to receive
    :param int flags: ignored, present for compatibility.
    :returns: the number of bytes received
    """
    if nbytes == 0:
        nbytes = len(buf)
    ret = self.recv(nbytes)
    nbytes = len(ret)
    buf[:nbytes] = ret
    return nbytes

def recvfrom_into(self, buf, nbytes=0, flags=0):
    """Reads some bytes from the connected remote address info the provided buffer.
    :param bytearray buf: Data buffer
    :param nbytes: Maximum number of bytes to receive
    :param int flags: ignored, present for compatibility.
    :returns a tuple (nbytes, address) where address is a tuple (ip, port)
    """
    return (
        self.recv_into(buf, nbytes),
        (
            _the_interface.remote_ip(self.socknum),
            _the_interface.remote_port(self.socknum),
        ),
    )

def readline(self):
    """Attempt to return as many bytes as we can up to \
    but not including '\r\n'.

    """
    stamp = time.time()
    while b"\r\n" not in self._buffer:
        avail = self.available()
        if avail:
            if self._sock_type == SOCK_STREAM:
                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]
        if (
            not avail
            and self._timeout > 0
            and time.time() - stamp > self._timeout
        ):
            self.close()
            raise RuntimeError("Didn't receive response, failing out...")
    firstline, self._buffer = self._buffer.split(b"\r\n", 1)
    gc.collect()
    return firstline

def disconnect(self):
    """Disconnects a TCP socket."""
    assert self._sock_type == SOCK_STREAM, "Socket must be a TCP socket."
    _the_interface.socket_disconnect(self.socknum)

def close(self):
    """Closes the socket."""
    _the_interface.socket_close(self.socknum)

def available(self):
    """Returns how many bytes of data are available to be read from the socket."""
    return _the_interface.socket_available(self.socknum, self._sock_type)

def settimeout(self, value):
    """Sets socket read timeout.
    :param int value: Socket read timeout, in seconds.

    """
    if value < 0:
        raise Exception("Timeout period should be non-negative.")
    self._timeout = value

def gettimeout(self):
    """Return the timeout in seconds (float) associated
    with socket operations, or None if no timeout is set.

    """
    return self._timeout

**七、驱动5**


sma\_esp32\_w5500\_requests.py



""" sma_requests

A requests-like library for web interfacing

  • Author(s): Seyed Mohammad Ayyoubzadeh

Implementation Notes

Adapted from github.com/adafruit/Ad…

author='Seyed Mohammad Ayyoubzadeh' license='MIT'

version = "1.0.0" repo = "github.com/Ayyoubzadeh…" """

import errno import sys

import json as json_module

always_safe = ('ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' '0123456789' '_.-') def encode(s): res = [] replacements = {} for c in s: if c in always_safe: res.append(c) continue res.append('%%%x' % ord(c)) return ''.join(res)

def cast(_t, value): return value

class _RawResponse: def init(self, response: "Response") -> None: self._response = response

def read(self, size: int = -1) -> bytes:
    """Read as much as available or up to size and return it in a byte string.

    Do NOT use this unless you really need to. Reusing memory with `readinto` is much better.
    """
    if size == -1:
        return self._response.content
    return self._response.socket.recv(size)

def readinto(self, buf: bytearray) -> int:
    """Read as much as available into buf or until it is full. Returns the number of bytes read
    into buf."""
    return self._response._readinto(buf)  # pylint: disable=protected-access

class OutOfRetries(Exception): """Raised when requests has retried to make a request unsuccessfully."""

class Response: """The response from a request, contains all the headers/content"""

# pylint: disable=too-many-instance-attributes

encoding = None

def __init__(self, sock: SocketType, session: Optional["Session"] = None) -> None:
    self.socket = sock
    self.encoding = "utf-8"
    self._cached = None
    self._headers = {}

    # _start_index and _receive_buffer are used when parsing headers.
    # _receive_buffer will grow by 32 bytes everytime it is too small.
    self._received_length = 0
    self._receive_buffer = bytearray(32)
    self._remaining = None
    self._chunked = False

    self._backwards_compatible = not hasattr(sock, "recv_into")

    http = self._readto(b" ")
    if not http:
        if session:
            session._close_socket(self.socket)
        else:
            self.socket.close()
        raise RuntimeError("Unable to read HTTP response.")
    self.status_code = int(bytes(self._readto(b" ")))
    self.reason = self._readto(b"\r\n")
    self._parse_headers()
    self._raw = None
    self._session = session

def __enter__(self) -> "Response":
    return self

def __exit__(
    self,
    exc_type: Optional[Type[type]],
    exc_value: Optional[BaseException],
    traceback: Optional[TracebackType],
) -> None:
    self.close()

def _recv_into(self, buf: bytearray, size: int = 0) -> int:
    if self._backwards_compatible:
        size = len(buf) if size == 0 else size
        b = self.socket.recv(size)
        read_size = len(b)
        buf[:read_size] = b
        return read_size
    return cast("SupportsRecvInto", self.socket).recv_into(buf, size)

def _readto(self, stop: bytes) -> bytearray:
    buf = self._receive_buffer
    end = self._received_length
    while True:
        i = bytes(buf).find(stop, 0, end)
        if i >= 0:
            # Stop was found. Return everything up to but not including stop.
            result = buf[:i]
            new_start = i + len(stop)
            # Remove everything up to and including stop from the buffer.
            new_end = end - new_start
            buf[:new_end] = buf[new_start:end]
            self._received_length = new_end
            return result

        # Not found so load more bytes.
        # If our buffer is full, then make it bigger to load more.
        if end == len(buf):
            new_buf = bytearray(len(buf) + 32)
            new_buf[: len(buf)] = buf
            buf = new_buf
            self._receive_buffer = buf

        read = self._recv_into(memoryview(buf)[end:])
        if read == 0:
            self._received_length = 0
            return buf[:end]
        end += read

def _read_from_buffer(
    self, buf: Optional[bytearray] = None, nbytes: Optional[int] = None
) -> int:
    if self._received_length == 0:
        return 0
    read = self._received_length
    if nbytes < read:
        read = nbytes
    membuf = memoryview(self._receive_buffer)
    if buf:
        buf[:read] = membuf[:read]
    if read < self._received_length:
        new_end = self._received_length - read
        self._receive_buffer[:new_end] = membuf[read : self._received_length]
        self._received_length = new_end
    else:
        self._received_length = 0
    return read

def _readinto(self, buf: bytearray) -> int:
    if not self.socket:
        raise RuntimeError(
            "Newer Response closed this one. Use Responses immediately."
        )

    if not self._remaining:
        # Consume the chunk header if need be.
        if self._chunked:
            # Consume trailing \r\n for chunks 2+
            if self._remaining == 0:
                self._throw_away(2)
            chunk_header = bytes(self._readto(b"\r\n")).split(b";", 1)[0]
            http_chunk_size = int(bytes(chunk_header), 16)
            if http_chunk_size == 0:
                self._chunked = False
                self._parse_headers()
                return 0
            self._remaining = http_chunk_size
        else:
            return 0

    nbytes = len(buf)
    if nbytes > self._remaining:
        nbytes = self._remaining

    read = self._read_from_buffer(buf, nbytes)
    if read == 0:
        read = self._recv_into(buf, nbytes)
    self._remaining -= read

    return read

def _throw_away(self, nbytes: int) -> None:
    nbytes -= self._read_from_buffer(nbytes=nbytes)

    buf = self._receive_buffer
    len_buf = len(buf)
    for _ in range(nbytes // len_buf):
        to_read = len_buf
        while to_read > 0:
            to_read -= self._recv_into(buf, to_read)
    to_read = nbytes % len_buf
    while to_read > 0:
        to_read -= self._recv_into(buf, to_read)

def close(self) -> None:
    """Drain the remaining ESP socket buffers. We assume we already got what we wanted."""
    if not self.socket:
        return
    # Make sure we've read all of our response.
    if self._cached is None:
        if self._remaining and self._remaining > 0:
            self._throw_away(self._remaining)
        elif self._chunked:
            while True:
                chunk_header = bytes(self._readto(b"\r\n")).split(b";", 1)[0]
                chunk_size = int(bytes(chunk_header), 16)
                if chunk_size == 0:
                    break
                self._throw_away(chunk_size + 2)
            self._parse_headers()
    if self._session:
        self._session._free_socket(self.socket)  # pylint: disable=protected-access
    else:
        self.socket.close()
    self.socket = None

def _parse_headers(self) -> None:
    """
    Parses the header portion of an HTTP request/response from the socket.
    Expects first line of HTTP request/response to have been read already.
    """
    while True:
        header = self._readto(b"\r\n")
        if not header:
            break
        title, content = bytes(header).split(b": ", 1)
        if title and content:
            # enforce that all headers are lowercase
            title = str(title, "utf-8").lower()

img img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取