DeterministicESPAsyncWebServer v6.28.0
Zero-allocation, bounded-execution async HTTP server for ESP32
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 DETWS_ENABLE_BUS_CAPTURE
12
13size_t can_to_socketcan(const CanFrame *f, uint8_t *out, size_t cap)
14{
15 if (!f || !out || cap < DET_SOCKETCAN_FRAME_LEN)
16 return 0;
17
18 uint32_t id = f->id & (f->extended ? DET_CAN_EXT_ID_MASK : DET_CAN_STD_ID_MASK);
19 if (f->extended)
20 id |= DET_CAN_EFF_FLAG;
21 if (f->rtr)
22 id |= DET_CAN_RTR_FLAG;
23
24 out[0] = (uint8_t)(id >> 24); // can_id, big-endian
25 out[1] = (uint8_t)(id >> 16);
26 out[2] = (uint8_t)(id >> 8);
27 out[3] = (uint8_t)id;
28
29 uint8_t dlc = f->dlc > DET_CAN_MAX_DLC ? DET_CAN_MAX_DLC : f->dlc;
30 out[4] = dlc; // length
31 out[5] = 0; // __pad
32 out[6] = 0; // __res0
33 out[7] = 0; // len8_dlc / __res1
34 for (int i = 0; i < DET_CAN_MAX_DLC; i++)
35 out[8 + i] = (i < dlc && !f->rtr) ? f->data[i] : 0;
36 return DET_SOCKETCAN_FRAME_LEN;
37}
38
39// --- ESP32 TWAI (CAN) binding ------------------------------------------------------------
40#ifdef ARDUINO
41
42#include "driver/twai.h"
43
44namespace
45{
46// All bus-capture bind state, owned by one instance (internal linkage): the frame sink and
47// the running flag, grouped so it is one named owner, unreachable from any other TU.
48struct BusCaptureCtx
49{
50 bus_capture_sink_fn sink = nullptr;
51 bool running = false;
52};
53BusCaptureCtx s_bus;
54
55bool timing_for(uint32_t bitrate, twai_timing_config_t *t)
56{
57 switch (bitrate)
58 {
59 case 1000000: {
60 twai_timing_config_t c = TWAI_TIMING_CONFIG_1MBITS();
61 *t = c;
62 return true;
63 }
64 case 500000: {
65 twai_timing_config_t c = TWAI_TIMING_CONFIG_500KBITS();
66 *t = c;
67 return true;
68 }
69 case 250000: {
70 twai_timing_config_t c = TWAI_TIMING_CONFIG_250KBITS();
71 *t = c;
72 return true;
73 }
74 case 125000: {
75 twai_timing_config_t c = TWAI_TIMING_CONFIG_125KBITS();
76 *t = c;
77 return true;
78 }
79 default:
80 return false;
81 }
82}
83} // namespace
84
85bool bus_capture_begin(int tx_pin, int rx_pin, uint32_t bitrate, bus_capture_sink_fn sink)
86{
87 if (!sink)
88 return false;
89 twai_timing_config_t timing;
90 if (!timing_for(bitrate, &timing))
91 return false;
92 twai_general_config_t gen =
93 TWAI_GENERAL_CONFIG_DEFAULT((gpio_num_t)tx_pin, (gpio_num_t)rx_pin, TWAI_MODE_LISTEN_ONLY);
94 twai_filter_config_t filt = TWAI_FILTER_CONFIG_ACCEPT_ALL();
95 if (twai_driver_install(&gen, &timing, &filt) != ESP_OK)
96 return false;
97 if (twai_start() != ESP_OK)
98 {
99 twai_driver_uninstall();
100 return false;
101 }
102 s_bus.sink = sink;
103 s_bus.running = true;
104 return true;
105}
106
107void bus_capture_poll(void)
108{
109 if (!s_bus.running || !s_bus.sink)
110 return;
111 twai_message_t m;
112 while (twai_receive(&m, 0) == ESP_OK) // 0 ticks = non-blocking drain
113 {
114 CanFrame f;
115 f.id = m.identifier;
116 f.extended = m.extd;
117 f.rtr = m.rtr;
118 f.dlc = m.data_length_code > DET_CAN_MAX_DLC ? DET_CAN_MAX_DLC : m.data_length_code;
119 for (int i = 0; i < DET_CAN_MAX_DLC; i++)
120 f.data[i] = (i < f.dlc) ? m.data[i] : 0;
121 s_bus.sink(&f);
122 }
123}
124
125void bus_capture_end(void)
126{
127 if (!s_bus.running)
128 return;
129 twai_stop();
130 twai_driver_uninstall();
131 s_bus.running = false;
132 s_bus.sink = nullptr;
133}
134
135#else // host build - no TWAI controller
136
137bool bus_capture_begin(int, int, uint32_t, bus_capture_sink_fn)
138{
139 return false;
140}
141void bus_capture_poll(void)
142{
143 // host build: no TWAI controller, nothing to drain
144}
145void bus_capture_end(void)
146{
147 // host build: no TWAI controller, nothing to stop
148}
149
150#endif // ARDUINO
151
152#endif // DETWS_ENABLE_BUS_CAPTURE
Wired field-bus listen-only capture (DETWS_ENABLE_BUS_CAPTURE) - passive CAN sniffing.
#define DET_CAN_STD_ID_MASK
11-bit standard identifier.
Definition can.h:31
#define DET_CAN_MAX_DLC
classic CAN carries at most 8 data octets.
Definition can.h:30
#define DET_CAN_EXT_ID_MASK
29-bit extended identifier.
Definition can.h:32
Definition can.h:44
uint8_t data[DET_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