diff --git a/.github/workflows/compile-examples.yml b/.github/workflows/compile-examples.yml index 808258e..5541359 100644 --- a/.github/workflows/compile-examples.yml +++ b/.github/workflows/compile-examples.yml @@ -38,6 +38,7 @@ jobs: - name: Blues Wireless Notecard SKETCH_PATHS: | - examples/ConnectionHandlerDemo + - examples/CheckInternetAvailabilityDemo ARDUINOCORE_MBED_STAGING_PATH: extras/ArduinoCore-mbed ARDUINOCORE_API_STAGING_PATH: extras/ArduinoCore-API SKETCHES_REPORTS_PATH: sketches-reports diff --git a/examples/CheckInternetAvailabilityDemo/CheckInternetAvailabilityDemo.ino b/examples/CheckInternetAvailabilityDemo/CheckInternetAvailabilityDemo.ino new file mode 100644 index 0000000..ca530dd --- /dev/null +++ b/examples/CheckInternetAvailabilityDemo/CheckInternetAvailabilityDemo.ino @@ -0,0 +1,152 @@ +/* SECRET_ fields are in `arduino_secrets.h` (included below) + * + * If using a WiFi board (Arduino MKR1000, MKR WiFi 1010, Nano 33 IoT, UNO + * WiFi Rev 2 or ESP8266/32), create a WiFiConnectionHandler object by adding + * Network Name (SECRET_WIFI_SSID) and password (SECRET_WIFI_PASS) in the + * arduino_secrets.h file (or Secrets tab in Create Web Editor). + * + * WiFiConnectionHandler conMan(SECRET_WIFI_SSID, SECRET_WIFI_PASS); + * + * If using a MKR GSM 1400 or other GSM boards supporting the same API you'll + * need a GSMConnectionHandler object as follows + * + * GSMConnectionHandler conMan(SECRET_PIN, SECRET_APN, SECRET_GSM_USER, SECRET_GSM_PASS); + * + * If using a MKR NB1500 you'll need a NBConnectionHandler object as follows + * + * NBConnectionHandler conMan(SECRET_PIN); + * + * If using a Portenta + Ethernet shield you'll need a EthernetConnectionHandler object as follows: + * + * DHCP mode + * EthernetConnectionHandler conMan; + * + * Manual configuration + * EthernetConnectionHandler conMan(SECRET_IP, SECRET_DNS, SECRET_GATEWAY, SECRET_NETMASK); + * + * Manual configuration will fallback on DHCP mode if SECRET_IP is invalid or equal to INADDR_NONE. + * + * This sketch enables the ConnectionHandler to check for internet availability (only for IP based connectivity) + * before reporting the Connected state. By default the check is disabled. + * + */ + +#include + +#include "arduino_secrets.h" + +#define CONN_TOGGLE_MS 60000 + +#if !(defined(BOARD_HAS_WIFI) || defined(BOARD_HAS_GSM) || defined(BOARD_HAS_LORA) || \ + defined(BOARD_HAS_NB) || defined(BOARD_HAS_ETHERNET) || defined(BOARD_HAS_CATM1_NBIOT)) + #error "Please check Arduino Connection Handler supported boards list: https://github.com/arduino-libraries/Arduino_ConnectionHandler/blob/master/README.md" +#endif + +#if defined(BOARD_HAS_ETHERNET) +EthernetConnectionHandler conMan(SECRET_IP, SECRET_DNS, SECRET_GATEWAY, SECRET_NETMASK); +#elif defined(BOARD_HAS_WIFI) +WiFiConnectionHandler conMan(SECRET_WIFI_SSID, SECRET_WIFI_PASS); +#elif defined(BOARD_HAS_GSM) +GSMConnectionHandler conMan(SECRET_PIN, SECRET_APN, SECRET_GSM_USER, SECRET_GSM_PASS); +#elif defined(BOARD_HAS_NB) +NBConnectionHandler conMan(SECRET_PIN); +#elif defined(BOARD_HAS_LORA) +LoRaConnectionHandler conMan(SECRET_APP_EUI, SECRET_APP_KEY); +#elif defined(BOARD_HAS_CATM1_NBIOT) +CatM1ConnectionHandler conMan(SECRET_PIN, SECRET_APN, SECRET_GSM_USER, SECRET_GSM_PASS); +#elif defined(BOARD_HAS_CELLULAR) +CellularConnectionHandler conMan(SECRET_PIN, SECRET_APN, SECRET_GSM_USER, SECRET_GSM_PASS); +#endif + +bool attemptConnect = false; +uint32_t lastConnToggleMs = 0; + +void setup() { + /* Initialize serial debug port and wait up to 5 seconds for port to open */ + Serial.begin(9600); + for(unsigned long const serialBeginTime = millis(); !Serial && (millis() - serialBeginTime <= 5000); ) { } + +#ifndef __AVR__ + /* Set the debug message level: + * - DBG_ERROR: Only show error messages + * - DBG_WARNING: Show warning and error messages + * - DBG_INFO: Show info, warning, and error messages + * - DBG_DEBUG: Show debug, info, warning, and error messages + * - DBG_VERBOSE: Show all messages + */ + setDebugMessageLevel(DBG_INFO); +#endif + /* Enable the connection handler to check for internet availability. + * By default is disabled. + */ + conMan.enableCheckInternetAvailability(true); + /* Add callbacks to the ConnectionHandler object to get notified of network + * connection events. */ + conMan.addCallback(NetworkConnectionEvent::CONNECTED, onNetworkConnect); + conMan.addCallback(NetworkConnectionEvent::DISCONNECTED, onNetworkDisconnect); + conMan.addCallback(NetworkConnectionEvent::ERROR, onNetworkError); + + Serial.print("Network Adapter Interface: "); + switch (conMan.getInterface()) { + case NetworkAdapter::WIFI: + Serial.println("Wi-Fi"); + break; + case NetworkAdapter::ETHERNET: + Serial.println("Ethernet"); + break; + case NetworkAdapter::NB: + Serial.println("Narrowband"); + break; + case NetworkAdapter::GSM: + Serial.println("GSM"); + break; + case NetworkAdapter::LORA: + Serial.println("LoRa"); + break; + case NetworkAdapter::CATM1: + Serial.println("Category M1"); + break; + case NetworkAdapter::CELL: + Serial.println("Cellular"); + break; + default: + Serial.println("Unknown"); + break; + } +} + +void loop() { + /* Toggle the connection every `CONN_TOGGLE_MS` milliseconds */ + if ((millis() - lastConnToggleMs) > CONN_TOGGLE_MS) { + Serial.println("Toggling connection..."); + if (attemptConnect) { + conMan.connect(); + } else { + conMan.disconnect(); + } + attemptConnect = !attemptConnect; + lastConnToggleMs = millis(); + } + + /* The following code keeps on running connection workflows on our + * ConnectionHandler object, hence allowing reconnection in case of failure + * and notification of connect/disconnect event if enabled (see + * addConnectCallback/addDisconnectCallback) NOTE: any use of delay() within + * the loop or methods called from it will delay the execution of .update(), + * which might not guarantee the correct functioning of the ConnectionHandler + * object. + */ + conMan.check(); +} + +void onNetworkConnect() { + Serial.println(">>>> CONNECTED to network"); +} + +void onNetworkDisconnect() { + Serial.println(">>>> DISCONNECTED from network"); +} + +void onNetworkError() { + Serial.println(">>>> ERROR"); +} diff --git a/examples/CheckInternetAvailabilityDemo/arduino_secrets.h b/examples/CheckInternetAvailabilityDemo/arduino_secrets.h new file mode 100644 index 0000000..f9906f6 --- /dev/null +++ b/examples/CheckInternetAvailabilityDemo/arduino_secrets.h @@ -0,0 +1,19 @@ +// Required for WiFiConnectionHandler +const char SECRET_WIFI_SSID[] = "NETWORK NAME"; +const char SECRET_WIFI_PASS[] = "NETWORK PASSWORD"; + +// Required for GSMConnectionHandler +const char SECRET_APN[] = "MOBILE PROVIDER APN ADDRESS"; +const char SECRET_PIN[] = "0000"; // Required for NBConnectionHandler +const char SECRET_GSM_USER[] = "GSM USERNAME"; +const char SECRET_GSM_PASS[] = "GSM PASSWORD"; + +// Required for LoRaConnectionHandler +const char SECRET_APP_EUI[] = "APP_EUI"; +const char SECRET_APP_KEY[] = "APP_KEY"; + +// Required for EthernetConnectionHandler (without DHCP mode) +const char SECRET_IP[] = "IP ADDRESS"; +const char SECRET_DNS[] = "DNS ADDRESS"; +const char SECRET_GATEWAY[] = "GATEWAY ADDRESS"; +const char SECRET_NETMASK[] = "NETWORK MASK"; diff --git a/src/CatM1ConnectionHandler.cpp b/src/CatM1ConnectionHandler.cpp index b739bea..2a63dea 100644 --- a/src/CatM1ConnectionHandler.cpp +++ b/src/CatM1ConnectionHandler.cpp @@ -64,11 +64,7 @@ NetworkConnectionState CatM1ConnectionHandler::update_handleInit() pinMode(ON_MKR2, OUTPUT); digitalWrite(ON_MKR2, HIGH); #endif - return NetworkConnectionState::CONNECTING; -} -NetworkConnectionState CatM1ConnectionHandler::update_handleConnecting() -{ if(!GSM.begin( _settings.catm1.pin, _settings.catm1.apn, @@ -80,8 +76,33 @@ NetworkConnectionState CatM1ConnectionHandler::update_handleConnecting() Debug.print(DBG_ERROR, F("The board was not able to register to the network...")); return NetworkConnectionState::ERROR; } - Debug.print(DBG_INFO, F("Connected to Network")); - return NetworkConnectionState::CONNECTED; + return NetworkConnectionState::CONNECTING; +} + +NetworkConnectionState CatM1ConnectionHandler::update_handleConnecting() +{ + if (!GSM.isConnected()) + { + return NetworkConnectionState::INIT; + } + + if(!_check_internet_availability){ + return NetworkConnectionState::CONNECTED; + } + + int ping_result = GSM.ping("time.arduino.cc"); + Debug.print(DBG_INFO, F("GSM.ping(): %d"), ping_result); + if (ping_result < 0) + { + Debug.print(DBG_ERROR, F("Internet check failed")); + Debug.print(DBG_INFO, F("Retrying in \"%d\" milliseconds"), CHECK_INTERVAL_TABLE[static_cast(NetworkConnectionState::CONNECTING)]); + return NetworkConnectionState::CONNECTING; + } + else + { + Debug.print(DBG_INFO, F("Connected to Internet")); + return NetworkConnectionState::CONNECTED; + } } NetworkConnectionState CatM1ConnectionHandler::update_handleConnected() diff --git a/src/CellularConnectionHandler.cpp b/src/CellularConnectionHandler.cpp index d0877d3..c6a9077 100644 --- a/src/CellularConnectionHandler.cpp +++ b/src/CellularConnectionHandler.cpp @@ -62,16 +62,31 @@ NetworkConnectionState CellularConnectionHandler::update_handleInit() Debug.print(DBG_ERROR, F("SIM not present or wrong PIN")); return NetworkConnectionState::ERROR; } - return NetworkConnectionState::CONNECTING; -} -NetworkConnectionState CellularConnectionHandler::update_handleConnecting() -{ if (!_cellular.connect(String(_settings.cell.apn), String(_settings.cell.login), String(_settings.cell.pass))) { Debug.print(DBG_ERROR, F("The board was not able to register to the network...")); return NetworkConnectionState::ERROR; } Debug.print(DBG_INFO, F("Connected to Network")); + return NetworkConnectionState::CONNECTING; +} + +NetworkConnectionState CellularConnectionHandler::update_handleConnecting() +{ + if (!_cellular.isConnectedToInternet()) { + return NetworkConnectionState::INIT; + } + + if (!_check_internet_availability) { + return NetworkConnectionState::CONNECTED; + } + + if(getTime() == 0){ + Debug.print(DBG_ERROR, F("Internet check failed")); + Debug.print(DBG_INFO, F("Retrying in \"%d\" milliseconds"), CHECK_INTERVAL_TABLE[static_cast(NetworkConnectionState::CONNECTING)]); + return NetworkConnectionState::CONNECTING; + } + return NetworkConnectionState::CONNECTED; } diff --git a/src/ConnectionHandlerDefinitions.h b/src/ConnectionHandlerDefinitions.h index 9297dc0..4cefc5d 100644 --- a/src/ConnectionHandlerDefinitions.h +++ b/src/ConnectionHandlerDefinitions.h @@ -191,12 +191,12 @@ enum class NetworkAdapter { static unsigned int const CHECK_INTERVAL_TABLE[] = { - /* INIT */ 100, #if defined(BOARD_HAS_NOTECARD) || defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) - /* CONNECTING */ 4000, + /* INIT */ 4000, #else - /* CONNECTING */ 500, + /* INIT */ 500, #endif + /* CONNECTING */ 500, /* CONNECTED */ 10000, /* DISCONNECTING */ 100, /* DISCONNECTED */ 1000, diff --git a/src/ConnectionHandlerInterface.cpp b/src/ConnectionHandlerInterface.cpp index 1f62bde..237b465 100644 --- a/src/ConnectionHandlerInterface.cpp +++ b/src/ConnectionHandlerInterface.cpp @@ -29,6 +29,7 @@ ConnectionHandler::ConnectionHandler(bool const keep_alive, NetworkAdapter inter : _keep_alive{keep_alive} , _interface{interface} , _lastConnectionTickTime{millis()} +, _check_internet_availability{false} , _current_net_connection_state{NetworkConnectionState::INIT} { diff --git a/src/ConnectionHandlerInterface.h b/src/ConnectionHandlerInterface.h index 8081960..6af40d4 100644 --- a/src/ConnectionHandlerInterface.h +++ b/src/ConnectionHandlerInterface.h @@ -76,6 +76,9 @@ class ConnectionHandler { virtual void connect(); virtual void disconnect(); + void enableCheckInternetAvailability(bool enable) { + _check_internet_availability = enable; + } virtual void addCallback(NetworkConnectionEvent const event, OnNetworkEventCallback callback); void addConnectCallback(OnNetworkEventCallback callback) __attribute__((deprecated)); @@ -106,6 +109,7 @@ class ConnectionHandler { virtual void updateCallback(NetworkConnectionState next_net_connection_state); bool _keep_alive; + bool _check_internet_availability; NetworkAdapter _interface; virtual NetworkConnectionState update_handleInit () = 0; diff --git a/src/EthernetConnectionHandler.cpp b/src/EthernetConnectionHandler.cpp index af5b118..6e73038 100644 --- a/src/EthernetConnectionHandler.cpp +++ b/src/EthernetConnectionHandler.cpp @@ -72,11 +72,6 @@ NetworkConnectionState EthernetConnectionHandler::update_handleInit() Debug.print(DBG_ERROR, F("Error, ethernet shield was not found.")); return NetworkConnectionState::ERROR; } - return NetworkConnectionState::CONNECTING; -} - -NetworkConnectionState EthernetConnectionHandler::update_handleConnecting() -{ IPAddress ip(_settings.eth.ip.type, _settings.eth.ip.bytes); // An ip address is provided -> static ip configuration @@ -91,7 +86,7 @@ NetworkConnectionState EthernetConnectionHandler::update_handleConnecting() Debug.print(DBG_ERROR, F("Failed to configure Ethernet, check cable connection")); Debug.print(DBG_VERBOSE, "timeout: %d, response timeout: %d", _settings.eth.timeout, _settings.eth.response_timeout); - return NetworkConnectionState::CONNECTING; + return NetworkConnectionState::INIT; } // An ip address is not provided -> dhcp configuration } else { @@ -100,11 +95,37 @@ NetworkConnectionState EthernetConnectionHandler::update_handleConnecting() Debug.print(DBG_VERBOSE, "timeout: %d, response timeout: %d", _settings.eth.timeout, _settings.eth.response_timeout); - return NetworkConnectionState::CONNECTING; + return NetworkConnectionState::INIT; } } - return NetworkConnectionState::CONNECTED; + return NetworkConnectionState::CONNECTING; +} + +NetworkConnectionState EthernetConnectionHandler::update_handleConnecting() +{ + if (Ethernet.linkStatus() == LinkOFF) { + return NetworkConnectionState::INIT; + } + + if (!_check_internet_availability) { + return NetworkConnectionState::CONNECTED; + } + + int ping_result = Ethernet.ping("time.arduino.cc"); + Debug.print(DBG_INFO, F("Ethernet.ping(): %d"), ping_result); + if (ping_result < 0) + { + Debug.print(DBG_ERROR, F("Internet check failed")); + Debug.print(DBG_INFO, F("Retrying in \"%d\" milliseconds"), CHECK_INTERVAL_TABLE[static_cast(NetworkConnectionState::CONNECTING)]); + return NetworkConnectionState::CONNECTING; + } + else + { + Debug.print(DBG_INFO, F("Connected to Internet")); + return NetworkConnectionState::CONNECTED; + } + } NetworkConnectionState EthernetConnectionHandler::update_handleConnected() diff --git a/src/GSMConnectionHandler.cpp b/src/GSMConnectionHandler.cpp index 75f9e53..64b04d0 100644 --- a/src/GSMConnectionHandler.cpp +++ b/src/GSMConnectionHandler.cpp @@ -53,6 +53,8 @@ GSMConnectionHandler::GSMConnectionHandler(const char * pin, const char * apn, c : ConnectionHandler{keep_alive, NetworkAdapter::GSM} { _settings.type = NetworkAdapter::GSM; + // To keep the backward compatibility, the user can call enableCheckInternetAvailability(false) for disabling the check + _check_internet_availability = true; strncpy(_settings.gsm.pin, pin, sizeof(_settings.gsm.pin)-1); strncpy(_settings.gsm.apn, apn, sizeof(_settings.gsm.apn)-1); strncpy(_settings.gsm.login, login, sizeof(_settings.gsm.login)-1); @@ -105,6 +107,10 @@ NetworkConnectionState GSMConnectionHandler::update_handleInit() NetworkConnectionState GSMConnectionHandler::update_handleConnecting() { + if(!_check_internet_availability){ + return NetworkConnectionState::CONNECTED; + } + Debug.print(DBG_INFO, F("Sending PING to outer space...")); int const ping_result = _gprs.ping("time.arduino.cc"); Debug.print(DBG_INFO, F("GPRS.ping(): %d"), ping_result); diff --git a/src/GenericConnectionHandler.cpp b/src/GenericConnectionHandler.cpp index f1f9eb6..ce6c198 100644 --- a/src/GenericConnectionHandler.cpp +++ b/src/GenericConnectionHandler.cpp @@ -37,6 +37,7 @@ bool GenericConnectionHandler::updateSetting(const models::NetworkSetting& s) { if(_ch != nullptr) { _interface = s.type; _ch->setKeepAlive(_keep_alive); + _ch->enableCheckInternetAvailability(_check_internet_availability); return _ch->updateSetting(s); } else { _interface = NetworkAdapter::NONE; diff --git a/src/WiFiConnectionHandler.cpp b/src/WiFiConnectionHandler.cpp index efc30fa..37d3ff6 100644 --- a/src/WiFiConnectionHandler.cpp +++ b/src/WiFiConnectionHandler.cpp @@ -96,11 +96,7 @@ NetworkConnectionState WiFiConnectionHandler::update_handleInit() #else WiFi.mode(WIFI_STA); #endif /* #if !defined(ARDUINO_ARCH_ESP8266) && !defined(ARDUINO_ARCH_ESP32) */ - return NetworkConnectionState::CONNECTING; -} -NetworkConnectionState WiFiConnectionHandler::update_handleConnecting() -{ if (WiFi.status() != WL_CONNECTED) { WiFi.begin(_settings.wifi.ssid, _settings.wifi.pwd); @@ -118,9 +114,9 @@ NetworkConnectionState WiFiConnectionHandler::update_handleConnecting() { #if !defined(__AVR__) Debug.print(DBG_ERROR, F("Connection to \"%s\" failed"), _settings.wifi.ssid); - Debug.print(DBG_INFO, F("Retrying in \"%d\" milliseconds"), CHECK_INTERVAL_TABLE[static_cast(NetworkConnectionState::CONNECTING)]); + Debug.print(DBG_INFO, F("Retrying in \"%d\" milliseconds"), CHECK_INTERVAL_TABLE[static_cast(NetworkConnectionState::INIT)]); #endif - return NetworkConnectionState::CONNECTING; + return NetworkConnectionState::INIT; } else { @@ -130,8 +126,33 @@ NetworkConnectionState WiFiConnectionHandler::update_handleConnecting() #if defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) configTime(0, 0, "time.arduino.cc", "pool.ntp.org", "time.nist.gov"); #endif + return NetworkConnectionState::CONNECTING; + } +} + +NetworkConnectionState WiFiConnectionHandler::update_handleConnecting() +{ + if (WiFi.status() != WL_CONNECTED){ + return NetworkConnectionState::INIT; + } + + if(!_check_internet_availability){ return NetworkConnectionState::CONNECTED; } + + #if !defined(ARDUINO_ARCH_ESP8266) && !defined(ARDUINO_ARCH_ESP32) + int ping_result = WiFi.ping("time.arduino.cc"); + Debug.print(DBG_INFO, F("WiFi.ping(): %d"), ping_result); + if (ping_result < 0) + { + Debug.print(DBG_ERROR, F("Internet check failed")); + Debug.print(DBG_INFO, F("Retrying in \"%d\" milliseconds"), CHECK_INTERVAL_TABLE[static_cast(NetworkConnectionState::CONNECTING)]); + return NetworkConnectionState::CONNECTING; + } + #endif + Debug.print(DBG_INFO, F("Connected to Internet")); + return NetworkConnectionState::CONNECTED; + } NetworkConnectionState WiFiConnectionHandler::update_handleConnected()