A simple interface to mbed Device Connector, where you just declare variables to push them to the cloud.
Dependents: Wifi_Get_Test_V1 simple-mbed-client-example simple-client-app-shield simple-sensor-client
Fork of simple-mbed-client by
simple-mbed-client.h
00001 /* 00002 * Copyright (c) 2015 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 #ifndef __SIMPLE_MBED_CLIENT_H__ 00018 #define __SIMPLE_MBED_CLIENT_H__ 00019 00020 #define smc_debug_msg(...) if (debug) output.printf(__VA_ARGS__) 00021 00022 #include <map> 00023 #include <string> 00024 #include <vector> 00025 #include "mbed-client-wrapper.h" 00026 00027 using namespace std; 00028 00029 class SimpleResourceBase { 00030 public: 00031 virtual void update(string v) {} 00032 virtual void clear_pending_value() {} 00033 }; 00034 00035 class SimpleMbedClientBase { 00036 public: 00037 00038 SimpleMbedClientBase(bool aDebug = true) 00039 : output(USBTX, USBRX), evQueue(new EventQueue()), evThread(new Thread()), debug(aDebug) 00040 { 00041 evThread->start(callback(evQueue, &EventQueue::dispatch_forever)); 00042 } 00043 00044 SimpleMbedClientBase(EventQueue* aQueue, bool aDebug = true) 00045 : output(USBTX, USBRX), evQueue(aQueue), debug(aDebug) 00046 { 00047 00048 } 00049 00050 ~SimpleMbedClientBase() { 00051 if (evThread) { 00052 delete evQueue; 00053 delete evThread; 00054 } 00055 // if no evThread then evQueue is not owned by us 00056 } 00057 00058 struct MbedClientOptions get_default_options() { 00059 struct MbedClientOptions options; 00060 options.Manufacturer = "Manufacturer_String"; 00061 options.Type = "Type_String"; 00062 options.ModelNumber = "ModelNumber_String"; 00063 options.SerialNumber = "SerialNumber_String"; 00064 options.DeviceType = "test"; 00065 options.SocketMode = M2MInterface::TCP; 00066 #ifdef MBED_SERVER_ADDRESS 00067 options.ServerAddress = MBED_SERVER_ADDRESS; 00068 #else 00069 options.ServerAddress = "coap://api.connector.mbed.com:5684"; 00070 #endif 00071 00072 return options; 00073 } 00074 00075 bool init(NetworkInterface* iface) { 00076 smc_debug_msg("[SMC] Device name %s\r\n", MBED_ENDPOINT_NAME); 00077 00078 // Create endpoint interface to manage register and unregister 00079 client->create_interface(iface); 00080 00081 // Create Objects of varying types, see simpleclient.h for more details on implementation. 00082 M2MSecurity* register_object = client->create_register_object(); // server object specifying connector info 00083 M2MDevice* device_object = client->create_device_object(); // device resources object 00084 00085 // Create list of Objects to register 00086 M2MObjectList object_list; 00087 00088 // Add objects to list 00089 object_list.push_back(device_object); 00090 00091 map<string, M2MObject*>::iterator it; 00092 for (it = objects.begin(); it != objects.end(); it++) 00093 { 00094 object_list.push_back(it->second); 00095 } 00096 00097 // Set endpoint registration object 00098 client->set_register_object(register_object); 00099 00100 // Issue register command. 00101 client->test_register(register_object, object_list); 00102 00103 // Keep alive ticker (every 25 seconds) 00104 evQueue->call_every(MBED_CONF_SIMPLE_MBED_CLIENT_UPDATE_INTERVAL, 00105 callback(this, &SimpleMbedClientBase::keep_alive)); 00106 00107 return true; 00108 } 00109 00110 bool setup(NetworkInterface* iface) { 00111 smc_debug_msg("[SMC] In mbed_client_setup\r\n"); 00112 if (client) { 00113 smc_debug_msg("[SMC] [ERROR] mbed_client_setup called, but mbed_client is already instantiated\r\n"); 00114 return false; 00115 } 00116 00117 struct MbedClientOptions options = get_default_options(); 00118 00119 Callback<void(string)> updateFp(this, &SimpleMbedClientBase::resource_updated); 00120 client = new MbedClient(options, updateFp, debug); 00121 00122 return init(iface); 00123 } 00124 00125 bool setup(MbedClientOptions options, NetworkInterface* iface) { 00126 if (client) { 00127 smc_debug_msg("[SMC] [ERROR] mbed_client_setup called, but mbed_client is already instantiated\r\n"); 00128 return false; 00129 } 00130 00131 Callback<void(string)> updateFp(this, &SimpleMbedClientBase::resource_updated); 00132 client = new MbedClient(options, updateFp, debug); 00133 00134 return init(iface); 00135 } 00136 00137 void on_registered(void(*fn)(void)) { 00138 Callback<void()> fp(fn); 00139 client->set_registered_function(fp); 00140 } 00141 00142 void on_registered(Callback<void()> fp) { 00143 client->set_registered_function(fp); 00144 } 00145 00146 template<typename T> 00147 void on_registered(T *object, void (T::*member)(void)) { 00148 Callback<void()> fp(object, member); 00149 client->set_registered_function(fp); 00150 } 00151 00152 void on_unregistered(void(*fn)(void)) { 00153 Callback<void()> fp(fn); 00154 client->set_unregistered_function(fp); 00155 } 00156 00157 void on_unregistered(Callback<void()> fp) { 00158 client->set_unregistered_function(fp); 00159 } 00160 00161 template<typename T> 00162 void on_unregistered(T *object, void (T::*member)(void)) { 00163 Callback<void()> fp(object, member); 00164 client->set_unregistered_function(fp); 00165 } 00166 00167 bool define_function(const char* route, Event<void(void*)> ev) { 00168 if (!define_resource_internal(route, string(), M2MBase::POST_ALLOWED, false)) { 00169 return false; 00170 } 00171 00172 string route_str(route); 00173 if (!resources.count(route_str)) { 00174 smc_debug_msg("[SMC] [ERROR] Should be created, but no such route (%s)\r\n", route); 00175 return false; 00176 } 00177 00178 // We need a copy of the event. The original event might go out of scope. 00179 // @todo, do we need to clear this? It's actually meant to live until the end of the program... But it's not nice to alloc and never free. 00180 Event<void(void*)>* copy = new Event<void(void*)>(ev); 00181 00182 // Event::call is const, which FP1 does not like. Cast it to non-const. 00183 FP1<void, void*> fp(copy, (void (events::Event<void(void*)>::*)(void*))&Event<void(void*)>::call); 00184 00185 resources[route_str]->set_execute_function(fp); 00186 return true; 00187 } 00188 00189 bool define_function(const char* route, void(*fn)(void*)) { 00190 if (!define_resource_internal(route, string(), M2MBase::POST_ALLOWED, false)) { 00191 return false; 00192 } 00193 00194 string route_str(route); 00195 if (!resources.count(route_str)) { 00196 smc_debug_msg("[SMC] [ERROR] Should be created, but no such route (%s)\r\n", route); 00197 return false; 00198 } 00199 00200 resources[route_str]->set_execute_function(execute_callback_2(fn)); 00201 return true; 00202 } 00203 00204 bool define_function(const char* route, execute_callback fn) { 00205 if (!define_resource_internal(route, string(), M2MBase::POST_ALLOWED, false)) { 00206 return false; 00207 } 00208 00209 string route_str(route); 00210 if (!resources.count(route_str)) { 00211 smc_debug_msg("[SMC] [ERROR] Should be created, but no such route (%s)\r\n", route); 00212 return false; 00213 } 00214 // No clue why this is not working?! It works with class member, but not with static function... 00215 resources[route_str]->set_execute_function(fn); 00216 return true; 00217 } 00218 00219 string get(string route_str) { 00220 if (!resources.count(route_str)) { 00221 smc_debug_msg("[SMC] [ERROR] No such route (%s)\r\n", route_str.c_str()); 00222 return string(); 00223 } 00224 00225 // otherwise ask mbed Client... 00226 uint8_t* buffIn = NULL; 00227 uint32_t sizeIn; 00228 resources[route_str]->get_value(buffIn, sizeIn); 00229 00230 string s((char*)buffIn, sizeIn); 00231 return s; 00232 } 00233 00234 // Note: these `set` calls are async. 00235 // SimpleResource* buffers the value, so when using it through operators you'll get the right value back. 00236 void set(string route_str, string v) { 00237 evQueue->call(callback(this, &SimpleMbedClientBase::internal_set_str), route_str, v); 00238 } 00239 00240 void set(string route_str, const int& v) { 00241 evQueue->call(callback(this, &SimpleMbedClientBase::internal_set_int), route_str, v); 00242 } 00243 00244 void set(string route_str, const float& v) { 00245 evQueue->call(callback(this, &SimpleMbedClientBase::internal_set_float), route_str, v); 00246 } 00247 00248 bool define_resource_internal(const char* route, string v, M2MBase::Operation opr, bool observable) { 00249 if (client) { 00250 smc_debug_msg("[SMC] [ERROR] mbed_client_define_resource, Can only define resources before mbed_client_setup is called!\r\n"); 00251 return false; 00252 } 00253 00254 vector<string> segments = parse_route(route); 00255 if (segments.size() != 3) { 00256 smc_debug_msg("[SMC] [ERROR] mbed_client_define_resource, Route needs to have three segments, split by '/' (%s)\r\n", route); 00257 return false; 00258 } 00259 00260 // segments[1] should be one digit and numeric 00261 char n = segments.at(1).c_str()[0]; 00262 if (n < '0' || n > '9') { 00263 smc_debug_msg("[SMC] [ERROR] mbed_client_define_resource, second route segment should be numeric, but was not (%s)\r\n", route); 00264 return false; 00265 } 00266 00267 int inst_id = atoi(segments.at(1).c_str()); 00268 00269 M2MObjectInstance* inst; 00270 if (objectInstances.count(segments.at(0))) { 00271 inst = objectInstances[segments.at(0)]; 00272 } 00273 else { 00274 M2MObject* obj = M2MInterfaceFactory::create_object(segments.at(0).c_str()); 00275 inst = obj->create_object_instance(inst_id); 00276 objects.insert(std::pair<string, M2MObject*>(segments.at(0), obj)); 00277 objectInstances.insert(std::pair<string, M2MObjectInstance*>(segments.at(0), inst)); 00278 } 00279 00280 // @todo check if the resource exists yet 00281 M2MResource* res = inst->create_dynamic_resource(segments.at(2).c_str(), "", 00282 M2MResourceInstance::STRING, observable); 00283 res->set_operation(opr); 00284 res->set_value((uint8_t*)v.c_str(), v.length()); 00285 00286 string route_str(route); 00287 resources.insert(pair<string, M2MResource*>(route_str, res)); 00288 00289 return true; 00290 } 00291 00292 void register_update_callback(string route, SimpleResourceBase* simpleResource) { 00293 updateValues[route] = simpleResource; 00294 } 00295 00296 M2MResource* get_resource(string route) { 00297 if (!resources.count(route)) { 00298 smc_debug_msg("[SMC] [ERROR] No such route (%s)\r\n", route.c_str()); 00299 return NULL; 00300 } 00301 00302 return resources[route]; 00303 } 00304 00305 EventQueue* eventQueue() const { 00306 return evQueue; 00307 } 00308 00309 private: 00310 vector<string> parse_route(const char* route) { 00311 const string s(route); 00312 vector<string> v; 00313 00314 split(s, '/', v); 00315 00316 return v; 00317 } 00318 00319 void split(const string& s, char delim, vector<string>& v) { 00320 size_t i = 0; 00321 size_t pos = s.find(delim); 00322 while (pos != string::npos) { 00323 v.push_back(s.substr(i, pos - i)); 00324 i = ++pos; 00325 pos = s.find(delim, pos); 00326 00327 if (pos == string::npos) { 00328 v.push_back(s.substr(i, s.length())); 00329 } 00330 } 00331 } 00332 00333 void resource_updated(string uri) { 00334 if (updateValues.count(uri) == 0) return; 00335 00336 string v = get(uri); 00337 if (v.empty()) return; 00338 00339 // Schedule this on the other thread, to avoid blocking this thread 00340 evQueue->call(callback(updateValues[uri], &SimpleResourceBase::update), v); 00341 } 00342 00343 // These operations have side effects, they should not be called immediately, 00344 // but always through the eventqueue 00345 void internal_set_str(string route_str, string v) { 00346 if (!resources.count(route_str)) { 00347 smc_debug_msg("[SMC] [ERROR] No such route (%s)\r\n", route_str.c_str()); 00348 return; 00349 } 00350 00351 if (v.length() == 0) { 00352 resources[route_str]->clear_value(); 00353 } 00354 else { 00355 resources[route_str]->set_value((uint8_t*)v.c_str(), v.length()); 00356 } 00357 00358 updateValues[route_str]->clear_pending_value(); 00359 } 00360 00361 void internal_set_int(string route, const int& v) { 00362 char str[13]; 00363 sprintf(str, "%d", v); 00364 00365 internal_set_str(route, string(str)); 00366 } 00367 00368 void internal_set_float(string route, const float& v) { 00369 int size = snprintf(NULL, 0, "%g", v); 00370 00371 char str[size]; 00372 sprintf(str, "%g", v); 00373 00374 internal_set_str(route, string(str)); 00375 } 00376 00377 void keep_alive() { 00378 client->test_update_register(); 00379 } 00380 00381 Serial output; 00382 00383 MbedClient* client; 00384 map<string, M2MObject*> objects; 00385 map<string, M2MObjectInstance*> objectInstances; 00386 map<string, M2MResource*> resources; 00387 00388 EventQueue* evQueue; 00389 Thread* evThread; 00390 00391 bool debug; 00392 00393 map<string, SimpleResourceBase*> updateValues; 00394 }; 00395 00396 class SimpleResourceString : public SimpleResourceBase { 00397 public: 00398 SimpleResourceString(SimpleMbedClientBase* aSimpleClient, string aRoute, Callback<void(string)> aOnUpdate) : 00399 simpleClient(aSimpleClient), route(aRoute), onUpdate(aOnUpdate), hasPendingValue(false) {} 00400 00401 string operator=(const string& newValue) { 00402 pendingValue = newValue; 00403 hasPendingValue = true; 00404 00405 simpleClient->set(route, newValue); 00406 return newValue; 00407 }; 00408 00409 operator string() const { 00410 if (hasPendingValue) { 00411 return pendingValue; 00412 } 00413 00414 return simpleClient->get(route); 00415 }; 00416 00417 virtual void update(string v) { 00418 if (onUpdate) onUpdate(v); 00419 } 00420 00421 M2MResource* get_resource() { 00422 return simpleClient->get_resource(route); 00423 } 00424 00425 virtual void clear_pending_value() { 00426 hasPendingValue = false; 00427 } 00428 00429 private: 00430 SimpleMbedClientBase* simpleClient; 00431 string route; 00432 Callback<void(string)> onUpdate; 00433 00434 // set() is async (because on the event queue, so store the pending value here...) 00435 bool hasPendingValue; 00436 string pendingValue; 00437 }; 00438 00439 class SimpleResourceInt : public SimpleResourceBase { 00440 public: 00441 SimpleResourceInt(SimpleMbedClientBase* aSimpleClient, string aRoute, Callback<void(int)> aOnUpdate) : 00442 simpleClient(aSimpleClient), route(aRoute), onUpdate(aOnUpdate), hasPendingValue(false), pendingValue(0) {} 00443 00444 int operator=(int newValue) { 00445 pendingValue = newValue; 00446 hasPendingValue = true; 00447 00448 simpleClient->set(route, newValue); 00449 return newValue; 00450 }; 00451 operator int() const { 00452 if (hasPendingValue) { 00453 return pendingValue; 00454 } 00455 00456 string v = simpleClient->get(route); 00457 if (v.empty()) return 0; 00458 00459 return atoi((const char*)v.c_str()); 00460 }; 00461 00462 virtual void update(string v) { 00463 if (!onUpdate) return; 00464 00465 onUpdate(atoi((const char*)v.c_str())); 00466 } 00467 00468 M2MResource* get_resource() { 00469 return simpleClient->get_resource(route); 00470 } 00471 00472 virtual void clear_pending_value() { 00473 hasPendingValue = false; 00474 } 00475 00476 private: 00477 SimpleMbedClientBase* simpleClient; 00478 string route; 00479 Callback<void(int)> onUpdate; 00480 00481 // set() is async (because on the event queue, so store the pending value here...) 00482 bool hasPendingValue; 00483 int pendingValue; 00484 }; 00485 00486 class SimpleResourceFloat : public SimpleResourceBase { 00487 public: 00488 SimpleResourceFloat(SimpleMbedClientBase* aSimpleClient, string aRoute, Callback<void(float)> aOnUpdate) : 00489 simpleClient(aSimpleClient), route(aRoute), onUpdate(aOnUpdate), hasPendingValue(false), pendingValue(0) {} 00490 00491 float operator=(float newValue) { 00492 pendingValue = newValue; 00493 hasPendingValue = true; 00494 00495 simpleClient->set(route, newValue); 00496 return newValue; 00497 }; 00498 operator float() const { 00499 if (hasPendingValue) { 00500 return pendingValue; 00501 } 00502 00503 string v = simpleClient->get(route); 00504 if (v.empty()) return 0; 00505 00506 return atof((const char*)v.c_str()); 00507 }; 00508 00509 virtual void update(string v) { 00510 if (!onUpdate) return; 00511 00512 onUpdate(atof((const char*)v.c_str())); 00513 } 00514 00515 M2MResource* get_resource() { 00516 return simpleClient->get_resource(route); 00517 } 00518 00519 virtual void clear_pending_value() { 00520 hasPendingValue = false; 00521 } 00522 00523 private: 00524 SimpleMbedClientBase* simpleClient; 00525 string route; 00526 Callback<void(float)> onUpdate; 00527 00528 // set() is async (because on the event queue, so store the pending value here...) 00529 bool hasPendingValue; 00530 float pendingValue; 00531 }; 00532 00533 class SimpleMbedClient : public SimpleMbedClientBase { 00534 public: 00535 00536 SimpleMbedClient(bool aDebug = true) 00537 : SimpleMbedClientBase(aDebug) 00538 { 00539 00540 } 00541 00542 SimpleMbedClient(EventQueue* aQueue, bool aDebug = true) 00543 : SimpleMbedClientBase(aQueue, aDebug) 00544 { 00545 00546 } 00547 00548 // @todo: macro this up 00549 00550 // String 00551 SimpleResourceString define_resource( 00552 const char* route, 00553 string v, 00554 M2MBase::Operation opr = M2MBase::GET_PUT_ALLOWED, 00555 bool observable = true, 00556 Callback<void(string)> onUpdate = NULL) 00557 { 00558 SimpleResourceString* simpleResource = new SimpleResourceString(this, route, onUpdate); 00559 bool res = define_resource_internal(route, v, opr, observable); 00560 if (!res) { 00561 printf("Error while creating %s\n", route); 00562 } 00563 else { 00564 register_update_callback(route, simpleResource); 00565 } 00566 return *simpleResource; 00567 } 00568 00569 SimpleResourceString define_resource( 00570 const char* route, 00571 string v, 00572 M2MBase::Operation opr, 00573 bool observable, 00574 void(*onUpdate)(string)) 00575 { 00576 Callback<void(string)> fp; 00577 fp.attach(onUpdate); 00578 return define_resource(route, v, opr, observable, fp); 00579 } 00580 00581 SimpleResourceString define_resource( 00582 const char* route, 00583 string v, 00584 Callback<void(string)> onUpdate) 00585 { 00586 return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, onUpdate); 00587 } 00588 00589 SimpleResourceString define_resource( 00590 const char* route, 00591 string v, 00592 void(*onUpdate)(string)) 00593 { 00594 Callback<void(string)> fp; 00595 fp.attach(onUpdate); 00596 return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, fp); 00597 } 00598 00599 // Int 00600 SimpleResourceInt define_resource( 00601 const char* route, 00602 int v, 00603 M2MBase::Operation opr = M2MBase::GET_PUT_ALLOWED, 00604 bool observable = true, 00605 Callback<void(int)> onUpdate = NULL) 00606 { 00607 SimpleResourceInt* simpleResource = new SimpleResourceInt(this, route, onUpdate); 00608 00609 char str[13]; 00610 sprintf(str, "%d", v); 00611 00612 bool res = define_resource_internal(route, string(str), opr, observable); 00613 if (!res) { 00614 printf("Error while creating %s\n", route); 00615 } 00616 else { 00617 register_update_callback(route, simpleResource); 00618 } 00619 return *simpleResource; 00620 } 00621 00622 SimpleResourceInt define_resource( 00623 const char* route, 00624 int v, 00625 M2MBase::Operation opr, 00626 bool observable, 00627 void(*onUpdate)(int)) 00628 { 00629 Callback<void(int)> fp; 00630 fp.attach(onUpdate); 00631 return define_resource(route, v, opr, observable, fp); 00632 } 00633 00634 SimpleResourceInt define_resource( 00635 const char* route, 00636 int v, 00637 Callback<void(int)> onUpdate) 00638 { 00639 return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, onUpdate); 00640 } 00641 00642 SimpleResourceInt define_resource( 00643 const char* route, 00644 int v, 00645 void(*onUpdate)(int)) 00646 { 00647 Callback<void(int)> fp; 00648 fp.attach(onUpdate); 00649 return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, fp); 00650 } 00651 00652 // Float 00653 SimpleResourceFloat define_resource( 00654 const char* route, 00655 float v, 00656 M2MBase::Operation opr = M2MBase::GET_PUT_ALLOWED, 00657 bool observable = true, 00658 Callback<void(float)> onUpdate = NULL) 00659 { 00660 SimpleResourceFloat* simpleResource = new SimpleResourceFloat(this, route, onUpdate); 00661 00662 int size = snprintf(NULL, 0, "%g", v); 00663 00664 // malloc() would probably be better here 00665 char str[size]; 00666 sprintf(str, "%g", v); 00667 00668 bool res = define_resource_internal(route, string(str), opr, observable); 00669 if (!res) { 00670 printf("Error while creating %s\n", route); 00671 } 00672 else { 00673 register_update_callback(route, simpleResource); 00674 } 00675 return *simpleResource; 00676 } 00677 00678 SimpleResourceFloat define_resource( 00679 const char* route, 00680 float v, 00681 M2MBase::Operation opr, 00682 bool observable, 00683 void(*onUpdate)(float)) 00684 { 00685 Callback<void(float)> fp; 00686 fp.attach(onUpdate); 00687 return define_resource(route, v, opr, observable, fp); 00688 } 00689 00690 SimpleResourceFloat define_resource( 00691 const char* route, 00692 float v, 00693 Callback<void(float)> onUpdate) 00694 { 00695 return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, onUpdate); 00696 } 00697 00698 SimpleResourceFloat define_resource( 00699 const char* route, 00700 float v, 00701 void(*onUpdate)(float)) 00702 { 00703 Callback<void(float)> fp; 00704 fp.attach(onUpdate); 00705 return define_resource(route, v, M2MBase::GET_PUT_ALLOWED, true, fp); 00706 } 00707 }; 00708 00709 #endif // __SIMPLE_MBED_CLIENT_H__
Generated on Wed Jul 13 2022 20:25:03 by 1.7.2