/* Project that displays Megasquirt engine control data over CAN utilizing a Teensy 3.2 and Nextion 3.2 touch screen display. Thanks again to defragster on PJRC forums for the different tips and tricks! Hardware used is Teensy 3.2 http://www.pjrc.com/store/teensy32.html Nextion 3.2" resistive touch display https://www.itead.cc/nextion-nx4024t032.html WaveShare SN65HVD230 CAN Board http://www.amazon.com/gp/product/B00KM6XMXO?psc=1&redirect=true&ref_=od_aui_detailpages00 Megasquirt/Microsquirt hardware from http://www.diyautotune.com/ Thank you to xrattiracer for the inspiration here https://github.com/merkur2k/MSCan_Gauge/tree/teensy */ #include #include FlexCAN CANbus(500000); static CAN_message_t rxmsg; //Megasquirt data vars byte indicator[7]; // where to store indicator data float BATTV, IAC, dwell, idle_tar, AFRtgt, AFR, newBATTV, oldBATTV; unsigned int MAP, SPKADV, RPM, TPS, MAT, CLT, injduty, Baro, PW1, nexAFR, nexCLT; void setup() { //Initialize CAN and start Serial and Serial2, Serial2 is for Nextion display CANbus.begin(); // Serial.begin(115200); //***Uncomment this section to output CAN message IDs to Serial Monitor*** // while (!Serial) ; // Serial.println("Hello Megasquirt"); Serial2.begin(115200); } void loop(void) { // Gauge display function gauge_display(); //Look for CAN broadcasts if ( CANbus.read(rxmsg) ) { switch (rxmsg.id) { // ID's 1520+ are Megasquirt CAN broadcast frames. EAch frame represents a data group http://www.msextra.com/doc/pdf/Megasquirt_CAN_Broadcast.pdf case 1520: // Group 0 RPM = (float)(word(rxmsg.buf[6], rxmsg.buf[7])); PW1 = (float)(word(rxmsg.buf[2], rxmsg.buf[3])); injduty = ((PW1 / 1000 * RPM / 120) / 10); break; case 1521: // Group 1 SPKADV = (float)(word(rxmsg.buf[0], rxmsg.buf[1])); indicator[0] = rxmsg.buf[3]; // engine AFRtgt = (float)(word(0x00, rxmsg.buf[4])); break; case 1522: // Group 2 Baro = (float)(word(rxmsg.buf[0], rxmsg.buf[1])); MAP = (float)(word(rxmsg.buf[2], rxmsg.buf[3])); MAT = (float)(word(rxmsg.buf[4], rxmsg.buf[5])); CLT = (float)(word(rxmsg.buf[6], rxmsg.buf[7])); nexCLT = (float)(word(rxmsg.buf[6], rxmsg.buf[7])); break; case 1523: // Group 3 TPS = (float)(word(rxmsg.buf[0], rxmsg.buf[1])); BATTV = (float)(word(rxmsg.buf[2], rxmsg.buf[3])); AFR = (float)(word(rxmsg.buf[4], rxmsg.buf[5])); nexAFR = (float)(word(rxmsg.buf[4], rxmsg.buf[5])); break; case 1524: // Group 4 break; case 1526: // Group 6 IAC = (float)(word(rxmsg.buf[6], rxmsg.buf[7])); //IAC = (IAC * 49) / 125; case 1529: // 9 dwell = (float)(word(rxmsg.buf[4], rxmsg.buf[5])); break; case 1530: // Group 10 indicator[1] = rxmsg.buf[0]; // status 1 indicator[2] = rxmsg.buf[1]; // status 2 indicator[3] = rxmsg.buf[2]; // status 3 indicator[6] = rxmsg.buf[6]; // status 6 indicator[7] = rxmsg.buf[7]; // status 7 break; case 1537: // Group 17 break; case 1548: // Group 28 idle_tar = (float)(word(rxmsg.buf[0], rxmsg.buf[1])); break; case 1551: // Group 31 break; case 1574: // Group 54 indicator[4] = rxmsg.buf[2]; // cel break; } } } elapsedMillis DisplayTime; //Establish a timer to prevent unnecessary screen rewrites void gauge_display() { //Prints captured data from above to display if ( DisplayTime < 150 ) return; DisplayTime = 0; //Serial.println(rxmsg.id); Any type of data you want to go to Serial Monitor place here // Display Spark Advance Serial2.print("t3.txt="); Serial2.write(0x22); Serial2.print(SPKADV / 10); Serial2.write(0x22); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); // Display Engine MAP Serial2.print("t4.txt="); Serial2.write(0x22); Serial2.print(MAP / 10); Serial2.write(0x22); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); // Display Engine Air Fuel Ratio Serial2.print("j6.val="); Serial2.print(nexAFR / 2); //Nextion is weird and will not display floating point data for a progress bar, so I had to massage the output for the progress bar *only* Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); Serial2.print("t14.txt="); Serial2.write(0x22); Serial2.print(AFR / 10); Serial2.write(0x22); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); // Define WOT AFR Thresholds if ((AFR / 10 < 10.5) && (AFR / 10 > 10) && (TPS / 10 > 80)) gauge_display_AFR_YELLOW(); if ((AFR / 10 < 10.0) && (TPS / 10 > 80)) gauge_display_AFR_RED(); if ((AFR / 10 < 12.5) && (AFR / 10 > 10.5) && (TPS / 10 > 80)) gauge_display_AFR_BLACK(); if ((AFR / 10 > 12.5) && (TPS / 10 > 80)) gauge_display_AFR_RED(); // Display Injector Duty Cycle Serial2.print("j2.val="); Serial2.print(injduty); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); Serial2.print("t15.txt="); Serial2.write(0x22); Serial2.print(injduty); Serial2.write(0x22); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); // Several functions to alert if duty cycle is above certain thresholds if (injduty < 60) gauge_display_injduty_black(); if ((injduty >= 60) && (injduty < 79)) gauge_display_injduty_yellow(); if (injduty > 79) gauge_display_injduty_RED(); // Display Throttle Position Serial2.print("j3.val="); Serial2.print(TPS / 10); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); Serial2.print("t16.txt="); Serial2.write(0x22); Serial2.print(TPS / 10); Serial2.write(0x22); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); // Battery voltage generally does not change that fast, so this is trying to reduce a bit of overhead by not updating if the data has not changed newBATTV = BATTV / 10; if ( newBATTV != oldBATTV ) gauge_display_BATTV(); Serial2.print("t8.txt="); Serial2.write(0x22); Serial2.print(MAT / 10); Serial2.write(0x22); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); // Display Engine RPM Serial2.print("t0.txt="); Serial2.write(0x22); Serial2.print(RPM); Serial2.write(0x22); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); Serial2.print("j5.val="); Serial2.print(RPM / 100); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); // Thresholds for warning colors for Engine RPM if (RPM < 5000) gauge_display_RPM_BLACK(); if ((RPM > 5000) && (RPM < 6000)) gauge_display_RPM_YELLOW(); if ((RPM > 6000)) gauge_display_RPM_RED(); // Display Engine Coolant Temperature Serial2.print("t17.txt="); Serial2.write(0x22); Serial2.print(CLT / 10); Serial2.write(0x22); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); Serial2.print("j4.val="); Serial2.print(nexCLT / 20); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); } // Gauge functions for thresholds void gauge_display_injduty_black() { Serial2.print("j2.pco=BLACK"); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); Serial2.print("t15.pco=BLACK"); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); } void gauge_display_injduty_yellow() { Serial2.print("j2.pco=YELLOW"); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); } void gauge_display_injduty_RED() { Serial2.print("j2.pco=RED"); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); } void gauge_display_BATTV() { oldBATTV = Serial2.print("t6.txt="); Serial2.write(0x22); Serial2.print(BATTV / 10); Serial2.write(0x22); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); } void gauge_display_RPM_BLACK() { Serial2.print("t0.pco=BLACK"); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); Serial2.print("j5.pco=BLACK"); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); } void gauge_display_RPM_YELLOW() { Serial2.print("t0.pco=YELLOW"); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); Serial2.print("j5.pco=YELLOW"); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); } void gauge_display_RPM_RED() { Serial2.print("t0.pco=RED"); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); Serial2.print("j5.pco=RED"); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); } void gauge_display_AFR_RED() { Serial2.print("j6.pco=RED"); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); } void gauge_display_AFR_YELLOW() { Serial2.print("j6.pco=YELLOW"); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); } void gauge_display_AFR_BLACK() { Serial2.print("j6.pco=BLACK"); Serial2.write(0xff); Serial2.write(0xff); Serial2.write(0xff); }