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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 /*
00002  * Copyright (c) 2015, 2016 ARM Limited. All rights reserved.
00003  * SPDX-License-Identifier: Apache-2.0
00004  * Licensed under the Apache License, Version 2.0 (the License); you may
00005  * not use this file except in compliance with the License.
00006  * You may obtain a copy of the License at
00007  *
00008  * http://www.apache.org/licenses/LICENSE-2.0
00009  *
00010  * Unless required by applicable law or agreed to in writing, software
00011  * distributed under the License is distributed on an AS IS BASIS, WITHOUT
00012  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00013  * See the License for the specific language governing permissions and
00014  * limitations under the License.
00015  */
00016 #define __STDC_FORMAT_MACROS
00017 #include <inttypes.h>
00018 #include "simpleclient.h"
00019 #include <string>
00020 #include <sstream>
00021 #include <vector>
00022 #include "mbed-trace/mbed_trace.h"
00023 #include "mbedtls/entropy_poll.h"
00024 #include "BLE.h"
00025 #include "DiscoveredCharacteristic.h"
00026 #include "DiscoveredService.h"
00027 
00028 
00029 #include "security.h"
00030 
00031 #include "mbed.h"
00032 
00033 // easy-connect compliancy, it has 2 sets of wifi pins we have only one
00034 #define MBED_CONF_APP_ESP8266_TX MBED_CONF_APP_WIFI_TX
00035 #define MBED_CONF_APP_ESP8266_RX MBED_CONF_APP_WIFI_RX
00036 #define MBED_CFG_SPWF01SA_TX MBED_CONF_APP_WIFI_TX
00037 #define MBED_CFG_SPWF01SA_RX MBED_CONF_APP_WIFI_RX
00038 #include "easy-connect/easy-connect.h"
00039 
00040 #ifdef TARGET_STM
00041 #define RED_LED (LED3)
00042 #define GREEN_LED (LED1)
00043 #define BLUE_LED (LED2)
00044 #define LED_ON (1)           
00045 #else // !TARGET_STM
00046 #define RED_LED (LED1)
00047 #define GREEN_LED (LED2)
00048 #define BLUE_LED (LED3)              
00049 #define LED_ON (0) 
00050 #endif // !TARGET_STM
00051 #define LED_OFF (!LED_ON)
00052 
00053 // Status indication
00054 DigitalOut red_led(RED_LED);
00055 #ifdef NUCLEO_F429ZI
00056 DigitalOut green_led(NC);   // avoid interference with SPI_CLK
00057 #else
00058 DigitalOut green_led(GREEN_LED);
00059 #endif
00060 DigitalOut blue_led(BLUE_LED);
00061 
00062 
00063 /************************************************************BLE Stuff from here *********************************/
00064 
00065 BLE &ble = BLE::Instance();
00066 static EventQueue eventQueue(/* event count */ 16 * EVENTS_EVENT_SIZE);
00067 Thread BLE_thread;
00068 static bool triggerEnvCharacteristic;
00069 static bool envDataAvailable = false;
00070 static DiscoveredCharacteristic envCharacteristic;
00071 uint16_t payload_length = 0;
00072 uint8_t dataforClient[12];
00073 
00074 void BLEConnect(BLEProtocol::AddressBytes_t address) {
00075     BLE::Instance().gap().connect(address, Gap::ADDR_TYPE_RANDOM_STATIC, NULL, NULL);
00076 }
00077 
00078 
00079 void advertisementCallback(const Gap::AdvertisementCallbackParams_t *params) {
00080     // parse the advertising payload, looking for data type COMPLETE_LOCAL_NAME
00081     // The advertising payload is a collection of key/value records where
00082     // byte 0: length of the record excluding this byte
00083     // byte 1: The key, it is the type of the data
00084     // byte [2..N] The value. N is equal to byte0 - 1
00085     
00086     //printf("Starting advertisementCallback...\r\n");
00087     
00088     for (uint8_t i = 0; i < params->advertisingDataLen; ++i) {
00089 
00090         const uint8_t record_length = params->advertisingData[i];
00091         if (record_length == 0) {
00092             continue;
00093         }
00094         const uint8_t type = params->advertisingData[i + 1];
00095         const uint8_t* value = params->advertisingData + i + 2;
00096         const uint8_t value_length = record_length - 1;
00097         
00098         if(type == GapAdvertisingData::COMPLETE_LOCAL_NAME) {
00099             char devName[16];
00100             int strLength = value_length > 15 ? 15 : value_length;
00101             memcpy (devName, value, strLength);
00102             devName[strLength] = '\0';
00103             printf("Found a device with name: %s\n\r", devName);
00104             
00105             if (memcmp(value, "MotEnvMbed", 10) == 0) { // search for MotEnvMbedXX devices
00106                 printf("Found an mbed device node\n");
00107                 BLEProtocol::AddressBytes_t devAddress;
00108                 memcpy (devAddress, params->peerAddr,BLEProtocol::ADDR_LEN);
00109                 BLEConnect(devAddress);
00110                 break;
00111             }
00112         }
00113 
00114         i += record_length;
00115     } 
00116 }
00117 
00118 void serviceDiscoveryCallback(const DiscoveredService *service) {
00119     
00120     if (service->getUUID().shortOrLong() == UUID::UUID_TYPE_SHORT) {
00121         printf("S UUID-%x attrs[%u %u]\r\n", service->getUUID().getShortUUID(), service->getStartHandle(), service->getEndHandle());
00122     } else {
00123         printf("S UUID-");
00124         const uint8_t *longUUIDBytes = service->getUUID().getBaseUUID();
00125         for (unsigned i = 0; i < UUID::LENGTH_OF_LONG_UUID; i++) {
00126             printf("%02x", longUUIDBytes[i]);
00127         }
00128         printf(" attrs[%u %u]\r\n", service->getStartHandle(), service->getEndHandle());
00129     }
00130 }
00131 
00132 //read data from BLE
00133 void updateEnvCharacteristic(void) {
00134     if (!BLE::Instance().gattClient().isServiceDiscoveryActive()) {
00135         //printf("Reading environmental data\n\n");
00136         envCharacteristic.read();
00137         envDataAvailable = true;
00138 #ifdef NUCLEO_F429ZI
00139         red_led = LED_ON;
00140 #else
00141         green_led = LED_ON;
00142 #endif
00143     } else {
00144         envDataAvailable = false;
00145     }
00146 }
00147 
00148 
00149 void characteristicDiscoveryCallback(const DiscoveredCharacteristic *characteristicP) {
00150     
00151     printf("Found environmental data\n");
00152     envCharacteristic        = *characteristicP;
00153     triggerEnvCharacteristic = true;
00154 }
00155 
00156 void discoveryTerminationCallback(Gap::Handle_t connectionHandle) {
00157     
00158     //printf("terminated SD for handle %u\r\n", connectionHandle);
00159     
00160     if (triggerEnvCharacteristic) {
00161         triggerEnvCharacteristic = false;
00162         eventQueue.call_in(500, updateEnvCharacteristic); 
00163     } 
00164 }
00165 
00166 void connectionCallback(const Gap::ConnectionCallbackParams_t *params) {
00167     printf("Connected to ST Node now...\r\n");
00168     if (params->role == Gap::CENTRAL) {
00169         BLE &ble = BLE::Instance();
00170         ble.gattClient().onServiceDiscoveryTermination(discoveryTerminationCallback);
00171         const char *servUUIDString = "00000000-0001-11e1-9ab4-0002a5d5c51b";
00172         UUID servUUID(servUUIDString);
00173         const char *charUUIDString = "001c0000-0001-11e1-ac36-0002a5d5c51b";
00174         UUID charUUID(charUUIDString);
00175         ble.gattClient().launchServiceDiscovery(params->handle, serviceDiscoveryCallback, characteristicDiscoveryCallback, servUUID, charUUID);
00176     }
00177 }
00178 
00179 
00180 void triggerRead(const GattReadCallbackParams *response) {
00181     
00182     if (response->handle == envCharacteristic.getValueHandle()) {
00183         payload_length = response-> len;
00184         for(int i=0; i< response-> len; i++) {
00185 //          printf("%02x", response->data[i]);
00186             dataforClient[i] = response -> data[i];
00187     //      printf("%d", dataforClient[i]);
00188         }
00189     
00190         //printf("Temperature: %f\r\n", (uint32_t)((dataforClient[9]<<8) | dataforClient[8])/10.0);
00191         //printf("Humidity: %f\r\n", (uint32_t)((dataforClient[7]<<8) | dataforClient[6])/10.0);
00192         //printf("Pressure: %f\r\n\r\n\r\n", (uint32_t)((dataforClient[5]<<24) |(dataforClient[4]<<16) |(dataforClient[3]<<8) | dataforClient[2])/100.0);
00193         
00194         eventQueue.call_in(500, updateEnvCharacteristic);  // triggering BLE data read again in 0.5s
00195     } 
00196 }
00197 
00198 void triggerNotify(const GattHVXCallbackParams *response) {
00199     
00200     if (response->handle == envCharacteristic.getValueHandle()) {
00201         payload_length = response-> len;
00202         for(int i=0; i< response-> len; i++) {
00203 //          printf("%02x", response->data[i]);
00204             dataforClient[i] = response -> data[i];
00205     //      printf("%d", dataforClient[i]);
00206         }
00207     
00208         //printf("Temperature: %f\r\n", (uint32_t)((dataforClient[9]<<8) | dataforClient[8])/10.0);
00209         //printf("Humidity: %f\r\n", (uint32_t)((dataforClient[7]<<8) | dataforClient[6])/10.0);
00210         //printf("Pressure: %f\r\n\r\n\r\n", (uint32_t)((dataforClient[5]<<24) |(dataforClient[4]<<16) |(dataforClient[3]<<8) | dataforClient[2])/100.0);
00211 
00212     }
00213 }
00214 
00215 void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *) {
00216     printf("Got disconnected from the device!\r\n");
00217     /* Start scanning and try to connect again */
00218 #ifdef NUCLEO_F429ZI
00219     red_led = LED_OFF;
00220 #else
00221     green_led = LED_OFF;
00222 #endif
00223     BLE::Instance().gap().startScan(advertisementCallback);
00224 }
00225 
00226 void onBleInitError(BLE &ble, ble_error_t error)
00227 {
00228    /* Initialization error handling should go here */
00229     printf("BLE Error = %u\r\n", error);
00230 }
00231 
00232 void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
00233 {
00234     //printf("I'm inside BLE init\r\n");
00235     BLE&        ble   = params->ble;
00236     ble_error_t error = params->error;
00237     ble_error_t error1 = params->error;
00238 
00239     if (error != BLE_ERROR_NONE) {
00240         /* In case of error, forward the error handling to onBleInitError */
00241         onBleInitError(ble, error);         
00242         return;
00243     }
00244         
00245    /* Ensure that it is the default instance of BLE */
00246     if (ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
00247             printf("Not the default instance\r\n");
00248         return;
00249     }
00250 
00251     ble.gap().onDisconnection(disconnectionCallback);
00252     ble.gap().onConnection(connectionCallback);
00253 
00254 // On reading data, call triggerRead function.
00255     ble.gattClient().onDataRead(triggerRead);
00256     //ble.gattClient().onHVX(triggerNotify);
00257         // scan interval: 400ms and scan window: 400ms.
00258     // Every 400ms the device will scan for 400ms
00259     // This means that the device will scan continuously.
00260     ble.gap().setScanParams(400, 400);
00261     error =   ble.gap().startScan(advertisementCallback);
00262     if (error) {
00263         printf("BLE Error startScan = %u\r\n", error);
00264     }
00265         
00266 }
00267 
00268 void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
00269     BLE &ble = BLE::Instance();
00270     eventQueue.call(Callback<void()>(&ble, &BLE::processEvents));
00271 }
00272 
00273 //BLE thread init and further calls to other BLE methods.
00274 void BLE_thread_init(void){
00275     //printf("I'm inside BLE thread.....\r\n");
00276 
00277 //Schedule events before starting the thread since there might be some missed events while scanning / pairing.
00278     ble.onEventsToProcess(scheduleBleEventsProcessing);
00279     ble.init(bleInitComplete);
00280 //Loop forever the BLE thread
00281     eventQueue.dispatch_forever();
00282 }
00283 
00284 /************************************************************BLE Stuff to here *********************************/
00285 
00286 // These are example resource values for the Device Object
00287 struct MbedClientDevice device = {
00288     "Manufacturer_String",      // Manufacturer
00289     "Type_String",              // Type
00290     "ModelNumber_String",       // ModelNumber
00291     "SerialNumber_String"       // SerialNumber
00292 };
00293 
00294 // Instantiate the class which implements LWM2M Client API (from simpleclient.h)
00295 MbedClient mbed_client(device);
00296 
00297 
00298 class EnvResource {
00299 public:
00300     EnvResource() {
00301         
00302         temp_object = M2MInterfaceFactory::create_object("3303");
00303         temp_inst = temp_object->create_object_instance();
00304         temp_res = temp_inst->create_dynamic_resource("5700", "Temperature",
00305             M2MResourceInstance::FLOAT, true);  // observable
00306         // we can only read this value
00307         temp_res->set_operation(M2MBase::GET_ALLOWED);
00308         temp_res->set_value((uint8_t*)"20.0", 4);
00309         
00310         hum_object = M2MInterfaceFactory::create_object("3304");
00311         hum_inst = hum_object->create_object_instance();
00312         hum_res = hum_inst->create_dynamic_resource("5700", "Humidity",
00313             M2MResourceInstance::FLOAT, true);  // observable
00314         // we can only read this value
00315         hum_res->set_operation(M2MBase::GET_ALLOWED);
00316         hum_res->set_value((uint8_t*)"50.0", 4);
00317         
00318         press_object = M2MInterfaceFactory::create_object("3300");
00319         press_inst = press_object->create_object_instance();
00320         press_res = press_inst->create_dynamic_resource("5700", "Pressure",
00321             M2MResourceInstance::FLOAT, true);  // observable
00322         // we can only read this value
00323         press_res->set_operation(M2MBase::GET_ALLOWED);
00324         press_res->set_value((uint8_t*)"1000", 4);
00325         
00326         timer_res = press_inst->create_dynamic_resource("5603", "PollingPeriodMs",  // one polling time for all env values, associated to humidity
00327             M2MResourceInstance::INTEGER, false);   // not observable
00328         // we can read/wr this value
00329         timer_res->set_operation(M2MBase::GET_PUT_ALLOWED);
00330         timer_res->set_value((uint8_t*)"1000",4); // default 1s polling
00331         
00332     }
00333     
00334     M2MObject* get_temp_object() {
00335         return temp_object;
00336     }
00337     
00338     M2MObject* get_hum_object() {
00339         return hum_object;
00340     }
00341     
00342     M2MObject* get_press_object() {
00343         return press_object;
00344     }
00345     
00346     int get_polling_period() {
00347         return timer_res->get_value_int();
00348     }
00349 
00350     void update_resources() {
00351        float temp;
00352        float hum;
00353        float press;
00354       
00355       if (!envDataAvailable) {
00356           return;
00357       }
00358       
00359       temp = (uint32_t)((dataforClient[9]<<8) | dataforClient[8])/10.0;
00360       hum = ((dataforClient[7]<<8) | dataforClient[6])/10.0;
00361       press = (uint32_t)((dataforClient[5]<<24) |(dataforClient[4]<<16) |(dataforClient[3]<<8) | dataforClient[2])/100.0;
00362       
00363       stringstream ss_temp;
00364       ss_temp << temp;
00365       std::string stringified = ss_temp.str();
00366       temp_res->set_value((uint8_t*)stringified.c_str(), stringified.length());
00367       
00368       stringstream ss_hum;
00369       ss_hum << hum;
00370       stringified = ss_hum.str();
00371       hum_res->set_value((uint8_t*)stringified.c_str(), stringified.length());
00372       
00373       stringstream ss_press;
00374       ss_press << press;
00375       stringified = ss_press.str();
00376       press_res->set_value((uint8_t*)stringified.c_str(), stringified.length());
00377       
00378    }
00379     
00380 private:
00381 
00382    M2MObject* temp_object;
00383    M2MObjectInstance* temp_inst;
00384    M2MResource* temp_res;
00385    
00386    M2MObject* hum_object;
00387    M2MObjectInstance* hum_inst;
00388    M2MResource* hum_res;
00389    
00390    M2MObject* press_object;
00391    M2MObjectInstance* press_inst;
00392    M2MResource* press_res;
00393    
00394    M2MResource* timer_res;
00395     
00396 };
00397 
00398 
00399 osThreadId mainThread;
00400 
00401 // Entry point to the program
00402 int main() {
00403 
00404     unsigned int seed;
00405     size_t len;
00406 
00407 #ifdef MBEDTLS_ENTROPY_HARDWARE_ALT
00408     // Used to randomize source port
00409     mbedtls_hardware_poll(NULL, (unsigned char *) &seed, sizeof seed, &len);
00410 
00411 #elif defined MBEDTLS_TEST_NULL_ENTROPY
00412 
00413 #warning "mbedTLS security feature is disabled. Connection will not be secure !! Implement proper hardware entropy for your selected hardware."
00414     // Used to randomize source port
00415     mbedtls_null_entropy_poll( NULL,(unsigned char *) &seed, sizeof seed, &len);
00416 
00417 #else
00418 
00419 #error "This hardware does not have entropy, endpoint will not register to Connector.\
00420 You need to enable NULL ENTROPY for your application, but if this configuration change is made then no security is offered by mbed TLS.\
00421 Add MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES and MBEDTLS_TEST_NULL_ENTROPY in mbed_app.json macros to register your endpoint."
00422 
00423 #endif
00424 
00425     srand(seed);
00426     red_led = LED_OFF;
00427     blue_led = LED_OFF;
00428     green_led = LED_OFF;
00429 
00430     // Keep track of the main thread
00431     mainThread = osThreadGetId();
00432 
00433     printf("\nStarting mbed Client example\n");
00434 
00435     //mbed_trace_init();
00436 
00437     NetworkInterface* network = easy_connect(true);
00438     if(network == NULL) {
00439         printf("\nConnection to Network Failed - exiting application...\n");
00440         return -1;
00441     }
00442     
00443     // environmental data
00444     EnvResource env_resource;
00445     
00446 
00447     // Create endpoint interface to manage register and unregister
00448     mbed_client.create_interface(MBED_SERVER_ADDRESS, network);
00449 
00450     // Create Objects of varying types, see simpleclient.h for more details on implementation.
00451     M2MSecurity* register_object = mbed_client.create_register_object(); // server object specifying connector info
00452     M2MDevice*   device_object   = mbed_client.create_device_object();   // device resources object
00453 
00454     // Create list of Objects to register
00455     M2MObjectList object_list;
00456 
00457     // Add objects to list
00458     object_list.push_back(device_object);
00459     object_list.push_back(env_resource.get_temp_object());
00460     object_list.push_back(env_resource.get_hum_object());
00461     object_list.push_back(env_resource.get_press_object());
00462     
00463     // Set endpoint registration object
00464     mbed_client.set_register_object(register_object);
00465 
00466     // Register with mbed Device Connector
00467     mbed_client.test_register(register_object, object_list);
00468 
00469     // wait for registration and BLE data started flushing
00470     while (!mbed_client.register_successful())  {
00471         Thread::wait(500);
00472     }
00473     
00474     printf("\nNow starting BLE thread\n");
00475     BLE_thread.start(BLE_thread_init);
00476     
00477     while (!envDataAvailable) {
00478         Thread::wait(500);
00479     }
00480     
00481     printf ("\nNow pushing data to the mbed device connector.\n");
00482     
00483     while (true) {
00484         
00485           env_resource.update_resources();
00486           int timer_val = env_resource.get_polling_period();
00487           Thread::wait(timer_val);
00488     }
00489 
00490     mbed_client.test_unregister();
00491 
00492 }