Application Development

Last Updated on : 2024-11-20 02:14:53download

This topic describes how to develop a temperature and humidity sensor by using Tuya’s Bluetooth module and the Bluetooth SDK.

Prerequisites

Product creation

Create a temperature and humidity sensor on the Tuya Developer Platform. For more information, see Create Products.

Set up development environment

You will need the following to proceed:

  • Tuya Sandwich Bluetooth Low Energy SoC microcontroller board (BTU)
  • Tuya Sandwich temperature and humidity sensor function board (SHT30-DIS)
  • USB to TTL converter and serial debugging assistant
  • Tuya production toolkit (Cloud Module Burning Authorization Platform)
  • Telink programmer, Telink IDE, and Telink Burning and Debugging Tools (BDT)
  • PC running Windows 10
  • SDK (tuya-ble-sdk-demo-project-tlsr8253)
  • Mobile phone and Smart Life app

If you use the BTU module or module built with the same chip platform, have the above items ready before development. The microcontroller board and function board are optional. If you use Tuya’s Bluetooth module of a different chip platform, see Set up Development Environment and documentation from the chip vendor for environment setup.

Flashing and authorization

See Flash Firmware and Authorize Module to flash the initial firmware to the module and authorize it to connect to the cloud by using the Cloud Module Burning Authorization Platform. Then, pair the device with the Smart Life app to verify cloud connectivity. Then, you can use Telink BDT to flash firmware to the module when you debug the code.

Quick start

Walk through the following steps to try out the demo.

  1. Download the demo file app_demo from GitHub repository tuya-ble-sdk-development-course-demo.
  2. Copy all files except tuya_ble_sdk_demo.h in the app_demo directory to your project. Make sure to replace the value of the PID and firmware version with your own. Then, set up the include path.
  3. Check if you need to modify the initial parameter of the use_ext_license_key and device_id_len according to the authorization solution you use.
  4. Check if you need to modify the pin configuration according to your circuit design. For more information, see Pin functions.
  5. Compile your project. Flash the generated firmware to your development board by using Telink BDT.
  6. Pair your device with the Smart Life app to try out features. For more information, see Feature demonstration.

If you use Tuya’s Bluetooth module of a different chip platform, there is no need to copy the board directory under app_demo to your project, but make sure to check if the API functions in the board directory are implemented. You should use the flashing tool provided by the chip vendor to flash firmware to your board.

SDK introduction

File structure

Use the SDK for TLSR825x as an example to describe the SDK directory.

├── ble_sdk_multimode
|    ├── telink_sdk         /* SDK from the chip vendor */
|    ├── tuya_ble_sdk_demo   /* Tuya Bluetooth SDK and the demo, which are adapted to the TLSR825x platform. */
|    |    ├── app            /* The application code with demos included. You can develop on top of it. */
|    |    ├── board          /* The chip platform specific code, including the peripheral hardware abstraction and the `tuya_ble_sdk`-specific port API and application configuration file. */
|    |    ├── components     /* The component code, including generic components. Your custom components can be put here. */
|    |    ├── doc            /* Documentation */
|    |    ├── tools          /* Toolkit */
|    |    └── tuya_ble_sdk   /* Tuya Bluetooth SDK. It implements the communication between smart devices and Smart Life app as well as task scheduler. */
|    |
|    └── .cproject and others     /* The project file. */
|
├── CHANGELOG.md             /* The changelog. */
├── README.md                /* README in English */
└── README_zh-CN             /* README in Chinese */
  • tuya_ble_sdk_demo

    • tuya_ble_sdk_demo.c: Implements tuya_ble_sdk initialization and includes the event scheduler APIs of the application layer.
    • tuya_ble_sdk_test.c: Includes serial commands for testing tuya_ble_sdk. It is used for testing only and irrelevant to the application.
    • tuya_ble_bulk_data_demo.c: Includes the routine for bulk transfer.
    • tuya_ble_product_test_demo.c: Includes the routine for end product test.
  • component

    • external\easylogger: The logger to print debug messages.
  • board

    • TLSR825X\ty_board_tlsr825x: Peripheral hardware abstraction.
    • TLSR825X\tuya_ble_port: Includes the tuya_ble_sdk-specific port API and application configuration file.
    • TLSR825X\ota: OTA update implementation for different chip platforms. They use the same serial transmission protocols, but different flash operations.

Entry to application

  • tuya_ble_sdk_demo_init: The Bluetooth SDKs for different chip platforms come with demos, allowing you to directly develop on top of tuya_ble_sdk_demo.c. tuya_ble_sdk_demo_init is used to initialize the demo. Add your application initialization code at the end of this function.

    void tuya_ble_sdk_demo_init(void)
    {
        /* Whether to use the license in `tuya_ble_sdk_demo.h` to configure the related parameters. */
        if (tuya_ble_device_param.use_ext_license_key) {
            memcpy(tuya_ble_device_param.device_id, device_id_test, DEVICE_ID_LEN);
            memcpy(tuya_ble_device_param.auth_key, auth_key_test, AUTH_KEY_LEN);
            memcpy(tuya_ble_device_param.mac_addr_string, TY_DEVICE_MAC, MAC_STRING_LEN);
        }
        /* Configure PID and device name */
        memcpy(tuya_ble_device_param.product_id, TY_DEVICE_PID, tuya_ble_device_param.product_id_len);
        memcpy(tuya_ble_device_param.adv_local_name, TY_DEVICE_NAME, tuya_ble_device_param.adv_local_name_len);
        /* Initialize tuya_ble_sdk. */
        tuya_ble_sdk_init(&tuya_ble_device_param);
        /* Register tuya_ble_sdk callback */
        tuya_ble_callback_queue_register(tuya_ble_sdk_callback);
        /* Initialize OTA and timer */
        tuya_ble_ota_init();
        tuya_ble_disconnect_and_reset_timer_init();
        tuya_ble_update_conn_param_timer_init();
        /* Initialize applications of the smart product */
        tuya_ble_app_init();
    }
    
  • tuya_ble_main_tasks_exec: If you do not use the RTOS, this function is used as the event scheduler of the Bluetooth SDK for cyclic execution. It is located in tuya_ble_main.c. You can put tasks in this function to cycle them. Alternatively, we recommend you create a timer to schedule task execution. For more information, see Software timer.

    void tuya_ble_main_tasks_exec(void)
    {
        /* The main loop of applications of the smart product */
        tuya_ble_app_loop();
        /* The main scheduler */
        tuya_sched_execute();
    }
    

Common APIs

Log printing

API list

Function name Description
TUYA_APP_LOG_ERROR Print error messages.
TUYA_APP_LOG_WARNING Print warning messages.
TUYA_APP_LOG_INFO Print information messages.
TUYA_APP_LOG_DEBUG Print debugging messages.

API description

API file: tuya_ble_log.h

/* Enable/disable logger */
#define TUYA_APP_LOG_ENABLE 1	/* 0: Disable. 1: Enable. (custom_tuya_ble_config.h) */
#define TY_LOG_ENABLE       1	/* 0: Disable. 1: Enable. (board.h) */
158: /* Set logging levels */
#defien TUYA_APP_LOG_LEVEL  TUYA_APP_LOG_LEVEL_DEBUG

/**
 * @brief Print logs. You can replace xxxx with ERROR, WARNING, INFO, or DEBUG.
 * @param[in] format: Format control
 * @param[in] …: Variable parameter
 * @return None
 */
void TUYA_APP_LOG_xxxx(const char *format, …)

Sample application

TUYA_APP_LOG_xxxx("tuya_ble_sdk_init succeeded.");
TUYA_APP_LOG_xxxx("tuya_ble_sdk_init failed, error code: %d.", res);
TUYA_APP_LOG_xxxx("receive data: %x.", data);

Software timer

API list

Function name Description
tuya_ble_timer_create Create a timer.
tuya_ble_timer_start Start a specified timer.
tuya_ble_timer_stop Stop a specified timer.
tuya_ble_timer_restart Restart a specified timer.
tuya_ble_timer_delete Delete a specified timer.

API description

API file: tuya_ble_port.h

/**
 * @brief Create a timer
 * @param[in] p_timer_id: Timer ID
 * @param[in] timeout_value_ms: Timeout period in milliseconds
 * @param[in] mode: Working mode
 * @param[in] timeout_handler: Timeout handler
 * @return  The operation result
 */
tuya_ble_status_t tuya_ble_timer_create(void** p_timer_id,
                                        uint32_t timeout_value_ms,
                                        tuya_ble_timer_mode mode,
                                        tuya_ble_timer_handler_t timeout_handler);

/**
 * @brief Start the specified timer
 * @param[in] timer_id: Timer ID
 * @return The operation result
 */
tuya_ble_status_t tuya_ble_timer_start(void* timer_id);

/**
 * @brief Stop the specified timer
 * @param[in] timer_id: Timer ID
 * @return The operation result
 */
tuya_ble_status_t tuya_ble_timer_stop(void* timer_id);

/**
 * @brief Restart the specified timer
 * @param[in] timer_id: Timer ID
 * @return The operation result
 */
tuya_ble_status_t tuya_ble_timer_restart(void* timer_id, uint32_t timeout_value_ms);

/**
 * @brief Delete the specified timer
 * @param[in] timer_id: Timer ID
 * @return The operation result
 */
tuya_ble_status_t tuya_ble_timer_delete(void* timer_id);

Data type

/* Timer working mode */
typedef enum {
    TUYA_BLE_TIMER_SINGLE_SHOT, /* One-time task. The timer stops counting when it expires. */
    TUYA_BLE_TIMER_REPEATED,	/* Recurring task. The timer begins counting again when it expires. */
} tuya_ble_timer_mode;

/* The type of timer callback */
typedef void (*tuya_ble_timer_handler_t)(void*);

Sample application

After power-on, the indicator comes on for two seconds and then goes off. When the button is pressed, the indicator comes on for five seconds and then goes off.

#include "tuya_ble_port.h"
#include "ty_pin.h"

/* Pin */
#define KEY_PIN             GPIO_PA0    /* KEY */
#define LED_PIN             GPIO_PD7    /* LED */
/* The timer period */
#define KEY_SCAN_TIME_MS    10          /* 10ms */
#define LED_TURN_TIME_MS_1  2000        /* 2s */
#define LED_TURN_TIME_MS_2  5000        /* 5s */

/* The deletion flag of a timer */
uint8_t delete_flag = 0;
/* Timer ID */
tuya_ble_timer_t key_timer;
tuya_ble_timer_t led_timer;

/**
 * @brief key_timer Timeout handler
 * @param None
 * @return None
 */
void key_timer_cb(void)
{
    /* Read the voltage level on the button pin */
    ty_pin_level_t pin_level;
    ty_pin_get(KEY_PIN, &pin_level);
    if (TY_PIN_LOW == pin_level) {
        /* Turn on the LED and restart the led_timer that expires after 5s. */
        ty_pin_set(LED_PIN, TY_PIN_HIGH);
        tuya_ble_timer_restart(led_timer, LED_TURN_TIME_MS_2);
        /* Stop the key_timer and set the key_timer to be deletable. */
        tuya_ble_timer_stop(key_timer);
        delete_flag = 1;
    }
}

/**
 * @brief led_timer Timeout handler
 * @param None
 * @return None
 */
void led_timer_cb(void)
{
    /* Turn off the LED */
    ty_pin_set(LED_PIN, TY_PIN_LOW);
    if (delete_flag) {
        /* Delete the key_timer */
        tuya_ble_timer_delete(key_timer);
    } else {
        /* Start the key_timer */
        tuya_ble_timer_start(key_timer);
    }
}

/**
 * @brief Initialize functions
 * @param None
 * @return None
 */
void app_init(void)
{
    /* Initialize the button and LED pin and turn on the LED. */
    ty_pin_init(KEY_PIN, TY_PIN_MODE_IN_PU);
    ty_pin_init(LED_PIN, TY_PIN_MODE_OUT_PP_LOW);
    ty_pin_set(LED_PIN, TY_PIN_HIGH);
    /* Create a recurring key_timer that expires after 10 ms. Register the timeout handler key_timer_cb. */
    tuya_ble_timer_create(&key_timer, KEY_SCAN_TIME_MS, TUYA_BLE_TIMER_REPEATED, (tuya_ble_timer_handler_t)key_timer_cb);
    /* Create and start a one-time led_timer that expires after 2s. Register the timeout handler led_timer_cb. */
    tuya_ble_timer_create(&led_timer, LED_TURN_TIME_MS_1, TUYA_BLE_TIMER_SINGLE_SHOT, (tuya_ble_timer_handler_t)led_timer_cb);
    tuya_ble_timer_start(led_timer);
}

Report DP status

API list

Function name Description
tuya_ble_dp_data_send Send data point (DP) data to the cloud.

API description

API file: tuya_ble_api.h

/**
 * @brief Send DP data.
 * @param[in] sn: The sequence number of DP data transmission, which is defined and managed by the application and incremented by 1 with each operation.
 * @param[in] type: The transmission type.
 * @param[in] mode: The transmission mode.
 * @param[in] ack: The acknowledgement flag.
 * @param[in] p_dp_data: DP data.
 * @param[in] dp_data_len: The total length of data, which cannot exceed TUYA_BLE_SEND_MAX_DATA_LEN-7.
 * @return The operation result
 */
tuya_ble_status_t tuya_ble_dp_data_send(uint32_t sn,
                                        tuya_ble_dp_data_send_type_t type,
                                        tuya_ble_dp_data_send_mode_t mode,
                                        tuya_ble_dp_data_send_ack_t ack,
                                        uint8_t *p_dp_data,
                                        uint32_t dp_data_len);

Data type

/* The transmission type */
typedef enum {
    DP_SEND_TYPE_ACTIVE = 0,        /* The device proactively sends DP data. */
    DP_SEND_TYPE_PASSIVE,           /* The device responds to DP data queries from the app. */
} tuya_ble_dp_data_send_type_t;

/* The transmission mode */
typedef enum {
    DP_SEND_FOR_CLOUD_PANEL = 0,    /* The app sends the received data to the cloud and the control panel for data updating. */
    DP_SEND_FOR_CLOUD,              /* The app sends the received data to the cloud only. */
    DP_SEND_FOR_PANEL,              /* The app sends the received data to the control panel for data updating only. */
    DP_SEND_FOR_NONE,               /* The app sends data neither to the cloud nor to the control panel. */
} tuya_ble_dp_data_send_mode_t;

/* The response mode */
typedef enum {
    DP_SEND_WITH_RESPONSE = 0,      /* A response from the app is required. */
    DP_SEND_WITHOUT_RESPONSE,       /* A response from the app is not required. */
} tuya_ble_dp_data_send_ack_t;

Description

The DP is used to represent device functions defined on the Tuya Developer Platform. The DP model abstracts away the data that a device generates. It consists of DP ID, DP data type, DP data length, and DP data. For more information, see Custom Functions.

The Bluetooth SDK respects the following DP data format.

Field Length (byte) Description
dp_id 1 The ID of a DP.
dp_type 1 The data type of a DP.
dp_len 2 The data length of a DP.
dp_data dp_len The DP data.

The data type and length respect the following rules.

dp_type Identifier Value dp_len
Raw DT_RAW 0 1 to 255
Boolean DT_BOOL 1 1
Value DT_VALUE 2 4
String DT_STRING 3 0 to 255
Enum DT_ENUM 4 1

The maximum length of a DP is specified when you define a DP on the Tuya Developer Platform. If dp_type is 0 or 3, dp_len can be customized but must not exceed the maximum length defined on the Tuya Developer Platform.

The data that p_dp_data of tuya_ble_dp_data_send points to must be concatenated in the following format.

Data point (DP) Data of the first DP Data of the second DP
Byte 0 1 2 to 3 4 to n-1 n n+1 n+2 to n+3 n+4 to m-1
Field dp1_id dp1_type dp1_len dp1_data dp2_id dp2_type dp2_len dp2_data

n-1 = (4 + dp1_len) − 1. m-1 = n + (4 + dp2_len) – 1.

You can send the data of multiple DPs at a time. Make sure the total length does not exceed TUYA_BLE_SEND_MAX_DATA_LEN-7. The parameter TUYA_BLE_SEND_MAX_DATA_LEN is configurable.

Sample application

When the temperature is updated, the device reports the temperature data. If Bluetooth is connected, it reports data of all DPs.

/* DP ID */
#define DP_ID_TEMP_CURRENT          1	/* Temperature */
#define DP_ID_TEMP_ALARM            14	/* Temperature alert */

/* DP data type. You can use the definition in the `tuya_ble_mutli_tsf_protocol.h` file. */
#define DT_RAW                      0	/* Raw */
#define DT_BOOL                     1	/* Boolean */
#define DT_VALUE                    2	/* Value */
#define DT_STRING                   3	/* String */
#define DT_ENUM                     4	/* Enum */

/* The field offset of DP model */
#define DP_DATA_INDEX_OFFSET_ID     0	/* dp_id */
#define DP_DATA_INDEX_OFFSET_TYPE   1	/* dp_type */
#define DP_DATA_INDEX_OFFSET_LEN_H  2	/* dp_len */
#define DP_DATA_INDEX_OFFSET_LEN_L  3	/* dp_len */
#define DP_DATA_INDEX_OFFSET_DATA   4	/* dp_data */

/* DP data variable */
static int32_t sg_cur_temp = 0, sg_prv_temp = 0;
static uint8_t sg_temp_alarm = 0;

/* The DP array assembled for reporting */
static uint8_t sg_repo_array[255+4];

/* The sequence number of data transmission */
static uint32_t sg_sn = 0;

/**
 * @brief Report data of a DP.
 * @param[in] dp_id: DP ID
 * @param[in] dp_type: DP data type.
 * @param[in] dp_len: DP data length.
 * @param[in] dp_data: DP data
 * @return None
 */
static void __report_one_dp_data(const uint8_t dp_id, const uint8_t dp_type, const uint16_t dp_len, const uint8_t *dp_data)
{
    uint16_t i;
    /* Save DP data to an array. */
    sg_repo_array[DP_DATA_INDEX_OFFSET_ID] = dp_id;
    sg_repo_array[DP_DATA_INDEX_OFFSET_TYPE] = dp_type;
    sg_repo_array[DP_DATA_INDEX_OFFSET_LEN_H] = (uint8_t)(dp_len >> 8);
    sg_repo_array[DP_DATA_INDEX_OFFSET_LEN_L] = (uint8_t)dp_len;
    for (i = 0; i < dp_len; i++) {
        sg_repo_array[DP_DATA_INDEX_OFFSET_DATA + i] = *(dp_data + (dp_len-i-1));
    }
    /* Call an API to send DP data. */
    tuya_ble_dp_data_send(sg_sn++, DP_SEND_TYPE_ACTIVE, DP_SEND_FOR_CLOUD_PANEL, DP_SEND_WITHOUT_RESPONSE, sg_repo_array, dp_len + DP_DATA_INDEX_OFFSET_DATA);
}

/**
 * @brief Report temperature data.
 * @param None
 * @return None
 */
void app_repo_dp_temp(void)
{
    if (sg_cur_temp != sg_prv_temp)) {
        __report_one_dp_data(DP_ID_TEMP_CURRENT, DT_VALUE, 4, (uint8_t *)&sg_cur_temp);
        sg_prv_temp = sg_cur_temp;
    }
}

/**
 * @brief Add DP data.
 * @param[in] dp_id: DP ID
 * @param[in] dp_type: DP data type.
 * @param[in] dp_len: DP data length.
 * @param[in] dp_data: DP data
 * @param[in] addr: The start address of DP data storage.
 * @return The total length of stored data.
 */
static uint8_t __add_one_dp_data(const uint8_t dp_id, const uint8_t dp_type, const uint16_t dp_len, const uint8_t *dp_data, uint8_t *addr)
{
    uint16_t i;
    *(addr + DP_DATA_INDEX_OFFSET_ID) = dp_id;
    *(addr + DP_DATA_INDEX_OFFSET_TYPE) = dp_type;
    *(addr + DP_DATA_INDEX_OFFSET_LEN_H) = (UCHAR_T)(dp_len >> 8);
    *(addr + DP_DATA_INDEX_OFFSET_LEN_L) = (UCHAR_T)dp_len;
    for (i = 0; i < dp_len; i++) {
        *(addr + DP_DATA_INDEX_OFFSET_DATA + i) = *(dp_data + (dp_len-i-1));
    }
    return (dp_len + DP_DATA_INDEX_OFFSET_DATA);
}

/**
 * @brief Report all DP data.
 * @param None
 * @return None
 */
void app_repo_dp_all(void)
{
    uint32_t total_len = 0;
    /* Add all data to DP array assembled for reporting. */
    total_len += __add_one_dp_data(DP_ID_TEMP_CURRENT, DT_VALUE, 4, &sg_cur_temp, sg_repo_array);
    total_len += __add_one_dp_data(DP_ID_TEMP_ALARM, DT_ENUM, 1, &sg_temp_alarm, sg_repo_array+total_len);
    /* Call an API to send DP data. */
    tuya_ble_dp_data_send(sg_sn++, DP_SEND_TYPE_ACTIVE, DP_SEND_FOR_CLOUD_PANEL, DP_SEND_WITHOUT_RESPONSE, sg_repo_array, total_len);
}

Device status

API list

Function name Description
tuya_ble_connect_status_get Query Bluetooth connection status.
tuya_ble_device_unbind Unbind a device locally, without clearing the virtual device ID.
tuya_ble_device_factory_reset Reset a device and clear the virtual device ID.

The virtual device ID manages the historical data in the cloud.

API description

API file: tuya_ble_api.h

/**
 * @brief Query Bluetooth connection status.
 * @param None
 * @return Bluetooth connection status.
 */
tuya_ble_connect_status_t tuya_ble_connect_status_get(void);

/**
 * @brief Unbind a device locally.
 * @param None
 * @return The operation result
 */
tuya_ble_status_t tuya_ble_device_unbind(void);

/**
 * @brief Reset a device.
 * @param None
 * @return The operation result
 */
tuya_ble_status_t tuya_ble_device_factory_reset(void);

Data type

/* Bluetooth connection status */
typedef enum {
    UNBONDING_UNCONN = 0,   /* The device is unbound and not connected. */
    UNBONDING_CONN,         /* The device is unbound and connected. */
    BONDING_UNCONN,         /* The device is bound and not connected. */
    BONDING_CONN,           /* The device is bound and connected. */
    BONDING_UNAUTH_CONN,    /* The device is bound, connected, and unauthenticated. */
    UNBONDING_UNAUTH_CONN,  /* The device is unbound, connected, and unauthenticated. */
    UNKNOW_STATUS           /* Unknown status */
} tuya_ble_connect_status_t;

The following table lists the possible Bluetooth connection status.

Bluetooth connection status Description
Unbound and not connected A device is neither registered with the cloud nor connected over Bluetooth. If the device is advertising, it is ready for pairing.
Unbound and connected An unbound device is being connected over Bluetooth.
Bound and not connected A Bluetooth device is offline. The device is bound with but not connected to the app.
Bound and connected A Bluetooth device is online. The device establishes a secure connection with the app through the Tuya-specific Bluetooth protocol.
Bound, connected, and unauthenticated This status occurs when a device is being paired or reconnected, indicating a Bluetooth connection is just made.
Unbound, connected, and unauthenticated A device is connected over Bluetooth, but is not discoverable currently.

Sample application

When the application requests to pair again, it queries Bluetooth connection status first. If the device has been bound, the application calls the API to request unbinding. When the application requests to reset the device, it calls the API for a reset.

/**
 * @brief Unbind a device.
 * @param None
 * @return None
 */
void app_unbind(void)
{
    tuya_ble_connect_status_t ble_conn_sta = tuya_ble_connect_status_get();
    if ((ble_conn_sta == BONDING_UNCONN) ||
        (ble_conn_sta == BONDING_CONN)   ||
        (ble_conn_sta == BONDING_UNAUTH_CONN)) {
        tuya_ble_device_unbind();
    }
}

/**
 * @brief Reset a device.
 * @param None
 * @return None
 */
void app_reset(void)
{
    tuya_ble_device_factory_reset();
}

Callback

tuya_ble_sdk_callback is in tuya_ble_sdk_demo.c. This is a message callback registered on initialization, which the Bluetooth SDK uses to send messages such as status and data to the device application. Add the code to process under the corresponding case statement.

The following table lists the common events. For more information, see API Reference.

Event Description
TUYA_BLE_CB_EVT_CONNECTE_STATUS The Bluetooth SDK sends this event to the device application on changes in Bluetooth status.
TUYA_BLE_CB_EVT_DP_DATA_RECEIVED The Bluetooth SDK receives DP data from the mobile app.
TUYA_BLE_CB_EVT_DP_QUERY The Bluetooth SDK receives an array of queried DP IDs from the mobile app.
TUYA_BLE_CB_EVT_OTA_DATA The Bluetooth SDK receives OTA firmware updates from the mobile app.
TUYA_BLE_CB_EVT_TIME_STAMP The Bluetooth SDK receives the timestamp in string format from the mobile app.
TUYA_BLE_CB_EVT_TIME_NORMAL The Bluetooth SDK receives time data in general format from the mobile app.
TUYA_BLE_CB_EVT_UNBOUND The Bluetooth SDK receives an unbinding command from the mobile app.
TUYA_BLE_CB_EVT_ANOMALY_UNBOUND The Bluetooth SDK receives an offline removing command from the mobile app.

Demo introduction

Demo design

Function Definition

This demo implements the following features:

Feature Description
Data acquisition The sensor collects the temperature and humidity data every second.
When the temperature and humidity change, the sensor reports the current data to the mobile app for updating the control panel display.
Threshold alerts Set upper and lower limits on temperature and humidity by using the app.
If one of the metrics goes above or below that limit, the sensor will send a notification to the mobile app.
Pairing indication - The indicator blinks when the device is not bound.
- The indicator comes off when the device is bound.
- The indicator blinks when the device is unbound.

Add the standard functions shown in the following figure to your product to implement the above-listed features.

Application Development

Note that if you modify the definition of a DP or an error occurs on the control panel display, unbinding and binding the device again is recommended.

File structure

The Bluetooth SDK comes with a basic file structure. The application code is stored in tuya_ble_sdk_demo\app. The component code is stored in tuya_ble_sdk_demo\components.

The demo directory is shown below.

tuya_ble_sdk_demo
├── app           /* Application */
|    ├── include  /* Header directory, having the same file name as the src directory  */
|    └── src      /* Source directory */
|         ├── tuya_ble_bulk_data_demo.h    /* The routine for bulk transfer */
|         ├── tuya_ble_product_test_demo.h /* The routine for end product test */
|         ├── tuya_ble_sdk_demo.h          /* Implements tuya_ble_sdk initialization. The entry point to application. */
|         ├── tuya_ble_sdk_test.h          /* Implements the serial commands for tuya_ble_sdk testing */
|         └── tuya_ble_sensor_rht_demo.h   /* The sample of temperature and humidity sensor application */
├── board
├── components    /* Component */
|    ├── external
|    ├── ty_oled
|    └── ty_sht3x /* SHT3x driver component */
|         ├── ty_sht3x.c
|         └── ty_sht3x.h
├── doc
├── tools
└── tuya_ble_sdk

Create src and include folders in app. Copy the sample file tuya_ble_sdk_demo.c to the folder by kind. Create tuya_ble_sensor_rht_demo.c and tuya_ble_sensor_rht_demo.h in src and include to write the sample code of the temperature and humidity sensor application. Create ty_sht3x folder in components and then create ty_sht3x.c and ty_sht3x.h files to write SHT3x driver code.

Configure project

Since we add some include directories in the above step, we need to add their path in the project property. Otherwise, the compilation will fail. Add the directory path as instructed below and verify the compilation.

Application Development

Application Development

Pin definition

No. Symbol I/O type Usage Notes
1 D3 I/O - -
2 D7 I/O LED control LED indicator on the microcontroller board. It comes on at high level.
3 C0 I/O I2C bus – SDA line Corresponds to SDA on the temperature and humidity board.
4 SWS I/O SWS Used to flash firmware using Telink programmer.
5 B6 I - -
6 A0 I/O - -
7 A1 I/O - -
8 C2 I/O - -
9 C3 I/O Log printing Used to debug serial communication.
10 D2 I/O - -
11 B4 I/O - -
12 B5 I/O - -
13 GND P GND The ground pin.
14 VCC P VCC 3.3V
15 B1 I/O TX pin for serial communication. Used to flash firmware over the serial port.
16 B7 I/O TX pin for serial communication. Used to flash firmware over the serial port.
17 C4 I/O - -
18 RST I/O RST Reset button on the microcontroller board.
19 C1 I/O I2C bus – SCL line Corresponds to SCL on the temperature and humidity board.
20 D4 I/O - -
21 NC I/O - -

The following code snippet shows where to modify these pins. You can find the configuration function by macro name and edit the pin function.

/* tuya_ble_sensor_rht_demo.c */
#define LED_PIN                 GPIO_PD7

/* ty_i2c_tlsr825x.c */
#define I2C_PIN_SDA             GPIO_PC0
#define I2C_PIN_SCL             GPIO_PC1

/* ty_uart_tlsr825x.c */
#define TLSR_UART_GPIO_TX       UART_TX_PB1
#define TLSR_UART_GPIO_RX       UART_RX_PB7

/* telink_sdk\vendor\8258_module\app_config.h */
#define DEBUG_INFO_TX_PIN       GPIO_PC3
#define PC3_FUNC                AS_GPIO
#define PC3_INPUT_ENABLE        0
#define PC3_OUTPUT_ENABLE       1
#define PC3_DATA_OUT            1

Sensor

This demo uses the Tuya Sandwich temperature and humidity board to collect data. This board has a Sensirion’s SHT30-DIS temperature and humidity sensor, using the I2C protocol. Its peripheral is controlled by ADDR pin (Pin2). When ADDR is connected to GND, the device address is 0x44. When ADDR is connected to VDD, the device address is 0x45.

Download official materials:

Software process

Application Development

Code implementation

Initialization function

tuya_ble_sdk_demo_init

Located in tuya_ble_sdk_demo.c

Add tuya_ble_sensor_rht_init at the end of tuya_ble_sdk_demo_init to initialize the temperature and humidity sensor.

/**
 * @brief The entry point to application initialization.
 * @param None
 * @return None
 */
void tuya_ble_sdk_demo_init(void)
{
    /* ... */
    /* Initialize the temperature and humidity sensor sample application */
    tuya_ble_sensor_rht_init();
}

tuya_ble_sensor_rht_init

Located in tuya_ble_sensor_rht_demo.c

/* The data scale. 1 represents 10 to the 1 power. */
#define TEMP_SCALE          1       /* Temperature data: 10 times the original data  */
#define HUMI_SCALE          1       /* Humidity data: 10 times the original data */

/* Data collection cycle */
#define SHT3X_PERI_TIME_MS  1000    /* 1s / 1Hz */

/* The type of temperature and humidity data */
typedef struct {
    int32_t temp;                   /* The temperature value. */
    int32_t humi;                   /* The humidity value. */
    uint8_t temp_alarm;             /* Temperature threshold to trigger alerts. */
    uint8_t humi_alarm;             /* Humidity threshold to trigger alerts. */
} RHT_DP_DATA_T;

/* Temperature and humidity data */
static RHT_DP_DATA_T sg_rht_data = {
    .temp = 0,                      /* Temperature. Initial value: 0 */
    .humi = 0,                      /* Humidity. Initial value: 0 */
    .temp_alarm = ALARM_CANCEL,     /* Temperature alert. Initial value: Alert canceled. */
    .humi_alarm = ALARM_CANCEL      /* Humidity alert. Initial value: Alert canceled. */
};

/* Timer for temperature and humidify collection */
static tuya_ble_timer_t sg_rht_daq_timer;

/**
 * @brief Initialize temperature and humidity sensor application
 * @param None
 * @return None
 */
void tuya_ble_sensor_rht_init(void)
{
    /* Print notification: Start the temperature and humidity sensor sample application */
    TUYA_APP_LOG_INFO("Sensor RH-T demo start.");
    /* Initialize networking */
    __net_proc_init();
    /* Initialize the SHT3x driver. The ADDR pin is connected to ground. */
    ty_sht3x_init(false);

    /* Measure data once and save the reading to sg_rht_data, with data multiplied by 10 times. */
    if (ty_sht3x_measure_single_shot(&sg_rht_data.temp, &sg_rht_data.humi, TEMP_SCALE, HUMI_SCALE)) {
        /* Print debugging message: Output the temperature and humidity data in integer. */
        TUYA_APP_LOG_DEBUG("Temperature: %d, Humidity: %d", sg_rht_data.temp, sg_rht_data.humi);
    } else {
        /* Print error message: Failed to execute ty_sht3x_measure_single_shot. */
        TUYA_APP_LOG_ERROR("ty_sht3x_measure_single_shot failed.");
    }

    /* Start scheduled measurement of high repetition, with a sampling frequency of 1 Hz. */
    ty_sht3x_start_periodic_measure(REPEATAB_HIGH, FREQ_1HZ);
    /* Create and start a recurring sg_rht_daq_timer that expires after 1s. Register the handler  __rht_daq_timer_handler. */
    tuya_ble_timer_create(&sg_rht_daq_timer, SHT3X_PERI_TIME_MS, TUYA_BLE_TIMER_REPEATED, (tuya_ble_timer_handler_t)__rht_daq_timer_handler);
    tuya_ble_timer_start(sg_rht_daq_timer);
}

__net_proc_init

Located in tuya_ble_sensor_rht_demo.c

/* Pin definition */
#define LED_PIN             GPIO_PD7    /* LED pin */
/* Blinking interval */
#define LED_TIMER_VAL_MS    300         /* 300ms */

/* Timer for LED blinking control */
static tuya_ble_timer_t sg_led_flash_timer;

/* The flag of LED status */
static uint8_t sg_led_status = 0;

/**
 * @brief Initialize networking.
 * @param None
 * @return None
 */
static void __net_proc_init(void)
{
    /* Initialize LED pin (PD7). Push-pull output and low level in the initial stage. */
    ty_pin_init(LED_PIN, TY_PIN_MODE_OUT_PP_LOW);
    /* Create a recurring sg_led_flash_timer that expires after 300 ms. Register the timeout handler __led_flash_timer_cb. */
    tuya_ble_timer_create(&sg_led_flash_timer, LED_TIMER_VAL_MS, TUYA_BLE_TIMER_REPEATED, (tuya_ble_timer_handler_t)__led_flash_timer_cb);
    /* Get and print Bluetooth connection status */
    tuya_ble_connect_status_t ble_conn_sta = tuya_ble_connect_status_get();
    TUYA_APP_LOG_DEBUG("BLE connect status: %d.", ble_conn_sta);
    /* Control the LED behavior according to the Bluetooth connection status. */
    if ((ble_conn_sta == BONDING_UNCONN) ||
        (ble_conn_sta == BONDING_CONN)   ||
        (ble_conn_sta == BONDING_UNAUTH_CONN)) {
        /* If the Bluetooth is bound, the LED comes off. */
        TUYA_APP_LOG_INFO("LED keep off.");         /* Print message: LED steady off */
    } else {
        /* If the Bluetooth is unbound, the LED starts blinking. */
        sg_led_status = 1;                          /* Update the flag of LED status. */
        ty_pin_set(LED_PIN, TY_PIN_HIGH);           /* The LED pin outputs high. */
        tuya_ble_timer_start(sg_led_flash_timer);   /* Start sg_led_flash_timer. */
        TUYA_APP_LOG_INFO("LED start falshing.");   /* Print message: LED starts blinking */
    }
}

ty_pin_init

The GPIO driver for TLSR825x platform, located in ty_pin_tlsr825x.c.

You can port the code in this demo to your project or directly call the API from the chip vendor to drive pins.

/**
 * @brief Initialize pins
 * @param[in] pin: The pin number, which can be modified according to the chip platform.
 * @param[in] mode: The pin mode.
 * @return none
 */
uint32_t ty_pin_init(uint16_t pin, ty_pin_mode_t mode)
{
    /* Set the pin function. */
    gpio_set_func(pin, AS_GPIO);
    /* Set the pin output direction. */
    if ((mode & TY_PIN_INOUT_MASK) <= TY_PIN_IN_IRQ) {
        gpio_set_input_en(pin, 1);
        gpio_set_output_en(pin, 0);
    } else {
        gpio_set_input_en(pin, 0);
        gpio_set_output_en(pin, 1);
    }
    /* Set the pin mode and initial level. */
    switch (mode) {
    case TY_PIN_MODE_IN_PU:
        gpio_setup_up_down_resistor(pin, PM_PIN_PULLUP_10K);
        break;
    case TY_PIN_MODE_IN_PD:
        gpio_setup_up_down_resistor(pin, PM_PIN_PULLDOWN_100K);
        break;
    case TY_PIN_MODE_IN_FL:
        gpio_setup_up_down_resistor(pin, PM_PIN_UP_DOWN_FLOAT);
        break;
    case TY_PIN_MODE_OUT_PP_LOW:
        gpio_write(pin, 0);
        break;
    case TY_PIN_MODE_OUT_PP_HIGH:
        gpio_write(pin, 1);
        break;
    default:
    	break;
    }
    /* Set the wake-up pin. */
#if (GPIO_WAKEUP_MODULE_POLARITY == 1)
    cpu_set_gpio_wakeup (WAKEUP_MODULE_GPIO, Level_High, 1);
    GPIO_WAKEUP_MODULE_LOW;
#else
    cpu_set_gpio_wakeup (WAKEUP_MODULE_GPIO, Level_Low, 1);
    GPIO_WAKEUP_MODULE_HIGH;
#endif
    return 0;
}

ty_sht3x_init

Located in ty_sht3x.c

/* The address of the I2C device. */
#define SHT3X_DEV_ADDR_A    0x44    /* The ADDR pin (pin2) is connected to logic low. */
#define SHT3X_DEV_ADDR_B    0x45    /* The ADDR pin (pin2) is connected to logic high. */

/* The address of the I2C device. The ADDR pin is connected to ground by default. */
uint8_t g_dev_addr = SHT3X_DEV_ADDR_A;

/**
 * @brief Initialize the SHT3x driver.
 * @param[in] addr_pin_high: Specify whether the ADDR pin is connected high.
 * @return None
 */
void ty_sht3x_init(bool addr_pin_high)
{
    /* Initialize I2C */
    ty_i2c_init();
    /* Set the address of the peripheral device. */
    if (addr_pin_high) {
        g_dev_addr = SHT3X_DEV_ADDR_B;
    }
}

ty_i2c_init

The I2C driver for TLSR825x platform, located in ty_i2c_tlsr825x.c.

To use the I2C driver, change the value of USE_SOFT_I2C in ty_i2c.h to 1.

/* I2C pin definition. You can modify the pin number according to your hardware design. */
#define I2C_PIN_SDA     GPIO_PC0
#define I2C_PIN_SCL     GPIO_PC1

/**
 * @brief Initialize the I2C pin.
 * @param None
 * @return None
 */
void i2c_soft_gpio_init(void)
{
    gpio_set_func(I2C_PIN_SDA, AS_GPIO);
    gpio_set_func(I2C_PIN_SCL, AS_GPIO);
    gpio_set_output_en(I2C_PIN_SDA, 1);
    gpio_set_input_en(I2C_PIN_SDA, 0);
    gpio_set_output_en(I2C_PIN_SCL, 1);
    gpio_set_input_en(I2C_PIN_SCL, 0);
}

/**
 * @brief Initialize I2C.
 * @param None
 * @return The operation result
 */
uint32_t ty_i2c_init(void)
{
#if (USE_SOFT_I2C == 0)
    /* ... */
#else
    /* Initialize the I2C pin. */
    i2c_soft_gpio_init();
#endif
    return 0;
}

Timeout processing function

__led_flash_timer_cb

Located in tuya_ble_sensor_rht_demo.c

/**
 * @brief The timeout handler for LED on/off control.
 * @param None
 * @return None
 */
static void __led_flash_timer_cb(void)
{
    /* LED on/off control */
    sg_led_status = !sg_led_status;
    /* Control the pin output level according to LED status */
    if (sg_led_status) {
        ty_pin_set(LED_PIN, TY_PIN_HIGH);
    } else {
        ty_pin_set(LED_PIN, TY_PIN_LOW);
    }
}

__rht_daq_timer_handler

Located in tuya_ble_sensor_rht_demo.c

/* Alert code */
#define ALARM_LOWER     0   /* Trigger an alert when the lower limit is reached. */
#define ALARM_UPPER     1   /* Trigger an alert when the upper limit is reached. */
#define ALARM_CANCEL    2   /* Alert is canceled. */

/* The data type of the threshold value */
typedef struct {
    int32_t temp_max;       /* The upper limit of temperature */
    int32_t temp_min;       /* The lower limit of temperature */
    int32_t humi_max;       /* The upper limit of humidity */
    int32_t humi_min;       /* The lower limit of humidity */
} ALARM_THR_T;

/* Alert threshold */
static ALARM_THR_T sg_alarm_thr = {
    .temp_max = 400,        /* The upper limit of temperature. Initial value: 40°C */
    .temp_min = 0,          /* The lower limit of temperature. Initial value: 0°C */
    .humi_max = 700,        /* The upper limit of humidity. Initial value: 70% */
    .humi_min = 300         /* The lower limit of humidity. Initial value: 30% */
};

/**
 * @brief Check if the temperature exceeds the alert threshold.
 * @param[in] cur_temp: The current temperature.
 * @return Alert code.
 */
static uint8_t __check_temp_val(int32_t cur_temp)
{
    uint8_t res;
    if (cur_temp < sg_alarm_thr.temp_min) {
        res = ALARM_LOWER;
    } else if (cur_temp > sg_alarm_thr.temp_max) {
        res = ALARM_UPPER;
    } else {
        res = ALARM_CANCEL;
    }
    return res;
}

/**
 * @brief Check if the humidity exceeds the alert threshold.
 * @param[in] cur_humi: The current humidity.
 * @return Alert code.
 */
static uint8_t __check_humi_val(int32_t cur_humi)
{
    uint8_t res;
    if (cur_humi < sg_alarm_thr.humi_min) {
        res = ALARM_LOWER;
    } else if (cur_humi > sg_alarm_thr.humi_max) {
        res = ALARM_UPPER;
    } else {
        res = ALARM_CANCEL;
    }
    return res;
}

/**
 * @brief The timeout handler for temperature and humidity collection.
 * @param None
 * @return None
 */
static void __rht_daq_timer_handler(void)
{
    int32_t temp, humi;
    /* Read the temperature and humidity and save the reading to temp and humi, with data multiplied by 10 times. */
    if (ty_sht3x_read_data(&temp, &humi, TEMP_SCALE, HUMI_SCALE)) {
    	/* Print when data is read. */
        TUYA_APP_LOG_DEBUG("Temperature: %d, Humidity: %d", temp, humi);
        /* Archive data to sg_rht_data */
        sg_rht_data.temp = temp;
        sg_rht_data.humi = humi;
        /* Check if data exceeds the alert threshold. */
        sg_rht_data.temp_alarm = __check_temp_val(temp);
        sg_rht_data.humi_alarm = __check_humi_val(humi);
        /* If Bluetooth is connected, the device reports data. */
        if (BONDING_CONN == tuya_ble_connect_status_get()) {
            __repo_dp_data_all();
        }
    } else {
    	/* Print error message: Failed to execute ty_sht3x_read_data. */
        TUYA_APP_LOG_ERROR("ty_sht3x_read_data failed.");
    }
}

Report DP status

__repo_dp_data_all

Located in tuya_ble_sensor_rht_demo.c

/* DP ID (The ID of a DP defined on the Tuya Developer Platform) */
#define DP_ID_TEMP_CURRENT          1
#define DP_ID_HUMIDITY_VALUE        2
#define DP_ID_TEMP_ALARM            14
#define DP_ID_HUM_ALARM             15

/**
 * @brief Report all DP data that is updated.
 * @param None
 * @return None
 */
static void __repo_dp_data_all(void)
{
    /* Before definition, return the datastore variables. */
    static RHT_DP_DATA_T s_prv_val = {
        .temp = 0,
        .humi = 0,
        .temp_alarm = ALARM_CANCEL,
        .humi_alarm = ALARM_CANCEL
    };
    /* Report temperature data. */
    if (sg_rht_data.temp != s_prv_val.temp) {
        __report_one_dp_data(DP_ID_TEMP_CURRENT, DT_VALUE, 4, (uint8_t *)&sg_rht_data.temp);
        s_prv_val.temp = sg_rht_data.temp;
    }
    /* Report humidity data. */
    if (sg_rht_data.humi != s_prv_val.humi) {
        __report_one_dp_data(DP_ID_HUMIDITY_VALUE, DT_VALUE, 4, (uint8_t *)&sg_rht_data.humi);
        s_prv_val.humi = sg_rht_data.humi;
    }
    /* Report temperature alerts. */
    if (sg_rht_data.temp_alarm != s_prv_val.temp_alarm) {
        __report_one_dp_data(DP_ID_TEMP_ALARM, DT_ENUM, 1, (uint8_t *)&sg_rht_data.temp_alarm);
        s_prv_val.temp_alarm = sg_rht_data.temp_alarm;
    }
    /* Report humidity alerts. */
    if (sg_rht_data.humi_alarm != s_prv_val.humi_alarm) {
        __report_one_dp_data(DP_ID_HUM_ALARM, DT_ENUM, 1, (uint8_t *)&sg_rht_data.humi_alarm);
        s_prv_val.humi_alarm = sg_rht_data.humi_alarm;
    }
}

__report_one_dp_data

Located in tuya_ble_sensor_rht_demo.c

/* The field offset of DP model */
#define DP_DATA_INDEX_OFFSET_ID     0   /* dp_id */
#define DP_DATA_INDEX_OFFSET_TYPE   1   /* dp_type */
#define DP_DATA_INDEX_OFFSET_LEN_H  2   /* dp_len high byte */
#define DP_DATA_INDEX_OFFSET_LEN_L  3   /* dp_len low byte */
#define DP_DATA_INDEX_OFFSET_DATA   4   /* dp_data */

/* Report the size of the array. */
#define REPO_ARRAY_SIZE             8

/* Report the datastore array. */
static uint8_t sg_repo_array[REPO_ARRAY_SIZE];

/* The sequence number of data transmission. Initial value: 0 */
static uint32_t sg_sn = 0;

/**
 * @brief Report data of a DP.
 * @param[in] dp_id: DP ID
 * @param[in] dp_type: DP data type.
 * @param[in] dp_len: DP data length.
 * @param[in] dp_data: DP data.
 * @return None
 */
static void __report_one_dp_data(const uint8_t dp_id, const uint8_t dp_type, const uint16_t dp_len, const uint8_t *dp_data)
{
    uint16_t i;
    /* Save DP data to an array. */
    sg_repo_array[DP_DATA_INDEX_OFFSET_ID] = dp_id;
    sg_repo_array[DP_DATA_INDEX_OFFSET_TYPE] = dp_type;
    sg_repo_array[DP_DATA_INDEX_OFFSET_LEN_H] = (uint8_t)(dp_len >> 8);
    sg_repo_array[DP_DATA_INDEX_OFFSET_LEN_L] = (uint8_t)dp_len;
    for (i = 0; i < dp_len; i++) {
        sg_repo_array[DP_DATA_INDEX_OFFSET_DATA + i] = *(dp_data + (dp_len-i-1));
    }
    /* Call an API to send DP data. */
    tuya_ble_dp_data_send(sg_sn++, DP_SEND_TYPE_ACTIVE, DP_SEND_FOR_CLOUD_PANEL, DP_SEND_WITHOUT_RESPONSE, sg_repo_array, dp_len + DP_DATA_INDEX_OFFSET_DATA);
}

Callback

tuya_ble_sdk_callback

Located in tuya_ble_sdk_demo.c

This demo uses the following events.

/**
 * @brief tuya_ble_sdk callback
 * @param[in] event: Event (Message)
 * @return None
 */
static void tuya_ble_sdk_callback(tuya_ble_cb_evt_param_t* event)
{
    switch (event->evt) {
        /* Bluetooth connection status changes */
        case TUYA_BLE_CB_EVT_CONNECTE_STATUS: {
            /* When Bluetooth connection status changes to bound and connected  */
            if (event->connect_status == BONDING_CONN) {
                TUYA_APP_LOG_INFO("bonding and connecting");
                tuya_ble_update_conn_param_timer_start();
                /* Bluetooth connection processing */
                tuya_net_proc_ble_conn();
            }
        } break;
        /* Receive DP data from the mobile app. */
        case TUYA_BLE_CB_EVT_DP_DATA_RECEIVED: {
            /* Pass in the DP data and the total length of the DP data. */
            tuya_net_proc_dp_recv(event->dp_received_data.p_data, event->dp_received_data.data_len);
        } break;
        /* Receive an unbinding command from the mobile app. */
        case TUYA_BLE_CB_EVT_UNBOUND: {
            /* Unbind Bluetooth connection. */
            tuya_net_proc_ble_unbound();
            TUYA_APP_LOG_INFO("TUYA_BLE_CB_EVT_UNBOUND");
        } break;
        /* Receive an offline unbinding command from the mobile app. */
        case TUYA_BLE_CB_EVT_ANOMALY_UNBOUND: {
            /* Unbind Bluetooth connection. */
            tuya_net_proc_ble_unbound();
            TUYA_APP_LOG_INFO("TUYA_BLE_CB_EVT_ANOMALY_UNBOUND");
        } break;
        /* Default */
        default: {
            TUYA_APP_LOG_INFO("tuya_ble_sdk_callback unknown event type 0x%04x", event->evt);
        } break;
    }
}

tuya_net_proc_ble_conn

Located in tuya_ble_sensor_rht_demo.c

/**
 * @brief Bluetooth connection handler.
 * @param None
 * @return None
 */
void tuya_net_proc_ble_conn(void)
{
    sg_led_status = 0;                      /* Update the flag of LED status. */
    ty_pin_set(LED_PIN, TY_PIN_LOW);        /* The LED pin outputs low. */
    tuya_ble_timer_stop(sg_led_flash_timer);/* Stop sg_led_flash_timer */
    TUYA_APP_LOG_INFO("LED stop falshing.");/* Print message: LED stops blinking */
}

tuya_net_proc_dp_recv

Located in tuya_ble_sensor_rht_demo.c

/* DP ID (The ID of a DP defined on the Tuya Developer Platform) */
#define DP_ID_MAXTEMP_SET           10
#define DP_ID_MINITEMP_SET          11
#define DP_ID_MAXHUM_SET            12
#define DP_ID_MINIHUM_SET           13

/**
 * @brief DP data reception handler
 * @param[in] dp_data: DP data
 * @param[in] dp_len: The total length of DP data.
 * @return None
 */
void tuya_net_proc_dp_recv(uint8_t *dp_data, uint16_t dp_len)
{
    int32_t val = 0;
    /* Check if the length of DP data is 4. */
    if (dp_len - 4 == 4) {
        /* Concatenate data and print debugging message. */
        val = dp_data[4] << 24 | dp_data[5] << 16 | dp_data[6] << 8 | dp_data[7];
        TUYA_APP_LOG_DEBUG("val: %x", val);
    }
    /* Process data by DP_ID. */
    switch (dp_data[0]) {
    /* The upper limit of temperature: Store and report data and print notification. */
    case DP_ID_MAXTEMP_SET:
        sg_alarm_thr.temp_max = val;
        __report_one_dp_data(DP_ID_MAXTEMP_SET, DT_VALUE, 4, (uint8_t *)&val);
        TUYA_APP_LOG_INFO("Set the maximum temperature to %d.", sg_alarm_thr.temp_max);
        break;
    /* The lower limit of temperature: Store and report data and print notification. */
    case DP_ID_MINITEMP_SET:
        sg_alarm_thr.temp_min = val;
        __report_one_dp_data(DP_ID_MINITEMP_SET, DT_VALUE, 4, (uint8_t *)&val);
        TUYA_APP_LOG_INFO("Set the minimum temperature to %d.", sg_alarm_thr.temp_min);
        break;
    /* The upper limit of humidity: Store and report data and print notification. */
    case DP_ID_MAXHUM_SET:
        sg_alarm_thr.humi_max = val;
        __report_one_dp_data(DP_ID_MAXHUM_SET, DT_VALUE, 4, (uint8_t *)&val);
        TUYA_APP_LOG_INFO("Set the maximum humidity to %d.", sg_alarm_thr.humi_max);
        break;
    /* The lower limit of humidity: Store and report data and print notification. */
    case DP_ID_MINIHUM_SET:
        sg_alarm_thr.humi_min = val;
        __report_one_dp_data(DP_ID_MINIHUM_SET, DT_VALUE, 4, (uint8_t *)&val);
        TUYA_APP_LOG_INFO("Set the minimum humidity to %d.", sg_alarm_thr.humi_min);
        break;
    /* Default */
    default:
        break;
    }
}

tuya_net_proc_ble_unbound

Located in tuya_ble_sensor_rht_demo.c

/**
 * @brief Bluetooth unbinding handler
 * @param None
 * @return None
 */
void tuya_net_proc_ble_unbound(void)
{
    sg_led_status = 1;                          /* Update the flag of LED status. */
    ty_pin_set(LED_PIN, TY_PIN_HIGH);           /* The LED pin outputs high. */
    tuya_ble_timer_start(sg_led_flash_timer);   /* Start sg_led_flash_timer */
    TUYA_APP_LOG_INFO("LED start falshing.");   /* Print message: LED starts blinking */
}

Sensor driver

ty_sht3x_measure_single_shot

Located in ty_sht3x.c

#define SHT3X_CMD_MEAS_CLOCKSTR_H   0x2C06  /* One-time measurement: Clock stretching and high repetition */

/**
 * @brief Measure data once.
 * @param[out] temp: Temperature value.
 * @param[out] humi: Humidity value.
 * @param[in] temp_scale: Temperature scale.
 * @param[in] humi_scale: Humidity scale.
 * @return The operation result
 */
uint8_t ty_sht3x_measure_single_shot(int32_t *temp, int32_t *humi, uint8_t temp_scale, uint8_t humi_scale)
{
    /* The write command: One-time measurement with the stop signal */
    __sht3x_write_cmd(SHT3X_CMD_MEAS_CLOCKSTR_H, 1);
    /* Delay of 20 ms */
    i2c_delay(20000);
    /* Read data and return operation result. */
    return __sht3x_read_data(temp, humi, temp_scale, humi_scale);
}

ty_sht3x_start_periodic_measure

Located in ty_sht3x.c

#define SHT3X_CMD_MEAS_PERI_05_H    0x2032  /* Measure data 0.5 times (0.5 Hz) every second, with high repetition. */
#define SHT3X_CMD_MEAS_PERI_05_M    0x2024  /* Measure data 0.5 times (0.5 Hz) every second, with medium repetition. */
#define SHT3X_CMD_MEAS_PERI_05_L    0x202F  /* Measure data 0.5 times (0.5 Hz) every second, with low repetition. */
#define SHT3X_CMD_MEAS_PERI_1_H     0x2130  /* Measure data once (1 Hz) every second, with high repetition. */
#define SHT3X_CMD_MEAS_PERI_1_M     0x2126  /* Measure data once (1 Hz) every second, with medium repetition. */
#define SHT3X_CMD_MEAS_PERI_1_L     0x212D  /* Measure data once (1 Hz) every second, with low repetition. */
#define SHT3X_CMD_MEAS_PERI_2_H     0x2236  /* Measure data twice (2 Hz) every second, with high repetition. */
#define SHT3X_CMD_MEAS_PERI_2_M     0x2220  /* Measure data twice (2 Hz) every second, with medium repetition. */
#define SHT3X_CMD_MEAS_PERI_2_L     0x222B  /* Measure data twice (2 Hz) every second, with low repetition. */
#define SHT3X_CMD_MEAS_PERI_4_H     0x2334  /* Measure data 4 times (4 Hz) every second, with high repetition. */
#define SHT3X_CMD_MEAS_PERI_4_M     0x2322  /* Measure data 4 times (4 Hz) every second, with medium repetition. */
#define SHT3X_CMD_MEAS_PERI_4_L     0x2329  /* Measure data 4 times (4 Hz) every second, with low repetition. */
#define SHT3X_CMD_MEAS_PERI_10_H    0x2737  /* Measure data 10 times (10 Hz) every second, with high repetition. */
#define SHT3X_CMD_MEAS_PERI_10_M    0x2721  /* Measure data 10 times (10 Hz) every second, with medium repetition. */
#define SHT3X_CMD_MEAS_PERI_10_L    0x272A  /* Measure data 10 times (10 Hz) every second, with low repetition. */

/**
 * @brief Start scheduled measurement.
 * @param[in] rept: Repetition
 * @param[in] freq: Sampling frequency
 * @return None
 */
void ty_sht3x_start_periodic_measure(SHT3X_REPT_E rept, SHT3X_FREQ_E freq)
{
    switch (rept) {
    /* High repetition */
    case REPEATAB_HIGH:
        switch (freq) {
        case FREQ_HZ5:  /* 0.5Hz */
            __sht3x_write_cmd(SHT3X_CMD_MEAS_PERI_05_H, 0);
            break;
        case FREQ_1HZ:  /* 1Hz */
            __sht3x_write_cmd(SHT3X_CMD_MEAS_PERI_1_H, 0);
            break;
        case FREQ_2HZ:  /* 2Hz */
            __sht3x_write_cmd(SHT3X_CMD_MEAS_PERI_2_H, 0);
            break;
        case FREQ_4HZ:  /* 4Hz */
            __sht3x_write_cmd(SHT3X_CMD_MEAS_PERI_4_H, 0);
            break;
        case FREQ_10HZ: /* 10Hz */
            __sht3x_write_cmd(SHT3X_CMD_MEAS_PERI_10_H, 0);
            break;
        default:
            break;
        }
        break;
    /* Medium repetition */
    case REPEATAB_MEDIUM:
        switch (freq) {
        case FREQ_HZ5:  /* 0.5Hz */
            __sht3x_write_cmd(SHT3X_CMD_MEAS_PERI_05_M, 0);
            break;
        case FREQ_1HZ:  /* 1Hz */
            __sht3x_write_cmd(SHT3X_CMD_MEAS_PERI_1_M, 0);
            break;
        case FREQ_2HZ:  /* 2Hz */
            __sht3x_write_cmd(SHT3X_CMD_MEAS_PERI_2_M, 0);
            break;
        case FREQ_4HZ:  /* 4Hz */
            __sht3x_write_cmd(SHT3X_CMD_MEAS_PERI_4_M, 0);
            break;
        case FREQ_10HZ: /* 10Hz */
            __sht3x_write_cmd(SHT3X_CMD_MEAS_PERI_10_M, 0);
            break;
        default:
            break;
        }
        break;
    /* Low repetition */
    case REPEATAB_LOW:
        switch (freq) {
        case FREQ_HZ5:  /* 0.5Hz */
            __sht3x_write_cmd(SHT3X_CMD_MEAS_PERI_05_L, 0);
            break;
        case FREQ_1HZ:  /* 1Hz */
            __sht3x_write_cmd(SHT3X_CMD_MEAS_PERI_1_L, 0);
            break;
        case FREQ_2HZ:  /* 2Hz */
            __sht3x_write_cmd(SHT3X_CMD_MEAS_PERI_2_L, 0);
            break;
        case FREQ_4HZ:  /* 4Hz */
            __sht3x_write_cmd(SHT3X_CMD_MEAS_PERI_4_L, 0);
            break;
        case FREQ_10HZ: /* 10Hz */
            __sht3x_write_cmd(SHT3X_CMD_MEAS_PERI_10_L, 0);
            break;
        default:
            break;
        }
        break;
    default:
        break;
    }
}

ty_sht3x_read_data

Located in ty_sht3x.c

#define SHT3X_CMD_FETCH_DATA        0xE000  /* Read data (scheduled measurement) */

/**
 * @brief Read data from SHT3x
 * @param[out] temp: Temperature value.
 * @param[out] humi: Humidity value.
 * @param[in] temp_scale: Temperature scale.
 * @param[in] humi_scale: Humidity scale.
 * @return The operation result
 */
uint8_t ty_sht3x_read_data(int32_t *temp, int32_t *humi, uint8_t temp_scale, uint8_t humi_scale)
{
    /* The write command: Read data without the stop signal */
    __sht3x_write_cmd(SHT3X_CMD_FETCH_DATA, 0);
    /* Read data and return operation result. */
    return __sht3x_read_data(temp, humi, temp_scale, humi_scale);
}

__sht3x_write_cmd

Located in ty_sht3x.c

/* The write command bit of I2C*/
#define I2C_CMD_BIT_WRITE           0

/**
 * @brief Write command to SHT3x
 * @param[in] cmd: The command
 * @param[in] stop: Whether to send the stop signal.
 * @return None
 */
static void __sht3x_write_cmd(uint16_t cmd, bool stop)
{
    /* Split command */
    uint8_t cmd_bytes[2];
    cmd_bytes[0] = (uint8_t)(cmd >> 8);
    cmd_bytes[1] = (uint8_t)(cmd & 0x00FF);
    /* I2C transmission */
    i2c_start();
    i2c_send_bytes((g_dev_addr << 1) | I2C_CMD_BIT_WRITE, cmd_bytes, 2);
    if (stop) {
        i2c_stop();
    }
}

__sht3x_read_data

Located in ty_sht3x.c

/* The read command bit of I2C */
#define I2C_CMD_BIT_READ            1

/**
 * @brief Read multiple bytes.
 * @param[out] buffer: Buffer
 * @param[in] len: Data length
 * @return None
 */
static void __sht3x_read_bytes(uint8_t *buffer, uint8_t len)
{
    i2c_start();
    i2c_rcv_bytes((g_dev_addr << 1) | I2C_CMD_BIT_READ, buffer, len);
    i2c_stop();
}

/**
 * @brief Calculate the temperature.
 * @param[in] raw_data: Raw data.
 * @param[in] scale: The scale.
 * @return Temperature value in °C.
 */
static int32_t __sht3x_calc_temp(int16_t raw_data, uint8_t scale)
{
    int32_t gain = 1;
    while(scale--) {
        gain *= 10;
    }
    return (gain * 175 * (int32_t)raw_data / 65535 - gain * 45);
}

/**
 * @brief Calculate the humidity.
 * @param[in] raw_data: Raw data.
 * @param[in] scale: The scale.
 * @return Humidity value in %RH.
 */
static int32_t __sht3x_calc_humi(uint16_t raw_data, uint8_t scale)
{
    int32_t gain = 1;
    while(scale--) {
        gain *= 10;
    }
    return (gain * 100 * (int32_t)raw_data / 65535);
}

/**
 * @brief Read data
 * @param[out] temp: Temperature value.
 * @param[out] humi: Humidity value.
 * @param[in] temp_scale: Temperature scale.
 * @param[in] humi_scale: Humidity scale.
 * @return The operation result
 */
uint8_t __sht3x_read_data(int32_t *temp, int32_t *humi, uint8_t temp_scale, uint8_t humi_scale)
{
    uint8_t buf[6];
    /* Read 6-byte data: temperature (2 bytes), humidity (2 bytes), and checksum (2 bytes) */
    __sht3x_read_bytes(buf, 6);
    /* CRC8 checksum */
    if ((!__sht3x_check_crc(buf, 2, buf[2])) ||
        (!__sht3x_check_crc(buf+3, 2, buf[5]))) {
        return 0;   /* Check failed, with 0 returned. */
    }
    /* The data calculated based on the scale. */
    *temp = __sht3x_calc_temp(((int16_t)buf[0] << 8) | buf[1], temp_scale);
    *humi = __sht3x_calc_humi(((int16_t)buf[3] << 8) | buf[4], humi_scale);
    return 1;       /* Check succeeded, with 1 returned. */
}

Feature demonstration

Device pairing

Flash firmware to your board and reset it. If the indicator is blinking, the device is ready for binding.

Application Development

Pair the device by using the Smart Life app. The indicator comes off on successful binding.

Application Development

If the device has been bound, the indicator does not blink. When you open the app, the device will automatically connect to it. You can unbind the device as instructed below. Then, the indicator will blink.

Application Development

Data update

The app updates the temperature and humidity data in real time.

Threshold alerts

Set upper and lower limits on temperature and humidity by using the app.

Application Development

If one of the metrics goes above or below that limit, the sensor will send a notification to the mobile app.