What is the purpose of if(ret) return 1; after a function call in examples

Looking through a lot of the examples there is a pattern like the following:

  ret = gpio_pin_configure_dt( &led0, GPIO_OUTPUT_ACTIVE );
  if ( ret < 0 ) {
    return 1;
  }

Where you capture the success of the function, and on a failure return 1. My main question is what is the ultimate purpose of doing this and what is considered best practice when using this pattern? In the examples it results in the main loop just returning a 1. What happens then? Does the microcontroller throw an error or fault? Or does it just try to restart the main loop? Does Zephyr have some sort of handling of the error if the main loop returns 1? It is kind of unclear what the purpose of these if() return statements are beyond "You should check that config/reading/etc worked"

If I am using functions within my application am I expected to raise the error up to the main loop? Is it used for debugging?

Mostly just looking to gain insight on the programming pattern and its purpose and uses when developing in the Zephyr ecosystem which I am fairly new to.

Parents
  • Hello, 

    Where you capture the success of the function, and on a failure return 1.


    Could you please provide what sample that uses this code snippet? I was not able to find any in our SDK. Negative numbers are usually errors.  I will need to see the sample with usage in order to determine what the use case is. What version of the nRF Connect SDK are you working on?

    Kind regards,
    Øyvind

  • Using nRF Connect SDK V2.1.0

    The above snippet is directly from the DevAcademy Lesson 5 Excercise 1 on the setting up the GPIO. Here is the full snippet of that exact section.

    ret = gpio_pin_configure_dt(&led0, GPIO_OUTPUT_ACTIVE);
    	if (ret < 0) {
    		return 1 ;
    	}
    	ret = gpio_pin_configure_dt(&led1, GPIO_OUTPUT_ACTIVE);
    	if (ret < 0) {
    		return 1 ;
    	}
    	ret = gpio_pin_configure_dt(&led2, GPIO_OUTPUT_ACTIVE);
    	if (ret < 0) {
    		return 1 ;
    	}

    But my question is asking about the pattern in general. If you look at the basic zephyr blinky example you see the pattern again, this time just returning since main is declared void in this one.

    void main(void)
    {
    	int ret;
    
    	if (!device_is_ready(led.port)) {
    		return;
    	}
    
    	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
    	if (ret < 0) {
    		return;
    	}
    
    	while (1) {
    		ret = gpio_pin_toggle_dt(&led);
    		if (ret < 0) {
    			return;
    		}
    		k_msleep(SLEEP_TIME_MS);
    	}
    }

    In the blinky_pwm example it also prints an error to console for debugging before returning (this is lines 61-64)

    		ret = pwm_set_dt(&pwm_led0, period, period / 2U);
    		if (ret) {
    			printk("Error %d: failed to set pulse width\n", ret);
    			return;

    It is similar to the device initialization pattern (this one is from blinky):

    if (!device_is_ready(led.port)) {
    	return;
    }

    It seems like this is done to end the program in a controlled manner in the event of an error, but it seems odd to me. In contrast in the nrf lpuart sample __ASSERT() seems to be used to fulfill the same role and be clearer with intention and easier to read and implement. (lines 96 - 133 for reference).

    static void async(const struct device *lpuart)
    {
    	uint8_t txbuf[5] = {1, 2, 3, 4, 5};
    	int err;
    	uint8_t *buf;
    
    	err = k_mem_slab_alloc(&uart_slab, (void **)&buf, K_NO_WAIT);
    	__ASSERT(err == 0, "Failed to alloc slab");
    
    	err = uart_callback_set(lpuart, uart_callback, (void *)lpuart);
    	__ASSERT(err == 0, "Failed to set callback");
    
    	err = uart_rx_enable(lpuart, buf, BUF_SIZE, 10000);
    	__ASSERT(err == 0, "Failed to enable RX");
    
    	while (1) {
    		err = uart_tx(lpuart, txbuf, sizeof(txbuf), 10000);
    		__ASSERT(err == 0, "Failed to initiate transmission");
    
    		k_sleep(K_MSEC(500));
    
    		uart_poll_out(lpuart, txbuf[0]);
    		k_sleep(K_MSEC(100));
    	}
    }
    
    void main(void)
    {
    	const struct device *lpuart = DEVICE_DT_GET(DT_NODELABEL(lpuart));
    
    	__ASSERT(device_is_ready(lpuart), "LPUART device not ready");
    
    	if (IS_ENABLED(CONFIG_NRF_SW_LPUART_INT_DRIVEN)) {
    		interrupt_driven(lpuart);
    	} else {
    		async(lpuart);
    	}
    }

    So my questions are what is the purpose of the return in the if(ret), is it to end the program or is there more? If you do it that way, is it good practice to elevate the error in functions up to the main thread to exit the program in a controlled manner in that way. And I suppose follow up question is why not just use the __ASSERT() if that is the case because it seems to fill the same role while being more clear with intention and easier to implement? The lpuart sample seemed to be the only one that used __ASSERT() I could find in either zephyr or nrf samples, but I also didn't do an exhaustive search.

Reply
  • Using nRF Connect SDK V2.1.0

    The above snippet is directly from the DevAcademy Lesson 5 Excercise 1 on the setting up the GPIO. Here is the full snippet of that exact section.

    ret = gpio_pin_configure_dt(&led0, GPIO_OUTPUT_ACTIVE);
    	if (ret < 0) {
    		return 1 ;
    	}
    	ret = gpio_pin_configure_dt(&led1, GPIO_OUTPUT_ACTIVE);
    	if (ret < 0) {
    		return 1 ;
    	}
    	ret = gpio_pin_configure_dt(&led2, GPIO_OUTPUT_ACTIVE);
    	if (ret < 0) {
    		return 1 ;
    	}

    But my question is asking about the pattern in general. If you look at the basic zephyr blinky example you see the pattern again, this time just returning since main is declared void in this one.

    void main(void)
    {
    	int ret;
    
    	if (!device_is_ready(led.port)) {
    		return;
    	}
    
    	ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
    	if (ret < 0) {
    		return;
    	}
    
    	while (1) {
    		ret = gpio_pin_toggle_dt(&led);
    		if (ret < 0) {
    			return;
    		}
    		k_msleep(SLEEP_TIME_MS);
    	}
    }

    In the blinky_pwm example it also prints an error to console for debugging before returning (this is lines 61-64)

    		ret = pwm_set_dt(&pwm_led0, period, period / 2U);
    		if (ret) {
    			printk("Error %d: failed to set pulse width\n", ret);
    			return;

    It is similar to the device initialization pattern (this one is from blinky):

    if (!device_is_ready(led.port)) {
    	return;
    }

    It seems like this is done to end the program in a controlled manner in the event of an error, but it seems odd to me. In contrast in the nrf lpuart sample __ASSERT() seems to be used to fulfill the same role and be clearer with intention and easier to read and implement. (lines 96 - 133 for reference).

    static void async(const struct device *lpuart)
    {
    	uint8_t txbuf[5] = {1, 2, 3, 4, 5};
    	int err;
    	uint8_t *buf;
    
    	err = k_mem_slab_alloc(&uart_slab, (void **)&buf, K_NO_WAIT);
    	__ASSERT(err == 0, "Failed to alloc slab");
    
    	err = uart_callback_set(lpuart, uart_callback, (void *)lpuart);
    	__ASSERT(err == 0, "Failed to set callback");
    
    	err = uart_rx_enable(lpuart, buf, BUF_SIZE, 10000);
    	__ASSERT(err == 0, "Failed to enable RX");
    
    	while (1) {
    		err = uart_tx(lpuart, txbuf, sizeof(txbuf), 10000);
    		__ASSERT(err == 0, "Failed to initiate transmission");
    
    		k_sleep(K_MSEC(500));
    
    		uart_poll_out(lpuart, txbuf[0]);
    		k_sleep(K_MSEC(100));
    	}
    }
    
    void main(void)
    {
    	const struct device *lpuart = DEVICE_DT_GET(DT_NODELABEL(lpuart));
    
    	__ASSERT(device_is_ready(lpuart), "LPUART device not ready");
    
    	if (IS_ENABLED(CONFIG_NRF_SW_LPUART_INT_DRIVEN)) {
    		interrupt_driven(lpuart);
    	} else {
    		async(lpuart);
    	}
    }

    So my questions are what is the purpose of the return in the if(ret), is it to end the program or is there more? If you do it that way, is it good practice to elevate the error in functions up to the main thread to exit the program in a controlled manner in that way. And I suppose follow up question is why not just use the __ASSERT() if that is the case because it seems to fill the same role while being more clear with intention and easier to implement? The lpuart sample seemed to be the only one that used __ASSERT() I could find in either zephyr or nrf samples, but I also didn't do an exhaustive search.

Children
Related