Merge branch 'master' into move_serial_config_to_variants

This commit is contained in:
rogerclarkmelbourne 2015-05-31 15:28:16 +10:00
commit 29f7cc74b4
39 changed files with 6223 additions and 36 deletions

View File

@ -6,35 +6,35 @@ menu.bootloader_version=Bootloader version
menu.upload_method=Upload method
##############################################################
maple_mini.name=LeafLabs Maple Mini Rev 2 to Flash
mapleMini.name=LeafLabs Maple Mini Rev 2 to Flash
maple_mini.build.board=MAPLE_MINI
maple_mini.build.core=maple
maple_mini.build.cpu_flags=-DMCU_STM32F103CB -DSERIAL_USB
maple_mini.build.variant=maple_mini
maple_mini.upload.usbID=1EAF:0003
mapleMini.build.board=MAPLE_MINI
mapleMini.build.core=maple
mapleMini.build.cpu_flags=-DMCU_STM32F103CB -DSERIAL_USB
mapleMini.build.variant=maple_mini
mapleMini.upload.usbID=1EAF:0003
maple_mini.upload.tool=maple_upload
maple_mini.upload.protocol=maple_dfu
maple_mini.upload.use_1200bps_touch=false
maple_mini.upload.file_type=bin
maple_mini.upload.auto_reset=true
mapleMini.upload.tool=maple_upload
mapleMini.upload.protocol=maple_dfu
mapleMini.upload.use_1200bps_touch=false
mapleMini.upload.file_type=bin
mapleMini.upload.auto_reset=true
maple_mini.menu.bootloader_version.original = Original (17k RAM,108k Flash)
maple_mini.menu.bootloader_version.original.build.vect=VECT_TAB_ADDR=0x8005000
maple_mini.menu.bootloader_version.original.build.ldscript=ld/flash.ld
maple_mini.menu.bootloader_version.original.upload.ram.maximum_size=17408
maple_mini.menu.bootloader_version.original.upload.flash.maximum_size=110592
maple_mini.menu.bootloader_version.original.upload.maximum_size=110592
maple_mini.menu.bootloader_version.original.upload.altID=1
mapleMini.menu.bootloader_version.original = Original (17k RAM,108k Flash)
mapleMini.menu.bootloader_version.original.build.vect=VECT_TAB_ADDR=0x8005000
mapleMini.menu.bootloader_version.original.build.ldscript=ld/flash.ld
mapleMini.menu.bootloader_version.original.upload.ram.maximum_size=17408
mapleMini.menu.bootloader_version.original.upload.flash.maximum_size=110592
mapleMini.menu.bootloader_version.original.upload.maximum_size=110592
mapleMini.menu.bootloader_version.original.upload.altID=1
maple_mini.menu.bootloader_version.bootloader20 = Bootloader 2.0 (20k RAM,120k Flash)
maple_mini.menu.bootloader_version.bootloader20.build.vect=VECT_TAB_ADDR=0x8002000
maple_mini.menu.bootloader_version.bootloader20.build.ldscript=ld/bootloader_20.ld
maple_mini.menu.bootloader_version.bootloader20.upload.ram.maximum_size=20480
maple_mini.menu.bootloader_version.bootloader20.upload.flash.maximum_size=122880
maple_mini.menu.bootloader_version.bootloader20.upload.maximum_size=122880
maple_mini.menu.bootloader_version.bootloader20.upload.altID=2
mapleMini.menu.bootloader_version.bootloader20 = Bootloader 2.0 (20k RAM,120k Flash)
mapleMini.menu.bootloader_version.bootloader20.build.vect=VECT_TAB_ADDR=0x8002000
mapleMini.menu.bootloader_version.bootloader20.build.ldscript=ld/bootloader_20.ld
mapleMini.menu.bootloader_version.bootloader20.upload.ram.maximum_size=20480
mapleMini.menu.bootloader_version.bootloader20.upload.flash.maximum_size=122880
mapleMini.menu.bootloader_version.bootloader20.upload.maximum_size=122880
mapleMini.menu.bootloader_version.bootloader20.upload.altID=2
##############################################################
maple.name=LeafLabs Maple Rev 3+ to Flash

View File

@ -163,15 +163,14 @@ __default_handler:
.weak __irq_tim1_cc
.globl __irq_tim1_cc
.set __irq_tim1_cc, __default_handler
.weak __irq_tim2
.globl __irq_tim2
.set __irq_tim2, __default_handler
.weak __irq_tim3
.globl __irq_tim3
.set __irq_tim3, __default_handler
.weak __irq_tim4
.globl __irq_tim4
.set __irq_tim4, __default_handler
.weakref __irq_tim2, __default_handler
.globl __irq_tim2
.weakref __irq_tim3, __default_handler
.globl __irq_tim3
.weakref __irq_tim4, __default_handler
.globl __irq_tim4
.weak __irq_i2c1_ev
.globl __irq_i2c1_ev
.set __irq_i2c1_ev, __default_handler

View File

@ -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

View File

@ -0,0 +1,320 @@
// Collect RF12 packets and send them on as UDP collectd packets on Ethernet.
// 2010-05-20 <jc@wippler.nl> 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 <EtherCard_STM.h>
#include <JeeLib.h>
#include <avr/eeprom.h>
#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"
"<title>RF12 JeeUdp</title>"
"<h2>RF12 JeeUdp @ $D - RF12 @ $D.$D</h2>"
"<a href='c'>Configure</a> - <a href='s'>Send Packet</a>"
"<h3>Last $D messages:</h3>"
"<pre>"), 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(
"</pre>"
"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"
"<h3>Server node configuration</h3>"
"<form>"
"<p>"
"Freq band <input type=text name=b value='$D' size=1> (4, 8, or 9)<br>"
"Net group <input type=text name=g value='$D' size=3> (1..250)<br>"
"Collect mode: <input type=checkbox name=c value='1' $S> "
"(don't send ACKs)<br><br>"
"UDP Port <input type=text name=p value='$D' size=5> (1024..30000)"
"</p>"
"<input type=submit value=Set>"
"</form>"), 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"
"<h3>Send a wireless data packet</h3>"
"<form>"
"<p>"
"Data bytes <input type=text name=b size=50> (decimal)<br>"
"Destination node <input type=text name=d size=3> "
"(1..31, or 0 to broadcast)<br>"
"</p>"
"<input type=submit value=Send>"
"</form>"), 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"
"<h1>401 Unauthorized</h1>"));
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;
}
}

View File

@ -0,0 +1,145 @@
#include <EtherCard_STM.h> |Mac adress|
#include <SPI.h>
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<?xml version='1.0'?>\r<root xmlns='urn:schemas-upnp-org:device-1-0'><device><deviceType>urn:schemas-upnp-org:device:BinaryLight:1</deviceType><presentationURL>/</presentationURL><friendlyName>Arduino</friendlyName><manufacturer>Fredycpu</manufacturer><manufacturerURL>http://fredycpu.pro</manufacturerURL><serialNumber>1</serialNumber><UDN>uuid:abcdefgh-7dec-11d0-a765-7499692d3040</UDN></device></root> ";
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 // <deviceType>urn:schemas-upnp-org:device:BinaryLight:1</deviceType> // declare as home automation
// in XML_DESCRIP // <friendlyName>Arduino</friendlyName> // declare the name of the service here Arduino
// in XML_DESCRIP // <presentationURL>/</presentationURL> // 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"
"<html>"
"<head><title>"
"multipackets Test"
"</title></head>"
"<body>"
"<a href='/'>Start here</a><br>"
"<a href='/test.jpg'>Image test</a><br>"
"<h3>packet 1</h3>"
"<p><em>"
"the first packet send "
"</em></p>"
;
const char pageB[] PROGMEM =
"<h3>packet 2</h3>"
"<p><em>"
"if you read this it mean it works"
"</em></p>"
;
const char pageC[] PROGMEM =
"<h3>packet 3</h3>"
"<p><em>"
"if you read this it mean it works"
"</em></p>"
;
const char pageD[] PROGMEM =
"<h3>packet 4</h3>"
"<p><em>"
"if you read this it mean it works"
"</em></p>"
;
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
}

View File

@ -0,0 +1,84 @@
// Present a "Will be back soon web page", as stand-in webserver.
// 2011-01-30 <jc@wippler.nl> 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 <EtherCard_STM.h>
#include <SPI.h>
#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"
"<html>"
"<head><title>"
"Service Temporarily Unavailable"
"</title></head>"
"<body>"
"<h3>This service is currently unavailable</h3>"
"<p><em>"
"The main server is currently off-line.<br />"
"Please try again later."
"</em></p>"
"</body>"
"</html>"
;
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);
}
}

View File

@ -0,0 +1,295 @@
// Arduino demo sketch for testing RFM12B + ethernet
// 2010-05-20 <jc@wippler.nl> 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 <EtherCard_STM.h>
#include <JeeLib.h>
#include <avr/eeprom.h>
#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"
"<meta http-equiv='refresh' content='$D'/>"
"<title>RF12 etherNode - $D MHz, group $D</title>"
"RF12 etherNode - $D MHz, group $D "
"- <a href='c'>configure</a> - <a href='s'>send packet</a>"
"<h3>Last $D messages:</h3>"
"<pre>"), 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(
"</pre>"
"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"
"<h3>Server node configuration</h3>"
"<form>"
"<p>"
"Freq band <input type=text name=b value='$D' size=1> (4, 8, or 9)<br>"
"Net group <input type=text name=g value='$D' size=3> (1..250)<br>"
"Collect mode: <input type=checkbox name=c value='1' $S> "
"Don't send ACKs<br><br>"
"Refresh rate <input type=text name=r value='$D' size=4> (1..3600 seconds)"
"</p>"
"<input type=submit value=Set>"
"</form>"), 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"
"<h3>Send a wireless data packet</h3>"
"<form>"
"<p>"
"Data bytes <input type=text name=b size=50> (decimal)<br>"
"Destination node <input type=text name=d size=3> "
"(1..31, or 0 to broadcast)<br>"
"</p>"
"<input type=submit value=Send>"
"</form>"), 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"
"<h1>401 Unauthorized</h1>"));
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;
}
}

View File

@ -0,0 +1,75 @@
// This demo does web requests via DHCP and DNS lookup.
// 2011-07-05 <jc@wippler.nl> 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 <EtherCard_STM.h>
#include <SPI.h>
#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);
}
}

View File

@ -0,0 +1,76 @@
// This demo does web requests via DNS lookup, using a fixed gateway.
// 2010-11-27 <jc@wippler.nl> 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 <EtherCard_STM.h>
#include <SPI.h>
#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);
}
}

View File

@ -0,0 +1,72 @@
// This demo does web requests via DNS lookup, using a fixed gateway.
// 2010-11-27 <jc@wippler.nl> 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 <EtherCard_STM.h>
#include <SPI.h>
#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);
}
}

View File

@ -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 <EtherCard_STM.h>
#include <SPI.h>
#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"
"<html>"
"<head><title>"
"multipackets Test"
"</title></head>"
"<body>"
"<a href='/'>Start here</a><br>"
"<h3>packet 1</h3>"
"<p><em>"
"the first packet send "
"</em></p>"
;
const char pageB[] PROGMEM =
"<h3>packet 2</h3>"
"<p><em>"
"if you read this it mean it works"
"</em></p>"
;
const char pageC[] PROGMEM =
"<h3>packet 3</h3>"
"<p><em>"
"if you read this it mean it works"
"</em></p>"
;
const char pageD[] PROGMEM =
"<h3>packet 4</h3>"
"<p><em>"
"if you read this it mean it works"
"</em></p>"
;
const char pageE[] PROGMEM =
"<h3>packet 5</h3>"
"<p><em>"
"this is the last packet"
"</em></p>"
;
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);
}
}
}

View File

@ -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 <EtherCard_STM.h>
#include <tinyFAT.h>
#include <avr/pgmspace.h>
#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<car;i++) {
cur++;
Ethernet::buffer[cur+53]=file.buffer[i];
}
if (cur>=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<i) {
dtype[a-b-1]=name[a];
a++;
}
dtype[a-b-1]='.';
dtype[a-b]='h';
dtype[a-b+1]='e';
dtype[a-b+2]='a';
//Serial.println(dtype); // print the requested header file
if (streamfile ((char *)dtype,0)==0) {
streamfile ("txt.hea",0);
}
//Serial.println(name); // print the requested file
if (streamfile ((char *)name,TCP_FLAGS_FIN_V)==0) {
cur=0;
not_found();
}
/* uncomment this if you want to have printed the ip of the target browser
Serial.print("content send to ");
for(int i=30; i<34; i++) {
Serial.print(Ethernet::buffer[i]);
if (i<33) Serial.print(".");
}
Serial.println(" ");
*/
}

View File

@ -0,0 +1,59 @@
// Example of EtherCard usage, contributed by Will Rose, 2012-07-05.
#include <EtherCard_STM.h>
#include <SPI.h>
#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;
}

View File

@ -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 <luca@dentella.it>
// 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 <EtherCard_STM.h>
#include <SPI.h>
// 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 = "<YourDomainName>"; //e.g. myname.no-ip.info
const char noIP_auth[] PROGMEM = "<YourPassword>";
//----------------------------------------------------------
// 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);
}

View File

@ -0,0 +1,96 @@
// Ping a remote server, also uses DHCP and DNS.
// 2011-06-12 <jc@wippler.nl> 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 <SPI.h>
#include <EtherCard_STM.h>
// 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);
}
}

View File

@ -0,0 +1,72 @@
// This is a demo of the RBBB running as webserver with the Ether Card
// 2010-05-28 <jc@wippler.nl> 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 <EtherCard_STM.h>
#include <SPI.h>
// 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"
"<meta http-equiv='refresh' content='1'/>"
"<title>RBBB server</title>"
"<h1>$D$D:$D$D:$D$D</h1>"),
h/10, h%10, m/10, m%10, s/10, s%10);
return bfill.position();
}
void loop () {
if (ether.packetLoop(ether.packetReceive())){ // check if valid tcp data is received
ether.httpServerReply(homePage()); // send web page data
}
}

View File

@ -0,0 +1,119 @@
// Test the offloaded RAM stash mechanism.
// 2011-07-10 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php
#include <EtherCard_STM.h>
#include <SPI.h>
// 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("<XYZ>");
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());
}

View File

@ -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 <SPI.h>
#include <EtherCard_STM.h>
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 () {}

View File

@ -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 <SPI.h>
#include <EtherCard_STM.h>
// 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);
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}
}
}

View File

@ -0,0 +1,40 @@
#include <SPI.h>
#include <EtherCard_STM.h>
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 );
}
}

View File

@ -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 <cybexsoft@hotmail.com>
//*************************************************************
//
// IT DOESN'T WORK ON STM32 YET (IS NOT PORTED YET).
//
//*************************************************************
#include <EtherCard_STM.h>
#include <IPAddress.h>
#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();
}
*/

View File

@ -0,0 +1,69 @@
// Demo using DHCP and DNS to perform a web client request.
// 2011-06-08 <jc@wippler.nl> 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 <SPI.h>
#include <EtherCard_STM.h>
// 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);
}
}

View File

@ -0,0 +1,158 @@
// Simple demo for feeding some random data to Pachube.
// 2011-07-08 <jc@wippler.nl> 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 <EtherCard_STM.h>
#include <SPI.h>
// 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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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 <jc@wippler.nl>
//
//-----------------------------------------------------------------
// 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 <EtherCard_STM.h>
#include <stdarg.h>
//#include <avr/eeprom.h>
//#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;
}

View File

@ -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 <jc@wippler.nl>
//
//-----------------------------------------------------------------
// 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.h> // Arduino 1.0
#define WRITE_RESULT size_t
#define WRITE_RETURN return 1;
#else
#include <WProgram.h> // Arduino 0022
#define WRITE_RESULT void
#define WRITE_RETURN
#endif
#include <avr/pgmspace.h>
#include "enc28j60.h"
#include "net.h"
/** 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 <i>uint8_t*</i> Pointer to start of buffer
*/
uint8_t* buffer () const { return start; }
/** @brief Get cursor position
* @return <i>uint16_t</i> Cursor postion
*/
uint16_t position () const { return ptr - start; }
/** @brief Write one byte to buffer
* @param v Byte to add to buffer
*/
virtual WRITE_RESULT write (uint8_t v) { *ptr++ = v; WRITE_RETURN }
};
/** This class provides the main interface to a ENC28J60 based network interface card and is the class most users will use.
* @note All TCP/IP client (outgoing) connections are made from source port in range 2816-3071. Do not use these source ports for other purposes.
*/
class EtherCard : public Ethernet {
public:
static uint8_t mymac[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 <i>uint8_t</i> Firmware version or zero on failure.
*/
static uint8_t begin (const uint16_t size, const uint8_t* macaddr,
uint8_t csPin =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 <i>bool</i> Returns true on success - actually always true
*/
static bool staticSetup (const uint8_t* my_ip,
const uint8_t* gw_ip = 0,
const uint8_t* dns_ip = 0,
const uint8_t* mask = 0);
// tcpip.cpp
/** @brief Sends a UDP packet to the IP address of last processed received packet
* @param data Pointer to data payload
* @param len Size of data payload (max 220)
* @param port Source IP port
*/
static void makeUdpReply (const char *data, uint8_t len, uint16_t port);
/** @brief Parse received data
* @param plen Size of data to parse (e.g. return value of packetReceive()).
* @return <i>uint16_t</i> Offset of TCP payload data in data buffer or zero if packet processed
* @note Data buffer is shared by receive and transmit functions
* @note Only handles ARP and IP
*/
static uint16_t packetLoop (uint16_t plen);
/** @brief Accept a TCP/IP connection
* @param port IP port to accept on - do nothing if wrong port
* @param plen Number of bytes in packet
* @return <i>uint16_t</i> Offset within packet of TCP payload. Zero for no data.
*/
static uint16_t accept (uint16_t port, uint16_t plen);
/** @brief Send a response to a HTTP request
* @param dlen Size of the HTTP (TCP) payload
*/
static void httpServerReply (uint16_t dlen);
/** @brief Send a response to a HTTP request
* @param dlen Size of the HTTP (TCP) payload
* @param flags TCP flags
*/
static void httpServerReply_with_flags (uint16_t dlen , uint8_t flags);
/** @brief Acknowledge TCP message
* @todo Is this / should this be private?
*/
static void httpServerReplyAck ();
/** @brief Set the gateway address
* @param gwipaddr Gateway address (4 bytes)
*/
static void setGwIp (const uint8_t *gwipaddr);
/** @brief Updates the broadcast address based on current IP address and subnet mask
*/
static void updateBroadcastAddress();
/** @brief Check if got gateway hardware address (ARP lookup)
* @return <i>unit8_t</i> True if gateway found
*/
static uint8_t clientWaitingGw ();
/** @brief Check if got gateway DNS address (ARP lookup)
* @return <i>unit8_t</i> True if DNS found
*/
static uint8_t clientWaitingDns ();
/** @brief Prepare a TCP request
* @param result_cb Pointer to callback function that handles TCP result
* @param datafill_cb Pointer to callback function that handles TCP data payload
* @param port Remote TCP/IP port to connect to
* @return <i>unit8_t</i> ID of TCP/IP session (0-7)
* @note Return value provides id of the request to allow up to 7 concurrent requests
*/
static uint8_t clientTcpReq (uint8_t (*result_cb)(uint8_t,uint8_t,uint16_t,uint16_t),
uint16_t (*datafill_cb)(uint8_t),uint16_t port);
/** @brief Prepare HTTP request
* @param urlbuf Pointer to c-string URL folder
* @param urlbuf_varpart Pointer to c-string URL file
* @param hoststr Pointer to c-string hostname
* @param additionalheaderline Pointer to c-string with additional HTTP header info
* @param callback Pointer to callback function to handle response
* @note Request sent in main packetloop
*/
static void browseUrl (const char *urlbuf, const char *urlbuf_varpart,
const char *hoststr, const char *additionalheaderline,
void (*callback)(uint8_t,uint16_t,uint16_t));
/** @brief Prepare HTTP request
* @param urlbuf Pointer to c-string URL folder
* @param urlbuf_varpart Pointer to c-string URL file
* @param hoststr Pointer to c-string hostname
* @param callback Pointer to callback function to handle response
* @note Request sent in main packetloop
*/
static void browseUrl (const char *urlbuf, const char *urlbuf_varpart,
const char *hoststr,
void (*callback)(uint8_t,uint16_t,uint16_t));
/** @brief Prepare HTTP post message
* @param urlbuf Pointer to c-string URL folder
* @param hoststr Pointer to c-string hostname
* @param additionalheaderline Pointer to c-string with additional HTTP header info
* @param postval Pointer to c-string HTML Post value
* @param callback Pointer to callback function to handle response
* @note Request sent in main packetloop
*/
static void httpPost (const char *urlbuf, const char *hoststr,
const char *additionalheaderline, const char *postval,
void (*callback)(uint8_t,uint16_t,uint16_t));
/** @brief Send NTP request
* @param ntpip IP address of NTP server
* @param srcport IP port to send from
*/
static void ntpRequest (uint8_t *ntpip,uint8_t srcport);
/** @brief Process network time protocol response
* @param time Pointer to integer to hold result
* @param dstport_l Destination port to expect response. Set to zero to accept on any port
* @return <i>uint8_t</i> True (1) on success
*/
static uint8_t ntpProcessAnswer (uint32_t *time, uint8_t dstport_l);
/** @brief Prepare a UDP message for transmission
* @param sport Source port
* @param dip Pointer to 4 byte destination IP address
* @param dport Destination port
*/
static void udpPrepare (uint16_t sport, const uint8_t *dip, uint16_t dport);
/** @brief Transmit UDP packet
* @param len Size of payload
*/
static void udpTransmit (uint16_t len);
/** @brief Sends a UDP packet
* @param data Pointer to data
* @param len Size of payload (maximum 220 octets / bytes)
* @param sport Source port
* @param dip Pointer to 4 byte destination IP address
* @param dport Destination port
*/
static void sendUdp (const char *data, uint8_t len, uint16_t sport,
const uint8_t *dip, uint16_t dport);
/** @brief Resister the function to handle ping events
* @param cb Pointer to function
*/
static void registerPingCallback (void (*cb)(uint8_t*));
/** @brief Send ping
* @param destip Ponter to 4 byte destination IP address
*/
static void clientIcmpRequest (const uint8_t *destip);
/** @brief Check for ping response
* @param ip_monitoredhost Pointer to 4 byte IP address of host to check
* @return <i>uint8_t</i> True (1) if ping response from specified host
*/
static uint8_t packetLoopIcmpCheckReply (const uint8_t *ip_monitoredhost);
/** @brief Send a wake on lan message
* @param wolmac Pointer to 6 byte hardware (MAC) address of host to send message to
*/
static void sendWol (uint8_t *wolmac);
// new stash-based API
/** @brief Send TCP request
*/
static uint8_t tcpSend ();
/** @brief Get TCP reply
* @return <i>char*</i> Pointer to TCP reply payload. NULL if no data.
*/
static const char* tcpReply (uint8_t fd);
/** @brief Configure TCP connections to be persistent or not
* @param persist True to maintain TCP connection. False to finish TCP connection after first packet.
*/
static void persistTcpConnection(bool persist);
//udpserver.cpp
/** @brief Register function to handle incomint UDP events
* @param callback Function to handle event
* @param port Port to listen on
*/
static void udpServerListenOnPort(UdpServerCallback callback, uint16_t port);
/** @brief Pause listing on UDP port
* @brief port Port to pause
*/
static void udpServerPauseListenOnPort(uint16_t port);
/** @brief Resume listing on UDP port
* @brief port Port to pause
*/
static void udpServerResumeListenOnPort(uint16_t port);
/** @brief Check if UDP server is listening on any ports
* @return <i>bool</i> True if listening on any ports
*/
static bool udpServerListening(); //called by tcpip, in packetLoop
/** @brief Passes packet to UDP Server
* @param len Not used
* @return <i>bool</i> True if packet processed
*/
static bool udpServerHasProcessedPacket(uint16_t len); //called by tcpip, in packetLoop
// dhcp.cpp
/** @brief Update DHCP state
* @param len Length of received data packet
*/
static void DhcpStateMachine(uint16_t len);
/** @brief Not implemented
* @todo Implement dhcpStartTime or remove declaration
*/
static uint32_t dhcpStartTime ();
/** @brief Not implemented
* @todo Implement dhcpLeaseTime or remove declaration
*/
static uint32_t dhcpLeaseTime ();
/** @brief Not implemented
* @todo Implement dhcpLease or remove declaration
*/
static bool dhcpLease ();
/** @brief Configure network interface with DHCP
* @return <i>bool</i> True if DHCP successful
* @note Blocks until DHCP complete or timeout after 60 seconds
*/
static bool dhcpSetup (const char *hname = NULL, bool fromRam =false);
/** @brief Register a callback for a specific DHCP option number
* @param option The option number to request from the DHCP server
* @param callback The function to be call when the option is received
*/
static void dhcpAddOptionCallback(uint8_t option, DhcpOptionCallback callback);
// dns.cpp
/** @brief Perform DNS lookup
* @param name Host name to lookup
* @param fromRam Set true to look up cached name. Default = false
* @return <i>bool</i> True on success.
* @note Result is stored in <i>hisip</i> member
*/
static bool dnsLookup (const char* name, bool fromRam =false);
// webutil.cpp
/** @brief Copies an IP address
* @param dst Pointer to the 4 byte destination
* @param src Pointer to the 4 byte source
* @note There is no check of source or destination size. Ensure both are 4 bytes
*/
static void copyIp (uint8_t *dst, const uint8_t *src);
/** @brief Copies a hardware address
* @param dst Pointer to the 6 byte destination
* @param src Pointer to the 6 byte destination
* @note There is no check of source or destination size. Ensure both are 6 bytes
*/
static void copyMac (uint8_t *dst, const uint8_t *src);
/** @brief Output to serial port in dotted decimal IP format
* @param buf Pointer to 4 byte IP address
* @note There is no check of source or destination size. Ensure both are 4 bytes
*/
static void printIp (const uint8_t *buf);
/** @brief Output message and IP address to serial port in dotted decimal IP format
* @param msg Pointer to null terminated string
* @param buf Pointer to 4 byte IP address
* @note There is no check of source or destination size. Ensure both are 4 bytes
*/
static void printIp (const char* msg, const uint8_t *buf);
/** @brief Output Flash String Helper and IP address to serial port in dotted decimal IP format
* @param ifsh Pointer to Flash String Helper
* @param buf Pointer to 4 byte IP address
* @note There is no check of source or destination size. Ensure both are 4 bytes
* @todo What is a FlashStringHelper?
*/
static void printIp (const __FlashStringHelper *ifsh, const uint8_t *buf);
/** @brief Search for a string of the form key=value in a string that looks like q?xyz=abc&uvw=defgh HTTP/1.1\\r\\n
* @param str Pointer to the null terminated string to search
* @param strbuf Pointer to buffer to hold null terminated result string
* @param maxlen Maximum length of result
* @param key Pointer to null terminated string holding the key to search for
* @return <i>unit_t</i> Length of the value. 0 if not found
* @note Ensure strbuf has memory allocated of at least maxlen + 1 (to accomodate result plus terminating null)
*/
static uint8_t findKeyVal(const char *str,char *strbuf,
uint8_t maxlen, const char *key);
/** @brief Decode a URL string e.g "hello%20joe" or "hello+joe" becomes "hello joe"
* @param urlbuf Pointer to the null terminated URL
* @note urlbuf is modified
*/
static void urlDecode(char *urlbuf);
/** @brief Encode a URL, replacing illegal charaters like ' '
* @param str Pointer to the null terminated string to encode
* @param urlbuf Pointer to a buffer to contain the null terminated encoded URL
* @note There must be enough space in urlbuf. In the worst case that is 3 times the length of str
*/
static void urlEncode(char *str,char *urlbuf);
/** @brief Convert an IP address from dotted decimal formated string to 4 bytes
* @param bytestr Pointer to the 4 byte array to store IP address
* @param str Pointer to string to parse
* @return <i>uint8_t</i> 0 on success
*/
static uint8_t parseIp(uint8_t *bytestr,char *str);
/** @brief Convert a byte array to a human readable display string
* @param resultstr Pointer to a buffer to hold the resulting null terminated string
* @param bytestr Pointer to the byte array containing the address to convert
* @param len Length of the array (4 for IP address, 6 for hardware (MAC) address)
* @param separator Delimiter character (typically '.' for IP address and ':' for hardware (MAC) address)
* @param base Base for numerical representation (typically 10 for IP address and 16 for hardware (MAC) address
*/
static void makeNetStr(char *resultstr,uint8_t *bytestr,uint8_t len,
char separator,uint8_t base);
};
extern EtherCard ether; //!< Global presentation of EtherCard class
#endif

View File

@ -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;
}
}

View File

@ -0,0 +1,117 @@
// DNS look-up functions based on the udp client
// Author: Guido Socher
// Copyright: GPL V2
//
// 2010-05-20 <jc@wippler.nl>
#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 <i>bool</i> 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;
}

View File

@ -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 <jc@wippler.nl>
//
//-----------------------------------------------------------------
// Ported to STM32F103 by Vassilis Serasidis on 21 May 2015
// Home: http://www.serasidis.gr
// email: avrsite@yahoo.gr
//-----------------------------------------------------------------
#if ARDUINO >= 100
#include <Arduino.h> // Arduino 1.0
#else
#include <Wprogram.h> // Arduino 0022
#endif
#include "enc28j60.h"
#include <SPI.h> // 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<<SPIF)))
//}
static byte readOp (byte op, byte address) {
enableChip();
byte result;
//xferSPI(op | (address & ADDR_MASK));
//xferSPI(0x00);
//if (address & 0x80)
// xferSPI(0x00);
//byte result = SPDR;
SPI.transfer(op | (address & ADDR_MASK));
result = SPI.transfer(0x00);
if (address & 0x80)
result = SPI.transfer(0x00);
disableChip();
return result;
}
static void writeOp (byte op, byte address, byte data) {
enableChip();
//xferSPI(op | (address & ADDR_MASK));
//xferSPI(data);
SPI.transfer(op | (address & ADDR_MASK));
SPI.transfer(data);
disableChip();
}
static void readBuf(uint16_t len, byte* data) {
enableChip();
//xferSPI(ENC28J60_READ_BUF_MEM);
SPI.transfer(ENC28J60_READ_BUF_MEM);
while (len--) {
//xferSPI(0x00);
//*data++ = SPDR;
*data++ = SPI.transfer(0x00);
}
disableChip();
}
static void writeBuf(uint16_t len, const byte* data) {
enableChip();
//xferSPI(ENC28J60_WRITE_BUF_MEM);
SPI.transfer(ENC28J60_WRITE_BUF_MEM);
//while (len--)
// xferSPI(*data++);
while (len--)
SPI.transfer(*data++);
disableChip();
}
static void SetBank (byte address) {
if ((address & BANK_MASK) != Enc28j60Bank) {
writeOp(ENC28J60_BIT_FIELD_CLR, ECON1, ECON1_BSEL1|ECON1_BSEL0);
Enc28j60Bank = address & BANK_MASK;
writeOp(ENC28J60_BIT_FIELD_SET, ECON1, Enc28j60Bank>>5);
}
}
static byte readRegByte (byte address) {
SetBank(address);
return readOp(ENC28J60_READ_CTRL_REG, address);
}
static uint16_t readReg(byte address) {
return readRegByte(address) + (readRegByte(address+1) << 8);
}
static void writeRegByte (byte address, byte data) {
SetBank(address);
writeOp(ENC28J60_WRITE_CTRL_REG, address, data);
}
static void writeReg(byte address, uint16_t data) {
writeRegByte(address, data);
writeRegByte(address + 1, data >> 8);
}
static uint16_t readPhyByte (byte address) {
writeRegByte(MIREGADR, address);
writeRegByte(MICMD, MICMD_MIIRD);
while (readRegByte(MISTAT) & MISTAT_BUSY)
;
writeRegByte(MICMD, 0x00);
return readRegByte(MIRD+1);
}
static void writePhy (byte address, uint16_t data) {
writeRegByte(MIREGADR, address);
writeReg(MIWR, data);
while (readRegByte(MISTAT) & MISTAT_BUSY)
;
}
byte ENC28J60::initialize (uint16_t size, const byte* macaddr, byte csPin) {
bufferSize = size;
//if (bitRead(SPCR, SPE) == 0)
initSPI();
selectPin = csPin;
pinMode(selectPin, OUTPUT);
disableChip();
writeOp(ENC28J60_SOFT_RESET, 0, ENC28J60_SOFT_RESET);
delay(2); // errata B7/2
while (!readOp(ENC28J60_READ_CTRL_REG, ESTAT) & ESTAT_CLKRDY)
;
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;
}

View File

@ -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 <jc@wippler.nl>
//
//-----------------------------------------------------------------
// 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 <i>uint8_t</i> ENC28J60 firmware version or zero on failure.
*/
static uint8_t initialize (const uint16_t size, const uint8_t* macaddr,
uint8_t csPin = PA8);
/** @brief Check if network link is connected
* @return <i>bool</i> True if link is up
*/
static bool isLinkUp ();
/** @brief Sends data to network interface
* @param len Size of data to send
* @note Data buffer is shared by recieve and transmit functions
*/
static void packetSend (uint16_t len);
/** @brief Copy recieved packets to data buffer
* @return <i>uint16_t</i> Size of recieved data
* @note Data buffer is shared by recieve and transmit functions
*/
static uint16_t packetReceive ();
/** @brief Copy data from ENC28J60 memory
* @param page Data page of memory
* @param data Pointer to buffer to copy data to
*/
static void copyout (uint8_t page, const uint8_t* data);
/** @brief Copy data to ENC28J60 memory
* @param page Data page of memory
* @param data Pointer to buffer to copy data from
*/
static void copyin (uint8_t page, uint8_t* data);
/** @brief Get single byte of data from ENC28J60 memory
* @param page Data page of memory
* @param off Offset of data within page
* @return Data value
*/
static uint8_t peekin (uint8_t page, uint8_t off);
/** @brief Put ENC28J60 in sleep mode
*/
static void powerDown(); // contrib by Alex M.
/** @brief Wake ENC28J60 from sleep mode
*/
static void powerUp(); // contrib by Alex M.
/** @brief Enable reception of broadcast messages
* @param temporary Set true to temporarily enable broadcast
* @note This will increase load on recieved data handling
*/
static void enableBroadcast(bool temporary = false);
/** @brief Disable reception of broadcast messages
* @param temporary Set true to only disable if temporarily enabled
* @note This will reduce load on recieved data handling
*/
static void disableBroadcast(bool temporary = false);
/** @brief Enables reception of mulitcast messages
* @note This will increase load on recieved data handling
*/
static void enableMulticast ();
/** @brief Disable reception of mulitcast messages
* @note This will reduce load on recieved data handling
*/
static void disableMulticast();
/** @brief Reset and fully initialise ENC28J60
* @param csPin Arduino pin used for chip select (enable SPI bus)
* @return <i>uint8_t</i> 0 on failure
*/
static uint8_t doBIST(uint8_t csPin = PA8);
};
typedef ENC28J60 Ethernet; //!< Define alias Ethernet for ENC28J60
#endif

View File

@ -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 <jc@wippler.nl>
// 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

View File

@ -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 <jc@wippler.nl>
#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;
}

View File

@ -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;
}

View File

@ -0,0 +1,200 @@
// Some common utilities needed for IP and web applications
// Author: Guido Socher
// Copyright: GPL V2
//
// 2010-05-20 <jc@wippler.nl>
#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<maxlen-1){
*strbuf=*str;
i++;
str++;
strbuf++;
}
*strbuf='\0';
}
// return the length of the value
return(i);
}
// convert a single hex digit character to its integer value
unsigned char h2int(char c)
{
if (c >= '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<len){
//itoa((int)bytestr[i],&resultstr[j],base);
// search end of str:
while(resultstr[j]){j++;}
resultstr[j]=separator;
j++;
i++;
}
j--;
resultstr[j]='\0';
}
// end of webutil.c

View File

@ -91,8 +91,8 @@ recipe.S.o.pattern="{compiler.path}{compiler.c.cmd}" {compiler.S.flags} -mcpu={b
recipe.ar.pattern="{compiler.path}{compiler.ar.cmd}" {compiler.ar.flags} {compiler.ar.extra_flags} "{build.path}/{archive_file}" "{object_file}"
## Combine gc-sections, archives, and objects
#recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} -mcpu={build.mcu} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" "-L{build.path}" -lm -lgcc -mthumb -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--warn-unresolved-symbols -Wl,--start-group {object_files} "{build.path}/{archive_file}" -Wl,--end-group
recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} -mcpu={build.mcu} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" "-L{build.path}" -lm -lgcc -mthumb -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--warn-unresolved-symbols -Wl,--start-group {object_files} -Wl,--whole-archive "{build.path}/{archive_file}" -Wl,--no-whole-archive -Wl,--end-group
recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} -mcpu={build.mcu} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" "-L{build.path}" -lm -lgcc -mthumb -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--warn-unresolved-symbols -Wl,--start-group {object_files} "{build.path}/{archive_file}" -Wl,--end-group
#recipe.c.combine.pattern="{compiler.path}{compiler.c.elf.cmd}" {compiler.c.elf.flags} -mcpu={build.mcu} "-T{build.variant.path}/{build.ldscript}" "-Wl,-Map,{build.path}/{build.project_name}.map" {compiler.c.elf.extra_flags} -o "{build.path}/{build.project_name}.elf" "-L{build.path}" -lm -lgcc -mthumb -Wl,--cref -Wl,--check-sections -Wl,--gc-sections -Wl,--unresolved-symbols=report-all -Wl,--warn-common -Wl,--warn-section-align -Wl,--warn-unresolved-symbols -Wl,--start-group {object_files} -Wl,--whole-archive "{build.path}/{archive_file}" -Wl,--no-whole-archive -Wl,--end-group
## Create eeprom
recipe.objcopy.eep.pattern=