├── .gitattributes ├── .gitignore ├── DTC_arrays.h ├── SubaruSelectMonitor ├── DTC_arrays.h ├── SubaruSelectMonitor.cpp └── SubaruSelectMonitor.h ├── WRXClockPodModv2 ├── DTC_arrays.h └── WRXClockPodModv2.ino ├── WRXClockPodModv3.ino ├── basic_SSM_comms_format.ino └── readme.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /SubaruSelectMonitor/SubaruSelectMonitor.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "SubaruSelectMonitor.h" 3 | #include "Arduino.h" 4 | #include "DTC_arrays.h" 5 | 6 | 7 | bool readSSM(SoftwareSerial &serialPort, uint8_t *dataBuffer, uint16_t bufferSize) 8 | { 9 | //read in the response from the ECU 10 | bool success = readSSM_Fast(serialPort, dataBuffer, bufferSize, false); 11 | return success; 12 | } 13 | 14 | bool readSSM_Fast(SoftwareSerial &serialPort, uint8_t *dataBuffer, uint16_t bufferSize, bool ignoreZeroValues) 15 | { 16 | //read in an SSM serial response packet, place the data bytes in the data buffer 17 | //and return a boolean value indicating whether the checksum failed 18 | uint16_t timeout = 100;//100 millisecond timeout for response 19 | uint32_t timerstart = millis();//start the timer 20 | boolean notFinished = true;//a flag to indicate that we are done 21 | boolean checksumSuccess = false; 22 | uint8_t dataSize = 0; 23 | uint8_t calcSum = 0; 24 | uint8_t packetIndex = 0; 25 | uint8_t nonZeroDataIndex = 0; 26 | 27 | Serial.print(F("Read Packet: [ ")); 28 | while(millis() - timerstart <= timeout && notFinished) 29 | { 30 | if(serialPort.available() > 0) 31 | { 32 | uint8_t data = serialPort.read(); 33 | Serial.print(data); 34 | Serial.print(F(" "));//if not printing data, be sure to delay to allow for more bytes to come in 35 | if(packetIndex == 0 && data == 128) 36 | { 37 | //0x80 or 128 marks the beginning of a packet 38 | packetIndex += 1; 39 | calcSum += data; 40 | } 41 | else if(packetIndex == 1 && data == 240) 42 | { 43 | //this byte indicates that the message recipient is the 'Diagnostic tool' 44 | packetIndex += 1; 45 | calcSum += data; 46 | } 47 | else if(packetIndex == 2 && data == 16) 48 | { 49 | //this byte indicates that the message sender is the ECU 50 | packetIndex += 1; 51 | calcSum += data; 52 | } 53 | else if(packetIndex == 3) 54 | { 55 | //this byte indicates the number of data bytes which follow 56 | dataSize = data; 57 | packetIndex += 1; 58 | calcSum += data; 59 | } 60 | else if(packetIndex == 4) 61 | { 62 | //I don't know what this byte is for 63 | packetIndex += 1; 64 | calcSum += data; 65 | } 66 | else if(packetIndex > 4 && packetIndex < (dataSize+4)) 67 | { 68 | //this byte is data 69 | if(packetIndex - 5 < bufferSize && ignoreZeroValues == false)//make sure it fits into the buffer 70 | { 71 | dataBuffer[packetIndex - 5] = data; 72 | } 73 | else if(nonZeroDataIndex < bufferSize/2 && ignoreZeroValues == true && data != 0) 74 | { 75 | //here we save only the non-zero values 76 | dataBuffer[nonZeroDataIndex] = data;//save the data 77 | dataBuffer[nonZeroDataIndex + bufferSize/2] = packetIndex;//save the packet-index of the data 78 | nonZeroDataIndex += 1; 79 | } 80 | packetIndex += 1; 81 | calcSum += data; 82 | } 83 | else if(packetIndex == dataSize + 4) 84 | { 85 | //this is the checksum byte 86 | if(data == calcSum) 87 | { 88 | //checksum was successful 89 | checksumSuccess = true; 90 | } 91 | else 92 | { 93 | Serial.print(F("Checksum fail ")); 94 | } 95 | notFinished = false;//we're done now 96 | break; 97 | } 98 | timerstart = millis();//reset the timeout if we're not finished 99 | } 100 | } 101 | Serial.println(F("]")); 102 | if(notFinished) 103 | { 104 | Serial.println(F("Comm. timeout")); 105 | } 106 | return checksumSuccess; 107 | } 108 | 109 | //writes data over the software serial port 110 | void writeSSM(SoftwareSerial &serialPort, uint8_t *dataBuffer, uint8_t bufferSize) { 111 | //this function needs to catch mistakes that I make in preparing 112 | //the packets -> {128, 16, 240, #data, 168, cont. response flag, addr1,addr2,addr3,checksum} = 10 bytes, 5 of which are data 113 | dataBuffer[0] = 128;//begin packet 114 | dataBuffer[1] = 16;//to subaru ECU 115 | dataBuffer[2] = 240;//from diagnostic tool 116 | //number of data bytes include 3*(number of addresses), cont. response flag, and mode (address read) 117 | //this means that the header, to, from, #data, and checksum are not part. 118 | dataBuffer[3] = bufferSize - 5;//# data bytes 119 | dataBuffer[4] = 168;//single address read 120 | dataBuffer[5] = 0;//no continuous polling 121 | uint8_t sum = 0; 122 | Serial.print(F("Sending packet: [ ")); 123 | for (uint8_t x = 0; x < bufferSize; x++) { 124 | if(x == bufferSize - 1 && sum != dataBuffer[x]) 125 | { 126 | dataBuffer[x] = sum;//fix the checksum in the data buffer 127 | } 128 | else 129 | { 130 | sum += dataBuffer[x];//build the checksum 131 | } 132 | serialPort.write(dataBuffer[x]); 133 | Serial.print(dataBuffer[x]); 134 | Serial.print(F(" ")); 135 | } 136 | Serial.println(F("]")); 137 | } 138 | 139 | /*writes data over the software serial port 140 | the &serialPort passes a reference to the external 141 | object so that we can control it outside of the function*/ 142 | void writeSSM_Fast(SoftwareSerial &serialPort, uint8_t data[], uint8_t length) { 143 | 144 | for (uint8_t x = 0; x < length; x++) { 145 | serialPort.write(data[x]); 146 | } 147 | 148 | } 149 | 150 | uint8_t serialCallSSM(SoftwareSerial &serialPort, uint8_t *sendDataBuffer, uint16_t sendBufferSize, uint8_t *receiveDataBuffer, uint16_t receiveBufferSize, uint8_t attempts) 151 | { 152 | //this function performs the call and response routine for 153 | //exchanging serial data with the Subaru ECU via SSM 154 | //it sends a message and awaits a response, resending the 155 | //message if we reach timeout or if the checksum failed 156 | //it stops trying the data exchange if 10 failures occur and 157 | //it outputs the number of attempts it failed to send data 158 | //input: handle to software serial pins 159 | // buffer with outgoing data and length of buffer 160 | // buffer for received data (not the whole packet), and length of buffer 161 | // the initial value for a counter of failed attempts 162 | 163 | writeSSM(serialPort, sendDataBuffer, sendBufferSize);//send the message 164 | bool success = readSSM(serialPort, receiveDataBuffer, receiveBufferSize);//receive the response 165 | uint8_t newAttempt = 0; 166 | 167 | if(!success && attempts < 10) 168 | { 169 | Serial.println(F("Packet exchange failed, trying again... ")); 170 | newAttempt = serialCallSSM(serialPort, sendDataBuffer, sendBufferSize, receiveDataBuffer, receiveBufferSize, attempts + 1); 171 | } 172 | newAttempt += attempts; 173 | 174 | return newAttempt; 175 | } 176 | 177 | //************************************************************* 178 | //now for DTC reading functions 179 | String checkForCEL(SoftwareSerial &serialPort, bool &MILon) 180 | { 181 | //first, we populate packets to send to the ECU. 182 | //These request the bytes where DTC flags are stored. 183 | uint8_t readDTCTemp1[] = {128, 16, 240, 98, 168, 0, 0, 0, 142, 0, 0, 143, 0, 0, 144, 0, 0, 145, 0, 0, 146, 0, 0, 147, 0, 0, 148, 0, 0, 149, 0, 0, 150, 0, 0, 151, 0, 0, 152, 0, 0, 153, 0, 0, 154, 0, 0, 155, 0, 0, 156, 0, 0, 157, 0, 0, 158, 0, 0, 159, 0, 0, 160, 0, 0, 161, 0, 0, 162, 0, 0, 163, 0, 0, 164, 0, 0, 165, 0, 0, 166, 0, 0, 167, 0, 0, 168, 0, 0, 169, 0, 0, 170, 0, 0, 171, 0, 0, 172, 0, 0, 173, 58}; 184 | uint8_t readDTCTemp2[] = {128, 16, 240, 68, 168, 0, 0, 0, 240, 0, 0, 241, 0, 0, 242, 0, 0, 243, 0, 1, 35, 0, 1, 36, 0, 1, 37, 0, 1, 38, 0, 1, 39, 0, 1, 40, 0, 1, 41, 0, 1, 42, 0, 1, 80, 0, 1, 81, 0, 1, 82, 0, 1, 83, 0, 1, 84, 0, 1, 96, 0, 1, 97, 0, 1, 98, 0, 1, 99, 0, 1, 100, 252}; 185 | uint8_t readDTCMem1[] = {128, 16, 240, 98, 168, 0, 0, 0, 174, 0, 0, 175, 0, 0, 176, 0, 0, 177, 0, 0, 178, 0, 0, 179, 0, 0, 180, 0, 0, 181, 0, 0, 182, 0, 0, 183, 0, 0, 184, 0, 0, 185, 0, 0, 186, 0, 0, 187, 0, 0, 188, 0, 0, 189, 0, 0, 190, 0, 0, 191, 0, 0, 192, 0, 0, 193, 0, 0, 194, 0, 0, 195, 0, 0, 196, 0, 0, 197, 0, 0, 198, 0, 0, 199, 0, 0, 200, 0, 0, 201, 0, 0, 202, 0, 0, 203, 0, 0, 204, 0, 0, 205, 58}; 186 | uint8_t readDTCMem2[] = {128, 16, 240, 68, 168, 0, 0, 0, 244, 0, 0, 245, 0, 0, 246, 0, 0, 247, 0, 1, 43, 0, 1, 44, 0, 1, 45, 0, 1, 46, 0, 1, 47, 0, 1, 48, 0, 1, 49, 0, 1, 50, 0, 1, 85, 0, 1, 86, 0, 1, 87, 0, 1, 88, 0, 1, 89, 0, 1, 101, 0, 1, 102, 0, 1, 103, 0, 1, 104, 0, 1, 105, 126}; 187 | 188 | //Next, we prepare some DTC byte and flag index variables 189 | uint8_t DTC[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 190 | int DTCFlag1[][2] = {{ -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}}; 191 | int DTCFlag2[][2] = {{ -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}}; 192 | 193 | 194 | Serial.println(F("Reading CELs")); 195 | 196 | writeSSM_Fast(serialPort, readDTCTemp1, 103); 197 | readSSM_Fast(serialPort, DTC, 10, true); 198 | //readECU(DTC, 10, true);//old function 199 | 200 | if (DTC[0] != 0) { 201 | DTCread(DTC, 10, DTCFlag1, 10, 1); 202 | resetDTCArray(DTC, 10); 203 | MILon = true; 204 | } 205 | 206 | writeSSM_Fast(serialPort, readDTCTemp2, 73); 207 | readSSM_Fast(serialPort, DTC, 10, true); 208 | //readECU(DTC, 10, true); 209 | 210 | if (DTC[0] != 0) { 211 | DTCread(DTC, 10, DTCFlag2, 10, 2); 212 | resetDTCArray(DTC, 10); 213 | MILon = true; 214 | } 215 | 216 | writeSSM_Fast(serialPort, readDTCMem1, 103); 217 | readSSM_Fast(serialPort, DTC, 10, true); 218 | //readECU(DTC, 10, true); 219 | 220 | if (DTC[0] != 0) { 221 | DTCread(DTC, 10, DTCFlag1, 10, 1); 222 | resetDTCArray(DTC, 10); 223 | MILon = true; 224 | } 225 | 226 | writeSSM_Fast(serialPort, readDTCMem2, 73); 227 | readSSM_Fast(serialPort, DTC, 10, true); 228 | //readECU(DTC, 10, true); 229 | 230 | if (DTC[0] != 0) { 231 | DTCread(DTC, 10, DTCFlag2, 10, 2); 232 | resetDTCArray(DTC, 10); 233 | MILon = true; 234 | } 235 | 236 | //if we read in a MIL, we should look up the code 237 | //and prepare a string with the code to return. 238 | String DTCcodes = ""; 239 | if (MILon == true) 240 | { 241 | Serial.println(F("Displaying CELs")); 242 | String DTC1string = DTCupdate(DTCFlag1, 10, 1); 243 | String DTC2string = DTCupdate(DTCFlag2, 10, 2); 244 | DTCcodes.concat(DTC1string); 245 | DTCcodes.concat(DTC2string); 246 | } 247 | return DTCcodes; 248 | } 249 | 250 | 251 | //input the DTC flags, with half the array as the DTC flag number and the other half as the packet index (address) 252 | //This function will change DTCs[] to include the error code addresses: [address][byte], but not overwrite or duplicate values 253 | void DTCread(uint8_t flags[], uint8_t flagLength, int DTCs[][2], int DTCsLength, int DTCTableNumber) 254 | { 255 | int DTCconvoluted = 0; 256 | int DTCaddr = 0; 257 | int index = 0; 258 | bool duplicate = false; 259 | 260 | for (int plus = 0; plus < DTCsLength; plus++); 261 | { 262 | if (DTCs[index][0] != -1) 263 | index++; 264 | } 265 | 266 | for (uint8_t i = 0; i < flagLength / 2; i++) 267 | { 268 | if (flags[i] != 0) 269 | { 270 | DTCconvoluted = flags[i]; 271 | DTCaddr = flags[i + flagLength / 2] - 5; 272 | for (int f = 0; f < 8; f++) 273 | { 274 | if (bitRead(DTCconvoluted, f) != 0 && index < DTCsLength) 275 | { 276 | //loop to check if this value is already present in DTCs 277 | for (int pres = 0; pres < DTCsLength; pres++) 278 | { 279 | if (DTCs[pres][0] == DTCaddr && DTCs[pres][1] == f) 280 | { 281 | duplicate = true; 282 | } 283 | } 284 | if (duplicate == false) 285 | { 286 | DTCs[index][0] = DTCaddr; 287 | DTCs[index][1] = f; 288 | index++; 289 | } 290 | duplicate = false; 291 | } 292 | } 293 | } 294 | } 295 | } 296 | 297 | 298 | //This function takes in an array of DTC addresses, and reads off the string values from PROGMEM 299 | //Finally, it displays them on the serial monitor. Values of '-1' are ignored. DTCTableNumber is 1 or 2. 300 | String DTCupdate(int DTCs[][2], byte DTCsLength, byte DTCTableNumber) 301 | { 302 | char buffer[5]; 303 | byte DTCcount = 0; 304 | String DTCstring = ""; 305 | 306 | 307 | for (byte a = 0; a < DTCsLength; a++) 308 | { 309 | byte b = DTCs[a][0]; 310 | byte c = DTCs[a][1]; 311 | if (DTCs[a][0] != -1 && DTCTableNumber == 1) 312 | { 313 | strcpy_P(buffer, (char*)pgm_read_word(&(DTCset1[b][c]))); 314 | Serial.println(buffer); 315 | DTCstring.concat(buffer); 316 | DTCstring.concat(" "); 317 | 318 | DTCcount += 6; 319 | } 320 | else if (DTCs[a][0] != -1 && DTCTableNumber == 2) 321 | { 322 | strcpy_P(buffer, (char*)pgm_read_word(&(DTCset2[b][c]))); 323 | Serial.println(buffer); 324 | DTCstring.concat(buffer); 325 | DTCstring.concat(" "); 326 | 327 | DTCcount += 6; 328 | } 329 | } 330 | return DTCstring; 331 | } 332 | 333 | void resetDTCArray(uint8_t*DTC, uint8_t DTCarrayLength) 334 | { 335 | for (uint8_t x = 0; x < DTCarrayLength; x++) 336 | { 337 | DTC[x] = 0; 338 | } 339 | } 340 | 341 | //end DTC reading functions *********************** -------------------------------------------------------------------------------- /SubaruSelectMonitor/SubaruSelectMonitor.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include "Arduino.h" 3 | 4 | 5 | //use this function to read (and display over serial comms) 6 | //the response that the ECU gives to a query packet 7 | bool readSSM(SoftwareSerial &serialPort, uint8_t *dataBuffer, uint16_t bufferSize); 8 | 9 | 10 | //use this function to read the response that the ECU 11 | //gives to a query packet- set a flag to ignore values 12 | //which are zero 13 | bool readSSM_Fast(SoftwareSerial &serialPort, uint8_t *dataBuffer, uint16_t bufferSize, bool ignoreZeroValues); 14 | 15 | 16 | //use this function to write a serial packet to the ECU 17 | //this function displays the packet it sends over 18 | //serial comms for diagnostic support. It also corrects 19 | //the packet header, command and checksum bytes to be 20 | //a 'read ECU single address' query 21 | void writeSSM(SoftwareSerial &serialPort, uint8_t *dataBuffer, uint8_t bufferSize); 22 | 23 | //use this function to write the contents of the data 24 | //buffer to the ECU- no serial display or error 25 | //correction is performed 26 | void writeSSM_Fast(SoftwareSerial &serialPort, uint8_t data[], uint8_t length); 27 | 28 | //use this function to send data to the ECU and 29 | //to read the response- if an error occurs 30 | //the function will re-try the ECU query 31 | uint8_t serialCallSSM(SoftwareSerial &serialPort, uint8_t *sendDataBuffer, uint16_t sendBufferSize, uint8_t *receiveDataBuffer, uint16_t receiveBufferSize, uint8_t attempts); 32 | 33 | 34 | //below are a bunch of functions related to reading DTCs 35 | 36 | //use this function to check for DTCs and return a string 37 | //containing all DTCs discovered. The boolean that is passed 38 | //in is updated if DTCs are found. 39 | String checkForCEL(SoftwareSerial &serialPort, bool &MILon); 40 | 41 | //the below functions are used in 'checkForCEL' to 42 | //decode the DTCs as read from the ECU 43 | void DTCread(uint8_t flags[], uint8_t flagLength, int DTCs[][2], int DTCsLength, int DTCTableNumber); 44 | String DTCupdate(int DTCs[][2], byte DTCsLength, byte DTCTableNumber); 45 | void resetDTCArray(uint8_t *DTC, uint8_t DTCarrayLength); -------------------------------------------------------------------------------- /WRXClockPodModv2/DTC_arrays.h: -------------------------------------------------------------------------------- 1 | /*these are adopted from the romraider logger definitions v253 2 | use PROGMEM to save variable space except for the active DTCs 3 | A 3D array, with the 1st index set the same as the request order 4 | of the ECU calls to tempDTCs and memDTCs addresses, the second 5 | index set to the bit position of the DTC address, and the third 6 | as the DTC code chars */ 7 | 8 | prog_char P0335[] PROGMEM = "P0335"; 9 | prog_char unkn[] PROGMEM = "unkn"; 10 | prog_char P0336[] PROGMEM = "P0336"; 11 | prog_char P0341[] PROGMEM = "P0341"; 12 | prog_char P0340[] PROGMEM = "P0340"; 13 | prog_char P0604[] PROGMEM = "P0604"; 14 | prog_char P0601[] PROGMEM = "P0601"; 15 | 16 | //0x0000AE and temp 0x00008E 17 | //PROGMEM const char *addr0x0000AE[] = {P0335,unkn,P0336,P0341,P0340,unkn,P0604,P0601}; 18 | 19 | prog_char P0102[] PROGMEM = "P0102"; 20 | prog_char P0103[] PROGMEM = "P0103"; 21 | prog_char P1141[] PROGMEM = "P1141"; 22 | prog_char P0101[] PROGMEM = "P0101"; 23 | prog_char P0500[] PROGMEM = "P0500"; 24 | prog_char P1540[] PROGMEM = "P1540"; 25 | prog_char P0332[] PROGMEM = "P0332"; 26 | prog_char P0333[] PROGMEM = "P0333"; 27 | 28 | //0x0000AF and temp 0x00008F 29 | //PROGMEM const char *addr0x0000AF[] = {P0102,P0103,P1141,P0101,P0500,P1540,P0332,P0333}; 30 | 31 | prog_char P0325[] PROGMEM = "P0325"; 32 | prog_char P0330[] PROGMEM = "P0330"; 33 | prog_char P0327[] PROGMEM = "P0327"; 34 | prog_char P0328[] PROGMEM = "P0328"; 35 | prog_char P0122[] PROGMEM = "P0122"; 36 | prog_char P0123[] PROGMEM = "P0123"; 37 | prog_char P1142[] PROGMEM = "P1142"; 38 | prog_char P0121[] PROGMEM = "P0121"; 39 | 40 | //0x0000B0 and temp 0x000090 41 | //PROGMEM const char *addr0x0000B0[] = {P0325,P0330,P0327,P0328,P0122,P0123,P1142,P0121}; 42 | 43 | prog_char P0117[] PROGMEM = "P0117"; 44 | prog_char P0118[] PROGMEM = "P0118"; 45 | prog_char P0464[] PROGMEM = "P0464"; 46 | prog_char P0125[] PROGMEM = "P0125"; 47 | prog_char P0462[] PROGMEM = "P0462"; 48 | prog_char P0463[] PROGMEM = "P0463"; 49 | prog_char P0461[] PROGMEM = "P0461"; 50 | prog_char P1442[] PROGMEM = "P1442"; 51 | 52 | //0x0000B1 and temp 0x000091 53 | //PROGMEM const char *addr0x0000B1[] = {P0117,P0118,P0464,P0125,P0462,P0463,P0461,P1442}; 54 | 55 | prog_char P0107[] PROGMEM = "P0107"; 56 | prog_char P0108[] PROGMEM = "P0108"; 57 | prog_char P0143[] PROGMEM = "P0143"; 58 | prog_char P0144[] PROGMEM = "P0144"; 59 | prog_char P0106[] PROGMEM = "P0106"; 60 | prog_char P0350[] PROGMEM = "P0350"; 61 | prog_char P1518[] PROGMEM = "P1518"; 62 | prog_char P0512[] PROGMEM = "P0512"; 63 | 64 | //0x0000B2 and temp 0x000092 65 | //PROGMEM const char *addr0x0000B2[] = {P0107,P0108,P0143,P0144,P0106,P0350,P1518,P0512}; 66 | 67 | prog_char P0452[] PROGMEM = "P0452"; 68 | prog_char P0453[] PROGMEM = "P0453"; 69 | prog_char P0451[] PROGMEM = "P0451"; 70 | prog_char P1237[] PROGMEM = "P1237"; 71 | prog_char P1238[] PROGMEM = "P1238"; 72 | prog_char P1239[] PROGMEM = "P1238"; 73 | prog_char P1240[] PROGMEM = "P1240"; 74 | 75 | //0x0000B3 and temp 0x000093 76 | //PROGMEM const char *addr0x0000B3[] = {P0452,P0453,unkn,P0451,P1237,P1238,P1239,P1240}; 77 | 78 | prog_char P1590[] PROGMEM = "P1590"; 79 | prog_char P1591[] PROGMEM = "P1591"; 80 | prog_char P1592[] PROGMEM = "P1592"; 81 | prog_char P0851[] PROGMEM = "P0851"; 82 | prog_char P0182[] PROGMEM = "P0182"; 83 | prog_char P0183[] PROGMEM = "P0183"; 84 | prog_char P0181[] PROGMEM = "P0181"; 85 | prog_char P0852[] PROGMEM = "P0852"; 86 | 87 | //0x0000B4 and temp 0x000094 88 | //PROGMEM const char *addr0x0000B4[] = {P1590,P1591,P1592,P0851,P0182,P0183,P0181,P0852}; 89 | 90 | prog_char P1510[] PROGMEM = "P1510"; 91 | prog_char P1511[] PROGMEM = "P1511"; 92 | prog_char P1512[] PROGMEM = "P1512"; 93 | prog_char P1513[] PROGMEM = "P1513"; 94 | prog_char P1514[] PROGMEM = "P1514"; 95 | prog_char P1515[] PROGMEM = "P1515"; 96 | prog_char P1516[] PROGMEM = "P1516"; 97 | prog_char P1517[] PROGMEM = "P1517"; 98 | 99 | //0x0000B5 and temp 0x000095 100 | //PROGMEM const char *addr0x0000B5[] = {P1510,P1511,P1512,P1513,P1514,P1515,P1516,P1517}; 101 | 102 | prog_char P1492[] PROGMEM = "P1492"; 103 | prog_char P1493[] PROGMEM = "P1493"; 104 | prog_char P1494[] PROGMEM = "P1494"; 105 | prog_char P1495[] PROGMEM = "P1495"; 106 | prog_char P1496[] PROGMEM = "P1496"; 107 | prog_char P1497[] PROGMEM = "P1497"; 108 | prog_char P1498[] PROGMEM = "P1498"; 109 | prog_char P1499[] PROGMEM = "P1499"; 110 | 111 | //0x0000B6 and temp 0x000096 112 | //PROGMEM const char *addr0x0000B6[] = {P1492,P1493,P1494,P1495,P1496,P1497,P1498,P1499}; 113 | 114 | prog_char P1102[] PROGMEM = "P1102"; 115 | prog_char P1122[] PROGMEM = "P1122"; 116 | prog_char P1446[] PROGMEM = "P1446"; 117 | prog_char P1447[] PROGMEM = "P1447"; 118 | prog_char P1090[] PROGMEM = "P1090"; 119 | prog_char P1091[] PROGMEM = "P1091"; 120 | prog_char P1092[] PROGMEM = "P1092"; 121 | prog_char P1093[] PROGMEM = "P1093"; 122 | 123 | //0x0000B7 and temp 0x000097 124 | //PROGMEM const char *addr0x0000B7[] = {P1102,P1122,P1446,P1447,P1090,P1091,P1092,P1093}; 125 | 126 | prog_char P0508[] PROGMEM = "P0508"; 127 | prog_char P0509[] PROGMEM = "P0509"; 128 | prog_char P1507[] PROGMEM = "P1507"; 129 | prog_char P0506[] PROGMEM = "P0506"; 130 | prog_char P0507[] PROGMEM = "P0507"; 131 | prog_char P1698[] PROGMEM = "P1698"; 132 | prog_char P1699[] PROGMEM = "P1699"; 133 | prog_char P1448[] PROGMEM = "P1448"; 134 | 135 | //0x0000B8 and temp 0x000098 136 | //PROGMEM const char *addr0x0000B8[] = {P0508,P0509,P1507,P0506,P0507,P1698,P1699,P1448}; 137 | 138 | prog_char P0444[] PROGMEM = "P0444"; 139 | prog_char P0445[] PROGMEM = "P0445"; 140 | prog_char P0691[] PROGMEM = "P0691"; 141 | prog_char P0692[] PROGMEM = "P0692"; 142 | prog_char P0480[] PROGMEM = "P0480"; 143 | prog_char P1480[] PROGMEM = "P1480"; 144 | prog_char P0483[] PROGMEM = "P0483"; 145 | prog_char P0864[] PROGMEM = "P0864"; 146 | 147 | //0x0000B9 and temp 0x000099 148 | //PROGMEM const char *addr0x0000B9[] = {P0444,P0445,P0691,P0692,P0480,P1480,P0483,P0864}; 149 | 150 | prog_char P0365[] PROGMEM = "P0365"; 151 | prog_char P0390[] PROGMEM = "P0390"; 152 | prog_char P0011[] PROGMEM = "P0011"; 153 | prog_char P0021[] PROGMEM = "P0021"; 154 | prog_char P1400[] PROGMEM = "P1400"; 155 | prog_char P1420[] PROGMEM = "P1420"; 156 | prog_char P0458[] PROGMEM = "P0458"; 157 | prog_char P0459[] PROGMEM = "P0459"; 158 | 159 | //0x0000BA and temp 0x00009A 160 | //PROGMEM const char *addr0x0000BA[] = {P0365,P0390,P0011,P0021,P1400,P1420,P0458,P0459}; 161 | 162 | prog_char P0865[] PROGMEM = "P0865"; 163 | prog_char P0866[] PROGMEM = "P0866"; 164 | prog_char P1443[] PROGMEM = "P1443"; 165 | prog_char P1559[] PROGMEM = "P1559"; 166 | prog_char P0661[] PROGMEM = "P0661"; 167 | prog_char P0662[] PROGMEM = "P0662"; 168 | prog_char P0447[] PROGMEM = "P0447"; 169 | prog_char P0448[] PROGMEM = "P0448"; 170 | 171 | //0x0000BB and temp 0x00009B 172 | //PROGMEM const char *addr0x0000BB[] = {P0865,P0866,P1443,P1559,P0661,P0662,P0447,P0448}; 173 | 174 | prog_char P0720[] PROGMEM = "P0720"; 175 | prog_char P0725[] PROGMEM = "P0725"; 176 | prog_char P1700[] PROGMEM = "P1700"; 177 | prog_char P0710[] PROGMEM = "P0710"; 178 | prog_char P0705[] PROGMEM = "P0705"; 179 | prog_char P1701[] PROGMEM = "P1701"; 180 | prog_char P0703[] PROGMEM = "P0703"; 181 | prog_char P0741[] PROGMEM = "P0741"; 182 | 183 | //0x0000BC and temp 0x00009C 184 | //PROGMEM const char *addr0x0000BC[] = {P0720,P0725,P1700,P0710,P0705,P1701,P0703,P0741}; 185 | 186 | prog_char P0753[] PROGMEM = "P0753"; 187 | prog_char P0758[] PROGMEM = "P0758"; 188 | prog_char P1706[] PROGMEM = "P1706"; 189 | prog_char P0748[] PROGMEM = "P0748"; 190 | prog_char P0743[] PROGMEM = "P0743"; 191 | prog_char P0731[] PROGMEM = "P0731"; 192 | prog_char P0732[] PROGMEM = "P0732"; 193 | prog_char P0733[] PROGMEM = "P0733"; 194 | 195 | //0x0000BD and temp 0x00009D 196 | //PROGMEM const char *addr0x0000BD[] = {P0753,P0758,P1706,P0748,P0743,P0731,P0732,P0733}; 197 | 198 | prog_char P0734[] PROGMEM = "P0734"; 199 | prog_char P1707[] PROGMEM = "P1707"; 200 | prog_char P1595[] PROGMEM = "P1595"; 201 | prog_char P1596[] PROGMEM = "P1596"; 202 | prog_char P0135[] PROGMEM = "P0135"; 203 | prog_char P0141[] PROGMEM = "P0141"; 204 | prog_char P1593[] PROGMEM = "P1593"; 205 | prog_char P1594[] PROGMEM = "P1594"; 206 | 207 | //0x0000BE and temp 0x00009E 208 | //PROGMEM const char *addr0x0000BE[] = {P0734,P1707,P1595,P1596,P0135,P0141,P1593,P1594}; 209 | 210 | prog_char P0133[] PROGMEM = "P0133"; 211 | prog_char P0130[] PROGMEM = "P0130"; 212 | prog_char P0139[] PROGMEM = "P0139"; 213 | prog_char P0136[] PROGMEM = "P0136"; 214 | prog_char P1152[] PROGMEM = "P1152"; 215 | prog_char P1153[] PROGMEM = "P1153"; 216 | prog_char P0174[] PROGMEM = "P0174"; 217 | prog_char P0175[] PROGMEM = "P0175"; 218 | 219 | //0x0000BF and temp 0x00009F 220 | //PROGMEM const char *addr0x0000BF[] = {P0133,P0130,P0139,P0136,P1152,P1153,P0174,P0175}; 221 | 222 | prog_char P0420[] PROGMEM = "P0420"; 223 | prog_char P0442[] PROGMEM = "P0442"; 224 | prog_char P0170[] PROGMEM = "P0170"; 225 | prog_char P0456[] PROGMEM = "P0456"; 226 | prog_char P0400[] PROGMEM = "P0400"; 227 | prog_char P1230[] PROGMEM = "P1230"; 228 | prog_char P0171[] PROGMEM = "P0171"; 229 | prog_char P0172[] PROGMEM = "P0172"; 230 | 231 | //0x0000C0 and temp 0x0000A0 232 | //PROGMEM const char *addr0x0000C0[] = {P0420,P0442,P0170,P0456,P0400,P1230,P0171,P0172}; 233 | 234 | prog_char P0301[] PROGMEM = "P0301"; 235 | prog_char P0302[] PROGMEM = "P0302"; 236 | prog_char P0303[] PROGMEM = "P0303"; 237 | prog_char P0304[] PROGMEM = "P0304"; 238 | prog_char P0305[] PROGMEM = "P0305"; 239 | prog_char P0306[] PROGMEM = "P0306"; 240 | prog_char P1301[] PROGMEM = "P1301"; 241 | prog_char P0457[] PROGMEM = "P0457"; 242 | 243 | //0x0000C1 and temp 0x0000A1 244 | //PROGMEM const char *addr0x0000C1[] = {P0301,P0302,P0303,P0304,P0305,P0306,P1301,P0457}; 245 | 246 | prog_char P0000[] PROGMEM = "P0000"; 247 | prog_char P1235[] PROGMEM = "P1235"; 248 | prog_char P1236[] PROGMEM = "P1236"; 249 | prog_char P1597[] PROGMEM = "P1597"; 250 | prog_char P1598[] PROGMEM = "P1598"; 251 | prog_char P0034[] PROGMEM = "P0034"; 252 | prog_char P0035[] PROGMEM = "P0035"; 253 | 254 | //0x0000C2 and temp 0x0000A2 255 | //PROGMEM const char *addr0x0000C2[] = {P0000,P0000,P1235,P1236,P1597,P1598,P0034,P0035}; 256 | 257 | prog_char P0137[] PROGMEM = "P0137"; 258 | prog_char P1134[] PROGMEM = "P1134"; 259 | prog_char P0131[] PROGMEM = "P0131"; 260 | prog_char P0151[] PROGMEM = "P0151"; 261 | prog_char P0132[] PROGMEM = "P0132"; 262 | prog_char P0152[] PROGMEM = "P0152"; 263 | prog_char P0138[] PROGMEM = "P0138"; 264 | prog_char P0153[] PROGMEM = "P0153"; 265 | 266 | //0x0000C3 and temp 0x0000A3 267 | //PROGMEM const char *addr0x0000C3[] = {P0137,P1134,P0131,P0151,P0132,P0152,P0138,P0153}; 268 | 269 | prog_char P0112[] PROGMEM = "P0112"; 270 | prog_char P0113[] PROGMEM = "P0113"; 271 | prog_char P0111[] PROGMEM = "P0111"; 272 | prog_char P1546[] PROGMEM = "P1546"; 273 | prog_char P0038[] PROGMEM = "P0038"; 274 | prog_char P0032[] PROGMEM = "P0032"; 275 | prog_char P0037[] PROGMEM = "P0037"; 276 | prog_char P0031[] PROGMEM = "P0031"; 277 | 278 | //0x0000C4 and temp 0x0000A4 279 | //PROGMEM const char *addr0x0000C4[] = {P0112,P0113,P0111,P1546,P0038,P0032,P0037,P0031}; 280 | 281 | prog_char P1110[] PROGMEM = "P1110"; 282 | prog_char P1111[] PROGMEM = "P1111"; 283 | prog_char P1112[] PROGMEM = "P1112"; 284 | //prog_char P0106[] PROGMEM = "P0106"; 285 | //prog_char P0107[] PROGMEM = "P0107"; 286 | //prog_char P0108[] PROGMEM = "P0108"; 287 | prog_char P1545[] PROGMEM = "P1545"; 288 | prog_char P1146[] PROGMEM = "P1146"; 289 | 290 | //0x0000C5 and temp 0x0000A5 291 | //PROGMEM const char *addr0x0000C5[] = {P1110,P1111,P1112,P0106,P0107,P0108,P1545,P1146}; 292 | 293 | prog_char P1139[] PROGMEM = "P1139"; 294 | prog_char P1140[] PROGMEM = "P1140"; 295 | prog_char P1711[] PROGMEM = "P1711"; 296 | prog_char P1712[] PROGMEM = "P1712"; 297 | prog_char P0715[] PROGMEM = "P0715"; 298 | prog_char P1703[] PROGMEM = "P1703"; 299 | prog_char P0785[] PROGMEM = "P0785"; 300 | prog_char P0778[] PROGMEM = "P0778"; 301 | 302 | //0x0000C6 and temp 0x0000A6 303 | //PROGMEM const char *addr0x0000C6[] = {P1139,P1140,P1711,P1712,P0715,P1703,P0785,P0778}; 304 | 305 | prog_char P1130[] PROGMEM = "P1130"; 306 | prog_char P1135[] PROGMEM = "P1135"; 307 | prog_char P1131[] PROGMEM = "P1131"; 308 | prog_char P1136[] PROGMEM = "P1136"; 309 | prog_char P1154[] PROGMEM = "P1154"; 310 | prog_char P1155[] PROGMEM = "P1155"; 311 | prog_char P0052[] PROGMEM = "P0052"; 312 | prog_char P0051[] PROGMEM = "P0051"; 313 | 314 | //0x0000C7 and temp 0x0000A7 315 | //PROGMEM const char *addr0x0000C7[] = {P1130,P1135,P1131,P1136,P1154,P1155,P0052,P0051}; 316 | 317 | prog_char P0128[] PROGMEM = "P0128"; 318 | prog_char P1491[] PROGMEM = "P1491"; 319 | prog_char P0066[] PROGMEM = "P0066"; 320 | prog_char P0067[] PROGMEM = "P0067"; 321 | prog_char P0065[] PROGMEM = "P0065"; 322 | //prog_char P0130[] PROGMEM = "P0130"; 323 | prog_char P1137[] PROGMEM = "P1137"; 324 | 325 | //0x0000C8 and temp 0x0000A8 326 | //PROGMEM const char *addr0x0000C8[] = {P0128,P1491,P0066,P0067,P0065,P0130,P1137,P1137}; 327 | 328 | prog_char P1248[] PROGMEM = "P1248"; 329 | prog_char P1249[] PROGMEM = "P1249"; 330 | prog_char P1250[] PROGMEM = "P1250"; 331 | prog_char P1560[] PROGMEM = "P1560"; 332 | prog_char P0192[] PROGMEM = "P0192"; 333 | prog_char P0193[] PROGMEM = "P0193"; 334 | prog_char P0562[] PROGMEM = "P0562"; 335 | prog_char P0563[] PROGMEM = "P0563"; 336 | 337 | //0x0000C9 and temp 0x0000A9 338 | //PROGMEM const char *addr0x0000C9[] = {P1248,P1249,P1250,P1560,P0192,P0193,P0562,P0563}; 339 | 340 | prog_char P0245[] PROGMEM = "P0245"; 341 | prog_char P0246[] PROGMEM = "P0246"; 342 | prog_char P1244[] PROGMEM = "P1244"; 343 | prog_char P0244[] PROGMEM = "P0244"; 344 | prog_char P1245[] PROGMEM = "P1245"; 345 | prog_char P0249[] PROGMEM = "P0249"; 346 | prog_char P0250[] PROGMEM = "P0250"; 347 | prog_char P1247[] PROGMEM = "P1247"; 348 | 349 | //0x0000CA and temp 0x0000AA 350 | //PROGMEM const char *addr0x0000CA[] = {P0245,P0246,P1244,P0244,P1245,P0249,P0250,P1247}; 351 | 352 | prog_char P1577[] PROGMEM = "P1577"; 353 | prog_char P1576[] PROGMEM = "P1576"; 354 | prog_char P0513[] PROGMEM = "P0513"; 355 | prog_char P1574[] PROGMEM = "P1574"; 356 | prog_char P1578[] PROGMEM = "P1578"; 357 | prog_char P1572[] PROGMEM = "P1572"; 358 | prog_char P1571[] PROGMEM = "P1571"; 359 | prog_char P1570[] PROGMEM = "P1570"; 360 | 361 | //0x0000CB and temp 0x0000AB 362 | //PROGMEM const char *addr0x0000CB[] = {P1577,P1576,P0513,P1574,P1578,P1572,P1571,P1570}; 363 | 364 | prog_char P1095[] PROGMEM = "P1095"; 365 | prog_char P1097[] PROGMEM = "P1097"; 366 | prog_char P1094[] PROGMEM = "P1094"; 367 | prog_char P1096[] PROGMEM = "P1096"; 368 | prog_char P0261[] PROGMEM = "P0261"; 369 | prog_char P0264[] PROGMEM = "P0264"; 370 | prog_char P0267[] PROGMEM = "P0267"; 371 | prog_char P0270[] PROGMEM = "P0270"; 372 | 373 | //0x0000CC and temp 0x0000AC 374 | //PROGMEM const char *addr0x0000CC[] = {P1095,P1097,P1094,P1096,P0261,P0264,P0267,P0270}; 375 | 376 | prog_char P0545[] PROGMEM = "P0545"; 377 | prog_char P0546[] PROGMEM = "P0546"; 378 | prog_char P1312[] PROGMEM = "P1312"; 379 | prog_char P1544[] PROGMEM = "P1544"; 380 | prog_char P1306[] PROGMEM = "P1306"; 381 | prog_char P1308[] PROGMEM = "P1308"; 382 | prog_char P1307[] PROGMEM = "P1307"; 383 | prog_char P1309[] PROGMEM = "P1309"; 384 | 385 | //0x0000CD and temp 0x0000AD 386 | //PROGMEM const char *addr0x0000CD[] = {P0545,P0546,P1312,P1544,P1306,P1308,P1307,P1309}; 387 | 388 | prog_char P1719[] PROGMEM = "P1719"; 389 | prog_char P1242[] PROGMEM = "P1242"; 390 | prog_char P1241[] PROGMEM = "P1241"; 391 | prog_char P1199[] PROGMEM = "P1199"; 392 | prog_char P1086[] PROGMEM = "P1086"; 393 | prog_char P1087[] PROGMEM = "P1087"; 394 | prog_char P1088[] PROGMEM = "P1088"; 395 | prog_char P1089[] PROGMEM = "P1089"; 396 | 397 | //0x0000F4 and temp 0x0000F0 398 | //PROGMEM const char *addr0x0000F4[] = {P1719,P1242,P1241,P1199,P1086,P1087,P1088,P1089}; 399 | 400 | prog_char P0801[] PROGMEM = "P0801"; 401 | prog_char P0768[] PROGMEM = "P0768"; 402 | prog_char P0763[] PROGMEM = "P0763"; 403 | prog_char P0736[] PROGMEM = "P0736"; 404 | prog_char P0724[] PROGMEM = "P0724"; 405 | prog_char P0719[] PROGMEM = "P0719"; 406 | prog_char P0713[] PROGMEM = "P0713"; 407 | prog_char P0712[] PROGMEM = "P0712"; 408 | 409 | //0x0000F5 and temp 0x0000F1 410 | //PROGMEM const char *addr0x0000F5[] = {P0801,P0768,P0763,P0736,P0724,P0719,P0713,P0712}; 411 | 412 | prog_char P1817[] PROGMEM = "P1817"; 413 | prog_char P1762[] PROGMEM = "P1762"; 414 | prog_char P1761[] PROGMEM = "P1761"; 415 | prog_char P1760[] PROGMEM = "P1760"; 416 | prog_char P1718[] PROGMEM = "P1718"; 417 | prog_char P1714[] PROGMEM = "P1714"; 418 | prog_char P1709[] PROGMEM = "P1709"; 419 | prog_char P1708[] PROGMEM = "P1708"; 420 | 421 | //0x0000F6 and temp 0x0000F2 422 | //PROGMEM const char *addr0x0000F6[] = {P1817,P1762,P1761,P1760,P1718,P1714,P1709,P1708}; 423 | 424 | prog_char P1600[] PROGMEM = "P1600"; 425 | prog_char P2709[] PROGMEM = "P2709"; 426 | prog_char P0773[] PROGMEM = "P0773"; 427 | prog_char P0735[] PROGMEM = "P0735"; 428 | prog_char P1799[] PROGMEM = "P1799"; 429 | prog_char P1798[] PROGMEM = "P1798"; 430 | prog_char P1314[] PROGMEM = "P1314"; 431 | prog_char P1313[] PROGMEM = "P1313"; 432 | 433 | //0x0000F7 and temp 0x0000F3 434 | //PROGMEM const char *addr0x0000F7[] = {P1600,P2709,P0773,P0735,P1799,P1798,P1314,P1313}; 435 | 436 | prog_char P1710[] PROGMEM = "P1710"; 437 | prog_char P1717[] PROGMEM = "P1717"; 438 | prog_char P1716[] PROGMEM = "P1716"; 439 | prog_char P0771[] PROGMEM = "P0771"; 440 | prog_char P0716[] PROGMEM = "P0716"; 441 | prog_char P0726[] PROGMEM = "P0726"; 442 | //prog_char P0720[] PROGMEM = "P0720"; 443 | prog_char P0722[] PROGMEM = "P0722"; 444 | 445 | //0x00012B and temp 0x000123 446 | //PROGMEM const char *addr0x00012B[] = {P1710,P1717,P1716,P0771,P0716,P0726,P0720,P0722}; 447 | 448 | prog_char P2707[] PROGMEM = "P2707"; 449 | //prog_char P0771[] PROGMEM = "P0771"; 450 | prog_char P0766[] PROGMEM = "P0766"; 451 | prog_char P0761[] PROGMEM = "P0761"; 452 | prog_char P0756[] PROGMEM = "P0756"; 453 | prog_char P0751[] PROGMEM = "P0751"; 454 | prog_char P1769[] PROGMEM = "P1769"; 455 | prog_char P1282[] PROGMEM = "P1282"; 456 | 457 | //0x00012C and temp 0x000124 458 | //PROGMEM const char *addr0x00012C[] = {P2707,P0771,P0766,P0761,P0756,P0751,P1769,P1282}; 459 | 460 | prog_char P1844[] PROGMEM = "P1844"; 461 | prog_char P1843[] PROGMEM = "P1843"; 462 | prog_char P1842[] PROGMEM = "P1842"; 463 | prog_char P1841[] PROGMEM = "P1841"; 464 | prog_char P1840[] PROGMEM = "P1840"; 465 | prog_char P1875[] PROGMEM = "P1875"; 466 | prog_char P0558[] PROGMEM = "P0558"; 467 | prog_char P0559[] PROGMEM = "P0559"; 468 | 469 | //0x00012D and temp 0x000125 470 | //PROGMEM const char *addr0x00012D[] = {P1844,P1843,P1842,P1841,P1840,P1875,P0558,P0559}; 471 | 472 | prog_char P0502[] PROGMEM = "P0502"; 473 | prog_char P0230[] PROGMEM = "P0230"; 474 | prog_char P0565[] PROGMEM = "P0565"; 475 | prog_char P0068[] PROGMEM = "P0068"; 476 | prog_char P0129[] PROGMEM = "P0129"; 477 | prog_char P0519[] PROGMEM = "P0519"; 478 | prog_char P0345[] PROGMEM = "P0345"; 479 | //prog_char P0101[] PROGMEM = "P0101"; 480 | 481 | //0x00012E and temp 0x000126 482 | //PROGMEM const char *addr0x00012E[] = {P0502,P0230,P0565,P0068,P0129,P0519,P0345,P0101}; 483 | 484 | prog_char P0154[] PROGMEM = "P0154"; 485 | prog_char P0134[] PROGMEM = "P0134"; 486 | prog_char P0150[] PROGMEM = "P0150"; 487 | //prog_char P0130[] PROGMEM = "P0130"; 488 | prog_char P0050[] PROGMEM = "P0050"; 489 | prog_char P0030[] PROGMEM = "P0030"; 490 | prog_char P0501[] PROGMEM = "P0501"; 491 | prog_char P0503[] PROGMEM = "P0503"; 492 | 493 | //0x00012F and temp 0x000127 494 | //PROGMEM const char *addr0x00012F[] = {P0154,P0134,P0150,P0130,P0050,P0030,P0501,P0503}; 495 | 496 | prog_char P0142[] PROGMEM = "P0142"; 497 | prog_char P0145[] PROGMEM = "P0145"; 498 | prog_char P0156[] PROGMEM = "P0156"; 499 | prog_char P0159[] PROGMEM = "P0159"; 500 | prog_char P0162[] PROGMEM = "P0162"; 501 | prog_char P0165[] PROGMEM = "P0165"; 502 | prog_char P0043[] PROGMEM = "P0043"; 503 | prog_char P0044[] PROGMEM = "P0044"; 504 | 505 | //0x000130 and temp 0x000128 506 | //PROGMEM const char *addr0x000130[] = {P0142,P0145,P0156,P0159,P0162,P0165,P0043,P0044}; 507 | 508 | prog_char P0057[] PROGMEM = "P0057"; 509 | prog_char P0058[] PROGMEM = "P0058"; 510 | prog_char P0063[] PROGMEM = "P0063"; 511 | prog_char P0064[] PROGMEM = "P0064"; 512 | prog_char P0157[] PROGMEM = "P0157"; 513 | prog_char P0158[] PROGMEM = "P0158"; 514 | //prog_char P0143[] PROGMEM = "P0143"; 515 | //prog_char P0144[] PROGMEM = "P0144"; 516 | 517 | //0x000131 and temp 0x000129 518 | //PROGMEM const char *addr0x000131[] = {P0057,P0058,P0063,P0064,P0157,P0158,P0143,P0144}; 519 | 520 | prog_char P2109[] PROGMEM = "P2109"; 521 | prog_char P1759[] PROGMEM = "P1759"; 522 | prog_char P1873[] PROGMEM = "P1873"; 523 | prog_char P1872[] PROGMEM = "P1872"; 524 | prog_char P1871[] PROGMEM = "P1871"; 525 | prog_char P1870[] PROGMEM = "P1870"; 526 | prog_char P2125[] PROGMEM = "P2125"; 527 | //prog_char P1700[] PROGMEM = "P1700"; 528 | 529 | //0x000132 and temp 0x00012A 530 | //PROGMEM const char *addr0x000132[] = {P2109,P1759,P1873,P1872,P1871,P1870,P2125,P1700}; 531 | 532 | prog_char P0222[] PROGMEM = "P0222"; 533 | prog_char P0223[] PROGMEM = "P0223"; 534 | prog_char P1160[] PROGMEM = "P1160"; 535 | prog_char P2102[] PROGMEM = "P2102"; 536 | prog_char P2103[] PROGMEM = "P2103"; 537 | prog_char P2101[] PROGMEM = "P2101"; 538 | prog_char P2096[] PROGMEM = "P2096"; 539 | prog_char P0638[] PROGMEM = "P0638"; 540 | 541 | //0x000155 and temp 0x000150 542 | //PROGMEM const char *addr0x000155[] = {P0222,P0223,P1160,P2102,P2103,P2101,P2096,P0638}; 543 | 544 | prog_char P0607[] PROGMEM = "P0607"; 545 | prog_char P2138[] PROGMEM = "P2138"; 546 | prog_char P2127[] PROGMEM = "P2127"; 547 | prog_char P2128[] PROGMEM = "P2128"; 548 | prog_char P2122[] PROGMEM = "P2122"; 549 | prog_char P2123[] PROGMEM = "P2123"; 550 | prog_char P2135[] PROGMEM = "P2135"; 551 | prog_char P2097[] PROGMEM = "P2097"; 552 | 553 | //0x000156 and temp 0x000151 554 | //PROGMEM const char *addr0x000156[] = {P0607,P2138,P2127,P2128,P2122,P2123,P2135,P2097}; 555 | 556 | prog_char P0600[] PROGMEM = "P0600"; 557 | //prog_char P0390[] PROGMEM = "P0390"; 558 | //prog_char P0365[] PROGMEM = "P0365"; 559 | //prog_char P0345[] PROGMEM = "P0345"; 560 | //prog_char P0340[] PROGMEM = "P0340"; 561 | prog_char P0605[] PROGMEM = "P0605"; 562 | prog_char P1521[] PROGMEM = "P1521"; 563 | prog_char P0579[] PROGMEM = "P0579"; 564 | 565 | //0x000157 and temp 0x000152 566 | //PROGMEM const char *addr0x000157[] = {P0600,P0390,P0365,P0345,P0340,P0605,P1521,P0579}; 567 | 568 | prog_char P2095[] PROGMEM = "P2095"; 569 | prog_char P2094[] PROGMEM = "P2094"; 570 | prog_char P2091[] PROGMEM = "P2091"; 571 | prog_char P2090[] PROGMEM = "P2090"; 572 | prog_char P2093[] PROGMEM = "P2093"; 573 | prog_char P2092[] PROGMEM = "P2092"; 574 | prog_char P2089[] PROGMEM = "P2089"; 575 | prog_char P2088[] PROGMEM = "P2088"; 576 | 577 | //0x000158 and temp 0x000153 578 | //PROGMEM const char *addr0x000158[] = {P2095,P2094,P2091,P2090,P2093,P2092,P2089,P2088}; 579 | 580 | prog_char P0197[] PROGMEM = "P0197"; 581 | prog_char P1547[] PROGMEM = "P1547"; 582 | prog_char P1476[] PROGMEM = "P1476"; 583 | prog_char P1475[] PROGMEM = "P1475"; 584 | prog_char P1477[] PROGMEM = "P1477"; 585 | prog_char P2099[] PROGMEM = "P2099"; 586 | prog_char P2098[] PROGMEM = "P2098"; 587 | prog_char P0700[] PROGMEM = "P0700"; 588 | 589 | //0x000159 and temp 0x000154 590 | //PROGMEM const char *addr0x000159[] = {P0197,P1547,P1476,P1475,P1477,P2099,P2098,P0700}; 591 | 592 | prog_char P0028[] PROGMEM = "P0028"; 593 | prog_char P0083[] PROGMEM = "P0083"; 594 | prog_char P0082[] PROGMEM = "P0082"; 595 | prog_char P0026[] PROGMEM = "P0026"; 596 | prog_char P0077[] PROGMEM = "P0077"; 597 | prog_char P0076[] PROGMEM = "P0076"; 598 | prog_char P0196[] PROGMEM = "P0196"; 599 | prog_char P0198[] PROGMEM = "P0198"; 600 | 601 | //0x000165 and temp 0x000160 602 | //PROGMEM const char *addr0x000165[] = {P0028,P0083,P0082,P0026,P0077,P0076,P0196,P0198}; 603 | 604 | prog_char P2100[] PROGMEM = "P2100"; 605 | prog_char P2111[] PROGMEM = "P2111"; 606 | prog_char P2504[] PROGMEM = "P2504"; 607 | prog_char P2503[] PROGMEM = "P2503"; 608 | prog_char P1462[] PROGMEM = "P1462"; 609 | prog_char P1463[] PROGMEM = "P1463"; 610 | prog_char P1028[] PROGMEM = "P1028"; 611 | prog_char P1026[] PROGMEM = "P1026"; 612 | 613 | //0x000166 and temp 0x000161 614 | //PROGMEM const char *addr0x000166[] = {P2100,P2111,P2504,P2503,P1462,P1463,P1028,P1026}; 615 | 616 | prog_char P0958[] PROGMEM = "P0958"; 617 | prog_char P0957[] PROGMEM = "P0957"; 618 | prog_char P0955[] PROGMEM = "P0955"; 619 | prog_char P0883[] PROGMEM = "P0883"; 620 | prog_char P0882[] PROGMEM = "P0882"; 621 | prog_char P0880[] PROGMEM = "P0880"; 622 | prog_char P0817[] PROGMEM = "P0817"; 623 | prog_char P1601[] PROGMEM = "P1601"; 624 | 625 | //0x000167 and temp 0x000162 626 | //PROGMEM const char *addr0x000167[] = {P0958,P0957,P0955,P0883,P0882,P0880,P0817,P1601}; 627 | 628 | prog_char P2004[] PROGMEM = "P2004"; 629 | prog_char P2006[] PROGMEM = "P2006"; 630 | prog_char P2005[] PROGMEM = "P2005"; 631 | prog_char P2007[] PROGMEM = "P2007"; 632 | prog_char P2227[] PROGMEM = "P2227"; 633 | prog_char P0126[] PROGMEM = "P0126"; 634 | prog_char P2229[] PROGMEM = "P2229"; 635 | prog_char P2228[] PROGMEM = "P2228"; 636 | 637 | //0x000168 and temp 0x000163 638 | //PROGMEM const char *addr0x000168[] = {P2004,P2006,P2005,P2007,P2227,P0126,P2229,P2228}; 639 | 640 | prog_char P2016[] PROGMEM = "P2016"; 641 | prog_char P2017[] PROGMEM = "P2017"; 642 | prog_char P2021[] PROGMEM = "P2021"; 643 | prog_char P2022[] PROGMEM = "P2022"; 644 | prog_char P2009[] PROGMEM = "P2009"; 645 | prog_char P2012[] PROGMEM = "P2012"; 646 | prog_char P2008[] PROGMEM = "P2008"; 647 | prog_char P2011[] PROGMEM = "P2011"; 648 | 649 | //0x000169 and temp 0x000164 650 | //PROGMEM const char *addr0x000169[] = {P2016,P2017,P2021,P2022,P2009,P2012,P2008,P2011}; 651 | 652 | //These arrays line up with the address calls to the ECU 653 | PROGMEM const char *DTCset1[][8] = {{P0335,unkn,P0336,P0341,P0340,unkn,P0604,P0601}, 654 | {P0102,P0103,P1141,P0101,P0500,P1540,P0332,P0333}, 655 | {P0325,P0330,P0327,P0328,P0122,P0123,P1142,P0121}, 656 | {P0117,P0118,P0464,P0125,P0462,P0463,P0461,P1442}, 657 | {P0107,P0108,P0143,P0144,P0106,P0350,P1518,P0512}, 658 | {P0452,P0453,unkn,P0451,P1237,P1238,P1239,P1240}, 659 | {P1590,P1591,P1592,P0851,P0182,P0183,P0181,P0852}, 660 | {P1510,P1511,P1512,P1513,P1514,P1515,P1516,P1517}, 661 | {P1492,P1493,P1494,P1495,P1496,P1497,P1498,P1499}, 662 | {P1102,P1122,P1446,P1447,P1090,P1091,P1092,P1093}, 663 | {P0508,P0509,P1507,P0506,P0507,P1698,P1699,P1448}, 664 | {P0444,P0445,P0691,P0692,P0480,P1480,P0483,P0864}, 665 | {P0365,P0390,P0011,P0021,P1400,P1420,P0458,P0459}, 666 | {P0865,P0866,P1443,P1559,P0661,P0662,P0447,P0448}, 667 | {P0720,P0725,P1700,P0710,P0705,P1701,P0703,P0741}, 668 | {P0753,P0758,P1706,P0748,P0743,P0731,P0732,P0733}, 669 | {P0734,P1707,P1595,P1596,P0135,P0141,P1593,P1594}, 670 | {P0133,P0130,P0139,P0136,P1152,P1153,P0174,P0175}, 671 | {P0420,P0442,P0170,P0456,P0400,P1230,P0171,P0172}, 672 | {P0301,P0302,P0303,P0304,P0305,P0306,P1301,P0457}, 673 | {P0000,P0000,P1235,P1236,P1597,P1598,P0034,P0035}, 674 | {P0137,P1134,P0131,P0151,P0132,P0152,P0138,P0153}, 675 | {P0112,P0113,P0111,P1546,P0038,P0032,P0037,P0031}, 676 | {P1110,P1111,P1112,P0106,P0107,P0108,P1545,P1146}, 677 | {P1139,P1140,P1711,P1712,P0715,P1703,P0785,P0778}, 678 | {P1130,P1135,P1131,P1136,P1154,P1155,P0052,P0051}, 679 | {P0128,P1491,P0066,P0067,P0065,P0130,P1137,P1137}, 680 | {P1248,P1249,P1250,P1560,P0192,P0193,P0562,P0563}, 681 | {P0245,P0246,P1244,P0244,P1245,P0249,P0250,P1247}, 682 | {P1577,P1576,P0513,P1574,P1578,P1572,P1571,P1570}, 683 | {P1095,P1097,P1094,P1096,P0261,P0264,P0267,P0270}, 684 | {P0545,P0546,P1312,P1544,P1306,P1308,P1307,P1309}}; 685 | 686 | PROGMEM const char *DTCset2[][8] = {{P1719,P1242,P1241,P1199,P1086,P1087,P1088,P1089}, 687 | {P0801,P0768,P0763,P0736,P0724,P0719,P0713,P0712}, 688 | {P1817,P1762,P1761,P1760,P1718,P1714,P1709,P1708}, 689 | {P1600,P2709,P0773,P0735,P1799,P1798,P1314,P1313}, 690 | {P1710,P1717,P1716,P0771,P0716,P0726,P0720,P0722}, 691 | {P2707,P0771,P0766,P0761,P0756,P0751,P1769,P1282}, 692 | {P1844,P1843,P1842,P1841,P1840,P1875,P0558,P0559}, 693 | {P0502,P0230,P0565,P0068,P0129,P0519,P0345,P0101}, 694 | {P0154,P0134,P0150,P0130,P0050,P0030,P0501,P0503}, 695 | {P0142,P0145,P0156,P0159,P0162,P0165,P0043,P0044}, 696 | {P0057,P0058,P0063,P0064,P0157,P0158,P0143,P0144}, 697 | {P2109,P1759,P1873,P1872,P1871,P1870,P2125,P1700}, 698 | {P0222,P0223,P1160,P2102,P2103,P2101,P2096,P0638}, 699 | {P0607,P2138,P2127,P2128,P2122,P2123,P2135,P2097}, 700 | {P0600,P0390,P0365,P0345,P0340,P0605,P1521,P0579}, 701 | {P2095,P2094,P2091,P2090,P2093,P2092,P2089,P2088}, 702 | {P0197,P1547,P1476,P1475,P1477,P2099,P2098,P0700}, 703 | {P0028,P0083,P0082,P0026,P0077,P0076,P0196,P0198}, 704 | {P2100,P2111,P2504,P2503,P1462,P1463,P1028,P1026}, 705 | {P0958,P0957,P0955,P0883,P0882,P0880,P0817,P1601}, 706 | {P2004,P2006,P2005,P2007,P2227,P0126,P2229,P2228}, 707 | {P2016,P2017,P2021,P2022,P2009,P2012,P2008,P2011}}; 708 | /* 709 | From romraider logger defn 253 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1132 | 1133 | 1134 | 1135 | 1136 | 1137 | 1138 | 1139 | 1140 | 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | 1155 | 1156 | 1157 | 1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | 1166 | 1167 | 1168 | 1169 | 1170 | 1171 | 1172 | 1173 | 1174 | 1175 | 1176 | 1177 | 1178 | 1179 | 1180 | 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | 1187 | 1188 | 1189 | 1190 | 1191 | 1192 | 1193 | 1194 | 1195 | 1196 | 1197 | 1198 | 1199 | 1200 | 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | 1217 | 1218 | 1219 | 1220 | 1221 | 1222 | 1223 | 1224 | 1225 | 1226 | 1227 | 1228 | 1229 | 1230 | 1231 | 1232 | 1233 | 1234 | 1235 | 1236 | 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | 1251 | 1252 | 1253 | 1254 | 1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 1271 | 1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | 1282 | 1283 | 1284 | 1285 | 1286 | 1287 | 1288 | 1289 | 1290 | 1291 | 1292 | 1293 | 1294 | 1295 | 1296 | 1297 | 1298 | 1299 | 1300 | */ 1301 | 1302 | 1303 | 1304 | -------------------------------------------------------------------------------- /WRXClockPodModv2/WRXClockPodModv2.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "RTClib.h" 4 | #include "DTC_arrays.h" 5 | 6 | /* 7 | This code should allow for interfacing with Subaru ECUs 8 | The intention is to pull DTC's and display them on an 9 | LCD. There will also be functionality to display the time 10 | from a clock module and to adjust the time. 11 | 12 | Some of the clock code came from the RTClib ds1307 example 13 | */ 14 | 15 | SoftwareSerial sendSerial = SoftwareSerial(10,11); //Rx, Tx 16 | SoftwareSerial lcd = SoftwareSerial(8,9); 17 | RTC_DS1307 rtc; 18 | //int mem = freeRam(); 19 | 20 | prog_char phrase1[] PROGMEM = " Subaru Impossibru "; 21 | prog_char phrase2[] PROGMEM = "Sit down shut up and hang on "; 22 | prog_char phrase3[] PROGMEM = "Scubaru: beware deep water "; 23 | prog_char phrase4[] PROGMEM = "Guess what will fail today "; 24 | prog_char phrase5[] PROGMEM = " ur a bus "; 25 | prog_char phrase6[] PROGMEM = "Prepare ship for ludicrous speed"; 26 | prog_char phrase7[] PROGMEM = "60% of time it works every time"; 27 | prog_char phrase8[] PROGMEM = " Understeers like a pig "; 28 | prog_char phrase9[] PROGMEM = "The password is 'password' "; 29 | prog_char phrase10[] PROGMEM = " Stop. Hammer time "; 30 | prog_char phrase11[] PROGMEM = "vtec just kicked in yo "; 31 | prog_char phrase12[] PROGMEM = " When in doubt, flat out "; 32 | prog_char phrase13[] PROGMEM = " See no Evo, hear no Evo..."; 33 | prog_char phrase14[] PROGMEM = " Cool story, bro "; 34 | prog_char phrase15[] PROGMEM = " What is that rattling sound?!"; 35 | prog_char phrase16[] PROGMEM = " Made in Australia -_- "; 36 | prog_char phrase17[] PROGMEM = " Power Level: It's over 9000!"; 37 | prog_char phrase18[] PROGMEM = "Do a barrel roll "; 38 | prog_char phrase19[] PROGMEM = " (-_-)zzz "; 39 | prog_char phrase20[] PROGMEM = " Drive free or die "; 40 | prog_char phrase21[] PROGMEM = " Hello Dave "; 41 | prog_char phrase22[] PROGMEM = " It's dangerous to go alone! "; 42 | prog_char phrase23[] PROGMEM = " All your base are belong to us"; 43 | prog_char phrase24[] PROGMEM = " It's super effective! "; 44 | prog_char phrase25[] PROGMEM = " After testing there is cake "; 45 | 46 | PROGMEM const char *phraseSet[] = {phrase1,phrase2,phrase3,phrase4, 47 | phrase5,phrase6,phrase7,phrase8,phrase9,phrase10,phrase11,phrase12, 48 | phrase13,phrase14,phrase15,phrase16,phrase17,phrase18,phrase19, 49 | phrase20,phrase21,phrase22,phrase23,phrase24,phrase25}; 50 | 51 | //ECU variables 52 | //byte ecuInit[6] = {128,16,240,1,191,64}; 53 | byte readDTCTemp1[] = {128,16,240,98,168,0,0,0,142,0,0,143,0,0,144,0,0,145,0,0,146,0,0,147,0,0,148,0,0,149,0,0,150,0,0,151,0,0,152,0,0,153,0,0,154,0,0,155,0,0,156,0,0,157,0,0,158,0,0,159,0,0,160,0,0,161,0,0,162,0,0,163,0,0,164,0,0,165,0,0,166,0,0,167,0,0,168,0,0,169,0,0,170,0,0,171,0,0,172,0,0,173,58}; 54 | byte readDTCTemp2[] = {128,16,240,68,168,0,0,0,240,0,0,241,0,0,242,0,0,243,0,1,35,0,1,36,0,1,37,0,1,38,0,1,39,0,1,40,0,1,41,0,1,42,0,1,80,0,1,81,0,1,82,0,1,83,0,1,84,0,1,96,0,1,97,0,1,98,0,1,99,0,1,100,252}; 55 | byte readDTCMem1[] = {128,16,240,98,168,0,0,0,174,0,0,175,0,0,176,0,0,177,0,0,178,0,0,179,0,0,180,0,0,181,0,0,182,0,0,183,0,0,184,0,0,185,0,0,186,0,0,187,0,0,188,0,0,189,0,0,190,0,0,191,0,0,192,0,0,193,0,0,194,0,0,195,0,0,196,0,0,197,0,0,198,0,0,199,0,0,200,0,0,201,0,0,202,0,0,203,0,0,204,0,0,205,58}; 56 | byte readDTCMem2[] = {128,16,240,68,168,0,0,0,244,0,0,245,0,0,246,0,0,247,0,1,43,0,1,44,0,1,45,0,1,46,0,1,47,0,1,48,0,1,49,0,1,50,0,1,85,0,1,86,0,1,87,0,1,88,0,1,89,0,1,101,0,1,102,0,1,103,0,1,104,0,1,105,126}; 57 | byte pollECUbytes[] = {128,16,240,20,168,0,2,24,32,2,24,33,0,0,18,0,0,32,0,0,14,0,0,15,0}; //response [mrp,mrp,iat,idc,idc,idc] 58 | byte pollECUshort[] = {128,16,240,17,168,0,2,24,32,2,24,33,0,0,32,0,0,14,0,0,15,235}; //response [mrp,mrp,idc,idc,idc] 59 | 60 | int ECUbytes[6] = {0,0,0,0,0,0}; 61 | int DTC[10] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; 62 | int DTCFlag1[][2] = {{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}}; 63 | int DTCFlag2[][2] = {{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1},{-1,-1}}; 64 | 65 | 66 | void setup() 67 | { 68 | Serial.begin(57600); //for diagnostics 69 | lcd.begin(57600); //somehow, this must begin before sendSerial or else ECU comms fail 70 | sendSerial.begin(4800); //SSM uses 4800 8N1 baud rate 71 | 72 | //one time turn off the lcd splash screen 73 | //setSplashScreen(); 74 | 75 | //one time adjust backlight to max 76 | setBacklight(255); 77 | 78 | 79 | //one time change the LCD baud rate to 57600 80 | //lcd.write(0x81); 81 | //lcd.write(9); 82 | //delay(1000); 83 | 84 | Wire.begin(); 85 | rtc.begin(); 86 | lcdStartupMessage(); 87 | 88 | //in order to update the time without pulling the battery, uncomment this code 89 | //upload, then recomment the code and re-upload the code 90 | rtc.adjust(DateTime(__DATE__, __TIME__)); 91 | if (! rtc.isrunning()) { 92 | Serial.println(F("RTC is NOT running!")); 93 | // following line sets the RTC to the date & time this sketch was compiled 94 | rtc.adjust(DateTime(__DATE__, __TIME__)); 95 | setLCDCursor(0); 96 | lcd.print(F("repl. clk batt., re-upload code")); 97 | delay(10000); 98 | clearDisplay(); 99 | 100 | } 101 | 102 | //diagnostic: check free ram 103 | /*Serial.print(F("ram before setup: ")); 104 | Serial.println(mem); 105 | mem = freeRam(); 106 | Serial.print(F("ram in setup: ")); 107 | Serial.println(mem);*/ 108 | } 109 | 110 | 111 | //Variables for ECU communication 112 | //boolean ecuInitCheck = false; 113 | boolean ecuPollCheck = true; 114 | boolean MILon = false; 115 | boolean MILread = false; 116 | int IATbyte = 0; 117 | int IDCbyteIPW = 0; 118 | int IDCbyteRPM1 = 0; 119 | int IDCbyteRPM2 = 0; 120 | int MRPbyteAtm = 0; 121 | int MRPbyteMani = 0; 122 | 123 | //Timekeeping variables 124 | byte minTimeNext = 0; 125 | //unsigned long time; 126 | void loop() 127 | { 128 | /*mem = freeRam(); 129 | Serial.print(F("ram in main: ")); 130 | Serial.println(mem);*/ 131 | //time = micros(); 132 | //Serial.println(time); 133 | 134 | if(MILread == false) 135 | { 136 | Serial.println(F("Reading CELs")); 137 | writeSSM(readDTCTemp1, 103, sendSerial); 138 | readECU(DTC, 10, true); 139 | 140 | //readDTCArray(); 141 | if(DTC[0] != -1){ 142 | DTCread(DTC, 10, DTCFlag1, 10, 1); 143 | resetDTCArray(); 144 | MILon = true; 145 | } 146 | writeSSM(readDTCTemp2, 73, sendSerial); 147 | readECU(DTC, 10, true); 148 | 149 | //readDTCArray(); 150 | //readDTCFlag(); 151 | if(DTC[0] != -1){ 152 | DTCread(DTC, 10, DTCFlag2, 10, 2); 153 | resetDTCArray(); 154 | MILon = true; 155 | } 156 | 157 | writeSSM(readDTCMem1, 103, sendSerial); 158 | readECU(DTC, 10, true); 159 | 160 | //readDTCArray(); 161 | //readDTCFlag(); 162 | if(DTC[0] != -1){ 163 | DTCread(DTC, 10, DTCFlag1, 10, 1); 164 | resetDTCArray(); 165 | MILon = true; 166 | } 167 | 168 | writeSSM(readDTCMem2, 73, sendSerial); 169 | readECU(DTC, 10, true); 170 | 171 | //readDTCArray(); 172 | //readDTCFlag(); 173 | if(DTC[0] != -1){ 174 | DTCread(DTC, 10, DTCFlag2, 10, 2); 175 | resetDTCArray(); 176 | MILon = true; 177 | } 178 | 179 | MILread = true; 180 | delay(1000); 181 | sweepClearLCD(); 182 | } 183 | if(MILon == true && MILread == true) 184 | { 185 | Serial.println(F("Displaying CELs")); 186 | DTCupdate(DTCFlag1, 10, 1); 187 | delay(2000); 188 | DTCupdate(DTCFlag2, 10, 2); 189 | delay(2000); 190 | } 191 | else 192 | { 193 | //We'll first get the clock information onto the first 5 LCD spots 194 | updateTime(); 195 | //Serial.println(F("polling ecu")); 196 | writeSSM(pollECUbytes, 25, sendSerial); 197 | readECU(ECUbytes, 6, false); 198 | //interpret and display the ECU data on the LCD 199 | MRPbyteAtm = ECUbytes[0]; 200 | //Serial.print(F("MRPbyteAtm: ")); 201 | //Serial.println(MRPbyteAtm); 202 | MRPbyteMani = ECUbytes[1]; 203 | //Serial.print(F("MRPbyteMani: ")); 204 | //Serial.println(MRPbyteMani); 205 | IATbyte = ECUbytes[2]; 206 | //Serial.print(F("IATbyte: ")); 207 | //Serial.println(IATbyte); 208 | IDCbyteIPW = ECUbytes[3]; 209 | //Serial.print(F("IDCbyteIPW: ")); 210 | //Serial.println(IDCbyteIPW); 211 | IDCbyteRPM1 = ECUbytes[4]; 212 | //Serial.print(F("IDCbyteRPM1: ")); 213 | //Serial.println(IDCbyteRPM1); 214 | IDCbyteRPM2 = ECUbytes[5]; 215 | //Serial.print(F("IDCbyteRPM2: ")); 216 | //Serial.println(IDCbyteRPM2); 217 | 218 | int IAT = computeIAT(IATbyte); 219 | updateTemp(IAT); 220 | 221 | int IDC = computeIDC(IDCbyteIPW, IDCbyteRPM1, IDCbyteRPM2); 222 | updateIDC(IDC); 223 | 224 | float MRP = computeMRP(MRPbyteAtm, MRPbyteMani); 225 | updateBoost(MRP); 226 | 227 | 228 | for(int count = 0; count < 884; count++) //about 60s loop 229 | { 230 | //time = micros(); 231 | //Serial.println(time); 232 | writeSSM(pollECUshort, 22, sendSerial); 233 | readECU(ECUbytes, 6, false); 234 | //interpret and display the ECU data on the LCD 235 | MRPbyteAtm = ECUbytes[0]; 236 | //Serial.print(F("MRPbyteAtm: ")); 237 | //Serial.println(MRPbyteAtm); 238 | MRPbyteMani = ECUbytes[1]; 239 | //Serial.print(F("MRPbyteMani: ")); 240 | //Serial.println(MRPbyteMani); 241 | IDCbyteIPW = ECUbytes[2]; 242 | //Serial.print(F("IDCbyteIPW: ")); 243 | //Serial.println(IDCbyteIPW); 244 | IDCbyteRPM1 = ECUbytes[3]; 245 | //Serial.print(F("IDCbyteRPM1: ")); 246 | //Serial.println(IDCbyteRPM1); 247 | IDCbyteRPM2 = ECUbytes[4]; 248 | //Serial.print(F("IDCbyteRPM2: ")); 249 | //Serial.println(IDCbyteRPM2); 250 | 251 | int IDC = computeIDC(IDCbyteIPW, IDCbyteRPM1, IDCbyteRPM2); 252 | updateIDC(IDC); 253 | 254 | float MRP = computeMRP(MRPbyteAtm, MRPbyteMani); 255 | updateBoost(MRP); 256 | } 257 | } 258 | 259 | } 260 | 261 | 262 | /* returns the 8 least significant bits of an input byte*/ 263 | byte CheckSum(byte sum){ 264 | byte counter = 0; 265 | byte power = 1; 266 | for(byte n = 0; n < 8; n++){ 267 | counter += bitRead(sum,n)*power; 268 | power = power*2; 269 | } 270 | return counter; 271 | } 272 | 273 | /*writes data over the software serial port 274 | the &digiSerial passes a reference to the external 275 | object so that we can control it outside of the function*/ 276 | void writeSSM(byte data[], byte length, SoftwareSerial &digiSerial){ 277 | //Serial.println(F("Sending packet... ")); 278 | for(byte x = 0; x < length; x++){ 279 | digiSerial.write(data[x]); 280 | } 281 | //Serial.println(F("done sending.")); 282 | } 283 | 284 | /*LCD Functions borrowed from: https://www.sparkfun.com/tutorials/289 285 | Some other character options: 286 | 0×0D, will advance the cursor to the next row 287 | 0×09, will advance the cursor 5 places 288 | 0×08, will operate as a standard backspace 289 | */ 290 | void setBacklight(byte brightness) 291 | { 292 | lcd.write(0x80); // send the backlight command, decimal 128 293 | lcd.write(brightness); // send the brightness value 0-255 294 | } 295 | 296 | void clearDisplay() 297 | { 298 | lcd.write(0xFE); // send the special command 299 | lcd.write(0x01); // send the clear screen command 300 | } 301 | 302 | void setLCDCursor(byte cursor_position) 303 | { 304 | lcd.write(0xFE); // send the special command 305 | lcd.write(0x80); // send the set cursor command 306 | lcd.write(cursor_position); // send the cursor position 0-31, 0 is top left 307 | } 308 | 309 | void setSplashScreen() 310 | { 311 | lcd.write(0xFE); // send the special command 312 | lcd.write(0x1E); // toggle the splash screen appearance 313 | } 314 | 315 | void lcdStartupMessage() 316 | { 317 | Serial.println(F("Displaying Welcome Message")); 318 | clearDisplay(); 319 | 320 | 321 | DateTime now = rtc.now(); 322 | byte phraseSeed = now.second(); 323 | byte animationSeed = now.minute(); 324 | 325 | randomSeed(phraseSeed); 326 | long phrasePick = random(0,25); 327 | randomSeed(animationSeed); 328 | long animationPick = random(0,4); 329 | char buffer[32]; 330 | 331 | strcpy_P(buffer, (char*)pgm_read_word(&(phraseSet[phrasePick]))); 332 | Serial.println(buffer); 333 | 334 | if(animationPick == 0){ 335 | for(byte x = 0; x < 16; x++) 336 | { 337 | setLCDCursor(x); 338 | lcd.print(buffer[x]); 339 | delay(30); 340 | setLCDCursor(31-x); 341 | lcd.print(buffer[31-x]); 342 | delay(30); 343 | } 344 | } 345 | if(animationPick == 1){ 346 | for(byte x = 0; x < 8; x++) 347 | { 348 | setLCDCursor(x + 8); 349 | lcd.print(buffer[x + 8]); 350 | delay(30); 351 | setLCDCursor(x + 24); 352 | lcd.print(buffer[x + 24]); 353 | delay(30); 354 | setLCDCursor(7 - x); 355 | lcd.print(buffer[7 - x]); 356 | delay(30); 357 | setLCDCursor(23 - x); 358 | lcd.print(buffer[23 - x]); 359 | delay(30); 360 | } 361 | } 362 | if(animationPick == 2){ 363 | int x[32] = {12,16,21,3,17,4,15,22,11,24,25,5,14,1,31,18,2,6,30,29, 364 | 20,23,7,26,9,28,8,10,13,19,27,0}; 365 | for(int y = 0; y < 32; y++){ 366 | setLCDCursor(x[y]); 367 | lcd.print(buffer[x[y]]); 368 | delay(30); 369 | } 370 | } 371 | if(animationPick == 3){ 372 | for(int x = 15; x > -1; x--) 373 | { 374 | setLCDCursor(x); 375 | lcd.print(buffer[x]); 376 | delay(30); 377 | } 378 | for(int x = 16; x < 32; x++) 379 | { 380 | setLCDCursor(x); 381 | lcd.print(buffer[x]); 382 | delay(30); 383 | } 384 | } 385 | } 386 | 387 | void sweepClearLCD() 388 | { 389 | for(int x = 0; x < 16; x++){ 390 | setLCDCursor(x); 391 | lcd.print((char)255); //0xFF, the filled space character 392 | delay(30); 393 | if(x > 0){ 394 | setLCDCursor(x + 15); 395 | lcd.print((char)255); //0xFF, the filled space character 396 | } 397 | setLCDCursor(x); 398 | lcd.print(F(" ")); 399 | delay(30); 400 | if(x > 0){ 401 | setLCDCursor(x + 15); 402 | lcd.print(F(" ")); 403 | } 404 | } 405 | } 406 | /*********************************************************/ 407 | 408 | void updateTime() 409 | { 410 | DateTime now = rtc.now(); 411 | byte hourTime = now.hour(); 412 | byte minTime = now.minute(); 413 | 414 | if(minTimeNext != minTime){ 415 | clearDisplay(); 416 | setLCDCursor(0); 417 | if(hourTime > 12){ 418 | hourTime = hourTime - 12; 419 | lcd.print(F(" ")); 420 | } 421 | lcd.print(hourTime); 422 | Serial.print(hourTime); 423 | lcd.print(':'); 424 | Serial.print(':'); 425 | if(minTime < 10) 426 | lcd.print(F("0")); 427 | lcd.print(minTime); 428 | lcd.print(F(" ")); 429 | Serial.println(minTime); 430 | minTimeNext = minTime; 431 | } 432 | } 433 | 434 | void updateBoost(float boostValue) 435 | { 436 | if(boostValue > 15) 437 | boostValue = 15; 438 | //display the boost value on the lower left of the lcd 439 | setLCDCursor(16); 440 | if(boostValue >= 0.0 && boostValue < 10.0) 441 | { 442 | lcd.print(F(" ")); 443 | lcd.print(boostValue,1); //print one digit of the float value 444 | //Serial.print(boostValue); 445 | } 446 | else 447 | { 448 | lcd.print(boostValue,1); 449 | //Serial.print(boostValue); 450 | } 451 | setLCDCursor(20); 452 | lcd.print(F("psi")); 453 | //Serial.println(F("psi")); 454 | /*display graphical gauge on lower right, 455 | using blocks "0xFF" and a bar "0x7C" at 0 456 | 0xFE clears a cell*/ 457 | setLCDCursor(23); 458 | lcd.print(F(" ")); //clear the plot 459 | int value = ((int)boostValue+11) / 3; 460 | for(int y = 0; y <= value; y++) 461 | { 462 | setLCDCursor(31-y); 463 | lcd.print((char)255); //0xFF, the filled space character 464 | } 465 | setLCDCursor(28); 466 | lcd.print((char)124); //0x7C, the | character 467 | } 468 | 469 | void updateTemp(int tempValue) 470 | { 471 | setLCDCursor(6); 472 | if(tempValue >= 0) 473 | lcd.print(F(" ")); 474 | lcd.print(tempValue); 475 | lcd.print((char)223); //degree sign 476 | lcd.print(F("C ")); 477 | //Serial.print(tempValue); 478 | //Serial.println(F("C")); 479 | } 480 | 481 | void updateIDC(int idcValue) 482 | { 483 | setLCDCursor(12); 484 | if(idcValue < 100) 485 | { 486 | lcd.print(F(" ")); 487 | } 488 | if(idcValue < 10) 489 | { 490 | lcd.print(F(" ")); 491 | } 492 | lcd.print(idcValue); 493 | lcd.print((char)37); //percent character 494 | //Serial.print(idcValue); 495 | //Serial.println(F("%")); 496 | } 497 | 498 | 499 | /* ********************************************************* 500 | Packets must follow this structure: 501 | 502 | 0x80 503 | Destination byte 504 | Source byte 505 | Data size byte 506 | data.. 507 | 508 | Checksum byte 509 | 510 | Data size byte specities the number of data bytes in 511 | the packet 512 | 513 | Checksum byte is the 8 least significant bits of the 514 | sum of every packet byte (including the header) 515 | 516 | see: http://www.romraider.com/RomRaider/SsmProtocol 517 | ************************************************************ */ 518 | //this will change the values in dataArray, populating them with values respective of the poll array address calls 519 | boolean readECU(int* dataArray, byte dataArrayLength, boolean nonZeroes) 520 | { 521 | byte data = 0; 522 | boolean isPacket = false; 523 | byte sumBytes = 0; 524 | byte checkSumByte = 0; 525 | byte dataSize = 0; 526 | byte bytePlace = 0; 527 | byte zeroesLoopSpot = 0; 528 | byte loopLength = 10; 529 | for(byte j = 0; j < loopLength; j++) 530 | { 531 | //mem = freeRam(); 532 | //Serial.print(F("ram in readECU: ")); 533 | //Serial.println(mem); 534 | 535 | data = sendSerial.read(); 536 | Serial.print(F("data: ")); //diagnostic: shows any data stream 537 | Serial.println(data); //somehow, the delay? from these diagnostic lines allows the program to work.. 538 | 539 | if(data == 128 && dataSize == 0){ //0x80 or 128 marks the beginning of a packet 540 | isPacket = true; 541 | //Serial.println(F("Begin Packet")); 542 | j = 0; 543 | } 544 | 545 | //terminate function and return false if no response is detected 546 | if(j == (loopLength - 1) && isPacket != true) 547 | { 548 | //Serial.println(F("booted 1")); 549 | return false; 550 | } 551 | 552 | if(isPacket == true && data != -1){ 553 | //Serial.println(data); // for debugging: shows in-packet data 554 | 555 | if(bytePlace == 3){ // how much data is coming 556 | dataSize = data; 557 | //Serial.print(F("dataSize: ")); 558 | //Serial.println(dataSize); 559 | loopLength = data + 6; 560 | } 561 | 562 | if(bytePlace > 4 && bytePlace-5 < dataArrayLength && nonZeroes == false) 563 | { 564 | dataArray[bytePlace-5] = data; 565 | } 566 | else if(bytePlace > 4 && zeroesLoopSpot < dataArrayLength/2 && nonZeroes == true && data != 0 && bytePlace < dataSize + 4) 567 | { 568 | dataArray[zeroesLoopSpot] = data; 569 | dataArray[zeroesLoopSpot + (dataArrayLength / 2)] = bytePlace; 570 | zeroesLoopSpot++; 571 | } 572 | 573 | bytePlace += 1; //increment bytePlace 574 | 575 | //once the data is all recieved, checksum and re-set counters 576 | if(bytePlace == dataSize+5){ 577 | checkSumByte = CheckSum(sumBytes); //the 8 least significant bits of sumBytes 578 | 579 | if(data != checkSumByte){ 580 | //Serial.println(F("checksum error")); 581 | return false; 582 | } 583 | 584 | isPacket = false; 585 | sumBytes = 0; 586 | bytePlace = 0; 587 | checkSumByte = 0; 588 | dataSize = 0; 589 | //Serial.println(F("End Packet")); 590 | return true; 591 | } 592 | else{ 593 | sumBytes += data; // this is to compare with the checksum byte 594 | //Serial.print(F("sum: ")); 595 | //Serial.println(sumBytes); 596 | } 597 | } 598 | } 599 | } 600 | 601 | //input the DTC flags, with half the array as the DTC flag number and the other half as the address 602 | //This function will change DTCs[] to include the error code addresses: [address][byte], but not overwrite or duplicate values 603 | void DTCread(int flags[], int flagLength, int DTCs[][2], int DTCsLength, int DTCTableNumber) 604 | { 605 | int DTCconvoluted = 0; 606 | int DTCaddr = 0; 607 | int index = 0; 608 | boolean duplicate = false; 609 | 610 | for(int plus = 0; plus < DTCsLength; plus++); 611 | { 612 | if(DTCs[index][0] != -1) 613 | index++; 614 | } 615 | 616 | for(int i = 0; i < flagLength/2; i++) 617 | { 618 | if(flags[i] != -1) 619 | { 620 | DTCconvoluted = flags[i]; 621 | DTCaddr = flags[i + flagLength/2] - 5; 622 | for(int f = 0; f < 8; f++) 623 | { 624 | if(bitRead(DTCconvoluted, f) != 0 && index < DTCsLength) 625 | { 626 | //loop to check if this value is already present in DTCs 627 | for(int pres = 0; pres < DTCsLength; pres++) 628 | { 629 | if(DTCs[pres][0] == DTCaddr && DTCs[pres][1] == f) 630 | { 631 | duplicate = true; 632 | } 633 | } 634 | if(duplicate == false) 635 | { 636 | DTCs[index][0] = DTCaddr; 637 | DTCs[index][1] = f; 638 | index++; 639 | } 640 | duplicate = false; 641 | } 642 | } 643 | } 644 | } 645 | //DTCupdate(DTCs, DTCsLength, DTCTableNumber); 646 | //delay(2000); 647 | } 648 | 649 | //This function takes in an array of DTC addresses, and reads off the string values from PROGMEM 650 | //Finally, it displays them on an LCD and the serial monitor. Values of '-1' are ignored. DTCTableNumber is 1 or 2. 651 | void DTCupdate(int DTCs[][2], byte DTCsLength, byte DTCTableNumber) 652 | { 653 | char buffer[5]; 654 | byte DTCcount = 0; 655 | setLCDCursor(6+DTCcount); 656 | 657 | for(byte a = 0; a < DTCsLength; a++) 658 | { 659 | byte b = DTCs[a][0]; 660 | byte c = DTCs[a][1]; 661 | if(DTCs[a][0] != -1 && DTCTableNumber == 1) 662 | { 663 | strcpy_P(buffer, (char*)pgm_read_word(&(DTCset1[b][c]))); 664 | Serial.println(buffer); 665 | lcd.print(buffer); 666 | DTCcount += 6; 667 | } 668 | else if(DTCs[a][0] != -1 && DTCTableNumber == 2) 669 | { 670 | strcpy_P(buffer, (char*)pgm_read_word(&(DTCset2[b][c]))); 671 | Serial.println(buffer); 672 | lcd.print(buffer); 673 | DTCcount += 6; 674 | } 675 | } 676 | } 677 | 678 | int computeIAT(int IAT) 679 | { 680 | int temperature = IAT - 40; 681 | return temperature; 682 | } 683 | 684 | float computeMRP(int boost1, int boost2) 685 | { 686 | int boostCor = ((boost1*256 + boost2) - 32768); 687 | float MRP = boostCor*0.01933677; 688 | return MRP; 689 | } 690 | 691 | int computeIDC(int IPW, int rpm1, int rpm2) 692 | { 693 | /* 694 | rpm = (rpm1*256+rpm2)/4 695 | IPW = IPW*256/1000 696 | IDC = rpm*IPW/1200 697 | */ 698 | 699 | float rpm = ((rpm1*256) + rpm2)/4; 700 | float IPWcor = IPW*256/1000; 701 | int IDC = (IPWcor*rpm)/(1200); 702 | return IDC; 703 | } 704 | 705 | //returns the amount of free memory for variable use out of the 2k on the UNO 706 | //from: https://learn.adafruit.com/memories-of-an-arduino/measuring-free-memory 707 | int freeRam () 708 | { 709 | extern int __heap_start, *__brkval; 710 | int v; 711 | return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 712 | } 713 | 714 | void readDTCArray() 715 | { 716 | Serial.print(F("(")); 717 | for(int x = 0; x < 10; x++) 718 | { 719 | Serial.print(DTC[x]); 720 | if(x < 9) 721 | Serial.print(F(",")); 722 | } 723 | Serial.println(F(")")); 724 | } 725 | 726 | void readDTCFlag() 727 | { 728 | Serial.print(F("(")); 729 | for(int x = 0; x < 10; x++) 730 | { 731 | Serial.print(F("(")); 732 | for(int y = 0; y < 2; y++) 733 | { 734 | Serial.print(DTCFlag1[x][y]); 735 | if(y < 1) 736 | Serial.print(F(",")); 737 | } 738 | if(x < 9) 739 | Serial.print(F("),")); 740 | } 741 | Serial.println(F("))")); 742 | } 743 | 744 | void resetDTCArray() 745 | { 746 | for(int x = 0; x < 10; x++) 747 | { 748 | DTC[x] = -1; 749 | } 750 | } 751 | -------------------------------------------------------------------------------- /WRXClockPodModv3.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "RTClib.h" 4 | #include "DTC_arrays.h" 5 | 6 | /* 7 | This code should allow for interfacing with Subaru ECUs 8 | The intention is to pull DTC's and display them on an 9 | LCD. There will also be functionality to display the time 10 | from a clock module and to adjust the time. 11 | 12 | Some of the clock code came from the RTClib ds1307 example 13 | */ 14 | 15 | SoftwareSerial sendSerial = SoftwareSerial(10, 11); //Rx, Tx 16 | SoftwareSerial lcd = SoftwareSerial(8, 9); 17 | RTC_DS1307 rtc; 18 | //int mem = freeRam(); 19 | 20 | const char phrase1[] PROGMEM = " Subaru Impossibru "; 21 | const char phrase2[] PROGMEM = "Sit down shut up and hang on "; 22 | const char phrase3[] PROGMEM = "Scubaru: beware deep water "; 23 | const char phrase4[] PROGMEM = "Guess what will fail today "; 24 | const char phrase5[] PROGMEM = " ur a bus "; 25 | const char phrase6[] PROGMEM = "Prepare ship for ludicrous speed"; 26 | const char phrase7[] PROGMEM = "60% of time it works every time"; 27 | const char phrase8[] PROGMEM = " Understeers like a pig "; 28 | const char phrase9[] PROGMEM = "The password is 'password' "; 29 | const char phrase10[] PROGMEM = " Stop. Hammer time "; 30 | const char phrase11[] PROGMEM = "vtec just kicked in yo "; 31 | const char phrase12[] PROGMEM = " When in doubt, flat out "; 32 | const char phrase13[] PROGMEM = " See no Evo, hear no Evo..."; 33 | const char phrase14[] PROGMEM = " Cool story, bro "; 34 | const char phrase15[] PROGMEM = " What is that rattling sound?!"; 35 | const char phrase16[] PROGMEM = " Made in Australia -_- "; 36 | const char phrase17[] PROGMEM = " Power Level: It's over 9000!"; 37 | const char phrase18[] PROGMEM = "Do a barrel roll "; 38 | const char phrase19[] PROGMEM = " (-_-)zzz "; 39 | const char phrase20[] PROGMEM = " Drive free or die "; 40 | const char phrase21[] PROGMEM = " Hello Dave "; 41 | const char phrase22[] PROGMEM = " It's dangerous to go alone! "; 42 | const char phrase23[] PROGMEM = " All your base are belong to us"; 43 | const char phrase24[] PROGMEM = " It's super effective! "; 44 | const char phrase25[] PROGMEM = " After testing there is cake "; 45 | 46 | PGM_P const phraseSet[] PROGMEM = {phrase1, phrase2, phrase3, phrase4, 47 | phrase5, phrase6, phrase7, phrase8, phrase9, phrase10, phrase11, phrase12, 48 | phrase13, phrase14, phrase15, phrase16, phrase17, phrase18, phrase19, 49 | phrase20, phrase21, phrase22, phrase23, phrase24, phrase25 50 | }; 51 | 52 | //ECU variables 53 | //byte ecuInit[6] = {128,16,240,1,191,64}; 54 | byte readDTCTemp1[] = {128, 16, 240, 98, 168, 0, 0, 0, 142, 0, 0, 143, 0, 0, 144, 0, 0, 145, 0, 0, 146, 0, 0, 147, 0, 0, 148, 0, 0, 149, 0, 0, 150, 0, 0, 151, 0, 0, 152, 0, 0, 153, 0, 0, 154, 0, 0, 155, 0, 0, 156, 0, 0, 157, 0, 0, 158, 0, 0, 159, 0, 0, 160, 0, 0, 161, 0, 0, 162, 0, 0, 163, 0, 0, 164, 0, 0, 165, 0, 0, 166, 0, 0, 167, 0, 0, 168, 0, 0, 169, 0, 0, 170, 0, 0, 171, 0, 0, 172, 0, 0, 173, 58}; 55 | byte readDTCTemp2[] = {128, 16, 240, 68, 168, 0, 0, 0, 240, 0, 0, 241, 0, 0, 242, 0, 0, 243, 0, 1, 35, 0, 1, 36, 0, 1, 37, 0, 1, 38, 0, 1, 39, 0, 1, 40, 0, 1, 41, 0, 1, 42, 0, 1, 80, 0, 1, 81, 0, 1, 82, 0, 1, 83, 0, 1, 84, 0, 1, 96, 0, 1, 97, 0, 1, 98, 0, 1, 99, 0, 1, 100, 252}; 56 | byte readDTCMem1[] = {128, 16, 240, 98, 168, 0, 0, 0, 174, 0, 0, 175, 0, 0, 176, 0, 0, 177, 0, 0, 178, 0, 0, 179, 0, 0, 180, 0, 0, 181, 0, 0, 182, 0, 0, 183, 0, 0, 184, 0, 0, 185, 0, 0, 186, 0, 0, 187, 0, 0, 188, 0, 0, 189, 0, 0, 190, 0, 0, 191, 0, 0, 192, 0, 0, 193, 0, 0, 194, 0, 0, 195, 0, 0, 196, 0, 0, 197, 0, 0, 198, 0, 0, 199, 0, 0, 200, 0, 0, 201, 0, 0, 202, 0, 0, 203, 0, 0, 204, 0, 0, 205, 58}; 57 | byte readDTCMem2[] = {128, 16, 240, 68, 168, 0, 0, 0, 244, 0, 0, 245, 0, 0, 246, 0, 0, 247, 0, 1, 43, 0, 1, 44, 0, 1, 45, 0, 1, 46, 0, 1, 47, 0, 1, 48, 0, 1, 49, 0, 1, 50, 0, 1, 85, 0, 1, 86, 0, 1, 87, 0, 1, 88, 0, 1, 89, 0, 1, 101, 0, 1, 102, 0, 1, 103, 0, 1, 104, 0, 1, 105, 126}; 58 | byte pollECUbytes[] = {128, 16, 240, 20, 168, 0, 2, 24, 32, 2, 24, 33, 0, 0, 18, 0, 0, 32, 0, 0, 14, 0, 0, 15, 0}; //response [mrp,mrp,iat,idc,idc,idc] 59 | byte pollECUshort[] = {128, 16, 240, 17, 168, 0, 2, 24, 32, 2, 24, 33, 0, 0, 32, 0, 0, 14, 0, 0, 15, 235}; //response [mrp,mrp,idc,idc,idc] 60 | 61 | int ECUbytes[6] = {0, 0, 0, 0, 0, 0}; 62 | int DTC[10] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; 63 | int DTCFlag1[][2] = {{ -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}}; 64 | int DTCFlag2[][2] = {{ -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}, { -1, -1}}; 65 | 66 | //Variables for ECU communication 67 | boolean ecuPollCheck = true; 68 | boolean MILon = false; 69 | boolean scanning = true; 70 | byte breakScanning = 0; 71 | int IATbyte = 0; 72 | int IDCbyteIPW = 0; 73 | int IDCbyteRPM1 = 0; 74 | int IDCbyteRPM2 = 0; 75 | int MRPbyteAtm = 0; 76 | int MRPbyteMani = 0; 77 | 78 | //Timekeeping variables 79 | byte minTimeNext = 0; 80 | //unsigned long time; 81 | 82 | 83 | void setup() 84 | { 85 | Serial.begin(57600); //for diagnostics 86 | lcd.begin(57600); //somehow, this must begin before sendSerial or else ECU comms fail 87 | sendSerial.begin(4800); //SSM uses 4800 8N1 baud rate 88 | 89 | //one time turn off the lcd splash screen 90 | //setSplashScreen(); 91 | 92 | //one time adjust backlight to max 93 | setBacklight(255); 94 | 95 | 96 | //one time change the LCD baud rate to 57600 97 | //lcd.write(0x81); 98 | //lcd.write(9); 99 | //delay(1000); 100 | 101 | Wire.begin(); 102 | rtc.begin(); 103 | lcdStartupMessage(); 104 | 105 | //in order to update the time without pulling the battery, uncomment this code 106 | //upload, then recomment the code and re-upload the code 107 | //rtc.adjust(DateTime(__DATE__, __TIME__)); 108 | if (! rtc.isrunning()) { 109 | Serial.println(F("RTC is NOT running!")); 110 | // following line sets the RTC to the date & time this sketch was compiled 111 | rtc.adjust(DateTime(__DATE__, __TIME__)); 112 | setLCDCursor(0); 113 | lcd.print(F("repl. clk batt., re-upload code")); 114 | delay(10000); 115 | clearDisplay(); 116 | 117 | } 118 | 119 | //diagnostic: check free ram 120 | /*Serial.print(F("ram before setup: ")); 121 | Serial.println(mem); 122 | mem = freeRam(); 123 | Serial.print(F("ram in setup: ")); 124 | Serial.println(mem);*/ 125 | 126 | 127 | //scan for CELs 128 | checkForCEL(); 129 | } 130 | 131 | 132 | 133 | void loop() 134 | { 135 | /*mem = freeRam(); 136 | Serial.print(F("ram in main: ")); 137 | Serial.println(mem);*/ 138 | //time = micros(); 139 | //Serial.println(time); 140 | 141 | 142 | if (scanning == true) 143 | { 144 | //We'll first get the clock information onto the first 5 LCD spots 145 | if (updateTime()) 146 | { 147 | //then, if the time has changed, poll and read the ECU including the temperature 148 | if (!scanWithTemp()) 149 | { 150 | //if the checksum fails more than 10 times in the same minute, stop scanning 151 | if (scanBreaker()) 152 | { 153 | scanning = false; 154 | sweepClearLCD(); 155 | } 156 | } 157 | } 158 | else 159 | //speed up things by not asking for the temperature reading 160 | { 161 | //time counters to optimize the code 162 | //time = micros(); 163 | //Serial.println(time); 164 | if (!scanWithoutTemp()) 165 | { 166 | //if the checksum fails more than 10 times in the same minute, stop scanning 167 | if (scanBreaker()) 168 | { 169 | scanning = false; 170 | sweepClearLCD(); 171 | } 172 | } 173 | 174 | } 175 | 176 | } 177 | else 178 | { 179 | updateTime(); 180 | } 181 | 182 | } 183 | 184 | boolean scanBreaker() 185 | { 186 | if (!updateTime()) 187 | breakScanning++; 188 | else 189 | breakScanning = 0; 190 | 191 | if (breakScanning >= 10) 192 | return true; 193 | else 194 | return false; 195 | } 196 | 197 | /* returns the 8 least significant bits of an input byte*/ 198 | byte CheckSum(byte sum) { 199 | byte counter = 0; 200 | byte power = 1; 201 | for (byte n = 0; n < 8; n++) { 202 | counter += bitRead(sum, n) * power; 203 | power = power * 2; 204 | } 205 | return counter; 206 | } 207 | 208 | boolean scanWithTemp() 209 | { 210 | //Serial.println(F("polling ecu")); 211 | writeSSM(pollECUbytes, 25, sendSerial); 212 | if (readECU(ECUbytes, 6, false)) 213 | { 214 | //if the checksum passes, carry on as normal 215 | 216 | //interpret and display the ECU data on the LCD 217 | MRPbyteAtm = ECUbytes[0]; 218 | //Serial.print(F("MRPbyteAtm: ")); 219 | //Serial.println(MRPbyteAtm); 220 | MRPbyteMani = ECUbytes[1]; 221 | //Serial.print(F("MRPbyteMani: ")); 222 | //Serial.println(MRPbyteMani); 223 | IATbyte = ECUbytes[2]; 224 | //Serial.print(F("IATbyte: ")); 225 | //Serial.println(IATbyte); 226 | IDCbyteIPW = ECUbytes[3]; 227 | //Serial.print(F("IDCbyteIPW: ")); 228 | //Serial.println(IDCbyteIPW); 229 | IDCbyteRPM1 = ECUbytes[4]; 230 | //Serial.print(F("IDCbyteRPM1: ")); 231 | //Serial.println(IDCbyteRPM1); 232 | IDCbyteRPM2 = ECUbytes[5]; 233 | //Serial.print(F("IDCbyteRPM2: ")); 234 | //Serial.println(IDCbyteRPM2); 235 | 236 | int IAT = computeIAT(IATbyte); 237 | updateTemp(IAT); 238 | 239 | int IDC = computeIDC(IDCbyteIPW, IDCbyteRPM1, IDCbyteRPM2); 240 | updateIDC(IDC); 241 | 242 | float MRP = computeMRP(MRPbyteAtm, MRPbyteMani); 243 | updateBoost(MRP); 244 | 245 | return true; 246 | } 247 | else 248 | { 249 | return false; 250 | } 251 | } 252 | 253 | boolean scanWithoutTemp() 254 | { 255 | writeSSM(pollECUshort, 22, sendSerial); 256 | if (readECU(ECUbytes, 6, false)) 257 | { 258 | //interpret and display the ECU data on the LCD 259 | MRPbyteAtm = ECUbytes[0]; 260 | //Serial.print(F("MRPbyteAtm: ")); 261 | //Serial.println(MRPbyteAtm); 262 | MRPbyteMani = ECUbytes[1]; 263 | //Serial.print(F("MRPbyteMani: ")); 264 | //Serial.println(MRPbyteMani); 265 | IDCbyteIPW = ECUbytes[2]; 266 | //Serial.print(F("IDCbyteIPW: ")); 267 | //Serial.println(IDCbyteIPW); 268 | IDCbyteRPM1 = ECUbytes[3]; 269 | //Serial.print(F("IDCbyteRPM1: ")); 270 | //Serial.println(IDCbyteRPM1); 271 | IDCbyteRPM2 = ECUbytes[4]; 272 | //Serial.print(F("IDCbyteRPM2: ")); 273 | //Serial.println(IDCbyteRPM2); 274 | 275 | int IDC = computeIDC(IDCbyteIPW, IDCbyteRPM1, IDCbyteRPM2); 276 | updateIDC(IDC); 277 | 278 | float MRP = computeMRP(MRPbyteAtm, MRPbyteMani); 279 | updateBoost(MRP); 280 | return true; 281 | } 282 | else 283 | { 284 | return false; 285 | } 286 | } 287 | 288 | 289 | //one time check for CELs during startup 290 | void checkForCEL() 291 | { 292 | Serial.println(F("Reading CELs")); 293 | 294 | writeSSM(readDTCTemp1, 103, sendSerial); 295 | readECU(DTC, 10, true); 296 | 297 | if (DTC[0] != -1) { 298 | DTCread(DTC, 10, DTCFlag1, 10, 1); 299 | resetDTCArray(); 300 | MILon = true; 301 | } 302 | 303 | writeSSM(readDTCTemp2, 73, sendSerial); 304 | readECU(DTC, 10, true); 305 | 306 | if (DTC[0] != -1) { 307 | DTCread(DTC, 10, DTCFlag2, 10, 2); 308 | resetDTCArray(); 309 | MILon = true; 310 | } 311 | 312 | writeSSM(readDTCMem1, 103, sendSerial); 313 | readECU(DTC, 10, true); 314 | 315 | if (DTC[0] != -1) { 316 | DTCread(DTC, 10, DTCFlag1, 10, 1); 317 | resetDTCArray(); 318 | MILon = true; 319 | } 320 | 321 | writeSSM(readDTCMem2, 73, sendSerial); 322 | readECU(DTC, 10, true); 323 | 324 | if (DTC[0] != -1) { 325 | DTCread(DTC, 10, DTCFlag2, 10, 2); 326 | resetDTCArray(); 327 | MILon = true; 328 | } 329 | 330 | delay(1000); 331 | sweepClearLCD(); 332 | 333 | if (MILon == true) 334 | { 335 | Serial.println(F("Displaying CELs")); 336 | DTCupdate(DTCFlag1, 10, 1); 337 | delay(2000); 338 | DTCupdate(DTCFlag2, 10, 2); 339 | delay(2000); 340 | scanning = false; 341 | } 342 | 343 | } 344 | 345 | 346 | 347 | 348 | /*writes data over the software serial port 349 | the &digiSerial passes a reference to the external 350 | object so that we can control it outside of the function*/ 351 | void writeSSM(byte data[], byte length, SoftwareSerial &digiSerial) { 352 | //Serial.println(F("Sending packet... ")); 353 | for (byte x = 0; x < length; x++) { 354 | digiSerial.write(data[x]); 355 | } 356 | //Serial.println(F("done sending.")); 357 | } 358 | 359 | /*LCD Functions borrowed from: https://www.sparkfun.com/tutorials/289 360 | Some other character options: 361 | 0×0D, will advance the cursor to the next row 362 | 0×09, will advance the cursor 5 places 363 | 0×08, will operate as a standard backspace 364 | */ 365 | void setBacklight(byte brightness) 366 | { 367 | lcd.write(0x80); // send the backlight command, decimal 128 368 | lcd.write(brightness); // send the brightness value 0-255 369 | } 370 | 371 | void clearDisplay() 372 | { 373 | lcd.write(0xFE); // send the special command 374 | lcd.write(0x01); // send the clear screen command 375 | } 376 | 377 | void setLCDCursor(byte cursor_position) 378 | { 379 | lcd.write(0xFE); // send the special command 380 | lcd.write(0x80); // send the set cursor command 381 | lcd.write(cursor_position); // send the cursor position 0-31, 0 is top left 382 | } 383 | 384 | void setSplashScreen() 385 | { 386 | lcd.write(0xFE); // send the special command 387 | lcd.write(0x1E); // toggle the splash screen appearance 388 | } 389 | 390 | void lcdStartupMessage() 391 | { 392 | Serial.println(F("Displaying Welcome Message")); 393 | clearDisplay(); 394 | 395 | 396 | DateTime now = rtc.now(); 397 | byte phraseSeed = now.second(); 398 | byte animationSeed = now.minute(); 399 | 400 | randomSeed(phraseSeed); 401 | long phrasePick = random(0, 25); 402 | randomSeed(animationSeed); 403 | long animationPick = random(0, 4); 404 | char buffer[32]; 405 | 406 | strcpy_P(buffer, (char*)pgm_read_word(&(phraseSet[phrasePick]))); 407 | Serial.println(buffer); 408 | 409 | if (animationPick == 0) { 410 | for (byte x = 0; x < 16; x++) 411 | { 412 | setLCDCursor(x); 413 | lcd.print(buffer[x]); 414 | delay(30); 415 | setLCDCursor(31 - x); 416 | lcd.print(buffer[31 - x]); 417 | delay(30); 418 | } 419 | } 420 | if (animationPick == 1) { 421 | for (byte x = 0; x < 8; x++) 422 | { 423 | setLCDCursor(x + 8); 424 | lcd.print(buffer[x + 8]); 425 | delay(30); 426 | setLCDCursor(x + 24); 427 | lcd.print(buffer[x + 24]); 428 | delay(30); 429 | setLCDCursor(7 - x); 430 | lcd.print(buffer[7 - x]); 431 | delay(30); 432 | setLCDCursor(23 - x); 433 | lcd.print(buffer[23 - x]); 434 | delay(30); 435 | } 436 | } 437 | if (animationPick == 2) { 438 | int x[32] = {12, 16, 21, 3, 17, 4, 15, 22, 11, 24, 25, 5, 14, 1, 31, 18, 2, 6, 30, 29, 439 | 20, 23, 7, 26, 9, 28, 8, 10, 13, 19, 27, 0 440 | }; 441 | for (int y = 0; y < 32; y++) { 442 | setLCDCursor(x[y]); 443 | lcd.print(buffer[x[y]]); 444 | delay(30); 445 | } 446 | } 447 | if (animationPick == 3) { 448 | for (int x = 15; x > -1; x--) 449 | { 450 | setLCDCursor(x); 451 | lcd.print(buffer[x]); 452 | delay(30); 453 | } 454 | for (int x = 16; x < 32; x++) 455 | { 456 | setLCDCursor(x); 457 | lcd.print(buffer[x]); 458 | delay(30); 459 | } 460 | } 461 | } 462 | 463 | void sweepClearLCD() 464 | { 465 | for (int x = 0; x < 16; x++) { 466 | setLCDCursor(x); 467 | lcd.print((char)255); //0xFF, the filled space character 468 | delay(30); 469 | if (x > 0) { 470 | setLCDCursor(x + 15); 471 | lcd.print((char)255); //0xFF, the filled space character 472 | } 473 | setLCDCursor(x); 474 | lcd.print(F(" ")); 475 | delay(30); 476 | if (x > 0) { 477 | setLCDCursor(x + 15); 478 | lcd.print(F(" ")); 479 | } 480 | } 481 | } 482 | /*********************************************************/ 483 | 484 | boolean updateTime() 485 | { 486 | DateTime now = rtc.now(); 487 | byte hourTime = now.hour(); 488 | byte minTime = now.minute(); 489 | 490 | if (minTimeNext != minTime) { 491 | clearDisplay(); 492 | setLCDCursor(0); 493 | if (hourTime > 12) { 494 | hourTime = hourTime - 12; 495 | lcd.print(F(" ")); 496 | } 497 | lcd.print(hourTime); 498 | Serial.print(hourTime); 499 | lcd.print(':'); 500 | Serial.print(':'); 501 | if (minTime < 10) 502 | lcd.print(F("0")); 503 | lcd.print(minTime); 504 | lcd.print(F(" ")); 505 | Serial.println(minTime); 506 | minTimeNext = minTime; 507 | return true; 508 | } 509 | else 510 | { 511 | return false; 512 | } 513 | 514 | } 515 | 516 | void updateBoost(float boostValue) 517 | { 518 | if (boostValue > 15) 519 | boostValue = 15; 520 | //display the boost value on the lower left of the lcd 521 | setLCDCursor(16); 522 | if (boostValue >= 0.0 && boostValue < 10.0) 523 | { 524 | lcd.print(F(" ")); 525 | lcd.print(boostValue, 1); //print one digit of the float value 526 | //Serial.print(boostValue); 527 | } 528 | else 529 | { 530 | lcd.print(boostValue, 1); 531 | //Serial.print(boostValue); 532 | } 533 | setLCDCursor(20); 534 | lcd.print(F("psi")); 535 | //Serial.println(F("psi")); 536 | /*display graphical gauge on lower right, 537 | using blocks "0xFF" and a bar "0x7C" at 0 538 | 0xFE clears a cell*/ 539 | setLCDCursor(23); 540 | lcd.print(F(" ")); //clear the plot 541 | int value = ((int)boostValue + 11) / 3; 542 | for (int y = 0; y <= value; y++) 543 | { 544 | setLCDCursor(31 - y); 545 | lcd.print((char)255); //0xFF, the filled space character 546 | } 547 | setLCDCursor(28); 548 | lcd.print((char)124); //0x7C, the | character 549 | } 550 | 551 | void updateTemp(int tempValue) 552 | { 553 | setLCDCursor(6); 554 | if (tempValue >= 0) 555 | lcd.print(F(" ")); 556 | lcd.print(tempValue); 557 | lcd.print((char)223); //degree sign 558 | lcd.print(F("C ")); 559 | //Serial.print(tempValue); 560 | //Serial.println(F("C")); 561 | } 562 | 563 | void updateIDC(int idcValue) 564 | { 565 | setLCDCursor(12); 566 | if (idcValue < 100) 567 | { 568 | lcd.print(F(" ")); 569 | } 570 | if (idcValue < 10) 571 | { 572 | lcd.print(F(" ")); 573 | } 574 | lcd.print(idcValue); 575 | lcd.print((char)37); //percent character 576 | //Serial.print(idcValue); 577 | //Serial.println(F("%")); 578 | } 579 | 580 | 581 | /* ********************************************************* 582 | Packets must follow this structure: 583 | 584 | 0x80 585 | Destination byte 586 | Source byte 587 | Data size byte 588 | data.. 589 | 590 | Checksum byte 591 | 592 | Data size byte specities the number of data bytes in 593 | the packet 594 | 595 | Checksum byte is the 8 least significant bits of the 596 | sum of every packet byte (including the header) 597 | 598 | see: http://www.romraider.com/RomRaider/SsmProtocol 599 | ************************************************************ */ 600 | //this will change the values in dataArray, populating them with values respective of the poll array address calls 601 | boolean readECU(int* dataArray, byte dataArrayLength, boolean nonZeroes) 602 | { 603 | byte data = 0; 604 | boolean isPacket = false; 605 | byte sumBytes = 0; 606 | byte checkSumByte = 0; 607 | byte dataSize = 0; 608 | byte bytePlace = 0; 609 | byte zeroesLoopSpot = 0; 610 | byte loopLength = 10; 611 | for (byte j = 0; j < loopLength; j++) 612 | { 613 | //mem = freeRam(); 614 | //Serial.print(F("ram in readECU: ")); 615 | //Serial.println(mem); 616 | 617 | data = sendSerial.read(); 618 | Serial.print(F("data: ")); //diagnostic: shows any data stream 619 | Serial.println(data); //somehow, the delay? from these diagnostic lines allows the program to work.. 620 | 621 | if (data == 128 && dataSize == 0) { //0x80 or 128 marks the beginning of a packet 622 | isPacket = true; 623 | //Serial.println(F("Begin Packet")); 624 | j = 0; 625 | } 626 | 627 | //terminate function and return false if no response is detected 628 | if (j == (loopLength - 1) && isPacket != true) 629 | { 630 | //Serial.println(F("booted 1")); 631 | return false; 632 | } 633 | 634 | if (isPacket == true && data != -1) { 635 | //Serial.println(data); // for debugging: shows in-packet data 636 | 637 | if (bytePlace == 3) { // how much data is coming 638 | dataSize = data; 639 | //Serial.print(F("dataSize: ")); 640 | //Serial.println(dataSize); 641 | loopLength = data + 6; 642 | } 643 | 644 | if (bytePlace > 4 && bytePlace - 5 < dataArrayLength && nonZeroes == false) 645 | { 646 | dataArray[bytePlace - 5] = data; 647 | } 648 | else if (bytePlace > 4 && zeroesLoopSpot < dataArrayLength / 2 && nonZeroes == true && data != 0 && bytePlace < dataSize + 4) 649 | { 650 | dataArray[zeroesLoopSpot] = data; 651 | dataArray[zeroesLoopSpot + (dataArrayLength / 2)] = bytePlace; 652 | zeroesLoopSpot++; 653 | } 654 | 655 | bytePlace += 1; //increment bytePlace 656 | 657 | //once the data is all recieved, checksum and re-set counters 658 | if (bytePlace == dataSize + 5) { 659 | checkSumByte = CheckSum(sumBytes); //the 8 least significant bits of sumBytes 660 | 661 | if (data != checkSumByte) { 662 | //Serial.println(F("checksum error")); 663 | return false; 664 | } 665 | 666 | isPacket = false; 667 | sumBytes = 0; 668 | bytePlace = 0; 669 | checkSumByte = 0; 670 | dataSize = 0; 671 | //Serial.println(F("End Packet")); 672 | return true; 673 | } 674 | else { 675 | sumBytes += data; // this is to compare with the checksum byte 676 | //Serial.print(F("sum: ")); 677 | //Serial.println(sumBytes); 678 | } 679 | } 680 | } 681 | } 682 | 683 | //input the DTC flags, with half the array as the DTC flag number and the other half as the address 684 | //This function will change DTCs[] to include the error code addresses: [address][byte], but not overwrite or duplicate values 685 | void DTCread(int flags[], int flagLength, int DTCs[][2], int DTCsLength, int DTCTableNumber) 686 | { 687 | int DTCconvoluted = 0; 688 | int DTCaddr = 0; 689 | int index = 0; 690 | boolean duplicate = false; 691 | 692 | for (int plus = 0; plus < DTCsLength; plus++); 693 | { 694 | if (DTCs[index][0] != -1) 695 | index++; 696 | } 697 | 698 | for (int i = 0; i < flagLength / 2; i++) 699 | { 700 | if (flags[i] != -1) 701 | { 702 | DTCconvoluted = flags[i]; 703 | DTCaddr = flags[i + flagLength / 2] - 5; 704 | for (int f = 0; f < 8; f++) 705 | { 706 | if (bitRead(DTCconvoluted, f) != 0 && index < DTCsLength) 707 | { 708 | //loop to check if this value is already present in DTCs 709 | for (int pres = 0; pres < DTCsLength; pres++) 710 | { 711 | if (DTCs[pres][0] == DTCaddr && DTCs[pres][1] == f) 712 | { 713 | duplicate = true; 714 | } 715 | } 716 | if (duplicate == false) 717 | { 718 | DTCs[index][0] = DTCaddr; 719 | DTCs[index][1] = f; 720 | index++; 721 | } 722 | duplicate = false; 723 | } 724 | } 725 | } 726 | } 727 | //DTCupdate(DTCs, DTCsLength, DTCTableNumber); 728 | //delay(2000); 729 | } 730 | 731 | //This function takes in an array of DTC addresses, and reads off the string values from PROGMEM 732 | //Finally, it displays them on an LCD and the serial monitor. Values of '-1' are ignored. DTCTableNumber is 1 or 2. 733 | void DTCupdate(int DTCs[][2], byte DTCsLength, byte DTCTableNumber) 734 | { 735 | char buffer[5]; 736 | byte DTCcount = 0; 737 | setLCDCursor(6 + DTCcount); 738 | 739 | for (byte a = 0; a < DTCsLength; a++) 740 | { 741 | byte b = DTCs[a][0]; 742 | byte c = DTCs[a][1]; 743 | if (DTCs[a][0] != -1 && DTCTableNumber == 1) 744 | { 745 | strcpy_P(buffer, (char*)pgm_read_word(&(DTCset1[b][c]))); 746 | Serial.println(buffer); 747 | lcd.print(buffer); 748 | DTCcount += 6; 749 | } 750 | else if (DTCs[a][0] != -1 && DTCTableNumber == 2) 751 | { 752 | strcpy_P(buffer, (char*)pgm_read_word(&(DTCset2[b][c]))); 753 | Serial.println(buffer); 754 | lcd.print(buffer); 755 | DTCcount += 6; 756 | } 757 | } 758 | } 759 | 760 | int computeIAT(int IAT) 761 | { 762 | int temperature = IAT - 40; 763 | return temperature; 764 | } 765 | 766 | float computeMRP(int boost1, int boost2) 767 | { 768 | int boostCor = ((boost1 * 256 + boost2) - 32768); 769 | float MRP = boostCor * 0.01933677; 770 | return MRP; 771 | } 772 | 773 | int computeIDC(int IPW, int rpm1, int rpm2) 774 | { 775 | /* 776 | rpm = (rpm1*256+rpm2)/4 777 | IPW = IPW*256/1000 778 | IDC = rpm*IPW/1200 779 | */ 780 | 781 | float rpm = ((rpm1 * 256) + rpm2) / 4; 782 | float IPWcor = IPW * 256 / 1000; 783 | int IDC = (IPWcor * rpm) / (1200); 784 | return IDC; 785 | } 786 | 787 | //returns the amount of free memory for variable use out of the 2k on the UNO 788 | //from: https://learn.adafruit.com/memories-of-an-arduino/measuring-free-memory 789 | int freeRam () 790 | { 791 | extern int __heap_start, *__brkval; 792 | int v; 793 | return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 794 | } 795 | 796 | void readDTCArray() 797 | { 798 | Serial.print(F("(")); 799 | for (int x = 0; x < 10; x++) 800 | { 801 | Serial.print(DTC[x]); 802 | if (x < 9) 803 | Serial.print(F(",")); 804 | } 805 | Serial.println(F(")")); 806 | } 807 | 808 | void readDTCFlag() 809 | { 810 | Serial.print(F("(")); 811 | for (int x = 0; x < 10; x++) 812 | { 813 | Serial.print(F("(")); 814 | for (int y = 0; y < 2; y++) 815 | { 816 | Serial.print(DTCFlag1[x][y]); 817 | if (y < 1) 818 | Serial.print(F(",")); 819 | } 820 | if (x < 9) 821 | Serial.print(F("),")); 822 | } 823 | Serial.println(F("))")); 824 | } 825 | 826 | void resetDTCArray() 827 | { 828 | for (int x = 0; x < 10; x++) 829 | { 830 | DTC[x] = -1; 831 | } 832 | } 833 | -------------------------------------------------------------------------------- /basic_SSM_comms_format.ino: -------------------------------------------------------------------------------- 1 | #include 2 | //#include //uncomment this if you want to use SPI to control the LCD, but change softwareserial pins to not be 9-13 3 | #include 4 | #include 5 | 6 | SoftwareSerial kLine = SoftwareSerial(10, 11); //Rx, Tx 7 | 8 | //button to switch between modes 9 | //consider adding two buttons to cycle up and down 10 | #define buttonPin 2 11 | boolean lastButtonState = false; 12 | 13 | //ECU variables 14 | /*here are the addresses we want: 15 | manifold pressure sensor: 0x0200EA,0x0200EB 16 | A/F sensor #1:0x020952,0x020953 17 | Coolant temperature: 0x000008 18 | Battery Voltage: 0x00001C 19 | Intake air temperature: 0x000012 20 | */ 21 | //lets make individual polling messages for each parameter so we can switch between them 22 | //the requests include the 3-byte address of the data byte that we want to read from ECU memory 23 | //msg format = {header,to,from,# data bytes,read type flag(?),continuous response request flag,addr part 1,addr part 2, addr part 3,..., checksum} 24 | uint8_t pollECU_pressure[13] = {128, 16, 240, 8, 168, 0, 0x02, 0x00, 0xEA, 0x02, 0x00, 0xEB, 0x09}; //response [X,X] 25 | uint8_t pollECU_AF[13] = {128, 16, 240, 8, 168, 0, 0x02, 0x09, 0x52, 0x02, 0x09, 0x53, 0xEB}; //response [X,X] 26 | uint8_t pollECU_Coolant[10] = {128, 16, 240, 5, 168, 0, 0x00, 0x00, 0x08, 0x35}; //response [X] 27 | uint8_t pollECU_BattVolt[10] = {128, 16, 240, 5, 168, 0, 0x00, 0x00, 0x1C, 0x49}; //response [X] 28 | uint8_t pollECU_IAT[10] = {128, 16, 240, 5, 168, 0, 0x00, 0x00, 0x12, 0x3F}; //response [X] 29 | 30 | uint8_t ECUResponseBuffer[2] = {0, 0};//the longest number of bytes we get from the ECU in response to our queries is 2 bytes 31 | uint8_t parameterSelect = 0;//an index to keep track of which parameter to poll 32 | 33 | //display pins -> use the hardware I2C pins 34 | #define OLED_RESET 8 35 | Adafruit_SSD1306 display(OLED_RESET); 36 | /*if you want to use hardware SPI pins, uncomment this section 37 | //and change the software serial pins to not use 9-13 38 | #define OLED_DC 6 39 | #define OLED_CS 7 40 | #define OLED_RESET 8 41 | Adafruit_SSD1306 display(OLED_DC, OLED_RESET, OLED_CS); 42 | */ 43 | 44 | //logo to display upon startup 45 | const unsigned char PROGMEM S204logo [] = { 46 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 47 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 48 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 49 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 50 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 51 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 52 | 0x00, 0x00, 0x1F, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 53 | 0x00, 0x07, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 54 | 0x00, 0x1F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 55 | 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 56 | 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0xFF, 0xFF, 0x80, 0x00, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0xFF, 57 | 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0xE0, 0x00, 0x01, 0xFF, 58 | 0x03, 0xFF, 0xC0, 0x00, 0x06, 0x03, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0xFF, 0xF0, 0x00, 0x07, 0xFF, 59 | 0x07, 0xFF, 0x80, 0x00, 0x06, 0x01, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xF0, 0x00, 0x3F, 0xFF, 60 | 0x0F, 0xFF, 0xFF, 0xFF, 0x04, 0x00, 0x00, 0x07, 0xF0, 0x1F, 0xE0, 0x0F, 0xF0, 0x00, 0xFE, 0xFE, 61 | 0x0F, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, 0x07, 0xE0, 0x1F, 0xC0, 0x0F, 0xF0, 0x03, 0xF0, 0xFE, 62 | 0x07, 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0xFF, 0xE0, 0x3F, 0x80, 0x1F, 0xE0, 0x0F, 0x80, 0xFE, 63 | 0x03, 0xFF, 0xFF, 0xFF, 0xF8, 0x00, 0xFF, 0xFF, 0xC0, 0x3F, 0x80, 0x1F, 0xE0, 0x3E, 0x01, 0xFC, 64 | 0x01, 0xFF, 0xFF, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0x80, 0x3F, 0x80, 0x3F, 0xE0, 0xFC, 0x01, 0xFC, 65 | 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x0F, 0xFC, 0x00, 0x00, 0x7F, 0x00, 0x3F, 0xC1, 0xFC, 0x03, 0xFF, 66 | 0x20, 0x00, 0x00, 0xFF, 0xF8, 0x1F, 0xC0, 0x00, 0x00, 0x7F, 0x00, 0x7F, 0xC1, 0xFF, 0xFF, 0xFF, 67 | 0x3C, 0x00, 0x00, 0xFF, 0xF0, 0x1F, 0xC0, 0x00, 0x00, 0x7F, 0x00, 0x7F, 0x81, 0xFF, 0xFF, 0xFF, 68 | 0x7F, 0xFF, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0x00, 0x00, 0x17, 0xF8, 69 | 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x0F, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFE, 0x00, 0x00, 0x07, 0xF0, 70 | 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0x07, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xF8, 0x00, 0x00, 0x07, 0xE0, 71 | 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 72 | 0xFF, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 73 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 74 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 75 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 76 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 77 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 78 | }; 79 | 80 | const unsigned char PROGMEM STIlogo [] = { 81 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 82 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 83 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF8, 0x00, 0x00, 0x00, 0x00, 84 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x00, 85 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 86 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xF0, 0x00, 0x00, 0x00, 0x00, 87 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xE0, 0x00, 0x00, 0x00, 0x00, 88 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF1, 0xC0, 0x00, 0x00, 0x00, 0x00, 89 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1, 0xC0, 0x00, 0x00, 0x00, 0x00, 90 | 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE3, 0xC0, 0x00, 0x00, 0x00, 0x00, 91 | 0x00, 0x00, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 92 | 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 93 | 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 94 | 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0xFF, 0xFF, 0x8F, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 95 | 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0xFF, 0xFF, 0x8F, 0xFF, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, 96 | 0x00, 0x00, 0x00, 0x00, 0x07, 0x0F, 0xFF, 0xFF, 0x8F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 97 | 0x00, 0x00, 0x00, 0x00, 0x07, 0x80, 0x01, 0xE3, 0x8E, 0x0E, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 98 | 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0xF3, 0x8E, 0x0E, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 99 | 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x73, 0x8E, 0x1C, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 100 | 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x73, 0x8E, 0x3C, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 101 | 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xF8, 0x73, 0x8E, 0x38, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 102 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x73, 0x8E, 0x38, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 103 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x8E, 0x71, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 104 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xE3, 0x8E, 0xF1, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 105 | 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xE3, 0x8E, 0xE3, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 106 | 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xC3, 0x8E, 0xE3, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 107 | 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xFF, 0x03, 0x8F, 0xC7, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 108 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 109 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 110 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 111 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 112 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 113 | }; 114 | 115 | void setup() { 116 | Serial.begin(57600); //for diagnostics 117 | kLine.begin(4800); //SSM uses 4800 8N1 baud rate 118 | pinMode(buttonPin,INPUT);//initialize the button 119 | 120 | display.begin(SSD1306_SWITCHCAPVCC);//start the display 121 | display.clearDisplay();//make sure it is clear 122 | display.drawBitmap(0, 0, S204logo, 128, 64, 1);//load the logo 123 | display.display();//display the logo 124 | delay(5000); 125 | } 126 | 127 | void loop() { 128 | 129 | //let's read the individual ECU parameters based on a 'parameterSelect' index 130 | 131 | //these are buffers for displayable data 132 | double parameter = 0; 133 | String unit = ""; 134 | String title = ""; 135 | bool displayFlag = false;//if we fail an ECU poll, we don't want to update the display 136 | 137 | //make sure the parameter we are polling for is correctly indexed 138 | boolean button = digitalRead(buttonPin);//read the state of the 'switch parameter' button-> pull to high voltage to switch state 139 | //then, adjust the parameter we plan to poll next 140 | if(button && !lastButtonState) 141 | { 142 | parameterSelect += 1; 143 | if(parameterSelect > 4) 144 | { 145 | parameterSelect = 0; 146 | } 147 | } 148 | else if(false) 149 | { 150 | //can add a second button to switch parameters the other direction 151 | parameterSelect -= 1; 152 | if(parameterSelect < 0) 153 | { 154 | parameterSelect = 4; 155 | } 156 | } 157 | lastButtonState = button;//keep track of this so that each button press only increments the index by 1 158 | 159 | if(parameterSelect == 0) 160 | { 161 | //pressure sensor 162 | if(serialCallSSM(kLine, pollECU_pressure, 13, ECUResponseBuffer, 2, 0) < 10) 163 | { 164 | //the pressure calc looks like: x*0.1333224 for units of kPa absolute, or (x-760)*0.001333224 for units of Bar relative to sea level 165 | uint16_t combBytes = 256*ECUResponseBuffer[0] + ECUResponseBuffer[1];//we must combine the ECU response bytes into one 16-bit word - the response bytes are big endian 166 | double pressure = combBytes * 0.1333224;//kPa 167 | Serial.print(F("Absolute pressure: ")); 168 | Serial.println(pressure); 169 | //update the buffers for displayable data 170 | parameter = pressure; 171 | unit = F(" kPa"); 172 | title = F("Manifold pressure"); 173 | displayFlag = true; 174 | } 175 | } 176 | else if(parameterSelect == 1) 177 | { 178 | //oxygen sensor 179 | if(serialCallSSM(kLine, pollECU_AF, 13, ECUResponseBuffer, 2, 0) < 10) 180 | { 181 | //the Air/Fuel ratio calc looks like: x*0.00179443359375 for units of A/F ratio 182 | uint16_t combBytes = 256*ECUResponseBuffer[0] + ECUResponseBuffer[1]; 183 | double AFR = combBytes * 0.00179443359375; 184 | Serial.print(F("Air/Fuel ratio: ")); 185 | Serial.println(AFR); 186 | //update the buffers for displayable data 187 | parameter = AFR; 188 | unit = F(""); 189 | title = F("Air/Fuel ratio"); 190 | displayFlag = true; 191 | } 192 | } 193 | else if(parameterSelect == 2) 194 | { 195 | //Coolant temp sensor - we polled two addresses to get the same info (same calculation, even)-> I ignored one of them 196 | if(serialCallSSM(kLine, pollECU_Coolant, 10, ECUResponseBuffer, 2, 0) < 10) 197 | { 198 | //the coolant temp calc looks like: x-40 for units of Deg C 199 | double coolantTemp = ECUResponseBuffer[0] - 40.0; 200 | Serial.print(F("Coolant temperature: ")); 201 | Serial.println(coolantTemp); 202 | //update the buffers for displayable data 203 | parameter = coolantTemp; 204 | unit = F("degC"); 205 | title = F("Coolant temp."); 206 | displayFlag = true; 207 | } 208 | } 209 | else if(parameterSelect == 3) 210 | { 211 | //battery voltage 212 | if(serialCallSSM(kLine, pollECU_BattVolt, 10, ECUResponseBuffer, 2, 0) < 10) 213 | { 214 | //the battery voltage calc looks like: x*8/100 for units of Volts 215 | double volts = ECUResponseBuffer[0] * 8.00; 216 | volts = volts/100.00; 217 | Serial.print(F("Battery Voltage: ")); 218 | Serial.println(volts); 219 | //update the buffers for displayable data 220 | parameter = volts; 221 | unit = F(" V"); 222 | title = F("Battery voltage"); 223 | displayFlag = true; 224 | } 225 | } 226 | else if(parameterSelect == 4) 227 | { 228 | //intake air temperature 229 | if(serialCallSSM(kLine, pollECU_IAT, 10, ECUResponseBuffer, 2, 0) < 10) 230 | { 231 | //the intake air temperature calc looks like: x-40 for units of Deg C 232 | double IAT = ECUResponseBuffer[0] - 40.0; 233 | Serial.print(F("Intake air temperature: ")); 234 | Serial.println(IAT); 235 | //update the buffers for displayable data 236 | parameter = IAT; 237 | unit = F("degC"); 238 | title = F("Intake air temp."); 239 | displayFlag = true; 240 | } 241 | } 242 | Serial.println(); 243 | 244 | //now that we've read the parameters we're interested in, lets display one 245 | if(displayFlag) 246 | { 247 | writeToDisplay(parameter,unit,title); 248 | } 249 | else 250 | { 251 | //in case we want to do something special when comms stops, put it here 252 | //this could be at engine off. 253 | display.clearDisplay();//make sure it is clear 254 | display.drawBitmap(0, 0, STIlogo, 128, 64, 1);//load the logo 255 | display.display();//display the logo 256 | } 257 | } 258 | 259 | 260 | boolean readSSM(SoftwareSerial &serialPort, uint8_t *dataBuffer, uint16_t bufferSize) 261 | { 262 | //read in an SSM serial response packet, place the data bytes in the data buffer 263 | //and return a boolean value indicating whether the checksum failed 264 | uint16_t timeout = 100;//100 millisecond timeout for response 265 | uint32_t timerstart = millis();//start the timer 266 | boolean notFinished = true;//a flag to indicate that we are done 267 | boolean checksumSuccess = false; 268 | uint8_t dataSize = 0; 269 | uint8_t calcSum = 0; 270 | uint8_t packetIndex = 0; 271 | 272 | Serial.print(F("Read Packet: [ ")); 273 | while(millis() - timerstart <= timeout && notFinished) 274 | { 275 | if(serialPort.available() > 0) 276 | { 277 | uint8_t data = serialPort.read(); 278 | Serial.print(data); 279 | Serial.print(F(" "));//if not printing data, be sure to delay to allow for more bytes to come in 280 | if(packetIndex == 0 && data == 128) 281 | { 282 | //0x80 or 128 marks the beginning of a packet 283 | packetIndex += 1; 284 | calcSum += data; 285 | } 286 | else if(packetIndex == 1 && data == 240) 287 | { 288 | //this byte indicates that the message recipient is the 'Diagnostic tool' 289 | packetIndex += 1; 290 | calcSum += data; 291 | } 292 | else if(packetIndex == 2 && data == 16) 293 | { 294 | //this byte indicates that the message sender is the ECU 295 | packetIndex += 1; 296 | calcSum += data; 297 | } 298 | else if(packetIndex == 3) 299 | { 300 | //this byte indicates the number of data bytes which follow 301 | dataSize = data; 302 | packetIndex += 1; 303 | calcSum += data; 304 | } 305 | else if(packetIndex == 4) 306 | { 307 | //I don't know what this byte is for 308 | packetIndex += 1; 309 | calcSum += data; 310 | } 311 | else if(packetIndex > 4 && packetIndex < (dataSize+4)) 312 | { 313 | //this byte is data 314 | if(packetIndex - 5 < bufferSize)//make sure it fits into the buffer 315 | { 316 | dataBuffer[packetIndex - 5] = data; 317 | } 318 | packetIndex += 1; 319 | calcSum += data; 320 | } 321 | else if(packetIndex == dataSize + 4) 322 | { 323 | //this is the checksum byte 324 | if(data == calcSum) 325 | { 326 | //checksum was successful 327 | checksumSuccess = true; 328 | } 329 | else 330 | { 331 | Serial.print(F("Checksum fail ")); 332 | } 333 | notFinished = false;//we're done now 334 | break; 335 | } 336 | timerstart = millis();//reset the timeout if we're not finished 337 | } 338 | } 339 | Serial.println(F("]")); 340 | if(notFinished) 341 | { 342 | Serial.println(F("Comm. timeout")); 343 | } 344 | return checksumSuccess; 345 | } 346 | 347 | //writes data over the software serial port 348 | void writeSSM(SoftwareSerial &serialPort, uint8_t *dataBuffer, uint8_t bufferSize) { 349 | //this function needs to catch mistakes that I make in preparing 350 | //the packets -> {128, 16, 240, #data, 168, cont. response flag, addr1,addr2,addr3,checksum} = 10 bytes, 5 of which are data 351 | dataBuffer[0] = 128;//begin packet 352 | dataBuffer[1] = 16;//to subaru ECU 353 | dataBuffer[2] = 240;//from diagnostic tool 354 | //number of data bytes include 3*(number of addresses), cont. response flag, and mode (address read) 355 | //this means that the header, to, from, #data, and checksum are not part. 356 | dataBuffer[3] = bufferSize - 5;//# data bytes 357 | dataBuffer[4] = 168;//single address read 358 | dataBuffer[5] = 0;//no continuous polling 359 | uint8_t sum = 0; 360 | Serial.print(F("Sending packet: [ ")); 361 | for (uint8_t x = 0; x < bufferSize; x++) { 362 | if(x == bufferSize - 1 && sum != dataBuffer[x]) 363 | { 364 | dataBuffer[x] = sum;//fix the checksum in the data buffer 365 | } 366 | else 367 | { 368 | sum += dataBuffer[x];//build the checksum 369 | } 370 | serialPort.write(dataBuffer[x]); 371 | Serial.print(dataBuffer[x]); 372 | Serial.print(F(" ")); 373 | } 374 | Serial.println(F("]")); 375 | } 376 | 377 | uint8_t serialCallSSM(SoftwareSerial &serialPort, uint8_t *sendDataBuffer, uint16_t sendBufferSize, uint8_t *receiveDataBuffer, uint16_t receiveBufferSize, uint8_t attempts) 378 | { 379 | //this function performs the call and response routine for 380 | //exchanging serial data with the Subaru ECU via SSM 381 | //it sends a message and awaits a response, resending the 382 | //message if we reach timeout or if the checksum failed 383 | //it stops trying the data exchange if 10 failures occur and 384 | //it outputs the number of attempts it failed to send data 385 | //input: handle to software serial pins 386 | // buffer with outgoing data and length of buffer 387 | // buffer for received data (not the whole packet), and length of buffer 388 | // the initial value for a counter of failed attempts 389 | 390 | writeSSM(serialPort, sendDataBuffer, sendBufferSize);//send the message 391 | boolean success = readSSM(serialPort, receiveDataBuffer, receiveBufferSize);//receive the response 392 | uint8_t newAttempt = 0; 393 | 394 | if(!success && attempts < 10) 395 | { 396 | Serial.println(F("Packet exchange failed, trying again... ")); 397 | newAttempt = serialCallSSM(serialPort, sendDataBuffer, sendBufferSize, receiveDataBuffer, receiveBufferSize, attempts + 1); 398 | } 399 | newAttempt += attempts; 400 | 401 | return newAttempt; 402 | } 403 | 404 | 405 | void writeToDisplay(double parameter, String unit, String title) 406 | { 407 | display.clearDisplay(); 408 | display.setTextSize(1); 409 | display.setTextColor(WHITE); 410 | display.setCursor(0,0); 411 | display.println(title); 412 | display.setTextSize(3); 413 | display.print(parameter); 414 | display.setTextSize(2); 415 | display.println(unit); 416 | display.display(); 417 | } 418 | 419 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | Subaru Clock Pod Mod V3 2 | 2015-03-29 3 | 4 | More information can be found on: 5 | http://www.clubwrx.net/forums/tutorials-diy/134423369-clock-pod-mod-subarb-select-monitor-ecu-polling-arduino.html 6 | 7 | This code includes routines for communicating via the subaru select monitor protocol. 8 | 9 | I want to encourage the adaptation and implementation of this code in other projects. 10 | Please post questions or ideas to the above forum to support constructive dialogue. 11 | 12 | -Obeisance --------------------------------------------------------------------------------