/**
* ============================================================================
* Name : RealDash_CAN_E30_A1.ino
* Part of : RealDash
* Author : Jani Immonen
* Major Changes By : Alec Dodd
* Created : 15.10.2017
* Modified : 25.05.2023
*
* Arduino sketch to use RealDash CAN protocol.
*
* This code is free for any use.
*
* www.realdash.net
* ============================================================================
**/
#include "Adafruit_MAX31855.h"
#define GND 3
#define VIN 7
#define MISO 50
#define CS 53
#define SCK 52
// control pins output table in array form
// connect max31855 S0~S2 to Arduino pins 35-37 respectively
byte controlPins[] = {B11100000,
B01100000,
B10100000,
B00100000,
B11000000,
B01000000,
B10000000,
B00000000 };
double thermValues[8]; // holds incoming values from Max31855
Adafruit_MAX31855 thermocouple(MISO, CS, SCK); // initialize the Thermocouple
unsigned long previousMillisread = 0; // will store last time thermocouple was read
unsigned long previousMillisselect = 0; // will store last time thermocouple was selected
const long intervalread = 125; // interval at which to check thermocouples (milliseconds)
const long intervalselect = 125; // interval at which to check thermocouples (milliseconds)
int tc; // to select thermocouple
// Arduino analog pins used for sensors via Realdash
int digitalPins[22] = {0};
int analogPins[16] = {0};
unsigned int rpm = A0;
unsigned int kpa = A1;
unsigned int lambda1 = A2;
unsigned int tps = A3;
unsigned int fuelpressure2 = A8;
unsigned int fuellevel = A9;
unsigned int coolantlevel = A10;
unsigned int humidity = A11;
float intakeairtemperature = A4;
float ambienttemperature = A5;
float hoodtemperature = A6;
float roadtemperature = A7;
byte dummy1 = 12;
byte dummy2 = 13;
byte windshieldwasheractive = 14;
byte hazardswitch = 15;
byte reverselights = 16;
byte interiorlights = 17;
byte frontfoglights = 18;
byte gear = 19;
byte horn = 20;
byte lowbeam = 21;
byte drl = 22;
byte highbeam = 23;
byte turnsignalleft = 24;
byte turnsignalright = 25;
byte aircompressor = 26;
byte blowermotor = 27;
byte wipermotors = 28;
byte diffuserflap = 29;
byte drs = 30;
byte defrost = 31;
byte parachutearmed = 32;
byte extinguisherarmed = 33;
void setup()
{
// init serial
Serial.begin(115200);
delay(100);
// Initialize pins for max31855
pinMode(GND, OUTPUT); // sets pin 7 as gnd output
pinMode(VIN, OUTPUT); // sets pin 3 as v output
DDRC = B11111111; // sets Arduino pins 35-37 as outputs
// Power up the board
digitalWrite(GND, LOW);
digitalWrite(VIN, HIGH);
delay(200);
}
void ReadDigitalStatuses()
{
//int bitposition = 0;
for (int i=12; i<33; i++)
{
digitalPins[i] = digitalRead(i);
}
//if (digitalRead(i) == HIGH) digitalPins |= (1 << bitposition);
//bitposition++;
}
void ReadAnalogStatuses()
{
for (int i=0; i<11; i++) // read analog pins (0-11)
{
analogPins[i] = analogRead(i);
}
}
void SetPin(int outputPin) // function to select pin on max31855
{
PORTC = controlPins[outputPin];
}
void SelectThermocouples()
{
unsigned long currentMillisselect = millis();
if (currentMillisselect - previousMillisselect >= intervalselect) //check if it is time to select the next thermocouple
{
previousMillisselect = currentMillisselect; //save the last time you selected the next thermocouple
SetPin(tc); // choose an input pin on the max31855
}
}
void ReadThermocouples()
{
unsigned long currentMillisread = millis();
if (currentMillisread - previousMillisread >= intervalread) //check if it is time to read the thermocouple
{
previousMillisread = currentMillisread; //save the last time you checked the thermocouple
thermValues[tc] = thermocouple.readCelsius(); // read the value on that thermocouple and store in array
if (tc == 7) //change to the next thermocouple, return to the first thermocouple after the last one
{
tc = 0;
}
else if (tc == 6)
{
tc = 7;
}
else if (tc == 5)
{
tc = 6;
}
else if (tc == 4)
{
tc = 5;
}
else if (tc == 3)
{
tc = 4;
}
else if (tc == 2)
{
tc = 3;
}
else if (tc == 1)
{
tc = 2;
}
else if (tc == 0)
{
tc = 1;
}
}
}
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 SendCANFramesToSerial()
{
byte buf[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
memcpy(buf, &rpm, 2);
memcpy(buf + 2, &kpa, 2);
memcpy(buf + 4, &fuelpressure2, 2);
memcpy(buf + 6, &tps, 2);
// write first CAN frame to serial
SendCANFrameToSerial(3200, buf);
// build 2nd CAN frame
memcpy(buf, &fuellevel, 2);
memcpy(buf + 2, &coolantlevel, 2);
memcpy(buf + 4, &intakeairtemperature, 2);
memcpy(buf + 6, &ambienttemperature, 2);
// write 2nd CAN frame to serial
SendCANFrameToSerial(3201, buf);
// build 3rd CAN frame
memcpy(buf, &hoodtemperature, 2);
memcpy(buf + 2, &roadtemperature, 2);
memcpy(buf + 4, &windshieldwasheractive, 2);
memcpy(buf + 6, &hazardswitch, 2);
// write 3rd CAN frame to serial
SendCANFrameToSerial(3202, buf);
// build 4th CAN frame
memcpy(buf, &reverselights, 2);
memcpy(buf + 2, &interiorlights, 2);
memcpy(buf + 4, &frontfoglights, 2);
memcpy(buf + 6, &gear, 2);
// write 4th CAN frame to serial
SendCANFrameToSerial(3203, buf);
// build 5th CAN frame
memcpy(buf, &horn, 2);
memcpy(buf + 2, &lowbeam, 2);
memcpy(buf + 4, &drl, 2);
memcpy(buf + 6, &highbeam, 2);
// write 5th CAN frame to serial
SendCANFrameToSerial(3204, buf);
// build 6th CAN frame
memcpy(buf, &turnsignalleft, 2);
memcpy(buf + 2, &turnsignalright, 2);
memcpy(buf + 4, &aircompressor, 2);
memcpy(buf + 6, &blowermotor, 2);
// write 6th CAN frame to serial
SendCANFrameToSerial(3205, buf);
// build 7th CAN frame
memcpy(buf, &drs, 2);
memcpy(buf + 2, &wipermotors, 2);
memcpy(buf + 4, &defrost, 2);
memcpy(buf + 6, ¶chutearmed, 2);
// write 7th CAN frame to serial
SendCANFrameToSerial(3206, buf);
// build 8th CAN frame
memcpy(buf, &extinguisherarmed, 2);
memcpy(buf + 2, &diffuserflap, 2);
memcpy(buf + 4, &dummy1, 2);
memcpy(buf + 6, &dummy2, 2);
// write 8th CAN frame to serial
SendCANFrameToSerial(3207, buf);
// build 9th CAN frame
memcpy(buf, &thermValues[0], 2);
memcpy(buf + 2, &thermValues[1], 2);
memcpy(buf + 4, &thermValues[2], 2);
memcpy(buf + 6, &thermValues[3], 2);
// write 9th CAN frame to serial
SendCANFrameToSerial(3208, buf);
// build 10th CAN frame
memcpy(buf, &thermValues[4], 2);
memcpy(buf + 2, &thermValues[5], 2);
memcpy(buf + 4, &thermValues[6], 2);
memcpy(buf + 6, &thermValues[7], 2);
// write 10th CAN frame to serial
SendCANFrameToSerial(3209, buf);
}
void loop()
{
ReadDigitalStatuses(); // read digital pins
ReadAnalogStatuses(); // read analog pins
SelectThermocouples(); // choose thermocouple to sample
ReadThermocouples(); // sample the chosen thermocouple
SendCANFramesToSerial(); // send data to realdash via serial CAN
delay(50);
}