mbed-os-examples / Mbed OS mbed-os-example-client
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 <string>
00019 #include <sstream>
00020 #include <vector>
00021 #include "mbed-trace/mbed_trace.h"
00022 #include "mbedtls/entropy_poll.h"
00023 
00024 #include "security.h"
00025 
00026 #include "mbed.h"
00027 
00028 // easy-connect compliancy, it has 2 sets of wifi pins we have only one
00029 #define MBED_CONF_APP_ESP8266_TX MBED_CONF_APP_WIFI_TX
00030 #define MBED_CONF_APP_ESP8266_RX MBED_CONF_APP_WIFI_RX
00031 #include "easy-connect/easy-connect.h"
00032 
00033 // Should be defined after easy-connect.h
00034 #include "simpleclient.h"
00035 
00036 #ifdef TARGET_STM
00037 #define RED_LED (LED3)
00038 #define GREEN_LED (LED1)
00039 #define BLUE_LED (LED2)
00040 #define LED_ON (1)
00041 #else // !TARGET_STM
00042 #define RED_LED (LED1)
00043 #define GREEN_LED (LED2)
00044 #define BLUE_LED (LED3)
00045 #define LED_ON (0)
00046 #endif // !TARGET_STM
00047 #define LED_OFF (!LED_ON)
00048 
00049 #define BLINK_SIGNAL 0x1
00050 
00051 // Status indication
00052 DigitalOut red_led(RED_LED);
00053 DigitalOut green_led(GREEN_LED);
00054 DigitalOut blue_led(BLUE_LED);
00055 
00056 Ticker status_ticker;
00057 void blinky() {
00058     green_led = !green_led;
00059 }
00060 
00061 // These are example resource values for the Device Object
00062 struct MbedClientDevice device = {
00063     "Manufacturer_String",      // Manufacturer
00064     "Type_String",              // Type
00065     "ModelNumber_String",       // ModelNumber
00066     "SerialNumber_String"       // SerialNumber
00067 };
00068 
00069 // Instantiate the class which implements LWM2M Client API (from simpleclient.h)
00070 MbedClient mbed_client(device);
00071 
00072 // Set up a button interrupt for user interaction
00073 #ifdef MBED_CONF_APP_BUTTON1
00074     InterruptIn counter_btn(MBED_CONF_APP_BUTTON1);
00075 #endif
00076 
00077 
00078 /**
00079  * User interaction handler / simulator. Sets up physical button handler and a ticker
00080  * for regular updates for the resources.
00081  *
00082  * MBED_CONF_APP_BUTTON1 is mapped to actual button pin the mbed_app.json file, where you need to
00083  * specify board-specific value or leave it undefined if the board does not have buttons.
00084  */
00085 class InteractionProvider {
00086 
00087 public:
00088     InteractionProvider(Semaphore& updates_sem) : updates(updates_sem) {
00089 
00090         timer_ticked = false;
00091         clicked = false;
00092 
00093         // Set up handler function for the interaction button, if available
00094 
00095 #ifdef MBED_CONF_APP_BUTTON1
00096         counter_btn.fall(this, &InteractionProvider::counter_button_handler);
00097 #endif
00098 
00099         // Use the counter button handler to send an update of endpoint resource values
00100         // to connector every 15 seconds periodically.
00101         timer.attach(this, &InteractionProvider::timer_handler, 15.0);
00102     }
00103 
00104     // flags for interaction, these are read from outside interrupt context
00105     volatile bool timer_ticked;
00106     volatile bool clicked;
00107 
00108 
00109 private:
00110 
00111     void timer_handler() {
00112         timer_ticked = true;
00113         updates.release();
00114     }
00115 
00116     void counter_button_handler() {
00117         clicked = true;
00118         updates.release();
00119     }
00120 
00121     // time-based event source for regular resource updates
00122     Ticker timer;
00123 
00124     // Network interaction must be performed outside of interrupt context
00125     Semaphore& updates;
00126 
00127 };
00128 
00129 /*
00130  * Arguments for running "blink" in it's own thread.
00131  */
00132 class BlinkArgs {
00133 public:
00134     BlinkArgs() {
00135         clear();
00136     }
00137     void clear() {
00138         position = 0;
00139         blink_pattern.clear();
00140     }
00141     uint16_t position;
00142     std::vector<uint32_t> blink_pattern;
00143 };
00144 
00145 /*
00146  * The Led contains one property (pattern) and a function (blink).
00147  * When the function blink is executed, the pattern is read, and the LED
00148  * will blink based on the pattern.
00149  */
00150 class LedResource {
00151 public:
00152     LedResource() {
00153         // create ObjectID with metadata tag of '3201', which is 'digital output'
00154         blinky_thread.start(callback(this, &LedResource::do_blink));
00155         led_object = M2MInterfaceFactory::create_object("3201");
00156         M2MObjectInstance* led_inst = led_object->create_object_instance();
00157 
00158         // 5853 = Multi-state output
00159         M2MResource* pattern_res = led_inst->create_dynamic_resource("5853", "Pattern",
00160             M2MResourceInstance::STRING, false);
00161         // read and write
00162         pattern_res->set_operation(M2MBase::GET_PUT_ALLOWED);
00163         // set initial pattern (toggle every 200ms. 7 toggles in total)
00164         pattern_res->set_value((const uint8_t*)"500:500:500:500:500:500:500", 27);
00165 
00166         // there's not really an execute LWM2M ID that matches... hmm...
00167         M2MResource* led_res = led_inst->create_dynamic_resource("5850", "Blink",
00168             M2MResourceInstance::OPAQUE, false);
00169         // we allow executing a function here...
00170         led_res->set_operation(M2MBase::POST_ALLOWED);
00171         // when a POST comes in, we want to execute the led_execute_callback
00172         led_res->set_execute_function(execute_callback(this, &LedResource::blink));
00173         // Completion of execute function can take a time, that's why delayed response is used
00174         led_res->set_delayed_response(true);
00175         blink_args = new BlinkArgs();
00176     }
00177 
00178     ~LedResource() {
00179         delete blink_args;
00180     }
00181 
00182     M2MObject* get_object() {
00183         return led_object;
00184     }
00185 
00186     void blink(void *argument) {
00187         // read the value of 'Pattern'
00188         status_ticker.detach();
00189         green_led = LED_OFF;
00190 
00191         M2MObjectInstance* inst = led_object->object_instance();
00192         M2MResource* res = inst->resource("5853");
00193         // Clear previous blink data
00194         blink_args->clear();
00195 
00196         // values in mbed Client are all buffers, and we need a vector of int's
00197         uint8_t* buffIn = NULL;
00198         uint32_t sizeIn;
00199         res->get_value(buffIn, sizeIn);
00200 
00201         // turn the buffer into a string, and initialize a vector<int> on the heap
00202         std::string s((char*)buffIn, sizeIn);
00203         free(buffIn);
00204         printf("led_execute_callback pattern=%s\n", s.c_str());
00205 
00206         // our pattern is something like 500:200:500, so parse that
00207         std::size_t found = s.find_first_of(":");
00208         while (found!=std::string::npos) {
00209             blink_args->blink_pattern.push_back(atoi((const char*)s.substr(0,found).c_str()));
00210             s = s.substr(found+1);
00211             found=s.find_first_of(":");
00212             if(found == std::string::npos) {
00213                 blink_args->blink_pattern.push_back(atoi((const char*)s.c_str()));
00214             }
00215         }
00216         // check if POST contains payload
00217         if (argument) {
00218             M2MResource::M2MExecuteParameter* param = (M2MResource::M2MExecuteParameter*)argument;
00219             String object_name = param->get_argument_object_name();
00220             uint16_t object_instance_id = param->get_argument_object_instance_id();
00221             String resource_name = param->get_argument_resource_name();
00222             int payload_length = param->get_argument_value_length();
00223             const uint8_t* payload = param->get_argument_value();
00224             printf("Resource: %s/%d/%s executed\n", object_name.c_str(), object_instance_id, resource_name.c_str());
00225             printf("Payload: %.*s\n", payload_length, payload);
00226         }
00227         // do_blink is called with the vector, and starting at -1
00228         blinky_thread.signal_set(BLINK_SIGNAL);
00229     }
00230 
00231 private:
00232     M2MObject* led_object;
00233     Thread blinky_thread;
00234     BlinkArgs *blink_args;
00235     void do_blink() {
00236         for(;;) {
00237             blinky_thread.signal_wait(BLINK_SIGNAL);
00238             for (;;) {
00239                 // blink the LED
00240                 red_led = !red_led;
00241                 // up the position, if we reached the end of the vector
00242                 if (blink_args->position >= blink_args->blink_pattern.size()) {
00243                     // send delayed response after blink is done
00244                     M2MObjectInstance* inst = led_object->object_instance();
00245                     M2MResource* led_res = inst->resource("5850");
00246                     led_res->send_delayed_post_response();
00247                     red_led = LED_OFF;
00248                     status_ticker.attach_us(blinky, 250000);
00249                     break;
00250                 }
00251                 // Wait requested time, then continue prosessing the blink pattern from next position.
00252                 Thread::wait(blink_args->blink_pattern.at(blink_args->position));
00253                 blink_args->position++;
00254             }
00255         }
00256     }
00257 };
00258 
00259 /*
00260  * The button contains one property (click count).
00261  * When `handle_button_click` is executed, the counter updates.
00262  */
00263 class ButtonResource {
00264 public:
00265     ButtonResource(): counter(0) {
00266         // create ObjectID with metadata tag of '3200', which is 'digital input'
00267         btn_object = M2MInterfaceFactory::create_object("3200");
00268         M2MObjectInstance* btn_inst = btn_object->create_object_instance();
00269         // create resource with ID '5501', which is digital input counter
00270         M2MResource* btn_res = btn_inst->create_dynamic_resource("5501", "Button",
00271             M2MResourceInstance::INTEGER, true /* observable */);
00272         // we can read this value
00273         btn_res->set_operation(M2MBase::GET_ALLOWED);
00274         // set initial value (all values in mbed Client are buffers)
00275         // to be able to read this data easily in the Connector console, we'll use a string
00276         btn_res->set_value((uint8_t*)"0", 1);
00277     }
00278 
00279     ~ButtonResource() {
00280     }
00281 
00282     M2MObject* get_object() {
00283         return btn_object;
00284     }
00285 
00286     /*
00287      * When you press the button, we read the current value of the click counter
00288      * from mbed Device Connector, then up the value with one.
00289      */
00290     void handle_button_click() {
00291         if (mbed_client.register_successful()) {
00292             M2MObjectInstance* inst = btn_object->object_instance();
00293             M2MResource* res = inst->resource("5501");
00294 
00295             // up counter
00296             counter++;
00297             printf("handle_button_click, new value of counter is %d\n", counter);
00298             // serialize the value of counter as a string, and tell connector
00299             char buffer[20];
00300             int size = sprintf(buffer,"%d",counter);
00301             res->set_value((uint8_t*)buffer, size);
00302         } else {
00303             printf("simulate button_click, device not registered\n");
00304         }
00305     }
00306 
00307 private:
00308     M2MObject* btn_object;
00309     uint16_t counter;
00310 };
00311 
00312 /*
00313  * The timer contains one property: counter.
00314  * When `handle_timer_tick` is executed, the counter updates.
00315  */
00316 class TimerResource {
00317 public:
00318     TimerResource(): counter(0) {
00319         // create ObjectID with metadata tag of '3200', which is 'digital input'
00320         btn_object = M2MInterfaceFactory::create_object("3200");
00321         M2MObjectInstance* btn_inst = btn_object->create_object_instance();
00322         // create resource with ID '5502', which is digital input counter
00323         M2MResource* btn_res = btn_inst->create_dynamic_resource("5502", "Timer",
00324             M2MResourceInstance::INTEGER, true /* observable */);
00325         // we can read this value
00326         btn_res->set_operation(M2MBase::GET_ALLOWED);
00327         // set initial value (all values in mbed Client are buffers)
00328         // to be able to read this data easily in the Connector console, we'll use a string
00329         btn_res->set_value((uint8_t*)"0", 1);
00330     }
00331 
00332     ~TimerResource() {
00333     }
00334 
00335     M2MObject* get_object() {
00336         return btn_object;
00337     }
00338 
00339     /*
00340      * When the timer ticks, we read the current value of the click counter
00341      * from mbed Device Connector, then up the value with one.l
00342      */
00343     void handle_timer_tick() {
00344         if (mbed_client.register_successful()) {
00345             M2MObjectInstance* inst = btn_object->object_instance();
00346             M2MResource* res = inst->resource("5502");
00347 
00348             // up counter
00349             counter++;
00350             printf("handle_timer_click, new value of counter is %d\n", counter);
00351             // serialize the value of counter as a string, and tell connector
00352             char buffer[20];
00353             int size = sprintf(buffer,"%d",counter);
00354             res->set_value((uint8_t*)buffer, size);
00355         } else {
00356             printf("handle_timer_tick, device not registered\n");
00357         }
00358     }
00359 
00360 private:
00361     M2MObject* btn_object;
00362     uint16_t counter;
00363 };
00364 
00365 
00366 
00367 class BigPayloadResource {
00368 public:
00369     BigPayloadResource() {
00370         big_payload = M2MInterfaceFactory::create_object("1000");
00371         M2MObjectInstance* payload_inst = big_payload->create_object_instance();
00372         M2MResource* payload_res = payload_inst->create_dynamic_resource("1", "BigData",
00373             M2MResourceInstance::STRING, true /* observable */);
00374         payload_res->set_operation(M2MBase::GET_PUT_ALLOWED);
00375         payload_res->set_value((uint8_t*)"0", 1);
00376         payload_res->set_incoming_block_message_callback(
00377                     incoming_block_message_callback(this, &BigPayloadResource::block_message_received));
00378         payload_res->set_outgoing_block_message_callback(
00379                     outgoing_block_message_callback(this, &BigPayloadResource::block_message_requested));
00380     }
00381 
00382     M2MObject* get_object() {
00383         return big_payload;
00384     }
00385 
00386     void block_message_received(M2MBlockMessage *argument) {
00387         if (argument) {
00388             if (M2MBlockMessage::ErrorNone == argument->error_code()) {
00389                 if (argument->is_last_block()) {
00390                     printf("Last block received\n");
00391                 }
00392                 printf("Block number: %d\n", argument->block_number());
00393                 // First block received
00394                 if (argument->block_number() == 0) {
00395                     // Store block
00396                 // More blocks coming
00397                 } else {
00398                     // Store blocks
00399                 }
00400             } else {
00401                 printf("Error when receiving block message!  - EntityTooLarge\n");
00402             }
00403             printf("Total message size: %" PRIu32 "\n", argument->total_message_size());
00404         }
00405     }
00406 
00407     void block_message_requested(const String& resource, uint8_t *&/*data*/, uint32_t &/*len*/) {
00408         printf("GET request received for resource: %s\n", resource.c_str());
00409         // Copy data and length to coap response
00410     }
00411 
00412 private:
00413     M2MObject*  big_payload;
00414 };
00415 
00416 
00417 
00418 
00419 // debug printf function
00420 void trace_printer(const char* str) {
00421     printf("%s\r\n", str);
00422 }
00423 
00424 // Entry point to the program
00425 int main() {
00426 
00427     unsigned int seed;
00428     size_t len;
00429 
00430 #ifdef MBEDTLS_ENTROPY_HARDWARE_ALT
00431     // Used to randomize source port
00432     mbedtls_hardware_poll(NULL, (unsigned char *) &seed, sizeof seed, &len);
00433 
00434 #elif defined MBEDTLS_TEST_NULL_ENTROPY
00435 
00436 #warning "mbedTLS security feature is disabled. Connection will not be secure !! Implement proper hardware entropy for your selected hardware."
00437     // Used to randomize source port
00438     mbedtls_null_entropy_poll( NULL,(unsigned char *) &seed, sizeof seed, &len);
00439 
00440 #else
00441 
00442 #error "This hardware does not have entropy, endpoint will not register to Connector.\
00443 You need to enable NULL ENTROPY for your application, but if this configuration change is made then no security is offered by mbed TLS.\
00444 Add MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES and MBEDTLS_TEST_NULL_ENTROPY in mbed_app.json macros to register your endpoint."
00445 
00446 #endif
00447 
00448     srand(seed);
00449     red_led = LED_OFF;
00450     blue_led = LED_OFF;
00451 
00452     status_ticker.attach_us(blinky, 250000);
00453     // Keep track of the main thread
00454     osThreadId mainThread = osThreadGetId();
00455 
00456     printf("\nStarting mbed Client example\n");
00457 
00458     mbed_trace_init();
00459     mbed_trace_print_function_set(trace_printer);
00460     mbed_trace_config_set(TRACE_MODE_COLOR | TRACE_ACTIVE_LEVEL_INFO | TRACE_CARRIAGE_RETURN);
00461 
00462     NetworkInterface* network = easy_connect(true);
00463     if(network == NULL) {
00464         printf("\nConnection to Network Failed - exiting application...\n");
00465         return -1;
00466     }
00467 
00468     // we create our button, timer and LED resources
00469     ButtonResource button_resource;
00470     LedResource led_resource;
00471     BigPayloadResource big_payload_resource;
00472     TimerResource timer_resource;
00473 
00474     // Network interaction must be performed outside of interrupt context
00475     Semaphore updates(0);
00476 
00477     InteractionProvider interaction_provider(updates);
00478 
00479 
00480     // Create endpoint interface to manage register and unregister
00481     mbed_client.create_interface(MBED_SERVER_ADDRESS, network);
00482 
00483     // Create Objects of varying types, see simpleclient.h for more details on implementation.
00484     M2MSecurity* register_object = mbed_client.create_register_object(); // server object specifying connector info
00485     M2MDevice*   device_object   = mbed_client.create_device_object();   // device resources object
00486 
00487     // Create list of Objects to register
00488     M2MObjectList object_list;
00489 
00490     // Add objects to list
00491     object_list.push_back(device_object);
00492     object_list.push_back(button_resource.get_object());
00493     object_list.push_back(led_resource.get_object());
00494     object_list.push_back(big_payload_resource.get_object());
00495     object_list.push_back(timer_resource.get_object());
00496 
00497     // Set endpoint registration object
00498     mbed_client.set_register_object(register_object);
00499 
00500     // Register with mbed Device Connector
00501     mbed_client.test_register(register_object, object_list);
00502     volatile bool registered = true;
00503 
00504     while (true) {
00505         updates.wait(25000);
00506         if(registered) {
00507             if(!interaction_provider.clicked) {
00508                 mbed_client.test_update_register();
00509             }
00510         }else {
00511             break;
00512         }
00513         if(interaction_provider.clicked) {
00514             interaction_provider.clicked = false;
00515             button_resource.handle_button_click();
00516         }
00517         if(interaction_provider.timer_ticked) {
00518             interaction_provider.timer_ticked = false;
00519             timer_resource.handle_timer_tick();
00520         }
00521     }
00522 
00523     mbed_client.test_unregister();
00524     status_ticker.detach();
00525 }