From b3ce93254c867ca2b96a3ed7b3e0ce9cb434746b Mon Sep 17 00:00:00 2001 From: mdelain Date: Fri, 3 Apr 2020 12:24:40 +0200 Subject: [PATCH] Adding MQTTS support Now supports SSL connection to Live Objects (activated by default). For this, the sketch loads the DigiCert Root CA certificate in the radio module. Also more comments in the code ;) --- Arduino_MKR1500NB.ino | 152 +++++++++++++++++++++++++++++------------- LiveObjectsCert.h | 99 +++++++++++++++++++++++++++ arduino_secrets.h | 20 +++--- 3 files changed, 216 insertions(+), 55 deletions(-) create mode 100644 LiveObjectsCert.h diff --git a/Arduino_MKR1500NB.ino b/Arduino_MKR1500NB.ino index d42ad06..1f0ce2a 100644 --- a/Arduino_MKR1500NB.ino +++ b/Arduino_MKR1500NB.ino @@ -1,60 +1,107 @@ -#define DEFAULT_TX_FREQUENCY 60 // message rate by default - // (can be updated over the air from Live Objects platform) +/****************************************************************************** + DEFAULT VALUES + ******************************************************************************/ + + // Set MQTT security: + // comment the line to disable security (MQTT), + // uncomment to activate TLS security (MQTTS). +#define MQTT_TLS + + // Set the default message transmission rate (in seconds). + // This parameter can be updated later over the air from Live Objects. +#define DEFAULT_MESSAGE_RATE 60 + +#define SW_REVISION "1.5.0" + +/****************************************************************************** + INCLUDES + ******************************************************************************/ #include "arduino_secrets.h" +#if defined MQTT_TLS + #include "LiveObjectsCert.h" +#endif +#include #include #include -#include -#define sw_revision "1.0.1" +/****************************************************************************** + TYPEDEFS + ******************************************************************************/ -NB nbAccess; -//GPRS gprs; -NBClient nbClient; -MqttClient mqttClient(nbClient); +enum NetworkConnectionAction { + CONNECT, + DISCONNECT +}; -const char mqtt_user[] = "json+device"; -const char mqtt_pass[] = SECRET_LIVEOBJECTS_API_KEY; +/****************************************************************************** + CONSTANTS + ******************************************************************************/ + + // Live Objects information const char mqtt_id[] = "MKRNB1500"; const char mqtt_broker[] = "liveobjects.orange-business.com"; -const char mqtt_pubdata[] = "dev/data"; -const char mqtt_pubcfg[] = "dev/cfg"; -const char mqtt_subcfg[] = "dev/cfg/upd"; +const char mqtt_user[] = "json+device"; // MQTT username for 'device' role +#if defined MQTT_TLS + const uint16_t mqtt_port = 8883; // TLS MQTT port +#else + const uint16_t mqtt_port = 1883; // plain MQTT port +#endif +const char mqtt_pubdata[] = "dev/data"; // topic for publishing data +const char mqtt_pubcfg[] = "dev/cfg"; // subscribed topic for configuration updates +const char mqtt_subcfg[] = "dev/cfg/upd"; // topic for publishing confuguration + + // Buffers holding the serialized default payloads for data and configuration +const char* JSONdata = "{\"model\":\"github_sample_MKR\",\"value\":{\"uptime\":0}}"; +const char* JSONcfg = "{\"cfg\":{\"message rate (s)\":{\"t\":\"u32\",\"v\":0}}}"; -const char pin[] = SECRET_PINNUMBER; -const char apn[] = SECRET_APN; -const char apn_user[] = SECRET_APN_USER; -const char apn_pass[] = SECRET_APN_PASS; +/****************************************************************************** + VARIABLES + ******************************************************************************/ -const char* JSONdata = "{\"model\":\"github_sample_MKR\",\"value\":{\"uptime\":0}}"; -const char* JSONcfg= "{\"cfg\":{\"transmit frequency (s)\":{\"t\":\"u32\",\"v\":0}}}"; +uint32_t messageRate = DEFAULT_MESSAGE_RATE * 1000; // stores the current data message rate +uint32_t lastMessageTime = DEFAULT_MESSAGE_RATE * 1000; // stores the time when last data message was sent +uint32_t uptimeInSec; // stores the device uptime in seconds (sent in the data message) +bool DigiCert_rootCA_loaded = false; // controls if the root CA certificate has already been stored in the SARA module -uint32_t transmissionFrequency = DEFAULT_TX_FREQUENCY * 1000; -uint32_t lastTransmission = DEFAULT_TX_FREQUENCY * 1000; +void connectionManager(NetworkConnectionAction _action); -uint32_t uptimeInSec = 0; +/****************************************************************************** + OBJECTS CREATION + ******************************************************************************/ +NB nbAccess; +#if defined MQTT_TLS + NBSSLClient nbClient; // for TLS MQTT connection to Live Objects +#else + NBClient nbClient; // for plain MQTT connection to Live Objects +#endif +MqttClient mqttClient(nbClient); StaticJsonDocument<350> payload; -void connectionManager(bool _way); void setup() { Serial.begin(9600); - while (!Serial); - Serial.print("\n\n***\nUsing Arduino LTE-M with Live Objects\nversion "); - Serial.println(sw_revision); - Serial.println("***\n"); + while (!Serial); // uncomment this line if you want to run the sketch without monitoring the Serial line. + Serial.print("\n***\nLive Objects on Arduino MKR 1500NB\nrevision "); + Serial.println(SW_REVISION); + Serial.println("***"); + + if (sizeof(SECRET_LIVEOBJECTS_API_KEY) != 33) { + Serial.print("Please check your Live Objects API key (arduino_secrets.h file).\nStopping here."); + while (1) ; + } mqttClient.setId(mqtt_id); - mqttClient.setUsernamePassword(mqtt_user, mqtt_pass); + mqttClient.setUsernamePassword(mqtt_user, SECRET_LIVEOBJECTS_API_KEY); mqttClient.onMessage(onMessageReceived); - connectionManager(1); + connectionManager(CONNECT); updateConfig(); } void loop() { - if (millis() - lastTransmission > transmissionFrequency) { + if (millis() - lastMessageTime > messageRate) { // get data from sensors Serial.println("Sampling data"); sampleData(); @@ -63,7 +110,7 @@ void loop() { Serial.println("Sending data to Live Objects"); //if (nbAccess.status() != NB_READY || gprs.status() != GPRS_READY || !mqttClient.connected()) if (nbAccess.status() != NB_READY || !mqttClient.connected()) - connectionManager(1); + connectionManager(CONNECT); sendData(); } @@ -72,23 +119,38 @@ void loop() { mqttClient.poll(); } -void connectionManager(bool _way = 1) { - switch (_way) { - case 1: +void connectionManager(NetworkConnectionAction _action = CONNECT) { + switch (_action) { + case CONNECT: Serial.print("Connecting to cellular network"); - //while ((nbAccess.begin() != NB_READY) || (gprs.attachGPRS() != GPRS_READY)) - while (nbAccess.begin(pin, apn, apn_user, apn_pass) != NB_READY) + while (nbAccess.begin(SECRET_PINNUMBER, SECRET_APN, SECRET_APN_USER, SECRET_APN_PASS) != NB_READY) Serial.print("."); - Serial.println("\nYou're connected to the network"); - + + #if defined MQTT_TLS // only performed when using TLS, in order to load the needed root CA into the SARA module + if (!DigiCert_rootCA_loaded) { + Serial.println("Loading DigiCert Root CA certificate"); + MODEM.sendf("AT+USECMNG=0,0,\"%s\",%d", LO_ROOT_CERT.name, LO_ROOT_CERT.size); + if (MODEM.waitForPrompt() != 1) { + Serial.print("Problem loading certificate!\nStopping here."); + while (1) ; + } + else { + MODEM.write(LO_ROOT_CERT.data, LO_ROOT_CERT.size); + int ready; + while (!MODEM.ready()) ; + DigiCert_rootCA_loaded = true; + Serial.println("Certificate loaded"); + } + } + #endif + Serial.print("Connecting to MQTT broker '"); Serial.print(mqtt_broker); Serial.print("'"); - while (!mqttClient.connect(mqtt_broker)) + while (!mqttClient.connect(mqtt_broker, mqtt_port)) Serial.print("."); - Serial.println("\nYou're connected to the MQTT broker"); Serial.println(); @@ -97,7 +159,7 @@ void connectionManager(bool _way = 1) { break; - case 0: + case DISCONNECT: Serial.println("\nClosing MQTT connection..."); mqttClient.stop(); Serial.println("Disconnecting from cellular network..."); @@ -144,14 +206,14 @@ void onMessageReceived(int messageSize) { void updateConfig() { if (payload.containsKey(F("cid"))) { if (payload[F("cfg")].containsKey(F("transmit frequency (s)"))) { - transmissionFrequency = payload[F("cfg")][F("transmit frequency (s)")][F("v")].as() * 1000; - payload[F("cfg")][F("transmit frequency (s)")][F("v")] = transmissionFrequency / 1000; + messageRate = payload[F("cfg")][F("transmit frequency (s)")][F("v")].as() * 1000; + payload[F("cfg")][F("transmit frequency (s)")][F("v")] = messageRate / 1000; } } else { payload.clear(); deserializeJson(payload, JSONcfg); - payload[F("cfg")][F("transmit frequency (s)")][F("v")] = transmissionFrequency / 1000; + payload[F("cfg")][F("transmit frequency (s)")][F("v")] = messageRate / 1000; } char _buffer[300]; @@ -172,5 +234,5 @@ void sendData() { char _buffer[300]; serializeJson(payload, _buffer); publishMessage(mqtt_pubdata, _buffer); - lastTransmission = millis(); + lastMessageTime = millis(); } diff --git a/LiveObjectsCert.h b/LiveObjectsCert.h new file mode 100644 index 0000000..dd5f3d5 --- /dev/null +++ b/LiveObjectsCert.h @@ -0,0 +1,99 @@ +#ifndef _LIVEOBJECTS_CERT_H_INCLUDED +#define _LIVEOBJECTS_CERT_H_INCLUDED + +#include +#include + +struct LORootCert { + const char* name; + const uint8_t* data; + const int size; +}; + +static const LORootCert LO_ROOT_CERT = { + "DigiCert_Global_Root_CA", + (const uint8_t[]){ + 0x30, 0x82, 0x03, 0xaf, 0x30, 0x82, 0x02, 0x97, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x08, 0x3b, 0xe0, 0x56, 0x90, 0x42, 0x46, 0xb1, 0xa1, + 0x75, 0x6a, 0xc9, 0x59, 0x91, 0xc7, 0x4a, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x61, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, + 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x17, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, + 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x36, 0x31, 0x31, 0x31, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x31, 0x31, + 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x30, 0x61, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, + 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, + 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, 0x77, + 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x17, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x47, 0x6c, + 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, 0x41, + 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, + 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xe2, 0x3b, 0xe1, + 0x11, 0x72, 0xde, 0xa8, 0xa4, 0xd3, 0xa3, 0x57, 0xaa, 0x50, 0xa2, 0x8f, + 0x0b, 0x77, 0x90, 0xc9, 0xa2, 0xa5, 0xee, 0x12, 0xce, 0x96, 0x5b, 0x01, + 0x09, 0x20, 0xcc, 0x01, 0x93, 0xa7, 0x4e, 0x30, 0xb7, 0x53, 0xf7, 0x43, + 0xc4, 0x69, 0x00, 0x57, 0x9d, 0xe2, 0x8d, 0x22, 0xdd, 0x87, 0x06, 0x40, + 0x00, 0x81, 0x09, 0xce, 0xce, 0x1b, 0x83, 0xbf, 0xdf, 0xcd, 0x3b, 0x71, + 0x46, 0xe2, 0xd6, 0x66, 0xc7, 0x05, 0xb3, 0x76, 0x27, 0x16, 0x8f, 0x7b, + 0x9e, 0x1e, 0x95, 0x7d, 0xee, 0xb7, 0x48, 0xa3, 0x08, 0xda, 0xd6, 0xaf, + 0x7a, 0x0c, 0x39, 0x06, 0x65, 0x7f, 0x4a, 0x5d, 0x1f, 0xbc, 0x17, 0xf8, + 0xab, 0xbe, 0xee, 0x28, 0xd7, 0x74, 0x7f, 0x7a, 0x78, 0x99, 0x59, 0x85, + 0x68, 0x6e, 0x5c, 0x23, 0x32, 0x4b, 0xbf, 0x4e, 0xc0, 0xe8, 0x5a, 0x6d, + 0xe3, 0x70, 0xbf, 0x77, 0x10, 0xbf, 0xfc, 0x01, 0xf6, 0x85, 0xd9, 0xa8, + 0x44, 0x10, 0x58, 0x32, 0xa9, 0x75, 0x18, 0xd5, 0xd1, 0xa2, 0xbe, 0x47, + 0xe2, 0x27, 0x6a, 0xf4, 0x9a, 0x33, 0xf8, 0x49, 0x08, 0x60, 0x8b, 0xd4, + 0x5f, 0xb4, 0x3a, 0x84, 0xbf, 0xa1, 0xaa, 0x4a, 0x4c, 0x7d, 0x3e, 0xcf, + 0x4f, 0x5f, 0x6c, 0x76, 0x5e, 0xa0, 0x4b, 0x37, 0x91, 0x9e, 0xdc, 0x22, + 0xe6, 0x6d, 0xce, 0x14, 0x1a, 0x8e, 0x6a, 0xcb, 0xfe, 0xcd, 0xb3, 0x14, + 0x64, 0x17, 0xc7, 0x5b, 0x29, 0x9e, 0x32, 0xbf, 0xf2, 0xee, 0xfa, 0xd3, + 0x0b, 0x42, 0xd4, 0xab, 0xb7, 0x41, 0x32, 0xda, 0x0c, 0xd4, 0xef, 0xf8, + 0x81, 0xd5, 0xbb, 0x8d, 0x58, 0x3f, 0xb5, 0x1b, 0xe8, 0x49, 0x28, 0xa2, + 0x70, 0xda, 0x31, 0x04, 0xdd, 0xf7, 0xb2, 0x16, 0xf2, 0x4c, 0x0a, 0x4e, + 0x07, 0xa8, 0xed, 0x4a, 0x3d, 0x5e, 0xb5, 0x7f, 0xa3, 0x90, 0xc3, 0xaf, + 0x27, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x63, 0x30, 0x61, 0x30, 0x0e, + 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, + 0x01, 0x86, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, + 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x03, 0xde, 0x50, 0x35, 0x56, 0xd1, + 0x4c, 0xbb, 0x66, 0xf0, 0xa3, 0xe2, 0x1b, 0x1b, 0xc3, 0x97, 0xb2, 0x3d, + 0xd1, 0x55, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, + 0x16, 0x80, 0x14, 0x03, 0xde, 0x50, 0x35, 0x56, 0xd1, 0x4c, 0xbb, 0x66, + 0xf0, 0xa3, 0xe2, 0x1b, 0x1b, 0xc3, 0x97, 0xb2, 0x3d, 0xd1, 0x55, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0xcb, 0x9c, 0x37, 0xaa, 0x48, + 0x13, 0x12, 0x0a, 0xfa, 0xdd, 0x44, 0x9c, 0x4f, 0x52, 0xb0, 0xf4, 0xdf, + 0xae, 0x04, 0xf5, 0x79, 0x79, 0x08, 0xa3, 0x24, 0x18, 0xfc, 0x4b, 0x2b, + 0x84, 0xc0, 0x2d, 0xb9, 0xd5, 0xc7, 0xfe, 0xf4, 0xc1, 0x1f, 0x58, 0xcb, + 0xb8, 0x6d, 0x9c, 0x7a, 0x74, 0xe7, 0x98, 0x29, 0xab, 0x11, 0xb5, 0xe3, + 0x70, 0xa0, 0xa1, 0xcd, 0x4c, 0x88, 0x99, 0x93, 0x8c, 0x91, 0x70, 0xe2, + 0xab, 0x0f, 0x1c, 0xbe, 0x93, 0xa9, 0xff, 0x63, 0xd5, 0xe4, 0x07, 0x60, + 0xd3, 0xa3, 0xbf, 0x9d, 0x5b, 0x09, 0xf1, 0xd5, 0x8e, 0xe3, 0x53, 0xf4, + 0x8e, 0x63, 0xfa, 0x3f, 0xa7, 0xdb, 0xb4, 0x66, 0xdf, 0x62, 0x66, 0xd6, + 0xd1, 0x6e, 0x41, 0x8d, 0xf2, 0x2d, 0xb5, 0xea, 0x77, 0x4a, 0x9f, 0x9d, + 0x58, 0xe2, 0x2b, 0x59, 0xc0, 0x40, 0x23, 0xed, 0x2d, 0x28, 0x82, 0x45, + 0x3e, 0x79, 0x54, 0x92, 0x26, 0x98, 0xe0, 0x80, 0x48, 0xa8, 0x37, 0xef, + 0xf0, 0xd6, 0x79, 0x60, 0x16, 0xde, 0xac, 0xe8, 0x0e, 0xcd, 0x6e, 0xac, + 0x44, 0x17, 0x38, 0x2f, 0x49, 0xda, 0xe1, 0x45, 0x3e, 0x2a, 0xb9, 0x36, + 0x53, 0xcf, 0x3a, 0x50, 0x06, 0xf7, 0x2e, 0xe8, 0xc4, 0x57, 0x49, 0x6c, + 0x61, 0x21, 0x18, 0xd5, 0x04, 0xad, 0x78, 0x3c, 0x2c, 0x3a, 0x80, 0x6b, + 0xa7, 0xeb, 0xaf, 0x15, 0x14, 0xe9, 0xd8, 0x89, 0xc1, 0xb9, 0x38, 0x6c, + 0xe2, 0x91, 0x6c, 0x8a, 0xff, 0x64, 0xb9, 0x77, 0x25, 0x57, 0x30, 0xc0, + 0x1b, 0x24, 0xa3, 0xe1, 0xdc, 0xe9, 0xdf, 0x47, 0x7c, 0xb5, 0xb4, 0x24, + 0x08, 0x05, 0x30, 0xec, 0x2d, 0xbd, 0x0b, 0xbf, 0x45, 0xbf, 0x50, 0xb9, + 0xa9, 0xf3, 0xeb, 0x98, 0x01, 0x12, 0xad, 0xc8, 0x88, 0xc6, 0x98, 0x34, + 0x5f, 0x8d, 0x0a, 0x3c, 0xc6, 0xe9, 0xd5, 0x95, 0x95, 0x6d, 0xde + }, + 947 +}; + +#endif diff --git a/arduino_secrets.h b/arduino_secrets.h index eb2c018..2f7a850 100644 --- a/arduino_secrets.h +++ b/arduino_secrets.h @@ -1,12 +1,12 @@ -//// cellular connection cerdentials -#define SECRET_PINNUMBER "" // unless PIN is deactivated, specify your SIM card PIN number -#define SECRET_APN "" // specify the APN name (if needed) -#define SECRET_APN_USER "" // specify the username for your APN (if needed) -#define SECRET_APN_PASS "" // specify the password for your APN (if needed) + // Cellular connection cerdentials +const char SECRET_PINNUMBER[] = ""; // unless PIN is deactivated, specify your SIM card PIN number +const char SECRET_APN[] = ""; // specify the APN name (if needed) +const char SECRET_APN_USER[] = ""; // specify the username for your APN (if needed) +const char SECRET_APN_PASS[] = ""; // specify the password for your APN (if needed) -//// your Live Objects credential -#define SECRET_LIVEOBJECTS_API_KEY "..." // paste here the Live Objects API key for your device; - // your key must use the "MQTT device" profile - // You API key must have the predefined 'MQTT Device' right (alternatively: 'Device Access' read + write rights). - // If your API key has more important rights, you *need* to use a TLS connection (MQTTS). + // Live Objects credential: paste below your API key (see Configuration > API keys on the portal). + // You API key must have at least the predefined 'MQTT Device' rights profile + // (alternatively: 'Device Access' read + write rights if need to customise the rights). + // Please note that you *must* use a TLS connection (MQTTS) if you grant more rights to the API key. +const char SECRET_LIVEOBJECTS_API_KEY[] = "...";