Skip to content

[CM-123] VerneMQ integration #7

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

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c9764bf
Integrate ArduinoCloudThing
facchinm Feb 16, 2018
4a52413
Fix .readOnly/.writeOnly compisition
facchinm Feb 22, 2018
e626ea7
Add addProperty API with complete signature
facchinm May 30, 2018
f5f4399
Initial testing on OTA
facchinm May 30, 2018
ba53001
Port to fixed ArduinoCloudThing
facchinm May 30, 2018
cb76f6f
Add missing libraries
facchinm Jun 7, 2018
8bf9cbc
Fix addProperty macro, again
facchinm Jun 27, 2018
fcffb1b
Fix reference as value in addProperty signature
facchinm Jun 27, 2018
8f6964d
Call THing.poll with the new API
facchinm Jun 27, 2018
c1c2f43
TEMP: remove OTA stuff
facchinm Jul 3, 2018
62d2ffe
Add minDelta API and a bunch of overloaded not clashing calls
facchinm Jul 3, 2018
bf25568
work in progress, no secure communication
Sid23 Aug 6, 2018
48c3ba8
Reconnection ok!
Sid23 Aug 9, 2018
6448bc5
Added disconnect method
Sid23 Aug 9, 2018
270d67b
Updated example sketch cloud_blink
Sid23 Aug 9, 2018
3ddf07a
blink via cloud monitor
Aug 9, 2018
e30b390
use AWS server
Aug 10, 2018
5ffabab
connect to vernemq
Aug 21, 2018
c87c017
Adapt to String property type
facchinm Aug 29, 2018
de50dde
Remove useless dependency on HTTPClient
facchinm Aug 29, 2018
80bc4dd
Fix secrets include in example
facchinm Aug 29, 2018
ad4788d
Remove nonsense Thing.hasAllReadProperties
facchinm Aug 29, 2018
d570ac2
TEMP: fully remove OTA support
facchinm Aug 21, 2018
f5228c8
wait for 7 seconds only if serial is not found
Aug 30, 2018
2bf5d1b
print compressed cert only in debug mode
Aug 30, 2018
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
86 changes: 76 additions & 10 deletions examples/MKR1000_Cloud_Blink/MKR1000_Cloud_Blink.ino
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
#include <WiFi101.h>
#include <ArduinoCloudV2.h>

#include "arduino_secrets.h"

#define TIMEOUT 7000

///////please enter your sensitive data in the Secret tab/arduino_secrets.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)
char ssid[] = SECRET_SSID; // your network SSID (name)
char pass[] = SECRET_PASS; // your network password (use for WPA, or use as key for WEP)
int status = WL_IDLE_STATUS; // the WiFi radio's status
String cloudSerialBuffer = ""; // the string used to compose network messages from the received characters

WiFiClient wifiClient;

Expand All @@ -16,9 +19,8 @@ unsigned long getTime() {
void setup() {
//Initialize serial and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
int timeout = millis() + TIMEOUT;
while (!Serial && (millis() < timeout)) {}

// check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Expand All @@ -33,42 +35,106 @@ void setup() {
}

// attempt to connect to WiFi network:
while (status != WL_CONNECTED) {
int attempts = 0;
while (status != WL_CONNECTED && attempts < 6) {
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network:
status = WiFi.begin(ssid, pass);

// wait 10 seconds for connection:
delay(10000);
attempts++;
}

if (status != WL_CONNECTED) {
Serial.println("Failed to connect to Wifi!");
while (true);
}

// you're connected now, so print out the data:
Serial.print("You're connected to the network");

Serial.println();
Serial.println("Attempting to connect to Arduino Cloud ...");
Serial.println("Attempting to connect to Arduino Cloud");

ArduinoCloud.onGetTime(getTime);
if (!ArduinoCloud.connect()) {

attempts = 0;
while (!ArduinoCloud.connect() && attempts < 10) {
Serial.print(".");
attempts++;
}

if (attempts >= 10) {
Serial.println("Failed to connect to Arduino Cloud!");
while (1);
}

Serial.println("Successfully connected to Arduino Cloud :)");

CloudSerial.begin(9600);
CloudSerial.print("I'm ready for blinking!\n");
}

void loop() {
ArduinoCloud.poll();

// check if there is something waiting to be read
if (CloudSerial.available()) {
Serial.write(CloudSerial.read());
char character = CloudSerial.read();
Copy link
Contributor

Choose a reason for hiding this comment

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

can't we just echo the character back here? instead of in sendString?

cloudSerialBuffer += character;

// if a \n character has been received, there should be a complete command inside cloudSerialBuffer
if (character == '\n') {
manageString();
}
}
else // if there is nothing to read, it could be that the last command didn't end with a '\n'. Check.
{
manageString();
}

// Just to be able to simulate MKR1000's responses through the serial monitor
Copy link
Contributor

Choose a reason for hiding this comment

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

This sketch can also run on the MKR WiFi 1010?

Copy link
Author

Choose a reason for hiding this comment

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

I think the MKR1010 would need WiFiNINA instead of WiFi101

Copy link
Contributor

Choose a reason for hiding this comment

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

Right, maybe we can add a comment at the start of the sketch that says:

// change to WiFiNINA.h if you are using the MKR WiFi 1010 or MKR Vidor 4000

if (Serial.available()) {
CloudSerial.write(Serial.read());
}
}

void manageString() {
// Don't proceed if the string is empty
if (cloudSerialBuffer.equals("")) return;

// Remove whitespaces
cloudSerialBuffer.trim();

// Make it uppercase;
cloudSerialBuffer.toUpperCase();

if (cloudSerialBuffer.equals("ON")) {
digitalWrite(6, HIGH);
}
if (cloudSerialBuffer.equals("OFF")) {
digitalWrite(6, LOW);
}

sendString(cloudSerialBuffer);

// Reset cloudSerialBuffer
cloudSerialBuffer = "";
}

// sendString sends a string to the Arduino Cloud.
void sendString(String stringToSend) {
// send the characters one at a time
char lastSentChar = 0;
for (int i = 0; i < stringToSend.length(); i++) {
lastSentChar = stringToSend.charAt(i);
CloudSerial.write(lastSentChar);
}

// if the last sent character wasn't a '\n' add it
if (lastSentChar != '\n') {
CloudSerial.write('\n');
}
}
26 changes: 15 additions & 11 deletions examples/utility/Provisioning/Provisioning.ino
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <ArduinoBearSSL.h>
#include <ArduinoECCX08.h>

const bool DEBUG = true;
const int keySlot = 0;
const int compressedCertSlot = 10;
const int serialNumberAndAuthorityKeyIdentifierSlot = 11;
Expand Down Expand Up @@ -55,7 +56,8 @@ void setup() {
while (1);
}

ECCX08Cert.setSubjectCommonName(ECCX08.serialNumber());
String thingId = promptAndReadLine("Please enter the thing id: ");
ECCX08Cert.setSubjectCommonName(thingId);

String csr = ECCX08Cert.endCSR();

Expand All @@ -68,7 +70,6 @@ void setup() {
Serial.println();
Serial.println(csr);

String thingId = promptAndReadLine("Please enter the thing id: ");
String issueYear = promptAndReadLine("Please enter the issue year of the certificate (2000 - 2031): ");
String issueMonth = promptAndReadLine("Please enter the issue month of the certificate (1 - 12): ");
String issueDay = promptAndReadLine("Please enter the issue day of the certificate (1 - 31): ");
Expand Down Expand Up @@ -127,22 +128,25 @@ void setup() {
while (1);
}

Serial.println("Compressed cert = ");
if (DEBUG) {
Serial.println("Compressed cert = ");

const byte* certData = ECCX08Cert.bytes();
int certLength = ECCX08Cert.length();
const byte* certData = ECCX08Cert.bytes();
int certLength = ECCX08Cert.length();

for (int i = 0; i < certLength; i++) {
byte b = certData[i];
for (int i = 0; i < certLength; i++) {
byte b = certData[i];

if (b < 16) {
Serial.print('0');
if (b < 16) {
Serial.print('0');
}
Serial.print(b, HEX);
}
Serial.print(b, HEX);
Serial.println();
}
Serial.println();
}


void loop() {
}

Expand Down
120 changes: 105 additions & 15 deletions src/ArduinoCloud.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
#include <ArduinoECCX08.h>

#include "utility/ECCX08Cert.h"
#include "CloudSerial.h"

#include "ArduinoCloudV2.h"
#include <ArduinoECCX08.h>
#include <utility/ECCX08Cert.h>
#include "CloudSerial.h"

const static char server[] = "a19g5nbe27wn47.iot.us-east-1.amazonaws.com"; //"xxxxxxxxxxxxxx.iot.xx-xxxx-x.amazonaws.com";
const static char server[] = "mqtts-sa.iot.oniudra.cc";

const static int keySlot = 0;
const static int compressedCertSlot = 10;
Expand All @@ -14,7 +13,8 @@ const static int thingIdSlot = 12;

ArduinoCloudClass::ArduinoCloudClass() :
_bearSslClient(NULL),
_mqttClient(256)
// Size of the receive buffer
_mqttClient(MQTT_BUFFER_SIZE)
{
}

Expand All @@ -28,7 +28,7 @@ ArduinoCloudClass::~ArduinoCloudClass()
int ArduinoCloudClass::begin(Client& net)
{
byte thingIdBytes[72];

if (!ECCX08.begin()) {
return 0;
}
Expand All @@ -42,7 +42,7 @@ int ArduinoCloudClass::begin(Client& net)
return 0;
}

ECCX08Cert.setSubjectCommonName(ECCX08.serialNumber());
ECCX08Cert.setSubjectCommonName(_id);
ECCX08Cert.setIssuerCountryName("US");
ECCX08Cert.setIssuerOrganizationName("Arduino LLC US");
ECCX08Cert.setIssuerOrganizationalUnitName("IT");
Expand All @@ -57,30 +57,112 @@ int ArduinoCloudClass::begin(Client& net)
}
_bearSslClient = new BearSSLClient(net);
_bearSslClient->setEccSlot(keySlot, ECCX08Cert.bytes(), ECCX08Cert.length());

// Begin function for the MQTTClient
mqttClientBegin(*_bearSslClient);
// Thing initialization
Thing.begin();

_mqttClient.onMessageAdvanced(ArduinoCloudClass::onMessage);
_mqttClient.begin(server, 8883, *_bearSslClient);
return 1;
}

_stdoutTopic = "$aws/things/" + _id + "/stdout";
_stdinTopic = "$aws/things/" + _id + "/stdin";
// private class method used to initialize mqttClient class member. (called in the begin class method)
void ArduinoCloudClass::mqttClientBegin(Client& net)
{
// MQTT topics definition
_stdoutTopic = "/a/d/" + _id + "/s/o";
_stdinTopic = "/a/d/" + _id + "/s/i";
_dataTopicIn = "/a/d/" + _id + "/e/i";
Copy link
Contributor

Choose a reason for hiding this comment

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

We need to have a discussion on this topic structure. It makes things a bit complicated if one thing wants to listen to another things property.

_dataTopicOut = "/a/d/" + _id + "/e/o";

return 1;
// use onMessage as callback for received mqtt messages
_mqttClient.onMessageAdvanced(ArduinoCloudClass::onMessage);
_mqttClient.begin(server, 8883, net);
// Set will for MQTT client: {topic, qos, retain message}
const char lastMessage[] = "abcb";
Copy link
Contributor

Choose a reason for hiding this comment

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

what does the last well message mean?

_mqttClient.setWill(_dataTopicOut.c_str(), lastMessage, false, 1);
// Set MQTT connection options
_mqttClient.setOptions(mqttOpt.keepAlive, mqttOpt.cleanSession, mqttOpt.timeout);
}

int ArduinoCloudClass::connect()
{
//TODO MQTT brocker connection
Copy link
Contributor

Choose a reason for hiding this comment

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

this whole TODO can be removed

// Username: device id
// Password: empty
if (!_mqttClient.connect(_id.c_str())) {
return 0;
}

_mqttClient.subscribe(_stdinTopic);
_mqttClient.subscribe(_dataTopicIn);

return 1;
}

bool ArduinoCloudClass::disconnect()
{
return _mqttClient.disconnect();
}

void ArduinoCloudClass::poll()
{
// If user call poll() without parameters use the default ones
poll(MAX_RETRIES, RECONNECTION_TIMEOUT);
}

bool ArduinoCloudClass::mqttReconnect(int maxRetries, int timeout)
{
// Counter for reconnection retries
int retries = 0;

// 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) {

// Get last MTTQClient error, (a common error may be a buffer overflow)
lwmqtt_err_t err = _mqttClient.lastError();

// try establish the MQTT broker connection
connect();
// delay eventually used for the nex re-connection try
delay(timeout);
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems weird to have a delay in the lib.

Copy link
Contributor

Choose a reason for hiding this comment

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

If there's no problem with the tight connect() loop I'd replace the condition as

timeout += millis();
while(!_mqttClient.connected() && (retries++ < maxRetries) && (millis() < timeout)) {...}

if (retries == maxRetries || millis() < timeout) 
  return false;

so it fails in both cases without locking for too long

Copy link
Contributor

Choose a reason for hiding this comment

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

same here, this code has integer overflow problems. Please use only unsigned long and subtractions when dealing with millis().

}

// It was impossible to establish a connection, return
if (retries == maxRetries)
return false;

return true;
}

void ArduinoCloudClass::poll(int reconnectionMaxRetries, int reconnectionTimeoutMs)
{
// Method's argument controls
int maxRetries = (reconnectionMaxRetries > 0) ? reconnectionMaxRetries : MAX_RETRIES;
int 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.loop();

uint8_t data[MQTT_BUFFER_SIZE];
int length = Thing.poll(data, sizeof(data));
// Are there some read properties that must be sent to the cloud ??
if (length > 0) {
// Check the connection is ok! if not reconnect
// If a thing has all read properties only try to reconnect when the have to be sent(based on their update policy)
writeProperties(data, length);
}
}

void ArduinoCloudClass::reconnect(Client& net)
{
// Initialize again the MQTTClient, otherwise it would not be able to receive messages through its callback
mqttClientBegin(net);
connect();
}

void ArduinoCloudClass::onGetTime(unsigned long(*callback)(void))
Expand All @@ -93,6 +175,11 @@ int ArduinoCloudClass::connected()
return _mqttClient.connected();
}

int ArduinoCloudClass::writeProperties(const byte data[], int length)
{
return _mqttClient.publish(_dataTopicOut.c_str(), (const char*)data, length);
}

int ArduinoCloudClass::writeStdout(const byte data[], int length)
{
return _mqttClient.publish(_stdoutTopic.c_str(), (const char*)data, length);
Expand All @@ -105,8 +192,11 @@ void ArduinoCloudClass::onMessage(MQTTClient* /*client*/, char topic[], char byt

void ArduinoCloudClass::handleMessage(char topic[], char bytes[], int length)
{
if (_stdinTopic == topic) {
if (strcmp(_stdinTopic.c_str(), topic) == 0) {
CloudSerial.appendStdin((uint8_t*)bytes, length);
}
if (strcmp(_dataTopicIn.c_str(), topic) == 0) {
Thing.decode((uint8_t*)bytes, length);
}
}

Expand Down
Loading