diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e9f8f6b --- /dev/null +++ b/AUTHORS @@ -0,0 +1,40 @@ + +Alberto Panu https://github.com/bigjohnson +Alasdair Allan https://github.com/aallan +Alice Pintus https://github.com/00alis +Adrian McEwen https://github.com/amcewen +Arduino LLC http://arduino.cc/ +Arnie97 https://github.com/Arnie97 +Arturo Guadalupi https://github.com/agdl +Bjoern Hartmann https://people.eecs.berkeley.edu/~bjoern/ +chaveiro https://github.com/chaveiro +Cristian Maglie https://github.com/cmaglie +David A. Mellis https://github.com/damellis +Dino Tinitigan https://github.com/bigdinotech +Eddy https://github.com/eddyst +Federico Vanzati https://github.com/Fede85 +Federico Fissore https://github.com/ffissore +Jack Christensen https://github.com/JChristensen +Johann Richard https://github.com/johannrichard +Jordan Terrell https://github.com/iSynaptic +Justin Paulin https://github.com/interwho +lathoub https://github.com/lathoub +Martino Facchin https://github.com/facchinm +Matthias Hertel https://github.com/mathertel +Matthijs Kooijman https://github.com/matthijskooijman +Matt Robinson https://github.com/ribbons +MCQN Ltd. http://mcqn.com/ +Michael Amie https://github.com/michaelamie +Michael Margolis https://github.com/michaelmargolis +Norbert Truchsess https://github.com/ntruchsess +Paul Stoffregen https://github.com/PaulStoffregen +per1234 https://github.com/per1234 +Richard Sim +Scott Fitzgerald https://github.com/shfitz +STMicroelectronics https://github.com/stm32duino +Thibaut Viard https://github.com/aethaniel +Tom Igoe https://github.com/tigoe +Wi6Labs http://www.wi6labs.com/ +WizNet http://www.wiznet.co.kr +Zach Eveland https://github.com/zeveland + diff --git a/README b/README new file mode 100644 index 0000000..1e02cf2 --- /dev/null +++ b/README @@ -0,0 +1,17 @@ +== License == + +Copyright (c) 2010 Arduino LLC. All right reserved. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA diff --git a/README.md b/README.md index db25a0d..43e406f 100644 --- a/README.md +++ b/README.md @@ -1 +1,29 @@ -# STM32Ethernet \ No newline at end of file +# STM32Ethernet + +## Ethernet Library for Arduino + +With an STM32 board with Ethernet compatibility, this library allows a STM32 +board (NUCLEO, DISCOVERY, ...) to connect to the internet. + +For more information about this library please visit us at +http://www.arduino.cc/en/Reference/Ethernet + +## Note + +The library is based on LwIP, a Lightweight TCP/IP stack. +http://git.savannah.gnu.org/cgit/lwip.git +The LwIP has been ported as Arduino library. The STM32Ethernet library depends +on it. + +EthernetClass::maintain() in no more required to renew IP address from DHCP. +It is done automatically by the LwIP stack in a background task. + +An Idle task is required by the LwIP stack to handle timer and data reception. +This idle task is called inside the main loop in background by the function +stm32_eth_scheduler(). Be careful to not lock the system inside the function +loop() where LwIP could never be updated. Call EthernetUDP::parsePacket() or +EthernetClient::available() performs an update of the LwIP stack. + +## Wiki + +You can find information at https://github.com/stm32duino/wiki/wiki/STM32Ethernet diff --git a/examples/AdvancedChatServer/AdvancedChatServer.ino b/examples/AdvancedChatServer/AdvancedChatServer.ino new file mode 100644 index 0000000..e3719a6 --- /dev/null +++ b/examples/AdvancedChatServer/AdvancedChatServer.ino @@ -0,0 +1,110 @@ +/* + Advanced Chat Server + + A more advanced server that distributes any incoming messages + to all connected clients but the client the message comes from. + To use, telnet to your device's IP address and type. + You can see the client's input in the serial monitor as well. + + Circuit: + * STM32 board with Ethernet support + + created 18 Dec 2009 + by David A. Mellis + modified 9 Apr 2012 + by Tom Igoe + redesigned to make use of operator== 25 Nov 2013 + by Norbert Truchsess + modified 23 Jun 2017 + by Wi6Labs + + */ + +#include +#include + +// Enter a MAC address and IP address for your controller below. +// The IP address will be dependent on your local network. +// gateway and subnet are optional: +byte mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED +}; +IPAddress ip(192, 168, 1, 177); +IPAddress myDns(192, 168, 1, 1); +IPAddress gateway(192, 168, 1, 1); +IPAddress subnet(255, 255, 0, 0); + + +// telnet defaults to port 23 +EthernetServer server(23); + +EthernetClient clients[4]; + +void setup() { + // initialize the Ethernet device + Ethernet.begin(mac, ip, myDns, gateway, subnet); + // start listening for clients + server.begin(); + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + + Serial.print("Chat server address:"); + Serial.println(Ethernet.localIP()); +} + +void loop() { + // wait for a new client: + EthernetClient client = server.available(); + + // when the client sends the first byte, say hello: + if (client) { + + boolean newClient = true; + for (byte i = 0; i < 4; i++) { + //check whether this client refers to the same socket as one of the existing instances: + if (clients[i] == client) { + newClient = false; + break; + } + } + + if (newClient) { + //check which of the existing clients can be overridden: + for (byte i = 0; i < 4; i++) { + if (!clients[i] && clients[i] != client) { + clients[i] = client; + // clear out the input buffer: + client.flush(); + Serial.println("We have a new client"); + client.print("Hello, client number: "); + client.print(i); + client.println(); + break; + } + } + } + + if (client.available() > 0) { + // read the bytes incoming from the client: + char thisChar = client.read(); + // echo the bytes back to all other connected clients: + for (byte i = 0; i < 4; i++) { + if (clients[i] && (clients[i] != client)) { + clients[i].write(thisChar); + } + } + // echo the bytes to the server as well: + Serial.write(thisChar); + } + } + for (byte i = 0; i < 4; i++) { + if (!(clients[i].connected())) { + // client.stop() invalidates the internal socket-descriptor, so next use of == will allways return false; + clients[i].stop(); + } + } +} diff --git a/examples/BarometricPressureWebServer/BarometricPressureWebServer.ino b/examples/BarometricPressureWebServer/BarometricPressureWebServer.ino new file mode 100644 index 0000000..32516cd --- /dev/null +++ b/examples/BarometricPressureWebServer/BarometricPressureWebServer.ino @@ -0,0 +1,222 @@ +/* + SCP1000 Barometric Pressure Sensor Display + + Serves the output of a Barometric Pressure Sensor as a web page. + Uses the SPI library. For details on the sensor, see: + http://www.sparkfun.com/commerce/product_info.php?products_id=8161 + + This sketch adapted from Nathan Seidle's SCP1000 example for PIC: + http://www.sparkfun.com/datasheets/Sensors/SCP1000-Testing.zip + + Circuit: + SCP1000 sensor attached to pins 6,7, and 11 - 13: + DRDY: pin 6 + CSB: pin 7 + MOSI: pin 11 + MISO: pin 12 + SCK: pin 13 + + created 31 July 2010 + by Tom Igoe + modified 23 Jun 2017 + by Wi6Labs + */ + +#include +#include +// the sensor communicates using SPI, so include the library: +#include + + +// assign a MAC address for the Ethernet controller. +// fill in your address here: +byte mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED +}; +// assign an IP address for the controller: +IPAddress ip(192, 168, 1, 20); + + +// Initialize the Ethernet server library +// with the IP address and port you want to use +// (port 80 is default for HTTP): +EthernetServer server(80); + + +//Sensor's memory register addresses: +const int PRESSURE = 0x1F; //3 most significant bits of pressure +const int PRESSURE_LSB = 0x20; //16 least significant bits of pressure +const int TEMPERATURE = 0x21; //16 bit temperature reading + +// pins used for the connection with the sensor +// the others you need are controlled by the SPI library): +const int dataReadyPin = 6; +const int chipSelectPin = 7; + +float temperature = 0.0; +long pressure = 0; +long lastReadingTime = 0; + +void setup() { + // start the SPI library: + SPI.begin(); + + // start the Ethernet connection and the server: + Ethernet.begin(mac, ip); + server.begin(); + + // initalize the data ready and chip select pins: + pinMode(dataReadyPin, INPUT); + pinMode(chipSelectPin, OUTPUT); + + Serial.begin(9600); + + //Configure SCP1000 for low noise configuration: + writeRegister(0x02, 0x2D); + writeRegister(0x01, 0x03); + writeRegister(0x03, 0x02); + + // give the sensor and Ethernet shield time to set up: + delay(1000); + + //Set the sensor to high resolution mode tp start readings: + writeRegister(0x03, 0x0A); + +} + +void loop() { + // check for a reading no more than once a second. + if (millis() - lastReadingTime > 1000) { + // if there's a reading ready, read it: + // don't do anything until the data ready pin is high: + if (digitalRead(dataReadyPin) == HIGH) { + getData(); + // timestamp the last time you got a reading: + lastReadingTime = millis(); + } + } + + // listen for incoming Ethernet connections: + listenForEthernetClients(); +} + + +void getData() { + Serial.println("Getting reading"); + //Read the temperature data + int tempData = readRegister(0x21, 2); + + // convert the temperature to celsius and display it: + temperature = (float)tempData / 20.0; + + //Read the pressure data highest 3 bits: + byte pressureDataHigh = readRegister(0x1F, 1); + pressureDataHigh &= 0b00000111; //you only needs bits 2 to 0 + + //Read the pressure data lower 16 bits: + unsigned int pressureDataLow = readRegister(0x20, 2); + //combine the two parts into one 19-bit number: + pressure = ((pressureDataHigh << 16) | pressureDataLow) / 4; + + Serial.print("Temperature: "); + Serial.print(temperature); + Serial.println(" degrees C"); + Serial.print("Pressure: " + String(pressure)); + Serial.println(" Pa"); +} + +void listenForEthernetClients() { + // listen for incoming clients + EthernetClient client = server.available(); + if (client) { + Serial.println("Got a client"); + // an http request ends with a blank line + boolean currentLineIsBlank = true; + while (client.connected()) { + if (client.available()) { + char c = client.read(); + // if you've gotten to the end of the line (received a newline + // character) and the line is blank, the http request has ended, + // so you can send a reply + if (c == '\n' && currentLineIsBlank) { + // send a standard http response header + client.println("HTTP/1.1 200 OK"); + client.println("Content-Type: text/html"); + client.println(); + // print the current readings, in HTML format: + client.print("Temperature: "); + client.print(temperature); + client.print(" degrees C"); + client.println("
"); + client.print("Pressure: " + String(pressure)); + client.print(" Pa"); + client.println("
"); + break; + } + if (c == '\n') { + // you're starting a new line + currentLineIsBlank = true; + } else if (c != '\r') { + // you've gotten a character on the current line + currentLineIsBlank = false; + } + } + } + // give the web browser time to receive the data + delay(1); + // close the connection: + client.stop(); + } +} + + +//Send a write command to SCP1000 +void writeRegister(byte registerName, byte registerValue) { + // SCP1000 expects the register name in the upper 6 bits + // of the byte: + registerName <<= 2; + // command (read or write) goes in the lower two bits: + registerName |= 0b00000010; //Write command + + // take the chip select low to select the device: + digitalWrite(chipSelectPin, LOW); + + SPI.transfer(registerName); //Send register location + SPI.transfer(registerValue); //Send value to record into register + + // take the chip select high to de-select: + digitalWrite(chipSelectPin, HIGH); +} + + +//Read register from the SCP1000: +unsigned int readRegister(byte registerName, int numBytes) { + byte inByte = 0; // incoming from the SPI read + unsigned int result = 0; // result to return + + // SCP1000 expects the register name in the upper 6 bits + // of the byte: + registerName <<= 2; + // command (read or write) goes in the lower two bits: + registerName &= 0b11111100; //Read command + + // take the chip select low to select the device: + digitalWrite(chipSelectPin, LOW); + // send the device the register you want to read: + int command = SPI.transfer(registerName); + // send a value of 0 to read the first byte returned: + inByte = SPI.transfer(0x00); + + result = inByte; + // if there's more than one byte returned, + // shift the first byte then get the second byte: + if (numBytes > 1) { + result = inByte << 8; + inByte = SPI.transfer(0x00); + result = result | inByte; + } + // take the chip select high to de-select: + digitalWrite(chipSelectPin, HIGH); + // return the result: + return (result); +} diff --git a/examples/ChatServer/ChatServer.ino b/examples/ChatServer/ChatServer.ino new file mode 100644 index 0000000..8ee5126 --- /dev/null +++ b/examples/ChatServer/ChatServer.ino @@ -0,0 +1,77 @@ +/* + Chat Server + + A simple server that distributes any incoming messages to all + connected clients. To use, telnet to your device's IP address and type. + You can see the client's input in the serial monitor as well. + + Circuit: + * STM32 board with Ethernet support + + created 18 Dec 2009 + by David A. Mellis + modified 9 Apr 2012 + by Tom Igoe + modified 23 Jun 2017 + by Wi6Labs + */ + +#include +#include + +// Enter a MAC address and IP address for your controller below. +// The IP address will be dependent on your local network. +// gateway and subnet are optional: +byte mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED +}; +IPAddress ip(192, 168, 1, 177); +IPAddress myDns(192,168,1, 1); +IPAddress gateway(192, 168, 1, 1); +IPAddress subnet(255, 255, 0, 0); + + +// telnet defaults to port 23 +EthernetServer server(23); +boolean alreadyConnected = false; // whether or not the client was connected previously + +void setup() { + // initialize the ethernet device + Ethernet.begin(mac, ip, myDns, gateway, subnet); + // start listening for clients + server.begin(); + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + + Serial.print("Chat server address:"); + Serial.println(Ethernet.localIP()); +} + +void loop() { + // wait for a new client: + EthernetClient client = server.available(); + + // when the client sends the first byte, say hello: + if (client) { + if (!alreadyConnected) { + // clear out the input buffer: + client.flush(); + Serial.println("We have a new client"); + client.println("Hello, client!"); + alreadyConnected = true; + } + + if (client.available() > 0) { + // read the bytes incoming from the client: + char thisChar = client.read(); + // echo the bytes back to the client: + server.write(thisChar); + // echo the bytes to the server as well: + Serial.write(thisChar); + } + } +} diff --git a/examples/DhcpAddressPrinter/DhcpAddressPrinter.ino b/examples/DhcpAddressPrinter/DhcpAddressPrinter.ino new file mode 100644 index 0000000..d17d97c --- /dev/null +++ b/examples/DhcpAddressPrinter/DhcpAddressPrinter.ino @@ -0,0 +1,98 @@ +/* + DHCP-based IP printer + + This sketch uses the DHCP extensions to the Ethernet library + to get an IP address via DHCP and print the address obtained. + + Circuit: + STM32 board with Ethernet support + + created 12 April 2011 + modified 9 Apr 2012 + by Tom Igoe + modified 02 Sept 2015 + by Arturo Guadalupi + modified 23 Jun 2017 + by Wi6Labs +*/ + +#include +#include + +// Enter a MAC address for your controller below. +byte mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED +}; + +// Initialize the Ethernet client library +// with the IP address and port of the server +// that you want to connect to (port 80 is default for HTTP): +EthernetClient client; + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(9600); + // this check is only needed on the Leonardo: + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // start the Ethernet connection: + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // no point in carrying on, so do nothing forevermore: + for (;;) + ; + } + // print your local IP address: + printIPAddress(); +} + +void loop() { + + switch (Ethernet.maintain()) + { + case 1: + //renewed fail + Serial.println("Error: renewed fail"); + break; + + case 2: + //renewed success + Serial.println("Renewed success"); + + //print your local IP address: + printIPAddress(); + break; + + case 3: + //rebind fail + Serial.println("Error: rebind fail"); + break; + + case 4: + //rebind success + Serial.println("Rebind success"); + + //print your local IP address: + printIPAddress(); + break; + + default: + //nothing happened + break; + + } +} + +void printIPAddress() +{ + Serial.print("My IP address: "); + for (byte thisByte = 0; thisByte < 4; thisByte++) { + // print the value of each byte of the IP address: + Serial.print(Ethernet.localIP()[thisByte], DEC); + Serial.print("."); + } + + Serial.println(); +} diff --git a/examples/DhcpChatServer/DhcpChatServer.ino b/examples/DhcpChatServer/DhcpChatServer.ino new file mode 100644 index 0000000..842bf4d --- /dev/null +++ b/examples/DhcpChatServer/DhcpChatServer.ino @@ -0,0 +1,92 @@ +/* + DHCP Chat Server + + A simple server that distributes any incoming messages to all + connected clients. To use, telnet to your device's IP address and type. + You can see the client's input in the serial monitor as well. + + THis version attempts to get an IP address using DHCP + + Circuit: + * STM32 board with Ethernet support + + created 21 May 2011 + modified 9 Apr 2012 + by Tom Igoe + modified 02 Sept 2015 + by Arturo Guadalupi + Based on ChatServer example by David A. Mellis + modified 23 Jun 2017 + by Wi6Labs + + */ + +#include +#include + +// Enter a MAC address and IP address for your controller below. +// The IP address will be dependent on your local network. +// gateway and subnet are optional: +byte mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED +}; +IPAddress ip(192, 168, 1, 177); +IPAddress myDns(192,168,1, 1); +IPAddress gateway(192, 168, 1, 1); +IPAddress subnet(255, 255, 0, 0); + +// telnet defaults to port 23 +EthernetServer server(23); +boolean gotAMessage = false; // whether or not you got a message from the client yet + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(9600); + // this check is only needed on the Leonardo: + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + + // start the Ethernet connection: + Serial.println("Trying to get an IP address using DHCP"); + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // initialize the Ethernet device not using DHCP: + Ethernet.begin(mac, ip, myDns, gateway, subnet); + } + // print your local IP address: + Serial.print("My IP address: "); + ip = Ethernet.localIP(); + for (byte thisByte = 0; thisByte < 4; thisByte++) { + // print the value of each byte of the IP address: + Serial.print(ip[thisByte], DEC); + Serial.print("."); + } + Serial.println(); + // start listening for clients + server.begin(); + +} + +void loop() { + // wait for a new client: + EthernetClient client = server.available(); + + // when the client sends the first byte, say hello: + if (client) { + if (!gotAMessage) { + Serial.println("We have a new client"); + client.println("Hello, client!"); + gotAMessage = true; + } + + // read the bytes incoming from the client: + char thisChar = client.read(); + // echo the bytes back to the client: + server.write(thisChar); + // echo the bytes to the server as well: + Serial.print(thisChar); + Ethernet.maintain(); + } +} diff --git a/examples/TelnetClient/TelnetClient.ino b/examples/TelnetClient/TelnetClient.ino new file mode 100644 index 0000000..e503504 --- /dev/null +++ b/examples/TelnetClient/TelnetClient.ino @@ -0,0 +1,90 @@ +/* + Telnet client + + This sketch connects to a a telnet server (http://www.google.com) + using an Arduino Wiznet Ethernet shield. You'll need a telnet server + to test this with. + Processing's ChatServer example (part of the network library) works well, + running on port 10002. It can be found as part of the examples + in the Processing application, available at + http://processing.org/ + + Circuit: + * STM32 board with Ethernet support + + created 14 Sep 2010 + modified 9 Apr 2012 + by Tom Igoe + modified 23 Jun 2017 + by Wi6Labs + + */ + +#include +#include + +// Enter a MAC address and IP address for your controller below. +// The IP address will be dependent on your local network: +byte mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED +}; +IPAddress ip(192, 168, 1, 177); + +// Enter the IP address of the server you're connecting to: +IPAddress server(1, 1, 1, 1); + +// Initialize the Ethernet client library +// with the IP address and port of the server +// that you want to connect to (port 23 is default for telnet; +// if you're using Processing's ChatServer, use port 10002): +EthernetClient client; + +void setup() { + // start the Ethernet connection: + Ethernet.begin(mac, ip); + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + + // give the Ethernet shield a second to initialize: + delay(1000); + Serial.println("connecting..."); + + // if you get a connection, report back via serial: + if (client.connect(server, 10002)) { + Serial.println("connected"); + } else { + // if you didn't get a connection to the server: + Serial.println("connection failed"); + } +} + +void loop() { + // if there are incoming bytes available + // from the server, read them and print them: + if (client.available()) { + char c = client.read(); + Serial.print(c); + } + + // as long as there are bytes in the serial queue, + // read them and send them out the socket if it's open: + while (Serial.available() > 0) { + char inChar = Serial.read(); + if (client.connected()) { + client.print(inChar); + } + } + + // if the server's disconnected, stop the client: + if (!client.connected()) { + Serial.println(); + Serial.println("disconnecting."); + client.stop(); + // do nothing: + while (true); + } +} diff --git a/examples/UDPSendReceiveString/UDPSendReceiveString.ino b/examples/UDPSendReceiveString/UDPSendReceiveString.ino new file mode 100644 index 0000000..00e040b --- /dev/null +++ b/examples/UDPSendReceiveString/UDPSendReceiveString.ino @@ -0,0 +1,115 @@ +/* + UDPSendReceiveString: + This sketch receives UDP message strings, prints them to the serial port + and sends an "acknowledge" string back to the sender + + A Processing sketch is included at the end of file that can be used to send + and received messages for testing with a computer. + + created 21 Aug 2010 + by Michael Margolis + modified 23 Jun 2017 + by Wi6Labs + + This code is in the public domain. + */ + +#include +#include +#include // UDP library from: bjoern@cs.stanford.edu 12/30/2008 + + +// Enter a MAC address and IP address for your controller below. +// The IP address will be dependent on your local network: +byte mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED +}; +IPAddress ip(192, 168, 1, 177); + +unsigned int localPort = 8888; // local port to listen on + +// buffers for receiving and sending data +char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet, +char ReplyBuffer[] = "acknowledged"; // a string to send back + +// An EthernetUDP instance to let us send and receive packets over UDP +EthernetUDP Udp; + +void setup() { + // start the Ethernet and UDP: + Ethernet.begin(mac, ip); + Udp.begin(localPort); + + Serial.begin(9600); +} + +void loop() { + // if there's data available, read a packet + int packetSize = Udp.parsePacket(); + if (packetSize) { + Serial.print("Received packet of size "); + Serial.println(packetSize); + Serial.print("From "); + IPAddress remote = Udp.remoteIP(); + for (int i = 0; i < 4; i++) { + Serial.print(remote[i], DEC); + if (i < 3) { + Serial.print("."); + } + } + Serial.print(", port "); + Serial.println(Udp.remotePort()); + + // read the packet into packetBufffer + Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE); + Serial.println("Contents:"); + Serial.println(packetBuffer); + + // send a reply to the IP address and port that sent us the packet we received + Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); + Udp.write(ReplyBuffer); + Udp.endPacket(); + } + delay(10); +} + + +/* + Processing sketch to run with this example + ===================================================== + + // Processing UDP example to send and receive string data from Arduino + // press any key to send the "Hello Arduino" message + + + import hypermedia.net.*; + + UDP udp; // define the UDP object + + + void setup() { + udp = new UDP( this, 6000 ); // create a new datagram connection on port 6000 + //udp.log( true ); // <-- printout the connection activity + udp.listen( true ); // and wait for incoming message + } + + void draw() + { + } + + void keyPressed() { + String ip = "192.168.1.177"; // the remote IP address + int port = 8888; // the destination port + + udp.send("Hello World", ip, port ); // the message to send + + } + + void receive( byte[] data ) { // <-- default handler + //void receive( byte[] data, String ip, int port ) { // <-- extended handler + + for(int i=0; i < data.length; i++) + print(char(data[i])); + println(); + } + */ diff --git a/examples/UdpNtpClient/UdpNtpClient.ino b/examples/UdpNtpClient/UdpNtpClient.ino new file mode 100644 index 0000000..716c365 --- /dev/null +++ b/examples/UdpNtpClient/UdpNtpClient.ino @@ -0,0 +1,133 @@ +/* + + Udp NTP Client + + Get the time from a Network Time Protocol (NTP) time server + Demonstrates use of UDP sendPacket and ReceivePacket + For more on NTP time servers and the messages needed to communicate with them, + see http://en.wikipedia.org/wiki/Network_Time_Protocol + + created 4 Sep 2010 + by Michael Margolis + modified 9 Apr 2012 + by Tom Igoe + modified 02 Sept 2015 + by Arturo Guadalupi + modified 23 Jun 2017 + by Wi6Labs + + This code is in the public domain. + + */ + +#include +#include +#include + +// Enter a MAC address for your controller below. +byte mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED +}; + +unsigned int localPort = 8888; // local port to listen for UDP packets + +char timeServer[] = "time.nist.gov"; // time.nist.gov NTP server + +const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message + +byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets + +// A UDP instance to let us send and receive packets over UDP +EthernetUDP Udp; + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + + // start Ethernet and UDP + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // no point in carrying on, so do nothing forevermore: + for (;;) + ; + } + Udp.begin(localPort); +} + +void loop() { + sendNTPpacket(timeServer); // send an NTP packet to a time server + + // wait to see if a reply is available + delay(1000); + if (Udp.parsePacket()) { + // We've received a packet, read the data from it + Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer + + // the timestamp starts at byte 40 of the received packet and is four bytes, + // or two words, long. First, extract the two words: + + unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); + unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); + // combine the four bytes (two words) into a long integer + // this is NTP time (seconds since Jan 1 1900): + unsigned long secsSince1900 = highWord << 16 | lowWord; + Serial.print("Seconds since Jan 1 1900 = "); + Serial.println(secsSince1900); + + // now convert NTP time into everyday time: + Serial.print("Unix time = "); + // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: + const unsigned long seventyYears = 2208988800UL; + // subtract seventy years: + unsigned long epoch = secsSince1900 - seventyYears; + // print Unix time: + Serial.println(epoch); + + + // print the hour, minute and second: + Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT) + Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day) + Serial.print(':'); + if (((epoch % 3600) / 60) < 10) { + // In the first 10 minutes of each hour, we'll want a leading '0' + Serial.print('0'); + } + Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute) + Serial.print(':'); + if ((epoch % 60) < 10) { + // In the first 10 seconds of each minute, we'll want a leading '0' + Serial.print('0'); + } + Serial.println(epoch % 60); // print the second + } + // wait ten seconds before asking for the time again + delay(10000); + Ethernet.maintain(); +} + +// send an NTP request to the time server at the given address +void sendNTPpacket(char* address) { + // set all bytes in the buffer to 0 + memset(packetBuffer, 0, NTP_PACKET_SIZE); + // Initialize values needed to form NTP request + // (see URL above for details on the packets) + packetBuffer[0] = 0b11100011; // LI, Version, Mode + packetBuffer[1] = 0; // Stratum, or type of clock + packetBuffer[2] = 6; // Polling Interval + packetBuffer[3] = 0xEC; // Peer Clock Precision + // 8 bytes of zero for Root Delay & Root Dispersion + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; + + // all NTP fields have been given values, now + // you can send a packet requesting a timestamp: + Udp.beginPacket(address, 123); //NTP requests are to port 123 + Udp.write(packetBuffer, NTP_PACKET_SIZE); + Udp.endPacket(); +} diff --git a/examples/WebClient/WebClient.ino b/examples/WebClient/WebClient.ino new file mode 100644 index 0000000..18be08d --- /dev/null +++ b/examples/WebClient/WebClient.ino @@ -0,0 +1,84 @@ +/* + Web client + + This sketch connects to a website (http://www.google.com) + + Circuit: + * STM32 board with Ethernet support + + created 18 Dec 2009 + by David A. Mellis + modified 9 Apr 2012 + by Tom Igoe, based on work by Adrian McEwen + modified 23 Jun 2017 + by Wi6Labs + + */ + +#include +#include + +// Enter a MAC address for your controller below. +byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; +// if you don't want to use DNS (and reduce your sketch size) +// use the numeric IP instead of the name for the server: +//IPAddress server(74,125,232,128); // numeric IP for Google (no DNS) +char server[] = "www.google.com"; // name address for Google (using DNS) + +// Set the static IP address to use if the DHCP fails to assign +IPAddress ip(192, 168, 0, 177); + +// Initialize the Ethernet client library +// with the IP address and port of the server +// that you want to connect to (port 80 is default for HTTP): +EthernetClient client; + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // start the Ethernet connection: + if (Ethernet.begin(mac) == 0) { + Serial.println("Failed to configure Ethernet using DHCP"); + // try to congifure using IP address instead of DHCP: + Ethernet.begin(mac, ip); + } + // give the Ethernet shield a second to initialize: + delay(1000); + Serial.println("connecting..."); + + // if you get a connection, report back via serial: + if (client.connect(server, 80)) { + Serial.println("connected"); + // Make a HTTP request: + client.println("GET /search?q=arduino HTTP/1.1"); + client.println("Host: www.google.com"); + client.println("Connection: close"); + client.println(); + } else { + // if you didn't get a connection to the server: + Serial.println("connection failed"); + } +} + +void loop() { + // if there are incoming bytes available + // from the server, read them and print them: + if (client.available()) { + char c = client.read(); + Serial.print(c); + } + + // if the server's disconnected, stop the client: + if (!client.connected()) { + Serial.println(); + Serial.println("disconnecting."); + client.stop(); + + // do nothing forevermore: + while (true); + } +} diff --git a/examples/WebClientRepeating/WebClientRepeating.ino b/examples/WebClientRepeating/WebClientRepeating.ino new file mode 100644 index 0000000..61919d7 --- /dev/null +++ b/examples/WebClientRepeating/WebClientRepeating.ino @@ -0,0 +1,104 @@ +/* + Repeating Web client + + This sketch connects to a a web server and makes a request. + + This example uses DNS, by assigning the Ethernet client with a MAC address, + IP address, and DNS address. + + Circuit: + * STM32 board with Ethernet support + + created 19 Apr 2012 + by Tom Igoe + modified 21 Jan 2014 + by Federico Vanzati + modified 23 Jun 2017 + by Wi6Labs + + http://www.arduino.cc/en/Tutorial/WebClientRepeating + This code is in the public domain. + + */ + +#include +#include + +// assign a MAC address for the ethernet controller. +// fill in your address here: +byte mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED +}; +// fill in an available IP address on your network here, +// for manual configuration: +IPAddress ip(192, 168, 1, 177); + +// fill in your Domain Name Server address here: +IPAddress myDns(1, 1, 1, 1); + +// initialize the library instance: +EthernetClient client; + +char server[] = "www.arduino.cc"; +//IPAddress server(64,131,82,241); + +unsigned long lastConnectionTime = 0; // last time you connected to the server, in milliseconds +const unsigned long postingInterval = 10L * 1000L; // delay between updates, in milliseconds +// the "L" is needed to use long type numbers + +void setup() { + // start serial port: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + // give the ethernet module time to boot up: + delay(1000); + // start the Ethernet connection using a fixed IP address and DNS server: + Ethernet.begin(mac, ip, myDns); + // print the Ethernet board/shield's IP address: + Serial.print("My IP address: "); + Serial.println(Ethernet.localIP()); +} + +void loop() { + // if there's incoming data from the net connection. + // send it out the serial port. This is for debugging + // purposes only: + if (client.available()) { + char c = client.read(); + Serial.write(c); + } + + // if ten seconds have passed since your last connection, + // then connect again and send data: + if (millis() - lastConnectionTime > postingInterval) { + httpRequest(); + } + +} + +// this method makes a HTTP connection to the server: +void httpRequest() { + // close any connection before send a new request. + // This will free the socket on the WiFi shield + client.stop(); + + // if there's a successful connection: + if (client.connect(server, 80)) { + Serial.println("connecting..."); + // send the HTTP GET request: + client.println("GET /latest.txt HTTP/1.1"); + client.println("Host: www.arduino.cc"); + client.println("User-Agent: arduino-ethernet"); + client.println("Connection: close"); + client.println(); + + // note the time that the connection was made: + lastConnectionTime = millis(); + } else { + // if you couldn't make a connection: + Serial.println("connection failed"); + } +} diff --git a/examples/WebServer/WebServer.ino b/examples/WebServer/WebServer.ino new file mode 100644 index 0000000..3e6b318 --- /dev/null +++ b/examples/WebServer/WebServer.ino @@ -0,0 +1,102 @@ +/* + Web Server + + A simple web server that shows the value of the analog input pins. + + Circuit: + * STM32 board with Ethernet support + * Analog inputs attached to pins A0 through A5 (optional) + + created 18 Dec 2009 + by David A. Mellis + modified 9 Apr 2012 + by Tom Igoe + modified 02 Sept 2015 + by Arturo Guadalupi + modified 23 Jun 2017 + by Wi6Labs + + */ + +#include +#include + +// Enter a MAC address and IP address for your controller below. +// The IP address will be dependent on your local network: +byte mac[] = { + 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED +}; +IPAddress ip(192, 168, 1, 177); + +// Initialize the Ethernet server library +// with the IP address and port you want to use +// (port 80 is default for HTTP): +EthernetServer server(80); + +void setup() { + // Open serial communications and wait for port to open: + Serial.begin(9600); + while (!Serial) { + ; // wait for serial port to connect. Needed for native USB port only + } + + + // start the Ethernet connection and the server: + Ethernet.begin(mac, ip); + server.begin(); + Serial.print("server is at "); + Serial.println(Ethernet.localIP()); +} + + +void loop() { + // listen for incoming clients + EthernetClient client = server.available(); + if (client) { + Serial.println("new client"); + // an http request ends with a blank line + boolean currentLineIsBlank = true; + while (client.connected()) { + if (client.available()) { + char c = client.read(); + Serial.write(c); + // if you've gotten to the end of the line (received a newline + // character) and the line is blank, the http request has ended, + // so you can send a reply + if (c == '\n' && currentLineIsBlank) { + // send a standard http response header + client.println("HTTP/1.1 200 OK"); + client.println("Content-Type: text/html"); + client.println("Connection: close"); // the connection will be closed after completion of the response + client.println("Refresh: 5"); // refresh the page automatically every 5 sec + client.println(); + client.println(""); + client.println(""); + // output the value of each analog input pin + for (int analogChannel = 0; analogChannel < 6; analogChannel++) { + int sensorReading = analogRead(analogChannel); + client.print("analog input "); + client.print(analogChannel); + client.print(" is "); + client.print(sensorReading); + client.println("
"); + } + client.println(""); + break; + } + if (c == '\n') { + // you're starting a new line + currentLineIsBlank = true; + } else if (c != '\r') { + // you've gotten a character on the current line + currentLineIsBlank = false; + } + } + } + // give the web browser time to receive the data + delay(1); + // close the connection: + client.stop(); + Serial.println("client disconnected"); + } +} diff --git a/keywords.txt b/keywords.txt new file mode 100644 index 0000000..1f19b2a --- /dev/null +++ b/keywords.txt @@ -0,0 +1,40 @@ +####################################### +# Syntax Coloring Map For Ethernet +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +Ethernet KEYWORD1 Ethernet +EthernetClient KEYWORD1 EthernetClient +EthernetServer KEYWORD1 EthernetServer +IPAddress KEYWORD1 EthernetIPAddress + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +status KEYWORD2 +connect KEYWORD2 +write KEYWORD2 +available KEYWORD2 +read KEYWORD2 +peek KEYWORD2 +flush KEYWORD2 +stop KEYWORD2 +connected KEYWORD2 +begin KEYWORD2 +beginPacket KEYWORD2 +endPacket KEYWORD2 +parsePacket KEYWORD2 +remoteIP KEYWORD2 +remotePort KEYWORD2 +getSocketNumber KEYWORD2 +localIP KEYWORD2 +maintain KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..b0037f8 --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=STM32Ethernet +version=1.0.0 +author=Various +maintainer=STMicroelectronics +sentence=Enables network connection (local and Internet) using the STM32 Board. +paragraph=With this library you can use the STM32 board to connect to Internet. The library provides both Client and server functionalities. The library permits you to connect to a local network also with DHCP and to resolve DNS. This library depends on the LwIP library. +category=Communication +url=http://www.arduino.cc/en/Reference/Ethernet +architectures=stm32 diff --git a/src/Dhcp.cpp b/src/Dhcp.cpp new file mode 100644 index 0000000..ac75c71 --- /dev/null +++ b/src/Dhcp.cpp @@ -0,0 +1,129 @@ +// DHCP Library v0.3 - April 25, 2009 +// Author: Jordan Terrell - blog.jordanterrell.com + +#include +#include +#include "Dhcp.h" +#include "Arduino.h" +#include "utility/stm32_eth.h" + +int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) +{ + UNUSED(responseTimeout); + _timeout = timeout; + _dhcp_lease_state = DHCP_CHECK_NONE; + + // zero out _dhcpMacAddr + memset(_dhcpMacAddr, 0, 6); + reset_DHCP_lease(); + + memcpy((void*)_dhcpMacAddr, (void*)mac, 6); + _dhcp_state = STATE_DHCP_START; + stm32_set_DHCP_state(_dhcp_state); + return request_DHCP_lease(); +} + +void DhcpClass::reset_DHCP_lease(){ + _dhcp_state = STATE_DHCP_RELEASE; + stm32_set_DHCP_state(_dhcp_state); + stm32_eth_scheduler(); + _dhcp_state = stm32_get_DHCP_state(); +} + +//return:0 on error, 1 if request is sent and response is received +int DhcpClass::request_DHCP_lease(){ + + int result = 0; + unsigned long startTime = millis(); + + while(_dhcp_state != STATE_DHCP_LEASED) + { + stm32_eth_scheduler(); + _dhcp_state = stm32_get_DHCP_state(); + + if(result != 1 && ((millis() - startTime) > _timeout)) { + reset_DHCP_lease(); + break; + } + } + + if(_dhcp_state == STATE_DHCP_LEASED) { + result = 1; + } + + return result; +} + +/* + returns: + 0/DHCP_CHECK_NONE: nothing happened + 1/DHCP_CHECK_RENEW_FAIL: renew failed + 2/DHCP_CHECK_RENEW_OK: renew success + 3/DHCP_CHECK_REBIND_FAIL: rebind fail + 4/DHCP_CHECK_REBIND_OK: rebind success +*/ +int DhcpClass::checkLease(){ + int rc = DHCP_CHECK_NONE; + + stm32_eth_scheduler(); + rc = stm32_get_DHCP_lease_state(); + + if(rc != _dhcp_lease_state) + { + switch(_dhcp_lease_state) { + case DHCP_CHECK_NONE: + _dhcp_lease_state = rc; + rc = DHCP_CHECK_NONE; + break; + + case DHCP_CHECK_RENEW_OK: + _dhcp_lease_state = rc; + if(rc == DHCP_CHECK_NONE) { + rc = DHCP_CHECK_RENEW_OK; + } else { + rc = DHCP_CHECK_RENEW_FAIL; + } + break; + + case DHCP_CHECK_REBIND_OK: + _dhcp_lease_state = rc; + if(rc == DHCP_CHECK_NONE) { + rc = DHCP_CHECK_REBIND_OK; + } else { + rc = DHCP_CHECK_REBIND_FAIL; + } + break; + + default: + _dhcp_lease_state = DHCP_CHECK_NONE; + break; + } + } + + return rc; +} + +IPAddress DhcpClass::getLocalIp() +{ + return IPAddress(stm32_eth_get_ipaddr()); +} + +IPAddress DhcpClass::getSubnetMask() +{ + return IPAddress(stm32_eth_get_netmaskaddr()); +} + +IPAddress DhcpClass::getGatewayIp() +{ + return IPAddress(stm32_eth_get_gwaddr()); +} + +IPAddress DhcpClass::getDhcpServerIp() +{ + return IPAddress(stm32_eth_get_dhcpaddr()); +} + +IPAddress DhcpClass::getDnsServerIp() +{ + return IPAddress(stm32_eth_get_dnsaddr()); +} diff --git a/src/Dhcp.h b/src/Dhcp.h new file mode 100644 index 0000000..089fbde --- /dev/null +++ b/src/Dhcp.h @@ -0,0 +1,45 @@ +// DHCP Library v0.3 - April 25, 2009 +// Author: Jordan Terrell - blog.jordanterrell.com + +#ifndef Dhcp_h +#define Dhcp_h + +#include "EthernetUdp.h" + +/* DHCP state machine. */ +#define STATE_DHCP_STOP DHCP_OFF +#define STATE_DHCP_START DHCP_START +#define STATE_DHCP_DISCOVER DHCP_WAIT_ADDRESS +#define STATE_DHCP_REQUEST 0 +#define STATE_DHCP_LEASED DHCP_ADDRESS_ASSIGNED +#define STATE_DHCP_REREQUEST 0 +#define STATE_DHCP_RELEASE DHCP_ASK_RELEASE + +#define DHCP_CHECK_NONE (0) +#define DHCP_CHECK_RENEW_FAIL (1) +#define DHCP_CHECK_RENEW_OK (2) +#define DHCP_CHECK_REBIND_FAIL (3) +#define DHCP_CHECK_REBIND_OK (4) + +class DhcpClass { +private: + uint8_t _dhcpMacAddr[6]; + unsigned long _timeout; + uint8_t _dhcp_lease_state; + uint8_t _dhcp_state; + + int request_DHCP_lease(); + void reset_DHCP_lease(); + +public: + IPAddress getLocalIp(); + IPAddress getSubnetMask(); + IPAddress getGatewayIp(); + IPAddress getDhcpServerIp(); + IPAddress getDnsServerIp(); + + int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); + int checkLease(); +}; + +#endif diff --git a/src/Dns.cpp b/src/Dns.cpp new file mode 100644 index 0000000..434c139 --- /dev/null +++ b/src/Dns.cpp @@ -0,0 +1,107 @@ +// Arduino DNS client for WizNet5100-based Ethernet shield +// (c) Copyright 2009-2010 MCQN Ltd. +// Released under Apache License, version 2.0 + +#include "EthernetUdp.h" +#include "utility/stm32_eth.h" + +#include "Dns.h" +#include +#include "Arduino.h" + +// Possible return codes from ProcessResponse +#define SUCCESS 1 +#define TIMED_OUT -1 +#define INVALID_SERVER -2 +#define TRUNCATED -3 +#define INVALID_RESPONSE -4 + +void DNSClient::begin(const IPAddress& aDNSServer) +{ + iDNSServer = aDNSServer; + stm32_dns_init(iDNSServer.raw_address()); +} + + +int DNSClient::inet_aton(const char* address, IPAddress& result) +{ + uint16_t acc = 0; // Accumulator + uint8_t dots = 0; + + if(address == NULL) + return 0; + + while (*address) + { + char c = *address++; + if (c >= '0' && c <= '9') + { + acc = acc * 10 + (c - '0'); + if (acc > 255) { + // Value out of [0..255] range + return 0; + } + } + else if (c == '.') + { + if (dots == 3) { + // Too much dots (there must be 3 dots) + return 0; + } + result[dots++] = acc; + acc = 0; + } + else + { + // Invalid char + return 0; + } + } + + if (dots != 3) { + // Too few dots (there must be 3 dots) + return 0; + } + result[3] = acc; + return 1; +} + +int DNSClient::getHostByName(const char* aHostname, IPAddress& aResult) +{ + int ret = 0; + uint32_t ipResult = 0; + + // See if it's a numeric IP address + if (inet_aton(aHostname, aResult)) + { + // It is, our work here is done + return SUCCESS; + } + + // Check we've got a valid DNS server to use + if (iDNSServer == INADDR_NONE) + { + return INVALID_SERVER; + } + + ret = stm32_dns_gethostbyname(aHostname, &ipResult); + aResult = IPAddress(ipResult); + + return ret; +} + +/* Deprecated function. Do not use anymore. */ +uint16_t DNSClient::BuildRequest(const char* aName) +{ + UNUSED(aName); + return 0; +} + +/* Deprecated function. Do not use anymore. */ +uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress& aAddress) +{ + UNUSED(aTimeout); + UNUSED(aAddress); + // If we get here then we haven't found an answer + return -10;//INVALID_RESPONSE; +} diff --git a/src/Dns.h b/src/Dns.h new file mode 100644 index 0000000..6f2773f --- /dev/null +++ b/src/Dns.h @@ -0,0 +1,39 @@ +// Arduino DNS client for WizNet5100-based Ethernet shield +// (c) Copyright 2009-2010 MCQN Ltd. +// Released under Apache License, version 2.0 + +#ifndef DNSClient_h +#define DNSClient_h + +#include + +class DNSClient +{ +public: + // ctor + void begin(const IPAddress& aDNSServer); + + /** Convert a numeric IP address string into a four-byte IP address. + @param aIPAddrString IP address to convert + @param aResult IPAddress structure to store the returned IP address + @result 1 if aIPAddrString was successfully converted to an IP address, + else error code + */ + int inet_aton(const char *aIPAddrString, IPAddress& aResult); + + /** Resolve the given hostname to an IP address. + @param aHostname Name to be resolved + @param aResult IPAddress structure to store the returned IP address + @result 1 if aIPAddrString was successfully converted to an IP address, + else error code + */ + int getHostByName(const char* aHostname, IPAddress& aResult); + +protected: + uint16_t BuildRequest(const char* aName); + uint16_t ProcessResponse(uint16_t aTimeout, IPAddress& aAddress); + + IPAddress iDNSServer; +}; + +#endif diff --git a/src/EthernetClient.cpp b/src/EthernetClient.cpp new file mode 100644 index 0000000..f6aef07 --- /dev/null +++ b/src/EthernetClient.cpp @@ -0,0 +1,201 @@ +extern "C" { + #include "string.h" +} + +#include "Arduino.h" + +#include "STM32Ethernet.h" +#include "EthernetClient.h" +#include "EthernetServer.h" +#include "Dns.h" + +EthernetClient::EthernetClient() { +} + +/* Deprecated constructor. Keeps compatibility with W5100 architecture +sketches but sock is ignored. */ +EthernetClient::EthernetClient(uint8_t sock) { + UNUSED(sock); +} + +EthernetClient::EthernetClient(struct tcp_struct *tcpClient) { + _tcp_client = tcpClient; +} + +int EthernetClient::connect(const char* host, uint16_t port) { + // Look up the host first + int ret = 0; + DNSClient dns; + IPAddress remote_addr; + + dns.begin(Ethernet.dnsServerIP()); + ret = dns.getHostByName(host, remote_addr); + if (ret == 1) { + return connect(remote_addr, port); + } else { + return ret; + } +} + +int EthernetClient::connect(IPAddress ip, uint16_t port) { + /* Can't create twice the same client */ + if(_tcp_client != NULL) { + return 0; + } + + /* Allocates memory for client */ + _tcp_client = (struct tcp_struct *)mem_malloc(sizeof(struct tcp_struct)); + + if(_tcp_client == NULL) { + return 0; + } + + /* Creates a new TCP protocol control block */ + _tcp_client->pcb = tcp_new(); + + if(_tcp_client->pcb == NULL) { + return 0; + } + + _tcp_client->data.p = NULL; + _tcp_client->data.available = 0; + _tcp_client->state = TCP_NONE; + + ip_addr_t ipaddr; + tcp_arg(_tcp_client->pcb, _tcp_client); + if(ERR_OK != tcp_connect(_tcp_client->pcb, u8_to_ip_addr(rawIPAddress(ip), &ipaddr), port, &tcp_connected_callback)) { + stop(); + return 0; + } + + uint32_t startTime = millis(); + while(_tcp_client->state == TCP_NONE) { + stm32_eth_scheduler(); + if((_tcp_client->state == TCP_CLOSING) || ((millis() - startTime) >= 10000)) { + stop(); + return 0; + } + } + + return 1; +} + +size_t EthernetClient::write(uint8_t b) { + return write(&b, 1); +} + +size_t EthernetClient::write(const uint8_t *buf, size_t size) { + if( (_tcp_client == NULL) || (_tcp_client->pcb == NULL) || + (buf == NULL) || (size == 0)) { + return 0; + } + + /* If client not connected or accepted, it can't write because connection is + not ready */ + if((_tcp_client->state != TCP_ACCEPTED) && + (_tcp_client->state != TCP_CONNECTED)) { + return 0; + } + + if(ERR_OK != tcp_write(_tcp_client->pcb, buf, size, TCP_WRITE_FLAG_COPY)) { + return 0; + } + + //Force to send data right now! + if(ERR_OK != tcp_output(_tcp_client->pcb)) { + return 0; + } + + stm32_eth_scheduler(); + + return size; +} + +int EthernetClient::available() { + stm32_eth_scheduler(); + if(_tcp_client != NULL) { + return _tcp_client->data.available; + } + return 0; +} + +int EthernetClient::read() { + uint8_t b; + if((_tcp_client != NULL) && (_tcp_client->data.p != NULL)) { + stm32_get_data(&(_tcp_client->data), &b, 1); + return b; + } + // No data available + return -1; +} + +int EthernetClient::read(uint8_t *buf, size_t size) { + if((_tcp_client != NULL) && (_tcp_client->data.p != NULL)) { + return stm32_get_data(&(_tcp_client->data), buf, size); + } + return -1; +} + +int EthernetClient::peek() { + uint8_t b; + // Unlike recv, peek doesn't check to see if there's any data available, so we must + if (!available()) + return -1; + b = pbuf_get_at(_tcp_client->data.p, 0); + return b; +} + +void EthernetClient::flush() { + if((_tcp_client == NULL) || (_tcp_client->pcb == NULL)) { + return; + } + tcp_output(_tcp_client->pcb); + stm32_eth_scheduler(); +} + +void EthernetClient::stop() { + if(_tcp_client == NULL) { + return; + } + + // Close tcp connection. If failed, force to close connection. + if(ERR_OK != tcp_close(_tcp_client->pcb)) { + tcp_abort(_tcp_client->pcb); + } + + _tcp_client->pcb = NULL; + + mem_free(_tcp_client); + _tcp_client = NULL; +} + +uint8_t EthernetClient::connected() { + uint8_t s = status(); + return ((available() && (s == TCP_CLOSING)) || + (s == TCP_CONNECTED) || (s == TCP_ACCEPTED)); +} + +uint8_t EthernetClient::status() { + if(_tcp_client == NULL) { + return TCP_NONE; + } + return _tcp_client->state; +} + +// the next function allows us to use the client returned by +// EthernetServer::available() as the condition in an if-statement. + +EthernetClient::operator bool() { + return _tcp_client != NULL; +} + +bool EthernetClient::operator==(const EthernetClient& rhs) { + return _tcp_client == rhs._tcp_client && _tcp_client->pcb == rhs._tcp_client->pcb; +} + +/* This function is not a function defined by Arduino. This is a function +specific to the W5100 architecture. To keep the compatibility we leave it and +returns always 0. */ +uint8_t EthernetClient::getSocketNumber() { + return 0; +} diff --git a/src/EthernetClient.h b/src/EthernetClient.h new file mode 100644 index 0000000..1cf73d8 --- /dev/null +++ b/src/EthernetClient.h @@ -0,0 +1,43 @@ +#ifndef ethernetclient_h +#define ethernetclient_h +#include "Arduino.h" +#include "Print.h" +#include "Client.h" +#include "IPAddress.h" +#include "utility/stm32_eth.h" + +class EthernetClient : public Client { + +public: + EthernetClient(); + EthernetClient(uint8_t sock); + EthernetClient(struct tcp_struct *tcpClient); + + uint8_t status(); + virtual int connect(IPAddress ip, uint16_t port); + virtual int connect(const char *host, 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 void flush(); + virtual void stop(); + virtual uint8_t connected(); + virtual operator bool(); + virtual bool operator==(const bool value) { return bool() == value; } + virtual bool operator!=(const bool value) { return bool() != value; } + virtual bool operator==(const EthernetClient&); + virtual bool operator!=(const EthernetClient& rhs) { return !this->operator==(rhs); }; + uint8_t getSocketNumber(); + + friend class EthernetServer; + + using Print::write; + +private: + struct tcp_struct *_tcp_client; +}; + +#endif diff --git a/src/EthernetServer.cpp b/src/EthernetServer.cpp new file mode 100644 index 0000000..f5353d8 --- /dev/null +++ b/src/EthernetServer.cpp @@ -0,0 +1,99 @@ +extern "C" { +#include "string.h" +} + +#include "STM32Ethernet.h" +#include "EthernetClient.h" +#include "EthernetServer.h" + +EthernetServer::EthernetServer(uint16_t port) +{ + _port = port; +} + +void EthernetServer::begin() +{ + if(_tcp_server.pcb != NULL) { + return; + } + + _tcp_server.pcb = tcp_new(); + + if(_tcp_server.pcb == NULL) { + return; + } + + tcp_arg(_tcp_server.pcb, &_tcp_client); + _tcp_server.state = TCP_NONE; + + if(ERR_OK != tcp_bind(_tcp_server.pcb, IP_ADDR_ANY, _port)) { + memp_free(MEMP_TCP_PCB, _tcp_server.pcb); + _tcp_server.pcb = NULL; + return; + } + + _tcp_server.pcb = tcp_listen(_tcp_server.pcb); + tcp_accept(_tcp_server.pcb, tcp_accept_callback); +} + +void EthernetServer::accept() +{ + /* Free client if disconnected */ + for (int n = 0; n < MAX_CLIENT; n++) { + if(_tcp_client[n] != NULL) { + EthernetClient client(_tcp_client[n]); + if (client.status() == TCP_CLOSING) { + mem_free(_tcp_client[n]); + _tcp_client[n] = NULL; + } + } + } +} + +EthernetClient EthernetServer::available() +{ + accept(); + + for (int n = 0; n < MAX_CLIENT; n++) { + if(_tcp_client[n] != NULL) { + if(_tcp_client[n]->pcb != NULL) { + EthernetClient client(_tcp_client[n]); + uint8_t s = client.status(); + if (s == TCP_ACCEPTED) { + if (client.available()) { + return client; + } + } + } + } + } + + struct tcp_struct *default_client = NULL; + return EthernetClient(default_client); +} + +size_t EthernetServer::write(uint8_t b) +{ + return write(&b, 1); +} + +size_t EthernetServer::write(const uint8_t *buffer, size_t size) +{ + size_t n = 0; + + accept(); + + for (int n = 0; n < MAX_CLIENT; n++) { + if(_tcp_client[n] != NULL) { + if(_tcp_client[n]->pcb != NULL) { + EthernetClient client(_tcp_client[n]); + uint8_t s = client.status(); + if (s == TCP_ACCEPTED) { + n += client.write(buffer, size); + } + } + } + } + + return n; +} diff --git a/src/EthernetServer.h b/src/EthernetServer.h new file mode 100644 index 0000000..317bf41 --- /dev/null +++ b/src/EthernetServer.h @@ -0,0 +1,25 @@ +#ifndef ethernetserver_h +#define ethernetserver_h + +#include "Server.h" + +class EthernetClient; + +class EthernetServer : +public Server { +private: + uint16_t _port; + struct tcp_struct _tcp_server; + struct tcp_struct *_tcp_client[MAX_CLIENT]; + + void accept(void); +public: + EthernetServer(uint16_t); + EthernetClient available(); + virtual void begin(); + virtual size_t write(uint8_t); + virtual size_t write(const uint8_t *buf, size_t size); + using Print::write; +}; + +#endif diff --git a/src/EthernetUdp.cpp b/src/EthernetUdp.cpp new file mode 100644 index 0000000..c5ae45c --- /dev/null +++ b/src/EthernetUdp.cpp @@ -0,0 +1,255 @@ +/* + * Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield. + * This version only offers minimal wrapping of socket.c/socket.h + * Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ + * + * MIT License: + * Copyright (c) 2008 Bjoern Hartmann + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * bjoern@cs.stanford.edu 12/30/2008 + */ + +#include "STM32Ethernet.h" +#include "Udp.h" +#include "Dns.h" + +/* Constructor */ +EthernetUDP::EthernetUDP() {} + +/* Start EthernetUDP socket, listening at local port PORT */ +uint8_t EthernetUDP::begin(uint16_t port) { + // Can create a single udp connection per socket + if(_udp.pcb != NULL) { + return 0; + } + + _udp.pcb = udp_new(); + + if(_udp.pcb == NULL) { + return 0; + } + + IPAddress ip = Ethernet.localIP(); + ip_addr_t ipaddr; + + if(ERR_OK != udp_bind(_udp.pcb, u8_to_ip_addr(rawIPAddress(ip), &ipaddr), port)) { + stop(); + return 0; + } + + udp_recv(_udp.pcb, &udp_receive_callback, &_udp); + + _port = port; + _remaining = 0; + + stm32_eth_scheduler(); + + return 1; +} + +/* return number of bytes available in the current packet, + will return zero if parsePacket hasn't been called yet */ +int EthernetUDP::available() { + return _remaining; +} + +/* Release any resources being used by this EthernetUDP instance */ +void EthernetUDP::stop() +{ + if(_udp.pcb != NULL) { + udp_disconnect(_udp.pcb); + udp_remove(_udp.pcb); + _udp.pcb = NULL; + } + + stm32_eth_scheduler(); +} + +int EthernetUDP::beginPacket(const char *host, uint16_t port) +{ + // Look up the host first + int ret = 0; + DNSClient dns; + IPAddress remote_addr; + + dns.begin(Ethernet.dnsServerIP()); + ret = dns.getHostByName(host, remote_addr); + if (ret == 1) { + return beginPacket(remote_addr, port); + } else { + return ret; + } +} + +int EthernetUDP::beginPacket(IPAddress ip, uint16_t port) +{ + if(_udp.pcb == NULL) { + return 0; + } + + ip_addr_t ipaddr; + + if(ERR_OK != udp_connect( _udp.pcb, u8_to_ip_addr(rawIPAddress(ip), &ipaddr), port)) { + return 0; + } + + udp_recv(_udp.pcb, &udp_receive_callback, &_udp); + stm32_eth_scheduler(); + + return 1; +} + +int EthernetUDP::endPacket() +{ + if((_udp.pcb == NULL) || (_data == NULL)) { + return 0; + } + + // A remote IP & port must be connected to udp pcb. Call ::beginPacket before. + if((udp_flags(_udp.pcb) & UDP_FLAGS_CONNECTED) != UDP_FLAGS_CONNECTED) { + return 0; + } + + if(ERR_OK != udp_send(_udp.pcb, _data)) { + _data = stm32_free_data(_data); + return 0; + } + + _data = stm32_free_data(_data); + stm32_eth_scheduler(); + + return 1; +} + +size_t EthernetUDP::write(uint8_t byte) +{ + return write(&byte, 1); +} + +size_t EthernetUDP::write(const uint8_t *buffer, size_t size) +{ + _data = stm32_new_data(_data, buffer, size); + if(_data == NULL) { + return 0; + } + + return size; +} + +int EthernetUDP::parsePacket() +{ + // discard any remaining bytes in the last packet + // while (_remaining) { + // // could this fail (loop endlessly) if _remaining > 0 and recv in read fails? + // // should only occur if recv fails after telling us the data is there, lets + // // hope the w5100 always behaves :) + // read(); + // } + + stm32_eth_scheduler(); + + if (_udp.data.available > 0) + { + _remoteIP = IPAddress(ip_addr_to_u32(&(_udp.ip))); + _remotePort = _udp.port; + _remaining = _udp.data.available; + + return _remaining; + } + // There aren't any packets available + return 0; +} + +int EthernetUDP::read() +{ + uint8_t byte; + + if(_udp.data.p == NULL) { + return -1; + } + + if (_remaining > 0) + { + if(read(&byte, 1) > 0) + return byte; + } + + // If we get here, there's no data available + return -1; +} + +int EthernetUDP::read(unsigned char* buffer, size_t len) +{ + if(_udp.data.p == NULL) { + return -1; + } + + if (_remaining > 0) + { + int got; + + if (_remaining <= len) + { + // data should fit in the buffer + got = (int)stm32_get_data(&(_udp.data), (uint8_t *)buffer, _remaining); + } + else + { + // too much data for the buffer, + // grab as much as will fit + got = (int)stm32_get_data(&(_udp.data), (uint8_t *)buffer, len); + } + + if (got > 0) + { + _remaining -= got; + return got; + } + + } + + // If we get here, there's no data available or recv failed + return -1; + +} + +int EthernetUDP::peek() +{ + uint8_t b; + // Unlike recv, peek doesn't check to see if there's any data available, so we must. + // If the user hasn't called parsePacket yet then return nothing otherwise they + // may get the UDP header + if (!_remaining) + return -1; + b = pbuf_get_at(_udp.data.p, 0); + return b; +} + +void EthernetUDP::flush() +{ + // TODO: we should wait for TX buffer to be emptied +} + +/* Start EthernetUDP socket, listening at local port PORT */ +uint8_t EthernetUDP::beginMulticast(IPAddress ip, uint16_t port) +{ + UNUSED(ip); + return begin(port); +} diff --git a/src/EthernetUdp.h b/src/EthernetUdp.h new file mode 100644 index 0000000..8d7d878 --- /dev/null +++ b/src/EthernetUdp.h @@ -0,0 +1,107 @@ +/* + * Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield. + * This version only offers minimal wrapping of socket.c/socket.h + * Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ + * + * NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these) + * 1) UDP does not guarantee the order in which assembled UDP packets are received. This + * might not happen often in practice, but in larger network topologies, a UDP + * packet can be received out of sequence. + * 2) UDP does not guard against lost packets - so packets *can* disappear without the sender being + * aware of it. Again, this may not be a concern in practice on small local networks. + * For more information, see http://www.cafeaulait.org/course/week12/35.html + * + * MIT License: + * Copyright (c) 2008 Bjoern Hartmann + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * bjoern@cs.stanford.edu 12/30/2008 + */ + +#ifndef ethernetudp_h +#define ethernetudp_h + +#include + +extern "C" { +#include "utility/stm32_eth.h" +} + +#define UDP_TX_PACKET_MAX_SIZE 24 + +class EthernetUDP : public UDP { +private: + uint16_t _port; // local port to listen on + IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed + uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed + + struct pbuf *_data; //pbuf for data to send + struct udp_struct _udp; //udp settings + +protected: + uint16_t _remaining; // remaining bytes of incoming packet yet to be processed + +public: + EthernetUDP(); // Constructor + virtual uint8_t begin(uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + virtual uint8_t beginMulticast(IPAddress, uint16_t); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use + virtual void stop(); // Finish with the UDP socket + + // Sending UDP packets + + // Start building up a packet to send to the remote host specific in ip and port + // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port + virtual int beginPacket(IPAddress ip, uint16_t port); + // Start building up a packet to send to the remote host specific in host and port + // Returns 1 if successful, 0 if there was a problem resolving the hostname or port + virtual int beginPacket(const char *host, uint16_t port); + // Finish off this packet and send it + // Returns 1 if the packet was sent successfully, 0 if there was an error + virtual int endPacket(); + // Write a single byte into the packet + virtual size_t write(uint8_t); + // Write size bytes from buffer into the packet + virtual size_t write(const uint8_t *buffer, size_t size); + + using Print::write; + + // Start processing the next available incoming packet + // Returns the size of the packet in bytes, or 0 if no packets are available + virtual int parsePacket(); + // Number of bytes remaining in the current packet + virtual int available(); + // Read a single byte from the current packet + virtual int read(); + // Read up to len bytes from the current packet and place them into buffer + // Returns the number of bytes read, or 0 if none are available + virtual int read(unsigned char* buffer, size_t len); + // Read up to len characters from the current packet and place them into buffer + // Returns the number of characters read, or 0 if none are available + virtual int read(char* buffer, size_t len) { return read((unsigned char*)buffer, len); }; + // Return the next byte from the current packet without moving on to the next byte + virtual int peek(); + virtual void flush(); // Finish reading the current packet + + // Return the IP address of the host who sent the current incoming packet + virtual IPAddress remoteIP() { return _remoteIP; }; + // Return the port of the host who sent the current incoming packet + virtual uint16_t remotePort() { return _remotePort; }; +}; + +#endif diff --git a/src/STM32Ethernet.cpp b/src/STM32Ethernet.cpp new file mode 100644 index 0000000..f42c843 --- /dev/null +++ b/src/STM32Ethernet.cpp @@ -0,0 +1,96 @@ +#include "STM32Ethernet.h" +#include "Dhcp.h" + +int EthernetClass::begin(uint8_t *mac_address, unsigned long timeout, unsigned long responseTimeout) +{ + static DhcpClass s_dhcp; + _dhcp = &s_dhcp; + + stm32_eth_init(mac_address, NULL, NULL, NULL); + + // Now try to get our config info from a DHCP server + int ret = _dhcp->beginWithDHCP(mac_address, timeout, responseTimeout); + if(ret == 1) + { + _dnsServerAddress = _dhcp->getDnsServerIp(); + } + + return ret; +} + +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip) +{ + // Assume the DNS server will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress dns_server = local_ip; + dns_server[3] = 1; + begin(mac_address, local_ip, dns_server); +} + +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server) +{ + // Assume the gateway will be the machine on the same network as the local IP + // but with last octet being '1' + IPAddress gateway = local_ip; + gateway[3] = 1; + begin(mac_address, local_ip, dns_server, gateway); +} + +void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway) +{ + IPAddress subnet(255, 255, 255, 0); + begin(mac_address, local_ip, dns_server, gateway, subnet); +} + +void EthernetClass::begin(uint8_t *mac, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) +{ + stm32_eth_init(mac, local_ip.raw_address(), gateway.raw_address(), subnet.raw_address()); + /* If there is a local DHCP informs it of our manual IP configuration to + prevent IP conflict */ + stm32_DHCP_manual_config(); + _dnsServerAddress = dns_server; +} + +int EthernetClass::maintain(){ + int rc = DHCP_CHECK_NONE; + + if(_dhcp != NULL){ + //we have a pointer to dhcp, use it + rc = _dhcp->checkLease(); + switch ( rc ){ + case DHCP_CHECK_NONE: + //nothing done + break; + case DHCP_CHECK_RENEW_OK: + case DHCP_CHECK_REBIND_OK: + _dnsServerAddress = _dhcp->getDnsServerIp(); + break; + default: + //this is actually a error, it will retry though + break; + } + } + return rc; +} + +IPAddress EthernetClass::localIP() +{ + return IPAddress(stm32_eth_get_ipaddr()); +} + +IPAddress EthernetClass::subnetMask() +{ + return IPAddress(stm32_eth_get_netmaskaddr()); +} + +IPAddress EthernetClass::gatewayIP() +{ + return IPAddress(stm32_eth_get_gwaddr()); +} + +IPAddress EthernetClass::dnsServerIP() +{ + return _dnsServerAddress; +} + +EthernetClass Ethernet; diff --git a/src/STM32Ethernet.h b/src/STM32Ethernet.h new file mode 100644 index 0000000..68739f9 --- /dev/null +++ b/src/STM32Ethernet.h @@ -0,0 +1,36 @@ +#ifndef ethernet_h +#define ethernet_h + +#include +#include "IPAddress.h" +#include "EthernetClient.h" +#include "EthernetServer.h" +#include "Dhcp.h" + +class EthernetClass { +private: + IPAddress _dnsServerAddress; + DhcpClass* _dhcp; +public: + // Initialise the Ethernet shield to use the provided MAC address and gain the rest of the + // configuration through DHCP. + // Returns 0 if the DHCP configuration failed, and 1 if it succeeded + int begin(uint8_t *mac_address, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); + void begin(uint8_t *mac_address, IPAddress local_ip); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway); + void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet); + int maintain(); + + IPAddress localIP(); + IPAddress subnetMask(); + IPAddress gatewayIP(); + IPAddress dnsServerIP(); + + friend class EthernetClient; + friend class EthernetServer; +}; + +extern EthernetClass Ethernet; + +#endif diff --git a/src/utility/ethernetif.c b/src/utility/ethernetif.c new file mode 100644 index 0000000..eeb966c --- /dev/null +++ b/src/utility/ethernetif.c @@ -0,0 +1,643 @@ +/** + ****************************************************************************** + * @file ethernetif.c + * @author MCD Application Team & Wi6Labs + * @version V1.5.0 + * @date 20-june-2017 + * @brief This file implements Ethernet network interface drivers for lwIP + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2017 STMicroelectronics International N.V. + * All rights reserved.

+ * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions are met: + * + * 1. Redistribution of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific written permission. + * 4. This software, including modifications and/or derivative works of this + * software, must execute solely and exclusively on microcontroller or + * microprocessor devices manufactured by or for STMicroelectronics. + * 5. Redistribution and use of this software other than as permitted under + * this license is void and will automatically terminate your rights under + * this license. + * + * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY + * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT + * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ +/* Includes ------------------------------------------------------------------*/ +#include "stm32_def.h" +#include "lwip/timeouts.h" +#include "netif/etharp.h" +#include "ethernetif.h" +#include +#include "PeripheralPins.h" +#include "stm32_eth.h" +#include "variant.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +/* Network interface name */ +#define IFNAME0 's' +#define IFNAME1 't' + +/* Private macro -------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +#if defined ( __ICCARM__ ) /*!< IAR Compiler */ + #pragma data_alignment=4 +#endif +__ALIGN_BEGIN ETH_DMADescTypeDef DMARxDscrTab[ETH_RXBUFNB] __ALIGN_END;/* Ethernet Rx MA Descriptor */ + +#if defined ( __ICCARM__ ) /*!< IAR Compiler */ + #pragma data_alignment=4 +#endif +__ALIGN_BEGIN ETH_DMADescTypeDef DMATxDscrTab[ETH_TXBUFNB] __ALIGN_END;/* Ethernet Tx DMA Descriptor */ + +#if defined ( __ICCARM__ ) /*!< IAR Compiler */ + #pragma data_alignment=4 +#endif +__ALIGN_BEGIN uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE] __ALIGN_END; /* Ethernet Receive Buffer */ + +#if defined ( __ICCARM__ ) /*!< IAR Compiler */ + #pragma data_alignment=4 +#endif +__ALIGN_BEGIN uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE] __ALIGN_END; /* Ethernet Transmit Buffer */ + +static ETH_HandleTypeDef EthHandle; + +static uint8_t macaddress[6]= { MAC_ADDR0, MAC_ADDR1, MAC_ADDR2, MAC_ADDR3, MAC_ADDR4, MAC_ADDR5 }; + +/* Private function prototypes -----------------------------------------------*/ +/* Private functions ---------------------------------------------------------*/ +/******************************************************************************* + Ethernet MSP Routines +*******************************************************************************/ +/** + * @brief Initializes the ETH MSP. + * @param heth: ETH handle + * @retval None + */ +void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) +{ + GPIO_InitTypeDef GPIO_InitStructure; + const PinMap* map = PinMap_Ethernet; + PinName pin = pin_pinName(map); + GPIO_TypeDef* port; + + UNUSED(heth); + +/* Ethernet pins configuration ************************************************/ + + if(map != NULL) { + while(pin != NC) { + /* Set port clock */ + port = set_GPIO_Port_Clock(STM_PORT(pin)); + + /* pin configuration */ + GPIO_InitStructure.Pin = STM_GPIO_PIN(pin); + GPIO_InitStructure.Mode = STM_PIN_MODE(pinmap_function(pin, PinMap_Ethernet)); + GPIO_InitStructure.Pull = STM_PIN_PUPD(pinmap_function(pin, PinMap_Ethernet)); + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; + GPIO_InitStructure.Alternate = STM_PIN_AFNUM(pinmap_function(pin, PinMap_Ethernet)); + HAL_GPIO_Init(port, &GPIO_InitStructure); + + pin = pin_pinName(++map); + } + } + +#ifdef ETH_INPUT_USE_IT + /* Enable the Ethernet global Interrupt */ + HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); + HAL_NVIC_EnableIRQ(ETH_IRQn); +#endif /* ETH_INPUT_USE_IT */ + + /* Enable ETHERNET clock */ + __HAL_RCC_ETH_CLK_ENABLE(); +} + +/******************************************************************************* + LL Driver Interface ( LwIP stack --> ETH) +*******************************************************************************/ +/** + * @brief In this function, the hardware should be initialized. + * Called from ethernetif_init(). + * + * @param netif the already initialized lwip network interface structure + * for this ethernetif + */ +static void low_level_init(struct netif *netif) +{ + uint32_t regvalue; + + EthHandle.Instance = ETH; + EthHandle.Init.MACAddr = macaddress; + EthHandle.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE; + EthHandle.Init.Speed = ETH_SPEED_100M; + EthHandle.Init.DuplexMode = ETH_MODE_FULLDUPLEX; +#ifdef ETHERNET_RMII_MODE_CONFIGURATION + EthHandle.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII; +#else + EthHandle.Init.MediaInterface = ETH_MEDIA_INTERFACE_MII; +#endif /* ETHERNET_RMII_MODE_CONFIGURATION */ +#ifdef ETH_INPUT_USE_IT + EthHandle.Init.RxMode = ETH_RXINTERRUPT_MODE; +#else + EthHandle.Init.RxMode = ETH_RXPOLLING_MODE; +#endif /* ETH_INPUT_USE_IT */ + EthHandle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE; + EthHandle.Init.PhyAddress = LAN8742A_PHY_ADDRESS; + + /* configure ethernet peripheral (GPIOs, clocks, MAC, DMA) */ + if (HAL_ETH_Init(&EthHandle) == HAL_OK) + { + /* Set netif link flag */ + netif->flags |= NETIF_FLAG_LINK_UP; + } + + /* Initialize Tx Descriptors list: Chain Mode */ + HAL_ETH_DMATxDescListInit(&EthHandle, DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); + + /* Initialize Rx Descriptors list: Chain Mode */ + HAL_ETH_DMARxDescListInit(&EthHandle, DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); + + /* set MAC hardware address length */ + netif->hwaddr_len = ETH_HWADDR_LEN; + + /* set MAC hardware address */ + netif->hwaddr[0] = macaddress[0]; + netif->hwaddr[1] = macaddress[1]; + netif->hwaddr[2] = macaddress[2]; + netif->hwaddr[3] = macaddress[3]; + netif->hwaddr[4] = macaddress[4]; + netif->hwaddr[5] = macaddress[5]; + + /* maximum transfer unit */ + netif->mtu = 1500; + + /* device capabilities */ + /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ + netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; + + /* Enable MAC and DMA transmission and reception */ + HAL_ETH_Start(&EthHandle); + + /**** Configure PHY to generate an interrupt when Eth Link state changes ****/ + /* Read Register Configuration */ + HAL_ETH_ReadPHYRegister(&EthHandle, PHY_IMR, ®value); + + regvalue |= PHY_ISFR_INT4; + + /* Enable Interrupt on change of link status */ + HAL_ETH_WritePHYRegister(&EthHandle, PHY_IMR, regvalue ); +} + +/** + * @brief This function should do the actual transmission of the packet. The packet is + * contained in the pbuf that is passed to the function. This pbuf + * might be chained. + * + * @param netif the lwip network interface structure for this ethernetif + * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) + * @return ERR_OK if the packet could be sent + * an err_t value if the packet couldn't be sent + * + * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to + * strange results. You might consider waiting for space in the DMA queue + * to become availale since the stack doesn't retry to send a packet + * dropped because of memory failure (except for the TCP timers). + */ +static err_t low_level_output(struct netif *netif, struct pbuf *p) +{ + err_t errval; + struct pbuf *q; + uint8_t *buffer = (uint8_t *)(EthHandle.TxDesc->Buffer1Addr); + __IO ETH_DMADescTypeDef *DmaTxDesc; + uint32_t framelength = 0; + uint32_t bufferoffset = 0; + uint32_t byteslefttocopy = 0; + uint32_t payloadoffset = 0; + + UNUSED(netif); + + DmaTxDesc = EthHandle.TxDesc; + bufferoffset = 0; + + /* copy frame from pbufs to driver buffers */ + for(q = p; q != NULL; q = q->next) + { + /* Is this buffer available? If not, goto error */ + if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET) + { + errval = ERR_USE; + goto error; + } + + /* Get bytes in current lwIP buffer */ + byteslefttocopy = q->len; + payloadoffset = 0; + + /* Check if the length of data to copy is bigger than Tx buffer size*/ + while( (byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE ) + { + /* Copy data to Tx buffer*/ + memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset) ); + + /* Point to next descriptor */ + DmaTxDesc = (ETH_DMADescTypeDef *)(DmaTxDesc->Buffer2NextDescAddr); + + /* Check if the buffer is available */ + if((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET) + { + errval = ERR_USE; + goto error; + } + + buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr); + + byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset); + payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset); + framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset); + bufferoffset = 0; + } + + /* Copy the remaining bytes */ + memcpy( (uint8_t*)((uint8_t*)buffer + bufferoffset), (uint8_t*)((uint8_t*)q->payload + payloadoffset), byteslefttocopy ); + bufferoffset = bufferoffset + byteslefttocopy; + framelength = framelength + byteslefttocopy; + } + + /* Prepare transmit descriptors to give to DMA */ + HAL_ETH_TransmitFrame(&EthHandle, framelength); + + errval = ERR_OK; + +error: + + /* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */ + if ((EthHandle.Instance->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET) + { + /* Clear TUS ETHERNET DMA flag */ + EthHandle.Instance->DMASR = ETH_DMASR_TUS; + + /* Resume DMA transmission*/ + EthHandle.Instance->DMATPDR = 0; + } + return errval; +} + +/** + * @brief Should allocate a pbuf and transfer the bytes of the incoming + * packet from the interface into the pbuf. + * + * @param netif the lwip network interface structure for this ethernetif + * @return a pbuf filled with the received packet (including MAC header) + * NULL on memory error + */ +static struct pbuf * low_level_input(struct netif *netif) +{ + struct pbuf *p = NULL; + struct pbuf *q; + uint16_t len; + uint8_t *buffer; + __IO ETH_DMADescTypeDef *dmarxdesc; + uint32_t bufferoffset = 0; + uint32_t payloadoffset = 0; + uint32_t byteslefttocopy = 0; + uint32_t i=0; + + UNUSED(netif); + + if (HAL_ETH_GetReceivedFrame_IT(&EthHandle) != HAL_OK) + return NULL; + + /* Obtain the size of the packet and put it into the "len" variable. */ + len = EthHandle.RxFrameInfos.length; + buffer = (uint8_t *)EthHandle.RxFrameInfos.buffer; + + if (len > 0) + { + /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */ + p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); + } + + if (p != NULL) + { + dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc; + bufferoffset = 0; + + for(q = p; q != NULL; q = q->next) + { + byteslefttocopy = q->len; + payloadoffset = 0; + + /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size */ + while( (byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE ) + { + /* Copy data to pbuf */ + memcpy( (uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset)); + + /* Point to next descriptor */ + dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr); + buffer = (uint8_t *)(dmarxdesc->Buffer1Addr); + + byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset); + payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset); + bufferoffset = 0; + } + + /* Copy remaining data in pbuf */ + memcpy( (uint8_t*)((uint8_t*)q->payload + payloadoffset), (uint8_t*)((uint8_t*)buffer + bufferoffset), byteslefttocopy); + bufferoffset = bufferoffset + byteslefttocopy; + } + } + + /* Release descriptors to DMA */ + /* Point to first descriptor */ + dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc; + /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ + for (i=0; i< EthHandle.RxFrameInfos.SegCount; i++) + { + dmarxdesc->Status |= ETH_DMARXDESC_OWN; + dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr); + } + + /* Clear Segment_Count */ + EthHandle.RxFrameInfos.SegCount =0; + + /* When Rx Buffer unavailable flag is set: clear it and resume reception */ + if ((EthHandle.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET) + { + /* Clear RBUS ETHERNET DMA flag */ + EthHandle.Instance->DMASR = ETH_DMASR_RBUS; + /* Resume DMA reception */ + EthHandle.Instance->DMARPDR = 0; + } + return p; +} + +/** + * @brief This function should be called when a packet is ready to be read + * from the interface. It uses the function low_level_input() that + * should handle the actual reception of bytes from the network + * interface. Then the type of the received packet is determined and + * the appropriate input function is called. + * + * @param netif the lwip network interface structure for this ethernetif + */ +void ethernetif_input(struct netif *netif) +{ + err_t err; + struct pbuf *p; + + /* move received packet into a new pbuf */ + p = low_level_input(netif); + + /* no packet could be read, silently ignore this */ + if (p == NULL) return; + + /* entry point to the LwIP stack */ + err = netif->input(p, netif); + + if (err != ERR_OK) + { + LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); + pbuf_free(p); + p = NULL; + } +} + +/** + * @brief Should be called at the beginning of the program to set up the + * network interface. It calls the function low_level_init() to do the + * actual setup of the hardware. + * + * This function should be passed as a parameter to netif_add(). + * + * @param netif the lwip network interface structure for this ethernetif + * @return ERR_OK if the loopif is initialized + * ERR_MEM if private data couldn't be allocated + * any other err_t on error + */ +err_t ethernetif_init(struct netif *netif) +{ + LWIP_ASSERT("netif != NULL", (netif != NULL)); + +#if LWIP_NETIF_HOSTNAME + /* Initialize interface hostname */ + netif->hostname = "lwip"; +#endif /* LWIP_NETIF_HOSTNAME */ + + netif->name[0] = IFNAME0; + netif->name[1] = IFNAME1; + /* We directly use etharp_output() here to save a function call. + * You can instead declare your own function an call etharp_output() + * from it if you have to do some checks before sending (e.g. if link + * is available...) */ + netif->output = etharp_output; + netif->linkoutput = low_level_output; + + /* initialize the hardware */ + low_level_init(netif); + + return ERR_OK; +} + +/** + * @brief Returns the current time in milliseconds + * when LWIP_TIMERS == 1 and NO_SYS == 1 + * @param None + * @retval Current Time value + */ +u32_t sys_now(void) +{ + return HAL_GetTick(); +} + +/** + * @brief This function sets the netif link status. + * @param netif: the network interface + * @retval None + */ +void ethernetif_set_link(struct netif *netif) +{ + uint32_t regvalue = 0; + + /* Read PHY_MISR*/ + HAL_ETH_ReadPHYRegister(&EthHandle, PHY_ISFR, ®value); + + /* Check whether the link interrupt has occurred or not */ + if((regvalue & PHY_ISFR_INT4) != (uint16_t)RESET) + { + netif_set_link_down(netif); + } + + HAL_ETH_ReadPHYRegister(&EthHandle, PHY_BSR, ®value); + + if((regvalue & PHY_LINKED_STATUS) != (uint16_t)RESET) { + netif_set_link_up(netif); + } +} + +/** + * @brief Link callback function, this function is called on change of link status + * to update low level driver configuration. + * @param netif: The network interface + * @retval None + */ +void ethernetif_update_config(struct netif *netif) +{ + __IO uint32_t tickstart = 0; + uint32_t regvalue = 0; + + if(netif_is_link_up(netif)) + { + /* Restart the auto-negotiation */ + if(EthHandle.Init.AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE) + { + /* Enable Auto-Negotiation */ + HAL_ETH_WritePHYRegister(&EthHandle, PHY_BCR, PHY_AUTONEGOTIATION); + + /* Get tick */ + tickstart = HAL_GetTick(); + + /* Wait until the auto-negotiation will be completed */ + do + { + HAL_ETH_ReadPHYRegister(&EthHandle, PHY_BSR, ®value); + + /* Check for the Timeout ( 1s ) */ + if((HAL_GetTick() - tickstart ) > 1000) + { + /* In case of timeout */ + goto error; + } + + } while (((regvalue & PHY_AUTONEGO_COMPLETE) != PHY_AUTONEGO_COMPLETE)); + + /* Read the result of the auto-negotiation */ + HAL_ETH_ReadPHYRegister(&EthHandle, PHY_SR, ®value); + + /* Configure the MAC with the Duplex Mode fixed by the auto-negotiation process */ + if((regvalue & PHY_DUPLEX_STATUS) != (uint32_t)RESET) + { + /* Set Ethernet duplex mode to Full-duplex following the auto-negotiation */ + EthHandle.Init.DuplexMode = ETH_MODE_FULLDUPLEX; + } + else + { + /* Set Ethernet duplex mode to Half-duplex following the auto-negotiation */ + EthHandle.Init.DuplexMode = ETH_MODE_HALFDUPLEX; + } + /* Configure the MAC with the speed fixed by the auto-negotiation process */ + if(regvalue & PHY_SPEED_STATUS) + { + /* Set Ethernet speed to 10M following the auto-negotiation */ + EthHandle.Init.Speed = ETH_SPEED_10M; + } + else + { + /* Set Ethernet speed to 100M following the auto-negotiation */ + EthHandle.Init.Speed = ETH_SPEED_100M; + } + } + else /* AutoNegotiation Disable */ + { + error : + /* Check parameters */ + assert_param(IS_ETH_SPEED(EthHandle.Init.Speed)); + assert_param(IS_ETH_DUPLEX_MODE(EthHandle.Init.DuplexMode)); + + /* Set MAC Speed and Duplex Mode to PHY */ + HAL_ETH_WritePHYRegister(&EthHandle, PHY_BCR, ((uint16_t)(EthHandle.Init.DuplexMode >> 3) | + (uint16_t)(EthHandle.Init.Speed >> 1))); + } + + /* ETHERNET MAC Re-Configuration */ + HAL_ETH_ConfigMAC(&EthHandle, (ETH_MACInitTypeDef *) NULL); + + /* Restart MAC interface */ + HAL_ETH_Start(&EthHandle); + } + else + { + /* Stop MAC interface */ + HAL_ETH_Stop(&EthHandle); + } + + ethernetif_notify_conn_changed(netif); +} + +/** + * @brief This function notify user about link status changement. + * @param netif: the network interface + * @retval None + */ +__weak void ethernetif_notify_conn_changed(struct netif *netif) +{ + /* NOTE : This is function clould be implemented in user file + when the callback is needed, + */ + UNUSED(netif); +} + +/** + * @brief This function set a custom MAC address. This function must be called + * before ethernetif_init(). + * @param mac: mac address + * @retval None + */ +void ethernetif_set_mac_addr(const uint8_t *mac) { + if(mac != NULL) { + memcpy(macaddress,mac,6); + } +} + +#ifdef ETH_INPUT_USE_IT +/** + * @brief Ethernet Rx Transfer completed callback + * @param heth: ETH handle + * @retval None + */ +void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth) +{ + ethernetif_input(&gnetif); +} + +/** + * @brief This function handles Ethernet interrupt request. + * @param None + * @retval None + */ +void ETH_IRQHandler(void) +{ + HAL_ETH_IRQHandler(&EthHandle); +} +#endif /* ETH_INPUT_USE_IT */ + +#ifdef __cplusplus +} +#endif +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/src/utility/ethernetif.h b/src/utility/ethernetif.h new file mode 100644 index 0000000..e851c19 --- /dev/null +++ b/src/utility/ethernetif.h @@ -0,0 +1,71 @@ +/** + ****************************************************************************** + * @file ethernetif.h + * @author MCD Application Team & Wi6Labs + * @version V1.5.0 + * @date 20-June-2017 + * @brief Ethernet interface header file. + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2017 STMicroelectronics International N.V. + * All rights reserved.

+ * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted, provided that the following conditions are met: + * + * 1. Redistribution of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of other + * contributors to this software may be used to endorse or promote products + * derived from this software without specific written permission. + * 4. This software, including modifications and/or derivative works of this + * software, must execute solely and exclusively on microcontroller or + * microprocessor devices manufactured by or for STMicroelectronics. + * 5. Redistribution and use of this software other than as permitted under + * this license is void and will automatically terminate your rights under + * this license. + * + * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY + * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT + * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, + * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +#ifndef __ETHERNETIF_H__ +#define __ETHERNETIF_H__ + +#include "lwip/err.h" +#include "lwip/netif.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/* Exported types ------------------------------------------------------------*/ +err_t ethernetif_init(struct netif *netif); +void ethernetif_input(struct netif *netif); +void ethernetif_set_link(struct netif *netif); +void ethernetif_update_config(struct netif *netif); +void ethernetif_notify_conn_changed(struct netif *netif); + +void ethernetif_set_mac_addr(const uint8_t *mac); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/utility/stm32_eth.c b/src/utility/stm32_eth.c new file mode 100644 index 0000000..be78fc5 --- /dev/null +++ b/src/utility/stm32_eth.c @@ -0,0 +1,965 @@ +/** + ****************************************************************************** + * @file stm32_eth.c + * @author WI6LABS + * @version V1.0.0 + * @date 24-May-2017 + * @brief This file implements Ethernet network interface drivers for + * Arduino STM32Ethernet library. + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +#include "stm32_eth.h" +#include "lwip/init.h" +#include "lwip/netif.h" +#include "lwip/timeouts.h" +#include "netif/ethernet.h" +#include "ethernetif.h" +#include "lwip/dhcp.h" +#include "lwip/prot/dhcp.h" +#include "lwip/dns.h" + + +#ifdef __cplusplus + extern "C" { +#endif + +/* Check ethernet link status every seconds */ +#define TIME_CHECK_ETH_LINK_STATE 500U + +/* Timeout for DNS request */ +#define TIMEOUT_DNS_REQUEST 10000U + +/* Maximum number of retries for DHCP request */ +#define MAX_DHCP_TRIES 4 + +/* Ethernet configuration: user parameters */ +struct stm32_eth_config { + ip_addr_t ipaddr; + ip_addr_t netmask; + ip_addr_t gw; +}; + +/* Use to give user parameters to netif configuration */ +static struct stm32_eth_config gconfig; + +/* Netif global configuration structure */ +struct netif gnetif; + +/* DHCP periodic timer */ +static uint32_t DHCPfineTimer = 0; + +/* DHCP current state */ +__IO uint8_t DHCP_state = DHCP_OFF; + +/* Set to 1 if user use DHCP to obtain network addresses */ +static uint8_t DHCP_Started_by_user = 0; + +/* Ethernet link status periodic timer */ +static uint32_t gEhtLinkTickStart = 0; + +/*************************** Function prototype *******************************/ +static void Netif_Config(void); +static void tcp_connection_close(struct tcp_pcb *tpcb, struct tcp_struct *tcp); +static err_t tcp_recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); +static err_t tcp_sent_callback(void *arg, struct tcp_pcb *tpcb, u16_t len); +static void tcp_err_callback(void *arg, err_t err); + + +/** +* @brief Configurates the network interface +* @param None +* @retval None +*/ +static void Netif_Config(void) +{ + /* Add the network interface */ + netif_add(&gnetif, &(gconfig.ipaddr), &(gconfig.netmask), &(gconfig.gw), NULL, ðernetif_init, ðernet_input); + + /* Registers the default network interface */ + netif_set_default(&gnetif); + + if (netif_is_link_up(&gnetif)) + { + /* When the netif is fully configured this function must be called */ + netif_set_up(&gnetif); + } + else + { + /* When the netif link is down this function must be called */ + netif_set_down(&gnetif); + } + +#if LWIP_NETIF_LINK_CALLBACK + /* Set the link callback function, this function is called on change of link status */ + netif_set_link_callback(&gnetif, ethernetif_update_config); +#endif /* LWIP_NETIF_LINK_CALLBACK */ +} + +void stm32_eth_init(const uint8_t *mac, const uint8_t *ip, const uint8_t *gw, const uint8_t *netmask) +{ + /* Initialize the LwIP stack */ + lwip_init(); + + if(mac != NULL) { + ethernetif_set_mac_addr(mac); + } // else default value is used: MAC_ADDR0 ... MAC_ADDR5 + + if(ip != NULL) { + IP_ADDR4(&(gconfig.ipaddr),ip[0],ip[1],ip[2],ip[3]); + } else { + #if LWIP_DHCP + ip_addr_set_zero_ip4(&(gconfig.ipaddr)); + #else + IP_ADDR4(&(gconfig.ipaddr),IP_ADDR0,IP_ADDR1,IP_ADDR2,IP_ADDR3); + #endif /* LWIP_DHCP */ + } + + if(gw != NULL) { + IP_ADDR4(&(gconfig.gw),gw[0],gw[1],gw[2],gw[3]); + } else { + #if LWIP_DHCP + ip_addr_set_zero_ip4(&(gconfig.gw)); + #else + IP_ADDR4(&(gconfig.gw),GW_ADDR0,GW_ADDR1,GW_ADDR2,GW_ADDR3); + #endif /* LWIP_DHCP */ + } + + if(netmask != NULL) { + IP_ADDR4(&(gconfig.netmask),netmask[0],netmask[1],netmask[2],netmask[3]); + } else { + #if LWIP_DHCP + ip_addr_set_zero_ip4(&(gconfig.netmask)); + #else + IP_ADDR4(&(gconfig.netmask),NETMASK_ADDR0,NETMASK_ADDR1,NETMASK_ADDR2,NETMASK_ADDR3); + #endif /* LWIP_DHCP */ + } + + /* Configure the Network interface */ + Netif_Config(); + + User_notification(&gnetif); + + stm32_eth_scheduler(); +} + +/** + * @brief This function must be called in main loop in standalone mode. + * @param None + * @retval None + */ +void stm32_eth_scheduler(void) { + /* Read a received packet from the Ethernet buffers and send it + to the lwIP for handling */ +#ifndef ETH_INPUT_USE_IT + ethernetif_input(&gnetif); +#endif /* ETH_INPUT_USE_IT */ + + /* Check ethernet link status */ + if((HAL_GetTick() - gEhtLinkTickStart) >= TIME_CHECK_ETH_LINK_STATE) { + ethernetif_set_link(&gnetif); + gEhtLinkTickStart = HAL_GetTick(); + } + + /* Handle LwIP timeouts */ + sys_check_timeouts(); + +#if LWIP_DHCP + stm32_DHCP_Periodic_Handle(&gnetif); +#endif /* LWIP_DHCP */ +} + +#if LWIP_DHCP + +/** + * @brief Returns DHCP activation state + * @param None + * @retval true if DHCP enabled or false if not used + */ +uint8_t stm32_dhcp_started(void) { + return DHCP_Started_by_user; +} + +/** + * @brief DHCP_Process_Handle + * @param netif pointer to generic data structure used for all lwIP network interfaces + * @retval None + */ +void stm32_DHCP_process(struct netif *netif) { + struct dhcp *dhcp; + + if(netif_is_link_up(netif)){ + switch (DHCP_state) + { + case DHCP_START: + { + ip_addr_set_zero_ip4(&netif->ip_addr); + ip_addr_set_zero_ip4(&netif->netmask); + ip_addr_set_zero_ip4(&netif->gw); + DHCP_state = DHCP_WAIT_ADDRESS; + dhcp_start(netif); + DHCP_Started_by_user = 1; + } + break; + + case DHCP_WAIT_ADDRESS: + { + if (dhcp_supplied_address(netif)) + { + DHCP_state = DHCP_ADDRESS_ASSIGNED; + } + else + { + dhcp = (struct dhcp *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP); + + /* DHCP timeout */ + if (dhcp->tries > MAX_DHCP_TRIES) + { + DHCP_state = DHCP_TIMEOUT; + + // If DHCP address not bind, keep DHCP stopped + DHCP_Started_by_user = 0; + + /* Stop DHCP */ + dhcp_stop(netif); + } + } + } + break; + case DHCP_ASK_RELEASE: + { + /* Force release */ + dhcp_release(netif); + dhcp_stop(netif); + DHCP_state = DHCP_OFF; + } + break; + case DHCP_LINK_DOWN: + { + /* Stop DHCP */ + dhcp_stop(netif); + DHCP_state = DHCP_OFF; + } + break; + default: break; + } + } else { + DHCP_state = DHCP_OFF; + } +} + +/** + * @brief DHCP periodic check + * @param netif pointer to generic data structure used for all lwIP network interfaces + * @retval None + */ +void stm32_DHCP_Periodic_Handle(struct netif *netif) +{ + /* Fine DHCP periodic process every 500ms */ + if (HAL_GetTick() - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS) + { + DHCPfineTimer = HAL_GetTick(); + /* process DHCP state machine */ + stm32_DHCP_process(netif); + } +} + +/** + * @brief Inform the local DHCP of our manual IP configuration + * @param None + * @retval None + */ +void stm32_DHCP_manual_config(void) { + dhcp_inform(&gnetif); +} + +/** + * @brief Return status of the DHCP when renew or rebind + * @param None + * @retval Renew or rebind. Adapted from Arduino Ethernet library. + */ +uint8_t stm32_get_DHCP_lease_state(void) { + uint8_t res = 0; + struct dhcp* dhcp = (struct dhcp *)netif_get_client_data(&gnetif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP); + + if(dhcp->state == DHCP_STATE_RENEWING) { + res = 2; + } else if(dhcp->state == DHCP_STATE_REBINDING) { + res = 4; + } + + return res; +} + +/** + * @brief Set DHCP state + * @param state: DHCP_START, DHCP_ASK_RELEASE or DHCP_STOP. Others should not be used. + * @retval None + */ +void stm32_set_DHCP_state(uint8_t state) { + DHCP_state = state; +} + +/** + * @brief Return DHCP state + * @param None + * @retval One of the following state: + DHCP_OFF + DHCP_START + DHCP_WAIT_ADDRESS + DHCP_ADDRESS_ASSIGNED + DHCP_TIMEOUT + DHCP_LINK_DOWN + DHCP_ASK_RELEASE + */ +uint8_t stm32_get_DHCP_state(void) { + return DHCP_state; +} + +#endif /* LWIP_DHCP */ + +/** + * @brief Converts IP address in readable format for user. + * @param None + * @retval address in uint32_t format + */ +uint32_t stm32_eth_get_ipaddr(void) { + return ip4_addr_get_u32(&(gnetif.ip_addr)); +} + +/** + * @brief Converts gateway address in readable format for user. + * @param None + * @retval address in uint32_t format + */ +uint32_t stm32_eth_get_gwaddr(void) { + return ip4_addr_get_u32(&(gnetif.gw)); +} + +/** + * @brief Converts network mask address in readable format for user. + * @param None + * @retval address in uint32_t format + */ +uint32_t stm32_eth_get_netmaskaddr(void) { + return ip4_addr_get_u32(&(gnetif.netmask)); +} + +/** + * @brief Converts DNS address in readable format for user. + * @param None + * @retval address in uint32_t format + */ +uint32_t stm32_eth_get_dnsaddr(void) { + const ip_addr_t *tmp = dns_getserver(0); + return ip4_addr_get_u32(tmp); +} + +/** + * @brief Converts DHCP address in readable format for user. + * @param None + * @retval address in uint32_t format + */ +uint32_t stm32_eth_get_dhcpaddr(void) { + struct dhcp *dhcp = (struct dhcp *)netif_get_client_data(&gnetif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP); + return ip4_addr_get_u32(&(dhcp->server_ip_addr)); +} + +#if LWIP_NETIF_LINK_CALLBACK + +/** + * @brief This function notify user about link status changement. + * @param netif: the network interface + * @retval None + */ +void ethernetif_notify_conn_changed(struct netif *netif) +{ + if(netif_is_link_up(netif)) + { + printf("Link up\n"); + + /* Update DHCP state machine if DHCP used */ + if(DHCP_Started_by_user == 1) { + DHCP_state = DHCP_START; + } + + /* When the netif is fully configured this function must be called.*/ + netif_set_up(netif); + } + else + { + /* Update DHCP state machine if DHCP used */ + if(DHCP_Started_by_user == 1) { + DHCP_state = DHCP_LINK_DOWN; + } + + /* When the netif link is down this function must be called.*/ + netif_set_down(netif); + + printf("Link down\n"); + } +} + +#endif /* LWIP_NETIF_LINK_CALLBACK */ + +/** + * @brief Notify the User about the nework interface config status + * @param netif: the network interface + * @retval None + */ +void User_notification(struct netif *netif) +{ + if (netif_is_up(netif)) + { + //Link up + } + else + { + /* Update DHCP state machine */ + if(DHCP_Started_by_user == 1) { + DHCP_state = DHCP_LINK_DOWN; + } + } +} + +#if LWIP_DNS + +/** + * @brief Initializes DNS + * @param dnsaddr: DNS address + * @retval None + */ +void stm32_dns_init(const uint8_t *dnsaddr) +{ + ip_addr_t ip; + + /* DNS initialized by DHCP when call dhcp_start() */ + if(!stm32_dhcp_started()) { + dns_init(); + IP_ADDR4(&ip,dnsaddr[0],dnsaddr[1],dnsaddr[2],dnsaddr[3]); + dns_setserver(0, &ip); + } +} + +/** Callback which is invoked when a hostname is found. + * A function of this type must be implemented by the application using the DNS resolver. + * @param name pointer to the name that was looked up. + * @param ipaddr pointer to an ip_addr_t containing the IP address of the hostname, + * or NULL if the name could not be found (or on any other error). + * @param callback_arg a user-specified callback argument passed to dns_gethostbyname +*/ +void dns_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) +{ + UNUSED(name); + + if(ipaddr != NULL) { + *((uint32_t *)callback_arg) = ip4_addr_get_u32(ipaddr); + } else { + *((uint32_t *)callback_arg) = 0; + } +} + +/** + * Resolve a hostname (string) into an IP address. + * + * @param hostname the hostname that is to be queried + * @param addr pointer to a uint8_t where to store the address + * @return an error code compatible with Arduino Ethernet library + */ +int8_t stm32_dns_gethostbyname(const char *hostname, uint32_t *ipaddr) +{ + ip_addr_t iphost; + err_t err; + uint32_t tickstart = 0; + int8_t ret = 0; + + *ipaddr = 0; + err = dns_gethostbyname(hostname, &iphost, &dns_callback, ipaddr); + + switch(err) { + case ERR_OK: + *ipaddr = ip4_addr_get_u32(&iphost); + ret = 1; + break; + + case ERR_INPROGRESS: + tickstart = HAL_GetTick(); + while(*ipaddr == 0) { + stm32_eth_scheduler(); + if((HAL_GetTick() - tickstart) >= TIMEOUT_DNS_REQUEST) { + ret = -1; + break; + } + } + + if(ret == 0) { + if(*ipaddr == 0) { + ret = -2; + } else { + ret = 1; + } + } + break; + + case ERR_ARG: + ret = -4; + break; + + default: + ret = -4; + break; + } + + return ret; +} + +#endif /* LWIP_DNS */ + +/** + * @brief Converts a uint8_t IP address to a ip_addr_t address + * @param ipu8: pointer to an address to convert + * @param ipaddr: pointer where store the address converted + * @retval pointer to an address in ip_addr_t format + */ +ip_addr_t *u8_to_ip_addr(uint8_t *ipu8, ip_addr_t *ipaddr) +{ + IP_ADDR4(ipaddr,ipu8[0],ipu8[1],ipu8[2],ipu8[3]); + return ipaddr; +} + +/** + * @brief Converts a ip_addr_t IP address to a uint32_t address + * @param ipaddr: pointer to an address to convert + * @retval pointer to the address converted + */ +uint32_t ip_addr_to_u32(ip_addr_t *ipaddr) +{ + return ip4_addr_get_u32(ipaddr); +} + +/** + * @brief Allocate a pbuf with data pass in parameter + * @param p: pointer to pbuf + * @param buffer: pointer to data to store + * @param size: number of data to store + * @retval pointer to the pbuf allocated + */ +struct pbuf *stm32_new_data(struct pbuf *p, const uint8_t *buffer, size_t size) +{ + // Allocate memory if pbuf doesn't exit yet. + if(p == NULL) { + p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM); + + if(p != NULL) { + // Copy data inside pbuf + if(ERR_OK == pbuf_take(p, (uint8_t *)buffer, size)) { + return p; + } else { + pbuf_free(p); + } + } + } + // If pbuf allocated, grow the size of pbuf and add new data + // NOTE: pbuf_realloc can't be used to grow the size of pbuf + else { + struct pbuf *q = pbuf_alloc(PBUF_TRANSPORT, size + p->tot_len, PBUF_RAM); + + if(q != NULL) { + if(ERR_OK == pbuf_copy(q, p)) { + if(ERR_OK == pbuf_take_at(q, (uint8_t *)buffer, size, p->tot_len)) { + pbuf_free(p); + p = q; + return p; + } + } + + pbuf_free(q); + } + } + + return 0; +} + +/** + * @brief Free pbuf + * @param p: pointer to pbuf + * @retval return always NULL + */ +struct pbuf *stm32_free_data(struct pbuf *p) +{ + uint16_t n; + + if(p != NULL) { + do { + n = pbuf_free(p); + } while(n == 0); + } + + return NULL; +} + +/** + * @brief This function passes pbuf data to uin8_t buffer. It takes account if + * pbuf is chained. + * @param data pointer to data structure + * @param buffer the buffer where write the data read + * @param size the number of data to read + * @retval number of data read + */ +uint16_t stm32_get_data(struct pbuf_data *data, uint8_t *buffer, size_t size) +{ + uint16_t i; + uint16_t offset; + uint16_t nb; + struct pbuf * ptr; + + if((data->p == NULL) || (buffer == NULL) || (size == 0) || + (data->available == 0) || (data->available > data->p->tot_len)) { + return 0; + } + + nb = 0; + + while((nb < size) && (data->p != NULL) && (data->available > 0)) { + ptr = data->p; + offset = ptr->tot_len - data->available; + + /* Get data from p */ + for(i = 0; (nb < size) && ((offset + i) < ptr->len) && (data->available > 0); i++) { + buffer[nb] = pbuf_get_at(ptr, offset + i); + nb++; + data->available--; + } + + if(nb < size) { + /* continue with next pbuf in chain (if any) */ + data->p = ptr->next; + + if(data->p != NULL) { + /* increment reference count for p */ + pbuf_ref(data->p); + } + + /* chop first pbuf from chain */ + ptr = stm32_free_data(ptr); + } + } + + if(data->available == 0) { + data->p = stm32_free_data(data->p); + } + + return nb; +} + +#if LWIP_UDP + +/** + * @brief This function is called when an UDP datagram has been received on + * the port UDP_PORT. + * @param arg user supplied argument + * @param pcb the udp_pcb which received data + * @param p the packet buffer that was received + * @param addr the remote IP address from which the packet was received + * @param port the remote port from which the packet was received + * @retval None + */ +void udp_receive_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *addr, u16_t port) +{ + struct udp_struct *udp_arg = (struct udp_struct *)arg; + + /* Send data to the application layer */ + if((udp_arg != NULL) && (udp_arg->pcb == pcb)) { + // Free the old p buffer if not read + if(udp_arg->data.p != NULL) { + pbuf_free(udp_arg->data.p); + } + + udp_arg->data.p = p; + udp_arg->data.available = p->len; + + ip_addr_copy(udp_arg->ip, *addr); + udp_arg->port = port; + } else { + pbuf_free(p); + } +} + +#endif /* LWIP_UDP */ + +#if LWIP_TCP + +/** + * @brief Function called when TCP connection established + * @param arg: user supplied argument + * @param tpcb: pointer on the connection contol block + * @param err: when connection correctly established err should be ERR_OK + * @retval err_t: returned error + */ +err_t tcp_connected_callback(void *arg, struct tcp_pcb *tpcb, err_t err) +{ + struct tcp_struct *tcp_arg = (struct tcp_struct *)arg; + + if (err == ERR_OK) + { + if ((tcp_arg != NULL) && (tcp_arg->pcb == tpcb)) + { + tcp_arg->state = TCP_CONNECTED; + + /* initialize LwIP tcp_recv callback function */ + tcp_recv(tpcb, tcp_recv_callback); + + /* initialize LwIP tcp_sent callback function */ + tcp_sent(tpcb, tcp_sent_callback); + + /* initialize LwIP tcp_err callback function */ + tcp_err(tpcb, tcp_err_callback); + + return ERR_OK; + } + else + { + /* close connection */ + tcp_connection_close(tpcb, tcp_arg); + + return ERR_ARG; + } + } + else + { + /* close connection */ + tcp_connection_close(tpcb, tcp_arg); + } + return err; +} + +/** + * @brief This function is the implementation of tcp_accept LwIP callback + * @param arg user supplied argument + * @param newpcb: pointer on tcp_pcb struct for the newly created tcp connection + * @param err: when connection correctly established err should be ERR_OK + * @retval err_t: error status + */ +err_t tcp_accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err) +{ + err_t ret_err; + uint8_t accepted; + struct tcp_struct **tcpClient = (struct tcp_struct **)arg; + + /* set priority for the newly accepted tcp connection newpcb */ + tcp_setprio(newpcb, TCP_PRIO_MIN); + + if((tcpClient != NULL) && (ERR_OK == err)) { + struct tcp_struct *client = (struct tcp_struct *)mem_malloc(sizeof(struct tcp_struct)); + + if (client != NULL) + { + client->state = TCP_ACCEPTED; + client->pcb = newpcb; + client->data.p = NULL; + client->data.available = 0; + + /* Looking for an empty soket */ + for(uint16_t i = 0; i < MAX_CLIENT; i++) { + if(tcpClient[i] == NULL) { + tcpClient[i] = client; + accepted = 1; + break; + } + } + + if(accepted) { + /* pass newly allocated client structure as argument to newpcb */ + tcp_arg(newpcb, client); + + /* initialize lwip tcp_recv callback function for newpcb */ + tcp_recv(newpcb, tcp_recv_callback); + + /* initialize lwip tcp_err callback function for newpcb */ + tcp_err(newpcb, tcp_err_callback); + + /* initialize LwIP tcp_sent callback function */ + tcp_sent(newpcb, tcp_sent_callback); + + ret_err = ERR_OK; + } else { + /* close tcp connection */ + tcp_connection_close(newpcb, client); + mem_free(client); + + /* return memory error */ + ret_err = ERR_MEM; + } + } + else + { + /* close tcp connection */ + tcp_connection_close(newpcb, client); + mem_free(client); + + /* return memory error */ + ret_err = ERR_MEM; + } + } else { + tcp_close(newpcb); + ret_err = ERR_ARG; + } + return ret_err; +} + +/** + * @brief tcp_receiv callback + * @param arg: argument to be passed to receive callback + * @param tpcb: tcp connection control block + * @param err: receive error code + * @retval err_t: retuned error + */ +static err_t tcp_recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) +{ + struct tcp_struct *tcp_arg = (struct tcp_struct *)arg; + err_t ret_err; + + /* if we receive an empty tcp frame from server => close connection */ + if (p == NULL) + { + /* we're done sending, close connection */ + tcp_connection_close(tpcb, tcp_arg); + ret_err = ERR_OK; + } + /* else : a non empty frame was received from echo server but for some reason err != ERR_OK */ + else if(err != ERR_OK) + { + /* free received pbuf*/ + if (p != NULL) + { + pbuf_free(p); + } + ret_err = err; + } + else if((tcp_arg->state == TCP_CONNECTED) || (tcp_arg->state == TCP_ACCEPTED)) + { + /* Acknowledge data reception */ + tcp_recved(tpcb, p->tot_len); + + if(tcp_arg->data.p == NULL) { + tcp_arg->data.p = p; + } + else { + pbuf_chain(tcp_arg->data.p, p); + } + + tcp_arg->data.available += p->len; + ret_err = ERR_OK; + } + /* data received when connection already closed */ + else + { + /* Acknowledge data reception */ + tcp_recved(tpcb, p->tot_len); + + /* free pbuf and do nothing */ + pbuf_free(p); + ret_err = ERR_OK; + } + return ret_err; +} + +/** + * @brief This function implements the tcp_sent LwIP callback (called when ACK + * is received from remote host for sent data) + * @param arg: pointer on argument passed to callback + * @param tcp_pcb: tcp connection control block + * @param len: length of data sent + * @retval err_t: returned error code + */ +static err_t tcp_sent_callback(void *arg, struct tcp_pcb *tpcb, u16_t len) +{ + struct tcp_struct *tcp_arg = (struct tcp_struct *)arg; + + LWIP_UNUSED_ARG(len); + + if((tcp_arg != NULL) && (tcp_arg->pcb == tpcb)) + { + return ERR_OK; + } + + return ERR_ARG; +} + +/** Function prototype for tcp error callback functions. Called when the pcb + * receives a RST or is unexpectedly closed for any other reason. + * + * @note The corresponding pcb is already freed when this callback is called! + * + * @param arg Additional argument to pass to the callback function (@see tcp_arg()) + * @param err Error code to indicate why the pcb has been closed + * ERR_ABRT: aborted through tcp_abort or by a TCP timer + * ERR_RST: the connection was reset by the remote host + */ +static void tcp_err_callback(void *arg, err_t err) +{ + struct tcp_struct *tcp_arg = (struct tcp_struct *)arg; + + if(tcp_arg != NULL) { + if(ERR_OK != err) { + tcp_arg->pcb = NULL; + tcp_arg->state = TCP_CLOSING; + } + } +} + +/** + * @brief This function is used to close the tcp connection with server + * @param tpcb: tcp connection control block + * @param es: pointer on echoclient structure + * @retval None + */ +static void tcp_connection_close(struct tcp_pcb *tpcb, struct tcp_struct *tcp) +{ + /* remove callbacks */ + tcp_recv(tpcb, NULL); + tcp_sent(tpcb, NULL); + tcp_poll(tpcb, NULL,0); + tcp_err(tpcb, NULL); + tcp_accept(tpcb, NULL); + + /* close tcp connection */ + tcp_close(tpcb); + + tcp->pcb = NULL; + tcp->state = TCP_CLOSING; +} + +#endif /* LWIP_TCP */ + +#ifdef __cplusplus +} +#endif diff --git a/src/utility/stm32_eth.h b/src/utility/stm32_eth.h new file mode 100644 index 0000000..2dd7257 --- /dev/null +++ b/src/utility/stm32_eth.h @@ -0,0 +1,178 @@ +/** + ****************************************************************************** + * @file stm32_eth.h + * @author WI6LABS + * @version V1.0.0 + * @date 24-May-2017 + * @brief Include stm32_eth source files + ****************************************************************************** + * @attention + * + *

© COPYRIGHT(c) 2016 STMicroelectronics

+ * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + */ + +#ifndef __STM32_ETH_H__ +#define __STM32_ETH_H__ + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32_def.h" +#include "lwip/ip_addr.h" +#include "lwip/dhcp.h" +#include "lwip/udp.h" +#include "lwip/tcp.h" +#include "lwip/opt.h" + +/* Exported types ------------------------------------------------------------*/ +/* TCP connection state */ +typedef enum +{ + TCP_NONE = 0, + TCP_CONNECTED, + TCP_RECEIVED, + TCP_SENT, + TCP_ACCEPTED, + TCP_CLOSING, +}tcp_client_states; + +/* Struct to store received data */ +struct pbuf_data { + struct pbuf *p; // the packet buffer that was received + uint16_t available; // number of data +}; + +/* UDP structure */ +struct udp_struct { + struct udp_pcb *pcb; /* pointer on the current udp_pcb */ + struct pbuf_data data; + ip_addr_t ip; // the remote IP address from which the packet was received + u16_t port; // the remote port from which the packet was received +}; + +/* TCP structure */ +struct tcp_struct { + struct tcp_pcb *pcb; /* pointer on the current tcp_pcb */ + struct pbuf_data data; + tcp_client_states state; /* current connection state */ +}; + +/* Exported constants --------------------------------------------------------*/ +/*Static IP ADDRESS: IP_ADDR0.IP_ADDR1.IP_ADDR2.IP_ADDR3 */ +#define IP_ADDR0 (uint8_t) 192 +#define IP_ADDR1 (uint8_t) 168 +#define IP_ADDR2 (uint8_t) 0 +#define IP_ADDR3 (uint8_t) 10 + +/*NETMASK*/ +#define NETMASK_ADDR0 (uint8_t) 255 +#define NETMASK_ADDR1 (uint8_t) 255 +#define NETMASK_ADDR2 (uint8_t) 255 +#define NETMASK_ADDR3 (uint8_t) 0 + +/*Gateway Address*/ +#define GW_ADDR0 (uint8_t) 192 +#define GW_ADDR1 (uint8_t) 168 +#define GW_ADDR2 (uint8_t) 0 +#define GW_ADDR3 (uint8_t) 1 + +/* DHCP process states */ +#define DHCP_OFF (uint8_t) 0 +#define DHCP_START (uint8_t) 1 +#define DHCP_WAIT_ADDRESS (uint8_t) 2 +#define DHCP_ADDRESS_ASSIGNED (uint8_t) 3 +#define DHCP_TIMEOUT (uint8_t) 4 +#define DHCP_LINK_DOWN (uint8_t) 5 +#define DHCP_ASK_RELEASE (uint8_t) 6 + +/* Maximum number of client per server */ +#define MAX_CLIENT 32 + +#ifdef ETH_INPUT_USE_IT +extern struct netif gnetif; +#endif + + +/* Exported functions ------------------------------------------------------- */ +void stm32_eth_init(const uint8_t *mac, const uint8_t *ip, const uint8_t *gw, const uint8_t *netmask); +void stm32_eth_scheduler(void); + +void User_notification(struct netif *netif); + +#if LWIP_DHCP +void stm32_DHCP_Process(struct netif *netif); +void stm32_DHCP_Periodic_Handle(struct netif *netif); +void stm32_DHCP_manual_config(void); +uint8_t stm32_get_DHCP_lease_state(void); +void stm32_set_DHCP_state(uint8_t state); +uint8_t stm32_get_DHCP_state(void); +uint8_t stm32_dhcp_started(void); +#else +#error "LWIP_DHCP must be enabled in lwipopts.h" +#endif + +#if LWIP_DNS +void stm32_dns_init(const uint8_t *dnsaddr); +int8_t stm32_dns_gethostbyname(const char *hostname, uint32_t *ipaddr); +#else +#error "LWIP_DNS must be enabled in lwipopts.h" +#endif + +#if LWIP_UDP +void udp_receive_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, + const ip_addr_t *addr, u16_t port); +#else +#error "LWIP_UDP must be enabled in lwipopts.h" +#endif + +uint32_t stm32_eth_get_ipaddr(void); +uint32_t stm32_eth_get_gwaddr(void); +uint32_t stm32_eth_get_netmaskaddr(void); +uint32_t stm32_eth_get_dnsaddr(void); +uint32_t stm32_eth_get_dhcpaddr(void); + +struct pbuf *stm32_new_data(struct pbuf *p, const uint8_t *buffer, size_t size); +struct pbuf *stm32_free_data(struct pbuf *p); +uint16_t stm32_get_data(struct pbuf_data *data, uint8_t *buffer, size_t size); + +ip_addr_t *u8_to_ip_addr(uint8_t *ipu8, ip_addr_t *ipaddr); +uint32_t ip_addr_to_u32(ip_addr_t *ipaddr); + +#if LWIP_TCP +err_t tcp_connected_callback(void *arg, struct tcp_pcb *tpcb, err_t err); +err_t tcp_accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err); +#else +#error "LWIP_TCP must be enabled in lwipopts.h" +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32_ETH_H__ */