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