Innovate MTS to Megasquirt MSCAN

Dependencies:   CANnucleo mbed

Fork of CANnucleo_Hello by Zoltan Hudak

INNOVATE MTS SERIAL STREAM TO MEGASQUIRT MSCAN ANALOG

This was tested on the olimex olimexino-stm32. code comes with no warrenty, always confirm AFR readings at the device.

I haven't tested this on an ECU or with an LM2 yet this has all been done with simulations, so there may be real world bugs.

code was based off http://developer.mbed.org/users/hudakz/code/CAN_Nucleo_Hello/ mts datasheet http://www.innovatemotorsports.com/support/downloads/Seriallog-2.pdf MSCAN http://www.msextra.com/doc/pdf/Megasquirt_CAN_Broadcast.pdf

** PLEASE NOTE ** MSCAN has table and data offsets to choose what data it requires, this code hasn't implimented any of that so regardless of them it just sends lambda for the first analog value and the mts function value in the second

MTS Serial is RS232 not ttl so you will need an RS-232 Transceiver

main.cpp

Committer:
sordfish
Date:
2015-12-06
Revision:
6:e977526d114f
Parent:
5:c6503b7ae971

File content as of revision 6:e977526d114f:

/*
* INNOVATE MTS SERIAL STREAM TO MEGASQUIRT MSCAN ANALOG 
*
* Written by Arran Short <sordfish2050@gmail.com>
*
* This was tested on the olimex olimexino-stm32. 
* code comes with no warrenty, always confirm AFR readings at the device.
*
* code was based off http://developer.mbed.org/users/hudakz/code/CAN_Nucleo_Hello/
* mts datasheet http://www.innovatemotorsports.com/support/downloads/Seriallog-2.pdf
* MSCAN http://www.msextra.com/doc/pdf/Megasquirt_CAN_Broadcast.pdf
*
*
*
* ************ PLEASE NOTE ************
* MSCAN has table and data offsets to choose what data it requires,
* this code hasn't implimented any of that so regardless of them
* it just sends lambda for the first analog value and the mts function value in the second
*
*
* MTS Serial is RS232 not ttl so you will need an RS-232 Transceiver
*
*/ 

#include "mbed.h"
#include "CAN.h"

Serial device(PA_9, PA_10);
Serial pc(SERIAL_TX, SERIAL_RX);

DigitalOut      led1(PA_5); // GREEN
DigitalOut      led2(PA_1); // YELLOW

CAN             can(PB_8, PB_9);  // CAN Rx pin name, CAN Tx pin name, Automatic recovery from bus-off state enabled by default
CANMessage      rxMsg;
CANMessage      txMsg;

uint16_t        lambda;
uint8_t         mtsfunction;

volatile bool   CANmsgAvailable = false;
volatile bool   LM1 = false;
volatile bool   gotLambda = false;
volatile bool   gotMscanreq = false;

uint8_t mycanid = 0x09; // this is the id set in tunerstudio

uint8_t offset = 0;
uint8_t msgtype = 0;
uint8_t fromid = 0;
uint8_t toid = 0;
uint8_t table = 0;

uint8_t myvarblk;   // these three bytes are the location of where the MS cpu wants the data to be sent,
uint16_t myvaroff;  // MS doesnt have any error checking so make sure these values are correct.
uint8_t varbyt;     //

char rxSerial[8];
char header[2];
char txData[8];


// macros ripped from arduino
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
#define bitSet(value, bit) ((value) |= (1UL << (bit)))
#define bitClear(value, bit) ((value) &= ~(1UL << (bit)))
#define bitWrite(value, bit, bitvalue) (bitvalue ? bitSet(value, bit) : bitClear(value, bit))  

/**
 * @brief   'CAN receive-complete' interrup handler.
 * @note    Called on arrival of new CAN message.
 *          Keep it as short as possible.
 * @param   
 * @retval  
 */
void onCanMsgReceived() {
    CANmsgAvailable = true;
}

/**
 * @brief   'process sub packet'
 * @note    Called when main loop detects a matching Innovate MTS header 
 *          it then detects if its legacy or new protocol, then strips afr data from stream, TODO: Need to add mts inputs and 2nd o2 sensor to mscan messages
 * @param   
 * @retval  
 */
void processsubpacket() {
           
    led2 = !led2;
           
    if ( bitRead(rxSerial[2], 6) == 1 && bitRead(rxSerial[2], 1) == 1 && bitRead(rxSerial[3], 7) == 0) {
        LM1 = false;
        //pc.printf("not LM1\n");
    }
    if ( bitRead(rxSerial[2], 7) == 1 && bitRead(rxSerial[2], 1) == 0 && bitRead(rxSerial[3], 7) == 0) {
        LM1 = true;
        //pc.printf("is LM1\n");
    }

    mtsfunction = 0;

    bitWrite(mtsfunction, 2, bitRead(rxSerial[2], 4));
    bitWrite(mtsfunction, 1, bitRead(rxSerial[2], 3));
    bitWrite(mtsfunction, 0, bitRead(rxSerial[2], 2));
    
    pc.printf("function is "+mtsfunction+"\r\n");
            
    if (mtsfunction == 0) { //function 0 mean good data.
   
        lambda = rxSerial[4] << 6;
        lambda = lambda + rxSerial[5];

        gotLambda = true;
    } else {
        //turn off serial led
        led2 = 0;
    } 
 
}//processsubpacket() 

/**
 * @brief   'ms can id'
 * @note    Called when can message has been received 
 *          it then converts data into human readable names
 * @param   
 * @retval  
 */
void mscanid() {

    offset = rxMsg.id >> 18;
    msgtype = (rxMsg.id << 14) >> 29;
    fromid = (rxMsg.id << 17) >> 28;
    toid = (rxMsg.id << 21) >> 28;
    table = (((rxMsg.id << 29) >> 31) << 5) + ((rxMsg.id << 25) >> 28);
    
}

/**
 * @brief   'mscan tx header'
 * @note    Called when mscan message has been processed 
 *           and a responce needs to be generated from serial data
 * @param   
 * @retval  
 */
void mscantxheader(uint8_t messagetype, uint8_t sendtoid) { // makes an mscan header, byte is for message type
    
    switch (messagetype) {
        case 0:
            // command
            break;
        case 1:
            // request
            break;
        case 2:
            // response
            txMsg.clear();
            txMsg.type   = CANData;
            txMsg.format = CANExtended;
                        
            bitWrite(txMsg.len, 3, bitRead(varbyt, 3));
            bitWrite(txMsg.len, 2, bitRead(varbyt, 2));
            bitWrite(txMsg.len, 1, bitRead(varbyt, 1));
            bitWrite(txMsg.len, 0, bitRead(varbyt, 0)); 
            
            //offset
            bitWrite(txMsg.id, 28, bitRead(myvaroff, 10));
            bitWrite(txMsg.id, 27, bitRead(myvaroff, 9));
            bitWrite(txMsg.id, 26, bitRead(myvaroff, 8));
            bitWrite(txMsg.id, 25, bitRead(myvaroff, 7));
            bitWrite(txMsg.id, 24, bitRead(myvaroff, 6));
            bitWrite(txMsg.id, 23, bitRead(myvaroff, 5));
            bitWrite(txMsg.id, 22, bitRead(myvaroff, 4));
            bitWrite(txMsg.id, 21, bitRead(myvaroff, 3));
            bitWrite(txMsg.id, 20, bitRead(myvaroff, 2));
            bitWrite(txMsg.id, 19, bitRead(myvaroff, 1));
            bitWrite(txMsg.id, 18, bitRead(myvaroff, 0));
            //message type = responce
            bitWrite(txMsg.id, 17, 0);
            bitWrite(txMsg.id, 16, 1);
            bitWrite(txMsg.id, 15, 0);  
            //from id 
            bitWrite(txMsg.id, 14, bitRead(mycanid, 3));
            bitWrite(txMsg.id, 13, bitRead(mycanid, 2));
            bitWrite(txMsg.id, 12, bitRead(mycanid, 1));
            bitWrite(txMsg.id, 11, bitRead(mycanid, 0));
            //to id
            bitWrite(txMsg.id, 10, bitRead(sendtoid, 3));
            bitWrite(txMsg.id, 9, bitRead(sendtoid, 2));
            bitWrite(txMsg.id, 8, bitRead(sendtoid, 1));
            bitWrite(txMsg.id, 7, bitRead(sendtoid, 0));
            //var block
            bitWrite(txMsg.id, 6, bitRead(myvarblk, 3));
            bitWrite(txMsg.id, 5, bitRead(myvarblk, 2));
            bitWrite(txMsg.id, 4, bitRead(myvarblk, 1));
            bitWrite(txMsg.id, 3, bitRead(myvarblk, 0));
            //spares
            bitWrite(txMsg.id, 2, bitRead(myvarblk, 4));
            bitWrite(txMsg.id, 1, 0);
            bitWrite(txMsg.id, 0, 0);

            txMsg.data[0] = lambda >> 8;
            txMsg.data[1] = lambda;
            txMsg.data[2] = 0x00;
            txMsg.data[3] = mtsfunction;
            txMsg.data[4] = 0x00;
            txMsg.data[5] = 0x00; // 2 Spare channels, could add values from shield or other sensors.
            txMsg.data[6] = 0x00; //
            txMsg.data[7] = 0x00;
            
            break;
        case 3:
            // xsub
            break;
        case 4:
            // burn
            break;      
            }
    pc.printf("Tx Message id is: %d \r\n", txMsg.id);
    gotMscanreq = true;

}


void mscanrxdata() {

    if (msgtype == 1 ) { //message is a request
       
        myvarblk = rxMsg.data[0]; 
        pc.printf("message is a request\r\n");

        bitWrite(myvaroff, 10, bitRead(rxMsg.data[1], 7));
        bitWrite(myvaroff, 9, bitRead(rxMsg.data[1], 6));
        bitWrite(myvaroff, 8, bitRead(rxMsg.data[1], 5));
        bitWrite(myvaroff, 7, bitRead(rxMsg.data[1], 4));
        bitWrite(myvaroff, 6, bitRead(rxMsg.data[1], 3));
        bitWrite(myvaroff, 5, bitRead(rxMsg.data[1], 2));
        bitWrite(myvaroff, 4, bitRead(rxMsg.data[1], 1));
        bitWrite(myvaroff, 3, bitRead(rxMsg.data[1], 0));
        bitWrite(myvaroff, 2, bitRead(rxMsg.data[2], 7));
        bitWrite(myvaroff, 1, bitRead(rxMsg.data[2], 6));
        bitWrite(myvaroff, 0, bitRead(rxMsg.data[2], 5));

        varbyt = rxMsg.data[2] << 3 >> 3;
        
        //megasquirt ecu has requested some data, details of what data it wants is provided in the header. TODO - add a function say what data it wants.
        // we need to generate some data to send back, this is done with mscantxheader
        mscantxheader(0x02, fromid);
        }
}//mscanrxdata()
 

void flushSerialBuffer(void) { 
    //pc.printf("flushing..\r\n");
    char char1 = 0; 
    while (device.readable()) {
        char1 = device.getc();
        }
    return;
    } 
 
void testcantx(void) { 
    txMsg.clear();                      // clear Tx message storage
    txMsg.type   = CANData;
    txMsg.format = CANExtended;
    txMsg.len = 0x03;
    txMsg.id = 0x1FFD487C;
    txMsg.data[0] = 0x01;
    txMsg.data[1] = 0x02;
    txMsg.data[2] = 0x03;
    can.write(txMsg);
    pc.printf("test CAN message sent\r\n");

    } 

 
/**
 * @brief   Main
 * @note
 * @param 
 * @retval
 */ 
int main() {
    device.baud(19200);                             // set bit rate to 19200, this is the baudrate of innovate mts
    device.format(8,SerialBase::None,1);
    can.frequency(500000);                          // set bit rate to 500k
    can.attach(&onCanMsgReceived, CAN::RxIrq);      // attach 'CAN receive-complete' interrupt handler
    can.filter(mycanid << 7, 0x780, CANExtended);
    //testcantx();
    
    while (1) { //loop forever
        led1 = 1;
        
        if ( gotLambda && gotMscanreq ) { //send reply to megasquirt when we get data from mts serial and we have mscan frame ready.
            pc.printf("ready to send\r\n");  
            led2 = !led2;
            pc.printf("can tx id is: %d \r\n", txMsg.id);
            pc.printf("can tx type is: %d \r\n", txMsg.type); 
            pc.printf("can tx format is: %d \r\n", txMsg.format); 
            pc.printf("can tx len is: %d \r\n", txMsg.len); 
            pc.printf("can tx data 0 is: %d \r\n", txMsg.data[0]);
            pc.printf("can tx data 1 is: %d \r\n", txMsg.data[1]);
            pc.printf("can tx data 2 is: %d \r\n", txMsg.data[2]);
            pc.printf("can tx data 3 is: %d \r\n", txMsg.data[3]);
            pc.printf("can tx data 4 is: %d \r\n", txMsg.data[4]);
            pc.printf("can tx data 5 is: %d \r\n", txMsg.data[5]);
            pc.printf("can tx data 6 is: %d \r\n", txMsg.data[6]);
            pc.printf("can tx data 7 is: %d \r\n", txMsg.data[7]);   
            can.write(txMsg);
            pc.printf("CAN message sent\r\n");            
            //gotLambda = false;
            gotMscanreq = false;
            
        }
        
        if(CANmsgAvailable) {  //check if interupt has set the flag for avaliable messages
            //led2 = !led2;
            CANmsgAvailable = false;               // reset flag for next use
            can.read(rxMsg);                       // read message into Rx message storage 
            mscanid();
            mscanrxdata();                            
            //led2 = 0;
            }
        
        while (device.readable()) { // checks if there has been and messages from serial port
            for (int x=0; x < 8; x++){
                rxSerial[x] = device.getc();
                if (  x==0 ) {
                    if ( (rxSerial[0] & 0xA2) == 0xA2 ){
                        //led1 = 1; 
                    } else {
                        x=0;
                        pc.printf("serial data reset\r\n");
                        }
                }
            }    
            
            header[0] = (rxSerial[0] & 0xA2);
            header[1] = (rxSerial[1] & 0x80);
        
            if ( header[0] == 0xA2 && header[1] == 0x80) {
                processsubpacket();
            }
            flushSerialBuffer();
        }
        led1 = 0;
    }

}//main