Small project to display some OBD values from the Toyota GT86/ Subaru BRZ/ Scion FRS on an OLED display.

Dependencies:   Adafruit_GFX MODSERIAL mbed-rtos mbed

Files at this revision

API Documentation at this revision

Comitter:
chrta
Date:
Tue Apr 22 14:51:04 2014 +0000
Child:
1:ca506b88b1d6
Commit message:
Initial checkin

Changed in this revision

IsoTpHandler.cpp Show annotated file Show diff for this revision Revisions of this file
IsoTpHandler.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-rtos.lib Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IsoTpHandler.cpp	Tue Apr 22 14:51:04 2014 +0000
@@ -0,0 +1,261 @@
+#include "IsoTpHandler.h"
+
+enum IsoTpMessageType
+{
+    SINGLE_FRAME = 0,      ///<  The single frame transferred contains the complete payload of up to 7 bytes (normal addressing) or 6 bytes (extended addressing)
+    FIRST_FRAME = 1,       ///<  The first frame of a longer multi-frame message packet, used when more than 6/7 bytes of data segmented must be communicated. The first frame contains the length of the full packet, along with the initial data.
+    CONSECUTIVE_FRAME = 2, ///<  A frame containing subsequent data for a multi-frame packet
+    FLOW_CONTOL_FRAME = 3, ///<  The response from the receiver, acknowledging a First-frame segment. It lays down the parameters for the transmission of further consecutive frames.
+};
+
+enum IsoTpFlowType
+{
+    CLEAR_TO_SEND = 0,
+    WAIT = 1,
+    OVERFLOW_ABORT = 2,
+};
+
+const IsoTpHandler::IdleState IsoTpHandler::idleState;
+const IsoTpHandler::ConsequtiveTransferState IsoTpHandler::consequtiveTransferState;
+
+IsoTpHandler::IdleState::IdleState()
+{
+}
+
+void IsoTpHandler::IdleState::processInput(const CANMessage* message, IsoTpHandler* context) const
+{        
+    if (!IsoTpHandler::isValidIsoTpPacket(message))
+    {
+        return;
+    }
+    uint8_t messageType = message->data[0] >> 4;
+    if (messageType == SINGLE_FRAME)
+    {
+        uint8_t messageSize = message->data[0] & 0x0F;
+        if (messageSize > message->len - 1)
+        {
+            printf("Iso tp message is too short: iso len %d vs can len %d\n", messageSize, message->len);
+            return;
+        }
+        context->handle_decoded_packet(&message->data[1], messageSize);
+        return;
+    }
+    if (messageType == FIRST_FRAME)
+    {
+        if (message->len != 8)
+        {
+            printf("Invalid iso tp message length for FIRST_FRAME, length is %d\n", message->len);
+            return;
+        }
+        
+        uint16_t messageSize = ((message->data[0] & 0x0F) << 8) | (message->data[1]);
+        context->init_consequtive_reading(messageSize, &message->data[2]);
+        context->setState(&consequtiveTransferState);
+        return;
+    }
+    if (messageType == CONSECUTIVE_FRAME)
+    {
+        printf("Invalid iso tp message in idle state, because unexpected CONSECUTIVE_FRAME received\n");
+        return;
+    }
+    if (messageType == FLOW_CONTOL_FRAME)
+    {
+        printf("Invalid iso tp message, because unexpected FLOW_CONTOL_FRAME received\n");
+        return;
+    }
+    
+    printf("Invalid iso tp message ?!\n");
+}
+
+void IsoTpHandler::IdleState::onEnter(IsoTpHandler* context) const
+{
+}
+
+void IsoTpHandler::IdleState::onLeave(IsoTpHandler* context) const
+{
+}
+
+IsoTpHandler::ConsequtiveTransferState::ConsequtiveTransferState()
+{
+}
+
+void IsoTpHandler::ConsequtiveTransferState::processInput(const CANMessage* message, IsoTpHandler* context) const
+{
+     if (!IsoTpHandler::isValidIsoTpPacket(message))
+    {
+        return;
+    }
+    uint8_t messageType = message->data[0] >> 4;
+    if (messageType == SINGLE_FRAME)
+    {
+            printf("Received SINGLE_FRAME, expected consequitve frame\n");
+            return;
+    }
+    if (messageType == FIRST_FRAME)
+    {
+        printf("Received FIRST_FRAME, expected consequitve frame\n");
+        return;
+    }
+    if (messageType == CONSECUTIVE_FRAME)
+    {
+        uint8_t index = message->data[0] & 0x0F;
+        if (index != context->getExpectedIndex())
+        {
+            printf("In consequiive frame, received index %d, expected %d\n", index, context->getExpectedIndex());
+            context->setState(&IsoTpHandler::idleState);
+            return;
+    
+        }
+        
+        if (context->appendReceivedData(&message->data[1], message->len - 1))
+        {
+            printf("In consequtive frame, change state\n");
+            
+            context->setState(&IsoTpHandler::idleState);
+        }
+        return;
+    }
+    if (messageType == FLOW_CONTOL_FRAME)
+    {
+        printf("Received FLOW_CONTROL_FRAME, expected consequitve frame\n");
+        return;
+    }
+    
+    printf("Invalid iso tp message, expected consequitve frame ?!\n");
+}
+
+void IsoTpHandler::ConsequtiveTransferState::onEnter(IsoTpHandler* context) const
+{
+}
+
+void IsoTpHandler::ConsequtiveTransferState::onLeave(IsoTpHandler* context) const
+{
+}
+    
+IsoTpHandler::IsoTpHandler(CAN* canInterface)
+: m_state(&idleState)
+, m_canInterface (canInterface)
+{
+    m_state->onEnter(this);
+}
+
+void IsoTpHandler::processCanMessage(const CANMessage* message)
+{
+    printf("Received new CAN message:\n");
+    printf(" ID: 0x%X\n", message->id);
+    printf(" Len: %d\n", message->len);
+    printf(" Type: %s\n", (message->type == CANData ? "data" : "remote"));
+    printf(" Format: %s\n", (message->format == CANStandard ? "standard" : "extended"));
+    printf( "Data: ");
+    if (message->len > 8) {
+        //paranoia
+        error(" WRONG DATA LEN! ");
+        return;
+    }
+    for (unsigned int i = 0; i < message->len; ++i) {
+        printf("%X ", message->data[i]);
+    }
+    printf("\n");
+    m_state->processInput(message, this);
+}
+
+void IsoTpHandler::handle_decoded_packet(const uint8_t* data, uint16_t length)
+{
+    //todo write into mailbox so another thread can consume this or directly call a callback
+    printf("New decoded packet: Length: %d\n", length);
+    printf(" Data: ");
+    for (uint16_t i = 0; i < length; ++i)
+    {
+        printf("%X ", data[i]);
+    }
+    printf("\n");
+}
+
+void IsoTpHandler::init_consequtive_reading(uint16_t messageSize, const uint8_t* data)
+{
+    char msgContent[8];
+    msgContent[0] = (FLOW_CONTOL_FRAME << 4) | CLEAR_TO_SEND;
+    msgContent[1] = 0; //remaining frames should to be sent without flow control or delay
+    msgContent[2] = 0; //Separation Time (ST), minimum delay time between frames (end of one frame and the beginning of the other)
+                       //<= 127, separation time in milliseconds.
+                       //0xF1 to 0xF9, 100 to 900 microseconds.
+    msgContent[3] = 0;
+    msgContent[4] = 0;
+    msgContent[5] = 0;
+    msgContent[6] = 0;
+    msgContent[7] = 0;
+    m_canInterface->write(CANMessage(0x7DF, msgContent, sizeof(msgContent)));
+    
+    memcpy(m_messageBuffer, data, 6);
+    m_expectedMessageSize = messageSize;
+    m_currentMessageSize = 6;
+    m_expectedIndex = 1;
+}
+
+void IsoTpHandler::setState(const State* state)
+{
+    if (state == m_state)
+    {
+        return;
+    }
+    
+    m_state->onLeave(this);
+    m_state = state;
+    m_state->onEnter(this);
+}
+
+bool IsoTpHandler::isValidIsoTpPacket(const CANMessage* message)
+{
+    if (message->len < 1)
+    {
+        printf("Invalid iso tp message, length is zero\n");
+        return false;
+    } 
+    uint8_t messageType = message->data[0] >> 4;
+    if (messageType > FLOW_CONTOL_FRAME)
+    {
+        printf("Invalid iso tp message type %d\n", messageType);
+        return false;
+    }
+    return true;
+}
+
+uint8_t IsoTpHandler::getExpectedIndex() const
+{
+    return m_expectedIndex;
+}
+
+void IsoTpHandler::incrementExpectedIndex()
+{
+    ++m_expectedIndex;
+    if (m_expectedIndex >= 16)
+    {
+        m_expectedIndex = 0;
+    }
+}
+
+bool IsoTpHandler::appendReceivedData(const uint8_t* data, uint8_t length)
+{
+    if (sizeof(m_messageBuffer) < m_currentMessageSize + length)
+    {
+        printf("Buffer in appendReceivedData too small, already got %d bytes, new %d bytes, expected %d bytes.\n", m_currentMessageSize, length, m_expectedMessageSize);
+        return true; //switch state
+    }
+    
+    if (m_expectedMessageSize < m_currentMessageSize + length)
+    {
+        printf("Got too much data in appendReceivedData, already got %d bytes, new %d bytes, expected %d bytes.\n", m_currentMessageSize, length, m_expectedMessageSize);
+        length = m_expectedMessageSize - m_currentMessageSize;
+    }
+    
+    memcpy(m_messageBuffer + m_currentMessageSize, data, length);
+    m_currentMessageSize += length;
+    
+    if (m_expectedMessageSize == m_currentMessageSize)
+    {
+        handle_decoded_packet(m_messageBuffer, m_expectedMessageSize);
+        return true; //switch state
+    }
+    
+    return false; //do not switch state
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/IsoTpHandler.h	Tue Apr 22 14:51:04 2014 +0000
@@ -0,0 +1,85 @@
+#ifndef ISO_TP_HANDLER
+#define ISO_TP_HANDLER
+
+#include "mbed.h"
+
+/**
+ * http://en.wikipedia.org/wiki/ISO_15765-2
+ */
+class IsoTpHandler
+{
+private:
+    class State
+    {
+    public:
+        virtual ~State() {}
+        virtual void processInput(const CANMessage* message, IsoTpHandler* context) const = 0;
+        virtual void onEnter(IsoTpHandler* context) const = 0;
+        virtual void onLeave(IsoTpHandler* context) const = 0;
+    };
+    
+    /**
+     * No special packet expected 
+     */
+    class IdleState : public State
+    {
+    public:
+        IdleState();
+        
+        virtual void processInput(const CANMessage* message, IsoTpHandler* context) const;
+        virtual void onEnter(IsoTpHandler* context) const;
+        virtual void onLeave(IsoTpHandler* context) const;
+    };
+    
+    /**
+     * Expect packets of type "consecutive frame"
+     */
+    class ConsequtiveTransferState : public State
+    {
+    public:
+        ConsequtiveTransferState();
+        
+        virtual void processInput(const CANMessage* message, IsoTpHandler* context) const;
+        virtual void onEnter(IsoTpHandler* context) const;
+        virtual void onLeave(IsoTpHandler* context) const;
+    };
+public:
+    IsoTpHandler(CAN* canInterface);
+      
+    void processCanMessage(const CANMessage* message);
+    
+    void handle_decoded_packet(const uint8_t* data, uint16_t length);
+        
+    /**
+     *
+     * \param[in] data Always 6 bytes.
+     */
+    void init_consequtive_reading(uint16_t messageSize, const uint8_t* data);
+    uint8_t getExpectedIndex() const;
+    void incrementExpectedIndex();
+    /**
+     * \retval \c True if the state should be switched.
+     * \retval \c False if the state not change.
+     */
+    bool appendReceivedData(const uint8_t* data, uint8_t length);
+
+    void setState(const State* state);
+
+    static const IdleState idleState;
+    static const ConsequtiveTransferState consequtiveTransferState;
+    
+    static bool isValidIsoTpPacket(const CANMessage* message);
+
+private:
+    const State* m_state;
+    
+    CAN* m_canInterface;
+    
+    uint8_t m_messageBuffer[256];
+    uint16_t m_expectedMessageSize;
+    uint16_t m_currentMessageSize;
+    uint8_t m_expectedIndex;
+};
+    
+
+#endif //ISO_TP_HANDLER
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Tue Apr 22 14:51:04 2014 +0000
@@ -0,0 +1,56 @@
+#include "mbed.h"
+#include "rtos.h"
+#include "IsoTpHandler.h"
+ 
+DigitalOut led1(LED1);
+DigitalOut led2(LED2);
+CAN can2(p30, p29);
+IsoTpHandler tpHandler(&can2);
+ 
+void led2_thread(void const *args) {
+    while (true) {
+        led2 = !led2;
+        Thread::wait(1000);
+    }
+}
+
+Mail<CANMessage, 16> can_rx_queue;
+
+void can_process_packets(void const *args) {
+    while (true) {
+        osEvent evt = can_rx_queue.get(osWaitForever);
+        if (evt.status == osEventMail) {
+            CANMessage *msg = (CANMessage*) evt.value.p;
+            tpHandler.processCanMessage(msg);
+            can_rx_queue.free(msg);
+        }
+    }
+}
+
+ 
+void can_rx_int_handler() {
+    CANMessage* msg = can_rx_queue.alloc();
+    if (!can2.read(*msg))
+    {
+        //this should not happen, because this function is called from the rx interrupt
+        can_rx_queue.free(msg);
+        return;
+    }
+    
+    osStatus error_code = can_rx_queue.put(msg); 
+    if (error_code != osOK) {
+        error("Putting can message into mailbox failed with code %d!", error);
+    }
+}
+ 
+int main() {
+    can2.frequency(500000);
+    can2.attach(can_rx_int_handler);
+    Thread thread(led2_thread);
+    Thread can_thread(can_process_packets);
+    
+    while (true) {
+        led1 = !led1;
+        Thread::wait(500);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-rtos.lib	Tue Apr 22 14:51:04 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed-rtos/#4ef72665e2c8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Tue Apr 22 14:51:04 2014 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/6473597d706e
\ No newline at end of file