An mbed BLE-to-Cloud Gateway using Nucleo-F429ZI+X-Nucleo-IDB05A1 or Nucleo-L476RG+X-Nucleo-IDB05A1+X-Nucleo-IDW01M1.

Information

Nucleo- F429ZI configuration requires two hardware patches:

  1. on Nucleo-F429ZI open SB121 and close SB122
  2. on X-Nucleo-IDB05A1 move R4 to R6

The BLE client searches for and connects to a MotEnv node.

main.cpp

Committer:
nikapov
Date:
2017-10-20
Revision:
4:d5f5559b48f7
Parent:
3:39c8d17bed52

File content as of revision 4:d5f5559b48f7:

/*
 * Copyright (c) 2015, 2016 ARM Limited. All rights reserved.
 * SPDX-License-Identifier: Apache-2.0
 * 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.
 */
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include "simpleclient.h"
#include <string>
#include <sstream>
#include <vector>
#include "mbed-trace/mbed_trace.h"
#include "mbedtls/entropy_poll.h"
#include "BLE.h"
#include "DiscoveredCharacteristic.h"
#include "DiscoveredService.h"


#include "security.h"

#include "mbed.h"

// easy-connect compliancy, it has 2 sets of wifi pins we have only one
#define MBED_CONF_APP_ESP8266_TX MBED_CONF_APP_WIFI_TX
#define MBED_CONF_APP_ESP8266_RX MBED_CONF_APP_WIFI_RX
#define MBED_CFG_SPWF01SA_TX MBED_CONF_APP_WIFI_TX
#define MBED_CFG_SPWF01SA_RX MBED_CONF_APP_WIFI_RX
#include "easy-connect/easy-connect.h"

#ifdef TARGET_STM
#define RED_LED (LED3)
#define GREEN_LED (LED1)
#define BLUE_LED (LED2)
#define LED_ON (1)		     
#else // !TARGET_STM
#define RED_LED (LED1)
#define GREEN_LED (LED2)
#define BLUE_LED (LED3)			     
#define LED_ON (0) 
#endif // !TARGET_STM
#define LED_OFF (!LED_ON)

// Status indication
DigitalOut red_led(RED_LED);
#ifdef NUCLEO_F429ZI
DigitalOut green_led(NC);   // avoid interference with SPI_CLK
#else
DigitalOut green_led(GREEN_LED);
#endif
DigitalOut blue_led(BLUE_LED);


/************************************************************BLE Stuff from here *********************************/

BLE &ble = BLE::Instance();
static EventQueue eventQueue(/* event count */ 16 * EVENTS_EVENT_SIZE);
Thread BLE_thread;
static bool triggerEnvCharacteristic;
static bool envDataAvailable = false;
static DiscoveredCharacteristic envCharacteristic;
uint16_t payload_length = 0;
uint8_t dataforClient[12];

void BLEConnect(BLEProtocol::AddressBytes_t address) {
	BLE::Instance().gap().connect(address, Gap::ADDR_TYPE_RANDOM_STATIC, NULL, NULL);
}


void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params) {
    // parse the advertising payload, looking for data type COMPLETE_LOCAL_NAME
    // The advertising payload is a collection of key/value records where
    // byte 0: length of the record excluding this byte
    // byte 1: The key, it is the type of the data
    // byte [2..N] The value. N is equal to byte0 - 1
	
	//printf("Starting advertisementCallback...\r\n");
	
    for (uint8_t i = 0; i < params->advertisingDataLen; ++i) {

        const uint8_t record_length = params->advertisingData[i];
        if (record_length == 0) {
            continue;
        }
        const uint8_t type = params->advertisingData[i + 1];
        const uint8_t* value = params->advertisingData + i + 2;
        const uint8_t value_length = record_length - 1;
		
		if(type == GapAdvertisingData::COMPLETE_LOCAL_NAME) {
			char devName[16];
			int strLength = value_length > 15 ? 15 : value_length;
			memcpy (devName, value, strLength);
			devName[strLength] = '\0';
			printf("Found a device with name: %s\n\r", devName);
			
			if (memcmp(value, "MotEnvMbed", 10) == 0) { // search for MotEnvMbedXX devices
				printf("Found an mbed device node\n");
				BLEProtocol::AddressBytes_t devAddress;
			    memcpy (devAddress, params->peerAddr,BLEProtocol::ADDR_LEN);
				BLEConnect(devAddress);
				break;
			}
		}

        i += record_length;
    } 
}

void serviceDiscoveryCallback(const DiscoveredService *service) {
	
    if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) {
        printf("S UUID-%x attrs[%u %u]\r\n", service->getUUID().getShortUUID(), service->getStartHandle(), service->getEndHandle());
    } else {
        printf("S UUID-");
        const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID();
        for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) {
            printf("%02x", longUUIDBytes[i]);
        }
        printf(" attrs[%u %u]\r\n", service->getStartHandle(), service->getEndHandle());
    }
}

//read data from BLE
void updateEnvCharacteristic(void) {
	if (!BLE::Instance().gattClient().isServiceDiscoveryActive()) {
		//printf("Reading environmental data\n\n");
        envCharacteristic.read();
		envDataAvailable = true;
#ifdef NUCLEO_F429ZI
		red_led = LED_ON;
#else
		green_led = LED_ON;
#endif
    } else {
		envDataAvailable = false;
	}
}


void characteristicDiscoveryCallback(const DiscoveredCharacteristic *characteristicP) {
	
	printf("Found environmental data\n");
	envCharacteristic        = *characteristicP;
	triggerEnvCharacteristic = true;
}

void discoveryTerminationCallback(Gap::Handle_t connectionHandle) {
	
    //printf("terminated SD for handle %u\r\n", connectionHandle);
	
    if (triggerEnvCharacteristic) {
        triggerEnvCharacteristic = false;
        eventQueue.call_in(500, updateEnvCharacteristic); 
    } 
}

void connectionCallback(const Gap::ConnectionCallbackParams_t *params) {
	printf("Connected to ST Node now...\r\n");
    if (params->role == Gap::CENTRAL) {
        BLE &ble = BLE::Instance();
        ble.gattClient().onServiceDiscoveryTermination(discoveryTerminationCallback);
		const char *servUUIDString = "00000000-0001-11e1-9ab4-0002a5d5c51b";
		UUID servUUID(servUUIDString);
		const char *charUUIDString = "001c0000-0001-11e1-ac36-0002a5d5c51b";
		UUID charUUID(charUUIDString);
        ble.gattClient().launchServiceDiscovery(params->handle, serviceDiscoveryCallback, characteristicDiscoveryCallback, servUUID, charUUID);
    }
}


void triggerRead(const GattReadCallbackParams *response) {
	
	if (response->handle == envCharacteristic.getValueHandle()) {
		payload_length = response-> len;
		for(int i=0; i< response-> len; i++) {
//			printf("%02x", response->data[i]);
			dataforClient[i] = response -> data[i];
	//		printf("%d", dataforClient[i]);
		}
	
		//printf("Temperature: %f\r\n", (uint32_t)((dataforClient[9]<<8) | dataforClient[8])/10.0);
		//printf("Humidity: %f\r\n", (uint32_t)((dataforClient[7]<<8) | dataforClient[6])/10.0);
		//printf("Pressure: %f\r\n\r\n\r\n", (uint32_t)((dataforClient[5]<<24) |(dataforClient[4]<<16) |(dataforClient[3]<<8) | dataforClient[2])/100.0);
		
		eventQueue.call_in(500, updateEnvCharacteristic);  // triggering BLE data read again in 0.5s
    } 
}

void triggerNotify(const GattHVXCallbackParams *response) {
	
	if (response->handle == envCharacteristic.getValueHandle()) {
		payload_length = response-> len;
		for(int i=0; i< response-> len; i++) {
//			printf("%02x", response->data[i]);
			dataforClient[i] = response -> data[i];
	//		printf("%d", dataforClient[i]);
		}
	
		//printf("Temperature: %f\r\n", (uint32_t)((dataforClient[9]<<8) | dataforClient[8])/10.0);
		//printf("Humidity: %f\r\n", (uint32_t)((dataforClient[7]<<8) | dataforClient[6])/10.0);
		//printf("Pressure: %f\r\n\r\n\r\n", (uint32_t)((dataforClient[5]<<24) |(dataforClient[4]<<16) |(dataforClient[3]<<8) | dataforClient[2])/100.0);

    }
}

void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *) {
    printf("Got disconnected from the device!\r\n");
    /* Start scanning and try to connect again */
#ifdef NUCLEO_F429ZI
	red_led = LED_OFF;
#else
	green_led = LED_OFF;
#endif
    BLE::Instance().gap().startScan(advertisementCallback);
}

void onBleInitError(BLE &ble, ble_error_t error)
{
   /* Initialization error handling should go here */
	printf("BLE Error = %u\r\n", error);
}

void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
{
	//printf("I'm inside BLE init\r\n");
    BLE&        ble   = params->ble;
    ble_error_t error = params->error;
	ble_error_t error1 = 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) {
			printf("Not the default instance\r\n");
        return;
    }

	ble.gap().onDisconnection(disconnectionCallback);
	ble.gap().onConnection(connectionCallback);

// On reading data, call triggerRead function.
    ble.gattClient().onDataRead(triggerRead);
	//ble.gattClient().onHVX(triggerNotify);
		// scan interval: 400ms and scan window: 400ms.
    // Every 400ms the device will scan for 400ms
    // This means that the device will scan continuously.
    ble.gap().setScanParams(400, 400);
    error =   ble.gap().startScan(advertisementCallback);
	if (error) {
		printf("BLE Error startScan = %u\r\n", error);
	}
		
}

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

//BLE thread init and further calls to other BLE methods.
void BLE_thread_init(void){
	//printf("I'm inside BLE thread.....\r\n");

//Schedule events before starting the thread since there might be some missed events while scanning / pairing.
	ble.onEventsToProcess(scheduleBleEventsProcessing);
	ble.init(bleInitComplete);
//Loop forever the BLE thread
	eventQueue.dispatch_forever();
}

/************************************************************BLE Stuff to here *********************************/

// These are example resource values for the Device Object
struct MbedClientDevice device = {
    "Manufacturer_String",      // Manufacturer
    "Type_String",              // Type
    "ModelNumber_String",       // ModelNumber
    "SerialNumber_String"       // SerialNumber
};

// Instantiate the class which implements LWM2M Client API (from simpleclient.h)
MbedClient mbed_client(device);


class EnvResource {
public:
	EnvResource() {
		
        temp_object = M2MInterfaceFactory::create_object("3303");
        temp_inst = temp_object->create_object_instance();
        temp_res = temp_inst->create_dynamic_resource("5700", "Temperature",
            M2MResourceInstance::FLOAT, true);	// observable
        // we can only read this value
	    temp_res->set_operation(M2MBase::GET_ALLOWED);
		temp_res->set_value((uint8_t*)"20.0", 4);
		
		hum_object = M2MInterfaceFactory::create_object("3304");
        hum_inst = hum_object->create_object_instance();
        hum_res = hum_inst->create_dynamic_resource("5700", "Humidity",
            M2MResourceInstance::FLOAT, true);	// observable
        // we can only read this value
	    hum_res->set_operation(M2MBase::GET_ALLOWED);
		hum_res->set_value((uint8_t*)"50.0", 4);
		
		press_object = M2MInterfaceFactory::create_object("3300");
        press_inst = press_object->create_object_instance();
        press_res = press_inst->create_dynamic_resource("5700", "Pressure",
            M2MResourceInstance::FLOAT, true);	// observable
        // we can only read this value
	    press_res->set_operation(M2MBase::GET_ALLOWED);
		press_res->set_value((uint8_t*)"1000", 4);
		
        timer_res = press_inst->create_dynamic_resource("5603", "PollingPeriodMs",  // one polling time for all env values, associated to humidity
		    M2MResourceInstance::INTEGER, false);	// not observable
        // we can read/wr this value
	    timer_res->set_operation(M2MBase::GET_PUT_ALLOWED);
        timer_res->set_value((uint8_t*)"1000",4); // default 1s polling
		
	}
	
	M2MObject* get_temp_object() {
        return temp_object;
	}
	
	M2MObject* get_hum_object() {
        return hum_object;
	}
	
	M2MObject* get_press_object() {
        return press_object;
	}
	
	int get_polling_period() {
		return timer_res->get_value_int();
	}

	void update_resources() {
	   float temp;
	   float hum;
	   float press;
	  
      if (!envDataAvailable) {
		  return;
	  }
	  
	  temp = (uint32_t)((dataforClient[9]<<8) | dataforClient[8])/10.0;
	  hum = ((dataforClient[7]<<8) | dataforClient[6])/10.0;
	  press = (uint32_t)((dataforClient[5]<<24) |(dataforClient[4]<<16) |(dataforClient[3]<<8) | dataforClient[2])/100.0;
	  
	  stringstream ss_temp;
	  ss_temp << temp;
      std::string stringified = ss_temp.str();
      temp_res->set_value((uint8_t*)stringified.c_str(), stringified.length());
	  
	  stringstream ss_hum;
	  ss_hum << hum;
      stringified = ss_hum.str();
      hum_res->set_value((uint8_t*)stringified.c_str(), stringified.length());
	  
	  stringstream ss_press;
	  ss_press << press;
      stringified = ss_press.str();
      press_res->set_value((uint8_t*)stringified.c_str(), stringified.length());
	  
   }
	
private:

   M2MObject* temp_object;
   M2MObjectInstance* temp_inst;
   M2MResource* temp_res;
   
   M2MObject* hum_object;
   M2MObjectInstance* hum_inst;
   M2MResource* hum_res;
   
   M2MObject* press_object;
   M2MObjectInstance* press_inst;
   M2MResource* press_res;
   
   M2MResource* timer_res;
	
};


osThreadId mainThread;

// Entry point to the program
int main() {

    unsigned int seed;
    size_t len;

#ifdef MBEDTLS_ENTROPY_HARDWARE_ALT
    // Used to randomize source port
    mbedtls_hardware_poll(NULL, (unsigned char *) &seed, sizeof seed, &len);

#elif defined MBEDTLS_TEST_NULL_ENTROPY

#warning "mbedTLS security feature is disabled. Connection will not be secure !! Implement proper hardware entropy for your selected hardware."
    // Used to randomize source port
    mbedtls_null_entropy_poll( NULL,(unsigned char *) &seed, sizeof seed, &len);

#else

#error "This hardware does not have entropy, endpoint will not register to Connector.\
You need to enable NULL ENTROPY for your application, but if this configuration change is made then no security is offered by mbed TLS.\
Add MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES and MBEDTLS_TEST_NULL_ENTROPY in mbed_app.json macros to register your endpoint."

#endif

    srand(seed);
    red_led = LED_OFF;
    blue_led = LED_OFF;
	green_led = LED_OFF;

    // Keep track of the main thread
    mainThread = osThreadGetId();

    printf("\nStarting mbed Client example\n");

    //mbed_trace_init();

    NetworkInterface* network = easy_connect(true);
    if(network == NULL) {
        printf("\nConnection to Network Failed - exiting application...\n");
        return -1;
    }
	
	// environmental data
	EnvResource env_resource;
	

    // Create endpoint interface to manage register and unregister
    mbed_client.create_interface(MBED_SERVER_ADDRESS, network);

    // Create Objects of varying types, see simpleclient.h for more details on implementation.
    M2MSecurity* register_object = mbed_client.create_register_object(); // server object specifying connector info
    M2MDevice*   device_object   = mbed_client.create_device_object();   // device resources object

    // Create list of Objects to register
    M2MObjectList object_list;

    // Add objects to list
    object_list.push_back(device_object);
	object_list.push_back(env_resource.get_temp_object());
	object_list.push_back(env_resource.get_hum_object());
	object_list.push_back(env_resource.get_press_object());
	
    // Set endpoint registration object
    mbed_client.set_register_object(register_object);

    // Register with mbed Device Connector
    mbed_client.test_register(register_object, object_list);

	// wait for registration and BLE data started flushing
	while (!mbed_client.register_successful())  {
		Thread::wait(500);
	}
	
	printf("\nNow starting BLE thread\n");
	BLE_thread.start(BLE_thread_init);
	
	while (!envDataAvailable) {
		Thread::wait(500);
	}
	
	printf ("\nNow pushing data to the mbed device connector.\n");
	
    while (true) {
		
          env_resource.update_resources();
		  int timer_val = env_resource.get_polling_period();
		  Thread::wait(timer_val);
    }

    mbed_client.test_unregister();

}