libsvah Library API Reference
v0.5.2.86
Table of Contents
- 1. Introduction
- 2. HalCan — CAN Hardware Abstraction Layer
- 3. HalTime — Monotonic Time Abstraction
- 4. CanopenSimple — CANSimple Protocol Codec
- 4.1. Types
- 4.2. Functions
- 4.2.1.
CansimpMakeId - 4.2.2.
CansimpParseId - 4.2.3.
CansimpEncodeSetInputPos - 4.2.4.
CansimpEncodeSetInputVel - 4.2.5.
CansimpEncodeSetInputTorque - 4.2.6.
CansimpEncodeSetControllerMode - 4.2.7.
CansimpEncodeSetLimits - 4.2.8.
CansimpEncodeSetTrajVelLimit - 4.2.9.
CansimpEncodeSetTrajAccelLimit - 4.2.10.
CansimpEncodeSetTrajInertia - 4.2.11.
CansimpEncodeSetAbsolutePos - 4.2.12.
CansimpEncodeSetPosGain - 4.2.13.
CansimpEncodeSetVelGains - 4.2.14.
CansimpEncodeSetAxisState - 4.2.15.
CansimpDecodeHeartbeat - 4.2.16.
CansimpDecodeEncoderEstimates - 4.2.17.
CansimpDecodeEncoderCount - 4.2.18.
CansimpDecodeIq - 4.2.19.
CansimpDecodeTemperature - 4.2.20.
CansimpDecodeBusVoltageCurrent
- 4.2.1.
- 5. OdriveApi — High-Level ODrive Interface
- 5.1. Types
- 5.2. Functions
- 5.2.1.
OdriveAxisInit - 5.2.2.
OdriveSetAxisState - 5.2.3.
OdriveWaitHeartbeat - 5.2.4.
OdriveSetInputPos - 5.2.5.
OdriveSetInputVel - 5.2.6.
OdriveSetInputTorque - 5.2.7.
OdriveSetControllerMode - 5.2.8.
OdriveSetLimits - 5.2.9.
OdriveClearErrors - 5.2.10.
OdriveReboot - 5.2.11.
OdriveRequestEncoderEstimates - 5.2.12.
OdriveRequestIq - 5.2.13.
OdriveRequestTemperature - 5.2.14.
OdriveRequestBusVoltage - 5.2.15.
OdriveFeedWatchdog - 5.2.16.
OdriveProcessHeartbeat - 5.2.17.
OdriveProcessFrame - 5.2.18.
SvahLogSyslogCallback - 5.2.19.
SvahLogStderrCallback
- 5.2.1.
- 5.3. Syslog usage example
1. Introduction
This document is the API reference for libsvah, the C library for
controlling ODrive BLDC motor controllers via the CANSimple protocol.
The library is structured in three layers, each with its own header:
| Header | Layer | Portability |
|---|---|---|
HalCan.h |
CAN hardware abstraction | Platform-specific |
HalTime.h |
Monotonic time abstraction | Platform-specific |
CanopenSimple.h |
CANSimple frame codec | Portable |
OdriveApi.h |
High-level ODrive commands | Portable |
Platform-specific implementations currently provided:
| File | Platform | Interface |
|---|---|---|
HalCanLinux.c |
Linux | SocketCAN |
HalTimeLinux.c |
Linux | CLOCK_MONOTONIC |
2. HalCan — CAN Hardware Abstraction Layer
Defined in HalCan.h. Provides a platform-independent interface
for CAN frame transmission and reception.
The receive model is hybrid: a callback function pointer (nullable)
can be registered for interrupt-driven reception. When the callback is
NULL, incoming frames are buffered in a ring buffer for retrieval by
polling via HalCanReceive().
2.1. Types
2.1.1. HalCanFrame
A single CAN 2.0B data frame.
typedef struct
{
uint32_t id; /* 11-bit or 29-bit CAN identifier */
uint8_t dlc; /* Data Length Code (0..8) */
uint8_t data[8]; /* Payload bytes */
uint8_t isExtended; /* Non-zero if 29-bit extended frame */
uint8_t isRemote; /* Non-zero if RTR frame */
} HalCanFrame;
2.1.2. HalCanStatus
Return code for all HAL CAN functions.
| Value | Meaning |
|---|---|
HAL_CAN_OK |
Success |
HAL_CAN_NO_FRAME |
No frame available (polling, non-fatal) |
HAL_CAN_ERR_INIT |
Initialisation failure |
HAL_CAN_ERR_TX |
Transmit failure |
HAL_CAN_ERR_RX |
Receive failure |
HAL_CAN_ERR_PARAM |
Invalid parameter (NULL pointer, etc.) |
2.1.3. HalCanRxCallback
Prototype for the optional receive callback.
typedef void (*HalCanRxCallback)(const HalCanFrame *pFrame,
void *pUserData);
The callback may be invoked from an interrupt context on bare-metal targets. Keep it short and non-blocking.
2.1.4. HAL_CAN_RX_BUFFER_SIZE
Compile-time constant controlling the receive ring buffer depth.
Default: 16. Must be a power of two. Override with
-DHAL_CAN_RX_BUFFER_SIZE=n at compile time.
2.2. Functions
2.2.1. HalCanInit
HalCanStatus HalCanInit(HalCanHandle **ppHandle,
const char *pInterface,
uint32_t pBitrate);
Initialise the CAN interface.
ppHandle- out — receives the allocated handle on success; set
to
NULLon failure. pInterface- platform-specific name. Linux:
"can0","vcan0", etc. ESP32: ignored (single TWAI peripheral). pBitrate- bus bitrate in bits/s (e.g. 250000, 500000, 1000000).
On Linux, the bitrate must be configured externally via
ip(8); this value is informational only.
Returns HAL_CAN_OK on success, HAL_CAN_ERR_INIT on failure.
2.2.2. HalCanSetRxCallback
void HalCanSetRxCallback(HalCanHandle *pHandle,
HalCanRxCallback pCallback,
void *pUserData);
Register an optional receive callback. Pass NULL to revert to
polling mode. Call after HalCanInit and before frames are expected.
2.2.3. HalCanTransmit
HalCanStatus HalCanTransmit(HalCanHandle *pHandle,
const HalCanFrame *pFrame);
Transmit a single CAN frame. Returns HAL_CAN_OK on success,
HAL_CAN_ERR_TX on failure.
2.2.4. HalCanReceive
HalCanStatus HalCanReceive(HalCanHandle *pHandle,
HalCanFrame *pFrame);
Retrieve one frame in polling mode.
| Return value | Meaning |
|---|---|
HAL_CAN_OK |
Frame written to *pFrame |
HAL_CAN_NO_FRAME |
No frame available |
HAL_CAN_ERR_RX |
Receive error |
HAL_CAN_ERR_PARAM |
pHandle or pFrame is NULL |
2.2.5. HalCanClose
void HalCanClose(HalCanHandle *pHandle);
Release all resources. After this call pHandle must not be used.
3. HalTime — Monotonic Time Abstraction
Defined in HalTime.h. Provides a platform-independent monotonic
time source for timeout management and watchdog feeding.
Both functions return uint32_t values that wrap at UINT32_MAX.
Always compute elapsed time with unsigned subtraction:
uint32_t lStart = HalTimeMs(); uint32_t lElapsed = HalTimeMs() - lStart; /* correct across wrap */
3.1. Functions
3.1.1. HalTimeMs
uint32_t HalTimeMs(void);
Monotonic timestamp in milliseconds. Wraps at UINT32_MAX (~49.7 days).
3.1.2. HalTimeUs
uint32_t HalTimeUs(void);
Monotonic timestamp in microseconds. Wraps at UINT32_MAX (~71.6 min).
Use for short-duration measurements only.
4. CanopenSimple — CANSimple Protocol Codec
Defined in CanopenSimple.h. Portable, platform-independent
encoding and decoding of ODrive CANSimple protocol frames.
The CANSimple message identifier is:
msg_id = (axis_id << 5) | ~cmd_id~
Payload fields are encoded by memcpy of native C types
(float, uint32_t, int16_t) into an 8-byte payload, in
host byte order (little-endian on x86/ARM/ESP32).
4.1. Types
4.1.1. CansimpCmd
Enumeration of all CANSimple command identifiers (5-bit cmd_id
field). Notable values:
| Constant | Value | Direction |
|---|---|---|
CANSIMP_CMD_HEARTBEAT |
0x001 | RX |
CANSIMP_CMD_SET_AXIS_STATE |
0x007 | TX |
CANSIMP_CMD_SET_INPUT_POS |
0x00C | TX |
CANSIMP_CMD_SET_INPUT_VEL |
0x00D | TX |
CANSIMP_CMD_SET_INPUT_TORQUE |
0x00E | TX |
CANSIMP_CMD_CLEAR_ERRORS |
0x018 | TX |
See CanopenSimple.h for the full enumeration.
4.1.2. AxisState
ODrive axis state machine states. Notable values:
| Constant | Value |
|---|---|
AXIS_STATE_IDLE |
1 |
AXIS_STATE_FULL_CALIBRATION_SEQ |
3 |
AXIS_STATE_CLOSED_LOOP_CONTROL |
8 |
4.1.3. ControlMode
Controller operating mode.
| Constant | Value |
|---|---|
CONTROL_MODE_VOLTAGE_CONTROL |
0 |
CONTROL_MODE_TORQUE_CONTROL |
1 |
CONTROL_MODE_VELOCITY_CONTROL |
2 |
CONTROL_MODE_POSITION_CONTROL |
3 |
4.1.4. InputMode
How setpoints are interpreted by the controller.
| Constant | Value | Description |
|---|---|---|
INPUT_MODE_INACTIVE |
0 | No input |
INPUT_MODE_PASSTHROUGH |
1 | Direct setpoint |
INPUT_MODE_VEL_RAMP |
2 | Velocity ramp |
INPUT_MODE_POS_FILTER |
3 | Second-order position filter |
INPUT_MODE_TRAP_TRAJ |
5 | Trapezoidal trajectory planner |
INPUT_MODE_TORQUE_RAMP |
6 | Torque ramp |
4.2. Functions
4.2.1. CansimpMakeId
uint32_t CansimpMakeId(uint8_t pAxisId, CansimpCmd pCmdId);
Build a CANSimple 11-bit message identifier.
4.2.2. CansimpParseId
void CansimpParseId(uint32_t pMsgId,
uint8_t *pAxisId,
CansimpCmd *pCmdId);
Decompose a received CAN message identifier into axis id and command.
4.2.3. CansimpEncodeSetInputPos
void CansimpEncodeSetInputPos(uint8_t pAxisId,
float pInputPos,
int16_t pVelFeedForward,
int16_t pTorqueFeedForward,
HalCanFrame *pFrame);
Encode a Set Input Position command (cmd 0x00C).
pInputPos- position setpoint in turns.
pVelFeedForward- velocity feed-forward in turns/s, Q16.
pTorqueFeedForward- torque feed-forward in Nm, Q16.
4.2.4. CansimpEncodeSetInputVel
void CansimpEncodeSetInputVel(uint8_t pAxisId,
float pInputVel,
float pTorqueFeedForward,
HalCanFrame *pFrame);
Encode a Set Input Velocity command (cmd 0x00D).
4.2.5. CansimpEncodeSetInputTorque
void CansimpEncodeSetInputTorque(uint8_t pAxisId,
float pInputTorque,
HalCanFrame *pFrame);
Encode a Set Input Torque command (cmd 0x00E).
pInputTorque- torque setpoint in Nm.
4.2.6. CansimpEncodeSetControllerMode
void CansimpEncodeSetControllerMode(uint8_t pAxisId,
ControlMode pControlMode,
InputMode pInputMode,
HalCanFrame *pFrame);
Encode a Set Controller Mode command (cmd 0x00B). Must be sent
before entering AXIS_STATE_CLOSED_LOOP_CONTROL.
4.2.7. CansimpEncodeSetLimits
void CansimpEncodeSetLimits(uint8_t pAxisId,
float pVelocityLimit,
float pCurrentLimit,
HalCanFrame *pFrame);
Encode a Set Limits command (cmd 0x00F).
pVelocityLimit- velocity limit in turns/s.
pCurrentLimit- current limit in A.
4.2.8. CansimpEncodeSetTrajVelLimit
void CansimpEncodeSetTrajVelLimit(uint8_t pAxisId,
float pTrajVelLimit,
HalCanFrame *pFrame);
Encode a Set Traj Vel Limit command (cmd 0x011). Used with
INPUT_MODE_TRAP_TRAJ.
4.2.9. CansimpEncodeSetTrajAccelLimit
void CansimpEncodeSetTrajAccelLimit(uint8_t pAxisId,
float pTrajAccelLimit,
float pTrajDecelLimit,
HalCanFrame *pFrame);
Encode a Set Traj Accel Limit command (cmd 0x012).
Both values in turns/s². pTrajDecelLimit should be positive.
4.2.10. CansimpEncodeSetTrajInertia
void CansimpEncodeSetTrajInertia(uint8_t pAxisId,
float pInertia,
HalCanFrame *pFrame);
Encode a Set Traj Inertia command (cmd 0x013). Units: Nm/(turns/s²).
4.2.11. CansimpEncodeSetAbsolutePos
void CansimpEncodeSetAbsolutePos(uint8_t pAxisId,
float pAbsPos,
HalCanFrame *pFrame);
Encode a Set Absolute Position command (cmd 0x019). Sets the encoder shadow count without commanding motion.
4.2.12. CansimpEncodeSetPosGain
void CansimpEncodeSetPosGain(uint8_t pAxisId,
float pPosGain,
HalCanFrame *pFrame);
Encode a Set Pos Gain command (cmd 0x01A). Units: (turns/s)/turn.
4.2.13. CansimpEncodeSetVelGains
void CansimpEncodeSetVelGains(uint8_t pAxisId,
float pVelGain,
float pVelIntegratorGain,
HalCanFrame *pFrame);
Encode a Set Vel Gains command (cmd 0x01B).
pVelGain- velocity proportional gain in Nm/(turns/s).
pVelIntegratorGain- velocity integrator gain in Nm/turn.
4.2.14. CansimpEncodeSetAxisState
void CansimpEncodeSetAxisState(uint8_t pAxisId,
AxisState pState,
HalCanFrame *pFrame);
Encode a Set Axis State command (cmd 0x007).
4.2.15. CansimpDecodeHeartbeat
int CansimpDecodeHeartbeat(const HalCanFrame *pFrame,
uint32_t *pAxisError,
AxisState *pAxisState,
uint8_t *pProcedureResult,
uint8_t *pTrajDone);
Decode a Heartbeat frame (cmd 0x001). Returns 0 on success,
-1 if the frame is not a valid heartbeat. Any output pointer may
be NULL if the corresponding field is not needed.
4.2.16. CansimpDecodeEncoderEstimates
int CansimpDecodeEncoderEstimates(const HalCanFrame *pFrame,
float *pPosEstimate,
float *pVelEstimate);
Decode a Get_Encoder_Estimates frame (cmd 0x009).
Returns 0 on success, -1 on error.
4.2.17. CansimpDecodeEncoderCount
int CansimpDecodeEncoderCount(const HalCanFrame *pFrame,
int32_t *pShadowCount,
int32_t *pCountCpr);
Decode a Get_Encoder_Count frame (cmd 0x00A).
4.2.18. CansimpDecodeIq
int CansimpDecodeIq(const HalCanFrame *pFrame,
float *pIqSetpoint,
float *pIqMeasured);
Decode a Get_Iq frame (cmd 0x014). Units: A.
4.2.19. CansimpDecodeTemperature
int CansimpDecodeTemperature(const HalCanFrame *pFrame,
float *pFetTemp,
float *pMotorTemp);
Decode a Get_Temperature frame (cmd 0x015). Units: °C.
4.2.20. CansimpDecodeBusVoltageCurrent
int CansimpDecodeBusVoltageCurrent(const HalCanFrame *pFrame,
float *pBusVoltage,
float *pBusCurrent);
Decode a Get_Bus_Voltage_Current frame (cmd 0x017).
Units: V and A respectively.
5. OdriveApi — High-Level ODrive Interface
Defined in OdriveApi.h. Builds on CanopenSimple and HalCan
to provide application-level motor control commands.
5.1. Types
5.1.1. OdriveStatus
| Value | Meaning |
|---|---|
ODRIVE_OK |
Success |
ODRIVE_ERR_HAL |
HAL transmit or receive error |
ODRIVE_ERR_TIMEOUT |
Operation timed out |
ODRIVE_ERR_STATE |
Axis reported an error state |
ODRIVE_ERR_PARAM |
Invalid parameter (NULL pointer) |
5.1.2. OdriveAxis
Context for a single ODrive axis. Initialise with OdriveAxisInit;
do not modify fields directly.
typedef struct
{
uint8_t axisId; /* CANSimple axis node id */
HalCanHandle *canHandle; /* HAL handle (not owned) */
AxisState lastState; /* Last state from heartbeat */
uint32_t lastError; /* Last error from heartbeat */
uint8_t trajDone; /* Non-zero: trajectory done */
float posEstimate; /* Position estimate (turns) */
float velEstimate; /* Velocity estimate (turns/s) */
float iqSetpoint; /* Iq setpoint (A) */
float iqMeasured; /* Iq measured (A) */
float fetTemp; /* FET temperature (°C) */
float motorTemp; /* Motor temperature (°C) */
float busVoltage; /* DC bus voltage (V) */
float busCurrent; /* DC bus current (A) */
} OdriveAxis;
Feedback fields (posEstimate through busCurrent) are updated
by OdriveProcessFrame as the corresponding cyclic messages are
received.
5.2. Functions
5.2.1. OdriveAxisInit
OdriveStatus OdriveAxisInit(OdriveAxis *pAxis,
uint8_t pAxisId,
HalCanHandle *pCanHandle);
Initialise an OdriveAxis context. pAxis must be pre-allocated
(stack or static). The HAL handle is not owned and must remain valid
for the lifetime of pAxis. All feedback fields are zeroed.
5.2.2. OdriveSetAxisState
OdriveStatus OdriveSetAxisState(OdriveAxis *pAxis,
AxisState pState,
uint32_t pTimeoutMs);
Request a state transition and wait for acknowledgement via
heartbeat polling. Returns ODRIVE_ERR_TIMEOUT if the axis does not
report the requested state within pTimeoutMs milliseconds.
5.2.3. OdriveWaitHeartbeat
OdriveStatus OdriveWaitHeartbeat(OdriveAxis *pAxis,
uint32_t pTimeoutMs);
Block until one heartbeat frame is received, updating
pAxis->lastState and pAxis->lastError on success. Returns
ODRIVE_ERR_TIMEOUT if no heartbeat arrives within pTimeoutMs
milliseconds — for example because the CAN bus was disconnected.
Used by svah --hold to detect bus disruption after the initial
state transition; see TestPlan.org, PI-05.
5.2.4. OdriveSetInputPos
OdriveStatus OdriveSetInputPos(OdriveAxis *pAxis,
float pInputPos,
int16_t pVelFeedForward,
int16_t pTorqueFeedForward);
Send a position setpoint. The axis must be in
AXIS_STATE_CLOSED_LOOP_CONTROL.
5.2.5. OdriveSetInputVel
OdriveStatus OdriveSetInputVel(OdriveAxis *pAxis,
float pInputVel,
float pTorqueFeedForward);
Send a velocity setpoint.
5.2.6. OdriveSetInputTorque
OdriveStatus OdriveSetInputTorque(OdriveAxis *pAxis,
float pInputTorque);
Send a torque setpoint in Nm.
5.2.7. OdriveSetControllerMode
OdriveStatus OdriveSetControllerMode(OdriveAxis *pAxis,
ControlMode pControlMode,
InputMode pInputMode);
Set the control mode and input mode. Typically called before
entering AXIS_STATE_CLOSED_LOOP_CONTROL.
5.2.8. OdriveSetLimits
OdriveStatus OdriveSetLimits(OdriveAxis *pAxis,
float pVelocityLimit,
float pCurrentLimit);
Set velocity limit (turns/s) and current limit (A).
5.2.9. OdriveClearErrors
OdriveStatus OdriveClearErrors(OdriveAxis *pAxis);
Send the Clear Errors command (cmd 0x018).
5.2.10. OdriveReboot
OdriveStatus OdriveReboot(OdriveAxis *pAxis);
Send the Reboot command. The axis context becomes invalid after this call; allow time for the ODrive to restart before re-initialising.
5.2.11. OdriveRequestEncoderEstimates
OdriveStatus OdriveRequestEncoderEstimates(OdriveAxis *pAxis);
Send an RTR frame requesting encoder estimates. The response
updates pAxis->posEstimate and pAxis->velEstimate when
processed by OdriveProcessFrame.
5.2.12. OdriveRequestIq
OdriveStatus OdriveRequestIq(OdriveAxis *pAxis);
Request Iq feedback. Response updates iqSetpoint and iqMeasured.
5.2.13. OdriveRequestTemperature
OdriveStatus OdriveRequestTemperature(OdriveAxis *pAxis);
Request temperature feedback. Response updates fetTemp and
motorTemp.
5.2.14. OdriveRequestBusVoltage
OdriveStatus OdriveRequestBusVoltage(OdriveAxis *pAxis);
Request bus voltage/current feedback. Response updates busVoltage
and busCurrent.
5.2.15. OdriveFeedWatchdog
OdriveStatus OdriveFeedWatchdog(OdriveAxis *pAxis);
Send a lightweight RTR frame to reset the ODrive watchdog timer.
Call at a rate faster than axis.config.watchdog_timeout
(configured on the ODrive). Sends a Get_Encoder_Estimates RTR,
which also produces a useful response.
5.2.16. OdriveProcessHeartbeat
int OdriveProcessHeartbeat(OdriveAxis *pAxis,
const HalCanFrame *pFrame);
Update lastState, lastError, and trajDone from a Heartbeat
frame. Returns ODRIVE_OK if processed, 1 if ignored, -1 on error.
5.2.17. OdriveProcessFrame
int OdriveProcessFrame(OdriveAxis *pAxis,
const HalCanFrame *pFrame);
Dispatch a received frame to the appropriate decoder. Handles
Heartbeat, Get_Encoder_Estimates, Get_Encoder_Count, Get_Iq,
Get_Temperature, and Get_Bus_Voltage_Current. Updates the
corresponding OdriveAxis fields. Returns ODRIVE_OK if
processed, 1 if ignored (different axis or unknown cmd_id),
-1 on decode error.
Preferred over OdriveProcessHeartbeat when multiple message
types are expected.
5.2.18. SvahLogSyslogCallback
Ready-made callback for the Linux system logger.
void SvahLogSyslogCallback(SvahLogLevel pLevel, const char *pMessage);
Routes messages to syslog(3). Level mapping:
SvahLogLevel |
syslog priority |
|---|---|
SVAH_LOG_DEBUG |
LOG_DEBUG |
SVAH_LOG_INFO |
LOG_INFO |
SVAH_LOG_WARN |
LOG_WARNING |
SVAH_LOG_ERROR |
LOG_ERR |
The caller must call openlog(3) before registering this callback
if a custom ident or facility is required.
5.2.19. SvahLogStderrCallback
Ready-made callback for standard error output.
void SvahLogStderrCallback(SvahLogLevel pLevel, const char *pMessage);
Writes [svah LEVEL] message\n to stderr.
5.3. Syslog usage example
This example shows the recommended logging setup for a Linux/SocketCAN
application using libsvah.
/*
* Recommended logging setup for Linux.
*
* Default: route library messages to the system logger.
* Override: --log-on-stderr sends them to stderr instead.
*
* The log level is controlled by --debug (DEBUG), normal (INFO),
* or --quiet (WARN).
*
* Include rationale:
* syslog.h : openlog(3), LOG_PID, LOG_CONS, LOG_USER
*/
#include <syslog.h>
#include "SvahLog.h"
#include "OdriveApi.h"
int main(int argc, char *argv[])
{
int lLogOnStderr = 0; /* set from --log-on-stderr option */
int lDebug = 0; /* set from --debug option */
int lQuiet = 0; /* set from --quiet option */
SvahLogLevel lLevel = lDebug ? SVAH_LOG_DEBUG :
lQuiet ? SVAH_LOG_WARN :
SVAH_LOG_INFO;
if(lLogOnStderr)
{
/* Redirect to stderr — useful during development and debugging */
SvahLogSetCallback(SvahLogStderrCallback, lLevel);
}
else
{
/* Default: log to syslog.
* Entries appear in /var/log/syslog (or journalctl on systemd).
* LOG_PID: include the process ID in each entry.
* LOG_CONS: fall back to the console if syslog is unavailable.
* LOG_USER: general user-level messages. */
openlog("svah", LOG_PID | LOG_CONS, LOG_USER);
SvahLogSetCallback(SvahLogSyslogCallback, lLevel);
}
/* All subsequent library calls route their diagnostics through
* the registered callback. The application never calls
* SvahLogWrite directly. */
HalCanHandle *lHandle = NULL;
HalCanInit(&lHandle, "can0", 250000);
/* ... rest of application ... */
if(!lLogOnStderr)
{
closelog();
}
return 0;
}
Observing syslog output:
# On systemd systems: journalctl -f -t svah # On traditional syslog: tail -f /var/log/syslog | grep svah # During development — bypass syslog entirely: svah --log-on-stderr --debug --interface vcan0 --no-motion
Bare-metal usage:
On bare-metal targets (ESP32, SAMD) where neither syslog nor
stderr exists, the application registers its own callback:
static void UartLog(SvahLogLevel pLevel, const char *pMsg)
{
uart_puts("[");
uart_puts(SvahLogLevelName(pLevel));
uart_puts("] ");
uart_puts(pMsg);
uart_puts("\r\n");
}
/* In application init — before any libsvah call: */
SvahLogSetCallback(UartLog, SVAH_LOG_WARN);