diff --git a/ELClient/ELClient.h b/ELClient/ELClient.h
index 5a9c73d..b62aec8 100644
--- a/ELClient/ELClient.h
+++ b/ELClient/ELClient.h
@@ -24,12 +24,15 @@ 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_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 */
@@ -40,6 +43,14 @@ typedef enum {
CMD_SOCKET_SETUP = 40, /**< Setup socket connection */
CMD_SOCKET_SEND, /**< Send socket packet */
+
+ 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! */
enum WIFI_STATUS {
diff --git a/ELClient/ELClientCmd.cpp b/ELClient/ELClientCmd.cpp
index 87cd03d..9ffa3ee 100644
--- a/ELClient/ELClientCmd.cpp
+++ b/ELClient/ELClientCmd.cpp
@@ -36,3 +36,208 @@ 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;
+}
+
+/*! 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()
+@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);
+ }
+
+ 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;
+}
+
+/*
+ * Query RSSI (signal strength)
+ */
+int ELClientCmd::GetRSSI(int i) {
+ char x = i;
+ _elc->Request(CMD_WIFI_SIGNAL_STRENGTH, 0, 1);
+ _elc->Request(&x, 1);
+ _elc->Request();
+
+ 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;
+}
+
+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 eb7eec5..19d02d4 100644
--- a/ELClient/ELClientCmd.h
+++ b/ELClient/ELClientCmd.h
@@ -17,8 +17,30 @@ 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 *);
+ void SetWifiInfo(uint32_t, uint32_t, uint32_t);
+ uint32_t GetWifiApCount();
+ char * GetWifiApName(int);
+ 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();
+ void StartScan();
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];
+
+ 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
new file mode 100644
index 0000000..5d5c9dd
--- /dev/null
+++ b/ELClient/examples/get-info/get-info.ino
@@ -0,0 +1,82 @@
+#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;
+
+#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);
+ 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);
+
+ 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 : ");
+ 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("");
+
+ // Query the MQTT clientid
+ Serial.print("MQTT client id : ");
+ char *mqtt_clientid = cmd.mqttGetClientId();
+ Serial.println(mqtt_clientid);
+
+}
+
+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;
+ }
+ }
+}
+
diff --git a/ELClient/examples/switchwifi/Dyndns.cpp b/ELClient/examples/switchwifi/Dyndns.cpp
new file mode 100644
index 0000000..6531fd1
--- /dev/null
+++ b/ELClient/examples/switchwifi/Dyndns.cpp
@@ -0,0 +1,156 @@
+/*
+ * 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);
+ // Serial.print("Query "); Serial.println(msg1);
+
+ 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);
+ // Serial.print("Header "); Serial.println(msg2);
+
+ 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);
+
+ // Serial.print("Error code "); Serial.println(err); Serial.println(buffer);
+
+ 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/switchwifi/Dyndns.h b/ELClient/examples/switchwifi/Dyndns.h
new file mode 100644
index 0000000..28dd392
--- /dev/null
+++ b/ELClient/examples/switchwifi/Dyndns.h
@@ -0,0 +1,46 @@
+/*
+ * 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_
+
+// #include "item.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/switchwifi/Makefile b/ELClient/examples/switchwifi/Makefile
new file mode 100644
index 0000000..8af3f37
--- /dev/null
+++ b/ELClient/examples/switchwifi/Makefile
@@ -0,0 +1,206 @@
+#====================================================================================
+#
+# 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/switchwifi/switchwifi.ino
+#EXTRA_DEFINES= -DBUILT_BY_MAKE
+EXTRA_SRC= Dyndns.cpp UpnpIgd.cpp
+UPLOAD_HOST= unowifi
+UPLOAD_PORT= /dev/ttyACM0
+
+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/switchwifi/UpnpIgd.cpp b/ELClient/examples/switchwifi/UpnpIgd.cpp
new file mode 100644
index 0000000..78b9747
--- /dev/null
+++ b/ELClient/examples/switchwifi/UpnpIgd.cpp
@@ -0,0 +1,323 @@
+/*
+ * 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 ");
+ int rt = resp_type; Serial.println(rt);
+ // Serial.println(resp_type);
+ Serial.print("udpCb-data "); Serial.println(data);
+
+ // if (len > 0 && resp_type == USERCB_RECV) {
+ if (len > 0) {
+ // 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
+}
+
+void UpnpIgd::startSSDPListener() {
+ udpSocket = new ELClientSocket(&esp);
+ // int err = udpSocket->begin("239.255.255.250", 1900, SOCKET_UDP, udpCb);
+ int err = udpSocket->begin("192.168.1.150", 1900, SOCKET_UDP, udpCb);
+
+}
+
+/*
+
+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
+
+76
+
+GET /o8ee3npj36j/IGD/upnp/IGD.xml HTTP/1.1
+Host: 192.168.1.1:8000
+Connection: Close
+User-Agent: Ubuntu/16.04, UPnP/1.1, MiniUPnPc/2.0
+
+77+79+81+83
+
+HTTP/1.0 200 OK
+Cache-Control: no-cache
+Expires: -1
+Date: Sat, 25 Mar 2017 17:54:40 GMT
+Content-Type: text/xml
+
+
+
+
+1
+0
+
+http://192.168.1.1:8000
+
+ urn:schemas-upnp-org:device:InternetGatewayDevice:1
+ MediaAccess TG789Ovn Xtream (1332VAERL)
+ Technicolor
+ Technicolor Internet Gateway Device
+ MediaAccess TG
+ 789Ovn Xtream
+ http://www.technicolor.com
+ 1332VAERL
+ uuid:c5eb6a02-c0b8-5afe-83da-3965c9516822
+ /
+
+
+ urn:schemas-upnp-org:service:Layer3Forwarding:1
+ urn:upnp-org:serviceId:L3Forwarding1
+ /o8ee3npj36j/IGD/upnp/control/igd/layer3f
+ /o8ee3npj36j/IGD/upnp/event/igd/layer3f
+ /o8ee3npj36j/IGD/upnp/Layer3Forwarding.xml
+
+
+
+
+ urn:schemas-upnp-org:device:WANDevice:1
+ WANDevice
+ Technicolor
+ MediaAccess TG789Ovn Xtream
+ 9C-97-26-26-44-DE
+ uuid:ffd2ddd3-8a99-5e2e-aa8f-a413c4e8ba5f
+
+
+ urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1
+ urn:upnp-org:serviceId:WANCommonIFC1
+ /o8ee3npj36j/IGD/upnp/control/igd/wancic_1
+ /o8ee3npj36j/IGD/upnp/event/igd/wancic_1
+ /o8ee3npj36j/IGD/upnp/WANCommonInterfaceConfig.xml
+
+
+
+
+ urn:schemas-upnp-org:device:WANConnectionDevice:1
+ WANConnectionDevice
+ Technicolor
+ MediaAccess TG789Ovn Xtream
+ 9C-97-26-26-44-DE
+ uuid:33cc1d7c-edd5-54ab-a063-03555e86fe8e
+
+
+ urn:schemas-upnp-org:service:WANPPPConnection:1
+ urn:upnp-org:serviceId:WANPPPConn1
+ /o8ee3npj36j/IGD/upnp/control/igd/wanpppc_1_1_1
+ /o8ee3npj36j/IGD/upnp/event/igd/wanpppc_1_1_1
+ /o8ee3npj36j/IGD/upnp/WANPPPConnection.xml
+
+
+
+
+
+
+
+
+
+ 90
+
+ 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: 272
+ Content-Type: text/xml
+ SOAPAction: "urn:schemas-upnp-org:service:WANPPPConnection:1#GetStatusInfo"
+ Connection: Close
+ Cache-Control: no-cache
+ Pragma: no-cache
+
+
+
+
+
+ 91+93+95
+
+ HTTP/1.0 200 OK
+ Connection: close
+ Date: Sat, 25 Mar 2017 16:54:40 GMT
+ Server: MediaAccess TG 789Ovn Xtream 10.A.0.I UPnP/1.0 (9C-97-26-26-44-DE)
+ Content-Length: 449
+ Content-Type: text/xml; charset="utf-8"
+ EXT:
+
+
+
+
+ ConnectedERROR_IP_CONFIGURATION93343
+
+
+
+
+
+ 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
+
+
+ 312,314,316,318
+
+
+
+
+
+
+
+ /* */
diff --git a/ELClient/examples/switchwifi/UpnpIgd.h b/ELClient/examples/switchwifi/UpnpIgd.h
new file mode 100644
index 0000000..4ce3d84
--- /dev/null
+++ b/ELClient/examples/switchwifi/UpnpIgd.h
@@ -0,0 +1,40 @@
+/*
+ * 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();
+ void startSSDPListener();
+
+private:
+ ELClientSocket *udpSocket;
+ static void udpCb(uint8_t, uint8_t, uint16_t, char *);
+};
+
+#endif
diff --git a/ELClient/examples/switchwifi/secret.h.sample b/ELClient/examples/switchwifi/secret.h.sample
new file mode 100644
index 0000000..f410078
--- /dev/null
+++ b/ELClient/examples/switchwifi/secret.h.sample
@@ -0,0 +1,12 @@
+// Define the SSID and password of your home access point here
+#define HOME_SSID "HomeSSIDName"
+#define HOME_PASS "123654789"
+
+// Define SSID+password of another access point here
+#define REMOTE_SSID "ThisMayBeAPublicAP"
+#define REMOTE_PASS "passalready"
+
+// The hostname as which this esp-link should be advertised on noip.com
+#define NOIP_HOSTNAME "your-host.hopto.org"
+// the AUTH is the base64 of user:password
+#define NOIP_AUTH "the-base64-passcode"
diff --git a/ELClient/examples/switchwifi/switchwifi.ino b/ELClient/examples/switchwifi/switchwifi.ino
new file mode 100644
index 0000000..9a689c5
--- /dev/null
+++ b/ELClient/examples/switchwifi/switchwifi.ino
@@ -0,0 +1,305 @@
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+// This contains the WiFi SSID and passwords
+#include "secret.h"
+
+const int buffer_size = 80;
+const int loop_delay = 50;
+
+time_t boot_time = 0, nowts, change_time;
+char buffer[buffer_size];
+uint32_t ip, nm, gw;
+
+int busy = 1;
+
+ELClient esp(&Serial, &Serial);
+ELClientCmd cmd(&esp);
+ELClientUPnP upnp(&esp);
+ELClientMqtt mqtt(&esp);
+
+// Forward
+void mqData(void *response);
+void MqttSetup(), MqttSubscribe(), MqttHostname();
+void WifiStatusCallback(void *response);
+void NoIP();
+void Punch();
+
+// Reboot the Arduino
+void (*resetFunc) (void) = 0; // Declare reset function @ address 0
+
+#define IP(ip, x) ((int)((ip >> (8*x)) & 0xFF))
+
+// Helper function because the ELClientCmd doesn't have a static function for this.
+time_t mygettime() {
+ return cmd.GetTime();
+}
+
+void resetCb(void) {
+ Serial.println("EL-Client (re)starting");
+}
+
+void setup() {
+ // delay(2000);
+ Serial.begin(115200);
+ Serial.print("Starting wifi ");
+
+ esp.resetCb = resetCb;
+
+ while (! esp.Sync()) {
+ Serial.print(".");
+ }
+ Serial.println(" ok");
+
+ // Put me on the home network, this can save me from reflashing the ESP completely
+ cmd.SelectSSID(HOME_SSID, HOME_PASS);
+
+ Serial.print("Get IP address ");
+ // Share our IP address
+ uint32_t ip, nm, gw;
+ cmd.GetWifiInfo(&ip, &nm, &gw);
+
+ // After SelectSSID, wait for valid IP
+ while (IP(ip, 0) == 0) {
+ Serial.print(".");
+ delay(1000);
+ cmd.GetWifiInfo(&ip, &nm, &gw);
+ }
+ Serial.print(" ok -> ");
+
+ sprintf(buffer, "IP %d.%d.%d.%d", IP(ip, 0), IP(ip, 1), IP(ip, 2), IP(ip, 3));
+ Serial.println(buffer);
+ mqtt.publish("espmega", (char *)buffer);
+
+ Serial.print("MQTT ... ");
+ MqttSetup();
+ MqttSubscribe();
+ // MqttHostname();
+ Serial.println("done");
+
+ // Time
+ setSyncProvider(mygettime); // Set the function to get time from the ESP's esp-link
+ if (timeStatus() != timeSet)
+ Serial.println("RTC failure");
+ else {
+ Serial.print("RTC ok, ");
+ boot_time = now();
+ sprintf(buffer, "%02d:%02d:%02d %02d/%02d/%04d",
+ hour(), minute(), second(), day(), month(), year());
+ Serial.println(buffer);
+ }
+
+ NoIP();
+
+ // Punch();
+
+ // Get status changes after initial connect
+ // esp.wifiCb.attach(WifiStatusCallback);
+
+ // Allow for status change function to work
+ busy = 0;
+}
+
+void Act() {
+ change_time = nowts;
+ Serial.print("Act() - ");
+ sprintf(buffer, "%02d:%02d:%02d", hour(), minute(), second());
+ Serial.println(buffer);
+
+ // Select a network
+ cmd.SelectSSID(REMOTE_SSID, REMOTE_PASS);
+}
+
+void Reset() {
+ Serial.print("Reset() - ");
+ sprintf(buffer, "%02d:%02d:%02d", hour(), minute(), second());
+ Serial.println(buffer);
+
+ // Select a network
+ cmd.SelectSSID(HOME_SSID, HOME_PASS);
+}
+
+void Report() {
+ // Get our IP address
+ uint32_t ip, nm, gw;
+ cmd.GetWifiInfo(&ip, &nm, &gw);
+
+ sprintf(buffer, ", IP %d.%d.%d.%d, %02d:%02d:%02d",
+ IP(ip, 0), IP(ip, 1), IP(ip, 2), IP(ip, 3),
+ hour(), minute(), second());
+ mqtt.publish("espmega", (char *)buffer);
+}
+
+void loop() {
+ esp.Process();
+ nowts = now();
+
+ if (change_time != 0 && (nowts - change_time) > 110) {
+ Report();
+ } else if (change_time != 0 && (nowts - change_time) > 120) {
+ Reset();
+ change_time = 0;
+ }
+ delay(loop_delay);
+}
+
+void MqttSetup() {
+ mqtt.setup();
+}
+
+void MqttSubscribe() {
+ // Subscribe to MQTT messages
+ mqtt.subscribe("/wifi/query");
+ mqtt.subscribe("/wifi/select");
+ mqtt.dataCb.attach(mqData);
+}
+
+void MqttHostname() {
+ // Query MQTT hostname
+ Serial.print("hostname {");
+ char *mqtt_clientid = cmd.mqttGetClientId();
+ Serial.print(mqtt_clientid);
+ Serial.print("} ");
+}
+
+// This catches commands via MQTT
+void mqData(void *response) {
+ ELClientResponse *res = (ELClientResponse *)response;
+ String topic = res->popString();
+ String data = res->popString();
+
+ Serial.print("MQTT query(");
+ Serial.print(topic);
+ Serial.print(") data (");
+ Serial.print(data);
+ Serial.println(")");
+
+ if (strcmp(data.c_str(), "back") == 0) {
+ mqtt.publish("espmega", "Changing to home network");
+ Reset();
+ } else if (strcmp(data.c_str(), "remote") == 0) {
+ mqtt.publish("espmega", "Changing to remote AP");
+ Act();
+ } else if (strcmp(data.c_str(), "reboot") == 0) {
+ mqtt.publish("espmega", "Rebooting Arduino ...");
+ resetFunc();
+ } else if (strcmp(data.c_str(), "ping") == 0) {
+ mqtt.publish("espmega", "Hello, cruel world !");
+ } else if (strcmp(data.c_str(), "noip") == 0) {
+ NoIP();
+ } else if (strcmp(data.c_str(), "punch") == 0) {
+ mqtt.publish("espmega", "Punching a hole through the firewall");
+ Punch();
+ } else {
+ mqtt.publish("espmega", "Changing to remote AP");
+ Act();
+ }
+}
+
+// This can be used to detect connectivity changes
+void WifiStatusCallback(void *response) {
+ ELClientResponse *res = (ELClientResponse *)response;
+
+ if (busy == 1)
+ return;
+ busy = 1;
+
+ NoIP();
+
+ if (res->argc() == 1) {
+ uint8_t status;
+ res->popArg(&status, 1);
+
+ if (status == STATION_GOT_IP) {
+ Serial.println("Network status change\n");
+#if 1
+ // Share our IP address
+ uint32_t ip, nm, gw;
+ cmd.GetWifiInfo(&ip, &nm, &gw);
+
+ sprintf(buffer, ", IP %d.%d.%d.%d", IP(ip, 0), IP(ip, 1), IP(ip, 2), IP(ip, 3));
+ Serial.println(buffer);
+#endif
+ }
+ }
+
+ busy = 0;
+}
+
+void NoIP() {
+ Serial.print("Registering with no-ip.com ... ");
+ Dyndns *d = new Dyndns();
+ d->setHostname(NOIP_HOSTNAME);
+ d->setAuth(NOIP_AUTH); // base64 of user:password
+ d->update();
+ Serial.println("done");
+}
+
+int in_punch = 0;
+
+void Punch() {
+ int i;
+
+ if (in_punch)
+ return;
+ in_punch++;
+
+ cmd.GetWifiInfo(&ip, &nm, &gw);
+ sprintf(buffer, "Preparing punching : local %d.%d.%d.%d %u remote %u",
+ IP(ip, 0), IP(ip, 1), IP(ip, 2), IP(ip, 3), 80, 43000);
+ mqtt.publish("espmega", buffer); delay(500);
+ // UPnP
+ Serial.print("UPnP SCAN ");
+ upnp.begin();
+ mqtt.publish("espmega", "after upnp.begin()"); delay(500);
+ uint32_t igd;
+#if 1
+ i=0;
+ while ((igd = upnp.scan()) == 0 && i++ < 5) {
+ delay(1000);
+ Serial.print(".");
+ }
+ Serial.print(" done, router is at ");
+#else
+ while ((igd = upnp.scan()) == 0) {
+ delay(10000);
+ Serial.print(".");
+ }
+ Serial.print(" done, router is at ");
+#endif
+ sprintf(buffer, "%d.%d.%d.%d, ", IP(igd, 0), IP(igd, 1), IP(igd, 2), IP(igd, 3));
+ Serial.print(buffer);
+#if 1
+ uint32_t ext = upnp.getExternalAddress();
+ while (ext == 0) {
+ delay(1000);
+ ext = upnp.getExternalAddress();
+ }
+ sprintf(buffer, "external IP %d.%d.%d.%d", IP(ext, 0), IP(ext, 1), IP(ext, 2), IP(ext, 3));
+ Serial.println(buffer);
+ mqtt.publish("espmega", buffer); delay(500);
+
+ Serial.print("Signal strength : ");
+ int rssi = cmd.GetRSSI(-1);
+ Serial.println(rssi);
+#endif
+ Serial.print("UPnP Add Port mapping ... ");
+ mqtt.publish("espmega", "add port mapping ... "); delay(500);
+ // upnp.add(0xC0A80196, 0x1234, 0x8765);
+ upnp.add(ip, 80, 43000);
+ sprintf(buffer, "Punching : local %d.%d.%d.%d %u remote %u",
+ IP(ip, 0), IP(ip, 1), IP(ip, 2), IP(ip, 3), 80, 43000);
+ mqtt.publish("espmega", buffer); delay(500);
+ Serial.println("done");
+
+ in_punch--;
+}