#include <LiquidCrystal_I2C.h>
#include <max6675.h>
/**
* ============================================================================
* Name : RealDash_CAN_2way.ino
* Part of : RealDash
* Author : Jani Immonen
* Created : 15.10.2017
*
* Arduino example sketch of how to use RealDash CAN protocol.
* This example code is free for any use.
*
* www.realdash.net
* ============================================================================
**/
LiquidCrystal_I2C lcd(0x27, 20, 4);
//initialize the liquid crystal library
//the first parameter is the I2C address
//the second parameter is how many rows are on your screen
//the third parameter is how many columns are on your screen
unsigned int FUEL = A2;
unsigned int DRIVE = A1;
unsigned int BOOST = A0;
//EGT
int thermoDO = 31;
int thermoCS = 33;
int thermoCLK = 35;
MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);
int egtRead = thermocouple.readFahrenheit();
unsigned int egtLevel = egtRead;
// Arduino digital and analog pins
// digital pin statuses are stored as bits in one variable
unsigned int digitalPins = 0;
// analog pin values are stored in array of ints
int analogPins[7] = {0};
unsigned int rpm = 0;
unsigned int kpa = 992; // 99.2
unsigned int tps = 965; // 96.5
unsigned int clt = 80; // 80 - 100
unsigned int textCounter = 0;
// incoming data
byte incomingFrame[17] = { 0 };
unsigned int incomingFramePos = 0;
// if READWRITE_PINS is defined, the values are read from and written to Arduino
// digital and analog pins.
#define READWRITE_PINS
void setup()
{
#if defined (READWRITE_PINS)
// set digital pins as outputs
for (int i=1; i<14; i++)
{
pinMode(i, OUTPUT);
digitalWrite(i, HIGH);
}
#endif
// init serial
Serial.begin(115200);
delay(100);
pinMode(53, OUTPUT); //Set LED Warning Output
digitalWrite(53, HIGH); //Test Warning Light
lcd.init(); //Initiallize LCD
lcd.backlight();
lcd.setCursor(6,1);
lcd.print("FUMMINS");
delay(1000);
lcd.clear(); //Reset
digitalWrite(53, LOW);
}
void loop()
{
//Boost Level
int boostSensor = analogRead(BOOST); //Read Boost Sensor
int boostLevel = map(boostSensor, 100, 1000, 0, 100); //Map the Sensor
lcd.setCursor(0, 1);
if (boostLevel < 10) { lcd.print("0"); }
if (boostLevel < 1) { lcd.print("00"); }
lcd.print(boostLevel);
lcd.setCursor(2, 1);
if (boostLevel < 100) { lcd.print(" "); }
lcd.setCursor(3, 1);
lcd.print("PSI");
if (boostLevel < 100) {
lcd.setCursor(2, 1);
lcd.print(" ");
}
lcd.setCursor(0, 0);
lcd.print("Boost");
//Drive Pressure
int driveSensor = analogRead(DRIVE); //Read DRIVE Sensor
int driveLevel = map(driveSensor, 110, 1000, 0, 100); //Map the Sensor
lcd.setCursor(0, 3);
if (driveLevel < 10) { lcd.print("0"); }
if (driveLevel < 1) { lcd.print("00"); }
lcd.print(driveLevel);
lcd.setCursor(2, 3);
if (driveLevel < 100) { lcd.print(" "); }
lcd.setCursor(0, 2);
lcd.print("Ex Pres");
if (driveLevel < 100) {
lcd.setCursor(2, 1);
lcd.print(" ");
}
lcd.setCursor(3, 3);
lcd.print("PSI");
//EGT
int egtLevel = thermocouple.readFahrenheit();
unsigned int egtmapped = map(egtLevel, 70, 1600, 0, 255 );
//analogWrite(13 , egtLevel);
lcd.setCursor(10, 1);
if(egtLevel < 10){
lcd.print("0");}
if(egtLevel < 0 ){ lcd.print(" ");}
if(egtLevel > 0) {lcd.print(egtLevel);}
if(egtLevel <100){ lcd.setCursor(12,1); lcd.print(" ");} //print blanks
if(egtLevel <1000){ lcd.setCursor(13,1); lcd.print(" ");} //print blanks
lcd.setCursor(14, 1);
if(egtLevel < 1000){lcd.print(" ");}
lcd.setCursor(10, 0);
lcd.print("EGT");
lcd.setCursor(15, 1);
lcd.print("F");
//Serial.print("F = ");
//Serial.println(thermocouple.readFahrenheit());
//Fuel Pressure
int fuelSensor = analogRead(FUEL); //Read Boost Sensor
int fuelLevel = map(fuelSensor, 110, 1023, 0, 100); //Map the Sensor
lcd.setCursor(10, 2);
lcd.print("Fuel Pres");
lcd.setCursor(10, 3);
if (fuelLevel < 10) { lcd.print("0"); }
if (fuelLevel < 1) { lcd.print("00"); }
lcd.print(fuelLevel);
if (fuelLevel < 100) {
lcd.setCursor(12, 3);
lcd.print(" ");
}
lcd.setCursor(13, 3);
lcd.print("PSI");
if (boostLevel > 35 || egtLevel > 1200 || driveLevel > 50 || fuelLevel < 30) { digitalWrite(53, HIGH); }
if (boostLevel < 35 && egtLevel < 1200 && driveLevel < 50 && fuelLevel > 30) { digitalWrite(53, LOW); }
ReadDigitalStatuses();
ReadAnalogStatuses();
SendCANFramesToSerial();
ReadIncomingSerialData();
// just some dummy values for simulated engine parameters
if (rpm++ > 10000)
{
rpm = 500;
}
if (kpa++ > 2500)
{
kpa = 10;
}
if (tps++ > 1000)
{
tps = 0;
}
if (clt++ > 230)
{
// all values in frame are handled as unsigned values. To use negative values,
// offset actual value and write corresponding conversion to XML file imported to RealDash
// From RealDash 1.7.6 its also possible to specify value as signed="true" in XML file.
clt = 0;
}
// simple counter for sending the text frame to avoid sending it too often.
if (textCounter++ > 4000)
{
textCounter = 0;
}
delay(150);
}
void ReadDigitalStatuses()
{
#if defined (READWRITE_PINS)
// read status of digital pins (1-13)
digitalPins = 0;
int bitposition = 0;
for (int i=1; i<14; i++)
{
if (digitalRead(i) == HIGH) digitalPins |= (1 << bitposition);
bitposition++;
}
#endif
}
void ReadAnalogStatuses()
{
#if defined (READWRITE_PINS)
// read analog pins (0-7)
for (int i=0; i<7; i++)
{
analogPins[i] = analogRead(i);
}
#endif
}
void SendCANFramesToSerial()
{
byte frameData[8];
// build & send CAN frames to RealDash.
// a CAN frame payload is always 8 bytes containing data in a manner
// described by the RealDash custom channel description XML file
// all multibyte values are handled as little endian by default.
// endianess of the values can be specified in XML file if it is required to use big endian values
// build 1st CAN frame, RPM, MAP, CLT, TPS (just example data)
memcpy(frameData, &egtRead, 2);
memcpy(frameData + 2, &kpa, 2);
memcpy(frameData + 4, &clt, 2);
memcpy(frameData + 6, &tps, 2);
// write first CAN frame to serial
SendCANFrameToSerial(3200, frameData);
// build 2nd CAN frame, Arduino digital pins and 2 analog values
memcpy(frameData, &digitalPins, 2);
memcpy(frameData + 2, &analogPins[0], 2);
memcpy(frameData + 4, &analogPins[1], 2);
memcpy(frameData + 6, &analogPins[2], 2);
// write 2nd CAN frame to serial
SendCANFrameToSerial(3201, frameData);
// build 3rd CAN frame, rest of Arduino analog values
memcpy(frameData, &analogPins[3], 2);
memcpy(frameData + 2, &analogPins[4], 2);
memcpy(frameData + 4, &analogPins[5], 2);
memcpy(frameData + 6, &analogPins[6], 2);
// write 3rd CAN frame to serial
SendCANFrameToSerial(3202, frameData);
// build 4th frame, this is a text extension frame
// only send once at 1000 loops
if (textCounter == 0)
{
SendTextExtensionFrameToSerial(3203, "egtLevel");
}
else if (textCounter == 1000)
{
//SendTextExtensionFrameToSerial(3203, "Tomorrow's forecast: Lots of sun and 30 degrees centigate");
}
else if (textCounter == 2000)
{
//SendTextExtensionFrameToSerial(3203, "Now Playing: Insert your favorite song info here");
}
else if (textCounter == 3000)
{
//SendTextExtensionFrameToSerial(3203, "Message from Arduino: All systems running at nominal efficiency");
}
}
void SendCANFrameToSerial(unsigned long canFrameId, const byte* frameData)
{
// the 4 byte identifier at the beginning of each CAN frame
// this is required for RealDash to 'catch-up' on ongoing stream of CAN frames
const byte serialBlockTag[4] = { 0x44, 0x33, 0x22, 0x11 };
Serial.write(serialBlockTag, 4);
// the CAN frame id number (as 32bit little endian value)
Serial.write((const byte*)&canFrameId, 4);
// CAN frame payload
Serial.write(frameData, 8);
}
void SendTextExtensionFrameToSerial(unsigned long canFrameId, const char* text)
{
if (text)
{
// the 4 byte identifier at the beginning of each CAN frame
// this is required for RealDash to 'catch-up' on ongoing stream of CAN frames
const byte textExtensionBlockTag[4] = { 0x55, 0x33, 0x22, 0x11 };
Serial.write(textExtensionBlockTag, 4);
// the CAN frame id number (as 32bit little endian value)
Serial.write((const byte*)&canFrameId, 4);
// text payload
Serial.write(text, strlen(text) + 1);
}
}
void ReadIncomingSerialData()
{
while (Serial.available() > 0)
{
// little bit of extra effort here, since especially Bluetooth connections
// may leave unsent/received data in internal buffer for a long time
// therefore, we cannot be sure that incoming byte stream really starts at
// where we expect it to start.
// read one byte from serial stream
incomingFrame[incomingFramePos++] = Serial.read();
// check the first incoming bytes tag (0x44, 0x33, 0x22, 0x11)
if (incomingFrame[0] != 0x44)
{
// first incoming byte is not 0x44,
// the tag at the beginning of the frame does not match, this is an invalid frame
// just zero the incomingFrame buffer and start expecting first byte again
memset(incomingFrame, 0, 17);
incomingFramePos = 0;
}
if (incomingFramePos >= 17)
{
// frame complete, process it
ProcessIncomingFrame(incomingFrame);
// zero the incomingFrame buffer and start expecting first byte again
memset(incomingFrame, 0, 17);
incomingFramePos = 0;
}
}
}
void ProcessIncomingFrame(const byte* frame)
{
// first four bytes contain set value frame separator bytes, always 0x44,0x33,0x22,x11
// check that first 4 bytes match the tag
if (frame[0] != 0x44 ||
frame[1] != 0x33 ||
frame[2] != 0x22 ||
frame[3] != 0x11)
{
// frame tag does not match, wait for another frame
return;
}
// next four bytes contain set value CAN frame id in little endian form
unsigned long canFrameId = 0;
memcpy(&canFrameId, frame + 4, 4);
// next 8 bytes are the frame data
// ...
// last byte is check byte calculated as sum of previous 13 bytes (ignore overflow)
byte checkByte = 0;
for (int i=0; i<16; i++)
{
checkByte += frame[i];
}
if (frame[16] == checkByte)
{
// checksum match, this is a valid set value-frame:
// the frame payload data is in frame + 8 bytes
HandleIncomingSetValueFrame(canFrameId, frame + 8);
}
}
void HandleIncomingSetValueFrame(unsigned long canFrameId, const byte* frameData)
{
if (canFrameId == 3201)
{
memcpy(&digitalPins, frameData, 2);
memcpy(&analogPins[0], frameData + 2, 2);
memcpy(&analogPins[1], frameData + 4, 2);
memcpy(&analogPins[2], frameData + 6, 2);
#if defined (READWRITE_PINS)
// write digital pins
for (int i=0; i<13; i++)
{
digitalWrite(i + 1, (digitalPins & (1 << i)) ? HIGH : LOW);
}
analogWrite(0, analogPins[0]);
analogWrite(1, analogPins[1]);
analogWrite(2, analogPins[2]);
#endif
}
else if (canFrameId == 3202)
{
memcpy(&analogPins[3], frameData + 0, 2);
memcpy(&analogPins[4], frameData + 2, 2);
memcpy(&analogPins[5], frameData + 4, 2);
memcpy(&analogPins[6], frameData + 6, 2);
#if defined (READWRITE_PINS)
analogWrite(3, analogPins[3]);
analogWrite(4, analogPins[4]);
analogWrite(5, analogPins[5]);
analogWrite(6, analogPins[6]);
#endif
}
}