Develop products based on certified modules
This topic describes how device developers can use Alibaba Cloud IoT-certified Wi-Fi modules to develop product features and connect devices to IoT Platform.
Prerequisites
You have installed the development environment. For more information, see Compile the SDK.
Get and compile the SDK code
Obtain the SDK code. For more information, see Obtain the SDK.
Unzip the downloaded package.
Verify that AOS can be compiled.
The following example shows how to compile a living_platform program for a module.
./build.sh example living_platform mk3080For example, for a module named mk3080, the living_platform@mk3080.all.bin file is generated in the out\living_platform@mk3080\binary\ directory after compilation. You can flash this file to the module to verify that the program runs as expected.
NoteYou can view the supported modules in the `board` directory.
Flash firmware to the module
After you compile the firmware, you must flash it to the module. The flashing procedure varies depending on the module. Contact the module manufacturer to obtain the flashing tool and instructions.
Define a product in IoT Platform
Create a product.
For more information, see Create a product. Set the parameters as follows.
Set Node Type to Device.
Set Network Connection Method to Wi-Fi.
Set Chip Module to Certified Module.
Set Data Format to ICA Standard Data Format (Alink JSON).
Define product features.
Define the features for your product. For more information, see Function Overview.
Debug the device.
Add a test device and obtain its device certificate.
For more information, see Add a test device.
On the Human-Computer Interaction page for the product, you can enable the option to control the product using a public version of the app. This lets you use the public app that is provided by IoT Platform to control the device. For more information about how to configure app parameters, see Configure human-computer interaction.
On the Batch Production page, confirm the device information.
NoteProduction starts only after device development is complete. During the development phase, do not click Complete Development.
Product feature development
Develop the product model.
For a Wi-Fi device, you must first use Wi-Fi provisioning to obtain the SSID and password of a Wi-Fi hot spot. Porting and debugging the Wi-Fi provisioning feature can be time-consuming. The following two methods let you develop product features while you port and debug the Wi-Fi provisioning feature.
Connect the device directly to a specified hot spot and then to IoT Platform. This lets you start developing and debugging the product's Thing Specification Language (TSL) model features. In the following sample code, the code for provisioning is commented out.
NoteThe modified
start_netmgr()function is in the AliOS-Things/example/linkkitapp/app_entry.c file.int application_start(int argc, char **argv) { ... #if 0 #ifdef SUPPORT_DEV_AP aos_task_new("dap_open", awss_open_dev_ap, NULL, 4096); #else aos_task_new("netmgr_start", start_netmgr, NULL, 4096); #endif #endif // The following code connects the device directly to a specified SSID. netmgr_ap_config_t config; strncpy(config.ssid, "your_ssid", sizeof(config.ssid) - 1); strncpy(config.pwd, "your_ssid_password", sizeof(config.pwd) - 1); netmgr_set_ap_config(&config); netmgr_start(false); ... }During the debugging phase, you can use cli commands for provisioning. After the device connects to a Wi-Fi hot spot and obtains an IP address, it automatically connects to IoT Platform.
netmgr connect ssid password
Configure device identity information.
The device identity information is set in the linkkit_example_solo.c file. Replace this information with the information for your test device.
// for demo only #define PRODUCT_KEY "a15****PqM" #define PRODUCT_SECRET "4uZsr*****zhjPM" #define DEVICE_NAME "IFn6******OaI2cJy" #define DEVICE_SECRET "qwvShyphC*******NZFjc8S"After you set the device identity information in the program, compile the code and flash the firmware to the module. Use the flashing tool provided by the module manufacturer. Make sure the device can connect to Alibaba Cloud IoT Platform. If the module provides serial port output, a message similar to the following is displayed after the module connects to Alibaba Cloud IoT Platform.

When the module connects to Alibaba Cloud IoT Platform, its status changes to Online in the IoT Platform console. You can also view the time at which the device connected to the platform.
Report product properties.
When a product property changes, you must report the new value to IoT Platform. You must define and implement the detection and reporting of property changes.
The product in the sample code has a `LightSwitch` property of the Boolean type and an `RGBColor` property of the struct type. In the
linkkit_example()function, theuser_post_property()function is called to report the relevant properties. You can use this code as a reference to report product property changes.void user_post_property(void) { static int example_index = 0; int res = 0; user_example_ctx_t *user_example_ctx = user_example_get_ctx(); char *property_payload = "NULL"; if (example_index == 0) { /* Normal Example */ property_payload = "{\"LightSwitch\":1}"; example_index++; } else if (example_index == 1) { /* Wrong Property ID */ property_payload = "{\"LightSwitchxxxx\":1}"; example_index++; } else if (example_index == 2) { /* Wrong Value Format */ property_payload = "{\"LightSwitch\":\"test\"}"; example_index++; } else if (example_index == 3) { /* Wrong Value Range */ property_payload = "{\"LightSwitch\":10}"; example_index++; } else if (example_index == 4) { /* Missing Property Item */ property_payload = "{\"RGBColor\":{\"Red\":45,\"Green\":30}}"; example_index++; } else if (example_index == 5) { /* Wrong Params Format */ property_payload = "\"hello world\""; example_index++; } else if (example_index == 6) { /* Wrong Json Format */ property_payload = "hello world"; example_index = 0; } res = IOT_Linkkit_Report(user_example_ctx->master_devid, ITM_MSG_POST_PROPERTY, (unsigned char *)property_payload, strlen(property_payload)); EXAMPLE_TRACE("Post Property Message ID: %d", res); }You can report product events.
If a product has defined events, you must implement the logic to detect these events and send them to the cloud when they occur.
For example, a product defines an event with the identifier `Error` that has an output parameter with the identifier `ErrorCode`. The following sample code shows how to send this event to IoT Platform.
void user_post_event(void) { static int example_index = 0; int res = 0; user_example_ctx_t *user_example_ctx = user_example_get_ctx(); char *event_id = "Error"; char *event_payload = "NULL"; if (example_index == 0) { /* Normal Example */ event_payload = "{\"ErrorCode\":0}"; example_index++; } else if (example_index == 1) { /* Wrong Property ID */ event_payload = "{\"ErrorCodexxx\":0}"; example_index++; } else if (example_index == 2) { /* Wrong Value Format */ event_payload = "{\"ErrorCode\":\"test\"}"; example_index++; } else if (example_index == 3) { /* Wrong Value Range */ event_payload = "{\"ErrorCode\":10}"; example_index++; } else if (example_index == 4) { /* Wrong Value Range */ event_payload = "\"hello world\""; example_index++; } else if (example_index == 5) { /* Wrong Json Format */ event_payload = "hello world"; example_index = 0; } res = IOT_Linkkit_TriggerEvent(user_example_ctx->master_devid, event_id, strlen(event_id), event_payload, strlen(event_payload)); EXAMPLE_TRACE("Post Event Message ID: %d", res); }In the
linkkit_example()function, the `linkkit_ops` structure defines various system event handlers to process product callback functions. The following code provides an example.int linkkit_example() { ... /* Register Callback */ IOT_RegisterCallback(ITE_CONNECT_SUCC, user_connected_event_handler); IOT_RegisterCallback(ITE_DISCONNECTED, user_disconnected_event_handler); IOT_RegisterCallback(ITE_RAWDATA_ARRIVED, user_down_raw_data_arrived_event_handler); IOT_RegisterCallback(ITE_SERVICE_REQUST, user_service_request_event_handler); IOT_RegisterCallback(ITE_PROPERTY_SET, user_property_set_event_handler); IOT_RegisterCallback(ITE_PROPERTY_GET, user_property_get_event_handler); IOT_RegisterCallback(ITE_REPORT_REPLY, user_report_reply_event_handler); IOT_RegisterCallback(ITE_TRIGGER_EVENT_REPLY, user_trigger_event_reply_event_handler); IOT_RegisterCallback(ITE_TIMESTAMP_REPLY, user_timestamp_reply_event_handler); IOT_RegisterCallback(ITE_INITIALIZE_COMPLETED, user_initialized); IOT_RegisterCallback(ITE_FOTA, user_fota_event_handler); IOT_RegisterCallback(ITE_COTA, user_cota_event_handler); };The callback function is described as follows.
Event
Callback function prototype
Event trigger condition
ITE_CONNECT_SUCC
int callback(void);When the device successfully connects to the cloud.
ITE_DISCONNECTED
int callback(void);When the device disconnects from the cloud.
ITE_RAWDATA_ARRIVED
int callback(const int devid, const unsigned char *payload, const int payload_len);When Linkkit receives raw data.
ITE_SERVICE_REQUEST
int callback(const int devid, const char _serviceid, const int serviceid_len, const char request, const int request_len, char _response, int *response_len);When Linkkit receives a service call request (sync or asynchronous).
ITE_PROPERTY_SET
int callback(const int devid, const char *request, const int request_len);When Linkkit receives a property setting request.
ITE_PROPERTY_GET
int callback(const int devid, const char _request, const int request_len, char _response, int response_len);When Linkkit receives a property query request.
ITE_REPORT_REPLY
int callback(const int devid, const int msgid, const int code, const char *reply, const int reply_len);When Linkkit receives an acknowledgement for a reported message.
ITE_TRIGGER_EVENT_REPLY
int callback(const int devid, const int msgid, const int code, const char eventid, const int eventid_len, const char message, const int message_len);When Linkkit receives an acknowledgement for a reported event.
ITE_TIMESTAMP_REPLY
int callback(const char *timestamp);When Linkkit receives an acknowledgement for a timestamp query request.
ITE_TOPOLIST_REPLY
int callback(const int devid, const int msgid, const int code, const char * payload, const int payload_len);When Linkkit receives an acknowledgement for a topology query request.
ITE_PERMIT_JOIN
int callback(const char * product_key, const int time);When Linkkit receives a request to allow a sub-device to connect to the network.
ITE_INITIALIZE_COMPLETED
int callback(const int devid);When device initialization is complete.
ITE_FOTA
int callback(int type, const char *version);When Linkkit receives a notification about available firmware.
ITE_COTA
int callback(int type, const char config_id, int config_size, const char get_type, const char sign, const char sign_method, const char *url);When Linkkit receives a notification about an available remote configuration file.
Process the main loop.
The
linkkit_example()function contains a loop. In this loop, you must periodically callIOT_Linkkit_Yieldto process Linkit services. The following code provides an example.time_begin_sec = user_update_sec(); while (1) { IOT_Linkkit_Yield(USER_EXAMPLE_YIELD_TIMEOUT_MS); time_now_sec = user_update_sec(); if (time_prev_sec == time_now_sec) { continue; } if (max_running_seconds && (time_now_sec - time_begin_sec > max_running_seconds)) { EXAMPLE_TRACE("Example Run for Over %d Seconds, Break Loop!\n", max_running_seconds); break; } /* Post Property Example */ if (time_now_sec % 11 == 0 && user_master_dev_available()) { user_post_property(); } /* Post Event Example */ if (time_now_sec % 17 == 0 && user_master_dev_available()) { user_post_event(); } /* Device Info Update Example */ if (time_now_sec % 23 == 0 && user_master_dev_available()) { user_deviceinfo_update(); } /* Device Info Delete Example */ if (time_now_sec % 29 == 0 && user_master_dev_available()) { user_deviceinfo_delete(); } /* Post Raw Example */ if (time_now_sec % 37 == 0 && user_master_dev_available()) { user_post_raw_data(); } time_prev_sec = time_now_sec; }The
linkkit_example()function also includes demo code for reporting all properties and events. When you develop your product, replace this demo code with your own business logic.while (1) { IOT_Linkkit_Yield(USER_EXAMPLE_YIELD_TIMEOUT_MS); /* Post Property Example */ if (user_master_dev_available()) { user_post_property(); } /* Post Event Example */ if (user_master_dev_available()) { user_post_event(); } }After you implement the features for your Thing Specification Language (TSL) model, compile the firmware. Then, flash the firmware to the module using its flashing method to verify and debug the features.
Wi-Fi provisioning
The following provisioning methods are supported.
One-click provisioning (Smartconfig): The app directly provisions the device.
Phone hot spot provisioning (phone-config): The app directly provisions the device.
Router hot spot provisioning (router-config): This method is intended for router manufacturers or ISPs.
Zero-configuration provisioning (zero-config): A provisioned device provisions another device that is waiting to be provisioned.
Device hot spot provisioning (dev-ap): The device creates a hot spot. A phone connects to the device hot spot to provision the device.
Bluetooth provisioning (ble-config): Use Bluetooth (BT) or Bluetooth Low Energy (BLE) to provision the device.

When you develop the Wi-Fi provisioning feature, make sure to remove the modifications that you made to start_netmgr() when you developed the product's Thing Specification Language (TSL) model in the previous section.
static void start_netmgr(void *p)
{
/*
* register event callback to detect event of AWSS
*/
iotx_event_regist_cb(linkkit_event_monitor);
netmgr_start(true);
aos_task_exit(0);
}
The detailed development procedure is as follows.
Public APIs for network provisioning
/* * Copyright (C) 2015-2018 Alibaba Group Holding Limited */ #ifndef __IOT_EXPORT_AWSS_H__ #define __IOT_EXPORT_AWSS_H__ #if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */ extern "C" { #endif /** * @brief start Wi-Fi setup service * * @retval -1 : Wi-Fi setup fail * @retval 0 : sucess * @note: awss_config_press must been called to enable Wi-Fi setup service */ int awss_start(); /** * @brief stop wifi setup service * * @retval -1 : failure * @retval 0 : sucess * @note * if awss_stop is called before exit of awss_start, awss and notify will stop. * it may cause failutre of awss and device bind. */ int awss_stop(); /** * @brief make sure user touches device belong to themselves * * @retval -1 : failure * @retval 0 : sucess * @note: AWSS dosen't parse awss packet until user touch device using this api. */ int awss_config_press(); /** * @brief start Wi-Fi setup service with device ap * * @retval -1 : failure * @retval 0 : sucess * @note * 1. if awss_stop or awss_dev_ap_stop is called before exit of awss_dev_ap_start * awss with device ap and notify will stop, it may cause failutre of device ap * and device bind. * 2. awss_dev_ap_start doesn't need to call awss_config_press to been enabled. */ int awss_dev_ap_start(); /** * @brief stop Wi-Fi setup service with device ap * * @retval -1 : failure * @retval 0 : sucess * @note * if awss_dev_ap_stop is called before exit of awss_dev_ap_start * awss with device ap and notify will stop, it may cause failutre of device ap */ int awss_dev_ap_stop(); /** * @brief report token to cloud after Wi-Fi setup success * * @retval -1 : failure * @retval 0 : sucess */ int awss_report_cloud(); /** * @brief report reset to cloud. * * @retval -1 : failure * @retval 0 : sucess * @note * device will save reset flag if device dosen't connect cloud, device will fails to send reset to cloud. * when connection between device and cloud is ready, device will retry to report reset to cloud. */ int awss_report_reset(); enum awss_event_t { AWSS_START = 0x1000, // AWSS start without enbale, just supports device discover AWSS_ENABLE, // AWSS enable AWSS_LOCK_CHAN, // AWSS lock channel(Got AWSS sync packet) AWSS_CS_ERR, // AWSS AWSS checksum is error AWSS_PASSWD_ERR, // AWSS decrypt passwd error AWSS_GOT_SSID_PASSWD, // AWSS parse ssid and passwd successfully AWSS_CONNECT_ADHA, // AWSS try to connnect adha (device discover, router solution) AWSS_CONNECT_ADHA_FAIL, // AWSS fails to connect adha AWSS_CONNECT_AHA, // AWSS try to connect aha (AP solution) AWSS_CONNECT_AHA_FAIL, // AWSS fails to connect aha AWSS_SETUP_NOTIFY, // AWSS sends out device setup information (AP and router solution) AWSS_CONNECT_ROUTER, // AWSS try to connect destination router AWSS_CONNECT_ROUTER_FAIL, // AWSS fails to connect destination router. AWSS_GOT_IP, // AWSS connects destination successfully and got ip address AWSS_SUC_NOTIFY, // AWSS sends out success notify (AWSS sucess) AWSS_BIND_NOTIFY, // AWSS sends out bind notify information to support bind between user and device AWSS_ENABLE_TIMEOUT, // AWSS enable timeout(user needs to call awss_config_press again to enable awss) AWSS_RESET = 0x3000, // Linkkit reset success (just got reset response from cloud without any other operation) }; #if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */ } #endif #endifCall the provisioning service from the app.
/* * application_start is application entrance based on sdk. */ int application_start(int argc, char **argv) { ...... /* * set device triple ID information before AWSS, otherwise AWSS will fail. * HAL_SetProductKey(product_key); * HAL_SetProductSecret(product_secret); * HAL_SetDeviceName(dev_name); * HAL_SetDeviceSecret(dev_secret); */ set_iotx_info(); /* * set log level to print debug information about AWSS */ LITE_set_loglevel(5); // 5 for debug level /* * Start netmgr task for AWSS * default stack size of netmgr task is 4096B, * if the module or die takes more stack size, * please set larger stack size (maybe, 6KB) */ #ifdef SUPPORT_DEV_AP aos_task_new("dap_open", awss_open_dev_ap, NULL, 4096); #else aos_task_new("netmgr_start", start_netmgr, NULL, 4096); #endif aos_loop_run(); return 0; } #ifdef SUPPORT_DEV_AP void awss_open_dev_ap(void *p) { iotx_event_regist_cb(linkkit_event_monitor); LOG("%s\n", __func__); if (netmgr_start(false) != 0) { aos_msleep(2000); awss_dev_ap_start(); } aos_task_exit(0); } #endif static void start_netmgr(void *p) { /* * register event callback to detect event of AWSS */ iotx_event_regist_cb(linkkit_event_monitor); netmgr_start(true); aos_task_exit(0); }NoteYou must call
set_iotx_infoto set the device certificate information before you call awss_start or netmgr_start. Otherwise, the device cannot parse the router's password. This is because the password is encrypted, and the decryption key is derived from the device certificate information.To enable logging for debugging, call
LIET_set_loglevelbefore awss_start.awss_start only starts the Alibaba Wireless Setup Service (AWSS) to discover nearby access points (APs), but it does not enable the AWSS service itself. For security reasons, to allow the device to parse provisioning packets, you must also call
awss_press_configto enable the service. This requirement does not apply to device hot spot provisioning.The demo uses a button press to trigger the call to
awss_press_config. However, you can design a custom trigger method based on your product's features.
Currently, `awss_start` is encapsulated as `netmgr_start` in the `netmgr` module of AliOS. For more information, refer to the `netmgr_start` code.
int netmgr_start(bool autoconfig) { ...... /* * if the last AP information exists * try to connect the last AP. */ if (has_valid_ap() == 1) { aos_post_event(EV_WIFI, CODE_WIFI_CMD_RECONNECT, 0); return 0; } ...... /* * if the last AP information doesn't exist * start AWSS (Alibaba Wireless Setup Service) */ if (autoconfig) { netmgr_wifi_config_start(); // call awss_start() return 0; } ...... return -1; }Demo description.
To switch from device hot spot provisioning to other provisioning modes, call `do_awss`.
void do_awss() { aos_task_new("dap_close", awss_close_dev_ap, NULL, 2048); aos_task_new("netmgr_start", start_netmgr, NULL, 4096); } static void awss_close_dev_ap(void *p) { awss_dev_ap_stop(); LOG("%s exit\n", __func__); aos_task_exit(0); } static void start_netmgr(void *p) { iotx_event_regist_cb(linkkit_event_monitor); LOG("%s\n", __func__); aos_msleep(2000); netmgr_start(true); aos_task_exit(0); }To switch from other provisioning modes to device hot spot provisioning, call do_awss_dev_ap.
void do_awss_dev_ap() { aos_task_new("netmgr_stop", stop_netmgr, NULL, 4096); aos_task_new("dap_open", awss_open_dev_ap, NULL, 4096); } static void stop_netmgr(void *p) { awss_stop(); LOG("%s\n", __func__); aos_task_exit(0); } static void awss_open_dev_ap(void *p) { iotx_event_regist_cb(linkkit_event_monitor); LOG("%s\n", __func__); if (netmgr_start(false) != 0) { aos_msleep(2000); awss_dev_ap_start(); } aos_task_exit(0); }
For more information about Wi-Fi provisioning, see the Provisioning development documentation.
Cloud unbinding and factory reset notifications
After the device is unbound, the cloud sends an unbinding event notification: {"identifier":"awss.BindNotify","value":{"Operation":"Unbind"}}. When the device receives this message, it can perform operations such as resetting provisioning and clearing local data.
If you use the app to restore the device to factory settings, the cloud sends a reset event notification: {"identifier":"awss.BindNotify","value":{"Operation":"Reset"}}. When the device receives this message, it can perform operations such as resetting provisioning and clearing local data. You can decide which clearing operations to perform after the device receives an unbinding or a factory reset notification, depending on your specific product type.
Refer to the notify_msg_handle function in the example/smart_outlet/smart_outlet_main.c sample file and make the necessary changes.
static int notify_msg_handle(const char *request, const int request_len)
{
....
if (!strcmp(item->valuestring, "awss.BindNotify")) {
cJSON *value = cJSON_GetObjectItem(request_root, "value");
if (item == NULL || !cJSON_IsObject(value)) {
cJSON_Delete(request_root);
return -1;
}
cJSON *op = cJSON_GetObjectItem(value, "Operation");
if (op != NULL && cJSON_IsString(op)) {
if (!strcmp(op->valuestring, "Bind")) {
EXAMPLE_TRACE("Device Bind");
vendor_device_bind();
}
if (!strcmp(op->valuestring, "Unbind")) {
EXAMPLE_TRACE("Device unBind");
vendor_device_unbind();
}
if (!strcmp(op->valuestring, "Reset")) {
EXAMPLE_TRACE("Device Reset");
vendor_device_reset();
}
}
}
....
}
Device reset
You can add a reset button to your product for IoT Platform to clear the device configuration and restore the device to its factory state. You must also call the awss_report_reset() function to notify the cloud to unbind the device from the user.
Therefore, you must add a call to the awss_report_reset() function in the handler logic for the reset button.
/*
* After the application calls this API, Linkkit first stores a factory reset flag in the flash memory and reports the reset operation to the cloud.
* If no response is received from the cloud within a specified time (3 seconds), the device re-uploads the reset report until a response is received.
* Some products may need to restart upon reset. If the reset report is not successfully sent before the restart,
* the device first checks if the factory reset flag is set in the flash memory after the next connection to the cloud.
* If the flag is set, the device first reports the reset to the cloud and continues to do so until it succeeds.
*/
int awss_report_reset();
Develop OTA
If the Over-the-Air (OTA) feature is enabled, see OTA programming.