Azure / azure-iot-sdk-c

A C99 SDK for connecting devices to Microsoft Azure IoT services
https://azure.github.io/azure-iot-sdk-c
Other
579 stars 738 forks source link

Initiating device-to-cloud (Azure IoT Hub) connection issues over cellular #1914

Closed ejri closed 3 years ago

ejri commented 3 years ago

Hey,

Issue: I have an issue when trying to connect to Azure IoT Hub over cellular. The (ESP32) board connects perfectly fine with Azure over wifi, but doesn't connect over cellular. Device-to-cloud messaging examples work over cellular for other cloud services.

Hardware I'm using a LilyGO-T-SIM7000G board (ESP32 with a LTE-Cat module onboard) to connect to Azure IoT Hub.

Also using: AzureIoTHub - Azure IoT Hub library for Arduino VSChina/ESP32_AzureIoT_Arduino

Error: With Azure over GSM the last thing I see is:

Modem Connected to Rogers' LTE (channel--> 38) CAT-M (preferred network). TLE CAT-M OK

IoT Hub Info: Initializing SNTP

Attempted solution: Azure initiates a cloud to device call on startup (default, can't be changed it seems). However, since the gsm IP is private, Azure can't connect to the ESP unless the device sends a (device-to-cloud) message first.

In order to initiate a device-to-cloud communication, the ESP would need a ATECC508 (and ssl) module in order to send a message through Azure's MQTT broker.

example Arduino NB 1500 example

Initiating an HTTP post request from the device to prompt a callback from the cloud didn't seem to work either.

Any ideas?

Arduino Code:

// This code: connecting 5 accelerometers to esp32. The processed data is sent to IoT Hub.
// IoT Hub connection, and device provisioning, etc. :

// SimpleMQTT_esp32_azure_iothub.ino
// downloads/ubc_5G/sensor_networks_azure/

// IoT Hub has to be Standard Tier, Basic Tier creates a lot of issues

// Azure ESP32 IoT DevKit Get Started
// https://docs.microsoft.com/en-us/samples/azure-samples/esp32-iot-devkit-get-started/sample/

/**
   A simple Azure IoT example for sending telemetry to Iot Hub.

  original code from:
  https://github.com/VSChina/ESP32_AzureIoT_Arduino
  se
*/

// TCA9548A I2C Switch between:
// adafruit LSM9DS1 at address, on TCA9548's channel 0
// GY521-MPU 6050 at address, on TCA9548's channel 1
// TCA9548 module I2C address:  0x70
//

//======================================================
//I2C Mutiplexing using TCA9548A at I2C address 0x70
//======================================================

// GY-521 / mpu6050 on esp32
// connections: 5v, GND, SDA, SCL, on sensor to 5v, GND, SDA, SCL on esp32 respectively

#include "Esp32MQTTClient.h"

/*
///////////////////////////MQTT device to cloud initial message setup///////
#include <ArduinoMqttClient.h>
#include <ArduinoBearSSL.h>
///////////////////////////MQTT device to cloud initial message setup///////
*/

//////////////////////////// Accelerometer ////////////////////
// Include Wire Library for I2C
#include <Wire.h>
// Amazing MPU6050 library by rfetick.
#include <MPU6050_light.h>
MPU6050 mpu(Wire);

const int MPU = 0x68;
int16_t roll1, pitch1, yaw1, roll2, pitch2, yaw2, roll3, pitch3, yaw3, roll4, pitch4, yaw4, roll5, pitch5, yaw5;
//////////////////////////// Accelerometer ////////////////////

void TCA9548A(uint8_t bus)
{
  Wire.beginTransmission(0x70);
  Wire.write(1 << bus);
  Wire.endTransmission();
}

//=============================================================================

#include "time.h"

//////////////// LTE-M connection ////////////////
#define TINY_GSM_MODEM_SIM7000

#define TINY_GSM_RX_BUFFER 1024 // Set RX buffer to 1Kb
#define DUMP_AT_COMMANDS
#include "TinyGsmClient.h"

#define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 60       /* ESP32 should sleep more seconds  (note SIM7000 needs ~20sec to turn off if sleep is activated) */
RTC_DATA_ATTR int bootCount = 0;

HardwareSerial serialGsm(1);
#define SerialAT serialGsm
#define TINY_GSM_USE_GPRS true
#define TINY_GSM_USE_WIFI false

// rogers
const char apn[] = "ciot";
const char nbiot_apn[] = "ciot";

// 1nce
// const char apn[] = "iot.1nce.net";
// const char nbiot_apn[] = "iot.1nce.net";

#define isNBIOT false

const char user[] = "";
const char pass[] = "";

// TTGO T-SIM pin definitions
#define MODEM_RST 5
#define MODEM_PWKEY 4
#define MODEM_DTR 25

#define MODEM_TX 26
#define MODEM_RX 27

//#define I2C_SDA 21
//#define I2C_SCL 22

#define reading_samles 100

#ifdef DUMP_AT_COMMANDS
#include "StreamDebugger.h"
StreamDebugger debugger(serialGsm, Serial);
TinyGsm modem(debugger);
#else
TinyGsm modem(serialGsm);
#endif

/*
///////////////////////////MQTT device to cloud initial message setup///////
/////////////////////////////////////////////////////
const char broker[]        = "<>.azure-devices.net"; // "<IoT Hub name>.azure-devices.net"
String     deviceId        = "<>"; //<device id>

#define IOT_HUB_NAME "<>"
#define DEVICE_NAME "<>"
#define SAS_TOKEN "<>"
////////////////////////////////////////////////////
const char* sslClient=
"-----BEGIN CERTIFICATE-----\n"
"<>";
///////////////////////////MQTT device to cloud initial message setup///////
*/

// Initialize GSM client
TinyGsmClient client(modem);

///////////////////////////MQTT device to cloud initial message setup///////
//////////////////////////////////////////////////
//////////////////////////////////////////////////
///////////////////////////MQTT device to cloud initial message setup///////

// Set to true, if modem is connected
bool modemConnected = false;

void shutdown();
void wait_till_ready();
void modem_off();

void print_wakeup_reason()
{
  esp_sleep_wakeup_cause_t wakeup_reason;

  wakeup_reason = esp_sleep_get_wakeup_cause();

  switch (wakeup_reason)
  {
    case ESP_SLEEP_WAKEUP_EXT0:
      Serial.println("Wakeup caused by external signal using RTC_IO");
      break;
    case ESP_SLEEP_WAKEUP_EXT1:
      Serial.println("Wakeup caused by external signal using RTC_CNTL");
      break;
    case ESP_SLEEP_WAKEUP_TIMER:
      Serial.println("Wakeup caused by timer");
      break;
    case ESP_SLEEP_WAKEUP_TOUCHPAD:
      Serial.println("Wakeup caused by touchpad");
      break;
    case ESP_SLEEP_WAKEUP_ULP:
      Serial.println("Wakeup caused by ULP program");
      break;
    default:
      Serial.printf("Wakeup was not caused by deep sleep: %d\n", wakeup_reason);
      break;
  }
}

void modem_reset()
{
  Serial.println("Modem hardware reset");
  pinMode(MODEM_RST, OUTPUT);
  digitalWrite(MODEM_RST, LOW);
  delay(260); //Treset 252ms
  digitalWrite(MODEM_RST, HIGH);
  delay(4000); //Modem takes longer to get ready and reply after this kind of reset vs power on

  //modem.factoryDefault();
  //modem.restart(); //this results in +CGREG: 0,0
}

void modem_on()
{
  // Set-up modem  power pin
  pinMode(MODEM_PWKEY, OUTPUT);
  digitalWrite(MODEM_PWKEY, HIGH);
  delay(10);
  digitalWrite(MODEM_PWKEY, LOW);
  delay(1010); //Ton 1sec
  digitalWrite(MODEM_PWKEY, HIGH);

  //wait_till_ready();
  Serial.println("Waiting till modem ready...");
  delay(4510); //Ton uart 4.5sec but seems to need ~7sec after hard (button) reset
  //On soft-reset serial replies immediately.

}

void modem_off()
{
  //if you turn modem off while activating the fancy sleep modes it takes ~20sec, else its immediate
  Serial.println("Going to sleep now with modem turned off");
  //modem.gprsDisconnect();
  //modem.radioOff();
  modem.sleepEnable(false); // required in case sleep was activated and will apply after reboot
  modem.poweroff();
}

// fancy low power mode - while connected
void modem_sleep() // will have an effect after reboot and will replace normal power down
{
  Serial.println("Going to sleep now with modem in power save mode");
  // needs reboot to activa and takes ~20sec to sleep
  //modem.PSM_mode();    //if network supports will enter a low power sleep PCM (9uA)
  //modem.eDRX_mode14(); // https://github.com/botletics/SIM7000-LTE-Shield/wiki/Current-Consumption#e-drx-mode
  modem.sleepEnable(); //will sleep (1.7mA), needs DTR or PWRKEY to wake
  pinMode(MODEM_DTR, OUTPUT);
  digitalWrite(MODEM_DTR, HIGH);
}

void modem_wake()
{
  Serial.println("Wake up modem from sleep");
  // DTR low to wake serial
  pinMode(MODEM_DTR, OUTPUT);
  digitalWrite(MODEM_DTR, LOW);
  delay(50);
  //wait_till_ready();
}

void shutdown()
{

  //modem_sleep();
  modem_off();

  delay(1000);
  Serial.flush();
  esp_deep_sleep_start();
  Serial.println("Going to sleep now");
  delay(1000);
  Serial.flush();
  esp_deep_sleep_start();
}

void wait_till_ready() // NOT WORKING - Attempt to minimize waiting time
{

  for (int8_t i = 0; i < 100; i++) //timeout 100 x 100ms = 10sec
  {
    if (modem.testAT())
    {
      //Serial.println("Wait time:%F sec\n", i/10));
      Serial.printf("Wait time: %d\n", i);
      break;
    }
    delay(100);
  }
}

//////////////// LTE-M connection ////////////////

//=============================================================================

#define INTERVAL 15000 //10seconds intervals sending messages
#define MESSAGE_MAX_LEN 400

/// Primaty Connection String///
/*String containing Hostname, Device Id & Device Key in the format:                         */
/*  "HostName=<host_name>;DeviceId=<device_id>;SharedAccessKey=<device_key>"                */
/*  "HostName=<host_name>;DeviceId=<device_id>;SharedAccessSignature=<device_sas_token>"    */
static const char *connectionString = "";
//const char *messageData = "{\"messageId\":%d, \"Temperature\":%f, \"Humidity\":%f}";
const char *messageData = "{\"DeviceID\":\"<>\",\"TreeType\":\"Cedar\" ,\"messageId\":%d, \"roll1\":%d, \"pitch1\":%d, \"yaw1\":%d, \"roll2\":%d, \"pitch2\":%d, \"yaw2\":%d, \"roll3\":%d, \"pitch3\":%d, \"yaw3\":%d, \"roll4\":%d, \"pitch4\":%d, \"yaw4\":%d, \"roll5\":%d, \"pitch5\":%d, \"yaw5\":%d}";
static bool hasIoTHub = false;
static bool hasWifi = false;
int messageCount = 1;
static bool messageSending = true;
static uint64_t send_interval_ms;

static void SendConfirmationCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result)
{
  if (result == IOTHUB_CLIENT_CONFIRMATION_OK)
  {
    Serial.println("Send Confirmation Callback finished.");
  }
}

static void MessageCallback(const char *payLoad, int size)
{
  Serial.println("Message callback:");
  Serial.println(payLoad);
}

static void DeviceTwinCallback(DEVICE_TWIN_UPDATE_STATE updateState, const unsigned char *payLoad, int size)
{
  char *temp = (char *)malloc(size + 1);
  if (temp == NULL)
  {
    return;
  }
  memcpy(temp, payLoad, size);
  temp[size] = '\0';
  // Display Twin message.
  Serial.println(temp);
  free(temp);
}

static int DeviceMethodCallback(const char *methodName, const unsigned char *payload, int size, unsigned char **response, int *response_size)
{
  LogInfo("Try to invoke method %s", methodName);
  const char *responseMessage = "\"Successfully invoke device method\"";
  int result = 200;

  if (strcmp(methodName, "start") == 0)
  {
    LogInfo("Start sending MPU - DeviceID: Cedar data");
    messageSending = true;
  }
  else if (strcmp(methodName, "stop") == 0)
  {
    LogInfo("Stop sending MPU - DeviceID: Cedar data");
    messageSending = false;
  }
  else
  {
    LogInfo("No method %s found", methodName);
    responseMessage = "\"No method found\"";
    result = 404;
  }

  *response_size = strlen(responseMessage) + 1;
  *response = (unsigned char *)strdup(responseMessage);

  return result;
}

/*
//////////////////////////NTP- Time ////////////////////////
const char* ntpServer = "pool.ntp.org";
const long  gmtOffset_sec = 3600;
const int   daylightOffset_sec = 3600;

void printLocalTime()
{
  struct tm timeinfo;
  if(!getLocalTime(&timeinfo)){
    Serial.println("Failed to obtain time");
    return;
  }
  Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}
//////////////////////////NTP- Time ////////////////////////
*/

void setup()
{

  Serial.begin(115200);

  //// start count/// --> moved to after modemOn() function
  //send_interval_ms = millis();

  delay(10);
  Serial.println(F("Started"));
  Serial.println("ESP32 Device");
  Serial.println("Initializing...");

  Wire.begin();
  TCA9548A(3);
  mpu.begin();
  Serial.println(F("Calculating gyro offset, do not move MPU6050"));
  mpu.calcGyroOffsets(); // This does the calibration
  delay(20);

  TCA9548A(4);
  mpu.begin();
  Serial.println(F("Calculating gyro offset, do not move MPU6050"));
  mpu.calcGyroOffsets();
  delay(20);

  TCA9548A(5);
  mpu.begin();
  Serial.println(F("Calculating gyro offset, do not move MPU6050"));
  mpu.calcGyroOffsets();
  delay(20);

  TCA9548A(6);
  mpu.begin();
  Serial.println(F("Calculating gyro offset, do not move MPU6050"));
  mpu.calcGyroOffsets();
  delay(20);

  TCA9548A(7);
  mpu.begin();
  Serial.println(F("Calculating gyro offset, do not move MPU6050"));
  mpu.calcGyroOffsets();
  delay(20);

  //Increment boot number and print it every reboot
  ++bootCount;
  Serial.println("Boot number: " + String(bootCount));

  //Print the wakeup reason for ESP32
  print_wakeup_reason();

  /*
    First we configure the wake up source
    We set our ESP32 to wake up every 5 seconds
  */
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
  Serial.println("Setup ESP32 to sleep for every " + String(TIME_TO_SLEEP) +
                 " Seconds");

  // if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER)
  // {
  //   modem_wake();
  // }
  // else
  {
    modem_on();
    //modem_wake();
    //modem_reset();
  }
  // Set GSM module baud rate and UART pins
  SerialAT.begin(9600, SERIAL_8N1, MODEM_TX, MODEM_RX); //reversing them
  String modemInfo = modem.getModemInfo();
  Serial.print(F("Modem: "));
  Serial.println(modemInfo);

  if (!modemConnected)
  {

    //SIM7000

    if (isNBIOT)
    {
      Serial.println("configuring NBIoT mode");
      modem.setPreferredMode(38);
      //modem.setPreferredLTEMode(2);
      //modem.setOperatingBand(20); // Required for cosmote Greece

      Serial.print(F("Waiting for network..."));
      if (!modem.waitForNetwork(60000L))
      {
        Serial.println(" fail");
        modem_reset();
        shutdown();
      }
      Serial.println("Connected to LTE Mode Only (channel--> 38).... OK");

      Serial.print("Signal quality:");
      Serial.println(modem.getSignalQuality());

      Serial.print(F("Connecting to apn:"));
      Serial.println(nbiot_apn);
      if (!modem.gprsConnect(nbiot_apn, user, pass))
      {
        Serial.println(" failed");
        modem_reset();
        shutdown();
      }

      modemConnected = true;
      Serial.println("Modem Connected (channel--> 38). OK");
    }
    else
    {
      Serial.println("configuring LTE, Cat-M mode"); // AUTO or GSM ONLY

      //2 Auto
      // 13 GSM only
      // 38 LTE only
      // 51 GSM and LTE only
      modem.setNetworkMode(38);
      //modem.setOperatingBand("CAT-M",12)
      delay(500);

      /*
          1 CAT-M
          2 NB-Iot
          3 CAT-M and NB-IoT
        * * */

      modem.setPreferredMode(3);
      delay(500);

      Serial.print(F("Waiting for network..."));
      if (!modem.waitForNetwork(60000L))
      {
        Serial.println(" fail");
        modem_reset();
        shutdown();
      }
      Serial.println(" OK");

      Serial.print("Signal quality:");
      Serial.println(modem.getSignalQuality());

      Serial.print(F("Connecting to Rogers APN at LTE Mode Only (channel--> 38): "));
      Serial.print(apn);
      if (!modem.gprsConnect(apn, user, pass))
      {
        Serial.println(" failed");
        modem_reset();
        shutdown();
      }

      modemConnected = true;
      Serial.println("Modem Connected to Rogers' LTE (channel--> 38) CAT-M (preferred network). TLE CAT-M OK");
    }
  }
//////////////////////////////////////////////////// //////////////////////////////// 
/// send an http post request to IoT HUB from the device. in order to make the modem's IP public 
// this way, when the MQTT server initiates (from the cloud to the device), it connects through the 
// the modem's public IP
//////////////////////////////// ////////////////////////////////
  // Set the username to "<broker>/<device id>/?api-version=2018-06-30" and empty password  
  /*
  String username = "<>";
  MqttClient.connect(broker, 8883);
  MqttClient.setUsernamePassword(username, "");
  connectMQTT();
  MqttClient.poll();
  publishMessage();
  */

  Serial.println(" > IoT Hub"); 
  if (!Esp32MQTTClient_Init((const uint8_t *)connectionString, true))
  {
    hasIoTHub = false;
    Serial.println("Initializing IoT hub failed.");
    modem_reset();
    shutdown();
    return;
  }
 //////////////////////////////// 
  hasIoTHub = true;
  Esp32MQTTClient_SetSendConfirmationCallback(SendConfirmationCallback);
  Esp32MQTTClient_SetMessageCallback(MessageCallback);
  Esp32MQTTClient_SetDeviceTwinCallback(DeviceTwinCallback);
  Esp32MQTTClient_SetDeviceMethodCallback(DeviceMethodCallback);
  Serial.println("Start sending events.");
  randomSeed(analogRead(0));

  send_interval_ms = millis();   // moved to after adding the accelerometer

}

void loop()
{

  delay(1000);
//  printLocalTime();

if (modemConnected && hasIoTHub)
  {
    if (messageSending &&
        (int)(millis() - send_interval_ms) >= INTERVAL)
    {

      read_MPU_1();
      read_MPU_2();
      read_MPU_3();
      read_MPU_4();
      read_MPU_5();
      delay(2000);
      ////  raw accelerometer data //////

      char messagePayload[MESSAGE_MAX_LEN];

      snprintf(messagePayload, MESSAGE_MAX_LEN, messageData, messageCount++, roll1, pitch1, yaw1, roll2, pitch2, yaw2, roll3, pitch3, yaw3, roll4, pitch4, yaw4, roll5, pitch5, yaw5);
      Serial.println(messagePayload);
      EVENT_INSTANCE *message = Esp32MQTTClient_Event_Generate(messagePayload, MESSAGE);
      Esp32MQTTClient_SendEventInstance(message);
      send_interval_ms = millis();
    }
    else
    {
      Esp32MQTTClient_Check();
    }
  }
  delay(5000);

  //delay(1000); // required - else will shutdown too early and miss last value
  shutdown();
}

void read_MPU_1()
{
  // get pitch, roll, yaw accelerometer data /////
  TCA9548A(3);
      mpu.update();
      pitch1 = mpu.getAngleX();
      roll1 = mpu.getAngleY();
      yaw1 = mpu.getAngleZ();
      Serial.print(roll1);
      Serial.print("/");
      Serial.print(pitch1);
      Serial.print("/");
      Serial.println(yaw1);
      delay(1000);
}

void read_MPU_2()
{
  TCA9548A(4);
      mpu.update();
       pitch2 = mpu.getAngleX();
       roll2 = mpu.getAngleY();
       yaw2 = mpu.getAngleZ();
      Serial.print("pitch2: ");
      Serial.println(pitch2);
      Serial.print("roll2: ");
      Serial.println(roll2);
      Serial.print("yaw2: ");
      Serial.println(yaw2);
      delay(1000);
  }

 void read_MPU_3()
{
  TCA9548A(5);
      mpu.update();
       pitch3 = mpu.getAngleX();
       roll3 = mpu.getAngleY();
       yaw3 = mpu.getAngleZ();
      Serial.print("pitch3: ");
      Serial.println(pitch3);
      Serial.print("roll3: ");
      Serial.println(roll3);
      Serial.print("yaw3: ");
      Serial.println(yaw3);
      delay(100);
}

void read_MPU_4()
{
TCA9548A(6);
      mpu.update();
       pitch4 = mpu.getAngleX();
       roll4 = mpu.getAngleY();
       yaw4 = mpu.getAngleZ();
      Serial.print("pitch4: ");
      Serial.println(pitch4);
      Serial.print("roll4: ");
      Serial.println(roll4);
      Serial.print("yaw4: ");
      Serial.println(yaw4);
      delay(100);
}

void read_MPU_5()
{
  TCA9548A(7);
      mpu.update();
       pitch5 = mpu.getAngleX();
       roll5 = mpu.getAngleY();
       yaw5 = mpu.getAngleZ();
      Serial.print("pitch5: ");
      Serial.println(pitch5);
      Serial.print("roll5: ");
      Serial.println(roll5);
      Serial.print("yaw5: ");
      Serial.println(yaw5);

      delay(2000);
}

/*
void connectMQTT() {
  Serial.print("Attempting to MQTT broker: ");
  Serial.print(broker);
  Serial.println(" ");

  while (!MqttClient.connect(broker, 8883)) {
    // failed, retry
    Serial.print(".");
    Serial.println(MqttClient.connectError());
    delay(5000);
  }
  Serial.println();

  Serial.println("You're connected to the MQTT broker");
  Serial.println();

  // subscribe to a topic
  MqttClient.subscribe("devices/" + deviceId + "/messages/devicebound/#");
}

void publishMessage() {
  Serial.println("Publishing message");

  // send message, the Print interface can be used to set the message contents
  MqttClient.beginMessage("devices/" + deviceId + "/messages/events/");
  MqttClient.print("hello ");
  MqttClient.print(millis());
  MqttClient.endMessage();
}
*/
ericwol-msft commented 3 years ago

SNTP is the time server. Most WIFI routers will also allow SNTP to the gateway address. You have the ntpServer commented out in the code. Verify your devices time. Probably need to add manual clock sync to the ntpServer.

ejri commented 3 years ago

Thank you for getting back to me.

I added the ntpServer bit to add a manual clock sync to the ntpServer, but that didn't seem to work. Over wifi, there's not need to add a manual clock sync.

This code over wifi works.

///// WIFI

#include <WiFi.h>
#include "Esp32MQTTClient.h"

//////////////////////////// Accelerometer ////////////////////
// Include Wire Library for I2C
#include <Wire.h>
// Amazing MPU6050 library by rfetick.
#include <MPU6050_light.h>
MPU6050 mpu(Wire);

const int MPU = 0x68;
int16_t roll1, pitch1, yaw1, roll2, pitch2, yaw2, roll3, pitch3, yaw3, roll4, pitch4, yaw4, roll5, pitch5, yaw5;
//////////////////////////// Accelerometer ////////////////////

void TCA9548A(uint8_t bus)
{
  Wire.beginTransmission(0x70);
  Wire.write(1 << bus);
  Wire.endTransmission();
}

//=============================================================================

#define INTERVAL 15000 //10seconds intervals sending messages
#define MESSAGE_MAX_LEN 400
// Please input the SSID and password of WiFi
const char *ssid = "";
const char *password = "";

/// Primaty Connection String///
/*String containing Hostname, Device Id & Device Key in the format:                         */
/*  "HostName=<host_name>;DeviceId=<device_id>;SharedAccessKey=<device_key>"                */
/*  "HostName=<host_name>;DeviceId=<device_id>;SharedAccessSignature=<device_sas_token>"    */
static const char *connectionString = "";
//const char *messageData = "{\"messageId\":%d, \"Temperature\":%f, \"Humidity\":%f}";
const char *DeviceID = "GreenSpaceTree1";
const char *messageData = "{\"DeviceID\":\"GreenSpaceTree1\",\"TreeType\":\"Cedar\" ,\"messageId\":%d, \"roll1\":%d, \"pitch1\":%d, \"yaw1\":%d, \"roll2\":%d, \"pitch2\":%d, \"yaw2\":%d, \"roll3\":%d, \"pitch3\":%d, \"yaw3\":%d, \"roll4\":%d, \"pitch4\":%d, \"yaw4\":%d, \"roll5\":%d, \"pitch5\":%d, \"yaw5\":%d}";
static bool hasIoTHub = false;
static bool hasWifi = false;
int messageCount = 1;
static bool messageSending = true;
static uint64_t send_interval_ms;

static void SendConfirmationCallback(IOTHUB_CLIENT_CONFIRMATION_RESULT result)
{
  if (result == IOTHUB_CLIENT_CONFIRMATION_OK)
  {
    Serial.println("Send Confirmation Callback finished.");
  }
}

static void MessageCallback(const char *payLoad, int size)
{
  Serial.println("Message callback:");
  Serial.println(payLoad);
}

static void DeviceTwinCallback(DEVICE_TWIN_UPDATE_STATE updateState, const unsigned char *payLoad, int size)
{
  char *temp = (char *)malloc(size + 1);
  if (temp == NULL)
  {
    return;
  }
  memcpy(temp, payLoad, size);
  temp[size] = '\0';
  // Display Twin message.
  Serial.println(temp);
  free(temp);
}

static int DeviceMethodCallback(const char *methodName, const unsigned char *payload, int size, unsigned char **response, int *response_size)
{
  LogInfo("Try to invoke method %s", methodName);
  const char *responseMessage = "\"Successfully invoke device method\"";
  int result = 200;

  if (strcmp(methodName, "start") == 0)
  {
    LogInfo("Start sending temperature and humidity data");
    messageSending = true;
  }
  else if (strcmp(methodName, "stop") == 0)
  {
    LogInfo("Stop sending temperature and humidity data");
    messageSending = false;
  }
  else
  {
    LogInfo("No method %s found", methodName);
    responseMessage = "\"No method found\"";
    result = 404;
  }

  *response_size = strlen(responseMessage) + 1;
  *response = (unsigned char *)strdup(responseMessage);

  return result;
}

void setup()
{
  Serial.begin(115200);
  Serial.println("ESP32 Device");
  Serial.println("Initializing...");
  Serial.println(" > WiFi");
  Serial.println("Starting connecting WiFi.");

  delay(10);
  WiFi.mode(WIFI_AP);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
    hasWifi = false;
  }
  hasWifi = true;

  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println(" > IoT Hub");
  if (!Esp32MQTTClient_Init((const uint8_t *)connectionString, true))
  {
    hasIoTHub = false;
    Serial.println("Initializing IoT hub failed.");
    return;
  }
  hasIoTHub = true;
  Esp32MQTTClient_SetSendConfirmationCallback(SendConfirmationCallback);
  Esp32MQTTClient_SetMessageCallback(MessageCallback);
  Esp32MQTTClient_SetDeviceTwinCallback(DeviceTwinCallback);
  Esp32MQTTClient_SetDeviceMethodCallback(DeviceMethodCallback);
  Serial.println("Start sending events.");
  randomSeed(analogRead(0));
  //send_interval_ms = millis();   // moved to after adding the accelerometer

  Wire.begin();
  // deleted initializing sensor data to shorten the code a bit for github

  //// start count///
  send_interval_ms = millis();
}

void loop()
{
  if (hasWifi && hasIoTHub)
  {
    if (messageSending &&
        (int)(millis() - send_interval_ms) >= INTERVAL)
    {

// deleted grabbing sensor data to shorten the code a bit for github
      // get MPU6050 data /////
      // get MPU6050 data /////

      char messagePayload[MESSAGE_MAX_LEN];

      snprintf(messagePayload, MESSAGE_MAX_LEN, messageData, messageCount++, roll1, pitch1, yaw1, roll2, pitch2, yaw2, roll3, pitch3, yaw3, roll4, pitch4, yaw4, roll5, pitch5, yaw5);
      Serial.println(messagePayload);
      EVENT_INSTANCE *message = Esp32MQTTClient_Event_Generate(messagePayload, MESSAGE);
      Esp32MQTTClient_SendEventInstance(message);
      send_interval_ms = millis();
    }
    else
    {
      Esp32MQTTClient_Check();
    }
  }
  delay(5000);
}

It appears that the issue in the cellular connection code is the callback during initialization.

Serial.println(" > IoT Hub");
  if (!Esp32MQTTClient_Init((const uint8_t *)connectionString, true))
  {
    hasIoTHub = false;
    Serial.println("Initializing IoT hub failed.");
    return;
  }
  hasIoTHub = true;
  Esp32MQTTClient_SetSendConfirmationCallback(SendConfirmationCallback);
  Esp32MQTTClient_SetMessageCallback(MessageCallback);
  Esp32MQTTClient_SetDeviceTwinCallback(DeviceTwinCallback);
  Esp32MQTTClient_SetDeviceMethodCallback(DeviceMethodCallback);
ericwol-msft commented 3 years ago

You might want to turn tracing on

bool Esp32MQTTClient_Init(const uint8_t* deviceConnString, bool hasDeviceTwin, bool traceOn)

ejri commented 3 years ago

it seems that the ESP32's onboard clock needs to sync to NTP as soon as it gets a wifi connection. This is essential for it to be functional. With GSM, we're not connecting to wifi so it keeps waiting for a wifi connection in order to know/sync the time, but that never happens.

It seems to have nothing to do with initializing the MQTTClient.

However, in order to take advantage of the esp32 other features, such as deepsleep, power saving modes, etc, it relies on the internal clock, which doesn't get initiated. This also affects any other libraries that relies on time, including Esp32MQTTClient_Init, etc...

Manually setting the clock seems to work to initialize it.

#include <ESP32Time.h>
ESP32Time internal_time;
void setup()
{
internal_time.setTime(30, 24, 15, 17, 1, 2021);  // 17th Jan 2021 15:24:30
  //internal_time.setTime(1609459200);  // 1st Jan 2021 00:00:00
}

void loop()
{

  Serial.println(internal_time.getTime("%A, %B %d %Y %H:%M:%S"));   // (String) returns time with specified format 
  // formating options  http://www.cplusplus.com/reference/ctime/strftime/

  struct tm timeinfo = internal_time.getTimeStruct();
  //Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");   //  (tm struct) Sunday, January 17 2021 07:24:38

  delay(1000);
}

However, it seems that it has issues with TCP IP, which may be needed to be initialized manually, though it does get a local IP address (AT+CIFSR?). Will be looking more into this in the upcoming days...

 > IoT Hub
Info: Initializing SNTP
Info: SNTP initialization complete
Info: IoT Hub SDK for C, version 1.1.23

assertion "Invalid mbox" failed: file "/home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/lwip/lwip/src/api/tcpip.c", line 267, function: tcpip_callback_with_block
abort() was called at PC 0x400fab97 on core 1

ELF file SHA256: 0000000000000000

Backtrace: 0x40088414:0x3ffb1bf0 0x40088691:0x3ffb1c10 0x400fab97:0x3ffb1c30 0x400e876a:0x3ffb1c60 0x400e8729:0x3ffb1c80 0x400e8325:0x3ffb1cc0 0x400d522d:0x3ffb1d10 0x400d6424:0x3ffb1d70 0x401320c7:0x3ffb1df0 0x400e000c:0x3ffb1e10 0x400de615:0x3ffb1e40 0x400deb59:0x3ffb1eb0 0x400da689:0x3ffb1ed0 0x400d4e8d:0x3ffb1f00 0x400d4721:0x3ffb1f40 0x400e35d2:0x3ffb1fb0 0x40089692:0x3ffb1fd0

Rebooting...
ericwol-msft commented 3 years ago

looks like an issue in your lwip networking layer. Try and update to a newer lwip version?

https://github.com/lwip-tcpip/lwip/blob/master/src/api/tcpip.c

ejri commented 3 years ago

updated working code: https://github.com/ejri/sensor_networks_azure/blob/main/Esp32/Device/SimpleMQTT_esp32_azure_iothub_5G/SimpleMQTT_esp32_azure_iothub_5G.ino

Note: even though wifi doesn't actually connect:

must be included WiFi.begin(); must be initiated Esp32's internal clock, tcp, other components and features are only initiated when wifi is initiated. There are some ways to circumvent this by using libraries such as https://github.com/fbiego/ESP32Time/blob/main/examples/esp32_time/esp32_time.ino to manually configure esp32's RTC, but there's so much more that would need to be manually configured. Thank you for your help.