This software setup a central node of a star topology network

Dependencies:   MQTT target_st_bluenrg

Fork of ble-star-mbed by Lorenzo Invidia

source/main.cpp

Committer:
lorevee
Date:
2018-04-04
Revision:
5:5cfb069b2587
Parent:
4:4af40af2530e

File content as of revision 5:5cfb069b2587:

#include <events/mbed_events.h>
#include <mbed.h>
#include "ble/BLE.h"
#include "ble/DiscoveredCharacteristic.h"
#include "ble/DiscoveredService.h"
#include <UUID.h>
#include <BleMasterService.h>
#include <BleSlaveService.h>
#include <string.h>
#include "easy-connect.h"
#include "MQTTClient.h"
#include "MQTTmbed.h"
#include "MQTTNetwork.h"

/*----------------------------------------------------------------------------*/

/* Enable/Disable WiFi (1 = WiFi Enabled, 0 = WiFi Disabled) */
#define ENABLE_WIFI 1

/****  System configuration define   ****/
#define ORG_QUICKSTART           // comment to connect to play.internetofthings.ibmcloud.com
#ifndef ORG_QUICKSTART
//#define TLS_EN                   // uncomment to add TLS to NON quickstart connections
#endif

#ifdef TLS_EN     // Digicert Root Certificate in PEM format (from IBM website)
const char SSL_CA_PEM[] ="-----BEGIN CERTIFICATE-----\n"
"MIIDrzCCApegAwIBAgIQCDvgVpBCRrGhdWrJWZHHSjANBgkqhkiG9w0BAQUFADBh\n"
"MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3\n"
"d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBD\n"
"QTAeFw0wNjExMTAwMDAwMDBaFw0zMTExMTAwMDAwMDBaMGExCzAJBgNVBAYTAlVT\n"
"MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j\n"
"b20xIDAeBgNVBAMTF0RpZ2lDZXJ0IEdsb2JhbCBSb290IENBMIIBIjANBgkqhkiG\n"
"9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4jvhEXLeqKTTo1eqUKKPC3eQyaKl7hLOllsB\n"
"CSDMAZOnTjC3U/dDxGkAV53ijSLdhwZAAIEJzs4bg7/fzTtxRuLWZscFs3YnFo97\n"
"nh6Vfe63SKMI2tavegw5BmV/Sl0fvBf4q77uKNd0f3p4mVmFaG5cIzJLv07A6Fpt\n"
"43C/dxC//AH2hdmoRBBYMql1GNXRor5H4idq9Joz+EkIYIvUX7Q6hL+hqkpMfT7P\n"
"T19sdl6gSzeRntwi5m3OFBqOasv+zbMUZBfHWymeMr/y7vrTC0LUq7dBMtoM1O/4\n"
"gdW7jVg/tRvoSSiicNoxBN33shbyTApOB6jtSj1etX+jkMOvJwIDAQABo2MwYTAO\n"
"BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUA95QNVbR\n"
"TLtm8KPiGxvDl7I90VUwHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUw\n"
"DQYJKoZIhvcNAQEFBQADggEBAMucN6pIExIK+t1EnE9SsPTfrgT1eXkIoyQY/Esr\n"
"hMAtudXH/vTBH1jLuG2cenTnmCmrEbXjcKChzUyImZOMkXDiqw8cvpOp/2PV5Adg\n"
"06O/nVsJ8dWO41P0jmP6P6fbtGbfYmbW0W5BjfIttep3Sp+dWOIrWcBAI+0tKIJF\n"
"PnlUkiaY4IBIqDfv8NZ5YBberOgOzW6sRBc4L0na4UU+Krk2U886UAb3LujEV0ls\n"
"YSEY1QSteDwsOoBrp+uvFRTp2InBuThs4pFsiv9kuXclVzDAGySj4dzp30d8tbQk\n"
"CAUw7C29C79Fv1C5qfPrmAESrciIxpg0X40KPMbp1ZWVbd4=\n"
"-----END CERTIFICATE-----\n";
#endif



static bool quickstartMode = true;      // set to false to connect with authentication tocken

#define MQTT_MAX_PACKET_SIZE 400
#define MQTT_MAX_PAYLOAD_SIZE 300



// Configuration values needed to connect to IBM IoT Cloud
#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 "sensor"
#define DEFAULT_PORT  MQTT_PORT

#else   // not def ORG_QUICKSTART
#define ORG MQTT_ORG_ID            // connect to ORG.internetofthings.ibmcloud.com/ For a registered connection, replace with your org
#define ID MQTT_DEVICE_ID          // For a registered connection is your device id
#define AUTH_TOKEN  MQTT_DEVICE_PASSWORD  // For a registered connection is a device auth-token
#define DEFAULT_TYPE_NAME  MQTT_DEVICE_TYPE  // For a registered connection is device type

#ifdef  TLS_EN
#define DEFAULT_PORT  MQTT_TLS_PORT
#else
#define DEFAULT_PORT  MQTT_PORT

#endif
#endif



#define TYPE DEFAULT_TYPE_NAME       // For a registered connection, replace with your type
#define IBM_IOT_PORT  DEFAULT_PORT

#define MAXLEN_MBED_CONF_APP_WIFI_SSID       32  // same as WIFI_SSID_MAX_LEN in easy_connect
#define MAXLEN_MBED_CONF_APP_WIFI_PASSWORD   64  // same as WIFI_PASSWORD_MAX_LEN

static char     id[30] = ID;                 // mac without colons
static char     org[12] = ORG;
static int      connack_rc = 0; // MQTT connack return code
static char     type[30] = TYPE;
static char     auth_token[30] = AUTH_TOKEN; // Auth_token is only used in non-quickstart mode
static bool     netConnecting = false;
static bool     mqttConnecting = false;
static bool     netConnected = false;
static bool     connected = false;
static int      retryAttempt = 0;
static char     subscription_url[MQTT_MAX_PAYLOAD_SIZE];
static char     ssid[MAXLEN_MBED_CONF_APP_WIFI_SSID];     // Network must be visible otherwise it can't connect
static char     seckey[MAXLEN_MBED_CONF_APP_WIFI_PASSWORD];

MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE> *pClient;

const char NAME_BLESTAR1[] =    "BleStar1";

uint8_t wifi_status;

uint8_t wifi_data[256];
uint8_t new_data = 0;
uint8_t *data;
uint8_t wifi_present;
uint8_t start_bnrg;
extern  PeripheralDevices_t perDevs;
uint8_t json_buffer[512];

/*----------------------------------------------------------------------------*/



/* Prepare JSON packet with sensors data */
void prepare_json_pkt (uint8_t * buffer){
  char tempbuff[256];

  strcpy((char *)buffer,"{\"d\":{\"ST\":\"BLEStar\"");     
  sprintf(tempbuff, ",%s", data);
  strcat((char *)buffer,tempbuff); 
  strcat((char *)buffer,"}}");
  
  return;
}
/*----------------------------------------------------------------------------*/



/* Connect the broker - return the CONNACK*/
int connect(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE> * client, MQTTNetwork *mqttNetwork, NetworkInterface* network)
{
    const char* iot_ibm = MQTT_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];
    sprintf(clientId, "d:%s:%s:%s", org, type, id);
    sprintf(subscription_url, "%s.%s/#/device/%s/%s/", org, "internetofthings.ibmcloud.com", id, TYPE);

    // Network debug statements
    printf("\r\n=====================================");
    printf("\r\nNucleo IP ADDRESS: %s\n", network->get_ip_address());
    printf("\r\nNucleo MAC ADDRESS: %s\n", network->get_mac_address());
    printf("\r\nServer Hostname: %s port: %d\n", hostname, IBM_IOT_PORT);
    printf("\r\nClient ID: %s\n", clientId);
    printf("\r\nTopic: %s\n",MQTT_TOPIC);
    printf("\r\nSubscription URL: %s", subscription_url);
    printf("\r\n=====================================\r\n");
    netConnecting = true;



#ifdef ORG_QUICKSTART
    int tls = TLS_OFF;
    const char * cert = NULL;
    unsigned int sizeof_cert = 0;
    
#else  // if !QUICKSTART possible to connect with TLS or not
#ifdef TLS_EN
    int tls = TLS_ON;
    const char * cert = SSL_CA_PEM;
    unsigned int sizeof_cert = sizeof(SSL_CA_PEM);

#else
    int tls = TLS_OFF;
    const char * cert = 0;
    unsigned int sizeof_cert = 0;

#endif
#endif


    //Return code
    int rc = mqttNetwork->connect(hostname, IBM_IOT_PORT, tls, cert, sizeof_cert);
    if (rc != 0)
    {
        printf("\r\nrc from TCP connect is %d\n", rc);
        return rc;
    }
    netConnected = true;
    netConnecting = false;


    // MQTT Connect
    mqttConnecting = true;
    MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
    data.MQTTVersion = 4;
    data.struct_version=0;
    data.clientID.cstring = clientId;
    data.keepAliveInterval = 0; //MQTT_KEEPALIVE;  // in Sec
    if (!quickstartMode){
        data.username.cstring = "use-token-auth";
        data.password.cstring = auth_token;
        printf ("\r\nAutToken: %s\n", auth_token);
    }
    if ((rc = client->connect(data)) != MQTT::SUCCESS) {
        printf("\r\nrc from MQTT connect is %d\n", rc);
        connack_rc = rc;
        return rc;
    }
    connected = true;
    printf ("\r\n--->MQTT Connected\n");

    mqttConnecting = false;
    connack_rc = rc;
    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<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client, MQTTNetwork *mqttNetwork, NetworkInterface* network)
{
    connected = false;

    while (connect(client, mqttNetwork, network) != MQTT_CONNECTION_ACCEPTED)
    {
        if (connack_rc == MQTT_NOT_AUTHORIZED || connack_rc == MQTT_BAD_USERNAME_OR_PASSWORD) {
            printf ("\r\nError MQTT_BAD_USERNAME_OR_PASSWORDFile: %s, Line: %d Error: %d \n",__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)
            NVIC_SystemReset();
        else
            wait(timeout);
    }
}
/*----------------------------------------------------------------------------*/


/* Method to publish data to client (sending data to broker) */
int publish(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client){
    //printf("\r\npublish");//DEBUG

    MQTT::Message message;
    const char* pubTopic = MQTT_TOPIC;


    if (!client->isConnected()){ 
        printf ("\r\npublish failed: MQTT disconnected\n"); 
        return MQTT::FAILURE; 
    }
    
            
    message.qos = MQTT::QOS0;               // quality of service 0 default
    message.retained = false;               // (false) new clients will not receive past data
    message.dup = false;                    // (false) no duplicated message
    message.payload = (void*)json_buffer;   // DATA to be sent
    message.payloadlen = strlen((const char *)(json_buffer));


    return client->publish(pubTopic, message);
}
/*----------------------------------------------------------------------------*/



/* scheduleBleEventsProcessing */
void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
    BLE &ble = BLE::Instance();
    eventQ.call(Callback<void()>(&ble, &BLE::processEvents));
}
/*----------------------------------------------------------------------------*/




/* Complete the initialization of ble module */
void bleInitComplete(BLE::InitializationCompleteCallbackContext *params){
  
  
    initProcess();
    ble_error_t a0, a1, a2, a3;
        
    BLE&        ble   = params->ble;
    ble_error_t error = params->error;

    if (error != BLE_ERROR_NONE) {
        /* In case of error, forward the error handling to onBleInitError */
        onBleInitError(ble, error);
        return;
    }



    /* Ensure that it is the default instance of BLE */
    if (ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
        return;
    }
    
    
    /* notification + attr writing */
    ble.gattServer().onDataWritten(AttributeModified_CB);
    /* data read */
    ble.gattClient().onDataRead(readCharacteristicCallback);
    /* when a peripheral node characteristics change */
    ble.gattClient().onHVX(onNotificationCallback);    
    /* when a peripheral descriptor is written */
    ble.gattClient().onDataWritten(perDescriptorWrittenCallback);
    
    
    /* disconnection */
    ble.gap().onDisconnection(disconnectionCallback);    
    /* connection */
    ble.gap().onConnection(connectionCallback);
    ble.gap().setScanParams(200, 200);    //(scanInterval,scanWindow)ms
    ble.gap().setScanTimeout(0x0004);     //stop scanning after N sec
    ble.gap().onTimeout(onStopScan);      //callback when scan stops
    
    
    
    
    /* Setup adv */
    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
    a0 = ble.gap().accumulateScanResponse(GapAdvertisingData::MANUFACTURER_SPECIFIC_DATA, manuf_data, sizeof(manuf_data));
    a1 = ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::UNKNOWN);
    a2 = ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)NAME_BLESTAR1, sizeof(NAME_BLESTAR1));
    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); //Advertising_Event_Type
    ble.gap().setAdvertisingInterval(1000); //Adv_Interval
    a3 = ble.gap().setAdvertisingPolicyMode(Gap::ADV_POLICY_IGNORE_WHITELIST); //Adv_Filter_Policy      
    if ((a0 != BLE_ERROR_NONE) || (a1 != BLE_ERROR_NONE) || (a2 != BLE_ERROR_NONE) || (a3 != BLE_ERROR_NONE)){
        printf("\r\nError setup ADV\n");
    }
    
    
    addAllServices();
    printMacAddress();

}
/*----------------------------------------------------------------------------*/



void onBleInitError(BLE &ble, ble_error_t error){}
/*----------------------------------------------------------------------------*/



void mainFunc(void){
    
    if ((wifi_present) && (perDevs.status != NOTIFICATIONS_DATA_READ)){
        
        if((new_data)){
            prepare_json_pkt(json_buffer);
        }//if-new_data
        start_bnrg = 1;
        
    }else{
        start_bnrg = 1;
        if (perDevs.status != NOTIFICATIONS_DATA_READ) {
            HAL_Delay(1000);
        }
    }//if-else
    
    
    if (start_bnrg){
        eventQ.call(connectionProcess);
    }
}
/*----------------------------------------------------------------------------*/


void MQTTpublish(){
    
    if((new_data)){
        /* publish every 5 seconds */
        publish(pClient);            
    }//if-new_data    
}
/*----------------------------------------------------------------------------*/



int main()
{
    printf("\r\n\n/*******************************************************\n");
    printf("\r*                                                      *\n");
    printf("\r*           BLESTAR1 MBED Expansion Software           *\n");
    printf("\r*                                                      *\n");
    printf("\r*******************************************************/\n\n\n");
  
  
#if ENABLE_WIFI
    wifi_present = ENABLE_WIFI;
    printf("\r\nWi-Fi Enabled!\n");
    printf("\rTo edit SSID and/or Password please refer to mbed_app.json file\n");

    /* QUICK START MODE */
    quickstartMode=false;
    if (strcmp(org, "quickstart") == 0){
        quickstartMode = true;
    }
    
    /* Connect network */
    printf("\r\nConnecting to Access Point...\n\n");
    NetworkInterface * network = easy_connect(true); // SSID and pw in .jason
    if (!network){
        printf("\r\nError easy_connect\n");
    }
            
    /* MQTT CONFIG*/
    printf("\r\nConfiguring MQTT network...\n");
    MQTTNetwork mqttNetwork(network);
    MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE> client(mqttNetwork);
    pClient = &client;
    
    
    if (quickstartMode){
        char mac[50];  // remove all : from mac
        char *digit=NULL;
        sprintf (id,"%s", "");
        sprintf (mac,"%s",network->get_mac_address());
        digit = strtok (mac,":");
        while (digit != NULL)
        {
            strcat (id, digit);
            digit = strtok (NULL, ":");
        }
    }//if-quickstart
            
    /* Connect MQTT broker  */
    printf("\r\nConnecting MQTT broker...\n");
    attemptConnect(&client, &mqttNetwork, network);
    
#else
    printf("\r\nWi-Fi Disabled!\n");    

#endif  
  
    printf("\r\n\nStarting the BLE module...\n");
  
    /* Create the ble instance */
    BLE &ble = BLE::Instance();
    
    ble.onEventsToProcess(scheduleBleEventsProcessing);
    
    /* Uncomment to debug the status*/
    //eventQ.call_every(20000, checkStatus);
    
    ble.init(bleInitComplete);   
    
    /* Start main method */
    eventQ.call_every(100, mainFunc);
    eventQ.call_every(1000, MQTTpublish);
    
    //dispatch events
    eventQ.dispatch_forever();

    return 0;   
}
/*----------------------------------------------------------------------------*/