mbedOS client example, modified to push X-Nucleo-IKS01A1/2 Environmental Sensor data to mbed Cloud Connector.
Dependencies: X_NUCLEO_IKS01A1 X_NUCLEO_IKS01A2
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 00017 /* 00018 * Modified by STMicroelectronics to support X-NUCLEO-IKS01A1/2 environmental 00019 * sensors on top of Nucleo-F429ZI. 00020 */ 00021 00022 00023 #define __STDC_FORMAT_MACROS 00024 #include <inttypes.h> 00025 #include "simpleclient.h" 00026 #include <string> 00027 #include <sstream> 00028 #include <vector> 00029 #include "mbed-trace/mbed_trace.h" 00030 #include "mbedtls/entropy_poll.h" 00031 00032 #include "security.h" 00033 00034 #include "mbed.h" 00035 00036 // easy-connect compliancy, it has 2 sets of wifi pins we have only one 00037 #define MBED_CONF_APP_ESP8266_TX MBED_CONF_APP_WIFI_TX 00038 #define MBED_CONF_APP_ESP8266_RX MBED_CONF_APP_WIFI_RX 00039 #include "easy-connect/easy-connect.h" 00040 00041 #ifdef TARGET_STM 00042 #define RED_LED (LED3) 00043 #define GREEN_LED (LED1) 00044 #define BLUE_LED (LED2) 00045 #define LED_ON (1) 00046 #else // !TARGET_STM 00047 #define RED_LED (LED1) 00048 #define GREEN_LED (LED2) 00049 #define BLUE_LED (LED3) 00050 #define LED_ON (0) 00051 #endif // !TARGET_STM 00052 #define LED_OFF (!LED_ON) 00053 00054 // Status indication 00055 DigitalOut red_led(RED_LED); 00056 DigitalOut green_led(GREEN_LED); 00057 DigitalOut blue_led(BLUE_LED); 00058 00059 Ticker status_ticker; 00060 void blinky() { 00061 green_led = !green_led; 00062 } 00063 00064 // These are example resource values for the Device Object 00065 struct MbedClientDevice device = { 00066 "Manufacturer_String", // Manufacturer 00067 "Type_String", // Type 00068 "ModelNumber_String", // ModelNumber 00069 "SerialNumber_String" // SerialNumber 00070 }; 00071 00072 // Instantiate the class which implements LWM2M Client API (from simpleclient.h) 00073 MbedClient mbed_client(device); 00074 00075 00076 // In case of K64F board , there is button resource available 00077 // to change resource value and unregister 00078 #ifdef TARGET_K64F 00079 // Set up Hardware interrupt button. 00080 InterruptIn obs_button(SW2); 00081 InterruptIn unreg_button(SW3); 00082 #else 00083 //In non K64F boards , set up a timer to simulate updating resource, 00084 // there is no functionality to unregister. 00085 Ticker timer; 00086 #endif 00087 00088 /* 00089 * Arguments for running "blink" in it's own thread. 00090 */ 00091 class BlinkArgs { 00092 public: 00093 BlinkArgs() { 00094 clear(); 00095 } 00096 void clear() { 00097 position = 0; 00098 blink_pattern.clear(); 00099 } 00100 uint16_t position; 00101 std::vector<uint32_t> blink_pattern; 00102 }; 00103 00104 /* 00105 * The Led contains one property (pattern) and a function (blink). 00106 * When the function blink is executed, the pattern is read, and the LED 00107 * will blink based on the pattern. 00108 */ 00109 class LedResource { 00110 public: 00111 LedResource() { 00112 // create ObjectID with metadata tag of '3201', which is 'digital output' 00113 led_object = M2MInterfaceFactory::create_object("3201"); 00114 M2MObjectInstance* led_inst = led_object->create_object_instance(); 00115 00116 // 5853 = Multi-state output 00117 M2MResource* pattern_res = led_inst->create_dynamic_resource("5853", "Pattern", 00118 M2MResourceInstance::STRING, false); 00119 // read and write 00120 pattern_res->set_operation(M2MBase::GET_PUT_ALLOWED); 00121 // set initial pattern (toggle every 200ms. 7 toggles in total) 00122 pattern_res->set_value((const uint8_t*)"500:500:500:500:500:500:500", 27); 00123 00124 // there's not really an execute LWM2M ID that matches... hmm... 00125 M2MResource* led_res = led_inst->create_dynamic_resource("5850", "Blink", 00126 M2MResourceInstance::OPAQUE, false); 00127 // we allow executing a function here... 00128 led_res->set_operation(M2MBase::POST_ALLOWED); 00129 // when a POST comes in, we want to execute the led_execute_callback 00130 led_res->set_execute_function(execute_callback(this, &LedResource::blink)); 00131 // Completion of execute function can take a time, that's why delayed response is used 00132 led_res->set_delayed_response(true); 00133 blink_args = new BlinkArgs(); 00134 } 00135 00136 ~LedResource() { 00137 delete blink_args; 00138 } 00139 00140 M2MObject* get_object() { 00141 return led_object; 00142 } 00143 00144 void blink(void *argument) { 00145 // read the value of 'Pattern' 00146 status_ticker.detach(); 00147 green_led = LED_OFF; 00148 00149 M2MObjectInstance* inst = led_object->object_instance(); 00150 M2MResource* res = inst->resource("5853"); 00151 // Clear previous blink data 00152 blink_args->clear(); 00153 00154 // values in mbed Client are all buffers, and we need a vector of int's 00155 uint8_t* buffIn = NULL; 00156 uint32_t sizeIn; 00157 res->get_value(buffIn, sizeIn); 00158 00159 // turn the buffer into a string, and initialize a vector<int> on the heap 00160 std::string s((char*)buffIn, sizeIn); 00161 free(buffIn); 00162 printf("led_execute_callback pattern=%s\n", s.c_str()); 00163 00164 // our pattern is something like 500:200:500, so parse that 00165 std::size_t found = s.find_first_of(":"); 00166 while (found!=std::string::npos) { 00167 blink_args->blink_pattern.push_back(atoi((const char*)s.substr(0,found).c_str())); 00168 s = s.substr(found+1); 00169 found=s.find_first_of(":"); 00170 if(found == std::string::npos) { 00171 blink_args->blink_pattern.push_back(atoi((const char*)s.c_str())); 00172 } 00173 } 00174 // check if POST contains payload 00175 if (argument) { 00176 M2MResource::M2MExecuteParameter* param = (M2MResource::M2MExecuteParameter*)argument; 00177 String object_name = param->get_argument_object_name(); 00178 uint16_t object_instance_id = param->get_argument_object_instance_id(); 00179 String resource_name = param->get_argument_resource_name(); 00180 int payload_length = param->get_argument_value_length(); 00181 uint8_t* payload = param->get_argument_value(); 00182 printf("Resource: %s/%d/%s executed\n", object_name.c_str(), object_instance_id, resource_name.c_str()); 00183 printf("Payload: %.*s\n", payload_length, payload); 00184 } 00185 // do_blink is called with the vector, and starting at -1 00186 blinky_thread.start(callback(this, &LedResource::do_blink)); 00187 } 00188 00189 private: 00190 M2MObject* led_object; 00191 Thread blinky_thread; 00192 BlinkArgs *blink_args; 00193 void do_blink() { 00194 for (;;) { 00195 // blink the LED 00196 red_led = !red_led; 00197 // up the position, if we reached the end of the vector 00198 if (blink_args->position >= blink_args->blink_pattern.size()) { 00199 // send delayed response after blink is done 00200 M2MObjectInstance* inst = led_object->object_instance(); 00201 M2MResource* led_res = inst->resource("5850"); 00202 led_res->send_delayed_post_response(); 00203 red_led = LED_OFF; 00204 status_ticker.attach_us(blinky, 250000); 00205 return; 00206 } 00207 // Wait requested time, then continue prosessing the blink pattern from next position. 00208 Thread::wait(blink_args->blink_pattern.at(blink_args->position)); 00209 blink_args->position++; 00210 } 00211 } 00212 }; 00213 00214 /* 00215 * The button contains one property (click count). 00216 * When `handle_button_click` is executed, the counter updates. 00217 */ 00218 class ButtonResource { 00219 public: 00220 ButtonResource(): counter(0) { 00221 // create ObjectID with metadata tag of '3200', which is 'digital input' 00222 btn_object = M2MInterfaceFactory::create_object("3200"); 00223 M2MObjectInstance* btn_inst = btn_object->create_object_instance(); 00224 // create resource with ID '5501', which is digital input counter 00225 M2MResource* btn_res = btn_inst->create_dynamic_resource("5501", "Button", 00226 M2MResourceInstance::INTEGER, true /* observable */); 00227 // we can read this value 00228 btn_res->set_operation(M2MBase::GET_ALLOWED); 00229 // set initial value (all values in mbed Client are buffers) 00230 // to be able to read this data easily in the Connector console, we'll use a string 00231 btn_res->set_value((uint8_t*)"0", 1); 00232 } 00233 00234 ~ButtonResource() { 00235 } 00236 00237 M2MObject* get_object() { 00238 return btn_object; 00239 } 00240 00241 /* 00242 * When you press the button, we read the current value of the click counter 00243 * from mbed Device Connector, then up the value with one. 00244 */ 00245 void handle_button_click() { 00246 if (mbed_client.register_successful()) { 00247 M2MObjectInstance* inst = btn_object->object_instance(); 00248 M2MResource* res = inst->resource("5501"); 00249 00250 // up counter 00251 counter++; 00252 #if defined(TARGET_K64F) || defined(TARGET_NUCLEO_F429ZI) 00253 printf("handle_button_click, new value of counter is %d\r\n", counter); 00254 #else 00255 printf("simulate button_click, new value of counter is %d\n", counter); 00256 #endif 00257 // serialize the value of counter as a string, and tell connector 00258 char buffer[20]; 00259 int size = sprintf(buffer,"%d",counter); 00260 res->set_value((uint8_t*)buffer, size); 00261 } else { 00262 printf("simulate button_click, device not registered\n"); 00263 } 00264 } 00265 00266 private: 00267 M2MObject* btn_object; 00268 uint16_t counter; 00269 }; 00270 00271 class BigPayloadResource { 00272 public: 00273 BigPayloadResource() { 00274 big_payload = M2MInterfaceFactory::create_object("1000"); 00275 M2MObjectInstance* payload_inst = big_payload->create_object_instance(); 00276 M2MResource* payload_res = payload_inst->create_dynamic_resource("1", "BigData", 00277 M2MResourceInstance::STRING, true /* observable */); 00278 payload_res->set_operation(M2MBase::GET_PUT_ALLOWED); 00279 payload_res->set_value((uint8_t*)"0", 1); 00280 payload_res->set_incoming_block_message_callback( 00281 incoming_block_message_callback(this, &BigPayloadResource::block_message_received)); 00282 payload_res->set_outgoing_block_message_callback( 00283 outgoing_block_message_callback(this, &BigPayloadResource::block_message_requested)); 00284 } 00285 00286 M2MObject* get_object() { 00287 return big_payload; 00288 } 00289 00290 void block_message_received(M2MBlockMessage *argument) { 00291 if (argument) { 00292 if (M2MBlockMessage::ErrorNone == argument->error_code()) { 00293 if (argument->is_last_block()) { 00294 printf("Last block received\n"); 00295 } 00296 printf("Block number: %d\n", argument->block_number()); 00297 // First block received 00298 if (argument->block_number() == 0) { 00299 // Store block 00300 // More blocks coming 00301 } else { 00302 // Store blocks 00303 } 00304 } else { 00305 printf("Error when receiving block message! - EntityTooLarge\n"); 00306 } 00307 printf("Total message size: %" PRIu32 "\n", argument->total_message_size()); 00308 } 00309 } 00310 00311 void block_message_requested(const String& resource, uint8_t *&/*data*/, uint32_t &/*len*/) { 00312 printf("GET request received for resource: %s\n", resource.c_str()); 00313 // Copy data and length to coap response 00314 } 00315 00316 private: 00317 M2MObject* big_payload; 00318 }; 00319 00320 /* STMicroelectronics IKS01A1/2 code - BEGIN */ 00321 00322 #define IKS01A2 // comment out for IKS01A1 00323 00324 #ifdef IKS01A2 00325 #define __ARCHDEP__TYPES // fix to a typedef redefinition with LWIP stack 00326 typedef unsigned char u8_t; 00327 typedef unsigned short int u16_t; 00328 //typedef unsigned int u32_t; // conflictiong definition 00329 typedef int i32_t; 00330 typedef short int i16_t; 00331 typedef signed char i8_t; 00332 #include "XNucleoIKS01A2.h" 00333 #else 00334 #include "XNucleoIKS01A1.h" 00335 #endif 00336 00337 #define SENSOR_OK 0 00338 #define DEFAULT_ENV_POLLING_PERIOD_MS 5000 // default polling time 00339 00340 class EnvResource { 00341 public: 00342 EnvResource(const char* endpointNumber, const char* resName) { 00343 #ifdef IKS01A2 00344 p_mems_expansion_board = XNucleoIKS01A2::instance(IKS01A2_PIN_I2C_SDA,IKS01A2_PIN_I2C_SCL); 00345 #else 00346 p_mems_expansion_board = XNucleoIKS01A1::instance(IKS01A1_PIN_I2C_SDA,IKS01A1_PIN_I2C_SCL); 00347 #endif 00348 env_object = M2MInterfaceFactory::create_object(endpointNumber); 00349 M2MObjectInstance* env_inst = env_object->create_object_instance(); 00350 M2MResource* env_res = env_inst->create_dynamic_resource("5700", resName, 00351 M2MResourceInstance::FLOAT, true); // observable 00352 // we can only read this value 00353 env_res->set_operation(M2MBase::GET_ALLOWED); 00354 00355 M2MResource* timer_res = env_inst->create_dynamic_resource("5603", "PollingPeriodMs", 00356 M2MResourceInstance::INTEGER, false); // not observable 00357 // we can read/wr this value 00358 timer_res->set_operation(M2MBase::GET_PUT_ALLOWED); 00359 timer_res->set_value(DEFAULT_ENV_POLLING_PERIOD_MS); 00360 } 00361 00362 virtual void init_resource()=0; 00363 00364 M2MObject* get_object() { 00365 return env_object; 00366 } 00367 00368 void start_measure (void) { 00369 env_thread.start(this, &EnvResource::read_env); 00370 } 00371 00372 protected: 00373 #ifdef IKS01A2 00374 XNucleoIKS01A2 *p_mems_expansion_board; 00375 #else 00376 XNucleoIKS01A1 *p_mems_expansion_board; 00377 #endif 00378 M2MObject* env_object; 00379 00380 virtual void update_resource(M2MResource* resource)=0; 00381 00382 void read_env(void) { 00383 00384 M2MObjectInstance* inst = env_object->object_instance(); 00385 M2MResource* res_env = inst->resource("5700"); 00386 M2MResource* res_timer = inst->resource("5603"); 00387 00388 while (1) { 00389 env_mutex.lock(); // avoid concurrent access to the component 00390 update_resource(res_env); 00391 env_mutex.unlock(); 00392 int timer_val = res_timer->get_value_int(); 00393 Thread::wait(timer_val); 00394 } 00395 } 00396 00397 private: 00398 Thread env_thread; 00399 static Mutex env_mutex; 00400 }; 00401 00402 Mutex EnvResource::env_mutex; 00403 00404 class TempResource : public EnvResource { 00405 public: 00406 TempResource() : EnvResource("3303", "Temperature") {} 00407 00408 void init_resource () { 00409 M2MObjectInstance* inst = env_object->object_instance(); 00410 M2MResource* resource = inst->resource("5700"); 00411 resource->set_value((uint8_t*)"20.0", 4); 00412 #ifdef IKS01A2 // need to explicitly enable the sensor with IKS01A2 00413 HTS221Sensor *hum_temp = p_mems_expansion_board->ht_sensor; 00414 hum_temp->enable(); 00415 #endif 00416 } 00417 00418 protected: 00419 void update_resource (M2MResource* resource) { 00420 float temp; 00421 int err; 00422 err = p_mems_expansion_board->ht_sensor->get_temperature(&temp); 00423 if ( err != SENSOR_OK) { 00424 printf ("= * ERROR %d get_temperature\n\r", err); 00425 return; 00426 } else { 00427 printf ("Temp C: %f\n\r", temp); 00428 } 00429 stringstream ss; 00430 ss << temp; 00431 std::string stringified = ss.str(); 00432 resource->set_value((uint8_t*)stringified.c_str(), stringified.length()); 00433 } 00434 00435 }; 00436 00437 class HumResource : public EnvResource { 00438 public: 00439 HumResource() : EnvResource("3304", "Humidity") {} 00440 00441 void init_resource () { 00442 M2MObjectInstance* inst = env_object->object_instance(); 00443 M2MResource* resource = inst->resource("5700"); 00444 resource->set_value((uint8_t*)"50.0", 4); 00445 #ifdef IKS01A2 // need to explicitly enable the sensor with IKS01A2 00446 HTS221Sensor *hum_temp = p_mems_expansion_board->ht_sensor; 00447 hum_temp->enable(); 00448 #endif 00449 } 00450 00451 protected: 00452 void update_resource (M2MResource* resource) { 00453 float hum; 00454 int err; 00455 err = p_mems_expansion_board->ht_sensor->get_humidity(&hum); 00456 if ( err != SENSOR_OK) { 00457 printf ("= * ERROR %d get_humidity\n\r", err); 00458 return; 00459 } else { 00460 printf ("Humidity %: %f\n\r", hum); 00461 } 00462 stringstream ss; 00463 ss << hum; 00464 std::string stringified = ss.str(); 00465 resource->set_value((uint8_t*)stringified.c_str(), stringified.length()); 00466 } 00467 }; 00468 00469 class PressResource : public EnvResource { 00470 public: 00471 PressResource() : EnvResource("3300", "Pressure") {} 00472 00473 void init_resource () { 00474 M2MObjectInstance* inst = env_object->object_instance(); 00475 M2MResource* resource = inst->resource("5700"); 00476 resource->set_value((uint8_t*)"1000", 4); 00477 #ifdef IKS01A2 // need to explicitly enable the sensor with IKS01A2 00478 LPS22HBSensor *press_temp = p_mems_expansion_board->pt_sensor; 00479 press_temp->enable(); 00480 #endif 00481 } 00482 00483 protected: 00484 void update_resource (M2MResource* resource) { 00485 float press; 00486 int err; 00487 err = p_mems_expansion_board->pt_sensor->get_pressure(&press); 00488 if ( err != SENSOR_OK) { 00489 printf ("= * ERROR %d get_pressure\n\r", err); 00490 return; 00491 } else { 00492 printf ("Pressure mBar: %f\n\r", press); 00493 } 00494 stringstream ss; 00495 ss << press; 00496 std::string stringified = ss.str(); 00497 resource->set_value((uint8_t*)stringified.c_str(), stringified.length()); 00498 } 00499 }; 00500 00501 /* STMicroelectronics IKS01A1/2 code - END */ 00502 00503 // Network interaction must be performed outside of interrupt context 00504 Semaphore updates(0); 00505 volatile bool registered = false; 00506 volatile bool clicked = false; 00507 osThreadId mainThread; 00508 00509 void unregister() { 00510 registered = false; 00511 updates.release(); 00512 } 00513 00514 void button_clicked() { 00515 clicked = true; 00516 updates.release(); 00517 } 00518 00519 // Entry point to the program 00520 int main() { 00521 00522 unsigned int seed; 00523 size_t len; 00524 00525 #ifdef MBEDTLS_ENTROPY_HARDWARE_ALT 00526 // Used to randomize source port 00527 mbedtls_hardware_poll(NULL, (unsigned char *) &seed, sizeof seed, &len); 00528 00529 #elif defined MBEDTLS_TEST_NULL_ENTROPY 00530 00531 #warning "mbedTLS security feature is disabled. Connection will not be secure !! Implement proper hardware entropy for your selected hardware." 00532 // Used to randomize source port 00533 mbedtls_null_entropy_poll( NULL,(unsigned char *) &seed, sizeof seed, &len); 00534 00535 #else 00536 00537 #error "This hardware does not have entropy, endpoint will not register to Connector.\ 00538 You need to enable NULL ENTROPY for your application, but if this configuration change is made then no security is offered by mbed TLS.\ 00539 Add MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES and MBEDTLS_TEST_NULL_ENTROPY in mbed_app.json macros to register your endpoint." 00540 00541 #endif 00542 00543 srand(seed); 00544 red_led = LED_OFF; 00545 blue_led = LED_OFF; 00546 00547 status_ticker.attach_us(blinky, 250000); 00548 // Keep track of the main thread 00549 mainThread = osThreadGetId(); 00550 00551 printf("\nStarting mbed Client example in "); 00552 #if defined (MESH) || (MBED_CONF_LWIP_IPV6_ENABLED==true) 00553 printf("IPv6 mode\n"); 00554 #else 00555 printf("IPv4 mode\n"); 00556 #endif 00557 00558 mbed_trace_init(); 00559 00560 NetworkInterface* network = easy_connect(true); 00561 if(network == NULL) { 00562 printf("\nConnection to Network Failed - exiting application...\n"); 00563 return -1; 00564 } 00565 00566 // we create our button and LED resources 00567 ButtonResource button_resource; 00568 LedResource led_resource; 00569 BigPayloadResource big_payload_resource; 00570 00571 // STMicroelectronics IKS01A1/2 00572 TempResource temp_resource; 00573 temp_resource.init_resource(); // set the default value 00574 HumResource hum_resource; 00575 hum_resource.init_resource(); // set the default value 00576 PressResource press_resource; 00577 press_resource.init_resource(); // set the default value 00578 00579 #ifdef TARGET_K64F 00580 // On press of SW3 button on K64F board, example application 00581 // will call unregister API towards mbed Device Connector 00582 //unreg_button.fall(&mbed_client,&MbedClient::test_unregister); 00583 unreg_button.fall(&unregister); 00584 00585 // Observation Button (SW2) press will send update of endpoint resource values to connector 00586 obs_button.fall(&button_clicked); 00587 #elif defined(TARGET_NUCLEO_F429ZI) 00588 InterruptIn user_button(USER_BUTTON); 00589 user_button.fall(&button_clicked); 00590 #else 00591 // Send update of endpoint resource values to connector every 15 seconds periodically 00592 timer.attach(&button_clicked, 15.0); 00593 #endif 00594 00595 // Create endpoint interface to manage register and unregister 00596 mbed_client.create_interface(MBED_SERVER_ADDRESS, network); 00597 00598 // Create Objects of varying types, see simpleclient.h for more details on implementation. 00599 M2MSecurity* register_object = mbed_client.create_register_object(); // server object specifying connector info 00600 M2MDevice* device_object = mbed_client.create_device_object(); // device resources object 00601 00602 // Create list of Objects to register 00603 M2MObjectList object_list; 00604 00605 // Add objects to list 00606 object_list.push_back(device_object); 00607 object_list.push_back(button_resource.get_object()); 00608 object_list.push_back(led_resource.get_object()); 00609 object_list.push_back(big_payload_resource.get_object()); 00610 00611 // STMicroelectronics IKS01A1/2 00612 object_list.push_back(temp_resource.get_object()); 00613 object_list.push_back(hum_resource.get_object()); 00614 object_list.push_back(press_resource.get_object()); 00615 00616 // Set endpoint registration object 00617 mbed_client.set_register_object(register_object); 00618 00619 // Register with mbed Device Connector 00620 mbed_client.test_register(register_object, object_list); 00621 registered = true; 00622 00623 // STMicroelectronics IKS01A1/2 00624 updates.wait(); // do not start before the user button is pressed once 00625 clicked = false; // do not increase the counter 00626 printf ("\r\nStart getting sensor data\r\n\n"); 00627 temp_resource.start_measure(); // start getting data from sensor 00628 hum_resource.start_measure(); // start getting data from sensor 00629 press_resource.start_measure(); // start getting data from sensor 00630 00631 while (true) { 00632 updates.wait(25000); 00633 if(registered) { 00634 if(!clicked) { 00635 mbed_client.test_update_register(); 00636 } 00637 }else { 00638 break; 00639 } 00640 if(clicked) { 00641 clicked = false; 00642 button_resource.handle_button_click(); 00643 } 00644 } 00645 00646 mbed_client.test_unregister(); 00647 status_ticker.detach(); 00648 }
Generated on Fri Jul 15 2022 13:50:43 by
1.7.2