3
0
Fork 0
xnode/lib-ext/ethercard.git/examples/noipClient/noipClient.ino

280 lines
8.9 KiB
Arduino
Raw Permalink Normal View History

2022-11-13 00:46:38 +00:00
// 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/
#include <EtherCard.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[] = {0xDD,0xDD,0xDD,0x00,0x00,0x01};
// Insert your hostname and authentication string
const char noIP_host[] PROGMEM = "myhost.no-ip.info";
const char noIP_auth[] PROGMEM = "XXXXXXXXXX";
// 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, 10))
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);
}