Last active
December 28, 2015 22:59
-
-
Save sebgiroux/7575688 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* 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