Arduino Web Server

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_      | '
enc28j60.h
// 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
enc28j60.h

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

enc28j60.h

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

enc28j60.h

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

enc28j60.h

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

enc28j60.h

net.h


enc28j60.cpp


EtherCard.cpp


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注