iso14229是个针对嵌入式系统的UDS(ISO14229-1:2013)服务器和客户端执行。
iso14229 is a UDS server and client implementation (ISO14229-1:2013) targeting embedded systems.
Stability: Experimental
// =====================================
// STEP 1: implement the hooks
// =====================================
/**
* @brief iso14229.h required function
* Implement this with the functions available on your host platform
*/
int userSendCAN(const uint32_t arbitration_id, const uint8_t *data, const uint8_t size) {
struct can_frame frame = {0};
frame.can_id = arbitration_id;
frame.can_dlc = size;
memmove(frame.data, data, size);
if (write(g_sockfd, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) {
perror("Write err");
exit(-1);
}
return 0;
}
/**
* @brief iso14229.h required function
* Implement this with the functions available on your host platform
*/
uint32_t userGetms() {
struct timeval te;
gettimeofday(&te, NULL); // get current time
long long milliseconds = te.tv_sec * 1000LL + te.tv_usec / 1000; // calculate milliseconds
return milliseconds;
}
/**
* @brief iso14229.h required function
* Implement this with the functions available on your host platform
*/
void userDebug(const char *fmt, ...) {}
/**
* @brief Reset the server
*/
void hardReset() { printf("server hardReset!\n"); }
// =====================================
// STEP 2: initialize the server
// =====================================
int main(int ac, char **av) {
// setup the linux CAN socket. This will vary depending on your platform. see example/server.c
setupSocket(ac, av);
uint8_t isotpPhysRecvBuf[ISOTP_BUFSIZE];
uint8_t isotpPhysSendBuf[ISOTP_BUFSIZE];
uint8_t isotpFuncRecvBuf[ISOTP_BUFSIZE];
uint8_t isotpFuncSendBuf[ISOTP_BUFSIZE];
uint8_t udsSendBuf[ISOTP_BUFSIZE];
uint8_t udsRecvBuf[ISOTP_BUFSIZE];
IsoTpLink isotpPhysLink;
IsoTpLink isotpFuncLink;
Iso14229Server uds;
const Iso14229ServerConfig cfg = {
.phys_recv_id = SRV_PHYS_RECV_ID,
.func_recv_id = SRV_FUNC_RECV_ID,
.send_id = SRV_SEND_ID,
.phys_link = &isotpPhysLink,
.func_link = &isotpFuncLink,
.receive_buffer = udsRecvBuf,
.receive_buf_size = sizeof(udsRecvBuf),
.send_buffer = udsSendBuf,
.send_buf_size = sizeof(udsSendBuf),
.userRDBIHandler = NULL,
.userWDBIHandler = NULL,
.userHardReset = hardReset,
.userGetms = userGetms,
.p2_ms = 50,
.p2_star_ms = 2000,
.s3_ms = 5000,
};
Iso14229Server srv;
/* initialize the ISO-TP links */
isotp_init_link(&isotpPhysLink, SRV_SEND_ID, isotpPhysSendBuf, sizeof(isotpPhysSendBuf),
isotpPhysRecvBuf, sizeof(isotpPhysRecvBuf), userGetms, userSendCAN,
userDebug);
isotp_init_link(&isotpFuncLink, SRV_SEND_ID, isotpFuncSendBuf, sizeof(isotpFuncSendBuf),
isotpFuncRecvBuf, sizeof(isotpFuncRecvBuf), userGetms, userSendCAN,
userDebug);
Iso14229ServerInit(&srv, &cfg);
iso14229ServerEnableService(&srv, kSID_ECU_RESET);
// =====================================
// STEP 3: poll the server
// =====================================
while (!g_should_exit) {
uint32_t arb_id;
uint8_t data[8];
uint8_t size;
Iso14229ServerPoll(&srv);
if (0 == CANRxPoll(&arb_id, data, &size)) {
iso14229ServerReceiveCAN(&srv, arb_id, data, size);
}
msleep(10);
}
}
See example for a simple server with socketCAN bindings
# 设置虚拟socketCAN接口
# setup a virtual socketCAN interface
sudo ip link add name can9 type vcan
sudo ip link set can9 up
# 构建例子服务器
# build the example server
make example/linux
# 在can9接口上运行例子服务器
# run the example server on can9
./example/linux can9
# (可选)在另外一个终端,看看虚拟CAN母线上的数据
# (Optional) In a another shell, monitor the virtual link
candump can9
# 在另外一个终端,安装python依赖性
# In another shell, install the required python packages
pip3 install -r example/requirements.txt
# 然后运行客户端
# then run the client
./example/client.py can9
Service | iso14229 Function |
---|---|
0x11 ECUReset | void userHardReset() |
0x22 ReadDataByIdentifier | enum Iso14229ResponseCode userRDBIHandler(uint16_t dataId, const uint8_t *data, uint16_t *len) |
0x27 SecurityAccess | enum Iso14229ResponseCode userSecurityAccessHandler() |
0x28 CommunicationControl | enum Iso14229ResponseCode userCommunicationControlHandler(uint8_t controlType, uint8_t communicationType) |
0x2E WriteDataByIdentifier | enum Iso14229ResponseCode userWDBIHandler(uint16_t dataId, const uint8_t *data, uint16_t len) |
0x31 RoutineControl | int Iso14229ServerRegisterRoutine(Iso14229Server* self, const Iso14229Routine *routine); |
0x34 RequestDownload, 0x36 TransferData, 0x37 RequestTransferExit | int iso14229ServerRegisterDownloadHandler(Iso14229Server* self, Iso14229DownloadHandlerConfig *handler); |
用户自定的服务器逻辑(比如ISO-14229规范指定的”Application Software"和"Boot Software")可以用中间件来实现。 User-defined server behavior such as the "Application Software" and "Boot Software" described in ISO-14229 can be implemented through middleware.
struct Iso14229UserMiddleware;
Currently undocumented. See test_iso14229.c
for usage examples
欢迎来贡献/contributions are welcome
isotp
which this project embeds
MIT
- initial release
- Add client
- Add server SID 0x27 SecurityAccess
- API changes
bazel test --compilation_mode=dbg //...
@startuml
title 客户端请求状态机
note as N1
enum {
kNoError=0,
kErrBadRequest,
kErrP2Timeout,
} ClientErr;
static inline bool isRequestComplete() {return state==Idle;}
while (Idle != client->state) {
receiveCAN(client);
Iso14229ClientPoll(client);
}
end note
state Idle
state Sending
state Sent
state SentAwaitResponse
state ProcessResponse
Idle: if (ISOTP_RET_OK == isotp_receive(...)) // Error
ProcessResponse: isotp_receive()
ProcessResponse: _ClientValidateResponse(...)
ProcessResponse: _ClientHandleResponse(...)
Sending --> Sent: 传输层完成传输
Sent --> Idle : suppressPositiveResponse
Sending --> SentAwaitResponse: !suppressPositiveResponse
SentAwaitResponse -> Idle: 响应收到了 ||\np2 超时
SentAwaitResponse --> ProcessResponse : ISOTP_RECEIVE_STATUS_FULL == link->receive_status
ProcessResponse --> Idle
[*] -> Idle
Idle -> Sending : _SendRequest()
@enduml
@startuml
title Request Lifecycle
alt normal
alt positive response
client --> client: Sending
client -> server : *Any* Service
client --> client: SentAwaitResponse: set p2
alt 0x78 requestCorrectlyReceived-ResponsePending
server -> client : 0x3F 0x78
client -->server : txLink idle
client --> client: SentAwaitResponse: set p2star
end
server -> client : Positive Service Response
client --> client: Idle
else negative response
server -> client !! : Negative Service Response
client --> client: Idle: RequestErrorNegativeResponse
else SID mismatch
server -> client !! : Mismatched Service Response
client --> client: Idle: RequestErrorResponseSIDMismatch
end
else unexpected response
server -> client !! : Unexpected Response
client --> client: Idle: RequestErrorUnsolicitedResponse
end
@enduml
@startuml
' !pragma useVerticalIf on
title 客户端请求流程
start
:clientSendRequest();
if (验证参数) then (对)
:ok;
else (不对)
:foo;
detach
endif
:clearRequestContext();
if (等待UDS访问) then (访问接收了,进入UDS会话)
else (时间超过<b>20ms)
@enduml
@startuml
client -> server : *Any* Service
server -> userServiceHandler: handler(args)
note right: Doing this will take a long time\nso I return 0x78
userServiceHandler -> server: 0x78
server -> client : 0x3F 0x78
client -->server : txLink idle
server -> userServiceHandler: handler(args)
note right: actually call the long-running service
... p2* > t > p2 ...
userServiceHandler -> server : Service Response
server -> client : Service Response
@enduml