tio
|
With tio
and Kii Interaction Framework, you can quickly implement IoT devices which supports following functionalities.
tio
consists of following modules.
Responsible for watching remote control command and propagate the command to IoT device application.
Once the module has been started, The loop inside the module keeps watching command arrival from the cloud.
Responsible for upload data read from sensors equipped to IoT devices.
Once the module has been started, The loop inside the module keeps asking for update by sensors periodically with the specified interval.
You can choose to run both tio_handler_t
and tio_updater_t
or either one.
tio_handler_t
Application developer needs to prepare following callback functions.
tio_handler_t
needs executing task asynchronously since it keeps waiting for commands sent to MQTT topic.
tio
needs abstraction with callbacks since the way of dispatch async tasks varies in different environments.
Task create callback function signature is following.
Typical implementation with pthread
:
Task continue callback is used to determine whether to continue or exit task from the loop.
Task continue callback signature:
Implementation in example app:
term_flag
is a variable determines whether to continue. Example app set it true when received SIGINT
generated by Ctrl-C.
Note that we use atomic_bool
type introduced in C11 since term_flag
is referenced from signal handler, main thread and handler task thread (_handler_continue()
is executed in handler task thread.)
Task exit callback is called when task continue callback returns KII_FALSE
or unrecoverable error occurs.
In this callback, you may need to free memories used for the handler task and other necessary steps when exiting task.
In the example app, we don't give allocated memory to tio_handler
and pthread
doesn't requires special treatment when exit loop, just set flag that indicate task has been exited safely.
handler_terminated
is a variable indicates that resources allocated by handler is now all freed.
Details of memory management when exiting handler task, please refer to API reference of tio_handler_set_cb_task_exit()
.
We use atomic_bool
here since the flag is referenced in main thread as well before exiting the process. (_handler_exit()
is executed in handler task thread.)
In some situation program needs to wait for specified period of time to avoid the loop runs too fast or making too many requests to servers, etc.
tio
needs abstraction with callbacks since the way varies in different environments.
Delay callback function signature is following.
Typical implementation with usleep
provided by libc
:
tio
provides abstraction of socket related functions.
Socket callback functions signature:
You can see implementation with OpenSSL at linux-sample
sock_ctx
argument is arbitrary data context which application can use. Application should pass the pointer of the data when calling tio_handler_set_cb_sock_connect_http()
, tio_handler_set_cb_sock_send_http()
, tio_handler_set_cb_sock_recv_http()
, tio_handler_set_cb_sock_close_http()
, tio_handler_set_cb_sock_connect_mqtt()
, tio_handler_set_cb_sock_send_mqtt()
, tio_handler_set_cb_sock_recv_mqtt()
, tio_handler_set_cb_sock_close_mqtt()
methods.
Note that, tio_handler
uses MQTT(s) to receive remote control commands and uses HTTP(s) to send the result of command execution.
Application can choose to pass different implementations of socket functions or same one. If you choose to pass same pointer of the function, Please also check Asynchronous task management section as well.
For both MQTT and HTTP, using them over secure connection is highly recommended. Our cloud supports non-secure connection for now. However, we may terminate supports of insecure connections in the future.
Underlying socket implementation used for MQTT must be blocking mode and its recv/ send timeout must be shared with tio_handler_t
.
We use socket timeout to estimate elapsed time since we sent last MQTT messages to send MQTT PINGREQ
message periodically.
MQTT keep alive interval must be larger than recv/ send timeout. Twice as large as recv/ send timeout works fine but recommend few minutes to avoid congestion.
Action callback is called when the IoT device receives remote control command. You can implement IoT device specific control in the callback such as turn on/ off devices or changes level of actuators, etc.
Action callback signature:
tio_action_t.alias
is a string represents the alias of the trait which represents set of capabilities equipped.eg.) "AirConditioner", "LevelMonitor", etc.
tio_action_t.action_name
is a string represents the name of action.eg.) "setPresetTemperature", "executeMonitering", etc.
tio_action_t.action_value
is a subject of the action.eg.) If the action_name
is "setPresetTemperature", action_value.type
might be a long_value
and it's value is 26, 18, etc.
When the execution is failed, you can set the message in err
argument and returns false. The result of execution and error message is stored in the cloud.
user_data
argument is a context object pointer given to tio_handler_start()
method.
Alias, action name, action value and its type is defined by the feature called Trait
.
For more details about Trait, please refer to the document.
tio_handler_t
instanceHere's the set-up code extracted from example app. The full code can be checked handler_init() in example.c
Here's anatomy of set-up calls.
tio_handler_init
callThis function must be called prior to any other functions of tio_handler_t
.
tio_handler_set_app
callSet the Kii application information.
In the example, KII_APP_ID
and KII_APP_HOST
is defined as Macro. Those are used to identify work space in the cloud and the value is determined when you Kii Cloud App has been created. To create your Kii Cloud App, Sign-up to Developer console.
Set callback function pointers.
Set callback function pointers and context data pointers. If you application allocates memory/ resources for context data, application is responsible to free those memory/ resources.
Different context objects named http_ssl_ctx
and mqtt_ssl_ctx
is used since the connection and it's life-cycle is different between them.
tio_handler
needs memory buffer to store HTTP/ MQTT payloads.
In this example, assigned 4KB for HTTP payloads and 2KB for MQTT payloads. This size may covers most use-cases. However, If you're remote command definition is more and larger, you may need to allocate larger size.
Note that buffer for HTTP and MQTT must be isolated. Passing overlapping memory causes undefined behavior.
tio_handler_t
needs socket timeout in seconds to send MQTT PINGREQ
periodically.
Note that application must implement socket timeout in the socket callback implementation. You can see example here
In the example, HANDLER_KEEP_ALIVE_SEC
is defined as Macro and value is 300 (in seconds).
MQTT have mechanism called Keep Alive
detecting stale connection between the MQTT broker.
tio_handler_t
acts as MQTT clients and send PINGREQ
to MQTT broker periodically with the specified interval. If PINGRESP
from MQTT broker is not present, tio_handler_t
would close the current connection and make fresh connection again.
If interval is set to 0, Keep Alive
is turned off and no PINGREQ
message is send to MQTT broker.
We highly recommend setting Keep Alive interval greater than 0 to detect disconnection. Recommended interval is few minutes since too small interval may cause network congestion and increases cloud cost.
MQTT keep alive interval must be larger than recv/ send timeout.
tio_handler_t
uses jkii
json parser library. jkii
uses array of tokens to parse json string.
In this example, allocates 256 tokens statically.
Number of tokens to be used to parse json varies depending on how complex the target json string is. If you defined complex(i.e, a lot of fields or long arrays in the commands) control command, you would need to give larger number. Alternatively, you can use dynamic allocation for tokens by using following API:
cb_alloc
is called when the token is required and it's number is exactly same as numbers need to parse json string.
cb_free
is called when the parse has been done.
For the first time, Step called onboarding
is required. In this step, Identifier of IoT device (thing ID) is generated and stored in cloud.
After the step, thing ID and access token is returned to IoT device. By using thing ID and access token, cloud can identify the device when the remote command is sent to the device with the thing ID or sensor data sent from the device with the token.
This step can be skipped once thing ID and access token is stored by the IoT devices.
Alternatively, you can execute this step outside of the IoT Device and pass thing ID and access token to IoT devices. Typical example is using Mobile apps to execute the onboarding step and passing thing ID and access token to the devices via BLE, etc.
Now, it's ready to start tio_handler_t
module.
author
consists of thing ID and access token obtained in the step of Onboarding.tio_action_handler
is the callback function pointer explained in Action callbackThis call results to execute asynchronous tasks created by Task callbacks. The name of tasks initiated by this call is exported as macro KII_TASK_NAME_MQTT
and KII_TASK_NAME_PING_REQ
defined in kii.h
.
tio_updater_t
Similar to tio_handler_t
.
tio_updater_t
only uses HTTP(s) and does not use MQTT(s).
tio_updater_t
uploads the IoT device state such as sensor readings periodically with the specified interval.
When the specified interval elapsed, tio_updater_t
execute callback function to read the latest state of the IoT device.
Application can implement the process reading values from sensors, etc.
Size callback is called to ask the size of new IoT device state. This callback is called before the read callback. If the returned size is 0, tio_updater_t
skips updating device state.
Signature of size callback:
userdata
is context object pointer given to tio_updater_start()
argument.
Read callback is used to read actual IoT device state. Callback is repeatedly called until it returns 0.
buffer
: Callback implementation writes the state data to this buffer.size
: Requested size to be written to the buffer. If the returned value does not muches the requested size, tio_updater
aborts the update process this time but the loop continues to run.userdata
: Context object pointer passed to tio_updater_start()
function.Expected format of the IoT device state is defined by Trait
.
eg.)
AirConditionerAlias
is the name of the Alias
.temperature
is number type property defined in the Trait
.presetTemperature
is number type property defined in the Trait
.For more details about format of the IoT device state and Trait
, Please refer to (http://docs.kii.com/en/guides/thingifsdk/).
tio_updater_t
instanceHere's the set-up code extracted from example app. The full code can be checked updater_init() in example.c
tio_updater_set_interval()
specifies interval of uploading state in seconds.Other set-up process is similar to tio_handler_t
.
tio_updater_onboard()
method is similar to tio_handler_onboard()
.
If you use both tio_handler_t
and tio_updater_t
for an IoT device, you just need to execute onboarding process from either one. No need to execute onboarding for both method since the thing ID bonding process just need to be executed once.
After the onboarding has been done, you can get tio_author_t
instance from either tio_handler_t
or tio_updater_t
and pass it to tio_handler_start()
or tio_updater_start()
method.
Now, it's ready to start the updater module.
Here's code extracted from example app.
author
: Pointer to tio_author_t
instance obtained from onboarded tio_handler_t
.updater_cb_state_size
: Size callback function pointer.updater_cb_read
: Read callback function pointer.updater_file_ctx
: Context object pointer referenced from both size callback/ read callback.tio
execute multiple asynchronous tasks.
The name of tasks executed by tio_handler_t
and tio_updater_t
listed bellow.
tio_handler_t
.KII_TASK_NAME_MQTT
is a name of the task defined at kii.h
and passed as argument when tio_handler_set_cb_task_create()
is called to create task/ thread.
The task creation is requested once when the tio_handler_start()
method is invoked.
This task is responsible for getting MQTT endpoint information thru REST API and connect, receive message from MQTT and propagate message to app by the TIO_CB_ACTION
callback, and send MQTT PINGREQ
periodically.
This task executes callbacks set by following APIs.
tio_handler_set_cb_sock_connect_http()
tio_handler_set_cb_sock_send_http()
tio_handler_set_cb_sock_recv_http()
tio_handler_set_cb_sock_close_http()
tio_handler_set_cb_sock_connect_mqtt()
tio_handler_set_cb_sock_send_mqtt()
tio_handler_set_cb_sock_recv_mqtt()
tio_handler_set_cb_sock_close_mqtt()
tio_handler_set_cb_delay_ms()
tio_handler_start()
(TIO_CB_ACTION
callback)tio_handler_set_cb_err()
(Optional. In case set the error handler for debugging, etc.)tio_handler_set_cb_json_parser_resource()
(Optional. In case dynamic memory allocation is chosen for parsing JSON.)This task uses buffers set by following APIs.
Those two buffers must be separated and have no overlaps.
tio_updater_t
.TIO_TASK_NAME_UPDATE_STATE
is a name of the task defined at kii.h
and passed as argument when tio_handler_set_cb_task_create()
is called to create task/ thread.
The task creation is requested once when the tio_updater_start()
method is invoked.
This task is responsible for periodically updates the IoT device state with the specified interval.
This task executes callbacks set by following APIs.
tio_updater_set_cb_sock_connect()
tio_updater_set_cb_sock_send()
tio_updater_set_cb_sock_recv()
tio_updater_set_cb_sock_close()
tio_updater_set_cb_delay_ms()
tio_updater_start()
(TIO_CB_SIZE
, TIO_CB_READ
callbacks.)tio_updater_set_cb_err()
(Optional. In case set the error handler for debugging, etc.)tio_updater_set_cb_json_parser_resource()
(Optional. In case dynamic memory allocation is chosen for parsing JSON.)This task uses buffers set by following APIs.
KII_TASK_NAME_MQTT
and TIO_TASK_NAME_UPDATE_STATE
would be executed in parallel. Therefore, if the callback implementation shares the resource that need exclusive access, you need to implement access control mechanism.