Description
Hello,
I am facing an issue while attempting to connect my Arduino MKR 1500 NB to an MQTT broker using a combination of NBClient, BearSSLClient, and arduinoMqttClient libraries. Despite successfully connecting to the NB IoT network and establishing a UDP connection to an NTP server, the connection to the MQTT broker fails during the inital BearSSL connect process. The failure occurs specifically during the br_sslio_flush(&_ioc
) call in the BearSSLClient::connectSSL
function, where the program freezes without generating a response, timeout, or error. Could this issue be related to an inconsistency in the character sequence length following the AT command issued to the NBIoT module? Or is it something else?
I am using an Arduino MKR 1500 NB client -> BearSSLClient ->arduinoMqttClient
// Initialize MQTT and SSL clients from library
NBClient nbClient;
BearSSLClient sslClient(nbClient);
MqttClient arduinoMqttClient(sslClient);
I am able to connect to the NB IoT network and I am even able to start a UDP connection to an NTP server (which is used for
ArduinoBearSSL.onGetTime(get_time); // Required for server trusted root validation.
)
We then get ready to initialize the MQTT client, and the problem comes up when we run arduinoMqttClient.connect(address,port)
link:
arduinoMqttClient.setId(client_id);
arduinoMqttClient.setUsernamePassword(username, password);
arduinoMqttClient.setCleanSession(true);
arduinoMqttClient.onMessage(on_message_received);
LogInfo("MQTT Client ID: %s", client_id);
LogInfo("MQTT Username: %s", username);
LogInfo("MQTT Password: ***");
LogInfo("MQTT client address: %s", address);
LogInfo("MQTT client port: %d", port);
bool mqtt_connected = false;
Serial.println("MQTT Connecting MQTT client...");
while (!mqtt_connected) {
if (arduinoMqttClient.connect(address, port)){
Serial.println("Succesful MQTT connection to Azure broker!");
mqtt_connected = true;
}
else {
Serial.print("MQTT connection failed! Error code = ");
Serial.println(arduinoMqttClient.connectError());
//int code = arduinoMqttClient.connectError();
//LogError("Cannot connect. Error Code: %d", code);
delay(5000);
}
}
arduinoMqttClient.connect(address,port)
calls, which runs until _client->connect(host, port)
:
int MqttClient::connect(IPAddress ip, const char* host, uint16_t port)
{
Serial.println("MQTTClient : Start connect");
if (clientConnected()) {
_client->stop();
}
_rxState = MQTT_CLIENT_RX_STATE_READ_TYPE;
_connected = false;
_txPacketId = 0x0000;
if (host) {
Serial.println("MQTTClient : Connecting by host");
if (!_client->connect(host, port)) {
_connectError = MQTT_CONNECTION_REFUSED;
Serial.println("MQTTClient : 0 - host connection refused");
return 0;
}
}
_client->connect(host, port)
seems to trigger a call to two functions:
- First it calls
int NBClient::connect()
link which executes successfully and creates a TCP socket with "global.azure-devices-provisioning.net",8883: - After that in triggers
int BearSSLClient::connectSSL(const char* host)
which is where the failure occurs, specifically on the call tobr_sslio_flush(&_ioc);
function:
-
At that time there is an AT command issued to the NBIoT module and after that the program doesn't genereate any reposnse or timeout or error return. It just freezes.
-
One thing that I did note is that the character sequence following the AT+USOWR command is not of the same length as specified in the start of the AT command (178 vs the 242 supposed to be sent). But I don't know if this is just an issue with MAX number of characters in arduino's Serial.println()
-Here are the Serial.print() statments I am using for debugging aling with the AT commands sent by MKRNB module:
14:03:39.127 -> MQTTClient : Start connect
14:03:39.127 -> MQTTClient : Connecting by host
14:03:39.127 -> NBC : Starting NB connect
14:03:39.127 -> 1
14:03:39.218 -> AT+USOCR=6
14:03:39.218 -> +USOCR: 1
14:03:39.218 ->
14:03:39.252 -> OK
14:03:39.428 -> AT+USOCO=1,"global.azure-devices-provisioning.net",8883
14:03:43.042 -> OK
14:03:43.042 -> NBC : 1 - success END
14:03:43.042 -> BSSL : Starting BSSL connect
14:03:43.168 -> AT+USOST=0,"129.6.15.28",123,48,"E30006EC0000000000000000314E31340000000000000000000000000000000000000000000000000000000000000000"
14:03:43.168 -> +USOST: 0,48
14:03:43.214 ->
14:03:43.214 -> OK
14:03:43.214 -> 1 - Waiting for time packet....
14:03:44.199 -> +UUSORF: 0,48
14:03:44.199 -> AT+USORF=0,512
14:03:44.240 ->
14:03:44.240 -> +USORF: 0,"129.6.15.28",123,48,"1C010DE300000010000000204E495354EA60F480000000000000000000000000EA60F4A027DB0056EA60F4A027DB15F6"
14:03:44.240 -> OK
14:03:44.240 -> NTP packet received
14:03:44.240 -> BSSL : Finish random - get_time()
14:03:44.240 -> BSSL : Begin socket init
14:03:44.240 -> BSSL : socketflush
14:03:44.240 -> AT+USORD=1,512
14:03:44.240 -> +USORD: 1,""
14:03:44.240 ->
14:03:44.240 -> OK
14:03:44.308 -> AT+USOWR=1,242,"16030100ED010000E9030300000000D7C94A75B33CD7C36976D1E83A101F715E653FA3017151659FDD639900005ACCA9CCA8C02BC02FC02CC030C0ACC0ADC0AEC0AFC023C027C024C028C009C013C00AC014C02DC031C02EC032C025C029C026C02AC004C00EC005C00F009C009DC09CC09DC0A0C0A1003C003D002F0035C008C012C003C00D000A01000066FF010001000000002A0028000025676C6F62616C2E617A7572652D646576696365732D70726F
Additional int BearSSLClient::connectSSL(const char* host)
reference code with debug Serial.println() statements added:
int BearSSLClient::connectSSL(const char* host)
{
Serial.println("BSSL : Starting BSSL connect");
if (!_br_ssl_client_init_function) {
return 0;
}
// initialize client context with enabled algorithms and trust anchors
_br_ssl_client_init_function(&_sc, &_xc, _TAs, _numTAs);
br_ssl_engine_set_buffers_bidi(&_sc.eng, _ibuf, sizeof(_ibuf), _obuf, sizeof(_obuf));
// inject entropy in engine
unsigned char entropy[32];
#ifndef ARDUINO_DISABLE_ECCX08
if (!ECCX08.begin() || !ECCX08.locked() || !ECCX08.random(entropy, sizeof(entropy))) {
#endif
// no ECCX08 or random failed, fallback to pseudo random
for (size_t i = 0; i < sizeof(entropy); i++) {
entropy[i] = random(0, 255);
}
#ifndef ARDUINO_DISABLE_ECCX08
}
#endif
br_ssl_engine_inject_entropy(&_sc.eng, entropy, sizeof(entropy));
// add custom ECDSA vfry and EC sign
br_ssl_engine_set_ecdsa(&_sc.eng, _ecVrfy);
br_x509_minimal_set_ecdsa(&_xc, br_ssl_engine_get_ec(&_sc.eng), br_ssl_engine_get_ecdsa(&_sc.eng));
// enable client auth
if (_ecCert[0].data_len) {
#ifndef ARDUINO_BEARSSL_DISABLE_KEY_DECODER
if (_skeyDecoder) {
int skeyType = br_skey_decoder_key_type(_skeyDecoder);
if (skeyType == BR_KEYTYPE_EC) {
br_ssl_client_set_single_ec(&_sc, _ecCert, _ecChainLen, br_skey_decoder_get_ec(_skeyDecoder), BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN, BR_KEYTYPE_EC, br_ec_get_default(), br_ecdsa_sign_asn1_get_default());
} else if (skeyType == BR_KEYTYPE_RSA) {
br_ssl_client_set_single_rsa(&_sc, _ecCert, _ecChainLen, br_skey_decoder_get_rsa(_skeyDecoder), br_rsa_pkcs1_sign_get_default());
}
} else {
#endif
br_ssl_client_set_single_ec(&_sc, _ecCert, _ecChainLen, &_ecKey, BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN, BR_KEYTYPE_EC, br_ec_get_default(), _ecSign);
#ifndef ARDUINO_BEARSSL_DISABLE_KEY_DECODER
}
#endif
}
// set the hostname used for SNI
br_ssl_client_reset(&_sc, host, 0);
// get the current time and set it for X.509 validation
uint32_t now = ArduinoBearSSL.getTime();
uint32_t days = now / 86400 + 719528;
uint32_t sec = now % 86400;
Serial.println("BSSL : Finish random - get_time()");
br_x509_minimal_set_time(&_xc, days, sec);
Serial.println("BSSL : Begin socket init");
// use our own socket I/O operations
br_sslio_init(&_ioc, &_sc.eng, BearSSLClient::clientRead, _client, BearSSLClient::clientWrite, _client);
Serial.println("BSSL : socketflush");
br_sslio_flush(&_ioc);
Serial.println("BSSL : PROBLEM HERE!");
while (1) {
Serial.println("BSSL : get state, on loop");
unsigned state = br_ssl_engine_current_state(&_sc.eng);
Serial.println("BSSL : run ifs");
if (state & BR_SSL_SENDAPP) {
break;
} else if (state & BR_SSL_CLOSED) {
return 0;
}
Serial.println("BSSL : help, trap");
}
Serial.println("BSSL : 1 - success END");
return 1;
}