MCU SDK Porting

Last Updated on : 2024-08-06 09:19:18download

The MCU SDK is automatically generated based on the product features defined on the Tuya Developer Platform. The MCU SDK includes the communication architecture and protocol parsing, allowing you to directly import it to your project to proceed with MCU program development.

Resource requirements

The SDK requirements for the MCU are as follows.

  • Memory: 4 KB

  • RAM: About 100 bytes of RAM are required, depending on the data length of the data point (DP). If you enable OTA updates, it must be greater than 260 bytes.

  • Nested function: 9-level.

    If your hardware is constrained in resources, you can refer to functions in the SDK and port the protocol yourself.

File structure

For more information about downloading the MCU SDK, see Develop Hardware. The MCU SDK consists of the following files:

File
Description
mcu_api.c Contains functions that can be called.
mcu_api.h Contains declarations for functions in mcu_api.c.
protocol.c Contains functions for processing protocol data. You can add code to related functions as needed to get data sent by the Zigbee module to the MCU.
protocol.h protocol.h contains the following information:
  • Parameters that the MCU sends to the Zigbee module for initialization.
  • Macros defined for custom features. You can enable macros as needed.
  • Declarations for functions in protocol.c.
system.c Contains the implementation of parsing the serial protocol.
system.h system.h contains the following information:
  • Definitions of commands.
  • Some global variables.
  • Declarations for functions in system.c.
zigbee.h Contains macro definitions for Zigbee communication.

How to port SDK

  1. Program the MCU and port the SDK.
  2. Check macro definitions in protocol.h.
  3. Port the protocol.c file and functions.
  4. Add the function for DP data communication.
  5. Add the production test feature.
  6. Add the pairing feature.

Program MCU and port SDK

  1. In your original project, initialize the MCU peripherals, including the serial port, external interrupt (button), timer (LED indicator), and more.

  2. Copy the .c and .h files in the MCU SDK folder to your project directory where the .c and .h files are located.

    MCU SDK Porting

    MCU SDK Porting

Check macro definitions in protocol.h

Product information

  1. Define the product ID (PID). PRODUCT_KEY is the macro defined for the PID. A PID is a unique identifier for each product. It can be found on the page of Product Development.

    	#define PRODUCT_KEY "ax23rawjo4np****"
    

    If the PRODUCT_KEY does not match the PID of your Zigbee product created on the Tuya Developer Platform, update it to that PID.

  2. Define the version number. MCU_VER defines the software version, which defaults to 1.0.0. If you enable OTA updates for the MCU firmware, you need to update the MCU version number after the firmware update is installed.

    	#define MCU_VER "1.0.0"
    

(Optional) Set OTA updates

To support OTA firmware updates, define SUPPORT_MCU_FIRM_UPDATE and enable the OTA update feature. This feature is disabled by default.

#define         SUPPORT_MCU_FIRM_UPDATE

For more information about the OTA updates, see OTA Update Guide.

(Optional) Transmit and receive buffer

Modify the buffer size according to the DP definition.

  • The serial transmit and receive buffer size must exceed the maximum length of DP data, with a default size of 24 bytes.

  • If you enable the MCU OTA update feature, the buffer size should be greater than 260 bytes.

  • If RAM is limited, the size of the receive queue can be reduced accordingly.

    	#define ZIGBEE_UART_QUEUE_LMT             256             // The size of receive queues
    	#define ZIGBEE_UART_RECV_BUF_LMT          128             // Serial port receiving buffer
    	#define ZIGBEE_UART_SEND_BUF_LMT          128             // Serial port sending buffer
    

Port protocol.c file and functions

  1. Copy the zigbee.h file to the folder where Zigbee files are stored, such as the main.c folder.

  2. Call the zigbee_protocol_init() function in the mcu_api.c file after the MCU serial port and other peripherals are initialized.

  3. Specify the serial transmission function in uart_transmit_output in the protocol.c file.

    	/**
    	* @brief encapsulates a generic send function, developer should use their own function to completing this fuction
    	* @param[in] {value} send signle data
    	* @return  void
    	*/
    	void uart_transmit_output(unsigned char value);
    
    	UART3_SendByte(value);
    
  4. In the interrupt handler for serial data reception, call uart_receive_input in the mcu_api.c file and pass in the received data as the parameters.

    	/**
    	* @brief copy receive data from uart receive interrupt
    	* @param[in]  {value} Data received from interrupt
    	* @return void
    	*/
    	void uart_receive_input(unsigned char value)
    	{
    	  #error "please call this function in the interrupt function of serial receive, and delete this line"
    
    	    if(1 == queue_out - queue_in) {
    	        //Serial receiving buffer is full
    	    }else if((queue_in > queue_out) && ((queue_in - queue_out) >= sizeof(zigbee_uart_rx_buf))) {
    	        //Serial receiving buffer is full
    	    }else {
    	        //Serial receiving buffer is not full
    	        if(queue_in >= (unsigned char *)(zigbee_uart_rx_buf + sizeof(zigbee_uart_rx_buf))) {
    	            queue_in = (unsigned char *)(zigbee_uart_rx_buf);
    	        }
    	        *queue_in ++ = value;
    	    }
    	}
    
    
  5. After the MCU runs in the while loop, it calls the zigbee_uart_service() function in the mcu_api.c. The sample code in main.c is as follows:

    	include "zigbee.h"
    	...
    	void main(void)
    	{
    		zigbee_protocol_init();
    		...
    		while(1)
    		{
    			zigbee_uart_service();
    			...
    		}
    	}
    

    The MCU must directly call zigbee_uart_service() in the mcu_api.c file in the while(1) loop. After the program is initialized, if an interrupt service routine (ISR) is necessary, you must keep it as short as possible. Do not call the data reporting function to avoid loss of data.

Report and send DP data

Report all DPs

After the Zigbee module is restarted or paired again, it will initiate a status query. The MCU must return the status of all DPs.

  1. Open protocol.c and find the function all_data_update(void).
  2. Specify the initial values in corresponding functions for all DPs to be reported. These values will be displayed on the mobile app.

Avoid manually calling all_data_update() because this function will be automatically triggered at the specified time.

/**
 * @brief  Upload the information of all DPs in the system, to synchronize data between the app and the MCU.
 * @param  Null
 * @return Null
 * @note  This function shall be called in the SDK. The MCU must implement data reporting in this function, including the data for reporting only and the data for reporting and sending.
 */
void all_data_update(void)
{
    //#error "Process the data for reporting and sending and the data for reporting only. Delete the line after processing is completed."

    /*
    //This code is automatically generated by the Tuya Developer Platform. Modify each sendable and reportable function and report-only function according to the actual data.
    mcu_dp_bool_update(DPID_SWITCH,current switch); //Report bool type data;
    mcu_dp_value_update(DPID_TEMP_SET,current temperature setting); //Report value type data;
    mcu_dp_value_update(DPID_TEMP_CURRENT,current temperature); //Report value type data;
    mcu_dp_enum_update(DPID_MODE,current working mode); //Report enumeration data;
    mcu_dp_enum_update(DPID_FAN_SPEED_ENUM,current wind speed); //Report enumeration data;
    mcu_dp_enum_update(DPID_STATUS,current status); //Report enumeration data;
    mcu_dp_bool_update(DPID_ECO,current ECO mode); //Report bool type data;
    mcu_dp_bool_update(DPID_DRYING,current drying mode); //Report bool type data;
    mcu_dp_bool_update(DPID_VENTILATION,current ventilation mode); //Report bool type data;
    mcu_dp_bool_update(DPID_HEAT,current auxiliary heat); //Report bool type data;
    mcu_dp_bool_update(DPID_LIGHT,current light); //Report bool type data;
    mcu_dp_bool_update(DPID_CHILD_LOCK,current child lock); //Report bool type data;
    mcu_dp_bool_update(DPID_BEEP,current beep); //Report bool type data;
    mcu_dp_value_update(DPID_HUMIDITY_SET,current humidity setting); //Report value type data;
    mcu_dp_value_update(DPID_HUMIDITY_CURRENT,current humidity); //Report value type data;
    mcu_dp_enum_update(DPID_TEMP_UNIT_CONVERT,temperature unit switch); //Report enumeration data;
    mcu_dp_enum_update(DPID_COUNTDOWN_SET,current countdown); //Report enumeration data;
    mcu_dp_value_update(DPID_COUNTDOWN_LEFT,remaining time of current countdown); //Report value type data;
    mcu_dp_fault_update(DPID_FAULT,current fault alert); //Report fault type data;
    mcu_dp_value_update(DPID_TEMP_CURRENT_F,current temperature-°F); //Report value type data;
    mcu_dp_value_update(DPID_TEMP_SET_F,current temperature setting-°F); //Report value type data;
    mcu_dp_bool_update(DPID_SLEEP,current sleep feature); //Report bool type data;
    mcu_dp_bool_update(DPID_CLEANING,current self-cleaning); //Report bool type data;
    mcu_dp_bool_update(DPID_SWITCH_VERTICAL,current vertical swing); //Report bool type data;
    mcu_dp_enum_update(DPID_GEAR_VERTICAL,current vertical swing level); //Report enumeration data;
    mcu_dp_value_update(DPID_ANGLE_VERTICAL,current vertical swing angle); //Report value type data;
    mcu_dp_bool_update(DPID_SWITCH_HORIZONTAL,current horizontal swing); //Report bool type data;
    mcu_dp_enum_update(DPID_GEAR_HORIZONTAL,current horizontal swing level); //Report enumeration data;
    mcu_dp_value_update(DPID_ANGLE_HORIZONTAL,current horizontal swing angle); //Report value type data;
    mcu_dp_bool_update(DPID_DISPLAY,current screen display switch); //Report bool type data;
    */
}

Report a single DP

When the status of a single DP changes, the MCU must proactively report the current DP status to sync with the app. The data format is mcu_dp_xxxx_updata(DPID_X,n), where DPID_X is the DP whose status changes. You can call the functions in all_data_update() individually.

Example:

mcu_dp_bool_update(DPID_SWITCH,1); //Report bool type data 
mcu_dp_value_update(DPID_TEMPER_SET,25); //Report value type data 
mcu_dp_string_update(DPID_DAY,"1234",4); //Report string data

Send DP commands

In protocol.c, each DP that can send control commands has an individual command handler. The format is dp_download_xxx_handle(), where xxx is the DP that can send commands. After the function parses the DP, the MCU will execute commands accordingly.

Take the received switch DP data as an example:

/*****************************************************************************
Function name: dp_download_switch_handle. 
Function description: Processing function of DPID_SWITCH. 
Input parameters: value indicates data source. 
length indicates data length. 
Return parameters: Return SUCCESS on success. Return ERROR on a failure. 
Instruction: Regarding the data for reporting and sending, the processing results are reported to the app after processing is completed. 
*****************************************************************************/
static unsigned char dp_download_switch_handle(const unsigned char value[], unsigned short length)
{
  // Example: The current DP type is Bool
  unsigned char ret;
  //0: Off /1: On
  unsigned char switch1;

  switch1 = mcu_get_dp_download_bool(value,length);
  if(switch1 == 0)
  {
    // The switch is off
    MCU_OFF_switch1();
  }
  else
  {
    // The switch is on
    MCU_ON_switch1();
  }

  // Response after the DP data is processed
  ret = mcu_dp_bool_update(DPID_SWITCH,switch1);
  if(ret == SUCCESS)
    return SUCCESS;
  else
    return ERROR;
}

If the change of device status is not triggered by control commands, the MCU will call mcu_dp_bool_update(DPID_SWITCH_1,switch_1); to upload the current status of the DP for feedback. You can specify the reporting timing as needed.

Production testing

The Zigbee firmware includes RF production testing capabilities. The MCU can initiate an RF test on the Zigbee module using serial commands and receive the results through the serial port when the test is complete. The testing process depends on the Tuya Zigbee production testing feature. For more information, see UART Protocol for Zigbee Three-level Architecture.

Device pairing

The MCU determines when to initiate the Zigbee pairing. When the MCU receives network status from the Zigbee module, it can send a 0x03 command to instruct the module to enter pairing mode.

Example: 0x55 aa 02 00 01 03 00 01 01 xx

Communication between MCU and Zigbee module

Frame format

See Frame Format for details.

Initialization

After the MCU and the module are powered on, the initialization configuration starts. Initialization communication includes but is not limited to:

  • Verify whether the MCU and the module work properly.
  • Verify whether the connection works properly.
  • Get the data required for module activation.
  • Get the working mode of the module.

Query product information

After the module is powered on, it retrieves the product PID, firmware version, and pairing mode from the MCU.

  • PID: product ID, used to activate the product.
  • Version number: MCU firmware version number, used to verify whether the OTA updates are successful.
  • Pairing mode: the pairing mode of the module.

Things to note

  • To ensure proper data communication, we recommend that you start service data transmission like DP data and OTA updates after the network is initialized.
  • If you use the MCU to control the on/off of the module, data communication must be started after all the initialization is done.