Complex Protocol Control for Laser Robot Vacuum

Last Updated on : 2026-04-22 02:53:40Copy for LLMView as MarkdownDownload PDF

Overview

To reduce development costs and facilitate the interaction between laser navigation robot vacuums and app panels, the laser robot vacuum SDK encapsulates complex controls such as virtual walls and room properties into different APIs. You can use the APIs to easily complete data communication.

Features

The APIs support the following features:

  • Complete the protocol parsing for functional parameter settings sent by the cloud, and deliver the set/query commands and their parameters to the business application through the callback interface.
  • Encapsulate device functional parameter reporting APIs. If the device has data synchronized with the cloud, the reporting can be completed by calling the specified functional reporting interface.

Commands supported by the APIs

Feature Command type identifier Direction
Set a virtual wall VIRTUAL_WALL_SET Panel->Device
Query a virtual wall VIRTUAL_WALL_QUERY Panel->Device
Set a no-go area RESTRICTED_AREA_SET Panel->Device
Query a no-go area RESTRICTED_AREA_QUERY Panel->Device
Set room properties ROOM_PROPERTY_SET Panel->Device
Query room properties ROOM_PROPERTY_QUERY Panel->Device
Set a selected area cleaning ROOM_CLEAN_SET Panel->Device
Query a selected area cleaning ROOM_CLEAN_QUERY Panel->Device
Set a selected zone cleaning ZONE_CLEAN_SET Panel->Device
Query a selected zone cleaning ZONE_CLEAN_QUERY Panel->Device
Set a selected spot cleaning SPOT_CLEAN_SET Panel->Device
Query a selected spot cleaning SPOT_CLEAN_QUERY Panel->Device
Set a schedule SCHEDULE_SET Panel->Device
Query a schedule SCHEDULE_QUERY Panel->Device
Set a Do not disturb (DND) period QUIET_HOUR_SET Panel->Device
Query a DND period QUIET_HOUR_QUERY Panel->Device
Split an area on the map PART_DIVI_SET Panel->Device
Merge areas on the map PART_MERGE_SET Panel->Device
Restore default areas on the map PART_DEFAULT_SET Panel->Device
Reset a map RESET_CURR_MAP_SET Panel->Device
Save a map SAVE_CURR_MAP_SET Panel->Device
Delete a map from the cloud DELETE_CLOUD_MAP_SET Panel->Device
Query a voice pack VOICE_LANGUAGE_QUERY Panel->Device
Query device information DEV_INFO_QUERY Panel->Device
Query password status PASSWORD_STATE_QUERY Panel->Device
Verify a password PASSWORD_CHECK Panel->Device
Set a password PASSWORD_SET Panel->Device
Query current room information MCS_ROOM_INFO_QUERY Panel->Device
Set furniture models FURNITURE_MODEL_SET Panel->Device
Query furniture models FURNITURE_MODEL_QUERY Panel->Device
Set device models DEVICE_MODEL_SET Panel->Device
Query device models DEVICE_MODEL_QUERY Panel->Device
Set custom carpet CUSTOM_CARPET_SET Panel->Device
Query custom carpet CUSTOM_CARPET_QUERY Panel->Device
Create a Wi-Fi heatmap WIFI_MAP_SET Panel->Device

Trigger timing

The SDK passes commands to the application through registered callback functions. Commands are triggered in the following two scenarios:

Scenario 1: Triggered by users’ proactive operation (SET command)

Trigger scenario: Triggered when the user performs an operation on the app panel.

User operation Trigger command Trigger timing
Enter the map editing interface and draw a virtual wall on the map VIRTUAL_WALL_SET After the user taps to confirm
Enter the map editing interface and set a no-go area RESTRICTED_AREA_SET After the user draws a no-go area and confirms
Enter the map editing interface and modify the room name/cleaning parameters ROOM_PROPERTY_SET Triggered immediately after the user modifies room properties
Enter the homepage map interface and select room cleaning ROOM_CLEAN_SET After the user selects a room and taps Start Cleaning
Enter the homepage map interface and start the selected zone cleaning ZONE_CLEAN_SET After the user draws a zone on the map and taps Start Cleaning
Enter the homepage map interface and start the selected spot cleaning SPOT_CLEAN_SET After the user taps a spot on the map and taps Start Cleaning
Enter the device settings interface and create/modify a scheduled cleaning SCHEDULE_SET After the user sets a scheduled task
Enter the device settings interface and set a DND period QUIET_HOUR_SET After the user configures a DND period
Enter the map editing interface and split a room PART_DIVI_SET After the user draws a divider on the map
Enter the map editing interface and merge rooms PART_MERGE_SET After the user selects two rooms to merge
Enter the map editing interface and restore default areas PART_DEFAULT_SET After the user taps the Restore Default button
Enter the map editing interface and reset the map RESET_CURR_MAP_SET After the user taps Reset Map and confirms
Enter the map editing interface and save the map SAVE_CURR_MAP_SET After the user taps Save Current Map
Enter the multi-map management interface and delete a cloud map DELETE_CLOUD_MAP_SET After the user selects a map and taps delete
Enter the video channel and verify the password PASSWORD_CHECK After the user enters the password and submits for verification
Enter the video channel and set the password PASSWORD_SET After the user sets a new password
Set furniture models FURNITURE_MODEL_SET After the user configures on the furniture management interface and saves
Set device models DEVICE_MODEL_SET After the user configures on the device management interface and saves
Set custom carpet CUSTOM_CARPET_SET After the user configures on the floor material interface and taps confirm
Enable/Cancel Wi-Fi heatmap WIFI_MAP_SET After the user enters the Wi-Fi heatmap page

Process:

User operation → app panel → cloud → SDK parsing → FUNC_SET_CB callback → application processing → Call the response interface to report results

Scenario 2: Panel refresh query (QUERY command)

Trigger scenario: Triggered when the user opens the app panel or performs a pull-to-refresh.

Query command Trigger timing Expected response
VIRTUAL_WALL_QUERY Enter the homepage map page Return all virtual walls on the current map
RESTRICTED_AREA_QUERY Enter the homepage map page Return all no-go areas on the current map
ROOM_PROPERTY_QUERY Enter the homepage map page Return property information for all rooms
ROOM_CLEAN_QUERY Enter the selected area cleaning page Return the last selected area cleaning configuration
ZONE_CLEAN_QUERY Enter the selected zone cleaning page Return the last selected zone cleaning parameters
SPOT_CLEAN_QUERY Enter the selected spot cleaning page Return the last selected spot cleaning configuration
SCHEDULE_QUERY Enter the scheduled task page Return all scheduled cleaning tasks
QUIET_HOUR_QUERY Enter the DND setting page Return the DND period configuration
VOICE_LANGUAGE_QUERY Enter the voice settings page Return the current voice pack status
DEV_INFO_QUERY Enter the device settings page Return detailed device information
MAP_EMPTY_QUERY App homepage map loading (backup feature) Return whether map data exists
PASSWORD_STATE_QUERY Enter the password settings page Return whether a password has been set
MCS_ROOM_INFO_QUERY Alexa voice control Return the current room where the device is located
FURNITURE_MODEL_QUERY Enter the homepage map page Return the list of furniture models
CUSTOM_CARPET_QUERY Enter or refresh the floor material page Return the list of custom carpets
DEVICE_MODEL_QUERY Enter the homepage map page Return the list of device models

Process:

Panel refresh → cloud → SDK parsing → FUNC_QUERY_CB callback → application queries data → Call the response interface to report results

Features and structs

Set and query a virtual wall

Feature: A virtual wall is a virtual barrier line set on the map, and the robot will not cross the line to clean.

Data structure:

    /**
    * @brief Forbidden modes
    */
    typedef enum {
        FORBIT_ALL = 0, // All prohibited
        FORBIT_SWEEP, // Prohibit mopping
        FORBIT_MOP, // Prohibit sweeping
        FORBIT_RESERVE, // Not set, reserved
    } FORBIT_MODE_E;

    /**
    * @brief Coordinate point structure
    */
    typedef struct {
        int x; // X value of the coordinate point
        int y; // Y value of the coordinate point
    } POINT_COOR_S;

    /**
    * @brief Virtual line coordinate structure
    */
    typedef struct {
        FORBIT_MODE_E mode; // Forbidden mode
        POINT_COOR_S points[2]; // Endpoints of the line, first element is the starting point of the virtual wall, second element is the ending point of the virtual wall
    } VIRTUAL_LINE_S;

    /**
    * @brief Virtual wall structure
    */
    typedef struct {
        int num; // Number of virtual walls
        int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
        VIRTUAL_LINE_S* line; // Virtual wall coordinate points and forbidden mode
    } VIRTUAL_WALL_S;

Struct description:

Field Type SET command processing QUERY command response Proactive reporting
num int Receive the number of virtual walls set via the panel Populate the total number of virtual walls Populate the total number of virtual walls
map_id int Not sent by the panel. Ignore this field Must populate the current map ID Must populate the current map ID
line Array pointer Parse the coordinates and mode of each virtual wall Populate information for all virtual walls Populate information for all virtual walls
line[i].mode Enum Virtual wall mode (All-prohibited, no-go, and no-mop) Populate the virtual wall mode Populate the virtual wall mode
line[i].points[0] Coordinates Virtual wall start point (x0, y0) Populate the start point coordinates Populate the start point coordinates
line[i].points[1] Coordinates Virtual wall end point (x1, y1) Populate the end point coordinates Populate the end point coordinates

Example:

 // Example of SET command processing
static OPERATE_RET handle_virtual_wall_set(VIRTUAL_WALL_S* p_virtual_wall)
{
    PR_DEBUG("Received virtual wall setting command, number of virtual walls: %d", p_virtual_wall->num);

    // Traverse all virtual walls
    for(int i = 0; i < p_virtual_wall->num; i++) {
        PR_DEBUG("Virtual wall [%d] mode:%d", i, p_virtual_wall->line[i].mode);
        PR_DEBUG("  Start point: (%d, %d)",
                 p_virtual_wall->line[i].points[0].x,
                 p_virtual_wall->line[i].points[0].y);
        PR_DEBUG("  End point: (%d, %d)",
                 p_virtual_wall->line[i].points[1].x,
                 p_virtual_wall->line[i].points[1].y);

        // TODO: Save virtual wall data to device memory/persistent storage
        // save_virtual_wall_to_device(i, &p_virtual_wall->line[i]); // Pseudocode. You need to implement it yourself
    }

    // Report the result after processing
    VIRTUAL_WALL_S report_data;
    report_data.num = p_virtual_wall->num;
    report_data.map_id = get_current_map_id();  // Get the current map ID. Pseudocode. You need to implement it yourself
    report_data.line = p_virtual_wall->line;

    return ty_rvc_virtual_wall_data_response(&report_data, 0);  // 0 indicates success
}

// Example of QUERY command response
static OPERATE_RET handle_virtual_wall_query(void)
{
    PR_DEBUG("Received virtual wall query command");

    // Get current virtual wall data from the device
    VIRTUAL_WALL_S query_data;
    VIRTUAL_LINE_S lines[10];  // Assume a maximum of 10 virtual walls

    query_data.num = get_virtual_wall_count();  // Get the number of virtual walls. Pseudocode. You need to implement it yourself
    query_data.map_id = get_current_map_id();   // Must Populate the map ID. Pseudocode. You need to implement it yourself
    query_data.line = lines;

    // Populate the data for each virtual wall
    for(int i = 0; i < query_data.num; i++) {
        // load_virtual_wall_from_device(i, &lines[i]);//Pseudocode. You need to implement it yourself
        lines[i].mode = FORBIT_ALL;
        lines[i].points[0].x = 100;
        lines[i].points[0].y = 200;
        lines[i].points[1].x = 500;
        lines[i].points[1].y = 200;
    }

    return ty_rvc_virtual_wall_data_response(&query_data, 0);
}

Things to note:

  • The SET command sent by the panel does not contain the map_id field. The device must determine the current map on its own.
  • When reporting data, the device must populate the map_id field. Otherwise, the panel will be unable to match the data with the correct map.
  • Coordinate system:
    • Virtual wall coordinates use the robot coordinate system (with the robot’s starting position as the origin O).
    • Coordinate type: INT32 integer values.
    • Coordinate meaning: The coordinate point M (x, y) represents the number of grid cells of that point relative to the origin O.
    • Actual position calculation: Actual distance = Number of grid cells × Grid resolution.
    • Example: If the grid resolution is 5 cm, the coordinate (100, 200) represents a distance of (500 cm, 1000 cm) = (5m, 10m) from the origin.
    • For more information, see Map coordinate system.
  • Memory management:
    • Sent by the app (SET command): Memory is managed by the SDK and will be automatically released after the callback function returns. If the application needs to process the data asynchronously (e.g., using it in another thread), it must copy the data to memory allocated by the application itself.
    • Reported by the device (response interface): The line array and its associated memory are allocated and managed by the application. The SDK will not free this memory.

Set and query a no-go area

Feature: A no-go area is a polygonal area (usually rectangular) on the map, and the robot will not enter this area to clean.

Data structure:

    typedef struct {
        int len; // string length
        char* name; // string memory points
    } STR_ELEMENT_S;

    /**
    * @brief Area coordinate point structure
    */
    typedef struct {
        int point_num; // Number of endpoints forming the area
        POINT_COOR_S* point; // Endpoint coordinates forming the restricted area
    } AREA_S;

    /**
    * @brief Virtual area structure
    */
    typedef struct {
        FORBIT_MODE_E mode; // Forbidden mode
        AREA_S area; // Endpoint coordinates of the restricted area
        STR_ELEMENT_S cur_name; // Name of the restricted area
    } VIRTUAL_AREA_S;

    /**
    * @brief Restricted area structure
    */
    typedef struct {
        int num; // Number of restricted areas
        int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
        VIRTUAL_AREA_S* restrict_zone; // Coordinates and forbidden mode of the restricted area
    } RESTRICTED_AREA_S;

Struct description:

Field Type SET command processing QUERY command response Proactive reporting
num int Receive the number of no-go areas set via the panel Populate the total number of no-go areas Populate the total number of no-go areas
map_id int Not sent by the panel. Ignore this field Must populate the current map ID Must populate the current map ID
restrict_zone Array pointer Parse the information of each no-go area Populate all no-go area information Populate all no-go area information
restrict_zone[i].mode Enum No-go mode (All-prohibited, no-go, and no-mop) Populate the no-go area mode Populate the no-go area mode
restrict_zone[i].area Area structure The polygon coordinate point set for the no-go area Populate the area coordinates Populate the area coordinates
restrict_zone[i].area.point_num int The number of vertices of the no-go area (typically 4) Populate the number of vertices Populate the number of vertices
restrict_zone[i].area.point Coordinate array The coordinates of each vertex of the no-go area Populate the coordinates of each vertex Populate the coordinates of each vertex
restrict_zone[i].cur_name String The name of the no-go area Populate the name of the no-go area Populate the name of the no-go area

Example:

// Example of SET command processing
static OPERATE_RET handle_restricted_area_set(RESTRICTED_AREA_S* p_restricted_area)
{
    PR_DEBUG("Received no-go area setting command, number of no-go areas: %d", p_restricted_area->num);

    // Traverse all no-go areas
    for(int i = 0; i < p_restricted_area->num; i++) {
        PR_DEBUG("No-go area [%d] mode: %d", i, p_restricted_area->restrict_zone[i].mode);
        PR_DEBUG("  Name: %.*s",
                 p_restricted_area->restrict_zone[i].cur_name.len,
                 p_restricted_area->restrict_zone[i].cur_name.name);
        PR_DEBUG(" Number of vertices: %d",
                 p_restricted_area->restrict_zone[i].area.point_num);

        // Traverse all vertices of the no-go area
        for(int j = 0; j < p_restricted_area->restrict_zone[i].area.point_num; j++) {
            PR_DEBUG("    Vertex [%d]: (%d, %d)", j,
                     p_restricted_area->restrict_zone[i].area.point[j].x,
                     p_restricted_area->restrict_zone[i].area.point[j].y);
        }

        // TODO: Save no-go area data to device memory/persistent storage
        // save_restricted_area_to_device(i, &p_restricted_area->restrict_zone[i]);//Pseudocode. You need to implement it yourself
    }

    // Report the result after processing
    RESTRICTED_AREA_S report_data;
    report_data.num = p_restricted_area->num;
    report_data.map_id = get_current_map_id();  // Get the current map ID. Pseudocode. You need to implement it yourself
    report_data.restrict_zone = p_restricted_area->restrict_zone;

    return ty_rvc_restricted_area_data_response(&report_data, 0);  // 0 indicates success
}

// Example of QUERY command response
static OPERATE_RET handle_restricted_area_query(void)
{
    PR_DEBUG("Received no-go area query command");

    // Get current no-go area data from the device
    RESTRICTED_AREA_S query_data;
    VIRTUAL_AREA_S areas[5];  // Assume a maximum of 5 no-go areas
    POINT_COOR_S area_points[5][4];  // 4 vertices per no-go area

    query_data.num = get_restricted_area_count();  // Get the number of no-go areas. Pseudocode. You need to implement it yourself
    query_data.map_id = get_current_map_id();      // Map ID must be filled in. Pseudocode. You need to implement it yourself
    query_data.restrict_zone = areas;

    // Populate the data for each no-go area
    for(int i = 0; i < query_data.num; i++) {
        // load_restricted_area_from_device(i, &areas[i]);//Pseudocode. You need to implement it yourself

        // Example: Set the no-go area mode
        areas[i].mode = FORBIT_ALL;

        // Example: Set the no-go area name
        areas[i].cur_name.name = "No-go area 1";
        areas[i].cur_name.len = strlen(areas[i].cur_name.name);

        // Example: Set the no-go area (rectangle with 4 vertices)
        areas[i].area.point_num = 4;
        areas[i].area.point = area_points[i];

        // Populate the rectangle with the coordinates of its 4 vertices (clockwise or counterclockwise)
        area_points[i][0].x = 100; area_points[i][0].y = 100;  // Bottom left
        area_points[i][1].x = 200; area_points[i][1].y = 100;  // Bottom right
        area_points[i][2].x = 200; area_points[i][2].y = 200;  // Top right
        area_points[i][3].x = 100; area_points[i][3].y = 200;  // Top left
    }

    return ty_rvc_restricted_area_data_response(&query_data, 0);
}

Things to note:

  • The SET command sent by the panel does not contain the map_id field. The device must determine the current map on its own.
  • When reporting data, the device must populate the map_id field. Otherwise, the panel will be unable to match the data with the correct map.
  • Shape of a no-go area:
    • Typically, it is a rectangular area (with 4 vertices).
    • Vertex coordinates must be arranged in order (clockwise or counterclockwise).
    • Support polygonal no-go areas, with the number of vertices determined by point_num.
  • Coordinate system:
    • Use the robot coordinate system (same as virtual walls).
    • Coordinate type: INT32 integer values, representing the number of grid units.
    • Actual position calculation: Actual distance = Number of grid units × Grid resolution.
    • For more information, see Map coordinate system.
  • String processing:
    • The cur_name field must populate both the name pointer and the len (length) value.
  • Memory management:
    • Sent by the app (SET command): Memory is managed by the SDK and will be automatically released after the callback function returns. If the application needs to process the data asynchronously (e.g., placing it in a queue), it must copy the data to memory allocated by the application itself.
    • Reported by the device (response interface): The restrict_zone array, its internal point arrays, and the cur_name.name string memory are allocated and managed by the application. The SDK will not free this memory.

Set and query room properties

Feature: Room property management includes configuring parameters such as room naming, cleaning mode, cleaning order, and floor type.

Data structure:

/**
    * @brief Cleaning parameters
    */
    typedef struct {
      int valid; // Whether parameters are valid
      STR_ELEMENT_S suction; // Suction setting, values as agreed by the project
      STR_ELEMENT_S cistern; // Water volume setting, values as agreed by the project
      int water_value; // Custom water volume level, bound to DP in project design. When cistern is empty or invalid, water_value takes effect, range 1-30, from low to high
      STR_ELEMENT_S route_preference; // Route preference setting, values as agreed by the project
      STR_ELEMENT_S clean_direction; // Cleaning direction setting, values as agreed by the project
      bool y_mop; // Y-shaped mopping, TRUE means enabled
      int clean_cnt; // Room cleaning count
    } CLEAN_PARAM_S;

    /**
    * @brief Room property structure
    */
    typedef struct {
        int num; // Number of rooms
        int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
        int* ids; // Room ID numbers, each room corresponds to one ID number
        STR_ELEMENT_S* sweep_mode; // room sweep mode,each room corresponds to one sweep mode
        CLEAN_PARAM_S* param; // Cleaning parameters for rooms, each room corresponds to one cleaning setting parameter, param[0] corresponds to ids[0] area
        int* orders; // Cleaning order for rooms, each room corresponds to one cleaning order, cleaning order is represented by non-zero Arabic numerals, smaller values indicate higher cleaning priority, zero represents the lowest priority
       int* nameLabels; // Room label array, each room corresponds to one preset label ID
        STR_ELEMENT_S* cus_name; // Room naming list, each room corresponds to a name, cus_name[0] corresponds to id[0]
        int* floor_type; // Room floor types,each room corresponds to one floor type
    } ROOM_PROPERTY_S;

Struct description:

Field Type SET command processing QUERY command response Proactive reporting
num int Receive the number of rooms Populate the total number of rooms Populate the total number of rooms
map_id int Not sent by the panel. Ignore this field Must populate the current map ID Must populate the current map ID
ids int array Parse each room ID Populate all room IDs Populate all room IDs
sweep_mode String array Parse the cleaning mode (string format) Populate the cleaning mode strings Populate the cleaning mode strings
param Cleaning parameter array Parse cleaning parameters for each room Populate cleaning parameters for each room Populate cleaning parameters for each room
param[i].valid int Parameter valid flag (reserved field) Populate the valid flag Populate the valid flag
param[i].suction String Suction level (string format) Populate the suction string Populate the suction string
param[i].cistern String Water volume level (string format) Populate the water volume string Populate the water volume string
param[i].water_value int Custom water volume value (1-30) Populate the water volume value Populate the water volume value
param[i].route_preference String Route preference (string format) Populate the route preference string Populate the route preference string
param[i].clean_direction String Cleaning direction (string format) Populate the cleaning direction string Populate the cleaning direction string
param[i].y_mop bool Y-shaped mopping switch Populate the Y-shaped mopping switch status Populate the Y-shaped mopping switch status
param[i].clean_cnt int Cleaning times Populate cleaning times Populate cleaning times
orders int array Cleaning order (lower numbers indicate higher priority) Populate the cleaning order Populate the cleaning order
nameLabels int array Array of room tag IDs, used to express preset room types Populate room tag IDs Populate room tag IDs
cus_name String array Custom room names Populate room names Populate room names
floor_type int array Floor type (reserved field. If implemented, it must align with the panel’s floor type definitions) Populate the floor type Populate the floor type

Example:

// Example of SET command processing
static OPERATE_RET handle_room_property_set(ROOM_PROPERTY_S* p_room_property)
{
    PR_DEBUG("Received room property setting command, number of rooms: %d", p_room_property->num);

    // Traverse all rooms
    for(int i = 0; i < p_room_property->num; i++) {
        PR_DEBUG("Room [%d] ID: %d", i, p_room_property->ids[i]);

        // Parse room names
        if(p_room_property->cus_name) {
            PR_DEBUG("  Name: %d-%s",
                     p_room_property->cus_name[i].len,
                     p_room_property->cus_name[i].name);
        }

        // Parse the cleaning mode (string format, it needs to be converted to an enumeration)
        if(p_room_property->sweep_mode) {
            PR_DEBUG("  Cleaning mode: %d-%s",
                     p_room_property->sweep_mode[i].len,
                     p_room_property->sweep_mode[i].name);
            // TODO: Convert the string to an enumeration value
            // int mode_enum = convert_sweep_mode_str_to_enum(&p_room_property->sweep_mode[i]); //Pseudocode. You need to implement it yourself
        }

         // Parse the cleaning parameters
        if(p_room_property->param) {
            PR_DEBUG("  Suction: %d-%s",
                     p_room_property->param[i].suction.len,
                     p_room_property->param[i].suction.name);
            PR_DEBUG("  Water volume: %d-%s",
                     p_room_property->param[i].cistern.len,
                     p_room_property->param[i].cistern.name);
            PR_DEBUG("  Water volume value: %d", p_room_property->param[i].water_value);
            PR_DEBUG("  Path preference: %d-%s",
                     p_room_property->param[i].route_preference.len,
                     p_room_property->param[i].route_preference.name);
            PR_DEBUG("  Cleaning direction: %d-%s",
                     p_room_property->param[i].clean_direction.len,
                     p_room_property->param[i].clean_direction.name);
            PR_DEBUG("  Y-shaped mopping: %d", p_room_property->param[i].y_mop);
            PR_DEBUG("  Cleaning count: %d", p_room_property->param[i].clean_cnt);
        }

        // Parse cleaning order
        if(p_room_property->orders) {
            PR_DEBUG("  Cleaning order: %d", p_room_property->orders[i]);
        }
        // Parse room label
        if(p_room_property->nameLabels) {
            PR_DEBUG("  Room label ID: %d", p_room_property->nameLabels[i]);
        }
        // Parse floor type
        if(p_room_property->floor_type) {      // Reserved function. If implemented, it needs to be aligned with the panel floor type
            PR_DEBUG("  Floor type: %d", p_room_property->floor_type[i]);
        }

        // TODO: Save room properties to device memory/persistent storage
        // save_room_property_to_device(i, ...); //Pseudocode. You need to implement it yourself
    }

    // Report the result after processing
    ROOM_PROPERTY_S report_data;
    report_data.num = p_room_property->num;
    report_data.map_id = get_current_map_id();  // Get the current map ID. Pseudocode. You need to implement it yourself
    report_data.ids = p_room_property->ids;
    report_data.sweep_mode = p_room_property->sweep_mode;
    report_data.param = p_room_property->param;
    report_data.orders = p_room_property->orders;
    report_data.cus_name = p_room_property->cus_name;
    report_data.floor_type = p_room_property->floor_type;

    return ty_rvc_room_property_data_response(&report_data, 0);  // 0 indicates success
}

// Example of QUERY command response
static OPERATE_RET handle_room_property_query(void)
{
    PR_DEBUG("Received room property query command");

    // Get current room property data from the device
    ROOM_PROPERTY_S query_data;
    int room_ids[10];           // Array of room IDs
    STR_ELEMENT_S sweep_modes[10];  // Array of cleaning modes
    CLEAN_PARAM_S params[10];   // Array of cleaning parameters
    int orders[10];             // Array of cleaning orders
    int name_labels[10];        // Array of room labels
    STR_ELEMENT_S room_names[10];   // Array of room names
    int floor_types[10];        // Array of floor types

    query_data.num = get_room_count();     // Get the number of rooms. Pseudocode. You need to implement it yourself
    query_data.map_id = get_current_map_id();  // Map ID must be filled in. Pseudocode. You need to implement it yourself
    query_data.ids = room_ids;
    query_data.sweep_mode = sweep_modes;
    query_data.param = params;
    query_data.orders = orders;
    query_data.nameLabels = name_labels;
    query_data.cus_name = room_names;
    query_data.floor_type = floor_types;

    // Populate the data for each room
    for(int i = 0; i < query_data.num; i++) {
        // Populate the room IDs
        room_ids[i] = get_room_id(i);

        // Populate the room names
        room_names[i].name = get_room_name(i);  // For example: "Living Room"
        room_names[i].len = strlen(room_names[i].name);

        // Populate cleaning mode (string format)
        sweep_modes[i].name = "only_sweep";  // For example: "only_sweep","only_mop","both_work"
        sweep_modes[i].len = strlen(sweep_modes[i].name);

        // Populate cleaning parameters
        params[i].suction.name = "strong";  // For example: closed","gentle","normal","strong","max
        params[i].suction.len = strlen(params[i].suction.name);
        params[i].cistern.name = "middle";  // For example: closed","low","middle","high"
        params[i].cistern.len = strlen(params[i].cistern.name);
        params[i].water_va// When using cisterns for reporting, water_value will not be reported. To report waterValues, you need to set cistern.name to NULL and set water_value(1-30)er_value to 0
        params[i].route_preference.name = "standard"; // For example: "standard","fast","deep"
        params[i].route_preference.len = strlen(params[i].route_preference.name);
        params[i].clean_direction.name = "auto"; // For example: "auto","horizontal","vertical"
        params[i].clean_direction.len = strlen(params[i].clean_direction.name);
        params[i].y_mop = true;
        params[i].clean_cnt = 2;

        // Populate the cleaning order (1 highest, 0 lowest)
        orders[i] = i + 1;

        // Populate the room labels (Example: 0=Entrance Hall, 1=Study, 2=Hallway, 3=Bedroom, 4=Balcony, 5=Bathroom, 6=Dining Room, 7=Living Room, 8=Master Bedroom, 9=Secondary Bedroom, 10=Kitchen)
        name_labels[i] = 7;  // For example, Populate "Living Room" first
        // Populate the floor type (reserved field)
        floor_types[i] = 1;  // For example: 0 = Tile, 1 = Wood Floor, 2 = Carpet
    }

    return ty_rvc_room_property_data_response(&query_data, 0);
}

Things to note:

  • The SET command sent by the panel does not contain the map_id field. The device must determine the current map on its own.
  • When reporting data, the device must populate the map_id field. Otherwise, the panel will be unable to match the data with the correct map.
  • Important: After any operation involving room editing (such as map area splitting, merging, restoring defaults, switching between maps, and creating a new map), the room properties must be proactively reported to ensure the app panel shows the latest room information.
  • Room property changes include changes in room count, room IDs, room names, and room parameters.
  • It is recommended to call ty_rvc_room_property_data_response() within the response function of the room editing operation to report the latest room properties.
  • String processing:
    • sweep_mode, cus_name, suction, cistern, route_preference, and clean_direction are all in string format.
    • Each string requires populating both the name pointer and the len (length).
    • String memory is managed by the application. Pay attention to the memory lifecycle.
  • String to enum mapping:
    • Cleaning mode example: "only_sweep" / "only_mop" / "both_work"
    • Suction level example: "closed" / "gentle" / "normal" / "strong" / "max"
    • Water volume level example: "closed" / "low" / "middle" / "high"
    • Route preference example: "standard" / "fast" / "deep"
    • Cleaning direction example: "auto" / "horizontal" / "vertical"
  • Water volume reporting rules:
    • cistern.name != NULL: The SDK will prioritize reporting by "enumerated water volume level" (corresponding to protocol field cisterns).
    • cistern.name == NULL: The SDK will report by "custom water volume value" (corresponding to protocol field waterValues, ranging from 1 to 30, low to high).
    • Note: In a single report, if any element has cistern.name != NULL, the SDK will report cisterns as a whole and will not report waterValues. Therefore, do not mix values: either use cistern for all rooms, or use water_value for all rooms and set all cistern.name pointers to NULL.
  • Room tag:
    • The nameLabels field corresponds to the protocol field nameLabels.
    • Each element represents the preset tag ID for a room. For example, foyer, study, hallway, bedroom, balcony, bathroom, dining room, living room, master bedroom, secondary bedroom, and kitchen.
    • If the current room has no preset tag, fill with -1.
    • nameLabels[0] corresponds to the same room as ids[0] and cus_name[0].
  • Cleaning order rules:
    • Lower numbers indicate higher priority (1 > 2 > 3 > …).
    • 0 means the room is not included in the sorting order.
  • Array index correspondence:
    • All array indices correspond one-to-one with the ids array.
    • ids[0] corresponds to cus_name[0], sweep_mode[0], param[0], orders[0], nameLabels[0], and floor_type[0].
  • Memory management:
    • Sent by the app (SET command): Memory is managed by the SDK and will be automatically released after the callback function returns. If the application needs to process the data asynchronously (e.g., using it in another thread), it must copy the data (including all arrays and strings) to memory allocated by the application itself.
    • Reported by the device (response interface): All arrays (for example, ids, sweep_mode, param, orders, floor_material, and room_name) and string memory (for example, suction.name, cistern.name, sweep_mode[i].name) are allocated and managed by the application. The SDK will not free this memory.

Set and query the selected area cleaning

Feature: Users can select one or more rooms for cleaning, and configure independent cleaning modes and parameters for each room.

Data structure:

 /**
    * @brief Cleaning parameters
    */
    typedef struct {
      int valid; // Whether parameters are valid
      STR_ELEMENT_S suction; // Suction setting, values as agreed by the project
      STR_ELEMENT_S cistern; // Water volume setting, values as agreed by the project
      int water_value; // Custom water volume level, bound to DP in project design. When cistern is empty or invalid, water_value takes effect, range 1-30, from low to high
      STR_ELEMENT_S route_preference; // Route preference setting, values as agreed by the project
      STR_ELEMENT_S clean_direction; // Cleaning direction setting, values as agreed by the project
      bool y_mop; // Y-shaped mopping, TRUE means enabled
      int clean_cnt; // Room cleaning count
    } CLEAN_PARAM_S;

    /**
    * @brief Selected area cleaning structure
    */
    typedef struct {
        int num; // Number of selected areas
        int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
        int* ids; // List of IDs for the selected area rooms
        STR_ELEMENT_S* sweep_mode; // sweep mode for the selected area rooms
        CLEAN_PARAM_S* param; // Cleaning parameters for selected areas, each room corresponds to one cleaning parameter, param[0] corresponds to ids[0] area, if not set valid is 0
    } ROOM_CLEAN_S;

Struct description:

Field Type SET command processing QUERY command response Proactive reporting
num int Receive the number of selected rooms Populate the number of rooms cleaned in the last selected area cleaning task Populate the number of rooms cleaned in the current selected area cleaning task
map_id int Not sent by the panel. Ignore this field Must populate the current map ID Must populate the current map ID
ids int array Parse the list of selected room IDs Populate the last selected room ID Populate the currently selected room ID
sweep_mode String array Parse the cleaning mode for each room (string format) Populate the cleaning mode strings for each room Populate the cleaning mode strings for each room
param Cleaning parameter array Parse cleaning parameters for each room Populate cleaning parameters for each room Populate cleaning parameters for each room
param[i].valid int Parameter valid flag (reserved field) Populate the valid flag Populate the valid flag
param[i].suction String Suction level (string format) Populate the suction string Populate the suction string
param[i].cistern String Water volume level (string format) Populate the water volume string Populate the water volume string
param[i].water_value int Custom water volume value (1-30) Populate the water volume value Populate the water volume value
param[i].route_preference String Route preference (string format) Populate the route preference string Populate the route preference string
param[i].clean_direction String Cleaning direction (string format) Populate the cleaning direction string Populate the cleaning direction string
param[i].y_mop bool Y-shaped mopping switch Populate the Y-shaped mopping switch status Populate the Y-shaped mopping switch status
param[i].clean_cnt int Cleaning times Populate cleaning times Populate cleaning times

Example:

// Example of SET command processing
static OPERATE_RET handle_room_clean_set(ROOM_CLEAN_S* p_room_clean)
{
    PR_DEBUG("Received the selected area cleaning setting command, number of rooms: %d", p_room_clean->num);

    // Traverse all selected rooms
    for(int i = 0; i < p_room_clean->num; i++) {
        PR_DEBUG("Room [%d] ID: %d", i, p_room_clean->ids[i]);

        // Parse the cleaning mode (string format, it needs to be converted to an enumeration)
        if(p_room_clean->sweep_mode) {
            PR_DEBUG("  Cleaning mode: %d-%s",
                     p_room_clean->sweep_mode[i].len,
                     p_room_clean->sweep_mode[i].name);
            // TODO: Convert the string to an enumeration value
            // int mode_enum = convert_sweep_mode_str_to_enum(&p_room_clean->sweep_mode[i]);//Pseudocode. You need to implement it yourself
        }

        // Parse the cleaning parameters
        PR_DEBUG("  Suction: %d-%s",
                    p_room_clean->param[i].suction.len,
                    p_room_clean->param[i].suction.name);
        PR_DEBUG("  Water volume: %d-%s",
              p_room_clean->param[i].cistern.len,
              p_room_clean->param[i].cistern.name);
        PR_DEBUG("  Water volume value: %d", p_room_clean->param[i].water_value);
        PR_DEBUG("  Path preference: %d-%s",
              p_room_clean->param[i].route_preference.len,
              p_room_clean->param[i].route_preference.name);
        PR_DEBUG("  Cleaning direction: %d-%s",
              p_room_clean->param[i].clean_direction.len,
              p_room_clean->param[i].clean_direction.name);
        PR_DEBUG("  Y-shaped mopping: %d", p_room_clean->param[i].y_mop);
        PR_DEBUG("  Cleaning count: %d", p_room_clean->param[i].clean_cnt);

        // TODO: Save the selected area cleaning configuration
        // save_room_clean_config(i, p_room_clean->ids[i], ...);//Pseudocode. You need to implement it yourself
    }

    // Start the selected area cleaning task
    // start_room_clean_task(p_room_clean->ids, p_room_clean->num);//Pseudocode. You need to implement it yourself

    // Report the result after processing
    ROOM_CLEAN_S report_data;
    report_data.num = p_room_clean->num;
    report_data.map_id = get_current_map_id();  // Get the current map ID. Pseudocode. You need to implement it yourself
    report_data.ids = p_room_clean->ids;
    report_data.sweep_mode = p_room_clean->sweep_mode;
    report_data.param = p_room_clean->param;

    return ty_rvc_room_clean_data_response(&report_data, 0);  // 0 indicates success
}

// Example of QUERY command response
static OPERATE_RET handle_room_clean_query(void)
{
    PR_DEBUG("Received the selected area cleaning query command");

    // Get the last selected area cleaning configuration from the device
    ROOM_CLEAN_S query_data;
    int room_ids[10];               // Array of room IDs
    STR_ELEMENT_S sweep_modes[10];  // Array of cleaning modes
    CLEAN_PARAM_S params[10];       // Array of cleaning parameters

    // Get the last selected area cleaning configuration
    query_data.num = get_last_room_clean_count();  // Get the number of rooms selected last time. Pseudocode. You need to implement it yourself
    query_data.map_id = get_current_map_id();      // Map ID must be filled in. Pseudocode. You need to implement it yourself
    query_data.ids = room_ids;
    query_data.sweep_mode = sweep_modes;
    query_data.param = params;

    // Populate data for each room
    for(int i = 0; i < query_data.num; i++) {
        // Populate room IDs
        room_ids[i] = get_last_clean_room_id(i);

        // Populate cleaning mode (string format)
        sweep_modes[i].name = "both_work";  // For example: "only_sweep","only_mop","both_work"
        sweep_modes[i].len = strlen(sweep_modes[i].name);

        // Populate cleaning parameters
        params[i].suction.name = "strong";  // For example: "closed","gentle","normal","strong","max"
        params[i].suction.len = strlen(params[i].suction.name);
        params[i].cistern.name = "high";    // For example: "closed","low","middle","high"
        params[i].cistern.len = strlen(params[i].cistern.name);
        params[i].water_value = 0; // When using cisterns for reporting, water_value will not be reported. To report waterValues, set cistern.name to NULL and set water_value (1-30)
        params[i].route_preference.name = "standard"; // For example: "standard","fast","deep"
        params[i].route_preference.len = strlen(params[i].route_preference.name);
        params[i].clean_direction.name = "auto"; // For example: "auto","horizontal","vertical"
        params[i].clean_direction.len = strlen(params[i].clean_direction.name);
        params[i].y_mop = false;
        params[i].clean_cnt = 1;
    }

    return ty_rvc_room_clean_data_response(&query_data, 0);
}

Things to note:

  • The SET command sent by the panel does not contain the map_id field. The device must determine the current map on its own.
  • When reporting data, the device must populate the map_id field. Otherwise, the panel will be unable to match the data with the correct map.
  • String processing:
    • sweep_mode, suction, cistern, route_preference, and clean_direction are all in string format.
    • Each string requires populating both the name pointer and the len (length).
    • String memory is managed by the application. Pay attention to the memory lifecycle.
  • String to enum mapping:
    • Cleaning mode example: "only_sweep" / "only_mop" / "both_work"
    • Suction level example: "closed" / "gentle" / "normal" / "strong" / "max"
    • Water volume level example: "closed" / "low" / "middle" / "high"
    • Route preference example: "standard" / "fast" / "deep"
    • Cleaning direction example: "auto" / "horizontal" / "vertical"
  • Water volume reporting rules:
    • cistern.name != NULL: The SDK will prioritize reporting by "enumerated water volume level" (corresponding to protocol field cisterns).
    • cistern.name == NULL: The SDK will report by "custom water volume value" (corresponding to protocol field waterValues, ranging from 1 to 30, low to high).
    • Note: In a single report, if any element has cistern.name != NULL, the SDK will report cisterns as a whole and will not report waterValues. Therefore, do not mix values: either use cistern for all rooms, or use water_value for all rooms and set all cistern.name pointers to NULL.
  • Array index correspondence:
    • All array indices correspond one-to-one with the ids array.
    • ids[0] corresponds to sweep_mode[0] and param[0].
  • Memory management:
    • Sent by the app (SET command): Memory is managed by the SDK and will be automatically released after the callback function returns. If the application needs to process the data asynchronously (e.g., using it in another thread), it must copy the data (including all arrays and strings) to memory allocated by the application itself.
    • Reported by the device (response interface): All arrays (ids, param) and string memory (suction.name, cistern.name, and sweep_mode[i].name) are allocated and managed by the application itself. The SDK does not release this memory.

Set and query the selected zone cleaning

Feature: Users can select any polygonal area on the map for cleaning, and cleaning parameters can be configured independently for each area.

Data structure:

    /**
    * @brief Area coordinate point structure
    */
    typedef struct {
        int point_num; // Number of endpoints forming the area
        POINT_COOR_S* point; // Endpoint coordinates forming the restricted area
    } AREA_S;

    /**
    * @brief Zone parameters structure
    */
    typedef struct {
        STR_ELEMENT_S mode; // Cleaning mode
        CLEAN_PARAM_S param; // Cleaning parameters
        AREA_S area; // Area coordinates
        STR_ELEMENT_S cur_name; // Area name
    } ZONE_AREA_PARAM_S;
    /**
    * @brief Zone cleaning structure
    */
    typedef struct {
        int num; // Number of zones
        int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
        ZONE_AREA_PARAM_S* clean_zone; // Cleaning parameters for zone cleaning, each zone corresponds to one cleaning parameter
    } ZONE_CLEAN_S;

Struct description:

Field Type SET command processing QUERY command response Proactive reporting
num int Receive the number of zones Populate the number of zones cleaned in the last selected zone cleaning task Populate the number of zones cleaned in the current selected zone cleaning task
map_id int Not sent by the panel. Ignore this field Must populate the current map ID Must populate the current map ID
clean_zone Array of zone parameters Parse all zone parameters Populate the last zone parameters Populate the current zone parameters
clean_zone[i].mode String Cleaning mode (string format) Populate the cleaning mode strings Populate the cleaning mode strings
clean_zone[i].param Cleaning parameters Cleaning parameters for this area Populate cleaning parameters Populate cleaning parameters
clean_zone[i].param.valid int Parameter valid flag (reserved field) Populate the valid flag Populate the valid flag
clean_zone[i].param.suction String Suction level (string format) Populate the suction string Populate the suction string
clean_zone[i].param.cistern String Water volume level (string format) Populate the water volume string Populate the water volume string
clean_zone[i].param.water_value int Custom water volume value (1-30) Populate the water volume value Populate the water volume value
clean_zone[i].param.route_preference String Route preference (string format) Populate the route preference string Populate the route preference string
clean_zone[i].param.clean_direction String Cleaning direction (string format) Populate the cleaning direction string Populate the cleaning direction string
clean_zone[i].param.y_mop bool Y-shaped mopping switch Populate the Y-shaped mopping switch status Populate the Y-shaped mopping switch status
clean_zone[i].param.clean_cnt int Cleaning times Populate cleaning times Populate cleaning times
clean_zone[i].area Area structure The polygon coordinate point set for the zone Populate the area coordinates Populate the area coordinates
clean_zone[i].area.point_num int Number of polygon vertices (typically 4) Populate the number of vertices Populate the number of vertices
clean_zone[i].area.point Coordinate array Coordinates of each vertex of the polygon Populate the coordinates of each vertex Populate the coordinates of each vertex
clean_zone[i].cur_name String Zone name Populate the zone name Populate the zone name

Example:

// Example of SET command processing
static OPERATE_RET handle_zone_clean_set(ZONE_CLEAN_S* p_zone_clean)
{
    PR_DEBUG("Received zone cleaning set command, number of zones: %d", p_zone_clean->num);

    // Traverse all zones
    for(int i = 0; i < p_zone_clean->num; i++) {
        PR_DEBUG("Zone [%d]:", i);

        // Parse zone name
        if(p_zone_clean->clean_zone[i].cur_name.name) {
            PR_DEBUG("  Name: %d-%s",
                     p_zone_clean->clean_zone[i].cur_name.len,
                     p_zone_clean->clean_zone[i].cur_name.name);
        }

        // Parse the cleaning mode (string format, it needs to be converted to an enumeration)
        PR_DEBUG("  Cleaning mode: %d-%s",
                 p_zone_clean->clean_zone[i].mode.len,
                 p_zone_clean->clean_zone[i].mode.name);
        // TODO: Convert the string to an enumeration value
        // int mode_enum = convert_sweep_mode_str_to_enum(&p_zone_clean->clean_zone[i].mode);//Pseudocode. You need to implement it yourself

        // Parse the cleaning parameters
        CLEAN_PARAM_S* param = &p_zone_clean->clean_zone[i].param;

        PR_DEBUG("  Suction: %d-%s", param->suction.len, param->suction.name);
        PR_DEBUG("  Water volume: %d-%s", param->cistern.len, param->cistern.name);
        PR_DEBUG("  Y-shaped mopping: %d", param->y_mop);
        PR_DEBUG("  Cleaning count: %d", param->clean_cnt);


        // Parse the coordinates of the zone (polygon vertices)
        AREA_S* area = &p_zone_clean->clean_zone[i].area;
        PR_DEBUG("  Number of vertices: %d", area->point_num);
        for(int j = 0; j < area->point_num; j++) {
            PR_DEBUG("    Vertex [%d]: (%d, %d)", j,
                     area->point[j].x, area->point[j].y);
        }

        // TODO: Save the zone cleaning configuration
        // save_zone_clean_config(i, &p_zone_clean->clean_zone[i]);//Pseudocode. You need to implement it yourself
    }

    // Start the zone cleaning task
    // start_zone_clean_task(p_zone_clean->clean_zone, p_zone_clean->num);//Pseudocode. You need to implement it yourself

    // Report the result after processing
    ZONE_CLEAN_S report_data;
    report_data.num = p_zone_clean->num;
    report_data.map_id = get_current_map_id();  // Get the current map ID
    report_data.clean_zone = p_zone_clean->clean_zone;

    return ty_rvc_zone_clean_data_response(&report_data, 0);  // 0 indicates success
}

// Example of QUERY command response
static OPERATE_RET handle_zone_clean_query(void)
{
    PR_DEBUG("Received zone cleaning query command");

    // Get the last zone cleaning configuration from the device
    ZONE_CLEAN_S query_data;
    ZONE_AREA_PARAM_S zones[5];     // Assume a maximum of 5 zones
    POINT_COOR_S zone_points[5][4]; // Each zone has 4 vertices (rectangles)

    query_data.num = get_last_zone_clean_count();  // Get the last zone count. Pseudocode. You need to implement it yourself
    query_data.map_id = get_current_map_id();      // Map ID must be filled in. Pseudocode. You need to implement it yourself
    query_data.clean_zone = zones;

    // Populate the data for each zone
    for(int i = 0; i < query_data.num; i++) {
        // Populate the zone name
        zones[i].cur_name.name = "Zone 1";
        zones[i].cur_name.len = strlen(zones[i].cur_name.name);

        Populate cleaning mode (string format)
        zones[i].mode.name = "both_work";  // For example: "only_sweep","only_mop","both_work"
        zones[i].mode.len = strlen(zones[i].mode.name);

        // Populate cleaning parameters
        zones[i].param.suction.name = "normal";  // For example: "closed","gentle","normal","strong","max"
        zones[i].param.suction.len = strlen(zones[i].param.suction.name);
        zones[i].param.cistern.name = "middle";  // For example: "closed","low","middle","high"
        zones[i].param.cistern.len = strlen(zones[i].param.cistern.name);
        zones[i].param.water_value = 0; // When using cisterns for reporting, water_value will not be reported. To report waterValues, you need to set cistern.name to NULL and set water_value(1-30)
        zones[i].param.route_preference.name = "standard"; // For example: "standard","fast","deep"
        zones[i].param.route_preference.len = strlen(zones[i].param.route_preference.name);
        zones[i].param.clean_direction.name = "auto"; // For example: "auto","horizontal","vertical"
        zones[i].param.clean_direction.len = strlen(zones[i].param.clean_direction.name);
        zones[i].param.y_mop = true;
        zones[i].param.clean_cnt = 2;

        // Populate the coordinates of the zone (4 vertices of the rectangle)
        zones[i].area.point_num = 4;
        zones[i].area.point = zone_points[i];

        // Populate the rectangle with the coordinates of its 4 vertices (clockwise or counterclockwise)
        zone_points[i][0].x = 100; zone_points[i][0].y = 100;  // Bottom left
        zone_points[i][1].x = 300; zone_points[i][1].y = 100;  // Bottom right
        zone_points[i][2].x = 300; zone_points[i][2].y = 300;  // Top right
        zone_points[i][3].x = 100; zone_points[i][3].y = 300;  // Top left
    }

    return ty_rvc_zone_clean_data_response(&query_data, 0);
}

Things to note:

  • The SET command sent by the panel does not contain the map_id field. The device must determine the current map on its own.
  • When reporting data, the device must populate the map_id field. Otherwise, the panel will be unable to match the data with the correct map.
  • Shape of the zone:
    • Typically, it is a rectangular area (with 4 vertices).
    • Support arbitrary polygons, with the number of vertices determined by point_num.
    • Vertex coordinates must be arranged in order (clockwise or counterclockwise).
  • Coordinate system:
    • Use the robot coordinate system (same as virtual walls and no-go areas).
    • Coordinate type: INT32 integer values, representing the number of grid units.
    • Actual position calculation: Actual distance = Number of grid cells × Grid resolution.
    • For more information, see Map coordinate system.
  • String processing:
    • mode, cur_name, suction, cistern, route_preference, and clean_direction are all in string format.
    • Each string requires populating both the name pointer and the len (length).
    • String memory is managed by the application. Pay attention to the memory lifecycle.
  • String to enum mapping:
    • Cleaning mode example: "only_sweep" / "only_mop" / "both_work"
    • Suction level example: "closed" / "gentle" / "normal" / "strong" / "max"
    • Water volume level example: "closed" / "low" / "middle" / "high"
    • Route preference example: "standard" / "fast" / "deep"
    • Cleaning direction example: "auto" / "horizontal" / "vertical"
  • Water volume reporting rules:
    • cistern.name != NULL: The SDK will prioritize reporting by "enumerated water volume level" (corresponding to protocol field cisterns).
    • cistern.name == NULL: The SDK will report by "custom water volume value" (corresponding to protocol field waterValues, ranging from 1 to 30, low to high).
    • Note: In a single report, if any element has cistern.name != NULL, the SDK will report cisterns as a whole and will not report waterValues. Therefore, do not mix values: either use cistern for all rooms, or use water_value for all rooms and set all cistern.name pointers to NULL.
  • Memory management:
    • Sent by the app (SET command): Memory is managed by the SDK and will be automatically released after the callback function returns. If the application needs to process the data asynchronously (e.g., using it in another thread), it must copy the data (including all arrays and strings) to memory allocated by the application itself.
    • Reported by the device (response interface): All arrays (buff and nested coordinate arrays) and string memory (mode.name, suction.name, cistern.name, and cur_name.name) are allocated and managed by the application itself. The SDK does not release this memory.

Set and query the selected spot cleaning

Feature: Users specify a spot on the map, and the robot will clean a fixed area (usually one square meter) centered on that spot.

Data structure:

    /**
    * @brief Spot cleaning structure
    */
    typedef struct {
        int num; // Number of spot cleanings
        int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
        POINT_COOR_S* points; // List of spot coordinates, each spot area corresponds to one points element
        STR_ELEMENT_S* mode; // Cleaning mode for the zone, each area corresponds to one cleaning mode, mode[0] corresponds to point[0] area
        CLEAN_PARAM_S* param; // Cleaning parameters for selected areas, param[0] corresponds to point[0] area, if not set valid is 0
    } SPOT_CLEAN_S;

Struct description:

Field Type SET command processing QUERY command response Proactive reporting
num int Receive the number of spots (typically 1) Populate the number of spots cleaned in the last selected spot cleaning task Populate the number of spots cleaned in the current selected spot cleaning task
map_id int Not sent by the panel. Ignore this field Must populate the current map ID Must populate the current map ID
points Coordinate array Parse the coordinates of a spot (center point) Populate the coordinates of the last spot Populate the coordinates of the current spot
mode String array Parse the cleaning mode (string format) Populate the cleaning mode strings Populate the cleaning mode strings
param Cleaning parameter array Parse cleaning parameters Populate cleaning parameters Populate cleaning parameters
param[i].valid int Parameter valid flag (reserved field) Populate the valid flag Populate the valid flag
param[i].suction String Suction level (string format) Populate the suction string Populate the suction string
param[i].cistern String Water volume level (string format) Populate the water volume string Populate the water volume string
param[i].water_value int Custom water volume value (1-30) Populate the water volume value Populate the water volume value
param[i].route_preference String Route preference (string format) Populate the route preference string Populate the route preference string
param[i].clean_direction String Cleaning direction (string format) Populate the cleaning direction string Populate the cleaning direction string
param[i].y_mop bool Y-shaped mopping switch Populate the Y-shaped mopping switch status Populate the Y-shaped mopping switch status
param[i].clean_cnt int Cleaning times Populate cleaning times Populate cleaning times

Example:

// Example of SET command processing
static OPERATE_RET handle_spot_clean_set(SPOT_CLEAN_S* p_spot_clean)
{
    PR_DEBUG("Received spot cleaning set command, number of spots: %d", p_spot_clean->num);

    // Typically, spot cleaning num is fixed at 1
    if(p_spot_clean->num != 1) {
        PR_WARN("Spot cleaning number abnormal: %d, expected to be 1", p_spot_clean->num);
    }

    // Parse spot coordinates
    for(int i = 0; i < p_spot_clean->num; i++) {
        PR_DEBUG("Spot [%d] coordinates: (%d, %d)", i,
                 p_spot_clean->points[i].x,
                 p_spot_clean->points[i].y);

        // Parse the cleaning mode (string format, it needs to be converted to an enumeration)
        if(p_spot_clean->mode) {
            PR_DEBUG("  Cleaning mode: %d-%s",
                     p_spot_clean->mode[i].len,
                     p_spot_clean->mode[i].name);
            //  TODO: Convert the string to an enumeration value
            // int mode_enum = convert_sweep_mode_str_to_enum(&p_spot_clean->mode[i]);//Pseudocode. You need to implement it yourself
        }

        // Parse the cleaning parameters
        PR_DEBUG("  Suction: %d-%s",
                    p_spot_clean->param[i].suction.len,
                    p_spot_clean->param[i].suction.name);
        PR_DEBUG("  Water volume: %d-%s",
                    p_spot_clean->param[i].cistern.len,
                    p_spot_clean->param[i].cistern.name);
        PR_DEBUG("  Y-shaped mopping: %d", p_spot_clean->param[i].y_mop);
        PR_DEBUG("   Cleaning count: %d", p_spot_clean->param[i].clean_cnt);

        // TODO: Save the spot cleaning configuration
        // save_spot_clean_config(&p_spot_clean->points[i], ...);//Pseudocode. You need to implement it yourself
    }

    // Start the spot cleaning task (clean a 1 square meter area centered on this point)
    // start_spot_clean_task(&p_spot_clean->points[0]);//Pseudocode. You need to implement it yourself

    // Report the result after processing
    SPOT_CLEAN_S report_data;
    report_data.num = p_spot_clean->num;
    report_data.map_id = get_current_map_id();  // Get the current map ID. Pseudocode. You need to implement it yourself
    report_data.points = p_spot_clean->points;
    report_data.mode = p_spot_clean->mode;
    report_data.param = p_spot_clean->param;

    return ty_rvc_spot_clean_data_response(&report_data, 0);  // 0 indicates success
}

// Example of QUERY command response
static OPERATE_RET handle_spot_clean_query(void)
{
    PR_DEBUG("Received spot cleaning query command");

    // Get the last spot cleaning configuration from the device
    SPOT_CLEAN_S query_data;
    POINT_COOR_S spot_point;        // Spot coordinates
    STR_ELEMENT_S spot_mode;        // Cleaning mode
    CLEAN_PARAM_S spot_param;       // Cleaning parameter

    // Spot cleaning usually only has one spot
    query_data.num = 1;
    query_data.map_id = get_current_map_id();  // Map ID must be filled in. Pseudocode. You need to implement it yourself
    query_data.points = &spot_point;
    query_data.mode = &spot_mode;
    query_data.param = &spot_param;

    // Populate the spot coordinates (the position of the last spot cleaning)
    spot_point.x = get_last_spot_x();  // For example: 150,Pseudocode. You need to implement it yourself
    spot_point.y = get_last_spot_y();  // For example: 200,Pseudocode. You need to implement it yourself

    Populate cleaning mode (string format)
    spot_mode.name = "only_sweep";  // For example: "only_sweep","only_mop","both_work"
    spot_mode.len = strlen(spot_mode.name);

    // Populate cleaning parameters (Report the parameters sent by the panel)
    spot_param.suction.name = "normal";  // For example: "closed","gentle","normal","strong","max"
    spot_param.suction.len = strlen(spot_param.suction.name);
    spot_param.cistern.name = "middle";  // For example: "closed","low","middle","high"
    spot_param.cistern.len = strlen(spot_param.cistern.name);
    spot_param.water_value = 0; // If cistern has a value, set water_value to 0
    spot_param.route_preference.name = "standard"; // For example: "standard","fast","deep"
    spot_param.route_preference.len = strlen(spot_param.route_preference.name);
    spot_param.clean_direction.name = "auto"; // For example: "auto","horizontal","vertical"
    spot_param.clean_direction.len = strlen(spot_param.clean_direction.name);
    spot_param.y_mop = false;
    spot_param.clean_cnt = 1;

    return ty_rvc_spot_clean_data_response(&query_data, 0);
}

Things to note:

  • The SET command sent by the panel does not contain the map_id field. The device must determine the current map on its own.
  • When reporting data, the device must populate the map_id field. Otherwise, the panel will be unable to match the data with the correct map.
  • Spot cleaning characteristics:
    • num is usually fixed at 1, representing a single fixed spot.
    • Cleaning area: A 1-square-meter area centered on the points coordinates.
    • Cleaning radius: Usually around 0.5 meters (adjustable according to the actual project).
  • Coordinate system:
    • Use the robot coordinate system (same as virtual walls and no-go areas).
    • Coordinate type: INT32 integer values, representing the number of grid units.
    • Actual position calculation: Actual distance = Number of grid cells × Grid resolution.
    • For more information, see Map coordinate system.
  • String processing:
    • mode, suction, cistern, route_preference, and clean_direction are all in string format.
    • Each string requires populating both the name pointer and the len (length).
    • String memory is managed by the application. Pay attention to the memory lifecycle.
  • String to enum mapping:
    • Cleaning mode example: "only_sweep" / "only_mop" / "both_work"
    • Suction level example: "closed" / "gentle" / "normal" / "strong" / "max"
    • Water volume level example: "closed" / "low" / "middle" / "high"
    • Route preference example: "standard" / "fast" / "deep"
    • Cleaning direction example: "auto" / "horizontal" / "vertical"
  • Water volume reporting rules:
    • cistern.name != NULL: The SDK will prioritize reporting by "enumerated water volume level" (corresponding to protocol field cisterns).
    • cistern.name == NULL: The SDK will report by "custom water volume value" (corresponding to protocol field waterValues, ranging from 1 to 30, low to high).
    • Note: In a single report, if any element has cistern.name != NULL, the SDK will report cisterns as a whole and will not report waterValues. Therefore, do not mix values: either use cistern for all rooms, or use water_value for all rooms and set all cistern.name pointers to NULL.
  • Memory management:
    • Sent by the app (SET command): Memory is managed by the SDK and will be automatically released after the callback function returns. If the application needs to process the data asynchronously (e.g., using it in another thread), it must copy the data (including all arrays and strings) to memory allocated by the application itself.
    • Reported by the device (response interface): All arrays (buff) and string memory (mode.name, suction.name, and cistern.name) are allocated and managed by the application itself. The SDK does not release this memory.

Set and query a schedule

Feature: Users can set up scheduled cleaning tasks, which can be executed once or repeatedly (weekly). Each task can be configured with the rooms to be cleaned and cleaning parameters.

Data structure:

    /**
    * @brief Scheduled time structure
    */
    typedef struct {
        char hour; // Scheduled hour value
        char min; // Scheduled minute value
    } SCHEDULE_TIME_S;

    /**
    * @brief Scheduled information structure
    */
    typedef struct {
        unsigned char active; // Scheduled switch, 0 for off, 1 for on
        unsigned char cycle; // Whether to loop the schedule, value 0 indicates a one-time schedule, non-zero indicates a looping schedule, cycle's bit0~bit6 correspond to Monday~Sunday
        SCHEDULE_TIME_S start_time; // Scheduled effective time
        int num; // Number of rooms corresponding to each schedule
        bool customed_switch; // the switch of the clean param,when it is true, the clean param of the app send is unvalid
        int* ids; // Room ID categories corresponding to each schedule
        STR_ELEMENT_S* mode; // List of cleaning modes, mode[0] corresponds to ids[0]
        CLEAN_PARAM_S* param; // List of cleaning parameters, param[0] corresponds to ids[0]
    } SCHEDULE_PARAM_S;
    /**
    * @brief Local schedule structure
    */
    typedef struct {
        int num; // Number of schedules
        int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
        SCHEDULE_PARAM_S* time_sets; // Parameter categories for each schedule
    } SCHEDULE_S;

Struct description:

Field Type SET command processing QUERY command response Proactive reporting
num int Receive the number of scheduled tasks Populate the current number of all scheduled tasks Populate the current number of all scheduled tasks
map_id int Not sent by the panel. Ignore this field Must populate the current map ID Must populate the current map ID
time_sets Array of schedule parameters Parse all scheduled task parameters Populate all scheduled tasks Populate all scheduled tasks
time_sets[i].active unsigned char Task status (0: off, 1: on) Populate task on/off status Populate task on/off status
time_sets[i].cycle unsigned char Recurring mode. 0: only once. Other values: recurring Populate recurring mode Populate recurring mode
time_sets[i].start_time Time structure Task execution time (hour:minute) Populate execution time Populate execution time
time_sets[i].num int The number of rooms to be cleaned in this task Populate the number of rooms Populate the number of rooms
time_sets[i].customed_switch bool Custom parameter on/off status Populate the custom on/off status Populate the custom on/off status
time_sets[i].ids int array IDs of rooms to be cleaned Populate room IDs Populate room IDs
time_sets[i].mode String array Cleaning mode for each room (string) Populate cleaning mode Populate cleaning mode
time_sets[i].param Cleaning parameter array Cleaning parameters for each room Populate cleaning parameters Populate cleaning parameters

Example:

// Example of SET command processing
static OPERATE_RET handle_schedule_set(SCHEDULE_S* p_schedule)
{
    PR_DEBUG("Received scheduled task setting command, number of scheduled tasks: %d", p_schedule->num);

    // Traverse all scheduled tasks
    for(int i = 0; i < p_schedule->num; i++) {
        SCHEDULE_PARAM_S* task = &p_schedule->time_sets[i];

        PR_DEBUG("Scheduled task [%d]:", i);
        PR_DEBUG("  On/off status: %d", task->active);
        PR_DEBUG("  Execution time: %02d:%02d", task->start_time.hour, task->start_time.min);

        // Parse the cycle pattern
        if(task->cycle == 0) {
            PR_DEBUG("  Mode: Single execution");
        } else {
            PR_DEBUG("  Mode: Cycle execution");
            // bit0 to bit6 of cycle correspond to Monday to Sunday
            for(int day = 0; day < 7; day++) {
                if(task->cycle & (1 << day)) {
                    PR_DEBUG("    Week %d", day + 1);
                }
            }
        }

        // Parse the rooms to be cleaned
        PR_DEBUG("  Number of rooms to clean: %d", task->num);
        for(int j = 0; j < task->num; j++) {
            PR_DEBUG("    Room ID[%d]: %d", j, task->ids[j]);
        }

        // Check if custom cleaning parameters are used
        if(task->customed_switch) {
            PR_DEBUG("  Use custom cleaning parameters:");

            // Parse the cleaning mode and parameters for each room
            for(int j = 0; j < task->num; j++) {
                if(task->mode) {
                    PR_DEBUG("    Room [%d] Cleaning mode: %.*s", j,
                             task->mode[j].len, task->mode[j].name);
                }

                if(task->param) {
                    PR_DEBUG("    Room [%d] Suction: %.*s", j,
                             task->param[j].suction.len,
                             task->param[j].suction.name);
                    PR_DEBUG("    Room [%d] Water volume: %.*s", j,
                             task->param[j].cistern.len,
                             task->param[j].cistern.name);
                    PR_DEBUG("    Room [%d] Y-shaped mopping: %d", j, task->param[j].y_mop);
                    PR_DEBUG("    Room [%d] Cleaning count: %d", j, task->param[j].clean_cnt);
                }
            }
        } else {
            PR_DEBUG("  Using default cleaning parameters");
        }

        // TODO: Save the scheduled task to device storage
        // save_schedule_task(i, task);//Pseudocode. You need to implement it yourself
    }

    // Report the result after processing
    SCHEDULE_S report_data;
    report_data.num = p_schedule->num;
    report_data.map_id = get_current_map_id();  // Get the current map ID. Pseudocode. You need to implement it yourself
    report_data.time_sets = p_schedule->time_sets;

    return ty_rvc_schedule_data_response(&report_data, 0);  // 0 indicates success
}

// Example of QUERY command response
static OPERATE_RET handle_schedule_query(void)
{
    PR_DEBUG("Received scheduled task query command");

    // Get all scheduled tasks from the device
    SCHEDULE_S query_data;
    SCHEDULE_PARAM_S tasks[10];     // Assume a maximum of 10 scheduled tasks

    query_data.num = get_schedule_count();     // Get the number of scheduled tasks. Pseudocode. You need to implement it yourself
    query_data.map_id = get_current_map_id();  // Map ID must be filled in. Pseudocode. You need to implement it yourself
    query_data.time_sets = tasks;

    // Populate the data for each scheduled task
    for(int i = 0; i < query_data.num; i++) {
        // Populate the task on/off and time
        tasks[i].active = 1;  // 1=On
        tasks[i].start_time.hour = 9;   // 09:00
        tasks[i].start_time.min = 0;

        //Populate the cycle mode (bit0 to bit6 corresponds to Monday to Sunday)
        tasks[i].cycle = 0x1F;  // 0b00011111 = Monday to Friday

        // Populate the rooms to clean
        tasks[i].num = 2;  // Clean 2 rooms

        // Allocate and Populate the room ID array
        static int room_ids[10][10];  // Static array. It should be dynamically allocated in practice
        tasks[i].ids = room_ids[i];
        room_ids[i][0] = 1;  // Room 1
        room_ids[i][1] = 2;  // Room 2

        // Custom parameter switch
        tasks[i].customed_switch = true;

        // Allocate and Populate the cleaning mode array
        static STR_ELEMENT_S modes[10][10];
        tasks[i].mode = modes[i];
        modes[i][0].name = "both_work";
        modes[i][0].len = strlen(modes[i][0].name);
        modes[i][1].name = "only_sweep";
        modes[i][1].len = strlen(modes[i][1].name);

        // Allocate and Populate the cleaning parameter array
        static CLEAN_PARAM_S params[10][10];
        tasks[i].param = params[i];
        for(int j = 0; j < tasks[i].num; j++) {
            params[i][j].suction.name = "strong";
            params[i][j].suction.len = strlen(params[i][j].suction.name);
            params[i][j].cistern.name = "high";
            params[i][j].cistern.len = strlen(params[i][j].cistern.name);
            params[i][j].y_mop = false;
            params[i][j].clean_cnt = 1;
        }
    }

    return ty_rvc_schedule_data_response(&query_data, 0);
}

Things to note:

  • The SET command sent by the panel does not contain the map_id field. The device must determine the current map on its own.
  • When reporting data, the device must populate the map_id field. Otherwise, the panel will be unable to match the data with the correct map.
  • Description of recurring mode:
    • cycle = 0: One-time task. It automatically becomes invalid after execution once.
    • cycle != 0: Recurring task. bit0 to bit6 correspond to Monday through Sunday.~~
    • Example: cycle = 0x7F (binary 0b01111111) means executed every day.
    • Example: cycle = 0x1F (binary 0b00011111) means executed from Monday to Friday.
    • Example: cycle = 0x60 (binary 0b01100000) means executed on Saturday and Sunday.
  • Time format:
    • start_time.hour: 0 to 23 (24-hour clock).
    • start_time.min: 0 to 59.
  • Custom parameter on/off status:
    • customed_switch = false: Use the device’s default cleaning parameters, ignoring the mode and param fields.
    • customed_switch = true: Use the custom cleaning parameters sent by the panel.
  • String processing:
    • mode, suction, cistern, route_preference, and clean_direction are all in string format.
    • Each string requires populating both the name pointer and the len (length).
  • String to enum mapping:
    • Cleaning mode example: "only_sweep" / "only_mop" / "both_work"
    • Suction level example: "closed" / "gentle" / "normal" / "strong" / "max"
    • Water volume level example: "closed" / "low" / "middle" / "high"
    • Route preference example: "standard" / "fast" / "deep"
    • Cleaning direction example: "auto" / "horizontal" / "vertical"
  • Water volume reporting rules:
    • cistern.name != NULL: The SDK will prioritize reporting by "enumerated water volume level" (corresponding to protocol field cisterns).
    • cistern.name == NULL: The SDK will report by "custom water volume value" (corresponding to protocol field waterValues, ranging from 1 to 30, low to high).
    • Note: In a single report, if any element has cistern.name != NULL, the SDK will report cisterns as a whole and will not report waterValues. Therefore, do not mix values: either use cistern for all rooms, or use water_value for all rooms and set all cistern.name pointers to NULL.
  • Array index correspondence:
    • All array indices correspond one-to-one with the ids array.
    • ids[0] corresponds to mode[0] and param[0].
  • Memory management:
    • Sent by the app (SET command): Memory is managed by the SDK and will be automatically released after the callback function returns. If the application needs to process the data asynchronously (e.g., using it in another thread), it must copy the data (including all arrays and strings) to memory allocated by the application itself.
    • Reported by the device (response interface): All arrays (time_sets, ids, mode, and param) and string memory (mode[i].name, suction.name, and cistern.name) are allocated and managed by the application itself. The SDK does not release this memory.

Set and query a DND period

Feature: Users can set a time period during which the robot will not automatically start cleaning tasks (such as scheduled tasks), and sound and lights will be turned off to avoid disturbing the user’s rest.

Data structure:

    /**
    * @brief Scheduled time structure
    */
    typedef struct {
        char hour; // Scheduled hour value
        char min; // Scheduled minute value
    } SCHEDULE_TIME_S;

    /**
    * @brief Do not disturb settings structure
    */
    typedef struct {
        unsigned char active; // Do not disturb switch, 0 for off, 1 for on
        unsigned char other_day; // Effective time, 0 for today, 1 for the next day
        SCHEDULE_TIME_S start_time; // Do not disturb start time
        SCHEDULE_TIME_S end_time; // Do not disturb end time
    } QUIET_HOURS_S;

Struct description:

Field Type SET command processing QUERY command response Proactive reporting
active unsigned char DND mode on/off status. 0: off, 1: on Populate DND mode on/off status Populate DND mode on/off status
other_day unsigned char End date. 0: current day, 1: the next day Populate date flags Populate date flags
start_time Time structure DND start time (hour:minute) Populate start time Populate start time
start_time.hour char Start time – hour (0–23) Populate hours Populate hours
start_time.min char Start time – minute (0–59) Populate minutes Populate minutes
end_time Time structure DND end time (hour:minute) Populate end time Populate end time
end_time.hour char End time – hour (0–23) Populate hours Populate hours
end_time.min char End time – minute (0–59) Populate minutes Populate minutes

Example:

// Example of SET command processing
static OPERATE_RET handle_quiet_hours_set(QUIET_HOURS_S* p_quiet_hours)
{
    PR_DEBUG("Received DND timer setting command");

    // Parse DND on/off status
    PR_DEBUG("DND mode: %s", p_quiet_hours->active ? "On" : "Off");

    // Parse DND time period
    PR_DEBUG("Start time: %02d:%02d",
             p_quiet_hours->start_time.hour,
             p_quiet_hours->start_time.min);
    PR_DEBUG("End time: %02d:%02d %s",
             p_quiet_hours->end_time.hour,
             p_quiet_hours->end_time.min,
             p_quiet_hours->other_day ? "(Next Day)" : "(Same Day)");

    // Check for spanning-day scenarios
    if(p_quiet_hours->other_day == 1) {
        // Spanning-day scenarios: For example, 22:00 to 08:00 the next day
        PR_DEBUG("DND period spans days");
    } else {
        // Same-day scenario: For example, 12:00 to 14:00
        PR_DEBUG("DND period is on the same day");
    }

    // Calculate DND duration (example)
    int start_minutes = p_quiet_hours->start_time.hour * 60 + p_quiet_hours->start_time.min;
    int end_minutes = p_quiet_hours->end_time.hour * 60 + p_quiet_hours->end_time.min;
    int duration;

    if(p_quiet_hours->other_day == 1) {
        // Span a day: From start to midnight + from midnight to end
        duration = (24 * 60 - start_minutes) + end_minutes;
    } else {
        duration = end_minutes - start_minutes;
    }
    PR_DEBUG("DND duration: %d minutes (%d hours %d minutes)",
             duration, duration / 60, duration % 60);

    // TODO: Save DND settings to device storage
    // save_quiet_hours_config(p_quiet_hours);//Pseudocode. You need to implement it yourself

    // TODO: Enable/disable DND mode
    if(p_quiet_hours->active) {
        // enable_quiet_mode(p_quiet_hours);//Pseudocode. You need to implement it yourself
    } else {
        // disable_quiet_mode();//Pseudocode. You need to implement it yourself
    }

    // Report the result after processing
    QUIET_HOURS_S report_data = *p_quiet_hours;

    return ty_rvc_quiet_hours_data_response(&report_data, 0);  // 0 indicates success
}

// Example of QUERY command response
static OPERATE_RET handle_quiet_hours_query(void)
{
    PR_DEBUG("Received DND timer query command");

    // Get current DND settings from device
    QUIET_HOURS_S query_data;

    // Populate DND on/off status
    query_data.active = get_quiet_mode_status();  // 0=off, 1=on. Pseudocode. You need to implement it yourself

    // Populate DND time period
    query_data.start_time.hour = 22;  // 22:00
    query_data.start_time.min = 0;

    query_data.end_time.hour = 8;     // 08:00
    query_data.end_time.min = 0;

    // Check if it spans a day (if the end time is earlier than the start time)
    if(query_data.end_time.hour < query_data.start_time.hour ||
       (query_data.end_time.hour == query_data.start_time.hour &&
        query_data.end_time.min < query_data.start_time.min)) {
        query_data.other_day = 1;  // Spanning a day: 22:00 to 08:00 the next day
    } else {
        query_data.other_day = 0;  // Same day: 12:00 to 14:00
    }

    return ty_rvc_quiet_hours_data_response(&query_data, 0);
}

Things to note:

  • DND mode:
    • During the DND period, the device will not automatically execute scheduled tasks. Light effects and sounds will be turned off.
    • The user can manually start cleaning (via app/remote control/voice control).
    • The device can charge and remain on standby as expected.
    • You can define the exact behavior of DND mode based on the specific product requirements.
  • Time format:
    • hour: 0 to 23 (24-hour clock).
    • min: 0 to 59.
  • Cross-day criteria:
    • other_day = 0: Same day (for example, 12:00 to 14:00).
    • other_day = 1: Crosses midnight (for example, 22:00 to 08:00 the next day).
    • Logic: If the end time is earlier than the start time, it is considered a cross-day period.
  • Common scenarios:
    • Nap time DND: 12:00 to 14:00 (other_day = 0).
    • Nighttime DND: 22:00 to 08:00 the next day (other_day = 1)
    • Full-day DND: 00:00 to 23:59 (other_day = 0)
  • Implementation recommendations:
    • The device needs to determine in real-time whether the current time falls within the DND period.
    • When DND is enabled, automatic triggering of intrusive tasks must be prohibited.
    • When DND is disabled, normal operation should resume immediately.
  • All fields in the structure are used and must be fully parsed and reported.

Split an area on the map

Feature: Users can split a room into two separate rooms by drawing a divider on the map, which facilitates zoning management and cleaning.

Data structure:

    /**
    * @brief Division line coordinate structure
    */
    typedef struct {
        POINT_COOR_S points[2]; // Endpoints of the line
    } DIVIDE_LINE_S;

    /**
    * @brief Manual partition structure
    */
    typedef struct {
        int num; // Number of partitions to be divided
        int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
        int* ids; // List of IDs to be divided
        DIVIDE_LINE_S* divi_line; // Partition division line coordinates, each room ID corresponds to one division line, divi_line[0] corresponds to ids[0]
    } PART_DIVI_S;

Struct description:

Field Type SET command processing Description
num int Receive the number of rooms to be split (currently only 1 is supported) It is typically fixed at 1, indicating a single room division.
map_id int Not sent by the panel. Ignore this field When a device reports a map, the current map ID must be populated.
ids int array Receive the ID of the room to be split Currently, only a single room ID is supported.
divi_line Divider array Receive the coordinates of the divider Each room ID corresponds to a divider
divi_line[i].points[0] Coordinates Starting coordinates of the divider (x0, y0) The first endpoint of the divider
divi_line[i].points[1] Coordinates Ending coordinates of the divider (x1, y1) The second endpoint of the divider

Example:

// Example of SET command processing
static OPERATE_RET handle_part_division_set(PART_DIVI_S* p_part_divi)
{
    PR_DEBUG("Received map area split command, number of areas: %d", p_part_divi->num);

    // Currently only single room splitting is supported
    if(p_part_divi->num != 1) {
        PR_ERR("Batch splitting is not supported, currently only single room splitting is supported");
        return ty_rvc_part_division_data_response(p_part_divi, -1);
    }

    // Parse the room ID to be split
    int room_id = p_part_divi->ids[0];
    PR_DEBUG("Room ID to be split: %d", room_id);

    // Check if the room ID is valid
    if(!is_valid_room_id(room_id)) {  //Pseudocode. You need to implement it yourself
        PR_ERR("Invalid room ID: %d", room_id);
        return ty_rvc_part_division_data_response(p_part_divi, -1);
    }

    // Parse the coordinates of the dividing line
    DIVIDE_LINE_S* line = &p_part_divi->divi_line[0];
    PR_DEBUG("Start point of the dividing line: (%d, %d)", line->points[0].x, line->points[0].y);
    PR_DEBUG("End point of the dividing line: (%d, %d)", line->points[1].x, line->points[1].y);

    // TODO: Execute the room splitting algorithm
    // 1. Verify whether the dividing line passes through the room area
    // 2. Calculate the two new room areas after division
    // 3. Update the map data structure
    // 4. Assign new room IDs

    int result = perform_room_division(room_id, line);  //Room splitting algorithm (pseudocode). You need to implement it yourself
    if(result != 0) {
        PR_ERR("Room splitting failed");
        return ty_rvc_part_division_data_response(p_part_divi, -1);
    }

    // Report the result after successful splitting
    PART_DIVI_S report_data;
    report_data.num = p_part_divi->num;
    report_data.map_id = get_current_map_id();  // Map ID must be filled in
    report_data.ids = p_part_divi->ids;
    report_data.divi_line = p_part_divi->divi_line;

    return ty_rvc_part_division_data_response(&report_data, 0);
}

Things to note:

  • Limitations:
    • Only single-room splitting is supported (num = 1).
    • It is not possible to split multiple rooms in bulk.
  • The SET command sent by the panel does not contain the map_id field. The device must determine the current map on its own.
  • When reporting data, the device must populate the map_id field. Otherwise, the panel will be unable to match the data with the correct map.
  • Divider requirements:
    • The divider must pass through the interior of the room area.
    • The divider cannot merely touch the room boundary.
    • The divider must divide the room into two valid areas.
  • Coordinate system:
    • The divider coordinates use the robot coordinate system.
    • Coordinate type: INT32 integer values, representing the number of grid units.
    • Actual position calculation: Actual distance = Number of grid cells × Grid resolution.
  • Post-dividing processing:
    • The original room ID becomes invalid, and two new room IDs are generated.
    • Room names must be reassigned (for example, "Room 1-1", "Room 1-2").
    • Room properties (including cleaning parameters and cleaning order) can either be inherited or reset.
    • Map data must be updated and persistently saved.
  • ⚠️ Room property reporting is mandatory:
    • After the split operation is completed, you must immediately call ty_rvc_room_property_data_response() to report the latest room properties.
    • The report should include complete information for all rooms after the split: room IDs, room names, and cleaning parameters.
    • If the information is not reported, the app panel will not show the newly split room information.
  • Response status code:
    • 0: The room was split successfully.
    • -1: Failed to split the room.
  • This is a response interface for SET commands only, and it has no QUERY function.

Merge areas on the map

Feature: Users can merge two adjacent rooms into one room to simplify room management or correct overly divided rooms.

Data structure:

    /**
    * @brief Manual area merge structure
    */
    typedef struct {
        int num; // Number of partitions to be merged
        int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
        int* ids; // List of IDs to be merged
    } PART_MERGE_S;

Struct description:

Field Type SET command processing Description
num int Receive the number of rooms to be merged (currently only 2 is supported) It is typically fixed at 2, indicating two rooms are merged.
map_id int Not sent by the panel. Ignore this field When a device reports a map, the current map ID must be populated.
ids int array Receive the IDs of the rooms to be merged Contain IDs of two adjacent rooms

Example:

// Example of SET command processing
static OPERATE_RET handle_part_merge_set(PART_MERGE_S* p_part_merge)
{
    PR_DEBUG("Received map area merge command, number of areas to merge: %d", p_part_merge->num);

    // Currently only two room merges are supported
    if(p_part_merge->num != 2) {
        PR_ERR("Unsupported merge quantity, currently only two room merges are supported");
        return ty_rvc_part_merge_data_response(p_part_merge, -1);
    }

    // Parse the IDs of the two rooms to be merged
    int room_id1 = p_part_merge->ids[0];
    int room_id2 = p_part_merge->ids[1];
    PR_DEBUG("Rooms to be merged: %d + %d", room_id1, room_id2);

    // Check if room ID is valid
    if(!is_valid_room_id(room_id1) || !is_valid_room_id(room_id2)) {  //Pseudocode. You need to implement it yourself
        PR_ERR("Invalid room ID: %d or %d", room_id1, room_id2);
        return ty_rvc_part_merge_data_response(p_part_merge, -1);
    }

    // Check if two rooms are adjacent
    if(!are_rooms_adjacent(room_id1, room_id2)) {  //Pseudocode. You need to implement it yourself
        PR_ERR("Rooms are not adjacent and cannot be merged: %d and %d", room_id1, room_id2);
        return ty_rvc_part_merge_data_response(p_part_merge, -1);
    }

    // TODO: Execute the room merging algorithm
    // 1. Verify if two rooms are adjacent (shared boundary)
    // 2. Merge the areas of the two rooms
    // 3. Update the map data structure
    // 4. Assign a new room ID or keep one of them

    int result = perform_room_merge(room_id1, room_id2); // Room merging algorithm (pseudocode). You need to implement it yourself
    if(result != 0) {
        PR_ERR("Failed to merge the rooms");
        return ty_rvc_part_merge_data_response(p_part_merge, -1);
    }

    // Report the result after successful merge
    PART_MERGE_S report_data;
    report_data.num = p_part_merge->num;
    report_data.map_id = get_current_map_id();  // Map ID must be filled in. Pseudocode. You need to implement it yourself
    report_data.ids = p_part_merge->ids;

    return ty_rvc_part_merge_data_response(&report_data, 0);
}

Things to note:

  • Limitations:
    • Only two rooms can be merged (num = 2).
    • The two rooms must be adjacent (sharing a boundary).
    • Non-adjacent rooms cannot be merged.
  • The SET command sent by the panel does not contain the map_id field. The device must determine the current map on its own.
  • When reporting data, the device must populate the map_id field. Otherwise, the panel will be unable to match the data with the correct map.
  • Adjacency criteria:
    • The two rooms must share a common boundary (at least one shared boundary point).
    • Mere corner contact is insufficient, and there must be overlapping boundaries.
    • This can be verified by checking whether the boundary point sets of the two rooms intersect.
  • Post-merge processing:
    • Typically, the smaller room ID is retained, and the other is removed.
    • The name of the merged room can either be inherited or reset.
    • Room properties (including cleaning parameters and cleaning order) can inherit the properties of the retained room.
    • Map data must be updated and persistently saved.
  • ⚠️ Room property reporting is mandatory:
    • After the merge operation is completed, you must immediately call ty_rvc_room_property_data_response() to report the latest room properties.
    • The report should include complete information for all rooms after the merge: room IDs, room names, and cleaning parameters.
    • Merging reduces the number of rooms. The app relies on this room property report to update its display.
  • Common failure reasons:
    • The room ID does not exist or is invalid.
    • The two rooms are not adjacent.
    • The number of rooms to merge is not equal to 2.
    • The merge algorithm execution failed.
  • Response status code:
    • 0: The rooms are merged successfully.
    • -1: Failed to merge the rooms.
  • This is a response interface for SET commands only, and it has no QUERY function.

Restore default areas on the map

Feature: Restore manually edited room information to its initial default state, clearing all user operations such as room splitting, merging, and other room-related modifications.

Data structure:

/**
 * @brief Manual area reset structure
 */
typedef struct {
    int map_id; // Current command operation corresponding map ID
} MAP_PART_RESET_S;

Struct description:

Field Type SET command processing Description
map_id int The ID of the map to be restored defaults (reserved field, not sent to the panel). Specify which map needs to have its default area restored (reserved feature).

Example:

// Example of SET command processing
static OPERATE_RET handle_map_part_default_set(MAP_PART_RESET_S* p_part_reset)
{
    // Perform the restore default area operation
    // 1. Delete all manually split/merged rooms
    // 2. Reload the original areas automatically split by the system
    // 3. Reset room IDs and names
    int map_id = get_current_map_id();  // Map ID must be filled in. Pseudocode. You need to implement it yourself
    int ret = restore_default_partitions(map_id);  // Restore default areas (pseudocode). You need to implement it yourself
    if(ret != 0) {
        PR_ERR("Failed to restore default areas");
        return ty_rvc_map_part_default_data_response(p_part_reset, -1);
    }

    // Save the updated map data. You need to implement it yourself
    save_map_data(map_id);

    PR_DEBUG("Restored default areas successfully");

    return ty_rvc_map_part_default_data_response(p_part_reset, 0);
}

Things to note:

  • Operation is irreversible:
    • After restoring to default, all manually split and merged room records will be lost.
  • Scope of impact:
    • Delete all manually split rooms.
    • Delete all manually merged rooms.
    • Restore the automatic area state generated when the device first created the map.
    • Rooms revert to default names (for example, "Room 1" and "Room 2").
  • Room properties:
    • Room properties such as cleaning parameters and cleaning order will be reset.
    • User-defined custom room names will be lost.
  • Room property reporting is mandatory:
    • ⚠️ After the restore defaults operation is completed, you must immediately call ty_rvc_room_property_data_response() to report the restored room properties.
    • The report should include: all restored room IDs, default room names, and cleaning parameters after reset.
    • This is a major change operation, so the app must refresh the entire room list based on this room property report.
  • Response status code:
    • 0: Restored successfully.
    • -1: Failed to restore.
  • Scenarios:
    • The user is unsatisfied with manual edits and wants to start over.
    • The area layout is messy and needs to be reset to the initial state.
    • Quick reset during testing or debugging.
  • This is a response interface for SET commands only, and it has no QUERY function.

Reset a map

Feature: Clear all cached data of the current map. The device will restart the mapping process from scratch. This is suitable for scenarios such as changes in the usage environment or corrupted map data.

Data structure:

/**
 * @brief Current map reset structure
 */
typedef struct {
    int map_id; // Current command operation corresponding map ID
} CURRENT_MAP_RESET_S;

Struct description:

Field Type SET command processing Description
map_id int The ID of the map to be reset (reserved field, not sent to the panel). Specify which map needs to have its data cleared (reserved feature).

Example:

// Example of SET command processing
static OPERATE_RET handle_map_reset_set(CURRENT_MAP_RESET_S* p_map_reset)
{
    // Check if it's the currently used map
    int current_map_id = get_current_map_id();   // Get the current map ID (pseudocode). You need to implement it yourself

    // Perform map reset operation
    // 1. Clear map data
    // 2. Clear all room information
    // 3. Clear virtual walls, no-go areas, and other configurations
    // 4. Clear cleaning records
    int ret = clear_map_data(current_map_id);   // Delete the homepage map data (pseudocode). You need to implement it yourself
    if(ret != 0) {
        PR_ERR("Failed to clear map data");
        return ty_rvc_map_clear_data_response(p_map_reset, -1);
    }

    PR_DEBUG("Map reset was successful, preparing to recreate map");

    return ty_rvc_map_clear_data_response(p_map_reset, 0);
}

Things to note:

  • Business logic:
    • After a map is reset, the current cached map will be cleared. The device must report empty map data for panel display.
    • Clear cached data, including maps, rooms, virtual walls, and no-go areas.
    • Depending on your requirements, decide whether to delete in-memory data for the current map (if applicable).
  • Remapping:
    • After the reset, the device will enter mapping mode.
    • The map will be reconstructed during the next cleaning session.
    • The new map might be assigned a new map_id.
  • ⚠️ Room property reporting:
    • If remapping after the reset generates new room areas, you must call ty_rvc_room_property_data_response() to report the new room properties.
    • A map reset typically clears all room information. After remapping, if rooms exist, they must be reported.
    • If the map is completely cleared, an empty room list (num = 0) can be reported.
  • Response status code:
    • 0: The map is reset successfully.
    • -1: Failed to reset the map.
  • Scenarios:
    • The usage environment is changed (for example, moving to a new home).
    • Map data is corrupted or abnormal.
    • The initial mapping during the first cleaning is unsatisfactory and requires reconstruction.
  • This is a response interface for SET commands only, and it has no QUERY function.

Save a map

Feature: Save the current map data locally or to the cloud, manage multiple floor maps, and switch and use the saved maps between different floors.

Data structure:

typedef enum {
    MAP_SAVE_LOCAL = 0, // Save map locally
    MAP_SAVE_CLOUD, // Save map to cloud
    MAP_SAVE_MAX,
} MAP_SAVE_MODE_E;

/**
 * @brief Map save structure
 */
typedef struct {
    MAP_SAVE_MODE_E save_mode; // Save mode
} MAP_SAVE_S;

Struct description:

Field Type SET command processing Description
save_mode Enum Receive the save mode Specify whether to save locally or in the cloud.

Description of the save mode:

Mode value Enum name Meaning Description
0 MAP_SAVE_LOCAL Save to local Save the map only to the device’s local storage.
1 MAP_SAVE_CLOUD Save to the cloud (reserved field) Save the map to the cloud to support sync across multiple devices.

Example:

// Example of SET command processing
static OPERATE_RET handle_map_save_set(MAP_SAVE_S* p_map_save)
{

    PR_DEBUG("Received map save command, save mode: %d", p_map_save->save_mode);

    // Check if the save mode is valid
    if(p_map_save->save_mode >= MAP_SAVE_MAX) {
        PR_ERR("Invalid save mode: %d", p_map_save->save_mode);
        return ty_rvc_map_save_data_response(p_map_save, -1);
    }

    // Get the current map ID
    int current_map_id = get_current_map_id();     // Get the current map ID (pseudocode). You need to implement it yourself
    if(current_map_id <= 0) {
        PR_ERR("No valid map currently");
        return ty_rvc_map_save_data_response(p_map_save, -1);
    }

    int ret = 0;

    // Save to local
    ret = save_map_to_local(current_map_id);  // Save to local (pseudocode). You need to implement it yourself
    if(ret != 0) {
        PR_ERR("Failed to save to local");
        return ty_rvc_map_save_data_response(p_map_save, -1);
    }
    // If you need to save to the cloud
    PR_DEBUG("Save map to cloud");
    // Then upload to the cloud
    ret = upload_map_to_cloud(current_map_id);  // Save to the cloud (pseudocode). You need to implement it yourself
    if(ret != 0) {
        PR_ERR("Failed to upload to the cloud");
        return ty_rvc_map_save_data_response(p_map_save, -1);
    }

    if(ret != 0) {
        PR_ERR("Failed to save the map");
        return ty_rvc_map_save_data_response(p_map_save, -1);
    }

    PR_DEBUG("Saved the map successfully");

    return ty_rvc_map_save_data_response(p_map_save, 0);
}

Things to note:

  • Save mode:
    • MAP_SAVE_LOCAL: Save to the local storage only. Currently, the panel only supports this mode. If you want to save to the cloud, you can report to the cloud after receiving this mode.
    • MAP_SAVE_CLOUD: Save to the cloud. For the reporting interfaces, refer to Transmit Multiple Floor Maps and Cleaning History.
  • Save contents:
    • Map data
    • Room area information
    • Virtual wall and no-go area configurations
    • Room properties (for example, names and cleaning parameters)
  • Multi-floor support:
    • Each floor corresponds to a unique map_id.
    • Multiple floor maps can be saved to the cloud.
    • Allow switching between different floors for use.
  • Response status code:
    • 0: The map is saved successfully.
    • -1: Failed to save the map.
  • This is a response interface for SET commands only, and it has no QUERY function.

Delete a map from the cloud

Feature: Delete the specified map data from the cloud, while also deleting the corresponding local map data (if present). This is typically used to remove floor maps that are no longer in use.

Data structure:

/**
 * @brief Delete cloud map structure
 */
typedef struct {
    unsigned int map_id; // Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
} DELETE_CLOUD_MAP_S;

Struct description:

Field Type SET command processing Description
map_id unsigned int Receive the ID of the cloud map to be deleted Specify the map to be deleted from the cloud.

Example:

// Example of SET command processing
static OPERATE_RET handle_delete_cloud_map_set(DELETE_CLOUD_MAP_S* p_map_delete)
{

    PR_DEBUG("Received cloud map delete command, cloud map ID: %d", p_map_delete->map_id);

    // Check if the map exists locally
    if(!check_map_exists_in_cloud(p_map_delete->map_id)) {   // Pseudocode. You need to implement it yourself
        PR_ERR("The map does not exist locally: %d", p_map_delete->map_id);
        return ty_rvc_map_delete_data_response(p_map_delete, 0);
    }

    // Delete local cache (optional)
    ret = delete_local_map_cache(p_map_delete->map_id);
    if(ret != 0) {
        PR_ERR("Failed to delete local map");
        return ty_rvc_map_delete_data_response(p_map_delete, -1);
    }
    PR_DEBUG("Deleted the map successfully");    //After receiving the successful local deletion response, the panel will automatically delete the cloud map data

    return ty_rvc_map_delete_data_response(p_map_delete, 0);
}

Things to note:

  • Deletion scope:
    • Only delete map data stored in the cloud.
    • You can selectively keep or delete local map data.
    • Once deleted, the map cannot be recovered from the cloud.
  • Response status code:
    • 0: The map is deleted successfully.
    • -1: Failed to delete the map.
  • Scenarios:
    • Delete floor maps that are no longer in use.
    • Clean up old maps before starting a new mapping process.
    • Change the usage environment.
  • Differences from map reset:
    • Map reset: Clear the cached data of the current map and prepare for new mapping.
    • Cloud map deletion: Delete historical maps stored both locally and in the cloud.
  • Multi-floor management:
    • Delete the map for a specific floor.
    • After deletion, mapping must be performed again for that floor.
    • Maps for other floors remain unaffected.
  • This is a response interface for SET commands only, and it has no QUERY function.

Query a voice pack

Feature: Query the current voice pack status used by the device, allowing users to switch between voice packs of different languages in the app.

Data structure:

    /**
    * @brief Voice download status
    */
    typedef enum {
        RVC_DOWNLOAD_ST_FAILED = 0, // Voice download error
        RVC_DOWNLOAD_ST_LOADING, // Voice downloading
        RVC_DOWNLOAD_ST_SUCC, // Voice download successful
        RVC_DOWNLOAD_ST_USING // Voice in use
    } VOICE_DOWNLOAD_ST_E;

    /**
    * @brief Use voice response structure
    */
    typedef struct {
        unsigned int id; // Voice ID number
        VOICE_DOWNLOAD_ST_E download_status; // Voice download status
        int percent;  // Voice download progress percentage,range is 0~100
    } USE_VOICE_LANGUAGE_RESPONSE_S;

Struct description:

Field Type QUERY command response Proactive reporting
id unsigned int Populate the ID of the currently used voice pack Populate the ID of the voice pack
download_status Enum Populate the voice pack download status Populate the download status
percent int Populate the download progress (0-100) Populate the download progress

Description of download status:

Status value Enum name Meaning Scenario
0 RVC_DOWNLOAD_ST_FAILED Download failed An error occurred while downloading the voice pack.
1 RVC_DOWNLOAD_ST_LOADING Downloading Downloading the voice pack.
2 RVC_DOWNLOAD_ST_SUCC Download successful The voice pack has been downloaded but not yet activated.
3 RVC_DOWNLOAD_ST_USING In use The voice pack is in use.

Things to note:

  • Automatic processing by the SDK:

    • The SDK internally handles the entire voice pack download process.
    • You only need to call the ty_rvc_voice_download_init interface during the device initialization phase.
    • The SDK will automatically manage voice pack downloads and related operations.
  • Voice pack ID:

    • Different IDs represent different languages (the specific mapping is defined by the project).
    • Example: 1 = Chinese, 2 = English, 3 = German, 4 = French.
    • The ID must be consistent with the voice pack ID configured on the Tuya platform.
  • Progress reporting:

    • LOADING state: The percent field should reflect the real-time download progress (0 to 99).
    • SUCC/USING state: The percent field should be 100.
    • FAILED state: The percent field should be 0.

Use a map

Feature: Support multi-floor map management. The SDK automatically downloads floor maps to the device, and you are responsible for switching between maps as needed.

How to use:

// Called during device initialization
OPERATE_RET ret = ty_rvc_map_download_init("Pseudocode - input parameters are required here");
if(ret != OPRT_OK) {
    PR_ERR("Failed to initialize map download: %d", ret);
    return ret;
}
PR_DEBUG("Initialized the map download feature successfully");

Things to note:

  • Automatic processing by the SDK:
    • You only need to call the ty_rvc_map_download_init interface during the device initialization phase.
    • The SDK automatically handles map download, storage, and management.
  • Multi-floor support:
    • Support the management of multiple floor maps.
    • Each floor has a unique map ID (map_id).
    • The SDK automatically identifies and downloads map data for new floors to the local device.
  • ⚠️ Room property reporting is mandatory:
    • When using map functions involving map switching, if the map being switched to contains room information, you must call ty_rvc_room_property_data_response() to report the room properties of that map.
    • After map download is completed, if the map contains room area information, the room properties must be reported.
    • When switching between floor maps, the room properties of the new floor must be reported to ensure the app shows the correct room list.
  • Implementation recommendations:
    • Call this initialization interface after the device’s IoT SDK is initialized.
    • Make sure the device is connected to the network before executing the map download features.
    • Reserve sufficient storage space for multi-floor map data.
    • When switching between maps, promptly update and report the room properties.
  • The business logic layer does not need to implement map download logic, because the SDK provides complete automated support.

Verify a password

Feature: The system verifies whether the password entered by the user is correct. Only after successful verification can the user access the protected features.

Data structure:

typedef struct {
    int len; // string length
    char* name; // string memory points
} STR_ELEMENT_S;

typedef struct {
    STR_ELEMENT_S password;
} PASSWORD_CHECK_S;

Struct description:

Field Type SET command processing Description
password String Receive the password entered by the user It needs to be compared with the stored password.
password.name char* Pointer to the password string Actual password content.
password.len int Length of the password string Number of bytes in the password

Example:

// Example of SET command processing
static OPERATE_RET handle_password_check(PASSWORD_CHECK_S* p_password_check)
{
    if(NULL == p_password_check || NULL == p_password_check->password.name) {
        PR_ERR("Invalid password verification parameter");
        return ty_rvc_password_check_response(false, 0);
    }

    PR_DEBUG("Password verification request received, password length: %d", p_password_check->password.len);

    // Get the stored password
    char stored_password[32] = {0};
    int ret = read_password_from_storage(stored_password, sizeof(stored_password)); //Pseudocode. You need to implement it yourself

    if(ret != 0) {
        PR_ERR("Failed to read password");
        return ty_rvc_password_check_response(false, 0);
    }

    // Compare passwords (Note: In practice, it is recommended to use encrypted hash comparison)
    if(p_password_check->password.len == strlen(stored_password) &&
       memcmp(p_password_check->password.name, stored_password, p_password_check->password.len) == 0) {
        PR_DEBUG("Password verification was successful");
        return ty_rvc_password_check_response(true, 0);
    } else {
        PR_WARN("Password verification failed");
        return ty_rvc_password_check_response(true, 0);
    }
}

Things to note:

  • Verification result:
    • true: The password is correct.
    • false: The password is incorrect.
  • String processing:
    • The password field requires both name and len.
    • Note that the password may contain any characters.
  • This is a SET command interface used to verify operations.
  • Memory management:
    • Sent by the app (SET command): The string memory (password.name) is managed by the SDK. The SDK will automatically release it after the callback function returns. If the application needs to process the data asynchronously (e.g., using it in another thread), it must copy the string to memory allocated by the application itself.

Set a password

Feature: Set a new password or change an existing password. The old password is required for verification when changing the password.

Data structure:

typedef struct {
    int len; // string length
    char* name; // string memory points
} STR_ELEMENT_S;

typedef struct {
    STR_ELEMENT_S password;      // New password
    STR_ELEMENT_S old_password;  // Old password (required when changing the password)
} PASSWORD_SET_S;

Struct description:

Field Type SET command processing Description
password String Receive a new password New password to set
password.name char* Pointer to the new password string Content of the new password
password.len int Length of the new password string Number of bytes in the new password
old_password String Receive the old password This is required when changing the password. It is empty when the password is set for the first time.
old_password.name char* Pointer to the old password string Content of the old password
old_password.len int Length of the old password string Number of bytes in the old password

Example:

// Example of SET command processing
static OPERATE_RET handle_password_set(PASSWORD_SET_S* p_password_set)
{
    if(NULL == p_password_set || NULL == p_password_set->password.name) {
        PR_ERR("Invalid password setting parameter");
        return ty_rvc_password_set_response(false, 0);
    }

    PR_DEBUG("Password setting request received, new password length: %d", p_password_set->password.len);

    // Check if a password has already been set
    bool password_exists = is_password_configured();  //Pseudocode. You need to implement it yourself

    if(password_exists) {
        // Password modification scenario: old password needs to be verified
        if(NULL == p_password_set->old_password.name || p_password_set->old_password.len == 0) {
            PR_ERR("Password change requires old password");
            return ty_rvc_password_set_response(false, 0);
        }

        // Verify old password
        char stored_password[32] = {0};
        int ret = read_password_from_storage(stored_password, sizeof(stored_password));  //Pseudocode. You need to implement it yourself

        if(ret != 0 ||
           p_password_set->old_password.len != strlen(stored_password) ||
           memcmp(p_password_set->old_password.name, stored_password, p_password_set->old_password.len) != 0) {
            PR_ERR("Failed to verify the old password");
            return ty_rvc_password_set_response(false, 0);
        }

        PR_DEBUG("Old password was verified successfully, starting password update");
    } else {
        // First password setting scenario
        PR_DEBUG("First password setting");
    }

    // Save new password (in actual applications, it should be stored encrypted)
    char new_password[32] = {0};
    memcpy(new_password, p_password_set->password.name, p_password_set->password.len);
    new_password[p_password_set->password.len] = '\0';

    int ret = save_password_to_storage(new_password);  //Pseudocode. You need to implement it yourself
    if(ret != 0) {
        PR_ERR("Failed to save the password");
        return ty_rvc_password_set_response(false, 0);
    }

    PR_DEBUG("Password was set successfully");

    return ty_rvc_password_set_response(true, 0);
}

Things to note:

  • Returned result:
    • true: The password is set successfully.
    • false: Failed to set the password.
  • Two scenarios:
    • First-time setup: old_password is empty. Set a new password.
    • Password change: old_password is not empty, and you need to verify the old password first.
  • String processing:
    • Both password and old_password require the use of name and len.
    • Note that the password may contain any characters.
  • This is a SET command interface used to set operations.
  • Memory management:
    • Sent by the app (SET command): The string memory (password.name and old_password.name) is managed by the SDK. The SDK will automatically release it after the callback function returns. If the application needs to process the data asynchronously (e.g., using it in another thread), it must copy the string to memory allocated by the application itself.

Query current room information

Feature: Get the location information of the device’s current room, primarily for Alexa voice control scenarios. Note that the Tuya all-in-one app panel does not support this feature.

Data structure:

    typedef struct {
        int len; // string length
        char* name; // string memory points
    } STR_ELEMENT_S;

    /**
    * @brief  MCS Room info structure
    */
    typedef struct {
        unsigned int map_id;  //Current command operation corresponding map ID, when the device reports, it's the map ID where the device currently is, the panel uses this field to confirm whether the reported parameters and the parameters set are on the same map
        unsigned int curr_name_id; //The current room ID where the machine is located.
        STR_ELEMENT_S curr_name;   //The current room name where the machine is located.
    } MCS_ROOM_INFO_S;

Struct description:

Field Type QUERY command response Proactive reporting
map_id unsigned int Populate the current map ID Populate the current map ID
curr_name_id unsigned int Populate the current room ID Populate the current room ID
curr_name String Populate the current room name Populate the current room name
curr_name.name char* Pointer to the room name string Pointer to the room name string
curr_name.len int Length of the room name string Length of the room name string

Example:

// Example of QUERY command response
static OPERATE_RET handle_mcs_room_info_query(void)
{
    PR_DEBUG("Received current room information query command");

    // Prepare to report data
    MCS_ROOM_INFO_S room_info;

    // Populate the current map ID
    room_info.map_id = get_current_map_id(); //Pseudocode. You need to implement it yourself

    // Populate the current room ID
    room_info.curr_name_id = get_current_room_id(); //Pseudocode. You need to implement it yourself

    // Populate the current room name
    char* room_name = get_current_room_name();  // For example: "living room", "bedroom", "kitchen"
    if(room_name) {
        room_info.curr_name.name = room_name;
        room_info.curr_name.len = strlen(room_name);
    } else {
        // If the room name cannot be retrieved, use the default name
        room_info.curr_name.name = "Unknown Room";
        room_info.curr_name.len = strlen("Unknown Room");
    }

    PR_DEBUG("Current Location: Map ID=%d, Room ID=%d, Room Name=%.*s",
             room_info.map_id,
             room_info.curr_name_id,
             room_info.curr_name.len,
             room_info.curr_name.name);

    return ty_rvc_mcs_room_info_response(&room_info, 0);  // 0 indicates successful query
}

Things to note:

  • Scenarios:
    • It is primarily used for Alexa voice control scenarios.
    • Alexa needs to know the device’s current location to run cleaning commands for specific rooms.
    • This feature is not supported on the panel of Tuya’s all-in-one app.
  • String processing:
    • The cur_name field must populate both the name pointer and the len (length) value.
    • Use user-defined room names (for example, "Living Room" and "Bedroom").
    • String memory is managed by the application. Pay attention to the memory lifecycle.
  • Room positioning method:
    • Determine the room based on the robot’s current coordinates.
    • Check whether the coordinates fall within the area range of a room.
    • Use a point-in-polygon algorithm to determine the room.
  • Proactive reporting triggers:
    • When the robot moves from one room to another.
    • When a cleaning task starts (report the starting room).
    • When queried via Alexa voice control.
  • Data sync:
    • The room name should be consistent with the name set in the room properties.
    • map_id must be populated with the current map ID.
    • curr_name_id should be a valid room ID.
  • This is a QUERY interface and also supports the device proactively reporting location changes.

Set and query furniture models

Feature: Set and query the furniture model information recognized by the robot vacuum, enabling intelligent linkage between the map and furniture.

  • FURNITURE_MODEL_SET: Furniture model configuration is sent from the control panel, and the parameters are saved on the device.
  • FURNITURE_MODEL_QUERY: The panel queries the list of furniture models configured for the current device.

Data structure

/**
 * @brief Model size structure
 */
typedef struct {
    float length; // Length
    float width;  // Width
    float height; // Height
} MODEL_SIZE_S;

/**
 * @brief Model rotation structure (Euler angles)
 */
typedef struct {
    int x; // Rotation angle around X axis
    int y; // Rotation angle around Y axis
    int z; // Rotation angle around Z axis
} MODEL_ROTATION_S;

/**
 * @brief Model position structure
 */
typedef struct {
    float x; // X coordinate in map
    float y; // Y coordinate in map
    float z; // Z coordinate in map (usually 0, placed on ground)
} MODEL_POSITION_S;

/**
 * @brief Furniture model information structure
 */
typedef struct {
  int id;                 // Unique identifier for the furniture model, assigned by the panel
  int model_type;         // Furniture type code (e.g., 0=table, 1=sofa, 2=bed, etc.)
  float scale;            // Scale factor, overall proportional scaling of the model, default is 1.0
  MODEL_SIZE_S size;      // Actual furniture dimensions [length, width, height]
  MODEL_ROTATION_S rotation; // Euler angle rotation values [x, y, z], representing rotation angles around each axis
  MODEL_POSITION_S position; // Furniture position in the map coordinate system [x, y, z], z is usually 0 (on the ground)
} FURNITURE_MODEL_INFO_S;

/**
 * @brief Furniture model list structure  
 */
typedef struct {
    int model_count;                // Number of models
    FURNITURE_MODEL_INFO_S* models; // Array of model information
} FURNITURE_MODEL_LIST_S;

Struct description:

Field Type Description
model_count int The number of furniture models.
id int The unique identifier for the furniture model, assigned by the panel.
model_type int The furniture type code. For example, 0 = dining table, 1 = sofa, and 2 = bed. The specific mapping is maintained by the frontend configuration table.
scale float Scaling factor. Scales the model size overall. Default is 1.0.
size MODEL_SIZE_S Actual dimensions of the furniture (length, width, height) in meters.
rotation MODEL_ROTATION_S Euler angle rotation values: rotation angles around the X, Y, and Z axes respectively, in degrees.
position MODEL_POSITION_S Position of the furniture in the map coordinate system, in meters. Z is typically 0 (on the ground).

SET command processing example:

static OPERATE_RET handle_furniture_model_set(FURNITURE_MODEL_LIST_S *p_models)
{
  // Save furniture models to local storage
  save_furniture_models(p_models); // Pseudocode
  // Report processing results
  return ty_rvc_furniture_model_data_response(p_models, 0);
}

QUERY command response example:

static OPERATE_RET handle_furniture_model_query(void)
{
  FURNITURE_MODEL_LIST_S query_data;
  get_furniture_models(&query_data); // Pseudocode
  return ty_rvc_furniture_model_data_response(&query_data, 0);
}

Things to note:

  • Typical scenarios:
    • After the user configures furniture models on the app’s furniture management interface and taps Save, FURNITURE_MODEL_SET is triggered.
    • When the furniture management interface is refreshed, it triggers FURNITURE_MODEL_QUERY to retrieve the current list of furniture models.
  • Special instructions:
    • When model_count is 0, it indicates that all models should be cleared.
    • When setting multiple models in bulk, the device saves the data directly according to the array index. No ID matching logic is required.
    • The ID value is sent by the cloud, and the device uses it directly without automatically generating a new one.
    • The memory for the furniture list (models array) is allocated by the caller and must be released promptly after use to prevent memory leaks.

Set and query device models

Feature: Set and query the sub-device models recognized by the robot vacuum (such as smart home devices), enabling linkage between the map and devices.

  • DEVICE_MODEL_SET: Device model configuration is sent from the control panel in bulk, and the parameters are saved and used on the device.
  • DEVICE_MODEL_QUERY: The panel queries the list of device models currently configured on the device.

Data structure

/**
 * @brief Device model information structure (with sub-device ID)
 */
typedef struct {
    int id;                 // Unique identifier for the furniture model, assigned by the panel
    int model_type;         // Model type code (e.g., 0=dining table, 1=sofa, 2=bed, etc.)
    float scale;            // Scale ratio, globally scales the model proportionally, default is 1.0
    MODEL_SIZE_S size;      // Model actual dimensions [length, width, height]
    MODEL_ROTATION_S rotation; // Euler angle rotation values [x, y, z], representing rotation angles around each axis
    MODEL_POSITION_S position; // Model position in map coordinate system [x, y, z], z is usually 0 (placed on ground)
    STR_ELEMENT_S sub_dev_id; // Sub-device ID associated with this model
} DEVICE_MODEL_INFO_S;

/**
 * @brief Device model list structure  
 */
typedef struct {
    int model_count;              // Number of models
    DEVICE_MODEL_INFO_S* models;  // Array of model information
} DEVICE_MODEL_LIST_S;

Struct description:

Field Type Description
model_count int The number of device models.
id int The unique identifier for the device model, assigned by the panel.
model_type int The device type code. For example, 0 = socket, 1 = light, and 2 = air conditioner. The specific mapping is maintained by the frontend configuration table.
scale float Scaling factor. Scales the model size overall. Default is 1.0.
size MODEL_SIZE_S Actual dimensions of the device (length, width, height) in meters.
rotation MODEL_ROTATION_S Euler angle rotation values: rotation angles around the X, Y, and Z axes respectively, in degrees.
position MODEL_POSITION_S Position of the device in the map coordinate system, in meters. Z is typically 0 (on the ground).
sub_dev_id STR_ELEMENT_S The linked sub-device ID in a string.

SET command processing example:

static OPERATE_RET handle_device_model_set(DEVICE_MODEL_LIST_S *p_models)
{
  save_device_models(p_models); // Pseudocode
  return ty_rvc_device_model_data_response(p_models, 0);
}

QUERY command response example:

static OPERATE_RET handle_device_model_query(void)
{
  DEVICE_MODEL_LIST_S query_data;
  get_device_models(&query_data); // Pseudocode
  return ty_rvc_device_model_data_response(&query_data, 0);
}

Things to note:

  • Typical scenarios:
    • After the user configures device models on the app’s device management interface and taps Save, DEVICE_MODEL_SET is triggered.
    • When the device management interface is refreshed, it triggers DEVICE_MODEL_QUERY to retrieve the current list of device models.
  • Special instructions:
    • When model_count is 0, it indicates that all models should be cleared.
    • When setting multiple models in bulk, the device saves the data directly according to the array index. No ID matching logic is required.
    • The ID value is sent by the cloud, and the device uses it directly without automatically generating a new one.
    • The memory for the device list (models array) is allocated by the caller and must be released promptly after use to prevent memory leaks.
    • It is recommended that the sub_dev_id be consistent with the unique identifier of the cloud/local sub-device.

Manage custom carpet

Feature: Manage carpet areas manually added by the user on the map. You can set and query carpet information, including rectangular, circular/elliptical, and custom polygonal areas. This way, the device can perform differentiated cleaning strategies depending on carpet types.

  • CUSTOM_CARPET_SET: The panel sends the entire custom carpet list in bulk. The device saves the list as a whole based on the current array.
  • CUSTOM_CARPET_QUERY: The panel queries the list of custom carpets currently saved on the device.

Data structure

typedef enum {
    CARPET_SHAPE_UNKNOWN = 0,  // Unknown carpet shape
    CARPET_SHAPE_RECTANGLE,    // Rectangular carpet
    CARPET_SHAPE_ROUND,        // Circular/Elliptical carpet
    CARPET_SHAPE_CUSTOM,       // Custom polygonal carpet
} CARPET_SHAPE_E;

typedef enum {
    CARPET_MATERIAL_SHORT_PILE = 0,    // Short-pile carpet
    CARPET_MATERIAL_LONG_PILE,         // Long-pile carpet
    CARPET_MATERIAL_SHAG_FRINGE,       // Shag/fringe carpet
    CARPET_MATERIAL_FLAT_WEAVE,        // Flat-weave carpet
    CARPET_MATERIAL_SYNTHETIC,         // Synthetic carpet
    CARPET_MATERIAL_WOOL,              // Wool carpet
    CARPET_MATERIAL_UNKNOWN,           // Unknown material
} CARPET_MATERIAL_E;

typedef struct {
    int id;                    // Unique carpet ID (unique within the map)
    CARPET_SHAPE_E shape;      // Carpet shape: "rectangle", "round" (including ellipse), or "custom"
    CARPET_MATERIAL_E type;    // Carpet material type (affects cleaning strategy)
    int cleanMode;             // Cleaning attribute; this field maintains the same order as enumerations in dp: carpet_clean_prefer
    int point_num;             // Number of points defining the carpet area
    POINT_COOR_S* point;       // Array of point coordinates for the carpet area (dynamically allocated)
    bool autoBoost;            // Carpet auto-boost mode, 0 = disabled, 1 = enabled (consistent with dp: auto_boost)
    bool fineCleaning;         // Carpet fine cleaning mode, 0 = disabled, 1 = enabled (reserved field)
    bool sideBrushRotating;    // Side brush rotation control, 0 = disabled, 1 = enabled (reserved field)
} CUSTOM_CARPET_INFO_S;

typedef struct
{
    int carpet_count;                  // Number of carpets
    CUSTOM_CARPET_INFO_S* carpets;     // Array of carpets
} CUSTOM_CARPET_LIST_S;

Struct description:

Field Type Description
carpet_count int The number of custom carpets. 0 indicates clearing all carpets.
id int The unique carpet ID, assigned by the panel.
shape CARPET_SHAPE_E Carpet shape:
  • CARPET_SHAPE_RECTANGLE: Rectangular
  • CARPET_SHAPE_ROUND: Circular/Elliptical
  • CARPET_SHAPE_CUSTOM: Custom shape (polygonal or irregular)
type CARPET_MATERIAL_E The material type of the carpet (refer to the CARPET_MATERIAL_E type).
cleanMode int Cleaning properties. This field maintains the same order as enumerations in dp: carpet_clean_prefer.
point_num int The number of points defining the carpet area. Supports any number of points.
point POINT_COOR_S* The pointer to the array of point coordinates for the carpet area (dynamically allocated memory). Rectangles typically use 4 corner points, circular/elliptical shapes use bounding box points, and custom shapes support multiple points.
autoBoost bool The auto-boost mode.
  • 0: Disabled (normal suction power).
  • 1: Enabled (automatically increases suction power on carpet).
This field is consistent with dp: auto_boost.
fineCleaning bool Fine cleaning mode (reserved field).
  • 0: Disabled (standard cleaning).
  • 1: Enabled (reduces speed, improves cleaning effectiveness).
sideBrushRotating bool Side brush rotation control (reserved field).
  • 0: Disabled (side brush off to prevent fiber entanglement).
  • 1: Enabled (side brush is in normal operation).

SET command processing example:

static OPERATE_RET handle_custom_carpet_set(CUSTOM_CARPET_LIST_S* carpet_list)
{
    // save_custom_carpet_list(carpet_list); // Pseudocode. You need to implement it yourself
    return ty_rvc_custom_carpet_set_response(carpet_list, 0);
}

QUERY command response example:

static OPERATE_RET handle_custom_carpet_query(void)
{
    CUSTOM_CARPET_LIST_S query_data;

    // get_custom_carpet_list(&query_data); // Pseudocode. You need to implement it yourself

    return ty_rvc_custom_carpet_query_response(&query_data, 0);
}

Things to note:

  • Typical scenarios:
    • When the user manually selects a carpet on the map editing page or taps a carpet on the map homepage, CUSTOM_CARPET_SET is triggered.
    • When the user enters the carpet management page or refreshes the page, CUSTOM_CARPET_QUERY is triggered.
  • ID constraints:
    • The carpet ID must be unique within the current map.
    • The device supports both automatically recognized carpets and custom carpets, with IDs uniformly managed by the panel.
    • CUSTOM_CARPET_SET follows full overwrite semantics. When carpet_count is 0, it indicates clearing all custom carpets.
  • Memory and asynchronous processing:
    • The points array is allocated and released by the application. Before processing commands asynchronously, you should first copy the data.

Create a Wi-Fi heatmap

Feature: Show the Wi-Fi signal strength distribution collected by the robot vacuum in the home, enabling smart map integration and network coverage analytics.

Initialization interface:

/***********************************************************
 *@function: ty_rvc_wifi_map_file_init
 * @brief wifi map file registration callback function for the rvc
 * @param[in] path: Path where the wifi map file is stored
 * @param[in] manual_flag: TRUE for manual completion of mapping, FALSE for automatic completion of mapping
 * @return OPERATE_RET: 0 for success, other error codes indicate failure
 ***********************************************************/
OPERATE_RET ty_rvc_wifi_map_file_init(IN CONST CHAR_T* path, IN CONST BOOL_T manual_flag);

API description:

Field Type Description
path String The directory path for storing the Wi-Fi heatmap file. It must not be NULL, and the length must be less than 128 bytes.
manual_flag Mapping completion flag.
  • FALSE: Mapping is completed automatically. The device triggers heatmap saving and ends the process when returning to the dock for charging.
  • TRUE: Mapping is completed manually. Externally call ty_rvc_set_wifi_map_active_flag(TRUE) at an appropriate time to manually trigger heatmap saving and end the process.

Typical scenarios:

  • When the user enters the Wi-Fi heatmap page in the app and taps to start drawing the map, the WIFI_MAP_SET control command is triggered. Concurrently, the SDK will enable the Wi-Fi heatmap collection switch. The next time the device cleans, the SDK will automatically collect and report the data.
  • Before a new Wi-Fi heatmap is created, the old Wi-Fi heatmap is automatically deleted. You can also choose to proactively cancel the creation process.

SET command processing example:

 case WIFI_MAP_SET: {
      // Upon receiving the WIFI_MAP_SET status, the service can begin cleaning and generating motion paths. Note that the paths must be reported in real time.
    } break;

Things to note:

  • After receiving the WIFI_MAP_SET command, the business logic should immediately start full-house cleaning to ensure complete Wi-Fi signal point collection during the next cleaning session.
  • Even if a real-time map and route transmission channel is not established between the panel and the device, path data must still be reported whenever a path is generated.
  • When you reset, use, or delete the current map, the SDK will automatically delete the old Wi-Fi heatmap. You need to initiate the mapping process again.

How it works

  • The device can synchronize data with the Tuya-enabled app in the following three ways:
    • When the device data status changes, the device proactively calls the interface to report the data.
    • When the panel initiates a data query command, the device responds passively to complete data reporting.
    • When the panel initiates a data setting command, the device completes the functional processing and calls the interface to report and synchronize data.
  • After the application receives the processing command corresponding to the FUNC_SET_CB callback, it needs to handle the event asynchronously to ensure that frequent clicks on the panel for sending commands do not block subsequent command parsing. During asynchronous processing, the command parameters should be copied first, and then the copied data should be used for command event processing.
ApplicationTuyaOS SDKTuya-Enabled AppPower on deviceRegister data commandset/query callback functions toty_rvc_advance_func_registerDevice is connectedto the networkReport data via responseinterfaces such asty_rvc_device_info_data_respon-seReport data to sync with thecloudUser interacts with the panelUI, sending a command to setparametersSDK performs dataparsingTo avoid blocking subsequent command parsing, it is recommended that the application handle commands asynchronously.Copy the command parameters and then use the copied data for command event processing.SDK calls the data command setcallback for data processingAfter completing the commandset function processing, thedevice calls the responseinterface to report dataReport data to sync with thecloudUser interacts with the panelUI, sending a command toquery parametersSDK performs dataparsingSDK calls the data commandquery callback for dataprocessingAfter completing the commandquery function processing, thedevice calls the responseinterface to report dataReport data to sync with thecloudApplicationTuyaOS SDKTuya-Enabled App

APIs

Register a protocol handler callback

This interface is used to register command handler callback functions. After the callback is successfully registered, the SDK receives and parses the command protocol sent by the cloud, and then calls the registered callback to execute the business functionalities. Note that the param variable in the callback will be released after the callback is invoked. If the callback has asynchronous operations, you need to copy the param data to the variable space requested by your application.

/**
 * @brief Sweeper advanced function setting callback function pointer
 * @param [ADVANCE_CMD_E] cmd, protocol command, the SDK uses this parameter to notify the application of the current setting command to be processed, see the list for setting commands, all ending with SET
 * @param [void *] param, parameters corresponding to the command, the application converts the param parameter into the command corresponding parameter structure based on the cmd command field, the structure correspondence can refer to the accompanying demo
 */
typedef OPERATE_RET (*FUNC_SET_CB)(IN ADVANCE_CMD_E cmd, IN void* param);

/**
 * @brief Sweeper advanced function query callback function pointer
 * @param [ADVANCE_CMD_E] cmd, protocol command, the SDK uses this parameter to notify the application of the current query command to be processed, see the list for query commands, all ending with QUERY
 * @param [void *] param, parameters corresponding to the command, current query command, its parameters are meaningless and do not need to be processed
 * @note  When the application gets the corresponding command parameters in the query callback function and needs to report, please call the corresponding response reporting function, for example, after processing the VIRTUAL_WALL_QUERY command, when needing to report parameters, use the ty_rvc_virtual_wall_data_response interface to report
 */
typedef OPERATE_RET (*FUNC_QUERY_CB)(IN ADVANCE_CMD_E cmd, IN void* param);

/**
 * @brief Register advanced capability setting and query callback functions
 * @param[in] sets_handler: Command setting callback function that the application needs to implement
 * @param[in] query_handler: Command query callback function that the application needs to implement
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_advance_func_register(IN FUNC_SET_CB sets_handler, IN FUNC_QUERY_CB query_handler);

Parameter Description
sets_handler This request parameter is the handler callback of the set command. It is implemented by the business application. Register with the SDK through this interface, and the param request parameter of the callback is a command parameter. The application converts this parameter into the specified command parameter struct according to the command type, and then uses it.
query_handler This request parameter is the handler callback of the query command. It is implemented by the business application. Register with the SDK through this interface. The param request parameter of the callback is not currently used, and the application does not need to care about it.
OPERATE_RET The return value. OPRT_OK means successful execution, and other values ​​mean failed execution. For the meaning of error codes, refer to definitions in the TuyaOS SDK header file.

Data reporting interfaces

The device data reporting interfaces are used to complete data synchronization with Tuya-enabled apps. The following table lists the data reporting features provided by each interface:

Interface Feature Remarks
ty_rvc_virtual_wall_data_response Report virtual wall data When the device data changes, proactively call the interface to report the data.
ty_rvc_restricted_area_data_response Report no-go area data When the device data changes, proactively call the interface to report the data.
ty_rvc_room_property_data_response Report room properties data When the device data changes, proactively call the interface to report the data.
ty_rvc_room_clean_data_response Report selected area cleaning data When the device data changes, proactively call the interface to report the data.
ty_rvc_spot_clean_data_response Report selected spot cleaning data When the device data changes, proactively call the interface to report the data.
ty_rvc_zone_clean_data_response Report selected zone cleaning data When the device data changes, proactively call the interface to report the data.
ty_rvc_schedule_data_response Report schedule data When the device data changes, proactively call the interface to report the data.
ty_rvc_quiet_hours_data_response Report DND period data When the device data changes, proactively call the interface to report the data.
ty_rvc_voice_language_data_response Report language switching data When the device data changes, proactively call the interface to report the data.
ty_rvc_device_info_data_response Report device information data When the device data changes, proactively call the interface to report the data.
ty_rvc_part_division_data_response Report the result of splitting the map area This feature is only used to return the result after the set command is executed. When the panel does not send a set command, the device does not need to call this interface for data synchronization.
ty_rvc_part_merge_data_response Report the result of merging the map areas This feature is only used to return the result after the set command is executed. When the panel does not send a set command, the device does not need to call this interface for data synchronization.
ty_rvc_map_part_default_data_response Report the result of restoring default areas on the map This feature is only used to return the result after the set command is executed. When the panel does not send a set command, the device does not need to call this interface for data synchronization.
ty_rvc_map_clear_data_response Report the result of resetting the map This feature is only used to return the result after the set command is executed. When the panel does not send a set command, the device does not need to call this interface for data synchronization.
ty_rvc_map_save_data_response Report the result of saving the map This feature is only used to return the result after the set command is executed. When the panel does not send a set command, the device does not need to call this interface for data synchronization.
ty_rvc_map_delete_data_response Report the result of deleting the map This feature is only used to return the result after the set command is executed. When the panel does not send a set command, the device does not need to call this interface for data synchronization.
ty_rvc_map_empty_response Report the result of querying an empty map This feature is only used to return the result after the query command is executed. When the panel does not send a query command, the device does not need to call this interface for data synchronization.
ty_rvc_password_check_response Report the result of verifying a password This feature is only used to return the result after the query command is executed. When the panel does not send a query command, the device does not need to call this interface for data synchronization.
ty_rvc_password_state_response Report the result of querying a password status This feature is only used to return the result after the set or query command is executed. When the panel does not send a command, the device does not need to call this interface for data synchronization.
ty_rvc_password_set_response Report the result of setting a password This feature is only used to return the result after the set command is executed. When the panel does not send a set command, the device does not need to call this interface for data synchronization.
ty_rvc_mcs_room_info_response Report the result of querying the current room When the device data changes, proactively call the interface to report the data.
ty_rvc_furniture_model_list_response Report the list of furniture models This feature is only used to return the result after the set or query command is executed. When the panel does not send a command, the device does not need to call this interface for data synchronization.
ty_rvc_device_model_list_response Report the list of device models This feature is only used to return the result after the set or query command is executed. When the panel does not send a command, the device does not need to call this interface for data synchronization.
ty_rvc_custom_carpet_set_response Report the result of setting a custom carpet This feature is only used to return the result after the set command is executed. When the panel does not send a set command, the device does not need to call this interface for data synchronization.
ty_rvc_custom_carpet_query_response Report the result of querying a custom carpet This feature is only used to return the result after the query command is executed. When the panel does not send a query command, the device does not need to call this interface for data synchronization.
ty_rvc_carpet_clean_set_response Report the result of setting carpet cleaning This feature is only used to return the result after the set command is executed. When the panel does not send a set command, the device does not need to call this interface for data synchronization.
ty_rvc_carpet_clean_data_response Report the result of querying carpet cleaning This feature is only used to return the result after the query command is executed. When the panel does not send a query command, the device does not need to call this interface for data synchronization.

/**
 * @brief Report virtual wall information
 * @param[in] p_virtual_wall: Virtual wall data, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Command execution result, 0 for success, other values for failure
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_virtual_wall_data_response(VIRTUAL_WALL_S* p_virtual_wall, int errcode);

/**
 * @brief Report restricted area data
 * @param[in] p_restricted_area: Restricted area data input parameter, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution result corresponding to the parameter setting, 0 for success, other values for failure
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_restricted_area_data_response(RESTRICTED_AREA_S* p_restricted_area, int errcode);

/**
 * @brief Report room property data
 * @param[in] p_room_clean: Room property data, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution result corresponding to the parameter setting, 0 for success, other values for failure
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_room_property_data_response(ROOM_PROPERTY_S* p_room_clean, int errcode);

/**
 * @brief Report selected area cleaning parameter values
 * @param[in] p_room_clean: Cleaning parameter input parameter, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, no error, input parameter is 0
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_room_clean_data_response(ROOM_CLEAN_S* p_room_clean, int errcode);

/**
 * @brief Report spot cleaning data
 * @param[in] spot_clean_data: Spot cleaning data input parameter, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, input 0 when no error
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_spot_clean_data_response(SPOT_CLEAN_S* spot_clean_data, int errcode);

/**
 * @brief Report zone cleaning data
 * @param[in] p_zone_area: Zone cleaning data input parameter, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, input 0 when no error
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_zone_clean_data_response(ZONE_CLEAN_S* p_zone_area, int errcode);

/**
 * @brief Partition setting reply
 * @param[in] p_part_division: Partition parameters, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, input 0 when no error
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_part_division_data_response(PART_DIVI_S* p_part_division, PART_DIV_ST_E errcode);

/**
 * @brief Partition merge command reply
 * @param[in] p_part_merge: Partition setting reply parameters, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, input 0 when no error
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_part_merge_data_response(PART_MERGE_S* p_part_merge, PART_MERGE_ST_E errcode);

/**
 * @brief Partition restore default command response
 * @param[in] p_part_merge: Structure parameters, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_map_part_default_data_response(MAP_PART_RESET_S* p_part_merge, PART_RESET_ST_E errcode);

/**
 * @brief Clear home map reply
 * @param[in] p_map_reset: Map reset structure, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_map_clear_data_response(CURRENT_MAP_RESET_S* p_map_reset, MAP_RESET_ST_E errcode);

/**
 * @brief Map save command reply
 * @param[in] p_map_save: Map save reply parameter input, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Error code
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_map_save_data_response(MAP_SAVE_S* p_map_save, MAP_SAVE_ST_E errcode);

/**
 * @brief Map delete command reply
 * @param[in] p_map_delete: Map delete reply parameters, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Error code
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_map_delete_data_response(DELETE_CLOUD_MAP_S* p_map_delete, MAP_DELETE_ST_E errcode);

/**
 * @brief Use map command reply
 * @param[in] p_map_use_res: Use map reply parameters, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Error code
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_map_use_data_response(USE_CLOUD_MAP_RESPONSE_S* p_map_use_res, MAP_USE_ST_E errcode);

/**
 * @brief Scheduled data report
 * @param[in] p_schedule: Scheduled data, passed in by the application, parameters are defined in the structure
 * @param[in] current_map_id:
 * @param[in] errcode:
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_schedule_data_response(SCHEDULE_S* p_schedule, int errcode);

/**
 * @brief Do not disturb scheduled data report
 * @param[in] p_quiet_hours: Do not disturb scheduled data input, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Error code
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_quiet_hours_data_response(QUIET_HOURS_S* p_quiet_hours, int errcode);

/**
 * @brief Voice use parameter report
 * @param[in] p_voice_language_res: Voice use parameter report, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_voice_language_data_response(USE_VOICE_LANGUAGE_RESPONSE_S* p_voice_language_res, VOICE_USE_ST_E errcode);

/**
 * @brief Device information report, info field is concatenated by the customer in key:value format in JSON, when the app panel displays device information, it shows each value in the order of the key list
 * The panel needs to correctly configure multilingual support for each key value
 * This interface is responsible for completing data reporting via the MQTT channel
 *
 * @param[char*] info: Application's JSON format string
 * @param [int] len: String length
 * @return OPERATE_RET, OPRT_OK indicates success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_device_info_data_response(char* info, int len);            

/**
 * @brief Report dev room info
 * @param[in] room_info: Room information parameters, passed in by the application, are defined in the structure.
 * @param[in] errcode: Execution result corresponding to the parameter setting, 0 for success, other values for failure
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_mcs_room_info_response(MCS_ROOM_INFO_S* room_info, int errcode);      

/**
 * @brief Report furniture model list
 * @param[in] model_list: Furniture model list, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, 0 for success, other values for failure
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_furniture_model_list_response(IN FURNITURE_MODEL_LIST_S* model_list, int errcode);

/**
 * @brief Report device model list
 * @param[in] model_list: Device model list, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, 0 for success, other values for failure
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_device_model_list_response(IN DEVICE_MODEL_LIST_S* model_list, int errcode);

/**
 * @brief wifi map status report
 * @param[in] status: map status
 * @param[in] errcode: Execution error code
 * @return OPERATE_RET: on success, or an error code on failure
 */
OPERATE_RET ty_rvc_wifi_map_status_response(WIFI_MAP_ST_E status, int errcode);

/**
 * @brief wifi map setting report
 * @param[in] state: true:set is exist, false:set not exist
 * @param[in] errcode: Execution error code
 * @return OPERATE_RET: on success, or an error code on failure
 */
OPERATE_RET ty_rvc_wifi_map_rst_response(IN bool state, int errcode);

/**
 * @brief Report custom carpet set response
 * @param[in] carpet_list: Custom carpet list, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, 0 for success, other values for failure
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_custom_carpet_set_response(IN CUSTOM_CARPET_LIST_S* carpet_list, int errcode);

/**
 * @brief Report custom carpet query response
 * @param[in] carpet_list: Custom carpet list, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, 0 for success, other values for failure
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_custom_carpet_query_response(IN CUSTOM_CARPET_LIST_S* carpet_list, int errcode);

/**
 * @brief Report carpet cleaning set response
 * @param[in] carpet_clean: Carpet cleaning data, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, 0 for success, other values for failure
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_carpet_clean_set_response(IN CARPET_CLEAN_S* carpet_clean, int errcode);

/**
 * @brief Report carpet cleaning data
 * @param[in] carpet_clean: Carpet cleaning data, passed in by the application, parameters are defined in the structure
 * @param[in] errcode: Execution error code, 0 for success, other values for failure
 * @return OPERATE_RET: 0 success, other error codes indicate failure
 */
OPERATE_RET ty_rvc_carpet_clean_data_response(IN CARPET_CLEAN_S* carpet_clean, int errcode);


Example


static OPERATE_RET __sweeper_advance_function_set(OUT ADVANCE_CMD_E cmd, OUT void *param)
{
    OPERATE_RET ret = 0;
    int i=0,j=0;
    switch (cmd) {
        case VIRTUAL_WALL_SET: {
            VIRTUAL_WALL_S* p_virtual_wall = (VIRTUAL_WALL_S*)param;
            PR_DEBUG("virtual wall num:%d", p_virtual_wall->num);
            for(i = 0; i < p_virtual_wall->num;i++){
                PR_DEBUG("mode:%d", p_virtual_wall->line[i].mode);
                PR_DEBUG("line:%d", p_virtual_wall->line[i].points[0].x);
                PR_DEBUG("line:%d", p_virtual_wall->line[i].points[0].y);
                PR_DEBUG("line:%d", p_virtual_wall->line[i].points[1].x);
                PR_DEBUG("line:%d", p_virtual_wall->line[i].points[1].y);
            }

            ........
            // This section only shows the how to use the interface. In actual use, the actual device data should be obtained and reported in the asynchronous event handling function.
            current_map_id = 8;
            errcode = 0;
            ty_rvc_virtual_wall_data_response(p_virtual_wall, current_map_id, errcode);  

            }break;
            .......
            default:
                PR_DEBUG("cmd not support now");
                break;
    }

}


static OPERATE_RET __sweeper_advance_function_query(OUT ADVANCE_CMD_E cmd, OUT void *param)
{
    OPERATE_RET ret = OPRT_OK;
    uint16_t cmd_index = 0;
    switch(cmd){
        case VIRTUAL_WALL_QUERY:{
			// Demonstrate data reporting using test data
            VIRTUAL_WALL_S test_virtual_wall;
            VIRTUAL_LINE_S virtual_line[10] = {0};
            OPERATE_RET ret = OPRT_OK;

            test_virtual_wall.num = 1;
            for (int i = 0; i < test_virtual_wall.num; i++) {
                virtual_line[i].mode = FORBIT_ALL;
                virtual_line[i].points[0].x = 100;
                virtual_line[i].points[0].y = 200;
                virtual_line[i].points[1].x = 50;
                virtual_line[i].points[1].y = 60;
            }
            test_virtual_wall.line = virtual_line;
            int current_map_id = 7;
            ty_rvc_virtual_wall_data_response(&test_virtual_wall, current_map_id, errcode);          
        }break;
        default:
        PR_DEBUG("cmd not support now");
        break;
    }
    return ret;
}


// Power-on main process
int main(int argc, char* argv[])
{
    OPERATE_RET ret = 0;
    ....
    ret = ty_sys_start();
    if (ret != OPRT_OK) {
        PR_ERR("[%s, %d] sys start failed", __FUNCTION__, __LINE__);
        return ret;
    }

    ty_rvc_advance_func_register(__sweeper_advance_function_set, __sweeper_advance_function_query);
	....
}