From 3db74ab66511717bac0794f8be25d8b46a9082ad Mon Sep 17 00:00:00 2001 From: dannybackx Date: Sun, 30 Oct 2016 09:45:57 +0100 Subject: [PATCH 01/14] These changes are not functional in nature : meant to compile in my environment. --- ELClient/ELClientSocket.cpp | 2 +- ELClient/ELClientWebServer.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/ELClient/ELClientSocket.cpp b/ELClient/ELClientSocket.cpp index b5a8c7f..b255e3b 100644 --- a/ELClient/ELClientSocket.cpp +++ b/ELClient/ELClientSocket.cpp @@ -67,7 +67,7 @@ void ELClientSocket::socketCallback(void *res) int argLen = resp->argLen(); Serial.print(" data length: "+String(argLen)); #endif - resp->popArgPtr(&_data); + resp->popArgPtr((void **)&_data); #ifdef DEBUG_EN _data[_len] = '\0'; Serial.print(" data: "+String(_data)); diff --git a/ELClient/ELClientWebServer.cpp b/ELClient/ELClientWebServer.cpp index 48106ba..568c235 100644 --- a/ELClient/ELClientWebServer.cpp +++ b/ELClient/ELClientWebServer.cpp @@ -21,7 +21,7 @@ typedef enum } WebValueType; -static ELClientWebServer * ELClientWebServer::instance = 0; +ELClientWebServer * ELClientWebServer::instance = 0; /*! ELClientWebServer(ELClient* elClient) @@ -48,7 +48,7 @@ ELClientWebServer::ELClientWebServer(ELClient* elc) :_elc(elc),handlers(0), arg_ } // packet handler for web-server -static void ELClientWebServer::webServerPacketHandler(void * response) +void ELClientWebServer::webServerPacketHandler(void * response) { ELClientWebServer::getInstance()->processResponse((ELClientResponse*)response); } @@ -267,7 +267,7 @@ void ELClientWebServer::setup() // register here to the web callback // periodic reregistration is required in case of ESP8266 reset _elc->Request(CMD_WEB_SETUP, 0, 1); - uint32_t cb = &webServerCb; + uint32_t cb = (uint32_t) &webServerCb; _elc->Request(&cb, 4); _elc->Request(); } @@ -282,7 +282,7 @@ void ELClientWebServer::processResponse(ELClientResponse *response) response->popArg(&remote_port, 2); // remote port char * url; - int urlLen = response->popArgPtr(&url); + int urlLen = response->popArgPtr((void **)&url); struct URL_HANDLER *hnd = handlers; while( hnd != 0 ) @@ -310,7 +310,7 @@ void ELClientWebServer::processResponse(ELClientResponse *response) case WS_BUTTON: // invoked when a button pressed { char * idPtr; - int idLen = response->popArgPtr(&idPtr); + int idLen = response->popArgPtr((void **)&idPtr); // add terminating 0 char id[idLen+1]; @@ -327,7 +327,7 @@ void ELClientWebServer::processResponse(ELClientResponse *response) while( cnt < response->argc() ) { char * idPtr; - int idLen = response->popArgPtr(&idPtr); + int idLen = response->popArgPtr((void **)&idPtr); int nameLen = strlen(idPtr+1); int valueLen = idLen - nameLen -2; @@ -358,9 +358,9 @@ void ELClientWebServer::processResponse(ELClientResponse *response) _elc->Request((uint8_t *)&remote_port, 2); // send remote port if( reason == WS_LOAD ) - hnd->loadCb( hnd->URL.c_str() ); + hnd->loadCb( (char *)hnd->URL.c_str() ); else - hnd->refreshCb( hnd->URL.c_str() ); + hnd->refreshCb( (char *)hnd->URL.c_str() ); _elc->Request((uint8_t *)NULL, 0); // end indicator _elc->Request(); // finish packet From 8000e3ec59af5d27893b41eecaa0fea8736db532 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Sun, 30 Oct 2016 09:46:51 +0100 Subject: [PATCH 02/14] Provide additional functionality to query the ESP's IP address info. --- ELClient/ELClient.h | 2 ++ ELClient/ELClientCmd.cpp | 53 ++++++++++++++++++++++++++++++++++++++++ ELClient/ELClientCmd.h | 4 +++ 3 files changed, 59 insertions(+) diff --git a/ELClient/ELClient.h b/ELClient/ELClient.h index 5a9c73d..b8cbaf0 100644 --- a/ELClient/ELClient.h +++ b/ELClient/ELClient.h @@ -40,6 +40,8 @@ typedef enum { CMD_SOCKET_SETUP = 40, /**< Setup socket connection */ CMD_SOCKET_SEND, /**< Send socket packet */ + + CMD_GET_WIFI_INFO = 50, /**< Get several bits of IP address info */ } CmdName; /**< Enumeration of commands supported by esp-link, this needs to match the definition in esp-link! */ enum WIFI_STATUS { diff --git a/ELClient/ELClientCmd.cpp b/ELClient/ELClientCmd.cpp index 87cd03d..3544f6d 100644 --- a/ELClient/ELClientCmd.cpp +++ b/ELClient/ELClientCmd.cpp @@ -36,3 +36,56 @@ uint32_t ELClientCmd::GetTime() { return pkt ? pkt->value : 0; } +/*! GetWifiInfo() +@brief Get IP address info from ESP +@details ip address, network mask, gateway ip +@return Three parameters allow returning the values looked up, specify pointer to uint32_t in them. +@par Example +@code + uint32_t ip, nm, gw; + cmd.GetWifiInfo(&ip, &nm, &gw); +@endcode +*/ +void ELClientCmd::GetWifiInfo(uint32_t *ptr_ip, uint32_t *ptr_netmask, uint32_t *ptr_gateway) { + clientCmdCb.attach(this, &ELClientCmd::wifiInfoCmdCallback); + _elc->Request(CMD_GET_WIFI_INFO, (uint32_t)&clientCmdCb, 0); + _elc->Request(); + + ELClientPacket *pkt = _elc->WaitReturn(); + if (_elc->_debugEn) { + _elc->_debug->println("Returning ..."); + } + if (ptr_ip) + *ptr_ip = ip; + if (ptr_netmask) + *ptr_netmask = netmask; + if (ptr_gateway) + *ptr_gateway = gateway; +} + +/*! wifiInfoCmdCallback() +@brief Helper function to decode the three bits of information from the packet +@details See GetWifiInfo() +@return none +*/ +void ELClientCmd::wifiInfoCmdCallback(void *res) { + ELClientResponse *resp = (ELClientResponse *)res; + + resp->popArg(&ip, sizeof(ip)); + if (_elc->_debugEn) { + _elc->_debug->print("IP "); + _elc->_debug->println(ip); + } + + resp->popArg(&netmask, sizeof(netmask)); + if (_elc->_debugEn) { + _elc->_debug->print("NM "); + _elc->_debug->println(netmask); + } + + resp->popArg(&gateway, sizeof(gateway)); + if (_elc->_debugEn) { + _elc->_debug->print("GW "); + _elc->_debug->println(gateway); + } +} diff --git a/ELClient/ELClientCmd.h b/ELClient/ELClientCmd.h index eb7eec5..39cccd8 100644 --- a/ELClient/ELClientCmd.h +++ b/ELClient/ELClientCmd.h @@ -17,8 +17,12 @@ class ELClientCmd { ELClientCmd(ELClient* elc); // Get the current time in seconds since the epoch, 0 if the time is unknown uint32_t GetTime(); + void GetWifiInfo(uint32_t *, uint32_t *, uint32_t *); private: ELClient* _elc; /**< ELClient instance */ + FP clientCmdCb; /**< Pointer to external callback function */ + void wifiInfoCmdCallback(void *resp); + uint32_t ip, netmask, gateway; }; #endif From 9119f0e44ca8531f7f261ee24539e408c9b0ff65 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Tue, 1 Nov 2016 08:09:53 +0100 Subject: [PATCH 03/14] Add test --- ELClient/examples/get-info/get-info.ino | 87 +++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 ELClient/examples/get-info/get-info.ino diff --git a/ELClient/examples/get-info/get-info.ino b/ELClient/examples/get-info/get-info.ino new file mode 100644 index 0000000..b5136bb --- /dev/null +++ b/ELClient/examples/get-info/get-info.ino @@ -0,0 +1,87 @@ +#include +#include +#include + +// Forward declarations +void wifiCb(void *response); + +// Initialize a connection to esp-link using the normal hardware serial port both for +// SLIP and for debug messages. +ELClient esp(&Serial, &Serial); +ELClientCmd cmd(&esp); + +boolean wifiConnected = false; + +void setup() { + Serial.begin(9600); // Match the esplink config (Arduino serial <-> ESP) + delay(3000); + Serial.println("ELClient test : show IP information"); + + esp.wifiCb.attach(wifiCb); // wifi status change callback, optional (delete if not desired) + bool ok; + do { + ok = esp.Sync(); // sync up with esp-link, blocks for up to 2 seconds + if (!ok) Serial.println("EL-Client sync failed!"); + } while(!ok); + Serial.println("EL-Client synced!"); + + uint32_t ip, nm, gw; + cmd.GetWifiInfo(&ip, &nm, &gw); + + // FIX ME in reverse + Serial.print("IP Address "); + Serial.print((ip & 0x000000FF)); + Serial.print("."); + Serial.print((ip & 0x0000FF00) >> 8); + Serial.print("."); + Serial.print((ip & 0x00FF0000) >> 16); + Serial.print("."); + Serial.print((ip & 0xFF000000) >> 24); + Serial.println(""); + + // FIX ME in reverse + Serial.print("Network mask "); + Serial.print((nm & 0x000000FF)); + Serial.print("."); + Serial.print((nm & 0x0000FF00) >> 8); + Serial.print("."); + Serial.print((nm & 0x00FF0000) >> 16); + Serial.print("."); + Serial.print((nm & 0xFF000000) >> 24); + Serial.println(""); + + // FIX ME in reverse + Serial.print("IP Gateway "); + Serial.print((gw & 0x000000FF)); + Serial.print("."); + Serial.print((gw & 0x0000FF00) >> 8); + Serial.print("."); + Serial.print((gw & 0x00FF0000) >> 16); + Serial.print("."); + Serial.print((gw & 0xFF000000) >> 24); + Serial.println(""); +} + +void loop() { + delay(100); +} + +// Callback made from esp-link to notify of wifi status changes +// Here we print something out and set a global flag +void wifiCb(void *response) { + ELClientResponse *res = (ELClientResponse*)response; + if (res->argc() == 1) { + uint8_t status; + res->popArg(&status, 1); + + if(status == STATION_GOT_IP) { + Serial.println("WIFI CONNECTED"); + wifiConnected = true; + } else { + Serial.print("WIFI NOT READY: "); + Serial.println(status); + wifiConnected = false; + } + } +} + From d0c957c2d746970bbb2219b56f7e9572bcd3aac7 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Tue, 1 Nov 2016 09:03:49 +0100 Subject: [PATCH 04/14] Add MAC, and change command as in esp-link --- ELClient/ELClient.h | 3 +-- ELClient/ELClientCmd.cpp | 6 ++++++ ELClient/ELClientCmd.h | 2 ++ ELClient/examples/get-info/get-info.ino | 12 ++++++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/ELClient/ELClient.h b/ELClient/ELClient.h index b8cbaf0..a2b0763 100644 --- a/ELClient/ELClient.h +++ b/ELClient/ELClient.h @@ -24,6 +24,7 @@ typedef enum { CMD_CB_ADD, /**< Add a custom callback */ CMD_CB_EVENTS, /**< ??? */ CMD_GET_TIME, /**< Get current time in seconds since the unix epoch */ + CMD_GET_WIFI_INFO, /**< Get several bits of IP address info */ //CMD_GET_INFO, CMD_MQTT_SETUP = 10, /**< Register callback functions */ @@ -40,8 +41,6 @@ typedef enum { CMD_SOCKET_SETUP = 40, /**< Setup socket connection */ CMD_SOCKET_SEND, /**< Send socket packet */ - - CMD_GET_WIFI_INFO = 50, /**< Get several bits of IP address info */ } CmdName; /**< Enumeration of commands supported by esp-link, this needs to match the definition in esp-link! */ enum WIFI_STATUS { diff --git a/ELClient/ELClientCmd.cpp b/ELClient/ELClientCmd.cpp index 3544f6d..9932019 100644 --- a/ELClient/ELClientCmd.cpp +++ b/ELClient/ELClientCmd.cpp @@ -88,4 +88,10 @@ void ELClientCmd::wifiInfoCmdCallback(void *res) { _elc->_debug->print("GW "); _elc->_debug->println(gateway); } + + resp->popArg(&mac, sizeof(mac)); +} + +char *ELClientCmd::getMac() { + return (char *)mac; } diff --git a/ELClient/ELClientCmd.h b/ELClient/ELClientCmd.h index 39cccd8..c018842 100644 --- a/ELClient/ELClientCmd.h +++ b/ELClient/ELClientCmd.h @@ -18,11 +18,13 @@ class ELClientCmd { // Get the current time in seconds since the epoch, 0 if the time is unknown uint32_t GetTime(); void GetWifiInfo(uint32_t *, uint32_t *, uint32_t *); + char *getMac(); private: ELClient* _elc; /**< ELClient instance */ FP clientCmdCb; /**< Pointer to external callback function */ void wifiInfoCmdCallback(void *resp); uint32_t ip, netmask, gateway; + uint8_t mac[6]; }; #endif diff --git a/ELClient/examples/get-info/get-info.ino b/ELClient/examples/get-info/get-info.ino index b5136bb..6d5236f 100644 --- a/ELClient/examples/get-info/get-info.ino +++ b/ELClient/examples/get-info/get-info.ino @@ -60,6 +60,18 @@ void setup() { Serial.print("."); Serial.print((gw & 0xFF000000) >> 24); Serial.println(""); + + char *mac = cmd.getMac(); + Serial.print("MAC Address : "); + for (int i=0; i<6; i++) { + char buf[4]; + if (i < 5) + sprintf(buf, "%02X:", 0xFF & mac[i]); + else + sprintf(buf, "%02X", 0xFF & mac[i]); + Serial.print(buf); + } + Serial.println(""); } void loop() { From 1455e5b094bccced2e20f99c1a088ab83a74a428 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Sun, 12 Mar 2017 12:05:45 +0100 Subject: [PATCH 05/14] Add code to query the MQTT client-id and various experimental bits of networking code. --- ELClient/ELClient.cpp | 2 +- ELClient/ELClient.h | 7 +++ ELClient/ELClientCmd.cpp | 66 +++++++++++++++++++++++++ ELClient/ELClientCmd.h | 10 ++++ ELClient/examples/get-info/get-info.ino | 6 +++ 5 files changed, 90 insertions(+), 1 deletion(-) diff --git a/ELClient/ELClient.cpp b/ELClient/ELClient.cpp index f688a42..c0bf057 100644 --- a/ELClient/ELClient.cpp +++ b/ELClient/ELClient.cpp @@ -403,7 +403,7 @@ _serial(serial) { */ ELClient::ELClient(Stream* serial, Stream* debug) : _debug(debug), _serial(serial) { - _debugEn = true; + // _debugEn = true; // Danny init(); } diff --git a/ELClient/ELClient.h b/ELClient/ELClient.h index a2b0763..2f49863 100644 --- a/ELClient/ELClient.h +++ b/ELClient/ELClient.h @@ -25,12 +25,14 @@ typedef enum { CMD_CB_EVENTS, /**< ??? */ CMD_GET_TIME, /**< Get current time in seconds since the unix epoch */ CMD_GET_WIFI_INFO, /**< Get several bits of IP address info */ + CMD_SET_WIFI_INFO, /**< Set several bits of IP address info */ //CMD_GET_INFO, CMD_MQTT_SETUP = 10, /**< Register callback functions */ CMD_MQTT_PUBLISH, /**< Publish MQTT topic */ CMD_MQTT_SUBSCRIBE, /**< Subscribe to MQTT topic */ CMD_MQTT_LWT, /**< Define MQTT last will */ + CMD_MQTT_GET_CLIENTID, CMD_REST_SETUP = 20, /**< Setup REST connection */ CMD_REST_REQUEST, /**< Make request to REST server */ @@ -41,6 +43,11 @@ typedef enum { CMD_SOCKET_SETUP = 40, /**< Setup socket connection */ CMD_SOCKET_SEND, /**< Send socket packet */ + + CMD_WIFI_GET_APCOUNT = 50, /* */ + CMD_WIFI_GET_APNAME, /* */ + CMD_WIFI_SELECT_SSID, + } CmdName; /**< Enumeration of commands supported by esp-link, this needs to match the definition in esp-link! */ enum WIFI_STATUS { diff --git a/ELClient/ELClientCmd.cpp b/ELClient/ELClientCmd.cpp index 9932019..829415a 100644 --- a/ELClient/ELClientCmd.cpp +++ b/ELClient/ELClientCmd.cpp @@ -92,6 +92,72 @@ void ELClientCmd::wifiInfoCmdCallback(void *res) { resp->popArg(&mac, sizeof(mac)); } +/* + * FIXME this depends on having called getWifiInfo + */ char *ELClientCmd::getMac() { return (char *)mac; } + +/* + * Query the number of Access Points scanned. + * FIXME this relies on having triggered such a scan + */ +uint32_t ELClientCmd::GetWifiApCount() { + _elc->Request(CMD_WIFI_GET_APCOUNT, 0, 0); + _elc->Request(); + + ELClientPacket *pkt = _elc->WaitReturn(); + return pkt ? pkt->value : -1; +} + +/* + * Query the SSID of a network. Range and FIXME as with the ApCount. + */ +char * ELClientCmd::GetWifiApName(int i) { + uint16_t ix = i; + + clientCmdCb.attach(this, &ELClientCmd::wifiGetApNameCallback); + _elc->Request(CMD_WIFI_GET_APNAME, (uint32_t)&clientCmdCb, 1); + _elc->Request(&ix, 2); + _elc->Request(); + + ELClientPacket *pkt = _elc->WaitReturn(); + if (_elc->_debugEn) { + _elc->_debug->println("Returning ..."); + } + + return ssid; +} + +void ELClientCmd::wifiGetApNameCallback(void *res) { + ELClientResponse *resp = (ELClientResponse *)res; + + if (ssid == 0) ssid = (char *)malloc(33); + resp->popArg(ssid, 33); + ssid[32] = '\0'; +} + +/* + * Query the MQTT clientid + */ +void ELClientCmd:: mqttGetClientIdCallback(void *res) { + ELClientResponse *resp = (ELClientResponse *)res; + + if (mqtt_clientid == 0) mqtt_clientid = (char *)malloc(33); + resp->popArg(mqtt_clientid, 32); + mqtt_clientid[32] = '\0'; +} + +char *ELClientCmd::mqttGetClientId() { + mqttCmdCb.attach(this, &ELClientCmd::mqttGetClientIdCallback); + _elc->Request(CMD_MQTT_GET_CLIENTID, (uint32_t)&mqttCmdCb, 0); + _elc->Request(); + + ELClientPacket *pkt = _elc->WaitReturn(); + if (_elc->_debugEn) { + _elc->_debug->println("Returning ..."); + } + + return mqtt_clientid; +} diff --git a/ELClient/ELClientCmd.h b/ELClient/ELClientCmd.h index c018842..be52612 100644 --- a/ELClient/ELClientCmd.h +++ b/ELClient/ELClientCmd.h @@ -18,7 +18,10 @@ class ELClientCmd { // Get the current time in seconds since the epoch, 0 if the time is unknown uint32_t GetTime(); void GetWifiInfo(uint32_t *, uint32_t *, uint32_t *); + uint32_t GetWifiApCount(); + char * GetWifiApName(int i); char *getMac(); + char *mqttGetClientId(); private: ELClient* _elc; /**< ELClient instance */ @@ -26,5 +29,12 @@ class ELClientCmd { void wifiInfoCmdCallback(void *resp); uint32_t ip, netmask, gateway; uint8_t mac[6]; + + char *ssid; + void wifiGetApNameCallback(void *); + + FP mqttCmdCb; /**< Pointer to external callback function */ + void mqttGetClientIdCallback(void *); + char *mqtt_clientid; }; #endif diff --git a/ELClient/examples/get-info/get-info.ino b/ELClient/examples/get-info/get-info.ino index 6d5236f..f6a7cca 100644 --- a/ELClient/examples/get-info/get-info.ino +++ b/ELClient/examples/get-info/get-info.ino @@ -72,6 +72,12 @@ void setup() { Serial.print(buf); } Serial.println(""); + + // Query the MQTT clientid + Serial.print("MQTT client id : "); + char *mqtt_clientid = cmd.mqttGetClientId(); + Serial.println(mqtt_clientid); + } void loop() { From bb1bf80cfa5648d941c4a1862a59637ea486199c Mon Sep 17 00:00:00 2001 From: dannybackx Date: Wed, 15 Mar 2017 17:44:58 +0100 Subject: [PATCH 06/14] Query the signal strength of a wifi network --- ELClient/ELClient.h | 1 + ELClient/ELClientCmd.cpp | 31 +++++++++++++++++++++++++++++++ ELClient/ELClientCmd.h | 4 +++- ELClient/ELClientMqtt.cpp | 2 +- 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/ELClient/ELClient.h b/ELClient/ELClient.h index 2f49863..e8e7730 100644 --- a/ELClient/ELClient.h +++ b/ELClient/ELClient.h @@ -47,6 +47,7 @@ typedef enum { CMD_WIFI_GET_APCOUNT = 50, /* */ CMD_WIFI_GET_APNAME, /* */ CMD_WIFI_SELECT_SSID, + CMD_WIFI_SIGNAL_STRENGTH, } CmdName; /**< Enumeration of commands supported by esp-link, this needs to match the definition in esp-link! */ diff --git a/ELClient/ELClientCmd.cpp b/ELClient/ELClientCmd.cpp index 829415a..9583796 100644 --- a/ELClient/ELClientCmd.cpp +++ b/ELClient/ELClientCmd.cpp @@ -63,6 +63,25 @@ void ELClientCmd::GetWifiInfo(uint32_t *ptr_ip, uint32_t *ptr_netmask, uint32_t *ptr_gateway = gateway; } +/*! SetWifiInfo() +@brief Set IP address +@details ip address, network mask, gateway ip +@return +@par Example +@code + uint32_t ip, nm, gw; + cmd.SetWifiInfo(ip, nm, gw); +@endcode +*/ +void ELClientCmd::SetWifiInfo(uint32_t ptr_ip, uint32_t ptr_netmask, uint32_t ptr_gateway) { + _elc->Request(CMD_SET_WIFI_INFO, 0, 0); + _elc->Request(); + + ELClientPacket *pkt = _elc->WaitReturn(); + // return pkt ? pkt->value : 0; +} + + /*! wifiInfoCmdCallback() @brief Helper function to decode the three bits of information from the packet @details See GetWifiInfo() @@ -161,3 +180,15 @@ char *ELClientCmd::mqttGetClientId() { return mqtt_clientid; } + +/* + * Query RSSI (signal strength) + */ +int ELClientCmd::GetRSSI(int i) { + _elc->Request(CMD_WIFI_SIGNAL_STRENGTH, 0, 1); + _elc->Request(&i, 4); + _elc->Request(); + + ELClientPacket *pkt = _elc->WaitReturn(); + return pkt ? pkt->value : 0; +} diff --git a/ELClient/ELClientCmd.h b/ELClient/ELClientCmd.h index be52612..9585cc5 100644 --- a/ELClient/ELClientCmd.h +++ b/ELClient/ELClientCmd.h @@ -18,10 +18,12 @@ class ELClientCmd { // Get the current time in seconds since the epoch, 0 if the time is unknown uint32_t GetTime(); void GetWifiInfo(uint32_t *, uint32_t *, uint32_t *); + void SetWifiInfo(uint32_t, uint32_t, uint32_t); uint32_t GetWifiApCount(); - char * GetWifiApName(int i); + char * GetWifiApName(int); char *getMac(); char *mqttGetClientId(); + int GetRSSI(int); // Current signal strength if <0, or selected network's rssi private: ELClient* _elc; /**< ELClient instance */ diff --git a/ELClient/ELClientMqtt.cpp b/ELClient/ELClientMqtt.cpp index b883f47..415c4f3 100644 --- a/ELClient/ELClientMqtt.cpp +++ b/ELClient/ELClientMqtt.cpp @@ -32,7 +32,7 @@ ELClientMqtt::ELClientMqtt(ELClient* elc) :_elc(elc) {} @endcode */ void ELClientMqtt::setup(void) { - Serial.print(F("ConnectedCB is 0x")); Serial.println((uint32_t)&connectedCb, 16); + // Serial.print(F("ConnectedCB is 0x")); Serial.println((uint32_t)&connectedCb, 16); _elc->Request(CMD_MQTT_SETUP, 0, 4); uint32_t cb = (uint32_t)&connectedCb; _elc->Request(&cb, 4); From d4747d6afa97121e2c828954db172f53fbd896c0 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Wed, 15 Mar 2017 19:40:39 +0100 Subject: [PATCH 07/14] Just a one byte parameter works better in this case. --- ELClient/ELClientCmd.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ELClient/ELClientCmd.cpp b/ELClient/ELClientCmd.cpp index 9583796..4c93678 100644 --- a/ELClient/ELClientCmd.cpp +++ b/ELClient/ELClientCmd.cpp @@ -185,8 +185,9 @@ char *ELClientCmd::mqttGetClientId() { * Query RSSI (signal strength) */ int ELClientCmd::GetRSSI(int i) { + char x = i; _elc->Request(CMD_WIFI_SIGNAL_STRENGTH, 0, 1); - _elc->Request(&i, 4); + _elc->Request(&x, 1); _elc->Request(); ELClientPacket *pkt = _elc->WaitReturn(); From afb9072f473a5e78e8505f223b7f7966f3c99d9b Mon Sep 17 00:00:00 2001 From: dannybackx Date: Sat, 18 Mar 2017 16:00:21 +0100 Subject: [PATCH 08/14] Add code to query and set the current network. --- ELClient/ELClient.h | 1 + ELClient/ELClientCmd.cpp | 38 ++++++++++++++++++++++++++++++++++++++ ELClient/ELClientCmd.h | 3 +++ 3 files changed, 42 insertions(+) diff --git a/ELClient/ELClient.h b/ELClient/ELClient.h index e8e7730..35108f5 100644 --- a/ELClient/ELClient.h +++ b/ELClient/ELClient.h @@ -48,6 +48,7 @@ typedef enum { CMD_WIFI_GET_APNAME, /* */ CMD_WIFI_SELECT_SSID, CMD_WIFI_SIGNAL_STRENGTH, + CMD_WIFI_GET_SSID, } CmdName; /**< Enumeration of commands supported by esp-link, this needs to match the definition in esp-link! */ diff --git a/ELClient/ELClientCmd.cpp b/ELClient/ELClientCmd.cpp index 4c93678..2d035dd 100644 --- a/ELClient/ELClientCmd.cpp +++ b/ELClient/ELClientCmd.cpp @@ -193,3 +193,41 @@ int ELClientCmd::GetRSSI(int i) { ELClientPacket *pkt = _elc->WaitReturn(); return pkt ? pkt->value : 0; } + +void ELClientCmd::SelectSSID(char *ssid, char *pass) { + _elc->Request(CMD_WIFI_SELECT_SSID, 0, 2); + _elc->Request(ssid, strlen(ssid)); + _elc->Request(pass, strlen(pass)); + _elc->Request(); + + ELClientPacket *pkt = _elc->WaitReturn(); + if (_elc->_debugEn) { + _elc->_debug->println("Returning ..."); + } +} + +void ELClientCmd::SelectSSID(int xssid, char *pass) { + unsigned char x = xssid; + _elc->Request(CMD_WIFI_SELECT_SSID, 0, 2); + _elc->Request(&x, 1); + _elc->Request(pass, strlen(pass)); + _elc->Request(); + + ELClientPacket *pkt = _elc->WaitReturn(); + if (_elc->_debugEn) { + _elc->_debug->println("Returning ..."); + } +} + +char *ELClientCmd::GetSSID() { + clientCmdCb.attach(this, &ELClientCmd::wifiGetApNameCallback); + _elc->Request(CMD_WIFI_GET_SSID, (uint32_t)&clientCmdCb, 0); + _elc->Request(); + + ELClientPacket *pkt = _elc->WaitReturn(); + if (_elc->_debugEn) { + _elc->_debug->println("Returning ..."); + } + + return ssid; +} diff --git a/ELClient/ELClientCmd.h b/ELClient/ELClientCmd.h index 9585cc5..a931699 100644 --- a/ELClient/ELClientCmd.h +++ b/ELClient/ELClientCmd.h @@ -24,6 +24,9 @@ class ELClientCmd { char *getMac(); char *mqttGetClientId(); int GetRSSI(int); // Current signal strength if <0, or selected network's rssi + void SelectSSID(char *, char *); + void SelectSSID(int, char *); + char *GetSSID(); private: ELClient* _elc; /**< ELClient instance */ From 5de8dd40e389dad92cf8c9064f994185a1786f13 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Tue, 21 Mar 2017 17:52:08 +0100 Subject: [PATCH 09/14] Add code to trigger a scan for wifi networks --- ELClient/ELClient.h | 11 ++++--- ELClient/ELClientCmd.cpp | 10 ++++++ ELClient/ELClientCmd.h | 1 + ELClient/examples/get-info/get-info.ino | 41 ++++++------------------- 4 files changed, 26 insertions(+), 37 deletions(-) diff --git a/ELClient/ELClient.h b/ELClient/ELClient.h index 35108f5..b62aec8 100644 --- a/ELClient/ELClient.h +++ b/ELClient/ELClient.h @@ -44,11 +44,12 @@ typedef enum { CMD_SOCKET_SETUP = 40, /**< Setup socket connection */ CMD_SOCKET_SEND, /**< Send socket packet */ - CMD_WIFI_GET_APCOUNT = 50, /* */ - CMD_WIFI_GET_APNAME, /* */ - CMD_WIFI_SELECT_SSID, - CMD_WIFI_SIGNAL_STRENGTH, - CMD_WIFI_GET_SSID, + CMD_WIFI_GET_APCOUNT = 50, // Query number of access pointer + CMD_WIFI_GET_APNAME, // Get the name for an access point + CMD_WIFI_SELECT_SSID, // Connect to this network + CMD_WIFI_SIGNAL_STRENGTH, // Query RSSI + CMD_WIFI_GET_SSID, // Query SSID currently connected to + CMD_WIFI_START_SCAN, // Trigger a scan (takes a long time) } CmdName; /**< Enumeration of commands supported by esp-link, this needs to match the definition in esp-link! */ diff --git a/ELClient/ELClientCmd.cpp b/ELClient/ELClientCmd.cpp index 2d035dd..9ffa3ee 100644 --- a/ELClient/ELClientCmd.cpp +++ b/ELClient/ELClientCmd.cpp @@ -231,3 +231,13 @@ char *ELClientCmd::GetSSID() { return ssid; } + +void ELClientCmd::StartScan() { + _elc->Request(CMD_WIFI_START_SCAN, 0, 0); + _elc->Request(); + + ELClientPacket *pkt = _elc->WaitReturn(); + if (_elc->_debugEn) { + _elc->_debug->println("Returning ..."); + } +} diff --git a/ELClient/ELClientCmd.h b/ELClient/ELClientCmd.h index a931699..19d02d4 100644 --- a/ELClient/ELClientCmd.h +++ b/ELClient/ELClientCmd.h @@ -27,6 +27,7 @@ class ELClientCmd { void SelectSSID(char *, char *); void SelectSSID(int, char *); char *GetSSID(); + void StartScan(); private: ELClient* _elc; /**< ELClient instance */ diff --git a/ELClient/examples/get-info/get-info.ino b/ELClient/examples/get-info/get-info.ino index f6a7cca..5d5c9dd 100644 --- a/ELClient/examples/get-info/get-info.ino +++ b/ELClient/examples/get-info/get-info.ino @@ -12,6 +12,9 @@ ELClientCmd cmd(&esp); boolean wifiConnected = false; +#define IP(x, y) ((int)((x >> (8*y)) & 0xFF)) +static char buffer[80]; + void setup() { Serial.begin(9600); // Match the esplink config (Arduino serial <-> ESP) delay(3000); @@ -28,38 +31,12 @@ void setup() { uint32_t ip, nm, gw; cmd.GetWifiInfo(&ip, &nm, &gw); - // FIX ME in reverse - Serial.print("IP Address "); - Serial.print((ip & 0x000000FF)); - Serial.print("."); - Serial.print((ip & 0x0000FF00) >> 8); - Serial.print("."); - Serial.print((ip & 0x00FF0000) >> 16); - Serial.print("."); - Serial.print((ip & 0xFF000000) >> 24); - Serial.println(""); - - // FIX ME in reverse - Serial.print("Network mask "); - Serial.print((nm & 0x000000FF)); - Serial.print("."); - Serial.print((nm & 0x0000FF00) >> 8); - Serial.print("."); - Serial.print((nm & 0x00FF0000) >> 16); - Serial.print("."); - Serial.print((nm & 0xFF000000) >> 24); - Serial.println(""); - - // FIX ME in reverse - Serial.print("IP Gateway "); - Serial.print((gw & 0x000000FF)); - Serial.print("."); - Serial.print((gw & 0x0000FF00) >> 8); - Serial.print("."); - Serial.print((gw & 0x00FF0000) >> 16); - Serial.print("."); - Serial.print((gw & 0xFF000000) >> 24); - Serial.println(""); + sprintf(buffer, "IP Address %d.%d.%d.%d\n", IP(ip, 0), IP(ip, 1), IP(ip, 2), IP(ip, 3)); + Serial.print(buffer); + sprintf(buffer, "Network mask %d.%d.%d.%d\n", IP(nm, 0), IP(nm, 1), IP(nm, 2), IP(nm, 3)); + Serial.print(buffer); + sprintf(buffer, "IP Gateway %d.%d.%d.%d\n", IP(gw, 0), IP(gw, 1), IP(gw, 2), IP(gw, 3)); + Serial.print(buffer); char *mac = cmd.getMac(); Serial.print("MAC Address : "); From eff028f5cd1b5405bf9e8ad4cb9fd93ba725c041 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Sun, 26 Mar 2017 15:59:35 +0200 Subject: [PATCH 10/14] Add partially tested stuff --- ELClient/examples/firewall/Dyndns.cpp | 152 +++++++++++++++++ ELClient/examples/firewall/Dyndns.h | 44 +++++ ELClient/examples/firewall/Makefile | 203 +++++++++++++++++++++++ ELClient/examples/firewall/PcpClient.cpp | 166 ++++++++++++++++++ ELClient/examples/firewall/PcpClient.h | 86 ++++++++++ ELClient/examples/firewall/UpnpIgd.cpp | 191 +++++++++++++++++++++ ELClient/examples/firewall/UpnpIgd.h | 39 +++++ ELClient/examples/firewall/firewall.ino | 127 ++++++++++++++ 8 files changed, 1008 insertions(+) create mode 100644 ELClient/examples/firewall/Dyndns.cpp create mode 100644 ELClient/examples/firewall/Dyndns.h create mode 100644 ELClient/examples/firewall/Makefile create mode 100644 ELClient/examples/firewall/PcpClient.cpp create mode 100644 ELClient/examples/firewall/PcpClient.h create mode 100644 ELClient/examples/firewall/UpnpIgd.cpp create mode 100644 ELClient/examples/firewall/UpnpIgd.h create mode 100644 ELClient/examples/firewall/firewall.ino diff --git a/ELClient/examples/firewall/Dyndns.cpp b/ELClient/examples/firewall/Dyndns.cpp new file mode 100644 index 0000000..9819195 --- /dev/null +++ b/ELClient/examples/firewall/Dyndns.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2016, 2017 Danny Backx + * + * License (MIT license): + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This contains code to send a message to a dynamic dns service such as no-ip.com + * to register the IP address of this device. + * The goal is that devices get a fixed DNS name such as esp-device-xxahg.no-ip.com, determined + * by the owner of the device, and authomatically attach their current IP address (usually variable, + * depending on the setup of the network they're in) to it. + * + * From https://www.noip.com/integrate/request : + * + * An example update request string + * http://username:password@dynupdate.no-ip.com/nic/update?hostname=mytest.testdomain.com&myip=1.2.3.4 + * + * An example basic, raw HTTP header GET request + * GET /nic/update?hostname=mytest.testdomain.com&myip=1.2.3.4 HTTP/1.0 + * Host: dynupdate.no-ip.com + * Authorization: Basic base64-encoded-auth-string + * User-Agent: Bobs Update Client WindowsXP/1.2 bob@somedomain.com + * + * The base-encoded-auth-string can be created by using the base64 command and entering + * userid:password + * on a single line. For instance : + * acer: {1} base64 + * userid:password <-- substitute your own + * dXNlcmlkOnBhc3N3b3JkCg== <-- this output is what you need + * acer: {2} + */ +#include + +#include +#include + +#include "Dyndns.h" + +extern ELClient esp; + +const char *Dyndns::get_template1 = "/nic/update?hostname=%s"; +const char *Dyndns::get_template2 = "/nic/update?hostname=%s&myip=%s"; +const char *Dyndns::hdr_template = "Authorization: Basic %s\r\n"; + +Dyndns::Dyndns() { + url = "dynupdate.no-ip.com"; // Default, can be overruled by method + + rest = new ELClientRest(&esp); + hostname = ip = auth = 0; + buffer = 0; +} + +Dyndns::~Dyndns() { + if (rest) { + delete rest; + rest = 0; + } +} + +void Dyndns::setHostname(const char *hostname) { + this->hostname = (char *)hostname; +} + +void Dyndns::setAddress(const char *ip) { + this->ip = (char *)ip; +} + +void Dyndns::setAuth(const char *auth) { + this->auth = (char *)auth; +} + +void Dyndns::setUrl(const char *url) { + this->url = (char *)url; +} + +void Dyndns::update() { + char *msg1, *msg2; + int err = rest->begin(url); + if (err != 0) { + delete rest; + rest = 0; + + Serial.print("Dyndns : could not initialize "); + Serial.print(url); + Serial.print(", error code "); + Serial.println(err); + return; + } + + if (ip != 0) + msg1 = (char *)malloc(strlen(get_template2) + strlen(hostname) + strlen(ip) + 5); + else + msg1 = (char *)malloc(strlen(get_template1) + strlen(hostname) + 5); + if (msg1 == 0) { + Serial.println("Could not allocate memory for HTTP query"); + return; + } + + if (ip != 0) + sprintf(msg1, get_template2, hostname, ip); + else + sprintf(msg1, get_template1, hostname); + + msg2 = (char *)malloc(strlen(hdr_template) + strlen(url) + strlen(auth) + 5); + if (msg2 == 0) { + Serial.println("Could not allocate memory for HTTP query"); + free(msg1); + return; + } + sprintf(msg2, hdr_template, auth); + + rest->setHeader(msg2); + rest->setUserAgent("dannys esp-link library"); + rest->request(msg1, "GET", "", 0); + + buffer = (char *)malloc(buffer_size); + if (buffer == 0) { + Serial.println("Could not allocate memory for HTTP query"); + free(msg1); free(msg2); + return; + } + buffer[0] = 0; + + err = rest->waitResponse(buffer, buffer_size); + + if (err == HTTP_STATUS_OK) { + // Serial.println("Success "); + } else if (err == 0) { + // timeout + Serial.println("Timeout"); + } else { + Serial.println("HTTP GET failed"); + } + free(msg1); free(msg2); + free(buffer); +} diff --git a/ELClient/examples/firewall/Dyndns.h b/ELClient/examples/firewall/Dyndns.h new file mode 100644 index 0000000..7684fe9 --- /dev/null +++ b/ELClient/examples/firewall/Dyndns.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016, 2017 Danny Backx + * + * License (MIT license): + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef _INCLUDE_DYNDNS_H_ +#define _INCLUDE_DYNDNS_H_ + +class Dyndns { +public: + Dyndns(); + ~Dyndns(); + void update(); + void setHostname(const char *); + void setAddress(const char *); + void setAuth(const char *); + void setUrl(const char *); + +private: + ELClientRest *rest; + static const char *get_template1, *get_template2, *hdr_template; + char *hostname, *ip, *auth; + char *buffer; + const int buffer_size = 256; + char *url; +}; +#endif diff --git a/ELClient/examples/firewall/Makefile b/ELClient/examples/firewall/Makefile new file mode 100644 index 0000000..1117852 --- /dev/null +++ b/ELClient/examples/firewall/Makefile @@ -0,0 +1,203 @@ +#==================================================================================== +# +# A makefile for Arduino projects. +# This is based on work by Peter Lerup, but heavily modified. +# +# License: GPL 2.1 +# General and full license information is available at: +# https://github.com/plerup/makeEspArduino +# +# Copyright (c) 2016 Peter Lerup. All rights reserved. +# +#==================================================================================== +#==================================================================================== +# User editable area +#==================================================================================== + +SKETCH= $(HOME)/src/sketchbook/mega-esp/firewall/firewall.ino +EXTRA_SRC= Dyndns.cpp PcpClient.cpp UpnpIgd.cpp + +BUILD_ROOT= tmp +LIBCPP= + +AVR_MYLIBS = $(HOME)/src/sketchbook/libraries +AVR_MEGALIBS = $(HOME)/src/sketchbook/mega-esp/libraries + +LIBS = \ + ${AVR_MEGALIBS}/ELClient \ + ${AVR_LIBS}/Wire \ + ${AVR_MYLIBS}/Time + +# ${AVR_MYLIBS}/ArduinoWiFi \ +#=== Project specific definitions: sketch and list of needed libraries + +#==================================================================================== +# The area below should normally not need to be edited +#==================================================================================== + +ARDUINO_ROOT= /home/danny/src/arduino/arduino-1.7.11-linux64 +AVR_ROOT= ${ARDUINO_ROOT}/hardware/arduino/avr +AVR_LIBS= $(AVR_ROOT)/libraries +AVRFLASH= ${ARDUINO_ROOT}/hardware/tools/esp-link-tools/avrflash +ARCH= avr + +time_string = $(shell perl -e 'use POSIX qw(strftime); print strftime($(1), localtime());') + +START_TIME := $(shell perl -e "print time();") +# Main output definitions +MAIN_NAME = $(basename $(notdir $(SKETCH))) +MAIN_EXE = $(BUILD_ROOT)/$(MAIN_NAME).bin +MAIN_ELF = $(OBJ_DIR)/$(MAIN_NAME).elf +MAIN_EEP = $(OBJ_DIR)/$(MAIN_NAME).eep +MAIN_HEX = $(OBJ_DIR)/$(MAIN_NAME).hex +SRC_GIT_VERSION = + +# Directory for intermedite build files +OBJ_DIR = $(BUILD_ROOT)/obj +OBJ_EXT = .o +DEP_EXT = .d + +# Compiler definitions +CC = ${ARCH}-gcc +CPP = ${ARCH}-g++ +LD = $(CC) +AR = ${ARCH}-ar +AVR_TOOL = /home/danny/.arduino15/packages/esp8266/tools/esptool/0.4.6/esptool + +#DEBUG=-g +DEBUG=-Os -g + +#MCU= atmega328p +MCU= atmega2560 +#VARIANT= standard +VARIANT= mega + +INCLUDE_DIRS += ${CORE_DIR} ${AVR_ROOT}/variants/${VARIANT} ${OBJ_DIR} +C_DEFINES= -c ${DEBUG} -w -MMD \ + -ffunction-sections -fdata-sections -mmcu=${MCU} \ + -DF_CPU=16000000L -DARDUINO=107011 -DARDUINO_AVR_UNO_WIFI -DARDUINO_ARCH_AVR \ + ${EXTRA_DEFINES} +C_INCLUDES= $(foreach dir,$(INCLUDE_DIRS) $(USER_DIRS),-I$(dir)) +C_FLAGS ?= -c ${DEBUG} -std=gnu11 -ffunction-sections -fdata-sections -MMD -mmcu=${MCU} +CPP_FLAGS ?= -c ${DEBUG} -std=gnu++11 \ + -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics \ + -MMD -mmcu=${MCU} +S_FLAGS ?= -c ${DEBUG} -x assembler-with-cpp -MMD +LD_FLAGS ?= -w ${DEBUG} -Wl,--gc-sections -mmcu=${MCU} +LD_STD_LIBS ?= -lm + +# Core source files +CORE_DIR = $(AVR_ROOT)/cores/arduino +CORE_SRC = $(shell find $(CORE_DIR) -name "*.S" -o -name "*.c" -o -name "*.cpp") +CORE_OBJ = $(patsubst %,$(OBJ_DIR)/%$(OBJ_EXT),$(notdir $(CORE_SRC))) +CORE_LIB = $(OBJ_DIR)/core.ar + +# User defined compilation units +USER_SRC = $(SKETCH) ${EXTRA_SRC} $(shell find $(LIBS) -name "*.cpp" -o -name "*.c") +USER_INCL = $(SKETCH) ${EXTRA_SRC} $(shell find $(LIBS) -name "*.h") +# Object file suffix seems to be significant for the linker... +USER_OBJ = $(subst .ino,.cpp,$(patsubst %,$(OBJ_DIR)/%$(OBJ_EXT),$(notdir $(USER_SRC)))) +USER_DIRS = $(sort $(dir $(USER_SRC) $(USER_INCL))) + +VPATH += $(shell find $(CORE_DIR) -type d) $(USER_DIRS) + +# Automatically generated build information data +# Makes the build date and git descriptions at the actual build +# event available as string constants in the program +BUILD_INFO_H = $(OBJ_DIR)/buildinfo.h +BUILD_INFO_CPP = $(OBJ_DIR)/buildinfo.cpp +BUILD_INFO_OBJ = $(BUILD_INFO_CPP)$(OBJ_EXT) + +# Temp files +VI_TMP1= ${USER_SRC:.c=.c~} +VI_TMP2= ${VI_TMP1:.ino=.ino~} +VI_TMP3= ${VI_TMP2:.cpp=.cpp~} +VI_TMPFILES= ${VI_TMP3:.h=.h~} +BAK_FILES= + +$(BUILD_INFO_H): | $(OBJ_DIR) + echo "typedef struct { const char *date, *time, *src_filename, *src_version, *env_version;} _tBuildInfo; extern _tBuildInfo _BuildInfo;" >$@ + + +# Build rules +$(OBJ_DIR)/%.cpp$(OBJ_EXT): %.cpp $(BUILD_INFO_H) + echo C++-Compile $(' >$(BUILD_INFO_CPP) + echo '_tBuildInfo _BuildInfo = {"$(BUILD_DATE)","$(BUILD_TIME)","$(SKETCH)","",""};' >>$(BUILD_INFO_CPP) + $(CPP) $(C_DEFINES) $(C_INCLUDES) $(CPP_FLAGS) $(BUILD_INFO_CPP) -o $(BUILD_INFO_OBJ) + $(LD) $(LD_FLAGS) -Wl,--start-group $^ $(BUILD_INFO_OBJ) $(LD_STD_LIBS) -Wl,--end-group -L$(OBJ_DIR) -o $(MAIN_ELF) + ${ARCH}-objcopy -O ihex -j .eeprom --set-section-flags=.eeprom=alloc,load --no-change-warnings --change-section-lma .eeprom=0 ${MAIN_ELF} ${MAIN_EEP} + ${ARCH}-objcopy -O ihex -R .eeprom ${MAIN_ELF} ${MAIN_HEX} + ${ARCH}-size ${MAIN_ELF} + perl -e 'print "Build complete. Elapsed time: ", time()-$(START_TIME), " seconds\n\n"' + +upload: all + avrdude -C${ARDUINO_ROOT}/hardware/tools/avr/etc/avrdude.conf -v true -pm2560 -cwiring -P${UPLOAD_PORT} -b115200 -D -Uflash:w:${MAIN_HEX}:i + +ota: all + $(AVRFLASH) $(UPLOAD_HOST) $(MAIN_HEX) + +showotaip: + echo "IP Address for OTA is $(UPLOAD_AVAHI_IP) (symbol $(UPLOAD_AVAHI_NAME))" + +objdump: ${MAIN_ELF} + ${ARCH}-objdump -xSsd ${MAIN_ELF} >$@ + +clean:: + echo Removing all intermediate build files... + -rm -f ${VI_TMPFILES} ${BAK_FILES} + -rm -f $(OBJ_DIR)/* + -rm -rf ${BUILD_ROOT} + -rm -f objdump + +clean:: + -rm -f ${LINKS} links + +all:: ${LINKS} + +${LINKS}:: links + +links:: ${LINKS:%=../serre/%} + -rm -f ${LINKS} + for i in ${LINKS}; do ln -s ../serre/$$i .; done + touch links + +$(OBJ_DIR): + mkdir -p $(OBJ_DIR) + +.PHONY: all +all:: $(OBJ_DIR) $(BUILD_INFO_H) $(MAIN_EXE) + +# Include all available dependencies +-include $(wildcard $(OBJ_DIR)/*$(DEP_EXT)) + +.DEFAULT_GOAL = all + +ifndef VERBOSE +# Set silent mode as default +# MAKEFLAGS += --silent +endif diff --git a/ELClient/examples/firewall/PcpClient.cpp b/ELClient/examples/firewall/PcpClient.cpp new file mode 100644 index 0000000..0a9f6f8 --- /dev/null +++ b/ELClient/examples/firewall/PcpClient.cpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2017 Danny Backx + * + * License (MIT license): + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This class allows you to "punch a hole" in the NAT router/firewall typically used. + * + * A local port on the esp-link can be remapped to some other port number on the router. + * Someone accessing that port number can thereby get access to the esp-link even through + * the NAT router. + * + * Protocol used : PCP (Port Control Protocol), RFC 6887, which is a superset of + * NAT-PMP (NAT Port Mapping Protocol), RFC 6886. + * + * Obviously (grin) this is a minimal implementation. + */ +#include + +#include +#include + +#include "PcpClient.h" +#include "IPAddress.h" + +extern ELClient esp; +static PcpClient *self; + +PcpClient::PcpClient() { + sock = new ELClientSocket(&esp); + self = this; + catcher = 0; +} + +PcpClient::~PcpClient() { + if (sock) { + delete sock; + sock = 0; + } +} + +void PcpClient::setLocalIP(IPAddress local) { + this->local = local; +} + +void PcpClient::setRouter(const char *hostname) { + router_name = (char *)hostname; +} + +void PcpClient::queryRouterExternalAddress() { + struct PcpPacketExternalAddressRequest p; + p.version = p.opcode = 0; + external = IPAddress((uint32_t)0); + + catcher = &PcpClient::catchRouterExternalAddress; + + int err = sock->begin(router_name, pcp_server_port, SOCKET_UDP, udpCb); + sock->send((char *)&p); +} + +IPAddress PcpClient::getRouterExternalAddress() { + return external; +} + +void PcpClient::catchRouterExternalAddress(uint16_t len, char *data) { + if (len == sizeof(PcpPacketExternalAddressReply)) { + PcpPacketExternalAddressReply *p = (PcpPacketExternalAddressReply *)data; + if (p->opcode != 0x80) + return; + if (p->version != 0 && p->version != 2) // NAT-PMP & PCP + return; + // Looks right now + external = p->address; + } else + return; // silently + + // Success, so switch ourselves off + catcher = 0; +} + +void PcpClient::addPort(int16_t localport, int16_t remoteport, int8_t protocol, int32_t lifetime) { + struct PcpPacketMappingRequest p; + p.version = 0; + p.opcode = protocol; + p.reserved = 0; + p.internal_port = localport; + p.external_port = remoteport; + p.lifetime = lifetime; + + catcher = &PcpClient::catchAddPort; + + int err = sock->begin(router_name, pcp_server_port, SOCKET_UDP, udpCb); + sock->send((char *)&p); +} + +void PcpClient::catchAddPort(uint16_t len, char *data) { +#if 0 + if (len == sizeof(PcpPacketMappingReply)) { + PcpPacketMappingReply *p = (PcpPacketMappingReply *)data; + if (p->opcode != 0x81 && p->opcode != 0x82) + return; + if (p->version != 0 && p->version != 2) // NAT-PMP & PCP + return; + // Looks right now + // Nothing to do really + } else + return; // silently +#endif + // Success, so switch ourselves off + catcher = 0; +} + +void PcpClient::deletePort(int16_t localport, int8_t protocol) { + struct PcpPacketMappingRequest p; + p.version = 0; + p.opcode = protocol; + p.reserved = 0; + p.internal_port = localport; + p.external_port = 0; + p.lifetime = 0; + + catcher = &PcpClient::catchDeletePort; + + int err = sock->begin(router_name, pcp_server_port, SOCKET_UDP, udpCb); + sock->send((char *)&p); +} + +void PcpClient::catchDeletePort(uint16_t len, char *data) { + // Nothing to do ? + + // Success, so switch ourselves off + catcher = 0; +} + +void PcpClient::udpCb(uint8_t resp_type, uint8_t client_num, uint16_t len, char *data) { + if (len > 0 && resp_type == USERCB_RECV) { + if (self && self->catcher) + // (self .* catcher)(len, data); + (self ->* ((PcpClient*)self)->PcpClient::catcher)(len, data); + } +} + +/* Note for later : + * Syntax in a static member function : + * (self ->* ((PcpClient*)self)->PcpClient::catcher)(1, "xx"); + * Syntax in a member function : + * (this ->* ((PcpClient*)this)->PcpClient::catcher)(1, "xx"); + * + * See also http://stackoverflow.com/questions/990625/c-function-pointer-class-member-to-non-static-member-function + */ diff --git a/ELClient/examples/firewall/PcpClient.h b/ELClient/examples/firewall/PcpClient.h new file mode 100644 index 0000000..18e6444 --- /dev/null +++ b/ELClient/examples/firewall/PcpClient.h @@ -0,0 +1,86 @@ + +/* + * Copyright (c) 2017 Danny Backx + * + * License (MIT license): + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef _INCLUDE_PCPCLIENT_H_ +#define _INCLUDE_PCPCLIENT_H_ + +#include + +class PcpClient { +public: + PcpClient(); + ~PcpClient(); + + void setLocalIP(IPAddress local); + void setRouter(const char *hostname); + void queryRouterExternalAddress(); + IPAddress getRouterExternalAddress(); + void addPort(int16_t localport, int16_t remoteport, int8_t protocol, int32_t lifetime); + void deletePort(int16_t localport, int8_t protocol); + +private: + IPAddress local, external; + char *router_name; + ELClientSocket *sock; + const int pcp_client_port = 5350, + pcp_server_port = 5351; + static void udpCb(uint8_t, uint8_t, uint16_t, char *); + + void (PcpClient::*catcher)(uint16_t, char *); + void catchRouterExternalAddress(uint16_t len, char *data); + void catchAddPort(uint16_t, char *); + void catchDeletePort(uint16_t, char *); +}; + +struct PcpPacketExternalAddressRequest { + uint8_t version; // 0 + uint8_t opcode; // 0 +}; + +struct PcpPacketExternalAddressReply { + uint8_t version; // 0 + uint8_t opcode; // Reply should be 128 + uint16_t result; + uint32_t seconds; + uint32_t address; +}; + +struct PcpPacketMappingRequest { + uint8_t version; // 0 + uint8_t opcode; // 1 for UDP, 2 for TCP + uint16_t reserved; + int16_t internal_port; + uint16_t external_port; + uint32_t lifetime; // in seconds +}; + +struct PcpPacketMappingReply { + uint8_t version; // 0 + uint8_t opcode; // 128 + query-opcode + uint16_t result; + uint32_t seconds; + int16_t internal_port; + uint16_t external_port; + uint32_t lifetime; // in seconds +}; +#endif diff --git a/ELClient/examples/firewall/UpnpIgd.cpp b/ELClient/examples/firewall/UpnpIgd.cpp new file mode 100644 index 0000000..6883fc9 --- /dev/null +++ b/ELClient/examples/firewall/UpnpIgd.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2017 Danny Backx + * + * License (MIT license): + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This class allows you to "punch a hole" in the NAT router/firewall typically used. + * + * A local port on the esp-link can be remapped to some other port number on the router. + * Someone accessing that port number can thereby get access to the esp-link even through + * the NAT router. + * + * Protocol used : UPNP. + * + * This implementation is minimalistic : it can just create one packet type. + */ +#include + +#include +#include + +#include "UpnpIgd.h" + +extern ELClient esp; +char *location; + +UpnpIgd::UpnpIgd() { + udpSocket = 0; +} + +UpnpIgd::~UpnpIgd() { +} + +void UpnpIgd::udpCb(uint8_t resp_type, uint8_t client_num, uint16_t len, char *data) { + Serial.print("udpCb len "); + Serial.print(len); + Serial.print(" resp_type "); + Serial.println(resp_type); + + if (len > 0 && resp_type == USERCB_RECV) { + // Filter LOCATION: line + char *p, *q; + for (p=data; pbegin("239.255.255.250", 1900, SOCKET_UDP, udpCb); + udpSocket->send(msg); + esp.Process(); +#if 0 + delay(10); + udpSocket->send(msg); + delay(10); + udpSocket->send(msg); + delay(10); + udpSocket->send(msg); + delay(50); + udpSocket->send(msg); +#endif +} + +/* + +34 : UDP multicast to 239.255.255.250:1900 + +M-SEARCH * HTTP/1.1 +HOST: 239.255.255.250:1900 +ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1 +MAN: "ssdp:discover" +MX: 2 + +35 : UDP reply + +HTTP/1.1 200 OK +CACHE-CONTROL:max-age=1800 +EXT: +LOCATION:http://192.168.1.1:8000/o8ee3npj36j/IGD/upnp/IGD.xml +SERVER:MediaAccess TG 789Ovn Xtream 10.A.0.I UPnP/1.0 (9C-97-26-26-44-DE) +ST:urn:schemas-upnp-org:device:InternetGatewayDevice:1 +USN:uuid:c5eb6a02-c0b8-5afe-83da-3965c9516822::urn:schemas-upnp-org:device:InternetGatewayDevice:1 + +114 + +POST /o8ee3npj36j/IGD/upnp/control/igd/wanpppc_1_1_1 HTTP/1.1 +Host: 192.168.1.1:8000 +User-Agent: Ubuntu/16.04, UPnP/1.1, MiniUPnPc/2.0 +Content-Length: 286 +Content-Type: text/xml +SOAPAction: "urn:schemas-upnp-org:service:WANPPPConnection:1#GetExternalIPAddress" +Connection: Close +Cache-Control: no-cache +Pragma: no-cache + + + + +117 + + + + +213.49.166.224 + + +125 + +POST /o8ee3npj36j/IGD/upnp/control/igd/wanpppc_1_1_1 HTTP/1.1 +Host: 192.168.1.1:8000 +User-Agent: Ubuntu/16.04, UPnP/1.1, MiniUPnPc/2.0 +Content-Length: 594 +Content-Type: text/xml +SOAPAction: "urn:schemas-upnp-org:service:WANPPPConnection:1#AddPortMapping" +Connection: Close +Cache-Control: no-cache +Pragma: no-cache + + +9876TCP80192.168.1.1761libminiupnpc0 + +127+129 + +HTTP/1.0 200 OK +Connection: close +Date: Sat, 25 Mar 2017 16:54:41 GMT +Server: MediaAccess TG 789Ovn Xtream 10.A.0.I UPnP/1.0 (9C-97-26-26-44-DE) +Content-Length: 300 +Content-Type: text/xml; charset="utf-8" +EXT: + + + + + + + + +252 + +POST /o8ee3npj36j/IGD/upnp/control/igd/wanpppc_1_1_1 HTTP/1.1 +Host: 192.168.1.1:8000 +User-Agent: Ubuntu/16.04, UPnP/1.1, MiniUPnPc/2.0 +Content-Length: 380 +Content-Type: text/xml +SOAPAction: "urn:schemas-upnp-org:service:WANPPPConnection:1#DeletePortMapping" +Connection: Close +Cache-Control: no-cache +Pragma: no-cache + + +9876TCP + + +314,316,318 + + + + +/* */ diff --git a/ELClient/examples/firewall/UpnpIgd.h b/ELClient/examples/firewall/UpnpIgd.h new file mode 100644 index 0000000..4058996 --- /dev/null +++ b/ELClient/examples/firewall/UpnpIgd.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 Danny Backx + * + * License (MIT license): + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef _INCLUDE_UPNPIGD_H_ +#define _INCLUDE_UPNPIGD_H_ + +#include + +class UpnpIgd { +public: + UpnpIgd(); + ~UpnpIgd(); + void discoverGateway(); + +private: + ELClientSocket *udpSocket; + static void udpCb(uint8_t, uint8_t, uint16_t, char *); +}; + +#endif diff --git a/ELClient/examples/firewall/firewall.ino b/ELClient/examples/firewall/firewall.ino new file mode 100644 index 0000000..fdc2907 --- /dev/null +++ b/ELClient/examples/firewall/firewall.ino @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2016, 2017 Danny Backx + * + * License (MIT license): + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#include + +#include +#include +#include +#include + +#include +#include +#include + +const int buffer_size = 80; +const int loop_delay = 50; + +time_t boot_time = 0; + +char buffer[buffer_size]; +uint32_t ip, nm, gw; + +ELClient esp(&Serial, &Serial); +ELClientCmd cmd(&esp); + +const char *YOURHOSTNAME = "your-host.dyndns.org"; +const char *YOURAUTH = "azerty"; + +#define IP(ip, x) ((int)((ip >> (8*x)) & 0xFF)) + +// forward +time_t mygettime(); + +void setup() { + delay(2000); + + // Serial.begin(9600); + Serial.begin(115200); + Serial.println("Yow !"); + + // Start WiFi + Serial.println("Starting wifi"); + while (! esp.Sync()) { + Serial.println("ELClient sync failed"); + } + + // Get our IP address + uint32_t ip, nm, gw; + cmd.GetWifiInfo(&ip, &nm, &gw); + + sprintf(buffer, "IP Address %d.%d.%d.%d", IP(ip, 0), IP(ip, 1), IP(ip, 2), IP(ip, 3)); + Serial.println(buffer); + + Serial.print("Registering with no-ip.com ... "); + Dyndns *d = new Dyndns(); + d->setHostname(YOURHOSTNAME); + // If you don't set the address and you're behind a NAT router, + // then no-ip will pick up the router instead, which is what you want. + // d->setAddress("192.168.1.150"); + d->setAuth(YOURAUTH); // base64 of user:password + d->update(); + Serial.println("done"); + + // Punch a hole through the firewall to reach us + Serial.print("Preparing firewall ... "); + PcpClient *pcp = new PcpClient(); + sprintf(buffer, "%d.%d.%d.%d", IP(gw, 0), IP(gw, 1), IP(gw, 2), IP(gw, 3)); + pcp->setRouter(buffer); + + // See if our router supports the PCP protocol + IPAddress ext; + pcp->queryRouterExternalAddress(); + int i=20; + while (i-- > 0) { + ext = pcp->getRouterExternalAddress(); + if (ext != IPAddress((uint32_t)0)) + break; + delay(10); + esp.Process(); + } + if (ext == IPAddress((uint32_t)0)) { + Serial.println("failed, hanging..."); + while (1) ; + } + + pcp->addPort(80, 8765, 2 /* TCP */, -1); + Serial.println("done"); +} + +/********************************************************************************* + * * + * Loop * + * * + *********************************************************************************/ +void loop() { + esp.Process(); + + time_t nowts = now(); + + delay(loop_delay); +} + +/* + * Helper function because the ELClientCmd doesn't have a static function for this. + */ +time_t mygettime() { + return cmd.GetTime(); +} From eb7c08e5a44a150b283c7186d22ef5a8a5bb3155 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Mon, 17 Apr 2017 13:49:59 +0200 Subject: [PATCH 11/14] Add code to use UPnP to add a port mapping to a NAT firewall. --- ELClient/ELClient.h | 5 ++++ ELClient/ELClientUPnP.cpp | 53 +++++++++++++++++++++++++++++++++++++++ ELClient/ELClientUPnP.h | 21 ++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100644 ELClient/ELClientUPnP.cpp create mode 100644 ELClient/ELClientUPnP.h diff --git a/ELClient/ELClient.h b/ELClient/ELClient.h index b62aec8..91aad91 100644 --- a/ELClient/ELClient.h +++ b/ELClient/ELClient.h @@ -51,6 +51,11 @@ typedef enum { CMD_WIFI_GET_SSID, // Query SSID currently connected to CMD_WIFI_START_SCAN, // Trigger a scan (takes a long time) + CMD_UPNP_SCAN = 60, + CMD_UPNP_ADD_PORT, + CMD_UPNP_REMOVE_PORT, + CMD_UPNP_BEGIN, + } CmdName; /**< Enumeration of commands supported by esp-link, this needs to match the definition in esp-link! */ enum WIFI_STATUS { diff --git a/ELClient/ELClientUPnP.cpp b/ELClient/ELClientUPnP.cpp new file mode 100644 index 0000000..53a70e0 --- /dev/null +++ b/ELClient/ELClientUPnP.cpp @@ -0,0 +1,53 @@ +#include "ELClientUPnP.h" + +ELClientUPnP::ELClientUPnP(ELClient *e) +{ + _elc = e; +} + +int ELClientUPnP::begin() { + _elc->Request(CMD_UPNP_BEGIN, 0, 0); + _elc->Request(); +} + +/* + * Query for a NAT router on the network (via UPnP multicast). + * Return its IP address if found. + * + * This assumes we find only one (rather safe for home use). FIXME ? + * This allows an application to call the scan function in a loop awaiting non-null reply. + */ +uint32_t ELClientUPnP::scan() { + _elc->Request(CMD_UPNP_SCAN, 0, 0); + _elc->Request(); + + ELClientPacket *pkt = _elc->WaitReturn(); + return pkt ? pkt->value : 0; +} + +/* + * Add a port mapping to the firewall / home router. + */ +void ELClientUPnP::add(uint32_t local_ip, uint16_t local_port, uint16_t remote_port) { + _elc->Request(CMD_UPNP_ADD_PORT, 0, 3); + _elc->Request(&local_ip, 4); + _elc->Request(&local_port, 2); + _elc->Request(&remote_port, 2); + _elc->Request(); + + ELClientPacket *pkt = _elc->WaitReturn(); +} + +void ELClientUPnP::remove(uint16_t remote_port) { + _elc->Request(CMD_UPNP_REMOVE_PORT, 0, 1); + _elc->Request(&remote_port, 2); + _elc->Request(); + + ELClientPacket *pkt = _elc->WaitReturn(); +} + +/* + * Query the external IP address of the router + */ +uint32_t ELClientUPnP::getExternalAddress() { +} diff --git a/ELClient/ELClientUPnP.h b/ELClient/ELClientUPnP.h new file mode 100644 index 0000000..81e0172 --- /dev/null +++ b/ELClient/ELClientUPnP.h @@ -0,0 +1,21 @@ +#ifndef _EL_CLIENT_UPNP_H_ +#define _EL_CLIENT_UPNP_H_ + +#include +#include "ELClient.h" + +class ELClientUPnP { + public: + ELClientUPnP(ELClient *e); + + int begin(); + + uint32_t scan(); + void add(uint32_t local_ip, uint16_t local_port, uint16_t remote_port); + void remove(uint16_t remote_port); + uint32_t getExternalAddress(); + + private: + ELClient *_elc; +}; +#endif // _EL_CLIENT_UPNP_H_ From 0277fd1923f8edfec4f97176a633682cebd17c98 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Mon, 17 Apr 2017 14:03:52 +0200 Subject: [PATCH 12/14] Add copyright --- ELClient/ELClientUPnP.cpp | 27 +++++++++++++++++++++++++++ ELClient/ELClientUPnP.h | 27 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/ELClient/ELClientUPnP.cpp b/ELClient/ELClientUPnP.cpp index 53a70e0..6ac4261 100644 --- a/ELClient/ELClientUPnP.cpp +++ b/ELClient/ELClientUPnP.cpp @@ -1,3 +1,30 @@ +/* + * Copyright (c) 2016, 2017 Danny Backx + * + * License (MIT license): + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This contains code to "punch a hole" in a NAT firewall, so a device on the inside + * network can be accessed from the outside by parties that know how to do so. + * + * This is basically a poor man's version of some of the UPnP protocols. + */ #include "ELClientUPnP.h" ELClientUPnP::ELClientUPnP(ELClient *e) diff --git a/ELClient/ELClientUPnP.h b/ELClient/ELClientUPnP.h index 81e0172..844285a 100644 --- a/ELClient/ELClientUPnP.h +++ b/ELClient/ELClientUPnP.h @@ -1,3 +1,30 @@ +/* + * Copyright (c) 2016, 2017 Danny Backx + * + * License (MIT license): + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * This contains code to "punch a hole" in a NAT firewall, so a device on the inside + * network can be accessed from the outside by parties that know how to do so. + * + * This is basically a poor man's version of some of the UPnP protocols. + */ #ifndef _EL_CLIENT_UPNP_H_ #define _EL_CLIENT_UPNP_H_ From 96c78bf97c136568334807241e83b128e11d55e3 Mon Sep 17 00:00:00 2001 From: dannybackx Date: Mon, 17 Apr 2017 15:15:58 +0200 Subject: [PATCH 13/14] Add code to query external address of the router. --- ELClient/ELClient.h | 1 + ELClient/ELClientUPnP.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/ELClient/ELClient.h b/ELClient/ELClient.h index 91aad91..547c348 100644 --- a/ELClient/ELClient.h +++ b/ELClient/ELClient.h @@ -55,6 +55,7 @@ typedef enum { CMD_UPNP_ADD_PORT, CMD_UPNP_REMOVE_PORT, CMD_UPNP_BEGIN, + CMD_UPNP_QUERY_EXTERNAL_ADDRESS, } CmdName; /**< Enumeration of commands supported by esp-link, this needs to match the definition in esp-link! */ diff --git a/ELClient/ELClientUPnP.cpp b/ELClient/ELClientUPnP.cpp index 6ac4261..c1581d4 100644 --- a/ELClient/ELClientUPnP.cpp +++ b/ELClient/ELClientUPnP.cpp @@ -77,4 +77,9 @@ void ELClientUPnP::remove(uint16_t remote_port) { * Query the external IP address of the router */ uint32_t ELClientUPnP::getExternalAddress() { + _elc->Request(CMD_UPNP_QUERY_EXTERNAL_ADDRESS, 0, 0); + _elc->Request(); + + ELClientPacket *pkt = _elc->WaitReturn(); + return pkt ? pkt->value : 0; } From dd51416303b541e4e3d77d24cf47978824f4921d Mon Sep 17 00:00:00 2001 From: dannybackx Date: Mon, 14 Aug 2017 20:43:25 +0200 Subject: [PATCH 14/14] Changes to cope with REST replies longer than 100 bytes --- ELClient/ELClient.cpp | 21 ++++++++++ ELClient/ELClient.h | 3 ++ ELClient/ELClientRest.cpp | 85 ++++++++++++++++++++++++++++++++++++--- ELClient/ELClientRest.h | 2 + 4 files changed, 105 insertions(+), 6 deletions(-) diff --git a/ELClient/ELClient.cpp b/ELClient/ELClient.cpp index c0bf057..87d4a57 100644 --- a/ELClient/ELClient.cpp +++ b/ELClient/ELClient.cpp @@ -65,8 +65,26 @@ ELClientPacket* ELClient::protoCompletedCb(void) { _debug->print("RESP_V: "); _debug->println(packet->value); } + _longPacket = 0; return packet; + case CMD_RESP_CB_CONTINUE: + // response callback, but one of the parts of a long packet --> just prepare assembly + // Currently just a copy (without notes) of the code below... + + { + // Serial.print("CMD_RESP_CB_CONTINUE\n"); + _longPacket = 1; + FP *fp; + fp = (FP*)packet->value; + if (fp->attached()) { + ELClientResponse resp(packet); + (*fp)(&resp); + } + return NULL; + } + case CMD_RESP_CB: // response callback: perform the callback! + { FP *fp; // callback reponse if (_debugEn) { @@ -75,12 +93,15 @@ ELClientPacket* ELClient::protoCompletedCb(void) { _debug->print(" "); _debug->println(packet->argc); } + + _longPacket = 0; fp = (FP*)packet->value; if (fp->attached()) { ELClientResponse resp(packet); (*fp)(&resp); } return NULL; + } case CMD_SYNC: // esp-link is not in sync, it may have reset, signal up the stack _debug->println("NEED_SYNC!"); if (resetCb != NULL) (*resetCb)(); diff --git a/ELClient/ELClient.h b/ELClient/ELClient.h index 547c348..8fc2e79 100644 --- a/ELClient/ELClient.h +++ b/ELClient/ELClient.h @@ -57,6 +57,8 @@ typedef enum { CMD_UPNP_BEGIN, CMD_UPNP_QUERY_EXTERNAL_ADDRESS, + CMD_RESP_CB_CONTINUE = 70, // RESP_CB for a long packet + } CmdName; /**< Enumeration of commands supported by esp-link, this needs to match the definition in esp-link! */ enum WIFI_STATUS { @@ -143,6 +145,7 @@ class ELClient { uint16_t crc; /**< CRC checksum */ ELClientProtocol _proto; /**< Protocol structure */ uint8_t _protoBuf[128]; /**< Protocol buffer */ + uint16_t _longPacket; /**< Packet length in case of long packet */ void init(); void DBG(const char* info); diff --git a/ELClient/ELClientRest.cpp b/ELClient/ELClientRest.cpp index c08d075..34543d1 100644 --- a/ELClient/ELClientRest.cpp +++ b/ELClient/ELClientRest.cpp @@ -43,13 +43,26 @@ void ELClientRest::restCallback(void *res) ELClientResponse *resp = (ELClientResponse *)res; - resp->popArg(&_status, sizeof(_status)); - if (_elc->_debugEn) { - _elc->_debug->print("REST code "); - _elc->_debug->println(_status); - } + if (_elc->_longPacket == 0) { + resp->popArg(&_status, sizeof(_status)); + + if (_elc->_debugEn) { + _elc->_debug->print("REST code "); + _elc->_debug->println(_status); + } + + _len = resp->popArgPtr(&_data); - _len = resp->popArgPtr(&_data); + // Serial.print("restCallback status "); Serial.print(_status); Serial.print(", len "); Serial.println(_len); + } else { + int16_t lp; + resp->popArg(&lp, sizeof(lp)); + _elc->_longPacket = lp; + _status = 200; // Hack + _len = resp->popArgPtr(&_data); + + // Serial.print("restCallback LONG totalLength "); Serial.print(_elc->_longPacket); Serial.print(", len "); Serial.println(_len); + } } /*! begin(const char* host, uint16_t port, boolean security) @@ -379,3 +392,63 @@ uint16_t ELClientRest::waitResponse(char* data, uint16_t maxLen, uint32_t timeou } return getResponse(data, maxLen); } + +/*! waitResponse2(char* data, uint16_t maxLen, uint32_t timeout, uint16_t *totalLength, uint16_t packetLength) +@brief Wait for the response, cope with long packets +@details Wait for the response from the remote server for time_out, + returns the HTTP status code, 0 if no response (may need to wait longer) +@warning Blocks the Arduino code for 5 seconds! not recommended to use. + Received packet is NOT null-terminated +@param data + Pointer to buffer for received packet +@param maxLen + Size of buffer for received packet. If the received packet is larger than the buffer, the received packet will be truncated. +@param timeout + Timout in milli seconds to wait for a response. +@param totalLength + if non-NULL, used to return the total length of the multi-packet data +@param packetLength + if non-NULL, used to return the length of this chunk of data (typically 100 if not the last packet) +@return uint16_t + Size of received packet or number of sent bytes or 0 if no response +@par Example +@code + static char *buf = 0; + static int bufsiz = 700; + buf = (char *)malloc(bufsiz); + uint16_t datalen = 0, packetlen = 0; + memset(buf, 0, bufsiz); + char *ptr = buf; + err = rest->waitResponse2(ptr, bufsiz-1, DEFAULT_REST_TIMEOUT, &datalen, &packetlen); + while (datalen) { + ptr += packetlen; + err = rest->waitResponse2(ptr, bufsiz-1, DEFAULT_REST_TIMEOUT, &datalen, &packetlen); + } + ... + free(buf); + buf = 0; +@endcode +*/ +uint16_t ELClientRest::waitResponse2(char* data, uint16_t maxLen, uint16_t *totalLength, uint16_t *packetLength, uint32_t timeout) +{ + uint32_t wait = millis(); + while (_status == 0 && (millis() - wait < timeout)) { + _elc->Process(); + } + return getResponse2(data, maxLen, totalLength, packetLength); +} + +uint16_t ELClientRest::getResponse2(char* data, uint16_t maxLen, uint16_t *totalLength, uint16_t *packetLength) +{ + if (_status == 0) + return 0; + if (totalLength != 0) + *totalLength = _elc->_longPacket; + if (packetLength != 0) + *packetLength = _len; + memcpy(data, _data, _len>maxLen?maxLen:_len); + int16_t s = _status; + _status = 0; + return s; +} + diff --git a/ELClient/ELClientRest.h b/ELClient/ELClientRest.h index 51546f9..9ff5a9b 100644 --- a/ELClient/ELClientRest.h +++ b/ELClient/ELClientRest.h @@ -67,11 +67,13 @@ class ELClientRest { // Retrieve the response from the remote server, returns the HTTP status code, 0 if no // response (may need to wait longer) uint16_t getResponse(char* data, uint16_t maxLen); + uint16_t getResponse2(char* data, uint16_t maxLen, uint16_t *totalLength, uint16_t *packetLength); // Wait for the response from the remote server, returns the HTTP status code, 0 if no // response (timeout occurred). This is not recommended except for quick demos, use // getResponse periodically instead. uint16_t waitResponse(char* data, uint16_t maxLen, uint32_t timeout=DEFAULT_REST_TIMEOUT); + uint16_t waitResponse2(char* data, uint16_t maxLen, uint16_t *totalLength, uint16_t *packetLength, uint32_t timeout = DEFAULT_REST_TIMEOUT); // Set the user-agent for all subsequent requests void setUserAgent(const char* value);