ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
bus_capture.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file bus_capture.cpp
6 * @brief bus_capture implementation: the pure SocketCAN framer + the ESP32 TWAI listen-only bind.
7 */
8
10
11#if PC_ENABLE_BUS_CAPTURE
12
13#if defined(ARDUINO)
14#include "driver/twai.h"
15#endif
16size_t can_to_socketcan(const CanFrame *f, uint8_t *out, size_t cap)
17{
18 if (!f || !out || cap < PC_SOCKETCAN_FRAME_LEN)
19 {
20 return 0;
21 }
22
23 uint32_t id = f->id & (f->extended ? PC_CAN_EXT_ID_MASK : PC_CAN_STD_ID_MASK);
24 if (f->extended)
25 {
26 id |= PC_CAN_EFF_FLAG;
27 }
28 if (f->rtr)
29 {
30 id |= PC_CAN_RTR_FLAG;
31 }
32
33 out[0] = (uint8_t)(id >> 24); // can_id, big-endian
34 out[1] = (uint8_t)(id >> 16);
35 out[2] = (uint8_t)(id >> 8);
36 out[3] = (uint8_t)id;
37
38 uint8_t dlc = f->dlc > PC_CAN_MAX_DLC ? PC_CAN_MAX_DLC : f->dlc;
39 out[4] = dlc; // length
40 out[5] = 0; // __pad
41 out[6] = 0; // __res0
42 out[7] = 0; // len8_dlc / __res1
43 for (int i = 0; i < PC_CAN_MAX_DLC; i++)
44 {
45 out[8 + i] = (i < dlc && !f->rtr) ? f->data[i] : 0;
46 }
47 return PC_SOCKETCAN_FRAME_LEN;
48}
49
50// --- ESP32 TWAI (CAN) binding ------------------------------------------------------------
51#ifdef ARDUINO
52
53namespace
54{
55// All bus-capture bind state, owned by one instance (internal linkage): the frame sink and
56// the running flag, grouped so it is one named owner, unreachable from any other TU.
57struct BusCaptureCtx
58{
59 bus_capture_sink_fn sink = nullptr;
60 bool running = false;
61};
62BusCaptureCtx s_bus;
63
64bool timing_for(uint32_t bitrate, twai_timing_config_t *t)
65{
66 switch (bitrate)
67 {
68 case 1000000: {
69 twai_timing_config_t c = TWAI_TIMING_CONFIG_1MBITS();
70 *t = c;
71 return true;
72 }
73 case 500000: {
74 twai_timing_config_t c = TWAI_TIMING_CONFIG_500KBITS();
75 *t = c;
76 return true;
77 }
78 case 250000: {
79 twai_timing_config_t c = TWAI_TIMING_CONFIG_250KBITS();
80 *t = c;
81 return true;
82 }
83 case 125000: {
84 twai_timing_config_t c = TWAI_TIMING_CONFIG_125KBITS();
85 *t = c;
86 return true;
87 }
88 default:
89 return false;
90 }
91}
92} // namespace
93
94bool bus_capture_begin(int tx_pin, int rx_pin, uint32_t bitrate, bus_capture_sink_fn sink)
95{
96 if (!sink)
97 {
98 return false;
99 }
100 twai_timing_config_t timing;
101 if (!timing_for(bitrate, &timing))
102 {
103 return false;
104 }
105 twai_general_config_t gen =
106 TWAI_GENERAL_CONFIG_DEFAULT((gpio_num_t)tx_pin, (gpio_num_t)rx_pin, TWAI_MODE_LISTEN_ONLY);
107 twai_filter_config_t filt = TWAI_FILTER_CONFIG_ACCEPT_ALL();
108 if (twai_driver_install(&gen, &timing, &filt) != ESP_OK)
109 {
110 return false;
111 }
112 if (twai_start() != ESP_OK)
113 {
114 twai_driver_uninstall();
115 return false;
116 }
117 s_bus.sink = sink;
118 s_bus.running = true;
119 return true;
120}
121
122void bus_capture_poll(void)
123{
124 if (!s_bus.running || !s_bus.sink)
125 {
126 return;
127 }
128 twai_message_t m;
129 while (twai_receive(&m, 0) == ESP_OK) // 0 ticks = non-blocking drain
130 {
131 CanFrame f;
132 f.id = m.identifier;
133 f.extended = m.extd;
134 f.rtr = m.rtr;
135 f.dlc = m.data_length_code > PC_CAN_MAX_DLC ? PC_CAN_MAX_DLC : m.data_length_code;
136 for (int i = 0; i < PC_CAN_MAX_DLC; i++)
137 {
138 f.data[i] = (i < f.dlc) ? m.data[i] : 0;
139 }
140 s_bus.sink(&f);
141 }
142}
143
144void bus_capture_end(void)
145{
146 if (!s_bus.running)
147 {
148 return;
149 }
150 twai_stop();
151 twai_driver_uninstall();
152 s_bus.running = false;
153 s_bus.sink = nullptr;
154}
155
156#else // host build - no TWAI controller
157
158bool bus_capture_begin(int, int, uint32_t, bus_capture_sink_fn)
159{
160 return false;
161}
162void bus_capture_poll(void)
163{
164 // host build: no TWAI controller, nothing to drain
165}
166void bus_capture_end(void)
167{
168 // host build: no TWAI controller, nothing to stop
169}
170
171#endif // ARDUINO
172
173#endif // PC_ENABLE_BUS_CAPTURE
Wired field-bus listen-only capture (PC_ENABLE_BUS_CAPTURE) - passive CAN sniffing.
#define PC_CAN_MAX_DLC
classic CAN carries at most 8 data octets.
Definition can.h:30
#define PC_CAN_STD_ID_MASK
11-bit standard identifier.
Definition can.h:31
#define PC_CAN_EXT_ID_MASK
29-bit extended identifier.
Definition can.h:32
Definition can.h:44
uint8_t data[PC_CAN_MAX_DLC]
Definition can.h:49
bool rtr
Definition can.h:47
uint32_t id
Definition can.h:45
uint8_t dlc
Definition can.h:48
bool extended
Definition can.h:46