Arduino Web Server
Arduino uno + enc28j60
#include <EtherCard.h> static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; static byte myip[] = { 192,168,3,69 }; byte Ethernet::buffer[1500]; BufferFiller bfill; void setup() { Serial.begin(57600); Serial.println("Cardinal"); if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) { Serial.println(F("Failed to access Ethernet controller")); } ether.staticSetup(myip); } static word homePage() { long t = millis() / 1000; word h = t / 3600; byte m = (t / 60) % 60; byte s = t % 60; bfill = ether.tcpOffset(); bfill.emit_p(PSTR( "HTTP/1.0 200 OK\r\n" "Content-Type: text/html\r\n" "Pragma: no-cache\r\n" "\r\n" "<meta http-equiv='refresh' content='1'/><title>Cardinal</title><style type='text/css'>body{background-color: #3399cc}h1{opacity:0.8;color:#fff;font-family:'Source Sans Pro',sans-serif;text-align:center;}a{text-decoration:none}a:link,a:visited{color:#fff}</style><h1>$D$D:$D$D:$D$D</h1>" ), h / 10, h % 10, m / 10, m % 10, s / 10, s % 10); return bfill.position(); } void loop() { word len = ether.packetReceive(); word pos = ether.packetLoop(len); Serial.println("Cardinal"); if (pos) { ether.httpServerReply(homePage()); } }
EtherCard.h
// This code slightly follows the conventions of, but is not derived from: // EHTERSHIELD_H library for Arduino etherShield // Copyright (c) 2008 Xing Yu. All right reserved. (this is LGPL v2.1) // It is however derived from the enc28j60 and ip code (which is GPL v2) // Author: Pascal Stang // Modified by: Guido Socher // DHCP code: Andrew Lindsay // Hence: GPL V2 // // 2010-05-19 <[email protected]> // // // PIN Connections (Using Arduino UNO): // VCC - 3.3V // GND - GND // SCK - Pin 13 // SO - Pin 12 // SI - Pin 11 // CS - Pin 8 // /** @file */ #ifndef EtherCard_h #define EtherCard_h #ifndef __PROG_TYPES_COMPAT__ #define __PROG_TYPES_COMPAT__ #endif #if ARDUINO >= 100 #include <Arduino.h> // Arduino 1.0 #define WRITE_RESULT size_t #define WRITE_RETURN return 1; #else #include <WProgram.h> // Arduino 0022 #define WRITE_RESULT void #define WRITE_RETURN #endif #include <avr/pgmspace.h> #include "enc28j60.h" #include "net.h" /** Enable DHCP. * Setting this to zero disables the use of DHCP; if a program uses DHCP it will * still compile but the program will not work. Saves about 60 bytes SRAM and * 1550 bytes flash. */ #define ETHERCARD_DHCP 1 /** Enable client connections. * Setting this to zero means that the program cannot issue TCP client requests * anymore. Compilation will still work but the request will never be * issued. Saves 4 bytes SRAM and 550 byte flash. */ #define ETHERCARD_TCPCLIENT 1 /** Enable TCP server functionality. * Setting this to zero means that the program will not accept TCP client * requests. Saves 2 bytes SRAM and 250 bytes flash. */ #define ETHERCARD_TCPSERVER 1 /** Enable UDP server functionality. * If zero UDP server is disabled. It is * still possible to register callbacks but these will never be called. Saves * about 40 bytes SRAM and 200 bytes flash. If building with -flto this does not * seem to save anything; maybe the linker is then smart enough to optimize the * call away. */ #define ETHERCARD_UDPSERVER 1 /** Enable automatic reply to pings. * Setting to zero means that the program will not automatically answer to * PINGs anymore. Also the callback that can be registered to answer incoming * pings will not be called. Saves 2 bytes SRAM and 230 bytes flash. */ #define ETHERCARD_ICMP 1 /** Enable use of stash. * Setting this to zero means that the stash mechanism cannot be used. Again * compilation will still work but the program may behave very unexpectedly. * Saves 30 bytes SRAM and 80 bytes flash. */ #define ETHERCARD_STASH 1 /** This type definition defines the structure of a UDP server event handler callback funtion */ typedef void (*UdpServerCallback)( uint16_t dest_port, ///< Port the packet was sent to uint8_t src_ip[IP_LEN], ///< IP address of the sender uint16_t src_port, ///< Port the packet was sent from const char *data, ///< UDP payload data uint16_t len); ///< Length of the payload data /** This type definition defines the structure of a DHCP Option callback funtion */ typedef void (*DhcpOptionCallback)( uint8_t option, ///< The option number const byte* data, ///< DHCP option data uint8_t len); ///< Length of the DHCP option data /** This structure describes the structure of memory used within the ENC28J60 network interface. */ typedef struct { uint8_t count; ///< Number of allocated pages uint8_t first; ///< First allocated page uint8_t last; ///< Last allocated page } StashHeader; /** This class provides access to the memory within the ENC28J60 network interface. */ class Stash : public /*Stream*/ Print, private StashHeader { uint8_t curr; //!< Current page uint8_t offs; //!< Current offset in page typedef struct { union { uint8_t bytes[64]; uint16_t words[32]; struct { StashHeader head; // StashHeader is only stored in first block uint8_t filler[59]; uint8_t tail; // only meaningful if bnum==last; number of bytes in last block uint8_t next; // pointer to next block }; }; uint8_t bnum; } Block; static uint8_t allocBlock (); static void freeBlock (uint8_t block); static uint8_t fetchByte (uint8_t blk, uint8_t off); static Block bufs[2]; static uint8_t map[SCRATCH_MAP_SIZE]; public: static void initMap (uint8_t last=SCRATCH_PAGE_NUM); static void load (uint8_t idx, uint8_t blk); static uint8_t freeCount (); Stash () : curr (0) { first = 0; } Stash (uint8_t fd) { open(fd); } uint8_t create (); uint8_t open (uint8_t blk); void save (); void release (); void put (char c); char get (); uint16_t size (); virtual WRITE_RESULT write(uint8_t b) { put(b); WRITE_RETURN } // virtual int available() { // if (curr != last) // return 1; // load(1, last); // return offs < bufs[1].tail; // } // virtual int read() { // return available() ? get() : -1; // } // virtual int peek() { // return available() ? bufs[1].bytes[offs] : -1; // } // virtual void flush() { // curr = last; // offs = 63; // } static void prepare (const char* fmt PROGMEM, ...); static uint16_t length (); static void extract (uint16_t offset, uint16_t count, void* buf); static void cleanup (); friend void dumpBlock (const char* msg, uint8_t idx); // optional friend void dumpStash (const char* msg, void* ptr); // optional }; /** This class populates network send and receive buffers. * * This class provides formatted printing into memory. Users can use it to write into send buffers. * * Nota: PGM_P: is a pointer to a string in program space (defined in the source code, updated to PROGMEM) * * # Format string * * | Format | Parameter | Output * |--------|-------------|---------- * | $D | uint16_t | Decimal representation * | $T ¤ | double | Decimal representation with 3 digits after decimal sign ([-]d.ddd) * | $H | uint16_t | Hexadecimal value of lsb (from 00 to ff) * | $L | long | Decimal representation * | $S | const char* | Copy null terminated string from main memory * | $F | PGM_P | Copy null terminated string from program space * | $E | byte* | Copy null terminated string from EEPROM space * | $ | _none_ | '
// Microchip ENC28J60 Ethernet Interface Driver // Author: Pascal Stang // Modified by: Guido Socher // Copyright: GPL V2 // // This driver provides initialization and transmit/receive // functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY. // This chip is novel in that it is a full MAC+PHY interface all in a 28-pin // chip, using an SPI interface to the host processor. // // 2010-05-20 <[email protected]> /** @file */ #ifndef ENC28J60_H #define ENC28J60_H // buffer boundaries applied to internal 8K ram // the entire available packet buffer space is allocated #define RXSTART_INIT 0x0000 // start of RX buffer, (must be zero, Rev. B4 Errata point 5) #define RXSTOP_INIT 0x0BFF // end of RX buffer, room for 2 packets #define TXSTART_INIT 0x0C00 // start of TX buffer, room for 1 packet #define TXSTOP_INIT 0x11FF // end of TX buffer #define SCRATCH_START 0x1200 // start of scratch area #define SCRATCH_LIMIT 0x2000 // past end of area, i.e. 3 Kb #define SCRATCH_PAGE_SHIFT 6 // addressing is in pages of 64 bytes #define SCRATCH_PAGE_SIZE (1 << SCRATCH_PAGE_SHIFT) #define SCRATCH_PAGE_NUM ((SCRATCH_LIMIT-SCRATCH_START) >> SCRATCH_PAGE_SHIFT) #define SCRATCH_MAP_SIZE (((SCRATCH_PAGE_NUM % 8) == 0) ? (SCRATCH_PAGE_NUM / 8) : (SCRATCH_PAGE_NUM/8+1)) // area in the enc memory that can be used via enc_malloc; by default 0 bytes; decrease SCRATCH_LIMIT in order // to use this functionality #define ENC_HEAP_START SCRATCH_LIMIT #define ENC_HEAP_END 0x2000 /** This class provide low-level interfacing with the ENC28J60 network interface. This is used by the EtherCard class and not intended for use by (normal) end users. */ class ENC28J60 { public: static uint8_t buffer[]; //!< Data buffer (shared by recieve and transmit) static uint16_t bufferSize; //!< Size of data buffer static bool broadcast_enabled; //!< True if broadcasts enabled (used to allow temporary disable of broadcast for DHCP or other internal functions) static bool promiscuous_enabled; //!< True if promiscuous mode enabled (used to allow temporary disable of promiscuous mode) static uint8_t* tcpOffset () { return buffer + 0x36; } //!< Pointer to the start of TCP payload /** @brief Initialise SPI interface * @note Configures Arduino pins as input / output, etc. */ static void initSPI (); /** @brief Initialise network interface * @param size Size of data buffer * @param macaddr Pointer to 6 byte hardware (MAC) address * @param csPin Arduino pin used for chip select (enable network interface SPI bus). Default = 8 * @return <i>uint8_t</i> ENC28J60 firmware version or zero on failure. */ static uint8_t initialize (const uint16_t size, const uint8_t* macaddr, uint8_t csPin = 8); /** @brief Check if network link is connected * @return <i>bool</i> True if link is up */ static bool isLinkUp (); /** @brief Sends data to network interface * @param len Size of data to send * @note Data buffer is shared by recieve and transmit functions */ static void packetSend (uint16_t len); /** @brief Copy recieved packets to data buffer * @return <i>uint16_t</i> Size of recieved data * @note Data buffer is shared by recieve and transmit functions */ static uint16_t packetReceive (); /** @brief Copy data from ENC28J60 memory * @param page Data page of memory * @param data Pointer to buffer to copy data to */ static void copyout (uint8_t page, const uint8_t* data); /** @brief Copy data to ENC28J60 memory * @param page Data page of memory * @param data Pointer to buffer to copy data from */ static void copyin (uint8_t page, uint8_t* data); /** @brief Get single byte of data from ENC28J60 memory * @param page Data page of memory * @param off Offset of data within page * @return Data value */ static uint8_t peekin (uint8_t page, uint8_t off); /** @brief Put ENC28J60 in sleep mode */ static void powerDown(); // contrib by Alex M. /** @brief Wake ENC28J60 from sleep mode */ static void powerUp(); // contrib by Alex M. /** @brief Enable reception of broadcast messages * @param temporary Set true to temporarily enable broadcast * @note This will increase load on recieved data handling */ static void enableBroadcast(bool temporary = false); /** @brief Disable reception of broadcast messages * @param temporary Set true to only disable if temporarily enabled * @note This will reduce load on recieved data handling */ static void disableBroadcast(bool temporary = false); /** @brief Enables reception of mulitcast messages * @note This will increase load on recieved data handling */ static void enableMulticast (); /** @brief Enables reception of all messages * @param temporary Set true to temporarily enable promiscuous * @note This will increase load significantly on recieved data handling * @note All messages will be accepted, even messages with destination MAC other than own * @note Messages with invalid CRC checksum will still be rejected */ static void enablePromiscuous (bool temporary = false); /** @brief Disable reception of all messages and go back to default mode * @param temporary Set true to only disable if temporarily enabled * @note This will reduce load on received data handling * @note In this mode only unicast and broadcast messages will be received */ static void disablePromiscuous(bool temporary = false); /** @brief Disable reception of mulitcast messages * @note This will reduce load on recieved data handling */ static void disableMulticast(); /** @brief Reset and fully initialise ENC28J60 * @param csPin Arduino pin used for chip select (enable SPI bus) * @return <i>uint8_t</i> 0 on failure */ static uint8_t doBIST(uint8_t csPin = 8); /** @brief Copies a slice from the current packet to RAM * @param dest pointer in RAM where the data is copied to * @param maxlength how many bytes to copy; * @param packetOffset where within the packet to start; if less than maxlength bytes are available only the remaining bytes are copied. * @return <i>uint16_t</i> the number of bytes that have been read * @note At the destination at least maxlength+1 bytes should be reserved because the copied content will be 0-terminated. */ static uint16_t readPacketSlice(char* dest, int16_t maxlength, int16_t packetOffset); /** @brief reserves a block of RAM in the memory of the enc chip * @param size number of bytes to reserve * @return <i>uint16_t</i> start address of the block within the enc memory. 0 if the remaining memory for malloc operation is less than size. * @note There is no enc_free(), i.e., reserved blocks stay reserved for the duration of the program. * @note The total memory available for malloc-operations is determined by ENC_HEAP_END-ENC_HEAP_START, defined in enc28j60.h; by default this is 0, i.e., you have to change these values in order to use enc_malloc(). */ static uint16_t enc_malloc(uint16_t size); /** @brief returns the amount of memory within the enc28j60 chip that is still available for malloc. * @return <i>uint16_t</i> the amount of memory in bytes. */ static uint16_t enc_freemem(); /** @brief copies a block of data from SRAM to the enc memory @param dest destination address within enc memory @param source source pointer to a block of SRAM in the arduino @param num number of bytes to copy @note There is no sanity check. Handle with care */ static void memcpy_to_enc(uint16_t dest, void* source, int16_t num); /** @brief copies a block of data from the enc memory to SRAM @param dest destination address within SRAM @param source source address within enc memory @param num number of bytes to copy */ static void memcpy_from_enc(void* dest, uint16_t source, int16_t num); }; typedef ENC28J60 Ethernet; //!< Define alias Ethernet for ENC28J60 /** Workaround for Errata 13. * The transmission hardware may drop some packets because it thinks a late collision * occurred (which should never happen if all cable length etc. are ok). If setting * this to 1 these packages will be retried a fixed number of times. Costs about 150bytes * of flash. */ #define ETHERCARD_RETRY_LATECOLLISIONS 0 /** Enable pipelining of packet transmissions. * If enabled the packetSend function will not block/wait until the packet is actually * transmitted; but instead this wait is shifted to the next time that packetSend is * called. This gives higher performance; however in combination with * ETHERCARD_RETRY_LATECOLLISIONS this may lead to problems because a packet whose * transmission fails because the ENC-chip thinks that it is a late collision will * not be retried until the next call to packetSend. */ #define ETHERCARD_SEND_PIPELINING 0 #endif
net.h
// Based on the net.h file from the AVRlib library by Pascal Stang. // Author: Guido Socher // Copyright: GPL V2 // // For AVRlib See http://www.procyonengineering.com/ // Used with explicit permission of Pascal Stang. // // 2010-05-20 <[email protected]> // notation: _P = position of a field // _V = value of a field #ifndef NET_H #define NET_H // ******* SERVICE PORTS ******* #define HTTP_PORT 80 #define DNS_PORT 53 #define NTP_PORT 123 // ******* ETH ******* #define ETH_HEADER_LEN 14 #define ETH_LEN 6 // values of certain bytes: #define ETHTYPE_ARP_H_V 0x08 #define ETHTYPE_ARP_L_V 0x06 #define ETHTYPE_IP_H_V 0x08 #define ETHTYPE_IP_L_V 0x00 // byte positions in the ethernet frame: // // Ethernet type field (2bytes): #define ETH_TYPE_H_P 12 #define ETH_TYPE_L_P 13 // #define ETH_DST_MAC 0 #define ETH_SRC_MAC 6 // ******* ARP ******* #define ETH_ARP_OPCODE_REPLY_H_V 0x0 #define ETH_ARP_OPCODE_REPLY_L_V 0x02 #define ETH_ARP_OPCODE_REQ_H_V 0x0 #define ETH_ARP_OPCODE_REQ_L_V 0x01 // start of arp header: #define ETH_ARP_P 0xe // #define ETHTYPE_ARP_L_V 0x06 // arp.dst.ip #define ETH_ARP_DST_IP_P 0x26 // arp.opcode #define ETH_ARP_OPCODE_H_P 0x14 #define ETH_ARP_OPCODE_L_P 0x15 // arp.src.mac #define ETH_ARP_SRC_MAC_P 0x16 #define ETH_ARP_SRC_IP_P 0x1c #define ETH_ARP_DST_MAC_P 0x20 #define ETH_ARP_DST_IP_P 0x26 // ******* IP ******* #define IP_HEADER_LEN 20 #define IP_LEN 4 // ip.src #define IP_SRC_P 0x1a #define IP_DST_P 0x1e #define IP_HEADER_LEN_VER_P 0xe #define IP_CHECKSUM_P 0x18 #define IP_TTL_P 0x16 #define IP_FLAGS_P 0x14 #define IP_P 0xe #define IP_TOTLEN_H_P 0x10 #define IP_TOTLEN_L_P 0x11 #define IP_PROTO_P 0x17 #define IP_PROTO_ICMP_V 1 #define IP_PROTO_TCP_V 6 // 17=0x11 #define IP_PROTO_UDP_V 17 // ******* ICMP ******* #define ICMP_TYPE_ECHOREPLY_V 0 #define ICMP_TYPE_ECHOREQUEST_V 8 // #define ICMP_TYPE_P 0x22 #define ICMP_CHECKSUM_P 0x24 #define ICMP_CHECKSUM_H_P 0x24 #define ICMP_CHECKSUM_L_P 0x25 #define ICMP_IDENT_H_P 0x26 #define ICMP_IDENT_L_P 0x27 #define ICMP_DATA_P 0x2a // ******* UDP ******* #define UDP_HEADER_LEN 8 // #define UDP_SRC_PORT_H_P 0x22 #define UDP_SRC_PORT_L_P 0x23 #define UDP_DST_PORT_H_P 0x24 #define UDP_DST_PORT_L_P 0x25 // #define UDP_LEN_H_P 0x26 #define UDP_LEN_L_P 0x27 #define UDP_CHECKSUM_H_P 0x28 #define UDP_CHECKSUM_L_P 0x29 #define UDP_DATA_P 0x2a // ******* TCP ******* #define TCP_SRC_PORT_H_P 0x22 #define TCP_SRC_PORT_L_P 0x23 #define TCP_DST_PORT_H_P 0x24 #define TCP_DST_PORT_L_P 0x25 // the tcp seq number is 4 bytes 0x26-0x29 #define TCP_SEQ_H_P 0x26 #define TCP_SEQACK_H_P 0x2a // flags: SYN=2 #define TCP_FLAGS_P 0x2f #define TCP_FLAGS_SYN_V 2 #define TCP_FLAGS_FIN_V 1 #define TCP_FLAGS_RST_V 4 #define TCP_FLAGS_PUSH_V 8 #define TCP_FLAGS_SYNACK_V 0x12 #define TCP_FLAGS_ACK_V 0x10 #define TCP_FLAGS_PSHACK_V 0x18 // plain len without the options: #define TCP_HEADER_LEN_PLAIN 20 #define TCP_HEADER_LEN_P 0x2e #define TCP_WIN_SIZE 0x30 #define TCP_CHECKSUM_H_P 0x32 #define TCP_CHECKSUM_L_P 0x33 #define TCP_OPTIONS_P 0x36 // #endif
enc28j60.cpp
// Microchip ENC28J60 Ethernet Interface Driver // Author: Guido Socher // Copyright: GPL V2 // // Based on the enc28j60.c file from the AVRlib library by Pascal Stang. // For AVRlib See http://www.procyonengineering.com/ // Used with explicit permission of Pascal Stang. // // 2010-05-20 <[email protected]> #if ARDUINO >= 100 #include <Arduino.h> // Arduino 1.0 #else #include <Wprogram.h> // Arduino 0022 #endif #include "enc28j60.h" uint16_t ENC28J60::bufferSize; bool ENC28J60::broadcast_enabled = false; bool ENC28J60::promiscuous_enabled = false; // ENC28J60 Control Registers // Control register definitions are a combination of address, // bank number, and Ethernet/MAC/PHY indicator bits. // - Register address (bits 0-4) // - Bank number (bits 5-6) // - MAC/PHY indicator (bit 7) #define ADDR_MASK 0x1F #define BANK_MASK 0x60 #define SPRD_MASK 0x80 // All-bank registers #define EIE 0x1B #define EIR 0x1C #define ESTAT 0x1D #define ECON2 0x1E #define ECON1 0x1F // Bank 0 registers #define ERDPT (0x00|0x00) #define EWRPT (0x02|0x00) #define ETXST (0x04|0x00) #define ETXND (0x06|0x00) #define ERXST (0x08|0x00) #define ERXND (0x0A|0x00) #define ERXRDPT (0x0C|0x00) // #define ERXWRPT (0x0E|0x00) #define EDMAST (0x10|0x00) #define EDMAND (0x12|0x00) // #define EDMADST (0x14|0x00) #define EDMACS (0x16|0x00) // Bank 1 registers #define EHT0 (0x00|0x20) #define EHT1 (0x01|0x20) #define EHT2 (0x02|0x20) #define EHT3 (0x03|0x20) #define EHT4 (0x04|0x20) #define EHT5 (0x05|0x20) #define EHT6 (0x06|0x20) #define EHT7 (0x07|0x20) #define EPMM0 (0x08|0x20) #define EPMM1 (0x09|0x20) #define EPMM2 (0x0A|0x20) #define EPMM3 (0x0B|0x20) #define EPMM4 (0x0C|0x20) #define EPMM5 (0x0D|0x20) #define EPMM6 (0x0E|0x20) #define EPMM7 (0x0F|0x20) #define EPMCS (0x10|0x20) // #define EPMO (0x14|0x20) #define EWOLIE (0x16|0x20) #define EWOLIR (0x17|0x20) #define ERXFCON (0x18|0x20) #define EPKTCNT (0x19|0x20) // Bank 2 registers #define MACON1 (0x00|0x40|0x80) #define MACON2 (0x01|0x40|0x80) #define MACON3 (0x02|0x40|0x80) #define MACON4 (0x03|0x40|0x80) #define MABBIPG (0x04|0x40|0x80) #define MAIPG (0x06|0x40|0x80) #define MACLCON1 (0x08|0x40|0x80) #define MACLCON2 (0x09|0x40|0x80) #define MAMXFL (0x0A|0x40|0x80) #define MAPHSUP (0x0D|0x40|0x80) #define MICON (0x11|0x40|0x80) #define MICMD (0x12|0x40|0x80) #define MIREGADR (0x14|0x40|0x80) #define MIWR (0x16|0x40|0x80) #define MIRD (0x18|0x40|0x80) // Bank 3 registers #define MAADR1 (0x00|0x60|0x80) #define MAADR0 (0x01|0x60|0x80) #define MAADR3 (0x02|0x60|0x80) #define MAADR2 (0x03|0x60|0x80) #define MAADR5 (0x04|0x60|0x80) #define MAADR4 (0x05|0x60|0x80) #define EBSTSD (0x06|0x60) #define EBSTCON (0x07|0x60) #define EBSTCS (0x08|0x60) #define MISTAT (0x0A|0x60|0x80) #define EREVID (0x12|0x60) #define ECOCON (0x15|0x60) #define EFLOCON (0x17|0x60) #define EPAUS (0x18|0x60) // ENC28J60 ERXFCON Register Bit Definitions #define ERXFCON_UCEN 0x80 #define ERXFCON_ANDOR 0x40 #define ERXFCON_CRCEN 0x20 #define ERXFCON_PMEN 0x10 #define ERXFCON_MPEN 0x08 #define ERXFCON_HTEN 0x04 #define ERXFCON_MCEN 0x02 #define ERXFCON_BCEN 0x01 // ENC28J60 EIE Register Bit Definitions #define EIE_INTIE 0x80 #define EIE_PKTIE 0x40 #define EIE_DMAIE 0x20 #define EIE_LINKIE 0x10 #define EIE_TXIE 0x08 #define EIE_WOLIE 0x04 #define EIE_TXERIE 0x02 #define EIE_RXERIE 0x01 // ENC28J60 EIR Register Bit Definitions #define EIR_PKTIF 0x40 #define EIR_DMAIF 0x20 #define EIR_LINKIF 0x10 #define EIR_TXIF 0x08 #define EIR_WOLIF 0x04 #define EIR_TXERIF 0x02 #define EIR_RXERIF 0x01 // ENC28J60 ESTAT Register Bit Definitions #define ESTAT_INT 0x80 #define ESTAT_LATECOL 0x10 #define ESTAT_RXBUSY 0x04 #define ESTAT_TXABRT 0x02 #define ESTAT_CLKRDY 0x01 // ENC28J60 ECON2 Register Bit Definitions #define ECON2_AUTOINC 0x80 #define ECON2_PKTDEC 0x40 #define ECON2_PWRSV 0x20 #define ECON2_VRPS 0x08 // ENC28J60 ECON1 Register Bit Definitions #define ECON1_TXRST 0x80 #define ECON1_RXRST 0x40 #define ECON1_DMAST 0x20 #define ECON1_CSUMEN 0x10 #define ECON1_TXRTS 0x08 #define ECON1_RXEN 0x04 #define ECON1_BSEL1 0x02 #define ECON1_BSEL0 0x01 // ENC28J60 MACON1 Register Bit Definitions #define MACON1_LOOPBK 0x10 #define MACON1_TXPAUS 0x08 #define MACON1_RXPAUS 0x04 #define MACON1_PASSALL 0x02 #define MACON1_MARXEN 0x01 // ENC28J60 MACON2 Register Bit Definitions #define MACON2_MARST 0x80 #define MACON2_RNDRST 0x40 #define MACON2_MARXRST 0x08 #define MACON2_RFUNRST 0x04 #define MACON2_MATXRST 0x02 #define MACON2_TFUNRST 0x01 // ENC28J60 MACON3 Register Bit Definitions #define MACON3_PADCFG2 0x80 #define MACON3_PADCFG1 0x40 #define MACON3_PADCFG0 0x20 #define MACON3_TXCRCEN 0x10 #define MACON3_PHDRLEN 0x08 #define MACON3_HFRMLEN 0x04 #define MACON3_FRMLNEN 0x02 #define MACON3_FULDPX 0x01 // ENC28J60 MICMD Register Bit Definitions #define MICMD_MIISCAN 0x02 #define MICMD_MIIRD 0x01 // ENC28J60 MISTAT Register Bit Definitions #define MISTAT_NVALID 0x04 #define MISTAT_SCAN 0x02 #define MISTAT_BUSY 0x01 // ENC28J60 EBSTCON Register Bit Definitions #define EBSTCON_PSV2 0x80 #define EBSTCON_PSV1 0x40 #define EBSTCON_PSV0 0x20 #define EBSTCON_PSEL 0x10 #define EBSTCON_TMSEL1 0x08 #define EBSTCON_TMSEL0 0x04 #define EBSTCON_TME 0x02 #define EBSTCON_BISTST 0x01 // PHY registers #define PHCON1 0x00 #define PHSTAT1 0x01 #define PHHID1 0x02 #define PHHID2 0x03 #define PHCON2 0x10 #define PHSTAT2 0x11 #define PHIE 0x12 #define PHIR 0x13 #define PHLCON 0x14 // ENC28J60 PHY PHCON1 Register Bit Definitions #define PHCON1_PRST 0x8000 #define PHCON1_PLOOPBK 0x4000 #define PHCON1_PPWRSV 0x0800 #define PHCON1_PDPXMD 0x0100 // ENC28J60 PHY PHSTAT1 Register Bit Definitions #define PHSTAT1_PFDPX 0x1000 #define PHSTAT1_PHDPX 0x0800 #define PHSTAT1_LLSTAT 0x0004 #define PHSTAT1_JBSTAT 0x0002 // ENC28J60 PHY PHCON2 Register Bit Definitions #define PHCON2_FRCLINK 0x4000 #define PHCON2_TXDIS 0x2000 #define PHCON2_JABBER 0x0400 #define PHCON2_HDLDIS 0x0100 // ENC28J60 Packet Control Byte Bit Definitions #define PKTCTRL_PHUGEEN 0x08 #define PKTCTRL_PPADEN 0x04 #define PKTCTRL_PCRCEN 0x02 #define PKTCTRL_POVERRIDE 0x01 // SPI operation codes #define ENC28J60_READ_CTRL_REG 0x00 #define ENC28J60_READ_BUF_MEM 0x3A #define ENC28J60_WRITE_CTRL_REG 0x40 #define ENC28J60_WRITE_BUF_MEM 0x7A #define ENC28J60_BIT_FIELD_SET 0x80 #define ENC28J60_BIT_FIELD_CLR 0xA0 #define ENC28J60_SOFT_RESET 0xFF // max frame length which the controller will accept: // (note: maximum ethernet frame length would be 1518) #define MAX_FRAMELEN 1500 #define FULL_SPEED 1 // switch to full-speed SPI for bulk transfers static byte Enc28j60Bank; static byte selectPin; void ENC28J60::initSPI () { pinMode(SS, OUTPUT); digitalWrite(SS, HIGH); pinMode(MOSI, OUTPUT); pinMode(SCK, OUTPUT); pinMode(MISO, INPUT); digitalWrite(MOSI, HIGH); digitalWrite(MOSI, LOW); digitalWrite(SCK, LOW); SPCR = bit(SPE) | bit(MSTR); // 8 MHz @ 16 bitSet(SPSR, SPI2X); } static void enableChip () { cli(); digitalWrite(selectPin, LOW); } static void disableChip () { digitalWrite(selectPin, HIGH); sei(); } static void xferSPI (byte data) { SPDR = data; while (!(SPSR&(1<<SPIF))) ; } static byte readOp (byte op, byte address) { enableChip(); xferSPI(op | (address & ADDR_MASK)); xferSPI(0x00); if (address & 0x80) xferSPI(0x00); byte result = SPDR; disableChip(); return result; } static void writeOp (byte op, byte address, byte data) { enableChip(); xferSPI(op | (address & ADDR_MASK)); xferSPI(data); disableChip(); } static void readBuf(uint16_t len, byte* data) { uint8_t nextbyte; enableChip(); if (len != 0) { xferSPI(ENC28J60_READ_BUF_MEM); SPDR = 0x00; while (--len) { while (!(SPSR & (1<<SPIF))) ; nextbyte = SPDR; SPDR = 0x00; *data++ = nextbyte; } while (!(SPSR & (1<<SPIF))) ; *data++ = SPDR; } disableChip(); } static void writeBuf(uint16_t len, const byte* data) { enableChip(); if (len != 0) { xferSPI(ENC28J60_WRITE_BUF_MEM); SPDR = *data++; while (--len) { uint8_t nextbyte = *data++; while (!(SPSR & (1<<SPIF))) ; SPDR = nextbyte; }; while (!(SPSR & (1<<SPIF))) ; } disableChip(); } static void SetBank (byte address) { if ((address & BANK_MASK) != Enc28j60Bank) { writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_BSEL1|ECON1_BSEL0); Enc28j60Bank = address & BANK_MASK; writeOp(ENC28J60_BIT_FIELD_SET, ECON1, Enc28j60Bank>>5); } } static byte readRegByte (byte address) { SetBank(address); return readOp(ENC28J60_READ_CTRL_REG, address); } static uint16_t readReg(byte address) { return readRegByte(address) + (readRegByte(address+1) << 8); } static void writeRegByte (byte address, byte data) { SetBank(address); writeOp(ENC28J60_WRITE_CTRL_REG, address, data); } static void writeReg(byte address, uint16_t data) { writeRegByte(address, data); writeRegByte(address + 1, data >> 8); } static uint16_t readPhyByte (byte address) { writeRegByte(MIREGADR, address); writeRegByte(MICMD, MICMD_MIIRD); while (readRegByte(MISTAT) & MISTAT_BUSY) ; writeRegByte(MICMD, 0x00); return readRegByte(MIRD+1); } static void writePhy (byte address, uint16_t data) { writeRegByte(MIREGADR, address); writeReg(MIWR, data); while (readRegByte(MISTAT) & MISTAT_BUSY) ; } byte ENC28J60::initialize (uint16_t size, const byte* macaddr, byte csPin) { bufferSize = size; if (bitRead(SPCR, SPE) == 0) initSPI(); selectPin = csPin; pinMode(selectPin, OUTPUT); disableChip(); writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); delay(2); // errata B7/2 while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) ; writeReg(ERXST, RXSTART_INIT); writeReg(ERXRDPT, RXSTART_INIT); writeReg(ERXND, RXSTOP_INIT); writeReg(ETXST, TXSTART_INIT); writeReg(ETXND, TXSTOP_INIT); writeRegByte(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN); writeReg(EPMM0, 0x303f); writeReg(EPMCS, 0xf7f9); writeRegByte(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); writeRegByte(MACON2, 0x00); writeOp(ENC28J60_BIT_FIELD_SET, MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); writeReg(MAIPG, 0x0C12); writeRegByte(MABBIPG, 0x12); writeReg(MAMXFL, MAX_FRAMELEN); writeRegByte(MAADR5, macaddr[0]); writeRegByte(MAADR4, macaddr[1]); writeRegByte(MAADR3, macaddr[2]); writeRegByte(MAADR2, macaddr[3]); writeRegByte(MAADR1, macaddr[4]); writeRegByte(MAADR0, macaddr[5]); writePhy(PHCON2, PHCON2_HDLDIS); SetBank(ECON1); writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); byte rev = readRegByte(EREVID); // microchip forgot to step the number on the silcon when they // released the revision B7. 6 is now rev B7. We still have // to see what they do when they release B8. At the moment // there is no B8 out yet if (rev > 5) ++rev; return rev; } bool ENC28J60::isLinkUp() { return (readPhyByte(PHSTAT2) >> 2) & 1; } /* struct __attribute__((__packed__)) transmit_status_vector { uint16_t transmitByteCount; byte transmitCollisionCount : 4; byte transmitCrcError : 1; byte transmitLengthCheckError : 1; byte transmitLengthOutRangeError : 1; byte transmitDone : 1; byte transmitMulticast : 1; byte transmitBroadcast : 1; byte transmitPacketDefer : 1; byte transmitExcessiveDefer : 1; byte transmitExcessiveCollision : 1; byte transmitLateCollision : 1; byte transmitGiant : 1; byte transmitUnderrun : 1; uint16_t totalTransmitted; byte transmitControlFrame : 1; byte transmitPauseControlFrame : 1; byte backpressureApplied : 1; byte transmitVLAN : 1; byte zero : 4; }; */ struct transmit_status_vector { uint8_t bytes[7]; }; #if ETHERCARD_SEND_PIPELINING #define BREAKORCONTINUE retry=0; continue; #else #define BREAKORCONTINUE break; #endif void ENC28J60::packetSend(uint16_t len) { byte retry = 0; #if ETHERCARD_SEND_PIPELINING goto resume_last_transmission; #endif while (1) { // latest errata sheet: DS80349C // always reset transmit logic (Errata Issue 12) // the Microchip TCP/IP stack implementation used to first check // whether TXERIF is set and only then reset the transmit logic // but this has been changed in later versions; possibly they // have a reason for this; they don't mention this in the errata // sheet writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); writeOp(ENC28J60_BIT_FIELD_CLR, EIR, EIR_TXERIF|EIR_TXIF); // prepare new transmission if (retry == 0) { writeReg(EWRPT, TXSTART_INIT); writeReg(ETXND, TXSTART_INIT+len); writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); writeBuf(len, buffer); } // initiate transmission writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); #if ETHERCARD_SEND_PIPELINING if (retry == 0) return; #endif resume_last_transmission: // wait until transmission has finished; referrring to the data sheet and // to the errata (Errata Issue 13; Example 1) you only need to wait until either // TXIF or TXERIF gets set; however this leads to hangs; apparently Microchip // realized this and in later implementations of their tcp/ip stack they introduced // a counter to avoid hangs; of course they didn't update the errata sheet uint16_t count = 0; while ((readRegByte(EIR) & (EIR_TXIF | EIR_TXERIF)) == 0 && ++count < 1000U) ; if (!(readRegByte(EIR) & EIR_TXERIF) && count < 1000U) { // no error; start new transmission BREAKORCONTINUE } // cancel previous transmission if stuck writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRTS); #if ETHERCARD_RETRY_LATECOLLISIONS == 0 BREAKORCONTINUE #endif // Check whether the chip thinks that a late collision ocurred; the chip // may be wrong (Errata Issue 13); therefore we retry. We could check // LATECOL in the ESTAT register in order to find out whether the chip // thinks a late collision ocurred but (Errata Issue 15) tells us that // this is not working. Therefore we check TSV transmit_status_vector tsv; uint16_t etxnd = readReg(ETXND); writeReg(ERDPT, etxnd+1); readBuf(sizeof(transmit_status_vector), (byte*) &tsv); // LATECOL is bit number 29 in TSV (starting from 0) if (!((readRegByte(EIR) & EIR_TXERIF) && (tsv.bytes[3] & 1<<5) /*tsv.transmitLateCollision*/) || retry > 16U) { // there was some error but no LATECOL so we do not repeat BREAKORCONTINUE } retry++; } } uint16_t ENC28J60::packetReceive() { static uint16_t gNextPacketPtr = RXSTART_INIT; static bool unreleasedPacket = false; uint16_t len = 0; if (unreleasedPacket) { if (gNextPacketPtr == 0) writeReg(ERXRDPT, RXSTOP_INIT); else writeReg(ERXRDPT, gNextPacketPtr - 1); unreleasedPacket = false; } if (readRegByte(EPKTCNT) > 0) { writeReg(ERDPT, gNextPacketPtr); struct { uint16_t nextPacket; uint16_t byteCount; uint16_t status; } header; readBuf(sizeof header, (byte*) &header); gNextPacketPtr = header.nextPacket; len = header.byteCount - 4; //remove the CRC count if (len>bufferSize-1) len=bufferSize-1; if ((header.status & 0x80)==0) len = 0; else readBuf(len, buffer); buffer[len] = 0; unreleasedPacket = true; writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); } return len; } void ENC28J60::copyout (byte page, const byte* data) { uint16_t destPos = SCRATCH_START + (page << SCRATCH_PAGE_SHIFT); if (destPos < SCRATCH_START || destPos > SCRATCH_LIMIT - SCRATCH_PAGE_SIZE) return; writeReg(EWRPT, destPos); writeBuf(SCRATCH_PAGE_SIZE, data); } void ENC28J60::copyin (byte page, byte* data) { uint16_t destPos = SCRATCH_START + (page << SCRATCH_PAGE_SHIFT); if (destPos < SCRATCH_START || destPos > SCRATCH_LIMIT - SCRATCH_PAGE_SIZE) return; writeReg(ERDPT, destPos); readBuf(SCRATCH_PAGE_SIZE, data); } byte ENC28J60::peekin (byte page, byte off) { byte result = 0; uint16_t destPos = SCRATCH_START + (page << SCRATCH_PAGE_SHIFT) + off; if (SCRATCH_START <= destPos && destPos < SCRATCH_LIMIT) { writeReg(ERDPT, destPos); readBuf(1, &result); } return result; } // Contributed by Alex M. Based on code from: http://blog.derouineau.fr // /2011/07/putting-enc28j60-ethernet-controler-in-sleep-mode/ void ENC28J60::powerDown() { writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN); while(readRegByte(ESTAT) & ESTAT_RXBUSY); while(readRegByte(ECON1) & ECON1_TXRTS); writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS); writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV); } void ENC28J60::powerUp() { writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV); while(!readRegByte(ESTAT) & ESTAT_CLKRDY); writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); } void ENC28J60::enableBroadcast (bool temporary) { writeRegByte(ERXFCON, readRegByte(ERXFCON) | ERXFCON_BCEN); if(!temporary) broadcast_enabled = true; } void ENC28J60::disableBroadcast (bool temporary) { if(!temporary) broadcast_enabled = false; if(!broadcast_enabled) writeRegByte(ERXFCON, readRegByte(ERXFCON) & ~ERXFCON_BCEN); } void ENC28J60::enableMulticast () { writeRegByte(ERXFCON, readRegByte(ERXFCON) | ERXFCON_MCEN); } void ENC28J60::disableMulticast () { writeRegByte(ERXFCON, readRegByte(ERXFCON) & ~ERXFCON_MCEN); } void ENC28J60::enablePromiscuous (bool temporary) { writeRegByte(ERXFCON, readRegByte(ERXFCON) & ERXFCON_CRCEN); if(!temporary) promiscuous_enabled = true; } void ENC28J60::disablePromiscuous (bool temporary) { if(!temporary) promiscuous_enabled = false; if(!promiscuous_enabled) { writeRegByte(ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN|ERXFCON_BCEN); } } uint8_t ENC28J60::doBIST ( byte csPin) { #define RANDOM_FILL 0b0000 #define ADDRESS_FILL 0b0100 #define PATTERN_SHIFT 0b1000 #define RANDOM_RACE 0b1100 // init if (bitRead(SPCR, SPE) == 0) initSPI(); selectPin = csPin; pinMode(selectPin, OUTPUT); disableChip(); writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); delay(2); // errata B7/2 while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) ; // now we can start the memory test uint16_t macResult; uint16_t bitsResult; // clear some of the registers registers writeRegByte(ECON1, 0); writeReg(EDMAST, 0); // Set up necessary pointers for the DMA to calculate over the entire memory writeReg(EDMAND, 0x1FFFu); writeReg(ERXND, 0x1FFFu); // Enable Test Mode and do an Address Fill SetBank(EBSTCON); writeRegByte(EBSTCON, EBSTCON_TME | EBSTCON_BISTST | ADDRESS_FILL); // wait for BISTST to be reset, only after that are we actually ready to // start the test // this was undocumented :( while (readOp(ENC28J60_READ_CTRL_REG, EBSTCON) & EBSTCON_BISTST); writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME); // now start the actual reading an calculating the checksum until the end is // reached writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST | ECON1_CSUMEN); SetBank(EDMACS); while(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST); macResult = readReg(EDMACS); bitsResult = readReg(EBSTCS); // Compare the results // 0xF807 should always be generated in Address fill mode if ((macResult != bitsResult) || (bitsResult != 0xF807)) { return 0; } // reset test flag writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME); // Now start the BIST with random data test, and also keep on swapping the // DMA/BIST memory ports. writeRegByte(EBSTSD, 0b10101010 | millis()); writeRegByte(EBSTCON, EBSTCON_TME | EBSTCON_PSEL | EBSTCON_BISTST | RANDOM_FILL); // wait for BISTST to be reset, only after that are we actually ready to // start the test // this was undocumented :( while (readOp(ENC28J60_READ_CTRL_REG, EBSTCON) & EBSTCON_BISTST); writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME); // now start the actual reading an calculating the checksum until the end is // reached writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST | ECON1_CSUMEN); SetBank(EDMACS); while(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST); macResult = readReg(EDMACS); bitsResult = readReg(EBSTCS); // The checksum should be equal return macResult == bitsResult; } void ENC28J60::memcpy_to_enc(uint16_t dest, void* source, int16_t num) { writeReg(EWRPT, dest); writeBuf(num, (uint8_t*) source); } void ENC28J60::memcpy_from_enc(void* dest, uint16_t source, int16_t num) { writeReg(ERDPT, source); readBuf(num, (uint8_t*) dest); } static uint16_t endRam = ENC_HEAP_END; uint16_t ENC28J60::enc_malloc(uint16_t size) { if (endRam-size >= ENC_HEAP_START) { endRam -= size; return endRam; } return 0; } uint16_t ENC28J60::enc_freemem() { return endRam-ENC_HEAP_START; } uint16_t ENC28J60::readPacketSlice(char* dest, int16_t maxlength, int16_t packetOffset) { uint16_t erxrdpt = readReg(ERXRDPT); int16_t packetLength; memcpy_from_enc((char*) &packetLength, (erxrdpt+3)%(RXSTOP_INIT+1), 2); packetLength -= 4; // remove crc int16_t bytesToCopy = packetLength - packetOffset; if (bytesToCopy > maxlength) bytesToCopy = maxlength; if (bytesToCopy <= 0) bytesToCopy = 0; int16_t startofSlice = (erxrdpt+7+packetOffset)%(RXSTOP_INIT+1); memcpy_from_enc(dest, startofSlice, bytesToCopy); dest[bytesToCopy] = 0; return bytesToCopy; }
EtherCard.cpp
// This code slightly follows the conventions of, but is not derived from: // EHTERSHIELD_H library for Arduino etherShield // Copyright (c) 2008 Xing Yu. All right reserved. (this is LGPL v2.1) // It is however derived from the enc28j60 and ip code (which is GPL v2) // Author: Pascal Stang // Modified by: Guido Socher // DHCP code: Andrew Lindsay // Hence: GPL V2 // // 2010-05-19 <[email protected]> #include <EtherCard.h> #include <stdarg.h> #include <avr/eeprom.h> #define WRITEBUF 0 #define READBUF 1 #define BUFCOUNT 2 //#define FLOATEMIT // uncomment line to enable $T in emit_P for float emitting byte Stash::map[SCRATCH_MAP_SIZE]; Stash::Block Stash::bufs[BUFCOUNT]; uint8_t Stash::allocBlock () { for (uint8_t i = 0; i < sizeof map; ++i) if (map[i] != 0) for (uint8_t j = 0; j < 8; ++j) if (bitRead(map[i], j)) { bitClear(map[i], j); return (i << 3) + j; } return 0; } void Stash::freeBlock (uint8_t block) { bitSet(map[block>>3], block & 7); } uint8_t Stash::fetchByte (uint8_t blk, uint8_t off) { return blk == bufs[WRITEBUF].bnum ? bufs[WRITEBUF].bytes[off] : blk == bufs[READBUF].bnum ? bufs[READBUF].bytes[off] : ether.peekin(blk, off); } // block 0 is special since always occupied void Stash::initMap (uint8_t last /*=SCRATCH_PAGE_NUM*/) { last = SCRATCH_PAGE_NUM; while (--last > 0) freeBlock(last); } // load a page/block either into the write or into the readbuffer void Stash::load (uint8_t idx, uint8_t blk) { if (blk != bufs[idx].bnum) { if (idx == WRITEBUF) { ether.copyout(bufs[idx].bnum, bufs[idx].bytes); if (blk == bufs[READBUF].bnum) bufs[READBUF].bnum = 255; // forget read page if same } else if (blk == bufs[WRITEBUF].bnum) { // special case: read page is same as write buffer memcpy(&bufs[READBUF], &bufs[WRITEBUF], sizeof bufs[0]); return; } bufs[idx].bnum = blk; ether.copyin(bufs[idx].bnum, bufs[idx].bytes); } } uint8_t Stash::freeCount () { uint8_t count = 0; for (uint8_t i = 0; i < sizeof map; ++i) for (uint8_t m = 0x80; m != 0; m >>= 1) if (map[i] & m) ++count; return count; } // create a new stash; make it the active stash; return the first block as a handle uint8_t Stash::create () { uint8_t blk = allocBlock(); load(WRITEBUF, blk); bufs[WRITEBUF].head.count = 0; bufs[WRITEBUF].head.first = bufs[0].head.last = blk; bufs[WRITEBUF].tail = sizeof (StashHeader); bufs[WRITEBUF].next = 0; return open(blk); // you are now the active stash } // the stashheader part only contains reasonable data if we are the first block uint8_t Stash::open (uint8_t blk) { curr = blk; offs = sizeof (StashHeader); // goto first byte load(READBUF, curr); memcpy((StashHeader*) this, bufs[READBUF].bytes, sizeof (StashHeader)); return curr; } // save the metadata of current block into the first block void Stash::save () { load(WRITEBUF, first); memcpy(bufs[WRITEBUF].bytes, (StashHeader*) this, sizeof (StashHeader)); if (bufs[READBUF].bnum == first) load(READBUF, 0); // invalidates original in case it was the same block } // follow the linked list of blocks and free every block void Stash::release () { while (first > 0) { freeBlock(first); first = ether.peekin(first, 63); } } void Stash::put (char c) { load(WRITEBUF, last); uint8_t t = bufs[WRITEBUF].tail; bufs[WRITEBUF].bytes[t++] = c; if (t <= 62) bufs[WRITEBUF].tail = t; else { bufs[WRITEBUF].next = allocBlock(); last = bufs[WRITEBUF].next; load(WRITEBUF, last); bufs[WRITEBUF].tail = bufs[WRITEBUF].next = 0; ++count; } } char Stash::get () { load(READBUF, curr); if (curr == last && offs >= bufs[READBUF].tail) return 0; uint8_t b = bufs[READBUF].bytes[offs]; if (++offs >= 63 && curr != last) { curr = bufs[READBUF].next; offs = 0; } return b; } // fetchbyte(last, 62) is tail, i.e., number of characters in last block uint16_t Stash::size () { return 63 * count + fetchByte(last, 62) - sizeof (StashHeader); } static char* wtoa (uint16_t value, char* ptr) { if (value > 9) ptr = wtoa(value / 10, ptr); *ptr = '0' + value % 10; *++ptr = 0; return ptr; } // write information about the fmt string and the arguments into special page/block 0 // block 0 is initially marked as allocated and never returned by allocateBlock void Stash::prepare (const char* fmt PROGMEM, ...) { Stash::load(WRITEBUF, 0); uint16_t* segs = Stash::bufs[WRITEBUF].words; *segs++ = strlen_P(fmt); #ifdef __AVR__ *segs++ = (uint16_t) fmt; #else *segs++ = (uint32_t) fmt; *segs++ = (uint32_t) fmt >> 16; #endif va_list ap; va_start(ap, fmt); for (;;) { char c = pgm_read_byte(fmt++); if (c == 0) break; if (c == '
* * ¤ _Available only if FLOATEMIT is defined_ * * # Examples * ~~~~~~~~~~~~~{.c} * uint16_t ddd = 123; * double ttt = 1.23; * uint16_t hhh = 0xa4; * long lll = 123456789; * char * sss; * char fff[] PROGMEM = "MyMemory"; * * sss[0] = 'G'; * sss[1] = 'P'; * sss[2] = 'L'; * sss[3] = 0; * buf.emit_p( PSTR("ddd=$D\n"), ddd ); // "ddd=123\n" * buf.emit_p( PSTR("ttt=$T\n"), ttt ); // "ttt=1.23\n" **TO CHECK** * buf.emit_p( PSTR("hhh=$H\n"), hhh ); // "hhh=a4\n" * buf.emit_p( PSTR("lll=$L\n"), lll ); // "lll=123456789\n" * buf.emit_p( PSTR("sss=$S\n"), sss ); // "sss=GPL\n" * buf.emit_p( PSTR("fff=$F\n"), fff ); // "fff=MyMemory\n" * ~~~~~~~~~~~~~ * */ class BufferFiller : public Print { uint8_t *start; //!< Pointer to start of buffer uint8_t *ptr; //!< Pointer to cursor position public: /** @brief Empty constructor */ BufferFiller () {} /** @brief Constructor * @param buf Pointer to the ethernet data buffer */ BufferFiller (uint8_t* buf) : start (buf), ptr (buf) {} /** @brief Add formatted text to buffer * @param fmt Format string (see Class description) * @param ... parameters for format string */ void emit_p (const char* fmt PROGMEM, ...); /** @brief Add data to buffer from main memory * @param s Pointer to data * @param n Number of characters to copy */ void emit_raw (const char* s, uint16_t n) { memcpy(ptr, s, n); ptr += n; } /** @brief Add data to buffer from program space string * @param p Program space string pointer * @param n Number of characters to copy */ void emit_raw_p (const char* p PROGMEM, uint16_t n) { memcpy_P(ptr, p, n); ptr += n; } /** @brief Get pointer to start of buffer * @return <i>uint8_t*</i> Pointer to start of buffer */ uint8_t* buffer () const { return start; } /** @brief Get cursor position * @return <i>uint16_t</i> Cursor postion */ uint16_t position () const { return ptr - start; } /** @brief Write one byte to buffer * @param v Byte to add to buffer */ virtual WRITE_RESULT write (uint8_t v) { *ptr++ = v; WRITE_RETURN } }; /** This class provides the main interface to a ENC28J60 based network interface card and is the class most users will use. * @note All TCP/IP client (outgoing) connections are made from source port in range 2816-3071. Do not use these source ports for other purposes. */ class EtherCard : public Ethernet { public: static uint8_t mymac[ETH_LEN]; ///< MAC address static uint8_t myip[IP_LEN]; ///< IP address static uint8_t netmask[IP_LEN]; ///< Netmask static uint8_t broadcastip[IP_LEN]; ///< Subnet broadcast address static uint8_t gwip[IP_LEN]; ///< Gateway static uint8_t dhcpip[IP_LEN]; ///< DHCP server IP address static uint8_t dnsip[IP_LEN]; ///< DNS server IP address static uint8_t hisip[IP_LEN]; ///< DNS lookup result static uint16_t hisport; ///< TCP port to connect to (default 80) static bool using_dhcp; ///< True if using DHCP static bool persist_tcp_connection; ///< False to break connections on first packet received static uint16_t delaycnt; ///< Counts number of cycles of packetLoop when no packet received - used to trigger periodic gateway ARP request // EtherCard.cpp /** @brief Initialise the network interface * @param size Size of data buffer * @param macaddr Hardware address to assign to the network interface (6 bytes) * @param csPin Arduino pin number connected to chip select. Default = 8 * @return <i>uint8_t</i> Firmware version or zero on failure. */ static uint8_t begin (const uint16_t size, const uint8_t* macaddr, uint8_t csPin = SS); /** @brief Configure network interface with static IP * @param my_ip IP address (4 bytes). 0 for no change. * @param gw_ip Gateway address (4 bytes). 0 for no change. Default = 0 * @param dns_ip DNS address (4 bytes). 0 for no change. Default = 0 * @param mask Subnet mask (4 bytes). 0 for no change. Default = 0 * @return <i>bool</i> Returns true on success - actually always true */ static bool staticSetup (const uint8_t* my_ip, const uint8_t* gw_ip = 0, const uint8_t* dns_ip = 0, const uint8_t* mask = 0); // tcpip.cpp /** @brief Sends a UDP packet to the IP address of last processed received packet * @param data Pointer to data payload * @param len Size of data payload (max 220) * @param port Source IP port */ static void makeUdpReply (const char *data, uint8_t len, uint16_t port); /** @brief Parse received data * @param plen Size of data to parse (e.g. return value of packetReceive()). * @return <i>uint16_t</i> Offset of TCP payload data in data buffer or zero if packet processed * @note Data buffer is shared by receive and transmit functions * @note Only handles ARP and IP */ static uint16_t packetLoop (uint16_t plen); /** @brief Accept a TCP/IP connection * @param port IP port to accept on - do nothing if wrong port * @param plen Number of bytes in packet * @return <i>uint16_t</i> Offset within packet of TCP payload. Zero for no data. */ static uint16_t accept (uint16_t port, uint16_t plen); /** @brief Send a response to a HTTP request * @param dlen Size of the HTTP (TCP) payload */ static void httpServerReply (uint16_t dlen); /** @brief Send a response to a HTTP request * @param dlen Size of the HTTP (TCP) payload * @param flags TCP flags */ static void httpServerReply_with_flags (uint16_t dlen , uint8_t flags); /** @brief Acknowledge TCP message * @todo Is this / should this be private? */ static void httpServerReplyAck (); /** @brief Set the gateway address * @param gwipaddr Gateway address (4 bytes) */ static void setGwIp (const uint8_t *gwipaddr); /** @brief Updates the broadcast address based on current IP address and subnet mask */ static void updateBroadcastAddress(); /** @brief Check if got gateway hardware address (ARP lookup) * @return <i>unit8_t</i> True if gateway found */ static uint8_t clientWaitingGw (); /** @brief Check if got gateway DNS address (ARP lookup) * @return <i>unit8_t</i> True if DNS found */ static uint8_t clientWaitingDns (); /** @brief Prepare a TCP request * @param result_cb Pointer to callback function that handles TCP result * @param datafill_cb Pointer to callback function that handles TCP data payload * @param port Remote TCP/IP port to connect to * @return <i>unit8_t</i> ID of TCP/IP session (0-7) * @note Return value provides id of the request to allow up to 7 concurrent requests */ static uint8_t clientTcpReq (uint8_t (*result_cb)(uint8_t,uint8_t,uint16_t,uint16_t), uint16_t (*datafill_cb)(uint8_t),uint16_t port); /** @brief Prepare HTTP request * @param urlbuf Pointer to c-string URL folder * @param urlbuf_varpart Pointer to c-string URL file * @param hoststr Pointer to c-string hostname * @param additionalheaderline Pointer to c-string with additional HTTP header info * @param callback Pointer to callback function to handle response * @note Request sent in main packetloop */ static void browseUrl (const char *urlbuf, const char *urlbuf_varpart, const char *hoststr, const char *additionalheaderline, void (*callback)(uint8_t,uint16_t,uint16_t)); /** @brief Prepare HTTP request * @param urlbuf Pointer to c-string URL folder * @param urlbuf_varpart Pointer to c-string URL file * @param hoststr Pointer to c-string hostname * @param callback Pointer to callback function to handle response * @note Request sent in main packetloop */ static void browseUrl (const char *urlbuf, const char *urlbuf_varpart, const char *hoststr, void (*callback)(uint8_t,uint16_t,uint16_t)); /** @brief Prepare HTTP post message * @param urlbuf Pointer to c-string URL folder * @param hoststr Pointer to c-string hostname * @param additionalheaderline Pointer to c-string with additional HTTP header info * @param postval Pointer to c-string HTML Post value * @param callback Pointer to callback function to handle response * @note Request sent in main packetloop */ static void httpPost (const char *urlbuf, const char *hoststr, const char *additionalheaderline, const char *postval, void (*callback)(uint8_t,uint16_t,uint16_t)); /** @brief Send NTP request * @param ntpip IP address of NTP server * @param srcport IP port to send from */ static void ntpRequest (uint8_t *ntpip,uint8_t srcport); /** @brief Process network time protocol response * @param time Pointer to integer to hold result * @param dstport_l Destination port to expect response. Set to zero to accept on any port * @return <i>uint8_t</i> True (1) on success */ static uint8_t ntpProcessAnswer (uint32_t *time, uint8_t dstport_l); /** @brief Prepare a UDP message for transmission * @param sport Source port * @param dip Pointer to 4 byte destination IP address * @param dport Destination port */ static void udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport); /** @brief Transmit UDP packet * @param len Size of payload */ static void udpTransmit (uint16_t len); /** @brief Sends a UDP packet * @param data Pointer to data * @param len Size of payload (maximum 220 octets / bytes) * @param sport Source port * @param dip Pointer to 4 byte destination IP address * @param dport Destination port */ static void sendUdp (const char *data, uint8_t len, uint16_t sport, const uint8_t *dip, uint16_t dport); /** @brief Resister the function to handle ping events * @param cb Pointer to function */ static void registerPingCallback (void (*cb)(uint8_t*)); /** @brief Send ping * @param destip Ponter to 4 byte destination IP address */ static void clientIcmpRequest (const uint8_t *destip); /** @brief Check for ping response * @param ip_monitoredhost Pointer to 4 byte IP address of host to check * @return <i>uint8_t</i> True (1) if ping response from specified host */ static uint8_t packetLoopIcmpCheckReply (const uint8_t *ip_monitoredhost); /** @brief Send a wake on lan message * @param wolmac Pointer to 6 byte hardware (MAC) address of host to send message to */ static void sendWol (uint8_t *wolmac); // new stash-based API /** @brief Send TCP request */ static uint8_t tcpSend (); /** @brief Get TCP reply * @return <i>char*</i> Pointer to TCP reply payload. NULL if no data. */ static const char* tcpReply (uint8_t fd); /** @brief Configure TCP connections to be persistent or not * @param persist True to maintain TCP connection. False to finish TCP connection after first packet. */ static void persistTcpConnection(bool persist); //udpserver.cpp /** @brief Register function to handle incomint UDP events * @param callback Function to handle event * @param port Port to listen on */ static void udpServerListenOnPort(UdpServerCallback callback, uint16_t port); /** @brief Pause listing on UDP port * @brief port Port to pause */ static void udpServerPauseListenOnPort(uint16_t port); /** @brief Resume listing on UDP port * @brief port Port to pause */ static void udpServerResumeListenOnPort(uint16_t port); /** @brief Check if UDP server is listening on any ports * @return <i>bool</i> True if listening on any ports */ static bool udpServerListening(); //called by tcpip, in packetLoop /** @brief Passes packet to UDP Server * @param len Not used * @return <i>bool</i> True if packet processed */ static bool udpServerHasProcessedPacket(uint16_t len); //called by tcpip, in packetLoop // dhcp.cpp /** @brief Update DHCP state * @param len Length of received data packet */ static void DhcpStateMachine(uint16_t len); /** @brief Not implemented * @todo Implement dhcpStartTime or remove declaration */ static uint32_t dhcpStartTime (); /** @brief Not implemented * @todo Implement dhcpLeaseTime or remove declaration */ static uint32_t dhcpLeaseTime (); /** @brief Not implemented * @todo Implement dhcpLease or remove declaration */ static bool dhcpLease (); /** @brief Configure network interface with DHCP * @param hname The hostname to pass to the DHCP server * @param fromRam Set true to indicate whether hname is in RAM or in program space. Default = false * @return <i>bool</i> True if DHCP successful * @note Blocks until DHCP complete or timeout after 60 seconds */ static bool dhcpSetup (const char *hname = NULL, bool fromRam =false); /** @brief Register a callback for a specific DHCP option number * @param option The option number to request from the DHCP server * @param callback The function to be call when the option is received */ static void dhcpAddOptionCallback(uint8_t option, DhcpOptionCallback callback); // dns.cpp /** @brief Perform DNS lookup * @param name Host name to lookup * @param fromRam Set true to indicate whether name is in RAM or in program space. Default = false * @return <i>bool</i> True on success. * @note Result is stored in <i>hisip</i> member */ static bool dnsLookup (const char* name, bool fromRam =false); // webutil.cpp /** @brief Copies an IP address * @param dst Pointer to the 4 byte destination * @param src Pointer to the 4 byte source * @note There is no check of source or destination size. Ensure both are 4 bytes */ static void copyIp (uint8_t *dst, const uint8_t *src); /** @brief Copies a hardware address * @param dst Pointer to the 6 byte destination * @param src Pointer to the 6 byte destination * @note There is no check of source or destination size. Ensure both are 6 bytes */ static void copyMac (uint8_t *dst, const uint8_t *src); /** @brief Output to serial port in dotted decimal IP format * @param buf Pointer to 4 byte IP address * @note There is no check of source or destination size. Ensure both are 4 bytes */ static void printIp (const uint8_t *buf); /** @brief Output message and IP address to serial port in dotted decimal IP format * @param msg Pointer to null terminated string * @param buf Pointer to 4 byte IP address * @note There is no check of source or destination size. Ensure both are 4 bytes */ static void printIp (const char* msg, const uint8_t *buf); /** @brief Output Flash String Helper and IP address to serial port in dotted decimal IP format * @param ifsh Pointer to Flash String Helper * @param buf Pointer to 4 byte IP address * @note There is no check of source or destination size. Ensure both are 4 bytes * @todo What is a FlashStringHelper? */ static void printIp (const __FlashStringHelper *ifsh, const uint8_t *buf); /** @brief Search for a string of the form key=value in a string that looks like q?xyz=abc&uvw=defgh HTTP/1.1\\r\\n * @param str Pointer to the null terminated string to search * @param strbuf Pointer to buffer to hold null terminated result string * @param maxlen Maximum length of result * @param key Pointer to null terminated string holding the key to search for * @return <i>unit_t</i> Length of the value. 0 if not found * @note Ensure strbuf has memory allocated of at least maxlen + 1 (to accomodate result plus terminating null) */ static uint8_t findKeyVal(const char *str,char *strbuf, uint8_t maxlen, const char *key); /** @brief Decode a URL string e.g "hello%20joe" or "hello+joe" becomes "hello joe" * @param urlbuf Pointer to the null terminated URL * @note urlbuf is modified */ static void urlDecode(char *urlbuf); /** @brief Encode a URL, replacing illegal charaters like ' ' * @param str Pointer to the null terminated string to encode * @param urlbuf Pointer to a buffer to contain the null terminated encoded URL * @note There must be enough space in urlbuf. In the worst case that is 3 times the length of str */ static void urlEncode(char *str,char *urlbuf); /** @brief Convert an IP address from dotted decimal formated string to 4 bytes * @param bytestr Pointer to the 4 byte array to store IP address * @param str Pointer to string to parse * @return <i>uint8_t</i> 0 on success */ static uint8_t parseIp(uint8_t *bytestr,char *str); /** @brief Convert a byte array to a human readable display string * @param resultstr Pointer to a buffer to hold the resulting null terminated string * @param bytestr Pointer to the byte array containing the address to convert * @param len Length of the array (4 for IP address, 6 for hardware (MAC) address) * @param separator Delimiter character (typically '.' for IP address and ':' for hardware (MAC) address) * @param base Base for numerical representation (typically 10 for IP address and 16 for hardware (MAC) address */ static void makeNetStr(char *resultstr,uint8_t *bytestr,uint8_t len, char separator,uint8_t base); /** @brief Return the sequence number of the current TCP package */ static uint32_t getSequenceNumber(); /** @brief Return the payload length of the current Tcp package */ static uint16_t getTcpPayloadLength(); }; extern EtherCard ether; //!< Global presentation of EtherCard class #endif
net.h
enc28j60.cpp
EtherCard.cpp
) { #ifdef __AVR__ uint16_t argval = va_arg(ap, uint16_t), arglen = 0; #else uint32_t argval = va_arg(ap, int), arglen = 0; #endif switch (pgm_read_byte(fmt++)) { case ‘D’: { char buf[7]; wtoa(argval, buf); arglen = strlen(buf); break; } case ‘S’: arglen = strlen((const char*) argval); break; case ‘F’: arglen = strlen_P((const char*) argval); break; case ‘E’: { byte* s = (byte*) argval; char d; while ((d = eeprom_read_byte(s++)) != 0) ++arglen; break; } case ‘H’: { Stash stash (argval); arglen = stash.size(); break; } } #ifdef __AVR__ *segs++ = argval; #else *segs++ = argval; *segs++ = argval >> 16; #endif Stash::bufs[WRITEBUF].words[0] += arglen – 2; } } va_end(ap); } uint16_t Stash::length () { Stash::load(WRITEBUF, 0); return Stash::bufs[WRITEBUF].words[0]; } void Stash::extract (uint16_t offset, uint16_t count, void* buf) { Stash::load(WRITEBUF, 0); uint16_t* segs = Stash::bufs[WRITEBUF].words; #ifdef __AVR__ const char* fmt PROGMEM = (const char*) *++segs; #else const char* fmt PROGMEM = (const char*)((segs[2] << 16) | segs[1]); segs += 2; #endif Stash stash; char mode = ‘@’, tmp[7], *ptr = NULL, *out = (char*) buf; for (uint16_t i = 0; i < offset + count; ) { char c = 0; switch (mode) { case ‘@’: { c = pgm_read_byte(fmt++); if (c == 0) return; if (c != ‘
* * ¤ _Available only if FLOATEMIT is defined_ * * # Examples * ~~~~~~~~~~~~~{.c} * uint16_t ddd = 123; * double ttt = 1.23; * uint16_t hhh = 0xa4; * long lll = 123456789; * char * sss; * char fff[] PROGMEM = “MyMemory”; * * sss[0] = ‘G’; * sss[1] = ‘P’; * sss[2] = ‘L’; * sss[3] = 0; * buf.emit_p( PSTR(“ddd=$D\n”), ddd ); // “ddd=123\n” * buf.emit_p( PSTR(“ttt=$T\n”), ttt ); // “ttt=1.23\n” **TO CHECK** * buf.emit_p( PSTR(“hhh=$H\n”), hhh ); // “hhh=a4\n” * buf.emit_p( PSTR(“lll=$L\n”), lll ); // “lll=123456789\n” * buf.emit_p( PSTR(“sss=$S\n”), sss ); // “sss=GPL\n” * buf.emit_p( PSTR(“fff=$F\n”), fff ); // “fff=MyMemory\n” * ~~~~~~~~~~~~~ * */ class BufferFiller : public Print { uint8_t *start; //!< Pointer to start of buffer uint8_t *ptr; //!< Pointer to cursor position public: /** @brief Empty constructor */ BufferFiller () {} /** @brief Constructor * @param buf Pointer to the ethernet data buffer */ BufferFiller (uint8_t* buf) : start (buf), ptr (buf) {} /** @brief Add formatted text to buffer * @param fmt Format string (see Class description) * @param … parameters for format string */ void emit_p (const char* fmt PROGMEM, …); /** @brief Add data to buffer from main memory * @param s Pointer to data * @param n Number of characters to copy */ void emit_raw (const char* s, uint16_t n) { memcpy(ptr, s, n); ptr += n; } /** @brief Add data to buffer from program space string * @param p Program space string pointer * @param n Number of characters to copy */ void emit_raw_p (const char* p PROGMEM, uint16_t n) { memcpy_P(ptr, p, n); ptr += n; } /** @brief Get pointer to start of buffer * @return <i>uint8_t*</i> Pointer to start of buffer */ uint8_t* buffer () const { return start; } /** @brief Get cursor position * @return <i>uint16_t</i> Cursor postion */ uint16_t position () const { return ptr – start; } /** @brief Write one byte to buffer * @param v Byte to add to buffer */ virtual WRITE_RESULT write (uint8_t v) { *ptr++ = v; WRITE_RETURN } }; /** This class provides the main interface to a ENC28J60 based network interface card and is the class most users will use. * @note All TCP/IP client (outgoing) connections are made from source port in range 2816-3071. Do not use these source ports for other purposes. */ class EtherCard : public Ethernet { public: static uint8_t mymac[ETH_LEN]; ///< MAC address static uint8_t myip[IP_LEN]; ///< IP address static uint8_t netmask[IP_LEN]; ///< Netmask static uint8_t broadcastip[IP_LEN]; ///< Subnet broadcast address static uint8_t gwip[IP_LEN]; ///< Gateway static uint8_t dhcpip[IP_LEN]; ///< DHCP server IP address static uint8_t dnsip[IP_LEN]; ///< DNS server IP address static uint8_t hisip[IP_LEN]; ///< DNS lookup result static uint16_t hisport; ///< TCP port to connect to (default 80) static bool using_dhcp; ///< True if using DHCP static bool persist_tcp_connection; ///< False to break connections on first packet received static uint16_t delaycnt; ///< Counts number of cycles of packetLoop when no packet received – used to trigger periodic gateway ARP request // EtherCard.cpp /** @brief Initialise the network interface * @param size Size of data buffer * @param macaddr Hardware address to assign to the network interface (6 bytes) * @param csPin Arduino pin number connected to chip select. Default = 8 * @return <i>uint8_t</i> Firmware version or zero on failure. */ static uint8_t begin (const uint16_t size, const uint8_t* macaddr, uint8_t csPin = SS); /** @brief Configure network interface with static IP * @param my_ip IP address (4 bytes). 0 for no change. * @param gw_ip Gateway address (4 bytes). 0 for no change. Default = 0 * @param dns_ip DNS address (4 bytes). 0 for no change. Default = 0 * @param mask Subnet mask (4 bytes). 0 for no change. Default = 0 * @return <i>bool</i> Returns true on success – actually always true */ static bool staticSetup (const uint8_t* my_ip, const uint8_t* gw_ip = 0, const uint8_t* dns_ip = 0, const uint8_t* mask = 0); // tcpip.cpp /** @brief Sends a UDP packet to the IP address of last processed received packet * @param data Pointer to data payload * @param len Size of data payload (max 220) * @param port Source IP port */ static void makeUdpReply (const char *data, uint8_t len, uint16_t port); /** @brief Parse received data * @param plen Size of data to parse (e.g. return value of packetReceive()). * @return <i>uint16_t</i> Offset of TCP payload data in data buffer or zero if packet processed * @note Data buffer is shared by receive and transmit functions * @note Only handles ARP and IP */ static uint16_t packetLoop (uint16_t plen); /** @brief Accept a TCP/IP connection * @param port IP port to accept on – do nothing if wrong port * @param plen Number of bytes in packet * @return <i>uint16_t</i> Offset within packet of TCP payload. Zero for no data. */ static uint16_t accept (uint16_t port, uint16_t plen); /** @brief Send a response to a HTTP request * @param dlen Size of the HTTP (TCP) payload */ static void httpServerReply (uint16_t dlen); /** @brief Send a response to a HTTP request * @param dlen Size of the HTTP (TCP) payload * @param flags TCP flags */ static void httpServerReply_with_flags (uint16_t dlen , uint8_t flags); /** @brief Acknowledge TCP message * @todo Is this / should this be private? */ static void httpServerReplyAck (); /** @brief Set the gateway address * @param gwipaddr Gateway address (4 bytes) */ static void setGwIp (const uint8_t *gwipaddr); /** @brief Updates the broadcast address based on current IP address and subnet mask */ static void updateBroadcastAddress(); /** @brief Check if got gateway hardware address (ARP lookup) * @return <i>unit8_t</i> True if gateway found */ static uint8_t clientWaitingGw (); /** @brief Check if got gateway DNS address (ARP lookup) * @return <i>unit8_t</i> True if DNS found */ static uint8_t clientWaitingDns (); /** @brief Prepare a TCP request * @param result_cb Pointer to callback function that handles TCP result * @param datafill_cb Pointer to callback function that handles TCP data payload * @param port Remote TCP/IP port to connect to * @return <i>unit8_t</i> ID of TCP/IP session (0-7) * @note Return value provides id of the request to allow up to 7 concurrent requests */ static uint8_t clientTcpReq (uint8_t (*result_cb)(uint8_t,uint8_t,uint16_t,uint16_t), uint16_t (*datafill_cb)(uint8_t),uint16_t port); /** @brief Prepare HTTP request * @param urlbuf Pointer to c-string URL folder * @param urlbuf_varpart Pointer to c-string URL file * @param hoststr Pointer to c-string hostname * @param additionalheaderline Pointer to c-string with additional HTTP header info * @param callback Pointer to callback function to handle response * @note Request sent in main packetloop */ static void browseUrl (const char *urlbuf, const char *urlbuf_varpart, const char *hoststr, const char *additionalheaderline, void (*callback)(uint8_t,uint16_t,uint16_t)); /** @brief Prepare HTTP request * @param urlbuf Pointer to c-string URL folder * @param urlbuf_varpart Pointer to c-string URL file * @param hoststr Pointer to c-string hostname * @param callback Pointer to callback function to handle response * @note Request sent in main packetloop */ static void browseUrl (const char *urlbuf, const char *urlbuf_varpart, const char *hoststr, void (*callback)(uint8_t,uint16_t,uint16_t)); /** @brief Prepare HTTP post message * @param urlbuf Pointer to c-string URL folder * @param hoststr Pointer to c-string hostname * @param additionalheaderline Pointer to c-string with additional HTTP header info * @param postval Pointer to c-string HTML Post value * @param callback Pointer to callback function to handle response * @note Request sent in main packetloop */ static void httpPost (const char *urlbuf, const char *hoststr, const char *additionalheaderline, const char *postval, void (*callback)(uint8_t,uint16_t,uint16_t)); /** @brief Send NTP request * @param ntpip IP address of NTP server * @param srcport IP port to send from */ static void ntpRequest (uint8_t *ntpip,uint8_t srcport); /** @brief Process network time protocol response * @param time Pointer to integer to hold result * @param dstport_l Destination port to expect response. Set to zero to accept on any port * @return <i>uint8_t</i> True (1) on success */ static uint8_t ntpProcessAnswer (uint32_t *time, uint8_t dstport_l); /** @brief Prepare a UDP message for transmission * @param sport Source port * @param dip Pointer to 4 byte destination IP address * @param dport Destination port */ static void udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport); /** @brief Transmit UDP packet * @param len Size of payload */ static void udpTransmit (uint16_t len); /** @brief Sends a UDP packet * @param data Pointer to data * @param len Size of payload (maximum 220 octets / bytes) * @param sport Source port * @param dip Pointer to 4 byte destination IP address * @param dport Destination port */ static void sendUdp (const char *data, uint8_t len, uint16_t sport, const uint8_t *dip, uint16_t dport); /** @brief Resister the function to handle ping events * @param cb Pointer to function */ static void registerPingCallback (void (*cb)(uint8_t*)); /** @brief Send ping * @param destip Ponter to 4 byte destination IP address */ static void clientIcmpRequest (const uint8_t *destip); /** @brief Check for ping response * @param ip_monitoredhost Pointer to 4 byte IP address of host to check * @return <i>uint8_t</i> True (1) if ping response from specified host */ static uint8_t packetLoopIcmpCheckReply (const uint8_t *ip_monitoredhost); /** @brief Send a wake on lan message * @param wolmac Pointer to 6 byte hardware (MAC) address of host to send message to */ static void sendWol (uint8_t *wolmac); // new stash-based API /** @brief Send TCP request */ static uint8_t tcpSend (); /** @brief Get TCP reply * @return <i>char*</i> Pointer to TCP reply payload. NULL if no data. */ static const char* tcpReply (uint8_t fd); /** @brief Configure TCP connections to be persistent or not * @param persist True to maintain TCP connection. False to finish TCP connection after first packet. */ static void persistTcpConnection(bool persist); //udpserver.cpp /** @brief Register function to handle incomint UDP events * @param callback Function to handle event * @param port Port to listen on */ static void udpServerListenOnPort(UdpServerCallback callback, uint16_t port); /** @brief Pause listing on UDP port * @brief port Port to pause */ static void udpServerPauseListenOnPort(uint16_t port); /** @brief Resume listing on UDP port * @brief port Port to pause */ static void udpServerResumeListenOnPort(uint16_t port); /** @brief Check if UDP server is listening on any ports * @return <i>bool</i> True if listening on any ports */ static bool udpServerListening(); //called by tcpip, in packetLoop /** @brief Passes packet to UDP Server * @param len Not used * @return <i>bool</i> True if packet processed */ static bool udpServerHasProcessedPacket(uint16_t len); //called by tcpip, in packetLoop // dhcp.cpp /** @brief Update DHCP state * @param len Length of received data packet */ static void DhcpStateMachine(uint16_t len); /** @brief Not implemented * @todo Implement dhcpStartTime or remove declaration */ static uint32_t dhcpStartTime (); /** @brief Not implemented * @todo Implement dhcpLeaseTime or remove declaration */ static uint32_t dhcpLeaseTime (); /** @brief Not implemented * @todo Implement dhcpLease or remove declaration */ static bool dhcpLease (); /** @brief Configure network interface with DHCP * @param hname The hostname to pass to the DHCP server * @param fromRam Set true to indicate whether hname is in RAM or in program space. Default = false * @return <i>bool</i> True if DHCP successful * @note Blocks until DHCP complete or timeout after 60 seconds */ static bool dhcpSetup (const char *hname = NULL, bool fromRam =false); /** @brief Register a callback for a specific DHCP option number * @param option The option number to request from the DHCP server * @param callback The function to be call when the option is received */ static void dhcpAddOptionCallback(uint8_t option, DhcpOptionCallback callback); // dns.cpp /** @brief Perform DNS lookup * @param name Host name to lookup * @param fromRam Set true to indicate whether name is in RAM or in program space. Default = false * @return <i>bool</i> True on success. * @note Result is stored in <i>hisip</i> member */ static bool dnsLookup (const char* name, bool fromRam =false); // webutil.cpp /** @brief Copies an IP address * @param dst Pointer to the 4 byte destination * @param src Pointer to the 4 byte source * @note There is no check of source or destination size. Ensure both are 4 bytes */ static void copyIp (uint8_t *dst, const uint8_t *src); /** @brief Copies a hardware address * @param dst Pointer to the 6 byte destination * @param src Pointer to the 6 byte destination * @note There is no check of source or destination size. Ensure both are 6 bytes */ static void copyMac (uint8_t *dst, const uint8_t *src); /** @brief Output to serial port in dotted decimal IP format * @param buf Pointer to 4 byte IP address * @note There is no check of source or destination size. Ensure both are 4 bytes */ static void printIp (const uint8_t *buf); /** @brief Output message and IP address to serial port in dotted decimal IP format * @param msg Pointer to null terminated string * @param buf Pointer to 4 byte IP address * @note There is no check of source or destination size. Ensure both are 4 bytes */ static void printIp (const char* msg, const uint8_t *buf); /** @brief Output Flash String Helper and IP address to serial port in dotted decimal IP format * @param ifsh Pointer to Flash String Helper * @param buf Pointer to 4 byte IP address * @note There is no check of source or destination size. Ensure both are 4 bytes * @todo What is a FlashStringHelper? */ static void printIp (const __FlashStringHelper *ifsh, const uint8_t *buf); /** @brief Search for a string of the form key=value in a string that looks like q?xyz=abc&uvw=defgh HTTP/1.1\\r\\n * @param str Pointer to the null terminated string to search * @param strbuf Pointer to buffer to hold null terminated result string * @param maxlen Maximum length of result * @param key Pointer to null terminated string holding the key to search for * @return <i>unit_t</i> Length of the value. 0 if not found * @note Ensure strbuf has memory allocated of at least maxlen + 1 (to accomodate result plus terminating null) */ static uint8_t findKeyVal(const char *str,char *strbuf, uint8_t maxlen, const char *key); /** @brief Decode a URL string e.g “hello%20joe” or “hello+joe” becomes “hello joe” * @param urlbuf Pointer to the null terminated URL * @note urlbuf is modified */ static void urlDecode(char *urlbuf); /** @brief Encode a URL, replacing illegal charaters like ‘ ‘ * @param str Pointer to the null terminated string to encode * @param urlbuf Pointer to a buffer to contain the null terminated encoded URL * @note There must be enough space in urlbuf. In the worst case that is 3 times the length of str */ static void urlEncode(char *str,char *urlbuf); /** @brief Convert an IP address from dotted decimal formated string to 4 bytes * @param bytestr Pointer to the 4 byte array to store IP address * @param str Pointer to string to parse * @return <i>uint8_t</i> 0 on success */ static uint8_t parseIp(uint8_t *bytestr,char *str); /** @brief Convert a byte array to a human readable display string * @param resultstr Pointer to a buffer to hold the resulting null terminated string * @param bytestr Pointer to the byte array containing the address to convert * @param len Length of the array (4 for IP address, 6 for hardware (MAC) address) * @param separator Delimiter character (typically ‘.’ for IP address and ‘:’ for hardware (MAC) address) * @param base Base for numerical representation (typically 10 for IP address and 16 for hardware (MAC) address */ static void makeNetStr(char *resultstr,uint8_t *bytestr,uint8_t len, char separator,uint8_t base); /** @brief Return the sequence number of the current TCP package */ static uint32_t getSequenceNumber(); /** @brief Return the payload length of the current Tcp package */ static uint16_t getTcpPayloadLength(); }; extern EtherCard ether; //!< Global presentation of EtherCard class #endif
net.h
enc28j60.cpp
EtherCard.cpp
) break; #ifdef __AVR__ uint16_t arg = *++segs; #else uint32_t arg = *++segs; arg |= *++segs << 16; #endif mode = pgm_read_byte(fmt++); switch (mode) { case ‘D’: wtoa(arg, tmp); ptr = tmp; break; case ‘S’: case ‘F’: case ‘E’: ptr = (char*) arg; break; case ‘H’: stash.open(arg); ptr = (char*) &stash; break; } continue; } case ‘D’: case ‘S’: c = *ptr++; break; case ‘F’: c = pgm_read_byte(ptr++); break; case ‘E’: c = eeprom_read_byte((byte*) ptr++); break; case ‘H’: c = ((Stash*) ptr)->get(); break; } if (c == 0) { mode = ‘@’; continue; } if (i >= offset) *out++ = c; ++i; } } void Stash::cleanup () { Stash::load(WRITEBUF, 0); uint16_t* segs = Stash::bufs[WRITEBUF].words; #ifdef __AVR__ const char* fmt PROGMEM = (const char*) *++segs; #else const char* fmt PROGMEM = (const char*)((segs[2] << 16) | segs[1]); segs += 2; #endif for (;;) { char c = pgm_read_byte(fmt++); if (c == 0) break; if (c == ‘
* * ¤ _Available only if FLOATEMIT is defined_ * * # Examples * ~~~~~~~~~~~~~{.c} * uint16_t ddd = 123; * double ttt = 1.23; * uint16_t hhh = 0xa4; * long lll = 123456789; * char * sss; * char fff[] PROGMEM = “MyMemory”; * * sss[0] = ‘G’; * sss[1] = ‘P’; * sss[2] = ‘L’; * sss[3] = 0; * buf.emit_p( PSTR(“ddd=$D\n”), ddd ); // “ddd=123\n” * buf.emit_p( PSTR(“ttt=$T\n”), ttt ); // “ttt=1.23\n” **TO CHECK** * buf.emit_p( PSTR(“hhh=$H\n”), hhh ); // “hhh=a4\n” * buf.emit_p( PSTR(“lll=$L\n”), lll ); // “lll=123456789\n” * buf.emit_p( PSTR(“sss=$S\n”), sss ); // “sss=GPL\n” * buf.emit_p( PSTR(“fff=$F\n”), fff ); // “fff=MyMemory\n” * ~~~~~~~~~~~~~ * */ class BufferFiller : public Print { uint8_t *start; //!< Pointer to start of buffer uint8_t *ptr; //!< Pointer to cursor position public: /** @brief Empty constructor */ BufferFiller () {} /** @brief Constructor * @param buf Pointer to the ethernet data buffer */ BufferFiller (uint8_t* buf) : start (buf), ptr (buf) {} /** @brief Add formatted text to buffer * @param fmt Format string (see Class description) * @param … parameters for format string */ void emit_p (const char* fmt PROGMEM, …); /** @brief Add data to buffer from main memory * @param s Pointer to data * @param n Number of characters to copy */ void emit_raw (const char* s, uint16_t n) { memcpy(ptr, s, n); ptr += n; } /** @brief Add data to buffer from program space string * @param p Program space string pointer * @param n Number of characters to copy */ void emit_raw_p (const char* p PROGMEM, uint16_t n) { memcpy_P(ptr, p, n); ptr += n; } /** @brief Get pointer to start of buffer * @return <i>uint8_t*</i> Pointer to start of buffer */ uint8_t* buffer () const { return start; } /** @brief Get cursor position * @return <i>uint16_t</i> Cursor postion */ uint16_t position () const { return ptr – start; } /** @brief Write one byte to buffer * @param v Byte to add to buffer */ virtual WRITE_RESULT write (uint8_t v) { *ptr++ = v; WRITE_RETURN } }; /** This class provides the main interface to a ENC28J60 based network interface card and is the class most users will use. * @note All TCP/IP client (outgoing) connections are made from source port in range 2816-3071. Do not use these source ports for other purposes. */ class EtherCard : public Ethernet { public: static uint8_t mymac[ETH_LEN]; ///< MAC address static uint8_t myip[IP_LEN]; ///< IP address static uint8_t netmask[IP_LEN]; ///< Netmask static uint8_t broadcastip[IP_LEN]; ///< Subnet broadcast address static uint8_t gwip[IP_LEN]; ///< Gateway static uint8_t dhcpip[IP_LEN]; ///< DHCP server IP address static uint8_t dnsip[IP_LEN]; ///< DNS server IP address static uint8_t hisip[IP_LEN]; ///< DNS lookup result static uint16_t hisport; ///< TCP port to connect to (default 80) static bool using_dhcp; ///< True if using DHCP static bool persist_tcp_connection; ///< False to break connections on first packet received static uint16_t delaycnt; ///< Counts number of cycles of packetLoop when no packet received – used to trigger periodic gateway ARP request // EtherCard.cpp /** @brief Initialise the network interface * @param size Size of data buffer * @param macaddr Hardware address to assign to the network interface (6 bytes) * @param csPin Arduino pin number connected to chip select. Default = 8 * @return <i>uint8_t</i> Firmware version or zero on failure. */ static uint8_t begin (const uint16_t size, const uint8_t* macaddr, uint8_t csPin = SS); /** @brief Configure network interface with static IP * @param my_ip IP address (4 bytes). 0 for no change. * @param gw_ip Gateway address (4 bytes). 0 for no change. Default = 0 * @param dns_ip DNS address (4 bytes). 0 for no change. Default = 0 * @param mask Subnet mask (4 bytes). 0 for no change. Default = 0 * @return <i>bool</i> Returns true on success – actually always true */ static bool staticSetup (const uint8_t* my_ip, const uint8_t* gw_ip = 0, const uint8_t* dns_ip = 0, const uint8_t* mask = 0); // tcpip.cpp /** @brief Sends a UDP packet to the IP address of last processed received packet * @param data Pointer to data payload * @param len Size of data payload (max 220) * @param port Source IP port */ static void makeUdpReply (const char *data, uint8_t len, uint16_t port); /** @brief Parse received data * @param plen Size of data to parse (e.g. return value of packetReceive()). * @return <i>uint16_t</i> Offset of TCP payload data in data buffer or zero if packet processed * @note Data buffer is shared by receive and transmit functions * @note Only handles ARP and IP */ static uint16_t packetLoop (uint16_t plen); /** @brief Accept a TCP/IP connection * @param port IP port to accept on – do nothing if wrong port * @param plen Number of bytes in packet * @return <i>uint16_t</i> Offset within packet of TCP payload. Zero for no data. */ static uint16_t accept (uint16_t port, uint16_t plen); /** @brief Send a response to a HTTP request * @param dlen Size of the HTTP (TCP) payload */ static void httpServerReply (uint16_t dlen); /** @brief Send a response to a HTTP request * @param dlen Size of the HTTP (TCP) payload * @param flags TCP flags */ static void httpServerReply_with_flags (uint16_t dlen , uint8_t flags); /** @brief Acknowledge TCP message * @todo Is this / should this be private? */ static void httpServerReplyAck (); /** @brief Set the gateway address * @param gwipaddr Gateway address (4 bytes) */ static void setGwIp (const uint8_t *gwipaddr); /** @brief Updates the broadcast address based on current IP address and subnet mask */ static void updateBroadcastAddress(); /** @brief Check if got gateway hardware address (ARP lookup) * @return <i>unit8_t</i> True if gateway found */ static uint8_t clientWaitingGw (); /** @brief Check if got gateway DNS address (ARP lookup) * @return <i>unit8_t</i> True if DNS found */ static uint8_t clientWaitingDns (); /** @brief Prepare a TCP request * @param result_cb Pointer to callback function that handles TCP result * @param datafill_cb Pointer to callback function that handles TCP data payload * @param port Remote TCP/IP port to connect to * @return <i>unit8_t</i> ID of TCP/IP session (0-7) * @note Return value provides id of the request to allow up to 7 concurrent requests */ static uint8_t clientTcpReq (uint8_t (*result_cb)(uint8_t,uint8_t,uint16_t,uint16_t), uint16_t (*datafill_cb)(uint8_t),uint16_t port); /** @brief Prepare HTTP request * @param urlbuf Pointer to c-string URL folder * @param urlbuf_varpart Pointer to c-string URL file * @param hoststr Pointer to c-string hostname * @param additionalheaderline Pointer to c-string with additional HTTP header info * @param callback Pointer to callback function to handle response * @note Request sent in main packetloop */ static void browseUrl (const char *urlbuf, const char *urlbuf_varpart, const char *hoststr, const char *additionalheaderline, void (*callback)(uint8_t,uint16_t,uint16_t)); /** @brief Prepare HTTP request * @param urlbuf Pointer to c-string URL folder * @param urlbuf_varpart Pointer to c-string URL file * @param hoststr Pointer to c-string hostname * @param callback Pointer to callback function to handle response * @note Request sent in main packetloop */ static void browseUrl (const char *urlbuf, const char *urlbuf_varpart, const char *hoststr, void (*callback)(uint8_t,uint16_t,uint16_t)); /** @brief Prepare HTTP post message * @param urlbuf Pointer to c-string URL folder * @param hoststr Pointer to c-string hostname * @param additionalheaderline Pointer to c-string with additional HTTP header info * @param postval Pointer to c-string HTML Post value * @param callback Pointer to callback function to handle response * @note Request sent in main packetloop */ static void httpPost (const char *urlbuf, const char *hoststr, const char *additionalheaderline, const char *postval, void (*callback)(uint8_t,uint16_t,uint16_t)); /** @brief Send NTP request * @param ntpip IP address of NTP server * @param srcport IP port to send from */ static void ntpRequest (uint8_t *ntpip,uint8_t srcport); /** @brief Process network time protocol response * @param time Pointer to integer to hold result * @param dstport_l Destination port to expect response. Set to zero to accept on any port * @return <i>uint8_t</i> True (1) on success */ static uint8_t ntpProcessAnswer (uint32_t *time, uint8_t dstport_l); /** @brief Prepare a UDP message for transmission * @param sport Source port * @param dip Pointer to 4 byte destination IP address * @param dport Destination port */ static void udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport); /** @brief Transmit UDP packet * @param len Size of payload */ static void udpTransmit (uint16_t len); /** @brief Sends a UDP packet * @param data Pointer to data * @param len Size of payload (maximum 220 octets / bytes) * @param sport Source port * @param dip Pointer to 4 byte destination IP address * @param dport Destination port */ static void sendUdp (const char *data, uint8_t len, uint16_t sport, const uint8_t *dip, uint16_t dport); /** @brief Resister the function to handle ping events * @param cb Pointer to function */ static void registerPingCallback (void (*cb)(uint8_t*)); /** @brief Send ping * @param destip Ponter to 4 byte destination IP address */ static void clientIcmpRequest (const uint8_t *destip); /** @brief Check for ping response * @param ip_monitoredhost Pointer to 4 byte IP address of host to check * @return <i>uint8_t</i> True (1) if ping response from specified host */ static uint8_t packetLoopIcmpCheckReply (const uint8_t *ip_monitoredhost); /** @brief Send a wake on lan message * @param wolmac Pointer to 6 byte hardware (MAC) address of host to send message to */ static void sendWol (uint8_t *wolmac); // new stash-based API /** @brief Send TCP request */ static uint8_t tcpSend (); /** @brief Get TCP reply * @return <i>char*</i> Pointer to TCP reply payload. NULL if no data. */ static const char* tcpReply (uint8_t fd); /** @brief Configure TCP connections to be persistent or not * @param persist True to maintain TCP connection. False to finish TCP connection after first packet. */ static void persistTcpConnection(bool persist); //udpserver.cpp /** @brief Register function to handle incomint UDP events * @param callback Function to handle event * @param port Port to listen on */ static void udpServerListenOnPort(UdpServerCallback callback, uint16_t port); /** @brief Pause listing on UDP port * @brief port Port to pause */ static void udpServerPauseListenOnPort(uint16_t port); /** @brief Resume listing on UDP port * @brief port Port to pause */ static void udpServerResumeListenOnPort(uint16_t port); /** @brief Check if UDP server is listening on any ports * @return <i>bool</i> True if listening on any ports */ static bool udpServerListening(); //called by tcpip, in packetLoop /** @brief Passes packet to UDP Server * @param len Not used * @return <i>bool</i> True if packet processed */ static bool udpServerHasProcessedPacket(uint16_t len); //called by tcpip, in packetLoop // dhcp.cpp /** @brief Update DHCP state * @param len Length of received data packet */ static void DhcpStateMachine(uint16_t len); /** @brief Not implemented * @todo Implement dhcpStartTime or remove declaration */ static uint32_t dhcpStartTime (); /** @brief Not implemented * @todo Implement dhcpLeaseTime or remove declaration */ static uint32_t dhcpLeaseTime (); /** @brief Not implemented * @todo Implement dhcpLease or remove declaration */ static bool dhcpLease (); /** @brief Configure network interface with DHCP * @param hname The hostname to pass to the DHCP server * @param fromRam Set true to indicate whether hname is in RAM or in program space. Default = false * @return <i>bool</i> True if DHCP successful * @note Blocks until DHCP complete or timeout after 60 seconds */ static bool dhcpSetup (const char *hname = NULL, bool fromRam =false); /** @brief Register a callback for a specific DHCP option number * @param option The option number to request from the DHCP server * @param callback The function to be call when the option is received */ static void dhcpAddOptionCallback(uint8_t option, DhcpOptionCallback callback); // dns.cpp /** @brief Perform DNS lookup * @param name Host name to lookup * @param fromRam Set true to indicate whether name is in RAM or in program space. Default = false * @return <i>bool</i> True on success. * @note Result is stored in <i>hisip</i> member */ static bool dnsLookup (const char* name, bool fromRam =false); // webutil.cpp /** @brief Copies an IP address * @param dst Pointer to the 4 byte destination * @param src Pointer to the 4 byte source * @note There is no check of source or destination size. Ensure both are 4 bytes */ static void copyIp (uint8_t *dst, const uint8_t *src); /** @brief Copies a hardware address * @param dst Pointer to the 6 byte destination * @param src Pointer to the 6 byte destination * @note There is no check of source or destination size. Ensure both are 6 bytes */ static void copyMac (uint8_t *dst, const uint8_t *src); /** @brief Output to serial port in dotted decimal IP format * @param buf Pointer to 4 byte IP address * @note There is no check of source or destination size. Ensure both are 4 bytes */ static void printIp (const uint8_t *buf); /** @brief Output message and IP address to serial port in dotted decimal IP format * @param msg Pointer to null terminated string * @param buf Pointer to 4 byte IP address * @note There is no check of source or destination size. Ensure both are 4 bytes */ static void printIp (const char* msg, const uint8_t *buf); /** @brief Output Flash String Helper and IP address to serial port in dotted decimal IP format * @param ifsh Pointer to Flash String Helper * @param buf Pointer to 4 byte IP address * @note There is no check of source or destination size. Ensure both are 4 bytes * @todo What is a FlashStringHelper? */ static void printIp (const __FlashStringHelper *ifsh, const uint8_t *buf); /** @brief Search for a string of the form key=value in a string that looks like q?xyz=abc&uvw=defgh HTTP/1.1\\r\\n * @param str Pointer to the null terminated string to search * @param strbuf Pointer to buffer to hold null terminated result string * @param maxlen Maximum length of result * @param key Pointer to null terminated string holding the key to search for * @return <i>unit_t</i> Length of the value. 0 if not found * @note Ensure strbuf has memory allocated of at least maxlen + 1 (to accomodate result plus terminating null) */ static uint8_t findKeyVal(const char *str,char *strbuf, uint8_t maxlen, const char *key); /** @brief Decode a URL string e.g “hello%20joe” or “hello+joe” becomes “hello joe” * @param urlbuf Pointer to the null terminated URL * @note urlbuf is modified */ static void urlDecode(char *urlbuf); /** @brief Encode a URL, replacing illegal charaters like ‘ ‘ * @param str Pointer to the null terminated string to encode * @param urlbuf Pointer to a buffer to contain the null terminated encoded URL * @note There must be enough space in urlbuf. In the worst case that is 3 times the length of str */ static void urlEncode(char *str,char *urlbuf); /** @brief Convert an IP address from dotted decimal formated string to 4 bytes * @param bytestr Pointer to the 4 byte array to store IP address * @param str Pointer to string to parse * @return <i>uint8_t</i> 0 on success */ static uint8_t parseIp(uint8_t *bytestr,char *str); /** @brief Convert a byte array to a human readable display string * @param resultstr Pointer to a buffer to hold the resulting null terminated string * @param bytestr Pointer to the byte array containing the address to convert * @param len Length of the array (4 for IP address, 6 for hardware (MAC) address) * @param separator Delimiter character (typically ‘.’ for IP address and ‘:’ for hardware (MAC) address) * @param base Base for numerical representation (typically 10 for IP address and 16 for hardware (MAC) address */ static void makeNetStr(char *resultstr,uint8_t *bytestr,uint8_t len, char separator,uint8_t base); /** @brief Return the sequence number of the current TCP package */ static uint32_t getSequenceNumber(); /** @brief Return the payload length of the current Tcp package */ static uint16_t getTcpPayloadLength(); }; extern EtherCard ether; //!< Global presentation of EtherCard class #endif
net.h
enc28j60.cpp
EtherCard.cpp
) { #ifdef __AVR__ uint16_t arg = *++segs; #else uint32_t arg = *++segs; arg |= *++segs << 16; #endif if (pgm_read_byte(fmt++) == ‘H’) { Stash stash (arg); stash.release(); } } } } void BufferFiller::emit_p(const char* fmt PROGMEM, …) { va_list ap; va_start(ap, fmt); for (;;) { char c = pgm_read_byte(fmt++); if (c == 0) break; if (c != ‘
* * ¤ _Available only if FLOATEMIT is defined_ * * # Examples * ~~~~~~~~~~~~~{.c} * uint16_t ddd = 123; * double ttt = 1.23; * uint16_t hhh = 0xa4; * long lll = 123456789; * char * sss; * char fff[] PROGMEM = “MyMemory”; * * sss[0] = ‘G’; * sss[1] = ‘P’; * sss[2] = ‘L’; * sss[3] = 0; * buf.emit_p( PSTR(“ddd=$D\n”), ddd ); // “ddd=123\n” * buf.emit_p( PSTR(“ttt=$T\n”), ttt ); // “ttt=1.23\n” **TO CHECK** * buf.emit_p( PSTR(“hhh=$H\n”), hhh ); // “hhh=a4\n” * buf.emit_p( PSTR(“lll=$L\n”), lll ); // “lll=123456789\n” * buf.emit_p( PSTR(“sss=$S\n”), sss ); // “sss=GPL\n” * buf.emit_p( PSTR(“fff=$F\n”), fff ); // “fff=MyMemory\n” * ~~~~~~~~~~~~~ * */ class BufferFiller : public Print { uint8_t *start; //!< Pointer to start of buffer uint8_t *ptr; //!< Pointer to cursor position public: /** @brief Empty constructor */ BufferFiller () {} /** @brief Constructor * @param buf Pointer to the ethernet data buffer */ BufferFiller (uint8_t* buf) : start (buf), ptr (buf) {} /** @brief Add formatted text to buffer * @param fmt Format string (see Class description) * @param … parameters for format string */ void emit_p (const char* fmt PROGMEM, …); /** @brief Add data to buffer from main memory * @param s Pointer to data * @param n Number of characters to copy */ void emit_raw (const char* s, uint16_t n) { memcpy(ptr, s, n); ptr += n; } /** @brief Add data to buffer from program space string * @param p Program space string pointer * @param n Number of characters to copy */ void emit_raw_p (const char* p PROGMEM, uint16_t n) { memcpy_P(ptr, p, n); ptr += n; } /** @brief Get pointer to start of buffer * @return <i>uint8_t*</i> Pointer to start of buffer */ uint8_t* buffer () const { return start; } /** @brief Get cursor position * @return <i>uint16_t</i> Cursor postion */ uint16_t position () const { return ptr – start; } /** @brief Write one byte to buffer * @param v Byte to add to buffer */ virtual WRITE_RESULT write (uint8_t v) { *ptr++ = v; WRITE_RETURN } }; /** This class provides the main interface to a ENC28J60 based network interface card and is the class most users will use. * @note All TCP/IP client (outgoing) connections are made from source port in range 2816-3071. Do not use these source ports for other purposes. */ class EtherCard : public Ethernet { public: static uint8_t mymac[ETH_LEN]; ///< MAC address static uint8_t myip[IP_LEN]; ///< IP address static uint8_t netmask[IP_LEN]; ///< Netmask static uint8_t broadcastip[IP_LEN]; ///< Subnet broadcast address static uint8_t gwip[IP_LEN]; ///< Gateway static uint8_t dhcpip[IP_LEN]; ///< DHCP server IP address static uint8_t dnsip[IP_LEN]; ///< DNS server IP address static uint8_t hisip[IP_LEN]; ///< DNS lookup result static uint16_t hisport; ///< TCP port to connect to (default 80) static bool using_dhcp; ///< True if using DHCP static bool persist_tcp_connection; ///< False to break connections on first packet received static uint16_t delaycnt; ///< Counts number of cycles of packetLoop when no packet received – used to trigger periodic gateway ARP request // EtherCard.cpp /** @brief Initialise the network interface * @param size Size of data buffer * @param macaddr Hardware address to assign to the network interface (6 bytes) * @param csPin Arduino pin number connected to chip select. Default = 8 * @return <i>uint8_t</i> Firmware version or zero on failure. */ static uint8_t begin (const uint16_t size, const uint8_t* macaddr, uint8_t csPin = SS); /** @brief Configure network interface with static IP * @param my_ip IP address (4 bytes). 0 for no change. * @param gw_ip Gateway address (4 bytes). 0 for no change. Default = 0 * @param dns_ip DNS address (4 bytes). 0 for no change. Default = 0 * @param mask Subnet mask (4 bytes). 0 for no change. Default = 0 * @return <i>bool</i> Returns true on success – actually always true */ static bool staticSetup (const uint8_t* my_ip, const uint8_t* gw_ip = 0, const uint8_t* dns_ip = 0, const uint8_t* mask = 0); // tcpip.cpp /** @brief Sends a UDP packet to the IP address of last processed received packet * @param data Pointer to data payload * @param len Size of data payload (max 220) * @param port Source IP port */ static void makeUdpReply (const char *data, uint8_t len, uint16_t port); /** @brief Parse received data * @param plen Size of data to parse (e.g. return value of packetReceive()). * @return <i>uint16_t</i> Offset of TCP payload data in data buffer or zero if packet processed * @note Data buffer is shared by receive and transmit functions * @note Only handles ARP and IP */ static uint16_t packetLoop (uint16_t plen); /** @brief Accept a TCP/IP connection * @param port IP port to accept on – do nothing if wrong port * @param plen Number of bytes in packet * @return <i>uint16_t</i> Offset within packet of TCP payload. Zero for no data. */ static uint16_t accept (uint16_t port, uint16_t plen); /** @brief Send a response to a HTTP request * @param dlen Size of the HTTP (TCP) payload */ static void httpServerReply (uint16_t dlen); /** @brief Send a response to a HTTP request * @param dlen Size of the HTTP (TCP) payload * @param flags TCP flags */ static void httpServerReply_with_flags (uint16_t dlen , uint8_t flags); /** @brief Acknowledge TCP message * @todo Is this / should this be private? */ static void httpServerReplyAck (); /** @brief Set the gateway address * @param gwipaddr Gateway address (4 bytes) */ static void setGwIp (const uint8_t *gwipaddr); /** @brief Updates the broadcast address based on current IP address and subnet mask */ static void updateBroadcastAddress(); /** @brief Check if got gateway hardware address (ARP lookup) * @return <i>unit8_t</i> True if gateway found */ static uint8_t clientWaitingGw (); /** @brief Check if got gateway DNS address (ARP lookup) * @return <i>unit8_t</i> True if DNS found */ static uint8_t clientWaitingDns (); /** @brief Prepare a TCP request * @param result_cb Pointer to callback function that handles TCP result * @param datafill_cb Pointer to callback function that handles TCP data payload * @param port Remote TCP/IP port to connect to * @return <i>unit8_t</i> ID of TCP/IP session (0-7) * @note Return value provides id of the request to allow up to 7 concurrent requests */ static uint8_t clientTcpReq (uint8_t (*result_cb)(uint8_t,uint8_t,uint16_t,uint16_t), uint16_t (*datafill_cb)(uint8_t),uint16_t port); /** @brief Prepare HTTP request * @param urlbuf Pointer to c-string URL folder * @param urlbuf_varpart Pointer to c-string URL file * @param hoststr Pointer to c-string hostname * @param additionalheaderline Pointer to c-string with additional HTTP header info * @param callback Pointer to callback function to handle response * @note Request sent in main packetloop */ static void browseUrl (const char *urlbuf, const char *urlbuf_varpart, const char *hoststr, const char *additionalheaderline, void (*callback)(uint8_t,uint16_t,uint16_t)); /** @brief Prepare HTTP request * @param urlbuf Pointer to c-string URL folder * @param urlbuf_varpart Pointer to c-string URL file * @param hoststr Pointer to c-string hostname * @param callback Pointer to callback function to handle response * @note Request sent in main packetloop */ static void browseUrl (const char *urlbuf, const char *urlbuf_varpart, const char *hoststr, void (*callback)(uint8_t,uint16_t,uint16_t)); /** @brief Prepare HTTP post message * @param urlbuf Pointer to c-string URL folder * @param hoststr Pointer to c-string hostname * @param additionalheaderline Pointer to c-string with additional HTTP header info * @param postval Pointer to c-string HTML Post value * @param callback Pointer to callback function to handle response * @note Request sent in main packetloop */ static void httpPost (const char *urlbuf, const char *hoststr, const char *additionalheaderline, const char *postval, void (*callback)(uint8_t,uint16_t,uint16_t)); /** @brief Send NTP request * @param ntpip IP address of NTP server * @param srcport IP port to send from */ static void ntpRequest (uint8_t *ntpip,uint8_t srcport); /** @brief Process network time protocol response * @param time Pointer to integer to hold result * @param dstport_l Destination port to expect response. Set to zero to accept on any port * @return <i>uint8_t</i> True (1) on success */ static uint8_t ntpProcessAnswer (uint32_t *time, uint8_t dstport_l); /** @brief Prepare a UDP message for transmission * @param sport Source port * @param dip Pointer to 4 byte destination IP address * @param dport Destination port */ static void udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport); /** @brief Transmit UDP packet * @param len Size of payload */ static void udpTransmit (uint16_t len); /** @brief Sends a UDP packet * @param data Pointer to data * @param len Size of payload (maximum 220 octets / bytes) * @param sport Source port * @param dip Pointer to 4 byte destination IP address * @param dport Destination port */ static void sendUdp (const char *data, uint8_t len, uint16_t sport, const uint8_t *dip, uint16_t dport); /** @brief Resister the function to handle ping events * @param cb Pointer to function */ static void registerPingCallback (void (*cb)(uint8_t*)); /** @brief Send ping * @param destip Ponter to 4 byte destination IP address */ static void clientIcmpRequest (const uint8_t *destip); /** @brief Check for ping response * @param ip_monitoredhost Pointer to 4 byte IP address of host to check * @return <i>uint8_t</i> True (1) if ping response from specified host */ static uint8_t packetLoopIcmpCheckReply (const uint8_t *ip_monitoredhost); /** @brief Send a wake on lan message * @param wolmac Pointer to 6 byte hardware (MAC) address of host to send message to */ static void sendWol (uint8_t *wolmac); // new stash-based API /** @brief Send TCP request */ static uint8_t tcpSend (); /** @brief Get TCP reply * @return <i>char*</i> Pointer to TCP reply payload. NULL if no data. */ static const char* tcpReply (uint8_t fd); /** @brief Configure TCP connections to be persistent or not * @param persist True to maintain TCP connection. False to finish TCP connection after first packet. */ static void persistTcpConnection(bool persist); //udpserver.cpp /** @brief Register function to handle incomint UDP events * @param callback Function to handle event * @param port Port to listen on */ static void udpServerListenOnPort(UdpServerCallback callback, uint16_t port); /** @brief Pause listing on UDP port * @brief port Port to pause */ static void udpServerPauseListenOnPort(uint16_t port); /** @brief Resume listing on UDP port * @brief port Port to pause */ static void udpServerResumeListenOnPort(uint16_t port); /** @brief Check if UDP server is listening on any ports * @return <i>bool</i> True if listening on any ports */ static bool udpServerListening(); //called by tcpip, in packetLoop /** @brief Passes packet to UDP Server * @param len Not used * @return <i>bool</i> True if packet processed */ static bool udpServerHasProcessedPacket(uint16_t len); //called by tcpip, in packetLoop // dhcp.cpp /** @brief Update DHCP state * @param len Length of received data packet */ static void DhcpStateMachine(uint16_t len); /** @brief Not implemented * @todo Implement dhcpStartTime or remove declaration */ static uint32_t dhcpStartTime (); /** @brief Not implemented * @todo Implement dhcpLeaseTime or remove declaration */ static uint32_t dhcpLeaseTime (); /** @brief Not implemented * @todo Implement dhcpLease or remove declaration */ static bool dhcpLease (); /** @brief Configure network interface with DHCP * @param hname The hostname to pass to the DHCP server * @param fromRam Set true to indicate whether hname is in RAM or in program space. Default = false * @return <i>bool</i> True if DHCP successful * @note Blocks until DHCP complete or timeout after 60 seconds */ static bool dhcpSetup (const char *hname = NULL, bool fromRam =false); /** @brief Register a callback for a specific DHCP option number * @param option The option number to request from the DHCP server * @param callback The function to be call when the option is received */ static void dhcpAddOptionCallback(uint8_t option, DhcpOptionCallback callback); // dns.cpp /** @brief Perform DNS lookup * @param name Host name to lookup * @param fromRam Set true to indicate whether name is in RAM or in program space. Default = false * @return <i>bool</i> True on success. * @note Result is stored in <i>hisip</i> member */ static bool dnsLookup (const char* name, bool fromRam =false); // webutil.cpp /** @brief Copies an IP address * @param dst Pointer to the 4 byte destination * @param src Pointer to the 4 byte source * @note There is no check of source or destination size. Ensure both are 4 bytes */ static void copyIp (uint8_t *dst, const uint8_t *src); /** @brief Copies a hardware address * @param dst Pointer to the 6 byte destination * @param src Pointer to the 6 byte destination * @note There is no check of source or destination size. Ensure both are 6 bytes */ static void copyMac (uint8_t *dst, const uint8_t *src); /** @brief Output to serial port in dotted decimal IP format * @param buf Pointer to 4 byte IP address * @note There is no check of source or destination size. Ensure both are 4 bytes */ static void printIp (const uint8_t *buf); /** @brief Output message and IP address to serial port in dotted decimal IP format * @param msg Pointer to null terminated string * @param buf Pointer to 4 byte IP address * @note There is no check of source or destination size. Ensure both are 4 bytes */ static void printIp (const char* msg, const uint8_t *buf); /** @brief Output Flash String Helper and IP address to serial port in dotted decimal IP format * @param ifsh Pointer to Flash String Helper * @param buf Pointer to 4 byte IP address * @note There is no check of source or destination size. Ensure both are 4 bytes * @todo What is a FlashStringHelper? */ static void printIp (const __FlashStringHelper *ifsh, const uint8_t *buf); /** @brief Search for a string of the form key=value in a string that looks like q?xyz=abc&uvw=defgh HTTP/1.1\\r\\n * @param str Pointer to the null terminated string to search * @param strbuf Pointer to buffer to hold null terminated result string * @param maxlen Maximum length of result * @param key Pointer to null terminated string holding the key to search for * @return <i>unit_t</i> Length of the value. 0 if not found * @note Ensure strbuf has memory allocated of at least maxlen + 1 (to accomodate result plus terminating null) */ static uint8_t findKeyVal(const char *str,char *strbuf, uint8_t maxlen, const char *key); /** @brief Decode a URL string e.g “hello%20joe” or “hello+joe” becomes “hello joe” * @param urlbuf Pointer to the null terminated URL * @note urlbuf is modified */ static void urlDecode(char *urlbuf); /** @brief Encode a URL, replacing illegal charaters like ‘ ‘ * @param str Pointer to the null terminated string to encode * @param urlbuf Pointer to a buffer to contain the null terminated encoded URL * @note There must be enough space in urlbuf. In the worst case that is 3 times the length of str */ static void urlEncode(char *str,char *urlbuf); /** @brief Convert an IP address from dotted decimal formated string to 4 bytes * @param bytestr Pointer to the 4 byte array to store IP address * @param str Pointer to string to parse * @return <i>uint8_t</i> 0 on success */ static uint8_t parseIp(uint8_t *bytestr,char *str); /** @brief Convert a byte array to a human readable display string * @param resultstr Pointer to a buffer to hold the resulting null terminated string * @param bytestr Pointer to the byte array containing the address to convert * @param len Length of the array (4 for IP address, 6 for hardware (MAC) address) * @param separator Delimiter character (typically ‘.’ for IP address and ‘:’ for hardware (MAC) address) * @param base Base for numerical representation (typically 10 for IP address and 16 for hardware (MAC) address */ static void makeNetStr(char *resultstr,uint8_t *bytestr,uint8_t len, char separator,uint8_t base); /** @brief Return the sequence number of the current TCP package */ static uint32_t getSequenceNumber(); /** @brief Return the payload length of the current Tcp package */ static uint16_t getTcpPayloadLength(); }; extern EtherCard ether; //!< Global presentation of EtherCard class #endif
net.h
enc28j60.cpp
EtherCard.cpp
) { *ptr++ = c; continue; } c = pgm_read_byte(fmt++); switch (c) { case ‘D’: #ifdef __AVR__ wtoa(va_arg(ap, uint16_t), (char*) ptr); #else wtoa(va_arg(ap, int), (char*) ptr); #endif break; #ifdef FLOATEMIT case ‘T’: dtostrf ( va_arg(ap, double), 10, 3, (char*)ptr ); break; #endif case ‘H’: { #ifdef __AVR__ char p1 = va_arg(ap, uint16_t); #else char p1 = va_arg(ap, int); #endif char p2; p2 = (p1 >> 4) & 0x0F; p1 = p1 & 0x0F; if (p1 > 9) p1 += 0x07; // adjust 0x0a-0x0f to come out ‘a’-‘f’ p1 += 0x30; // and complete if (p2 > 9) p2 += 0x07; // adjust 0x0a-0x0f to come out ‘a’-‘f’ p2 += 0x30; // and complete *ptr++ = p2; *ptr++ = p1; continue; } case ‘L’: ltoa(va_arg(ap, long), (char*) ptr, 10); break; case ‘S’: strcpy((char*) ptr, va_arg(ap, const char*)); break; case ‘F’: { const char* s PROGMEM = va_arg(ap, const char*); char d; while ((d = pgm_read_byte(s++)) != 0) *ptr++ = d; continue; } case ‘E’: { byte* s = va_arg(ap, byte*); char d; while ((d = eeprom_read_byte(s++)) != 0) *ptr++ = d; continue; } default: *ptr++ = c; continue; } ptr += strlen((char*) ptr); } va_end(ap); } EtherCard ether; uint8_t EtherCard::mymac[ETH_LEN]; // my MAC address uint8_t EtherCard::myip[IP_LEN]; // my ip address uint8_t EtherCard::netmask[IP_LEN]; // subnet mask uint8_t EtherCard::broadcastip[IP_LEN]; // broadcast address uint8_t EtherCard::gwip[IP_LEN]; // gateway uint8_t EtherCard::dhcpip[IP_LEN]; // dhcp server uint8_t EtherCard::dnsip[IP_LEN]; // dns server uint8_t EtherCard::hisip[IP_LEN]; // ip address of remote host uint16_t EtherCard::hisport = HTTP_PORT; // tcp port to browse to bool EtherCard::using_dhcp = false; bool EtherCard::persist_tcp_connection = false; uint16_t EtherCard::delaycnt = 0; //request gateway ARP lookup uint8_t EtherCard::begin (const uint16_t size, const uint8_t* macaddr, uint8_t csPin) { using_dhcp = false; #if ETHERCARD_STASH Stash::initMap(); #endif copyMac(mymac, macaddr); return initialize(size, mymac, csPin); } bool EtherCard::staticSetup (const uint8_t* my_ip, const uint8_t* gw_ip, const uint8_t* dns_ip, const uint8_t* mask) { using_dhcp = false; if (my_ip != 0) copyIp(myip, my_ip); if (gw_ip != 0) setGwIp(gw_ip); if (dns_ip != 0) copyIp(dnsip, dns_ip); if(mask != 0) copyIp(netmask, mask); updateBroadcastAddress(); delaycnt = 0; //request gateway ARP lookup return true; }
* * ¤ _Available only if FLOATEMIT is defined_ * * # Examples * ~~~~~~~~~~~~~{.c} * uint16_t ddd = 123; * double ttt = 1.23; * uint16_t hhh = 0xa4; * long lll = 123456789; * char * sss; * char fff[] PROGMEM = “MyMemory”; * * sss[0] = ‘G’; * sss[1] = ‘P’; * sss[2] = ‘L’; * sss[3] = 0; * buf.emit_p( PSTR(“ddd=$D\n”), ddd ); // “ddd=123\n” * buf.emit_p( PSTR(“ttt=$T\n”), ttt ); // “ttt=1.23\n” **TO CHECK** * buf.emit_p( PSTR(“hhh=$H\n”), hhh ); // “hhh=a4\n” * buf.emit_p( PSTR(“lll=$L\n”), lll ); // “lll=123456789\n” * buf.emit_p( PSTR(“sss=$S\n”), sss ); // “sss=GPL\n” * buf.emit_p( PSTR(“fff=$F\n”), fff ); // “fff=MyMemory\n” * ~~~~~~~~~~~~~ * */ class BufferFiller : public Print { uint8_t *start; //!< Pointer to start of buffer uint8_t *ptr; //!< Pointer to cursor position public: /** @brief Empty constructor */ BufferFiller () {} /** @brief Constructor * @param buf Pointer to the ethernet data buffer */ BufferFiller (uint8_t* buf) : start (buf), ptr (buf) {} /** @brief Add formatted text to buffer * @param fmt Format string (see Class description) * @param … parameters for format string */ void emit_p (const char* fmt PROGMEM, …); /** @brief Add data to buffer from main memory * @param s Pointer to data * @param n Number of characters to copy */ void emit_raw (const char* s, uint16_t n) { memcpy(ptr, s, n); ptr += n; } /** @brief Add data to buffer from program space string * @param p Program space string pointer * @param n Number of characters to copy */ void emit_raw_p (const char* p PROGMEM, uint16_t n) { memcpy_P(ptr, p, n); ptr += n; } /** @brief Get pointer to start of buffer * @return <i>uint8_t*</i> Pointer to start of buffer */ uint8_t* buffer () const { return start; } /** @brief Get cursor position * @return <i>uint16_t</i> Cursor postion */ uint16_t position () const { return ptr – start; } /** @brief Write one byte to buffer * @param v Byte to add to buffer */ virtual WRITE_RESULT write (uint8_t v) { *ptr++ = v; WRITE_RETURN } }; /** This class provides the main interface to a ENC28J60 based network interface card and is the class most users will use. * @note All TCP/IP client (outgoing) connections are made from source port in range 2816-3071. Do not use these source ports for other purposes. */ class EtherCard : public Ethernet { public: static uint8_t mymac[ETH_LEN]; ///< MAC address static uint8_t myip[IP_LEN]; ///< IP address static uint8_t netmask[IP_LEN]; ///< Netmask static uint8_t broadcastip[IP_LEN]; ///< Subnet broadcast address static uint8_t gwip[IP_LEN]; ///< Gateway static uint8_t dhcpip[IP_LEN]; ///< DHCP server IP address static uint8_t dnsip[IP_LEN]; ///< DNS server IP address static uint8_t hisip[IP_LEN]; ///< DNS lookup result static uint16_t hisport; ///< TCP port to connect to (default 80) static bool using_dhcp; ///< True if using DHCP static bool persist_tcp_connection; ///< False to break connections on first packet received static uint16_t delaycnt; ///< Counts number of cycles of packetLoop when no packet received – used to trigger periodic gateway ARP request // EtherCard.cpp /** @brief Initialise the network interface * @param size Size of data buffer * @param macaddr Hardware address to assign to the network interface (6 bytes) * @param csPin Arduino pin number connected to chip select. Default = 8 * @return <i>uint8_t</i> Firmware version or zero on failure. */ static uint8_t begin (const uint16_t size, const uint8_t* macaddr, uint8_t csPin = SS); /** @brief Configure network interface with static IP * @param my_ip IP address (4 bytes). 0 for no change. * @param gw_ip Gateway address (4 bytes). 0 for no change. Default = 0 * @param dns_ip DNS address (4 bytes). 0 for no change. Default = 0 * @param mask Subnet mask (4 bytes). 0 for no change. Default = 0 * @return <i>bool</i> Returns true on success – actually always true */ static bool staticSetup (const uint8_t* my_ip, const uint8_t* gw_ip = 0, const uint8_t* dns_ip = 0, const uint8_t* mask = 0); // tcpip.cpp /** @brief Sends a UDP packet to the IP address of last processed received packet * @param data Pointer to data payload * @param len Size of data payload (max 220) * @param port Source IP port */ static void makeUdpReply (const char *data, uint8_t len, uint16_t port); /** @brief Parse received data * @param plen Size of data to parse (e.g. return value of packetReceive()). * @return <i>uint16_t</i> Offset of TCP payload data in data buffer or zero if packet processed * @note Data buffer is shared by receive and transmit functions * @note Only handles ARP and IP */ static uint16_t packetLoop (uint16_t plen); /** @brief Accept a TCP/IP connection * @param port IP port to accept on – do nothing if wrong port * @param plen Number of bytes in packet * @return <i>uint16_t</i> Offset within packet of TCP payload. Zero for no data. */ static uint16_t accept (uint16_t port, uint16_t plen); /** @brief Send a response to a HTTP request * @param dlen Size of the HTTP (TCP) payload */ static void httpServerReply (uint16_t dlen); /** @brief Send a response to a HTTP request * @param dlen Size of the HTTP (TCP) payload * @param flags TCP flags */ static void httpServerReply_with_flags (uint16_t dlen , uint8_t flags); /** @brief Acknowledge TCP message * @todo Is this / should this be private? */ static void httpServerReplyAck (); /** @brief Set the gateway address * @param gwipaddr Gateway address (4 bytes) */ static void setGwIp (const uint8_t *gwipaddr); /** @brief Updates the broadcast address based on current IP address and subnet mask */ static void updateBroadcastAddress(); /** @brief Check if got gateway hardware address (ARP lookup) * @return <i>unit8_t</i> True if gateway found */ static uint8_t clientWaitingGw (); /** @brief Check if got gateway DNS address (ARP lookup) * @return <i>unit8_t</i> True if DNS found */ static uint8_t clientWaitingDns (); /** @brief Prepare a TCP request * @param result_cb Pointer to callback function that handles TCP result * @param datafill_cb Pointer to callback function that handles TCP data payload * @param port Remote TCP/IP port to connect to * @return <i>unit8_t</i> ID of TCP/IP session (0-7) * @note Return value provides id of the request to allow up to 7 concurrent requests */ static uint8_t clientTcpReq (uint8_t (*result_cb)(uint8_t,uint8_t,uint16_t,uint16_t), uint16_t (*datafill_cb)(uint8_t),uint16_t port); /** @brief Prepare HTTP request * @param urlbuf Pointer to c-string URL folder * @param urlbuf_varpart Pointer to c-string URL file * @param hoststr Pointer to c-string hostname * @param additionalheaderline Pointer to c-string with additional HTTP header info * @param callback Pointer to callback function to handle response * @note Request sent in main packetloop */ static void browseUrl (const char *urlbuf, const char *urlbuf_varpart, const char *hoststr, const char *additionalheaderline, void (*callback)(uint8_t,uint16_t,uint16_t)); /** @brief Prepare HTTP request * @param urlbuf Pointer to c-string URL folder * @param urlbuf_varpart Pointer to c-string URL file * @param hoststr Pointer to c-string hostname * @param callback Pointer to callback function to handle response * @note Request sent in main packetloop */ static void browseUrl (const char *urlbuf, const char *urlbuf_varpart, const char *hoststr, void (*callback)(uint8_t,uint16_t,uint16_t)); /** @brief Prepare HTTP post message * @param urlbuf Pointer to c-string URL folder * @param hoststr Pointer to c-string hostname * @param additionalheaderline Pointer to c-string with additional HTTP header info * @param postval Pointer to c-string HTML Post value * @param callback Pointer to callback function to handle response * @note Request sent in main packetloop */ static void httpPost (const char *urlbuf, const char *hoststr, const char *additionalheaderline, const char *postval, void (*callback)(uint8_t,uint16_t,uint16_t)); /** @brief Send NTP request * @param ntpip IP address of NTP server * @param srcport IP port to send from */ static void ntpRequest (uint8_t *ntpip,uint8_t srcport); /** @brief Process network time protocol response * @param time Pointer to integer to hold result * @param dstport_l Destination port to expect response. Set to zero to accept on any port * @return <i>uint8_t</i> True (1) on success */ static uint8_t ntpProcessAnswer (uint32_t *time, uint8_t dstport_l); /** @brief Prepare a UDP message for transmission * @param sport Source port * @param dip Pointer to 4 byte destination IP address * @param dport Destination port */ static void udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport); /** @brief Transmit UDP packet * @param len Size of payload */ static void udpTransmit (uint16_t len); /** @brief Sends a UDP packet * @param data Pointer to data * @param len Size of payload (maximum 220 octets / bytes) * @param sport Source port * @param dip Pointer to 4 byte destination IP address * @param dport Destination port */ static void sendUdp (const char *data, uint8_t len, uint16_t sport, const uint8_t *dip, uint16_t dport); /** @brief Resister the function to handle ping events * @param cb Pointer to function */ static void registerPingCallback (void (*cb)(uint8_t*)); /** @brief Send ping * @param destip Ponter to 4 byte destination IP address */ static void clientIcmpRequest (const uint8_t *destip); /** @brief Check for ping response * @param ip_monitoredhost Pointer to 4 byte IP address of host to check * @return <i>uint8_t</i> True (1) if ping response from specified host */ static uint8_t packetLoopIcmpCheckReply (const uint8_t *ip_monitoredhost); /** @brief Send a wake on lan message * @param wolmac Pointer to 6 byte hardware (MAC) address of host to send message to */ static void sendWol (uint8_t *wolmac); // new stash-based API /** @brief Send TCP request */ static uint8_t tcpSend (); /** @brief Get TCP reply * @return <i>char*</i> Pointer to TCP reply payload. NULL if no data. */ static const char* tcpReply (uint8_t fd); /** @brief Configure TCP connections to be persistent or not * @param persist True to maintain TCP connection. False to finish TCP connection after first packet. */ static void persistTcpConnection(bool persist); //udpserver.cpp /** @brief Register function to handle incomint UDP events * @param callback Function to handle event * @param port Port to listen on */ static void udpServerListenOnPort(UdpServerCallback callback, uint16_t port); /** @brief Pause listing on UDP port * @brief port Port to pause */ static void udpServerPauseListenOnPort(uint16_t port); /** @brief Resume listing on UDP port * @brief port Port to pause */ static void udpServerResumeListenOnPort(uint16_t port); /** @brief Check if UDP server is listening on any ports * @return <i>bool</i> True if listening on any ports */ static bool udpServerListening(); //called by tcpip, in packetLoop /** @brief Passes packet to UDP Server * @param len Not used * @return <i>bool</i> True if packet processed */ static bool udpServerHasProcessedPacket(uint16_t len); //called by tcpip, in packetLoop // dhcp.cpp /** @brief Update DHCP state * @param len Length of received data packet */ static void DhcpStateMachine(uint16_t len); /** @brief Not implemented * @todo Implement dhcpStartTime or remove declaration */ static uint32_t dhcpStartTime (); /** @brief Not implemented * @todo Implement dhcpLeaseTime or remove declaration */ static uint32_t dhcpLeaseTime (); /** @brief Not implemented * @todo Implement dhcpLease or remove declaration */ static bool dhcpLease (); /** @brief Configure network interface with DHCP * @param hname The hostname to pass to the DHCP server * @param fromRam Set true to indicate whether hname is in RAM or in program space. Default = false * @return <i>bool</i> True if DHCP successful * @note Blocks until DHCP complete or timeout after 60 seconds */ static bool dhcpSetup (const char *hname = NULL, bool fromRam =false); /** @brief Register a callback for a specific DHCP option number * @param option The option number to request from the DHCP server * @param callback The function to be call when the option is received */ static void dhcpAddOptionCallback(uint8_t option, DhcpOptionCallback callback); // dns.cpp /** @brief Perform DNS lookup * @param name Host name to lookup * @param fromRam Set true to indicate whether name is in RAM or in program space. Default = false * @return <i>bool</i> True on success. * @note Result is stored in <i>hisip</i> member */ static bool dnsLookup (const char* name, bool fromRam =false); // webutil.cpp /** @brief Copies an IP address * @param dst Pointer to the 4 byte destination * @param src Pointer to the 4 byte source * @note There is no check of source or destination size. Ensure both are 4 bytes */ static void copyIp (uint8_t *dst, const uint8_t *src); /** @brief Copies a hardware address * @param dst Pointer to the 6 byte destination * @param src Pointer to the 6 byte destination * @note There is no check of source or destination size. Ensure both are 6 bytes */ static void copyMac (uint8_t *dst, const uint8_t *src); /** @brief Output to serial port in dotted decimal IP format * @param buf Pointer to 4 byte IP address * @note There is no check of source or destination size. Ensure both are 4 bytes */ static void printIp (const uint8_t *buf); /** @brief Output message and IP address to serial port in dotted decimal IP format * @param msg Pointer to null terminated string * @param buf Pointer to 4 byte IP address * @note There is no check of source or destination size. Ensure both are 4 bytes */ static void printIp (const char* msg, const uint8_t *buf); /** @brief Output Flash String Helper and IP address to serial port in dotted decimal IP format * @param ifsh Pointer to Flash String Helper * @param buf Pointer to 4 byte IP address * @note There is no check of source or destination size. Ensure both are 4 bytes * @todo What is a FlashStringHelper? */ static void printIp (const __FlashStringHelper *ifsh, const uint8_t *buf); /** @brief Search for a string of the form key=value in a string that looks like q?xyz=abc&uvw=defgh HTTP/1.1\\r\\n * @param str Pointer to the null terminated string to search * @param strbuf Pointer to buffer to hold null terminated result string * @param maxlen Maximum length of result * @param key Pointer to null terminated string holding the key to search for * @return <i>unit_t</i> Length of the value. 0 if not found * @note Ensure strbuf has memory allocated of at least maxlen + 1 (to accomodate result plus terminating null) */ static uint8_t findKeyVal(const char *str,char *strbuf, uint8_t maxlen, const char *key); /** @brief Decode a URL string e.g “hello%20joe” or “hello+joe” becomes “hello joe” * @param urlbuf Pointer to the null terminated URL * @note urlbuf is modified */ static void urlDecode(char *urlbuf); /** @brief Encode a URL, replacing illegal charaters like ‘ ‘ * @param str Pointer to the null terminated string to encode * @param urlbuf Pointer to a buffer to contain the null terminated encoded URL * @note There must be enough space in urlbuf. In the worst case that is 3 times the length of str */ static void urlEncode(char *str,char *urlbuf); /** @brief Convert an IP address from dotted decimal formated string to 4 bytes * @param bytestr Pointer to the 4 byte array to store IP address * @param str Pointer to string to parse * @return <i>uint8_t</i> 0 on success */ static uint8_t parseIp(uint8_t *bytestr,char *str); /** @brief Convert a byte array to a human readable display string * @param resultstr Pointer to a buffer to hold the resulting null terminated string * @param bytestr Pointer to the byte array containing the address to convert * @param len Length of the array (4 for IP address, 6 for hardware (MAC) address) * @param separator Delimiter character (typically ‘.’ for IP address and ‘:’ for hardware (MAC) address) * @param base Base for numerical representation (typically 10 for IP address and 16 for hardware (MAC) address */ static void makeNetStr(char *resultstr,uint8_t *bytestr,uint8_t len, char separator,uint8_t base); /** @brief Return the sequence number of the current TCP package */ static uint32_t getSequenceNumber(); /** @brief Return the payload length of the current Tcp package */ static uint16_t getTcpPayloadLength(); }; extern EtherCard ether; //!< Global presentation of EtherCard class #endif
net.h
enc28j60.cpp
EtherCard.cpp