/**************************************************************************** * apps/examples/opencyphal/canard_main.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "socketcan.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ /* Application constants */ #define APP_VERSION_MAJOR 1 #define APP_VERSION_MINOR 0 #define APP_NODE_NAME CONFIG_EXAMPLES_LIBOPENCYPHAL_APP_NODE_NAME /**************************************************************************** * Private Data ****************************************************************************/ /* Arena for memory allocation, used by the library */ #define O1_HEAP_SIZE CONFIG_EXAMPLES_LIBOPENCYPHAL_NODE_MEM_POOL_SIZE /* Temporary development UAVCAN topic service ID to publish/subscribe from */ #define PORT_ID 4421 #define TOPIC_SIZE 512 O1HeapInstance *my_allocator; static uint8_t uavcan_heap[O1_HEAP_SIZE] __attribute__((aligned(O1HEAP_ALIGNMENT))); /* Node status variables */ static bool g_canard_daemon_started; static uint8_t my_message_transfer_id; struct pollfd fd; /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: memallocate * * Description: * ****************************************************************************/ static void *memallocate(CanardInstance *const ins, const size_t amount) { (void) ins; return o1heapAllocate(my_allocator, amount); } /**************************************************************************** * Name: memfree * * Description: * ****************************************************************************/ static void memfree(CanardInstance *const ins, void *const pointer) { (void) ins; o1heapFree(my_allocator, pointer); } /**************************************************************************** * 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: process1hztasks * * Description: * This function is called at 1 Hz rate from the main loop. * ****************************************************************************/ void process1hztasks(CanardInstance *ins, uint64_t timestamp_usec) { CanardMicrosecond transmission_deadline = getmonotonictimestampusec() + 1000 * 10; const CanardTransfer transfer = { .timestamp_usec = transmission_deadline, /* Zero if transmission deadline is not limited. */ .priority = CanardPriorityNominal, .transfer_kind = CanardTransferKindMessage, .port_id = 1234, /* This is the subject-ID. */ .remote_node_id = CANARD_NODE_ID_UNSET, /* Messages cannot be unicast, so use UNSET. */ .transfer_id = my_message_transfer_id, .payload_size = 47, .payload = "\x2D\x00" "Sancho, it strikes me thou art in great fear.", }; ++my_message_transfer_id; /* The transfer-ID shall be incremented after every transmission on this subject. */ int32_t result = canardTxPush(ins, &transfer); if (result < 0) { /* An error has occurred: either an argument is invalid or we've * ran out of memory. It is possible to statically prove that an * out-of-memory will never occur for a given application if the * heap is sized correctly; for background, refer to the Robson's * Proof and the documentation for O1Heap. */ fprintf(stderr, "Transmit error %ld\n", result); } } /**************************************************************************** * Name: processTxRxOnce * * Description: * Transmits all frames from the TX queue, receives up to one frame. * ****************************************************************************/ void processtxrxonce(CanardInstance *ins, canardsocketinstance *sock_ins, int timeout_msec) { int32_t result; /* Transmitting, Look at the top of the TX queue. */ for (const CanardFrame *txf = NULL; (txf = canardTxPeek(ins)) != NULL; ) { if (txf->timestamp_usec > getmonotonictimestampusec()) /* Check if the frame has timed out. */ { if (socketcantransmit(sock_ins, txf) == 0) /* Send the frame. Redundant interfaces may be used here. */ { /* If the driver is busy, break and retry later. */ break; } } canardTxPop(ins); /* Remove the frame from the queue after it's transmitted. */ ins->memory_free(ins, (CanardFrame *)txf); } /* Poll receive */ if (poll(&fd, 1, timeout_msec) <= 0) { return; } /* Receiving */ CanardFrame received_frame; socketcanreceive(sock_ins, &received_frame); CanardTransfer receive; result = canardRxAccept(ins, &received_frame, /* The CAN frame received from the bus. */ 0, /* If the transport is not redundant, use 0. */ &receive); if (result < 0) { /* An error has occurred: either an argument is invalid or we've ran * out of memory. It is possible to statically prove that an * out-of-memory will never occur for a given application * if the heap is sized correctly; for background, refer to the * Robson's Proof and the documentation for O1Heap. * Reception of an invalid frame is NOT an error. */ fprintf(stderr, "Receive error %ld\n", result); } else if (result == 1) { /* A transfer has been received, process it */ printf("Receive UAVCAN port id%d TODO process me\n", receive.port_id); ins->memory_free(ins, (void *)receive.payload); } else { /* Nothing to do. * The received frame is either invalid or it's a non-last frame * of a multi-frame transfer. * Reception of an invalid frame is NOT reported as an error * because it is not an error. */ } } /**************************************************************************** * Name: canard_daemon * * Description: * ****************************************************************************/ static int canard_daemon(int argc, char *argv[]) { int errval = 0; int can_fd = 0; int pub = 1; if (argc > 2) { for (int args = 2; args < argc; args++) { if (!strcmp(argv[args], "canfd")) { can_fd = 1; } if (!strcmp(argv[args], "pub")) { pub = 1; } if (!strcmp(argv[args], "sub")) { pub = 0; } } } my_allocator = o1heapInit(&uavcan_heap, O1_HEAP_SIZE); if (my_allocator == NULL) { printf("o1heapInit failed with size %d\n", O1_HEAP_SIZE); errval = 2; goto errout_with_dev; } CanardInstance ins = canardInit(&memallocate, &memfree); if (can_fd) { ins.mtu_bytes = CANARD_MTU_CAN_FD; } else { ins.mtu_bytes = CANARD_MTU_CAN_CLASSIC; } ins.node_id = (pub ? CONFIG_EXAMPLES_LIBOPENCYPHAL_NODE_ID : CONFIG_EXAMPLES_LIBOPENCYPHAL_NODE_ID + 1); /* Open the CAN device for reading */ canardsocketinstance sock_ins; socketcanopen(&sock_ins, CONFIG_EXAMPLES_LIBOPENCYPHAL_DEV, can_fd); /* setup poll fd */ fd.fd = sock_ins.s; fd.events = POLLIN; if (sock_ins.s < 0) { printf("canard_daemon: ERROR: open %s failed: %d\n", CONFIG_EXAMPLES_LIBOPENCYPHAL_DEV, errno); errval = 2; goto errout_with_dev; } printf("canard_daemon: canard initialized\n"); printf("start node (ID: %d Name: %s MTU: %d PUB: %d TOPIC_SIZE: %d)\n", ins.node_id, APP_NODE_NAME, ins.mtu_bytes, pub, TOPIC_SIZE); CanardRxSubscription heartbeat_subscription; (void) canardRxSubscribe(&ins, /* Subscribe to messages uavcan.node.Heartbeat. */ CanardTransferKindMessage, 32085, /* The fixed Subject-ID of the Heartbeat message type (see DSDL definition). */ 7, /* The maximum payload size (max DSDL object size) from the DSDL definition. */ CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC, &heartbeat_subscription); CanardRxSubscription my_subscription; (void) canardRxSubscribe(&ins, CanardTransferKindMessage, PORT_ID, /* The Service-ID to subscribe to. */ TOPIC_SIZE, /* The maximum payload size (max DSDL object size). */ CANARD_DEFAULT_TRANSFER_ID_TIMEOUT_USEC, &my_subscription); g_canard_daemon_started = true; uint64_t next_1hz_service_at = getmonotonictimestampusec(); for (; ; ) { processtxrxonce(&ins, &sock_ins, 10); const uint64_t ts = getmonotonictimestampusec(); if (ts >= next_1hz_service_at) { next_1hz_service_at += 1000000; process1hztasks(&ins, ts); } } errout_with_dev: g_canard_daemon_started = false; printf("canard_daemon: Terminating!\n"); fflush(stdout); return errval; } /**************************************************************************** * Name: canard_main * * Description: * ****************************************************************************/ int opencyphal_main(int argc, FAR char *argv[]) { 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; } ret = task_create("canard_daemon", CONFIG_EXAMPLES_LIBOPENCYPHAL_DAEMON_PRIORITY, CONFIG_EXAMPLES_LIBOPENCYPHAL_DAEMON_STACK_SIZE, canard_daemon, argv); 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; }