IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Arduino Discussion :

Shield Ethernet : émettre un seul packet avec plusieurs client.write() ?


Sujet :

Arduino

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre chevronné Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    999
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 999
    Par défaut Shield Ethernet : émettre un seul packet avec plusieurs client.write() ?
    Bonjour,

    J'utilise le Shield Ethernet 2 Arduino et j'ai constaté le comportement suivant, grâce à WireShark qui permet d'analyser les échanges TCP/IP

    Le code suivant marche bien, la réponse envoyée au client est faite dans un unique packet TCP/IP

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    void loop() { // Looking for client connections and send them responses
    	client = server.available();
    	if (client) {
    		//while (client.connected()) {
    		if (client.connected()) {
    			if (client.available()) {
    				Built_Response();
    				// It's important to use a buffer, to send all data in a single packet
    				// WARNING : Buffer_HTTP can contains null \0 bytes, so we have to use client.write instead of client.print
    				client.write(Buffer_HTTP+Buffer_HTTP_Offset, Buffer_HTTP_Stop-Buffer_HTTP_Offset);
    			}
    		}
    		delay(1); // give the web browser time to receive the data
    		client.stop(); // close the connection:
    	}
    }
    En revanche, si je construit la réponse "en plusieurs fois", c'est à dire avec plusieurs instructions client.write(), j'ai un packet pour chaque instruction write(), même si elles sont effectuées immédiatement l'une après l'autre.

    Pourquoi c'est ennuyeux ?

    Hé bien faire plusieurs instructions write() au lieu d'une seule permet de ne pas avoir un gros buffer en RAM.
    Il est stupide de devoir stocker en RAM un gros buffer dont une partie non négligeable sert à écrire des chaines fixes en PROGMEM

    J'avais un code qui économisait la RAM, mais je pouvait me retrouver avec des paquets TCP/IP n'ayant qu'un seul octet de charge utile, sachant qu'un paquets possède 54 octets de "header" (je parle des "headers" du paquet TCP/IP, rien à voir avec le "header" HTTP) donc l'efficacité de la communication réseau est médiocre

    Comment faire, avec le W5500, pour "remplir" d'abord son buffer de données et ensuite seulement lui donner le feu vert pour envoyer un packet ?

    Voici le bout de code "write" de ma version de W5100.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    uint16_t W5100Class::write(uint16_t addr, const uint8_t *buf, uint16_t len)
    {
    	uint8_t cmd[8];
    	setSS();
    	if (addr < 0x100) {
    		// common registers 00nn
    		cmd[0] = 0;
    		cmd[1] = addr & 0xFF;
    		cmd[2] = 0x04;
    	} else if (addr < 0x8000) {
    		// socket registers  10nn, 11nn, 12nn, 13nn, etc
    		cmd[0] = 0;
    		cmd[1] = addr & 0xFF;
    		cmd[2] = ((addr >> 3) & 0xE0) | 0x0C;
    	} else if (addr < 0xC000) {
    		// transmit buffers  8000-87FF, 8800-8FFF, 9000-97FF, etc
    		//  10## #nnn nnnn nnnn
    		cmd[0] = addr >> 8;
    		cmd[1] = addr & 0xFF;
    		#if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1
    		cmd[2] = 0x14;                       // 16K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2
    		cmd[2] = ((addr >> 8) & 0x20) | 0x14; // 8K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4
    		cmd[2] = ((addr >> 7) & 0x60) | 0x14; // 4K buffers
    		#else
    		cmd[2] = ((addr >> 6) & 0xE0) | 0x14; // 2K buffers
    		#endif
    	} else {
    		// receive buffers
    		cmd[0] = addr >> 8;
    		cmd[1] = addr & 0xFF;
    		#if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1
    		cmd[2] = 0x1C;                       // 16K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2
    		cmd[2] = ((addr >> 8) & 0x20) | 0x1C; // 8K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4
    		cmd[2] = ((addr >> 7) & 0x60) | 0x1C; // 4K buffers
    		#else
    		cmd[2] = ((addr >> 6) & 0xE0) | 0x1C; // 2K buffers
    		#endif
    	}
    	if (len <= 5) {
    		for (uint8_t i=0; i < len; i++) {
    			cmd[i + 3] = buf[i];
    		}
    		SPI.transfer(cmd, len + 3);
    	} else {
    		SPI.transfer(cmd, 3);
    #ifdef SPI_HAS_TRANSFER_BUF
    		SPI.transfer(buf, NULL, len);
    #else
    		// TODO: copy 8 bytes at a time to cmd[] and block transfer
    		for (uint16_t i=0; i < len; i++) {
    			SPI.transfer(buf[i]);
    		}
    #endif
    	}
    	resetSS();
    	return len;
    }
    Voici le bout de code "write" de ma version de socket.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    static void write_data(uint8_t s, uint16_t data_offset, const uint8_t *data, uint16_t len) {
    	uint16_t ptr = W5100.readSnTX_WR(s);
    	ptr += data_offset;
    	uint16_t offset = ptr & W5100.SMASK;
    	uint16_t dstAddr = offset + W5100.SBASE(s);
     
    	if (W5100.hasOffsetAddressMapping() || offset + len <= W5100.SSIZE) {
    		W5100.write(dstAddr, data, len);
    	} else {
    		// Wrap around circular buffer
    		uint16_t size = W5100.SSIZE - offset;
    		W5100.write(dstAddr, data, size);
    		W5100.write(W5100.SBASE(s), data + size, len - size);
    	}
    	ptr += len;
    	W5100.writeSnTX_WR(s, ptr);
    }
    Voici le bout de code "write" de ma version de EthernetServer.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    size_t RLucas_EthernetServer::write(uint8_t b) {
    	return write(&b, 1);
    }
     
    size_t RLucas_EthernetServer::write(const uint8_t *buffer, size_t size) {
    	available();
    	for (uint8_t i=0; i < MAX_SOCK_NUM; i++) { //for (uint8_t i=0; i < maxindex; i++) {
    		if (server_port[i] == _port) {
    			if (Ethernet.socketStatus(i) == SnSR::ESTABLISHED) {
    				Ethernet.socketSend(i, buffer, size);
    			}
    		}
    	}
    	return size;
    }
    Merci

    A bientôt

  2. #2
    Membre chevronné Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    999
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 999
    Par défaut
    Sujet évoqué ici : https://arduino.stackexchange.com/qu...-fragmentation

    et ici : https://forum.arduino.cc/index.php?topic=529569.0

    Mais les solutions proposés n'en sont pas, il utilisent juste comme dans mon code un buffer pour tout envoyer d'un coup

    Alors que le W5500 possède lui-même un grand buffer, c'est dommage de gâcher la RAM de l'Arduino.

    Il va falloir modifier en profondeur le code de EthernetServer.cpp, socket.cpp, W5100.cpp

    On en apprend un peu plus dans cette fonction de socket.cpp

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    //	This function used to send the data in TCP mode - return 1 for success else 0.
    uint16_t RLucas_EthernetClass::socketSend(uint8_t s, const uint8_t * buf, uint16_t len) {
    	uint8_t status=0;
    	uint16_t ret=0;
    	uint16_t freesize=0;
     
    	if (len > W5100.SSIZE) {
    		ret = W5100.SSIZE; // check size not to exceed MAX size.
    	} else {
    		ret = len;
    	}
     
    	// if freebuf is available, start.
    	do {
    		SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    		freesize = getSnTX_FSR(s);
    		status = W5100.readSnSR(s);
    		SPI.endTransaction();
    		if ((status != SnSR::ESTABLISHED) && (status != SnSR::CLOSE_WAIT)) {
    			ret = 0;
    			break;
    		}
    		yield();
    	} while (freesize < ret);
     
    	// copy data
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	write_data(s, 0, (uint8_t *)buf, ret);
    	W5100.execCmdSn(s, Sock_SEND);
     
    	while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) {
    		if ( W5100.readSnSR(s) == SnSR::CLOSED ) {
    			SPI.endTransaction();
    			return 0;
    		}
    		SPI.endTransaction();
    		yield();
    		SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	}
    	W5100.writeSnIR(s, SnIR::SEND_OK);
    	SPI.endTransaction();
    	return ret;
    }

  3. #3
    Membre chevronné Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    999
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 999
    Par défaut
    Pour que ce soit plus clair,

    voici les codes sources complets :

    RLucas_Ethernet.h :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    #ifndef ethernet_h_
    #define ethernet_h_
     
    // Configure the maximum number of sockets to support.  W5100 chips can have up to 4 sockets.  W5200 & W5500 can have up to 8 sockets.  Several bytes
    // of RAM are used for each socket.  Reducing the maximum can save RAM, but you are limited to fewer simultaneous connections.
    #if defined(RAMEND) && defined(RAMSTART) && ((RAMEND - RAMSTART) <= 2048)
    #define MAX_SOCK_NUM 4
    #else
    #define MAX_SOCK_NUM 8
    #endif
     
    // By default, each socket uses 2K buffers inside the Wiznet chip.  If MAX_SOCK_NUM is set to fewer than the chip's maximum, uncommenting
    // this will use larger buffers within the Wiznet chip.  Large buffers can really help with UDP protocols like Artnet.  In theory larger
    // buffers should allow faster TCP over high-latency links, but this does not always seem to work in practice (maybe Wiznet bugs?)
    //#define ETHERNET_LARGE_BUFFERS
     
    #include <Arduino.h>
    #include "Print.h"
    #include "Stream.h"
    #include "RLucas_IPAddress.h"
     
    class RLucas_EthernetClient;
    class RLucas_EthernetServer;
     
    class RLucas_EthernetClass {
    public:
    	static int begin(uint8_t *mac, unsigned long timeout = 60000, unsigned long responseTimeout = 4000);
    	static void begin(uint8_t *mac, RLucas_IPAddress ip);
    	static void init(uint8_t sspin = 10);
     
    	void setMACAddress(const uint8_t *mac_address);
    	void setSubnetMask(const RLucas_IPAddress subnet);
    	void setRetransmissionCount(uint8_t num);
     
    	friend class RLucas_EthernetClient;
    	friend class RLucas_EthernetServer;
    private:
    	static uint8_t socketBegin(uint8_t protocol, uint16_t port);
    	static uint8_t socketStatus(uint8_t s);
    	static void socketClose(uint8_t s);
    	static void socketConnect(uint8_t s, uint8_t * addr, uint16_t port);
    	static void socketDisconnect(uint8_t s);
    	static uint8_t socketListen(uint8_t s);
    	// Send data (TCP)
    	static uint16_t socketSend(uint8_t s, const uint8_t * buf, uint16_t len);
    	static uint16_t socketSendAvailable(uint8_t s);
    	// Receive data (TCP)
    	static int socketRecv(uint8_t s, uint8_t * buf, int16_t len);
    	static uint16_t socketRecvAvailable(uint8_t s);
    	static uint8_t socketPeek(uint8_t s);
    };
     
    extern RLucas_EthernetClass Ethernet;
     
    class RLucas_EthernetClient : public Stream {
    public:
    	RLucas_EthernetClient() : sockindex(MAX_SOCK_NUM), _timeout(1000) { }
    	RLucas_EthernetClient(uint8_t s) : sockindex(s), _timeout(1000) { }
    	virtual int connect(RLucas_IPAddress ip, uint16_t port);
    	virtual size_t write(uint8_t);
    	virtual size_t write(const uint8_t *buf, size_t size);
    	virtual int available();
    	virtual int read();
    	virtual int read(uint8_t *buf, size_t size);
    	virtual int peek();
    	virtual uint8_t connected();
    	virtual operator bool() { return sockindex < MAX_SOCK_NUM; }
    	virtual void stop();
    	//virtual RLucas_IPAddress remoteIP();
    	virtual byte IPLastByte();
    	friend class RLucas_EthernetServer;
    	using Print::write;
    //protected:
    	uint8_t* rawIPAddress(RLucas_IPAddress& addr) { return addr.raw_address(); };
    private:
    	uint8_t sockindex; // MAX_SOCK_NUM means client not in use
    	uint16_t _timeout;
    };
     
    class RLucas_EthernetServer : public Print {
    private:
    	uint16_t _port;
    public:
    	RLucas_EthernetServer(uint16_t port) : _port(port) { }
    	RLucas_EthernetClient available();
    	RLucas_EthernetClient accept();
    	virtual void begin();
    	virtual size_t write(uint8_t);
    	virtual size_t write(const uint8_t *buf, size_t size);
    	using Print::write;
    	// TODO: make private when socket allocation moves to RLucas_EthernetClass
    	static uint16_t server_port[MAX_SOCK_NUM];
    };
     
    #endif
    RLucas_Ethernet.cpp :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    #include <Arduino.h>
    #include "RLucas_Ethernet.h"
    #include "RLucas_w5100.h"
     
    void RLucas_EthernetClass::begin(uint8_t *mac, RLucas_IPAddress ip) {
    	//RLucas_IPAddress subnet(255, 255, 255, 0);
    	RLucas_IPAddress subnet(0x00FFFFFFul); // This is the single time we need RLucas_IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
    	RLucas_IPAddress dns = ip;
    	dns[3] = 1; // We don't use DNS, but we need to send it to W5100 chip
    	if (W5100.init() == 0) {
    		SPI.endTransaction();
    		return;
    	}
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	W5100.setMACAddress(mac);
    #if ARDUINO > 106 || TEENSYDUINO > 121
    	W5100.setIPAddress(ip._address.bytes);
    	W5100.setGatewayIp(dns._address.bytes);
    	W5100.setSubnetMask(subnet._address.bytes);
    #else
    	W5100.setIPAddress(ip._address);
    	W5100.setGatewayIp(dns._address);
    	W5100.setSubnetMask(subnet._address);
    #endif
    	SPI.endTransaction();
    }
     
    void RLucas_EthernetClass::init(uint8_t sspin) {
    	W5100.setSS(sspin);
    }
     
    void RLucas_EthernetClass::setMACAddress(const uint8_t *mac_address) {
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	W5100.setMACAddress(mac_address);
    	SPI.endTransaction();
    }
     
    void RLucas_EthernetClass::setSubnetMask(const RLucas_IPAddress subnet) {
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	RLucas_IPAddress ip = subnet;
    	W5100.setSubnetMask(ip.raw_address());
    	SPI.endTransaction();
    }
    void RLucas_EthernetClass::setRetransmissionCount(uint8_t num) {
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	W5100.setRetransmissionCount(num);
    	SPI.endTransaction();
    }
     
    RLucas_EthernetClass Ethernet;

    RLucas_EthernetServer.cpp - C'est la fonction write(const uint8_t *buffer, size_t size) qui est appellée par mon projet Arduino pour écrire des données :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    #include <Arduino.h>
    #include "RLucas_Ethernet.h"
    #include "RLucas_w5100.h"
     
    uint16_t RLucas_EthernetServer::server_port[MAX_SOCK_NUM];
     
    void RLucas_EthernetServer::begin() {
    	uint8_t sockindex = Ethernet.socketBegin(SnMR::TCP, _port);
    	if (sockindex < MAX_SOCK_NUM) {
    		if (Ethernet.socketListen(sockindex)) {
    			server_port[sockindex] = _port;
    		} else {
    			Ethernet.socketDisconnect(sockindex);
    		}
    	}
    }
     
    RLucas_EthernetClient RLucas_EthernetServer::available() {
    	bool listening = false;
    	uint8_t sockindex = MAX_SOCK_NUM;
    	for (uint8_t i=0; i < MAX_SOCK_NUM; i++) { //for (uint8_t i=0; i < maxindex; i++) {
    		if (server_port[i] == _port) {
    			uint8_t stat = Ethernet.socketStatus(i);
    			if (stat == SnSR::ESTABLISHED || stat == SnSR::CLOSE_WAIT) {
    				if (Ethernet.socketRecvAvailable(i) > 0) {
    					sockindex = i;
    				} else {
    					// remote host closed connection, our end still open
    					if (stat == SnSR::CLOSE_WAIT) {
    						Ethernet.socketDisconnect(i);
    						// status becomes LAST_ACK for short time
    					}
    				}
    			} else if (stat == SnSR::LISTEN) {
    				listening = true;
    			} else if (stat == SnSR::CLOSED) {
    				server_port[i] = 0;
    			}
    		}
    	}
    	if (!listening) begin();
    	return RLucas_EthernetClient(sockindex);
    }
     
    RLucas_EthernetClient RLucas_EthernetServer::accept() {
    	bool listening = false;
    	uint8_t sockindex = MAX_SOCK_NUM;
    	for (uint8_t i=0; i < MAX_SOCK_NUM; i++) { //for (uint8_t i=0; i < maxindex; i++) {
    		if (server_port[i] == _port) {
    			uint8_t stat = Ethernet.socketStatus(i);
    			if (sockindex == MAX_SOCK_NUM &&
    			  (stat == SnSR::ESTABLISHED || stat == SnSR::CLOSE_WAIT)) {
    				// Return the connected client even if no data received. Some protocols like FTP expect the server to send the first data.
    				sockindex = i;
    				server_port[i] = 0; // only return the client once
    			} else if (stat == SnSR::LISTEN) {
    				listening = true;
    			} else if (stat == SnSR::CLOSED) {
    				server_port[i] = 0;
    			}
    		}
    	}
    	if (!listening) begin();
    	return RLucas_EthernetClient(sockindex);
    }
     
    size_t RLucas_EthernetServer::write(uint8_t b) {
    	return write(&b, 1);
    }
    size_t RLucas_EthernetServer::write(const uint8_t *buffer, size_t size) {
    	available();
    	for (uint8_t i=0; i < MAX_SOCK_NUM; i++) { //for (uint8_t i=0; i < maxindex; i++) {
    		if (server_port[i] == _port) {
    			if (Ethernet.socketStatus(i) == SnSR::ESTABLISHED) {
    				Ethernet.socketSend(i, buffer, size);
    			}
    		}
    	}
    	return size;
    }
    On rentre dans le hardware... fichier RLucas_socket.cpp - les données sont écrites par socketSend() qui appelle d'autres fonctions comme write_data()

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    #include <Arduino.h>
    #include "RLucas_Ethernet.h"
    #include "RLucas_w5100.h"
     
    #if ARDUINO >= 156 && !defined(ARDUINO_ARCH_PIC32)
    extern void yield(void);
    #else
    #define yield()
    #endif
     
    // TODO: randomize this when not using DHCP, but how?
    static uint16_t local_port = 49152;  // 49152 to 65535
     
    typedef struct {
    	uint16_t RX_RSR; // Number of bytes received
    	uint16_t RX_RD;  // Address to read
    	uint16_t TX_FSR; // Free space ready for transmit
    	uint8_t  RX_inc; // how much have we advanced RX_RD
    } socketstate_t;
    static socketstate_t state[MAX_SOCK_NUM];
    static uint16_t getSnTX_FSR(uint8_t s);
    static uint16_t getSnRX_RSR(uint8_t s);
    static void write_data(uint8_t s, uint16_t offset, const uint8_t *data, uint16_t len);
    static void read_data(uint8_t s, uint16_t src, uint8_t *dst, uint16_t len);
     
    /*****************************************/
    /*          Socket management            */
    /*****************************************/
     
    uint8_t RLucas_EthernetClass::socketBegin(uint8_t protocol, uint16_t port) {
    	uint8_t s, status[MAX_SOCK_NUM]; 
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	// look at all the hardware sockets, use any that are closed (unused)
    	for (s=0; s < MAX_SOCK_NUM; s++) {
    		status[s] = W5100.readSnSR(s);
    		if (status[s] == SnSR::CLOSED) goto makesocket;
    	}
    	// as a last resort, forcibly close any already closing
    	for (s=0; s < MAX_SOCK_NUM; s++) {
    		uint8_t stat = status[s];
    		if (stat == SnSR::LAST_ACK) goto closemakesocket;
    		if (stat == SnSR::TIME_WAIT) goto closemakesocket;
    		if (stat == SnSR::FIN_WAIT) goto closemakesocket;
    		if (stat == SnSR::CLOSING) goto closemakesocket;
    	}
    	SPI.endTransaction();
    	return MAX_SOCK_NUM; // all sockets are in use
    closemakesocket:
    	W5100.execCmdSn(s, Sock_CLOSE);
    makesocket:
    	RLucas_EthernetServer::server_port[s] = 0;
    	delayMicroseconds(250); // TODO: is this needed??
    	W5100.writeSnMR(s, protocol);
    	W5100.writeSnIR(s, 0xFF);
    	if (port > 0) {
    		W5100.writeSnPORT(s, port);
    	} else {
    		// if don't set the source port, set local_port number.
    		if (++local_port < 49152) local_port = 49152;
    		W5100.writeSnPORT(s, local_port);
    	}
    	W5100.execCmdSn(s, Sock_OPEN);
    	state[s].RX_RSR = 0;
    	state[s].RX_RD  = W5100.readSnRX_RD(s); // always zero?
    	state[s].RX_inc = 0;
    	state[s].TX_FSR = 0;
    	SPI.endTransaction();
    	return s;
    }
     
    // Return the socket's status
    uint8_t RLucas_EthernetClass::socketStatus(uint8_t s) {
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	uint8_t status = W5100.readSnSR(s);
    	SPI.endTransaction();
    	return status;
    }
     
    // Immediately close.  If a TCP connection is established, the remote host is left unaware we closed.
    void RLucas_EthernetClass::socketClose(uint8_t s) {
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	W5100.execCmdSn(s, Sock_CLOSE);
    	SPI.endTransaction();
    }
     
    // Place the socket in listening (server) mode
    uint8_t RLucas_EthernetClass::socketListen(uint8_t s) {
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	if (W5100.readSnSR(s) != SnSR::INIT) {
    		SPI.endTransaction();
    		return 0;
    	}
    	W5100.execCmdSn(s, Sock_LISTEN);
    	SPI.endTransaction();
    	return 1;
    }
     
    // establish a TCP connection in Active (client) mode.
    void RLucas_EthernetClass::socketConnect(uint8_t s, uint8_t * addr, uint16_t port) {
    	// set destination IP
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	W5100.writeSnDIPR(s, addr);
    	W5100.writeSnDPORT(s, port);
    	W5100.execCmdSn(s, Sock_CONNECT);
    	SPI.endTransaction();
    }
     
    // Gracefully disconnect a TCP connection.
    void RLucas_EthernetClass::socketDisconnect(uint8_t s) {
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	W5100.execCmdSn(s, Sock_DISCON);
    	SPI.endTransaction();
    }
     
    /*****************************************/
    /*    Socket Data Receive Functions      */
    /*****************************************/
     
    static uint16_t getSnRX_RSR(uint8_t s){
    	uint16_t val, prev;
    	prev = W5100.readSnRX_RSR(s);
    	while (1) {
    		val = W5100.readSnRX_RSR(s);
    		if (val == prev) {
    			return val;
    		}
    		prev = val;
    	}
    }
     
    static void read_data(uint8_t s, uint16_t src, uint8_t *dst, uint16_t len) {
    	uint16_t size;
    	uint16_t src_mask;
    	uint16_t src_ptr;
    	src_mask = (uint16_t)src & W5100.SMASK;
    	src_ptr = W5100.RBASE(s) + src_mask;
    	if (W5100.hasOffsetAddressMapping() || src_mask + len <= W5100.SSIZE) {
    		W5100.read(src_ptr, dst, len);
    	} else {
    		size = W5100.SSIZE - src_mask;
    		W5100.read(src_ptr, dst, size);
    		dst += size;
    		W5100.read(W5100.RBASE(s), dst, len - size);
    	}
    }
     
    // Receive data.  Returns size, or -1 for no data, or 0 if connection closed
    int RLucas_EthernetClass::socketRecv(uint8_t s, uint8_t *buf, int16_t len) {
    	// Check how much data is available
    	int ret = state[s].RX_RSR;
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	if (ret < len) {
    		uint16_t rsr = getSnRX_RSR(s);
    		ret = rsr - state[s].RX_inc;
    		state[s].RX_RSR = ret;
    	}
    	if (ret == 0) {
    		// No data available.
    		uint8_t status = W5100.readSnSR(s);
    		if ( status == SnSR::LISTEN || status == SnSR::CLOSED ||
    		  status == SnSR::CLOSE_WAIT ) {
    			// The remote end has closed its side of the connection, so this is the eof state
    			ret = 0;
    		} else {
    			// The connection is still up, but there's no data waiting to be read
    			ret = -1;
    		}
    	} else {
    		if (ret > len) ret = len; // more data available than buffer length
    		uint16_t ptr = state[s].RX_RD;
    		if (buf) read_data(s, ptr, buf, ret);
    		ptr += ret;
    		state[s].RX_RD = ptr;
    		state[s].RX_RSR -= ret;
    		uint16_t inc = state[s].RX_inc + ret;
    		if (inc >= 250 || state[s].RX_RSR == 0) {
    			state[s].RX_inc = 0;
    			W5100.writeSnRX_RD(s, ptr);
    			W5100.execCmdSn(s, Sock_RECV);
    		} else {
    			state[s].RX_inc = inc;
    		}
    	}
    	SPI.endTransaction();
    	return ret;
    }
     
    uint16_t RLucas_EthernetClass::socketRecvAvailable(uint8_t s) {
    	uint16_t ret = state[s].RX_RSR;
    	if (ret == 0) {
    		SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    		uint16_t rsr = getSnRX_RSR(s);
    		SPI.endTransaction();
    		ret = rsr - state[s].RX_inc;
    		state[s].RX_RSR = ret;
    	}
    	return ret;
    }
     
    // get the first byte in the receive queue (no checking)
    uint8_t RLucas_EthernetClass::socketPeek(uint8_t s) {
    	uint8_t b;
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	uint16_t ptr = state[s].RX_RD;
    	W5100.read((ptr & W5100.SMASK) + W5100.RBASE(s), &b, 1);
    	SPI.endTransaction();
    	return b;
    }
     
    /*****************************************/
    /*    Socket Data Transmit Functions     */
    /*****************************************/
     
    static uint16_t getSnTX_FSR(uint8_t s) {
    	uint16_t val, prev;
    	prev = W5100.readSnTX_FSR(s);
    	while (1) {
    		val = W5100.readSnTX_FSR(s);
    		if (val == prev) {
    			state[s].TX_FSR = val;
    			return val;
    		}
    		prev = val;
    	}
    }
     
    static void write_data(uint8_t s, uint16_t data_offset, const uint8_t *data, uint16_t len) {
    	uint16_t ptr = W5100.readSnTX_WR(s);
    	ptr += data_offset;
    	uint16_t offset = ptr & W5100.SMASK;
    	uint16_t dstAddr = offset + W5100.SBASE(s);
    	if (W5100.hasOffsetAddressMapping() || offset + len <= W5100.SSIZE) {
    		W5100.write(dstAddr, data, len);
    	} else {
    		// Wrap around circular buffer
    		uint16_t size = W5100.SSIZE - offset;
    		W5100.write(dstAddr, data, size);
    		W5100.write(W5100.SBASE(s), data + size, len - size);
    	}
    	ptr += len;
    	W5100.writeSnTX_WR(s, ptr);
    }
     
    //	This function used to send the data in TCP mode - return 1 for success else 0.
    uint16_t RLucas_EthernetClass::socketSend(uint8_t s, const uint8_t * buf, uint16_t len) {
    	uint8_t status=0;
    	uint16_t ret=0;
    	uint16_t freesize=0;
    	if (len > W5100.SSIZE) {
    		ret = W5100.SSIZE; // check size not to exceed MAX size.
    	} else {
    		ret = len;
    	}
    	// if freebuf is available, start.
    	do {
    		SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    		freesize = getSnTX_FSR(s);
    		status = W5100.readSnSR(s);
    		SPI.endTransaction();
    		if ((status != SnSR::ESTABLISHED) && (status != SnSR::CLOSE_WAIT)) {
    			ret = 0;
    			break;
    		}
    		yield();
    	} while (freesize < ret);
    	// copy data
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	write_data(s, 0, (uint8_t *)buf, ret);
    	W5100.execCmdSn(s, Sock_SEND);
     
    	while ( (W5100.readSnIR(s) & SnIR::SEND_OK) != SnIR::SEND_OK ) {
    		if ( W5100.readSnSR(s) == SnSR::CLOSED ) {
    			SPI.endTransaction();
    			return 0;
    		}
    		SPI.endTransaction();
    		yield();
    		SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	}
    	W5100.writeSnIR(s, SnIR::SEND_OK);
    	SPI.endTransaction();
    	return ret;
    }
     
    uint16_t RLucas_EthernetClass::socketSendAvailable(uint8_t s) {
    	uint8_t status=0;
    	uint16_t freesize=0;
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	freesize = getSnTX_FSR(s);
    	status = W5100.readSnSR(s);
    	SPI.endTransaction();
    	if ((status == SnSR::ESTABLISHED) || (status == SnSR::CLOSE_WAIT)) {
    		return freesize;
    	}
    	return 0;
    }
    RLucas_W5100.h :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    #ifndef	W5100_H_INCLUDED
    #define	W5100_H_INCLUDED
     
    #include <Arduino.h>
    #include <SPI.h>
     
    // Safe for all chips
    #define SPI_ETHERNET_SETTINGS SPISettings(14000000, MSBFIRST, SPI_MODE0)
    //#define SPI_ETHERNET_SETTINGS SPISettings(1000, MSBFIRST, SPI_MODE0)
     
    // Safe for W5200 and W5500, but too fast for W5100
    // Uncomment this if you know you'll never need W5100 support.
    //  Higher SPI clock only results in faster transfer to hosts on a LAN
    //  or with very low packet latency.  With ordinary internet latency,
    //  the TCP window size & packet loss determine your overall speed.
    //#define SPI_ETHERNET_SETTINGS SPISettings(30000000, MSBFIRST, SPI_MODE0)
    //#define SPI_ETHERNET_SETTINGS SPISettings(10000, MSBFIRST, SPI_MODE0)
     
    // Require Ethernet.h, because we need MAX_SOCK_NUM
    #ifndef ethernet_h_
    #error "Ethernet.h must be included before w5100.h"
    #endif
     
    // Arduino 101's SPI can not run faster than 8 MHz.
    #if defined(ARDUINO_ARCH_ARC32)
    #undef SPI_ETHERNET_SETTINGS
    #define SPI_ETHERNET_SETTINGS SPISettings(8000000, MSBFIRST, SPI_MODE0)
    #endif
     
    // Arduino Zero can't use W5100-based shields faster than 8 MHz
    // https://github.com/arduino-libraries/Ethernet/issues/37#issuecomment-408036848
    // W5500 does seem to work at 12 MHz.  Delete this if only using W5500
    #if defined(__SAMD21G18A__)
    #undef SPI_ETHERNET_SETTINGS
    #define SPI_ETHERNET_SETTINGS SPISettings(8000000, MSBFIRST, SPI_MODE0)
    #endif
     
    typedef uint8_t SOCKET;
     
    class SnMR {
    public:
      static const uint8_t CLOSE  = 0x00;
      static const uint8_t TCP    = 0x21;
      static const uint8_t UDP    = 0x02;
      static const uint8_t IPRAW  = 0x03;
      static const uint8_t MACRAW = 0x04;
      static const uint8_t PPPOE  = 0x05;
      static const uint8_t ND     = 0x20;
      static const uint8_t MULTI  = 0x80;
    };
     
    enum SockCMD {
      Sock_OPEN      = 0x01,
      Sock_LISTEN    = 0x02,
      Sock_CONNECT   = 0x04,
      Sock_DISCON    = 0x08,
      Sock_CLOSE     = 0x10,
      Sock_SEND      = 0x20,
      Sock_SEND_MAC  = 0x21,
      Sock_SEND_KEEP = 0x22,
      Sock_RECV      = 0x40
    };
     
    class SnIR {
    public:
      static const uint8_t SEND_OK = 0x10;
      static const uint8_t TIMEOUT = 0x08;
      static const uint8_t RECV    = 0x04;
      static const uint8_t DISCON  = 0x02;
      static const uint8_t CON     = 0x01;
    };
     
    class SnSR {
    public:
      static const uint8_t CLOSED      = 0x00;
      static const uint8_t INIT        = 0x13;
      static const uint8_t LISTEN      = 0x14;
      static const uint8_t SYNSENT     = 0x15;
      static const uint8_t SYNRECV     = 0x16;
      static const uint8_t ESTABLISHED = 0x17;
      static const uint8_t FIN_WAIT    = 0x18;
      static const uint8_t CLOSING     = 0x1A;
      static const uint8_t TIME_WAIT   = 0x1B;
      static const uint8_t CLOSE_WAIT  = 0x1C;
      static const uint8_t LAST_ACK    = 0x1D;
      static const uint8_t UDP         = 0x22;
      static const uint8_t IPRAW       = 0x32;
      static const uint8_t MACRAW      = 0x42;
      static const uint8_t PPPOE       = 0x5F;
    };
     
    class IPPROTO {
    public:
      static const uint8_t IP   = 0;
      static const uint8_t ICMP = 1;
      static const uint8_t IGMP = 2;
      static const uint8_t GGP  = 3;
      static const uint8_t TCP  = 6;
      static const uint8_t PUP  = 12;
      static const uint8_t UDP  = 17;
      static const uint8_t IDP  = 22;
      static const uint8_t ND   = 77;
      static const uint8_t RAW  = 255;
    };
     
    enum W5100Linkstatus {
      UNKNOWN,
      LINK_ON,
      LINK_OFF
    };
     
    class W5100Class {
     
    public:
      static uint8_t init(void);
     
      inline void setGatewayIp(const uint8_t * addr) { writeGAR(addr); }
      inline void getGatewayIp(uint8_t * addr) { readGAR(addr); }
     
      inline void setSubnetMask(const uint8_t * addr) { writeSUBR(addr); }
      inline void getSubnetMask(uint8_t * addr) { readSUBR(addr); }
     
      inline void setMACAddress(const uint8_t * addr) { writeSHAR(addr); }
      inline void getMACAddress(uint8_t * addr) { readSHAR(addr); }
     
      inline void setIPAddress(const uint8_t * addr) { writeSIPR(addr); }
      inline void getIPAddress(uint8_t * addr) { readSIPR(addr); }
     
      inline void setRetransmissionTime(uint16_t timeout) { writeRTR(timeout); }
      inline void setRetransmissionCount(uint8_t retry) { writeRCR(retry); }
     
      static void execCmdSn(SOCKET s, SockCMD _cmd);
     
      // W5100 Registers
      // ---------------
    //private:
    public:
      static uint16_t write(uint16_t addr, const uint8_t *buf, uint16_t len);
      static uint8_t write(uint16_t addr, uint8_t data) {
        return write(addr, &data, 1);
      }
      static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len);
      static uint8_t read(uint16_t addr) {
        uint8_t data;
        read(addr, &data, 1);
        return data;
      }
     
    #define __GP_REGISTER8(name, address)             \
      static inline void write##name(uint8_t _data) { \
        write(address, _data);                        \
      }                                               \
      static inline uint8_t read##name() {            \
        return read(address);                         \
      }
    #define __GP_REGISTER16(name, address)            \
      static void write##name(uint16_t _data) {       \
        uint8_t buf[2];                               \
        buf[0] = _data >> 8;                          \
        buf[1] = _data & 0xFF;                        \
        write(address, buf, 2);                       \
      }                                               \
      static uint16_t read##name() {                  \
        uint8_t buf[2];                               \
        read(address, buf, 2);                        \
        return (buf[0] << 8) | buf[1];                \
      }
    #define __GP_REGISTER_N(name, address, size)      \
      static uint16_t write##name(const uint8_t *_buff) {   \
        return write(address, _buff, size);           \
      }                                               \
      static uint16_t read##name(uint8_t *_buff) {    \
        return read(address, _buff, size);            \
      }
      static W5100Linkstatus getLinkStatus();
     
    public:
      __GP_REGISTER8 (MR,     0x0000);    // Mode
      __GP_REGISTER_N(GAR,    0x0001, 4); // Gateway IP address
      __GP_REGISTER_N(SUBR,   0x0005, 4); // Subnet mask address
      __GP_REGISTER_N(SHAR,   0x0009, 6); // Source MAC address
      __GP_REGISTER_N(SIPR,   0x000F, 4); // Source IP address
      __GP_REGISTER8 (IR,     0x0015);    // Interrupt
      __GP_REGISTER8 (IMR,    0x0016);    // Interrupt Mask
      __GP_REGISTER16(RTR,    0x0017);    // Timeout address
      __GP_REGISTER8 (RCR,    0x0019);    // Retry count
      __GP_REGISTER8 (RMSR,   0x001A);    // Receive memory size (W5100 only)
      __GP_REGISTER8 (TMSR,   0x001B);    // Transmit memory size (W5100 only)
      __GP_REGISTER8 (PATR,   0x001C);    // Authentication type address in PPPoE mode
      __GP_REGISTER8 (PTIMER, 0x0028);    // PPP LCP Request Timer
      __GP_REGISTER8 (PMAGIC, 0x0029);    // PPP LCP Magic Number
      __GP_REGISTER_N(UIPR,   0x002A, 4); // Unreachable IP address in UDP mode (W5100 only)
      __GP_REGISTER16(UPORT,  0x002E);    // Unreachable Port address in UDP mode (W5100 only)
      __GP_REGISTER8 (VERSIONR_W5200,0x001F);   // Chip Version Register (W5200 only)
      __GP_REGISTER8 (VERSIONR_W5500,0x0039);   // Chip Version Register (W5500 only)
      __GP_REGISTER8 (PSTATUS_W5200,     0x0035);    // PHY Status
      __GP_REGISTER8 (PHYCFGR_W5500,     0x002E);    // PHY Configuration register, default: 10111xxx
     
    #undef __GP_REGISTER8
    #undef __GP_REGISTER16
    #undef __GP_REGISTER_N
     
      // W5100 Socket registers
      // ----------------------
    private:
      static uint16_t CH_BASE(void) {
        //if (chip == 55) return 0x1000;
        //if (chip == 52) return 0x4000;
        //return 0x0400;
        return CH_BASE_MSB << 8;
      }
      static uint8_t CH_BASE_MSB; // 1 redundant byte, saves ~80 bytes code on AVR
      static const uint16_t CH_SIZE = 0x0100;
     
      static inline uint8_t readSn(SOCKET s, uint16_t addr) {
        return read(CH_BASE() + s * CH_SIZE + addr);
      }
      static inline uint8_t writeSn(SOCKET s, uint16_t addr, uint8_t data) {
        return write(CH_BASE() + s * CH_SIZE + addr, data);
      }
      static inline uint16_t readSn(SOCKET s, uint16_t addr, uint8_t *buf, uint16_t len) {
        return read(CH_BASE() + s * CH_SIZE + addr, buf, len);
      }
      static inline uint16_t writeSn(SOCKET s, uint16_t addr, uint8_t *buf, uint16_t len) {
        return write(CH_BASE() + s * CH_SIZE + addr, buf, len);
      }
     
    #define __SOCKET_REGISTER8(name, address)                    \
      static inline void write##name(SOCKET _s, uint8_t _data) { \
        writeSn(_s, address, _data);                             \
      }                                                          \
      static inline uint8_t read##name(SOCKET _s) {              \
        return readSn(_s, address);                              \
      }
    #define __SOCKET_REGISTER16(name, address)                   \
      static void write##name(SOCKET _s, uint16_t _data) {       \
        uint8_t buf[2];                                          \
        buf[0] = _data >> 8;                                     \
        buf[1] = _data & 0xFF;                                   \
        writeSn(_s, address, buf, 2);                            \
      }                                                          \
      static uint16_t read##name(SOCKET _s) {                    \
        uint8_t buf[2];                                          \
        readSn(_s, address, buf, 2);                             \
        return (buf[0] << 8) | buf[1];                           \
      }
    #define __SOCKET_REGISTER_N(name, address, size)             \
      static uint16_t write##name(SOCKET _s, uint8_t *_buff) {   \
        return writeSn(_s, address, _buff, size);                \
      }                                                          \
      static uint16_t read##name(SOCKET _s, uint8_t *_buff) {    \
        return readSn(_s, address, _buff, size);                 \
      }
     
    public:
      __SOCKET_REGISTER8(SnMR,        0x0000)        // Mode
      __SOCKET_REGISTER8(SnCR,        0x0001)        // Command
      __SOCKET_REGISTER8(SnIR,        0x0002)        // Interrupt
      __SOCKET_REGISTER8(SnSR,        0x0003)        // Status
      __SOCKET_REGISTER16(SnPORT,     0x0004)        // Source Port
      __SOCKET_REGISTER_N(SnDHAR,     0x0006, 6)     // Destination Hardw Addr
      __SOCKET_REGISTER_N(SnDIPR,     0x000C, 4)     // Destination IP Addr
      __SOCKET_REGISTER16(SnDPORT,    0x0010)        // Destination Port
      __SOCKET_REGISTER16(SnMSSR,     0x0012)        // Max Segment Size
      __SOCKET_REGISTER8(SnPROTO,     0x0014)        // Protocol in IP RAW Mode
      __SOCKET_REGISTER8(SnTOS,       0x0015)        // IP TOS
      __SOCKET_REGISTER8(SnTTL,       0x0016)        // IP TTL
      __SOCKET_REGISTER8(SnRX_SIZE,   0x001E)        // RX Memory Size (W5200 only)
      __SOCKET_REGISTER8(SnTX_SIZE,   0x001F)        // RX Memory Size (W5200 only)
      __SOCKET_REGISTER16(SnTX_FSR,   0x0020)        // TX Free Size
      __SOCKET_REGISTER16(SnTX_RD,    0x0022)        // TX Read Pointer
      __SOCKET_REGISTER16(SnTX_WR,    0x0024)        // TX Write Pointer
      __SOCKET_REGISTER16(SnRX_RSR,   0x0026)        // RX Free Size
      __SOCKET_REGISTER16(SnRX_RD,    0x0028)        // RX Read Pointer
      __SOCKET_REGISTER16(SnRX_WR,    0x002A)        // RX Write Pointer (supported?)
     
    #undef __SOCKET_REGISTER8
    #undef __SOCKET_REGISTER16
    #undef __SOCKET_REGISTER_N
     
    private:
      //static uint8_t chip;
      static uint8_t ss_pin;
      static uint8_t softReset(void);
    //  static uint8_t isW5100(void);
    //  static uint8_t isW5200(void);
    //  static uint8_t isW5500(void);
     
    public:
      //static uint8_t getChip(void) { return chip; }
    #ifdef ETHERNET_LARGE_BUFFERS
      static uint16_t SSIZE;
      static uint16_t SMASK;
    #else
      static const uint16_t SSIZE = 2048;
      static const uint16_t SMASK = 0x07FF;
    #endif
      static uint16_t SBASE(uint8_t socknum) {
        //if (chip == 51) {
        //  return socknum * SSIZE + 0x4000;
        //} else {
          return socknum * SSIZE + 0x8000;
        //}
      }
      static uint16_t RBASE(uint8_t socknum) {
        //if (chip == 51) {
        //  return socknum * SSIZE + 0x6000;
        //} else {
          return socknum * SSIZE + 0xC000;
        //}
      }
     
      static bool hasOffsetAddressMapping(void) {
        //if (chip == 55) return true;
        //return false;
    	return true;
      }
      static void setSS(uint8_t pin) { ss_pin = pin; }
     
    private:
    #if defined(__AVR__)
    	static volatile uint8_t *ss_pin_reg;
    	static uint8_t ss_pin_mask;
    	inline static void initSS() {
    		ss_pin_reg = portOutputRegister(digitalPinToPort(ss_pin));
    		ss_pin_mask = digitalPinToBitMask(ss_pin);
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		*(ss_pin_reg) &= ~ss_pin_mask;
    	}
    	inline static void resetSS() {
    		*(ss_pin_reg) |= ss_pin_mask;
    	}
    #elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
    	static volatile uint8_t *ss_pin_reg;
    	inline static void initSS() {
    		ss_pin_reg = portOutputRegister(ss_pin);
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		*(ss_pin_reg+256) = 1;
    	}
    	inline static void resetSS() {
    		*(ss_pin_reg+128) = 1;
    	}
    #elif defined(__MKL26Z64__)
    	static volatile uint8_t *ss_pin_reg;
    	static uint8_t ss_pin_mask;
    	inline static void initSS() {
    		ss_pin_reg = portOutputRegister(digitalPinToPort(ss_pin));
    		ss_pin_mask = digitalPinToBitMask(ss_pin);
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		*(ss_pin_reg+8) = ss_pin_mask;
    	}
    	inline static void resetSS() {
    		*(ss_pin_reg+4) = ss_pin_mask;
    	}
    #elif defined(__SAM3X8E__) || defined(__SAM3A8C__) || defined(__SAM3A4C__)
    	static volatile uint32_t *ss_pin_reg;
    	static uint32_t ss_pin_mask;
    	inline static void initSS() {
    		ss_pin_reg = &(digitalPinToPort(ss_pin)->PIO_PER);
    		ss_pin_mask = digitalPinToBitMask(ss_pin);
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		*(ss_pin_reg+13) = ss_pin_mask;
    	}
    	inline static void resetSS() {
    		*(ss_pin_reg+12) = ss_pin_mask;
    	}
    #elif defined(__PIC32MX__)
    	static volatile uint32_t *ss_pin_reg;
    	static uint32_t ss_pin_mask;
    	inline static void initSS() {
    		ss_pin_reg = portModeRegister(digitalPinToPort(ss_pin));
    		ss_pin_mask = digitalPinToBitMask(ss_pin);
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		*(ss_pin_reg+8+1) = ss_pin_mask;
    	}
    	inline static void resetSS() {
    		*(ss_pin_reg+8+2) = ss_pin_mask;
    	}
     
    #elif defined(ARDUINO_ARCH_ESP8266)
    	static volatile uint32_t *ss_pin_reg;
    	static uint32_t ss_pin_mask;
    	inline static void initSS() {
    		ss_pin_reg = (volatile uint32_t*)GPO;
    		ss_pin_mask = 1 << ss_pin;
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		GPOC = ss_pin_mask;
    	}
    	inline static void resetSS() {
    		GPOS = ss_pin_mask;
    	}
     
    #elif defined(__SAMD21G18A__)
    	static volatile uint32_t *ss_pin_reg;
    	static uint32_t ss_pin_mask;
    	inline static void initSS() {
    		ss_pin_reg = portModeRegister(digitalPinToPort(ss_pin));
    		ss_pin_mask = digitalPinToBitMask(ss_pin);
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		*(ss_pin_reg+5) = ss_pin_mask;
    	}
    	inline static void resetSS() {
    		*(ss_pin_reg+6) = ss_pin_mask;
    	}
    #else
    	inline static void initSS() {
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		digitalWrite(ss_pin, LOW);
    	}
    	inline static void resetSS() {
    		digitalWrite(ss_pin, HIGH);
    	}
    #endif
    };
     
    extern W5100Class W5100;
     
    #endif
     
    #ifndef UTIL_H
    #define UTIL_H
     
    #define htons(x) ( (((x)<<8)&0xFF00) | (((x)>>8)&0xFF) )
    #define ntohs(x) htons(x)
     
    #define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \
                       ((x)<< 8 & 0x00FF0000UL) | \
                       ((x)>> 8 & 0x0000FF00UL) | \
                       ((x)>>24 & 0x000000FFUL) )
    #define ntohl(x) htonl(x)
     
    #endif
    Et enfin, RLucas_W5100.cpp, le pur hardware :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    #include <Arduino.h>
    #include "RLucas_Ethernet.h"
    #include "RLucas_w5100.h"
     
    /***************************************************/
    /**            Default SS pin setting             **/
    /***************************************************/
     
    // If variant.h or other headers specifically define the default SS pin for ethernet, use it.
    #if defined(PIN_SPI_SS_ETHERNET_LIB)
    #define SS_PIN_DEFAULT  PIN_SPI_SS_ETHERNET_LIB
     
    // MKR boards default to pin 5 for MKR ETH Pins 8-10 are MOSI/SCK/MISO on MRK, so don't use pin 10
    #elif defined(USE_ARDUINO_MKR_PIN_LAYOUT) || defined(ARDUINO_SAMD_MKRZERO) || defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRFox1200) || defined(ARDUINO_SAMD_MKRGSM1400) || defined(ARDUINO_SAMD_MKRWAN1300)
    #define SS_PIN_DEFAULT  5
     
    // For boards using AVR, assume shields with SS on pin 10 will be used.  This allows for Arduino Mega (where
    // SS is pin 53) and Arduino Leonardo (where SS is pin 17) to work by default with Arduino Ethernet Shield R2 & R3.
    #elif defined(__AVR__)
    #define SS_PIN_DEFAULT  10
     
    // If variant.h or other headers define these names use them if none of the other cases match
    #elif defined(PIN_SPI_SS)
    #define SS_PIN_DEFAULT  PIN_SPI_SS
    #elif defined(CORE_SS0_PIN)
    #define SS_PIN_DEFAULT  CORE_SS0_PIN
     
    // As a final fallback, use pin 10
    #else
    #define SS_PIN_DEFAULT  10
    #endif
     
    // W5100 controller instance
    uint8_t  W5100Class::CH_BASE_MSB;
    uint8_t  W5100Class::ss_pin = SS_PIN_DEFAULT;
    #ifdef ETHERNET_LARGE_BUFFERS
    uint16_t W5100Class::SSIZE = 2048;
    uint16_t W5100Class::SMASK = 0x07FF;
    #endif
    W5100Class W5100;
     
    // pointers and bitmasks for optimized SS pin
    #if defined(__AVR__)
      volatile uint8_t * W5100Class::ss_pin_reg;
      uint8_t W5100Class::ss_pin_mask;
    #elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
      volatile uint8_t * W5100Class::ss_pin_reg;
    #elif defined(__MKL26Z64__)
      volatile uint8_t * W5100Class::ss_pin_reg;
      uint8_t W5100Class::ss_pin_mask;
    #elif defined(__SAM3X8E__) || defined(__SAM3A8C__) || defined(__SAM3A4C__)
      volatile uint32_t * W5100Class::ss_pin_reg;
      uint32_t W5100Class::ss_pin_mask;
    #elif defined(__PIC32MX__)
      volatile uint32_t * W5100Class::ss_pin_reg;
      uint32_t W5100Class::ss_pin_mask;
    #elif defined(ARDUINO_ARCH_ESP8266)
      volatile uint32_t * W5100Class::ss_pin_reg;
      uint32_t W5100Class::ss_pin_mask;
    #elif defined(__SAMD21G18A__)
      volatile uint32_t * W5100Class::ss_pin_reg;
      uint32_t W5100Class::ss_pin_mask;
    #endif
     
    uint8_t W5100Class::init(void) {
    	static bool initialized = false;
    	uint8_t i;
    	if (initialized) return 1;
    	// Many Ethernet shields have a CAT811 or similar reset chip connected to W5100 or W5200 chips.  The W5200 will not work at
    	// all, and may even drive its MISO pin, until given an active low reset pulse!  The CAT811 has a 240 ms typical pulse length, and
    	// a 400 ms worst case maximum pulse length.  MAX811 has a worst case maximum 560 ms pulse length.  This delay is meant to wait
    	// until the reset pulse is ended.  If your hardware has a shorter reset time, this can be edited or removed.
    	//delay(560);
    	//delay(400);
    	SPI.begin();
    	initSS();
    	resetSS();
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	if (!softReset()) return 0;
    	writeMR(0x08);
    	if (readMR() != 0x08) return 0;
    	writeMR(0x10);
    	if (readMR() != 0x10) return 0;
    	writeMR(0x00);
    	if (readMR() != 0x00) return 0;
    	int ver = readVERSIONR_W5500();
    	if (ver != 4) return 0;
    	CH_BASE_MSB = 0x10;
    #ifdef ETHERNET_LARGE_BUFFERS
    #if MAX_SOCK_NUM <= 1
    	SSIZE = 16384;
    #elif MAX_SOCK_NUM <= 2
    	SSIZE = 8192;
    #elif MAX_SOCK_NUM <= 4
    	SSIZE = 4096;
    #else
    	SSIZE = 2048;
    #endif
    	SMASK = SSIZE - 1;
    	for (i=0; i<MAX_SOCK_NUM; i++) {
    		writeSnRX_SIZE(i, SSIZE >> 10);
    		writeSnTX_SIZE(i, SSIZE >> 10);
    	}
    	for (; i<8; i++) {
    		writeSnRX_SIZE(i, 0);
    		writeSnTX_SIZE(i, 0);
    	}
    #endif
    	SPI.endTransaction();
    	initialized = true;
    	return 1; // successful init
    }
     
    // Soft reset the Wiznet chip, by writing to its MR register reset bit
    uint8_t W5100Class::softReset(void) {
    	uint16_t count=0;
    	// write to reset bit
    	writeMR(0x80);
    	// then wait for soft reset to complete
    	do {
    		uint8_t mr = readMR();
    		if (mr == 0) return 1;
    		delay(1);
    	} while (++count < 20);
    	return 0;
    }
     
    W5100Linkstatus W5100Class::getLinkStatus() {
    	uint8_t phystatus;
    	if (!init()) return UNKNOWN;
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	phystatus = readPHYCFGR_W5500();
    	SPI.endTransaction();
    	if (phystatus & 0x01) return LINK_ON;
    	return LINK_OFF;
    }
     
    uint16_t W5100Class::write(uint16_t addr, const uint8_t *buf, uint16_t len) {
    	uint8_t cmd[8];
    	setSS();
    	if (addr < 0x100) {
    		// common registers 00nn
    		cmd[0] = 0;
    		cmd[1] = addr & 0xFF;
    		cmd[2] = 0x04;
    	} else if (addr < 0x8000) {
    		// socket registers  10nn, 11nn, 12nn, 13nn, etc
    		cmd[0] = 0;
    		cmd[1] = addr & 0xFF;
    		cmd[2] = ((addr >> 3) & 0xE0) | 0x0C;
    	} else if (addr < 0xC000) {
    		// transmit buffers  8000-87FF, 8800-8FFF, 9000-97FF, etc
    		//  10## #nnn nnnn nnnn
    		cmd[0] = addr >> 8;
    		cmd[1] = addr & 0xFF;
    		#if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1
    		cmd[2] = 0x14;                       // 16K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2
    		cmd[2] = ((addr >> 8) & 0x20) | 0x14; // 8K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4
    		cmd[2] = ((addr >> 7) & 0x60) | 0x14; // 4K buffers
    		#else
    		cmd[2] = ((addr >> 6) & 0xE0) | 0x14; // 2K buffers
    		#endif
    	} else {
    		// receive buffers
    		cmd[0] = addr >> 8;
    		cmd[1] = addr & 0xFF;
    		#if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1
    		cmd[2] = 0x1C;                       // 16K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2
    		cmd[2] = ((addr >> 8) & 0x20) | 0x1C; // 8K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4
    		cmd[2] = ((addr >> 7) & 0x60) | 0x1C; // 4K buffers
    		#else
    		cmd[2] = ((addr >> 6) & 0xE0) | 0x1C; // 2K buffers
    		#endif
    	}
    	if (len <= 5) {
    		for (uint8_t i=0; i < len; i++) {
    			cmd[i + 3] = buf[i];
    		}
    		SPI.transfer(cmd, len + 3);
    	} else {
    		SPI.transfer(cmd, 3);
    #ifdef SPI_HAS_TRANSFER_BUF
    		SPI.transfer(buf, NULL, len);
    #else
    		// TODO: copy 8 bytes at a time to cmd[] and block transfer
    		for (uint16_t i=0; i < len; i++) {
    			SPI.transfer(buf[i]);
    		}
    #endif
    	}
    	resetSS();
    	return len;
    }
     
    uint16_t W5100Class::read(uint16_t addr, uint8_t *buf, uint16_t len) {
    	uint8_t cmd[4];
    	setSS();
    	if (addr < 0x100) {
    		// common registers 00nn
    		cmd[0] = 0;
    		cmd[1] = addr & 0xFF;
    		cmd[2] = 0x00;
    	} else if (addr < 0x8000) {
    		// socket registers  10nn, 11nn, 12nn, 13nn, etc
    		cmd[0] = 0;
    		cmd[1] = addr & 0xFF;
    		cmd[2] = ((addr >> 3) & 0xE0) | 0x08;
    	} else if (addr < 0xC000) {
    		// transmit buffers  8000-87FF, 8800-8FFF, 9000-97FF, etc
    		//  10## #nnn nnnn nnnn
    		cmd[0] = addr >> 8;
    		cmd[1] = addr & 0xFF;
    		#if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1
    		cmd[2] = 0x10;                       // 16K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2
    		cmd[2] = ((addr >> 8) & 0x20) | 0x10; // 8K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4
    		cmd[2] = ((addr >> 7) & 0x60) | 0x10; // 4K buffers
    		#else
    		cmd[2] = ((addr >> 6) & 0xE0) | 0x10; // 2K buffers
    		#endif
    	} else {
    		// receive buffers
    		cmd[0] = addr >> 8;
    		cmd[1] = addr & 0xFF;
    		#if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1
    		cmd[2] = 0x18;                       // 16K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2
    		cmd[2] = ((addr >> 8) & 0x20) | 0x18; // 8K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4
    		cmd[2] = ((addr >> 7) & 0x60) | 0x18; // 4K buffers
    		#else
    		cmd[2] = ((addr >> 6) & 0xE0) | 0x18; // 2K buffers
    		#endif
    	}
    	SPI.transfer(cmd, 3);
    	memset(buf, 0, len);
    	SPI.transfer(buf, len);
    	resetSS();
    	return len;
    }
     
    void W5100Class::execCmdSn(SOCKET s, SockCMD _cmd) {
    	// Send command to socket
    	writeSnCR(s, _cmd);
    	// Wait for command to complete
    	while (readSnCR(s)) ;
    }
    Quelques remarques importantes :
    - Tous ces codes ont été simplifiés pour fonctionner seulement avec le W5500 (cela m'a permis d'économiser pas mal de ROM)
    - On voit des "TODO" et ils sont d'origine dans la librairie Arduino, ils ne viennent pas de moi...
    - On voit que certaines erreurs ne sont pas gérées, par exemple socketSend() retourne le nombre de caractères écrits, qui peut être inférieur à celui des caractères envoyés, et la fonction write() de Ethernet_Server n'en tiens pas compte...

    La librairie n'a pas été terminée à 100% ; certes elle fonctionne mais leurs auteurs ne sont pas allé au bout.
    C'est vraiment dommage ça pénalise les utilisateurs et les oblige à utiliser un Arduino avec plus de mémoire alors qu'ils ne devrait pas si la librairie étaient plus optimale...

    Le gros du travail sera à faire dans RLucas_socket.cpp, et probablement dans RLucas_W5100.cpp

    Le fichier RLucas_EthernetServer.cpp est juste "le point d'entrée", il suffit d'ajouter une fonction "flush()" qu'il suffira d'appeller après une série de server.print() ou de server.write()

    Il va certainement falloir éplucher la datasheet du W5500 https://cdn.sparkfun.com/datasheets/...t_v1.0.2_1.pdf

    A bientôt

  4. #4
    Membre chevronné Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    999
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 999
    Par défaut
    Bonjour,

    alors j'ai réussi le code est même légèrement plus compact et j'ai pu rendre Ethernet indépendant de Print.h et Stream.h

    en plus de client.write(), il y a maintenant 3 fonctions supplémentaires :

    client.write_begin(const uint8_t *buf, size_t size)
    client.write_continue(const uint8_t *buf, size_t size)
    client.write_end(const uint8_t *buf, size_t size)

    Quand vous voulez écrire quelque chose au client, il faut d'abord appeler une fois et une seule client.write_begin()
    Ensuite, autant de fois que vous voulez client.write_continue()
    Puis, il faut terminer en appelant une fois et une seule client.write_end()

    La fonction client.write_end() envoi toutes les données écrites depuis l'appel de client.write_begin() en un seul paquet TCP/IP

    Il faut faire attention :
    - à ne pas dépasser la capacité du buffer du chip W5500 (rien n'interdit de faire plus séries de write_begin()...write_end()
    - à appeler les fonctions dans le bon ordre et le bon nombre de fois
    - entre client.write_begin() et client.write_end() n'utilisez pas le bus SPI, ni d'autres fonctions Ethernet cela va de soi
    Le but de ces fonctions d'envoi "séparées", c'est d'éviter la fragmentation de paquets TCP/IP sans être obligé d'avoir un gros buffer en RAM.
    Ainsi, même s'il est possible de faire des choses entre chaque fonction, il est logique qu'elles s'enchaînent rapidement.

    Ajouter des tests pour éviter tout plantage en cas de mauvais usage de ces fonctions ferait gonfler la taille du programme en ROM et nuirait aux performances.
    C'est au programmeur à faire attention, c'est de l'embarqué et des fonctions qui pilotent du hardware.


    Attention également, code compatible uniquement avec W5500 (utilisé par le Shield Ethernet 2 Arduino officiel)

    Voici le code complet :

    RLucas_Ethernet.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    #ifndef ethernet_h_
    #define ethernet_h_
     
    // Configure the maximum number of sockets to support.  W5100 chips can have up to 4 sockets.  W5200 & W5500 can have up to 8 sockets.  Several bytes
    // of RAM are used for each socket.  Reducing the maximum can save RAM, but you are limited to fewer simultaneous connections.
    #if defined(RAMEND) && defined(RAMSTART) && ((RAMEND - RAMSTART) <= 2048)
    #define MAX_SOCK_NUM 4
    #else
    #define MAX_SOCK_NUM 8
    #endif
     
    // By default, each socket uses 2K buffers inside the Wiznet chip.  If MAX_SOCK_NUM is set to fewer than the chip's maximum, uncommenting
    // this will use larger buffers within the Wiznet chip.  Large buffers can really help with UDP protocols like Artnet.  In theory larger
    // buffers should allow faster TCP over high-latency links, but this does not always seem to work in practice (maybe Wiznet bugs?)
    //#define ETHERNET_LARGE_BUFFERS
     
    #include <Arduino.h>
    #include "RLucas_IPAddress.h"
     
    class RLucas_EthernetClient;
    class RLucas_EthernetServer;
     
    class RLucas_EthernetClass {
    public:
    	static int begin(uint8_t *mac, unsigned long timeout = 60000, unsigned long responseTimeout = 4000);
    	static void begin(uint8_t *mac, RLucas_IPAddress ip);
    	static void init(uint8_t sspin = 10);
     
    	void setMACAddress(const uint8_t *mac_address);
    	void setSubnetMask(const RLucas_IPAddress subnet);
    	void setRetransmissionCount(uint8_t num);
     
    	friend class RLucas_EthernetClient;
    	friend class RLucas_EthernetServer;
    private:
    	static uint8_t socketBegin(uint8_t protocol, uint16_t port);
    	static uint8_t socketStatus(uint8_t s);
    	static void socketClose(uint8_t s);
    	static void socketConnect(uint8_t s, uint8_t * addr, uint16_t port);
    	static void socketDisconnect(uint8_t s);
    	static uint8_t socketListen(uint8_t s);
     
    	// Send data (TCP)
    	static void socketSend_Begin(uint8_t s, const uint8_t * buf, uint16_t len);
    	static void socketSend_Continue(const uint8_t * buf, uint16_t len);
    	static void socketSend_End(const uint8_t * buf, uint16_t len);
     
    	static uint16_t socketSendAvailable(uint8_t s);
    	// Receive data (TCP)
    	static int socketRecv(uint8_t s, uint8_t * buf, int16_t len);
    	static uint16_t socketRecvAvailable(uint8_t s);
    	static uint8_t socketPeek(uint8_t s);
    };
     
    extern RLucas_EthernetClass Ethernet;
     
    class RLucas_EthernetClient {
    public:
    	RLucas_EthernetClient() : sockindex(MAX_SOCK_NUM), _timeout(1000) { }
    	RLucas_EthernetClient(uint8_t s) : sockindex(s), _timeout(1000) { }
    	virtual int connect(RLucas_IPAddress ip, uint16_t port);
    	virtual void write(const uint8_t *buf, size_t size);
    	virtual void write_begin(const uint8_t *buf, size_t size);
    	virtual void write_continue(const uint8_t *buf, size_t size);
    	virtual void write_end(const uint8_t *buf, size_t size);
    	virtual int available();
    	virtual int read();
    	virtual int read(uint8_t *buf, size_t size);
    	virtual int peek();
    	virtual uint8_t connected();
    	virtual operator bool() { return sockindex < MAX_SOCK_NUM; }
    	virtual void stop();
    	//virtual RLucas_IPAddress remoteIP();
    	virtual byte IPLastByte();
    	friend class RLucas_EthernetServer;
    //protected:
    	uint8_t* rawIPAddress(RLucas_IPAddress& addr) { return addr.raw_address(); };
    private:
    	uint8_t sockindex; // MAX_SOCK_NUM means client not in use
    	uint16_t _timeout;
    };
     
    class RLucas_EthernetServer {
    private:
    	uint16_t _port;
    public:
    	RLucas_EthernetServer(uint16_t port) : _port(port) { }
    	RLucas_EthernetClient available();
    	RLucas_EthernetClient accept();
    	virtual void begin();
    	virtual size_t write(const uint8_t *buf, size_t size);
    	// TODO: make private when socket allocation moves to RLucas_EthernetClass
    	static uint16_t server_port[MAX_SOCK_NUM];
    };
     
    #endif
    RLucas_IPAddress.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    #ifndef IPAddress_h
    #define IPAddress_h
    #include <stdint.h>
    class RLucas_IPAddress {
    private:
        union {
    	uint8_t bytes[4];  // IPv4 address
    	uint32_t dword;
        } _address;
        // Access the raw byte array containing the address.  Because this returns a pointer to the internal structure rather than a copy of
    	// the address this function should only be used when you know that the usage of the returned uint8_t* will be transient and not stored.
        uint8_t* raw_address() { return _address.bytes; };
     
    public:
        RLucas_IPAddress(uint32_t address);
        RLucas_IPAddress(const uint8_t *address);
     
        // Overloaded index operator to allow getting and setting individual octets of the address
        uint8_t operator[](int index) const { return _address.bytes[index]; };
        uint8_t& operator[](int index) { return _address.bytes[index]; };
     
        // Overloaded copy operators to allow initialisation of RLucas_IPAddress objects from other types
        RLucas_IPAddress& operator=(const uint8_t *address);
        RLucas_IPAddress& operator=(uint32_t address);
     
        friend class RLucas_EthernetClass;
        friend class RLucas_EthernetClient;
    };
    #endif
    RLucas_w5100.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    #ifndef	W5100_H_INCLUDED
    #define	W5100_H_INCLUDED
     
    #include <Arduino.h>
    #include <SPI.h>
     
    // Safe for all chips
    #define SPI_ETHERNET_SETTINGS SPISettings(14000000, MSBFIRST, SPI_MODE0)
    //#define SPI_ETHERNET_SETTINGS SPISettings(1000, MSBFIRST, SPI_MODE0)
     
    // Safe for W5200 and W5500, but too fast for W5100
    // Uncomment this if you know you'll never need W5100 support.
    //  Higher SPI clock only results in faster transfer to hosts on a LAN
    //  or with very low packet latency.  With ordinary internet latency,
    //  the TCP window size & packet loss determine your overall speed.
    //#define SPI_ETHERNET_SETTINGS SPISettings(30000000, MSBFIRST, SPI_MODE0)
    //#define SPI_ETHERNET_SETTINGS SPISettings(10000, MSBFIRST, SPI_MODE0)
     
    // Require Ethernet.h, because we need MAX_SOCK_NUM
    #ifndef ethernet_h_
    #error "Ethernet.h must be included before w5100.h"
    #endif
     
    // Arduino 101's SPI can not run faster than 8 MHz.
    #if defined(ARDUINO_ARCH_ARC32)
    #undef SPI_ETHERNET_SETTINGS
    #define SPI_ETHERNET_SETTINGS SPISettings(8000000, MSBFIRST, SPI_MODE0)
    #endif
     
    // Arduino Zero can't use W5100-based shields faster than 8 MHz
    // https://github.com/arduino-libraries/Ethernet/issues/37#issuecomment-408036848
    // W5500 does seem to work at 12 MHz.  Delete this if only using W5500
    #if defined(__SAMD21G18A__)
    #undef SPI_ETHERNET_SETTINGS
    #define SPI_ETHERNET_SETTINGS SPISettings(8000000, MSBFIRST, SPI_MODE0)
    #endif
     
    typedef uint8_t SOCKET;
     
    class SnMR {
    public:
      static const uint8_t CLOSE  = 0x00;
      static const uint8_t TCP    = 0x21;
      static const uint8_t UDP    = 0x02;
      static const uint8_t IPRAW  = 0x03;
      static const uint8_t MACRAW = 0x04;
      static const uint8_t PPPOE  = 0x05;
      static const uint8_t ND     = 0x20;
      static const uint8_t MULTI  = 0x80;
    };
     
    enum SockCMD {
      Sock_OPEN      = 0x01,
      Sock_LISTEN    = 0x02,
      Sock_CONNECT   = 0x04,
      Sock_DISCON    = 0x08,
      Sock_CLOSE     = 0x10,
      Sock_SEND      = 0x20,
      Sock_SEND_MAC  = 0x21,
      Sock_SEND_KEEP = 0x22,
      Sock_RECV      = 0x40
    };
     
    class SnIR {
    public:
      static const uint8_t SEND_OK = 0x10;
      static const uint8_t TIMEOUT = 0x08;
      static const uint8_t RECV    = 0x04;
      static const uint8_t DISCON  = 0x02;
      static const uint8_t CON     = 0x01;
    };
     
    class SnSR {
    public:
      static const uint8_t CLOSED      = 0x00;
      static const uint8_t INIT        = 0x13;
      static const uint8_t LISTEN      = 0x14;
      static const uint8_t SYNSENT     = 0x15;
      static const uint8_t SYNRECV     = 0x16;
      static const uint8_t ESTABLISHED = 0x17;
      static const uint8_t FIN_WAIT    = 0x18;
      static const uint8_t CLOSING     = 0x1A;
      static const uint8_t TIME_WAIT   = 0x1B;
      static const uint8_t CLOSE_WAIT  = 0x1C;
      static const uint8_t LAST_ACK    = 0x1D;
      static const uint8_t UDP         = 0x22;
      static const uint8_t IPRAW       = 0x32;
      static const uint8_t MACRAW      = 0x42;
      static const uint8_t PPPOE       = 0x5F;
    };
     
    class IPPROTO {
    public:
      static const uint8_t IP   = 0;
      static const uint8_t ICMP = 1;
      static const uint8_t IGMP = 2;
      static const uint8_t GGP  = 3;
      static const uint8_t TCP  = 6;
      static const uint8_t PUP  = 12;
      static const uint8_t UDP  = 17;
      static const uint8_t IDP  = 22;
      static const uint8_t ND   = 77;
      static const uint8_t RAW  = 255;
    };
     
    enum W5100Linkstatus {
      UNKNOWN,
      LINK_ON,
      LINK_OFF
    };
     
    class W5100Class {
     
    public:
      static uint8_t init(void);
     
      inline void setGatewayIp(const uint8_t * addr) { writeGAR(addr); }
      inline void getGatewayIp(uint8_t * addr) { readGAR(addr); }
     
      inline void setSubnetMask(const uint8_t * addr) { writeSUBR(addr); }
      inline void getSubnetMask(uint8_t * addr) { readSUBR(addr); }
     
      inline void setMACAddress(const uint8_t * addr) { writeSHAR(addr); }
      inline void getMACAddress(uint8_t * addr) { readSHAR(addr); }
     
      inline void setIPAddress(const uint8_t * addr) { writeSIPR(addr); }
      inline void getIPAddress(uint8_t * addr) { readSIPR(addr); }
     
      inline void setRetransmissionTime(uint16_t timeout) { writeRTR(timeout); }
      inline void setRetransmissionCount(uint8_t retry) { writeRCR(retry); }
     
      static void execCmdSn(SOCKET s, SockCMD _cmd);
     
      // W5100 Registers
      // ---------------
    //private:
    public:
      static uint16_t write(uint16_t addr, const uint8_t *buf, uint16_t len);
      static uint8_t write(uint16_t addr, uint8_t data) {
        return write(addr, &data, 1);
      }
      static uint16_t read(uint16_t addr, uint8_t *buf, uint16_t len);
      static uint8_t read(uint16_t addr) {
        uint8_t data;
        read(addr, &data, 1);
        return data;
      }
     
    #define __GP_REGISTER8(name, address)             \
      static inline void write##name(uint8_t _data) { \
        write(address, _data);                        \
      }                                               \
      static inline uint8_t read##name() {            \
        return read(address);                         \
      }
    #define __GP_REGISTER16(name, address)            \
      static void write##name(uint16_t _data) {       \
        uint8_t buf[2];                               \
        buf[0] = _data >> 8;                          \
        buf[1] = _data & 0xFF;                        \
        write(address, buf, 2);                       \
      }                                               \
      static uint16_t read##name() {                  \
        uint8_t buf[2];                               \
        read(address, buf, 2);                        \
        return (buf[0] << 8) | buf[1];                \
      }
    #define __GP_REGISTER_N(name, address, size)      \
      static uint16_t write##name(const uint8_t *_buff) {   \
        return write(address, _buff, size);           \
      }                                               \
      static uint16_t read##name(uint8_t *_buff) {    \
        return read(address, _buff, size);            \
      }
      static W5100Linkstatus getLinkStatus();
     
    public:
      __GP_REGISTER8 (MR,     0x0000);    // Mode
      __GP_REGISTER_N(GAR,    0x0001, 4); // Gateway IP address
      __GP_REGISTER_N(SUBR,   0x0005, 4); // Subnet mask address
      __GP_REGISTER_N(SHAR,   0x0009, 6); // Source MAC address
      __GP_REGISTER_N(SIPR,   0x000F, 4); // Source IP address
      __GP_REGISTER8 (IR,     0x0015);    // Interrupt
      __GP_REGISTER8 (IMR,    0x0016);    // Interrupt Mask
      __GP_REGISTER16(RTR,    0x0017);    // Timeout address
      __GP_REGISTER8 (RCR,    0x0019);    // Retry count
      __GP_REGISTER8 (RMSR,   0x001A);    // Receive memory size (W5100 only)
      __GP_REGISTER8 (TMSR,   0x001B);    // Transmit memory size (W5100 only)
      __GP_REGISTER8 (PATR,   0x001C);    // Authentication type address in PPPoE mode
      __GP_REGISTER8 (PTIMER, 0x0028);    // PPP LCP Request Timer
      __GP_REGISTER8 (PMAGIC, 0x0029);    // PPP LCP Magic Number
      __GP_REGISTER_N(UIPR,   0x002A, 4); // Unreachable IP address in UDP mode (W5100 only)
      __GP_REGISTER16(UPORT,  0x002E);    // Unreachable Port address in UDP mode (W5100 only)
      __GP_REGISTER8 (VERSIONR_W5200,0x001F);   // Chip Version Register (W5200 only)
      __GP_REGISTER8 (VERSIONR_W5500,0x0039);   // Chip Version Register (W5500 only)
      __GP_REGISTER8 (PSTATUS_W5200,     0x0035);    // PHY Status
      __GP_REGISTER8 (PHYCFGR_W5500,     0x002E);    // PHY Configuration register, default: 10111xxx
     
    #undef __GP_REGISTER8
    #undef __GP_REGISTER16
    #undef __GP_REGISTER_N
     
      // W5100 Socket registers
      // ----------------------
    private:
      static uint16_t CH_BASE(void) {
        //if (chip == 55) return 0x1000;
        //if (chip == 52) return 0x4000;
        //return 0x0400;
        return CH_BASE_MSB << 8;
      }
      static uint8_t CH_BASE_MSB; // 1 redundant byte, saves ~80 bytes code on AVR
      static const uint16_t CH_SIZE = 0x0100;
     
      static inline uint8_t readSn(SOCKET s, uint16_t addr) {
        return read(CH_BASE() + s * CH_SIZE + addr);
      }
      static inline uint8_t writeSn(SOCKET s, uint16_t addr, uint8_t data) {
        return write(CH_BASE() + s * CH_SIZE + addr, data);
      }
      static inline uint16_t readSn(SOCKET s, uint16_t addr, uint8_t *buf, uint16_t len) {
        return read(CH_BASE() + s * CH_SIZE + addr, buf, len);
      }
      static inline uint16_t writeSn(SOCKET s, uint16_t addr, uint8_t *buf, uint16_t len) {
        return write(CH_BASE() + s * CH_SIZE + addr, buf, len);
      }
     
    #define __SOCKET_REGISTER8(name, address)                    \
      static inline void write##name(SOCKET _s, uint8_t _data) { \
        writeSn(_s, address, _data);                             \
      }                                                          \
      static inline uint8_t read##name(SOCKET _s) {              \
        return readSn(_s, address);                              \
      }
    #define __SOCKET_REGISTER16(name, address)                   \
      static void write##name(SOCKET _s, uint16_t _data) {       \
        uint8_t buf[2];                                          \
        buf[0] = _data >> 8;                                     \
        buf[1] = _data & 0xFF;                                   \
        writeSn(_s, address, buf, 2);                            \
      }                                                          \
      static uint16_t read##name(SOCKET _s) {                    \
        uint8_t buf[2];                                          \
        readSn(_s, address, buf, 2);                             \
        return (buf[0] << 8) | buf[1];                           \
      }
    #define __SOCKET_REGISTER_N(name, address, size)             \
      static uint16_t write##name(SOCKET _s, uint8_t *_buff) {   \
        return writeSn(_s, address, _buff, size);                \
      }                                                          \
      static uint16_t read##name(SOCKET _s, uint8_t *_buff) {    \
        return readSn(_s, address, _buff, size);                 \
      }
     
    public:
      __SOCKET_REGISTER8(SnMR,        0x0000)        // Mode
      __SOCKET_REGISTER8(SnCR,        0x0001)        // Command
      __SOCKET_REGISTER8(SnIR,        0x0002)        // Interrupt
      __SOCKET_REGISTER8(SnSR,        0x0003)        // Status
      __SOCKET_REGISTER16(SnPORT,     0x0004)        // Source Port
      __SOCKET_REGISTER_N(SnDHAR,     0x0006, 6)     // Destination Hardw Addr
      __SOCKET_REGISTER_N(SnDIPR,     0x000C, 4)     // Destination IP Addr
      __SOCKET_REGISTER16(SnDPORT,    0x0010)        // Destination Port
      __SOCKET_REGISTER16(SnMSSR,     0x0012)        // Max Segment Size
      __SOCKET_REGISTER8(SnPROTO,     0x0014)        // Protocol in IP RAW Mode
      __SOCKET_REGISTER8(SnTOS,       0x0015)        // IP TOS
      __SOCKET_REGISTER8(SnTTL,       0x0016)        // IP TTL
      __SOCKET_REGISTER8(SnRX_SIZE,   0x001E)        // RX Memory Size (W5200 only)
      __SOCKET_REGISTER8(SnTX_SIZE,   0x001F)        // RX Memory Size (W5200 only)
      __SOCKET_REGISTER16(SnTX_FSR,   0x0020)        // TX Free Size
      __SOCKET_REGISTER16(SnTX_RD,    0x0022)        // TX Read Pointer
      __SOCKET_REGISTER16(SnTX_WR,    0x0024)        // TX Write Pointer
      __SOCKET_REGISTER16(SnRX_RSR,   0x0026)        // RX Free Size
      __SOCKET_REGISTER16(SnRX_RD,    0x0028)        // RX Read Pointer
      __SOCKET_REGISTER16(SnRX_WR,    0x002A)        // RX Write Pointer (supported?)
     
    #undef __SOCKET_REGISTER8
    #undef __SOCKET_REGISTER16
    #undef __SOCKET_REGISTER_N
     
    private:
      //static uint8_t chip;
      static uint8_t ss_pin;
      static uint8_t softReset(void);
    //  static uint8_t isW5100(void);
    //  static uint8_t isW5200(void);
    //  static uint8_t isW5500(void);
     
    public:
      //static uint8_t getChip(void) { return chip; }
    #ifdef ETHERNET_LARGE_BUFFERS
      static uint16_t SSIZE;
      static uint16_t SMASK;
    #else
      static const uint16_t SSIZE = 2048;
      static const uint16_t SMASK = 0x07FF;
    #endif
      static uint16_t SBASE(uint8_t socknum) {
        //if (chip == 51) {
        //  return socknum * SSIZE + 0x4000;
        //} else {
          return socknum * SSIZE + 0x8000;
        //}
      }
      static uint16_t RBASE(uint8_t socknum) {
        //if (chip == 51) {
        //  return socknum * SSIZE + 0x6000;
        //} else {
          return socknum * SSIZE + 0xC000;
        //}
      }
     
      static bool hasOffsetAddressMapping(void) {
        //if (chip == 55) return true;
        //return false;
    	return true;
      }
      static void setSS(uint8_t pin) { ss_pin = pin; }
     
    private:
    #if defined(__AVR__)
    	static volatile uint8_t *ss_pin_reg;
    	static uint8_t ss_pin_mask;
    	inline static void initSS() {
    		ss_pin_reg = portOutputRegister(digitalPinToPort(ss_pin));
    		ss_pin_mask = digitalPinToBitMask(ss_pin);
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		*(ss_pin_reg) &= ~ss_pin_mask;
    	}
    	inline static void resetSS() {
    		*(ss_pin_reg) |= ss_pin_mask;
    	}
    #elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
    	static volatile uint8_t *ss_pin_reg;
    	inline static void initSS() {
    		ss_pin_reg = portOutputRegister(ss_pin);
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		*(ss_pin_reg+256) = 1;
    	}
    	inline static void resetSS() {
    		*(ss_pin_reg+128) = 1;
    	}
    #elif defined(__MKL26Z64__)
    	static volatile uint8_t *ss_pin_reg;
    	static uint8_t ss_pin_mask;
    	inline static void initSS() {
    		ss_pin_reg = portOutputRegister(digitalPinToPort(ss_pin));
    		ss_pin_mask = digitalPinToBitMask(ss_pin);
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		*(ss_pin_reg+8) = ss_pin_mask;
    	}
    	inline static void resetSS() {
    		*(ss_pin_reg+4) = ss_pin_mask;
    	}
    #elif defined(__SAM3X8E__) || defined(__SAM3A8C__) || defined(__SAM3A4C__)
    	static volatile uint32_t *ss_pin_reg;
    	static uint32_t ss_pin_mask;
    	inline static void initSS() {
    		ss_pin_reg = &(digitalPinToPort(ss_pin)->PIO_PER);
    		ss_pin_mask = digitalPinToBitMask(ss_pin);
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		*(ss_pin_reg+13) = ss_pin_mask;
    	}
    	inline static void resetSS() {
    		*(ss_pin_reg+12) = ss_pin_mask;
    	}
    #elif defined(__PIC32MX__)
    	static volatile uint32_t *ss_pin_reg;
    	static uint32_t ss_pin_mask;
    	inline static void initSS() {
    		ss_pin_reg = portModeRegister(digitalPinToPort(ss_pin));
    		ss_pin_mask = digitalPinToBitMask(ss_pin);
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		*(ss_pin_reg+8+1) = ss_pin_mask;
    	}
    	inline static void resetSS() {
    		*(ss_pin_reg+8+2) = ss_pin_mask;
    	}
     
    #elif defined(ARDUINO_ARCH_ESP8266)
    	static volatile uint32_t *ss_pin_reg;
    	static uint32_t ss_pin_mask;
    	inline static void initSS() {
    		ss_pin_reg = (volatile uint32_t*)GPO;
    		ss_pin_mask = 1 << ss_pin;
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		GPOC = ss_pin_mask;
    	}
    	inline static void resetSS() {
    		GPOS = ss_pin_mask;
    	}
     
    #elif defined(__SAMD21G18A__)
    	static volatile uint32_t *ss_pin_reg;
    	static uint32_t ss_pin_mask;
    	inline static void initSS() {
    		ss_pin_reg = portModeRegister(digitalPinToPort(ss_pin));
    		ss_pin_mask = digitalPinToBitMask(ss_pin);
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		*(ss_pin_reg+5) = ss_pin_mask;
    	}
    	inline static void resetSS() {
    		*(ss_pin_reg+6) = ss_pin_mask;
    	}
    #else
    	inline static void initSS() {
    		pinMode(ss_pin, OUTPUT);
    	}
    	inline static void setSS() {
    		digitalWrite(ss_pin, LOW);
    	}
    	inline static void resetSS() {
    		digitalWrite(ss_pin, HIGH);
    	}
    #endif
    };
     
    extern W5100Class W5100;
     
    #endif
     
    #ifndef UTIL_H
    #define UTIL_H
     
    #define htons(x) ( (((x)<<8)&0xFF00) | (((x)>>8)&0xFF) )
    #define ntohs(x) htons(x)
     
    #define htonl(x) ( ((x)<<24 & 0xFF000000UL) | \
                       ((x)<< 8 & 0x00FF0000UL) | \
                       ((x)>> 8 & 0x0000FF00UL) | \
                       ((x)>>24 & 0x000000FFUL) )
    #define ntohl(x) htonl(x)
     
    #endif
    RLucas_Ethernet.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    #include <Arduino.h>
    #include "RLucas_Ethernet.h"
    #include "RLucas_w5100.h"
     
    void RLucas_EthernetClass::begin(uint8_t *mac, RLucas_IPAddress ip) {
    	//RLucas_IPAddress subnet(255, 255, 255, 0);
    	RLucas_IPAddress subnet(0x00FFFFFFul); // This is the single time we need RLucas_IPAddress(uint8_t first_octet, uint8_t second_octet, uint8_t third_octet, uint8_t fourth_octet);
    	RLucas_IPAddress dns = ip;
    	dns[3] = 1; // We don't use DNS, but we need to send it to W5100 chip
    	if (W5100.init() == 0) {
    		SPI.endTransaction();
    		return;
    	}
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	W5100.setMACAddress(mac);
    #if ARDUINO > 106 || TEENSYDUINO > 121
    	W5100.setIPAddress(ip._address.bytes);
    	W5100.setGatewayIp(dns._address.bytes);
    	W5100.setSubnetMask(subnet._address.bytes);
    #else
    	W5100.setIPAddress(ip._address);
    	W5100.setGatewayIp(dns._address);
    	W5100.setSubnetMask(subnet._address);
    #endif
    	SPI.endTransaction();
    }
     
    void RLucas_EthernetClass::init(uint8_t sspin) {
    	W5100.setSS(sspin);
    }
     
    void RLucas_EthernetClass::setMACAddress(const uint8_t *mac_address) {
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	W5100.setMACAddress(mac_address);
    	SPI.endTransaction();
    }
     
    void RLucas_EthernetClass::setSubnetMask(const RLucas_IPAddress subnet) {
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	RLucas_IPAddress ip = subnet;
    	W5100.setSubnetMask(ip.raw_address());
    	SPI.endTransaction();
    }
    void RLucas_EthernetClass::setRetransmissionCount(uint8_t num) {
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	W5100.setRetransmissionCount(num);
    	SPI.endTransaction();
    }
     
    RLucas_EthernetClass Ethernet;
    RLucas_EthernetClient.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    #include <Arduino.h>
    #include "RLucas_Ethernet.h"
    #include "RLucas_w5100.h"
     
    int RLucas_EthernetClient::connect(RLucas_IPAddress ip, uint16_t port) {
    	if (sockindex < MAX_SOCK_NUM) {
    		if (Ethernet.socketStatus(sockindex) != SnSR::CLOSED) {
    			Ethernet.socketDisconnect(sockindex); // TODO: should we call stop()?
    		}
    		sockindex = MAX_SOCK_NUM;
    	}
    	// if (ip == RLucas_IPAddress(0ul) || ip == RLucas_IPAddress(0xFFFFFFFFul)) return 0;
    	sockindex = Ethernet.socketBegin(SnMR::TCP, 0);
    	if (sockindex >= MAX_SOCK_NUM) return 0;
    	Ethernet.socketConnect(sockindex, rawIPAddress(ip), port);
    	uint32_t start = millis();
    	while (1) {
    		uint8_t stat = Ethernet.socketStatus(sockindex);
    		if (stat == SnSR::ESTABLISHED) return 1;
    		if (stat == SnSR::CLOSE_WAIT) return 1;
    		if (stat == SnSR::CLOSED) return 0;
    		if (millis() - start > _timeout) break;
    		delay(1);
    	}
    	Ethernet.socketClose(sockindex);
    	sockindex = MAX_SOCK_NUM;
    	return 0;
    }
     
    void RLucas_EthernetClient::write(const uint8_t *buf, size_t size) {
    	write_begin(buf, size);
    	write_end(buf, 0);
    }
     
    void RLucas_EthernetClient::write_begin(const uint8_t *buf, size_t size) {
    	if (sockindex >= MAX_SOCK_NUM) return;
    	Ethernet.socketSend_Begin(sockindex, buf, size);
    }
    void RLucas_EthernetClient::write_continue(const uint8_t *buf, size_t size) {
    	Ethernet.socketSend_Continue(buf, size);
    }
    void RLucas_EthernetClient::write_end(const uint8_t *buf, size_t size) {
    	Ethernet.socketSend_End(buf, size);
    }
     
    int RLucas_EthernetClient::available() {
    	if (sockindex >= MAX_SOCK_NUM) return 0;
    	return Ethernet.socketRecvAvailable(sockindex);
    	// TODO: do the Wiznet chips automatically retransmit TCP ACK packets if they are lost by the network ? Someday this should
    	// be checked by a man-in-the-middle test which discards certain packets.  If ACKs aren't resent, we would need to check for
    	// returning 0 here and after a timeout do another Sock_RECV command to cause the Wiznet chip to resend the ACK packet.
    }
     
    int RLucas_EthernetClient::read(uint8_t *buf, size_t size) {
    	if (sockindex >= MAX_SOCK_NUM) return 0;
    	return Ethernet.socketRecv(sockindex, buf, size);
    }
     
    int RLucas_EthernetClient::peek() {
    	if (sockindex >= MAX_SOCK_NUM) return -1;
    	if (!available()) return -1;
    	return Ethernet.socketPeek(sockindex);
    }
     
    int RLucas_EthernetClient::read() {
    	uint8_t b;
    	if (Ethernet.socketRecv(sockindex, &b, 1) > 0) return b;
    	return -1;
    }
     
    uint8_t RLucas_EthernetClient::connected()
    {
    	if (sockindex >= MAX_SOCK_NUM) return 0;
     
    	uint8_t s = Ethernet.socketStatus(sockindex);
    	return !(s == SnSR::LISTEN || s == SnSR::CLOSED || s == SnSR::FIN_WAIT ||
    		(s == SnSR::CLOSE_WAIT && !available()));
    }
     
    byte RLucas_EthernetClient::IPLastByte()
    {
    	//if (sockindex >= MAX_SOCK_NUM) return RLucas_IPAddress((uint32_t)0);
    	uint8_t remoteIParray[4];
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	W5100.readSnDIPR(sockindex, remoteIParray);
    	SPI.endTransaction();
    	return remoteIParray[3];
    }
     
    void RLucas_EthernetClient::stop() {
    	if (sockindex >= MAX_SOCK_NUM) return;
    	// attempt to close the connection gracefully (send a FIN to other side)
    	Ethernet.socketDisconnect(sockindex);
    	unsigned long start = millis();
    	// wait up to a second for the connection to close
    	do {
    		if (Ethernet.socketStatus(sockindex) == SnSR::CLOSED) {
    			sockindex = MAX_SOCK_NUM;
    			return; // exit the loop
    		}
    		delay(1); 
    	} while (millis() - start < _timeout); // R�my LUCAS : le probl�me de la dur�e de la fonction STOP est ici !!!
    	// if it hasn't closed, close it forcefully
    	Ethernet.socketClose(sockindex);
    	sockindex = MAX_SOCK_NUM;
    }
    RLucas_EthernetServer.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    #include <Arduino.h>
    #include "RLucas_Ethernet.h"
    #include "RLucas_w5100.h"
     
    uint16_t RLucas_EthernetServer::server_port[MAX_SOCK_NUM];
     
    void RLucas_EthernetServer::begin() {
    	uint8_t sockindex = Ethernet.socketBegin(SnMR::TCP, _port);
    	if (sockindex < MAX_SOCK_NUM) {
    		if (Ethernet.socketListen(sockindex)) {
    			server_port[sockindex] = _port;
    		} else {
    			Ethernet.socketDisconnect(sockindex);
    		}
    	}
    }
     
    RLucas_EthernetClient RLucas_EthernetServer::available() {
    	bool listening = false;
    	uint8_t sockindex = MAX_SOCK_NUM;
    	for (uint8_t i=0; i < MAX_SOCK_NUM; i++) { //for (uint8_t i=0; i < maxindex; i++) {
    		if (server_port[i] == _port) {
    			uint8_t stat = Ethernet.socketStatus(i);
    			if (stat == SnSR::ESTABLISHED || stat == SnSR::CLOSE_WAIT) {
    				if (Ethernet.socketRecvAvailable(i) > 0) {
    					sockindex = i;
    				} else {
    					// remote host closed connection, our end still open
    					if (stat == SnSR::CLOSE_WAIT) {
    						Ethernet.socketDisconnect(i);
    						// status becomes LAST_ACK for short time
    					}
    				}
    			} else if (stat == SnSR::LISTEN) {
    				listening = true;
    			} else if (stat == SnSR::CLOSED) {
    				server_port[i] = 0;
    			}
    		}
    	}
    	if (!listening) begin();
    	return RLucas_EthernetClient(sockindex);
    }
     
    RLucas_EthernetClient RLucas_EthernetServer::accept() {
    	bool listening = false;
    	uint8_t sockindex = MAX_SOCK_NUM;
    	for (uint8_t i=0; i < MAX_SOCK_NUM; i++) { //for (uint8_t i=0; i < maxindex; i++) {
    		if (server_port[i] == _port) {
    			uint8_t stat = Ethernet.socketStatus(i);
    			if (sockindex == MAX_SOCK_NUM &&
    			  (stat == SnSR::ESTABLISHED || stat == SnSR::CLOSE_WAIT)) {
    				// Return the connected client even if no data received. Some protocols like FTP expect the server to send the first data.
    				sockindex = i;
    				server_port[i] = 0; // only return the client once
    			} else if (stat == SnSR::LISTEN) {
    				listening = true;
    			} else if (stat == SnSR::CLOSED) {
    				server_port[i] = 0;
    			}
    		}
    	}
    	if (!listening) begin();
    	return RLucas_EthernetClient(sockindex);
    }
     
    size_t RLucas_EthernetServer::write(const uint8_t *buffer, size_t size) {
    	available();
    	for (uint8_t i=0; i < MAX_SOCK_NUM; i++) { //for (uint8_t i=0; i < maxindex; i++) {
    		if (server_port[i] == _port) {
    			if (Ethernet.socketStatus(i) == SnSR::ESTABLISHED) {
    				Ethernet.socketSend_Begin(i, buffer, size);
    				Ethernet.socketSend_End(buffer, 0);
    			}
    		}
    	}
    	return size;
    }
    RLucas_IPAddress.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #include <Arduino.h>
    #include "RLucas_IPAddress.h"
     
    RLucas_IPAddress::RLucas_IPAddress(uint32_t address) {
        _address.dword = address;
    }
     
    RLucas_IPAddress::RLucas_IPAddress(const uint8_t *address) {
        memcpy(_address.bytes, address, sizeof(_address.bytes));
    }
     
    RLucas_IPAddress& RLucas_IPAddress::operator=(const uint8_t *address) {
        memcpy(_address.bytes, address, sizeof(_address.bytes));
        return *this;
    }
     
    RLucas_IPAddress& RLucas_IPAddress::operator=(uint32_t address) {
        _address.dword = address;
        return *this;
    }
    RLucas_socket.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    #include <Arduino.h>
    #include "RLucas_Ethernet.h"
    #include "RLucas_w5100.h"
     
    #if ARDUINO >= 156 && !defined(ARDUINO_ARCH_PIC32)
    extern void yield(void);
    #else
    #define yield()
    #endif
     
    // TODO: randomize this when not using DHCP, but how?
    static uint16_t local_port = 49152;  // 49152 to 65535
     
    typedef struct {
    	uint16_t RX_RSR; // Number of bytes received
    	uint16_t RX_RD;  // Address to read
    	uint16_t TX_FSR; // Free space ready for transmit
    	uint8_t  RX_inc; // how much have we advanced RX_RD
    } socketstate_t;
    static socketstate_t state[MAX_SOCK_NUM];
    static uint16_t getSnTX_FSR(uint8_t s);
    static uint16_t getSnRX_RSR(uint8_t s);
    static void write_data(uint8_t s, uint16_t offset, const uint8_t *data, uint16_t len);
    static void read_data(uint8_t s, uint16_t src, uint8_t *dst, uint16_t len);
     
    static uint16_t w_offset;
    static uint8_t w_s;
     
    /*****************************************/
    /*          Socket management            */
    /*****************************************/
     
    uint8_t RLucas_EthernetClass::socketBegin(uint8_t protocol, uint16_t port) {
    	uint8_t s, status[MAX_SOCK_NUM]; 
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	// look at all the hardware sockets, use any that are closed (unused)
    	for (s=0; s < MAX_SOCK_NUM; s++) {
    		status[s] = W5100.readSnSR(s);
    		if (status[s] == SnSR::CLOSED) goto makesocket;
    	}
    	// as a last resort, forcibly close any already closing
    	for (s=0; s < MAX_SOCK_NUM; s++) {
    		uint8_t stat = status[s];
    		if (stat == SnSR::LAST_ACK) goto closemakesocket;
    		if (stat == SnSR::TIME_WAIT) goto closemakesocket;
    		if (stat == SnSR::FIN_WAIT) goto closemakesocket;
    		if (stat == SnSR::CLOSING) goto closemakesocket;
    	}
    	SPI.endTransaction();
    	return MAX_SOCK_NUM; // all sockets are in use
    closemakesocket:
    	W5100.execCmdSn(s, Sock_CLOSE);
    makesocket:
    	RLucas_EthernetServer::server_port[s] = 0;
    	delayMicroseconds(250); // TODO: is this needed??
    	W5100.writeSnMR(s, protocol);
    	W5100.writeSnIR(s, 0xFF);
    	if (port > 0) {
    		W5100.writeSnPORT(s, port);
    	} else {
    		// if don't set the source port, set local_port number.
    		if (++local_port < 49152) local_port = 49152;
    		W5100.writeSnPORT(s, local_port);
    	}
    	W5100.execCmdSn(s, Sock_OPEN);
    	state[s].RX_RSR = 0;
    	state[s].RX_RD  = W5100.readSnRX_RD(s); // always zero?
    	state[s].RX_inc = 0;
    	state[s].TX_FSR = 0;
    	SPI.endTransaction();
    	return s;
    }
     
    // Return the socket's status
    uint8_t RLucas_EthernetClass::socketStatus(uint8_t s) {
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	uint8_t status = W5100.readSnSR(s);
    	SPI.endTransaction();
    	return status;
    }
     
    // Immediately close.  If a TCP connection is established, the remote host is left unaware we closed.
    void RLucas_EthernetClass::socketClose(uint8_t s) {
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	W5100.execCmdSn(s, Sock_CLOSE);
    	SPI.endTransaction();
    }
     
    // Place the socket in listening (server) mode
    uint8_t RLucas_EthernetClass::socketListen(uint8_t s) {
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	if (W5100.readSnSR(s) != SnSR::INIT) {
    		SPI.endTransaction();
    		return 0;
    	}
    	W5100.execCmdSn(s, Sock_LISTEN);
    	SPI.endTransaction();
    	return 1;
    }
     
    // establish a TCP connection in Active (client) mode.
    void RLucas_EthernetClass::socketConnect(uint8_t s, uint8_t * addr, uint16_t port) {
    	// set destination IP
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	W5100.writeSnDIPR(s, addr);
    	W5100.writeSnDPORT(s, port);
    	W5100.execCmdSn(s, Sock_CONNECT);
    	SPI.endTransaction();
    }
     
    // Gracefully disconnect a TCP connection.
    void RLucas_EthernetClass::socketDisconnect(uint8_t s) {
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	W5100.execCmdSn(s, Sock_DISCON);
    	SPI.endTransaction();
    }
     
    /*****************************************/
    /*    Socket Data Receive Functions      */
    /*****************************************/
     
    static uint16_t getSnRX_RSR(uint8_t s){
    	uint16_t val, prev;
    	prev = W5100.readSnRX_RSR(s);
    	while (1) {
    		val = W5100.readSnRX_RSR(s);
    		if (val == prev) {
    			return val;
    		}
    		prev = val;
    	}
    }
     
    static void read_data(uint8_t s, uint16_t src, uint8_t *dst, uint16_t len) {
    	uint16_t size;
    	uint16_t src_mask;
    	uint16_t src_ptr;
    	src_mask = (uint16_t)src & W5100.SMASK;
    	src_ptr = W5100.RBASE(s) + src_mask;
    	if (W5100.hasOffsetAddressMapping() || src_mask + len <= W5100.SSIZE) {
    		W5100.read(src_ptr, dst, len);
    	} else {
    		size = W5100.SSIZE - src_mask;
    		W5100.read(src_ptr, dst, size);
    		dst += size;
    		W5100.read(W5100.RBASE(s), dst, len - size);
    	}
    }
     
    // Receive data.  Returns size, or -1 for no data, or 0 if connection closed
    int RLucas_EthernetClass::socketRecv(uint8_t s, uint8_t *buf, int16_t len) {
    	// Check how much data is available
    	int ret = state[s].RX_RSR;
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	if (ret < len) {
    		uint16_t rsr = getSnRX_RSR(s);
    		ret = rsr - state[s].RX_inc;
    		state[s].RX_RSR = ret;
    	}
    	if (ret == 0) {
    		// No data available.
    		uint8_t status = W5100.readSnSR(s);
    		if ( status == SnSR::LISTEN || status == SnSR::CLOSED ||
    		  status == SnSR::CLOSE_WAIT ) {
    			// The remote end has closed its side of the connection, so this is the eof state
    			ret = 0;
    		} else {
    			// The connection is still up, but there's no data waiting to be read
    			ret = -1;
    		}
    	} else {
    		if (ret > len) ret = len; // more data available than buffer length
    		uint16_t ptr = state[s].RX_RD;
    		if (buf) read_data(s, ptr, buf, ret);
    		ptr += ret;
    		state[s].RX_RD = ptr;
    		state[s].RX_RSR -= ret;
    		uint16_t inc = state[s].RX_inc + ret;
    		if (inc >= 250 || state[s].RX_RSR == 0) {
    			state[s].RX_inc = 0;
    			W5100.writeSnRX_RD(s, ptr);
    			W5100.execCmdSn(s, Sock_RECV);
    		} else {
    			state[s].RX_inc = inc;
    		}
    	}
    	SPI.endTransaction();
    	return ret;
    }
     
    uint16_t RLucas_EthernetClass::socketRecvAvailable(uint8_t s) {
    	uint16_t ret = state[s].RX_RSR;
    	if (ret == 0) {
    		SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    		uint16_t rsr = getSnRX_RSR(s);
    		SPI.endTransaction();
    		ret = rsr - state[s].RX_inc;
    		state[s].RX_RSR = ret;
    	}
    	return ret;
    }
     
    // get the first byte in the receive queue (no checking)
    uint8_t RLucas_EthernetClass::socketPeek(uint8_t s) {
    	uint8_t b;
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	uint16_t ptr = state[s].RX_RD;
    	W5100.read((ptr & W5100.SMASK) + W5100.RBASE(s), &b, 1);
    	SPI.endTransaction();
    	return b;
    }
     
    /*****************************************/
    /*    Socket Data Transmit Functions     */
    /*****************************************/
     
    static uint16_t getSnTX_FSR(uint8_t s) {
    	uint16_t val, prev;
    	prev = W5100.readSnTX_FSR(s);
    	while (1) {
    		val = W5100.readSnTX_FSR(s);
    		if (val == prev) {
    			state[s].TX_FSR = val;
    			return val;
    		}
    		prev = val;
    	}
    }
     
    static void write_data(uint8_t s, uint16_t data_offset, const uint8_t *data, uint16_t len) {
    	uint16_t ptr = W5100.readSnTX_WR(s);
    	ptr += data_offset;
    	uint16_t offset = ptr & W5100.SMASK;
    	uint16_t dstAddr = offset + W5100.SBASE(s);
    	if (W5100.hasOffsetAddressMapping() || offset + len <= W5100.SSIZE) {
    		W5100.write(dstAddr, data, len);
    	} else {
    		// Wrap around circular buffer
    		uint16_t size = W5100.SSIZE - offset;
    		W5100.write(dstAddr, data, size);
    		W5100.write(W5100.SBASE(s), data + size, len - size);
    	}
    	ptr += len;
    	W5100.writeSnTX_WR(s, ptr);
    }
     
    //	This function used to send the data in TCP mode
    void RLucas_EthernetClass::socketSend_Begin(uint8_t s, const uint8_t * buf, uint16_t len) {
    	uint8_t status=0;
    	uint16_t freesize=0;
    	// if freebuf is available, start.
    	do {
    		SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    		freesize = getSnTX_FSR(s);
    		status = W5100.readSnSR(s);
    		SPI.endTransaction();
    		if ((status != SnSR::ESTABLISHED) && (status != SnSR::CLOSE_WAIT)) {
    			len = 0;
    			break;
    		}
    		yield();
    	} while (freesize < len);
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	write_data(s, 0, (uint8_t *)buf, len);
    	w_offset = len;
    	w_s = s;
    }
    void RLucas_EthernetClass::socketSend_Continue(const uint8_t * buf, uint16_t len) {
    	write_data(w_s, w_offset, (uint8_t *)buf, len);
    	w_offset += len;
    }
    void RLucas_EthernetClass::socketSend_End(const uint8_t * buf, uint16_t len) {
    	write_data(w_s, w_offset, (uint8_t *)buf, len);
    	W5100.execCmdSn(w_s, Sock_SEND);
    	while ( (W5100.readSnIR(w_s) & SnIR::SEND_OK) != SnIR::SEND_OK ) {
    		if ( W5100.readSnSR(w_s) == SnSR::CLOSED ) {
    			SPI.endTransaction();
    			return;
    		}
    		SPI.endTransaction();
    		yield();
    		SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	}
    	W5100.writeSnIR(w_s, SnIR::SEND_OK);
    	SPI.endTransaction();
    }
     
    uint16_t RLucas_EthernetClass::socketSendAvailable(uint8_t s) {
    	uint8_t status=0;
    	uint16_t freesize=0;
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	freesize = getSnTX_FSR(s);
    	status = W5100.readSnSR(s);
    	SPI.endTransaction();
    	if ((status == SnSR::ESTABLISHED) || (status == SnSR::CLOSE_WAIT)) {
    		return freesize;
    	}
    	return 0;
    }
    RLucas_w5100.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    #include <Arduino.h>
    #include "RLucas_Ethernet.h"
    #include "RLucas_w5100.h"
     
    /***************************************************/
    /**            Default SS pin setting             **/
    /***************************************************/
     
    // If variant.h or other headers specifically define the default SS pin for ethernet, use it.
    #if defined(PIN_SPI_SS_ETHERNET_LIB)
    #define SS_PIN_DEFAULT  PIN_SPI_SS_ETHERNET_LIB
     
    // MKR boards default to pin 5 for MKR ETH Pins 8-10 are MOSI/SCK/MISO on MRK, so don't use pin 10
    #elif defined(USE_ARDUINO_MKR_PIN_LAYOUT) || defined(ARDUINO_SAMD_MKRZERO) || defined(ARDUINO_SAMD_MKR1000) || defined(ARDUINO_SAMD_MKRFox1200) || defined(ARDUINO_SAMD_MKRGSM1400) || defined(ARDUINO_SAMD_MKRWAN1300)
    #define SS_PIN_DEFAULT  5
     
    // For boards using AVR, assume shields with SS on pin 10 will be used.  This allows for Arduino Mega (where
    // SS is pin 53) and Arduino Leonardo (where SS is pin 17) to work by default with Arduino Ethernet Shield R2 & R3.
    #elif defined(__AVR__)
    #define SS_PIN_DEFAULT  10
     
    // If variant.h or other headers define these names use them if none of the other cases match
    #elif defined(PIN_SPI_SS)
    #define SS_PIN_DEFAULT  PIN_SPI_SS
    #elif defined(CORE_SS0_PIN)
    #define SS_PIN_DEFAULT  CORE_SS0_PIN
     
    // As a final fallback, use pin 10
    #else
    #define SS_PIN_DEFAULT  10
    #endif
     
    // W5100 controller instance
    uint8_t  W5100Class::CH_BASE_MSB;
    uint8_t  W5100Class::ss_pin = SS_PIN_DEFAULT;
    #ifdef ETHERNET_LARGE_BUFFERS
    uint16_t W5100Class::SSIZE = 2048;
    uint16_t W5100Class::SMASK = 0x07FF;
    #endif
    W5100Class W5100;
     
    // pointers and bitmasks for optimized SS pin
    #if defined(__AVR__)
      volatile uint8_t * W5100Class::ss_pin_reg;
      uint8_t W5100Class::ss_pin_mask;
    #elif defined(__MK20DX128__) || defined(__MK20DX256__) || defined(__MK66FX1M0__) || defined(__MK64FX512__)
      volatile uint8_t * W5100Class::ss_pin_reg;
    #elif defined(__MKL26Z64__)
      volatile uint8_t * W5100Class::ss_pin_reg;
      uint8_t W5100Class::ss_pin_mask;
    #elif defined(__SAM3X8E__) || defined(__SAM3A8C__) || defined(__SAM3A4C__)
      volatile uint32_t * W5100Class::ss_pin_reg;
      uint32_t W5100Class::ss_pin_mask;
    #elif defined(__PIC32MX__)
      volatile uint32_t * W5100Class::ss_pin_reg;
      uint32_t W5100Class::ss_pin_mask;
    #elif defined(ARDUINO_ARCH_ESP8266)
      volatile uint32_t * W5100Class::ss_pin_reg;
      uint32_t W5100Class::ss_pin_mask;
    #elif defined(__SAMD21G18A__)
      volatile uint32_t * W5100Class::ss_pin_reg;
      uint32_t W5100Class::ss_pin_mask;
    #endif
     
    uint8_t W5100Class::init(void) {
    	static bool initialized = false;
    	uint8_t i;
    	if (initialized) return 1;
    	// Many Ethernet shields have a CAT811 or similar reset chip connected to W5100 or W5200 chips.  The W5200 will not work at
    	// all, and may even drive its MISO pin, until given an active low reset pulse!  The CAT811 has a 240 ms typical pulse length, and
    	// a 400 ms worst case maximum pulse length.  MAX811 has a worst case maximum 560 ms pulse length.  This delay is meant to wait
    	// until the reset pulse is ended.  If your hardware has a shorter reset time, this can be edited or removed.
    	//delay(560);
    	//delay(400);
    	SPI.begin();
    	initSS();
    	resetSS();
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	if (!softReset()) return 0;
    	writeMR(0x08);
    	if (readMR() != 0x08) return 0;
    	writeMR(0x10);
    	if (readMR() != 0x10) return 0;
    	writeMR(0x00);
    	if (readMR() != 0x00) return 0;
    	int ver = readVERSIONR_W5500();
    	if (ver != 4) return 0;
    	CH_BASE_MSB = 0x10;
    #ifdef ETHERNET_LARGE_BUFFERS
    #if MAX_SOCK_NUM <= 1
    	SSIZE = 16384;
    #elif MAX_SOCK_NUM <= 2
    	SSIZE = 8192;
    #elif MAX_SOCK_NUM <= 4
    	SSIZE = 4096;
    #else
    	SSIZE = 2048;
    #endif
    	SMASK = SSIZE - 1;
    	for (i=0; i<MAX_SOCK_NUM; i++) {
    		writeSnRX_SIZE(i, SSIZE >> 10);
    		writeSnTX_SIZE(i, SSIZE >> 10);
    	}
    	for (; i<8; i++) {
    		writeSnRX_SIZE(i, 0);
    		writeSnTX_SIZE(i, 0);
    	}
    #endif
    	SPI.endTransaction();
    	initialized = true;
    	return 1; // successful init
    }
     
    // Soft reset the Wiznet chip, by writing to its MR register reset bit
    uint8_t W5100Class::softReset(void) {
    	uint16_t count=0;
    	// write to reset bit
    	writeMR(0x80);
    	// then wait for soft reset to complete
    	do {
    		uint8_t mr = readMR();
    		if (mr == 0) return 1;
    		delay(1);
    	} while (++count < 20);
    	return 0;
    }
     
    W5100Linkstatus W5100Class::getLinkStatus() {
    	uint8_t phystatus;
    	if (!init()) return UNKNOWN;
    	SPI.beginTransaction(SPI_ETHERNET_SETTINGS);
    	phystatus = readPHYCFGR_W5500();
    	SPI.endTransaction();
    	if (phystatus & 0x01) return LINK_ON;
    	return LINK_OFF;
    }
     
    uint16_t W5100Class::write(uint16_t addr, const uint8_t *buf, uint16_t len) {
    	uint8_t cmd[8];
    	setSS();
    	if (addr < 0x100) {
    		// common registers 00nn
    		cmd[0] = 0;
    		cmd[1] = addr & 0xFF;
    		cmd[2] = 0x04;
    	} else if (addr < 0x8000) {
    		// socket registers  10nn, 11nn, 12nn, 13nn, etc
    		cmd[0] = 0;
    		cmd[1] = addr & 0xFF;
    		cmd[2] = ((addr >> 3) & 0xE0) | 0x0C;
    	} else if (addr < 0xC000) {
    		// transmit buffers  8000-87FF, 8800-8FFF, 9000-97FF, etc
    		//  10## #nnn nnnn nnnn
    		cmd[0] = addr >> 8;
    		cmd[1] = addr & 0xFF;
    		#if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1
    		cmd[2] = 0x14;                       // 16K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2
    		cmd[2] = ((addr >> 8) & 0x20) | 0x14; // 8K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4
    		cmd[2] = ((addr >> 7) & 0x60) | 0x14; // 4K buffers
    		#else
    		cmd[2] = ((addr >> 6) & 0xE0) | 0x14; // 2K buffers
    		#endif
    	} else {
    		// receive buffers
    		cmd[0] = addr >> 8;
    		cmd[1] = addr & 0xFF;
    		#if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1
    		cmd[2] = 0x1C;                       // 16K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2
    		cmd[2] = ((addr >> 8) & 0x20) | 0x1C; // 8K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4
    		cmd[2] = ((addr >> 7) & 0x60) | 0x1C; // 4K buffers
    		#else
    		cmd[2] = ((addr >> 6) & 0xE0) | 0x1C; // 2K buffers
    		#endif
    	}
    	if (len <= 5) {
    		for (uint8_t i=0; i < len; i++) {
    			cmd[i + 3] = buf[i];
    		}
    		SPI.transfer(cmd, len + 3);
    	} else {
    		SPI.transfer(cmd, 3);
    #ifdef SPI_HAS_TRANSFER_BUF
    		SPI.transfer(buf, NULL, len);
    #else
    		// TODO: copy 8 bytes at a time to cmd[] and block transfer
    		for (uint16_t i=0; i < len; i++) {
    			SPI.transfer(buf[i]);
    		}
    #endif
    	}
    	resetSS();
    	return len;
    }
     
    uint16_t W5100Class::read(uint16_t addr, uint8_t *buf, uint16_t len) {
    	uint8_t cmd[4];
    	setSS();
    	if (addr < 0x100) {
    		// common registers 00nn
    		cmd[0] = 0;
    		cmd[1] = addr & 0xFF;
    		cmd[2] = 0x00;
    	} else if (addr < 0x8000) {
    		// socket registers  10nn, 11nn, 12nn, 13nn, etc
    		cmd[0] = 0;
    		cmd[1] = addr & 0xFF;
    		cmd[2] = ((addr >> 3) & 0xE0) | 0x08;
    	} else if (addr < 0xC000) {
    		// transmit buffers  8000-87FF, 8800-8FFF, 9000-97FF, etc
    		//  10## #nnn nnnn nnnn
    		cmd[0] = addr >> 8;
    		cmd[1] = addr & 0xFF;
    		#if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1
    		cmd[2] = 0x10;                       // 16K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2
    		cmd[2] = ((addr >> 8) & 0x20) | 0x10; // 8K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4
    		cmd[2] = ((addr >> 7) & 0x60) | 0x10; // 4K buffers
    		#else
    		cmd[2] = ((addr >> 6) & 0xE0) | 0x10; // 2K buffers
    		#endif
    	} else {
    		// receive buffers
    		cmd[0] = addr >> 8;
    		cmd[1] = addr & 0xFF;
    		#if defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 1
    		cmd[2] = 0x18;                       // 16K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 2
    		cmd[2] = ((addr >> 8) & 0x20) | 0x18; // 8K buffers
    		#elif defined(ETHERNET_LARGE_BUFFERS) && MAX_SOCK_NUM <= 4
    		cmd[2] = ((addr >> 7) & 0x60) | 0x18; // 4K buffers
    		#else
    		cmd[2] = ((addr >> 6) & 0xE0) | 0x18; // 2K buffers
    		#endif
    	}
    	SPI.transfer(cmd, 3);
    	memset(buf, 0, len);
    	SPI.transfer(buf, len);
    	resetSS();
    	return len;
    }
     
    void W5100Class::execCmdSn(SOCKET s, SockCMD _cmd) {
    	// Send command to socket
    	writeSnCR(s, _cmd);
    	// Wait for command to complete
    	while (readSnCR(s)) ;
    }
    A bientôt

  5. #5
    Membre chevronné Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    999
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 999
    Par défaut
    Bonjour,

    Voici quelques informations importantes :

    D'abord deux articles très intéressants sur la fragmentation de packets, en français :
    https://www.bortzmeyer.org/8900.html
    http://racine.gatoux.com/lmdr/index....gmentation-ip/

    Pour faire simple : la fragmentation de packets, il faut l'éviter au maximum.

    Ensuite, l'indispensable documentation officielle du W5500 :
    http://wizwiki.net/wiki/doku.php/products:w5500:start
    Elle est bien faite et illustrée.

    Le W5500 ne gère pas la fragmentation de packets, c'est à dire qu'il n'est pas capable de recevoir des packets fragmentés pour les réassembler

    En revanche, quant on utilise le W5500 pour envoyer des données, il est capable d'envoyer les données en de multiples petits paquets - faites ce que je dis, pas ce que je fais

    Dans le cas de figure où votre W5500 doit communiquer avec un autre W5500 (typiquement si vous avez un projet où des cartes embarquées communiques entre elles via Ethernet), il faut donc faire attention :
    - utiliser un buffer pour envoyer les données en un seul packet
    - utiliser mon code qui permet d'envoyer les données en plusieurs fois tout en ayant un seul packet

    La taille maximale d'un packet serait de 1500 octets.
    En fait les packets pourraient faire 65 535 octets MAIS les routeurs ont la possibilité de découper les packets.
    C'est ça le problème : dans une communication Ethernet, entre l'émetteur et le destinataire (serveur / client), il y a des intermédiaires (un ou plusieurs routeurs, la box Internet...) qui ont la possibilité de découper les packets.
    Les réseaux ont ce qu'on appelle une "MTU", la taille maxi des packets, fixée à 1500 octets pour Ethernet.
    Il faut se rendre compte qu'Ethernet est une invention ancienne, les premiers réseaux datent de l'époque où les PC étaient beaucoup moins puissants qu'aujourd'hui...

    Il peut arriver que des réseaux aient une MTU plus basse, la littérature explique que lorsque les données transitent par Internet, la MTU peut descendre à 68 octets
    Cela pose un problème dans le cas de figure où votre W5500 doit recevoir des données via Internet.
    Il faut être conscient de cette possibilité, et en cas de bug incompréhensible, examiner ce qu'il se passe avec WireShark (https://www.wireshark.org/)

    J'ai de la chance, mon projet n'utilisera que le réseau local et 1500 octets c'est pile la taille maxi des données que mes applications peuvent gérer

    Et cela correspond aux capacités du W5500 : il a 32 ko de buffer (c'est énorme, 16 fois plus de RAM que l'Arduino UNO) qu'il peut répartir dans 8 sockets ; typiquement chacun des 8 sockets va utiliser 2 ko de buffer en TX et 2 ko de buffer en RX.

    Il reste possible d'utiliser moins de sockets pour avoir plus de buffer par socket (mais quel intérêt si la taille maxi d'un packet est de 1500 octets ???)

    N’espérez pas rassembler les packets vous-même
    D'une part, cette fonction devrait être faite par le W5500, qui ne la prend pas en charge.
    D'autre part, TCP/IP prévoit la possibilité que les packets puissent arriver dans le désordre et que certains packets soient perdus bref le foutoir puissance 10
    C'est plus simple d'avoir des packets uniques, on a juste a gérer la possibilité qu'ils n'arrivent pas.

    On peut néanmoins dépasser cette limite de 1500 octets de données échangées entre deux W5500.
    Si, dans votre application, vous programmez vous-même les clients et les servers, rien ne vous empêche de faire en sorte qu'un client fasse plusieurs requêtes l'une après l'autre pour dépasser cette limite de 1500 octets.
    C'est ce que j'ai prévu dans mon projet, dans sa réponse le server envoi un "statut" au client qui peut prendre une valeur disant au client "fais-moi une autre requête pour avoir la suite des données".
    Evidemment, on s’écarte du standard HTTP et HTML, mais ce n'est pas gênant pour faire s'échanger entre eux des W5500.

    Sur le forum de Wiznet il y a une discussion à ce sujet : https://forum.wiznet.io/t/topic/2561/10

    L'ESP8266 est aussi concerné par les limites de MTU : https://github.com/ARMmbed/esp8266-driver/issues/18

    A bientôt

  6. #6
    Expert confirmé

    Homme Profil pro
    mad scientist :)
    Inscrit en
    Septembre 2019
    Messages
    2 921
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : mad scientist :)

    Informations forums :
    Inscription : Septembre 2019
    Messages : 2 921
    Par défaut
    bravo pour les recherches. Si vous arrivez à une bibliothèque aboutie et optimisée, je suis sûr qu'il aura des heureux!

  7. #7
    Membre chevronné Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    999
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 999
    Par défaut
    Je suis encore en pleine phase d'expérimentation

    J'arrive à avoir un server qui, pendant qu'il est en train de répondre à un client, peut se connecter à un autre server en tant que client

    Cela permet d'utiliser d'autres servers pour trouver des données pour répondre aux clients.

    Dans mon projet, cela va me permettre, par exemple :
    - d'avoir un boitier client avec écran tactile qui va interroger le boitier serveur domotique
    - pour répondre au client, le server domotique va pouvoir lui-même interroger en tant que client le raspberry Pi utilisé en lecteur multimédia

    La seule chose qui compte, c'est d'avoir assez de sockets disponibles.

    Concrètement, pour que tout fonctionne sans erreurs, il faut que le nombre de client simultanés s'adressant au serveur soit inférieur au nombre maxi de sockets - 1 (puisqu'il faut un socket pour se connecter à un server en tant que client)

    J'arrive à faire ça sans bricoler, en utilisant juste les classes de base...

    Bien sûr j'ai un W5500 qui a 8 sockets et assez de RAM pour leur donner 2ko de buffer TX et 2ko de buffer RX.

    Les W5100 sont certainement trop limités pour envisager cet usage.
    Malheureusement ils sont très répandus, notamment dans tous les clones chinois pas chers, avec, pour ne rien arranger, des spécifications SPI pas toujours respectées, sans parler de la qualité des composants et du circuit imprimé...
    C'est ce matos bas de gamme qui fait penser à tout le monde qu'il y a plein de limitations...

    Je pense revenir en arrière dans mon travail d'optimisation car je me rend compte que si je veux consulter un site web (typiquement, pour récupérer l'heure ou des données météo), il me faut les fonctionnalités DNS que j'avais retirée.

    La bonne nouvelle c'est que mon boitier serveur n'utilise que le shield Ethernet et des fonctions I/O de base, donc j'ai encore plein de place en ROM (contrairement à mes boîtiers client domotiques qui sont pleins à craquer, car ils doivent gérer le shield Ethernet + l'écran TFT + la dalle tactile)

    Il faudra quand même que je modifie la bibliothèque en profondeur car la façon dont sont gérées les erreurs n'est pas totalement complète par rapport à l'usage que je fait.
    J'aimerais aussi comprendre les "TODO" laissés par les hauteurs des bibliothèques, il ne faut pas laisser ce genre de verrues dans le code.

    En particulier, il faut gérer le cas de figure où trop de clients se connectent au server sans que celui-ci n'arrive à suivre le rythme.

    Ca ne sera pas trop compliqué car mes boitiers client avec écran tactile ont été conçus pour gérer le fait que le serveur ne réponde pas.
    Ils attendent un peu et font une autre requête, autant de fois que nécessaire.
    De plus j'ai bien "blindé" le protocole maison utilisé par mes boitiers client avec un CRC, un contrôle de la longueur, et des codes d'erreurs... tout ça en plus de ce que fait déjà la couche TCP/IP
    Dans ce cas de figure, en cas de surmenage temporaire, mon boitier serveur domotique peut envoyer chier mes boîtiers clients sans planter le système on aura juste quelques clients devant attendre un peu.

    L'art de concevoir un système robuste... pour pouvoir ensuite le maltraiter avec la méthode bourrin

    En pratique, même si j'ai plusieurs personnes chez moi, il serait très surprenant que plus de 7 requêtes soient faites simultanément !

    Très paradoxalement le cas de figure se posera plutôt lorsque mon boitier server fera un "ping" de tous les clients (j'ai prévu de le faire en hardware avec un fil non utilisé du câble Ethernet)
    Sachant que j'ai déjà prévu un réglage de "temporisation au ping" dans mes clients, chaque client a un délai de réponse individuel.
    Ce délai laisse le temps au server de répondre aux premiers clients avant que les autres le sollicite, avec des valeurs judicieuses, si je "ping" 10 clients en même temps, le serveur n'en aura pas plus de 5 ou 6 en simultané.
    Et rien ne m'empêche de faire le ping en deux groupes de clients pour rester inférieur à 7.

    Il va juste falloir que je modifie le code 'socket' et 'W5100' pour que le server me garde toujours un socket de côté pour une utilisation en tant que client.

    Mine de rien, le W5500 est beaucoup plus puissant que l'Arduino UNO auquel il est associé.
    Tout l'enjeu sera de voir si les sockets sont vraiment indépendants les uns des autres.


    A bientôt

  8. #8
    Membre Expert
    Homme Profil pro
    Inscrit en
    Octobre 2011
    Messages
    2 910
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 2 910
    Par défaut
    Salut,

    Je n'ai pas tout compris mais ça m'a l'air intéressant.
    Bon courage pour la suite...

  9. #9
    Membre chevronné Avatar de electroremy
    Homme Profil pro
    Ingénieur sécurité
    Inscrit en
    Juin 2007
    Messages
    999
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Doubs (Franche Comté)

    Informations professionnelles :
    Activité : Ingénieur sécurité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2007
    Messages : 999
    Par défaut
    J'espère publier le projet final bientôt

    Il est déjà bien avancé

    Ce sera plus clair : il y aura une petite doc et des exemples, les gens intéressés pourront faire des tests chez eux avec une vraie carte et expérimenter en modifiant le code source.

    A bientôt

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [AC-2013] Requête paramétrée, un seul champ avec plusieurs critères différents
    Par Camour dans le forum Requêtes et SQL.
    Réponses: 17
    Dernier message: 27/04/2017, 16h55
  2. [Toutes versions] Copier Coller des information provenant de fichier vers un seul fichier avec plusieurs onglets
    Par Hazard17 dans le forum Macros et VBA Excel
    Réponses: 22
    Dernier message: 10/10/2014, 18h15
  3. [WD15] Un seul Pdf avec plusieurs états
    Par GEOFDEN dans le forum WinDev
    Réponses: 2
    Dernier message: 03/06/2010, 15h40
  4. Plantage avec plusieurs clients
    Par marlozo dans le forum IHM
    Réponses: 2
    Dernier message: 06/06/2007, 08h34
  5. faire une seule table avec plusieurs table
    Par bossboss dans le forum Access
    Réponses: 4
    Dernier message: 08/01/2007, 16h11

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo