Skip to content

Commit

Permalink
support both multicast UDP and socketcan in ESCNode
Browse files Browse the repository at this point in the history
  • Loading branch information
tridge committed Aug 31, 2023
1 parent 3f71ad5 commit 2c8e3f4
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 12 deletions.
92 changes: 92 additions & 0 deletions drivers/linux/linux.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
implement LinuxCAN, wrapper around socketcan and multicast UDP
*/

#include "linux.h"
#include <string.h>
#include <stdlib.h>
#include <errno.h>

/*
Initializes the instance.
Returns 0 on success, negative on error.
*/
#if CANARD_ENABLE_CANFD
int16_t LinuxCANInit(LinuxCANInstance* out_ins, const char* can_iface_name, bool canfd)
#else
int16_t LinuxCANInit(LinuxCANInstance* out_ins, const char* can_iface_name)
#endif
{
out_ins->socketcan = NULL;
out_ins->mcast = NULL;
if (strncmp(can_iface_name, "vcan", 4) == 0) {
out_ins->socketcan = (SocketCANInstance *)calloc(1, sizeof(SocketCANInstance));
if (out_ins->socketcan == NULL) {
return -ENOMEM;
}
#if CANARD_ENABLE_CANFD
return socketcanInit(out_ins->socketcan, can_iface_name, can_fd);
#else
return socketcanInit(out_ins->socketcan, can_iface_name);
#endif
}
if (strncmp(can_iface_name, "mcast", 5) == 0) {
out_ins->mcast = (MCASTCANInstance *)calloc(1, sizeof(MCASTCANInstance));
if (out_ins->mcast == NULL) {
return -ENOMEM;
}
#if CANARD_ENABLE_CANFD
return mcastInit(out_ins->mcast, can_iface_name, can_fd);
#else
return mcastInit(out_ins->mcast, can_iface_name);
#endif
}
return -EINVAL;
}

/*
Deinitializes the mcast instance.
Returns 0 on success, negative on error.
*/
int16_t LinuxCANClose(LinuxCANInstance* ins)
{
if (ins->socketcan != NULL) {
return socketcanClose(ins->socketcan);
}
if (ins->mcast != NULL) {
return mcastClose(ins->mcast);
}
return -EINVAL;
}

/*
Transmits a CanardCANFrame to the CAN socket.
Use negative timeout to block infinitely.
Returns 1 on successful transmission, 0 on timeout, negative on error.
*/
int16_t LinuxCANTransmit(LinuxCANInstance* ins, const CanardCANFrame* frame, int32_t timeout_msec)
{
if (ins->socketcan != NULL) {
return socketcanTransmit(ins->socketcan, frame, timeout_msec);
}
if (ins->mcast != NULL) {
return mcastTransmit(ins->mcast, frame, timeout_msec);
}
return -EINVAL;
}

/*
Receives a CanardCANFrame from the CAN socket.
Use negative timeout to block infinitely.
Returns 1 on successful reception, 0 on timeout, negative on error.
*/
int16_t LinuxCANReceive(LinuxCANInstance* ins, CanardCANFrame* out_frame, int32_t timeout_msec)
{
if (ins->socketcan != NULL) {
return socketcanReceive(ins->socketcan, out_frame, timeout_msec);
}
if (ins->mcast != NULL) {
return mcastReceive(ins->mcast, out_frame, timeout_msec);
}
return -EINVAL;
}
59 changes: 59 additions & 0 deletions drivers/linux/linux.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2023 DroneCAN Team
*
* Distributed under the MIT License, available in the file LICENSE.
*/
/*
this wraps the socketcan and multicast drivers, allowing either to be selected
*/

#pragma once

#include <canard.h>
#include "../mcast/mcast.h"
#include "../socketcan/socketcan.h"

#ifdef __cplusplus
extern "C"
{
#endif

typedef struct
{
MCASTCANInstance *mcast;
SocketCANInstance *socketcan;
} LinuxCANInstance;

/*
Initializes the instance.
Returns 0 on success, negative on error.
*/
#if CANARD_ENABLE_CANFD
int16_t LinuxCANInit(LinuxCANInstance* out_ins, const char* can_iface_name, bool canfd);
#else
int16_t LinuxCANInit(LinuxCANInstance* out_ins, const char* can_iface_name);
#endif

/*
Deinitializes the mcast instance.
Returns 0 on success, negative on error.
*/
int16_t LinuxCANClose(LinuxCANInstance* ins);

/*
Transmits a CanardCANFrame to the CAN socket.
Use negative timeout to block infinitely.
Returns 1 on successful transmission, 0 on timeout, negative on error.
*/
int16_t LinuxCANTransmit(LinuxCANInstance* ins, const CanardCANFrame* frame, int32_t timeout_msec);

/*
Receives a CanardCANFrame from the CAN socket.
Use negative timeout to block infinitely.
Returns 1 on successful reception, 0 on timeout, negative on error.
*/
int16_t LinuxCANReceive(LinuxCANInstance* ins, CanardCANFrame* out_frame, int32_t timeout_msec);

#ifdef __cplusplus
}
#endif
6 changes: 4 additions & 2 deletions examples/ESCNode/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
CANARD_BASE=../..

CC=gcc
CFLAGS = -g -Wall -Idsdl_generated/include -I$(CANARD_BASE) -I$(CANARD_BASE)/drivers/socketcan
CFLAGS = -g -Wall -Idsdl_generated/include -I$(CANARD_BASE) -I$(CANARD_BASE)/drivers/linux

LIBS=$(CANARD_BASE)/canard.c

# add socketcan driver for linux
# add socketcan and multicast drivers for linux
LIBS+=$(CANARD_BASE)/drivers/socketcan/socketcan.c
LIBS+=$(CANARD_BASE)/drivers/mcast/mcast.c
LIBS+=$(CANARD_BASE)/drivers/linux/linux.c

# add in generated code
LIBS+=dsdl_generated/src/uavcan.protocol.NodeStatus.c
Expand Down
22 changes: 12 additions & 10 deletions examples/ESCNode/esc_node.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
- sends ESC Status messages (with synthetic data based on throttles)
- a parameter server for reading and writing node parameters
This example uses socketcan on Linux for CAN transport
This example uses socketcan or multicast UDP on Linux for CAN transport
Example usage: ./esc_node vcan0
Example usage:
./esc_node vcan0
./esc_node mcast:0
*/
/*
This example application is distributed under the terms of CC0 (public domain dedication).
Expand All @@ -24,7 +26,7 @@
#endif

#include <canard.h>
#include <socketcan.h>
#include <linux.h>

#include <stdio.h>
#include <stdlib.h>
Expand Down Expand Up @@ -631,11 +633,11 @@ static void send_ESCStatus(void)
/*
Transmits all frames from the TX queue, receives up to one frame.
*/
static void processTxRxOnce(SocketCANInstance *socketcan, int32_t timeout_msec)
static void processTxRxOnce(LinuxCANInstance *can, int32_t timeout_msec)
{
// Transmitting
for (const CanardCANFrame* txf = NULL; (txf = canardPeekTxQueue(&canard)) != NULL;) {
const int16_t tx_res = socketcanTransmit(socketcan, txf, 0);
const int16_t tx_res = LinuxCANTransmit(can, txf, 0);
if (tx_res < 0) { // Failure - drop the frame
canardPopTxQueue(&canard);
}
Expand All @@ -653,7 +655,7 @@ static void processTxRxOnce(SocketCANInstance *socketcan, int32_t timeout_msec)
CanardCANFrame rx_frame;

const uint64_t timestamp = micros64();
const int16_t rx_res = socketcanReceive(socketcan, &rx_frame, timeout_msec);
const int16_t rx_res = LinuxCANReceive(can, &rx_frame, timeout_msec);
if (rx_res < 0) {
(void)fprintf(stderr, "Receive error %d, errno '%s'\n", rx_res, strerror(errno));
}
Expand All @@ -679,11 +681,11 @@ int main(int argc, char** argv)
load_settings();

/*
* Initializing the CAN backend driver; in this example we're using SocketCAN
* Initializing the CAN backend driver
*/
SocketCANInstance socketcan;
LinuxCANInstance can;
const char* const can_iface_name = argv[1];
int16_t res = socketcanInit(&socketcan, can_iface_name);
int16_t res = LinuxCANInit(&can, can_iface_name);
if (res < 0) {
(void)fprintf(stderr, "Failed to open CAN iface '%s'\n", can_iface_name);
return 1;
Expand Down Expand Up @@ -712,7 +714,7 @@ int main(int argc, char** argv)
uint64_t next_50hz_service_at = micros64();

while (true) {
processTxRxOnce(&socketcan, 10);
processTxRxOnce(&can, 10);

const uint64_t ts = micros64();

Expand Down

0 comments on commit 2c8e3f4

Please sign in to comment.