From afad30ff348892bd2efca9ce4dfbf12213089246 Mon Sep 17 00:00:00 2001 From: ConnerMcLaughlin Date: Sun, 11 Sep 2016 18:12:01 -0500 Subject: [PATCH 1/7] CLI ascii boost and vvt table view --- comms.h | 6 ++- comms.ino | 128 +++++++++++++++++++++++++++++++++++++----------------- 2 files changed, 92 insertions(+), 42 deletions(-) diff --git a/comms.h b/comms.h index bcfaed4..b9e9702 100644 --- a/comms.h +++ b/comms.h @@ -22,7 +22,9 @@ const char pageTitles[] PROGMEM //This is being stored in the avr flash instead "\nPage 2 Config\0" "\nAir/Fuel Ratio Map\0" "\nPage 3 Config\0" - "\nPage 4 Config"//No need to put a trailing null because it's the last string and the compliler does it for you. + "\nPage 4 Config\0" + "\nBoost Map\0" + "\nVVT Map"//No need to put a trailing null because it's the last string and the compliler does it for you. }; void command();//This is the heart of the Command Line Interpeter. All that needed to be done was to make it human readable. @@ -34,4 +36,4 @@ void receiveCalibration(byte tableID); void sendToothLog(bool useChar); void testComm(); -#endif // COMMS_H +#endif // COMMS_H diff --git a/comms.ino b/comms.ino index 8465727..feec8dd 100644 --- a/comms.ino +++ b/comms.ino @@ -45,7 +45,7 @@ void command() if (currentPage >= '0') {//This converts the ascii number char into binary currentPage -= '0'; } - if (currentPage == veMapPage || currentPage == ignMapPage || currentPage == afrMapPage) {// Detecting if the current page is a table/map + if (currentPage == veMapPage || currentPage == ignMapPage || currentPage == afrMapPage || currentPage == boostvvtPage) {// Detecting if the current page is a table/map isMap = true; } else { @@ -62,14 +62,39 @@ void command() break; case 'S': // send code version - Serial.print("Speeduino 2016.09"); + //Serial.print(signature); + //break; + + /* + char titleString[18]; + strcat(titleString, displaySignature); + strcat(titleString, " "); + strcat(titleString, TSfirmwareVersion); + + //Serial.print(titleString); + //Serial.write(titleString,16); + */ + Serial.print("Speeduino 2016.08"); currentStatus.secl = 0; //This is required in TS3 due to its stricter timings break; - case 'Q': // send code signature - Serial.print("speeduino 201609-dev"); + case 'Q': // send code version + Serial.print("speeduino 201608"); + + //Serial.print(signature); + //Serial.write(signature); break; + //The following requires TunerStudio 3 + /* + strcat(titleString, signature); + strcat(titleString, " "); + strcat(titleString, TSfirmwareVersion); + + Serial.write(titleString,19); + break; + */ + case 'V': // send VE table and constants in binary sendPage(false); break; @@ -206,8 +231,6 @@ void sendValues(int length) if(requestCount == 0) { currentStatus.secl = 0; } requestCount++; - 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) response[1] = currentStatus.squirt; //Squirt Bitfield response[2] = currentStatus.engine; //Engine Status Bitfield @@ -628,7 +651,12 @@ void sendPage(bool useChar) case boostvvtPage: { - if(!useChar) + if(useChar) + { + currentTable = boostTable; + currentTitleIndex = 121; + } + else { //Need to perform a translation of the values[MAP/TPS][RPM] into the MS expected format byte response[160]; //Bit hacky, but the size is: (8x8 + 8 + 8) + (8x8 + 8 + 8) = 160 @@ -644,6 +672,7 @@ void sendPage(bool useChar) Serial.write((byte *)&response, sizeof(response)); return; } + break; } default: { @@ -656,49 +685,68 @@ void sendPage(bool useChar) { if (useChar) { - const char spaceChar = ' '; - /*while(pageTitles[currentTitleIndex]) + do { - Serial.print(pageTitles[currentTitleIndex]); - currentTitleIndex++; - }*/ - Serial.println((const __FlashStringHelper *)&pageTitles[currentTitleIndex]);// F macro hack - Serial.print(F("\n ")); - for (int x = 0; x < currentTable.xSize; x++)// Horizontal bins - { - byte axisX = byte(currentTable.axisX[x] / 100); - if (axisX < 100) + const char spaceChar = ' '; + /*while(pageTitles[currentTitleIndex]) { - Serial.write(spaceChar); - if (axisX < 10) + Serial.print(pageTitles[currentTitleIndex]); + currentTitleIndex++; + }*/ + Serial.println((const __FlashStringHelper *)&pageTitles[currentTitleIndex]);// F macro hack + Serial.println(); + for (int y = 0; y < currentTable.ySize; y++) + { + byte axisY = byte(currentTable.axisY[y]); + if (axisY < 100) { Serial.write(spaceChar); - } - } - Serial.print(axisX); - Serial.write(spaceChar); - } - Serial.println(); - for (int y = 0; y < currentTable.ySize; y++) - { - Serial.print(byte(currentTable.axisY[y]));// Vertical Bins - Serial.write(spaceChar); - for (int x = 0; x < currentTable.xSize; x++) - { - byte value = currentTable.values[y][x]; - if (value < 100) - { - Serial.write(spaceChar); - if (value < 10) + if (axisY < 10) { Serial.write(spaceChar); } } - Serial.print(value); + Serial.print(axisY);// Vertical Bins + Serial.write(spaceChar); + for (int x = 0; x < currentTable.xSize; x++) + { + byte value = currentTable.values[y][x]; + if (value < 100) + { + Serial.write(spaceChar); + if (value < 10) + { + Serial.write(spaceChar); + } + } + Serial.print(value); + Serial.write(spaceChar); + } + Serial.println(); + } + Serial.print(F(" ")); + for (int x = 0; x < currentTable.xSize; x++)// Horizontal bins + { + byte axisX = byte(currentTable.axisX[x] / 100); + if (axisX < 100) + { + Serial.write(spaceChar); + if (axisX < 10) + { + Serial.write(spaceChar); + } + } + Serial.print(axisX); Serial.write(spaceChar); } Serial.println(); - } + if(currentTitleIndex == 121) + { + currentTitleIndex = 132; + currentTable = vvtTable; + } + else currentTitleIndex = 0; + }while(currentTitleIndex == 132); } else { @@ -869,4 +917,4 @@ void testComm() { Serial.write(1); return; -} +} From c6e42b297110ae06e3c706369a49e7038f07f5db Mon Sep 17 00:00:00 2001 From: ConnerMcLaughlin Date: Sun, 11 Sep 2016 18:21:28 -0500 Subject: [PATCH 2/7] Add files via upload --- comms.ino | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/comms.ino b/comms.ino index feec8dd..6d9ce0b 100644 --- a/comms.ino +++ b/comms.ino @@ -685,7 +685,7 @@ void sendPage(bool useChar) { if (useChar) { - do + do //This is a do while loop that kicks in for the boostvvtPage { const char spaceChar = ' '; /*while(pageTitles[currentTitleIndex]) @@ -740,13 +740,13 @@ void sendPage(bool useChar) Serial.write(spaceChar); } Serial.println(); - if(currentTitleIndex == 121) + if(currentTitleIndex == 121) //Check to see if on boostTable { - currentTitleIndex = 132; + currentTitleIndex = 132; //Change over to vvtTable mid display currentTable = vvtTable; } else currentTitleIndex = 0; - }while(currentTitleIndex == 132); + }while(currentTitleIndex == 132); //Should never loop unless going to display vvtTable } else { From 95e93f02bff13e03b6ed265a4c3dcc42798c853d Mon Sep 17 00:00:00 2001 From: ConnerMcLaughlin Date: Sun, 11 Sep 2016 19:05:22 -0500 Subject: [PATCH 4/7] Update comms.ino --- comms.ino | 1838 ++++++++++++++++++++++++++--------------------------- 1 file changed, 919 insertions(+), 919 deletions(-) diff --git a/comms.ino b/comms.ino index 6d9ce0b..e0ddf9e 100644 --- a/comms.ino +++ b/comms.ino @@ -1,920 +1,920 @@ -/* -Speeduino - Simple engine management for the Arduino Mega 2560 platform -Copyright (C) Josh Stewart -A full copy of the license may be found in the projects root directory -*/ - -/* -This is called when a command is received over serial from TunerStudio / Megatune -It parses the command and calls the relevant function -A detailed description of each call can be found at: http://www.msextra.com/doc/ms1extra/COM_RS232.htm -*/ -//#include "comms.h" -//#include "globals.h" -//#include "storage.h" - -void command() -{ - switch (Serial.read()) - { - case 'A': // send 22 bytes of realtime values - sendValues(22); - break; - - case 'B': // Burn current values to eeprom - writeConfig(); - break; - - 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(signature); - //break; - - /* - char titleString[18]; - strcat(titleString, displaySignature); - strcat(titleString, " "); - strcat(titleString, TSfirmwareVersion); - - //Serial.print(titleString); - //Serial.write(titleString,16); - */ - Serial.print("Speeduino 2016.08"); - currentStatus.secl = 0; //This is required in TS3 due to its stricter timings - break; - - case 'Q': // send code version - Serial.print("speeduino 201608"); - - //Serial.print(signature); - //Serial.write(signature); - break; - - //The following requires TunerStudio 3 - /* - strcat(titleString, signature); - strcat(titleString, " "); - strcat(titleString, TSfirmwareVersion); - - Serial.write(titleString,19); - break; - */ - - case 'V': // send VE table and constants in binary - sendPage(false); - break; - - case 'W': // receive new VE obr constant at 'W'++ - int offset; - while (Serial.available() == 0) { } - - if (isMap) - { - byte offset1, offset2; - offset1 = Serial.read(); - while (Serial.available() == 0) { } - offset2 = Serial.read(); - offset = word(offset2, offset1); - } - else - { - offset = Serial.read(); - } - while (Serial.available() == 0) { } - - receiveValue(offset, Serial.read()); - break; - - case 't': // receive new Calibration info. Command structure: "t", . This is an MS2/Extra command, NOT part of MS1 spec - byte tableID; - //byte canID; - - //The first 2 bytes sent represent the canID and tableID - while (Serial.available() == 0) { } - tableID = Serial.read(); //Not currently used for anything - - receiveCalibration(tableID); //Receive new values and store in memory - writeCalibration(); //Store received values in EEPROM - - break; - - case 'Z': //Totally non-standard testing function. Will be removed once calibration testing is completed. This function takes 1.5kb of program space! :S - digitalWrite(pinInjector1, HIGH); - digitalWrite(pinInjector2, HIGH); - delay(20); - digitalWrite(pinInjector1, LOW); - digitalWrite(pinInjector2, LOW); - return; - Serial.println(F("Coolant")); - for (int x = 0; x < CALIBRATION_TABLE_SIZE; x++) - { - Serial.print(x); - Serial.print(", "); - Serial.println(cltCalibrationTable[x]); - } - Serial.println(F("Inlet temp")); - for (int x = 0; x < CALIBRATION_TABLE_SIZE; x++) - { - Serial.print(x); - Serial.print(", "); - Serial.println(iatCalibrationTable[x]); - } - Serial.println(F("O2")); - for (int x = 0; x < CALIBRATION_TABLE_SIZE; x++) - { - Serial.print(x); - Serial.print(", "); - Serial.println(o2CalibrationTable[x]); - } - Serial.println(F("WUE")); - for (int x = 0; x < 10; x++) - { - Serial.print(configPage2.wueBins[x]); - Serial.print(", "); - Serial.println(configPage1.wueValues[x]); - } - Serial.flush(); - break; - - case 'T': //Send 256 tooth log entries to Tuner Studios tooth logger - sendToothLog(false); //Sends tooth log values as ints - break; - - case 'r': //Send 256 tooth log entries to a terminal emulator - sendToothLog(true); //Sends tooth log values as chars - break; - - - - case '?': - Serial.println - (F( - "\n" - "===Command Help===\n\n" - "All commands are single character and are concatenated with their parameters \n" - "without spaces. Some parameters are binary and cannot be entered through this \n" - "prompt by conventional means. \n" - "Syntax: +++\n\n" - "===List of Commands===\n\n" - "A - Displays 31 bytes of currentStatus values in binary (live data)\n" - "B - Burn current map and configPage values to eeprom\n" - "C - Test COM port. Used by Tunerstudio to see whether an ECU is on a given serial \n" - " port. Returns a binary number.\n" - "L - Displays map page (aka table) or configPage values. Use P to change page (not \n" - " every page is a map)\n" - "N - Print new line.\n" - "P - Set current page. Syntax: P+\n" - "R - Same as A command\n" - "S - Display signature number\n" - "Q - Same as S command\n" - "V - Display map or configPage values in binary\n" - "W - Set one byte in map or configPage. Expects binary parameters. \n" - " Syntax: W++\n" - "t - Set calibration values. Expects binary parameters. Table index is either 0, \n" - " 1, or 2. Syntax: t++++\n" - "Z - Display calibration values\n" - "T - Displays 256 tooth log entries in binary\n" - "r - Displays 256 tooth log entries\n" - "? - Displays this help page" - )); - - break; - - default: - break; - } -} - -/* -This function returns the current values of a fixed group of variables -*/ -void sendValues(int length) -{ - byte packetSize = 35; - byte response[packetSize]; - - if(requestCount == 0) { currentStatus.secl = 0; } - requestCount++; - - 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) - response[1] = currentStatus.squirt; //Squirt Bitfield - response[2] = currentStatus.engine; //Engine Status Bitfield - response[3] = (byte)(divu100(currentStatus.dwell)); //Dwell in ms * 10 - response[4] = (byte)(currentStatus.MAP >> 1); //map value is divided by 2 - response[5] = (byte)(currentStatus.IAT + CALIBRATION_TEMPERATURE_OFFSET); //mat - response[6] = (byte)(currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET); //Coolant ADC - response[7] = currentStatus.tpsADC; //TPS (Raw 0-255) - response[8] = currentStatus.battery10; //battery voltage - response[9] = currentStatus.O2; //O2 - response[10] = currentStatus.egoCorrection; //Exhaust gas correction (%) - response[11] = currentStatus.iatCorrection; //Air temperature Correction (%) - response[12] = currentStatus.wueCorrection; //Warmup enrichment (%) - response[13] = lowByte(currentStatus.RPM); //rpm HB - response[14] = highByte(currentStatus.RPM); //rpm LB - response[15] = currentStatus.TAEamount; //acceleration enrichment (%) - response[16] = 0x00; //Barometer correction (%) - response[17] = currentStatus.corrections; //Total GammaE (%) - response[18] = currentStatus.VE; //Current VE 1 (%) - response[19] = currentStatus.afrTarget; - response[20] = (byte)(currentStatus.PW / 100); //Pulsewidth 1 multiplied by 10 in ms. Have to convert from uS to mS. - response[21] = currentStatus.tpsDOT; //TPS DOT - response[22] = currentStatus.advance; - response[23] = currentStatus.TPS; // TPS (0% to 100%) - //Need to split the int loopsPerSecond value into 2 bytes - response[24] = lowByte(currentStatus.loopsPerSecond); - response[25] = highByte(currentStatus.loopsPerSecond); - - //The following can be used to show the amount of free memory - currentStatus.freeRAM = freeRam(); - response[26] = lowByte(currentStatus.freeRAM); //(byte)((currentStatus.loopsPerSecond >> 8) & 0xFF); - response[27] = highByte(currentStatus.freeRAM); - - response[28] = currentStatus.batCorrection; //Battery voltage correction (%) - response[29] = currentStatus.spark; //Spark related bitfield - response[30] = currentStatus.O2_2; //O2 - - //rpmDOT must be sent as a signed integer - response[31] = lowByte(currentStatus.rpmDOT); - response[32] = highByte(currentStatus.rpmDOT); - - response[33] = currentStatus.flex; //Flex sensor value (or 0 if not used) - response[34] = getNextError(); - -//cli(); - Serial.write(response, (size_t)packetSize); - //Serial.flush(); -//sei(); - return; -} - -void receiveValue(int offset, byte newValue) -{ - - void* pnt_configPage;//This only stores the address of the value that it's pointing to and not the max size - - switch (currentPage) - { - case veMapPage: - if (offset < 256) //New value is part of the fuel map - { - fuelTable.values[15 - offset / 16][offset % 16] = newValue; - return; - } - else - { - //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; - - case veSetPage: - 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; - - case ignMapPage: //Ignition settings page (Page 2) - if (offset < 256) //New value is part of the ignition map - { - ignitionTable.values[15 - offset / 16][offset % 16] = newValue; - return; - } - else - { - //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; - } - - case ignSetPage: - 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; - - case afrMapPage: //Air/Fuel ratio target settings page - if (offset < 256) //New value is part of the afr map - { - afrTable.values[15 - offset / 16][offset % 16] = newValue; - return; - } - else - { - //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; - } - - case afrSetPage: - 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; - - case iacPage: //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; - case boostvvtPage: //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; - } - else if (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; - } - else if (offset < 80) //New value is on the Y (TPS) axis of the boost table - { - boostTable.axisY[(7 - (offset - 72))] = int(newValue); - return; - } - else if (offset < 144) //New value is part of the vvt map - { - offset = offset - 80; - vvtTable.values[7 - offset / 8][offset % 8] = newValue; - return; - } - else if (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 - { - offset = offset - 152; - vvtTable.axisY[(7 - offset)] = int(newValue); - return; - } - default: - break; - } -} - -/* -sendPage() packs the data within the current page (As set with the 'P' command) -into a buffer and sends it. -Note that some translation of the data is required to lay it out in the way Megasqurit / TunerStudio expect it -useChar - If true, all values are send as chars, this is for the serial command line interface. TunerStudio expects data as raw values, so this must be set false in that case -*/ -void sendPage(bool useChar) -{ - void* pnt_configPage; - struct table3D currentTable; - byte currentTitleIndex = 0;// This corresponds to the count up to the first char of a string in pageTitles - - switch (currentPage) - { - case veMapPage: - { - currentTitleIndex = 0; - currentTable = fuelTable; - break; - } - - case veSetPage: - { - // currentTitleIndex = 27; - if (useChar) - { - // To Display Values from Config Page 1 - // When casting to the __FlashStringHelper type Serial.println uses the same subroutine as when using the F macro - Serial.println((const __FlashStringHelper *)&pageTitles[27]);//27 is the index to the first char in the second sting in pageTitles - // The following loop displays in human readable form of all byte values in config page 1 up to but not including the first array. - // incrementing void pointers is cumbersome. Thus we have "pnt_configPage = (byte *)pnt_configPage + 1" - for (pnt_configPage = &configPage1; pnt_configPage < &configPage1.wueValues[0]; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); - for (byte x = 10; x; x--)// The x between the ';' has the same representation as the "x != 0" test or comparision - { - Serial.print(configPage1.wueValues[10 - x]);// This displays the values horizantially on the screen - Serial.print(' '); - } - Serial.println(); - for (pnt_configPage = (byte *)&configPage1.wueValues[9] + 1; pnt_configPage < &configPage1.inj1Ang; pnt_configPage = (byte *)pnt_configPage + 1) { - 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 - } - // The following loop displays four unsigned ints - for (pnt_configPage = &configPage1.inj1Ang; pnt_configPage < (unsigned int *)&configPage1.inj4Ang + 1; pnt_configPage = (unsigned int *)pnt_configPage + 1) Serial.println(*((unsigned int *)pnt_configPage)); - // Following loop displays byte values between the unsigned ints - for (pnt_configPage = (unsigned int *)&configPage1.inj4Ang + 1; pnt_configPage < &configPage1.mapMax; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); - Serial.println(configPage1.mapMax); - // Following loop displays remaining byte values of the page - for (pnt_configPage = (unsigned int *)&configPage1.mapMax + 1; pnt_configPage < (byte *)&configPage1 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); - return; - } - else pnt_configPage = &configPage1; //Create a pointer to Page 1 in memory - break; - } - - case ignMapPage: - { - currentTitleIndex = 42;// the index to the first char of the third string in pageTitles - currentTable = ignitionTable; - break; - } - - case ignSetPage: - { - //currentTitleIndex = 56; - if (useChar) - { - //To Display Values from Config Page 2 - Serial.println((const __FlashStringHelper *)&pageTitles[56]); - Serial.println(configPage2.triggerAngle);// configPsge2.triggerAngle is an int so just display it without complication - // Following loop displays byte values after that first int up to but not including the first array in config page 2 - for (pnt_configPage = (int *)&configPage2 + 1; pnt_configPage < &configPage2.taeBins[0]; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); - for (byte y = 2; y; y--)// Displaying two equal sized arrays - { - byte * currentVar;// A placeholder for each array - if (y == 2) { - currentVar = configPage2.taeBins; - } - else { - currentVar = configPage2.taeValues; - } - - for (byte x = 4; x; x--) - { - Serial.print(currentVar[4 - x]); - Serial.print(' '); - } - Serial.println(); - } - for (byte x = 10; x ; x--) - { - Serial.print(configPage2.wueBins[10 - x]);//Displaying array horizontally across screen - Serial.print(' '); - } - Serial.println(); - Serial.println(configPage2.dwellLimit);// Little lonely byte stuck between two arrays. No complications just display it. - for (byte x = 6; x; x--) - { - Serial.print(configPage2.dwellCorrectionValues[6 - x]); - Serial.print(' '); - } - Serial.println(); - for (pnt_configPage = (byte *)&configPage2.dwellCorrectionValues[5] + 1; pnt_configPage < (byte *)&configPage2 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) - { - Serial.println(*((byte *)pnt_configPage));// Displaying remaining byte values of the page - } - return; - } - else pnt_configPage = &configPage2; //Create a pointer to Page 2 in memory - break; - } - - case afrMapPage: - { - currentTitleIndex = 71;//Array index to next string - currentTable = afrTable; - break; - } - - case afrSetPage: - { - //currentTitleIndex = 91; - if (useChar) - { - //To Display Values from Config Page 3 - Serial.println((const __FlashStringHelper *)&pageTitles[91]);//special typecasting to enable suroutine that the F macro uses - for (pnt_configPage = &configPage3; pnt_configPage < &configPage3.voltageCorrectionBins[0]; pnt_configPage = (byte *)pnt_configPage + 1) - { - Serial.println(*((byte *)pnt_configPage));// Displaying byte values of config page 3 up to but not including the first array - } - for (byte y = 2; y; y--)// Displaying two equally sized arrays that are next to each other - { - byte * currentVar; - if (y == 2) { currentVar = configPage3.voltageCorrectionBins; } - else { currentVar = configPage3.injVoltageCorrectionValues; } - - for (byte x = 6; x; x--) - { - Serial.print(currentVar[6 - x]); - Serial.print(' '); - } - Serial.println(); - } - for (byte y = 2; y; y--)// and again - { - byte* currentVar; - if (y == 2) currentVar = configPage3.airDenBins; - else currentVar = configPage3.airDenRates; - for (byte x = 9; x; x--) - { - Serial.print(currentVar[9 - x]); - Serial.print(' '); - } - Serial.println(); - } - // Following loop displays the remaining byte values of the page - for (pnt_configPage = (byte *)&configPage3.airDenRates[8] + 1; pnt_configPage < (byte *)&configPage3 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) - { - Serial.println(*((byte *)pnt_configPage)); - } - return; - } - else pnt_configPage = &configPage3; //Create a pointer to Page 3 in memory - break; - } - - case iacPage: - { - //currentTitleIndex = 106; - //To Display Values from Config Page 4 - if (useChar) - { - Serial.println((const __FlashStringHelper *)&pageTitles[106]);// F macro hack - for (byte y = 4; y; y--)// Display four equally sized arrays - { - byte * currentVar; - switch (y) - { - case 1: currentVar = configPage4.iacBins; break; - case 2: currentVar = configPage4.iacOLPWMVal; break; - case 3: currentVar = configPage4.iacOLStepVal; break; - case 4: currentVar = configPage4.iacCLValues; break; - default: break; - } - for (byte x = 10; x; x--) - { - Serial.print(currentVar[10 - x]); - Serial.print(' '); - } - Serial.println(); - } - for (byte y = 3; y; y--)// Three equally sized arrays - { - byte * currentVar; - switch (y) - { - case 1: currentVar = configPage4.iacCrankBins; break; - case 2: currentVar = configPage4.iacCrankDuty; break; - case 3: currentVar = configPage4.iacCrankSteps; break; - default: break; - } - for (byte x = 4; x; x--) - { - Serial.print(currentVar[4 - x]); - Serial.print(' '); - } - Serial.println(); - } - // Following loop is for remaining byte value of page - for (pnt_configPage = (byte *)&configPage4.iacCrankBins[3] + 1; pnt_configPage < (byte *)&configPage4 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); - return; - } - else pnt_configPage = &configPage4; //Create a pointer to Page 4 in memory - break; - } - - case boostvvtPage: - { - if(useChar) - { - currentTable = boostTable; - currentTitleIndex = 121; - } - else - { - //Need to perform a translation of the values[MAP/TPS][RPM] into the MS expected format - byte response[160]; //Bit hacky, but the size is: (8x8 + 8 + 8) + (8x8 + 8 + 8) = 160 - - //Boost table - for (int x = 0; x < 64; x++) { response[x] = boostTable.values[7 - x / 8][x % 8]; } - for (int x = 64; x < 72; x++) { response[x] = byte(boostTable.axisX[(x - 64)] / 100); } - for (int y = 72; y < 80; y++) { response[y] = byte(boostTable.axisY[7 - (y - 72)]); } - //VVT table - for (int x = 0; x < 64; x++) { response[x + 80] = vvtTable.values[7 - x / 8][x % 8]; } - for (int x = 64; x < 72; x++) { response[x + 80] = byte(vvtTable.axisX[(x - 64)] / 100); } - for (int y = 72; y < 80; y++) { response[y + 80] = byte(vvtTable.axisY[7 - (y - 72)]); } - Serial.write((byte *)&response, sizeof(response)); - return; - } - break; - } - default: - { - Serial.println(F("\nPage has not been implemented yet. Change to another page.")); - return; - break; - } - } - if (isMap) - { - if (useChar) - { - do //This is a do while loop that kicks in for the boostvvtPage - { - const char spaceChar = ' '; - /*while(pageTitles[currentTitleIndex]) - { - Serial.print(pageTitles[currentTitleIndex]); - currentTitleIndex++; - }*/ - Serial.println((const __FlashStringHelper *)&pageTitles[currentTitleIndex]);// F macro hack - Serial.println(); - for (int y = 0; y < currentTable.ySize; y++) - { - byte axisY = byte(currentTable.axisY[y]); - if (axisY < 100) - { - Serial.write(spaceChar); - if (axisY < 10) - { - Serial.write(spaceChar); - } - } - Serial.print(axisY);// Vertical Bins - Serial.write(spaceChar); - for (int x = 0; x < currentTable.xSize; x++) - { - byte value = currentTable.values[y][x]; - if (value < 100) - { - Serial.write(spaceChar); - if (value < 10) - { - Serial.write(spaceChar); - } - } - Serial.print(value); - Serial.write(spaceChar); - } - Serial.println(); - } - Serial.print(F(" ")); - for (int x = 0; x < currentTable.xSize; x++)// Horizontal bins - { - byte axisX = byte(currentTable.axisX[x] / 100); - if (axisX < 100) - { - Serial.write(spaceChar); - if (axisX < 10) - { - Serial.write(spaceChar); - } - } - Serial.print(axisX); - Serial.write(spaceChar); - } - Serial.println(); - if(currentTitleIndex == 121) //Check to see if on boostTable - { - currentTitleIndex = 132; //Change over to vvtTable mid display - currentTable = vvtTable; - } - else currentTitleIndex = 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 - byte response[map_page_size]; - - for (int x = 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 (int x = 256; x < 272; x++) { response[x] = byte(currentTable.axisX[(x - 256)] / 100); } //RPM Bins for VE table (Need to be dvidied by 100) - for (int y = 272; y < 288; y++) { response[y] = byte(currentTable.axisY[15 - (y - 272)]); } //MAP or TPS bins for VE table - Serial.write((byte *)&response, sizeof(response)); - } - } - else - { - /*if(useChar) - { - while(pageTitles[currentTitleIndex]) - { - Serial.print(pageTitles[currentTitleIndex]); - currentTitleIndex++; - } - Serial.println(); - for(byte x=0;x 255) { - tempValue = 255; // Cap the maximum value to prevent overflow when converting to byte - } - if (tempValue < 0) { - tempValue = 0; - } - - pnt_TargetTable[(x / 2)] = (byte)tempValue; - int y = EEPROM_CALIBRATION_O2 + counter; - - every2nd = false; - analogWrite(13, (counter % 50) ); - counter++; - } - else { - every2nd = true; - } - - } - -} - -/* -Send 256 tooth log entries - * if useChar is true, the values are sent as chars to be printed out by a terminal emulator - * if useChar is false, the values are sent as a 2 byte integer which is readable by TunerStudios tooth logger -*/ -void sendToothLog(bool useChar) -{ - //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 - } - unsigned int tempToothHistory[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 - memcpy( (void*)tempToothHistory, (void*)toothHistory, sizeof(tempToothHistory) ); - toothHistoryIndex = 0; //Reset the history index - - //Loop only needs to go to half the buffer size - if (useChar) - { - for (int x = 0; x < TOOTH_LOG_SIZE; x++) - { - Serial.println(tempToothHistory[x]); - } - } - else - { - for (int x = 0; x < TOOTH_LOG_SIZE; x++) - { - Serial.write(highByte(tempToothHistory[x])); - Serial.write(lowByte(tempToothHistory[x])); - } - BIT_CLEAR(currentStatus.squirt, BIT_SQUIRT_TOOTHLOG1READY); - } -} - -void testComm() -{ - Serial.write(1); - return; +/* +Speeduino - Simple engine management for the Arduino Mega 2560 platform +Copyright (C) Josh Stewart +A full copy of the license may be found in the projects root directory +*/ + +/* +This is called when a command is received over serial from TunerStudio / Megatune +It parses the command and calls the relevant function +A detailed description of each call can be found at: http://www.msextra.com/doc/ms1extra/COM_RS232.htm +*/ +//#include "comms.h" +//#include "globals.h" +//#include "storage.h" + +void command() +{ + switch (Serial.read()) + { + case 'A': // send 22 bytes of realtime values + sendValues(22); + break; + + case 'B': // Burn current values to eeprom + writeConfig(); + break; + + 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(signature); + //break; + + /* + char titleString[18]; + strcat(titleString, displaySignature); + strcat(titleString, " "); + strcat(titleString, TSfirmwareVersion); + + //Serial.print(titleString); + //Serial.write(titleString,16); + */ + Serial.print("Speeduino 2016.09"); + currentStatus.secl = 0; //This is required in TS3 due to its stricter timings + break; + + case 'Q': // send code version + Serial.print("speeduino 201608"); + + //Serial.print(signature); + //Serial.write(signature); + break; + + //The following requires TunerStudio 3 + /* + strcat(titleString, signature); + strcat(titleString, " "); + strcat(titleString, TSfirmwareVersion); + + Serial.write(titleString,19); + break; + */ + + case 'V': // send VE table and constants in binary + sendPage(false); + break; + + case 'W': // receive new VE obr constant at 'W'++ + int offset; + while (Serial.available() == 0) { } + + if (isMap) + { + byte offset1, offset2; + offset1 = Serial.read(); + while (Serial.available() == 0) { } + offset2 = Serial.read(); + offset = word(offset2, offset1); + } + else + { + offset = Serial.read(); + } + while (Serial.available() == 0) { } + + receiveValue(offset, Serial.read()); + break; + + case 't': // receive new Calibration info. Command structure: "t", . This is an MS2/Extra command, NOT part of MS1 spec + byte tableID; + //byte canID; + + //The first 2 bytes sent represent the canID and tableID + while (Serial.available() == 0) { } + tableID = Serial.read(); //Not currently used for anything + + receiveCalibration(tableID); //Receive new values and store in memory + writeCalibration(); //Store received values in EEPROM + + break; + + case 'Z': //Totally non-standard testing function. Will be removed once calibration testing is completed. This function takes 1.5kb of program space! :S + digitalWrite(pinInjector1, HIGH); + digitalWrite(pinInjector2, HIGH); + delay(20); + digitalWrite(pinInjector1, LOW); + digitalWrite(pinInjector2, LOW); + return; + Serial.println(F("Coolant")); + for (int x = 0; x < CALIBRATION_TABLE_SIZE; x++) + { + Serial.print(x); + Serial.print(", "); + Serial.println(cltCalibrationTable[x]); + } + Serial.println(F("Inlet temp")); + for (int x = 0; x < CALIBRATION_TABLE_SIZE; x++) + { + Serial.print(x); + Serial.print(", "); + Serial.println(iatCalibrationTable[x]); + } + Serial.println(F("O2")); + for (int x = 0; x < CALIBRATION_TABLE_SIZE; x++) + { + Serial.print(x); + Serial.print(", "); + Serial.println(o2CalibrationTable[x]); + } + Serial.println(F("WUE")); + for (int x = 0; x < 10; x++) + { + Serial.print(configPage2.wueBins[x]); + Serial.print(", "); + Serial.println(configPage1.wueValues[x]); + } + Serial.flush(); + break; + + case 'T': //Send 256 tooth log entries to Tuner Studios tooth logger + sendToothLog(false); //Sends tooth log values as ints + break; + + case 'r': //Send 256 tooth log entries to a terminal emulator + sendToothLog(true); //Sends tooth log values as chars + break; + + + + case '?': + Serial.println + (F( + "\n" + "===Command Help===\n\n" + "All commands are single character and are concatenated with their parameters \n" + "without spaces. Some parameters are binary and cannot be entered through this \n" + "prompt by conventional means. \n" + "Syntax: +++\n\n" + "===List of Commands===\n\n" + "A - Displays 31 bytes of currentStatus values in binary (live data)\n" + "B - Burn current map and configPage values to eeprom\n" + "C - Test COM port. Used by Tunerstudio to see whether an ECU is on a given serial \n" + " port. Returns a binary number.\n" + "L - Displays map page (aka table) or configPage values. Use P to change page (not \n" + " every page is a map)\n" + "N - Print new line.\n" + "P - Set current page. Syntax: P+\n" + "R - Same as A command\n" + "S - Display signature number\n" + "Q - Same as S command\n" + "V - Display map or configPage values in binary\n" + "W - Set one byte in map or configPage. Expects binary parameters. \n" + " Syntax: W++\n" + "t - Set calibration values. Expects binary parameters. Table index is either 0, \n" + " 1, or 2. Syntax: t++++\n" + "Z - Display calibration values\n" + "T - Displays 256 tooth log entries in binary\n" + "r - Displays 256 tooth log entries\n" + "? - Displays this help page" + )); + + break; + + default: + break; + } +} + +/* +This function returns the current values of a fixed group of variables +*/ +void sendValues(int length) +{ + byte packetSize = 35; + byte response[packetSize]; + + if(requestCount == 0) { currentStatus.secl = 0; } + requestCount++; + + currentStatus.spark ^= (-currentStatus.hasSync ^ currentStatus.spark) & (1 << BIT_SPARK_SYNC); //Set the sync bit of the Spark variable to match the hasSync variableresponse[0] = currentStatus.secl; //secl is simply a counter that increments each second. Used to track unexpected resets (Which will reset this count to 0) + response[1] = currentStatus.squirt; //Squirt Bitfield + response[2] = currentStatus.engine; //Engine Status Bitfield + response[3] = (byte)(divu100(currentStatus.dwell)); //Dwell in ms * 10 + response[4] = (byte)(currentStatus.MAP >> 1); //map value is divided by 2 + response[5] = (byte)(currentStatus.IAT + CALIBRATION_TEMPERATURE_OFFSET); //mat + response[6] = (byte)(currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET); //Coolant ADC + response[7] = currentStatus.tpsADC; //TPS (Raw 0-255) + response[8] = currentStatus.battery10; //battery voltage + response[9] = currentStatus.O2; //O2 + response[10] = currentStatus.egoCorrection; //Exhaust gas correction (%) + response[11] = currentStatus.iatCorrection; //Air temperature Correction (%) + response[12] = currentStatus.wueCorrection; //Warmup enrichment (%) + response[13] = lowByte(currentStatus.RPM); //rpm HB + response[14] = highByte(currentStatus.RPM); //rpm LB + response[15] = currentStatus.TAEamount; //acceleration enrichment (%) + response[16] = 0x00; //Barometer correction (%) + response[17] = currentStatus.corrections; //Total GammaE (%) + response[18] = currentStatus.VE; //Current VE 1 (%) + response[19] = currentStatus.afrTarget; + response[20] = (byte)(currentStatus.PW / 100); //Pulsewidth 1 multiplied by 10 in ms. Have to convert from uS to mS. + response[21] = currentStatus.tpsDOT; //TPS DOT + response[22] = currentStatus.advance; + response[23] = currentStatus.TPS; // TPS (0% to 100%) + //Need to split the int loopsPerSecond value into 2 bytes + response[24] = lowByte(currentStatus.loopsPerSecond); + response[25] = highByte(currentStatus.loopsPerSecond); + + //The following can be used to show the amount of free memory + currentStatus.freeRAM = freeRam(); + response[26] = lowByte(currentStatus.freeRAM); //(byte)((currentStatus.loopsPerSecond >> 8) & 0xFF); + response[27] = highByte(currentStatus.freeRAM); + + response[28] = currentStatus.batCorrection; //Battery voltage correction (%) + response[29] = currentStatus.spark; //Spark related bitfield + response[30] = currentStatus.O2_2; //O2 + + //rpmDOT must be sent as a signed integer + response[31] = lowByte(currentStatus.rpmDOT); + response[32] = highByte(currentStatus.rpmDOT); + + response[33] = currentStatus.flex; //Flex sensor value (or 0 if not used) + response[34] = getNextError(); + +//cli(); + Serial.write(response, (size_t)packetSize); + //Serial.flush(); +//sei(); + return; +} + +void receiveValue(int offset, byte newValue) +{ + + void* pnt_configPage;//This only stores the address of the value that it's pointing to and not the max size + + switch (currentPage) + { + case veMapPage: + if (offset < 256) //New value is part of the fuel map + { + fuelTable.values[15 - offset / 16][offset % 16] = newValue; + return; + } + else + { + //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; + + case veSetPage: + 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; + + case ignMapPage: //Ignition settings page (Page 2) + if (offset < 256) //New value is part of the ignition map + { + ignitionTable.values[15 - offset / 16][offset % 16] = newValue; + return; + } + else + { + //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; + } + + case ignSetPage: + 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; + + case afrMapPage: //Air/Fuel ratio target settings page + if (offset < 256) //New value is part of the afr map + { + afrTable.values[15 - offset / 16][offset % 16] = newValue; + return; + } + else + { + //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; + } + + case afrSetPage: + 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; + + case iacPage: //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; + case boostvvtPage: //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; + } + else if (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; + } + else if (offset < 80) //New value is on the Y (TPS) axis of the boost table + { + boostTable.axisY[(7 - (offset - 72))] = int(newValue); + return; + } + else if (offset < 144) //New value is part of the vvt map + { + offset = offset - 80; + vvtTable.values[7 - offset / 8][offset % 8] = newValue; + return; + } + else if (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 + { + offset = offset - 152; + vvtTable.axisY[(7 - offset)] = int(newValue); + return; + } + default: + break; + } +} + +/* +sendPage() packs the data within the current page (As set with the 'P' command) +into a buffer and sends it. +Note that some translation of the data is required to lay it out in the way Megasqurit / TunerStudio expect it +useChar - If true, all values are send as chars, this is for the serial command line interface. TunerStudio expects data as raw values, so this must be set false in that case +*/ +void sendPage(bool useChar) +{ + void* pnt_configPage; + struct table3D currentTable; + byte currentTitleIndex = 0;// This corresponds to the count up to the first char of a string in pageTitles + + switch (currentPage) + { + case veMapPage: + { + currentTitleIndex = 0; + currentTable = fuelTable; + break; + } + + case veSetPage: + { + // currentTitleIndex = 27; + if (useChar) + { + // To Display Values from Config Page 1 + // When casting to the __FlashStringHelper type Serial.println uses the same subroutine as when using the F macro + Serial.println((const __FlashStringHelper *)&pageTitles[27]);//27 is the index to the first char in the second sting in pageTitles + // The following loop displays in human readable form of all byte values in config page 1 up to but not including the first array. + // incrementing void pointers is cumbersome. Thus we have "pnt_configPage = (byte *)pnt_configPage + 1" + for (pnt_configPage = &configPage1; pnt_configPage < &configPage1.wueValues[0]; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); + for (byte x = 10; x; x--)// The x between the ';' has the same representation as the "x != 0" test or comparision + { + Serial.print(configPage1.wueValues[10 - x]);// This displays the values horizantially on the screen + Serial.print(' '); + } + Serial.println(); + for (pnt_configPage = (byte *)&configPage1.wueValues[9] + 1; pnt_configPage < &configPage1.inj1Ang; pnt_configPage = (byte *)pnt_configPage + 1) { + 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 + } + // The following loop displays four unsigned ints + for (pnt_configPage = &configPage1.inj1Ang; pnt_configPage < (unsigned int *)&configPage1.inj4Ang + 1; pnt_configPage = (unsigned int *)pnt_configPage + 1) Serial.println(*((unsigned int *)pnt_configPage)); + // Following loop displays byte values between the unsigned ints + for (pnt_configPage = (unsigned int *)&configPage1.inj4Ang + 1; pnt_configPage < &configPage1.mapMax; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); + Serial.println(configPage1.mapMax); + // Following loop displays remaining byte values of the page + for (pnt_configPage = (unsigned int *)&configPage1.mapMax + 1; pnt_configPage < (byte *)&configPage1 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); + return; + } + else pnt_configPage = &configPage1; //Create a pointer to Page 1 in memory + break; + } + + case ignMapPage: + { + currentTitleIndex = 42;// the index to the first char of the third string in pageTitles + currentTable = ignitionTable; + break; + } + + case ignSetPage: + { + //currentTitleIndex = 56; + if (useChar) + { + //To Display Values from Config Page 2 + Serial.println((const __FlashStringHelper *)&pageTitles[56]); + Serial.println(configPage2.triggerAngle);// configPsge2.triggerAngle is an int so just display it without complication + // Following loop displays byte values after that first int up to but not including the first array in config page 2 + for (pnt_configPage = (int *)&configPage2 + 1; pnt_configPage < &configPage2.taeBins[0]; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); + for (byte y = 2; y; y--)// Displaying two equal sized arrays + { + byte * currentVar;// A placeholder for each array + if (y == 2) { + currentVar = configPage2.taeBins; + } + else { + currentVar = configPage2.taeValues; + } + + for (byte x = 4; x; x--) + { + Serial.print(currentVar[4 - x]); + Serial.print(' '); + } + Serial.println(); + } + for (byte x = 10; x ; x--) + { + Serial.print(configPage2.wueBins[10 - x]);//Displaying array horizontally across screen + Serial.print(' '); + } + Serial.println(); + Serial.println(configPage2.dwellLimit);// Little lonely byte stuck between two arrays. No complications just display it. + for (byte x = 6; x; x--) + { + Serial.print(configPage2.dwellCorrectionValues[6 - x]); + Serial.print(' '); + } + Serial.println(); + for (pnt_configPage = (byte *)&configPage2.dwellCorrectionValues[5] + 1; pnt_configPage < (byte *)&configPage2 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) + { + Serial.println(*((byte *)pnt_configPage));// Displaying remaining byte values of the page + } + return; + } + else pnt_configPage = &configPage2; //Create a pointer to Page 2 in memory + break; + } + + case afrMapPage: + { + currentTitleIndex = 71;//Array index to next string + currentTable = afrTable; + break; + } + + case afrSetPage: + { + //currentTitleIndex = 91; + if (useChar) + { + //To Display Values from Config Page 3 + Serial.println((const __FlashStringHelper *)&pageTitles[91]);//special typecasting to enable suroutine that the F macro uses + for (pnt_configPage = &configPage3; pnt_configPage < &configPage3.voltageCorrectionBins[0]; pnt_configPage = (byte *)pnt_configPage + 1) + { + Serial.println(*((byte *)pnt_configPage));// Displaying byte values of config page 3 up to but not including the first array + } + for (byte y = 2; y; y--)// Displaying two equally sized arrays that are next to each other + { + byte * currentVar; + if (y == 2) { currentVar = configPage3.voltageCorrectionBins; } + else { currentVar = configPage3.injVoltageCorrectionValues; } + + for (byte x = 6; x; x--) + { + Serial.print(currentVar[6 - x]); + Serial.print(' '); + } + Serial.println(); + } + for (byte y = 2; y; y--)// and again + { + byte* currentVar; + if (y == 2) currentVar = configPage3.airDenBins; + else currentVar = configPage3.airDenRates; + for (byte x = 9; x; x--) + { + Serial.print(currentVar[9 - x]); + Serial.print(' '); + } + Serial.println(); + } + // Following loop displays the remaining byte values of the page + for (pnt_configPage = (byte *)&configPage3.airDenRates[8] + 1; pnt_configPage < (byte *)&configPage3 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) + { + Serial.println(*((byte *)pnt_configPage)); + } + return; + } + else pnt_configPage = &configPage3; //Create a pointer to Page 3 in memory + break; + } + + case iacPage: + { + //currentTitleIndex = 106; + //To Display Values from Config Page 4 + if (useChar) + { + Serial.println((const __FlashStringHelper *)&pageTitles[106]);// F macro hack + for (byte y = 4; y; y--)// Display four equally sized arrays + { + byte * currentVar; + switch (y) + { + case 1: currentVar = configPage4.iacBins; break; + case 2: currentVar = configPage4.iacOLPWMVal; break; + case 3: currentVar = configPage4.iacOLStepVal; break; + case 4: currentVar = configPage4.iacCLValues; break; + default: break; + } + for (byte x = 10; x; x--) + { + Serial.print(currentVar[10 - x]); + Serial.print(' '); + } + Serial.println(); + } + for (byte y = 3; y; y--)// Three equally sized arrays + { + byte * currentVar; + switch (y) + { + case 1: currentVar = configPage4.iacCrankBins; break; + case 2: currentVar = configPage4.iacCrankDuty; break; + case 3: currentVar = configPage4.iacCrankSteps; break; + default: break; + } + for (byte x = 4; x; x--) + { + Serial.print(currentVar[4 - x]); + Serial.print(' '); + } + Serial.println(); + } + // Following loop is for remaining byte value of page + for (pnt_configPage = (byte *)&configPage4.iacCrankBins[3] + 1; pnt_configPage < (byte *)&configPage4 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); + return; + } + else pnt_configPage = &configPage4; //Create a pointer to Page 4 in memory + break; + } + + case boostvvtPage: + { + if(useChar) + { + currentTable = boostTable; + currentTitleIndex = 121; + } + else + { + //Need to perform a translation of the values[MAP/TPS][RPM] into the MS expected format + byte response[160]; //Bit hacky, but the size is: (8x8 + 8 + 8) + (8x8 + 8 + 8) = 160 + + //Boost table + for (int x = 0; x < 64; x++) { response[x] = boostTable.values[7 - x / 8][x % 8]; } + for (int x = 64; x < 72; x++) { response[x] = byte(boostTable.axisX[(x - 64)] / 100); } + for (int y = 72; y < 80; y++) { response[y] = byte(boostTable.axisY[7 - (y - 72)]); } + //VVT table + for (int x = 0; x < 64; x++) { response[x + 80] = vvtTable.values[7 - x / 8][x % 8]; } + for (int x = 64; x < 72; x++) { response[x + 80] = byte(vvtTable.axisX[(x - 64)] / 100); } + for (int y = 72; y < 80; y++) { response[y + 80] = byte(vvtTable.axisY[7 - (y - 72)]); } + Serial.write((byte *)&response, sizeof(response)); + return; + } + break; + } + default: + { + Serial.println(F("\nPage has not been implemented yet. Change to another page.")); + return; + break; + } + } + if (isMap) + { + if (useChar) + { + do //This is a do while loop that kicks in for the boostvvtPage + { + const char spaceChar = ' '; + /*while(pageTitles[currentTitleIndex]) + { + Serial.print(pageTitles[currentTitleIndex]); + currentTitleIndex++; + }*/ + Serial.println((const __FlashStringHelper *)&pageTitles[currentTitleIndex]);// F macro hack + Serial.println(); + for (int y = 0; y < currentTable.ySize; y++) + { + byte axisY = byte(currentTable.axisY[y]); + if (axisY < 100) + { + Serial.write(spaceChar); + if (axisY < 10) + { + Serial.write(spaceChar); + } + } + Serial.print(axisY);// Vertical Bins + Serial.write(spaceChar); + for (int x = 0; x < currentTable.xSize; x++) + { + byte value = currentTable.values[y][x]; + if (value < 100) + { + Serial.write(spaceChar); + if (value < 10) + { + Serial.write(spaceChar); + } + } + Serial.print(value); + Serial.write(spaceChar); + } + Serial.println(); + } + Serial.print(F(" ")); + for (int x = 0; x < currentTable.xSize; x++)// Horizontal bins + { + byte axisX = byte(currentTable.axisX[x] / 100); + if (axisX < 100) + { + Serial.write(spaceChar); + if (axisX < 10) + { + Serial.write(spaceChar); + } + } + Serial.print(axisX); + Serial.write(spaceChar); + } + Serial.println(); + if(currentTitleIndex == 121) //Check to see if on boostTable + { + currentTitleIndex = 132; //Change over to vvtTable mid display + currentTable = vvtTable; + } + else currentTitleIndex = 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 + byte response[map_page_size]; + + for (int x = 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 (int x = 256; x < 272; x++) { response[x] = byte(currentTable.axisX[(x - 256)] / 100); } //RPM Bins for VE table (Need to be dvidied by 100) + for (int y = 272; y < 288; y++) { response[y] = byte(currentTable.axisY[15 - (y - 272)]); } //MAP or TPS bins for VE table + Serial.write((byte *)&response, sizeof(response)); + } + } + else + { + /*if(useChar) + { + while(pageTitles[currentTitleIndex]) + { + Serial.print(pageTitles[currentTitleIndex]); + currentTitleIndex++; + } + Serial.println(); + for(byte x=0;x 255) { + tempValue = 255; // Cap the maximum value to prevent overflow when converting to byte + } + if (tempValue < 0) { + tempValue = 0; + } + + pnt_TargetTable[(x / 2)] = (byte)tempValue; + int y = EEPROM_CALIBRATION_O2 + counter; + + every2nd = false; + analogWrite(13, (counter % 50) ); + counter++; + } + else { + every2nd = true; + } + + } + +} + +/* +Send 256 tooth log entries + * if useChar is true, the values are sent as chars to be printed out by a terminal emulator + * if useChar is false, the values are sent as a 2 byte integer which is readable by TunerStudios tooth logger +*/ +void sendToothLog(bool useChar) +{ + //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 + } + unsigned int tempToothHistory[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 + memcpy( (void*)tempToothHistory, (void*)toothHistory, sizeof(tempToothHistory) ); + toothHistoryIndex = 0; //Reset the history index + + //Loop only needs to go to half the buffer size + if (useChar) + { + for (int x = 0; x < TOOTH_LOG_SIZE; x++) + { + Serial.println(tempToothHistory[x]); + } + } + else + { + for (int x = 0; x < TOOTH_LOG_SIZE; x++) + { + Serial.write(highByte(tempToothHistory[x])); + Serial.write(lowByte(tempToothHistory[x])); + } + BIT_CLEAR(currentStatus.squirt, BIT_SQUIRT_TOOTHLOG1READY); + } +} + +void testComm() +{ + Serial.write(1); + return; } From b68a1c4912f5b8147fce9108ea4dfeaaca01b0e8 Mon Sep 17 00:00:00 2001 From: ConnerMcLaughlin Date: Sun, 11 Sep 2016 19:28:19 -0500 Subject: [PATCH 5/7] Last time hopefully --- comms.ino | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/comms.ino b/comms.ino index e0ddf9e..b41d0f3 100644 --- a/comms.ino +++ b/comms.ino @@ -231,7 +231,8 @@ void sendValues(int length) if(requestCount == 0) { currentStatus.secl = 0; } requestCount++; - currentStatus.spark ^= (-currentStatus.hasSync ^ currentStatus.spark) & (1 << BIT_SPARK_SYNC); //Set the sync bit of the Spark variable to match the hasSync variableresponse[0] = currentStatus.secl; //secl is simply a counter that increments each second. Used to track unexpected resets (Which will reset this count to 0) + 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) response[1] = currentStatus.squirt; //Squirt Bitfield response[2] = currentStatus.engine; //Engine Status Bitfield response[3] = (byte)(divu100(currentStatus.dwell)); //Dwell in ms * 10 From 5195e705d8ce344d938aa6b0d8c64064285dae2a Mon Sep 17 00:00:00 2001 From: ConnerMcLaughlin Date: Sun, 11 Sep 2016 20:04:14 -0500 Subject: [PATCH 6/7] Update comms.ino --- comms.ino | 29 ++--------------------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/comms.ino b/comms.ino index b41d0f3..e90ec3f 100644 --- a/comms.ino +++ b/comms.ino @@ -62,38 +62,13 @@ void command() break; case 'S': // send code version - //Serial.print(signature); - //break; - - /* - char titleString[18]; - strcat(titleString, displaySignature); - strcat(titleString, " "); - strcat(titleString, TSfirmwareVersion); - - //Serial.print(titleString); - //Serial.write(titleString,16); - */ Serial.print("Speeduino 2016.09"); currentStatus.secl = 0; //This is required in TS3 due to its stricter timings break; case 'Q': // send code version - Serial.print("speeduino 201608"); - - //Serial.print(signature); - //Serial.write(signature); - break; - - //The following requires TunerStudio 3 - /* - strcat(titleString, signature); - strcat(titleString, " "); - strcat(titleString, TSfirmwareVersion); - - Serial.write(titleString,19); - break; - */ + Serial.print("speeduino 201609"); + break; case 'V': // send VE table and constants in binary sendPage(false); From 2c979ec7d8d332dce4fa1bccb7629f0fd61094b2 Mon Sep 17 00:00:00 2001 From: Josh Stewart Date: Mon, 12 Sep 2016 12:14:41 +1000 Subject: [PATCH 7/7] Minor cleanup and re-add dev take to signature --- comms.ino | 1793 +++++++++++++++++++++++++++-------------------------- 1 file changed, 897 insertions(+), 896 deletions(-) diff --git a/comms.ino b/comms.ino index e90ec3f..603605e 100644 --- a/comms.ino +++ b/comms.ino @@ -1,896 +1,897 @@ -/* -Speeduino - Simple engine management for the Arduino Mega 2560 platform -Copyright (C) Josh Stewart -A full copy of the license may be found in the projects root directory -*/ - -/* -This is called when a command is received over serial from TunerStudio / Megatune -It parses the command and calls the relevant function -A detailed description of each call can be found at: http://www.msextra.com/doc/ms1extra/COM_RS232.htm -*/ -//#include "comms.h" -//#include "globals.h" -//#include "storage.h" - -void command() -{ - switch (Serial.read()) - { - case 'A': // send 22 bytes of realtime values - sendValues(22); - break; - - case 'B': // Burn current values to eeprom - writeConfig(); - break; - - 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 - break; - - case 'Q': // send code version - Serial.print("speeduino 201609"); - break; - - case 'V': // send VE table and constants in binary - sendPage(false); - break; - - case 'W': // receive new VE obr constant at 'W'++ - int offset; - while (Serial.available() == 0) { } - - if (isMap) - { - byte offset1, offset2; - offset1 = Serial.read(); - while (Serial.available() == 0) { } - offset2 = Serial.read(); - offset = word(offset2, offset1); - } - else - { - offset = Serial.read(); - } - while (Serial.available() == 0) { } - - receiveValue(offset, Serial.read()); - break; - - case 't': // receive new Calibration info. Command structure: "t", . This is an MS2/Extra command, NOT part of MS1 spec - byte tableID; - //byte canID; - - //The first 2 bytes sent represent the canID and tableID - while (Serial.available() == 0) { } - tableID = Serial.read(); //Not currently used for anything - - receiveCalibration(tableID); //Receive new values and store in memory - writeCalibration(); //Store received values in EEPROM - - break; - - case 'Z': //Totally non-standard testing function. Will be removed once calibration testing is completed. This function takes 1.5kb of program space! :S - digitalWrite(pinInjector1, HIGH); - digitalWrite(pinInjector2, HIGH); - delay(20); - digitalWrite(pinInjector1, LOW); - digitalWrite(pinInjector2, LOW); - return; - Serial.println(F("Coolant")); - for (int x = 0; x < CALIBRATION_TABLE_SIZE; x++) - { - Serial.print(x); - Serial.print(", "); - Serial.println(cltCalibrationTable[x]); - } - Serial.println(F("Inlet temp")); - for (int x = 0; x < CALIBRATION_TABLE_SIZE; x++) - { - Serial.print(x); - Serial.print(", "); - Serial.println(iatCalibrationTable[x]); - } - Serial.println(F("O2")); - for (int x = 0; x < CALIBRATION_TABLE_SIZE; x++) - { - Serial.print(x); - Serial.print(", "); - Serial.println(o2CalibrationTable[x]); - } - Serial.println(F("WUE")); - for (int x = 0; x < 10; x++) - { - Serial.print(configPage2.wueBins[x]); - Serial.print(", "); - Serial.println(configPage1.wueValues[x]); - } - Serial.flush(); - break; - - case 'T': //Send 256 tooth log entries to Tuner Studios tooth logger - sendToothLog(false); //Sends tooth log values as ints - break; - - case 'r': //Send 256 tooth log entries to a terminal emulator - sendToothLog(true); //Sends tooth log values as chars - break; - - - - case '?': - Serial.println - (F( - "\n" - "===Command Help===\n\n" - "All commands are single character and are concatenated with their parameters \n" - "without spaces. Some parameters are binary and cannot be entered through this \n" - "prompt by conventional means. \n" - "Syntax: +++\n\n" - "===List of Commands===\n\n" - "A - Displays 31 bytes of currentStatus values in binary (live data)\n" - "B - Burn current map and configPage values to eeprom\n" - "C - Test COM port. Used by Tunerstudio to see whether an ECU is on a given serial \n" - " port. Returns a binary number.\n" - "L - Displays map page (aka table) or configPage values. Use P to change page (not \n" - " every page is a map)\n" - "N - Print new line.\n" - "P - Set current page. Syntax: P+\n" - "R - Same as A command\n" - "S - Display signature number\n" - "Q - Same as S command\n" - "V - Display map or configPage values in binary\n" - "W - Set one byte in map or configPage. Expects binary parameters. \n" - " Syntax: W++\n" - "t - Set calibration values. Expects binary parameters. Table index is either 0, \n" - " 1, or 2. Syntax: t++++\n" - "Z - Display calibration values\n" - "T - Displays 256 tooth log entries in binary\n" - "r - Displays 256 tooth log entries\n" - "? - Displays this help page" - )); - - break; - - default: - break; - } -} - -/* -This function returns the current values of a fixed group of variables -*/ -void sendValues(int length) -{ - byte packetSize = 35; - byte response[packetSize]; - - if(requestCount == 0) { currentStatus.secl = 0; } - requestCount++; - - 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) - response[1] = currentStatus.squirt; //Squirt Bitfield - response[2] = currentStatus.engine; //Engine Status Bitfield - response[3] = (byte)(divu100(currentStatus.dwell)); //Dwell in ms * 10 - response[4] = (byte)(currentStatus.MAP >> 1); //map value is divided by 2 - response[5] = (byte)(currentStatus.IAT + CALIBRATION_TEMPERATURE_OFFSET); //mat - response[6] = (byte)(currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET); //Coolant ADC - response[7] = currentStatus.tpsADC; //TPS (Raw 0-255) - response[8] = currentStatus.battery10; //battery voltage - response[9] = currentStatus.O2; //O2 - response[10] = currentStatus.egoCorrection; //Exhaust gas correction (%) - response[11] = currentStatus.iatCorrection; //Air temperature Correction (%) - response[12] = currentStatus.wueCorrection; //Warmup enrichment (%) - response[13] = lowByte(currentStatus.RPM); //rpm HB - response[14] = highByte(currentStatus.RPM); //rpm LB - response[15] = currentStatus.TAEamount; //acceleration enrichment (%) - response[16] = 0x00; //Barometer correction (%) - response[17] = currentStatus.corrections; //Total GammaE (%) - response[18] = currentStatus.VE; //Current VE 1 (%) - response[19] = currentStatus.afrTarget; - response[20] = (byte)(currentStatus.PW / 100); //Pulsewidth 1 multiplied by 10 in ms. Have to convert from uS to mS. - response[21] = currentStatus.tpsDOT; //TPS DOT - response[22] = currentStatus.advance; - response[23] = currentStatus.TPS; // TPS (0% to 100%) - //Need to split the int loopsPerSecond value into 2 bytes - response[24] = lowByte(currentStatus.loopsPerSecond); - response[25] = highByte(currentStatus.loopsPerSecond); - - //The following can be used to show the amount of free memory - currentStatus.freeRAM = freeRam(); - response[26] = lowByte(currentStatus.freeRAM); //(byte)((currentStatus.loopsPerSecond >> 8) & 0xFF); - response[27] = highByte(currentStatus.freeRAM); - - response[28] = currentStatus.batCorrection; //Battery voltage correction (%) - response[29] = currentStatus.spark; //Spark related bitfield - response[30] = currentStatus.O2_2; //O2 - - //rpmDOT must be sent as a signed integer - response[31] = lowByte(currentStatus.rpmDOT); - response[32] = highByte(currentStatus.rpmDOT); - - response[33] = currentStatus.flex; //Flex sensor value (or 0 if not used) - response[34] = getNextError(); - -//cli(); - Serial.write(response, (size_t)packetSize); - //Serial.flush(); -//sei(); - return; -} - -void receiveValue(int offset, byte newValue) -{ - - void* pnt_configPage;//This only stores the address of the value that it's pointing to and not the max size - - switch (currentPage) - { - case veMapPage: - if (offset < 256) //New value is part of the fuel map - { - fuelTable.values[15 - offset / 16][offset % 16] = newValue; - return; - } - else - { - //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; - - case veSetPage: - 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; - - case ignMapPage: //Ignition settings page (Page 2) - if (offset < 256) //New value is part of the ignition map - { - ignitionTable.values[15 - offset / 16][offset % 16] = newValue; - return; - } - else - { - //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; - } - - case ignSetPage: - 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; - - case afrMapPage: //Air/Fuel ratio target settings page - if (offset < 256) //New value is part of the afr map - { - afrTable.values[15 - offset / 16][offset % 16] = newValue; - return; - } - else - { - //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; - } - - case afrSetPage: - 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; - - case iacPage: //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; - case boostvvtPage: //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; - } - else if (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; - } - else if (offset < 80) //New value is on the Y (TPS) axis of the boost table - { - boostTable.axisY[(7 - (offset - 72))] = int(newValue); - return; - } - else if (offset < 144) //New value is part of the vvt map - { - offset = offset - 80; - vvtTable.values[7 - offset / 8][offset % 8] = newValue; - return; - } - else if (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 - { - offset = offset - 152; - vvtTable.axisY[(7 - offset)] = int(newValue); - return; - } - default: - break; - } -} - -/* -sendPage() packs the data within the current page (As set with the 'P' command) -into a buffer and sends it. -Note that some translation of the data is required to lay it out in the way Megasqurit / TunerStudio expect it -useChar - If true, all values are send as chars, this is for the serial command line interface. TunerStudio expects data as raw values, so this must be set false in that case -*/ -void sendPage(bool useChar) -{ - void* pnt_configPage; - struct table3D currentTable; - byte currentTitleIndex = 0;// This corresponds to the count up to the first char of a string in pageTitles - - switch (currentPage) - { - case veMapPage: - { - currentTitleIndex = 0; - currentTable = fuelTable; - break; - } - - case veSetPage: - { - // currentTitleIndex = 27; - if (useChar) - { - // To Display Values from Config Page 1 - // When casting to the __FlashStringHelper type Serial.println uses the same subroutine as when using the F macro - Serial.println((const __FlashStringHelper *)&pageTitles[27]);//27 is the index to the first char in the second sting in pageTitles - // The following loop displays in human readable form of all byte values in config page 1 up to but not including the first array. - // incrementing void pointers is cumbersome. Thus we have "pnt_configPage = (byte *)pnt_configPage + 1" - for (pnt_configPage = &configPage1; pnt_configPage < &configPage1.wueValues[0]; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); - for (byte x = 10; x; x--)// The x between the ';' has the same representation as the "x != 0" test or comparision - { - Serial.print(configPage1.wueValues[10 - x]);// This displays the values horizantially on the screen - Serial.print(' '); - } - Serial.println(); - for (pnt_configPage = (byte *)&configPage1.wueValues[9] + 1; pnt_configPage < &configPage1.inj1Ang; pnt_configPage = (byte *)pnt_configPage + 1) { - 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 - } - // The following loop displays four unsigned ints - for (pnt_configPage = &configPage1.inj1Ang; pnt_configPage < (unsigned int *)&configPage1.inj4Ang + 1; pnt_configPage = (unsigned int *)pnt_configPage + 1) Serial.println(*((unsigned int *)pnt_configPage)); - // Following loop displays byte values between the unsigned ints - for (pnt_configPage = (unsigned int *)&configPage1.inj4Ang + 1; pnt_configPage < &configPage1.mapMax; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); - Serial.println(configPage1.mapMax); - // Following loop displays remaining byte values of the page - for (pnt_configPage = (unsigned int *)&configPage1.mapMax + 1; pnt_configPage < (byte *)&configPage1 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); - return; - } - else pnt_configPage = &configPage1; //Create a pointer to Page 1 in memory - break; - } - - case ignMapPage: - { - currentTitleIndex = 42;// the index to the first char of the third string in pageTitles - currentTable = ignitionTable; - break; - } - - case ignSetPage: - { - //currentTitleIndex = 56; - if (useChar) - { - //To Display Values from Config Page 2 - Serial.println((const __FlashStringHelper *)&pageTitles[56]); - Serial.println(configPage2.triggerAngle);// configPsge2.triggerAngle is an int so just display it without complication - // Following loop displays byte values after that first int up to but not including the first array in config page 2 - for (pnt_configPage = (int *)&configPage2 + 1; pnt_configPage < &configPage2.taeBins[0]; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); - for (byte y = 2; y; y--)// Displaying two equal sized arrays - { - byte * currentVar;// A placeholder for each array - if (y == 2) { - currentVar = configPage2.taeBins; - } - else { - currentVar = configPage2.taeValues; - } - - for (byte x = 4; x; x--) - { - Serial.print(currentVar[4 - x]); - Serial.print(' '); - } - Serial.println(); - } - for (byte x = 10; x ; x--) - { - Serial.print(configPage2.wueBins[10 - x]);//Displaying array horizontally across screen - Serial.print(' '); - } - Serial.println(); - Serial.println(configPage2.dwellLimit);// Little lonely byte stuck between two arrays. No complications just display it. - for (byte x = 6; x; x--) - { - Serial.print(configPage2.dwellCorrectionValues[6 - x]); - Serial.print(' '); - } - Serial.println(); - for (pnt_configPage = (byte *)&configPage2.dwellCorrectionValues[5] + 1; pnt_configPage < (byte *)&configPage2 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) - { - Serial.println(*((byte *)pnt_configPage));// Displaying remaining byte values of the page - } - return; - } - else pnt_configPage = &configPage2; //Create a pointer to Page 2 in memory - break; - } - - case afrMapPage: - { - currentTitleIndex = 71;//Array index to next string - currentTable = afrTable; - break; - } - - case afrSetPage: - { - //currentTitleIndex = 91; - if (useChar) - { - //To Display Values from Config Page 3 - Serial.println((const __FlashStringHelper *)&pageTitles[91]);//special typecasting to enable suroutine that the F macro uses - for (pnt_configPage = &configPage3; pnt_configPage < &configPage3.voltageCorrectionBins[0]; pnt_configPage = (byte *)pnt_configPage + 1) - { - Serial.println(*((byte *)pnt_configPage));// Displaying byte values of config page 3 up to but not including the first array - } - for (byte y = 2; y; y--)// Displaying two equally sized arrays that are next to each other - { - byte * currentVar; - if (y == 2) { currentVar = configPage3.voltageCorrectionBins; } - else { currentVar = configPage3.injVoltageCorrectionValues; } - - for (byte x = 6; x; x--) - { - Serial.print(currentVar[6 - x]); - Serial.print(' '); - } - Serial.println(); - } - for (byte y = 2; y; y--)// and again - { - byte* currentVar; - if (y == 2) currentVar = configPage3.airDenBins; - else currentVar = configPage3.airDenRates; - for (byte x = 9; x; x--) - { - Serial.print(currentVar[9 - x]); - Serial.print(' '); - } - Serial.println(); - } - // Following loop displays the remaining byte values of the page - for (pnt_configPage = (byte *)&configPage3.airDenRates[8] + 1; pnt_configPage < (byte *)&configPage3 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) - { - Serial.println(*((byte *)pnt_configPage)); - } - return; - } - else pnt_configPage = &configPage3; //Create a pointer to Page 3 in memory - break; - } - - case iacPage: - { - //currentTitleIndex = 106; - //To Display Values from Config Page 4 - if (useChar) - { - Serial.println((const __FlashStringHelper *)&pageTitles[106]);// F macro hack - for (byte y = 4; y; y--)// Display four equally sized arrays - { - byte * currentVar; - switch (y) - { - case 1: currentVar = configPage4.iacBins; break; - case 2: currentVar = configPage4.iacOLPWMVal; break; - case 3: currentVar = configPage4.iacOLStepVal; break; - case 4: currentVar = configPage4.iacCLValues; break; - default: break; - } - for (byte x = 10; x; x--) - { - Serial.print(currentVar[10 - x]); - Serial.print(' '); - } - Serial.println(); - } - for (byte y = 3; y; y--)// Three equally sized arrays - { - byte * currentVar; - switch (y) - { - case 1: currentVar = configPage4.iacCrankBins; break; - case 2: currentVar = configPage4.iacCrankDuty; break; - case 3: currentVar = configPage4.iacCrankSteps; break; - default: break; - } - for (byte x = 4; x; x--) - { - Serial.print(currentVar[4 - x]); - Serial.print(' '); - } - Serial.println(); - } - // Following loop is for remaining byte value of page - for (pnt_configPage = (byte *)&configPage4.iacCrankBins[3] + 1; pnt_configPage < (byte *)&configPage4 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); - return; - } - else pnt_configPage = &configPage4; //Create a pointer to Page 4 in memory - break; - } - - case boostvvtPage: - { - if(useChar) - { - currentTable = boostTable; - currentTitleIndex = 121; - } - else - { - //Need to perform a translation of the values[MAP/TPS][RPM] into the MS expected format - byte response[160]; //Bit hacky, but the size is: (8x8 + 8 + 8) + (8x8 + 8 + 8) = 160 - - //Boost table - for (int x = 0; x < 64; x++) { response[x] = boostTable.values[7 - x / 8][x % 8]; } - for (int x = 64; x < 72; x++) { response[x] = byte(boostTable.axisX[(x - 64)] / 100); } - for (int y = 72; y < 80; y++) { response[y] = byte(boostTable.axisY[7 - (y - 72)]); } - //VVT table - for (int x = 0; x < 64; x++) { response[x + 80] = vvtTable.values[7 - x / 8][x % 8]; } - for (int x = 64; x < 72; x++) { response[x + 80] = byte(vvtTable.axisX[(x - 64)] / 100); } - for (int y = 72; y < 80; y++) { response[y + 80] = byte(vvtTable.axisY[7 - (y - 72)]); } - Serial.write((byte *)&response, sizeof(response)); - return; - } - break; - } - default: - { - Serial.println(F("\nPage has not been implemented yet. Change to another page.")); - return; - break; - } - } - if (isMap) - { - if (useChar) - { - do //This is a do while loop that kicks in for the boostvvtPage - { - const char spaceChar = ' '; - /*while(pageTitles[currentTitleIndex]) - { - Serial.print(pageTitles[currentTitleIndex]); - currentTitleIndex++; - }*/ - Serial.println((const __FlashStringHelper *)&pageTitles[currentTitleIndex]);// F macro hack - Serial.println(); - for (int y = 0; y < currentTable.ySize; y++) - { - byte axisY = byte(currentTable.axisY[y]); - if (axisY < 100) - { - Serial.write(spaceChar); - if (axisY < 10) - { - Serial.write(spaceChar); - } - } - Serial.print(axisY);// Vertical Bins - Serial.write(spaceChar); - for (int x = 0; x < currentTable.xSize; x++) - { - byte value = currentTable.values[y][x]; - if (value < 100) - { - Serial.write(spaceChar); - if (value < 10) - { - Serial.write(spaceChar); - } - } - Serial.print(value); - Serial.write(spaceChar); - } - Serial.println(); - } - Serial.print(F(" ")); - for (int x = 0; x < currentTable.xSize; x++)// Horizontal bins - { - byte axisX = byte(currentTable.axisX[x] / 100); - if (axisX < 100) - { - Serial.write(spaceChar); - if (axisX < 10) - { - Serial.write(spaceChar); - } - } - Serial.print(axisX); - Serial.write(spaceChar); - } - Serial.println(); - if(currentTitleIndex == 121) //Check to see if on boostTable - { - currentTitleIndex = 132; //Change over to vvtTable mid display - currentTable = vvtTable; - } - else currentTitleIndex = 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 - byte response[map_page_size]; - - for (int x = 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 (int x = 256; x < 272; x++) { response[x] = byte(currentTable.axisX[(x - 256)] / 100); } //RPM Bins for VE table (Need to be dvidied by 100) - for (int y = 272; y < 288; y++) { response[y] = byte(currentTable.axisY[15 - (y - 272)]); } //MAP or TPS bins for VE table - Serial.write((byte *)&response, sizeof(response)); - } - } - else - { - /*if(useChar) - { - while(pageTitles[currentTitleIndex]) - { - Serial.print(pageTitles[currentTitleIndex]); - currentTitleIndex++; - } - Serial.println(); - for(byte x=0;x 255) { - tempValue = 255; // Cap the maximum value to prevent overflow when converting to byte - } - if (tempValue < 0) { - tempValue = 0; - } - - pnt_TargetTable[(x / 2)] = (byte)tempValue; - int y = EEPROM_CALIBRATION_O2 + counter; - - every2nd = false; - analogWrite(13, (counter % 50) ); - counter++; - } - else { - every2nd = true; - } - - } - -} - -/* -Send 256 tooth log entries - * if useChar is true, the values are sent as chars to be printed out by a terminal emulator - * if useChar is false, the values are sent as a 2 byte integer which is readable by TunerStudios tooth logger -*/ -void sendToothLog(bool useChar) -{ - //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 - } - unsigned int tempToothHistory[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 - memcpy( (void*)tempToothHistory, (void*)toothHistory, sizeof(tempToothHistory) ); - toothHistoryIndex = 0; //Reset the history index - - //Loop only needs to go to half the buffer size - if (useChar) - { - for (int x = 0; x < TOOTH_LOG_SIZE; x++) - { - Serial.println(tempToothHistory[x]); - } - } - else - { - for (int x = 0; x < TOOTH_LOG_SIZE; x++) - { - Serial.write(highByte(tempToothHistory[x])); - Serial.write(lowByte(tempToothHistory[x])); - } - BIT_CLEAR(currentStatus.squirt, BIT_SQUIRT_TOOTHLOG1READY); - } -} - -void testComm() -{ - Serial.write(1); - return; -} +/* +Speeduino - Simple engine management for the Arduino Mega 2560 platform +Copyright (C) Josh Stewart +A full copy of the license may be found in the projects root directory +*/ + +/* +This is called when a command is received over serial from TunerStudio / Megatune +It parses the command and calls the relevant function +A detailed description of each call can be found at: http://www.msextra.com/doc/ms1extra/COM_RS232.htm +*/ +//#include "comms.h" +//#include "globals.h" +//#include "storage.h" + +void command() +{ + switch (Serial.read()) + { + case 'A': // send 22 bytes of realtime values + sendValues(22); + break; + + case 'B': // Burn current values to eeprom + writeConfig(); + break; + + 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 + break; + + case 'Q': // send code version + Serial.print("speeduino 201609-dev"); + break; + + case 'V': // send VE table and constants in binary + sendPage(false); + break; + + case 'W': // receive new VE obr constant at 'W'++ + int offset; + while (Serial.available() == 0) { } + + if (isMap) + { + byte offset1, offset2; + offset1 = Serial.read(); + while (Serial.available() == 0) { } + offset2 = Serial.read(); + offset = word(offset2, offset1); + } + else + { + offset = Serial.read(); + } + while (Serial.available() == 0) { } + + receiveValue(offset, Serial.read()); + break; + + case 't': // receive new Calibration info. Command structure: "t", . This is an MS2/Extra command, NOT part of MS1 spec + byte tableID; + //byte canID; + + //The first 2 bytes sent represent the canID and tableID + while (Serial.available() == 0) { } + tableID = Serial.read(); //Not currently used for anything + + receiveCalibration(tableID); //Receive new values and store in memory + writeCalibration(); //Store received values in EEPROM + + break; + + case 'Z': //Totally non-standard testing function. Will be removed once calibration testing is completed. This function takes 1.5kb of program space! :S + digitalWrite(pinInjector1, HIGH); + digitalWrite(pinInjector2, HIGH); + delay(20); + digitalWrite(pinInjector1, LOW); + digitalWrite(pinInjector2, LOW); + return; + Serial.println(F("Coolant")); + for (int x = 0; x < CALIBRATION_TABLE_SIZE; x++) + { + Serial.print(x); + Serial.print(", "); + Serial.println(cltCalibrationTable[x]); + } + Serial.println(F("Inlet temp")); + for (int x = 0; x < CALIBRATION_TABLE_SIZE; x++) + { + Serial.print(x); + Serial.print(", "); + Serial.println(iatCalibrationTable[x]); + } + Serial.println(F("O2")); + for (int x = 0; x < CALIBRATION_TABLE_SIZE; x++) + { + Serial.print(x); + Serial.print(", "); + Serial.println(o2CalibrationTable[x]); + } + Serial.println(F("WUE")); + for (int x = 0; x < 10; x++) + { + Serial.print(configPage2.wueBins[x]); + Serial.print(", "); + Serial.println(configPage1.wueValues[x]); + } + Serial.flush(); + break; + + case 'T': //Send 256 tooth log entries to Tuner Studios tooth logger + sendToothLog(false); //Sends tooth log values as ints + break; + + case 'r': //Send 256 tooth log entries to a terminal emulator + sendToothLog(true); //Sends tooth log values as chars + break; + + + + case '?': + Serial.println + (F( + "\n" + "===Command Help===\n\n" + "All commands are single character and are concatenated with their parameters \n" + "without spaces. Some parameters are binary and cannot be entered through this \n" + "prompt by conventional means. \n" + "Syntax: +++\n\n" + "===List of Commands===\n\n" + "A - Displays 31 bytes of currentStatus values in binary (live data)\n" + "B - Burn current map and configPage values to eeprom\n" + "C - Test COM port. Used by Tunerstudio to see whether an ECU is on a given serial \n" + " port. Returns a binary number.\n" + "L - Displays map page (aka table) or configPage values. Use P to change page (not \n" + " every page is a map)\n" + "N - Print new line.\n" + "P - Set current page. Syntax: P+\n" + "R - Same as A command\n" + "S - Display signature number\n" + "Q - Same as S command\n" + "V - Display map or configPage values in binary\n" + "W - Set one byte in map or configPage. Expects binary parameters. \n" + " Syntax: W++\n" + "t - Set calibration values. Expects binary parameters. Table index is either 0, \n" + " 1, or 2. Syntax: t++++\n" + "Z - Display calibration values\n" + "T - Displays 256 tooth log entries in binary\n" + "r - Displays 256 tooth log entries\n" + "? - Displays this help page" + )); + + break; + + default: + break; + } +} + +/* +This function returns the current values of a fixed group of variables +*/ +void sendValues(int length) +{ + byte packetSize = 35; + byte response[packetSize]; + + if(requestCount == 0) { currentStatus.secl = 0; } + requestCount++; + + 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) + response[1] = currentStatus.squirt; //Squirt Bitfield + response[2] = currentStatus.engine; //Engine Status Bitfield + response[3] = (byte)(divu100(currentStatus.dwell)); //Dwell in ms * 10 + response[4] = (byte)(currentStatus.MAP >> 1); //map value is divided by 2 + response[5] = (byte)(currentStatus.IAT + CALIBRATION_TEMPERATURE_OFFSET); //mat + response[6] = (byte)(currentStatus.coolant + CALIBRATION_TEMPERATURE_OFFSET); //Coolant ADC + response[7] = currentStatus.tpsADC; //TPS (Raw 0-255) + response[8] = currentStatus.battery10; //battery voltage + response[9] = currentStatus.O2; //O2 + response[10] = currentStatus.egoCorrection; //Exhaust gas correction (%) + response[11] = currentStatus.iatCorrection; //Air temperature Correction (%) + response[12] = currentStatus.wueCorrection; //Warmup enrichment (%) + response[13] = lowByte(currentStatus.RPM); //rpm HB + response[14] = highByte(currentStatus.RPM); //rpm LB + response[15] = currentStatus.TAEamount; //acceleration enrichment (%) + response[16] = 0x00; //Barometer correction (%) + response[17] = currentStatus.corrections; //Total GammaE (%) + response[18] = currentStatus.VE; //Current VE 1 (%) + response[19] = currentStatus.afrTarget; + response[20] = (byte)(currentStatus.PW / 100); //Pulsewidth 1 multiplied by 10 in ms. Have to convert from uS to mS. + response[21] = currentStatus.tpsDOT; //TPS DOT + response[22] = currentStatus.advance; + response[23] = currentStatus.TPS; // TPS (0% to 100%) + //Need to split the int loopsPerSecond value into 2 bytes + response[24] = lowByte(currentStatus.loopsPerSecond); + response[25] = highByte(currentStatus.loopsPerSecond); + + //The following can be used to show the amount of free memory + currentStatus.freeRAM = freeRam(); + response[26] = lowByte(currentStatus.freeRAM); //(byte)((currentStatus.loopsPerSecond >> 8) & 0xFF); + response[27] = highByte(currentStatus.freeRAM); + + response[28] = currentStatus.batCorrection; //Battery voltage correction (%) + response[29] = currentStatus.spark; //Spark related bitfield + response[30] = currentStatus.O2_2; //O2 + + //rpmDOT must be sent as a signed integer + response[31] = lowByte(currentStatus.rpmDOT); + response[32] = highByte(currentStatus.rpmDOT); + + response[33] = currentStatus.flex; //Flex sensor value (or 0 if not used) + response[34] = getNextError(); + +//cli(); + Serial.write(response, (size_t)packetSize); + //Serial.flush(); +//sei(); + return; +} + +void receiveValue(int offset, byte newValue) +{ + + void* pnt_configPage;//This only stores the address of the value that it's pointing to and not the max size + + switch (currentPage) + { + case veMapPage: + if (offset < 256) //New value is part of the fuel map + { + fuelTable.values[15 - offset / 16][offset % 16] = newValue; + return; + } + else + { + //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; + + case veSetPage: + 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; + + case ignMapPage: //Ignition settings page (Page 2) + if (offset < 256) //New value is part of the ignition map + { + ignitionTable.values[15 - offset / 16][offset % 16] = newValue; + return; + } + else + { + //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; + } + + case ignSetPage: + 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; + + case afrMapPage: //Air/Fuel ratio target settings page + if (offset < 256) //New value is part of the afr map + { + afrTable.values[15 - offset / 16][offset % 16] = newValue; + return; + } + else + { + //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; + } + + case afrSetPage: + 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; + + case iacPage: //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; + case boostvvtPage: //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; + } + else if (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; + } + else if (offset < 80) //New value is on the Y (TPS) axis of the boost table + { + boostTable.axisY[(7 - (offset - 72))] = int(newValue); + return; + } + else if (offset < 144) //New value is part of the vvt map + { + offset = offset - 80; + vvtTable.values[7 - offset / 8][offset % 8] = newValue; + return; + } + else if (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 + { + offset = offset - 152; + vvtTable.axisY[(7 - offset)] = int(newValue); + return; + } + default: + break; + } +} + +/* +sendPage() packs the data within the current page (As set with the 'P' command) +into a buffer and sends it. +Note that some translation of the data is required to lay it out in the way Megasqurit / TunerStudio expect it +useChar - If true, all values are send as chars, this is for the serial command line interface. TunerStudio expects data as raw values, so this must be set false in that case +*/ +void sendPage(bool useChar) +{ + void* pnt_configPage; + struct table3D currentTable; + byte currentTitleIndex = 0;// This corresponds to the count up to the first char of a string in pageTitles + + switch (currentPage) + { + case veMapPage: + { + currentTitleIndex = 0; + currentTable = fuelTable; + break; + } + + case veSetPage: + { + // currentTitleIndex = 27; + if (useChar) + { + // To Display Values from Config Page 1 + // When casting to the __FlashStringHelper type Serial.println uses the same subroutine as when using the F macro + Serial.println((const __FlashStringHelper *)&pageTitles[27]);//27 is the index to the first char in the second sting in pageTitles + // The following loop displays in human readable form of all byte values in config page 1 up to but not including the first array. + // incrementing void pointers is cumbersome. Thus we have "pnt_configPage = (byte *)pnt_configPage + 1" + for (pnt_configPage = &configPage1; pnt_configPage < &configPage1.wueValues[0]; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); + for (byte x = 10; x; x--)// The x between the ';' has the same representation as the "x != 0" test or comparision + { + Serial.print(configPage1.wueValues[10 - x]);// This displays the values horizantially on the screen + Serial.print(' '); + } + Serial.println(); + for (pnt_configPage = (byte *)&configPage1.wueValues[9] + 1; pnt_configPage < &configPage1.inj1Ang; pnt_configPage = (byte *)pnt_configPage + 1) { + 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 + } + // The following loop displays four unsigned ints + for (pnt_configPage = &configPage1.inj1Ang; pnt_configPage < (unsigned int *)&configPage1.inj4Ang + 1; pnt_configPage = (unsigned int *)pnt_configPage + 1) Serial.println(*((unsigned int *)pnt_configPage)); + // Following loop displays byte values between the unsigned ints + for (pnt_configPage = (unsigned int *)&configPage1.inj4Ang + 1; pnt_configPage < &configPage1.mapMax; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); + Serial.println(configPage1.mapMax); + // Following loop displays remaining byte values of the page + for (pnt_configPage = (unsigned int *)&configPage1.mapMax + 1; pnt_configPage < (byte *)&configPage1 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); + return; + } + else pnt_configPage = &configPage1; //Create a pointer to Page 1 in memory + break; + } + + case ignMapPage: + { + currentTitleIndex = 42;// the index to the first char of the third string in pageTitles + currentTable = ignitionTable; + break; + } + + case ignSetPage: + { + //currentTitleIndex = 56; + if (useChar) + { + //To Display Values from Config Page 2 + Serial.println((const __FlashStringHelper *)&pageTitles[56]); + Serial.println(configPage2.triggerAngle);// configPsge2.triggerAngle is an int so just display it without complication + // Following loop displays byte values after that first int up to but not including the first array in config page 2 + for (pnt_configPage = (int *)&configPage2 + 1; pnt_configPage < &configPage2.taeBins[0]; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); + for (byte y = 2; y; y--)// Displaying two equal sized arrays + { + byte * currentVar;// A placeholder for each array + if (y == 2) { + currentVar = configPage2.taeBins; + } + else { + currentVar = configPage2.taeValues; + } + + for (byte x = 4; x; x--) + { + Serial.print(currentVar[4 - x]); + Serial.print(' '); + } + Serial.println(); + } + for (byte x = 10; x ; x--) + { + Serial.print(configPage2.wueBins[10 - x]);//Displaying array horizontally across screen + Serial.print(' '); + } + Serial.println(); + Serial.println(configPage2.dwellLimit);// Little lonely byte stuck between two arrays. No complications just display it. + for (byte x = 6; x; x--) + { + Serial.print(configPage2.dwellCorrectionValues[6 - x]); + Serial.print(' '); + } + Serial.println(); + for (pnt_configPage = (byte *)&configPage2.dwellCorrectionValues[5] + 1; pnt_configPage < (byte *)&configPage2 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) + { + Serial.println(*((byte *)pnt_configPage));// Displaying remaining byte values of the page + } + return; + } + else pnt_configPage = &configPage2; //Create a pointer to Page 2 in memory + break; + } + + case afrMapPage: + { + currentTitleIndex = 71;//Array index to next string + currentTable = afrTable; + break; + } + + case afrSetPage: + { + //currentTitleIndex = 91; + if (useChar) + { + //To Display Values from Config Page 3 + Serial.println((const __FlashStringHelper *)&pageTitles[91]);//special typecasting to enable suroutine that the F macro uses + for (pnt_configPage = &configPage3; pnt_configPage < &configPage3.voltageCorrectionBins[0]; pnt_configPage = (byte *)pnt_configPage + 1) + { + Serial.println(*((byte *)pnt_configPage));// Displaying byte values of config page 3 up to but not including the first array + } + for (byte y = 2; y; y--)// Displaying two equally sized arrays that are next to each other + { + byte * currentVar; + if (y == 2) { currentVar = configPage3.voltageCorrectionBins; } + else { currentVar = configPage3.injVoltageCorrectionValues; } + + for (byte x = 6; x; x--) + { + Serial.print(currentVar[6 - x]); + Serial.print(' '); + } + Serial.println(); + } + for (byte y = 2; y; y--)// and again + { + byte* currentVar; + if (y == 2) currentVar = configPage3.airDenBins; + else currentVar = configPage3.airDenRates; + for (byte x = 9; x; x--) + { + Serial.print(currentVar[9 - x]); + Serial.print(' '); + } + Serial.println(); + } + // Following loop displays the remaining byte values of the page + for (pnt_configPage = (byte *)&configPage3.airDenRates[8] + 1; pnt_configPage < (byte *)&configPage3 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) + { + Serial.println(*((byte *)pnt_configPage)); + } + return; + } + else pnt_configPage = &configPage3; //Create a pointer to Page 3 in memory + break; + } + + case iacPage: + { + //currentTitleIndex = 106; + //To Display Values from Config Page 4 + if (useChar) + { + Serial.println((const __FlashStringHelper *)&pageTitles[106]);// F macro hack + for (byte y = 4; y; y--)// Display four equally sized arrays + { + byte * currentVar; + switch (y) + { + case 1: currentVar = configPage4.iacBins; break; + case 2: currentVar = configPage4.iacOLPWMVal; break; + case 3: currentVar = configPage4.iacOLStepVal; break; + case 4: currentVar = configPage4.iacCLValues; break; + default: break; + } + for (byte x = 10; x; x--) + { + Serial.print(currentVar[10 - x]); + Serial.print(' '); + } + Serial.println(); + } + for (byte y = 3; y; y--)// Three equally sized arrays + { + byte * currentVar; + switch (y) + { + case 1: currentVar = configPage4.iacCrankBins; break; + case 2: currentVar = configPage4.iacCrankDuty; break; + case 3: currentVar = configPage4.iacCrankSteps; break; + default: break; + } + for (byte x = 4; x; x--) + { + Serial.print(currentVar[4 - x]); + Serial.print(' '); + } + Serial.println(); + } + // Following loop is for remaining byte value of page + for (pnt_configPage = (byte *)&configPage4.iacCrankBins[3] + 1; pnt_configPage < (byte *)&configPage4 + page_size; pnt_configPage = (byte *)pnt_configPage + 1) Serial.println(*((byte *)pnt_configPage)); + return; + } + else pnt_configPage = &configPage4; //Create a pointer to Page 4 in memory + break; + } + + case boostvvtPage: + { + if(useChar) + { + currentTable = boostTable; + currentTitleIndex = 121; + } + else + { + //Need to perform a translation of the values[MAP/TPS][RPM] into the MS expected format + byte response[160]; //Bit hacky, but the size is: (8x8 + 8 + 8) + (8x8 + 8 + 8) = 160 + + //Boost table + for (int x = 0; x < 64; x++) { response[x] = boostTable.values[7 - x / 8][x % 8]; } + for (int x = 64; x < 72; x++) { response[x] = byte(boostTable.axisX[(x - 64)] / 100); } + for (int y = 72; y < 80; y++) { response[y] = byte(boostTable.axisY[7 - (y - 72)]); } + //VVT table + for (int x = 0; x < 64; x++) { response[x + 80] = vvtTable.values[7 - x / 8][x % 8]; } + for (int x = 64; x < 72; x++) { response[x + 80] = byte(vvtTable.axisX[(x - 64)] / 100); } + for (int y = 72; y < 80; y++) { response[y + 80] = byte(vvtTable.axisY[7 - (y - 72)]); } + Serial.write((byte *)&response, sizeof(response)); + return; + } + break; + } + default: + { + Serial.println(F("\nPage has not been implemented yet. Change to another page.")); + return; + break; + } + } + if (isMap) + { + if (useChar) + { + do //This is a do while loop that kicks in for the boostvvtPage + { + const char spaceChar = ' '; + /*while(pageTitles[currentTitleIndex]) + { + Serial.print(pageTitles[currentTitleIndex]); + currentTitleIndex++; + }*/ + Serial.println((const __FlashStringHelper *)&pageTitles[currentTitleIndex]);// F macro hack + Serial.println(); + for (int y = 0; y < currentTable.ySize; y++) + { + byte axisY = byte(currentTable.axisY[y]); + if (axisY < 100) + { + Serial.write(spaceChar); + if (axisY < 10) + { + Serial.write(spaceChar); + } + } + Serial.print(axisY);// Vertical Bins + Serial.write(spaceChar); + for (int x = 0; x < currentTable.xSize; x++) + { + byte value = currentTable.values[y][x]; + if (value < 100) + { + Serial.write(spaceChar); + if (value < 10) + { + Serial.write(spaceChar); + } + } + Serial.print(value); + Serial.write(spaceChar); + } + Serial.println(); + } + Serial.print(F(" ")); + for (int x = 0; x < currentTable.xSize; x++)// Horizontal bins + { + byte axisX = byte(currentTable.axisX[x] / 100); + if (axisX < 100) + { + Serial.write(spaceChar); + if (axisX < 10) + { + Serial.write(spaceChar); + } + } + Serial.print(axisX); + Serial.write(spaceChar); + } + Serial.println(); + if(currentTitleIndex == 121) //Check to see if on boostTable + { + currentTitleIndex = 132; //Change over to vvtTable mid display + currentTable = vvtTable; + } + else currentTitleIndex = 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 + byte response[map_page_size]; + + for (int x = 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 (int x = 256; x < 272; x++) { response[x] = byte(currentTable.axisX[(x - 256)] / 100); } //RPM Bins for VE table (Need to be dvidied by 100) + for (int y = 272; y < 288; y++) { response[y] = byte(currentTable.axisY[15 - (y - 272)]); } //MAP or TPS bins for VE table + Serial.write((byte *)&response, sizeof(response)); + } + } + else + { + /*if(useChar) + { + while(pageTitles[currentTitleIndex]) + { + Serial.print(pageTitles[currentTitleIndex]); + currentTitleIndex++; + } + Serial.println(); + for(byte x=0;x 255) { + tempValue = 255; // Cap the maximum value to prevent overflow when converting to byte + } + if (tempValue < 0) { + tempValue = 0; + } + + pnt_TargetTable[(x / 2)] = (byte)tempValue; + int y = EEPROM_CALIBRATION_O2 + counter; + + every2nd = false; + analogWrite(13, (counter % 50) ); + counter++; + } + else { + every2nd = true; + } + + } + +} + +/* +Send 256 tooth log entries + * if useChar is true, the values are sent as chars to be printed out by a terminal emulator + * if useChar is false, the values are sent as a 2 byte integer which is readable by TunerStudios tooth logger +*/ +void sendToothLog(bool useChar) +{ + //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 + } + unsigned int tempToothHistory[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 + memcpy( (void*)tempToothHistory, (void*)toothHistory, sizeof(tempToothHistory) ); + toothHistoryIndex = 0; //Reset the history index + + //Loop only needs to go to half the buffer size + if (useChar) + { + for (int x = 0; x < TOOTH_LOG_SIZE; x++) + { + Serial.println(tempToothHistory[x]); + } + } + else + { + for (int x = 0; x < TOOTH_LOG_SIZE; x++) + { + Serial.write(highByte(tempToothHistory[x])); + Serial.write(lowByte(tempToothHistory[x])); + } + BIT_CLEAR(currentStatus.squirt, BIT_SQUIRT_TOOTHLOG1READY); + } +} + +void testComm() +{ + Serial.write(1); + return; +}