Skip to content

Instantly share code, notes, and snippets.

@sebgiroux
Last active December 28, 2015 22:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sebgiroux/7575688 to your computer and use it in GitHub Desktop.
Save sebgiroux/7575688 to your computer and use it in GitHub Desktop.
/**
* MS CAN Gauge
*
* @author Sébastien Giroux
* @coypright 2013 - All rights reserved
*
*/
#include <SPI.h>
#include <Wire.h>
#include <MCP2515.h>
#include <MCP2515_defs.h>
#include <Utils.h>
// Pin Definitions - MCP2515
#define MCP_CS_PIN 10
#define MCP_RESET_PIN 12
#define MCP_CAN_INTERRUPT 0
#define MCP_CAN_INTERRUPT_PIN 3 // interrupt 0 = pin 3
// Debug
#define DEBUG_SERIAL_BAUD_RATE 115200
// In MS3 (+ MS2 + MS2/Extra), block 7 is "outpc" (outputs-to-PC), the collection of realtime data that is displayed on the gauges in the tuning software.
#define MS_OUTPC_BLOCK 7
#define MS_NB_ITEMS_MONITORED 16
struct MSDataObject {
char title[20];
byte block;
unsigned int offset;
byte reqbytes;
float scale;
float translate;
float value;
unsigned int digits;
};
MSDataObject MSData[MS_NB_ITEMS_MONITORED] = {
// Title, Block, Offset, Reqbytes, Scale, Translate, Value, Digits
{"RPM", MS_OUTPC_BLOCK, 6, 2, 1, 0, 0, 0},
{"AFR", MS_OUTPC_BLOCK, 252, 1, 0.1, 0, 0, 2},
{"CLT", MS_OUTPC_BLOCK, 22, 2, 0.1, 0, 0, 1},
{"MAP", MS_OUTPC_BLOCK, 18, 2, 0.1, 0, 0, 1},
{"MAT", MS_OUTPC_BLOCK, 20, 2, 0.1, 0, 0, 1},
{"SPKADV", MS_OUTPC_BLOCK, 8, 2, 0.1, 0, 0, 2},
{"BATTV", MS_OUTPC_BLOCK, 26, 2, 0.1, 0, 0, 2},
{"TPS", MS_OUTPC_BLOCK, 24, 2, 0.1, 0, 0, 0},
{"Knock", MS_OUTPC_BLOCK, 32, 2, 0.1, 0, 0, 0},
{"Baro", MS_OUTPC_BLOCK, 16, 2, 0.1, 0, 0, 2},
{"EGOc", MS_OUTPC_BLOCK, 34, 2, 0.1, 0, 0, 0},
{"IAC", MS_OUTPC_BLOCK, 54, 2, 0.392, 0, 0, 0},
{"dwell", MS_OUTPC_BLOCK, 62, 2, 0.1, 0, 0, 2},
{"bstduty", MS_OUTPC_BLOCK, 39, 1, 1, 0, 0, 2},
{"idletar", MS_OUTPC_BLOCK, 380, 2, 1, 0, 0, 2},
{"AFRtgt", MS_OUTPC_BLOCK, 12, 1, 0.1, 0, 0, 2}
};
MCP2515 CAN(MCP_CS_PIN, MCP_RESET_PIN, MCP_CAN_INTERRUPT_PIN);
/**
* Setup
*/
void setup() {
Serial.begin(DEBUG_SERIAL_BAUD_RATE);
// Set up SPI Communication
// dataMode can be SPI_MODE0 or SPI_MODE3 only for MCP2515
SPI.setClockDivider(SPI_CLOCK_DIV2);
SPI.setDataMode(SPI_MODE0);
SPI.setBitOrder(MSBFIRST);
SPI.begin();
// Initialize MCP2515 CAN controller at the specified speed and clock frequency
// (Note: This is the oscillator attached to the MCP2515, not the Arduino oscillator)
//speed in KHz, clock in MHz
if (CAN.Init(500, 16)) {
Serial.println("CAN init OK");
}
else {
Serial.println("CAN init failed");
}
CAN.Mode(MODE_CONFIG);
// CNF1 b00000000
CAN.Write(CNF1, 0x00);
// CNF2 b10100100
CAN.Write(CNF2, 0xA4);
// CNF3 b10000100
CAN.Write(CNF3, 0x84);
CAN.Mode(MODE_NORMAL);
// RXB0CTRL clear receive buffers
CAN.Write(RXB0CTRL, B01101000);
// RXB1CTRL clear receive buffers
CAN.Write(RXB1CTRL, B01101000);
// Enable interrupt on RXB0, RXB1
CAN.Write(CANINTE, B00000011);
// Setting interrupts
CAN.Write(BFPCTRL, B00001111);
// Setting the interrupt pin and attaching it
pinMode(MCP_CAN_INTERRUPT_PIN, INPUT); // CAN_INTERRUPT (0) is on pin 3
attachInterrupt(MCP_CAN_INTERRUPT, canISR, FALLING);
}
/**
* Main loop
*/
void loop() {
requestData();
outputData();
delay(2000);
}
/**
* Output fetched data from MS to serial monitor
*/
void outputData() {
Serial.print("RPM ");
Serial.println(getItemValueByTitle("RPM"));
Serial.print("CLT ");
Serial.println(getItemValueByTitle("CLT"));
Serial.print("MAT ");
Serial.println(getItemValueByTitle("MAT"));
Serial.print("SPKADV ");
Serial.println(getItemValueByTitle("SPKADV"));
Serial.print("TPS ");
Serial.println(getItemValueByTitle("TPS"));
Serial.print("BATTV ");
Serial.println(getItemValueByTitle("BATTV"));
Serial.print("AFR ");
Serial.println(getItemValueByTitle("AFR"));
Serial.print("Knock ");
Serial.println(getItemValueByTitle("Knock"));
Serial.print("Baro ");
Serial.println(getItemValueByTitle("Baro"));
Serial.print("EGOC ");
Serial.println(getItemValueByTitle("EGOc"));
Serial.print("IAC ");
Serial.println(getItemValueByTitle("IAC"));
}
/**
* Send a bunch of requests on the CAN bus of the MS for all the items we're monitoring
*/
void requestData() {
for (byte x = 0; x < MS_NB_ITEMS_MONITORED; x++) {
sendRequest(MSData[x].block, MSData[x].offset, MSData[x].reqbytes);
delay(1);
}
}
/**
* Transform a value we got from the MS to a value that is user readable
*
* @param item The item to calculate the user value for
*
*/
float getUserValueFromMSValue(MSDataObject item) {
return (item.value + item.translate) * item.scale;
}
/**
* Interrupt handler function called when we receive data on the CAN bus
*/
void canISR() {
byte SIDH, SIDL, EID8, EID0, DLC;
byte databuffer[7];
unsigned int data;
byte block, canintf, temp;
unsigned int offset;
canintf = CAN.Read(CANINTF);
if (canintf & B00000001) {
SIDH = CAN.Read(RXB0SIDH);
SIDL = CAN.Read(RXB0SIDL);
EID8 = CAN.Read(RXB0EID8);
EID0 = CAN.Read(RXB0EID0);
DLC = CAN.Read(RXB0DLC);
databuffer[0] = CAN.Read(RXB0D0);
databuffer[1] = CAN.Read(RXB0D1);
databuffer[2] = CAN.Read(RXB0D2);
databuffer[3] = CAN.Read(RXB0D3);
databuffer[4] = CAN.Read(RXB0D4);
databuffer[5] = CAN.Read(RXB0D5);
databuffer[6] = CAN.Read(RXB0D6);
databuffer[7] = CAN.Read(RXB0D7);
}
else if (canintf & B00000010) {
SIDH = CAN.Read(RXB1SIDH);
SIDL = CAN.Read(RXB1SIDL);
EID8 = CAN.Read(RXB1EID8);
EID0 = CAN.Read(RXB1EID0);
DLC = CAN.Read(RXB0DLC);
databuffer[0] = CAN.Read(RXB1D0);
databuffer[1] = CAN.Read(RXB1D1);
databuffer[2] = CAN.Read(RXB1D2);
databuffer[3] = CAN.Read(RXB1D3);
databuffer[4] = CAN.Read(RXB1D4);
databuffer[5] = CAN.Read(RXB1D5);
databuffer[6] = CAN.Read(RXB1D6);
databuffer[7] = CAN.Read(RXB1D7);
}
// Figure out block
block = ((B01111000 & EID0) >> 3);
temp = ((B00000100 & EID0) << 3);
block=block | temp;
// Figure out offset
offset = SIDH;
temp = ((SIDL & B11100000) >> 5);
offset = ((offset << 3) | temp);
int itemIndex = getItemIndexByBlockAndOffset(block, offset);
if (itemIndex > -1) {
MSDataObject item = MSData[itemIndex];
switch (item.reqbytes) {
case 1:
item.value = databuffer[0];
break;
case 2:
item.value = word(databuffer[0], databuffer[1]);
break;
default:
Serial.print("Invalid number of bytes");
break;
}
item.value = getUserValueFromMSValue(item);
MSData[itemIndex] = item;
}
else {
Serial.print("Couldn't find item for block ");
Serial.print(block);
Serial.print("and offset ");
Serial.print(offset);
}
CAN.Write(CANINTF, 0x00); // clear interrupt
}
/**
* Get an MS item index by its block and offset value
*
* @param block The block we're looking for
* @param offset The offset we're looking for
*
* @return The index of the item in the MSData array
*/
int getItemIndexByBlockAndOffset(byte block, unsigned int offset) {
int index = 0;
for (int i = 0; i < MS_NB_ITEMS_MONITORED; i++) {
MSDataObject item = MSData[i];
if (item.block == block && item.offset == offset) {
index = i;
break;
}
}
return index;
}
/**
* Get an MS item value by its title
*
* @param title The title we're looking for
*
* @return The current value of the item
*/
String getItemValueByTitle(char title[]) {
MSDataObject itemFound;
for (byte i = 0; i < MS_NB_ITEMS_MONITORED; i++) {
MSDataObject item = MSData[i];
if (strcmp(item.title, title) == 0) {
itemFound = item;
break;
}
}
// Apply number of digits to the value
return Utils::ftoa(itemFound.value, itemFound.digits);
}
/**
* Get an MS item value by its title
*
* @param block The block the item is in
* @param offset The block from the beginning of the block for the item
* @param reqBytes The number of bytes the item is
*
*/
void sendRequest(byte block, unsigned int offset, byte reqBytes) {
byte SIDH, SIDL, EID8, EID0, DLC, D0, D1, D2;
SIDH = lowByte(offset >> 3);
// var_offset<2:0> SRR IDE msg_type <3:0>
SIDL = (lowByte((offset << 5)) | B0001000); //set IDE bit
// MFFFFTTT msg_type, From, To
EID8 = B10011000; //:7 msg_req, from id 3 (4:3)
// TBBBBBSS To, Block, Spare
EID0 = (( block & B00001111) << 3); // last 4 bits, move them to 6:3
EID0 = ((( block & B00010000) >> 2) | EID0); // bit 5 goes to :2
DLC = B00000011;
D0 = (block);
D1 = (offset >> 3);
D2 = (((offset & B00000111) << 5) | reqBytes); // shift offset
digitalWrite(MCP_CS_PIN, LOW);
SPI.transfer(0x40); // Push bits starting at 0x31 (RXB0SIDH)
SPI.transfer(SIDH); //0x31
SPI.transfer(SIDL); //0x32
SPI.transfer(EID8); //0x33
SPI.transfer(EID0); //0x34
SPI.transfer(DLC); //0x35
SPI.transfer(D0); // 0x36 TXB0D0 my_varblk
SPI.transfer(D1); // 0x37 TXB0D1 my_offset
SPI.transfer(D2); // 0x38 TXB0D2 - request 8 bytes(?) from MS3
digitalWrite(MCP_CS_PIN, HIGH); // end write
// RTS - Send this buffer down the wire
CAN.SendBuffer(B10000001);
// Clear interrupt
CAN.Write(CANINTF, 0x00);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment