Mbed OS Device Connector client pushing environmental sensor data via 6LowPan.
Dependencies: X_NUCLEO_IKS01A2
Fork of mbed-os-example-client-Sensors_6LowPan by
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/Nucleo-L476RG. 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 // betzw: Start blinky thread 00136 blinky_thread.start(callback(this, &LedResource::do_blink)); // betzw - NOTE: threads can only be started once! 00137 } 00138 00139 ~LedResource() { 00140 delete blink_args; 00141 } 00142 00143 M2MObject* get_object() { 00144 return led_object; 00145 } 00146 00147 void blink(void *argument) { 00148 // read the value of 'Pattern' 00149 status_ticker.detach(); 00150 green_led = LED_OFF; 00151 00152 M2MObjectInstance* inst = led_object->object_instance(); 00153 M2MResource* res = inst->resource("5853"); 00154 // Clear previous blink data 00155 blink_args->clear(); 00156 00157 // values in mbed Client are all buffers, and we need a vector of int's 00158 uint8_t* buffIn = NULL; 00159 uint32_t sizeIn; 00160 res->get_value(buffIn, sizeIn); 00161 00162 // turn the buffer into a string, and initialize a vector<int> on the heap 00163 std::string s((char*)buffIn, sizeIn); 00164 free(buffIn); 00165 printf("led_execute_callback pattern=%s\n", s.c_str()); 00166 00167 // our pattern is something like 500:200:500, so parse that 00168 std::size_t found = s.find_first_of(":"); 00169 while (found!=std::string::npos) { 00170 blink_args->blink_pattern.push_back(atoi((const char*)s.substr(0,found).c_str())); 00171 s = s.substr(found+1); 00172 found=s.find_first_of(":"); 00173 if(found == std::string::npos) { 00174 blink_args->blink_pattern.push_back(atoi((const char*)s.c_str())); 00175 } 00176 } 00177 // check if POST contains payload 00178 if (argument) { 00179 M2MResource::M2MExecuteParameter* param = (M2MResource::M2MExecuteParameter*)argument; 00180 String object_name = param->get_argument_object_name(); 00181 uint16_t object_instance_id = param->get_argument_object_instance_id(); 00182 String resource_name = param->get_argument_resource_name(); 00183 int payload_length = param->get_argument_value_length(); 00184 uint8_t* payload = param->get_argument_value(); 00185 printf("Resource: %s/%d/%s executed\n", object_name.c_str(), object_instance_id, resource_name.c_str()); 00186 printf("Payload: %.*s\n", payload_length, payload); 00187 } 00188 // do_blink is called with the vector, and starting at -1 00189 // blinky_thread.start(callback(this, &LedResource::do_blink)); // betzw - NOTE: threads can only be started once! 00190 blinky_thread.signal_set(1); 00191 } 00192 00193 private: 00194 M2MObject* led_object; 00195 Thread blinky_thread; 00196 BlinkArgs *blink_args; 00197 void do_blink() { 00198 blinky_thread.signal_wait(0); 00199 for (;;) { 00200 // blink the LED 00201 red_led = !red_led; 00202 // up the position, if we reached the end of the vector 00203 if (blink_args->position >= blink_args->blink_pattern.size()) { 00204 // send delayed response after blink is done 00205 M2MObjectInstance* inst = led_object->object_instance(); 00206 M2MResource* led_res = inst->resource("5850"); 00207 led_res->send_delayed_post_response(); 00208 red_led = LED_OFF; 00209 status_ticker.attach_us(blinky, 250000); 00210 // betzw - WAS: return; 00211 blinky_thread.signal_wait(0); 00212 continue; 00213 } 00214 // Wait requested time, then continue processing the blink pattern from next position. 00215 Thread::wait(blink_args->blink_pattern.at(blink_args->position)); 00216 blink_args->position++; 00217 } 00218 } 00219 }; 00220 00221 /* 00222 * The button contains one property (click count). 00223 * When `handle_button_click` is executed, the counter updates. 00224 */ 00225 class ButtonResource { 00226 public: 00227 ButtonResource(): counter(0) { 00228 // create ObjectID with metadata tag of '3200', which is 'digital input' 00229 btn_object = M2MInterfaceFactory::create_object("3200"); 00230 M2MObjectInstance* btn_inst = btn_object->create_object_instance(); 00231 // create resource with ID '5501', which is digital input counter 00232 M2MResource* btn_res = btn_inst->create_dynamic_resource("5501", "Button", 00233 M2MResourceInstance::INTEGER, true /* observable */); 00234 // we can read this value 00235 btn_res->set_operation(M2MBase::GET_ALLOWED); 00236 // set initial value (all values in mbed Client are buffers) 00237 // to be able to read this data easily in the Connector console, we'll use a string 00238 btn_res->set_value((uint8_t*)"0", 1); 00239 } 00240 00241 ~ButtonResource() { 00242 } 00243 00244 M2MObject* get_object() { 00245 return btn_object; 00246 } 00247 00248 /* 00249 * When you press the button, we read the current value of the click counter 00250 * from mbed Device Connector, then up the value with one. 00251 */ 00252 void handle_button_click() { 00253 if (mbed_client.register_successful()) { 00254 M2MObjectInstance* inst = btn_object->object_instance(); 00255 M2MResource* res = inst->resource("5501"); 00256 00257 // up counter 00258 counter++; 00259 #if defined(TARGET_K64F) || defined(TARGET_NUCLEO_F429ZI) || defined(TARGET_NUCLEO_L476RG) 00260 printf("handle_button_click, new value of counter is %d\r\n", counter); 00261 #else 00262 printf("simulate button_click, new value of counter is %d\n", counter); 00263 #endif 00264 // serialize the value of counter as a string, and tell connector 00265 char buffer[20]; 00266 int size = sprintf(buffer,"%d",counter); 00267 res->set_value((uint8_t*)buffer, size); 00268 } else { 00269 printf("simulate button_click, device not registered\n"); 00270 } 00271 } 00272 00273 private: 00274 M2MObject* btn_object; 00275 uint16_t counter; 00276 }; 00277 00278 class BigPayloadResource { 00279 public: 00280 BigPayloadResource() { 00281 big_payload = M2MInterfaceFactory::create_object("1000"); 00282 M2MObjectInstance* payload_inst = big_payload->create_object_instance(); 00283 M2MResource* payload_res = payload_inst->create_dynamic_resource("1", "BigData", 00284 M2MResourceInstance::STRING, true /* observable */); 00285 payload_res->set_operation(M2MBase::GET_PUT_ALLOWED); 00286 payload_res->set_value((uint8_t*)"0", 1); 00287 payload_res->set_incoming_block_message_callback( 00288 incoming_block_message_callback(this, &BigPayloadResource::block_message_received)); 00289 payload_res->set_outgoing_block_message_callback( 00290 outgoing_block_message_callback(this, &BigPayloadResource::block_message_requested)); 00291 } 00292 00293 M2MObject* get_object() { 00294 return big_payload; 00295 } 00296 00297 void block_message_received(M2MBlockMessage *argument) { 00298 if (argument) { 00299 if (M2MBlockMessage::ErrorNone == argument->error_code()) { 00300 if (argument->is_last_block()) { 00301 printf("Last block received\n"); 00302 } 00303 printf("Block number: %d\n", argument->block_number()); 00304 // First block received 00305 if (argument->block_number() == 0) { 00306 // Store block 00307 // More blocks coming 00308 } else { 00309 // Store blocks 00310 } 00311 } else { 00312 printf("Error when receiving block message! - EntityTooLarge\n"); 00313 } 00314 printf("Total message size: %" PRIu32 "\n", argument->total_message_size()); 00315 } 00316 } 00317 00318 void block_message_requested(const String& resource, uint8_t *&/*data*/, uint32_t &/*len*/) { 00319 printf("GET request received for resource: %s\n", resource.c_str()); 00320 // Copy data and length to coap response 00321 } 00322 00323 private: 00324 M2MObject* big_payload; 00325 }; 00326 00327 /* STMicroelectronics IKS01A1/2 code - BEGIN */ 00328 00329 #define IKS01A2 // comment out for IKS01A1 00330 00331 #ifdef IKS01A2 00332 #define __ARCHDEP__TYPES // fix to a typedef redefinition with LWIP stack 00333 typedef unsigned char u8_t; 00334 typedef unsigned short int u16_t; 00335 //typedef unsigned int u32_t; // conflictiong definition 00336 typedef int i32_t; 00337 typedef short int i16_t; 00338 typedef signed char i8_t; 00339 #include "XNucleoIKS01A2.h" 00340 #else 00341 #include "XNucleoIKS01A1.h" 00342 #endif 00343 00344 #define SENSOR_OK 0 00345 #define DEFAULT_ENV_POLLING_PERIOD_MS 5000 // default polling time 00346 00347 class EnvResource { 00348 public: 00349 EnvResource() { 00350 #ifdef IKS01A2 00351 p_mems_expansion_board = XNucleoIKS01A2::instance(IKS01A2_PIN_I2C_SDA,IKS01A2_PIN_I2C_SCL); 00352 #else 00353 p_mems_expansion_board = XNucleoIKS01A1::instance(IKS01A1_PIN_I2C_SDA,IKS01A1_PIN_I2C_SCL); 00354 #endif 00355 00356 temp_object = M2MInterfaceFactory::create_object("3303"); 00357 temp_inst = temp_object->create_object_instance(); 00358 temp_res = temp_inst->create_dynamic_resource("5700", "Temperature", 00359 M2MResourceInstance::FLOAT, true); // observable 00360 // we can only read this value 00361 temp_res->set_operation(M2MBase::GET_ALLOWED); 00362 temp_res->set_value((uint8_t*)"20.0", 4); 00363 00364 hum_object = M2MInterfaceFactory::create_object("3304"); 00365 hum_inst = hum_object->create_object_instance(); 00366 hum_res = hum_inst->create_dynamic_resource("5700", "Humidity", 00367 M2MResourceInstance::FLOAT, true); // observable 00368 // we can only read this value 00369 hum_res->set_operation(M2MBase::GET_ALLOWED); 00370 hum_res->set_value((uint8_t*)"50.0", 4); 00371 00372 #ifdef IKS01A2 // need to explicitly enable the sensor with IKS01A2 00373 HTS221Sensor *hum_temp = p_mems_expansion_board->ht_sensor; 00374 hum_temp->enable(); 00375 #endif 00376 00377 press_object = M2MInterfaceFactory::create_object("3300"); 00378 press_inst = press_object->create_object_instance(); 00379 press_res = press_inst->create_dynamic_resource("5700", "Pressure", 00380 M2MResourceInstance::FLOAT, true); // observable 00381 // we can only read this value 00382 press_res->set_operation(M2MBase::GET_ALLOWED); 00383 press_res->set_value((uint8_t*)"5000", 4); 00384 00385 #ifdef IKS01A2 // need to explicitly enable the sensor with IKS01A2 00386 LPS22HBSensor *press_temp = p_mems_expansion_board->pt_sensor; 00387 press_temp->enable(); 00388 #endif 00389 00390 timer_res = press_inst->create_dynamic_resource("5603", "PollingPeriodMs", // one polling time for all env values, associated to humidity 00391 M2MResourceInstance::INTEGER, false); // not observable 00392 // we can read/wr this value 00393 timer_res->set_operation(M2MBase::GET_PUT_ALLOWED); 00394 timer_res->set_value((uint8_t*)"1000",4); // default 1s polling 00395 00396 } 00397 00398 M2MObject* get_temp_object() { 00399 return temp_object; 00400 } 00401 00402 M2MObject* get_hum_object() { 00403 return hum_object; 00404 } 00405 00406 M2MObject* get_press_object() { 00407 return press_object; 00408 } 00409 00410 int get_polling_period() { 00411 return timer_res->get_value_int(); 00412 } 00413 00414 void update_resources() { 00415 float temp; 00416 float hum; 00417 float press; 00418 int err; 00419 00420 err = p_mems_expansion_board->ht_sensor->get_temperature(&temp); 00421 if ( err != SENSOR_OK) { 00422 printf ("= * ERROR %d get_temperature\n\r", err); 00423 temp = 20.0; 00424 red_led = 1; 00425 } else { 00426 printf ("Temp C: %f\n\r", temp); 00427 red_led = 0; 00428 } 00429 00430 err = p_mems_expansion_board->ht_sensor->get_humidity(&hum); 00431 if ( err != SENSOR_OK) { 00432 printf ("= * ERROR %d get_humidity\n\r", err); 00433 hum= 50.0; 00434 red_led = 1; 00435 } else { 00436 printf ("Humidity %: %f\n\r", hum); 00437 red_led = 0; 00438 } 00439 00440 err = p_mems_expansion_board->pt_sensor->get_pressure(&press); 00441 if ( err != SENSOR_OK) { 00442 printf ("= * ERROR %d get_pressure\n\r", err); 00443 red_led = 1; 00444 return; 00445 } else { 00446 printf ("Pressure mBar: %f\n\r", press); 00447 red_led = 0; 00448 } 00449 00450 stringstream ss_temp; 00451 ss_temp << temp; 00452 std::string stringified = ss_temp.str(); 00453 temp_res->set_value((uint8_t*)stringified.c_str(), stringified.length()); 00454 00455 stringstream ss_hum; 00456 ss_hum << hum; 00457 stringified = ss_hum.str(); 00458 hum_res->set_value((uint8_t*)stringified.c_str(), stringified.length()); 00459 00460 stringstream ss_press; 00461 ss_press << press; 00462 stringified = ss_press.str(); 00463 press_res->set_value((uint8_t*)stringified.c_str(), stringified.length()); 00464 } 00465 00466 private: 00467 00468 M2MObject* temp_object; 00469 M2MObjectInstance* temp_inst; 00470 M2MResource* temp_res; 00471 00472 M2MObject* hum_object; 00473 M2MObjectInstance* hum_inst; 00474 M2MResource* hum_res; 00475 00476 M2MObject* press_object; 00477 M2MObjectInstance* press_inst; 00478 M2MResource* press_res; 00479 00480 M2MResource* timer_res; 00481 00482 #ifdef IKS01A2 00483 XNucleoIKS01A2 *p_mems_expansion_board; 00484 #else 00485 XNucleoIKS01A1 *p_mems_expansion_board; 00486 #endif 00487 00488 }; 00489 00490 00491 /* STMicroelectronics IKS01A1/2 code - END */ 00492 00493 // Network interaction must be performed outside of interrupt context 00494 Semaphore updates(0); 00495 volatile bool registered = false; 00496 volatile bool clicked = false; 00497 osThreadId mainThread; 00498 00499 void unregister() { 00500 registered = false; 00501 blue_led = 0; 00502 updates.release(); 00503 } 00504 00505 void button_clicked() { 00506 clicked = true; 00507 } 00508 00509 // debug printf function 00510 void trace_printer(const char* str) { 00511 printf("%s\r\n", str); 00512 } 00513 00514 // Entry point to the program 00515 int main() { 00516 00517 unsigned int seed; 00518 size_t len; 00519 00520 #ifdef MBEDTLS_ENTROPY_HARDWARE_ALT 00521 // Used to randomize source port 00522 mbedtls_hardware_poll(NULL, (unsigned char *) &seed, sizeof seed, &len); 00523 00524 #elif defined MBEDTLS_TEST_NULL_ENTROPY 00525 00526 #warning "mbedTLS security feature is disabled. Connection will not be secure !! Implement proper hardware entropy for your selected hardware." 00527 // Used to randomize source port 00528 mbedtls_null_entropy_poll( NULL,(unsigned char *) &seed, sizeof seed, &len); 00529 00530 #else 00531 00532 #error "This hardware does not have entropy, endpoint will not register to Connector.\ 00533 You need to enable NULL ENTROPY for your application, but if this configuration change is made then no security is offered by mbed TLS.\ 00534 Add MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES and MBEDTLS_TEST_NULL_ENTROPY in mbed_app.json macros to register your endpoint." 00535 00536 #endif 00537 00538 srand(seed); 00539 red_led = LED_OFF; 00540 blue_led = LED_OFF; 00541 00542 status_ticker.attach_us(blinky, 250000); 00543 // Keep track of the main thread 00544 mainThread = osThreadGetId(); 00545 00546 printf("\nStarting mbed Client example\n"); 00547 00548 mbed_trace_init(); 00549 mbed_trace_print_function_set(trace_printer); 00550 mbed_trace_config_set(TRACE_MODE_COLOR | TRACE_ACTIVE_LEVEL_INFO | TRACE_CARRIAGE_RETURN); 00551 00552 NetworkInterface* network = easy_connect(true); 00553 if(network == NULL) { 00554 printf("\nConnection to Network Failed - exiting application...\n"); 00555 return -1; 00556 } 00557 00558 // we create our button and LED resources 00559 ButtonResource button_resource; 00560 LedResource led_resource; 00561 BigPayloadResource big_payload_resource; 00562 00563 // STMicroelectronics IKS01A1/2 00564 EnvResource env_resource; 00565 00566 00567 #ifdef TARGET_K64F 00568 // On press of SW3 button on K64F board, example application 00569 // will call unregister API towards mbed Device Connector 00570 //unreg_button.fall(&mbed_client,&MbedClient::test_unregister); 00571 unreg_button.fall(&unregister); 00572 00573 // Observation Button (SW2) press will send update of endpoint resource values to connector 00574 obs_button.fall(&button_clicked); 00575 #elif defined(TARGET_NUCLEO_F429ZI) || defined (TARGET_NUCLEO_L476RG) 00576 InterruptIn user_button(USER_BUTTON); 00577 user_button.fall(&button_clicked); 00578 #else 00579 // Send update of endpoint resource values to connector every 15 seconds periodically 00580 timer.attach(&button_clicked, 15.0); 00581 #endif 00582 00583 // Create endpoint interface to manage register and unregister 00584 mbed_client.create_interface(MBED_SERVER_ADDRESS, network); 00585 00586 // Create Objects of varying types, see simpleclient.h for more details on implementation. 00587 M2MSecurity* register_object = mbed_client.create_register_object(); // server object specifying connector info 00588 M2MDevice* device_object = mbed_client.create_device_object(); // device resources object 00589 00590 // Create list of Objects to register 00591 M2MObjectList object_list; 00592 00593 // Add objects to list 00594 object_list.push_back(device_object); 00595 object_list.push_back(button_resource.get_object()); 00596 object_list.push_back(led_resource.get_object()); 00597 object_list.push_back(big_payload_resource.get_object()); 00598 00599 // STMicroelectronics IKS01A1/2 00600 object_list.push_back(env_resource.get_temp_object()); 00601 object_list.push_back(env_resource.get_hum_object()); 00602 object_list.push_back(env_resource.get_press_object()); 00603 00604 // Set endpoint registration object 00605 mbed_client.set_register_object(register_object); 00606 00607 // Register with mbed Device Connector 00608 mbed_client.test_register(register_object, object_list); 00609 00610 00611 // STMicroelectronics IKS01A1/2 00612 // wait for registration before getting data 00613 while (!mbed_client.register_successful()) { 00614 Thread::wait(500); 00615 } 00616 registered = true; 00617 blue_led = 1; 00618 printf ("\r\nRegistered! Now getting sensor data\r\n\n"); 00619 00620 while (true) { 00621 00622 int timer_val = env_resource.get_polling_period(); 00623 env_resource.update_resources(); 00624 00625 if(clicked) { 00626 clicked = false; 00627 button_resource.handle_button_click(); 00628 } 00629 Thread::wait(timer_val); 00630 } 00631 00632 mbed_client.test_unregister(); 00633 status_ticker.detach(); 00634 }
Generated on Sat Jul 16 2022 04:52:17 by
1.7.2
