diff --git a/examples/ArduinoIoTCloud_LED_switch/ArduinoIoTCloud_LED_switch.ino b/examples/ArduinoIoTCloud_LED_switch/ArduinoIoTCloud_LED_switch.ino index 4514c7770..9516eb158 100644 --- a/examples/ArduinoIoTCloud_LED_switch/ArduinoIoTCloud_LED_switch.ino +++ b/examples/ArduinoIoTCloud_LED_switch/ArduinoIoTCloud_LED_switch.ino @@ -5,7 +5,11 @@ When the potentiometer (or sensor) value changes the data is sent to the Cloud. When you flip the switch in the Cloud dashboard the onboard LED lights gets turned ON or OFF. - + IMPORTANT: + This sketch will work with both WiFi and GSM enabled boards supported by Arduino IoT Cloud. + By default, settings for WiFi are chosen. If you prefer to use a GSM board take a look at thingProperties.h arduino_secrets.h, + to make sure you uncomment what's needed and comment incompatible instructions. + */ #include "arduino_secrets.h" #include "thingProperties.h" diff --git a/examples/ArduinoIoTCloud_LED_switch/arduino_secrets.h b/examples/ArduinoIoTCloud_LED_switch/arduino_secrets.h index 783dea956..3f5bb26cf 100644 --- a/examples/ArduinoIoTCloud_LED_switch/arduino_secrets.h +++ b/examples/ArduinoIoTCloud_LED_switch/arduino_secrets.h @@ -1,3 +1,15 @@ +/* + Fill in your login credentials: + + The following lines are used for WiFi enabled boards (MKR1000, MKR WiFi 1010) +*/ #define SECRET_SSID "YOUR_WIFI_NETWORK_NAME" #define SECRET_PASS "YOUR_WIFI_PASSWORD" - +/* + If you prefer using a MKR GSM 1400 comment the lines above and uncommet the following. + PIN, APN, Login and Password are supplied by your Cellular Data provider. +*/ +//#define SECRET_PIN "" +//#define SECRET_APN "" +//#define SECRET_LOGIN "" +//#define SECRET_PASS "" diff --git a/examples/ArduinoIoTCloud_LED_switch/thingProperties.h b/examples/ArduinoIoTCloud_LED_switch/thingProperties.h index a28d03af1..07481610c 100644 --- a/examples/ArduinoIoTCloud_LED_switch/thingProperties.h +++ b/examples/ArduinoIoTCloud_LED_switch/thingProperties.h @@ -1,9 +1,13 @@ #include +/* + The following include line is used for WiFi enabled boards (MKR1000, MKR WiFi 1010) +*/ #include +/* + If you prefer using a MKR GSM 1400 comment the line above and uncommet the following. +*/ +//#include - -char ssid[] = SECRET_SSID; // your network SSID (name) -char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP) // Your THING_ID #define THING_ID "ARDUINO_IOT_CLOUD_THING_ID" @@ -18,4 +22,11 @@ void initProperties() { ArduinoCloud.addProperty(potentiometer, READ, ON_CHANGE); } -ConnectionManager *ArduinoIoTPreferredConnection = new WiFiConnectionManager(SECRET_SSID, SECRET_PASS); \ No newline at end of file +/* + The following include line is used for WiFi enabled boards (MKR1000, MKR WiFi 1010) +*/ +ConnectionManager *ArduinoIoTPreferredConnection = new WiFiConnectionManager(SECRET_SSID, SECRET_PASS); +/* + If you prefer using a MKR GSM 1400 comment the line above and uncommet the following. +*/ +//ConnectionManager *ArduinoIoTPreferredConnection = new GSMConnectionManager(SECRET_PIN, SECRET_APN, SECRET_LOGIN, SECRET_PASS); diff --git a/library.properties b/library.properties index 3b5f37c4e..aedb3868e 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=ArduinoIoTCloud -version=0.5.0 +version=0.5.1 author=Arduino maintainer=Arduino sentence=This library allows to connect to the Arduino IoT Cloud service. diff --git a/src/ArduinoIoTCloud.cpp b/src/ArduinoIoTCloud.cpp index beb566879..fb0ca3758 100644 --- a/src/ArduinoIoTCloud.cpp +++ b/src/ArduinoIoTCloud.cpp @@ -29,7 +29,12 @@ static ConnectionManager *getTimeConnection = NULL; static unsigned long getTime() { if (!getTimeConnection) return 0; - return getTimeConnection->getTime(); + unsigned long time = getTimeConnection->getTime(); + if (!NTPUtils::isTimeValid(time)) { + debugMessage("Bogus NTP time from API, fallback to UDP method", 0); + time = NTPUtils(getTimeConnection->getUDP()).getTime(); + } + return time; } ArduinoIoTCloudClass::ArduinoIoTCloudClass() : @@ -118,7 +123,6 @@ int ArduinoIoTCloudClass::begin(Client& net, String brokerAddress, uint16_t brok // TODO: Find a better way to allow callback into object method - // Begin function for the MQTTClient mqttClientBegin(); @@ -184,41 +188,12 @@ void ArduinoIoTCloudClass::update() update(MAX_RETRIES, RECONNECTION_TIMEOUT); } -bool ArduinoIoTCloudClass::mqttReconnect(int const maxRetries, int const timeout) -{ - // Counter for reconnection retries - int retries = 0; - unsigned long start = millis(); - - // Check for MQTT broker connection, of if maxReties limit is reached - // if MQTTClient is connected , simply do nothing and retun true - while (!_mqttClient->connected() && (retries++ < maxRetries) && (millis() - start < timeout)) { - // int connectError = _mqttClient->connectError(); - - // try establish the MQTT broker connection - connect(); - } - - // It was impossible to establish a connection, return - if ((retries == maxRetries) || (millis() - start >= timeout)) - return false; - - return true; -} - void ArduinoIoTCloudClass::update(int const reconnectionMaxRetries, int const reconnectionTimeoutMs) { connectionCheck(); if(iotStatus != IOT_STATUS_CLOUD_CONNECTED){ return; } - // Method's argument controls - int const maxRetries = (reconnectionMaxRetries > 0) ? reconnectionMaxRetries : MAX_RETRIES; - int const timeout = (reconnectionTimeoutMs > 0) ? reconnectionTimeoutMs : RECONNECTION_TIMEOUT; - - // If the reconnect() culd not establish the connection, return the control to the user sketch - if (!mqttReconnect(maxRetries, timeout)) - return; // MTTQClient connected!, poll() used to retrieve data from MQTT broker _mqttClient->poll(); @@ -320,28 +295,18 @@ void ArduinoIoTCloudClass::connectionCheck() switch (iotStatus) { - case IOT_STATUS_IDLE: - { - int connectionAttempt; - if(connection == NULL){ - connectionAttempt = begin(*_net, _brokerAddress, _brokerPort); - }else{ - connectionAttempt = begin(connection, _brokerAddress, _brokerPort); - } - if(!connectionAttempt){ - debugMessage("Error Starting Arduino Cloud\nTrying again in a few seconds", 0); - setIoTConnectionState(IOT_STATUS_CLOUD_ERROR); - return; - } + case IOT_STATUS_CLOUD_IDLE: setIoTConnectionState(IOT_STATUS_CLOUD_CONNECTING); break; - } case IOT_STATUS_CLOUD_ERROR: debugMessage("Cloud Error. Retrying...", 0); setIoTConnectionState(IOT_STATUS_CLOUD_RECONNECTING); break; case IOT_STATUS_CLOUD_CONNECTED: debugMessage(".", 4, false, true); + if (!_mqttClient->connected()){ + setIoTConnectionState(IOT_STATUS_CLOUD_DISCONNECTED); + } break; case IOT_STATUS_CLOUD_DISCONNECTED: setIoTConnectionState(IOT_STATUS_CLOUD_RECONNECTING); diff --git a/src/ArduinoIoTCloud.h b/src/ArduinoIoTCloud.h index 0bb8294a1..41fa94d65 100644 --- a/src/ArduinoIoTCloud.h +++ b/src/ArduinoIoTCloud.h @@ -43,14 +43,12 @@ typedef struct { extern ConnectionManager *ArduinoIoTPreferredConnection; enum ArduinoIoTConnectionStatus { - IOT_STATUS_IDLE,/* only at start */ IOT_STATUS_CLOUD_IDLE, IOT_STATUS_CLOUD_CONNECTING, IOT_STATUS_CLOUD_CONNECTED, IOT_STATUS_CLOUD_DISCONNECTED, IOT_STATUS_CLOUD_RECONNECTING, IOT_STATUS_CLOUD_ERROR, - IOT_STATUS_ERROR_GENERIC }; class ArduinoIoTCloudClass { @@ -130,7 +128,7 @@ class ArduinoIoTCloudClass { ArduinoIoTConnectionStatus getIoTStatus() { return iotStatus; } void setIoTConnectionState(ArduinoIoTConnectionStatus _newState); private: - ArduinoIoTConnectionStatus iotStatus = IOT_STATUS_IDLE; + ArduinoIoTConnectionStatus iotStatus = IOT_STATUS_CLOUD_IDLE; ConnectionManager *connection; static void onMessage(int length); void handleMessage(int length); diff --git a/src/CloudSerial.cpp b/src/CloudSerial.cpp index 3657321ab..e4e3a16a8 100644 --- a/src/CloudSerial.cpp +++ b/src/CloudSerial.cpp @@ -38,35 +38,30 @@ void CloudSerialClass::end() int CloudSerialClass::available() { - ArduinoCloud.update(); return _rxBuffer.available(); } int CloudSerialClass::availableForWrite() { - ArduinoCloud.update(); return _txBuffer.availableForStore(); } int CloudSerialClass::peek() { - ArduinoCloud.update(); return _rxBuffer.peek(); } int CloudSerialClass::read() { - ArduinoCloud.update(); return _rxBuffer.read_char(); } void CloudSerialClass::flush() { - ArduinoCloud.update(); byte out[CLOUD_SERIAL_TX_BUFFER_SIZE]; int length = 0; @@ -91,7 +86,6 @@ size_t CloudSerialClass::write(const uint8_t data) CloudSerialClass::operator bool() { - ArduinoCloud.update(); return ArduinoCloud.connected(); } diff --git a/src/ConnectionManager.h b/src/ConnectionManager.h index c2376eb2f..f8dc92339 100644 --- a/src/ConnectionManager.h +++ b/src/ConnectionManager.h @@ -21,6 +21,8 @@ #define ARDUINO_CLOUD_DEBUG_LEVEL 2 #include +#include +#include "utility/NTPUtils.h" enum NetworkConnectionState { CONNECTION_STATE_INIT, @@ -38,6 +40,7 @@ class ConnectionManager { virtual void check() = 0; virtual unsigned long getTime() = 0; virtual Client &getClient(); + virtual UDP &getUDP(); virtual NetworkConnectionState getStatus() { return netConnectionState; } @@ -82,14 +85,14 @@ inline void debugMessage(char *_msg, int _debugLevel, bool _timestamp = true, bo if (_debugLevel <= debugMessageLevel) { char prepend[20]; sprintf(prepend, "\n[ %d ] ", millis()); - if(_timestamp) + if(_timestamp){ Serial.print(prepend); + } if(_newline){ Serial.println(_msg); }else{ Serial.print(_msg); } - } } diff --git a/src/EthernetConnectionManager.h b/src/EthernetConnectionManager.h new file mode 100644 index 000000000..9042ed39b --- /dev/null +++ b/src/EthernetConnectionManager.h @@ -0,0 +1,187 @@ +#include "ConnectionManager.h" + +#include +#define BOARD_HAS_ETHERNET + +class EthConnectionManager : public ConnectionManager { +public: + EthConnectionManager(uint8_t *mac, int ss_pin); + + virtual unsigned long getTime(); + virtual void init(); + virtual void check(); + virtual Client &getClient() { return ethClient; }; + virtual UDP &getUDP() { return udp; }; + +private: + + void changeConnectionState(NetworkConnectionState _newState); + const int CHECK_INTERVAL_IDLE = 100; + const int CHECK_INTERVAL_INIT = 100; + const int CHECK_INTERVAL_CONNECTING = 500; + const int CHECK_INTERVAL_GETTIME = 100; + const int CHECK_INTERVAL_CONNECTED = 10000; + const int CHECK_INTERVAL_RETRYING = 5000; + const int CHECK_INTERVAL_DISCONNECTED = 1000; + const int CHECK_INTERVAL_ERROR = 500; + + unsigned long lastConnectionTickTime, lastNetworkStep; + uint8_t* mac; + int ss_pin; + EthernetClient ethClient; + EthernetUDP udp; + int connectionTickTimeInterval; +}; + +#if !defined(BOARD_HAS_WIFI) && !defined(BOARD_HAS_GSM) +static const unsigned long NETWORK_CONNECTION_INTERVAL = 30000; +#endif + +EthConnectionManager::EthConnectionManager(uint8_t *mac, int ss_pin = -1) : + mac(mac), + ss_pin(ss_pin), + lastConnectionTickTime(millis()), + connectionTickTimeInterval(CHECK_INTERVAL_IDLE) { +} + +unsigned long EthConnectionManager::getTime() { + //handled by fallback manager + return lastValidTimestamp + 1; +} + +void EthConnectionManager::init() { +} + +void EthConnectionManager::changeConnectionState(NetworkConnectionState _newState) { + netConnectionState = _newState; + int newInterval = CHECK_INTERVAL_IDLE; + switch (_newState) { + case CONNECTION_STATE_INIT: + newInterval = CHECK_INTERVAL_INIT; + break; + case CONNECTION_STATE_CONNECTING: + newInterval = CHECK_INTERVAL_CONNECTING; + break; + case CONNECTION_STATE_GETTIME: + newInterval = CHECK_INTERVAL_GETTIME; + break; + case CONNECTION_STATE_CONNECTED: + newInterval = CHECK_INTERVAL_CONNECTED; + break; + case CONNECTION_STATE_DISCONNECTED: + newInterval = CHECK_INTERVAL_DISCONNECTED; + + break; + } + connectionTickTimeInterval = newInterval; + lastConnectionTickTime = millis(); +} + +void EthConnectionManager::check() { + char msgBuffer[120]; + unsigned long const now = millis(); + int networkStatus = 0; + if (now - lastConnectionTickTime > connectionTickTimeInterval) { + switch (netConnectionState) { + case CONNECTION_STATE_INIT: + if (ss_pin == -1) { + networkStatus = Ethernet.begin(mac); + } else { + networkStatus = Ethernet.begin(mac, ss_pin); + } + networkStatus = Ethernet.hardwareStatus(); + *msgBuffer = 0; + sprintf(msgBuffer, "Eth hardware status(): %d", networkStatus); + debugMessage(msgBuffer, 2); + if (networkStatus == EthernetNoHardware) { + debugMessage("No Ethernet chip connected", 0); + // don't continue: + changeConnectionState(CONNECTION_STATE_ERROR); + lastConnectionTickTime = now; + return; + } + networkStatus = Ethernet.linkStatus(); + *msgBuffer = 0; + sprintf(msgBuffer, "Eth link status(): %d", networkStatus); + debugMessage(msgBuffer, 2); + if (networkStatus == LinkOFF) { + debugMessage("Failed to configure Ethernet via dhcp", 0); + // don't continue: + changeConnectionState(CONNECTION_STATE_ERROR); + lastConnectionTickTime = now; + return; + } + *msgBuffer = 0; + sprintf(msgBuffer, "Ethernet shield recognized: ID", Ethernet.hardwareStatus()); + debugMessage(msgBuffer, 0); + changeConnectionState(CONNECTION_STATE_CONNECTING); + break; + case CONNECTION_STATE_CONNECTING: + *msgBuffer = 0; + sprintf(msgBuffer, "Connecting via dhcp"); + debugMessage(msgBuffer, 2); + if (ss_pin == -1) { + networkStatus = Ethernet.begin(mac); + } else { + networkStatus = Ethernet.begin(mac, ss_pin); + } + *msgBuffer = 0; + sprintf(msgBuffer, "Ethernet.status(): %d", networkStatus); + debugMessage(msgBuffer, 2); + if (networkStatus == 0) { + *msgBuffer = 0; + sprintf(msgBuffer, "Connection failed"); + debugMessage(msgBuffer, 0); + + *msgBuffer = 0; + sprintf(msgBuffer, "Retrying in \"%d\" milliseconds", connectionTickTimeInterval); + debugMessage(msgBuffer, 2); + //changeConnectionState(CONNECTION_STATE_CONNECTING); + return; + } else { + *msgBuffer = 0; + sprintf(msgBuffer, "Connected!"); + debugMessage(msgBuffer, 2); + changeConnectionState(CONNECTION_STATE_GETTIME); + return; + } + break; + case CONNECTION_STATE_GETTIME: + debugMessage("Acquiring Time from Network", 3); + unsigned long networkTime; + networkTime = getTime(); + *msgBuffer = 0; + sprintf(msgBuffer, "Network Time: %u", networkTime); + debugMessage(msgBuffer, 3); + if(networkTime > lastValidTimestamp){ + lastValidTimestamp = networkTime; + changeConnectionState(CONNECTION_STATE_CONNECTED); + } + break; + case CONNECTION_STATE_CONNECTED: + // keep testing connection + Ethernet.maintain(); + networkStatus = Ethernet.linkStatus(); + *msgBuffer = 0; + sprintf(msgBuffer, "Eth link status(): %d", networkStatus); + debugMessage(msgBuffer, 4); + if (networkStatus != LinkON) { + changeConnectionState(CONNECTION_STATE_DISCONNECTED); + return; + } + *msgBuffer = 0; + sprintf(msgBuffer, "Connected"); + debugMessage(msgBuffer, 2); + break; + case CONNECTION_STATE_DISCONNECTED: + *msgBuffer = 0; + sprintf(msgBuffer, "Connection lost."); + debugMessage(msgBuffer, 0); + debugMessage("Attempting reconnection", 1); + changeConnectionState(CONNECTION_STATE_CONNECTING); + //wifiClient.stop(); + break; + } + lastConnectionTickTime = now; + } +} diff --git a/src/GSMConnectionManager.h b/src/GSMConnectionManager.h index 32c046360..676f924cd 100644 --- a/src/GSMConnectionManager.h +++ b/src/GSMConnectionManager.h @@ -15,7 +15,6 @@ * a commercial license, send an email to license@arduino.cc. */ -#include #include "ConnectionManager.h" class GSMConnectionManager : public ConnectionManager { @@ -26,19 +25,45 @@ class GSMConnectionManager : public ConnectionManager { virtual void init(); virtual void check(); virtual Client &getClient() { return networkClient; }; + virtual UDP &getUDP() { return udp; }; GSMClient networkClient; GSM gsmAccess; GPRS gprs; + GSMUDP udp; + private: + + void changeConnectionState(NetworkConnectionState _newState); + + const int CHECK_INTERVAL_IDLE = 100; + const int CHECK_INTERVAL_INIT = 100; + const int CHECK_INTERVAL_CONNECTING = 500; + const int CHECK_INTERVAL_GETTIME = 666; + const int CHECK_INTERVAL_CONNECTED = 10000; + const int CHECK_INTERVAL_RETRYING = 5000; + const int CHECK_INTERVAL_DISCONNECTED = 1000; + const int CHECK_INTERVAL_ERROR = 500; + + const int MAX_GETTIME_RETRIES = 30; + const char *pin, *apn, *login, *pass; - unsigned long lastNetworkCheck, lastNetworkStep; + unsigned long lastConnectionTickTime; + unsigned long getTimeRetries; + int connectionTickTimeInterval; + }; static const unsigned long NETWORK_CONNECTION_INTERVAL = 30000; GSMConnectionManager::GSMConnectionManager(const char *pin, const char *apn, const char *login, const char *pass) : - pin(pin), apn(apn), login(login), pass(pass), lastNetworkCheck(millis()) { + pin(pin), + apn(apn), + login(login), + pass(pass), + lastConnectionTickTime(millis()), + connectionTickTimeInterval(CHECK_INTERVAL_IDLE), + getTimeRetries(MAX_GETTIME_RETRIES) { } unsigned long GSMConnectionManager::getTime() { @@ -46,30 +71,124 @@ unsigned long GSMConnectionManager::getTime() { } void GSMConnectionManager::init() { - if (gsmAccess.begin(pin) == GSM_READY && gprs.attachGPRS(apn, login, pass) == GPRS_READY) { - Serial.println("GPRS connected"); - int pingResult = gprs.ping("google.com"); - if (pingResult >= 0) { - Serial.print("SUCCESS! RTT = "); - Serial.print(pingResult); - Serial.println(" ms"); - } + char msgBuffer[120]; + if (gsmAccess.begin(pin) == GSM_READY) { + debugMessage("SIM card ok", 2); + gsmAccess.setTimeout(CHECK_INTERVAL_RETRYING); + changeConnectionState(CONNECTION_STATE_CONNECTING); } else { - Serial.println("GPRS not connected"); + debugMessage("SIM not present or wrong PIN", 0); while(1); } } -void GSMConnectionManager::check() { - if (millis() - lastNetworkCheck < NETWORK_CONNECTION_INTERVAL) { - return; +void GSMConnectionManager::changeConnectionState(NetworkConnectionState _newState) { + char msgBuffer[120]; + int newInterval = CHECK_INTERVAL_IDLE; + switch (_newState) { + case CONNECTION_STATE_INIT: + newInterval = CHECK_INTERVAL_INIT; + break; + case CONNECTION_STATE_CONNECTING: + sprintf(msgBuffer, "Connecting to Cellular Network"); + debugMessage(msgBuffer, 2); + newInterval = CHECK_INTERVAL_CONNECTING; + break; + case CONNECTION_STATE_GETTIME: + debugMessage("Acquiring Time from Network", 3); + newInterval = CHECK_INTERVAL_GETTIME; + getTimeRetries = MAX_GETTIME_RETRIES; + break; + case CONNECTION_STATE_CONNECTED: + newInterval = CHECK_INTERVAL_CONNECTED; + break; + case CONNECTION_STATE_DISCONNECTED: + if(netConnectionState == CONNECTION_STATE_CONNECTED){ + debugMessage("Disconnected from Cellular Network", 0); + debugMessage("Attempting reconnection", 0); + }else if(netConnectionState == CONNECTION_STATE_GETTIME){ + debugMessage("Connection to Cellular Network lost during Time acquisition.\nAttempting reconnection", 0); + } + newInterval = CHECK_INTERVAL_DISCONNECTED; + break; + case CONNECTION_STATE_ERROR: + debugMessage("GPRS attach failed\nMake sure the antenna is connected and reset your board.", 0); + break; } + connectionTickTimeInterval = newInterval; + lastConnectionTickTime = millis(); + netConnectionState = _newState; +} - Serial.print("< connectionTickTimeInterval) { + switch (netConnectionState) { + case CONNECTION_STATE_INIT: + init(); + break; + case CONNECTION_STATE_CONNECTING: + // NOTE: Blocking Call when 4th parameter == true + GSM3_NetworkStatus_t networkStatus; + networkStatus = gprs.attachGPRS(apn, login, pass, true); + sprintf(msgBuffer, "GPRS.attachGPRS(): %d", networkStatus); + debugMessage(msgBuffer, 3); + if (networkStatus == GSM3_NetworkStatus_t::ERROR) { + // NO FURTHER ACTION WILL FOLLOW THIS + changeConnectionState(CONNECTION_STATE_ERROR); + return; + } + debugMessage("Sending PING to outer space...", 2); + int pingResult; + pingResult = gprs.ping("google.com"); + sprintf(msgBuffer, "GSM.ping(): %d", pingResult); + debugMessage(msgBuffer, 2); + if (pingResult < 0) { + debugMessage("PING failed", 0); + sprintf(msgBuffer, "Retrying in \"%d\" milliseconds", connectionTickTimeInterval); + debugMessage(msgBuffer, 2); + return; + } else { + sprintf(msgBuffer, "Connected to GPRS Network"); + debugMessage(msgBuffer, 2); + changeConnectionState(CONNECTION_STATE_GETTIME); + return; + } + break; + case CONNECTION_STATE_GETTIME: + debugMessage("Acquiring Time from Network", 3); + unsigned long networkTime; + networkTime = getTime(); + debugMessage(".", 3, false, false); + if(networkTime > lastValidTimestamp){ + lastValidTimestamp = networkTime; + sprintf(msgBuffer, "Network Time: %u", networkTime); + debugMessage(msgBuffer, 3); + changeConnectionState(CONNECTION_STATE_CONNECTED); + }else if(gsmAccess.isAccessAlive() != 1){ + changeConnectionState(CONNECTION_STATE_DISCONNECTED); + }else if (!getTimeRetries--) { + changeConnectionState(CONNECTION_STATE_DISCONNECTED); + } + break; + case CONNECTION_STATE_CONNECTED: + gsmAccessAlive = gsmAccess.isAccessAlive(); + sprintf(msgBuffer, "GPRS.isAccessAlive(): %d", gsmAccessAlive); + debugMessage(msgBuffer, 4); + if (gsmAccessAlive != 1) { + changeConnectionState(CONNECTION_STATE_DISCONNECTED); + return; + } + sprintf(msgBuffer, "Connected to Cellular Network"); + debugMessage(msgBuffer, 4); + break; + case CONNECTION_STATE_DISCONNECTED: + gprs.detachGPRS(); + changeConnectionState(CONNECTION_STATE_CONNECTING); + break; + } + lastConnectionTickTime = now; } } diff --git a/src/WiFiConnectionManager.h b/src/WiFiConnectionManager.h index 4e30cff54..c9915e334 100644 --- a/src/WiFiConnectionManager.h +++ b/src/WiFiConnectionManager.h @@ -16,6 +16,7 @@ */ #include "ConnectionManager.h" +#include class WiFiConnectionManager : public ConnectionManager { public: @@ -25,12 +26,15 @@ class WiFiConnectionManager : public ConnectionManager { virtual void init(); virtual void check(); virtual Client &getClient() { return wifiClient; }; - + virtual UDP &getUDP() { return udp; }; + WiFiUDP udp; + private: void changeConnectionState(NetworkConnectionState _newState); + const int CHECK_INTERVAL_IDLE = 100; const int CHECK_INTERVAL_INIT = 100; const int CHECK_INTERVAL_CONNECTING = 500; const int CHECK_INTERVAL_GETTIME = 666; @@ -39,10 +43,10 @@ class WiFiConnectionManager : public ConnectionManager { const int CHECK_INTERVAL_DISCONNECTED = 1000; const int CHECK_INTERVAL_ERROR = 500; - const int MAX_GETTIME_RETRY = 30; + const int MAX_GETTIME_RETRIES = 30; const char *ssid, *pass; - unsigned long lastConnectionTickTime, lastNetworkStep; + unsigned long lastConnectionTickTime; unsigned long getTimeRetries; WiFiClient wifiClient; @@ -54,8 +58,8 @@ static const unsigned long NETWORK_CONNECTION_INTERVAL = 30000; WiFiConnectionManager::WiFiConnectionManager(const char *ssid, const char *pass) : ssid(ssid), pass(pass), lastConnectionTickTime(millis()), - connectionTickTimeInterval(CHECK_INTERVAL_INIT), - getTimeRetries(MAX_GETTIME_RETRY) { + connectionTickTimeInterval(CHECK_INTERVAL_IDLE), + getTimeRetries(MAX_GETTIME_RETRIES) { } unsigned long WiFiConnectionManager::getTime() { @@ -80,12 +84,12 @@ void WiFiConnectionManager::changeConnectionState(NetworkConnectionState _newSta case CONNECTION_STATE_GETTIME: newInterval = CHECK_INTERVAL_GETTIME; debugMessage("Acquiring Time from Network", 3); + getTimeRetries = MAX_GETTIME_RETRIES; break; case CONNECTION_STATE_CONNECTED: newInterval = CHECK_INTERVAL_CONNECTED; break; case CONNECTION_STATE_DISCONNECTED: - debugMessage("", 3, false, true); sprintf(msgBuffer, "WiFi.status(): %d", WiFi.status()); debugMessage(msgBuffer, 4); sprintf(msgBuffer, "Connection to \"%s\" lost.", ssid); @@ -93,6 +97,10 @@ void WiFiConnectionManager::changeConnectionState(NetworkConnectionState _newSta debugMessage("Attempting reconnection", 0); newInterval = CHECK_INTERVAL_DISCONNECTED; break; + case CONNECTION_STATE_ERROR: + debugMessage("WiFi Hardware failure.\nMake sure you are using a WiFi enabled board/shield.", 0); + debugMessage("Then reset and retry.", 0); + break; } connectionTickTimeInterval = newInterval; lastConnectionTickTime = millis(); @@ -101,15 +109,16 @@ void WiFiConnectionManager::changeConnectionState(NetworkConnectionState _newSta void WiFiConnectionManager::check() { char msgBuffer[120]; - unsigned long now = millis(); + unsigned long const now = millis(); int networkStatus = 0; if (now - lastConnectionTickTime > connectionTickTimeInterval) { switch (netConnectionState) { case CONNECTION_STATE_INIT: networkStatus = WiFi.status(); + sprintf(msgBuffer, "WiFi.status(): %d", networkStatus); + debugMessage(msgBuffer, 2); if (networkStatus == NETWORK_HARDWARE_ERROR) { - debugMessage("WiFi Hardware not available\nMake sure you are using a WiFi enabled board/shield", 0); - // don't continue: + // NO FURTHER ACTION WILL FOLLOW THIS changeConnectionState(CONNECTION_STATE_ERROR); lastConnectionTickTime = now; return; @@ -119,7 +128,7 @@ void WiFiConnectionManager::check() { if(WiFi.firmwareVersion() < WIFI_FIRMWARE_VERSION_REQUIRED){ sprintf(msgBuffer, "Latest WiFi Firmware: %s", WIFI_FIRMWARE_VERSION_REQUIRED); debugMessage(msgBuffer, 0); - debugMessage("Please update to latest version for optimal performance.", 0); + debugMessage("Please update to the latest version for best performance.", 0); delay(5000); } changeConnectionState(CONNECTION_STATE_CONNECTING); @@ -129,10 +138,8 @@ void WiFiConnectionManager::check() { sprintf(msgBuffer, "WiFi.status(): %d", networkStatus); debugMessage(msgBuffer, 4); if (networkStatus != NETWORK_CONNECTED) { - debugMessage("", 3, false, true); sprintf(msgBuffer, "Connection to \"%s\" failed", ssid); debugMessage(msgBuffer, 0); - sprintf(msgBuffer, "Retrying in \"%d\" milliseconds", connectionTickTimeInterval); debugMessage(msgBuffer, 2); //changeConnectionState(CONNECTION_STATE_CONNECTING); @@ -141,15 +148,12 @@ void WiFiConnectionManager::check() { sprintf(msgBuffer, "Connected to \"%s\"", ssid); debugMessage(msgBuffer, 2); changeConnectionState(CONNECTION_STATE_GETTIME); - getTimeRetries = MAX_GETTIME_RETRY; return; } break; case CONNECTION_STATE_GETTIME: - unsigned long networkTime; - networkTime = WiFi.getTime(); - + networkTime = getTime(); debugMessage(".", 3, false, false); if(networkTime > lastValidTimestamp){ debugMessage("", 3, false, true); @@ -176,12 +180,8 @@ void WiFiConnectionManager::check() { debugMessage(msgBuffer, 4); break; case CONNECTION_STATE_DISCONNECTED: - //WiFi.disconnect(); WiFi.end(); - - changeConnectionState(CONNECTION_STATE_CONNECTING); - //wifiClient.stop(); break; } lastConnectionTickTime = now; diff --git a/src/utility/NTPUtils.cpp b/src/utility/NTPUtils.cpp new file mode 100644 index 000000000..5d1129117 --- /dev/null +++ b/src/utility/NTPUtils.cpp @@ -0,0 +1,74 @@ +#include "NTPUtils.h" +#include "Arduino.h" +/* + This Utility Class is derived from the example code found here https://www.arduino.cc/en/Tutorial/UdpNTPClient + For more information on NTP (Network Time Protocol) you can refer to this Wikipedia article https://en.wikipedia.org/wiki/Network_Time_Protocol +*/ + + +// could be a constexpr in C++14 +static time_t cvt_TIME(char const *time) { + char s_month[5]; + int month, day, year; + struct tm t = {0}; + static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; + + sscanf(time, "%s %d %d", s_month, &day, &year); + + month = (strstr(month_names, s_month)-month_names)/3; + + t.tm_mon = month; + t.tm_mday = day; + t.tm_year = year - 1900; + t.tm_isdst = -1; + + return mktime(&t); +} + + +NTPUtils::NTPUtils(UDP& Udp) : Udp(Udp) {} + +bool NTPUtils::isTimeValid(unsigned long time) { + Serial.println("Compile time: " + String(cvt_TIME(__DATE__))); + return (time > cvt_TIME(__DATE__)); +} + +void NTPUtils::sendNTPpacket(uint8_t* packetBuffer) { + memset(packetBuffer, 0, NTP_PACKET_SIZE); + packetBuffer[0] = 0b11100011; + packetBuffer[1] = 0; + packetBuffer[2] = 6; + packetBuffer[3] = 0xEC; + packetBuffer[12] = 49; + packetBuffer[13] = 0x4E; + packetBuffer[14] = 49; + packetBuffer[15] = 52; + Udp.beginPacket(timeServer, 123); + Udp.write(packetBuffer, NTP_PACKET_SIZE); + Udp.endPacket(); +} + +unsigned long NTPUtils::getTime() { + + unsigned int localPort = 8888; + uint8_t packetBuffer[NTP_PACKET_SIZE]; + + Udp.begin(localPort); + sendNTPpacket(packetBuffer); + long start = millis(); + while (!Udp.parsePacket() && (millis() - start < 10000)){} + if (millis() - start >= 1000) { + //timeout reached + return 0; + } + Udp.read(packetBuffer, NTP_PACKET_SIZE); + + unsigned long highWord = word(packetBuffer[40], packetBuffer[41]); + unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]); + unsigned long secsSince1900 = highWord << 16 | lowWord; + const unsigned long seventyYears = 2208988800UL; + unsigned long epoch = secsSince1900 - seventyYears; + + Udp.stop(); + return epoch; +} diff --git a/src/utility/NTPUtils.h b/src/utility/NTPUtils.h new file mode 100644 index 000000000..8a7fe5f68 --- /dev/null +++ b/src/utility/NTPUtils.h @@ -0,0 +1,23 @@ +#ifndef __NTP_UTILS__ +#define __NTP_UTILS__ +/* + This Utility Class is derived from the example code found here https://www.arduino.cc/en/Tutorial/UdpNTPClient + For more information on NTP (Network Time Protocol) you can refer to this Wikipedia article https://en.wikipedia.org/wiki/Network_Time_Protocol +*/ + +#include "Udp.h" +#include + +class NTPUtils { + public: + NTPUtils(UDP& Udp); + void sendNTPpacket(uint8_t* packetBuffer); + unsigned long getTime(); + static bool isTimeValid(unsigned long time); + private: + const char* timeServer = "time.apple.com"; + const int NTP_PACKET_SIZE = 48; + UDP& Udp; +}; + +#endif