case'C':// test communications. This is used by Tunerstudio to see whether there is an ECU on a given serial port
testComm();
break;
case'L':// List the contents of current page in human readable form
sendPage(true);
break;
case'N':// Displays a new line. Like pushing enter in a text editor
Serial.println();
break;
case'P':// set the current page
//A 2nd byte of data is required after the 'P' specifying the new page number.
//This loop should never need to run as the byte should already be in the buffer, but is here just in case
while(Serial.available()==0){}
currentPage=Serial.read();
if(currentPage>='0'){//This converts the ascii number char into binary
currentPage-='0';
}
if(currentPage==veMapPage||currentPage==ignMapPage||currentPage==afrMapPage||currentPage==boostvvtPage){// Detecting if the current page is a table/map
isMap=true;
}
else{
isMap=false;
}
break;
case'R':// send 39 bytes of realtime values
sendValues(39);
break;
case'F':// send serial protocol version
Serial.print("001");
break;
case'S':// send code version
Serial.print("Speeduino 2016.09");
currentStatus.secl=0;//This is required in TS3 due to its stricter timings
currentStatus.spark^=(-currentStatus.hasSync^currentStatus.spark)&(1<<BIT_SPARK_SYNC);//Set the sync bit of the Spark variable to match the hasSync variable
response[0]=currentStatus.secl;//secl is simply a counter that increments each second. Used to track unexpected resets (Which will reset this count to 0)
//Check whether this is on the X (RPM) or Y (MAP/TPS) axis
if(offset<272)
{
//X Axis
fuelTable.axisX[(offset-256)]=((int)(newValue)*100);//The RPM values sent by megasquirt are divided by 100, need to multiple it back by 100 to make it correct
}
else
{
//Y Axis
offset=15-(offset-272);//Need to do a translation to flip the order (Due to us using (0,0) in the top left rather than bottom right
fuelTable.axisY[offset]=(int)(newValue);
}
return;
}
break;
caseveSetPage:
pnt_configPage=&configPage1;//Setup a pointer to the relevant config page
//For some reason, TunerStudio is sending offsets greater than the maximum page size. I'm not sure if it's their bug or mine, but the fix is to only update the config page if the offset is less than the maximum size
if(offset<page_size)
{
*((byte*)pnt_configPage+(byte)offset)=newValue;//Need to subtract 80 because the map and bins (Which make up 80 bytes) aren't part of the config pages
}
break;
caseignMapPage://Ignition settings page (Page 2)
if(offset<256)//New value is part of the ignition map
//Check whether this is on the X (RPM) or Y (MAP/TPS) axis
if(offset<272)
{
//X Axis
ignitionTable.axisX[(offset-256)]=(int)(newValue)*int(100);//The RPM values sent by megasquirt are divided by 100, need to multiple it back by 100 to make it correct
}
else
{
//Y Axis
offset=15-(offset-272);//Need to do a translation to flip the order
ignitionTable.axisY[offset]=(int)(newValue);
}
return;
}
caseignSetPage:
pnt_configPage=&configPage2;
//For some reason, TunerStudio is sending offsets greater than the maximum page size. I'm not sure if it's their bug or mine, but the fix is to only update the config page if the offset is less than the maximum size
if(offset<page_size)
{
*((byte*)pnt_configPage+(byte)offset)=newValue;//Need to subtract 80 because the map and bins (Which make up 80 bytes) aren't part of the config pages
}
break;
caseafrMapPage://Air/Fuel ratio target settings page
//Check whether this is on the X (RPM) or Y (MAP/TPS) axis
if(offset<272)
{
//X Axis
afrTable.axisX[(offset-256)]=int(newValue)*int(100);//The RPM values sent by megasquirt are divided by 100, need to multiply it back by 100 to make it correct
}
else
{
//Y Axis
offset=15-(offset-272);//Need to do a translation to flip the order
afrTable.axisY[offset]=int(newValue);
}
return;
}
caseafrSetPage:
pnt_configPage=&configPage3;
//For some reason, TunerStudio is sending offsets greater than the maximum page size. I'm not sure if it's their bug or mine, but the fix is to only update the config page if the offset is less than the maximum size
if(offset<page_size)
{
*((byte*)pnt_configPage+(byte)offset)=newValue;//Need to subtract 80 because the map and bins (Which make up 80 bytes) aren't part of the config pages
}
break;
caseiacPage://Idle Air Control settings page (Page 4)
pnt_configPage=&configPage4;
//For some reason, TunerStudio is sending offsets greater than the maximum page size. I'm not sure if it's their bug or mine, but the fix is to only update the config page if the offset is less than the maximum size
if(offset<page_size)
{
*((byte*)pnt_configPage+(byte)offset)=newValue;
}
break;
caseboostvvtPage://Boost and VVT maps (8x8)
if(offset<64)//New value is part of the boost map
{
boostTable.values[7-offset/8][offset%8]=newValue;
return;
}
elseif(offset<72)//New value is on the X (RPM) axis of the boost table
{
boostTable.axisX[(offset-64)]=int(newValue)*int(100);//The RPM values sent by TunerStudio are divided by 100, need to multiply it back by 100 to make it correct
return;
}
elseif(offset<80)//New value is on the Y (TPS) axis of the boost table
{
boostTable.axisY[(7-(offset-72))]=int(newValue);
return;
}
elseif(offset<144)//New value is part of the vvt map
{
offset=offset-80;
vvtTable.values[7-offset/8][offset%8]=newValue;
return;
}
elseif(offset<152)//New value is on the X (RPM) axis of the vvt table
{
offset=offset-144;
vvtTable.axisX[offset]=int(newValue)*int(100);//The RPM values sent by TunerStudio are divided by 100, need to multiply it back by 100 to make it correct
return;
}
else//New value is on the Y (Load) axis of the vvt table
Serial.println(*((byte*)pnt_configPage));// This displays all the byte values between the last array up to but not including the first unsigned int on config page 1
if(currentTitleIndex==121)//Check to see if on boostTable
{
currentTitleIndex=132;//Change over to vvtTable mid display
currentTable=vvtTable;
}
elsecurrentTitleIndex=0;
}while(currentTitleIndex==132);//Should never loop unless going to display vvtTable
}
else
{
//Need to perform a translation of the values[yaxis][xaxis] into the MS expected format
//MS format has origin (0,0) in the bottom left corner, we use the top left for efficiency reasons
byteresponse[map_page_size];
for(intx=0;x<256;x++){response[x]=currentTable.values[15-x/16][x%16];}//This is slightly non-intuitive, but essentially just flips the table vertically (IE top line becomes the bottom line etc). Columns are unchanged
for(intx=256;x<272;x++){response[x]=byte(currentTable.axisX[(x-256)]/100);}//RPM Bins for VE table (Need to be dvidied by 100)
for(inty=272;y<288;y++){response[y]=byte(currentTable.axisY[15-(y-272)]);}//MAP or TPS bins for VE table
//1024 value pairs are sent. We have to receive them all, but only use every second one (We only store 512 calibratino table entries to save on EEPROM space)
//The values are sent as 2 byte ints, but we convert them to single bytes. Any values over 255 are capped at 255.
inttempValue;
bytetempBuffer[2];
boolevery2nd=true;
intx;
intcounter=0;
pinMode(13,OUTPUT);
digitalWrite(13,LOW);
for(x=0;x<1024;x++)
{
//UNlike what is listed in the protocol documentation, the O2 sensor values are sent as bytes rather than ints
if(BYTES_PER_VALUE==1)
{
while(Serial.available()<1){}
tempValue=Serial.read();
}
else
{
while(Serial.available()<2){}
tempBuffer[0]=Serial.read();
tempBuffer[1]=Serial.read();
tempValue=div(int(word(tempBuffer[1],tempBuffer[0])),DIVISION_FACTOR).quot;//Read 2 bytes, convert to word (an unsigned int), convert to signed int. These values come through * 10 from Tuner Studio
tempValue=((tempValue-32)*5)/9;//Convert from F to C
}
tempValue=tempValue+OFFSET;
if(every2nd)//Only use every 2nd value
{
if(tempValue>255){
tempValue=255;// Cap the maximum value to prevent overflow when converting to byte
//We need TOOTH_LOG_SIZE number of records to send to TunerStudio. If there aren't that many in the buffer then we just return and wait for the next call
if(toothHistoryIndex<TOOTH_LOG_SIZE){
return;//This should no longer ever occur since the flagging system was put in place
}
unsignedinttempToothHistory[TOOTH_LOG_BUFFER];//Create a temporary array that will contain a copy of what is in the main toothHistory array
//Copy the working history into the temporary buffer array. This is done so that, if the history loops whilst the values are being sent over serial, it doesn't affect the values