Osamu Nakamura
/
GR-PEACH_mbed-os-example-client
mbed-os-example-client which can run on GR-PEACH as-is
Diff: main.cpp
- Revision:
- 0:c0b64bdd4f07
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Mon Sep 04 16:41:44 2017 +0900 @@ -0,0 +1,470 @@ +/* + * Copyright (c) 2015, 2016 ARM Limited. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#define __STDC_FORMAT_MACROS +#include <inttypes.h> +#include "simpleclient.h" +#include <string> +#include <sstream> +#include <vector> +#include "mbed-trace/mbed_trace.h" +#include "mbedtls/entropy_poll.h" + +#include "security.h" + +#include "mbed.h" + +// easy-connect compliancy, it has 2 sets of wifi pins we have only one +#define MBED_CONF_APP_ESP8266_TX MBED_CONF_APP_WIFI_TX +#define MBED_CONF_APP_ESP8266_RX MBED_CONF_APP_WIFI_RX +#include "easy-connect/easy-connect.h" + +#ifdef TARGET_STM +#define RED_LED (LED3) +#define GREEN_LED (LED1) +#define BLUE_LED (LED2) +#define LED_ON (1) +#elif TARGET_RZ_A1H +#define RED_LED (LED1) +#define GREEN_LED (LED3) +#define BLUE_LED (LED3) +#define USER_LED (LED4) +#define LED_ON (1) +#else // !TARGET_STM +#define RED_LED (LED1) +#define GREEN_LED (LED2) +#define BLUE_LED (LED3) +#define LED_ON (0) +#endif // !TARGET_STM +#define LED_OFF (!LED_ON) + +// Status indication +DigitalOut red_led(RED_LED); +DigitalOut green_led(GREEN_LED); +DigitalOut blue_led(BLUE_LED); +DigitalOut user_led(USER_LED); + +Ticker status_ticker; +void blinky() { + green_led = !green_led; + +} +#ifdef TARGET_RZ_A1H +void blinky_status() { + user_led = !user_led; +} +#endif // TARGET_RZ_A1H + +// These are example resource values for the Device Object +struct MbedClientDevice device = { + "Manufacturer_String", // Manufacturer + "Type_String", // Type + "ModelNumber_String", // ModelNumber + "SerialNumber_String" // SerialNumber +}; + +// Instantiate the class which implements LWM2M Client API (from simpleclient.h) +MbedClient mbed_client(device); + + +// In case of K64F board , there is button resource available +// to change resource value and unregister +#ifdef TARGET_K64F +// Set up Hardware interrupt button. +InterruptIn obs_button(SW2); +InterruptIn unreg_button(SW3); +#elif TARGET_RZ_A1H +InterruptIn obs_button(USER_BUTTON0); +#else // TARGET_RZ_A1H +//In non K64F boards , set up a timer to simulate updating resource, +// there is no functionality to unregister. +Ticker timer; +#endif + +/* + * Arguments for running "blink" in it's own thread. + */ +class BlinkArgs { +public: + BlinkArgs() { + clear(); + } + void clear() { + position = 0; + blink_pattern.clear(); + } + uint16_t position; + std::vector<uint32_t> blink_pattern; +}; + +/* + * The Led contains one property (pattern) and a function (blink). + * When the function blink is executed, the pattern is read, and the LED + * will blink based on the pattern. + */ +class LedResource { +public: + LedResource() { + // create ObjectID with metadata tag of '3201', which is 'digital output' + led_object = M2MInterfaceFactory::create_object("3201"); + M2MObjectInstance* led_inst = led_object->create_object_instance(); + + // 5853 = Multi-state output + M2MResource* pattern_res = led_inst->create_dynamic_resource("5853", "Pattern", + M2MResourceInstance::STRING, false); + // read and write + pattern_res->set_operation(M2MBase::GET_PUT_ALLOWED); + // set initial pattern (toggle every 200ms. 7 toggles in total) + pattern_res->set_value((const uint8_t*)"500:500:500:500:500:500:500", 27); + + // there's not really an execute LWM2M ID that matches... hmm... + M2MResource* led_res = led_inst->create_dynamic_resource("5850", "Blink", + M2MResourceInstance::OPAQUE, false); + // we allow executing a function here... + led_res->set_operation(M2MBase::POST_ALLOWED); + // when a POST comes in, we want to execute the led_execute_callback + led_res->set_execute_function(execute_callback(this, &LedResource::blink)); + // Completion of execute function can take a time, that's why delayed response is used + led_res->set_delayed_response(true); + blink_args = new BlinkArgs(); + } + + ~LedResource() { + delete blink_args; + } + + M2MObject* get_object() { + return led_object; + } + + void blink(void *argument) { +#ifndef TARGET_RZ_A1H + // read the value of 'Pattern' + status_ticker.detach(); + green_led = LED_OFF; +#endif // TARGET_RZ_A1H + + M2MObjectInstance* inst = led_object->object_instance(); + M2MResource* res = inst->resource("5853"); + // Clear previous blink data + blink_args->clear(); + + // values in mbed Client are all buffers, and we need a vector of int's + uint8_t* buffIn = NULL; + uint32_t sizeIn; + res->get_value(buffIn, sizeIn); + + // turn the buffer into a string, and initialize a vector<int> on the heap + std::string s((char*)buffIn, sizeIn); + free(buffIn); + printf("led_execute_callback pattern=%s\n", s.c_str()); + + // our pattern is something like 500:200:500, so parse that + std::size_t found = s.find_first_of(":"); + while (found!=std::string::npos) { + blink_args->blink_pattern.push_back(atoi((const char*)s.substr(0,found).c_str())); + s = s.substr(found+1); + found=s.find_first_of(":"); + if(found == std::string::npos) { + blink_args->blink_pattern.push_back(atoi((const char*)s.c_str())); + } + } + // check if POST contains payload + if (argument) { + M2MResource::M2MExecuteParameter* param = (M2MResource::M2MExecuteParameter*)argument; + String object_name = param->get_argument_object_name(); + uint16_t object_instance_id = param->get_argument_object_instance_id(); + String resource_name = param->get_argument_resource_name(); + int payload_length = param->get_argument_value_length(); + uint8_t* payload = param->get_argument_value(); + printf("Resource: %s/%d/%s executed\n", object_name.c_str(), object_instance_id, resource_name.c_str()); + printf("Payload: %.*s\n", payload_length, payload); + } + // do_blink is called with the vector, and starting at -1 + blinky_thread.start(callback(this, &LedResource::do_blink)); + } + +private: + M2MObject* led_object; + Thread blinky_thread; + BlinkArgs *blink_args; + void do_blink() { + for (;;) { + // blink the LED + red_led = !red_led; + // up the position, if we reached the end of the vector + if (blink_args->position >= blink_args->blink_pattern.size()) { + // send delayed response after blink is done + M2MObjectInstance* inst = led_object->object_instance(); + M2MResource* led_res = inst->resource("5850"); + led_res->send_delayed_post_response(); +#ifndef TARGET_RZ_A1H + red_led = LED_OFF; + status_ticker.attach_us(blinky, 250000); +#endif // TARGET_RZ_A1H + return; + } + // Wait requested time, then continue prosessing the blink pattern from next position. + Thread::wait(blink_args->blink_pattern.at(blink_args->position)); + blink_args->position++; + } + } +}; + +/* + * The button contains one property (click count). + * When `handle_button_click` is executed, the counter updates. + */ +class ButtonResource { +public: + ButtonResource(): counter(0) { + // create ObjectID with metadata tag of '3200', which is 'digital input' + btn_object = M2MInterfaceFactory::create_object("3200"); + M2MObjectInstance* btn_inst = btn_object->create_object_instance(); + // create resource with ID '5501', which is digital input counter + M2MResource* btn_res = btn_inst->create_dynamic_resource("5501", "Button", + M2MResourceInstance::INTEGER, true /* observable */); + // we can read this value + btn_res->set_operation(M2MBase::GET_ALLOWED); + // set initial value (all values in mbed Client are buffers) + // to be able to read this data easily in the Connector console, we'll use a string + btn_res->set_value((uint8_t*)"0", 1); + } + + ~ButtonResource() { + } + + M2MObject* get_object() { + return btn_object; + } + + /* + * When you press the button, we read the current value of the click counter + * from mbed Device Connector, then up the value with one. + */ + void handle_button_click() { + if (mbed_client.register_successful()) { + M2MObjectInstance* inst = btn_object->object_instance(); + M2MResource* res = inst->resource("5501"); + + // up counter + counter++; + #if TARGET_K64F || TARGET_RZ_A1H + printf("handle_button_click, new value of counter is %d\n", counter); + #else + printf("simulate button_click, new value of counter is %d\n", counter); + #endif + // serialize the value of counter as a string, and tell connector + char buffer[20]; + int size = sprintf(buffer,"%d",counter); + res->set_value((uint8_t*)buffer, size); + } else { + printf("simulate button_click, device not registered\n"); + } + } + +private: + M2MObject* btn_object; + uint16_t counter; +}; + +class BigPayloadResource { +public: + BigPayloadResource() { + big_payload = M2MInterfaceFactory::create_object("1000"); + M2MObjectInstance* payload_inst = big_payload->create_object_instance(); + M2MResource* payload_res = payload_inst->create_dynamic_resource("1", "BigData", + M2MResourceInstance::STRING, true /* observable */); + payload_res->set_operation(M2MBase::GET_PUT_ALLOWED); + payload_res->set_value((uint8_t*)"0", 1); + payload_res->set_incoming_block_message_callback( + incoming_block_message_callback(this, &BigPayloadResource::block_message_received)); + payload_res->set_outgoing_block_message_callback( + outgoing_block_message_callback(this, &BigPayloadResource::block_message_requested)); + } + + M2MObject* get_object() { + return big_payload; + } + + void block_message_received(M2MBlockMessage *argument) { + if (argument) { + if (M2MBlockMessage::ErrorNone == argument->error_code()) { + if (argument->is_last_block()) { + printf("Last block received\n"); + } + printf("Block number: %d\n", argument->block_number()); + // First block received + if (argument->block_number() == 0) { + // Store block + // More blocks coming + } else { + // Store blocks + } + } else { + printf("Error when receiving block message! - EntityTooLarge\n"); + } + printf("Total message size: %" PRIu32 "\n", argument->total_message_size()); + } + } + + void block_message_requested(const String& resource, uint8_t *&/*data*/, uint32_t &/*len*/) { + printf("GET request received for resource: %s\n", resource.c_str()); + // Copy data and length to coap response + } + +private: + M2MObject* big_payload; +}; + +// Network interaction must be performed outside of interrupt context +Semaphore updates(0); +volatile bool registered = false; +volatile bool clicked = false; +osThreadId mainThread; + +void unregister() { + registered = false; + updates.release(); +} + +void button_clicked() { + clicked = true; + updates.release(); +} + +// set mac address +void mbed_mac_address(char *mac) { + mac[0] = 0x00; + mac[1] = 0x11; + mac[2] = 0x22; + mac[3] = 0x33; + mac[4] = 0x44; + mac[5] = 0x55; +} + +// Entry point to the program +int main() { + + unsigned int seed; + size_t len; + +#ifdef MBEDTLS_ENTROPY_HARDWARE_ALT + // Used to randomize source port + mbedtls_hardware_poll(NULL, (unsigned char *) &seed, sizeof seed, &len); + +#elif defined MBEDTLS_TEST_NULL_ENTROPY + +#warning "mbedTLS security feature is disabled. Connection will not be secure !! Implement proper hardware entropy for your selected hardware." + // Used to randomize source port + mbedtls_null_entropy_poll( NULL,(unsigned char *) &seed, sizeof seed, &len); + +#else + +#error "This hardware does not have entropy, endpoint will not register to Connector.\ +You need to enable NULL ENTROPY for your application, but if this configuration change is made then no security is offered by mbed TLS.\ +Add MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES and MBEDTLS_TEST_NULL_ENTROPY in mbed_app.json macros to register your endpoint." + +#endif + + srand(seed); + red_led = LED_OFF; + blue_led = LED_OFF; + +#ifndef TARGET_RZ_A1H + status_ticker.attach_us(blinky, 250000); +#else + status_ticker.attach_us(blinky_status, 250000); +#endif + // Keep track of the main thread + mainThread = osThreadGetId(); + + printf("\nStarting mbed Client example in "); +#if defined (MESH) || (MBED_CONF_LWIP_IPV6_ENABLED==true) + printf("IPv6 mode\n"); +#else + printf("IPv4 mode\n"); +#endif + + mbed_trace_init(); + + NetworkInterface* network = easy_connect(true); + if(network == NULL) { + printf("\nConnection to Network Failed - exiting application...\n"); + return -1; + } + + // we create our button and LED resources + ButtonResource button_resource; + LedResource led_resource; + BigPayloadResource big_payload_resource; + +#ifdef TARGET_K64F + // On press of SW3 button on K64F board, example application + // will call unregister API towards mbed Device Connector + //unreg_button.fall(&mbed_client,&MbedClient::test_unregister); + unreg_button.fall(&unregister); + + // Observation Button (SW2) press will send update of endpoint resource values to connector + obs_button.fall(&button_clicked); +#elif TARGET_RZ_A1H + // Observation Button (USER_BUTTON0) press will send update of endpoint resource values to connector + obs_button.fall(&button_clicked); +#else + // Send update of endpoint resource values to connector every 15 seconds periodically + timer.attach(&button_clicked, 15.0); +#endif + + // Create endpoint interface to manage register and unregister + mbed_client.create_interface(MBED_SERVER_ADDRESS, network); + + // Create Objects of varying types, see simpleclient.h for more details on implementation. + M2MSecurity* register_object = mbed_client.create_register_object(); // server object specifying connector info + M2MDevice* device_object = mbed_client.create_device_object(); // device resources object + + // Create list of Objects to register + M2MObjectList object_list; + + // Add objects to list + object_list.push_back(device_object); + object_list.push_back(button_resource.get_object()); + object_list.push_back(led_resource.get_object()); + object_list.push_back(big_payload_resource.get_object()); + + // Set endpoint registration object + mbed_client.set_register_object(register_object); + + // Register with mbed Device Connector + mbed_client.test_register(register_object, object_list); + registered = true; + + while (true) { + updates.wait(25000); + if(registered) { + if(!clicked) { + mbed_client.test_update_register(); + } + }else { + break; + } + if(clicked) { + clicked = false; + button_resource.handle_button_click(); + } + } + + mbed_client.test_unregister(); + status_ticker.detach(); +}