2022-02-27 15:28:06 -08:00
/*
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 "comms_legacy.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 "table3d_axis_io.h"
# ifdef RTC_ENABLED
# include "rtc_common.h"
# endif
2023-02-20 17:55:54 -08:00
static byte currentPage = 1 ; //Not the same as the speeduino config page numbers
bool firstCommsRequest = true ; /**< The number of times the A command has been issued. This is used to track whether a reset has recently been performed on the controller */
static byte currentCommand ; /**< The serial command that is currently being processed. This is only useful when cmdPending=True */
static bool chunkPending = false ; /**< Whether or not the current chunk write is complete or not */
static uint16_t chunkComplete = 0 ; /**< The number of bytes in a chunk write that have been written so far */
static uint16_t chunkSize = 0 ; /**< The complete size of the requested chunk write */
static int valueOffset ; /**< The memory offset within a given page for a value to be read from or written to. Note that we cannot use 'offset' as a variable name, it is a reserved word for several teensy libraries */
byte logItemsTransmitted ;
2022-02-27 15:28:06 -08:00
byte inProgressLength ;
2023-02-20 17:55:54 -08:00
SerialStatus serialStatusFlag ;
2023-10-04 18:54:22 -07:00
SerialStatus serialSecondaryStatusFlag ;
2023-02-20 17:55:54 -08:00
static bool isMap ( void ) {
// Detecting if the current page is a table/map
return ( currentPage = = veMapPage ) | | ( currentPage = = ignMapPage ) | | ( currentPage = = afrMapPage ) | | ( currentPage = = fuelMap2Page ) | | ( currentPage = = ignMap2Page ) ;
}
2022-02-27 15:28:06 -08:00
/** 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 :
2022-04-10 17:49:58 -07:00
- cmdPending = If a command has started but is waiting on further data to complete
2022-02-27 15:28:06 -08:00
- chunkPending = Specifically for the new receive value method where TS will send a known number of contiguous bytes to be written to a table
2022-04-10 17:49:58 -07:00
Commands are single byte ( letter symbol ) commands .
2022-02-27 15:28:06 -08:00
*/
2022-11-05 15:43:29 -07:00
void legacySerialCommand ( void )
2022-02-27 15:28:06 -08:00
{
2023-02-20 17:55:54 -08:00
if ( serialStatusFlag = = SERIAL_INACTIVE ) { currentCommand = Serial . read ( ) ; }
2022-02-27 15:28:06 -08:00
switch ( currentCommand )
{
case ' a ' :
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_COMMAND_INPROGRESS_LEGACY ;
2022-02-27 15:28:06 -08:00
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 ( ) ;
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
}
break ;
case ' A ' : // send x bytes of realtime values
2023-10-05 04:24:13 -07:00
sendValues ( 0 , LOG_ENTRY_SIZE , 0x31 , Serial ) ; //send values to serial0
2023-10-04 18:54:22 -07:00
firstCommsRequest = false ;
2022-02-27 15:28:06 -08:00
break ;
2023-03-22 17:05:10 -07:00
case ' b ' : // New EEPROM burn command to only burn a single page at a time
2023-10-05 04:24:13 -07:00
legacySerialHandler ( currentCommand , Serial , serialStatusFlag ) ;
2022-02-27 15:28:06 -08:00
break ;
2023-03-22 17:05:10 -07:00
case ' B ' : // AS above but for the serial compatibility mode.
BIT_SET ( currentStatus . status4 , BIT_STATUS4_COMMS_COMPAT ) ; //Force the compat mode
2023-10-05 04:24:13 -07:00
legacySerialHandler ( currentCommand , Serial , serialStatusFlag ) ;
2022-02-27 15:28:06 -08:00
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 ' c ' : //Send the current loops/sec value
Serial . write ( lowByte ( currentStatus . loopsPerSecond ) ) ;
Serial . write ( highByte ( currentStatus . loopsPerSecond ) ) ;
break ;
case ' d ' : // Send a CRC32 hash of a given page
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_COMMAND_INPROGRESS_LEGACY ;
2022-02-27 15:28:06 -08:00
if ( Serial . available ( ) > = 2 )
{
Serial . read ( ) ; //Ignore the first byte value, it's always 0
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 ) ) ;
Serial . write ( ( ( CRC32_val > > 16 ) & 255 ) ) ;
Serial . write ( ( ( CRC32_val > > 8 ) & 255 ) ) ;
Serial . write ( ( CRC32_val & 255 ) ) ;
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
}
break ;
case ' E ' : // receive command button commands
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_COMMAND_INPROGRESS_LEGACY ;
2022-02-27 15:28:06 -08:00
if ( Serial . available ( ) > = 2 )
{
2023-02-20 17:55:54 -08:00
byte cmdGroup = ( byte ) Serial . read ( ) ;
( void ) TS_CommandButtonsHandler ( word ( cmdGroup , Serial . read ( ) ) ) ;
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
}
break ;
case ' F ' : // send serial protocol version
2023-04-25 20:45:22 -07:00
Serial . print ( F ( " 002 " ) ) ;
2022-02-27 15:28:06 -08:00
break ;
//The G/g commands are used for bulk reading and writing to the EEPROM directly. This is typically a non-user feature but will be incorporated into SpeedyLoader for anyone programming many boards at once
case ' G ' : // Dumps the EEPROM values to serial
//The format is 2 bytes for the overall EEPROM size, a comma and then a raw dump of the EEPROM values
Serial . write ( lowByte ( getEEPROMSize ( ) ) ) ;
Serial . write ( highByte ( getEEPROMSize ( ) ) ) ;
Serial . print ( ' , ' ) ;
for ( uint16_t x = 0 ; x < getEEPROMSize ( ) ; x + + )
{
Serial . write ( EEPROMReadRaw ( x ) ) ;
}
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
break ;
case ' g ' : // Receive a dump of raw EEPROM values from the user
{
2022-04-10 17:49:58 -07:00
//Format is similar to the above command. 2 bytes for the EEPROM size that is about to be transmitted, a comma and then a raw dump of the EEPROM values
2022-02-27 15:28:06 -08:00
while ( Serial . available ( ) < 3 ) { delay ( 1 ) ; }
uint16_t eepromSize = word ( Serial . read ( ) , Serial . read ( ) ) ;
if ( eepromSize ! = getEEPROMSize ( ) )
{
//Client is trying to send the wrong EEPROM size. Don't let it
Serial . println ( F ( " ERR; Incorrect EEPROM size " ) ) ;
break ;
}
else
{
for ( uint16_t x = 0 ; x < eepromSize ; x + + )
{
while ( Serial . available ( ) < 3 ) { delay ( 1 ) ; }
EEPROMWriteRaw ( x , Serial . read ( ) ) ;
}
}
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
break ;
}
case ' H ' : //Start the tooth logger
2023-02-20 17:55:54 -08:00
startToothLogger ( ) ;
2022-02-27 15:28:06 -08:00
Serial . write ( 1 ) ; //TS needs an acknowledgement that this was received. I don't know if this is the correct response, but it seems to work
break ;
case ' h ' : //Stop the tooth logger
2023-02-20 17:55:54 -08:00
stopToothLogger ( ) ;
2022-02-27 15:28:06 -08:00
break ;
case ' J ' : //Start the composite logger
2023-02-20 17:55:54 -08:00
startCompositeLogger ( ) ;
2022-02-27 15:28:06 -08:00
Serial . write ( 1 ) ; //TS needs an acknowledgement that this was received. I don't know if this is the correct response, but it seems to work
break ;
case ' j ' : //Stop the composite logger
2023-02-20 17:55:54 -08:00
stopCompositeLogger ( ) ;
2022-02-27 15:28:06 -08:00
break ;
case ' L ' : // List the contents of current page in human readable form
# ifndef SMALL_FLASH_MODE
sendPageASCII ( ) ;
# endif
break ;
case ' m ' : //Send the current free memory
currentStatus . freeRAM = freeRam ( ) ;
Serial . write ( lowByte ( currentStatus . freeRAM ) ) ;
Serial . write ( highByte ( currentStatus . freeRAM ) ) ;
break ;
case ' N ' : // Displays a new line. Like pushing enter in a text editor
Serial . println ( ) ;
break ;
2023-05-14 21:15:46 -07:00
case ' O ' : //Start the composite logger 2nd cam (teritary)
startCompositeLoggerTertiary ( ) ;
Serial . write ( 1 ) ; //TS needs an acknowledgement that this was received. I don't know if this is the correct response, but it seems to work
break ;
case ' o ' : //Stop the composite logger 2nd cam (tertiary)
stopCompositeLoggerTertiary ( ) ;
break ;
case ' X ' : //Start the composite logger 2nd cam (teritary)
startCompositeLoggerCams ( ) ;
Serial . write ( 1 ) ; //TS needs an acknowledgement that this was received. I don't know if this is the correct response, but it seems to work
break ;
case ' x ' : //Stop the composite logger 2nd cam (tertiary)
stopCompositeLoggerCams ( ) ;
break ;
2022-02-27 15:28:06 -08:00
case ' P ' : // set the current page
//This is a legacy function and is no longer used by TunerStudio. It is maintained for compatibility with other systems
//A 2nd byte of data is required after the 'P' specifying the new page number.
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_COMMAND_INPROGRESS_LEGACY ;
2022-02-27 15:28:06 -08:00
if ( Serial . available ( ) > 0 )
{
currentPage = Serial . read ( ) ;
2022-04-10 17:49:58 -07:00
//This converts the ASCII number char into binary. Note that this will break everything if there are ever more than 48 pages (48 = asci code for '0')
2022-02-27 15:28:06 -08:00
if ( ( currentPage > = ' 0 ' ) & & ( currentPage < = ' 9 ' ) ) // 0 - 9
{
currentPage - = 48 ;
}
else if ( ( currentPage > = ' a ' ) & & ( currentPage < = ' f ' ) ) // 10 - 15
{
currentPage - = 87 ;
}
else if ( ( currentPage > = ' A ' ) & & ( currentPage < = ' F ' ) )
{
currentPage - = 55 ;
}
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
}
break ;
/*
* New method for sending page values
*/
case ' p ' :
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_COMMAND_INPROGRESS_LEGACY ;
2022-02-27 15:28:06 -08:00
//6 bytes required:
//2 - Page identifier
//2 - offset
//2 - Length
if ( Serial . available ( ) > = 6 )
{
byte offset1 , offset2 , length1 , length2 ;
int length ;
byte tempPage ;
Serial . read ( ) ; // First byte of the page identifier can be ignored. It's always 0
tempPage = Serial . read ( ) ;
//currentPage = 1;
offset1 = Serial . read ( ) ;
offset2 = Serial . read ( ) ;
valueOffset = word ( offset2 , offset1 ) ;
length1 = Serial . read ( ) ;
length2 = Serial . read ( ) ;
length = word ( length2 , length1 ) ;
for ( int i = 0 ; i < length ; i + + )
{
Serial . write ( getPageValue ( tempPage , valueOffset + i ) ) ;
}
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
}
break ;
case ' Q ' : // send code version
2023-10-05 04:24:13 -07:00
legacySerialHandler ( currentCommand , Serial , serialStatusFlag ) ;
2022-02-27 15:28:06 -08:00
break ;
case ' r ' : //New format for the optimised OutputChannels
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_COMMAND_INPROGRESS_LEGACY ;
2022-02-27 15:28:06 -08:00
byte cmd ;
if ( Serial . available ( ) > = 6 )
{
2023-02-20 17:55:54 -08:00
Serial . read ( ) ; //Read the $tsCanId
2022-02-27 15:28:06 -08:00
cmd = Serial . read ( ) ; // read the command
uint16_t offset , length ;
byte tmp ;
tmp = Serial . read ( ) ;
offset = word ( Serial . read ( ) , tmp ) ;
tmp = Serial . read ( ) ;
length = word ( Serial . read ( ) , tmp ) ;
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
if ( cmd = = 0x30 ) //Send output channels command 0x30 is 48dec
{
2023-10-05 04:24:13 -07:00
sendValues ( offset , length , cmd , Serial ) ;
2022-02-27 15:28:06 -08:00
}
else
{
//No other r/ commands are supported in legacy mode
}
}
break ;
case ' S ' : // send code version
2023-10-05 04:24:13 -07:00
legacySerialHandler ( currentCommand , Serial , serialStatusFlag ) ; //Send the bootloader capabilities
2022-02-27 15:28:06 -08:00
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
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_COMMAND_INPROGRESS_LEGACY ;
2022-02-27 15:28:06 -08:00
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
2022-02-28 20:27:46 -08:00
if ( currentStatus . toothLogEnabled = = true ) { sendToothLog_legacy ( 0 ) ; } //Sends tooth log values as ints
2023-05-14 21:15:46 -07:00
else if ( currentStatus . compositeTriggerUsed > 0 ) { sendCompositeLog_legacy ( 0 ) ; }
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
}
break ;
case ' t ' : // receive new Calibration info. Command structure: "t", <tble_idx> <data array>.
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 ' U ' : //User wants to reset the Arduino (probably for FW update)
if ( resetControl ! = RESET_CONTROL_DISABLED )
{
# ifndef SMALL_FLASH_MODE
2023-02-20 17:55:54 -08:00
if ( serialStatusFlag = = SERIAL_INACTIVE ) { Serial . println ( F ( " Comms halted. Next byte will reset the Arduino. " ) ) ; }
2022-02-27 15:28:06 -08:00
# endif
while ( Serial . available ( ) = = 0 ) { }
digitalWrite ( pinResetControl , LOW ) ;
}
else
{
# ifndef SMALL_FLASH_MODE
2023-02-20 17:55:54 -08:00
if ( serialStatusFlag = = SERIAL_INACTIVE ) { Serial . println ( F ( " Reset control is currently disabled. " ) ) ; }
2022-02-27 15:28:06 -08:00
# endif
}
break ;
case ' V ' : // send VE table and constants in binary
sendPage ( ) ;
break ;
case ' W ' : // receive new VE obr constant at 'W'+<offset>+<newbyte>
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_COMMAND_INPROGRESS_LEGACY ;
2022-02-27 15:28:06 -08:00
2023-02-20 17:55:54 -08:00
if ( isMap ( ) )
2022-02-27 15:28:06 -08:00
{
if ( Serial . available ( ) > = 3 ) // 1 additional byte is required on the MAP pages which are larger than 255 bytes
{
byte offset1 , offset2 ;
offset1 = Serial . read ( ) ;
offset2 = Serial . read ( ) ;
valueOffset = word ( offset2 , offset1 ) ;
setPageValue ( currentPage , valueOffset , Serial . read ( ) ) ;
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
}
}
else
{
if ( Serial . available ( ) > = 2 )
{
valueOffset = Serial . read ( ) ;
setPageValue ( currentPage , valueOffset , Serial . read ( ) ) ;
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
}
}
break ;
case ' M ' :
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_COMMAND_INPROGRESS_LEGACY ;
2022-02-27 15:28:06 -08:00
if ( chunkPending = = false )
{
//This means it's a new request
//7 bytes required:
//2 - Page identifier
//2 - offset
//2 - Length
//1 - 1st New value
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 ) ;
//Regular page data
chunkPending = true ;
chunkComplete = 0 ;
}
}
//This CANNOT be an else of the above if statement as chunkPending gets set to true above
if ( chunkPending = = true )
{
while ( ( Serial . available ( ) > 0 ) & & ( chunkComplete < chunkSize ) )
{
setPageValue ( currentPage , ( valueOffset + chunkComplete ) , Serial . read ( ) ) ;
chunkComplete + + ;
}
2023-02-20 17:55:54 -08:00
if ( chunkComplete > = chunkSize ) { serialStatusFlag = SERIAL_INACTIVE ; chunkPending = false ; }
2022-02-27 15:28:06 -08:00
}
break ;
case ' w ' :
//No w commands are supported in legacy mode. This should never be called
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 ) ;
}
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
# ifndef SMALL_FLASH_MODE
Serial . println ( F ( " Coolant " ) ) ;
for ( int x = 0 ; x < 32 ; x + + )
{
Serial . print ( cltCalibration_bins [ x ] ) ;
Serial . print ( " , " ) ;
Serial . println ( cltCalibration_values [ x ] ) ;
}
Serial . println ( F ( " Inlet temp " ) ) ;
for ( int x = 0 ; x < 32 ; x + + )
{
Serial . print ( iatCalibration_bins [ x ] ) ;
Serial . print ( " , " ) ;
Serial . println ( iatCalibration_values [ x ] ) ;
}
Serial . println ( F ( " O2 " ) ) ;
for ( int x = 0 ; x < 32 ; x + + )
{
Serial . print ( o2Calibration_bins [ x ] ) ;
Serial . print ( " , " ) ;
Serial . println ( o2Calibration_values [ x ] ) ;
}
Serial . println ( F ( " WUE " ) ) ;
for ( int x = 0 ; x < 10 ; x + + )
{
Serial . print ( configPage4 . wueBins [ x ] ) ;
Serial . print ( F ( " , " ) ) ;
Serial . println ( configPage2 . wueValues [ x ] ) ;
}
Serial . flush ( ) ;
# endif
break ;
case ' z ' : //Send 256 tooth log entries to a terminal emulator
2022-02-28 20:27:46 -08:00
sendToothLog_legacy ( 0 ) ; //Sends tooth log values as chars
2022-02-27 15:28:06 -08:00
break ;
case ' ` ' : //Custom 16u2 firmware is making its presence known
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_COMMAND_INPROGRESS_LEGACY ;
2022-02-27 15:28:06 -08:00
if ( Serial . available ( ) > = 1 ) {
configPage4 . bootloaderCaps = Serial . read ( ) ;
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
}
break ;
case ' ? ' :
# ifndef SMALL_FLASH_MODE
Serial . println
( F (
" \n "
" ===Command Help=== \n \n "
" All commands are single character and are concatenated with their parameters \n "
" without spaces. "
" Syntax: <command>+<parameter1>+<parameter2>+<parameterN> \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 "
" N - Print new line. \n "
" P - Set current page. Syntax: P+<pageNumber> \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+<offset>+<newbyte> \n "
" t - Set calibration values. Expects binary parameters. Table index is either 0, \n "
" 1, or 2. Syntax: t+<tble_idx>+<newValue1>+<newValue2>+<newValueN> \n "
" Z - Display calibration values \n "
" T - Displays 256 tooth log entries in binary \n "
" r - Displays 256 tooth log entries \n "
" U - Prepare for firmware update. The next byte received will cause the Arduino to reset. \n "
" ? - Displays this help page "
) ) ;
# endif
break ;
default :
2023-10-04 18:54:22 -07:00
//Serial.println(F("Err: Unknown cmd"));
//while(Serial.available() && Serial.peek()!='A') { Serial.read(); }
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
break ;
}
}
2023-10-05 04:24:13 -07:00
void legacySerialHandler ( byte cmd , Stream & targetPort , SerialStatus & targetStatusFlag )
{
switch ( cmd )
{
case ' b ' : // New EEPROM burn command to only burn a single page at a time
targetStatusFlag = SERIAL_COMMAND_INPROGRESS_LEGACY ;
if ( targetPort . available ( ) > = 2 )
{
targetPort . read ( ) ; //Ignore the first table value, it's always 0
writeConfig ( targetPort . read ( ) ) ;
targetStatusFlag = SERIAL_INACTIVE ;
}
break ;
case ' B ' : // AS above but for the serial compatibility mode.
targetStatusFlag = SERIAL_COMMAND_INPROGRESS_LEGACY ;
if ( targetPort . available ( ) > = 2 )
{
targetPort . read ( ) ; //Ignore the first table value, it's always 0
writeConfig ( targetPort . read ( ) ) ;
targetStatusFlag = SERIAL_INACTIVE ;
}
break ;
case ' d ' :
targetStatusFlag = SERIAL_COMMAND_INPROGRESS_LEGACY ;
if ( targetPort . available ( ) > = 2 )
{
targetPort . read ( ) ; //Ignore the first byte value, it's always 0
uint32_t CRC32_val = calculatePageCRC32 ( targetPort . read ( ) ) ;
//Split the 4 bytes of the CRC32 value into individual bytes and send
targetPort . write ( ( ( CRC32_val > > 24 ) & 255 ) ) ;
targetPort . write ( ( ( CRC32_val > > 16 ) & 255 ) ) ;
targetPort . write ( ( ( CRC32_val > > 8 ) & 255 ) ) ;
targetPort . write ( ( CRC32_val & 255 ) ) ;
targetStatusFlag = SERIAL_INACTIVE ;
}
break ;
case ' p ' :
targetStatusFlag = SERIAL_COMMAND_INPROGRESS_LEGACY ;
//6 bytes required:
//2 - Page identifier
//2 - offset
//2 - Length
if ( targetPort . available ( ) > = 6 )
{
byte offset1 , offset2 , length1 , length2 ;
int length ;
byte tempPage ;
targetPort . read ( ) ; // First byte of the page identifier can be ignored. It's always 0
tempPage = targetPort . read ( ) ;
//currentPage = 1;
offset1 = targetPort . read ( ) ;
offset2 = targetPort . read ( ) ;
valueOffset = word ( offset2 , offset1 ) ;
length1 = targetPort . read ( ) ;
length2 = targetPort . read ( ) ;
length = word ( length2 , length1 ) ;
for ( int i = 0 ; i < length ; i + + )
{
targetPort . write ( getPageValue ( tempPage , valueOffset + i ) ) ;
}
targetStatusFlag = SERIAL_INACTIVE ;
}
break ;
case ' Q ' : // send code version
targetPort . print ( F ( " speeduino 202306-dev " ) ) ;
break ;
case ' r ' : //New format for the optimised OutputChannels
targetStatusFlag = SERIAL_COMMAND_INPROGRESS_LEGACY ;
byte cmd ;
if ( targetPort . available ( ) > = 6 )
{
targetPort . read ( ) ; //Read the $tsCanId
cmd = targetPort . read ( ) ; // read the command
uint16_t offset , length ;
byte tmp ;
tmp = targetPort . read ( ) ;
offset = word ( targetPort . read ( ) , tmp ) ;
tmp = targetPort . read ( ) ;
length = word ( targetPort . read ( ) , tmp ) ;
targetStatusFlag = SERIAL_INACTIVE ;
if ( cmd = = 0x30 ) //Send output channels command 0x30 is 48dec
{
sendValues ( offset , length , cmd , targetPort ) ;
}
else
{
//No other r/ commands are supported in legacy mode
}
}
break ;
case ' S ' : // send code version
targetPort . print ( F ( " Speeduino 2023.06-dev " ) ) ;
break ;
}
}
2022-02-27 15:28:06 -08:00
/** 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 )
* @ param cmd - ? ? ? - Will be used as some kind of ack on CANSerial
* @ param portNum - Port number ( 0 = Serial , 3 = CANSerial )
* E . g . tuning sw command ' A ' ( Send all values ) will send data from field number 0 , LOG_ENTRY_SIZE fields .
* @ return the current values of a fixed group of variables
*/
2023-10-05 04:24:13 -07:00
void sendValues ( uint16_t offset , uint16_t packetLength , byte cmd , Stream & targetPort )
2022-02-27 15:28:06 -08:00
{
2023-10-05 04:24:13 -07:00
if ( & targetPort = = & CANSerial )
2022-02-27 15:28:06 -08:00
{
2023-10-04 18:54:22 -07:00
serialSecondaryStatusFlag = SERIAL_TRANSMIT_INPROGRESS_LEGACY ;
2022-02-27 15:28:06 -08:00
//CAN serial
2023-10-05 04:24:13 -07:00
if ( ( configPage9 . secondarySerialProtocol = = SECONDARY_SERIAL_PROTO_GENERIC ) | | ( configPage9 . secondarySerialProtocol = = SECONDARY_SERIAL_PROTO_REALDASH ) )
{
# if defined(CANSerial_AVAILABLE)
if ( cmd = = 0x30 )
{
CANSerial . write ( " r " ) ; //confirm cmd type
CANSerial . write ( cmd ) ;
}
else if ( cmd = = 0x31 )
{
CANSerial . write ( " A " ) ; // confirm command type
}
else if ( cmd = = 0x32 )
{
CANSerial . write ( " n " ) ; // confirm command type
CANSerial . write ( cmd ) ; // send command type , 0x32 (dec50) is ascii '0'
CANSerial . write ( NEW_CAN_PACKET_SIZE ) ; // send the packet size the receiving device should expect.
}
# endif
}
2022-02-27 15:28:06 -08:00
}
else
{
2023-10-04 18:54:22 -07:00
serialStatusFlag = SERIAL_TRANSMIT_INPROGRESS_LEGACY ;
2023-02-20 17:55:54 -08:00
if ( firstCommsRequest )
{
firstCommsRequest = false ;
currentStatus . secl = 0 ;
}
2022-02-27 15:28:06 -08:00
}
2023-10-04 18:54:22 -07:00
//
2022-02-27 15:28:06 -08:00
currentStatus . spark ^ = ( - currentStatus . hasSync ^ currentStatus . spark ) & ( 1U < < BIT_SPARK_SYNC ) ; //Set the sync bit of the Spark variable to match the hasSync variable
for ( byte x = 0 ; x < packetLength ; x + + )
{
2023-10-04 18:54:22 -07:00
bool bufferFull = false ;
2023-10-05 04:24:13 -07:00
targetPort . write ( getTSLogEntry ( offset + x ) ) ;
if ( ( & targetPort = = & Serial ) | | ( configPage9 . secondarySerialProtocol ! = SECONDARY_SERIAL_PROTO_REALDASH ) )
2023-10-04 18:54:22 -07:00
{
2023-10-05 04:24:13 -07:00
//If the transmit buffer is full, wait for it to clear. This cannot be used with Read Dash as it will cause a timeout
if ( targetPort . availableForWrite ( ) < 1 ) { bufferFull = true ; }
2023-10-04 18:54:22 -07:00
}
2022-02-27 15:28:06 -08:00
//Check whether the tx buffer still has space
2023-10-04 18:54:22 -07:00
if ( bufferFull = = true )
2022-02-27 15:28:06 -08:00
{
//tx buffer is full. Store the current state so it can be resumed later
2023-02-20 17:55:54 -08:00
logItemsTransmitted = offset + x + 1 ;
2022-02-27 15:28:06 -08:00
inProgressLength = packetLength - x - 1 ;
return ;
}
}
2023-10-04 18:54:22 -07:00
2023-10-05 04:24:13 -07:00
if ( & targetPort = = & Serial )
2023-10-04 18:54:22 -07:00
{
serialStatusFlag = SERIAL_INACTIVE ;
while ( Serial . available ( ) ) { Serial . read ( ) ; }
// Reset any flags that are being used to trigger page refreshes
BIT_CLEAR ( currentStatus . status3 , BIT_STATUS3_VSS_REFRESH ) ;
}
2023-10-05 04:24:13 -07:00
else if ( & targetPort = = & CANSerial )
2023-10-04 18:54:22 -07:00
{
serialSecondaryStatusFlag = SERIAL_INACTIVE ;
}
2022-02-27 15:28:06 -08:00
}
2022-11-05 15:43:29 -07:00
void sendValuesLegacy ( void )
2022-02-27 15:28:06 -08:00
{
uint16_t temp ;
int bytestosend = 114 ;
bytestosend - = Serial . write ( currentStatus . secl > > 8 ) ;
bytestosend - = Serial . write ( currentStatus . secl ) ;
bytestosend - = Serial . write ( currentStatus . PW1 > > 8 ) ;
bytestosend - = Serial . write ( currentStatus . PW1 ) ;
bytestosend - = Serial . write ( currentStatus . PW2 > > 8 ) ;
bytestosend - = Serial . write ( currentStatus . PW2 ) ;
bytestosend - = Serial . write ( currentStatus . RPM > > 8 ) ;
bytestosend - = Serial . write ( currentStatus . RPM ) ;
temp = currentStatus . advance * 10 ;
bytestosend - = Serial . write ( temp > > 8 ) ;
bytestosend - = Serial . write ( temp ) ;
bytestosend - = Serial . write ( currentStatus . nSquirts ) ;
bytestosend - = Serial . write ( currentStatus . engine ) ;
bytestosend - = Serial . write ( currentStatus . afrTarget ) ;
bytestosend - = Serial . write ( currentStatus . afrTarget ) ; // send twice so afrtgt1 == afrtgt2
bytestosend - = Serial . write ( 99 ) ; // send dummy data as we don't have wbo2_en1
bytestosend - = Serial . write ( 99 ) ; // send dummy data as we don't have wbo2_en2
temp = currentStatus . baro * 10 ;
bytestosend - = Serial . write ( temp > > 8 ) ;
bytestosend - = Serial . write ( temp ) ;
temp = currentStatus . MAP * 10 ;
bytestosend - = Serial . write ( temp > > 8 ) ;
bytestosend - = Serial . write ( temp ) ;
temp = currentStatus . IAT * 10 ;
bytestosend - = Serial . write ( temp > > 8 ) ;
bytestosend - = Serial . write ( temp ) ;
temp = currentStatus . coolant * 10 ;
bytestosend - = Serial . write ( temp > > 8 ) ;
bytestosend - = Serial . write ( temp ) ;
temp = currentStatus . TPS * 10 ;
bytestosend - = Serial . write ( temp > > 8 ) ;
bytestosend - = Serial . write ( temp ) ;
bytestosend - = Serial . write ( currentStatus . battery10 > > 8 ) ;
bytestosend - = Serial . write ( currentStatus . battery10 ) ;
bytestosend - = Serial . write ( currentStatus . O2 > > 8 ) ;
bytestosend - = Serial . write ( currentStatus . O2 ) ;
bytestosend - = Serial . write ( currentStatus . O2_2 > > 8 ) ;
bytestosend - = Serial . write ( currentStatus . O2_2 ) ;
bytestosend - = Serial . write ( 99 ) ; // knock
bytestosend - = Serial . write ( 99 ) ; // knock
temp = currentStatus . egoCorrection * 10 ;
bytestosend - = Serial . write ( temp > > 8 ) ; // egocor1
bytestosend - = Serial . write ( temp ) ; // egocor1
bytestosend - = Serial . write ( temp > > 8 ) ; // egocor2
bytestosend - = Serial . write ( temp ) ; // egocor2
temp = currentStatus . iatCorrection * 10 ;
bytestosend - = Serial . write ( temp > > 8 ) ; // aircor
bytestosend - = Serial . write ( temp ) ; // aircor
temp = currentStatus . wueCorrection * 10 ;
bytestosend - = Serial . write ( temp > > 8 ) ; // warmcor
bytestosend - = Serial . write ( temp ) ; // warmcor
bytestosend - = Serial . write ( 99 ) ; // accelEnrich
bytestosend - = Serial . write ( 99 ) ; // accelEnrich
bytestosend - = Serial . write ( 99 ) ; // tpsFuelCut
bytestosend - = Serial . write ( 99 ) ; // tpsFuelCut
bytestosend - = Serial . write ( 99 ) ; // baroCorrection
bytestosend - = Serial . write ( 99 ) ; // baroCorrection
temp = currentStatus . corrections * 10 ;
bytestosend - = Serial . write ( temp > > 8 ) ; // gammaEnrich
bytestosend - = Serial . write ( temp ) ; // gammaEnrich
temp = currentStatus . VE * 10 ;
bytestosend - = Serial . write ( temp > > 8 ) ; // ve1
bytestosend - = Serial . write ( temp ) ; // ve1
temp = currentStatus . VE2 * 10 ;
bytestosend - = Serial . write ( temp > > 8 ) ; // ve2
bytestosend - = Serial . write ( temp ) ; // ve2
bytestosend - = Serial . write ( 99 ) ; // iacstep
bytestosend - = Serial . write ( 99 ) ; // iacstep
bytestosend - = Serial . write ( 99 ) ; // cold_adv_deg
bytestosend - = Serial . write ( 99 ) ; // cold_adv_deg
2022-12-13 16:17:47 -08:00
temp = currentStatus . tpsDOT ;
2022-02-27 15:28:06 -08:00
bytestosend - = Serial . write ( temp > > 8 ) ; // TPSdot
bytestosend - = Serial . write ( temp ) ; // TPSdot
2022-12-13 16:17:47 -08:00
temp = currentStatus . mapDOT ;
2022-02-27 15:28:06 -08:00
bytestosend - = Serial . write ( temp > > 8 ) ; // MAPdot
bytestosend - = Serial . write ( temp ) ; // MAPdot
temp = currentStatus . dwell * 10 ;
bytestosend - = Serial . write ( temp > > 8 ) ; // dwell
bytestosend - = Serial . write ( temp ) ; // dwell
bytestosend - = Serial . write ( 99 ) ; // MAF
bytestosend - = Serial . write ( 99 ) ; // MAF
bytestosend - = Serial . write ( currentStatus . fuelLoad * 10 ) ; // fuelload
bytestosend - = Serial . write ( 99 ) ; // fuelcor
bytestosend - = Serial . write ( 99 ) ; // fuelcor
bytestosend - = Serial . write ( 99 ) ; // portStatus
temp = currentStatus . advance1 * 10 ;
bytestosend - = Serial . write ( temp > > 8 ) ;
bytestosend - = Serial . write ( temp ) ;
temp = currentStatus . advance2 * 10 ;
bytestosend - = Serial . write ( temp > > 8 ) ;
bytestosend - = Serial . write ( temp ) ;
for ( int i = 0 ; i < bytestosend ; i + + )
{
// send dummy data to fill remote's buffer
Serial . write ( 99 ) ;
}
}
namespace {
void send_raw_entity ( const page_iterator_t & entity )
{
Serial . write ( ( byte * ) entity . pData , entity . size ) ;
}
inline void send_table_values ( table_value_iterator it )
{
while ( ! it . at_end ( ) )
{
auto row = * it ;
Serial . write ( & * row , row . size ( ) ) ;
+ + it ;
}
}
inline void send_table_axis ( table_axis_iterator it )
{
2023-01-10 17:58:48 -08:00
const int16_byte * pConverter = table3d_axis_io : : get_converter ( it . get_domain ( ) ) ;
2022-02-27 15:28:06 -08:00
while ( ! it . at_end ( ) )
{
Serial . write ( pConverter - > to_byte ( * it ) ) ;
+ + it ;
}
}
void send_table_entity ( const page_iterator_t & entity )
{
send_table_values ( rows_begin ( entity ) ) ;
send_table_axis ( x_begin ( entity ) ) ;
send_table_axis ( y_begin ( entity ) ) ;
}
void send_entity ( const page_iterator_t & entity )
{
switch ( entity . type )
{
case Raw :
return send_raw_entity ( entity ) ;
break ;
case Table :
return send_table_entity ( entity ) ;
break ;
case NoEntity :
// No-op
break ;
default :
abort ( ) ;
break ;
}
}
}
/** Pack the data within the current page (As set with the 'P' command) into a buffer and send it.
*
* Creates a page iterator by @ ref page_begin ( ) ( See : pages . cpp ) . Sends page given in @ ref currentPage .
*
2022-04-10 17:49:58 -07:00
* Note that some translation of the data is required to lay it out in the way Megasquirt / TunerStudio expect it .
2022-02-27 15:28:06 -08:00
* Data is sent in binary format , as defined by in each page in the speeduino . ini .
*/
2022-11-05 15:43:29 -07:00
void sendPage ( void )
2022-02-27 15:28:06 -08:00
{
page_iterator_t entity = page_begin ( currentPage ) ;
while ( entity . type ! = End )
{
send_entity ( entity ) ;
entity = advance ( entity ) ;
}
}
namespace {
/// Prints each element in the memory byte range (*first, *last).
void serial_println_range ( const byte * first , const byte * last )
{
while ( first ! = last )
{
Serial . println ( * first ) ;
+ + first ;
}
}
void serial_println_range ( const uint16_t * first , const uint16_t * last )
{
while ( first ! = last )
{
Serial . println ( * first ) ;
+ + first ;
}
}
void serial_print_space_delimited ( const byte * first , const byte * last )
{
while ( first ! = last )
{
2022-04-10 17:49:58 -07:00
Serial . print ( * first ) ; // This displays the values horizontally on the screen
2022-02-27 15:28:06 -08:00
Serial . print ( F ( " " ) ) ;
+ + first ;
}
Serial . println ( ) ;
}
# define serial_print_space_delimited_array(array) serial_print_space_delimited(array, _end_range_address(array))
void serial_print_prepadding ( byte value )
{
if ( value < 100 )
{
Serial . print ( F ( " " ) ) ;
if ( value < 10 )
{
Serial . print ( F ( " " ) ) ;
}
}
}
void serial_print_prepadded_value ( byte value )
{
serial_print_prepadding ( value ) ;
Serial . print ( value ) ;
Serial . print ( F ( " " ) ) ;
}
void print_row ( const table_axis_iterator & y_it , table_row_iterator row )
{
2023-01-10 17:58:48 -08:00
serial_print_prepadded_value ( table3d_axis_io : : to_byte ( y_it . get_domain ( ) , * y_it ) ) ;
2022-02-27 15:28:06 -08:00
while ( ! row . at_end ( ) )
{
serial_print_prepadded_value ( * row ) ;
+ + row ;
}
Serial . println ( ) ;
}
void print_x_axis ( const void * pTable , table_type_t key )
{
Serial . print ( F ( " " ) ) ;
auto x_it = x_begin ( pTable , key ) ;
2023-01-10 17:58:48 -08:00
const int16_byte * pConverter = table3d_axis_io : : get_converter ( x_it . get_domain ( ) ) ;
2022-02-27 15:28:06 -08:00
while ( ! x_it . at_end ( ) )
{
serial_print_prepadded_value ( pConverter - > to_byte ( * x_it ) ) ;
+ + x_it ;
}
}
void serial_print_3dtable ( const void * pTable , table_type_t key )
{
auto y_it = y_begin ( pTable , key ) ;
auto row_it = rows_begin ( pTable , key ) ;
while ( ! row_it . at_end ( ) )
{
print_row ( y_it , * row_it ) ;
+ + y_it ;
+ + row_it ;
}
print_x_axis ( pTable , key ) ;
Serial . println ( ) ;
}
}
/** Send page as ASCII for debugging purposes.
* Similar to sendPage ( ) , however data is sent in human readable format . Sends page given in @ ref currentPage .
*
* This is used for testing only ( Not used by TunerStudio ) in order to see current map and config data without the need for TunerStudio .
*/
2022-11-05 15:43:29 -07:00
void sendPageASCII ( void )
2022-02-27 15:28:06 -08:00
{
switch ( currentPage )
{
case veMapPage :
Serial . println ( F ( " \n VE Map " ) ) ;
serial_print_3dtable ( & fuelTable , fuelTable . type_key ) ;
break ;
case veSetPage :
Serial . println ( F ( " \n Pg 2 Cfg " ) ) ;
// The following loop displays in human readable form of all byte values in config page 1 up to but not including the first array.
serial_println_range ( ( byte * ) & configPage2 , configPage2 . wueValues ) ;
serial_print_space_delimited_array ( configPage2 . wueValues ) ;
// This displays all the byte values between the last array up to but not including the first unsigned int on config page 1
serial_println_range ( _end_range_byte_address ( configPage2 . wueValues ) , ( byte * ) & configPage2 . injAng ) ;
// The following loop displays four unsigned ints
serial_println_range ( configPage2 . injAng , configPage2 . injAng + _countof ( configPage2 . injAng ) ) ;
// Following loop displays byte values between the unsigned ints
serial_println_range ( _end_range_byte_address ( configPage2 . injAng ) , ( byte * ) & configPage2 . mapMax ) ;
Serial . println ( configPage2 . mapMax ) ;
// Following loop displays remaining byte values of the page
serial_println_range ( & configPage2 . fpPrime , ( byte * ) & configPage2 + sizeof ( configPage2 ) ) ;
break ;
case ignMapPage :
Serial . println ( F ( " \n Ignition Map " ) ) ;
serial_print_3dtable ( & ignitionTable , ignitionTable . type_key ) ;
break ;
case ignSetPage :
Serial . println ( F ( " \n Pg 4 Cfg " ) ) ;
Serial . println ( configPage4 . triggerAngle ) ; // configPage4.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
serial_println_range ( ( byte * ) & configPage4 . FixAng , configPage4 . taeBins ) ;
serial_print_space_delimited_array ( configPage4 . taeBins ) ;
serial_print_space_delimited_array ( configPage4 . taeValues ) ;
serial_print_space_delimited_array ( configPage4 . wueBins ) ;
Serial . println ( configPage4 . dwellLimit ) ; // Little lonely byte stuck between two arrays. No complications just display it.
serial_print_space_delimited_array ( configPage4 . dwellCorrectionValues ) ;
serial_println_range ( _end_range_byte_address ( configPage4 . dwellCorrectionValues ) , ( byte * ) & configPage4 + sizeof ( configPage4 ) ) ;
break ;
case afrMapPage :
Serial . println ( F ( " \n AFR Map " ) ) ;
serial_print_3dtable ( & afrTable , afrTable . type_key ) ;
break ;
case afrSetPage :
Serial . println ( F ( " \n Pg 6 Config " ) ) ;
serial_println_range ( ( byte * ) & configPage6 , configPage6 . voltageCorrectionBins ) ;
serial_print_space_delimited_array ( configPage6 . voltageCorrectionBins ) ;
serial_print_space_delimited_array ( configPage6 . injVoltageCorrectionValues ) ;
serial_print_space_delimited_array ( configPage6 . airDenBins ) ;
serial_print_space_delimited_array ( configPage6 . airDenRates ) ;
serial_println_range ( _end_range_byte_address ( configPage6 . airDenRates ) , configPage6 . iacCLValues ) ;
serial_print_space_delimited_array ( configPage6 . iacCLValues ) ;
serial_print_space_delimited_array ( configPage6 . iacOLStepVal ) ;
serial_print_space_delimited_array ( configPage6 . iacOLPWMVal ) ;
serial_print_space_delimited_array ( configPage6 . iacBins ) ;
serial_print_space_delimited_array ( configPage6 . iacCrankSteps ) ;
serial_print_space_delimited_array ( configPage6 . iacCrankDuty ) ;
serial_print_space_delimited_array ( configPage6 . iacCrankBins ) ;
// Following loop is for remaining byte value of page
serial_println_range ( _end_range_byte_address ( configPage6 . iacCrankBins ) , ( byte * ) & configPage6 + sizeof ( configPage6 ) ) ;
break ;
case boostvvtPage :
Serial . println ( F ( " \n Boost Map " ) ) ;
serial_print_3dtable ( & boostTable , boostTable . type_key ) ;
Serial . println ( F ( " \n VVT Map " ) ) ;
serial_print_3dtable ( & vvtTable , vvtTable . type_key ) ;
break ;
case seqFuelPage :
Serial . println ( F ( " \n Trim 1 Table " ) ) ;
serial_print_3dtable ( & trim1Table , trim1Table . type_key ) ;
break ;
case canbusPage :
Serial . println ( F ( " \n Page 9 Cfg " ) ) ;
serial_println_range ( ( byte * ) & configPage9 , ( byte * ) & configPage9 + sizeof ( configPage9 ) ) ;
break ;
case fuelMap2Page :
Serial . println ( F ( " \n 2nd Fuel Map " ) ) ;
serial_print_3dtable ( & fuelTable2 , fuelTable2 . type_key ) ;
break ;
case ignMap2Page :
Serial . println ( F ( " \n 2nd Ignition Map " ) ) ;
serial_print_3dtable ( & ignitionTable2 , ignitionTable2 . type_key ) ;
break ;
2022-06-02 22:44:09 -07:00
case boostvvtPage2 :
Serial . println ( F ( " \n Boost lookup table " ) ) ;
serial_print_3dtable ( & boostTableLookupDuty , boostTableLookupDuty . type_key ) ;
break ;
2022-02-27 15:28:06 -08:00
case warmupPage :
case progOutsPage :
default :
# ifndef SMALL_FLASH_MODE
Serial . println ( F ( " \n Page has not been implemented yet " ) ) ;
# endif
break ;
}
}
/** Processes an incoming stream of calibration data (for CLT, IAT or O2) from TunerStudio.
* Result is store in EEPROM and memory .
*
* @ param tableID - calibration table to process . 0 = Coolant Sensor . 1 = IAT Sensor . 2 = O2 Sensor .
*/
void receiveCalibration ( byte tableID )
{
void * pnt_TargetTable_values ; //Pointer that will be used to point to the required target table values
uint16_t * pnt_TargetTable_bins ; //Pointer that will be used to point to the required target table bins
int OFFSET , DIVISION_FACTOR ;
switch ( tableID )
{
case 0 :
//coolant table
pnt_TargetTable_values = ( uint16_t * ) & cltCalibration_values ;
pnt_TargetTable_bins = ( uint16_t * ) & cltCalibration_bins ;
OFFSET = CALIBRATION_TEMPERATURE_OFFSET ; //
DIVISION_FACTOR = 10 ;
break ;
case 1 :
//Inlet air temp table
pnt_TargetTable_values = ( uint16_t * ) & iatCalibration_values ;
pnt_TargetTable_bins = ( uint16_t * ) & iatCalibration_bins ;
OFFSET = CALIBRATION_TEMPERATURE_OFFSET ;
DIVISION_FACTOR = 10 ;
break ;
case 2 :
//O2 table
//pnt_TargetTable = (byte *)&o2CalibrationTable;
pnt_TargetTable_values = ( uint8_t * ) & o2Calibration_values ;
pnt_TargetTable_bins = ( uint16_t * ) & o2Calibration_bins ;
OFFSET = 0 ;
DIVISION_FACTOR = 1 ;
break ;
default :
OFFSET = 0 ;
pnt_TargetTable_values = ( uint16_t * ) & iatCalibration_values ;
pnt_TargetTable_bins = ( uint16_t * ) & iatCalibration_bins ;
DIVISION_FACTOR = 10 ;
break ; //Should never get here, but if we do, just fail back to main loop
}
int16_t tempValue ;
byte tempBuffer [ 2 ] ;
if ( tableID = = 2 )
{
//O2 calibration. Comes through as 1024 8-bit values of which we use every 32nd
for ( int x = 0 ; x < 1024 ; x + + )
{
while ( Serial . available ( ) < 1 ) { }
tempValue = Serial . read ( ) ;
if ( ( x % 32 ) = = 0 )
{
( ( uint8_t * ) pnt_TargetTable_values ) [ ( x / 32 ) ] = ( byte ) tempValue ; //O2 table stores 8 bit values
pnt_TargetTable_bins [ ( x / 32 ) ] = ( x ) ;
}
}
}
else
{
//Temperature calibrations are sent as 32 16-bit values
for ( uint16_t x = 0 ; x < 32 ; x + + )
{
while ( Serial . available ( ) < 2 ) { }
tempBuffer [ 0 ] = Serial . read ( ) ;
tempBuffer [ 1 ] = Serial . read ( ) ;
tempValue = ( int16_t ) ( word ( tempBuffer [ 1 ] , tempBuffer [ 0 ] ) ) ; //Combine the 2 bytes into a single, signed 16-bit value
2022-04-10 17:49:58 -07:00
tempValue = div ( tempValue , DIVISION_FACTOR ) . quot ; //TS sends values multiplied by 10 so divide back to whole degrees.
2022-02-27 15:28:06 -08:00
tempValue = ( ( tempValue - 32 ) * 5 ) / 9 ; //Convert from F to C
//Apply the temp offset and check that it results in all values being positive
tempValue = tempValue + OFFSET ;
if ( tempValue < 0 ) { tempValue = 0 ; }
( ( uint16_t * ) pnt_TargetTable_values ) [ x ] = tempValue ; //Both temp tables have 16-bit values
pnt_TargetTable_bins [ x ] = ( x * 32U ) ;
writeCalibration ( ) ;
}
}
writeCalibration ( ) ;
}
/** Send 256 tooth log entries to serial.
* 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
*/
2023-02-20 17:55:54 -08:00
void sendToothLog_legacy ( byte startOffset ) /* Blocking */
2022-02-27 15:28:06 -08:00
{
//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 ( BIT_CHECK ( currentStatus . status1 , BIT_STATUS1_TOOTHLOG1READY ) ) //Sanity check. Flagging system means this should always be true
{
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_TRANSMIT_TOOTH_INPROGRESS_LEGACY ;
2022-02-27 15:28:06 -08:00
for ( int x = startOffset ; x < TOOTH_LOG_SIZE ; x + + )
{
Serial . write ( toothHistory [ x ] > > 24 ) ;
Serial . write ( toothHistory [ x ] > > 16 ) ;
Serial . write ( toothHistory [ x ] > > 8 ) ;
Serial . write ( toothHistory [ x ] ) ;
}
BIT_CLEAR ( currentStatus . status1 , BIT_STATUS1_TOOTHLOG1READY ) ;
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
toothHistoryIndex = 0 ;
}
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 < byte > ( 0x00 ) ) ; //GCC9 fix
}
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
}
}
2023-02-20 17:55:54 -08:00
void sendCompositeLog_legacy ( byte startOffset ) /* Non-blocking */
2022-02-27 15:28:06 -08:00
{
if ( BIT_CHECK ( currentStatus . status1 , BIT_STATUS1_TOOTHLOG1READY ) ) //Sanity check. Flagging system means this should always be true
{
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_TRANSMIT_COMPOSITE_INPROGRESS_LEGACY ;
2022-02-27 15:28:06 -08:00
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
2023-02-20 17:55:54 -08:00
logItemsTransmitted = x ;
2022-02-27 15:28:06 -08:00
return ;
}
2023-02-20 17:55:54 -08:00
uint32_t inProgressCompositeTime = toothHistory [ x ] ; //This combined runtime (in us) that the log was going for by this record)
2022-02-27 15:28:06 -08:00
Serial . write ( inProgressCompositeTime > > 24 ) ;
Serial . write ( inProgressCompositeTime > > 16 ) ;
Serial . write ( inProgressCompositeTime > > 8 ) ;
Serial . write ( inProgressCompositeTime ) ;
Serial . write ( compositeLogHistory [ x ] ) ; //The status byte (Indicates the trigger edge, whether it was a pri/sec pulse, the sync status)
}
BIT_CLEAR ( currentStatus . status1 , BIT_STATUS1_TOOTHLOG1READY ) ;
toothHistoryIndex = 0 ;
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
}
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 < byte > ( 0x00 ) ) ; //GCC9 fix
}
2023-02-20 17:55:54 -08:00
serialStatusFlag = SERIAL_INACTIVE ;
2022-02-27 15:28:06 -08:00
}
}
2023-02-20 17:55:54 -08:00
void testComm ( void )
2022-02-27 15:28:06 -08:00
{
Serial . write ( 1 ) ;
return ;
}