├── .github └── workflows │ └── Continuous-Integration.yml ├── AUTHORS ├── README ├── README.md ├── examples ├── AdvancedChatServer │ └── AdvancedChatServer.ino ├── BarometricPressureWebServer │ └── BarometricPressureWebServer.ino ├── ChatServer │ └── ChatServer.ino ├── DhcpAddressPrinter │ └── DhcpAddressPrinter.ino ├── DhcpChatServer │ └── DhcpChatServer.ino ├── TelnetClient │ └── TelnetClient.ino ├── UDPSendReceiveString │ └── UDPSendReceiveString.ino ├── UdpNtpClient │ └── UdpNtpClient.ino ├── WebClient │ └── WebClient.ino ├── WebClientFreeRTOS │ └── WebClientFreeRTOS.ino ├── WebClientRepeating │ └── WebClientRepeating.ino ├── WebServer │ └── WebServer.ino └── WebServerFreeRTOS │ └── WebServerFreeRTOS.ino ├── keywords.txt ├── library.json ├── library.properties └── src ├── Dhcp.cpp ├── Dhcp.h ├── Dns.cpp ├── Dns.h ├── EthernetClient.cpp ├── EthernetClient.h ├── EthernetServer.cpp ├── EthernetServer.h ├── EthernetUdp.cpp ├── EthernetUdp.h ├── STM32Ethernet.cpp ├── STM32Ethernet.h ├── lwipopts.h ├── lwipopts_default.h └── utility ├── ethernetif.cpp ├── ethernetif.h ├── stm32_eth.cpp └── stm32_eth.h /.github/workflows/Continuous-Integration.yml: -------------------------------------------------------------------------------- 1 | name: STM32Ethernet Continuous Integration 2 | on: 3 | push: 4 | branches: 5 | - main 6 | paths-ignore: 7 | - '*' 8 | - '**.md' 9 | - '**.txt' 10 | pull_request: 11 | paths-ignore: 12 | - '*' 13 | - '**.md' 14 | - '**.txt' 15 | jobs: 16 | astyle_check: 17 | runs-on: ubuntu-latest 18 | name: AStyle check 19 | steps: 20 | # First of all, clone the repo using the checkout action. 21 | - name: Checkout 22 | uses: actions/checkout@main 23 | 24 | - name: Astyle check 25 | id: Astyle 26 | uses: stm32duino/actions/astyle-check@main 27 | 28 | # Use the output from the `Astyle` step 29 | - name: Astyle Errors 30 | if: failure() 31 | run: | 32 | cat ${{ steps.Astyle.outputs.astyle-result }} 33 | exit 1 34 | codespell: 35 | name: Check for spelling errors 36 | runs-on: ubuntu-latest 37 | steps: 38 | - name: Checkout 39 | uses: actions/checkout@main 40 | 41 | # See: https://github.com/codespell-project/actions-codespell/blob/master/README.md 42 | - name: Spell check 43 | uses: codespell-project/actions-codespell@master 44 | with: 45 | check_filenames: true 46 | check_hidden: true 47 | # In the event of a false positive, add the word in all lower case to this file: 48 | # ignore_words_file: ./extras/codespell-ignore-words-list.txt 49 | lib_build: 50 | runs-on: ubuntu-latest 51 | name: Library compilation 52 | steps: 53 | # First of all, clone the repo using the checkout action. 54 | - name: Checkout 55 | uses: actions/checkout@main 56 | 57 | - name: Compilation 58 | id: Compile 59 | uses: stm32duino/actions/compile-examples@main 60 | with: 61 | board-pattern: "DISCO_F746NG|NUCLEO_F429ZI|NUCLEO_F767ZI" 62 | libraries: "STM32duino LwIP, STM32duino FreeRTOS" 63 | 64 | # Use the output from the `Compile` step 65 | - name: Compilation Errors 66 | if: failure() 67 | run: | 68 | cat ${{ steps.Compile.outputs.compile-result }} 69 | exit 1 70 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | 2 | Alberto Panu https://github.com/bigjohnson 3 | Alasdair Allan https://github.com/aallan 4 | Alice Pintus https://github.com/00alis 5 | Adrian McEwen https://github.com/amcewen 6 | Arduino LLC http://arduino.cc/ 7 | Arnie97 https://github.com/Arnie97 8 | Arturo Guadalupi https://github.com/agdl 9 | Bjoern Hartmann https://people.eecs.berkeley.edu/~bjoern/ 10 | chaveiro https://github.com/chaveiro 11 | Cristian Maglie https://github.com/cmaglie 12 | David A. Mellis https://github.com/damellis 13 | Dino Tinitigan https://github.com/bigdinotech 14 | Eddy https://github.com/eddyst 15 | Federico Vanzati https://github.com/Fede85 16 | Federico Fissore https://github.com/ffissore 17 | Jack Christensen https://github.com/JChristensen 18 | Johann Richard https://github.com/johannrichard 19 | Jordan Terrell https://github.com/iSynaptic 20 | Justin Paulin https://github.com/interwho 21 | lathoub https://github.com/lathoub 22 | Martino Facchin https://github.com/facchinm 23 | Matthias Hertel https://github.com/mathertel 24 | Matthijs Kooijman https://github.com/matthijskooijman 25 | Matt Robinson https://github.com/ribbons 26 | MCQN Ltd. http://mcqn.com/ 27 | Michael Amie https://github.com/michaelamie 28 | Michael Margolis https://github.com/michaelmargolis 29 | Norbert Truchsess https://github.com/ntruchsess 30 | Paul Stoffregen https://github.com/PaulStoffregen 31 | per1234 https://github.com/per1234 32 | Richard Sim 33 | Scott Fitzgerald https://github.com/shfitz 34 | Stefan Staub https://github.com/sstaub 35 | STMicroelectronics https://github.com/stm32duino 36 | Thibaut Viard https://github.com/aethaniel 37 | Tom Igoe https://github.com/tigoe 38 | Wi6Labs http://www.wi6labs.com/ 39 | WizNet http://www.wiznet.co.kr 40 | Zach Eveland https://github.com/zeveland 41 | 42 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | == License == 2 | 3 | Copyright (c) 2010 Arduino LLC. All right reserved. 4 | 5 | This library is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU Lesser General Public 7 | License as published by the Free Software Foundation; either 8 | version 2.1 of the License, or (at your option) any later version. 9 | 10 | This library is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public 16 | License along with this library; if not, write to the Free Software 17 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # STM32 Ethernet Library for Arduino 2 | 3 | With an STM32 board with Ethernet compatibility, this library allows a STM32 4 | board (NUCLEO, DISCOVERY, ...) to connect to the internet. 5 | 6 | This library follows the Ethernet API from Arduino.
7 | For more information about it please visit: http://www.arduino.cc/en/Reference/Ethernet 8 | 9 | ## Dependency 10 | 11 | This library is based on LwIP, a Lightweight TCP/IP stack, available here: 12 | 13 | http://git.savannah.gnu.org/cgit/lwip.git 14 | 15 | The LwIP has been ported as Arduino library and is available thanks Arduino Library Manager. 16 | 17 | Source: https://github.com/stm32duino/LwIP 18 | 19 | ## Configuration 20 | 21 | The LwIP has several user defined options, which is specified from within the `lwipopts.h` file. 22 | 23 | This library provides a default user defined options file named `lwipopts_default.h`. 24 | 25 | User can provide his own defined options at sketch level by adding his configuration in a file named `STM32lwipopts.h` or 26 | extend the default one by adding some extra configuration in a file named `lwipopts_extra.h`. 27 | 28 | 29 | 30 | ## New alternative init procedure **!!!** 31 | 32 | There are alternative inits of the Ethernet interface with following orders: 33 | 34 | Ethernet.begin(); 35 | Ethernet.begin(ip); 36 | Ethernet.begin(ip, subnet); 37 | Ethernet.begin(ip, subnet, gateway); 38 | Ethernet.begin(ip, subnet, gateway, dns); 39 | 40 | This is more logical. A MAC address is no more needed and will retrieved internally by the mbed MAC address! 41 | 42 | You can get the MAC address with following function, this must be done after Ethernet.Begin() 43 | 44 | uint8_t *mac; 45 | Ethernet.begin(); 46 | Ethernet.MACAddress(mac); 47 | 48 | You can also set a new user based MAC address, this must be done before Ethernet.begin() 49 | 50 | uint8_t newMAC[] = {0x00, 0x80, 0xE1, 0x01, 0x01, 0x01}; 51 | Ethernet.setMACAddress(newMAC); 52 | Ethernet.begin(); 53 | 54 | ## Note 55 | 56 | `EthernetClass::maintain()` in no more required to renew IP address from DHCP.
57 | It is done automatically by the LwIP stack in a background task. 58 | 59 | An Idle task is required by the LwIP stack to handle timer and data reception.
60 | This idle task is called inside a timer callback each 1 ms by the 61 | function `stm32_eth_scheduler()`.
62 | A `DEFAULT_ETHERNET_TIMER` is set in the library to `TIM14`.
63 | `DEFAULT_ETHERNET_TIMER` can be redefined in the core variant.
64 | Be careful to not lock the system in a function which disabling IRQ.
65 | Call `Ethernet::schedule()` performs an update of the LwIP stack.
66 | 67 | ## Wiki 68 | 69 | You can find information at https://github.com/stm32duino/Arduino_Core_STM32/wiki/STM32Ethernet 70 | -------------------------------------------------------------------------------- /examples/AdvancedChatServer/AdvancedChatServer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Advanced Chat Server 3 | 4 | A more advanced server that distributes any incoming messages 5 | to all connected clients but the client the message comes from. 6 | To use, telnet to your device's IP address and type. 7 | You can see the client's input in the serial monitor as well. 8 | 9 | Circuit: 10 | * STM32 board with Ethernet support 11 | 12 | created 18 Dec 2009 13 | by David A. Mellis 14 | modified 9 Apr 2012 15 | by Tom Igoe 16 | redesigned to make use of operator== 25 Nov 2013 17 | by Norbert Truchsess 18 | modified 23 Jun 2017 19 | by Wi6Labs 20 | modified 1 Jun 2018 21 | by sstaub 22 | 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | // Enter an IP address for your controller below. 29 | // The IP address will be dependent on your local network. 30 | // gateway and subnet are optional: 31 | 32 | IPAddress ip(192, 168, 1, 177); 33 | IPAddress myDns(192, 168, 1, 1); 34 | IPAddress gateway(192, 168, 1, 1); 35 | IPAddress subnet(255, 255, 0, 0); 36 | 37 | 38 | // telnet defaults to port 23 39 | EthernetServer server(23); 40 | 41 | EthernetClient clients[4]; 42 | 43 | void setup() { 44 | // initialize the Ethernet device 45 | Ethernet.begin(ip, subnet, gateway, myDns); 46 | // start listening for clients 47 | server.begin(); 48 | // Open serial communications and wait for port to open: 49 | Serial.begin(9600); 50 | while (!Serial) { 51 | ; // wait for serial port to connect. Needed for native USB port only 52 | } 53 | 54 | 55 | Serial.print("Chat server address:"); 56 | Serial.println(Ethernet.localIP()); 57 | } 58 | 59 | void loop() { 60 | // wait for a new client: 61 | EthernetClient client = server.available(); 62 | 63 | // when the client sends the first byte, say hello: 64 | if (client) { 65 | 66 | bool newClient = true; 67 | for (byte i = 0; i < 4; i++) { 68 | //check whether this client refers to the same socket as one of the existing instances: 69 | if (clients[i] == client) { 70 | newClient = false; 71 | break; 72 | } 73 | } 74 | 75 | if (newClient) { 76 | //check which of the existing clients can be overridden: 77 | for (byte i = 0; i < 4; i++) { 78 | if (!clients[i] && clients[i] != client) { 79 | clients[i] = client; 80 | // clear out the input buffer: 81 | client.flush(); 82 | Serial.println("We have a new client"); 83 | client.print("Hello, client number: "); 84 | client.print(i); 85 | client.println(); 86 | break; 87 | } 88 | } 89 | } 90 | 91 | if (client.available() > 0) { 92 | // read the bytes incoming from the client: 93 | char thisChar = client.read(); 94 | // echo the bytes back to all other connected clients: 95 | for (byte i = 0; i < 4; i++) { 96 | if (clients[i] && (clients[i] != client)) { 97 | clients[i].write(thisChar); 98 | } 99 | } 100 | // echo the bytes to the server as well: 101 | Serial.write(thisChar); 102 | } 103 | } 104 | for (byte i = 0; i < 4; i++) { 105 | if (!(clients[i].connected())) { 106 | // client.stop() invalidates the internal socket-descriptor, so next use of == will always return false; 107 | clients[i].stop(); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /examples/BarometricPressureWebServer/BarometricPressureWebServer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | SCP1000 Barometric Pressure Sensor Display 3 | 4 | Serves the output of a Barometric Pressure Sensor as a web page. 5 | Uses the SPI library. For details on the sensor, see: 6 | http://www.sparkfun.com/commerce/product_info.php?products_id=8161 7 | 8 | This sketch adapted from Nathan Seidle's SCP1000 example for PIC: 9 | http://www.sparkfun.com/datasheets/Sensors/SCP1000-Testing.zip 10 | 11 | Circuit: 12 | SCP1000 sensor attached to pins 6,7, and 11 - 13: 13 | DRDY: pin 6 14 | CSB: pin 7 15 | MOSI: pin 11 16 | MISO: pin 12 17 | SCK: pin 13 18 | 19 | created 31 July 2010 20 | by Tom Igoe 21 | modified 23 Jun 2017 22 | by Wi6Labs 23 | modified 1 Jun 2018 24 | by sstaub 25 | */ 26 | 27 | #include 28 | #include 29 | // the sensor communicates using SPI, so include the library: 30 | #include 31 | 32 | 33 | // assign an IP address for the controller: 34 | IPAddress ip(192, 168, 1, 20); 35 | 36 | 37 | // Initialize the Ethernet server library 38 | // with the IP address and port you want to use 39 | // (port 80 is default for HTTP): 40 | EthernetServer server(80); 41 | 42 | 43 | //Sensor's memory register addresses: 44 | const int PRESSURE = 0x1F; //3 most significant bits of pressure 45 | const int PRESSURE_LSB = 0x20; //16 least significant bits of pressure 46 | const int TEMPERATURE = 0x21; //16 bit temperature reading 47 | 48 | // pins used for the connection with the sensor 49 | // the others you need are controlled by the SPI library): 50 | const int dataReadyPin = 6; 51 | const int chipSelectPin = 7; 52 | 53 | float temperature = 0.0; 54 | long pressure = 0; 55 | long lastReadingTime = 0; 56 | 57 | void setup() { 58 | // start the SPI library: 59 | SPI.begin(); 60 | 61 | // start the Ethernet connection and the server: 62 | Ethernet.begin(ip); 63 | server.begin(); 64 | 65 | // initialize the data ready and chip select pins: 66 | pinMode(dataReadyPin, INPUT); 67 | pinMode(chipSelectPin, OUTPUT); 68 | 69 | Serial.begin(9600); 70 | 71 | //Configure SCP1000 for low noise configuration: 72 | writeRegister(0x02, 0x2D); 73 | writeRegister(0x01, 0x03); 74 | writeRegister(0x03, 0x02); 75 | 76 | // give the sensor and Ethernet shield time to set up: 77 | delay(1000); 78 | 79 | //Set the sensor to high resolution mode tp start readings: 80 | writeRegister(0x03, 0x0A); 81 | 82 | } 83 | 84 | void loop() { 85 | // check for a reading no more than once a second. 86 | if (millis() - lastReadingTime > 1000) { 87 | // if there's a reading ready, read it: 88 | // don't do anything until the data ready pin is high: 89 | if (digitalRead(dataReadyPin) == HIGH) { 90 | getData(); 91 | // timestamp the last time you got a reading: 92 | lastReadingTime = millis(); 93 | } 94 | } 95 | 96 | // listen for incoming Ethernet connections: 97 | listenForEthernetClients(); 98 | } 99 | 100 | 101 | void getData() { 102 | Serial.println("Getting reading"); 103 | //Read the temperature data 104 | int tempData = readRegister(0x21, 2); 105 | 106 | // convert the temperature to celsius and display it: 107 | temperature = (float)tempData / 20.0; 108 | 109 | //Read the pressure data highest 3 bits: 110 | byte pressureDataHigh = readRegister(0x1F, 1); 111 | pressureDataHigh &= 0b00000111; //you only needs bits 2 to 0 112 | 113 | //Read the pressure data lower 16 bits: 114 | unsigned int pressureDataLow = readRegister(0x20, 2); 115 | //combine the two parts into one 19-bit number: 116 | pressure = ((pressureDataHigh << 16) | pressureDataLow) / 4; 117 | 118 | Serial.print("Temperature: "); 119 | Serial.print(temperature); 120 | Serial.println(" degrees C"); 121 | Serial.print("Pressure: " + String(pressure)); 122 | Serial.println(" Pa"); 123 | } 124 | 125 | void listenForEthernetClients() { 126 | // listen for incoming clients 127 | EthernetClient client = server.available(); 128 | if (client) { 129 | Serial.println("Got a client"); 130 | // an http request ends with a blank line 131 | bool currentLineIsBlank = true; 132 | while (client.connected()) { 133 | if (client.available()) { 134 | char c = client.read(); 135 | // if you've gotten to the end of the line (received a newline 136 | // character) and the line is blank, the http request has ended, 137 | // so you can send a reply 138 | if (c == '\n' && currentLineIsBlank) { 139 | // send a standard http response header 140 | client.println("HTTP/1.1 200 OK"); 141 | client.println("Content-Type: text/html"); 142 | client.println(); 143 | // print the current readings, in HTML format: 144 | client.print("Temperature: "); 145 | client.print(temperature); 146 | client.print(" degrees C"); 147 | client.println("
"); 148 | client.print("Pressure: " + String(pressure)); 149 | client.print(" Pa"); 150 | client.println("
"); 151 | break; 152 | } 153 | if (c == '\n') { 154 | // you're starting a new line 155 | currentLineIsBlank = true; 156 | } else if (c != '\r') { 157 | // you've gotten a character on the current line 158 | currentLineIsBlank = false; 159 | } 160 | } 161 | } 162 | // give the web browser time to receive the data 163 | delay(1); 164 | // close the connection: 165 | client.stop(); 166 | } 167 | } 168 | 169 | 170 | //Send a write command to SCP1000 171 | void writeRegister(byte registerName, byte registerValue) { 172 | // SCP1000 expects the register name in the upper 6 bits 173 | // of the byte: 174 | registerName <<= 2; 175 | // command (read or write) goes in the lower two bits: 176 | registerName |= 0b00000010; //Write command 177 | 178 | // take the chip select low to select the device: 179 | digitalWrite(chipSelectPin, LOW); 180 | 181 | SPI.transfer(registerName); //Send register location 182 | SPI.transfer(registerValue); //Send value to record into register 183 | 184 | // take the chip select high to de-select: 185 | digitalWrite(chipSelectPin, HIGH); 186 | } 187 | 188 | 189 | //Read register from the SCP1000: 190 | unsigned int readRegister(byte registerName, int numBytes) { 191 | byte inByte = 0; // incoming from the SPI read 192 | unsigned int result = 0; // result to return 193 | 194 | // SCP1000 expects the register name in the upper 6 bits 195 | // of the byte: 196 | registerName <<= 2; 197 | // command (read or write) goes in the lower two bits: 198 | registerName &= 0b11111100; //Read command 199 | 200 | // take the chip select low to select the device: 201 | digitalWrite(chipSelectPin, LOW); 202 | // send the device the register you want to read: 203 | int command = SPI.transfer(registerName); 204 | // send a value of 0 to read the first byte returned: 205 | inByte = SPI.transfer(0x00); 206 | 207 | result = inByte; 208 | // if there's more than one byte returned, 209 | // shift the first byte then get the second byte: 210 | if (numBytes > 1) { 211 | result = inByte << 8; 212 | inByte = SPI.transfer(0x00); 213 | result = result | inByte; 214 | } 215 | // take the chip select high to de-select: 216 | digitalWrite(chipSelectPin, HIGH); 217 | // return the result: 218 | return (result); 219 | } 220 | -------------------------------------------------------------------------------- /examples/ChatServer/ChatServer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Chat Server 3 | 4 | A simple server that distributes any incoming messages to all 5 | connected clients. To use, telnet to your device's IP address and type. 6 | You can see the client's input in the serial monitor as well. 7 | 8 | Circuit: 9 | * STM32 board with Ethernet support 10 | 11 | created 18 Dec 2009 12 | by David A. Mellis 13 | modified 9 Apr 2012 14 | by Tom Igoe 15 | modified 23 Jun 2017 16 | by Wi6Labs 17 | modified 1 Jun 2018 18 | by sstaub 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | // Enter an IP address for your controller below. 25 | // The IP address will be dependent on your local network. 26 | // gateway and subnet are optional: 27 | 28 | IPAddress ip(192, 168, 1, 177); 29 | IPAddress myDns(192,168,1, 1); 30 | IPAddress gateway(192, 168, 1, 1); 31 | IPAddress subnet(255, 255, 0, 0); 32 | 33 | 34 | // telnet defaults to port 23 35 | EthernetServer server(23); 36 | bool alreadyConnected = false; // whether or not the client was connected previously 37 | 38 | void setup() { 39 | // initialize the ethernet device 40 | Ethernet.begin(ip, subnet, gateway, myDns); 41 | // start listening for clients 42 | server.begin(); 43 | // Open serial communications and wait for port to open: 44 | Serial.begin(9600); 45 | while (!Serial) { 46 | ; // wait for serial port to connect. Needed for native USB port only 47 | } 48 | 49 | 50 | Serial.print("Chat server address:"); 51 | Serial.println(Ethernet.localIP()); 52 | } 53 | 54 | void loop() { 55 | // wait for a new client: 56 | EthernetClient client = server.available(); 57 | 58 | // when the client sends the first byte, say hello: 59 | if (client) { 60 | if (!alreadyConnected) { 61 | // clear out the input buffer: 62 | client.flush(); 63 | Serial.println("We have a new client"); 64 | client.println("Hello, client!"); 65 | alreadyConnected = true; 66 | } 67 | 68 | if (client.available() > 0) { 69 | // read the bytes incoming from the client: 70 | char thisChar = client.read(); 71 | // echo the bytes back to the client: 72 | server.write(thisChar); 73 | // echo the bytes to the server as well: 74 | Serial.write(thisChar); 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /examples/DhcpAddressPrinter/DhcpAddressPrinter.ino: -------------------------------------------------------------------------------- 1 | /* 2 | DHCP-based IP printer 3 | 4 | This sketch uses the DHCP extensions to the Ethernet library 5 | to get an IP address via DHCP and print the address obtained. 6 | 7 | Circuit: 8 | STM32 board with Ethernet support 9 | 10 | created 12 April 2011 11 | modified 9 Apr 2012 12 | by Tom Igoe 13 | modified 02 Sept 2015 14 | by Arturo Guadalupi 15 | modified 23 Jun 2017 16 | by Wi6Labs 17 | modified 1 Jun 2018 18 | by sstaub 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | 25 | // Initialize the Ethernet client library 26 | // with the IP address and port of the server 27 | // that you want to connect to (port 80 is default for HTTP): 28 | EthernetClient client; 29 | 30 | void setup() { 31 | // Open serial communications and wait for port to open: 32 | Serial.begin(9600); 33 | // this check is only needed on the Leonardo: 34 | while (!Serial) { 35 | ; // wait for serial port to connect. Needed for native USB port only 36 | } 37 | 38 | // start the Ethernet connection: 39 | if (Ethernet.begin() == 0) { 40 | Serial.println("Failed to configure Ethernet using DHCP"); 41 | // no point in carrying on, so do nothing forevermore: 42 | for (;;) 43 | ; 44 | } 45 | // print your local IP address: 46 | printIPAddress(); 47 | } 48 | 49 | void loop() { 50 | 51 | switch (Ethernet.maintain()) 52 | { 53 | case 1: 54 | //renewed fail 55 | Serial.println("Error: renewed fail"); 56 | break; 57 | 58 | case 2: 59 | //renewed success 60 | Serial.println("Renewed success"); 61 | 62 | //print your local IP address: 63 | printIPAddress(); 64 | break; 65 | 66 | case 3: 67 | //rebind fail 68 | Serial.println("Error: rebind fail"); 69 | break; 70 | 71 | case 4: 72 | //rebind success 73 | Serial.println("Rebind success"); 74 | 75 | //print your local IP address: 76 | printIPAddress(); 77 | break; 78 | 79 | default: 80 | //nothing happened 81 | break; 82 | 83 | } 84 | } 85 | 86 | void printIPAddress() 87 | { 88 | Serial.print("My IP address: "); 89 | for (byte thisByte = 0; thisByte < 4; thisByte++) { 90 | // print the value of each byte of the IP address: 91 | Serial.print(Ethernet.localIP()[thisByte], DEC); 92 | Serial.print("."); 93 | } 94 | 95 | Serial.println(); 96 | } 97 | -------------------------------------------------------------------------------- /examples/DhcpChatServer/DhcpChatServer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | DHCP Chat Server 3 | 4 | A simple server that distributes any incoming messages to all 5 | connected clients. To use, telnet to your device's IP address and type. 6 | You can see the client's input in the serial monitor as well. 7 | 8 | THis version attempts to get an IP address using DHCP 9 | 10 | Circuit: 11 | * STM32 board with Ethernet support 12 | 13 | created 21 May 2011 14 | modified 9 Apr 2012 15 | by Tom Igoe 16 | modified 02 Sept 2015 17 | by Arturo Guadalupi 18 | Based on ChatServer example by David A. Mellis 19 | modified 23 Jun 2017 20 | by Wi6Labs 21 | modified 1 Jun 2018 22 | by sstaub 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | // Enter an IP address for your controller below. 29 | // The IP address will be dependent on your local network. 30 | // gateway and subnet are optional: 31 | IPAddress ip(192, 168, 1, 177); 32 | IPAddress myDns(192,168,1, 1); 33 | IPAddress gateway(192, 168, 1, 1); 34 | IPAddress subnet(255, 255, 0, 0); 35 | 36 | // telnet defaults to port 23 37 | EthernetServer server(23); 38 | bool gotAMessage = false; // whether or not you got a message from the client yet 39 | 40 | void setup() { 41 | // Open serial communications and wait for port to open: 42 | Serial.begin(9600); 43 | // this check is only needed on the Leonardo: 44 | while (!Serial) { 45 | ; // wait for serial port to connect. Needed for native USB port only 46 | } 47 | 48 | 49 | // start the Ethernet connection: 50 | Serial.println("Trying to get an IP address using DHCP"); 51 | if (Ethernet.begin() == 0) { 52 | Serial.println("Failed to configure Ethernet using DHCP"); 53 | // initialize the Ethernet device not using DHCP: 54 | Ethernet.begin(ip, subnet, gateway, myDns); 55 | } 56 | // print your local IP address: 57 | Serial.print("My IP address: "); 58 | ip = Ethernet.localIP(); 59 | for (byte thisByte = 0; thisByte < 4; thisByte++) { 60 | // print the value of each byte of the IP address: 61 | Serial.print(ip[thisByte], DEC); 62 | Serial.print("."); 63 | } 64 | Serial.println(); 65 | // start listening for clients 66 | server.begin(); 67 | 68 | } 69 | 70 | void loop() { 71 | // wait for a new client: 72 | EthernetClient client = server.available(); 73 | 74 | // when the client sends the first byte, say hello: 75 | if (client) { 76 | if (!gotAMessage) { 77 | Serial.println("We have a new client"); 78 | client.println("Hello, client!"); 79 | gotAMessage = true; 80 | } 81 | 82 | // read the bytes incoming from the client: 83 | char thisChar = client.read(); 84 | // echo the bytes back to the client: 85 | server.write(thisChar); 86 | // echo the bytes to the server as well: 87 | Serial.print(thisChar); 88 | Ethernet.maintain(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /examples/TelnetClient/TelnetClient.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Telnet client 3 | 4 | This sketch connects to a a telnet server (http://www.google.com) 5 | using an Arduino Wiznet Ethernet shield. You'll need a telnet server 6 | to test this with. 7 | Processing's ChatServer example (part of the network library) works well, 8 | running on port 10002. It can be found as part of the examples 9 | in the Processing application, available at 10 | http://processing.org/ 11 | 12 | Circuit: 13 | * STM32 board with Ethernet support 14 | 15 | created 14 Sep 2010 16 | modified 9 Apr 2012 17 | by Tom Igoe 18 | modified 23 Jun 2017 19 | by Wi6Labs 20 | modified 1 Jun 2018 21 | by sstaub 22 | */ 23 | 24 | #include 25 | #include 26 | 27 | // Enter an IP address for your controller below. 28 | // The IP address will be dependent on your local network: 29 | IPAddress ip(192, 168, 1, 177); 30 | 31 | // Enter the IP address of the server you're connecting to: 32 | IPAddress server(1, 1, 1, 1); 33 | 34 | // Initialize the Ethernet client library 35 | // with the IP address and port of the server 36 | // that you want to connect to (port 23 is default for telnet; 37 | // if you're using Processing's ChatServer, use port 10002): 38 | EthernetClient client; 39 | 40 | void setup() { 41 | // start the Ethernet connection: 42 | Ethernet.begin(ip); 43 | // Open serial communications and wait for port to open: 44 | Serial.begin(9600); 45 | while (!Serial) { 46 | ; // wait for serial port to connect. Needed for native USB port only 47 | } 48 | 49 | 50 | // give the Ethernet shield a second to initialize: 51 | delay(1000); 52 | Serial.println("connecting..."); 53 | 54 | // if you get a connection, report back via serial: 55 | if (client.connect(server, 10002)) { 56 | Serial.println("connected"); 57 | } else { 58 | // if you didn't get a connection to the server: 59 | Serial.println("connection failed"); 60 | } 61 | } 62 | 63 | void loop() { 64 | // if there are incoming bytes available 65 | // from the server, read them and print them: 66 | if (client.available()) { 67 | char c = client.read(); 68 | Serial.print(c); 69 | } 70 | 71 | // as long as there are bytes in the serial queue, 72 | // read them and send them out the socket if it's open: 73 | while (Serial.available() > 0) { 74 | char inChar = Serial.read(); 75 | if (client.connected()) { 76 | client.print(inChar); 77 | } 78 | } 79 | 80 | // if the server's disconnected, stop the client: 81 | if (!client.connected()) { 82 | Serial.println(); 83 | Serial.println("disconnecting."); 84 | client.stop(); 85 | // do nothing: 86 | while (true); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /examples/UDPSendReceiveString/UDPSendReceiveString.ino: -------------------------------------------------------------------------------- 1 | /* 2 | UDPSendReceiveString: 3 | This sketch receives UDP message strings, prints them to the serial port 4 | and sends an "acknowledge" string back to the sender 5 | 6 | A Processing sketch is included at the end of file that can be used to send 7 | and received messages for testing with a computer. 8 | 9 | created 21 Aug 2010 10 | by Michael Margolis 11 | modified 23 Jun 2017 12 | by Wi6Labs 13 | modified 1 Jun 2018 14 | by sstaub 15 | This code is in the public domain. 16 | */ 17 | 18 | #include 19 | #include 20 | #include // UDP library from: bjoern@cs.stanford.edu 12/30/2008 21 | 22 | 23 | // Enter an IP address for your controller below. 24 | // The IP address will be dependent on your local network: 25 | IPAddress ip(192, 168, 1, 177); 26 | 27 | unsigned int localPort = 8888; // local port to listen on 28 | 29 | // buffers for receiving and sending data 30 | char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; //buffer to hold incoming packet, 31 | char ReplyBuffer[] = "acknowledged"; // a string to send back 32 | 33 | // An EthernetUDP instance to let us send and receive packets over UDP 34 | EthernetUDP Udp; 35 | 36 | void setup() { 37 | // start the Ethernet and UDP: 38 | Ethernet.begin(ip); 39 | Udp.begin(localPort); 40 | 41 | Serial.begin(9600); 42 | } 43 | 44 | void loop() { 45 | // if there's data available, read a packet 46 | int packetSize = Udp.parsePacket(); 47 | if (packetSize) { 48 | Serial.print("Received packet of size "); 49 | Serial.println(packetSize); 50 | Serial.print("From "); 51 | IPAddress remote = Udp.remoteIP(); 52 | for (int i = 0; i < 4; i++) { 53 | Serial.print(remote[i], DEC); 54 | if (i < 3) { 55 | Serial.print("."); 56 | } 57 | } 58 | Serial.print(", port "); 59 | Serial.println(Udp.remotePort()); 60 | 61 | // read the packet into packetBufffer 62 | Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE); 63 | Serial.println("Contents:"); 64 | Serial.println(packetBuffer); 65 | 66 | // send a reply to the IP address and port that sent us the packet we received 67 | Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); 68 | Udp.write(ReplyBuffer); 69 | Udp.endPacket(); 70 | } 71 | delay(10); 72 | } 73 | 74 | 75 | /* 76 | Processing sketch to run with this example 77 | ===================================================== 78 | 79 | // Processing UDP example to send and receive string data from Arduino 80 | // press any key to send the "Hello Arduino" message 81 | 82 | 83 | import hypermedia.net.*; 84 | 85 | UDP udp; // define the UDP object 86 | 87 | 88 | void setup() { 89 | udp = new UDP( this, 6000 ); // create a new datagram connection on port 6000 90 | //udp.log( true ); // <-- printout the connection activity 91 | udp.listen( true ); // and wait for incoming message 92 | } 93 | 94 | void draw() 95 | { 96 | } 97 | 98 | void keyPressed() { 99 | String ip = "192.168.1.177"; // the remote IP address 100 | int port = 8888; // the destination port 101 | 102 | udp.send("Hello World", ip, port ); // the message to send 103 | 104 | } 105 | 106 | void receive( byte[] data ) { // <-- default handler 107 | //void receive( byte[] data, String ip, int port ) { // <-- extended handler 108 | 109 | for(int i=0; i < data.length; i++) 110 | print(char(data[i])); 111 | println(); 112 | } 113 | */ 114 | -------------------------------------------------------------------------------- /examples/UdpNtpClient/UdpNtpClient.ino: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Udp NTP Client 4 | 5 | Get the time from a Network Time Protocol (NTP) time server 6 | Demonstrates use of UDP sendPacket and ReceivePacket 7 | For more on NTP time servers and the messages needed to communicate with them, 8 | see http://en.wikipedia.org/wiki/Network_Time_Protocol 9 | 10 | created 4 Sep 2010 11 | by Michael Margolis 12 | modified 9 Apr 2012 13 | by Tom Igoe 14 | modified 02 Sept 2015 15 | by Arturo Guadalupi 16 | modified 23 Jun 2017 17 | by Wi6Labs 18 | modified 1 Jun 2018 19 | by sstaub 20 | This code is in the public domain. 21 | 22 | */ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | unsigned int localPort = 8888; // local port to listen for UDP packets 29 | 30 | char timeServer[] = "time.nist.gov"; // time.nist.gov NTP server 31 | 32 | const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message 33 | 34 | byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets 35 | 36 | // A UDP instance to let us send and receive packets over UDP 37 | EthernetUDP Udp; 38 | 39 | void setup() { 40 | // Open serial communications and wait for port to open: 41 | Serial.begin(9600); 42 | while (!Serial) { 43 | ; // wait for serial port to connect. Needed for native USB port only 44 | } 45 | 46 | 47 | // start Ethernet and UDP 48 | if (Ethernet.begin() == 0) { 49 | Serial.println("Failed to configure Ethernet using DHCP"); 50 | // no point in carrying on, so do nothing forevermore: 51 | for (;;) 52 | ; 53 | } 54 | Udp.begin(localPort); 55 | } 56 | 57 | void loop() { 58 | sendNTPpacket(timeServer); // send an NTP packet to a time server 59 | 60 | // wait to see if a reply is available 61 | delay(1000); 62 | if (Udp.parsePacket()) { 63 | // We've received a packet, read the data from it 64 | Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer 65 | 66 | // the timestamp starts at byte 40 of the received packet and is four bytes, 67 | // or two words, long. First, extract the two words: 68 | 69 | unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); 70 | unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); 71 | // combine the four bytes (two words) into a long integer 72 | // this is NTP time (seconds since Jan 1 1900): 73 | unsigned long secsSince1900 = highWord << 16 | lowWord; 74 | Serial.print("Seconds since Jan 1 1900 = "); 75 | Serial.println(secsSince1900); 76 | 77 | // now convert NTP time into everyday time: 78 | Serial.print("Unix time = "); 79 | // Unix time starts on Jan 1 1970. In seconds, that's 2208988800: 80 | const unsigned long seventyYears = 2208988800UL; 81 | // subtract seventy years: 82 | unsigned long epoch = secsSince1900 - seventyYears; 83 | // print Unix time: 84 | Serial.println(epoch); 85 | 86 | 87 | // print the hour, minute and second: 88 | Serial.print("The UTC time is "); // UTC is the time at Greenwich Meridian (GMT) 89 | Serial.print((epoch % 86400L) / 3600); // print the hour (86400 equals secs per day) 90 | Serial.print(':'); 91 | if (((epoch % 3600) / 60) < 10) { 92 | // In the first 10 minutes of each hour, we'll want a leading '0' 93 | Serial.print('0'); 94 | } 95 | Serial.print((epoch % 3600) / 60); // print the minute (3600 equals secs per minute) 96 | Serial.print(':'); 97 | if ((epoch % 60) < 10) { 98 | // In the first 10 seconds of each minute, we'll want a leading '0' 99 | Serial.print('0'); 100 | } 101 | Serial.println(epoch % 60); // print the second 102 | } 103 | // wait ten seconds before asking for the time again 104 | delay(10000); 105 | Ethernet.maintain(); 106 | } 107 | 108 | // send an NTP request to the time server at the given address 109 | void sendNTPpacket(char* address) { 110 | // set all bytes in the buffer to 0 111 | memset(packetBuffer, 0, NTP_PACKET_SIZE); 112 | // Initialize values needed to form NTP request 113 | // (see URL above for details on the packets) 114 | packetBuffer[0] = 0b11100011; // LI, Version, Mode 115 | packetBuffer[1] = 0; // Stratum, or type of clock 116 | packetBuffer[2] = 6; // Polling Interval 117 | packetBuffer[3] = 0xEC; // Peer Clock Precision 118 | // 8 bytes of zero for Root Delay & Root Dispersion 119 | packetBuffer[12] = 49; 120 | packetBuffer[13] = 0x4E; 121 | packetBuffer[14] = 49; 122 | packetBuffer[15] = 52; 123 | 124 | // all NTP fields have been given values, now 125 | // you can send a packet requesting a timestamp: 126 | Udp.beginPacket(address, 123); //NTP requests are to port 123 127 | Udp.write(packetBuffer, NTP_PACKET_SIZE); 128 | Udp.endPacket(); 129 | } 130 | -------------------------------------------------------------------------------- /examples/WebClient/WebClient.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Web client 3 | 4 | This sketch connects to a website (http://www.google.com) 5 | 6 | Circuit: 7 | * STM32 board with Ethernet support 8 | 9 | created 18 Dec 2009 10 | by David A. Mellis 11 | modified 9 Apr 2012 12 | by Tom Igoe, based on work by Adrian McEwen 13 | modified 23 Jun 2017 14 | by Wi6Labs 15 | modified 1 Jun 2018 16 | by sstaub 17 | */ 18 | 19 | #include 20 | #include 21 | 22 | // if you don't want to use DNS (and reduce your sketch size) 23 | // use the numeric IP instead of the name for the server: 24 | //IPAddress server(74,125,232,128); // numeric IP for Google (no DNS) 25 | char server[] = "www.google.com"; // name address for Google (using DNS) 26 | 27 | // Set the static IP address to use if the DHCP fails to assign 28 | IPAddress ip(192, 168, 0, 177); 29 | 30 | // Initialize the Ethernet client library 31 | // with the IP address and port of the server 32 | // that you want to connect to (port 80 is default for HTTP): 33 | EthernetClient client; 34 | 35 | void setup() { 36 | // Open serial communications and wait for port to open: 37 | Serial.begin(9600); 38 | while (!Serial) { 39 | ; // wait for serial port to connect. Needed for native USB port only 40 | } 41 | 42 | // start the Ethernet connection: 43 | if (Ethernet.begin() == 0) { 44 | Serial.println("Failed to configure Ethernet using DHCP"); 45 | // try to configure using IP address instead of DHCP: 46 | Ethernet.begin(ip); 47 | } 48 | // give the Ethernet shield a second to initialize: 49 | delay(1000); 50 | Serial.println("connecting..."); 51 | 52 | // if you get a connection, report back via serial: 53 | if (client.connect(server, 80)) { 54 | Serial.println("connected"); 55 | // Make a HTTP request: 56 | client.println("GET /search?q=arduino HTTP/1.1"); 57 | client.println("Host: www.google.com"); 58 | client.println("Connection: close"); 59 | client.println(); 60 | } else { 61 | // if you didn't get a connection to the server: 62 | Serial.println("connection failed"); 63 | } 64 | } 65 | 66 | void loop() { 67 | // if there are incoming bytes available 68 | // from the server, read them and print them: 69 | if (client.available()) { 70 | char c = client.read(); 71 | Serial.print(c); 72 | } 73 | 74 | // if the server's disconnected, stop the client: 75 | if (!client.connected()) { 76 | Serial.println(); 77 | Serial.println("disconnecting."); 78 | client.stop(); 79 | 80 | // do nothing forevermore: 81 | while (true); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /examples/WebClientFreeRTOS/WebClientFreeRTOS.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Port of WebClient on FreeRTOS 3 | 4 | This sketch connects to a website (http://www.google.com) 5 | 6 | Circuit: 7 | * STM32 board with Ethernet support 8 | 9 | created 18 Dec 2009 10 | by David A. Mellis 11 | modified 9 Apr 2012 12 | by Tom Igoe, based on work by Adrian McEwen 13 | modified 23 Jun 2017 14 | by Wi6Labs 15 | modified 1 Jun 2018 16 | by sstaub 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | // if you don't want to use DNS (and reduce your sketch size) 24 | // use the numeric IP instead of the name for the server: 25 | //IPAddress server(74,125,232,128); // numeric IP for Google (no DNS) 26 | char server[] = "www.google.com"; // name address for Google (using DNS) 27 | 28 | // Set the static IP address to use if the DHCP fails to assign 29 | IPAddress ip(192, 168, 0, 177); 30 | 31 | // Initialize the Ethernet client library 32 | // with the IP address and port of the server 33 | // that you want to connect to (port 80 is default for HTTP): 34 | EthernetClient client; 35 | 36 | 37 | // task code 38 | void taskETH(void* arg) { 39 | UNUSED(arg); 40 | // start the Ethernet connection: 41 | if (Ethernet.begin() == 0) { 42 | Serial.println("Failed to configure Ethernet using DHCP"); 43 | // try to configure using IP address instead of DHCP: 44 | Ethernet.begin(ip); 45 | } 46 | // give the Ethernet shield a second to initialize: 47 | delay(1000); 48 | Serial.println("connecting..."); 49 | 50 | // if you get a connection, report back via serial: 51 | if (client.connect(server, 80)) { 52 | Serial.println("connected"); 53 | // Make a HTTP request: 54 | client.println("GET /search?q=arduino HTTP/1.1"); 55 | client.println("Host: www.google.com"); 56 | client.println("Connection: close"); 57 | client.println(); 58 | } else { 59 | // if you didn't get a connection to the server: 60 | Serial.println("connection failed"); 61 | } 62 | 63 | while(1){ 64 | // if there are incoming bytes available 65 | // from the server, read them and print them: 66 | if (client.available()) { 67 | char c = client.read(); 68 | Serial.print(c); 69 | } 70 | 71 | // if the server's disconnected, stop the client: 72 | if (!client.connected()) { 73 | Serial.println(); 74 | Serial.println("disconnecting."); 75 | client.stop(); 76 | 77 | // do nothing forevermore: 78 | while (true); 79 | } 80 | } 81 | } 82 | 83 | 84 | void setup() { 85 | // Open serial communications and wait for port to open: 86 | Serial.begin(9600); 87 | while (!Serial) { 88 | ; // wait for serial port to connect. Needed for native USB port only 89 | } 90 | 91 | portBASE_TYPE s = xTaskCreate(taskETH, NULL, 200, NULL, 1, NULL); 92 | if (s != pdPASS) { 93 | printf("Ethernet task creation failed\n"); 94 | while (1); 95 | } 96 | 97 | vTaskStartScheduler(); 98 | Serial.println("Scheduler failed"); 99 | while (1); 100 | } 101 | 102 | void loop() { 103 | // Not used. 104 | } 105 | -------------------------------------------------------------------------------- /examples/WebClientRepeating/WebClientRepeating.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Repeating Web client 3 | 4 | This sketch connects to a a web server and makes a request. 5 | 6 | This example uses DNS, by assigning the Ethernet client with a MAC address, 7 | IP address, and DNS address. 8 | 9 | Circuit: 10 | * STM32 board with Ethernet support 11 | 12 | created 19 Apr 2012 13 | by Tom Igoe 14 | modified 21 Jan 2014 15 | by Federico Vanzati 16 | modified 23 Jun 2017 17 | by Wi6Labs 18 | modified 1 Jun 2018 19 | by sstaub 20 | http://www.arduino.cc/en/Tutorial/WebClientRepeating 21 | This code is in the public domain. 22 | 23 | */ 24 | 25 | #include 26 | #include 27 | 28 | // fill in an available IP address on your network here, 29 | // for manual configuration: 30 | IPAddress ip(192, 168, 1, 177); 31 | IPAddress gateway(192, 168, 1, 1); 32 | IPAddress subnet(255, 255, 0, 0); 33 | // fill in your Domain Name Server address here: 34 | IPAddress myDns(1, 1, 1, 1); 35 | 36 | // initialize the library instance: 37 | EthernetClient client; 38 | 39 | char server[] = "www.arduino.cc"; 40 | //IPAddress server(64,131,82,241); 41 | 42 | unsigned long lastConnectionTime = 0; // last time you connected to the server, in milliseconds 43 | const unsigned long postingInterval = 10L * 1000L; // delay between updates, in milliseconds 44 | // the "L" is needed to use long type numbers 45 | 46 | void setup() { 47 | // start serial port: 48 | Serial.begin(9600); 49 | while (!Serial) { 50 | ; // wait for serial port to connect. Needed for native USB port only 51 | } 52 | 53 | // give the ethernet module time to boot up: 54 | delay(1000); 55 | // start the Ethernet connection using a fixed IP address and DNS server: 56 | Ethernet.begin(ip, subnet, gateway, myDns); 57 | // print the Ethernet board/shield's IP address: 58 | Serial.print("My IP address: "); 59 | Serial.println(Ethernet.localIP()); 60 | } 61 | 62 | void loop() { 63 | // if there's incoming data from the net connection. 64 | // send it out the serial port. This is for debugging 65 | // purposes only: 66 | if (client.available()) { 67 | char c = client.read(); 68 | Serial.write(c); 69 | } 70 | 71 | // if ten seconds have passed since your last connection, 72 | // then connect again and send data: 73 | if (millis() - lastConnectionTime > postingInterval) { 74 | httpRequest(); 75 | } 76 | 77 | } 78 | 79 | // this method makes a HTTP connection to the server: 80 | void httpRequest() { 81 | // close any connection before send a new request. 82 | // This will free the socket on the WiFi shield 83 | client.stop(); 84 | 85 | // if there's a successful connection: 86 | if (client.connect(server, 80)) { 87 | Serial.println("connecting..."); 88 | // send the HTTP GET request: 89 | client.println("GET /latest.txt HTTP/1.1"); 90 | client.println("Host: www.arduino.cc"); 91 | client.println("User-Agent: arduino-ethernet"); 92 | client.println("Connection: close"); 93 | client.println(); 94 | 95 | // note the time that the connection was made: 96 | lastConnectionTime = millis(); 97 | } else { 98 | // if you couldn't make a connection: 99 | Serial.println("connection failed"); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /examples/WebServer/WebServer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Web Server 3 | 4 | A simple web server that shows the value of the analog input pins. 5 | 6 | Circuit: 7 | * STM32 board with Ethernet support 8 | * Analog inputs attached to pins A0 through A5 (optional) 9 | 10 | created 18 Dec 2009 11 | by David A. Mellis 12 | modified 9 Apr 2012 13 | by Tom Igoe 14 | modified 02 Sept 2015 15 | by Arturo Guadalupi 16 | modified 23 Jun 2017 17 | by Wi6Labs 18 | modified 1 Jun 2018 19 | by sstaub 20 | */ 21 | 22 | #include 23 | #include 24 | 25 | // Enter an IP address for your controller below. 26 | // The IP address will be dependent on your local network: 27 | IPAddress ip(192, 168, 1, 177); 28 | 29 | // Initialize the Ethernet server library 30 | // with the IP address and port you want to use 31 | // (port 80 is default for HTTP): 32 | EthernetServer server(80); 33 | 34 | void setup() { 35 | // Open serial communications and wait for port to open: 36 | Serial.begin(9600); 37 | while (!Serial) { 38 | ; // wait for serial port to connect. Needed for native USB port only 39 | } 40 | 41 | 42 | // start the Ethernet connection and the server: 43 | Ethernet.begin(ip); 44 | server.begin(); 45 | Serial.print("server is at "); 46 | Serial.println(Ethernet.localIP()); 47 | } 48 | 49 | 50 | void loop() { 51 | // listen for incoming clients 52 | EthernetClient client = server.available(); 53 | if (client) { 54 | Serial.println("new client"); 55 | // an http request ends with a blank line 56 | bool currentLineIsBlank = true; 57 | while (client.connected()) { 58 | if (client.available()) { 59 | char c = client.read(); 60 | Serial.write(c); 61 | // if you've gotten to the end of the line (received a newline 62 | // character) and the line is blank, the http request has ended, 63 | // so you can send a reply 64 | if (c == '\n' && currentLineIsBlank) { 65 | // send a standard http response header 66 | client.println("HTTP/1.1 200 OK"); 67 | client.println("Content-Type: text/html"); 68 | client.println("Connection: close"); // the connection will be closed after completion of the response 69 | client.println("Refresh: 5"); // refresh the page automatically every 5 sec 70 | client.println(); 71 | client.println(""); 72 | client.println(""); 73 | // output the value of each analog input pin 74 | for (int analogChannel = 0; analogChannel < 6; analogChannel++) { 75 | int sensorReading = analogRead(analogChannel); 76 | client.print("analog input "); 77 | client.print(analogChannel); 78 | client.print(" is "); 79 | client.print(sensorReading); 80 | client.println("
"); 81 | } 82 | client.println(""); 83 | break; 84 | } 85 | if (c == '\n') { 86 | // you're starting a new line 87 | currentLineIsBlank = true; 88 | } else if (c != '\r') { 89 | // you've gotten a character on the current line 90 | currentLineIsBlank = false; 91 | } 92 | } 93 | } 94 | // give the web browser time to receive the data 95 | delay(1); 96 | // close the connection: 97 | client.stop(); 98 | Serial.println("client disconnected"); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /examples/WebServerFreeRTOS/WebServerFreeRTOS.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Port of WebServer on FreeRTOS 3 | 4 | A simple web server that shows the value of the analog input pins. 5 | 2 task are created: 6 | Ethernet: to manage the server 7 | Analog to read input values 8 | 9 | Circuit: 10 | STM32 board with Ethernet support 11 | Analog inputs attached to pins A0 through A5 (optional) 12 | 13 | created 18 Dec 2009 14 | by David A. Mellis 15 | modified 9 Apr 2012 16 | by Tom Igoe 17 | modified 02 Sept 2015 18 | by Arturo Guadalupi 19 | modified 23 Jun 2017 20 | by Wi6Labs 21 | modified 1 Jun 2018 22 | by sstaub 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | // Enter an IP address for your controller below. 30 | // The IP address will be dependent on your local network: 31 | IPAddress ip(192, 168, 0, 177); 32 | 33 | // Initialize the Ethernet server library 34 | // with the IP address and port you want to use 35 | // (port 80 is default for HTTP): 36 | EthernetServer server(80); 37 | 38 | #define ANALOG_CHANEL_NUMBER 6 39 | int sensorReading[ANALOG_CHANEL_NUMBER]; 40 | 41 | void taskAnalog(void* arg) { 42 | UNUSED(arg); 43 | while (1) { 44 | for (int analogChannel = 0; analogChannel < ANALOG_CHANEL_NUMBER; analogChannel++) { 45 | sensorReading[analogChannel] = analogRead(analogChannel); 46 | } 47 | vTaskDelay(1000); // read Analog every seconds 48 | } 49 | } 50 | 51 | // task code 52 | void taskETH(void* arg) { 53 | UNUSED(arg); 54 | // start the Ethernet connection and the server: 55 | Ethernet.begin(ip); 56 | server.begin(); 57 | Serial.print("server is at "); 58 | Serial.println(Ethernet.localIP()); 59 | 60 | while (1) { 61 | // listen for incoming clients 62 | EthernetClient client = server.available(); 63 | if (client) { 64 | Serial.println("new client"); 65 | // an http request ends with a blank line 66 | bool currentLineIsBlank = true; 67 | while (client.connected()) { 68 | if (client.available()) { 69 | char c = client.read(); 70 | Serial.write(c); 71 | // if you've gotten to the end of the line (received a newline 72 | // character) and the line is blank, the http request has ended, 73 | // so you can send a reply 74 | if (c == '\n' && currentLineIsBlank) { 75 | // send a standard http response header 76 | client.println("HTTP/1.1 200 OK"); 77 | client.println("Content-Type: text/html"); 78 | client.println("Connection: close"); // the connection will be closed after completion of the response 79 | client.println("Refresh: 5"); // refresh the page automatically every 5 sec 80 | client.println(); 81 | client.println(""); 82 | client.println(""); 83 | // output the value of each analog input pin 84 | for (int analogChannel = 0; analogChannel < ANALOG_CHANEL_NUMBER; analogChannel++) { 85 | client.print("analog input "); 86 | client.print(analogChannel); 87 | client.print(" is "); 88 | client.print(sensorReading[analogChannel]); 89 | client.println("
"); 90 | } 91 | client.println(""); 92 | break; 93 | } 94 | if (c == '\n') { 95 | // you're starting a new line 96 | currentLineIsBlank = true; 97 | } else if (c != '\r') { 98 | // you've gotten a character on the current line 99 | currentLineIsBlank = false; 100 | } 101 | } 102 | } 103 | // give the web browser time to receive the data 104 | delay(1); 105 | // close the connection: 106 | client.stop(); 107 | Serial.println("client disconnected"); 108 | } 109 | } 110 | } 111 | 112 | void setup() { 113 | // Open serial communications and wait for port to open: 114 | Serial.begin(9600); 115 | while (!Serial) { 116 | ; // wait for serial port to connect. Needed for native USB port only 117 | } 118 | 119 | portBASE_TYPE s = xTaskCreate(taskETH, NULL, 200, NULL, 1, NULL); 120 | if (s != pdPASS) { 121 | printf("Ethernet task creation failed\n"); 122 | while (1); 123 | } 124 | 125 | s = xTaskCreate(taskAnalog, NULL, 200, NULL, 2, NULL); 126 | if (s != pdPASS) { 127 | printf("Analog task creation failed\n"); 128 | while (1); 129 | } 130 | 131 | vTaskStartScheduler(); 132 | Serial.println("Scheduler failed"); 133 | while (1); 134 | } 135 | 136 | 137 | void loop() { 138 | // Not used. 139 | } -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For Ethernet 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | Ethernet KEYWORD1 Ethernet 10 | EthernetClient KEYWORD1 EthernetClient 11 | EthernetServer KEYWORD1 EthernetServer 12 | IPAddress KEYWORD1 EthernetIPAddress 13 | 14 | ####################################### 15 | # Methods and Functions (KEYWORD2) 16 | ####################################### 17 | 18 | status KEYWORD2 19 | connect KEYWORD2 20 | write KEYWORD2 21 | available KEYWORD2 22 | read KEYWORD2 23 | peek KEYWORD2 24 | flush KEYWORD2 25 | stop KEYWORD2 26 | connected KEYWORD2 27 | accept KEYWORD2 28 | begin KEYWORD2 29 | beginMulticast KEYWORD2 30 | beginPacket KEYWORD2 31 | endPacket KEYWORD2 32 | parsePacket KEYWORD2 33 | remoteIP KEYWORD2 34 | remotePort KEYWORD2 35 | getSocketNumber KEYWORD2 36 | localIP KEYWORD2 37 | localPort KEYWORD2 38 | maintain KEYWORD2 39 | linkStatus KEYWORD2 40 | MACAddress KEYWORD2 41 | setMACAddress KEYWORD2 42 | subnetMask KEYWORD2 43 | gatewayIP KEYWORD2 44 | dnsServerIP KEYWORD2 45 | setDnsServerIP KEYWORD2 46 | setConnectionTimeout KEYWORD2 47 | 48 | ####################################### 49 | # Constants (LITERAL1) 50 | ####################################### 51 | LinkON LITERAL1 52 | LinkOFF LITERAL1 53 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "STM32Ethernet", 3 | "keywords": "Ethernet", 4 | "description": "Arduino library to support Ethernet for STM32 based board.", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/stm32duino/STM32Ethernet" 9 | }, 10 | "version": "1.4.0", 11 | "frameworks": "arduino", 12 | "platforms": "ststm32", 13 | "build": { 14 | "libArchive": false 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=STM32duino STM32Ethernet 2 | version=1.4.0 3 | author=Various 4 | maintainer=STMicroelectronics 5 | sentence=Enables network connection (local and Internet) using the STM32 Board. 6 | 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. 7 | category=Communication 8 | url=https://github.com/stm32duino/STM32Ethernet 9 | architectures=stm32 10 | depends=STM32duino LwIP 11 | -------------------------------------------------------------------------------- /src/Dhcp.cpp: -------------------------------------------------------------------------------- 1 | // DHCP Library v0.3 - April 25, 2009 2 | // Author: Jordan Terrell - blog.jordanterrell.com 3 | 4 | #include 5 | #include 6 | #include "Dhcp.h" 7 | #include "Arduino.h" 8 | #include "utility/stm32_eth.h" 9 | 10 | int DhcpClass::beginWithDHCP(uint8_t *mac, unsigned long timeout, unsigned long responseTimeout) 11 | { 12 | UNUSED(responseTimeout); 13 | _timeout = timeout; 14 | _dhcp_lease_state = DHCP_CHECK_NONE; 15 | 16 | // zero out _dhcpMacAddr 17 | memset(_dhcpMacAddr, 0, 6); 18 | reset_DHCP_lease(); 19 | if (mac == NULL) { 20 | // use mac from Ethernet chip 21 | stm32_eth_get_macaddr(_dhcpMacAddr); 22 | } else { 23 | memcpy((void *)_dhcpMacAddr, (void *)mac, 6); 24 | } 25 | _dhcp_state = STATE_DHCP_START; 26 | stm32_set_DHCP_state(_dhcp_state); 27 | return request_DHCP_lease(); 28 | } 29 | 30 | void DhcpClass::reset_DHCP_lease() 31 | { 32 | _dhcp_state = STATE_DHCP_RELEASE; 33 | stm32_set_DHCP_state(_dhcp_state); 34 | stm32_eth_scheduler(); 35 | _dhcp_state = stm32_get_DHCP_state(); 36 | } 37 | 38 | //return:0 on error, 1 if request is sent and response is received 39 | int DhcpClass::request_DHCP_lease() 40 | { 41 | 42 | int result = 0; 43 | unsigned long startTime = millis(); 44 | 45 | while (_dhcp_state != STATE_DHCP_LEASED) { 46 | stm32_eth_scheduler(); 47 | _dhcp_state = stm32_get_DHCP_state(); 48 | 49 | if (result != 1 && ((millis() - startTime) > _timeout)) { 50 | reset_DHCP_lease(); 51 | break; 52 | } 53 | } 54 | 55 | if (_dhcp_state == STATE_DHCP_LEASED) { 56 | result = 1; 57 | } 58 | 59 | return result; 60 | } 61 | 62 | /* 63 | returns: 64 | 0/DHCP_CHECK_NONE: nothing happened 65 | 1/DHCP_CHECK_RENEW_FAIL: renew failed 66 | 2/DHCP_CHECK_RENEW_OK: renew success 67 | 3/DHCP_CHECK_REBIND_FAIL: rebind fail 68 | 4/DHCP_CHECK_REBIND_OK: rebind success 69 | */ 70 | int DhcpClass::checkLease() 71 | { 72 | int rc = DHCP_CHECK_NONE; 73 | 74 | stm32_eth_scheduler(); 75 | rc = stm32_get_DHCP_lease_state(); 76 | 77 | if (rc != _dhcp_lease_state) { 78 | switch (_dhcp_lease_state) { 79 | case DHCP_CHECK_NONE: 80 | _dhcp_lease_state = rc; 81 | rc = DHCP_CHECK_NONE; 82 | break; 83 | 84 | case DHCP_CHECK_RENEW_OK: 85 | _dhcp_lease_state = rc; 86 | if (rc == DHCP_CHECK_NONE) { 87 | rc = DHCP_CHECK_RENEW_OK; 88 | } else { 89 | rc = DHCP_CHECK_RENEW_FAIL; 90 | } 91 | break; 92 | 93 | case DHCP_CHECK_REBIND_OK: 94 | _dhcp_lease_state = rc; 95 | if (rc == DHCP_CHECK_NONE) { 96 | rc = DHCP_CHECK_REBIND_OK; 97 | } else { 98 | rc = DHCP_CHECK_REBIND_FAIL; 99 | } 100 | break; 101 | 102 | default: 103 | _dhcp_lease_state = DHCP_CHECK_NONE; 104 | break; 105 | } 106 | } 107 | 108 | return rc; 109 | } 110 | 111 | IPAddress DhcpClass::getLocalIp() 112 | { 113 | return IPAddress(stm32_eth_get_ipaddr()); 114 | } 115 | 116 | IPAddress DhcpClass::getSubnetMask() 117 | { 118 | return IPAddress(stm32_eth_get_netmaskaddr()); 119 | } 120 | 121 | IPAddress DhcpClass::getGatewayIp() 122 | { 123 | return IPAddress(stm32_eth_get_gwaddr()); 124 | } 125 | 126 | IPAddress DhcpClass::getDhcpServerIp() 127 | { 128 | return IPAddress(stm32_eth_get_dhcpaddr()); 129 | } 130 | 131 | IPAddress DhcpClass::getDnsServerIp() 132 | { 133 | return IPAddress(stm32_eth_get_dnsaddr()); 134 | } 135 | -------------------------------------------------------------------------------- /src/Dhcp.h: -------------------------------------------------------------------------------- 1 | // DHCP Library v0.3 - April 25, 2009 2 | // Author: Jordan Terrell - blog.jordanterrell.com 3 | 4 | #ifndef Dhcp_h 5 | #define Dhcp_h 6 | 7 | #include "EthernetUdp.h" 8 | 9 | /* DHCP state machine. */ 10 | #define STATE_DHCP_STOP DHCP_OFF 11 | #define STATE_DHCP_START DHCP_START 12 | #define STATE_DHCP_DISCOVER DHCP_WAIT_ADDRESS 13 | #define STATE_DHCP_REQUEST 0 14 | #define STATE_DHCP_LEASED DHCP_ADDRESS_ASSIGNED 15 | #define STATE_DHCP_REREQUEST 0 16 | #define STATE_DHCP_RELEASE DHCP_ASK_RELEASE 17 | 18 | #define DHCP_CHECK_NONE (0) 19 | #define DHCP_CHECK_RENEW_FAIL (1) 20 | #define DHCP_CHECK_RENEW_OK (2) 21 | #define DHCP_CHECK_REBIND_FAIL (3) 22 | #define DHCP_CHECK_REBIND_OK (4) 23 | 24 | class DhcpClass { 25 | private: 26 | uint8_t _dhcpMacAddr[6]; 27 | unsigned long _timeout; 28 | uint8_t _dhcp_lease_state; 29 | uint8_t _dhcp_state; 30 | 31 | int request_DHCP_lease(); 32 | void reset_DHCP_lease(); 33 | 34 | public: 35 | IPAddress getLocalIp(); 36 | IPAddress getSubnetMask(); 37 | IPAddress getGatewayIp(); 38 | IPAddress getDhcpServerIp(); 39 | IPAddress getDnsServerIp(); 40 | 41 | int beginWithDHCP(uint8_t *, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); 42 | int checkLease(); 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /src/Dns.cpp: -------------------------------------------------------------------------------- 1 | // Arduino DNS client for WizNet5100-based Ethernet shield 2 | // (c) Copyright 2009-2010 MCQN Ltd. 3 | // Released under Apache License, version 2.0 4 | 5 | #include "EthernetUdp.h" 6 | #include "utility/stm32_eth.h" 7 | 8 | #include "Dns.h" 9 | #include 10 | #include "Arduino.h" 11 | 12 | // Possible return codes from ProcessResponse 13 | #define SUCCESS 1 14 | #define TIMED_OUT -1 15 | #define INVALID_SERVER -2 16 | #define TRUNCATED -3 17 | #define INVALID_RESPONSE -4 18 | 19 | void DNSClient::begin(const IPAddress &aDNSServer) 20 | { 21 | iDNSServer = aDNSServer; 22 | stm32_dns_init(iDNSServer.raw_address()); 23 | } 24 | 25 | 26 | int DNSClient::inet_aton(const char *address, IPAddress &result) 27 | { 28 | uint16_t acc = 0; // Accumulator 29 | uint8_t dots = 0; 30 | 31 | if (address == NULL) { 32 | return 0; 33 | } 34 | 35 | while (*address) { 36 | char c = *address++; 37 | if (c >= '0' && c <= '9') { 38 | acc = acc * 10 + (c - '0'); 39 | if (acc > 255) { 40 | // Value out of [0..255] range 41 | return 0; 42 | } 43 | } else if (c == '.') { 44 | if (dots == 3) { 45 | // Too much dots (there must be 3 dots) 46 | return 0; 47 | } 48 | result[dots++] = acc; 49 | acc = 0; 50 | } else { 51 | // Invalid char 52 | return 0; 53 | } 54 | } 55 | 56 | if (dots != 3) { 57 | // Too few dots (there must be 3 dots) 58 | return 0; 59 | } 60 | result[3] = acc; 61 | return 1; 62 | } 63 | 64 | int DNSClient::getHostByName(const char *aHostname, IPAddress &aResult) 65 | { 66 | int ret = 0; 67 | uint32_t ipResult = 0; 68 | 69 | // See if it's a numeric IP address 70 | if (inet_aton(aHostname, aResult)) { 71 | // It is, our work here is done 72 | return SUCCESS; 73 | } 74 | 75 | // Check we've got a valid DNS server to use 76 | if (iDNSServer == INADDR_NONE) { 77 | return INVALID_SERVER; 78 | } 79 | 80 | ret = stm32_dns_gethostbyname(aHostname, &ipResult); 81 | aResult = IPAddress(ipResult); 82 | 83 | return ret; 84 | } 85 | 86 | /* Deprecated function. Do not use anymore. */ 87 | uint16_t DNSClient::BuildRequest(const char *aName) 88 | { 89 | UNUSED(aName); 90 | return 0; 91 | } 92 | 93 | /* Deprecated function. Do not use anymore. */ 94 | uint16_t DNSClient::ProcessResponse(uint16_t aTimeout, IPAddress &aAddress) 95 | { 96 | UNUSED(aTimeout); 97 | UNUSED(aAddress); 98 | // If we get here then we haven't found an answer 99 | return -10;//INVALID_RESPONSE; 100 | } 101 | -------------------------------------------------------------------------------- /src/Dns.h: -------------------------------------------------------------------------------- 1 | // Arduino DNS client for WizNet5100-based Ethernet shield 2 | // (c) Copyright 2009-2010 MCQN Ltd. 3 | // Released under Apache License, version 2.0 4 | 5 | #ifndef DNSClient_h 6 | #define DNSClient_h 7 | 8 | #include 9 | 10 | class DNSClient { 11 | public: 12 | // ctor 13 | void begin(const IPAddress &aDNSServer); 14 | 15 | /** Convert a numeric IP address string into a four-byte IP address. 16 | @param aIPAddrString IP address to convert 17 | @param aResult IPAddress structure to store the returned IP address 18 | @result 1 if aIPAddrString was successfully converted to an IP address, 19 | else error code 20 | */ 21 | int inet_aton(const char *aIPAddrString, IPAddress &aResult); 22 | 23 | /** Resolve the given hostname to an IP address. 24 | @param aHostname Name to be resolved 25 | @param aResult IPAddress structure to store the returned IP address 26 | @result 1 if aIPAddrString was successfully converted to an IP address, 27 | else error code 28 | */ 29 | int getHostByName(const char *aHostname, IPAddress &aResult); 30 | 31 | protected: 32 | uint16_t BuildRequest(const char *aName); 33 | uint16_t ProcessResponse(uint16_t aTimeout, IPAddress &aAddress); 34 | 35 | IPAddress iDNSServer; 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /src/EthernetClient.cpp: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | #include "string.h" 3 | } 4 | 5 | #include "Arduino.h" 6 | 7 | #include "STM32Ethernet.h" 8 | #include "EthernetClient.h" 9 | #include "EthernetServer.h" 10 | #include "Dns.h" 11 | 12 | EthernetClient::EthernetClient() 13 | : _tcp_client(NULL) 14 | { 15 | } 16 | 17 | /* Deprecated constructor. Keeps compatibility with W5100 architecture 18 | sketches but sock is ignored. */ 19 | EthernetClient::EthernetClient(uint8_t sock) 20 | : _tcp_client(NULL) 21 | { 22 | UNUSED(sock); 23 | } 24 | 25 | EthernetClient::EthernetClient(struct tcp_struct *tcpClient) 26 | { 27 | _tcp_client = tcpClient; 28 | } 29 | 30 | int EthernetClient::connect(const char *host, uint16_t port) 31 | { 32 | // Look up the host first 33 | int ret = 0; 34 | DNSClient dns; 35 | IPAddress remote_addr; 36 | 37 | dns.begin(Ethernet.dnsServerIP()); 38 | ret = dns.getHostByName(host, remote_addr); 39 | if (ret == 1) { 40 | return connect(remote_addr, port); 41 | } else { 42 | return 0; 43 | } 44 | } 45 | 46 | int EthernetClient::connect(IPAddress ip, uint16_t port) 47 | { 48 | if (_tcp_client == NULL) { 49 | /* Allocates memory for client */ 50 | _tcp_client = (struct tcp_struct *)mem_malloc(sizeof(struct tcp_struct)); 51 | 52 | if (_tcp_client == NULL) { 53 | return 0; 54 | } 55 | } 56 | 57 | /* Creates a new TCP protocol control block */ 58 | _tcp_client->pcb = tcp_new(); 59 | 60 | if (_tcp_client->pcb == NULL) { 61 | return 0; 62 | } 63 | 64 | _tcp_client->data.p = NULL; 65 | _tcp_client->data.available = 0; 66 | _tcp_client->state = TCP_NONE; 67 | 68 | uint32_t startTime = millis(); 69 | ip_addr_t ipaddr; 70 | tcp_arg(_tcp_client->pcb, _tcp_client); 71 | if (ERR_OK != tcp_connect(_tcp_client->pcb, u8_to_ip_addr(rawIPAddress(ip), &ipaddr), port, &tcp_connected_callback)) { 72 | stop(); 73 | return 0; 74 | } 75 | 76 | startTime = millis(); 77 | while (_tcp_client->state == TCP_NONE) { 78 | stm32_eth_scheduler(); 79 | if ((_tcp_client->state == TCP_CLOSING) || ((millis() - startTime) >= _connectionTimeout)) { 80 | stop(); 81 | return 0; 82 | } 83 | } 84 | 85 | return 1; 86 | } 87 | 88 | size_t EthernetClient::write(uint8_t b) 89 | { 90 | return write(&b, 1); 91 | } 92 | 93 | size_t EthernetClient::write(const uint8_t *buf, size_t size) 94 | { 95 | if ((_tcp_client == NULL) || (_tcp_client->pcb == NULL) || 96 | (buf == NULL) || (size == 0)) { 97 | return 0; 98 | } 99 | 100 | /* If client not connected or accepted, it can't write because connection is 101 | not ready */ 102 | if ((_tcp_client->state != TCP_ACCEPTED) && 103 | (_tcp_client->state != TCP_CONNECTED)) { 104 | return 0; 105 | } 106 | 107 | size_t max_send_size, bytes_to_send; 108 | size_t bytes_sent = 0; 109 | size_t bytes_left = size; 110 | err_t res; 111 | 112 | do { 113 | max_send_size = tcp_sndbuf(_tcp_client->pcb); 114 | bytes_to_send = bytes_left > max_send_size ? max_send_size : bytes_left; 115 | 116 | if (bytes_to_send > 0) { 117 | res = tcp_write(_tcp_client->pcb, &buf[bytes_sent], bytes_to_send, TCP_WRITE_FLAG_COPY); 118 | 119 | if (res == ERR_OK) { 120 | bytes_sent += bytes_to_send; 121 | bytes_left = size - bytes_sent; 122 | } else if (res != ERR_MEM) { 123 | // other error, cannot continue 124 | return 0; 125 | } 126 | } 127 | 128 | //Force to send data right now! 129 | if (ERR_OK != tcp_output(_tcp_client->pcb)) { 130 | return 0; 131 | } 132 | stm32_eth_scheduler(); 133 | 134 | } while (bytes_sent != size); 135 | 136 | return size; 137 | } 138 | 139 | int EthernetClient::available() 140 | { 141 | stm32_eth_scheduler(); 142 | if (_tcp_client != NULL) { 143 | return _tcp_client->data.available; 144 | } 145 | return 0; 146 | } 147 | 148 | int EthernetClient::read() 149 | { 150 | uint8_t b; 151 | if ((_tcp_client != NULL) && (_tcp_client->data.p != NULL)) { 152 | stm32_get_data(&(_tcp_client->data), &b, 1); 153 | return b; 154 | } 155 | // No data available 156 | return -1; 157 | } 158 | 159 | int EthernetClient::read(uint8_t *buf, size_t size) 160 | { 161 | if ((_tcp_client != NULL) && (_tcp_client->data.p != NULL)) { 162 | return stm32_get_data(&(_tcp_client->data), buf, size); 163 | } 164 | return -1; 165 | } 166 | 167 | int EthernetClient::peek() 168 | { 169 | uint8_t b; 170 | // Unlike recv, peek doesn't check to see if there's any data available, so we must 171 | if (!available()) { 172 | return -1; 173 | } 174 | b = pbuf_get_at(_tcp_client->data.p, _tcp_client->data.p->tot_len - _tcp_client->data.available); 175 | return b; 176 | } 177 | 178 | void EthernetClient::flush() 179 | { 180 | if ((_tcp_client == NULL) || (_tcp_client->pcb == NULL)) { 181 | return; 182 | } 183 | tcp_output(_tcp_client->pcb); 184 | stm32_eth_scheduler(); 185 | } 186 | 187 | void EthernetClient::stop() 188 | { 189 | if (_tcp_client == NULL) { 190 | return; 191 | } 192 | 193 | // close tcp connection if not closed yet 194 | if (status() != TCP_CLOSING) { 195 | tcp_connection_close(_tcp_client->pcb, _tcp_client); 196 | } 197 | mem_free(_tcp_client); 198 | _tcp_client = NULL; 199 | } 200 | 201 | uint8_t EthernetClient::connected() 202 | { 203 | uint8_t s = status(); 204 | return ((available() && (s == TCP_CLOSING)) || 205 | (s == TCP_CONNECTED) || (s == TCP_ACCEPTED)); 206 | } 207 | 208 | uint8_t EthernetClient::status() 209 | { 210 | if (_tcp_client == NULL) { 211 | return TCP_NONE; 212 | } 213 | return _tcp_client->state; 214 | } 215 | 216 | // the next function allows us to use the client returned by 217 | // EthernetServer::available() as the condition in an if-statement. 218 | 219 | EthernetClient::operator bool() 220 | { 221 | return (_tcp_client && (_tcp_client->state != TCP_CLOSING)); 222 | } 223 | 224 | bool EthernetClient::operator==(const EthernetClient &rhs) 225 | { 226 | return _tcp_client == rhs._tcp_client && _tcp_client->pcb == rhs._tcp_client->pcb; 227 | } 228 | 229 | /* This function is not a function defined by Arduino. This is a function 230 | specific to the W5100 architecture. To keep the compatibility we leave it and 231 | returns always 0. */ 232 | uint8_t EthernetClient::getSocketNumber() 233 | { 234 | return 0; 235 | } 236 | -------------------------------------------------------------------------------- /src/EthernetClient.h: -------------------------------------------------------------------------------- 1 | #ifndef ethernetclient_h 2 | #define ethernetclient_h 3 | #include "Arduino.h" 4 | #include "Print.h" 5 | #include "Client.h" 6 | #include "IPAddress.h" 7 | #include "utility/stm32_eth.h" 8 | 9 | class EthernetClient : public Client { 10 | 11 | public: 12 | EthernetClient(); 13 | EthernetClient(uint8_t sock); 14 | EthernetClient(struct tcp_struct *tcpClient); 15 | 16 | uint8_t status(); 17 | virtual int connect(IPAddress ip, uint16_t port); 18 | virtual int connect(const char *host, uint16_t port); 19 | virtual size_t write(uint8_t); 20 | virtual size_t write(const uint8_t *buf, size_t size); 21 | virtual int available(); 22 | virtual int read(); 23 | virtual int read(uint8_t *buf, size_t size); 24 | virtual int peek(); 25 | virtual void flush(); 26 | virtual void stop(); 27 | virtual uint8_t connected(); 28 | virtual operator bool(); 29 | virtual bool operator==(const bool value) 30 | { 31 | return bool() == value; 32 | } 33 | virtual bool operator!=(const bool value) 34 | { 35 | return bool() != value; 36 | } 37 | virtual bool operator==(const EthernetClient &); 38 | virtual bool operator!=(const EthernetClient &rhs) 39 | { 40 | return !this->operator==(rhs); 41 | }; 42 | uint8_t getSocketNumber(); 43 | virtual uint16_t localPort() 44 | { 45 | return (_tcp_client->pcb->local_port); 46 | }; 47 | virtual IPAddress remoteIP() 48 | { 49 | return (IPAddress(_tcp_client->pcb->remote_ip.addr)); 50 | }; 51 | virtual uint16_t remotePort() 52 | { 53 | return (_tcp_client->pcb->remote_port); 54 | }; 55 | void setConnectionTimeout(uint16_t timeout) 56 | { 57 | _connectionTimeout = timeout; 58 | } 59 | 60 | friend class EthernetServer; 61 | 62 | using Print::write; 63 | 64 | private: 65 | struct tcp_struct *_tcp_client; 66 | uint16_t _connectionTimeout = 10000; 67 | }; 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /src/EthernetServer.cpp: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | #include "string.h" 3 | } 4 | 5 | #include "STM32Ethernet.h" 6 | #include "EthernetClient.h" 7 | #include "EthernetServer.h" 8 | 9 | EthernetServer::EthernetServer(uint16_t port) 10 | { 11 | _port = port; 12 | for (int i = 0; i < MAX_CLIENT; i++) { 13 | _tcp_client[i] = {}; 14 | } 15 | _tcp_server = {}; 16 | } 17 | 18 | void EthernetServer::begin() 19 | { 20 | if (_tcp_server.pcb != NULL) { 21 | return; 22 | } 23 | 24 | _tcp_server.pcb = tcp_new(); 25 | 26 | if (_tcp_server.pcb == NULL) { 27 | return; 28 | } 29 | 30 | tcp_arg(_tcp_server.pcb, &_tcp_client); 31 | _tcp_server.state = TCP_NONE; 32 | 33 | if (ERR_OK != tcp_bind(_tcp_server.pcb, IP_ADDR_ANY, _port)) { 34 | memp_free(MEMP_TCP_PCB, _tcp_server.pcb); 35 | _tcp_server.pcb = NULL; 36 | return; 37 | } 38 | 39 | _tcp_server.pcb = tcp_listen(_tcp_server.pcb); 40 | tcp_accept(_tcp_server.pcb, tcp_accept_callback); 41 | } 42 | 43 | void EthernetServer::begin(uint16_t port) 44 | { 45 | _port = port; 46 | begin(); 47 | } 48 | 49 | void EthernetServer::end(void) 50 | { 51 | /* Free client */ 52 | for (int n = 0; n < MAX_CLIENT; n++) { 53 | if (_tcp_client[n] != NULL) { 54 | EthernetClient client(_tcp_client[n]); 55 | client.stop(); 56 | _tcp_client[n] = NULL; 57 | } 58 | } 59 | if (_tcp_server.pcb != NULL) { 60 | tcp_close(_tcp_server.pcb); 61 | _tcp_server.pcb = NULL; 62 | } 63 | } 64 | 65 | void EthernetServer::accept() 66 | { 67 | /* Free client if disconnected */ 68 | for (int n = 0; n < MAX_CLIENT; n++) { 69 | if (_tcp_client[n] != NULL) { 70 | EthernetClient client(_tcp_client[n]); 71 | if (client.status() == TCP_CLOSING) { 72 | mem_free(_tcp_client[n]); 73 | _tcp_client[n] = NULL; 74 | } 75 | } 76 | } 77 | } 78 | 79 | EthernetClient EthernetServer::available() 80 | { 81 | accept(); 82 | 83 | for (int n = 0; n < MAX_CLIENT; n++) { 84 | if (_tcp_client[n] != NULL) { 85 | if (_tcp_client[n]->pcb != NULL) { 86 | EthernetClient client(_tcp_client[n]); 87 | uint8_t s = client.status(); 88 | if (s == TCP_ACCEPTED) { 89 | if (client.available()) { 90 | return client; 91 | } 92 | } 93 | } 94 | } 95 | } 96 | 97 | struct tcp_struct *default_client = NULL; 98 | return EthernetClient(default_client); 99 | } 100 | 101 | size_t EthernetServer::write(uint8_t b) 102 | { 103 | return write(&b, 1); 104 | } 105 | 106 | size_t EthernetServer::write(const uint8_t *buffer, size_t size) 107 | { 108 | size_t n = 0; 109 | 110 | accept(); 111 | 112 | for (int i = 0; i < MAX_CLIENT; i++) { 113 | if (_tcp_client[i] != NULL) { 114 | if (_tcp_client[i]->pcb != NULL) { 115 | EthernetClient client(_tcp_client[i]); 116 | uint8_t s = client.status(); 117 | if (s == TCP_ACCEPTED) { 118 | n += client.write(buffer, size); 119 | } 120 | } 121 | } 122 | } 123 | 124 | return n; 125 | } 126 | 127 | EthernetServer::operator bool() 128 | { 129 | // server is listening for incoming clients 130 | return ((_tcp_server.pcb != NULL) && (_tcp_server.pcb->state == LISTEN)); 131 | } 132 | -------------------------------------------------------------------------------- /src/EthernetServer.h: -------------------------------------------------------------------------------- 1 | #ifndef ethernetserver_h 2 | #define ethernetserver_h 3 | 4 | #include "Server.h" 5 | 6 | class EthernetClient; 7 | 8 | class EthernetServer : 9 | public Server { 10 | private: 11 | uint16_t _port; 12 | struct tcp_struct _tcp_server; 13 | struct tcp_struct *_tcp_client[MAX_CLIENT]; 14 | 15 | void accept(void); 16 | public: 17 | EthernetServer(uint16_t port = 80); 18 | EthernetClient available(); 19 | virtual void begin(); 20 | virtual void begin(uint16_t port); 21 | void end(void); 22 | virtual size_t write(uint8_t); 23 | virtual size_t write(const uint8_t *buf, size_t size); 24 | virtual operator bool(); 25 | using Print::write; 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /src/EthernetUdp.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield. 3 | * This version only offers minimal wrapping of socket.c/socket.h 4 | * Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ 5 | * 6 | * MIT License: 7 | * Copyright (c) 2008 Bjoern Hartmann 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | * 26 | * bjoern@cs.stanford.edu 12/30/2008 27 | */ 28 | #include "STM32Ethernet.h" 29 | #include "Udp.h" 30 | #include "Dns.h" 31 | 32 | #include "lwip/igmp.h" 33 | #include "lwip/ip_addr.h" 34 | 35 | /* Constructor */ 36 | EthernetUDP::EthernetUDP() {} 37 | 38 | /* Start EthernetUDP socket, listening at local port PORT */ 39 | uint8_t EthernetUDP::begin(uint16_t port) 40 | { 41 | return begin(Ethernet.localIP(), port); 42 | } 43 | 44 | /* Start EthernetUDP socket, listening at local IP ip and port PORT */ 45 | uint8_t EthernetUDP::begin(IPAddress ip, uint16_t port, bool multicast) 46 | { 47 | // Can create a single udp connection per socket 48 | if (_udp.pcb != NULL) { 49 | return 0; 50 | } 51 | 52 | _udp.pcb = udp_new(); 53 | 54 | if (_udp.pcb == NULL) { 55 | return 0; 56 | } 57 | 58 | ip_addr_t ipaddr; 59 | err_t err; 60 | u8_to_ip_addr(rawIPAddress(ip), &ipaddr); 61 | if (multicast) { 62 | err = udp_bind(_udp.pcb, IP_ADDR_ANY, port); 63 | } else { 64 | err = udp_bind(_udp.pcb, &ipaddr, port); 65 | } 66 | 67 | if (ERR_OK != err) { 68 | stop(); 69 | return 0; 70 | } 71 | 72 | #if LWIP_IGMP 73 | if ((multicast) && (ERR_OK != igmp_joingroup(IP_ADDR_ANY, &ipaddr))) { 74 | return 0; 75 | } 76 | #endif 77 | udp_recv(_udp.pcb, &udp_receive_callback, &_udp); 78 | 79 | _port = port; 80 | _remaining = 0; 81 | 82 | stm32_eth_scheduler(); 83 | 84 | return 1; 85 | } 86 | 87 | /* return number of bytes available in the current packet, 88 | will return zero if parsePacket hasn't been called yet */ 89 | int EthernetUDP::available() 90 | { 91 | return _remaining; 92 | } 93 | 94 | /* Release any resources being used by this EthernetUDP instance */ 95 | void EthernetUDP::stop() 96 | { 97 | if (_udp.pcb != NULL) { 98 | udp_disconnect(_udp.pcb); 99 | udp_remove(_udp.pcb); 100 | _udp.pcb = NULL; 101 | } 102 | 103 | stm32_eth_scheduler(); 104 | } 105 | 106 | int EthernetUDP::beginPacket(const char *host, uint16_t port) 107 | { 108 | // Look up the host first 109 | int ret = 0; 110 | DNSClient dns; 111 | IPAddress remote_addr; 112 | 113 | dns.begin(Ethernet.dnsServerIP()); 114 | ret = dns.getHostByName(host, remote_addr); 115 | if (ret == 1) { 116 | return beginPacket(remote_addr, port); 117 | } else { 118 | return ret; 119 | } 120 | } 121 | 122 | int EthernetUDP::beginPacket(IPAddress ip, uint16_t port) 123 | { 124 | if (_udp.pcb == NULL) { 125 | return 0; 126 | } 127 | 128 | _sendtoIP = ip; 129 | _sendtoPort = port; 130 | 131 | udp_recv(_udp.pcb, &udp_receive_callback, &_udp); 132 | stm32_eth_scheduler(); 133 | 134 | return 1; 135 | } 136 | 137 | int EthernetUDP::endPacket() 138 | { 139 | if ((_udp.pcb == NULL) || (_data == NULL)) { 140 | return 0; 141 | } 142 | 143 | ip_addr_t ipaddr; 144 | if (ERR_OK != udp_sendto(_udp.pcb, _data, u8_to_ip_addr(rawIPAddress(_sendtoIP), &ipaddr), _sendtoPort)) { 145 | _data = stm32_free_data(_data); 146 | return 0; 147 | } 148 | 149 | _data = stm32_free_data(_data); 150 | stm32_eth_scheduler(); 151 | 152 | return 1; 153 | } 154 | 155 | size_t EthernetUDP::write(uint8_t byte) 156 | { 157 | return write(&byte, 1); 158 | } 159 | 160 | size_t EthernetUDP::write(const uint8_t *buffer, size_t size) 161 | { 162 | _data = stm32_new_data(_data, buffer, size); 163 | if (_data == NULL) { 164 | return 0; 165 | } 166 | 167 | return size; 168 | } 169 | 170 | int EthernetUDP::parsePacket() 171 | { 172 | // discard any remaining bytes in the last packet 173 | // while (_remaining) { 174 | // // could this fail (loop endlessly) if _remaining > 0 and recv in read fails? 175 | // // should only occur if recv fails after telling us the data is there, lets 176 | // // hope the w5100 always behaves :) 177 | // read(); 178 | // } 179 | 180 | stm32_eth_scheduler(); 181 | 182 | if (_udp.data.available > 0) { 183 | _remoteIP = IPAddress(ip_addr_to_u32(&(_udp.ip))); 184 | _remotePort = _udp.port; 185 | _remaining = _udp.data.available; 186 | 187 | return _remaining; 188 | } 189 | // There aren't any packets available 190 | return 0; 191 | } 192 | 193 | int EthernetUDP::read() 194 | { 195 | uint8_t byte; 196 | 197 | if (_udp.data.p == NULL) { 198 | return -1; 199 | } 200 | 201 | if (_remaining > 0) { 202 | if (read(&byte, 1) > 0) { 203 | return byte; 204 | } 205 | } 206 | 207 | // If we get here, there's no data available 208 | return -1; 209 | } 210 | 211 | int EthernetUDP::read(unsigned char *buffer, size_t len) 212 | { 213 | if (_udp.data.p == NULL) { 214 | return -1; 215 | } 216 | 217 | if (_remaining > 0) { 218 | int got; 219 | 220 | if (_remaining <= len) { 221 | // data should fit in the buffer 222 | got = (int)stm32_get_data(&(_udp.data), (uint8_t *)buffer, _remaining); 223 | } else { 224 | // too much data for the buffer, 225 | // grab as much as will fit 226 | got = (int)stm32_get_data(&(_udp.data), (uint8_t *)buffer, len); 227 | } 228 | 229 | if (got > 0) { 230 | _remaining -= got; 231 | return got; 232 | } 233 | 234 | } 235 | 236 | // If we get here, there's no data available or recv failed 237 | return -1; 238 | 239 | } 240 | 241 | int EthernetUDP::peek() 242 | { 243 | uint8_t b; 244 | // Unlike recv, peek doesn't check to see if there's any data available, so we must. 245 | // If the user hasn't called parsePacket yet then return nothing otherwise they 246 | // may get the UDP header 247 | if (!_remaining) { 248 | return -1; 249 | } 250 | b = pbuf_get_at(_udp.data.p, _udp.data.p->tot_len - _udp.data.available); 251 | return b; 252 | } 253 | 254 | void EthernetUDP::flush() 255 | { 256 | // TODO: we should wait for TX buffer to be emptied 257 | } 258 | 259 | /* Start EthernetUDP socket, listening at local port PORT */ 260 | uint8_t EthernetUDP::beginMulticast(IPAddress ip, uint16_t port) 261 | { 262 | return begin(ip, port, true); 263 | } 264 | 265 | #if LWIP_UDP 266 | void EthernetUDP::onDataArrival(std::function onDataArrival_fn) 267 | { 268 | _udp.onDataArrival = onDataArrival_fn; 269 | } 270 | #endif 271 | -------------------------------------------------------------------------------- /src/EthernetUdp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Udp.cpp: Library to send/receive UDP packets with the Arduino ethernet shield. 3 | * This version only offers minimal wrapping of socket.c/socket.h 4 | * Drop Udp.h/.cpp into the Ethernet library directory at hardware/libraries/Ethernet/ 5 | * 6 | * NOTE: UDP is fast, but has some important limitations (thanks to Warren Gray for mentioning these) 7 | * 1) UDP does not guarantee the order in which assembled UDP packets are received. This 8 | * might not happen often in practice, but in larger network topologies, a UDP 9 | * packet can be received out of sequence. 10 | * 2) UDP does not guard against lost packets - so packets *can* disappear without the sender being 11 | * aware of it. Again, this may not be a concern in practice on small local networks. 12 | * For more information, see http://www.cafeaulait.org/course/week12/35.html 13 | * 14 | * MIT License: 15 | * Copyright (c) 2008 Bjoern Hartmann 16 | * Permission is hereby granted, free of charge, to any person obtaining a copy 17 | * of this software and associated documentation files (the "Software"), to deal 18 | * in the Software without restriction, including without limitation the rights 19 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 20 | * copies of the Software, and to permit persons to whom the Software is 21 | * furnished to do so, subject to the following conditions: 22 | * 23 | * The above copyright notice and this permission notice shall be included in 24 | * all copies or substantial portions of the Software. 25 | * 26 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 27 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 28 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 29 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 30 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 31 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 32 | * THE SOFTWARE. 33 | * 34 | * bjoern@cs.stanford.edu 12/30/2008 35 | */ 36 | 37 | #ifndef ethernetudp_h 38 | #define ethernetudp_h 39 | 40 | #include 41 | #include 42 | 43 | #include "utility/stm32_eth.h" 44 | 45 | #define UDP_TX_PACKET_MAX_SIZE 24 46 | 47 | class EthernetUDP : public UDP { 48 | private: 49 | uint16_t _port; // local port to listen on 50 | IPAddress _remoteIP; // remote IP address for the incoming packet whilst it's being processed 51 | uint16_t _remotePort; // remote port for the incoming packet whilst it's being processed 52 | IPAddress _sendtoIP; // the remote IP address set by beginPacket 53 | uint16_t _sendtoPort; // the remote port set by beginPacket 54 | 55 | struct pbuf *_data; //pbuf for data to send 56 | struct udp_struct _udp; //udp settings 57 | 58 | protected: 59 | uint16_t _remaining; // remaining bytes of incoming packet yet to be processed 60 | 61 | public: 62 | EthernetUDP(); // Constructor 63 | 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 64 | virtual uint8_t begin(IPAddress, uint16_t, bool multicast = false); // initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use 65 | 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 66 | virtual void stop(); // Finish with the UDP socket 67 | 68 | // Sending UDP packets 69 | 70 | // Start building up a packet to send to the remote host specific in ip and port 71 | // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port 72 | virtual int beginPacket(IPAddress ip, uint16_t port); 73 | // Start building up a packet to send to the remote host specific in host and port 74 | // Returns 1 if successful, 0 if there was a problem resolving the hostname or port 75 | virtual int beginPacket(const char *host, uint16_t port); 76 | // Finish off this packet and send it 77 | // Returns 1 if the packet was sent successfully, 0 if there was an error 78 | virtual int endPacket(); 79 | // Write a single byte into the packet 80 | virtual size_t write(uint8_t); 81 | // Write size bytes from buffer into the packet 82 | virtual size_t write(const uint8_t *buffer, size_t size); 83 | 84 | using Print::write; 85 | 86 | // Start processing the next available incoming packet 87 | // Returns the size of the packet in bytes, or 0 if no packets are available 88 | virtual int parsePacket(); 89 | // Number of bytes remaining in the current packet 90 | virtual int available(); 91 | // Read a single byte from the current packet 92 | virtual int read(); 93 | // Read up to len bytes from the current packet and place them into buffer 94 | // Returns the number of bytes read, or 0 if none are available 95 | virtual int read(unsigned char *buffer, size_t len); 96 | // Read up to len characters from the current packet and place them into buffer 97 | // Returns the number of characters read, or 0 if none are available 98 | virtual int read(char *buffer, size_t len) 99 | { 100 | return read((unsigned char *)buffer, len); 101 | }; 102 | // Return the next byte from the current packet without moving on to the next byte 103 | virtual int peek(); 104 | virtual void flush(); // Finish reading the current packet 105 | 106 | // Return the IP address of the host who sent the current incoming packet 107 | virtual IPAddress remoteIP() 108 | { 109 | return _remoteIP; 110 | }; 111 | // Return the port of the host who sent the current incoming packet 112 | virtual uint16_t remotePort() 113 | { 114 | return _remotePort; 115 | }; 116 | virtual void onDataArrival(std::function onDataArrival_fn); 117 | }; 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /src/STM32Ethernet.cpp: -------------------------------------------------------------------------------- 1 | #include "STM32Ethernet.h" 2 | #include "Dhcp.h" 3 | 4 | int EthernetClass::begin(unsigned long timeout, unsigned long responseTimeout) 5 | { 6 | static DhcpClass s_dhcp; 7 | _dhcp = &s_dhcp; 8 | stm32_eth_init(NULL, NULL, NULL, NULL); 9 | 10 | // Now try to get our config info from a DHCP server 11 | int ret = _dhcp->beginWithDHCP(NULL, timeout, responseTimeout); 12 | if (ret == 1) { 13 | _dnsServerAddress = _dhcp->getDnsServerIp(); 14 | } 15 | 16 | return ret; 17 | } 18 | 19 | void EthernetClass::begin(IPAddress local_ip) 20 | { 21 | IPAddress subnet(255, 255, 255, 0); 22 | begin(local_ip, subnet); 23 | } 24 | 25 | void EthernetClass::begin(IPAddress local_ip, IPAddress subnet) 26 | { 27 | // Assume the gateway will be the machine on the same network as the local IP 28 | // but with last octet being '1' 29 | IPAddress gateway = local_ip; 30 | gateway[3] = 1; 31 | begin(local_ip, subnet, gateway); 32 | } 33 | 34 | void EthernetClass::begin(IPAddress local_ip, IPAddress subnet, IPAddress gateway) 35 | { 36 | // Assume the DNS server will be the same machine than gateway 37 | begin(local_ip, subnet, gateway, gateway); 38 | } 39 | 40 | void EthernetClass::begin(IPAddress local_ip, IPAddress subnet, IPAddress gateway, IPAddress dns_server) 41 | { 42 | stm32_eth_init(NULL, local_ip.raw_address(), gateway.raw_address(), subnet.raw_address()); 43 | /* If there is a local DHCP informs it of our manual IP configuration to 44 | prevent IP conflict */ 45 | stm32_DHCP_manual_config(); 46 | _dnsServerAddress = dns_server; 47 | } 48 | 49 | int EthernetClass::begin(uint8_t *mac_address, unsigned long timeout, unsigned long responseTimeout) 50 | { 51 | static DhcpClass s_dhcp; 52 | _dhcp = &s_dhcp; 53 | 54 | stm32_eth_init(mac_address, NULL, NULL, NULL); 55 | 56 | // Now try to get our config info from a DHCP server 57 | int ret = _dhcp->beginWithDHCP(mac_address, timeout, responseTimeout); 58 | if (ret == 1) { 59 | _dnsServerAddress = _dhcp->getDnsServerIp(); 60 | } 61 | return ret; 62 | } 63 | 64 | void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip) 65 | { 66 | // Assume the DNS server will be the machine on the same network as the local IP 67 | // but with last octet being '1' 68 | IPAddress dns_server = local_ip; 69 | dns_server[3] = 1; 70 | begin(mac_address, local_ip, dns_server); 71 | } 72 | 73 | void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server) 74 | { 75 | // Assume the gateway will be the machine on the same network as the local IP 76 | // but with last octet being '1' 77 | IPAddress gateway = local_ip; 78 | gateway[3] = 1; 79 | begin(mac_address, local_ip, dns_server, gateway); 80 | } 81 | 82 | void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway) 83 | { 84 | IPAddress subnet(255, 255, 255, 0); 85 | begin(mac_address, local_ip, dns_server, gateway, subnet); 86 | } 87 | 88 | void EthernetClass::begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet) 89 | { 90 | stm32_eth_init(mac_address, local_ip.raw_address(), gateway.raw_address(), subnet.raw_address()); 91 | /* If there is a local DHCP informs it of our manual IP configuration to 92 | prevent IP conflict */ 93 | stm32_DHCP_manual_config(); 94 | _dnsServerAddress = dns_server; 95 | } 96 | 97 | EthernetLinkStatus EthernetClass::linkStatus() 98 | { 99 | return (!stm32_eth_is_init()) ? Unknown : (stm32_eth_link_up() ? LinkON : LinkOFF); 100 | } 101 | 102 | int EthernetClass::maintain() 103 | { 104 | int rc = DHCP_CHECK_NONE; 105 | 106 | if (_dhcp != NULL) { 107 | //we have a pointer to dhcp, use it 108 | rc = _dhcp->checkLease(); 109 | switch (rc) { 110 | case DHCP_CHECK_NONE: 111 | //nothing done 112 | break; 113 | case DHCP_CHECK_RENEW_OK: 114 | case DHCP_CHECK_REBIND_OK: 115 | _dnsServerAddress = _dhcp->getDnsServerIp(); 116 | break; 117 | default: 118 | //this is actually a error, it will retry though 119 | break; 120 | } 121 | } 122 | return rc; 123 | } 124 | 125 | /* 126 | * This function updates the LwIP stack and can be called to be sure to update 127 | * the stack (e.g. in case of a long loop). 128 | */ 129 | void EthernetClass::schedule(void) 130 | { 131 | stm32_eth_scheduler(); 132 | } 133 | 134 | void EthernetClass::setMACAddress(const uint8_t *mac_address) 135 | { 136 | stm32_eth_set_macaddr(mac_address); 137 | } 138 | 139 | void EthernetClass::MACAddress(uint8_t *mac_address) 140 | { 141 | stm32_eth_get_macaddr(mac_address); 142 | } 143 | 144 | IPAddress EthernetClass::localIP() 145 | { 146 | return IPAddress(stm32_eth_get_ipaddr()); 147 | } 148 | 149 | IPAddress EthernetClass::subnetMask() 150 | { 151 | return IPAddress(stm32_eth_get_netmaskaddr()); 152 | } 153 | 154 | IPAddress EthernetClass::gatewayIP() 155 | { 156 | return IPAddress(stm32_eth_get_gwaddr()); 157 | } 158 | 159 | IPAddress EthernetClass::dnsServerIP() 160 | { 161 | return _dnsServerAddress; 162 | } 163 | 164 | void EthernetClass::setDnsServerIP(const IPAddress dns_server) 165 | { 166 | _dnsServerAddress = dns_server; 167 | } 168 | 169 | EthernetClass Ethernet; 170 | -------------------------------------------------------------------------------- /src/STM32Ethernet.h: -------------------------------------------------------------------------------- 1 | #ifndef ethernet_h 2 | #define ethernet_h 3 | 4 | #include 5 | #include "IPAddress.h" 6 | #include "EthernetClient.h" 7 | #include "EthernetServer.h" 8 | #include "Dhcp.h" 9 | 10 | enum EthernetLinkStatus { 11 | Unknown, 12 | LinkON, 13 | LinkOFF 14 | }; 15 | 16 | class EthernetClass { 17 | private: 18 | IPAddress _dnsServerAddress; 19 | DhcpClass *_dhcp; 20 | 21 | public: 22 | // Initialise the Ethernet with the internal provided MAC address and gain the rest of the 23 | // configuration through DHCP. 24 | // Returns 0 if the DHCP configuration failed, and 1 if it succeeded 25 | int begin(unsigned long timeout = 60000, unsigned long responseTimeout = 4000); 26 | EthernetLinkStatus linkStatus(); 27 | void begin(IPAddress local_ip); 28 | void begin(IPAddress local_ip, IPAddress subnet); 29 | void begin(IPAddress local_ip, IPAddress subnet, IPAddress gateway); 30 | void begin(IPAddress local_ip, IPAddress subnet, IPAddress gateway, IPAddress dns_server); 31 | // Initialise the Ethernet shield to use the provided MAC address and gain the rest of the 32 | // configuration through DHCP. 33 | // Returns 0 if the DHCP configuration failed, and 1 if it succeeded 34 | int begin(uint8_t *mac_address, unsigned long timeout = 60000, unsigned long responseTimeout = 4000); 35 | void begin(uint8_t *mac_address, IPAddress local_ip); 36 | void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server); 37 | void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway); 38 | void begin(uint8_t *mac_address, IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet); 39 | 40 | 41 | int maintain(); 42 | void schedule(void); 43 | 44 | void MACAddress(uint8_t *mac_address); 45 | IPAddress localIP(); 46 | IPAddress subnetMask(); 47 | IPAddress gatewayIP(); 48 | IPAddress dnsServerIP(); 49 | 50 | void setMACAddress(const uint8_t *mac_address); 51 | void setDnsServerIP(const IPAddress dns_server); 52 | 53 | friend class EthernetClient; 54 | friend class EthernetServer; 55 | }; 56 | 57 | extern EthernetClass Ethernet; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /src/lwipopts.h: -------------------------------------------------------------------------------- 1 | /* 2 | * @file lwipopts.h 3 | * @author Frederic Pillon for STMicroelectronics. 4 | * @brief Include header file to match Arduino library format 5 | */ 6 | 7 | #ifndef _ARDUINO_LWIPOPTS_H 8 | #define _ARDUINO_LWIPOPTS_H 9 | 10 | /* LwIP specific configuration options. */ 11 | #if __has_include("STM32lwipopts.h") 12 | #include "STM32lwipopts.h" 13 | #else 14 | #if __has_include("lwipopts_extra.h") 15 | #include "lwipopts_extra.h" 16 | #endif 17 | #include "lwipopts_default.h" 18 | #endif 19 | 20 | #endif /* _ARDUINO_LWIPOPTS_H */ 21 | 22 | -------------------------------------------------------------------------------- /src/lwipopts_default.h: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file lwipopts_default.h 4 | * @author MCD Application Team 5 | * @brief lwIP Options Configuration. 6 | ****************************************************************************** 7 | * @attention 8 | * 9 | *

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

11 | * 12 | * This software component is licensed by ST under Ultimate Liberty license 13 | * SLA0044, the "License"; You may not use this file except in compliance with 14 | * the License. You may obtain a copy of the License at: 15 | * www.st.com/SLA0044 16 | * 17 | ****************************************************************************** 18 | */ 19 | #ifndef __LWIPOPTS_H__ 20 | #define __LWIPOPTS_H__ 21 | 22 | /** 23 | * NO_SYS==1: Provides VERY minimal functionality. Otherwise, 24 | * use lwIP facilities. 25 | */ 26 | #define NO_SYS 1 27 | 28 | /** 29 | * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain 30 | * critical regions during buffer allocation, deallocation and memory 31 | * allocation and deallocation. 32 | */ 33 | #define SYS_LIGHTWEIGHT_PROT 0 34 | 35 | #define LWIP_NOASSERT 36 | 37 | /* ---------- Memory options ---------- */ 38 | /* MEM_ALIGNMENT: should be set to the alignment of the CPU for which 39 | lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2 40 | byte alignment -> define MEM_ALIGNMENT to 2. */ 41 | #define MEM_ALIGNMENT 4 42 | 43 | /* MEM_SIZE: the size of the heap memory. If the application will send 44 | a lot of data that needs to be copied, this should be set high. */ 45 | #define MEM_SIZE (10*1024) 46 | 47 | /* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application 48 | sends a lot of data out of ROM (or other static memory), this 49 | should be set high. */ 50 | #define MEMP_NUM_PBUF 10 51 | /* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One 52 | per active UDP "connection". */ 53 | #define MEMP_NUM_UDP_PCB 6 54 | /* MEMP_NUM_TCP_PCB: the number of simultaneously active TCP 55 | connections. */ 56 | #define MEMP_NUM_TCP_PCB 10 57 | /* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP 58 | connections. */ 59 | #define MEMP_NUM_TCP_PCB_LISTEN 6 60 | /* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP 61 | segments. */ 62 | #define MEMP_NUM_TCP_SEG 8 63 | /* MEMP_NUM_SYS_TIMEOUT: the number of simultaneously active 64 | timeouts. */ 65 | #define MEMP_NUM_SYS_TIMEOUT 10 66 | 67 | 68 | /* ---------- Pbuf options ---------- */ 69 | /* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */ 70 | #define PBUF_POOL_SIZE 8 71 | 72 | /* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */ 73 | #define PBUF_POOL_BUFSIZE 1524 74 | 75 | 76 | /* ---------- TCP options ---------- */ 77 | #define LWIP_TCP 1 78 | #define TCP_TTL 255 79 | #define LWIP_SO_RCVTIMEO 1 80 | #define LWIP_SO_RCVRCVTIMEO_NONSTANDARD 1 /* Pass an integer number of ms instead of a timeval struct. */ 81 | #define LWIP_SO_SNDTIMEO 1 82 | #define LWIP_SO_SNDRCVTIMEO_NONSTANDARD 1 /* Pass an integer number of ms instead of a timeval struct. */ 83 | 84 | /* Controls if TCP should queue segments that arrive out of 85 | order. Define to 0 if your device is low on memory and you are not scared by TCP congestion and latencies. */ 86 | #define TCP_QUEUE_OOSEQ 0 87 | 88 | /* TCP Maximum segment size. */ 89 | #define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */ 90 | 91 | /* TCP sender buffer space (bytes). */ 92 | #define TCP_SND_BUF (4*TCP_MSS) 93 | 94 | /* TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least 95 | as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */ 96 | 97 | #define TCP_SND_QUEUELEN (2* TCP_SND_BUF/TCP_MSS) 98 | 99 | /* TCP receive window. */ 100 | #define TCP_WND (3*TCP_MSS) 101 | 102 | #define LWIP_TCP_KEEPALIVE 1 /* Keep the TCP link active. Important for MQTT/TLS */ 103 | #define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 1 /* Prevent the same port to be used after reset. 104 | Otherwise, the remote host may be confused if the port was not explicitly closed before the reset. */ 105 | 106 | 107 | /* ---------- ICMP options ---------- */ 108 | #define LWIP_ICMP 1 109 | #define LWIP_RAW 1 /* PING changed to 1 */ 110 | #define DEFAULT_RAW_RECVMBOX_SIZE 3 /* for ICMP PING */ 111 | 112 | 113 | /* ---------- DHCP options ---------- */ 114 | /* Define LWIP_DHCP to 1 if you want DHCP configuration of 115 | interfaces. DHCP is not implemented in lwIP 0.5.1, however, so 116 | turning this on does currently not work. */ 117 | #define LWIP_DHCP 1 118 | 119 | 120 | /* ---------- UDP options ---------- */ 121 | #define LWIP_UDP 1 122 | #define UDP_TTL 255 123 | 124 | 125 | /* ---------- Statistics options ---------- */ 126 | #define LWIP_STATS 0 127 | #define LWIP_PROVIDE_ERRNO 128 | 129 | /* ---------- link callback options ---------- */ 130 | /* LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface 131 | * whenever the link changes (i.e., link down) 132 | */ 133 | // need for building net_ip.c 134 | #define LWIP_NETIF_HOSTNAME 1 135 | #define LWIP_NETIF_STATUS_CALLBACK 1 136 | #define LWIP_NETIF_LINK_CALLBACK 1 137 | #define LWIP_DHCP_CHECK_LINK_UP 1 138 | 139 | /* 140 | -------------------------------------- 141 | ---------- Checksum options ---------- 142 | -------------------------------------- 143 | */ 144 | 145 | /* 146 | The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware: 147 | - To use this feature let the following define uncommented. 148 | - To disable it and process by CPU comment the the checksum. 149 | */ 150 | #define CHECKSUM_BY_HARDWARE 151 | 152 | 153 | #ifdef CHECKSUM_BY_HARDWARE 154 | /* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/ 155 | #define CHECKSUM_GEN_IP 0 156 | /* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/ 157 | #define CHECKSUM_GEN_UDP 0 158 | /* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/ 159 | #define CHECKSUM_GEN_TCP 0 160 | /* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/ 161 | #define CHECKSUM_CHECK_IP 0 162 | /* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/ 163 | #define CHECKSUM_CHECK_UDP 0 164 | /* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/ 165 | #define CHECKSUM_CHECK_TCP 0 166 | /* CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/ 167 | #define CHECKSUM_GEN_ICMP 0 168 | #else 169 | /* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/ 170 | #define CHECKSUM_GEN_IP 1 171 | /* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/ 172 | #define CHECKSUM_GEN_UDP 1 173 | /* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/ 174 | #define CHECKSUM_GEN_TCP 1 175 | /* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/ 176 | #define CHECKSUM_CHECK_IP 1 177 | /* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/ 178 | #define CHECKSUM_CHECK_UDP 1 179 | /* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/ 180 | #define CHECKSUM_CHECK_TCP 1 181 | /* CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/ 182 | #define CHECKSUM_GEN_ICMP 1 183 | #endif 184 | 185 | 186 | /* 187 | ---------------------------------------------- 188 | ---------- Sequential layer options ---------- 189 | ---------------------------------------------- 190 | */ 191 | /** 192 | * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) 193 | */ 194 | #define LWIP_NETCONN 0 195 | 196 | /* 197 | ------------------------------------ 198 | ---------- Socket options ---------- 199 | ------------------------------------ 200 | */ 201 | /** 202 | * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) 203 | */ 204 | #define LWIP_SOCKET 0 205 | #define LWIP_DNS 1 206 | 207 | /* 208 | ------------------------------------ 209 | ---------- httpd options ---------- 210 | ------------------------------------ 211 | */ 212 | 213 | /** Set this to 1 to support CGI */ 214 | #define LWIP_HTTPD_CGI 1 215 | 216 | /** Set this to 1 to support SSI (Server-Side-Includes) */ 217 | #define LWIP_HTTPD_SSI 1 218 | 219 | /** Set this to 1 to include "fsdata_custom.c" instead of "fsdata.c" for the 220 | * file system (to prevent changing the file included in CVS) */ 221 | #define HTTPD_USE_CUSTOM_FSDATA 1 222 | 223 | /* 224 | ------------------------------------ 225 | ---------- Custom options ---------- 226 | ------------------------------------ 227 | */ 228 | 229 | /** Enables the Ethernet peripheral in RMII mode. If not defined, MII mode will 230 | be enabled. Pin mapping must be configured for the selected mode 231 | (see PinMap_Ethernet in PeripheralPins.c). */ 232 | #define ETHERNET_RMII_MODE_CONFIGURATION 1 233 | 234 | /** Uncomment this line to use the ethernet input in interrupt mode. 235 | * NOTE: LwIP stack documentation recommends to use the polling mode without 236 | * an operating system. */ 237 | //#define ETH_INPUT_USE_IT 1 238 | 239 | #endif /* __LWIPOPTS_H__ */ 240 | 241 | /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 242 | -------------------------------------------------------------------------------- /src/utility/ethernetif.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file ethernetif.c 4 | * @author MCD Application Team & Wi6Labs 5 | * @version V1.5.0 6 | * @date 20-june-2017 7 | * @brief This file implements Ethernet network interface drivers for lwIP 8 | ****************************************************************************** 9 | * @attention 10 | * 11 | *

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

13 | * 14 | * Redistribution and use in source and binary forms, with or without 15 | * modification, are permitted, provided that the following conditions are met: 16 | * 17 | * 1. Redistribution of source code must retain the above copyright notice, 18 | * this list of conditions and the following disclaimer. 19 | * 2. Redistributions in binary form must reproduce the above copyright notice, 20 | * this list of conditions and the following disclaimer in the documentation 21 | * and/or other materials provided with the distribution. 22 | * 3. Neither the name of STMicroelectronics nor the names of other 23 | * contributors to this software may be used to endorse or promote products 24 | * derived from this software without specific written permission. 25 | * 4. This software, including modifications and/or derivative works of this 26 | * software, must execute solely and exclusively on microcontroller or 27 | * microprocessor devices manufactured by or for STMicroelectronics. 28 | * 5. Redistribution and use of this software other than as permitted under 29 | * this license is void and will automatically terminate your rights under 30 | * this license. 31 | * 32 | * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" 33 | * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT 34 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 35 | * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY 36 | * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT 37 | * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 38 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 39 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 40 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 41 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 42 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 43 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 44 | * 45 | ****************************************************************************** 46 | */ 47 | /* Includes ------------------------------------------------------------------*/ 48 | #include "stm32_def.h" 49 | #include "lwip/timeouts.h" 50 | #include "netif/etharp.h" 51 | #include "ethernetif.h" 52 | #include 53 | #include "PeripheralPins.h" 54 | #include "lwip/igmp.h" 55 | #include "stm32_eth.h" 56 | #if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION <= 0x01050000) 57 | #include "variant.h" 58 | #endif 59 | 60 | #ifdef __cplusplus 61 | extern "C" { 62 | #endif 63 | 64 | /* Private typedef -----------------------------------------------------------*/ 65 | /* Private define ------------------------------------------------------------*/ 66 | /* Network interface name */ 67 | #define IFNAME0 's' 68 | #define IFNAME1 't' 69 | 70 | /* Definition of PHY SPECIAL CONTROL/STATUS REGISTER bitfield Auto-negotiation done indication */ 71 | /* Placed in STM32Ethernet library instead of HAL conf to avoid compatibility dependence with Arduino_Core_STM32 */ 72 | /* Could be moved from this file once Generic PHY is implemented */ 73 | #define PHY_SR_AUTODONE ((uint16_t)0x1000) 74 | 75 | /* Private macro -------------------------------------------------------------*/ 76 | /* Private variables ---------------------------------------------------------*/ 77 | #if defined ( __ICCARM__ ) /*!< IAR Compiler */ 78 | #pragma data_alignment=4 79 | #endif 80 | __ALIGN_BEGIN ETH_DMADescTypeDef DMARxDscrTab[ETH_RXBUFNB] __ALIGN_END;/* Ethernet Rx MA Descriptor */ 81 | 82 | #if defined ( __ICCARM__ ) /*!< IAR Compiler */ 83 | #pragma data_alignment=4 84 | #endif 85 | __ALIGN_BEGIN ETH_DMADescTypeDef DMATxDscrTab[ETH_TXBUFNB] __ALIGN_END;/* Ethernet Tx DMA Descriptor */ 86 | 87 | #if defined ( __ICCARM__ ) /*!< IAR Compiler */ 88 | #pragma data_alignment=4 89 | #endif 90 | __ALIGN_BEGIN uint8_t Rx_Buff[ETH_RXBUFNB][ETH_RX_BUF_SIZE] __ALIGN_END; /* Ethernet Receive Buffer */ 91 | 92 | #if defined ( __ICCARM__ ) /*!< IAR Compiler */ 93 | #pragma data_alignment=4 94 | #endif 95 | __ALIGN_BEGIN uint8_t Tx_Buff[ETH_TXBUFNB][ETH_TX_BUF_SIZE] __ALIGN_END; /* Ethernet Transmit Buffer */ 96 | 97 | static ETH_HandleTypeDef EthHandle; 98 | 99 | /* If default MAC fields is not defined use default values based on UID */ 100 | #if !defined(MAC_ADDR0) 101 | #define MAC_ADDR0 0x00 102 | #endif 103 | #if !defined(MAC_ADDR1) 104 | #define MAC_ADDR1 0x80 105 | #endif 106 | #if !defined(MAC_ADDR2) 107 | #define MAC_ADDR2 0xE1 108 | #endif 109 | #if !defined(MAC_ADDR3) 110 | #define MAC_ADDR3 ((uint8_t)(((*(uint32_t *)UID_BASE) & 0x00FF0000) >> 16)) 111 | #endif 112 | #if !defined(MAC_ADDR4) 113 | #define MAC_ADDR4 ((uint8_t)(((*(uint32_t *)UID_BASE) & 0x0000FF00) >> 8)) 114 | #endif 115 | #if !defined(MAC_ADDR5) 116 | #define MAC_ADDR5 ((uint8_t)((*(uint32_t *)UID_BASE) & 0x000000FF)) 117 | #endif 118 | static uint8_t macaddress[6] = { MAC_ADDR0, MAC_ADDR1, MAC_ADDR2, MAC_ADDR3, MAC_ADDR4, MAC_ADDR5 }; 119 | 120 | #if LWIP_IGMP 121 | uint32_t ETH_HashTableHigh = 0x0; 122 | uint32_t ETH_HashTableLow = 0x0; 123 | #endif 124 | 125 | /* Private function prototypes -----------------------------------------------*/ 126 | /* Private functions ---------------------------------------------------------*/ 127 | /******************************************************************************* 128 | Ethernet MSP Routines 129 | *******************************************************************************/ 130 | /** 131 | * @brief Initializes the ETH MSP. 132 | * @param heth: ETH handle 133 | * @retval None 134 | */ 135 | void HAL_ETH_MspInit(ETH_HandleTypeDef *heth) 136 | { 137 | GPIO_InitTypeDef GPIO_InitStructure; 138 | const PinMap *map = PinMap_Ethernet; 139 | PinName pin = pin_pinName(map); 140 | GPIO_TypeDef *port; 141 | 142 | UNUSED(heth); 143 | 144 | /* Ethernet pins configuration ************************************************/ 145 | 146 | if (map != NULL) { 147 | while (pin != NC) { 148 | /* Set port clock */ 149 | port = set_GPIO_Port_Clock(STM_PORT(pin)); 150 | 151 | /* pin configuration */ 152 | GPIO_InitStructure.Pin = STM_GPIO_PIN(pin); 153 | GPIO_InitStructure.Mode = STM_PIN_MODE(pinmap_function(pin, PinMap_Ethernet)); 154 | GPIO_InitStructure.Pull = STM_PIN_PUPD(pinmap_function(pin, PinMap_Ethernet)); 155 | GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; 156 | GPIO_InitStructure.Alternate = STM_PIN_AFNUM(pinmap_function(pin, PinMap_Ethernet)); 157 | HAL_GPIO_Init(port, &GPIO_InitStructure); 158 | 159 | pin = pin_pinName(++map); 160 | } 161 | } 162 | 163 | #ifdef ETH_INPUT_USE_IT 164 | /* Enable the Ethernet global Interrupt */ 165 | HAL_NVIC_SetPriority(ETH_IRQn, 0x7, 0); 166 | HAL_NVIC_EnableIRQ(ETH_IRQn); 167 | #endif /* ETH_INPUT_USE_IT */ 168 | 169 | /* Enable ETHERNET clock */ 170 | __HAL_RCC_ETH_CLK_ENABLE(); 171 | } 172 | 173 | /******************************************************************************* 174 | LL Driver Interface ( LwIP stack --> ETH) 175 | *******************************************************************************/ 176 | /** 177 | * @brief In this function, the hardware should be initialized. 178 | * Called from ethernetif_init(). 179 | * 180 | * @param netif the already initialized lwip network interface structure 181 | * for this ethernetif 182 | */ 183 | static void low_level_init(struct netif *netif) 184 | { 185 | uint32_t regvalue; 186 | 187 | EthHandle.Instance = ETH; 188 | EthHandle.Init.MACAddr = macaddress; 189 | EthHandle.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE; 190 | EthHandle.Init.Speed = ETH_SPEED_100M; 191 | EthHandle.Init.DuplexMode = ETH_MODE_FULLDUPLEX; 192 | #ifdef ETHERNET_RMII_MODE_CONFIGURATION 193 | EthHandle.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII; 194 | #else 195 | EthHandle.Init.MediaInterface = ETH_MEDIA_INTERFACE_MII; 196 | #endif /* ETHERNET_RMII_MODE_CONFIGURATION */ 197 | #ifdef ETH_INPUT_USE_IT 198 | EthHandle.Init.RxMode = ETH_RXINTERRUPT_MODE; 199 | #else 200 | EthHandle.Init.RxMode = ETH_RXPOLLING_MODE; 201 | #endif /* ETH_INPUT_USE_IT */ 202 | EthHandle.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE; 203 | EthHandle.Init.PhyAddress = LAN8742A_PHY_ADDRESS; 204 | 205 | /* configure ethernet peripheral (GPIOs, clocks, MAC, DMA) */ 206 | if (HAL_ETH_Init(&EthHandle) == HAL_OK) { 207 | /* Set netif link flag */ 208 | netif->flags |= NETIF_FLAG_LINK_UP; 209 | } 210 | 211 | /* Initialize Tx Descriptors list: Chain Mode */ 212 | HAL_ETH_DMATxDescListInit(&EthHandle, DMATxDscrTab, &Tx_Buff[0][0], ETH_TXBUFNB); 213 | 214 | /* Initialize Rx Descriptors list: Chain Mode */ 215 | HAL_ETH_DMARxDescListInit(&EthHandle, DMARxDscrTab, &Rx_Buff[0][0], ETH_RXBUFNB); 216 | 217 | /* set MAC hardware address length */ 218 | netif->hwaddr_len = ETH_HWADDR_LEN; 219 | 220 | /* set MAC hardware address */ 221 | netif->hwaddr[0] = macaddress[0]; 222 | netif->hwaddr[1] = macaddress[1]; 223 | netif->hwaddr[2] = macaddress[2]; 224 | netif->hwaddr[3] = macaddress[3]; 225 | netif->hwaddr[4] = macaddress[4]; 226 | netif->hwaddr[5] = macaddress[5]; 227 | 228 | /* maximum transfer unit */ 229 | netif->mtu = 1500; 230 | 231 | /* device capabilities */ 232 | /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ 233 | netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP; 234 | 235 | /* Enable MAC and DMA transmission and reception */ 236 | HAL_ETH_Start(&EthHandle); 237 | #if LWIP_IGMP 238 | netif_set_igmp_mac_filter(netif, igmp_mac_filter); 239 | #endif 240 | /**** Configure PHY to generate an interrupt when Eth Link state changes ****/ 241 | /* Read Register Configuration */ 242 | HAL_ETH_ReadPHYRegister(&EthHandle, PHY_IMR, ®value); 243 | 244 | regvalue |= PHY_ISFR_INT4; 245 | 246 | /* Enable Interrupt on change of link status */ 247 | HAL_ETH_WritePHYRegister(&EthHandle, PHY_IMR, regvalue); 248 | #if LWIP_IGMP 249 | ETH_HashTableHigh = EthHandle.Instance->MACHTHR; 250 | ETH_HashTableLow = EthHandle.Instance->MACHTLR; 251 | #endif 252 | } 253 | 254 | /** 255 | * @brief This function should do the actual transmission of the packet. The packet is 256 | * contained in the pbuf that is passed to the function. This pbuf 257 | * might be chained. 258 | * 259 | * @param netif the lwip network interface structure for this ethernetif 260 | * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) 261 | * @return ERR_OK if the packet could be sent 262 | * an err_t value if the packet couldn't be sent 263 | * 264 | * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to 265 | * strange results. You might consider waiting for space in the DMA queue 266 | * to become available since the stack doesn't retry to send a packet 267 | * dropped because of memory failure (except for the TCP timers). 268 | */ 269 | static err_t low_level_output(struct netif *netif, struct pbuf *p) 270 | { 271 | err_t errval; 272 | struct pbuf *q; 273 | uint8_t *buffer = (uint8_t *)(EthHandle.TxDesc->Buffer1Addr); 274 | __IO ETH_DMADescTypeDef *DmaTxDesc; 275 | uint32_t framelength = 0; 276 | uint32_t bufferoffset = 0; 277 | uint32_t byteslefttocopy = 0; 278 | uint32_t payloadoffset = 0; 279 | 280 | UNUSED(netif); 281 | 282 | DmaTxDesc = EthHandle.TxDesc; 283 | bufferoffset = 0; 284 | 285 | /* copy frame from pbufs to driver buffers */ 286 | for (q = p; q != NULL; q = q->next) { 287 | /* Is this buffer available? If not, goto error */ 288 | if ((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET) { 289 | errval = ERR_USE; 290 | goto error; 291 | } 292 | 293 | /* Get bytes in current lwIP buffer */ 294 | byteslefttocopy = q->len; 295 | payloadoffset = 0; 296 | 297 | /* Check if the length of data to copy is bigger than Tx buffer size*/ 298 | while ((byteslefttocopy + bufferoffset) > ETH_TX_BUF_SIZE) { 299 | /* Copy data to Tx buffer*/ 300 | memcpy((uint8_t *)((uint8_t *)buffer + bufferoffset), (uint8_t *)((uint8_t *)q->payload + payloadoffset), (ETH_TX_BUF_SIZE - bufferoffset)); 301 | 302 | /* Point to next descriptor */ 303 | DmaTxDesc = (ETH_DMADescTypeDef *)(DmaTxDesc->Buffer2NextDescAddr); 304 | 305 | /* Check if the buffer is available */ 306 | if ((DmaTxDesc->Status & ETH_DMATXDESC_OWN) != (uint32_t)RESET) { 307 | errval = ERR_USE; 308 | goto error; 309 | } 310 | 311 | buffer = (uint8_t *)(DmaTxDesc->Buffer1Addr); 312 | 313 | byteslefttocopy = byteslefttocopy - (ETH_TX_BUF_SIZE - bufferoffset); 314 | payloadoffset = payloadoffset + (ETH_TX_BUF_SIZE - bufferoffset); 315 | framelength = framelength + (ETH_TX_BUF_SIZE - bufferoffset); 316 | bufferoffset = 0; 317 | } 318 | 319 | /* Copy the remaining bytes */ 320 | memcpy((uint8_t *)((uint8_t *)buffer + bufferoffset), (uint8_t *)((uint8_t *)q->payload + payloadoffset), byteslefttocopy); 321 | bufferoffset = bufferoffset + byteslefttocopy; 322 | framelength = framelength + byteslefttocopy; 323 | } 324 | 325 | /* Prepare transmit descriptors to give to DMA */ 326 | HAL_ETH_TransmitFrame(&EthHandle, framelength); 327 | 328 | errval = ERR_OK; 329 | 330 | error: 331 | 332 | /* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */ 333 | if ((EthHandle.Instance->DMASR & ETH_DMASR_TUS) != (uint32_t)RESET) { 334 | /* Clear TUS ETHERNET DMA flag */ 335 | EthHandle.Instance->DMASR = ETH_DMASR_TUS; 336 | 337 | /* Resume DMA transmission*/ 338 | EthHandle.Instance->DMATPDR = 0; 339 | } 340 | return errval; 341 | } 342 | 343 | /** 344 | * @brief Should allocate a pbuf and transfer the bytes of the incoming 345 | * packet from the interface into the pbuf. 346 | * 347 | * @param netif the lwip network interface structure for this ethernetif 348 | * @return a pbuf filled with the received packet (including MAC header) 349 | * NULL on memory error 350 | */ 351 | static struct pbuf *low_level_input(struct netif *netif) 352 | { 353 | struct pbuf *p = NULL; 354 | struct pbuf *q; 355 | uint16_t len; 356 | uint8_t *buffer; 357 | __IO ETH_DMADescTypeDef *dmarxdesc; 358 | uint32_t bufferoffset = 0; 359 | uint32_t payloadoffset = 0; 360 | uint32_t byteslefttocopy = 0; 361 | uint32_t i = 0; 362 | 363 | UNUSED(netif); 364 | 365 | if (HAL_ETH_GetReceivedFrame_IT(&EthHandle) != HAL_OK) { 366 | return NULL; 367 | } 368 | 369 | /* Obtain the size of the packet and put it into the "len" variable. */ 370 | len = EthHandle.RxFrameInfos.length; 371 | buffer = (uint8_t *)EthHandle.RxFrameInfos.buffer; 372 | 373 | if (len > 0) { 374 | /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */ 375 | p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL); 376 | } 377 | 378 | if (p != NULL) { 379 | dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc; 380 | bufferoffset = 0; 381 | 382 | for (q = p; q != NULL; q = q->next) { 383 | byteslefttocopy = q->len; 384 | payloadoffset = 0; 385 | 386 | /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size */ 387 | while ((byteslefttocopy + bufferoffset) > ETH_RX_BUF_SIZE) { 388 | /* Copy data to pbuf */ 389 | memcpy((uint8_t *)((uint8_t *)q->payload + payloadoffset), (uint8_t *)((uint8_t *)buffer + bufferoffset), (ETH_RX_BUF_SIZE - bufferoffset)); 390 | 391 | /* Point to next descriptor */ 392 | dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr); 393 | buffer = (uint8_t *)(dmarxdesc->Buffer1Addr); 394 | 395 | byteslefttocopy = byteslefttocopy - (ETH_RX_BUF_SIZE - bufferoffset); 396 | payloadoffset = payloadoffset + (ETH_RX_BUF_SIZE - bufferoffset); 397 | bufferoffset = 0; 398 | } 399 | 400 | /* Copy remaining data in pbuf */ 401 | memcpy((uint8_t *)((uint8_t *)q->payload + payloadoffset), (uint8_t *)((uint8_t *)buffer + bufferoffset), byteslefttocopy); 402 | bufferoffset = bufferoffset + byteslefttocopy; 403 | } 404 | } 405 | 406 | /* Release descriptors to DMA */ 407 | /* Point to first descriptor */ 408 | dmarxdesc = EthHandle.RxFrameInfos.FSRxDesc; 409 | /* Set Own bit in Rx descriptors: gives the buffers back to DMA */ 410 | for (i = 0; i < EthHandle.RxFrameInfos.SegCount; i++) { 411 | dmarxdesc->Status |= ETH_DMARXDESC_OWN; 412 | dmarxdesc = (ETH_DMADescTypeDef *)(dmarxdesc->Buffer2NextDescAddr); 413 | } 414 | 415 | /* Clear Segment_Count */ 416 | EthHandle.RxFrameInfos.SegCount = 0; 417 | 418 | /* When Rx Buffer unavailable flag is set: clear it and resume reception */ 419 | if ((EthHandle.Instance->DMASR & ETH_DMASR_RBUS) != (uint32_t)RESET) { 420 | /* Clear RBUS ETHERNET DMA flag */ 421 | EthHandle.Instance->DMASR = ETH_DMASR_RBUS; 422 | /* Resume DMA reception */ 423 | EthHandle.Instance->DMARPDR = 0; 424 | } 425 | return p; 426 | } 427 | 428 | /** 429 | * @brief This function should be called when a packet is ready to be read 430 | * from the interface. It uses the function low_level_input() that 431 | * should handle the actual reception of bytes from the network 432 | * interface. Then the type of the received packet is determined and 433 | * the appropriate input function is called. 434 | * 435 | * @param netif the lwip network interface structure for this ethernetif 436 | */ 437 | void ethernetif_input(struct netif *netif) 438 | { 439 | err_t err; 440 | struct pbuf *p; 441 | 442 | /* move received packet into a new pbuf */ 443 | p = low_level_input(netif); 444 | 445 | /* no packet could be read, silently ignore this */ 446 | if (p == NULL) { 447 | return; 448 | } 449 | 450 | /* entry point to the LwIP stack */ 451 | err = netif->input(p, netif); 452 | 453 | if (err != ERR_OK) { 454 | LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n")); 455 | pbuf_free(p); 456 | p = NULL; 457 | } 458 | } 459 | 460 | /** 461 | * @brief Returns the current state 462 | * 463 | * @param None 464 | * @return 0 not initialized else 1 465 | */ 466 | uint8_t ethernetif_is_init(void) 467 | { 468 | return (EthHandle.State != HAL_ETH_STATE_RESET); 469 | } 470 | 471 | /** 472 | * @brief Should be called at the beginning of the program to set up the 473 | * network interface. It calls the function low_level_init() to do the 474 | * actual setup of the hardware. 475 | * 476 | * This function should be passed as a parameter to netif_add(). 477 | * 478 | * @param netif the lwip network interface structure for this ethernetif 479 | * @return ERR_OK if the loopif is initialized 480 | * ERR_MEM if private data couldn't be allocated 481 | * any other err_t on error 482 | */ 483 | err_t ethernetif_init(struct netif *netif) 484 | { 485 | LWIP_ASSERT("netif != NULL", (netif != NULL)); 486 | 487 | #if LWIP_NETIF_HOSTNAME 488 | /* Initialize interface hostname */ 489 | netif->hostname = "lwip"; 490 | #endif /* LWIP_NETIF_HOSTNAME */ 491 | 492 | netif->name[0] = IFNAME0; 493 | netif->name[1] = IFNAME1; 494 | /* We directly use etharp_output() here to save a function call. 495 | * You can instead declare your own function an call etharp_output() 496 | * from it if you have to do some checks before sending (e.g. if link 497 | * is available...) */ 498 | netif->output = etharp_output; 499 | netif->linkoutput = low_level_output; 500 | 501 | /* initialize the hardware */ 502 | low_level_init(netif); 503 | 504 | return ERR_OK; 505 | } 506 | 507 | /** 508 | * @brief Returns the current time in milliseconds 509 | * when LWIP_TIMERS == 1 and NO_SYS == 1 510 | * @param None 511 | * @retval Current Time value 512 | */ 513 | u32_t sys_now(void) 514 | { 515 | return HAL_GetTick(); 516 | } 517 | 518 | /** 519 | * @brief This function sets the netif link status. 520 | * @param netif: the network interface 521 | * @retval None 522 | */ 523 | void ethernetif_set_link(struct netif *netif) 524 | { 525 | uint32_t regvalue = 0; 526 | 527 | /* Read PHY_MISR*/ 528 | HAL_ETH_ReadPHYRegister(&EthHandle, PHY_ISFR, ®value); 529 | 530 | /* Check whether the link interrupt has occurred or not */ 531 | if ((regvalue & PHY_ISFR_INT4) != (uint16_t)RESET) { 532 | netif_set_link_down(netif); 533 | } 534 | 535 | HAL_ETH_ReadPHYRegister(&EthHandle, PHY_BSR, ®value); 536 | 537 | if ((regvalue & PHY_LINKED_STATUS) != (uint16_t)RESET) { 538 | #if LWIP_IGMP 539 | if (!(netif->flags & NETIF_FLAG_IGMP)) { 540 | netif->flags |= NETIF_FLAG_IGMP; 541 | igmp_init(); 542 | igmp_start(netif); 543 | } 544 | #endif 545 | netif_set_link_up(netif); 546 | } 547 | } 548 | 549 | /** 550 | * @brief Link callback function, this function is called on change of link status 551 | * to update low level driver configuration. 552 | * @param netif: The network interface 553 | * @retval None 554 | */ 555 | void ethernetif_update_config(struct netif *netif) 556 | { 557 | uint32_t regvalue = 0; 558 | 559 | if (netif_is_link_up(netif)) { 560 | /* Restart the auto-negotiation */ 561 | if (EthHandle.Init.AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE) { 562 | 563 | /* Check Auto negotiation */ 564 | HAL_ETH_ReadPHYRegister(&EthHandle, PHY_SR, ®value); 565 | if ((regvalue & PHY_SR_AUTODONE) != PHY_SR_AUTODONE) { 566 | goto error; 567 | } 568 | 569 | /* Configure the MAC with the Duplex Mode fixed by the auto-negotiation process */ 570 | if ((regvalue & PHY_DUPLEX_STATUS) != (uint32_t)RESET) { 571 | /* Set Ethernet duplex mode to Full-duplex following the auto-negotiation */ 572 | EthHandle.Init.DuplexMode = ETH_MODE_FULLDUPLEX; 573 | } else { 574 | /* Set Ethernet duplex mode to Half-duplex following the auto-negotiation */ 575 | EthHandle.Init.DuplexMode = ETH_MODE_HALFDUPLEX; 576 | } 577 | /* Configure the MAC with the speed fixed by the auto-negotiation process */ 578 | if (regvalue & PHY_SPEED_STATUS) { 579 | /* Set Ethernet speed to 10M following the auto-negotiation */ 580 | EthHandle.Init.Speed = ETH_SPEED_10M; 581 | } else { 582 | /* Set Ethernet speed to 100M following the auto-negotiation */ 583 | EthHandle.Init.Speed = ETH_SPEED_100M; 584 | } 585 | } else { /* AutoNegotiation Disable */ 586 | error : 587 | /* Check parameters */ 588 | assert_param(IS_ETH_SPEED(EthHandle.Init.Speed)); 589 | assert_param(IS_ETH_DUPLEX_MODE(EthHandle.Init.DuplexMode)); 590 | 591 | /* Set MAC Speed and Duplex Mode to PHY */ 592 | HAL_ETH_WritePHYRegister(&EthHandle, PHY_BCR, ((uint16_t)(EthHandle.Init.DuplexMode >> 3) | 593 | (uint16_t)(EthHandle.Init.Speed >> 1))); 594 | } 595 | 596 | /* ETHERNET MAC Re-Configuration */ 597 | HAL_ETH_ConfigMAC(&EthHandle, (ETH_MACInitTypeDef *) NULL); 598 | 599 | /* Restart MAC interface */ 600 | HAL_ETH_Start(&EthHandle); 601 | } else { 602 | /* Stop MAC interface */ 603 | HAL_ETH_Stop(&EthHandle); 604 | } 605 | 606 | ethernetif_notify_conn_changed(netif); 607 | } 608 | 609 | /** 610 | * @brief This function notify user about link status changement. 611 | * @param netif: the network interface 612 | * @retval None 613 | */ 614 | __weak void ethernetif_notify_conn_changed(struct netif *netif) 615 | { 616 | /* NOTE : This is function clould be implemented in user file 617 | when the callback is needed, 618 | */ 619 | UNUSED(netif); 620 | } 621 | 622 | /** 623 | * @brief This function set a custom MAC address. This function must be called 624 | * before ethernetif_init(). 625 | * @param mac: mac address 626 | * @retval None 627 | */ 628 | void ethernetif_set_mac_addr(const uint8_t *mac) 629 | { 630 | if ((mac != NULL) && !(ethernetif_is_init())) { 631 | memcpy(macaddress, mac, 6); 632 | } 633 | } 634 | 635 | /** 636 | * @brief This function get the current MAC address. 637 | * @param mac: mac address 638 | * @retval None 639 | */ 640 | void ethernetif_get_mac_addr(uint8_t *mac) 641 | { 642 | if (mac != NULL) { 643 | memcpy(mac, macaddress, 6); 644 | } 645 | } 646 | 647 | #if LWIP_IGMP 648 | err_t igmp_mac_filter(struct netif *netif, const ip4_addr_t *ip4_addr, netif_mac_filter_action action) 649 | { 650 | uint8_t mac[6]; 651 | const uint8_t *p = (const uint8_t *)ip4_addr; 652 | 653 | mac[0] = 0x01; 654 | mac[1] = 0x00; 655 | mac[2] = 0x5E; 656 | mac[3] = *(p + 1) & 0x7F; 657 | mac[4] = *(p + 2); 658 | mac[5] = *(p + 3); 659 | 660 | register_multicast_address(mac); 661 | 662 | return 0; 663 | } 664 | 665 | #ifndef HASH_BITS 666 | #define HASH_BITS 6 /* #bits in hash */ 667 | #endif 668 | 669 | uint32_t ethcrc(const uint8_t *data, size_t length) 670 | { 671 | uint32_t crc = 0xffffffff; 672 | size_t i; 673 | int j; 674 | 675 | for (i = 0; i < length; i++) { 676 | for (j = 0; j < 8; j++) { 677 | if (((crc >> 31) ^ (data[i] >> j)) & 0x01) { 678 | /* x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1 */ 679 | crc = (crc << 1) ^ 0x04C11DB7; 680 | } else { 681 | crc = crc << 1; 682 | } 683 | } 684 | } 685 | return ~crc; 686 | } 687 | 688 | void register_multicast_address(const uint8_t *mac) 689 | { 690 | uint32_t crc; 691 | uint8_t hash; 692 | 693 | /* Calculate crc32 value of mac address */ 694 | crc = ethcrc(mac, HASH_BITS); 695 | 696 | /* 697 | * Only upper HASH_BITS are used 698 | * which point to specific bit in the hash registers 699 | */ 700 | hash = (crc >> 26) & 0x3F; 701 | 702 | if (hash > 31) { 703 | ETH_HashTableHigh |= 1 << (hash - 32); 704 | EthHandle.Instance->MACHTHR = ETH_HashTableHigh; 705 | } else { 706 | ETH_HashTableLow |= 1 << hash; 707 | EthHandle.Instance->MACHTLR = ETH_HashTableLow; 708 | } 709 | } 710 | #endif /* LWIP_IGMP */ 711 | 712 | 713 | #ifdef ETH_INPUT_USE_IT 714 | /** 715 | * @brief Ethernet Rx Transfer completed callback 716 | * @param heth: ETH handle 717 | * @retval None 718 | */ 719 | void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth) 720 | { 721 | ethernetif_input(&gnetif); 722 | } 723 | 724 | /** 725 | * @brief This function handles Ethernet interrupt request. 726 | * @param None 727 | * @retval None 728 | */ 729 | void ETH_IRQHandler(void) 730 | { 731 | HAL_ETH_IRQHandler(&EthHandle); 732 | } 733 | #endif /* ETH_INPUT_USE_IT */ 734 | 735 | #ifdef __cplusplus 736 | } 737 | #endif 738 | /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 739 | -------------------------------------------------------------------------------- /src/utility/ethernetif.h: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file ethernetif.h 4 | * @author MCD Application Team & Wi6Labs 5 | * @version V1.5.0 6 | * @date 20-June-2017 7 | * @brief Ethernet interface header file. 8 | ****************************************************************************** 9 | * @attention 10 | * 11 | *

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

13 | * 14 | * Redistribution and use in source and binary forms, with or without 15 | * modification, are permitted, provided that the following conditions are met: 16 | * 17 | * 1. Redistribution of source code must retain the above copyright notice, 18 | * this list of conditions and the following disclaimer. 19 | * 2. Redistributions in binary form must reproduce the above copyright notice, 20 | * this list of conditions and the following disclaimer in the documentation 21 | * and/or other materials provided with the distribution. 22 | * 3. Neither the name of STMicroelectronics nor the names of other 23 | * contributors to this software may be used to endorse or promote products 24 | * derived from this software without specific written permission. 25 | * 4. This software, including modifications and/or derivative works of this 26 | * software, must execute solely and exclusively on microcontroller or 27 | * microprocessor devices manufactured by or for STMicroelectronics. 28 | * 5. Redistribution and use of this software other than as permitted under 29 | * this license is void and will automatically terminate your rights under 30 | * this license. 31 | * 32 | * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" 33 | * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT 34 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 35 | * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY 36 | * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT 37 | * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 38 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 39 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 40 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 41 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 42 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 43 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 44 | * 45 | ****************************************************************************** 46 | */ 47 | 48 | #ifndef __ETHERNETIF_H__ 49 | #define __ETHERNETIF_H__ 50 | #ifdef __cplusplus 51 | extern "C" { 52 | #endif 53 | #include "lwip/err.h" 54 | #include "lwip/netif.h" 55 | /* Exported types ------------------------------------------------------------*/ 56 | uint8_t ethernetif_is_init(void); 57 | err_t ethernetif_init(struct netif *netif); 58 | void ethernetif_input(struct netif *netif); 59 | void ethernetif_set_link(struct netif *netif); 60 | void ethernetif_update_config(struct netif *netif); 61 | void ethernetif_notify_conn_changed(struct netif *netif); 62 | 63 | void ethernetif_set_mac_addr(const uint8_t *mac); 64 | void ethernetif_get_mac_addr(uint8_t *mac); 65 | 66 | #if LWIP_IGMP 67 | err_t igmp_mac_filter(struct netif *netif, const ip4_addr_t *ip4_addr, netif_mac_filter_action action); 68 | void register_multicast_address(const uint8_t *mac); 69 | #endif 70 | 71 | #ifdef __cplusplus 72 | } 73 | #endif 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /src/utility/stm32_eth.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file stm32_eth.cpp 4 | * @author WI6LABS 5 | * @version V1.0.0 6 | * @date 24-May-2017 7 | * @brief This file implements Ethernet network interface drivers for 8 | * Arduino STM32Ethernet library. 9 | ****************************************************************************** 10 | * @attention 11 | * 12 | *

© COPYRIGHT(c) 2016 STMicroelectronics

13 | * 14 | * Redistribution and use in source and binary forms, with or without modification, 15 | * are permitted provided that the following conditions are met: 16 | * 1. Redistributions of source code must retain the above copyright notice, 17 | * this list of conditions and the following disclaimer. 18 | * 2. Redistributions in binary form must reproduce the above copyright notice, 19 | * this list of conditions and the following disclaimer in the documentation 20 | * and/or other materials provided with the distribution. 21 | * 3. Neither the name of STMicroelectronics nor the names of its contributors 22 | * may be used to endorse or promote products derived from this software 23 | * without specific prior written permission. 24 | * 25 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 26 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 28 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 29 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 31 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 33 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | * 36 | ****************************************************************************** 37 | */ 38 | 39 | #include "Arduino.h" 40 | #include "stm32_eth.h" 41 | #include "lwip/init.h" 42 | #include "lwip/netif.h" 43 | #include "lwip/timeouts.h" 44 | #include "netif/ethernet.h" 45 | #include "ethernetif.h" 46 | #include "lwip/dhcp.h" 47 | #include "lwip/prot/dhcp.h" 48 | #include "lwip/dns.h" 49 | 50 | /* Check ethernet link status every seconds */ 51 | #define TIME_CHECK_ETH_LINK_STATE 500U 52 | 53 | /* Timeout for DNS request */ 54 | #define TIMEOUT_DNS_REQUEST 10000U 55 | 56 | /* Maximum number of retries for DHCP request */ 57 | #define MAX_DHCP_TRIES 4 58 | 59 | /* 60 | * Defined a default timer used to call ethernet scheduler at regular interval 61 | * Could be redefined in the variant 62 | * Note: This is planned to extend Timer API's of the Arduino STM32 core 63 | * They could be used for this library when available 64 | */ 65 | #ifndef DEFAULT_ETHERNET_TIMER 66 | #define DEFAULT_ETHERNET_TIMER TIM14 67 | #warning "Default timer used to call ethernet scheduler at regular interval: TIM14" 68 | #endif 69 | 70 | /* Interrupt priority */ 71 | #ifndef ETH_TIM_IRQ_PRIO 72 | #define ETH_TIM_IRQ_PRIO 15 // Warning: it should be lower prio (higher value) than Systick 73 | #endif 74 | #ifndef ETH_TIM_IRQ_SUBPRIO 75 | #define ETH_TIM_IRQ_SUBPRIO 0 76 | #endif 77 | /* Ethernet configuration: user parameters */ 78 | struct stm32_eth_config { 79 | ip_addr_t ipaddr; 80 | ip_addr_t netmask; 81 | ip_addr_t gw; 82 | }; 83 | 84 | /* Use to give user parameters to netif configuration */ 85 | static struct stm32_eth_config gconfig; 86 | 87 | /* Netif global configuration structure */ 88 | struct netif gnetif; 89 | 90 | /* DHCP periodic timer */ 91 | static uint32_t DHCPfineTimer = 0; 92 | 93 | /* DHCP current state */ 94 | __IO uint8_t DHCP_state = DHCP_OFF; 95 | 96 | /* Set to 1 if user use DHCP to obtain network addresses */ 97 | static uint8_t DHCP_Started_by_user = 0; 98 | 99 | /* Ethernet link status periodic timer */ 100 | static uint32_t gEhtLinkTickStart = 0; 101 | 102 | #if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION <= 0x01060100) 103 | /* Handler for stimer */ 104 | static stimer_t TimHandle; 105 | #else 106 | HardwareTimer *EthTim = NULL; 107 | #endif 108 | 109 | /*************************** Function prototype *******************************/ 110 | static void Netif_Config(void); 111 | static err_t tcp_recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err); 112 | static err_t tcp_sent_callback(void *arg, struct tcp_pcb *tpcb, u16_t len); 113 | static void tcp_err_callback(void *arg, err_t err); 114 | static void TIM_scheduler_Config(void); 115 | #if defined(STM32_CORE_VERSION) && (STM32_CORE_VERSION > 0x01060100) 116 | void _stm32_eth_scheduler(void); 117 | #endif 118 | 119 | /** 120 | * @brief Configure the network interface 121 | * @param None 122 | * @retval None 123 | */ 124 | static void Netif_Config(void) 125 | { 126 | netif_remove(&gnetif); 127 | /* Add the network interface */ 128 | netif_add(&gnetif, &(gconfig.ipaddr), &(gconfig.netmask), &(gconfig.gw), NULL, ðernetif_init, ðernet_input); 129 | 130 | /* Registers the default network interface */ 131 | netif_set_default(&gnetif); 132 | 133 | if (netif_is_link_up(&gnetif)) { 134 | /* When the netif is fully configured this function must be called */ 135 | netif_set_up(&gnetif); 136 | } else { 137 | /* When the netif link is down this function must be called */ 138 | netif_set_down(&gnetif); 139 | } 140 | 141 | #if LWIP_NETIF_LINK_CALLBACK 142 | /* Set the link callback function, this function is called on change of link status */ 143 | netif_set_link_callback(&gnetif, ethernetif_update_config); 144 | #endif /* LWIP_NETIF_LINK_CALLBACK */ 145 | } 146 | 147 | /** 148 | * @brief Scheduler callback. Call by a timer interrupt. 149 | * @param htim: pointer to stimer_t or Hardware Timer 150 | * @retval None 151 | */ 152 | #if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION <= 0x01060100) 153 | static void scheduler_callback(stimer_t *htim) 154 | #elif (STM32_CORE_VERSION <= 0x01080000) 155 | static void scheduler_callback(HardwareTimer *htim) 156 | #else 157 | static void scheduler_callback(void) 158 | #endif 159 | { 160 | #if (STM32_CORE_VERSION <= 0x01080000) 161 | UNUSED(htim); 162 | #endif 163 | _stm32_eth_scheduler(); 164 | } 165 | 166 | #if !defined(STM32_CORE_VERSION) || (STM32_CORE_VERSION <= 0x01060100) 167 | /** 168 | * @brief Enable the timer used to call ethernet scheduler function at regular 169 | * interval. 170 | * @param None 171 | * @retval None 172 | */ 173 | static void TIM_scheduler_Config(void) 174 | { 175 | /* Set TIMx instance. */ 176 | TimHandle.timer = DEFAULT_ETHERNET_TIMER; 177 | /* Timer set to 1ms */ 178 | TimerHandleInit(&TimHandle, (uint16_t)(1000 - 1), ((uint32_t)(getTimerClkFreq(DEFAULT_ETHERNET_TIMER) / (1000000)) - 1)); 179 | attachIntHandle(&TimHandle, scheduler_callback); 180 | } 181 | #else 182 | /** 183 | * @brief Enable the timer used to call ethernet scheduler function at regular 184 | * interval. 185 | * @param None 186 | * @retval None 187 | */ 188 | static void TIM_scheduler_Config(void) 189 | { 190 | /* Configure HardwareTimer */ 191 | EthTim = new HardwareTimer(DEFAULT_ETHERNET_TIMER); 192 | EthTim->setInterruptPriority(ETH_TIM_IRQ_PRIO, ETH_TIM_IRQ_SUBPRIO); 193 | EthTim->setMode(1, TIMER_OUTPUT_COMPARE); 194 | 195 | /* Timer set to 1ms */ 196 | EthTim->setOverflow(1000, MICROSEC_FORMAT); 197 | EthTim->attachInterrupt(scheduler_callback); 198 | EthTim->resume(); 199 | } 200 | #endif 201 | 202 | void stm32_eth_init(const uint8_t *mac, const uint8_t *ip, const uint8_t *gw, const uint8_t *netmask) 203 | { 204 | static uint8_t initDone = 0; 205 | 206 | if (!initDone) { 207 | /* Initialize the LwIP stack */ 208 | lwip_init(); 209 | } 210 | 211 | if (mac != NULL) { 212 | ethernetif_set_mac_addr(mac); 213 | } // else default value is used: MAC_ADDR0 ... MAC_ADDR5 214 | 215 | if (ip != NULL) { 216 | IP_ADDR4(&(gconfig.ipaddr), ip[0], ip[1], ip[2], ip[3]); 217 | } else { 218 | #if LWIP_DHCP 219 | ip_addr_set_zero_ip4(&(gconfig.ipaddr)); 220 | #else 221 | IP_ADDR4(&(gconfig.ipaddr), IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3); 222 | #endif /* LWIP_DHCP */ 223 | } 224 | 225 | if (gw != NULL) { 226 | IP_ADDR4(&(gconfig.gw), gw[0], gw[1], gw[2], gw[3]); 227 | } else { 228 | #if LWIP_DHCP 229 | ip_addr_set_zero_ip4(&(gconfig.gw)); 230 | #else 231 | IP_ADDR4(&(gconfig.gw), GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3); 232 | #endif /* LWIP_DHCP */ 233 | } 234 | 235 | if (netmask != NULL) { 236 | IP_ADDR4(&(gconfig.netmask), netmask[0], netmask[1], netmask[2], netmask[3]); 237 | } else { 238 | #if LWIP_DHCP 239 | ip_addr_set_zero_ip4(&(gconfig.netmask)); 240 | #else 241 | IP_ADDR4(&(gconfig.netmask), NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3); 242 | #endif /* LWIP_DHCP */ 243 | } 244 | 245 | /* Configure the Network interface */ 246 | Netif_Config(); 247 | 248 | if (!initDone) { 249 | // stm32_eth_scheduler() will be called every 1ms. 250 | TIM_scheduler_Config(); 251 | initDone = 1; 252 | } 253 | 254 | /* Reset DHCP if used */ 255 | User_notification(&gnetif); 256 | 257 | /* Update LwIP stack */ 258 | stm32_eth_scheduler(); 259 | } 260 | 261 | /** 262 | * @brief Return Ethernet init status 263 | * @param None 264 | * @retval 1 for initialized, 0 for not initialized 265 | */ 266 | uint8_t stm32_eth_is_init(void) 267 | { 268 | return ethernetif_is_init(); 269 | } 270 | 271 | /** 272 | * @brief Set Ethernet MAC address 273 | * @param mac: mac address 274 | * @retval None 275 | */ 276 | void stm32_eth_set_macaddr(const uint8_t *mac) 277 | { 278 | ethernetif_set_mac_addr(mac); 279 | } 280 | /** 281 | * @brief Return Ethernet MAC address 282 | * @param mac: mac address 283 | * @retval None 284 | */ 285 | void stm32_eth_get_macaddr(uint8_t *mac) 286 | { 287 | return ethernetif_get_mac_addr(mac); 288 | } 289 | 290 | /** 291 | * @brief Return Ethernet link status 292 | * @param None 293 | * @retval 1 for link up, 0 for link down 294 | */ 295 | uint8_t stm32_eth_link_up(void) 296 | { 297 | return netif_is_link_up(&gnetif); 298 | } 299 | 300 | #if defined(STM32_CORE_VERSION) && (STM32_CORE_VERSION > 0x01060100) 301 | /** 302 | * @brief This function generates Timer Update event to force call to _stm32_eth_scheduler(). 303 | * @param None 304 | * @retval None 305 | */ 306 | void stm32_eth_scheduler(void) 307 | { 308 | if (EthTim != NULL) { 309 | EthTim->refresh(); 310 | } 311 | } 312 | 313 | /** 314 | * @brief This function is called solely by Timer callback to avoid race condition. 315 | * @param None 316 | * @retval None 317 | */ 318 | void _stm32_eth_scheduler(void) 319 | #else 320 | /** 321 | * @brief This function is called solely by Timer callback to avoid race condition. 322 | * @param None 323 | * @retval None 324 | */ 325 | void stm32_eth_scheduler(void) 326 | #endif 327 | { 328 | /* Read a received packet from the Ethernet buffers and send it 329 | to the lwIP for handling */ 330 | #ifndef ETH_INPUT_USE_IT 331 | ethernetif_input(&gnetif); 332 | #endif /* ETH_INPUT_USE_IT */ 333 | 334 | /* Check ethernet link status */ 335 | if ((HAL_GetTick() - gEhtLinkTickStart) >= TIME_CHECK_ETH_LINK_STATE) { 336 | ethernetif_set_link(&gnetif); 337 | gEhtLinkTickStart = HAL_GetTick(); 338 | } 339 | 340 | /* Handle LwIP timeouts */ 341 | sys_check_timeouts(); 342 | 343 | #if LWIP_DHCP 344 | stm32_DHCP_Periodic_Handle(&gnetif); 345 | #endif /* LWIP_DHCP */ 346 | } 347 | 348 | #if LWIP_DHCP 349 | 350 | /** 351 | * @brief Returns DHCP activation state 352 | * @param None 353 | * @retval true if DHCP enabled or false if not used 354 | */ 355 | uint8_t stm32_dhcp_started(void) 356 | { 357 | return DHCP_Started_by_user; 358 | } 359 | 360 | /** 361 | * @brief DHCP_Process_Handle 362 | * @param netif pointer to generic data structure used for all lwIP network interfaces 363 | * @retval None 364 | */ 365 | void stm32_DHCP_process(struct netif *netif) 366 | { 367 | struct dhcp *dhcp; 368 | 369 | if (netif_is_link_up(netif)) { 370 | switch (DHCP_state) { 371 | case DHCP_START: { 372 | ip_addr_set_zero_ip4(&netif->ip_addr); 373 | ip_addr_set_zero_ip4(&netif->netmask); 374 | ip_addr_set_zero_ip4(&netif->gw); 375 | DHCP_state = DHCP_WAIT_ADDRESS; 376 | dhcp_start(netif); 377 | DHCP_Started_by_user = 1; 378 | } 379 | break; 380 | 381 | case DHCP_WAIT_ADDRESS: { 382 | if (dhcp_supplied_address(netif)) { 383 | DHCP_state = DHCP_ADDRESS_ASSIGNED; 384 | } else { 385 | dhcp = (struct dhcp *)netif_get_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP); 386 | 387 | /* DHCP timeout */ 388 | if (dhcp->tries > MAX_DHCP_TRIES) { 389 | DHCP_state = DHCP_TIMEOUT; 390 | 391 | // If DHCP address not bind, keep DHCP stopped 392 | DHCP_Started_by_user = 0; 393 | 394 | /* Stop DHCP */ 395 | dhcp_stop(netif); 396 | } 397 | } 398 | } 399 | break; 400 | case DHCP_ASK_RELEASE: { 401 | /* Force release */ 402 | dhcp_release(netif); 403 | dhcp_stop(netif); 404 | DHCP_state = DHCP_OFF; 405 | } 406 | break; 407 | case DHCP_LINK_DOWN: { 408 | /* Stop DHCP */ 409 | dhcp_stop(netif); 410 | DHCP_state = DHCP_OFF; 411 | } 412 | break; 413 | default: break; 414 | } 415 | } else { 416 | DHCP_state = DHCP_OFF; 417 | } 418 | } 419 | 420 | /** 421 | * @brief DHCP periodic check 422 | * @param netif pointer to generic data structure used for all lwIP network interfaces 423 | * @retval None 424 | */ 425 | void stm32_DHCP_Periodic_Handle(struct netif *netif) 426 | { 427 | /* Fine DHCP periodic process every 500ms */ 428 | if (HAL_GetTick() - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS) { 429 | DHCPfineTimer = HAL_GetTick(); 430 | /* process DHCP state machine */ 431 | stm32_DHCP_process(netif); 432 | } 433 | } 434 | 435 | /** 436 | * @brief Inform the local DHCP of our manual IP configuration 437 | * @param None 438 | * @retval None 439 | */ 440 | void stm32_DHCP_manual_config(void) 441 | { 442 | dhcp_inform(&gnetif); 443 | } 444 | 445 | /** 446 | * @brief Return status of the DHCP when renew or rebind 447 | * @param None 448 | * @retval Renew or rebind. Adapted from Arduino Ethernet library. 449 | */ 450 | uint8_t stm32_get_DHCP_lease_state(void) 451 | { 452 | uint8_t res = 0; 453 | struct dhcp *dhcp = (struct dhcp *)netif_get_client_data(&gnetif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP); 454 | 455 | if (dhcp->state == DHCP_STATE_RENEWING) { 456 | res = 2; 457 | } else if (dhcp->state == DHCP_STATE_REBINDING) { 458 | res = 4; 459 | } 460 | 461 | return res; 462 | } 463 | 464 | /** 465 | * @brief Set DHCP state 466 | * @param state: DHCP_START, DHCP_ASK_RELEASE or DHCP_STOP. Others should not be used. 467 | * @retval None 468 | */ 469 | void stm32_set_DHCP_state(uint8_t state) 470 | { 471 | DHCP_state = state; 472 | } 473 | 474 | /** 475 | * @brief Return DHCP state 476 | * @param None 477 | * @retval One of the following state: 478 | DHCP_OFF 479 | DHCP_START 480 | DHCP_WAIT_ADDRESS 481 | DHCP_ADDRESS_ASSIGNED 482 | DHCP_TIMEOUT 483 | DHCP_LINK_DOWN 484 | DHCP_ASK_RELEASE 485 | */ 486 | uint8_t stm32_get_DHCP_state(void) 487 | { 488 | return DHCP_state; 489 | } 490 | 491 | #endif /* LWIP_DHCP */ 492 | 493 | /** 494 | * @brief Converts IP address in readable format for user. 495 | * @param None 496 | * @retval address in uint32_t format 497 | */ 498 | uint32_t stm32_eth_get_ipaddr(void) 499 | { 500 | return ip4_addr_get_u32(&(gnetif.ip_addr)); 501 | } 502 | 503 | /** 504 | * @brief Converts gateway address in readable format for user. 505 | * @param None 506 | * @retval address in uint32_t format 507 | */ 508 | uint32_t stm32_eth_get_gwaddr(void) 509 | { 510 | return ip4_addr_get_u32(&(gnetif.gw)); 511 | } 512 | 513 | /** 514 | * @brief Converts network mask address in readable format for user. 515 | * @param None 516 | * @retval address in uint32_t format 517 | */ 518 | uint32_t stm32_eth_get_netmaskaddr(void) 519 | { 520 | return ip4_addr_get_u32(&(gnetif.netmask)); 521 | } 522 | 523 | /** 524 | * @brief Converts DNS address in readable format for user. 525 | * @param None 526 | * @retval address in uint32_t format 527 | */ 528 | uint32_t stm32_eth_get_dnsaddr(void) 529 | { 530 | const ip_addr_t *tmp = dns_getserver(0); 531 | return ip4_addr_get_u32(tmp); 532 | } 533 | 534 | /** 535 | * @brief Converts DHCP address in readable format for user. 536 | * @param None 537 | * @retval address in uint32_t format 538 | */ 539 | uint32_t stm32_eth_get_dhcpaddr(void) 540 | { 541 | struct dhcp *dhcp = (struct dhcp *)netif_get_client_data(&gnetif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP); 542 | return ip4_addr_get_u32(&(dhcp->server_ip_addr)); 543 | } 544 | 545 | #if LWIP_NETIF_LINK_CALLBACK 546 | 547 | /** 548 | * @brief This function notify user about link status changement. 549 | * @param netif: the network interface 550 | * @retval None 551 | */ 552 | void ethernetif_notify_conn_changed(struct netif *netif) 553 | { 554 | if (netif_is_link_up(netif)) { 555 | /* Update DHCP state machine if DHCP used */ 556 | if (DHCP_Started_by_user == 1) { 557 | DHCP_state = DHCP_START; 558 | } 559 | 560 | /* When the netif is fully configured this function must be called.*/ 561 | netif_set_up(netif); 562 | } else { 563 | /* Update DHCP state machine if DHCP used */ 564 | if (DHCP_Started_by_user == 1) { 565 | DHCP_state = DHCP_LINK_DOWN; 566 | } 567 | 568 | /* When the netif link is down this function must be called.*/ 569 | netif_set_down(netif); 570 | } 571 | } 572 | 573 | #endif /* LWIP_NETIF_LINK_CALLBACK */ 574 | 575 | /** 576 | * @brief Notify the User about the network interface config status 577 | * @param netif: the network interface 578 | * @retval None 579 | */ 580 | void User_notification(struct netif *netif) 581 | { 582 | if (netif_is_up(netif)) { 583 | //Link up 584 | } else { 585 | /* Update DHCP state machine */ 586 | if (DHCP_Started_by_user == 1) { 587 | DHCP_state = DHCP_LINK_DOWN; 588 | } 589 | } 590 | } 591 | 592 | #if LWIP_DNS 593 | 594 | /** 595 | * @brief Initializes DNS 596 | * @param dnsaddr: DNS address 597 | * @retval None 598 | */ 599 | void stm32_dns_init(const uint8_t *dnsaddr) 600 | { 601 | ip_addr_t ip; 602 | 603 | /* DNS initialized by DHCP when call dhcp_start() */ 604 | if (!stm32_dhcp_started()) { 605 | dns_init(); 606 | IP_ADDR4(&ip, dnsaddr[0], dnsaddr[1], dnsaddr[2], dnsaddr[3]); 607 | dns_setserver(0, &ip); 608 | } 609 | } 610 | 611 | /** Callback which is invoked when a hostname is found. 612 | * A function of this type must be implemented by the application using the DNS resolver. 613 | * @param name pointer to the name that was looked up. 614 | * @param ipaddr pointer to an ip_addr_t containing the IP address of the hostname, 615 | * or NULL if the name could not be found (or on any other error). 616 | * @param callback_arg a user-specified callback argument passed to dns_gethostbyname 617 | */ 618 | void dns_callback(const char *name, const ip_addr_t *ipaddr, void *callback_arg) 619 | { 620 | UNUSED(name); 621 | 622 | if (ipaddr != NULL) { 623 | *((uint32_t *)callback_arg) = ip4_addr_get_u32(ipaddr); 624 | } else { 625 | *((uint32_t *)callback_arg) = 0; 626 | } 627 | } 628 | 629 | /** 630 | * Resolve a hostname (string) into an IP address. 631 | * 632 | * @param hostname the hostname that is to be queried 633 | * @param addr pointer to a uint8_t where to store the address 634 | * @return an error code compatible with Arduino Ethernet library 635 | */ 636 | int8_t stm32_dns_gethostbyname(const char *hostname, uint32_t *ipaddr) 637 | { 638 | ip_addr_t iphost; 639 | err_t err; 640 | uint32_t tickstart = 0; 641 | int8_t ret = 0; 642 | 643 | *ipaddr = 0; 644 | err = dns_gethostbyname(hostname, &iphost, &dns_callback, ipaddr); 645 | 646 | switch (err) { 647 | case ERR_OK: 648 | *ipaddr = ip4_addr_get_u32(&iphost); 649 | ret = 1; 650 | break; 651 | 652 | case ERR_INPROGRESS: 653 | tickstart = HAL_GetTick(); 654 | while (*ipaddr == 0) { 655 | stm32_eth_scheduler(); 656 | if ((HAL_GetTick() - tickstart) >= TIMEOUT_DNS_REQUEST) { 657 | ret = -1; 658 | break; 659 | } 660 | } 661 | 662 | if (ret == 0) { 663 | if (*ipaddr == 0) { 664 | ret = -2; 665 | } else { 666 | ret = 1; 667 | } 668 | } 669 | break; 670 | 671 | case ERR_ARG: 672 | ret = -4; 673 | break; 674 | 675 | default: 676 | ret = -4; 677 | break; 678 | } 679 | 680 | return ret; 681 | } 682 | 683 | #endif /* LWIP_DNS */ 684 | 685 | /** 686 | * @brief Converts a uint8_t IP address to a ip_addr_t address 687 | * @param ipu8: pointer to an address to convert 688 | * @param ipaddr: pointer where store the address converted 689 | * @retval pointer to an address in ip_addr_t format 690 | */ 691 | ip_addr_t *u8_to_ip_addr(uint8_t *ipu8, ip_addr_t *ipaddr) 692 | { 693 | IP_ADDR4(ipaddr, ipu8[0], ipu8[1], ipu8[2], ipu8[3]); 694 | return ipaddr; 695 | } 696 | 697 | /** 698 | * @brief Converts a ip_addr_t IP address to a uint32_t address 699 | * @param ipaddr: pointer to an address to convert 700 | * @retval pointer to the address converted 701 | */ 702 | uint32_t ip_addr_to_u32(ip_addr_t *ipaddr) 703 | { 704 | return ip4_addr_get_u32(ipaddr); 705 | } 706 | 707 | /** 708 | * @brief Allocate a pbuf with data pass in parameter 709 | * @param p: pointer to pbuf 710 | * @param buffer: pointer to data to store 711 | * @param size: number of data to store 712 | * @retval pointer to the pbuf allocated 713 | */ 714 | struct pbuf *stm32_new_data(struct pbuf *p, const uint8_t *buffer, size_t size) 715 | { 716 | // Allocate memory if pbuf doesn't exit yet. 717 | if (p == NULL) { 718 | p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM); 719 | 720 | if (p != NULL) { 721 | // Copy data inside pbuf 722 | if (ERR_OK == pbuf_take(p, (uint8_t *)buffer, size)) { 723 | return p; 724 | } else { 725 | pbuf_free(p); 726 | } 727 | } 728 | } 729 | // If pbuf allocated, grow the size of pbuf and add new data 730 | // NOTE: pbuf_realloc can't be used to grow the size of pbuf 731 | else { 732 | struct pbuf *q = pbuf_alloc(PBUF_TRANSPORT, size + p->tot_len, PBUF_RAM); 733 | 734 | if (q != NULL) { 735 | if (ERR_OK == pbuf_copy(q, p)) { 736 | if (ERR_OK == pbuf_take_at(q, (uint8_t *)buffer, size, p->tot_len)) { 737 | pbuf_free(p); 738 | p = q; 739 | return p; 740 | } 741 | } 742 | 743 | pbuf_free(q); 744 | } 745 | } 746 | 747 | return 0; 748 | } 749 | 750 | /** 751 | * @brief Free pbuf 752 | * @param p: pointer to pbuf 753 | * @retval return always NULL 754 | */ 755 | struct pbuf *stm32_free_data(struct pbuf *p) 756 | { 757 | uint16_t n; 758 | 759 | if (p != NULL) { 760 | do { 761 | n = pbuf_free(p); 762 | } while (n == 0); 763 | } 764 | 765 | return NULL; 766 | } 767 | 768 | /** 769 | * @brief This function passes pbuf data to uin8_t buffer. It takes account if 770 | * pbuf is chained. 771 | * @param data pointer to data structure 772 | * @param buffer the buffer where write the data read 773 | * @param size the number of data to read 774 | * @retval number of data read 775 | */ 776 | uint16_t stm32_get_data(struct pbuf_data *data, uint8_t *buffer, size_t size) 777 | { 778 | uint16_t i; 779 | uint16_t offset; 780 | uint16_t nb; 781 | struct pbuf *ptr; 782 | 783 | if ((data->p == NULL) || (buffer == NULL) || (size == 0) || 784 | (data->available == 0) || (data->available > data->p->tot_len)) { 785 | return 0; 786 | } 787 | 788 | nb = 0; 789 | 790 | while ((nb < size) && (data->p != NULL) && (data->available > 0)) { 791 | ptr = data->p; 792 | offset = ptr->tot_len - data->available; 793 | 794 | /* Get data from p */ 795 | for (i = 0; (nb < size) && ((offset + i) < ptr->len) && (data->available > 0); i++) { 796 | buffer[nb] = pbuf_get_at(ptr, offset + i); 797 | nb++; 798 | data->available--; 799 | } 800 | 801 | if (nb < size) { 802 | /* continue with next pbuf in chain (if any) */ 803 | data->p = ptr->next; 804 | 805 | if (data->p != NULL) { 806 | /* increment reference count for p */ 807 | pbuf_ref(data->p); 808 | } 809 | 810 | /* chop first pbuf from chain */ 811 | ptr = stm32_free_data(ptr); 812 | } 813 | } 814 | 815 | if (data->available == 0) { 816 | data->p = stm32_free_data(data->p); 817 | } 818 | 819 | return nb; 820 | } 821 | 822 | #if LWIP_UDP 823 | 824 | /** 825 | * @brief This function is called when an UDP datagram has been received on 826 | * the port UDP_PORT. 827 | * @param arg user supplied argument 828 | * @param pcb the udp_pcb which received data 829 | * @param p the packet buffer that was received 830 | * @param addr the remote IP address from which the packet was received 831 | * @param port the remote port from which the packet was received 832 | * @retval None 833 | */ 834 | void udp_receive_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, 835 | const ip_addr_t *addr, u16_t port) 836 | { 837 | struct udp_struct *udp_arg = (struct udp_struct *)arg; 838 | 839 | /* Send data to the application layer */ 840 | if ((udp_arg != NULL) && (udp_arg->pcb == pcb)) { 841 | // Free the old p buffer if not read 842 | if (udp_arg->data.p != NULL) { 843 | pbuf_free(udp_arg->data.p); 844 | } 845 | 846 | udp_arg->data.p = p; 847 | udp_arg->data.available = p->len; 848 | 849 | ip_addr_copy(udp_arg->ip, *addr); 850 | udp_arg->port = port; 851 | 852 | if (udp_arg->onDataArrival != NULL) { 853 | udp_arg->onDataArrival(); 854 | } 855 | } else { 856 | pbuf_free(p); 857 | } 858 | } 859 | 860 | #endif /* LWIP_UDP */ 861 | 862 | #if LWIP_TCP 863 | 864 | /** 865 | * @brief Function called when TCP connection established 866 | * @param arg: user supplied argument 867 | * @param tpcb: pointer on the connection control block 868 | * @param err: when connection correctly established err should be ERR_OK 869 | * @retval err_t: returned error 870 | */ 871 | err_t tcp_connected_callback(void *arg, struct tcp_pcb *tpcb, err_t err) 872 | { 873 | struct tcp_struct *tcp_arg = (struct tcp_struct *)arg; 874 | 875 | if (err == ERR_OK) { 876 | if ((tcp_arg != NULL) && (tcp_arg->pcb == tpcb)) { 877 | tcp_arg->state = TCP_CONNECTED; 878 | 879 | /* initialize LwIP tcp_recv callback function */ 880 | tcp_recv(tpcb, tcp_recv_callback); 881 | 882 | /* initialize LwIP tcp_sent callback function */ 883 | tcp_sent(tpcb, tcp_sent_callback); 884 | 885 | /* initialize LwIP tcp_err callback function */ 886 | tcp_err(tpcb, tcp_err_callback); 887 | 888 | return ERR_OK; 889 | } else { 890 | /* close connection */ 891 | tcp_connection_close(tpcb, tcp_arg); 892 | 893 | return ERR_ARG; 894 | } 895 | } else { 896 | /* close connection */ 897 | tcp_connection_close(tpcb, tcp_arg); 898 | } 899 | return err; 900 | } 901 | 902 | /** 903 | * @brief This function is the implementation of tcp_accept LwIP callback 904 | * @param arg user supplied argument 905 | * @param newpcb: pointer on tcp_pcb struct for the newly created tcp connection 906 | * @param err: when connection correctly established err should be ERR_OK 907 | * @retval err_t: error status 908 | */ 909 | err_t tcp_accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err) 910 | { 911 | err_t ret_err; 912 | uint8_t accepted; 913 | struct tcp_struct **tcpClient = (struct tcp_struct **)arg; 914 | 915 | /* set priority for the newly accepted tcp connection newpcb */ 916 | tcp_setprio(newpcb, TCP_PRIO_MIN); 917 | 918 | if ((tcpClient != NULL) && (ERR_OK == err)) { 919 | struct tcp_struct *client = (struct tcp_struct *)mem_malloc(sizeof(struct tcp_struct)); 920 | 921 | if (client != NULL) { 922 | client->state = TCP_ACCEPTED; 923 | client->pcb = newpcb; 924 | client->data.p = NULL; 925 | client->data.available = 0; 926 | 927 | /* Looking for an empty socket */ 928 | for (uint16_t i = 0; i < MAX_CLIENT; i++) { 929 | if (tcpClient[i] == NULL) { 930 | tcpClient[i] = client; 931 | accepted = 1; 932 | break; 933 | } 934 | } 935 | 936 | if (accepted) { 937 | /* pass newly allocated client structure as argument to newpcb */ 938 | tcp_arg(newpcb, client); 939 | 940 | /* initialize lwip tcp_recv callback function for newpcb */ 941 | tcp_recv(newpcb, tcp_recv_callback); 942 | 943 | /* initialize lwip tcp_err callback function for newpcb */ 944 | tcp_err(newpcb, tcp_err_callback); 945 | 946 | /* initialize LwIP tcp_sent callback function */ 947 | tcp_sent(newpcb, tcp_sent_callback); 948 | 949 | ret_err = ERR_OK; 950 | } else { 951 | /* close tcp connection */ 952 | tcp_connection_close(newpcb, client); 953 | mem_free(client); 954 | 955 | /* return memory error */ 956 | ret_err = ERR_MEM; 957 | } 958 | } else { 959 | /* close tcp connection */ 960 | tcp_connection_close(newpcb, client); 961 | mem_free(client); 962 | 963 | /* return memory error */ 964 | ret_err = ERR_MEM; 965 | } 966 | } else { 967 | tcp_close(newpcb); 968 | ret_err = ERR_ARG; 969 | } 970 | return ret_err; 971 | } 972 | 973 | /** 974 | * @brief tcp_receiv callback 975 | * @param arg: argument to be passed to receive callback 976 | * @param tpcb: tcp connection control block 977 | * @param err: receive error code 978 | * @retval err_t: returned error 979 | */ 980 | static err_t tcp_recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) 981 | { 982 | struct tcp_struct *tcp_arg = (struct tcp_struct *)arg; 983 | err_t ret_err; 984 | 985 | /* if we receive an empty tcp frame from server => close connection */ 986 | if (p == NULL) { 987 | /* we're done sending, close connection */ 988 | tcp_connection_close(tpcb, tcp_arg); 989 | ret_err = ERR_OK; 990 | } 991 | /* else : a non empty frame was received from echo server but for some reason err != ERR_OK */ 992 | else if (err != ERR_OK) { 993 | /* free received pbuf*/ 994 | if (p != NULL) { 995 | pbuf_free(p); 996 | } 997 | ret_err = err; 998 | } else if ((tcp_arg->state == TCP_CONNECTED) || (tcp_arg->state == TCP_ACCEPTED)) { 999 | /* Acknowledge data reception */ 1000 | tcp_recved(tpcb, p->tot_len); 1001 | 1002 | if (tcp_arg->data.p == NULL) { 1003 | tcp_arg->data.p = p; 1004 | } else { 1005 | pbuf_chain(tcp_arg->data.p, p); 1006 | } 1007 | 1008 | tcp_arg->data.available += p->len; 1009 | ret_err = ERR_OK; 1010 | } 1011 | /* data received when connection already closed */ 1012 | else { 1013 | /* Acknowledge data reception */ 1014 | tcp_recved(tpcb, p->tot_len); 1015 | 1016 | /* free pbuf and do nothing */ 1017 | pbuf_free(p); 1018 | ret_err = ERR_OK; 1019 | } 1020 | return ret_err; 1021 | } 1022 | 1023 | /** 1024 | * @brief This function implements the tcp_sent LwIP callback (called when ACK 1025 | * is received from remote host for sent data) 1026 | * @param arg: pointer on argument passed to callback 1027 | * @param tcp_pcb: tcp connection control block 1028 | * @param len: length of data sent 1029 | * @retval err_t: returned error code 1030 | */ 1031 | static err_t tcp_sent_callback(void *arg, struct tcp_pcb *tpcb, u16_t len) 1032 | { 1033 | struct tcp_struct *tcp_arg = (struct tcp_struct *)arg; 1034 | 1035 | LWIP_UNUSED_ARG(len); 1036 | 1037 | if ((tcp_arg != NULL) && (tcp_arg->pcb == tpcb)) { 1038 | return ERR_OK; 1039 | } 1040 | 1041 | return ERR_ARG; 1042 | } 1043 | 1044 | /** Function prototype for tcp error callback functions. Called when the pcb 1045 | * receives a RST or is unexpectedly closed for any other reason. 1046 | * 1047 | * @note The corresponding pcb is already freed when this callback is called! 1048 | * 1049 | * @param arg Additional argument to pass to the callback function (@see tcp_arg()) 1050 | * @param err Error code to indicate why the pcb has been closed 1051 | * ERR_ABRT: aborted through tcp_abort or by a TCP timer 1052 | * ERR_RST: the connection was reset by the remote host 1053 | */ 1054 | static void tcp_err_callback(void *arg, err_t err) 1055 | { 1056 | struct tcp_struct *tcp_arg = (struct tcp_struct *)arg; 1057 | 1058 | if (tcp_arg != NULL) { 1059 | if (ERR_OK != err) { 1060 | tcp_arg->pcb = NULL; 1061 | tcp_arg->state = TCP_CLOSING; 1062 | } 1063 | } 1064 | } 1065 | 1066 | /** 1067 | * @brief This function is used to close the tcp connection with server 1068 | * @param tpcb: tcp connection control block 1069 | * @param es: pointer on echoclient structure 1070 | * @retval None 1071 | */ 1072 | void tcp_connection_close(struct tcp_pcb *tpcb, struct tcp_struct *tcp) 1073 | { 1074 | /* remove callbacks */ 1075 | tcp_recv(tpcb, NULL); 1076 | tcp_sent(tpcb, NULL); 1077 | tcp_poll(tpcb, NULL, 0); 1078 | tcp_err(tpcb, NULL); 1079 | tcp_accept(tpcb, NULL); 1080 | 1081 | /* close tcp connection */ 1082 | tcp_close(tpcb); 1083 | 1084 | tcp->pcb = NULL; 1085 | tcp->state = TCP_CLOSING; 1086 | } 1087 | 1088 | #endif /* LWIP_TCP */ 1089 | -------------------------------------------------------------------------------- /src/utility/stm32_eth.h: -------------------------------------------------------------------------------- 1 | /** 2 | ****************************************************************************** 3 | * @file stm32_eth.h 4 | * @author WI6LABS 5 | * @version V1.0.0 6 | * @date 24-May-2017 7 | * @brief Include stm32_eth source files 8 | ****************************************************************************** 9 | * @attention 10 | * 11 | *

© COPYRIGHT(c) 2016 STMicroelectronics

12 | * 13 | * Redistribution and use in source and binary forms, with or without modification, 14 | * are permitted provided that the following conditions are met: 15 | * 1. Redistributions of source code must retain the above copyright notice, 16 | * this list of conditions and the following disclaimer. 17 | * 2. Redistributions in binary form must reproduce the above copyright notice, 18 | * this list of conditions and the following disclaimer in the documentation 19 | * and/or other materials provided with the distribution. 20 | * 3. Neither the name of STMicroelectronics nor the names of its contributors 21 | * may be used to endorse or promote products derived from this software 22 | * without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | * 35 | ****************************************************************************** 36 | */ 37 | 38 | #ifndef __STM32_ETH_H__ 39 | #define __STM32_ETH_H__ 40 | 41 | /* Includes ------------------------------------------------------------------*/ 42 | #include "stm32_def.h" 43 | #include "lwip/ip_addr.h" 44 | #include "lwip/dhcp.h" 45 | #include "lwip/udp.h" 46 | #include "lwip/tcp.h" 47 | #include "lwip/opt.h" 48 | #include 49 | 50 | /* Exported types ------------------------------------------------------------*/ 51 | /* TCP connection state */ 52 | typedef enum { 53 | TCP_NONE = 0, 54 | TCP_CONNECTED, 55 | TCP_RECEIVED, 56 | TCP_SENT, 57 | TCP_ACCEPTED, 58 | TCP_CLOSING, 59 | } tcp_client_states; 60 | 61 | /* Struct to store received data */ 62 | struct pbuf_data { 63 | struct pbuf *p; // the packet buffer that was received 64 | uint16_t available; // number of data 65 | }; 66 | 67 | /* UDP structure */ 68 | struct udp_struct { 69 | struct udp_pcb *pcb; /* pointer on the current udp_pcb */ 70 | struct pbuf_data data; 71 | ip_addr_t ip; // the remote IP address from which the packet was received 72 | u16_t port; // the remote port from which the packet was received 73 | std::function onDataArrival; 74 | }; 75 | 76 | /* TCP structure */ 77 | struct tcp_struct { 78 | struct tcp_pcb *pcb; /* pointer on the current tcp_pcb */ 79 | struct pbuf_data data; 80 | tcp_client_states state; /* current connection state */ 81 | }; 82 | 83 | /* Exported constants --------------------------------------------------------*/ 84 | /*Static IP ADDRESS: IP_ADDR0.IP_ADDR1.IP_ADDR2.IP_ADDR3 */ 85 | #define IP_ADDR0 (uint8_t) 192 86 | #define IP_ADDR1 (uint8_t) 168 87 | #define IP_ADDR2 (uint8_t) 0 88 | #define IP_ADDR3 (uint8_t) 10 89 | 90 | /*NETMASK*/ 91 | #define NETMASK_ADDR0 (uint8_t) 255 92 | #define NETMASK_ADDR1 (uint8_t) 255 93 | #define NETMASK_ADDR2 (uint8_t) 255 94 | #define NETMASK_ADDR3 (uint8_t) 0 95 | 96 | /*Gateway Address*/ 97 | #define GW_ADDR0 (uint8_t) 192 98 | #define GW_ADDR1 (uint8_t) 168 99 | #define GW_ADDR2 (uint8_t) 0 100 | #define GW_ADDR3 (uint8_t) 1 101 | 102 | /* DHCP process states */ 103 | #define DHCP_OFF (uint8_t) 0 104 | #define DHCP_START (uint8_t) 1 105 | #define DHCP_WAIT_ADDRESS (uint8_t) 2 106 | #define DHCP_ADDRESS_ASSIGNED (uint8_t) 3 107 | #define DHCP_TIMEOUT (uint8_t) 4 108 | #define DHCP_LINK_DOWN (uint8_t) 5 109 | #define DHCP_ASK_RELEASE (uint8_t) 6 110 | 111 | /* Maximum number of client per server */ 112 | #define MAX_CLIENT 32 113 | 114 | #ifdef ETH_INPUT_USE_IT 115 | extern struct netif gnetif; 116 | #endif 117 | 118 | 119 | /* Exported functions ------------------------------------------------------- */ 120 | void stm32_eth_init(const uint8_t *mac, const uint8_t *ip, const uint8_t *gw, const uint8_t *netmask); 121 | uint8_t stm32_eth_is_init(void); 122 | void stm32_eth_get_macaddr(uint8_t *mac); 123 | void stm32_eth_set_macaddr(const uint8_t *mac); 124 | uint8_t stm32_eth_link_up(void); 125 | void stm32_eth_scheduler(void); 126 | 127 | void User_notification(struct netif *netif); 128 | 129 | #if LWIP_DHCP 130 | void stm32_DHCP_Process(struct netif *netif); 131 | void stm32_DHCP_Periodic_Handle(struct netif *netif); 132 | void stm32_DHCP_manual_config(void); 133 | uint8_t stm32_get_DHCP_lease_state(void); 134 | void stm32_set_DHCP_state(uint8_t state); 135 | uint8_t stm32_get_DHCP_state(void); 136 | uint8_t stm32_dhcp_started(void); 137 | #else 138 | #error "LWIP_DHCP must be enabled in lwipopts.h" 139 | #endif 140 | 141 | #if LWIP_DNS 142 | void stm32_dns_init(const uint8_t *dnsaddr); 143 | int8_t stm32_dns_gethostbyname(const char *hostname, uint32_t *ipaddr); 144 | #else 145 | #error "LWIP_DNS must be enabled in lwipopts.h" 146 | #endif 147 | 148 | #if LWIP_UDP 149 | void udp_receive_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, 150 | const ip_addr_t *addr, u16_t port); 151 | #else 152 | #error "LWIP_UDP must be enabled in lwipopts.h" 153 | #endif 154 | 155 | uint32_t stm32_eth_get_ipaddr(void); 156 | uint32_t stm32_eth_get_gwaddr(void); 157 | uint32_t stm32_eth_get_netmaskaddr(void); 158 | uint32_t stm32_eth_get_dnsaddr(void); 159 | uint32_t stm32_eth_get_dhcpaddr(void); 160 | 161 | struct pbuf *stm32_new_data(struct pbuf *p, const uint8_t *buffer, size_t size); 162 | struct pbuf *stm32_free_data(struct pbuf *p); 163 | uint16_t stm32_get_data(struct pbuf_data *data, uint8_t *buffer, size_t size); 164 | 165 | ip_addr_t *u8_to_ip_addr(uint8_t *ipu8, ip_addr_t *ipaddr); 166 | uint32_t ip_addr_to_u32(ip_addr_t *ipaddr); 167 | 168 | #if LWIP_TCP 169 | err_t tcp_connected_callback(void *arg, struct tcp_pcb *tpcb, err_t err); 170 | err_t tcp_accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err); 171 | void tcp_connection_close(struct tcp_pcb *tpcb, struct tcp_struct *tcp); 172 | #else 173 | #error "LWIP_TCP must be enabled in lwipopts.h" 174 | #endif 175 | 176 | #endif /* __STM32_ETH_H__ */ 177 | --------------------------------------------------------------------------------