BG96 Module MQTT client example using X-NUCLEO-IKS01A2

Dependencies:   mbed X_NUCLEO_IKS01A2 NetworkSocketAPI MQTT

Getting started mbed NbIot with BG96 module

NbIot BG96 board Main features:

  • LTE/EDGE modem for IOT connection
  • 4A type Pmod interface (expanded uart) except for signals on pin 1 (MODEM RING) and pin 4 (MODEM POWER KEY) here below schematics of pmod connector /media/uploads/abci5961/pmod_sch.jpg

/media/uploads/abci5961/hw_details.jpg

Using USB - AT command port and RealTerm for AT command line

  • Connect the BG96 board to the PC using USB port,

Warning

How to power up BG96 in USB-AT standalone

If you like to use the board STAND-ALONE ( USB - AT port), you must manually power up th BG96 modem. To make this:

  1. Connect the USB
  2. Make a short connection between pin 5 of ARDUINO POWER and pin 4 of PMOD to enable BG96_PWRKEY_ACTIVE_HIGH ( on the rev.C of the board we will add a pushbutton to make this easy )
  3. After short time the blue led start flashing
  4. After a short time the USB will be active and ( you need Quectel drivers )
  5. com port are now shown on the PC

/media/uploads/abci5961/bg96_start_from_usb_a.jpg

  • Open RealTerm and configure as follow: /media/uploads/abci5961/real_term.jpg
  • try to send "AT" command and see the "OK" answer from BG96
  • try any other command from BG96 AT user's guide /media/uploads/abci5961/at_command.jpg

NbIot BG96 MQTT Client example

for mbed OS online Compiler or user's IDE toolchain

It demonstrates how register and send data of some sensor to IBM BlueMix MQTT Quickstart server. It also can connect with MyBlueMix Avnet server (to make this user must have credential to log into Avnet server)

Please note that this example was derived from original IDW01M1_Cloud_IBM for WiFi connections. The WIFI original project can be found here

The application, by default:

  • Connects to LTE network via BG96
  • opens a UDP socket to Google DNS to obtain ip address of Quickstart server
  • opens a TCP socket to Quickstart server to register and send data
  • Send, every 30 seconds, local sensors data (temperature, humidity, pressure)

Hardware requirement

  • X_NUCLEO_L476RG mcu board
  • X_NUCLEO_IKS01A2 sensors expansion board
  • AVNET RSR1157 NbIOT BG96 expansion board

/media/uploads/abci5961/hw_req_1.jpg

BG96 Mqtt client example User's Guide here

main.cpp

Committer:
abci5961:cmatgr59a@os.mbed.com
Date:
2019-02-05
Revision:
4:43647366c69e
Parent:
3:37ebef02b334

File content as of revision 4:43647366c69e:

/* BG96 NetworkSocketAPI Example Program
 * Copyright (c) 2015 ARM Limited
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mbed.h"
#include "BG96Interface.h"
#include "TCPSocket.h"
#include "MQTTClient.h"
#include "MQTT_GSM.h"
#include <ctype.h>
//#include "x_nucleo_iks01a1.h"
#include "XNucleoIKS01A2.h"

#include "BG96.h"

#include "SLG46824Interface.h"
#include "SLG46824_driver.h"

//------------------------------------
// Hyperterminal default configuration
// 9600 bauds, 8-bit data, no parity
//------------------------------------
Serial pc(SERIAL_TX, SERIAL_RX); 
DigitalOut myled(LED1);
bool quickstartMode = true;    

#define MQTT_MAX_PACKET_SIZE 300   
#define MQTT_MAX_PAYLOAD_SIZE 500 


#define ORG_QUICKSTART         							// comment to connect to play.internetofthings.ibmcloud.com
//#define SUBSCRIBE              							// uncomment to subscribe to broker msgs (not to be used with IBM broker) 

 // Configuration values needed to connect to IBM IoT Cloud
#define BROKER_URL ".messaging.internetofthings.ibmcloud.com";     
#ifdef ORG_QUICKSTART
	#define ORG "quickstart"     								// connect to quickstart.internetofthings.ibmcloud.com/ For a registered connection, replace with your org 
	#define ID ""
	#define AUTH_TOKEN ""
	#define DEFAULT_TYPE_NAME "iotsample-mbed-Nucleo"
	#define TOPIC  "iot-2/evt/status/fmt/json" 
#else   // not def ORG_QUICKSTART
	#define ORG "pvko17"             						// connect to play.internetofthings.ibmcloud.com/ For a registered connection, replace with your org
	#define ID "testtype_112233445566"       		// For a registered connection, replace with your id
	#define AUTH_TOKEN "testtype_112233445566"	// For a registered connection, replace with your auth-token
	#define DEFAULT_TYPE_NAME "TestType"
	#define TOPIC   "iot-2/type/TestType/id/testtype_112233445566/evt/status/fmt/json" 
#endif

// network credential
#define APN   "web.omnitel.it"  //VODAFONE apn definition's
//#define APN			"internet.wind"		//WIND apn definition's
#define PASSW  ""
#define USNAME ""

#define TYPE DEFAULT_TYPE_NAME       // For a registered connection, replace with your type
#define MQTT_PORT 1883
#define MQTT_TLS_PORT 8883
#define IBM_IOT_PORT MQTT_PORT
    
char id[30] = ID;                 // mac without colons  
char org[12] = ORG;        
int connack_rc = 0; // MQTT connack return code
//const char* ip_addr = "11.12.13.14";
//char* host_addr = "11.12.13.14";
char sensor_id[50];
char type[30] = TYPE;
char auth_token[30] = AUTH_TOKEN; // Auth_token is only used in non-quickstart mode
bool netConnecting = false;
int connectTimeout = 1000;
bool mqttConnecting = false;
bool netConnected = false;
bool connected = false;
int retryAttempt = 0;
char subscription_url[MQTT_MAX_PAYLOAD_SIZE];

#define SENSOR_ENABLED		1
#define SENSOR_MODEL			2

#define FW_REV						"1.0a"

PressureSensor *pressure_sensor;
HumiditySensor *humidity_sensor;
TempSensor *temp_sensor1;

MQTT::Message message;
MQTTString TopicName={TOPIC};
MQTT::MessageData MsgData(TopicName, message);

/* Instantiate the expansion board */
static XNucleoIKS01A2 *mems_expansion_board = XNucleoIKS01A2::instance(D14, D15, D4, D5);

/* Retrieve the composing elements of the expansion board */
static LSM303AGRMagSensor *magnetometer = mems_expansion_board->magnetometer;
static HTS221Sensor *hum_temp = mems_expansion_board->ht_sensor;
static LPS22HBSensor *press_temp = mems_expansion_board->pt_sensor;
static LSM6DSLSensor *acc_gyro = mems_expansion_board->acc_gyro;
static LSM303AGRAccSensor *accelerometer = mems_expansion_board->accelerometer;

void subscribe_cb(MQTT::MessageData & msgMQTT) {
    char msg[MQTT_MAX_PAYLOAD_SIZE];
    msg[0]='\0';
    strncat (msg, (char*)msgMQTT.message.payload, msgMQTT.message.payloadlen);
    printf ("--->>> subscribe_cb msg: %s\n\r", msg);
}

int subscribe(MQTT::Client<MQTT_GSM, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTT_GSM* ipstack)
{
    char* pubTopic = TOPIC;    
    return client->subscribe(pubTopic, MQTT::QOS1, subscribe_cb);
}

int connect(MQTT::Client<MQTT_GSM, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTT_GSM* ipstack)
{ 
    const char* iot_ibm = BROKER_URL; 

    
    char hostname[strlen(org) + strlen(iot_ibm) + 1];
    sprintf(hostname, "%s%s", org, iot_ibm);
	

    // Construct clientId - d:org:type:id
    char clientId[strlen(org) + strlen(type) + strlen(id) + 5];  
	
		#ifdef ORG_QUICKSTART
    sprintf(clientId, "d:%s:%s:%s", org, type, id);  //@@
		#else
		sprintf(clientId, "g:%s:%s:%s", org, type, id);  //@@
		#endif
	
    sprintf(subscription_url, "%s.%s/#/device/%s/sensor/", org, "internetofthings.ibmcloud.com",id);
		
    netConnecting = true;
    ipstack->open(&ipstack->getGSM());
    int rc = ipstack->connect(hostname, IBM_IOT_PORT, connectTimeout);    
    if (rc != 0)
    {
        //WARN("IP Stack connect returned: %d\n", rc);    
        return rc;
    }
    pc.printf ("--->TCP Connected\n\r");
    netConnected = true;
    netConnecting = false;

    // MQTT Connect
    mqttConnecting = true;
    MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
    data.MQTTVersion = 3;
    data.struct_version=0;
    data.clientID.cstring = clientId;
 
    if (!quickstartMode) 
    {        
        data.username.cstring = "use-token-auth";
        data.password.cstring = auth_token;
    }   
    if ((rc = client->connect(data)) == 0) 
    {       
        connected = true;
        pc.printf ("--->MQTT Connected\n\r");
	#ifdef SUBSCRIBE
        if (!subscribe(client, ipstack)) printf ("--->>>MQTT subscribed to: %s\n\r",TOPIC);
	#endif           
    }
    else {
        //WARN("MQTT connect returned %d\n", rc);        
    }
    if (rc >= 0)
        connack_rc = rc;
    mqttConnecting = false;
    return rc;
}

int getConnTimeout(int attemptNumber)
{  // First 10 attempts try within 3 seconds, next 10 attempts retry after every 1 minute
   // after 20 attempts, retry every 10 minutes
    return (attemptNumber < 10) ? 3 : (attemptNumber < 20) ? 60 : 600;
}

void attemptConnect(MQTT::Client<MQTT_GSM, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTT_GSM* ipstack)
{
    connected = false;
           
    while (connect(client, ipstack) != MQTT_CONNECTION_ACCEPTED) 
    {    
        if (connack_rc == MQTT_NOT_AUTHORIZED || connack_rc == MQTT_BAD_USERNAME_OR_PASSWORD) {
            printf ("File: %s, Line: %d Error: %d\n\r",__FILE__,__LINE__, connack_rc);        
            return; // don't reattempt to connect if credentials are wrong
        } 
        int timeout = getConnTimeout(++retryAttempt);
        //WARN("Retry attempt number %d waiting %d\n", retryAttempt, timeout);
        
        // if ipstack and client were on the heap we could deconstruct and goto a label where they are constructed
        //  or maybe just add the proper members to do this disconnect and call attemptConnect(...)        
        // this works - reset the system when the retry count gets to a threshold
        if (retryAttempt == 5){
						pc.printf ("\n\n\rFAIL!! system reset!!\n\n\r");
            NVIC_SystemReset();
				}
        else
            wait(timeout);
    }
}
float hum_global = 50.0;
uint32_t n_msg = 0;

int publish(MQTT::Client<MQTT_GSM, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTT_GSM* ipstack)
{
    MQTT::Message message;
    char* pubTopic = TOPIC;
            
    char buf[MQTT_MAX_PAYLOAD_SIZE];
    float temp, temp1, temp2, press, hum;
	
	#if SENSOR_ENABLED
		pc.printf("A02 reading sensors...");

	    hum_temp->get_temperature(&temp1);
			hum_temp->get_humidity(&hum);
   
			press_temp->get_temperature(&temp2);
			press_temp->get_pressure(&press);
			temp = (temp1+temp2)/2;
		
	pc.printf(" DONE\r\n");
	#else
		temp=25.5;
		hum_global +=0.1;
		if (hum_global>99.0)
			hum_global = 50.0;
		hum=hum_global;
		press=999;
	#endif
		
	#ifdef ORG_QUICKSTART
    sprintf(buf,
     "{\"d\":{\"ST\":\"Nucleo-IoT-mbed\",\"Temp\":%0.4f,\"Pressure\":%0.4f,\"Humidity\":%0.4f}}",
              temp, press, hum);
	#else
		sprintf (buf, 
		"{\"%s\": {\"temp\":%0.4f,\"humidity\":%0.4f,\"pressure\":%0.4f,\"ambient\":0,\"uv\":0,\"accel_X\":0,\"accel_Y\":0,\"accel_Z\":0}}", 
				sensor_id, temp, hum, press);
	#endif
		
		message.qos = MQTT::QOS0;
    message.retained = false;
    message.dup = false;
    message.payload = (void*)buf;
    message.payloadlen = strlen(buf);
    
		//LOG("Publishing %s\n\r", buf);
		n_msg++;
    pc.printf("Publishing V%s #%d %s\n\r", FW_REV, n_msg, buf);
    return client->publish(pubTopic, message);
} 
    

int loop_count = 0;  
void test_sens(void);
bool slg_active =  false;

int main()
{
		uint8_t SL46824_I2C_addr = 0;
    const char * apn = APN; // Network must be visible otherwise it can't connect
    const char * username = USNAME;
		const char * password = PASSW;
		SLG46824Interface sl_if(A4, A5);
    BG96Interface bg96_if(D8, D2, false);
		//sprintf(sensor_id,"%s",bg96_if.get_mac_address()); 
		//Timer tyeld;
	
		//change serial baud to 115200
		pc.baud(115200);
		//wait(0.1);
	
		wait(0.5);

	  slg_active = sl_if.get_i2c_address(&SL46824_I2C_addr);
		
		if(slg_active == true)
		{
			//sl_if.startup();		//only for debug..
			sl_if.hw_set();				//not needed if SLG46824 already programmed

		}

    myled=1;
		//wait(0.5);
    pc.printf("\r\n*************************************************");
	  wait( 0.1 ); 
		pc.printf("\r\nAvnet Silica NbIotBG96 A02 mbed-os application\r\n");  
    wait( 0.1 );   
    pc.printf("MBED online version %s\r\n", FW_REV);     
    wait( 0.1 );
    //pc.printf("\r\nwait for APN ready ...\r\n");  
    //wait( 0.1 );     
	
	 #if SENSOR_ENABLED
				/* Enable all sensors */
				hum_temp->enable();
				press_temp->enable();
				//magnetometer->enable();
				//accelerometer->enable();
				//acc_gyro->enable_x();
				//acc_gyro->enable_g();
	 #endif


   quickstartMode=false;
   if (strcmp(org, "quickstart") == 0){quickstartMode = true;}
   MQTT_GSM ipstack(bg96_if, apn, username, password);
   MQTT::Client<MQTT_GSM, Countdown, MQTT_MAX_PACKET_SIZE> client(ipstack);
   if (quickstartMode){
        char mac[50];  // remove all : from mac
        char *digit=NULL;
        sprintf (id,"%s", "");                
        sprintf (mac,"%s",ipstack.getGSM().get_mac_address()); 
        digit = strtok (mac,":");
        while (digit != NULL)
        {
            strcat (id, digit);
            digit = strtok (NULL, ":");
        }     
   }
   attemptConnect(&client, &ipstack);
   if (connack_rc == MQTT_NOT_AUTHORIZED || connack_rc == MQTT_BAD_USERNAME_OR_PASSWORD)    
   {
      while (true)
      wait(1.0); // Permanent failures - don't retry
   }
		myled=0;      
		sprintf(sensor_id,"%s",bg96_if.get_mac_address()); 

    while (true)
    {
        if (++loop_count == 60)
        {   
						// Publish a message every 30 second
						pc.printf("\n");
						myled=1;
            if (publish(&client, &ipstack) != 0) { 
                myled=0;
                attemptConnect(&client, &ipstack);   // if we have lost the connection                
            } 
						//else 
						myled=0;
            loop_count = 0;
        }        
//      int start = tyeld.read_ms();
        client.yield(500);  // allow the MQTT client to receive messages
				pc.printf ("loop %d\r", (loop_count+1));		//: %d\n\r",tyeld.read_ms()-start);

    }

}




/* Helper function for printing floats & doubles */
static char *print_double(char* str, double v, int decimalDigits=2)
{
  int i = 1;
  int intPart, fractPart;
  int len;
  char *ptr;

  /* prepare decimal digits multiplicator */
  for (;decimalDigits!=0; i*=10, decimalDigits--);

  /* calculate integer & fractinal parts */
  intPart = (int)v;
  fractPart = (int)((v-(double)(int)v)*i);

  /* fill in integer part */
  sprintf(str, "%i.", intPart);

  /* prepare fill in of fractional part */
  len = strlen(str);
  ptr = &str[len];

  /* fill in leading fractional zeros */
  for (i/=10;i>1; i/=10, ptr++) {
    if (fractPart >= i) {
      break;
    }
    *ptr = '0';
  }

  /* fill in (rest of) fractional part */
  sprintf(ptr, "%i", fractPart);

  return str;
}


		//for testing sensor board ...
void test_sens(void)
{	
		while(1)
		{
			
			  float value1, value2;
			  char buffer1[32], buffer2[32];
			  printf("\r\n");

			  hum_temp->get_temperature(&value1);
				hum_temp->get_humidity(&value2);
				printf("HTS221: [temp] %7s C,   [hum] %s%%\r\n", print_double(buffer1, value1), print_double(buffer2, value2));
    
				press_temp->get_temperature(&value1);
				press_temp->get_pressure(&value2);
				printf("LPS22HB: [temp] %7s C, [press] %s mbar\r\n", print_double(buffer1, value1), print_double(buffer2, value2));
				
				wait(2);
			
		}
}