Skip to content

Commit f0f25cd

Browse files
authored
Merge pull request #563 from eternal-echo/features/isotp-fix-interface
feat(isotp): Add thread safety, callbacks, and extended ID support (IEC-370)
2 parents ba10116 + 49e973f commit f0f25cd

File tree

4 files changed

+612
-129
lines changed

4 files changed

+612
-129
lines changed

esp_isotp/Kconfig

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,4 @@ menu "ISO-TP Protocol"
5151
Value used for padding when ISO_TP_FRAME_PADDING is enabled.
5252
Common values: 0x00, 0xAA, 0xCC, 0xFF.
5353

54-
config ISO_TP_USER_SEND_CAN_ARG
55-
bool "Enable User Send CAN Argument"
56-
default y
57-
help
58-
Determines if an additional argument is present in the
59-
definition of isotp_user_send_can function.
60-
6154
endmenu

esp_isotp/README.md

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,42 +2,50 @@
22

33
[![Component Registry](https://components.espressif.com/components/espressif/esp_isotp/badge.svg)](https://components.espressif.com/components/espressif/esp_isotp)
44

5-
ISO 15765-2 (ISO-TP) transport protocol implementation for ESP-IDF, enabling reliable transmission of large data payloads (up to 4095 bytes) over TWAI networks with automatic segmentation and reassembly.
5+
ISO 15765-2 (ISO-TP) for ESP-IDF. Sends/receives large payloads (4095 B) over TWAI with segmentation and reassembly.
66

77
## Key Features
88

9-
- **Automatic segmentation** for messages >7 bytes with flow control
10-
- **Non-blocking API** with ISR-based frame processing
11-
- **Multi-instance support** for concurrent communication channels
12-
- **Automotive compliance** (UDS, OBD-II compatible)
13-
- **Robust error handling** with timeout and sequence validation
9+
- Segmentation + flow control for >7 B
10+
- Non-blocking API, ISR-backed
11+
- Multiple links in parallel
12+
- UDS/OBD-II friendly
13+
- Timeouts + sequence checks
14+
- 11-bit and 29-bit IDs
1415

1516
> [!NOTE]
1617
> TWAI-FD (Flexible Data-rate) is not supported in this version.
1718
1819
## Installation
1920

21+
To add this component to your project, run the following command from your project's root directory:
22+
2023
```bash
2124
idf.py add-dependency espressif/esp_isotp
2225
```
2326

2427
## Configuration
2528

26-
Configure ISO-TP protocol parameters:
29+
You can configure protocol parameters like timing through the project configuration menu:
2730

2831
```bash
2932
idf.py menuconfig
30-
# Navigate to: Component config → ISO-TP Protocol Configuration
3133
```
3234

33-
## Quick Start
35+
Navigate to `Component config``ISO-TP Protocol Configuration`.
36+
37+
## Quick Start Guide
38+
39+
Here's a simple example that initializes the TWAI driver and an ISO-TP link, then echoes back any received data.
40+
41+
### Example Code
3442

3543
```c
3644
#include "esp_isotp.h"
3745
#include "esp_twai_onchip.h"
3846

3947
void app_main(void) {
40-
// 1. Initialize TWAI
48+
// 1) Init TWAI
4149
twai_onchip_node_config_t twai_cfg = {
4250
.io_cfg = {.tx = GPIO_NUM_5, .rx = GPIO_NUM_4},
4351
.bit_timing = {.bitrate = 500000},
@@ -46,27 +54,61 @@ void app_main(void) {
4654
twai_node_handle_t twai_node;
4755
ESP_ERROR_CHECK(twai_new_node_onchip(&twai_cfg, &twai_node));
4856

49-
// 2. Create ISO-TP transport
57+
// 2) Create ISO-TP (11-bit IDs, auto-detected)
5058
esp_isotp_config_t config = {
51-
.tx_id = 0x7E0, .rx_id = 0x7E8,
52-
.tx_buffer_size = 4096, .rx_buffer_size = 4096,
59+
.tx_id = 0x7E0, // request ID (11-bit, auto-detected)
60+
.rx_id = 0x7E8, // response ID (11-bit, auto-detected)
61+
.tx_buffer_size = 4096,
62+
.rx_buffer_size = 4096,
5363
};
5464
esp_isotp_handle_t isotp_handle;
5565
ESP_ERROR_CHECK(esp_isotp_new_transport(twai_node, &config, &isotp_handle));
5666

57-
// 3. Communication loop
67+
// 3) Loop
5868
uint8_t buffer[4096];
5969
uint32_t received_size;
6070

6171
while (1) {
62-
esp_isotp_poll(isotp_handle); // CRITICAL: Call every 1-10ms
72+
// This is the engine of the component. Call it frequently!
73+
esp_isotp_poll(isotp_handle);
6374

75+
// Check if a full message has been received
6476
if (esp_isotp_receive(isotp_handle, buffer, sizeof(buffer), &received_size) == ESP_OK) {
6577
printf("Received %lu bytes\n", received_size);
66-
esp_isotp_send(isotp_handle, buffer, received_size); // Echo back
78+
// Echo the message back
79+
esp_isotp_send(isotp_handle, buffer, received_size);
6780
}
6881

69-
vTaskDelay(pdMS_TO_TICKS(5));
82+
vTaskDelay(pdMS_TO_TICKS(5)); // A small delay is good practice
7083
}
7184
}
7285
```
86+
87+
## Extended (29-bit) IDs
88+
89+
- ID format is auto-detected: IDs > 0x7FF use 29-bit extended format, others use 11-bit standard format.
90+
- `tx_id` and `rx_id` must differ and match peer's expected format.
91+
92+
```c
93+
esp_isotp_config_t cfg = {
94+
.tx_id = 0x18DAF110, // 29-bit ID (auto-detected as extended)
95+
.rx_id = 0x18DA10F1, // 29-bit ID (auto-detected as extended)
96+
.tx_buffer_size = 4096,
97+
.rx_buffer_size = 4096,
98+
};
99+
```
100+
101+
## Errors
102+
103+
- Common: ESP_OK, ESP_ERR_INVALID_ARG, ESP_ERR_INVALID_SIZE, ESP_ERR_NO_MEM, ESP_ERR_TIMEOUT
104+
- Send: ESP_ERR_NOT_FINISHED when previous TX in progress
105+
- Receive: ESP_ERR_NOT_FOUND when no complete message; ESP_ERR_INVALID_RESPONSE on bad sequence
106+
- Full list: see `esp_isotp.h`
107+
108+
## Checklist
109+
110+
- IDs valid and different; 11-bit vs 29-bit matches `use_extended_id`
111+
- Buffers > 0; size gates max single-message length (≤4095 B)
112+
- Call `esp_isotp_poll()` every 1–10 ms
113+
- TWAI node created and enabled before use
114+

esp_isotp/inc/esp_isotp.h

Lines changed: 107 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,6 @@
1818
* **Large packets (>7 bytes)**: Split into multiple frames - first frame sent immediately,
1919
* remaining frames sent during esp_isotp_poll() calls.
2020
*
21-
* ## Basic Usage
22-
* ```c
23-
* esp_isotp_handle_t handle;
24-
* esp_isotp_new_transport(twai_node, &config, &handle);
25-
*
26-
* while (1) {
27-
* esp_isotp_poll(handle); // MUST call regularly!
28-
*
29-
* // Send data (non-blocking)
30-
* esp_isotp_send(handle, data, size);
31-
*
32-
* // Check for received data (non-blocking)
33-
* uint16_t received_size;
34-
* if (esp_isotp_receive(handle, buffer, sizeof(buffer), &received_size) == ESP_OK) {
35-
* // Complete message received
36-
* }
37-
*
38-
* vTaskDelay(pdMS_TO_TICKS(5));
39-
* }
40-
* ```
4121
*/
4222

4323
#include "esp_err.h"
@@ -53,67 +33,155 @@ extern "C" {
5333
*/
5434
typedef struct esp_isotp_link_t *esp_isotp_handle_t;
5535

36+
/**
37+
* @brief ISO-TP receive callback function type
38+
*
39+
* Called when a complete message has been received successfully.
40+
*
41+
* @warning This callback executes in the same context as isotp_on_can_message(),
42+
* which is typically ISR context. Keep callback execution time minimal
43+
* and avoid blocking operations.
44+
* @note The data pointer is only valid during the callback execution.
45+
* Copy data immediately if needed beyond the callback scope.
46+
*
47+
* @param[in] handle ISO-TP handle that received the message
48+
* @param[in] data Pointer to received data (valid only during callback)
49+
* @param[in] size Size of received data in bytes
50+
* @param[in] user_arg User argument provided during configuration
51+
*/
52+
typedef void (*esp_isotp_rx_callback_t)(esp_isotp_handle_t handle, const uint8_t *data, uint32_t size, void *user_arg);
53+
54+
/**
55+
* @brief ISO-TP transmit callback function type
56+
*
57+
* Called when a complete message has been transmitted successfully.
58+
*
59+
* @note Execution context depends on message type:
60+
* - Single-frame: Called immediately from isotp_send() in caller's context
61+
* - Multi-frame: Called from isotp_poll() in task context
62+
* @note Keep callback execution time minimal to avoid affecting system performance.
63+
*
64+
* @param[in] handle ISO-TP handle that transmitted the message
65+
* @param[in] tx_size Size of transmitted data in bytes
66+
* @param[in] user_arg User argument provided during configuration
67+
*/
68+
typedef void (*esp_isotp_tx_callback_t)(esp_isotp_handle_t handle, uint32_t tx_size, void *user_arg);
69+
5670
/**
5771
* @brief Configuration structure for creating a new ISO-TP link
5872
*/
5973
typedef struct {
60-
uint32_t tx_id; /*!< TWAI ID for transmitting ISO-TP frames */
61-
uint32_t rx_id; /*!< TWAI ID for receiving ISO-TP frames */
74+
uint32_t tx_id; /*!< TWAI ID for transmitting ISO-TP frames (11-bit or 29-bit, auto-detected from value) */
75+
uint32_t rx_id; /*!< TWAI ID for receiving ISO-TP frames (11-bit or 29-bit, auto-detected from value) */
6276
uint32_t tx_buffer_size; /*!< Size of the transmit buffer (max message size to send) */
6377
uint32_t rx_buffer_size; /*!< Size of the receive buffer (max message size to receive) */
78+
uint32_t tx_frame_pool_size; /*!< Size of TX frame pool */
79+
80+
esp_isotp_rx_callback_t rx_callback; /*!< Receive completion callback (NULL for polling mode) */
81+
esp_isotp_tx_callback_t tx_callback; /*!< Transmit completion callback (NULL to disable) */
82+
void *callback_arg; /*!< User argument passed to callbacks */
6483
} esp_isotp_config_t;
6584

6685
/**
67-
* @brief Create a new ISO-TP link
86+
* @brief Create a new ISO-TP transport bound to a TWAI node.
87+
*
88+
* Allocates internal buffers, creates TX frame pool, registers TWAI callbacks
89+
* and enables the provided TWAI node.
6890
*
69-
* @param twai_node TWAI node handle
70-
* @param config Pointer to the configuration structure
71-
* @param[out] out_handle Pointer to store the created ISO-TP handle
91+
* @param twai_node TWAI node handle to bind.
92+
* @param config Transport configuration.
93+
* @param[out] out_handle Returned ISO-TP transport handle.
7294
* @return esp_err_t
73-
* - ESP_OK: Success
74-
* - ESP_ERR_INVALID_ARG: Invalid argument
75-
* - ESP_ERR_NO_MEM: Out of memory
95+
* - ESP_OK on success
96+
* - ESP_ERR_INVALID_ARG for invalid parameters
97+
* - ESP_ERR_INVALID_SIZE for invalid buffer sizes
98+
* - ESP_ERR_NO_MEM when allocation fails
99+
* - Other error codes from TWAI functions
76100
*/
77101
esp_err_t esp_isotp_new_transport(twai_node_handle_t twai_node, const esp_isotp_config_t *config, esp_isotp_handle_t *out_handle);
78102

79103
/**
80-
* @brief Send data over an ISO-TP link (non-blocking)
104+
* @brief Send data over an ISO-TP link (non-blocking, ISR-safe)
81105
*
82106
* Immediately sends first/single frame and returns. For multi-frame messages,
83107
* remaining frames are sent during subsequent esp_isotp_poll() calls.
84108
*
109+
* @note This function is ISR-safe and can be called from interrupt context.
110+
* @note TX completion callback timing:
111+
* - Single-frame: Called immediately from isotp_send()
112+
* - Multi-frame: Called from isotp_poll() when last frame is sent
113+
*
85114
* @param handle ISO-TP handle
86115
* @param data Data to send
87116
* @param size Data length in bytes
88117
* @return
89118
* - ESP_OK: Send initiated successfully
90119
* - ESP_ERR_NOT_FINISHED: Previous send still in progress
91-
* - ESP_ERR_NO_MEM: Data too large for buffer
120+
* - ESP_ERR_NO_MEM: Data too large for buffer or no space available
121+
* - ESP_ERR_INVALID_SIZE: Invalid data size
122+
* - ESP_ERR_TIMEOUT: Send operation timed out
92123
* - ESP_ERR_INVALID_ARG: Invalid parameters
124+
* - ESP_FAIL: Other send errors
93125
*/
94126
esp_err_t esp_isotp_send(esp_isotp_handle_t handle, const uint8_t *data, uint32_t size);
95127

96128
/**
97-
* @brief Extract a complete received message (non-blocking)
129+
* @brief Send data over an ISO-TP link with specified TWAI ID (non-blocking, ISR-safe)
130+
*
131+
* Similar to esp_isotp_send(), but allows specifying a different TWAI ID for transmission.
132+
* This function is primarily used for functional addressing where multiple nodes
133+
* may respond to the same request.
134+
*
135+
* @note This function is ISR-safe and can be called from interrupt context.
136+
* @note TX completion callback timing:
137+
* - Single-frame: Called immediately from isotp_send_with_id()
138+
* - Multi-frame: Called from isotp_poll() when last frame is sent
139+
*
140+
* @param handle ISO-TP handle
141+
* @param id TWAI identifier to use for transmission (overrides configured tx_id)
142+
* @param data Data to send
143+
* @param size Data length in bytes
144+
* @return
145+
* - ESP_OK: Send initiated successfully
146+
* - ESP_ERR_NOT_FINISHED: Previous send still in progress
147+
* - ESP_ERR_NO_MEM: Data too large for buffer or no space available
148+
* - ESP_ERR_INVALID_SIZE: Invalid data size
149+
* - ESP_ERR_TIMEOUT: Send operation timed out
150+
* - ESP_ERR_INVALID_ARG: Invalid parameters or ID exceeds maximum value
151+
* - ESP_FAIL: Other send errors
152+
*/
153+
esp_err_t esp_isotp_send_with_id(esp_isotp_handle_t handle, uint32_t id, const uint8_t *data, uint32_t size);
154+
155+
/**
156+
* @brief Extract a complete received message (non-blocking, task context only)
98157
*
99158
* This function only extracts data that has already been assembled by esp_isotp_poll().
100159
* It does NOT process incoming TWAI frames - that happens in esp_isotp_poll().
101160
*
102161
* Process: TWAI frames → esp_isotp_poll() assembles → esp_isotp_receive() extracts
103162
*
163+
* @warning This function is NOT ISR-safe and must only be called from task context.
164+
* @note When using callback mode (rx_callback != NULL), received messages are
165+
* automatically delivered via callback. This function should only be used
166+
* in polling mode (rx_callback == NULL).
167+
*
104168
* @param handle ISO-TP handle
105169
* @param data Buffer to store received data
106170
* @param size Buffer size in bytes
107171
* @param[out] received_size Actual received data length
108172
* @return
109173
* - ESP_OK: Complete message extracted and internal buffer cleared
110174
* - ESP_ERR_NOT_FOUND: No complete message ready for extraction
175+
* - ESP_ERR_INVALID_SIZE: Receive buffer overflow or invalid size
176+
* - ESP_ERR_INVALID_RESPONSE: Invalid sequence number or protocol error
177+
* - ESP_ERR_TIMEOUT: Receive operation timed out
111178
* - ESP_ERR_INVALID_ARG: Invalid parameters
179+
* - ESP_FAIL: Other receive errors
112180
*/
113181
esp_err_t esp_isotp_receive(esp_isotp_handle_t handle, uint8_t *data, uint32_t size, uint32_t *received_size);
114182

115183
/**
116-
* @brief Poll the ISO-TP link to process messages (CRITICAL - call regularly!)
184+
* @brief Poll the ISO-TP link to process messages (CRITICAL - call regularly, task context only)
117185
*
118186
* This function drives the ISO-TP state machine. Call every 1-10ms for proper operation.
119187
*
@@ -122,9 +190,13 @@ esp_err_t esp_isotp_receive(esp_isotp_handle_t handle, uint8_t *data, uint32_t s
122190
* - Processes incoming TWAI frames and assembles complete messages
123191
* - Handles flow control and timeouts
124192
* - Updates internal state machine
193+
* - Triggers TX completion callbacks for multi-frame messages
125194
*
126195
* Without regular polling: multi-frame sends will stall and receives won't complete.
127196
*
197+
* @warning This function is NOT ISR-safe and must only be called from task context.
198+
* @note TX completion callbacks for multi-frame messages are triggered from this function.
199+
*
128200
* @param handle ISO-TP handle
129201
* @return
130202
* - ESP_OK: Processing successful
@@ -137,8 +209,9 @@ esp_err_t esp_isotp_poll(esp_isotp_handle_t handle);
137209
*
138210
* @param handle The handle of the ISO-TP link to delete
139211
* @return
140-
* - ESP_OK: Success
212+
* - ESP_OK: Success (or TWAI disable warning logged)
141213
* - ESP_ERR_INVALID_ARG: Invalid argument
214+
* - Other ESP error codes: TWAI node disable failed
142215
*/
143216
esp_err_t esp_isotp_delete(esp_isotp_handle_t handle);
144217

0 commit comments

Comments
 (0)