Develop a Wi-Fi smart plug device

更新时间:
复制 MD 格式

This topic describes how to develop firmware for a single-socket Wi-Fi smart plug. It uses the TG7100C chip and the smart_outlet application example from the IoT Platform software development kit (SDK) V1.6.6.

Background information

The smart_outlet application example has the following features:

  • Supports Bluetooth-assisted network provisioning using the Cloud Intelligence App (V3.5.5 or later) and the Tmall Genie App (V4.13.0 or later).
  • Supports device control through the cloud and local communication. Currently, only the Cloud Intelligence App supports local communication.
  • Supports over-the-air (OTA) device updates through the IoT Platform.
  • Supports restoring factory settings.
  • Supports retaining user settings after a power outage.

TG7100C overview

The TG7100C is a Wi-Fi and Bluetooth combo chip from Tmall Genie. For more information about the TG7100C chip documentation and software tools, see TG7100C.

Note

For feedback on the TG7100B chip, including issues related to drivers, production testing, hardware design, and radio frequency (RF), contact technical support through your business representative. You can also obtain support for application development using the IoT Platform Bluetooth Mesh SDK for topics such as product configuration, network provisioning, cloud connection, and OTA.

Compile the firmware

  1. Download the SDK. For the download link, see Get the SDK.
  2. Configure the development environment. For more information, see Prepare the development environment.
  3. Compile the code.
    1. (Optional) Unzip the SDK package.
      If you downloaded the SDK using Git commands, you do not need to unzip it.

      In the build.sh file in the SDK root directory, modify the following parameters based on your hardware module and application.

      default_type="example"     // Configure the product type
      default_app="smart_outlet" // Configure the name of the application to compile
      default_board="tg7100cevb" // Configure the module model to compile
      default_region=MAINLAND    // Configure the device's cloud connection region. You can set this to MAINLAND or SINGAPORE. The device can be activated globally.
      default_env=ONLINE         // Configure the cloud connection environment. The default is the online environment (ONLINE).
      default_debug=0            // Configure the debug level. A value of 0 is recommended for production firmware.
      default_args=""            // Configure other compilation parameters
      // For more information, see README.md
    2. Place the developed service code in the corresponding SDK directory. For example, the code for a standard non-metering plug is in the Products/example/smart_outlet directory.
    3. Run the following command to quickly compile the smart_outlet application example.
      ./build.sh example smart_outlet tg7100cevb MAINLAND ONLINE 0

      After compilation, the smart_outlet@tg7100cevb.bin file is generated in the out/smart_outlet@tg7100cevb/ directory.

    4. Flash the smart_outlet@tg7100cevb.bin file to a physical device.
      tg7100cevb_ota.bin file is the firmware used for OTA updates. TG7100C编译结果

Flash and run the firmware

  1. Flash the firmware to the TG7100C development board. For detailed instructions, see the TG7100C Development Board User Manual.
  2. Short-circuit pin 4 and pin 5 on the development board, and then press the reset button on the board.
    The following figure shows the development board setup.
  3. Open TGFlashEnv.exe from the downloaded flashing tool directory. Click Finish to open the flashing interface.
  4. Set Interface to Uart and click the Refresh button.
  5. Set the serial port parameters and select the corresponding flashing file.
    The serial port parameter configuration is shown in the following figure.
    • Partition Table, Boot 2 Bin, and MFG Bin, select the corresponding files from the flashing tool directory. The required filenames are shown in the figure.
    • Firmware Bin, select the compiled firmware.
    • Chip Erase, select True or False depending on whether you want to erase the entire flash memory. For example, during debugging, select False to retain a previously written device certificate.
  6. Click the Download button and simultaneously press the reset button on the development board to start flashing the firmware.
  7. After flashing is complete, view the runtime logs.
    Short-circuit pin 3 and pin 4 as shown in the following figure. Set the baud rate of the serial tool to 2000000 and press the reset button on the development board.

    The commonly used CLI commands are as follows.

    • reset: Resets the device and clears its network provisioning information.
    • free: Views memory usage.
    • linkkey: Writes and views certificates.
    • mac: Views the Wi-Fi MAC address of the development board.

smart_outlet application code structure

The file structure of the smart_outlet application example is as follows.

├── Products
│  │  ├── example/smart_outlet
│  │  │  ├── app_entry.c
│  │  │  ├── app_entry.h
│  │  │  ├── combo_net.c
│  │  │  ├── device_state_manager.c
│  │  │  ├── device_state_manager.h
│  │  │  ├── factory.c
│  │  │  ├── factory.h
│  │  │  ├── makefile
│  │  │  ├── make.settings
│  │  │  ├── msg_process_center.c
│  │  │  ├── msg_process_center.h
│  │  │  ├── property_report.c
│  │  │  ├── property_report.h
│  │  │  ├── smart_outlet.h
│  │  │  ├── smart_outlet.json
│  │  │  ├── smart_outlet_main.c
│  │  │  ├── smart_outlet.mk
│  │  │  ├── vendor.c
│  │  │  └── vendor.h

The files are described as follows.

  • Files that manufacturers must adapt for tasks such as device initialization: vendor.c and vendor.h
  • Application main entry points: app_entry.c and smart_outlet_main.c
  • Network provisioning and cloud connection state management: device_state_manager.c
  • Device control instruction processing: msg_process_center.c
  • Device property reporting: property_report.c
  • Factory test mode: factory.c
  • Bluetooth-assisted network provisioning: combo_net.c

Firmware adaptation instructions

To adapt the standard firmware for a single-socket smart plug application, you only need to make minor modifications to produce the final product firmware. The required adjustments vary based on your product needs and are described in the following sections.

  • GPIO adaptation

    A single-socket plug requires two GPIOs to control the relay and LED, and one GPIO to read the button status. You only need to modify the definitions in vendor.c. An example is shown below.

    ......
    #elif (defined (TG7100CEVB))
    #define LED_GPIO    1               // Controls the LED
    #define RELAY_GPIO  5               // Controls the relay switch
    #define KEY_GPIO    3               // Reads the button status
    ......

    During product development, configure the corresponding GPIOs based on the specific schematic design.

  • Status LED display adaptation
    • Device states are defined in the Products/example/smart_outlet/device_state_manager.h file.
      typedef enum {
          RECONFIGED = 0,            // Reconfigure with existing network configuration
          UNCONFIGED,                // Network provisioning started
          AWSS_NOT_START,            // Network provisioning timed out
          GOT_AP_SSID,               // Connected to AP successfully
          CONNECT_CLOUD_SUCCESS,     // Connected to the cloud successfully
          CONNECT_CLOUD_FAILED,      // Failed to connect to the cloud
          CONNECT_AP_FAILED,         // Failed to connect to AP
          CONNECT_AP_FAILED_TIMEOUT, // Connection to AP timed out
          APP_BIND_SUCCESS,          // App binding successful
          ...
          UNKNOW_STATE
      } eNetState;
    • The code for handling the status display is in the indicate_net_state_task function in the file. You can adjust it based on your product requirements.
      static void indicate_net_state_task(void *arg)
      {
          uint32_t nCount = 0;
          uint32_t duration = 0;
          int pre_state = UNKNOW_STATE;
          int cur_state = UNKNOW_STATE;
          int switch_stat = 0;
      
          while (1) {
              pre_state = cur_state;
              cur_state = get_net_state();
              switch (cur_state) {
                  case RECONFIGED:
                      ...
                      break;
                  case UNCONFIGED:
                      ...
                      break;
                  case AWSS_NOT_START:
                      ...
                      break;
                  case GOT_AP_SSID:
                  case CONNECT_CLOUD_FAILED:
                      ...
                      break;
                  case CONNECT_AP_FAILED_TIMEOUT:
                      ...
                      break;
                  case CONNECT_AP_FAILED:
                      ...
                      break;
                  case CONNECT_CLOUD_SUCCESS:
                      ...
                      break;
                  case APP_BIND_SUCCESS:        
                      ...
                      break;
                  ...
                  default:
                      break;
              }
              aos_msleep(100);
          }
      
          ...
      }
    • The default LED behaviors implemented in the code are as follows.
      Status Default LED display
      Network provisioning mode The LED blinks on for 0.8 seconds and off for 0.8 seconds.
      Restore Factory Settings The LED blinks on for 0.2 seconds and off for 0.2 seconds.
      AP connection timeout or AP authentication failed (2-minute timeout) The LED blinks on for 0.5 seconds and off for 0.5 seconds. The blinking stops after two minutes. Then, the LED turns on if the plug is powered on or turns off if the plug is powered off.
      Connected to the AP and attempting to connect to the cloud The LED blinks on for 0.8 seconds and off for 0.8 seconds while the device connects to the cloud.
      Connection to the cloud failed If the cloud connection fails, the device retries the connection. During the retry attempts, the LED behavior is the same as the 'Connected to the AP and attempting to connect to the cloud' state.
      Successfully connected to the cloud When the device successfully connects to the cloud, the LED stops blinking. The LED then turns on if the plug is powered on or turns off if the plug is powered off.
  • Button handling adaptation
    The standard firmware determines the user's action based on how long the button is pressed. Currently, three user actions are handled. The key_detect_event_task function in the file is responsible for button handling. The press durations for different actions are defined as follows. You can modify the button press durations for each action as needed.
    #define AWSS_REBOOT_TIMEOUT (4 * 1000) // Press and hold for 4s to enter network configuration mode and start re-provisioning
    #define AWSS_RESET_TIMEOUT (6 * 1000) // Press and hold for 6s to restore factory settings (when the device is already in network configuration mode)
    #define KEY_PRESSED_VALID_TIME_MIN 100
    #define KEY_PRESSED_VALID_TIME_MAX 500 // A button press between 100ms and 500ms is considered a valid press
    #define KEY_DETECT_INTERVAL 50 // Button press detection interval: 50ms
    #define AWSS_REBOOT_CNT AWSS_REBOOT_TIMEOUT /KEY_DETECT_INTERVAL
    #define AWSS_RESET_CNT AWSS_RESET_TIMEOUT /KEY_DETECT_INTERVAL
    #define KEY_PRESSED_CNT KEY_PRESSED_VALID_TIME /KEY_DETECT_INTERVAL
    
    // This function handles plug button detection
    void key_detect_event_task(void *arg)
    {
        int nCount = 0, awss_mode = 0;
        int timeout = (AWSS_REBOOT_CNT < AWSS_RESET_TIMEOUT)? AWSS_REBOOT_CNT : AWSS_RESET_TIMEOUT;
    
        while (1) {
            if (!product_get_key()) {
                nCount++;
                LOG("nCount :%d", nCount);
            } else {
                if (nCount >= KEY_PRESSED_CNT && nCount < timeout) {  // Button control
                    if (product_get_switch() == ON) {   // Button press turns off the plug relay
                        product_set_switch(OFF);
                        user_post_powerstate(OFF);
                    } else {                            // Button press turns on the plug relay
                        product_set_switch(ON);
                        user_post_powerstate(ON);
                    }
                }
                if ((awss_flag == 0) && (nCount >= AWSS_REBOOT_CNT)) {
                    LOG("do awss reboot");              // Press and hold for 4s to enter network configuration mode and start re-provisioning
                    do_awss_reboot();
                    break;
                } else if ((awss_flag == 1) && (nCount > AWSS_RESET_CNT)) {
                    LOG("do awss reset");               // Press and hold for 6s to restore factory settings
                    do_awss_reset();                    // Actually performs the device reset
                    break;
                }
                nCount = 0;
            }
            if ((awss_flag == 0) && (nCount >= AWSS_REBOOT_CNT && awss_mode == 0)) {
                set_net_state(RECONFIGED);              // Set the corresponding device state
                awss_mode = 1;
            } else if ((awss_flag == 1) && (nCount > AWSS_RESET_CNT && awss_mode == 0)) {
                set_net_state(UNCONFIGED);              // Set the corresponding device state
                awss_mode = 1;
            }
            aos_msleep(KEY_DETECT_INTERVAL);            // Button detection interval is 50ms
        }
        aos_task_exit(0);
    }
    • Short press: If the button is pressed for more than 100 ms but less than 500 ms, it is considered a toggle action.
    • Long press: If the button is pressed for more than 4 seconds, the device enters network provisioning mode. If the device is already in network provisioning mode and the button is pressed for another 6 seconds, the device enters factory reset mode.

General device-side features

The following features are already implemented in the smart_outlet application example. This section provides additional information about general device-side features.

  • Event callbacks
    Various system event handler functions are defined in the smart_outlet_main.c file. The callback functions are registered in the linkkit_main function.
    int linkkit_main()
    {
        ...
        
        /* 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_REQUEST, user_service_request_event_handler);
        IOT_RegisterCallback(ITE_PROPERTY_SET, user_property_set_event_handler);
    #ifdef ALCS_ENABLED
        /*Only for local communication service(ALCS) */
        IOT_RegisterCallback(ITE_PROPERTY_GET, user_property_get_event_handler);
    #endif
        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_INITIALIZE_COMPLETED, user_initialized);
        IOT_RegisterCallback(ITE_EVENT_NOTIFY, user_event_notify_handler);
        ...
    }
    Event Event trigger condition
    ITE_CONNECT_SUCC When the device connects to the cloud successfully.
    ITE_DISCONNECTED When the device disconnects from the cloud.
    ITE_RAWDATA_ARRIVED When the SDK receives raw data.
    ITE_SERVICE_REQUEST When the SDK receives a service (synchronous/asynchronous) call request.
    ITE_PROPERTY_SET When the SDK receives a property setting request.
    ITE_PROPERTY_GET When the SDK receives a property get request.
    ITE_REPORT_REPLY When the SDK receives an acknowledgement for a reported message.
    ITE_TRIGGER_EVENT_REPLY When the SDK receives an acknowledgement for a reported event.
    ITE_EVENT_NOTIFY When the SDK receives an event notification.
    ITE_INITIALIZE_COMPLETED When device initialization is complete.
  • Property reporting
    When a product's properties change, the new values must be reported to the IoT Platform. You can add logic to detect and report property changes based on your product requirements.
    void user_post_property(property_report_msg_t * msg)
    {
        int res = 0;
        user_example_ctx_t *user_example_ctx = user_example_get_ctx();
        char *property_payload = NULL;
        cJSON *response_root = NULL, *item_csr = NULL;
    
        response_root = cJSON_CreateObject();
        if (response_root == NULL) {
            return;
        }
    
        if (msg->seq != NULL && strcmp(msg->seq, SPEC_SEQ)) {
            item_csr = cJSON_CreateObject();
            if (item_csr == NULL) {
                cJSON_Delete(response_root);
                return;
            }
            cJSON_AddStringToObject(item_csr, "seq", msg->seq);
            cJSON_AddItemToObject(response_root, "CommonServiceResponse", item_csr);
        }
    #ifdef TSL_FY_SUPPORT
        // Compatible with the PowerSwitch property of older versions
        cJSON_AddNumberToObject(response_root, "PowerSwitch", msg->powerswitch);
    #endif
        // Handle the powerstate property of the new Thing Specification Language model
        cJSON_AddNumberToObject(response_root, "powerstate", msg->powerswitch);
        // Handle the allPowerstate property of the new Thing Specification Language model
        cJSON_AddNumberToObject(response_root, "allPowerstate", msg->all_powerstate);
        property_payload = cJSON_PrintUnformatted(response_root);
        cJSON_Delete(response_root);
    
        char *property_formated;
        uint32_t len;
        res = user_property_format(property_payload,strlen(property_payload),&property_formated,&len);
    #ifdef EN_COMBO_NET // For Wi-Fi & BLE Combo devices, property values can be reported simultaneously through the Bluetooth control link.
        if (combo_ble_conn_state()) {
            if (0 == res) {
                combo_status_report(property_formated, strlen(property_formated));
                LOG_TRACE("Post Property Message ID: %d Payload %s", res, property_formated);
            } else {
                combo_status_report(property_payload, strlen(property_payload));
                LOG_TRACE("Post Property Message ID: %d Payload %s", res, property_payload);
            }
        }
    #endif
        if (0 == res) {
            if (msg->seq != NULL && strcmp(msg->seq, SPEC_SEQ)) {
                res = IOT_Linkkit_Report_Ext(user_example_ctx->master_devid, ITM_MSG_POST_PROPERTY,
                        (unsigned char *)property_formated, strlen(property_formated), msg->flag);
            } else {
                res = IOT_Linkkit_Report(user_example_ctx->master_devid, ITM_MSG_POST_PROPERTY,
                        (unsigned char *)property_formated, strlen(property_formated));
            }
            LOG_TRACE("Post Property Message ID: %d Payload %s", res, property_formated);
            example_free(property_formated);
        } else {
            if (msg->seq != NULL && strcmp(msg->seq, SPEC_SEQ)) {
                res = IOT_Linkkit_Report_Ext(user_example_ctx->master_devid, ITM_MSG_POST_PROPERTY,
                        (unsigned char *)property_payload, strlen(property_payload), msg->flag);
            } else {
                res = IOT_Linkkit_Report(user_example_ctx->master_devid, ITM_MSG_POST_PROPERTY,
                        (unsigned char *)property_payload, strlen(property_payload));
            }
            LOG_TRACE("Post Property Message ID: %d Payload %s", res, property_payload);
        }
        example_free(property_payload);
    }
  • Property settings
    The smart_outlet application registers a callback function for ITE_PROPERTY_SET. In the user_property_set_event_handler callback function, it retrieves the property values set from the cloud and echoes the received data back to the cloud. This updates the device property values in the cloud. You can add your logic to process the received property values in this function.
    static int user_property_set_event_handler(const int devid, const char *request, const int request_len)
    {
        ...
        property_setting_handle(request, request_len, &msg);
        ...
    }
    
    static int property_setting_handle(const char *request, const int request_len, recv_msg_t * msg)
    {
        ...
        if ((item = cJSON_GetObjectItem(root, "setPropsExtends")) != NULL && cJSON_IsObject(item)) {
            ...
        }
        if ((item = cJSON_GetObjectItem(root, "powerstate")) != NULL && cJSON_IsNumber(item)) {
            // Handle setting the powerstate property
            msg->powerswitch = item->valueint;
            msg->all_powerstate = msg->powerswitch;
            ret = 0;
        }
    #ifdef TSL_FY_SUPPORT /* Support the PowerSwitch property of older versions */
        else if ((item = cJSON_GetObjectItem(root, "PowerSwitch")) != NULL && cJSON_IsNumber(item)) {
            msg->powerswitch = item->valueint;
            ret = 0;
        }
    #endif
        else if ((item = cJSON_GetObjectItem(root, "allPowerstate")) != NULL && cJSON_IsNumber(item)) {
            // Handle setting the allPowerstate property
            msg->powerswitch = item->valueint;
            msg->all_powerstate = msg->powerswitch;
            ret = 0;
        }
    #ifdef AOS_TIMER_SERVICE
        else if (((item = cJSON_GetObjectItem(root, "LocalTimer")) != NULL && cJSON_IsArray(item))|| \
            ((item = cJSON_GetObjectItem(root, "CountDownList")) != NULL && cJSON_IsObject(item)) || \
            ((item = cJSON_GetObjectItem(root, "PeriodTimer")) != NULL && cJSON_IsObject(item)) || \
            ((item = cJSON_GetObjectItem(root, "RandomTimer")) != NULL && cJSON_IsObject(item)))
        {
            // Handle settings for timer service properties, such as scheduled timers and countdowns
            cJSON_Delete(root);         // Before LocalTimer Handle, Free Memory
            timer_service_property_set(request);
            user_example_ctx_t *user_example_ctx = user_example_get_ctx();
            IOT_Linkkit_Report(user_example_ctx->master_devid, ITM_MSG_POST_PROPERTY,
                    (unsigned char *)request, request_len);
            return 0;
        }
    #endif
        else {
            LOG_TRACE("property set payload is not JSON format");
            ret = -1;
        }
    
        cJSON_Delete(root);
        if (ret != -1)
            send_msg_to_queue(msg);
    
        return ret;
    }
  • Local communication (currently supported only by the Cloud Intelligence App)

    For an introduction to the local communication feature, see Local communication development practice.

    The local communication feature is managed by the ALCS_ENABLED macro in the make.settings file. Use the IOT_RegisterCallback function to register the ITE_PROPERTY_GET event. The corresponding callback function is user_property_get_event_handler. This function currently implements the device properties for local communication requests as shown in the following code. If your product requires additional features, you can add processing cases for new properties.
    #ifdef ALCS_ENABLED
    static int user_property_get_event_handler(const int devid, const char *request, const int request_len, char **response,
            int *response_len)
    {
        user_example_ctx_t *user_example_ctx = user_example_get_ctx();
        device_status_t *device_status = &user_example_ctx->status;
        cJSON *request_root = NULL, *item_propertyid = NULL;
        cJSON *response_root = NULL;
    
        ...
    
        for (int index = 0; index < cJSON_GetArraySize(request_root); index++) {
            item_propertyid = cJSON_GetArrayItem(request_root, index);
            ...
            LOG_TRACE("Property ID, index: %d, Value: %s", index, item_propertyid->valuestring);
            if (strcmp("powerstate", item_propertyid->valuestring) == 0) { 
                // Handle the powerstate property of the new Thing Specification Language model
                cJSON_AddNumberToObject(response_root, "powerstate", device_status->powerswitch);
            }
            else if (strcmp("allPowerstate", item_propertyid->valuestring) == 0) {
                // Handle the allPowerstate property of the new Thing Specification Language model
                cJSON_AddNumberToObject(response_root,"allPowerstate", device_status->all_powerstate);
            }
    #ifdef TSL_FY_SUPPORT /* support old feiyan TSL */
            else if (strcmp("PowerSwitch", item_propertyid->valuestring) == 0) {
                // Compatible with the PowerSwitch property of older versions
                cJSON_AddNumberToObject(response_root, "PowerSwitch", device_status->powerswitch);
            }
    #endif
    #ifdef AOS_TIMER_SERVICE
            else if (strcmp("LocalTimer", item_propertyid->valuestring) == 0) {
                ...  // Handle the local timer LocalTimer
            } else if (strcmp("CountDownList", item_propertyid->valuestring) == 0) {
                ...  // Handle the countdown timer
    #endif
            }
        }
    
        ...
    }
    #endif
  • Cloud unbinding and restore factory settings notifications
    After a 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 network provisioning and clearing local data. If you use an 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 similar operations such as resetting network provisioning and clearing local data. You can decide which data to clear based on your product requirements after receiving these notifications. For more information, see the notify_msg_handle function in the example code at .
    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 (value == 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")) {     // Bind notification
                    LOG_TRACE("Device Bind");
                    vendor_device_bind();                   // Operations to be completed when the device is bound, definable by the device application
                } else if (!strcmp(op->valuestring, "Unbind")) {    // Unbind notification
                    LOG_TRACE("Device unBind");
                    vendor_device_unbind();                 // Operations to be completed when the device is unbound, definable by the device application
                } else if (!strcmp(op->valuestring, "Reset")) {     // Reset notification
                    LOG_TRACE("Device reset");
                    vendor_device_reset();                  // Operations to be completed when the device is reset, definable by the device application
                }
            }
        }
        ....
    }
  • Bluetooth-assisted network provisioning
    For information about device-side development for Bluetooth-assisted network provisioning, see Device-side development.
    Note SDK V1.6.6 and later support a new Bluetooth-assisted network provisioning solution. This solution works with Tmall Genie App V4.13.0 or later and Cloud Intelligence App V3.5.5 or later. The new solution requires the Device Name in the device certificate to match the Wi-Fi MAC address. For more information, see Develop a socket product for your own brand and Develop a socket product for the Tmall Genie ecosystem.
  • Device-side timer feature

    The platform uses DeviceTimer to develop the device-side timer feature. For detailed instructions, see Develop a local device-side timer feature.