ThinCloud Embedded C SDK
thincloud.h
Go to the documentation of this file.
1 /*
2  * Copyright 2019 Yonomi, Inc. or its affiliates. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef THINCLOUD_EMBEDDED_C_SDK_
18 #define THINCLOUD_EMBEDDED_C_SDK_
19 
20 /* 1.1.0 */
21 #define TC_EMBEDDED_C_SDK_VERSION_MAJOR 1
22 #define TC_EMBEDDED_C_SDK_VERSION_MINOR 1
23 #define TC_EMBEDDED_C_SDK_VERSION_PATCH 0
24 
25 /*
26  * Thincloud C Embedded SDK
27  *
28  * Contains utilities to construct ThinCloud standard topics
29  * and marshal and unmarshal request and responses.
30  */
31 
32 #include <stdbool.h>
33 #include <stdint.h>
34 #include <stdio.h>
35 #include <string.h>
36 
37 #include <json-c/json.h>
38 
39 #include "aws_iot_log.h"
40 #include "aws_iot_error.h"
41 #include "aws_iot_mqtt_client_interface.h"
42 
46 #define TC_ID_LENGTH 37
47 
51 #define MAX_TOPIC_LENGTH 257
52 
53 #define REQUEST_METHOD_GET "GET"
54 #define REQUEST_METHOD_PUT "PUT"
55 #define REQUEST_METHOD_POST "POST"
56 #define REQUEST_METHOD_DELETE "DELETE"
57 
61 
74 IoT_Error_t commission_request_topic(char *buffer, const char *deviceType, const char *physicalId)
75 {
76  if (buffer == NULL)
77  {
78  return 0;
79  }
80 
81  if (deviceType == NULL || physicalId == NULL)
82  {
83  FUNC_EXIT_RC(NULL_VALUE_ERROR);
84  }
85 
86  sprintf(buffer, "thincloud/registration/%s_%s/requests", deviceType, physicalId);
87 
88  FUNC_EXIT_RC(SUCCESS);
89 }
90 
104 IoT_Error_t commission_response_topic(char *buffer, const char *deviceType, const char *physicalId, const char *requestId)
105 {
106  if (buffer == NULL)
107  {
108  return 0;
109  }
110 
111  if (deviceType == NULL || physicalId == NULL || requestId == NULL)
112  {
113  FUNC_EXIT_RC(NULL_VALUE_ERROR);
114  }
115 
116  sprintf(buffer, "thincloud/registration/%s_%s/requests/%s/response", deviceType, physicalId, requestId);
117 
118  FUNC_EXIT_RC(SUCCESS);
119 }
120 
132 IoT_Error_t command_request_topic(char *buffer, const char *deviceId)
133 {
134  if (buffer == NULL)
135  {
136  return 0;
137  }
138 
139  if (deviceId == NULL)
140  {
141  FUNC_EXIT_RC(NULL_VALUE_ERROR);
142  }
143 
144  sprintf(buffer, "thincloud/devices/%s/command", deviceId);
145 
146  FUNC_EXIT_RC(SUCCESS);
147 }
148 
161 IoT_Error_t command_response_topic(char *buffer, const char *deviceId, const char *commandId)
162 {
163  if (buffer == NULL)
164  {
165  return 0;
166  }
167 
168  if (deviceId == NULL || commandId == NULL)
169  {
170  FUNC_EXIT_RC(NULL_VALUE_ERROR);
171  }
172 
173  sprintf(buffer, "thincloud/devices/%s/command/%s/response", deviceId, commandId);
174 
175  FUNC_EXIT_RC(SUCCESS);
176 }
177 
189 IoT_Error_t service_request_topic(char *buffer, const char *deviceId)
190 {
191  if (buffer == NULL)
192  {
193  return 0;
194  }
195 
196  if (deviceId == NULL)
197  {
198  FUNC_EXIT_RC(NULL_VALUE_ERROR);
199  }
200 
201  sprintf(buffer, "thincloud/devices/%s/requests", deviceId);
202 
203  FUNC_EXIT_RC(SUCCESS);
204 }
205 
218 IoT_Error_t service_response_topic(char *buffer, const char *deviceId, const char *requestId)
219 {
220  if (buffer == NULL)
221  {
222  return 0;
223  }
224 
225  if (deviceId == NULL || requestId == NULL)
226  {
227  FUNC_EXIT_RC(NULL_VALUE_ERROR);
228  }
229 
230  sprintf(buffer, "thincloud/devices/%s/requests/%s/response", deviceId, requestId);
231 
232  FUNC_EXIT_RC(SUCCESS);
233 }
234 
249 IoT_Error_t commissioning_request(char *buffer, const char *requestId, const char *deviceType, const char *physicalId, char **relatedDeviceIds, uint32_t idsSize)
250 {
251  if (deviceType == NULL || physicalId == NULL)
252  {
253  FUNC_EXIT_RC(NULL_VALUE_ERROR);
254  }
255 
256  json_object *obj = json_object_new_object();
257  if (requestId != NULL)
258  {
259  json_object *value = json_object_new_string(requestId);
260  json_object_object_add(obj, "id", value);
261  }
262 
263  json_object *method = json_object_new_string("commission");
264  json_object_object_add(obj, "method", method);
265 
266  json_object *params = json_object_new_array();
267  json_object *data = json_object_new_object();
268  json_object *dataObj = json_object_new_object();
269 
270  json_object *deviceTypeValue = json_object_new_string(deviceType);
271  json_object *physicalIdValue = json_object_new_string(physicalId);
272  json_object_object_add(data, "deviceType", deviceTypeValue);
273  json_object_object_add(data, "physicalId", physicalIdValue);
274 
275  if (relatedDeviceIds != NULL && idsSize > 0)
276  {
277  json_object *relatedDevices = json_object_new_array();
278  for (uint32_t i = 0; i < idsSize; i++)
279  {
280  const char *id = relatedDeviceIds[i];
281  if (id == NULL || strlen(id) <= 0)
282  {
283  continue;
284  }
285 
286  json_object *deviceId = json_object_new_string(id);
287  json_object *deviceIdObj = json_object_new_object();
288  json_object_object_add(deviceIdObj, "deviceId", deviceId);
289  json_object_array_add(relatedDevices, deviceIdObj);
290  }
291 
292  json_object_object_add(data, "relatedDevices", relatedDevices);
293  }
294 
295  json_object_object_add(dataObj, "data", data);
296  json_object_array_add(params, dataObj);
297 
298  json_object_object_add(obj, "params", params);
299 
300  const char *str = json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PLAIN);
301 
302  strcpy(buffer, str);
303 
304  json_object_put(obj);
305 
306  FUNC_EXIT_RC(SUCCESS);
307 }
308 
322 IoT_Error_t commissioning_response(char *deviceId, uint16_t *statusCode, char *requestId, char *payload, uint16_t payloadLen)
323 {
324  if (payload == NULL || payloadLen == 0)
325  {
326  FUNC_EXIT_RC(SUCCESS);
327  }
328 
329  json_tokener *tok = json_tokener_new();
330 
331  json_object *obj = json_tokener_parse_ex(tok, payload, payloadLen);
332  if (obj == NULL)
333  {
334  FUNC_EXIT_RC(JSON_PARSE_ERROR);
335  }
336 
337  if (requestId != NULL)
338  {
339  const char *requestIdValue = json_object_get_string(json_object_object_get(obj, "id"));
340  if (requestIdValue != NULL)
341  {
342  strcpy(requestId, requestIdValue);
343  }
344  }
345 
346  json_object *result = json_object_object_get(obj, "result");
347 
348  if (result != NULL)
349  {
350  if (statusCode != NULL)
351  {
352  *statusCode = json_object_get_int(json_object_object_get(result, "statusCode"));
353  }
354 
355  if (deviceId != NULL)
356  {
357  const char *deviceIdValue = json_object_get_string(json_object_object_get(result, "deviceId"));
358  if (deviceIdValue != NULL)
359  {
360  strcpy(deviceId, deviceIdValue);
361  }
362  }
363  }
364 
365  json_tokener_free(tok);
366  json_object_put(obj);
367 
368  FUNC_EXIT_RC(SUCCESS);
369 }
370 
385 IoT_Error_t command_response(char *buffer, const char *requestId, uint16_t statusCode, bool isErrorResponse, char *errorMessage, json_object *body)
386 {
387  json_object *obj = json_object_new_object();
388  if (requestId != NULL)
389  {
390  json_object *value = json_object_new_string(requestId);
391  json_object_object_add(obj, "id", value);
392  }
393 
394  json_object *statusCodeValue = json_object_new_int(statusCode);
395 
396  if (isErrorResponse)
397  {
398  json_object *error = json_object_new_object();
399  json_object_object_add(error, "statusCode", statusCodeValue);
400  if (errorMessage != NULL)
401  {
402  json_object *errorMessageValue = json_object_new_string(errorMessage);
403  json_object_object_add(error, "message", errorMessageValue);
404  }
405 
406  json_object_object_add(obj, "error", error);
407  }
408  else
409  {
410  json_object *result = json_object_new_object();
411  json_object_object_add(result, "statusCode", statusCodeValue);
412 
413  if (body != NULL)
414  {
415  json_object_object_add(result, "body", body);
416  }
417 
418  json_object_object_add(obj, "result", result);
419  }
420 
421  const char *str = json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PLAIN);
422 
423  strcpy(buffer, str);
424 
425  json_object_put(obj);
426 
427  FUNC_EXIT_RC(SUCCESS);
428 }
429 
443 IoT_Error_t command_request(char *requestId, char *method, json_object **params, const char *payload, const unsigned int payloadLen)
444 {
445  if (payload == NULL || payloadLen == 0)
446  {
447  FUNC_EXIT_RC(SUCCESS);
448  }
449 
450  json_tokener *tok = json_tokener_new();
451 
452  json_object *obj = json_tokener_parse_ex(tok, payload, payloadLen);
453  if (obj == NULL)
454  {
455  FUNC_EXIT_RC(JSON_PARSE_ERROR);
456  }
457 
458  if (requestId != NULL)
459  {
460  const char *requestIdValue = json_object_get_string(json_object_object_get(obj, "id"));
461  if (requestIdValue != NULL)
462  {
463  strcpy(requestId, requestIdValue);
464  }
465  }
466 
467  if (method != NULL)
468  {
469  const char *methodValue = json_object_get_string(json_object_object_get(obj, "method"));
470  if (methodValue != NULL)
471  {
472  strcpy(method, methodValue);
473  }
474  }
475 
476  json_object *pParams = json_object_object_get(obj, "params");
477 
478  if (pParams != NULL && params != NULL)
479  {
480  const int rc = json_object_deep_copy(pParams, params, NULL);
481  if (rc < 0)
482  {
483  FUNC_EXIT_RC(JSON_PARSE_ERROR);
484  }
485  }
486 
487  json_tokener_free(tok);
488  json_object_put(obj);
489 
490  FUNC_EXIT_RC(SUCCESS);
491 }
492 
505 IoT_Error_t service_request(char *buffer, const char *requestId, const char *method, json_object *params)
506 {
507  json_object *obj = json_object_new_object();
508  if (requestId != NULL)
509  {
510  json_object *value = json_object_new_string(requestId);
511  json_object_object_add(obj, "id", value);
512  }
513 
514  json_object *methodValue = json_object_new_string(method);
515  json_object_object_add(obj, "method", methodValue);
516 
517  if (params != NULL)
518  {
519  json_object_object_add(obj, "params", params);
520  }
521 
522  const char *str = json_object_to_json_string_ext(obj, JSON_C_TO_STRING_PLAIN);
523 
524  strcpy(buffer, str);
525 
526  json_object_put(obj);
527 
528  FUNC_EXIT_RC(SUCCESS);
529 }
530 
544 IoT_Error_t service_response(char *requestId, uint16_t *statusCode, json_object **data, const char *payload, const unsigned int payloadLen)
545 {
546  if (payload == NULL || payloadLen == 0)
547  {
548  FUNC_EXIT_RC(SUCCESS);
549  }
550 
551  json_tokener *tok = json_tokener_new();
552 
553  json_object *obj = json_tokener_parse_ex(tok, payload, payloadLen);
554  if (obj == NULL)
555  {
556  FUNC_EXIT_RC(JSON_PARSE_ERROR);
557  }
558 
559  if (requestId != NULL)
560  {
561  const char *requestIdValue = json_object_get_string(json_object_object_get(obj, "id"));
562  if (requestIdValue != NULL)
563  {
564 
565  strcpy(requestId, requestIdValue);
566  }
567  }
568 
569  json_object *result = json_object_object_get(obj, "result");
570 
571  if (result != NULL)
572  {
573  if (statusCode != NULL)
574  {
575  *statusCode = json_object_get_int(json_object_object_get(result, "statusCode"));
576  }
577 
578  json_object *body = json_object_object_get(result, "body");
579  if (body != NULL && data != NULL)
580  {
581  const int rc = json_object_deep_copy(body, data, NULL);
582  if (rc < 0)
583  {
584  FUNC_EXIT_RC(JSON_PARSE_ERROR);
585  }
586  }
587  }
588 
589  json_tokener_free(tok);
590  json_object_put(obj);
591 
592  FUNC_EXIT_RC(SUCCESS);
593 }
594 
610 IoT_Error_t send_command_response(AWS_IoT_Client *client, const char *deviceId, const char *commandId, uint16_t statusCode, bool isErrorResponse, char *errorMessage, json_object *body)
611 {
612  char topic[MAX_TOPIC_LENGTH];
613 
614  IoT_Error_t rc = command_response_topic(topic, deviceId, commandId);
615  if (rc != SUCCESS)
616  {
617  FUNC_EXIT_RC(rc);
618  }
619 
620  char payload[MAX_JSON_TOKEN_EXPECTED];
621 
622  rc = command_response(payload, commandId, statusCode, isErrorResponse, errorMessage, body);
623 
624  if (rc != SUCCESS)
625  {
626  FUNC_EXIT_RC(rc);
627  }
628 
629  IoT_Publish_Message_Params params;
630  params.qos = QOS0;
631  params.isRetained = false;
632  params.payload = (void *)payload;
633  params.payloadLen = strlen(payload);
634 
635  return aws_iot_mqtt_publish(client, topic, strlen(topic), &params);
636 }
637 
652 IoT_Error_t send_commissioning_request(AWS_IoT_Client *client, const char *requestId, const char *deviceType, const char *physicalId, char **relatedDeviceIds, uint32_t idsSize)
653 {
654  char topic[MAX_TOPIC_LENGTH];
655 
656  IoT_Error_t rc = commission_request_topic(topic, deviceType, physicalId);
657  if (rc != SUCCESS)
658  {
659  FUNC_EXIT_RC(rc);
660  }
661 
662  char payload[MAX_JSON_TOKEN_EXPECTED];
663 
664  rc = commissioning_request(payload, requestId, deviceType, physicalId, relatedDeviceIds, idsSize);
665 
666  if (rc != SUCCESS)
667  {
668  FUNC_EXIT_RC(rc);
669  }
670 
671  IoT_Publish_Message_Params params;
672  params.qos = QOS0;
673  params.isRetained = false;
674  params.payload = (void *)payload;
675  params.payloadLen = strlen(payload);
676 
677  return aws_iot_mqtt_publish(client, topic, strlen(topic), &params);
678 }
679 
693 IoT_Error_t send_service_request(AWS_IoT_Client *client, const char *requestId, const char *deviceId, const char *method, json_object *reqParams)
694 {
695  char topic[MAX_TOPIC_LENGTH];
696 
697  IoT_Error_t rc = service_request_topic(topic, deviceId);
698 
699  if (rc != SUCCESS)
700  {
701  FUNC_EXIT_RC(rc);
702  }
703 
704  char payload[MAX_JSON_TOKEN_EXPECTED];
705 
706  rc = service_request(payload, requestId, method, reqParams);
707 
708  if (rc != SUCCESS)
709  {
710  FUNC_EXIT_RC(rc);
711  }
712 
713  IoT_Publish_Message_Params params;
714  params.qos = QOS0;
715  params.isRetained = false;
716  params.payload = (void *)payload;
717  params.payloadLen = strlen(payload);
718 
719  return aws_iot_mqtt_publish(client, topic, strlen(topic), &params);
720 }
721 
734 IoT_Error_t subscribe_to_commissioning_response(AWS_IoT_Client *client, const char *requestId, const char *deviceType, const char *physicalId, pApplicationHandler_t handler, void *subscribeData)
735 {
736  IoT_Error_t rc = commission_response_topic(COMMISSIONING_RESPONSE_TOPIC_BUFFER, deviceType, physicalId, requestId);
737 
738  if (rc != SUCCESS)
739  {
740  FUNC_EXIT_RC(rc);
741  }
742 
743  return aws_iot_mqtt_subscribe(client, COMMISSIONING_RESPONSE_TOPIC_BUFFER, strlen(COMMISSIONING_RESPONSE_TOPIC_BUFFER), QOS0, handler, subscribeData);
744 }
745 
756 IoT_Error_t subscribe_to_command_request(AWS_IoT_Client *client, const char *deviceId, pApplicationHandler_t handler, void *subscribeData)
757 {
758  IoT_Error_t rc = command_request_topic(COMMAND_TOPIC_BUFFER, deviceId);
759 
760  if (rc != SUCCESS)
761  {
762  FUNC_EXIT_RC(rc);
763  }
764 
765  return aws_iot_mqtt_subscribe(client, COMMAND_TOPIC_BUFFER, strlen(COMMAND_TOPIC_BUFFER), QOS0, handler, subscribeData);
766 }
767 
779 IoT_Error_t subscribe_to_service_response(AWS_IoT_Client *client, const char *deviceId, const char *requestId, pApplicationHandler_t handler, void *subscribeData)
780 {
781  IoT_Error_t rc = service_response_topic(SERVICE_RESPONSE_TOPIC_BUFFER, deviceId, requestId);
782 
783  if (rc != SUCCESS)
784  {
785  FUNC_EXIT_RC(rc);
786  }
787 
788  return aws_iot_mqtt_subscribe(client, SERVICE_RESPONSE_TOPIC_BUFFER, strlen(SERVICE_RESPONSE_TOPIC_BUFFER), QOS0, handler, subscribeData);
789 }
790 
806 IoT_Error_t tc_init(AWS_IoT_Client *client, char *hostAddr, char *rootCAPath, char *clientCRTPath, char *clientKeyPath, iot_disconnect_handler handler, void *disconnectData)
807 {
808  IoT_Client_Init_Params params = iotClientInitParamsDefault;
809 
810  params.enableAutoReconnect = false; // We enable this on connect
811  params.pHostURL = hostAddr;
812  params.port = 443; // Default MQTT port
813  params.pRootCALocation = rootCAPath;
814  params.pDeviceCertLocation = clientCRTPath;
815  params.pDevicePrivateKeyLocation = clientKeyPath;
816  params.mqttCommandTimeout_ms = 20000;
817  params.tlsHandshakeTimeout_ms = 5000;
818  params.isSSLHostnameVerify = true;
819  params.disconnectHandler = handler;
820  params.disconnectHandlerData = disconnectData;
821 
822  return aws_iot_mqtt_init(client, &params);
823 }
824 
837 IoT_Error_t tc_connect(AWS_IoT_Client *client, char *clientId, bool autoReconnect)
838 {
839  IoT_Client_Connect_Params params = iotClientConnectParamsDefault;
840 
841  params.keepAliveIntervalInSec = 600;
842  params.isCleanSession = true;
843  params.MQTTVersion = MQTT_3_1_1;
844  params.pClientID = clientId;
845  params.clientIDLen = (uint16_t)strlen(clientId);
846  params.isWillMsgPresent = false;
847 
848  IoT_Error_t rc = aws_iot_mqtt_connect(client, &params);
849  if (rc != SUCCESS)
850  {
851  FUNC_EXIT_RC(rc);
852  }
853 
854  if (autoReconnect)
855  {
856  rc = aws_iot_mqtt_autoreconnect_set_status(client, true);
857  if (rc != SUCCESS)
858  {
859  FUNC_EXIT_RC(rc);
860  }
861  }
862 
863  FUNC_EXIT_RC(SUCCESS);
864 }
865 
866 #endif /* THINCLOUD_EMBEDDED_C_SDK_ */
IoT_Error_t tc_connect(AWS_IoT_Client *client, char *clientId, bool autoReconnect)
Start MQTT connection to ThinCloud host.
Definition: thincloud.h:837
IoT_Error_t tc_init(AWS_IoT_Client *client, char *hostAddr, char *rootCAPath, char *clientCRTPath, char *clientKeyPath, iot_disconnect_handler handler, void *disconnectData)
Initialize an AWS IoT Client.
Definition: thincloud.h:806
IoT_Error_t command_request_topic(char *buffer, const char *deviceId)
Build a command request topic.
Definition: thincloud.h:132
IoT_Error_t service_response(char *requestId, uint16_t *statusCode, json_object **data, const char *payload, const unsigned int payloadLen)
Unmarshall a service response payload.
Definition: thincloud.h:544
IoT_Error_t command_request(char *requestId, char *method, json_object **params, const char *payload, const unsigned int payloadLen)
Unmarshall a command request payload.
Definition: thincloud.h:443
IoT_Error_t commission_response_topic(char *buffer, const char *deviceType, const char *physicalId, const char *requestId)
Build a commission response topic.
Definition: thincloud.h:104
IoT_Error_t commissioning_request(char *buffer, const char *requestId, const char *deviceType, const char *physicalId, char **relatedDeviceIds, uint32_t idsSize)
Build a commissioning request.
Definition: thincloud.h:249
#define MAX_TOPIC_LENGTH
Definition: thincloud.h:51
IoT_Error_t command_response_topic(char *buffer, const char *deviceId, const char *commandId)
Build a command response topic.
Definition: thincloud.h:161
IoT_Error_t service_request(char *buffer, const char *requestId, const char *method, json_object *params)
Marshal a service request.
Definition: thincloud.h:505
IoT_Error_t subscribe_to_commissioning_response(AWS_IoT_Client *client, const char *requestId, const char *deviceType, const char *physicalId, pApplicationHandler_t handler, void *subscribeData)
Subscribe to commissioning respones.
Definition: thincloud.h:734
char SERVICE_RESPONSE_TOPIC_BUFFER[MAX_TOPIC_LENGTH]
Definition: thincloud.h:60
char COMMISSIONING_RESPONSE_TOPIC_BUFFER[MAX_TOPIC_LENGTH]
Definition: thincloud.h:58
IoT_Error_t send_commissioning_request(AWS_IoT_Client *client, const char *requestId, const char *deviceType, const char *physicalId, char **relatedDeviceIds, uint32_t idsSize)
Send commissioning request.
Definition: thincloud.h:652
IoT_Error_t command_response(char *buffer, const char *requestId, uint16_t statusCode, bool isErrorResponse, char *errorMessage, json_object *body)
Marshal a command response.
Definition: thincloud.h:385
char COMMAND_TOPIC_BUFFER[MAX_TOPIC_LENGTH]
Definition: thincloud.h:59
IoT_Error_t send_service_request(AWS_IoT_Client *client, const char *requestId, const char *deviceId, const char *method, json_object *reqParams)
Send service request.
Definition: thincloud.h:693
IoT_Error_t commission_request_topic(char *buffer, const char *deviceType, const char *physicalId)
Build a commission request topic.
Definition: thincloud.h:74
IoT_Error_t service_response_topic(char *buffer, const char *deviceId, const char *requestId)
Build a service response topic.
Definition: thincloud.h:218
IoT_Error_t commissioning_response(char *deviceId, uint16_t *statusCode, char *requestId, char *payload, uint16_t payloadLen)
Unmarshall a commissioning response.
Definition: thincloud.h:322
IoT_Error_t subscribe_to_command_request(AWS_IoT_Client *client, const char *deviceId, pApplicationHandler_t handler, void *subscribeData)
Subscribe to command requests.
Definition: thincloud.h:756
IoT_Error_t send_command_response(AWS_IoT_Client *client, const char *deviceId, const char *commandId, uint16_t statusCode, bool isErrorResponse, char *errorMessage, json_object *body)
Send a command response.
Definition: thincloud.h:610
IoT_Error_t subscribe_to_service_response(AWS_IoT_Client *client, const char *deviceId, const char *requestId, pApplicationHandler_t handler, void *subscribeData)
Subscribe to service responses.
Definition: thincloud.h:779
IoT_Error_t service_request_topic(char *buffer, const char *deviceId)
Build a service request topic.
Definition: thincloud.h:189