diff --git a/reference/speeduino.ini b/reference/speeduino.ini index 8a3af150..44ce10b5 100644 --- a/reference/speeduino.ini +++ b/reference/speeduino.ini @@ -213,7 +213,8 @@ crc32CheckCommand = "d%2i", "d%2i", "d%2i", "d%2i", "d%2i", "d%2i", "d%2i", "d%2i", "d%2i", "d%2i", "d%2i", "d%2i", "d%2i", "d%2i" blockingFactor = 256 - tableBlockingFactor = 2048 + ;tableBlockingFactor = 2048 + tableBlockingFactor = 256 delayAfterPortOpen=1000 ;validateArrayBounds = true blockReadTimeout = 2000 @@ -221,6 +222,7 @@ interWriteDelay = 1 ;Ignored when tsWriteBlocks is on pageActivationDelay = 10 restrictSquirtRelationship = false ;This requires TS 3.1 or above + ;messageEnvelopeFormat = msEnvelope_1.0 ;New and testing only ;New for TS 3.0.08ish upwards, define lists of standard I/O options diff --git a/speeduino/comms.cpp b/speeduino/comms.cpp index f0bb0e6b..9282d785 100644 --- a/speeduino/comms.cpp +++ b/speeduino/comms.cpp @@ -38,6 +38,7 @@ uint32_t inProgressCompositeTime; bool serialInProgress = false; bool toothLogSendInProgress = false; bool compositeLogSendInProgress = false; +bool legacySerial = false; /** Processes the incoming data on the serial buffer based on the command sent. Can be either data for a new command or a continuation of data for command that is already in progress: @@ -48,7 +49,7 @@ Comands are single byte (letter symbol) commands. */ void command() { - if (cmdPending == false) { currentCommand = Serial.read(); } + if ( (cmdPending == false) && (legacySerial == false) ) { currentCommand = Serial.read(); } switch (currentCommand) { @@ -100,7 +101,7 @@ void command() if (Serial.available() >= 2) { Serial.read(); //Ignore the first byte value, it's always 0 - uint32_t CRC32_val = calculateCRC32( Serial.read() ); + uint32_t CRC32_val = calculatePageCRC32( Serial.read() ); //Split the 4 bytes of the CRC32 value into individual bytes and send Serial.write( ((CRC32_val >> 24) & 255) ); diff --git a/speeduino/comms.h b/speeduino/comms.h index aeddfdbb..f6b73309 100644 --- a/speeduino/comms.h +++ b/speeduino/comms.h @@ -47,6 +47,7 @@ extern int valueOffset; /**< THe memory offset within a given page for a value t extern byte tsCanId; // current tscanid requested extern byte inProgressOffset; extern byte inProgressLength; +extern bool legacySerial; extern uint32_t inProgressCompositeTime; extern bool serialInProgress; extern bool toothLogSendInProgress; diff --git a/speeduino/globals.h b/speeduino/globals.h index b575aa19..72f5abc5 100644 --- a/speeduino/globals.h +++ b/speeduino/globals.h @@ -29,6 +29,7 @@ #include "table3d.h" #include #include "logger.h" +#include "src/FastCRC/FastCRC.h" #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega2561__) #define BOARD_MAX_DIGITAL_PINS 54 //digital pins +1 @@ -431,6 +432,8 @@ This is so we can use an unsigned byte (0-255) to represent temperature ranges f extern const char TSfirmwareVersion[] PROGMEM; extern const byte data_structure_version; //This identifies the data structure when reading / writing. Now in use: CURRENT_DATA_VERSION (migration on-the fly) ? +extern FastCRC32 CRC32; + extern struct table3d16RpmLoad fuelTable; //16x16 fuel map extern struct table3d16RpmLoad fuelTable2; //16x16 fuel map diff --git a/speeduino/globals.ino b/speeduino/globals.ino index 9585d3fb..2fb336da 100644 --- a/speeduino/globals.ino +++ b/speeduino/globals.ino @@ -6,6 +6,7 @@ const char TSfirmwareVersion[] PROGMEM = "Speeduino"; const byte data_structure_version = 2; //This identifies the data structure when reading / writing. (outdated ?) +FastCRC32 CRC32; struct table3d16RpmLoad fuelTable; ///< 16x16 fuel map struct table3d16RpmLoad fuelTable2; ///< 16x16 fuel map diff --git a/speeduino/newComms.cpp b/speeduino/newComms.cpp new file mode 100644 index 00000000..a9da5531 --- /dev/null +++ b/speeduino/newComms.cpp @@ -0,0 +1,906 @@ +/* +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 +*/ +/** @file + * Process Incoming and outgoing serial communications. + */ +#include "globals.h" +#include "newComms.h" +#include "cancomms.h" +#include "storage.h" +#include "maths.h" +#include "utilities.h" +#include "decoders.h" +#include "TS_CommandButtonHandler.h" +#include "errors.h" +#include "pages.h" +#include "page_crc.h" +#include "logger.h" +#include "comms.h" +#include "src/FastCRC/FastCRC.h" +#ifdef RTC_ENABLED + #include "rtc_common.h" +#endif + +uint16_t serialPayloadLength = 0; +bool serialReceivePending = false; /**< Whether or not a serial request has only been partially received. This occurs when a the length has been received in the serial buffer, but not all of the payload or CRC has yet been received. */ +uint16_t serialBytesReceived = 0; +uint32_t serialCRC = 0; +uint8_t serialPayload[257]; /**< Pointer to the serial payload buffer. */ + +/** Processes the incoming data on the serial buffer based on the command sent. +Can be either data for a new command or a continuation of data for command that is already in progress: +- cmdPending = If a command has started but is wairing on further data to complete +- chunkPending = Specifically for the new receive value method where TS will send a known number of contiguous bytes to be written to a table + +Comands are single byte (letter symbol) commands. +*/ +void parseSerial() +{ + + //Check for an existing legacy command in progress + if(cmdPending == true) + { + command(); + return; + } + + if (serialReceivePending == false) + { + serialBytesReceived = 0; //Reset the number of bytes received as we're starting a new command + + //New command received + //Need at least 2 bytes to read the length of the command + serialReceivePending = true; //Flag the serial receive as being in progress + byte lowByte = Serial.read(); + + //Check if the command is legacy using the call/response mechanism + if((lowByte >= 'A') && (lowByte <= 'z') ) + { + //Handle legacy cases here + serialReceivePending = false; //Make sure new serial handling does not interfere with legacy handling + legacySerial = true; + currentCommand = lowByte; + command(); + } + else + { + while(Serial.available() == 0) { } //Wait for the 2nd byte to be received (This will almost never happen) + + byte highByte = Serial.read(); + serialPayloadLength = word(lowByte, highByte); + serialBytesReceived = 2; + cmdPending = false; // Make sure legacy handling does not interfere with new serial handling + + //serialReceivePayload = (uint8_t *)malloc(serialPayloadLength); + } + } + + //If there is a serial receive in progress, read as much from the buffer as possible or until we receive all bytes + while( (Serial.available() > 0) && (serialReceivePending == true) ) + { + if (serialBytesReceived < (serialPayloadLength + SERIAL_LEN_SIZE) ) + { + serialPayload[(serialBytesReceived - SERIAL_LEN_SIZE)] = Serial.read(); + serialBytesReceived++; + } + else if (Serial.available() >= SERIAL_CRC_LENGTH) + { + uint32_t crc1 = Serial.read(); + uint32_t crc2 = Serial.read(); + uint32_t crc3 = Serial.read(); + uint32_t crc4 = Serial.read(); + serialCRC = (crc1<<24) | (crc2<<16) | (crc3<<8) | crc4; + + serialReceivePending = false; //The serial receive is now complete + + //Test the CRC + uint32_t receivedCRC = CRC32.crc32(serialPayload, serialPayloadLength); + //receivedCRC++; + if(serialCRC != receivedCRC) + { + //CRC Error. Need to send an error message + sendSerialReturnCode(SERIAL_RC_CRC_ERROR); + } + else + { + //CRC is correct. Process the command + processSerialCommand(); + } + //free(serialReceivePayload); //Finally free the memory from the payload buffer + } + } +} + +void sendSerialReturnCode(byte returnCode) +{ + Serial.write(0); + Serial.write(1); //Size is always 1 + + Serial.write(returnCode); + + //Calculate and send CRC + uint32_t CRC32_val = CRC32.crc32(&returnCode, 1); + Serial.write( ((CRC32_val >> 24) & 255) ); + Serial.write( ((CRC32_val >> 16) & 255) ); + Serial.write( ((CRC32_val >> 8) & 255) ); + Serial.write( (CRC32_val & 255) ); +} + +void sendSerialPayload(void *payload, byte payloadLength) +{ + //uint16_t totalPayloadLength = payloadLength + SERIAL_CRC_LENGTH; + uint16_t totalPayloadLength = payloadLength; + Serial.write(totalPayloadLength >> 8); + Serial.write(totalPayloadLength); + + //Need to handle serial buffer being full. This is just for testing + for(int i = 0; i < payloadLength; i++) + { + Serial.write(((uint8_t*)payload)[i]); + } + + //Calculate and send CRC + uint32_t CRC32_val = CRC32.crc32((uint8_t*)payload, payloadLength); + Serial.write( ((CRC32_val >> 24) & 255) ); + Serial.write( ((CRC32_val >> 16) & 255) ); + Serial.write( ((CRC32_val >> 8) & 255) ); + Serial.write( (CRC32_val & 255) ); +} + +void processSerialCommand() +{ + currentCommand = serialPayload[0]; + + switch (currentCommand) + { + /* + Should not happen with the new mode + case 'a': + cmdPending = true; + + if (Serial.available() >= 2) + { + Serial.read(); //Ignore the first value, it's always 0 + Serial.read(); //Ignore the second value, it's always 6 + sendValuesLegacy(); + cmdPending = false; + } + break; + */ + + case 'A': // send x bytes of realtime values + //sendValues(0, LOG_ENTRY_SIZE, 0x31, 0); //send values to serial0 + generateLiveValues(0, LOG_ENTRY_SIZE); + break; + + /* + Should not happen with the new mode + case 'B': // Burn current values to eeprom + writeAllConfig(); + break; + */ + + case 'b': // New EEPROM burn command to only burn a single page at a time + writeConfig(serialPayload[2]); //Read the table number and perform burn. Note that byte 1 in the array is unused + sendSerialReturnCode(SERIAL_RC_BURN_OK); + break; + + case 'C': // test communications. This is used by Tunerstudio to see whether there is an ECU on a given serial port + { + uint8_t tempPayload[] = {SERIAL_RC_OK, currentStatus.secl}; + sendSerialPayload(&tempPayload, 2); + break; + } + + /* + Should not happen with the new mode + case 'c': //Send the current loops/sec value + Serial.write(lowByte(currentStatus.loopsPerSecond)); + Serial.write(highByte(currentStatus.loopsPerSecond)); + break; + */ + + case 'E': // receive command button commands + { + byte cmdGroup = serialPayload[1]; + byte cmdValue = serialPayload[2]; + uint16_t cmdCombined = word(cmdGroup, cmdValue); + + if ( ((cmdCombined >= TS_CMD_INJ1_ON) && (cmdCombined <= TS_CMD_IGN8_50PC)) || (cmdCombined == TS_CMD_TEST_ENBL) || (cmdCombined == TS_CMD_TEST_DSBL) ) + { + //Hardware test buttons + if (currentStatus.RPM == 0) { TS_CommandButtonsHandler(cmdCombined); } + } + else if( (cmdCombined >= TS_CMD_VSS_60KMH) && (cmdCombined <= TS_CMD_VSS_RATIO6) ) + { + //VSS Calibration commands + TS_CommandButtonsHandler(cmdCombined); + } + else if( (cmdCombined >= TS_CMD_STM32_REBOOT) && (cmdCombined <= TS_CMD_STM32_BOOTLOADER) ) + { + //STM32 DFU mode button + TS_CommandButtonsHandler(cmdCombined); + } + sendSerialReturnCode(SERIAL_RC_OK); + break; + } + + case 'F': // send serial protocol version + { + byte serialVersion[] = {SERIAL_RC_OK, '0', '0', '2'}; + sendSerialPayload(&serialVersion, 4); + break; + } + + case 'H': //Start the tooth logger + currentStatus.toothLogEnabled = true; + currentStatus.compositeLogEnabled = false; //Safety first (Should never be required) + BIT_CLEAR(currentStatus.status1, BIT_STATUS1_TOOTHLOG1READY); + toothHistoryIndex = 0; + toothHistorySerialIndex = 0; + + //Disconnect the standard interrupt and add the logger version + detachInterrupt( digitalPinToInterrupt(pinTrigger) ); + attachInterrupt( digitalPinToInterrupt(pinTrigger), loggerPrimaryISR, CHANGE ); + + detachInterrupt( digitalPinToInterrupt(pinTrigger2) ); + attachInterrupt( digitalPinToInterrupt(pinTrigger2), loggerSecondaryISR, CHANGE ); + + sendSerialReturnCode(SERIAL_RC_OK); + break; + + case 'h': //Stop the tooth logger + currentStatus.toothLogEnabled = false; + + //Disconnect the logger interrupts and attach the normal ones + detachInterrupt( digitalPinToInterrupt(pinTrigger) ); + attachInterrupt( digitalPinToInterrupt(pinTrigger), triggerHandler, primaryTriggerEdge ); + + detachInterrupt( digitalPinToInterrupt(pinTrigger2) ); + attachInterrupt( digitalPinToInterrupt(pinTrigger2), triggerSecondaryHandler, secondaryTriggerEdge ); + sendSerialReturnCode(SERIAL_RC_OK); + break; + + case 'J': //Start the composite logger + currentStatus.compositeLogEnabled = true; + currentStatus.toothLogEnabled = false; //Safety first (Should never be required) + BIT_CLEAR(currentStatus.status1, BIT_STATUS1_TOOTHLOG1READY); + toothHistoryIndex = 0; + toothHistorySerialIndex = 0; + compositeLastToothTime = 0; + + //Disconnect the standard interrupt and add the logger version + detachInterrupt( digitalPinToInterrupt(pinTrigger) ); + attachInterrupt( digitalPinToInterrupt(pinTrigger), loggerPrimaryISR, CHANGE ); + + detachInterrupt( digitalPinToInterrupt(pinTrigger2) ); + attachInterrupt( digitalPinToInterrupt(pinTrigger2), loggerSecondaryISR, CHANGE ); + + sendSerialReturnCode(SERIAL_RC_OK); + break; + + case 'j': //Stop the composite logger + currentStatus.compositeLogEnabled = false; + + //Disconnect the logger interrupts and attach the normal ones + detachInterrupt( digitalPinToInterrupt(pinTrigger) ); + attachInterrupt( digitalPinToInterrupt(pinTrigger), triggerHandler, primaryTriggerEdge ); + + detachInterrupt( digitalPinToInterrupt(pinTrigger2) ); + attachInterrupt( digitalPinToInterrupt(pinTrigger2), triggerSecondaryHandler, secondaryTriggerEdge ); + sendSerialReturnCode(SERIAL_RC_OK); + break; + + case 'd': // Send a CRC32 hash of a given page + { + uint32_t CRC32_val = calculatePageCRC32( serialPayload[2] ); + uint8_t payloadCRC32[5]; + + //First byte is the flag + payloadCRC32[0] = SERIAL_RC_OK; + + //Split the 4 bytes of the CRC32 value into individual bytes and send + payloadCRC32[1] = ((CRC32_val >> 24) & 255); + payloadCRC32[2] = ((CRC32_val >> 16) & 255); + payloadCRC32[3] = ((CRC32_val >> 8) & 255); + payloadCRC32[4] = (CRC32_val & 255); + + sendSerialPayload( &payloadCRC32, 5); + + break; + } + + + /* + * New method for sending page values (MS command equivalent is 'r') + */ + case 'p': + { + //6 bytes required: + //2 - Page identifier + //2 - offset + //2 - Length + byte offset1, offset2, length1, length2; + int length; + byte tempPage; + + tempPage = serialPayload[2]; + offset1 = serialPayload[3]; + offset2 = serialPayload[4]; + valueOffset = word(offset2, offset1); + length1 = serialPayload[5]; + length2 = serialPayload[6]; + length = word(length2, length1); + + //Setup the transmit buffer + //serialTransmitPayload = (byte*) malloc(length + 1); + serialPayload[0] = SERIAL_RC_OK; + for(int i = 0; i < length; i++) + { + serialPayload[i+1] = getPageValue(tempPage, valueOffset + i); + } + sendSerialPayload(&serialPayload, (length + 1)); + //free(serialTransmitPayload); + break; + } + + case 'Q': // send code version + { + char productString[21] = { SERIAL_RC_OK, 's','p','e','e','d','u','i','n','o',' ','2','0','2','1','0','9','-','d','e','v'} ; //Note no null terminator in array and statu variable at the start + sendSerialPayload(&productString, 21); + break; + } + + case 'r': //New format for the optimised OutputChannels + { + + uint8_t cmd = serialPayload[2]; + uint8_t offset1 = serialPayload[3]; + uint8_t offset2 = serialPayload[4]; + uint16_t offset = word(offset2, offset1); + uint8_t length1 = serialPayload[5]; + uint8_t length2 = serialPayload[6]; + uint16_t length = word(length2, length1); + + if(cmd == 0x30) //Send output channels command 0x30 is 48dec + { + generateLiveValues(offset, length); + sendSerialPayload(&serialPayload, (length + 1)); + } +#ifdef RTC_ENABLED + else if(cmd == SD_RTC_PAGE) //Request to read SD card RTC + { + Serial.write(rtc_getSecond()); //Seconds + Serial.write(rtc_getMinute()); //Minutes + Serial.write(rtc_getHour()); //Hours + Serial.write(rtc_getDOW()); //Day of Week + Serial.write(rtc_getDay()); //Date + Serial.write(rtc_getMonth()); //Month + Serial.write(lowByte(rtc_getYear())); //Year - NOTE 2 bytes + Serial.write(highByte(rtc_getYear())); //Year + + } + else if(cmd == SD_READWRITE_PAGE) //Request SD card extended parameters + { + //SD read commands use the offset and length fields to indicate the request type + if((offset == SD_READ_STAT_OFFSET) && (length == SD_READ_STAT_LENGTH)) + { + //Read the status of the SD card + + //Serial.write(0); + + + //Serial.write(currentStatus.TS_SD_Status); + Serial.write((uint8_t)5); + Serial.write((uint8_t)0); + + //All other values are 2 bytes + Serial.write((uint8_t)2); //Sector size + Serial.write((uint8_t)0); //Sector size + + //Max blocks (4 bytes) + Serial.write((uint8_t)0); + Serial.write((uint8_t)0x20); //1gb dummy card + Serial.write((uint8_t)0); + Serial.write((uint8_t)0); + + //Max roots (Number of files) + Serial.write((uint8_t)0); + Serial.write((uint8_t)1); + + //Dir Start (4 bytes) + Serial.write((uint8_t)0); //Dir start lower 2 bytes + Serial.write((uint8_t)0); //Dir start lower 2 bytes + Serial.write((uint8_t)0); //Dir start lower 2 bytes + Serial.write((uint8_t)0); //Dir start lower 2 bytes + + //Unkown purpose for last 2 bytes + Serial.write((uint8_t)0); //Dir start lower 2 bytes + Serial.write((uint8_t)0); //Dir start lower 2 bytes + + /* + Serial.write(lowByte(23)); + Serial.write(highByte(23)); + + byte packet[17]; + packet[0] = 0; + packet[1] = 5; + packet[2] = 0; + + packet[3] = 2; + packet[4] = 0; + + packet[5] = 0; + packet[6] = 0x20; + packet[7] = 0; + packet[8] = 0; + + packet[9] = 0; + packet[10] = 1; + + packet[11] = 0; + packet[12] = 0; + packet[13] = 0; + packet[14] = 0; + + packet[15] = 0; + packet[16] = 0; + + Serial.write(packet, 17); + uint32_t CRC32_val = CRC32.crc32((byte *)packet, sizeof(packet) );; + + //Split the 4 bytes of the CRC32 value into individual bytes and send + Serial.write( ((CRC32_val >> 24) & 255) ); + Serial.write( ((CRC32_val >> 16) & 255) ); + Serial.write( ((CRC32_val >> 8) & 255) ); + Serial.write( (CRC32_val & 255) ); + */ + + } + //else if(length == 0x202) + { + //File info + } + } + else if(cmd == 0x14) + { + //Fetch data from file + } +#endif + else + { + //No other r/ commands should be called + } + cmdPending = false; + + break; + } + + case 'S': // send code version + { + byte productString[] = { SERIAL_RC_OK, 'S', 'p', 'e', 'e', 'd', 'u', 'i', 'n', 'o', ' ', '2', '0', '2', '1', '.', '0', '9', '-', 'd', 'e', 'v'}; + //productString = F("Speeduino 2021.09-dev"); + sendSerialPayload(&productString, sizeof(productString)); + currentStatus.secl = 0; //This is required in TS3 due to its stricter timings + break; + } + + + case 'T': //Send 256 tooth log entries to Tuner Studios tooth logger + //6 bytes required: + //2 - Page identifier + //2 - offset + //2 - Length + cmdPending = true; + if(Serial.available() >= 6) + { + Serial.read(); // First byte of the page identifier can be ignored. It's always 0 + Serial.read(); // First byte of the page identifier can be ignored. It's always 0 + Serial.read(); // First byte of the page identifier can be ignored. It's always 0 + Serial.read(); // First byte of the page identifier can be ignored. It's always 0 + Serial.read(); // First byte of the page identifier can be ignored. It's always 0 + Serial.read(); // First byte of the page identifier can be ignored. It's always 0 + + if(currentStatus.toothLogEnabled == true) { generateToothLog(0); } //Sends tooth log values as ints + else if (currentStatus.compositeLogEnabled == true) { generateCompositeLog(0); } + + cmdPending = false; + } + + + + break; + + case 't': // receive new Calibration info. Command structure: "t", . + 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 + + receiveCalibrationNew(tableID); //Receive new values and store in memory + writeCalibration(); //Store received values in EEPROM + + break; + + case 'U': //User wants to reset the Arduino (probably for FW update) + if (resetControl != RESET_CONTROL_DISABLED) + { + #ifndef SMALL_FLASH_MODE + if (!cmdPending) { Serial.println(F("Comms halted. Next byte will reset the Arduino.")); } + #endif + + while (Serial.available() == 0) { } + digitalWrite(pinResetControl, LOW); + } + else + { + #ifndef SMALL_FLASH_MODE + if (!cmdPending) { Serial.println(F("Reset control is currently disabled.")); } + #endif + } + break; + + case 'V': // send VE table and constants in binary + sendPage(); + break; + + + case 'M': + { + //New write command + //7 bytes required: + //2 - Page identifier + //2 - offset + //2 - Length + //1 - 1st New value + byte offset1, offset2, length1, length2; + + uint8_t currentPage = serialPayload[2]; + offset1 = serialPayload[3]; + offset2 = serialPayload[4]; + uint16_t valueOffset = word(offset2, offset1); + length1 = serialPayload[5]; + length2 = serialPayload[6]; + uint16_t chunkSize = word(length2, length1); + + for(uint16_t i = 0; i < chunkSize; i++) + { + setPageValue(currentPage, (valueOffset + i), serialPayload[7 + i]); + } + sendSerialReturnCode(SERIAL_RC_OK); + break; + } + + case 'w': + if(Serial.available() >= 7) + { + byte offset1, offset2, length1, length2; + + Serial.read(); // First byte of the page identifier can be ignored. It's always 0 + currentPage = Serial.read(); + //currentPage = 1; + offset1 = Serial.read(); + offset2 = Serial.read(); + valueOffset = word(offset2, offset1); + length1 = Serial.read(); + length2 = Serial.read(); + chunkSize = word(length2, length1); + } +#ifdef RTC_ENABLED + if(currentPage == SD_READWRITE_PAGE) + { + cmdPending = false; + + //Reserved for the SD card settings. Appears to be hardcoded into TS. Flush the final byte in the buffer as its not used for now + Serial.read(); + if((valueOffset == SD_WRITE_DO_OFFSET) && (chunkSize == SD_WRITE_DO_LENGTH)) + { + /* + SD DO command. Single byte of data where the commands are: + 0 Reset + 1 Reset + 2 Stop logging + 3 Start logging + 4 Load status variable + 5 Init SD card + */ + Serial.read(); + } + else if((valueOffset == SD_WRITE_SEC_OFFSET) && (chunkSize == SD_WRITE_SEC_LENGTH)) + { + //SD write sector command + } + else if((valueOffset == SD_ERASEFILE_OFFSET) && (chunkSize == SD_ERASEFILE_LENGTH)) + { + //Erase file command + //First 4 bytes are the log number in ASCII + /* + char log1 = Serial.read(); + char log2 = Serial.read(); + char log3 = Serial.read(); + char log4 = Serial.read(); + */ + + //Next 2 bytes are the directory block no + Serial.read(); + Serial.read(); + } + else if((valueOffset == SD_SPD_TEST_OFFSET) && (chunkSize == SD_SPD_TEST_LENGTH)) + { + //Perform a speed test on the SD card + //First 4 bytes are the sector number to write to + Serial.read(); + Serial.read(); + Serial.read(); + Serial.read(); + + //Last 4 bytes are the number of sectors to test + Serial.read(); + Serial.read(); + Serial.read(); + Serial.read(); + } + } + else if(currentPage == SD_RTC_PAGE) + { + cmdPending = false; + //Used for setting RTC settings + if((valueOffset == SD_RTC_WRITE_OFFSET) && (chunkSize == SD_RTC_WRITE_LENGTH)) + { + //Set the RTC date/time + //Need to ensure there are 9 more bytes with the new values + while(Serial.available() < 9) {} //Terrible hack, but RTC values should not be set with the engine running anyway + byte second = Serial.read(); + byte minute = Serial.read(); + byte hour = Serial.read(); + //byte dow = Serial.read(); + Serial.read(); // This is the day of week value, which is currently unused + byte day = Serial.read(); + byte month = Serial.read(); + uint16_t year = Serial.read(); + year = word(Serial.read(), year); + Serial.read(); //Final byte is unused (Always has value 0x5a) + rtc_setTime(second, minute, hour, day, month, year); + } + } +#endif + break; + + default: + break; + } +} + +/** Send a status record back to tuning/logging SW. + * This will "live" information from @ref currentStatus struct. + * @param offset - Start field number + * @param packetLength - Length of actual message (after possible ack/confirm headers) + * E.g. tuning sw command 'A' (Send all values) will send data from field number 0, LOG_ENTRY_SIZE fields. + */ +//void sendValues(int packetlength, byte portNum) +void generateLiveValues(uint16_t offset, uint16_t packetLength) +{ + if(requestCount == 0) { currentStatus.secl = 0; } + requestCount++; + + currentStatus.spark ^= (-currentStatus.hasSync ^ currentStatus.spark) & (1U << BIT_SPARK_SYNC); //Set the sync bit of the Spark variable to match the hasSync variable + + serialPayload[0] = SERIAL_RC_OK; + for(byte x=0; x> 24); + Serial.write(toothHistory[toothHistorySerialIndex] >> 16); + Serial.write(toothHistory[toothHistorySerialIndex] >> 8); + Serial.write(toothHistory[toothHistorySerialIndex]); + + if(toothHistorySerialIndex == (TOOTH_LOG_BUFFER-1)) { toothHistorySerialIndex = 0; } + else { toothHistorySerialIndex++; } + } + BIT_CLEAR(currentStatus.status1, BIT_STATUS1_TOOTHLOG1READY); + cmdPending = false; + toothLogSendInProgress = false; + } + else + { + //TunerStudio has timed out, send a LOG of all 0s + for(int x = 0; x < (4*TOOTH_LOG_SIZE); x++) + { + Serial.write(static_cast(0x00)); //GCC9 fix + } + cmdPending = false; + } +} + +void generateCompositeLog(byte startOffset) +{ + if (BIT_CHECK(currentStatus.status1, BIT_STATUS1_TOOTHLOG1READY)) //Sanity check. Flagging system means this should always be true + { + if(startOffset == 0) { inProgressCompositeTime = 0; } + for (int x = startOffset; x < TOOTH_LOG_SIZE; x++) + { + //Check whether the tx buffer still has space + if(Serial.availableForWrite() < 4) + { + //tx buffer is full. Store the current state so it can be resumed later + inProgressOffset = x; + compositeLogSendInProgress = true; + return; + } + + inProgressCompositeTime += toothHistory[toothHistorySerialIndex]; //This combined runtime (in us) that the log was going for by this record) + + Serial.write(inProgressCompositeTime >> 24); + Serial.write(inProgressCompositeTime >> 16); + Serial.write(inProgressCompositeTime >> 8); + Serial.write(inProgressCompositeTime); + + Serial.write(compositeLogHistory[toothHistorySerialIndex]); //The status byte (Indicates the trigger edge, whether it was a pri/sec pulse, the sync status) + + if(toothHistorySerialIndex == (TOOTH_LOG_BUFFER-1)) { toothHistorySerialIndex = 0; } + else { toothHistorySerialIndex++; } + } + BIT_CLEAR(currentStatus.status1, BIT_STATUS1_TOOTHLOG1READY); + toothHistoryIndex = 0; + toothHistorySerialIndex = 0; + compositeLastToothTime = 0; + cmdPending = false; + compositeLogSendInProgress = false; + inProgressCompositeTime = 0; + } + else + { + //TunerStudio has timed out, send a LOG of all 0s + for(int x = 0; x < (5*TOOTH_LOG_SIZE); x++) + { + Serial.write(static_cast(0x00)); //GCC9 fix + } + cmdPending = false; + } +} \ No newline at end of file diff --git a/speeduino/newComms.h b/speeduino/newComms.h new file mode 100644 index 00000000..b14549e4 --- /dev/null +++ b/speeduino/newComms.h @@ -0,0 +1,70 @@ +/** \file comms.h + * @brief File for handling all serial requests + * @author Josh Stewart + * + * This file contains all the functions associated with serial comms. + * This includes sending of live data, sending/receiving current page data, sending CRC values of pages, receiving sensor calibration data etc + * + */ + +#ifndef NEW_COMMS_H +#define NEW_COMMS_H + +//Hardcoded TunerStudio addresses/commands for various SD/RTC commands +#define SD_READWRITE_PAGE 0x11 +#define SD_RTC_PAGE 0x07 +#define SD_READ_STAT_OFFSET 0x0000 +#define SD_READ_STAT_LENGTH 0x1000 +#define SD_READ_DIR_OFFSET 0x0100 +#define SD_READ_DIR_LENGTH 0x0200 +#define SD_READ_SEC_OFFSET 0x0200 +#define SD_READ_SEC_LENGTH 0x0400 +#define SD_READ_STRM_OFFSET 0x0400 +#define SD_READ_STRM_LENGTH 0x0100 +#define SD_WRITE_DO_OFFSET 0x0000 +#define SD_WRITE_DO_LENGTH 0x0001 +#define SD_WRITE_SEC_OFFSET 0x0300 +#define SD_WRITE_SEC_LENGTH 0x0402 +#define SD_ERASEFILE_OFFSET 0x0600 +#define SD_ERASEFILE_LENGTH 0x0600 +#define SD_SPD_TEST_OFFSET 0x0700 +#define SD_SPD_TEST_LENGTH 0x0400 +#define SD_RTC_WRITE_OFFSET 0x7E02 +#define SD_RTC_WRITE_LENGTH 0x0900 +#define SD_RTC_READ_OFFSET 0x4D02 +#define SD_RTC_READ_LENGTH 0x0800 + +#define SERIAL_CRC_LENGTH 4 +#define SERIAL_LEN_SIZE 2 +#define SERIAL_OVERHEAD_SIZE (SERIAL_LEN_SIZE + SERIAL_CRC_LENGTH) //The overhead for each serial command is 6 bytes. 2 bytes for the length and 4 bytes for the CRC + +//Serial return codes +#define SERIAL_RC_OK 0x00 +#define SERIAL_RC_REALTIME 0x01 +#define SERIAL_RC_PAGE 0x02 + +#define SERIAL_RC_BURN_OK 0x04 + +#define SERIAL_RC_CRC_ERROR 0x82 + +extern uint16_t serialPayloadLength; +extern uint32_t serialCRC; +extern bool serialReceivePending; /**< Whether or not a serial request has only been partially received. This occurs when a the length has been received in the serial buffer, but not all of the payload or CRC has yet been received. */ +//extern uint8_t *serialPayload; /**< Pointer to the serial payload buffer. */ +extern uint8_t serialPayload[257]; /**< Pointer to the serial payload buffer. */ +extern uint16_t serialBytesReceived; /**< The number of bytes received in the serial buffer during the current command. */ +extern bool serialWriteInProgress; + +void parseSerial();//This is the heart of the Command Line Interpeter. All that needed to be done was to make it human readable. +void processSerialCommand(); +void sendSerialReturnCode(byte returnCode); +void sendSerialPayload(void*, byte payloadLength); + +void generateLiveValues(uint16_t, uint16_t); +void saveConfig(); +void receiveCalibrationNew(byte); +void generateToothLog(uint8_t); +void commandButtons(int16_t); +void generateCompositeLog(uint8_t); + +#endif // COMMS_H diff --git a/speeduino/page_crc.cpp b/speeduino/page_crc.cpp index 44400931..104c0eeb 100644 --- a/speeduino/page_crc.cpp +++ b/speeduino/page_crc.cpp @@ -1,8 +1,7 @@ +#include "globals.h" #include "page_crc.h" #include "pages.h" -#include "src/FastCRC/FastCRC.h" - -static FastCRC32 CRC32; +//#include "src/FastCRC/FastCRC.h" typedef uint32_t (FastCRC32::*pCrcCalc)(const uint8_t *, const uint16_t, bool); @@ -81,7 +80,7 @@ static inline uint32_t compute_crc(page_iterator_t &entity, pCrcCalc calcFunc) } } -uint32_t calculateCRC32(byte pageNum) +uint32_t calculatePageCRC32(byte pageNum) { page_iterator_t entity = page_begin(pageNum); // Initial CRC calc diff --git a/speeduino/page_crc.h b/speeduino/page_crc.h index 67745214..71a133c7 100644 --- a/speeduino/page_crc.h +++ b/speeduino/page_crc.h @@ -4,4 +4,4 @@ /* * Calculates and returns the CRC32 value of a given page of memory */ -uint32_t calculateCRC32(byte pageNum /**< [in] The page number to compute CRC for. */); \ No newline at end of file +uint32_t calculatePageCRC32(byte pageNum /**< [in] The page number to compute CRC for. */); \ No newline at end of file diff --git a/speeduino/sensors.ino b/speeduino/sensors.ino index f933c397..96bd26bf 100644 --- a/speeduino/sensors.ino +++ b/speeduino/sensors.ino @@ -514,9 +514,9 @@ void readBaro() /* * The highest sea-level pressure on Earth occurs in Siberia, where the Siberian High often attains a sea-level pressure above 105 kPa; * with record highs close to 108.5 kPa. - * The lowest measurable sea-level pressure is found at the centers of tropical cyclones and tornadoes, with a record low of 87 kPa; + * The lowest possible baro reading is based on an altitude of 3500m above sea level. */ - if ((currentStatus.MAP >= BARO_MIN) && (currentStatus.MAP <= BARO_MAX)) //Check if engine isn't running + if ((currentStatus.MAP >= BARO_MIN) && (currentStatus.MAP <= BARO_MAX)) //Safety check to ensure the baro reading is within the physical limits { currentStatus.baro = currentStatus.MAP; storeLastBaro(currentStatus.baro); diff --git a/speeduino/speeduino.ino b/speeduino/speeduino.ino index 730ad125..1ee770ed 100644 --- a/speeduino/speeduino.ino +++ b/speeduino/speeduino.ino @@ -25,6 +25,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "speeduino.h" #include "scheduler.h" #include "comms.h" +#include "newComms.h" #include "cancomms.h" #include "maths.h" #include "corrections.h" @@ -126,7 +127,9 @@ void loop() } //Check for any new requets from serial. - if ( (Serial.available()) > 0) { command(); } + //if ( (Serial.available()) > 0) { command(); } + if ( (Serial.available()) > 0) { parseSerial(); } + else if(cmdPending == true) { //This is a special case just for the tooth and composite loggers