Skip to content

Add GSM and Ethernet conn_manager + add fallback UDP based getTime() #31

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 23 commits into from
Feb 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
db14c67
GSM: implement full state machine
facchinm Feb 12, 2019
68d2c3c
GSM: add fallback getTime retrieve
facchinm Feb 12, 2019
1324ac6
Merge branch 'master' into gsm_proper_connmanager
facchinm Feb 13, 2019
19c688e
Create NTP util class to handle getTime() fallback
facchinm Feb 13, 2019
e0abeb8
Restructure GSM connection manager
facchinm Feb 13, 2019
75ddfcc
Automatically handle getTime fallback in IoTCloud class
facchinm Feb 13, 2019
da7179b
Add EthernetConnectionManager
facchinm Feb 13, 2019
e01a981
[EthConnectionManager] Make "now" const
aentinger Feb 18, 2019
4014095
- added link to NPT on Arduino Tutorial and WikiPedia
ubidefeo Feb 20, 2019
a243573
- refactor GSMConnectionManager
ubidefeo Feb 21, 2019
096022a
- cleanup
ubidefeo Feb 21, 2019
98efd08
- cleanup WiFi and GSM ConnectionManager
ubidefeo Feb 21, 2019
e905238
- whitespace cleanup
ubidefeo Feb 21, 2019
8646505
- more cleanup and minor refactoring
ubidefeo Feb 21, 2019
8eefcc0
- reworked sentence
ubidefeo Feb 21, 2019
e4e11e5
- extended example with GSM compatibility options
ubidefeo Feb 21, 2019
20cbb75
Applying suggestions by A. Catozzi upon internal review
ubidefeo Feb 22, 2019
7fc775b
- switch state cleanup in ArduinoIoTCloud
ubidefeo Feb 23, 2019
bff994e
Merge branch 'master' into gsm_proper_connmanager
ubidefeo Feb 23, 2019
ae7dae9
- applied per1234's suggestions on #31
ubidefeo Feb 24, 2019
f86f04f
- one more missing from per1234's list
ubidefeo Feb 24, 2019
961a71c
- implemented some of the changes requested by @ilcato on PR #37
ubidefeo Feb 25, 2019
fb161bd
- `ArduinoIoTConnectionStatus` cleanup as per @ilcato 's suggestion
ubidefeo Feb 25, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
14 changes: 13 additions & 1 deletion examples/ArduinoIoTCloud_LED_switch/arduino_secrets.h
Original file line number Diff line number Diff line change
@@ -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 ""
19 changes: 15 additions & 4 deletions examples/ArduinoIoTCloud_LED_switch/thingProperties.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
#include <ArduinoIoTCloud.h>
/*
The following include line is used for WiFi enabled boards (MKR1000, MKR WiFi 1010)
*/
#include <WiFiConnectionManager.h>
/*
If you prefer using a MKR GSM 1400 comment the line above and uncommet the following.
*/
//#include <GSMConnectionManager.h>


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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Your THING_ID
// Enter your THING_ID here

#define THING_ID "ARDUINO_IOT_CLOUD_THING_ID"

Expand All @@ -18,4 +22,11 @@ void initProperties() {
ArduinoCloud.addProperty(potentiometer, READ, ON_CHANGE);
}

ConnectionManager *ArduinoIoTPreferredConnection = new WiFiConnectionManager(SECRET_SSID, SECRET_PASS);
/*
The following include line is used for WiFi enabled boards (MKR1000, MKR WiFi 1010)
*/
ConnectionManager *ArduinoIoTPreferredConnection = new WiFiConnectionManager(SECRET_SSID, SECRET_PASS);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A quick side note: Everything that follows is a suggestion for improvement and must not necessarily implemented within this PR. We can (and probably should) do cleanups in further PRs after this is merged.

A misconception, for which I've fallen myself until quite recently, is that if you instantiate derived classes with virtual functions in it (C++ runtime polymorphism) you always need to use the keyword new, effectively allocating the memory on the heap at runtime. Interestingly enough, the following statement also works:

Suggested change
ConnectionManager *ArduinoIoTPreferredConnection = new WiFiConnectionManager(SECRET_SSID, SECRET_PASS);
WiFiConnectionManager connection_mgr(SECRET_SSID, SECRET_PASS);

Doing it this way the memory for the object is allocated on the stack and not on the heap (static memory allocation vs dynamic memory allocation - prefer the former whenever possible/feasible). The reason above statement works is because of a C++ "feature" called object slicing.

Now object slicing has some pitfalls to it but it can be used effectively in this scenario where we have an abstract base class (= one or more pure virtual functions in it) ConnectionManager from which WiFiConnectionManager and GSMConnectionManager are derived. ConnectionManager serves to define the interface for the derived classes and only those functions defined within ConnectionManager are called by users of the derived classes.

When passing the connection around to various classes you then have to use pass-by-reference.

void myFunc(ConnectionManager & connection_mgr) { ...

It´s also possible to store a reference of it as a class member variable.

class MyClass {
public:
  void MyClass(ConnectionManager & connection_mgr) : _connection_mgr(connection_mgr) { ... }
  void myMemberFunc();
private:
  ConnectionManager & _connection_mgr;
};

The ConnectionManager can than be accessed within member functions via the . operator instead of -> as would be the case if the ConnectionManager would be stored as a pointer (ConnectionManager * _connection_mgr;)

void MyClass:myMemberFunc() {
  _connection_mgr.check();
}

Furthermore I'd also suggest to keep variable names in lowercase so to not confuse them with class/type names.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting...
I'm gonna level up on this stuff for the next version.
Thanks, @lxrobotics

I'll proceed to merge this PR in :)

/*
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);
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=ArduinoIoTCloud
version=0.5.0
version=0.5.1
author=Arduino
maintainer=Arduino <[email protected]>
sentence=This library allows to connect to the Arduino IoT Cloud service.
Expand Down
55 changes: 10 additions & 45 deletions src/ArduinoIoTCloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() :
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down
4 changes: 1 addition & 3 deletions src/ArduinoIoTCloud.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,12 @@ typedef struct {
extern ConnectionManager *ArduinoIoTPreferredConnection;

enum ArduinoIoTConnectionStatus {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be an idea to convert the enum into a enum class?

Suggested change
enum ArduinoIoTConnectionStatus {
enum class CloudConnectionState {
Idle,
Connecting,
Connected,
...

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lxrobotics
I've been looking up enum class and I think it's a great way to go, but not for this PR.
We're awaiting on this merge to move forward and release MKR GSM support which after this is merged in will be ready.
this will definitely end up in the next update :)

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 {
Expand Down Expand Up @@ -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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case you want to go ahead with an enum class:

Suggested change
ArduinoIoTConnectionStatus iotStatus = IOT_STATUS_CLOUD_IDLE;
CloudConnectionState _iot_cloud_state = CloudConnectionState::Idle;

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above

ConnectionManager *connection;
static void onMessage(int length);
void handleMessage(int length);
Expand Down
6 changes: 0 additions & 6 deletions src/CloudSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -91,7 +86,6 @@ size_t CloudSerialClass::write(const uint8_t data)

CloudSerialClass::operator bool()
{
ArduinoCloud.update();

return ArduinoCloud.connected();
}
Expand Down
7 changes: 5 additions & 2 deletions src/ConnectionManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#define ARDUINO_CLOUD_DEBUG_LEVEL 2

#include <Client.h>
#include <Udp.h>
#include "utility/NTPUtils.h"

enum NetworkConnectionState {
CONNECTION_STATE_INIT,
Expand All @@ -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; }

Expand Down Expand Up @@ -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);
}

}
}

Expand Down
Loading