{"action":"created","comment":{"url":"https://api.github.com/repos/electricimp/Wiznet_5500/pulls/comments/106309154","pull_request_review_id":27225725,"id":106309154,"diff_hunk":"@@ -0,0 +1,2256 @@\n+// Copyright (c) 2016-2017 Electric Imp\n+// This file is licensed under the MIT License\n+// http://opensource.org/licenses/MIT\n+\n+// BLOCK SELECT BITS\n+const W5500_COMMON_REGISTER = 0x00;\n+const W5500_S0_REGISTER = 0x08;\n+const W5500_S0_TX_BUFFER = 0x10;\n+const W5500_S0_RX_BUFFER = 0x18;\n+const W5500_S1_REGISTER = 0x28;\n+const W5500_S1_TX_BUFFER = 0x30;\n+const W5500_S1_RX_BUFFER = 0x38;\n+const W5500_S2_REGISTER = 0x48;\n+const W5500_S2_TX_BUFFER = 0x50;\n+const W5500_S2_RX_BUFFER = 0x58;\n+const W5500_S3_REGISTER = 0x68;\n+const W5500_S3_TX_BUFFER = 0x70;\n+const W5500_S3_RX_BUFFER = 0x78;\n+const W5500_S4_REGISTER = 0x88;\n+const W5500_S4_TX_BUFFER = 0x90;\n+const W5500_S4_RX_BUFFER = 0x98;\n+const W5500_S5_REGISTER = 0xA8;\n+const W5500_S5_TX_BUFFER = 0xB0;\n+const W5500_S5_RX_BUFFER = 0xB8;\n+const W5500_S6_REGISTER = 0xC8;\n+const W5500_S6_TX_BUFFER = 0xD0;\n+const W5500_S6_RX_BUFFER = 0xD8;\n+const W5500_S7_REGISTER = 0xE8;\n+const W5500_S7_TX_BUFFER = 0xF0;\n+const W5500_S7_RX_BUFFER = 0xF8;\n+\n+// READ/WRITE BIT\n+const W5500_READ_COMMAND = 0x00;\n+const W5500_WRITE_COMMAND = 0x04;\n+\n+// SPI OPPERATION MODE\n+const W5500_VARIABLE_DATA_LENGTH = 0x00;\n+const W5500_FIXED_DATA_LENGTH_1 = 0x01;\n+const W5500_FIXED_DATA_LENGTH_2 = 0x02;\n+const W5500_FIXED_DATA_LENGTH_4 = 0x03;\n+\n+// COMMON REGISTERS OFFSET ADDRESSES\n+// MR\n+const W5500_MODE = 0x0000;\n+\n+// GWR\n+const W5500_GATEWAY_ADDR_0 = 0x0001;\n+const W5500_GATEWAY_ADDR_1 = 0x0002;\n+const W5500_GATEWAY_ADDR_2 = 0x0003;\n+const W5500_GATEWAY_ADDR_3 = 0x0004;\n+\n+// SUBR\n+const W5500_SUBNET_MASK_ADDR_0 = 0x0005;\n+const W5500_SUBNET_MASK_ADDR_1 = 0x0006;\n+const W5500_SUBNET_MASK_ADDR_2 = 0x0007;\n+const W5500_SUBNET_MASK_ADDR_3 = 0x0008;\n+\n+// SHAR\n+const W5500_SOURCE_HW_ADDR_0 = 0x0009;\n+const W5500_SOURCE_HW_ADDR_1 = 0x000A;\n+const W5500_SOURCE_HW_ADDR_2 = 0x000B;\n+const W5500_SOURCE_HW_ADDR_3 = 0x000C;\n+const W5500_SOURCE_HW_ADDR_4 = 0x000D;\n+const W5500_SOURCE_HW_ADDR_5 = 0x000E;\n+\n+// SIPR\n+const W5500_SOURCE_IP_ADDR_0 = 0x000F;\n+const W5500_SOURCE_IP_ADDR_1 = 0x0010;\n+const W5500_SOURCE_IP_ADDR_2 = 0x0011;\n+const W5500_SOURCE_IP_ADDR_3 = 0x0012;\n+\n+// INTLEVEL\n+const W5500_INTERRUPT_LOW_LEVEL_TIMER_0 = 0x0013;\n+const W5500_INTERRUPT_LOW_LEVEL_TIMER_1 = 0x0014;\n+\n+// IR\n+const W5500_INTERRUPT = 0x0015;\n+// IMR\n+const W5500_INTERRUPT_MASK = 0x0016;\n+\n+// SIR\n+const W5500_SOCKET_INTERRUPT = 0x0017;\n+// SIMR\n+const W5500_SOCKET_INTERRUPT_MASK = 0x0018;\n+\n+// RTR\n+const W5500_RETRY_TIME_0 = 0x0019;\n+const W5500_RETRY_TIME_1 = 0x001A;\n+// RCR\n+const W5500_RETRY_COUNT = 0x001B;\n+\n+// PHYSICAL CONFIG\n+const W5500_PHYSICAL_CONFIG = 0x002E;\n+\n+// VERSION\n+const W5500_CHIP_VERSION = 0x0039;\n+\n+\n+// SOCKET REGISTER OFFSET ADDRESSES\n+const W5500_SOCKET_MODE = 0x0000;\n+const W5500_SOCKET_COMMAND = 0x0001;\n+const W5500_SOCKET_N_INTERRUPT = 0x0002;\n+const W5500_SOCKET_STATUS = 0x0003;\n+\n+const W5500_SOCKET_SOURCE_PORT_0 = 0x0004;\n+const W5500_SOCKET_SOURCE_PORT_1 = 0x0005;\n+\n+const W5500_SOCKET_DEST_HW_ADDR_0 = 0x0006;\n+const W5500_SOCKET_DEST_HW_ADDR_1 = 0x0007;\n+const W5500_SOCKET_DEST_HW_ADDR_2 = 0x0008;\n+const W5500_SOCKET_DEST_HW_ADDR_3 = 0x0009;\n+const W5500_SOCKET_DEST_HW_ADDR_4 = 0x000A;\n+const W5500_SOCKET_DEST_HW_ADDR_5 = 0x000B;\n+\n+const W5500_SOCKET_DEST_IP_ADDR_0 = 0x000C;\n+const W5500_SOCKET_DEST_IP_ADDR_1 = 0x000D;\n+const W5500_SOCKET_DEST_IP_ADDR_2 = 0x000E;\n+const W5500_SOCKET_DEST_IP_ADDR_3 = 0x000F;\n+\n+const W5500_SOCKET_DEST_PORT_0 = 0x0010;\n+const W5500_SOCKET_DEST_PORT_1 = 0x0011;\n+\n+const W5500_SOCKET_RX_BUFFER_SIZE = 0x001E;\n+const W5500_SOCKET_TX_BUFFER_SIZE = 0x001F;\n+\n+// SOCKET TX FREE SIZE REGISTER (Sn_TX_FREE_SIZE)\n+const W5500_SOCKET_TX_SIZE_R1 = 0x0020;\n+const W5500_SOCKET_TX_SIZE_R2 = 0x0021;\n+\n+// SOCKET TX READ POINTER\n+const W5500_SOCKET_TX_RP_R1 = 0x0022;\n+const W5500_SOCKET_TX_RP_R2 = 0x0023;\n+\n+// SOCKET TX WRITE POINTER\n+const W5500_SOCKET_TX_WP_R1 = 0x0024;\n+const W5500_SOCKET_TX_WP_R2 = 0x0025;\n+\n+// SOCKET RX RECEIVED SIZE REGISTER (Sn_RX_RSR)\n+const W5500_SOCKET_RX_SIZE_R1 = 0x0026;\n+const W5500_SOCKET_RX_SIZE_R2 = 0x0027;\n+\n+// SOCKET RX READ POINTER (Sn_RX_RD)\n+const W5500_SOCKET_RX_RP_R1 = 0x0028;\n+const W5500_SOCKET_RX_RP_R2 = 0x0029;\n+\n+// SOCKET N INTERRUPT MASK (Sn_IMR)\n+const W5500_SOCKET_N_INTERRUPT_MASK = 0x002C;\n+\n+// MODES\n+const W5500_SW_RESET = 0x80;\n+const W5500_WAKE_ON_LAN = 0x20;\n+const W5500_PING_BLOCK = 0x10;\n+const W5500_PPPoE = 0x08;\n+const W5500_FORCE_ARP = 0x01;\n+const W5500_DEFAULT_MODE = 0x00;\n+\n+// SOCKET MODES (Sn_MR)\n+const W5500_SOCKET_MODE_MULTI = 0x80;\n+const W5500_SOCKET_MODE_BROADCAST_BLOCKING = 0x40;\n+const W5500_SOCKET_MODE_NO_DELAY_ACK = 0x20;\n+const W5500_SOCKET_MODE_UNICAST_BLOCKING = 0x10;\n+const W5500_SOCKET_MODE_CLOSED = 0x00;\n+const W5500_SOCKET_MODE_TCP = 0x01;\n+const W5500_SOCKET_MODE_UDP = 0x02;\n+const W5500_SOCKET_MODE_MACRAW = 0x03;\n+\n+// SOCKET COMMANDS (Sn_CR)\n+const W5500_SOCKET_OPEN = 0x01;\n+const W5500_SOCKET_LISTEN = 0x02;\n+const W5500_SOCKET_CONNECT = 0x04;\n+const W5500_SOCKET_DISCONNECT = 0x08;\n+const W5500_SOCKET_CLOSE = 0x10;\n+const W5500_SOCKET_SEND = 0x20;\n+const W5500_SOCKET_SEND_MAC = 0x21;\n+const W5500_SOCKET_SEND_KEEP = 0x22;\n+const W5500_SOCKET_RECEIVE = 0x40;\n+\n+// SOCKET STATUS (Sn_SR)\n+const W5500_SOCKET_STATUS_CLOSED = 0x00;\n+const W5500_SOCKET_STATUS_INIT = 0x13;\n+const W5500_SOCKET_STATUS_LISTEN = 0x14;\n+const W5500_SOCKET_STATUS_ESTABLISHED = 0x17;\n+const W5500_SOCKET_STATUS_CLOSE_WAIT = 0x1C;\n+const W5500_SOCKET_STATUS_UDP = 0x22;\n+const W5500_SOCKET_STATUS_MACRAW = 0x42;\n+const W5500_SOCKET_STATUS_SYNSENT = 0x15;\n+const W5500_SOCKET_STATUS_SYNRECV = 0x16;\n+const W5500_SOCKET_STATUS_FIN_WAIT = 0x18;\n+const W5500_SOCKET_STATUS_CLOSING = 0x1A;\n+const W5500_SOCKET_STATUS_TIME_WAIT = 0x1B;\n+const W5500_SOCKET_STATUS_LAST_ACK = 0x1D;\n+\n+// INTERRUPT TYPES\n+const W5500_CONFLICT_INT_TYPE = 0x80;\n+const W5500_UNREACH_INT_TYPE = 0x40;\n+const W5500_PPPoE_INT_TYPE = 0x20;\n+const W5500_MAGIC_PACKET_TYPE = 0x10;\n+const W5500_NONE_INT_TYPE = 0x00;\n+\n+// SOCKET INTERRUPTS\n+const W5500_DISABLE_SOCKET_INTERRUPTS = 0x00;\n+const W5500_S0_INTERRUPT = 0x01;\n+const W5500_S1_INTERRUPT = 0x02;\n+const W5500_S2_INTERRUPT = 0x04;\n+const W5500_S3_INTERRUPT = 0x08;\n+const W5500_S4_INTERRUPT = 0x10;\n+const W5500_S5_INTERRUPT = 0x20;\n+const W5500_S6_INTERRUPT = 0x40;\n+const W5500_S7_INTERRUPT = 0x80;\n+\n+// SOCKET INTERRUPT TYPES\n+const W5500_SEND_COMPLETE_INT_TYPE = 0x10;\n+const W5500_TIMEOUT_INT_TYPE = 0x08;\n+const W5500_DATA_RECEIVED_INT_TYPE = 0x04;\n+const W5500_DISCONNECTED_INT_TYPE = 0x02;\n+const W5500_CONNECTED_INT_TYPE = 0x01;\n+\n+// Socket states\n+enum W5500_SOCKET_STATES {\n+    CLOSED,\n+    INIT,\n+    CONNECTING,\n+    ESTABLISHED,\n+    DISCONNECTING\n+}\n+\n+// Error messages\n+const W5500_INVALID_PARAMETERS = \"Provide 'ip', 'port', 'mode' and a callback.\";\n+const W5500_CANNOT_CONNECT_STILL_CLEANING = \"Cannot open a connection. Still cleaning up.\";\n+const W5500_CANNOT_CONNECT_SOCKETS_IN_USE = \"Cannot open a connection. All connection sockets in use.\";\n+const W5500_CANNOT_CONNECT_TIMEOUT = \"Connection timeout\";\n+const W5500_TRANSMIT_TIMEOUT = \"Transmit timeout\";\n+\n+// Miscellaneous constants\n+const W5500_TRANSMIT_TIMEOUT = 8;\n+\n+// ==============================================================================\n+// CLASS: W5500\n+// ==============================================================================\n+\n+class W5500 {\n+\n+    static VERSION = \"1.0.0\";\n+\n+    _driver = null;\n+    _interruptPin = null;\n+    _isReady = false; // set to true once the driver is loaded and connection to chip made\n+    _readyCb = null; // callback for when isReady becomes true\n+    _networkSettings = null;\n+\n+    // TODO: implement this\n+    _autoRetry = false;\n+\n+\n+    // ***************************************************************************\n+    // Constructor\n+    // Returns: null\n+    // Parameters:\n+    //      spi - configured spi bus, chip supports spi mode 0 or 3\n+    //      cs(optional) -  chip select pin, pass in if not using imp005\n+    //      interruptPin - inerrupt pin\n+    //      reset(optional) - reset pin\n+    // ***************************************************************************\n+    constructor(interruptPin, spi, csPin = null, resetPin = null, autoRetry = false) {\n+\n+        // Initialise the driver and close any stale connections\n+        _driver = W5500.Driver(spi, csPin, resetPin);\n+        _driver.init(function() {\n+\n+            // Configure interrupts\n+            _driver.setInterrupt(W5500_CONFLICT_INT_TYPE);\n+            _driver.clearInterrupt();\n+            _driver.clearSocketInterrupts();\n+            _interruptPin = interruptPin.configure(DIGITAL_IN_PULLUP, _interruptHandler.bindenv(this));\n+\n+            // Let the caller know the device is ready\n+            _isReady = true;\n+            if (_readyCb != null) imp.wakeup(0, _readyCb);\n+        }.bindenv(this));\n+    }\n+\n+    // ***************************************************************************\n+    // onReady\n+    // Returns: this\n+    // Parameters:\n+    // cb -  function to be called in the even _isReady is true\n+    // ***************************************************************************\n+    function onReady(cb) {\n+        local _cb = cb;\n+        if (_networkSettings != null) {\n+            // Slip the network settings into the _cb\n+            _cb = function() {\n+                configureNetworkSettings(_networkSettings.sourceIP, _networkSettings.subnetMask, _networkSettings.gatewayIP, _networkSettings.mac);\n+                _networkSettings = null;\n+                cb();\n+            }.bindenv(this);\n+        }\n+        if (_isReady) _cb();\n+        else _readyCb = _cb;\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // configureNetworkSettings\n+    // Returns: this\n+    // Parameters:\n+    //      sourceIP    -   ip adress of the Wiznet\n+    //      subnetMask  -   subnetMask for the Wiznet\n+    //      gatewayIP   -   gatewayIP for the Wiznet\n+    //      mac         -   mac adress for the wiznet\n+    // ***************************************************************************\n+    function configureNetworkSettings(sourceIP, subnetMask = null, gatewayIP = null, mac = null) {\n+        if (_isReady) {\n+            if (gatewayIP) _driver.setGatewayAddr(gatewayIP);\n+            if (mac) _driver.setSourceHWAddr(mac);\n+            else _driver.setSourceHWAddr(imp.getmacaddress(), true);\n+            if (subnetMask) _driver.setSubnetMask(subnetMask);\n+            if (sourceIP) _driver.setSourceIP(sourceIP);\n+            _networkSettings = null;\n+        } else {\n+            _networkSettings = {};\n+            _networkSettings.sourceIP <- sourceIP;\n+            _networkSettings.subnetMask <- subnetMask;\n+            _networkSettings.gatewayIP <- gatewayIP;\n+            _networkSettings.mac <- mac;\n+        }\n+\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // openConnection\n+    // Returns: connection instance\n+    // Parameters:\n+    //      ip - the ip address of the destination\n+    //      port - the port of the destination\n+    //      mode - TCP or UDP\n+    //      cb - function to be called when connection successfully\n+    //                     established or a timeout has occurred\n+    // ****************************************************************************\n+    function openConnection(ip, port, mode = W5500_SOCKET_MODE_TCP, cb = null) {\n+        if (!_isReady) throw \"Wiznet driver not ready\";\n+\n+        if ((mode == null && cb == null) || (typeof mode != \"function\" && cb == null)) {\n+            if (cb) cb(W5500_INVALID_PARAMETERS, null);\n+            else throw W5500_INVALID_PARAMETERS;\n+            return;\n+        } else if (typeof mode == \"function\") {\n+            cb = mode;\n+            mode = W5500_SOCKET_MODE_TCP;\n+        }\n+\n+        // Create a socket, a connection object & a handler\n+        _driver.openConnection(ip, port, mode, cb);\n+    }\n+\n+    // ***************************************************************************\n+    // reset, note this is blocking for 0.2s\n+    // Returns: this\n+    // Parameters:\n+    //          sw(optional) - boolean if true forces a software reset\n+    //          Note: Datasheet states that SW reset should not be used\n+    //                 on the W5500.\n+    // ***************************************************************************\n+    function reset(sw = false) {\n+        _driver.reset(sw);\n+    }\n+\n+\n+    // PRIVATE FUNCTIONS\n+    // ---------------------------------------------\n+\n+    // ***************************************************************************\n+    // _interruptHandler, checks interrupt registers and calls appropriate handlers\n+    // Returns: null\n+    // Parameters: none\n+    // ***************************************************************************\n+    function _interruptHandler() {\n+        // Handle the main interrupt\n+        local interrupt = _driver.getInterruptStatus();\n+        if (interrupt.CONFLICT) _handleConflictInt();\n+\n+        // Handle the socket interrupts\n+        _driver.socketInterruptHandler();\n+    }\n+\n+    // ***************************************************************************\n+    // _handleConflictInt, logs conflict error message & clears interrupt\n+    // Returns: null\n+    // Parameters: none\n+    // ***************************************************************************\n+    function _handleConflictInt() {\n+        _driver.clearInterrupt(W5500_CONFLICT_INT_TYPE);\n+        server.error(\"Conflict interrupt occured. Please check IP source and destination addresses\");\n+    }\n+\n+}\n+\n+\n+// ==============================================================================\n+// CLASS: W5500.Driver\n+// ==============================================================================\n+\n+class W5500.Driver {\n+\n+    // Chip can support 8 sockets\n+    static TOTAL_SUPPORTED_SOCKETS = 8;\n+    static MAX_TX_MEM_BUFFER = 16;\n+    static MAX_RX_MEM_BUFFER = 16;\n+\n+    // CLASS VARIABLES\n+    _spi = null;\n+    _cs = null;\n+    _resetPin = null;\n+\n+    _connections = null; // open connections\n+    _availableSockets = null; // array of sockets available\n+    _maxNoOfSockets = 0; // max number of sockets wiznet may use\n+    _noOfSockets = 0; // number of sockets\n+    _socketMemory = null; // amount of memeory each socket has\n+\n+    // ***************************************************************************\n+    // Constructor\n+    // Returns: null\n+    // Parameters:\n+    //      spi - configured spi, W5500 supports spi mode 0 or 3\n+    //      cs(optional) - configured chip select pin\n+    //      reset(optional) - configured reset pin\n+    // ***************************************************************************\n+    constructor(spi, cs = null, resetPin = null) {\n+        _spi = spi;\n+        _cs = cs;\n+        _resetPin = resetPin;\n+        _connections = {};\n+        _availableSockets = [];\n+\n+        // Check the pins\n+        if (resetPin) _resetPin.configure(DIGITAL_OUT, 1);\n+\n+        local imp005 = (\"spi0\" in hardware);\n+        if (_cs != null) {\n+            _cs.configure(DIGITAL_OUT, 1);\n+        } else if (!imp005) {\n+            throw \"You must pass in a chip select pin.\"\n+        }\n+\n+        // Reset the hardware\n+        reset();\n+\n+        // server.log(\"Wiznet chip version: \" + getChipVersion());\n+\n+    }\n+\n+    // ***************************************************************************\n+    // init(cb)\n+    // Returns: nothing\n+    // Parameters:\n+    //        cb                        - callback function\n+    //        remaining(optional)       - for first time Initialise == null\n+    //                                  - subseuqent goes to to else\n+    //        closes all sockets and fires the callback when complete\n+    // ***************************************************************************\n+    function init(cb, remaining = null) {\n+\n+        if (remaining == null) {\n+\n+            // Initialise the number of sockets\n+            _maxNoOfSockets = getTotalSupportedSockets();\n+\n+            // The first time through, check what needs to be dealt with\n+            remaining = [];\n+            for (local socket = 0; socket < _maxNoOfSockets; socket++) {\n+                local status = getSocketStatus(socket);\n+                if (status == W5500_SOCKET_STATUS_ESTABLISHED) {\n+                    sendSocketCommand(socket, W5500_SOCKET_DISCONNECT);\n+                    remaining.push({ socket = socket, status = status });\n+                } else if (status != W5500_SOCKET_STATUS_CLOSED) {\n+                    remaining.push({ socket = socket, status = status });\n+                }\n+            }\n+\n+        } else {\n+\n+            // The second+ time through\n+            local oldremaining = remaining;\n+            remaining = [];\n+            foreach (prev in oldremaining) {\n+\n+                local socket = prev.socket;\n+                local prevstatus = prev.status;\n+                local newstatus = getSocketStatus(socket);\n+\n+                if (newstatus == W5500_SOCKET_STATUS_CLOSED) {\n+                    sendSocketCommand(socket, W5500_SOCKET_CLOSE);\n+                } else {\n+                    remaining.push({ socket = socket, status = newstatus });\n+                }\n+\n+            }\n+\n+        }\n+\n+        if (remaining.len() == 0) {\n+\n+            // Set the defaults\n+            _availableSockets = setNumberOfAvailableSockets(4);\n+\n+            // Let the sockets settle down before starting anything\n+            imp.wakeup(1, cb);\n+        } else {\n+            imp.wakeup(0.1, function() {\n+                init(cb, remaining);\n+            }.bindenv(this));\n+        }\n+\n+    }\n+\n+\n+    // ***************************************************************************\n+    // getPhysicalLinkStatus\n+    // Returns: true or false\n+    // Parameters:\n+    //      none\n+    // ***************************************************************************\n+    function getPhysicalLinkStatus() {\n+        local status = readReg(W5500_PHYSICAL_CONFIG, W5500_COMMON_REGISTER);\n+        return (status & 0x01);\n+    }\n+\n+    // ***************************************************************************\n+    // openConnection\n+    // Returns: this\n+    // Parameters:\n+    //      destIP - the ip address of the destination\n+    //      destPort - the port of the destination \n+    //      mode - TCP or UDP\n+    //      sendPort(optional) - the port to send from\n+    //      cb - function to be called when connection successfully established \n+    // ****************************************************************************\n+    function openConnection(destIP, destPort, mode, sendPort = null, cb = null) {\n+\n+        if (typeof sendPort == \"function\") {\n+            cb = sendPort;\n+            sendPort = null;\n+        }\n+\n+        // check for required parameters\n+        if (_availableSockets.len() == 0) {\n+            if (cb) return cb(W5500_CANNOT_CONNECT_SOCKETS_IN_USE, null);\n+            else throw W5500_CANNOT_CONNECT_SOCKETS_IN_USE;\n+        }\n+\n+        local socket = _availableSockets.pop();\n+        _connections[socket] <- W5500.Connection(this, socket, destIP, destPort, mode);\n+\n+        if (typeof sendPort == \"integer\") _connections[socket].setSourcePort(sendPort);\n+\n+        _connections[socket].open(cb);\n+\n+    }\n+\n+\n+    // ***************************************************************************\n+    // closeConnection\n+    // Returns: this\n+    // Parameters:\n+    //      socket              - the socket of the connection to close\n+    //      cb(optional)        - function\n+    // ***************************************************************************\n+    function closeConnection(socket, cb = null) {\n+\n+        if (getSocketStatus(socket) == W5500_SOCKET_STATUS_ESTABLISHED) {\n+            sendSocketCommand(socket, W5500_SOCKET_DISCONNECT);\n+        } else {\n+            sendSocketCommand(socket, W5500_SOCKET_CLOSE);\n+        }\n+\n+        local started = hardware.millis();\n+        waitForSocketStatus(socket, W5500_SOCKET_STATUS_CLOSED, function(success) {\n+\n+            // Wait a second from the start\n+            local waitfor = 1000 - (hardware.millis() - started);\n+            if (waitfor < 0) waitfor = 0;\n+\n+            imp.wakeup(waitfor / 1000.0, function() {\n+\n+                deregisterSocket(socket);\n+                if (cb) cb();\n+\n+            }.bindenv(this));\n+\n+        }.bindenv(this));\n+\n+        return this;\n+    }\n+\n+\n+    // ***************************************************************************\n+    // deregisterSocket\n+    // Returns:\n+    // Parameters:\n+    //      socket - the socket of the connection to deregister\n+    // ***************************************************************************\n+    function deregisterSocket(socket) {\n+        // Return this to the available pool\n+        if (_availableSockets.find(socket) == null) {\n+            _availableSockets.push(socket);\n+        }\n+        if (socket in _connections) {\n+            _connections.rawdelete(socket);\n+        }\n+    }\n+\n+\n+\n+    // GETTERS AND SETTERS\n+    // ---------------------------------------------\n+\n+    // ***************************************************************************\n+    // setMemory\n+    // Returns: this\n+    // Parameters:\n+    //      memory - an array of four integers with the desired transmit memory\n+    //               allotment for each socket (supported values are 0, 1, 2, 4, 8, 16)\n+    //      dir - a string containing the transmition direction, accepted values\n+    //            are \"tx\" or \"rx\"\n+    // ***************************************************************************\n+    function setMemory(memory, dir) {\n+        local memoryBufferSize = 16;\n+        local addr = (dir == \"rx\") ? W5500_SOCKET_RX_BUFFER_SIZE : W5500_SOCKET_TX_BUFFER_SIZE;\n+\n+        local bits = 0x00;\n+        local total = 0;\n+\n+        foreach (socket, mem_size in memory) {\n+            local socket_mem = 0x00;\n+            local bytes = 0;\n+\n+            // adjust memory size if total memory is used up\n+            if (total + mem_size > memoryBufferSize) {\n+                mem_size = memoryBufferSize - total;\n+                if (mem_size < 0) mem_size = 0;\n+            }\n+\n+            if (mem_size == memoryBufferSize) {\n+                bytes = 16384;\n+            } else if (mem_size >= 8) {\n+                mem_size = 8;\n+                bytes = 8192;\n+            } else if (mem_size >= 4) {\n+                mem_size = 4;\n+                bytes = 4096;\n+            } else if (mem_size >= 2) {\n+                mem_size = 2;\n+                bytes = 2048;\n+            } else if (mem_size >= 1) {\n+                mem_size = 1;\n+                bytes = 1024;\n+            } else {\n+                mem_size = 0;\n+                bytes = 0;\n+            }\n+\n+            total += mem_size;\n+            _socketMemory[dir][socket] = bytes;\n+            local bsb = _getSocketRegBlockSelectBit(socket);\n+            writeReg(addr, bsb, mem_size);\n+        }\n+        return this;\n+    }\n+\n+\n+    // ***************************************************************************\n+    // getMemory\n+    // Returns: the buffer size\n+    // Parameters:\n+    //      dir - a string containing the transmition direction, accepted values\n+    //            are \"tx\" or \"rx\"\n+    // ***************************************************************************\n+    function getMemory(dir, socket) {\n+        return _socketMemory[dir][socket];\n+    }\n+\n+    // ***************************************************************************\n+    // setMode\n+    // Returns: this\n+    // Parameters:\n+    //      mode - select mode using MODE constants or-ed together\n+    // ***************************************************************************\n+    function setMode(mode) {\n+        writeReg(W5500_MODE, W5500_COMMON_REGISTER, mode);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // setSocketMode\n+    // Returns: this\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    //      mode - select mode using W5500_SOCKET_MODE constant\n+    // ***************************************************************************\n+    function setSocketMode(socket, mode) {\n+        local bsb = _getSocketRegBlockSelectBit(socket)\n+        writeReg(W5500_SOCKET_MODE, bsb, mode);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // setGatewayAddr\n+    // Returns: this\n+    // Parameters:\n+    //      addr - an array of four integers with the gateway IP address\n+    // ***************************************************************************\n+    function setGatewayAddr(addr) {\n+        local addr = _addrToIP(addr);\n+        writeReg(W5500_GATEWAY_ADDR_0, W5500_COMMON_REGISTER, addr[0]);\n+        writeReg(W5500_GATEWAY_ADDR_1, W5500_COMMON_REGISTER, addr[1]);\n+        writeReg(W5500_GATEWAY_ADDR_2, W5500_COMMON_REGISTER, addr[2]);\n+        writeReg(W5500_GATEWAY_ADDR_3, W5500_COMMON_REGISTER, addr[3]);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // getGatewayAddr\n+    // Returns:  an array of four integers with the gateway IP address\n+    // Parameters:\n+    //        none\n+    // **************************************************************************\n+    function getGatewayAddr() {\n+        local addr = array(4);\n+        // server.log((hardware.millis() - started) + \": DBG setGatewayAddr: \" + pformat(addr));\n+        addr[0] = readReg(W5500_GATEWAY_ADDR_0, W5500_COMMON_REGISTER);\n+        addr[1] = readReg(W5500_GATEWAY_ADDR_1, W5500_COMMON_REGISTER);\n+        addr[2] = readReg(W5500_GATEWAY_ADDR_2, W5500_COMMON_REGISTER);\n+        addr[3] = readReg(W5500_GATEWAY_ADDR_3, W5500_COMMON_REGISTER);\n+        return addr;\n+    }\n+\n+    // ***************************************************************************\n+    // setSubnetMask\n+    // Returns: this\n+    // Parameters:\n+    //      addr - an array of four integers with the subnet mask \n+    // **************************************************************************\n+    function setSubnetMask(addr) {\n+        local addr = _addrToIP(addr);\n+        writeReg(W5500_SUBNET_MASK_ADDR_0, W5500_COMMON_REGISTER, addr[0]);\n+        writeReg(W5500_SUBNET_MASK_ADDR_1, W5500_COMMON_REGISTER, addr[1]);\n+        writeReg(W5500_SUBNET_MASK_ADDR_2, W5500_COMMON_REGISTER, addr[2]);\n+        writeReg(W5500_SUBNET_MASK_ADDR_3, W5500_COMMON_REGISTER, addr[3]);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // getSubnet\n+    // Returns: an array of four integers with the subnet address\n+    // Parameters:\n+    //         none \n+    // **************************************************************************\n+    function getSubnetMask() {\n+        local addr = array(4);\n+        // server.log((hardware.millis() - started) + \": DBG setSubnet: \" + pformat(addr));\n+        addr[0] = readReg(W5500_SUBNET_MASK_ADDR_0, W5500_COMMON_REGISTER);\n+        addr[1] = readReg(W5500_SUBNET_MASK_ADDR_1, W5500_COMMON_REGISTER);\n+        addr[2] = readReg(W5500_SUBNET_MASK_ADDR_2, W5500_COMMON_REGISTER);\n+        addr[3] = readReg(W5500_SUBNET_MASK_ADDR_3, W5500_COMMON_REGISTER);\n+        return addr;\n+    }\n+\n+    // ***************************************************************************\n+    // setSourceHWAddr\n+    // Returns: this\n+    // Parameters:\n+    //      addr - an array of 6 integers with the mac address for the\n+    //             source hardware\n+    // **************************************************************************\n+    function setSourceHWAddr(addr, flip_public_bit = false) {\n+\n+        if (typeof addr == \"string\") {\n+            // Convert from a string\n+            addr = _addrToMac(addr);\n+        }\n+\n+        // Flip the public bit if required\n+        if (flip_public_bit) {\n+            addr[0] = addr[0] | 0x02;\n+        }\n+\n+        writeReg(W5500_SOURCE_HW_ADDR_0, W5500_COMMON_REGISTER, addr[0]);\n+        writeReg(W5500_SOURCE_HW_ADDR_1, W5500_COMMON_REGISTER, addr[1]);\n+        writeReg(W5500_SOURCE_HW_ADDR_2, W5500_COMMON_REGISTER, addr[2]);\n+        writeReg(W5500_SOURCE_HW_ADDR_3, W5500_COMMON_REGISTER, addr[3]);\n+        writeReg(W5500_SOURCE_HW_ADDR_4, W5500_COMMON_REGISTER, addr[4]);\n+        writeReg(W5500_SOURCE_HW_ADDR_5, W5500_COMMON_REGISTER, addr[5]);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // getSourceHWAddr\n+    // Returns: addr - an array of 6 integers with the mac address for the\n+    //             source hardware\n+    // Parameters:\n+    //      none\n+    // **************************************************************************\n+    function getSourceHWAddr() {\n+        // server.log((hardware.millis() - started) + \": DBG setSourceHWAddr: \" + pformat(addr));\n+        local addr = array(6);\n+        addr[0] = readReg(W5500_SOURCE_HW_ADDR_0, W5500_COMMON_REGISTER);\n+        addr[1] = readReg(W5500_SOURCE_HW_ADDR_1, W5500_COMMON_REGISTER);\n+        addr[2] = readReg(W5500_SOURCE_HW_ADDR_2, W5500_COMMON_REGISTER);\n+        addr[3] = readReg(W5500_SOURCE_HW_ADDR_3, W5500_COMMON_REGISTER);\n+        addr[4] = readReg(W5500_SOURCE_HW_ADDR_4, W5500_COMMON_REGISTER);\n+        addr[5] = readReg(W5500_SOURCE_HW_ADDR_5, W5500_COMMON_REGISTER);\n+\n+        return addr;\n+    }\n+\n+    // ***************************************************************************\n+    // setSourceIP\n+    // Returns: this\n+    // Parameters:\n+    //      addr - an array of 4 integers with the IP address for the\n+    //             source hardware\n+    // **************************************************************************\n+    function setSourceIP(addr) {\n+        local addr = _addrToIP(addr);\n+        writeReg(W5500_SOURCE_IP_ADDR_0, W5500_COMMON_REGISTER, addr[0]);\n+        writeReg(W5500_SOURCE_IP_ADDR_1, W5500_COMMON_REGISTER, addr[1]);\n+        writeReg(W5500_SOURCE_IP_ADDR_2, W5500_COMMON_REGISTER, addr[2]);\n+        writeReg(W5500_SOURCE_IP_ADDR_3, W5500_COMMON_REGISTER, addr[3]);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // getSourceIP\n+    // Returns: addr - an array of 4 integers with the IP address for the\n+    //                 source hardware\n+    // Parameters:\n+    //          none    \n+    // **************************************************************************\n+    function getSourceIP() {\n+        local addr = array(4);\n+        // server.log((hardware.millis() - started) + \": DBG setSourceIP: \" + pformat(addr));\n+        addr[0] = readReg(W5500_SOURCE_IP_ADDR_0, W5500_COMMON_REGISTER);\n+        addr[1] = readReg(W5500_SOURCE_IP_ADDR_1, W5500_COMMON_REGISTER);\n+        addr[2] = readReg(W5500_SOURCE_IP_ADDR_2, W5500_COMMON_REGISTER);\n+        addr[3] = readReg(W5500_SOURCE_IP_ADDR_3, W5500_COMMON_REGISTER);\n+        return addr;\n+    }\n+\n+    // ***************************************************************************\n+    // setSourcePort\n+    // Returns: this\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    //      addr   - an array of 2 integers with the port for the\n+    //               source hardware (ex: for port 4242, addr = [0x10, 0x92])\n+    // **************************************************************************\n+    function setSourcePort(socket, port) {\n+        // Convert the type\n+        if (typeof port == \"integer\") {\n+            local newport = array(2, 0);\n+            newport[0] = (port & 0xFF00) >> 8;\n+            newport[1] = (port & 0x00FF);\n+            port = newport;\n+        }\n+\n+        local bsb = _getSocketRegBlockSelectBit(socket);\n+        writeReg(W5500_SOCKET_SOURCE_PORT_0, bsb, port[0]);\n+        writeReg(W5500_SOCKET_SOURCE_PORT_1, bsb, port[1]);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // setDestHWAddr\n+    // Returns: this\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    //      addr - an array of 6 integers with the mac address for the\n+    //             destination hardware\n+    // **************************************************************************\n+    function setDestHWAddr(socket, addr) {\n+        local bsb = _getSocketRegBlockSelectBit(socket);\n+        writeReg(W5500_SOCKET_DEST_HW_ADDR_0, bsb, addr[0]);\n+        writeReg(W5500_SOCKET_DEST_HW_ADDR_1, bsb, addr[1]);\n+        writeReg(W5500_SOCKET_DEST_HW_ADDR_2, bsb, addr[2]);\n+        writeReg(W5500_SOCKET_DEST_HW_ADDR_3, bsb, addr[3]);\n+        writeReg(W5500_SOCKET_DEST_HW_ADDR_4, bsb, addr[4]);\n+        writeReg(W5500_SOCKET_DEST_HW_ADDR_5, bsb, addr[5]);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // setDestIP\n+    // Returns: this\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    //      addr - an array of 4 integers with the IP address for the\n+    //             destination hardware\n+    // **************************************************************************\n+    function setDestIP(socket, addr) {\n+\n+        // Convert the type\n+        local addr = _addrToIP(addr);\n+        if (typeof addr == \"array\") {\n+            local bsb = _getSocketRegBlockSelectBit(socket);\n+            writeReg(W5500_SOCKET_DEST_IP_ADDR_0, bsb, addr[0]);\n+            writeReg(W5500_SOCKET_DEST_IP_ADDR_1, bsb, addr[1]);\n+            writeReg(W5500_SOCKET_DEST_IP_ADDR_2, bsb, addr[2]);\n+            writeReg(W5500_SOCKET_DEST_IP_ADDR_3, bsb, addr[3]);\n+        } else {\n+            // Send to the DNS and require a callback\n+            throw \"DNS not available yet\";\n+        }\n+\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // setDestPort\n+    // Returns: this\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    //      addr - an array of 2 integers with the port for the\n+    //             destination hardware\n+    //             (ex: for port 4242, addr = [0x10, 0x92])\n+    // **************************************************************************\n+    function setDestPort(socket, port) {\n+\n+        // Convert the type\n+        if (typeof port == \"integer\") {\n+            local newport = array(2, 0);\n+            newport[0] = (port & 0xFF00) >> 8;\n+            newport[1] = (port & 0x00FF);\n+            port = newport;\n+        }\n+\n+        local bsb = _getSocketRegBlockSelectBit(socket);\n+        writeReg(W5500_SOCKET_DEST_PORT_0, bsb, port[0]);\n+        writeReg(W5500_SOCKET_DEST_PORT_1, bsb, port[1]);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // setRxReadPointer\n+    // Returns: this\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    //      value - new RX read pointer value\n+    // **************************************************************************\n+    function setRxReadPointer(socket, value) {\n+        local rx_pointer_r1 = (value & 0xFF00) >> 8;\n+        local rx_pointer_r2 = value & 0x00FF;\n+        local bsb = _getSocketRegBlockSelectBit(socket);\n+        writeReg(W5500_SOCKET_RX_RP_R1, bsb, rx_pointer_r1);\n+        writeReg(W5500_SOCKET_RX_RP_R2, bsb, rx_pointer_r2);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // setTxWritePointer\n+    // Returns: this\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    //      value - new TX write pointer value\n+    // **************************************************************************\n+    function setTxWritePointer(socket, value) {\n+        local tx_pointer_r1 = (value & 0xFF00) >> 8;\n+        local tx_pointer_r2 = value & 0x00FF;\n+        local bsb = _getSocketRegBlockSelectBit(socket);\n+        writeReg(W5500_SOCKET_TX_WP_R1, bsb, tx_pointer_r1);\n+        writeReg(W5500_SOCKET_TX_WP_R2, bsb, tx_pointer_r2);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // getRxReadPointer\n+    // Returns: value of read pointer\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    // **************************************************************************\n+    function getRxReadPointer(socket) {\n+        local bsb = _getSocketRegBlockSelectBit(socket);\n+        local rx_pointer_pt1 = readReg(W5500_SOCKET_RX_RP_R1, bsb) << 8;\n+        local rx_pointer_pt2 = readReg(W5500_SOCKET_RX_RP_R2, bsb);\n+\n+        return rx_pointer_pt1 | rx_pointer_pt2;\n+    }\n+\n+    // ***************************************************************************\n+    // getTxReadPointer\n+    // Returns: value of read pointer\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    // **************************************************************************\n+    function getTxReadPointer(socket) {\n+        local bsb = _getSocketRegBlockSelectBit(socket);\n+        local tx_pointer_pt1 = readReg(W5500_SOCKET_TX_RP_R1, bsb) << 8;\n+        local tx_pointer_pt2 = readReg(W5500_SOCKET_TX_RP_R2, bsb);\n+\n+        return tx_pointer_pt1 | tx_pointer_pt2;\n+    }\n+\n+    // ***************************************************************************\n+    // getRxDataSize\n+    // Returns: value of received data size register\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    // **************************************************************************\n+    function getRxDataSize(socket) {\n+        local bsb = _getSocketRegBlockSelectBit(socket);\n+        local rx_pt1 = readReg(W5500_SOCKET_RX_SIZE_R1, bsb) << 8;\n+        local rx_pt2 = readReg(W5500_SOCKET_RX_SIZE_R2, bsb);\n+\n+        return rx_pt1 | rx_pt2;\n+    }\n+\n+    // ***************************************************************************\n+    // getFreeTxDataSize\n+    // Returns: value of TX data free size register\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    // **************************************************************************\n+    function getFreeTxDataSize(socket) {\n+        local bsb = _getSocketRegBlockSelectBit(socket);\n+        local tx_fd_pt1 = readReg(W5500_SOCKET_TX_SIZE_R1, bsb) << 8;\n+        local tx_fd_pt2 = readReg(W5500_SOCKET_TX_SIZE_R2, bsb);\n+\n+        return tx_fd_pt1 | tx_fd_pt2;\n+    }\n+\n+    // ***************************************************************************\n+    // getSocketRXBufferSize\n+    // Returns: value of RX data buffer size register\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    // **************************************************************************\n+    function getSocketRXBufferSize(socket) {\n+        local bsb = _getSocketRegBlockSelectBit(socket);\n+        return readReg(W5500_SOCKET_RX_BUFFER_SIZE, bsb);\n+    }\n+\n+    // ***************************************************************************\n+    // getSocketTXBufferSize\n+    // Returns: value of TX data buffer size register\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    // **************************************************************************\n+    function getSocketTXBufferSize(socket) {\n+        local bsb = _getSocketRegBlockSelectBit(socket);\n+        return readReg(W5500_SOCKET_TX_BUFFER_SIZE, bsb);\n+    }\n+\n+    // ***************************************************************************\n+    // getChipVersion\n+    // Returns: chip version (should be 0x04 for the W5500)\n+    // Parameters: none\n+    // **************************************************************************\n+    function getChipVersion() {\n+        return readReg(W5500_CHIP_VERSION, W5500_COMMON_REGISTER);\n+    }\n+\n+    // ***************************************************************************\n+    // getTotalSupportedSockets\n+    // Returns: number of sockets the driver code currently supports\n+    // Parameters: none\n+    // **************************************************************************\n+    function getTotalSupportedSockets() {\n+        return TOTAL_SUPPORTED_SOCKETS;\n+    }\n+\n+    // ***************************************************************************\n+    // getMemoryInfo\n+    // Returns: table with memory maximums and supported memory block sizes\n+    // Parameters: none\n+    // **************************************************************************\n+    function getMemoryInfo() {\n+        // mem_block_sizes - an array with supported values highest to lowest\n+        return { \"tx_max\": MAX_TX_MEM_BUFFER, \"rx_max\": MAX_RX_MEM_BUFFER, \"mem_block_sizes\": [16, 8, 4, 2, 1, 0] }\n+    }\n+\n+    // ***************************************************************************\n+    // getSocketStatus\n+    // Returns: integer with the connection status for the socket passed in\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    // **************************************************************************\n+    function getSocketStatus(socket) {\n+        local bsb = _getSocketRegBlockSelectBit(socket);\n+        local status = readReg(W5500_SOCKET_STATUS, bsb);\n+        return status;\n+    }\n+\n+    // ***************************************************************************\n+    // waitForSocketStatus\n+    // Returns: nothing\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    //      state - the state we are waiting for\n+    //      callback - the callback to execute when the state is reached\n+    //      timeout - the number of seconds after which we will give up\n+    // **************************************************************************\n+    function waitForSocketStatus(socket, state, callback, timeout = 10) {\n+        if (getSocketStatus(socket) == state) {\n+            return callback(true);\n+        } else if (timeout <= 0) {\n+            return callback(false);\n+        } else {\n+            imp.wakeup(0.1, function() {\n+                return waitForSocketStatus(socket, state, callback, timeout - 0.1);\n+            }.bindenv(this))\n+        }\n+    }\n+\n+    // ***************************************************************************\n+    // getSocketMode\n+    // Returns: socketMode\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    // **************************************************************************\n+    function getSocketMode(socket) {\n+        local bsb = _getSocketRegBlockSelectBit(socket)\n+        local mode = readReg(W5500_SOCKET_MODE, bsb);\n+        return mode;\n+    }\n+\n+    // ***************************************************************************\n+    // sendSocketCommand\n+    // Returns: this\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    //      command - select command using SOCKET_COMMANDS constant\n+    // **************************************************************************\n+    function sendSocketCommand(socket, command) {\n+        local bsb = _getSocketRegBlockSelectBit(socket);\n+        writeReg(W5500_SOCKET_COMMAND, bsb, command);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // setNumSockets - configures interrupts and memory for each connection\n+    // Returns: number of actual connections configured\n+    // Parameters:\n+    //      numSockets - number of desired connections\n+    // **************************************************************************\n+    function setNumberOfAvailableSockets(numSockets = null) {\n+\n+        // limit number of sockets to what driver can support\n+        if (numSockets == null) numSockets = _maxNoOfSockets;\n+        else if (numSockets < 1) numSockets = 1;\n+        else if (numSockets > _maxNoOfSockets) numSockets = _maxNoOfSockets;\n+\n+        // Store this for later\n+        _noOfSockets = numSockets;\n+\n+        // calculate max memory for number of sockets\n+        local memoryInfo = getMemoryInfo();\n+        local tx_mem = _getMaxMemValue(numSockets, memoryInfo.tx_max, memoryInfo.mem_block_sizes);\n+        local rx_mem = _getMaxMemValue(numSockets, memoryInfo.rx_max, memoryInfo.mem_block_sizes);\n+\n+        // Enable interrupt for each connection\n+        // Set max equal memory across sockets\n+        local sockets = [], interrupts = 0, txmem = array(numSockets, 0), rxmem = array(numSockets, 0);\n+        for (local c = 0; c < numSockets; ++c) {\n+            sockets.insert(0, c);\n+            interrupts = interrupts | (0x01 << c);\n+            txmem[c] = tx_mem;\n+            rxmem[c] = rx_mem;\n+        }\n+\n+        setSocketInterrupt(interrupts);\n+        setMemory(txmem, \"tx\");\n+        setMemory(rxmem, \"rx\");\n+\n+        return sockets;\n+    }\n+\n+    // ***************************************************************************\n+    // readRxData\n+    // Returns: data from received data register\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    // **************************************************************************\n+    function readRxData(socket) {\n+\n+        // get the received data size\n+        local dataSize = getRxDataSize(socket);\n+        // TODO: check dataSize is within range of avail memory - throw error\n+\n+        // select RX memory\n+        local cntl_byte = getSocketRXBufferSize(socket);\n+        // Get offset address\n+        local src_ptr = getRxReadPointer(socket);\n+        // get Block select bit for RX buffer\n+        local bsb = _getSocketRXBufferBlockSelectBit(socket);\n+\n+        // use cntl_byte and src_ptr to get addr??\n+        // read transmitted data here\n+        local data = _readData(src_ptr, bsb, dataSize);\n+\n+        // increase Sn_RX_RD as length of len\n+        src_ptr += dataSize;\n+        setRxReadPointer(socket, src_ptr);\n+\n+        // set RECV command\n+        sendSocketCommand(socket, W5500_SOCKET_RECEIVE);\n+\n+        return data;\n+    }\n+\n+    // ***************************************************************************\n+    // sendTxData\n+    // Returns: length of data to transmit\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    //      transmitData - data to be sent\n+    // **************************************************************************\n+    function sendTxData(socket, transmitData) {\n+\n+        local tx_length = transmitData.len();\n+\n+        // select TX memory\n+        local cntl_byte = getSocketTXBufferSize(socket);\n+        // Get offset address\n+        local dst_ptr = getTxReadPointer(socket); // Sn_TX_RD;\n+        //  get Block select bit for RX buffer\n+        local bsb = _getSocketTXBufferBlockSelectBit(socket);\n+\n+        // write transmit data\n+        _writeData(dst_ptr, bsb, transmitData);\n+\n+        // increase Sn_TX_WR as length of send_size\n+        dst_ptr += tx_length; // adjust write pointer get first??\n+        setTxWritePointer(socket, dst_ptr);\n+\n+        // set SEND command\n+        sendSocketCommand(socket, W5500_SOCKET_SEND);\n+\n+        return tx_length;\n+    }\n+\n+\n+    // INTERRUPT FUNCTIONS\n+    // ---------------------------------------------\n+\n+    // ***************************************************************************\n+    // setInterrupt\n+    // Returns: this\n+    // Parameters:\n+    //      type - select interrupt type using interrupt type\n+    //             constants or-ed together\n+    // **************************************************************************\n+    function setInterrupt(type) {\n+        writeReg(W5500_INTERRUPT_MASK, W5500_COMMON_REGISTER, type);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // clearInterrupt\n+    // Returns: this\n+    // Parameters:\n+    //      type(optional) - clear specified interrupt type\n+    //                               if nothing passed in clears all interrupts\n+    // **************************************************************************\n+    function clearInterrupt(type = 0xF0) {\n+        writeReg(W5500_INTERRUPT, W5500_COMMON_REGISTER, type);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // setSocketInterrupt\n+    // Returns: this\n+    // Parameters:\n+    //      socketInt - select the sockets to enable interrupts on using\n+    //                  SOCKET INTERRUPTS constants or-ed together\n+    //      type(optional) - select SOCKET INTERRUPT TYPES using constants\n+    //              or-ed together, if type not passed in all type interrupts\n+    //              will be cleared.\n+    // **************************************************************************\n+    function setSocketInterrupt(socketInt, type = 0x1F) {\n+        writeReg(W5500_SOCKET_INTERRUPT_MASK, W5500_COMMON_REGISTER, socketInt);\n+        // default enables all socket interrupt types\n+        writeReg(W5500_SOCKET_N_INTERRUPT_MASK, W5500_COMMON_REGISTER, type);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // clearSocketInterrupt\n+    // Returns: this\n+    // Parameters:\n+    //      socket : socket (selected by integer) to clear interrupt on\n+    //      type(optional) - clear specified interrupt type\n+    //                       if nothing passed in clears all interrupts\n+    // **************************************************************************\n+    function clearSocketInterrupt(socket, type = 0x1F) {\n+        local bsb = _getSocketRegBlockSelectBit(socket);\n+        writeReg(W5500_SOCKET_N_INTERRUPT, bsb, type);\n+        return this;\n+    }\n+\n+    // ***************************************************************************\n+    // clearSocketInterrupts\n+    // Returns: clears the Sockets Interrupt\n+    // Parameters:\n+    //      none\n+    // **************************************************************************\n+    function clearSocketInterrupts() {\n+        for (local socket = 0; socket < _noOfSockets; socket++) {\n+            clearSocketInterrupt(socket);\n+        }\n+    }\n+\n+    // ***************************************************************************\n+    // getInterruptStatus\n+    // Returns: interrupt status table\n+    // Parameters: none\n+    // **************************************************************************\n+    function getInterruptStatus() {\n+        local status = readReg(W5500_INTERRUPT, W5500_COMMON_REGISTER);\n+        local intStatus = {\n+            \"CONFLICT\": status & W5500_CONFLICT_INT_TYPE ? true : false,\n+            \"UNREACH\": status & W5500_UNREACH_INT_TYPE ? true : false,\n+            \"PPPoE\": status & W5500_PPPoE_INT_TYPE ? true : false,\n+            \"MAGIC_PACKET\": status & W5500_MAGIC_PACKET_TYPE ? true : false,\n+            \"REGISTER_VALUE\": status\n+        };\n+        return intStatus;\n+    }\n+\n+    // ***************************************************************************\n+    // socketInterruptHandler\n+    // Returns: processes a socket interrupt\n+    // Parameters: none\n+    // **************************************************************************\n+    function socketInterruptHandler() {\n+        local status = readReg(W5500_SOCKET_INTERRUPT, W5500_COMMON_REGISTER);\n+        local intStatus = array(8, false);\n+        // Split the byte into bits\n+        for (local i = 0; i < 8; ++i) {\n+            if (status & (0x01 << i)) {\n+                if (i < _connections.len()) {\n+                    _connections[i].interruptHandler();\n+                } else {\n+                    // This connection has already been closed\n+                }\n+            }\n+        }\n+    }\n+\n+    // ***************************************************************************\n+    // getSocketInterruptTypeStatus\n+    // Returns: socket interrupt status table\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    // **************************************************************************\n+    function getSocketInterruptTypeStatus(socket) {\n+        local bsb = _getSocketRegBlockSelectBit(socket);\n+        local status = readReg(W5500_SOCKET_N_INTERRUPT, bsb);\n+        local intStatus = {\n+            \"SEND_COMPLETE\": status & W5500_SEND_COMPLETE_INT_TYPE ? true : false,\n+            \"TIMEOUT\": status & W5500_TIMEOUT_INT_TYPE ? true : false,\n+            \"DATA_RECEIVED\": status & W5500_DATA_RECEIVED_INT_TYPE ? true : false,\n+            \"DISCONNECTED\": status & W5500_DISCONNECTED_INT_TYPE ? true : false,\n+            \"CONNECTED\": status & W5500_CONNECTED_INT_TYPE ? true : false,\n+            \"REGISTER_VALUE\": status\n+        };\n+        return intStatus;\n+    }\n+\n+    // ***************************************************************************\n+    // getTimeoutCount\n+    // Returns: Connection timeout count\n+    // Parameters:\n+    //      none\n+    // **************************************************************************\n+    function getTimeoutCount() {\n+        return readReg(W5500_RETRY_COUNT, 0x00);\n+    }\n+\n+    // ***************************************************************************\n+    // setTimeoutCount\n+    // Returns: none\n+    // Parameters:\n+    //      count - number of times to retry on timeout\n+    // **************************************************************************\n+    function setTimeoutCount(count) {\n+        writeReg(W5500_RETRY_COUNT, 0x00, count);\n+    }\n+\n+\n+    // SPI FUNCTIONS\n+    // ---------------------------------------------\n+\n+    // ***************************************************************************\n+    // readReg\n+    // Returns: data stored at the specified register\n+    // Parameters:\n+    //      reg - register to read\n+    // **************************************************************************\n+    function readReg(addr, bsb) {\n+        local b = blob();\n+        local cp = bsb | W5500_READ_COMMAND | W5500_VARIABLE_DATA_LENGTH;\n+        local res = blob();\n+\n+        (_cs) ? _cs.write(0): _spi.chipselect(1);\n+\n+        b.writen(addr >> 8, 'b');\n+        b.writen(addr & 0xFF, 'b');\n+        b.writen(cp, 'b');\n+        b.writen(0x00, 'b');\n+\n+        res = _spi.writeread(b);\n+\n+        (_cs) ? _cs.write(1): _spi.chipselect(0);\n+\n+        return res[3];\n+    }\n+\n+    // ***************************************************************************\n+    // writeReg\n+    // Returns: null\n+    // Parameters:\n+    //      reg - register to write to\n+    //      value - data to write to register\n+    // **************************************************************************\n+    function writeReg(addr, bsb, value) {\n+        local b = blob();\n+        local cp = bsb | W5500_WRITE_COMMAND | W5500_VARIABLE_DATA_LENGTH;\n+\n+        (_cs) ? _cs.write(0): _spi.chipselect(1);\n+\n+        b.writen(addr >> 8, 'b');\n+        b.writen(addr & 0xFF, 'b');\n+        b.writen(cp, 'b');\n+        b.writen(value, 'b');\n+\n+        _spi.write(b);\n+\n+        (_cs) ? _cs.write(1): _spi.chipselect(0);\n+    }\n+\n+\n+    // ***************************************************************************\n+    // reset, note this is blocking for 0.2s\n+    // Returns: this\n+    // Parameters:\n+    //          sw(optional) - boolean if true forces a software reset\n+    //          Note: datasheet for W5500 states that software reset is\n+    //                unreliable - don't use\n+    // **************************************************************************\n+    function reset(sw = false) {\n+        // Reset chip to default state, blocks for 0.2s\n+        // Note: Datasheet states that W5500 software reset\n+        //       is not reliable, use hardware reset.\n+        if (sw || _resetPin == null) {\n+            setMode(W5500_SW_RESET);\n+            imp.sleep(0.2);\n+        } else {\n+            _resetPin.write(0);\n+            imp.sleep(0.001); // hold at least 500us after assert low\n+            _resetPin.write(1);\n+            imp.sleep(0.2); // wait at least 150ms before configuring\n+        }\n+\n+        // Reset the default memory allocation\n+        _setMemDefaults();\n+        return this;\n+    }\n+\n+\n+    // PRIVATE FUNCTIONS\n+    // ---------------------------------------------\n+\n+    // ***************************************************************************\n+    // _hexToInt\n+    // Returns: an integer representation of the string passed in\n+    // Parameters:\n+    //      hexString - A string representing a hexadecimal number\n+    // **************************************************************************\n+    function _hexToInt(hexString) {\n+        // Does the string start with '0x'? If so, remove it\n+        if (hexString.slice(0, 2) == \"0x\") hexString = hexString.slice(2);\n+\n+        // Get the integer value of the remaining string\n+        local intValue = 0;\n+\n+        foreach (character in hexString) {\n+            local nibble = character - '0';\n+            if (nibble > 9) nibble = ((nibble & 0x1F) - 7);\n+            intValue = (intValue << 4) + nibble;\n+        }\n+\n+        return intValue;\n+    }\n+\n+    // ***************************************************************************\n+    // _addrToIP\n+    // Returns: an array of four numbers representing an ip address or false if it is a string for the DNS\n+    // Parameters:\n+    //      addr - a string or array representing an IP address\n+    // **************************************************************************\n+    function _addrToIP(addr) {\n+\n+        if (typeof addr == \"string\" && addr.len() > 0) {\n+\n+            try {\n+                local parts = split(addr, \".\");\n+                if (parts.len() == 4) {\n+                    foreach (part in parts) {\n+                        local ipart = part.tointeger();\n+                        if (ipart.tostring() != part || ipart < 0 || ipart >= 256) {\n+                            return false;\n+                        }\n+                    }\n+\n+                    return [parts[0].tointeger(), parts[1].tointeger(), parts[2].tointeger(), parts[3].tointeger()];\n+                } else {\n+                    return false;\n+                }\n+            } catch (e) {\n+                return false;\n+            }\n+\n+        } else if (typeof addr == \"blob\" && addr.len() == 4) {\n+            local parts = array();\n+            addr.seek(0, 'b');\n+            while (!addr.eos()) {\n+                parts.push(addr.readn('b'))\n+            }\n+            return parts;\n+        } else if (typeof addr == \"array\" && addr.len() == 4) {\n+            return addr;\n+        } else {\n+            throw \"Bad IP address\";\n+        }\n+\n+    }\n+\n+\n+    // ***************************************************************************\n+    // _addrToIP\n+    // Returns: an array of four numbers representing an ip address or false if it is a string for the DNS\n+    // Parameters:\n+    //      addr - a string or array representing an IP address\n+    // **************************************************************************\n+    function _addrToMac(addr) {\n+\n+        if (typeof addr == \"string\" && addr.len() == 12) {\n+            // Convert a string address to an array\n+            local mac = [];\n+            for (local i = 0; i < addr.len(); i += 2) {\n+                local byte = addr.slice(i, i + 2);\n+                mac.push(_hexToInt(byte));\n+            }\n+            return mac;\n+        } else if (typeof addr == \"blob\" && addr.len() == 6) {\n+            local parts = array();\n+            addr.seek(0, 'b');\n+            while (!addr.eos()) {\n+                parts.push(addr.readn('b'))\n+            }\n+            return parts;\n+        } else if (typeof addr == \"array\" && addr.len() == 6) {\n+            return addr;\n+        } else {\n+            throw \"Bad Mac address\";\n+        }\n+    }\n+\n+    // ***************************************************************************\n+    // _getSocketRegBlockSelectBit\n+    // Returns: the socket register Control Phase Block Select Bit for the socket passed in\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    // **************************************************************************\n+    function _getSocketRegBlockSelectBit(socket) {\n+        switch (socket) {\n+            case 0:\n+                return W5500_S0_REGISTER;\n+            case 1:\n+                return W5500_S1_REGISTER;\n+            case 2:\n+                return W5500_S2_REGISTER;\n+            case 3:\n+                return W5500_S3_REGISTER;\n+            case 4:\n+                return W5500_S4_REGISTER;\n+            case 5:\n+                return W5500_S5_REGISTER;\n+            case 6:\n+                return W5500_S6_REGISTER;\n+            case 7:\n+                return W5500_S7_REGISTER;\n+        }\n+        return null;\n+    }\n+\n+    // ***************************************************************************\n+    // _getSocketRXBufferBlockSelectBit\n+    // Returns: the socket RX buffer Control Phase Block Select Bit for the socket passed in\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    // **************************************************************************\n+    function _getSocketRXBufferBlockSelectBit(socket) {\n+        switch (socket) {\n+            case 0:\n+                return W5500_S0_RX_BUFFER;\n+            case 1:\n+                return W5500_S1_RX_BUFFER;\n+            case 2:\n+                return W5500_S2_RX_BUFFER;\n+            case 3:\n+                return W5500_S3_RX_BUFFER;\n+            case 4:\n+                return W5500_S4_RX_BUFFER;\n+            case 5:\n+                return W5500_S5_RX_BUFFER;\n+            case 6:\n+                return W5500_S6_RX_BUFFER;\n+            case 7:\n+                return W5500_S7_RX_BUFFER;\n+        }\n+        return null;\n+    }\n+\n+    // ***************************************************************************\n+    // _getSocketRXBufferBlockSelectBit\n+    // Returns: the socket TX buffer Control Phase Block Select Bit for the socket passed in\n+    // Parameters:\n+    //      socket - select the socket using an integer 0-3\n+    // **************************************************************************\n+    function _getSocketTXBufferBlockSelectBit(socket) {\n+        switch (socket) {\n+            case 0:\n+                return W5500_S0_TX_BUFFER;\n+            case 1:\n+                return W5500_S1_TX_BUFFER;\n+            case 2:\n+                return W5500_S2_TX_BUFFER;\n+            case 3:\n+                return W5500_S3_TX_BUFFER;\n+            case 4:\n+                return W5500_S4_TX_BUFFER;\n+            case 5:\n+                return W5500_S5_TX_BUFFER;\n+            case 6:\n+                return W5500_S6_TX_BUFFER;\n+            case 7:\n+                return W5500_S7_TX_BUFFER;\n+        }\n+        return null;\n+    }\n+\n+\n+    // ***************************************************************************\n+    // _setMemDefaults, sets locally stored default socket memory info\n+    // Returns: null\n+    // Parameters: none\n+    // **************************************************************************\n+    function _setMemDefaults() {\n+        _socketMemory = {\n+            \"rx\": [2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048],\n+            \"tx\": [2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048]\n+        }\n+    }\n+\n+\n+    // ***************************************************************************\n+    // _writeData, writes data to transmit memory buffer\n+    // Returns: size of data written\n+    // Parameters:\n+    //      addr - offset address to start the data write\n+    //      bsb - socket tx buffer block select bit\n+    //      data - data to be written\n+    // **************************************************************************\n+    function _writeData(addr, bsb, data) {\n+        foreach (item in data) {\n+            writeReg(addr, bsb, item);\n+            addr++;\n+        }\n+\n+        return data.len();\n+    }\n+\n+    // ***************************************************************************\n+    // _readData\n+    // Returns: blob containing data from memory buffer\n+    // Parameters:\n+    //      addr - offset address to start the read\n+    //      bsb - block select bit for the socket rx buffer\n+    //      size - size of data to be written\n+    // **************************************************************************\n+    function _readData(addr, bsb, size) {\n+        local b = blob();\n+\n+        while (size != 0) {\n+            b.writen(readReg(addr, bsb), 'b');\n+            addr++;\n+            size--;\n+        }\n+\n+        return b\n+    }\n+\n+    // ***************************************************************************\n+   ","path":"W5500.device.nut","position":null,"original_position":1931,"commit_id":"c624f3d07305594b4dcce3587e742a8c88f35d31","original_commit_id":"f6c18d1719a8a8624e0528f0021f4970b18c3616","user":{"login":"betzrhodes","id":10034302,"avatar_url":"https://avatars2.githubusercontent.com/u/10034302?v=3","gravatar_id":"","url":"https://api.github.com/users/betzrhodes","html_url":"https://github.com/betzrhodes","followers_url":"https://api.github.com/users/betzrhodes/followers","following_url":"https://api.github.com/users/betzrhodes/following{/other_user}","gists_url":"https://api.github.com/users/betzrhodes/gists{/gist_id}","starred_url":"https://api.github.com/users/betzrhodes/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/betzrhodes/subscriptions","organizations_url":"https://api.github.com/users/betzrhodes/orgs","repos_url":"https://api.github.com/users/betzrhodes/repos","events_url":"https://api.github.com/users/betzrhodes/events{/privacy}","received_events_url":"https://api.github.com/users/betzrhodes/received_events","type":"User","site_admin":false},"body":"Private function - should move to bottom of the class","created_at":"2017-03-15T23:37:39Z","updated_at":"2017-03-15T23:37:39Z","html_url":"https://github.com/electricimp/Wiznet_5500/pull/1#discussion_r106309154","pull_request_url":"https://api.github.com/repos/electricimp/Wiznet_5500/pulls/1","_links":{"self":{"href":"https://api.github.com/repos/electricimp/Wiznet_5500/pulls/comments/106309154"},"html":{"href":"https://github.com/electricimp/Wiznet_5500/pull/1#discussion_r106309154"},"pull_request":{"href":"https://api.github.com/repos/electricimp/Wiznet_5500/pulls/1"}}},"pull_request":{"url":"https://api.github.com/repos/electricimp/Wiznet_5500/pulls/1","id":110527093,"html_url":"https://github.com/electricimp/Wiznet_5500/pull/1","diff_url":"https://github.com/electricimp/Wiznet_5500/pull/1.diff","patch_url":"https://github.com/electricimp/Wiznet_5500/pull/1.patch","issue_url":"https://api.github.com/repos/electricimp/Wiznet_5500/issues/1","number":1,"state":"open","locked":false,"title":"Major update to v1.0.0","user":{"login":"blindman2k","id":771549,"avatar_url":"https://avatars2.githubusercontent.com/u/771549?v=3","gravatar_id":"","url":"https://api.github.com/users/blindman2k","html_url":"https://github.com/blindman2k","followers_url":"https://api.github.com/users/blindman2k/followers","following_url":"https://api.github.com/users/blindman2k/following{/other_user}","gists_url":"https://api.github.com/users/blindman2k/gists{/gist_id}","starred_url":"https://api.github.com/users/blindman2k/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/blindman2k/subscriptions","organizations_url":"https://api.github.com/users/blindman2k/orgs","repos_url":"https://api.github.com/users/blindman2k/repos","events_url":"https://api.github.com/users/blindman2k/events{/privacy}","received_events_url":"https://api.github.com/users/blindman2k/received_events","type":"User","site_admin":false},"body":"Complete refactor, added 8 socket support, string parameters (such as IP address), auto creation of mac address, documentation and test cases.","created_at":"2017-03-14T00:41:43Z","updated_at":"2017-03-15T23:37:39Z","closed_at":null,"merged_at":null,"merge_commit_sha":"cc960b1a420e4a093aad514968fc835d43eeda20","assignee":{"login":"ppetrosh","id":20688680,"avatar_url":"https://avatars1.githubusercontent.com/u/20688680?v=3","gravatar_id":"","url":"https://api.github.com/users/ppetrosh","html_url":"https://github.com/ppetrosh","followers_url":"https://api.github.com/users/ppetrosh/followers","following_url":"https://api.github.com/users/ppetrosh/following{/other_user}","gists_url":"https://api.github.com/users/ppetrosh/gists{/gist_id}","starred_url":"https://api.github.com/users/ppetrosh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ppetrosh/subscriptions","organizations_url":"https://api.github.com/users/ppetrosh/orgs","repos_url":"https://api.github.com/users/ppetrosh/repos","events_url":"https://api.github.com/users/ppetrosh/events{/privacy}","received_events_url":"https://api.github.com/users/ppetrosh/received_events","type":"User","site_admin":false},"assignees":[{"login":"ppetrosh","id":20688680,"avatar_url":"https://avatars1.githubusercontent.com/u/20688680?v=3","gravatar_id":"","url":"https://api.github.com/users/ppetrosh","html_url":"https://github.com/ppetrosh","followers_url":"https://api.github.com/users/ppetrosh/followers","following_url":"https://api.github.com/users/ppetrosh/following{/other_user}","gists_url":"https://api.github.com/users/ppetrosh/gists{/gist_id}","starred_url":"https://api.github.com/users/ppetrosh/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ppetrosh/subscriptions","organizations_url":"https://api.github.com/users/ppetrosh/orgs","repos_url":"https://api.github.com/users/ppetrosh/repos","events_url":"https://api.github.com/users/ppetrosh/events{/privacy}","received_events_url":"https://api.github.com/users/ppetrosh/received_events","type":"User","site_admin":false}],"milestone":null,"commits_url":"https://api.github.com/repos/electricimp/Wiznet_5500/pulls/1/commits","review_comments_url":"https://api.github.com/repos/electricimp/Wiznet_5500/pulls/1/comments","review_comment_url":"https://api.github.com/repos/electricimp/Wiznet_5500/pulls/comments{/number}","comments_url":"https://api.github.com/repos/electricimp/Wiznet_5500/issues/1/comments","statuses_url":"https://api.github.com/repos/electricimp/Wiznet_5500/statuses/c624f3d07305594b4dcce3587e742a8c88f35d31","head":{"label":"mysticpants:master","ref":"master","sha":"c624f3d07305594b4dcce3587e742a8c88f35d31","user":{"login":"mysticpants","id":17821176,"avatar_url":"https://avatars3.githubusercontent.com/u/17821176?v=3","gravatar_id":"","url":"https://api.github.com/users/mysticpants","html_url":"https://github.com/mysticpants","followers_url":"https://api.github.com/users/mysticpants/followers","following_url":"https://api.github.com/users/mysticpants/following{/other_user}","gists_url":"https://api.github.com/users/mysticpants/gists{/gist_id}","starred_url":"https://api.github.com/users/mysticpants/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mysticpants/subscriptions","organizations_url":"https://api.github.com/users/mysticpants/orgs","repos_url":"https://api.github.com/users/mysticpants/repos","events_url":"https://api.github.com/users/mysticpants/events{/privacy}","received_events_url":"https://api.github.com/users/mysticpants/received_events","type":"Organization","site_admin":false},"repo":{"id":84888026,"name":"Wiznet_5500","full_name":"mysticpants/Wiznet_5500","owner":{"login":"mysticpants","id":17821176,"avatar_url":"https://avatars3.githubusercontent.com/u/17821176?v=3","gravatar_id":"","url":"https://api.github.com/users/mysticpants","html_url":"https://github.com/mysticpants","followers_url":"https://api.github.com/users/mysticpants/followers","following_url":"https://api.github.com/users/mysticpants/following{/other_user}","gists_url":"https://api.github.com/users/mysticpants/gists{/gist_id}","starred_url":"https://api.github.com/users/mysticpants/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mysticpants/subscriptions","organizations_url":"https://api.github.com/users/mysticpants/orgs","repos_url":"https://api.github.com/users/mysticpants/repos","events_url":"https://api.github.com/users/mysticpants/events{/privacy}","received_events_url":"https://api.github.com/users/mysticpants/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/mysticpants/Wiznet_5500","description":null,"fork":true,"url":"https://api.github.com/repos/mysticpants/Wiznet_5500","forks_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/forks","keys_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/keys{/key_id}","collaborators_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/teams","hooks_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/hooks","issue_events_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/issues/events{/number}","events_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/events","assignees_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/assignees{/user}","branches_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/branches{/branch}","tags_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/tags","blobs_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/git/refs{/sha}","trees_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/git/trees{/sha}","statuses_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/statuses/{sha}","languages_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/languages","stargazers_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/stargazers","contributors_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/contributors","subscribers_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/subscribers","subscription_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/subscription","commits_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/commits{/sha}","git_commits_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/git/commits{/sha}","comments_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/comments{/number}","issue_comment_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/issues/comments{/number}","contents_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/contents/{+path}","compare_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/compare/{base}...{head}","merges_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/merges","archive_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/downloads","issues_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/issues{/number}","pulls_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/pulls{/number}","milestones_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/milestones{/number}","notifications_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/labels{/name}","releases_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/releases{/id}","deployments_url":"https://api.github.com/repos/mysticpants/Wiznet_5500/deployments","created_at":"2017-03-14T00:33:37Z","updated_at":"2017-03-14T00:33:38Z","pushed_at":"2017-03-15T23:29:37Z","git_url":"git://github.com/mysticpants/Wiznet_5500.git","ssh_url":"git@github.com:mysticpants/Wiznet_5500.git","clone_url":"https://github.com/mysticpants/Wiznet_5500.git","svn_url":"https://github.com/mysticpants/Wiznet_5500","homepage":null,"size":140,"stargazers_count":0,"watchers_count":0,"language":"Squirrel","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"electricimp:master","ref":"master","sha":"bcb915bb108381a2b8f4467f118b0d1788a5f1a2","user":{"login":"electricimp","id":3960327,"avatar_url":"https://avatars3.githubusercontent.com/u/3960327?v=3","gravatar_id":"","url":"https://api.github.com/users/electricimp","html_url":"https://github.com/electricimp","followers_url":"https://api.github.com/users/electricimp/followers","following_url":"https://api.github.com/users/electricimp/following{/other_user}","gists_url":"https://api.github.com/users/electricimp/gists{/gist_id}","starred_url":"https://api.github.com/users/electricimp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/electricimp/subscriptions","organizations_url":"https://api.github.com/users/electricimp/orgs","repos_url":"https://api.github.com/users/electricimp/repos","events_url":"https://api.github.com/users/electricimp/events{/privacy}","received_events_url":"https://api.github.com/users/electricimp/received_events","type":"Organization","site_admin":false},"repo":{"id":64708192,"name":"Wiznet_5500","full_name":"electricimp/Wiznet_5500","owner":{"login":"electricimp","id":3960327,"avatar_url":"https://avatars3.githubusercontent.com/u/3960327?v=3","gravatar_id":"","url":"https://api.github.com/users/electricimp","html_url":"https://github.com/electricimp","followers_url":"https://api.github.com/users/electricimp/followers","following_url":"https://api.github.com/users/electricimp/following{/other_user}","gists_url":"https://api.github.com/users/electricimp/gists{/gist_id}","starred_url":"https://api.github.com/users/electricimp/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/electricimp/subscriptions","organizations_url":"https://api.github.com/users/electricimp/orgs","repos_url":"https://api.github.com/users/electricimp/repos","events_url":"https://api.github.com/users/electricimp/events{/privacy}","received_events_url":"https://api.github.com/users/electricimp/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/electricimp/Wiznet_5500","description":null,"fork":false,"url":"https://api.github.com/repos/electricimp/Wiznet_5500","forks_url":"https://api.github.com/repos/electricimp/Wiznet_5500/forks","keys_url":"https://api.github.com/repos/electricimp/Wiznet_5500/keys{/key_id}","collaborators_url":"https://api.github.com/repos/electricimp/Wiznet_5500/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/electricimp/Wiznet_5500/teams","hooks_url":"https://api.github.com/repos/electricimp/Wiznet_5500/hooks","issue_events_url":"https://api.github.com/repos/electricimp/Wiznet_5500/issues/events{/number}","events_url":"https://api.github.com/repos/electricimp/Wiznet_5500/events","assignees_url":"https://api.github.com/repos/electricimp/Wiznet_5500/assignees{/user}","branches_url":"https://api.github.com/repos/electricimp/Wiznet_5500/branches{/branch}","tags_url":"https://api.github.com/repos/electricimp/Wiznet_5500/tags","blobs_url":"https://api.github.com/repos/electricimp/Wiznet_5500/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/electricimp/Wiznet_5500/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/electricimp/Wiznet_5500/git/refs{/sha}","trees_url":"https://api.github.com/repos/electricimp/Wiznet_5500/git/trees{/sha}","statuses_url":"https://api.github.com/repos/electricimp/Wiznet_5500/statuses/{sha}","languages_url":"https://api.github.com/repos/electricimp/Wiznet_5500/languages","stargazers_url":"https://api.github.com/repos/electricimp/Wiznet_5500/stargazers","contributors_url":"https://api.github.com/repos/electricimp/Wiznet_5500/contributors","subscribers_url":"https://api.github.com/repos/electricimp/Wiznet_5500/subscribers","subscription_url":"https://api.github.com/repos/electricimp/Wiznet_5500/subscription","commits_url":"https://api.github.com/repos/electricimp/Wiznet_5500/commits{/sha}","git_commits_url":"https://api.github.com/repos/electricimp/Wiznet_5500/git/commits{/sha}","comments_url":"https://api.github.com/repos/electricimp/Wiznet_5500/comments{/number}","issue_comment_url":"https://api.github.com/repos/electricimp/Wiznet_5500/issues/comments{/number}","contents_url":"https://api.github.com/repos/electricimp/Wiznet_5500/contents/{+path}","compare_url":"https://api.github.com/repos/electricimp/Wiznet_5500/compare/{base}...{head}","merges_url":"https://api.github.com/repos/electricimp/Wiznet_5500/merges","archive_url":"https://api.github.com/repos/electricimp/Wiznet_5500/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/electricimp/Wiznet_5500/downloads","issues_url":"https://api.github.com/repos/electricimp/Wiznet_5500/issues{/number}","pulls_url":"https://api.github.com/repos/electricimp/Wiznet_5500/pulls{/number}","milestones_url":"https://api.github.com/repos/electricimp/Wiznet_5500/milestones{/number}","notifications_url":"https://api.github.com/repos/electricimp/Wiznet_5500/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/electricimp/Wiznet_5500/labels{/name}","releases_url":"https://api.github.com/repos/electricimp/Wiznet_5500/releases{/id}","deployments_url":"https://api.github.com/repos/electricimp/Wiznet_5500/deployments","created_at":"2016-08-01T23:22:51Z","updated_at":"2016-08-01T23:28:28Z","pushed_at":"2017-03-15T23:29:39Z","git_url":"git://github.com/electricimp/Wiznet_5500.git","ssh_url":"git@github.com:electricimp/Wiznet_5500.git","clone_url":"https://github.com/electricimp/Wiznet_5500.git","svn_url":"https://github.com/electricimp/Wiznet_5500","homepage":null,"size":33,"stargazers_count":0,"watchers_count":0,"language":"Squirrel","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":1,"forks":1,"open_issues":1,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/electricimp/Wiznet_5500/pulls/1"},"html":{"href":"https://github.com/electricimp/Wiznet_5500/pull/1"},"issue":{"href":"https://api.github.com/repos/electricimp/Wiznet_5500/issues/1"},"comments":{"href":"https://api.github.com/repos/electricimp/Wiznet_5500/issues/1/comments"},"review_comments":{"href":"https://api.github.com/repos/electricimp/Wiznet_5500/pulls/1/comments"},"review_comment":{"href":"https://api.github.com/repos/electricimp/Wiznet_5500/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/electricimp/Wiznet_5500/pulls/1/commits"},"statuses":{"href":"https://api.github.com/repos/electricimp/Wiznet_5500/statuses/c624f3d07305594b4dcce3587e742a8c88f35d31"}}}}
{"actor":{"display_login":"betzrhodes"}}