Sandwich IoT
Published © GPL3+

DIY Smart Indoor Garden Lite

In this project, we will show you how to build a simpler version of an indoor garden system, which can do the hard work for you and take goo

BeginnerFull instructions provided24 hours278
DIY Smart Indoor Garden Lite

Things used in this project

Hardware components

Tuya Sandwich Wi-Fi SoC master control board (WB3S)
A development board helps you easily prototype IoT hardware.
×1
Functional board
Provide 12V DC power supply, control the water pump and PWM grow light board, and read analog values from the soil moisture sensor.
×1
Warm white and multi-color (RGBW) PWM grow light board
Dim the RGBW grow light with an app to provide the spectrum needed for plant growth.
×1
Vertical micro submersible water pump
The pump delivers water from your container to plants.
×1
Capacitive soil moisture sensor module
Measure soil moisture.
×1
XH 2.54 mm 2-pin male connector wire
Length: 80 mm
×1
XH 2.54 mm 3-pin male to male reversed connector wire
Length: 150 mm
×1
XH 2.54 mm 5-pin male to male reversed connector wire
Length: 400 mm
×1
C-shape acrylic light fixture
Fixture
×1
Water tank structure
3D printing is recommended.
×1
M3 × 16 mm flat-head machine screw
Standard specification
×1
M3 hexagon nut
Standard specification
×1
Silicone tubing hose
Connected to the water pump
×1

Software apps and online services

(Recommended) App demo for Android on GitHub
https://github.com/tuya/tuya-smart-planter-sample-kotlin
(Recommended) App demo for iOS on GitHub
https://github.com/tuya/tuya-smart-planter-sample-objc
Tuya Smart app
https://developer.tuya.com/en/docs/iot/tuya-smart-app-smart-life-app-advantages?id=K989rqa49rluq

Story

Read more

Code

Code snippet #1

Plain text
├── src
|    ├── plant_driver
|    |      └── plant_pwm.c      // PWM driver. The PWM API on the SoC layer is called and re-encapsulated.
|    ├── plant_soc               // APIs running on the SoC layer
|    ├── tuya_device.c           // Entry file of application layer
|    ├── app_plant.c             // The application layer
|    └── plant_control.c         // Control logic of each functional component
|
├── include				// Header file directory
|    ├── plant_driver
|    |      └── plant_pwm.h
|    ├── plant_soc
|    ├── tuya_device.h
|    ├── app_plant.h
|    └── plant_control.h
|
└── output              // Production

Code snippet #10

Plain text
    op_ret = app_plant_init(APP_PLANT_NORMAL);
    if(OPRT_OK != op_ret) {
        PR_ERR("plant init err!");
        return op_ret;
    }

Code snippet #11

Plain text
OPERATE_RET app_plant_init(IN APP_PLANT_MODE mode)
{
    OPERATE_RET op_ret = OPRT_OK;


    if(APP_PLANT_NORMAL == mode) {

        // Initialize I/O, sensors, PWM, and more
        plant_device_init();

        // Create data collection tasks for ADC sensors
        tuya_hal_thread_create(NULL, "thread_data_get_adc", 512*4, TRD_PRIO_4, sensor_data_get_adc_theard, NULL);

        // Create data processing tasks
        tuya_hal_thread_create(NULL, "thread_data_deal", 512*4, TRD_PRIO_4, sensor_data_deal_theard, NULL);

        // Create scheduled tasks for DP data reporting
        tuya_hal_thread_create(NULL, "thread_data_report", 512*4, TRD_PRIO_4, sensor_data_report_theard, NULL);

    }else {
        // Non-production test mode
    }

    return op_ret;
}

Code snippet #12

Plain text
OPERATE_RET app_plant_init(IN APP_PLANT_MODE mode)
{
    OPERATE_RET op_ret = OPRT_OK;


    if(APP_PLANT_NORMAL == mode) {

        // Initialize I/O, sensors, PWM, and more
        plant_device_init();

        // Create data collection tasks for ADC sensors
        tuya_hal_thread_create(NULL, "thread_data_get_adc", 512*4, TRD_PRIO_4, sensor_data_get_adc_theard, NULL);

        // Create data processing tasks
        tuya_hal_thread_create(NULL, "thread_data_deal", 512*4, TRD_PRIO_4, sensor_data_deal_theard, NULL);

        // Create scheduled tasks for DP data reporting
        tuya_hal_thread_create(NULL, "thread_data_report", 512*4, TRD_PRIO_4, sensor_data_report_theard, NULL);

    }else {
        // Non-production test mode
    }

    return op_ret;
}

Code snippet #13

Plain text
VOID app_report_all_dp_status(VOID)
{
    OPERATE_RET op_ret = OPRT_OK;

    INT_T dp_cnt = 0;
    dp_cnt = 12;

    TY_OBJ_DP_S *dp_arr = (TY_OBJ_DP_S *)Malloc(dp_cnt*SIZEOF(TY_OBJ_DP_S));
    if(NULL == dp_arr) {
        PR_ERR("malloc failed");
        return;
    }

    memset(dp_arr, 0, dp_cnt*SIZEOF(TY_OBJ_DP_S));

    dp_arr[0].dpid = DPID_SWITCH_P;
    dp_arr[0].type = PROP_BOOL;
    dp_arr[0].time_stamp = 0;
    dp_arr[0].value.dp_value = plant_ctrl_data.Switch;
    ......

    op_ret = dev_report_dp_json_async(NULL,dp_arr,dp_cnt);
    Free(dp_arr);
    if(OPRT_OK != op_ret) {
        PR_ERR("dev_report_dp_json_async relay_config data error,err_num",op_ret);
    }

    PR_DEBUG("dp_query report_all_dp_data");
    return;
}

Code snippet #14

Plain text
VOID app_report_all_dp_status(VOID)
{
    OPERATE_RET op_ret = OPRT_OK;

    INT_T dp_cnt = 0;
    dp_cnt = 12;

    TY_OBJ_DP_S *dp_arr = (TY_OBJ_DP_S *)Malloc(dp_cnt*SIZEOF(TY_OBJ_DP_S));
    if(NULL == dp_arr) {
        PR_ERR("malloc failed");
        return;
    }

    memset(dp_arr, 0, dp_cnt*SIZEOF(TY_OBJ_DP_S));

    dp_arr[0].dpid = DPID_SWITCH_P;
    dp_arr[0].type = PROP_BOOL;
    dp_arr[0].time_stamp = 0;
    dp_arr[0].value.dp_value = plant_ctrl_data.Switch;
    ......

    op_ret = dev_report_dp_json_async(NULL,dp_arr,dp_cnt);
    Free(dp_arr);
    if(OPRT_OK != op_ret) {
        PR_ERR("dev_report_dp_json_async relay_config data error,err_num",op_ret);
    }

    PR_DEBUG("dp_query report_all_dp_data");
    return;
}

Code snippet #15

Plain text
STATIC VOID sensor_data_get_adc_theard(PVOID_T pArg)
{
    while(1) {

        PR_DEBUG("plant_get_adc_sensor_data");
        tuya_hal_system_sleep(TASKDELAY_SEC*2);

        if(TRUE == plant_ctrl_data.Switch) {
            plant_get_adc_sensor_data();
        }

    }
}

STATIC VOID sensor_data_deal_theard(PVOID_T pArg)
{
    while(1) {
        tuya_hal_system_sleep(TASKDELAY_SEC);

        if(TRUE == plant_ctrl_data.Switch) {
            plant_ctrl_handle();
        }else {
            plant_ctrl_all_off();
        }

    }

}

STATIC VOID sensor_data_report_theard(PVOID_T pArg)
{
    while(1) {
        tuya_hal_system_sleep(TASKDELAY_SEC*5);
        app_report_all_dp_status();
    }

}

Code snippet #16

Plain text
STATIC VOID sensor_data_get_adc_theard(PVOID_T pArg)
{
    while(1) {

        PR_DEBUG("plant_get_adc_sensor_data");
        tuya_hal_system_sleep(TASKDELAY_SEC*2);

        if(TRUE == plant_ctrl_data.Switch) {
            plant_get_adc_sensor_data();
        }

    }
}

STATIC VOID sensor_data_deal_theard(PVOID_T pArg)
{
    while(1) {
        tuya_hal_system_sleep(TASKDELAY_SEC);

        if(TRUE == plant_ctrl_data.Switch) {
            plant_ctrl_handle();
        }else {
            plant_ctrl_all_off();
        }

    }

}

STATIC VOID sensor_data_report_theard(PVOID_T pArg)
{
    while(1) {
        tuya_hal_system_sleep(TASKDELAY_SEC*5);
        app_report_all_dp_status();
    }

}

Code snippet #17

Plain text
VOID deal_dp_proc(IN CONST TY_OBJ_DP_S *root)
{
    UCHAR_T dpid;

    dpid = root->dpid;
    PR_DEBUG("dpid:%d",dpid);

    switch (dpid) {

    case DPID_SWITCH_P:
        PR_DEBUG("set switch:%d",root->value.dp_bool);
        plant_ctrl_data.Switch = root->value.dp_bool;
        break;

    case DPID_PUMP:
        PR_DEBUG("set pump:%d",root->value.dp_bool);
        plant_ctrl_data.Pump = root->value.dp_bool;
        break;

        ......

    default:
        break;
    }

    return;

}

Code snippet #18

Plain text
VOID deal_dp_proc(IN CONST TY_OBJ_DP_S *root)
{
    UCHAR_T dpid;

    dpid = root->dpid;
    PR_DEBUG("dpid:%d",dpid);

    switch (dpid) {

    case DPID_SWITCH_P:
        PR_DEBUG("set switch:%d",root->value.dp_bool);
        plant_ctrl_data.Switch = root->value.dp_bool;
        break;

    case DPID_PUMP:
        PR_DEBUG("set pump:%d",root->value.dp_bool);
        plant_ctrl_data.Pump = root->value.dp_bool;
        break;

        ......

    default:
        break;
    }

    return;

}

Code snippet #19

Plain text
USER_PWM_DUTY_T user_pwm_duty = {0};

VOID plant_device_init(VOID)
{
    ......
    plant_pwm_init();
    ......
}

STATIC VOID __set_pwm_duty(VOID)
{
    user_pwm_duty.duty_red = (USHORT_T)(((float)plant_ctrl_data.Red_value/255.0)*1000);
    user_pwm_duty.duty_green = (USHORT_T)(((float)plant_ctrl_data.Green_value/255.0)*1000);
    user_pwm_duty.duty_blue = (USHORT_T)(((float)plant_ctrl_data.Blue_value/255.0)*1000);
    user_pwm_duty.duty_warm = (USHORT_T)(((float)plant_ctrl_data.Warm_value/255.0)*1000);
}

STATIC VOID __initiative_ctrl_module_light(VOID)
{
    if (plant_ctrl_data.Countdown_set != cancel)
    {
        if(IsThisSysTimerRun(light_timer) == FALSE) {
            light_flag_min = (USHORT_T)plant_ctrl_data.Countdown_set * 60;
            plant_pwm_set(&user_pwm_duty);
            sys_start_timer(light_timer,1000*60,TIMER_CYCLE);
        }else {
            plant_pwm_set(&user_pwm_duty);
        }
    }else {
        light_flag_min = 0;
        if(TRUE == plant_ctrl_data.Light_switch) {
            plant_pwm_set(&user_pwm_duty);
        }else {
            plant_pwm_off();
        }
    }
    plant_report_data.Countdown_left = light_flag_min;
}

VOID plant_ctrl_handle(VOID)
{
    ......
    __set_pwm_duty();
    __initiative_ctrl_module_light();
}

Code snippet #2

Plain text
├── src
|    ├── plant_driver
|    |      └── plant_pwm.c      // PWM driver. The PWM API on the SoC layer is called and re-encapsulated.
|    ├── plant_soc               // APIs running on the SoC layer
|    ├── tuya_device.c           // Entry file of application layer
|    ├── app_plant.c             // The application layer
|    └── plant_control.c         // Control logic of each functional component
|
├── include				// Header file directory
|    ├── plant_driver
|    |      └── plant_pwm.h
|    ├── plant_soc
|    ├── tuya_device.h
|    ├── app_plant.h
|    └── plant_control.h
|
└── output              // Production

Code snippet #20

Plain text
USER_PWM_DUTY_T user_pwm_duty = {0};

VOID plant_device_init(VOID)
{
    ......
    plant_pwm_init();
    ......
}

STATIC VOID __set_pwm_duty(VOID)
{
    user_pwm_duty.duty_red = (USHORT_T)(((float)plant_ctrl_data.Red_value/255.0)*1000);
    user_pwm_duty.duty_green = (USHORT_T)(((float)plant_ctrl_data.Green_value/255.0)*1000);
    user_pwm_duty.duty_blue = (USHORT_T)(((float)plant_ctrl_data.Blue_value/255.0)*1000);
    user_pwm_duty.duty_warm = (USHORT_T)(((float)plant_ctrl_data.Warm_value/255.0)*1000);
}

STATIC VOID __initiative_ctrl_module_light(VOID)
{
    if (plant_ctrl_data.Countdown_set != cancel)
    {
        if(IsThisSysTimerRun(light_timer) == FALSE) {
            light_flag_min = (USHORT_T)plant_ctrl_data.Countdown_set * 60;
            plant_pwm_set(&user_pwm_duty);
            sys_start_timer(light_timer,1000*60,TIMER_CYCLE);
        }else {
            plant_pwm_set(&user_pwm_duty);
        }
    }else {
        light_flag_min = 0;
        if(TRUE == plant_ctrl_data.Light_switch) {
            plant_pwm_set(&user_pwm_duty);
        }else {
            plant_pwm_off();
        }
    }
    plant_report_data.Countdown_left = light_flag_min;
}

VOID plant_ctrl_handle(VOID)
{
    ......
    __set_pwm_duty();
    __initiative_ctrl_module_light();
}

Code snippet #21

Plain text
VOID plant_get_adc_sensor_data(VOID)
{
    tuya_hal_adc_init(&tuya_adc);

    tuya_hal_adc_value_get(TEMP_ADC_DATA_LEN, &soil_moisture_value);

    PR_NOTICE("water_tank_value = %d",soil_moisture_value);

    tuya_hal_adc_finalize(&tuya_adc);
}

Code snippet #22

Plain text
VOID plant_get_adc_sensor_data(VOID)
{
    tuya_hal_adc_init(&tuya_adc);

    tuya_hal_adc_value_get(TEMP_ADC_DATA_LEN, &soil_moisture_value);

    PR_NOTICE("water_tank_value = %d",soil_moisture_value);

    tuya_hal_adc_finalize(&tuya_adc);
}

Code snippet #23

Plain text
STATIC VOID __passive_ctrl_module_soil_humidity(VOID)
{
    if(!ADD_WATER_READY) {
        tuya_gpio_write(WATER_PUMP_PORT, WATER_PUMP_LEVEL);
        ADD_WATER_COUNT++;
        if(ADD_WATER_COUNT >15) {
            ADD_WATER_READY = 1;
            ADD_WATER_COUNT = 0;
        }
    }

    switch (plant_ctrl_data.soil_moisture_level) {

    case manual_control:
        ADD_WATER_COUNT == 0;
        ADD_WATER_READY == 1;
        break;
    case low:
        if(soil_moisture_value > soil_moisture_threshold.Low) {
            if(ADD_WATER_READY) {
                tuya_gpio_write(WATER_PUMP_PORT, !WATER_PUMP_LEVEL);
                ADD_WATER_COUNT++;
                if(ADD_WATER_COUNT > 2) {
                    ADD_WATER_READY = 0;
                }
            }
        }else {
            tuya_gpio_write(WATER_PUMP_PORT, WATER_PUMP_LEVEL);
        }
        break;
    case medium:
        if(soil_moisture_value > soil_moisture_threshold.Medium) {
            if(ADD_WATER_READY) {
                tuya_gpio_write(WATER_PUMP_PORT, !WATER_PUMP_LEVEL);
                ADD_WATER_COUNT++;
                if(ADD_WATER_COUNT > 2) {
                    ADD_WATER_READY = 0;
                }
            }
        }else {
            tuya_gpio_write(WATER_PUMP_PORT, WATER_PUMP_LEVEL);
        }
        break;
    case high:
        if(soil_moisture_value > soil_moisture_threshold.High) {
            if(ADD_WATER_READY) {
                tuya_gpio_write(WATER_PUMP_PORT, !WATER_PUMP_LEVEL);
                ADD_WATER_COUNT++;
                if(ADD_WATER_COUNT > 2) {
                    ADD_WATER_READY = 0;
                }
            }
        }else {
            tuya_gpio_write(WATER_PUMP_PORT, WATER_PUMP_LEVEL);
        }
        break;
    default:
        break;
    }
}

STATIC VOID __initiative_ctrl_module_pump(VOID)
{
    if(plant_ctrl_data.soil_moisture_level != manual_control) {
        return;
    }

    if(TRUE == plant_ctrl_data.Pump) {
        tuya_gpio_write(WATER_PUMP_PORT, !WATER_PUMP_LEVEL);
    }else {
        tuya_gpio_write(WATER_PUMP_PORT, WATER_PUMP_LEVEL);
    }

}

VOID plant_ctrl_handle(VOID)
{
    ......
    // Automatic control of the water pump
    __passive_ctrl_module_pump();
	// Manual control of the water pump
    __initiative_ctrl_module_pump();
    ......
}

Code snippet #24

Plain text
STATIC VOID __passive_ctrl_module_soil_humidity(VOID)
{
    if(!ADD_WATER_READY) {
        tuya_gpio_write(WATER_PUMP_PORT, WATER_PUMP_LEVEL);
        ADD_WATER_COUNT++;
        if(ADD_WATER_COUNT >15) {
            ADD_WATER_READY = 1;
            ADD_WATER_COUNT = 0;
        }
    }

    switch (plant_ctrl_data.soil_moisture_level) {

    case manual_control:
        ADD_WATER_COUNT == 0;
        ADD_WATER_READY == 1;
        break;
    case low:
        if(soil_moisture_value > soil_moisture_threshold.Low) {
            if(ADD_WATER_READY) {
                tuya_gpio_write(WATER_PUMP_PORT, !WATER_PUMP_LEVEL);
                ADD_WATER_COUNT++;
                if(ADD_WATER_COUNT > 2) {
                    ADD_WATER_READY = 0;
                }
            }
        }else {
            tuya_gpio_write(WATER_PUMP_PORT, WATER_PUMP_LEVEL);
        }
        break;
    case medium:
        if(soil_moisture_value > soil_moisture_threshold.Medium) {
            if(ADD_WATER_READY) {
                tuya_gpio_write(WATER_PUMP_PORT, !WATER_PUMP_LEVEL);
                ADD_WATER_COUNT++;
                if(ADD_WATER_COUNT > 2) {
                    ADD_WATER_READY = 0;
                }
            }
        }else {
            tuya_gpio_write(WATER_PUMP_PORT, WATER_PUMP_LEVEL);
        }
        break;
    case high:
        if(soil_moisture_value > soil_moisture_threshold.High) {
            if(ADD_WATER_READY) {
                tuya_gpio_write(WATER_PUMP_PORT, !WATER_PUMP_LEVEL);
                ADD_WATER_COUNT++;
                if(ADD_WATER_COUNT > 2) {
                    ADD_WATER_READY = 0;
                }
            }
        }else {
            tuya_gpio_write(WATER_PUMP_PORT, WATER_PUMP_LEVEL);
        }
        break;
    default:
        break;
    }
}

STATIC VOID __initiative_ctrl_module_pump(VOID)
{
    if(plant_ctrl_data.soil_moisture_level != manual_control) {
        return;
    }

    if(TRUE == plant_ctrl_data.Pump) {
        tuya_gpio_write(WATER_PUMP_PORT, !WATER_PUMP_LEVEL);
    }else {
        tuya_gpio_write(WATER_PUMP_PORT, WATER_PUMP_LEVEL);
    }

}

VOID plant_ctrl_handle(VOID)
{
    ......
    // Automatic control of the water pump
    __passive_ctrl_module_pump();
	// Manual control of the water pump
    __initiative_ctrl_module_pump();
    ......
}

Code snippet #3

Plain text
OPERATE_RET device_init(VOID)
{
  OPERATE_RET op_ret = OPRT_OK;

  UCHAR_T connect_mode = 0;

  PR_NOTICE("goto device_init!!!");

  TY_IOT_CBS_S wf_cbs = {
      status_changed_cb,\
      gw_ug_inform_cb,\
      gw_reset_cb,\
      dev_obj_dp_cb,\
      dev_raw_dp_cb,\
      dev_dp_query_cb,\
      NULL,
  };

  connect_mode = GWCM_OLD;

  op_ret = tuya_iot_wf_soc_dev_init_param(connect_mode,WF_START_SMART_FIRST,&wf_cbs,NULL,PRODECT_KEY,DEV_SW_VERSION);

  if(OPRT_OK != op_ret) {
      PR_ERR("tuya_iot_wf_soc_dev_init_param error,err_num:%d",op_ret);
      return op_ret;
  }

  op_ret = tuya_iot_reg_get_wf_nw_stat_cb(wf_nw_status_cb);
  if(OPRT_OK != op_ret) {
      PR_ERR("tuya_iot_reg_get_wf_nw_stat_cb is error,err_num:%d",op_ret);
      return op_ret;
  }

  op_ret = app_plant_init(APP_PLANT_NORMAL);
  if(OPRT_OK != op_ret) {
      PR_ERR("plant init err!");
      return op_ret;
  }

  return op_ret;
}

Code snippet #4

Plain text
OPERATE_RET device_init(VOID)
{
  OPERATE_RET op_ret = OPRT_OK;

  UCHAR_T connect_mode = 0;

  PR_NOTICE("goto device_init!!!");

  TY_IOT_CBS_S wf_cbs = {
      status_changed_cb,\
      gw_ug_inform_cb,\
      gw_reset_cb,\
      dev_obj_dp_cb,\
      dev_raw_dp_cb,\
      dev_dp_query_cb,\
      NULL,
  };

  connect_mode = GWCM_OLD;

  op_ret = tuya_iot_wf_soc_dev_init_param(connect_mode,WF_START_SMART_FIRST,&wf_cbs,NULL,PRODECT_KEY,DEV_SW_VERSION);

  if(OPRT_OK != op_ret) {
      PR_ERR("tuya_iot_wf_soc_dev_init_param error,err_num:%d",op_ret);
      return op_ret;
  }

  op_ret = tuya_iot_reg_get_wf_nw_stat_cb(wf_nw_status_cb);
  if(OPRT_OK != op_ret) {
      PR_ERR("tuya_iot_reg_get_wf_nw_stat_cb is error,err_num:%d",op_ret);
      return op_ret;
  }

  op_ret = app_plant_init(APP_PLANT_NORMAL);
  if(OPRT_OK != op_ret) {
      PR_ERR("plant init err!");
      return op_ret;
  }

  return op_ret;
}

Code snippet #5

Plain text
    TY_IOT_CBS_S wf_cbs = {
        status_changed_cb,\
        gw_ug_inform_cb,\
        gw_reset_cb,\
        dev_obj_dp_cb,\
        dev_raw_dp_cb,\
        dev_dp_query_cb,\
        NULL,
    };

    connect_mode = GWCM_OLD;

    op_ret = tuya_iot_wf_soc_dev_init_param(connect_mode,WF_START_SMART_FIRST,\
    &wf_cbs,NULL,PRODECT_KEY,DEV_SW_VERSION);

    if(OPRT_OK != op_ret) {
        PR_ERR("tuya_iot_wf_soc_dev_init_param error,err_num:%d",op_ret);
        return op_ret;
    }

Code snippet #6

Plain text
    TY_IOT_CBS_S wf_cbs = {
        status_changed_cb,\
        gw_ug_inform_cb,\
        gw_reset_cb,\
        dev_obj_dp_cb,\
        dev_raw_dp_cb,\
        dev_dp_query_cb,\
        NULL,
    };

    connect_mode = GWCM_OLD;

    op_ret = tuya_iot_wf_soc_dev_init_param(connect_mode,WF_START_SMART_FIRST,\
    &wf_cbs,NULL,PRODECT_KEY,DEV_SW_VERSION);

    if(OPRT_OK != op_ret) {
        PR_ERR("tuya_iot_wf_soc_dev_init_param error,err_num:%d",op_ret);
        return op_ret;
    }

Code snippet #7

Plain text
    op_ret = tuya_iot_reg_get_wf_nw_stat_cb(wf_nw_status_cb);
    if(OPRT_OK != op_ret) {
        PR_ERR("tuya_iot_reg_get_wf_nw_stat_cb is error,err_num:%d",op_ret);
        return op_ret;
    }

Code snippet #8

Plain text
    op_ret = tuya_iot_reg_get_wf_nw_stat_cb(wf_nw_status_cb);
    if(OPRT_OK != op_ret) {
        PR_ERR("tuya_iot_reg_get_wf_nw_stat_cb is error,err_num:%d",op_ret);
        return op_ret;
    }

Code snippet #9

Plain text
    op_ret = app_plant_init(APP_PLANT_NORMAL);
    if(OPRT_OK != op_ret) {
        PR_ERR("plant init err!");
        return op_ret;
    }

Credits

Sandwich IoT
40 projects • 5 followers

Comments