M5StickC PlusでミニUDSサーバを実行（その４ UDS編）

こちらの続きです

https://qiita.com/O_positive/items/4d8d0be213687fc1e60d

前回までで作成したCAN通信環境上でUDSサーバを実行します。
本当はAUTOSAR的な[ssas-public](https://github.com/autoas/ssas-public)というソフトウェアを使用したかったのですが、難しくて今回は断念。
ミニUDSサーバを作成します。

## ミニUDSサーバ
- ECU Resetリクエスト（SID: $11）を受信したら、M5StickC Plusをリセットする
- Read Data By Identifierリクエスト（SID: $22）を受信したら、以下のDIDに対応した値（M5StickC Plusのセンサーデータ）を返信する

|DID|内容|
|:-:|:-:|
|0xF100|加速度センサー（x軸）|
|0xF101|加速度センサー（y軸）|
|0xF102|加速度センサー（z軸）|
|0xF103|温度センサー|

## 実装
main.cppを[こちら](#作成したファイル)の「作成したファイル」のように記述します。

**概要**
```plantuml
@startuml
participant isotp
participant loop
participant processUdsRequest

loop
    loop -> isotp:isotp.receive
    note right:受信Check
    alt 受信データあり
        isotp -> loop:受信データ

        loop -> processUdsRequest:UDSリクエスト
        rnote over processUdsRequest:UDSサービス処理
        isotp <- processUdsRequest:isotp.send
        note right:UDSレスポンス
    end
end
@enduml

```
## 動作確認
platformIOのプロジェクトをビルドしてM5StickC Plusにアップロードすると、UDSリクエスト受信を待ちます。

### ECU Reset
ラズパイからリセット要求（```$11 01```）を送ると、M5StickC Plusでリセット実行されます。
```plantuml
@startuml
participant M5StickC_Plus
== リクエスト ==
ラズパイ -> M5StickC_Plus: SF
note right:$11 01
== レスポンス ==
M5StickC_Plus -> ラズパイ: SF
note left:$51 01
rnote over M5StickC_Plus:リセット実行
@enduml
```
```console
# candump can0
  can0  456   [8]  02 11 01 00 00 00 00 00
  can0  123   [8]  02 51 01 00 00 00 00 00
```

### Read Data By Identifier
ラズパイから４つのDIDの読み出し要求（```$22 F100 F101 F102 F103```）を送ると、以下のように応答します。

```plantuml
@startuml
participant M5StickC_Plus
== リクエスト ==
ラズパイ -> M5StickC_Plus: FF
M5StickC_Plus -> ラズパイ: FC
ラズパイ -> M5StickC_Plus: CF
note right:$22 F100 F101 F102 F103
== レスポンス ==
M5StickC_Plus -> ラズパイ: FF
ラズパイ -> M5StickC_Plus: FC
M5StickC_Plus -> ラズパイ: CF
M5StickC_Plus -> ラズパイ: CF
note left
    $62
    F100 FDA8
    F101 FFE7
    F102 0361
    F103 129E
end note
rnote left of M5StickC_Plus #pink
    換算すると：
    加速度(x) -0.600 g
    加速度(y) -0.025 g
    加速度(z) 0.865 g
    温度 47.66 ℃
end note
@enduml
```
```console
# candump can0
  can0  456   [8]  10 09 22 F1 00 F1 01 F1
  can0  123   [8]  30 00 00 00 00 00 00 00
  can0  456   [8]  21 02 F1 03 00 00 00 00
  can0  123   [8]  10 11 62 F1 00 FD A8 F1
  can0  456   [8]  30 08 20 00 00 00 00 00
  can0  123   [8]  21 01 FF E7 F1 02 03 6A
  can0  123   [8]  22 F1 03 12 9E 00 00 00
```

### 作成したファイル
<details><summary><font color="Brown">main.cpp</font></summary>

```c:main.cpp
#include "M5StickCPlus.h"
#include "ESP32CAN.h"
#include "CAN_config.h"
#include "iso-tp-ESP32CAN.h"
#include "esp_system.h"

// ========================
// GPIO 定義
// ========================
#define TX_GPIO GPIO_NUM_32  // TXピン
#define RX_GPIO GPIO_NUM_33  // RXピン

// ========================
// グローバル変数
// ========================
CAN_device_t CAN_cfg;  // CAN設定構造体
IsoTp isotp;
struct Message_t txMsg, rxMsg;
uint32_t tx_can_id = 0x123;
uint32_t rx_can_id = 0x456;

// ========================
// ミニUDSサーバ処理
// ========================
void processUdsRequest(uint8_t *data, uint8_t length) {
    uint8_t sid = data[0];  // SID を取得

    if (sid == 0x22) {
        Serial.println("0x22の処理");

        float accX, accY, accZ, temp;
        M5.IMU.getAccelData(&accX, &accY, &accZ);
        M5.IMU.getTempData(&temp);
        Serial.printf("X: %.3f, Y: %.3f, Z: %.3f, temp: %.3f\n", accX, accY, accZ, temp);

        uint8_t response[64];
        uint8_t response_length = 0;

        response[response_length++] = 0x62;

        for (int i = 1; i + 1 < length; i += 2) {
            uint16_t did = (data[i] << 8) | data[i + 1];

            response[response_length++] = data[i];
            response[response_length++] = data[i + 1];

            switch (did) {
                case 0xF100: {  // Accel X
                    int16_t val = accX * 1000;  // 例: milli-g 単位で送信
                    response[response_length++] = (val >> 8) & 0xFF;
                    response[response_length++] = val & 0xFF;
                    break;
                }
                case 0xF101: {  // Accel Y
                    int16_t val = accY * 1000;
                    response[response_length++] = (val >> 8) & 0xFF;
                    response[response_length++] = val & 0xFF;
                    break;
                }
                case 0xF102: {  // Accel Z
                    int16_t val = accZ * 1000;
                    response[response_length++] = (val >> 8) & 0xFF;
                    response[response_length++] = val & 0xFF;
                    break;
                }
                case 0xF103: {  // 温度（例：℃ × 100）
                    int16_t val = temp * 100;
                    response[response_length++] = (val >> 8) & 0xFF;
                    response[response_length++] = val & 0xFF;
                    break;
                }
                default:
                    response_length -= 2;  // 未対応DIDのID削除
                    Serial.printf("未対応DID: 0x%04X\n", did);
                    break;
            }
        }

        txMsg.len = response_length;
        memcpy(txMsg.Buffer, response, response_length);
        txMsg.tx_id = tx_can_id;
        txMsg.rx_id = rx_can_id;
        isotp.send(&txMsg);
    }
    else if (sid == 0x11) {
        Serial.println("0x11の処理");
        uint8_t response[] = {0x51, 0x01};
        txMsg.len = sizeof(response);
        memcpy(txMsg.Buffer, response, txMsg.len);
        txMsg.tx_id = tx_can_id;
        txMsg.rx_id = rx_can_id;
        isotp.send(&txMsg);
        esp_restart();
    } 
    else {
        Serial.println("未対応サービスのリクエスト");
    }
}

// ========================
// CAN 初期化関数
// ========================
void initCAN() {
    CAN_cfg.speed = CAN_SPEED_500KBPS;
    CAN_cfg.tx_pin_id = TX_GPIO;
    CAN_cfg.rx_pin_id = RX_GPIO;
    CAN_cfg.rx_queue = xQueueCreate(10, sizeof(CAN_frame_t));  // 受信キュー

    if (ESP32Can.CANInit() == ESP_OK) {
        Serial.println("CAN bus started successfully!");
    } else {
        Serial.println("CAN bus failed to start!");
        while (1);  // 停止
    }
}

// ========================
// CAN 送信関数（ESP32CAN 用）
// ========================
int can_tx_ESP32CAN(uint32_t id, uint8_t *data, uint8_t len) {
    CAN_frame_t txFrame = { 0 };
    txFrame.FIR.B.FF = CAN_frame_std;
    txFrame.MsgID = id;
    txFrame.FIR.B.DLC = (len > 8) ? 8 : len;
    memcpy(txFrame.data.u8, data, txFrame.FIR.B.DLC);

    Serial.print("送信データ: ");
    for (int i = 0; i < txFrame.FIR.B.DLC; i++) {
        Serial.printf("%02X ", txFrame.data.u8[i]);
    }

    if (ESP32Can.CANWriteFrame(&txFrame) == ESP_OK) {
        Serial.println("-> 送信成功");
        return 0;
    } else {
        Serial.println("-> 送信エラー");
        return -1;
    }
}

// ========================
// CAN 受信関数（ESP32CAN 用）
// ========================
int can_rx_ESP32CAN(uint32_t *id, uint8_t *data, uint8_t *len) {
    CAN_frame_t rxFrame = { 0 };

    if (xQueueReceive(CAN_cfg.rx_queue, &rxFrame, pdMS_TO_TICKS(100)) == pdTRUE) {
        *id = rxFrame.MsgID;
        *len = rxFrame.FIR.B.DLC;
        memcpy(data, rxFrame.data.u8, *len);
        return 0;
    }

    return -1;  // 受信失敗
}

// ========================
// Arduino 初期化関数
// ========================
void setup() {
    M5.begin();
    M5.IMU.Init();
    Serial.begin(115200);
    Serial.println("Start");

    initCAN();  // CAN初期化

    // ISO-TP の初期化（ESP32CAN 用の送受信関数を登録）
    isotp.set_can_tx_rx_ESP32CAN(can_tx_ESP32CAN, can_rx_ESP32CAN);
    txMsg.Buffer = (uint8_t *)calloc(MAX_MSGBUF, sizeof(uint8_t));
    rxMsg.Buffer = (uint8_t *)calloc(MAX_MSGBUF, sizeof(uint8_t));
}

// ========================
// メインループ
// ========================
void loop() {
    uint8_t len = 0;
    uint8_t data[8];
    rxMsg.tx_id = tx_can_id;
    rxMsg.rx_id = rx_can_id;
    if (isotp.receive(&rxMsg) == 0) {
        isotp.print_buffer(rxMsg.rx_id, rxMsg.Buffer, rxMsg.len);
        processUdsRequest(rxMsg.Buffer, rxMsg.len);
    }
}
```
</details>
<details><summary><font color="Brown">iso-tp-ESP32CAN.h</font></summary>

```c:iso-tp-ESP32CAN.h
#ifndef _ISOTP_H
#define _ISOTP_H

// #include <mcp_can.h>

// #define ISO_TP_DEBUG

typedef enum {
  ISOTP_IDLE = 0,
  ISOTP_SEND,
  ISOTP_SEND_FF,
  ISOTP_SEND_CF,
  ISOTP_WAIT_FIRST_FC,
  ISOTP_WAIT_FC,
  ISOTP_WAIT_DATA,
  ISOTP_FINISHED,
  ISOTP_ERROR
} isotp_states_t;

#define CAN_MAX_DLEN 8  //Not extended CAN

/* N_PCI type values in bits 7-4 of N_PCI bytes */
#define N_PCI_SF  0x00  /* single frame */
#define N_PCI_FF  0x10  /* first frame */
#define N_PCI_CF  0x20  /* consecutive frame */
#define N_PCI_FC  0x30  /* flow control */

#define FC_CONTENT_SZ 3 /* flow control content size in byte (FS/BS/STmin) */

/* Flow Status given in FC frame */
#define ISOTP_FC_CTS  0   /* clear to send */
#define ISOTP_FC_WT 1     /* wait */
#define ISOTP_FC_OVFLW  2 /* overflow */

/* Timeout values */
#define TIMEOUT_SESSION  500 /* Timeout between successfull send and receive */
#define TIMEOUT_FC       250 /* Timeout between FF and FC or Block CF and FC */
#define TIMEOUT_CF       250 /* Timeout between CFs                          */
#define MAX_FCWAIT_FRAME  10

#define MAX_MSGBUF 128    /* Received Message Buffer. Depends on uC ressources!
                             Should be enough for our needs */
struct Message_t
{
  uint16_t len=0;
  isotp_states_t tp_state=ISOTP_IDLE;
  uint16_t seq_id=1;
  uint8_t fc_status=ISOTP_FC_CTS;
  uint8_t blocksize=0;
  uint8_t min_sep_time=0;
  uint32_t tx_id=0;
  uint32_t rx_id=0;
  uint8_t *Buffer;
};

class IsoTp
{
  public:
    // IsoTp(MCP_CAN* bus, uint8_t mcp_int);
    IsoTp();
    void set_can_tx_rx_ESP32CAN(int (*tx_func)(uint32_t, uint8_t*, uint8_t),
                                int (*rx_func)(uint32_t*, uint8_t*, uint8_t*));
    uint8_t send(Message_t* msg);
    uint8_t receive(Message_t* msg);
    void    print_buffer(uint32_t id, uint8_t *buffer, uint16_t len);
  private:
    // MCP_CAN* _bus;
    // uint8_t  _mcp_int;
    int (*can_tx_func)(uint32_t, uint8_t*, uint8_t);
    int (*can_rx_func)(uint32_t*, uint8_t*, uint8_t*);
    uint32_t   rxId;
    uint8_t  rxLen;
    uint8_t  rxBuffer[8];
    uint16_t rest;
    uint8_t  fc_wait_frames=0;
    uint32_t   wait_fc=0;
    uint32_t   wait_cf=0;
    uint32_t   wait_session=0;
    // uint8_t  can_send(uint32_t id, uint8_t len, uint8_t *data);
    // uint8_t  can_receive(void);
    uint8_t  send_fc(struct Message_t* msg);
    uint8_t  send_sf(struct Message_t* msg);
    uint8_t  send_ff(struct Message_t* msg);
    uint8_t  send_cf(struct Message_t* msg);
    uint8_t  rcv_sf(struct Message_t* msg);
    uint8_t  rcv_ff(struct Message_t* msg);
    uint8_t  rcv_cf(struct Message_t* msg);
    uint8_t  rcv_fc(struct Message_t* msg);
    void     fc_delay(uint8_t sep_time);
};

#endif
```
</details>
<details><summary><font color="Brown">iso-tp-ESP32CAN.cpp</font></summary>

```c:iso-tp-ESP32CAN.cpp
#include "Arduino.h"
// #include "iso-tp.h"
// #include <mcp_can.h>
// #include <mcp_can_dfs.h>
// #include <SPI.h>
#include "iso-tp-ESP32CAN.h"

// IsoTp::IsoTp(MCP_CAN* bus, uint8_t mcp_int)
// {
//   _mcp_int = mcp_int;
//   _bus = bus;
// }

IsoTp::IsoTp() {
    can_tx_func = nullptr;
    can_rx_func = nullptr;
}
 
void IsoTp::set_can_tx_rx_ESP32CAN(int (*tx_func)(uint32_t, uint8_t*, uint8_t),
                                   int (*rx_func)(uint32_t*, uint8_t*, uint8_t*)) {
    can_tx_func = tx_func;
    can_rx_func = rx_func;
}

void IsoTp::print_buffer(uint32_t id, uint8_t *buffer, uint16_t len)
{
  uint16_t i=0;

  Serial.print(F("Buffer: "));
  Serial.print(id,HEX); Serial.print(F(" ["));
  Serial.print(len); Serial.print(F("] "));
  for(i=0;i<len;i++)
  {
    if(buffer[i] < 0x10) Serial.print(F("0"));
    Serial.print(buffer[i],HEX);
    Serial.print(F(" "));
  }
  Serial.println();
}

// uint8_t IsoTp::can_send(uint32_t id, uint8_t len, uint8_t *data)
// {
// #ifdef ISO_TP_DEBUG
//   Serial.println(F("Send CAN RAW Data:"));
//   print_buffer(id, data, len);
// #endif
//   return _bus->sendMsgBuf(id, 0, len, data);
// }

// uint8_t IsoTp::can_receive(void)
// {
//   bool msgReceived;

//   if (_mcp_int)
//     msgReceived = (!digitalRead(_mcp_int));                     // IRQ: if pin is low, read receive buffer
//   else
//     msgReceived = (_bus->checkReceive() == CAN_MSGAVAIL);       // No IRQ: poll receive buffer

//   if (msgReceived)
//   {
//      memset(rxBuffer,0,sizeof(rxBuffer));       // Cleanup Buffer
//      _bus->readMsgBuf(&rxId, &rxLen, rxBuffer); // Read data: buf = data byte(s)
// #ifdef ISO_TP_DEBUG
//      Serial.println(F("Received CAN RAW Data:"));
//      print_buffer(rxId, rxBuffer, rxLen);
// #endif
//     return true;
//   }
//   else return false;
// }

uint8_t IsoTp::send_fc(struct Message_t *msg)
{
  uint8_t TxBuf[8]={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  // FC message high nibble = 0x3 , low nibble = FC Status
  TxBuf[0]=(N_PCI_FC | msg->fc_status);
  TxBuf[1]=msg->blocksize;
  /* fix wrong separation time values according spec */
  if ((msg->min_sep_time > 0x7F) && ((msg->min_sep_time < 0xF1)
      || (msg->min_sep_time > 0xF9))) msg->min_sep_time = 0x7F;
  TxBuf[2]=msg->min_sep_time;
  // return can_send(msg->tx_id,8,TxBuf);
  return can_tx_func(msg->tx_id, TxBuf, 8);
}

uint8_t IsoTp::send_sf(struct Message_t *msg) //Send SF Message
{
  uint8_t TxBuf[8]={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  // SF message high nibble = 0x0 , low nibble = Length
  TxBuf[0]=(N_PCI_SF | msg->len);
  memcpy(TxBuf+1,msg->Buffer,msg->len);
//  return can_send(msg->tx_id,msg->len+1,TxBuf);// Add PCI length
  // return can_send(msg->tx_id,8,TxBuf);// Always send full frame
  return can_tx_func(msg->tx_id, TxBuf, 8);
}

uint8_t IsoTp::send_ff(struct Message_t *msg) // Send FF
{
  uint8_t TxBuf[8]={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  msg->seq_id=1;

  TxBuf[0]=(N_PCI_FF | ((msg->len&0x0F00) >> 8));
  TxBuf[1]=(msg->len&0x00FF);
  memcpy(TxBuf+2,msg->Buffer,6);             // Skip 2 Bytes PCI
  // return can_send(msg->tx_id,8,TxBuf);       // First Frame has full length
  return can_tx_func(msg->tx_id, TxBuf, 8);
}

uint8_t IsoTp::send_cf(struct Message_t *msg) // Send SF Message
{
  uint8_t TxBuf[8]={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
  uint16_t len=7;

  TxBuf[0]=(N_PCI_CF | (msg->seq_id & 0x0F));
  if(msg->len>7) len=7; else len=msg->len;
  memcpy(TxBuf+1,msg->Buffer,len);         // Skip 1 Byte PCI
  //return can_send(msg->tx_id,len+1,TxBuf); // Last frame is probably shorter
                                           // than 8 -> Signals last CF Frame
  // return can_send(msg->tx_id,8,TxBuf);     // Last frame is probably shorter
  return can_tx_func(msg->tx_id, TxBuf, 8);
                                           // than 8, pad with 00
}

void IsoTp::fc_delay(uint8_t sep_time)
{
  /*
  * 0x00 - 0x7F: 0 - 127ms
  * 0x80 - 0xF0: reserved
  * 0xF1 - 0xF9: 100us - 900us
  * 0xFA - 0xFF: reserved
  * default 0x7F, 127ms
  */
  if(sep_time <= 0x7F)
    delay(sep_time);
  else if ((sep_time >= 0xF1) && (sep_time <= 0xF9))
    delayMicroseconds((sep_time-0xF0)*100);
  else
    delay(0x7F);
}

uint8_t IsoTp::rcv_sf(struct Message_t* msg)
{
  /* get the SF_DL from the N_PCI byte */
  msg->len = rxBuffer[0] & 0x0F;
  /* copy the received data bytes */
  memcpy(msg->Buffer,rxBuffer+1,msg->len); // Skip PCI, SF uses len bytes
  msg->tp_state=ISOTP_FINISHED;

  return 0;
}

uint8_t IsoTp::rcv_ff(struct Message_t* msg)
{
  msg->seq_id=1;

  /* get the FF_DL */
  msg->len = (rxBuffer[0] & 0x0F) << 8;
  msg->len += rxBuffer[1];
  rest=msg->len;

  /* copy the first received data bytes */
  memcpy(msg->Buffer,rxBuffer+2,6); // Skip 2 bytes PCI, FF must have 6 bytes!
  rest-=6; // Restlength

  msg->tp_state = ISOTP_WAIT_DATA;

#ifdef ISO_TP_DEBUG
  Serial.print(F("First frame received with message length: "));
  Serial.println(rest);
  Serial.println(F("Send flow controll."));
  Serial.print(F("ISO-TP state: ")); Serial.println(msg->tp_state);
#endif

  /* send our first FC frame with Target Address*/
  struct Message_t fc;
  fc.tx_id=msg->tx_id;
  fc.fc_status=ISOTP_FC_CTS;
  fc.blocksize=0;
  fc.min_sep_time=0;
  return send_fc(&fc);
}

uint8_t IsoTp::rcv_cf(struct Message_t* msg)
{
  //Handle Timeout
  //If no Frame within 250ms change State to ISOTP_IDLE
  uint32_t delta=millis()-wait_cf;

  if((delta >= TIMEOUT_FC) && msg->seq_id>1)
  {
#ifdef ISO_TP_DEBUG
    Serial.println(F("CF frame timeout during receive wait_cf="));
    Serial.print(wait_cf); Serial.print(F(" delta="));
    Serial.println(delta);
#endif
    msg->tp_state = ISOTP_IDLE;
    return 1;
  }
  wait_cf=millis();

#ifdef ISO_TP_DEBUG
  Serial.print(F("ISO-TP state: ")); Serial.println(msg->tp_state);
  Serial.print(F("CF received with message rest length: "));
  Serial.println(rest);
#endif

  if (msg->tp_state != ISOTP_WAIT_DATA) return 0;

  if ((rxBuffer[0] & 0x0F) != (msg->seq_id & 0x0F))
  {
#ifdef ISO_TP_DEBUG
    Serial.print(F("Got sequence ID: ")); Serial.print(rxBuffer[0] & 0x0F);
    Serial.print(F(" Expected: ")); Serial.println(msg->seq_id & 0x0F);
#endif
    msg->tp_state = ISOTP_IDLE;
    msg->seq_id = 1;
    return 1;
  }

  if(rest<=7) // Last Frame
  {
    memcpy(msg->Buffer+6+7*(msg->seq_id-1),rxBuffer+1,rest);// 6 Bytes in FF +7
    msg->tp_state=ISOTP_FINISHED;                           // per CF skip PCI
#ifdef ISO_TP_DEBUG
    Serial.print(F("Last CF received with seq. ID: "));
    Serial.println(msg->seq_id);
#endif
  }
  else
  {
#ifdef ISO_TP_DEBUG
    Serial.print(F("CF received with seq. ID: "));
    Serial.println(msg->seq_id);
#endif
    memcpy(msg->Buffer+6+7*(msg->seq_id-1),rxBuffer+1,7); // 6 Bytes in FF +7
                                                          // per CF
    rest-=7; // Got another 7 Bytes of Data;
  }

  msg->seq_id++;

  return 0;
}

uint8_t IsoTp::rcv_fc(struct Message_t* msg)
{
  uint8_t retval=0;

  if (msg->tp_state != ISOTP_WAIT_FC && msg->tp_state != ISOTP_WAIT_FIRST_FC)
    return 0;

  /* get communication parameters only from the first FC frame */
  if (msg->tp_state == ISOTP_WAIT_FIRST_FC)
  {
    msg->blocksize = rxBuffer[1];
    msg->min_sep_time = rxBuffer[2];

    /* fix wrong separation time values according spec */
    if ((msg->min_sep_time > 0x7F) && ((msg->min_sep_time < 0xF1)
	|| (msg->min_sep_time > 0xF9))) msg->min_sep_time = 0x7F;
  }

#ifdef ISO_TP_DEBUG
  Serial.print(F("FC frame: FS "));
  Serial.print(rxBuffer[0]&0x0F);
  Serial.print(F(", Blocksize "));
  Serial.print(msg->blocksize);
  Serial.print(F(", Min. separation Time "));
  Serial.println(msg->min_sep_time);
#endif

  switch (rxBuffer[0] & 0x0F)
  {
    case ISOTP_FC_CTS:
                         msg->tp_state = ISOTP_SEND_CF;
                         break;
    case ISOTP_FC_WT:
                         fc_wait_frames++;
			 if(fc_wait_frames >= MAX_FCWAIT_FRAME)
                         {
#ifdef ISO_TP_DEBUG
                           Serial.println(F("FC wait frames exceeded."));
#endif
                           fc_wait_frames=0;
                           msg->tp_state = ISOTP_IDLE;
                           retval=1;
                         }
#ifdef ISO_TP_DEBUG
                         Serial.println(F("Start waiting for next FC"));
#endif
                         break;
    case ISOTP_FC_OVFLW:
#ifdef ISO_TP_DEBUG
                         Serial.println(F("Overflow in receiver side"));
#endif
    default:
                         msg->tp_state = ISOTP_IDLE;
                         retval=1;
  }
  return retval;
}

uint8_t IsoTp::send(Message_t* msg)
{
  uint8_t bs=false;
  uint32_t delta=0;
  uint8_t retval=0;

  msg->tp_state=ISOTP_SEND;

  while(msg->tp_state!=ISOTP_IDLE && msg->tp_state!=ISOTP_ERROR)
  {
    bs=false;

#ifdef ISO_TP_DEBUG
    Serial.print(F("ISO-TP State: ")); Serial.println(msg->tp_state);
    Serial.print(F("Length      : ")); Serial.println(msg->len);
#endif

    switch(msg->tp_state)
    {
      case ISOTP_IDLE         :  break;
      case ISOTP_SEND         :
                                 if(msg->len<=7)
                                 {
#ifdef ISO_TP_DEBUG
                                   Serial.println(F("Send SF"));
#endif
                                   retval=send_sf(msg);
                                   msg->tp_state=ISOTP_IDLE;
                                 }
                                 else
                                 {
#ifdef ISO_TP_DEBUG
                                   Serial.println(F("Send FF"));
#endif
                                   if(!(retval=send_ff(msg))) // FF complete
                                   {
                                     msg->Buffer+=6;
                                     msg->len-=6;
                                     msg->tp_state=ISOTP_WAIT_FIRST_FC;
                                     fc_wait_frames=0;
                                     wait_fc=millis();
                                   }
                                 }
                                 break;
      case ISOTP_WAIT_FIRST_FC:
#ifdef ISO_TP_DEBUG
                                 Serial.println(F("Wait first FC"));
#endif
                                 delta=millis()-wait_fc;
                                 if(delta >= TIMEOUT_FC)
                                 {
#ifdef ISO_TP_DEBUG
                                   Serial.print(F("FC timeout during receive"));
                                   Serial.print(F(" wait_fc="));
                                   Serial.print(wait_fc);
                                   Serial.print(F(" delta="));
                                   Serial.println(delta);
#endif
                                   msg->tp_state = ISOTP_IDLE;
				   retval=1;
                                 }
                                 break;
      case ISOTP_WAIT_FC      :
#ifdef ISO_TP_DEBUG
                                 Serial.println(F("Wait FC"));
#endif
                                 break;
      case ISOTP_SEND_CF      :
#ifdef ISO_TP_DEBUG
                                 Serial.println(F("Send CF"));
#endif
                                 while(msg->len>7 && !bs)
                                 {
                                   fc_delay(msg->min_sep_time);
                                   if(!(retval=send_cf(msg)))
                                   {
#ifdef ISO_TP_DEBUG
                                     Serial.print(F("Send Seq "));
                                     Serial.println(msg->seq_id);
#endif
                                     if(msg->blocksize > 0)
                                     {
#ifdef ISO_TP_DEBUG
                                       Serial.print(F("Blocksize trigger "));
                                       Serial.print(msg->seq_id %
                                                    msg->blocksize);
#endif
                                       if(!(msg->seq_id % msg->blocksize))
                                       {
                                         bs=true;
                                         msg->tp_state=ISOTP_WAIT_FC;
#ifdef ISO_TP_DEBUG
                                         Serial.println(F(" yes"));
#endif
                                       }
#ifdef ISO_TP_DEBUG
                                       else Serial.println(F(" no"));
#endif
                                     }
                                     msg->seq_id++;
				     if (msg->blocksize < 16)
                                       msg->seq_id %= 16;
                                     else
                                       msg->seq_id %= msg->blocksize;
                                     msg->Buffer+=7;
                                     msg->len-=7;
#ifdef ISO_TP_DEBUG
                                     Serial.print(F("Length      : "));
                                     Serial.println(msg->len);
#endif
                                   }
                                 }
                                 if(!bs)
                                 {
                                   fc_delay(msg->min_sep_time);
#ifdef ISO_TP_DEBUG
                                   Serial.print(F("Send last Seq "));
                                   Serial.println(msg->seq_id);
#endif
                                   retval=send_cf(msg);
                                   msg->tp_state=ISOTP_IDLE;
                                 }
                                 break;
      default                 :  break;
    }


    if(msg->tp_state==ISOTP_WAIT_FIRST_FC ||
       msg->tp_state==ISOTP_WAIT_FC)
    {
      // if(can_receive())
      uint8_t data[8];
      uint8_t len;
      uint32_t id;
      if (can_rx_func(&id, data, &len) == 0)
      {
#ifdef ISO_TP_DEBUG
        Serial.println(F("Send branch:"));
#endif
        // if(rxId==msg->rx_id)
        if(id==msg->rx_id)
        {
          retval=rcv_fc(msg);
          memset(rxBuffer,0,sizeof(rxBuffer));
#ifdef ISO_TP_DEBUG
          Serial.println(F("rxId OK!"));
#endif
        }
      }
    }
  }

  return retval;
}

uint8_t IsoTp::receive(Message_t* msg)
{
  uint8_t n_pci_type=0;
  uint32_t delta=0;

  wait_session=millis();
#ifdef ISO_TP_DEBUG
  Serial.println(F("Start receive..."));
#endif
  msg->tp_state=ISOTP_IDLE;

  while(msg->tp_state!=ISOTP_FINISHED && msg->tp_state!=ISOTP_ERROR)
  {
    delta=millis()-wait_session;
    if(delta >= TIMEOUT_SESSION)
    {
#ifdef ISO_TP_DEBUG
      Serial.print(F("ISO-TP Session timeout wait_session="));
      Serial.print(wait_session); Serial.print(F(" delta="));
      Serial.println(delta);
#endif
      return 1;
    }

    // if(can_receive())
    uint8_t data[8];
    uint8_t len;
    uint32_t id;
    if (can_rx_func(&id, data, &len) == 0)
    {
      // if(rxId==msg->rx_id)
      if(id==msg->rx_id)
      {
#ifdef ISO_TP_DEBUG
        Serial.println(F("rxId OK!"));
#endif
        memcpy(rxBuffer, data, len); // rxBuffer にデータをコピー
        n_pci_type=rxBuffer[0] & 0xF0;

        switch (n_pci_type)
        {
          case N_PCI_FC:
#ifdef ISO_TP_DEBUG
                      Serial.println(F("FC"));
#endif
                      /* tx path: fc frame */
                      rcv_fc(msg);
                      break;

          case N_PCI_SF:
#ifdef ISO_TP_DEBUG
                      Serial.println(F("SF"));
#endif
                      /* rx path: single frame */
                      rcv_sf(msg);
//		      msg->tp_state=ISOTP_FINISHED;
                      break;

          case N_PCI_FF:
#ifdef ISO_TP_DEBUG
                      Serial.println(F("FF"));
#endif
                      /* rx path: first frame */
                      rcv_ff(msg);
//		      msg->tp_state=ISOTP_WAIT_DATA;
                      break;
                      break;

          case N_PCI_CF:
#ifdef ISO_TP_DEBUG
                      Serial.println(F("CF"));
#endif
                      /* rx path: consecutive frame */
                      rcv_cf(msg);
                      break;
        }
        memset(rxBuffer,0,sizeof(rxBuffer));
      }
    }
  }
#ifdef ISO_TP_DEBUG
  Serial.println(F("ISO-TP message received:"));
  print_buffer(msg->rx_id, msg->Buffer, msg->len);
#endif

  return 0;
}
```
</details>

UDS編 以上