From 715fb8fa431ce10cb7e15f7ba3f0dd3aee85c1ca Mon Sep 17 00:00:00 2001 From: Matthias Renner Date: Fri, 8 Jul 2016 07:55:30 -0600 Subject: [PATCH] add canard app, untested --- examples/README.txt | 5 + examples/canard/.gitignore | 11 + examples/canard/Kconfig | 48 +++ examples/canard/Make.defs | 39 +++ examples/canard/Makefile | 52 ++++ examples/canard/canard_main.c | 552 ++++++++++++++++++++++++++++++++++ 6 files changed, 707 insertions(+) create mode 100644 examples/canard/.gitignore create mode 100644 examples/canard/Kconfig create mode 100644 examples/canard/Make.defs create mode 100644 examples/canard/Makefile create mode 100644 examples/canard/canard_main.c diff --git a/examples/README.txt b/examples/README.txt index bd053d634..7900e4b03 100644 --- a/examples/README.txt +++ b/examples/README.txt @@ -208,6 +208,11 @@ examples/can CONFIG_EXAMPLES_CAN_READONLY - Only receive messages CONFIG_EXAMPLES_CAN_WRITEONLY - Only send messages +examples/canard +^^^^^^^^^^^^^^^ + + Example application for canutils/libcarnard. + examples/cc3000 ^^^^^^^^^^^^^^^ diff --git a/examples/canard/.gitignore b/examples/canard/.gitignore new file mode 100644 index 000000000..fa1ec7579 --- /dev/null +++ b/examples/canard/.gitignore @@ -0,0 +1,11 @@ +/Make.dep +/.depend +/.built +/*.asm +/*.obj +/*.rel +/*.lst +/*.sym +/*.adb +/*.lib +/*.src diff --git a/examples/canard/Kconfig b/examples/canard/Kconfig new file mode 100644 index 000000000..d7bc8f65a --- /dev/null +++ b/examples/canard/Kconfig @@ -0,0 +1,48 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config EXAMPLES_LIBCANARD + bool "libcandard example" + default n + depends on CANUTILS_LIBCANARD && CLOCK_MONOTONIC && SYSTEM_TIME64 + ---help--- + Enable the LIBCANARD example + +if EXAMPLES_LIBCANARD + +config EXAMPLES_LIBCANARD_DEVPATH + string "Device Path" + default "/dev/can0" + ---help--- + The device path + +config EXAMPLES_LIBCANARD_NODE_ID + int "Node ID" + default 1 + range 1 127 + ---help--- + Specifies the node's ID + +config EXAMPLES_LIBCANARD_APP_NODE_NAME + string "Node name" + default "org.uavcan.libcanard.nuttx.demo" + ---help--- + app node name + +config EXAMPLES_LIBCANARD_NODE_MEM_POOL_SIZE + int "Node Memory Pool Size" + default 1024 + ---help--- + Specifies the node's memory pool size + +config EXAMPLES_LIBCANARD_DAEMON_PRIORITY + int "daemon task priority" + default 100 + +config EXAMPLES_LIBCANARD_STACKSIZE + int "canard stack size" + default 2048 + +endif diff --git a/examples/canard/Make.defs b/examples/canard/Make.defs new file mode 100644 index 000000000..c14b9ec52 --- /dev/null +++ b/examples/canard/Make.defs @@ -0,0 +1,39 @@ +############################################################################ +# apps/examples/canard/Make.defs +# Adds selected applications to apps/ build +# +# Copyright (C) 2015 Omni Hoverboards Inc. All rights reserved. +# Author: Paul Alexander Patience +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +ifeq ($(CONFIG_EXAMPLES_LIBCANARD),y) +CONFIGURED_APPS += examples/canard +endif diff --git a/examples/canard/Makefile b/examples/canard/Makefile new file mode 100644 index 000000000..5010c6c2e --- /dev/null +++ b/examples/canard/Makefile @@ -0,0 +1,52 @@ +############################################################################ +# apps/examples/canard/Makefile +# +# Copyright (C) 2015 Omni Hoverboards Inc. All rights reserved. +# Author: Paul Alexander Patience +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# 3. Neither the name NuttX nor the names of its contributors may be +# used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS +# OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +############################################################################ + +-include $(TOPDIR)/Make.defs + +CONFIG_EXAMPLES_LIBCANARD_STACKSIZE ?= 2048 + +CONFIG_XYZ_PROGNAME ?= canard$(EXEEXT) +PROGNAME = $(CONFIG_XYZ_PROGNAME) + +APPNAME = canard +PRIORITY = SCHED_PRIORITY_DEFAULT +STACKSIZE = $(CONFIG_EXAMPLES_LIBCANARD_STACKSIZE) + +ASRCS = +CSRCS = +CFLAGS += -I$(APPDIR)/include/canutils +MAINSRC = canard_main.c + +include $(APPDIR)/Application.mk diff --git a/examples/canard/canard_main.c b/examples/canard/canard_main.c new file mode 100644 index 000000000..8b14d96d6 --- /dev/null +++ b/examples/canard/canard_main.c @@ -0,0 +1,552 @@ +/**************************************************************************** + * examples/canard/canard_main.c + * + * Copyright (C) 2016 ETH Zuerich. All rights reserved. + * Author: Matthias Renner + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include /* CAN backend driver for nuttx, distributed + * with Libcanard */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + + /* Application constants */ + +#define APP_VERSION_MAJOR 1 +#define APP_VERSION_MINOR 0 +#define APP_NODE_NAME CONFIG_EXAMPLES_LIBCANARD_APP_NODE_NAME +#define GIT_HASH 0xb28bf6ac + + /* Some useful constants defined by the UAVCAN specification. + * Data type signature values can be easily obtained with the script + * show_data_type_info.py + */ + +#define UAVCAN_NODE_STATUS_MESSAGE_SIZE 7 +#define UAVCAN_NODE_STATUS_DATA_TYPE_ID 341 +#define UAVCAN_NODE_STATUS_DATA_TYPE_SIGNATURE 0x0f0868d0c1a7c6f1 + +#define UAVCAN_NODE_HEALTH_OK 0 +#define UAVCAN_NODE_HEALTH_WARNING 1 +#define UAVCAN_NODE_HEALTH_ERROR 2 +#define UAVCAN_NODE_HEALTH_CRITICAL 3 + +#define UAVCAN_NODE_MODE_OPERATIONAL 0 +#define UAVCAN_NODE_MODE_INITIALIZATION 1 + +#define UAVCAN_GET_NODE_INFO_RESPONSE_MAX_SIZE ((3015 + 7) / 8) +#define UAVCAN_GET_NODE_INFO_DATA_TYPE_SIGNATURE 0xee468a8121c46a9e +#define UAVCAN_GET_NODE_INFO_DATA_TYPE_ID 1 + +#define UNIQUE_ID_LENGTH_BYTES 16 + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Library instance. + * In simple applications it makes sense to make it static, but it is not + * necessary. + */ + +static CanardInstance canard; + +/* Arena for memory allocation, used by the library */ + +static uint8_t canard_memory_pool[CONFIG_EXAMPLES_LIBCANARD_NODE_MEM_POOL_SIZE]; + +static uint8_t unique_id[UNIQUE_ID_LENGTH_BYTES] = +{ 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01 +}; + +/* Node status variables */ + +static uint8_t node_health = UAVCAN_NODE_HEALTH_OK; +static uint8_t node_mode = UAVCAN_NODE_MODE_INITIALIZATION; +static bool g_canard_daemon_started; + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: getMonotonicTimestampUSec + * + * Description: + * + ****************************************************************************/ + +uint64_t getMonotonicTimestampUSec(void) +{ + struct timespec ts; + + memset(&ts, 0, sizeof(ts)); + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) + { + abort(); + } + + return ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000ULL; +} + +/**************************************************************************** + * Name: makeNodeStatusMessage + * + * Description: + * + ****************************************************************************/ + +void makeNodeStatusMessage(uint8_t buffer[UAVCAN_NODE_STATUS_MESSAGE_SIZE]) +{ + static uint32_t started_at_sec = 0; + + memset(buffer, 0, UAVCAN_NODE_STATUS_MESSAGE_SIZE); + + if (started_at_sec == 0) + { + started_at_sec = (uint32_t) (getMonotonicTimestampUSec() / 1000000U); + } + + const uint32_t uptime_sec = + (uint32_t) ((getMonotonicTimestampUSec() / 1000000U) - started_at_sec); + + /* Here we're using the helper for demonstrational purposes; in this + * simple case it could be preferred to encode the values manually. + */ + + canardEncodeScalar(buffer, 0, 32, &uptime_sec); + canardEncodeScalar(buffer, 32, 2, &node_health); + canardEncodeScalar(buffer, 34, 3, &node_mode); +} + +/**************************************************************************** + * Name: onTransferReceived + * + * Description: + * This callback is invoked by the library when a new message or request + * or response is received. + * + ****************************************************************************/ + +static void onTransferReceived(CanardInstance *ins, + CanardRxTransfer *transfer) +{ + if ((transfer->transfer_type == CanardTransferTypeRequest) && + (transfer->data_type_id == UAVCAN_GET_NODE_INFO_DATA_TYPE_ID)) + { + printf("GetNodeInfo request from %d\n", transfer->source_node_id); + + uint8_t buffer[UAVCAN_GET_NODE_INFO_RESPONSE_MAX_SIZE]; + memset(buffer, 0, UAVCAN_GET_NODE_INFO_RESPONSE_MAX_SIZE); + + /* NodeStatus */ + + makeNodeStatusMessage(buffer); + + /* SoftwareVersion */ + + buffer[7] = APP_VERSION_MAJOR; + buffer[8] = APP_VERSION_MINOR; + buffer[9] = 1; /* Optional field flags, VCS commit is set */ + + /* uint32_t u32 = GIT_HASH; + * canardEncodeScalar(buffer, 80, 32, &u32); + */ + + /* Image CRC skipped */ + /* HardwareVersion */ + /* Major skipped */ + /* Minor skipped */ + + memcpy(&buffer[24], unique_id, UNIQUE_ID_LENGTH_BYTES); + + /* Certificate of authenticity skipped */ + /* Name */ + + const size_t name_len = strlen(APP_NODE_NAME); + memcpy(&buffer[41], APP_NODE_NAME, name_len); + + const size_t total_size = 41 + name_len; + + /* Transmitting; in this case we don't have to release the payload + * because it's empty anyway. + */ + + const int resp_res = + canardRequestOrRespond(ins, + transfer->source_node_id, + UAVCAN_GET_NODE_INFO_DATA_TYPE_SIGNATURE, + UAVCAN_GET_NODE_INFO_DATA_TYPE_ID, + &transfer->transfer_id, + transfer->priority, + CanardResponse, + &buffer[0], + (uint16_t) total_size); + if (resp_res <= 0) + { + (void)fprintf(stderr, "Could not respond to GetNodeInfo; error %d\n", + resp_res); + } + } +} + +/**************************************************************************** + * Name: shouldAcceptTransfer + * + * Description: + * This callback is invoked by the library when it detects beginning of a + * new transfer on the bus that can be received by the local node. + * If the callback returns true, the library will receive the transfer. + * If the callback returns false, the library will ignore the transfer. + * All transfers that are addressed to other nodes are always ignored. + * + ****************************************************************************/ + +static bool shouldAcceptTransfer(const CanardInstance * ins, + uint64_t * out_data_type_signature, + uint16_t data_type_id, + CanardTransferType transfer_type, + uint8_t source_node_id) +{ + (void)source_node_id; + + if (canardGetLocalNodeID(ins) == CANARD_BROADCAST_NODE_ID) + { + /* If we're in the process of allocation of dynamic node ID, accept + * only relevant transfers. + */ + } + else + { + if ((transfer_type == CanardTransferTypeRequest) && + (data_type_id == UAVCAN_GET_NODE_INFO_DATA_TYPE_ID)) + { + *out_data_type_signature = UAVCAN_GET_NODE_INFO_DATA_TYPE_SIGNATURE; + return true; + } + } + + return false; +} + +/**************************************************************************** + * Name: process1HzTasks + * + * Description: + * This function is called at 1 Hz rate from the main loop. + * + ****************************************************************************/ + +void process1HzTasks(uint64_t timestamp_usec) +{ + /* Purging transfers that are no longer transmitted. This will occasionally + * free up some memory. + */ + + canardCleanupStaleTransfers(&canard, timestamp_usec); + + /* Printing the memory usage statistics. */ + + { + const CanardPoolAllocatorStatistics stats = + canardGetPoolAllocatorStatistics(&canard); + const unsigned peak_percent = + 100U * stats.peak_usage_blocks / stats.capacity_blocks; + +#ifdef CONFIG_DEBUG_CAN + printf + ("Memory pool stats: capacity %u blocks, usage %u blocks, peak usage %u blocks (%u%%)\n", + stats.capacity_blocks, stats.current_usage_blocks, + stats.peak_usage_blocks, peak_percent); +#endif + + /* The recommended way to establish the minimal size of the memory pool + * is to stress-test the application and record the worst case memory + * usage. + */ + + if (peak_percent > 70) + { + puts("WARNING: ENLARGE MEMORY POOL"); + } + } + + /* Transmitting the node status message periodically. */ + + { + uint8_t buffer[UAVCAN_NODE_STATUS_MESSAGE_SIZE]; + makeNodeStatusMessage(buffer); + + static uint8_t transfer_id; + + const int bc_res = + canardBroadcast(&canard, UAVCAN_NODE_STATUS_DATA_TYPE_SIGNATURE, + UAVCAN_NODE_STATUS_DATA_TYPE_ID, &transfer_id, + CANARD_TRANSFER_PRIORITY_LOW, + buffer, UAVCAN_NODE_STATUS_MESSAGE_SIZE); + if (bc_res <= 0) + { + (void)fprintf(stderr, "Could not broadcast node status; error %d\n", + bc_res); + } + } + + { + static uint8_t transfer_id; + uint8_t payload[1]; + uint8_t dest_id = 2; + const int resp_res = + canardRequestOrRespond(&canard, dest_id, + UAVCAN_GET_NODE_INFO_DATA_TYPE_SIGNATURE, + UAVCAN_GET_NODE_INFO_DATA_TYPE_ID, &transfer_id, + CANARD_TRANSFER_PRIORITY_LOW, CanardRequest, + payload, 0); + if (resp_res <= 0) + { + (void)fprintf(stderr, "Could not request GetNodeInfo; error %d\n", + resp_res); + } + } + + node_mode = UAVCAN_NODE_MODE_OPERATIONAL; +} + +/**************************************************************************** + * Name: processTxRxOnce + * + * Description: + * Transmits all frames from the TX queue, receives up to one frame. + * + ****************************************************************************/ + +void processTxRxOnce(CanardNuttXInstance * nuttxcan, int timeout_msec) +{ + const CanardCANFrame *txf; + + /* Transmitting */ + + for (txf = NULL; (txf = canardPeekTxQueue(&canard)) != NULL;) + { + const int tx_res = canardNuttXTransmit(nuttxcan, txf, 0); + if (tx_res < 0) /* Failure - drop the frame and report */ + { + canardPopTxQueue(&canard); + (void)fprintf(stderr, + "Transmit error %d, frame dropped, errno '%s'\n", + tx_res, strerror(errno)); + } + else if (tx_res > 0) /* Success - just drop the frame */ + { + canardPopTxQueue(&canard); + } + else /* Timeout - just exit and try again later */ + { + break; + } + } + + /* Receiving */ + + CanardCANFrame rx_frame; + const uint64_t timestamp = getMonotonicTimestampUSec(); + const int rx_res = canardNuttXReceive(nuttxcan, &rx_frame, timeout_msec); + + if (rx_res < 0) /* Failure - report */ + { + (void)fprintf(stderr, "Receive error %d, errno '%s'\n", rx_res, + strerror(errno)); + } + else if (rx_res > 0) /* Success - process the frame */ + { + canardHandleRxFrame(&canard, &rx_frame, timestamp); + } + else + { + ; /* Timeout - nothing to do */ + } +} + +/**************************************************************************** + * Name: canard_daemon + * + * Description: + * + ****************************************************************************/ + +static int canard_daemon(int argc, char *argv[]) +{ + int ret; + int errval = 0; + struct canioc_bittiming_s bt; + static CanardNuttXInstance canardnuttx_instance; + + /* Initialization of the CAN hardware is performed by logic external to + * this test. + */ + + ret = boardctl(BOARDIOC_CAN_INITIALIZE, 0); + if (ret < 0) + { + printf("canard_daemon: ERROR: BOARDIOC_CAN_INITIALIZE failed: %d\n", + ret); + errval = 1; + goto errout; + } + + /* Open the CAN device for reading */ + + ret = canardNuttXInit(&canardnuttx_instance, + CONFIG_EXAMPLES_LIBCANARD_DEVPATH); + if (ret < 0) + { + printf("canard_daemon: ERROR: open %s failed: %d\n", + CONFIG_EXAMPLES_LIBCANARD_DEVPATH, errno); + errval = 2; + goto errout_with_dev; + } + +#ifdef CONFIG_DEBUG_CAN + /* Show bit timing information if provided by the driver. Not all CAN + * drivers will support this IOCTL. + */ + + ret = + ioctl(canardNuttXGetDeviceFileDescriptor(&canardnuttx_instance), + CANIOC_GET_BITTIMING, (unsigned long)((uintptr_t) & bt)); + if (ret < 0) + { + printf("canard_daemon: Bit timing not available: %d\n", errno); + } + else + { + printf("canard_daemon: Bit timing:\n"); + printf("canard_daemon: Baud: %lu\n", (unsigned long)bt.bt_baud); + printf("canard_daemon: TSEG1: %u\n", bt.bt_tseg1); + printf("canard_daemon: TSEG2: %u\n", bt.bt_tseg2); + printf("canard_daemon: SJW: %u\n", bt.bt_sjw); + } +#endif + + canardInit(&canard, canard_memory_pool, sizeof(canard_memory_pool), + onTransferReceived, shouldAcceptTransfer); + canardSetLocalNodeID(&canard, CONFIG_EXAMPLES_LIBCANARD_NODE_ID); + printf("canard_daemon: canard initialized\n"); + printf("start node (ID: %d Name: %s)\n", CONFIG_EXAMPLES_LIBCANARD_NODE_ID, + APP_NODE_NAME); + + g_canard_daemon_started = true; + uint64_t next_1hz_service_at = getMonotonicTimestampUSec(); + + for (;;) + { + processTxRxOnce(&canardnuttx_instance, 10); + + const uint64_t ts = getMonotonicTimestampUSec(); + + if (ts >= next_1hz_service_at) + { + next_1hz_service_at += 1000000; + process1HzTasks(ts); + } + } + +errout_with_dev: + canardNuttXClose(&canardnuttx_instance); + +errout: + g_canard_daemon_started = false; + printf("canard_daemon: Terminating!\n"); + fflush(stdout); + return errval; +} + +/**************************************************************************** + * Name: canard_main + * + * Description: + * + ****************************************************************************/ + +#ifdef CONFIG_BUILD_KERNEL +int main(int argc, FAR char *argv[]) +#else +int canard_main(int argc, FAR char *argv[]) +#endif +{ + FAR char *canardargv[2]; + int ret; + + printf("canard_main: Starting canard_daemon\n"); + if (g_canard_daemon_started) + { + printf("canard_main: receive and send task already running\n"); + return EXIT_SUCCESS; + } + + canardargv[0] = "canard_daemon"; + canardargv[1] = NULL; + + ret = task_create("canard_daemon", CONFIG_EXAMPLES_LIBCANARD_DAEMON_PRIORITY, + CONFIG_EXAMPLES_LIBCANARD_STACKSIZE, canard_daemon, + (FAR char *const *)canardargv); + if (ret < 0) + { + int errcode = errno; + printf("canard_main: ERROR: Failed to start canard_daemon: %d\n", + errcode); + return EXIT_FAILURE; + } + + printf("canard_main: canard_daemon started\n"); + return EXIT_SUCCESS; +}