mbedOS client example, modified to push X-Nucleo-IKS01A1/2 Environmental Sensor data to mbed Cloud Connector.

Dependencies:   X_NUCLEO_IKS01A1 X_NUCLEO_IKS01A2

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  
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 }