diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/README.md b/STM32F1/libraries/Serasidis_EtherCard_STM/README.md new file mode 100644 index 0000000..aa366ee --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/README.md @@ -0,0 +1,45 @@ +# EtherCard + +**EtherCard** is a driver for the ENC28J60 chip, compatible with Arduino IDE 1.6.3 (or later). +Adapted and extended from code written by Guido Socher and Pascal Stang. + +License: GPL2 + +The documentation for this library is at http://jeelabs.net/pub/docs/ethercard/. + +## Physical Installation + +### PIN Connections (Using STM32F103): + +|ENC28J60|STM32F103| +|:------:|:-----:| +|Vcc|3.3V| +|SCK|PA5| +|SO|PA6| +|SI|PA7| +|CS|PA8| # Selectable with the ether.begin() function +|RST|3.3V| + +The **CS** pin is selectable with the ether.begin() function. + +e.g. If you want to use the **PA9** as **CS** pin then you can initiallize the ethernet module inside the sketch as: + +`if (ether.begin(sizeof Ethernet::buffer, mymac) == 0,`**`PA9`**`) ` + +By default the pin **PA8** is used as **CS** (you don't have to initiallize it). + + +## Credits + +The **EtherCard** library was written by [Jean-Claude Wippler][F] and ported to **STM32F103** by [Vassilis Serasidis][V] + +## Support + +That library works perfect on Arduino IDE 1.6.3 (or later) with [Arduino_STM32 project][S] installed. + +The Arduino STM32 project has a forum ([STM32duino][F]) also for getting or for providing help to people that are interested in that project . + +[F]: https://github.com/jcw +[S]: https://github.com/rogerclarkmelbourne/Arduino_STM32 +[F]: http://www.stm32duino.com +[V]: https://github.com/Serasidis diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/JeeUdp/JeeUdp.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/JeeUdp/JeeUdp.ino new file mode 100644 index 0000000..f22c538 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/JeeUdp/JeeUdp.ino @@ -0,0 +1,320 @@ +// Collect RF12 packets and send them on as UDP collectd packets on Ethernet. +// 2010-05-20 http://opensource.org/licenses/mit-license.php + +// This sketch is derived from RF12eth.pde (and etherNode.ino): +// May 2010, Andras Tucsni, http://opensource.org/licenses/mit-license.php + +#include +#include +#include + +#define DEBUG 1 // set to 1 to display free RAM on web page +#define SERIAL 1 // set to 1 to show incoming requests on serial port + +#define CONFIG_EEPROM_ADDR ((byte*) 0x10) + +// configuration, as stored in EEPROM +struct Config { + byte band; + byte group; + byte collect; + word port; + byte valid; // keep this as last byte +} config; + +// ethernet interface mac address - must be unique on your network +static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; + +// buffer for an outgoing data packet +static byte outBuf[RF12_MAXDATA], outDest; +static char outCount = -1; + +// this buffer will be used to construct a collectd UDP packet +static byte collBuf [200], collPos; + +#define NUM_MESSAGES 3 // Number of messages saved in history +#define MESSAGE_TRUNC 15 // Truncate message payload to reduce memory use + +static BufferFiller bfill; // used as cursor while filling the buffer + +static byte history_rcvd[NUM_MESSAGES][MESSAGE_TRUNC+1]; //history record +static byte history_len[NUM_MESSAGES]; // # of RF12 messages+header in history +static byte next_msg; // pointer to next rf12rcvd line +static word msgs_rcvd; // total number of lines received modulo 10,000 + +byte Ethernet::buffer[700]; // tcp/ip send and receive buffer + +static void loadConfig () { + for (byte i = 0; i < sizeof config; ++i) + ((byte*) &config)[i] = eeprom_read_byte(CONFIG_EEPROM_ADDR + i); + if (config.valid != 253) { + config.valid = 253; + config.band = 8; + config.group = 1; + config.collect = 1; + config.port = 25827; + } + byte freq = config.band == 4 ? RF12_433MHZ : + config.band == 8 ? RF12_868MHZ : + RF12_915MHZ; + rf12_initialize(31, freq, config.group); +} + +static void saveConfig () { + for (byte i = 0; i < sizeof config; ++i) + eeprom_write_byte(CONFIG_EEPROM_ADDR + i, ((byte*) &config)[i]); +} + +#if DEBUG +static int freeRam () { +extern int __heap_start, *__brkval; +int v; +return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); +} +#endif + +void setup (){ + Serial.begin(57600); + Serial.println("\n[JeeUdp]"); + loadConfig(); + if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) + Serial.println( "Failed to access Ethernet controller"); + if (!ether.dhcpSetup()) + Serial.println("DHCP failed"); + ether.printIp("IP: ", ether.myip); +} + +const char okHeader[] PROGMEM = + "HTTP/1.0 200 OK\r\n" + "Content-Type: text/html\r\n" + "Pragma: no-cache\r\n" +; + +static void homePage (BufferFiller& buf) { + word mhz = config.band == 4 ? 433 : config.band == 8 ? 868 : 915; + buf.emit_p(PSTR("$F\r\n" + "RF12 JeeUdp" + "

RF12 JeeUdp @ $D - RF12 @ $D.$D

" + "Configure - Send Packet" + "

Last $D messages:

" + "
"), okHeader, config.port, mhz, config.group, NUM_MESSAGES);
+  for (byte i = 0; i < NUM_MESSAGES; ++i) {
+    byte j = (next_msg + i) % NUM_MESSAGES;
+    if (history_len[j] > 0) {
+      word n = msgs_rcvd - NUM_MESSAGES + i;
+      buf.emit_p(PSTR("\n$D$D$D$D: OK"), // hack, to show leading zero's
+                          n/1000, (n/100) % 10, (n/10) % 10, n % 10);
+      for (byte k = 0; k < history_len[j]; ++k)
+        buf.emit_p(PSTR(" $D"), history_rcvd[j][k]);
+    }
+  }
+  long t = millis() / 1000;
+  word h = t / 3600;
+  byte m = (t / 60) % 60;
+  byte s = t % 60;
+  buf.emit_p(PSTR(
+    "
" + "Uptime is $D$D:$D$D:$D$D"), h/10, h%10, m/10, m%10, s/10, s%10); +#if DEBUG + buf.emit_p(PSTR(" ($D bytes free)"), freeRam()); +#endif +} + +static int getIntArg(const char* data, const char* key, int value =-1) { + char temp[10]; + if (ether.findKeyVal(data + 7, temp, sizeof temp, key) > 0) + value = atoi(temp); + return value; +} + +static void configPage (const char* data, BufferFiller& buf) { + // pick up submitted data, if present + if (data[6] == '?') { + byte b = getIntArg(data, "b", 8); + byte g = getIntArg(data, "g", 1); + byte c = getIntArg(data, "c", 0); + word p = getIntArg(data, "p", 25827); + if (1 <= g && g <= 250 && 1024 <= p && p <= 30000) { + // store values as new settings + config.band = b; + config.group = g; + config.collect = c; + config.port = p; + saveConfig(); + // re-init RF12 driver + loadConfig(); + // clear history + memset(history_len, 0, sizeof history_len); + // redirect to the home page + buf.emit_p(PSTR( + "HTTP/1.0 302 found\r\n" + "Location: /\r\n" + "\r\n")); + return; + } + } + // else show a configuration form + buf.emit_p(PSTR("$F\r\n" + "

Server node configuration

" + "
" + "

" + "Freq band (4, 8, or 9)
" + "Net group (1..250)
" + "Collect mode: " + "(don't send ACKs)

" + "UDP Port (1024..30000)" + "

" + "" + "
"), okHeader, config.band, config.group, + config.collect ? "CHECKED" : "", + config.port); +} + +static void sendPage (const char* data, BufferFiller& buf) { + // pick up submitted data, if present + const char* p = strstr(data, "b="); + byte d = getIntArg(data, "d"); + if (data[6] == '?' && p != 0 && 0 <= d && d <= 31) { + // prepare to send data as soon as possible in loop() + outDest = d & RF12_HDR_MASK ? RF12_HDR_DST | d : 0; + outCount = 0; + // convert the input string to a number of decimal data bytes in outBuf + ++p; + while (*p != 0 && *p != '&') { + outBuf[outCount] = 0; + while ('0' <= *++p && *p <= '9') + outBuf[outCount] = 10 * outBuf[outCount] + (*p - '0'); + ++outCount; + } +#if SERIAL + Serial.print("Send to "); + Serial.print(outDest, DEC); + Serial.print(':'); + for (byte i = 0; i < outCount; ++i) { + Serial.print(' '); + Serial.print(outBuf[i], DEC); + } + Serial.println(); +#endif + // redirect to home page + buf.emit_p(PSTR( + "HTTP/1.0 302 found\r\n" + "Location: /\r\n" + "\r\n")); + return; + } + // else show a send form + buf.emit_p(PSTR("$F\r\n" + "

Send a wireless data packet

" + "
" + "

" + "Data bytes (decimal)
" + "Destination node " + "(1..31, or 0 to broadcast)
" + "

" + "" + "
"), okHeader); +} + +static void collectTypeLen (word type, word len) { + len += 4; + collBuf[collPos++] = type >> 8; + collBuf[collPos++] = (byte) type; + collBuf[collPos++] = len >> 8; + collBuf[collPos++] = (byte) len; +} + +static void collectStr (word type, const char* data) { + word len = strlen(data) + 1; + collectTypeLen(type, len); + strcpy((char*) collBuf + collPos, data); + collPos += len; +} + +static void collectPayload (word type) { + // Copy the received RF12 data into a as many values as needed. + byte num = rf12_len / 8 + 1; // this many values will be needed + collectTypeLen(type, 2 + 9 * num); + collBuf[collPos++] = 0; + collBuf[collPos++] = num; + for (byte i = 0; i < num; ++i) + collBuf[collPos++] = 0; // counter + for (char i = 0; i < 8 * num; ++i) // include -1, i.e. the length byte + collBuf[collPos++] = i <= rf12_len ? rf12_data[i-1] : 0; +} + +static void forwardToUDP () { + static byte destIp[] = { 239,192,74,66 }; // UDP multicast address + char buf[10]; + + collPos = 0; + collectStr(0x0000, "JeeUdp"); + collectStr(0x0002, "RF12"); + word mhz = config.band == 4 ? 433 : config.band == 8 ? 868 : 915; + sprintf(buf, "%d.%d", mhz, config.group); + collectStr(0x0003, buf); + collectStr(0x0004, "OK"); + sprintf(buf, "%d", rf12_hdr); + collectStr(0x0005, buf); + collectPayload(0x0006); + + ether.sendUdp ((char*) collBuf, collPos, 23456, destIp, config.port); +#if SERIAL + Serial.println("UDP sent"); +#endif +} + +void loop (){ + word len = ether.packetReceive(); + word pos = ether.packetLoop(len); + // check if valid tcp data is received + if (pos) { + bfill = ether.tcpOffset(); + char* data = (char *) Ethernet::buffer + pos; +#if SERIAL + Serial.println(data); +#endif + // receive buf hasn't been clobbered by reply yet + if (strncmp("GET / ", data, 6) == 0) + homePage(bfill); + else if (strncmp("GET /c", data, 6) == 0) + configPage(data, bfill); + else if (strncmp("GET /s", data, 6) == 0) + sendPage(data, bfill); + else + bfill.emit_p(PSTR( + "HTTP/1.0 401 Unauthorized\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "

401 Unauthorized

")); + ether.httpServerReply(bfill.position()); // send web page data + } + + // RFM12 loop runner, don't report acks + if (rf12_recvDone() && rf12_crc == 0 && (rf12_hdr & RF12_HDR_CTL) == 0) { + history_rcvd[next_msg][0] = rf12_hdr; + for (byte i = 0; i < rf12_len; ++i) + if (i < MESSAGE_TRUNC) + history_rcvd[next_msg][i+1] = rf12_data[i]; + history_len[next_msg] = rf12_len < MESSAGE_TRUNC ? rf12_len+1 + : MESSAGE_TRUNC+1; + next_msg = (next_msg + 1) % NUM_MESSAGES; + msgs_rcvd = (msgs_rcvd + 1) % 10000; + + if (RF12_WANTS_ACK && !config.collect) { +#if SERIAL + Serial.println(" -> ack"); +#endif + rf12_sendStart(RF12_ACK_REPLY, 0, 0); + } + + forwardToUDP(); + } + + // send a data packet out if requested + if (outCount >= 0 && rf12_canSend()) { + rf12_sendStart(outDest, outBuf, outCount, 1); + outCount = -1; + } +} + diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/SSDP/SSDP.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/SSDP/SSDP.ino new file mode 100644 index 0000000..dd0778d --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/SSDP/SSDP.ino @@ -0,0 +1,145 @@ +#include |Mac adress| +#include + +const char SSDP_RESPONSE[] PROGMEM = "HTTP/1.1 200 OK\r\nCACHE-CONTROL: max-age=1200\r\nEXT:\r\nSERVER:Arduino\r\nST: upnp:rootdevice\r\nUSN: uuid:abcdefgh-7dec-11d0-a765-7499692d3040\r\nLOCATION: http://"; //dont forget our mac adress USN: uuid:abcdefgh-7dec-11d0-a765-Mac addr +const char SSDP_RESPONSE_XML[] PROGMEM = "/??\r\n\r\n"; // here is the adress of xml file /?? in this exemple but you could use another /upnp.xml\r\n\r\n +const char XML_DESCRIP[] PROGMEM = "HTTP/1.1 200 OK\r\nContent-Type: text/xml\r\n\r\n\rurn:schemas-upnp-org:device:BinaryLight:1/ArduinoFredycpuhttp://fredycpu.pro1uuid:abcdefgh-7dec-11d0-a765-7499692d3040 "; +const char SSDP_NOTIFY[] PROGMEM = "NOTIFY * HTTP/1.1\r\nHOST: 239.255.255.250:1900\r\nCACHE-CONTROL: max-age=1200\r\nNT: upnp:rootdevice\r\nUSN: uuid:abcdefga-7dec-11d0-a765-7499692d3040::upnp:rootdevice\r\nNTS: ssdp:alive\r\nSERVER: Arduino UPnP/1.0\r\nLOCATION: http://"; //dont forget our mac adress USN: uuid:abcdefgh-7dec-11d0-a765-Mac addr +// in XML_DESCRIP // urn:schemas-upnp-org:device:BinaryLight:1 // declare as home automation +// in XML_DESCRIP // Arduino // declare the name of the service here Arduino +// in XML_DESCRIP // / // adress of the page who would opened on service double click ,you could use http://ip but if you use dhcp it's better so and dont wase memory +// this is the entire protocol, but you can try to use SSDP_NOTIFY as SSDP_RESPONSE with most systems will work and you can free a bit of flash mem. +static byte myip[] = { + 192,168,1,204 }; +static byte gwip[] = { + 192,168,1,1 }; +static byte ssdp[] = { + 239,255,255,250 }; +static byte mymac[] = { + 0x74,0x99,0x69,0x2D,0x30,0x40 }; // if you change it you must update SSDP_RESPONSE and XML_DESCRIP +byte Ethernet::buffer[750]; // tcp ip send and receive buffer +unsigned long timer=9999; +const char pageA[] PROGMEM = +"HTTP/1.0 200 OK\r\n" +"Content-Type: text/html\r\n" +"\r\n" +"" +"" +"multipackets Test" +"" +"" +"Start here
" +"Image test
" +"

packet 1

" +"

" +"the first packet send " +"

" +; +const char pageB[] PROGMEM = +"

packet 2

" +"

" +"if you read this it mean it works" +"

" +; +const char pageC[] PROGMEM = +"

packet 3

" +"

" +"if you read this it mean it works" +"

" +; +const char pageD[] PROGMEM = +"

packet 4

" +"

" +"if you read this it mean it works" +"

" +; + +void setup(){ + ether.begin(sizeof Ethernet::buffer, mymac);// 53 for the mega ethernet shield and 10 for normal ethernet shield + ether.staticSetup(myip, gwip); + ENC28J60::disableMulticast(); //disable multicast filter means enable multicast reception + Serial.begin(57600); +} + +void loop(){ +wait: + word pos = ether.packetLoop(ether.packetReceive()); + // check if valid tcp data is received + if (pos) { + char* data = (char *) Ethernet::buffer + pos; + if (strncmp("GET / ", data, 6) == 0) { + ether.httpServerReplyAck(); // send ack to the request + memcpy_P(ether.tcpOffset(), pageA, sizeof pageA); // send first packet and not send the terminate flag + ether.httpServerReply_with_flags(sizeof pageA - 1,TCP_FLAGS_ACK_V); + memcpy_P(ether.tcpOffset(), pageB, sizeof pageB); // send second packet and not send the terminate flag + ether.httpServerReply_with_flags(sizeof pageB - 1,TCP_FLAGS_ACK_V); + memcpy_P(ether.tcpOffset(), pageC, sizeof pageC); // send thirdt packet and not send the terminate flag + ether.httpServerReply_with_flags(sizeof pageC - 1,TCP_FLAGS_ACK_V); + memcpy_P(ether.tcpOffset(), pageD, sizeof pageD); // send fourth packet and send the terminate flag + ether.httpServerReply_with_flags(sizeof pageD - 1,TCP_FLAGS_ACK_V|TCP_FLAGS_FIN_V); + goto wait; + } + if (strncmp("GET /??", data, 7) == 0) { // description of services (normaly an xml file but here .....) + ether.httpServerReplyAck(); + memcpy_P(Ethernet::buffer + TCP_OPTIONS_P,XML_DESCRIP, sizeof XML_DESCRIP); + ether.httpServerReply_with_flags(sizeof XML_DESCRIP - 1 ,TCP_FLAGS_ACK_V|TCP_FLAGS_FIN_V); + goto wait; + } + if (strncmp("M-SEARCH", data, 8) == 0) { // service discovery request comes here (udp protocol) + ssdpresp(); + goto wait; + } + } + if (((millis()-timer)>50000)||(timer>millis())) { + timer=millis(); + ssdpnotify(); + } +} +void ssdpresp() { //response to m-search + byte ip_dst[4]; + unsigned int port_dst=Ethernet::buffer[34]*256+Ethernet::buffer[35];//extract source port of request + for( int i=0; i<4;i++) { //extract source IP of request + ip_dst[i]=Ethernet::buffer[i+26]; + } + int udppos = UDP_DATA_P; + + EtherCard::udpPrepare(1900,ip_dst,port_dst); + memcpy_P(Ethernet::buffer + udppos, SSDP_RESPONSE, sizeof SSDP_RESPONSE); + udppos = udppos + sizeof SSDP_RESPONSE-1; + addip(udppos); +} + +void ssdpnotify() { //Notification + int udppos = UDP_DATA_P; + EtherCard::udpPrepare(1900,ssdp,1900); + memcpy_P(Ethernet::buffer + udppos, SSDP_NOTIFY, sizeof SSDP_NOTIFY); +udppos = udppos + sizeof SSDP_NOTIFY-1; +addip(udppos); +} + +void addip(int udppos) { // add current ip to the request and send it + int adr; + for(int i=0;i<4;i++) { // extract the current ip of arduino + adr = ether.myip[i]/100; + if (adr) { + Ethernet::buffer[udppos]=adr+48; + udppos++; + } + adr=(ether.myip[i]%100)/10; + if (adr) { + Ethernet::buffer[udppos]=adr+48; + udppos++; + } + adr=ether.myip[i]%10; + Ethernet::buffer[udppos]=adr+48; + udppos++; + Ethernet::buffer[udppos]=46; + udppos++; //"." + } + udppos--;//erase the last point + memcpy_P(Ethernet::buffer + udppos,SSDP_RESPONSE_XML,sizeof SSDP_RESPONSE_XML); + udppos = udppos + sizeof SSDP_RESPONSE_XML; + udppos--; + EtherCard::udpTransmit(udppos-UDP_DATA_P); // send all to the computer who make the request on her ip and port who make the request +} + diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/backSoon/backSoon.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/backSoon/backSoon.ino new file mode 100644 index 0000000..9d7834e --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/backSoon/backSoon.ino @@ -0,0 +1,84 @@ +// Present a "Will be back soon web page", as stand-in webserver. +// 2011-01-30 http://opensource.org/licenses/mit-license.php +// +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +// +// PIN Connections (Using STM32F103): +// +// ENC28J60 - STM32F103 +// VCC - 3.3V +// GND - GND +// SCK - Pin PA5 +// SO - Pin PA6 +// SI - Pin PA7 +// CS - Pin PA8 +//----------------------------------------------------------------- +// +// + +#include +#include + +#define STATIC 0 // set to 1 to disable DHCP (adjust myip/gwip values below) + +#if STATIC +// ethernet interface ip address +static byte myip[] = { 192,168,1,203 }; +// gateway ip address +static byte gwip[] = { 192,168,1,1 }; +#endif + +// ethernet mac address - must be unique on your network +static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; + +byte Ethernet::buffer[500]; // tcp/ip send and receive buffer + +const char page[] PROGMEM = +"HTTP/1.0 503 Service Unavailable\r\n" +"Content-Type: text/html\r\n" +"Retry-After: 600\r\n" +"\r\n" +"" + "" + "Service Temporarily Unavailable" + "" + "" + "

This service is currently unavailable

" + "

" + "The main server is currently off-line.
" + "Please try again later." + "

" + "" +"" +; + +void setup(){ + Serial.begin(57600); + delay(10); + Serial.println("\n[backSoon]"); + + if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) + Serial.println( "Failed to access Ethernet controller"); +#if STATIC + ether.staticSetup(myip, gwip); +#else + if (!ether.dhcpSetup()) + Serial.println("DHCP failed"); +#endif + + ether.printIp("IP: ", ether.myip); + ether.printIp("GW: ", ether.gwip); + ether.printIp("DNS: ", ether.dnsip); +} + +void loop(){ + // wait for an incoming TCP packet, but ignore its contents + if (ether.packetLoop(ether.packetReceive())) { + memcpy_P(ether.tcpOffset(), page, sizeof page); + ether.httpServerReply(sizeof page - 1); + } +} \ No newline at end of file diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/etherNode/etherNode.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/etherNode/etherNode.ino new file mode 100644 index 0000000..050f7fb --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/etherNode/etherNode.ino @@ -0,0 +1,295 @@ +// Arduino demo sketch for testing RFM12B + ethernet +// 2010-05-20 http://opensource.org/licenses/mit-license.php + +// Listens for RF12 messages and displays valid messages on a webpage +// Memory usage exceeds 1K, so use Atmega328 or decrease history/buffers +// +// This sketch is derived from RF12eth.pde: +// May 2010, Andras Tucsni, http://opensource.org/licenses/mit-license.php +// +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +// +// PIN Connections (Using STM32F103): +// +// ENC28J60 - STM32F103 +// VCC - 3.3V +// GND - GND +// SCK - Pin PA5 +// SO - Pin PA6 +// SI - Pin PA7 +// CS - Pin PA8 +//----------------------------------------------------------------- +// +// + +#include +#include +#include + +#define DEBUG 1 // set to 1 to display free RAM on web page +#define SERIAL 0 // set to 1 to show incoming requests on serial port + +#define CONFIG_EEPROM_ADDR ((byte*) 0x10) + +// configuration, as stored in EEPROM +struct Config { + byte band; + byte group; + byte collect; + word refresh; + byte valid; // keep this as last byte +} config; + +// ethernet interface mac address - must be unique on your network +static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; + +// buffer for an outgoing data packet +static byte outBuf[RF12_MAXDATA], outDest; +static char outCount = -1; + +#define NUM_MESSAGES 10 // Number of messages saved in history +#define MESSAGE_TRUNC 15 // Truncate message payload to reduce memory use + +static BufferFiller bfill; // used as cursor while filling the buffer + +static byte history_rcvd[NUM_MESSAGES][MESSAGE_TRUNC+1]; //history record +static byte history_len[NUM_MESSAGES]; // # of RF12 messages+header in history +static byte next_msg; // pointer to next rf12rcvd line +static word msgs_rcvd; // total number of lines received modulo 10,000 + +byte Ethernet::buffer[1000]; // tcp/ip send and receive buffer + +static void loadConfig() { + for (byte i = 0; i < sizeof config; ++i) + ((byte*) &config)[i] = eeprom_read_byte(CONFIG_EEPROM_ADDR + i); + if (config.valid != 253) { + config.valid = 253; + config.band = 8; + config.group = 1; + config.collect = 1; + config.refresh = 5; + } + byte freq = config.band == 4 ? RF12_433MHZ : + config.band == 8 ? RF12_868MHZ : + RF12_915MHZ; + rf12_initialize(31, freq, config.group); +} + +static void saveConfig() { + for (byte i = 0; i < sizeof config; ++i) + eeprom_write_byte(CONFIG_EEPROM_ADDR + i, ((byte*) &config)[i]); +} + +#if DEBUG +static int freeRam () { + extern int __heap_start, *__brkval; + int v; + return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); +} +#endif + +void setup(){ +#if SERIAL + Serial.begin(57600); + Serial.println("\n[etherNode]"); +#endif + loadConfig(); + + if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) + Serial.println( "Failed to access Ethernet controller"); + if (!ether.dhcpSetup()) + Serial.println("DHCP failed"); +#if SERIAL + ether.printIp("IP: ", ether.myip); +#endif +} + +const char okHeader[] PROGMEM = + "HTTP/1.0 200 OK\r\n" + "Content-Type: text/html\r\n" + "Pragma: no-cache\r\n" +; + +static void homePage(BufferFiller& buf) { + word mhz = config.band == 4 ? 433 : config.band == 8 ? 868 : 915; + buf.emit_p(PSTR("$F\r\n" + "" + "RF12 etherNode - $D MHz, group $D" + "RF12 etherNode - $D MHz, group $D " + "- configure - send packet" + "

Last $D messages:

" + "
"), okHeader, config.refresh, mhz, config.group,
+                                            mhz, config.group, NUM_MESSAGES);
+    for (byte i = 0; i < NUM_MESSAGES; ++i) {
+        byte j = (next_msg + i) % NUM_MESSAGES;
+        if (history_len[j] > 0) {
+            word n = msgs_rcvd - NUM_MESSAGES + i;
+            buf.emit_p(PSTR("\n$D$D$D$D: OK"), // hack, to show leading zero's
+                                n/1000, (n/100) % 10, (n/10) % 10, n % 10);
+            for (byte k = 0; k < history_len[j]; ++k)
+                buf.emit_p(PSTR(" $D"), history_rcvd[j][k]);
+        }
+    }
+    long t = millis() / 1000;
+    word h = t / 3600;
+    byte m = (t / 60) % 60;
+    byte s = t % 60;
+    buf.emit_p(PSTR(
+        "
" + "Uptime is $D$D:$D$D:$D$D"), h/10, h%10, m/10, m%10, s/10, s%10); +#if DEBUG + buf.emit_p(PSTR(" ($D bytes free)"), freeRam()); +#endif +} + +static int getIntArg(const char* data, const char* key, int value =-1) { + char temp[10]; + if (ether.findKeyVal(data + 7, temp, sizeof temp, key) > 0) + value = atoi(temp); + return value; +} + +static void configPage(const char* data, BufferFiller& buf) { + // pick up submitted data, if present + if (data[6] == '?') { + byte b = getIntArg(data, "b"); + byte g = getIntArg(data, "g"); + byte c = getIntArg(data, "c", 0); + word r = getIntArg(data, "r"); + if (1 <= g && g <= 250 && 1 <= r && r <= 3600) { + // store values as new settings + config.band = b; + config.group = g; + config.collect = c; + config.refresh = r; + saveConfig(); + // re-init RF12 driver + loadConfig(); + // clear history + memset(history_len, 0, sizeof history_len); + // redirect to the home page + buf.emit_p(PSTR( + "HTTP/1.0 302 found\r\n" + "Location: /\r\n" + "\r\n")); + return; + } + } + // else show a configuration form + buf.emit_p(PSTR("$F\r\n" + "

Server node configuration

" + "
" + "

" + "Freq band (4, 8, or 9)
" + "Net group (1..250)
" + "Collect mode: " + "Don't send ACKs

" + "Refresh rate (1..3600 seconds)" + "

" + "" + "
"), okHeader, config.band, config.group, + config.collect ? "CHECKED" : "", + config.refresh); +} + +static void sendPage(const char* data, BufferFiller& buf) { + // pick up submitted data, if present + const char* p = strstr(data, "b="); + byte d = getIntArg(data, "d"); + if (data[6] == '?' && p != 0 && 0 <= d && d <= 31) { + // prepare to send data as soon as possible in loop() + outDest = d & RF12_HDR_MASK ? RF12_HDR_DST | d : 0; + outCount = 0; + // convert the input string to a number of decimal data bytes in outBuf + ++p; + while (*p != 0 && *p != '&') { + outBuf[outCount] = 0; + while ('0' <= *++p && *p <= '9') + outBuf[outCount] = 10 * outBuf[outCount] + (*p - '0'); + ++outCount; + } +#if SERIAL + Serial.print("Send to "); + Serial.print(outDest, DEC); + Serial.print(':'); + for (byte i = 0; i < outCount; ++i) { + Serial.print(' '); + Serial.print(outBuf[i], DEC); + } + Serial.println(); +#endif + // redirect to home page + buf.emit_p(PSTR( + "HTTP/1.0 302 found\r\n" + "Location: /\r\n" + "\r\n")); + return; + } + // else show a send form + buf.emit_p(PSTR("$F\r\n" + "

Send a wireless data packet

" + "
" + "

" + "Data bytes (decimal)
" + "Destination node " + "(1..31, or 0 to broadcast)
" + "

" + "" + "
"), okHeader); +} + +void loop(){ + word len = ether.packetReceive(); + word pos = ether.packetLoop(len); + // check if valid tcp data is received + if (pos) { + bfill = ether.tcpOffset(); + char* data = (char *) Ethernet::buffer + pos; +#if SERIAL + Serial.println(data); +#endif + // receive buf hasn't been clobbered by reply yet + if (strncmp("GET / ", data, 6) == 0) + homePage(bfill); + else if (strncmp("GET /c", data, 6) == 0) + configPage(data, bfill); + else if (strncmp("GET /s", data, 6) == 0) + sendPage(data, bfill); + else + bfill.emit_p(PSTR( + "HTTP/1.0 401 Unauthorized\r\n" + "Content-Type: text/html\r\n" + "\r\n" + "

401 Unauthorized

")); + ether.httpServerReply(bfill.position()); // send web page data + } + + // RFM12 loop runner, don't report acks + if (rf12_recvDone() && rf12_crc == 0 && (rf12_hdr & RF12_HDR_CTL) == 0) { + history_rcvd[next_msg][0] = rf12_hdr; + for (byte i = 0; i < rf12_len; ++i) + if (i < MESSAGE_TRUNC) + history_rcvd[next_msg][i+1] = rf12_data[i]; + history_len[next_msg] = rf12_len < MESSAGE_TRUNC ? rf12_len+1 + : MESSAGE_TRUNC+1; + next_msg = (next_msg + 1) % NUM_MESSAGES; + msgs_rcvd = (msgs_rcvd + 1) % 10000; + + if (RF12_WANTS_ACK && !config.collect) { +#if SERIAL + Serial.println(" -> ack"); +#endif + rf12_sendStart(RF12_ACK_REPLY, 0, 0); + } + } + + // send a data packet out if requested + if (outCount >= 0 && rf12_canSend()) { + rf12_sendStart(outDest, outBuf, outCount, 1); + outCount = -1; + } +} diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/getDHCPandDNS/getDHCPandDNS.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/getDHCPandDNS/getDHCPandDNS.ino new file mode 100644 index 0000000..117922e --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/getDHCPandDNS/getDHCPandDNS.ino @@ -0,0 +1,75 @@ +// This demo does web requests via DHCP and DNS lookup. +// 2011-07-05 http://opensource.org/licenses/mit-license.php +// +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +// +// PIN Connections (Using STM32F103): +// +// ENC28J60 - STM32F103 +// VCC - 3.3V +// GND - GND +// SCK - Pin PA5 +// SO - Pin PA6 +// SI - Pin PA7 +// CS - Pin PA8 +//----------------------------------------------------------------- +// +// + +#include +#include + +#define REQUEST_RATE 5000 // milliseconds + +// ethernet interface mac address +static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; +// remote website name +const char website[] PROGMEM = "google.com"; + +byte Ethernet::buffer[700]; +static long timer; + +// called when the client request is complete +static void my_result_cb (uint8_t status, uint16_t off, uint16_t len) { + Serial.print("<<< reply "); + Serial.print(millis() - timer); + Serial.println(" ms"); + Serial.println((const char*) Ethernet::buffer + off); +} + +void setup () { + Serial.begin(57600); + Serial.println("\n[getDHCPandDNS]"); + + if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) + Serial.println( "Failed to access Ethernet controller"); + + if (!ether.dhcpSetup()) + Serial.println("DHCP failed"); + + ether.printIp("My IP: ", ether.myip); + // ether.printIp("Netmask: ", ether.mymask); + ether.printIp("GW IP: ", ether.gwip); + ether.printIp("DNS IP: ", ether.dnsip); + + if (!ether.dnsLookup(website)) + Serial.println("DNS failed"); + ether.printIp("Server: ", ether.hisip); + + timer = - REQUEST_RATE; // start timing out right away +} + +void loop () { + + ether.packetLoop(ether.packetReceive()); + + if (millis() > timer + REQUEST_RATE) { + timer = millis(); + Serial.println("\n>>> REQ"); + ether.browseUrl(PSTR("/foo/"), "bar", website, my_result_cb); + } +} \ No newline at end of file diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/getStaticIP/getStaticIP.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/getStaticIP/getStaticIP.ino new file mode 100644 index 0000000..2564e54 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/getStaticIP/getStaticIP.ino @@ -0,0 +1,76 @@ +// This demo does web requests via DNS lookup, using a fixed gateway. +// 2010-11-27 http://opensource.org/licenses/mit-license.php +// +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +// +// PIN Connections (Using STM32F103): +// +// ENC28J60 - STM32F103 +// VCC - 3.3V +// GND - GND +// SCK - Pin PA5 +// SO - Pin PA6 +// SI - Pin PA7 +// CS - Pin PA8 +//----------------------------------------------------------------- +// +// + +#include +#include + +#define REQUEST_RATE 5000 // milliseconds + +// ethernet interface mac address +static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; +// ethernet interface ip address +static byte myip[] = { 192,168,1,203 }; +// gateway ip address +static byte gwip[] = { 192,168,1,1 }; +// domain name server (dns) address +static byte dnsip[] = { 192,168,1,1 }; +// remote website name +const char website[] PROGMEM = "google.com"; + +byte Ethernet::buffer[300]; // a very small tcp/ip buffer is enough here +static long timer; + +// called when the client request is complete +static void my_result_cb (byte status, uint16_t off, uint16_t len) { + Serial.print("<<< reply "); + Serial.print(millis() - timer); + Serial.println(" ms"); + Serial.println((const char*) Ethernet::buffer + off); +} + +void setup () { + Serial.begin(57600); + Serial.println("\n[getViaDNS]"); + + if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) + Serial.println( "Failed to access Ethernet controller"); + + ether.staticSetup(myip, gwip); + + ether.copyIp(ether.dnsip, dnsip); + + if (!ether.dnsLookup(website)) + Serial.println("DNS failed"); + ether.printIp("Server: ", ether.hisip); + + timer = - REQUEST_RATE; // start timing out right away +} + +void loop () { + ether.packetLoop(ether.packetReceive()); + + if (millis() > timer + REQUEST_RATE) { + timer = millis(); + Serial.println("\n>>> REQ"); + ether.browseUrl(PSTR("/foo/"), "bar", website, my_result_cb); + } +} \ No newline at end of file diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/getViaDNS/getViaDNS.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/getViaDNS/getViaDNS.ino new file mode 100644 index 0000000..e67a854 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/getViaDNS/getViaDNS.ino @@ -0,0 +1,72 @@ +// This demo does web requests via DNS lookup, using a fixed gateway. +// 2010-11-27 http://opensource.org/licenses/mit-license.php +// +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +// +// PIN Connections (Using STM32F103): +// +// ENC28J60 - STM32F103 +// VCC - 3.3V +// GND - GND +// SCK - Pin PA5 +// SO - Pin PA6 +// SI - Pin PA7 +// CS - Pin PA8 +//----------------------------------------------------------------- +// +// + +#include +#include + +#define REQUEST_RATE 5000 // milliseconds + +// ethernet interface mac address +static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; +// ethernet interface ip address +static byte myip[] = { 192,168,1,203 }; +// gateway ip address +static byte gwip[] = { 192,168,1,1 }; +// remote website name +const char website[] PROGMEM = "google.com"; + +byte Ethernet::buffer[300]; // a very small tcp/ip buffer is enough here +static long timer; + +// called when the client request is complete +static void my_result_cb (byte status, uint16_t off, uint16_t len) { + Serial.print("<<< reply "); + Serial.print(millis() - timer); + Serial.println(" ms"); + Serial.println((const char*) Ethernet::buffer + off); +} + +void setup () { + Serial.begin(57600); + Serial.println("\n[getViaDNS]"); + + if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) + Serial.println( "Failed to access Ethernet controller"); + + ether.staticSetup(myip, gwip); + + if (!ether.dnsLookup(website)) + Serial.println("DNS failed"); + ether.printIp("Server: ", ether.hisip); + + timer = - REQUEST_RATE; // start timing out right away +} + +void loop () { + ether.packetLoop(ether.packetReceive()); + + if (millis() > timer + REQUEST_RATE) { + timer = millis(); + Serial.println("\n>>> REQ"); + ether.browseUrl(PSTR("/foo/"), "bar", website, my_result_cb); + } +} \ No newline at end of file diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/multipacket/multipacket.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/multipacket/multipacket.ino new file mode 100644 index 0000000..85d506f --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/multipacket/multipacket.ino @@ -0,0 +1,99 @@ +// +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +// +// PIN Connections (Using STM32F103): +// +// ENC28J60 - STM32F103 +// VCC - 3.3V +// GND - GND +// SCK - Pin PA5 +// SO - Pin PA6 +// SI - Pin PA7 +// CS - Pin PA8 +//----------------------------------------------------------------- +// +// + +#include +#include + +#define TCP_FLAGS_FIN_V 1 //as declared in net.h +#define TCP_FLAGS_ACK_V 0x10 //as declared in net.h +static byte myip[] = { 192,168,1,203 }; +static byte gwip[] = { 192,168,1,1 }; +static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x39 }; +byte Ethernet::buffer[900]; // tcp ip send and receive buffer +const char pageA[] PROGMEM = +"HTTP/1.0 200 OK\r\n" +"Content-Type: text/html\r\n" +"\r\n" +"" + "" + "multipackets Test" + "" + "" + "Start here
" + "

packet 1

" + "

" + "the first packet send " + "

" +; +const char pageB[] PROGMEM = + "

packet 2

" + "

" + "if you read this it mean it works" + "

" +; +const char pageC[] PROGMEM = + "

packet 3

" + "

" + "if you read this it mean it works" + "

" +; +const char pageD[] PROGMEM = + "

packet 4

" + "

" + "if you read this it mean it works" + "

" +; +const char pageE[] PROGMEM = + "

packet 5

" + "

" + "this is the last packet" + "

" +; + + +void setup(){ + ether.begin(sizeof Ethernet::buffer, mymac);// 53 for the mega ethernet shield and 10 for normal ethernet shield + ether.staticSetup(myip, gwip); +} +void loop(){ + word pos = ether.packetLoop(ether.packetReceive()); + // check if valid tcp data is received + if (pos) { + char* data = (char *) Ethernet::buffer + pos; + if (strncmp("GET / ", data, 6) == 0) { + ether.httpServerReplyAck(); // send ack to the request + memcpy_P(ether.tcpOffset(), pageA, sizeof pageA); // send first packet and not send the terminate flag + ether.httpServerReply_with_flags(sizeof pageA - 1,TCP_FLAGS_ACK_V); + memcpy_P(ether.tcpOffset(), pageB, sizeof pageB); // send second packet and not send the terminate flag + ether.httpServerReply_with_flags(sizeof pageB - 1,TCP_FLAGS_ACK_V); + memcpy_P(ether.tcpOffset(), pageC, sizeof pageC); // send thirdt packet and not send the terminate flag + ether.httpServerReply_with_flags(sizeof pageC - 1,TCP_FLAGS_ACK_V); + memcpy_P(ether.tcpOffset(), pageD, sizeof pageD); // send fourth packet and not send the terminate flag + ether.httpServerReply_with_flags(sizeof pageD - 1,TCP_FLAGS_ACK_V); + memcpy_P(ether.tcpOffset(), pageE, sizeof pageE); // send fiveth packet and send the terminate flag + ether.httpServerReply_with_flags(sizeof pageE - 1,TCP_FLAGS_ACK_V|TCP_FLAGS_FIN_V); } + else + { + ether.httpServerReplyAck(); // send ack to the request + memcpy_P(ether.tcpOffset(), pageA, sizeof pageA);//only the first part will sended + ether.httpServerReply_with_flags(sizeof pageA - 1,TCP_FLAGS_ACK_V|TCP_FLAGS_FIN_V); + } + } +} \ No newline at end of file diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/multipacketSD/multipacketSD.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/multipacketSD/multipacketSD.ino new file mode 100644 index 0000000..8822084 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/multipacketSD/multipacketSD.ino @@ -0,0 +1,129 @@ +// works only with tinyfat library +// with SD library packets lost will occurs +// don't know why ! +// tested with arduino mega 1280 and uno +// send 240 Megabyte file without packet loss +// at 100 kbyte/s +// tinyfat read a block of 512 bytes on SD card , +// so the buffer must be 512 + 60 bytes +// on the arduino mega with bigger buffer you can adjust +// if (cur>=512) { // 512 to 1024 with 1100 bytes buffer + +#include +#include +#include +#define TCP_FLAGS_FIN_V 1 //as declared in net.h +#define TCP_FLAGS_ACK_V 16 //as declared in net.h +static byte myip[] = { 192,168,0,66 }; +static byte gwip[] = { 192,168,0,250 }; +static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x39 }; +byte Ethernet::buffer[700]; // tcp/ip send and receive buffer +unsigned long cur; +unsigned long pos; +byte res; + +void setup() { + // Initialize serial communication at 115200 baud + Serial.begin(115200); + // Initialize tinyFAT + // You might need to select a lower speed than the default SPISPEED_HIGH + file.setSSpin(4); + res=file.initFAT(0); + if (res==NO_ERROR) Serial.println("SD started"); + ether.begin(sizeof Ethernet::buffer, mymac , 10); //53 on mega ethernet shield 10 on others + ether.staticSetup(myip, gwip); + Serial.println("ETH started"); +} + +void loop() +{ + wait: + pos = ether.packetLoop(ether.packetReceive());// check if valid tcp data is received + if (pos) { + char* data = (char *) Ethernet::buffer + pos; + cur=0; + if (strncmp("GET / ", data, 6) == 0) {// nothing specified + sendfiles("index.htm"); + goto wait; + } + if (strncmp("GET /", data, 5) == 0) { // serve anything on sd card + int i =0; + char temp[15]=""; // here will be the name of requested file + while (data[i+5]!=32) {temp[i]=data[i+5];i++;}//search the end + sendfiles((char*) temp); + goto wait; + } + not_found(); + + } + +} + +void not_found() { //content not found + cur=0; + streamfile ("404.hea",TCP_FLAGS_FIN_V); + // Serial.println("not found"); +} + +byte streamfile (char* name , byte lastflag) { //send a file to the buffer + if (!file.exists(name)) {return 0;} + res=file.openFile(name, FILEMODE_BINARY); + int car=512; + while (car==512) { + car=file.readBinary(); + for(int i=0;i=512) { + ether.httpServerReply_with_flags(cur,TCP_FLAGS_ACK_V); + cur=0; + } else { + + if (lastflag==TCP_FLAGS_FIN_V) { + ether.httpServerReply_with_flags(cur,TCP_FLAGS_ACK_V+TCP_FLAGS_FIN_V); + } + } +} + file.closeFile(); + return 1; +} + +byte sendfiles(char* name) { // function to find the correct header and send a file + ether.httpServerReplyAck(); + int i =0; + char dtype[13]=""; + while (name[i]!=0) { + i++; + }//search the end + int b=i-1; + while ((name[b]!=46)&&(b>0)) { + b--; + }//search the point + int a=b+1; + while (a +#include + +#define BUF_SIZE 512 + +byte mac[] = { 0x00, 0x04, 0xA3, 0x21, 0xCA, 0x38 }; // Nanode MAC address. + +uint8_t ip[] = { 192, 168, 1, 203 }; // The fallback board address. +uint8_t dns[] = { 192, 168, 1, 1 }; // The DNS server address. +uint8_t gateway[] = { 192, 168, 1, 1 }; // The gateway router address. +uint8_t subnet[] = { 255, 255, 255, 0 }; // The subnet mask. +byte Ethernet::buffer[BUF_SIZE]; +byte fixed; // Address fixed, no DHCP + +void setup(void) +{ + Serial.begin(57600); + delay(2000); + + /* Check that the Ethernet controller exists */ + Serial.println("Initialising the Ethernet controller"); + if (ether.begin(sizeof Ethernet::buffer, mac) == 0) { + Serial.println( "Ethernet controller NOT initialized"); + while (true) + /* MT */ ; + } + + /* Get a DHCP connection */ + Serial.println("Attempting to get an IP address using DHCP"); + fixed = false; + if (ether.dhcpSetup()) { + ether.printIp("Got an IP address using DHCP: ", ether.myip); + } + /* If DHCP fails, start with a hard-coded address */ + else { + ether.staticSetup(ip, gateway, dns); + ether.printIp("DHCP FAILED, using fixed address: ", ether.myip); + fixed = true; + } + + return; +} + +void loop(void) +{ + word rc; + + /* These function calls are required if ICMP packets are to be accepted */ + rc = ether.packetLoop(ether.packetReceive()); + Serial.print("ether.packetLoop() returned "); + Serial.println(rc, DEC); + + // For debugging + delay(5000); + + return; +} \ No newline at end of file diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/noipClient/noipClient.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/noipClient/noipClient.ino new file mode 100644 index 0000000..01454fc --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/noipClient/noipClient.ino @@ -0,0 +1,293 @@ +// NoIP client sketch for ENC28J60 based Ethernet Shield. +// You need a free account from www.no-ip.com +// +// 1. On NoIP website, create a host of type DNS Host (A) +// 2. Insert your host name in noIP_host[] variable +// 3. Encode "username:password" in base64 using an online tool like +// 4. Paste the encoded string in noIP_auth[] variable +// +// Contributed by Luca Dentella +// http://www.lucadentella.it/2012/04/28/enc28j60-e-arduino-6/ +// +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +// +// PIN Connections (Using STM32F103): +// +// ENC28J60 - STM32F103 +// VCC - 3.3V +// GND - GND +// SCK - Pin PA5 +// SO - Pin PA6 +// SI - Pin PA7 +// CS - Pin PA8 +//----------------------------------------------------------------- +// +// + + +#include +#include + +// Finite-State Machine states +#define STATUS_IDLE 0 +#define STATUS_WAITING_FOR_PUBLIC_IP 1 +#define STATUS_NOIP_NEEDS_UPDATE 2 +#define STATUS_WAITING_FOR_NOIP 3 +#define STATUS_ERROR 99 + +// The sketch will check your public IP every CHECK_IP_INTERVAL ms, +// and wait for REQUEST_TIMEOUT ms for a response. +// It will retry for maximum of MAX_ATTEMPTS attemps. +#define CHECK_IP_INTERVAL 60000 +#define REQUEST_TIMEOUT 10000 +#define MAX_ATTEMPTS 3 + +// MAC Address of Ethernet Shield +static byte mymac[] = {0x74,0x69,0x69,0x2D,0x30,0x31}; + +// Insert your hostname and authentication string +const char noIP_host[] PROGMEM = ""; //e.g. myname.no-ip.info +const char noIP_auth[] PROGMEM = ""; + +//---------------------------------------------------------- +// Don't change these ones... +const char getIP_web[] PROGMEM = "www.lucadentella.it"; +const char noIP_web[] PROGMEM = "dynupdate.no-ip.com"; +//---------------------------------------------------------- + +// Some global variables +byte Ethernet::buffer[700]; +byte getIP_address[4]; +byte noIP_address[4]; +byte actual_status; +static byte session_id; +static uint32_t check_ip_timer; +static uint32_t request_timer; +int attempt; +Stash stash; + +void setup () { + + Serial.begin(57600); + Serial.println(F("NoIP Client Demo")); + Serial.println(); + + if (!ether.begin(sizeof Ethernet::buffer, mymac)) + Serial.println(F( "Failed to access Ethernet controller")); + else + Serial.println(F("Ethernet controller initialized")); + Serial.println(); + + if (!ether.dhcpSetup()) + Serial.println(F("Failed to get configuration from DHCP")); + else + Serial.println(F("DHCP configuration done")); + + ether.printIp("IP Address:\t", ether.myip); + ether.printIp("Netmask:\t", ether.netmask); + ether.printIp("Gateway:\t", ether.gwip); + Serial.println(); + + actual_status = STATUS_IDLE; + attempt = 0; + + // Resolve IP for getIP_web and noIP_web + // and store them into global variables + if (!ether.dnsLookup(getIP_web)) { + Serial.print(F("Unable to resolve IP for ")); + SerialPrint_P(getIP_web); + actual_status = STATUS_ERROR; + } else { + ether.copyIp(getIP_address, ether.hisip); + SerialPrint_P(getIP_web); + ether.printIp(" resolved to:\t", ether.hisip); + } + if (!ether.dnsLookup(noIP_web)) { + Serial.print(F("Unable to resolve IP for ")); + SerialPrint_P(noIP_web); + actual_status = STATUS_ERROR; + } else { + ether.copyIp(noIP_address, ether.hisip); + SerialPrint_P(noIP_web); + ether.printIp(" resolved to:\t", ether.hisip); + } + + Serial.println(); +} + + +void loop() { + + ether.packetLoop(ether.packetReceive()); + + // FSM + switch(actual_status) { + + case STATUS_IDLE: checkPublicIP(); break; + case STATUS_WAITING_FOR_PUBLIC_IP: checkPublicIPResponse(); break; + case STATUS_NOIP_NEEDS_UPDATE: updateNoIP(); break; + case STATUS_WAITING_FOR_NOIP: checkNoIPResponse(); break; + } +} + +void checkPublicIP() { + + if(millis() > check_ip_timer) { + + Serial.print(F("Checking public IP... ")); + + // Create a request for GetIP service, + // set destination IP and send request saving session_id + Stash::prepare(PSTR("GET /demo/getip.php HTTP/1.1" "\r\n" "Host: $F" "\r\n" "\r\n"), getIP_web); + ether.copyIp(ether.hisip, getIP_address); + session_id = ether.tcpSend(); + + // Change FSM state, prepare for timeout and increment attempts counter + actual_status = STATUS_WAITING_FOR_PUBLIC_IP; + request_timer = millis() + REQUEST_TIMEOUT; + attempt++; + } +} + +void checkPublicIPResponse() { + + String actualIp, dnsIp; + + const char* reply = ether.tcpReply(session_id); + + // We got a valid response + if(reply != 0) { + + // Parse response for public IP + for(int i = 0; i < strlen(reply) - 189; i++) actualIp = actualIp + reply[187 + i]; + Serial.println(actualIp); + + // If we can't resolve actual IP for our hostname on NoIP, + // return to IDLE state and wait for the next interval + if(!ether.dnsLookup(noIP_host)) { + Serial.print(F("Unable to resolve actual IP for ")); + SerialPrint_P(noIP_host); + Serial.println(); + actual_status = STATUS_IDLE; + attempt = 0; + check_ip_timer = millis() + CHECK_IP_INTERVAL; + + // If we can resolve the IP for our hostname, save it in xxx.xxx.xxx.xxx form + // and compare it with our public IP + } else { + for(int i = 0; i < 4; i++) { + dnsIp = dnsIp + String(ether.hisip[i]); + if(i < 3) dnsIp = dnsIp + "."; + } + SerialPrint_P(noIP_host); + Serial.print(F(" resolved to ")); + Serial.println(dnsIp); + + // If they are the same, we can sit down and wait for the next interval + if(actualIp.compareTo(dnsIp) == 0) { + Serial.println(F("No update needed :)")); + actual_status = STATUS_IDLE; + attempt = 0; + check_ip_timer = millis() + CHECK_IP_INTERVAL; + + // Different? We'd better to update NoIP! + } else { + Serial.println(F("Update needed :(")); + actual_status = STATUS_NOIP_NEEDS_UPDATE; + attempt = 0; + } + } + + // No valid response? Check for timeout + // If we've already sent a max number of requests, return to IDLE state + // and wait for the next interval + } else { + if(millis() > request_timer) { + Serial.println(F(" no response :(")); + actual_status = STATUS_IDLE; + if(attempt == MAX_ATTEMPTS) { + Serial.println(F("Max number of attempts reached")); + attempt = 0; + check_ip_timer = millis() + CHECK_IP_INTERVAL; + } + } + } +} + +void updateNoIP() { + + Serial.print(F("Updating NoIP...")); + + // Create a request for updating NoIP using NoIP API, + // set destination IP and send request saving session_id + Stash::prepare(PSTR("GET /nic/update?hostname=$F HTTP/1.0" "\r\n" + "Host: $F" "\r\n" + "Authorization: Basic $F" "\r\n" + "User-Agent: NoIP_Client" "\r\n" "\r\n"), + noIP_host, noIP_web, noIP_auth); + ether.copyIp(ether.hisip, noIP_address); + session_id = ether.tcpSend(); + + // Wait for response or timeout... + actual_status = STATUS_WAITING_FOR_NOIP; + request_timer = millis() + REQUEST_TIMEOUT; + attempt++; +} + +void checkNoIPResponse() { + + const char* reply = ether.tcpReply(session_id); + boolean done; + + // We got a valid response... + if(reply != 0) { + + // Parse NoIP response looking for status/error codes... + if(strstr(reply, "good") != 0) { + Serial.println(F(" done!")); + done = true; + } + else if(strstr(reply, "nochg") != 0) { + Serial.println(F(" no change required!")); + done = true; + } + else if(strstr(reply, "nohost") != 0) Serial.println(F(" host not found :(")); + else if(strstr(reply, "badauth") != 0) Serial.println(F(" invalid username or password :(")); + else if(strstr(reply, "badagent") != 0) Serial.println(F(" agent banned :(")); + else if(strstr(reply, "!donator") != 0) Serial.println(F(" feature not available for specified username :(")); + else if(strstr(reply, "abuse") != 0) Serial.println(F(" username blocked due to abuse :(")); + else Serial.println(F(" generic error :(")); + + // Record has been updated? Ok wait for next interval... + if(done) { + actual_status = STATUS_IDLE; + attempt = 0; + check_ip_timer = millis() + CHECK_IP_INTERVAL; + } + + // No valid response? Check for timeout + // If we've already sent a max number of requests, return to IDLE state + // and wait for the next interval + } else { + + if(millis() > request_timer) { + Serial.println(F("No response from NoIP")); + if(attempt == MAX_ATTEMPTS) { + Serial.println(F("Max number of attempts reached")); + actual_status = STATUS_IDLE; + attempt = 0; + check_ip_timer = millis() + CHECK_IP_INTERVAL; + } + else + actual_status = STATUS_NOIP_NEEDS_UPDATE; + } + } +} + +void SerialPrint_P(PGM_P str) { + for (uint8_t c; (c = pgm_read_byte(str)); str++) Serial.write(c); +} \ No newline at end of file diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/pings/pings.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/pings/pings.ino new file mode 100644 index 0000000..834b9a0 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/pings/pings.ino @@ -0,0 +1,96 @@ +// Ping a remote server, also uses DHCP and DNS. +// 2011-06-12 http://opensource.org/licenses/mit-license.php +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +// +// PIN Connections (Using STM32F103): +// +// ENC28J60 - STM32F103 +// VCC - 3.3V +// GND - GND +// SCK - Pin PA5 +// SO - Pin PA6 +// SI - Pin PA7 +// CS - Pin PA8 +//----------------------------------------------------------------- +// +// + +#include +#include + +// ethernet interface mac address, must be unique on the LAN +static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; + +byte Ethernet::buffer[700]; + +static uint32_t timer; +static byte myip[] = { 192,168,1,203 }; + +// gateway ip address +static byte gwip[] = { 192,168,1,1 }; + +// domain name server (dns) address +static byte dnsip[] = { 192,168,1,1 }; + +// called when a ping comes in (replies to it are automatic) +static void gotPinged (byte* ptr) { + ether.printIp(">>> ping from: ", ptr); +} + +void setup () { + Serial.begin(57600); + delay(10); + Serial.println("\n[pings]"); + + if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) + Serial.println(("Failed to access Ethernet controller")); + + + if (!ether.dhcpSetup()){//If DHCP fails set the static IP, Gateway and DNS IP. + Serial.println(F("DHCP failed")); + ether.staticSetup(myip, gwip); + ether.copyIp(ether.dnsip, dnsip); + } + + ether.printIp("IP: ", ether.myip); + ether.printIp("GW: ", ether.gwip); + + +//#if 1 + // use DNS to locate the IP address we want to ping + if (!ether.dnsLookup(PSTR("www.google.com"))) + Serial.println("DNS failed"); +//#else + //ether.parseIp(ether.hisip, "173.194.112.119"); +//#endif + ether.printIp("SRV: ", ether.hisip); + + // call this to report others pinging us + ether.registerPingCallback(gotPinged); + + timer = -9999999; // start timing out right away + Serial.println(); +} + +void loop () { + word len = ether.packetReceive(); // go receive new packets + word pos = ether.packetLoop(len); // respond to incoming pings + + // report whenever a reply to our outgoing ping comes back + if (len > 0 && ether.packetLoopIcmpCheckReply(ether.hisip)) { + Serial.print(" "); + Serial.print((micros() - timer) * 0.001, 3); + Serial.println(" ms"); + } + + // ping a remote server once every few seconds + if (micros() - timer >= 5000000) { + ether.printIp("Pinging: ", ether.hisip); + timer = micros(); + ether.clientIcmpRequest(ether.hisip); + } +} \ No newline at end of file diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/rbbb_server/rbbb_server.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/rbbb_server/rbbb_server.ino new file mode 100644 index 0000000..5a21c05 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/rbbb_server/rbbb_server.ino @@ -0,0 +1,72 @@ +// This is a demo of the RBBB running as webserver with the Ether Card +// 2010-05-28 http://opensource.org/licenses/mit-license.php +// +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +// +// PIN Connections (Using STM32F103): +// +// ENC28J60 - STM32F103 +// VCC - 3.3V +// GND - GND +// SCK - Pin PA5 +// SO - Pin PA6 +// SI - Pin PA7 +// CS - Pin PA8 +//----------------------------------------------------------------- +// +// + +#include +#include + +// ethernet interface mac address, must be unique on the LAN +static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; +static byte myip[] = { 192,168,1,203 }; + +byte Ethernet::buffer[500]; +BufferFiller bfill; + +void setup () { + Serial.begin(57600); + delay(10); + if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) + Serial.println(F("Failed to access Ethernet controller")); + ether.staticSetup(myip); + + //if (!ether.dhcpSetup()) + // Serial.println("DHCP failed"); + + ether.printIp("IP: ", ether.myip); + ether.printIp("GW: ", ether.gwip); + ether.printIp("DNS: ", ether.dnsip); +} + +static word homePage() { + long t = millis() / 1000; + word h = t / 3600; + byte m = (t / 60) % 60; + byte s = t % 60; + bfill = ether.tcpOffset(); + bfill.emit_p(PSTR( + "HTTP/1.0 200 OK\r\n" + "Content-Type: text/html\r\n" + "Pragma: no-cache\r\n" + "\r\n" + "" + "RBBB server" + "

$D$D:$D$D:$D$D

"), + h/10, h%10, m/10, m%10, s/10, s%10); + return bfill.position(); +} + +void loop () { + + if (ether.packetLoop(ether.packetReceive())){ // check if valid tcp data is received + ether.httpServerReply(homePage()); // send web page data + + } +} \ No newline at end of file diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/stashTest/stashTest.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/stashTest/stashTest.ino new file mode 100644 index 0000000..13344c1 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/stashTest/stashTest.ino @@ -0,0 +1,119 @@ +// Test the offloaded RAM stash mechanism. +// 2011-07-10 http://opensource.org/licenses/mit-license.php + +#include +#include + +// ethernet interface mac address, must be unique on the LAN +byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; + +byte Ethernet::buffer[700]; + +void dumpBlock (const char* msg, byte idx) { + Serial.print(msg); + Serial.print(" ["); + Serial.print(idx, DEC); + Serial.print("] @ "); + Serial.print(Stash::bufs[idx].bnum, DEC); + Serial.print(" free "); + Serial.print(Stash::freeCount(), DEC); + for (byte i = 0; i < 64; ++i) { + if (i % 16 == 0) + Serial.println(); + Serial.print(' '); + Serial.print(Stash::bufs[idx].bytes[i], DEC); + } + Serial.println(); +} + +void dumpStash (const char* msg, void* ptr) { + Stash& buf = *(Stash*) ptr; + Serial.print(msg); + Serial.print(" c"); + Serial.print(buf.count, DEC); + Serial.print(" f"); + Serial.print(buf.first, DEC); + Serial.print(" l"); + Serial.print(buf.last, DEC); + Serial.print(" u"); + Serial.print(buf.curr, DEC); + Serial.print(" o"); + Serial.print(buf.offs, DEC); + Serial.print(" #"); + Serial.println(buf.size()); +} + +void setup () { + Serial.begin(57600); + delay(1); + Serial.println("\n[stashTest]"); + ether.begin(sizeof Ethernet::buffer, mymac); + +#if 1 + Stash buf; + dumpStash("> AAA", &buf); + byte fd = buf.create(); + Serial.print("fd "); + Serial.println(fd, DEC); + dumpStash("> BBB", &buf); + dumpBlock("EMPTY", 0); + for (char c = 'a'; c <= 'z'; ++c) + buf.put(c); + for (char c = 'A'; c <= 'Z'; ++c) + buf.put(c); + dumpBlock("LETTERS", 0); + dumpStash("> CCC", &buf); + for (char c = '0'; c <= '9'; ++c) + buf.put(c); + dumpBlock("DIGITS", 0); + dumpStash("> DDD", &buf); + buf.print(" x = "); + buf.println(123.45); + dumpBlock("PRINT", 0); + dumpStash("> EEE", &buf); + buf.load(1, 1); + dumpBlock("FIRST", 1); + buf.save(); + buf.load(1, 1); + dumpBlock("FLUSHED", 1); + dumpStash("> FFF", &buf); + for (;;) { + char c = buf.get(); + if (c == 0) + break; + Serial.print(c); + } + Serial.println(); + dumpStash("> GGG", &buf); +#endif + + Stash buf2; + byte fd2 = buf2.create(); + buf2.print(""); + buf2.save(); + buf2.load(1, fd2); + dumpBlock("SECOND", 1); + dumpStash("> HHH", &buf2); + + Stash::prepare(PSTR("a $S b $F c $D d $H e"), + "123", PSTR("4567"), -12345, fd2); + dumpBlock("BUFFER", 0); + Serial.println(Stash::length()); + for (word i = 0, n = Stash::length(); i < n; ++i) { + char c; + Stash::extract(i, 1, &c); + Serial.print(c); + } + Serial.println(); + Stash::cleanup(); +#if 1 + buf.release(); +#endif + Serial.print("free "); + Serial.println(Stash::freeCount(), DEC); + Serial.println("DONE"); +} + +void loop () { + // ether.packetLoop(ether.packetReceive()); +} diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/testDHCP/testDHCP.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/testDHCP/testDHCP.ino new file mode 100644 index 0000000..1066982 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/testDHCP/testDHCP.ino @@ -0,0 +1,60 @@ +// Arduino demo sketch for testing the DHCP client code +// +// Original author: Andrew Lindsay +// Major rewrite and API overhaul by jcw, 2011-06-07 +// +// Copyright: GPL V2 +// See http://www.gnu.org/licenses/gpl.html +// +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +// +// PIN Connections (Using STM32F103): +// +// ENC28J60 - STM32F103 +// VCC - 3.3V +// GND - GND +// SCK - Pin PA5 +// SO - Pin PA6 +// SI - Pin PA7 +// CS - Pin PA8 +//----------------------------------------------------------------- +// +// + +#include +#include + +static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; + +byte Ethernet::buffer[700]; + +void setup () { + Serial.begin(57600); + Serial.println(F("\n[testDHCP]")); + + Serial.print("MAC: "); + for (byte i = 0; i < 6; ++i) { + Serial.print(mymac[i], HEX); + if (i < 5) + Serial.print(':'); + } + Serial.println(); + + if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) + Serial.println(F("Failed to access Ethernet controller")); + + Serial.println(F("Setting up DHCP")); + if (!ether.dhcpSetup()) + Serial.println(F("DHCP failed")); + + ether.printIp("My IP: ", ether.myip); + ether.printIp("Netmask: ", ether.netmask); + ether.printIp("GW IP: ", ether.gwip); + ether.printIp("DNS IP: ", ether.dnsip); +} + +void loop () {} \ No newline at end of file diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/twitter/twitter.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/twitter/twitter.ino new file mode 100644 index 0000000..5520678 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/twitter/twitter.ino @@ -0,0 +1,85 @@ +// Twitter client sketch for ENC28J60 based Ethernet Shield. Uses +// arduino-tweet.appspot.com as a OAuth gateway. +// Step by step instructions: +// +// 1. Get a oauth token: +// http://arduino-tweet.appspot.com/oauth/twitter/login +// 2. Put the token value in the TOKEN define below +// 3. Run the sketch! +// +// WARNING: Don't send more than 1 tweet per minute! +// NOTE: Twitter rejects tweets with identical content as dupes (returns 403) + +#include +#include + +// OAUTH key from http://arduino-tweet.appspot.com/ +#define TOKEN "Insert-your-token-here" + +// ethernet interface mac address, must be unique on the LAN +byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; + +const char website[] PROGMEM = "arduino-tweet.appspot.com"; + +static byte session; + +byte Ethernet::buffer[700]; +Stash stash; + +static void sendToTwitter () { + Serial.println("Sending tweet..."); + byte sd = stash.create(); + + const char tweet[] = "@solarkennedy the test Twitter sketch works!"; + stash.print("token="); + stash.print(TOKEN); + stash.print("&status="); + stash.println(tweet); + stash.save(); + int stash_size = stash.size(); + + // Compose the http POST request, taking the headers below and appending + // previously created stash in the sd holder. + Stash::prepare(PSTR("POST http://$F/update HTTP/1.0" "\r\n" + "Host: $F" "\r\n" + "Content-Length: $D" "\r\n" + "\r\n" + "$H"), + website, website, stash_size, sd); + + // send the packet - this also releases all stash buffers once done + // Save the session ID so we can watch for it in the main loop. + session = ether.tcpSend(); +} + +void setup () { + Serial.begin(57600); + Serial.println("\n[Twitter Client]"); + + if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) + Serial.println(F("Failed to access Ethernet controller")); + if (!ether.dhcpSetup()) + Serial.println(F("DHCP failed")); + + ether.printIp("IP: ", ether.myip); + ether.printIp("GW: ", ether.gwip); + ether.printIp("DNS: ", ether.dnsip); + + if (!ether.dnsLookup(website)) + Serial.println(F("DNS failed")); + + ether.printIp("SRV: ", ether.hisip); + + sendToTwitter(); +} + +void loop () { + ether.packetLoop(ether.packetReceive()); + + const char* reply = ether.tcpReply(session); + if (reply != 0) { + Serial.println("Got a response!"); + Serial.println(reply); + } +} + diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/udpClientSendOnly/Java_ClientAndServer/UDPClient.java b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/udpClientSendOnly/Java_ClientAndServer/UDPClient.java new file mode 100644 index 0000000..0439c3e --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/udpClientSendOnly/Java_ClientAndServer/UDPClient.java @@ -0,0 +1,29 @@ +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.UnknownHostException; + +public class UDPClient { + + public static void main(String[] args) throws UnknownHostException { + DatagramSocket s; + byte[] sendBuffer = new byte[1024]; + DatagramPacket sendPacket; + final InetAddress ADDRESS = InetAddress.getByName("localhost"); + final int PORT = 1234; + try { + s = new DatagramSocket(); + System.out.println("Odesilam data na server..."); + + sendBuffer = "abc123".getBytes(); + sendPacket = new DatagramPacket(sendBuffer, sendBuffer.length, + ADDRESS, PORT); + s.send(sendPacket); + + } catch (IOException e) { + e.printStackTrace(); + } + } + +} \ No newline at end of file diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/udpClientSendOnly/Java_ClientAndServer/UDPserver.java b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/udpClientSendOnly/Java_ClientAndServer/UDPserver.java new file mode 100644 index 0000000..b7cda60 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/udpClientSendOnly/Java_ClientAndServer/UDPserver.java @@ -0,0 +1,39 @@ +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class UDPserver { + public static void main(String[] args) { + UDPserver server = new UDPserver(1234); + server.start(); + } + private int port; + private DatagramSocket s; + + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + + public UDPserver(int port) { + this.port = port; + } + + public void start() { + System.out.println("SERVER: Waiting for incomming connections..."); + System.out.println("DATE TIME IP:PORT received_data"); + try { + s = new DatagramSocket(this.port); + while (true) { + byte[] data = new byte[1412]; + DatagramPacket p = new DatagramPacket(data, + data.length); + s.receive(p); + System.out.print(sdf.format(new Date()).toString() + " "); + System.out.print(p.getSocketAddress() + " "); + System.out.println(new String(p.getData())); + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/udpClientSendOnly/udpClientSendOnly.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/udpClientSendOnly/udpClientSendOnly.ino new file mode 100644 index 0000000..da77a35 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/udpClientSendOnly/udpClientSendOnly.ino @@ -0,0 +1,40 @@ + +#include +#include + +static byte mymac[] = { 0x1A,0x2B,0x3C,0x4D,0x5E,0x6F }; +byte Ethernet::buffer[700]; +static uint32_t timer; + +char website[] PROGMEM = "server.example.net"; +const int dstPort PROGMEM = 1234; + +const int srcPort PROGMEM = 4321; + +void setup () { + Serial.begin(9600); + + if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) + Serial.println( "Failed to access Ethernet controller"); + if (!ether.dhcpSetup()) + Serial.println("DHCP failed"); + + ether.printIp("IP: ", ether.myip); + ether.printIp("GW: ", ether.gwip); + ether.printIp("DNS: ", ether.dnsip); + + if (!ether.dnsLookup(website)) + Serial.println("DNS failed"); + + ether.printIp("SRV: ", ether.hisip); +} + +char textToSend[] = "test 123"; + +void loop () { + if (millis() > timer) { + timer = millis() + 5000; + //static void sendUdp (char *data,uint8_t len,uint16_t sport, uint8_t *dip, uint16_t dport); + ether.sendUdp(textToSend, sizeof(textToSend), srcPort, ether.hisip, dstPort ); + } +} diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/udpListener/udpListener.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/udpListener/udpListener.ino new file mode 100644 index 0000000..7f9806c --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/udpListener/udpListener.ino @@ -0,0 +1,101 @@ +// Demonstrates usage of the new udpServer feature. +//You can register the same function to multiple ports, and multiple functions to the same port. +// +// 2013-4-7 Brian Lee + + +//************************************************************* +// +// IT DOESN'T WORK ON STM32 YET (IS NOT PORTED YET). +// +//************************************************************* +#include +#include + +#define STATIC 0 // set to 1 to disable DHCP (adjust myip/gwip values below) + +#if STATIC +// ethernet interface ip address +static byte myip[] = { 192,168,0,200 }; +// gateway ip address +static byte gwip[] = { 192,168,0,1 }; +#endif + +// ethernet mac address - must be unique on your network +static byte mymac[] = { 0x70,0x69,0x69,0x2D,0x30,0x31 }; + +byte Ethernet::buffer[500]; // tcp/ip send and receive buffer + +//callback that prints received packets to the serial port +void udpSerialPrint(word port, byte ip[4], const char *data, word len) { + IPAddress src(ip[0], ip[1], ip[2], ip[3]); + Serial.println(src); + Serial.println(port); + Serial.println(data); + Serial.println(len); +} + +void setup(){ + Serial.begin(57600); + Serial.println(F("\n[backSoon]")); + + if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) + Serial.println(F("Failed to access Ethernet controller")); +#if STATIC + ether.staticSetup(myip, gwip); +#else + if (!ether.dhcpSetup()) + Serial.println(F("DHCP failed")); +#endif + + ether.printIp("IP: ", ether.myip); + ether.printIp("GW: ", ether.gwip); + ether.printIp("DNS: ", ether.dnsip); + + //register udpSerialPrint() to port 1337 + ether.udpServerListenOnPort(&udpSerialPrint, 1337); + + //register udpSerialPrint() to port 42. + ether.udpServerListenOnPort(&udpSerialPrint, 42); +} + +void loop(){ + //this must be called for ethercard functions to work. + ether.packetLoop(ether.packetReceive()); +} + + +/* +//Processing sketch to send test UDP packets. + +import hypermedia.net.*; + + UDP udp; // define the UDP object + + + void setup() { + udp = new UDP( this, 6000 ); // create a new datagram connection on port 6000 + //udp.log( true ); // <-- printout the connection activity + udp.listen( true ); // and wait for incoming message + } + + void draw() + { + } + + void keyPressed() { + String ip = "192.168.0.200"; // the remote IP address + int port = 1337; // the destination port + + udp.send("Greetings via UDP!", ip, port ); // the message to send + + } + + void receive( byte[] data ) { // <-- default handler + //void receive( byte[] data, String ip, int port ) { // <-- extended handler + + for(int i=0; i < data.length; i++) + print(char(data[i])); + println(); + } +*/ diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/webClient/webClient.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/webClient/webClient.ino new file mode 100644 index 0000000..12acc4a --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/webClient/webClient.ino @@ -0,0 +1,69 @@ +// Demo using DHCP and DNS to perform a web client request. +// 2011-06-08 http://opensource.org/licenses/mit-license.php +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +// +// PIN Connections (Using STM32F103): +// +// ENC28J60 - STM32F103 +// VCC - 3.3V +// GND - GND +// SCK - Pin PA5 +// SO - Pin PA6 +// SI - Pin PA7 +// CS - Pin PA8 +//----------------------------------------------------------------- +// +// + +#include +#include + +// ethernet interface mac address, must be unique on the LAN +static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; + +byte Ethernet::buffer[700]; +static uint32_t timer; + +const char website[] PROGMEM = "www.google.com"; + +// called when the client request is complete +static void my_callback (byte status, uint16_t off, uint16_t len) { + Serial.println(">>>"); + Ethernet::buffer[off+300] = 0; + Serial.print((const char*) Ethernet::buffer + off); + Serial.println("..."); +} + +void setup () { + Serial.begin(57600); + Serial.println(F("\n[webClient]")); + + if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) + Serial.println(F("Failed to access Ethernet controller")); + if (!ether.dhcpSetup()) + Serial.println(F("DHCP failed")); + + ether.printIp("IP: ", ether.myip); + ether.printIp("GW: ", ether.gwip); + ether.printIp("DNS: ", ether.dnsip); + + if (!ether.dnsLookup(website)) + Serial.println("DNS failed"); + + ether.printIp("SRV: ", ether.hisip); +} + +void loop () { + ether.packetLoop(ether.packetReceive()); + + if (millis() > timer) { + timer = millis() + 5000; + Serial.println(); + Serial.print("<<< REQ "); + ether.browseUrl(PSTR("/foo/"), "bar", website, my_callback); + } +} \ No newline at end of file diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/examples/xively/xively.ino b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/xively/xively.ino new file mode 100644 index 0000000..3541743 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/examples/xively/xively.ino @@ -0,0 +1,158 @@ +// Simple demo for feeding some random data to Pachube. +// 2011-07-08 http://opensource.org/licenses/mit-license.php + +// Handle returning code and reset ethernet module if needed +// 2013-10-22 hneiraf@gmail.com +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +// +// PIN Connections (Using STM32F103): +// +// ENC28J60 - STM32F103 +// VCC - 3.3V +// GND - GND +// SCK - Pin PA5 +// SO - Pin PA6 +// SI - Pin PA7 +// CS - Pin PA8 +//----------------------------------------------------------------- +// +// + +#include +#include + +// change these settings to match your own setup +#define FEED "12345678" //Write here your FEED key (look at "Feed Keys" on xively. +#define APIKEY "asgEEwewtwerweTRRWER323SSDFfds" //Write your own APIKEY. + +// ethernet interface mac address, must be unique on the LAN +static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 }; + +const char website[] PROGMEM = "api.xively.com"; + +byte Ethernet::buffer[350]; +uint32_t timer; +Stash stash; +byte session; + +//timing variable +int res = 0; + + + +void setup () { + Serial.begin(57600); + Serial.println("\n[Xively example]"); + + //Initialize Ethernet + initialize_ethernet(); +} + + +void loop () { + + //if correct answer is not received then re-initialize ethernet module + if (res > 220){ + initialize_ethernet(); + } + + res = res + 1; + + ether.packetLoop(ether.packetReceive()); + + //200 res = 10 seconds (50ms each res) + if (res == 200) { + + + //Generate random info + float demo = random(0,500); + word one = random(0,500); + String msje; + + if (demo < 250){ + msje = "low"; + } + else{ + msje = "high"; + } + + + // generate two fake values as payload - by using a separate stash, + // we can determine the size of the generated message ahead of time + byte sd = stash.create(); + stash.print("demo,"); + stash.println(demo); + stash.print("one,"); + stash.println(one); + stash.print("mensaje,"); + stash.println(msje); + stash.save(); + + //Display data to be sent + Serial.println(demo); + Serial.println(one); + + + // generate the header with payload - note that the stash size is used, + // and that a "stash descriptor" is passed in as argument using "$H" + Stash::prepare(PSTR("PUT http://$F/v2/feeds/$F.csv HTTP/1.0" "\r\n" + "Host: $F" "\r\n" + "X-PachubeApiKey: $F" "\r\n" + "Content-Length: $D" "\r\n" + "\r\n" + "$H"), + website, PSTR(FEED), website, PSTR(APIKEY), stash.size(), sd); + + // send the packet - this also releases all stash buffers once done + session = ether.tcpSend(); + } + + const char* reply = ether.tcpReply(session); + + if (reply != 0) { + res = 0; + Serial.println(reply); + } + delay(50); +} + + + +void initialize_ethernet(void){ + for(;;){ // keep trying until you succeed + //Reinitialize ethernet module + pinMode(5, OUTPUT); + Serial.println("Reseting Ethernet..."); + digitalWrite(5, LOW); + delay(1000); + digitalWrite(5, HIGH); + delay(500); + + if (ether.begin(sizeof Ethernet::buffer, mymac) == 0){ + Serial.println( "Failed to access Ethernet controller"); + continue; + } + + if (!ether.dhcpSetup()){ + Serial.println("DHCP failed"); + continue; + } + + ether.printIp("IP: ", ether.myip); + ether.printIp("GW: ", ether.gwip); + ether.printIp("DNS: ", ether.dnsip); + + if (!ether.dnsLookup(website)) + Serial.println("DNS failed"); + + ether.printIp("SRV: ", ether.hisip); + + //reset init value + res = 0; + break; + } +} \ No newline at end of file diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/keywords.txt b/STM32F1/libraries/Serasidis_EtherCard_STM/keywords.txt new file mode 100644 index 0000000..ca73a6e --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/keywords.txt @@ -0,0 +1,53 @@ +####################################### +# Syntax Coloring Map EtherCard_STM +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +EtherCard_STM KEYWORD1 +ether KEYWORD1 +BufferFiller KEYWORD1 +Stash KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### +emit_p KEYWORD2 +emit_raw KEYWORD2 +emit_raw_p KEYWORD2 +buffer KEYWORD2 +Ethernet KEYWORD2 +printIp KEYWORD2 +staticSetup KEYWORD2 +dhcpSetup KEYWORD2 +packetLoop KEYWORD2 +packetReceive KEYWORD2 +httpServerReply KEYWORD2 +browseUrl KEYWORD2 +memcpy_P KEYWORD2 +tcpOffset KEYWORD2 +tcpSend KEYWORD2 +tcpReply KEYWORD2 +dnsLookup KEYWORD2 +httpServerReply KEYWORD2 +sizeof KEYWORD2 +SerialPrint_P KEYWORD2 +copyIp KEYWORD2 +getIP_address KEYWORD2 +hisip KEYWORD2 +myip KEYWORD2 +gwip KEYWORD2 +mymask KEYWORD2 +dnsip KEYWORD2 +cleanup KEYWORD2 +freeCount KEYWORD2 +extract KEYWORD2 +save KEYWORD2 +size KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### +STATUS_ERROR LITERAL1 diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/library.properties b/STM32F1/libraries/Serasidis_EtherCard_STM/library.properties new file mode 100644 index 0000000..403657b --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/library.properties @@ -0,0 +1,8 @@ +name=Serasidis_EtherCard_STM +version=1.0 +author=(Original) Jean-Claude Wippler :: Ported to STM32 by Vassilis Serasidis. +email= +sentence=ENC28J60 Ethernet module library +paragraph=Ethernet module library for STM32F1 +url=https://github.com/Serasidis/STM32duino/tree/master/libraries/Serasidis_EtherCard_STM +architectures=STM32F1 diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/src/Doxymods.css b/STM32F1/libraries/Serasidis_EtherCard_STM/src/Doxymods.css new file mode 100644 index 0000000..ec01e9e --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/src/Doxymods.css @@ -0,0 +1,73 @@ +/* Get rid of all those overriden font families */ +body, table, div, p, dl, +#projectname, +#projectbrief, +#nav-tree .label { + font: 15px/21px Georgia,serif +} + +#projectname { + color: #990000; + font-weight: bold; + font-size: 24px; +} + +#titlearea table { + padding: 20px; +} + +dl.reflist dt, div.memproto { + border: 1px solid #A8B8D9; +} + +dl.reflist dd, div.memdoc { + border-width: 0; +} + +div.contents { + margin-left: 20px; margin-right: 16px; + width: 700 px; +} + +/* Get rid of the gradient backgrounds and background colors */ +div.header, +#nav-tree, +.navpath ul, +.memproto, dl.reflist dt { + background: none; +} +#nav-tree .selected { + background: none; + background-color: #990000; + text-shadow: none; +} + +a, a:link, a:visited { + color: #2A5685; + text-decoration: underline; +} +a:active, a:hover { + color: #CC0000; +} + +.directory tr.even, +pre.fragment, +.mdescLeft, .mdescRight, .memItemLeft, .memItemRight, .memTemplItemLeft, .memTemplItemRight, .memTemplParams, +.memproto, dl.reflist dt { + background-color: #EEE; + box-shadow: none; + border-radius: 0; +} +.memdoc, dl.reflist dd { + background: none; + box-shadow: none; + border-radius: 0; +} +pre.fragment { + background-color: #FAFAFA; + border: 1px solid #DADADA; + margin: 1em 1em 1em 1.6em; + overflow-x: auto; + overflow-y: hidden; + width: auto; +} diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/src/EtherCard_STM.cpp b/STM32F1/libraries/Serasidis_EtherCard_STM/src/EtherCard_STM.cpp new file mode 100644 index 0000000..be99de4 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/src/EtherCard_STM.cpp @@ -0,0 +1,433 @@ +// This code slightly follows the conventions of, but is not derived from: +// EHTERSHIELD_H library for Arduino etherShield +// Copyright (c) 2008 Xing Yu. All right reserved. (this is LGPL v2.1) +// It is however derived from the enc28j60 and ip code (which is GPL v2) +// Author: Pascal Stang +// Modified by: Guido Socher +// DHCP code: Andrew Lindsay +// Hence: GPL V2 +// +// 2010-05-19 +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +// +// PIN Connections (Using STM32F103): +// +// ENC28J60 - STM32F103 +// VCC - 3.3V +// GND - GND +// SCK - Pin PA5 +// SO - Pin PA6 +// SI - Pin PA7 +// CS - Pin PA8 +//----------------------------------------------------------------- + + +#include +#include +//#include + +//#define FLOATEMIT // uncomment line to enable $T in emit_P for float emitting + +byte Stash::map[256/8]; +Stash::Block Stash::bufs[2]; + +uint8_t Stash::allocBlock () { + for (uint8_t i = 0; i < sizeof map; ++i) + if (map[i] != 0) + for (uint8_t j = 0; j < 8; ++j) + if (bitRead(map[i], j)) { + bitClear(map[i], j); + return (i << 3) + j; + } + return 0; +} + +void Stash::freeBlock (uint8_t block) { + bitSet(map[block>>3], block & 7); +} + +uint8_t Stash::fetchByte (uint8_t blk, uint8_t off) { + return blk == bufs[0].bnum ? bufs[0].bytes[off] : + blk == bufs[1].bnum ? bufs[1].bytes[off] : + ether.peekin(blk, off); +} + +void Stash::initMap (uint8_t last) { + while (--last > 0) + freeBlock(last); +} + +void Stash::load (uint8_t idx, uint8_t blk) { + if (blk != bufs[idx].bnum) { + if (idx == 0) { + ether.copyout(bufs[idx].bnum, bufs[idx].bytes); + if (blk == bufs[1].bnum) + bufs[1].bnum = 255; // forget read page if same + } else if (blk == bufs[0].bnum) { + // special case: read page is same as write buffer + memcpy(&bufs[1], &bufs[0], sizeof bufs[0]); + return; + } + bufs[idx].bnum = blk; + ether.copyin(bufs[idx].bnum, bufs[idx].bytes); + } +} + +uint8_t Stash::freeCount () { + uint8_t count = 0; + for (uint8_t i = 0; i < 256/8; ++i) + for (uint8_t m = 0x80; m != 0; m >>= 1) + if (map[i] & m) + ++count; + return count; +} + +uint8_t Stash::create () { + uint8_t blk = allocBlock(); + load(0, blk); + bufs[0].head.count = 0; + bufs[0].head.first = bufs[0].head.last = blk; + bufs[0].tail = sizeof (StashHeader); + bufs[0].next = 0; + return open(blk); +} + +uint8_t Stash::open (uint8_t blk) { + curr = blk; + offs = sizeof (StashHeader); + load(1, curr); + memcpy((StashHeader*) this, bufs[1].bytes, sizeof (StashHeader)); + return curr; +} + +void Stash::save () { + load(0, first); + memcpy(bufs[0].bytes, (StashHeader*) this, sizeof (StashHeader)); + if (bufs[1].bnum == first) + load(1, 0); // invalidates original in case it was the same block +} + +void Stash::release () { + while (first > 0) { + freeBlock(first); + first = ether.peekin(first, 63); + } +} + +void Stash::put (char c) { + load(0, last); + uint8_t t = bufs[0].tail; + bufs[0].bytes[t++] = c; + if (t <= 62) + bufs[0].tail = t; + else { + bufs[0].next = allocBlock(); + last = bufs[0].next; + load(0, last); + bufs[0].tail = bufs[0].next = 0; + ++count; + } +} + +char Stash::get () { + load(1, curr); + if (curr == last && offs >= bufs[1].tail) + return 0; + uint8_t b = bufs[1].bytes[offs]; + if (++offs >= 63 && curr != last) { + curr = bufs[1].next; + offs = 0; + } + return b; +} + +uint16_t Stash::size () { + return 63 * count + fetchByte(last, 62) - sizeof (StashHeader); +} + +static char* wtoa (uint16_t value, char* ptr) { + if (value > 9) + ptr = wtoa(value / 10, ptr); + *ptr = '0' + value % 10; + *++ptr = 0; + return ptr; +} + +void Stash::prepare (PGM_P fmt, ...) { + Stash::load(0, 0); + uint16_t* segs = Stash::bufs[0].words; + *segs++ = strlen_P(fmt); +#ifdef __AVR__ + *segs++ = (uint16_t) fmt; +#else + *segs++ = (uint32_t) fmt; + *segs++ = (uint32_t) fmt >> 16; +#endif + va_list ap; + va_start(ap, fmt); + for (;;) { + char c = pgm_read_byte(fmt++); + if (c == 0) + break; + if (c == '$') { +#ifdef __AVR__ + uint16_t argval = va_arg(ap, uint16_t), arglen = 0; +#else + uint32_t argval = va_arg(ap, int), arglen = 0; +#endif + switch (pgm_read_byte(fmt++)) { + case 'D': { + char buf[7]; + wtoa(argval, buf); + arglen = strlen(buf); + break; + } + case 'S': + arglen = strlen((const char*) argval); + break; + case 'F': + arglen = strlen_P((PGM_P) argval); + break; + case 'E': { + byte* s = (byte*) argval; + char d; + //while ((d = eeprom_read_byte(s++)) != 0) + // ++arglen; + break; + } + case 'H': { + Stash stash (argval); + arglen = stash.size(); + break; + } + } +#ifdef __AVR__ + *segs++ = argval; +#else + *segs++ = argval; + *segs++ = argval >> 16; +#endif + Stash::bufs[0].words[0] += arglen - 2; + } + } + va_end(ap); +} + +uint16_t Stash::length () { + Stash::load(0, 0); + return Stash::bufs[0].words[0]; +} + +void Stash::extract (uint16_t offset, uint16_t count, void* buf) { + Stash::load(0, 0); + uint16_t* segs = Stash::bufs[0].words; +#ifdef __AVR__ + PGM_P fmt = (PGM_P) *++segs; +#else + PGM_P fmt = (PGM_P)((segs[2] << 16) | segs[1]); + segs += 2; +#endif + Stash stash; + char mode = '@', tmp[7], *ptr = NULL, *out = (char*) buf; + for (uint16_t i = 0; i < offset + count; ) { + char c = 0; + switch (mode) { + case '@': { + c = pgm_read_byte(fmt++); + if (c == 0) + return; + if (c != '$') + break; +#ifdef __AVR__ + uint16_t arg = *++segs; +#else + uint32_t arg = *++segs; + arg |= *++segs << 16; +#endif + mode = pgm_read_byte(fmt++); + switch (mode) { + case 'D': + wtoa(arg, tmp); + ptr = tmp; + break; + case 'S': + case 'F': + case 'E': + ptr = (char*) arg; + break; + case 'H': + stash.open(arg); + ptr = (char*) &stash; + break; + } + continue; + } + case 'D': + case 'S': + c = *ptr++; + break; + case 'F': + c = pgm_read_byte(ptr++); + break; + case 'E': + //c = eeprom_read_byte((byte*) ptr++); + break; + case 'H': + c = ((Stash*) ptr)->get(); + break; + } + if (c == 0) { + mode = '@'; + continue; + } + if (i >= offset) + *out++ = c; + ++i; + } +} + +void Stash::cleanup () { + Stash::load(0, 0); + uint16_t* segs = Stash::bufs[0].words; +#ifdef __AVR__ + PGM_P fmt = (PGM_P) *++segs; +#else + PGM_P fmt = (PGM_P)((segs[2] << 16) | segs[1]); + segs += 2; +#endif + for (;;) { + char c = pgm_read_byte(fmt++); + if (c == 0) + break; + if (c == '$') { +#ifdef __AVR__ + uint16_t arg = *++segs; +#else + uint32_t arg = *++segs; + arg |= *++segs << 16; +#endif + if (pgm_read_byte(fmt++) == 'H') { + Stash stash (arg); + stash.release(); + } + } + } +} + +void BufferFiller::emit_p(PGM_P fmt, ...) { + va_list ap; + va_start(ap, fmt); + for (;;) { + char c = pgm_read_byte(fmt++); + if (c == 0) + break; + if (c != '$') { + *ptr++ = c; + continue; + } + c = pgm_read_byte(fmt++); + switch (c) { + case 'D': +#ifdef __AVR__ + wtoa(va_arg(ap, uint16_t), (char*) ptr); +#else + wtoa(va_arg(ap, int), (char*) ptr); +#endif + break; +#ifdef FLOATEMIT + case 'T': + dtostrf ( va_arg(ap, double), 10, 3, (char*)ptr ); + break; +#endif + case 'H': { +#ifdef __AVR__ + char p1 = va_arg(ap, uint16_t); +#else + char p1 = va_arg(ap, int); +#endif + char p2; + p2 = (p1 >> 4) & 0x0F; + p1 = p1 & 0x0F; + if (p1 > 9) p1 += 0x07; // adjust 0x0a-0x0f to come out 'a'-'f' + p1 += 0x30; // and complete + if (p2 > 9) p2 += 0x07; // adjust 0x0a-0x0f to come out 'a'-'f' + p2 += 0x30; // and complete + *ptr++ = p2; + *ptr++ = p1; + continue; + } + case 'L': + //ltoa(va_arg(ap, long), (char*) ptr, 10); + break; + case 'S': + strcpy((char*) ptr, va_arg(ap, const char*)); + break; + case 'F': { + PGM_P s = va_arg(ap, PGM_P); + char d; + while ((d = pgm_read_byte(s++)) != 0) + *ptr++ = d; + continue; + } + case 'E': { + byte* s = va_arg(ap, byte*); + char d; + //while ((d = eeprom_read_byte(s++)) != 0) + // *ptr++ = d; + continue; + } + default: + *ptr++ = c; + continue; + } + ptr += strlen((char*) ptr); + } + va_end(ap); +} + +EtherCard ether; + +uint8_t EtherCard::mymac[6]; // my MAC address +uint8_t EtherCard::myip[4]; // my ip address +uint8_t EtherCard::netmask[4]; // subnet mask +uint8_t EtherCard::broadcastip[4]; // broadcast address +uint8_t EtherCard::gwip[4]; // gateway +uint8_t EtherCard::dhcpip[4]; // dhcp server +uint8_t EtherCard::dnsip[4]; // dns server +uint8_t EtherCard::hisip[4]; // ip address of remote host +uint16_t EtherCard::hisport = 80; // tcp port to browse to +bool EtherCard::using_dhcp = false; +bool EtherCard::persist_tcp_connection = false; +uint16_t EtherCard::delaycnt = 0; //request gateway ARP lookup + +uint8_t EtherCard::begin (const uint16_t size, + const uint8_t* macaddr, + uint8_t csPin) { + using_dhcp = false; + Stash::initMap(56); + copyMac(mymac, macaddr); + return initialize(size, mymac, csPin); +} + +bool EtherCard::staticSetup (const uint8_t* my_ip, + const uint8_t* gw_ip, + const uint8_t* dns_ip, + const uint8_t* mask) { + using_dhcp = false; + + if (my_ip != 0) + copyIp(myip, my_ip); + if (gw_ip != 0) + setGwIp(gw_ip); + if (dns_ip != 0) + copyIp(dnsip, dns_ip); + if(mask != 0) + copyIp(netmask, mask); + updateBroadcastAddress(); + delaycnt = 0; //request gateway ARP lookup + return true; +} diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/src/EtherCard_STM.h b/STM32F1/libraries/Serasidis_EtherCard_STM/src/EtherCard_STM.h new file mode 100644 index 0000000..7a85834 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/src/EtherCard_STM.h @@ -0,0 +1,595 @@ +// This code slightly follows the conventions of, but is not derived from: +// EHTERSHIELD_H library for Arduino etherShield +// Copyright (c) 2008 Xing Yu. All right reserved. (this is LGPL v2.1) +// It is however derived from the enc28j60 and ip code (which is GPL v2) +// Author: Pascal Stang +// Modified by: Guido Socher +// DHCP code: Andrew Lindsay +// Hence: GPL V2 +// +// 2010-05-19 +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +// +// PIN Connections (Using STM32F103): +// VCC - 3.3V +// GND - GND +// SCK - Pin PA5 +// SO - Pin PA6 +// SI - Pin PA7 +// CS - Pin PA8 +//----------------------------------------------------------------- + + + +#ifndef EtherCard_STM_h +#define EtherCard_STM_h +#ifndef __PROG_TYPES_COMPAT__ + #define __PROG_TYPES_COMPAT__ +#endif + +#if ARDUINO >= 100 +#include // Arduino 1.0 +#define WRITE_RESULT size_t +#define WRITE_RETURN return 1; +#else +#include // Arduino 0022 +#define WRITE_RESULT void +#define WRITE_RETURN +#endif + +#include +#include "enc28j60.h" +#include "net.h" + +/** This type definition defines the structure of a UDP server event handler callback funtion */ +typedef void (*UdpServerCallback)( + uint16_t dest_port, ///< Port the packet was sent to + uint8_t src_ip[4], ///< IP address of the sender + uint16_t src_port, ///< Port the packet was sent from + const char *data, ///< UDP payload data + uint16_t len); ///< Length of the payload data + +/** This type definition defines the structure of a DHCP Option callback funtion */ +typedef void (*DhcpOptionCallback)( + uint8_t option, ///< The option number + const byte* data, ///< DHCP option data + uint8_t len); ///< Length of the DHCP option data + + +/** This structure describes the structure of memory used within the ENC28J60 network interface. */ +typedef struct { + uint8_t count; ///< Number of allocated pages + uint8_t first; ///< First allocated page + uint8_t last; ///< Last allocated page +} StashHeader; + +/** This class provides access to the memory within the ENC28J60 network interface. */ +class Stash : public /*Stream*/ Print, private StashHeader { + uint8_t curr; //!< Current page + uint8_t offs; //!< Current offset in page + + typedef struct { + union { + uint8_t bytes[64]; + uint16_t words[32]; + struct { + StashHeader head; + uint8_t filler[59]; + uint8_t tail; + uint8_t next; + }; + }; + uint8_t bnum; + } Block; + + static uint8_t allocBlock (); + static void freeBlock (uint8_t block); + static uint8_t fetchByte (uint8_t blk, uint8_t off); + + static Block bufs[2]; + static uint8_t map[256/8]; + +public: + static void initMap (uint8_t last); + static void load (uint8_t idx, uint8_t blk); + static uint8_t freeCount (); + + Stash () : curr (0) { first = 0; } + Stash (uint8_t fd) { open(fd); } + + uint8_t create (); + uint8_t open (uint8_t blk); + void save (); + void release (); + + void put (char c); + char get (); + uint16_t size (); + + virtual WRITE_RESULT write(uint8_t b) { put(b); WRITE_RETURN } + + // virtual int available() { + // if (curr != last) + // return 1; + // load(1, last); + // return offs < bufs[1].tail; + // } + // virtual int read() { + // return available() ? get() : -1; + // } + // virtual int peek() { + // return available() ? bufs[1].bytes[offs] : -1; + // } + // virtual void flush() { + // curr = last; + // offs = 63; + // } + + static void prepare (PGM_P fmt, ...); + static uint16_t length (); + static void extract (uint16_t offset, uint16_t count, void* buf); + static void cleanup (); + + friend void dumpBlock (const char* msg, uint8_t idx); // optional + friend void dumpStash (const char* msg, void* ptr); // optional +}; + +/** This class populates network send and receive buffers. +* +* This class provides formatted printing into memory. Users can use it to write into send buffers. +* +* Nota: PGM_P: is a pointer to a string in program space (defined in the source code) +* +* # Format string +* +* | Format | Parameter | Output +* |--------|-------------|---------- +* | $D | uint16_t | Decimal representation +* | $T ¤ | double | Decimal representation with 3 digits after decimal sign ([-]d.ddd) +* | $H | uint16_t | Hexadecimal value of lsb (from 00 to ff) +* | $L | long | Decimal representation +* | $S | const char* | Copy null terminated string from main memory +* | $F | PGM_P | Copy null terminated string from program space +* | $E | byte* | Copy null terminated string from EEPROM space +* | $$ | _none_ | '$' +* +* ¤ _Available only if FLOATEMIT is defined_ +* +* # Examples +* ~~~~~~~~~~~~~{.c} +* uint16_t ddd = 123; +* double ttt = 1.23; +* uint16_t hhh = 0xa4; +* long lll = 123456789; +* char * sss; +* char fff[] PROGMEM = "MyMemory"; +* +* sss[0] = 'G'; +* sss[1] = 'P'; +* sss[2] = 'L'; +* sss[3] = 0; +* buf.emit_p( PSTR("ddd=$D\n"), ddd ); // "ddd=123\n" +* buf.emit_p( PSTR("ttt=$T\n"), ttt ); // "ttt=1.23\n" **TO CHECK** +* buf.emit_p( PSTR("hhh=$H\n"), hhh ); // "hhh=a4\n" +* buf.emit_p( PSTR("lll=$L\n"), lll ); // "lll=123456789\n" +* buf.emit_p( PSTR("sss=$S\n"), sss ); // "sss=GPL\n" +* buf.emit_p( PSTR("fff=$F\n"), fff ); // "fff=MyMemory\n" +* ~~~~~~~~~~~~~ +* +*/ +class BufferFiller : public Print { + uint8_t *start; //!< Pointer to start of buffer + uint8_t *ptr; //!< Pointer to cursor position +public: + /** @brief Empty constructor + */ + BufferFiller () {} + + /** @brief Constructor + * @param buf Pointer to the ethernet data buffer + */ + BufferFiller (uint8_t* buf) : start (buf), ptr (buf) {} + + /** @brief Add formatted text to buffer + * @param fmt Format string (see Class description) + * @param ... parameters for format string + */ + void emit_p (PGM_P fmt, ...); + + /** @brief Add data to buffer from main memory + * @param s Pointer to data + * @param n Number of characters to copy + */ + void emit_raw (const char* s, uint16_t n) { memcpy(ptr, s, n); ptr += n; } + + /** @brief Add data to buffer from program space string + * @param p Program space string pointer + * @param n Number of characters to copy + */ + void emit_raw_p (PGM_P p, uint16_t n) { memcpy_P(ptr, p, n); ptr += n; } + + /** @brief Get pointer to start of buffer + * @return uint8_t* Pointer to start of buffer + */ + uint8_t* buffer () const { return start; } + + /** @brief Get cursor position + * @return uint16_t Cursor postion + */ + uint16_t position () const { return ptr - start; } + + /** @brief Write one byte to buffer + * @param v Byte to add to buffer + */ + virtual WRITE_RESULT write (uint8_t v) { *ptr++ = v; WRITE_RETURN } +}; + +/** This class provides the main interface to a ENC28J60 based network interface card and is the class most users will use. +* @note All TCP/IP client (outgoing) connections are made from source port in range 2816-3071. Do not use these source ports for other purposes. +*/ +class EtherCard : public Ethernet { +public: + static uint8_t mymac[6]; ///< MAC address + static uint8_t myip[4]; ///< IP address + static uint8_t netmask[4]; ///< Netmask + static uint8_t broadcastip[4]; ///< Subnet broadcast address + static uint8_t gwip[4]; ///< Gateway + static uint8_t dhcpip[4]; ///< DHCP server IP address + static uint8_t dnsip[4]; ///< DNS server IP address + static uint8_t hisip[4]; ///< DNS lookup result + static uint16_t hisport; ///< TCP port to connect to (default 80) + static bool using_dhcp; ///< True if using DHCP + static bool persist_tcp_connection; ///< False to break connections on first packet received + static uint16_t delaycnt; ///< Counts number of cycles of packetLoop when no packet received - used to trigger periodic gateway ARP request + + // EtherCard.cpp + /** @brief Initialise the network interface + * @param size Size of data buffer + * @param macaddr Hardware address to assign to the network interface (6 bytes) + * @param csPin Arduino pin number connected to chip select. Default = 8 + * @return uint8_t Firmware version or zero on failure. + */ + static uint8_t begin (const uint16_t size, const uint8_t* macaddr, + uint8_t csPin =PA8); + + /** @brief Configure network interface with static IP + * @param my_ip IP address (4 bytes). 0 for no change. + * @param gw_ip Gateway address (4 bytes). 0 for no change. Default = 0 + * @param dns_ip DNS address (4 bytes). 0 for no change. Default = 0 + * @param mask Subnet mask (4 bytes). 0 for no change. Default = 0 + * @return bool Returns true on success - actually always true + */ + static bool staticSetup (const uint8_t* my_ip, + const uint8_t* gw_ip = 0, + const uint8_t* dns_ip = 0, + const uint8_t* mask = 0); + + // tcpip.cpp + /** @brief Sends a UDP packet to the IP address of last processed received packet + * @param data Pointer to data payload + * @param len Size of data payload (max 220) + * @param port Source IP port + */ + static void makeUdpReply (const char *data, uint8_t len, uint16_t port); + + /** @brief Parse received data + * @param plen Size of data to parse (e.g. return value of packetReceive()). + * @return uint16_t Offset of TCP payload data in data buffer or zero if packet processed + * @note Data buffer is shared by receive and transmit functions + * @note Only handles ARP and IP + */ + static uint16_t packetLoop (uint16_t plen); + + /** @brief Accept a TCP/IP connection + * @param port IP port to accept on - do nothing if wrong port + * @param plen Number of bytes in packet + * @return uint16_t Offset within packet of TCP payload. Zero for no data. + */ + static uint16_t accept (uint16_t port, uint16_t plen); + + /** @brief Send a response to a HTTP request + * @param dlen Size of the HTTP (TCP) payload + */ + static void httpServerReply (uint16_t dlen); + + /** @brief Send a response to a HTTP request + * @param dlen Size of the HTTP (TCP) payload + * @param flags TCP flags + */ + static void httpServerReply_with_flags (uint16_t dlen , uint8_t flags); + + /** @brief Acknowledge TCP message + * @todo Is this / should this be private? + */ + static void httpServerReplyAck (); + + /** @brief Set the gateway address + * @param gwipaddr Gateway address (4 bytes) + */ + static void setGwIp (const uint8_t *gwipaddr); + + /** @brief Updates the broadcast address based on current IP address and subnet mask + */ + static void updateBroadcastAddress(); + + /** @brief Check if got gateway hardware address (ARP lookup) + * @return unit8_t True if gateway found + */ + static uint8_t clientWaitingGw (); + + /** @brief Check if got gateway DNS address (ARP lookup) + * @return unit8_t True if DNS found + */ + static uint8_t clientWaitingDns (); + + /** @brief Prepare a TCP request + * @param result_cb Pointer to callback function that handles TCP result + * @param datafill_cb Pointer to callback function that handles TCP data payload + * @param port Remote TCP/IP port to connect to + * @return unit8_t ID of TCP/IP session (0-7) + * @note Return value provides id of the request to allow up to 7 concurrent requests + */ + static uint8_t clientTcpReq (uint8_t (*result_cb)(uint8_t,uint8_t,uint16_t,uint16_t), + uint16_t (*datafill_cb)(uint8_t),uint16_t port); + + /** @brief Prepare HTTP request + * @param urlbuf Pointer to c-string URL folder + * @param urlbuf_varpart Pointer to c-string URL file + * @param hoststr Pointer to c-string hostname + * @param additionalheaderline Pointer to c-string with additional HTTP header info + * @param callback Pointer to callback function to handle response + * @note Request sent in main packetloop + */ + static void browseUrl (const char *urlbuf, const char *urlbuf_varpart, + const char *hoststr, const char *additionalheaderline, + void (*callback)(uint8_t,uint16_t,uint16_t)); + + /** @brief Prepare HTTP request + * @param urlbuf Pointer to c-string URL folder + * @param urlbuf_varpart Pointer to c-string URL file + * @param hoststr Pointer to c-string hostname + * @param callback Pointer to callback function to handle response + * @note Request sent in main packetloop + */ + static void browseUrl (const char *urlbuf, const char *urlbuf_varpart, + const char *hoststr, + void (*callback)(uint8_t,uint16_t,uint16_t)); + + /** @brief Prepare HTTP post message + * @param urlbuf Pointer to c-string URL folder + * @param hoststr Pointer to c-string hostname + * @param additionalheaderline Pointer to c-string with additional HTTP header info + * @param postval Pointer to c-string HTML Post value + * @param callback Pointer to callback function to handle response + * @note Request sent in main packetloop + */ + static void httpPost (const char *urlbuf, const char *hoststr, + const char *additionalheaderline, const char *postval, + void (*callback)(uint8_t,uint16_t,uint16_t)); + + /** @brief Send NTP request + * @param ntpip IP address of NTP server + * @param srcport IP port to send from + */ + static void ntpRequest (uint8_t *ntpip,uint8_t srcport); + + /** @brief Process network time protocol response + * @param time Pointer to integer to hold result + * @param dstport_l Destination port to expect response. Set to zero to accept on any port + * @return uint8_t True (1) on success + */ + static uint8_t ntpProcessAnswer (uint32_t *time, uint8_t dstport_l); + + /** @brief Prepare a UDP message for transmission + * @param sport Source port + * @param dip Pointer to 4 byte destination IP address + * @param dport Destination port + */ + static void udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport); + + /** @brief Transmit UDP packet + * @param len Size of payload + */ + static void udpTransmit (uint16_t len); + + /** @brief Sends a UDP packet + * @param data Pointer to data + * @param len Size of payload (maximum 220 octets / bytes) + * @param sport Source port + * @param dip Pointer to 4 byte destination IP address + * @param dport Destination port + */ + static void sendUdp (const char *data, uint8_t len, uint16_t sport, + const uint8_t *dip, uint16_t dport); + + /** @brief Resister the function to handle ping events + * @param cb Pointer to function + */ + static void registerPingCallback (void (*cb)(uint8_t*)); + + /** @brief Send ping + * @param destip Ponter to 4 byte destination IP address + */ + static void clientIcmpRequest (const uint8_t *destip); + + /** @brief Check for ping response + * @param ip_monitoredhost Pointer to 4 byte IP address of host to check + * @return uint8_t True (1) if ping response from specified host + */ + static uint8_t packetLoopIcmpCheckReply (const uint8_t *ip_monitoredhost); + + /** @brief Send a wake on lan message + * @param wolmac Pointer to 6 byte hardware (MAC) address of host to send message to + */ + static void sendWol (uint8_t *wolmac); + + // new stash-based API + /** @brief Send TCP request + */ + static uint8_t tcpSend (); + + /** @brief Get TCP reply + * @return char* Pointer to TCP reply payload. NULL if no data. + */ + static const char* tcpReply (uint8_t fd); + + /** @brief Configure TCP connections to be persistent or not + * @param persist True to maintain TCP connection. False to finish TCP connection after first packet. + */ + static void persistTcpConnection(bool persist); + + //udpserver.cpp + /** @brief Register function to handle incomint UDP events + * @param callback Function to handle event + * @param port Port to listen on + */ + static void udpServerListenOnPort(UdpServerCallback callback, uint16_t port); + + /** @brief Pause listing on UDP port + * @brief port Port to pause + */ + static void udpServerPauseListenOnPort(uint16_t port); + + /** @brief Resume listing on UDP port + * @brief port Port to pause + */ + static void udpServerResumeListenOnPort(uint16_t port); + + /** @brief Check if UDP server is listening on any ports + * @return bool True if listening on any ports + */ + static bool udpServerListening(); //called by tcpip, in packetLoop + + /** @brief Passes packet to UDP Server + * @param len Not used + * @return bool True if packet processed + */ + static bool udpServerHasProcessedPacket(uint16_t len); //called by tcpip, in packetLoop + + // dhcp.cpp + /** @brief Update DHCP state + * @param len Length of received data packet + */ + static void DhcpStateMachine(uint16_t len); + + /** @brief Not implemented + * @todo Implement dhcpStartTime or remove declaration + */ + static uint32_t dhcpStartTime (); + + /** @brief Not implemented + * @todo Implement dhcpLeaseTime or remove declaration + */ + static uint32_t dhcpLeaseTime (); + + /** @brief Not implemented + * @todo Implement dhcpLease or remove declaration + */ + static bool dhcpLease (); + + /** @brief Configure network interface with DHCP + * @return bool True if DHCP successful + * @note Blocks until DHCP complete or timeout after 60 seconds + */ + static bool dhcpSetup (const char *hname = NULL, bool fromRam =false); + + /** @brief Register a callback for a specific DHCP option number + * @param option The option number to request from the DHCP server + * @param callback The function to be call when the option is received + */ + static void dhcpAddOptionCallback(uint8_t option, DhcpOptionCallback callback); + + // dns.cpp + /** @brief Perform DNS lookup + * @param name Host name to lookup + * @param fromRam Set true to look up cached name. Default = false + * @return bool True on success. + * @note Result is stored in hisip member + */ + static bool dnsLookup (const char* name, bool fromRam =false); + + // webutil.cpp + /** @brief Copies an IP address + * @param dst Pointer to the 4 byte destination + * @param src Pointer to the 4 byte source + * @note There is no check of source or destination size. Ensure both are 4 bytes + */ + static void copyIp (uint8_t *dst, const uint8_t *src); + + /** @brief Copies a hardware address + * @param dst Pointer to the 6 byte destination + * @param src Pointer to the 6 byte destination + * @note There is no check of source or destination size. Ensure both are 6 bytes + */ + static void copyMac (uint8_t *dst, const uint8_t *src); + + /** @brief Output to serial port in dotted decimal IP format + * @param buf Pointer to 4 byte IP address + * @note There is no check of source or destination size. Ensure both are 4 bytes + */ + static void printIp (const uint8_t *buf); + + /** @brief Output message and IP address to serial port in dotted decimal IP format + * @param msg Pointer to null terminated string + * @param buf Pointer to 4 byte IP address + * @note There is no check of source or destination size. Ensure both are 4 bytes + */ + static void printIp (const char* msg, const uint8_t *buf); + + /** @brief Output Flash String Helper and IP address to serial port in dotted decimal IP format + * @param ifsh Pointer to Flash String Helper + * @param buf Pointer to 4 byte IP address + * @note There is no check of source or destination size. Ensure both are 4 bytes + * @todo What is a FlashStringHelper? + */ + static void printIp (const __FlashStringHelper *ifsh, const uint8_t *buf); + + /** @brief Search for a string of the form key=value in a string that looks like q?xyz=abc&uvw=defgh HTTP/1.1\\r\\n + * @param str Pointer to the null terminated string to search + * @param strbuf Pointer to buffer to hold null terminated result string + * @param maxlen Maximum length of result + * @param key Pointer to null terminated string holding the key to search for + * @return unit_t Length of the value. 0 if not found + * @note Ensure strbuf has memory allocated of at least maxlen + 1 (to accomodate result plus terminating null) + */ + static uint8_t findKeyVal(const char *str,char *strbuf, + uint8_t maxlen, const char *key); + + /** @brief Decode a URL string e.g "hello%20joe" or "hello+joe" becomes "hello joe" + * @param urlbuf Pointer to the null terminated URL + * @note urlbuf is modified + */ + static void urlDecode(char *urlbuf); + + /** @brief Encode a URL, replacing illegal charaters like ' ' + * @param str Pointer to the null terminated string to encode + * @param urlbuf Pointer to a buffer to contain the null terminated encoded URL + * @note There must be enough space in urlbuf. In the worst case that is 3 times the length of str + */ + static void urlEncode(char *str,char *urlbuf); + + /** @brief Convert an IP address from dotted decimal formated string to 4 bytes + * @param bytestr Pointer to the 4 byte array to store IP address + * @param str Pointer to string to parse + * @return uint8_t 0 on success + */ + static uint8_t parseIp(uint8_t *bytestr,char *str); + + /** @brief Convert a byte array to a human readable display string + * @param resultstr Pointer to a buffer to hold the resulting null terminated string + * @param bytestr Pointer to the byte array containing the address to convert + * @param len Length of the array (4 for IP address, 6 for hardware (MAC) address) + * @param separator Delimiter character (typically '.' for IP address and ':' for hardware (MAC) address) + * @param base Base for numerical representation (typically 10 for IP address and 16 for hardware (MAC) address + */ + static void makeNetStr(char *resultstr,uint8_t *bytestr,uint8_t len, + char separator,uint8_t base); +}; + +extern EtherCard ether; //!< Global presentation of EtherCard class + +#endif diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/src/dhcp.cpp b/STM32F1/libraries/Serasidis_EtherCard_STM/src/dhcp.cpp new file mode 100644 index 0000000..f8f8f21 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/src/dhcp.cpp @@ -0,0 +1,397 @@ +// DHCP look-up functions based on the udp client +// http://www.ietf.org/rfc/rfc2131.txt +// +// Author: Andrew Lindsay +// Rewritten and optimized by Jean-Claude Wippler, http://jeelabs.org/ +// +// Rewritten dhcpStateMachine by Chris van den Hooven +// as to implement dhcp-renew when lease expires (jun 2012) +// +// Various modifications and bug fixes contributed by Victor Aprea (oct 2012) +// +// Copyright: GPL V2 +// See http://www.gnu.org/licenses/gpl.html + +//#define DHCPDEBUG + +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +//----------------------------------------------------------------- + +#include "EtherCard_STM.h" +#include "net.h" + +#define gPB ether.buffer + +#define DHCP_BOOTREQUEST 1 +#define DHCP_BOOTRESPONSE 2 + +// DHCP Message Type (option 53) (ref RFC 2132) +#define DHCP_DISCOVER 1 +#define DHCP_OFFER 2 +#define DHCP_REQUEST 3 +#define DHCP_DECLINE 4 +#define DHCP_ACK 5 +#define DHCP_NAK 6 +#define DHCP_RELEASE 7 + +// DHCP States for access in applications (ref RFC 2131) +enum { + DHCP_STATE_INIT, + DHCP_STATE_SELECTING, + DHCP_STATE_REQUESTING, + DHCP_STATE_BOUND, + DHCP_STATE_RENEWING, +}; + +/* + op 1 Message op code / message type. + 1 = BOOTREQUEST, 2 = BOOTREPLY + htype 1 Hardware address type, see ARP section in "Assigned + Numbers" RFC; e.g., '1' = 10mb ethernet. + hlen 1 Hardware address length (e.g. '6' for 10mb + ethernet). + hops 1 Client sets to zero, optionally used by relay agents + when booting via a relay agent. + xid 4 Transaction ID, a random number chosen by the + client, used by the client and server to associate + messages and responses between a client and a + server. + secs 2 Filled in by client, seconds elapsed since client + began address acquisition or renewal process. + flags 2 Flags (see figure 2). + ciaddr 4 Client IP address; only filled in if client is in + BOUND, RENEW or REBINDING state and can respond + to ARP requests. + yiaddr 4 'your' (client) IP address. + siaddr 4 IP address of next server to use in bootstrap; + returned in DHCPOFFER, DHCPACK by server. + giaddr 4 Relay agent IP address, used in booting via a + relay agent. + chaddr 16 Client hardware address. + sname 64 Optional server host name, null terminated string. + file 128 Boot file name, null terminated string; "generic" + name or null in DHCPDISCOVER, fully qualified + directory-path name in DHCPOFFER. + options var Optional parameters field. See the options + documents for a list of defined options. + */ + + + + +// size 236 +typedef struct { + byte op, htype, hlen, hops; + uint32_t xid; + uint16_t secs, flags; + byte ciaddr[4], yiaddr[4], siaddr[4], giaddr[4]; + byte chaddr[16], sname[64], file[128]; + // options +} DHCPdata; + +#define DHCP_SRC_PORT 67 +#define DHCP_DEST_PORT 68 + +// timeouts im ms +#define DHCP_REQUEST_TIMEOUT 10000 + +#define DHCP_HOSTNAME_MAX_LEN 32 + +static byte dhcpState = DHCP_STATE_INIT; +static char hostname[DHCP_HOSTNAME_MAX_LEN] = "Arduino-00"; +static uint32_t currentXid; +static uint32_t stateTimer; +static uint32_t leaseStart; +static uint32_t leaseTime; +static byte* bufPtr; + +static uint8_t dhcpCustomOptionNum = 0; +static DhcpOptionCallback dhcpCustomOptionCallback = NULL; + +// static uint8_t allOnes[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + +static void addToBuf (byte b) { + *bufPtr++ = b; +} + +static void addBytes (byte len, const byte* data) { + while (len-- > 0) + addToBuf(*data++); +} + + +// Main DHCP sending function + +// implemented +// state / msgtype +// INIT / DHCPDISCOVER +// SELECTING / DHCPREQUEST +// BOUND (RENEWING) / DHCPREQUEST + +// ---------------------------------------------------------- +// | |SELECTING |RENEWING |INIT | +// ---------------------------------------------------------- +// |broad/unicast |broadcast |unicast |broadcast | +// |server-ip |MUST |MUST NOT |MUST NOT | option 54 +// |requested-ip |MUST |MUST NOT |MUST NOT | option 50 +// |ciaddr |zero |IP address |zero | +// ---------------------------------------------------------- + +// options used (both send/receive) +// 12 Host Name Option +// 50 Requested IP Address +// 51 IP Address Lease Time +// 53 DHCP message type +// 54 Server-identifier +// 55 Parameter request list +// 58 Renewal (T1) Time Value +// 61 Client-identifier +// 255 End + +static void send_dhcp_message (void) { + + uint8_t allOnes[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + + memset(gPB, 0, UDP_DATA_P + sizeof( DHCPdata )); + + EtherCard::udpPrepare(DHCP_DEST_PORT, + (dhcpState == DHCP_STATE_BOUND ? EtherCard::dhcpip : allOnes), + DHCP_SRC_PORT); // SRC<->DST ?? + + if (dhcpState != DHCP_STATE_BOUND) + EtherCard::copyMac(gPB + ETH_DST_MAC, allOnes); //force broadcast mac + + // Build DHCP Packet from buf[UDP_DATA_P] + DHCPdata *dhcpPtr = (DHCPdata*) (gPB + UDP_DATA_P); + dhcpPtr->op = DHCP_BOOTREQUEST; + dhcpPtr->htype = 1; + dhcpPtr->hlen = 6; + dhcpPtr->xid = currentXid; + if (dhcpState == DHCP_STATE_BOUND) { + EtherCard::copyIp(dhcpPtr->ciaddr, EtherCard::myip); + } + EtherCard::copyMac(dhcpPtr->chaddr, EtherCard::mymac); + + // options defined as option, length, value + bufPtr = gPB + UDP_DATA_P + sizeof( DHCPdata ); + // DHCP magic cookie, followed by message type + static byte cookie[] = { 99, 130, 83, 99, 53, 1 }; + addBytes(sizeof cookie, cookie); + // addToBuf(53); // DHCP_STATE_SELECTING, DHCP_STATE_REQUESTING + // addToBuf(1); // Length + addToBuf(dhcpState == DHCP_STATE_INIT ? DHCP_DISCOVER : DHCP_REQUEST); + + // Client Identifier Option, this is the client mac address + addToBuf(61); // Client identifier + addToBuf(7); // Length + addToBuf(0x01); // Ethernet + addBytes(6, EtherCard::mymac); + + addToBuf(12); // Host name Option + addToBuf(10); + addBytes(10, (byte*) hostname); + + if( dhcpState == DHCP_STATE_SELECTING) { + addToBuf(50); // Request IP address + addToBuf(4); + addBytes(4, EtherCard::myip); + + // Request using server ip address + addToBuf(54); // Server IP address + addToBuf(4); + addBytes(4, EtherCard::dhcpip); + } + + // Additional info in parameter list - minimal list for what we need + byte len = 3; + if (dhcpCustomOptionNum) + len++; + addToBuf(55); // Parameter request list + addToBuf(len); // Length + addToBuf(1); // Subnet mask + addToBuf(3); // Route/Gateway + addToBuf(6); // DNS Server + if (dhcpCustomOptionNum) + addToBuf(dhcpCustomOptionNum); // Custom option + addToBuf(255); // end option + + // packet size will be under 300 bytes + EtherCard::udpTransmit((bufPtr - gPB) - UDP_DATA_P); +} + +static void process_dhcp_offer (uint16_t len) { + // Map struct onto payload + DHCPdata *dhcpPtr = (DHCPdata*) (gPB + UDP_DATA_P); + // Offered IP address is in yiaddr + EtherCard::copyIp(EtherCard::myip, dhcpPtr->yiaddr); + // Scan through variable length option list identifying options we want + byte *ptr = (byte*) (dhcpPtr + 1) + 4; + bool done = false; + do { + byte option = *ptr++; + byte optionLen = *ptr++; + switch (option) { + case 1: EtherCard::copyIp(EtherCard::netmask, ptr); + break; + case 3: EtherCard::copyIp(EtherCard::gwip, ptr); + break; + case 6: EtherCard::copyIp(EtherCard::dnsip, ptr); + break; + case 51: + case 58: leaseTime = 0; // option 58 = Renewal Time, 51 = Lease Time + for (byte i = 0; i<4; i++) + leaseTime = (leaseTime << 8) + ptr[i]; + leaseTime *= 1000; // milliseconds + break; + case 54: EtherCard::copyIp(EtherCard::dhcpip, ptr); + break; + case 255: done = true; + break; + default: { + // Is is a custom configured option? + if (dhcpCustomOptionCallback && option == dhcpCustomOptionNum) { + dhcpCustomOptionCallback(option, ptr, optionLen); + } + } + } + ptr += optionLen; + } while (!done && ptr < gPB + len); +} + +static bool dhcp_received_message_type (uint16_t len, byte msgType) { + // Map struct onto payload + DHCPdata *dhcpPtr = (DHCPdata*) (gPB + UDP_DATA_P); + + if (len >= 70 && gPB[UDP_SRC_PORT_L_P] == DHCP_SRC_PORT && + dhcpPtr->xid == currentXid ) { + + byte *ptr = (byte*) (dhcpPtr + 1) + 4; + do { + byte option = *ptr++; + byte optionLen = *ptr++; + if(option == 53 && *ptr == msgType ) { + // DHCP Message type match found + return true; + } + ptr += optionLen; + } while (ptr < gPB + len); + } + return false; +} + +bool EtherCard::dhcpSetup (const char *hname, bool fromRam) { + // Use during setup, as this discards all incoming requests until it returns. + // That shouldn't be a problem, because we don't have an IP-address yet. + // Will try 60 secs to obtain DHCP-lease. + + using_dhcp = true; + + if(hname != NULL){ + if(fromRam){ + strncpy(hostname, hname, DHCP_HOSTNAME_MAX_LEN); + } + else{ + strncpy(hostname, hname, DHCP_HOSTNAME_MAX_LEN); + } + } + else{ + // Set a unique hostname, use Arduino-?? with last octet of mac address + hostname[8] = '0' + (mymac[5] >> 4); + hostname[9] = '0' + (mymac[5] & 0x0F); + } + + dhcpState = DHCP_STATE_INIT; + uint16_t start = millis(); + + while (dhcpState != DHCP_STATE_BOUND && (uint16_t) (millis() - start) < 60000) { + if (isLinkUp()) DhcpStateMachine(packetReceive()); + } + updateBroadcastAddress(); + delaycnt = 0; + return dhcpState == DHCP_STATE_BOUND ; +} + +void EtherCard::dhcpAddOptionCallback(uint8_t option, DhcpOptionCallback callback) +{ + dhcpCustomOptionNum = option; + dhcpCustomOptionCallback = callback; +} + +void EtherCard::DhcpStateMachine (uint16_t len) { + +#ifdef DHCPDEBUG + if (dhcpState != DHCP_STATE_BOUND) { + Serial.print(millis()); + Serial.print(" State: "); + } + switch (dhcpState) { + case DHCP_STATE_INIT: + Serial.println("Init"); + break; + case DHCP_STATE_SELECTING: + Serial.println("Selecting"); + break; + case DHCP_STATE_REQUESTING: + Serial.println("Requesting"); + break; + case DHCP_STATE_RENEWING: + Serial.println("Renew"); + break; + } +#endif + + switch (dhcpState) { + + case DHCP_STATE_BOUND: + //!@todo Due to millis() 49 day wrap-around, DHCP renewal may not work as expected + if (millis() >= leaseStart + leaseTime) { + send_dhcp_message(); + dhcpState = DHCP_STATE_RENEWING; + stateTimer = millis(); + } + break; + + case DHCP_STATE_INIT: + currentXid = millis(); + memset(myip,0,4); // force ip 0.0.0.0 + send_dhcp_message(); + enableBroadcast(true); //Temporarily enable broadcasts + dhcpState = DHCP_STATE_SELECTING; + stateTimer = millis(); + break; + + case DHCP_STATE_SELECTING: + if (dhcp_received_message_type(len, DHCP_OFFER)) { + process_dhcp_offer(len); + send_dhcp_message(); + dhcpState = DHCP_STATE_REQUESTING; + stateTimer = millis(); + } else { + if (millis() > stateTimer + DHCP_REQUEST_TIMEOUT) { + dhcpState = DHCP_STATE_INIT; + } + } + break; + + case DHCP_STATE_REQUESTING: + case DHCP_STATE_RENEWING: + if (dhcp_received_message_type(len, DHCP_ACK)) { + disableBroadcast(true); //Disable broadcast after temporary enable + leaseStart = millis(); + if (gwip[0] != 0) setGwIp(gwip); // why is this? because it initiates an arp request + dhcpState = DHCP_STATE_BOUND; + } else { + if (millis() > stateTimer + DHCP_REQUEST_TIMEOUT) { + dhcpState = DHCP_STATE_INIT; + } + } + break; + + } +} + + + diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/src/dns.cpp b/STM32F1/libraries/Serasidis_EtherCard_STM/src/dns.cpp new file mode 100644 index 0000000..4f4957f --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/src/dns.cpp @@ -0,0 +1,117 @@ +// DNS look-up functions based on the udp client +// Author: Guido Socher +// Copyright: GPL V2 +// +// 2010-05-20 + +#include "EtherCard_STM.h" +#include "net.h" + +#define gPB ether.buffer + +static byte dnstid_l; // a counter for transaction ID +#define DNSCLIENT_SRC_PORT_H 0xE0 + +static void dnsRequest (const char *hostname, bool fromRam) { + ++dnstid_l; // increment for next request, finally wrap + if (ether.dnsip[0] == 0) + memset(ether.dnsip, 8, 4); // use 8.8.8.8 Google DNS as default + ether.udpPrepare((DNSCLIENT_SRC_PORT_H << 8) | dnstid_l, ether.dnsip, 53); + memset(gPB + UDP_DATA_P, 0, 12); + + byte *p = gPB + UDP_DATA_P + 12; + char c; + do { + byte n = 0; + for(;;) { + c = fromRam ? *hostname : pgm_read_byte(hostname); + ++hostname; + if (c == '.' || c == 0) + break; + p[++n] = c; + } + *p++ = n; + p += n; + } while (c != 0); + + *p++ = 0; // terminate with zero, means root domain. + *p++ = 0; + *p++ = 1; // type A + *p++ = 0; + *p++ = 1; // class IN + byte i = p - gPB - UDP_DATA_P; + gPB[UDP_DATA_P] = i; + gPB[UDP_DATA_P+1] = dnstid_l; + gPB[UDP_DATA_P+2] = 1; // flags, standard recursive query + gPB[UDP_DATA_P+5] = 1; // 1 question + ether.udpTransmit(i); +} + +/** @brief Check if packet is DNS response. + @param plen Size of packet + @return bool True if DNS response has error. False if not DNS response or DNS response OK. + @note hisip contains IP address of requested host or 0.0.0.0 on failure +*/ +static bool checkForDnsAnswer (uint16_t plen) { + byte *p = gPB + UDP_DATA_P; //start of UDP payload + if (plen < 70 || gPB[UDP_SRC_PORT_L_P] != 53 || //from DNS source port + gPB[UDP_DST_PORT_H_P] != DNSCLIENT_SRC_PORT_H || //response to same port as we sent from (MSB) + gPB[UDP_DST_PORT_L_P] != dnstid_l || //response to same port as we sent from (LSB) + p[1] != dnstid_l) //message id same as we sent + return false; //not our DNS response + if((p[3] & 0x0F) != 0) + return true; //DNS response recieved with error + + p += *p; // we encoded the query len into tid + for (;;) { + if (*p & 0xC0) + p += 2; + else + while (++p < gPB + plen) { + if (*p == 0) { + ++p; + break; + } + } + if (p + 14 > gPB + plen) + break; + if (p[1] == 1 && p[9] == 4) { // type "A" and IPv4 + ether.copyIp(ether.hisip, p + 10); + break; + } + p += p[9] + 10; + } + return false; //No error +} + +// use during setup, as this discards all incoming requests until it returns +bool EtherCard::dnsLookup (const char* name, bool fromRam) { + uint16_t start = millis(); + + while(!isLinkUp()) + { + if ((uint16_t) (millis() - start) >= 30000) + return false; //timeout waiting for link + } + while(clientWaitingGw()) + { + packetLoop(packetReceive()); + if ((uint16_t) (millis() - start) >= 30000) + return false; //timeout waiting for gateway ARP + } + + memset(hisip, 0, 4); + dnsRequest(name, fromRam); + + start = millis(); + while (hisip[0] == 0) { + if ((uint16_t) (millis() - start) >= 30000) + return false; //timout waiting for dns response + uint16_t len = packetReceive(); + if (len > 0 && packetLoop(len) == 0) //packet not handled by tcp/ip packet loop + if(checkForDnsAnswer(len)) + return false; //DNS response recieved with error + } + + return true; +} diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/src/enc28j60.cpp b/STM32F1/libraries/Serasidis_EtherCard_STM/src/enc28j60.cpp new file mode 100644 index 0000000..db8b673 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/src/enc28j60.cpp @@ -0,0 +1,624 @@ +// Microchip ENC28J60 Ethernet Interface Driver +// Author: Guido Socher +// Copyright: GPL V2 +// +// Based on the enc28j60.c file from the AVRlib library by Pascal Stang. +// For AVRlib See http://www.procyonengineering.com/ +// Used with explicit permission of Pascal Stang. +// +// 2010-05-20 +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +//----------------------------------------------------------------- + +#if ARDUINO >= 100 +#include // Arduino 1.0 +#else +#include // Arduino 0022 +#endif +#include "enc28j60.h" +#include // Using library SPI in folder: D:\Documents\Arduino\hardware\STM32\STM32F1XX\libraries\SPI + +uint16_t ENC28J60::bufferSize; +bool ENC28J60::broadcast_enabled = false; + +// ENC28J60 Control Registers +// Control register definitions are a combination of address, +// bank number, and Ethernet/MAC/PHY indicator bits. +// - Register address (bits 0-4) +// - Bank number (bits 5-6) +// - MAC/PHY indicator (bit 7) +#define ADDR_MASK 0x1F +#define BANK_MASK 0x60 +#define SPRD_MASK 0x80 +// All-bank registers +#define EIE 0x1B +#define EIR 0x1C +#define ESTAT 0x1D +#define ECON2 0x1E +#define ECON1 0x1F +// Bank 0 registers +#define ERDPT (0x00|0x00) +#define EWRPT (0x02|0x00) +#define ETXST (0x04|0x00) +#define ETXND (0x06|0x00) +#define ERXST (0x08|0x00) +#define ERXND (0x0A|0x00) +#define ERXRDPT (0x0C|0x00) +// #define ERXWRPT (0x0E|0x00) +#define EDMAST (0x10|0x00) +#define EDMAND (0x12|0x00) +// #define EDMADST (0x14|0x00) +#define EDMACS (0x16|0x00) +// Bank 1 registers +#define EHT0 (0x00|0x20) +#define EHT1 (0x01|0x20) +#define EHT2 (0x02|0x20) +#define EHT3 (0x03|0x20) +#define EHT4 (0x04|0x20) +#define EHT5 (0x05|0x20) +#define EHT6 (0x06|0x20) +#define EHT7 (0x07|0x20) +#define EPMM0 (0x08|0x20) +#define EPMM1 (0x09|0x20) +#define EPMM2 (0x0A|0x20) +#define EPMM3 (0x0B|0x20) +#define EPMM4 (0x0C|0x20) +#define EPMM5 (0x0D|0x20) +#define EPMM6 (0x0E|0x20) +#define EPMM7 (0x0F|0x20) +#define EPMCS (0x10|0x20) +// #define EPMO (0x14|0x20) +#define EWOLIE (0x16|0x20) +#define EWOLIR (0x17|0x20) +#define ERXFCON (0x18|0x20) +#define EPKTCNT (0x19|0x20) +// Bank 2 registers +#define MACON1 (0x00|0x40|0x80) +#define MACON2 (0x01|0x40|0x80) +#define MACON3 (0x02|0x40|0x80) +#define MACON4 (0x03|0x40|0x80) +#define MABBIPG (0x04|0x40|0x80) +#define MAIPG (0x06|0x40|0x80) +#define MACLCON1 (0x08|0x40|0x80) +#define MACLCON2 (0x09|0x40|0x80) +#define MAMXFL (0x0A|0x40|0x80) +#define MAPHSUP (0x0D|0x40|0x80) +#define MICON (0x11|0x40|0x80) +#define MICMD (0x12|0x40|0x80) +#define MIREGADR (0x14|0x40|0x80) +#define MIWR (0x16|0x40|0x80) +#define MIRD (0x18|0x40|0x80) +// Bank 3 registers +#define MAADR1 (0x00|0x60|0x80) +#define MAADR0 (0x01|0x60|0x80) +#define MAADR3 (0x02|0x60|0x80) +#define MAADR2 (0x03|0x60|0x80) +#define MAADR5 (0x04|0x60|0x80) +#define MAADR4 (0x05|0x60|0x80) +#define EBSTSD (0x06|0x60) +#define EBSTCON (0x07|0x60) +#define EBSTCS (0x08|0x60) +#define MISTAT (0x0A|0x60|0x80) +#define EREVID (0x12|0x60) +#define ECOCON (0x15|0x60) +#define EFLOCON (0x17|0x60) +#define EPAUS (0x18|0x60) + +// ENC28J60 ERXFCON Register Bit Definitions +#define ERXFCON_UCEN 0x80 +#define ERXFCON_ANDOR 0x40 +#define ERXFCON_CRCEN 0x20 +#define ERXFCON_PMEN 0x10 +#define ERXFCON_MPEN 0x08 +#define ERXFCON_HTEN 0x04 +#define ERXFCON_MCEN 0x02 +#define ERXFCON_BCEN 0x01 +// ENC28J60 EIE Register Bit Definitions +#define EIE_INTIE 0x80 +#define EIE_PKTIE 0x40 +#define EIE_DMAIE 0x20 +#define EIE_LINKIE 0x10 +#define EIE_TXIE 0x08 +#define EIE_WOLIE 0x04 +#define EIE_TXERIE 0x02 +#define EIE_RXERIE 0x01 +// ENC28J60 EIR Register Bit Definitions +#define EIR_PKTIF 0x40 +#define EIR_DMAIF 0x20 +#define EIR_LINKIF 0x10 +#define EIR_TXIF 0x08 +#define EIR_WOLIF 0x04 +#define EIR_TXERIF 0x02 +#define EIR_RXERIF 0x01 +// ENC28J60 ESTAT Register Bit Definitions +#define ESTAT_INT 0x80 +#define ESTAT_LATECOL 0x10 +#define ESTAT_RXBUSY 0x04 +#define ESTAT_TXABRT 0x02 +#define ESTAT_CLKRDY 0x01 +// ENC28J60 ECON2 Register Bit Definitions +#define ECON2_AUTOINC 0x80 +#define ECON2_PKTDEC 0x40 +#define ECON2_PWRSV 0x20 +#define ECON2_VRPS 0x08 +// ENC28J60 ECON1 Register Bit Definitions +#define ECON1_TXRST 0x80 +#define ECON1_RXRST 0x40 +#define ECON1_DMAST 0x20 +#define ECON1_CSUMEN 0x10 +#define ECON1_TXRTS 0x08 +#define ECON1_RXEN 0x04 +#define ECON1_BSEL1 0x02 +#define ECON1_BSEL0 0x01 +// ENC28J60 MACON1 Register Bit Definitions +#define MACON1_LOOPBK 0x10 +#define MACON1_TXPAUS 0x08 +#define MACON1_RXPAUS 0x04 +#define MACON1_PASSALL 0x02 +#define MACON1_MARXEN 0x01 +// ENC28J60 MACON2 Register Bit Definitions +#define MACON2_MARST 0x80 +#define MACON2_RNDRST 0x40 +#define MACON2_MARXRST 0x08 +#define MACON2_RFUNRST 0x04 +#define MACON2_MATXRST 0x02 +#define MACON2_TFUNRST 0x01 +// ENC28J60 MACON3 Register Bit Definitions +#define MACON3_PADCFG2 0x80 +#define MACON3_PADCFG1 0x40 +#define MACON3_PADCFG0 0x20 +#define MACON3_TXCRCEN 0x10 +#define MACON3_PHDRLEN 0x08 +#define MACON3_HFRMLEN 0x04 +#define MACON3_FRMLNEN 0x02 +#define MACON3_FULDPX 0x01 +// ENC28J60 MICMD Register Bit Definitions +#define MICMD_MIISCAN 0x02 +#define MICMD_MIIRD 0x01 +// ENC28J60 MISTAT Register Bit Definitions +#define MISTAT_NVALID 0x04 +#define MISTAT_SCAN 0x02 +#define MISTAT_BUSY 0x01 + +// ENC28J60 EBSTCON Register Bit Definitions +#define EBSTCON_PSV2 0x80 +#define EBSTCON_PSV1 0x40 +#define EBSTCON_PSV0 0x20 +#define EBSTCON_PSEL 0x10 +#define EBSTCON_TMSEL1 0x08 +#define EBSTCON_TMSEL0 0x04 +#define EBSTCON_TME 0x02 +#define EBSTCON_BISTST 0x01 + +// PHY registers +#define PHCON1 0x00 +#define PHSTAT1 0x01 +#define PHHID1 0x02 +#define PHHID2 0x03 +#define PHCON2 0x10 +#define PHSTAT2 0x11 +#define PHIE 0x12 +#define PHIR 0x13 +#define PHLCON 0x14 + +// ENC28J60 PHY PHCON1 Register Bit Definitions +#define PHCON1_PRST 0x8000 +#define PHCON1_PLOOPBK 0x4000 +#define PHCON1_PPWRSV 0x0800 +#define PHCON1_PDPXMD 0x0100 +// ENC28J60 PHY PHSTAT1 Register Bit Definitions +#define PHSTAT1_PFDPX 0x1000 +#define PHSTAT1_PHDPX 0x0800 +#define PHSTAT1_LLSTAT 0x0004 +#define PHSTAT1_JBSTAT 0x0002 +// ENC28J60 PHY PHCON2 Register Bit Definitions +#define PHCON2_FRCLINK 0x4000 +#define PHCON2_TXDIS 0x2000 +#define PHCON2_JABBER 0x0400 +#define PHCON2_HDLDIS 0x0100 + +// ENC28J60 Packet Control Byte Bit Definitions +#define PKTCTRL_PHUGEEN 0x08 +#define PKTCTRL_PPADEN 0x04 +#define PKTCTRL_PCRCEN 0x02 +#define PKTCTRL_POVERRIDE 0x01 + +// SPI operation codes +#define ENC28J60_READ_CTRL_REG 0x00 +#define ENC28J60_READ_BUF_MEM 0x3A +#define ENC28J60_WRITE_CTRL_REG 0x40 +#define ENC28J60_WRITE_BUF_MEM 0x7A +#define ENC28J60_BIT_FIELD_SET 0x80 +#define ENC28J60_BIT_FIELD_CLR 0xA0 +#define ENC28J60_SOFT_RESET 0xFF + +// The RXSTART_INIT must be zero. See Rev. B4 Silicon Errata point 5. +// Buffer boundaries applied to internal 8K ram +// the entire available packet buffer space is allocated + +#define RXSTART_INIT 0x0000 // start of RX buffer, room for 2 packets +#define RXSTOP_INIT 0x0BFF // end of RX buffer + +#define TXSTART_INIT 0x0C00 // start of TX buffer, room for 1 packet +#define TXSTOP_INIT 0x11FF // end of TX buffer + +#define SCRATCH_START 0x1200 // start of scratch area +#define SCRATCH_LIMIT 0x2000 // past end of area, i.e. 3.5 Kb +#define SCRATCH_PAGE_SHIFT 6 // addressing is in pages of 64 bytes +#define SCRATCH_PAGE_SIZE (1 << SCRATCH_PAGE_SHIFT) + +// max frame length which the conroller will accept: +// (note: maximum ethernet frame length would be 1518) +#define MAX_FRAMELEN 1500 + +#define FULL_SPEED 1 // switch to full-speed SPI for bulk transfers + +static byte Enc28j60Bank; +static int gNextPacketPtr; +static byte selectPin; + +void ENC28J60::initSPI () { + + SPI.begin(); + SPI.setBitOrder(MSBFIRST); + //SPI.setDataMode(SPI_MODE0); + //SPI.setClockDivider(SPI_CLOCK_DIV16); +} + +static void enableChip () { + //cli(); + digitalWrite(selectPin, LOW); +} + +static void disableChip () { + digitalWrite(selectPin, HIGH); + //sei(); +} + +//static void xferSPI (byte data) { + //SPDR = data; + //while (!(SPSR&(1<>5); + } +} + +static byte readRegByte (byte address) { + SetBank(address); + return readOp(ENC28J60_READ_CTRL_REG, address); +} + +static uint16_t readReg(byte address) { + return readRegByte(address) + (readRegByte(address+1) << 8); +} + +static void writeRegByte (byte address, byte data) { + SetBank(address); + writeOp(ENC28J60_WRITE_CTRL_REG, address, data); +} + +static void writeReg(byte address, uint16_t data) { + writeRegByte(address, data); + writeRegByte(address + 1, data >> 8); +} + +static uint16_t readPhyByte (byte address) { + writeRegByte(MIREGADR, address); + writeRegByte(MICMD, MICMD_MIIRD); + while (readRegByte(MISTAT) & MISTAT_BUSY) + ; + writeRegByte(MICMD, 0x00); + return readRegByte(MIRD+1); +} + +static void writePhy (byte address, uint16_t data) { + writeRegByte(MIREGADR, address); + writeReg(MIWR, data); + while (readRegByte(MISTAT) & MISTAT_BUSY) + ; +} + +byte ENC28J60::initialize (uint16_t size, const byte* macaddr, byte csPin) { + + bufferSize = size; + //if (bitRead(SPCR, SPE) == 0) + initSPI(); + selectPin = csPin; + pinMode(selectPin, OUTPUT); + disableChip(); + + writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); + delay(2); // errata B7/2 + while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) + ; + gNextPacketPtr = RXSTART_INIT; + writeReg(ERXST, RXSTART_INIT); + writeReg(ERXRDPT, RXSTART_INIT); + writeReg(ERXND, RXSTOP_INIT); + writeReg(ETXST, TXSTART_INIT); + writeReg(ETXND, TXSTOP_INIT); + enableBroadcast(); // change to add ERXFCON_BCEN recommended by epam + writeReg(EPMM0, 0x303f); + writeReg(EPMCS, 0xf7f9); + writeRegByte(MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); + writeRegByte(MACON2, 0x00); + writeOp(ENC28J60_BIT_FIELD_SET, MACON3, + MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLNEN); + writeReg(MAIPG, 0x0C12); + writeRegByte(MABBIPG, 0x12); + writeReg(MAMXFL, MAX_FRAMELEN); + writeRegByte(MAADR5, macaddr[0]); + writeRegByte(MAADR4, macaddr[1]); + writeRegByte(MAADR3, macaddr[2]); + writeRegByte(MAADR2, macaddr[3]); + writeRegByte(MAADR1, macaddr[4]); + writeRegByte(MAADR0, macaddr[5]); + writePhy(PHCON2, PHCON2_HDLDIS); + SetBank(ECON1); + writeOp(ENC28J60_BIT_FIELD_SET, EIE, EIE_INTIE|EIE_PKTIE); + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); + + byte rev = readRegByte(EREVID); + // microchip forgot to step the number on the silcon when they + // released the revision B7. 6 is now rev B7. We still have + // to see what they do when they release B8. At the moment + // there is no B8 out yet + if (rev > 5) ++rev; + return rev; +} + +bool ENC28J60::isLinkUp() { + return (readPhyByte(PHSTAT2) >> 2) & 1; +} + +void ENC28J60::packetSend(uint16_t len) { + while (readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_TXRTS) + if (readRegByte(EIR) & EIR_TXERIF) { + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRST); + writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_TXRST); + } + writeReg(EWRPT, TXSTART_INIT); + writeReg(ETXND, TXSTART_INIT+len); + writeOp(ENC28J60_WRITE_BUF_MEM, 0, 0x00); + writeBuf(len, buffer); + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_TXRTS); +} + +uint16_t ENC28J60::packetReceive() { + uint16_t len = 0; + if (readRegByte(EPKTCNT) > 0) { + writeReg(ERDPT, gNextPacketPtr); + + struct { + uint16_t nextPacket; + uint16_t byteCount; + uint16_t status; + } header; + + readBuf(sizeof header, (byte*) &header); + + gNextPacketPtr = header.nextPacket; + len = header.byteCount - 4; //remove the CRC count + if (len>bufferSize-1) + len=bufferSize-1; + if ((header.status & 0x80)==0) + len = 0; + else + readBuf(len, buffer); + buffer[len] = 0; + if (gNextPacketPtr - 1 > RXSTOP_INIT) + writeReg(ERXRDPT, RXSTOP_INIT); + else + writeReg(ERXRDPT, gNextPacketPtr - 1); + writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PKTDEC); + } + return len; +} + +void ENC28J60::copyout (byte page, const byte* data) { + uint16_t destPos = SCRATCH_START + (page << SCRATCH_PAGE_SHIFT); + if (destPos < SCRATCH_START || destPos > SCRATCH_LIMIT - SCRATCH_PAGE_SIZE) + return; + writeReg(EWRPT, destPos); + writeBuf(SCRATCH_PAGE_SIZE, data); +} + +void ENC28J60::copyin (byte page, byte* data) { + uint16_t destPos = SCRATCH_START + (page << SCRATCH_PAGE_SHIFT); + if (destPos < SCRATCH_START || destPos > SCRATCH_LIMIT - SCRATCH_PAGE_SIZE) + return; + writeReg(ERDPT, destPos); + readBuf(SCRATCH_PAGE_SIZE, data); +} + +byte ENC28J60::peekin (byte page, byte off) { + byte result = 0; + uint16_t destPos = SCRATCH_START + (page << SCRATCH_PAGE_SHIFT) + off; + if (SCRATCH_START <= destPos && destPos < SCRATCH_LIMIT) { + writeReg(ERDPT, destPos); + readBuf(1, &result); + } + return result; +} + +// Contributed by Alex M. Based on code from: http://blog.derouineau.fr +// /2011/07/putting-enc28j60-ethernet-controler-in-sleep-mode/ +void ENC28J60::powerDown() { + writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_RXEN); + while(readRegByte(ESTAT) & ESTAT_RXBUSY); + while(readRegByte(ECON1) & ECON1_TXRTS); + writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_VRPS); + writeOp(ENC28J60_BIT_FIELD_SET, ECON2, ECON2_PWRSV); +} + +void ENC28J60::powerUp() { + writeOp(ENC28J60_BIT_FIELD_CLR, ECON2, ECON2_PWRSV); + while(!readRegByte(ESTAT) & ESTAT_CLKRDY); + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_RXEN); +} + +void ENC28J60::enableBroadcast (bool temporary) { + writeRegByte(ERXFCON, readRegByte(ERXFCON) | ERXFCON_BCEN); + if(!temporary) + broadcast_enabled = true; +} + +void ENC28J60::disableBroadcast (bool temporary) { + if(!temporary) + broadcast_enabled = false; + if(!broadcast_enabled) + writeRegByte(ERXFCON, readRegByte(ERXFCON) & ~ERXFCON_BCEN); +} + +void ENC28J60::enableMulticast () { + writeRegByte(ERXFCON, readRegByte(ERXFCON) | ERXFCON_MCEN); +} + +void ENC28J60::disableMulticast () { + writeRegByte(ERXFCON, readRegByte(ERXFCON) & ~ERXFCON_MCEN); +} + +uint8_t ENC28J60::doBIST ( byte csPin) { +#define RANDOM_FILL 0b0000 +#define ADDRESS_FILL 0b0100 +#define PATTERN_SHIFT 0b1000 +#define RANDOM_RACE 0b1100 + +// init + //if (bitRead(SPCR, SPE) == 0) + // initSPI(); + selectPin = csPin; + pinMode(selectPin, OUTPUT); + disableChip(); + + writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET); + delay(2); // errata B7/2 + while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY) ; + + + // now we can start the memory test + + uint16_t macResult; + uint16_t bitsResult; + + // clear some of the registers registers + writeRegByte(ECON1, 0); + writeReg(EDMAST, 0); + + // Set up necessary pointers for the DMA to calculate over the entire memory + writeReg(EDMAND, 0x1FFFu); + writeReg(ERXND, 0x1FFFu); + + // Enable Test Mode and do an Address Fill + SetBank(EBSTCON); + writeRegByte(EBSTCON, EBSTCON_TME | EBSTCON_BISTST | ADDRESS_FILL); + + // wait for BISTST to be reset, only after that are we actually ready to + // start the test + // this was undocumented :( + while (readOp(ENC28J60_READ_CTRL_REG, EBSTCON) & EBSTCON_BISTST); + writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME); + + + // now start the actual reading an calculating the checksum until the end is + // reached + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST | ECON1_CSUMEN); + SetBank(EDMACS); + while(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST); + macResult = readReg(EDMACS); + bitsResult = readReg(EBSTCS); + // Compare the results + // 0xF807 should always be generated in Address fill mode + if ((macResult != bitsResult) || (bitsResult != 0xF807)) { + return 0; + } + // reset test flag + writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME); + + + // Now start the BIST with random data test, and also keep on swapping the + // DMA/BIST memory ports. + writeRegByte(EBSTSD, 0b10101010 | millis()); + writeRegByte(EBSTCON, EBSTCON_TME | EBSTCON_PSEL | EBSTCON_BISTST | RANDOM_FILL); + + + // wait for BISTST to be reset, only after that are we actually ready to + // start the test + // this was undocumented :( + while (readOp(ENC28J60_READ_CTRL_REG, EBSTCON) & EBSTCON_BISTST); + writeOp(ENC28J60_BIT_FIELD_CLR, EBSTCON, EBSTCON_TME); + + + // now start the actual reading an calculating the checksum until the end is + // reached + writeOp(ENC28J60_BIT_FIELD_SET, ECON1, ECON1_DMAST | ECON1_CSUMEN); + SetBank(EDMACS); + while(readOp(ENC28J60_READ_CTRL_REG, ECON1) & ECON1_DMAST); + + macResult = readReg(EDMACS); + bitsResult = readReg(EBSTCS); + // The checksum should be equal + return macResult == bitsResult; +} + diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/src/enc28j60.h b/STM32F1/libraries/Serasidis_EtherCard_STM/src/enc28j60.h new file mode 100644 index 0000000..d5007d8 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/src/enc28j60.h @@ -0,0 +1,129 @@ +// Microchip ENC28J60 Ethernet Interface Driver +// Author: Pascal Stang +// Modified by: Guido Socher +// Copyright: GPL V2 +// +// This driver provides initialization and transmit/receive +// functions for the Microchip ENC28J60 10Mb Ethernet Controller and PHY. +// This chip is novel in that it is a full MAC+PHY interface all in a 28-pin +// chip, using an SPI interface to the host processor. +// +// 2010-05-20 +// +//----------------------------------------------------------------- +// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 +// Home: http://www.serasidis.gr +// email: avrsite@yahoo.gr +// +// PIN Connections (Using STM32F103): +// +// ENC28J60 - STM32F103 +// VCC - 3.3V +// GND - GND +// SCK - Pin PA5 +// SO - Pin PA6 +// SI - Pin PA7 +// CS - Pin PA8 +//----------------------------------------------------------------- +#ifndef ENC28J60_H +#define ENC28J60_H + +/** This class provide low-level interfacing with the ENC28J60 network interface. This is used by the EtherCard class and not intended for use by (normal) end users. */ +class ENC28J60 { +public: + static uint8_t buffer[]; //!< Data buffer (shared by recieve and transmit) + static uint16_t bufferSize; //!< Size of data buffer + static bool broadcast_enabled; //!< True if broadcasts enabled (used to allow temporary disable of broadcast for DHCP or other internal functions) + + static uint8_t* tcpOffset () { return buffer + 0x36; } //!< Pointer to the start of TCP payload + + /** @brief Initialise SPI interface + * @note Configures Arduino pins as input / output, etc. + */ + static void initSPI (); + + /** @brief Initialise network interface + * @param size Size of data buffer + * @param macaddr Pointer to 6 byte hardware (MAC) address + * @param csPin Arduino pin used for chip select (enable network interface SPI bus). Default = 8 = PA9 + * @return uint8_t ENC28J60 firmware version or zero on failure. + */ + static uint8_t initialize (const uint16_t size, const uint8_t* macaddr, + uint8_t csPin = PA8); + + /** @brief Check if network link is connected + * @return bool True if link is up + */ + static bool isLinkUp (); + + /** @brief Sends data to network interface + * @param len Size of data to send + * @note Data buffer is shared by recieve and transmit functions + */ + static void packetSend (uint16_t len); + + /** @brief Copy recieved packets to data buffer + * @return uint16_t Size of recieved data + * @note Data buffer is shared by recieve and transmit functions + */ + static uint16_t packetReceive (); + + /** @brief Copy data from ENC28J60 memory + * @param page Data page of memory + * @param data Pointer to buffer to copy data to + */ + static void copyout (uint8_t page, const uint8_t* data); + + /** @brief Copy data to ENC28J60 memory + * @param page Data page of memory + * @param data Pointer to buffer to copy data from + */ + static void copyin (uint8_t page, uint8_t* data); + + /** @brief Get single byte of data from ENC28J60 memory + * @param page Data page of memory + * @param off Offset of data within page + * @return Data value + */ + static uint8_t peekin (uint8_t page, uint8_t off); + + /** @brief Put ENC28J60 in sleep mode + */ + static void powerDown(); // contrib by Alex M. + + /** @brief Wake ENC28J60 from sleep mode + */ + static void powerUp(); // contrib by Alex M. + + /** @brief Enable reception of broadcast messages + * @param temporary Set true to temporarily enable broadcast + * @note This will increase load on recieved data handling + */ + static void enableBroadcast(bool temporary = false); + + /** @brief Disable reception of broadcast messages + * @param temporary Set true to only disable if temporarily enabled + * @note This will reduce load on recieved data handling + */ + static void disableBroadcast(bool temporary = false); + + /** @brief Enables reception of mulitcast messages + * @note This will increase load on recieved data handling + */ + static void enableMulticast (); + + /** @brief Disable reception of mulitcast messages + * @note This will reduce load on recieved data handling + */ + static void disableMulticast(); + + /** @brief Reset and fully initialise ENC28J60 + * @param csPin Arduino pin used for chip select (enable SPI bus) + * @return uint8_t 0 on failure + */ + static uint8_t doBIST(uint8_t csPin = PA8); +}; + +typedef ENC28J60 Ethernet; //!< Define alias Ethernet for ENC28J60 + +#endif diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/src/net.h b/STM32F1/libraries/Serasidis_EtherCard_STM/src/net.h new file mode 100644 index 0000000..a80c5b9 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/src/net.h @@ -0,0 +1,123 @@ +// Based on the net.h file from the AVRlib library by Pascal Stang. +// Author: Guido Socher +// Copyright: GPL V2 +// +// For AVRlib See http://www.procyonengineering.com/ +// Used with explicit permission of Pascal Stang. +// +// 2010-05-20 + +// notation: _P = position of a field +// _V = value of a field + +#ifndef NET_H +#define NET_H + +// ******* ETH ******* +#define ETH_HEADER_LEN 14 +// values of certain bytes: +#define ETHTYPE_ARP_H_V 0x08 +#define ETHTYPE_ARP_L_V 0x06 +#define ETHTYPE_IP_H_V 0x08 +#define ETHTYPE_IP_L_V 0x00 +// byte positions in the ethernet frame: +// +// Ethernet type field (2bytes): +#define ETH_TYPE_H_P 12 +#define ETH_TYPE_L_P 13 +// +#define ETH_DST_MAC 0 +#define ETH_SRC_MAC 6 + + +// ******* ARP ******* +#define ETH_ARP_OPCODE_REPLY_H_V 0x0 +#define ETH_ARP_OPCODE_REPLY_L_V 0x02 +#define ETH_ARP_OPCODE_REQ_H_V 0x0 +#define ETH_ARP_OPCODE_REQ_L_V 0x01 +// start of arp header: +#define ETH_ARP_P 0xe +// +#define ETHTYPE_ARP_L_V 0x06 +// arp.dst.ip +#define ETH_ARP_DST_IP_P 0x26 +// arp.opcode +#define ETH_ARP_OPCODE_H_P 0x14 +#define ETH_ARP_OPCODE_L_P 0x15 +// arp.src.mac +#define ETH_ARP_SRC_MAC_P 0x16 +#define ETH_ARP_SRC_IP_P 0x1c +#define ETH_ARP_DST_MAC_P 0x20 +#define ETH_ARP_DST_IP_P 0x26 + +// ******* IP ******* +#define IP_HEADER_LEN 20 +// ip.src +#define IP_SRC_P 0x1a +#define IP_DST_P 0x1e +#define IP_HEADER_LEN_VER_P 0xe +#define IP_CHECKSUM_P 0x18 +#define IP_TTL_P 0x16 +#define IP_FLAGS_P 0x14 +#define IP_P 0xe +#define IP_TOTLEN_H_P 0x10 +#define IP_TOTLEN_L_P 0x11 + +#define IP_PROTO_P 0x17 + +#define IP_PROTO_ICMP_V 1 +#define IP_PROTO_TCP_V 6 +// 17=0x11 +#define IP_PROTO_UDP_V 17 +// ******* ICMP ******* +#define ICMP_TYPE_ECHOREPLY_V 0 +#define ICMP_TYPE_ECHOREQUEST_V 8 +// +#define ICMP_TYPE_P 0x22 +#define ICMP_CHECKSUM_P 0x24 +#define ICMP_CHECKSUM_H_P 0x24 +#define ICMP_CHECKSUM_L_P 0x25 +#define ICMP_IDENT_H_P 0x26 +#define ICMP_IDENT_L_P 0x27 +#define ICMP_DATA_P 0x2a + +// ******* UDP ******* +#define UDP_HEADER_LEN 8 +// +#define UDP_SRC_PORT_H_P 0x22 +#define UDP_SRC_PORT_L_P 0x23 +#define UDP_DST_PORT_H_P 0x24 +#define UDP_DST_PORT_L_P 0x25 +// +#define UDP_LEN_H_P 0x26 +#define UDP_LEN_L_P 0x27 +#define UDP_CHECKSUM_H_P 0x28 +#define UDP_CHECKSUM_L_P 0x29 +#define UDP_DATA_P 0x2a + +// ******* TCP ******* +#define TCP_SRC_PORT_H_P 0x22 +#define TCP_SRC_PORT_L_P 0x23 +#define TCP_DST_PORT_H_P 0x24 +#define TCP_DST_PORT_L_P 0x25 +// the tcp seq number is 4 bytes 0x26-0x29 +#define TCP_SEQ_H_P 0x26 +#define TCP_SEQACK_H_P 0x2a +// flags: SYN=2 +#define TCP_FLAGS_P 0x2f +#define TCP_FLAGS_SYN_V 2 +#define TCP_FLAGS_FIN_V 1 +#define TCP_FLAGS_RST_V 4 +#define TCP_FLAGS_PUSH_V 8 +#define TCP_FLAGS_SYNACK_V 0x12 +#define TCP_FLAGS_ACK_V 0x10 +#define TCP_FLAGS_PSHACK_V 0x18 +// plain len without the options: +#define TCP_HEADER_LEN_PLAIN 20 +#define TCP_HEADER_LEN_P 0x2e +#define TCP_WIN_SIZE 0x30 +#define TCP_CHECKSUM_H_P 0x32 +#define TCP_CHECKSUM_L_P 0x33 +#define TCP_OPTIONS_P 0x36 +// +#endif diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/src/tcpip.cpp b/STM32F1/libraries/Serasidis_EtherCard_STM/src/tcpip.cpp new file mode 100644 index 0000000..4713e8c --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/src/tcpip.cpp @@ -0,0 +1,803 @@ +// IP, ARP, UDP and TCP functions. +// Author: Guido Socher +// Copyright: GPL V2 +// +// The TCP implementation uses some size optimisations which are valid +// only if all data can be sent in one single packet. This is however +// not a big limitation for a microcontroller as you will anyhow use +// small web-pages. The web server must send the entire web page in one +// packet. The client "web browser" as implemented here can also receive +// large pages. +// +// 2010-05-20 + +#include "EtherCard_STM.h" +#include "net.h" +#undef word // arduino nonsense + +#define gPB ether.buffer + +#define PINGPATTERN 0x42 + +// Avoid spurious pgmspace warnings - http://forum.jeelabs.net/node/327 +// See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34734 +//#undef PROGMEM +//#define PROGMEM __attribute__(( section(".progmem.data") )) +//#undef PSTR +//#define PSTR(s) (__extension__({static prog_char c[] PROGMEM = (s); &c[0];})) + +#define TCPCLIENT_SRC_PORT_H 11 //Source port (MSB) for TCP/IP client connections - hardcode all TCP/IP client connection from ports in range 2816-3071 +static uint8_t tcpclient_src_port_l=1; // Source port (LSB) for tcp/ip client connections - increments on each TCP/IP request +static uint8_t tcp_fd; // a file descriptor, will be encoded into the port +static uint8_t tcp_client_state; //TCP connection state: 1=Send SYN, 2=SYN sent awaiting SYN+ACK, 3=Established, 4=Not used, 5=Closing, 6=Closed +static uint8_t tcp_client_port_h; // Destination port (MSB) of TCP/IP client connection +static uint8_t tcp_client_port_l; // Destination port (LSB) of TCP/IP client connection +static uint8_t (*client_tcp_result_cb)(uint8_t,uint8_t,uint16_t,uint16_t); // Pointer to callback function to handle response to current TCP/IP request +static uint16_t (*client_tcp_datafill_cb)(uint8_t); //Pointer to callback function to handle payload data in response to current TCP/IP request +static uint8_t www_fd; // ID of current http request (only one http request at a time - one of the 8 possible concurrent TCP/IP connections) +static void (*client_browser_cb)(uint8_t,uint16_t,uint16_t); // Pointer to callback function to handle result of current HTTP request +static const char *client_additionalheaderline; // Pointer to c-string additional http request header info +static const char *client_postval; +static const char *client_urlbuf; // Pointer to c-string path part of HTTP request URL +static const char *client_urlbuf_var; // Pointer to c-string filename part of HTTP request URL +static const char *client_hoststr; // Pointer to c-string hostname of current HTTP request +static void (*icmp_cb)(uint8_t *ip); // Pointer to callback function for ICMP ECHO response handler (triggers when localhost recieves ping respnse (pong)) +static uint8_t destmacaddr[6]; // storing both dns server and destination mac addresses, but at different times because both are never needed at same time. +static boolean waiting_for_dns_mac = false; //might be better to use bit flags and bitmask operations for these conditions +static boolean has_dns_mac = false; +static boolean waiting_for_dest_mac = false; +static boolean has_dest_mac = false; +static uint8_t gwmacaddr[6]; // Hardware (MAC) address of gateway router +static uint8_t waitgwmac; // Bitwise flags of gateway router status - see below for states +//Define gatweay router ARP statuses +#define WGW_INITIAL_ARP 1 // First request, no answer yet +#define WGW_HAVE_GW_MAC 2 // Have gateway router MAC +#define WGW_REFRESHING 4 // Refreshing but already have gateway MAC +#define WGW_ACCEPT_ARP_REPLY 8 // Accept an ARP reply + +static uint16_t info_data_len; // Length of TCP/IP payload +static uint8_t seqnum = 0xa; // My initial tcp sequence number +static uint8_t result_fd = 123; // Session id of last reply +static const char* result_ptr; // Pointer to TCP/IP data +static unsigned long SEQ; // TCP/IP sequence number + +#define CLIENTMSS 550 +#define TCP_DATA_START ((uint16_t)TCP_SRC_PORT_H_P+(gPB[TCP_HEADER_LEN_P]>>4)*4) // Get offset of TCP/IP payload data + +const unsigned char arpreqhdr[] PROGMEM = { 0,1,8,0,6,4,0,1 }; // ARP request header +const unsigned char iphdr[] PROGMEM = { 0x45,0,0,0x82,0,0,0x40,0,0x20 }; //IP header +const unsigned char ntpreqhdr[] PROGMEM = { 0xE3,0,4,0xFA,0,1,0,0,0,1 }; //NTP request header +const uint8_t allOnes[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; // Used for hardware (MAC) and IP broadcast addresses + +static void fill_checksum(uint8_t dest, uint8_t off, uint16_t len,uint8_t type) { + const uint8_t* ptr = gPB + off; + uint32_t sum = type==1 ? IP_PROTO_UDP_V+len-8 : + type==2 ? IP_PROTO_TCP_V+len-8 : 0; + while(len >1) { + sum += (uint16_t) (((uint32_t)*ptr<<8)|*(ptr+1)); + ptr+=2; + len-=2; + } + if (len) + sum += ((uint32_t)*ptr)<<8; + while (sum>>16) + sum = (uint16_t) sum + (sum >> 16); + uint16_t ck = ~ (uint16_t) sum; + gPB[dest] = ck>>8; + gPB[dest+1] = ck; +} + +static void setMACs (const uint8_t *mac) { + EtherCard::copyMac(gPB + ETH_DST_MAC, mac); + EtherCard::copyMac(gPB + ETH_SRC_MAC, EtherCard::mymac); +} + +static void setMACandIPs (const uint8_t *mac, const uint8_t *dst) { + setMACs(mac); + EtherCard::copyIp(gPB + IP_DST_P, dst); + EtherCard::copyIp(gPB + IP_SRC_P, EtherCard::myip); +} + +static uint8_t check_ip_message_is_from(const uint8_t *ip) { + return memcmp(gPB + IP_SRC_P, ip, 4) == 0; +} + +static boolean is_lan(const uint8_t source[4], const uint8_t destination[4]) { + if(source[0] == 0 || destination[0] == 0) { + return false; + } + for(int i = 0; i < 4; i++) + if((source[i] & EtherCard::netmask[i]) != (destination[i] & EtherCard::netmask[i])) { + return false; + } + return true; +} + +static uint8_t eth_type_is_arp_and_my_ip(uint16_t len) { + return len >= 41 && gPB[ETH_TYPE_H_P] == ETHTYPE_ARP_H_V && + gPB[ETH_TYPE_L_P] == ETHTYPE_ARP_L_V && + memcmp(gPB + ETH_ARP_DST_IP_P, EtherCard::myip, 4) == 0; +} + +static uint8_t eth_type_is_ip_and_my_ip(uint16_t len) { + return len >= 42 && gPB[ETH_TYPE_H_P] == ETHTYPE_IP_H_V && + gPB[ETH_TYPE_L_P] == ETHTYPE_IP_L_V && + gPB[IP_HEADER_LEN_VER_P] == 0x45 && + (memcmp(gPB + IP_DST_P, EtherCard::myip, 4) == 0 //not my IP + || (memcmp(gPB + IP_DST_P, EtherCard::broadcastip, 4) == 0) //not subnet broadcast + || (memcmp(gPB + IP_DST_P, allOnes, 4) == 0)); //not global broadcasts + //!@todo Handle multicast +} + +static void fill_ip_hdr_checksum() { + gPB[IP_CHECKSUM_P] = 0; + gPB[IP_CHECKSUM_P+1] = 0; + gPB[IP_FLAGS_P] = 0x40; // don't fragment + gPB[IP_FLAGS_P+1] = 0; // fragement offset + gPB[IP_TTL_P] = 64; // ttl + fill_checksum(IP_CHECKSUM_P, IP_P, IP_HEADER_LEN,0); +} + +static void make_eth_ip() { + setMACs(gPB + ETH_SRC_MAC); + EtherCard::copyIp(gPB + IP_DST_P, gPB + IP_SRC_P); + EtherCard::copyIp(gPB + IP_SRC_P, EtherCard::myip); + fill_ip_hdr_checksum(); +} + +static void step_seq(uint16_t rel_ack_num,uint8_t cp_seq) { + uint8_t i; + uint8_t tseq; + i = 4; + while(i>0) { + rel_ack_num = gPB[TCP_SEQ_H_P+i-1]+rel_ack_num; + tseq = gPB[TCP_SEQACK_H_P+i-1]; + gPB[TCP_SEQACK_H_P+i-1] = rel_ack_num; + if (cp_seq) + gPB[TCP_SEQ_H_P+i-1] = tseq; + else + gPB[TCP_SEQ_H_P+i-1] = 0; // some preset value + rel_ack_num = rel_ack_num>>8; + i--; + } +} + +static void make_tcphead(uint16_t rel_ack_num,uint8_t cp_seq) { + uint8_t i = gPB[TCP_DST_PORT_H_P]; + gPB[TCP_DST_PORT_H_P] = gPB[TCP_SRC_PORT_H_P]; + gPB[TCP_SRC_PORT_H_P] = i; + uint8_t j = gPB[TCP_DST_PORT_L_P]; + gPB[TCP_DST_PORT_L_P] = gPB[TCP_SRC_PORT_L_P]; + gPB[TCP_SRC_PORT_L_P] = j; + step_seq(rel_ack_num,cp_seq); + gPB[TCP_CHECKSUM_H_P] = 0; + gPB[TCP_CHECKSUM_L_P] = 0; + gPB[TCP_HEADER_LEN_P] = 0x50; +} + +static void make_arp_answer_from_request() { + setMACs(gPB + ETH_SRC_MAC); + gPB[ETH_ARP_OPCODE_H_P] = ETH_ARP_OPCODE_REPLY_H_V; + gPB[ETH_ARP_OPCODE_L_P] = ETH_ARP_OPCODE_REPLY_L_V; + EtherCard::copyMac(gPB + ETH_ARP_DST_MAC_P, gPB + ETH_ARP_SRC_MAC_P); + EtherCard::copyMac(gPB + ETH_ARP_SRC_MAC_P, EtherCard::mymac); + EtherCard::copyIp(gPB + ETH_ARP_DST_IP_P, gPB + ETH_ARP_SRC_IP_P); + EtherCard::copyIp(gPB + ETH_ARP_SRC_IP_P, EtherCard::myip); + EtherCard::packetSend(42); +} + +static void make_echo_reply_from_request(uint16_t len) { + make_eth_ip(); + gPB[ICMP_TYPE_P] = ICMP_TYPE_ECHOREPLY_V; + if (gPB[ICMP_CHECKSUM_P] > (0xFF-0x08)) + gPB[ICMP_CHECKSUM_P+1]++; + gPB[ICMP_CHECKSUM_P] += 0x08; + EtherCard::packetSend(len); +} + +void EtherCard::makeUdpReply (const char *data,uint8_t datalen,uint16_t port) { + if (datalen>220) + datalen = 220; + gPB[IP_TOTLEN_H_P] = (IP_HEADER_LEN+UDP_HEADER_LEN+datalen) >>8; + gPB[IP_TOTLEN_L_P] = IP_HEADER_LEN+UDP_HEADER_LEN+datalen; + make_eth_ip(); + gPB[UDP_DST_PORT_H_P] = gPB[UDP_SRC_PORT_H_P]; + gPB[UDP_DST_PORT_L_P] = gPB[UDP_SRC_PORT_L_P]; + gPB[UDP_SRC_PORT_H_P] = port>>8; + gPB[UDP_SRC_PORT_L_P] = port; + gPB[UDP_LEN_H_P] = (UDP_HEADER_LEN+datalen) >> 8; + gPB[UDP_LEN_L_P] = UDP_HEADER_LEN+datalen; + gPB[UDP_CHECKSUM_H_P] = 0; + gPB[UDP_CHECKSUM_L_P] = 0; + memcpy(gPB + UDP_DATA_P, data, datalen); + fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + datalen,1); + packetSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen); +} + +static void make_tcp_synack_from_syn() { + gPB[IP_TOTLEN_H_P] = 0; + gPB[IP_TOTLEN_L_P] = IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4; + make_eth_ip(); + gPB[TCP_FLAGS_P] = TCP_FLAGS_SYNACK_V; + make_tcphead(1,0); + gPB[TCP_SEQ_H_P+0] = 0; + gPB[TCP_SEQ_H_P+1] = 0; + gPB[TCP_SEQ_H_P+2] = seqnum; + gPB[TCP_SEQ_H_P+3] = 0; + seqnum += 3; + gPB[TCP_OPTIONS_P] = 2; + gPB[TCP_OPTIONS_P+1] = 4; + gPB[TCP_OPTIONS_P+2] = 0x05; + gPB[TCP_OPTIONS_P+3] = 0x0; + gPB[TCP_HEADER_LEN_P] = 0x60; + gPB[TCP_WIN_SIZE] = 0x5; // 1400=0x578 + gPB[TCP_WIN_SIZE+1] = 0x78; + fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8+TCP_HEADER_LEN_PLAIN+4,2); + EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+4+ETH_HEADER_LEN); +} + +static uint16_t get_tcp_data_len() { + int16_t i = (((int16_t)gPB[IP_TOTLEN_H_P])<<8)|gPB[IP_TOTLEN_L_P]; + i -= IP_HEADER_LEN; + i -= (gPB[TCP_HEADER_LEN_P]>>4)*4; // generate len in bytes; + if (i<=0) + i = 0; + return (uint16_t)i; +} + +static void make_tcp_ack_from_any(int16_t datlentoack,uint8_t addflags) { + gPB[TCP_FLAGS_P] = TCP_FLAGS_ACK_V|addflags; + if (addflags!=TCP_FLAGS_RST_V && datlentoack==0) + datlentoack = 1; + make_tcphead(datlentoack,1); // no options + uint16_t j = IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN; + gPB[IP_TOTLEN_H_P] = j>>8; + gPB[IP_TOTLEN_L_P] = j; + make_eth_ip(); + gPB[TCP_WIN_SIZE] = 0x4; // 1024=0x400, 1280=0x500 2048=0x800 768=0x300 + gPB[TCP_WIN_SIZE+1] = 0; + fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8+TCP_HEADER_LEN_PLAIN,2); + EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN); +} + +static void make_tcp_ack_with_data_noflags(uint16_t dlen) { + uint16_t j = IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen; + gPB[IP_TOTLEN_H_P] = j>>8; + gPB[IP_TOTLEN_L_P] = j; + fill_ip_hdr_checksum(); + gPB[TCP_CHECKSUM_H_P] = 0; + gPB[TCP_CHECKSUM_L_P] = 0; + fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8+TCP_HEADER_LEN_PLAIN+dlen,2); + EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+dlen+ETH_HEADER_LEN); +} + +void EtherCard::httpServerReply (uint16_t dlen) { + make_tcp_ack_from_any(info_data_len,0); // send ack for http get + gPB[TCP_FLAGS_P] = TCP_FLAGS_ACK_V|TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V; + make_tcp_ack_with_data_noflags(dlen); // send data +} + +static void get_seq() { //get the sequence number of packets after an ack from GET + SEQ =(((unsigned long)gPB[TCP_SEQ_H_P]*256+gPB[TCP_SEQ_H_P+1])*256+gPB[TCP_SEQ_H_P+2])*256+gPB[TCP_SEQ_H_P+3]; +} //thanks to mstuetz for the missing (unsigned long) + +static void set_seq() { //set the correct sequence number and calculate the next with the lenght of current packet + gPB[TCP_SEQ_H_P]= (SEQ & 0xff000000 ) >> 24; + gPB[TCP_SEQ_H_P+1]= (SEQ & 0xff0000 ) >> 16; + gPB[TCP_SEQ_H_P+2]= (SEQ & 0xff00 ) >> 8; + gPB[TCP_SEQ_H_P+3]= (SEQ & 0xff ); +} + +void EtherCard::httpServerReplyAck () { + make_tcp_ack_from_any(info_data_len,0); // send ack for http get + get_seq(); //get the sequence number of packets after an ack from GET +} + +void EtherCard::httpServerReply_with_flags (uint16_t dlen , uint8_t flags) { + set_seq(); + gPB[TCP_FLAGS_P] = flags; // final packet + make_tcp_ack_with_data_noflags(dlen); // send data + SEQ=SEQ+dlen; +} + +void EtherCard::clientIcmpRequest(const uint8_t *destip) { + if(is_lan(EtherCard::myip, destip)) { + setMACandIPs(destmacaddr, destip); + } else + setMACandIPs(gwmacaddr, destip); + gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + memcpy_P(gPB + IP_P,iphdr,9); + gPB[IP_TOTLEN_L_P] = 0x54; + gPB[IP_PROTO_P] = IP_PROTO_ICMP_V; + fill_ip_hdr_checksum(); + gPB[ICMP_TYPE_P] = ICMP_TYPE_ECHOREQUEST_V; + gPB[ICMP_TYPE_P+1] = 0; // code + gPB[ICMP_CHECKSUM_H_P] = 0; + gPB[ICMP_CHECKSUM_L_P] = 0; + gPB[ICMP_IDENT_H_P] = 5; // some number + gPB[ICMP_IDENT_L_P] = EtherCard::myip[3]; // last byte of my IP + gPB[ICMP_IDENT_L_P+1] = 0; // seq number, high byte + gPB[ICMP_IDENT_L_P+2] = 1; // seq number, low byte, we send only 1 ping at a time + memset(gPB + ICMP_DATA_P, PINGPATTERN, 56); + fill_checksum(ICMP_CHECKSUM_H_P, ICMP_TYPE_P, 56+8,0); + packetSend(98); +} + +void EtherCard::ntpRequest (uint8_t *ntpip,uint8_t srcport) { + if(is_lan(myip, ntpip)) { + setMACandIPs(destmacaddr, ntpip); + } else + setMACandIPs(gwmacaddr, ntpip); + gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + memcpy_P(gPB + IP_P,iphdr,9); + gPB[IP_TOTLEN_L_P] = 0x4c; + gPB[IP_PROTO_P] = IP_PROTO_UDP_V; + fill_ip_hdr_checksum(); + gPB[UDP_DST_PORT_H_P] = 0; + gPB[UDP_DST_PORT_L_P] = 0x7b; // ntp = 123 + gPB[UDP_SRC_PORT_H_P] = 10; + gPB[UDP_SRC_PORT_L_P] = srcport; // lower 8 bit of src port + gPB[UDP_LEN_H_P] = 0; + gPB[UDP_LEN_L_P] = 56; // fixed len + gPB[UDP_CHECKSUM_H_P] = 0; + gPB[UDP_CHECKSUM_L_P] = 0; + memset(gPB + UDP_DATA_P, 0, 48); + memcpy_P(gPB + UDP_DATA_P,ntpreqhdr,10); + fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + 48,1); + packetSend(90); +} + +uint8_t EtherCard::ntpProcessAnswer (uint32_t *time,uint8_t dstport_l) { + if ((dstport_l && gPB[UDP_DST_PORT_L_P]!=dstport_l) || gPB[UDP_LEN_H_P]!=0 || + gPB[UDP_LEN_L_P]!=56 || gPB[UDP_SRC_PORT_L_P]!=0x7b) + return 0; + ((uint8_t*) time)[3] = gPB[0x52]; + ((uint8_t*) time)[2] = gPB[0x53]; + ((uint8_t*) time)[1] = gPB[0x54]; + ((uint8_t*) time)[0] = gPB[0x55]; + return 1; +} + +void EtherCard::udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport) { + if(is_lan(myip, dip)) // this works because both dns mac and destinations mac are stored in same variable - destmacaddr + setMACandIPs(destmacaddr, dip); // at different times. The program could have separate variable for dns mac, then here should be + else // checked if dip is dns ip and separately if dip is hisip and then use correct mac. + setMACandIPs(gwmacaddr, dip); + // see http://tldp.org/HOWTO/Multicast-HOWTO-2.html + // multicast or broadcast address, https://github.com/jcw/ethercard/issues/59 + if ((dip[0] & 0xF0) == 0xE0 || *((unsigned long*) dip) == 0xFFFFFFFF) + EtherCard::copyMac(gPB + ETH_DST_MAC, allOnes); + gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + memcpy_P(gPB + IP_P,iphdr,9); + gPB[IP_TOTLEN_H_P] = 0; + gPB[IP_PROTO_P] = IP_PROTO_UDP_V; + gPB[UDP_DST_PORT_H_P] = (dport>>8); + gPB[UDP_DST_PORT_L_P] = dport; + gPB[UDP_SRC_PORT_H_P] = (sport>>8); + gPB[UDP_SRC_PORT_L_P] = sport; + gPB[UDP_LEN_H_P] = 0; + gPB[UDP_CHECKSUM_H_P] = 0; + gPB[UDP_CHECKSUM_L_P] = 0; +} + +void EtherCard::udpTransmit (uint16_t datalen) { + gPB[IP_TOTLEN_H_P] = (IP_HEADER_LEN+UDP_HEADER_LEN+datalen) >> 8; + gPB[IP_TOTLEN_L_P] = IP_HEADER_LEN+UDP_HEADER_LEN+datalen; + fill_ip_hdr_checksum(); + gPB[UDP_LEN_H_P] = (UDP_HEADER_LEN+datalen) >>8; + gPB[UDP_LEN_L_P] = UDP_HEADER_LEN+datalen; + fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + datalen,1); + packetSend(UDP_HEADER_LEN+IP_HEADER_LEN+ETH_HEADER_LEN+datalen); +} + +void EtherCard::sendUdp (const char *data, uint8_t datalen, uint16_t sport, + const uint8_t *dip, uint16_t dport) { + udpPrepare(sport, dip, dport); + if (datalen>220) + datalen = 220; + memcpy(gPB + UDP_DATA_P, data, datalen); + udpTransmit(datalen); +} + +void EtherCard::sendWol (uint8_t *wolmac) { + setMACandIPs(allOnes, allOnes); + gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + memcpy_P(gPB + IP_P,iphdr,9); + gPB[IP_TOTLEN_L_P] = 0x82; + gPB[IP_PROTO_P] = IP_PROTO_UDP_V; + fill_ip_hdr_checksum(); + gPB[UDP_DST_PORT_H_P] = 0; + gPB[UDP_DST_PORT_L_P] = 0x9; // wol = normally 9 + gPB[UDP_SRC_PORT_H_P] = 10; + gPB[UDP_SRC_PORT_L_P] = 0x42; // source port does not matter + gPB[UDP_LEN_H_P] = 0; + gPB[UDP_LEN_L_P] = 110; // fixed len + gPB[UDP_CHECKSUM_H_P] = 0; + gPB[UDP_CHECKSUM_L_P] = 0; + copyMac(gPB + UDP_DATA_P, allOnes); + uint8_t pos = UDP_DATA_P; + for (uint8_t m = 0; m < 16; ++m) { + pos += 6; + copyMac(gPB + pos, wolmac); + } + fill_checksum(UDP_CHECKSUM_H_P, IP_SRC_P, 16 + 102,1); + packetSend(pos + 6); +} + +// make a arp request +static void client_arp_whohas(uint8_t *ip_we_search) { + setMACs(allOnes); + gPB[ETH_TYPE_H_P] = ETHTYPE_ARP_H_V; + gPB[ETH_TYPE_L_P] = ETHTYPE_ARP_L_V; + memcpy_P(gPB + ETH_ARP_P,arpreqhdr,8); + memset(gPB + ETH_ARP_DST_MAC_P, 0, 6); + EtherCard::copyMac(gPB + ETH_ARP_SRC_MAC_P, EtherCard::mymac); + EtherCard::copyIp(gPB + ETH_ARP_DST_IP_P, ip_we_search); + EtherCard::copyIp(gPB + ETH_ARP_SRC_IP_P, EtherCard::myip); + EtherCard::packetSend(42); +} + +uint8_t EtherCard::clientWaitingGw () { + return !(waitgwmac & WGW_HAVE_GW_MAC); +} + +static uint8_t client_store_mac(uint8_t *source_ip, uint8_t *mac) { + if (memcmp(gPB + ETH_ARP_SRC_IP_P, source_ip, 4) != 0) + return 0; + EtherCard::copyMac(mac, gPB + ETH_ARP_SRC_MAC_P); + return 1; +} + +// static void client_gw_arp_refresh() { +// if (waitgwmac & WGW_HAVE_GW_MAC) +// waitgwmac |= WGW_REFRESHING; +// } + +void EtherCard::setGwIp (const uint8_t *gwipaddr) { + delaycnt = 0; //request gateway ARP lookup + waitgwmac = WGW_INITIAL_ARP; // causes an arp request in the packet loop + copyIp(gwip, gwipaddr); +} + +void EtherCard::updateBroadcastAddress() +{ + for(uint8_t i=0; i<4; i++) + broadcastip[i] = myip[i] | ~netmask[i]; +} + +static void client_syn(uint8_t srcport,uint8_t dstport_h,uint8_t dstport_l) { + if(is_lan(EtherCard::myip, EtherCard::hisip)) { + setMACandIPs(destmacaddr, EtherCard::hisip); + } else + setMACandIPs(gwmacaddr, EtherCard::hisip); + gPB[ETH_TYPE_H_P] = ETHTYPE_IP_H_V; + gPB[ETH_TYPE_L_P] = ETHTYPE_IP_L_V; + memcpy_P(gPB + IP_P,iphdr,9); + gPB[IP_TOTLEN_L_P] = 44; // good for syn + gPB[IP_PROTO_P] = IP_PROTO_TCP_V; + fill_ip_hdr_checksum(); + gPB[TCP_DST_PORT_H_P] = dstport_h; + gPB[TCP_DST_PORT_L_P] = dstport_l; + gPB[TCP_SRC_PORT_H_P] = TCPCLIENT_SRC_PORT_H; + gPB[TCP_SRC_PORT_L_P] = srcport; // lower 8 bit of src port + memset(gPB + TCP_SEQ_H_P, 0, 8); + gPB[TCP_SEQ_H_P+2] = seqnum; + seqnum += 3; + gPB[TCP_HEADER_LEN_P] = 0x60; // 0x60=24 len: (0x60>>4) * 4 + gPB[TCP_FLAGS_P] = TCP_FLAGS_SYN_V; + gPB[TCP_WIN_SIZE] = 0x3; // 1024 = 0x400 768 = 0x300, initial window + gPB[TCP_WIN_SIZE+1] = 0x0; + gPB[TCP_CHECKSUM_H_P] = 0; + gPB[TCP_CHECKSUM_L_P] = 0; + gPB[TCP_CHECKSUM_L_P+1] = 0; + gPB[TCP_CHECKSUM_L_P+2] = 0; + gPB[TCP_OPTIONS_P] = 2; + gPB[TCP_OPTIONS_P+1] = 4; + gPB[TCP_OPTIONS_P+2] = (CLIENTMSS>>8); + gPB[TCP_OPTIONS_P+3] = (uint8_t) CLIENTMSS; + fill_checksum(TCP_CHECKSUM_H_P, IP_SRC_P, 8 +TCP_HEADER_LEN_PLAIN+4,2); + // 4 is the tcp mss option: + EtherCard::packetSend(IP_HEADER_LEN+TCP_HEADER_LEN_PLAIN+ETH_HEADER_LEN+4); +} + +uint8_t EtherCard::clientTcpReq (uint8_t (*result_cb)(uint8_t,uint8_t,uint16_t,uint16_t), + uint16_t (*datafill_cb)(uint8_t),uint16_t port) { + client_tcp_result_cb = result_cb; + client_tcp_datafill_cb = datafill_cb; + tcp_client_port_h = port>>8; + tcp_client_port_l = port; + tcp_client_state = 1; // Flag to packetloop to initiate a TCP/IP session by send a syn + tcp_fd = (tcp_fd + 1) & 7; + return tcp_fd; +} + +static uint16_t www_client_internal_datafill_cb(uint8_t fd) { + BufferFiller bfill = EtherCard::tcpOffset(); + if (fd==www_fd) { + if (client_postval == 0) { + bfill.emit_p(PSTR("GET $F$S HTTP/1.0\r\n" + "Host: $F\r\n" + "$F\r\n" + "\r\n"), client_urlbuf, + client_urlbuf_var, + client_hoststr, client_additionalheaderline); + } else { + const char* ahl = client_additionalheaderline; + bfill.emit_p(PSTR("POST $F HTTP/1.0\r\n" + "Host: $F\r\n" + "$F$S" + "Accept: */*\r\n" + "Content-Length: $D\r\n" + "Content-Type: application/x-www-form-urlencoded\r\n" + "\r\n" + "$S"), client_urlbuf, + client_hoststr, + ahl != 0 ? ahl : PSTR(""), + ahl != 0 ? "\r\n" : "", + strlen(client_postval), + client_postval); + } + } + return bfill.position(); +} + +static uint8_t www_client_internal_result_cb(uint8_t fd, uint8_t statuscode, uint16_t datapos, uint16_t len_of_data) { + if (fd!=www_fd) + (*client_browser_cb)(4,0,0); + else if (statuscode==0 && len_of_data>12 && client_browser_cb) { + uint8_t f = strncmp("200",(char *)&(gPB[datapos+9]),3) != 0; + (*client_browser_cb)(f, ((uint16_t)TCP_SRC_PORT_H_P+(gPB[TCP_HEADER_LEN_P]>>4)*4),len_of_data); + } + return 0; +} + +void EtherCard::browseUrl (const char *urlbuf, const char *urlbuf_varpart, const char *hoststr, void (*callback)(uint8_t,uint16_t,uint16_t)) { + browseUrl(urlbuf, urlbuf_varpart, hoststr, PSTR("Accept: text/html"), callback); +} + +void EtherCard::browseUrl (const char *urlbuf, const char *urlbuf_varpart, const char *hoststr, const char *additionalheaderline, void (*callback)(uint8_t,uint16_t,uint16_t)) { + client_urlbuf = urlbuf; + client_urlbuf_var = urlbuf_varpart; + client_hoststr = hoststr; + client_additionalheaderline = additionalheaderline; + client_postval = 0; + client_browser_cb = callback; + www_fd = clientTcpReq(&www_client_internal_result_cb,&www_client_internal_datafill_cb,hisport); +} + +void EtherCard::httpPost (const char *urlbuf, const char *hoststr, const char *additionalheaderline, const char *postval, void (*callback)(uint8_t,uint16_t,uint16_t)) { + client_urlbuf = urlbuf; + client_hoststr = hoststr; + client_additionalheaderline = additionalheaderline; + client_postval = postval; + client_browser_cb = callback; + www_fd = clientTcpReq(&www_client_internal_result_cb,&www_client_internal_datafill_cb,hisport); +} + +static uint16_t tcp_datafill_cb(uint8_t fd) { + uint16_t len = Stash::length(); + Stash::extract(0, len, EtherCard::tcpOffset()); + Stash::cleanup(); + EtherCard::tcpOffset()[len] = 0; +#if SERIAL + Serial.print("REQUEST: "); + Serial.println(len); + Serial.println((char*) EtherCard::tcpOffset()); +#endif + result_fd = 123; // bogus value + return len; +} + +static uint8_t tcp_result_cb(uint8_t fd, uint8_t status, uint16_t datapos, uint16_t datalen) { + if (status == 0) { + result_fd = fd; // a valid result has been received, remember its session id + result_ptr = (char*) ether.buffer + datapos; + // result_ptr[datalen] = 0; + } + return 1; +} + +uint8_t EtherCard::tcpSend () { + www_fd = clientTcpReq(&tcp_result_cb, &tcp_datafill_cb, hisport); + return www_fd; +} + +const char* EtherCard::tcpReply (uint8_t fd) { + if (result_fd != fd) + return 0; + result_fd = 123; // set to a bogus value to prevent future match + return result_ptr; +} + +void EtherCard::registerPingCallback (void (*callback)(uint8_t *srcip)) { + icmp_cb = callback; +} + +uint8_t EtherCard::packetLoopIcmpCheckReply (const uint8_t *ip_monitoredhost) { + return gPB[IP_PROTO_P]==IP_PROTO_ICMP_V && + gPB[ICMP_TYPE_P]==ICMP_TYPE_ECHOREPLY_V && + gPB[ICMP_DATA_P]== PINGPATTERN && + check_ip_message_is_from(ip_monitoredhost); +} + +uint16_t EtherCard::accept(const uint16_t port, uint16_t plen) { + uint16_t pos; + + if (gPB[TCP_DST_PORT_H_P] == (port >> 8) && + gPB[TCP_DST_PORT_L_P] == ((uint8_t) port)) + { //Packet targetted at specified port + if (gPB[TCP_FLAGS_P] & TCP_FLAGS_SYN_V) + make_tcp_synack_from_syn(); //send SYN+ACK + else if (gPB[TCP_FLAGS_P] & TCP_FLAGS_ACK_V) + { //This is an acknowledgement to our SYN+ACK so let's start processing that payload + info_data_len = get_tcp_data_len(); + if (info_data_len > 0) + { //Got some data + pos = TCP_DATA_START; // TCP_DATA_START is a formula + if (pos <= plen - 8) + return pos; + } + else if (gPB[TCP_FLAGS_P] & TCP_FLAGS_FIN_V) + make_tcp_ack_from_any(0,0); //No data so close connection + } + } + return 0; +} + +uint16_t EtherCard::packetLoop (uint16_t plen) { + uint16_t len; + if(using_dhcp){ + ether.DhcpStateMachine(plen); + } + + if (plen==0) { + //Check every 65536 (no-packet) cycles whether we need to retry ARP request for gateway + if ((waitgwmac & WGW_INITIAL_ARP || waitgwmac & WGW_REFRESHING) && + delaycnt==0 && isLinkUp()) { + client_arp_whohas(gwip); + waitgwmac |= WGW_ACCEPT_ARP_REPLY; + } + + + + delaycnt++; + //Initiate TCP/IP session if pending + if (tcp_client_state==1 && (waitgwmac & WGW_HAVE_GW_MAC)) { // send a syn + tcp_client_state = 2; + tcpclient_src_port_l++; // allocate a new port + client_syn(((tcp_fd<<5) | (0x1f & tcpclient_src_port_l)),tcp_client_port_h,tcp_client_port_l); + } + //!@todo this is trying to find mac only once. Need some timeout to make another call if first one doesn't succeed. + if(is_lan(myip, dnsip) && !has_dns_mac && !waiting_for_dns_mac) { + client_arp_whohas(dnsip); + waiting_for_dns_mac = true; + } + //!@todo this is trying to find mac only once. Need some timeout to make another call if first one doesn't succeed. + if(is_lan(myip, hisip) && !has_dest_mac && !waiting_for_dest_mac) { + client_arp_whohas(hisip); + waiting_for_dest_mac = true; + } + + return 0; + } + + if (eth_type_is_arp_and_my_ip(plen)) + { //Service ARP request + if (gPB[ETH_ARP_OPCODE_L_P]==ETH_ARP_OPCODE_REQ_L_V) + make_arp_answer_from_request(); + if (waitgwmac & WGW_ACCEPT_ARP_REPLY && (gPB[ETH_ARP_OPCODE_L_P]==ETH_ARP_OPCODE_REPLY_L_V) && client_store_mac(gwip, gwmacaddr)) + waitgwmac = WGW_HAVE_GW_MAC; + if (!has_dns_mac && waiting_for_dns_mac && client_store_mac(dnsip, destmacaddr)) { + has_dns_mac = true; + waiting_for_dns_mac = false; + } + if (!has_dest_mac && waiting_for_dest_mac && client_store_mac(hisip, destmacaddr)){ + has_dest_mac = true; + waiting_for_dest_mac = false; + } + return 0; + } + + if (eth_type_is_ip_and_my_ip(plen)==0) + { //Not IP so ignoring + //!@todo Add other protocols (and make each optional at compile time) + return 0; + } + if (gPB[IP_PROTO_P]==IP_PROTO_ICMP_V && gPB[ICMP_TYPE_P]==ICMP_TYPE_ECHOREQUEST_V) + { //Service ICMP echo request (ping) + if (icmp_cb) + (*icmp_cb)(&(gPB[IP_SRC_P])); + make_echo_reply_from_request(plen); + return 0; + } + if (ether.udpServerListening() && gPB[IP_PROTO_P]==IP_PROTO_UDP_V) + { //Call UDP server handler (callback) if one is defined for this packet + if(ether.udpServerHasProcessedPacket(plen)) + return 0; //An UDP server handler (callback) has processed this packet + } + if (plen<54 && gPB[IP_PROTO_P]!=IP_PROTO_TCP_V ) + return 0; //Packet flagged as TCP but shorter than minimum TCP packet length + if (gPB[TCP_DST_PORT_H_P]==TCPCLIENT_SRC_PORT_H) + { //Source port is in range reserved (by EtherCard) for client TCP/IP connections + if (check_ip_message_is_from(hisip)==0) + return 0; //Not current TCP/IP connection (only handle one at a time) + if (gPB[TCP_FLAGS_P] & TCP_FLAGS_RST_V) + { //TCP reset flagged + if (client_tcp_result_cb) + (*client_tcp_result_cb)((gPB[TCP_DST_PORT_L_P]>>5)&0x7,3,0,0); + tcp_client_state = 5; + return 0; + } + len = get_tcp_data_len(); + if (tcp_client_state==2) + { //Waiting for SYN-ACK + if ((gPB[TCP_FLAGS_P] & TCP_FLAGS_SYN_V) && (gPB[TCP_FLAGS_P] &TCP_FLAGS_ACK_V)) + { //SYN and ACK flags set so this is an acknowledgement to our SYN + make_tcp_ack_from_any(0,0); + gPB[TCP_FLAGS_P] = TCP_FLAGS_ACK_V|TCP_FLAGS_PUSH_V; + if (client_tcp_datafill_cb) + len = (*client_tcp_datafill_cb)((gPB[TCP_SRC_PORT_L_P]>>5)&0x7); + else + len = 0; + tcp_client_state = 3; + make_tcp_ack_with_data_noflags(len); + } + else + { //Expecting SYN+ACK so reset and resend SYN + tcp_client_state = 1; // retry + len++; + if (gPB[TCP_FLAGS_P] & TCP_FLAGS_ACK_V) + len = 0; + make_tcp_ack_from_any(len,TCP_FLAGS_RST_V); + } + return 0; + } + if (tcp_client_state==3 && len>0) + { //TCP connection established so read data + if (client_tcp_result_cb) { + uint16_t tcpstart = TCP_DATA_START; // TCP_DATA_START is a formula + if (tcpstart>plen-8) + tcpstart = plen-8; // dummy but save + uint16_t save_len = len; + if (tcpstart+len>plen) + save_len = plen-tcpstart; + (*client_tcp_result_cb)((gPB[TCP_DST_PORT_L_P]>>5)&0x7,0,tcpstart,save_len); //Call TCP handler (callback) function + + if(persist_tcp_connection) + { //Keep connection alive by sending ACK + make_tcp_ack_from_any(len,TCP_FLAGS_PUSH_V); + } + else + { //Close connection + make_tcp_ack_from_any(len,TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V); + tcp_client_state = 6; + } + return 0; + } + } + if (tcp_client_state != 5) + { // + if (gPB[TCP_FLAGS_P] & TCP_FLAGS_FIN_V) { + if(tcp_client_state == 3) { + return 0; // In some instances FIN is received *before* DATA. If that is the case, we just return here and keep looking for the data packet + } + make_tcp_ack_from_any(len+1,TCP_FLAGS_PUSH_V|TCP_FLAGS_FIN_V); + tcp_client_state = 6; // connection terminated + } else if (len>0) { + make_tcp_ack_from_any(len,0); + } + } + return 0; + } + + //If we are here then this is a TCP/IP packet targetted at us and not related to out client connection so accept + return accept(hisport, plen); +} + +void EtherCard::persistTcpConnection(bool persist){ + persist_tcp_connection = persist; +} diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/src/udpserver.cpp b/STM32F1/libraries/Serasidis_EtherCard_STM/src/udpserver.cpp new file mode 100644 index 0000000..08ac5fe --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/src/udpserver.cpp @@ -0,0 +1,73 @@ +// Simple UDP listening server +// +// Author: Brian Lee +// +// Copyright: GPL V2 +// See http://www.gnu.org/licenses/gpl.html + +#include "EtherCard_STM.h" +#include "net.h" + +#define gPB ether.buffer + +#define UDPSERVER_MAXLISTENERS 8 //the maximum number of port listeners. + +typedef struct { + UdpServerCallback callback; + uint16_t port; + bool listening; +} UdpServerListener; + +UdpServerListener listeners[UDPSERVER_MAXLISTENERS]; +byte numListeners = 0; + +void EtherCard::udpServerListenOnPort(UdpServerCallback callback, uint16_t port) { + if(numListeners < UDPSERVER_MAXLISTENERS) + { + listeners[numListeners] = (UdpServerListener) { + callback, port, true + }; + numListeners++; + } +} + +void EtherCard::udpServerPauseListenOnPort(uint16_t port) { + for(int i = 0; i < numListeners; i++) + { + if(gPB[UDP_DST_PORT_H_P] == (listeners[i].port >> 8) && gPB[UDP_DST_PORT_L_P] == ((byte) listeners[i].port)) { + listeners[i].listening = false; + } + } +} + +void EtherCard::udpServerResumeListenOnPort(uint16_t port) { + for(int i = 0; i < numListeners; i++) + { + if(gPB[UDP_DST_PORT_H_P] == (listeners[i].port >> 8) && gPB[UDP_DST_PORT_L_P] == ((byte) listeners[i].port)) { + listeners[i].listening = true; + } + } +} + +bool EtherCard::udpServerListening() { + return numListeners > 0; +} + +bool EtherCard::udpServerHasProcessedPacket(uint16_t plen) { + bool packetProcessed = false; + for(int i = 0; i < numListeners; i++) + { + if(gPB[UDP_DST_PORT_H_P] == (listeners[i].port >> 8) && gPB[UDP_DST_PORT_L_P] == ((byte) listeners[i].port) && listeners[i].listening) + { + uint16_t datalen = (uint16_t) (gPB[UDP_LEN_H_P] << 8) + gPB[UDP_LEN_L_P] - UDP_HEADER_LEN; + listeners[i].callback( + listeners[i].port, + gPB + IP_SRC_P, + (gPB[UDP_SRC_PORT_H_P] << 8) | gPB[UDP_SRC_PORT_L_P], + (const char *) (gPB + UDP_DATA_P), + datalen); + packetProcessed = true; + } + } + return packetProcessed; +} diff --git a/STM32F1/libraries/Serasidis_EtherCard_STM/src/webutil.cpp b/STM32F1/libraries/Serasidis_EtherCard_STM/src/webutil.cpp new file mode 100644 index 0000000..282ae48 --- /dev/null +++ b/STM32F1/libraries/Serasidis_EtherCard_STM/src/webutil.cpp @@ -0,0 +1,200 @@ +// Some common utilities needed for IP and web applications +// Author: Guido Socher +// Copyright: GPL V2 +// +// 2010-05-20 + +#include "EtherCard_STM.h" + +void EtherCard::copyIp (uint8_t *dst, const uint8_t *src) { + memcpy(dst, src, 4); +} + +void EtherCard::copyMac (uint8_t *dst, const uint8_t *src) { + memcpy(dst, src, 6); +} + +void EtherCard::printIp (const char* msg, const uint8_t *buf) { + Serial.print(msg); + EtherCard::printIp(buf); + Serial.println(); +} + +void EtherCard::printIp (const __FlashStringHelper *ifsh, const uint8_t *buf) { + Serial.print(ifsh); + EtherCard::printIp(buf); + Serial.println(); +} + +void EtherCard::printIp (const uint8_t *buf) { + for (uint8_t i = 0; i < 4; ++i) { + Serial.print( buf[i], DEC ); + if (i < 3) + Serial.print('.'); + } +} + +// search for a string of the form key=value in +// a string that looks like q?xyz=abc&uvw=defgh HTTP/1.1\r\n +// +// The returned value is stored in strbuf. You must allocate +// enough storage for strbuf, maxlen is the size of strbuf. +// I.e the value it is declated with: strbuf[5]-> maxlen=5 +uint8_t EtherCard::findKeyVal (const char *str,char *strbuf, uint8_t maxlen,const char *key) +{ + uint8_t found=0; + uint8_t i=0; + const char *kp; + kp=key; + while(*str && *str!=' ' && *str!='\n' && found==0){ + if (*str == *kp){ + kp++; + if (*kp == '\0'){ + str++; + kp=key; + if (*str == '='){ + found=1; + } + } + }else{ + kp=key; + } + str++; + } + if (found==1){ + // copy the value to a buffer and terminate it with '\0' + while(*str && *str!=' ' && *str!='\n' && *str!='&' && i= '0' && c <='9'){ + return((unsigned char)c - '0'); + } + if (c >= 'a' && c <='f'){ + return((unsigned char)c - 'a' + 10); + } + if (c >= 'A' && c <='F'){ + return((unsigned char)c - 'A' + 10); + } + return(0); +} + +// decode a url string e.g "hello%20joe" or "hello+joe" becomes "hello joe" +void EtherCard::urlDecode (char *urlbuf) +{ + char c; + char *dst = urlbuf; + while ((c = *urlbuf) != 0) { + if (c == '+') c = ' '; + if (c == '%') { + c = *++urlbuf; + c = (h2int(c) << 4) | h2int(*++urlbuf); + } + *dst++ = c; + urlbuf++; + } + *dst = '\0'; +} + +// convert a single character to a 2 digit hex str +// a terminating '\0' is added +void int2h(char c, char *hstr) +{ + hstr[1]=(c & 0xf)+'0'; + if ((c & 0xf) >9){ + hstr[1]=(c & 0xf) - 10 + 'a'; + } + c=(c>>4)&0xf; + hstr[0]=c+'0'; + if (c > 9){ + hstr[0]=c - 10 + 'a'; + } + hstr[2]='\0'; +} + +// there must be enough space in urlbuf. In the worst case that is +// 3 times the length of str +void EtherCard::urlEncode (char *str,char *urlbuf) +{ + char c; + while ((c = *str) != 0) { + if (c == ' '||isalnum(c)){ + if (c == ' '){ + c = '+'; + } + *urlbuf=c; + str++; + urlbuf++; + continue; + } + *urlbuf='%'; + urlbuf++; + int2h(c,urlbuf); + urlbuf++; + urlbuf++; + str++; + } + *urlbuf='\0'; +} + +// parse a string and extract the IP to bytestr +uint8_t EtherCard::parseIp (uint8_t *bytestr,char *str) +{ + char *sptr; + uint8_t i=0; + sptr=NULL; + while(i<4){ + bytestr[i]=0; + i++; + } + i=0; + while(*str && i<4){ + // if a number then start + if (sptr==NULL && isdigit(*str)){ + sptr=str; + } + if (*str == '.'){ + *str ='\0'; + bytestr[i]=(atoi(sptr)&0xff); + i++; + sptr=NULL; + } + str++; + } + *str ='\0'; + if (i==3){ + bytestr[i]=(atoi(sptr)&0xff); + return(0); + } + return(1); +} + +// take a byte string and convert it to a human readable display string (base is 10 for ip and 16 for mac addr), len is 4 for IP addr and 6 for mac. +void EtherCard::makeNetStr (char *resultstr,uint8_t *bytestr,uint8_t len,char separator,uint8_t base) +{ + uint8_t i=0; + uint8_t j=0; + while(i